Source to machdep/i386/APM_i386.c


Enter a symbol's name here to quickly find it.

/*
 * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
 *
 * @[email protected]
 * 
 * "Portions Copyright (c) 1999 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 1.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.apple.com/publicsource 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 OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License."
 * 
 * @[email protected]
 */

/*
 * Copyright 1994 NeXT Computer, Inc.
 * All rights reserved.
 */

/*
 * Power Management (PM) kernel interface.
 * This implementation uses the Microsoft/Intel APM 1.1 interface.
 */

#import <bsd/sys/time.h>

#import <kern/power.h>

#import <mach/mach_types.h>

#import <machdep/i386/pmap.h>
#import <machdep/i386/seg.h>
#import <machdep/i386/table_inline.h>
#import <machdep/i386/desc_inline.h>
#import <machdep/i386/kernBootStruct.h>
#import <machdep/i386/bios.h>
#import <machdep/i386/APM_BIOS.h>

static boolean_t APM_BIOS_connected;
static struct {
    unsigned major;
    unsigned minor;
} APM_BIOS_version;

#define VERSION_EQ(maj,min) ((APM_BIOS_version.major == (maj)) && \
			  (APM_BIOS_version.minor == (min)))
#define VERSION_GE(maj,min) ((APM_BIOS_version.major > (maj)) || \
			    ((APM_BIOS_version.major == (maj)) && \
			     (APM_BIOS_version.minor >= (min))))
/*
 * Note: we assume that the power management version is 1.0,
 * because some machine report a bogus version.
 * If we do the standard 1.0 "APM Connect" BIOS call in the booter,
 * then the BIOS should give us APM 1.0 behavior.
 */

static void
setUpGdtEntries()
{
    KERNBOOTSTRUCT *kbp = KERNSTRUCT_ADDR;
    
    map_code((data_desc_t *) sel_to_gdt_entry(APMCODE32_SEL),
	     (vm_offset_t) KERNEL_LINEAR_BASE + kbp->apm_config.cs32_base,
	     (vm_size_t) kbp->apm_config.cs_length,
	     KERN_PRIV,
	     FALSE
	    );

    map_code_16((data_desc_t *) sel_to_gdt_entry(APMCODE16_SEL),
	     (vm_offset_t) KERNEL_LINEAR_BASE + kbp->apm_config.cs16_base,
	     (vm_size_t) kbp->apm_config.cs_length,
	     KERN_PRIV,
	     FALSE
	    );

    map_data((data_desc_t *) sel_to_gdt_entry(APMDATA_SEL),
	     (vm_offset_t) KERNEL_LINEAR_BASE + kbp->apm_config.ds_base,
	     (vm_size_t) kbp->apm_config.ds_length,
	     KERN_PRIV,
	     FALSE
	    );
	    
    APM_BIOS_addr = kbp->apm_config.entry_offset;
}

/*
 * Initialize PM support.  Returns TRUE if PM is available.
 */
PMReturn PMConnect(void)
{
    KERNBOOTSTRUCT *kbp = KERNSTRUCT_ADDR;

    APM_BIOS_version.major = kbp->apm_config.major_vers;
    APM_BIOS_version.minor = kbp->apm_config.minor_vers;
    
    if (kbp->apm_config.connected) {
	APM_BIOS_connected = TRUE;
	setUpGdtEntries();
	printf("Power management is enabled.\n");
	return PM_R_SUCCESS;
    }
    return PM_R_NO_PM;
}

/*
 * Return PM control to previous user (probably the BIOS).
 */
PMReturn PMDisconnect(void)
{
    return PM_R_SUCCESS;
}

/*
 * Inform PM of the CPU state.  If the CPU is idle, PM may
 * put the CPU in a power-saving state until the next system event
 * (typically an interrupt).  Marking the CPU busy will ensure
 * that the CPU is running at full speed.
 */
PMReturn PMSetCpuState(
    PMCpuState state
)
{
    boolean_t ret;
    
    if (APM_BIOS_connected) {
	switch (state) {
	case PM_CPU_IDLE:
	    ret = APMBIOS_Idle();
	    break;
	case PM_CPU_BUSY:
	    ret = APMBIOS_Busy();
	    break;
	default:
	    ret = FALSE;
	}
	if (ret == FALSE)
	    return PM_R_BAD_STATE;
    } else {
	if (state == PM_CPU_IDLE) {
	    asm volatile("hlt");
	}
    }
    return PM_R_SUCCESS;
}

/*
 * Set the power state of a device or the entire computer.
 */
PMReturn PMSetPowerState(
    PMDeviceID device,
    PMPowerState state
)
{
    union {
	struct {
	    unsigned int 	deviceNumber	:8;
	    unsigned int 	deviceType	:8;
	} device;
	unsigned short data;
    } du;
    union {
	PMPowerState state;
	unsigned short data;
    } su;
    PMReturn ret;
    
    /* Eventually this should differentiate between devices. */
    if (device.deviceType == PM_SYSTEM && device.deviceNumber == 1) {
	    _io_setDriverPowerState(state);
    }

    if (APM_BIOS_connected) {
	if (device.deviceType == PM_SYSTEM && device.deviceNumber == 1) {
	    /*
	     * In PM 1.1 and 1.0,
	     * you can't set the system state to "ready".
	     */
	    if (state == PM_READY)
		return PM_R_BAD_STATE;
	    /*
	     * In PM 1.0, you can't set the system state to "off".
	     */
	    if (state == PM_OFF)
		return PM_R_BAD_STATE;
	}
	du.device.deviceNumber = device.deviceNumber;
	du.device.deviceType = device.deviceType;
	su.state = state;
	return APMBIOS_SetPowerState(du.data, su.data);
    }
    return PM_R_NOT_CONNECTED;
}

/*
 * Get a power event from PM, if one has occurred.
 */
PMReturn PMGetPowerEvent(
    PMPowerEvent *event
)
{
    unsigned short event_code;
    PMReturn ret;

    if (APM_BIOS_connected) {
	if ((ret = APMBIOS_GetPowerEvent(&event_code)) == PM_R_SUCCESS) {
	    *event = (PMPowerEvent)event_code;
	    return PM_R_SUCCESS;
	}
	return ret;
    }
    return PM_R_NOT_CONNECTED;
}

/*
 * Get the power state of the computer.
 */
PMReturn PMGetPowerStatus(
    PMPowerStatus *status
)
{
    unsigned char line_status, batt_status, batt_life;
    PMReturn ret;
    
    if (APM_BIOS_connected) {
	if ((ret = APMBIOS_GetPowerStatus(&line_status, &batt_status, 
					  &batt_life)) == PM_R_SUCCESS) {
	    status->lineStatus = (PMLineStatus)line_status;
	    status->batteryStatus = (PMBatteryStatus)batt_status;
	    status->batteryLife = (batt_life == 0xFF) ? -1 : (int)batt_life;
	    return PM_R_SUCCESS;
	} else {
	    return ret;
	}
    }
    return PM_R_NOT_CONNECTED;
}

/*
 * Enable or disable power management by the computer.
 * If enable == PM_ENABLED, the computer can take power saving steps
 * when the CPU state is idle.  If enable == PM_DISABLED, no automatic
 * power saving steps will be taken and power management functions
 * will be disabled.
 */
PMReturn PMSetPowerManagement(
    PMDeviceID device,
    PMPowerManagementState state
)
{
    if (APM_BIOS_connected) {
	if (device.deviceType == PM_SYSTEM && device.deviceNumber == 1)
	    return APMBIOS_SetSystemPowerManagement(state);
	else
	    return PM_R_BAD_ID;
    }

    return PM_R_NOT_CONNECTED;
}

/*
 * Restores the default settings for all power management settings.
 */
PMReturn PMRestoreDefaults(void)
{
    if (APM_BIOS_connected)
	return APMBIOS_RestoreDefaults();
    else
	return PM_R_NOT_CONNECTED;
}

/*
 * Update current kernel clock from real-time clock.
 */
void PMUpdateClock(void)
{
/* THIS IS WRONG!!
    extern struct timeval	time;
    readtodc(&time.tv_sec);
*/
}