/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ */ /* AppleUSBCDCDriver.cpp - MacOSX implementation of */ /* USB Communication Device Class (CDC) Driver. */ #include /* UINT_MAX */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "AppleUSBCDCDriver.h" #define MIN_BAUD (50 << 1) // Globals #if USE_ELG com_apple_iokit_XTrace *gXTrace = 0; UInt32 gTraceID; #endif #define super IOSerialDriverSync OSDefineMetaClassAndStructors(AppleUSBCDCDriver, 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 #define DEBUG_NAME "AppleUSBCDCDriver" /****************************************************************************************************/ // // Function: findKernelLogger // // Inputs: // // Outputs: // // Desc: Just like the name says // /****************************************************************************************************/ IOReturn findKernelLogger() { OSIterator *iterator = NULL; OSDictionary *matchingDictionary = NULL; IOReturn error = 0; // Get matching dictionary matchingDictionary = IOService::serviceMatching("com_apple_iokit_XTrace"); if (!matchingDictionary) { error = kIOReturnError; IOLog(DEBUG_NAME "[FindKernelLogger] Couldn't create a matching dictionary.\n"); goto exit; } // Get an iterator iterator = IOService::getMatchingServices(matchingDictionary); if (!iterator) { error = kIOReturnError; IOLog(DEBUG_NAME "[FindKernelLogger] No XTrace logger found.\n"); goto exit; } // User iterator to find each com_apple_iokit_XTrace instance. There should be only one, so we // won't iterate gXTrace = (com_apple_iokit_XTrace*)iterator->getNextObject(); if (gXTrace) { IOLog(DEBUG_NAME "[FindKernelLogger] Found XTrace logger at %p.\n", gXTrace); } exit: if (error != kIOReturnSuccess) { gXTrace = NULL; IOLog(DEBUG_NAME "[FindKernelLogger] Could not find a logger instance. Error = %X.\n", error); } if (matchingDictionary) matchingDictionary->release(); if (iterator) iterator->release(); return error; }/* end findKernelLogger */ #endif #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: USBLogData // // Inputs: Dir - direction // Count - number of bytes // buf - the data // // Outputs: // // Desc: Puts the data in the log. // /****************************************************************************************************/ void USBLogData(UInt8 Dir, UInt32 Count, char *buf, PortInfo_t *port) { SInt32 wlen; #if USE_ELG UInt8 *b; UInt8 w[8]; #else UInt32 llen, rlen; UInt16 i, Aspnt, Hxpnt; UInt8 wchr; char LocBuf[buflen+1]; #endif switch (Dir) { case kDataIn: #if USE_ELG XTRACE2(port, buf, Count, "USBLogData - Read Complete, address, size"); #else IOLog( "AppleUSBCDCDriver: USBLogData - Read Complete, address = %8x, size = %8d\n", (UInt)buf, (UInt)Count ); #endif break; case kDataOut: #if USE_ELG XTRACE2(port, buf, Count, "USBLogData - Write, address, size"); #else IOLog( "AppleUSBCDCDriver: USBLogData - Write, address = %8x, size = %8d\n", (UInt)buf, (UInt)Count ); #endif break; case kDataOther: #if USE_ELG XTRACE2(port, buf, Count, "USBLogData - Other, address, size"); #else IOLog( "AppleUSBCDCDriver: USBLogData - Other, address = %8x, size = %8d\n", (UInt)buf, (UInt)Count ); #endif break; } #if DUMPALL wlen = Count; #else if (Count > dumplen) { wlen = dumplen; } else { wlen = Count; } #endif if (wlen == 0) { #if USE_ELG XTRACE2(port, 0, Count, "USBLogData - No data, Count=0"); #else IOLog( "AppleUSBCDCDriver: USBLogData - No data, Count=0\n" ); #endif return; } #if (USE_ELG) b = (UInt8 *)buf; while (wlen > 0) // loop over the buffer { bzero(w, sizeof(w)); // zero it bcopy(b, w, min(wlen, 8)); // copy bytes over switch (Dir) { case kDataIn: XTRACE2(port, (w[0] << 24 | w[1] << 16 | w[2] << 8 | w[3]), (w[4] << 24 | w[5] << 16 | w[6] << 8 | w[7]), "USBLogData - Rx buffer dump"); break; case kDataOut: XTRACE2(port, (w[0] << 24 | w[1] << 16 | w[2] << 8 | w[3]), (w[4] << 24 | w[5] << 16 | w[6] << 8 | w[7]), "USBLogData - Tx buffer dump"); break; case kDataOther: XTRACE2(port, (w[0] << 24 | w[1] << 16 | w[2] << 8 | w[3]), (w[4] << 24 | w[5] << 16 | w[6] << 8 | w[7]), "USBLogData - Misc buffer dump"); break; } wlen -= 8; // adjust by 8 bytes for next time (if have more) b += 8; } #else rlen = 0; do { for (i=0; i<=buflen; i++) { LocBuf[i] = 0x20; } LocBuf[i] = 0x00; if (wlen > dumplen) { llen = dumplen; wlen -= dumplen; } else { llen = wlen; wlen = 0; } Aspnt = Asciistart; Hxpnt = 0; for (i=1; i<=llen; 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[(llen + Asciistart) + 1] = 0x00; IOLog(LocBuf); IOLog("\n"); IOSleep(Sleep_Time); // Try and keep the log from overflowing rlen += llen; buf = &buf[rlen]; } while (wlen != 0); #endif }/* end USBLogData */ #endif /****************************************************************************************************/ // // Method: AddBytetoQueue // // Inputs: Queue - the queue to be added to // Value - Byte to be added // // Outputs: Queue status - full or no error // // Desc: Add a byte to the circular queue. // 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 InQueue for zero. // /****************************************************************************************************/ QueueStatus AppleUSBCDCDriver::AddBytetoQueue(CirQueue *Queue, char Value) { if ((Queue->NextChar == Queue->LastChar) && Queue->InQueue) { 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; return queueNoError; }/* end AddBytetoQueue */ /****************************************************************************************************/ // // Method: GetBytetoQueue // // Inputs: Queue - the queue to be removed from // // Outputs: Value - where to put the byte // QueueStatus - empty or no error // // Desc: Remove a byte from the circular queue. // /****************************************************************************************************/ QueueStatus AppleUSBCDCDriver::GetBytetoQueue(CirQueue *Queue, UInt8 *Value) { if ((Queue->NextChar == Queue->LastChar) && !Queue->InQueue) { 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; return queueNoError; }/* end GetBytetoQueue */ /****************************************************************************************************/ // // Method: InitQueue // // Inputs: Queue - the queue to be initialized // Buffer - the buffer // size - length of buffer // // Outputs: QueueStatus - queueNoError. // // Desc: Pass a buffer of memory and this routine will set up the internal data structures. // /****************************************************************************************************/ QueueStatus AppleUSBCDCDriver::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: CloseQueue // // Inputs: Queue - the queue to be closed // // Outputs: QueueStatus - queueNoError. // // Desc: Clear out all of the data structures. // /****************************************************************************************************/ QueueStatus AppleUSBCDCDriver::CloseQueue(CirQueue *Queue) { Queue->Start = 0; Queue->End = 0; Queue->NextChar = 0; Queue->LastChar = 0; Queue->Size = 0; return queueNoError; }/* end CloseQueue */ /****************************************************************************************************/ // // Method: 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 AppleUSBCDCDriver::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: 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 AppleUSBCDCDriver::RemovefromQueue(CirQueue *Queue, UInt8 *Buffer, size_t MaxSize) { size_t BytesReceived = 0; UInt8 Value; // while((GetBytetoQueue(Queue, &Value) == queueNoError) && (MaxSize >= BytesReceived)) while((MaxSize > BytesReceived) && (GetBytetoQueue(Queue, &Value) == queueNoError)) { *Buffer++ = Value; BytesReceived++; }/* end while */ return BytesReceived; }/* end RemovefromQueue */ /****************************************************************************************************/ // // Method: 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 AppleUSBCDCDriver::FreeSpaceinQueue(CirQueue *Queue) { size_t retVal = 0; retVal = Queue->Size - Queue->InQueue; return retVal; }/* end FreeSpaceinQueue */ /****************************************************************************************************/ // // Method: 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 AppleUSBCDCDriver::UsedSpaceinQueue(CirQueue *Queue) { return Queue->InQueue; }/* end UsedSpaceinQueue */ /****************************************************************************************************/ // // Method: 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 AppleUSBCDCDriver::GetQueueSize(CirQueue *Queue) { return Queue->Size; }/* end GetQueueSize */ /****************************************************************************************************/ // // Method: 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 AppleUSBCDCDriver::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: CheckQueues // // Inputs: port - the port to check // // Outputs: None // // Desc: Checks the various queue's etc and manipulates the state(s) accordingly // Must be called from a gated method or completion routine. // /****************************************************************************************************/ void AppleUSBCDCDriver::CheckQueues(PortInfo_t *port) { unsigned long Used; unsigned long Free; unsigned long QueuingState; unsigned long DeltaState; // Initialise the QueueState with the current state. QueuingState = port->State; // Check to see if there is anything in the Transmit buffer. Used = UsedSpaceinQueue(&port->TX); Free = FreeSpaceinQueue(&port->TX); XTRACE(port, Free, Used, "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 ^ port->State; setStateGated(QueuingState, DeltaState, port); return; }/* end CheckQueues */ // Encode the 4 modem status bits (so we only make one call to changeState below) static UInt32 sMapModemStates[16] = { 0 | 0 | 0 | 0, // 0000 0 | 0 | 0 | PD_RS232_S_DCD, // 0001 0 | 0 | PD_RS232_S_DSR | 0, // 0010 0 | 0 | PD_RS232_S_DSR | PD_RS232_S_DCD, // 0011 0 | PD_RS232_S_BRK | 0 | 0, // 0100 0 | PD_RS232_S_BRK | 0 | PD_RS232_S_DCD, // 0101 0 | PD_RS232_S_BRK | PD_RS232_S_DSR | 0, // 0110 0 | PD_RS232_S_BRK | PD_RS232_S_DSR | PD_RS232_S_DCD, // 0111 PD_RS232_S_RNG | 0 | 0 | 0, // 1000 PD_RS232_S_RNG | 0 | 0 | PD_RS232_S_DCD, // 1001 PD_RS232_S_RNG | 0 | PD_RS232_S_DSR | 0, // 1010 PD_RS232_S_RNG | 0 | PD_RS232_S_DSR | PD_RS232_S_DCD, // 1011 PD_RS232_S_RNG | PD_RS232_S_BRK | 0 | 0, // 1100 PD_RS232_S_RNG | PD_RS232_S_BRK | 0 | PD_RS232_S_DCD, // 1101 PD_RS232_S_RNG | PD_RS232_S_BRK | PD_RS232_S_DSR | 0, // 1110 PD_RS232_S_RNG | PD_RS232_S_BRK | PD_RS232_S_DSR | PD_RS232_S_DCD, // 1111 }; /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::commReadComplete // // Inputs: obj - me // param - the Port // rc - return code // remaining - what's left // // Outputs: None // // Desc: Interrupt pipe (Comm interface) read completion routine // /****************************************************************************************************/ void AppleUSBCDCDriver::commReadComplete(void *obj, void *param, IOReturn rc, UInt32 remaining) { AppleUSBCDCDriver *me = (AppleUSBCDCDriver*)obj; PortInfo_t *port = (PortInfo_t*)param; IOReturn ior; UInt32 dLen; UInt16 *tState; UInt32 tempS, value, mask; XTRACE(port, rc, 0, "commReadComplete"); if (me->fStopping) return; if (rc == kIOReturnSuccess) // If operation returned ok { dLen = COMM_BUFF_SIZE - remaining; XTRACE(port, 0, dLen, "commReadComplete - data length"); // Now look at the state stuff // LogData(kDataOther, dLen, port->CommPipeBuffer, port); if ((dLen > 7) && (port->CommPipeBuffer[1] == kUSBSERIAL_STATE)) { tState = (UInt16 *)&port->CommPipeBuffer[8]; tempS = USBToHostWord(*tState); XTRACE(port, 0, tempS, "commReadComplete - kUSBSERIAL_STATE"); mask = sMapModemStates[15]; // All 4 on value = sMapModemStates[tempS & 15]; // now the status bits me->setStateGated(value, mask, port); } } else { XTRACE(port, 0, rc, "commReadComplete - error"); if (rc != kIOReturnAborted) { rc = me->clearPipeStall(port, port->CommPipe); if (rc != kIOReturnSuccess) { XTRACE(port, 0, rc, "dataReadComplete - clear stall failed (trying to continue)"); } } } // Queue the next read only if not aborted if (rc != kIOReturnAborted) { ior = port->CommPipe->Read(port->CommPipeMDP, &port->fCommCompletionInfo, NULL); if (ior != kIOReturnSuccess) { XTRACE(port, 0, rc, "commReadComplete - Read io error"); } } return; }/* end commReadComplete */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::dataReadComplete // // Inputs: obj - me // param - the Port // rc - return code // remaining - what's left // // Outputs: None // // Desc: BulkIn pipe (Data interface) read completion routine // /****************************************************************************************************/ void AppleUSBCDCDriver::dataReadComplete(void *obj, void *param, IOReturn rc, UInt32 remaining) { AppleUSBCDCDriver *me = (AppleUSBCDCDriver*)obj; PortInfo_t *port = (PortInfo_t*)param; IOReturn ior; size_t length; XTRACE(port, rc, 0, "dataReadComplete"); if (me->fStopping) return; if (rc == kIOReturnSuccess) // If operation returned ok { length = DATA_BUFF_SIZE - remaining; XTRACE(port, port->State, length, "dataReadComplete - data length"); LogData(kDataIn, length, port->PipeInBuffer, port); // Move the incoming bytes to the ring buffer me->AddtoQueue(&port->RX, port->PipeInBuffer, length); me->CheckQueues(port); } else { XTRACE(port, 0, rc, "dataReadComplete - error"); if (rc != kIOReturnAborted) { rc = me->clearPipeStall(port, port->InPipe); if (rc != kIOReturnSuccess) { XTRACE(port, 0, rc, "dataReadComplete - clear stall failed (trying to continue)"); } } } // Queue the next read only if not aborted if (rc != kIOReturnAborted) { ior = port->InPipe->Read(port->PipeInMDP, &port->fReadCompletionInfo, NULL); if (ior != kIOReturnSuccess) { XTRACE(port, 0, rc, "dataReadComplete - Read io err"); } } return; }/* end dataReadComplete */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::dataWriteComplete // // Inputs: obj - me // param - the Port // rc - return code // remaining - what's left // // Outputs: None // // Desc: BulkOut pipe (Data interface) write completion routine // /****************************************************************************************************/ void AppleUSBCDCDriver::dataWriteComplete(void *obj, void *param, IOReturn rc, UInt32 remaining) { AppleUSBCDCDriver *me = (AppleUSBCDCDriver *)obj; PortInfo_t *port = (PortInfo_t *)param; XTRACE(port, rc, 0, "dataWriteComplete"); if (me->fStopping) return; if (rc == kIOReturnSuccess) // If operation returned ok { XTRACE(port, 0, (port->Count - remaining), "dataWriteComplete - data length"); if (port->Count > 0) // Check if it was a zero length write { if ((port->Count % port->OutPacketSize) == 0) // If it was a multiple of max packet size then we need to do a zero length write { XTRACE(port, rc, (port->Count - remaining), "dataWriteComplete - writing zero length packet"); port->PipeOutMDP->setLength(0); port->Count = 0; port->OutPipe->Write(port->PipeOutMDP, &port->fWriteCompletionInfo); return; } } me->CheckQueues(port); port->AreTransmitting = false; me->setStateGated(0, PD_S_TX_BUSY, port); me->setUpTransmit(port); // just to keep it going?? // if (!port->AreTransmitting) // { // me->setStateGated(0, PD_S_TX_BUSY, port); // } } else { XTRACE(port, 0, rc, "dataWriteComplete - io error"); if (rc != kIOReturnAborted) { rc = me->clearPipeStall(port, port->OutPipe); if (rc != kIOReturnSuccess) { XTRACE(port, 0, rc, "dataWriteComplete - clear stall failed (trying to continue)"); } } port->AreTransmitting = false; me->setStateGated(0, PD_S_TX_BUSY, port); } return; }/* end dataWriteComplete */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::merWriteComplete // // Inputs: obj - me // param - port // rc - return code // remaining - what's left // // Outputs: None // // Desc: Management Element Request write completion routine // /****************************************************************************************************/ void AppleUSBCDCDriver::merWriteComplete(void *obj, void *param, IOReturn rc, UInt32 remaining) { AppleUSBCDCDriver *me = (AppleUSBCDCDriver *)obj; IOUSBDevRequest *MER = (IOUSBDevRequest *)param; UInt16 dataLen; XTRACE(me, 0, remaining, "merWriteComplete"); if (me->fStopping) { if (MER) { dataLen = MER->wLength; if ((dataLen != 0) && (MER->pData)) { IOFree(MER->pData, dataLen); } IOFree(MER, sizeof(IOUSBDevRequest)); } return; } if (MER) { if (rc == kIOReturnSuccess) { XTRACE(me, MER->bRequest, remaining, "merWriteComplete - request"); } else { XTRACE(me, MER->bRequest, rc, "merWriteComplete - io err"); } dataLen = MER->wLength; XTRACE(me, 0, dataLen, "merWriteComplete - data length"); if ((dataLen != 0) && (MER->pData)) { IOFree(MER->pData, dataLen); } IOFree(MER, sizeof(IOUSBDevRequest)); } else { if (rc == kIOReturnSuccess) { XTRACE(me, 0, remaining, "merWriteComplete (request unknown)"); } else { XTRACE(me, 0, rc, "merWriteComplete (request unknown) - io err"); } } return; }/* end merWriteComplete */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::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 AppleUSBCDCDriver::start(IOService *provider) { UInt8 configs; // number of device configurations UInt8 i; for (i=0; iretain(); // don't let it unload ... XTRACE(this, 0, 0xbeefbeef, "Hello from start"); logInfo = gXTrace->LogGetInfo(); IOLog("AppleUSBCDCDriver: start - Log is at %x\n", (unsigned int)logInfo); } else { return false; } #endif XTRACE(this, 0, provider, "start - provider."); if(!super::start(provider)) { ALERT(0, 0, "start - super failed"); return false; } // Get my USB device provider - the device fpDevice = OSDynamicCast(IOUSBDevice, provider); if(!fpDevice) { ALERT(0, 0, "start - provider invalid"); stop(provider); return false; } // Let's see if we have any configurations to play with configs = fpDevice->GetNumConfigurations(); if (configs < 1) { ALERT(0, 0, "start - no configurations"); stop(provider); return false; } // Now take control of the device and configure it if (!fpDevice->open(this)) { ALERT(0, 0, "start - unable to open device"); stop(provider); return false; } // get workloop fWorkLoop = getWorkLoop(); if (!fWorkLoop) { ALERT(0, 0, "start - getWorkLoop failed"); fpDevice->close(this); stop(provider); return false; } fWorkLoop->retain(); if (!configureDevice(configs)) { ALERT(0, 0, "start - configureDevice failed"); cleanUp(); fpDevice->close(this); stop(provider); return false; } XTRACE(this, 0, 0, "start - successful and IOModemSerialStreamSync created"); return true; }/* end start */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::stop // // Inputs: provider - my provider // // Outputs: None // // Desc: Stops the driver // /****************************************************************************************************/ void AppleUSBCDCDriver::stop(IOService *provider) { IOReturn ret; UInt16 i; parmList parms; XTRACE(this, 0, 0, "stop"); fStopping = true; for (i=0; icommandGate->runAction(stopAction, (void *)&parms); release(); IOFree(fPorts[i], sizeof(PortInfo_t)); fPorts[i] = NULL; } } removeProperty((const char *)propertyTag); if (fpDevice) { fpDevice->close(this); fpDevice = NULL; } super::stop(provider); return; }/* end stop */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::stopAction // // Desc: Dummy pass through for stopGated. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::stopAction(OSObject *owner, void *arg0, void *, void *, void *) { parmList *parms = (parmList *)arg0; ((AppleUSBCDCDriver *)owner)->stopGated(parms->port); return kIOReturnSuccess; }/* end stopAction */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::stopGated // // Inputs: provider - my provider // // Outputs: None // // Desc: Releases the interfaces, memory and general housekeeping // /****************************************************************************************************/ void AppleUSBCDCDriver::stopGated(PortInfo_t *port) { XTRACE(port, 0, 0, "stopGated"); releaseResources(port); return; }/* end stopGated */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::cleanUp // // Inputs: None // // Outputs: None // // Desc: Clean up the ports. // /****************************************************************************************************/ void AppleUSBCDCDriver::cleanUp(void) { UInt8 i; XTRACE(this, 0, 0, "cleanUp"); for (i=0; igetProperty("kDoNotSuspend")); if (boolObj && boolObj->isTrue()) { fSuspendOK = false; XTRACE(this, 0, 0, "configureDevice - Suspended has been canceled for this device"); } req.bInterfaceClass = kUSBCommClass; req.bInterfaceSubClass = kUSBAbstractControlModel; req.bInterfaceProtocol = kUSBv25; req.bAlternateSetting = kIOUSBFindInterfaceDontCare; Comm = fpDevice->FindNextInterface(NULL, &req); if (!Comm) { XTRACE(this, 0, 0, "configureDevice - Finding the first CDC interface failed"); } while (Comm) { port = NULL; for (i=0; iCommInterface = Comm; port->CommInterface->retain(); port->CommInterfaceNumber = Comm->GetInterfaceNumber(); XTRACE(port, 0, port->CommInterfaceNumber, "configureDevice - Comm interface number."); portok = getFunctionalDescriptors(port); if (portok) { req.bInterfaceClass = kUSBDataClass; req.bInterfaceSubClass = kIOUSBFindInterfaceDontCare; req.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; req.bAlternateSetting = kIOUSBFindInterfaceDontCare; do { Dataintf = fpDevice->FindNextInterface(Dataintf, &req); if (Dataintf) { dataindx = Dataintf->GetInterfaceNumber(); XTRACE(port, dataindx, port->DataInterfaceNumber, "configureDevice - finding Data interface"); if (dataindx == port->DataInterfaceNumber) { port->DataInterface = Dataintf; // We've found our man XTRACE(port, 0, 0, "configureDevice - found matching Data interface"); break; } } } while (Dataintf); if (!port->DataInterface) { port->DataInterface = fpDevice->FindNextInterface(NULL, &req); // Go with the first one (is this a good idea??) if (!port->DataInterface) { XTRACE(port, 0, 0, "configureDevice - Find next interface for the Data Class failed"); portok = false; } else { XTRACE(port, 0, 0, "configureDevice - going with the first (only?) Data interface"); } } if (port->DataInterface) { port->DataInterface->retain(); // Found both so now set the name for this port if (createSerialStream(port)) // Publish SerialStream services { portok = initPort(port); // Initilaize the Port structure if (portok) { goodCDC = true; } else { XTRACE(port, 0, 0, "configureDevice - initPort failed"); } } else { XTRACE(port, 0, 0, "configureDevice - createSerialStream failed"); portok = false; } } } } if (!portok) { if (port) { releaseResources(port); IOFree(port, sizeof(PortInfo_t)); fPorts[i] = NULL; } } // see if there's another CDC interface req.bInterfaceClass = kUSBCommClass; req.bInterfaceSubClass = kUSBAbstractControlModel; req.bInterfaceProtocol = kUSBv25; req.bAlternateSetting = kIOUSBFindInterfaceDontCare; Comm = fpDevice->FindNextInterface(Comm, &req); if (!Comm) { XTRACE(port, 0, 0, "configureDevice - No more CDC interfaces"); } portok = false; } if (fSuspendOK) { ior = fpDevice->SuspendDevice(true); // Suspend the device (if supported, bus powered ONLY and not canceled) if (ior) { XTRACE(port, 0, ior, "AppleUSBCDCDriver::configureDevice - SuspendDevice error"); } } return goodCDC; }/* end configureDevice */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::initDevice // // Inputs: numConfigs - number of configurations present // // Outputs: return Code - true (CDC present), false (CDC not present) // // Desc: Determines if this is a CDC compliant device and then sets the configuration // /****************************************************************************************************/ bool AppleUSBCDCDriver::initDevice(UInt8 numConfigs) { IOUSBFindInterfaceRequest req; IOUSBDevRequest devreq; const IOUSBConfigurationDescriptor *cd = NULL; // configuration descriptor IOUSBInterfaceDescriptor *intf = NULL; // interface descriptor IOReturn ior = kIOReturnSuccess; UInt8 cval; UInt8 config = 0; bool goodconfig = false; XTRACE(this, 0, numConfigs, "initDevice"); // Make sure we have a CDC interface to play with for (cval=0; cvalGetFullConfigurationDescriptor(cval); if (!cd) { XTRACE(this, 0, 0, "initDevice - Error getting the full configuration descriptor"); } else { req.bInterfaceClass = kUSBCommClass; req.bInterfaceSubClass = kUSBAbstractControlModel; req.bInterfaceProtocol = kUSBv25; req.bAlternateSetting = kIOUSBFindInterfaceDontCare; ior = fpDevice->FindNextInterfaceDescriptor(cd, intf, &req, &intf); if (ior == kIOReturnSuccess) { if (intf) { XTRACE(this, 0, config, "initDevice - Interface descriptor found"); config = cd->bConfigurationValue; goodconfig = true; // We have at least one CDC interface in this configuration break; } else { XTRACE(this, 0, config, "initDevice - That's weird the interface was null"); } } else { XTRACE(this, 0, cval, "initDevice - No CDC interface found this configuration"); } } } if (goodconfig) { ior = fpDevice->SetConfiguration(this, config); if (ior != kIOReturnSuccess) { XTRACE(this, 0, ior, "initDevice - SetConfiguration error"); goodconfig = false; } } if (!goodconfig) // If we're not good - bail return false; fbmAttributes = cd->bmAttributes; XTRACE(this, fbmAttributes, kUSBAtrRemoteWakeup, "initDevice - Configuration bmAttributes"); fSuspendOK = false; if (!(fbmAttributes & kUSBAtrSelfPowered)) { if (fbmAttributes & kUSBAtrBusPowered) { fSuspendOK = true; } } if (fSuspendOK) { XTRACE(this, 0, 0, "initDevice - Suspend/Resume is active"); } else { XTRACE(this, 0, 0, "initDevice - Suspend/Resume is inactive"); } if (fbmAttributes & kUSBAtrRemoteWakeup) { getPMRootDomain()->publishFeature("WakeOnRing"); // Clear the feature if wake-on-ring is not set (SetConfiguration sets the feature // automatically if the device supports remote wake up) if (!WakeonRing()) { devreq.bmRequestType = USBmakebmRequestType(kUSBOut, kUSBStandard, kUSBDevice); devreq.bRequest = kUSBRqClearFeature; devreq.wValue = kUSBFeatureDeviceRemoteWakeup; devreq.wIndex = 0; devreq.wLength = 0; devreq.pData = 0; ior = fpDevice->DeviceRequest(&devreq); if (ior == kIOReturnSuccess) { XTRACE(this, 0, ior, "initDevice - Clearing remote wake up feature successful"); } else { XTRACE(this, 0, ior, "initDevice - Clearing remote wake up feature failed"); } } } else { XTRACE(this, 0, 0, "initDevice - Remote wake up not supported"); } return goodconfig; }/* end initDevice */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::getFunctionalDescriptors // // Inputs: port - the port // // Outputs: return - true (descriptors ok), false (somethings not right or not supported) // // Desc: Finds all the functional descriptors for the specific interface // /****************************************************************************************************/ bool AppleUSBCDCDriver::getFunctionalDescriptors(PortInfo_t *port) { bool gotDescriptors = false; bool configok = true; const FunctionalDescriptorHeader *funcDesc = NULL; CMFunctionalDescriptor *CMFDesc; // call management functional descriptor ACMFunctionalDescriptor *ACMFDesc; // abstract control management functional descriptor XTRACE(port, 0, 0, "getFunctionalDescriptors"); // Set some defaults just in case and then get the associated functional descriptors port->CMCapabilities = CM_ManagementData + CM_ManagementOnData; port->ACMCapabilities = ACM_DeviceSuppControl + ACM_DeviceSuppBreak; port->DataInterfaceNumber = 0xff; do { (IOUSBDescriptorHeader*)funcDesc = port->CommInterface->FindNextAssociatedDescriptor((void*)funcDesc, CS_INTERFACE); if (!funcDesc) { gotDescriptors = true; } else { switch (funcDesc->bDescriptorSubtype) { case Header_FunctionalDescriptor: XTRACE(port, funcDesc->bDescriptorType, funcDesc->bDescriptorSubtype, "getFunctionalDescriptors - Header Functional Descriptor"); break; case CM_FunctionalDescriptor: (const FunctionalDescriptorHeader*)CMFDesc = funcDesc; XTRACE(port, funcDesc->bDescriptorType, funcDesc->bDescriptorSubtype, "getFunctionalDescriptors - CM Functional Descriptor"); port->DataInterfaceNumber = CMFDesc->bDataInterface; port->CMCapabilities = CMFDesc->bmCapabilities; // Check the configuration supports data management on the data interface (that's all we support) if (!(port->CMCapabilities & CM_ManagementData)) { XTRACE(port, 0, 0, "getFunctionalDescriptors - Interface doesn't support Call Management"); configok = false; } if (!(port->CMCapabilities & CM_ManagementOnData)) { XTRACE(port, 0, 0, "getFunctionalDescriptors - Interface doesn't support Call Management on Data Interface"); // configok = false; // Some devices get this wrong so we'll let it slide } break; case ACM_FunctionalDescriptor: (const FunctionalDescriptorHeader*)ACMFDesc = funcDesc; XTRACE(port, funcDesc->bDescriptorType, funcDesc->bDescriptorSubtype, "getFunctionalDescriptors - ACM Functional Descriptor"); port->ACMCapabilities = ACMFDesc->bmCapabilities; break; case Union_FunctionalDescriptor: XTRACE(port, funcDesc->bDescriptorType, funcDesc->bDescriptorSubtype, "getFunctionalDescriptors - Union Functional Descriptor"); break; case CS_FunctionalDescriptor: XTRACE(port, funcDesc->bDescriptorType, funcDesc->bDescriptorSubtype, "getFunctionalDescriptors - CS Functional Descriptor"); break; default: XTRACE(port, funcDesc->bDescriptorType, funcDesc->bDescriptorSubtype, "getFunctionalDescriptors - unknown Functional Descriptor"); break; } } } while(!gotDescriptors); return configok; }/* end getFunctionalDescriptors */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::createSuffix // // Inputs: port - the port // // 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. The interface number is also added to make it unique for // multiple CDC configuration devices. // /****************************************************************************************************/ bool AppleUSBCDCDriver::createSuffix(PortInfo_t *port, unsigned char *sufKey) { IOReturn rc; UInt8 serBuf[12]; // arbitrary size > 8 OSNumber *location; UInt32 locVal; UInt8 *rlocVal; UInt16 offs, i, sig = 0; UInt8 indx; bool keyOK = false; XTRACE(port, 0, 0, "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); sig = strlen((char *)sufKey); keyOK = true; } } else { XTRACE(port, 0, rc, "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; } keyOK = true; } } // Make it unique if there's more than one configuration and config value is none zero if (keyOK) { sufKey[sig] = Asciify((UInt8)port->CommInterfaceNumber >> 4); if (sufKey[sig] != '0') sig++; sufKey[sig] = Asciify((UInt8)port->CommInterfaceNumber); if (sufKey[sig] != '0') sig++; sufKey[sig] = 0x00; } return keyOK; }/* end createSuffix */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::createSerialStream // // Inputs: port - the port // // Outputs: return Code - true (created and initialilzed ok), false (it failed) // // Desc: Creates and initializes the nub and port structure // /****************************************************************************************************/ bool AppleUSBCDCDriver::createSerialStream(PortInfo_t *port) { IOModemSerialStreamSync *pNub = new IOModemSerialStreamSync; bool ret; UInt8 indx; IOReturn rc; unsigned char rname[20]; const char *suffix = (const char *)&rname; XTRACE(port, 0, pNub, "createSerialStream"); if (!pNub) { return false; } // Either we attached and should get rid of our reference // or we failed in which case we should get rid our reference as well. // This just makes sure the reference count is correct. ret = (pNub->init(0, port) && pNub->attach(this)); pNub->release(); if (!ret) { XTRACE(port, ret, 0, "createSerialStream - Failed to attach to the nub"); return false; } // Report the base name to be used for generating device nodes pNub->setProperty(kIOTTYBaseNameKey, baseName); // Create suffix key and set it if (createSuffix(port, (unsigned char *)suffix)) { pNub->setProperty(kIOTTYSuffixKey, suffix); } pNub->registerService(); // Save the Product String (at least the first productNameLength's worth). This is done (same name) per stream for the moment. 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); } pNub->setProperty((const char *)propertyTag, (const char *)fProductName); } } return true; }/* end createSerialStream */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::acquirePort // // Inputs: sleep - true (wait for it), false (don't) // refCon - the Port // // Outputs: Return Code - kIOReturnSuccess, kIOReturnExclusiveAccess, kIOReturnIOError and various others // // Desc: Set up for gated acquirePort call. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::acquirePort(bool sleep, void *refCon) { IOReturn ret; PortInfo_t *port = (PortInfo_t *)refCon; parmList parms; XTRACE(port, 0, sleep, "acquirePort"); parms.arg1 = (void *)sleep; parms.arg2 = NULL; parms.arg3 = NULL; parms.arg4 = NULL; parms.port = port; retain(); ret = port->commandGate->runAction(acquirePortAction, (void *)&parms); release(); return ret; }/* end acquirePort */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::acquirePortAction // // Desc: Dummy pass through for acquirePortGated. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::acquirePortAction(OSObject *owner, void *arg0, void *, void *, void *) { parmList *parms = (parmList *)arg0; return ((AppleUSBCDCDriver *)owner)->acquirePortGated((bool)parms->arg1, parms->port); }/* end acquirePortAction */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::acquirePortGated // // Inputs: sleep - true (wait for it), false (don't) // port - 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 AppleUSBCDCDriver::acquirePortGated(bool sleep, PortInfo_t *port) { UInt32 busyState = 0; IOReturn rtn = kIOReturnSuccess; XTRACE(port, 0, sleep, "acquirePortGated"); retain(); // Hold reference till releasePortgated, unless we fail to acquire while (true) { busyState = port->State & PD_S_ACQUIRED; if (!busyState) { // Set busy bit (acquired), and clear everything else setStateGated((UInt32)PD_S_ACQUIRED | DEFAULT_STATE, (UInt32)STATE_ALL, port); break; } else { if (!sleep) { XTRACE(port, 0, 0, "acquirePortGated - Busy exclusive access"); release(); return kIOReturnExclusiveAccess; } else { busyState = 0; rtn = watchStateGated(&busyState, PD_S_ACQUIRED, port); if ((rtn == kIOReturnIOError) || (rtn == kIOReturnSuccess)) { continue; } else { XTRACE(port, 0, 0, "acquirePortGated - Interrupted!"); release(); return rtn; } } } } do { if (fSuspendOK) { rtn = fpDevice->SuspendDevice(false); // Resume the device if (rtn != kIOReturnSuccess) { break; } } IOSleep(50); setStructureDefaults(port); // Set the default values // Read the comm interrupt pipe for status: port->fCommCompletionInfo.target = this; port->fCommCompletionInfo.action = commReadComplete; port->fCommCompletionInfo.parameter = port; rtn = port->CommPipe->Read(port->CommPipeMDP, &port->fCommCompletionInfo, NULL); if (rtn == kIOReturnSuccess) { // Read the data-in bulk pipe: port->fReadCompletionInfo.target = this; port->fReadCompletionInfo.action = dataReadComplete; port->fReadCompletionInfo.parameter = port; rtn = port->InPipe->Read(port->PipeInMDP, &port->fReadCompletionInfo, NULL); if (rtn == kIOReturnSuccess) { // Set up the data-out bulk pipe: port->fWriteCompletionInfo.target = this; port->fWriteCompletionInfo.action = dataWriteComplete; port->fWriteCompletionInfo.parameter = port; // Set up the management element request completion routine: port->fMERCompletionInfo.target = this; port->fMERCompletionInfo.action = merWriteComplete; port->fMERCompletionInfo.parameter = NULL; } else { break; } } else { break; } fSessions++; // Bump number of active sessions and turn on clear to send setStateGated(PD_RS232_S_CTS, PD_RS232_S_CTS, port); return kIOReturnSuccess; } while (0); // We failed for some reason setStateGated(0, STATE_ALL, port); // Clear the entire state word release(); return rtn; }/* end acquirePortGated */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::releasePort // // Inputs: refCon - the Port // // Outputs: Return Code - kIOReturnSuccess or kIOReturnNotOpen // // Desc: Set up for gated releasePort call. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::releasePort(void *refCon) { IOReturn ret = kIOReturnSuccess; PortInfo_t *port = (PortInfo_t *)refCon; parmList parms; UInt16 i; bool portok = false; XTRACE(port, 0, 0, "releasePort"); // In some odd situations releasePort can occur after stop has been issued. // We need to check here to make sure the refcon/port is still valid. for (i=0; icommandGate->runAction(releasePortAction, (void *)&parms); release(); } else { XTRACE(port, 0, 0, "releasePort - port is invalid"); } return ret; }/* end releasePort */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::releasePortAction // // Desc: Dummy pass through for releasePortGated. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::releasePortAction(OSObject *owner, void *arg0, void *, void *, void *) { parmList *parms = (parmList *)arg0; return ((AppleUSBCDCDriver *)owner)->releasePortGated(parms->port); }/* end acquirePortAction */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::releasePortGated // // Inputs: port - the Port // // Outputs: Return Code - kIOReturnSuccess or kIOReturnNotOpen // // Desc: releasePort returns all the resources and does clean up. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::releasePortGated(PortInfo_t *port) { UInt32 busyState; IOReturn ior; XTRACE(port, 0, 0, "releasePortGated"); busyState = (port->State & PD_S_ACQUIRED); if (!busyState) { XTRACE(port, 0, 0, "AppleUSBCDCDriver::releasePort - NOT OPEN"); if (fTerminate) return kIOReturnOffline; return kIOReturnNotOpen; } if (!fTerminate) USBSetControlLineState(port, false, false); // clear RTS and clear DTR only if not terminated setStateGated(0, (UInt32)STATE_ALL, port); // Clear the entire state word - which also deactivates the port port->CommPipe->Abort(); // Abort any outstanding reads port->InPipe->Abort(); if (!fTerminate) { if (fSuspendOK) { ior = fpDevice->SuspendDevice(true); // Suspend the device again (if supported and not unplugged) if (ior) { XTRACE(port, 0, ior, "releasePort - SuspendDevice error"); } } } fSessions--; // reduce number of active sessions if ((fTerminate) && (fSessions == 0)) // if it's the result of a terminate and session count is zero we also need to close the device { fpDevice->close(this); fpDevice = NULL; } else { fpDevice->ReEnumerateDevice(0); // Re-enumerate for now to clean up various problems } release(); // Dispose of the self-reference we took in acquirePortGated() XTRACE(port, 0, 0, "releasePort - Exit"); return kIOReturnSuccess; }/* end releasePortGated */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::getState // // Inputs: refCon - the Port // // Outputs: Return value - port state // // Desc: Set up for gated getState call. // /****************************************************************************************************/ UInt32 AppleUSBCDCDriver::getState(void *refCon) { UInt32 currState; PortInfo_t *port = (PortInfo_t *)refCon; parmList parms; XTRACE(port, 0, 0, "getState"); parms.arg1 = NULL; parms.arg2 = NULL; parms.arg3 = NULL; parms.arg4 = NULL; parms.port = port; retain(); currState = port->commandGate->runAction(getStateAction, (void *)&parms); release(); return currState; }/* end getState */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::getStateAction // // Desc: Dummy pass through for getStateGated. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::getStateAction(OSObject *owner, void *arg0, void *, void *, void *) { UInt32 newState; parmList *parms = (parmList *)arg0; newState = ((AppleUSBCDCDriver *)owner)->getStateGated(parms->port); return newState; }/* end getStateAction */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::getStateGated // // Inputs: port - the Port // // Outputs: return value - port state // // Desc: Get the state for the port. // /****************************************************************************************************/ UInt32 AppleUSBCDCDriver::getStateGated(PortInfo_t *port) { UInt32 state; XTRACE(port, 0, 0, "getStateGated"); CheckQueues(port); state = port->State & EXTERNAL_MASK; XTRACE(port, state, EXTERNAL_MASK, "getState - Exit"); return state; }/* end getStateGated */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::setState // // Inputs: state - the state // mask - the mask // refCon - the Port // // Outputs: Return code - kIOReturnSuccess or kIOReturnBadArgument // // Desc: Set up for gated setState call. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::setState(UInt32 state, UInt32 mask, void *refCon) { IOReturn ret = kIOReturnSuccess; PortInfo_t *port = (PortInfo_t *)refCon; parmList parms; XTRACE(port, 0, 0, "setState"); // Cannot acquire or activate via setState if (mask & (PD_S_ACQUIRED | PD_S_ACTIVE | (~EXTERNAL_MASK))) { ret = kIOReturnBadArgument; } else { // ignore any bits that are read-only mask &= (~port->FlowControl & PD_RS232_A_MASK) | PD_S_MASK; if (mask) { parms.arg1 = (void *)state; parms.arg2 = (void *)mask; parms.arg3 = NULL; parms.arg4 = NULL; parms.port = port; retain(); ret = port->commandGate->runAction(setStateAction, (void *)&parms); release(); } } return ret; }/* end setState */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::setStateAction // // Desc: Dummy pass through for setStateGated. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::setStateAction(OSObject *owner, void *arg0, void *, void *, void *) { parmList *parms = (parmList *)arg0; return ((AppleUSBCDCDriver *)owner)->setStateGated((UInt32)parms->arg1, (UInt32)parms->arg2, parms->port); }/* end setStateAction */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::setStateGated // // Inputs: state - state to set // mask - state mask // port - 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 AppleUSBCDCDriver::setStateGated(UInt32 state, UInt32 mask, PortInfo_t *port) { UInt32 delta; XTRACE(port, state, mask, "setStateGated"); if (fTerminate) return kIOReturnOffline; // Check if it's being acquired or already acquired if ((state & PD_S_ACQUIRED) || (port->State & PD_S_ACQUIRED)) { if (mask & PD_RS232_S_DTR) { if ((state & PD_RS232_S_DTR) != (port->State & PD_RS232_S_DTR)) { if (state & PD_RS232_S_DTR) { XTRACE(port, 0, 0, "setState - DTR TRUE"); USBSetControlLineState(port, false, true); } else { XTRACE(port, 0, 0, "setState - DTR FALSE"); USBSetControlLineState(port, false, false); } } } 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) { port->commandGate->commandWakeup((void *)&port->State); } return kIOReturnSuccess; } else { XTRACE(port, port->State, 0, "setStateGated - Not Acquired"); } return kIOReturnNotOpen; }/* end setStateGated */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::watchState // // Inputs: state - state to watch for // mask - state mask bits // refCon - the Port // // Outputs: Return Code - kIOReturnSuccess or value returned from ::watchState // // Desc: Set up for gated watchState call. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::watchState(UInt32 *state, UInt32 mask, void *refCon) { IOReturn ret; PortInfo_t *port = (PortInfo_t *)refCon; parmList parms; XTRACE(port, *state, mask, "watchState"); if (!state) return kIOReturnBadArgument; if (!mask) return kIOReturnSuccess; parms.arg1 = (void *)state; parms.arg2 = (void *)mask; parms.arg3 = NULL; parms.arg4 = NULL; parms.port = port; retain(); ret = port->commandGate->runAction(watchStateAction, (void *)&parms); release(); return ret; }/* end watchState */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::watchStateAction // // Desc: Dummy pass through for watchStateGated. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::watchStateAction(OSObject *owner, void *arg0, void *, void *, void *) { parmList *parms = (parmList *)arg0; return ((AppleUSBCDCDriver *)owner)->watchStateGated((UInt32 *)parms->arg1, (UInt32)parms->arg2, parms->port); }/* end watchStateAction */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::watchStateGated // // Inputs: state - state to watch for // mask - state mask bits // port - the Port // // Outputs: Return Code - kIOReturnSuccess or value returned from privateWatchState // // 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 AppleUSBCDCDriver::watchStateGated(UInt32 *state, UInt32 mask, PortInfo_t *port) { unsigned watchState, foundStates; bool autoActiveBit = false; IOReturn ret = kIOReturnNotOpen; XTRACE(port, *state, mask, "watchStateGated"); if (fTerminate) return kIOReturnOffline; if (port->State & PD_S_ACQUIRED) { ret = kIOReturnSuccess; mask &= EXTERNAL_MASK; watchState = *state; 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; } while (true) { // 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)) { ret = kIOReturnIOError; } else { ret = kIOReturnSuccess; } 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; XTRACE(port, port->State, port->WatchStateMask, "watchStateGated - Thread sleeping"); retain(); // Just to make sure all threads are awake port->commandGate->retain(); // before we're released ret = port->commandGate->commandSleep((void *)&port->State); // port->commandGate->retain(); port->commandGate->release(); XTRACE(port, port->State, ret, "watchStateGated - Thread restart"); if (ret == THREAD_TIMED_OUT) { ret = kIOReturnTimeout; break; } else { if (ret == THREAD_INTERRUPTED) { ret = kIOReturnAborted; break; } } release(); } // 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; XTRACE(port, *state, 0, "watchStateGated - Thread wakeing others"); port->commandGate->commandWakeup((void *)&port->State); *state &= EXTERNAL_MASK; } XTRACE(port, ret, 0, "watchState - Exit"); return ret; }/* end watchStateGated */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::nextEvent // // Inputs: refCon - the Port // // Outputs: Return Code - kIOReturnSuccess or kIOReturnOffline // // Desc: Not used by this driver. // /****************************************************************************************************/ UInt32 AppleUSBCDCDriver::nextEvent(void *refCon) { PortInfo_t *port = (PortInfo_t *)refCon; XTRACE(port, 0, 0, "nextEvent"); if (fTerminate) return kIOReturnOffline; if (getState(port) & PD_S_ACTIVE) { return kIOReturnSuccess; } return kIOReturnNotOpen; }/* end nextEvent */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::executeEvent // // Inputs: event - The event // data - any data associated with the event // refCon - the Port // // Outputs: Return Code - kIOReturnSuccess, kIOReturnNotOpen or kIOReturnBadArgument // // Desc: Set up for gated executeEvent call. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::executeEvent(UInt32 event, UInt32 data, void *refCon) { IOReturn ret; PortInfo_t *port = (PortInfo_t *)refCon; parmList parms; XTRACE(port, data, event, "executeEvent"); parms.arg1 = (void *)event; parms.arg2 = (void *)data; parms.arg3 = NULL; parms.arg4 = NULL; parms.port = port; retain(); ret = port->commandGate->runAction(executeEventAction, (void *)&parms); release(); return ret; }/* end executeEvent */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::executeEventAction // // Desc: Dummy pass through for executeEventGated. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::executeEventAction(OSObject *owner, void *arg0, void *, void *, void *) { parmList *parms = (parmList *)arg0; return ((AppleUSBCDCDriver *)owner)->executeEventGated((UInt32)parms->arg1, (UInt32)parms->arg2, parms->port); }/* end executeEventAction */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::executeEventGated // // Inputs: event - The event // data - any data associated with the event // port - 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 AppleUSBCDCDriver::executeEventGated(UInt32 event, UInt32 data, PortInfo_t *port) { IOReturn ret = kIOReturnSuccess; UInt32 state, delta; if (fTerminate) return kIOReturnOffline; delta = 0; state = port->State; XTRACE(port, state, event, "executeEventGated"); if ((state & PD_S_ACQUIRED) == 0) return kIOReturnNotOpen; switch (event) { case PD_RS232_E_XON_BYTE: XTRACE(port, data, event, "executeEventGated - PD_RS232_E_XON_BYTE"); port->XONchar = data; break; case PD_RS232_E_XOFF_BYTE: XTRACE(port, data, event, "executeEventGated - PD_RS232_E_XOFF_BYTE"); port->XOFFchar = data; break; case PD_E_SPECIAL_BYTE: XTRACE(port, data, event, "executeEventGated - PD_E_SPECIAL_BYTE"); port->SWspecial[ data >> SPECIAL_SHIFT ] |= (1 << (data & SPECIAL_MASK)); break; case PD_E_VALID_DATA_BYTE: XTRACE(port, data, event, "executeEventGated - PD_E_VALID_DATA_BYTE"); port->SWspecial[ data >> SPECIAL_SHIFT ] &= ~(1 << (data & SPECIAL_MASK)); break; case PD_E_FLOW_CONTROL: XTRACE(port, data, event, "executeEventGated - PD_E_FLOW_CONTROL"); break; case PD_E_ACTIVE: XTRACE(port, data, event, "executeEventGated - PD_E_ACTIVE"); if ((bool)data) { if (!(state & PD_S_ACTIVE)) { setStructureDefaults(port); setStateGated((UInt32)PD_S_ACTIVE, (UInt32)PD_S_ACTIVE, port); // activate port USBSetControlLineState(port, true, true); // set RTS and set DTR } } else { if ((state & PD_S_ACTIVE)) { setStateGated(0, (UInt32)PD_S_ACTIVE, port); // deactivate port USBSetControlLineState(port, false, false); // clear RTS and clear DTR } } break; case PD_E_DATA_LATENCY: XTRACE(port, data, event, "executeEventGated - PD_E_DATA_LATENCY"); port->DataLatInterval = long2tval(data * 1000); break; case PD_RS232_E_MIN_LATENCY: XTRACE(port, data, event, "executeEventGated - PD_RS232_E_MIN_LATENCY"); port->MinLatency = bool(data); break; case PD_E_DATA_INTEGRITY: XTRACE(port, data, event, "executeEventGated - 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; USBSetLineCoding(port); } break; case PD_E_DATA_RATE: XTRACE(port, data, event, "executeEventGated - PD_E_DATA_RATE"); // For API compatiblilty with Intel. data >>= 1; XTRACE(port, data, 0, "executeEventGated - actual data rate"); if ((data < MIN_BAUD) || (data > kMaxBaudRate)) { ret = kIOReturnBadArgument; } else { port->BaudRate = data; USBSetLineCoding(port); } break; case PD_E_DATA_SIZE: XTRACE(port, data, event, "executeEventGated - PD_E_DATA_SIZE"); // For API compatiblilty with Intel. data >>= 1; XTRACE(port, data, 0, "executeEventGated - actual data size"); if ((data < 5) || (data > 8)) { ret = kIOReturnBadArgument; } else { port->CharLength = data; USBSetLineCoding(port); } break; case PD_RS232_E_STOP_BITS: XTRACE(port, data, event, "executeEventGated - PD_RS232_E_STOP_BITS"); if ((data < 0) || (data > 20)) { ret = kIOReturnBadArgument; } else { port->StopBits = data; USBSetLineCoding(port); } break; case PD_E_RXQ_FLUSH: XTRACE(port, data, event, "executeEventGated - PD_E_RXQ_FLUSH"); break; case PD_E_RX_DATA_INTEGRITY: XTRACE(port, data, event, "executeEventGated - 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: XTRACE(port, data, event, "executeEventGated - PD_E_RX_DATA_RATE"); if (data) { ret = kIOReturnBadArgument; } break; case PD_E_RX_DATA_SIZE: XTRACE(port, data, event, "executeEventGated - PD_E_RX_DATA_SIZE"); if (data) { ret = kIOReturnBadArgument; } break; case PD_RS232_E_RX_STOP_BITS: XTRACE(port, data, event, "executeEventGated - PD_RS232_E_RX_STOP_BITS"); if (data) { ret = kIOReturnBadArgument; } break; case PD_E_TXQ_FLUSH: XTRACE(port, data, event, "executeEventGated - PD_E_TXQ_FLUSH"); break; case PD_RS232_E_LINE_BREAK: XTRACE(port, data, event, "executeEventGated - PD_RS232_E_LINE_BREAK"); state &= ~PD_RS232_S_BRK; delta |= PD_RS232_S_BRK; setStateGated(state, delta, port); break; case PD_E_DELAY: XTRACE(port, data, event, "executeEventGated - PD_E_DELAY"); port->CharLatInterval = long2tval(data * 1000); break; case PD_E_RXQ_SIZE: XTRACE(port, data, event, "executeEventGated - PD_E_RXQ_SIZE"); break; case PD_E_TXQ_SIZE: XTRACE(port, data, event, "executeEventGated - PD_E_TXQ_SIZE"); break; case PD_E_RXQ_HIGH_WATER: XTRACE(port, data, event, "executeEventGated - PD_E_RXQ_HIGH_WATER"); break; case PD_E_RXQ_LOW_WATER: XTRACE(port, data, event, "executeEventGated - PD_E_RXQ_LOW_WATER"); break; case PD_E_TXQ_HIGH_WATER: XTRACE(port, data, event, "executeEventGated - PD_E_TXQ_HIGH_WATER"); break; case PD_E_TXQ_LOW_WATER: XTRACE(port, data, event, "executeEventGated - PD_E_TXQ_LOW_WATER"); break; default: XTRACE(port, data, event, "executeEventGated - unrecognized event"); ret = kIOReturnBadArgument; break; } return ret; }/* end executeEventGated */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::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 etc. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::requestEvent(UInt32 event, UInt32 *data, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; IOReturn returnValue = kIOReturnSuccess; XTRACE(port, 0, event, "requestEvent"); if (fTerminate) return kIOReturnOffline; if (data == NULL) { XTRACE(port, 0, event, "requestEvent - data is null"); returnValue = kIOReturnBadArgument; } else { switch (event) { case PD_E_ACTIVE: XTRACE(port, 0, event, "requestEvent - PD_E_ACTIVE"); *data = bool(getState(port) & PD_S_ACTIVE); break; case PD_E_FLOW_CONTROL: XTRACE(port, port->FlowControl, event, "requestEvent - PD_E_FLOW_CONTROL"); *data = port->FlowControl; break; case PD_E_DELAY: XTRACE(port, 0, event, "requestEvent - PD_E_DELAY"); *data = tval2long(port->CharLatInterval)/ 1000; break; case PD_E_DATA_LATENCY: XTRACE(port, 0, event, "requestEvent - PD_E_DATA_LATENCY"); *data = tval2long(port->DataLatInterval)/ 1000; break; case PD_E_TXQ_SIZE: XTRACE(port, 0, event, "requestEvent - PD_E_TXQ_SIZE"); *data = GetQueueSize(&port->TX); break; case PD_E_RXQ_SIZE: XTRACE(port, 0, event, "requestEvent - PD_E_RXQ_SIZE"); *data = GetQueueSize(&port->RX); break; case PD_E_TXQ_LOW_WATER: XTRACE(port, 0, event, "requestEvent - PD_E_TXQ_LOW_WATER"); *data = 0; returnValue = kIOReturnBadArgument; break; case PD_E_RXQ_LOW_WATER: XTRACE(port, 0, event, "requestEvent - PD_E_RXQ_LOW_WATER"); *data = 0; returnValue = kIOReturnBadArgument; break; case PD_E_TXQ_HIGH_WATER: XTRACE(port, 0, event, "requestEvent - PD_E_TXQ_HIGH_WATER"); *data = 0; returnValue = kIOReturnBadArgument; break; case PD_E_RXQ_HIGH_WATER: XTRACE(port, 0, event, "requestEvent - PD_E_RXQ_HIGH_WATER"); *data = 0; returnValue = kIOReturnBadArgument; break; case PD_E_TXQ_AVAILABLE: XTRACE(port, 0, event, "requestEvent - PD_E_TXQ_AVAILABLE"); *data = FreeSpaceinQueue(&port->TX); break; case PD_E_RXQ_AVAILABLE: XTRACE(port, 0, event, "requestEvent - PD_E_RXQ_AVAILABLE"); *data = UsedSpaceinQueue(&port->RX); break; case PD_E_DATA_RATE: XTRACE(port, 0, event, "requestEvent - PD_E_DATA_RATE"); *data = port->BaudRate << 1; break; case PD_E_RX_DATA_RATE: XTRACE(port, 0, event, "requestEvent - PD_E_RX_DATA_RATE"); *data = 0x00; break; case PD_E_DATA_SIZE: XTRACE(port, 0, event, "requestEvent - PD_E_DATA_SIZE"); *data = port->CharLength << 1; break; case PD_E_RX_DATA_SIZE: XTRACE(port, 0, event, "requestEvent - PD_E_RX_DATA_SIZE"); *data = 0x00; break; case PD_E_DATA_INTEGRITY: XTRACE(port, 0, event, "requestEvent - PD_E_DATA_INTEGRITY"); *data = port->TX_Parity; break; case PD_E_RX_DATA_INTEGRITY: XTRACE(port, 0, event, "requestEvent - PD_E_RX_DATA_INTEGRITY"); *data = port->RX_Parity; break; case PD_RS232_E_STOP_BITS: XTRACE(port, 0, event, "requestEvent - PD_RS232_E_STOP_BITS"); *data = port->StopBits << 1; break; case PD_RS232_E_RX_STOP_BITS: XTRACE(port, 0, event, "requestEvent - PD_RS232_E_RX_STOP_BITS"); *data = 0x00; break; case PD_RS232_E_XON_BYTE: XTRACE(port, 0, event, "requestEvent - PD_RS232_E_XON_BYTE"); *data = port->XONchar; break; case PD_RS232_E_XOFF_BYTE: XTRACE(port, 0, event, "requestEvent - PD_RS232_E_XOFF_BYTE"); *data = port->XOFFchar; break; case PD_RS232_E_LINE_BREAK: XTRACE(port, 0, event, "requestEvent - PD_RS232_E_LINE_BREAK"); *data = bool(getState(port) & PD_RS232_S_BRK); break; case PD_RS232_E_MIN_LATENCY: XTRACE(port, 0, event, "requestEvent - PD_RS232_E_MIN_LATENCY"); *data = bool(port->MinLatency); break; default: XTRACE(port, 0, event, "requestEvent - unrecognized event"); returnValue = kIOReturnBadArgument; break; } } return kIOReturnSuccess; }/* end requestEvent */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::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 or kIOReturnNotOpen // // Desc: Not used by this driver. // Events are passed on to executeEvent for immediate action. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::enqueueEvent(UInt32 event, UInt32 data, bool sleep, void *refCon) { IOReturn ret; PortInfo_t *port = (PortInfo_t *)refCon; parmList parms; XTRACE(port, data, event, "enqueueEvent"); parms.arg1 = (void *)event; parms.arg2 = (void *)data; parms.arg3 = NULL; parms.arg4 = NULL; parms.port = port; retain(); ret = port->commandGate->runAction(executeEventAction, (void *)&parms); release(); return ret; }/* end enqueueEvent */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::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 AppleUSBCDCDriver::dequeueEvent(UInt32 *event, UInt32 *data, bool sleep, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; XTRACE(port, 0, 0, "dequeueEvent"); if (fTerminate) return kIOReturnOffline; if ((event == NULL) || (data == NULL)) return kIOReturnBadArgument; if (getState(port) & PD_S_ACTIVE) { return kIOReturnSuccess; } return kIOReturnNotOpen; }/* end dequeueEvent */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::enqueueData // // Inputs: buffer - the data // size - number of bytes // sleep - true (wait for it), false (don't) // refCon - the Port // // Outputs: Return Code - kIOReturnSuccess, kIOReturnBadArgument or value returned from watchState // count - bytes transferred // // Desc: set up for enqueueDataGated call. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::enqueueData(UInt8 *buffer, UInt32 size, UInt32 *count, bool sleep, void *refCon) { IOReturn ret; PortInfo_t *port = (PortInfo_t *)refCon; parmList parms; XTRACE(port, size, sleep, "enqueueData"); if (count == NULL || buffer == NULL) return kIOReturnBadArgument; parms.arg1 = (void *)buffer; parms.arg2 = (void *)size; parms.arg3 = (void *)count; parms.arg4 = (void *)sleep; parms.port = port; retain(); ret = port->commandGate->runAction(enqueueDataAction, (void *)&parms); release(); return ret; }/* end enqueueData */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::enqueueDatatAction // // Desc: Dummy pass through for equeueDataGated. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::enqueueDataAction(OSObject *owner, void *arg0, void *, void *, void *) { parmList *parms = (parmList *)arg0; return ((AppleUSBCDCDriver *)owner)->enqueueDataGated((UInt8 *)parms->arg1, (UInt32)parms->arg2, (UInt32 *)parms->arg3, (bool)parms->arg4, parms->port); }/* end enqueueDataAction */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::enqueueDataGated // // Inputs: buffer - the data // size - number of bytes // sleep - true (wait for it), false (don't) // port - 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 or kIOReturnOffline // if the device was unplugged. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::enqueueDataGated(UInt8 *buffer, UInt32 size, UInt32 *count, bool sleep, PortInfo_t *port) { UInt32 state = PD_S_TXQ_LOW_WATER; IOReturn rtn = kIOReturnSuccess; XTRACE(port, size, sleep, "enqueueDataGated"); if (fTerminate) return kIOReturnOffline; *count = 0; if (!(port->State & PD_S_ACTIVE)) return kIOReturnNotOpen; XTRACE(port, port->State, size, "enqueueDataGated - current State"); // LogData(kDataOther, size, buffer, port); // 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(port); // 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 = watchStateGated(&state, PD_S_TXQ_LOW_WATER, port); if (rtn != kIOReturnSuccess) { XTRACE(port, 0, rtn, "enqueueDataGated - 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(port); } XTRACE(port, *count, size, "enqueueDataGated - Exit"); return kIOReturnSuccess; }/* end enqueueDataGated */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::dequeueData // // Inputs: size - buffer size // min - minimum bytes required // refCon - the Port (not used) // // Outputs: buffer - data returned // min - number of bytes // Return Code - kIOReturnSuccess, kIOReturnBadArgument, kIOReturnNotOpen, or value returned from watchState // // Desc: set up for enqueueDataGated call. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::dequeueData(UInt8 *buffer, UInt32 size, UInt32 *count, UInt32 min, void *refCon) { IOReturn ret; PortInfo_t *port = (PortInfo_t *)refCon; parmList parms; XTRACE(port, size, min, "dequeueData"); if ((count == NULL) || (buffer == NULL) || (min > size)) return kIOReturnBadArgument; parms.arg1 = (void *)buffer; parms.arg2 = (void *)size; parms.arg3 = (void *)count; parms.arg4 = (void *)min; parms.port = port; retain(); ret = port->commandGate->runAction(dequeueDataAction, (void *)&parms); release(); return ret; }/* end dequeueData */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::dequeueDatatAction // // Desc: Dummy pass through for equeueDataGated. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::dequeueDataAction(OSObject *owner, void *arg0, void *, void *, void *) { parmList *parms = (parmList *)arg0; return ((AppleUSBCDCDriver *)owner)->dequeueDataGated((UInt8 *)parms->arg1, (UInt32)parms->arg2, (UInt32 *)parms->arg3, (UInt32)parms->arg4, parms->port); }/* end dequeueDataAction */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::dequeueDataGated // // Inputs: size - buffer size // min - minimum bytes required // port - 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 AppleUSBCDCDriver::dequeueDataGated(UInt8 *buffer, UInt32 size, UInt32 *count, UInt32 min, PortInfo_t *port) { IOReturn rtn = kIOReturnSuccess; UInt32 state = 0; bool goXOIdle; XTRACE(port, size, min, "dequeueDataGated"); if (fTerminate) return kIOReturnOffline; // If the port is not active then there should not be any chars. *count = 0; if (!(port->State & PD_S_ACTIVE)) return kIOReturnNotOpen; // Get any data living in the queue. *count = RemovefromQueue(&port->RX, buffer, size); CheckQueues(port); while ((min > 0) && (*count < min)) { // Figure out how many bytes we have left to queue up state = 0; rtn = watchStateGated(&state, PD_S_RXQ_EMPTY, port); if (rtn != kIOReturnSuccess) { XTRACE(port, 0, rtn, "dequeueDataGated - Interrupted!"); return rtn; } // Try and get more data starting from where we left off *count += RemovefromQueue(&port->RX, buffer + *count, (size - *count)); CheckQueues(port); } // Now let's check our receive buffer to see if we need to stop goXOIdle = (UsedSpaceinQueue(&port->RX) < port->RXStats.LowWater) && (port->RXOstate == SENT_XOFF); if (goXOIdle) { port->RXOstate = IDLE_XO; AddBytetoQueue(&port->TX, port->XOFFchar); setUpTransmit(port); } XTRACE(port, *count, size, "dequeueData - Exit"); return rtn; }/* end dequeueDataGated */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::setUpTransmit // // Inputs: port - the port // // Outputs: return code - true (transmit started), false (transmission already in progress) // // Desc: Setup and then start transmisson on the port specified // /****************************************************************************************************/ bool AppleUSBCDCDriver::setUpTransmit(PortInfo_t *port) { XTRACE(port, 0, port->AreTransmitting, "setUpTransmit"); // As a precaution just check we've not been terminated (may be a woken thread) if (fTerminate) { XTRACE(port, 0, 0, "setUpTransmit - terminating"); return false; } // If we are already in the cycle of transmitting characters, // then we do not need to do anything. if (port->AreTransmitting == TRUE) return FALSE; if (UsedSpaceinQueue(&port->TX) > 0) { startTransmission(port); } return TRUE; }/* end setUpTransmit */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::startTransmission // // Inputs: port - the port // // Outputs: None // // Desc: Start the transmisson on the port specified // Must be called from a gated method // /****************************************************************************************************/ void AppleUSBCDCDriver::startTransmission(PortInfo_t *port) { size_t count; IOReturn ior; XTRACE(port, 0, 0, "startTransmission"); // Sets up everything as we are running so as not to start this // port twice if a call occurs twice to this Method: port->AreTransmitting = TRUE; setStateGated(PD_S_TX_BUSY, PD_S_TX_BUSY, port); // Fill up the buffer with characters from the queue count = RemovefromQueue(&port->TX, port->PipeOutBuffer, MAX_BLOCK_SIZE); // If there are no bytes to send just exit: if (count <= 0) { // Updates all the status flags: CheckQueues(port); port->AreTransmitting = FALSE; setStateGated(0, PD_S_TX_BUSY, port); return; } XTRACE(port, port->State, count, "startTransmission - Bytes to write"); LogData(kDataOut, count, port->PipeOutBuffer, port); port->Count = count; port->PipeOutMDP->setLength(count); ior = port->OutPipe->Write(port->PipeOutMDP, &port->fWriteCompletionInfo); // We just removed a bunch of stuff from the // queue, so see if we can free some thread(s) // to enqueue more stuff. CheckQueues(port); return; }/* end startTransmission */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::USBSetLineCoding // // Inputs: port - the port // // Outputs: None // // Desc: Set up and send SetLineCoding Management Element Request(MER) for all settings. // /****************************************************************************************************/ void AppleUSBCDCDriver::USBSetLineCoding(PortInfo_t *port) { LineCoding *lineParms; IOUSBDevRequest *MER; IOReturn rc; UInt16 lcLen = sizeof(LineCoding)-1; XTRACE(port, 0, port->CommInterfaceNumber, "USBSetLineCoding"); // First check that Set Line Coding is supported if (!(port->ACMCapabilities & ACM_DeviceSuppControl)) { return; } // Check for changes and only do it if something's changed if ((port->BaudRate == port->LastBaudRate) && (port->StopBits == port->LastStopBits) && (port->TX_Parity == port->LastTX_Parity) && (port->CharLength == port->LastCharLength)) { return; } MER = (IOUSBDevRequest*)IOMalloc(sizeof(IOUSBDevRequest)); if (!MER) { XTRACE(port, 0, 0, "USBSetLineCoding - allocate MER failed"); return; } bzero(MER, sizeof(IOUSBDevRequest)); lineParms = (LineCoding*)IOMalloc(lcLen); if (!lineParms) { XTRACE(port, 0, 0, "USBSetLineCoding - allocate lineParms failed"); IOFree(MER, sizeof(IOUSBDevRequest)); return; } bzero(lineParms, lcLen); // convert BaudRate - intel format doubleword (32 bits) OSWriteLittleInt32(lineParms, dwDTERateOffset, port->BaudRate); port->LastBaudRate = port->BaudRate; lineParms->bCharFormat = port->StopBits - 2; port->LastStopBits = port->StopBits; lineParms->bParityType = port->TX_Parity - 1; port->LastTX_Parity = port->TX_Parity; lineParms->bDataBits = port->CharLength; port->LastCharLength = port->CharLength; // LogData(kDataOther, lcLen, lineParms, port); // now build the Management Element Request MER->bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBInterface); MER->bRequest = kUSBSET_LINE_CODING; MER->wValue = 0; MER->wIndex = port->CommInterfaceNumber; MER->wLength = lcLen; MER->pData = lineParms; port->fMERCompletionInfo.parameter = MER; rc = fpDevice->DeviceRequest(MER, &port->fMERCompletionInfo); if (rc != kIOReturnSuccess) { XTRACE(port, MER->bRequest, rc, "USBSetLineCoding - error issueing DeviceRequest"); IOFree(MER->pData, lcLen); IOFree(MER, sizeof(IOUSBDevRequest)); } }/* end USBSetLineCoding */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::USBSetControlLineState // // Inputs: port - the port // RTS - true(set RTS), false(clear RTS) // DTR - true(set DTR), false(clear DTR) // // Outputs: None // // Desc: Set up and send SetControlLineState Management Element Request(MER). // /****************************************************************************************************/ void AppleUSBCDCDriver::USBSetControlLineState(PortInfo_t *port, bool RTS, bool DTR) { IOReturn rc; IOUSBDevRequest *MER; UInt16 CSBitmap = 0; XTRACE(port, 0, port->CommInterfaceNumber, "USBSetControlLineState"); // First check that Set Control Line State is supported if (!(port->ACMCapabilities & ACM_DeviceSuppControl)) { return; } MER = (IOUSBDevRequest*)IOMalloc(sizeof(IOUSBDevRequest)); if (!MER) { XTRACE(port, 0, 0, "USBSetControlLineState - allocate MER failed"); return; } bzero(MER, sizeof(IOUSBDevRequest)); // now build the Management Element Request MER->bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBInterface); MER->bRequest = kUSBSET_CONTROL_LINE_STATE; if (RTS) CSBitmap |= kRTSOn; if (DTR) CSBitmap |= kDTROn; MER->wValue = CSBitmap; MER->wIndex = port->CommInterfaceNumber; MER->wLength = 0; MER->pData = NULL; port->fMERCompletionInfo.parameter = MER; rc = fpDevice->DeviceRequest(MER, &port->fMERCompletionInfo); if (rc != kIOReturnSuccess) { XTRACE(port, MER->bRequest, rc, "USBSetControlLineState - error issueing DeviceRequest"); IOFree(MER, sizeof(IOUSBDevRequest)); } }/* end USBSetControlLineState */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::USBSendBreak // // Inputs: port - the port // sBreak - true(set Break), false(clear Break) // // Outputs: None // // Desc: Set up and send SendBreak Management Element Request(MER). // /****************************************************************************************************/ void AppleUSBCDCDriver::USBSendBreak(PortInfo_t *port, bool sBreak) { IOReturn rc; IOUSBDevRequest *MER; XTRACE(port, 0, port->CommInterfaceNumber, "USBSendBreak"); // First check that Send Break is supported if (!(port->ACMCapabilities & ACM_DeviceSuppBreak)) { return; } MER = (IOUSBDevRequest*)IOMalloc(sizeof(IOUSBDevRequest)); if (!MER) { XTRACE(port, 0, 0, "USBSendBreak - allocate MER failed"); return; } bzero(MER, sizeof(IOUSBDevRequest)); // now build the Management Element Request MER->bmRequestType = USBmakebmRequestType(kUSBOut, kUSBClass, kUSBInterface); MER->bRequest = kUSBSEND_BREAK; if (sBreak) { MER->wValue = 0xFFFF; } else { MER->wValue = 0; } MER->wIndex = port->CommInterfaceNumber; MER->wLength = 0; MER->pData = NULL; port->fMERCompletionInfo.parameter = MER; rc = fpDevice->DeviceRequest(MER, &port->fMERCompletionInfo); if (rc != kIOReturnSuccess) { XTRACE(port, MER->bRequest, rc, "USBSendBreak - error issueing DeviceRequest"); IOFree(MER, sizeof(IOUSBDevRequest)); } }/* end USBSendBreak */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::clearPipeStall // // Inputs: port - the port // thePipe - the pipe // // Outputs: // // Desc: Clear a stall (by reset) on the specified pipe. All outstanding I/O // is returned as aborted. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::clearPipeStall(PortInfo_t *port, IOUSBPipe *thePipe) { UInt8 pipeStatus; IOReturn rtn = kIOReturnSuccess; XTRACE(port, 0, thePipe, "clearPipeStall"); pipeStatus = thePipe->GetStatus(); if (pipeStatus == kPipeStalled) { rtn = thePipe->Reset(); if (rtn == kIOReturnSuccess) { XTRACE(port, 0, 0, "clearPipeStall - Successful"); } else { XTRACE(port, 0, rtn, "clearPipeStall - Failed"); } } else { XTRACE(port, 0, 0, "clearPipeStall - Pipe not stalled"); } return rtn; }/* end clearPipeStall */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::initPort // // Inputs: port - the port // // Outputs: return code - true (initialized), false (failed) // // Desc: Initialize the rest of port data // /****************************************************************************************************/ bool AppleUSBCDCDriver::initPort(PortInfo_t *port) { XTRACE(port, 0, 0, "initPort"); port->commandGate = IOCommandGate::commandGate(this); if (!port->commandGate) { XTRACE(port, 0, 0, "initPort - commandGate failed"); return false; } if (fWorkLoop->addEventSource(port->commandGate) != kIOReturnSuccess) { XTRACE(port, 0, 0, "initPort - addEventSource(commandGate) failed"); return false; } port->commandGate->enable(); if (!allocateResources(port)) { XTRACE(port, 0, 0, "initPort - allocateResources failed"); return false; } return true; }/* end initPort */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::initStructure // // Inputs: port - the port // // Outputs: none // // Desc: Initialize the port structure // /****************************************************************************************************/ void AppleUSBCDCDriver::initStructure(PortInfo_t *port) { XTRACE(port, 0, 0, "initStructure"); // These are set up at start and should not be reset during execution. 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->PipeOutMDP = NULL; port->PipeInMDP = NULL; port->CommPipeMDP = NULL; port->CommInterface = NULL; port->DataInterface = NULL; port->InPipe = NULL; port->OutPipe = NULL; port->CommPipe = NULL; port->CommPipeBuffer = NULL; port->PipeInBuffer = NULL; port->PipeOutBuffer = NULL; port->Count = -1; }/* end initStructure */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::setStructureDefaults // // Inputs: port - the port // // Outputs: None // // Desc: Sets the defaults for the specified port structure // /****************************************************************************************************/ void AppleUSBCDCDriver::setStructureDefaults(PortInfo_t *port) { UInt32 tmp; XTRACE(port, 0, 0, "setStructureDefaults"); port->BaudRate = kDefaultBaudRate; // 9600 bps port->LastBaudRate = 0; port->CharLength = 8; // 8 Data bits port->LastCharLength = 0; port->StopBits = 2; // 1 Stop bit port->LastStopBits = 0; port->TX_Parity = 1; // No Parity port->LastTX_Parity = 0; 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 = kMaxCirBufferSize; port->RXStats.HighWater = (port->RXStats.BufferSize << 1) / 3; port->RXStats.LowWater = port->RXStats.HighWater >> 1; 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->AreTransmitting = FALSE; for (tmp=0; tmp < (256 >> SPECIAL_SHIFT); tmp++) port->SWspecial[ tmp ] = 0; return; }/* end setStructureDefaults */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::allocateResources // // Inputs: port - the Port // // Outputs: return code - true (allocate was successful), false (it failed) // // Desc: Finishes up the rest of the configuration and gets all the endpoints open etc. // /****************************************************************************************************/ bool AppleUSBCDCDriver::allocateResources(PortInfo_t *port) { IOUSBFindEndpointRequest epReq; // endPoint request bool goodCall; XTRACE(port, 0, 0, "allocateResources."); // Open all the end points and get the buffers goodCall = port->DataInterface->open(this); if (!goodCall) { XTRACE(port, 0, 0, "allocateResources - open data interface failed."); port->DataInterface->release(); port->DataInterface = NULL; return false; } goodCall = port->CommInterface->open(this); if (!goodCall) { XTRACE(port, 0, 0, "allocateResources - open comm interface failed."); port->CommInterface->release(); port->CommInterface = NULL; return false; } // Bulk In pipe epReq.type = kUSBBulk; epReq.direction = kUSBIn; epReq.maxPacketSize = 0; epReq.interval = 0; port->InPipe = port->DataInterface->FindNextPipe(0, &epReq); if (!port->InPipe) { XTRACE(port, 0, 0, "allocateResources - no bulk input pipe."); return false; } port->InPacketSize = epReq.maxPacketSize; XTRACE(port, epReq.maxPacketSize << 16 |epReq.interval, port->InPipe, "allocateResources - bulk input pipe."); // Allocate Memory Descriptor Pointer with memory for the bulk in pipe: port->PipeInMDP = IOBufferMemoryDescriptor::withCapacity(DATA_BUFF_SIZE, kIODirectionIn); if (!port->PipeInMDP) { XTRACE(port, 0, 0, "allocateResources - Couldn't allocate MDP for bulk in pipe"); return false; } // port->PipeInMDP->setLength(DATA_BUFF_SIZE); port->PipeInBuffer = (UInt8*)port->PipeInMDP->getBytesNoCopy(); XTRACE(port, 0, port->PipeInBuffer, "allocateResources - input buffer"); // Bulk Out pipe epReq.direction = kUSBOut; port->OutPipe = port->DataInterface->FindNextPipe(0, &epReq); if (!port->OutPipe) { XTRACE(port, 0, 0, "allocateResources - no bulk output pipe."); return false; } port->OutPacketSize = epReq.maxPacketSize; XTRACE(port, epReq.maxPacketSize << 16 |epReq.interval, port->OutPipe, "allocateResources - bulk output pipe."); // Allocate Memory Descriptor Pointer with memory for the bulk out pipe: port->PipeOutMDP = IOBufferMemoryDescriptor::withCapacity(MAX_BLOCK_SIZE, kIODirectionOut); if (!port->PipeOutMDP) { XTRACE(port, 0, 0, "allocateResources - Couldn't allocate MDP for bulk out pipe"); return false; } // port->PipeOutMDP->setLength(MAX_BLOCK_SIZE); port->PipeOutBuffer = (UInt8*)port->PipeOutMDP->getBytesNoCopy(); XTRACE(port, 0, port->PipeOutBuffer, "allocateResources - output buffer"); // Interrupt pipe epReq.type = kUSBInterrupt; epReq.direction = kUSBIn; port->CommPipe = port->CommInterface->FindNextPipe(0, &epReq); if (!port->CommPipe) { XTRACE(port, 0, 0, "allocateResources - no comm pipe."); return false; } XTRACE(port, epReq.maxPacketSize << 16 |epReq.interval, port->CommPipe, "allocateResources - comm pipe."); // Allocate Memory Descriptor Pointer with memory for the Interrupt pipe: port->CommPipeMDP = IOBufferMemoryDescriptor::withCapacity(COMM_BUFF_SIZE, kIODirectionIn); if (!port->CommPipeMDP) { XTRACE(port, 0, 0, "allocateResources - Couldn't allocate MDP for interrupt pipe"); return false; } // port->CommPipeMDP->setLength(COMM_BUFF_SIZE); port->CommPipeBuffer = (UInt8*)port->CommPipeMDP->getBytesNoCopy(); XTRACE(port, 0, port->CommPipeBuffer, "allocateResources - comm buffer"); // Now the ring buffers if (!allocateRingBuffer(port, &port->TX, port->TXStats.BufferSize)) { XTRACE(port, 0, 0, "allocateResources - Couldn't allocate TX ring buffer"); return false; } XTRACE(port, 0, port->TX.Start, "allocateResources - TX ring buffer"); if (!allocateRingBuffer(port, &port->RX, port->RXStats.BufferSize)) { XTRACE(port, 0, 0, "allocateResources - Couldn't allocate RX ring buffer"); return false; } XTRACE(port, 0, port->RX.Start, "allocateResources - RX ring buffer"); return true; }/* end allocateResources */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::releaseResources // // Inputs: port - the Port // // Outputs: None // // Desc: Frees up the resources allocated in allocateResources // /****************************************************************************************************/ void AppleUSBCDCDriver::releaseResources(PortInfo_t *port) { XTRACE(port, 0, 0, "releaseResources"); if (port->CommInterface) { port->CommInterface->close(this); port->CommInterface->release(); port->CommInterface = NULL; } if (port->DataInterface) { port->DataInterface->close(this); port->DataInterface->release(); port->DataInterface = NULL; } if (port->PipeOutMDP ) { port->PipeOutMDP->release(); port->PipeOutMDP = 0; } if (port->PipeInMDP ) { port->PipeInMDP->release(); port->PipeInMDP = 0; } if (port->CommPipeMDP) { port->CommPipeMDP->release(); port->CommPipeMDP = 0; } freeRingBuffer(port, &port->TX); freeRingBuffer(port, &port->RX); return; }/* end releaseResources */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::freeRingBuffer // // Inputs: port - the port // 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 AppleUSBCDCDriver::freeRingBuffer(PortInfo_t *port, CirQueue *Queue) { XTRACE(port, 0, Queue, "freeRingBuffer"); if (Queue) { if (Queue->Start) { IOFree(Queue->Start, Queue->Size); } CloseQueue(Queue); } return; }/* end freeRingBuffer */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::allocateRingBuffer // // Inputs: port - the port // 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 AppleUSBCDCDriver::allocateRingBuffer(PortInfo_t *port, CirQueue *Queue, size_t BufferSize) { UInt8 *Buffer; // Size is ignored and kMaxCirBufferSize, which is 4096, is used. XTRACE(port, 0, BufferSize, "allocateRingBuffer"); Buffer = (UInt8*)IOMalloc(kMaxCirBufferSize); InitQueue(Queue, Buffer, kMaxCirBufferSize); if (Buffer) return true; return false; }/* end allocateRingBuffer */ /****************************************************************************************************/ // // Function: AppleUSBCDCDriver::WakeonRing // // Inputs: none // // Outputs: return code - true(Wake-on-Ring enabled), false(disabled) // // Desc: Find the PMU entry and checks the wake-on-ring flag // /****************************************************************************************************/ bool AppleUSBCDCDriver::WakeonRing(void) { mach_timespec_t t; IOService *pmu; bool WoR = false; XTRACE(this, 0, 0, "WakeonRing"); t.tv_sec = 1; t.tv_nsec = 0; pmu = waitForService(IOService::serviceMatching("ApplePMU"), &t); if (pmu) { if (kOSBooleanTrue == pmu->getProperty("WakeOnRing")) { XTRACE(this, 0, 0, "WakeonRing - Enabled"); WoR = true; } else { XTRACE(this, 0, 0, "WakeonRing - Disabled"); } } else { XTRACE(this, 0, 0, "WakeonRing - serviceMatching ApplePMU failed"); } return WoR; }/* end WakeonRing */ /****************************************************************************************************/ // // Method: AppleUSBCDCDriver::message // // Inputs: type - message type // provider - my provider // argument - additional parameters // // Outputs: return Code - kIOReturnSuccess // // Desc: Handles IOKit messages. // /****************************************************************************************************/ IOReturn AppleUSBCDCDriver::message(UInt32 type, IOService *provider, void *argument) { XTRACE(this, 0, type, "message"); switch (type) { case kIOMessageServiceIsTerminated: XTRACE(this, fSessions, type, "message - kIOMessageServiceIsTerminated"); if (fSessions) { if (!fTerminate) // Check if we're already being terminated { // NOTE! This call below depends on the hard coded path of this KEXT. Make sure // that if the KEXT moves, this path is changed! KUNCUserNotificationDisplayNotice( 0, // Timeout in seconds 0, // Flags (for later usage) "", // iconPath (not supported yet) "", // soundPath (not supported yet) "/System/Library/Extensions/IOUSBFamily.kext/Contents/PlugIns/AppleUSBCDCDriver.kext", // localizationPath "Unplug Header", // the header "Unplug Notice", // the notice - look in Localizable.strings "OK"); } } else { // Clean up before closing the device cleanUp(); fpDevice->close(this); // Need to close so we can get the stop call (only if no sessions active - see releasePortGated) fpDevice = NULL; } fTerminate = true; // We're being terminated (unplugged) return kIOReturnSuccess; case kIOMessageServiceIsSuspended: XTRACE(this, 0, type, "message - kIOMessageServiceIsSuspended"); break; case kIOMessageServiceIsResumed: XTRACE(this, 0, type, "message - kIOMessageServiceIsResumed"); break; case kIOMessageServiceIsRequestingClose: XTRACE(this, 0, type, "message - kIOMessageServiceIsRequestingClose"); break; case kIOMessageServiceWasClosed: XTRACE(this, 0, type, "message - kIOMessageServiceWasClosed"); break; case kIOMessageServiceBusyStateChange: XTRACE(this, 0, type, "message - kIOMessageServiceBusyStateChange"); break; case kIOUSBMessagePortHasBeenResumed: XTRACE(this, 0, type, "message - kIOUSBMessagePortHasBeenResumed"); break; case kIOUSBMessageHubResumePort: XTRACE(this, 0, type, "message - kIOUSBMessageHubResumePort"); break; case kIOUSBMessagePortHasBeenReset: XTRACE(this, 0, type, "message - kIOUSBMessagePortHasBeenReset"); break; default: XTRACE(this, 0, type, "message - unknown message"); break; } return kIOReturnUnsupported; }/* end message */