/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
*/
/*
* Determine whether this machine is capable of suspending.
*
* This is done for the i386 architecture by determining if the machine has
* S3 capability, then ensuring it is on the whitelist.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/uadmin.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <stropts.h>
#include <string.h>
#include <kstat.h>
#include <stdlib.h>
#include <libuutil.h>
#include <libpower.h>
#include <libpower_impl.h>
#define PM_SE_ACPI "acpi"
/* This is defined in sys/epm.h, but is not visible in user space */
#define SYSTEM_POWER_S3 3
static boolean_t pm_has_sx(int);
/*
* Determine if this machine supports suspend by identifying
* if the hardware supports a usable "S" state. For now, we only
* care about S3, but this function should change when more are
* supported.
*/
boolean_t
pm_get_suspendenable(void)
{
/*
* All we care about currently on x86 platforms, is if the
* kstats indicate suspend is valid. It would probably be
* better if there were some API that returned the platform
* capabilities, but for now the only thing that isn't
* dependent on actual enablement, are the kstats.
*/
return (pm_has_sx(SYSTEM_POWER_S3));
}
/*
* Determine if the hardware supports suspend by probing the acpi kstat module.
* For now, we are only interested in "S3" support on x86, but when there
* are API's that allow for more specific suspend state selection, this
* function will be able to handle checking those states as well.
*/
static boolean_t
pm_has_sx(int state)
{
boolean_t result;
kstat_t *ksp;
kstat_ctl_t *kc;
kstat_named_t *dp;
char *name;
/* Assume capability not valid until proven otherwise */
errno = 0;
if ((kc = kstat_open()) == NULL) {
uu_dprintf(pm_log, UU_DPRINTF_FATAL,
"%s kstat_open failed %d (%s)\n", __FUNCTION__, errno,
strerror(errno));
return (B_FALSE);
}
if ((ksp = kstat_lookup(kc, PM_SE_ACPI, -1, PM_SE_ACPI)) == NULL) {
uu_dprintf(pm_log, UU_DPRINTF_FATAL,
"%s kstat_lookup \"%s\" failed errno %d (%s)\n",
__FUNCTION__, PM_SE_ACPI, errno, strerror(errno));
(void) kstat_close(kc);
return (B_FALSE);
}
if (kstat_read(kc, ksp, NULL) == -1) {
uu_dprintf(pm_log, UU_DPRINTF_FATAL,
"%s kstat_read \"%s\" failed errno %d (%s)\n",
__FUNCTION__, PM_SE_ACPI, errno, strerror(errno));
(void) kstat_close(kc);
return (B_FALSE);
}
/*
* Construct a name to pass for the interested state
*/
if (asprintf(&name, "S%-d", state) <= 0) {
(void) kstat_close(kc);
return (B_FALSE);
}
dp = kstat_data_lookup(ksp, name);
if (dp == NULL) {
uu_dprintf(pm_log, UU_DPRINTF_NOTICE,
"%s kstat_data_lookup machine does not support \"%s\"\n",
__FUNCTION__, name);
/*
* Not having this state is not an error, but we done
* checking now
*/
(void) kstat_close(kc);
free(name);
return (B_FALSE);
}
(void) kstat_close(kc);
result = B_FALSE;
if (dp->value.l >= 1) {
uu_dprintf(pm_log, UU_DPRINTF_NOTICE,
"%s kstat_data_lookup machine supports \"%s\" %ld\n",
__FUNCTION__, name, dp->value.l);
/*
* The kstat indicates this machine has this "S" state
* support.
*/
result = B_TRUE;
}
free(name);
return (result);
}