Index: head/sys/dev/acpica/acpi_pci.c =================================================================== --- head/sys/dev/acpica/acpi_pci.c (revision 130207) +++ head/sys/dev/acpica/acpi_pci.c (revision 130208) @@ -1,283 +1,280 @@ /* * Copyright (c) 1997, Stefan Esser * Copyright (c) 2000, Michael Smith * Copyright (c) 2000, BSDi * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "acpi.h" #include #include #include #include #include #include "pcib_if.h" #include "pci_if.h" /* Hooks for the ACPI CA debugging infrastructure. */ #define _COMPONENT ACPI_BUS ACPI_MODULE_NAME("PCI") struct acpi_pci_devinfo { struct pci_devinfo ap_dinfo; ACPI_HANDLE ap_handle; }; static int acpi_pci_probe(device_t dev); static int acpi_pci_attach(device_t dev); static int acpi_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result); static int acpi_pci_child_location_str_method(device_t cbdev, device_t child, char *buf, size_t buflen); static int acpi_pci_set_powerstate_method(device_t dev, device_t child, int state); static ACPI_STATUS acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context, void **status); static device_method_t acpi_pci_methods[] = { /* Device interface */ DEVMETHOD(device_probe, acpi_pci_probe), DEVMETHOD(device_attach, acpi_pci_attach), DEVMETHOD(device_shutdown, bus_generic_shutdown), DEVMETHOD(device_suspend, pci_suspend), DEVMETHOD(device_resume, pci_resume), /* Bus interface */ DEVMETHOD(bus_print_child, pci_print_child), DEVMETHOD(bus_probe_nomatch, pci_probe_nomatch), DEVMETHOD(bus_read_ivar, acpi_pci_read_ivar), DEVMETHOD(bus_write_ivar, pci_write_ivar), DEVMETHOD(bus_driver_added, pci_driver_added), DEVMETHOD(bus_setup_intr, bus_generic_setup_intr), DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr), DEVMETHOD(bus_get_resource_list,pci_get_resource_list), DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource), DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource), DEVMETHOD(bus_delete_resource, pci_delete_resource), DEVMETHOD(bus_alloc_resource, pci_alloc_resource), DEVMETHOD(bus_release_resource, bus_generic_rl_release_resource), DEVMETHOD(bus_activate_resource, bus_generic_activate_resource), DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource), DEVMETHOD(bus_child_pnpinfo_str, pci_child_pnpinfo_str_method), DEVMETHOD(bus_child_location_str, acpi_pci_child_location_str_method), /* PCI interface */ DEVMETHOD(pci_read_config, pci_read_config_method), DEVMETHOD(pci_write_config, pci_write_config_method), DEVMETHOD(pci_enable_busmaster, pci_enable_busmaster_method), DEVMETHOD(pci_disable_busmaster, pci_disable_busmaster_method), DEVMETHOD(pci_enable_io, pci_enable_io_method), DEVMETHOD(pci_disable_io, pci_disable_io_method), DEVMETHOD(pci_get_powerstate, pci_get_powerstate_method), DEVMETHOD(pci_set_powerstate, acpi_pci_set_powerstate_method), DEVMETHOD(pci_assign_interrupt, pci_assign_interrupt_method), { 0, 0 } }; static driver_t acpi_pci_driver = { "pci", acpi_pci_methods, 0, /* no softc */ }; DRIVER_MODULE(acpi_pci, pcib, acpi_pci_driver, pci_devclass, 0, 0); MODULE_DEPEND(acpi_pci, acpi, 1, 1, 1); MODULE_DEPEND(acpi_pci, pci, 1, 1, 1); MODULE_VERSION(acpi_pci, 1); static int acpi_pci_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) { struct acpi_pci_devinfo *dinfo; switch (which) { case ACPI_IVAR_HANDLE: dinfo = device_get_ivars(child); *result = (uintptr_t)dinfo->ap_handle; return (0); } return (pci_read_ivar(dev, child, which, result)); } static int acpi_pci_child_location_str_method(device_t cbdev, device_t child, char *buf, size_t buflen) { struct acpi_pci_devinfo *dinfo = device_get_ivars(child); pci_child_location_str_method(cbdev, child, buf, buflen); if (dinfo->ap_handle) { strlcat(buf, " path=", buflen); strlcat(buf, acpi_name(dinfo->ap_handle), buflen); } return (0); } /* * PCI power manangement */ static int acpi_pci_set_powerstate_method(device_t dev, device_t child, int state) { ACPI_HANDLE h; ACPI_STATUS status; int acpi_state, old_state, error; switch (state) { case PCI_POWERSTATE_D0: acpi_state = ACPI_STATE_D0; break; case PCI_POWERSTATE_D1: acpi_state = ACPI_STATE_D1; break; case PCI_POWERSTATE_D2: acpi_state = ACPI_STATE_D2; break; case PCI_POWERSTATE_D3: acpi_state = ACPI_STATE_D3; break; default: return (EINVAL); } /* * We set the state using PCI Power Management outside of setting * the ACPI state. This means that when powering down a device, we * first shut it down using PCI, and then using ACPI, which lets ACPI * try to power down any Power Resources that are now no longer used. * When powering up a device, we let ACPI set the state first so that * it can enable any needed Power Resources before changing the PCI * power state. */ old_state = pci_get_powerstate(child); if (old_state < state) { error = pci_set_powerstate_method(dev, child, state); if (error) return (error); } - h = acpi_get_handle(child); - if (h != NULL) { - status = acpi_pwr_switch_consumer(h, acpi_state); - if (ACPI_FAILURE(status)) - device_printf(dev, - "Failed to set ACPI power state D%d on %s: %s\n", - acpi_state, device_get_nameunit(child), - AcpiFormatException(status)); - } + h = acpi_get_handle(dev); + status = acpi_pwr_switch_consumer(h, acpi_state); + if (ACPI_FAILURE(status) && status != AE_NOT_FOUND) + device_printf(dev, + "Failed to set ACPI power state D%d on %s: %s\n", + acpi_state, acpi_name(h), AcpiFormatException(status)); if (old_state > state) return (pci_set_powerstate_method(dev, child, state)); else return (0); } static ACPI_STATUS acpi_pci_save_handle(ACPI_HANDLE handle, UINT32 level, void *context, void **status) { struct acpi_pci_devinfo *dinfo; device_t *devlist; int devcount, i, func, slot; UINT32 address; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); if (ACPI_FAILURE(acpi_GetInteger(handle, "_ADR", &address))) return_ACPI_STATUS (AE_OK); slot = address >> 16; func = address & 0xffff; if (device_get_children((device_t)context, &devlist, &devcount) != 0) return_ACPI_STATUS (AE_OK); for (i = 0; i < devcount; i++) { dinfo = device_get_ivars(devlist[i]); if (dinfo->ap_dinfo.cfg.func == func && dinfo->ap_dinfo.cfg.slot == slot) { dinfo->ap_handle = handle; free(devlist, M_TEMP); return_ACPI_STATUS (AE_OK); } } free(devlist, M_TEMP); return_ACPI_STATUS (AE_OK); } static int acpi_pci_probe(device_t dev) { if (pcib_get_bus(dev) < 0) return (ENXIO); if (acpi_get_handle(dev) == NULL) return (ENXIO); device_set_desc(dev, "ACPI PCI bus"); return (0); } static int acpi_pci_attach(device_t dev) { int busno; /* * Since there can be multiple independantly numbered PCI * busses on some large alpha systems, we can't use the unit * number to decide what bus we are probing. We ask the parent * pcib what our bus number is. */ busno = pcib_get_bus(dev); if (bootverbose) device_printf(dev, "physical bus=%d\n", busno); /* * First, PCI devices are added as in the normal PCI bus driver. * Afterwards, the ACPI namespace under the bridge driver is * walked to save ACPI handles to all the devices that appear in * the ACPI namespace as immediate descendants of the bridge. * * XXX: Sometimes PCI devices show up in the ACPI namespace that * pci_add_children() doesn't find. We currently just ignore * these devices. */ pci_add_children(dev, busno, sizeof(struct acpi_pci_devinfo)); AcpiWalkNamespace(ACPI_TYPE_DEVICE, acpi_get_handle(dev), 1, acpi_pci_save_handle, dev, NULL); return (bus_generic_attach(dev)); } Index: head/sys/dev/acpica/acpi_powerres.c =================================================================== --- head/sys/dev/acpica/acpi_powerres.c (revision 130207) +++ head/sys/dev/acpica/acpi_powerres.c (revision 130208) @@ -1,677 +1,680 @@ /*- * Copyright (c) 2001 Michael Smith * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_acpi.h" #include #include #include #include #include "acpi.h" #include /* * ACPI power resource management. * * Power resource behaviour is slightly complicated by the fact that * a single power resource may provide power for more than one device. * Thus, we must track the device(s) being powered by a given power * resource, and only deactivate it when there are no powered devices. * * Note that this only manages resources for known devices. There is an * ugly case where we may turn of power to a device which is in use because * we don't know that it depends on a given resource. We should perhaps * try to be smarter about this, but a more complete solution would involve * scanning all of the ACPI namespace to find devices we're not currently * aware of, and this raises questions about whether they should be left * on, turned off, etc. * * XXX locking */ MALLOC_DEFINE(M_ACPIPWR, "acpipwr", "ACPI power resources"); /* Hooks for the ACPI CA debugging infrastructure */ #define _COMPONENT ACPI_POWERRES ACPI_MODULE_NAME("POWERRES") /* Return values from _STA on a power resource */ #define ACPI_PWR_OFF 0 #define ACPI_PWR_ON 1 /* A relationship between a power resource and a consumer. */ struct acpi_powerreference { struct acpi_powerconsumer *ar_consumer; struct acpi_powerresource *ar_resource; TAILQ_ENTRY(acpi_powerreference) ar_rlink; /* link on resource list */ TAILQ_ENTRY(acpi_powerreference) ar_clink; /* link on consumer */ }; /* A power-managed device. */ struct acpi_powerconsumer { /* Device which is powered */ ACPI_HANDLE ac_consumer; int ac_state; TAILQ_ENTRY(acpi_powerconsumer) ac_link; TAILQ_HEAD(,acpi_powerreference) ac_references; }; /* A power resource. */ struct acpi_powerresource { TAILQ_ENTRY(acpi_powerresource) ap_link; TAILQ_HEAD(,acpi_powerreference) ap_references; ACPI_HANDLE ap_resource; ACPI_INTEGER ap_systemlevel; ACPI_INTEGER ap_order; }; static TAILQ_HEAD(acpi_powerresource_list, acpi_powerresource) acpi_powerresources; static TAILQ_HEAD(acpi_powerconsumer_list, acpi_powerconsumer) acpi_powerconsumers; static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer); #ifdef notyet static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer); #endif /* notyet */ static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res); #ifdef notyet static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res); #endif /* notyet */ static void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg); static ACPI_STATUS acpi_pwr_switch_power(void); static struct acpi_powerresource *acpi_pwr_find_resource(ACPI_HANDLE res); static struct acpi_powerconsumer *acpi_pwr_find_consumer(ACPI_HANDLE consumer); /* Initialise our lists. */ static void acpi_pwr_init(void *junk) { TAILQ_INIT(&acpi_powerresources); TAILQ_INIT(&acpi_powerconsumers); } SYSINIT(acpi_powerresource, SI_SUB_TUNABLES, SI_ORDER_ANY, acpi_pwr_init, NULL); /* * Register a power resource. * * It's OK to call this if we already know about the resource. */ static ACPI_STATUS acpi_pwr_register_resource(ACPI_HANDLE res) { ACPI_STATUS status; ACPI_BUFFER buf; ACPI_OBJECT *obj; struct acpi_powerresource *rp, *srp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); rp = NULL; buf.Pointer = NULL; /* Look to see if we know about this resource */ if (acpi_pwr_find_resource(res) != NULL) return_ACPI_STATUS (AE_OK); /* already know about it */ /* Allocate a new resource */ if ((rp = malloc(sizeof(*rp), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { status = AE_NO_MEMORY; goto out; } TAILQ_INIT(&rp->ap_references); rp->ap_resource = res; /* Get the Power Resource object */ buf.Length = ACPI_ALLOCATE_BUFFER; if (ACPI_FAILURE(status = AcpiEvaluateObject(res, NULL, NULL, &buf))) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "no power resource object\n")); goto out; } obj = buf.Pointer; if (obj->Type != ACPI_TYPE_POWER) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "questionable power resource object %s\n", acpi_name(res))); status = AE_TYPE; goto out; } rp->ap_systemlevel = obj->PowerResource.SystemLevel; rp->ap_order = obj->PowerResource.ResourceOrder; /* Sort the resource into the list */ status = AE_OK; srp = TAILQ_FIRST(&acpi_powerresources); if (srp == NULL || rp->ap_order < srp->ap_order) { TAILQ_INSERT_HEAD(&acpi_powerresources, rp, ap_link); goto done; } TAILQ_FOREACH(srp, &acpi_powerresources, ap_link) { if (rp->ap_order < srp->ap_order) { TAILQ_INSERT_BEFORE(srp, rp, ap_link); goto done; } } TAILQ_INSERT_TAIL(&acpi_powerresources, rp, ap_link); done: ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power resource %s\n", acpi_name(res))); out: if (buf.Pointer != NULL) AcpiOsFree(buf.Pointer); if (ACPI_FAILURE(status) && rp != NULL) free(rp, M_ACPIPWR); return_ACPI_STATUS (status); } #ifdef notyet /* * Deregister a power resource. */ static ACPI_STATUS acpi_pwr_deregister_resource(ACPI_HANDLE res) { struct acpi_powerresource *rp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); rp = NULL; /* Find the resource */ if ((rp = acpi_pwr_find_resource(res)) == NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); /* Check that there are no consumers referencing this resource */ if (TAILQ_FIRST(&rp->ap_references) != NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); /* Pull it off the list and free it */ TAILQ_REMOVE(&acpi_powerresources, rp, ap_link); free(rp, M_ACPIPWR); ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power resource %s\n", acpi_name(res))); return_ACPI_STATUS (AE_OK); } #endif /* notyet */ /* * Register a power consumer. * * It's OK to call this if we already know about the consumer. */ static ACPI_STATUS acpi_pwr_register_consumer(ACPI_HANDLE consumer) { struct acpi_powerconsumer *pc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* Check to see whether we know about this consumer already */ if ((pc = acpi_pwr_find_consumer(consumer)) != NULL) return_ACPI_STATUS (AE_OK); /* Allocate a new power consumer */ if ((pc = malloc(sizeof(*pc), M_ACPIPWR, M_NOWAIT)) == NULL) return_ACPI_STATUS (AE_NO_MEMORY); TAILQ_INSERT_HEAD(&acpi_powerconsumers, pc, ac_link); TAILQ_INIT(&pc->ac_references); pc->ac_consumer = consumer; /* XXX we should try to find its current state */ pc->ac_state = ACPI_STATE_UNKNOWN; ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "registered power consumer %s\n", acpi_name(consumer))); return_ACPI_STATUS (AE_OK); } #ifdef notyet /* * Deregister a power consumer. * * This should only be done once the consumer has been powered off. * (XXX is this correct? Check once implemented) */ static ACPI_STATUS acpi_pwr_deregister_consumer(ACPI_HANDLE consumer) { struct acpi_powerconsumer *pc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* Find the consumer */ if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); /* Make sure the consumer's not referencing anything right now */ if (TAILQ_FIRST(&pc->ac_references) != NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); /* Pull the consumer off the list and free it */ TAILQ_REMOVE(&acpi_powerconsumers, pc, ac_link); ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "deregistered power consumer %s\n", acpi_name(consumer))); return_ACPI_STATUS (AE_OK); } #endif /* notyet */ /* * Set a power consumer to a particular power state. */ ACPI_STATUS acpi_pwr_switch_consumer(ACPI_HANDLE consumer, int state) { struct acpi_powerconsumer *pc; struct acpi_powerreference *pr; ACPI_HANDLE method_handle, reslist_handle, pr0_handle; ACPI_BUFFER reslist_buffer; ACPI_OBJECT *reslist_object; ACPI_STATUS status; char *method_name, *reslist_name; int res_changed; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* It's never ok to switch a non-existent consumer. */ if (consumer == NULL) return_ACPI_STATUS (AE_NOT_FOUND); /* Find the consumer */ if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { if (ACPI_FAILURE(status = acpi_pwr_register_consumer(consumer))) return_ACPI_STATUS (status); if ((pc = acpi_pwr_find_consumer(consumer)) == NULL) { return_ACPI_STATUS (AE_ERROR); /* something very wrong */ } } /* Check for valid transitions */ if (pc->ac_state == ACPI_STATE_D3 && state != ACPI_STATE_D0) return_ACPI_STATUS (AE_BAD_PARAMETER); /* can only go to D0 from D3 */ /* Find transition mechanism(s) */ - switch(state) { + switch (state) { case ACPI_STATE_D0: method_name = "_PS0"; reslist_name = "_PR0"; break; case ACPI_STATE_D1: method_name = "_PS1"; reslist_name = "_PR1"; break; case ACPI_STATE_D2: method_name = "_PS2"; reslist_name = "_PR2"; break; case ACPI_STATE_D3: method_name = "_PS3"; reslist_name = "_PR3"; break; default: return_ACPI_STATUS (AE_BAD_PARAMETER); } ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "setup to switch %s D%d -> D%d\n", acpi_name(consumer), pc->ac_state, state)); /* * Verify that this state is supported, ie. one of method or * reslist must be present. We need to do this before we go * dereferencing resources (since we might be trying to go to * a state we don't support). * * Note that if any states are supported, the device has to * support D0 and D3. It's never an error to try to go to * D0. */ + status = AE_BAD_PARAMETER; reslist_buffer.Pointer = NULL; reslist_object = NULL; if (ACPI_FAILURE(AcpiGetHandle(consumer, method_name, &method_handle))) method_handle = NULL; if (ACPI_FAILURE(AcpiGetHandle(consumer, reslist_name, &reslist_handle))) reslist_handle = NULL; if (reslist_handle == NULL && method_handle == NULL) { if (state == ACPI_STATE_D0) { pc->ac_state = ACPI_STATE_D0; return_ACPI_STATUS (AE_OK); } if (state != ACPI_STATE_D3) goto bad; - /* Turn off the resources listed in _PR0 to go to D3. */ - if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) + /* + * Turn off the resources listed in _PR0 to go to D3. If there is + * no _PR0 method, this object doesn't support ACPI power states. + */ + if (ACPI_FAILURE(AcpiGetHandle(consumer, "_PR0", &pr0_handle))) { + status = AE_NOT_FOUND; goto bad; + } reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; status = AcpiEvaluateObject(pr0_handle, NULL, NULL, &reslist_buffer); if (ACPI_FAILURE(status)) goto bad; reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; if (reslist_object->Type != ACPI_TYPE_PACKAGE || - reslist_object->Package.Count == 0) { - + reslist_object->Package.Count == 0) goto bad; - } AcpiOsFree(reslist_buffer.Pointer); reslist_buffer.Pointer = NULL; reslist_object = NULL; } /* * Check that we can actually fetch the list of power resources */ if (reslist_handle != NULL) { reslist_buffer.Length = ACPI_ALLOCATE_BUFFER; status = AcpiEvaluateObject(reslist_handle, NULL, NULL, &reslist_buffer); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't evaluate resource list %s\n", acpi_name(reslist_handle))); goto out; } reslist_object = (ACPI_OBJECT *)reslist_buffer.Pointer; if (reslist_object->Type != ACPI_TYPE_PACKAGE) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "resource list is not ACPI_TYPE_PACKAGE (%d)\n", reslist_object->Type)); status = AE_TYPE; goto out; } } /* * Now we are ready to switch, so kill off any current power * resource references. */ res_changed = 0; - while((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { + while ((pr = TAILQ_FIRST(&pc->ac_references)) != NULL) { res_changed = 1; ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "removing reference to %s\n", acpi_name(pr->ar_resource->ap_resource))); TAILQ_REMOVE(&pr->ar_resource->ap_references, pr, ar_rlink); TAILQ_REMOVE(&pc->ac_references, pr, ar_clink); free(pr, M_ACPIPWR); } /* * Add new power resource references, if we have any. Traverse the * package that we got from evaluating reslist_handle, and look up each * of the resources that are referenced. */ if (reslist_object != NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "referencing %d new resources\n", reslist_object->Package.Count)); acpi_ForeachPackageObject(reslist_object, acpi_pwr_reference_resource, pc); res_changed = 1; } /* * If we changed anything in the resource list, we need to run a switch * pass now. */ if (ACPI_FAILURE(status = acpi_pwr_switch_power())) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch resources from %s to D%d\n", acpi_name(consumer), state)); /* XXX is this appropriate? Should we return to previous state? */ goto out; } /* Invoke power state switch method (if present) */ if (method_handle != NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "invoking state transition method %s\n", acpi_name(method_handle))); status = AcpiEvaluateObject(method_handle, NULL, NULL, NULL); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to set state - %s\n", AcpiFormatException(status))); pc->ac_state = ACPI_STATE_UNKNOWN; /* XXX Should we return to previous state? */ goto out; } } /* Transition was successful */ pc->ac_state = state; return_ACPI_STATUS (AE_OK); bad: ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "attempt to set unsupported state D%d\n", state)); - status = AE_BAD_PARAMETER; out: if (reslist_buffer.Pointer != NULL) AcpiOsFree(reslist_buffer.Pointer); return_ACPI_STATUS (status); } /* * Called to create a reference between a power consumer and a power resource * identified in the object. */ static void acpi_pwr_reference_resource(ACPI_OBJECT *obj, void *arg) { struct acpi_powerconsumer *pc = (struct acpi_powerconsumer *)arg; struct acpi_powerreference *pr; struct acpi_powerresource *rp; ACPI_HANDLE res; ACPI_STATUS status; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); res = acpi_GetReference(NULL, obj); if (res == NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't create a power reference for object type %d\n", obj->Type)); return_VOID; } /* Create/look up the resource */ if (ACPI_FAILURE(status = acpi_pwr_register_resource(res))) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "couldn't register power resource %s - %s\n", obj->String.Pointer, AcpiFormatException(status))); return_VOID; } if ((rp = acpi_pwr_find_resource(res)) == NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "power resource list corrupted\n")); return_VOID; } ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "found power resource %s\n", acpi_name(rp->ap_resource))); /* Create a reference between the consumer and resource */ if ((pr = malloc(sizeof(*pr), M_ACPIPWR, M_NOWAIT | M_ZERO)) == NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "allocation failed for a power consumer reference\n")); return_VOID; } pr->ar_consumer = pc; pr->ar_resource = rp; TAILQ_INSERT_TAIL(&pc->ac_references, pr, ar_clink); TAILQ_INSERT_TAIL(&rp->ap_references, pr, ar_rlink); return_VOID; } /* * Switch power resources to conform to the desired state. * * Consumers may have modified the power resource list in an arbitrary * fashion; we sweep it in sequence order. */ static ACPI_STATUS acpi_pwr_switch_power(void) { struct acpi_powerresource *rp; ACPI_STATUS status; int cur; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); /* * Sweep the list forwards turning things on. */ TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { if (TAILQ_FIRST(&rp->ap_references) == NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has no references, not turning on\n", acpi_name(rp->ap_resource))); continue; } /* We could cache this if we trusted it not to change under us */ status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", acpi_name(rp->ap_resource), status)); /* XXX is this correct? Always switch if in doubt? */ continue; } /* * Switch if required. Note that we ignore the result of the switch * effort; we don't know what to do if it fails, so checking wouldn't * help much. */ if (cur != ACPI_PWR_ON) { status = AcpiEvaluateObject(rp->ap_resource, "_ON", NULL, NULL); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s on - %s\n", acpi_name(rp->ap_resource), AcpiFormatException(status))); } else { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s on\n", acpi_name(rp->ap_resource))); } } else { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already on\n", acpi_name(rp->ap_resource))); } } /* Sweep the list backwards turning things off. */ TAILQ_FOREACH_REVERSE(rp, &acpi_powerresources, acpi_powerresource_list, ap_link) { if (TAILQ_FIRST(&rp->ap_references) != NULL) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s has references, not turning off\n", acpi_name(rp->ap_resource))); continue; } /* We could cache this if we trusted it not to change under us */ status = acpi_GetInteger(rp->ap_resource, "_STA", &cur); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "can't get status of %s - %d\n", acpi_name(rp->ap_resource), status)); /* XXX is this correct? Always switch if in doubt? */ continue; } /* * Switch if required. Note that we ignore the result of the switch * effort; we don't know what to do if it fails, so checking wouldn't * help much. */ if (cur != ACPI_PWR_OFF) { status = AcpiEvaluateObject(rp->ap_resource, "_OFF", NULL, NULL); if (ACPI_FAILURE(status)) { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "failed to switch %s off - %s\n", acpi_name(rp->ap_resource), AcpiFormatException(status))); } else { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "switched %s off\n", acpi_name(rp->ap_resource))); } } else { ACPI_DEBUG_PRINT((ACPI_DB_OBJECTS, "%s is already off\n", acpi_name(rp->ap_resource))); } } return_ACPI_STATUS (AE_OK); } /* * Find a power resource's control structure. */ static struct acpi_powerresource * acpi_pwr_find_resource(ACPI_HANDLE res) { struct acpi_powerresource *rp; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); TAILQ_FOREACH(rp, &acpi_powerresources, ap_link) { if (rp->ap_resource == res) break; } return_PTR (rp); } /* * Find a power consumer's control structure. */ static struct acpi_powerconsumer * acpi_pwr_find_consumer(ACPI_HANDLE consumer) { struct acpi_powerconsumer *pc; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); TAILQ_FOREACH(pc, &acpi_powerconsumers, ac_link) { if (pc->ac_consumer == consumer) break; } return_PTR (pc); }