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