AppleUSBIrDA.cpp   [plain text]



    /* Copyright (c) 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@
     */

    /* AppleUSBIrDA.cpp - MacOSX implementation of USB IrDA Driver. */

#include <machine/limits.h>         /* UINT_MAX */
#include <libkern/OSByteOrder.h>

#include <IOKit/assert.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOService.h>
#include <IOKit/IOBufferMemoryDescriptor.h>
#include <IOKit/IOMessage.h>

#include <IOKit/usb/IOUSBBus.h>
#include <IOKit/usb/IOUSBNub.h>
#include <IOKit/usb/IOUSBPipe.h>
#include <IOKit/usb/USB.h>
#include <IOKit/usb/IOUSBInterface.h>

#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/serial/IOModemSerialStreamSync.h>
#include <IOKit/serial/IORS232SerialStreamSync.h>

#include <UserNotification/KUNCUserNotifications.h>

#include "AppleUSBIrDA.h"
#include "IrDAComm.h"
#include "IrDAUser.h"
#include "IrDALog.h"
#include "IrDADebugging.h"


#if (hasTracing > 0 && hasAppleUSBIrDATracing > 0)

enum tracecodes
{
    kLogInit = 1,
    kLogFree,
    kLogProbe,
    kLogStart,
    kLogStop,
    
    kLogNewNub,
    kLogNewPort,
    kLogDestroyNub,
    
    kLogSetSpeed,
    kLogGetState,
    kLogXmitLen,
    kLogXmitData,
    
    kLogAddRxBytes,
    kLogSetBofCount,
    kLogGetIrDAStatus,
    
    kLogSetIrDAState,
    kLogInterruptRead,
    kLogDataReadComplete,
    
    kLogInputData,
    kLogDataWriteComplete,
    kLogDataWriteCompleteZero,
    
    kLogAllocateResources,
    kLogReleaseResources,
    kLogConfigureDevice,
    kLogCreateSerialStream,
    
    kLogAcquirePort,
    kLogReleasePort,
    kLogSetState,
    kLogWatchState,
    
    kLogExecEvent,
    kLogExecEventData,
    kLogReqEvent,
    kLogReqEventData,
    
    kLogSetupTransmit,
    kLogSetStructureDefaults,
    kLogMessage,
    
    kLogWorkAround,
    kLogWorkAroundComplete,
    
    kLogInitForPM,
    kLogInitialPowerState,
    kLogSetPowerState
};

static
EventTraceCauseDesc gTraceEvents[] = {
    {kLogInit,          "AppleUSBIrDADriver: init"},
    {kLogFree,          "AppleUSBIrDADriver: free"},
    {kLogProbe,         "AppleUSBIrDADriver: probe, provider="},
    {kLogStart,         "AppleUSBIrDADriver: start, provider="},
    {kLogStop,          "AppleUSBIrDADriver: stop"},
    
    {kLogNewNub,        "AppleUSBIrDADriver: new nub"},
    {kLogNewPort,       "AppleUSBIrDADriver: new port"},
    {kLogDestroyNub,    "AppleUSBIrDADriver: destroy nub"},
	    
    {kLogSetSpeed,      "AppleUSBIrDADriver: set irda speed"},
    {kLogGetState,      "AppleUSBIrDADriver: get state"},
    {kLogXmitLen,       "AppleUSBIrDADriver: xmit length"},
    {kLogXmitData,      "AppleUSBIrDADriver: xmit data"},
    
    {kLogAddRxBytes,    "AppleUSBIrDADriver: add rx bytes, count="},
    {kLogSetBofCount,   "AppleUSBIrDADriver: set bof count, input="},
    {kLogGetIrDAStatus, "AppleUSBIrDADriver: get irda status, on="},
    
    {kLogSetIrDAState,          "AppleUSBIrDADriver: set irda state, current=, want="},
    {kLogInterruptRead,         "AppleUSBIrDADriver: interrupt read complete"},
    {kLogDataReadComplete,      "AppleUSBIrDADriver: data read complete, rc, len="},
    
    {kLogInputData,             "AppleUSBIrDADriver: data read buffer"},
    {kLogDataWriteComplete,     "AppleUSBIrDADriver: data write complete, rc, len="},
    {kLogDataWriteCompleteZero, "AppleUSBIrDADriver: data write complete sending zero length packet"},
    
    {kLogAllocateResources,     "AppleUSBIrDADriver: allocate resources"},
    {kLogReleaseResources,      "AppleUSBIrDADriver: release resources"},
    {kLogConfigureDevice,       "AppleUSBIrDADriver: configure device"},
    {kLogCreateSerialStream,    "AppleUSBIrDADriver: create serial stream"},
    
    {kLogAcquirePort,       "AppleUSBIrDADriver: acquire port"},
    {kLogReleasePort,       "AppleUSBIrDADriver: release port"},
    {kLogSetState,          "AppleUSBIrDADriver: set state"},
    {kLogWatchState,        "AppleUSBIrDADriver: watch state"},
    
    {kLogExecEvent,         "AppleUSBIrDADriver: execute event"},
    {kLogExecEventData,     "AppleUSBIrDADriver: execute event, data="},
    {kLogReqEvent,          "AppleUSBIrDADriver: request event"},
    {kLogReqEventData,      "AppleUSBIrDADriver: request event, data="},
    
    {kLogSetupTransmit,         "AppleUSBIrDADriver: setup transmit"},
    {kLogSetStructureDefaults,  "AppleUSBIrDADriver: set structure defaults"},
    {kLogMessage,               "AppleUSBIrDADriver: message"},
    
    {kLogWorkAround,            "AppleUSBIrDADriver: workaround called"},
    {kLogWorkAroundComplete,    "AppleUSBIrDADriver: workaround complete"},
    
    {kLogInitForPM,             "AppleUSBIrDADriver: init power management"},
    {kLogInitialPowerState,     "AppleUSBIrDADriver: get initial power state, flags="},
    {kLogSetPowerState,         "AppleUSBIrDADriver: set power state, ordinal="}

};


#define XTRACE(x, y, z) IrDALogAdd ( x, y, ((int)z & 0xffff), gTraceEvents, true)
#else
#define XTRACE(x, y, z) ((void)0)
#endif


enum {
    kUseInterruptsForRead   = true
};

enum {
    kIrDAPowerOffState  = 0,
    kIrDAPowerOnState   = 1,
    kNumIrDAStates = 2
};

static IOPMPowerState gOurPowerStates[kNumIrDAStates] = {
    {1,0,0,0,0,0,0,0,0,0,0,0},
    {1,IOPMDeviceUsable,IOPMPowerOn,IOPMPowerOn,0,0,0,0,0,0,0,0}
};


    static IrDAglobals      g;  /**** Instantiate the globals ****/

#define super IOSerialDriverSync

    OSDefineMetaClassAndStructors( AppleUSBIrDADriver, IOSerialDriverSync );
    
/****************************************************************************************************/
//
//      Function:   Asciify
//
//      Inputs:     i - the nibble
//
//      Outputs:    return byte - ascii byte
//
//      Desc:       Converts to ascii. 
//
/****************************************************************************************************/
 
static UInt8 Asciify(UInt8 i)
{

    i &= 0xF;
    if ( i < 10 )
	 return( '0' + i );
    else return( 55  + i );
    
}/* end Asciify */

#if USE_ELG
/****************************************************************************************************/
//
//      Function:   AllocateEventLog
//
//      Inputs:     size - amount of memory to allocate
//
//      Outputs:    None
//
//      Desc:       Allocates the event log buffer
//
/****************************************************************************************************/

void AllocateEventLog( UInt32 size )
{
    if ( g.evLogBuf )
	return;

    g.evLogFlag = 0;            /* assume insufficient memory   */
    g.evLogBuf = (UInt8*)IOMalloc( size );
    if ( !g.evLogBuf )
    {
	kprintf( "AppleUSBIrDA: evLog allocation failed" );
	return;
    }

    bzero( g.evLogBuf, size );
    g.evLogBufp = g.evLogBuf;
    g.evLogBufe = g.evLogBufp + kEvLogSize - 0x20; // ??? overran buffer?
    g.evLogFlag  = 0xFEEDBEEF;  // continuous wraparound
//  g.evLogFlag  = 'step';      // stop at each ELG
//  g.evLogFlag  = 0x0333;      // any nonzero - don't wrap - stop logging at buffer end

    IOLog( "AppleUSBIrDA: AllocateEventLog - &USBglobals=%8x buffer=%8x", (unsigned int)&g, (unsigned int)g.evLogBuf );

    return;
    
}/* end AllocateEventLog */

/****************************************************************************************************/
//
//      Function:   EvLog
//
//      Inputs:     a - anything, b - anything, ascii - 4 charater tag, str - any info string           
//
//      Outputs:    None
//
//      Desc:       Writes the various inputs to the event log buffer
//
/****************************************************************************************************/

void EvLog( UInt32 a, UInt32 b, UInt32 ascii, char* str )
{
    register UInt32     *lp;           /* Long pointer      */
    mach_timespec_t     time;

    if ( g.evLogFlag == 0 )
	return;

    IOGetTime( &time );

    lp = (UInt32*)g.evLogBufp;
    g.evLogBufp += 0x10;

    if ( g.evLogBufp >= g.evLogBufe )       /* handle buffer wrap around if any */
    {    g.evLogBufp  = g.evLogBuf;
	if ( g.evLogFlag != 0xFEEDBEEF )    // make 0xFEEDBEEF a symbolic ???
	    g.evLogFlag = 0;                /* stop tracing if wrap undesired   */
    }

	/* compose interrupt level with 3 byte time stamp:  */

    *lp++ = (g.intLevel << 24) | ((time.tv_nsec >> 10) & 0x003FFFFF);   // ~ 1 microsec resolution
    *lp++ = a;
    *lp++ = b;
    *lp   = ascii;

    if( g.evLogFlag == 'step' )
    {   static char code[ 5 ] = {0,0,0,0,0};
	*(UInt32*)&code = ascii;
	IOLog( "AppleUSBIrDA: %8x %8x %8x %s\n", time.tv_nsec>>10, (unsigned int)a, (unsigned int)b, code );
    }

    return;
    
}/* end EvLog */
#endif // USE_ELG

#if LOG_DATA
#define dumplen     32      // Set this to the number of bytes to dump and the rest should work out correct

#define buflen      ((dumplen*2)+dumplen)+3
#define Asciistart  (dumplen*2)+3

/****************************************************************************************************/
//
//      Function:   DEVLogData
//
//      Inputs:     Dir - direction, Count - number of bytes, buf - the data
//
//      Outputs:    None
//
//      Desc:       Puts the data in the log. 
//
/****************************************************************************************************/

void DEVLogData(UInt8 Dir, UInt32 Count, char *buf)
{
    UInt8       wlen, i, Aspnt, Hxpnt;
    UInt8       wchr;
    char        LocBuf[buflen+1];

    for ( i=0; i<=buflen; i++ )
    {
	LocBuf[i] = 0x20;
    }
    LocBuf[i] = 0x00;
    
    if ( Dir == kUSBIn )
    {
	IOLog( "AppleUSBIrDA: USBLogData - Received, size = %8lx\n", Count );
    } else {
	if ( Dir == kUSBOut )
	{
	    IOLog( "AppleUSBIrDA: USBLogData - Write, size = %8lx\n", Count );
	} else {
	    if ( Dir == kUSBAnyDirn )
	    {
		IOLog( "AppleUSBIrDA: USBLogData - Other, size = %8lx\n", Count );
	    }
	}           
    }

    if ( Count > dumplen )
    {
	wlen = dumplen;
    } else {
	wlen = Count;
    }
    
    if ( wlen > 0 )
    {
	Aspnt = Asciistart;
	Hxpnt = 0;
	for ( i=1; i<=wlen; i++ )
	{
	    wchr = buf[i-1];
	    LocBuf[Hxpnt++] = Asciify( wchr >> 4 );
	    LocBuf[Hxpnt++] = Asciify( wchr );
	    if (( wchr < 0x20) || (wchr > 0x7F ))       // Non printable characters
	    {
		LocBuf[Aspnt++] = 0x2E;                 // Replace with a period
	    } else {
		LocBuf[Aspnt++] = wchr;
	    }
	}
	LocBuf[(wlen + Asciistart) + 1] = 0x00;
	IOLog( LocBuf );
	IOLog( "\n" );
    } else {
	IOLog( "AppleUSBIrDA: USBLogData - No data, Count = 0\n" );
    }
    
}/* end DEVLogData */
#endif // LOG_DATA

/* QueuePrimatives  */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::AddBytetoQueue
//
//      Inputs:     Queue - the queue to be added to
//
//      Outputs:    Value - Byte to be added, Queue status - full or no error
//
//      Desc:       Add a byte to the circular queue.
//
/****************************************************************************************************/

QueueStatus AppleUSBIrDADriver::AddBytetoQueue( CirQueue *Queue, char Value )
{
    /* Check to see if there is space by comparing the next pointer,    */
    /* with the last, If they match we are either Empty or full, so     */
    /* check the InQueue of being zero.                 */

    require(fPort && fPort->serialRequestLock, Fail);
    IOLockLock( fPort->serialRequestLock );

    if ( (Queue->NextChar == Queue->LastChar) && Queue->InQueue ) {
	IOLockUnlock( fPort->serialRequestLock);
	return queueFull;
    }

    *Queue->NextChar++ = Value;
    Queue->InQueue++;

	/* Check to see if we need to wrap the pointer. */
	
    if ( Queue->NextChar >= Queue->End )
	Queue->NextChar =  Queue->Start;

    IOLockUnlock( fPort->serialRequestLock);
    return queueNoError;
    
Fail:
    return queueFull;       // for lack of a better error
    
}/* end AddBytetoQueue */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::GetBytetoQueue
//
//      Inputs:     Queue - the queue to be removed from
//
//      Outputs:    Value - where to put the byte, Queue status - empty or no error
//
//      Desc:       Remove a byte from the circular queue.
//
/****************************************************************************************************/

QueueStatus AppleUSBIrDADriver::GetBytetoQueue( CirQueue *Queue, UInt8 *Value )
{

    require(fPort && fPort->serialRequestLock, Fail);
    IOLockLock( fPort->serialRequestLock );

	/* Check to see if the queue has something in it.   */
	
    if ( (Queue->NextChar == Queue->LastChar) && !Queue->InQueue ) {
	IOLockUnlock(fPort->serialRequestLock);
	return queueEmpty;
    }

    *Value = *Queue->LastChar++;
    Queue->InQueue--;

	/* Check to see if we need to wrap the pointer. */
	
    if ( Queue->LastChar >= Queue->End )
	Queue->LastChar =  Queue->Start;

    IOLockUnlock(fPort->serialRequestLock);
    return queueNoError;
    
Fail:
    return queueEmpty;          // can't get to it, pretend it's empty
    
}/* end GetBytetoQueue */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::InitQueue
//
//      Inputs:     Queue - the queue to be initialized, Buffer - the buffer, size - length of buffer
//
//      Outputs:    Queue status - queueNoError.
//
//      Desc:       Pass a buffer of memory and this routine will set up the internal data structures.
//
/****************************************************************************************************/

QueueStatus AppleUSBIrDADriver::InitQueue( CirQueue *Queue, UInt8 *Buffer, size_t Size )
{
    Queue->Start    = Buffer;
    Queue->End      = (UInt8*)((size_t)Buffer + Size);
    Queue->Size     = Size;
    Queue->NextChar = Buffer;
    Queue->LastChar = Buffer;
    Queue->InQueue  = 0;

    IOSleep( 1 );
    
    return queueNoError ;
    
}/* end InitQueue */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::CloseQueue
//
//      Inputs:     Queue - the queue to be closed
//
//      Outputs:    Queue status - queueNoError.
//
//      Desc:       Clear out all of the data structures.
//
/****************************************************************************************************/

QueueStatus AppleUSBIrDADriver::CloseQueue( CirQueue *Queue )
{

    Queue->Start    = 0;
    Queue->End      = 0;
    Queue->NextChar = 0;
    Queue->LastChar = 0;
    Queue->Size     = 0;

    return queueNoError;
    
}/* end CloseQueue */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::AddtoQueue
//
//      Inputs:     Queue - the queue to be added to, Buffer - data to add, Size - length of data
//
//      Outputs:    BytesWritten - Number of bytes actually put in the queue.
//
//      Desc:       Add an entire buffer to the queue.
//
/****************************************************************************************************/

size_t AppleUSBIrDADriver::AddtoQueue( CirQueue *Queue, UInt8 *Buffer, size_t Size )
{
    size_t      BytesWritten = 0;

    while ( FreeSpaceinQueue( Queue ) && (Size > BytesWritten) )
    {
	AddBytetoQueue( Queue, *Buffer++ );
	BytesWritten++;
    }

    return BytesWritten;
    
}/* end AddtoQueue */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::RemovefromQueue
//
//      Inputs:     Queue - the queue to be removed from, Size - size of buffer
//
//      Outputs:    Buffer - Where to put the data, BytesReceived - Number of bytes actually put in Buffer.
//
//      Desc:       Get a buffers worth of data from the queue.
//
/****************************************************************************************************/

size_t AppleUSBIrDADriver::RemovefromQueue( CirQueue *Queue, UInt8 *Buffer, size_t MaxSize )
{
    size_t      BytesReceived = 0;
    UInt8       Value;
    
    while( (MaxSize > BytesReceived) && (GetBytetoQueue(Queue, &Value) == queueNoError) ) 
    {
	*Buffer++ = Value;
	BytesReceived++;
    }/* end while */

    return BytesReceived;
    
}/* end RemovefromQueue */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::FreeSpaceinQueue
//
//      Inputs:     Queue - the queue to be queried
//
//      Outputs:    Return Value - Free space left
//
//      Desc:       Return the amount of free space left in this buffer.
//
/****************************************************************************************************/

size_t AppleUSBIrDADriver::FreeSpaceinQueue( CirQueue *Queue )
{
    size_t  retVal = 0;

    require(fPort && fPort->serialRequestLock, Fail);
    IOLockLock( fPort->serialRequestLock );

    retVal = Queue->Size - Queue->InQueue;
    
    IOLockUnlock(fPort->serialRequestLock);
    
Fail:
    return retVal;
    
}/* end FreeSpaceinQueue */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::UsedSpaceinQueue
//
//      Inputs:     Queue - the queue to be queried
//
//      Outputs:    UsedSpace - Amount of data in buffer
//
//      Desc:       Return the amount of data in this buffer.
//
/****************************************************************************************************/

size_t AppleUSBIrDADriver::UsedSpaceinQueue( CirQueue *Queue )
{
    return Queue->InQueue;
    
}/* end UsedSpaceinQueue */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::GetQueueSize
//
//      Inputs:     Queue - the queue to be queried
//
//      Outputs:    QueueSize - The size of the queue.
//
//      Desc:       Return the total size of the queue.
//
/****************************************************************************************************/

size_t AppleUSBIrDADriver::GetQueueSize( CirQueue *Queue )
{
    return Queue->Size;
    
}/* end GetQueueSize */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::GetQueueStatus
//
//      Inputs:     Queue - the queue to be queried
//
//      Outputs:    Queue status - full, empty or no error
//
//      Desc:       Returns the status of the circular queue.
//
/****************************************************************************************************/
/*
QueueStatus AppleUSBIrDADriver::GetQueueStatus( CirQueue *Queue )
{
    if ( (Queue->NextChar == Queue->LastChar) && Queue->InQueue )
	return queueFull;
    else if ( (Queue->NextChar == Queue->LastChar) && !Queue->InQueue )
	return queueEmpty;
	
    return queueNoError ;
    
}*/ /* end GetQueueStatus */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::CheckQueues
//
//      Inputs:     port - the port to check
//
//      Outputs:    None
//
//      Desc:       Checks the various queue's etc and manipulates the state(s) accordingly
//
/****************************************************************************************************/

void AppleUSBIrDADriver::CheckQueues( PortInfo_t *port )
{
    unsigned long   Used;
    unsigned long   Free;
    unsigned long   QueuingState;
    unsigned long   DeltaState;

    // Initialise the QueueState with the current state.
    QueuingState = readPortState( port );

	/* Check to see if there is anything in the Transmit buffer. */
    Used = UsedSpaceinQueue( &port->TX );
    Free = FreeSpaceinQueue( &port->TX );
//  ELG( Free, Used, 'CkQs', "CheckQueues" );
    if ( Free == 0 )
    {
	QueuingState |=  PD_S_TXQ_FULL;
	QueuingState &= ~PD_S_TXQ_EMPTY;
    }
    else if ( Used == 0 )
    {
	QueuingState &= ~PD_S_TXQ_FULL;
	QueuingState |=  PD_S_TXQ_EMPTY;
    }
    else
    {
	QueuingState &= ~PD_S_TXQ_FULL;
	QueuingState &= ~PD_S_TXQ_EMPTY;
    }

	/* Check to see if we are below the low water mark. */
    if ( Used < port->TXStats.LowWater )
	 QueuingState |=  PD_S_TXQ_LOW_WATER;
    else QueuingState &= ~PD_S_TXQ_LOW_WATER;

    if ( Used > port->TXStats.HighWater )
	 QueuingState |= PD_S_TXQ_HIGH_WATER;
    else QueuingState &= ~PD_S_TXQ_HIGH_WATER;


	/* Check to see if there is anything in the Receive buffer. */
    Used = UsedSpaceinQueue( &port->RX );
    Free = FreeSpaceinQueue( &port->RX );

    if ( Free == 0 )
    {
	QueuingState |= PD_S_RXQ_FULL;
	QueuingState &= ~PD_S_RXQ_EMPTY;
    }
    else if ( Used == 0 )
    {
	QueuingState &= ~PD_S_RXQ_FULL;
	QueuingState |= PD_S_RXQ_EMPTY;
    }
    else
    {
	QueuingState &= ~PD_S_RXQ_FULL;
	QueuingState &= ~PD_S_RXQ_EMPTY;
    }

	/* Check to see if we are below the low water mark. */
    if ( Used < port->RXStats.LowWater )
	 QueuingState |= PD_S_RXQ_LOW_WATER;
    else QueuingState &= ~PD_S_RXQ_LOW_WATER;

    if ( Used > port->RXStats.HighWater )
	 QueuingState |= PD_S_RXQ_HIGH_WATER;
    else QueuingState &= ~PD_S_RXQ_HIGH_WATER;

	/* Figure out what has changed to get mask.*/
    DeltaState = QueuingState ^ readPortState( port );
    changeState( port, QueuingState, DeltaState );
    
    return;
    
}/* end CheckQueues */

/* end of QueuePrimatives */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::Add_RXBytes
//
//      Inputs:     Buffer - the raw input data, Size - the length
//
//      Outputs:
//
//      Desc:       Adds data to the circular receive queue 
//
/****************************************************************************************************/

void AppleUSBIrDADriver::Add_RXBytes( UInt8 *Buffer, size_t Size )
{
    XTRACE(kLogAddRxBytes, 0, Size);
    ELG( 0, Size, 'AdRB', "Add_RXBytes" );
    
    AddtoQueue( &fPort->RX, Buffer, Size );
    CheckQueues( fPort );
}/* end Add_RXBytes */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::SetBofCount
//
//      Inputs:     bof_count - the requested number of Beginning Of Frames
//
//      Outputs:    return word - the actual count (not bofs)
//
//      Desc:       Encode the requested number of BOF bytes to the first value that's big enough 
//
/****************************************************************************************************/  

SInt16 AppleUSBIrDADriver::SetBofCount( SInt16 bof_count )
{
    SInt16 counts[] = { 0, 1, 2, 3, 6, 12, 24, 48, -1};     // the bof counts that are encoded below
    SInt16  codes[] = { 8, 7, 6, 5, 4,  3,  2,  1,  1};     // could use an f(i), but this is easier to match to spec
    int i, sz;
    
    ELG( 0, bof_count, 'Sbof', "SetBofCount" );
    XTRACE(kLogSetBofCount, 0, bof_count);
    
    // input is desired bof count at the current speed, but the usb hardware wants the
    // unadjusted bof count (i.e. xbofs at 115k bps), so we have to adjust back to 115k.
    
    if (fCurrentBaud < 115200) {
	bof_count = bof_count * (115200 / fCurrentBaud);
	XTRACE(kLogSetBofCount, 1, bof_count);
    }
    
    sz = sizeof(counts) / sizeof(counts[0]);
    
    // note that the input bof counts can be computed, so we do an 'at least' test instead
    // of insisting upon an exact match to one of the supported bof counts.
    
    for (i = 0 ; i < sz; i++) 
    {
	if (counts[i] >= bof_count || counts[i] < 0)        // if table entry at least what's wanted (no smaller), or end-of-table
	{       
	    fBofsCode = codes[i];                           // then save the encoded version
	    return counts[i];                               // and return raw count used to caller
	}
    }
    
    ELG( 0, i, 'Sbf-', "SetBofCount - logic error" );
    return 0;
}/* end SetBofCount */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::SetSpeed
//
//      Inputs:     brate - the requested baud rate
//
//      Outputs:    return word - baud coding
//
//      Desc:       Set the baudrate for the device 
//
/****************************************************************************************************/  

UInt16 AppleUSBIrDADriver::SetSpeed( UInt32 brate )
{
    XTRACE(kLogSetSpeed, brate >> 16, (short)brate);
    ELG( 0, brate, 'Sbof', "SetSpeed" );
    
    fCurrentBaud = brate;
    
    switch (brate)
    {
	case 2400: 
	    fBaudCode = kLinkSpeed2400;     // 0x01
	    break;
	    
	case 9600: 
	    fBaudCode = kLinkSpeed9600;     // 0x02
	    break;
	    
	case 19200: 
	    fBaudCode = kLinkSpeed19200;    // 0x03
	    break;
	    
	case 38400: 
	    fBaudCode = kLinkSpeed38400;    // 0x04
	    break;
	    
	case 57600: 
	    fBaudCode = kLinkSpeed57600;    // 0x05
	    break;
	    
	case 115200:
	    fBaudCode = kLinkSpeed115200;   // 0x06
	    break;
	    
	case 576000:
	    fBaudCode = kLinkSpeed576000;   // 0x07
	    break;
	    
	case 1152000:
	    fBaudCode = kLinkSpeed1152000;  // 0x08
	    break;
	    
	case 4000000:
	    fBaudCode = kLinkSpeed4000000;  // 0x09
	    break;
	    
	case 300: 
	case 600: 
	case 1200: 
	case 1800: 
	case 3600: 
	case 4800: 
	case 7200: 
	default:
	    ELG( 0, brate, 'SSp-', "SetSpeed - Unsupported baud rate");
	    fBaudCode = 0;
	    break;
    }
    
    // start a one-byte transmit to set the speed in the device
    StartTransmit(0, NULL, 0, NULL);            // no control or data bytes, just the mode byte please
						// note SetSpeedComplete is called out of transmit complete
    return fBaudCode;
}/* end SetSpeed */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::GetIrDAComm
//
//      Inputs: 
//
//      Outputs:    IrDAComm - Address of the IrDA object
//
//      Desc:       Returns the address of the IrDA object 
//
/****************************************************************************************************/

IrDAComm* AppleUSBIrDADriver::GetIrDAComm( void )
{
    return fIrDA;
}/* end GetIrDAComm */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::GetIrDAQoS
//
//      Inputs: 
//
//      Outputs:    USBIrDAQoS - Address of the QoS structure
//
//      Desc:       Returns the address of the Quality of Service structure
//
/****************************************************************************************************/

USBIrDAQoS* AppleUSBIrDADriver::GetIrDAQoS( void )
{
    return &fQoS;
}/* end GetIrDAQoS */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::GetIrDAStatus
//
//      Inputs:     status - status structure
//
//      Outputs:    
//
//      Desc:       Sets the connection state and CRC errors of the status structure
//
/****************************************************************************************************/

void AppleUSBIrDADriver::GetIrDAStatus( IrDAStatus *status )
{
    int review_get_irda_status;     // check w/irda on/off logic
    
    ELG( 0, 0, 'GIrS', "GetIrDAStatus" );
    XTRACE(kLogGetIrDAStatus, 0, fIrDAOn);
    
    if ( !fIrDAOn )
    {
	    //bzero( status, sizeof(IrDAStatus) );
	    status->connectionState = kIrDAStatusOff;
    } else {
	    if ( status->connectionState == kIrDAStatusOff )
	    {
		status->connectionState = kIrDAStatusIdle;
	    }
	    status->crcErrors = 0;                  // Unavailable
	}
    
}/* end GetIrDAStatus */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::CheckIrDAState
//
//      Inputs:     open session count (fSessions)
//                  user-client start request (fStartStopUserClient)
//                  usb start/stop (fStartStopUSB) -- replace with fTerminate?
//
//      Outputs:    
//
//      Desc:       Turns IrDA on or off if appropriate
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::CheckIrDAState( void )
{
    IOReturn    ior = kIOReturnSuccess;
    Boolean     newState = fUSBStarted &    // usb must have started, and 
			(fPowerState == kIrDAPowerOnState) &&   // powered on by the power manager, and
			(fUserClientStarted | (fSessions > 0)); // one of the clients too

    ELG( 0, 0, 'SIrS', "CheckIrDAState" );
    XTRACE(kLogSetIrDAState, fIrDAOn, newState);
    
    if ( newState && !fIrDAOn )         // Turn IrDA on if needed
    {
	fIrDAOn = true;
	fTerminate = false;
	
	if (!fSuspendFail) {            // if previous suspend worked, then
					// resume it and startIrDA will run from message().
	    ior = fpDevice->SuspendDevice( false );     // Ask to resume the device
	    if ( ior != kIOReturnSuccess )
	    {
		ELG( 0, ior, 'SIR-', "SetIrDAState - Resume failed" );
		IOLog("AppleUSBIrDA: failed to resume device\n");
		fIrDAOn = false;            // We're basically dead at this point
		fTerminate = true;
	    }
	}
	else{                   // earlier suspend failed, just start irda here
	    if ( !startIrDA() )
	    {
		fIrDAOn = false;
		fTerminate = true;
		IOLog("AppleUSBIrDADriver: SetIrDAState - startIrDA failed" );
	    } else {
		ELG( 0, 0, 'msc+', "SetIrDAState - startIrDA successful" );
		//IOLog("AppleUSBIrDADriver: message - startIrDA successful\n" );
	    }
	}
    }
    else if (!newState && fIrDAOn)      // Turn IrDA off if needed
    {
	fIrDAOn = false;
	fTerminate = true;              // Make it look like we've been terminated
	    
	stopIrDA();                     // stop irda and stop pipe i/o
	
	ior = fpDevice->SuspendDevice( true );  // Try to suspend the device
	if ( ior != kIOReturnSuccess )
	{
	    ELG( 0, 0, 'SIS-', "SetIrDAState - Suspend failed" );
	    IOLog("AppleUSBIrDA: failed to suspend device\n");
	}
    }
    
    return ior;
}/* end CheckIrDAState */

//
// User client has asked to start/stop irda.  do it if
// it's ok w/bsd open count and usb start/stop flag.
//
IOReturn AppleUSBIrDADriver::SetIrDAUserClientState( bool IrDAOn )
{
    fUserClientStarted = IrDAOn;
    return CheckIrDAState();
}

/****************************************************************************************************/
//
//      Function:   AppleUSBIrDADriver::init
//
//      Inputs:     dict - Dictionary
//
//      Outputs:    Return code - from super::init
//
//      Desc:       Driver initialization
//
/****************************************************************************************************/

bool AppleUSBIrDADriver::init( OSDictionary *dict )
{
    bool    rc;
    
    rc = super::init( dict );
    IOLogIt( (UInt32)IrDALogGetInfo(), rc, 'init', "init" );
    XTRACE(kLogInit, 0, rc);
    return rc;
    
}/* end init */

/****************************************************************************************************/
//
//      Function:   AppleUSBIrDADriver::probe
//
//      Inputs:     provider - my provider
//
//      Outputs:    IOService - from super::probe, score - probe score
//
//      Desc:       Modify the probe score if necessary (we don't  at the moment)
//
/****************************************************************************************************/

IOService* AppleUSBIrDADriver::probe( IOService *provider, SInt32 *score )
{ 
    IOService       *res;

    res = super::probe( provider, score );
    IOLogIt( provider, res, 'prob', "probe" );
    XTRACE(kLogProbe, (int)provider >> 16, provider);
    return res;
    
}/* end probe */


/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::start
//
//      Inputs:     provider - my provider
//
//      Outputs:    Return code - true (it's me), false (sorry it probably was me, but I can't configure it)
//
//      Desc:       This is called once it has beed determined I'm probably the best 
//                  driver for this device.
//
/****************************************************************************************************/

bool AppleUSBIrDADriver::start( IOService *provider )
{
    UInt8   configs;    // number of device configurations
    bool    ok;
	
    XTRACE(kLogStart, (int)provider >> 16, provider);

    g.evLogBufp = NULL;

    fTerminate = false;     // Make sure we don't think we're being terminated
    fPort = NULL;
    fIrDA = NULL;
    fNub = NULL;
    fIrDAOn = false;
    fSuspendFail = false;
    fpInterface = NULL;
    
    fpinterruptPipeBuffer = NULL;
    fPipeInBuffer = NULL;
    fPipeOutBuffer = NULL;
    
    fpDevice = NULL;
    fpInPipe = NULL;
    fpOutPipe = NULL;
    fpInterruptPipe = NULL;
    
    fUserClientStarted = false;     // user/client has not started us yet
    fUSBStarted = false;            // set to true when start finishes up ok
    fSessions = 0;
    
    fReadActive = false;
    
    fWriteActive = false;
	
#if USE_ELG
    AllocateEventLog( kEvLogSize );
    ELG( &g, g.evLogBufp, 'USBM', "start - event logging set up." );

    waitForService( resourceMatching( "kdp" ) );
#endif /* USE_ELG */

    ELG( this, provider, 'strt', "start - this, provider." );
    if( !super::start( provider ) )
    {
	IOLogIt( 0, 0, 'SS--', "start - super failed" );
	return false;
    }

    /* Get my USB provider - the interface and then get the device */

    fpInterface = OSDynamicCast( IOUSBInterface, provider );
    require(fpInterface, Fail);
    
    fpDevice = fpInterface->GetDevice();
    require(fpDevice, Fail);
    
    /* Let's see if we have any configurations to play with */
	
    configs = fpDevice->GetNumConfigurations();
    require(configs == 1, Fail);
	
    // make our nub (and fPort) now
    ok = createNub();
    require(ok, Fail);
	
    // Now configure it (leaves device suspended)
    ok = configureDevice(configs);
    require(ok, Fail);
    
    // Finally create the bsd tty (serial stream) and leave it there until usb stop
	
    ok = createSerialStream();
    require(ok, Fail);
    
    ok = initForPM(provider);
    require(ok, Fail);
    
    fUSBStarted = true;     // now pay attn to bsd open's and user client start-irda requests

    return true;
    
Fail:

    IOLogIt( 0, 0, 'sts-', "start - failed" );
    stop( provider );
    
    return false;
    
}/* end start */

//
// initForPM
//
// Add ourselves to the power management tree so we
// can do the right thing on sleep/wakeup.
//
bool AppleUSBIrDADriver::initForPM(IOService * provider)
{
    XTRACE(kLogInitForPM, 0, 0);
    
    fPowerState = kIrDAPowerOnState;        // init our power state to be 'on'
    PMinit();                               // init power manager instance variables
    provider->joinPMtree(this);             // add us to the power management tree
    require(pm_vars != NULL, Fail);

    // register ourselves with ourself as policy-maker
    registerPowerDriver(this, gOurPowerStates, kNumIrDAStates);
    return true;
    
Fail:
    return false;
}

//
// request for our initial power state
//
unsigned long AppleUSBIrDADriver::initialPowerStateForDomainState ( IOPMPowerFlags flags)
{
    XTRACE(kLogInitialPowerState, flags >> 16, (short)flags);
    return fPowerState;
}

//
// request to turn device on or off
//
IOReturn AppleUSBIrDADriver::setPowerState(unsigned long powerStateOrdinal, IOService * whatDevice)
{
    XTRACE(kLogSetPowerState, 0, powerStateOrdinal);
    
    require(powerStateOrdinal == kIrDAPowerOffState || powerStateOrdinal == kIrDAPowerOnState, Fail);

    if (powerStateOrdinal == fPowerState)
	return IOPMAckImplied;

    fPowerState = powerStateOrdinal;
    CheckIrDAState();
    
    return IOPMNoErr;

Fail:
    return IOPMNoSuchState;
}



/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::interruptReadComplete
//
//      Inputs:     obj - me, param - parameter block(the Port), rc - return code, remaining - what's left
//                                                                                  (whose idea was that?)
//
//      Outputs:    None
//
//      Desc:       Interrupt pipe read completion routine
//
/****************************************************************************************************/

void AppleUSBIrDADriver::interruptReadComplete( void *obj, void *param, IOReturn rc, UInt32 remaining )
{
    AppleUSBIrDADriver  *me = (AppleUSBIrDADriver*)obj;
    //PortInfo_t            *port = (PortInfo_t*)param;
    IOReturn    ior;
    UInt32      dLen;

    XTRACE(kLogInterruptRead, me->fpinterruptPipeBuffer[0], rc);
    if (me->fIrDAOn) {                  // if we're still "up" and haven't been turned off
	check(INTERRUPT_BUFF_SIZE - remaining == 1);
	check(me->fpinterruptPipeBuffer[0] == 1);
    }
    
    if ( rc == kIOReturnSuccess )   /* If operation returned ok:    */
    {
	dLen = INTERRUPT_BUFF_SIZE - remaining;
	ELG( rc, dLen, 'iRC+', "interruptReadComplete" );
	XTRACE(kLogInterruptRead, 1, dLen);
	
	    /* Now look at the data */
//      LogData( kUSBAnyDirn, dLen, me->fpinterruptPipeBuffer );
	
	if (dLen != 1)
	{
	    XTRACE(kLogInterruptRead, 0xdead, 0xbeef);
	    ELG( 0, dLen, 'iRC-', "interruptReadComplete - what was that?" );
	} else {
	    
	    if (kUseInterruptsForRead) {            // We're using interrupts to trigger reads ...
		check(me->fReadActive == false);
		if (me->fReadActive == false) {
		    ior = me->fpInPipe->Read( me->fpPipeInMDP, &me->fReadCompletionInfo, NULL );    // start a read
		    if (ior != kIOReturnSuccess)
		    {
			ELG( 0, ior, 'icf-', "interrupt complete failed to start read");
		    } else {
			me->fReadActive = true;
		    }
		}
	    }
	}
	
	    /* Queue the next interrupt read:   */
    
	ior = me->fpInterruptPipe->Read( me->fpinterruptPipeMDP, &me->finterruptCompletionInfo, NULL );
	if ( ior == kIOReturnSuccess ) {
	    XTRACE(kLogInterruptRead, 0xffff, 0xffff);
	    return;
	}
    }

	/* Read returned with error OR next interrupt read failed to be queued: */

    XTRACE(kLogInterruptRead, 0xdead, 2);
    ELG( 0, rc, 'iRC-', "interruptReadComplete - error" );

    return;
    
}/* end interruptReadComplete */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::dataReadComplete
//
//      Inputs:     obj - me, param - parameter block(the Port), rc - return code, remaining - what's left
//
//      Outputs:    None
//
//      Desc:       BulkIn pipe (Data interface) read completion routine
//
/****************************************************************************************************/

void AppleUSBIrDADriver::dataReadComplete( void *obj, void *param, IOReturn rc, UInt32 remaining )
{
    AppleUSBIrDADriver  *me = (AppleUSBIrDADriver*)obj;
    PortInfo_t      *port = (PortInfo_t*)param;
    UInt16          dtlength;
    IOReturn        ior = kIOReturnSuccess;

    XTRACE(kLogDataReadComplete, rc, USBLapPayLoad - remaining);

#if (hasTracing > 0 && hasAppleUSBIrDATracing > 1)
    if (1) {
	int len = USBLapPayLoad - remaining;
	UInt32 w;
	UInt8 *b = me->fPipeInBuffer;
	int i;
	
	while (len > 0) {
	    w = 0;
	    for (i = 0 ; i < 4; i++) {
		w = w << 8;
		if (len > 0)            // don't run off end (pad w/zeros)
		    w = w | *b;
		b++;
		len--;
	    }
	    XTRACE(kLogInputData, w >> 16, (short)w);
	}
    }
#endif  // tracing high
    
    check(me->fReadActive == true);
    
    if ( rc == kIOReturnSuccess )   /* If operation returned ok:    */
    {
	me->fReadActive = false;
	dtlength = USBLapPayLoad - remaining;
	ELG( port->State, dtlength, 'dRC+', "dataReadComplete" );
	
//      LogData( kUSBIn, dtlength, me->fPipeInBuffer );
	
	if ( dtlength > 1 )
	{
	    if ( me->fIrDA )
	    {
		ior = me->fIrDA->ReadComplete( &me->fPipeInBuffer[1], dtlength-1 );
	    }
	    if ( ior != kIOReturnSuccess )
	    {
		ELG( 0, ior, 'IrR-', "dataReadComplete - IrDA ReadComplete problem" );
	    }
	}

	/* Queue the next read if not using interrupts */
	
	if (kUseInterruptsForRead == false) {

	    ior = me->fpInPipe->Read( me->fpPipeInMDP, &me->fReadCompletionInfo, NULL );
	    
	    if ( ior == kIOReturnSuccess )
	    {
		me->fReadActive = true;
		me->CheckQueues( port );
		return;
	    } else {
		ELG( 0, ior, 'DtQ-', "dataReadComplete - queueing bulk read failed" );
	    }
	}
    } else {
	
	/* Read returned with error */
	ELG( 0, rc, 'DtR-', "dataReadComplete - io err" );
    }

    return;
    
}/* end dataReadComplete */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::dataWriteComplete
//
//      Inputs:     obj - me, param - parameter block(the Port), rc - return code, remaining - what's left
//
//      Outputs:    None
//
//      Desc:       BulkOut pipe (Data interface) write completion routine
//
/****************************************************************************************************/

void AppleUSBIrDADriver::dataWriteComplete( void *obj, void *param, IOReturn rc, UInt32 remaining )
{
    AppleUSBIrDADriver  *me = (AppleUSBIrDADriver*)obj;
    Boolean done = true;                // write really finished?

    ELG( rc, (me->fCount - remaining), 'dWCt', "dataWriteComplete" );
    XTRACE(kLogDataWriteComplete, rc, me->fCount - remaining);
    
    check(me->fWriteActive);
    me->fWriteActive = false;
    
    // first check for speed change, it's the only time we do a single-byte write
    if (me->fCount == 1) {
	if (me->fIrDA)
	    me->fIrDA->SetSpeedComplete(rc == kIOReturnSuccess);
	return;
    }
    
    // in a transmit complete, but need to manually transmit a zero-length packet
    // if it's a multiple of the max usb packet size for the bulk-out pipe (64 bytes)
    if ( rc == kIOReturnSuccess )   /* If operation returned ok:    */
    {
	if ( me->fCount > 0 )                       // Check if it was not a zero length write
	{
	    if ( (me->fCount % 64) == 0 )               // If was a multiple of 64 bytes then we need to do a zero length write
	    {
		XTRACE(kLogDataWriteCompleteZero, 0, 0);
		LogData( kUSBOut, 0, me->fPipeOutBuffer );
		me->fWriteActive = true;
		me->fpPipeOutMDP->setLength( 0 );
		me->fCount = 0;
		me->fpOutPipe->Write( me->fpPipeOutMDP, &me->fWriteCompletionInfo );
		done = false;               // don't complete back to irda quite yet
	    }
	}
    }
    
    if (done && me->fIrDA )     // if time to let irda know the write has finished
    {
	me->fIrDA->Transmit_Complete( rc == kIOReturnSuccess ); // let IrDA know how write finished
    }
    return;
    
}/* end dataWriteComplete */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::free
//
//      Inputs:     None
//
//      Outputs:    None
//
//      Desc:       Clean up and free the log 
//
/****************************************************************************************************/

void AppleUSBIrDADriver::free()
{
    XTRACE(kLogFree, 0, 0);
    ELG( 0, 0, 'free', "free" );
    
    if (fIrDA) {
	fIrDA->release();   // we don't do delete's in the kernal I suppose ...
	fIrDA=NULL;
    }
	    
#if USE_ELG
    if ( g.evLogBuf )
	IOFree( g.evLogBuf, kEvLogSize );
#endif /* USE_ELG */

    super::free();
    
    XTRACE(kLogFree, 0xffff, 0xffff);
    return;
    
}/* end free */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::stop
//
//      Inputs:     provider - my provider
//
//      Outputs:    None
//
//      Desc:       Stops
//
/****************************************************************************************************/

void AppleUSBIrDADriver::stop( IOService *provider )
{
    XTRACE(kLogStop, (int)provider >> 16, provider);
    ELG( 0, 0, 'stop', "stop" );
    
    fUSBStarted = false;        // reset usb start/stop flag for CheckIrDAState
    CheckIrDAState();           // turn irda off, release resources
    
    destroySerialStream();      // release the bsd tty
	
    destroyNub();               // delete the nubs and fPort
    
    if ( fpInterface )  
    {
	fpInterface->release();     // retain done in ConfigureDevice
	fpInterface = NULL; 
    }
    
    // release our power manager state
    PMstop();
    
    super::stop( provider );
    XTRACE(kLogStop, 0xffff, 0xffff);
    return;
    
}/* end stop */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::allocateResources
//
//      Inputs:     
//
//      Outputs:    return code - true (allocate was successful), false (it failed)
//
//      Desc:       Finishes up the rest of the configuration and gets all the endpoints open 
//
/****************************************************************************************************/

bool AppleUSBIrDADriver::allocateResources( void )
{
    IOUSBFindEndpointRequest    epReq;      // endPoint request struct on stack
    bool                        goodCall;   // return flag fm Interface call

    ELG( 0, 0, 'Allo', "allocateResources." );
    XTRACE(kLogAllocateResources, 0, 0);

    // Open all the end points
    require(fpInterface, Fail);
	
    goodCall = fpInterface->open( this );       // close done in releaseResources
    if ( !goodCall )
    {
	ELG( 0, 0, 'epD-', "allocateResources - open data interface failed." );
	fpInterface->release();
	fpInterface = NULL;
	return false;
    }

    fpInterfaceNumber = fpInterface->GetInterfaceNumber();
    
    epReq.type          = kUSBBulk;
    epReq.direction     = kUSBIn;
    epReq.maxPacketSize = 0;
    epReq.interval      = 0;
    fpInPipe = fpInterface->FindNextPipe( 0, &epReq );
    require(fpInPipe, Fail);
    ELG( epReq.maxPacketSize << 16 |epReq.interval, fpInPipe, 'i P+', "allocateResources - bulk input pipe." );

    epReq.direction = kUSBOut;
    fpOutPipe = fpInterface->FindNextPipe( 0, &epReq );
    require(fpOutPipe, Fail);
    ELG( epReq.maxPacketSize << 16 |epReq.interval, fpOutPipe, 'o P+', "allocateResources - bulk output pipe." );

    epReq.type          = kUSBInterrupt;
    epReq.direction     = kUSBIn;
    fpInterruptPipe = fpInterface->FindNextPipe( 0, &epReq );
    require(fpInterruptPipe, Fail);
    ELG( epReq.maxPacketSize << 16 |epReq.interval, fpInterruptPipe, 'irP+', "allocateResources - interrupt pipe." );

    // Allocate Memory Descriptor Pointer with memory for the interrupt-in pipe:

    fpinterruptPipeMDP = IOBufferMemoryDescriptor::withCapacity( INTERRUPT_BUFF_SIZE, kIODirectionIn );
    require(fpinterruptPipeMDP, Fail);
    
    fpinterruptPipeMDP->setLength( INTERRUPT_BUFF_SIZE );
    fpinterruptPipeBuffer = (UInt8*)fpinterruptPipeMDP->getBytesNoCopy();
    ELG( 0, fpinterruptPipeBuffer, 'iBuf', "allocateResources - interrupt in buffer" );

    // Allocate Memory Descriptor Pointer with memory for the data-in bulk pipe:

    fpPipeInMDP = IOBufferMemoryDescriptor::withCapacity( USBLapPayLoad, kIODirectionIn );
    require(fpPipeInMDP, Fail);
    
    fpPipeInMDP->setLength( USBLapPayLoad );
    fPipeInBuffer = (UInt8*)fpPipeInMDP->getBytesNoCopy();
    ELG( 0, fPipeInBuffer, 'iBuf', "allocateResources - input buffer" );

    // Allocate Memory Descriptor Pointer with memory for the data-out bulk pipe:

    fpPipeOutMDP = IOBufferMemoryDescriptor::withCapacity( MAX_BLOCK_SIZE, kIODirectionOut );
    require(fpPipeOutMDP, Fail);
    
    fpPipeOutMDP->setLength( MAX_BLOCK_SIZE );
    fPipeOutBuffer = (UInt8*)fpPipeOutMDP->getBytesNoCopy();
    ELG( 0, fPipeOutBuffer, 'oBuf', "allocateResources - output buffer" );
    
    // set up the completion info for all three pipes
    
    require(fPort, Fail);
    finterruptCompletionInfo.target = this;
    finterruptCompletionInfo.action = interruptReadComplete;
    finterruptCompletionInfo.parameter  = fPort;
    
    fReadCompletionInfo.target  = this;
    fReadCompletionInfo.action  = dataReadComplete;
    fReadCompletionInfo.parameter   = fPort;

    fWriteCompletionInfo.target = this;
    fWriteCompletionInfo.action = dataWriteComplete;
    fWriteCompletionInfo.parameter  = fPort;
    

    ELG( 0, 0, 'aRs+', "allocateResources successful" );
    return true;

Fail:
    return false;
    
} // allocateResources


/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::releaseResources
//
//      Inputs:     port - the Port
//
//      Outputs:    None
//
//      Desc:       Frees up the pipe resources allocated in allocateResources
//
/****************************************************************************************************/

void AppleUSBIrDADriver::releaseResources( void )
{
    ELG( 0, 0, 'rlRs', "releaseResources" );
    XTRACE(kLogReleaseResources, 0, 0);
    
    if ( fpInterface )  
    { 
	fpInterface->close( this ); 
    }    
    
    if ( fpPipeOutMDP  )    
    { 
	fpPipeOutMDP->release();    
	fpPipeOutMDP    = 0; 
    }
    
    if ( fpPipeInMDP   )    
    { 
	fpPipeInMDP->release(); 
	fpPipeInMDP     = 0; 
    }
    
    if ( fpinterruptPipeMDP )   
    { 
	fpinterruptPipeMDP->release();  
	fpinterruptPipeMDP  = 0; 
    }

    return;
    
}/* end releaseResources */

//
// start reading on the pipes
//
bool AppleUSBIrDADriver::startPipes( void )
{
    IOReturn                    rtn;
    
    require(fPort, Fail);
    require(fpinterruptPipeMDP, Fail);
    require(fpPipeInMDP, Fail);
    require(fpPipeOutMDP, Fail);


    if (kUseInterruptsForRead) {                // read on interrupt pipe if using interrupts
	rtn = fpInterruptPipe->Read(fpinterruptPipeMDP, &finterruptCompletionInfo, NULL );
    }
    else {                                      // Read the data-in bulk pipe if not using interrupts
	rtn = fpInPipe->Read(fpPipeInMDP, 1000, 1000, &fReadCompletionInfo, NULL );
    }
    require(rtn == kIOReturnSuccess, Fail);
    
    // is this really referenced by anyone??
    fReadActive = (kUseInterruptsForRead == false);     // remember if we did a read

    return true;
    
Fail:
    return false;
}/* end startPipes */

//
// stop i/o on the pipes
//
void AppleUSBIrDADriver::stopPipes()
{
    if (fpInPipe)           fpInPipe->Abort();
    if (fpOutPipe)          fpOutPipe->Abort();
    if (fpInterruptPipe)    fpInterruptPipe->Abort();
}


/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::configureDevice
//
//      Inputs:     numConfigs - number of configurations present
//
//      Outputs:    return Code - true (device configured), false (device not configured)
//
//      Desc:       Finds the configurations and then the appropriate interfaces etc.
//
/****************************************************************************************************/

bool AppleUSBIrDADriver::configureDevice( UInt8 numConfigs )
{
    IOUSBFindInterfaceRequest           req;            // device request Class on stack
    const IOUSBConfigurationDescriptor  *cd = NULL;     // configuration descriptor
    IOUSBInterfaceDescriptor            *intf = NULL;   // interface descriptor
    IOReturn                            ior;
    UInt8                               cval;
    UInt8                               config = 0;
    USBIrDAQoS                          *qos;
       
    ELG( 0, numConfigs, 'cDev', "configureDevice" );
    XTRACE(kLogConfigureDevice, 0, 0);
    
    for (cval=0; cval<numConfigs; cval++)
    {
	ELG( 0, cval, 'CkCn', "configureDevice - Checking Configuration" );
	
	cd = fpDevice->GetFullConfigurationDescriptor(cval);
	if ( !cd )
	{
	    ELG( 0, 0, 'GFC-', "configureDevice - Error getting the full configuration descriptor" );
	} else {
	
		// Find the first one - there may be more to go on in the future
		
	    req.bInterfaceClass = kIOUSBFindInterfaceDontCare;
	    req.bInterfaceSubClass  = kIOUSBFindInterfaceDontCare;
	    req.bInterfaceProtocol  = kIOUSBFindInterfaceDontCare;
	    req.bAlternateSetting   = kIOUSBFindInterfaceDontCare;
	    ior = fpDevice->FindNextInterfaceDescriptor(cd, intf, &req, &intf);
	    if ( ior == kIOReturnSuccess )
	    {
		if ( intf )
		{
		    config = cd->bConfigurationValue;
		    ELG( cd, config, 'FNI+', "configureDevice - Interface descriptor found" );
		    break;
		} else {
		    ELG( 0, config, 'FNI-', "configureDevice - That's weird the interface was null" );
		    cd = NULL;
		}
	    } else {
		ELG( 0, cval, 'FNID', "configureDevice - No CDC interface found this configuration" );
		cd = NULL;
	    }
	}
    }
	    
    if ( !cd )
    {
	return false;
    }
    
	// Now lets do it for real
	
    req.bInterfaceClass = kIOUSBFindInterfaceDontCare;
    req.bInterfaceSubClass  = kIOUSBFindInterfaceDontCare;
    req.bInterfaceProtocol  = kIOUSBFindInterfaceDontCare;
    req.bAlternateSetting   = kIOUSBFindInterfaceDontCare;
    
    fpInterface = fpDevice->FindNextInterface( NULL, &req );
    if ( !fpInterface )
    {
	ELG( 0, 0, 'FIC-', "configureDevice - Find next interface failed" );
	return false;
    }
    fpInterface->retain();      // release done in stop()
    
	// Get the QoS Functional Descriptor (it's the only one)
    
    qos = (USBIrDAQoS *)fpInterface->FindNextAssociatedDescriptor(NULL, USBIrDAClassDescriptor);
    if (!qos)
    {
	ELG( 0, 0, 'OSF-', "configureDevice - No QOS descriptor" );
	
	/* Set some defaults - qos values need tuning of course */
	
	fQoS.bFunctionLength = 12;
	fQoS.bDescriptorType = USBIrDAClassDescriptor;
	fQoS.version = 0x100;
	fQoS.datasize = 0x1f;       // 1k = 1f, 2k = 3f
	fQoS.windowsize = 1;
	fQoS.minturn = 2;           // review & tune.
	fQoS.baud1 = 0x01;          // 4mbit no mir, all sir
	fQoS.baud2 = 0x3e;
	fQoS.bofs = 4;              // review and tune.
	fQoS.sniff = 0;
	fQoS.unicast = 0;
    } else {
	ELG( qos->bDescriptorType, qos, 'QSFD', "AppleUSBCDCDriver::configureDevice - Got QoS Functional Descriptor" );
	
	/* Save the real values */
	
	fQoS.bFunctionLength = qos->bFunctionLength;
	fQoS.bDescriptorType = qos->bDescriptorType;
	fQoS.version = USBToHostWord(qos->version);
	fQoS.datasize = qos->datasize;
	fQoS.windowsize = qos->windowsize;
	fQoS.minturn = qos->minturn;
	fQoS.baud1 = qos->baud2;        // flipped because of our good friends at you know who
	fQoS.baud2 = qos->baud1;
	fQoS.bofs = qos->bofs;
	fQoS.sniff = qos->sniff;
	fQoS.unicast = qos->unicast;
    }
    
	
    // irda starts up turned off so always try and suspend the hardware
    ior = fpDevice->SuspendDevice( true );         // Suspend the device
    require(ior == kIOReturnSuccess, Fail);
	
    return true;
    
Fail:
    return false;

}/* end configureDevice */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::createSuffix
//
//      Inputs:     None
//
//      Outputs:    return Code - true (suffix created), false (suffix not create), sufKey - the key
//
//      Desc:       Creates the suffix key. It attempts to use the serial number string from the device
//                  if it's reasonable i.e. less than 8 bytes ascii. Remember it's stored in unicode 
//                  format. If it's not present or not reasonable it will generate the suffix based 
//                  on the location property tag. At least this remains the same across boots if the
//                  device is plugged into the same physical location. In the latter case trailing
//                  zeros are removed.
//
/****************************************************************************************************/

bool AppleUSBIrDADriver::createSuffix( unsigned char *sufKey )
{
    
    IOReturn                rc;
    UInt8                   serBuf[10];     // arbitrary size > 8
    OSNumber                *location;
    UInt32                  locVal;
    UInt8                   *rlocVal;
    UInt16                  offs, i, sig = 0;
    UInt8                   indx;
    bool                    keyOK = false;      
    
    ELG( 0, 0, 'cSuf', "createSuffix" );
    
    indx = fpDevice->GetSerialNumberStringIndex();  
    if (indx != 0 )
    {   
	// Generate suffix key based on the serial number string (if reasonable <= 8 and > 0)   

	rc = fpDevice->GetStringDescriptor(indx, (char *)&serBuf, sizeof(serBuf));
	if ( !rc )
	{
	    if ( (strlen((char *)&serBuf) < 9) && (strlen((char *)&serBuf) > 0) )
	    {
		strcpy( (char *)sufKey, (const char *)&serBuf);
		keyOK = true;
	    }           
	} else {
	    ELG( 0, rc, 'Sdt-', "createSuffix error reading serial number string" );
	}
    }
    
    if ( !keyOK )
    {
	// Generate suffix key based on the location property tag
    
	location = (OSNumber *)fpDevice->getProperty(kUSBDevicePropertyLocationID);
	if ( location )
	{
	    locVal = location->unsigned32BitValue();        
	    offs = 0;
	    rlocVal = (UInt8*)&locVal;
	    for (i=0; i<4; i++)
	    {
		sufKey[offs] = Asciify(rlocVal[i] >> 4);
		if ( sufKey[offs++] != '0')
		    sig = offs;
		sufKey[offs] = Asciify(rlocVal[i]);
		if ( sufKey[offs++] != '0')
		    sig = offs;
	    }           
	    sufKey[sig] = 0x00;
	    keyOK = true;
	}
    }
    
    return keyOK;

}/* end createSuffix */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::createSerialStream
//
//      Inputs:     None
//
//      Outputs:    return Code - true (created and initialilzed ok), false (it failed)
//
//      Desc:       Creates and initializes the nub and port structure
//
/****************************************************************************************************/

bool AppleUSBIrDADriver::createSerialStream()
{
    UInt8           indx;
    IOReturn            rc;
    unsigned char       rname[10];
    const char          *suffix = (const char *)&rname;
    
    ELG( 0, fNub, '=Nub', "createSerialStream" );
    XTRACE(kLogCreateSerialStream, 0, 0);
    
    check(fNub && fPort);
    if (!fNub || !fPort) return false;

    SetStructureDefaults( fPort, true );            // init the Port structure
    
    // Allocate the request lock
    fPort->serialRequestLock = IOLockAlloc();   // init lock used to protect code on MP
    if ( !fPort->serialRequestLock ) {
	return false;
    }
    
    // now the ring buffers
    if (!allocateRingBuffer(&(fPort->TX), fPort->TXStats.BufferSize) ||
	!allocateRingBuffer(&(fPort->RX), fPort->RXStats.BufferSize)) 
    {
	return false;
    }

	
    if ( !fTerminate )
    {
	// Report the base name to be used for generating device nodes
    
	fNub->setProperty( kIOTTYBaseNameKey, baseName );
    
	// Create suffix key and set it
    
	if ( createSuffix( (unsigned char *)suffix ) )
	{       
	    fNub->setProperty( kIOTTYSuffixKey, suffix );
	}

	
	// Save the Product String  (at least the first productNameLength's worth).
	
	indx = fpDevice->GetProductStringIndex();   
	if ( indx != 0 )
	{   
	    rc = fpDevice->GetStringDescriptor( indx, (char *)&fProductName, sizeof(fProductName) );
	    if ( !rc )
	    {
		if ( strlen((char *)fProductName) == 0 )        // believe it or not this sometimes happens (null string with an index defined???)
		{
		    strcpy( (char *)fProductName, defaultName);
		}
		fNub->setProperty( (const char *)propertyTag, (const char *)fProductName );
	    }
	}
	    
	fNub->registerService();
    }
    
    return true;
    
}/* end createSerialStream */

//
// release things created in createSerialStream
//
void
AppleUSBIrDADriver::destroySerialStream(void)
{
    require(fPort, Fail);
    
    if ( fPort->serialRequestLock )
    {
	IOLockFree( fPort->serialRequestLock ); // free the Serial Request Lock
	fPort->serialRequestLock = NULL;
    }

    // Remove all the buffers.

    freeRingBuffer( &fPort->TX );
    freeRingBuffer( &fPort->RX );

    removeProperty( (const char *)propertyTag );    // unhook from BSD
    
Fail:
    return;
}

//
// startIrDA
//
// assumes createSerialStream is called once at usb start time
// calls allocateResources to open endpoints
//
bool
AppleUSBIrDADriver::startIrDA()
{
    bool ok;
    
    require(fIrDA == NULL, Fail);
    require(fNub, Fail);
    require(fUserClientNub, Fail);
    
    Workaround();                           // make chip as sane as can be
    
    ok = allocateResources();               // open the pipe endpoints
    require(ok, Fail);
    
    startPipes();                           // start reading on the usb pipes

    fBaudCode = kLinkSpeed9600;             // the code for 9600 (see BaudRate above)
    fLastChangeByte = 0;                    // no known state of device, force mode to change on first i/o
    fCurrentBaud = 9600;
    SetBofCount(10);                        // start with about 10 bofs (sets fBofsCode)

    fIrDA = IrDAComm::irDAComm(fNub, fUserClientNub);       // create and init and start IrDA
    require(fIrDA, Fail);
	
    return true;

Fail:
    return false;
}

void
AppleUSBIrDADriver::stopIrDA()
{
    require(fIrDA, Fail);
    
    fIrDA->Stop();
    fIrDA->release();
    fIrDA = NULL;

    stopPipes();                            // stop reading on the usb pipes
    
    if (fpPipeOutMDP != NULL)               // better test for releaseResources?
	releaseResources( );
    
Fail:
    return;
}

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::createNub
//
//      Inputs:
//
//      Outputs:    fNub (an AppleUSBIrDA glue object) and fPort
//
//      Desc:       allocates and inits, but doesn't publish the BSD info on the nub yet
//              create serial stream finishes the job later.
//
/****************************************************************************************************/
bool
AppleUSBIrDADriver::createNub(void)
{
    bool ret;
    
    if (fNub == NULL) {
	fNub = new AppleUSBIrDA;
    }
    require(fNub, Failed);
    check(fNub->getRetainCount() == 1);     // testing
    
    if (fPort == NULL) {
	fPort = (PortInfo_t*)IOMalloc( sizeof(PortInfo_t) );
    }
    require(fPort, Failed);
    bzero(fPort, sizeof(PortInfo_t));
				    
    ret = fNub->init(0, fPort);
    require(ret == true, Failed);
    
    ret = fNub->attach( this );
    require(ret == true, Failed);
    check(fNub->getRetainCount() == 2);     // testing
    
    XTRACE(kLogNewNub, (int)fNub >> 16, fNub);
    XTRACE(kLogNewPort, (int)fPort >> 16, fPort);
    
    // now make the nub to act as a communication point for user-client
    if (fUserClientNub == NULL)
	fUserClientNub = AppleIrDA::withNub(fNub);      // it talks to the serial nub ...
    require(fUserClientNub, Failed);
    
    fUserClientNub->attach(this);
    
    return true;
	
Failed:
    IOLog("Create nub failed\n");
    // could try and clean up here, but let's start by just not crashing.
    return false;
}

void AppleUSBIrDADriver::destroyNub()
{
    if (fPort != NULL) {
	IOFree( fPort, sizeof(PortInfo_t) );
	fPort = NULL;
    }
    
    if (fUserClientNub) {
	XTRACE(kLogDestroyNub, 1, fUserClientNub->getRetainCount());
	fUserClientNub->detach(this);
	fUserClientNub->release();
	fUserClientNub = NULL;
    }
    
    if (fNub) {
	XTRACE(kLogDestroyNub, 2, fNub->getRetainCount());
	fNub->detach(this);
	fNub->release();    // crash boom?
	fNub = NULL;
    }
}

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::acquirePort
//
//      Inputs:     sleep - true (wait for it), false (don't), refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess, kIOReturnExclusiveAccess, kIOReturnIOError and various others
//
//      Desc:       acquirePort tests and sets the state of the port object.  If the port was
//                  available, then the state is set to busy, and kIOReturnSuccess is returned.
//                  If the port was already busy and sleep is YES, then the thread will sleep
//                  until the port is freed, then re-attempts the acquire.  If the port was
//                  already busy and sleep is NO, then kIOReturnExclusiveAccess is returned.
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::acquirePort( bool sleep, void *refCon )
{
    PortInfo_t          *port = (PortInfo_t *) refCon;
    UInt32              busyState = 0;
    IOReturn            rtn = kIOReturnSuccess;

    ELG( port, sleep, 'acqP', "acquirePort" );
    XTRACE(kLogAcquirePort, 0, 0);
    
    if ( fTerminate ) {
	int review_fTerminate;
	//return kIOReturnOffline;
    }
    SetStructureDefaults( port, FALSE );    /* Initialize all the structures */
    
    for (;;)
    {
	busyState = readPortState( port ) & PD_S_ACQUIRED;
	if ( !busyState )
	{       
	    // Set busy bit, and clear everything else
	    changeState( port, (UInt32)PD_S_ACQUIRED | DEFAULT_STATE, (UInt32)STATE_ALL);
	    break;
	} else {
	    if ( !sleep )
	    {
		ELG( 0, 0, 'busy', "acquirePort - Busy exclusive access" );
		return kIOReturnExclusiveAccess;
	    } else {
		busyState = 0;
		rtn = watchState( &busyState, PD_S_ACQUIRED, refCon );
		if ( (rtn == kIOReturnIOError) || (rtn == kIOReturnSuccess) )
		{
		    continue;
		} else {
		    ELG( 0, 0, 'int-', "acquirePort - Interrupted!" );
		    return rtn;
		}
	    }
	}
    } /* end for */
    
    fSessions++;    //bump number of active sessions and turn on clear to send
    changeState( port, PD_RS232_S_CTS, PD_RS232_S_CTS);

    CheckIrDAState();       // turn irda on/off if appropriate
    
    if (1) {                // wait for initial connect
	int counter = 0;
	//while (fIrDA && fIrDA->Starting()) {
	while (counter++ < (10 * 10)) {     // sanity check limit of 10 seconds
	    if (fIrDA && (fIrDA->Starting() == false)) break;
	    XTRACE(kLogAcquirePort, counter, 1);
	    IOSleep(100);                   // wait 1/10 of a second between polls and yield time
	}
	//IOLog("AppleUSBIrDA: acquire port paused %d ms for initial connection\n", counter*100);
    }
    
    return rtn;
    
}/* end acquirePort */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::releasePort
//
//      Inputs:     refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess or kIOReturnNotOpen
//
//      Desc:       releasePort returns all the resources and does clean up.
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::releasePort( void *refCon )
{
    PortInfo_t          *port = (PortInfo_t *) refCon;
    UInt32              busyState;

    ELG( 0, port, 'relP', "releasePort" );
    XTRACE(kLogReleasePort, 0, 0);
    
    busyState = (readPortState( port ) & PD_S_ACQUIRED);
    if ( !busyState )
    {
	ELG( 0, 0, 'rlP-', "releasePort - NOT OPEN" );
	return kIOReturnNotOpen;
    }
    
    changeState( port, 0, (UInt32)STATE_ALL );  // Clear the entire state word which also deactivates the port

    fSessions--;        // reduce number of active sessions
    CheckIrDAState();   // turn irda off if appropriate

    if ((fTerminate) && (fSessions == 0))       // if it's the result of a terminate and session count is zero we also need to close things
    {
	if (0 && fpInterface )      // jdg - this was bogus
	{
	    fpInterface->close( this ); 
	    fpInterface->release();
	    fpInterface = NULL; 
	}
       // else IOLog("appleusbirda - would have released fpInteface here\n");
    }
    
    ELG( 0, 0, 'RlP+', "releasePort - OK" );
    
    return kIOReturnSuccess;
    
}/* end releasePort */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::setState
//
//      Inputs:     state - state to set, mask - state mask, refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess or kIOReturnBadArgument
//
//      Desc:       Set the state for the port device.  The lower 16 bits are used to set the
//                  state of various flow control bits (this can also be done by enqueueing a
//                  PD_E_FLOW_CONTROL event).  If any of the flow control bits have been set
//                  for automatic control, then they can't be changed by setState.  For flow
//                  control bits set to manual (that are implemented in hardware), the lines
//                  will be changed before this method returns.  The one weird case is if RXO
//                  is set for manual, then an XON or XOFF character may be placed at the end
//                  of the TXQ and transmitted later.
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::setState( UInt32 state, UInt32 mask, void *refCon )
{
    PortInfo_t *port = (PortInfo_t *) refCon;
    
    ELG( state, mask, 'stSt', "setState" );
    XTRACE(kLogSetState, 0, 0);
    
    if ( mask & (PD_S_ACQUIRED | PD_S_ACTIVE | (~EXTERNAL_MASK)) )
	return kIOReturnBadArgument;

    if ( readPortState( port ) & PD_S_ACQUIRED )
    {
	    // ignore any bits that are read-only
	mask &= (~port->FlowControl & PD_RS232_A_MASK) | PD_S_MASK;

	if ( mask)
	    changeState( port, state, mask );

	return kIOReturnSuccess;
    }

    return kIOReturnNotOpen;
    
}/* end setState */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::getState
//
//      Inputs:     refCon - the Port
//
//      Outputs:    state - port state
//
//      Desc:       Get the state for the port.
//
/****************************************************************************************************/

UInt32 AppleUSBIrDADriver::getState( void *refCon )
{
    PortInfo_t  *port = (PortInfo_t *) refCon;
    UInt32      state;
    
    ELG( 0, port, 'gtSt', "getState" );
    
    CheckQueues( port );
	
    state = readPortState( port ) & EXTERNAL_MASK;
    
    ELG( state, EXTERNAL_MASK, 'gtS-', "getState-->State" );
	XTRACE(kLogGetState, state >> 16, (short)state);
    
    return state;
    
}/* end getState */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::watchState
//
//      Inputs:     state - state to watch for, mask - state mask bits, refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess or value returned from ::watchState
//
//      Desc:       Wait for the at least one of the state bits defined in mask to be equal
//                  to the value defined in state. Check on entry then sleep until necessary,
//                  see watchState for more details.
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::watchState( UInt32 *state, UInt32 mask, void *refCon)
{
    PortInfo_t  *port = (PortInfo_t *) refCon;
    IOReturn    ret = kIOReturnNotOpen;

    ELG( *state, mask, 'WatS', "watchState" );
    XTRACE(kLogWatchState, 0, 0);

    if ( readPortState( port ) & PD_S_ACQUIRED )
    {
	ret = kIOReturnSuccess;
	mask &= EXTERNAL_MASK;
	ret = privateWatchState( port, state, mask );
	*state &= EXTERNAL_MASK;
    }
    
    ELG( ret, 0, 'WatS', "watchState --> watchState" );
    XTRACE(kLogWatchState, 0xffff, 0xffff);
    return ret;
    
}/* end watchState */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::nextEvent
//
//      Inputs:     refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess
//
//      Desc:       Not used by this driver.
//
/****************************************************************************************************/

UInt32 AppleUSBIrDADriver::nextEvent( void *refCon )
{
    UInt32      ret = kIOReturnSuccess;

    ELG( 0, 0, 'NxtE', "nextEvent" );

    return ret;
    
}/* end nextEvent */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::executeEvent
//
//      Inputs:     event - The event, data - any data associated with the event, refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess, kIOReturnNotOpen or kIOReturnBadArgument
//
//      Desc:       executeEvent causes the specified event to be processed immediately.
//                  This is primarily used for channel control commands like START & STOP
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::executeEvent( UInt32 event, UInt32 data, void *refCon )
{
    PortInfo_t  *port = (PortInfo_t *) refCon;
    IOReturn    ret = kIOReturnSuccess;
    UInt32      state, delta;
    
    delta = 0;
    state = readPortState( port );  
    ELG( port, state, 'ExIm', "executeEvent" );
    XTRACE(kLogExecEvent, event >> 16, (short)event);
    XTRACE(kLogExecEventData, data >> 16, (short)data);
    
    if ( (state & PD_S_ACQUIRED) == 0 )
	return kIOReturnNotOpen;

    switch ( event )
    {
    case PD_RS232_E_XON_BYTE:
	ELG( data, event, 'ExIm', "executeEvent - PD_RS232_E_XON_BYTE" );
	port->XONchar = data;
	break;
    case PD_RS232_E_XOFF_BYTE:
	ELG( data, event, 'ExIm', "executeEvent - PD_RS232_E_XOFF_BYTE" );
	port->XOFFchar = data;
	break;
    case PD_E_SPECIAL_BYTE:
	ELG( data, event, 'ExIm', "executeEvent - PD_E_SPECIAL_BYTE" );
	port->SWspecial[ data >> SPECIAL_SHIFT ] |= (1 << (data & SPECIAL_MASK));
	break;

    case PD_E_VALID_DATA_BYTE:
	ELG( data, event, 'ExIm', "executeEvent - PD_E_VALID_DATA_BYTE" );
	port->SWspecial[ data >> SPECIAL_SHIFT ] &= ~(1 << (data & SPECIAL_MASK));
	break;

    case PD_E_FLOW_CONTROL:
	ELG( data, event, 'ExIm', "executeEvent - PD_E_FLOW_CONTROL" );
	break;

    case PD_E_ACTIVE:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_ACTIVE" );
	if ( (bool)data )
	{
	    if ( !(state & PD_S_ACTIVE) )
	    {
		SetStructureDefaults( port, FALSE );
		changeState( port, (UInt32)PD_S_ACTIVE, (UInt32)PD_S_ACTIVE ); // activate port
	    }
	} else {
	    if ( (state & PD_S_ACTIVE) )
	    {
		changeState( port, 0, (UInt32)PD_S_ACTIVE );
	    }
	}
	break;

    case PD_E_DATA_LATENCY:
	ELG( data, event, 'ExIm', "executeEvent - PD_E_DATA_LATENCY" );
	port->DataLatInterval = long2tval( data * 1000 );
	break;

    case PD_RS232_E_MIN_LATENCY:
	ELG( data, event, 'ExIm', "executeEvent - PD_RS232_E_MIN_LATENCY" );
	port->MinLatency = bool( data );
	break;

    case PD_E_DATA_INTEGRITY:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_DATA_INTEGRITY" );
	if ( (data < PD_RS232_PARITY_NONE) || (data > PD_RS232_PARITY_SPACE))
	{
	    ret = kIOReturnBadArgument;
	}
	else
	{
	    port->TX_Parity = data;
	    port->RX_Parity = PD_RS232_PARITY_DEFAULT;          
	}
	break;

    case PD_E_DATA_RATE:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_DATA_RATE" );
	    /* For API compatiblilty with Intel.    */
	data >>= 1;
	ELG( 0, data, 'Exlm', "executeEvent - actual data rate" );
	if ( (data < kMinBaudRate) || (data > kMaxBaudRate) )       // Do we really care
	    ret = kIOReturnBadArgument;
	else
	{
	    port->BaudRate = data;
	}       
	break;

    case PD_E_DATA_SIZE:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_DATA_SIZE" );
	    /* For API compatiblilty with Intel.    */
	data >>= 1;
	ELG( 0, data, 'Exlm', "executeEvent - actual data size" );
	if ( (data < 5) || (data > 8) )
	    ret = kIOReturnBadArgument;
	else
	{
	    port->CharLength = data;            
	}
	break;

    case PD_RS232_E_STOP_BITS:
	ELG( data, event, 'Exlm', "executeEvent - PD_RS232_E_STOP_BITS" );
	if ( (data < 0) || (data > 20) )
	    ret = kIOReturnBadArgument;
	else
	{
	    port->StopBits = data;
	}
	break;

    case PD_E_RXQ_FLUSH:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_RXQ_FLUSH" );
	break;

    case PD_E_RX_DATA_INTEGRITY:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_RX_DATA_INTEGRITY" );
	if ( (data != PD_RS232_PARITY_DEFAULT) &&  (data != PD_RS232_PARITY_ANY) )
	    ret = kIOReturnBadArgument;
	else
	    port->RX_Parity = data;
	break;

    case PD_E_RX_DATA_RATE:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_RX_DATA_RATE" );
	if ( data )
	    ret = kIOReturnBadArgument;
	break;

    case PD_E_RX_DATA_SIZE:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_RX_DATA_SIZE" );
	if ( data )
	    ret = kIOReturnBadArgument;
	break;

    case PD_RS232_E_RX_STOP_BITS:
	ELG( data, event, 'Exlm', "executeEvent - PD_RS232_E_RX_STOP_BITS" );
	if ( data )
	    ret = kIOReturnBadArgument;
	break;

    case PD_E_TXQ_FLUSH:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_TXQ_FLUSH" );
	break;

    case PD_RS232_E_LINE_BREAK:
	ELG( data, event, 'Exlm', "executeEvent - PD_RS232_E_LINE_BREAK" );
	state &= ~PD_RS232_S_BRK;
	delta |= PD_RS232_S_BRK;
	break;

    case PD_E_DELAY:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_DELAY" );
	port->CharLatInterval = long2tval(data * 1000);
	break;
	
    case PD_E_RXQ_SIZE:
	ELG( 0, event, 'Exlm', "executeEvent - PD_E_RXQ_SIZE" );
	break;

    case PD_E_TXQ_SIZE:
	ELG( 0, event, 'Exlm', "executeEvent - PD_E_TXQ_SIZE" );
	break;

    case PD_E_RXQ_HIGH_WATER:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_RXQ_HIGH_WATER" );
	break;

    case PD_E_RXQ_LOW_WATER:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_RXQ_LOW_WATER" );
	break;

    case PD_E_TXQ_HIGH_WATER:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_TXQ_HIGH_WATER" );
	break;

    case PD_E_TXQ_LOW_WATER:
	ELG( data, event, 'Exlm', "executeEvent - PD_E_TXQ_LOW_WATER" );
	break;

    default:
	ELG( data, event, 'Exlm', "executeEvent - unrecognized event" );
	ret = kIOReturnBadArgument;
	break;
    }

    state |= state;/* ejk for compiler warnings. ?? */
    changeState( port, state, delta );
    
    return ret;
    
}/* end executeEvent */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::requestEvent
//
//      Inputs:     event - The event, refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess, kIOReturnBadArgument, data - any data associated with the event
//
//      Desc:       requestEvent processes the specified event as an immediate request and
//                  returns the results in data.  This is primarily used for getting link
//                  status information and verifying baud rate and such.
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::requestEvent( UInt32 event, UInt32 *data, void *refCon )
{
    PortInfo_t  *port = (PortInfo_t *) refCon;
    IOReturn    returnValue = kIOReturnSuccess;

    ELG( 0, readPortState( port ), 'ReqE', "requestEvent" );
    XTRACE(kLogReqEvent, event >> 16, (short)event);

    if ( data == NULL ) {
	ELG( 0, event, 'ReqE', "requestEvent - data is null" );
	returnValue = kIOReturnBadArgument;
    }
    else
    {
	XTRACE(kLogReqEventData, (*data) >> 16, (short)*data);
	switch ( event )
	{
	    case PD_E_ACTIVE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_ACTIVE" );
		*data = bool(readPortState( port ) & PD_S_ACTIVE);  
		break;
	    
	    case PD_E_FLOW_CONTROL:
		ELG( port->FlowControl, event, 'ReqE', "requestEvent - PD_E_FLOW_CONTROL" );
		*data = port->FlowControl;                          
		break;
	    
	    case PD_E_DELAY:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_DELAY" );
		*data = tval2long( port->CharLatInterval )/ 1000;   
		break;
	    
	    case PD_E_DATA_LATENCY:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_DATA_LATENCY" );
		*data = tval2long( port->DataLatInterval )/ 1000;   
		break;

	    case PD_E_TXQ_SIZE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_TXQ_SIZE" );
		*data = GetQueueSize( &port->TX );  
		break;
	    
	    case PD_E_RXQ_SIZE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_RXQ_SIZE" );
		*data = GetQueueSize( &port->RX );  
		break;

	    case PD_E_TXQ_LOW_WATER:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_TXQ_LOW_WATER" );
		*data = 0; 
		returnValue = kIOReturnBadArgument; 
		break;
	
	    case PD_E_RXQ_LOW_WATER:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_RXQ_LOW_WATER" );
		*data = 0; 
		returnValue = kIOReturnBadArgument; 
		break;
	
	    case PD_E_TXQ_HIGH_WATER:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_TXQ_HIGH_WATER" );
		*data = 0; 
		returnValue = kIOReturnBadArgument; 
		break;
	
	    case PD_E_RXQ_HIGH_WATER:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_RXQ_HIGH_WATER" );
		*data = 0; 
		returnValue = kIOReturnBadArgument; 
		break;
	
	    case PD_E_TXQ_AVAILABLE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_TXQ_AVAILABLE" );
		*data = FreeSpaceinQueue( &port->TX );   
		break;
	    
	    case PD_E_RXQ_AVAILABLE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_RXQ_AVAILABLE" );
		*data = UsedSpaceinQueue( &port->RX );  
		break;

	    case PD_E_DATA_RATE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_DATA_RATE" );
		*data = port->BaudRate << 1;        
		break;
	    
	    case PD_E_RX_DATA_RATE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_RX_DATA_RATE" );
		*data = 0x00;                   
		break;
	    
	    case PD_E_DATA_SIZE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_DATA_SIZE" );
		*data = port->CharLength << 1;  
		break;
	    
	    case PD_E_RX_DATA_SIZE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_RX_DATA_SIZE" );
		*data = 0x00;                   
		break;
	    
	    case PD_E_DATA_INTEGRITY:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_DATA_INTEGRITY" );
		*data = port->TX_Parity;            
		break;
	    
	    case PD_E_RX_DATA_INTEGRITY:
		ELG( 0, event, 'ReqE', "requestEvent - PD_E_RX_DATA_INTEGRITY" );
		*data = port->RX_Parity;            
		break;

	    case PD_RS232_E_STOP_BITS:
		ELG( 0, event, 'ReqE', "requestEvent - PD_RS232_E_STOP_BITS" );
		*data = port->StopBits << 1;        
		break;
	    
	    case PD_RS232_E_RX_STOP_BITS:
		ELG( 0, event, 'ReqE', "requestEvent - PD_RS232_E_RX_STOP_BITS" );
		*data = 0x00;                   
		break;
	    
	    case PD_RS232_E_XON_BYTE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_RS232_E_XON_BYTE" );
		*data = port->XONchar;          
		break;
	    
	    case PD_RS232_E_XOFF_BYTE:
		ELG( 0, event, 'ReqE', "requestEvent - PD_RS232_E_XOFF_BYTE" );
		*data = port->XOFFchar;         
		break;
	    
	    case PD_RS232_E_LINE_BREAK:
		ELG( 0, event, 'ReqE', "requestEvent - PD_RS232_E_LINE_BREAK" );
		*data = bool(readPortState( port ) & PD_RS232_S_BRK);
		break;
	    
	    case PD_RS232_E_MIN_LATENCY:
		ELG( 0, event, 'ReqE', "requestEvent - PD_RS232_E_MIN_LATENCY" );
		*data = bool( port->MinLatency );       
		break;

	    default:
		ELG( 0, event, 'ReqE', "requestEvent - unrecognized event" );
		returnValue = kIOReturnBadArgument;             
		break;
	}
    }

    return kIOReturnSuccess;
    
}/* end requestEvent */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::enqueueEvent
//
//      Inputs:     event - The event, data - any data associated with the event, 
//                                              sleep - true (wait for it), false (don't), refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess, kIOReturnNotOpen
//
//      Desc:       Not used by this driver.    
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::enqueueEvent( UInt32 event, UInt32 data, bool sleep, void *refCon)
{
    PortInfo_t *port = (PortInfo_t *) refCon;
    
    ELG( data, event, 'EnqE', "enqueueEvent" );

    if ( readPortState( port ) & PD_S_ACTIVE )
    {
	return kIOReturnSuccess;
    }

    return kIOReturnNotOpen;
    
}/* end enqueueEvent */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::dequeueEvent
//
//      Inputs:     sleep - true (wait for it), false (don't), refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess, kIOReturnNotOpen
//
//      Desc:       Not used by this driver.        
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::dequeueEvent( UInt32 *event, UInt32 *data, bool sleep, void *refCon )
{
    PortInfo_t *port = (PortInfo_t *) refCon;
    
    ELG( 0, 0, 'DeqE', "dequeueEvent" );

    if ( (event == NULL) || (data == NULL) )
	return kIOReturnBadArgument;

    if ( readPortState( port ) & PD_S_ACTIVE )
    {
	return kIOReturnSuccess;
    }

    return kIOReturnNotOpen;
    
}/* end dequeueEvent */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::enqueueData
//
//      Inputs:     buffer - the data, size - number of bytes, sleep - true (wait for it), false (don't),
//                                                                                      refCon - the Port
//
//      Outputs:    Return Code - kIOReturnSuccess or value returned from watchState, count - bytes transferred,  
//
//      Desc:       enqueueData will attempt to copy data from the specified buffer to
//                  the TX queue as a sequence of VALID_DATA events.  The argument
//                  bufferSize specifies the number of bytes to be sent.  The actual
//                  number of bytes transferred is returned in count.
//                  If sleep is true, then this method will sleep until all bytes can be
//                  transferred.  If sleep is false, then as many bytes as possible
//                  will be copied to the TX queue.
//                  Note that the caller should ALWAYS check the transferCount unless
//                  the return value was kIOReturnBadArgument, indicating one or more
//                  arguments were not valid.  Other possible return values are
//                  kIOReturnSuccess if all requirements were met.      
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::enqueueData( UInt8 *buffer, UInt32 size, UInt32 *count, bool sleep, void *refCon )
{
    PortInfo_t  *port = (PortInfo_t *) refCon;
    UInt32      state = PD_S_TXQ_LOW_WATER;
    IOReturn    rtn = kIOReturnSuccess;

    ELG( 0, sleep, 'eqDt', "enqueData" );

    if ( fTerminate )
	return kIOReturnOffline;

    if ( count == NULL || buffer == NULL )
	return kIOReturnBadArgument;

    *count = 0;

    if ( !(readPortState( port ) & PD_S_ACTIVE) )
	return kIOReturnNotOpen;
    
    ELG( port->State, size, 'eqDt', "enqueData State" );    
    LogData( kUSBOut, size, buffer );

	/* OK, go ahead and try to add something to the buffer  */
    *count = AddtoQueue( &port->TX, buffer, size );
    CheckQueues( port );

	/* Let the tranmitter know that we have something ready to go   */
    SetUpTransmit( );

	/* If we could not queue up all of the data on the first pass and   */
	/* the user wants us to sleep until it's all out then sleep */

    while ( (*count < size) && sleep )
    {
	state = PD_S_TXQ_LOW_WATER;
	rtn = watchState( &state, PD_S_TXQ_LOW_WATER, refCon );
	if ( rtn != kIOReturnSuccess )
	{
	    ELG( 0, rtn, 'EqD-', "enqueueData - interrupted" );
	    return rtn;
	}

	*count += AddtoQueue( &port->TX, buffer + *count, size - *count );
	CheckQueues( port );

	/* Let the tranmitter know that we have something ready to go.  */

	SetUpTransmit( );
    }/* end while */

    ELG( *count, size, 'enqd', "enqueueData - Enqueue" );

    return kIOReturnSuccess;
    
}/* end enqueueData */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::dequeueData
//
//      Inputs:     size - buffer size, min - minimum bytes required, refCon - the Port
//
//      Outputs:    buffer - data returned, min - number of bytes
//                  Return Code - kIOReturnSuccess, kIOReturnBadArgument, kIOReturnNotOpen, or value returned from watchState
//
//      Desc:       dequeueData will attempt to copy data from the RX queue to the
//                  specified buffer.  No more than bufferSize VALID_DATA events
//                  will be transferred. In other words, copying will continue until
//                  either a non-data event is encountered or the transfer buffer
//                  is full.  The actual number of bytes transferred is returned
//                  in count.
//                  The sleep semantics of this method are slightly more complicated
//                  than other methods in this API. Basically, this method will
//                  continue to sleep until either min characters have been
//                  received or a non data event is next in the RX queue.  If
//                  min is zero, then this method never sleeps and will return
//                  immediately if the queue is empty.
//                  Note that the caller should ALWAYS check the transferCount
//                  unless the return value was kIOReturnBadArgument, indicating one or
//                  more arguments were not valid.
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::dequeueData( UInt8 *buffer, UInt32 size, UInt32 *count, UInt32 min, void *refCon )
{
    PortInfo_t  *port = (PortInfo_t *) refCon;
    IOReturn    rtn = kIOReturnSuccess;
    UInt32      state = 0;

    ELG( size, min, 'dqDt', "dequeueData" );
    
	/* Check to make sure we have good arguments.   */
    if ( (count == NULL) || (buffer == NULL) || (min > size) )
	return kIOReturnBadArgument;

	/* If the port is not active then there should not be any chars.    */
    *count = 0;
    if ( !(readPortState( port ) & PD_S_ACTIVE) )
	return kIOReturnNotOpen;

	/* Get any data living in the queue.    */
    *count = RemovefromQueue( &port->RX, buffer, size );
    if (fIrDA)
	fIrDA->ReturnCredit( *count );      // return credit when room in the queue
    
    CheckQueues( port );

    while ( (min > 0) && (*count < min) )
    {
	int count_read;
	
	    /* Figure out how many bytes we have left to queue up */
	state = 0;

	rtn = watchState( &state, PD_S_RXQ_EMPTY, refCon );

	if ( rtn != kIOReturnSuccess )
	{
	    ELG( 0, rtn, 'DqD-', "dequeueData - Interrupted!" );
	    LogData( kUSBIn, *count, buffer );
	    return rtn;
	}
	/* Try and get more data starting from where we left off */
	count_read = RemovefromQueue( &port->RX, buffer + *count, (size - *count) );
	if (fIrDA)
	    fIrDA->ReturnCredit(count_read);        // return credit when room in the queue
	    
	*count += count_read;
	CheckQueues( port );
	
    }/* end while */

    LogData( kUSBIn, *count, buffer );

    ELG( *count, size, 'deqd', "dequeueData -->Out Dequeue" );

    return rtn;
    
}/* end dequeueData */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::SetUpTransmit
//
//      Inputs:
//
//      Outputs:    return code - true (transmit started), false (transmission already in progress)
//
//      Desc:       Setup and then start transmisson on the channel specified
//
/****************************************************************************************************/

bool AppleUSBIrDADriver::SetUpTransmit( void )
{

    size_t      count = 0;
    size_t      data_Length, tCount;
    UInt8       *TempOutBuffer;

    ELG( fPort, fPort->AreTransmitting, 'upTx', "SetUpTransmit" );
    XTRACE(kLogSetupTransmit, 0, fPort->AreTransmitting);
    
	//  If we are already in the cycle of transmitting characters,
	//  then we do not need to do anything.
	
    if ( fPort->AreTransmitting == TRUE )
	return false;

	// First check if we can actually do anything, also if IrDA has no room we're done for now

    //if ( GetQueueStatus( &fPort->TX ) != queueEmpty )
    if (UsedSpaceinQueue(&fPort->TX) > 0)
    {
	data_Length = fIrDA->TXBufferAvailable();
	if ( data_Length == 0 )
	{
	    return false;
	}
    
	if ( data_Length > MAX_BLOCK_SIZE )
	{
	    data_Length = MAX_BLOCK_SIZE;
	}
	
	TempOutBuffer = (UInt8*)IOMalloc( data_Length );
	if ( !TempOutBuffer )
	{
	    ELG( 0, 0, 'STA-', "SetUpTransmit - buffer allocation problem" );
	    return false;
	}
	bzero( TempOutBuffer, data_Length );

	// Fill up the buffer with characters from the queue

	count = RemovefromQueue( &fPort->TX, TempOutBuffer, data_Length );
	ELG( fPort->State, count, ' Tx+', "SetUpTransmit - Sending to IrDA" );
    
	fPort->AreTransmitting = TRUE;
	changeState( fPort, PD_S_TX_BUSY, PD_S_TX_BUSY );

	tCount = fIrDA->Write( TempOutBuffer, count );      // do the "transmit" -- send to IrCOMM

	changeState( fPort, 0, PD_S_TX_BUSY );
	fPort->AreTransmitting = false;

	IOFree( TempOutBuffer, data_Length );
	if ( tCount != count )
	{
	    ELG( tCount, count, 'IrW-', "SetUpTransmit - IrDA write problem, data has been dropped" );
	    return false;
	}

	// We potentially removed a bunch of stuff from the
	// queue, so see if we can free some thread(s)
	// to enqueue more stuff.
	
	CheckQueues( fPort );
    }

    return true;
    
}/* end SetUpTransmit */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::StartTransmission
//
//      Inputs:     control_length - Length of control data
//                  control_buffer - Control data
//                  data_length - Length of raw data
//                  data_buffer - raw data
//
//      Outputs:    Return code - kIOReturnSuccess
//
//      Desc:       Start the transmisson. If both control and data length is zero then only
//                  the change byte will be sent.
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::StartTransmit(UInt32 control_length, UInt8 *control_buffer, UInt32 data_length, UInt8 *data_buffer)
{
    IOReturn    ior;
    UInt8       changeByte;
    
    ELG( 0, fPort, 'StTx', "StartTransmission" );
    check(fWriteActive == false);                   // bail?
    
    // Sending control and data
	
    changeByte = (fBofsCode << 4) | fBaudCode;      // compute new mode byte
    fPipeOutBuffer[0] = (changeByte != fLastChangeByte) ? changeByte : 0;   // tell hardware new mode if changed

    fLastChangeByte = changeByte;                   // save new mode for next time through
    
    // append the control and data buffers after the mode byte
    if ( control_length != 0 )
    {
	bcopy(control_buffer, &fPipeOutBuffer[1], control_length);
	if ( data_length != 0 )
	{
	    bcopy(data_buffer, &fPipeOutBuffer[control_length+1], data_length);
	}
    }
    
    // add up the total length to send off to the device
    fCount = control_length + data_length + 1;
    fpPipeOutMDP->setLength( fCount );
	    
//  LogData( kUSBOut, fCount, fPipeOutBuffer );
    XTRACE(kLogXmitLen, 0, fCount);
    fWriteActive = true;
    //ior = fpOutPipe->Write( fpPipeOutMDP, &fWriteCompletionInfo );
    ior = fpOutPipe->Write( fpPipeOutMDP, 1000, 1000, &fWriteCompletionInfo );  // 1 second timeouts
    
    return ior;
    
}/* end StartTransmission */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::SetStructureDefaults
//
//      Inputs:     port - the port to set the defaults, Init - Probe time or not
//
//      Outputs:    None
//
//      Desc:       Sets the defaults for the specified port structure
//
/****************************************************************************************************/

void AppleUSBIrDADriver::SetStructureDefaults( PortInfo_t *port, bool Init )
{
    UInt32  tmp;
    
    ELG( 0, 0, 'StSD', "SetStructureDefaults" );
    XTRACE(kLogSetStructureDefaults, 0, Init);

	/* These are initialized when the port is created and shouldn't be reinitialized. */
    if ( Init )
    {
	port->FCRimage          = 0x00;
	port->IERmask           = 0x00;

	port->State             = ( PD_S_TXQ_EMPTY | PD_S_TXQ_LOW_WATER | PD_S_RXQ_EMPTY | PD_S_RXQ_LOW_WATER );
	port->WatchStateMask    = 0x00000000;              
 //       port->serialRequestLock = 0;
//      port->readActive        = false;
    }

    port->BaudRate          = kDefaultBaudRate;         // 9600 bps
    port->CharLength        = 8;                        // 8 Data bits
    port->StopBits          = 2;                        // 1 Stop bit
    port->TX_Parity         = 1;                        // No Parity
    port->RX_Parity         = 1;                        // --ditto--
    port->MinLatency        = false;
    port->XONchar           = '\x11';
    port->XOFFchar          = '\x13';
    port->FlowControl       = 0x00000000;
    port->RXOstate          = IDLE_XO;
    port->TXOstate          = IDLE_XO;
    port->FrameTOEntry      = NULL;

//  port->RXStats.BufferSize    = BUFFER_SIZE_DEFAULT;
    port->RXStats.BufferSize    = kMaxCirBufferSize;
//  port->RXStats.HighWater     = port->RXStats.BufferSize - (DATA_BUFF_SIZE*2);
    port->RXStats.HighWater     = (port->RXStats.BufferSize << 1) / 3;
    port->RXStats.LowWater      = port->RXStats.HighWater >> 1;

//  port->TXStats.BufferSize    = BUFFER_SIZE_DEFAULT;
    port->TXStats.BufferSize    = kMaxCirBufferSize;
    port->TXStats.HighWater     = (port->RXStats.BufferSize << 1) / 3;
    port->TXStats.LowWater      = port->RXStats.HighWater >> 1;
    
    port->FlowControl           = (DEFAULT_AUTO | DEFAULT_NOTIFY);
//  port->FlowControl           = DEFAULT_NOTIFY;

    port->AreTransmitting   = FALSE;

    for ( tmp=0; tmp < (256 >> SPECIAL_SHIFT); tmp++ )
	port->SWspecial[ tmp ] = 0;

    return;
    
}/* end SetStructureDefaults */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::freeRingBuffer
//
//      Inputs:     Queue - the specified queue to free
//
//      Outputs:    None
//
//      Desc:       Frees all resources assocated with the queue, then sets all queue parameters 
//                  to safe values.
//
/****************************************************************************************************/

void AppleUSBIrDADriver::freeRingBuffer( CirQueue *Queue )
{
    ELG( 0, Queue, 'f rb', "freeRingBuffer" );
    require(Queue->Start, Bogus);
    
    IOFree( Queue->Start, Queue->Size );
    CloseQueue( Queue );

Bogus:
    return;
    
}/* end freeRingBuffer */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::allocateRingBuffer
//
//      Inputs:     Queue - the specified queue to allocate, BufferSize - size to allocate
//
//      Outputs:    return Code - true (buffer allocated), false (it failed)
//
//      Desc:       Allocates resources needed by the queue, then sets up all queue parameters. 
//
/****************************************************************************************************/

bool AppleUSBIrDADriver::allocateRingBuffer( CirQueue *Queue, size_t BufferSize )
{
    UInt8       *Buffer;

	// Size is ignored and kMaxCirBufferSize, which is 4096, is used.
	
    ELG( 0, BufferSize, 'alrb', "allocateRingBuffer" );
    Buffer = (UInt8*)IOMalloc( kMaxCirBufferSize );

    InitQueue( Queue, Buffer, kMaxCirBufferSize );

    if ( Buffer )
	return true;

    return false;
    
}/* end allocateRingBuffer */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::message
//
//      Inputs:     type - message type, provider - my provider, argument - additional parameters
//
//      Outputs:    return Code - kIOReturnSuccess
//
//      Desc:       Handles IOKit messages. 
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::message( UInt32 type, IOService *provider,  void *argument)
{
    
    ELG( 0, type, 'mess', "message" );
    XTRACE(kLogMessage, type >> 16, (short)type);
    
    switch ( type )
    {
	case kIOMessageServiceIsTerminated:
	    ELG( 0, type, 'mess', "message - kIOMessageServiceIsTerminated" );
	    
#ifdef old	// don't need to do stops, will be closed shortly
	    
	    if (fIrDA)
	    {
		int REVIEW_fTerminate;  // this isn't right yet.
		stopIrDA();         // stop irda now
	    }
	    
	    if ( fSessions )
	    {
		if ( (fPort != NULL) && (fPort->serialRequestLock != 0) )
		{
//                  changeState( fPort, 0, (UInt32)PD_S_ACTIVE );
		}
				
		KUNCUserNotificationDisplayNotice(
		0,      // Timeout in seconds
		0,      // Flags (for later usage)
		"",     // iconPath (not supported yet)
		"",     // soundPath (not supported yet)
		"",     // localizationPath (not supported  yet)
		"USB IrDA Unplug Notice",       // the header
		"The USB IrDA Pod has been unplugged while an Application was still active. This can result in loss of data.",
		"OK");
	    } else {
		if ( fpInterface )  
		{
		    fpInterface->close( this ); 
		    fpInterface->release();
		    fpInterface = NULL; 
		}
	    }
	    
	    fTerminate = true;      // we're being terminated (unplugged)
#endif // old
	    
	    /* We need to disconnect the user client interface */
	    messageClients(kIrDACallBack_Unplug, 0, 1);
	    break;
	    
	case kIOMessageServiceIsSuspended:  
	    ELG( 0, type, 'mess', "message - kIOMessageServiceIsSuspended" );
	    break;
	    
	case kIOMessageServiceIsResumed:    
	    ELG( 0, type, 'mess', "message - kIOMessageServiceIsResumed" );
	    break;
	    
	case kIOMessageServiceIsRequestingClose: 
	    ELG( 0, type, 'mess', "message - kIOMessageServiceIsRequestingClose" ); 
	    break;
	    
	case kIOMessageServiceWasClosed:    
	    ELG( 0, type, 'mess', "message - kIOMessageServiceWasClosed" ); 
	    break;
	    
	case kIOMessageServiceBusyStateChange:  
	    ELG( 0, type, 'mess', "message - kIOMessageServiceBusyStateChange" ); 
	    break;
	    
	case kIOUSBMessagePortHasBeenResumed:   
	    ELG( 0, type, 'mess', "message - kIOUSBMessagePortHasBeenResumed" );
	    DebugLog("message = kIOUSBMessagePortHasBeenResumed" );
	    
	    if ( !fIrDAOn )         // We tried to suspend, but it failed
	    {
		fSuspendFail = true;
		ELG( 0, 0, 'msS-', "message - Suspend device really failed" );
	    }
	    else {                  // we're trying to resume, so start irda
		if ( !startIrDA() )
		{
		    fIrDAOn = false;
		    fTerminate = true;
		    IOLog("AppleUSBIrDADriver: message - startIrDA failed\n" );
		} else {
		    ELG( 0, 0, 'msc+', "message - startIrDA successful" );
		    //IOLog("AppleUSBIrDADriver: message - startIrDA successful\n" );
		}
	    }
	    break;
	
	case kIOUSBMessageHubResumePort:
	    ELG( 0, type, 'mess', "message - kIOUSBMessageHubResumePort" );
	    DebugLog("message = kIOUSBMessageHubResumePort" );
			
	    if ( !fIrDAOn )         // Means the suspend failed
	    {
		fSuspendFail = true;
	    }
	    else {                  // we're being asked to resume
		if ( !startIrDA() )
		{
		    ELG( 0, 0, 'msc-', "message - startIrDA failed" );
		
		    KUNCUserNotificationDisplayNotice(
		    0,      // Timeout in seconds
		    0,      // Flags (for later usage)
		    "",     // iconPath (not supported yet)
		    "",     // soundPath (not supported yet)
		    "",     // localizationPath (not supported  yet)
		    "USB IrDA Problem Notice",      // the header
		    "The USB IrDA Pod has experienced difficulties. To continue either replug the device (if external) or restart the computer",
		    "OK");
		
		    fIrDAOn = false;        // We're basically sol at this point
		    fTerminate = true;  
		} else {
		    ELG( 0, 0, 'msc+', "message - createSerialStream successful" );
		    //IOLog("AppleUSBIrDADriver: message - createSerialStream successful\n" );
		}
	    }

	default:
	    ELG( 0, type, 'mess', "message - unknown message" ); 
	    break;
    }
    
    return kIOReturnSuccess;
}

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::readPortState
//
//      Inputs:     port - the specified port
//
//      Outputs:    returnState - current state of the port
//
//      Desc:       Reads the current Port->State. 
//
/****************************************************************************************************/

UInt32 AppleUSBIrDADriver::readPortState( PortInfo_t *port )
{
    UInt32              returnState;
    
//  ELG( 0, port, 'rPSt', "readPortState" );

    IOLockLock( port->serialRequestLock );

    returnState = port->State;

    IOLockUnlock( port->serialRequestLock);

//  ELG( returnState, 0, 'rPS-', "readPortState" );

    return returnState;
    
}/* end readPortState */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::changeState
//
//      Inputs:     port - the specified port, state - new state, mask - state mask (the specific bits)
//
//      Outputs:    None
//
//      Desc:       Change the current Port->State to state using the mask bits.
//                  if mask = 0 nothing is changed.
//                  delta contains the difference between the new and old state taking the
//                  mask into account and it's used to wake any waiting threads as appropriate. 
//
/****************************************************************************************************/

void AppleUSBIrDADriver::changeState( PortInfo_t *port, UInt32 state, UInt32 mask )
{
    UInt32              delta;
    
//  ELG( state, mask, 'chSt', "changeState" );

    IOLockLock( port->serialRequestLock );
    state = (port->State & ~mask) | (state & mask); // compute the new state
    delta = state ^ port->State;                    // keep a copy of the diffs
    port->State = state;

	// Wake up all threads asleep on WatchStateMask
	
    if ( delta & port->WatchStateMask )
    {
	thread_wakeup_with_result( &port->WatchStateMask, THREAD_RESTART );
    }

    IOLockUnlock( port->serialRequestLock );

    ELG( port->State, delta, 'chSt', "changeState - exit" );
    return;
    
}/* end changeState */

/****************************************************************************************************/
//
//      Method:     AppleUSBIrDADriver::privateWatchState
//
//      Inputs:     port - the specified port, state - state watching for, mask - state mask (the specific bits)
//
//      Outputs:    IOReturn - kIOReturnSuccess, kIOReturnIOError or kIOReturnIPCError
//
//      Desc:       Wait for the at least one of the state bits defined in mask to be equal
//                  to the value defined in state. Check on entry then sleep until necessary.
//                  A return value of kIOReturnSuccess means that at least one of the port state
//                  bits specified by mask is equal to the value passed in by state.  A return
//                  value of kIOReturnIOError indicates that the port went inactive.  A return
//                  value of kIOReturnIPCError indicates sleep was interrupted by a signal. 
//
/****************************************************************************************************/

IOReturn AppleUSBIrDADriver::privateWatchState( PortInfo_t *port, UInt32 *state, UInt32 mask )
{
    unsigned            watchState, foundStates;
    bool                autoActiveBit   = false;
    IOReturn            rtn             = kIOReturnSuccess;

//    ELG( mask, *state, 'wsta', "privateWatchState" );

    watchState              = *state;
    IOLockLock( port->serialRequestLock );

	// hack to get around problem with carrier detection
	
    if ( *state | 0x40 )    /// mlj ??? PD_S_RXQ_FULL?
    {
	port->State |= 0x40;
    }

    if ( !(mask & (PD_S_ACQUIRED | PD_S_ACTIVE)) )
    {
	watchState &= ~PD_S_ACTIVE; // Check for low PD_S_ACTIVE
	mask       |=  PD_S_ACTIVE; // Register interest in PD_S_ACTIVE bit
	autoActiveBit = true;
    }

    for (;;)
    {
	    // Check port state for any interesting bits with watchState value
	    // NB. the '^ ~' is a XNOR and tests for equality of bits.
	    
	foundStates = (watchState ^ ~port->State) & mask;

	if ( foundStates )
	{
	    *state = port->State;
	    if ( autoActiveBit && (foundStates & PD_S_ACTIVE) )
	    {
		rtn = kIOReturnIOError;
	    } else {
		rtn = kIOReturnSuccess;
	    }
//          ELG( rtn, foundStates, 'FndS', "privateWatchState - foundStates" );
	    break;
	}

	    // Everytime we go around the loop we have to reset the watch mask.
	    // This means any event that could affect the WatchStateMask must
	    // wakeup all watch state threads.  The two events are an interrupt
	    // or one of the bits in the WatchStateMask changing.
	    
	port->WatchStateMask |= mask;

	    // note: Interrupts need to be locked out completely here,
	    // since as assertwait is called other threads waiting on
	    // &port->WatchStateMask will be woken up and spun through the loop.
	    // If an interrupt occurs at this point then the current thread
	    // will end up waiting with a different port state than assumed
	    //  -- this problem was causing dequeueData to wait for a change in
	    // PD_E_RXQ_EMPTY to 0 after an interrupt had already changed it to 0.

	assert_wait( &port->WatchStateMask, true ); /* assert event */

	IOLockUnlock( port->serialRequestLock );
	rtn = thread_block( 0 );         /* block ourselves */
	IOLockLock( port->serialRequestLock );

	if ( rtn == THREAD_RESTART )
	{
	    continue;
	} else {
	    rtn = kIOReturnIPCError;
	    break;
	}
    }/* end for */

	    // As it is impossible to undo the masking used by this
	    // thread, we clear down the watch state mask and wakeup
	    // every sleeping thread to reinitialize the mask before exiting.
	
    port->WatchStateMask = 0;

    thread_wakeup_with_result( &port->WatchStateMask, THREAD_RESTART );
    IOLockUnlock( port->serialRequestLock);
    
    //    ELG( rtn, *state, 'wEnd', "privateWatchState end" );
    
    return rtn;
    
}/* end privateWatchState */

#pragma mark -- hardware workaround

/****************************************************************************************************/
//
// Workaround: (re)send the device configuration to put the KC hardware back into a known state
//
/****************************************************************************************************/

void AppleUSBIrDADriver::Workaround(void)
{
    IOReturn        rc;
    IOUSBDevRequest *request;
    
    XTRACE(kLogWorkAround, 0, 0);
    //DebugLog("AppleUSBIrDA: workaround called");
	
    request = (IOUSBDevRequest*)IOMalloc( sizeof(IOUSBDevRequest) );
    require(request, Failed);
    bzero( request, sizeof(IOUSBDevRequest) );
    // allocate pData here if needed
	    
    //request->bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBInterface);
    
    request->bmRequestType = USBmakebmRequestType(kUSBOut, kUSBStandard, kUSBDevice);   // long macro for zero
    request->bRequest = kUSBRqSetConfig;        // 9 - set configuration
    request->wValue = 1;                        // new configuration #1
    request->wIndex = 0;
    request->wLength = 0;
    
    check(request->bmRequestType == 0);
    check(request->bRequest == 9);      // sanity eludes
    
    fRequestCompletionInfo.target = this;
    fRequestCompletionInfo.action = workAroundComplete;
    fRequestCompletionInfo.parameter = request;
    
    rc = fpDevice->DeviceRequest(request, &fRequestCompletionInfo);
    check(rc == kIOReturnSuccess);
    
Failed:
    return;
}/* end Workaround */


/****************************************************************************************************/
//
//      Function:   AppleUSBCDCDriver::workAroundComplete
//
//      Inputs:     obj - me, param - request block 
//                  rc - return code, remaining - what's left
//
//      Outputs:    None
//
/****************************************************************************************************/

void AppleUSBIrDADriver::workAroundComplete(void *obj, void *param, IOReturn rc, UInt32 remaining )
{
    //AppleUSBIrDADriver    *me = (AppleUSBIrDADriver*)obj;
    IOUSBDevRequest     *request = (IOUSBDevRequest*)param;
    UInt16              dataLen;

    XTRACE(kLogWorkAroundComplete, 0, 0);
    require(request, Fail);

    dataLen = request->wLength;
    if ((dataLen != 0) && (request->pData)) {   // doesn't happen here
	IOFree(request->pData, dataLen);
    }
    IOFree(request, sizeof(IOUSBDevRequest));
		
Fail:
    return;
}   /* end workAroundComplete */


#pragma mark -- Glue
/****************************************************************************************************/
// Glue to call the actual USB driver over the IOSerialStreamSync hurdle
//
// todo: see if dynamic cast is slow, if so just check for AppleUSBIrDADriver
// once at attach time and maybe save a copy of it.
/****************************************************************************************************/
#undef super
#define super AppleIrDASerial
    OSDefineMetaClassAndStructors( AppleUSBIrDA, AppleIrDASerial );

bool
AppleUSBIrDA::attach(AppleUSBIrDADriver *provider)
{
    // any reason not to do the type check for AppleUSBIrDADriver at compile time?
    return super::attach(provider);
}

void
AppleUSBIrDA::Add_RXBytes( UInt8 *Buffer, size_t Size )
{
    AppleUSBIrDADriver *driver;
    driver = OSDynamicCast(AppleUSBIrDADriver, fProvider);
    if (driver)
	return driver->Add_RXBytes(Buffer, Size);
}

SInt16
AppleUSBIrDA::SetBofCount(SInt16 bof_count)
{
    AppleUSBIrDADriver *driver;
    driver = OSDynamicCast(AppleUSBIrDADriver, fProvider);
    if (driver)
	return driver->SetBofCount(bof_count);
    else
	return -1;
}

UInt16
AppleUSBIrDA::SetSpeed(UInt32 brate)
{
    AppleUSBIrDADriver *driver;
    driver = OSDynamicCast(AppleUSBIrDADriver, fProvider);
    if (driver)
	return driver->SetSpeed(brate);
    else
	return 0;
}

bool
AppleUSBIrDA::SetUpTransmit( void )
{
    AppleUSBIrDADriver *driver;
    driver = OSDynamicCast(AppleUSBIrDADriver, fProvider);
    if (driver)
	return driver->SetUpTransmit();
    else
	return false;
}

IOReturn
AppleUSBIrDA::StartTransmit( UInt32 control_length, UInt8 *control_buffer, UInt32 data_length, UInt8 *data_buffer )
{
    AppleUSBIrDADriver *driver;
    driver = OSDynamicCast(AppleUSBIrDADriver, fProvider);
    if (driver)
	return driver->StartTransmit(control_length, control_buffer, data_length, data_buffer);
    else
	return -1;
}

USBIrDAQoS *
AppleUSBIrDA::GetIrDAQoS( void )
{
    AppleUSBIrDADriver *driver;
    driver = OSDynamicCast(AppleUSBIrDADriver, fProvider);
    if (driver)
	return driver->GetIrDAQoS();
    else
	return NULL;
}

IrDAComm *
AppleUSBIrDA::GetIrDAComm( void )
{
    AppleUSBIrDADriver *driver;
    driver = OSDynamicCast(AppleUSBIrDADriver, fProvider);
    if (driver)
	return driver->GetIrDAComm();
    else
	return NULL;
}

void
AppleUSBIrDA::GetIrDAStatus( IrDAStatus *status )
{
    AppleUSBIrDADriver *driver;
    driver = OSDynamicCast(AppleUSBIrDADriver, fProvider);
    if (driver)
	return driver->GetIrDAStatus(status);
}

IOReturn
AppleUSBIrDA::SetIrDAUserClientState( bool IrDAOn )
{
    AppleUSBIrDADriver *driver;
    driver = OSDynamicCast(AppleUSBIrDADriver, fProvider);
    if (driver)
	return driver->SetIrDAUserClientState(IrDAOn);
    else
	return -1;
}