/*
    File:               Ambien.h
    Program:            Ambien
    Original author:    Michael Roßberg
                        mick@binaervarianz.de
    Rewritten by:       Joshua Wise
                        jwise@andrew.cmu.edu
    Description:        Ambien is a kernel extension to disable sleep-on-clamshell.
 
    Ambien is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Ambien is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Ambien; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

#include "Ambien.h"
#include <IOKit/IOLib.h>
#include <IOKit/pwr_mgt/RootDomain.h>
#include <IOKit/pwr_mgt/IOPM.h>

#define super IOService

OSDefineMetaClassAndStructors(Ambien, IOService);

bool Ambien::init(OSDictionary* properties)
{
	IOLog("Ambien.kext for Mac OS X Leopard, by Joshua Wise\n");
	
	_workLoop = 0;
	if (super::init(properties) == false) {
		IOLog("Ambien::init: super::init failed\n");
		return false;
	}

	return true;
}

bool Ambien::start(IOService* provider)
{
	static IOPMPowerState states[] =
	{
		{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
		{1, kIOPMDeviceUsable, kIOPMPowerOn, kIOPMPowerOn, 0, 0, 0, 0, 0, 0, 0, 0}
	};
	
	if (!super::start(provider)) {
		IOLog("Ambien::start: super::start failed\n");
		return false;
	}
	
	PMinit();
	provider->joinPMtree(this);
	if (registerPowerDriver(this, states, 2) != IOPMNoErr)
	{
		IOLog("Ambien::start: registerPowerDriver returned error -- uh-oh!\n");
		return false;
	}

	return true;
}

void Ambien::stop(IOService* provider)
{
	if (_workLoop)
	{
		_workLoop->release();
		_workLoop = 0;
	}
	
	PMstop();
	super::stop(provider);
	IOLog("Ambien: your sleep schedule is back to you!\n");
}

IOReturn Ambien::setPowerState(unsigned long which, IOService *dev)
{
	if (which == 0)
	{
		IOLog("Ambien::setPowerState: system going to sleep\n");
	} else {
		IOLog("Ambien::setPowerState: system is awake\n");
	}
	
	return kIOPMAckImplied;
}

void Ambien::free()
{
	super::free();
	return;
}

IOWorkLoop* Ambien::getWorkLoop()
{
	if (!_workLoop)
		_workLoop = IOWorkLoop::workLoop();
		
	return _workLoop;
}

IOReturn Ambien::message(UInt32 type, IOService *provider, void *arg)
{
	unsigned int argi = (unsigned int)(unsigned long)/* screw off, it's a bitmask, not a pointer */arg;
	
	if (type == kIOPMMessageClamshellStateChange)
	{
		IOPMrootDomain *root = NULL;
		
		IOLog("Ambien::message: kIOPMMessageClamshellStateChange: lid is %s, sleep bit is %d (arg %08x)\n",
		      (argi & kClamshellStateBit) ? "closed" : "open",
		      !!(argi & kClamshellSleepBit), argi);
		
		root = getPMRootDomain();
		if (!root)
		{
			IOLog("Ambien::message: failed to get PM root?\n");
			return 0;
		}
		root->receivePowerNotification((argi & kClamshellStateBit) ? kIOPMPreventSleep : kIOPMAllowSleep);
	} else
		IOLog("Ambien::message: unknown type %08x (so I won't worry about it)\n", (unsigned int)type);
	return super::message(type, provider, arg);
}
