#include <libkern/OSByteOrder.h>
#include <IOKit/IOLib.h>
#include <IOKit/IOReturn.h>
#include <IOKit/IOKitKeys.h>
#include <IOKit/IOPlatformExpert.h>
#include <IOKit/IODeviceTreeSupport.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/serial/IORS232SerialStreamSync.h>
#include <IOKit/serial/IOModemSerialStreamSync.h>
#include "PPCSerialPort.h"
#define fRS232Stream ((IORS232SerialStreamSync *) fProvider)
class AppleSCCModem : public IOModemSerialStreamSync
{
OSDeclareDefaultStructors(AppleSCCModem)
protected:
bool portOpened;
UInt32 startingTime;
bool bModemReady;
IOService *audio;
IOService *myProvider;
virtual bool initForPM(IOService *provider);
virtual unsigned long initialPowerStateForDomainState ( IOPMPowerFlags );
virtual IOReturn setPowerState(unsigned long powerStateOrdinal, IOService* whatDevice);
static void switchModeminputSource(OSObject *self, bool carrier);
public:
virtual bool attach(IOService *provider);
virtual void detach(IOService *provider);
virtual bool start(IOService *provider);
virtual void stop(IOService *provider);
virtual IOService *probe(IOService *provider, SInt32 *score);
virtual IOReturn acquirePort(bool sleep);
virtual IOReturn releasePort();
virtual UInt32 getState();
virtual IOReturn setState(UInt32 state, UInt32 mask);
virtual IOReturn watchState(UInt32 *state, UInt32 mask);
virtual UInt32 nextEvent();
virtual IOReturn executeEvent(UInt32 event, UInt32 data);
virtual IOReturn requestEvent(UInt32 event, UInt32 *data);
virtual IOReturn enqueueEvent(UInt32 event, UInt32 data, bool sleep);
virtual IOReturn dequeueEvent(UInt32 *event, UInt32 *data, bool sleep);
virtual IOReturn enqueueData(UInt8 *buffer, UInt32 size, UInt32 *count, bool sleep);
virtual IOReturn dequeueData(UInt8 *buffer, UInt32 size, UInt32 *count, UInt32 min);
};
OSDefineMetaClassAndStructors(AppleSCCModem, IOModemSerialStreamSync)
#define super IOModemSerialStreamSync
static inline UInt64 getDebugFlagsTable(OSDictionary *props)
{
OSNumber *debugProp;
UInt64 debugFlags = gIOKitDebug;
debugProp = OSDynamicCast(OSNumber, props->getObject(gIOKitDebugKey));
if (debugProp)
debugFlags = debugProp->unsigned64BitValue();
return debugFlags;
}
#define getDebugFlags() (getDebugFlagsTable(getPropertyTable()))
#define IOLogCond(cond, x) do { if (cond) (IOLog x); } while (0)
bool AppleSCCModem::attach(IOService *provider)
{
return IOService::attach(provider);
}
void AppleSCCModem::detach(IOService *provider)
{
IOService::detach(provider);
}
void AppleSCCModem::stop(IOService *provider)
{
PMstop();
IOService::stop(provider);
}
bool AppleSCCModem::start(IOService *provider)
{
bool logStart = (0 != (kIOLogStart & getDebugFlags()));
IOLogCond(logStart, ("%s(%x): Start is called\n", getName(), (unsigned)this));
portOpened = false;
audio = NULL;
myProvider = provider;
if (!IOService::start(provider))
{
return false;
}
fRS232Stream = OSDynamicCast(IORS232SerialStreamSync, provider);
if (!fRS232Stream) {
IOLogCond(logStart,
("%s(%x): Provider not IORS232?\n", getName(), (unsigned)this));
return false;
}
setProperty(kIOTTYBaseNameKey, "modem");
registerService();
initForPM(provider);
return true;
}
IOService * AppleSCCModem::probe(IOService *provider, SInt32 *)
{
OSString *matchEntry = 0;
bool logProbe = (0 != (kIOLogProbe & getDebugFlags()));
IOLogCond(logProbe,
("AppleSCCModem::probe called\n"));
matchEntry = OSDynamicCast (OSString, getProperty (kIONameMatchedKey));
if ((matchEntry == 0) || (matchEntry->isEqualTo("chrp,es2") == false))
return this;
OSData *s = OSDynamicCast(OSData, provider->getProperty("slot-names"));
if (!s)
return 0;
UInt32 *nWords = (UInt32*)s->getBytesNoCopy();
if (*nWords < 1) {
IOLogCond(logProbe,
("AppleSCCModem::probe chrp,es2 failed no slot-name\n"));
return 0;
}
char *tmpPtr, *tmpName;
tmpName = (char *) s->getBytesNoCopy() + sizeof(UInt32);
for (tmpPtr = tmpName; *tmpPtr != 0; tmpPtr++)
*tmpPtr |= 32;
if (strncmp (tmpName, "modem", 5) == 0)
return this;
else {
IOLogCond(logProbe, ("\n\nAppleSCCModem::probe -- did not match with modem string \n"));
return 0;
}
}
unsigned long AppleSCCModem::initialPowerStateForDomainState ( IOPMPowerFlags )
{
return 0;
}
bool
AppleSCCModem::initForPM(IOService *provider)
{
PMinit(); provider->joinPMtree(this);
if (pm_vars == NULL)
return false;
#define number_of_power_states 2
static IOPMPowerState ourPowerStates[number_of_power_states] = {
{1,0,0,0,0,0,0,0,0,0,0,0},
{1,IOPMDeviceUsable,IOPMPowerOn,IOPMPowerOn,0,0,0,0,0,0,0,0}
};
registerPowerDriver(this, ourPowerStates, number_of_power_states);
return true;
}
IOReturn AppleSCCModem::setPowerState(unsigned long powerStateOrdinal, IOService* whatDevice)
{
if (powerStateOrdinal == 1)
{
if (portOpened)
{
callPlatformFunction("PowerModem", false, (void *)true, 0, 0, 0);
IOSleep(250); callPlatformFunction("ModemResetHigh", false, 0, 0, 0, 0);
IOSleep(250); callPlatformFunction("ModemResetLow", false, 0, 0, 0, 0);
IOSleep(250); callPlatformFunction("ModemResetHigh", false, 0, 0, 0, 0);
IOSleep(250); }
}
else if (powerStateOrdinal == 0)
{
if (!portOpened)
{
callPlatformFunction("ModemResetLow", false, 0, 0, 0, 0);
IOSleep(250); callPlatformFunction("PowerModem", false, (void *)false, 0, 0, 0);
}
}
return IOPMAckImplied;
}
IOReturn AppleSCCModem::acquirePort(bool sleep)
{
IOReturn pwrResult;
IOReturn rtn = fRS232Stream->acquirePort(sleep);
if (myProvider)
{
AppleSCCSerial *scc = OSDynamicCast(AppleSCCSerial, myProvider->getProvider());
if (scc)
{
scc->setCarrierHack(this, &AppleSCCModem::switchModeminputSource);
}
}
if (rtn == kIOReturnSuccess)
{
portOpened = true;
pwrResult = changePowerStateTo(1);
bModemReady = false; startingTime = SCC_GetSystemTime() / 1000;
}
return rtn;
}
IOReturn AppleSCCModem::releasePort()
{
IOReturn rtn = fRS232Stream->releasePort();
if (rtn == kIOReturnSuccess)
{
portOpened = false;
bModemReady = false;
if (NULL != audio)
{
audio->callPlatformFunction (OSSymbol::withCString ("setModemSound"), false, (void *)false, 0, 0, 0);
}
changePowerStateTo(0);
}
return rtn;
}
UInt32 AppleSCCModem::getState()
{ return fRS232Stream->getState(); }
IOReturn AppleSCCModem::setState(UInt32 state, UInt32 mask)
{ return fRS232Stream->setState(state, mask); }
IOReturn AppleSCCModem::watchState(UInt32 *state, UInt32 mask)
{ return fRS232Stream->watchState(state, mask); }
UInt32 AppleSCCModem::nextEvent()
{ return fRS232Stream->nextEvent(); }
IOReturn AppleSCCModem::executeEvent(UInt32 event, UInt32 data)
{ return fRS232Stream->executeEvent(event, data); }
IOReturn AppleSCCModem::requestEvent(UInt32 event, UInt32 *data)
{ return fRS232Stream->requestEvent(event, data); }
IOReturn AppleSCCModem::enqueueEvent(UInt32 event, UInt32 data, bool sleep)
{ return fRS232Stream->enqueueEvent(event, data, sleep); }
IOReturn AppleSCCModem::dequeueEvent(UInt32 *event, UInt32 *data, bool sleep)
{ return fRS232Stream->dequeueEvent(event, data, sleep); }
IOReturn AppleSCCModem::
enqueueData(UInt8 *buffer, UInt32 size, UInt32 *count, bool sleep)
{
if (bModemReady)
{
}
else
{
UInt32 currentTime = SCC_GetSystemTime() / 1000; UInt32 diffTime = currentTime - startingTime;
mach_timespec_t timeOut;
timeOut.tv_sec = 0;
timeOut.tv_nsec = 1000;
if (audio == NULL)
{
audio = IOService::waitForService (IOService::serviceMatching ("AppleBurgundyAudio"), &timeOut);
}
if (audio == NULL)
{
audio = IOService::waitForService (IOService::serviceMatching ("AppleDACAAudio"), &timeOut);
}
if (audio == NULL)
{
audio = IOService::waitForService (IOService::serviceMatching ("AppleScreamerAudio"), &timeOut);
}
if (audio == NULL)
{
audio = IOService::waitForService (IOService::serviceMatching ("AppleTexasAudio"), &timeOut);
}
if (audio == NULL)
{
audio = IOService::waitForService (IOService::serviceMatching ("AppleTexas2Audio"), &timeOut);
}
if (diffTime < 3)
{
switch (diffTime)
{
case 2 : IOSleep (1000); break;
case 1 : IOSleep (2000); break;
default : IOSleep (3000);
}
}
if (NULL != audio)
{
audio->callPlatformFunction (OSSymbol::withCString ("setModemSound"), false, (void *)true, 0, 0, 0);
}
bModemReady = true;
}
return fRS232Stream->enqueueData(buffer, size, count, sleep);
}
IOReturn AppleSCCModem::
dequeueData(UInt8 *buffer, UInt32 size, UInt32 *count, UInt32 min)
{ return fRS232Stream->dequeueData(buffer, size, count, min); }
void AppleSCCModem::switchModeminputSource(OSObject *target, bool carrier)
{
AppleSCCModem *self = (AppleSCCModem *) target;
if (!self)
return;
if (carrier != false)
{
self->callPlatformFunction("setModemSound", false, (void *)false, 0, 0, 0);
}
else
{
self->callPlatformFunction("setModemSound", false, (void *)true, 0, 0, 0);
}
}