/* *Copyright (c) 2000 Apple Computer, Inc. All rights reserved. * *@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@ */ /* *AppleSCCSerial.cpp * *MacOSX implementation of Serial Port driver * * *Humphrey Looney MacOSX IOKit ppc *Elias Keshishoglou MacOSX Server ppc *Dean Reece Original Next Intel version * * 18-Apr-01 David Laupmanis Update executeEvent to recognize MIDI clock mode, and call * SccConfigureForMIDI if MIDI clock mode is active * * 26-Feb-01 Paul Sun allocateRingBuffer(), now uses passed in buffer size which is default to BUFFER_SIZE_DEFAULT * * 07-Feb-01 Paul Sun Implemented hardware and software flow control. * * 22-Jan-01 Paul Sun Fixed bug # 2560437 -- took out the enableScc() function * which is now supported by PlatformExpert. * Fixed bug # 2606888 -- added code to AppleSCCSerial::start() * function for not setting 'irda' port on all the machines. * * *Copyright ©: 1999 Apple Computer, Inc. all rights reserved. */ #include /* UINT_MAX */ #include #include #include #include #include #include #include #include #include #include #include #include "PPCSerialPort.h" #include "SccChipPrimatives.h" #define IOSS_HALFBIT_BRD 1 //#define USE_TIMER_EVENT_SOURCE_DEBUGGING 1 //#define kTimerTimeout 3000 //#define SHOW_DEBUG_STRINGS 1 #ifdef SHOW_DEBUG_STRINGS #define DLOG(fmt, args...) IOLog(fmt, ## args) #else #define DLOG(fmt, args...) #endif extern void flush_dcache(vm_offset_t addr, unsigned count, int phys); extern UInt32 gTimerCanceled; // This is to check the status of the debug flags #include class AppleSCCRS232SerialStreamSync : public IORS232SerialStreamSync { OSDeclareDefaultStructors(AppleSCCRS232SerialStreamSync) public: virtual bool compareName(OSString *name, OSString **matched = 0) const; }; OSDefineMetaClassAndStructors (AppleSCCRS232SerialStreamSync, IORS232SerialStreamSync) // Steal Device Tree matching from the AppleMacIODevice compareName function. bool AppleSCCRS232SerialStreamSync:: compareName(OSString *name, OSString ** matched) const { return IODTCompareNubName(this, name, matched) || IORegistryEntry::compareName(name, matched); } OSDefineMetaClassAndStructors(AppleSCCSerial, IOSerialDriverSync) #define super IOSerialDriverSync /* *AppleSCCSerial::start *note returning false currently causes a crash */ bool AppleSCCSerial::start(IOService *provider) { IOMemoryMap *map; if (!super::start(provider)) return false; fProvider = OSDynamicCast(AppleMacIODevice, provider); if (!fProvider) return false; // get our workloop myWorkLoop = getWorkLoop(); #if USE_TIMER_EVENT_SOURCE_DEBUGGING // get a timer and set our timeout handler to be called when it fires myTimer = IOTimerEventSource::timerEventSource( this, (IOTimerEventSource::Action)&AppleSCCSerial::timeoutHandler ); // make sure we got a timer if( !myTimer ) { //IOLog( "%s: Failed to create timer event source\n", getName() ); result = false; return result; } // add the timer to the workloop if(!myWorkLoop || (myWorkLoop->addEventSource(myTimer) != kIOReturnSuccess) ) { //IOLog( "%s: Failed to add timer event source to workloop\n", getName() ); result = false; return result; } // now set the timeout // this example uses 1 seconds // myTimer->setTimeoutMS( kTimerTimeout ); // zero counter counter = 0; // this is used to count the number of times that timeoutHandler is called #endif; // Never, never match if the user is debugging with kprintf: { UInt32 debugFlags; if (!PE_parse_boot_arg("debug", &debugFlags)) debugFlags = 0; if (debugFlags & 8) return NULL; } // Figure out what kind of serial device nub to make. // first check if the parent is escc or escc-legacy // don't need to match on escc-legacy nodes { IORegistryEntry *parentEntry = provider->getParentEntry(gIODTPlane); if (!parentEntry || IODTMatchNubWithKeys(parentEntry, "'escc-legacy'")) return false; } { OSString *matched = (OSString *) getProperty(gIONameMatchedKey); if (matched->isEqualTo("ch-a")) Port.whichPort = serialPortA; else if (matched->isEqualTo("ch-b")) Port.whichPort = serialPortB; else return false; } // for DCP -- begin // need to do the DCP stuff for port a only // find out is the modem a DCP modem or not if (Port.whichPort == serialPortA) { // IOLog ("AppleSCCSerial::start -- Calling LookForInternalModem\n"); Port.gDCPModemFound = LookForInternalModem (kDCPModem); /* if (Port.gDCPModemFound) { IOLog ("AppleSCCSerial::start -- Found DCP modem\n"); } else { IOLog ("AppleSCCSerial::start -- Did not find DCP modem\n"); } } else { IOLog ("AppleSCCSerial::start -- It is not for serial Port A\n"); */ } // for DCP -- end // sets the name for this port: setPortName(provider); OSString *name = OSDynamicCast(OSString, getProperty(kIOTTYBaseNameKey)); if ((name != NULL) && (name->isEqualTo("none"))) return false; /* Begin fixed bug # 2550140 & 2553750 */ /* init the DBDMA memory lock */ Port.IODBDMARxLock = IOLockAlloc(); Port.IODBDMATrLock = IOLockAlloc(); /* End fixed bug # 2550140 & 2553750 */ Port.DTRAsserted = true; //we are Data Terminal Ready Port.aboveRxHighWater = false; // Port.SCCWriteLock = IOLockAlloc(); Port.SCCAccessLock = IOLockAlloc(); // init the WatchStateLock //rs! Port.WatchLock = IOSimpleLockAlloc(); Port.WatchLock = IOLockAlloc(); if (!Port.WatchLock) return false; //rs! IOSimpleLockInit(Port.WatchLock); // init lock used to protect code on MP //rs if (!(Port.serialRequestLock = IOLockAlloc())) if (!(Port.serialRequestLock = IORecursiveLockAlloc())) return false; //rs! IOSimpleLockInit(Port.serialRequestLock); //init the Port structure SetStructureDefaults(&Port, true); Port.Instance = Port.whichPort; // Get chip access addresses if (!(map = provider->mapDeviceMemoryWithIndex(0))) return false; // Port.Base = map->getPhysicalAddress(); Port.Base = map->getVirtualAddress(); Port.ChipBaseAddress = Port.Base; // Calls the DMA function that knows how to // handle all the different hardware: SccSetDMARegisters(&Port, provider); if ((Port.TxDBDMAChannel.dmaChannelAddress == NULL) || (Port.TxDBDMAChannel.dmaBase == NULL) || (Port.RxDBDMAChannel.dmaChannelAddress == NULL) || (Port.RxDBDMAChannel.dmaBase == NULL)) return false; Port.TXStats.BufferSize= BUFFER_SIZE_DEFAULT; Port.RXStats.BufferSize= BUFFER_SIZE_DEFAULT; initChip(&Port); #if USE_WORK_LOOPS // myWorkLoop = (IOWorkLoop *)getWorkLoop(); sccInterruptSource = IOInterruptEventSource::interruptEventSource( this, (IOInterruptEventAction)&AppleSCCSerial::interruptHandler, provider, kIntChipSet); if (!sccInterruptSource) { //IOLog("%s: Failed to create SCC interrupt event source!\n", getName()); return false; } if (myWorkLoop->addEventSource(sccInterruptSource) != kIOReturnSuccess) { //IOLog("%s: Failed to add SCC interrupt event source to work loop!\n", getName()); return false; } #if USE_FILTER_EVENT_SOURCES txDMAInterruptSource = IOFilterInterruptEventSource::filterInterruptEventSource(this, (IOInterruptEventAction) &AppleSCCSerial::interruptHandler, (IOFilterInterruptAction) &AppleSCCSerial::interruptFilter, provider, kIntTxDMA); #else txDMAInterruptSource = IOInterruptEventSource::interruptEventSource( this, (IOInterruptEventAction)&AppleSCCSerial::interruptHandler, provider, kIntTxDMA); #endif if (!txDMAInterruptSource) { //IOLog("%s: Failed to create Tx DMA interrupt event source!\n", getName()); return false; } if (myWorkLoop->addEventSource(txDMAInterruptSource) != kIOReturnSuccess) { //IOLog("%s: Failed to add Tx DMA interrupt event source to work loop!\n", getName()); return false; } #if USE_FILTER_EVENT_SOURCES rxDMAInterruptSource = IOFilterInterruptEventSource::filterInterruptEventSource(this, (IOInterruptEventAction) &AppleSCCSerial::interruptHandler, (IOFilterInterruptAction) &AppleSCCSerial::interruptFilter, provider, kIntRxDMA); #else rxDMAInterruptSource = IOInterruptEventSource::interruptEventSource( this, (IOInterruptEventAction)&AppleSCCSerial::interruptHandler, provider, kIntRxDMA); #endif if (!rxDMAInterruptSource) { //IOLog("%s: Failed to create Rx DMA interrupt event source!\n", getName()); return false; } if (myWorkLoop->addEventSource(rxDMAInterruptSource) != kIOReturnSuccess) { //IOLog("%s: Failed to add Rx DMA interrupt event source to work loop!\n", getName()); return false; } // get a timer and set our timeout handler to be called when it fires portPtr()->rxTimer = IOTimerEventSource::timerEventSource( this, rxTimeoutHandler ); // make sure we got a timer if( !portPtr()->rxTimer ) { //IOLog( "%s: Failed to create timer event source\n", getName() ); return false; } // add the timer to the workloop if(!myWorkLoop || (myWorkLoop->addEventSource(portPtr()->rxTimer) != kIOReturnSuccess) ) { //IOLog( "%s: Failed to add timer event source to workloop\n", getName() ); return false; } if (OSCompareAndSwap(1,0,&gTimerCanceled) ) //Set gTimerCanceled to zero IOLog("%s: gTimerCanceled set to Zero\n", getName()); #else // Register all the interrupts. We need the chip interrupts to handle the // errors and the DMA ... well to transfer the data obviously. if (provider->registerInterrupt(kIntChipSet, this, handleInterrupt, 0) != kIOReturnSuccess) { return false; } if (provider->registerInterrupt(kIntTxDMA, this, handleDBDMATxInterrupt, 0) != kIOReturnSuccess) { DLOG("AppleSCCSerial: Didn't register kIntTxDMA\n"); return false; } if (provider->registerInterrupt(kIntRxDMA, this, handleDBDMARxInterrupt, 0) != kIOReturnSuccess) { DLOG("AppleSCCSerial: Didn't register kIntRxDMA\n"); return false; } #endif // enables the hardware (important for powerbooks and core 99) callPlatformFunction("EnableSCC", false, (void *)true, 0, 0, 0); fPollingThread = thread_call_allocate ( &AppleSCCSerial::callCarrierhack, ( thread_call_param_t ) this); if ( fPollingThread == NULL ) { return false; } // Finished all initialisation so start service matching return createSerialStream(); } #if USE_TIMER_EVENT_SOURCE_DEBUGGING // this function is called when the timer fires void AppleSCCSerial::timeoutHandler(OSObject *owner, IOTimerEventSource *sender) { AppleSCCSerial* serialPortPtr; // make sure that the owner of the timer is us serialPortPtr = OSDynamicCast( AppleSCCSerial, owner ); if( serialPortPtr ) // it's us { // increment the counter serialPortPtr->counter++; // indicate we were called // print the counter as well //IOLog( "%s: In timeoutHandler (%ld)\n", serialPortPtr->getName(), serialPortPtr->counter ); //let's display the entire DBDMA Command Struct HandleRxIntTimeout(serialPortPtr->portPtr()); // reset the timer for 1 seconds sender->setTimeoutMS( kTimerTimeout ); } } #endif void AppleSCCSerial::stop(IOService *provider) { //rcs we now move the Deallocation stuff here... // Disables the dma channles SccFreeReceptionChannel(portPtr()); SccFreeTansmissionChannel(portPtr()); #if USE_WORK_LOOPS if (txDMAInterruptSource) { //txDMAInterruptSource->disable(); myWorkLoop->removeEventSource(txDMAInterruptSource); txDMAInterruptSource->release(); txDMAInterruptSource = 0; } if (rxDMAInterruptSource) { //rxDMAInterruptSource->disable(); myWorkLoop->removeEventSource(rxDMAInterruptSource); rxDMAInterruptSource->release(); rxDMAInterruptSource = 0; } if (sccInterruptSource) { //sccInterruptSource->disable(); myWorkLoop->removeEventSource(sccInterruptSource); sccInterruptSource->release(); sccInterruptSource = 0; } if( portPtr()->rxTimer ) { if (OSCompareAndSwap(1,0,&gTimerCanceled) ) //Set gTimerCanceled to zero OSIncrementAtomic((SInt32 *) &gTimerCanceled); //Set the timer cancelled flag portPtr()->rxTimer->cancelTimeout(); // stop the timer portPtr()->rxTimer->disable(); // stop the timer myWorkLoop->removeEventSource( portPtr()->rxTimer ); // remove the timer from the workloop portPtr()->rxTimer->release(); // release the timer portPtr()->rxTimer = NULL; // } #else // And unregisters them ... provider->unregisterInterrupt(kIntChipSet); provider->unregisterInterrupt(kIntTxDMA); provider->unregisterInterrupt(kIntRxDMA); #endif #if USE_TIMER_EVENT_SOURCE_DEBUGGING if( myTimer ) { myTimer->cancelTimeout(); // stop the timer myWorkLoop->removeEventSource( myTimer ); // remove the timer from the workloop myTimer->release(); // release the timer myTimer = NULL; // } #endif // deactivates the port deactivatePort(&Port); if ( fPollingThread != NULL) { while (fCarrierHackCount > 0) { if (thread_call_cancel(fPollingThread)) break; else IOSleep(1); } thread_call_free ( fPollingThread ); fPollingThread = NULL; } /* Turn the chip off after closing a port */ if (Port.ControlRegister != NULL) SccCloseChannel(&Port); if (Port.FrameTOEntry != NULL) { //calloutEntryRemove(Port.FrameTOEntry); //calloutEntryFree(Port.FrameTOEntry); } if (Port.DataLatTOEntry != NULL) { //calloutEntryRemove(Port.DataLatTOEntry); //calloutEntryFree(Port.DataLatTOEntry); } if (Port.DelayTOEntry != NULL) { //calloutEntryRemove(Port.DelayTOEntry); //calloutEntryFree(Port.DelayTOEntry); } if (Port.HeartBeatTOEntry != NULL) { //calloutEntryRemove(Port.HeartBeatTOEntry); //calloutEntryFree(Port.HeartBeatTOEntry); } /* Begin fixed bug # 2550140 & 2553750 */ IOLockFree (Port.IODBDMARxLock); IOLockFree (Port.IODBDMATrLock); /* End fixed bug # 2550140 & 2553750 */ // IOLockFree (Port.SCCWriteLock); IOLockFree (Port.SCCAccessLock); // IOSimpleLockFree (Port.WatchLock); //hsjb 5/4/01, this was missing in SU2 IOLockFree (Port.WatchLock); //hsjb 5/4/01, this was missing in SU2 //rs 7/29/02 IOLockFree (Port.serialRequestLock); IORecursiveLockFree (Port.serialRequestLock); //hsjb 5/4/01, this was missing in SU2 super::stop(provider); } void AppleSCCSerial::setPortName(IOService *provider) { IOService *topProvider = NULL; IOService *myProvider; OSData *s; // if the argument is missing I've to get from myself: if (provider == NULL) provider = getProvider(); // We need to keep track of who is our provider: myProvider = provider; // This is the way it was encoded in the old machines: s = OSDynamicCast(OSData, myProvider->getProperty("AAPL,connector")); if (s) { // we are going to use it as it is char *tmpName = (char *) s->getBytesNoCopy(); setProperty(kIOTTYBaseNameKey, tmpName); // and exits: return; } // See if we find the top of the tree (with the machine type) // iterating all the way up: while (provider) { topProvider = provider; provider = topProvider->getProvider(); } // If we are here we did not find a name, so let's try the method used // on the newer hardware: s = OSDynamicCast(OSData, myProvider->getProperty("slot-names")); if (s != NULL) { UInt32 *nWords = (UInt32*)s->getBytesNoCopy(); // If there is more than one entry if (*nWords > 0) { char *tmpPtr, *tmpName; tmpName = (char *) s->getBytesNoCopy() + sizeof(UInt32); // To make parsing easy, sets the sting in low case: for (tmpPtr = tmpName; *tmpPtr != 0; tmpPtr++) *tmpPtr |= 32; // and sets the property if (IODTMatchNubWithKeys(myProvider, "'ch-b'")) { if (topProvider != NULL) { if (IODTMatchNubWithKeys(topProvider, "'PowerMac1,1'")) // B&W G3 { setProperty(kIOTTYBaseNameKey, "none"); return; } } } setProperty(kIOTTYBaseNameKey, tmpName); // and exits: return; } } // Again nothing. At this point we can be missing the name or we may // just have the modem disconnected. If it is an old machine we give // to the port the same name of the scc channel: if (topProvider != NULL) { if (IODTMatchNubWithKeys(topProvider, "'AAPL,9500'") || IODTMatchNubWithKeys(topProvider, "'AAPL,Gossamer'") || IODTMatchNubWithKeys(topProvider, "'AAPL,PowerBook1998'") || IODTMatchNubWithKeys(topProvider, "'AAPL,PowerMac G3'")) { // as last attempt we use the channel name: s = OSDynamicCast(OSData, myProvider->getProperty("name")); if (s != NULL) { // we are going to use it as it is char *tmpName = (char*)s->getBytesNoCopy(); setProperty(kIOTTYBaseNameKey, tmpName); // and exits: return; } } } // ****** Need to add code for irda support for the PowerBooks. // ****** Also need special code for irda support on WallStreet. // In all the other cases (PowerMacX,X and iMac,X) I assume ch-a to be // the modem port and there is no ch-b support. if (IODTMatchNubWithKeys(myProvider, "'ch-a'")) { if (topProvider != NULL) { if (IODTMatchNubWithKeys(topProvider, "'PowerMac1,1'")) // B&W G3 { setProperty(kIOTTYBaseNameKey, "modem"); } else { OSString *tmpName = OSDynamicCast(OSString, getProperty("kSCCDebugKey")); if (tmpName != NULL) setProperty(kIOTTYBaseNameKey, "serial"); else setProperty(kIOTTYBaseNameKey, "none"); } } else setProperty(kIOTTYBaseNameKey, "none"); } else if (IODTMatchNubWithKeys(myProvider, "'ch-b'")) setProperty(kIOTTYBaseNameKey, "none"); } bool AppleSCCSerial::createSerialStream() { AppleSCCRS232SerialStreamSync *rs232Stream; OSObject *dtProperty; bool ret; rs232Stream = new AppleSCCRS232SerialStreamSync; if (!rs232Stream) return false; // Either we attach and should get rid of our reference // or we have fail in which case we should get rid our reference. ret = (rs232Stream->init(0, &Port) && rs232Stream->attach(this)); rs232Stream->release(); if (!ret) return false; // Copy the provider's DeviceTree properties into our nub dtProperty = fProvider->getProperty("device_type"); if (dtProperty) rs232Stream->setProperty("device_type", dtProperty); dtProperty = fProvider->getProperty("compatible"); if (dtProperty) rs232Stream->setProperty("compatible", dtProperty); dtProperty = fProvider->getProperty("slot-names"); if (dtProperty) rs232Stream->setProperty("slot-names", dtProperty); dtProperty = fProvider->getProperty("AAPL,connector"); if (dtProperty) rs232Stream->setProperty("AAPL,connector", dtProperty); // Make sure we copy the PORTNAME up as well. dtProperty = getProperty(gIOTTYBaseNameKey); if (dtProperty) rs232Stream->setProperty(gIOTTYBaseNameKey, dtProperty); rs232Stream->registerService(); return true; } // end of function AppleSCCSerial::createSerialStream void AppleSCCSerial:: handleInterrupt(OSObject *target, void *refCon, IOService *nub, int source) { AppleSCCSerial *serialPortPtr = (AppleSCCSerial*)target; if (serialPortPtr != NULL) { #if ! USE_WORK_LOOPS serialPortPtr->fProvider->disableInterrupt(kIntChipSet); #endif PPCSerialISR(target, refCon, serialPortPtr->portPtr()); #if ! USE_WORK_LOOPS serialPortPtr->fProvider->enableInterrupt(kIntChipSet); #endif } } void AppleSCCSerial:: handleDBDMATxInterrupt(OSObject *target, void *refCon, IOService *nub, int source) { AppleSCCSerial *serialPortPtr = OSDynamicCast(AppleSCCSerial, target); if (serialPortPtr != NULL) { #if ! USE_WORK_LOOPS serialPortPtr->fProvider->disableInterrupt(kIntTxDMA); #endif PPCSerialTxDMAISR(NULL, NULL, serialPortPtr->portPtr()); #if ! USE_WORK_LOOPS serialPortPtr->fProvider->enableInterrupt(kIntTxDMA); #endif } } void AppleSCCSerial:: handleDBDMARxInterrupt(OSObject *target, void *refCon, IOService *nub, int source) { AppleSCCSerial *serialPortPtr = OSDynamicCast(AppleSCCSerial, target); if (serialPortPtr != NULL) { #if USE_TIMER_EVENT_SOURCE_DEBUGGING serialPortPtr->myTimer->cancelTimeout(); //cancel timeout serialPortPtr->myTimer->setTimeoutMS( kTimerTimeout ); //arm a 1 second timer #endif #if ! USE_WORK_LOOPS serialPortPtr->fProvider->disableInterrupt(kIntRxDMA); #endif PPCSerialRxDMAISR(NULL, NULL, serialPortPtr->portPtr()); #if ! USE_WORK_LOOPS serialPortPtr->fProvider->enableInterrupt(kIntRxDMA); #endif } } // ================================================= // New implementation of PortDevice protocol // ================================================= /* 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 IO_R_EXCLUSIVE_ACCESS is returned. */ IOReturn AppleSCCSerial::acquirePort(bool sleep, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; UInt32 busyState = 0; IOReturn rtn = kIOReturnSuccess; if (OSCompareAndSwap(1,0,&gTimerCanceled) ) //Set gTimerCanceled to zero IOLog("%s: gTimerCanceled set to Zero\n", getName()); DLOG("[acquirePort "); for (;;) { busyState = (readPortState(port) & PD_S_ACQUIRED); if (!busyState) { // Set busy bit, and clear everything else changeState(port, PD_S_ACQUIRED | DEFAULT_STATE, STATE_ALL); break; } else if (!sleep) { DLOG("Busy!]"); return kIOReturnExclusiveAccess; } else { busyState = 0; rtn = watchState(&busyState, PD_S_ACQUIRED, 0); if ((rtn == kIOReturnIOError) || (rtn == kIOReturnSuccess)) { continue; } else { DLOG("Interrupted!]"); return rtn; } } } /* We now own the port. */ do { if (!fProvider->open(this)) { rtn = kIOReturnBusy; break; } /* Creates and sets up the ring buffers */ if (!allocateRingBuffer(&(port->TX), port->TXStats.BufferSize) || !allocateRingBuffer(&(port->RX), port->RXStats.BufferSize)) { rtn = kIOReturnNoMemory; break; } // for DCP -- Begin if ((port->whichPort == serialPortA) && (port->gDCPModemFound)) { // IOLog ("AppleSCCSerial::acquirePort -- callPlatFormFunction for Port.DCPModemSupportPtr\n"); port->DCPModemSupportPtr = NULL; void *tmp = NULL; tmp = (void *) callPlatformFunction("GetDCPObject", false, 0, 0, 0, 0); if (tmp != NULL) { // IOLog ("AppleSCCSerial::acquirePort -- Port.DCPModemSupportPtr != NULL\n"); port->gDCPUserClientSet = true; port->DCPModemSupportPtr = (DCPModemSupport *) tmp; } else { port->gDCPUserClientSet = false; // IOLog ("AppleSCCSerial::acquirePort -- Port.DCPModemSupportPtr == NULL\n"); } if ((port->gDCPModemFound) && (port->gDCPUserClientSet)) { port->DCPModemSupportPtr->setDCPBufferSize (port->RXStats.BufferSize); } } // for DCP -- End /* Initialize all the structures */ SetStructureDefaults(port, FALSE); /* OK, Let's actually setup the chip. */ OpenScc(port); // Enables all the DMA transfers: SccSetupReceptionChannel(port); SccdbdmaDefineReceptionCommands(port); SccSetupTansmissionChannel(port); SccdbdmaDefineTansmissionCommands(port); // Enable all the interrupts #if USE_WORK_LOOPS sccInterruptSource->enable(); txDMAInterruptSource->enable(); rxDMAInterruptSource->enable(); #else rtn = fProvider->enableInterrupt(kIntChipSet); if (rtn != kIOReturnSuccess) break; rtn = fProvider->enableInterrupt(kIntTxDMA); if (rtn != kIOReturnSuccess) { // IOLog("AppleSCCSerial: Didn't enable kIntTxDMA interrupt\n"); break; } rtn = fProvider->enableInterrupt(kIntRxDMA); if (rtn != kIOReturnSuccess) { // IOLog ("AppleSCCSerial: Didn't enable kIntRxDMA interrupt\n"); break; } #endif // also begins to monitor the channel SccdbdmaStartReception(port); DLOG("Early]\n"); return kIOReturnSuccess; } while (0); // We failed for some reason freeRingBuffer(&(port->TX)); freeRingBuffer(&(port->RX)); fProvider->close(this); changeState(port, 0, STATE_ALL); // Clear the entire state word return rtn; } /* release sets the state of the port object to available and wakes up and *threads sleeping for access to this port. It will return IO_R_SUCCESS *if the port was in a busy state, and IO_R_NOT_OPEN if it was available. */ IOReturn AppleSCCSerial::releasePort(void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; UInt32 busyState = 0; int i; OSIncrementAtomic((SInt32 *) &gTimerCanceled); //Set the timer cancelled flag DLOG("[release "); busyState = (readPortState(port) & PD_S_ACQUIRED); if (!busyState) { DLOG("NOT OPEN]"); return kIOReturnNotOpen; } // for DCP -- Begin if (port->whichPort == serialPortA) { // IOLog ("AppleSCCSerial::releasePort -- For serial port A\n"); if (port->gDCPModemFound) { // IOLog ("AppleSCCSerial::releasePort -- calling DCPModemSupportFunction to quit DCP\n"); if (port->gDCPUserClientSet) Port.DCPModemSupportPtr->callDCPModemSupportFunctions (DCPFunction_QuitDCP, NULL, NULL, NULL); /* else IOLog ("AppleSCCSerial::releasePort -- Cannot call DCPModemSupportFunction due to DCPUserClient is not set\n"); } else { IOLog ("AppleSCCSerial::releasePort -- Not a DCP modem\n"); */ } } /* else { IOLog ("AppleSCCSerial::releasePort -- Not for serial port A\n"); } */ // for DCP -- End //calloutEntryRemove(port->HeartBeatTOEntry); //calloutEntryRemove(port->DelayTOEntry); //calloutEntryRemove(port->DataLatTOEntry); //HuL*4calloutEntryRemove(port->FrameTOEntry); //IOExitThread(port->HeartBeatTOEntry); //IOExitThread(port->DelayTOEntry); //IOExitThread(port->DataLatTOEntry); //IOExitThread(port->FrameTOEntry); // default everything for (i=0; i<(256>>SPECIAL_SHIFT); i++) port->SWspecial[i] = 0; port->CharLength = 8; port->BreakLength = 1; port->XONchar = '\x11'; port->XOFFchar = '\x13'; port->StopBits = 1<<1; port->TX_Parity = PD_RS232_PARITY_NONE; port->RX_Parity = PD_RS232_PARITY_DEFAULT; port->RXOstate = IDLE_XO; port->BaudRate = kDefaultBaudRate; port->FlowControl = DEFAULT_NOTIFY; port->FlowControlState = CONTINUE_SEND; port->DCDState = false; fModemObject = 0; deactivatePort(port); // Disables the interrupts: #if USE_WORK_LOOPS sccInterruptSource->disable(); txDMAInterruptSource->disable(); rxDMAInterruptSource->disable(); #else fProvider->disableInterrupt(kIntChipSet); fProvider->disableInterrupt(kIntTxDMA); fProvider->disableInterrupt(kIntRxDMA); #endif // Disables the dma channles /* rcs!!! SccFreeReceptionChannel(port); SccFreeTansmissionChannel(port); */ // Now that we do not get intrrupts anymore we can remove all the // buffers. freeRingBuffer(&(port->TX)); freeRingBuffer(&(port->RX)); DLOG("OK]\n"); fProvider->close(this); changeState(port, 0, STATE_ALL); // Clear the entire state word return kIOReturnSuccess; } /* *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 an *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 AppleSCCSerial::setState(UInt32 state, UInt32 mask, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; DLOG("++>setState %d %x\n",(int)state, (int)mask); if (mask & (PD_S_ACQUIRED | PD_S_ACTIVE | (~EXTERNAL_MASK))) return kIOReturnBadArgument; if ((readPortState(port) & PD_S_ACQUIRED) != 0) { // ignore any bits that are read-only mask &= (~port->FlowControl & PD_RS232_A_MASK) | PD_S_MASK; if (mask != 0) changeState(port, state, mask); DLOG("-->setState\n"); return kIOReturnSuccess; } return kIOReturnNotOpen; } /* *Get the state for the port device. */ UInt32 AppleSCCSerial::getState(void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; CheckQueues(port); return (readPortState(port) & EXTERNAL_MASK); } /* *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. */ IOReturn AppleSCCSerial::watchState(UInt32 *state, UInt32 mask, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; IOReturn ret = kIOReturnNotOpen; DLOG("watchState\n"); if ((readPortState(port) & PD_S_ACQUIRED) != 0) { ret = kIOReturnSuccess; mask &= EXTERNAL_MASK; ret = watchState(port, state, mask); (*state) &= EXTERNAL_MASK; } return ret; } /* nextEvent returns the type of the next event on the RX queue. If no *events are present on the RX queue, then PD_E_EOQ is returned. */ UInt32 AppleSCCSerial::nextEvent(void *refCon) { UInt32 ret = kIOReturnSuccess; DLOG("nextEvent\n"); // PortInfo_t *port = (PortInfo_t *) refCon; // ret=peekEvent(&(port->RX), 0); return ret; } /* executeEvent causes the specified event to be processed immediately. *This is primarily used for channel control commands like START & STOP */ IOReturn AppleSCCSerial::executeEvent(UInt32 event, UInt32 data, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; IOReturn ret = kIOReturnSuccess; UInt32 state, delta; DLOG("executeEvent\n"); if ((readPortState(port) & PD_S_ACQUIRED) != 0) { switch (event) { case PD_E_FLOW_CONTROL: UInt32 tmp; tmp = FLOW_RX_AUTO & (data ^ port->FlowControl); port->FlowControl = data & (CAN_BE_AUTO | CAN_NOTIFY); if (tmp) { if (tmp & PD_RS232_A_RXO) { port->RXOstate = NEEDS_XOFF; // port->RXOstate = IDLE_XO; } //changeState(port, flowMachine(port), FLOW_RX_AUTO); } //#if 0 /* ejk FIXTHIS */ // tmp = FLOW_RX_AUTO & (data ^ port->FlowControl); // port->FlowControl = data & (CAN_BE_AUTO | CAN_NOTIFY); // if (tmp) { // if (tmp & PD_RS232_A_RXO) // port->RXOstate = IDLE_XO; // //changeState(port, flowMachine(port), FLOW_RX_AUTO); // } //#endif // ×××break; break; case PD_E_DELAY: port->CharLatInterval = long2tval(data *1000); break; case PD_E_RXQ_SIZE: #ifdef IntelTest if (readPortState(port) & PD_S_ACTIVE) { ret = IO_R_BUSY; } else { port->RX.Size = validateRingBufferSize(data, &(port->RX)); if (port->RX.HighWater > (port->RX.Size - BIGGEST_EVENT)) port->RX.HighWater = (port->RX.Size - BIGGEST_EVENT); if (port->RX.LowWater > (port->RX.HighWater - BIGGEST_EVENT)) port->RX.LowWater = (port->RX.HighWater - BIGGEST_EVENT); } #endif break; case PD_E_TXQ_SIZE: #ifdef IntelTest if (readPortState(port) & PD_S_ACTIVE) { ret = IO_R_BUSY; } else { port->TX.Size = validateRingBufferSize(data, &(port->RX)); if (port->TX.HighWater > (port->TX.Size - BIGGEST_EVENT)) port->TX.HighWater = (port->TX.Size - BIGGEST_EVENT); if (port->TX.LowWater > (port->TX.HighWater - BIGGEST_EVENT)) port->TX.LowWater = (port->TX.HighWater - BIGGEST_EVENT); } #endif break; default: delta = 0; state = readPortState(port); ret = executeEvent(port, event, data, &state, &delta); changeState(port, state, delta); break; } } return ret; } /* requestEvent processes the specified event as an immediate request and *returns the results in data. This is primarily used for getting link *status information and verifying baud rate and such. * *Author's note: This was one of my favorite routines to code up! */ IOReturn AppleSCCSerial::requestEvent(UInt32 event, UInt32 *data, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; IOReturn returnValue = kIOReturnSuccess; DLOG("call requestEvent %ld\n", event); if (data == NULL) returnValue = kIOReturnBadArgument; else { switch (event) { case PD_E_ACTIVE : *data = bool(readPortState(port)&PD_S_ACTIVE); break; case PD_E_FLOW_CONTROL : *data = port->FlowControl; break; case PD_E_DELAY : *data = tval2long(port->CharLatInterval)/1000; break; case PD_E_DATA_LATENCY : *data = tval2long(port->DataLatInterval)/1000; break; case PD_E_TXQ_SIZE : *data = GetQueueSize(&(port->TX)); break; case PD_E_RXQ_SIZE : *data = GetQueueSize(&(port->RX)); break; #if 0 case PD_E_TXQ_LOW_WATER : *data = port->TX.LowWater; break; case PD_E_RXQ_LOW_WATER : *data = port->RX.LowWater; break; case PD_E_TXQ_HIGH_WATER : *data = port->TX.HighWater; break; case PD_E_RXQ_HIGH_WATER : *data = port->RX.HighWater; break; #else case PD_E_TXQ_LOW_WATER : case PD_E_RXQ_LOW_WATER : case PD_E_TXQ_HIGH_WATER : case PD_E_RXQ_HIGH_WATER : *data = 0; returnValue = kIOReturnBadArgument; break; #endif case PD_E_TXQ_AVAILABLE : *data = FreeSpaceinQueue(&(port->TX)); break; case PD_E_RXQ_AVAILABLE : *data = UsedSpaceinQueue(&(port->RX)); break; case PD_E_DATA_RATE : *data = port->BaudRate << 1; break; case PD_E_RX_DATA_RATE : *data = 0x00; break; case PD_E_DATA_SIZE : *data = port->CharLength << 1; break; case PD_E_RX_DATA_SIZE : *data = 0x00; break; case PD_E_DATA_INTEGRITY : *data = port->TX_Parity; break; case PD_E_RX_DATA_INTEGRITY : *data = port->RX_Parity; break; case PD_RS232_E_STOP_BITS : *data = port->StopBits << 1; break; case PD_RS232_E_RX_STOP_BITS : *data = 0x00; break; case PD_RS232_E_XON_BYTE : *data = port->XONchar; break; case PD_RS232_E_XOFF_BYTE : *data = port->XOFFchar; break; case PD_RS232_E_LINE_BREAK : *data = bool(readPortState(port)&PD_RS232_S_BRK); break; case PD_RS232_E_MIN_LATENCY : *data = bool(port->MinLatency); break; default : returnValue = kIOReturnBadArgument; break; } } DLOG("end requestEvent %ld\n", event); return kIOReturnSuccess; } /* enqueueEvent will place the specified event into the TX queue. The *sleep argument allows the caller to specify the enqueueEvent's *behaviour when the TX queue is full. If sleep is true, then this *method will sleep until the event is enqueued. If sleep is false, *then enqueueEvent will immediatly return IO_R_RESOURCE. */ IOReturn AppleSCCSerial:: enqueueEvent(UInt32 event, UInt32 data, bool sleep, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; DLOG("enqueueEvent (write)\n"); if (readPortState(port) & PD_S_ACTIVE != 0) { // rtn = TX_enqueueEvent(port, event, data, sleep); #ifdef IntelTest if ((rtn == kIOReturnSuccess) && (!((readPortState(port))&PD_S_TX_BUSY))) calloutEntryDispatch(port->FrameTOEntry); // restart TX engine #endif return kIOReturnSuccess; } return kIOReturnNotOpen; } /* dequeueEvent will remove the oldest event from the RX queue and return *it in event & data. The sleep argument defines the behavior if the RX *queue is empty. If sleep is true, then this method will sleep until an *event is available. If sleep is false, then an PD_E_EOQ event will be *returned. In either case IO_R_SUCCESS is returned. */ IOReturn AppleSCCSerial:: dequeueEvent(UInt32 *event, UInt32 *data, bool sleep, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; DLOG("dequeueEvent (read)\n"); if ((event == NULL) || (data == NULL)) return kIOReturnBadArgument; if (readPortState(port) & PD_S_ACTIVE !=0) { // rtn = RX_dequeueEvent(port, &e, data, sleep); // *event = (UInt32)e; return kIOReturnSuccess; } return kIOReturnNotOpen; } /* 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 transferCount. 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 IO_R_INVALID_ARG, indicating one or more arguments *were not valid. Other possible return values are IO_R_SUCCESS if all *requirements were met; IO_R_IPC_FAILURE if sleep was interrupted by *a signal; IO_R_IO if the port was deactivated. */ IOReturn AppleSCCSerial:: enqueueData(UInt8 *buffer, UInt32 size, UInt32 *count, bool sleep, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; UInt32 state = PD_S_TXQ_LOW_WATER; IOReturn rtn = kIOReturnSuccess; DLOG("++>In Enqueue %d %d sleep-%d\n", size, *count, sleep); if (count == NULL || buffer == NULL) return kIOReturnBadArgument; (*count) = 0; if (!(readPortState(port) & PD_S_ACTIVE)) { return kIOReturnNotOpen; } #ifdef TRACE /* The debug code consists of the following * *---------------------------------------------------------------------- *| | | |Func | *| Class (8) | SubClass (8) | Code (14) |Qual(2)| *---------------------------------------------------------------------- *Class = drivers = 0x06 *Sub Class = serial = 0x08 *Code = func ID = 1 *FuncQulif: it is *DBG_FUNC_START 1 *DBG_FUNC_END 2 *DBG_FUNC_NONE 0 *0x06080005 *how to trace: *trace -i enables the tracing and sets up the kernel buffer for tracing *trace -g shows the trace setup. *trace -e start tracing (but I use trace -e -c 6 -s 8 to trace only the calls in the * serial driver. *trace -d stop the tracer. *trace -t codefile >result dumps the content of the trace buffer in the file "result" */ KERNEL_DEBUG_CONSTANT((DRVDBG_CODE(DBG_DRVSERIAL, 2)) | DBG_FUNC_START, size, sleep, 0, 0, 0); //0x06080009 #endif /* OK, go ahead and try to add something to the buffer. */ *count = AddtoQueue(&(port->TX), buffer, size); CheckQueues(port); if (port->FlowControlState == PAUSE_SEND) { return kIOReturnSuccess; } /* 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 until it's all out then sleep. */ while ((*count < size) && sleep) { state = PD_S_TXQ_LOW_WATER; rtn = watchState(&state, PD_S_TXQ_LOW_WATER, 0); /* if ((rtn == IO_R_IO) || (rtn == kIOReturnSuccess)) */ if (rtn != kIOReturnSuccess) { DLOG("Interrupted!]"); #ifdef TRACE KERNEL_DEBUG_CONSTANT((DRVDBG_CODE(DBG_DRVSERIAL, 2)) | DBG_FUNC_END, size, sleep, *count, -1, 0); //0x0608000A #endif return rtn; } DLOG("Adding More to Queue %d\n", size - *count); *count += AddtoQueue(&(port->TX), buffer + *count, size - *count); CheckQueues(port); /* Let the tranmitter know that we have something ready to go. */ SetUpTransmit(port); } DLOG("Enqueue Check %x\n",(int)UsedSpaceinQueue(&(port->TX))); DLOG("chars sent = %d of %d\n", *count,size); DLOG("-->Out Enqueue\n"); #ifdef TRACE KERNEL_DEBUG_CONSTANT((DRVDBG_CODE(DBG_DRVSERIAL, 2)) | DBG_FUNC_END, size, sleep, *count, -2, 0); //0x0608000A #endif return kIOReturnSuccess; } /* 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 transferCount. * *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 minCount characters have been received or a non *data event is next in the RX queue. If minCount is zero, then this *method never sleeps and will return immediatly if the queue is empty. * *Note that the caller should ALWAYS check the transferCount unless the *return value was IO_R_INVALID_ARG, indicating one or more arguments *were not valid. Other possible return values are IO_R_SUCCESS if all *requirements were met; IO_R_IPC_FAILURE if sleep was interrupted by *a signal; IO_R_IO if the port was deactivated. */ IOReturn AppleSCCSerial:: dequeueData(UInt8 *buffer, UInt32 size, UInt32 *count, UInt32 min, void *refCon) { PortInfo_t *port = (PortInfo_t *) refCon; IOReturn rtn = kIOReturnSuccess; UInt32 state = 0; /* Set up interrupt variable.*/ DLOG("++>In Dequeue buf %x bufSize %d txCount %d minCount %d\n",(int)buffer, size, *count, min); /* Check to make sure we have good arguments. */ if ((count == NULL) || (buffer == NULL) || (min > size)) return kIOReturnBadArgument; /* If the port is not active then there should not be any chars. */ *count = 0; if (!(readPortState(port) & PD_S_ACTIVE)) return(kIOReturnNotOpen); #ifdef TRACE /* The debug code consists of the following * *---------------------------------------------------------------------- *| | | |Func | *| Class (8) | SubClass (8) | Code (14) |Qual(2)| *---------------------------------------------------------------------- *Class = drivers = 0x06 *Sub Class = serial = 0x08 *Code = func ID = 1 *FuncQulif: it is *DBG_FUNC_START 1 *DBG_FUNC_END 2 *DBG_FUNC_NONE 0 *0x06080005 *how to trace: *trace -i enables the tracing and sets up the kernel buffer for tracing *trace -g shows the trace setup. *trace -e start tracing (but I use trace -e -c 6 -s 8 to trace only the calls in the * serial driver). *trace -d stop the tracer. *trace -t codefile >result dumps the content of the trace buffer in the file "result" */ KERNEL_DEBUG_CONSTANT((DRVDBG_CODE(DBG_DRVSERIAL, 1)) | DBG_FUNC_START, size, min, 0, 0, 0); //0x06080005 #endif /* Get any data living in the queue. */ *count = RemovefromQueue(&(port->RX), buffer, size); CheckQueues(port); DLOG("-->chars receivedA = %d of needed %d\n", *count, size); #if 1 /*EJK REMOVE THIS*/ while ((min > 0) && (*count < min)) { //#define CHAR_DELAY #ifdef CHAR_DELAY UInt32 bitInterval = 1000000L/port->BaudRate; // microseconds UInt32 delay = (100L*bitInterval)/1000L; // numChars*numBits*bitInterval / 1000 IOSleep(delay); #endif /* Figure out how many bytes we have left to queue up */ state = 0; rtn = watchState(port, &state, PD_S_RXQ_EMPTY); // Test This if ((rtn == IO_R_IO) || (rtn == kIOReturnSuccess)) { if (rtn == kIOReturnSuccess) { //this may cause the queues to get corrupted so leaving ints enabled for now //IOSimpleLockUnlockEnableInterrupt(port->serialRequestLock, previousInterruptState); } else { DLOG("Interrupted!]"); #ifdef TRACE KERNEL_DEBUG_CONSTANT((DRVDBG_CODE(DBG_DRVSERIAL, 1)) | DBG_FUNC_END, size, min, *count, -1, 0); //0x06080006 #endif return rtn; } /* Try and get more data starting from where we left off */ *count += RemovefromQueue(&(port->RX), buffer + *count, (size - *count)); CheckQueues(port); DLOG("-->chars receivedB = %d of needed %d\n", *count, size); } #endif /* Now let's check our receive buffer to see if we need to stop */ // bool goXOIdle = (UsedSpaceinQueue(&(port->RX)) < port->RXStats.LowWater) && (port->RXOstate == SENT_XOFF); // if (goXOIdle) { // port->RXOstate = IDLE_XO; // AddBytetoQueue(&(port->TX), port->XOFFchar); // SetUpTransmit(port); // } //DLOG("-->chars received = %d of needed %d\n", *count, size); // DLOG("-->Out Dequeue\n"); #ifdef TRACE KERNEL_DEBUG_CONSTANT((DRVDBG_CODE(DBG_DRVSERIAL, 1)) | DBG_FUNC_END, size, min, *count, -2, 0); //0x06080006 #endif return rtn; } /* _______________________End Protocol________________________ */ /*--------------------------------------------------------------------- * activatePort *ejk Sat, Dec 27, 1997 16:47 * Comment Me * *-------------------------------------------------------------------*/ IOReturn AppleSCCSerial::activatePort(PortInfo_t *port) { DLOG(" activatePort\n"); if (readPortState(port) & PD_S_ACTIVE) return kIOReturnSuccess; /* if (!allocateRingBuffer(&(port->TX), port->TXStats.BufferSize)) { freeRingBuffer(&(port->TX)); return kIOReturnNoResources; } if (!allocateRingBuffer(&(port->RX) , port->RXStats.BufferSize)) { freeRingBuffer(&(port->TX)); return kIOReturnNoResources; } */ SetStructureDefaults(port, FALSE); //CirQueue *aQueue = NULL; //InitQueue(aQueue, (UInt8*)&port->TX, kMaxCirBufferSize); //InitQueue(aQueue, (UInt8*)&port->RX, kMaxCirBufferSize); // Clear stats // programChip(port); #ifdef IntelTest if ((port->Type) > NS16550) outb(FIFO_CTRL, ((port->FCRimage) | TX_FIFO_RESET | RX_FIFO_RESET)); #endif changeState(port, PD_S_ACTIVE, PD_S_ACTIVE); // activate port //changeState(port, flowMachine(port), FLOW_RX_AUTO); // set up HW Flow Control //changeState(port, generateTXQState(port) | generateRXQState(port), // PD_S_TXQ_MASK | PD_S_RXQ_MASK | FLOW_RX_AUTO); #ifdef IntelTest if (!(readPortState(port) & PD_S_TX_BUSY)) // restart TX engine if idle calloutEntryDispatch(port->FrameTOEntry); outb(IRQ_ENABLE, (port->IERmask) & (RX_DATA_AVAIL_IRQ_EN | TX_DATA_EMPTY_IRQ_EN | LINE_STAT_IRQ_EN | MODEM_STAT_IRQ_EN)); #endif DLOG("End Act State %x\n", (int)readPortState(port)); return kIOReturnSuccess; } /*--------------------------------------------------------------------- * deactivatePort *ejk Sat, Dec 27, 1997 16:48 * Comment Me * *-------------------------------------------------------------------*/ void AppleSCCSerial::deactivatePort(PortInfo_t *port) { DLOG("deactivatePort"); if (readPortState(port) & PD_S_ACTIVE) { // FIXTHIS outb(IRQ_ENABLE, (port->IERmask) & MODEM_STAT_IRQ_EN); // Clear active and wakeup all sleepers changeState(port, 0, PD_S_ACTIVE); //freeRingBuffer(&(port->TX)); //freeRingBuffer(&(port->RX)); //changeState(port, flowMachine(port), FLOW_RX_AUTO); /* Tell the chip to turn itself off.*/ SccCloseChannel(port); //CloseQueue(&port->TX); //CloseQueue(&port->RX); #ifdef DEBUG DLOG("%s: Ints=%ld TXInts=%ld RXInts=%ld mdmInts=%ld\n", port->PortName, port->Stats.ints, port->Stats.txInts, port->Stats.rxInts, port->Stats.mdmInts); DLOG("%s: txChars=%ld rxChars=%ld\n", port->PortName, port->Stats.txChars, port->Stats.rxChars); #endif } } /* Below this line added by Elias */ void AppleSCCSerial::CheckQueues(PortInfo_t *port) { UInt32 Used; UInt32 Free; UInt32 OldState; UInt32 DeltaState; // This lock is the fix for 2977847. The PD_S_TX_BUSY flag was getting cleared during // the execution of this routine by tx interrupt code, and but then this routine was // incorrectly restoring OldState (and the PD_S_TX_BUSY bit) at exit. IORecursiveLockLock(port->serialRequestLock); OldState = readPortState(port); /* Check to see if there is anything in the Transmit buffer. */ Used = UsedSpaceinQueue(&(port->TX)); Free = FreeSpaceinQueue(&(port->TX)); if (Free == 0) { OldState |= PD_S_TXQ_FULL; OldState &= ~PD_S_TXQ_EMPTY; } else if (Used == 0) { OldState &= ~PD_S_TXQ_FULL; OldState |= PD_S_TXQ_EMPTY; } else { OldState &= ~PD_S_TXQ_FULL; OldState &= ~PD_S_TXQ_EMPTY; } /* Check to see if we are below the low water mark. */ if (Used < port->TXStats.LowWater) OldState |= PD_S_TXQ_LOW_WATER; else OldState &= ~PD_S_TXQ_LOW_WATER; if (Used > port->TXStats.HighWater) OldState |= PD_S_TXQ_HIGH_WATER; else OldState &= ~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) { OldState |= PD_S_RXQ_FULL; OldState &= ~PD_S_RXQ_EMPTY; } else if (Used == 0) { OldState &= ~PD_S_RXQ_FULL; OldState |= PD_S_RXQ_EMPTY; } else { OldState &= ~PD_S_RXQ_FULL; OldState &= ~PD_S_RXQ_EMPTY; } //**************************************************/ // Begin software flow control code //**************************************************/ UInt32 SW_FlowControl = port->FlowControl & PD_RS232_A_RXO; UInt32 CTS_FlowControl = port->FlowControl & PD_RS232_A_CTS; UInt32 DTR_FlowControl = port->FlowControl & PD_RS232_A_DTR; //**************************************************/ // End software flow control code //**************************************************/ /* Check to see if we are below the low water mark. */ if (Used < port->RXStats.LowWater) { //**************************************************/ // Begin software flow control code //**************************************************/ if ((SW_FlowControl) && (port->xOffSent)) { port->xOffSent = false; AddBytetoQueue(&(port->TX), port->XONchar); SetUpTransmit(port); } //**************************************************/ // End software flow control code //**************************************************/ OldState |= PD_S_RXQ_LOW_WATER; } else OldState &= ~PD_S_RXQ_LOW_WATER; /* Check to see if we are above the high water mark. */ if (Used > port->RXStats.HighWater) { //**************************************************/ // Begin software flow control code //**************************************************/ if ((SW_FlowControl) && (!port->xOffSent)) { port->xOffSent = true; AddBytetoQueue(&(port->TX), port->XOFFchar); SetUpTransmit(port); } if (CTS_FlowControl) { // Need to set a software overrun error flag } if (DTR_FlowControl && port->DTRAsserted) { DLOG("Deassert DTR\n"); port->DTRAsserted = false; SccSetDTR(port, false); } //**************************************************/ // End software flow control code //**************************************************/ } else { if (DTR_FlowControl && !port->DTRAsserted) { DLOG("Assert DTR\n"); port->DTRAsserted = true; SccSetDTR(port, true); } OldState &= ~PD_S_RXQ_HIGH_WATER; if (port->aboveRxHighWater) { port->aboveRxHighWater = false; } } /* Figure out what has changed to get mask.*/ DeltaState = OldState ^ readPortState(port); changeState(port, OldState, DeltaState); IORecursiveLockUnlock(port->serialRequestLock); } /*--------------------------------------------------------------------- * SetStructureDefaults *ejk Mon, Jan 12, 1998 11:22 PM * Comment Me * *-------------------------------------------------------------------*/ void AppleSCCSerial::SetStructureDefaults(PortInfo_t *port, bool Init) { UInt32 tmp; /* These are set up at probe and connot get reset during execution. */ if (Init) { //SccSetupTansmissionChannel SerialDBDMAStatusInfo *dmaInfo = &port->TxDBDMAChannel; /* creates the transmit buffer */ dmaInfo->dmaTransferSize = 0; dmaInfo->dmaTransferBufferMDP = IOBufferMemoryDescriptor::withOptions(0, PAGE_SIZE, PAGE_SIZE); if (dmaInfo->dmaTransferBufferMDP == NULL) return; dmaInfo->dmaTransferBufferMDP->prepare(); dmaInfo->dmaTransferBuffer = (UInt8 *)dmaInfo->dmaTransferBufferMDP->getBytesNoCopy(); bzero(dmaInfo->dmaTransferBuffer, PAGE_SIZE); // dmaInfo->dmaTransferBuffer = (UInt8*)IOMallocContiguous(PAGE_SIZE, PAGE_SIZE,NULL); // if (dmaInfo->dmaTransferBuffer == NULL) { // return; // } // else // bzero(dmaInfo->dmaTransferBuffer , PAGE_SIZE); dmaInfo->dmaNumberOfDescriptors = 2; dmaInfo->dmaChannelCommandAreaMDP = IOBufferMemoryDescriptor::withOptions(0, PAGE_SIZE, PAGE_SIZE); if (dmaInfo->dmaChannelCommandAreaMDP == NULL) { dmaInfo->dmaNumberOfDescriptors = 0; return; } dmaInfo->dmaChannelCommandAreaMDP->prepare(); dmaInfo->dmaChannelCommandArea = (IODBDMADescriptor *)dmaInfo->dmaChannelCommandAreaMDP->getBytesNoCopy(); bzero(dmaInfo->dmaChannelCommandArea, sizeof(IODBDMADescriptor) * dmaInfo->dmaNumberOfDescriptors); // dmaInfo->dmaChannelCommandArea = (IODBDMADescriptor *)IOMallocContiguous(PAGE_SIZE, PAGE_SIZE,NULL); // if (dmaInfo->dmaChannelCommandArea == NULL) { // dmaInfo->dmaNumberOfDescriptors = 0; // return; // } // else // bzero(dmaInfo->dmaChannelCommandArea , sizeof(IODBDMADescriptor) * dmaInfo->dmaNumberOfDescriptors); //SccSetupReceptionChannel dmaInfo = &port->RxDBDMAChannel; dmaInfo->dmaNumberOfDescriptors = PAGE_SIZE / sizeof(IODBDMADescriptor); dmaInfo->dmaChannelCommandAreaMDP = IOBufferMemoryDescriptor::withOptions(0, sizeof(IODBDMADescriptor) * dmaInfo->dmaNumberOfDescriptors, PAGE_SIZE); if (dmaInfo->dmaChannelCommandAreaMDP == NULL) { dmaInfo->dmaNumberOfDescriptors = 0; return; } dmaInfo->dmaChannelCommandAreaMDP->prepare(); dmaInfo->dmaChannelCommandArea = (IODBDMADescriptor *)dmaInfo->dmaChannelCommandAreaMDP->getBytesNoCopy(); bzero(dmaInfo->dmaChannelCommandArea, sizeof(IODBDMADescriptor) * dmaInfo->dmaNumberOfDescriptors); // dmaInfo->dmaChannelCommandArea = (IODBDMADescriptor *)IOMallocContiguous(sizeof(IODBDMADescriptor) * dmaInfo->dmaNumberOfDescriptors, PAGE_SIZE, NULL); //IOLog("Number of Descriptors: %d. Total Size: %d \n",dmaInfo->dmaNumberOfDescriptors, (sizeof(IODBDMADescriptor) * dmaInfo->dmaNumberOfDescriptors)); //IOLog("Size of IODBDMADescriptor: %d. \n",sizeof(IODBDMADescriptor)); // if (dmaInfo->dmaChannelCommandArea == NULL) { // dmaInfo->dmaNumberOfDescriptors = 0; // return; // } // else // bzero(dmaInfo->dmaChannelCommandArea , sizeof(IODBDMADescriptor) * dmaInfo->dmaNumberOfDescriptors); /* creates the receive buffer, since each command transfers one byte, we need as many bytes as commands */ dmaInfo->dmaTransferSize = dmaInfo->dmaNumberOfDescriptors; dmaInfo->dmaTransferBufferMDP = IOBufferMemoryDescriptor::withOptions(0, dmaInfo->dmaNumberOfDescriptors, PAGE_SIZE); if (dmaInfo->dmaTransferBufferMDP == NULL) { return; } dmaInfo->dmaTransferBufferMDP->prepare(); dmaInfo->dmaTransferBuffer = (UInt8 *)dmaInfo->dmaTransferBufferMDP->getBytesNoCopy(); bzero(dmaInfo->dmaTransferBuffer, dmaInfo->dmaNumberOfDescriptors); // dmaInfo->dmaTransferBuffer = (UInt8*)IOMallocContiguous(dmaInfo->dmaNumberOfDescriptors, PAGE_SIZE,NULL); // if (dmaInfo->dmaTransferBuffer == NULL) { // return; // } // else // bzero(dmaInfo->dmaTransferBuffer , PAGE_SIZE); port->Instance = 0; port->PortName = NULL; port->MasterClock = CHIP_CLOCK_DEFAULT; port->DLRimage = 0x0000; port->LCRimage = 0x00; port->FCRimage = 0x00; port->IERmask = 0x00; port->RBRmask = 0x00; port->Base = 0x0000; port->DataLatInterval.tv_sec = 0; port->DataLatInterval.tv_nsec = 0; port->CharLatInterval.tv_sec = 0; port->CharLatInterval.tv_nsec = 0; port->HeartBeatInterval.tv_sec = 0; port->HeartBeatInterval.tv_nsec = 0; port->State = (PD_S_TXQ_EMPTY | PD_S_TXQ_LOW_WATER | PD_S_RXQ_EMPTY | PD_S_RXQ_LOW_WATER); port->WatchStateMask = 0x00000000; } port->BreakLength = 0; port->BaudRate = 0; port->CharLength = 0; port->BreakLength = 0; port->StopBits = 0; port->TX_Parity = 0; port->RX_Parity = 0; port->WaitingForTXIdle = false; port->MinLatency = false; port->XONchar = '\x11'; port->XOFFchar = '\x13'; port->FlowControl = 0x00000000; port->FlowControlState = CONTINUE_SEND; port->RXOstate = IDLE_XO; port->TXOstate = IDLE_XO; port->FrameTOEntry = NULL; port->DataLatTOEntry = NULL; port->FrameInterval.tv_sec = 0; port->FrameInterval.tv_nsec = 0; port->RXStats.BufferSize = BUFFER_SIZE_DEFAULT; port->RXStats.HighWater = (port->RXStats.BufferSize << 1) / 3; port->RXStats.LowWater = port->RXStats.HighWater >> 1; port->TXStats.BufferSize = BUFFER_SIZE_DEFAULT; port->TXStats.HighWater = (port->RXStats.BufferSize << 1) / 3; port->TXStats.LowWater = port->RXStats.HighWater >> 1; port->FlowControl = DEFAULT_NOTIFY; port->AreTransmitting = FALSE; for (tmp=0; tmp<(256>>SPECIAL_SHIFT); tmp++) port->SWspecial[tmp] = 0; port->Stats.ints = 0; port->Stats.txInts = 0; port->Stats.rxInts = 0; port->Stats.mdmInts = 0; port->Stats.txChars = 0; port->Stats.rxChars = 0; port->DCDState = false; port->lastCTSTime = 0; port->ctsTransitionCount = 0; } /* ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** *This is where the bulk of the chip driver logic lives. It isn't pretty. *This section includes the interrupt handler and a couple callouts *that are used to simulate interrupts: * dataLatTOHandler(port) * frameTOHandler(port) * signalTOHandler(port) ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** ** */ void AppleSCCSerial::dataLatTOHandler(PortInfo_t *port) { DLOG("dataLatTOHandler\n"); //IOSpinlockLock(port->serialRequestLock); //RX_enqueueLongEvent(port, PD_E_DATA_LATENCY, 0); //if (port->RX.Count >= port->RX.Enqueue) //changeState(port, generateRXQState(port), PD_S_RXQ_MASK | FLOW_RX_AUTO); //IOSpinlockUnlock(port->serialRequestLock); } /* *Frame TimeOut: *This timeout is used to simulate an interrupt after the transmitter *shift-register goes idle. Hardware interrupts are only available when *the holding register is empty. When setting the baud rate, or a line *break, it is important to wait for the xmit shift register to finish *its job first. */ void AppleSCCSerial::frameTOHandler(PortInfo_t *port) { DLOG("frameToHandler\n"); //IOSpinlockLock(port->serialRequestLock); port->WaitingForTXIdle = false; PPCSerialISR(NULL, NULL, port); //IOSpinlockUnlock(port->serialRequestLock); } void AppleSCCSerial::delayTOHandler(PortInfo_t *port) { UInt32 currentState = readPortState(port); DLOG("delayTOHandler\n"); assert (currentState & INTERNAL_DELAY); //IOSpinlockLock(port->serialRequestLock); currentState &= ~INTERNAL_DELAY; changeState(port, currentState, currentState); PPCSerialISR(NULL, NULL, port); //IOSpinlockUnlock(port->serialRequestLock); } void AppleSCCSerial::heartBeatTOHandler(PortInfo_t *port) { DLOG("heartBeatTOHandler\n"); assert (tval2long(port->HeartBeatInterval) > 0); //IOSpinlockLock(port->serialRequestLock); // if (!port->JustDoneInterrupt) { PPCSerialISR(NULL, NULL, port); // } // port->JustDoneInterrupt = NO; //calloutEntryDispatchDelayed( port->HeartBeatTOEntry, // calloutDeadlineFromInterval(port->HeartBeatInterval)); //*** thread related problems? ////IOSpinlockUnlock(port->serialRequestLock); } /* executeEvent causes the specified event to be processed immediately. */ IOReturn AppleSCCSerial:: executeEvent(PortInfo_t *port, UInt32 event, UInt32 data, UInt32 *state, UInt32 *delta) { IOReturn ret = kIOReturnSuccess; UInt32 tmp; unsigned char clockMode; // for MIDI clock mode DLOG("ExecuteEvent %d %d\n",(int)event,(int)data); if ((readPortState(port) & PD_S_ACQUIRED) == 0) return kIOReturnNotOpen; switch (event) { case PD_RS232_E_XON_BYTE: port->XONchar = data; break; case PD_RS232_E_XOFF_BYTE: port->XOFFchar = data; break; case PD_E_SPECIAL_BYTE: port->SWspecial[data>>SPECIAL_SHIFT] |= (1 << (data & SPECIAL_MASK)); break; case PD_E_VALID_DATA_BYTE: port->SWspecial[data>>SPECIAL_SHIFT] &= ~(1 << (data & SPECIAL_MASK)); break; // Enqueued flow control event allows the user to change the state of any flow control // signals set on Manual (its Auto bit in exec/req event is cleared) case PD_E_FLOW_CONTROL : DLOG("-ExecuteEvent PD_E_FLOW_CONTROL\n"); tmp = data & (PD_RS232_D_RFR | PD_RS232_D_DTR | PD_RS232_D_RXO); tmp >>= PD_RS232_D_SHIFT; tmp &= ~(port->FlowControl); *delta |= tmp; *state &= ~tmp; *state |= (tmp & data); if (tmp & PD_RS232_S_RXO) { if (data & PD_RS232_S_RXO) { port->RXOstate = NEEDS_XON; } else { port->RXOstate = NEEDS_XOFF; } } #if 0 programMCR(port, *state); #endif break; case PD_E_ACTIVE: DLOG("-ExecuteEvent PD_E_ACTIVE\n"); if ((bool)data) ret = activatePort(port); else deactivatePort(port); break; //Fix for IRDA and MIDI which want to know about bytes as soon as it arrives case PD_E_DATA_LATENCY: port->DataLatInterval = long2tval(data *1000); // IOLog("Setting PD_E_DATA_LATENCY to: %ld \n",data); break; case PD_RS232_E_MIN_LATENCY: port->MinLatency = bool(data); port->DLRimage = 0x0000; programChip(port); break; case PD_E_DATA_INTEGRITY: DLOG("-ExecuteEvent PD_E_DATA_INTEGRITY\n"); 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; if (!SccSetParity(port, (ParityType)data)) ret = kIOReturnBadArgument; } break; case PD_E_DATA_RATE: /* For API compatiblilty with Intel. */ data >>=1; DLOG("-ExecuteEvent PD_E_DATA_RATE %d\n",(int)data); if ((data < MIN_BAUD) || (data > kMaxBaudRate)) ret = kIOReturnBadArgument; else { port->BaudRate = data; // for DCP -- begin if ((port->whichPort == serialPortA) && (port->gDCPModemFound)) { // force the connect to be 115k for DCP // IOLog ("AppleSCCSerial::executeEvent -- forcing the baudrate to be 115k\n"); if (!SccSetBaud(port, 115200)) { // IOLog ("AppleSCCSerial::executeEvent -- forcing the baudrate to be 115k failed\n"); ret = kIOReturnBadArgument; } } // for DCP -- end else { if (!SccSetBaud(port, data)) ret = kIOReturnBadArgument; } } break; case PD_E_EXTERNAL_CLOCK_MODE: /* For compatiblilty with MIDI. */ DLOG("-ExecuteEvent PD_E_EXTERNAL_CLOCK_MODE data = %d\n", data); switch (data) { case kX1UserClockMode: // PPCSerialPort.h for value clockMode = kX1ClockMode; // Z85C30.h for value break; case kX16UserClockMode: clockMode = kX16ClockMode; break; case kX32UserClockMode: clockMode = kX32ClockMode; break; case kX64UserClockMode: clockMode = kX64ClockMode; break; default : clockMode = kX32ClockMode; break; } if (!SccConfigureForMIDI(port, clockMode)) ret = kIOReturnBadArgument; break; case PD_E_DATA_SIZE: /* For API compatiblilty with Intel. */ data >>=1; DLOG("-ExecuteEvent PD_E_DATA_SIZE data = %d\n", data); if ((data < 5) || (data > 8)) ret = kIOReturnBadArgument; else { port->CharLength = data; if (!SccSetDataBits(port, data)) ret = kIOReturnBadArgument; } break; case PD_RS232_E_STOP_BITS: DLOG("-ExecuteEvent PD_RS232_E_STOP_BITS\n"); if ((data < 0) || (data > 20)) ret = kIOReturnBadArgument; else { port->StopBits = data; if (!SccSetStopBits(port, data)) ret = kIOReturnBadArgument; } break; case PD_E_RXQ_FLUSH: #ifdef IntelTest port->RX.Input = port->RX.Output = port->RX.Base; port->RX.Count = port->RX.OverRun = 0; if ((port->Type) > NS16550) outb(FIFO_CTRL, ((port->FCRimage) | RX_FIFO_RESET)); *state &= ~(PD_S_RXQ_MASK | FLOW_RX_AUTO); // *state |= generateRXQState(port); *delta |= PD_S_RXQ_MASK | FLOW_RX_AUTO; #endif break; case 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: if (data != 0) ret = kIOReturnBadArgument; break; case PD_E_RX_DATA_SIZE: if (data != 0) ret = kIOReturnBadArgument; break; case PD_RS232_E_RX_STOP_BITS: if (data != 0) ret = kIOReturnBadArgument; break; case PD_E_TXQ_FLUSH: #ifdef IntelTest port->TX.Input = port->TX.Output = port->TX.Base; port->TX.Count = 0; if ((port->Type) > NS16550) outb(FIFO_CTRL, ((port->FCRimage) | TX_FIFO_RESET)); *state &= ~PD_S_TXQ_MASK; *state |= generateTXQState(port); *delta |= PD_S_TXQ_MASK; #endif break; case PD_RS232_E_LINE_BREAK : *state &= ~PD_RS232_S_BRK; // *state |= lineBreak(port, ((BOOL)data)); *delta |= PD_RS232_S_BRK; break; case PD_E_DELAY: assert(!((*state) & INTERNAL_DELAY)); if (data > 0) { if (data > (UINT_MAX/1000)) data = (UINT_MAX/1000); *state |= INTERNAL_DELAY; //calloutEntryDispatchDelayed(port->DelayTOEntry, calloutDeadlineFromInterval(long2tval(data*1000))); } break; case PD_E_RXQ_HIGH_WATER: #if 0 port->RX.HighWater = data; if ((port->RX.HighWater) > ((port->RX.Size) - BIGGEST_EVENT)) (port->RX.HighWater) = ((port->RX.Size) - BIGGEST_EVENT); if ((port->RX.HighWater) < (BIGGEST_EVENT *2)) (port->RX.HighWater) = (BIGGEST_EVENT *2); if ((port->RX.LowWater) > ((port->RX.HighWater) - BIGGEST_EVENT)) (port->RX.LowWater) = ((port->RX.HighWater) - BIGGEST_EVENT); *state &= ~(PD_S_RXQ_MASK | FLOW_RX_AUTO); // *state |= generateRXQState(port); *delta |= PD_S_RXQ_MASK | FLOW_RX_AUTO; #endif break; case PD_E_RXQ_LOW_WATER: #if 0 port->RX.LowWater = data; if ((port->RX.LowWater) > ((port->RX.HighWater) - BIGGEST_EVENT)) (port->RX.LowWater) = ((port->RX.HighWater) - BIGGEST_EVENT); if ((port->RX.LowWater) < BIGGEST_EVENT) (port->RX.LowWater) = BIGGEST_EVENT; *state &= ~(PD_S_RXQ_MASK | FLOW_RX_AUTO); // *state |= generateRXQState(port); *delta |= PD_S_RXQ_MASK | FLOW_RX_AUTO; #endif break; case PD_E_TXQ_HIGH_WATER: #if 0 port->RX.HighWater = data; if ((port->TX.HighWater) > ((port->TX.Size) - BIGGEST_EVENT)) (port->TX.HighWater) = ((port->TX.Size) - BIGGEST_EVENT); if ((port->TX.HighWater) < (BIGGEST_EVENT *2)) (port->TX.HighWater) = (BIGGEST_EVENT *2); if ((port->TX.LowWater) > ((port->TX.HighWater) - BIGGEST_EVENT)) (port->TX.LowWater) = ((port->TX.HighWater) - BIGGEST_EVENT); *state &= ~PD_S_TXQ_MASK; // *state |= generateTXQState(port); *delta |= PD_S_TXQ_MASK; #endif break; case PD_E_TXQ_LOW_WATER: #if 0 port->TX.LowWater = data; if ((port->TX.LowWater) > ((port->TX.HighWater) - BIGGEST_EVENT)) (port->TX.LowWater) = ((port->TX.HighWater) - BIGGEST_EVENT); if ((port->TX.LowWater) < BIGGEST_EVENT) (port->TX.LowWater) = BIGGEST_EVENT; *state &= ~PD_S_TXQ_MASK; // *state |= generateTXQState(port); *delta |= PD_S_TXQ_MASK; #endif break; default: ret = kIOReturnBadArgument; break; } /* ejk for compiler warnings. */ *state |= *state; return ret; } /* freeRingBuffer() : Accepts a queue as its argument. *First, it frees all resources assocated with the queue, *then sets all queue parameters to safe values. */ void AppleSCCSerial::freeRingBuffer(CirQueue *Queue) { DLOG("In freeRingBuffer\n"); if (Queue->Start) { IOFree(Queue->Start, Queue->Size); CloseQueue(Queue); } } /* allocateRingBuffer() : Accepts a queue as its argument. *First, it allocates resources needed by the queue, *then sets up all queue parameters. */ bool AppleSCCSerial::allocateRingBuffer(CirQueue *Queue, size_t BufferSize) { u_char *Buffer; DLOG("In allocateRingBuffer\n"); // Buffer = (u_char *)IOMalloc(kMaxCirBufferSize); Buffer = (u_char *)IOMalloc(BufferSize); // InitQueue(Queue, Buffer, kMaxCirBufferSize); InitQueue(Queue, Buffer, BufferSize); if (Buffer) return true; else return false; } /* *readPortState * *Reads the current Port->State. This is in a specific function so that *I know it is interrupt-preotected. * */ UInt32 AppleSCCSerial::readPortState(PortInfo_t *port) { //rs!IOSimpleLockLockDisableInterrupt(port->serialRequestLock); IORecursiveLockLock(port->serialRequestLock); UInt32 returnState = port->State; //rs! IOSimpleLockUnlockEnableInterrupt(port->serialRequestLock, previousInterruptState); IORecursiveLockUnlock(port->serialRequestLock); return returnState; } /* *changeState * *Changes the bits which are indicated by mask of the current Port->State *to the bits indicated by mask of state * *if state = 0 the mask bits are cleared *if mask = 0 nothing is changed * *delta contains the difference between the new and old state taking the *mask into account * */ void AppleSCCSerial::changeState(PortInfo_t *port, UInt32 state, UInt32 mask) { UInt32 delta; //DLOG("++>changeState(state%x, mask%x, port->state%x) watchStateMask%x\n",(int)state, (int)mask, (int)port->State, (int)port->WatchStateMask); if (port && port->serialRequestLock) { IORecursiveLockLock(port->serialRequestLock); //7/13/01-hsjb - do we need to do the same for RTS? if ((mask & PD_RS232_S_DTR) && ((port->FlowControl & PD_RS232_A_DTR) != PD_RS232_A_DTR)) { if ((state & PD_RS232_S_DTR) != (port->State & PD_RS232_S_DTR)) { if (state & PD_RS232_S_DTR) { DLOG("*** DTR ON\n"); SccSetDTR(port, true); } else { DLOG("*** DTR OFF\n"); SccSetDTR(port, 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) { //DLOG("changeState Calling thread_wakeup\n"); thread_wakeup_with_result (&port->WatchStateMask, THREAD_RESTART); } IORecursiveLockUnlock(port->serialRequestLock); } //DLOG("-->changeState %x \nstate %x\n p->wsm%x \ndelta %x\nmask %x\n", (int)port->State, state, port->WatchStateMask, delta, mask); } /* watchState() : *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 IO_R_IO indicates that the port went inactive. A return value of *IO_R_IPC_FAILURE indicates sleep was interrupted by a signal. */ IOReturn AppleSCCSerial::watchState(PortInfo_t *port, UInt32 *state, UInt32 mask) { unsigned watchState, foundStates; bool autoActiveBit = false; IOReturn rtn = kIOReturnSuccess; // IOInterruptState previousInterruptState; DLOG("[WatchState.."); watchState = *state; // previousInterruptState = IOSimpleLockLockDisableInterrupt(port->serialRequestLock); IORecursiveLockLock(port->serialRequestLock); // hack to get around problem with carrier detection /* if (*state & PD_RS232_S_CAR) { port->State |= PD_RS232_S_CAR; #if 0 //7/13/01 hsjb- This is a "watch" state, not a "set" state call, Also this breaks irda! SccSetDTR(port, true); #endif } */ if (!(mask & (PD_S_ACQUIRED | PD_S_ACTIVE))) { watchState &= ~PD_S_ACTIVE; // Check for low PD_S_ACTIVE mask |= PD_S_ACTIVE; // Register interest in PD_S_ACTIVE bit autoActiveBit = true; } for (;;) { // Check port state for any interesting bits with watchState value // NB. the '^ ~' is a XNOR and tests for equality of bits. foundStates = (watchState ^ ~(port->State)) & mask; if (foundStates) { *state = port->State; if (autoActiveBit && (foundStates & PD_S_ACTIVE)) rtn = kIOReturnIOError; else rtn = kIOReturnSuccess; 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 event's are an interrupt // or one of the bits in the WatchStateMask changing. // //rs! IOSimpleLockLock(port->WatchLock); IOLockLock(port->WatchLock); port->WatchStateMask |= mask; // note: Interrupts need to be locked out completely here, since as assertwait is called // other threads waiting on &port->WatchStateMask will be woken up and spun through the loop // if an interrupts occurs at this point then the current thread will end up waiting with a different // port state than assumed -- this problem was causing dequeueData to wait for a change // in PD_E_RXQ_EMPTY to 0 after an interrupt had already changed it to 0. assert_wait(&port->WatchStateMask, true); /* assert event */ //rs! IOSimpleLockUnlock(port->WatchLock); /* release the lock */ IOLockUnlock(port->WatchLock); /* release the lock */ // IOSimpleLockUnlockEnableInterrupt(port->serialRequestLock, previousInterruptState); IORecursiveLockUnlock(port->serialRequestLock); rtn = thread_block((void (*)(void)) 0); /* block ourselves */ //rs! previousInterruptState = IOSimpleLockLockDisableInterrupt(port->serialRequestLock); IORecursiveLockLock(port->serialRequestLock); if (rtn == THREAD_RESTART) { continue; } else { rtn = kIOReturnIPCError; break; } } DLOG("]\n"); // 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. //rs! IOSimpleLockLock(port->WatchLock); IOLockLock(port->WatchLock); port->WatchStateMask = 0; // IOSimpleLockUnlock(port->WatchLock); IOLockUnlock(port->WatchLock); thread_wakeup_with_result(&port->WatchStateMask, THREAD_RESTART); // IOSimpleLockUnlockEnableInterrupt(port->serialRequestLock, previousInterruptState); IORecursiveLockUnlock(port->serialRequestLock); return rtn; } #if USE_WORK_LOOPS bool AppleSCCSerial::interruptFilter(OSObject* obj, IOFilterInterruptEventSource * source) { // check if this interrupt belongs to me int interruptIndex = source->getIntIndex(); if (interruptIndex == kIntRxDMA) { //IOLog("Rx DMA Interrupt Filtered\n"); return true;// go ahead and invoke completion routine } if (interruptIndex == kIntTxDMA) { //IOLog("Tx DMA Interrupt Filtered\n"); return true;// go ahead and invoke completion routine } //IOLog("NOT Rx or Tx Interrupt Filtered\n"); return false; } void AppleSCCSerial::interruptHandler(OSObject* obj, IOInterruptEventSource * source, int count) { // handle the interrupt int interruptIndex = source->getIntIndex();//who are you? what do you want? who do you serve? if (interruptIndex == kIntChipSet) { //IOLog("Handle SCC Interrupt\n"); handleInterrupt(obj, 0, NULL, 0); return; } if (interruptIndex == kIntRxDMA) { //IOLog("Handle Rx DMA Interrupt\n"); handleDBDMARxInterrupt(obj, 0, NULL, 0); return; } if (interruptIndex == kIntTxDMA) { //IOLog("Handle Tx DMA Interrupt\n"); handleDBDMATxInterrupt(obj, 0, NULL, 0); } } #endif void AppleSCCSerial::callCarrierhack(thread_call_param_t whichDevice, thread_call_param_t carrier) { AppleSCCSerial *self = (AppleSCCSerial *) whichDevice; if (self->modemDCDStateChangeCallback) (*self->modemDCDStateChangeCallback)(self->fModemObject, (bool) carrier); // IOLog("callCarrierhack: carrier %d count: %d\n",carrier,self->fCarrierHackCount ); OSDecrementAtomic(&self->fCarrierHackCount); } void AppleSCCSerial::setCarrierHack(OSObject *target, SCCCarrierHack action) { modemDCDStateChangeCallback = action; fModemObject = target; } bool AppleSCCSerial::NonSCCModem() { IORegistryEntry * next; IORegistryIterator * iter; bool stopWhile = false; bool found = false; iter = IORegistryIterator::iterateOver( gIODTPlane ); assert( iter ); iter->reset(); while ((stopWhile == false) && (next = iter->getNextObjectRecursive())) { if (0 == strcmp ("i2c-modem", next->getName())) { stopWhile = true; OSData *s2 = OSDynamicCast(OSData, next->getProperty("modem-id")); if (s2) { char *tmpID = (char *) s2->getBytesNoCopy(); tmpID++; // need to look at the second byte which contains the modem ID switch (*tmpID) { case 0x04 : case 0x05 : case 0x07 : case 0x08 : case 0x0b : case 0x0c : found = true; break; } } } } iter->release(); return found; } // end of AppleSCCSerial::NonSCCModem UInt32 SCC_GetSystemTime(void) { AbsoluteTime atTime; UInt64 nsTime; clock_get_uptime(&atTime); absolutetime_to_nanoseconds( atTime, &nsTime); return (UInt32)(nsTime/1000000); } void SCC_TRACEMSG(char *msg) { UInt32 dwTime = SCC_GetSystemTime(); IOLog("%07ld.%03ld: ", dwTime/1000, dwTime%1000); IOLog (msg); } // for DCP -- begin // ------------------------------------------------------------------------------ // Search for a specified internal modem bool AppleSCCSerial::LookForInternalModem(UInt8 modemID = 0) { IORegistryEntry * next; IORegistryIterator * iter; bool found = false; // IOLog ("AppleSCCSerial::LookForInternalModem -- Entered\n"); iter = IORegistryIterator::iterateOver( gIODTPlane ); assert( iter ); iter->reset(); found = false; while ((found == false) && (next = iter->getNextObjectRecursive())) { if (0 == strcmp ("i2c-modem", next->getName())) { OSData *s2 = OSDynamicCast(OSData, next->getProperty("modem-id")); if (s2) { char *tmpID = (char *) s2->getBytesNoCopy(); tmpID++; // need to look at the second byte which contains the modem ID if (*tmpID == modemID) { // IOLog ("AppleSCCSerial::LookForInternalModem -- Found Mini Spring DCP Modem\n"); found = true; } } } } iter->release(); // IOLog ("AppleSCCSerial::LookForInternalModem -- Exited\n"); return found; } // end of AppleSCCSerial::LookForInternalModem // for DCP -- end