/* * Copyright (c) 1998-2002 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * * The contents of this file constitute Original Code as defined in and * are subject to the Apple Public Source License Version 1.2 (the * "License"). You may not use this file except in compliance with the * License. Please obtain a copy of the License at * http://www.apple.com/publicsource and read it before using this file. * * This Original Code and all software distributed under the License are * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ #include #include #include #include #include #include #include #include #include #include #include #include "IOUSBInterfaceUserClient.h" //============================================================================================= // // Note on the use of IncrementOutstandingIO(), DecrementOutstandingIO() and doing extra // retain()/release() on async calls: // // The UserClient is a complex driver. It is attached to its provider when it is instantiated // due to a device/interface being created but it is ALSO used by the user land IOUSBLib. // Thus, it effectively has 2 clients, one on the kernel side and one on the user side. When // the user side closes the connection to this user client, we terminate ourselves. When the // device is unplugged, we get terminated by our provider. The Increment/DecrementOutstandingIO() // calls are used to keep track of any IO that has not finished so that when OUR provider attempts // to terminate us, we won't get released (because we have our provider open) until the IO completes. // However, when our user land client closes us, we terminate ourselves. In this case the // OutstandingIO() calls do not help us in preventing our object from going away. That is why we // also use a retain()/release() pair on async calls; if we are closed before the async request is // complete, we will not go away and hence won't panic when we try to execute the completion routine. // // Yes, we could have use only retain()/release(), but all our other kernel drivers use the // outstandingIO metaphor so I thought it would be complete to use it here as well. // // Another interesting piece is that we wait 1 ms in our clientClose method. This will allow other // pending threads to run and possible enter our driver (See bug #2862199). // //============================================================================================= // /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #define super IOUserClient OSDefineMetaClassAndStructors(IOUSBInterfaceUserClient, IOUserClient) OSDefineMetaClassAndStructors(IOUSBLowLatencyCommand, IOCommand) /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ enum { kMethodObjectThis = 0, kMethodObjectOwner }; const IOExternalMethod IOUSBInterfaceUserClient::sMethods[kNumUSBInterfaceMethods] = { { // kUSBInterfaceUserClientOpen (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::open, kIOUCScalarIScalarO, 1, 0 }, { // kUSBInterfaceUserClientClose (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::close, kIOUCScalarIScalarO, 0, 0 }, { // kUSBInterfaceUserClientGetDevice (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::GetDevice, kIOUCScalarIScalarO, 0, 1 }, { // kUSBInterfaceUserClientSetAlternateInterface (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::SetAlternateInterface, kIOUCScalarIScalarO, 1, 0 }, { // kUSBInterfaceUserClientGetFrameNumber (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::GetFrameNumber, kIOUCScalarIStructO, 0, 0xffffffff }, { // kUSBInterfaceUserClientGetPipeProperties (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::GetPipeProperties, kIOUCScalarIScalarO, 1, 5 }, { // kUSBInterfaceUserClientReadPipe (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::ReadPipe, kIOUCScalarIStructO, 3, 0xffffffff }, { // kUSBInterfaceUserClientReadPipeOOL (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::ReadPipeOOL, kIOUCStructIStructO, sizeof(IOUSBBulkPipeReq), sizeof(UInt32) }, { // kUSBInterfaceUserClientWritePipe (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::WritePipe, kIOUCScalarIStructI, 3, 0xffffffff }, { // kUSBInterfaceUserClientWritePipeOOL (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::WritePipeOOL, kIOUCStructIStructO, sizeof(IOUSBBulkPipeReq), 0 }, { // kUSBInterfaceUserClientGetPipeStatus (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::GetPipeStatus, kIOUCScalarIScalarO, 1, 0 }, { // kUSBInterfaceUserClientAbortPipe (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::AbortPipe, kIOUCScalarIScalarO, 1, 0 }, { // kUSBInterfaceUserClientResetPipe (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::ResetPipe, kIOUCScalarIScalarO, 1, 0 }, { // kUSBInterfaceUserClientSetPipeIdle (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::SetPipeIdle, kIOUCScalarIScalarO, 1, 0 }, { // kUSBInterfaceUserClientSetPipeActive (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::SetPipeActive, kIOUCScalarIScalarO, 1, 0 }, { // kUSBInterfaceUserClientClearPipeStall (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::ClearPipeStall, kIOUCScalarIScalarO, 2, 0 }, { // kUSBInterfaceUserClientControlRequestOut (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::ControlRequestOut, kIOUCScalarIStructI, 4, 0xffffffff }, { // kUSBInterfaceUserClientControlRequestIn (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::ControlRequestIn, kIOUCScalarIStructO, 4, 0xffffffff }, { // kUSBInterfaceUserClientControlRequestOutOOL (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::ControlRequestOutOOL, kIOUCStructIStructO, sizeof(IOUSBDevReqOOLTO), 0 }, { // kUSBInterfaceUserClientControlRequestInOOL (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::ControlRequestInOOL, kIOUCStructIStructO, sizeof(IOUSBDevReqOOLTO), sizeof(UInt32) }, { // kUSBInterfaceuserClientSetPipePolicy (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::SetPipePolicy, kIOUCScalarIScalarO, 3, 0 }, { // kUSBInterfaceuserClientGetBandwidthAvailable (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::GetBandwidthAvailable, kIOUCScalarIScalarO, 0, 1 }, { // kUSBInterfaceuserClientGetEndpointProperties (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::GetEndpointProperties, kIOUCScalarIScalarO, 3, 3 }, { // kUSBInterfaceUserClientLowLatencyPrepareBuffer (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::LowLatencyPrepareBuffer, kIOUCStructIStructO, sizeof(LowLatencyUserBufferInfo), 0 }, { // kUSBInterfaceUserClientLowLatencyReleaseBuffer (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::LowLatencyReleaseBuffer, kIOUCStructIStructO, sizeof(LowLatencyUserBufferInfo), 0 }, { // kUSBInterfaceUserClientGetMicroFrameNumber (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::GetMicroFrameNumber, kIOUCScalarIStructO, 0, 0xffffffff }, { // kUSBInterfaceUserClientGetFrameListTime (IOService*)kMethodObjectThis, (IOMethod) &IOUSBInterfaceUserClient::GetFrameListTime, kIOUCScalarIScalarO, 0, 1 } }; const IOExternalAsyncMethod IOUSBInterfaceUserClient::sAsyncMethods[kNumUSBInterfaceAsyncMethods] = { { // kUSBDeviceUserClientSetAsyncPort (IOService*)kMethodObjectThis, (IOAsyncMethod) &IOUSBInterfaceUserClient::SetAsyncPort, kIOUCScalarIScalarO, 0, 0 }, { // kUSBInterfaceUserClientControlAsyncRequestOut (IOService*)kMethodObjectThis, (IOAsyncMethod) &IOUSBInterfaceUserClient::ControlAsyncRequestOut, kIOUCStructIStructO, sizeof(IOUSBDevReqOOLTO), 0 }, { // kUSBInterfaceUserClientControlAsyncRequestIn (IOService*)kMethodObjectThis, (IOAsyncMethod) &IOUSBInterfaceUserClient::ControlAsyncRequestIn, kIOUCStructIStructO, sizeof(IOUSBDevReqOOLTO), 0 }, { // kUSBInterfaceUserClientAsyncReadPipe (IOService*)kMethodObjectThis, (IOAsyncMethod) &IOUSBInterfaceUserClient::AsyncReadPipe, kIOUCScalarIScalarO, 5, 0 }, { // kUSBInterfaceUserClientAsyncWritePipe (IOService*)kMethodObjectThis, (IOAsyncMethod) &IOUSBInterfaceUserClient::AsyncWritePipe, kIOUCScalarIScalarO, 5, 0 }, { // kUSBInterfaceUserClientReadIsochPipe (IOService*)kMethodObjectThis, (IOAsyncMethod) &IOUSBInterfaceUserClient::ReadIsochPipe, kIOUCStructIStructO, sizeof(IOUSBIsocStruct), 0 }, { // kUSBInterfaceUserClientWriteIsochPipe (IOService*)kMethodObjectThis, (IOAsyncMethod) &IOUSBInterfaceUserClient::WriteIsochPipe, kIOUCStructIStructO, sizeof(IOUSBIsocStruct), 0 }, { // kUSBInterfaceUserClientLowLatencyReadIsochPipe (IOService*)kMethodObjectThis, (IOAsyncMethod) &IOUSBInterfaceUserClient::LowLatencyReadIsochPipe, kIOUCStructIStructO, sizeof(IOUSBLowLatencyIsocStruct), 0 }, { // kUSBInterfaceUserClientLowLatencyWriteIsochPipe (IOService*)kMethodObjectThis, (IOAsyncMethod) &IOUSBInterfaceUserClient::LowLatencyWriteIsochPipe, kIOUCStructIStructO, sizeof(IOUSBLowLatencyIsocStruct), 0 } }; void IOUSBInterfaceUserClient::SetExternalMethodVectors() { fMethods = sMethods; fNumMethods = kNumUSBInterfaceMethods; fAsyncMethods = sAsyncMethods; fNumAsyncMethods = kNumUSBInterfaceAsyncMethods; } IOExternalMethod * IOUSBInterfaceUserClient::getTargetAndMethodForIndex(IOService **target, UInt32 index) { if (index < (UInt32)fNumMethods) { if ((IOService*)kMethodObjectThis == fMethods[index].object) *target = this; else if ((IOService*)kMethodObjectOwner == fMethods[index].object) *target = fOwner; else return NULL; return (IOExternalMethod *) &fMethods[index]; } else return NULL; } IOExternalAsyncMethod * IOUSBInterfaceUserClient::getAsyncTargetAndMethodForIndex(IOService **target, UInt32 index) { if (index < (UInt32)fNumAsyncMethods) { if ((IOService*)kMethodObjectThis == fAsyncMethods[index].object) *target = this; else if ((IOService*)kMethodObjectOwner == fAsyncMethods[index].object) *target = fOwner; else return NULL; return (IOExternalAsyncMethod *) &fAsyncMethods[index]; } else return 0; } bool IOUSBInterfaceUserClient::initWithTask(task_t owningTask, void *security_id , UInt32 type ) { if (!super::initWithTask(owningTask, security_id , type)) return false; if (!owningTask) return false; fTask = owningTask; fDead = false; SetExternalMethodVectors(); return true; } bool IOUSBInterfaceUserClient::start( IOService * provider ) { USBLog(7, "+%s[%p]::start(%p)", getName(), this, provider); IncrementOutstandingIO(); // make sure we don't close until start is done fOwner = OSDynamicCast(IOUSBInterface, provider); if (!fOwner) { USBError(1, "%s[%p]::start - provider is NULL!", getName(), this); goto ErrorExit; } if(!super::start(provider)) { USBError(1, "%s[%p]::start - super::start returned false!", getName(), this); goto ErrorExit; } fGate = IOCommandGate::commandGate(this); if(!fGate) { USBError(1, "%s[%p]::start - unable to create command gate", getName(), this); goto ErrorExit; } fWorkLoop = getWorkLoop(); if (!fWorkLoop) { USBError(1, "%s[%p]::start - unable to find my workloop", getName(), this); goto ErrorExit; } fWorkLoop->retain(); if (fWorkLoop->addEventSource(fGate) != kIOReturnSuccess) { USBError(1, "%s[%p]::start - unable to add gate to work loop", getName(), this); goto ErrorExit; } fFreeUSBLowLatencyCommandPool = IOCommandPool::withWorkLoop(fWorkLoop); if (!fFreeUSBLowLatencyCommandPool) { USBError(1,"%s[%p]::start - unable to create free command pool", getName(), this); // Remove the event source we added above // fWorkLoop->removeEventSource(fGate); goto ErrorExit; } DecrementOutstandingIO(); USBLog(7, "-%s[%p]::start(%p)", getName(), this, provider); return true; ErrorExit: if ( fGate != NULL ) { fGate->release(); fGate = NULL; } if ( fWorkLoop != NULL ) { fWorkLoop->release(); fWorkLoop = NULL; } DecrementOutstandingIO(); return false; } // This method is NOT the IOService open(IOService*) method. IOReturn IOUSBInterfaceUserClient::open(bool seize) { IOOptionBits options = seize ? (IOOptionBits)kIOServiceSeize : 0; USBLog(7, "+%s[%p]::open(%p)", getName(), this); if (!fOwner) return kIOReturnNotAttached; if (!fOwner->open(this, options)) return kIOReturnExclusiveAccess; fNeedToClose = false; return kIOReturnSuccess; } // This is NOT the normal IOService::close(IOService*) method. // We are treating this is a proxy that we should close our parent, but // maintain the connection with the task IOReturn IOUSBInterfaceUserClient::close() { IOReturn ret = kIOReturnSuccess; USBLog(7, "+%s[%p]::close", getName(), this); IncrementOutstandingIO(); if (fOwner && !isInactive()) { if (fOwner->isOpen(this)) { fNeedToClose = true; // the last outstanding IO will close this if (GetOutstandingIO() > 1) // 1 for the one at the top of this routine { int i; USBLog(5, "%s[%p]::close - outstanding IO, aborting pipes", getName(), this); for (i=1; i <= kUSBMaxPipes; i++) AbortPipe(i); } } else ret = kIOReturnNotOpen; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); USBLog(7, "-%s[%p]::close - returning %x", getName(), this, ret); return ret; } // // clientClose - my client on the user side has released the mach port, so I will no longer // be talking to him // IOReturn IOUSBInterfaceUserClient::clientClose( void ) { USBLog(7, "+%s[%p]::clientClose(%p)", getName(), this, fUserClientBufferInfoListHead); // Sleep for 1 ms to allow other threads that are pending to run // IOSleep(1); // If we have any kernelDataBuffer pointers, then release them now // if (fUserClientBufferInfoListHead != NULL) { ReleasePreparedDescriptors(); } fTask = NULL; terminate(); USBLog(7, "-%s[%p]::clientClose(%p)", getName(), this, fUserClientBufferInfoListHead); return kIOReturnSuccess; // DONT call super::clientClose, which just returns notSupported } IOReturn IOUSBInterfaceUserClient::clientDied( void ) { IOReturn ret; USBLog(5, "+%s[%p]::clientDied()", getName(), this); fDead = true; // don't send any mach messages in this case ret = super::clientDied(); USBLog(5, "-%s[%p]::clientDied()", getName(), this); return ret; } void IOUSBInterfaceUserClient::ReqComplete(void *obj, void *param, IOReturn res, UInt32 remaining) { void * args[1]; AsyncPB * pb = (AsyncPB *)param; IOUSBInterfaceUserClient *me = OSDynamicCast(IOUSBInterfaceUserClient, (OSObject*)obj); if (!me) return; USBLog(7, "%s[%p]::ReqComplete, req = %08x, remaining = %08x", me->getName(), me, (int)pb->fMax, (int)remaining); if(res == kIOReturnSuccess) { args[0] = (void *)(pb->fMax - remaining); } else { args[0] = 0; } if (pb->fMem) { pb->fMem->complete(); pb->fMem->release(); } if (!me->fDead) sendAsyncResult(pb->fAsyncRef, res, args, 1); IOFree(pb, sizeof(*pb)); me->DecrementOutstandingIO(); me->release(); } void IOUSBInterfaceUserClient::IsoReqComplete(void *obj, void *param, IOReturn res, IOUSBIsocFrame *pFrames) { void * args[1]; IsoAsyncPB * pb = (IsoAsyncPB *)param; IOUSBInterfaceUserClient *me = OSDynamicCast(IOUSBInterfaceUserClient, (OSObject*)obj); if (!me) return; args[0] = pb->frameBase; pb->countMem->writeBytes(0, pb->frames, pb->frameLen); pb->dataMem->complete(); pb->dataMem->release(); pb->countMem->release(); if (!me->fDead) sendAsyncResult(pb->fAsyncRef, res, args, 1); IOFree(pb, sizeof(*pb)+pb->frameLen); me->DecrementOutstandingIO(); me->release(); } void IOUSBInterfaceUserClient::LowLatencyIsoReqComplete(void *obj, void *param, IOReturn res, IOUSBLowLatencyIsocFrame *pFrames) { void * args[1]; IOUSBLowLatencyCommand * command = (IOUSBLowLatencyCommand *) param; IOMemoryDescriptor * dataBufferDescriptor; OSAsyncReference asyncRef; IOUSBInterfaceUserClient * me = OSDynamicCast(IOUSBInterfaceUserClient, (OSObject*)obj); if (!me) return; args[0] = command->GetFrameBase(); command->GetAsyncReference(&asyncRef); if (!me->fDead) sendAsyncResult( asyncRef, res, args, 1); // Complete the memory descriptor // dataBufferDescriptor = command->GetDataBuffer(); dataBufferDescriptor->complete(); dataBufferDescriptor->release(); // Free/give back the command me->fFreeUSBLowLatencyCommandPool->returnCommand(command); me->DecrementOutstandingIO(); me->release(); } IOReturn IOUSBInterfaceUserClient::SetAlternateInterface(UInt8 altSetting) { IOReturn ret; USBLog(7, "+%s[%p]::SetAlternateInterface(%d)", getName(), this, altSetting); IncrementOutstandingIO(); if (fOwner && !isInactive()) ret = fOwner->SetAlternateInterface(this, altSetting); else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::SetAlternateInterface - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::GetDevice(io_service_t *device) { IOReturn ret; IncrementOutstandingIO(); if (fOwner && !isInactive()) { // Although not documented, Simon says that exportObjectToClient consumes a reference, // so we have to put an extra retain on the device. The user client side of the USB stack (USBLib) // always calls this routine in order to cache the USB device. Radar #2586534 fOwner->GetDevice()->retain(); ret = exportObjectToClient(fTask, fOwner->GetDevice(), device); } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::GetDevice - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::GetFrameNumber(IOUSBGetFrameStruct *data, UInt32 *size) { IOReturn ret = kIOReturnSuccess; USBLog(7, "+%s[%p]::GetFrameNumber", getName(), this); if(*size != sizeof(IOUSBGetFrameStruct)) return kIOReturnBadArgument; if (fOwner && !isInactive()) { clock_get_uptime(&data->timeStamp); data->frame = fOwner->GetDevice()->GetBus()->GetFrameNumber(); } else ret = kIOReturnNotAttached; if (ret) USBLog(3, "%s[%p]::GetFrameNumber - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::GetMicroFrameNumber(IOUSBGetFrameStruct *data, UInt32 *size) { // This method only available for v2 controllers // IOUSBControllerV2 *v2 = OSDynamicCast(IOUSBControllerV2, fOwner->GetDevice()->GetBus()); IOReturn ret = kIOReturnSuccess; if (!v2) { USBLog(3, "%s[%p]::GetMicroFrameNumber - Not a USB 2.0 controller! Returning 0x%x", getName(), this, kIOReturnNotAttached); return kIOReturnNotAttached; } if (*size != sizeof(IOUSBGetFrameStruct)) { USBLog(3, "%s[%p]::GetMicroFrameNumber - *size is not sizeof(IOUSBGetFrameStruct): %ld, %ld", getName(), this, *size, sizeof(IOUSBGetFrameStruct) ); return kIOReturnBadArgument; } IncrementOutstandingIO(); if (fOwner && !isInactive()) { clock_get_uptime(&data->timeStamp); data->frame = v2->GetMicroFrameNumber(); } else { USBLog(3, "%s[%p]::GetMicroFrameNumber - no fOwner(%p) or isInactive", getName(), this, fOwner); ret = kIOReturnNotAttached; } DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::GetFrameNumber - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::GetBandwidthAvailable(UInt32 *bandwidth) { IOReturn ret = kIOReturnSuccess; USBLog(7, "+%s[%p]::GetBandwidthAvailable", getName(), this); if (fOwner && !isInactive()) { *bandwidth = fOwner->GetDevice()->GetBus()->GetBandwidthAvailable(); } else ret = kIOReturnNotAttached; if (ret) USBLog(3, "%s[%p]::GetBandwidthAvailable - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::GetFrameListTime(UInt32 *microsecondsInFrame) { IOReturn ret = kIOReturnSuccess; USBLog(7, "+%s[%p]::GetFrameListTime", getName(), this); if (fOwner && !isInactive()) { UInt8 speed; // Find the speed of the device and return the appropriate microseconds // depending on the speed // speed = fOwner->GetDevice()->GetSpeed(); if ( speed == kUSBDeviceSpeedHigh ) { // High Speed Device // *microsecondsInFrame = kUSBHighSpeedMicrosecondsInFrame; } else { // Low and Full Speed // *microsecondsInFrame = kUSBFullSpeedMicrosecondsInFrame; } } else ret = kIOReturnNotAttached; if (ret) USBLog(3, "%s[%p]::GetFrameListTime - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::GetEndpointProperties(UInt8 alternateSetting, UInt8 endpointNumber, UInt8 direction, UInt32 *transferType, UInt32 *maxPacketSize, UInt32 *interval) { IOReturn ret = kIOReturnSuccess; UInt8 myTT; UInt16 myMPS; UInt8 myIV; USBLog(7, "+%s[%p]::GetEndpointProperties", getName(), this); if (fOwner && !isInactive()) ret = fOwner->GetEndpointProperties(alternateSetting, endpointNumber, direction, &myTT, &myMPS, &myIV); else ret = kIOReturnNotAttached; if (ret) USBLog(3, "%s[%p]::GetEndpointProperties - returning err %x", getName(), this, ret); else { *transferType = myTT; *maxPacketSize = myMPS; *interval = myIV; USBLog(3, "%s[%p]::GetEndpointProperties - tt=%d, mps=%d, int=%d", getName(), this, *transferType, *maxPacketSize, *interval); } return ret; } IOUSBPipe* IOUSBInterfaceUserClient::GetPipeObj(UInt8 pipeNo) { IOUSBPipe *pipe = NULL; if (fOwner && !isInactive()) { if (pipeNo == 0) pipe = fOwner->GetDevice()->GetPipeZero(); if ((pipeNo > 0) && (pipeNo <= kUSBMaxPipes)) pipe = fOwner->GetPipeObj(pipeNo-1); } // we need to retain the pipe object because it could actually get released by the device/interface // and we don't want it to go away. This retain should probably be done in IOUSBDevice or IOUSBInterface // but we would affect too many people if we did that right now. if (pipe) pipe->retain(); return pipe; } // because of the way User Client works, these params need to be treated as pointers to 32 bit ints instead // of pointers to smaller values, because on the other side they are automatically dereferenced as such IOReturn IOUSBInterfaceUserClient::GetPipeProperties(UInt8 pipeRef, UInt32 *direction, UInt32 *number, UInt32 *transferType, UInt32 *maxPacketSize, UInt32 *interval) { IOUSBPipe *pipeObj; IOReturn ret = kIOReturnSuccess; USBLog(7, "+%s[%p]::GetPipeProperties", getName(), this); IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if(pipeObj) { if (direction) *direction = pipeObj->GetDirection(); if (number) *number = pipeObj->GetEndpointNumber(); if (transferType) *transferType = pipeObj->GetType(); if (maxPacketSize) *maxPacketSize = pipeObj->GetMaxPacketSize(); if (interval) *interval = pipeObj->GetInterval(); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::GetPipeProperties - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::ReadPipe(UInt8 pipeRef, UInt32 noDataTimeout, UInt32 completionTimeout, void *buf, UInt32 *size) { IOReturn ret; IOMemoryDescriptor * mem; IOUSBPipe *pipeObj; USBLog(7, "+%s(%p)::ReadPipe(%d, %d, %d, %p, %p)", getName(), this, pipeRef, noDataTimeout, completionTimeout, buf, size); IncrementOutstandingIO(); // do this to "hold" ourselves until we complete if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if(pipeObj) { mem = IOMemoryDescriptor::withAddress(buf, *size, kIODirectionIn); if(mem) { *size = 0; ret = pipeObj->Read(mem, noDataTimeout, completionTimeout, 0, size); mem->release(); } else ret = kIOReturnNoMemory; pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::ReadPipe - returning err %x", getName(), this, ret); return ret; } /* * Out of line version of read pipe - the buffer isn't mapped (yet) into the kernel task */ IOReturn IOUSBInterfaceUserClient::ReadPipeOOL(IOUSBBulkPipeReq *reqIn, UInt32 *sizeOut, IOByteCount inCount, IOByteCount *outCount) { IOReturn ret; IOMemoryDescriptor * mem; UInt8 pipeRef = reqIn->pipeRef; IOUSBPipe *pipeObj; USBLog(7, "+%s(%p)::ReadPipeOOL(%p, %p, %d, %d)", getName(), this, reqIn, sizeOut, inCount, *outCount); IncrementOutstandingIO(); // do this to "hold" ourselves until we complete if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if(pipeObj) { *sizeOut = 0; mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->buf, reqIn->size, kIODirectionIn, fTask); if(mem) { ret = mem->prepare(); if(ret == kIOReturnSuccess) ret = pipeObj->Read(mem, reqIn->noDataTimeout, reqIn->completionTimeout, 0, sizeOut); mem->complete(); mem->release(); } else { ret = kIOReturnNoMemory; } pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::ReadPipeOOL - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::WritePipe(UInt8 pipeRef, UInt32 noDataTimeout, UInt32 completionTimeout, void *buf, UInt32 size) { IOReturn ret; IOMemoryDescriptor * mem; IOUSBPipe *pipeObj; USBLog(7, "+%s(%p)::WritePipe(%d, %d, %d, %p, %p)", getName(), this, pipeRef, noDataTimeout, completionTimeout, buf, size); IncrementOutstandingIO(); // do this to "hold" ourselves until we complete if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if(pipeObj) { mem = IOMemoryDescriptor::withAddress(buf, size, kIODirectionOut); if(mem) { ret = pipeObj->Write(mem, noDataTimeout, completionTimeout); mem->release(); } else ret = kIOReturnNoMemory; pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::WritePipe - returning err %x", getName(), this, ret); return ret; } /* * Out of line version of write pipe - the buffer isn't mapped (yet) into the kernel task */ IOReturn IOUSBInterfaceUserClient::WritePipeOOL(IOUSBBulkPipeReq *req, IOByteCount inCount) { IOReturn ret; IOMemoryDescriptor * mem; IOUSBPipe *pipeObj; USBLog(7, "+%s(%p)::WritePipeOOL(%d)", getName(), this, inCount); IncrementOutstandingIO(); // do this to "hold" ourselves until we complete if (fOwner && !isInactive()) { pipeObj = GetPipeObj(req->pipeRef); if(pipeObj) { mem = IOMemoryDescriptor::withAddress((vm_address_t)req->buf, req->size, kIODirectionOut, fTask); if(mem) { ret = mem->prepare(); if(ret == kIOReturnSuccess) ret = pipeObj->Write(mem, req->noDataTimeout, req->completionTimeout); mem->complete(); mem->release(); } else ret = kIOReturnNoMemory; pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::WritePipeOOL - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::GetPipeStatus(UInt8 pipeRef) { IOUSBPipe *pipeObj; IOReturn ret; USBLog(7, "+%s[%p]::GetPipeStatus", getName(), this); IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if(pipeObj) { ret = pipeObj->GetPipeStatus(); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); return ret; } IOReturn IOUSBInterfaceUserClient::AbortPipe(UInt8 pipeRef) { IOUSBPipe *pipeObj; IOReturn ret; USBLog(7, "+%s[%p]::AbortPipe", getName(), this); IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if(pipeObj) { ret = pipeObj->Abort(); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) { if ( ret == kIOUSBUnknownPipeErr ) USBLog(5, "%s[%p]::AbortPipe - returning err %x", getName(), this, ret); else USBLog(3, "%s[%p]::AbortPipe - returning err %x", getName(), this, ret); } return ret; } IOReturn IOUSBInterfaceUserClient::ResetPipe(UInt8 pipeRef) { IOUSBPipe *pipeObj; IOReturn ret; USBLog(7, "+%s[%p]::ResetPipe", getName(), this); IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if(pipeObj) { ret = pipeObj->Reset(); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::ResetPipe - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::SetPipeIdle(UInt8 pipeRef) { if (!fOwner || isInactive()) return kIOReturnNotAttached; return(0); } IOReturn IOUSBInterfaceUserClient::SetPipeActive(UInt8 pipeRef) { if (!fOwner || isInactive()) return kIOReturnNotAttached; return(0); } IOReturn IOUSBInterfaceUserClient::ClearPipeStall(UInt8 pipeRef, bool bothEnds) { IOUSBPipe *pipeObj; IOReturn ret; USBLog(7, "+%s[%p]::ClearPipeStall", getName(), this); IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if(pipeObj) { USBLog(2, "%s[%p]::ClearPipeStall = bothEnds = %d", getName(), this, bothEnds); ret = pipeObj->ClearPipeStall(bothEnds); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::ClearPipeStall - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::SetPipePolicy(UInt8 pipeRef, UInt16 maxPacketSize, UInt8 maxInterval) { IOUSBPipe *pipeObj; IOReturn ret; USBLog(7, "+%s[%p]::SetPipePolicy", getName(), this); IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if(pipeObj) { USBLog(2, "%s[%p]::SetPipePolicy(%d, %d)", getName(), this, maxPacketSize, maxInterval); ret = pipeObj->SetPipePolicy(maxPacketSize, maxInterval); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::SetPipePolicy - returning err %x", getName(), this, ret); return ret; } /* * There's a limit of max 6 arguments to user client methods, so the type, recipient and request * are packed into one 16 bit integer. */ IOReturn IOUSBInterfaceUserClient::ControlRequestOut(UInt32 param1, UInt32 param2, UInt32 noDataTimeout, UInt32 completionTimeout, void *buf, UInt32 size) { IOReturn ret; IOUSBDevRequest req; UInt8 pipeRef = (param1 >> 16) & 0xFF; UInt8 bmRequestType = (param1 >> 8) & 0xFF; UInt8 bRequest = param1 & 0xFF; UInt16 wValue = (param2 >> 16) & 0xFFFF; UInt16 wIndex = param2 & 0xFFFF; IOUSBPipe *pipeObj; USBLog(7, "+%s[%p]::ControlRequestOut", getName(), this); IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if (pipeObj) { req.bmRequestType = bmRequestType; req.bRequest = bRequest; req.wValue = wValue; req.wIndex = wIndex; req.wLength = size; req.pData = buf; ret = pipeObj->ControlRequest(&req, noDataTimeout, completionTimeout); if(kIOReturnSuccess != ret) USBLog(3, "%s[%p]::ControlRequestOut err:0x%x", getName(), this, ret); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::ControlRequestOut - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::ControlRequestIn(UInt32 param1, UInt32 param2, UInt32 noDataTimeout, UInt32 completionTimeout, void *buf, UInt32 *size) { IOReturn ret; IOUSBDevRequest req; UInt8 pipeRef = (param1 >> 16) & 0xFF; UInt8 bmRequestType = (param1 >> 8) & 0xFF; UInt8 bRequest = param1 & 0xFF; UInt16 wValue = (param2 >> 16) & 0xFFFF; UInt16 wIndex = param2 & 0xFFFF; IOUSBPipe *pipeObj; USBLog(7, "+%s[%p]::ControlRequestIn", getName(), this); IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipeRef); if (pipeObj) { req.bmRequestType = bmRequestType; req.bRequest = bRequest; req.wValue = wValue; req.wIndex = wIndex; req.wLength = *size; req.pData = buf; ret = pipeObj->ControlRequest(&req, noDataTimeout, completionTimeout); if(ret == kIOReturnSuccess) *size = req.wLenDone; else { USBLog(3, "%s[%p]::ControlRequestIn err:0x%x", getName(), this, ret); *size = 0; } pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::ControlRequestIn - returning err %x", getName(), this, ret); return ret; } // // ControlRequestOutOOL: reqIn->wLength > 4K // IOReturn IOUSBInterfaceUserClient::ControlRequestOutOOL(IOUSBDevReqOOLTO *reqIn, IOByteCount inCount) { IOReturn ret; IOUSBDevRequestDesc req; IOMemoryDescriptor * mem; IOUSBPipe * pipeObj; USBLog(7, "+%s[%p]::ControlRequestOutOOL", getName(), this); if(inCount != sizeof(IOUSBDevReqOOLTO)) return kIOReturnBadArgument; IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(reqIn->pipeRef); if(pipeObj) { mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->pData, reqIn->wLength, kIODirectionOut, fTask); if(mem) { req.bmRequestType = reqIn->bmRequestType; req.bRequest = reqIn->bRequest; req.wValue = reqIn->wValue; req.wIndex = reqIn->wIndex; req.wLength = reqIn->wLength; req.pData = mem; ret = mem->prepare(); if(ret == kIOReturnSuccess) ret = pipeObj->ControlRequest(&req, reqIn->noDataTimeout, reqIn->completionTimeout); mem->complete(); mem->release(); } else ret = kIOReturnNoMemory; pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::ControlRequestOutOOL - returning err %x", getName(), this, ret); return ret; } // // ControlRequestInOOL: reqIn->wLength > 4K // IOReturn IOUSBInterfaceUserClient::ControlRequestInOOL(IOUSBDevReqOOLTO *reqIn, UInt32 *sizeOut, IOByteCount inCount, IOByteCount *outCount) { IOReturn ret; IOUSBDevRequestDesc req; IOMemoryDescriptor * mem; IOUSBPipe * pipeObj; USBLog(7, "+%s[%p]::ControlRequestInOOL", getName(), this); if((inCount != sizeof(IOUSBDevReqOOLTO)) || (*outCount != sizeof(UInt32))) return kIOReturnBadArgument; IncrementOutstandingIO(); if (fOwner && !isInactive()) { pipeObj = GetPipeObj(reqIn->pipeRef); if(pipeObj) { mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->pData, reqIn->wLength, kIODirectionIn, fTask); if(mem) { req.bmRequestType = reqIn->bmRequestType; req.bRequest = reqIn->bRequest; req.wValue = reqIn->wValue; req.wIndex = reqIn->wIndex; req.wLength = reqIn->wLength; req.pData = mem; ret = mem->prepare(); if(ret == kIOReturnSuccess) ret = pipeObj->ControlRequest(&req, reqIn->noDataTimeout, reqIn->completionTimeout); mem->complete(); mem->release(); if(ret == kIOReturnSuccess) { *sizeOut = req.wLenDone; } else { USBLog(3, "%s[%p]::ControlRequestInOOL err:0x%x", getName(), this, ret); *sizeOut = 0; } *outCount = sizeof(UInt32); } else ret = kIOReturnNoMemory; pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; DecrementOutstandingIO(); if (ret) USBLog(3, "%s[%p]::ControlRequestInOOL - returning err %x", getName(), this, ret); return ret; } IOReturn IOUSBInterfaceUserClient::LowLatencyPrepareBuffer(LowLatencyUserBufferInfo *bufferData) { IOReturn ret = kIOReturnSuccess; IOMemoryDescriptor * aDescriptor = NULL; LowLatencyUserClientBufferInfo * kernelDataBuffer = NULL; IOMemoryMap * frameListMap = NULL; IODirection direction; IncrementOutstandingIO(); if (fOwner && !isInactive()) { USBLog(3, "%s[%p]::LowLatencyPrepareBuffer cookie: %d, buffer: %p, size: %d, type %d, isPrepared: %d, next: %p", getName(), this, bufferData->cookie, bufferData->bufferAddress, bufferData->bufferSize, bufferData->bufferType, bufferData->isPrepared, bufferData->nextBuffer); // Allocate a buffer and zero it // kernelDataBuffer = ( LowLatencyUserClientBufferInfo *) IOMalloc( sizeof(LowLatencyUserClientBufferInfo) ); if (kernelDataBuffer == NULL ) { USBLog(1,"%s[%p]::LowLatencyPrepareBuffer Could not malloc buffer info (size = %d)!", getName(), this, sizeof(LowLatencyUserClientBufferInfo) ); return kIOReturnNoMemory; } bzero(kernelDataBuffer, sizeof(LowLatencyUserClientBufferInfo)); // Set the known fields // kernelDataBuffer->cookie = bufferData->cookie; kernelDataBuffer->bufferType = bufferData->bufferType; // Create a memory descriptor for our data buffer and prepare it (pages it in if necesary and prepares it) // if ( (bufferData->bufferType == kUSBLowLatencyWriteBuffer) || ( bufferData->bufferType == kUSBLowLatencyReadBuffer) ) { // We have a data buffer, so create a IOMD and prepare it // direction = ( bufferData->bufferType == kUSBLowLatencyWriteBuffer ? kIODirectionOut : kIODirectionIn ); aDescriptor = IOMemoryDescriptor::withAddress((vm_address_t)bufferData->bufferAddress, bufferData->bufferSize, direction, fTask); if(!aDescriptor) { USBLog(1,"%s[%p]::LowLatencyPrepareBuffer Could not create a data buffer memory descriptor (addr: %p, size %d)!", getName(), this, bufferData->bufferAddress, bufferData->bufferSize ); ret = kIOReturnNoMemory; goto ErrorExit; } ret = aDescriptor->prepare(); if (ret != kIOReturnSuccess) { USBLog(1,"%s[%p]::LowLatencyPrepareBuffer Could not prepare the data buffer memory descriptor!", getName(), this ); goto ErrorExit; } // OK, now save this in our user client structure // kernelDataBuffer->bufferAddress = bufferData->bufferAddress; kernelDataBuffer->bufferSize = bufferData->bufferSize; kernelDataBuffer->bufferDescriptor = aDescriptor; USBLog(3, "%s[%p]::LowLatencyPrepareBuffer finished preparing data buffer: %p, size %d, desc: %p, cookie: %ld", getName(), this, kernelDataBuffer->bufferAddress, kernelDataBuffer->bufferSize, kernelDataBuffer->bufferDescriptor, kernelDataBuffer->cookie); } else if ( bufferData->bufferType == kUSBLowLatencyFrameListBuffer ) { // We have a frame list that we need to map to the kernel's memory space // // Create a memory descriptor for our frame list and prepare it (pages it in if necesary and prepares it). // aDescriptor = IOMemoryDescriptor::withAddress((vm_address_t)bufferData->bufferAddress, bufferData->bufferSize, kIODirectionOutIn, fTask); if(!aDescriptor) { USBLog(1,"%s[%p]::LowLatencyPrepareBuffer Could not create a frame list memory descriptor (addr: %p, size %d)!", getName(), this, bufferData->bufferAddress, bufferData->bufferSize ); ret = kIOReturnNoMemory; goto ErrorExit; } ret = aDescriptor->prepare(); if (ret != kIOReturnSuccess) { USBLog(1,"%s[%p]::LowLatencyPrepareBuffer Could not prepare the frame list memory descriptor!", getName(), this ); ret = kIOReturnNoMemory; goto ErrorExit; } // Map it into the kernel // frameListMap = aDescriptor->map(); if (!frameListMap) { USBLog(1,"%s[%p]::LowLatencyPrepareBuffer Could not prepare the frame list memory descriptor!", getName(), this ); ret = kIOReturnNoMemory; aDescriptor->complete(); goto ErrorExit; } // Get the the mapped in virtual address and save it // kernelDataBuffer->frameListKernelAddress = frameListMap->getVirtualAddress(); // Save the rest of the items // kernelDataBuffer->bufferAddress = bufferData->bufferAddress; kernelDataBuffer->bufferSize = bufferData->bufferSize; kernelDataBuffer->frameListDescriptor = aDescriptor; kernelDataBuffer->frameListMap = frameListMap; USBLog(3, "%s[%p]::LowLatencyPrepareBuffer finished preparing frame list buffer: %p, size %d, desc: %p, map %p, kernel address: %p, cookie: %ld", getName(), this, kernelDataBuffer->bufferAddress, kernelDataBuffer->bufferSize, kernelDataBuffer->frameListDescriptor, kernelDataBuffer->frameListMap, kernelDataBuffer->frameListKernelAddress, kernelDataBuffer->cookie); } // Cool, we have a good buffer, add it to our list // AddDataBufferToList( kernelDataBuffer ); } else ret = kIOReturnNotAttached; ErrorExit: if (ret) USBLog(3, "%s[%p]::LowLatencyPrepareBuffer - returning err %x", getName(), this, ret); DecrementOutstandingIO(); return ret; } IOReturn IOUSBInterfaceUserClient::LowLatencyReleaseBuffer(LowLatencyUserBufferInfo *dataBuffer) { LowLatencyUserClientBufferInfo * kernelDataBuffer = NULL; IOReturn ret = kIOReturnSuccess; bool found = false; IncrementOutstandingIO(); USBLog(3, "+%s[%p]::LowLatencyReleaseBuffer for cookie: %ld", getName(), this, dataBuffer->cookie); if (fOwner && !isInactive()) { // We need to find the LowLatencyUserBufferInfo structure that contains // this buffer and then remove it from the list and free the structure // and the memory that was allocated for it // kernelDataBuffer = FindBufferCookieInList( dataBuffer->cookie ); if ( kernelDataBuffer == NULL ) { USBLog(3, "+%s[%p]::LowLatencyReleaseBuffer cookie: %ld, could not find buffer in list", getName(), this, dataBuffer->cookie); ret = kIOReturnBadArgument; goto ErrorExit; } // Now, remove this bufferData from the list // found = RemoveDataBufferFromList( kernelDataBuffer ); if ( !found ) { USBLog(3, "+%s[%p]::LowLatencyReleaseBuffer cookie: %ld, could not remove buffer (%p) from list", getName(), this, dataBuffer->cookie); ret = kIOReturnBadArgument; goto ErrorExit; } // Now, need to complete/release/free the objects we allocated in our prepare // if ( kernelDataBuffer->frameListMap ) kernelDataBuffer->frameListMap->release(); if ( kernelDataBuffer->frameListDescriptor ) { kernelDataBuffer->frameListDescriptor->complete(); kernelDataBuffer->frameListDescriptor->release(); } if ( kernelDataBuffer->bufferDescriptor ) { kernelDataBuffer->bufferDescriptor->complete(); kernelDataBuffer->bufferDescriptor->release(); } // Finally, deallocate our kernelDataBuffer // IOFree(kernelDataBuffer, sizeof(LowLatencyUserClientBufferInfo)); } else ret = kIOReturnNotAttached; ErrorExit: if (ret) USBLog(3, "%s[%p]::LowLatencyReleaseBuffer - returning err %x", getName(), this, ret); DecrementOutstandingIO(); return ret; } void IOUSBInterfaceUserClient::AddDataBufferToList( LowLatencyUserClientBufferInfo * insertBuffer ) { LowLatencyUserClientBufferInfo * buffer; // Traverse the list looking for last buffer and insert ours into it // if ( fUserClientBufferInfoListHead == NULL ) { fUserClientBufferInfoListHead = insertBuffer; return; } buffer = fUserClientBufferInfoListHead; while ( buffer->nextBuffer != NULL ) { buffer = buffer->nextBuffer; } // When we get here, nextBuffer is pointing to NULL. Our insert buffer // already has nextBuffer = NULL, so we just insert it // buffer->nextBuffer = insertBuffer; } LowLatencyUserClientBufferInfo * IOUSBInterfaceUserClient::FindBufferCookieInList( UInt32 cookie) { LowLatencyUserClientBufferInfo * buffer; bool foundIt = true; // Traverse the list looking for this buffer // if ( fUserClientBufferInfoListHead == NULL ) { return NULL; } buffer = fUserClientBufferInfoListHead; // Now, we need to see if our cookie is the same as one in the buffer list // while ( buffer->cookie != cookie ) { buffer = buffer->nextBuffer; if ( buffer == NULL ) { foundIt = false; break; } } if ( foundIt ) return buffer; else return false; } bool IOUSBInterfaceUserClient::RemoveDataBufferFromList( LowLatencyUserClientBufferInfo *removeBuffer) { LowLatencyUserClientBufferInfo * buffer; LowLatencyUserClientBufferInfo * previousBuffer; // If our head is NULL, then this buffer does not exist in our list // if ( fUserClientBufferInfoListHead == NULL ) { return false; } buffer = fUserClientBufferInfoListHead; // if our removeBuffer is the first one in the list, then just update the head and // exit // if ( buffer == removeBuffer ) { fUserClientBufferInfoListHead = buffer->nextBuffer; } else { // Need to start previousBuffer pointing to our initial buffer, in case we match // the first time // previousBuffer = buffer; while ( buffer->nextBuffer != removeBuffer ) { previousBuffer = buffer; buffer = previousBuffer->nextBuffer; } // When we get here, buffer is pointing to the same buffer as removeBuffer // and previous buffer is pointing to the previous element in the link list, // so, update the link in previous to point to removeBuffer->nextBuffer; // buffer->nextBuffer = removeBuffer->nextBuffer; } return true; } // ASYNC METHODS IOReturn IOUSBInterfaceUserClient::SetAsyncPort(OSAsyncReference asyncRef) { if (!fOwner || isInactive()) return kIOReturnNotAttached; fWakePort = (mach_port_t) asyncRef[0]; return kIOReturnSuccess; } IOReturn IOUSBInterfaceUserClient::ControlAsyncRequestOut(OSAsyncReference asyncRef, IOUSBDevReqOOLTO *reqIn, IOByteCount inCount) { IOReturn ret = kIOReturnSuccess; IOUSBPipe * pipeObj; IOUSBCompletion tap; AsyncPB * pb = NULL; IOMemoryDescriptor * mem = NULL; USBLog(7, "+%s[%p]::ControlAsyncRequestOut", getName(), this); if(inCount != sizeof(IOUSBDevReqOOLTO)) return kIOReturnBadArgument; retain(); // to make sure ReqComplete is still around IncrementOutstandingIO(); // to make sure ReqComplete is still around if (fOwner && !isInactive()) { pipeObj = GetPipeObj(reqIn->pipeRef); if(pipeObj) { if (reqIn->wLength) { if (reqIn->pData) { mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->pData, reqIn->wLength, kIODirectionOut, fTask); if (!mem) ret = kIOReturnNoMemory; } else ret = kIOReturnBadArgument; } if (ret == kIOReturnSuccess) { pb = (AsyncPB *)IOMalloc(sizeof(AsyncPB)); if(!pb) ret = kIOReturnNoMemory; } if (mem && (ret == kIOReturnSuccess)) ret = mem->prepare(); if (ret == kIOReturnSuccess) { pb->req.bmRequestType = reqIn->bmRequestType; pb->req.bRequest = reqIn->bRequest; pb->req.wValue = reqIn->wValue; pb->req.wIndex = reqIn->wIndex; pb->req.wLength = reqIn->wLength; pb->req.pData = mem; bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference)); pb->fMax = reqIn->wLength; pb->fMem = mem; tap.target = this; tap.action = &ReqComplete; // Want same number of reply args as for ControlReqIn tap.parameter = pb; ret = pipeObj->ControlRequest(&pb->req, reqIn->noDataTimeout, reqIn->completionTimeout, &tap); } pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; if(ret != kIOReturnSuccess) { USBLog(3, "%s[%p]::ControlAsyncRequestOut err 0x%x", getName(), this, ret); if(mem) { mem->complete(); mem->release(); } if(pb) IOFree(pb, sizeof(*pb)); // only decrement if we are not going to be successful. otherwise, this will be done in the completion DecrementOutstandingIO(); release(); } return ret; } IOReturn IOUSBInterfaceUserClient::ControlAsyncRequestIn(OSAsyncReference asyncRef, IOUSBDevReqOOLTO *reqIn, IOByteCount inCount) { IOReturn ret = kIOReturnSuccess; IOUSBPipe * pipeObj; IOUSBCompletion tap; AsyncPB * pb = NULL; IOMemoryDescriptor * mem = NULL; USBLog(7, "+%s[%p]::ControlAsyncRequestIn", getName(), this); if(inCount != sizeof(IOUSBDevReqOOLTO)) return kIOReturnBadArgument; retain(); IncrementOutstandingIO(); // to make sure ReqComplete is still around if (fOwner && !isInactive()) { pipeObj = GetPipeObj(reqIn->pipeRef); if(pipeObj) { if (reqIn->wLength) { if (reqIn->pData) { mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->pData, reqIn->wLength, kIODirectionIn, fTask); if (!mem) ret = kIOReturnNoMemory; } else ret = kIOReturnBadArgument; } if (ret == kIOReturnSuccess) { pb = (AsyncPB *)IOMalloc(sizeof(AsyncPB)); if(!pb) ret = kIOReturnNoMemory; } if (mem && (ret == kIOReturnSuccess)) ret = mem->prepare(); if (ret == kIOReturnSuccess) { pb->req.bmRequestType = reqIn->bmRequestType; pb->req.bRequest = reqIn->bRequest; pb->req.wValue = reqIn->wValue; pb->req.wIndex = reqIn->wIndex; pb->req.wLength = reqIn->wLength; pb->req.pData = mem; bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference)); pb->fMax = reqIn->wLength; pb->fMem = mem; tap.target = this; tap.action = &ReqComplete; tap.parameter = pb; ret = pipeObj->ControlRequest(&pb->req, reqIn->noDataTimeout, reqIn->completionTimeout, &tap); } pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; if(ret != kIOReturnSuccess) { USBLog(3, "%s[%p]::ControlAsyncRequestIn err 0x%x", getName(), this, ret); if(mem) { mem->complete(); mem->release(); } if(pb) IOFree(pb, sizeof(*pb)); // only decrement if we are not going to be successful. otherwise, this will be done in the completion DecrementOutstandingIO(); release(); } return ret; } IOReturn IOUSBInterfaceUserClient::DoIsochPipeAsync(OSAsyncReference asyncRef, IOUSBIsocStruct *stuff, IODirection direction) { IOReturn ret; IOUSBPipe * pipeObj; IOUSBIsocCompletion tap; IOMemoryDescriptor * dataMem = NULL; IOMemoryDescriptor * countMem = NULL; int frameLen = 0; // In bytes IsoAsyncPB * pb = NULL; USBLog(7, "+%s[%p]::DoIsochPipeAsync", getName(), this); retain(); IncrementOutstandingIO(); // to make sure IsoReqComplete is still around if (fOwner && !isInactive()) { pipeObj = GetPipeObj(stuff->fPipe); if(pipeObj) { frameLen = stuff->fNumFrames * sizeof(IOUSBIsocFrame); do { dataMem = IOMemoryDescriptor::withAddress((vm_address_t)stuff->fBuffer, stuff->fBufSize, direction, fTask); if(!dataMem) { ret = kIOReturnNoMemory; break; } ret = dataMem->prepare(); if(ret != kIOReturnSuccess) break; countMem = IOMemoryDescriptor::withAddress((vm_address_t)stuff->fFrameCounts, frameLen, kIODirectionOutIn, fTask); if(!countMem) { ret = kIOReturnNoMemory; break; } // Copy in requested transfers, we'll copy out result in completion routine pb = (IsoAsyncPB *)IOMalloc(sizeof(IsoAsyncPB) + frameLen); if(!pb) { ret = kIOReturnNoMemory; break; } bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference)); pb->frameLen = frameLen; pb->frameBase = stuff->fFrameCounts; pb->dataMem = dataMem; pb->countMem = countMem; countMem->readBytes(0, pb->frames, frameLen); tap.target = this; tap.action = &IsoReqComplete; tap.parameter = pb; if(direction == kIODirectionOut) ret = pipeObj->Write(dataMem, stuff->fStartFrame, stuff->fNumFrames, pb->frames, &tap); else ret = pipeObj->Read(dataMem, stuff->fStartFrame, stuff->fNumFrames, pb->frames, &tap); } while (false); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; if(kIOReturnSuccess != ret) { USBLog(3, "%s[%p]::DoIsochPipeAsync err 0x%x", getName(), this, ret); if(dataMem) dataMem->release(); if(countMem) countMem->release(); if(pb) IOFree(pb, sizeof(*pb) + frameLen); DecrementOutstandingIO(); release(); } return ret; } IOReturn IOUSBInterfaceUserClient::DoLowLatencyIsochPipeAsync(OSAsyncReference asyncRef, IOUSBLowLatencyIsocStruct *isocInfo, IODirection direction) { IOReturn ret; IOUSBPipe * pipeObj; IOUSBLowLatencyIsocCompletion tap; IOMemoryDescriptor * aDescriptor = NULL; IOUSBLowLatencyIsocFrame * pFrameList = NULL; IOUSBLowLatencyCommand * command = NULL; LowLatencyUserClientBufferInfo * dataBuffer = NULL; LowLatencyUserClientBufferInfo * frameListDataBuffer = NULL; USBLog(7, "+%s[%p]::DoLowLatencyIsochPipeAsync", getName(), this); retain(); IncrementOutstandingIO(); // to make sure LowLatencyIsoReqComplete is still around if (fOwner && !isInactive()) { pipeObj = GetPipeObj(isocInfo->fPipe); if(pipeObj) { do { // First, attempt to get a command for our transfer // command = (IOUSBLowLatencyCommand *) fFreeUSBLowLatencyCommandPool->getCommand(false); // If we couldn't get a command, increase the allocation and try again // if ( command == NULL ) { IncreaseCommandPool(); command = (IOUSBLowLatencyCommand *) fFreeUSBLowLatencyCommandPool->getCommand(false); if ( command == NULL ) { USBLog(3,"%s[%p]::DoLowLatencyIsochPipeAsync Could not get a IOUSBLowLatencyIsocCommand",getName(),this); ret = kIOReturnNoResources; break; } } USBLog(7,"%s[%p]::DoLowLatencyIsochPipeAsync: dataBuffer cookie: %ld, offset: %ld, frameList cookie: %ld, offset : %ld", getName(),this, isocInfo->fDataBufferCookie, isocInfo->fDataBufferOffset, isocInfo->fFrameListBufferCookie, isocInfo->fFrameListBufferOffset ); // Find the buffer corresponding to the data buffer cookie: // dataBuffer = FindBufferCookieInList(isocInfo->fDataBufferCookie); if ( dataBuffer == NULL ) { ret = kIOReturnNoMemory; break; } // Create a new IOMD that is a subrange of our data buffer memory descriptor, and prepare it // aDescriptor = IOMemoryDescriptor::withSubRange( dataBuffer->bufferDescriptor, isocInfo->fDataBufferOffset, isocInfo->fBufSize, direction ); if ( aDescriptor == NULL ) { ret = kIOReturnNoMemory; break; } // Prepare this descriptor // ret = aDescriptor->prepare(); if (ret != kIOReturnSuccess) { break; } // Find the buffer corresponding to the frame list cookie: // frameListDataBuffer = FindBufferCookieInList(isocInfo->fFrameListBufferCookie); // Get our virtual address by looking at the buffer data and adding in the offset that was passed in // pFrameList = (IOUSBLowLatencyIsocFrame *) ( (UInt32) frameListDataBuffer->frameListKernelAddress + isocInfo->fFrameListBufferOffset); // Copy the data into our command buffer // command->SetAsyncReference( asyncRef ); command->SetFrameBase( (void *) ((UInt32) frameListDataBuffer->bufferAddress + isocInfo->fFrameListBufferOffset)); command->SetDataBuffer( aDescriptor ); // Populate our completion routine // tap.target = this; tap.action = &LowLatencyIsoReqComplete; tap.parameter = command; if ( direction == kIODirectionOut ) ret = pipeObj->Write(aDescriptor, isocInfo->fStartFrame, isocInfo->fNumFrames, pFrameList, &tap, isocInfo->fUpdateFrequency); else ret = pipeObj->Read(aDescriptor, isocInfo->fStartFrame, isocInfo->fNumFrames,pFrameList, &tap, isocInfo->fUpdateFrequency); } while (false); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; if(kIOReturnSuccess != ret) { USBLog(3, "%s[%p]::DoLowLatencyIsochPipeAsync err 0x%x", getName(), this, ret); if ( aDescriptor ) aDescriptor->release(); // return command // fFreeUSBLowLatencyCommandPool->returnCommand(command); DecrementOutstandingIO(); release(); } USBLog(7, "-%s[%p]::DoLowLatencyIsochPipeAsync", getName(), this); return ret; } IOReturn IOUSBInterfaceUserClient::ReadIsochPipe(OSAsyncReference asyncRef, IOUSBIsocStruct *stuff, UInt32 sizeIn) { return DoIsochPipeAsync(asyncRef, stuff, kIODirectionIn); } IOReturn IOUSBInterfaceUserClient::WriteIsochPipe(OSAsyncReference asyncRef, IOUSBIsocStruct *stuff, UInt32 sizeIn) { return DoIsochPipeAsync(asyncRef, stuff, kIODirectionOut); } IOReturn IOUSBInterfaceUserClient::LowLatencyReadIsochPipe(OSAsyncReference asyncRef, IOUSBLowLatencyIsocStruct *stuff, UInt32 sizeIn) { return DoLowLatencyIsochPipeAsync(asyncRef, stuff, kIODirectionIn); } IOReturn IOUSBInterfaceUserClient::LowLatencyWriteIsochPipe(OSAsyncReference asyncRef, IOUSBLowLatencyIsocStruct *stuff, UInt32 sizeIn) { return DoLowLatencyIsochPipeAsync(asyncRef, stuff, kIODirectionOut); } /* * Async version of read pipe - the buffer isn't mapped (yet) into the kernel task */ IOReturn IOUSBInterfaceUserClient::AsyncReadPipe(OSAsyncReference asyncRef, UInt32 pipe, void *buf, UInt32 size, UInt32 noDataTimeout, UInt32 completionTimeout) { IOReturn ret; IOUSBPipe *pipeObj; IOUSBCompletion tap; IOMemoryDescriptor * mem = NULL; AsyncPB * pb = NULL; USBLog(7, "+%s[%p]::AsyncReadPipe", getName(), this); retain(); IncrementOutstandingIO(); // to make sure ReqComplete is still around if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipe); if(pipeObj) { do { mem = IOMemoryDescriptor::withAddress((vm_address_t)buf, size, kIODirectionIn, fTask); if(!mem) { ret = kIOReturnNoMemory; break; } ret = mem->prepare(); if(ret != kIOReturnSuccess) break; pb = (AsyncPB *)IOMalloc(sizeof(AsyncPB)); if(!pb) { ret = kIOReturnNoMemory; break; } bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference)); pb->fMax = size; pb->fMem = mem; tap.target = this; tap.action = &ReqComplete; tap.parameter = pb; ret = pipeObj->Read(mem, noDataTimeout, completionTimeout, &tap, NULL); } while (false); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; if(ret != kIOReturnSuccess) { USBLog(3, "%s[%p]::AsyncReadPipe err 0x%x", getName(), this, ret); if(mem) { mem->complete(); mem->release(); } if(pb) IOFree(pb, sizeof(*pb)); DecrementOutstandingIO(); release(); } return ret; } /* * Async version of write pipe - the buffer isn't mapped (yet) into the kernel task */ IOReturn IOUSBInterfaceUserClient::AsyncWritePipe(OSAsyncReference asyncRef, UInt32 pipe, void *buf, UInt32 size, UInt32 noDataTimeout, UInt32 completionTimeout) { IOReturn ret; IOUSBPipe *pipeObj; IOUSBCompletion tap; IOMemoryDescriptor * mem = NULL; AsyncPB * pb = NULL; USBLog(7, "+%s[%p]::AsyncWritePipe", getName(), this); retain(); IncrementOutstandingIO(); // to make sure ReqComplete is still around if (fOwner && !isInactive()) { pipeObj = GetPipeObj(pipe); if(pipeObj) { do { mem = IOMemoryDescriptor::withAddress((vm_address_t)buf, size, kIODirectionOut, fTask); if(!mem) { ret = kIOReturnNoMemory; break; } ret = mem->prepare(); if(ret != kIOReturnSuccess) break; pb = (AsyncPB *)IOMalloc(sizeof(AsyncPB)); if(!pb) { ret = kIOReturnNoMemory; break; } bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference)); pb->fMax = size; pb->fMem = mem; tap.target = this; tap.action = &ReqComplete; tap.parameter = pb; ret = pipeObj->Write(mem, noDataTimeout, completionTimeout, &tap); } while (false); pipeObj->release(); } else ret = kIOUSBUnknownPipeErr; } else ret = kIOReturnNotAttached; if(ret != kIOReturnSuccess) { USBLog(3, "%s[%p]::AsyncWritePipe err 0x%x", getName(), this, ret); if(mem) { mem->complete(); mem->release(); } if(pb) IOFree(pb, sizeof(*pb)); DecrementOutstandingIO(); release(); } return ret; } // // stop // // This IOService method is called AFTER we have closed our provider, assuming that the provider was // ever opened. If we issue I/O to the provider, then we must have it open, and we will not close // our provider until all of that I/O is completed. void IOUSBInterfaceUserClient::stop(IOService * provider) { USBLog(7, "+%s[%p]::stop(%p)", getName(), this, provider); // If we have any kernelDataBuffer pointers, then release them now // if (fUserClientBufferInfoListHead != NULL) { ReleasePreparedDescriptors(); } super::stop(provider); USBLog(7, "-%s[%p]::stop(%p)", getName(), this, provider); } void IOUSBInterfaceUserClient::free() { IOReturn ret; // USBLog(7, "+%s[%p]::free", getName(), this); if ( fFreeUSBLowLatencyCommandPool ) { // USBLog(5, "+%s[%p]::free releasing fFreeUSBLowLatencyCommandPool", getName(), this); fFreeUSBLowLatencyCommandPool->release(); fFreeUSBLowLatencyCommandPool = NULL; } if (fGate) { if (fWorkLoop) { ret = fWorkLoop->removeEventSource(fGate); // USBLog(5, "+%s[%p]::free releasing fWorkLoop", getName(), this); fWorkLoop->release(); fWorkLoop = NULL; } else { USBError(1, "%s[%p]::free - have gate, but no valid workloop (%p)!", getName(), this, fWorkLoop); } fGate->release(); fGate = NULL; } super::free(); } bool IOUSBInterfaceUserClient::finalize( IOOptionBits options ) { bool ret; USBLog(7, "+%s[%p]::finalize(%08x)", getName(), this, (int)options); ret = super::finalize(options); USBLog(7, "-%s[%p]::finalize(%08x) - returning %s", getName(), this, (int)options, ret ? "true" : "false"); return ret; } bool IOUSBInterfaceUserClient::willTerminate( IOService * provider, IOOptionBits options ) { // this method is intended to be used to stop any pending I/O and to make sure that // we have begun getting our callbacks in order. by the time we get here, the // isInactive flag is set, so we really are marked as being done. we will do in here // what we used to do in the message method (this happens first) USBLog(3, "%s[%p]::willTerminate isInactive = %d", getName(), this, isInactive()); return super::willTerminate(provider, options); } bool IOUSBInterfaceUserClient::didTerminate( IOService * provider, IOOptionBits options, bool * defer ) { // this method comes at the end of the termination sequence. Hopefully, all of our outstanding IO is complete // in which case we can just close our provider and IOKit will take care of the rest. Otherwise, we need to // hold on to the device and IOKit will terminate us when we close it later USBLog(3, "%s[%p]::didTerminate isInactive = %d, outstandingIO = %d", getName(), this, isInactive(), fOutstandingIO); if ( fOutstandingIO == 0 ) fOwner->close(this); else fNeedToClose = true; return super::didTerminate(provider, options, defer); } void IOUSBInterfaceUserClient::DecrementOutstandingIO(void) { if (!fGate) { if (!--fOutstandingIO && fNeedToClose) { USBLog(3, "%s[%p]::DecrementOutstandingIO isInactive = %d, outstandingIO = %d - closing device", getName(), this, isInactive(), fOutstandingIO); fOwner->close(this); } return; } fGate->runAction(ChangeOutstandingIO, (void*)-1); } void IOUSBInterfaceUserClient::IncrementOutstandingIO(void) { if (!fGate) { fOutstandingIO++; return; } fGate->runAction(ChangeOutstandingIO, (void*)1); } IOReturn IOUSBInterfaceUserClient::ChangeOutstandingIO(OSObject *target, void *param1, void *param2, void *param3, void *param4) { IOUSBInterfaceUserClient *me = OSDynamicCast(IOUSBInterfaceUserClient, target); UInt32 direction = (UInt32)param1; if (!me) { USBLog(1, "IOUSBInterfaceUserClient::ChangeOutstandingIO - invalid target"); return kIOReturnSuccess; } switch (direction) { case 1: me->fOutstandingIO++; break; case -1: if (!--me->fOutstandingIO && me->fNeedToClose) { USBLog(3, "%s[%p]::ChangeOutstandingIO isInactive = %d, outstandingIO = %d - closing device", me->getName(), me, me->isInactive(), me->fOutstandingIO); me->fOwner->close(me); } break; default: USBLog(1, "%s[%p]::ChangeOutstandingIO - invalid direction", me->getName(), me); } return kIOReturnSuccess; } UInt32 IOUSBInterfaceUserClient::GetOutstandingIO() { UInt32 count = 0; if (!fGate) { return fOutstandingIO; } fGate->runAction(GetGatedOutstandingIO, (void*)&count); return count; } IOReturn IOUSBInterfaceUserClient::GetGatedOutstandingIO(OSObject *target, void *param1, void *param2, void *param3, void *param4) { IOUSBInterfaceUserClient *me = OSDynamicCast(IOUSBInterfaceUserClient, target); if (!me) { USBLog(1, "IOUSBInterfaceUserClient::GetGatedOutstandingIO - invalid target"); return kIOReturnSuccess; } *(UInt32 *) param1 = me->fOutstandingIO; return kIOReturnSuccess; } void IOUSBInterfaceUserClient::IncreaseCommandPool(void) { int i; USBLog(3,"%s[%p] Adding (%d) to Command Pool", getName(), this, kSizeToIncrementLowLatencyCommandPool); for (i = 0; i < kSizeToIncrementLowLatencyCommandPool; i++) { IOUSBLowLatencyCommand *command = IOUSBLowLatencyCommand::NewCommand(); if (command) fFreeUSBLowLatencyCommandPool->returnCommand(command); } fCurrentSizeOfCommandPool += kSizeToIncrementLowLatencyCommandPool; } void IOUSBInterfaceUserClient::ReleasePreparedDescriptors(void) { LowLatencyUserClientBufferInfo * kernelDataBuffer; LowLatencyUserClientBufferInfo * nextBuffer; // If we have any kernelDataBuffer pointers, then release them now // if (fUserClientBufferInfoListHead != NULL) { USBLog(5, "+%s[%p]::stop: fUserClientBufferInfoListHead NOT NULL (%p) ", getName(), this, fUserClientBufferInfoListHead); nextBuffer = fUserClientBufferInfoListHead; kernelDataBuffer = fUserClientBufferInfoListHead; // Traverse the list and release memory // while ( nextBuffer != NULL ) { nextBuffer = kernelDataBuffer->nextBuffer; // Now, need to complete/release/free the objects we allocated in our prepare // if ( kernelDataBuffer->frameListMap ) kernelDataBuffer->frameListMap->release(); if ( kernelDataBuffer->frameListDescriptor ) { kernelDataBuffer->frameListDescriptor->complete(); kernelDataBuffer->frameListDescriptor->release(); } if ( kernelDataBuffer->bufferDescriptor ) { // We call prepare on the bufferDescriptor, so we need to call complete on it // kernelDataBuffer->bufferDescriptor->complete(); kernelDataBuffer->bufferDescriptor->release(); } // Finally, deallocate our kernelDataBuffer // IOFree(kernelDataBuffer, sizeof(LowLatencyUserClientBufferInfo)); kernelDataBuffer = nextBuffer; } fUserClientBufferInfoListHead = NULL; } } IOUSBLowLatencyCommand * IOUSBLowLatencyCommand::NewCommand() { IOUSBLowLatencyCommand *me = new IOUSBLowLatencyCommand; return me; } void IOUSBLowLatencyCommand::SetAsyncReference(OSAsyncReference ref) { bcopy(ref, fAsyncRef, sizeof(OSAsyncReference)); }