Source to iokit/Families/IOUSBBus/IOUSBController.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) 1998 Apple Computer, Inc.  All rights reserved.
 *
 * HISTORY
 * 08 Dec 98 ehewitt created.
 *
 */

#include <libkern/OSByteOrder.h>

#define DEBUGGING_LEVEL 0	// 1 = low; 2 = high; 3 = extreme
#define DEBUGLOG kprintf

#include <libkern/c++/OSDictionary.h>
#include <libkern/c++/OSData.h>
#include <IOKit/usb/IOUSBController.h>
#include <IOKit/usb/IOUSBDevice.h>
#include "IOUSBRootHubDevice.h"
#include <IOKit/assert.h>
#include <IOKit/IOSyncer.h>

enum {
    kSetupSent  = 0x01,
    kDataSent   = 0x02,
    kStatusSent = 0x04,
    kSetupBack  = 0x10,
    kDataBack   = 0x20,
    kStatusBack = 0x40
};

#define kUSBSetup kUSBNone

void IOUSBSyncCompletion(void *	target,
                    void * 	parameter,
                    IOReturn	status,
                    UInt32	bufferSizeRemaining)
{
    IOSyncer *syncer = (IOSyncer *)target;

    if(parameter != NULL) {
        *(UInt32 *)parameter -= bufferSizeRemaining;
    }

    syncer->signal(status);
}

void IOUSBSyncIsoCompletion(void *target, void * 	parameter,
                                 IOReturn	status,
                                 IOUSBIsocFrame *pFrames)
{
    IOSyncer *syncer = (IOSyncer *)target;
    syncer->signal(status);
}


#define super IOUSBBus

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

OSDefineMetaClass( IOUSBController, IOUSBBus )
OSDefineAbstractStructors(IOUSBController, IOUSBBus)

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

bool IOUSBController::init(OSDictionary * propTable)
{
    if (!super::init(propTable))  return false;

    return (true);
}

bool IOUSBController::start( IOService * provider )
{
    IOUSBDeviceDescriptor desc;

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

    do {

        /*
         * Initialize the Workloop and Command gate
         */
        _workLoop = IOWorkLoop::workLoop();
        if (!_workLoop)
        {
            IOLog("%s: unable to create workloop\n", getName());
            break;
        }

        _commandGate = IOCommandGate:: commandGate(this, NULL);
        if (!_commandGate)
        {
            IOLog("%s: unable to create command gate\n", getName());
            break;
        }

        if (_workLoop->addEventSource(_commandGate) != kIOReturnSuccess)
        {
            IOLog("%s: unable to add command gate\n", getName());
            break;
        }
        
        /*
         * Initialize the UIM
         */
        if (UIMInitialize(provider) != kIOReturnSuccess)
        {
            IOLog("%s: unable to initialize UIM\n", getName());
            break;
        }

        /*
         * Initialize device zero
         */
	_devZeroLock = IOLockAlloc();
            
        /*
         * Create the root hub device
         */
        if (getRootHubDeviceDescriptor( &desc ) != kIOReturnSuccess)
        {
            IOLog("%s: unable to get root hub descriptor\n", getName());
            break;
        }

        _rootHubDevice = new IOUSBRootHubDevice;
        
        CreateDevice(_rootHubDevice, getNewAddress(), &desc,
                         kUSBHighSpeed, kUSB500mAAvailable);
        if (_rootHubDevice == 0)
        {
            IOLog("%s: unable to initialize root hub\n", getName());
            break;
        }
        _rootHubDevice->registerService();
        
        return(true);

    } while (false);

    if (_workLoop)	_workLoop->release();
    if (_commandGate)	_commandGate->release();

    return( false );
}

IOWorkLoop *IOUSBController::getWorkLoop() const
{
    return _workLoop;
}

IOUSBDevice *IOUSBController::newDevice()
{
    return(new IOUSBDevice);
}

/*
 * CreateDevice:
 *  This method just creates the device so we can minimally talk to it, e.g.
 *  get it's descriptor.  After this method, the device is not ready for
 *  prime time.
 */
IOReturn IOUSBController::CreateDevice(IOUSBDevice 		*newDevice,
                                           USBDeviceAddress	deviceAddress,
                                           IOUSBDeviceDescriptor *desc,
                                           UInt8		speed,
                                           UInt32		powerAvailable)
{
    OSDictionary 	*propTable = 0;
    OSData		*data;
    OSNumber		*offset;
    char		location[8];

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("%s: CreateDevice: addr=%d, speed=%s, power=%d\n", getName(), 
             deviceAddress, speed ? "low" : "high", powerAvailable*2);
#endif
    
    do {
        propTable = OSDictionary::withCapacity(4);
        if (!propTable)
            break;

        if (desc) {
	    data = OSData::withBytes( (void *)desc, sizeof( *desc ));
	    if(data) {
                propTable->setObject("device descriptor", data);
		data->release();
	    }
            offset = OSNumber::withNumber(desc->deviceClass, 8);
            if(offset) {
                propTable->setObject("class", offset);
                offset->release();
            }
            offset = OSNumber::withNumber(desc->deviceSubClass, 8);
            if(offset) {
                propTable->setObject("subClass", offset);
                offset->release();
            }
            offset = OSNumber::withNumber(desc->protocol, 8);
            if(offset) {
                propTable->setObject("protocol", offset);
                offset->release();
            }
            offset = OSNumber::withNumber(OSReadLittleInt16(&desc->vendor, 0), 16);
            if(offset) {
                propTable->setObject("vendor", offset);
                offset->release();
            }
            offset = OSNumber::withNumber(OSReadLittleInt16(&desc->product, 0), 16);
            if(offset) {
                propTable->setObject("product", offset);
                offset->release();
            }
            offset = OSNumber::withNumber(OSReadLittleInt16(&desc->devRel, 0), 16);
            if(offset) {
                propTable->setObject("version", offset);
                offset->release();
            }
	}

	data = OSData::withBytes( (void *)&deviceAddress,
                                    sizeof( deviceAddress ));
	if (data) {
            propTable->setObject("device address", data);
	    data->release();
	}

	data = OSData::withBytes( (void *)&powerAvailable,
                                    sizeof( powerAvailable ));
	if (data) {
            propTable->setObject("bus power available", data);
	    data->release();
	}

	data = OSData::withBytes( (void *)&speed, sizeof( speed ));
	if (data) {
            propTable->setObject("low speed device", data);
	    data->release();
	}

        if (!newDevice->init(propTable))
            break;
        
        propTable->release();	// done with it after init
        propTable = 0;

        sprintf( location, "%x", deviceAddress );
        newDevice->setLocation(location);

        if (!newDevice->attach(this))
            break;

        return(kIOReturnSuccess);

    } while (false);

    if (propTable)
        propTable->release();

    return(kIOReturnNoMemory);
}

/*
 * deviceRequest:
 * Queue up a low level device request.  It's very simple because the
 * device has done all the error checking.  Commands get allocated here and get
 * deallocated in the handlers.
 *
 */ 
IOReturn IOUSBController::deviceRequest(IOUSBDevRequest *	request,
                                        IOUSBCompletion *	completion,
                                        USBDeviceAddress address, UInt8 ep)
{
    // FIXME -- should preallocate a bunch of 'command's
    IOUSBCommand *command = (IOUSBCommand *)IOMalloc(sizeof(IOUSBCommand));
    IOReturn	err = kIOReturnSuccess;

    do
    {
        command->selector	= DEVICE_REQUEST;
        command->request	= request;
        command->address	= address;
	command->endpoint	= ep;
        command->type		= kUSBControl;
        command->buffer		= 0; // no buffer for device requests
        command->completion	= *completion;

        if (_commandGate == 0)
        {
            err = kIOReturnInternalError;
            break;
        }

        err = _commandGate->runAction(doControlTransfer, command);
        if ( err)
            break;
            
        return (err);
    } while (0);

    /* Free/give back the command */
    IOFree(command, sizeof(IOUSBCommand));

    return(err);
}

IOReturn IOUSBController::deviceRequest(IOUSBDevRequestDesc *	request,
                                        IOUSBCompletion *	completion,
                                        USBDeviceAddress address, UInt8 ep)
{
    // FIXME -- should preallocate a bunch of 'command's
    IOUSBCommand *command = (IOUSBCommand *)IOMalloc(sizeof(IOUSBCommand));
    IOReturn	err = kIOReturnSuccess;

    do
    {
        command->selector	= DEVICE_REQUEST_DESC;
	// IOUSBDevRequest and IOUSBDevRequestDesc are same except for
        // pData (void * or descriptor).
        command->request	= (IOUSBDevRequest *)request;
        command->address	= address;
        command->endpoint	= ep;
        command->type		= kUSBControl;
        command->buffer		= request->pData;
        command->completion	= *completion;

        if (_commandGate == 0)
        {
            err = kIOReturnInternalError;
            break;
        }

        err = _commandGate->runAction(doControlTransfer, command);
        if ( err)
            break;

        return (err);
    } while (0);

    /* Free/give back the command */
    IOFree(command, sizeof(IOUSBCommand));

    return(err);
}

USBDeviceAddress IOUSBController::getNewAddress(void)
{
    int i;
    bool assigned[kUSBMaxDevices];
    OSIterator * clients;

    bzero(assigned, sizeof(assigned));

    clients = getClientIterator();
    if(clients) {
        OSObject *next;
        while((next = clients->getNextObject())) {
            IOUSBDevice *testIt = OSDynamicCast(IOUSBDevice, next);
            if(testIt) {
                assigned[testIt->address()] = true;
            }
        }
        clients->release();
    }

    for(i=1; i<kUSBMaxDevices; i++) {
	if(!assigned[i])
            return i;
    }

    return(0);	// No free device addresses!
}

/*
 * ControlPacket:
 *   Send a USB control packet which consists of at least two stages: setup
 * and status.   Optionally there can be multiple data stages.
 *
 */
IOReturn IOUSBController::ControlTransaction(IOUSBCommand *command)
{
    IOUSBDevRequest	*request = command->request;
    UInt8		direction = request->rqDirection;
    UInt8		endpoint = command->endpoint;
    UInt16		wLength = OSReadLittleInt16(&request->wLength, 0);
    IOReturn		err = kIOReturnSuccess;
    IOUSBCompletion	completion;
    
    do
    {
        // Setup Stage
#if (DEBUGGING_LEVEL > 1)
        DEBUGLOG("\tQueueing Setup TD (dir=%d) packet=0x%lx 0x%lx\n",
              direction, *(UInt32*)request, *((UInt32*)request+1));
#endif

        completion.target    = (void *)this;
        completion.action    = &IOUSBController::ControlPacketHandler;
        completion.parameter = (void *)command;

        command->dataRemaining = wLength;
        command->stage = kSetupSent;
        err = UIMCreateControlTransfer(
                    command->address	/*functionAddress*/,
                    endpoint	 	/*endpointNumber*/,
                    completion   	/*completion*/,
                    request		/*packet*/,
                    true		/*bufferRounding*/,
                    8			/*packet size*/,
                    kUSBSetup		/*direction*/);
        if (err)
        {
            IOLog("ControlTransaction: control packet 1 error %d\n", err);
            break;
        }

        // Data Stage
        if (wLength != 0 && request->pData != 0)
        {
#if (DEBUGGING_LEVEL > 1)
            DEBUGLOG("\tQueueing Data TD (dir=%d, wLength=0x%x, pData=%lx)\n", direction,
                  wLength, request->pData);
#endif
            command->stage |= kDataSent;
            if(command->selector == DEVICE_REQUEST_DESC)
                err = UIMCreateControlTransfer(
                        command->address	/*functionAddress*/,
                        endpoint		/*endpointNumber*/,
                        completion   		/*completion*/,
                        command->buffer		/*buffer*/,
                        true			/*bufferRounding*/,
                        wLength			/*bufferSize*/,
                        direction		/*direction*/);
            else
                err = UIMCreateControlTransfer(
                        command->address	/*functionAddress*/,
                        endpoint		/*endpointNumber*/,
                        completion   		/*completion*/,
                        request->pData		/*buffer*/,
                        true			/*bufferRounding*/,
                        wLength			/*bufferSize*/,
                        direction		/*direction*/);
            if (err)
            {
                IOLog("ControlTransaction: control packet 2 error %d\n", err);
                break;
            }
            
            direction = kUSBOut + kUSBIn - direction; // swap direction
        }
        else
            direction = kUSBIn;

        // Status Stage
#if (DEBUGGING_LEVEL > 1)
        DEBUGLOG("\tQueueing Status TD (dir=%d)\n", direction);
#endif
        command->stage |= kStatusSent;
        err = UIMCreateControlTransfer(
                    command->address	/*functionAddress*/,
                    endpoint		/*endpointNumber*/,
                    completion   	/*completion*/,
                    (void *)0		/*buffer*/,
                    true		/*bufferRounding*/,
                    0			/*bufferSize*/,
                    direction		/*direction*/);
        if (err)
        {
            IOLog("ControlTransaction: control packet 3 error %d\n", err);
            break;
        }
    } while(false);

    if (err)
        complete(command->completion, err, 0);
    return(err);
}

/*
 * ControlPacketHandler:
 * Handle all three types of control packets and maintain what stage
 * we're at.  When we receive the last one, then call the clients
 * completion routine.
 */
void IOUSBController::ControlPacketHandler(void *	parameter,
                                           IOReturn	status,
                                           UInt32	bufferSizeRemaining)
{
    IOUSBCommand 	*command = (IOUSBCommand *)parameter;
    IOUSBDevRequest	*request;
    UInt8		sent, back, todo;

#if (DEBUGGING_LEVEL > 1)
    DEBUGLOG("ControlPacketHandler lParam=%lx  status=0x%x bufferSizeRemaining=0x%x\n", parameter, status, bufferSizeRemaining);
#endif

    if (command == 0)
        return;

    request = command->request;

    sent = (command->stage & 0x0f) << 4;
    back = command->stage & 0xf0;
    todo = sent ^ back; /* thats xor */

    if((todo & kSetupBack) != 0)
        command->stage |= kSetupBack;
    else if((todo & kDataBack) != 0)
    {
        /* This is the data transport phase, so this is the interesting one */
        command->stage |= kDataBack;
        command->dataRemaining = bufferSizeRemaining;
    }
    else if((todo & kStatusBack) != 0)
        command->stage |= kStatusBack;
    else
        IOLog("ControlPacketHandler: Spare transactions, This seems to be harmless");

    back = command->stage & 0xf0;
    todo = sent ^ back; /* thats xor */

    if (status != kIOReturnSuccess)
    {
        USBDeviceAddress	addr = command->address;
        UInt8			endpt = command->endpoint;

        command->status = status;

#if (DEBUGGING_LEVEL > 0)
        DEBUGLOG("ControlPacketHandler: error %d occured.  todo = %d\n", status, todo);
        
#endif
        UIMClearEndpointStall(addr, endpt, kUSBAnyDirn);
    }
    if (todo == 0)
    {
#if (DEBUGGING_LEVEL > 0)
        DEBUGLOG("ControlPacketHandler: transaction complete status=%d\n", status);
#endif

        /* Call the clients handler */
        complete(command->completion, status, command->dataRemaining);

        /* Free/give back the command */
        IOFree(command, sizeof(IOUSBCommand));
    }
}

/*
 * InterruptTransaction:
 *   Send a USB interrupt packet.
 *
 */
IOReturn IOUSBController::InterruptTransaction(IOUSBCommand *command)
{
    IOReturn		err = kIOReturnSuccess;
    IOUSBCompletion	completion;

    completion.target 	 = (void *)this;
    completion.action 	 = &IOUSBController::InterruptPacketHandler;
    completion.parameter = (void *)command;

    err = UIMCreateInterruptTransfer(
                command->address			/*functionAddress*/,
                command->endpoint			/*endpointNumber*/,
                completion   				/*completion*/,
                command->buffer				/*buffer*/,
                true					/*bufferRounding*/,
                command->buffer->getLength()       	/*bufferSize*/,
                command->direction);

    return(err);
}

void IOUSBController::InterruptPacketHandler(void * parameter, IOReturn status, UInt32 bufferSizeRemaining)
{
    IOUSBCommand 	*command = (IOUSBCommand *)parameter;

    if (command == 0)
        return;

#if (DEBUGGING_LEVEL > 1)
    DEBUGLOG("InterruptPacketHandler: transaction complete status=%d bufferSizeRemaining = %d\n", status, bufferSizeRemaining);
#endif
    if (status != kIOReturnSuccess)
        UIMClearEndpointStall(command->address,
                              command->endpoint,
                              command->direction);

    /* Call the clients handler */
    complete(command->completion, status, bufferSizeRemaining);

    /* Free/give back the command */
    IOFree(command, sizeof(IOUSBCommand));
}


/*
 * BulkTransaction:
 *   Send a USB bulk packet.
 *
 */
IOReturn IOUSBController::BulkTransaction(IOUSBCommand *command)
{
    IOUSBCompletion	completion;
    IOReturn		err = kIOReturnSuccess;

    
    completion.target 	 = (void *)this;
    completion.action 	 = &IOUSBController::BulkPacketHandler;
    completion.parameter = (void *)command;

    err = UIMCreateBulkTransfer(
                command->address			/*functionAddress*/,
                command->endpoint			/*endpointNumber*/,
                completion  				/*completion*/,
                command->buffer				/*buffer*/,
                true					/*bufferRounding*/,
                command->buffer->getLength()     	/*bufferSize*/,
                command->direction			/*direction*/);

    if (err)
        IOLog("BulkTransaction: error queueing bulk packet (%d)\n", err);

    return(err);
}

void IOUSBController::BulkPacketHandler(void * 		parameter,
                                        IOReturn	status,
                                        UInt32		bufferSizeRemaining)
{
    IOUSBCommand 	*command = (IOUSBCommand *)parameter;

    
    if (command == 0)
        return;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("BulkPacketHandler: transaction complete status=%d bufferSizeRemaining = %d\n", status, bufferSizeRemaining);
#endif
    if (status != kIOReturnSuccess)
        UIMClearEndpointStall(command->address,
                              command->endpoint,
                              command->direction);
    
    /* Call the clients handler */
    complete(command->completion, status, bufferSizeRemaining);

    /* Free/give back the command */
    IOFree(command, sizeof(IOUSBCommand));
}

/*
 * BulkTransaction:
 *   Send a USB bulk packet.
 *
 */
IOReturn IOUSBController::doIsocTransfer(OSObject *owner, void *cmd,
                        void */*field2*/, void */*field3*/, void */*field4*/)
{
    IOUSBController	*controller = (IOUSBController *)owner;
    IOUSBIsocCommand	*command  = (IOUSBIsocCommand *) cmd;
    return(controller->IsocTransaction(command));
}

IOReturn IOUSBController::IsocTransaction(IOUSBIsocCommand *command)
{
    IOUSBIsocCompletion	completion;
    IOReturn		err = kIOReturnSuccess;


    completion.target 	 = (void *)this;
    completion.action 	 = &IOUSBController::IsocCompletionHandler;
    completion.parameter = (void *)command;

    err = UIMCreateIsochTransfer(
                command->address	/*functionAddress*/,
                command->endpoint	/*endpointNumber*/,
                completion  		/*completion*/,
		command->direction	/*direction*/,
                command->startFrame	/*Start frame */,
                command->buffer		/*buffer*/,
                command->numFrames	/*number of frames*/,
                command->frameList     	/*transfer for each frame*/);

    if (err) {
        IOLog("IsocTransaction: error queueing isoc transfer (0x%x)\n", err);
        if (command->completion.action)
            (command->completion.action)(command->completion.target,
                                          command->completion.parameter,
                                          err,
                                          command->frameList);
    }
    return(err);
}

void IOUSBController::IsocCompletionHandler(void * 	parameter,
                                        IOReturn	status,
                                        IOUSBIsocFrame	*pFrames)
{
    IOUSBIsocCommand 	*command = (IOUSBIsocCommand *)parameter;

    if (command == 0)
        return;

    if (status != kIOReturnSuccess)
        UIMClearEndpointStall(command->address,
                              command->endpoint,
                              command->direction);

    /* Call the clients handler */
    IOUSBIsocCompletion *completion = &command->completion;
    if (completion->action)  (*completion->action)(completion->target,
                                                   completion->parameter,
                                                 status,
                                                 pFrames);

    /* Free/give back the command */
    IOFree(command, sizeof(IOUSBIsocCommand));
}


IOReturn IOUSBController::doIOTransfer(OSObject *owner,
                           void *cmd,
                           void */*field2*/, void */*field3*/, void */*field4*/)
{
    IOUSBController	*controller = (IOUSBController *)owner;
    IOUSBCommand	*command  = (IOUSBCommand *) cmd;
    IOReturn		err       = kIOReturnSuccess;

    switch (command->type)
    {
        case kUSBInterrupt:
            err = controller->InterruptTransaction(command);
            break;
        case kUSBIsoc:
            IOLog("Isoc transactions not supported on non-isoc pipes!!\n");
            err = kIOReturnBadArgument;
            break;
        case kUSBBulk:
            err = controller->BulkTransaction(command);
            break;
        default:
            IOLog("Unknown transaction type\n");
            err = kIOReturnBadArgument;
            break;
    }

    if (err)
        controller->complete(command->completion, err, 0);

    return err;
}

IOReturn IOUSBController::doControlTransfer(OSObject *owner,
                           void *arg0, void *arg1,
                           void *arg2, void *arg3)
{
    IOUSBController *me = (IOUSBController *)owner;

    return me->ControlTransaction((IOUSBCommand *)arg0);
}

IOReturn IOUSBController::doDeleteEP(OSObject *owner,
                           void *arg0, void *arg1,
                           void *arg2, void *arg3)
{
    IOUSBController *me = (IOUSBController *)owner;

    return me->UIMDeleteEndpoint((short)arg0, (short)arg1, (short)arg2);
}

IOReturn IOUSBController::doCreateEP(OSObject *owner,
                           void *arg0, void *arg1,
                           void *arg2, void *arg3)
{
    IOUSBController *me = (IOUSBController *)owner;
    UInt8 address = (UInt8)arg0;
    UInt8 speed = (UInt8)arg1;
    Endpoint *endpoint = (Endpoint *)arg2;
    IOReturn err;
    switch (endpoint->transferType)
    {
        case kUSBInterrupt:
            err = me->UIMCreateInterruptEndpoint(address,
                                             endpoint->number,
                                             endpoint->direction,
                                             speed,
                                             endpoint->maxPacketSize,
                                             endpoint->interval);
            break;

        case kUSBBulk:
            err = me->UIMCreateBulkEndpoint(address,
                                        endpoint->number,
                                        endpoint->direction,
                                        speed,
                                        endpoint->maxPacketSize);
            break;

        case kUSBControl:
            err = me->UIMCreateControlEndpoint(address,
                                           endpoint->number,
                                           endpoint->maxPacketSize,
                                           speed);
            break;

        case kUSBIsoc:
            err = me->UIMCreateIsochEndpoint(address,
                                        endpoint->number,
                                        endpoint->maxPacketSize,
                                        endpoint->direction);
            break;

        default:
            err = kIOReturnBadArgument;
            break;
    }
    return (err);
}

IOReturn IOUSBController::AcquireDeviceZero()
{
    IOReturn err = 0;
    Endpoint ep;

    ep.number = 0;
    ep.transferType = kUSBControl;
    ep.maxPacketSize = 8;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("%s: TRYING TO ACQUIRE DEVICE ZERO\n", getName());
#endif

    IOTakeLock(_devZeroLock);

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("%s: ACQUIRED DEVICE ZERO\n", getName());
#endif

    err = openPipe(0, kUSBHighSpeed, &ep);
    return(err);
}

void IOUSBController::ReleaseDeviceZero(void)
{
    IOReturn err = 0;

    err = _commandGate->runAction(doDeleteEP, (void *)0, (void *)0, (void *)kUSBAnyDirn);
    IOUnlock(_devZeroLock);

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("%s: RELEASED DEVICE ZERO\n", getName());
#endif

    return;
}

void IOUSBController::WaitForReleaseDeviceZero()
{
    IOTakeLock(_devZeroLock);
    IOUnlock(_devZeroLock);
}

IOReturn IOUSBController::ConfigureDeviceZero(UInt8 maxPacketSize, UInt8 speed)
{
    IOReturn	err = kIOReturnSuccess;
    Endpoint ep;

    ep.number = 0;
    ep.transferType = kUSBControl;
    ep.maxPacketSize = maxPacketSize;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("********** ConfigureDeviceZero %d %d **********\n", maxPacketSize, speed);
#endif
    err = _commandGate->runAction(doDeleteEP, (void *)0, (void *)0, (void *)kUSBAnyDirn);
    err = openPipe(0, speed, &ep);

    return(err);
}


IOReturn IOUSBController::GetDeviceZeroDescriptor(IOUSBDeviceDescriptor *desc)
{
    IOReturn		err = kIOReturnSuccess;
    IOUSBDevRequest	request;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("********** GET DEVICE ZERO DEVICE DESCRIPTOR **********\n");
#endif

    do
    {
        IOUSBCompletion	tap;
        IOSyncer *	syncer;

        syncer  = IOSyncer::create();

        tap.target = syncer;
        tap.action = &IOUSBSyncCompletion;
        tap.parameter = NULL;

        if (!desc)
        {
            err = kIOReturnBadArgument;
            break;
        }

        request.rqDirection = kUSBIn;
        request.rqType = kUSBStandard;
        request.rqRecipient = kUSBDevice;
        request.bRequest = kUSBRqGetDescriptor;
        OSWriteLittleInt16(&request.wValue, 0, (kUSBDeviceDesc << 8));
        OSWriteLittleInt16(&request.wIndex, 0, 0);
        OSWriteLittleInt16(&request.wLength, 0, sizeof(IOUSBDeviceDescriptor));
        request.pData = desc;

        err = deviceRequest(&request, &tap, 0, 0);

        if (err)
        {
#if (DEBUGGING_LEVEL > 0)
            DEBUGLOG("%s: error getting device descriptor. err=%d\n", getName(), err);
#endif
            syncer->release();
            syncer->release();
            break;
        }
        err = syncer->wait();

    } while(false);

    return(err);
}

IOReturn IOUSBController::SetDeviceZeroAddress(USBDeviceAddress address,
                                               UInt8 maxPacketSize, UInt8 speed)
{
    IOReturn		err = kIOReturnSuccess;
    IOUSBDevRequest	request;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("********** SET DEVICE ZERO ADDRESS: %d %d %d **********\n",
             address, maxPacketSize, speed);
#endif

    do
    {
        IOUSBCompletion	tap;
        IOSyncer *	syncer;

        syncer  = IOSyncer::create();

        tap.target = syncer;
        tap.action = &IOUSBSyncCompletion;
        tap.parameter = NULL;

        request.rqDirection = kUSBOut;
        request.rqType = kUSBStandard;
        request.rqRecipient = kUSBDevice;
        request.bRequest = kUSBRqSetAddress;
        OSWriteLittleInt16(&request.wValue, 0, address);
        OSWriteLittleInt16(&request.wIndex, 0, 0);
        OSWriteLittleInt16(&request.wLength, 0, 0);
        request.pData = 0;

        err = deviceRequest(&request, &tap, 0, 0);

        if (err)
        {
            syncer->release(); syncer->release();
            break;
        }
        err = syncer->wait();

    } while(false);

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("%s: error setting device address. err=%d\n", getName(), err);
#endif
    return(err);
}

IOUSBDevice *IOUSBController::MakeDevice(IOUSBDeviceDescriptor *desc,
                                        UInt8 speed, UInt32 power)
{
    IOReturn		err = kIOReturnSuccess;
    IOUSBDevice		*newDev;
    USBDeviceAddress	address;

#if (DEBUGGING_LEVEL > 0)
    DEBUGLOG("*************** MakeDevice ***************\n");
#endif

    newDev = newDevice();
    if (newDev == 0)
        return(0);

    address = getNewAddress();
    if(address == 0) {
	newDev->release();
	return 0;
    }

    err = SetDeviceZeroAddress(address, desc->maxPacketSize, speed);

    if (err)
    {
#if (DEBUGGING_LEVEL > 0)
        DEBUGLOG("%s: error setting address. err=%d device=%lx\n",
              getName(), err, (UInt32)this);
#endif
        //return(0); Some devices produce a spurious error here, eg. Altec Lansing speakers
    }

    err = CreateDevice(newDev, address, desc, speed, power);
    if(err) {
        newDev->release();
        return(0);
    }
    return(newDev);
}

IOReturn IOUSBController::PolledRead(
        short				functionNumber,
        short				endpointNumber,
        IOUSBCompletion			completion,
        IOMemoryDescriptor *		CBP,
        bool				bufferRounding,
        UInt32				bufferSize)
{
    return UIMCreateInterruptTransfer(functionNumber, endpointNumber,
			completion, CBP, bufferRounding, bufferSize, kUSBIn);
}