#if defined(__linux__) || defined(__FreeBSD__)
/*********************************************************************
 *                
 * Filename:      smapidev.c
 * Description:   IBM SMAPI (System Management API) interface functions
 * Author:        Bill Mair, Thomas Hood
 * Created:       19 July 1999
 *
 * Please report bugs to the author ASAP.
 * 
 *     Copyright (c) 1999 J.D. Thomas Hood and Bill Mair,
 *         All rights reserved
 *     
 *     This program 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.
 * 
 *     This program 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.
 * 
 *     To receive a copy of the GNU General Public License, please write
 *     to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 *     Boston, MA 02111-1307 USA
 *     
 ********************************************************************/

#include <fcntl.h>
#include <stdlib.h>
#ifdef __linux__
#include <linux/unistd.h>
#else
#include <unistd.h>
#endif
#include <getopt.h>
#include <sys/ioctl.h>
#include <errno.h>
#ifdef __linux__
#include <linux/types.h>
#else
#include <sys/types.h>
#endif
#include <stdio.h>
#include <string.h>

#ifndef __linux__
typedef uint8_t __u8 ;
typedef uint16_t __u16 ;
typedef uint32_t __u32 ;
#endif

#include "thinkpad_common.h"
/*#include "thinkpad.h" */
#include "smapi.h"
#include "smapidev.h"

/****** defines ******/

#define SZ_SMAPIDEV_VERSION "2.0"

#define SZ_SMAPIDEV_NAME "smapidev"

/*
 * The structures may need to be extended if more
 * functionality is added to the SMAPI by IBM.
 *
 * To cover this, future releases can check the size
 * defined in sizeStruct, and reduce the amount of
 * information put into the structure accordingly.
 */

#define SIZE_BIOSINFO_V1           ((size_t)24)
#define SIZE_CPUINFO_V1            ((size_t)16)
#define SIZE_DISPLAYINFO_V1        ((size_t) 8)
#define SIZE_DOCKINGINFO_V1        ((size_t)12)
#define SIZE_ULTRABAYINFO_V1       ((size_t) 8)
#define SIZE_SLAVEINFO_V1          ((size_t)12)
#define SIZE_SENSORINFO_V1         ((size_t) 8)
#define SIZE_SCREENREFRESHINFO_V1  ((size_t)12)
#define SIZE_DISPLAYCAP_V1         ((size_t)12)


/****** variables ******/

char szSmapidevName[] = SZ_SMAPIDEV_NAME;


/****** utility functions ******/

/*
 * This function returns the binary value of a two-digit bcd number
 * If the bcd8 value is 0xFF, as it may be if a location has never been
 * initialized in the ThinkPad CMOS RAM, then 0xFF is returned as the
 * binary equivalent.
 */
byte byte_of_bcd8( bcd8_t bcd8The )
{
	byte bTens, bUnits;

	/* catch uninitialized values: simply return them */
	if ( bcd8The == 0xFF ) return 0xFF;

	bUnits = (byte)bcd8The & 0xF;
	bTens = (byte)(bcd8The & 0xF0) >> 4;

	if ( bUnits > 9 || bTens > 9 ) {
		printf( "%s: Warning: value 0x%x which is supposed to be in BCD format is not; not converting.\n", szSmapidevName, bcd8The );
		return (byte)bcd8The;
	}

	return bUnits + (bTens * 10);
}


bcd8_t bcd8_of_byte( byte bThe )
{
	byte bTens, bUnits;

	if ( bThe > 99 ) {
		printf( "%s: the value %d being converted to BCD format will be limited to 99.\n", szSmapidevName, bThe );
		bThe = 99;
	}

	bTens = bThe / (byte)10;
	bUnits = bThe - (bTens * (byte)10);

	return (bTens << 4) | bUnits;
}

/*
 * This function returns the SMAPI BIOS error code if there is one,
 * otherwise the ioctl errno as a negative number
 */
int ioctl_smapi( int intFiledesc, smapi_ioparm_t *pioparmThe )
{
	int intRtn;

	intRtn = ioctl( intFiledesc, IOCTL_SMAPI_REQUEST, pioparmThe );
	if ( intRtn && errno == ETHINKPAD_SUBDRIVER ) return pioparmThe->out.bRc;
	if ( intRtn ) return -errno;
	return 0;
}


/****** functions ******/

int smapidev_GetInfo( smapidev_info_t *pinfoThe )
{

	strncpy( pinfoThe->szVersion, SZ_SMAPIDEV_VERSION, LEN_VERSION_MAX );

	/*** Make sure that the returned string is terminated ***/
	pinfoThe->szVersion[LEN_VERSION_MAX] = '\0';

	return 0;
}


/*** smapi-module-access functions ***/


/*
 * The Technical Reference fails to mention that the returned 
 * BIOS revision values are in BCD format
 */
int smapidev_GetBiosInfo(
	int intFiledesc,
	smapidev_biosinfo_t *pbiosinfoThe
) {
	bcd8_t bcd8SysHigh, bcd8SysLow;
	bcd8_t bcd8SysMgmtHigh, bcd8SysMgmtLow;
	bcd8_t bcd8SMAPIIfaceHigh, bcd8SMAPIIfaceLow;
	bcd8_t bcd8VideoHigh, bcd8VideoLow;
	smapi_ioparm_t ioparmThe;
	int intRtn;

	/* Check structure size */
	if( pbiosinfoThe->sizeStruct != SIZE_BIOSINFO_V1 ) {
#		if DEBUG_STRUCT_SIZES
		printf( "Declared size %d does not match expected size %d in GetBiosInfo\n" , pbiosinfoThe->sizeStruct, SIZE_BIOSINFO_V1 );
#		endif
		return ERR_SMAPIDEV_STRUCTURE_SIZE_INVALID;
	}

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );

	ioparmThe.in.bFunc = (byte) 0;
	ioparmThe.in.bSubFunc = (byte) 0;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	pbiosinfoThe->wSysId               = ioparmThe.out.wParm1;
	pbiosinfoThe->wCountryCode         = ioparmThe.out.wParm2;

	bcd8SysHigh =     (bcd8_t)( ioparmThe.out.wParm3 >> 8          );
	bcd8SysLow =      (bcd8_t)( ioparmThe.out.wParm3        & 0xFF );
	pbiosinfoThe->wSysBiosRevMajor     = (word) byte_of_bcd8( bcd8SysHigh );
	pbiosinfoThe->wSysBiosRevMinor     = (word) byte_of_bcd8( bcd8SysLow );

	bcd8SysMgmtHigh = (bcd8_t)( (ioparmThe.out.dwParm4 >> 8) & 0xFF );
	bcd8SysMgmtLow =  (bcd8_t)(  ioparmThe.out.dwParm4       & 0xFF );
	pbiosinfoThe->wSysMgmtBiosRevMajor = (word) byte_of_bcd8( bcd8SysMgmtHigh );
	pbiosinfoThe->wSysMgmtBiosRevMinor = (word) byte_of_bcd8( bcd8SysMgmtLow );

	bcd8SMAPIIfaceHigh =   (bcd8_t)( (ioparmThe.out.dwParm5 >> 8) & 0xFF );
	bcd8SMAPIIfaceLow =    (bcd8_t)(  ioparmThe.out.dwParm5       & 0xFF );
	pbiosinfoThe->wSmapiBiosIfaceRevMajor = (word) byte_of_bcd8( bcd8SMAPIIfaceHigh );
	pbiosinfoThe->wSmapiBiosIfaceRevMinor = (word) byte_of_bcd8( bcd8SMAPIIfaceLow );

	/*** Video BIOS info ***/
	ioparmThe.in.bFunc = (byte) 0;
	ioparmThe.in.bSubFunc = (byte) 8;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	bcd8VideoHigh =   (bcd8_t)( (ioparmThe.out.wParm1 >> 8)        );
	bcd8VideoLow =    (bcd8_t)(  ioparmThe.out.wParm1       & 0xFF );

	pbiosinfoThe->wVideoBiosRevMajor = (word) byte_of_bcd8( bcd8VideoHigh );
	pbiosinfoThe->wVideoBiosRevMinor = (word) byte_of_bcd8( bcd8VideoLow );

	return 0;
}


int smapidev_GetCpuInfo(
	int intFiledesc,
	smapidev_cpuinfo_t *pcpuinfoThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;

	/* Check structure size */
	if( pcpuinfoThe->sizeStruct != SIZE_CPUINFO_V1 ) {
#		if DEBUG_STRUCT_SIZES
		printf( "Declared size %d does not match expected size %d in GetCpuInfo\n" , pcpuinfoThe->sizeStruct, SIZE_CPUINFO_V1 );
#		endif
		return ERR_SMAPIDEV_STRUCTURE_SIZE_INVALID;
	}

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0;
	ioparmThe.in.bSubFunc = (byte) 1;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	pcpuinfoThe->wManufacturer = (word)( 0xFF &  ioparmThe.out.wParm1 );
	pcpuinfoThe->wType         = (word)( 0xFF & (ioparmThe.out.wParm2 >> 8) );
	pcpuinfoThe->wStepping     = (word)( 0xFF &  ioparmThe.out.wParm2 );

	pcpuinfoThe->wClock        = (word)( 0xFF & (ioparmThe.out.wParm3 >> 8) );

	if ( pcpuinfoThe->wClock == 0xfe )
		pcpuinfoThe->wClock = (word) ioparmThe.out.dwParm4;

	pcpuinfoThe->wInternalClock = (word)( 0xFF & ioparmThe.out.wParm3 );
	if ( pcpuinfoThe->wInternalClock == 0xfe )
		pcpuinfoThe->wInternalClock = (word) ioparmThe.out.dwParm5;

	return 0;
}


int smapidev_GetDisplayInfo(
	int intFiledesc,
	smapidev_displayinfo_t *pdisplayinfoThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;

	/* Check structure size */
	if( pdisplayinfoThe->sizeStruct != SIZE_DISPLAYINFO_V1 ) {
#		if DEBUG_STRUCT_SIZES
		printf( "Declared size %d does not match expected size %d in GetDisplayInfo\n" , pdisplayinfoThe->sizeStruct, SIZE_DISPLAYINFO_V1 );
#		endif
		return ERR_SMAPIDEV_STRUCTURE_SIZE_INVALID;
	}

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc    = (byte) 0;
	ioparmThe.in.bSubFunc = (byte) 2;
	ioparmThe.in.wParm1   = (word) 0x300;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	pdisplayinfoThe->bPanelType   = (byte)(ioparmThe.out.wParm1 >> 8);
	pdisplayinfoThe->bPanelDim    = (byte)(ioparmThe.out.wParm1 & 0xFF);
	pdisplayinfoThe->bCrtType     = (byte)(ioparmThe.out.wParm2 >> 8);
	pdisplayinfoThe->bCrtFeatures = (byte)(ioparmThe.out.wParm2 & 0xFF);

	return 0;
}


int smapidev_GetDockingInfo(
	int intFiledesc,
	smapidev_dockinginfo_t *pdockinginfoThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;

	/* Check structure size */
	if( pdockinginfoThe->sizeStruct != SIZE_DOCKINGINFO_V1 ) {
#		if DEBUG_STRUCT_SIZES
		printf( "Declared size %d does not match expected size %d in GetDockingInfo\n" , pdockinginfoThe->sizeStruct, SIZE_DOCKINGINFO_V1 );
#		endif
		return ERR_SMAPIDEV_STRUCTURE_SIZE_INVALID;
	}

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0;
	ioparmThe.in.bSubFunc = (byte) 3;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	pdockinginfoThe->wID           =           ioparmThe.out.wParm1;  
  	pdockinginfoThe->fDocked       = (flag_t)( ioparmThe.out.bSubRc       & 1);
	pdockinginfoThe->fKeyUnlocked  = (flag_t)((ioparmThe.out.bSubRc >> 6) & 1);
	pdockinginfoThe->fBusConnected = (flag_t)((ioparmThe.out.bSubRc >> 7) & 1);

	return 0;
}


int smapidev_GetUltrabayInfo(
	int intFiledesc,
	smapidev_ultrabayinfo_t *pultrabayinfoThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;

	/* Check structure size */
	if( pultrabayinfoThe->sizeStruct != SIZE_ULTRABAYINFO_V1 ) {
#		if DEBUG_STRUCT_SIZES
		printf( "Declared size %d does not match expected size %d in GetUltrabayInfo\n" , pultrabayinfoThe->sizeStruct, SIZE_ULTRABAYINFO_V1 );
#		endif
		return ERR_SMAPIDEV_STRUCTURE_SIZE_INVALID;
	}

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0;
	ioparmThe.in.bSubFunc = (byte) 4;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	pultrabayinfoThe->bType = (byte)(ioparmThe.out.wParm2 >> 8);
	pultrabayinfoThe->bID   = (byte)(ioparmThe.out.wParm2 & 0xFF);

	return 0;
}


/*
 * The ThinkPad 560Z Technical Reference describes function 0:6 as
 * "Get Power Management Module Information" while the ThinkPad 600
 * describes it as "Get Slave Micro Control Unit Information"
 */
int smapidev_GetSlaveControllerInfo(
	int intFiledesc,
	smapidev_slaveinfo_t *pslaveinfoThe
) {
	smapi_ioparm_t ioparmThe;
	bcd8_t bcd8High = 0, bcd8Low = 0;
	flag_t fInvalidID;
	int intRtn;

	/* Check structure size */
	if( pslaveinfoThe->sizeStruct != SIZE_SLAVEINFO_V1 ) {
#		if DEBUG_STRUCT_SIZES
		printf( "Declared size %d does not match expected size %d in GetSlaveControllerInfo\n" , pslaveinfoThe->sizeStruct, SIZE_SLAVEINFO_V1 );
#		endif
		return ERR_SMAPIDEV_STRUCTURE_SIZE_INVALID;
	}

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0;
	ioparmThe.in.bSubFunc = (byte) 6;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	if ( ioparmThe.out.wParm2 == 0xFFFF ) {
		fInvalidID = 1;
	} else {
		fInvalidID = 0;
		bcd8High = (bcd8_t)( ioparmThe.out.wParm2 >> 8 );
		bcd8Low  = (bcd8_t)( ioparmThe.out.wParm2 & 0xFF );
	}

	pslaveinfoThe->fAscii  = (ioparmThe.out.bSubRc == 0);
	if ( fInvalidID ) {
		pslaveinfoThe->wIDMajor = 0xFFFF;
		pslaveinfoThe->wIDMinor = 0xFFFF;
	} else {
		pslaveinfoThe->wIDMajor = byte_of_bcd8( bcd8High );
		pslaveinfoThe->wIDMinor = byte_of_bcd8( bcd8Low );
	}
	pslaveinfoThe->szID[0] = (char)(0xFF&(ioparmThe.out.wParm2>>8));
	pslaveinfoThe->szID[1] = (char)(0xFF&ioparmThe.out.wParm2);
	pslaveinfoThe->szID[2] = '\0'; /* Add zero termination */

	return 0;
}


int smapidev_GetSensorInfo(
	int intFiledesc,
	smapidev_sensorinfo_t *psensorinfoThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;

	/* Check structure size */
	if( psensorinfoThe->sizeStruct != SIZE_SENSORINFO_V1 ) {
#		if DEBUG_STRUCT_SIZES
		printf( "Declared size %d does not match expected size %d in GetSensorInfo\n" , psensorinfoThe->sizeStruct, SIZE_SENSORINFO_V1 );
#		endif
		return ERR_SMAPIDEV_STRUCTURE_SIZE_INVALID;
	}

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0;
	ioparmThe.in.bSubFunc = (byte) 7;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	psensorinfoThe->fLidClosed         = (ioparmThe.out.wParm2 >>  8) & 1;
	psensorinfoThe->fKeyboardOpen      = (ioparmThe.out.wParm2 >>  9) & 1;
	psensorinfoThe->fACAdapterAttached = (ioparmThe.out.wParm2 >> 10) & 1;

	return 0;
}

int smapidev_GetScreenRefreshInfo(
	int intFiledesc,
	word wMode,
	smapidev_screenrefreshinfo_t *pscreenrefreshinfoThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;

	/* Check structure size */
	if( pscreenrefreshinfoThe->sizeStruct != SIZE_SCREENREFRESHINFO_V1 ) {
#		if DEBUG_STRUCT_SIZES
		printf( "Declared size %d does not match expected size %d in GetScreenRefreshInfo\n" , pscreenrefreshinfoThe->sizeStruct, SIZE_SCREENREFRESHINFO_V1 );
#		endif
		return ERR_SMAPIDEV_STRUCTURE_SIZE_INVALID;
	}

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0;
	ioparmThe.in.bSubFunc = (byte) 9;
	ioparmThe.in.wParm1 = wMode;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	pscreenrefreshinfoThe->f43i = (ioparmThe.out.wParm2 >> 3) & 1;
	pscreenrefreshinfoThe->f48i = (ioparmThe.out.wParm2 >> 7) & 1;
	pscreenrefreshinfoThe->f56  = (ioparmThe.out.wParm2 >> 4) & 1;
	pscreenrefreshinfoThe->f60  =  ioparmThe.out.wParm2 & 1;
	pscreenrefreshinfoThe->f70  = (ioparmThe.out.wParm2 >> 5) & 1;
	pscreenrefreshinfoThe->f72  = (ioparmThe.out.wParm2 >> 1) & 1;
	pscreenrefreshinfoThe->f75  = (ioparmThe.out.wParm2 >> 2) & 1;
	pscreenrefreshinfoThe->f85  = (ioparmThe.out.wParm2 >> 6) & 1;

	return 0;
}


int smapidev_GetDisplayCapability(
	int intFiledesc,
	smapidev_stateplace_t stateplace,
	smapidev_displaycap_t *pdisplaycapThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;

	/* Check structure size */
	if( pdisplaycapThe->sizeStruct != SIZE_DISPLAYCAP_V1 ) {
#		if DEBUG_STRUCT_SIZES
		printf( "Declared size %d does not match expected size %d in GetDisplayCapability\n" , pdisplaycapThe->sizeStruct, SIZE_DISPLAYCAP_V1 );
#		endif
		return ERR_SMAPIDEV_STRUCTURE_SIZE_INVALID;
	}

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0x10;
	ioparmThe.in.bSubFunc = (byte) 0;
	switch ( stateplace ) {
		case SMAPIDEV_STATEPLACE_CMOS: ioparmThe.in.wParm1 = (word) 1; break;
		case SMAPIDEV_STATEPLACE_CURR: ioparmThe.in.wParm1 = (word) 0; break;
		case SMAPIDEV_STATEPLACE_CMOS_AND_CURR:
		default: return ERR_SMAPIDEV_PARM_INVALID;
	}

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	pdisplaycapThe->fSupported = (flag_t)(ioparmThe.out.wParm2 & 1);
	switch ( ioparmThe.out.wParm2 & 0xFF ) {
		case 0:
			pdisplaycapThe->tv = SMAPIDEV_DISPLAYCAPTV_NONE;
		break;
		case 1:
			pdisplaycapThe->tv = SMAPIDEV_DISPLAYCAPTV_NONSIMULTANEOUS;
		break;
		default:
			pdisplaycapThe->tv = SMAPIDEV_DISPLAYCAPTV_OTHER;
			return ERR_SMAPIDEV_SMAPI_RESULT_NOT_UNDERSTOOD;
	}

	return 0;
}


int smapidev_GetDisplayState(
	int intFiledesc,
	smapidev_stateplace_t stateplace,
	smapidev_dispmode_t dispmodeThe,
	smapidev_ablestate_t *pablestateThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0x10;
	ioparmThe.in.bSubFunc = (byte) 0;
	switch ( stateplace ) {
		case SMAPIDEV_STATEPLACE_CMOS: ioparmThe.in.wParm1 = (word) 1; break;
		case SMAPIDEV_STATEPLACE_CURR: ioparmThe.in.wParm1 = (word) 0; break;
		case SMAPIDEV_STATEPLACE_CMOS_AND_CURR:
		default: return ERR_SMAPIDEV_PARM_INVALID;
	}

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	switch( dispmodeThe )
	{
		case SMAPIDEV_DISPMODE_INTERNAL :
			*pablestateThe = ( ioparmThe.out.wParm2 & 0x100 ) ? SMAPIDEV_ABLESTATE_ENABLED : SMAPIDEV_ABLESTATE_DISABLED ;
		break;

		case SMAPIDEV_DISPMODE_CRT :
			*pablestateThe = ( ioparmThe.out.wParm2 & 0x200 ) ? SMAPIDEV_ABLESTATE_ENABLED : SMAPIDEV_ABLESTATE_DISABLED ;
		break;

		case SMAPIDEV_DISPMODE_TV :
			*pablestateThe = ( ioparmThe.out.wParm2 & 0x400 ) ? SMAPIDEV_ABLESTATE_ENABLED : SMAPIDEV_ABLESTATE_DISABLED ;
		break;

		case SMAPIDEV_DISPMODE_CRT_DETECTION_IGNORE :
			*pablestateThe = ( ioparmThe.out.wParm2 & 0x4000 ) ? SMAPIDEV_ABLESTATE_ENABLED : SMAPIDEV_ABLESTATE_DISABLED ;
		break;

		case SMAPIDEV_DISPMODE_DUAL :
			*pablestateThe = ( ioparmThe.out.wParm2 & 0x8000 ) ? SMAPIDEV_ABLESTATE_ENABLED : SMAPIDEV_ABLESTATE_DISABLED ;
		break;

		case SMAPIDEV_DISPMODE_SELECT_TV :
			*pablestateThe = ( ioparmThe.out.dwParm4 & 1 ) ? SMAPIDEV_ABLESTATE_ENABLED : SMAPIDEV_ABLESTATE_DISABLED ;
		break;

		default:
			return ERR_SMAPIDEV_PARM_INVALID;
	}

	return 0;
}


int smapidev_SetDisplayState(
	int intFiledesc,
	smapidev_stateplace_t stateplace,
	smapidev_dispmode_t dispmodeThe,
	smapidev_ablestate_t ablestateThe
) {
	smapi_ioparm_t ioparmGet;
	smapi_ioparm_t ioparmSet;
	int intRtn;

	/* We can only update CMOS and current state together */
	if ( stateplace != SMAPIDEV_STATEPLACE_CMOS_AND_CURR ) 
		return ERR_SMAPIDEV_PARM_INVALID;
	
	/* No SMAPIDEV_STATE_AUTO or other invalid values are allowed here */
	if( ablestateThe != SMAPIDEV_ABLESTATE_DISABLED && ablestateThe != SMAPIDEV_ABLESTATE_ENABLED )
	{
		return ERR_SMAPIDEV_PARM_INVALID;
	}

	/* Get the current CMOS state */
	memset( &ioparmGet, 0, sizeof( ioparmGet ) );
	ioparmGet.in.bFunc     = (byte)  0x10;
	ioparmGet.in.bSubFunc  = (byte)  0;
	ioparmGet.in.wParm1    = (word) 1; 

	intRtn = ioctl_smapi( intFiledesc, &ioparmGet );
	if ( intRtn ) return intRtn;

	memset( &ioparmSet, 0, sizeof( ioparmSet ) );
	ioparmSet.in.bFunc     = (byte) 0x10;
	ioparmSet.in.bSubFunc  = (byte) 1;
	ioparmSet.in.wParm1    = ioparmGet.out.wParm2 & 0xC700;
	ioparmSet.in.dwParm4   = ioparmGet.out.dwParm4 & 0x0001;

	switch( dispmodeThe )
	{

		case SMAPIDEV_DISPMODE_INTERNAL :
			if( ablestateThe == SMAPIDEV_ABLESTATE_ENABLED )
				ioparmSet.in.wParm1 |=  0x100;
			else
				ioparmSet.in.wParm1 &= ~0x100;
		break;

		case SMAPIDEV_DISPMODE_CRT :
			if( ablestateThe == SMAPIDEV_ABLESTATE_ENABLED )
				ioparmSet.in.wParm1 |=  0x200;
			else
				ioparmSet.in.wParm1 &= ~0x200;
		break;

		case SMAPIDEV_DISPMODE_TV :
			if( ablestateThe == SMAPIDEV_ABLESTATE_ENABLED )
				ioparmSet.in.wParm1 |=  0x400;
			else
				ioparmSet.in.wParm1 &= ~0x400;
		break;

		case SMAPIDEV_DISPMODE_CRT_DETECTION_IGNORE :
			if( ablestateThe == SMAPIDEV_ABLESTATE_ENABLED )
				ioparmSet.in.wParm1 |=  0x4000;
			else
				ioparmSet.in.wParm1 &= ~0x4000;
		break;

		case SMAPIDEV_DISPMODE_DUAL :
			if( ablestateThe == SMAPIDEV_ABLESTATE_ENABLED )
				ioparmSet.in.wParm1 |=  0x8000;
			else
				ioparmSet.in.wParm1 &= ~0x8000;
		break;

		case SMAPIDEV_DISPMODE_SELECT_TV :
			if( ablestateThe == SMAPIDEV_ABLESTATE_ENABLED )
				ioparmSet.in.dwParm4 |=  0x1;
			else
				ioparmSet.in.dwParm4 &= ~0x1;
		break;

		default:
			return ERR_SMAPIDEV_PARM_INVALID;
	}

	return ioctl_smapi( intFiledesc, &ioparmSet );
}


int smapidev_GetPowerExpenditureMode(
	int intFiledesc,
	smapidev_powersrc_t powersrcThe,
	smapidev_powermode_t *ppowermodeThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;
	byte bModeAC, bModeBat, bModeSelected;

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0x22;
	ioparmThe.in.bSubFunc = (byte) 0;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	bModeAC = (byte)(ioparmThe.out.wParm2 & 0xFF);
	bModeBat = (byte)(ioparmThe.out.wParm2 >> 8);
	bModeSelected = (powersrcThe == SMAPIDEV_POWERSRC_AC) ? bModeAC : bModeBat;

	switch ( bModeSelected ) {
		case 0: *ppowermodeThe = SMAPIDEV_POWERMODE_HIGH; break;
		case 1: *ppowermodeThe = SMAPIDEV_POWERMODE_AUTO; break;
		case 2: *ppowermodeThe = SMAPIDEV_POWERMODE_MANUAL; break;
		default:
		case 3: *ppowermodeThe = SMAPIDEV_POWERMODE_UNRECOGNIZED; break;
	}

	return 0;
}



int smapidev_SetPowerExpenditureMode(
	int intFiledesc,
	smapidev_powersrc_t powersrcThe,
	smapidev_powermode_t powermodeThe
) {
	smapi_ioparm_t ioparmThe;
	int intRtn;
	byte bMode;

	bMode =
		(powermodeThe == SMAPIDEV_POWERMODE_HIGH) ? 0 :
		(powermodeThe == SMAPIDEV_POWERMODE_AUTO) ? 1 :
		2
	;

	memset( &ioparmThe, 0, sizeof( ioparmThe ) );
	ioparmThe.in.bFunc = (byte) 0x22;
	ioparmThe.in.bSubFunc = (byte) 0;

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	ioparmThe.in.bFunc = (byte) 0x22;
	ioparmThe.in.bSubFunc = (byte) 1;
	ioparmThe.in.wParm1 = ioparmThe.out.wParm2;
	if ( powersrcThe == SMAPIDEV_POWERSRC_AC ) {
		ioparmThe.in.wParm1 &= 0xff00;
		ioparmThe.in.wParm1 |= bMode;
	} else { /* powersrcThe == SMAPIDEV_POWERSRC_BATTERY */
		ioparmThe.in.wParm1 &= 0x00ff;
		ioparmThe.in.wParm1 |= ((word)bMode) << 8;
	}

	intRtn = ioctl_smapi( intFiledesc, &ioparmThe );
	if ( intRtn ) return intRtn;

	return 0;
}
#else
int smapi_dummy(){}
#endif
