Source to iokit/Families/IOUSBBus/IOUSBUserClient.cpp


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1998-2000 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.1 (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 OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * Copyright (c) 1999 Apple Computer, Inc.  All rights reserved.
 *
 * HISTORY
 * 16 Mar 1999 wgulland created.
 *
 */
#include <libkern/OSByteOrder.h>

#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/usb/IOUSBDevice.h>
#include <IOKit/usb/IOUSBInterface.h>
#include <IOKit/usb/IOUSBPipe.h>

#include "IOUSBUserClient.h"

#define super IOUserClient
#define DEBUGGING_LEVEL 0

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

OSDefineMetaClass( IOUSBUserClient, IOUserClient )
OSDefineAbstractStructors(IOUSBUserClient, IOUserClient)

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

bool IOUSBUserClient::start( IOService * provider )
{
    IOUSBNub *owner = OSDynamicCast(IOUSBNub, provider);

    assert(owner);
    if(!super::start(provider))
        return false;

    fPipes[0] = owner->pipeZero();
    if(!fPipes[0])
	return false;

    // Initialize the call structures valid for IOUSBNub
    fMethods[kUSBSetConfig].object = NULL;
    fMethods[kUSBSetConfig].func = NULL;
    fMethods[kUSBSetConfig].count0 = 1;
    fMethods[kUSBSetConfig].count1 = 0;
    fMethods[kUSBSetConfig].flags = kIOUCScalarIScalarO;

    fMethods[kUSBGetConfig].object = provider;
    fMethods[kUSBGetConfig].func = (IOMethod)&IOUSBNub::GetConfiguration;
    fMethods[kUSBGetConfig].count0 = 0;
    fMethods[kUSBGetConfig].count1 = 1;
    fMethods[kUSBGetConfig].flags = kIOUCScalarIScalarO;

    fMethods[kUSBGetConfigDescriptor].object = NULL;
    fMethods[kUSBGetConfigDescriptor].func = NULL;
    fMethods[kUSBGetConfigDescriptor].count0 = 1;
    fMethods[kUSBGetConfigDescriptor].count1 = 0xffffffff; // variable
    fMethods[kUSBGetConfigDescriptor].flags = kIOUCScalarIStructO;

    fMethods[kUSBControlReqOut].object = this;
    fMethods[kUSBControlReqOut].func =
        (IOMethod)&IOUSBUserClient::ControlReqOut;
    fMethods[kUSBControlReqOut].count0 = 4;
    fMethods[kUSBControlReqOut].count1 = 0xffffffff; // variable
    fMethods[kUSBControlReqOut].flags = kIOUCScalarIStructI;

    fMethods[kUSBControlReqIn].object = this;
    fMethods[kUSBControlReqIn].func =
        (IOMethod)&IOUSBUserClient::ControlReqIn;
    fMethods[kUSBControlReqIn].count0 = 4;
    fMethods[kUSBControlReqIn].count1 = 0xffffffff; // variable
    fMethods[kUSBControlReqIn].flags = kIOUCScalarIStructO;

    fMethods[kUSBOpenPipe].object = NULL;
    fMethods[kUSBOpenPipe].func = NULL;
    fMethods[kUSBOpenPipe].count0 = 4;
    fMethods[kUSBOpenPipe].count1 = 1;
    fMethods[kUSBOpenPipe].flags = kIOUCScalarIScalarO;

    fMethods[kUSBClosePipe].object = this;
    fMethods[kUSBClosePipe].func =
        (IOMethod)&IOUSBUserClient::ClosePipe;
    fMethods[kUSBClosePipe].count0 = 1;
    fMethods[kUSBClosePipe].count1 = 0;
    fMethods[kUSBClosePipe].flags = kIOUCScalarIScalarO;

    fMethods[kUSBReadPipe].object = this;
    fMethods[kUSBReadPipe].func =
        (IOMethod)&IOUSBUserClient::ReadPipe;
    fMethods[kUSBReadPipe].count0 = 1;
    fMethods[kUSBReadPipe].count1 = 0xffffffff; // variable
    fMethods[kUSBReadPipe].flags = kIOUCScalarIStructO;

    fMethods[kUSBWritePipe].object = this;
    fMethods[kUSBWritePipe].func =
        (IOMethod)&IOUSBUserClient::WritePipe;
    fMethods[kUSBWritePipe].count0 = 1;
    fMethods[kUSBWritePipe].count1 = 0xffffffff; // variable
    fMethods[kUSBWritePipe].flags = kIOUCScalarIStructI;

    fMethods[kUSBGetPipeStatus].object = this;
    fMethods[kUSBGetPipeStatus].func =
        (IOMethod)&IOUSBUserClient::getPipeStatus;
    fMethods[kUSBGetPipeStatus].count0 = 1;
    fMethods[kUSBGetPipeStatus].count1 = 0;
    fMethods[kUSBGetPipeStatus].flags = kIOUCScalarIScalarO;

    fMethods[kUSBAbortPipe].object = this;
    fMethods[kUSBAbortPipe].func =
        (IOMethod)&IOUSBUserClient::abortPipe;
    fMethods[kUSBAbortPipe].count0 = 1;
    fMethods[kUSBAbortPipe].count1 = 0;
    fMethods[kUSBAbortPipe].flags = kIOUCScalarIScalarO;

    fMethods[kUSBResetPipe].object = this;
    fMethods[kUSBResetPipe].func =
        (IOMethod)&IOUSBUserClient::resetPipe;
    fMethods[kUSBResetPipe].count0 = 1;
    fMethods[kUSBResetPipe].count1 = 0;
    fMethods[kUSBResetPipe].flags = kIOUCScalarIScalarO;

    fMethods[kUSBSetPipeIdle].object = this;
    fMethods[kUSBSetPipeIdle].func =
        (IOMethod)&IOUSBUserClient::setPipeIdle;
    fMethods[kUSBSetPipeIdle].count0 = 1;
    fMethods[kUSBSetPipeIdle].count1 = 0;
    fMethods[kUSBSetPipeIdle].flags = kIOUCScalarIScalarO;

    fMethods[kUSBSetPipeActive].object = this;
    fMethods[kUSBSetPipeActive].func =
        (IOMethod)&IOUSBUserClient::setPipeActive;
    fMethods[kUSBSetPipeActive].count0 = 1;
    fMethods[kUSBSetPipeActive].count1 = 0;
    fMethods[kUSBSetPipeActive].flags = kIOUCScalarIScalarO;

    fMethods[kUSBClearPipeStall].object = this;
    fMethods[kUSBClearPipeStall].func =
        (IOMethod)&IOUSBUserClient::clearPipeStall;
    fMethods[kUSBClearPipeStall].count0 = 1;
    fMethods[kUSBClearPipeStall].count1 = 0;
    fMethods[kUSBClearPipeStall].flags = kIOUCScalarIScalarO;

    fMethods[kUSBReadPipeOOL].object = this;
    fMethods[kUSBReadPipeOOL].func =
        (IOMethod)&IOUSBUserClient::ReadPipeOOL;
    fMethods[kUSBReadPipeOOL].count0 = 3;
    fMethods[kUSBReadPipeOOL].count1 = 1;
    fMethods[kUSBReadPipeOOL].flags = kIOUCScalarIScalarO;

    fAsyncMethods[kUSBReadPipeOOL].object = this;
    fAsyncMethods[kUSBReadPipeOOL].func =
        (IOAsyncMethod)&IOUSBUserClient::ReadPipeAsync;
    fAsyncMethods[kUSBReadPipeOOL].count0 = 3;
    fAsyncMethods[kUSBReadPipeOOL].count1 = 0;
    fAsyncMethods[kUSBReadPipeOOL].flags = kIOUCScalarIScalarO;

    fMethods[kUSBWritePipeOOL].object = this;
    fMethods[kUSBWritePipeOOL].func =
        (IOMethod)&IOUSBUserClient::WritePipeOOL;
    fMethods[kUSBWritePipeOOL].count0 = 3;
    fMethods[kUSBWritePipeOOL].count1 = 0;
    fMethods[kUSBWritePipeOOL].flags = kIOUCScalarIScalarO;

    fAsyncMethods[kUSBWritePipeOOL].object = this;
    fAsyncMethods[kUSBWritePipeOOL].func =
        (IOAsyncMethod)&IOUSBUserClient::WritePipeAsync;
    fAsyncMethods[kUSBWritePipeOOL].count0 = 3;
    fAsyncMethods[kUSBWritePipeOOL].count1 = 0;
    fAsyncMethods[kUSBWritePipeOOL].flags = kIOUCScalarIScalarO;

    fMethods[kUSBControlReqInOOL].object = this;
    fMethods[kUSBControlReqInOOL].func =
        (IOMethod)&IOUSBUserClient::ControlReqInOOL;
    fMethods[kUSBControlReqInOOL].count0 = sizeof(DevReqOOL);
    fMethods[kUSBControlReqInOOL].count1 = sizeof(UInt32);
    fMethods[kUSBControlReqInOOL].flags = kIOUCStructIStructO;

    fAsyncMethods[kUSBControlReqInOOL].object = this;
    fAsyncMethods[kUSBControlReqInOOL].func =
        (IOAsyncMethod)&IOUSBUserClient::ControlReqInAsync;
    fAsyncMethods[kUSBControlReqInOOL].count0 = sizeof(DevReqOOL);
    fAsyncMethods[kUSBControlReqInOOL].count1 = 0;
    fAsyncMethods[kUSBControlReqInOOL].flags = kIOUCStructIStructO;

    fMethods[kUSBControlReqOutOOL].object = this;
    fMethods[kUSBControlReqOutOOL].func =
        (IOMethod)&IOUSBUserClient::ControlReqOutOOL;
    fMethods[kUSBControlReqOutOOL].count0 = sizeof(DevReqOOL);
    fMethods[kUSBControlReqOutOOL].count1 = 0;
    fMethods[kUSBControlReqOutOOL].flags = kIOUCStructIStructO;

    fAsyncMethods[kUSBControlReqOutOOL].object = this;
    fAsyncMethods[kUSBControlReqOutOOL].func =
        (IOAsyncMethod)&IOUSBUserClient::ControlReqOutAsync;
    fAsyncMethods[kUSBControlReqOutOOL].count0 = sizeof(DevReqOOL);
    fAsyncMethods[kUSBControlReqOutOOL].count1 = 0;
    fAsyncMethods[kUSBControlReqOutOOL].flags = kIOUCStructIStructO;


    fMethods[kUSBResetDevice].object = NULL;
    fMethods[kUSBResetDevice].func = NULL;
    fMethods[kUSBResetDevice].count0 = 0;
    fMethods[kUSBResetDevice].count1 = 0;
    fMethods[kUSBResetDevice].flags = kIOUCScalarIScalarO;

    return true;
}

IOExternalMethod *
IOUSBUserClient::getExternalMethodForIndex( UInt32 index )
{
    if(index >= kNumUSBMethods || fMethods[index].object == NULL)
    	return NULL;
    else
        return &fMethods[index];
}

IOExternalAsyncMethod *
IOUSBUserClient::getExternalAsyncMethodForIndex( UInt32 index )
{
    if(index >= kNumUSBMethods || fAsyncMethods[index].object == NULL)
        return NULL;
    else
        return &fAsyncMethods[index];
}

/*
 * 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
IOUSBUserClient::ControlReqIn(UInt32 pipe, UInt16 bmreqtypeRequest, UInt16 wValue,
		UInt16 wIndex, void *buf, UInt32 *size)
{
    IOReturn res;
    IOUSBDevRequest	req;
    IOUSBPipe *pipeObj;

    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    req.rqDirection = kUSBIn;
    req.rqType = (bmreqtypeRequest >> kUSBRqTypeShift) & kUSBRqTypeMask;
    req.rqRecipient = bmreqtypeRequest & kUSBRqRecipientMask;
    req.bRequest = bmreqtypeRequest >> 8;
    OSWriteLittleInt16(&req.wValue, 0, wValue);
    OSWriteLittleInt16(&req.wIndex, 0, wIndex);
    OSWriteLittleInt16(&req.wLength, 0, *size);
    req.pData = buf;
    res = pipeObj->controlRequest(&req);

    if(res == kIOReturnSuccess) {
        *size = req.wLenDone;
    }
    else {
        IOLog("IOUSBUserClient::ControlReqIn err:0x%x\n", res);
        *size = 0;
    }
    return res;
}

/*
 * 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
IOUSBUserClient::ControlReqOut(UInt32 pipe, UInt16 bmreqtypeRequest, UInt16 wValue,
                UInt16 wIndex, void *buf, UInt32 size)
{
    IOReturn res;
    IOUSBDevRequest	req;
    IOUSBPipe *pipeObj;

    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    req.rqDirection = kUSBOut;
    req.rqType = (bmreqtypeRequest >> kUSBRqTypeShift) & kUSBRqTypeMask;
    req.rqRecipient = bmreqtypeRequest & kUSBRqRecipientMask;
    req.bRequest = bmreqtypeRequest >> 8;
    OSWriteLittleInt16(&req.wValue, 0, wValue);
    OSWriteLittleInt16(&req.wIndex, 0, wIndex);
    OSWriteLittleInt16(&req.wLength, 0, size);
    req.pData = buf;
    res = pipeObj->controlRequest(&req);

    if(kIOReturnSuccess != res) {
        IOLog("IOUSBUserClient::ControlReqOut err:0x%x\n", res);
    }
    return res;
}

IOReturn
IOUSBUserClient::ControlReqInOOL(DevReqOOL *reqIn, UInt32 *sizeOut, IOByteCount inCount,
                                IOByteCount *outCount)
{
    IOReturn 			res;
    IOUSBDevRequestDesc		req;
    IOMemoryDescriptor *	mem;
    IOUSBPipe *			pipeObj;

    if(inCount != sizeof(DevReqOOL) ||
       *outCount != sizeof(UInt32))
        return kIOReturnBadArgument;

    pipeObj = fPipes[reqIn->pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->buf, reqIn->sizeIn,
                                          kIODirectionIn,
                                          fTask);
    if(!mem) {
        return kIOReturnNoMemory;
    }
    req.rqDirection = kUSBIn;
    req.rqType = (reqIn->bmreqtype >> kUSBRqTypeShift) & kUSBRqTypeMask;
    req.rqRecipient = reqIn->bmreqtype & kUSBRqRecipientMask;
    req.bRequest = reqIn->request;
    OSWriteLittleInt16(&req.wValue, 0, reqIn->wValue);
    OSWriteLittleInt16(&req.wIndex, 0, reqIn->wIndex);
    OSWriteLittleInt16(&req.wLength, 0, reqIn->sizeIn);
    req.pData = mem;

    res = mem->prepare();
    if(res == kIOReturnSuccess)
        res = pipeObj->controlRequest(&req);
    mem->complete();
    mem->release();
    if(res == kIOReturnSuccess) {
        *sizeOut = req.wLenDone;
    }
    else {
        IOLog("IOUSBUserClient::ControlReqInOOL err:0x%x\n", res);
        *sizeOut = 0;
    }
    *outCount = sizeof(UInt32);

    return res;
}

typedef struct AsyncPB {
    OSAsyncReference fAsyncRef;
    UInt32 fMax;
    IOMemoryDescriptor *fMem;
};

IOReturn
IOUSBUserClient::ControlReqInAsync(OSAsyncReference asyncRef,
                                   DevReqOOL *reqIn, IOByteCount inCount)
{
    IOReturn 			res;
    IOUSBDevRequestDesc		req;
    IOUSBPipe *			pipeObj;

    IOUSBCompletion		tap;
    AsyncPB * 			pb = NULL;
    IOMemoryDescriptor *	mem = NULL;

    if(inCount != sizeof(DevReqOOL))
        return kIOReturnBadArgument;

    pipeObj = fPipes[reqIn->pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    do {
        mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->buf,
                                            reqIn->sizeIn,
                                            kIODirectionIn,
                                            fTask);
        if(!mem) {
            res = kIOReturnNoMemory;
            break;
        }
        res = mem->prepare();
        if(res != kIOReturnSuccess)
            break;
        pb = (AsyncPB *)IOMalloc(sizeof(AsyncPB));
        if(!pb) {
            res = kIOReturnNoMemory;
            break;
        }

        req.rqDirection = kUSBIn;
        req.rqType = (reqIn->bmreqtype >> kUSBRqTypeShift) & kUSBRqTypeMask;
        req.rqRecipient = reqIn->bmreqtype & kUSBRqRecipientMask;
        req.bRequest = reqIn->request;
        OSWriteLittleInt16(&req.wValue, 0, reqIn->wValue);
        OSWriteLittleInt16(&req.wIndex, 0, reqIn->wIndex);
        OSWriteLittleInt16(&req.wLength, 0, reqIn->sizeIn);
        req.pData = mem;

        bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
        pb->fMax = reqIn->sizeIn;
        pb->fMem = mem;
        tap.target = this;
        tap.action = &ReqComplete;
        tap.parameter = pb;
        res = pipeObj->controlRequest(&req, &tap);

    } while (false);
    
    if(res != kIOReturnSuccess) {
        IOLog("ControlReqInAsync err 0x%x\n", res);
        if(mem) {
            mem->complete();
            mem->release();
        }
        if(pb)
            IOFree(pb, sizeof(*pb));
    }

    return res;
}

void
IOUSBUserClient::ReqComplete(void *obj, void *param, IOReturn res, UInt32 remaining)
{
    void *	args[1];
    AsyncPB * pb = (AsyncPB *)param;

    if(res == kIOReturnSuccess) {
        args[0] = (void *)(pb->fMax - remaining);
    }
    else {
        IOLog("IOUSBUserClient::ReqComplete err:0x%x\n", res);
        args[0] = 0;
    }
    pb->fMem->complete();
    pb->fMem->release();
    sendAsyncResult(pb->fAsyncRef, res, args, 1);

    IOFree(pb, sizeof(*pb));
}

IOReturn
IOUSBUserClient::ControlReqOutOOL(DevReqOOL *reqIn, IOByteCount inCount)
{
    IOReturn 			res;
    IOUSBDevRequestDesc		req;
    IOMemoryDescriptor *	mem;
    IOUSBPipe *			pipeObj;

    if(inCount != sizeof(DevReqOOL))
        return kIOReturnBadArgument;

    pipeObj = fPipes[reqIn->pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;


    mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->buf,
		reqIn->sizeIn, kIODirectionOut, fTask);
    if(!mem) {
        return kIOReturnNoMemory;
    }
    req.rqDirection = kUSBOut;
    req.rqType = (reqIn->bmreqtype >> kUSBRqTypeShift) & kUSBRqTypeMask;
    req.rqRecipient = reqIn->bmreqtype & kUSBRqRecipientMask;
    req.bRequest = reqIn->request;
    OSWriteLittleInt16(&req.wValue, 0, reqIn->wValue);
    OSWriteLittleInt16(&req.wIndex, 0, reqIn->wIndex);
    OSWriteLittleInt16(&req.wLength, 0, reqIn->sizeIn);
    req.pData = mem;

    res = mem->prepare();
    if(res == kIOReturnSuccess)
        res = pipeObj->controlRequest(&req);
    mem->complete();
    mem->release();
    return res;
}

IOReturn
IOUSBUserClient::ControlReqOutAsync(OSAsyncReference asyncRef,
                    DevReqOOL *reqIn, IOByteCount inCount)
{
    IOReturn 			res;
    IOUSBDevRequestDesc		req;
    IOUSBPipe *			pipeObj;

    IOUSBCompletion		tap;
    AsyncPB * 			pb = NULL;
    IOMemoryDescriptor *	mem = NULL;

    if(inCount != sizeof(DevReqOOL))
        return kIOReturnBadArgument;

    pipeObj = fPipes[reqIn->pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    do {
        mem = IOMemoryDescriptor::withAddress((vm_address_t)reqIn->buf,
                                            reqIn->sizeIn,
                                            kIODirectionOut,
                                            fTask);
        if(!mem) {
            res = kIOReturnNoMemory;
            break;
        }
        res = mem->prepare();
        if(res != kIOReturnSuccess)
            break;
        pb = (AsyncPB *)IOMalloc(sizeof(AsyncPB));
        if(!pb) {
            res = kIOReturnNoMemory;
            break;
        }

        req.rqDirection = kUSBOut;
        req.rqType = (reqIn->bmreqtype >> kUSBRqTypeShift) & kUSBRqTypeMask;
        req.rqRecipient = reqIn->bmreqtype & kUSBRqRecipientMask;
        req.bRequest = reqIn->request;
        OSWriteLittleInt16(&req.wValue, 0, reqIn->wValue);
        OSWriteLittleInt16(&req.wIndex, 0, reqIn->wIndex);
        OSWriteLittleInt16(&req.wLength, 0, reqIn->sizeIn);
        req.pData = mem;

        bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
        pb->fMax = reqIn->sizeIn;
        pb->fMem = mem;
        tap.target = this;
        tap.action = &ReqComplete;	// Want same number of reply args as for ControlReqIn
        tap.parameter = pb;
        res = pipeObj->controlRequest(&req, &tap);

    } while (false);

    if(res != kIOReturnSuccess) {
        IOLog("controlRequest err 0x%x\n", res);
        if(mem) {
            mem->complete();
            mem->release();
        }
        if(pb)
            IOFree(pb, sizeof(*pb));
    }

    return res;
}


// Controlling pipe state

IOReturn IOUSBUserClient::getPipeStatus(UInt32 pipe)
{
    IOUSBPipe *pipeObj;
    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;
    return pipeObj->status();
}

IOReturn IOUSBUserClient::abortPipe(UInt32 pipe)
{
    IOUSBPipe *pipeObj;
    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;
    return pipeObj->abort();
}

IOReturn IOUSBUserClient::resetPipe(UInt32 pipe)
{
    IOUSBPipe *pipeObj;
    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;
    return pipeObj->reset();
}

IOReturn IOUSBUserClient::setPipeIdle(UInt32 pipe)
{
    return(0);
}

IOReturn IOUSBUserClient::setPipeActive(UInt32 pipe)
{
    return(0);
}

IOReturn IOUSBUserClient::clearPipeStall(UInt32 pipe)
{
    IOUSBPipe *pipeObj;
    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;
    return pipeObj->clearStall();
}

IOReturn
IOUSBUserClient::ReadPipe(UInt32 pipe, void *buf, UInt32 *size)
{
    IOReturn 			res;
    IOMemoryDescriptor *	mem;
    IOUSBPipe *			pipeObj;

    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    mem = IOMemoryDescriptor::withAddress(buf, *size, kIODirectionIn);
    if(!mem) {
        *size = 0;
        return kIOReturnNoMemory;
    }
    res = pipeObj->read(mem, 0, size);
    if(kIOReturnSuccess != res)
        *size = 0;
    mem->release();

    return res;
}

IOReturn
IOUSBUserClient::WritePipe(UInt32 pipe, void *buf, UInt32 size)
{
    IOReturn 			res;
    IOMemoryDescriptor *	mem;
    IOUSBPipe *			pipeObj;

    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    mem = IOMemoryDescriptor::withAddress(buf, size, kIODirectionOut);
    if(!mem) {
        res = kIOReturnNoMemory;
    }
    else {
        res = pipeObj->write(mem);
        mem->release();
    }
    return res;
}

/*
 * Out of line version of read pipe - the buffer isn't mapped (yet) into the kernel task
 */
IOReturn
IOUSBUserClient::ReadPipeOOL(UInt32 pipe, void *buf, UInt32 sizeIn, UInt32 *sizeOut)
{
    IOReturn 			res;
    IOMemoryDescriptor *	mem;
    IOUSBPipe *			pipeObj;

    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    mem = IOMemoryDescriptor::withAddress((vm_address_t)buf, sizeIn,
                                          kIODirectionIn,
                                          fTask);
    if(!mem) {
        *sizeOut = 0;
        return kIOReturnNoMemory;
    }
    res = mem->prepare();
    if(res == kIOReturnSuccess)
        res = pipeObj->read(mem, 0, sizeOut);
    mem->complete();
    if(kIOReturnSuccess != res)
        *sizeOut = 0;
    mem->release();

    return res;
}

/*
 * Out of line version of write pipe - the buffer isn't mapped (yet) into the kernel task
 */
IOReturn
IOUSBUserClient::WritePipeOOL(UInt32 pipe, void *buf, UInt32 size)
{
    IOReturn 			res;
    IOMemoryDescriptor *	mem;
    IOUSBPipe *			pipeObj;

    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    mem = IOMemoryDescriptor::withAddress((vm_address_t)buf, size,
                                          kIODirectionOut,
                                          fTask);
    if(!mem) {
        res = kIOReturnNoMemory;
    }
    else {
        res = mem->prepare();
        if(res == kIOReturnSuccess)
            res = pipeObj->write(mem);
        mem->complete();
        mem->release();
    }
    return res;
}

/*
 * Async version of read pipe - the buffer isn't mapped (yet) into the kernel task
 */
IOReturn
IOUSBUserClient::ReadPipeAsync(OSAsyncReference asyncRef,
                               UInt32 pipe, void *buf, UInt32 size)
{
    IOReturn 			res;
    IOUSBPipe *			pipeObj;
    IOUSBCompletion		tap;
    IOMemoryDescriptor *	mem = NULL;
    AsyncPB * 			pb = NULL;

    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    do {
        mem = IOMemoryDescriptor::withAddress((vm_address_t)buf, size,
                                            kIODirectionIn,
                                            fTask);
        if(!mem) {
            res = kIOReturnNoMemory;
            break;
        }
        res = mem->prepare();
        if(res != kIOReturnSuccess)
            break;

        pb = (AsyncPB *)IOMalloc(sizeof(AsyncPB));
        if(!pb) {
            res = kIOReturnNoMemory;
            break;
        }

        bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
        pb->fMax = size;
        pb->fMem = mem;
        tap.target = this;
        tap.action = &ReqComplete;
        tap.parameter = pb;
        res = pipeObj->read(mem, &tap, NULL);
    } while (false);
    if(res != kIOReturnSuccess) {
        if(mem) {
            mem->complete();
            mem->release();
        }
        if(pb)
            IOFree(pb, sizeof(*pb));
    }

    return res;
}

/*
 * Async version of write pipe - the buffer isn't mapped (yet) into the kernel task
 */
IOReturn
IOUSBUserClient::WritePipeAsync(OSAsyncReference asyncRef,
                                UInt32 pipe, void *buf, UInt32 size)
{
    IOReturn 			res;
    IOUSBPipe *			pipeObj;
    IOUSBCompletion		tap;
    IOMemoryDescriptor *	mem = NULL;
    AsyncPB * 			pb = NULL;

    pipeObj = fPipes[pipe];
    if(!pipeObj)
        return kIOUSBUnknownPipeErr;

    do {
        mem = IOMemoryDescriptor::withAddress((vm_address_t)buf, size,
                                            kIODirectionOut,
                                            fTask);
        if(!mem) {
            res = kIOReturnNoMemory;
            break;
        }
        res = mem->prepare();
        if(res != kIOReturnSuccess)
            break;

        pb = (AsyncPB *)IOMalloc(sizeof(AsyncPB));
        if(!pb) {
            res = kIOReturnNoMemory;
            break;
        }

        bcopy(asyncRef, pb->fAsyncRef, sizeof(OSAsyncReference));
        pb->fMax = size;
        pb->fMem = mem;
        tap.target = this;
        tap.action = &ReqComplete;
        tap.parameter = pb;
        res = pipeObj->write(mem, &tap);
    } while (false);
    if(res != kIOReturnSuccess) {
        if(mem) {
            mem->complete();
            mem->release();
        }
        if(pb)
            IOFree(pb, sizeof(*pb));
    }
    return res;
}

IOReturn IOUSBUserClient::ClosePipe(UInt32 pipe)
{
    fPipes[pipe] = 0;
    return kIOReturnSuccess;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

OSDefineMetaClassAndStructors(IOUSBDeviceUserClient, IOUSBUserClient)

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

IOUSBDeviceUserClient *IOUSBDeviceUserClient::withTask(task_t owningTask)
{
    IOUSBDeviceUserClient *me;

    me = new IOUSBDeviceUserClient;
    if(me) {
        if(!me->init()) {
            me->release();
            return NULL;
        }
        me->fTask = owningTask;
    }
    return me;
}

bool IOUSBDeviceUserClient::start( IOService * provider )
{
    assert(OSDynamicCast(IOUSBDevice, provider));
    if(!IOUSBUserClient::start(provider))
        return false;
    fOwner = (IOUSBDevice *)provider;
    fOwner->retain();
    fInterfaces = OSSet::withCapacity(1);
    if(!fInterfaces)
        return false;

    // initialize the call structures valid for IOUSBDevice
    fMethods[kUSBSetConfig].object = provider;
    fMethods[kUSBSetConfig].func = (IOMethod)&IOUSBDevice::SetConfiguration;
    fMethods[kUSBSetConfig].count0 = 1;
    fMethods[kUSBSetConfig].count1 = 0;
    fMethods[kUSBSetConfig].flags = kIOUCScalarIScalarO;

    fMethods[kUSBGetConfigDescriptor].object = this;
    fMethods[kUSBGetConfigDescriptor].func =
        (IOMethod)&IOUSBDeviceUserClient::GetConfigDescriptor;
    fMethods[kUSBGetConfigDescriptor].count0 = 1;
    fMethods[kUSBGetConfigDescriptor].count1 = 0xffffffff; // variable
    fMethods[kUSBGetConfigDescriptor].flags = kIOUCScalarIStructO;

    fMethods[kUSBOpenPipe].object = this;
    fMethods[kUSBOpenPipe].func =
        (IOMethod)&IOUSBDeviceUserClient::OpenPipe;
    fMethods[kUSBOpenPipe].count0 = 4;
    fMethods[kUSBOpenPipe].count1 = 1;
    fMethods[kUSBOpenPipe].flags = kIOUCScalarIScalarO;

    fMethods[kUSBResetDevice].object = provider;
    fMethods[kUSBResetDevice].func =
        (IOMethod)&IOUSBDevice::resetDevice;
    fMethods[kUSBResetDevice].count0 = 0;
    fMethods[kUSBResetDevice].count1 = 0;
    fMethods[kUSBResetDevice].flags = kIOUCScalarIScalarO;

    return true;
}

IOReturn IOUSBDeviceUserClient::clientClose( void )
{
    if(fInterfaces) {
        OSCollectionIterator *iter;

        iter = OSCollectionIterator::withCollection(fInterfaces);
        if(iter) {
            OSObject *obj;
            while( (obj=iter->getNextObject()) ) {
                ((IOService *)obj)->close(this);
            }
            iter->release();
        }
        fInterfaces->release();
        fInterfaces = NULL;
    }
    if(fOwner) {
        detach( fOwner);
        fOwner->release();
        fOwner = NULL;
    }
    return IOUSBUserClient::clientClose();
}

IOReturn
IOUSBDeviceUserClient::OpenPipe(UInt32 configIndex,
                          UInt32 intfNo, UInt32 altSet, UInt32 endPtOff, UInt32 *pipe)
{
    IOReturn res;
    IOUSBInterface *intf;
    const IOUSBInterfaceDescriptor *id = NULL;
    UInt8 configVal;
    IOUSBDevice::FindInterfaceRequest request;
    UInt32 i;
    const IOUSBConfigurationDescriptor *config;

    config = fOwner->getFullConfigurationDescriptor(configIndex);
    configVal = config->configValue;

    intf = NULL;
    request.theClass = 0;
    request.subClass = 0;
    request.protocol = 0;
    request.maxPower = 0;

    while( (intf = fOwner->findNextInterface(intf, &request))) {
        if(intf->getConfigValue() == configVal) {
            id = intf->interfaceDescriptor();
            if(id->interfaceNumber == intfNo &&
                    id->alternateSetting == altSet)
                break;
        }
        intf->release();
        // Reset request fields to wildcard values.
        request.theClass = 0;
        request.subClass = 0;
        request.protocol = 0;
        request.maxPower = 0;
    }

    if(!intf)
        return kIOUSBInterfaceNotFound;

    if(id->numEndpoints <= endPtOff)
        return kIOUSBUnknownPipeErr;

    do {
        IOUSBFindEndpointRequest findPipe;
        IOUSBPipe *pipeObj;
        if(!fInterfaces->member(intf)) {
            if(!intf->open(this)) {
                res = kIOReturnExclusiveAccess;
                break;
            }
            fInterfaces->setObject(intf);
        }

        // Loop through pipes the requested number of times
        pipeObj = NULL;
        endPtOff++; // Easier to be 1-based.
        for(i=0; i<endPtOff; i++) {
            findPipe.type = kUSBAnyType;
            findPipe.direction = kUSBAnyDirn;
            pipeObj = intf->findNextPipe(pipeObj, &findPipe);
            if(pipeObj == NULL) {
                res = kIOUSBEndpointNotFound;
                break;
            }
        }
        if(i != endPtOff) {
           res = kIOUSBEndpointNotFound;
            break;
        }
        for(i=1; i<kUSBMaxPipes; i++) {
            if(!fPipes[i]) {
                fPipes[i] = pipeObj;
                break;
            }
        }
        if(i >= kUSBMaxPipes) {
            res = kIOUSBTooManyPipesErr;
            break;
        }
        *pipe = i;
        res = kIOReturnSuccess;
    } while(false);
    if(intf)
        intf->release();
    return res;
}

IOReturn
IOUSBDeviceUserClient::GetConfigDescriptor(UInt8 configIndex,
                        IOUSBConfigurationDescriptorPtr desc, UInt32 *size)
{
    UInt16 length;
    const IOUSBConfigurationDescriptor *cached;

    cached = fOwner->getFullConfigurationDescriptor(configIndex);
    length = OSReadLittleInt16(&cached->totalLength, 0);
    if(length < *size)
        *size = length;
    bcopy(cached, desc, *size);
    return kIOReturnSuccess;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

OSDefineMetaClassAndStructors(IOUSBInterfaceUserClient, IOUSBUserClient)

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

IOUSBInterfaceUserClient *IOUSBInterfaceUserClient::withTask(task_t owningTask)
{
    IOUSBInterfaceUserClient *me;

    me = new IOUSBInterfaceUserClient;
    if(me) {
        if(!me->init()) {
            me->release();
            return NULL;
        }
        me->fTask = owningTask;
    }
    return me;
}

bool IOUSBInterfaceUserClient::start( IOService * provider )
{
    assert(OSDynamicCast(IOUSBInterface, provider));
    if(!IOUSBUserClient::start(provider))
        return false;
    fOwner = (IOUSBInterface *)provider;
    fOwner->retain();

// initialize the call structures valid for IOUSBInterface
    fMethods[kUSBGetConfigDescriptor].object = this;
    fMethods[kUSBGetConfigDescriptor].func =
        (IOMethod)&IOUSBInterfaceUserClient::GetConfigDescriptor;
    fMethods[kUSBGetConfigDescriptor].count0 = 1;
    fMethods[kUSBGetConfigDescriptor].count1 = 0xffffffff; // variable
    fMethods[kUSBGetConfigDescriptor].flags = kIOUCScalarIStructO;

    fMethods[kUSBOpenPipe].object = this;
    fMethods[kUSBOpenPipe].func =
        (IOMethod)&IOUSBInterfaceUserClient::OpenPipe;
    fMethods[kUSBOpenPipe].count0 = 4;
    fMethods[kUSBOpenPipe].count1 = 1;
    fMethods[kUSBOpenPipe].flags = kIOUCScalarIScalarO;

    return true;
}

IOReturn IOUSBInterfaceUserClient::clientClose( void )
{
    if(fOwner) {
        if(fOpen) {
            fOwner->close(this);
            fOpen = false;
	}
        detach( fOwner);
        fOwner->release();
        fOwner = NULL;
    }
    return IOUSBUserClient::clientClose();
}

IOReturn
IOUSBInterfaceUserClient::OpenPipe(UInt32 configIndex,
                          UInt32 intfNo, UInt32 altSet, UInt32 endPtOff, UInt32 *pipe)
{
    IOReturn res;
    const IOUSBInterfaceDescriptor *id = fOwner->interfaceDescriptor();
    UInt32 i;
    const IOUSBConfigurationDescriptor *config;

    config = fOwner->device()->getFullConfigurationDescriptor(configIndex);

    if(config->configValue != fOwner->getConfigValue())
        return kIOUSBConfigNotFound;

    if(intfNo != id->interfaceNumber ||
       altSet != id->alternateSetting)
        return kIOUSBInterfaceNotFound;

    if(id->numEndpoints <= endPtOff)
        return kIOUSBUnknownPipeErr;

    do {
        IOUSBFindEndpointRequest findPipe;
        IOUSBPipe *pipeObj;
        if(!fOpen) {
            if(!fOwner->open(this)) {
                res = kIOReturnExclusiveAccess;
                break;
            }
            fOpen = true;
        }

        // Loop through pipes the requested number of times
        pipeObj = NULL;
        endPtOff++; // Easier to be 1-based.
        for(i=0; i<endPtOff; i++) {
            findPipe.type = kUSBAnyType;
            findPipe.direction = kUSBAnyDirn;
            pipeObj = fOwner->findNextPipe(pipeObj, &findPipe);
            if(pipeObj == NULL) {
                res = kIOUSBEndpointNotFound;
                break;
            }
        }
        if(i != endPtOff) {
           res = kIOUSBEndpointNotFound;
            break;
        }
        for(i=1; i<kUSBMaxPipes; i++) {
            if(!fPipes[i]) {
                fPipes[i] = pipeObj;
                break;
            }
        }
        if(i >= kUSBMaxPipes) {
            res = kIOUSBTooManyPipesErr;
            break;
        }
        *pipe = i;
        res = kIOReturnSuccess;
    } while(false);
    return res;
}

IOReturn
IOUSBInterfaceUserClient::GetConfigDescriptor(UInt8 configIndex,
                        IOUSBConfigurationDescriptorPtr desc, UInt32 *size)
{
    UInt16 length;
    const IOUSBConfigurationDescriptor *cached;

    cached = fOwner->device()->getFullConfigurationDescriptor(configIndex);
    length = OSReadLittleInt16(&cached->totalLength, 0);
    if(length < *size)
        *size = length;
    bcopy(cached, desc, *size);
    return kIOReturnSuccess;
}