diff options
Diffstat (limited to 'drivers/acpi')
153 files changed, 82730 insertions, 0 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig new file mode 100644 index 00000000000..0400a52d508 --- /dev/null +++ b/drivers/acpi/Kconfig @@ -0,0 +1,356 @@ +# +# ACPI Configuration +# + +menu "ACPI (Advanced Configuration and Power Interface) Support" + depends on !X86_VISWS + depends on !IA64_HP_SIM + depends on IA64 || X86 + +config ACPI + bool "ACPI Support" + depends on IA64 || X86 + + default y + ---help--- + Advanced Configuration and Power Interface (ACPI) support for + Linux requires an ACPI compliant platform (hardware/firmware), + and assumes the presence of OS-directed configuration and power + management (OSPM) software. This option will enlarge your + kernel by about 70K. + + Linux ACPI provides a robust functional replacement for several + legacy configuration and power management interfaces, including + the Plug-and-Play BIOS specification (PnP BIOS), the + MultiProcessor Specification (MPS), and the Advanced Power + Management (APM) specification. If both ACPI and APM support + are configured, whichever is loaded first shall be used. + + The ACPI SourceForge project contains the latest source code, + documentation, tools, mailing list subscription, and other + information. This project is available at: + <http://sourceforge.net/projects/acpi> + + Linux support for ACPI is based on Intel Corporation's ACPI + Component Architecture (ACPI CA). For more information see: + <http://developer.intel.com/technology/iapc/acpi> + + ACPI is an open industry specification co-developed by Compaq, + Intel, Microsoft, Phoenix, and Toshiba. The specification is + available at: + <http://www.acpi.info> + +config ACPI_BOOT + bool + depends on ACPI || X86_HT + default y + +if ACPI + +config ACPI_INTERPRETER + bool + depends on !IA64_SGI_SN + default y + +if ACPI_INTERPRETER + +config ACPI_SLEEP + bool "Sleep States (EXPERIMENTAL)" + depends on X86 + depends on EXPERIMENTAL && PM + default y + ---help--- + This option adds support for ACPI suspend states. + + With this option, you will be able to put the system "to sleep". + Sleep states are low power states for the system and devices. All + of the system operating state is saved to either memory or disk + (depending on the state), to allow the system to resume operation + quickly at your request. + + Although this option sounds really nifty, barely any of the device + drivers have been converted to the new driver model and hence few + have proper power management support. + + This option is not recommended for anyone except those doing driver + power management development. + +config ACPI_SLEEP_PROC_FS + bool + depends on ACPI_SLEEP && PROC_FS + default y + +config ACPI_AC + tristate "AC Adapter" + depends on X86 + default m + help + This driver adds support for the AC Adapter object, which indicates + whether a system is on AC, or not. Typically, only mobile systems + have this object, since desktops are always on AC. + +config ACPI_BATTERY + tristate "Battery" + depends on X86 + default m + help + This driver adds support for battery information through + /proc/acpi/battery. If you have a mobile system with a battery, + say Y. + +config ACPI_BUTTON + tristate "Button" + depends on !IA64_SGI_SN + default m + help + This driver registers for events based on buttons, such as the + power, sleep, and lid switch. In the future, a daemon will read + /proc/acpi/event and perform user-defined actions such as shutting + down the system. Until then, you can cat it, and see output when + a button is pressed. + +config ACPI_VIDEO + tristate "Video" + depends on EXPERIMENTAL + depends on !IA64_SGI_SN + default m + help + This driver implement the ACPI Extensions For Display Adapters + for integrated graphics devices on motherboard, as specified in + ACPI 2.0 Specification, Appendix B, allowing to perform some basic + control like defining the video POST device, retrieving EDID information + or to setup a video output, etc. + Note that this is an ref. implementation only. It may or may not work + for your integrated video device. + +config ACPI_FAN + tristate "Fan" + depends on !IA64_SGI_SN + default m + help + This driver adds support for ACPI fan devices, allowing user-mode + applications to perform basic fan control (on, off, status). + +config ACPI_PROCESSOR + tristate "Processor" + depends on !IA64_SGI_SN + default m + help + This driver installs ACPI as the idle handler for Linux, and uses + ACPI C2 and C3 processor states to save power, on systems that + support it. + +config ACPI_HOTPLUG_CPU + bool "Processor Hotplug (EXPERIMENTAL)" + depends on ACPI_PROCESSOR && HOTPLUG_CPU && EXPERIMENTAL + depends on !IA64_SGI_SN + select ACPI_CONTAINER + default n + ---help--- + Select this option if your platform support physical CPU hotplug. + +config ACPI_THERMAL + tristate "Thermal Zone" + depends on ACPI_PROCESSOR + default m + help + This driver adds support for ACPI thermal zones. Most mobile and + some desktop systems support ACPI thermal zones. It is HIGHLY + recommended that this option be enabled, as your processor(s) + may be damaged without it. + +config ACPI_NUMA + bool "NUMA support" + depends on NUMA + depends on (IA64 || X86_64) + default y if IA64_GENERIC || IA64_SGI_SN2 + +config ACPI_ASUS + tristate "ASUS/Medion Laptop Extras" + depends on X86 + default m + ---help--- + This driver provides support for extra features of ACPI-compatible + ASUS laptops. As some of Medion laptops are made by ASUS, it may also + support some Medion laptops (such as 9675 for example). It makes all + the extra buttons generate standard ACPI events that go through + /proc/acpi/events, and (on some models) adds support for changing the + display brightness and output, switching the LCD backlight on and off, + and most importantly, allows you to blink those fancy LEDs intended + for reporting mail and wireless status. + + Note: display switching code is currently considered EXPERIMENTAL, + toying with these values may even lock your machine. + + All settings are changed via /proc/acpi/asus directory entries. Owner + and group for these entries can be set with asus_uid and asus_gid + parameters. + + More information and a userspace daemon for handling the extra buttons + at <http://sourceforge.net/projects/acpi4asus/>. + + If you have an ACPI-compatible ASUS laptop, say Y or M here. This + driver is still under development, so if your laptop is unsupported or + something works not quite as expected, please use the mailing list + available on the above page (acpi4asus-user@lists.sourceforge.net) + +config ACPI_IBM + tristate "IBM ThinkPad Laptop Extras" + depends on X86 + default m + ---help--- + This is a Linux ACPI driver for the IBM ThinkPad laptops. It adds + support for Fn-Fx key combinations, Bluetooth control, video + output switching, ThinkLight control, UltraBay eject and more. + For more information about this driver see <file:Documentation/ibm-acpi.txt> + and <http://ibm-acpi.sf.net/> . + + If you have an IBM ThinkPad laptop, say Y or M here. + +config ACPI_TOSHIBA + tristate "Toshiba Laptop Extras" + depends on X86 + default m + ---help--- + This driver adds support for access to certain system settings + on "legacy free" Toshiba laptops. These laptops can be recognized by + their lack of a BIOS setup menu and APM support. + + On these machines, all system configuration is handled through the + ACPI. This driver is required for access to controls not covered + by the general ACPI drivers, such as LCD brightness, video output, + etc. + + This driver differs from the non-ACPI Toshiba laptop driver (located + under "Processor type and features") in several aspects. + Configuration is accessed by reading and writing text files in the + /proc tree instead of by program interface to /dev. Furthermore, no + power management functions are exposed, as those are handled by the + general ACPI drivers. + + More information about this driver is available at + <http://memebeam.org/toys/ToshibaAcpiDriver>. + + If you have a legacy free Toshiba laptop (such as the Libretto L1 + series), say Y. + +config ACPI_CUSTOM_DSDT + bool "Include Custom DSDT" + depends on !STANDALONE + default n + help + Thist option is to load a custom ACPI DSDT + If you don't know what that is, say N. + +config ACPI_CUSTOM_DSDT_FILE + string "Custom DSDT Table file to include" + depends on ACPI_CUSTOM_DSDT + default "" + help + Enter the full path name to the file wich includes the AmlCode declaration. + +config ACPI_BLACKLIST_YEAR + int "Disable ACPI for systems before Jan 1st this year" + depends on ACPI_INTERPRETER + default 0 + help + enter a 4-digit year, eg. 2001 to disable ACPI by default + on platforms with DMI BIOS date before January 1st that year. + "acpi=force" can be used to override this mechanism. + + Enter 0 to disable this mechanism and allow ACPI to + run by default no matter what the year. (default) + +config ACPI_DEBUG + bool "Debug Statements" + depends on !IA64_SGI_SN + default n + help + The ACPI driver can optionally report errors with a great deal + of verbosity. Saying Y enables these statements. This will increase + your kernel size by around 50K. + +config ACPI_BUS + bool + depends on !IA64_SGI_SN + default y + +config ACPI_EC + bool + depends on X86 + default y + help + This driver is required on some systems for the proper operation of + the battery and thermal drivers. If you are compiling for a + mobile system, say Y. + +config ACPI_POWER + bool + depends on !IA64_SGI_SN + default y + +config ACPI_PCI + bool + depends on !IA64_SGI_SN + default PCI + +config ACPI_SYSTEM + bool + depends on !IA64_SGI_SN + default y + help + This driver will enable your system to shut down using ACPI, and + dump your ACPI DSDT table using /proc/acpi/dsdt. + +endif # ACPI_INTERPRETER + +config X86_PM_TIMER + bool "Power Management Timer Support" + depends on X86 + depends on ACPI_BOOT && EXPERIMENTAL + depends on !X86_64 + default n + help + The Power Management Timer is available on all ACPI-capable, + in most cases even if ACPI is unusable or blacklisted. + + This timing source is not affected by powermanagement features + like aggressive processor idling, throttling, frequency and/or + voltage scaling, unlike the commonly used Time Stamp Counter + (TSC) timing source. + + So, if you see messages like 'Losing too many ticks!' in the + kernel logs, and/or you are using this on a notebook which + does not yet have an HPET, you should say "Y" here. + +config ACPI_CONTAINER + tristate "ACPI0004,PNP0A05 and PNP0A06 Container Driver (EXPERIMENTAL)" + depends on EXPERIMENTAL + default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU || ACPI_HOTPLUG_IO) + ---help--- + This is the ACPI generic container driver which supports + ACPI0004, PNP0A05 and PNP0A06 devices + +config ACPI_HOTPLUG_MEMORY + tristate "Memory Hotplug" + depends on ACPI + depends on MEMORY_HOTPLUG + default n + help + This driver adds supports for ACPI Memory Hotplug. This driver + provides support for fielding notifications on ACPI memory + devices (PNP0C80) which represent memory ranges that may be + onlined or offlined during runtime. + + Enabling this driver assumes that your platform hardware + and firmware have support for hot-plugging physical memory. If + your system does not support physically adding or ripping out + memory DIMMs at some platfrom defined granularity (individually + or as a bank) at runtime, then you need not enable this driver. + + If one selects "m," this driver can be loaded using the following + command: + $>modprobe acpi_memhotplug +endif # ACPI + +endmenu diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile new file mode 100644 index 00000000000..65c92e20566 --- /dev/null +++ b/drivers/acpi/Makefile @@ -0,0 +1,58 @@ +# +# Makefile for the Linux ACPI interpreter +# + +export ACPI_CFLAGS + +ACPI_CFLAGS := -Os + +ifdef CONFIG_ACPI_DEBUG + ACPI_CFLAGS += -DACPI_DEBUG_OUTPUT +endif + +EXTRA_CFLAGS += $(ACPI_CFLAGS) + +# +# ACPI Boot-Time Table Parsing +# +obj-$(CONFIG_ACPI_BOOT) += tables.o +obj-$(CONFIG_ACPI_INTERPRETER) += blacklist.o + +# +# ACPI Core Subsystem (Interpreter) +# +obj-$(CONFIG_ACPI_INTERPRETER) += osl.o utils.o \ + dispatcher/ events/ executer/ hardware/ \ + namespace/ parser/ resources/ tables/ \ + utilities/ + +# +# ACPI Bus and Device Drivers +# +processor-objs += processor_core.o processor_throttling.o \ + processor_idle.o processor_thermal.o +ifdef CONFIG_CPU_FREQ +processor-objs += processor_perflib.o +endif + +obj-$(CONFIG_ACPI_BUS) += sleep/ +obj-$(CONFIG_ACPI_BUS) += bus.o +obj-$(CONFIG_ACPI_AC) += ac.o +obj-$(CONFIG_ACPI_BATTERY) += battery.o +obj-$(CONFIG_ACPI_BUTTON) += button.o +obj-$(CONFIG_ACPI_EC) += ec.o +obj-$(CONFIG_ACPI_FAN) += fan.o +obj-$(CONFIG_ACPI_VIDEO) += video.o +obj-$(CONFIG_ACPI_PCI) += pci_root.o pci_link.o pci_irq.o pci_bind.o +obj-$(CONFIG_ACPI_POWER) += power.o +obj-$(CONFIG_ACPI_PROCESSOR) += processor.o +obj-$(CONFIG_ACPI_CONTAINER) += container.o +obj-$(CONFIG_ACPI_THERMAL) += thermal.o +obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o +obj-$(CONFIG_ACPI_DEBUG) += debug.o +obj-$(CONFIG_ACPI_NUMA) += numa.o +obj-$(CONFIG_ACPI_ASUS) += asus_acpi.o +obj-$(CONFIG_ACPI_IBM) += ibm_acpi.o +obj-$(CONFIG_ACPI_TOSHIBA) += toshiba_acpi.o +obj-$(CONFIG_ACPI_BUS) += scan.o motherboard.o +obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c new file mode 100644 index 00000000000..23ab761dd72 --- /dev/null +++ b/drivers/acpi/ac.c @@ -0,0 +1,354 @@ +/* + * acpi_ac.c - ACPI AC Adapter Driver ($Revision: 27 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define ACPI_AC_COMPONENT 0x00020000 +#define ACPI_AC_CLASS "ac_adapter" +#define ACPI_AC_HID "ACPI0003" +#define ACPI_AC_DRIVER_NAME "ACPI AC Adapter Driver" +#define ACPI_AC_DEVICE_NAME "AC Adapter" +#define ACPI_AC_FILE_STATE "state" +#define ACPI_AC_NOTIFY_STATUS 0x80 +#define ACPI_AC_STATUS_OFFLINE 0x00 +#define ACPI_AC_STATUS_ONLINE 0x01 +#define ACPI_AC_STATUS_UNKNOWN 0xFF + +#define _COMPONENT ACPI_AC_COMPONENT +ACPI_MODULE_NAME ("acpi_ac") + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION(ACPI_AC_DRIVER_NAME); +MODULE_LICENSE("GPL"); + +static int acpi_ac_add (struct acpi_device *device); +static int acpi_ac_remove (struct acpi_device *device, int type); +static int acpi_ac_open_fs(struct inode *inode, struct file *file); + +static struct acpi_driver acpi_ac_driver = { + .name = ACPI_AC_DRIVER_NAME, + .class = ACPI_AC_CLASS, + .ids = ACPI_AC_HID, + .ops = { + .add = acpi_ac_add, + .remove = acpi_ac_remove, + }, +}; + +struct acpi_ac { + acpi_handle handle; + unsigned long state; +}; + +static struct file_operations acpi_ac_fops = { + .open = acpi_ac_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* -------------------------------------------------------------------------- + AC Adapter Management + -------------------------------------------------------------------------- */ + +static int +acpi_ac_get_state ( + struct acpi_ac *ac) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE("acpi_ac_get_state"); + + if (!ac) + return_VALUE(-EINVAL); + + status = acpi_evaluate_integer(ac->handle, "_PSR", NULL, &ac->state); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error reading AC Adapter state\n")); + ac->state = ACPI_AC_STATUS_UNKNOWN; + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_ac_dir; + +static int acpi_ac_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_ac *ac = (struct acpi_ac *) seq->private; + + ACPI_FUNCTION_TRACE("acpi_ac_seq_show"); + + if (!ac) + return_VALUE(0); + + if (acpi_ac_get_state(ac)) { + seq_puts(seq, "ERROR: Unable to read AC Adapter state\n"); + return_VALUE(0); + } + + seq_puts(seq, "state: "); + switch (ac->state) { + case ACPI_AC_STATUS_OFFLINE: + seq_puts(seq, "off-line\n"); + break; + case ACPI_AC_STATUS_ONLINE: + seq_puts(seq, "on-line\n"); + break; + default: + seq_puts(seq, "unknown\n"); + break; + } + + return_VALUE(0); +} + +static int acpi_ac_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_ac_seq_show, PDE(inode)->data); +} + +static int +acpi_ac_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_ac_add_fs"); + + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_ac_dir); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; + } + + /* 'state' [R] */ + entry = create_proc_entry(ACPI_AC_FILE_STATE, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_AC_FILE_STATE)); + else { + entry->proc_fops = &acpi_ac_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + return_VALUE(0); +} + + +static int +acpi_ac_remove_fs ( + struct acpi_device *device) +{ + ACPI_FUNCTION_TRACE("acpi_ac_remove_fs"); + + if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_AC_FILE_STATE, + acpi_device_dir(device)); + + remove_proc_entry(acpi_device_bid(device), acpi_ac_dir); + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + + +/* -------------------------------------------------------------------------- + Driver Model + -------------------------------------------------------------------------- */ + +static void +acpi_ac_notify ( + acpi_handle handle, + u32 event, + void *data) +{ + struct acpi_ac *ac = (struct acpi_ac *) data; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_ac_notify"); + + if (!ac) + return_VOID; + + if (acpi_bus_get_device(ac->handle, &device)) + return_VOID; + + switch (event) { + case ACPI_AC_NOTIFY_STATUS: + acpi_ac_get_state(ac); + acpi_bus_generate_event(device, event, (u32) ac->state); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + return_VOID; +} + + +static int +acpi_ac_add ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_ac *ac = NULL; + + ACPI_FUNCTION_TRACE("acpi_ac_add"); + + if (!device) + return_VALUE(-EINVAL); + + ac = kmalloc(sizeof(struct acpi_ac), GFP_KERNEL); + if (!ac) + return_VALUE(-ENOMEM); + memset(ac, 0, sizeof(struct acpi_ac)); + + ac->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_AC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_AC_CLASS); + acpi_driver_data(device) = ac; + + result = acpi_ac_get_state(ac); + if (result) + goto end; + + result = acpi_ac_add_fs(device); + if (result) + goto end; + + status = acpi_install_notify_handler(ac->handle, + ACPI_DEVICE_NOTIFY, acpi_ac_notify, ac); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing notify handler\n")); + result = -ENODEV; + goto end; + } + + printk(KERN_INFO PREFIX "%s [%s] (%s)\n", + acpi_device_name(device), acpi_device_bid(device), + ac->state?"on-line":"off-line"); + +end: + if (result) { + acpi_ac_remove_fs(device); + kfree(ac); + } + + return_VALUE(result); +} + + +static int +acpi_ac_remove ( + struct acpi_device *device, + int type) +{ + acpi_status status = AE_OK; + struct acpi_ac *ac = NULL; + + ACPI_FUNCTION_TRACE("acpi_ac_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + ac = (struct acpi_ac *) acpi_driver_data(device); + + status = acpi_remove_notify_handler(ac->handle, + ACPI_DEVICE_NOTIFY, acpi_ac_notify); + if (ACPI_FAILURE(status)) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error removing notify handler\n")); + + acpi_ac_remove_fs(device); + + kfree(ac); + + return_VALUE(0); +} + + +static int __init +acpi_ac_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_ac_init"); + + acpi_ac_dir = proc_mkdir(ACPI_AC_CLASS, acpi_root_dir); + if (!acpi_ac_dir) + return_VALUE(-ENODEV); + acpi_ac_dir->owner = THIS_MODULE; + + result = acpi_bus_register_driver(&acpi_ac_driver); + if (result < 0) { + remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + + +static void __exit +acpi_ac_exit (void) +{ + ACPI_FUNCTION_TRACE("acpi_ac_exit"); + + acpi_bus_unregister_driver(&acpi_ac_driver); + + remove_proc_entry(ACPI_AC_CLASS, acpi_root_dir); + + return_VOID; +} + + +module_init(acpi_ac_init); +module_exit(acpi_ac_exit); diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c new file mode 100644 index 00000000000..77285ffe41c --- /dev/null +++ b/drivers/acpi/acpi_memhotplug.c @@ -0,0 +1,542 @@ +/* + * Copyright (C) 2004 Intel Corporation <naveen.b.s@intel.com> + * + * 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, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * ACPI based HotPlug driver that supports Memory Hotplug + * This driver fields notifications from firmare for memory add + * and remove operations and alerts the VM of the affected memory + * ranges. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/memory_hotplug.h> +#include <acpi/acpi_drivers.h> + + +#define ACPI_MEMORY_DEVICE_COMPONENT 0x08000000UL +#define ACPI_MEMORY_DEVICE_CLASS "memory" +#define ACPI_MEMORY_DEVICE_HID "PNP0C80" +#define ACPI_MEMORY_DEVICE_DRIVER_NAME "Hotplug Mem Driver" +#define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device" + +#define _COMPONENT ACPI_MEMORY_DEVICE_COMPONENT + +ACPI_MODULE_NAME ("acpi_memory") +MODULE_AUTHOR("Naveen B S <naveen.b.s@intel.com>"); +MODULE_DESCRIPTION(ACPI_MEMORY_DEVICE_DRIVER_NAME); +MODULE_LICENSE("GPL"); + +/* ACPI _STA method values */ +#define ACPI_MEMORY_STA_PRESENT (0x00000001UL) +#define ACPI_MEMORY_STA_ENABLED (0x00000002UL) +#define ACPI_MEMORY_STA_FUNCTIONAL (0x00000008UL) + +/* Memory Device States */ +#define MEMORY_INVALID_STATE 0 +#define MEMORY_POWER_ON_STATE 1 +#define MEMORY_POWER_OFF_STATE 2 + +static int acpi_memory_device_add (struct acpi_device *device); +static int acpi_memory_device_remove (struct acpi_device *device, int type); + +static struct acpi_driver acpi_memory_device_driver = { + .name = ACPI_MEMORY_DEVICE_DRIVER_NAME, + .class = ACPI_MEMORY_DEVICE_CLASS, + .ids = ACPI_MEMORY_DEVICE_HID, + .ops = { + .add = acpi_memory_device_add, + .remove = acpi_memory_device_remove, + }, +}; + +struct acpi_memory_device { + acpi_handle handle; + unsigned int state; /* State of the memory device */ + unsigned short cache_attribute; /* memory cache attribute */ + unsigned short read_write_attribute;/* memory read/write attribute */ + u64 start_addr; /* Memory Range start physical addr */ + u64 end_addr; /* Memory Range end physical addr */ +}; + + +static int +acpi_memory_get_device_resources(struct acpi_memory_device *mem_device) +{ + acpi_status status; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_resource *resource = NULL; + struct acpi_resource_address64 address64; + + ACPI_FUNCTION_TRACE("acpi_memory_get_device_resources"); + + /* Get the range from the _CRS */ + status = acpi_get_current_resources(mem_device->handle, &buffer); + if (ACPI_FAILURE(status)) + return_VALUE(-EINVAL); + + resource = (struct acpi_resource *) buffer.pointer; + status = acpi_resource_to_address64(resource, &address64); + if (ACPI_SUCCESS(status)) { + if (address64.resource_type == ACPI_MEMORY_RANGE) { + /* Populate the structure */ + mem_device->cache_attribute = + address64.attribute.memory.cache_attribute; + mem_device->read_write_attribute = + address64.attribute.memory.read_write_attribute; + mem_device->start_addr = address64.min_address_range; + mem_device->end_addr = address64.max_address_range; + } + } + + acpi_os_free(buffer.pointer); + return_VALUE(0); +} + +static int +acpi_memory_get_device(acpi_handle handle, + struct acpi_memory_device **mem_device) +{ + acpi_status status; + acpi_handle phandle; + struct acpi_device *device = NULL; + struct acpi_device *pdevice = NULL; + + ACPI_FUNCTION_TRACE("acpi_memory_get_device"); + + if (!acpi_bus_get_device(handle, &device) && device) + goto end; + + status = acpi_get_parent(handle, &phandle); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error in acpi_get_parent\n")); + return_VALUE(-EINVAL); + } + + /* Get the parent device */ + status = acpi_bus_get_device(phandle, &pdevice); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error in acpi_bus_get_device\n")); + return_VALUE(-EINVAL); + } + + /* + * Now add the notified device. This creates the acpi_device + * and invokes .add function + */ + status = acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error in acpi_bus_add\n")); + return_VALUE(-EINVAL); + } + +end: + *mem_device = acpi_driver_data(device); + if (!(*mem_device)) { + printk(KERN_ERR "\n driver data not found" ); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + +static int +acpi_memory_check_device(struct acpi_memory_device *mem_device) +{ + unsigned long current_status; + + ACPI_FUNCTION_TRACE("acpi_memory_check_device"); + + /* Get device present/absent information from the _STA */ + if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->handle, "_STA", + NULL, ¤t_status))) + return_VALUE(-ENODEV); + /* + * Check for device status. Device should be + * present/enabled/functioning. + */ + if (!((current_status & ACPI_MEMORY_STA_PRESENT) + && (current_status & ACPI_MEMORY_STA_ENABLED) + && (current_status & ACPI_MEMORY_STA_FUNCTIONAL))) + return_VALUE(-ENODEV); + + return_VALUE(0); +} + +static int +acpi_memory_enable_device(struct acpi_memory_device *mem_device) +{ + int result; + + ACPI_FUNCTION_TRACE("acpi_memory_enable_device"); + + /* Get the range from the _CRS */ + result = acpi_memory_get_device_resources(mem_device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "\nget_device_resources failed\n")); + mem_device->state = MEMORY_INVALID_STATE; + return result; + } + + /* + * Tell the VM there is more memory here... + * Note: Assume that this function returns zero on success + */ + result = add_memory(mem_device->start_addr, + (mem_device->end_addr - mem_device->start_addr) + 1, + mem_device->read_write_attribute); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "\nadd_memory failed\n")); + mem_device->state = MEMORY_INVALID_STATE; + return result; + } + + return result; +} + +static int +acpi_memory_powerdown_device(struct acpi_memory_device *mem_device) +{ + acpi_status status; + struct acpi_object_list arg_list; + union acpi_object arg; + unsigned long current_status; + + ACPI_FUNCTION_TRACE("acpi_memory_powerdown_device"); + + /* Issue the _EJ0 command */ + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = 1; + status = acpi_evaluate_object(mem_device->handle, + "_EJ0", &arg_list, NULL); + /* Return on _EJ0 failure */ + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"_EJ0 failed.\n")); + return_VALUE(-ENODEV); + } + + /* Evalute _STA to check if the device is disabled */ + status = acpi_evaluate_integer(mem_device->handle, "_STA", + NULL, ¤t_status); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + /* Check for device status. Device should be disabled */ + if (current_status & ACPI_MEMORY_STA_ENABLED) + return_VALUE(-EINVAL); + + return_VALUE(0); +} + +static int +acpi_memory_disable_device(struct acpi_memory_device *mem_device) +{ + int result; + u64 start = mem_device->start_addr; + u64 len = mem_device->end_addr - start + 1; + unsigned long attr = mem_device->read_write_attribute; + + ACPI_FUNCTION_TRACE("acpi_memory_disable_device"); + + /* + * Ask the VM to offline this memory range. + * Note: Assume that this function returns zero on success + */ + result = remove_memory(start, len, attr); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Hot-Remove failed.\n")); + return_VALUE(result); + } + + /* Power-off and eject the device */ + result = acpi_memory_powerdown_device(mem_device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Device Power Down failed.\n")); + /* Set the status of the device to invalid */ + mem_device->state = MEMORY_INVALID_STATE; + return result; + } + + mem_device->state = MEMORY_POWER_OFF_STATE; + return result; +} + +static void +acpi_memory_device_notify(acpi_handle handle, u32 event, void *data) +{ + struct acpi_memory_device *mem_device; + struct acpi_device *device; + + ACPI_FUNCTION_TRACE("acpi_memory_device_notify"); + + switch (event) { + case ACPI_NOTIFY_BUS_CHECK: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "\nReceived BUS CHECK notification for device\n")); + /* Fall Through */ + case ACPI_NOTIFY_DEVICE_CHECK: + if (event == ACPI_NOTIFY_DEVICE_CHECK) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "\nReceived DEVICE CHECK notification for device\n")); + if (acpi_memory_get_device(handle, &mem_device)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error in finding driver data\n")); + return_VOID; + } + + if (!acpi_memory_check_device(mem_device)) { + if (acpi_memory_enable_device(mem_device)) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error in acpi_memory_enable_device\n")); + } + break; + case ACPI_NOTIFY_EJECT_REQUEST: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "\nReceived EJECT REQUEST notification for device\n")); + + if (acpi_bus_get_device(handle, &device)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Device doesn't exist\n")); + break; + } + mem_device = acpi_driver_data(device); + if (!mem_device) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Driver Data is NULL\n")); + break; + } + + /* + * Currently disabling memory device from kernel mode + * TBD: Can also be disabled from user mode scripts + * TBD: Can also be disabled by Callback registration + * with generic sysfs driver + */ + if (acpi_memory_disable_device(mem_device)) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error in acpi_memory_disable_device\n")); + /* + * TBD: Invoke acpi_bus_remove to cleanup data structures + */ + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + return_VOID; +} + +static int +acpi_memory_device_add(struct acpi_device *device) +{ + int result; + struct acpi_memory_device *mem_device = NULL; + + ACPI_FUNCTION_TRACE("acpi_memory_device_add"); + + if (!device) + return_VALUE(-EINVAL); + + mem_device = kmalloc(sizeof(struct acpi_memory_device), GFP_KERNEL); + if (!mem_device) + return_VALUE(-ENOMEM); + memset(mem_device, 0, sizeof(struct acpi_memory_device)); + + mem_device->handle = device->handle; + sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME); + sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS); + acpi_driver_data(device) = mem_device; + + /* Get the range from the _CRS */ + result = acpi_memory_get_device_resources(mem_device); + if (result) { + kfree(mem_device); + return_VALUE(result); + } + + /* Set the device state */ + mem_device->state = MEMORY_POWER_ON_STATE; + + printk(KERN_INFO "%s \n", acpi_device_name(device)); + + return_VALUE(result); +} + +static int +acpi_memory_device_remove (struct acpi_device *device, int type) +{ + struct acpi_memory_device *mem_device = NULL; + + ACPI_FUNCTION_TRACE("acpi_memory_device_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + mem_device = (struct acpi_memory_device *) acpi_driver_data(device); + kfree(mem_device); + + return_VALUE(0); +} + +/* + * Helper function to check for memory device + */ +static acpi_status +is_memory_device(acpi_handle handle) +{ + char *hardware_id; + acpi_status status; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_device_info *info; + + ACPI_FUNCTION_TRACE("is_memory_device"); + + status = acpi_get_object_info(handle, &buffer); + if (ACPI_FAILURE(status)) + return_ACPI_STATUS(AE_ERROR); + + info = buffer.pointer; + if (!(info->valid & ACPI_VALID_HID)) { + acpi_os_free(buffer.pointer); + return_ACPI_STATUS(AE_ERROR); + } + + hardware_id = info->hardware_id.value; + if ((hardware_id == NULL) || + (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID))) + status = AE_ERROR; + + acpi_os_free(buffer.pointer); + return_ACPI_STATUS(status); +} + +static acpi_status +acpi_memory_register_notify_handler (acpi_handle handle, + u32 level, void *ctxt, void **retv) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE("acpi_memory_register_notify_handler"); + + status = is_memory_device(handle); + if (ACPI_FAILURE(status)) + return_ACPI_STATUS(AE_OK); /* continue */ + + status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + acpi_memory_device_notify, NULL); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing notify handler\n")); + return_ACPI_STATUS(AE_OK); /* continue */ + } + + return_ACPI_STATUS(status); +} + +static acpi_status +acpi_memory_deregister_notify_handler (acpi_handle handle, + u32 level, void *ctxt, void **retv) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE("acpi_memory_deregister_notify_handler"); + + status = is_memory_device(handle); + if (ACPI_FAILURE(status)) + return_ACPI_STATUS(AE_OK); /* continue */ + + status = acpi_remove_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, acpi_memory_device_notify); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error removing notify handler\n")); + return_ACPI_STATUS(AE_OK); /* continue */ + } + + return_ACPI_STATUS(status); +} + +static int __init +acpi_memory_device_init (void) +{ + int result; + acpi_status status; + + ACPI_FUNCTION_TRACE("acpi_memory_device_init"); + + result = acpi_bus_register_driver(&acpi_memory_device_driver); + + if (result < 0) + return_VALUE(-ENODEV); + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_memory_register_notify_handler, + NULL, NULL); + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "walk_namespace failed\n")); + acpi_bus_unregister_driver(&acpi_memory_device_driver); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + +static void __exit +acpi_memory_device_exit (void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE("acpi_memory_device_exit"); + + /* + * Adding this to un-install notification handlers for all the device + * handles. + */ + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + acpi_memory_deregister_notify_handler, + NULL, NULL); + + if (ACPI_FAILURE (status)) + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "walk_namespace failed\n")); + + acpi_bus_unregister_driver(&acpi_memory_device_driver); + + return_VOID; +} + +module_init(acpi_memory_device_init); +module_exit(acpi_memory_device_exit); + + diff --git a/drivers/acpi/asus_acpi.c b/drivers/acpi/asus_acpi.c new file mode 100644 index 00000000000..a75cb565cae --- /dev/null +++ b/drivers/acpi/asus_acpi.c @@ -0,0 +1,1236 @@ +/* + * asus_acpi.c - Asus Laptop ACPI Extras + * + * + * Copyright (C) 2002, 2003, 2004 Julien Lerouge, Karol Kozimor + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * The development page for this driver is located at + * http://sourceforge.net/projects/acpi4asus/ + * + * Credits: + * Pontus Fuchs - Helper functions, cleanup + * Johann Wiesner - Small compile fixes + * John Belmonte - ACPI code for Toshiba laptop was a good starting point. + * + * TODO: + * add Fn key status + * Add mode selection on module loading (parameter) -> still necessary? + * Complete display switching -- may require dirty hacks or calling _DOS? + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <acpi/acpi_drivers.h> +#include <acpi/acpi_bus.h> +#include <asm/uaccess.h> + +#define ASUS_ACPI_VERSION "0.29" + +#define PROC_ASUS "asus" //the directory +#define PROC_MLED "mled" +#define PROC_WLED "wled" +#define PROC_TLED "tled" +#define PROC_INFO "info" +#define PROC_LCD "lcd" +#define PROC_BRN "brn" +#define PROC_DISP "disp" + +#define ACPI_HOTK_NAME "Asus Laptop ACPI Extras Driver" +#define ACPI_HOTK_CLASS "hotkey" +#define ACPI_HOTK_DEVICE_NAME "Hotkey" +#define ACPI_HOTK_HID "ATK0100" + +/* + * Some events we use, same for all Asus + */ +#define BR_UP 0x10 +#define BR_DOWN 0x20 + +/* + * Flags for hotk status + */ +#define MLED_ON 0x01 //is MLED ON ? +#define WLED_ON 0x02 +#define TLED_ON 0x04 + +MODULE_AUTHOR("Julien Lerouge, Karol Kozimor"); +MODULE_DESCRIPTION(ACPI_HOTK_NAME); +MODULE_LICENSE("GPL"); + + +static uid_t asus_uid; +static gid_t asus_gid; +module_param(asus_uid, uint, 0); +MODULE_PARM_DESC(uid, "UID for entries in /proc/acpi/asus.\n"); +module_param(asus_gid, uint, 0); +MODULE_PARM_DESC(gid, "GID for entries in /proc/acpi/asus.\n"); + + +/* For each model, all features implemented, + * those marked with R are relative to HOTK, A for absolute */ +struct model_data { + char *name; //name of the laptop________________A + char *mt_mled; //method to handle mled_____________R + char *mled_status; //node to handle mled reading_______A + char *mt_wled; //method to handle wled_____________R + char *wled_status; //node to handle wled reading_______A + char *mt_tled; //method to handle tled_____________R + char *tled_status; //node to handle tled reading_______A + char *mt_lcd_switch; //method to turn LCD ON/OFF_________A + char *lcd_status; //node to read LCD panel state______A + char *brightness_up; //method to set brightness up_______A + char *brightness_down; //guess what ?______________________A + char *brightness_set; //method to set absolute brightness_R + char *brightness_get; //method to get absolute brightness_R + char *brightness_status; //node to get brightness____________A + char *display_set; //method to set video output________R + char *display_get; //method to get video output________R +}; + +/* + * This is the main structure, we can use it to store anything interesting + * about the hotk device + */ +struct asus_hotk { + struct acpi_device *device; //the device we are in + acpi_handle handle; //the handle of the hotk device + char status; //status of the hotk, for LEDs, ... + struct model_data *methods; //methods available on the laptop + u8 brightness; //brightness level + enum { + A1x = 0, //A1340D, A1300F + A2x, //A2500H + D1x, //D1 + L2D, //L2000D + L3C, //L3800C + L3D, //L3400D + L3H, //L3H, but also L2000E + L4R, //L4500R + L5x, //L5800C + L8L, //L8400L + M1A, //M1300A + M2E, //M2400E, L4400L + M6N, //M6800N + M6R, //M6700R + P30, //Samsung P30 + S1x, //S1300A, but also L1400B and M2400A (L84F) + S2x, //S200 (J1 reported), Victor MP-XP7210 + xxN, //M2400N, M3700N, M5200N, S1300N, S5200N, W1OOON + //(Centrino) + END_MODEL + } model; //Models currently supported + u16 event_count[128]; //count for each event TODO make this better +}; + +/* Here we go */ +#define A1x_PREFIX "\\_SB.PCI0.ISA.EC0." +#define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0." +#define M1A_PREFIX "\\_SB.PCI0.PX40.EC0." +#define P30_PREFIX "\\_SB.PCI0.LPCB.EC0." +#define S1x_PREFIX "\\_SB.PCI0.PX40." +#define S2x_PREFIX A1x_PREFIX +#define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0." + +static struct model_data model_conf[END_MODEL] = { + /* + * Those pathnames are relative to the HOTK / ATKD device : + * - mt_mled + * - mt_wled + * - brightness_set + * - brightness_get + * - display_set + * - display_get + * + * TODO I have seen a SWBX and AIBX method on some models, like L1400B, + * it seems to be a kind of switch, but what for ? + * + */ + + { + .name = "A1x", + .mt_mled = "MLED", + .mled_status = "\\MAIL", + .mt_lcd_switch = A1x_PREFIX "_Q10", + .lcd_status = "\\BKLI", + .brightness_up = A1x_PREFIX "_Q0E", + .brightness_down = A1x_PREFIX "_Q0F" + }, + + { + .name = "A2x", + .mt_mled = "MLED", + .mt_wled = "WLED", + .wled_status = "\\SG66", + .mt_lcd_switch = "\\Q10", + .lcd_status = "\\BAOF", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\INFB" + }, + + { + .name = "D1x", + .mt_mled = "MLED", + .mt_lcd_switch = "\\Q0D", + .lcd_status = "\\GP11", + .brightness_up = "\\Q0C", + .brightness_down = "\\Q0B", + .brightness_status = "\\BLVL", + .display_set = "SDSP", + .display_get = "\\INFB" + }, + + { + .name = "L2D", + .mt_mled = "MLED", + .mled_status = "\\SGP6", + .mt_wled = "WLED", + .wled_status = "\\RCP3", + .mt_lcd_switch = "\\Q10", + .lcd_status = "\\SGP0", + .brightness_up = "\\Q0E", + .brightness_down = "\\Q0F", + .display_set = "SDSP", + .display_get = "\\INFB" + }, + + { + .name = "L3C", + .mt_mled = "MLED", + .mt_wled = "WLED", + .mt_lcd_switch = L3C_PREFIX "_Q10", + .lcd_status = "\\GL32", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP" + }, + + { + .name = "L3D", + .mt_mled = "MLED", + .mled_status = "\\MALD", + .mt_wled = "WLED", + .mt_lcd_switch = "\\Q10", + .lcd_status = "\\BKLG", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\INFB" + }, + + { + .name = "L3H", + .mt_mled = "MLED", + .mt_wled = "WLED", + .mt_lcd_switch = "EHK", + .lcd_status = "\\_SB.PCI0.PM.PBC", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\INFB" + }, + + { + .name = "L4R", + .mt_mled = "MLED", + .mt_wled = "WLED", + .wled_status = "\\_SB.PCI0.SBRG.SG13", + .mt_lcd_switch = xxN_PREFIX "_Q10", + .lcd_status = "\\_SB.PCI0.SBSM.SEO4", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\_SB.PCI0.P0P1.VGA.GETD" + }, + + { + .name = "L5x", + .mt_mled = "MLED", +/* WLED present, but not controlled by ACPI */ + .mt_tled = "TLED", + .mt_lcd_switch = "\\Q0D", + .lcd_status = "\\BAOF", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\INFB" + }, + + { + .name = "L8L" +/* No features, but at least support the hotkeys */ + }, + + { + .name = "M1A", + .mt_mled = "MLED", + .mt_lcd_switch = M1A_PREFIX "Q10", + .lcd_status = "\\PNOF", + .brightness_up = M1A_PREFIX "Q0E", + .brightness_down = M1A_PREFIX "Q0F", + .brightness_status = "\\BRIT", + .display_set = "SDSP", + .display_get = "\\INFB" + }, + + { + .name = "M2E", + .mt_mled = "MLED", + .mt_wled = "WLED", + .mt_lcd_switch = "\\Q10", + .lcd_status = "\\GP06", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\INFB" + }, + + { + .name = "M6N", + .mt_mled = "MLED", + .mt_wled = "WLED", + .wled_status = "\\_SB.PCI0.SBRG.SG13", + .mt_lcd_switch = xxN_PREFIX "_Q10", + .lcd_status = "\\_SB.BKLT", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\SSTE" + }, + { + .name = "M6R", + .mt_mled = "MLED", + .mt_wled = "WLED", + .mt_lcd_switch = xxN_PREFIX "_Q10", + .lcd_status = "\\_SB.PCI0.SBSM.SEO4", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\SSTE" + }, + + + { + .name = "P30", + .mt_wled = "WLED", + .mt_lcd_switch = P30_PREFIX "_Q0E", + .lcd_status = "\\BKLT", + .brightness_up = P30_PREFIX "_Q68", + .brightness_down = P30_PREFIX "_Q69", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\DNXT" + }, + + { + .name = "S1x", + .mt_mled = "MLED", + .mled_status = "\\EMLE", + .mt_wled = "WLED", + .mt_lcd_switch = S1x_PREFIX "Q10" , + .lcd_status = "\\PNOF", + .brightness_set = "SPLV", + .brightness_get = "GPLV" + }, + + { + .name = "S2x", + .mt_mled = "MLED", + .mled_status = "\\MAIL", + .mt_lcd_switch = S2x_PREFIX "_Q10", + .lcd_status = "\\BKLI", + .brightness_up = S2x_PREFIX "_Q0B", + .brightness_down = S2x_PREFIX "_Q0A" + }, + + { + .name = "xxN", + .mt_mled = "MLED", +/* WLED present, but not controlled by ACPI */ + .mt_lcd_switch = xxN_PREFIX "_Q10", + .lcd_status = "\\BKLT", + .brightness_set = "SPLV", + .brightness_get = "GPLV", + .display_set = "SDSP", + .display_get = "\\ADVG" + } +}; + +/* procdir we use */ +static struct proc_dir_entry *asus_proc_dir; + +/* + * This header is made available to allow proper configuration given model, + * revision number , ... this info cannot go in struct asus_hotk because it is + * available before the hotk + */ +static struct acpi_table_header *asus_info; + +/* The actual device the driver binds to */ +static struct asus_hotk *hotk; + +/* + * The hotkey driver declaration + */ +static int asus_hotk_add(struct acpi_device *device); +static int asus_hotk_remove(struct acpi_device *device, int type); +static struct acpi_driver asus_hotk_driver = { + .name = ACPI_HOTK_NAME, + .class = ACPI_HOTK_CLASS, + .ids = ACPI_HOTK_HID, + .ops = { + .add = asus_hotk_add, + .remove = asus_hotk_remove, + }, +}; + +/* + * This function evaluates an ACPI method, given an int as parameter, the + * method is searched within the scope of the handle, can be NULL. The output + * of the method is written is output, which can also be NULL + * + * returns 1 if write is successful, 0 else. + */ +static int write_acpi_int(acpi_handle handle, const char *method, int val, + struct acpi_buffer *output) +{ + struct acpi_object_list params; //list of input parameters (an int here) + union acpi_object in_obj; //the only param we use + acpi_status status; + + params.count = 1; + params.pointer = &in_obj; + in_obj.type = ACPI_TYPE_INTEGER; + in_obj.integer.value = val; + + status = acpi_evaluate_object(handle, (char *) method, ¶ms, output); + return (status == AE_OK); +} + + +static int read_acpi_int(acpi_handle handle, const char *method, int *val) +{ + struct acpi_buffer output; + union acpi_object out_obj; + acpi_status status; + + output.length = sizeof(out_obj); + output.pointer = &out_obj; + + status = acpi_evaluate_object(handle, (char *) method, NULL, &output); + *val = out_obj.integer.value; + return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER); +} + +/* + * We write our info in page, we begin at offset off and cannot write more + * than count bytes. We set eof to 1 if we handle those 2 values. We return the + * number of bytes written in page + */ +static int +proc_read_info(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + int len = 0; + int temp; + char buf[16]; //enough for all info + /* + * We use the easy way, we don't care of off and count, so we don't set eof + * to 1 + */ + + len += sprintf(page, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n"); + len += sprintf(page + len, "Model reference : %s\n", + hotk->methods->name); + /* + * The SFUN method probably allows the original driver to get the list + * of features supported by a given model. For now, 0x0100 or 0x0800 + * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card. + * The significance of others is yet to be found. + */ + if (read_acpi_int(hotk->handle, "SFUN", &temp)) + len += sprintf(page + len, "SFUN value : 0x%04x\n", temp); + /* + * Another value for userspace: the ASYM method returns 0x02 for + * battery low and 0x04 for battery critical, its readings tend to be + * more accurate than those provided by _BST. + * Note: since not all the laptops provide this method, errors are + * silently ignored. + */ + if (read_acpi_int(hotk->handle, "ASYM", &temp)) + len += sprintf(page + len, "ASYM value : 0x%04x\n", temp); + if (asus_info) { + snprintf(buf, 16, "%d", asus_info->length); + len += sprintf(page + len, "DSDT length : %s\n", buf); + snprintf(buf, 16, "%d", asus_info->checksum); + len += sprintf(page + len, "DSDT checksum : %s\n", buf); + snprintf(buf, 16, "%d", asus_info->revision); + len += sprintf(page + len, "DSDT revision : %s\n", buf); + snprintf(buf, 7, "%s", asus_info->oem_id); + len += sprintf(page + len, "OEM id : %s\n", buf); + snprintf(buf, 9, "%s", asus_info->oem_table_id); + len += sprintf(page + len, "OEM table id : %s\n", buf); + snprintf(buf, 16, "%x", asus_info->oem_revision); + len += sprintf(page + len, "OEM revision : 0x%s\n", buf); + snprintf(buf, 5, "%s", asus_info->asl_compiler_id); + len += sprintf(page + len, "ASL comp vendor id : %s\n", buf); + snprintf(buf, 16, "%x", asus_info->asl_compiler_revision); + len += sprintf(page + len, "ASL comp revision : 0x%s\n", buf); + } + + return len; +} + + +/* + * /proc handlers + * We write our info in page, we begin at offset off and cannot write more + * than count bytes. We set eof to 1 if we handle those 2 values. We return the + * number of bytes written in page + */ + +/* Generic LED functions */ +static int +read_led(const char *ledname, int ledmask) +{ + if (ledname) { + int led_status; + + if (read_acpi_int(NULL, ledname, &led_status)) + return led_status; + else + printk(KERN_WARNING "Asus ACPI: Error reading LED " + "status\n"); + } + return (hotk->status & ledmask) ? 1 : 0; +} + +static int parse_arg(const char __user *buf, unsigned long count, int *val) +{ + char s[32]; + if (!count) + return 0; + if (count > 31) + return -EINVAL; + if (copy_from_user(s, buf, count)) + return -EFAULT; + s[count] = 0; + if (sscanf(s, "%i", val) != 1) + return -EINVAL; + return count; +} + +/* FIXME: kill extraneous args so it can be called independently */ +static int +write_led(const char __user *buffer, unsigned long count, + char *ledname, int ledmask, int invert) +{ + int value; + int led_out = 0; + + count = parse_arg(buffer, count, &value); + if (count > 0) + led_out = value ? 1 : 0; + + hotk->status = + (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask); + + if (invert) /* invert target value */ + led_out = !led_out & 0x1; + + if (!write_acpi_int(hotk->handle, ledname, led_out, NULL)) + printk(KERN_WARNING "Asus ACPI: LED (%s) write failed\n", ledname); + + return count; +} + + +/* + * Proc handlers for MLED + */ +static int +proc_read_mled(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + return sprintf(page, "%d\n", read_led(hotk->methods->mled_status, MLED_ON)); +} + + +static int +proc_write_mled(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1); +} + +/* + * Proc handlers for WLED + */ +static int +proc_read_wled(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + return sprintf(page, "%d\n", read_led(hotk->methods->wled_status, WLED_ON)); +} + +static int +proc_write_wled(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0); +} + +/* + * Proc handlers for TLED + */ +static int +proc_read_tled(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + return sprintf(page, "%d\n", read_led(hotk->methods->tled_status, TLED_ON)); +} + +static int +proc_write_tled(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0); +} + + +static int get_lcd_state(void) +{ + int lcd = 0; + + if (hotk->model != L3H) { + /* We don't have to check anything if we are here */ + if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd)) + printk(KERN_WARNING "Asus ACPI: Error reading LCD status\n"); + + if (hotk->model == L2D) + lcd = ~lcd; + } else { /* L3H and the like have to be handled differently */ + acpi_status status = 0; + struct acpi_object_list input; + union acpi_object mt_params[2]; + struct acpi_buffer output; + union acpi_object out_obj; + + input.count = 2; + input.pointer = mt_params; + /* Note: the following values are partly guessed up, but + otherwise they seem to work */ + mt_params[0].type = ACPI_TYPE_INTEGER; + mt_params[0].integer.value = 0x02; + mt_params[1].type = ACPI_TYPE_INTEGER; + mt_params[1].integer.value = 0x02; + + output.length = sizeof(out_obj); + output.pointer = &out_obj; + + status = acpi_evaluate_object(NULL, hotk->methods->lcd_status, &input, &output); + if (status != AE_OK) + return -1; + if (out_obj.type == ACPI_TYPE_INTEGER) + /* That's what the AML code does */ + lcd = out_obj.integer.value >> 8; + } + + return (lcd & 1); +} + +static int set_lcd_state(int value) +{ + int lcd = 0; + acpi_status status = 0; + + lcd = value ? 1 : 0; + if (lcd != get_lcd_state()) { + /* switch */ + if (hotk->model != L3H) { + status = + acpi_evaluate_object(NULL, hotk->methods->mt_lcd_switch, + NULL, NULL); + } else { /* L3H and the like have to be handled differently */ + if (!write_acpi_int(hotk->handle, hotk->methods->mt_lcd_switch, 0x07, NULL)) + status = AE_ERROR; + /* L3H's AML executes EHK (0x07) upon Fn+F7 keypress, + the exact behaviour is simulated here */ + } + if (ACPI_FAILURE(status)) + printk(KERN_WARNING "Asus ACPI: Error switching LCD\n"); + } + return 0; + +} + +static int +proc_read_lcd(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + return sprintf(page, "%d\n", get_lcd_state()); +} + + +static int +proc_write_lcd(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + int value; + + count = parse_arg(buffer, count, &value); + if (count > 0) + set_lcd_state(value); + return count; +} + + +static int read_brightness(void) +{ + int value; + + if(hotk->methods->brightness_get) { /* SPLV/GPLV laptop */ + if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get, + &value)) + printk(KERN_WARNING "Asus ACPI: Error reading brightness\n"); + } else if (hotk->methods->brightness_status) { /* For D1 for example */ + if (!read_acpi_int(NULL, hotk->methods->brightness_status, + &value)) + printk(KERN_WARNING "Asus ACPI: Error reading brightness\n"); + } else /* No GPLV method */ + value = hotk->brightness; + return value; +} + +/* + * Change the brightness level + */ +static void set_brightness(int value) +{ + acpi_status status = 0; + + /* SPLV laptop */ + if(hotk->methods->brightness_set) { + if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set, + value, NULL)) + printk(KERN_WARNING "Asus ACPI: Error changing brightness\n"); + return; + } + + /* No SPLV method if we are here, act as appropriate */ + value -= read_brightness(); + while (value != 0) { + status = acpi_evaluate_object(NULL, (value > 0) ? + hotk->methods->brightness_up : + hotk->methods->brightness_down, + NULL, NULL); + (value > 0) ? value-- : value++; + if (ACPI_FAILURE(status)) + printk(KERN_WARNING "Asus ACPI: Error changing brightness\n"); + } + return; +} + +static int +proc_read_brn(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + return sprintf(page, "%d\n", read_brightness()); +} + +static int +proc_write_brn(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + int value; + + count = parse_arg(buffer, count, &value); + if (count > 0) { + value = (0 < value) ? ((15 < value) ? 15 : value) : 0; + /* 0 <= value <= 15 */ + set_brightness(value); + } else if (count < 0) { + printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); + } + + return count; +} + +static void set_display(int value) +{ + /* no sanity check needed for now */ + if (!write_acpi_int(hotk->handle, hotk->methods->display_set, + value, NULL)) + printk(KERN_WARNING "Asus ACPI: Error setting display\n"); + return; +} + +/* + * Now, *this* one could be more user-friendly, but so far, no-one has + * complained. The significance of bits is the same as in proc_write_disp() + */ +static int +proc_read_disp(char *page, char **start, off_t off, int count, int *eof, + void *data) +{ + int value = 0; + + if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value)) + printk(KERN_WARNING "Asus ACPI: Error reading display status\n"); + value &= 0x07; /* needed for some models, shouldn't hurt others */ + return sprintf(page, "%d\n", value); +} + +/* + * Experimental support for display switching. As of now: 1 should activate + * the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination + * (bitwise) of these will suffice. I never actually tested 3 displays hooked up + * simultaneously, so be warned. See the acpi4asus README for more info. + */ +static int +proc_write_disp(struct file *file, const char __user *buffer, + unsigned long count, void *data) +{ + int value; + + count = parse_arg(buffer, count, &value); + if (count > 0) + set_display(value); + else if (count < 0) + printk(KERN_WARNING "Asus ACPI: Error reading user input\n"); + + return count; +} + + +typedef int (proc_readfunc)(char *page, char **start, off_t off, int count, + int *eof, void *data); +typedef int (proc_writefunc)(struct file *file, const char __user *buffer, + unsigned long count, void *data); + +static int +__init asus_proc_add(char *name, proc_writefunc *writefunc, + proc_readfunc *readfunc, mode_t mode, + struct acpi_device *device) +{ + struct proc_dir_entry *proc = create_proc_entry(name, mode, acpi_device_dir(device)); + if(!proc) { + printk(KERN_WARNING " Unable to create %s fs entry\n", name); + return -1; + } + proc->write_proc = writefunc; + proc->read_proc = readfunc; + proc->data = acpi_driver_data(device); + proc->owner = THIS_MODULE; + proc->uid = asus_uid; + proc->gid = asus_gid; + return 0; +} + +static int __init asus_hotk_add_fs(struct acpi_device *device) +{ + struct proc_dir_entry *proc; + mode_t mode; + + /* + * If parameter uid or gid is not changed, keep the default setting for + * our proc entries (-rw-rw-rw-) else, it means we care about security, + * and then set to -rw-rw---- + */ + + if ((asus_uid == 0) && (asus_gid == 0)){ + mode = S_IFREG | S_IRUGO | S_IWUGO; + } else { + mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP; + } + + acpi_device_dir(device) = asus_proc_dir; + if (!acpi_device_dir(device)) + return -ENODEV; + + proc = create_proc_entry(PROC_INFO, mode, acpi_device_dir(device)); + if (proc) { + proc->read_proc = proc_read_info; + proc->data = acpi_driver_data(device); + proc->owner = THIS_MODULE; + proc->uid = asus_uid; + proc->gid = asus_gid; + } else { + printk(KERN_WARNING " Unable to create " PROC_INFO + " fs entry\n"); + } + + if (hotk->methods->mt_wled) { + asus_proc_add(PROC_WLED, &proc_write_wled, &proc_read_wled, mode, device); + } + + if (hotk->methods->mt_mled) { + asus_proc_add(PROC_MLED, &proc_write_mled, &proc_read_mled, mode, device); + } + + if (hotk->methods->mt_tled) { + asus_proc_add(PROC_TLED, &proc_write_tled, &proc_read_tled, mode, device); + } + + /* + * We need both read node and write method as LCD switch is also accessible + * from keyboard + */ + if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) { + asus_proc_add(PROC_LCD, &proc_write_lcd, &proc_read_lcd, mode, device); + } + + if ((hotk->methods->brightness_up && hotk->methods->brightness_down) || + (hotk->methods->brightness_get && hotk->methods->brightness_set)) { + asus_proc_add(PROC_BRN, &proc_write_brn, &proc_read_brn, mode, device); + } + + if (hotk->methods->display_set) { + asus_proc_add(PROC_DISP, &proc_write_disp, &proc_read_disp, mode, device); + } + + return 0; +} + +static int asus_hotk_remove_fs(struct acpi_device* device) +{ + if(acpi_device_dir(device)) { + remove_proc_entry(PROC_INFO,acpi_device_dir(device)); + if (hotk->methods->mt_wled) + remove_proc_entry(PROC_WLED,acpi_device_dir(device)); + if (hotk->methods->mt_mled) + remove_proc_entry(PROC_MLED,acpi_device_dir(device)); + if (hotk->methods->mt_tled) + remove_proc_entry(PROC_TLED,acpi_device_dir(device)); + if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) + remove_proc_entry(PROC_LCD, acpi_device_dir(device)); + if ((hotk->methods->brightness_up && hotk->methods->brightness_down) || + (hotk->methods->brightness_get && hotk->methods->brightness_set)) + remove_proc_entry(PROC_BRN, acpi_device_dir(device)); + if (hotk->methods->display_set) + remove_proc_entry(PROC_DISP, acpi_device_dir(device)); + } + return 0; +} + + +static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) +{ + /* TODO Find a better way to handle events count.*/ + if (!hotk) + return; + + if ((event & ~((u32) BR_UP)) < 16) { + hotk->brightness = (event & ~((u32) BR_UP)); + } else if ((event & ~((u32) BR_DOWN)) < 16 ) { + hotk->brightness = (event & ~((u32) BR_DOWN)); + } + + acpi_bus_generate_event(hotk->device, event, + hotk->event_count[event % 128]++); + + return; +} + +/* + * This function is used to initialize the hotk with right values. In this + * method, we can make all the detection we want, and modify the hotk struct + */ +static int __init asus_hotk_get_info(void) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer dsdt = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *model = NULL; + int bsts_result; + acpi_status status; + + /* + * Get DSDT headers early enough to allow for differentiating between + * models, but late enough to allow acpi_bus_register_driver() to fail + * before doing anything ACPI-specific. Should we encounter a machine, + * which needs special handling (i.e. its hotkey device has a different + * HID), this bit will be moved. A global variable asus_info contains + * the DSDT header. + */ + status = acpi_get_table(ACPI_TABLE_DSDT, 1, &dsdt); + if (ACPI_FAILURE(status)) + printk(KERN_WARNING " Couldn't get the DSDT table header\n"); + else + asus_info = (struct acpi_table_header *) dsdt.pointer; + + /* We have to write 0 on init this far for all ASUS models */ + if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) { + printk(KERN_ERR " Hotkey initialization failed\n"); + return -ENODEV; + } + + /* This needs to be called for some laptops to init properly */ + if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result)) + printk(KERN_WARNING " Error calling BSTS\n"); + else if (bsts_result) + printk(KERN_NOTICE " BSTS called, 0x%02x returned\n", bsts_result); + + /* Samsung P30 has a device with a valid _HID whose INIT does not + * return anything. Catch this one and any similar here */ + if (buffer.pointer == NULL) { + if (asus_info && /* Samsung P30 */ + strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) { + hotk->model = P30; + printk(KERN_NOTICE " Samsung P30 detected, supported\n"); + } else { + hotk->model = M2E; + printk(KERN_WARNING " no string returned by INIT\n"); + printk(KERN_WARNING " trying default values, supply " + "the developers with your DSDT\n"); + } + hotk->methods = &model_conf[hotk->model]; + return AE_OK; + } + + model = (union acpi_object *) buffer.pointer; + if (model->type == ACPI_TYPE_STRING) { + printk(KERN_NOTICE " %s model detected, ", model->string.pointer); + } + + hotk->model = END_MODEL; + if (strncmp(model->string.pointer, "L3D", 3) == 0) + hotk->model = L3D; + else if (strncmp(model->string.pointer, "L3H", 3) == 0 || + strncmp(model->string.pointer, "L2E", 3) == 0) + hotk->model = L3H; + else if (strncmp(model->string.pointer, "L3", 2) == 0 || + strncmp(model->string.pointer, "L2B", 3) == 0) + hotk->model = L3C; + else if (strncmp(model->string.pointer, "L8L", 3) == 0) + hotk->model = L8L; + else if (strncmp(model->string.pointer, "L4R", 3) == 0) + hotk->model = L4R; + else if (strncmp(model->string.pointer, "M6N", 3) == 0) + hotk->model = M6N; + else if (strncmp(model->string.pointer, "M6R", 3) == 0) + hotk->model = M6R; + else if (strncmp(model->string.pointer, "M2N", 3) == 0 || + strncmp(model->string.pointer, "M3N", 3) == 0 || + strncmp(model->string.pointer, "M5N", 3) == 0 || + strncmp(model->string.pointer, "M6N", 3) == 0 || + strncmp(model->string.pointer, "S1N", 3) == 0 || + strncmp(model->string.pointer, "S5N", 3) == 0 || + strncmp(model->string.pointer, "W1N", 3) == 0) + hotk->model = xxN; + else if (strncmp(model->string.pointer, "M1", 2) == 0) + hotk->model = M1A; + else if (strncmp(model->string.pointer, "M2", 2) == 0 || + strncmp(model->string.pointer, "L4E", 3) == 0) + hotk->model = M2E; + else if (strncmp(model->string.pointer, "L2", 2) == 0) + hotk->model = L2D; + else if (strncmp(model->string.pointer, "L8", 2) == 0) + hotk->model = S1x; + else if (strncmp(model->string.pointer, "D1", 2) == 0) + hotk->model = D1x; + else if (strncmp(model->string.pointer, "A1", 2) == 0) + hotk->model = A1x; + else if (strncmp(model->string.pointer, "A2", 2) == 0) + hotk->model = A2x; + else if (strncmp(model->string.pointer, "J1", 2) == 0) + hotk->model = S2x; + else if (strncmp(model->string.pointer, "L5", 2) == 0) + hotk->model = L5x; + + if (hotk->model == END_MODEL) { + printk("unsupported, trying default values, supply the " + "developers with your DSDT\n"); + hotk->model = M2E; + } else { + printk("supported\n"); + } + + hotk->methods = &model_conf[hotk->model]; + + /* Sort of per-model blacklist */ + if (strncmp(model->string.pointer, "L2B", 3) == 0) + hotk->methods->lcd_status = NULL; + /* L2B is similar enough to L3C to use its settings, with this only + exception */ + else if (strncmp(model->string.pointer, "S5N", 3) == 0 || + strncmp(model->string.pointer, "M5N", 3) == 0) + hotk->methods->mt_mled = NULL; + /* S5N and M5N have no MLED */ + else if (strncmp(model->string.pointer, "M2N", 3) == 0 || + strncmp(model->string.pointer, "W1N", 3) == 0) + hotk->methods->mt_wled = "WLED"; + /* M2N and W1N have a usable WLED */ + else if (asus_info) { + if (strncmp(asus_info->oem_table_id, "L1", 2) == 0) + hotk->methods->mled_status = NULL; + /* S1300A reports L84F, but L1400B too, account for that */ + } + + acpi_os_free(model); + + return AE_OK; +} + + +static int __init asus_hotk_check(void) +{ + int result = 0; + + result = acpi_bus_get_status(hotk->device); + if (result) + return result; + + if (hotk->device->status.present) { + result = asus_hotk_get_info(); + } else { + printk(KERN_ERR " Hotkey device not present, aborting\n"); + return -EINVAL; + } + + return result; +} + + +static int __init asus_hotk_add(struct acpi_device *device) +{ + acpi_status status = AE_OK; + int result; + + if (!device) + return -EINVAL; + + printk(KERN_NOTICE "Asus Laptop ACPI Extras version %s\n", + ASUS_ACPI_VERSION); + + hotk = + (struct asus_hotk *) kmalloc(sizeof(struct asus_hotk), GFP_KERNEL); + if (!hotk) + return -ENOMEM; + memset(hotk, 0, sizeof(struct asus_hotk)); + + hotk->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_HOTK_CLASS); + acpi_driver_data(device) = hotk; + hotk->device = device; + + + result = asus_hotk_check(); + if (result) + goto end; + + result = asus_hotk_add_fs(device); + if (result) + goto end; + + /* + * We install the handler, it will receive the hotk in parameter, so, we + * could add other data to the hotk struct + */ + status = acpi_install_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY, + asus_hotk_notify, hotk); + if (ACPI_FAILURE(status)) + printk(KERN_ERR " Error installing notify handler\n"); + + /* For laptops without GPLV: init the hotk->brightness value */ + if ((!hotk->methods->brightness_get) && (!hotk->methods->brightness_status) && + (hotk->methods->brightness_up && hotk->methods->brightness_down)) { + status = acpi_evaluate_object(NULL, hotk->methods->brightness_down, + NULL, NULL); + if (ACPI_FAILURE(status)) + printk(KERN_WARNING " Error changing brightness\n"); + else { + status = acpi_evaluate_object(NULL, hotk->methods->brightness_up, + NULL, NULL); + if (ACPI_FAILURE(status)) + printk(KERN_WARNING " Strange, error changing" + " brightness\n"); + } + } + + end: + if (result) { + kfree(hotk); + } + + return result; +} + + +static int asus_hotk_remove(struct acpi_device *device, int type) +{ + acpi_status status = 0; + + if (!device || !acpi_driver_data(device)) + return -EINVAL; + + status = acpi_remove_notify_handler(hotk->handle, ACPI_SYSTEM_NOTIFY, + asus_hotk_notify); + if (ACPI_FAILURE(status)) + printk(KERN_ERR "Asus ACPI: Error removing notify handler\n"); + + asus_hotk_remove_fs(device); + + kfree(hotk); + + return 0; +} + + +static int __init asus_acpi_init(void) +{ + int result; + + if (acpi_disabled) + return -ENODEV; + + asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir); + if (!asus_proc_dir) { + printk(KERN_ERR "Asus ACPI: Unable to create /proc entry\n"); + return -ENODEV; + } + asus_proc_dir->owner = THIS_MODULE; + + result = acpi_bus_register_driver(&asus_hotk_driver); + if (result < 1) { + acpi_bus_unregister_driver(&asus_hotk_driver); + remove_proc_entry(PROC_ASUS, acpi_root_dir); + return -ENODEV; + } + + return 0; +} + + +static void __exit asus_acpi_exit(void) +{ + acpi_bus_unregister_driver(&asus_hotk_driver); + remove_proc_entry(PROC_ASUS, acpi_root_dir); + + acpi_os_free(asus_info); + + return; +} + +module_init(asus_acpi_init); +module_exit(asus_acpi_exit); diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c new file mode 100644 index 00000000000..c55feca9b7d --- /dev/null +++ b/drivers/acpi/battery.c @@ -0,0 +1,846 @@ +/* + * acpi_battery.c - ACPI Battery Driver ($Revision: 37 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF + +#define ACPI_BATTERY_FORMAT_BIF "NNNNNNNNNSSSS" +#define ACPI_BATTERY_FORMAT_BST "NNNN" + +#define ACPI_BATTERY_COMPONENT 0x00040000 +#define ACPI_BATTERY_CLASS "battery" +#define ACPI_BATTERY_HID "PNP0C0A" +#define ACPI_BATTERY_DRIVER_NAME "ACPI Battery Driver" +#define ACPI_BATTERY_DEVICE_NAME "Battery" +#define ACPI_BATTERY_FILE_INFO "info" +#define ACPI_BATTERY_FILE_STATUS "state" +#define ACPI_BATTERY_FILE_ALARM "alarm" +#define ACPI_BATTERY_NOTIFY_STATUS 0x80 +#define ACPI_BATTERY_NOTIFY_INFO 0x81 +#define ACPI_BATTERY_UNITS_WATTS "mW" +#define ACPI_BATTERY_UNITS_AMPS "mA" + + +#define _COMPONENT ACPI_BATTERY_COMPONENT +ACPI_MODULE_NAME ("acpi_battery") + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION(ACPI_BATTERY_DRIVER_NAME); +MODULE_LICENSE("GPL"); + +static int acpi_battery_add (struct acpi_device *device); +static int acpi_battery_remove (struct acpi_device *device, int type); + +static struct acpi_driver acpi_battery_driver = { + .name = ACPI_BATTERY_DRIVER_NAME, + .class = ACPI_BATTERY_CLASS, + .ids = ACPI_BATTERY_HID, + .ops = { + .add = acpi_battery_add, + .remove = acpi_battery_remove, + }, +}; + +struct acpi_battery_status { + acpi_integer state; + acpi_integer present_rate; + acpi_integer remaining_capacity; + acpi_integer present_voltage; +}; + +struct acpi_battery_info { + acpi_integer power_unit; + acpi_integer design_capacity; + acpi_integer last_full_capacity; + acpi_integer battery_technology; + acpi_integer design_voltage; + acpi_integer design_capacity_warning; + acpi_integer design_capacity_low; + acpi_integer battery_capacity_granularity_1; + acpi_integer battery_capacity_granularity_2; + acpi_string model_number; + acpi_string serial_number; + acpi_string battery_type; + acpi_string oem_info; +}; + +struct acpi_battery_flags { + u8 present:1; /* Bay occupied? */ + u8 power_unit:1; /* 0=watts, 1=apms */ + u8 alarm:1; /* _BTP present? */ + u8 reserved:5; +}; + +struct acpi_battery_trips { + unsigned long warning; + unsigned long low; +}; + +struct acpi_battery { + acpi_handle handle; + struct acpi_battery_flags flags; + struct acpi_battery_trips trips; + unsigned long alarm; + struct acpi_battery_info *info; +}; + + +/* -------------------------------------------------------------------------- + Battery Management + -------------------------------------------------------------------------- */ + +static int +acpi_battery_get_info ( + struct acpi_battery *battery, + struct acpi_battery_info **bif) +{ + int result = 0; + acpi_status status = 0; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_buffer format = {sizeof(ACPI_BATTERY_FORMAT_BIF), + ACPI_BATTERY_FORMAT_BIF}; + struct acpi_buffer data = {0, NULL}; + union acpi_object *package = NULL; + + ACPI_FUNCTION_TRACE("acpi_battery_get_info"); + + if (!battery || !bif) + return_VALUE(-EINVAL); + + /* Evalute _BIF */ + + status = acpi_evaluate_object(battery->handle, "_BIF", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _BIF\n")); + return_VALUE(-ENODEV); + } + + package = (union acpi_object *) buffer.pointer; + + /* Extract Package Data */ + + status = acpi_extract_package(package, &format, &data); + if (status != AE_BUFFER_OVERFLOW) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _BIF\n")); + result = -ENODEV; + goto end; + } + + data.pointer = kmalloc(data.length, GFP_KERNEL); + if (!data.pointer) { + result = -ENOMEM; + goto end; + } + memset(data.pointer, 0, data.length); + + status = acpi_extract_package(package, &format, &data); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _BIF\n")); + kfree(data.pointer); + result = -ENODEV; + goto end; + } + +end: + acpi_os_free(buffer.pointer); + + if (!result) + (*bif) = (struct acpi_battery_info *) data.pointer; + + return_VALUE(result); +} + +static int +acpi_battery_get_status ( + struct acpi_battery *battery, + struct acpi_battery_status **bst) +{ + int result = 0; + acpi_status status = 0; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_buffer format = {sizeof(ACPI_BATTERY_FORMAT_BST), + ACPI_BATTERY_FORMAT_BST}; + struct acpi_buffer data = {0, NULL}; + union acpi_object *package = NULL; + + ACPI_FUNCTION_TRACE("acpi_battery_get_status"); + + if (!battery || !bst) + return_VALUE(-EINVAL); + + /* Evalute _BST */ + + status = acpi_evaluate_object(battery->handle, "_BST", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _BST\n")); + return_VALUE(-ENODEV); + } + + package = (union acpi_object *) buffer.pointer; + + /* Extract Package Data */ + + status = acpi_extract_package(package, &format, &data); + if (status != AE_BUFFER_OVERFLOW) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _BST\n")); + result = -ENODEV; + goto end; + } + + data.pointer = kmalloc(data.length, GFP_KERNEL); + if (!data.pointer) { + result = -ENOMEM; + goto end; + } + memset(data.pointer, 0, data.length); + + status = acpi_extract_package(package, &format, &data); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _BST\n")); + kfree(data.pointer); + result = -ENODEV; + goto end; + } + +end: + acpi_os_free(buffer.pointer); + + if (!result) + (*bst) = (struct acpi_battery_status *) data.pointer; + + return_VALUE(result); +} + + +static int +acpi_battery_set_alarm ( + struct acpi_battery *battery, + unsigned long alarm) +{ + acpi_status status = 0; + union acpi_object arg0 = {ACPI_TYPE_INTEGER}; + struct acpi_object_list arg_list = {1, &arg0}; + + ACPI_FUNCTION_TRACE("acpi_battery_set_alarm"); + + if (!battery) + return_VALUE(-EINVAL); + + if (!battery->flags.alarm) + return_VALUE(-ENODEV); + + arg0.integer.value = alarm; + + status = acpi_evaluate_object(battery->handle, "_BTP", &arg_list, NULL); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm)); + + battery->alarm = alarm; + + return_VALUE(0); +} + + +static int +acpi_battery_check ( + struct acpi_battery *battery) +{ + int result = 0; + acpi_status status = AE_OK; + acpi_handle handle = NULL; + struct acpi_device *device = NULL; + struct acpi_battery_info *bif = NULL; + + ACPI_FUNCTION_TRACE("acpi_battery_check"); + + if (!battery) + return_VALUE(-EINVAL); + + result = acpi_bus_get_device(battery->handle, &device); + if (result) + return_VALUE(result); + + result = acpi_bus_get_status(device); + if (result) + return_VALUE(result); + + /* Insertion? */ + + if (!battery->flags.present && device->status.battery_present) { + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery inserted\n")); + + /* Evalute _BIF to get certain static information */ + + result = acpi_battery_get_info(battery, &bif); + if (result) + return_VALUE(result); + + battery->flags.power_unit = bif->power_unit; + battery->trips.warning = bif->design_capacity_warning; + battery->trips.low = bif->design_capacity_low; + kfree(bif); + + /* See if alarms are supported, and if so, set default */ + + status = acpi_get_handle(battery->handle, "_BTP", &handle); + if (ACPI_SUCCESS(status)) { + battery->flags.alarm = 1; + acpi_battery_set_alarm(battery, battery->trips.warning); + } + } + + /* Removal? */ + + else if (battery->flags.present && !device->status.battery_present) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery removed\n")); + } + + battery->flags.present = device->status.battery_present; + + return_VALUE(result); +} + + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_battery_dir; +static int acpi_battery_read_info(struct seq_file *seq, void *offset) +{ + int result = 0; + struct acpi_battery *battery = (struct acpi_battery *) seq->private; + struct acpi_battery_info *bif = NULL; + char *units = "?"; + + ACPI_FUNCTION_TRACE("acpi_battery_read_info"); + + if (!battery) + goto end; + + if (battery->flags.present) + seq_printf(seq, "present: yes\n"); + else { + seq_printf(seq, "present: no\n"); + goto end; + } + + /* Battery Info (_BIF) */ + + result = acpi_battery_get_info(battery, &bif); + if (result || !bif) { + seq_printf(seq, "ERROR: Unable to read battery information\n"); + goto end; + } + + units = bif->power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; + + if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "design capacity: unknown\n"); + else + seq_printf(seq, "design capacity: %d %sh\n", + (u32) bif->design_capacity, units); + + if (bif->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "last full capacity: unknown\n"); + else + seq_printf(seq, "last full capacity: %d %sh\n", + (u32) bif->last_full_capacity, units); + + switch ((u32) bif->battery_technology) { + case 0: + seq_printf(seq, "battery technology: non-rechargeable\n"); + break; + case 1: + seq_printf(seq, "battery technology: rechargeable\n"); + break; + default: + seq_printf(seq, "battery technology: unknown\n"); + break; + } + + if (bif->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "design voltage: unknown\n"); + else + seq_printf(seq, "design voltage: %d mV\n", + (u32) bif->design_voltage); + + seq_printf(seq, "design capacity warning: %d %sh\n", + (u32) bif->design_capacity_warning, units); + seq_printf(seq, "design capacity low: %d %sh\n", + (u32) bif->design_capacity_low, units); + seq_printf(seq, "capacity granularity 1: %d %sh\n", + (u32) bif->battery_capacity_granularity_1, units); + seq_printf(seq, "capacity granularity 2: %d %sh\n", + (u32) bif->battery_capacity_granularity_2, units); + seq_printf(seq, "model number: %s\n", + bif->model_number); + seq_printf(seq, "serial number: %s\n", + bif->serial_number); + seq_printf(seq, "battery type: %s\n", + bif->battery_type); + seq_printf(seq, "OEM info: %s\n", + bif->oem_info); + +end: + kfree(bif); + + return_VALUE(0); +} + +static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_battery_read_info, PDE(inode)->data); +} + + +static int +acpi_battery_read_state (struct seq_file *seq, void *offset) +{ + int result = 0; + struct acpi_battery *battery = (struct acpi_battery *) seq->private; + struct acpi_battery_status *bst = NULL; + char *units = "?"; + + ACPI_FUNCTION_TRACE("acpi_battery_read_state"); + + if (!battery) + goto end; + + if (battery->flags.present) + seq_printf(seq, "present: yes\n"); + else { + seq_printf(seq, "present: no\n"); + goto end; + } + + /* Battery Units */ + + units = battery->flags.power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; + + /* Battery Status (_BST) */ + + result = acpi_battery_get_status(battery, &bst); + if (result || !bst) { + seq_printf(seq, "ERROR: Unable to read battery status\n"); + goto end; + } + + if (!(bst->state & 0x04)) + seq_printf(seq, "capacity state: ok\n"); + else + seq_printf(seq, "capacity state: critical\n"); + + if ((bst->state & 0x01) && (bst->state & 0x02)){ + seq_printf(seq, "charging state: charging/discharging\n"); + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Battery Charging and Discharging?\n")); + } + else if (bst->state & 0x01) + seq_printf(seq, "charging state: discharging\n"); + else if (bst->state & 0x02) + seq_printf(seq, "charging state: charging\n"); + else { + seq_printf(seq, "charging state: charged\n"); + } + + if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "present rate: unknown\n"); + else + seq_printf(seq, "present rate: %d %s\n", + (u32) bst->present_rate, units); + + if (bst->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "remaining capacity: unknown\n"); + else + seq_printf(seq, "remaining capacity: %d %sh\n", + (u32) bst->remaining_capacity, units); + + if (bst->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN) + seq_printf(seq, "present voltage: unknown\n"); + else + seq_printf(seq, "present voltage: %d mV\n", + (u32) bst->present_voltage); + +end: + kfree(bst); + + return_VALUE(0); +} + +static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_battery_read_state, PDE(inode)->data); +} + + +static int +acpi_battery_read_alarm (struct seq_file *seq, void *offset) +{ + struct acpi_battery *battery = (struct acpi_battery *) seq->private; + char *units = "?"; + + ACPI_FUNCTION_TRACE("acpi_battery_read_alarm"); + + if (!battery) + goto end; + + if (!battery->flags.present) { + seq_printf(seq, "present: no\n"); + goto end; + } + + /* Battery Units */ + + units = battery->flags.power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; + + /* Battery Alarm */ + + seq_printf(seq, "alarm: "); + if (!battery->alarm) + seq_printf(seq, "unsupported\n"); + else + seq_printf(seq, "%d %sh\n", (u32) battery->alarm, units); + +end: + return_VALUE(0); +} + + +static ssize_t +acpi_battery_write_alarm ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *ppos) +{ + int result = 0; + char alarm_string[12] = {'\0'}; + struct seq_file *m = (struct seq_file *)file->private_data; + struct acpi_battery *battery = (struct acpi_battery *)m->private; + + ACPI_FUNCTION_TRACE("acpi_battery_write_alarm"); + + if (!battery || (count > sizeof(alarm_string) - 1)) + return_VALUE(-EINVAL); + + if (!battery->flags.present) + return_VALUE(-ENODEV); + + if (copy_from_user(alarm_string, buffer, count)) + return_VALUE(-EFAULT); + + alarm_string[count] = '\0'; + + result = acpi_battery_set_alarm(battery, + simple_strtoul(alarm_string, NULL, 0)); + if (result) + return_VALUE(result); + + return_VALUE(count); +} + +static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_battery_read_alarm, PDE(inode)->data); +} + +static struct file_operations acpi_battery_info_ops = { + .open = acpi_battery_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct file_operations acpi_battery_state_ops = { + .open = acpi_battery_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct file_operations acpi_battery_alarm_ops = { + .open = acpi_battery_alarm_open_fs, + .read = seq_read, + .write = acpi_battery_write_alarm, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int +acpi_battery_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_battery_add_fs"); + + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_battery_dir); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; + } + + /* 'info' [R] */ + entry = create_proc_entry(ACPI_BATTERY_FILE_INFO, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_BATTERY_FILE_INFO)); + else { + entry->proc_fops = &acpi_battery_info_ops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'status' [R] */ + entry = create_proc_entry(ACPI_BATTERY_FILE_STATUS, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_BATTERY_FILE_STATUS)); + else { + entry->proc_fops = &acpi_battery_state_ops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'alarm' [R/W] */ + entry = create_proc_entry(ACPI_BATTERY_FILE_ALARM, + S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_BATTERY_FILE_ALARM)); + else { + entry->proc_fops = &acpi_battery_alarm_ops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + return_VALUE(0); +} + + +static int +acpi_battery_remove_fs ( + struct acpi_device *device) +{ + ACPI_FUNCTION_TRACE("acpi_battery_remove_fs"); + + if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_BATTERY_FILE_ALARM, + acpi_device_dir(device)); + remove_proc_entry(ACPI_BATTERY_FILE_STATUS, + acpi_device_dir(device)); + remove_proc_entry(ACPI_BATTERY_FILE_INFO, + acpi_device_dir(device)); + + remove_proc_entry(acpi_device_bid(device), acpi_battery_dir); + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static void +acpi_battery_notify ( + acpi_handle handle, + u32 event, + void *data) +{ + struct acpi_battery *battery = (struct acpi_battery *) data; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_battery_notify"); + + if (!battery) + return_VOID; + + if (acpi_bus_get_device(handle, &device)) + return_VOID; + + switch (event) { + case ACPI_BATTERY_NOTIFY_STATUS: + case ACPI_BATTERY_NOTIFY_INFO: + acpi_battery_check(battery); + acpi_bus_generate_event(device, event, battery->flags.present); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + return_VOID; +} + + +static int +acpi_battery_add ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = 0; + struct acpi_battery *battery = NULL; + + ACPI_FUNCTION_TRACE("acpi_battery_add"); + + if (!device) + return_VALUE(-EINVAL); + + battery = kmalloc(sizeof(struct acpi_battery), GFP_KERNEL); + if (!battery) + return_VALUE(-ENOMEM); + memset(battery, 0, sizeof(struct acpi_battery)); + + battery->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS); + acpi_driver_data(device) = battery; + + result = acpi_battery_check(battery); + if (result) + goto end; + + result = acpi_battery_add_fs(device); + if (result) + goto end; + + status = acpi_install_notify_handler(battery->handle, + ACPI_DEVICE_NOTIFY, acpi_battery_notify, battery); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing notify handler\n")); + result = -ENODEV; + goto end; + } + + printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n", + ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device), + device->status.battery_present?"present":"absent"); + +end: + if (result) { + acpi_battery_remove_fs(device); + kfree(battery); + } + + return_VALUE(result); +} + + +static int +acpi_battery_remove ( + struct acpi_device *device, + int type) +{ + acpi_status status = 0; + struct acpi_battery *battery = NULL; + + ACPI_FUNCTION_TRACE("acpi_battery_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + battery = (struct acpi_battery *) acpi_driver_data(device); + + status = acpi_remove_notify_handler(battery->handle, + ACPI_DEVICE_NOTIFY, acpi_battery_notify); + if (ACPI_FAILURE(status)) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error removing notify handler\n")); + + acpi_battery_remove_fs(device); + + kfree(battery); + + return_VALUE(0); +} + + +static int __init +acpi_battery_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_battery_init"); + + acpi_battery_dir = proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir); + if (!acpi_battery_dir) + return_VALUE(-ENODEV); + acpi_battery_dir->owner = THIS_MODULE; + + result = acpi_bus_register_driver(&acpi_battery_driver); + if (result < 0) { + remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + + +static void __exit +acpi_battery_exit (void) +{ + ACPI_FUNCTION_TRACE("acpi_battery_exit"); + + acpi_bus_unregister_driver(&acpi_battery_driver); + + remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); + + return_VOID; +} + + +module_init(acpi_battery_init); +module_exit(acpi_battery_exit); diff --git a/drivers/acpi/blacklist.c b/drivers/acpi/blacklist.c new file mode 100644 index 00000000000..4c010e7f11b --- /dev/null +++ b/drivers/acpi/blacklist.c @@ -0,0 +1,169 @@ +/* + * blacklist.c + * + * Check to see if the given machine has a known bad ACPI BIOS + * or if the BIOS is too old. + * + * Copyright (C) 2004 Len Brown <len.brown@intel.com> + * Copyright (C) 2002 Andy Grover <andrew.grover@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> +#include <linux/dmi.h> + +enum acpi_blacklist_predicates +{ + all_versions, + less_than_or_equal, + equal, + greater_than_or_equal, +}; + +struct acpi_blacklist_item +{ + char oem_id[7]; + char oem_table_id[9]; + u32 oem_revision; + acpi_table_type table; + enum acpi_blacklist_predicates oem_revision_predicate; + char *reason; + u32 is_critical_error; +}; + +/* + * POLICY: If *anything* doesn't work, put it on the blacklist. + * If they are critical errors, mark it critical, and abort driver load. + */ +static struct acpi_blacklist_item acpi_blacklist[] __initdata = +{ + /* Compaq Presario 1700 */ + {"PTLTD ", " DSDT ", 0x06040000, ACPI_DSDT, less_than_or_equal, "Multiple problems", 1}, + /* Sony FX120, FX140, FX150? */ + {"SONY ", "U0 ", 0x20010313, ACPI_DSDT, less_than_or_equal, "ACPI driver problem", 1}, + /* Compaq Presario 800, Insyde BIOS */ + {"INT440", "SYSFexxx", 0x00001001, ACPI_DSDT, less_than_or_equal, "Does not use _REG to protect EC OpRegions", 1}, + /* IBM 600E - _ADR should return 7, but it returns 1 */ + {"IBM ", "TP600E ", 0x00000105, ACPI_DSDT, less_than_or_equal, "Incorrect _ADR", 1}, + {"ASUS\0\0", "P2B-S ", 0, ACPI_DSDT, all_versions, "Bogus PCI routing", 1}, + + {""} +}; + + +#if CONFIG_ACPI_BLACKLIST_YEAR + +static int __init +blacklist_by_year(void) +{ + int year; + char *s = dmi_get_system_info(DMI_BIOS_DATE); + + if (!s) + return 0; + if (!*s) + return 0; + + s = strrchr(s, '/'); + if (!s) + return 0; + + s += 1; + + year = simple_strtoul(s,NULL,0); + + if (year < 100) { /* 2-digit year */ + year += 1900; + if (year < 1996) /* no dates < spec 1.0 */ + year += 100; + } + + if (year < CONFIG_ACPI_BLACKLIST_YEAR) { + printk(KERN_ERR PREFIX "BIOS age (%d) fails cutoff (%d), " + "acpi=force is required to enable ACPI\n", + year, CONFIG_ACPI_BLACKLIST_YEAR); + return 1; + } + return 0; +} +#else +static inline int blacklist_by_year(void) { return 0; } +#endif + +int __init +acpi_blacklisted(void) +{ + int i = 0; + int blacklisted = 0; + struct acpi_table_header *table_header; + + while (acpi_blacklist[i].oem_id[0] != '\0') + { + if (acpi_get_table_header_early(acpi_blacklist[i].table, &table_header)) { + i++; + continue; + } + + if (strncmp(acpi_blacklist[i].oem_id, table_header->oem_id, 6)) { + i++; + continue; + } + + if (strncmp(acpi_blacklist[i].oem_table_id, table_header->oem_table_id, 8)) { + i++; + continue; + } + + if ((acpi_blacklist[i].oem_revision_predicate == all_versions) + || (acpi_blacklist[i].oem_revision_predicate == less_than_or_equal + && table_header->oem_revision <= acpi_blacklist[i].oem_revision) + || (acpi_blacklist[i].oem_revision_predicate == greater_than_or_equal + && table_header->oem_revision >= acpi_blacklist[i].oem_revision) + || (acpi_blacklist[i].oem_revision_predicate == equal + && table_header->oem_revision == acpi_blacklist[i].oem_revision)) { + + printk(KERN_ERR PREFIX "Vendor \"%6.6s\" System \"%8.8s\" " + "Revision 0x%x has a known ACPI BIOS problem.\n", + acpi_blacklist[i].oem_id, + acpi_blacklist[i].oem_table_id, + acpi_blacklist[i].oem_revision); + + printk(KERN_ERR PREFIX "Reason: %s. This is a %s error\n", + acpi_blacklist[i].reason, + (acpi_blacklist[i].is_critical_error ? "non-recoverable" : "recoverable")); + + blacklisted = acpi_blacklist[i].is_critical_error; + break; + } + else { + i++; + } + } + + blacklisted += blacklist_by_year(); + + return blacklisted; +} + diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c new file mode 100644 index 00000000000..4edff173857 --- /dev/null +++ b/drivers/acpi/bus.c @@ -0,0 +1,775 @@ +/* + * acpi_bus.c - ACPI Bus Driver ($Revision: 80 $) + * + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/list.h> +#include <linux/sched.h> +#include <linux/pm.h> +#include <linux/device.h> +#include <linux/proc_fs.h> +#ifdef CONFIG_X86 +#include <asm/mpspec.h> +#endif +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define _COMPONENT ACPI_BUS_COMPONENT +ACPI_MODULE_NAME ("acpi_bus") + +#ifdef CONFIG_X86 +extern void __init acpi_pic_sci_set_trigger(unsigned int irq, u16 trigger); +#endif + +FADT_DESCRIPTOR acpi_fadt; +EXPORT_SYMBOL(acpi_fadt); + +struct acpi_device *acpi_root; +struct proc_dir_entry *acpi_root_dir; +EXPORT_SYMBOL(acpi_root_dir); + +#define STRUCT_TO_INT(s) (*((int*)&s)) + +/* -------------------------------------------------------------------------- + Device Management + -------------------------------------------------------------------------- */ + +int +acpi_bus_get_device ( + acpi_handle handle, + struct acpi_device **device) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE("acpi_bus_get_device"); + + if (!device) + return_VALUE(-EINVAL); + + /* TBD: Support fixed-feature devices */ + + status = acpi_get_data(handle, acpi_bus_data_handler, (void**) device); + if (ACPI_FAILURE(status) || !*device) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "No context for object [%p]\n", + handle)); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} +EXPORT_SYMBOL(acpi_bus_get_device); + +int +acpi_bus_get_status ( + struct acpi_device *device) +{ + acpi_status status = AE_OK; + unsigned long sta = 0; + + ACPI_FUNCTION_TRACE("acpi_bus_get_status"); + + if (!device) + return_VALUE(-EINVAL); + + /* + * Evaluate _STA if present. + */ + if (device->flags.dynamic_status) { + status = acpi_evaluate_integer(device->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + STRUCT_TO_INT(device->status) = (int) sta; + } + + /* + * Otherwise we assume the status of our parent (unless we don't + * have one, in which case status is implied). + */ + else if (device->parent) + device->status = device->parent->status; + else + STRUCT_TO_INT(device->status) = 0x0F; + + if (device->status.functional && !device->status.present) { + printk(KERN_WARNING PREFIX "Device [%s] status [%08x]: " + "functional but not present; setting present\n", + device->pnp.bus_id, + (u32) STRUCT_TO_INT(device->status)); + device->status.present = 1; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] status [%08x]\n", + device->pnp.bus_id, (u32) STRUCT_TO_INT(device->status))); + + return_VALUE(0); +} +EXPORT_SYMBOL(acpi_bus_get_status); + + +/* -------------------------------------------------------------------------- + Power Management + -------------------------------------------------------------------------- */ + +int +acpi_bus_get_power ( + acpi_handle handle, + int *state) +{ + int result = 0; + acpi_status status = 0; + struct acpi_device *device = NULL; + unsigned long psc = 0; + + ACPI_FUNCTION_TRACE("acpi_bus_get_power"); + + result = acpi_bus_get_device(handle, &device); + if (result) + return_VALUE(result); + + *state = ACPI_STATE_UNKNOWN; + + if (!device->flags.power_manageable) { + /* TBD: Non-recursive algorithm for walking up hierarchy */ + if (device->parent) + *state = device->parent->power.state; + else + *state = ACPI_STATE_D0; + } + else { + /* + * Get the device's power state either directly (via _PSC) or + * indirectly (via power resources). + */ + if (device->power.flags.explicit_get) { + status = acpi_evaluate_integer(device->handle, "_PSC", + NULL, &psc); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + device->power.state = (int) psc; + } + else if (device->power.flags.power_resources) { + result = acpi_power_get_inferred_state(device); + if (result) + return_VALUE(result); + } + + *state = device->power.state; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] power state is D%d\n", + device->pnp.bus_id, device->power.state)); + + return_VALUE(0); +} +EXPORT_SYMBOL(acpi_bus_get_power); + + +int +acpi_bus_set_power ( + acpi_handle handle, + int state) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_device *device = NULL; + char object_name[5] = {'_','P','S','0'+state,'\0'}; + + ACPI_FUNCTION_TRACE("acpi_bus_set_power"); + + result = acpi_bus_get_device(handle, &device); + if (result) + return_VALUE(result); + + if ((state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) + return_VALUE(-EINVAL); + + /* Make sure this is a valid target state */ + + if (!device->flags.power_manageable) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Device is not power manageable\n")); + return_VALUE(-ENODEV); + } + if (state == device->power.state) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device is already at D%d\n", state)); + return_VALUE(0); + } + if (!device->power.states[state].flags.valid) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Device does not support D%d\n", state)); + return_VALUE(-ENODEV); + } + if (device->parent && (state < device->parent->power.state)) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Cannot set device to a higher-powered state than parent\n")); + return_VALUE(-ENODEV); + } + + /* + * Transition Power + * ---------------- + * On transitions to a high-powered state we first apply power (via + * power resources) then evalute _PSx. Conversly for transitions to + * a lower-powered state. + */ + if (state < device->power.state) { + if (device->power.flags.power_resources) { + result = acpi_power_transition(device, state); + if (result) + goto end; + } + if (device->power.states[state].flags.explicit_set) { + status = acpi_evaluate_object(device->handle, + object_name, NULL, NULL); + if (ACPI_FAILURE(status)) { + result = -ENODEV; + goto end; + } + } + } + else { + if (device->power.states[state].flags.explicit_set) { + status = acpi_evaluate_object(device->handle, + object_name, NULL, NULL); + if (ACPI_FAILURE(status)) { + result = -ENODEV; + goto end; + } + } + if (device->power.flags.power_resources) { + result = acpi_power_transition(device, state); + if (result) + goto end; + } + } + +end: + if (result) + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error transitioning device [%s] to D%d\n", + device->pnp.bus_id, state)); + else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] transitioned to D%d\n", + device->pnp.bus_id, state)); + + return_VALUE(result); +} +EXPORT_SYMBOL(acpi_bus_set_power); + + + +/* -------------------------------------------------------------------------- + Event Management + -------------------------------------------------------------------------- */ + +static DEFINE_SPINLOCK(acpi_bus_event_lock); + +LIST_HEAD(acpi_bus_event_list); +DECLARE_WAIT_QUEUE_HEAD(acpi_bus_event_queue); + +extern int event_is_open; + +int +acpi_bus_generate_event ( + struct acpi_device *device, + u8 type, + int data) +{ + struct acpi_bus_event *event = NULL; + unsigned long flags = 0; + + ACPI_FUNCTION_TRACE("acpi_bus_generate_event"); + + if (!device) + return_VALUE(-EINVAL); + + /* drop event on the floor if no one's listening */ + if (!event_is_open) + return_VALUE(0); + + event = kmalloc(sizeof(struct acpi_bus_event), GFP_ATOMIC); + if (!event) + return_VALUE(-ENOMEM); + + strcpy(event->device_class, device->pnp.device_class); + strcpy(event->bus_id, device->pnp.bus_id); + event->type = type; + event->data = data; + + spin_lock_irqsave(&acpi_bus_event_lock, flags); + list_add_tail(&event->node, &acpi_bus_event_list); + spin_unlock_irqrestore(&acpi_bus_event_lock, flags); + + wake_up_interruptible(&acpi_bus_event_queue); + + return_VALUE(0); +} +EXPORT_SYMBOL(acpi_bus_generate_event); + +int +acpi_bus_receive_event ( + struct acpi_bus_event *event) +{ + unsigned long flags = 0; + struct acpi_bus_event *entry = NULL; + + DECLARE_WAITQUEUE(wait, current); + + ACPI_FUNCTION_TRACE("acpi_bus_receive_event"); + + if (!event) + return_VALUE(-EINVAL); + + if (list_empty(&acpi_bus_event_list)) { + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&acpi_bus_event_queue, &wait); + + if (list_empty(&acpi_bus_event_list)) + schedule(); + + remove_wait_queue(&acpi_bus_event_queue, &wait); + set_current_state(TASK_RUNNING); + + if (signal_pending(current)) + return_VALUE(-ERESTARTSYS); + } + + spin_lock_irqsave(&acpi_bus_event_lock, flags); + entry = list_entry(acpi_bus_event_list.next, struct acpi_bus_event, node); + if (entry) + list_del(&entry->node); + spin_unlock_irqrestore(&acpi_bus_event_lock, flags); + + if (!entry) + return_VALUE(-ENODEV); + + memcpy(event, entry, sizeof(struct acpi_bus_event)); + + kfree(entry); + + return_VALUE(0); +} +EXPORT_SYMBOL(acpi_bus_receive_event); + + +/* -------------------------------------------------------------------------- + Notification Handling + -------------------------------------------------------------------------- */ + +static int +acpi_bus_check_device ( + struct acpi_device *device, + int *status_changed) +{ + acpi_status status = 0; + struct acpi_device_status old_status; + + ACPI_FUNCTION_TRACE("acpi_bus_check_device"); + + if (!device) + return_VALUE(-EINVAL); + + if (status_changed) + *status_changed = 0; + + old_status = device->status; + + /* + * Make sure this device's parent is present before we go about + * messing with the device. + */ + if (device->parent && !device->parent->status.present) { + device->status = device->parent->status; + if (STRUCT_TO_INT(old_status) != STRUCT_TO_INT(device->status)) { + if (status_changed) + *status_changed = 1; + } + return_VALUE(0); + } + + status = acpi_bus_get_status(device); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + if (STRUCT_TO_INT(old_status) == STRUCT_TO_INT(device->status)) + return_VALUE(0); + + if (status_changed) + *status_changed = 1; + + /* + * Device Insertion/Removal + */ + if ((device->status.present) && !(old_status.present)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device insertion detected\n")); + /* TBD: Handle device insertion */ + } + else if (!(device->status.present) && (old_status.present)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device removal detected\n")); + /* TBD: Handle device removal */ + } + + return_VALUE(0); +} + + +static int +acpi_bus_check_scope ( + struct acpi_device *device) +{ + int result = 0; + int status_changed = 0; + + ACPI_FUNCTION_TRACE("acpi_bus_check_scope"); + + if (!device) + return_VALUE(-EINVAL); + + /* Status Change? */ + result = acpi_bus_check_device(device, &status_changed); + if (result) + return_VALUE(result); + + if (!status_changed) + return_VALUE(0); + + /* + * TBD: Enumerate child devices within this device's scope and + * run acpi_bus_check_device()'s on them. + */ + + return_VALUE(0); +} + + +/** + * acpi_bus_notify + * --------------- + * Callback for all 'system-level' device notifications (values 0x00-0x7F). + */ +static void +acpi_bus_notify ( + acpi_handle handle, + u32 type, + void *data) +{ + int result = 0; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_bus_notify"); + + if (acpi_bus_get_device(handle, &device)) + return_VOID; + + switch (type) { + + case ACPI_NOTIFY_BUS_CHECK: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received BUS CHECK notification for device [%s]\n", + device->pnp.bus_id)); + result = acpi_bus_check_scope(device); + /* + * TBD: We'll need to outsource certain events to non-ACPI + * drivers via the device manager (device.c). + */ + break; + + case ACPI_NOTIFY_DEVICE_CHECK: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received DEVICE CHECK notification for device [%s]\n", + device->pnp.bus_id)); + result = acpi_bus_check_device(device, NULL); + /* + * TBD: We'll need to outsource certain events to non-ACPI + * drivers via the device manager (device.c). + */ + break; + + case ACPI_NOTIFY_DEVICE_WAKE: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received DEVICE WAKE notification for device [%s]\n", + device->pnp.bus_id)); + /* TBD */ + break; + + case ACPI_NOTIFY_EJECT_REQUEST: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received EJECT REQUEST notification for device [%s]\n", + device->pnp.bus_id)); + /* TBD */ + break; + + case ACPI_NOTIFY_DEVICE_CHECK_LIGHT: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received DEVICE CHECK LIGHT notification for device [%s]\n", + device->pnp.bus_id)); + /* TBD: Exactly what does 'light' mean? */ + break; + + case ACPI_NOTIFY_FREQUENCY_MISMATCH: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received FREQUENCY MISMATCH notification for device [%s]\n", + device->pnp.bus_id)); + /* TBD */ + break; + + case ACPI_NOTIFY_BUS_MODE_MISMATCH: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received BUS MODE MISMATCH notification for device [%s]\n", + device->pnp.bus_id)); + /* TBD */ + break; + + case ACPI_NOTIFY_POWER_FAULT: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received POWER FAULT notification for device [%s]\n", + device->pnp.bus_id)); + /* TBD */ + break; + + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Received unknown/unsupported notification [%08x]\n", + type)); + break; + } + + return_VOID; +} + +/* -------------------------------------------------------------------------- + Initialization/Cleanup + -------------------------------------------------------------------------- */ + +static int __init +acpi_bus_init_irq (void) +{ + acpi_status status = AE_OK; + union acpi_object arg = {ACPI_TYPE_INTEGER}; + struct acpi_object_list arg_list = {1, &arg}; + char *message = NULL; + + ACPI_FUNCTION_TRACE("acpi_bus_init_irq"); + + /* + * Let the system know what interrupt model we are using by + * evaluating the \_PIC object, if exists. + */ + + switch (acpi_irq_model) { + case ACPI_IRQ_MODEL_PIC: + message = "PIC"; + break; + case ACPI_IRQ_MODEL_IOAPIC: + message = "IOAPIC"; + break; + case ACPI_IRQ_MODEL_IOSAPIC: + message = "IOSAPIC"; + break; + default: + printk(KERN_WARNING PREFIX "Unknown interrupt routing model\n"); + return_VALUE(-ENODEV); + } + + printk(KERN_INFO PREFIX "Using %s for interrupt routing\n", message); + + arg.integer.value = acpi_irq_model; + + status = acpi_evaluate_object(NULL, "\\_PIC", &arg_list, NULL); + if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PIC\n")); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + + +void __init +acpi_early_init (void) +{ + acpi_status status = AE_OK; + struct acpi_buffer buffer = {sizeof(acpi_fadt), &acpi_fadt}; + + ACPI_FUNCTION_TRACE("acpi_early_init"); + + if (acpi_disabled) + return_VOID; + + /* enable workarounds, unless strict ACPI spec. compliance */ + if (!acpi_strict) + acpi_gbl_enable_interpreter_slack = TRUE; + + status = acpi_initialize_subsystem(); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to initialize the ACPI Interpreter\n"); + goto error0; + } + + status = acpi_load_tables(); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to load the System Description Tables\n"); + goto error0; + } + + /* + * Get a separate copy of the FADT for use by other drivers. + */ + status = acpi_get_table(ACPI_TABLE_FADT, 1, &buffer); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to get the FADT\n"); + goto error0; + } + +#ifdef CONFIG_X86 + if (!acpi_ioapic) { + extern acpi_interrupt_flags acpi_sci_flags; + + /* compatible (0) means level (3) */ + if (acpi_sci_flags.trigger == 0) + acpi_sci_flags.trigger = 3; + + /* Set PIC-mode SCI trigger type */ + acpi_pic_sci_set_trigger(acpi_fadt.sci_int, acpi_sci_flags.trigger); + } else { + extern int acpi_sci_override_gsi; + /* + * now that acpi_fadt is initialized, + * update it with result from INT_SRC_OVR parsing + */ + acpi_fadt.sci_int = acpi_sci_override_gsi; + } +#endif + + status = acpi_enable_subsystem(~(ACPI_NO_HARDWARE_INIT | ACPI_NO_ACPI_ENABLE)); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to enable ACPI\n"); + goto error0; + } + + return_VOID; + +error0: + disable_acpi(); + return_VOID; +} + +static int __init +acpi_bus_init (void) +{ + int result = 0; + acpi_status status = AE_OK; + extern acpi_status acpi_os_initialize1(void); + + ACPI_FUNCTION_TRACE("acpi_bus_init"); + + status = acpi_os_initialize1(); + + status = acpi_enable_subsystem(ACPI_NO_HARDWARE_INIT | ACPI_NO_ACPI_ENABLE); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to start the ACPI Interpreter\n"); + goto error1; + } + + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to initialize ACPI OS objects\n"); + goto error1; + } +#ifdef CONFIG_ACPI_EC + /* + * ACPI 2.0 requires the EC driver to be loaded and work before + * the EC device is found in the namespace (i.e. before acpi_initialize_objects() + * is called). + * + * This is accomplished by looking for the ECDT table, and getting + * the EC parameters out of that. + */ + status = acpi_ec_ecdt_probe(); + /* Ignore result. Not having an ECDT is not fatal. */ +#endif + + status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to initialize ACPI objects\n"); + goto error1; + } + + printk(KERN_INFO PREFIX "Interpreter enabled\n"); + + /* + * Get the system interrupt model and evaluate \_PIC. + */ + result = acpi_bus_init_irq(); + if (result) + goto error1; + + /* + * Register the for all standard device notifications. + */ + status = acpi_install_notify_handler(ACPI_ROOT_OBJECT, ACPI_SYSTEM_NOTIFY, &acpi_bus_notify, NULL); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Unable to register for device notifications\n"); + goto error1; + } + + /* + * Create the top ACPI proc directory + */ + acpi_root_dir = proc_mkdir(ACPI_BUS_FILE_ROOT, NULL); + + return_VALUE(0); + + /* Mimic structured exception handling */ +error1: + acpi_terminate(); + return_VALUE(-ENODEV); +} + +decl_subsys(acpi,NULL,NULL); + +static int __init acpi_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_init"); + + printk(KERN_INFO PREFIX "Subsystem revision %08x\n", + ACPI_CA_VERSION); + + if (acpi_disabled) { + printk(KERN_INFO PREFIX "Interpreter disabled.\n"); + return_VALUE(-ENODEV); + } + + firmware_register(&acpi_subsys); + + result = acpi_bus_init(); + + if (!result) { +#ifdef CONFIG_PM + if (!PM_IS_ACTIVE()) + pm_active = 1; + else { + printk(KERN_INFO PREFIX "APM is already active, exiting\n"); + disable_acpi(); + result = -ENODEV; + } +#endif + } else + disable_acpi(); + + return_VALUE(result); +} + +subsys_initcall(acpi_init); diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c new file mode 100644 index 00000000000..ec4430e3053 --- /dev/null +++ b/drivers/acpi/button.c @@ -0,0 +1,558 @@ +/* + * acpi_button.c - ACPI Button Driver ($Revision: 30 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define ACPI_BUTTON_COMPONENT 0x00080000 +#define ACPI_BUTTON_DRIVER_NAME "ACPI Button Driver" +#define ACPI_BUTTON_CLASS "button" +#define ACPI_BUTTON_FILE_INFO "info" +#define ACPI_BUTTON_FILE_STATE "state" +#define ACPI_BUTTON_TYPE_UNKNOWN 0x00 +#define ACPI_BUTTON_NOTIFY_STATUS 0x80 + +#define ACPI_BUTTON_SUBCLASS_POWER "power" +#define ACPI_BUTTON_HID_POWER "PNP0C0C" +#define ACPI_BUTTON_DEVICE_NAME_POWER "Power Button (CM)" +#define ACPI_BUTTON_DEVICE_NAME_POWERF "Power Button (FF)" +#define ACPI_BUTTON_TYPE_POWER 0x01 +#define ACPI_BUTTON_TYPE_POWERF 0x02 + +#define ACPI_BUTTON_SUBCLASS_SLEEP "sleep" +#define ACPI_BUTTON_HID_SLEEP "PNP0C0E" +#define ACPI_BUTTON_DEVICE_NAME_SLEEP "Sleep Button (CM)" +#define ACPI_BUTTON_DEVICE_NAME_SLEEPF "Sleep Button (FF)" +#define ACPI_BUTTON_TYPE_SLEEP 0x03 +#define ACPI_BUTTON_TYPE_SLEEPF 0x04 + +#define ACPI_BUTTON_SUBCLASS_LID "lid" +#define ACPI_BUTTON_HID_LID "PNP0C0D" +#define ACPI_BUTTON_DEVICE_NAME_LID "Lid Switch" +#define ACPI_BUTTON_TYPE_LID 0x05 + +#define _COMPONENT ACPI_BUTTON_COMPONENT +ACPI_MODULE_NAME ("acpi_button") + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME); +MODULE_LICENSE("GPL"); + + +static int acpi_button_add (struct acpi_device *device); +static int acpi_button_remove (struct acpi_device *device, int type); +static int acpi_button_info_open_fs(struct inode *inode, struct file *file); +static int acpi_button_state_open_fs(struct inode *inode, struct file *file); + +static struct acpi_driver acpi_button_driver = { + .name = ACPI_BUTTON_DRIVER_NAME, + .class = ACPI_BUTTON_CLASS, + .ids = "ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E", + .ops = { + .add = acpi_button_add, + .remove = acpi_button_remove, + }, +}; + +struct acpi_button { + acpi_handle handle; + struct acpi_device *device; /* Fixed button kludge */ + u8 type; + unsigned long pushed; +}; + +static struct file_operations acpi_button_info_fops = { + .open = acpi_button_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_button_state_fops = { + .open = acpi_button_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_button_dir; + +static int acpi_button_info_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_button *button = (struct acpi_button *) seq->private; + + ACPI_FUNCTION_TRACE("acpi_button_info_seq_show"); + + if (!button || !button->device) + return_VALUE(0); + + seq_printf(seq, "type: %s\n", + acpi_device_name(button->device)); + + return_VALUE(0); +} + +static int acpi_button_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_button_info_seq_show, PDE(inode)->data); +} + +static int acpi_button_state_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_button *button = (struct acpi_button *) seq->private; + acpi_status status; + unsigned long state; + + ACPI_FUNCTION_TRACE("acpi_button_state_seq_show"); + + if (!button || !button->device) + return_VALUE(0); + + status = acpi_evaluate_integer(button->handle,"_LID",NULL,&state); + if (ACPI_FAILURE(status)) { + seq_printf(seq, "state: unsupported\n"); + } + else{ + seq_printf(seq, "state: %s\n", (state ? "open" : "closed")); + } + + return_VALUE(0); +} + +static int acpi_button_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_button_state_seq_show, PDE(inode)->data); +} + +static int +acpi_button_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + struct acpi_button *button = NULL; + + ACPI_FUNCTION_TRACE("acpi_button_add_fs"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + button = acpi_driver_data(device); + + switch (button->type) { + case ACPI_BUTTON_TYPE_POWER: + case ACPI_BUTTON_TYPE_POWERF: + entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER, + acpi_button_dir); + break; + case ACPI_BUTTON_TYPE_SLEEP: + case ACPI_BUTTON_TYPE_SLEEPF: + entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP, + acpi_button_dir); + break; + case ACPI_BUTTON_TYPE_LID: + entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID, + acpi_button_dir); + break; + } + + if (!entry) + return_VALUE(-ENODEV); + entry->owner = THIS_MODULE; + + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; + + /* 'info' [R] */ + entry = create_proc_entry(ACPI_BUTTON_FILE_INFO, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_BUTTON_FILE_INFO)); + else { + entry->proc_fops = &acpi_button_info_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* show lid state [R] */ + if (button->type == ACPI_BUTTON_TYPE_LID) { + entry = create_proc_entry(ACPI_BUTTON_FILE_STATE, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_BUTTON_FILE_INFO)); + else { + entry->proc_fops = &acpi_button_state_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + } + + return_VALUE(0); +} + + +static int +acpi_button_remove_fs ( + struct acpi_device *device) +{ + struct acpi_button *button = NULL; + + ACPI_FUNCTION_TRACE("acpi_button_remove_fs"); + + button = acpi_driver_data(device); + if (acpi_device_dir(device)) { + if (button->type == ACPI_BUTTON_TYPE_LID) + remove_proc_entry(ACPI_BUTTON_FILE_STATE, + acpi_device_dir(device)); + remove_proc_entry(ACPI_BUTTON_FILE_INFO, + acpi_device_dir(device)); + + remove_proc_entry(acpi_device_bid(device), + acpi_device_dir(device)->parent); + + + switch (button->type) { + case ACPI_BUTTON_TYPE_POWER: + case ACPI_BUTTON_TYPE_POWERF: + remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, + acpi_button_dir); + break; + case ACPI_BUTTON_TYPE_SLEEP: + case ACPI_BUTTON_TYPE_SLEEPF: + remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, + acpi_button_dir); + break; + case ACPI_BUTTON_TYPE_LID: + remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, + acpi_button_dir); + break; + } + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static void +acpi_button_notify ( + acpi_handle handle, + u32 event, + void *data) +{ + struct acpi_button *button = (struct acpi_button *) data; + + ACPI_FUNCTION_TRACE("acpi_button_notify"); + + if (!button || !button->device) + return_VOID; + + switch (event) { + case ACPI_BUTTON_NOTIFY_STATUS: + acpi_bus_generate_event(button->device, event, ++button->pushed); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + return_VOID; +} + + +static acpi_status +acpi_button_notify_fixed ( + void *data) +{ + struct acpi_button *button = (struct acpi_button *) data; + + ACPI_FUNCTION_TRACE("acpi_button_notify_fixed"); + + if (!button) + return_ACPI_STATUS(AE_BAD_PARAMETER); + + acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button); + + return_ACPI_STATUS(AE_OK); +} + + +static int +acpi_button_add ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_button *button = NULL; + + static struct acpi_device *power_button; + static struct acpi_device *sleep_button; + static struct acpi_device *lid_button; + + ACPI_FUNCTION_TRACE("acpi_button_add"); + + if (!device) + return_VALUE(-EINVAL); + + button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL); + if (!button) + return_VALUE(-ENOMEM); + memset(button, 0, sizeof(struct acpi_button)); + + button->device = device; + button->handle = device->handle; + acpi_driver_data(device) = button; + + /* + * Determine the button type (via hid), as fixed-feature buttons + * need to be handled a bit differently than generic-space. + */ + if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) { + button->type = ACPI_BUTTON_TYPE_POWER; + strcpy(acpi_device_name(device), + ACPI_BUTTON_DEVICE_NAME_POWER); + sprintf(acpi_device_class(device), "%s/%s", + ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); + } + else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) { + button->type = ACPI_BUTTON_TYPE_POWERF; + strcpy(acpi_device_name(device), + ACPI_BUTTON_DEVICE_NAME_POWERF); + sprintf(acpi_device_class(device), "%s/%s", + ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER); + } + else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) { + button->type = ACPI_BUTTON_TYPE_SLEEP; + strcpy(acpi_device_name(device), + ACPI_BUTTON_DEVICE_NAME_SLEEP); + sprintf(acpi_device_class(device), "%s/%s", + ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); + } + else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) { + button->type = ACPI_BUTTON_TYPE_SLEEPF; + strcpy(acpi_device_name(device), + ACPI_BUTTON_DEVICE_NAME_SLEEPF); + sprintf(acpi_device_class(device), "%s/%s", + ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP); + } + else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) { + button->type = ACPI_BUTTON_TYPE_LID; + strcpy(acpi_device_name(device), + ACPI_BUTTON_DEVICE_NAME_LID); + sprintf(acpi_device_class(device), "%s/%s", + ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID); + } + else { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n", + acpi_device_hid(device))); + result = -ENODEV; + goto end; + } + + /* + * Ensure only one button of each type is used. + */ + switch (button->type) { + case ACPI_BUTTON_TYPE_POWER: + case ACPI_BUTTON_TYPE_POWERF: + if (!power_button) + power_button = device; + else { + kfree(button); + return_VALUE(-ENODEV); + } + break; + case ACPI_BUTTON_TYPE_SLEEP: + case ACPI_BUTTON_TYPE_SLEEPF: + if (!sleep_button) + sleep_button = device; + else { + kfree(button); + return_VALUE(-ENODEV); + } + break; + case ACPI_BUTTON_TYPE_LID: + if (!lid_button) + lid_button = device; + else { + kfree(button); + return_VALUE(-ENODEV); + } + break; + } + + result = acpi_button_add_fs(device); + if (result) + goto end; + + switch (button->type) { + case ACPI_BUTTON_TYPE_POWERF: + status = acpi_install_fixed_event_handler ( + ACPI_EVENT_POWER_BUTTON, + acpi_button_notify_fixed, + button); + break; + case ACPI_BUTTON_TYPE_SLEEPF: + status = acpi_install_fixed_event_handler ( + ACPI_EVENT_SLEEP_BUTTON, + acpi_button_notify_fixed, + button); + break; + default: + status = acpi_install_notify_handler ( + button->handle, + ACPI_DEVICE_NOTIFY, + acpi_button_notify, + button); + break; + } + + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing notify handler\n")); + result = -ENODEV; + goto end; + } + + if (device->wakeup.flags.valid) { + /* Button's GPE is run-wake GPE */ + acpi_set_gpe_type(device->wakeup.gpe_device, + device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); + acpi_enable_gpe(device->wakeup.gpe_device, + device->wakeup.gpe_number, ACPI_NOT_ISR); + device->wakeup.state.enabled = 1; + } + + printk(KERN_INFO PREFIX "%s [%s]\n", + acpi_device_name(device), acpi_device_bid(device)); + +end: + if (result) { + acpi_button_remove_fs(device); + kfree(button); + } + + return_VALUE(result); +} + + +static int +acpi_button_remove (struct acpi_device *device, int type) +{ + acpi_status status = 0; + struct acpi_button *button = NULL; + + ACPI_FUNCTION_TRACE("acpi_button_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + button = acpi_driver_data(device); + + /* Unregister for device notifications. */ + switch (button->type) { + case ACPI_BUTTON_TYPE_POWERF: + status = acpi_remove_fixed_event_handler( + ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed); + break; + case ACPI_BUTTON_TYPE_SLEEPF: + status = acpi_remove_fixed_event_handler( + ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed); + break; + default: + status = acpi_remove_notify_handler(button->handle, + ACPI_DEVICE_NOTIFY, acpi_button_notify); + break; + } + + if (ACPI_FAILURE(status)) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error removing notify handler\n")); + + acpi_button_remove_fs(device); + + kfree(button); + + return_VALUE(0); +} + + +static int __init +acpi_button_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_button_init"); + + acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir); + if (!acpi_button_dir) + return_VALUE(-ENODEV); + acpi_button_dir->owner = THIS_MODULE; + + result = acpi_bus_register_driver(&acpi_button_driver); + if (result < 0) { + remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + + +static void __exit +acpi_button_exit (void) +{ + ACPI_FUNCTION_TRACE("acpi_button_exit"); + + acpi_bus_unregister_driver(&acpi_button_driver); + + remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir); + + return_VOID; +} + + +module_init(acpi_button_init); +module_exit(acpi_button_exit); diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c new file mode 100644 index 00000000000..5a0adbf8bc0 --- /dev/null +++ b/drivers/acpi/container.c @@ -0,0 +1,303 @@ +/* + * acpi_container.c - ACPI Generic Container Driver + * ($Revision: ) + * + * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com) + * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com) + * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com) + * Copyright (C) 2004 Intel Corp. + * Copyright (C) 2004 FUJITSU LIMITED + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> +#include <acpi/container.h> + +#define ACPI_CONTAINER_DRIVER_NAME "ACPI container driver" +#define ACPI_CONTAINER_DEVICE_NAME "ACPI container device" +#define ACPI_CONTAINER_CLASS "container" + +#define INSTALL_NOTIFY_HANDLER 1 +#define UNINSTALL_NOTIFY_HANDLER 2 + +#define ACPI_CONTAINER_COMPONENT 0x01000000 +#define _COMPONENT ACPI_CONTAINER_COMPONENT +ACPI_MODULE_NAME ("acpi_container") + +MODULE_AUTHOR("Anil S Keshavamurthy"); +MODULE_DESCRIPTION(ACPI_CONTAINER_DRIVER_NAME); +MODULE_LICENSE("GPL"); + +#define ACPI_STA_PRESENT (0x00000001) + +static int acpi_container_add(struct acpi_device *device); +static int acpi_container_remove(struct acpi_device *device, int type); + +static struct acpi_driver acpi_container_driver = { + .name = ACPI_CONTAINER_DRIVER_NAME, + .class = ACPI_CONTAINER_CLASS, + .ids = "ACPI0004,PNP0A05,PNP0A06", + .ops = { + .add = acpi_container_add, + .remove = acpi_container_remove, + }, +}; + + +/*******************************************************************/ + +static int +is_device_present(acpi_handle handle) +{ + acpi_handle temp; + acpi_status status; + unsigned long sta; + + ACPI_FUNCTION_TRACE("is_device_present"); + + status = acpi_get_handle(handle, "_STA", &temp); + if (ACPI_FAILURE(status)) + return_VALUE(1); /* _STA not found, assmue device present */ + + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) + return_VALUE(0); /* Firmware error */ + + return_VALUE((sta & ACPI_STA_PRESENT) == ACPI_STA_PRESENT); +} + +/*******************************************************************/ +static int +acpi_container_add(struct acpi_device *device) +{ + struct acpi_container *container; + + ACPI_FUNCTION_TRACE("acpi_container_add"); + + if (!device) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "device is NULL\n")); + return_VALUE(-EINVAL); + } + + container = kmalloc(sizeof(struct acpi_container), GFP_KERNEL); + if(!container) + return_VALUE(-ENOMEM); + + memset(container, 0, sizeof(struct acpi_container)); + container->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_CONTAINER_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_CONTAINER_CLASS); + acpi_driver_data(device) = container; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device <%s> bid <%s>\n", \ + acpi_device_name(device), acpi_device_bid(device))); + + + return_VALUE(0); +} + +static int +acpi_container_remove(struct acpi_device *device, int type) +{ + acpi_status status = AE_OK; + struct acpi_container *pc = NULL; + pc = (struct acpi_container*) acpi_driver_data(device); + + if (pc) + kfree(pc); + + return status; +} + + +static int +container_device_add(struct acpi_device **device, acpi_handle handle) +{ + acpi_handle phandle; + struct acpi_device *pdev; + int result; + + ACPI_FUNCTION_TRACE("container_device_add"); + + if (acpi_get_parent(handle, &phandle)) { + return_VALUE(-ENODEV); + } + + if (acpi_bus_get_device(phandle, &pdev)) { + return_VALUE(-ENODEV); + } + + if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_DEVICE)) { + return_VALUE(-ENODEV); + } + + result = acpi_bus_scan(*device); + + return_VALUE(result); +} + +static void +container_notify_cb(acpi_handle handle, u32 type, void *context) +{ + struct acpi_device *device = NULL; + int result; + int present; + acpi_status status; + + ACPI_FUNCTION_TRACE("container_notify_cb"); + + present = is_device_present(handle); + + switch (type) { + case ACPI_NOTIFY_BUS_CHECK: + /* Fall through */ + case ACPI_NOTIFY_DEVICE_CHECK: + printk("Container driver received %s event\n", + (type == ACPI_NOTIFY_BUS_CHECK)? + "ACPI_NOTIFY_BUS_CHECK":"ACPI_NOTIFY_DEVICE_CHECK"); + status = acpi_bus_get_device(handle, &device); + if (present) { + if (ACPI_FAILURE(status) || !device) { + result = container_device_add(&device, handle); + if (!result) + kobject_hotplug(&device->kobj, + KOBJ_ONLINE); + else + printk("Failed to add container\n"); + } + } else { + if (ACPI_SUCCESS(status)) { + /* device exist and this is a remove request */ + kobject_hotplug(&device->kobj, KOBJ_OFFLINE); + } + } + break; + case ACPI_NOTIFY_EJECT_REQUEST: + if (!acpi_bus_get_device(handle, &device) && device) { + kobject_hotplug(&device->kobj, KOBJ_OFFLINE); + } + break; + default: + break; + } + return_VOID; +} + +static acpi_status +container_walk_namespace_cb(acpi_handle handle, + u32 lvl, + void *context, + void **rv) +{ + char *hid = NULL; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_device_info *info; + acpi_status status; + int *action = context; + + ACPI_FUNCTION_TRACE("container_walk_namespace_cb"); + + status = acpi_get_object_info(handle, &buffer); + if (ACPI_FAILURE(status) || !buffer.pointer) { + return_ACPI_STATUS(AE_OK); + } + + info = buffer.pointer; + if (info->valid & ACPI_VALID_HID) + hid = info->hardware_id.value; + + if (hid == NULL) { + goto end; + } + + if (strcmp(hid, "ACPI0004") && strcmp(hid, "PNP0A05") && + strcmp(hid, "PNP0A06")) { + goto end; + } + + switch(*action) { + case INSTALL_NOTIFY_HANDLER: + acpi_install_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + container_notify_cb, + NULL); + break; + case UNINSTALL_NOTIFY_HANDLER: + acpi_remove_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + container_notify_cb); + break; + default: + break; + } + +end: + acpi_os_free(buffer.pointer); + + return_ACPI_STATUS(AE_OK); +} + + +static int __init +acpi_container_init(void) +{ + int result = 0; + int action = INSTALL_NOTIFY_HANDLER; + + result = acpi_bus_register_driver(&acpi_container_driver); + if (result < 0) { + return(result); + } + + /* register notify handler to every container device */ + acpi_walk_namespace(ACPI_TYPE_DEVICE, + ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + container_walk_namespace_cb, + &action, NULL); + + return(0); +} + +static void __exit +acpi_container_exit(void) +{ + int action = UNINSTALL_NOTIFY_HANDLER; + + ACPI_FUNCTION_TRACE("acpi_container_exit"); + + acpi_walk_namespace(ACPI_TYPE_DEVICE, + ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + container_walk_namespace_cb, + &action, NULL); + + acpi_bus_unregister_driver(&acpi_container_driver); + + return_VOID; +} + +module_init(acpi_container_init); +module_exit(acpi_container_exit); diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c new file mode 100644 index 00000000000..2c0dac559f1 --- /dev/null +++ b/drivers/acpi/debug.c @@ -0,0 +1,233 @@ +/* + * debug.c - ACPI debug interface to userspace. + */ + +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/moduleparam.h> +#include <asm/uaccess.h> +#include <acpi/acpi_drivers.h> +#include <acpi/acglobal.h> + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME ("debug") + +#define ACPI_SYSTEM_FILE_DEBUG_LAYER "debug_layer" +#define ACPI_SYSTEM_FILE_DEBUG_LEVEL "debug_level" + +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif + +#define MODULE_PARAM_PREFIX +module_param(acpi_dbg_layer, uint, 0400); +module_param(acpi_dbg_level, uint, 0400); + +struct acpi_dlayer { + const char *name; + unsigned long value; +}; +struct acpi_dlevel { + const char *name; + unsigned long value; +}; +#define ACPI_DEBUG_INIT(v) { .name = #v, .value = v } + +static const struct acpi_dlayer acpi_debug_layers[] = +{ + ACPI_DEBUG_INIT(ACPI_UTILITIES), + ACPI_DEBUG_INIT(ACPI_HARDWARE), + ACPI_DEBUG_INIT(ACPI_EVENTS), + ACPI_DEBUG_INIT(ACPI_TABLES), + ACPI_DEBUG_INIT(ACPI_NAMESPACE), + ACPI_DEBUG_INIT(ACPI_PARSER), + ACPI_DEBUG_INIT(ACPI_DISPATCHER), + ACPI_DEBUG_INIT(ACPI_EXECUTER), + ACPI_DEBUG_INIT(ACPI_RESOURCES), + ACPI_DEBUG_INIT(ACPI_CA_DEBUGGER), + ACPI_DEBUG_INIT(ACPI_OS_SERVICES), + ACPI_DEBUG_INIT(ACPI_CA_DISASSEMBLER), + ACPI_DEBUG_INIT(ACPI_COMPILER), + ACPI_DEBUG_INIT(ACPI_TOOLS), +}; + +static const struct acpi_dlevel acpi_debug_levels[] = +{ + ACPI_DEBUG_INIT(ACPI_LV_ERROR), + ACPI_DEBUG_INIT(ACPI_LV_WARN), + ACPI_DEBUG_INIT(ACPI_LV_INIT), + ACPI_DEBUG_INIT(ACPI_LV_DEBUG_OBJECT), + ACPI_DEBUG_INIT(ACPI_LV_INFO), + + ACPI_DEBUG_INIT(ACPI_LV_INIT_NAMES), + ACPI_DEBUG_INIT(ACPI_LV_PARSE), + ACPI_DEBUG_INIT(ACPI_LV_LOAD), + ACPI_DEBUG_INIT(ACPI_LV_DISPATCH), + ACPI_DEBUG_INIT(ACPI_LV_EXEC), + ACPI_DEBUG_INIT(ACPI_LV_NAMES), + ACPI_DEBUG_INIT(ACPI_LV_OPREGION), + ACPI_DEBUG_INIT(ACPI_LV_BFIELD), + ACPI_DEBUG_INIT(ACPI_LV_TABLES), + ACPI_DEBUG_INIT(ACPI_LV_VALUES), + ACPI_DEBUG_INIT(ACPI_LV_OBJECTS), + ACPI_DEBUG_INIT(ACPI_LV_RESOURCES), + ACPI_DEBUG_INIT(ACPI_LV_USER_REQUESTS), + ACPI_DEBUG_INIT(ACPI_LV_PACKAGE), + + ACPI_DEBUG_INIT(ACPI_LV_ALLOCATIONS), + ACPI_DEBUG_INIT(ACPI_LV_FUNCTIONS), + ACPI_DEBUG_INIT(ACPI_LV_OPTIMIZATIONS), + + ACPI_DEBUG_INIT(ACPI_LV_MUTEX), + ACPI_DEBUG_INIT(ACPI_LV_THREADS), + ACPI_DEBUG_INIT(ACPI_LV_IO), + ACPI_DEBUG_INIT(ACPI_LV_INTERRUPTS), + + ACPI_DEBUG_INIT(ACPI_LV_AML_DISASSEMBLE), + ACPI_DEBUG_INIT(ACPI_LV_VERBOSE_INFO), + ACPI_DEBUG_INIT(ACPI_LV_FULL_TABLES), + ACPI_DEBUG_INIT(ACPI_LV_EVENTS), +}; + +static int +acpi_system_read_debug ( + char *page, + char **start, + off_t off, + int count, + int *eof, + void *data) +{ + char *p = page; + int size = 0; + unsigned int i; + + if (off != 0) + goto end; + + p += sprintf(p, "%-25s\tHex SET\n", "Description"); + + switch ((unsigned long) data) { + case 0: + for (i = 0; i < ARRAY_SIZE(acpi_debug_layers); i++) { + p += sprintf(p, "%-25s\t0x%08lX [%c]\n", + acpi_debug_layers[i].name, + acpi_debug_layers[i].value, + (acpi_dbg_layer & acpi_debug_layers[i].value) ? + '*' : ' '); + } + p += sprintf(p, "%-25s\t0x%08X [%c]\n", "ACPI_ALL_DRIVERS", + ACPI_ALL_DRIVERS, + (acpi_dbg_layer & ACPI_ALL_DRIVERS) == ACPI_ALL_DRIVERS? + '*' : (acpi_dbg_layer & ACPI_ALL_DRIVERS) == 0 ? + ' ' : '-'); + p += sprintf(p, + "--\ndebug_layer = 0x%08X (* = enabled, - = partial)\n", + acpi_dbg_layer); + break; + case 1: + for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) { + p += sprintf(p, "%-25s\t0x%08lX [%c]\n", + acpi_debug_levels[i].name, + acpi_debug_levels[i].value, + (acpi_dbg_level & acpi_debug_levels[i].value) ? + '*' : ' '); + } + p += sprintf(p, "--\ndebug_level = 0x%08X (* = enabled)\n", + acpi_dbg_level); + break; + default: + p += sprintf(p, "Invalid debug option\n"); + break; + } + +end: + size = (p - page); + if (size <= off+count) *eof = 1; + *start = page + off; + size -= off; + if (size>count) size = count; + if (size<0) size = 0; + + return size; +} + + +static int +acpi_system_write_debug ( + struct file *file, + const char __user *buffer, + unsigned long count, + void *data) +{ + char debug_string[12] = {'\0'}; + + ACPI_FUNCTION_TRACE("acpi_system_write_debug"); + + if (count > sizeof(debug_string) - 1) + return_VALUE(-EINVAL); + + if (copy_from_user(debug_string, buffer, count)) + return_VALUE(-EFAULT); + + debug_string[count] = '\0'; + + switch ((unsigned long) data) { + case 0: + acpi_dbg_layer = simple_strtoul(debug_string, NULL, 0); + break; + case 1: + acpi_dbg_level = simple_strtoul(debug_string, NULL, 0); + break; + default: + return_VALUE(-EINVAL); + } + + return_VALUE(count); +} + +static int __init acpi_debug_init(void) +{ + struct proc_dir_entry *entry; + int error = 0; + char * name; + + ACPI_FUNCTION_TRACE("acpi_debug_init"); + + if (acpi_disabled) + return_VALUE(0); + + /* 'debug_layer' [R/W] */ + name = ACPI_SYSTEM_FILE_DEBUG_LAYER; + entry = create_proc_read_entry(name, S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir, + acpi_system_read_debug,(void *)0); + if (entry) + entry->write_proc = acpi_system_write_debug; + else + goto Error; + + /* 'debug_level' [R/W] */ + name = ACPI_SYSTEM_FILE_DEBUG_LEVEL; + entry = create_proc_read_entry(name, S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir, + acpi_system_read_debug, (void *)1); + if (entry) + entry->write_proc = acpi_system_write_debug; + else + goto Error; + + Done: + return_VALUE(error); + + Error: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' proc fs entry\n", name)); + + remove_proc_entry(ACPI_SYSTEM_FILE_DEBUG_LEVEL, acpi_root_dir); + remove_proc_entry(ACPI_SYSTEM_FILE_DEBUG_LAYER, acpi_root_dir); + error = -EFAULT; + goto Done; +} + +subsys_initcall(acpi_debug_init); diff --git a/drivers/acpi/dispatcher/Makefile b/drivers/acpi/dispatcher/Makefile new file mode 100644 index 00000000000..eb7e602a83c --- /dev/null +++ b/drivers/acpi/dispatcher/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for all Linux ACPI interpreter subdirectories +# + +obj-y := dsfield.o dsmthdat.o dsopcode.o dswexec.o dswscope.o \ + dsmethod.o dsobject.o dsutils.o dswload.o dswstate.o \ + dsinit.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/dispatcher/dsfield.c b/drivers/acpi/dispatcher/dsfield.c new file mode 100644 index 00000000000..2779211be75 --- /dev/null +++ b/drivers/acpi/dispatcher/dsfield.c @@ -0,0 +1,601 @@ +/****************************************************************************** + * + * Module Name: dsfield - Dispatcher field routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/amlcode.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> +#include <acpi/acparser.h> + + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dsfield") + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_buffer_field + * + * PARAMETERS: Opcode - The opcode to be executed + * Operands - List of operands for the opcode + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Execute the create_field operators: + * create_bit_field_op, + * create_byte_field_op, + * create_word_field_op, + * create_dword_field_op, + * create_qword_field_op, + * create_field_op (all of which define fields in buffers) + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_buffer_field ( + union acpi_parse_object *op, + struct acpi_walk_state *walk_state) +{ + union acpi_parse_object *arg; + struct acpi_namespace_node *node; + acpi_status status; + union acpi_operand_object *obj_desc; + union acpi_operand_object *second_desc = NULL; + u32 flags; + + + ACPI_FUNCTION_TRACE ("ds_create_buffer_field"); + + + /* Get the name_string argument */ + + if (op->common.aml_opcode == AML_CREATE_FIELD_OP) { + arg = acpi_ps_get_arg (op, 3); + } + else { + /* Create Bit/Byte/Word/Dword field */ + + arg = acpi_ps_get_arg (op, 2); + } + + if (!arg) { + return_ACPI_STATUS (AE_AML_NO_OPERAND); + } + + if (walk_state->deferred_node) { + node = walk_state->deferred_node; + status = AE_OK; + } + else { + /* + * During the load phase, we want to enter the name of the field into + * the namespace. During the execute phase (when we evaluate the size + * operand), we want to lookup the name + */ + if (walk_state->parse_flags & ACPI_PARSE_EXECUTE) { + flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE; + } + else { + flags = ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND; + } + + /* + * Enter the name_string into the namespace + */ + status = acpi_ns_lookup (walk_state->scope_info, arg->common.value.string, + ACPI_TYPE_ANY, ACPI_IMODE_LOAD_PASS1, + flags, walk_state, &(node)); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (arg->common.value.string, status); + return_ACPI_STATUS (status); + } + } + + /* We could put the returned object (Node) on the object stack for later, but + * for now, we will put it in the "op" object that the parser uses, so we + * can get it again at the end of this scope + */ + op->common.node = node; + + /* + * If there is no object attached to the node, this node was just created and + * we need to create the field object. Otherwise, this was a lookup of an + * existing node and we don't want to create the field object again. + */ + obj_desc = acpi_ns_get_attached_object (node); + if (obj_desc) { + return_ACPI_STATUS (AE_OK); + } + + /* + * The Field definition is not fully parsed at this time. + * (We must save the address of the AML for the buffer and index operands) + */ + + /* Create the buffer field object */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_BUFFER_FIELD); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Remember location in AML stream of the field unit + * opcode and operands -- since the buffer and index + * operands must be evaluated. + */ + second_desc = obj_desc->common.next_object; + second_desc->extra.aml_start = op->named.data; + second_desc->extra.aml_length = op->named.length; + obj_desc->buffer_field.node = node; + + /* Attach constructed field descriptors to parent node */ + + status = acpi_ns_attach_object (node, obj_desc, ACPI_TYPE_BUFFER_FIELD); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + +cleanup: + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_get_field_names + * + * PARAMETERS: Info - create_field info structure + * ` walk_state - Current method state + * Arg - First parser arg for the field name list + * + * RETURN: Status + * + * DESCRIPTION: Process all named fields in a field declaration. Names are + * entered into the namespace. + * + ******************************************************************************/ + +acpi_status +acpi_ds_get_field_names ( + struct acpi_create_field_info *info, + struct acpi_walk_state *walk_state, + union acpi_parse_object *arg) +{ + acpi_status status; + acpi_integer position; + + + ACPI_FUNCTION_TRACE_PTR ("ds_get_field_names", info); + + + /* First field starts at bit zero */ + + info->field_bit_position = 0; + + /* Process all elements in the field list (of parse nodes) */ + + while (arg) { + /* + * Three types of field elements are handled: + * 1) Offset - specifies a bit offset + * 2) access_as - changes the access mode + * 3) Name - Enters a new named field into the namespace + */ + switch (arg->common.aml_opcode) { + case AML_INT_RESERVEDFIELD_OP: + + position = (acpi_integer) info->field_bit_position + + (acpi_integer) arg->common.value.size; + + if (position > ACPI_UINT32_MAX) { + ACPI_REPORT_ERROR (("Bit offset within field too large (> 0xFFFFFFFF)\n")); + return_ACPI_STATUS (AE_SUPPORT); + } + + info->field_bit_position = (u32) position; + break; + + + case AML_INT_ACCESSFIELD_OP: + + /* + * Get a new access_type and access_attribute -- to be used for all + * field units that follow, until field end or another access_as keyword. + * + * In field_flags, preserve the flag bits other than the ACCESS_TYPE bits + */ + info->field_flags = (u8) ((info->field_flags & ~(AML_FIELD_ACCESS_TYPE_MASK)) | + ((u8) ((u32) arg->common.value.integer >> 8))); + + info->attribute = (u8) (arg->common.value.integer); + break; + + + case AML_INT_NAMEDFIELD_OP: + + /* Lookup the name */ + + status = acpi_ns_lookup (walk_state->scope_info, + (char *) &arg->named.name, + info->field_type, ACPI_IMODE_EXECUTE, ACPI_NS_DONT_OPEN_SCOPE, + walk_state, &info->field_node); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR ((char *) &arg->named.name, status); + if (status != AE_ALREADY_EXISTS) { + return_ACPI_STATUS (status); + } + + /* Already exists, ignore error */ + } + else { + arg->common.node = info->field_node; + info->field_bit_length = arg->common.value.size; + + /* Create and initialize an object for the new Field Node */ + + status = acpi_ex_prep_field_value (info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Keep track of bit position for the next field */ + + position = (acpi_integer) info->field_bit_position + + (acpi_integer) arg->common.value.size; + + if (position > ACPI_UINT32_MAX) { + ACPI_REPORT_ERROR (("Field [%4.4s] bit offset too large (> 0xFFFFFFFF)\n", + (char *) &info->field_node->name)); + return_ACPI_STATUS (AE_SUPPORT); + } + + info->field_bit_position += info->field_bit_length; + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid opcode in field list: %X\n", + arg->common.aml_opcode)); + return_ACPI_STATUS (AE_AML_BAD_OPCODE); + } + + arg = arg->common.next; + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_field + * + * PARAMETERS: Op - Op containing the Field definition and args + * region_node - Object for the containing Operation Region + * ` walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Create a new field in the specified operation region + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_field ( + union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_parse_object *arg; + struct acpi_create_field_info info; + + + ACPI_FUNCTION_TRACE_PTR ("ds_create_field", op); + + + /* First arg is the name of the parent op_region (must already exist) */ + + arg = op->common.value.arg; + if (!region_node) { + status = acpi_ns_lookup (walk_state->scope_info, arg->common.value.name, + ACPI_TYPE_REGION, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, ®ion_node); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (arg->common.value.name, status); + return_ACPI_STATUS (status); + } + } + + /* Second arg is the field flags */ + + arg = arg->common.next; + info.field_flags = (u8) arg->common.value.integer; + info.attribute = 0; + + /* Each remaining arg is a Named Field */ + + info.field_type = ACPI_TYPE_LOCAL_REGION_FIELD; + info.region_node = region_node; + + status = acpi_ds_get_field_names (&info, walk_state, arg->common.next); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_field_objects + * + * PARAMETERS: Op - Op containing the Field definition and args + * ` walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: For each "Field Unit" name in the argument list that is + * part of the field declaration, enter the name into the + * namespace. + * + ******************************************************************************/ + +acpi_status +acpi_ds_init_field_objects ( + union acpi_parse_object *op, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_parse_object *arg = NULL; + struct acpi_namespace_node *node; + u8 type = 0; + + + ACPI_FUNCTION_TRACE_PTR ("ds_init_field_objects", op); + + + switch (walk_state->opcode) { + case AML_FIELD_OP: + arg = acpi_ps_get_arg (op, 2); + type = ACPI_TYPE_LOCAL_REGION_FIELD; + break; + + case AML_BANK_FIELD_OP: + arg = acpi_ps_get_arg (op, 4); + type = ACPI_TYPE_LOCAL_BANK_FIELD; + break; + + case AML_INDEX_FIELD_OP: + arg = acpi_ps_get_arg (op, 3); + type = ACPI_TYPE_LOCAL_INDEX_FIELD; + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Walk the list of entries in the field_list + */ + while (arg) { + /* Ignore OFFSET and ACCESSAS terms here */ + + if (arg->common.aml_opcode == AML_INT_NAMEDFIELD_OP) { + status = acpi_ns_lookup (walk_state->scope_info, + (char *) &arg->named.name, + type, ACPI_IMODE_LOAD_PASS1, + ACPI_NS_NO_UPSEARCH | ACPI_NS_DONT_OPEN_SCOPE | ACPI_NS_ERROR_IF_FOUND, + walk_state, &node); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR ((char *) &arg->named.name, status); + if (status != AE_ALREADY_EXISTS) { + return_ACPI_STATUS (status); + } + + /* Name already exists, just ignore this error */ + + status = AE_OK; + } + + arg->common.node = node; + } + + /* Move to next field in the list */ + + arg = arg->common.next; + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_bank_field + * + * PARAMETERS: Op - Op containing the Field definition and args + * region_node - Object for the containing Operation Region + * ` walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Create a new bank field in the specified operation region + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_bank_field ( + union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_parse_object *arg; + struct acpi_create_field_info info; + + + ACPI_FUNCTION_TRACE_PTR ("ds_create_bank_field", op); + + + /* First arg is the name of the parent op_region (must already exist) */ + + arg = op->common.value.arg; + if (!region_node) { + status = acpi_ns_lookup (walk_state->scope_info, arg->common.value.name, + ACPI_TYPE_REGION, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, ®ion_node); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (arg->common.value.name, status); + return_ACPI_STATUS (status); + } + } + + /* Second arg is the Bank Register (Field) (must already exist) */ + + arg = arg->common.next; + status = acpi_ns_lookup (walk_state->scope_info, arg->common.value.string, + ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, &info.register_node); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (arg->common.value.string, status); + return_ACPI_STATUS (status); + } + + /* Third arg is the bank_value */ + + arg = arg->common.next; + info.bank_value = (u32) arg->common.value.integer; + + /* Fourth arg is the field flags */ + + arg = arg->common.next; + info.field_flags = (u8) arg->common.value.integer; + + /* Each remaining arg is a Named Field */ + + info.field_type = ACPI_TYPE_LOCAL_BANK_FIELD; + info.region_node = region_node; + + status = acpi_ds_get_field_names (&info, walk_state, arg->common.next); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_index_field + * + * PARAMETERS: Op - Op containing the Field definition and args + * region_node - Object for the containing Operation Region + * ` walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Create a new index field in the specified operation region + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_index_field ( + union acpi_parse_object *op, + struct acpi_namespace_node *region_node, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_parse_object *arg; + struct acpi_create_field_info info; + + + ACPI_FUNCTION_TRACE_PTR ("ds_create_index_field", op); + + + /* First arg is the name of the Index register (must already exist) */ + + arg = op->common.value.arg; + status = acpi_ns_lookup (walk_state->scope_info, arg->common.value.string, + ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, &info.register_node); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (arg->common.value.string, status); + return_ACPI_STATUS (status); + } + + /* Second arg is the data register (must already exist) */ + + arg = arg->common.next; + status = acpi_ns_lookup (walk_state->scope_info, arg->common.value.string, + ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT, walk_state, &info.data_register_node); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (arg->common.value.string, status); + return_ACPI_STATUS (status); + } + + /* Next arg is the field flags */ + + arg = arg->common.next; + info.field_flags = (u8) arg->common.value.integer; + + /* Each remaining arg is a Named Field */ + + info.field_type = ACPI_TYPE_LOCAL_INDEX_FIELD; + info.region_node = region_node; + + status = acpi_ds_get_field_names (&info, walk_state, arg->common.next); + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/dispatcher/dsinit.c b/drivers/acpi/dispatcher/dsinit.c new file mode 100644 index 00000000000..b4d264dbbf6 --- /dev/null +++ b/drivers/acpi/dispatcher/dsinit.c @@ -0,0 +1,235 @@ +/****************************************************************************** + * + * Module Name: dsinit - Object initialization namespace walk + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acdispat.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dsinit") + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_one_object + * + * PARAMETERS: obj_handle - Node + * Level - Current nesting level + * Context - Points to a init info struct + * return_value - Not used + * + * RETURN: Status + * + * DESCRIPTION: Callback from acpi_walk_namespace. Invoked for every object + * within the namespace. + * + * Currently, the only objects that require initialization are: + * 1) Methods + * 2) Operation Regions + * + ******************************************************************************/ + +acpi_status +acpi_ds_init_one_object ( + acpi_handle obj_handle, + u32 level, + void *context, + void **return_value) +{ + acpi_object_type type; + acpi_status status; + struct acpi_init_walk_info *info = (struct acpi_init_walk_info *) context; + + + ACPI_FUNCTION_NAME ("ds_init_one_object"); + + + /* + * We are only interested in objects owned by the table that + * was just loaded + */ + if (((struct acpi_namespace_node *) obj_handle)->owner_id != + info->table_desc->table_id) { + return (AE_OK); + } + + info->object_count++; + + /* And even then, we are only interested in a few object types */ + + type = acpi_ns_get_type (obj_handle); + + switch (type) { + case ACPI_TYPE_REGION: + + status = acpi_ds_initialize_region (obj_handle); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Region %p [%4.4s] - Init failure, %s\n", + obj_handle, acpi_ut_get_node_name (obj_handle), + acpi_format_exception (status))); + } + + info->op_region_count++; + break; + + + case ACPI_TYPE_METHOD: + + info->method_count++; + + /* Print a dot for each method unless we are going to print the entire pathname */ + + if (!(acpi_dbg_level & ACPI_LV_INIT_NAMES)) { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, ".")); + } + + /* + * Set the execution data width (32 or 64) based upon the + * revision number of the parent ACPI table. + * TBD: This is really for possible future support of integer width + * on a per-table basis. Currently, we just use a global for the width. + */ + if (info->table_desc->pointer->revision == 1) { + ((struct acpi_namespace_node *) obj_handle)->flags |= ANOBJ_DATA_WIDTH_32; + } + + /* + * Always parse methods to detect errors, we will delete + * the parse tree below + */ + status = acpi_ds_parse_method (obj_handle); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Method %p [%4.4s] - parse failure, %s\n", + obj_handle, acpi_ut_get_node_name (obj_handle), + acpi_format_exception (status))); + + /* This parse failed, but we will continue parsing more methods */ + + break; + } + + /* + * Delete the parse tree. We simply re-parse the method + * for every execution since there isn't much overhead + */ + acpi_ns_delete_namespace_subtree (obj_handle); + acpi_ns_delete_namespace_by_owner (((struct acpi_namespace_node *) obj_handle)->object->method.owning_id); + break; + + + case ACPI_TYPE_DEVICE: + + info->device_count++; + break; + + + default: + break; + } + + /* + * We ignore errors from above, and always return OK, since + * we don't want to abort the walk on a single error. + */ + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_initialize_objects + * + * PARAMETERS: table_desc - Descriptor for parent ACPI table + * start_node - Root of subtree to be initialized. + * + * RETURN: Status + * + * DESCRIPTION: Walk the namespace starting at "start_node" and perform any + * necessary initialization on the objects found therein + * + ******************************************************************************/ + +acpi_status +acpi_ds_initialize_objects ( + struct acpi_table_desc *table_desc, + struct acpi_namespace_node *start_node) +{ + acpi_status status; + struct acpi_init_walk_info info; + + + ACPI_FUNCTION_TRACE ("ds_initialize_objects"); + + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "**** Starting initialization of namespace objects ****\n")); + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, "Parsing all Control Methods:")); + + info.method_count = 0; + info.op_region_count = 0; + info.object_count = 0; + info.device_count = 0; + info.table_desc = table_desc; + + /* Walk entire namespace from the supplied root */ + + status = acpi_walk_namespace (ACPI_TYPE_ANY, start_node, ACPI_UINT32_MAX, + acpi_ds_init_one_object, &info, NULL); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "walk_namespace failed, %s\n", + acpi_format_exception (status))); + } + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, + "\nTable [%4.4s](id %4.4X) - %hd Objects with %hd Devices %hd Methods %hd Regions\n", + table_desc->pointer->signature, table_desc->table_id, info.object_count, + info.device_count, info.method_count, info.op_region_count)); + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "%hd Methods, %hd Regions\n", info.method_count, info.op_region_count)); + + return_ACPI_STATUS (AE_OK); +} + + diff --git a/drivers/acpi/dispatcher/dsmethod.c b/drivers/acpi/dispatcher/dsmethod.c new file mode 100644 index 00000000000..9f0456cb9bb --- /dev/null +++ b/drivers/acpi/dispatcher/dsmethod.c @@ -0,0 +1,597 @@ +/****************************************************************************** + * + * Module Name: dsmethod - Parser/Interpreter interface - control method parsing + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dsmethod") + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_parse_method + * + * PARAMETERS: obj_handle - Method node + * + * RETURN: Status + * + * DESCRIPTION: Call the parser and parse the AML that is associated with the + * method. + * + * MUTEX: Assumes parser is locked + * + ******************************************************************************/ + +acpi_status +acpi_ds_parse_method ( + acpi_handle obj_handle) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + union acpi_parse_object *op; + struct acpi_namespace_node *node; + acpi_owner_id owner_id; + struct acpi_walk_state *walk_state; + + + ACPI_FUNCTION_TRACE_PTR ("ds_parse_method", obj_handle); + + + /* Parameter Validation */ + + if (!obj_handle) { + return_ACPI_STATUS (AE_NULL_ENTRY); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "**** Parsing [%4.4s] **** named_obj=%p\n", + acpi_ut_get_node_name (obj_handle), obj_handle)); + + /* Extract the method object from the method Node */ + + node = (struct acpi_namespace_node *) obj_handle; + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + return_ACPI_STATUS (AE_NULL_OBJECT); + } + + /* Create a mutex for the method if there is a concurrency limit */ + + if ((obj_desc->method.concurrency != ACPI_INFINITE_CONCURRENCY) && + (!obj_desc->method.semaphore)) { + status = acpi_os_create_semaphore (obj_desc->method.concurrency, + obj_desc->method.concurrency, + &obj_desc->method.semaphore); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* + * Allocate a new parser op to be the root of the parsed + * method tree + */ + op = acpi_ps_alloc_op (AML_METHOD_OP); + if (!op) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Init new op with the method name and pointer back to the Node */ + + acpi_ps_set_name (op, node->name.integer); + op->common.node = node; + + /* + * Get a new owner_id for objects created by this method. Namespace + * objects (such as Operation Regions) can be created during the + * first pass parse. + */ + owner_id = acpi_ut_allocate_owner_id (ACPI_OWNER_TYPE_METHOD); + obj_desc->method.owning_id = owner_id; + + /* Create and initialize a new walk state */ + + walk_state = acpi_ds_create_walk_state (owner_id, NULL, NULL, NULL); + if (!walk_state) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + status = acpi_ds_init_aml_walk (walk_state, op, node, + obj_desc->method.aml_start, + obj_desc->method.aml_length, NULL, 1); + if (ACPI_FAILURE (status)) { + acpi_ds_delete_walk_state (walk_state); + return_ACPI_STATUS (status); + } + + /* + * Parse the method, first pass + * + * The first pass load is where newly declared named objects are + * added into the namespace. Actual evaluation of + * the named objects (what would be called a "second + * pass") happens during the actual execution of the + * method so that operands to the named objects can + * take on dynamic run-time values. + */ + status = acpi_ps_parse_aml (walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, + "**** [%4.4s] Parsed **** named_obj=%p Op=%p\n", + acpi_ut_get_node_name (obj_handle), obj_handle, op)); + + acpi_ps_delete_parse_tree (op); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_begin_method_execution + * + * PARAMETERS: method_node - Node of the method + * obj_desc - The method object + * calling_method_node - Caller of this method (if non-null) + * + * RETURN: Status + * + * DESCRIPTION: Prepare a method for execution. Parses the method if necessary, + * increments the thread count, and waits at the method semaphore + * for clearance to execute. + * + ******************************************************************************/ + +acpi_status +acpi_ds_begin_method_execution ( + struct acpi_namespace_node *method_node, + union acpi_operand_object *obj_desc, + struct acpi_namespace_node *calling_method_node) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_PTR ("ds_begin_method_execution", method_node); + + + if (!method_node) { + return_ACPI_STATUS (AE_NULL_ENTRY); + } + + /* + * If there is a concurrency limit on this method, we need to + * obtain a unit from the method semaphore. + */ + if (obj_desc->method.semaphore) { + /* + * Allow recursive method calls, up to the reentrancy/concurrency + * limit imposed by the SERIALIZED rule and the sync_level method + * parameter. + * + * The point of this code is to avoid permanently blocking a + * thread that is making recursive method calls. + */ + if (method_node == calling_method_node) { + if (obj_desc->method.thread_count >= obj_desc->method.concurrency) { + return_ACPI_STATUS (AE_AML_METHOD_LIMIT); + } + } + + /* + * Get a unit from the method semaphore. This releases the + * interpreter if we block + */ + status = acpi_ex_system_wait_semaphore (obj_desc->method.semaphore, + ACPI_WAIT_FOREVER); + } + + /* + * Increment the method parse tree thread count since it has been + * reentered one more time (even if it is the same thread) + */ + obj_desc->method.thread_count++; + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_call_control_method + * + * PARAMETERS: Thread - Info for this thread + * this_walk_state - Current walk state + * Op - Current Op to be walked + * + * RETURN: Status + * + * DESCRIPTION: Transfer execution to a called control method + * + ******************************************************************************/ + +acpi_status +acpi_ds_call_control_method ( + struct acpi_thread_state *thread, + struct acpi_walk_state *this_walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + struct acpi_namespace_node *method_node; + struct acpi_walk_state *next_walk_state; + union acpi_operand_object *obj_desc; + struct acpi_parameter_info info; + u32 i; + + + ACPI_FUNCTION_TRACE_PTR ("ds_call_control_method", this_walk_state); + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Execute method %p, currentstate=%p\n", + this_walk_state->prev_op, this_walk_state)); + + /* + * Get the namespace entry for the control method we are about to call + */ + method_node = this_walk_state->method_call_node; + if (!method_node) { + return_ACPI_STATUS (AE_NULL_ENTRY); + } + + obj_desc = acpi_ns_get_attached_object (method_node); + if (!obj_desc) { + return_ACPI_STATUS (AE_NULL_OBJECT); + } + + obj_desc->method.owning_id = acpi_ut_allocate_owner_id (ACPI_OWNER_TYPE_METHOD); + + /* Init for new method, wait on concurrency semaphore */ + + status = acpi_ds_begin_method_execution (method_node, obj_desc, + this_walk_state->method_node); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (!(obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY)) { + /* 1) Parse: Create a new walk state for the preempting walk */ + + next_walk_state = acpi_ds_create_walk_state (obj_desc->method.owning_id, + op, obj_desc, NULL); + if (!next_walk_state) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Create and init a Root Node */ + + op = acpi_ps_create_scope_op (); + if (!op) { + status = AE_NO_MEMORY; + goto cleanup; + } + + status = acpi_ds_init_aml_walk (next_walk_state, op, method_node, + obj_desc->method.aml_start, obj_desc->method.aml_length, + NULL, 1); + if (ACPI_FAILURE (status)) { + acpi_ds_delete_walk_state (next_walk_state); + goto cleanup; + } + + /* Begin AML parse */ + + status = acpi_ps_parse_aml (next_walk_state); + acpi_ps_delete_parse_tree (op); + } + + /* 2) Execute: Create a new state for the preempting walk */ + + next_walk_state = acpi_ds_create_walk_state (obj_desc->method.owning_id, + NULL, obj_desc, thread); + if (!next_walk_state) { + status = AE_NO_MEMORY; + goto cleanup; + } + /* + * The resolved arguments were put on the previous walk state's operand + * stack. Operands on the previous walk state stack always + * start at index 0. + * Null terminate the list of arguments + */ + this_walk_state->operands [this_walk_state->num_operands] = NULL; + + info.parameters = &this_walk_state->operands[0]; + info.parameter_type = ACPI_PARAM_ARGS; + + status = acpi_ds_init_aml_walk (next_walk_state, NULL, method_node, + obj_desc->method.aml_start, obj_desc->method.aml_length, + &info, 3); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* + * Delete the operands on the previous walkstate operand stack + * (they were copied to new objects) + */ + for (i = 0; i < obj_desc->method.param_count; i++) { + acpi_ut_remove_reference (this_walk_state->operands [i]); + this_walk_state->operands [i] = NULL; + } + + /* Clear the operand stack */ + + this_walk_state->num_operands = 0; + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "Starting nested execution, newstate=%p\n", next_walk_state)); + + if (obj_desc->method.method_flags & AML_METHOD_INTERNAL_ONLY) { + status = obj_desc->method.implementation (next_walk_state); + return_ACPI_STATUS (status); + } + + return_ACPI_STATUS (AE_OK); + + + /* On error, we must delete the new walk state */ + +cleanup: + if (next_walk_state && (next_walk_state->method_desc)) { + /* Decrement the thread count on the method parse tree */ + + next_walk_state->method_desc->method.thread_count--; + } + (void) acpi_ds_terminate_control_method (next_walk_state); + acpi_ds_delete_walk_state (next_walk_state); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_restart_control_method + * + * PARAMETERS: walk_state - State for preempted method (caller) + * return_desc - Return value from the called method + * + * RETURN: Status + * + * DESCRIPTION: Restart a method that was preempted by another (nested) method + * invocation. Handle the return value (if any) from the callee. + * + ******************************************************************************/ + +acpi_status +acpi_ds_restart_control_method ( + struct acpi_walk_state *walk_state, + union acpi_operand_object *return_desc) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ds_restart_control_method", walk_state); + + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "****Restart [%4.4s] Op %p return_value_from_callee %p\n", + (char *) &walk_state->method_node->name, walk_state->method_call_op, + return_desc)); + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + " return_from_this_method_used?=%X res_stack %p Walk %p\n", + walk_state->return_used, + walk_state->results, walk_state)); + + /* Did the called method return a value? */ + + if (return_desc) { + /* Are we actually going to use the return value? */ + + if (walk_state->return_used) { + /* Save the return value from the previous method */ + + status = acpi_ds_result_push (return_desc, walk_state); + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (return_desc); + return_ACPI_STATUS (status); + } + + /* + * Save as THIS method's return value in case it is returned + * immediately to yet another method + */ + walk_state->return_desc = return_desc; + } + + /* + * The following code is the + * optional support for a so-called "implicit return". Some AML code + * assumes that the last value of the method is "implicitly" returned + * to the caller. Just save the last result as the return value. + * NOTE: this is optional because the ASL language does not actually + * support this behavior. + */ + else if (!acpi_ds_do_implicit_return (return_desc, walk_state, FALSE)) { + /* + * Delete the return value if it will not be used by the + * calling method + */ + acpi_ut_remove_reference (return_desc); + } + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_terminate_control_method + * + * PARAMETERS: walk_state - State of the method + * + * RETURN: Status + * + * DESCRIPTION: Terminate a control method. Delete everything that the method + * created, delete all locals and arguments, and delete the parse + * tree if requested. + * + ******************************************************************************/ + +acpi_status +acpi_ds_terminate_control_method ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *method_node; + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ds_terminate_control_method", walk_state); + + + if (!walk_state) { + return (AE_BAD_PARAMETER); + } + + /* The current method object was saved in the walk state */ + + obj_desc = walk_state->method_desc; + if (!obj_desc) { + return_ACPI_STATUS (AE_OK); + } + + /* Delete all arguments and locals */ + + acpi_ds_method_data_delete_all (walk_state); + + /* + * Lock the parser while we terminate this method. + * If this is the last thread executing the method, + * we have additional cleanup to perform + */ + status = acpi_ut_acquire_mutex (ACPI_MTX_PARSER); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Signal completion of the execution of this method if necessary */ + + if (walk_state->method_desc->method.semaphore) { + status = acpi_os_signal_semaphore ( + walk_state->method_desc->method.semaphore, 1); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not signal method semaphore\n")); + status = AE_OK; + + /* Ignore error and continue cleanup */ + } + } + + if (walk_state->method_desc->method.thread_count) { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "*** Not deleting method namespace, there are still %d threads\n", + walk_state->method_desc->method.thread_count)); + } + + if (!walk_state->method_desc->method.thread_count) { + /* + * Support to dynamically change a method from not_serialized to + * Serialized if it appears that the method is written foolishly and + * does not support multiple thread execution. The best example of this + * is if such a method creates namespace objects and blocks. A second + * thread will fail with an AE_ALREADY_EXISTS exception + * + * This code is here because we must wait until the last thread exits + * before creating the synchronization semaphore. + */ + if ((walk_state->method_desc->method.concurrency == 1) && + (!walk_state->method_desc->method.semaphore)) { + status = acpi_os_create_semaphore (1, + 1, + &walk_state->method_desc->method.semaphore); + } + + /* + * There are no more threads executing this method. Perform + * additional cleanup. + * + * The method Node is stored in the walk state + */ + method_node = walk_state->method_node; + + /* + * Delete any namespace entries created immediately underneath + * the method + */ + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (method_node->child) { + acpi_ns_delete_namespace_subtree (method_node); + } + + /* + * Delete any namespace entries created anywhere else within + * the namespace + */ + acpi_ns_delete_namespace_by_owner (walk_state->method_desc->method.owning_id); + status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + status = acpi_ut_release_mutex (ACPI_MTX_PARSER); + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/dispatcher/dsmthdat.c b/drivers/acpi/dispatcher/dsmthdat.c new file mode 100644 index 00000000000..f31d095f983 --- /dev/null +++ b/drivers/acpi/dispatcher/dsmthdat.c @@ -0,0 +1,715 @@ +/******************************************************************************* + * + * Module Name: dsmthdat - control method arguments and local variables + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acdispat.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> +#include <acpi/acinterp.h> + + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dsmthdat") + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_init + * + * PARAMETERS: walk_state - Current walk state object + * + * RETURN: Status + * + * DESCRIPTION: Initialize the data structures that hold the method's arguments + * and locals. The data struct is an array of NTEs for each. + * This allows ref_of and de_ref_of to work properly for these + * special data types. + * + * NOTES: walk_state fields are initialized to zero by the + * ACPI_MEM_CALLOCATE(). + * + * A pseudo-Namespace Node is assigned to each argument and local + * so that ref_of() can return a pointer to the Node. + * + ******************************************************************************/ + +void +acpi_ds_method_data_init ( + struct acpi_walk_state *walk_state) +{ + u32 i; + + + ACPI_FUNCTION_TRACE ("ds_method_data_init"); + + + /* Init the method arguments */ + + for (i = 0; i < ACPI_METHOD_NUM_ARGS; i++) { + ACPI_MOVE_32_TO_32 (&walk_state->arguments[i].name, + NAMEOF_ARG_NTE); + walk_state->arguments[i].name.integer |= (i << 24); + walk_state->arguments[i].descriptor = ACPI_DESC_TYPE_NAMED; + walk_state->arguments[i].type = ACPI_TYPE_ANY; + walk_state->arguments[i].flags = ANOBJ_END_OF_PEER_LIST | ANOBJ_METHOD_ARG; + } + + /* Init the method locals */ + + for (i = 0; i < ACPI_METHOD_NUM_LOCALS; i++) { + ACPI_MOVE_32_TO_32 (&walk_state->local_variables[i].name, + NAMEOF_LOCAL_NTE); + + walk_state->local_variables[i].name.integer |= (i << 24); + walk_state->local_variables[i].descriptor = ACPI_DESC_TYPE_NAMED; + walk_state->local_variables[i].type = ACPI_TYPE_ANY; + walk_state->local_variables[i].flags = ANOBJ_END_OF_PEER_LIST | ANOBJ_METHOD_LOCAL; + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_delete_all + * + * PARAMETERS: walk_state - Current walk state object + * + * RETURN: None + * + * DESCRIPTION: Delete method locals and arguments. Arguments are only + * deleted if this method was called from another method. + * + ******************************************************************************/ + +void +acpi_ds_method_data_delete_all ( + struct acpi_walk_state *walk_state) +{ + u32 index; + + + ACPI_FUNCTION_TRACE ("ds_method_data_delete_all"); + + + /* Detach the locals */ + + for (index = 0; index < ACPI_METHOD_NUM_LOCALS; index++) { + if (walk_state->local_variables[index].object) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Deleting Local%d=%p\n", + index, walk_state->local_variables[index].object)); + + /* Detach object (if present) and remove a reference */ + + acpi_ns_detach_object (&walk_state->local_variables[index]); + } + } + + /* Detach the arguments */ + + for (index = 0; index < ACPI_METHOD_NUM_ARGS; index++) { + if (walk_state->arguments[index].object) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Deleting Arg%d=%p\n", + index, walk_state->arguments[index].object)); + + /* Detach object (if present) and remove a reference */ + + acpi_ns_detach_object (&walk_state->arguments[index]); + } + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_init_args + * + * PARAMETERS: *Params - Pointer to a parameter list for the method + * max_param_count - The arg count for this method + * walk_state - Current walk state object + * + * RETURN: Status + * + * DESCRIPTION: Initialize arguments for a method. The parameter list is a list + * of ACPI operand objects, either null terminated or whose length + * is defined by max_param_count. + * + ******************************************************************************/ + +acpi_status +acpi_ds_method_data_init_args ( + union acpi_operand_object **params, + u32 max_param_count, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + u32 index = 0; + + + ACPI_FUNCTION_TRACE_PTR ("ds_method_data_init_args", params); + + + if (!params) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "No param list passed to method\n")); + return_ACPI_STATUS (AE_OK); + } + + /* Copy passed parameters into the new method stack frame */ + + while ((index < ACPI_METHOD_NUM_ARGS) && (index < max_param_count) && params[index]) { + /* + * A valid parameter. + * Store the argument in the method/walk descriptor. + * Do not copy the arg in order to implement call by reference + */ + status = acpi_ds_method_data_set_value (AML_ARG_OP, index, params[index], walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + index++; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "%d args passed to method\n", index)); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_get_node + * + * PARAMETERS: Opcode - Either AML_LOCAL_OP or AML_ARG_OP + * Index - which local_var or argument whose type + * to get + * walk_state - Current walk state object + * + * RETURN: Get the Node associated with a local or arg. + * + ******************************************************************************/ + +acpi_status +acpi_ds_method_data_get_node ( + u16 opcode, + u32 index, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **node) +{ + ACPI_FUNCTION_TRACE ("ds_method_data_get_node"); + + + /* + * Method Locals and Arguments are supported + */ + switch (opcode) { + case AML_LOCAL_OP: + + if (index > ACPI_METHOD_MAX_LOCAL) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Local index %d is invalid (max %d)\n", + index, ACPI_METHOD_MAX_LOCAL)); + return_ACPI_STATUS (AE_AML_INVALID_INDEX); + } + + /* Return a pointer to the pseudo-node */ + + *node = &walk_state->local_variables[index]; + break; + + case AML_ARG_OP: + + if (index > ACPI_METHOD_MAX_ARG) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Arg index %d is invalid (max %d)\n", + index, ACPI_METHOD_MAX_ARG)); + return_ACPI_STATUS (AE_AML_INVALID_INDEX); + } + + /* Return a pointer to the pseudo-node */ + + *node = &walk_state->arguments[index]; + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Opcode %d is invalid\n", opcode)); + return_ACPI_STATUS (AE_AML_BAD_OPCODE); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_set_value + * + * PARAMETERS: Opcode - Either AML_LOCAL_OP or AML_ARG_OP + * Index - which local_var or argument to get + * Object - Object to be inserted into the stack entry + * walk_state - Current walk state object + * + * RETURN: Status + * + * DESCRIPTION: Insert an object onto the method stack at entry Opcode:Index. + * Note: There is no "implicit conversion" for locals. + * + ******************************************************************************/ + +acpi_status +acpi_ds_method_data_set_value ( + u16 opcode, + u32 index, + union acpi_operand_object *object, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + struct acpi_namespace_node *node; + + + ACPI_FUNCTION_TRACE ("ds_method_data_set_value"); + + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "new_obj %p Opcode %X, Refs=%d [%s]\n", object, + opcode, object->common.reference_count, + acpi_ut_get_type_name (object->common.type))); + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node (opcode, index, walk_state, &node); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Increment ref count so object can't be deleted while installed. + * NOTE: We do not copy the object in order to preserve the call by + * reference semantics of ACPI Control Method invocation. + * (See ACPI specification 2.0_c) + */ + acpi_ut_add_reference (object); + + /* Install the object */ + + node->object = object; + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_get_type + * + * PARAMETERS: Opcode - Either AML_LOCAL_OP or AML_ARG_OP + * Index - which local_var or argument whose type + * to get + * walk_state - Current walk state object + * + * RETURN: Data type of current value of the selected Arg or Local + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_object_type +acpi_ds_method_data_get_type ( + u16 opcode, + u32 index, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + struct acpi_namespace_node *node; + union acpi_operand_object *object; + + + ACPI_FUNCTION_TRACE ("ds_method_data_get_type"); + + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node (opcode, index, walk_state, &node); + if (ACPI_FAILURE (status)) { + return_VALUE ((ACPI_TYPE_NOT_FOUND)); + } + + /* Get the object */ + + object = acpi_ns_get_attached_object (node); + if (!object) { + /* Uninitialized local/arg, return TYPE_ANY */ + + return_VALUE (ACPI_TYPE_ANY); + } + + /* Get the object type */ + + return_VALUE (ACPI_GET_OBJECT_TYPE (object)); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_get_value + * + * PARAMETERS: Opcode - Either AML_LOCAL_OP or AML_ARG_OP + * Index - which local_var or argument to get + * walk_state - Current walk state object + * *dest_desc - Ptr to Descriptor into which selected Arg + * or Local value should be copied + * + * RETURN: Status + * + * DESCRIPTION: Retrieve value of selected Arg or Local from the method frame + * at the current top of the method stack. + * Used only in acpi_ex_resolve_to_value(). + * + ******************************************************************************/ + +acpi_status +acpi_ds_method_data_get_value ( + u16 opcode, + u32 index, + struct acpi_walk_state *walk_state, + union acpi_operand_object **dest_desc) +{ + acpi_status status; + struct acpi_namespace_node *node; + union acpi_operand_object *object; + + + ACPI_FUNCTION_TRACE ("ds_method_data_get_value"); + + + /* Validate the object descriptor */ + + if (!dest_desc) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null object descriptor pointer\n")); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node (opcode, index, walk_state, &node); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Get the object from the node */ + + object = node->object; + + /* Examine the returned object, it must be valid. */ + + if (!object) { + /* + * Index points to uninitialized object. + * This means that either 1) The expected argument was + * not passed to the method, or 2) A local variable + * was referenced by the method (via the ASL) + * before it was initialized. Either case is an error. + */ + + /* If slack enabled, init the local_x/arg_x to an Integer of value zero */ + + if (acpi_gbl_enable_interpreter_slack) { + object = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!object) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + object->integer.value = 0; + node->object = object; + } + + /* Otherwise, return the error */ + + else switch (opcode) { + case AML_ARG_OP: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Uninitialized Arg[%d] at node %p\n", + index, node)); + + return_ACPI_STATUS (AE_AML_UNINITIALIZED_ARG); + + case AML_LOCAL_OP: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Uninitialized Local[%d] at node %p\n", + index, node)); + + return_ACPI_STATUS (AE_AML_UNINITIALIZED_LOCAL); + + default: + ACPI_REPORT_ERROR (("Not Arg/Local opcode: %X\n", opcode)); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + } + + /* + * The Index points to an initialized and valid object. + * Return an additional reference to the object + */ + *dest_desc = object; + acpi_ut_add_reference (object); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_method_data_delete_value + * + * PARAMETERS: Opcode - Either AML_LOCAL_OP or AML_ARG_OP + * Index - which local_var or argument to delete + * walk_state - Current walk state object + * + * RETURN: None + * + * DESCRIPTION: Delete the entry at Opcode:Index on the method stack. Inserts + * a null into the stack slot after the object is deleted. + * + ******************************************************************************/ + +void +acpi_ds_method_data_delete_value ( + u16 opcode, + u32 index, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + struct acpi_namespace_node *node; + union acpi_operand_object *object; + + + ACPI_FUNCTION_TRACE ("ds_method_data_delete_value"); + + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node (opcode, index, walk_state, &node); + if (ACPI_FAILURE (status)) { + return_VOID; + } + + /* Get the associated object */ + + object = acpi_ns_get_attached_object (node); + + /* + * Undefine the Arg or Local by setting its descriptor + * pointer to NULL. Locals/Args can contain both + * ACPI_OPERAND_OBJECTS and ACPI_NAMESPACE_NODEs + */ + node->object = NULL; + + if ((object) && + (ACPI_GET_DESCRIPTOR_TYPE (object) == ACPI_DESC_TYPE_OPERAND)) { + /* + * There is a valid object. + * Decrement the reference count by one to balance the + * increment when the object was stored. + */ + acpi_ut_remove_reference (object); + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_store_object_to_local + * + * PARAMETERS: Opcode - Either AML_LOCAL_OP or AML_ARG_OP + * Index - which local_var or argument to set + * obj_desc - Value to be stored + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Store a value in an Arg or Local. The obj_desc is installed + * as the new value for the Arg or Local and the reference count + * for obj_desc is incremented. + * + ******************************************************************************/ + +acpi_status +acpi_ds_store_object_to_local ( + u16 opcode, + u32 index, + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + struct acpi_namespace_node *node; + union acpi_operand_object *current_obj_desc; + union acpi_operand_object *new_obj_desc; + + + ACPI_FUNCTION_TRACE ("ds_store_object_to_local"); + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Opcode=%X Index=%d Obj=%p\n", + opcode, index, obj_desc)); + + /* Parameter validation */ + + if (!obj_desc) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Get the namespace node for the arg/local */ + + status = acpi_ds_method_data_get_node (opcode, index, walk_state, &node); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + current_obj_desc = acpi_ns_get_attached_object (node); + if (current_obj_desc == obj_desc) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Obj=%p already installed!\n", + obj_desc)); + return_ACPI_STATUS (status); + } + + /* + * If the reference count on the object is more than one, we must + * take a copy of the object before we store. A reference count + * of exactly 1 means that the object was just created during the + * evaluation of an expression, and we can safely use it since it + * is not used anywhere else. + */ + new_obj_desc = obj_desc; + if (obj_desc->common.reference_count > 1) { + status = acpi_ut_copy_iobject_to_iobject (obj_desc, &new_obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* + * If there is an object already in this slot, we either + * have to delete it, or if this is an argument and there + * is an object reference stored there, we have to do + * an indirect store! + */ + if (current_obj_desc) { + /* + * Check for an indirect store if an argument + * contains an object reference (stored as an Node). + * We don't allow this automatic dereferencing for + * locals, since a store to a local should overwrite + * anything there, including an object reference. + * + * If both Arg0 and Local0 contain ref_of (Local4): + * + * Store (1, Arg0) - Causes indirect store to local4 + * Store (1, Local0) - Stores 1 in local0, overwriting + * the reference to local4 + * Store (1, de_refof (Local0)) - Causes indirect store to local4 + * + * Weird, but true. + */ + if (opcode == AML_ARG_OP) { + /* + * Make sure that the object is the correct type. This may be overkill, but + * it is here because references were NS nodes in the past. Now they are + * operand objects of type Reference. + */ + if (ACPI_GET_DESCRIPTOR_TYPE (current_obj_desc) != ACPI_DESC_TYPE_OPERAND) { + ACPI_REPORT_ERROR (("Invalid descriptor type while storing to method arg: [%s]\n", + acpi_ut_get_descriptor_name (current_obj_desc))); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* + * If we have a valid reference object that came from ref_of(), do the + * indirect store + */ + if ((current_obj_desc->common.type == ACPI_TYPE_LOCAL_REFERENCE) && + (current_obj_desc->reference.opcode == AML_REF_OF_OP)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Arg (%p) is an obj_ref(Node), storing in node %p\n", + new_obj_desc, current_obj_desc)); + + /* + * Store this object to the Node (perform the indirect store) + * NOTE: No implicit conversion is performed, as per the ACPI + * specification rules on storing to Locals/Args. + */ + status = acpi_ex_store_object_to_node (new_obj_desc, + current_obj_desc->reference.object, walk_state, + ACPI_NO_IMPLICIT_CONVERSION); + + /* Remove local reference if we copied the object above */ + + if (new_obj_desc != obj_desc) { + acpi_ut_remove_reference (new_obj_desc); + } + return_ACPI_STATUS (status); + } + } + + /* + * Delete the existing object + * before storing the new one + */ + acpi_ds_method_data_delete_value (opcode, index, walk_state); + } + + /* + * Install the Obj descriptor (*new_obj_desc) into + * the descriptor for the Arg or Local. + * (increments the object reference count by one) + */ + status = acpi_ds_method_data_set_value (opcode, index, new_obj_desc, walk_state); + + /* Remove local reference if we copied the object above */ + + if (new_obj_desc != obj_desc) { + acpi_ut_remove_reference (new_obj_desc); + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/dispatcher/dsobject.c b/drivers/acpi/dispatcher/dsobject.c new file mode 100644 index 00000000000..eb8af4785bc --- /dev/null +++ b/drivers/acpi/dispatcher/dsobject.c @@ -0,0 +1,618 @@ +/****************************************************************************** + * + * Module Name: dsobject - Dispatcher object management routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> +#include <acpi/acdispat.h> +#include <acpi/acnamesp.h> +#include <acpi/acinterp.h> + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dsobject") + + +#ifndef ACPI_NO_METHOD_EXECUTION +/***************************************************************************** + * + * FUNCTION: acpi_ds_build_internal_object + * + * PARAMETERS: walk_state - Current walk state + * Op - Parser object to be translated + * obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Translate a parser Op object to the equivalent namespace object + * Simple objects are any objects other than a package object! + * + ****************************************************************************/ + +acpi_status +acpi_ds_build_internal_object ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + union acpi_operand_object **obj_desc_ptr) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ds_build_internal_object"); + + + *obj_desc_ptr = NULL; + if (op->common.aml_opcode == AML_INT_NAMEPATH_OP) { + /* + * This is an named object reference. If this name was + * previously looked up in the namespace, it was stored in this op. + * Otherwise, go ahead and look it up now + */ + if (!op->common.node) { + status = acpi_ns_lookup (walk_state->scope_info, op->common.value.string, + ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, NULL, + (struct acpi_namespace_node **) &(op->common.node)); + + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (op->common.value.string, status); + return_ACPI_STATUS (status); + } + } + } + + /* Create and init the internal ACPI object */ + + obj_desc = acpi_ut_create_internal_object ((acpi_ps_get_opcode_info (op->common.aml_opcode))->object_type); + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + status = acpi_ds_init_object_from_op (walk_state, op, op->common.aml_opcode, &obj_desc); + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); + } + + *obj_desc_ptr = obj_desc; + return_ACPI_STATUS (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_build_internal_buffer_obj + * + * PARAMETERS: walk_state - Current walk state + * Op - Parser object to be translated + * buffer_length - Length of the buffer + * obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Translate a parser Op package object to the equivalent + * namespace object + * + ****************************************************************************/ + +acpi_status +acpi_ds_build_internal_buffer_obj ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u32 buffer_length, + union acpi_operand_object **obj_desc_ptr) +{ + union acpi_parse_object *arg; + union acpi_operand_object *obj_desc; + union acpi_parse_object *byte_list; + u32 byte_list_length = 0; + + + ACPI_FUNCTION_TRACE ("ds_build_internal_buffer_obj"); + + + obj_desc = *obj_desc_ptr; + if (obj_desc) { + /* + * We are evaluating a Named buffer object "Name (xxxx, Buffer)". + * The buffer object already exists (from the NS node) + */ + } + else { + /* Create a new buffer object */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_BUFFER); + *obj_desc_ptr = obj_desc; + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + } + + /* + * Second arg is the buffer data (optional) byte_list can be either + * individual bytes or a string initializer. In either case, a + * byte_list appears in the AML. + */ + arg = op->common.value.arg; /* skip first arg */ + + byte_list = arg->named.next; + if (byte_list) { + if (byte_list->common.aml_opcode != AML_INT_BYTELIST_OP) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Expecting bytelist, got AML opcode %X in op %p\n", + byte_list->common.aml_opcode, byte_list)); + + acpi_ut_remove_reference (obj_desc); + return (AE_TYPE); + } + + byte_list_length = (u32) byte_list->common.value.integer; + } + + /* + * The buffer length (number of bytes) will be the larger of: + * 1) The specified buffer length and + * 2) The length of the initializer byte list + */ + obj_desc->buffer.length = buffer_length; + if (byte_list_length > buffer_length) { + obj_desc->buffer.length = byte_list_length; + } + + /* Allocate the buffer */ + + if (obj_desc->buffer.length == 0) { + obj_desc->buffer.pointer = NULL; + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Buffer defined with zero length in AML, creating\n")); + } + else { + obj_desc->buffer.pointer = ACPI_MEM_CALLOCATE ( + obj_desc->buffer.length); + if (!obj_desc->buffer.pointer) { + acpi_ut_delete_object_desc (obj_desc); + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Initialize buffer from the byte_list (if present) */ + + if (byte_list) { + ACPI_MEMCPY (obj_desc->buffer.pointer, byte_list->named.data, + byte_list_length); + } + } + + obj_desc->buffer.flags |= AOPOBJ_DATA_VALID; + op->common.node = (struct acpi_namespace_node *) obj_desc; + return_ACPI_STATUS (AE_OK); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_build_internal_package_obj + * + * PARAMETERS: walk_state - Current walk state + * Op - Parser object to be translated + * package_length - Number of elements in the package + * obj_desc_ptr - Where the ACPI internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: Translate a parser Op package object to the equivalent + * namespace object + * + ****************************************************************************/ + +acpi_status +acpi_ds_build_internal_package_obj ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u32 package_length, + union acpi_operand_object **obj_desc_ptr) +{ + union acpi_parse_object *arg; + union acpi_parse_object *parent; + union acpi_operand_object *obj_desc = NULL; + u32 package_list_length; + acpi_status status = AE_OK; + u32 i; + + + ACPI_FUNCTION_TRACE ("ds_build_internal_package_obj"); + + + /* Find the parent of a possibly nested package */ + + parent = op->common.parent; + while ((parent->common.aml_opcode == AML_PACKAGE_OP) || + (parent->common.aml_opcode == AML_VAR_PACKAGE_OP)) { + parent = parent->common.parent; + } + + obj_desc = *obj_desc_ptr; + if (obj_desc) { + /* + * We are evaluating a Named package object "Name (xxxx, Package)". + * Get the existing package object from the NS node + */ + } + else { + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_PACKAGE); + *obj_desc_ptr = obj_desc; + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + obj_desc->package.node = parent->common.node; + } + + obj_desc->package.count = package_length; + + /* Count the number of items in the package list */ + + package_list_length = 0; + arg = op->common.value.arg; + arg = arg->common.next; + while (arg) { + package_list_length++; + arg = arg->common.next; + } + + /* + * The package length (number of elements) will be the greater + * of the specified length and the length of the initializer list + */ + if (package_list_length > package_length) { + obj_desc->package.count = package_list_length; + } + + /* + * Allocate the pointer array (array of pointers to the + * individual objects). Add an extra pointer slot so + * that the list is always null terminated. + */ + obj_desc->package.elements = ACPI_MEM_CALLOCATE ( + ((acpi_size) obj_desc->package.count + 1) * sizeof (void *)); + + if (!obj_desc->package.elements) { + acpi_ut_delete_object_desc (obj_desc); + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* + * Now init the elements of the package + */ + i = 0; + arg = op->common.value.arg; + arg = arg->common.next; + while (arg) { + if (arg->common.aml_opcode == AML_INT_RETURN_VALUE_OP) { + /* Object (package or buffer) is already built */ + + obj_desc->package.elements[i] = ACPI_CAST_PTR (union acpi_operand_object, arg->common.node); + } + else { + status = acpi_ds_build_internal_object (walk_state, arg, + &obj_desc->package.elements[i]); + } + + i++; + arg = arg->common.next; + } + + obj_desc->package.flags |= AOPOBJ_DATA_VALID; + op->common.node = (struct acpi_namespace_node *) obj_desc; + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_create_node + * + * PARAMETERS: walk_state - Current walk state + * Node - NS Node to be initialized + * Op - Parser object to be translated + * + * RETURN: Status + * + * DESCRIPTION: Create the object to be associated with a namespace node + * + ****************************************************************************/ + +acpi_status +acpi_ds_create_node ( + struct acpi_walk_state *walk_state, + struct acpi_namespace_node *node, + union acpi_parse_object *op) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_TRACE_PTR ("ds_create_node", op); + + + /* + * Because of the execution pass through the non-control-method + * parts of the table, we can arrive here twice. Only init + * the named object node the first time through + */ + if (acpi_ns_get_attached_object (node)) { + return_ACPI_STATUS (AE_OK); + } + + if (!op->common.value.arg) { + /* No arguments, there is nothing to do */ + + return_ACPI_STATUS (AE_OK); + } + + /* Build an internal object for the argument(s) */ + + status = acpi_ds_build_internal_object (walk_state, op->common.value.arg, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Re-type the object according to its argument */ + + node->type = ACPI_GET_OBJECT_TYPE (obj_desc); + + /* Attach obj to node */ + + status = acpi_ns_attach_object (node, obj_desc, node->type); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + +#endif /* ACPI_NO_METHOD_EXECUTION */ + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_init_object_from_op + * + * PARAMETERS: walk_state - Current walk state + * Op - Parser op used to init the internal object + * Opcode - AML opcode associated with the object + * ret_obj_desc - Namespace object to be initialized + * + * RETURN: Status + * + * DESCRIPTION: Initialize a namespace object from a parser Op and its + * associated arguments. The namespace object is a more compact + * representation of the Op and its arguments. + * + ****************************************************************************/ + +acpi_status +acpi_ds_init_object_from_op ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + u16 opcode, + union acpi_operand_object **ret_obj_desc) +{ + const struct acpi_opcode_info *op_info; + union acpi_operand_object *obj_desc; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ds_init_object_from_op"); + + + obj_desc = *ret_obj_desc; + op_info = acpi_ps_get_opcode_info (opcode); + if (op_info->class == AML_CLASS_UNKNOWN) { + /* Unknown opcode */ + + return_ACPI_STATUS (AE_TYPE); + } + + /* Perform per-object initialization */ + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_BUFFER: + + /* + * Defer evaluation of Buffer term_arg operand + */ + obj_desc->buffer.node = (struct acpi_namespace_node *) walk_state->operands[0]; + obj_desc->buffer.aml_start = op->named.data; + obj_desc->buffer.aml_length = op->named.length; + break; + + + case ACPI_TYPE_PACKAGE: + + /* + * Defer evaluation of Package term_arg operand + */ + obj_desc->package.node = (struct acpi_namespace_node *) walk_state->operands[0]; + obj_desc->package.aml_start = op->named.data; + obj_desc->package.aml_length = op->named.length; + break; + + + case ACPI_TYPE_INTEGER: + + switch (op_info->type) { + case AML_TYPE_CONSTANT: + /* + * Resolve AML Constants here - AND ONLY HERE! + * All constants are integers. + * We mark the integer with a flag that indicates that it started life + * as a constant -- so that stores to constants will perform as expected (noop). + * (zero_op is used as a placeholder for optional target operands.) + */ + obj_desc->common.flags = AOPOBJ_AML_CONSTANT; + + switch (opcode) { + case AML_ZERO_OP: + + obj_desc->integer.value = 0; + break; + + case AML_ONE_OP: + + obj_desc->integer.value = 1; + break; + + case AML_ONES_OP: + + obj_desc->integer.value = ACPI_INTEGER_MAX; + + /* Truncate value if we are executing from a 32-bit ACPI table */ + +#ifndef ACPI_NO_METHOD_EXECUTION + acpi_ex_truncate_for32bit_table (obj_desc); +#endif + break; + + case AML_REVISION_OP: + + obj_desc->integer.value = ACPI_CA_VERSION; + break; + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown constant opcode %X\n", opcode)); + status = AE_AML_OPERAND_TYPE; + break; + } + break; + + + case AML_TYPE_LITERAL: + + obj_desc->integer.value = op->common.value.integer; + break; + + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown Integer type %X\n", op_info->type)); + status = AE_AML_OPERAND_TYPE; + break; + } + break; + + + case ACPI_TYPE_STRING: + + obj_desc->string.pointer = op->common.value.string; + obj_desc->string.length = (u32) ACPI_STRLEN (op->common.value.string); + + /* + * The string is contained in the ACPI table, don't ever try + * to delete it + */ + obj_desc->common.flags |= AOPOBJ_STATIC_POINTER; + break; + + + case ACPI_TYPE_METHOD: + break; + + + case ACPI_TYPE_LOCAL_REFERENCE: + + switch (op_info->type) { + case AML_TYPE_LOCAL_VARIABLE: + + /* Split the opcode into a base opcode + offset */ + + obj_desc->reference.opcode = AML_LOCAL_OP; + obj_desc->reference.offset = opcode - AML_LOCAL_OP; + +#ifndef ACPI_NO_METHOD_EXECUTION + status = acpi_ds_method_data_get_node (AML_LOCAL_OP, obj_desc->reference.offset, + walk_state, (struct acpi_namespace_node **) &obj_desc->reference.object); +#endif + break; + + + case AML_TYPE_METHOD_ARGUMENT: + + /* Split the opcode into a base opcode + offset */ + + obj_desc->reference.opcode = AML_ARG_OP; + obj_desc->reference.offset = opcode - AML_ARG_OP; + +#ifndef ACPI_NO_METHOD_EXECUTION + status = acpi_ds_method_data_get_node (AML_ARG_OP, obj_desc->reference.offset, + walk_state, (struct acpi_namespace_node **) &obj_desc->reference.object); +#endif + break; + + default: /* Other literals, etc.. */ + + if (op->common.aml_opcode == AML_INT_NAMEPATH_OP) { + /* Node was saved in Op */ + + obj_desc->reference.node = op->common.node; + } + + obj_desc->reference.opcode = opcode; + break; + } + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unimplemented data type: %X\n", + ACPI_GET_OBJECT_TYPE (obj_desc))); + + status = AE_AML_OPERAND_TYPE; + break; + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/dispatcher/dsopcode.c b/drivers/acpi/dispatcher/dsopcode.c new file mode 100644 index 00000000000..5c987a0e7b7 --- /dev/null +++ b/drivers/acpi/dispatcher/dsopcode.c @@ -0,0 +1,1151 @@ +/****************************************************************************** + * + * Module Name: dsopcode - Dispatcher Op Region support and handling of + * "control" opcodes + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> +#include <acpi/acevents.h> + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dsopcode") + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_execute_arguments + * + * PARAMETERS: Node - Parent NS node + * aml_length - Length of executable AML + * aml_start - Pointer to the AML + * + * RETURN: Status. + * + * DESCRIPTION: Late (deferred) execution of region or field arguments + * + ****************************************************************************/ + +acpi_status +acpi_ds_execute_arguments ( + struct acpi_namespace_node *node, + struct acpi_namespace_node *scope_node, + u32 aml_length, + u8 *aml_start) +{ + acpi_status status; + union acpi_parse_object *op; + struct acpi_walk_state *walk_state; + + + ACPI_FUNCTION_TRACE ("ds_execute_arguments"); + + + /* + * Allocate a new parser op to be the root of the parsed tree + */ + op = acpi_ps_alloc_op (AML_INT_EVAL_SUBTREE_OP); + if (!op) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Save the Node for use in acpi_ps_parse_aml */ + + op->common.node = scope_node; + + /* Create and initialize a new parser state */ + + walk_state = acpi_ds_create_walk_state (0, NULL, NULL, NULL); + if (!walk_state) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + status = acpi_ds_init_aml_walk (walk_state, op, NULL, aml_start, + aml_length, NULL, 1); + if (ACPI_FAILURE (status)) { + acpi_ds_delete_walk_state (walk_state); + return_ACPI_STATUS (status); + } + + /* Mark this parse as a deferred opcode */ + + walk_state->parse_flags = ACPI_PARSE_DEFERRED_OP; + walk_state->deferred_node = node; + + /* Pass1: Parse the entire declaration */ + + status = acpi_ps_parse_aml (walk_state); + if (ACPI_FAILURE (status)) { + acpi_ps_delete_parse_tree (op); + return_ACPI_STATUS (status); + } + + /* Get and init the Op created above */ + + op->common.node = node; + acpi_ps_delete_parse_tree (op); + + /* Evaluate the deferred arguments */ + + op = acpi_ps_alloc_op (AML_INT_EVAL_SUBTREE_OP); + if (!op) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + op->common.node = scope_node; + + /* Create and initialize a new parser state */ + + walk_state = acpi_ds_create_walk_state (0, NULL, NULL, NULL); + if (!walk_state) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Execute the opcode and arguments */ + + status = acpi_ds_init_aml_walk (walk_state, op, NULL, aml_start, + aml_length, NULL, 3); + if (ACPI_FAILURE (status)) { + acpi_ds_delete_walk_state (walk_state); + return_ACPI_STATUS (status); + } + + /* Mark this execution as a deferred opcode */ + + walk_state->deferred_node = node; + status = acpi_ps_parse_aml (walk_state); + acpi_ps_delete_parse_tree (op); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_get_buffer_field_arguments + * + * PARAMETERS: obj_desc - A valid buffer_field object + * + * RETURN: Status. + * + * DESCRIPTION: Get buffer_field Buffer and Index. This implements the late + * evaluation of these field attributes. + * + ****************************************************************************/ + +acpi_status +acpi_ds_get_buffer_field_arguments ( + union acpi_operand_object *obj_desc) +{ + union acpi_operand_object *extra_desc; + struct acpi_namespace_node *node; + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ds_get_buffer_field_arguments", obj_desc); + + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + return_ACPI_STATUS (AE_OK); + } + + /* Get the AML pointer (method object) and buffer_field node */ + + extra_desc = acpi_ns_get_secondary_object (obj_desc); + node = obj_desc->buffer_field.node; + + ACPI_DEBUG_EXEC(acpi_ut_display_init_pathname (ACPI_TYPE_BUFFER_FIELD, node, NULL)); + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s] buffer_field Arg Init\n", + acpi_ut_get_node_name (node))); + + /* Execute the AML code for the term_arg arguments */ + + status = acpi_ds_execute_arguments (node, acpi_ns_get_parent_node (node), + extra_desc->extra.aml_length, extra_desc->extra.aml_start); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_get_buffer_arguments + * + * PARAMETERS: obj_desc - A valid Buffer object + * + * RETURN: Status. + * + * DESCRIPTION: Get Buffer length and initializer byte list. This implements + * the late evaluation of these attributes. + * + ****************************************************************************/ + +acpi_status +acpi_ds_get_buffer_arguments ( + union acpi_operand_object *obj_desc) +{ + struct acpi_namespace_node *node; + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ds_get_buffer_arguments", obj_desc); + + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + return_ACPI_STATUS (AE_OK); + } + + /* Get the Buffer node */ + + node = obj_desc->buffer.node; + if (!node) { + ACPI_REPORT_ERROR (( + "No pointer back to NS node in buffer obj %p\n", obj_desc)); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Buffer Arg Init\n")); + + /* Execute the AML code for the term_arg arguments */ + + status = acpi_ds_execute_arguments (node, node, + obj_desc->buffer.aml_length, obj_desc->buffer.aml_start); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_get_package_arguments + * + * PARAMETERS: obj_desc - A valid Package object + * + * RETURN: Status. + * + * DESCRIPTION: Get Package length and initializer byte list. This implements + * the late evaluation of these attributes. + * + ****************************************************************************/ + +acpi_status +acpi_ds_get_package_arguments ( + union acpi_operand_object *obj_desc) +{ + struct acpi_namespace_node *node; + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ds_get_package_arguments", obj_desc); + + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + return_ACPI_STATUS (AE_OK); + } + + /* Get the Package node */ + + node = obj_desc->package.node; + if (!node) { + ACPI_REPORT_ERROR (( + "No pointer back to NS node in package %p\n", obj_desc)); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Package Arg Init\n")); + + /* Execute the AML code for the term_arg arguments */ + + status = acpi_ds_execute_arguments (node, node, + obj_desc->package.aml_length, obj_desc->package.aml_start); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_get_region_arguments + * + * PARAMETERS: obj_desc - A valid region object + * + * RETURN: Status. + * + * DESCRIPTION: Get region address and length. This implements the late + * evaluation of these region attributes. + * + ****************************************************************************/ + +acpi_status +acpi_ds_get_region_arguments ( + union acpi_operand_object *obj_desc) +{ + struct acpi_namespace_node *node; + acpi_status status; + union acpi_operand_object *extra_desc; + + + ACPI_FUNCTION_TRACE_PTR ("ds_get_region_arguments", obj_desc); + + + if (obj_desc->region.flags & AOPOBJ_DATA_VALID) { + return_ACPI_STATUS (AE_OK); + } + + extra_desc = acpi_ns_get_secondary_object (obj_desc); + if (!extra_desc) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + + /* Get the Region node */ + + node = obj_desc->region.node; + + ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname (ACPI_TYPE_REGION, node, NULL)); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s] op_region Arg Init at AML %p\n", + acpi_ut_get_node_name (node), extra_desc->extra.aml_start)); + + /* Execute the argument AML */ + + status = acpi_ds_execute_arguments (node, acpi_ns_get_parent_node (node), + extra_desc->extra.aml_length, extra_desc->extra.aml_start); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_initialize_region + * + * PARAMETERS: Op - A valid region Op object + * + * RETURN: Status + * + * DESCRIPTION: Front end to ev_initialize_region + * + ****************************************************************************/ + +acpi_status +acpi_ds_initialize_region ( + acpi_handle obj_handle) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + obj_desc = acpi_ns_get_attached_object (obj_handle); + + /* Namespace is NOT locked */ + + status = acpi_ev_initialize_region (obj_desc, FALSE); + return (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_init_buffer_field + * + * PARAMETERS: aml_opcode - create_xxx_field + * obj_desc - buffer_field object + * buffer_desc - Host Buffer + * offset_desc - Offset into buffer + * Length - Length of field (CREATE_FIELD_OP only) + * Result - Where to store the result + * + * RETURN: Status + * + * DESCRIPTION: Perform actual initialization of a buffer field + * + ****************************************************************************/ + +acpi_status +acpi_ds_init_buffer_field ( + u16 aml_opcode, + union acpi_operand_object *obj_desc, + union acpi_operand_object *buffer_desc, + union acpi_operand_object *offset_desc, + union acpi_operand_object *length_desc, + union acpi_operand_object *result_desc) +{ + u32 offset; + u32 bit_offset; + u32 bit_count; + u8 field_flags; + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ds_init_buffer_field", obj_desc); + + + /* Host object must be a Buffer */ + + if (ACPI_GET_OBJECT_TYPE (buffer_desc) != ACPI_TYPE_BUFFER) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Target of Create Field is not a Buffer object - %s\n", + acpi_ut_get_object_type_name (buffer_desc))); + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* + * The last parameter to all of these opcodes (result_desc) started + * out as a name_string, and should therefore now be a NS node + * after resolution in acpi_ex_resolve_operands(). + */ + if (ACPI_GET_DESCRIPTOR_TYPE (result_desc) != ACPI_DESC_TYPE_NAMED) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "(%s) destination not a NS Node [%s]\n", + acpi_ps_get_opcode_name (aml_opcode), acpi_ut_get_descriptor_name (result_desc))); + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + offset = (u32) offset_desc->integer.value; + + /* + * Setup the Bit offsets and counts, according to the opcode + */ + switch (aml_opcode) { + case AML_CREATE_FIELD_OP: + + /* Offset is in bits, count is in bits */ + + bit_offset = offset; + bit_count = (u32) length_desc->integer.value; + field_flags = AML_FIELD_ACCESS_BYTE; + break; + + case AML_CREATE_BIT_FIELD_OP: + + /* Offset is in bits, Field is one bit */ + + bit_offset = offset; + bit_count = 1; + field_flags = AML_FIELD_ACCESS_BYTE; + break; + + case AML_CREATE_BYTE_FIELD_OP: + + /* Offset is in bytes, field is one byte */ + + bit_offset = 8 * offset; + bit_count = 8; + field_flags = AML_FIELD_ACCESS_BYTE; + break; + + case AML_CREATE_WORD_FIELD_OP: + + /* Offset is in bytes, field is one word */ + + bit_offset = 8 * offset; + bit_count = 16; + field_flags = AML_FIELD_ACCESS_WORD; + break; + + case AML_CREATE_DWORD_FIELD_OP: + + /* Offset is in bytes, field is one dword */ + + bit_offset = 8 * offset; + bit_count = 32; + field_flags = AML_FIELD_ACCESS_DWORD; + break; + + case AML_CREATE_QWORD_FIELD_OP: + + /* Offset is in bytes, field is one qword */ + + bit_offset = 8 * offset; + bit_count = 64; + field_flags = AML_FIELD_ACCESS_QWORD; + break; + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unknown field creation opcode %02x\n", + aml_opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + /* Entire field must fit within the current length of the buffer */ + + if ((bit_offset + bit_count) > + (8 * (u32) buffer_desc->buffer.length)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Field [%4.4s] size %d exceeds Buffer [%4.4s] size %d (bits)\n", + acpi_ut_get_node_name (result_desc), + bit_offset + bit_count, + acpi_ut_get_node_name (buffer_desc->buffer.node), + 8 * (u32) buffer_desc->buffer.length)); + status = AE_AML_BUFFER_LIMIT; + goto cleanup; + } + + /* + * Initialize areas of the field object that are common to all fields + * For field_flags, use LOCK_RULE = 0 (NO_LOCK), UPDATE_RULE = 0 (UPDATE_PRESERVE) + */ + status = acpi_ex_prep_common_field_object (obj_desc, field_flags, 0, + bit_offset, bit_count); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + obj_desc->buffer_field.buffer_obj = buffer_desc; + + /* Reference count for buffer_desc inherits obj_desc count */ + + buffer_desc->common.reference_count = (u16) (buffer_desc->common.reference_count + + obj_desc->common.reference_count); + + +cleanup: + + /* Always delete the operands */ + + acpi_ut_remove_reference (offset_desc); + acpi_ut_remove_reference (buffer_desc); + + if (aml_opcode == AML_CREATE_FIELD_OP) { + acpi_ut_remove_reference (length_desc); + } + + /* On failure, delete the result descriptor */ + + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (result_desc); /* Result descriptor */ + } + else { + /* Now the address and length are valid for this buffer_field */ + + obj_desc->buffer_field.flags |= AOPOBJ_DATA_VALID; + } + + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_eval_buffer_field_operands + * + * PARAMETERS: walk_state - Current walk + * Op - A valid buffer_field Op object + * + * RETURN: Status + * + * DESCRIPTION: Get buffer_field Buffer and Index + * Called from acpi_ds_exec_end_op during buffer_field parse tree walk + * + ****************************************************************************/ + +acpi_status +acpi_ds_eval_buffer_field_operands ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + union acpi_parse_object *next_op; + + + ACPI_FUNCTION_TRACE_PTR ("ds_eval_buffer_field_operands", op); + + + /* + * This is where we evaluate the address and length fields of the + * create_xxx_field declaration + */ + node = op->common.node; + + /* next_op points to the op that holds the Buffer */ + + next_op = op->common.value.arg; + + /* Evaluate/create the address and length operands */ + + status = acpi_ds_create_operands (walk_state, next_op); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + + /* Resolve the operands */ + + status = acpi_ex_resolve_operands (op->common.aml_opcode, + ACPI_WALK_OPERANDS, walk_state); + + ACPI_DUMP_OPERANDS (ACPI_WALK_OPERANDS, ACPI_IMODE_EXECUTE, + acpi_ps_get_opcode_name (op->common.aml_opcode), + walk_state->num_operands, "after acpi_ex_resolve_operands"); + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "(%s) bad operand(s) (%X)\n", + acpi_ps_get_opcode_name (op->common.aml_opcode), status)); + + return_ACPI_STATUS (status); + } + + /* Initialize the Buffer Field */ + + if (op->common.aml_opcode == AML_CREATE_FIELD_OP) { + /* NOTE: Slightly different operands for this opcode */ + + status = acpi_ds_init_buffer_field (op->common.aml_opcode, obj_desc, + walk_state->operands[0], walk_state->operands[1], + walk_state->operands[2], walk_state->operands[3]); + } + else { + /* All other, create_xxx_field opcodes */ + + status = acpi_ds_init_buffer_field (op->common.aml_opcode, obj_desc, + walk_state->operands[0], walk_state->operands[1], + NULL, walk_state->operands[2]); + } + + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_eval_region_operands + * + * PARAMETERS: walk_state - Current walk + * Op - A valid region Op object + * + * RETURN: Status + * + * DESCRIPTION: Get region address and length + * Called from acpi_ds_exec_end_op during op_region parse tree walk + * + ****************************************************************************/ + +acpi_status +acpi_ds_eval_region_operands ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + union acpi_operand_object *operand_desc; + struct acpi_namespace_node *node; + union acpi_parse_object *next_op; + + + ACPI_FUNCTION_TRACE_PTR ("ds_eval_region_operands", op); + + + /* + * This is where we evaluate the address and length fields of the op_region declaration + */ + node = op->common.node; + + /* next_op points to the op that holds the space_iD */ + + next_op = op->common.value.arg; + + /* next_op points to address op */ + + next_op = next_op->common.next; + + /* Evaluate/create the address and length operands */ + + status = acpi_ds_create_operands (walk_state, next_op); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Resolve the length and address operands to numbers */ + + status = acpi_ex_resolve_operands (op->common.aml_opcode, ACPI_WALK_OPERANDS, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + ACPI_DUMP_OPERANDS (ACPI_WALK_OPERANDS, ACPI_IMODE_EXECUTE, + acpi_ps_get_opcode_name (op->common.aml_opcode), + 1, "after acpi_ex_resolve_operands"); + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + + /* + * Get the length operand and save it + * (at Top of stack) + */ + operand_desc = walk_state->operands[walk_state->num_operands - 1]; + + obj_desc->region.length = (u32) operand_desc->integer.value; + acpi_ut_remove_reference (operand_desc); + + /* + * Get the address and save it + * (at top of stack - 1) + */ + operand_desc = walk_state->operands[walk_state->num_operands - 2]; + + obj_desc->region.address = (acpi_physical_address) operand_desc->integer.value; + acpi_ut_remove_reference (operand_desc); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "rgn_obj %p Addr %8.8X%8.8X Len %X\n", + obj_desc, + ACPI_FORMAT_UINT64 (obj_desc->region.address), + obj_desc->region.length)); + + /* Now the address and length are valid for this opregion */ + + obj_desc->region.flags |= AOPOBJ_DATA_VALID; + + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_eval_data_object_operands + * + * PARAMETERS: walk_state - Current walk + * Op - A valid data_object Op object + * obj_desc - data_object + * + * RETURN: Status + * + * DESCRIPTION: Get the operands and complete the following data object types: + * Buffer, Package. + * + ****************************************************************************/ + +acpi_status +acpi_ds_eval_data_object_operands ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + union acpi_operand_object *obj_desc) +{ + acpi_status status; + union acpi_operand_object *arg_desc; + u32 length; + + + ACPI_FUNCTION_TRACE ("ds_eval_data_object_operands"); + + + /* The first operand (for all of these data objects) is the length */ + + status = acpi_ds_create_operand (walk_state, op->common.value.arg, 1); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_ex_resolve_operands (walk_state->opcode, + &(walk_state->operands [walk_state->num_operands -1]), + walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Extract length operand */ + + arg_desc = walk_state->operands [walk_state->num_operands - 1]; + length = (u32) arg_desc->integer.value; + + /* Cleanup for length operand */ + + status = acpi_ds_obj_stack_pop (1, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + acpi_ut_remove_reference (arg_desc); + + /* + * Create the actual data object + */ + switch (op->common.aml_opcode) { + case AML_BUFFER_OP: + + status = acpi_ds_build_internal_buffer_obj (walk_state, op, length, &obj_desc); + break; + + case AML_PACKAGE_OP: + case AML_VAR_PACKAGE_OP: + + status = acpi_ds_build_internal_package_obj (walk_state, op, length, &obj_desc); + break; + + default: + return_ACPI_STATUS (AE_AML_BAD_OPCODE); + } + + if (ACPI_SUCCESS (status)) { + /* + * Return the object in the walk_state, unless the parent is a package -- + * in this case, the return object will be stored in the parse tree + * for the package. + */ + if ((!op->common.parent) || + ((op->common.parent->common.aml_opcode != AML_PACKAGE_OP) && + (op->common.parent->common.aml_opcode != AML_VAR_PACKAGE_OP) && + (op->common.parent->common.aml_opcode != AML_NAME_OP))) { + walk_state->result_obj = obj_desc; + } + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_exec_begin_control_op + * + * PARAMETERS: walk_list - The list that owns the walk stack + * Op - The control Op + * + * RETURN: Status + * + * DESCRIPTION: Handles all control ops encountered during control method + * execution. + * + ******************************************************************************/ + +acpi_status +acpi_ds_exec_begin_control_op ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status = AE_OK; + union acpi_generic_state *control_state; + + + ACPI_FUNCTION_NAME ("ds_exec_begin_control_op"); + + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Op=%p Opcode=%2.2X State=%p\n", op, + op->common.aml_opcode, walk_state)); + + switch (op->common.aml_opcode) { + case AML_IF_OP: + case AML_WHILE_OP: + + /* + * IF/WHILE: Create a new control state to manage these + * constructs. We need to manage these as a stack, in order + * to handle nesting. + */ + control_state = acpi_ut_create_control_state (); + if (!control_state) { + status = AE_NO_MEMORY; + break; + } + /* + * Save a pointer to the predicate for multiple executions + * of a loop + */ + control_state->control.aml_predicate_start = walk_state->parser_state.aml - 1; + control_state->control.package_end = walk_state->parser_state.pkg_end; + control_state->control.opcode = op->common.aml_opcode; + + + /* Push the control state on this walk's control stack */ + + acpi_ut_push_generic_state (&walk_state->control_state, control_state); + break; + + case AML_ELSE_OP: + + /* Predicate is in the state object */ + /* If predicate is true, the IF was executed, ignore ELSE part */ + + if (walk_state->last_predicate) { + status = AE_CTRL_TRUE; + } + + break; + + case AML_RETURN_OP: + + break; + + default: + break; + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_exec_end_control_op + * + * PARAMETERS: walk_list - The list that owns the walk stack + * Op - The control Op + * + * RETURN: Status + * + * DESCRIPTION: Handles all control ops encountered during control method + * execution. + * + ******************************************************************************/ + +acpi_status +acpi_ds_exec_end_control_op ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + acpi_status status = AE_OK; + union acpi_generic_state *control_state; + + + ACPI_FUNCTION_NAME ("ds_exec_end_control_op"); + + + switch (op->common.aml_opcode) { + case AML_IF_OP: + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[IF_OP] Op=%p\n", op)); + + /* + * Save the result of the predicate in case there is an + * ELSE to come + */ + walk_state->last_predicate = + (u8) walk_state->control_state->common.value; + + /* + * Pop the control state that was created at the start + * of the IF and free it + */ + control_state = acpi_ut_pop_generic_state (&walk_state->control_state); + acpi_ut_delete_generic_state (control_state); + break; + + + case AML_ELSE_OP: + + break; + + + case AML_WHILE_OP: + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[WHILE_OP] Op=%p\n", op)); + + if (walk_state->control_state->common.value) { + /* Predicate was true, go back and evaluate it again! */ + + status = AE_CTRL_PENDING; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[WHILE_OP] termination! Op=%p\n", op)); + + /* Pop this control state and free it */ + + control_state = acpi_ut_pop_generic_state (&walk_state->control_state); + + walk_state->aml_last_while = control_state->control.aml_predicate_start; + acpi_ut_delete_generic_state (control_state); + break; + + + case AML_RETURN_OP: + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "[RETURN_OP] Op=%p Arg=%p\n",op, op->common.value.arg)); + + /* + * One optional operand -- the return value + * It can be either an immediate operand or a result that + * has been bubbled up the tree + */ + if (op->common.value.arg) { + /* Since we have a real Return(), delete any implicit return */ + + acpi_ds_clear_implicit_return (walk_state); + + /* Return statement has an immediate operand */ + + status = acpi_ds_create_operands (walk_state, op->common.value.arg); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * If value being returned is a Reference (such as + * an arg or local), resolve it now because it may + * cease to exist at the end of the method. + */ + status = acpi_ex_resolve_to_value (&walk_state->operands [0], walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * Get the return value and save as the last result + * value. This is the only place where walk_state->return_desc + * is set to anything other than zero! + */ + walk_state->return_desc = walk_state->operands[0]; + } + else if ((walk_state->results) && + (walk_state->results->results.num_results > 0)) { + /* Since we have a real Return(), delete any implicit return */ + + acpi_ds_clear_implicit_return (walk_state); + + /* + * The return value has come from a previous calculation. + * + * If value being returned is a Reference (such as + * an arg or local), resolve it now because it may + * cease to exist at the end of the method. + * + * Allow references created by the Index operator to return unchanged. + */ + if ((ACPI_GET_DESCRIPTOR_TYPE (walk_state->results->results.obj_desc[0]) == ACPI_DESC_TYPE_OPERAND) && + (ACPI_GET_OBJECT_TYPE (walk_state->results->results.obj_desc [0]) == ACPI_TYPE_LOCAL_REFERENCE) && + ((walk_state->results->results.obj_desc [0])->reference.opcode != AML_INDEX_OP)) { + status = acpi_ex_resolve_to_value (&walk_state->results->results.obj_desc [0], walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + walk_state->return_desc = walk_state->results->results.obj_desc [0]; + } + else { + /* No return operand */ + + if (walk_state->num_operands) { + acpi_ut_remove_reference (walk_state->operands [0]); + } + + walk_state->operands [0] = NULL; + walk_state->num_operands = 0; + walk_state->return_desc = NULL; + } + + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "Completed RETURN_OP State=%p, ret_val=%p\n", + walk_state, walk_state->return_desc)); + + /* End the control method execution right now */ + + status = AE_CTRL_TERMINATE; + break; + + + case AML_NOOP_OP: + + /* Just do nothing! */ + break; + + + case AML_BREAK_POINT_OP: + + /* Call up to the OS service layer to handle this */ + + status = acpi_os_signal (ACPI_SIGNAL_BREAKPOINT, "Executed AML Breakpoint opcode"); + + /* If and when it returns, all done. */ + + break; + + + case AML_BREAK_OP: + case AML_CONTINUE_OP: /* ACPI 2.0 */ + + + /* Pop and delete control states until we find a while */ + + while (walk_state->control_state && + (walk_state->control_state->control.opcode != AML_WHILE_OP)) { + control_state = acpi_ut_pop_generic_state (&walk_state->control_state); + acpi_ut_delete_generic_state (control_state); + } + + /* No while found? */ + + if (!walk_state->control_state) { + return (AE_AML_NO_WHILE); + } + + /* Was: walk_state->aml_last_while = walk_state->control_state->Control.aml_predicate_start; */ + + walk_state->aml_last_while = walk_state->control_state->control.package_end; + + /* Return status depending on opcode */ + + if (op->common.aml_opcode == AML_BREAK_OP) { + status = AE_CTRL_BREAK; + } + else { + status = AE_CTRL_CONTINUE; + } + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown control opcode=%X Op=%p\n", + op->common.aml_opcode, op)); + + status = AE_AML_BAD_OPCODE; + break; + } + + return (status); +} + diff --git a/drivers/acpi/dispatcher/dsutils.c b/drivers/acpi/dispatcher/dsutils.c new file mode 100644 index 00000000000..462c5d83e74 --- /dev/null +++ b/drivers/acpi/dispatcher/dsutils.c @@ -0,0 +1,744 @@ +/******************************************************************************* + * + * Module Name: dsutils - Dispatcher utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> +#include <acpi/acdebug.h> + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dsutils") + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_clear_implicit_return + * + * PARAMETERS: walk_state - Current State + * + * RETURN: None. + * + * DESCRIPTION: Clear and remove a reference on an implicit return value. Used + * to delete "stale" return values (if enabled, the return value + * from every operator is saved at least momentarily, in case the + * parent method exits.) + * + ******************************************************************************/ + +void +acpi_ds_clear_implicit_return ( + struct acpi_walk_state *walk_state) +{ + ACPI_FUNCTION_NAME ("ds_clear_implicit_return"); + + + /* + * Slack must be enabled for this feature + */ + if (!acpi_gbl_enable_interpreter_slack) { + return; + } + + if (walk_state->implicit_return_obj) { + /* + * Delete any "stale" implicit return. However, in + * complex statements, the implicit return value can be + * bubbled up several levels. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "Removing reference on stale implicit return obj %p\n", + walk_state->implicit_return_obj)); + + acpi_ut_remove_reference (walk_state->implicit_return_obj); + walk_state->implicit_return_obj = NULL; + } +} + + +#ifndef ACPI_NO_METHOD_EXECUTION + +/******************************************************************************* + * + * FUNCTION: acpi_ds_do_implicit_return + * + * PARAMETERS: return_desc - The return value + * walk_state - Current State + * add_reference - True if a reference should be added to the + * return object + * + * RETURN: TRUE if implicit return enabled, FALSE otherwise + * + * DESCRIPTION: Implements the optional "implicit return". We save the result + * of every ASL operator and control method invocation in case the + * parent method exit. Before storing a new return value, we + * delete the previous return value. + * + ******************************************************************************/ + +u8 +acpi_ds_do_implicit_return ( + union acpi_operand_object *return_desc, + struct acpi_walk_state *walk_state, + u8 add_reference) +{ + ACPI_FUNCTION_NAME ("ds_do_implicit_return"); + + + /* + * Slack must be enabled for this feature, and we must + * have a valid return object + */ + if ((!acpi_gbl_enable_interpreter_slack) || + (!return_desc)) { + return (FALSE); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "Result %p will be implicitly returned; Prev=%p\n", + return_desc, + walk_state->implicit_return_obj)); + + /* + * Delete any "stale" implicit return value first. However, in + * complex statements, the implicit return value can be + * bubbled up several levels, so we don't clear the value if it + * is the same as the return_desc. + */ + if (walk_state->implicit_return_obj) { + if (walk_state->implicit_return_obj == return_desc) { + return (TRUE); + } + acpi_ds_clear_implicit_return (walk_state); + } + + /* Save the implicit return value, add a reference if requested */ + + walk_state->implicit_return_obj = return_desc; + if (add_reference) { + acpi_ut_add_reference (return_desc); + } + + return (TRUE); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_is_result_used + * + * PARAMETERS: Op - Current Op + * walk_state - Current State + * + * RETURN: TRUE if result is used, FALSE otherwise + * + * DESCRIPTION: Check if a result object will be used by the parent + * + ******************************************************************************/ + +u8 +acpi_ds_is_result_used ( + union acpi_parse_object *op, + struct acpi_walk_state *walk_state) +{ + const struct acpi_opcode_info *parent_info; + + ACPI_FUNCTION_TRACE_PTR ("ds_is_result_used", op); + + + /* Must have both an Op and a Result Object */ + + if (!op) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null Op\n")); + return_VALUE (TRUE); + } + + /* + * We know that this operator is not a + * Return() operator (would not come here.) The following code is the + * optional support for a so-called "implicit return". Some AML code + * assumes that the last value of the method is "implicitly" returned + * to the caller. Just save the last result as the return value. + * NOTE: this is optional because the ASL language does not actually + * support this behavior. + */ + acpi_ds_do_implicit_return (walk_state->result_obj, walk_state, TRUE); + + /* + * Now determine if the parent will use the result + * + * If there is no parent, or the parent is a scope_op, we are executing + * at the method level. An executing method typically has no parent, + * since each method is parsed separately. A method invoked externally + * via execute_control_method has a scope_op as the parent. + */ + if ((!op->common.parent) || + (op->common.parent->common.aml_opcode == AML_SCOPE_OP)) { + /* No parent, the return value cannot possibly be used */ + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "At Method level, result of [%s] not used\n", + acpi_ps_get_opcode_name (op->common.aml_opcode))); + return_VALUE (FALSE); + } + + /* Get info on the parent. The root_op is AML_SCOPE */ + + parent_info = acpi_ps_get_opcode_info (op->common.parent->common.aml_opcode); + if (parent_info->class == AML_CLASS_UNKNOWN) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown parent opcode. Op=%p\n", op)); + return_VALUE (FALSE); + } + + /* + * Decide what to do with the result based on the parent. If + * the parent opcode will not use the result, delete the object. + * Otherwise leave it as is, it will be deleted when it is used + * as an operand later. + */ + switch (parent_info->class) { + case AML_CLASS_CONTROL: + + switch (op->common.parent->common.aml_opcode) { + case AML_RETURN_OP: + + /* Never delete the return value associated with a return opcode */ + + goto result_used; + + case AML_IF_OP: + case AML_WHILE_OP: + + /* + * If we are executing the predicate AND this is the predicate op, + * we will use the return value + */ + if ((walk_state->control_state->common.state == ACPI_CONTROL_PREDICATE_EXECUTING) && + (walk_state->control_state->control.predicate_op == op)) { + goto result_used; + } + break; + + default: + /* Ignore other control opcodes */ + break; + } + + /* The general control opcode returns no result */ + + goto result_not_used; + + + case AML_CLASS_CREATE: + + /* + * These opcodes allow term_arg(s) as operands and therefore + * the operands can be method calls. The result is used. + */ + goto result_used; + + + case AML_CLASS_NAMED_OBJECT: + + if ((op->common.parent->common.aml_opcode == AML_REGION_OP) || + (op->common.parent->common.aml_opcode == AML_DATA_REGION_OP) || + (op->common.parent->common.aml_opcode == AML_PACKAGE_OP) || + (op->common.parent->common.aml_opcode == AML_VAR_PACKAGE_OP) || + (op->common.parent->common.aml_opcode == AML_BUFFER_OP) || + (op->common.parent->common.aml_opcode == AML_INT_EVAL_SUBTREE_OP)) { + /* + * These opcodes allow term_arg(s) as operands and therefore + * the operands can be method calls. The result is used. + */ + goto result_used; + } + + goto result_not_used; + + + default: + + /* + * In all other cases. the parent will actually use the return + * object, so keep it. + */ + goto result_used; + } + + +result_used: + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Result of [%s] used by Parent [%s] Op=%p\n", + acpi_ps_get_opcode_name (op->common.aml_opcode), + acpi_ps_get_opcode_name (op->common.parent->common.aml_opcode), op)); + + return_VALUE (TRUE); + + +result_not_used: + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Result of [%s] not used by Parent [%s] Op=%p\n", + acpi_ps_get_opcode_name (op->common.aml_opcode), + acpi_ps_get_opcode_name (op->common.parent->common.aml_opcode), op)); + + return_VALUE (FALSE); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_delete_result_if_not_used + * + * PARAMETERS: Op - Current parse Op + * result_obj - Result of the operation + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Used after interpretation of an opcode. If there is an internal + * result descriptor, check if the parent opcode will actually use + * this result. If not, delete the result now so that it will + * not become orphaned. + * + ******************************************************************************/ + +void +acpi_ds_delete_result_if_not_used ( + union acpi_parse_object *op, + union acpi_operand_object *result_obj, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ds_delete_result_if_not_used", result_obj); + + + if (!op) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null Op\n")); + return_VOID; + } + + if (!result_obj) { + return_VOID; + } + + if (!acpi_ds_is_result_used (op, walk_state)) { + /* Must pop the result stack (obj_desc should be equal to result_obj) */ + + status = acpi_ds_result_pop (&obj_desc, walk_state); + if (ACPI_SUCCESS (status)) { + acpi_ut_remove_reference (result_obj); + } + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_resolve_operands + * + * PARAMETERS: walk_state - Current walk state with operands on stack + * + * RETURN: Status + * + * DESCRIPTION: Resolve all operands to their values. Used to prepare + * arguments to a control method invocation (a call from one + * method to another.) + * + ******************************************************************************/ + +acpi_status +acpi_ds_resolve_operands ( + struct acpi_walk_state *walk_state) +{ + u32 i; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_PTR ("ds_resolve_operands", walk_state); + + + /* + * Attempt to resolve each of the valid operands + * Method arguments are passed by reference, not by value. This means + * that the actual objects are passed, not copies of the objects. + */ + for (i = 0; i < walk_state->num_operands; i++) { + status = acpi_ex_resolve_to_value (&walk_state->operands[i], walk_state); + if (ACPI_FAILURE (status)) { + break; + } + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_clear_operands + * + * PARAMETERS: walk_state - Current walk state with operands on stack + * + * RETURN: None + * + * DESCRIPTION: Clear all operands on the current walk state operand stack. + * + ******************************************************************************/ + +void +acpi_ds_clear_operands ( + struct acpi_walk_state *walk_state) +{ + u32 i; + + + ACPI_FUNCTION_TRACE_PTR ("ds_clear_operands", walk_state); + + + /* Remove a reference on each operand on the stack */ + + for (i = 0; i < walk_state->num_operands; i++) { + /* + * Remove a reference to all operands, including both + * "Arguments" and "Targets". + */ + acpi_ut_remove_reference (walk_state->operands[i]); + walk_state->operands[i] = NULL; + } + + walk_state->num_operands = 0; + return_VOID; +} +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_operand + * + * PARAMETERS: walk_state - Current walk state + * Arg - Parse object for the argument + * arg_index - Which argument (zero based) + * + * RETURN: Status + * + * DESCRIPTION: Translate a parse tree object that is an argument to an AML + * opcode to the equivalent interpreter object. This may include + * looking up a name or entering a new name into the internal + * namespace. + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_operand ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *arg, + u32 arg_index) +{ + acpi_status status = AE_OK; + char *name_string; + u32 name_length; + union acpi_operand_object *obj_desc; + union acpi_parse_object *parent_op; + u16 opcode; + acpi_interpreter_mode interpreter_mode; + const struct acpi_opcode_info *op_info; + + + ACPI_FUNCTION_TRACE_PTR ("ds_create_operand", arg); + + + /* A valid name must be looked up in the namespace */ + + if ((arg->common.aml_opcode == AML_INT_NAMEPATH_OP) && + (arg->common.value.string)) { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Getting a name: Arg=%p\n", arg)); + + /* Get the entire name string from the AML stream */ + + status = acpi_ex_get_name_string (ACPI_TYPE_ANY, arg->common.value.buffer, + &name_string, &name_length); + + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* All prefixes have been handled, and the name is in name_string */ + + /* + * Special handling for buffer_field declarations. This is a deferred + * opcode that unfortunately defines the field name as the last + * parameter instead of the first. We get here when we are performing + * the deferred execution, so the actual name of the field is already + * in the namespace. We don't want to attempt to look it up again + * because we may be executing in a different scope than where the + * actual opcode exists. + */ + if ((walk_state->deferred_node) && + (walk_state->deferred_node->type == ACPI_TYPE_BUFFER_FIELD) && + (arg_index != 0)) { + obj_desc = ACPI_CAST_PTR (union acpi_operand_object, walk_state->deferred_node); + status = AE_OK; + } + else /* All other opcodes */ { + /* + * Differentiate between a namespace "create" operation + * versus a "lookup" operation (IMODE_LOAD_PASS2 vs. + * IMODE_EXECUTE) in order to support the creation of + * namespace objects during the execution of control methods. + */ + parent_op = arg->common.parent; + op_info = acpi_ps_get_opcode_info (parent_op->common.aml_opcode); + if ((op_info->flags & AML_NSNODE) && + (parent_op->common.aml_opcode != AML_INT_METHODCALL_OP) && + (parent_op->common.aml_opcode != AML_REGION_OP) && + (parent_op->common.aml_opcode != AML_INT_NAMEPATH_OP)) { + /* Enter name into namespace if not found */ + + interpreter_mode = ACPI_IMODE_LOAD_PASS2; + } + else { + /* Return a failure if name not found */ + + interpreter_mode = ACPI_IMODE_EXECUTE; + } + + status = acpi_ns_lookup (walk_state->scope_info, name_string, + ACPI_TYPE_ANY, interpreter_mode, + ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, + walk_state, + ACPI_CAST_INDIRECT_PTR (struct acpi_namespace_node, &obj_desc)); + /* + * The only case where we pass through (ignore) a NOT_FOUND + * error is for the cond_ref_of opcode. + */ + if (status == AE_NOT_FOUND) { + if (parent_op->common.aml_opcode == AML_COND_REF_OF_OP) { + /* + * For the Conditional Reference op, it's OK if + * the name is not found; We just need a way to + * indicate this to the interpreter, set the + * object to the root + */ + obj_desc = ACPI_CAST_PTR (union acpi_operand_object, acpi_gbl_root_node); + status = AE_OK; + } + else { + /* + * We just plain didn't find it -- which is a + * very serious error at this point + */ + status = AE_AML_NAME_NOT_FOUND; + } + } + + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (name_string, status); + } + } + + /* Free the namestring created above */ + + ACPI_MEM_FREE (name_string); + + /* Check status from the lookup */ + + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Put the resulting object onto the current object stack */ + + status = acpi_ds_obj_stack_push (obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + ACPI_DEBUGGER_EXEC (acpi_db_display_argument_object (obj_desc, walk_state)); + } + else { + /* Check for null name case */ + + if (arg->common.aml_opcode == AML_INT_NAMEPATH_OP) { + /* + * If the name is null, this means that this is an + * optional result parameter that was not specified + * in the original ASL. Create a Zero Constant for a + * placeholder. (Store to a constant is a Noop.) + */ + opcode = AML_ZERO_OP; /* Has no arguments! */ + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Null namepath: Arg=%p\n", arg)); + } + else { + opcode = arg->common.aml_opcode; + } + + /* Get the object type of the argument */ + + op_info = acpi_ps_get_opcode_info (opcode); + if (op_info->object_type == ACPI_TYPE_INVALID) { + return_ACPI_STATUS (AE_NOT_IMPLEMENTED); + } + + if (op_info->flags & AML_HAS_RETVAL) { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "Argument previously created, already stacked \n")); + + ACPI_DEBUGGER_EXEC (acpi_db_display_argument_object ( + walk_state->operands [walk_state->num_operands - 1], walk_state)); + + /* + * Use value that was already previously returned + * by the evaluation of this argument + */ + status = acpi_ds_result_pop_from_bottom (&obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + /* + * Only error is underflow, and this indicates + * a missing or null operand! + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Missing or null operand, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + } + else { + /* Create an ACPI_INTERNAL_OBJECT for the argument */ + + obj_desc = acpi_ut_create_internal_object (op_info->object_type); + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Initialize the new object */ + + status = acpi_ds_init_object_from_op (walk_state, arg, + opcode, &obj_desc); + if (ACPI_FAILURE (status)) { + acpi_ut_delete_object_desc (obj_desc); + return_ACPI_STATUS (status); + } + } + + /* Put the operand object on the object stack */ + + status = acpi_ds_obj_stack_push (obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + ACPI_DEBUGGER_EXEC (acpi_db_display_argument_object (obj_desc, walk_state)); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_operands + * + * PARAMETERS: walk_state - Current state + * first_arg - First argument of a parser argument tree + * + * RETURN: Status + * + * DESCRIPTION: Convert an operator's arguments from a parse tree format to + * namespace objects and place those argument object on the object + * stack in preparation for evaluation by the interpreter. + * + ******************************************************************************/ + +acpi_status +acpi_ds_create_operands ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *first_arg) +{ + acpi_status status = AE_OK; + union acpi_parse_object *arg; + u32 arg_count = 0; + + + ACPI_FUNCTION_TRACE_PTR ("ds_create_operands", first_arg); + + + /* For all arguments in the list... */ + + arg = first_arg; + while (arg) { + status = acpi_ds_create_operand (walk_state, arg, arg_count); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Arg #%d (%p) done, Arg1=%p\n", + arg_count, arg, first_arg)); + + /* Move on to next argument, if any */ + + arg = arg->common.next; + arg_count++; + } + + return_ACPI_STATUS (status); + + +cleanup: + /* + * We must undo everything done above; meaning that we must + * pop everything off of the operand stack and delete those + * objects + */ + (void) acpi_ds_obj_stack_pop_and_delete (arg_count, walk_state); + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "While creating Arg %d - %s\n", + (arg_count + 1), acpi_format_exception (status))); + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/dispatcher/dswexec.c b/drivers/acpi/dispatcher/dswexec.c new file mode 100644 index 00000000000..2071a0d2bbb --- /dev/null +++ b/drivers/acpi/dispatcher/dswexec.c @@ -0,0 +1,751 @@ +/****************************************************************************** + * + * Module Name: dswexec - Dispatcher method execution callbacks; + * dispatch to interpreter. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> +#include <acpi/acdebug.h> +#include <acpi/acdisasm.h> + + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dswexec") + +/* + * Dispatch table for opcode classes + */ +static ACPI_EXECUTE_OP acpi_gbl_op_type_dispatch [] = { + acpi_ex_opcode_0A_0T_1R, + acpi_ex_opcode_1A_0T_0R, + acpi_ex_opcode_1A_0T_1R, + acpi_ex_opcode_1A_1T_0R, + acpi_ex_opcode_1A_1T_1R, + acpi_ex_opcode_2A_0T_0R, + acpi_ex_opcode_2A_0T_1R, + acpi_ex_opcode_2A_1T_1R, + acpi_ex_opcode_2A_2T_1R, + acpi_ex_opcode_3A_0T_0R, + acpi_ex_opcode_3A_1T_1R, + acpi_ex_opcode_6A_0T_1R}; + +/***************************************************************************** + * + * FUNCTION: acpi_ds_get_predicate_value + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * + * RETURN: Status + * + * DESCRIPTION: Get the result of a predicate evaluation + * + ****************************************************************************/ + +acpi_status +acpi_ds_get_predicate_value ( + struct acpi_walk_state *walk_state, + union acpi_operand_object *result_obj) { + acpi_status status = AE_OK; + union acpi_operand_object *obj_desc; + union acpi_operand_object *local_obj_desc = NULL; + + + ACPI_FUNCTION_TRACE_PTR ("ds_get_predicate_value", walk_state); + + + walk_state->control_state->common.state = 0; + + if (result_obj) { + status = acpi_ds_result_pop (&obj_desc, walk_state); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not get result from predicate evaluation, %s\n", + acpi_format_exception (status))); + + return_ACPI_STATUS (status); + } + } + else { + status = acpi_ds_create_operand (walk_state, walk_state->op, 0); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_ex_resolve_to_value (&walk_state->operands [0], walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + obj_desc = walk_state->operands [0]; + } + + if (!obj_desc) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No predicate obj_desc=%p State=%p\n", + obj_desc, walk_state)); + + return_ACPI_STATUS (AE_AML_NO_OPERAND); + } + + /* + * Result of predicate evaluation must be an Integer + * object. Implicitly convert the argument if necessary. + */ + status = acpi_ex_convert_to_integer (obj_desc, &local_obj_desc, 16); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + if (ACPI_GET_OBJECT_TYPE (local_obj_desc) != ACPI_TYPE_INTEGER) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Bad predicate (not an integer) obj_desc=%p State=%p Type=%X\n", + obj_desc, walk_state, ACPI_GET_OBJECT_TYPE (obj_desc))); + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* Truncate the predicate to 32-bits if necessary */ + + acpi_ex_truncate_for32bit_table (local_obj_desc); + + /* + * Save the result of the predicate evaluation on + * the control stack + */ + if (local_obj_desc->integer.value) { + walk_state->control_state->common.value = TRUE; + } + else { + /* + * Predicate is FALSE, we will just toss the + * rest of the package + */ + walk_state->control_state->common.value = FALSE; + status = AE_CTRL_FALSE; + } + + +cleanup: + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Completed a predicate eval=%X Op=%p\n", + walk_state->control_state->common.value, walk_state->op)); + + /* Break to debugger to display result */ + + ACPI_DEBUGGER_EXEC (acpi_db_display_result_object (local_obj_desc, walk_state)); + + /* + * Delete the predicate result object (we know that + * we don't need it anymore) + */ + if (local_obj_desc != obj_desc) { + acpi_ut_remove_reference (local_obj_desc); + } + acpi_ut_remove_reference (obj_desc); + + walk_state->control_state->common.state = ACPI_CONTROL_NORMAL; + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_exec_begin_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * out_op - Return op if a new one is created + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the execution of control + * methods. This is where most operators and operands are + * dispatched to the interpreter. + * + ****************************************************************************/ + +acpi_status +acpi_ds_exec_begin_op ( + struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op) +{ + union acpi_parse_object *op; + acpi_status status = AE_OK; + u32 opcode_class; + + + ACPI_FUNCTION_TRACE_PTR ("ds_exec_begin_op", walk_state); + + + op = walk_state->op; + if (!op) { + status = acpi_ds_load2_begin_op (walk_state, out_op); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + op = *out_op; + walk_state->op = op; + walk_state->opcode = op->common.aml_opcode; + walk_state->op_info = acpi_ps_get_opcode_info (op->common.aml_opcode); + + if (acpi_ns_opens_scope (walk_state->op_info->object_type)) { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "(%s) Popping scope for Op %p\n", + acpi_ut_get_type_name (walk_state->op_info->object_type), op)); + + status = acpi_ds_scope_stack_pop (walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + } + + if (op == walk_state->origin) { + if (out_op) { + *out_op = op; + } + + return_ACPI_STATUS (AE_OK); + } + + /* + * If the previous opcode was a conditional, this opcode + * must be the beginning of the associated predicate. + * Save this knowledge in the current scope descriptor + */ + if ((walk_state->control_state) && + (walk_state->control_state->common.state == + ACPI_CONTROL_CONDITIONAL_EXECUTING)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Exec predicate Op=%p State=%p\n", + op, walk_state)); + + walk_state->control_state->common.state = ACPI_CONTROL_PREDICATE_EXECUTING; + + /* Save start of predicate */ + + walk_state->control_state->control.predicate_op = op; + } + + + opcode_class = walk_state->op_info->class; + + /* We want to send namepaths to the load code */ + + if (op->common.aml_opcode == AML_INT_NAMEPATH_OP) { + opcode_class = AML_CLASS_NAMED_OBJECT; + } + + /* + * Handle the opcode based upon the opcode type + */ + switch (opcode_class) { + case AML_CLASS_CONTROL: + + status = acpi_ds_result_stack_push (walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_ds_exec_begin_control_op (walk_state, op); + break; + + + case AML_CLASS_NAMED_OBJECT: + + if (walk_state->walk_type == ACPI_WALK_METHOD) { + /* + * Found a named object declaration during method + * execution; we must enter this object into the + * namespace. The created object is temporary and + * will be deleted upon completion of the execution + * of this method. + */ + status = acpi_ds_load2_begin_op (walk_state, NULL); + } + + if (op->common.aml_opcode == AML_REGION_OP) { + status = acpi_ds_result_stack_push (walk_state); + } + break; + + + case AML_CLASS_EXECUTE: + case AML_CLASS_CREATE: + + /* + * Most operators with arguments. + * Start a new result/operand state + */ + status = acpi_ds_result_stack_push (walk_state); + break; + + + default: + break; + } + + /* Nothing to do here during method execution */ + + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ds_exec_end_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * Op - Op that has been just been completed in the + * walk; Arguments have now been evaluated. + * + * RETURN: Status + * + * DESCRIPTION: Ascending callback used during the execution of control + * methods. The only thing we really need to do here is to + * notice the beginning of IF, ELSE, and WHILE blocks. + * + ****************************************************************************/ + +acpi_status +acpi_ds_exec_end_op ( + struct acpi_walk_state *walk_state) +{ + union acpi_parse_object *op; + acpi_status status = AE_OK; + u32 op_type; + u32 op_class; + union acpi_parse_object *next_op; + union acpi_parse_object *first_arg; + + + ACPI_FUNCTION_TRACE_PTR ("ds_exec_end_op", walk_state); + + + op = walk_state->op; + op_type = walk_state->op_info->type; + op_class = walk_state->op_info->class; + + if (op_class == AML_CLASS_UNKNOWN) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown opcode %X\n", op->common.aml_opcode)); + return_ACPI_STATUS (AE_NOT_IMPLEMENTED); + } + + first_arg = op->common.value.arg; + + /* Init the walk state */ + + walk_state->num_operands = 0; + walk_state->return_desc = NULL; + walk_state->result_obj = NULL; + + /* Call debugger for single step support (DEBUG build only) */ + + ACPI_DEBUGGER_EXEC (status = acpi_db_single_step (walk_state, op, op_class)); + ACPI_DEBUGGER_EXEC (if (ACPI_FAILURE (status)) {return_ACPI_STATUS (status);}); + + /* Decode the Opcode Class */ + + switch (op_class) { + case AML_CLASS_ARGUMENT: /* constants, literals, etc. -- do nothing */ + break; + + + case AML_CLASS_EXECUTE: /* most operators with arguments */ + + /* Build resolved operand stack */ + + status = acpi_ds_create_operands (walk_state, first_arg); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Done with this result state (Now that operand stack is built) */ + + status = acpi_ds_result_stack_pop (walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* + * All opcodes require operand resolution, with the only exceptions + * being the object_type and size_of operators. + */ + if (!(walk_state->op_info->flags & AML_NO_OPERAND_RESOLVE)) { + /* Resolve all operands */ + + status = acpi_ex_resolve_operands (walk_state->opcode, + &(walk_state->operands [walk_state->num_operands -1]), + walk_state); + if (ACPI_SUCCESS (status)) { + ACPI_DUMP_OPERANDS (ACPI_WALK_OPERANDS, ACPI_IMODE_EXECUTE, + acpi_ps_get_opcode_name (walk_state->opcode), + walk_state->num_operands, "after ex_resolve_operands"); + } + } + + if (ACPI_SUCCESS (status)) { + /* + * Dispatch the request to the appropriate interpreter handler + * routine. There is one routine per opcode "type" based upon the + * number of opcode arguments and return type. + */ + status = acpi_gbl_op_type_dispatch[op_type] (walk_state); + } + else { + /* + * Treat constructs of the form "Store(local_x,local_x)" as noops when the + * Local is uninitialized. + */ + if ((status == AE_AML_UNINITIALIZED_LOCAL) && + (walk_state->opcode == AML_STORE_OP) && + (walk_state->operands[0]->common.type == ACPI_TYPE_LOCAL_REFERENCE) && + (walk_state->operands[1]->common.type == ACPI_TYPE_LOCAL_REFERENCE) && + (walk_state->operands[0]->reference.opcode == + walk_state->operands[1]->reference.opcode) && + (walk_state->operands[0]->reference.offset == + walk_state->operands[1]->reference.offset)) { + status = AE_OK; + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "[%s]: Could not resolve operands, %s\n", + acpi_ps_get_opcode_name (walk_state->opcode), + acpi_format_exception (status))); + } + } + + /* Always delete the argument objects and clear the operand stack */ + + acpi_ds_clear_operands (walk_state); + + /* + * If a result object was returned from above, push it on the + * current result stack + */ + if (ACPI_SUCCESS (status) && + walk_state->result_obj) { + status = acpi_ds_result_push (walk_state->result_obj, walk_state); + } + + break; + + + default: + + switch (op_type) { + case AML_TYPE_CONTROL: /* Type 1 opcode, IF/ELSE/WHILE/NOOP */ + + /* 1 Operand, 0 external_result, 0 internal_result */ + + status = acpi_ds_exec_end_control_op (walk_state, op); + + /* Make sure to properly pop the result stack */ + + if (ACPI_SUCCESS (status)) { + status = acpi_ds_result_stack_pop (walk_state); + } + else if (status == AE_CTRL_PENDING) { + status = acpi_ds_result_stack_pop (walk_state); + if (ACPI_SUCCESS (status)) { + status = AE_CTRL_PENDING; + } + } + break; + + + case AML_TYPE_METHOD_CALL: + + /* + * If the method is referenced from within a package + * declaration, it is not a invocation of the method, just + * a reference to it. + */ + if ((op->asl.parent) && + ((op->asl.parent->asl.aml_opcode == AML_PACKAGE_OP) || + (op->asl.parent->asl.aml_opcode == AML_VAR_PACKAGE_OP))) { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Method Reference in a Package, Op=%p\n", op)); + op->common.node = (struct acpi_namespace_node *) op->asl.value.arg->asl.node->object; + acpi_ut_add_reference (op->asl.value.arg->asl.node->object); + return_ACPI_STATUS (AE_OK); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Method invocation, Op=%p\n", op)); + + /* + * (AML_METHODCALL) Op->Asl.Value.Arg->Asl.Node contains + * the method Node pointer + */ + /* next_op points to the op that holds the method name */ + + next_op = first_arg; + + /* next_op points to first argument op */ + + next_op = next_op->common.next; + + /* + * Get the method's arguments and put them on the operand stack + */ + status = acpi_ds_create_operands (walk_state, next_op); + if (ACPI_FAILURE (status)) { + break; + } + + /* + * Since the operands will be passed to another control method, + * we must resolve all local references here (Local variables, + * arguments to *this* method, etc.) + */ + status = acpi_ds_resolve_operands (walk_state); + if (ACPI_FAILURE (status)) { + /* On error, clear all resolved operands */ + + acpi_ds_clear_operands (walk_state); + break; + } + + /* + * Tell the walk loop to preempt this running method and + * execute the new method + */ + status = AE_CTRL_TRANSFER; + + /* + * Return now; we don't want to disturb anything, + * especially the operand count! + */ + return_ACPI_STATUS (status); + + + case AML_TYPE_CREATE_FIELD: + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Executing create_field Buffer/Index Op=%p\n", op)); + + status = acpi_ds_load2_end_op (walk_state); + if (ACPI_FAILURE (status)) { + break; + } + + status = acpi_ds_eval_buffer_field_operands (walk_state, op); + break; + + + case AML_TYPE_CREATE_OBJECT: + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Executing create_object (Buffer/Package) Op=%p\n", op)); + + switch (op->common.parent->common.aml_opcode) { + case AML_NAME_OP: + + /* + * Put the Node on the object stack (Contains the ACPI Name of + * this object) + */ + walk_state->operands[0] = (void *) op->common.parent->common.node; + walk_state->num_operands = 1; + + status = acpi_ds_create_node (walk_state, op->common.parent->common.node, op->common.parent); + if (ACPI_FAILURE (status)) { + break; + } + + /* Fall through */ + /*lint -fallthrough */ + + case AML_INT_EVAL_SUBTREE_OP: + + status = acpi_ds_eval_data_object_operands (walk_state, op, + acpi_ns_get_attached_object (op->common.parent->common.node)); + break; + + default: + + status = acpi_ds_eval_data_object_operands (walk_state, op, NULL); + break; + } + + /* Done with this result state (Now that operand stack is built) */ + + status = acpi_ds_result_stack_pop (walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* + * If a result object was returned from above, push it on the + * current result stack + */ + if (ACPI_SUCCESS (status) && + walk_state->result_obj) { + status = acpi_ds_result_push (walk_state->result_obj, walk_state); + } + break; + + + case AML_TYPE_NAMED_FIELD: + case AML_TYPE_NAMED_COMPLEX: + case AML_TYPE_NAMED_SIMPLE: + case AML_TYPE_NAMED_NO_OBJ: + + status = acpi_ds_load2_end_op (walk_state); + if (ACPI_FAILURE (status)) { + break; + } + + if (op->common.aml_opcode == AML_REGION_OP) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Executing op_region Address/Length Op=%p\n", op)); + + status = acpi_ds_eval_region_operands (walk_state, op); + if (ACPI_FAILURE (status)) { + break; + } + + status = acpi_ds_result_stack_pop (walk_state); + } + + break; + + + case AML_TYPE_UNDEFINED: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Undefined opcode type Op=%p\n", op)); + return_ACPI_STATUS (AE_NOT_IMPLEMENTED); + + + case AML_TYPE_BOGUS: + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "Internal opcode=%X type Op=%p\n", + walk_state->opcode, op)); + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unimplemented opcode, class=%X type=%X Opcode=%X Op=%p\n", + op_class, op_type, op->common.aml_opcode, op)); + + status = AE_NOT_IMPLEMENTED; + break; + } + } + + /* + * ACPI 2.0 support for 64-bit integers: Truncate numeric + * result value if we are executing from a 32-bit ACPI table + */ + acpi_ex_truncate_for32bit_table (walk_state->result_obj); + + /* + * Check if we just completed the evaluation of a + * conditional predicate + */ + + if ((ACPI_SUCCESS (status)) && + (walk_state->control_state) && + (walk_state->control_state->common.state == + ACPI_CONTROL_PREDICATE_EXECUTING) && + (walk_state->control_state->control.predicate_op == op)) { + status = acpi_ds_get_predicate_value (walk_state, walk_state->result_obj); + walk_state->result_obj = NULL; + } + + +cleanup: + + /* Invoke exception handler on error */ + + if (ACPI_FAILURE (status) && + acpi_gbl_exception_handler && + !(status & AE_CODE_CONTROL)) { + acpi_ex_exit_interpreter (); + status = acpi_gbl_exception_handler (status, + walk_state->method_node->name.integer, walk_state->opcode, + walk_state->aml_offset, NULL); + acpi_ex_enter_interpreter (); + } + + if (walk_state->result_obj) { + /* Break to debugger to display result */ + + ACPI_DEBUGGER_EXEC (acpi_db_display_result_object (walk_state->result_obj, walk_state)); + + /* + * Delete the result op if and only if: + * Parent will not use the result -- such as any + * non-nested type2 op in a method (parent will be method) + */ + acpi_ds_delete_result_if_not_used (op, walk_state->result_obj, walk_state); + } + +#ifdef _UNDER_DEVELOPMENT + + if (walk_state->parser_state.aml == walk_state->parser_state.aml_end) { + acpi_db_method_end (walk_state); + } +#endif + + /* Always clear the object stack */ + + walk_state->num_operands = 0; + +#ifdef ACPI_DISASSEMBLER + + /* On error, display method locals/args */ + + if (ACPI_FAILURE (status)) { + acpi_dm_dump_method_info (status, walk_state, op); + } +#endif + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/dispatcher/dswload.c b/drivers/acpi/dispatcher/dswload.c new file mode 100644 index 00000000000..06d75867958 --- /dev/null +++ b/drivers/acpi/dispatcher/dswload.c @@ -0,0 +1,976 @@ +/****************************************************************************** + * + * Module Name: dswload - Dispatcher namespace load callbacks + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> +#include <acpi/acevents.h> + +#ifdef _ACPI_ASL_COMPILER +#include <acpi/acdisasm.h> +#endif + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dswload") + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_callbacks + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * pass_number - 1, 2, or 3 + * + * RETURN: Status + * + * DESCRIPTION: Init walk state callbacks + * + ******************************************************************************/ + +acpi_status +acpi_ds_init_callbacks ( + struct acpi_walk_state *walk_state, + u32 pass_number) +{ + + switch (pass_number) { + case 1: + walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 | ACPI_PARSE_DELETE_TREE; + walk_state->descending_callback = acpi_ds_load1_begin_op; + walk_state->ascending_callback = acpi_ds_load1_end_op; + break; + + case 2: + walk_state->parse_flags = ACPI_PARSE_LOAD_PASS1 | ACPI_PARSE_DELETE_TREE; + walk_state->descending_callback = acpi_ds_load2_begin_op; + walk_state->ascending_callback = acpi_ds_load2_end_op; + break; + + case 3: +#ifndef ACPI_NO_METHOD_EXECUTION + walk_state->parse_flags |= ACPI_PARSE_EXECUTE | ACPI_PARSE_DELETE_TREE; + walk_state->descending_callback = acpi_ds_exec_begin_op; + walk_state->ascending_callback = acpi_ds_exec_end_op; +#endif + break; + + default: + return (AE_BAD_PARAMETER); + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_load1_begin_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * Op - Op that has been just been reached in the + * walk; Arguments have not been evaluated yet. + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the loading of ACPI tables. + * + ******************************************************************************/ + +acpi_status +acpi_ds_load1_begin_op ( + struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op) +{ + union acpi_parse_object *op; + struct acpi_namespace_node *node; + acpi_status status; + acpi_object_type object_type; + char *path; + u32 flags; + + + ACPI_FUNCTION_NAME ("ds_load1_begin_op"); + + + op = walk_state->op; + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op, walk_state)); + + /* We are only interested in opcodes that have an associated name */ + + if (op) { + if (!(walk_state->op_info->flags & AML_NAMED)) { +#if 0 + if ((walk_state->op_info->class == AML_CLASS_EXECUTE) || + (walk_state->op_info->class == AML_CLASS_CONTROL)) { + acpi_os_printf ("\n\n***EXECUTABLE OPCODE %s***\n\n", walk_state->op_info->name); + *out_op = op; + return (AE_CTRL_SKIP); + } +#endif + *out_op = op; + return (AE_OK); + } + + /* Check if this object has already been installed in the namespace */ + + if (op->common.node) { + *out_op = op; + return (AE_OK); + } + } + + path = acpi_ps_get_next_namestring (&walk_state->parser_state); + + /* Map the raw opcode into an internal object type */ + + object_type = walk_state->op_info->object_type; + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "State=%p Op=%p [%s]\n", walk_state, op, acpi_ut_get_type_name (object_type))); + + switch (walk_state->opcode) { + case AML_SCOPE_OP: + + /* + * The target name of the Scope() operator must exist at this point so + * that we can actually open the scope to enter new names underneath it. + * Allow search-to-root for single namesegs. + */ + status = acpi_ns_lookup (walk_state->scope_info, path, object_type, + ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, walk_state, &(node)); +#ifdef _ACPI_ASL_COMPILER + if (status == AE_NOT_FOUND) { + /* + * Table disassembly: + * Target of Scope() not found. Generate an External for it, and + * insert the name into the namespace. + */ + acpi_dm_add_to_external_list (path); + status = acpi_ns_lookup (walk_state->scope_info, path, object_type, + ACPI_IMODE_LOAD_PASS1, ACPI_NS_SEARCH_PARENT, walk_state, &(node)); + } +#endif + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (path, status); + return (status); + } + + /* + * Check to make sure that the target is + * one of the opcodes that actually opens a scope + */ + switch (node->type) { + case ACPI_TYPE_LOCAL_SCOPE: /* Scope */ + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + + /* These are acceptable types */ + break; + + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* + * These types we will allow, but we will change the type. This + * enables some existing code of the form: + * + * Name (DEB, 0) + * Scope (DEB) { ... } + * + * Note: silently change the type here. On the second pass, we will report a warning + */ + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Type override - [%4.4s] had invalid type (%s) for Scope operator, changed to (Scope)\n", + path, acpi_ut_get_type_name (node->type))); + + node->type = ACPI_TYPE_ANY; + walk_state->scope_info->common.value = ACPI_TYPE_ANY; + break; + + default: + + /* All other types are an error */ + + ACPI_REPORT_ERROR (("Invalid type (%s) for target of Scope operator [%4.4s] (Cannot override)\n", + acpi_ut_get_type_name (node->type), path)); + + return (AE_AML_OPERAND_TYPE); + } + break; + + + default: + + /* + * For all other named opcodes, we will enter the name into the namespace. + * + * Setup the search flags. + * Since we are entering a name into the namespace, we do not want to + * enable the search-to-root upsearch. + * + * There are only two conditions where it is acceptable that the name + * already exists: + * 1) the Scope() operator can reopen a scoping object that was + * previously defined (Scope, Method, Device, etc.) + * 2) Whenever we are parsing a deferred opcode (op_region, Buffer, + * buffer_field, or Package), the name of the object is already + * in the namespace. + */ + if (walk_state->deferred_node) { + /* This name is already in the namespace, get the node */ + + node = walk_state->deferred_node; + status = AE_OK; + break; + } + + flags = ACPI_NS_NO_UPSEARCH; + if ((walk_state->opcode != AML_SCOPE_OP) && + (!(walk_state->parse_flags & ACPI_PARSE_DEFERRED_OP))) { + flags |= ACPI_NS_ERROR_IF_FOUND; + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[%s] Cannot already exist\n", + acpi_ut_get_type_name (object_type))); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "[%s] Both Find or Create allowed\n", + acpi_ut_get_type_name (object_type))); + } + + /* + * Enter the named type into the internal namespace. We enter the name + * as we go downward in the parse tree. Any necessary subobjects that involve + * arguments to the opcode must be created as we go back up the parse tree later. + */ + status = acpi_ns_lookup (walk_state->scope_info, path, object_type, + ACPI_IMODE_LOAD_PASS1, flags, walk_state, &(node)); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (path, status); + return (status); + } + break; + } + + + /* Common exit */ + + if (!op) { + /* Create a new op */ + + op = acpi_ps_alloc_op (walk_state->opcode); + if (!op) { + return (AE_NO_MEMORY); + } + } + + /* Initialize */ + + op->named.name = node->name.integer; + +#if (defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY)) + op->named.path = (u8 *) path; +#endif + + + /* + * Put the Node in the "op" object that the parser uses, so we + * can get it again quickly when this scope is closed + */ + op->common.node = node; + acpi_ps_append_arg (acpi_ps_get_parent_scope (&walk_state->parser_state), op); + + *out_op = op; + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_load1_end_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * Op - Op that has been just been completed in the + * walk; Arguments have now been evaluated. + * + * RETURN: Status + * + * DESCRIPTION: Ascending callback used during the loading of the namespace, + * both control methods and everything else. + * + ******************************************************************************/ + +acpi_status +acpi_ds_load1_end_op ( + struct acpi_walk_state *walk_state) +{ + union acpi_parse_object *op; + acpi_object_type object_type; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_NAME ("ds_load1_end_op"); + + + op = walk_state->op; + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op, walk_state)); + + /* We are only interested in opcodes that have an associated name */ + + if (!(walk_state->op_info->flags & (AML_NAMED | AML_FIELD))) { + return (AE_OK); + } + + /* Get the object type to determine if we should pop the scope */ + + object_type = walk_state->op_info->object_type; + +#ifndef ACPI_NO_METHOD_EXECUTION + if (walk_state->op_info->flags & AML_FIELD) { + if (walk_state->opcode == AML_FIELD_OP || + walk_state->opcode == AML_BANK_FIELD_OP || + walk_state->opcode == AML_INDEX_FIELD_OP) { + status = acpi_ds_init_field_objects (op, walk_state); + } + return (status); + } + + + if (op->common.aml_opcode == AML_REGION_OP) { + status = acpi_ex_create_region (op->named.data, op->named.length, + (acpi_adr_space_type) ((op->common.value.arg)->common.value.integer), walk_state); + if (ACPI_FAILURE (status)) { + return (status); + } + } +#endif + + if (op->common.aml_opcode == AML_NAME_OP) { + /* For Name opcode, get the object type from the argument */ + + if (op->common.value.arg) { + object_type = (acpi_ps_get_opcode_info ((op->common.value.arg)->common.aml_opcode))->object_type; + op->common.node->type = (u8) object_type; + } + } + + if (op->common.aml_opcode == AML_METHOD_OP) { + /* + * method_op pkg_length name_string method_flags term_list + * + * Note: We must create the method node/object pair as soon as we + * see the method declaration. This allows later pass1 parsing + * of invocations of the method (need to know the number of + * arguments.) + */ + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "LOADING-Method: State=%p Op=%p named_obj=%p\n", + walk_state, op, op->named.node)); + + if (!acpi_ns_get_attached_object (op->named.node)) { + walk_state->operands[0] = (void *) op->named.node; + walk_state->num_operands = 1; + + status = acpi_ds_create_operands (walk_state, op->common.value.arg); + if (ACPI_SUCCESS (status)) { + status = acpi_ex_create_method (op->named.data, + op->named.length, walk_state); + } + walk_state->operands[0] = NULL; + walk_state->num_operands = 0; + + if (ACPI_FAILURE (status)) { + return (status); + } + } + } + + /* Pop the scope stack */ + + if (acpi_ns_opens_scope (object_type)) { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "(%s): Popping scope for Op %p\n", + acpi_ut_get_type_name (object_type), op)); + + status = acpi_ds_scope_stack_pop (walk_state); + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_load2_begin_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * Op - Op that has been just been reached in the + * walk; Arguments have not been evaluated yet. + * + * RETURN: Status + * + * DESCRIPTION: Descending callback used during the loading of ACPI tables. + * + ******************************************************************************/ + +acpi_status +acpi_ds_load2_begin_op ( + struct acpi_walk_state *walk_state, + union acpi_parse_object **out_op) +{ + union acpi_parse_object *op; + struct acpi_namespace_node *node; + acpi_status status; + acpi_object_type object_type; + char *buffer_ptr; + + + ACPI_FUNCTION_TRACE ("ds_load2_begin_op"); + + + op = walk_state->op; + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Op=%p State=%p\n", op, walk_state)); + + if (op) { + /* We only care about Namespace opcodes here */ + + if ((!(walk_state->op_info->flags & AML_NSOPCODE) && (walk_state->opcode != AML_INT_NAMEPATH_OP)) || + (!(walk_state->op_info->flags & AML_NAMED))) { + return_ACPI_STATUS (AE_OK); + } + + /* + * Get the name we are going to enter or lookup in the namespace + */ + if (walk_state->opcode == AML_INT_NAMEPATH_OP) { + /* For Namepath op, get the path string */ + + buffer_ptr = op->common.value.string; + if (!buffer_ptr) { + /* No name, just exit */ + + return_ACPI_STATUS (AE_OK); + } + } + else { + /* Get name from the op */ + + buffer_ptr = (char *) &op->named.name; + } + } + else { + /* Get the namestring from the raw AML */ + + buffer_ptr = acpi_ps_get_next_namestring (&walk_state->parser_state); + } + + /* Map the opcode into an internal object type */ + + object_type = walk_state->op_info->object_type; + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "State=%p Op=%p Type=%X\n", walk_state, op, object_type)); + + + switch (walk_state->opcode) { + case AML_FIELD_OP: + case AML_BANK_FIELD_OP: + case AML_INDEX_FIELD_OP: + + node = NULL; + status = AE_OK; + break; + + case AML_INT_NAMEPATH_OP: + + /* + * The name_path is an object reference to an existing object. Don't enter the + * name into the namespace, but look it up for use later + */ + status = acpi_ns_lookup (walk_state->scope_info, buffer_ptr, object_type, + ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, walk_state, &(node)); + break; + + case AML_SCOPE_OP: + + /* + * The Path is an object reference to an existing object. Don't enter the + * name into the namespace, but look it up for use later + */ + status = acpi_ns_lookup (walk_state->scope_info, buffer_ptr, object_type, + ACPI_IMODE_EXECUTE, ACPI_NS_SEARCH_PARENT, walk_state, &(node)); + if (ACPI_FAILURE (status)) { +#ifdef _ACPI_ASL_COMPILER + if (status == AE_NOT_FOUND) { + status = AE_OK; + } + else { + ACPI_REPORT_NSERROR (buffer_ptr, status); + } +#else + ACPI_REPORT_NSERROR (buffer_ptr, status); +#endif + return_ACPI_STATUS (status); + } + /* + * We must check to make sure that the target is + * one of the opcodes that actually opens a scope + */ + switch (node->type) { + case ACPI_TYPE_LOCAL_SCOPE: /* Scope */ + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + + /* These are acceptable types */ + break; + + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* + * These types we will allow, but we will change the type. This + * enables some existing code of the form: + * + * Name (DEB, 0) + * Scope (DEB) { ... } + */ + + ACPI_REPORT_WARNING (("Type override - [%4.4s] had invalid type (%s) for Scope operator, changed to (Scope)\n", + buffer_ptr, acpi_ut_get_type_name (node->type))); + + node->type = ACPI_TYPE_ANY; + walk_state->scope_info->common.value = ACPI_TYPE_ANY; + break; + + default: + + /* All other types are an error */ + + ACPI_REPORT_ERROR (("Invalid type (%s) for target of Scope operator [%4.4s]\n", + acpi_ut_get_type_name (node->type), buffer_ptr)); + + return (AE_AML_OPERAND_TYPE); + } + break; + + default: + + /* All other opcodes */ + + if (op && op->common.node) { + /* This op/node was previously entered into the namespace */ + + node = op->common.node; + + if (acpi_ns_opens_scope (object_type)) { + status = acpi_ds_scope_stack_push (node, object_type, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + } + return_ACPI_STATUS (AE_OK); + } + + /* + * Enter the named type into the internal namespace. We enter the name + * as we go downward in the parse tree. Any necessary subobjects that involve + * arguments to the opcode must be created as we go back up the parse tree later. + * + * Note: Name may already exist if we are executing a deferred opcode. + */ + if (walk_state->deferred_node) { + /* This name is already in the namespace, get the node */ + + node = walk_state->deferred_node; + status = AE_OK; + break; + } + + status = acpi_ns_lookup (walk_state->scope_info, buffer_ptr, object_type, + ACPI_IMODE_EXECUTE, ACPI_NS_NO_UPSEARCH, walk_state, &(node)); + break; + } + + if (ACPI_FAILURE (status)) { + ACPI_REPORT_NSERROR (buffer_ptr, status); + return_ACPI_STATUS (status); + } + + + if (!op) { + /* Create a new op */ + + op = acpi_ps_alloc_op (walk_state->opcode); + if (!op) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Initialize the new op */ + + if (node) { + op->named.name = node->name.integer; + } + if (out_op) { + *out_op = op; + } + } + + /* + * Put the Node in the "op" object that the parser uses, so we + * can get it again quickly when this scope is closed + */ + op->common.node = node; + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_load2_end_op + * + * PARAMETERS: walk_state - Current state of the parse tree walk + * Op - Op that has been just been completed in the + * walk; Arguments have now been evaluated. + * + * RETURN: Status + * + * DESCRIPTION: Ascending callback used during the loading of the namespace, + * both control methods and everything else. + * + ******************************************************************************/ + +acpi_status +acpi_ds_load2_end_op ( + struct acpi_walk_state *walk_state) +{ + union acpi_parse_object *op; + acpi_status status = AE_OK; + acpi_object_type object_type; + struct acpi_namespace_node *node; + union acpi_parse_object *arg; + struct acpi_namespace_node *new_node; +#ifndef ACPI_NO_METHOD_EXECUTION + u32 i; +#endif + + + ACPI_FUNCTION_TRACE ("ds_load2_end_op"); + + op = walk_state->op; + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Opcode [%s] Op %p State %p\n", + walk_state->op_info->name, op, walk_state)); + + /* Only interested in opcodes that have namespace objects */ + + if (!(walk_state->op_info->flags & AML_NSOBJECT)) { + return_ACPI_STATUS (AE_OK); + } + + if (op->common.aml_opcode == AML_SCOPE_OP) { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "Ending scope Op=%p State=%p\n", op, walk_state)); + } + + + object_type = walk_state->op_info->object_type; + + /* + * Get the Node/name from the earlier lookup + * (It was saved in the *op structure) + */ + node = op->common.node; + + /* + * Put the Node on the object stack (Contains the ACPI Name of + * this object) + */ + walk_state->operands[0] = (void *) node; + walk_state->num_operands = 1; + + /* Pop the scope stack */ + + if (acpi_ns_opens_scope (object_type) && (op->common.aml_opcode != AML_INT_METHODCALL_OP)) { + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "(%s) Popping scope for Op %p\n", + acpi_ut_get_type_name (object_type), op)); + + status = acpi_ds_scope_stack_pop (walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + } + + /* + * Named operations are as follows: + * + * AML_ALIAS + * AML_BANKFIELD + * AML_CREATEBITFIELD + * AML_CREATEBYTEFIELD + * AML_CREATEDWORDFIELD + * AML_CREATEFIELD + * AML_CREATEQWORDFIELD + * AML_CREATEWORDFIELD + * AML_DATA_REGION + * AML_DEVICE + * AML_EVENT + * AML_FIELD + * AML_INDEXFIELD + * AML_METHOD + * AML_METHODCALL + * AML_MUTEX + * AML_NAME + * AML_NAMEDFIELD + * AML_OPREGION + * AML_POWERRES + * AML_PROCESSOR + * AML_SCOPE + * AML_THERMALZONE + */ + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "Create-Load [%s] State=%p Op=%p named_obj=%p\n", + acpi_ps_get_opcode_name (op->common.aml_opcode), walk_state, op, node)); + + /* Decode the opcode */ + + arg = op->common.value.arg; + + switch (walk_state->op_info->type) { +#ifndef ACPI_NO_METHOD_EXECUTION + + case AML_TYPE_CREATE_FIELD: + + /* + * Create the field object, but the field buffer and index must + * be evaluated later during the execution phase + */ + status = acpi_ds_create_buffer_field (op, walk_state); + break; + + + case AML_TYPE_NAMED_FIELD: + + switch (op->common.aml_opcode) { + case AML_INDEX_FIELD_OP: + + status = acpi_ds_create_index_field (op, (acpi_handle) arg->common.node, + walk_state); + break; + + case AML_BANK_FIELD_OP: + + status = acpi_ds_create_bank_field (op, arg->common.node, walk_state); + break; + + case AML_FIELD_OP: + + status = acpi_ds_create_field (op, arg->common.node, walk_state); + break; + + default: + /* All NAMED_FIELD opcodes must be handled above */ + break; + } + break; + + + case AML_TYPE_NAMED_SIMPLE: + + status = acpi_ds_create_operands (walk_state, arg); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + switch (op->common.aml_opcode) { + case AML_PROCESSOR_OP: + + status = acpi_ex_create_processor (walk_state); + break; + + case AML_POWER_RES_OP: + + status = acpi_ex_create_power_resource (walk_state); + break; + + case AML_MUTEX_OP: + + status = acpi_ex_create_mutex (walk_state); + break; + + case AML_EVENT_OP: + + status = acpi_ex_create_event (walk_state); + break; + + case AML_DATA_REGION_OP: + + status = acpi_ex_create_table_region (walk_state); + break; + + case AML_ALIAS_OP: + + status = acpi_ex_create_alias (walk_state); + break; + + default: + /* Unknown opcode */ + + status = AE_OK; + goto cleanup; + } + + /* Delete operands */ + + for (i = 1; i < walk_state->num_operands; i++) { + acpi_ut_remove_reference (walk_state->operands[i]); + walk_state->operands[i] = NULL; + } + + break; +#endif /* ACPI_NO_METHOD_EXECUTION */ + + case AML_TYPE_NAMED_COMPLEX: + + switch (op->common.aml_opcode) { +#ifndef ACPI_NO_METHOD_EXECUTION + case AML_REGION_OP: + /* + * The op_region is not fully parsed at this time. Only valid argument is the space_id. + * (We must save the address of the AML of the address and length operands) + */ + /* + * If we have a valid region, initialize it + * Namespace is NOT locked at this point. + */ + status = acpi_ev_initialize_region (acpi_ns_get_attached_object (node), FALSE); + if (ACPI_FAILURE (status)) { + /* + * If AE_NOT_EXIST is returned, it is not fatal + * because many regions get created before a handler + * is installed for said region. + */ + if (AE_NOT_EXIST == status) { + status = AE_OK; + } + } + break; + + + case AML_NAME_OP: + + status = acpi_ds_create_node (walk_state, node, op); + break; +#endif /* ACPI_NO_METHOD_EXECUTION */ + + + default: + /* All NAMED_COMPLEX opcodes must be handled above */ + /* Note: Method objects were already created in Pass 1 */ + break; + } + break; + + + case AML_CLASS_INTERNAL: + + /* case AML_INT_NAMEPATH_OP: */ + break; + + + case AML_CLASS_METHOD_CALL: + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "RESOLVING-method_call: State=%p Op=%p named_obj=%p\n", + walk_state, op, node)); + + /* + * Lookup the method name and save the Node + */ + status = acpi_ns_lookup (walk_state->scope_info, arg->common.value.string, + ACPI_TYPE_ANY, ACPI_IMODE_LOAD_PASS2, + ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, + walk_state, &(new_node)); + if (ACPI_SUCCESS (status)) { + /* + * Make sure that what we found is indeed a method + * We didn't search for a method on purpose, to see if the name would resolve + */ + if (new_node->type != ACPI_TYPE_METHOD) { + status = AE_AML_OPERAND_TYPE; + } + + /* We could put the returned object (Node) on the object stack for later, but + * for now, we will put it in the "op" object that the parser uses, so we + * can get it again at the end of this scope + */ + op->common.node = new_node; + } + else { + ACPI_REPORT_NSERROR (arg->common.value.string, status); + } + break; + + + default: + break; + } + +cleanup: + + /* Remove the Node pushed at the very beginning */ + + walk_state->operands[0] = NULL; + walk_state->num_operands = 0; + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/dispatcher/dswscope.c b/drivers/acpi/dispatcher/dswscope.c new file mode 100644 index 00000000000..65f456151e2 --- /dev/null +++ b/drivers/acpi/dispatcher/dswscope.c @@ -0,0 +1,229 @@ +/****************************************************************************** + * + * Module Name: dswscope - Scope stack manipulation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acdispat.h> + + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dswscope") + + +#define STACK_POP(head) head + + +/**************************************************************************** + * + * FUNCTION: acpi_ds_scope_stack_clear + * + * PARAMETERS: None + * + * DESCRIPTION: Pop (and free) everything on the scope stack except the + * root scope object (which remains at the stack top.) + * + ***************************************************************************/ + +void +acpi_ds_scope_stack_clear ( + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *scope_info; + + ACPI_FUNCTION_NAME ("ds_scope_stack_clear"); + + + while (walk_state->scope_info) { + /* Pop a scope off the stack */ + + scope_info = walk_state->scope_info; + walk_state->scope_info = scope_info->scope.next; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Popped object type (%s)\n", acpi_ut_get_type_name (scope_info->common.value))); + acpi_ut_delete_generic_state (scope_info); + } +} + + +/**************************************************************************** + * + * FUNCTION: acpi_ds_scope_stack_push + * + * PARAMETERS: *Node, - Name to be made current + * Type, - Type of frame being pushed + * + * DESCRIPTION: Push the current scope on the scope stack, and make the + * passed Node current. + * + ***************************************************************************/ + +acpi_status +acpi_ds_scope_stack_push ( + struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *scope_info; + union acpi_generic_state *old_scope_info; + + + ACPI_FUNCTION_TRACE ("ds_scope_stack_push"); + + + if (!node) { + /* Invalid scope */ + + ACPI_REPORT_ERROR (("ds_scope_stack_push: null scope passed\n")); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Make sure object type is valid */ + + if (!acpi_ut_valid_object_type (type)) { + ACPI_REPORT_WARNING (("ds_scope_stack_push: Invalid object type: 0x%X\n", type)); + } + + /* Allocate a new scope object */ + + scope_info = acpi_ut_create_generic_state (); + if (!scope_info) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Init new scope object */ + + scope_info->common.data_type = ACPI_DESC_TYPE_STATE_WSCOPE; + scope_info->scope.node = node; + scope_info->common.value = (u16) type; + + walk_state->scope_depth++; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "[%.2d] Pushed scope ", (u32) walk_state->scope_depth)); + + old_scope_info = walk_state->scope_info; + if (old_scope_info) { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_EXEC, + "[%4.4s] (%s)", + acpi_ut_get_node_name (old_scope_info->scope.node), + acpi_ut_get_type_name (old_scope_info->common.value))); + } + else { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_EXEC, + "[\\___] (%s)", "ROOT")); + } + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_EXEC, + ", New scope -> [%4.4s] (%s)\n", + acpi_ut_get_node_name (scope_info->scope.node), + acpi_ut_get_type_name (scope_info->common.value))); + + /* Push new scope object onto stack */ + + acpi_ut_push_generic_state (&walk_state->scope_info, scope_info); + return_ACPI_STATUS (AE_OK); +} + + +/**************************************************************************** + * + * FUNCTION: acpi_ds_scope_stack_pop + * + * PARAMETERS: Type - The type of frame to be found + * + * DESCRIPTION: Pop the scope stack until a frame of the requested type + * is found. + * + * RETURN: Count of frames popped. If no frame of the requested type + * was found, the count is returned as a negative number and + * the scope stack is emptied (which sets the current scope + * to the root). If the scope stack was empty at entry, the + * function is a no-op and returns 0. + * + ***************************************************************************/ + +acpi_status +acpi_ds_scope_stack_pop ( + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *scope_info; + union acpi_generic_state *new_scope_info; + + + ACPI_FUNCTION_TRACE ("ds_scope_stack_pop"); + + + /* + * Pop scope info object off the stack. + */ + scope_info = acpi_ut_pop_generic_state (&walk_state->scope_info); + if (!scope_info) { + return_ACPI_STATUS (AE_STACK_UNDERFLOW); + } + + walk_state->scope_depth--; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "[%.2d] Popped scope [%4.4s] (%s), New scope -> ", + (u32) walk_state->scope_depth, + acpi_ut_get_node_name (scope_info->scope.node), + acpi_ut_get_type_name (scope_info->common.value))); + + new_scope_info = walk_state->scope_info; + if (new_scope_info) { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_EXEC, + "[%4.4s] (%s)\n", + acpi_ut_get_node_name (new_scope_info->scope.node), + acpi_ut_get_type_name (new_scope_info->common.value))); + } + else { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_EXEC, + "[\\___] (ROOT)\n")); + } + + acpi_ut_delete_generic_state (scope_info); + return_ACPI_STATUS (AE_OK); +} + + diff --git a/drivers/acpi/dispatcher/dswstate.c b/drivers/acpi/dispatcher/dswstate.c new file mode 100644 index 00000000000..e555b3fbd5e --- /dev/null +++ b/drivers/acpi/dispatcher/dswstate.c @@ -0,0 +1,1100 @@ +/****************************************************************************** + * + * Module Name: dswstate - Dispatcher parse tree walk management routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/acdispat.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_DISPATCHER + ACPI_MODULE_NAME ("dswstate") + + +#ifdef ACPI_FUTURE_USAGE + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_insert + * + * PARAMETERS: Object - Object to push + * Index - Where to insert the object + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Insert an object onto this walk's result stack + * + ******************************************************************************/ + +acpi_status +acpi_ds_result_insert ( + void *object, + u32 index, + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_NAME ("ds_result_insert"); + + + state = walk_state->results; + if (!state) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No result object pushed! State=%p\n", + walk_state)); + return (AE_NOT_EXIST); + } + + if (index >= ACPI_OBJ_NUM_OPERANDS) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Index out of range: %X Obj=%p State=%p Num=%X\n", + index, object, walk_state, state->results.num_results)); + return (AE_BAD_PARAMETER); + } + + if (!object) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Null Object! Index=%X Obj=%p State=%p Num=%X\n", + index, object, walk_state, state->results.num_results)); + return (AE_BAD_PARAMETER); + } + + state->results.obj_desc [index] = object; + state->results.num_results++; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Obj=%p [%s] State=%p Num=%X Cur=%X\n", + object, object ? acpi_ut_get_object_type_name ((union acpi_operand_object *) object) : "NULL", + walk_state, state->results.num_results, walk_state->current_result)); + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_remove + * + * PARAMETERS: Object - Where to return the popped object + * Index - Where to extract the object + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop an object off the bottom of this walk's result stack. In + * other words, this is a FIFO. + * + ******************************************************************************/ + +acpi_status +acpi_ds_result_remove ( + union acpi_operand_object **object, + u32 index, + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_NAME ("ds_result_remove"); + + + state = walk_state->results; + if (!state) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No result object pushed! State=%p\n", + walk_state)); + return (AE_NOT_EXIST); + } + + if (index >= ACPI_OBJ_MAX_OPERAND) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Index out of range: %X State=%p Num=%X\n", + index, walk_state, state->results.num_results)); + } + + /* Check for a valid result object */ + + if (!state->results.obj_desc [index]) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Null operand! State=%p #Ops=%X, Index=%X\n", + walk_state, state->results.num_results, index)); + return (AE_AML_NO_RETURN_VALUE); + } + + /* Remove the object */ + + state->results.num_results--; + + *object = state->results.obj_desc [index]; + state->results.obj_desc [index] = NULL; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Obj=%p [%s] Index=%X State=%p Num=%X\n", + *object, (*object) ? acpi_ut_get_object_type_name (*object) : "NULL", + index, walk_state, state->results.num_results)); + + return (AE_OK); +} + +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_pop + * + * PARAMETERS: Object - Where to return the popped object + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop an object off the bottom of this walk's result stack. In + * other words, this is a FIFO. + * + ******************************************************************************/ + +acpi_status +acpi_ds_result_pop ( + union acpi_operand_object **object, + struct acpi_walk_state *walk_state) +{ + acpi_native_uint index; + union acpi_generic_state *state; + + + ACPI_FUNCTION_NAME ("ds_result_pop"); + + + state = walk_state->results; + if (!state) { + return (AE_OK); + } + + if (!state->results.num_results) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Result stack is empty! State=%p\n", + walk_state)); + return (AE_AML_NO_RETURN_VALUE); + } + + /* Remove top element */ + + state->results.num_results--; + + for (index = ACPI_OBJ_NUM_OPERANDS; index; index--) { + /* Check for a valid result object */ + + if (state->results.obj_desc [index -1]) { + *object = state->results.obj_desc [index -1]; + state->results.obj_desc [index -1] = NULL; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Obj=%p [%s] Index=%X State=%p Num=%X\n", + *object, (*object) ? acpi_ut_get_object_type_name (*object) : "NULL", + (u32) index -1, walk_state, state->results.num_results)); + + return (AE_OK); + } + } + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No result objects! State=%p\n", walk_state)); + return (AE_AML_NO_RETURN_VALUE); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_pop_from_bottom + * + * PARAMETERS: Object - Where to return the popped object + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop an object off the bottom of this walk's result stack. In + * other words, this is a FIFO. + * + ******************************************************************************/ + +acpi_status +acpi_ds_result_pop_from_bottom ( + union acpi_operand_object **object, + struct acpi_walk_state *walk_state) +{ + acpi_native_uint index; + union acpi_generic_state *state; + + + ACPI_FUNCTION_NAME ("ds_result_pop_from_bottom"); + + + state = walk_state->results; + if (!state) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Warning: No result object pushed! State=%p\n", walk_state)); + return (AE_NOT_EXIST); + } + + if (!state->results.num_results) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No result objects! State=%p\n", walk_state)); + return (AE_AML_NO_RETURN_VALUE); + } + + /* Remove Bottom element */ + + *object = state->results.obj_desc [0]; + + /* Push entire stack down one element */ + + for (index = 0; index < state->results.num_results; index++) { + state->results.obj_desc [index] = state->results.obj_desc [index + 1]; + } + + state->results.num_results--; + + /* Check for a valid result object */ + + if (!*object) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null operand! State=%p #Ops=%X, Index=%X\n", + walk_state, state->results.num_results, (u32) index)); + return (AE_AML_NO_RETURN_VALUE); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Obj=%p [%s], Results=%p State=%p\n", + *object, (*object) ? acpi_ut_get_object_type_name (*object) : "NULL", + state, walk_state)); + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_push + * + * PARAMETERS: Object - Where to return the popped object + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Push an object onto the current result stack + * + ******************************************************************************/ + +acpi_status +acpi_ds_result_push ( + union acpi_operand_object *object, + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_NAME ("ds_result_push"); + + + state = walk_state->results; + if (!state) { + ACPI_REPORT_ERROR (("No result stack frame during push\n")); + return (AE_AML_INTERNAL); + } + + if (state->results.num_results == ACPI_OBJ_NUM_OPERANDS) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Result stack overflow: Obj=%p State=%p Num=%X\n", + object, walk_state, state->results.num_results)); + return (AE_STACK_OVERFLOW); + } + + if (!object) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null Object! Obj=%p State=%p Num=%X\n", + object, walk_state, state->results.num_results)); + return (AE_BAD_PARAMETER); + } + + state->results.obj_desc [state->results.num_results] = object; + state->results.num_results++; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Obj=%p [%s] State=%p Num=%X Cur=%X\n", + object, object ? acpi_ut_get_object_type_name ((union acpi_operand_object *) object) : "NULL", + walk_state, state->results.num_results, walk_state->current_result)); + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_stack_push + * + * PARAMETERS: walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Push an object onto the walk_state result stack. + * + ******************************************************************************/ + +acpi_status +acpi_ds_result_stack_push ( + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_NAME ("ds_result_stack_push"); + + + state = acpi_ut_create_generic_state (); + if (!state) { + return (AE_NO_MEMORY); + } + + state->common.data_type = ACPI_DESC_TYPE_STATE_RESULT; + acpi_ut_push_generic_state (&walk_state->results, state); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Results=%p State=%p\n", + state, walk_state)); + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_result_stack_pop + * + * PARAMETERS: walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop an object off of the walk_state result stack. + * + ******************************************************************************/ + +acpi_status +acpi_ds_result_stack_pop ( + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *state; + + ACPI_FUNCTION_NAME ("ds_result_stack_pop"); + + + /* Check for stack underflow */ + + if (walk_state->results == NULL) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Underflow - State=%p\n", + walk_state)); + return (AE_AML_NO_OPERAND); + } + + state = acpi_ut_pop_generic_state (&walk_state->results); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Result=%p remaining_results=%X State=%p\n", + state, state->results.num_results, walk_state)); + + acpi_ut_delete_generic_state (state); + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_obj_stack_delete_all + * + * PARAMETERS: walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Clear the object stack by deleting all objects that are on it. + * Should be used with great care, if at all! + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_ds_obj_stack_delete_all ( + struct acpi_walk_state *walk_state) +{ + u32 i; + + + ACPI_FUNCTION_TRACE_PTR ("ds_obj_stack_delete_all", walk_state); + + + /* The stack size is configurable, but fixed */ + + for (i = 0; i < ACPI_OBJ_NUM_OPERANDS; i++) { + if (walk_state->operands[i]) { + acpi_ut_remove_reference (walk_state->operands[i]); + walk_state->operands[i] = NULL; + } + } + + return_ACPI_STATUS (AE_OK); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_obj_stack_push + * + * PARAMETERS: Object - Object to push + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Push an object onto this walk's object/operand stack + * + ******************************************************************************/ + +acpi_status +acpi_ds_obj_stack_push ( + void *object, + struct acpi_walk_state *walk_state) +{ + ACPI_FUNCTION_NAME ("ds_obj_stack_push"); + + + /* Check for stack overflow */ + + if (walk_state->num_operands >= ACPI_OBJ_NUM_OPERANDS) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "overflow! Obj=%p State=%p #Ops=%X\n", + object, walk_state, walk_state->num_operands)); + return (AE_STACK_OVERFLOW); + } + + /* Put the object onto the stack */ + + walk_state->operands [walk_state->num_operands] = object; + walk_state->num_operands++; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Obj=%p [%s] State=%p #Ops=%X\n", + object, acpi_ut_get_object_type_name ((union acpi_operand_object *) object), + walk_state, walk_state->num_operands)); + + return (AE_OK); +} + + +#if 0 +/******************************************************************************* + * + * FUNCTION: acpi_ds_obj_stack_pop_object + * + * PARAMETERS: pop_count - Number of objects/entries to pop + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop this walk's object stack. Objects on the stack are NOT + * deleted by this routine. + * + ******************************************************************************/ + +acpi_status +acpi_ds_obj_stack_pop_object ( + union acpi_operand_object **object, + struct acpi_walk_state *walk_state) +{ + ACPI_FUNCTION_NAME ("ds_obj_stack_pop_object"); + + + /* Check for stack underflow */ + + if (walk_state->num_operands == 0) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Missing operand/stack empty! State=%p #Ops=%X\n", + walk_state, walk_state->num_operands)); + *object = NULL; + return (AE_AML_NO_OPERAND); + } + + /* Pop the stack */ + + walk_state->num_operands--; + + /* Check for a valid operand */ + + if (!walk_state->operands [walk_state->num_operands]) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Null operand! State=%p #Ops=%X\n", + walk_state, walk_state->num_operands)); + *object = NULL; + return (AE_AML_NO_OPERAND); + } + + /* Get operand and set stack entry to null */ + + *object = walk_state->operands [walk_state->num_operands]; + walk_state->operands [walk_state->num_operands] = NULL; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Obj=%p [%s] State=%p #Ops=%X\n", + *object, acpi_ut_get_object_type_name (*object), + walk_state, walk_state->num_operands)); + + return (AE_OK); +} +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_obj_stack_pop + * + * PARAMETERS: pop_count - Number of objects/entries to pop + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop this walk's object stack. Objects on the stack are NOT + * deleted by this routine. + * + ******************************************************************************/ + +acpi_status +acpi_ds_obj_stack_pop ( + u32 pop_count, + struct acpi_walk_state *walk_state) +{ + u32 i; + + ACPI_FUNCTION_NAME ("ds_obj_stack_pop"); + + + for (i = 0; i < pop_count; i++) { + /* Check for stack underflow */ + + if (walk_state->num_operands == 0) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Underflow! Count=%X State=%p #Ops=%X\n", + pop_count, walk_state, walk_state->num_operands)); + return (AE_STACK_UNDERFLOW); + } + + /* Just set the stack entry to null */ + + walk_state->num_operands--; + walk_state->operands [walk_state->num_operands] = NULL; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Count=%X State=%p #Ops=%X\n", + pop_count, walk_state, walk_state->num_operands)); + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_obj_stack_pop_and_delete + * + * PARAMETERS: pop_count - Number of objects/entries to pop + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Pop this walk's object stack and delete each object that is + * popped off. + * + ******************************************************************************/ + +acpi_status +acpi_ds_obj_stack_pop_and_delete ( + u32 pop_count, + struct acpi_walk_state *walk_state) +{ + u32 i; + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_NAME ("ds_obj_stack_pop_and_delete"); + + + for (i = 0; i < pop_count; i++) { + /* Check for stack underflow */ + + if (walk_state->num_operands == 0) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Underflow! Count=%X State=%p #Ops=%X\n", + pop_count, walk_state, walk_state->num_operands)); + return (AE_STACK_UNDERFLOW); + } + + /* Pop the stack and delete an object if present in this stack entry */ + + walk_state->num_operands--; + obj_desc = walk_state->operands [walk_state->num_operands]; + if (obj_desc) { + acpi_ut_remove_reference (walk_state->operands [walk_state->num_operands]); + walk_state->operands [walk_state->num_operands] = NULL; + } + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Count=%X State=%p #Ops=%X\n", + pop_count, walk_state, walk_state->num_operands)); + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_obj_stack_get_value + * + * PARAMETERS: Index - Stack index whose value is desired. Based + * on the top of the stack (index=0 == top) + * walk_state - Current Walk state + * + * RETURN: Status + * + * DESCRIPTION: Retrieve an object from this walk's object stack. Index must + * be within the range of the current stack pointer. + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +void * +acpi_ds_obj_stack_get_value ( + u32 index, + struct acpi_walk_state *walk_state) +{ + + ACPI_FUNCTION_TRACE_PTR ("ds_obj_stack_get_value", walk_state); + + + /* Can't do it if the stack is empty */ + + if (walk_state->num_operands == 0) { + return_PTR (NULL); + } + + /* or if the index is past the top of the stack */ + + if (index > (walk_state->num_operands - (u32) 1)) { + return_PTR (NULL); + } + + return_PTR (walk_state->operands[(acpi_native_uint)(walk_state->num_operands - 1) - + index]); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_get_current_walk_state + * + * PARAMETERS: Thread - Get current active state for this Thread + * + * RETURN: Pointer to the current walk state + * + * DESCRIPTION: Get the walk state that is at the head of the list (the "current" + * walk state.) + * + ******************************************************************************/ + +struct acpi_walk_state * +acpi_ds_get_current_walk_state ( + struct acpi_thread_state *thread) + +{ + ACPI_FUNCTION_NAME ("ds_get_current_walk_state"); + + + if (!thread) { + return (NULL); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Current walk_state %p\n", + thread->walk_state_list)); + + return (thread->walk_state_list); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_push_walk_state + * + * PARAMETERS: walk_state - State to push + * walk_list - The list that owns the walk stack + * + * RETURN: None + * + * DESCRIPTION: Place the walk_state at the head of the state list. + * + ******************************************************************************/ + +void +acpi_ds_push_walk_state ( + struct acpi_walk_state *walk_state, + struct acpi_thread_state *thread) +{ + ACPI_FUNCTION_TRACE ("ds_push_walk_state"); + + + walk_state->next = thread->walk_state_list; + thread->walk_state_list = walk_state; + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_pop_walk_state + * + * PARAMETERS: walk_list - The list that owns the walk stack + * + * RETURN: A walk_state object popped from the stack + * + * DESCRIPTION: Remove and return the walkstate object that is at the head of + * the walk stack for the given walk list. NULL indicates that + * the list is empty. + * + ******************************************************************************/ + +struct acpi_walk_state * +acpi_ds_pop_walk_state ( + struct acpi_thread_state *thread) +{ + struct acpi_walk_state *walk_state; + + + ACPI_FUNCTION_TRACE ("ds_pop_walk_state"); + + + walk_state = thread->walk_state_list; + + if (walk_state) { + /* Next walk state becomes the current walk state */ + + thread->walk_state_list = walk_state->next; + + /* + * Don't clear the NEXT field, this serves as an indicator + * that there is a parent WALK STATE + * NO: walk_state->Next = NULL; + */ + } + + return_PTR (walk_state); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_create_walk_state + * + * PARAMETERS: Origin - Starting point for this walk + * Thread - Current thread state + * + * RETURN: Pointer to the new walk state. + * + * DESCRIPTION: Allocate and initialize a new walk state. The current walk + * state is set to this new state. + * + ******************************************************************************/ + +struct acpi_walk_state * +acpi_ds_create_walk_state ( + acpi_owner_id owner_id, + union acpi_parse_object *origin, + union acpi_operand_object *mth_desc, + struct acpi_thread_state *thread) +{ + struct acpi_walk_state *walk_state; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ds_create_walk_state"); + + + walk_state = acpi_ut_acquire_from_cache (ACPI_MEM_LIST_WALK); + if (!walk_state) { + return_PTR (NULL); + } + + walk_state->data_type = ACPI_DESC_TYPE_WALK; + walk_state->owner_id = owner_id; + walk_state->origin = origin; + walk_state->method_desc = mth_desc; + walk_state->thread = thread; + + walk_state->parser_state.start_op = origin; + + /* Init the method args/local */ + +#if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY)) + acpi_ds_method_data_init (walk_state); +#endif + + /* Create an initial result stack entry */ + + status = acpi_ds_result_stack_push (walk_state); + if (ACPI_FAILURE (status)) { + acpi_ut_release_to_cache (ACPI_MEM_LIST_WALK, walk_state); + return_PTR (NULL); + } + + /* Put the new state at the head of the walk list */ + + if (thread) { + acpi_ds_push_walk_state (walk_state, thread); + } + + return_PTR (walk_state); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_init_aml_walk + * + * PARAMETERS: walk_state - New state to be initialized + * Op - Current parse op + * method_node - Control method NS node, if any + * aml_start - Start of AML + * aml_length - Length of AML + * Params - Method args, if any + * return_obj_desc - Where to store a return object, if any + * pass_number - 1, 2, or 3 + * + * RETURN: Status + * + * DESCRIPTION: Initialize a walk state for a pass 1 or 2 parse tree walk + * + ******************************************************************************/ + +acpi_status +acpi_ds_init_aml_walk ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + struct acpi_namespace_node *method_node, + u8 *aml_start, + u32 aml_length, + struct acpi_parameter_info *info, + u32 pass_number) +{ + acpi_status status; + struct acpi_parse_state *parser_state = &walk_state->parser_state; + union acpi_parse_object *extra_op; + + + ACPI_FUNCTION_TRACE ("ds_init_aml_walk"); + + + walk_state->parser_state.aml = + walk_state->parser_state.aml_start = aml_start; + walk_state->parser_state.aml_end = + walk_state->parser_state.pkg_end = aml_start + aml_length; + + /* The next_op of the next_walk will be the beginning of the method */ + + walk_state->next_op = NULL; + + if (info) { + if (info->parameter_type == ACPI_PARAM_GPE) { + walk_state->gpe_event_info = ACPI_CAST_PTR (struct acpi_gpe_event_info, + info->parameters); + } + else { + walk_state->params = info->parameters; + walk_state->caller_return_desc = &info->return_object; + } + } + + status = acpi_ps_init_scope (&walk_state->parser_state, op); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (method_node) { + walk_state->parser_state.start_node = method_node; + walk_state->walk_type = ACPI_WALK_METHOD; + walk_state->method_node = method_node; + walk_state->method_desc = acpi_ns_get_attached_object (method_node); + + /* Push start scope on scope stack and make it current */ + + status = acpi_ds_scope_stack_push (method_node, ACPI_TYPE_METHOD, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Init the method arguments */ + + status = acpi_ds_method_data_init_args (walk_state->params, ACPI_METHOD_NUM_ARGS, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + else { + /* + * Setup the current scope. + * Find a Named Op that has a namespace node associated with it. + * search upwards from this Op. Current scope is the first + * Op with a namespace node. + */ + extra_op = parser_state->start_op; + while (extra_op && !extra_op->common.node) { + extra_op = extra_op->common.parent; + } + + if (!extra_op) { + parser_state->start_node = NULL; + } + else { + parser_state->start_node = extra_op->common.node; + } + + if (parser_state->start_node) { + /* Push start scope on scope stack and make it current */ + + status = acpi_ds_scope_stack_push (parser_state->start_node, + parser_state->start_node->type, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + } + + status = acpi_ds_init_callbacks (walk_state, pass_number); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ds_delete_walk_state + * + * PARAMETERS: walk_state - State to delete + * + * RETURN: Status + * + * DESCRIPTION: Delete a walk state including all internal data structures + * + ******************************************************************************/ + +void +acpi_ds_delete_walk_state ( + struct acpi_walk_state *walk_state) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_TRACE_PTR ("ds_delete_walk_state", walk_state); + + + if (!walk_state) { + return; + } + + if (walk_state->data_type != ACPI_DESC_TYPE_WALK) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "%p is not a valid walk state\n", walk_state)); + return; + } + + if (walk_state->parser_state.scope) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "%p walk still has a scope list\n", walk_state)); + } + + /* Always must free any linked control states */ + + while (walk_state->control_state) { + state = walk_state->control_state; + walk_state->control_state = state->common.next; + + acpi_ut_delete_generic_state (state); + } + + /* Always must free any linked parse states */ + + while (walk_state->scope_info) { + state = walk_state->scope_info; + walk_state->scope_info = state->common.next; + + acpi_ut_delete_generic_state (state); + } + + /* Always must free any stacked result states */ + + while (walk_state->results) { + state = walk_state->results; + walk_state->results = state->common.next; + + acpi_ut_delete_generic_state (state); + } + + acpi_ut_release_to_cache (ACPI_MEM_LIST_WALK, walk_state); + return_VOID; +} + + +#ifdef ACPI_ENABLE_OBJECT_CACHE +/****************************************************************************** + * + * FUNCTION: acpi_ds_delete_walk_state_cache + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Purge the global state object cache. Used during subsystem + * termination. + * + ******************************************************************************/ + +void +acpi_ds_delete_walk_state_cache ( + void) +{ + ACPI_FUNCTION_TRACE ("ds_delete_walk_state_cache"); + + + acpi_ut_delete_generic_cache (ACPI_MEM_LIST_WALK); + return_VOID; +} +#endif + + diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c new file mode 100644 index 00000000000..fdf143b405b --- /dev/null +++ b/drivers/acpi/ec.c @@ -0,0 +1,1024 @@ +/* + * acpi_ec.c - ACPI Embedded Controller Driver ($Revision: 38 $) + * + * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <asm/io.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> +#include <acpi/actypes.h> + +#define _COMPONENT ACPI_EC_COMPONENT +ACPI_MODULE_NAME ("acpi_ec") + +#define ACPI_EC_COMPONENT 0x00100000 +#define ACPI_EC_CLASS "embedded_controller" +#define ACPI_EC_HID "PNP0C09" +#define ACPI_EC_DRIVER_NAME "ACPI Embedded Controller Driver" +#define ACPI_EC_DEVICE_NAME "Embedded Controller" +#define ACPI_EC_FILE_INFO "info" + + +#define ACPI_EC_FLAG_OBF 0x01 /* Output buffer full */ +#define ACPI_EC_FLAG_IBF 0x02 /* Input buffer full */ +#define ACPI_EC_FLAG_SCI 0x20 /* EC-SCI occurred */ + +#define ACPI_EC_EVENT_OBF 0x01 /* Output buffer full */ +#define ACPI_EC_EVENT_IBE 0x02 /* Input buffer empty */ + +#define ACPI_EC_UDELAY 100 /* Poll @ 100us increments */ +#define ACPI_EC_UDELAY_COUNT 1000 /* Wait 10ms max. during EC ops */ +#define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ + +#define ACPI_EC_COMMAND_READ 0x80 +#define ACPI_EC_COMMAND_WRITE 0x81 +#define ACPI_EC_COMMAND_QUERY 0x84 + +static int acpi_ec_add (struct acpi_device *device); +static int acpi_ec_remove (struct acpi_device *device, int type); +static int acpi_ec_start (struct acpi_device *device); +static int acpi_ec_stop (struct acpi_device *device, int type); + +static struct acpi_driver acpi_ec_driver = { + .name = ACPI_EC_DRIVER_NAME, + .class = ACPI_EC_CLASS, + .ids = ACPI_EC_HID, + .ops = { + .add = acpi_ec_add, + .remove = acpi_ec_remove, + .start = acpi_ec_start, + .stop = acpi_ec_stop, + }, +}; + +struct acpi_ec { + acpi_handle handle; + unsigned long uid; + unsigned long gpe_bit; + struct acpi_generic_address status_addr; + struct acpi_generic_address command_addr; + struct acpi_generic_address data_addr; + unsigned long global_lock; + spinlock_t lock; +}; + +/* If we find an EC via the ECDT, we need to keep a ptr to its context */ +static struct acpi_ec *ec_ecdt; + +/* External interfaces use first EC only, so remember */ +static struct acpi_device *first_ec; + +/* -------------------------------------------------------------------------- + Transaction Management + -------------------------------------------------------------------------- */ + +static int +acpi_ec_wait ( + struct acpi_ec *ec, + u8 event) +{ + u32 acpi_ec_status = 0; + u32 i = ACPI_EC_UDELAY_COUNT; + + if (!ec) + return -EINVAL; + + /* Poll the EC status register waiting for the event to occur. */ + switch (event) { + case ACPI_EC_EVENT_OBF: + do { + acpi_hw_low_level_read(8, &acpi_ec_status, &ec->status_addr); + if (acpi_ec_status & ACPI_EC_FLAG_OBF) + return 0; + udelay(ACPI_EC_UDELAY); + } while (--i>0); + break; + case ACPI_EC_EVENT_IBE: + do { + acpi_hw_low_level_read(8, &acpi_ec_status, &ec->status_addr); + if (!(acpi_ec_status & ACPI_EC_FLAG_IBF)) + return 0; + udelay(ACPI_EC_UDELAY); + } while (--i>0); + break; + default: + return -EINVAL; + } + + return -ETIME; +} + + +static int +acpi_ec_read ( + struct acpi_ec *ec, + u8 address, + u32 *data) +{ + acpi_status status = AE_OK; + int result = 0; + unsigned long flags = 0; + u32 glk = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_read"); + + if (!ec || !data) + return_VALUE(-EINVAL); + + *data = 0; + + if (ec->global_lock) { + status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + } + + spin_lock_irqsave(&ec->lock, flags); + + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_READ, &ec->command_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + goto end; + + acpi_hw_low_level_write(8, address, &ec->data_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (result) + goto end; + + + acpi_hw_low_level_read(8, data, &ec->data_addr); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Read [%02x] from address [%02x]\n", + *data, address)); + +end: + spin_unlock_irqrestore(&ec->lock, flags); + + if (ec->global_lock) + acpi_release_global_lock(glk); + + return_VALUE(result); +} + + +static int +acpi_ec_write ( + struct acpi_ec *ec, + u8 address, + u8 data) +{ + int result = 0; + acpi_status status = AE_OK; + unsigned long flags = 0; + u32 glk = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_write"); + + if (!ec) + return_VALUE(-EINVAL); + + if (ec->global_lock) { + status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + } + + spin_lock_irqsave(&ec->lock, flags); + + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_WRITE, &ec->command_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + goto end; + + acpi_hw_low_level_write(8, address, &ec->data_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + goto end; + + acpi_hw_low_level_write(8, data, &ec->data_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_IBE); + if (result) + goto end; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Wrote [%02x] to address [%02x]\n", + data, address)); + +end: + spin_unlock_irqrestore(&ec->lock, flags); + + if (ec->global_lock) + acpi_release_global_lock(glk); + + return_VALUE(result); +} + +/* + * Externally callable EC access functions. For now, assume 1 EC only + */ +int +ec_read(u8 addr, u8 *val) +{ + struct acpi_ec *ec; + int err; + u32 temp_data; + + if (!first_ec) + return -ENODEV; + + ec = acpi_driver_data(first_ec); + + err = acpi_ec_read(ec, addr, &temp_data); + + if (!err) { + *val = temp_data; + return 0; + } + else + return err; +} +EXPORT_SYMBOL(ec_read); + +int +ec_write(u8 addr, u8 val) +{ + struct acpi_ec *ec; + int err; + + if (!first_ec) + return -ENODEV; + + ec = acpi_driver_data(first_ec); + + err = acpi_ec_write(ec, addr, val); + + return err; +} +EXPORT_SYMBOL(ec_write); + + +static int +acpi_ec_query ( + struct acpi_ec *ec, + u32 *data) +{ + int result = 0; + acpi_status status = AE_OK; + unsigned long flags = 0; + u32 glk = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_query"); + + if (!ec || !data) + return_VALUE(-EINVAL); + + *data = 0; + + if (ec->global_lock) { + status = acpi_acquire_global_lock(ACPI_EC_UDELAY_GLK, &glk); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + } + + /* + * Query the EC to find out which _Qxx method we need to evaluate. + * Note that successful completion of the query causes the ACPI_EC_SCI + * bit to be cleared (and thus clearing the interrupt source). + */ + spin_lock_irqsave(&ec->lock, flags); + + acpi_hw_low_level_write(8, ACPI_EC_COMMAND_QUERY, &ec->command_addr); + result = acpi_ec_wait(ec, ACPI_EC_EVENT_OBF); + if (result) + goto end; + + acpi_hw_low_level_read(8, data, &ec->data_addr); + if (!*data) + result = -ENODATA; + +end: + spin_unlock_irqrestore(&ec->lock, flags); + + if (ec->global_lock) + acpi_release_global_lock(glk); + + return_VALUE(result); +} + + +/* -------------------------------------------------------------------------- + Event Management + -------------------------------------------------------------------------- */ + +struct acpi_ec_query_data { + acpi_handle handle; + u8 data; +}; + +static void +acpi_ec_gpe_query ( + void *ec_cxt) +{ + struct acpi_ec *ec = (struct acpi_ec *) ec_cxt; + u32 value = 0; + unsigned long flags = 0; + static char object_name[5] = {'_','Q','0','0','\0'}; + const char hex[] = {'0','1','2','3','4','5','6','7', + '8','9','A','B','C','D','E','F'}; + + ACPI_FUNCTION_TRACE("acpi_ec_gpe_query"); + + if (!ec_cxt) + goto end; + + spin_lock_irqsave(&ec->lock, flags); + acpi_hw_low_level_read(8, &value, &ec->command_addr); + spin_unlock_irqrestore(&ec->lock, flags); + + /* TBD: Implement asynch events! + * NOTE: All we care about are EC-SCI's. Other EC events are + * handled via polling (yuck!). This is because some systems + * treat EC-SCIs as level (versus EDGE!) triggered, preventing + * a purely interrupt-driven approach (grumble, grumble). + */ + if (!(value & ACPI_EC_FLAG_SCI)) + goto end; + + if (acpi_ec_query(ec, &value)) + goto end; + + object_name[2] = hex[((value >> 4) & 0x0F)]; + object_name[3] = hex[(value & 0x0F)]; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluating %s\n", object_name)); + + acpi_evaluate_object(ec->handle, object_name, NULL, NULL); + +end: + acpi_enable_gpe(NULL, ec->gpe_bit, ACPI_NOT_ISR); +} + +static u32 +acpi_ec_gpe_handler ( + void *data) +{ + acpi_status status = AE_OK; + struct acpi_ec *ec = (struct acpi_ec *) data; + + if (!ec) + return ACPI_INTERRUPT_NOT_HANDLED; + + acpi_disable_gpe(NULL, ec->gpe_bit, ACPI_ISR); + + status = acpi_os_queue_for_execution(OSD_PRIORITY_GPE, + acpi_ec_gpe_query, ec); + + if (status == AE_OK) + return ACPI_INTERRUPT_HANDLED; + else + return ACPI_INTERRUPT_NOT_HANDLED; +} + +/* -------------------------------------------------------------------------- + Address Space Management + -------------------------------------------------------------------------- */ + +static acpi_status +acpi_ec_space_setup ( + acpi_handle region_handle, + u32 function, + void *handler_context, + void **return_context) +{ + /* + * The EC object is in the handler context and is needed + * when calling the acpi_ec_space_handler. + */ + if(function == ACPI_REGION_DEACTIVATE) + *return_context = NULL; + else + *return_context = handler_context; + + return AE_OK; +} + + +static acpi_status +acpi_ec_space_handler ( + u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer *value, + void *handler_context, + void *region_context) +{ + int result = 0; + struct acpi_ec *ec = NULL; + u32 temp = 0; + acpi_integer f_v = 0; + int i = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_space_handler"); + + if ((address > 0xFF) || !value || !handler_context) + return_VALUE(AE_BAD_PARAMETER); + + if(bit_width != 8) { + printk(KERN_WARNING PREFIX "acpi_ec_space_handler: bit_width should be 8\n"); + if (acpi_strict) + return_VALUE(AE_BAD_PARAMETER); + } + + ec = (struct acpi_ec *) handler_context; + +next_byte: + switch (function) { + case ACPI_READ: + result = acpi_ec_read(ec, (u8) address, &temp); + *value = (acpi_integer) temp; + break; + case ACPI_WRITE: + result = acpi_ec_write(ec, (u8) address, (u8) *value); + break; + default: + result = -EINVAL; + goto out; + break; + } + + bit_width -= 8; + if(bit_width){ + + if(function == ACPI_READ) + f_v |= (acpi_integer) (*value) << 8*i; + if(function == ACPI_WRITE) + (*value) >>=8; + i++; + goto next_byte; + } + + + if(function == ACPI_READ){ + f_v |= (acpi_integer) (*value) << 8*i; + *value = f_v; + } + + +out: + switch (result) { + case -EINVAL: + return_VALUE(AE_BAD_PARAMETER); + break; + case -ENODEV: + return_VALUE(AE_NOT_FOUND); + break; + case -ETIME: + return_VALUE(AE_TIME); + break; + default: + return_VALUE(AE_OK); + } + + +} + + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_ec_dir; + + +static int +acpi_ec_read_info (struct seq_file *seq, void *offset) +{ + struct acpi_ec *ec = (struct acpi_ec *) seq->private; + + ACPI_FUNCTION_TRACE("acpi_ec_read_info"); + + if (!ec) + goto end; + + seq_printf(seq, "gpe bit: 0x%02x\n", + (u32) ec->gpe_bit); + seq_printf(seq, "ports: 0x%02x, 0x%02x\n", + (u32) ec->status_addr.address, (u32) ec->data_addr.address); + seq_printf(seq, "use global lock: %s\n", + ec->global_lock?"yes":"no"); + +end: + return_VALUE(0); +} + +static int acpi_ec_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_ec_read_info, PDE(inode)->data); +} + +static struct file_operations acpi_ec_info_ops = { + .open = acpi_ec_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int +acpi_ec_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_ec_add_fs"); + + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_ec_dir); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + } + + entry = create_proc_entry(ACPI_EC_FILE_INFO, S_IRUGO, + acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "Unable to create '%s' fs entry\n", + ACPI_EC_FILE_INFO)); + else { + entry->proc_fops = &acpi_ec_info_ops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + return_VALUE(0); +} + + +static int +acpi_ec_remove_fs ( + struct acpi_device *device) +{ + ACPI_FUNCTION_TRACE("acpi_ec_remove_fs"); + + if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_EC_FILE_INFO, acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), acpi_ec_dir); + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static int +acpi_ec_add ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_ec *ec = NULL; + unsigned long uid; + + ACPI_FUNCTION_TRACE("acpi_ec_add"); + + if (!device) + return_VALUE(-EINVAL); + + ec = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + if (!ec) + return_VALUE(-ENOMEM); + memset(ec, 0, sizeof(struct acpi_ec)); + + ec->handle = device->handle; + ec->uid = -1; + spin_lock_init(&ec->lock); + strcpy(acpi_device_name(device), ACPI_EC_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_EC_CLASS); + acpi_driver_data(device) = ec; + + /* Use the global lock for all EC transactions? */ + acpi_evaluate_integer(ec->handle, "_GLK", NULL, &ec->global_lock); + + /* If our UID matches the UID for the ECDT-enumerated EC, + we now have the *real* EC info, so kill the makeshift one.*/ + acpi_evaluate_integer(ec->handle, "_UID", NULL, &uid); + if (ec_ecdt && ec_ecdt->uid == uid) { + acpi_remove_address_space_handler(ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); + + acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, &acpi_ec_gpe_handler); + + kfree(ec_ecdt); + } + + /* Get GPE bit assignment (EC events). */ + /* TODO: Add support for _GPE returning a package */ + status = acpi_evaluate_integer(ec->handle, "_GPE", NULL, &ec->gpe_bit); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error obtaining GPE bit assignment\n")); + result = -ENODEV; + goto end; + } + + result = acpi_ec_add_fs(device); + if (result) + goto end; + + printk(KERN_INFO PREFIX "%s [%s] (gpe %d)\n", + acpi_device_name(device), acpi_device_bid(device), + (u32) ec->gpe_bit); + + if (!first_ec) + first_ec = device; + +end: + if (result) + kfree(ec); + + return_VALUE(result); +} + + +static int +acpi_ec_remove ( + struct acpi_device *device, + int type) +{ + struct acpi_ec *ec = NULL; + + ACPI_FUNCTION_TRACE("acpi_ec_remove"); + + if (!device) + return_VALUE(-EINVAL); + + ec = acpi_driver_data(device); + + acpi_ec_remove_fs(device); + + kfree(ec); + + return_VALUE(0); +} + + +static acpi_status +acpi_ec_io_ports ( + struct acpi_resource *resource, + void *context) +{ + struct acpi_ec *ec = (struct acpi_ec *) context; + struct acpi_generic_address *addr; + + if (resource->id != ACPI_RSTYPE_IO) { + return AE_OK; + } + + /* + * The first address region returned is the data port, and + * the second address region returned is the status/command + * port. + */ + if (ec->data_addr.register_bit_width == 0) { + addr = &ec->data_addr; + } else if (ec->command_addr.register_bit_width == 0) { + addr = &ec->command_addr; + } else { + return AE_CTRL_TERMINATE; + } + + addr->address_space_id = ACPI_ADR_SPACE_SYSTEM_IO; + addr->register_bit_width = 8; + addr->register_bit_offset = 0; + addr->address = resource->data.io.min_base_address; + + return AE_OK; +} + + +static int +acpi_ec_start ( + struct acpi_device *device) +{ + acpi_status status = AE_OK; + struct acpi_ec *ec = NULL; + + ACPI_FUNCTION_TRACE("acpi_ec_start"); + + if (!device) + return_VALUE(-EINVAL); + + ec = acpi_driver_data(device); + + if (!ec) + return_VALUE(-EINVAL); + + /* + * Get I/O port addresses. Convert to GAS format. + */ + status = acpi_walk_resources(ec->handle, METHOD_NAME__CRS, + acpi_ec_io_ports, ec); + if (ACPI_FAILURE(status) || ec->command_addr.register_bit_width == 0) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error getting I/O port addresses")); + return_VALUE(-ENODEV); + } + + ec->status_addr = ec->command_addr; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "gpe=0x%02x, ports=0x%2x,0x%2x\n", + (u32) ec->gpe_bit, (u32) ec->command_addr.address, + (u32) ec->data_addr.address)); + + /* + * Install GPE handler + */ + status = acpi_install_gpe_handler(NULL, ec->gpe_bit, + ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, ec); + if (ACPI_FAILURE(status)) { + return_VALUE(-ENODEV); + } + acpi_set_gpe_type (NULL, ec->gpe_bit, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe (NULL, ec->gpe_bit, ACPI_NOT_ISR); + + status = acpi_install_address_space_handler (ec->handle, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, + &acpi_ec_space_setup, ec); + if (ACPI_FAILURE(status)) { + acpi_remove_gpe_handler(NULL, ec->gpe_bit, &acpi_ec_gpe_handler); + return_VALUE(-ENODEV); + } + + return_VALUE(AE_OK); +} + + +static int +acpi_ec_stop ( + struct acpi_device *device, + int type) +{ + acpi_status status = AE_OK; + struct acpi_ec *ec = NULL; + + ACPI_FUNCTION_TRACE("acpi_ec_stop"); + + if (!device) + return_VALUE(-EINVAL); + + ec = acpi_driver_data(device); + + status = acpi_remove_address_space_handler(ec->handle, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + status = acpi_remove_gpe_handler(NULL, ec->gpe_bit, &acpi_ec_gpe_handler); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + return_VALUE(0); +} + +static acpi_status __init +acpi_fake_ecdt_callback ( + acpi_handle handle, + u32 Level, + void *context, + void **retval) +{ + acpi_status status; + + status = acpi_walk_resources(handle, METHOD_NAME__CRS, + acpi_ec_io_ports, ec_ecdt); + if (ACPI_FAILURE(status)) + return status; + ec_ecdt->status_addr = ec_ecdt->command_addr; + + ec_ecdt->uid = -1; + acpi_evaluate_integer(handle, "_UID", NULL, &ec_ecdt->uid); + + status = acpi_evaluate_integer(handle, "_GPE", NULL, &ec_ecdt->gpe_bit); + if (ACPI_FAILURE(status)) + return status; + spin_lock_init(&ec_ecdt->lock); + ec_ecdt->global_lock = TRUE; + ec_ecdt->handle = handle; + + printk(KERN_INFO PREFIX "GPE=0x%02x, ports=0x%2x, 0x%2x\n", + (u32) ec_ecdt->gpe_bit, (u32) ec_ecdt->command_addr.address, + (u32) ec_ecdt->data_addr.address); + + return AE_CTRL_TERMINATE; +} + +/* + * Some BIOS (such as some from Gateway laptops) access EC region very early + * such as in BAT0._INI or EC._INI before an EC device is found and + * do not provide an ECDT. According to ACPI spec, ECDT isn't mandatorily + * required, but if EC regison is accessed early, it is required. + * The routine tries to workaround the BIOS bug by pre-scan EC device + * It assumes that _CRS, _HID, _GPE, _UID methods of EC don't touch any + * op region (since _REG isn't invoked yet). The assumption is true for + * all systems found. + */ +static int __init +acpi_ec_fake_ecdt(void) +{ + acpi_status status; + int ret = 0; + + printk(KERN_INFO PREFIX "Try to make an fake ECDT\n"); + + ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + if (!ec_ecdt) { + ret = -ENOMEM; + goto error; + } + memset(ec_ecdt, 0, sizeof(struct acpi_ec)); + + status = acpi_get_devices (ACPI_EC_HID, + acpi_fake_ecdt_callback, + NULL, + NULL); + if (ACPI_FAILURE(status)) { + kfree(ec_ecdt); + ec_ecdt = NULL; + ret = -ENODEV; + goto error; + } + return 0; +error: + printk(KERN_ERR PREFIX "Can't make an fake ECDT\n"); + return ret; +} + +static int __init +acpi_ec_get_real_ecdt(void) +{ + acpi_status status; + struct acpi_table_ecdt *ecdt_ptr; + + status = acpi_get_firmware_table("ECDT", 1, ACPI_LOGICAL_ADDRESSING, + (struct acpi_table_header **) &ecdt_ptr); + if (ACPI_FAILURE(status)) + return -ENODEV; + + printk(KERN_INFO PREFIX "Found ECDT\n"); + + /* + * Generate a temporary ec context to use until the namespace is scanned + */ + ec_ecdt = kmalloc(sizeof(struct acpi_ec), GFP_KERNEL); + if (!ec_ecdt) + return -ENOMEM; + memset(ec_ecdt, 0, sizeof(struct acpi_ec)); + + ec_ecdt->command_addr = ecdt_ptr->ec_control; + ec_ecdt->status_addr = ecdt_ptr->ec_control; + ec_ecdt->data_addr = ecdt_ptr->ec_data; + ec_ecdt->gpe_bit = ecdt_ptr->gpe_bit; + spin_lock_init(&ec_ecdt->lock); + /* use the GL just to be safe */ + ec_ecdt->global_lock = TRUE; + ec_ecdt->uid = ecdt_ptr->uid; + + status = acpi_get_handle(NULL, ecdt_ptr->ec_id, &ec_ecdt->handle); + if (ACPI_FAILURE(status)) { + goto error; + } + + return 0; +error: + printk(KERN_ERR PREFIX "Could not use ECDT\n"); + kfree(ec_ecdt); + ec_ecdt = NULL; + + return -ENODEV; +} + +static int __initdata acpi_fake_ecdt_enabled; +int __init +acpi_ec_ecdt_probe (void) +{ + acpi_status status; + int ret; + + ret = acpi_ec_get_real_ecdt(); + /* Try to make a fake ECDT */ + if (ret && acpi_fake_ecdt_enabled) { + ret = acpi_ec_fake_ecdt(); + } + + if (ret) + return 0; + + /* + * Install GPE handler + */ + status = acpi_install_gpe_handler(NULL, ec_ecdt->gpe_bit, + ACPI_GPE_EDGE_TRIGGERED, &acpi_ec_gpe_handler, + ec_ecdt); + if (ACPI_FAILURE(status)) { + goto error; + } + acpi_set_gpe_type (NULL, ec_ecdt->gpe_bit, ACPI_GPE_TYPE_RUNTIME); + acpi_enable_gpe (NULL, ec_ecdt->gpe_bit, ACPI_NOT_ISR); + + status = acpi_install_address_space_handler (ACPI_ROOT_OBJECT, + ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, + &acpi_ec_space_setup, ec_ecdt); + if (ACPI_FAILURE(status)) { + acpi_remove_gpe_handler(NULL, ec_ecdt->gpe_bit, + &acpi_ec_gpe_handler); + goto error; + } + + return 0; + +error: + printk(KERN_ERR PREFIX "Could not use ECDT\n"); + kfree(ec_ecdt); + ec_ecdt = NULL; + + return -ENODEV; +} + + +static int __init acpi_ec_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_ec_init"); + + if (acpi_disabled) + return_VALUE(0); + + acpi_ec_dir = proc_mkdir(ACPI_EC_CLASS, acpi_root_dir); + if (!acpi_ec_dir) + return_VALUE(-ENODEV); + + /* Now register the driver for the EC */ + result = acpi_bus_register_driver(&acpi_ec_driver); + if (result < 0) { + remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir); + return_VALUE(-ENODEV); + } + + return_VALUE(result); +} + +subsys_initcall(acpi_ec_init); + +/* EC driver currently not unloadable */ +#if 0 +static void __exit +acpi_ec_exit (void) +{ + ACPI_FUNCTION_TRACE("acpi_ec_exit"); + + acpi_bus_unregister_driver(&acpi_ec_driver); + + remove_proc_entry(ACPI_EC_CLASS, acpi_root_dir); + + return_VOID; +} +#endif /* 0 */ + +static int __init acpi_fake_ecdt_setup(char *str) +{ + acpi_fake_ecdt_enabled = 1; + return 0; +} +__setup("acpi_fake_ecdt", acpi_fake_ecdt_setup); diff --git a/drivers/acpi/event.c b/drivers/acpi/event.c new file mode 100644 index 00000000000..43c49f66a32 --- /dev/null +++ b/drivers/acpi/event.c @@ -0,0 +1,140 @@ +/* + * event.c - exporting ACPI events via procfs + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + */ + +#include <linux/spinlock.h> +#include <linux/proc_fs.h> +#include <linux/init.h> +#include <linux/poll.h> +#include <acpi/acpi_drivers.h> + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME ("event") + +/* Global vars for handling event proc entry */ +static DEFINE_SPINLOCK(acpi_system_event_lock); +int event_is_open = 0; +extern struct list_head acpi_bus_event_list; +extern wait_queue_head_t acpi_bus_event_queue; + +static int +acpi_system_open_event(struct inode *inode, struct file *file) +{ + spin_lock_irq (&acpi_system_event_lock); + + if(event_is_open) + goto out_busy; + + event_is_open = 1; + + spin_unlock_irq (&acpi_system_event_lock); + return 0; + +out_busy: + spin_unlock_irq (&acpi_system_event_lock); + return -EBUSY; +} + +static ssize_t +acpi_system_read_event ( + struct file *file, + char __user *buffer, + size_t count, + loff_t *ppos) +{ + int result = 0; + struct acpi_bus_event event; + static char str[ACPI_MAX_STRING]; + static int chars_remaining = 0; + static char *ptr; + + + ACPI_FUNCTION_TRACE("acpi_system_read_event"); + + if (!chars_remaining) { + memset(&event, 0, sizeof(struct acpi_bus_event)); + + if ((file->f_flags & O_NONBLOCK) + && (list_empty(&acpi_bus_event_list))) + return_VALUE(-EAGAIN); + + result = acpi_bus_receive_event(&event); + if (result) { + return_VALUE(-EIO); + } + + chars_remaining = sprintf(str, "%s %s %08x %08x\n", + event.device_class?event.device_class:"<unknown>", + event.bus_id?event.bus_id:"<unknown>", + event.type, event.data); + ptr = str; + } + + if (chars_remaining < count) { + count = chars_remaining; + } + + if (copy_to_user(buffer, ptr, count)) + return_VALUE(-EFAULT); + + *ppos += count; + chars_remaining -= count; + ptr += count; + + return_VALUE(count); +} + +static int +acpi_system_close_event(struct inode *inode, struct file *file) +{ + spin_lock_irq (&acpi_system_event_lock); + event_is_open = 0; + spin_unlock_irq (&acpi_system_event_lock); + return 0; +} + +static unsigned int +acpi_system_poll_event( + struct file *file, + poll_table *wait) +{ + poll_wait(file, &acpi_bus_event_queue, wait); + if (!list_empty(&acpi_bus_event_list)) + return POLLIN | POLLRDNORM; + return 0; +} + +static struct file_operations acpi_system_event_ops = { + .open = acpi_system_open_event, + .read = acpi_system_read_event, + .release = acpi_system_close_event, + .poll = acpi_system_poll_event, +}; + +static int __init acpi_event_init(void) +{ + struct proc_dir_entry *entry; + int error = 0; + + ACPI_FUNCTION_TRACE("acpi_event_init"); + + if (acpi_disabled) + return_VALUE(0); + + /* 'event' [R] */ + entry = create_proc_entry("event", S_IRUSR, acpi_root_dir); + if (entry) + entry->proc_fops = &acpi_system_event_ops; + else { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' proc fs entry\n","event" )); + error = -EFAULT; + } + return_VALUE(error); +} + +subsys_initcall(acpi_event_init); diff --git a/drivers/acpi/events/Makefile b/drivers/acpi/events/Makefile new file mode 100644 index 00000000000..d29f2ee449c --- /dev/null +++ b/drivers/acpi/events/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for all Linux ACPI interpreter subdirectories +# + +obj-y := evevent.o evregion.o evsci.o evxfevnt.o \ + evmisc.o evrgnini.o evxface.o evxfregn.o \ + evgpe.o evgpeblk.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/events/evevent.c b/drivers/acpi/events/evevent.c new file mode 100644 index 00000000000..2a213604ae5 --- /dev/null +++ b/drivers/acpi/events/evevent.c @@ -0,0 +1,297 @@ +/****************************************************************************** + * + * Module Name: evevent - Fixed Event handling and dispatch + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <acpi/acpi.h> +#include <acpi/acevents.h> + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evevent") + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_initialize_events + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize global data structures for events. + * + ******************************************************************************/ + +acpi_status +acpi_ev_initialize_events ( + void) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_initialize_events"); + + + /* Make sure we have ACPI tables */ + + if (!acpi_gbl_DSDT) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "No ACPI tables present!\n")); + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + /* + * Initialize the Fixed and General Purpose Events. This is + * done prior to enabling SCIs to prevent interrupts from + * occurring before handers are installed. + */ + status = acpi_ev_fixed_event_initialize (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "Unable to initialize fixed events, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + status = acpi_ev_gpe_initialize (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "Unable to initialize general purpose events, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_xrupt_handlers + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Install interrupt handlers for the SCI and Global Lock + * + ******************************************************************************/ + +acpi_status +acpi_ev_install_xrupt_handlers ( + void) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_install_xrupt_handlers"); + + + /* Install the SCI handler */ + + status = acpi_ev_install_sci_handler (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "Unable to install System Control Interrupt Handler, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + /* Install the handler for the Global Lock */ + + status = acpi_ev_init_global_lock_handler (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "Unable to initialize Global Lock handler, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + acpi_gbl_events_initialized = TRUE; + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_fixed_event_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Install the fixed event handlers and enable the fixed events. + * + ******************************************************************************/ + +acpi_status +acpi_ev_fixed_event_initialize ( + void) +{ + acpi_native_uint i; + acpi_status status; + + + /* + * Initialize the structure that keeps track of fixed event handlers + * and enable the fixed events. + */ + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + acpi_gbl_fixed_event_handlers[i].handler = NULL; + acpi_gbl_fixed_event_handlers[i].context = NULL; + + /* Enable the fixed event */ + + if (acpi_gbl_fixed_event_info[i].enable_register_id != 0xFF) { + status = acpi_set_register (acpi_gbl_fixed_event_info[i].enable_register_id, + 0, ACPI_MTX_LOCK); + if (ACPI_FAILURE (status)) { + return (status); + } + } + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_fixed_event_detect + * + * PARAMETERS: None + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Checks the PM status register for fixed events + * + ******************************************************************************/ + +u32 +acpi_ev_fixed_event_detect ( + void) +{ + u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; + u32 fixed_status; + u32 fixed_enable; + acpi_native_uint i; + + + ACPI_FUNCTION_NAME ("ev_fixed_event_detect"); + + + /* + * Read the fixed feature status and enable registers, as all the cases + * depend on their values. Ignore errors here. + */ + (void) acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1_STATUS, &fixed_status); + (void) acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1_ENABLE, &fixed_enable); + + ACPI_DEBUG_PRINT ((ACPI_DB_INTERRUPTS, + "Fixed Event Block: Enable %08X Status %08X\n", + fixed_enable, fixed_status)); + + /* + * Check for all possible Fixed Events and dispatch those that are active + */ + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + /* Both the status and enable bits must be on for this event */ + + if ((fixed_status & acpi_gbl_fixed_event_info[i].status_bit_mask) && + (fixed_enable & acpi_gbl_fixed_event_info[i].enable_bit_mask)) { + /* Found an active (signalled) event */ + + int_status |= acpi_ev_fixed_event_dispatch ((u32) i); + } + } + + return (int_status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_fixed_event_dispatch + * + * PARAMETERS: Event - Event type + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Clears the status bit for the requested event, calls the + * handler that previously registered for the event. + * + ******************************************************************************/ + +u32 +acpi_ev_fixed_event_dispatch ( + u32 event) +{ + + + ACPI_FUNCTION_ENTRY (); + + + /* Clear the status bit */ + + (void) acpi_set_register (acpi_gbl_fixed_event_info[event].status_register_id, + 1, ACPI_MTX_DO_NOT_LOCK); + + /* + * Make sure we've got a handler. If not, report an error. + * The event is disabled to prevent further interrupts. + */ + if (NULL == acpi_gbl_fixed_event_handlers[event].handler) { + (void) acpi_set_register (acpi_gbl_fixed_event_info[event].enable_register_id, + 0, ACPI_MTX_DO_NOT_LOCK); + + ACPI_REPORT_ERROR ( + ("No installed handler for fixed event [%08X]\n", + event)); + + return (ACPI_INTERRUPT_NOT_HANDLED); + } + + /* Invoke the Fixed Event handler */ + + return ((acpi_gbl_fixed_event_handlers[event].handler)( + acpi_gbl_fixed_event_handlers[event].context)); +} + + diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c new file mode 100644 index 00000000000..118d72ac7c7 --- /dev/null +++ b/drivers/acpi/events/evgpe.c @@ -0,0 +1,756 @@ +/****************************************************************************** + * + * Module Name: evgpe - General Purpose Event handling and dispatch + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <acpi/acpi.h> +#include <acpi/acevents.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evgpe") + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_set_gpe_type + * + * PARAMETERS: gpe_event_info - GPE to set + * Type - New type + * + * RETURN: Status + * + * DESCRIPTION: Sets the new type for the GPE (wake, run, or wake/run) + * + ******************************************************************************/ + +acpi_status +acpi_ev_set_gpe_type ( + struct acpi_gpe_event_info *gpe_event_info, + u8 type) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_set_gpe_type"); + + + /* Validate type and update register enable masks */ + + switch (type) { + case ACPI_GPE_TYPE_WAKE: + case ACPI_GPE_TYPE_RUNTIME: + case ACPI_GPE_TYPE_WAKE_RUN: + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Disable the GPE if currently enabled */ + + status = acpi_ev_disable_gpe (gpe_event_info); + + /* Type was validated above */ + + gpe_event_info->flags &= ~ACPI_GPE_TYPE_MASK; /* Clear type bits */ + gpe_event_info->flags |= type; /* Insert type */ + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_update_gpe_enable_masks + * + * PARAMETERS: gpe_event_info - GPE to update + * Type - What to do: ACPI_GPE_DISABLE or + * ACPI_GPE_ENABLE + * + * RETURN: Status + * + * DESCRIPTION: Updates GPE register enable masks based on the GPE type + * + ******************************************************************************/ + +acpi_status +acpi_ev_update_gpe_enable_masks ( + struct acpi_gpe_event_info *gpe_event_info, + u8 type) +{ + struct acpi_gpe_register_info *gpe_register_info; + u8 register_bit; + + + ACPI_FUNCTION_TRACE ("ev_update_gpe_enable_masks"); + + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + register_bit = gpe_event_info->register_bit; + + /* 1) Disable case. Simply clear all enable bits */ + + if (type == ACPI_GPE_DISABLE) { + ACPI_CLEAR_BIT (gpe_register_info->enable_for_wake, register_bit); + ACPI_CLEAR_BIT (gpe_register_info->enable_for_run, register_bit); + return_ACPI_STATUS (AE_OK); + } + + /* 2) Enable case. Set/Clear the appropriate enable bits */ + + switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { + case ACPI_GPE_TYPE_WAKE: + ACPI_SET_BIT (gpe_register_info->enable_for_wake, register_bit); + ACPI_CLEAR_BIT (gpe_register_info->enable_for_run, register_bit); + break; + + case ACPI_GPE_TYPE_RUNTIME: + ACPI_CLEAR_BIT (gpe_register_info->enable_for_wake, register_bit); + ACPI_SET_BIT (gpe_register_info->enable_for_run, register_bit); + break; + + case ACPI_GPE_TYPE_WAKE_RUN: + ACPI_SET_BIT (gpe_register_info->enable_for_wake, register_bit); + ACPI_SET_BIT (gpe_register_info->enable_for_run, register_bit); + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_enable_gpe + * + * PARAMETERS: gpe_event_info - GPE to enable + * write_to_hardware - Enable now, or just mark data structs + * (WAKE GPEs should be deferred) + * + * RETURN: Status + * + * DESCRIPTION: Enable a GPE based on the GPE type + * + ******************************************************************************/ + +acpi_status +acpi_ev_enable_gpe ( + struct acpi_gpe_event_info *gpe_event_info, + u8 write_to_hardware) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_enable_gpe"); + + + /* Make sure HW enable masks are updated */ + + status = acpi_ev_update_gpe_enable_masks (gpe_event_info, ACPI_GPE_ENABLE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Mark wake-enabled or HW enable, or both */ + + switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { + case ACPI_GPE_TYPE_WAKE: + + ACPI_SET_BIT (gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); + break; + + case ACPI_GPE_TYPE_WAKE_RUN: + + ACPI_SET_BIT (gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); + + /*lint -fallthrough */ + + case ACPI_GPE_TYPE_RUNTIME: + + ACPI_SET_BIT (gpe_event_info->flags, ACPI_GPE_RUN_ENABLED); + + if (write_to_hardware) { + /* Clear the GPE (of stale events), then enable it */ + + status = acpi_hw_clear_gpe (gpe_event_info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Enable the requested runtime GPE */ + + status = acpi_hw_write_gpe_enable_reg (gpe_event_info); + } + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_disable_gpe + * + * PARAMETERS: gpe_event_info - GPE to disable + * + * RETURN: Status + * + * DESCRIPTION: Disable a GPE based on the GPE type + * + ******************************************************************************/ + +acpi_status +acpi_ev_disable_gpe ( + struct acpi_gpe_event_info *gpe_event_info) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_disable_gpe"); + + + if (!(gpe_event_info->flags & ACPI_GPE_ENABLE_MASK)) { + return_ACPI_STATUS (AE_OK); + } + + /* Make sure HW enable masks are updated */ + + status = acpi_ev_update_gpe_enable_masks (gpe_event_info, ACPI_GPE_DISABLE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Mark wake-disabled or HW disable, or both */ + + switch (gpe_event_info->flags & ACPI_GPE_TYPE_MASK) { + case ACPI_GPE_TYPE_WAKE: + ACPI_CLEAR_BIT (gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); + break; + + case ACPI_GPE_TYPE_WAKE_RUN: + ACPI_CLEAR_BIT (gpe_event_info->flags, ACPI_GPE_WAKE_ENABLED); + + /*lint -fallthrough */ + + case ACPI_GPE_TYPE_RUNTIME: + + /* Disable the requested runtime GPE */ + + ACPI_CLEAR_BIT (gpe_event_info->flags, ACPI_GPE_RUN_ENABLED); + status = acpi_hw_write_gpe_enable_reg (gpe_event_info); + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_get_gpe_event_info + * + * PARAMETERS: gpe_device - Device node. NULL for GPE0/GPE1 + * gpe_number - Raw GPE number + * + * RETURN: A GPE event_info struct. NULL if not a valid GPE + * + * DESCRIPTION: Returns the event_info struct associated with this GPE. + * Validates the gpe_block and the gpe_number + * + * Should be called only when the GPE lists are semaphore locked + * and not subject to change. + * + ******************************************************************************/ + +struct acpi_gpe_event_info * +acpi_ev_get_gpe_event_info ( + acpi_handle gpe_device, + u32 gpe_number) +{ + union acpi_operand_object *obj_desc; + struct acpi_gpe_block_info *gpe_block; + acpi_native_uint i; + + + ACPI_FUNCTION_ENTRY (); + + + /* A NULL gpe_block means use the FADT-defined GPE block(s) */ + + if (!gpe_device) { + /* Examine GPE Block 0 and 1 (These blocks are permanent) */ + + for (i = 0; i < ACPI_MAX_GPE_BLOCKS; i++) { + gpe_block = acpi_gbl_gpe_fadt_blocks[i]; + if (gpe_block) { + if ((gpe_number >= gpe_block->block_base_number) && + (gpe_number < gpe_block->block_base_number + (gpe_block->register_count * 8))) { + return (&gpe_block->event_info[gpe_number - gpe_block->block_base_number]); + } + } + } + + /* The gpe_number was not in the range of either FADT GPE block */ + + return (NULL); + } + + /* A Non-NULL gpe_device means this is a GPE Block Device */ + + obj_desc = acpi_ns_get_attached_object ((struct acpi_namespace_node *) gpe_device); + if (!obj_desc || + !obj_desc->device.gpe_block) { + return (NULL); + } + + gpe_block = obj_desc->device.gpe_block; + + if ((gpe_number >= gpe_block->block_base_number) && + (gpe_number < gpe_block->block_base_number + (gpe_block->register_count * 8))) { + return (&gpe_block->event_info[gpe_number - gpe_block->block_base_number]); + } + + return (NULL); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_gpe_detect + * + * PARAMETERS: gpe_xrupt_list - Interrupt block for this interrupt. + * Can have multiple GPE blocks attached. + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Detect if any GP events have occurred. This function is + * executed at interrupt level. + * + ******************************************************************************/ + +u32 +acpi_ev_gpe_detect ( + struct acpi_gpe_xrupt_info *gpe_xrupt_list) +{ + u32 int_status = ACPI_INTERRUPT_NOT_HANDLED; + u8 enabled_status_byte; + struct acpi_gpe_register_info *gpe_register_info; + u32 status_reg; + u32 enable_reg; + acpi_status status; + struct acpi_gpe_block_info *gpe_block; + acpi_native_uint i; + acpi_native_uint j; + + + ACPI_FUNCTION_NAME ("ev_gpe_detect"); + + /* Check for the case where there are no GPEs */ + + if (!gpe_xrupt_list) { + return (int_status); + } + + /* Examine all GPE blocks attached to this interrupt level */ + + acpi_os_acquire_lock (acpi_gbl_gpe_lock, ACPI_ISR); + gpe_block = gpe_xrupt_list->gpe_block_list_head; + while (gpe_block) { + /* + * Read all of the 8-bit GPE status and enable registers + * in this GPE block, saving all of them. + * Find all currently active GP events. + */ + for (i = 0; i < gpe_block->register_count; i++) { + /* Get the next status/enable pair */ + + gpe_register_info = &gpe_block->register_info[i]; + + /* Read the Status Register */ + + status = acpi_hw_low_level_read (ACPI_GPE_REGISTER_WIDTH, &status_reg, + &gpe_register_info->status_address); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* Read the Enable Register */ + + status = acpi_hw_low_level_read (ACPI_GPE_REGISTER_WIDTH, &enable_reg, + &gpe_register_info->enable_address); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INTERRUPTS, + "Read GPE Register at GPE%X: Status=%02X, Enable=%02X\n", + gpe_register_info->base_gpe_number, status_reg, enable_reg)); + + /* First check if there is anything active at all in this register */ + + enabled_status_byte = (u8) (status_reg & enable_reg); + if (!enabled_status_byte) { + /* No active GPEs in this register, move on */ + + continue; + } + + /* Now look at the individual GPEs in this byte register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + /* Examine one GPE bit */ + + if (enabled_status_byte & acpi_gbl_decode_to8bit[j]) { + /* + * Found an active GPE. Dispatch the event to a handler + * or method. + */ + int_status |= acpi_ev_gpe_dispatch ( + &gpe_block->event_info[(i * ACPI_GPE_REGISTER_WIDTH) + j], + (u32) j + gpe_register_info->base_gpe_number); + } + } + } + + gpe_block = gpe_block->next; + } + +unlock_and_exit: + + acpi_os_release_lock (acpi_gbl_gpe_lock, ACPI_ISR); + return (int_status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_asynch_execute_gpe_method + * + * PARAMETERS: Context (gpe_event_info) - Info for this GPE + * + * RETURN: None + * + * DESCRIPTION: Perform the actual execution of a GPE control method. This + * function is called from an invocation of acpi_os_queue_for_execution + * (and therefore does NOT execute at interrupt level) so that + * the control method itself is not executed in the context of + * an interrupt handler. + * + ******************************************************************************/ + +static void ACPI_SYSTEM_XFACE +acpi_ev_asynch_execute_gpe_method ( + void *context) +{ + struct acpi_gpe_event_info *gpe_event_info = (void *) context; + u32 gpe_number = 0; + acpi_status status; + struct acpi_gpe_event_info local_gpe_event_info; + struct acpi_parameter_info info; + + + ACPI_FUNCTION_TRACE ("ev_asynch_execute_gpe_method"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_VOID; + } + + /* Must revalidate the gpe_number/gpe_block */ + + if (!acpi_ev_valid_gpe_event (gpe_event_info)) { + status = acpi_ut_release_mutex (ACPI_MTX_EVENTS); + return_VOID; + } + + /* Set the GPE flags for return to enabled state */ + + (void) acpi_ev_enable_gpe (gpe_event_info, FALSE); + + /* + * Take a snapshot of the GPE info for this level - we copy the + * info to prevent a race condition with remove_handler/remove_block. + */ + ACPI_MEMCPY (&local_gpe_event_info, gpe_event_info, sizeof (struct acpi_gpe_event_info)); + + status = acpi_ut_release_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_VOID; + } + + /* + * Must check for control method type dispatch one more + * time to avoid race with ev_gpe_install_handler + */ + if ((local_gpe_event_info.flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_METHOD) { + /* + * Invoke the GPE Method (_Lxx, _Exx) i.e., evaluate the _Lxx/_Exx + * control method that corresponds to this GPE + */ + info.node = local_gpe_event_info.dispatch.method_node; + info.parameters = ACPI_CAST_PTR (union acpi_operand_object *, gpe_event_info); + info.parameter_type = ACPI_PARAM_GPE; + + status = acpi_ns_evaluate_by_handle (&info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "%s while evaluating method [%4.4s] for GPE[%2X]\n", + acpi_format_exception (status), + acpi_ut_get_node_name (local_gpe_event_info.dispatch.method_node), + gpe_number)); + } + } + + if ((local_gpe_event_info.flags & ACPI_GPE_XRUPT_TYPE_MASK) == ACPI_GPE_LEVEL_TRIGGERED) { + /* + * GPE is level-triggered, we clear the GPE status bit after + * handling the event. + */ + status = acpi_hw_clear_gpe (&local_gpe_event_info); + if (ACPI_FAILURE (status)) { + return_VOID; + } + } + + /* Enable this GPE */ + + (void) acpi_hw_write_gpe_enable_reg (&local_gpe_event_info); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_gpe_dispatch + * + * PARAMETERS: gpe_event_info - info for this GPE + * gpe_number - Number relative to the parent GPE block + * + * RETURN: INTERRUPT_HANDLED or INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Dispatch a General Purpose Event to either a function (e.g. EC) + * or method (e.g. _Lxx/_Exx) handler. + * + * This function executes at interrupt level. + * + ******************************************************************************/ + +u32 +acpi_ev_gpe_dispatch ( + struct acpi_gpe_event_info *gpe_event_info, + u32 gpe_number) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_gpe_dispatch"); + + + /* + * If edge-triggered, clear the GPE status bit now. Note that + * level-triggered events are cleared after the GPE is serviced. + */ + if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == ACPI_GPE_EDGE_TRIGGERED) { + status = acpi_hw_clear_gpe (gpe_event_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("acpi_ev_gpe_dispatch: %s, Unable to clear GPE[%2X]\n", + acpi_format_exception (status), gpe_number)); + return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); + } + } + + /* Save current system state */ + + if (acpi_gbl_system_awake_and_running) { + ACPI_SET_BIT (gpe_event_info->flags, ACPI_GPE_SYSTEM_RUNNING); + } + else { + ACPI_CLEAR_BIT (gpe_event_info->flags, ACPI_GPE_SYSTEM_RUNNING); + } + + /* + * Dispatch the GPE to either an installed handler, or the control + * method associated with this GPE (_Lxx or _Exx). + * If a handler exists, we invoke it and do not attempt to run the method. + * If there is neither a handler nor a method, we disable the level to + * prevent further events from coming in here. + */ + switch (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) { + case ACPI_GPE_DISPATCH_HANDLER: + + /* + * Invoke the installed handler (at interrupt level) + * Ignore return status for now. TBD: leave GPE disabled on error? + */ + (void) gpe_event_info->dispatch.handler->address ( + gpe_event_info->dispatch.handler->context); + + /* It is now safe to clear level-triggered events. */ + + if ((gpe_event_info->flags & ACPI_GPE_XRUPT_TYPE_MASK) == ACPI_GPE_LEVEL_TRIGGERED) { + status = acpi_hw_clear_gpe (gpe_event_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "acpi_ev_gpe_dispatch: %s, Unable to clear GPE[%2X]\n", + acpi_format_exception (status), gpe_number)); + return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); + } + } + break; + + case ACPI_GPE_DISPATCH_METHOD: + + /* + * Disable GPE, so it doesn't keep firing before the method has a + * chance to run. + */ + status = acpi_ev_disable_gpe (gpe_event_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "acpi_ev_gpe_dispatch: %s, Unable to disable GPE[%2X]\n", + acpi_format_exception (status), gpe_number)); + return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); + } + + /* + * Execute the method associated with the GPE + * NOTE: Level-triggered GPEs are cleared after the method completes. + */ + status = acpi_os_queue_for_execution (OSD_PRIORITY_GPE, + acpi_ev_asynch_execute_gpe_method, gpe_event_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "acpi_ev_gpe_dispatch: %s, Unable to queue handler for GPE[%2X] - event disabled\n", + acpi_format_exception (status), gpe_number)); + } + break; + + default: + + /* No handler or method to run! */ + + ACPI_REPORT_ERROR (( + "acpi_ev_gpe_dispatch: No handler or method for GPE[%2X], disabling event\n", + gpe_number)); + + /* + * Disable the GPE. The GPE will remain disabled until the ACPI + * Core Subsystem is restarted, or a handler is installed. + */ + status = acpi_ev_disable_gpe (gpe_event_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "acpi_ev_gpe_dispatch: %s, Unable to disable GPE[%2X]\n", + acpi_format_exception (status), gpe_number)); + return_VALUE (ACPI_INTERRUPT_NOT_HANDLED); + } + break; + } + + return_VALUE (ACPI_INTERRUPT_HANDLED); +} + + +#ifdef ACPI_GPE_NOTIFY_CHECK + +/******************************************************************************* + * TBD: NOT USED, PROTOTYPE ONLY AND WILL PROBABLY BE REMOVED + * + * FUNCTION: acpi_ev_check_for_wake_only_gpe + * + * PARAMETERS: gpe_event_info - info for this GPE + * + * RETURN: Status + * + * DESCRIPTION: Determine if a a GPE is "wake-only". + * + * Called from Notify() code in interpreter when a "device_wake" + * Notify comes in. + * + ******************************************************************************/ + +acpi_status +acpi_ev_check_for_wake_only_gpe ( + struct acpi_gpe_event_info *gpe_event_info) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_check_for_wake_only_gpe"); + + + if ((gpe_event_info) && /* Only >0 for _Lxx/_Exx */ + ((gpe_event_info->flags & ACPI_GPE_SYSTEM_MASK) == ACPI_GPE_SYSTEM_RUNNING)) /* System state at GPE time */ { + /* This must be a wake-only GPE, disable it */ + + status = acpi_ev_disable_gpe (gpe_event_info); + + /* Set GPE to wake-only. Do not change wake disabled/enabled status */ + + acpi_ev_set_gpe_type (gpe_event_info, ACPI_GPE_TYPE_WAKE); + + ACPI_REPORT_INFO (("GPE %p was updated from wake/run to wake-only\n", + gpe_event_info)); + + /* This was a wake-only GPE */ + + return_ACPI_STATUS (AE_WAKE_ONLY_GPE); + } + + return_ACPI_STATUS (AE_OK); +} +#endif + + diff --git a/drivers/acpi/events/evgpeblk.c b/drivers/acpi/events/evgpeblk.c new file mode 100644 index 00000000000..00d981f53c6 --- /dev/null +++ b/drivers/acpi/events/evgpeblk.c @@ -0,0 +1,1141 @@ +/****************************************************************************** + * + * Module Name: evgpeblk - GPE block creation and initialization. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <acpi/acpi.h> +#include <acpi/acevents.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evgpeblk") + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_valid_gpe_event + * + * PARAMETERS: gpe_event_info - Info for this GPE + * + * RETURN: TRUE if the gpe_event is valid + * + * DESCRIPTION: Validate a GPE event. DO NOT CALL FROM INTERRUPT LEVEL. + * Should be called only when the GPE lists are semaphore locked + * and not subject to change. + * + ******************************************************************************/ + +u8 +acpi_ev_valid_gpe_event ( + struct acpi_gpe_event_info *gpe_event_info) +{ + struct acpi_gpe_xrupt_info *gpe_xrupt_block; + struct acpi_gpe_block_info *gpe_block; + + + ACPI_FUNCTION_ENTRY (); + + + /* No need for spin lock since we are not changing any list elements */ + + /* Walk the GPE interrupt levels */ + + gpe_xrupt_block = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_block) { + gpe_block = gpe_xrupt_block->gpe_block_list_head; + + /* Walk the GPE blocks on this interrupt level */ + + while (gpe_block) { + if ((&gpe_block->event_info[0] <= gpe_event_info) && + (&gpe_block->event_info[((acpi_size) gpe_block->register_count) * 8] > gpe_event_info)) { + return (TRUE); + } + + gpe_block = gpe_block->next; + } + + gpe_xrupt_block = gpe_xrupt_block->next; + } + + return (FALSE); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_walk_gpe_list + * + * PARAMETERS: gpe_walk_callback - Routine called for each GPE block + * Flags - ACPI_NOT_ISR or ACPI_ISR + * + * RETURN: Status + * + * DESCRIPTION: Walk the GPE lists. + * + ******************************************************************************/ + +acpi_status +acpi_ev_walk_gpe_list ( + ACPI_GPE_CALLBACK gpe_walk_callback, + u32 flags) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ev_walk_gpe_list"); + + + acpi_os_acquire_lock (acpi_gbl_gpe_lock, flags); + + /* Walk the interrupt level descriptor list */ + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + /* Walk all Gpe Blocks attached to this interrupt level */ + + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + /* One callback per GPE block */ + + status = gpe_walk_callback (gpe_xrupt_info, gpe_block); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + gpe_block = gpe_block->next; + } + + gpe_xrupt_info = gpe_xrupt_info->next; + } + +unlock_and_exit: + acpi_os_release_lock (acpi_gbl_gpe_lock, flags); + return_ACPI_STATUS (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_ev_delete_gpe_handlers + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Delete all Handler objects found in the GPE data structs. + * Used only prior to termination. + * + ******************************************************************************/ + +acpi_status +acpi_ev_delete_gpe_handlers ( + struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block) +{ + struct acpi_gpe_event_info *gpe_event_info; + acpi_native_uint i; + acpi_native_uint j; + + + ACPI_FUNCTION_TRACE ("ev_delete_gpe_handlers"); + + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + /* Now look at the individual GPEs in this byte register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + gpe_event_info = &gpe_block->event_info[(i * ACPI_GPE_REGISTER_WIDTH) + j]; + + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_HANDLER) { + ACPI_MEM_FREE (gpe_event_info->dispatch.handler); + gpe_event_info->dispatch.handler = NULL; + gpe_event_info->flags &= ~ACPI_GPE_DISPATCH_MASK; + } + } + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_save_method_info + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status + * + * DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a + * control method under the _GPE portion of the namespace. + * Extract the name and GPE type from the object, saving this + * information for quick lookup during GPE dispatch + * + * The name of each GPE control method is of the form: + * "_Lxx" or "_Exx" + * Where: + * L - means that the GPE is level triggered + * E - means that the GPE is edge triggered + * xx - is the GPE number [in HEX] + * + ******************************************************************************/ + +static acpi_status +acpi_ev_save_method_info ( + acpi_handle obj_handle, + u32 level, + void *obj_desc, + void **return_value) +{ + struct acpi_gpe_block_info *gpe_block = (void *) obj_desc; + struct acpi_gpe_event_info *gpe_event_info; + u32 gpe_number; + char name[ACPI_NAME_SIZE + 1]; + u8 type; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_save_method_info"); + + + /* + * _Lxx and _Exx GPE method support + * + * 1) Extract the name from the object and convert to a string + */ + ACPI_MOVE_32_TO_32 (name, + &((struct acpi_namespace_node *) obj_handle)->name.integer); + name[ACPI_NAME_SIZE] = 0; + + /* + * 2) Edge/Level determination is based on the 2nd character + * of the method name + * + * NOTE: Default GPE type is RUNTIME. May be changed later to WAKE + * if a _PRW object is found that points to this GPE. + */ + switch (name[1]) { + case 'L': + type = ACPI_GPE_LEVEL_TRIGGERED; + break; + + case 'E': + type = ACPI_GPE_EDGE_TRIGGERED; + break; + + default: + /* Unknown method type, just ignore it! */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unknown GPE method type: %s (name not of form _Lxx or _Exx)\n", + name)); + return_ACPI_STATUS (AE_OK); + } + + /* Convert the last two characters of the name to the GPE Number */ + + gpe_number = ACPI_STRTOUL (&name[2], NULL, 16); + if (gpe_number == ACPI_UINT32_MAX) { + /* Conversion failed; invalid method, just ignore it */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not extract GPE number from name: %s (name is not of form _Lxx or _Exx)\n", + name)); + return_ACPI_STATUS (AE_OK); + } + + /* Ensure that we have a valid GPE number for this GPE block */ + + if ((gpe_number < gpe_block->block_base_number) || + (gpe_number >= (gpe_block->block_base_number + (gpe_block->register_count * 8)))) { + /* + * Not valid for this GPE block, just ignore it + * However, it may be valid for a different GPE block, since GPE0 and GPE1 + * methods both appear under \_GPE. + */ + return_ACPI_STATUS (AE_OK); + } + + /* + * Now we can add this information to the gpe_event_info block + * for use during dispatch of this GPE. Default type is RUNTIME, although + * this may change when the _PRW methods are executed later. + */ + gpe_event_info = &gpe_block->event_info[gpe_number - gpe_block->block_base_number]; + + gpe_event_info->flags = (u8) (type | ACPI_GPE_DISPATCH_METHOD | + ACPI_GPE_TYPE_RUNTIME); + + gpe_event_info->dispatch.method_node = (struct acpi_namespace_node *) obj_handle; + + /* Update enable mask, but don't enable the HW GPE as of yet */ + + status = acpi_ev_enable_gpe (gpe_event_info, FALSE); + + ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, + "Registered GPE method %s as GPE number 0x%.2X\n", + name, gpe_number)); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_match_prw_and_gpe + * + * PARAMETERS: Callback from walk_namespace + * + * RETURN: Status. NOTE: We ignore errors so that the _PRW walk is + * not aborted on a single _PRW failure. + * + * DESCRIPTION: Called from acpi_walk_namespace. Expects each object to be a + * Device. Run the _PRW method. If present, extract the GPE + * number and mark the GPE as a WAKE GPE. + * + ******************************************************************************/ + +static acpi_status +acpi_ev_match_prw_and_gpe ( + acpi_handle obj_handle, + u32 level, + void *info, + void **return_value) +{ + struct acpi_gpe_walk_info *gpe_info = (void *) info; + struct acpi_namespace_node *gpe_device; + struct acpi_gpe_block_info *gpe_block; + struct acpi_namespace_node *target_gpe_device; + struct acpi_gpe_event_info *gpe_event_info; + union acpi_operand_object *pkg_desc; + union acpi_operand_object *obj_desc; + u32 gpe_number; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_match_prw_and_gpe"); + + + /* Check for a _PRW method under this device */ + + status = acpi_ut_evaluate_object (obj_handle, METHOD_NAME__PRW, + ACPI_BTYPE_PACKAGE, &pkg_desc); + if (ACPI_FAILURE (status)) { + /* Ignore all errors from _PRW, we don't want to abort the subsystem */ + + return_ACPI_STATUS (AE_OK); + } + + /* The returned _PRW package must have at least two elements */ + + if (pkg_desc->package.count < 2) { + goto cleanup; + } + + /* Extract pointers from the input context */ + + gpe_device = gpe_info->gpe_device; + gpe_block = gpe_info->gpe_block; + + /* + * The _PRW object must return a package, we are only interested + * in the first element + */ + obj_desc = pkg_desc->package.elements[0]; + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_INTEGER) { + /* Use FADT-defined GPE device (from definition of _PRW) */ + + target_gpe_device = acpi_gbl_fadt_gpe_device; + + /* Integer is the GPE number in the FADT described GPE blocks */ + + gpe_number = (u32) obj_desc->integer.value; + } + else if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_PACKAGE) { + /* Package contains a GPE reference and GPE number within a GPE block */ + + if ((obj_desc->package.count < 2) || + (ACPI_GET_OBJECT_TYPE (obj_desc->package.elements[0]) != ACPI_TYPE_LOCAL_REFERENCE) || + (ACPI_GET_OBJECT_TYPE (obj_desc->package.elements[1]) != ACPI_TYPE_INTEGER)) { + goto cleanup; + } + + /* Get GPE block reference and decode */ + + target_gpe_device = obj_desc->package.elements[0]->reference.node; + gpe_number = (u32) obj_desc->package.elements[1]->integer.value; + } + else { + /* Unknown type, just ignore it */ + + goto cleanup; + } + + /* + * Is this GPE within this block? + * + * TRUE iff these conditions are true: + * 1) The GPE devices match. + * 2) The GPE index(number) is within the range of the Gpe Block + * associated with the GPE device. + */ + if ((gpe_device == target_gpe_device) && + (gpe_number >= gpe_block->block_base_number) && + (gpe_number < gpe_block->block_base_number + (gpe_block->register_count * 8))) { + gpe_event_info = &gpe_block->event_info[gpe_number - gpe_block->block_base_number]; + + /* Mark GPE for WAKE-ONLY but WAKE_DISABLED */ + + gpe_event_info->flags &= ~(ACPI_GPE_WAKE_ENABLED | ACPI_GPE_RUN_ENABLED); + status = acpi_ev_set_gpe_type (gpe_event_info, ACPI_GPE_TYPE_WAKE); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + status = acpi_ev_update_gpe_enable_masks (gpe_event_info, ACPI_GPE_DISABLE); + } + +cleanup: + acpi_ut_remove_reference (pkg_desc); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_get_gpe_xrupt_block + * + * PARAMETERS: interrupt_level - Interrupt for a GPE block + * + * RETURN: A GPE interrupt block + * + * DESCRIPTION: Get or Create a GPE interrupt block. There is one interrupt + * block per unique interrupt level used for GPEs. + * Should be called only when the GPE lists are semaphore locked + * and not subject to change. + * + ******************************************************************************/ + +static struct acpi_gpe_xrupt_info * +acpi_ev_get_gpe_xrupt_block ( + u32 interrupt_level) +{ + struct acpi_gpe_xrupt_info *next_gpe_xrupt; + struct acpi_gpe_xrupt_info *gpe_xrupt; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_get_gpe_xrupt_block"); + + + /* No need for spin lock since we are not changing any list elements here */ + + next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; + while (next_gpe_xrupt) { + if (next_gpe_xrupt->interrupt_level == interrupt_level) { + return_PTR (next_gpe_xrupt); + } + + next_gpe_xrupt = next_gpe_xrupt->next; + } + + /* Not found, must allocate a new xrupt descriptor */ + + gpe_xrupt = ACPI_MEM_CALLOCATE (sizeof (struct acpi_gpe_xrupt_info)); + if (!gpe_xrupt) { + return_PTR (NULL); + } + + gpe_xrupt->interrupt_level = interrupt_level; + + /* Install new interrupt descriptor with spin lock */ + + acpi_os_acquire_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + if (acpi_gbl_gpe_xrupt_list_head) { + next_gpe_xrupt = acpi_gbl_gpe_xrupt_list_head; + while (next_gpe_xrupt->next) { + next_gpe_xrupt = next_gpe_xrupt->next; + } + + next_gpe_xrupt->next = gpe_xrupt; + gpe_xrupt->previous = next_gpe_xrupt; + } + else { + acpi_gbl_gpe_xrupt_list_head = gpe_xrupt; + } + acpi_os_release_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + + /* Install new interrupt handler if not SCI_INT */ + + if (interrupt_level != acpi_gbl_FADT->sci_int) { + status = acpi_os_install_interrupt_handler (interrupt_level, + acpi_ev_gpe_xrupt_handler, gpe_xrupt); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not install GPE interrupt handler at level 0x%X\n", + interrupt_level)); + return_PTR (NULL); + } + } + + return_PTR (gpe_xrupt); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_delete_gpe_xrupt + * + * PARAMETERS: gpe_xrupt - A GPE interrupt info block + * + * RETURN: Status + * + * DESCRIPTION: Remove and free a gpe_xrupt block. Remove an associated + * interrupt handler if not the SCI interrupt. + * + ******************************************************************************/ + +static acpi_status +acpi_ev_delete_gpe_xrupt ( + struct acpi_gpe_xrupt_info *gpe_xrupt) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_delete_gpe_xrupt"); + + + /* We never want to remove the SCI interrupt handler */ + + if (gpe_xrupt->interrupt_level == acpi_gbl_FADT->sci_int) { + gpe_xrupt->gpe_block_list_head = NULL; + return_ACPI_STATUS (AE_OK); + } + + /* Disable this interrupt */ + + status = acpi_os_remove_interrupt_handler (gpe_xrupt->interrupt_level, + acpi_ev_gpe_xrupt_handler); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Unlink the interrupt block with lock */ + + acpi_os_acquire_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + if (gpe_xrupt->previous) { + gpe_xrupt->previous->next = gpe_xrupt->next; + } + + if (gpe_xrupt->next) { + gpe_xrupt->next->previous = gpe_xrupt->previous; + } + acpi_os_release_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + + /* Free the block */ + + ACPI_MEM_FREE (gpe_xrupt); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_gpe_block + * + * PARAMETERS: gpe_block - New GPE block + * interrupt_level - Level to be associated with this GPE block + * + * RETURN: Status + * + * DESCRIPTION: Install new GPE block with mutex support + * + ******************************************************************************/ + +static acpi_status +acpi_ev_install_gpe_block ( + struct acpi_gpe_block_info *gpe_block, + u32 interrupt_level) +{ + struct acpi_gpe_block_info *next_gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_block; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_install_gpe_block"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + gpe_xrupt_block = acpi_ev_get_gpe_xrupt_block (interrupt_level); + if (!gpe_xrupt_block) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Install the new block at the end of the list for this interrupt with lock */ + + acpi_os_acquire_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + if (gpe_xrupt_block->gpe_block_list_head) { + next_gpe_block = gpe_xrupt_block->gpe_block_list_head; + while (next_gpe_block->next) { + next_gpe_block = next_gpe_block->next; + } + + next_gpe_block->next = gpe_block; + gpe_block->previous = next_gpe_block; + } + else { + gpe_xrupt_block->gpe_block_list_head = gpe_block; + } + + gpe_block->xrupt_block = gpe_xrupt_block; + acpi_os_release_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + +unlock_and_exit: + status = acpi_ut_release_mutex (ACPI_MTX_EVENTS); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_delete_gpe_block + * + * PARAMETERS: gpe_block - Existing GPE block + * + * RETURN: Status + * + * DESCRIPTION: Remove a GPE block + * + ******************************************************************************/ + +acpi_status +acpi_ev_delete_gpe_block ( + struct acpi_gpe_block_info *gpe_block) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_install_gpe_block"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Disable all GPEs in this block */ + + status = acpi_hw_disable_gpe_block (gpe_block->xrupt_block, gpe_block); + + if (!gpe_block->previous && !gpe_block->next) { + /* This is the last gpe_block on this interrupt */ + + status = acpi_ev_delete_gpe_xrupt (gpe_block->xrupt_block); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + } + else { + /* Remove the block on this interrupt with lock */ + + acpi_os_acquire_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + if (gpe_block->previous) { + gpe_block->previous->next = gpe_block->next; + } + else { + gpe_block->xrupt_block->gpe_block_list_head = gpe_block->next; + } + + if (gpe_block->next) { + gpe_block->next->previous = gpe_block->previous; + } + acpi_os_release_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + } + + /* Free the gpe_block */ + + ACPI_MEM_FREE (gpe_block->register_info); + ACPI_MEM_FREE (gpe_block->event_info); + ACPI_MEM_FREE (gpe_block); + +unlock_and_exit: + status = acpi_ut_release_mutex (ACPI_MTX_EVENTS); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_create_gpe_info_blocks + * + * PARAMETERS: gpe_block - New GPE block + * + * RETURN: Status + * + * DESCRIPTION: Create the register_info and event_info blocks for this GPE block + * + ******************************************************************************/ + +static acpi_status +acpi_ev_create_gpe_info_blocks ( + struct acpi_gpe_block_info *gpe_block) +{ + struct acpi_gpe_register_info *gpe_register_info = NULL; + struct acpi_gpe_event_info *gpe_event_info = NULL; + struct acpi_gpe_event_info *this_event; + struct acpi_gpe_register_info *this_register; + acpi_native_uint i; + acpi_native_uint j; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_create_gpe_info_blocks"); + + + /* Allocate the GPE register information block */ + + gpe_register_info = ACPI_MEM_CALLOCATE ( + (acpi_size) gpe_block->register_count * + sizeof (struct acpi_gpe_register_info)); + if (!gpe_register_info) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not allocate the gpe_register_info table\n")); + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* + * Allocate the GPE event_info block. There are eight distinct GPEs + * per register. Initialization to zeros is sufficient. + */ + gpe_event_info = ACPI_MEM_CALLOCATE ( + ((acpi_size) gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH) * + sizeof (struct acpi_gpe_event_info)); + if (!gpe_event_info) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not allocate the gpe_event_info table\n")); + status = AE_NO_MEMORY; + goto error_exit; + } + + /* Save the new Info arrays in the GPE block */ + + gpe_block->register_info = gpe_register_info; + gpe_block->event_info = gpe_event_info; + + /* + * Initialize the GPE Register and Event structures. A goal of these + * tables is to hide the fact that there are two separate GPE register sets + * in a given gpe hardware block, the status registers occupy the first half, + * and the enable registers occupy the second half. + */ + this_register = gpe_register_info; + this_event = gpe_event_info; + + for (i = 0; i < gpe_block->register_count; i++) { + /* Init the register_info for this GPE register (8 GPEs) */ + + this_register->base_gpe_number = (u8) (gpe_block->block_base_number + + (i * ACPI_GPE_REGISTER_WIDTH)); + + ACPI_STORE_ADDRESS (this_register->status_address.address, + (gpe_block->block_address.address + + i)); + + ACPI_STORE_ADDRESS (this_register->enable_address.address, + (gpe_block->block_address.address + + i + + gpe_block->register_count)); + + this_register->status_address.address_space_id = gpe_block->block_address.address_space_id; + this_register->enable_address.address_space_id = gpe_block->block_address.address_space_id; + this_register->status_address.register_bit_width = ACPI_GPE_REGISTER_WIDTH; + this_register->enable_address.register_bit_width = ACPI_GPE_REGISTER_WIDTH; + this_register->status_address.register_bit_offset = ACPI_GPE_REGISTER_WIDTH; + this_register->enable_address.register_bit_offset = ACPI_GPE_REGISTER_WIDTH; + + /* Init the event_info for each GPE within this register */ + + for (j = 0; j < ACPI_GPE_REGISTER_WIDTH; j++) { + this_event->register_bit = acpi_gbl_decode_to8bit[j]; + this_event->register_info = this_register; + this_event++; + } + + /* + * Clear the status/enable registers. Note that status registers + * are cleared by writing a '1', while enable registers are cleared + * by writing a '0'. + */ + status = acpi_hw_low_level_write (ACPI_GPE_REGISTER_WIDTH, 0x00, + &this_register->enable_address); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + status = acpi_hw_low_level_write (ACPI_GPE_REGISTER_WIDTH, 0xFF, + &this_register->status_address); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + this_register++; + } + + return_ACPI_STATUS (AE_OK); + + +error_exit: + if (gpe_register_info) { + ACPI_MEM_FREE (gpe_register_info); + } + if (gpe_event_info) { + ACPI_MEM_FREE (gpe_event_info); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_create_gpe_block + * + * PARAMETERS: gpe_device - Handle to the parent GPE block + * gpe_block_address - Address and space_iD + * register_count - Number of GPE register pairs in the block + * gpe_block_base_number - Starting GPE number for the block + * interrupt_level - H/W interrupt for the block + * return_gpe_block - Where the new block descriptor is returned + * + * RETURN: Status + * + * DESCRIPTION: Create and Install a block of GPE registers + * + ******************************************************************************/ + +acpi_status +acpi_ev_create_gpe_block ( + struct acpi_namespace_node *gpe_device, + struct acpi_generic_address *gpe_block_address, + u32 register_count, + u8 gpe_block_base_number, + u32 interrupt_level, + struct acpi_gpe_block_info **return_gpe_block) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_event_info *gpe_event_info; + acpi_native_uint i; + acpi_native_uint j; + u32 wake_gpe_count; + u32 gpe_enabled_count; + acpi_status status; + struct acpi_gpe_walk_info gpe_info; + + + ACPI_FUNCTION_TRACE ("ev_create_gpe_block"); + + + if (!register_count) { + return_ACPI_STATUS (AE_OK); + } + + /* Allocate a new GPE block */ + + gpe_block = ACPI_MEM_CALLOCATE (sizeof (struct acpi_gpe_block_info)); + if (!gpe_block) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Initialize the new GPE block */ + + gpe_block->register_count = register_count; + gpe_block->block_base_number = gpe_block_base_number; + gpe_block->node = gpe_device; + + ACPI_MEMCPY (&gpe_block->block_address, gpe_block_address, sizeof (struct acpi_generic_address)); + + /* Create the register_info and event_info sub-structures */ + + status = acpi_ev_create_gpe_info_blocks (gpe_block); + if (ACPI_FAILURE (status)) { + ACPI_MEM_FREE (gpe_block); + return_ACPI_STATUS (status); + } + + /* Install the new block in the global list(s) */ + + status = acpi_ev_install_gpe_block (gpe_block, interrupt_level); + if (ACPI_FAILURE (status)) { + ACPI_MEM_FREE (gpe_block); + return_ACPI_STATUS (status); + } + + /* Find all GPE methods (_Lxx, _Exx) for this block */ + + status = acpi_ns_walk_namespace (ACPI_TYPE_METHOD, gpe_device, + ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK, acpi_ev_save_method_info, + gpe_block, NULL); + + /* + * Runtime option: Should Wake GPEs be enabled at runtime? The default + * is No, they should only be enabled just as the machine goes to sleep. + */ + if (acpi_gbl_leave_wake_gpes_disabled) { + /* + * Differentiate RUNTIME vs WAKE GPEs, via the _PRW control methods. + * (Each GPE that has one or more _PRWs that reference it is by + * definition a WAKE GPE and will not be enabled while the machine + * is running.) + */ + gpe_info.gpe_block = gpe_block; + gpe_info.gpe_device = gpe_device; + + status = acpi_ns_walk_namespace (ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ACPI_NS_WALK_UNLOCK, acpi_ev_match_prw_and_gpe, + &gpe_info, NULL); + } + + /* + * Enable all GPEs in this block that are 1) "runtime" or "run/wake" GPEs, + * and 2) have a corresponding _Lxx or _Exx method. All other GPEs must + * be enabled via the acpi_enable_gpe() external interface. + */ + wake_gpe_count = 0; + gpe_enabled_count = 0; + + for (i = 0; i < gpe_block->register_count; i++) { + for (j = 0; j < 8; j++) { + /* Get the info block for this particular GPE */ + + gpe_event_info = &gpe_block->event_info[(i * ACPI_GPE_REGISTER_WIDTH) + j]; + + if (((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_METHOD) && + (gpe_event_info->flags & ACPI_GPE_TYPE_RUNTIME)) { + gpe_enabled_count++; + } + + if (gpe_event_info->flags & ACPI_GPE_TYPE_WAKE) { + wake_gpe_count++; + } + } + } + + /* Dump info about this GPE block */ + + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, + "GPE %02X to %02X [%4.4s] %u regs on int 0x%X\n", + (u32) gpe_block->block_base_number, + (u32) (gpe_block->block_base_number + + ((gpe_block->register_count * ACPI_GPE_REGISTER_WIDTH) -1)), + gpe_device->name.ascii, + gpe_block->register_count, + interrupt_level)); + + /* Enable all valid GPEs found above */ + + status = acpi_hw_enable_runtime_gpe_block (NULL, gpe_block); + + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, + "Found %u Wake, Enabled %u Runtime GPEs in this block\n", + wake_gpe_count, gpe_enabled_count)); + + /* Return the new block */ + + if (return_gpe_block) { + (*return_gpe_block) = gpe_block; + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_gpe_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize the GPE data structures + * + ******************************************************************************/ + +acpi_status +acpi_ev_gpe_initialize ( + void) +{ + u32 register_count0 = 0; + u32 register_count1 = 0; + u32 gpe_number_max = 0; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_gpe_initialize"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Initialize the GPE Block(s) defined in the FADT + * + * Why the GPE register block lengths are divided by 2: From the ACPI Spec, + * section "General-Purpose Event Registers", we have: + * + * "Each register block contains two registers of equal length + * GPEx_STS and GPEx_EN (where x is 0 or 1). The length of the + * GPE0_STS and GPE0_EN registers is equal to half the GPE0_LEN + * The length of the GPE1_STS and GPE1_EN registers is equal to + * half the GPE1_LEN. If a generic register block is not supported + * then its respective block pointer and block length values in the + * FADT table contain zeros. The GPE0_LEN and GPE1_LEN do not need + * to be the same size." + */ + + /* + * Determine the maximum GPE number for this machine. + * + * Note: both GPE0 and GPE1 are optional, and either can exist without + * the other. + * + * If EITHER the register length OR the block address are zero, then that + * particular block is not supported. + */ + if (acpi_gbl_FADT->gpe0_blk_len && + acpi_gbl_FADT->xgpe0_blk.address) { + /* GPE block 0 exists (has both length and address > 0) */ + + register_count0 = (u16) (acpi_gbl_FADT->gpe0_blk_len / 2); + + gpe_number_max = (register_count0 * ACPI_GPE_REGISTER_WIDTH) - 1; + + /* Install GPE Block 0 */ + + status = acpi_ev_create_gpe_block (acpi_gbl_fadt_gpe_device, &acpi_gbl_FADT->xgpe0_blk, + register_count0, 0, acpi_gbl_FADT->sci_int, &acpi_gbl_gpe_fadt_blocks[0]); + + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "Could not create GPE Block 0, %s\n", + acpi_format_exception (status))); + } + } + + if (acpi_gbl_FADT->gpe1_blk_len && + acpi_gbl_FADT->xgpe1_blk.address) { + /* GPE block 1 exists (has both length and address > 0) */ + + register_count1 = (u16) (acpi_gbl_FADT->gpe1_blk_len / 2); + + /* Check for GPE0/GPE1 overlap (if both banks exist) */ + + if ((register_count0) && + (gpe_number_max >= acpi_gbl_FADT->gpe1_base)) { + ACPI_REPORT_ERROR (( + "GPE0 block (GPE 0 to %d) overlaps the GPE1 block (GPE %d to %d) - Ignoring GPE1\n", + gpe_number_max, acpi_gbl_FADT->gpe1_base, + acpi_gbl_FADT->gpe1_base + + ((register_count1 * ACPI_GPE_REGISTER_WIDTH) - 1))); + + /* Ignore GPE1 block by setting the register count to zero */ + + register_count1 = 0; + } + else { + /* Install GPE Block 1 */ + + status = acpi_ev_create_gpe_block (acpi_gbl_fadt_gpe_device, &acpi_gbl_FADT->xgpe1_blk, + register_count1, acpi_gbl_FADT->gpe1_base, + acpi_gbl_FADT->sci_int, &acpi_gbl_gpe_fadt_blocks[1]); + + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (( + "Could not create GPE Block 1, %s\n", + acpi_format_exception (status))); + } + + /* + * GPE0 and GPE1 do not have to be contiguous in the GPE number + * space. However, GPE0 always starts at GPE number zero. + */ + gpe_number_max = acpi_gbl_FADT->gpe1_base + + ((register_count1 * ACPI_GPE_REGISTER_WIDTH) - 1); + } + } + + /* Exit if there are no GPE registers */ + + if ((register_count0 + register_count1) == 0) { + /* GPEs are not required by ACPI, this is OK */ + + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, + "There are no GPE blocks defined in the FADT\n")); + status = AE_OK; + goto cleanup; + } + + /* Check for Max GPE number out-of-range */ + + if (gpe_number_max > ACPI_GPE_MAX) { + ACPI_REPORT_ERROR (("Maximum GPE number from FADT is too large: 0x%X\n", + gpe_number_max)); + status = AE_BAD_VALUE; + goto cleanup; + } + +cleanup: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (AE_OK); +} + + diff --git a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c new file mode 100644 index 00000000000..2548efa7a45 --- /dev/null +++ b/drivers/acpi/events/evmisc.c @@ -0,0 +1,588 @@ +/****************************************************************************** + * + * Module Name: evmisc - Miscellaneous event manager support functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <acpi/acpi.h> +#include <acpi/acevents.h> +#include <acpi/acnamesp.h> +#include <acpi/acinterp.h> + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evmisc") + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_is_notify_object + * + * PARAMETERS: Node - Node to check + * + * RETURN: TRUE if notifies allowed on this object + * + * DESCRIPTION: Check type of node for a object that supports notifies. + * + * TBD: This could be replaced by a flag bit in the node. + * + ******************************************************************************/ + +u8 +acpi_ev_is_notify_object ( + struct acpi_namespace_node *node) +{ + switch (node->type) { + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_POWER: + case ACPI_TYPE_THERMAL: + /* + * These are the ONLY objects that can receive ACPI notifications + */ + return (TRUE); + + default: + return (FALSE); + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_queue_notify_request + * + * PARAMETERS: Node - NS node for the notified object + * notify_value - Value from the Notify() request + * + * RETURN: Status + * + * DESCRIPTION: Dispatch a device notification event to a previously + * installed handler. + * + ******************************************************************************/ + +#ifdef ACPI_DEBUG_OUTPUT +static const char *acpi_notify_value_names[] = +{ + "Bus Check", + "Device Check", + "Device Wake", + "Eject request", + "Device Check Light", + "Frequency Mismatch", + "Bus Mode Mismatch", + "Power Fault" +}; +#endif + +acpi_status +acpi_ev_queue_notify_request ( + struct acpi_namespace_node *node, + u32 notify_value) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj = NULL; + union acpi_generic_state *notify_info; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_NAME ("ev_queue_notify_request"); + + + /* + * For value 3 (Ejection Request), some device method may need to be run. + * For value 2 (Device Wake) if _PRW exists, the _PS0 method may need to be run. + * For value 0x80 (Status Change) on the power button or sleep button, + * initiate soft-off or sleep operation? + */ + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Dispatching Notify(%X) on node %p\n", notify_value, node)); + + if (notify_value <= 7) { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Notify value: %s\n", + acpi_notify_value_names[notify_value])); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Notify value: 0x%2.2X **Device Specific**\n", + notify_value)); + } + + /* Get the notify object attached to the NS Node */ + + obj_desc = acpi_ns_get_attached_object (node); + if (obj_desc) { + /* We have the notify object, Get the right handler */ + + switch (node->type) { + case ACPI_TYPE_DEVICE: + case ACPI_TYPE_THERMAL: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_POWER: + + if (notify_value <= ACPI_MAX_SYS_NOTIFY) { + handler_obj = obj_desc->common_notify.system_notify; + } + else { + handler_obj = obj_desc->common_notify.device_notify; + } + break; + + default: + /* All other types are not supported */ + return (AE_TYPE); + } + } + + /* If there is any handler to run, schedule the dispatcher */ + + if ((acpi_gbl_system_notify.handler && (notify_value <= ACPI_MAX_SYS_NOTIFY)) || + (acpi_gbl_device_notify.handler && (notify_value > ACPI_MAX_SYS_NOTIFY)) || + handler_obj) { + notify_info = acpi_ut_create_generic_state (); + if (!notify_info) { + return (AE_NO_MEMORY); + } + + notify_info->common.data_type = ACPI_DESC_TYPE_STATE_NOTIFY; + notify_info->notify.node = node; + notify_info->notify.value = (u16) notify_value; + notify_info->notify.handler_obj = handler_obj; + + status = acpi_os_queue_for_execution (OSD_PRIORITY_HIGH, + acpi_ev_notify_dispatch, notify_info); + if (ACPI_FAILURE (status)) { + acpi_ut_delete_generic_state (notify_info); + } + } + + if (!handler_obj) { + /* + * There is no per-device notify handler for this device. + * This may or may not be a problem. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "No notify handler for Notify(%4.4s, %X) node %p\n", + acpi_ut_get_node_name (node), notify_value, node)); + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_notify_dispatch + * + * PARAMETERS: Context - To be passsed to the notify handler + * + * RETURN: None. + * + * DESCRIPTION: Dispatch a device notification event to a previously + * installed handler. + * + ******************************************************************************/ + +void ACPI_SYSTEM_XFACE +acpi_ev_notify_dispatch ( + void *context) +{ + union acpi_generic_state *notify_info = (union acpi_generic_state *) context; + acpi_notify_handler global_handler = NULL; + void *global_context = NULL; + union acpi_operand_object *handler_obj; + + + ACPI_FUNCTION_ENTRY (); + + + /* + * We will invoke a global notify handler if installed. + * This is done _before_ we invoke the per-device handler attached to the device. + */ + if (notify_info->notify.value <= ACPI_MAX_SYS_NOTIFY) { + /* Global system notification handler */ + + if (acpi_gbl_system_notify.handler) { + global_handler = acpi_gbl_system_notify.handler; + global_context = acpi_gbl_system_notify.context; + } + } + else { + /* Global driver notification handler */ + + if (acpi_gbl_device_notify.handler) { + global_handler = acpi_gbl_device_notify.handler; + global_context = acpi_gbl_device_notify.context; + } + } + + /* Invoke the system handler first, if present */ + + if (global_handler) { + global_handler (notify_info->notify.node, notify_info->notify.value, global_context); + } + + /* Now invoke the per-device handler, if present */ + + handler_obj = notify_info->notify.handler_obj; + if (handler_obj) { + handler_obj->notify.handler (notify_info->notify.node, notify_info->notify.value, + handler_obj->notify.context); + } + + /* All done with the info object */ + + acpi_ut_delete_generic_state (notify_info); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_global_lock_thread + * + * PARAMETERS: Context - From thread interface, not used + * + * RETURN: None + * + * DESCRIPTION: Invoked by SCI interrupt handler upon acquisition of the + * Global Lock. Simply signal all threads that are waiting + * for the lock. + * + ******************************************************************************/ + +static void ACPI_SYSTEM_XFACE +acpi_ev_global_lock_thread ( + void *context) +{ + acpi_status status; + + + /* Signal threads that are waiting for the lock */ + + if (acpi_gbl_global_lock_thread_count) { + /* Send sufficient units to the semaphore */ + + status = acpi_os_signal_semaphore (acpi_gbl_global_lock_semaphore, + acpi_gbl_global_lock_thread_count); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not signal Global Lock semaphore\n")); + } + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_global_lock_handler + * + * PARAMETERS: Context - From thread interface, not used + * + * RETURN: ACPI_INTERRUPT_HANDLED or ACPI_INTERRUPT_NOT_HANDLED + * + * DESCRIPTION: Invoked directly from the SCI handler when a global lock + * release interrupt occurs. Grab the global lock and queue + * the global lock thread for execution + * + ******************************************************************************/ + +static u32 +acpi_ev_global_lock_handler ( + void *context) +{ + u8 acquired = FALSE; + acpi_status status; + + + /* + * Attempt to get the lock + * If we don't get it now, it will be marked pending and we will + * take another interrupt when it becomes free. + */ + ACPI_ACQUIRE_GLOBAL_LOCK (acpi_gbl_common_fACS.global_lock, acquired); + if (acquired) { + /* Got the lock, now wake all threads waiting for it */ + + acpi_gbl_global_lock_acquired = TRUE; + + /* Run the Global Lock thread which will signal all waiting threads */ + + status = acpi_os_queue_for_execution (OSD_PRIORITY_HIGH, + acpi_ev_global_lock_thread, context); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not queue Global Lock thread, %s\n", + acpi_format_exception (status))); + + return (ACPI_INTERRUPT_NOT_HANDLED); + } + } + + return (ACPI_INTERRUPT_HANDLED); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_init_global_lock_handler + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for the global lock release event + * + ******************************************************************************/ + +acpi_status +acpi_ev_init_global_lock_handler (void) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_init_global_lock_handler"); + + + acpi_gbl_global_lock_present = TRUE; + status = acpi_install_fixed_event_handler (ACPI_EVENT_GLOBAL, + acpi_ev_global_lock_handler, NULL); + + /* + * If the global lock does not exist on this platform, the attempt + * to enable GBL_STATUS will fail (the GBL_ENABLE bit will not stick) + * Map to AE_OK, but mark global lock as not present. + * Any attempt to actually use the global lock will be flagged + * with an error. + */ + if (status == AE_NO_HARDWARE_RESPONSE) { + acpi_gbl_global_lock_present = FALSE; + status = AE_OK; + } + + return_ACPI_STATUS (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_ev_acquire_global_lock + * + * PARAMETERS: Timeout - Max time to wait for the lock, in millisec. + * + * RETURN: Status + * + * DESCRIPTION: Attempt to gain ownership of the Global Lock. + * + *****************************************************************************/ + +acpi_status +acpi_ev_acquire_global_lock ( + u16 timeout) +{ + acpi_status status = AE_OK; + u8 acquired = FALSE; + + + ACPI_FUNCTION_TRACE ("ev_acquire_global_lock"); + + +#ifndef ACPI_APPLICATION + /* Make sure that we actually have a global lock */ + + if (!acpi_gbl_global_lock_present) { + return_ACPI_STATUS (AE_NO_GLOBAL_LOCK); + } +#endif + + /* One more thread wants the global lock */ + + acpi_gbl_global_lock_thread_count++; + + /* If we (OS side vs. BIOS side) have the hardware lock already, we are done */ + + if (acpi_gbl_global_lock_acquired) { + return_ACPI_STATUS (AE_OK); + } + + /* We must acquire the actual hardware lock */ + + ACPI_ACQUIRE_GLOBAL_LOCK (acpi_gbl_common_fACS.global_lock, acquired); + if (acquired) { + /* We got the lock */ + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Acquired the HW Global Lock\n")); + + acpi_gbl_global_lock_acquired = TRUE; + return_ACPI_STATUS (AE_OK); + } + + /* + * Did not get the lock. The pending bit was set above, and we must now + * wait until we get the global lock released interrupt. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Waiting for the HW Global Lock\n")); + + /* + * Acquire the global lock semaphore first. + * Since this wait will block, we must release the interpreter + */ + status = acpi_ex_system_wait_semaphore (acpi_gbl_global_lock_semaphore, + timeout); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_release_global_lock + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Releases ownership of the Global Lock. + * + ******************************************************************************/ + +acpi_status +acpi_ev_release_global_lock (void) +{ + u8 pending = FALSE; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ev_release_global_lock"); + + + if (!acpi_gbl_global_lock_thread_count) { + ACPI_REPORT_WARNING(("Cannot release HW Global Lock, it has not been acquired\n")); + return_ACPI_STATUS (AE_NOT_ACQUIRED); + } + + /* One fewer thread has the global lock */ + + acpi_gbl_global_lock_thread_count--; + if (acpi_gbl_global_lock_thread_count) { + /* There are still some threads holding the lock, cannot release */ + + return_ACPI_STATUS (AE_OK); + } + + /* + * No more threads holding lock, we can do the actual hardware + * release + */ + ACPI_RELEASE_GLOBAL_LOCK (acpi_gbl_common_fACS.global_lock, pending); + acpi_gbl_global_lock_acquired = FALSE; + + /* + * If the pending bit was set, we must write GBL_RLS to the control + * register + */ + if (pending) { + status = acpi_set_register (ACPI_BITREG_GLOBAL_LOCK_RELEASE, 1, ACPI_MTX_LOCK); + } + + return_ACPI_STATUS (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_ev_terminate + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: Disable events and free memory allocated for table storage. + * + ******************************************************************************/ + +void +acpi_ev_terminate (void) +{ + acpi_native_uint i; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_terminate"); + + + if (acpi_gbl_events_initialized) { + /* + * Disable all event-related functionality. + * In all cases, on error, print a message but obviously we don't abort. + */ + + /* Disable all fixed events */ + + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + status = acpi_disable_event ((u32) i, 0); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not disable fixed event %d\n", (u32) i)); + } + } + + /* Disable all GPEs in all GPE blocks */ + + status = acpi_ev_walk_gpe_list (acpi_hw_disable_gpe_block, ACPI_NOT_ISR); + + /* Remove SCI handler */ + + status = acpi_ev_remove_sci_handler (); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not remove SCI handler\n")); + } + } + + /* Deallocate all handler objects installed within GPE info structs */ + + status = acpi_ev_walk_gpe_list (acpi_ev_delete_gpe_handlers, ACPI_NOT_ISR); + + /* Return to original mode if necessary */ + + if (acpi_gbl_original_mode == ACPI_SYS_MODE_LEGACY) { + status = acpi_disable (); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "acpi_disable failed\n")); + } + } + return_VOID; +} + diff --git a/drivers/acpi/events/evregion.c b/drivers/acpi/events/evregion.c new file mode 100644 index 00000000000..772342708a7 --- /dev/null +++ b/drivers/acpi/events/evregion.c @@ -0,0 +1,1067 @@ +/****************************************************************************** + * + * Module Name: evregion - ACPI address_space (op_region) handler dispatch + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acevents.h> +#include <acpi/acnamesp.h> +#include <acpi/acinterp.h> + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evregion") + +#define ACPI_NUM_DEFAULT_SPACES 4 + +static u8 acpi_gbl_default_address_spaces[ACPI_NUM_DEFAULT_SPACES] = { + ACPI_ADR_SPACE_SYSTEM_MEMORY, + ACPI_ADR_SPACE_SYSTEM_IO, + ACPI_ADR_SPACE_PCI_CONFIG, + ACPI_ADR_SPACE_DATA_TABLE}; + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_region_handlers + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Installs the core subsystem default address space handlers. + * + ******************************************************************************/ + +acpi_status +acpi_ev_install_region_handlers ( + void) { + acpi_status status; + acpi_native_uint i; + + + ACPI_FUNCTION_TRACE ("ev_install_region_handlers"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * All address spaces (PCI Config, EC, SMBus) are scope dependent + * and registration must occur for a specific device. + * + * In the case of the system memory and IO address spaces there is currently + * no device associated with the address space. For these we use the root. + * + * We install the default PCI config space handler at the root so + * that this space is immediately available even though the we have + * not enumerated all the PCI Root Buses yet. This is to conform + * to the ACPI specification which states that the PCI config + * space must be always available -- even though we are nowhere + * near ready to find the PCI root buses at this point. + * + * NOTE: We ignore AE_ALREADY_EXISTS because this means that a handler + * has already been installed (via acpi_install_address_space_handler). + * Similar for AE_SAME_HANDLER. + */ + for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { + status = acpi_ev_install_space_handler (acpi_gbl_root_node, + acpi_gbl_default_address_spaces[i], + ACPI_DEFAULT_HANDLER, NULL, NULL); + switch (status) { + case AE_OK: + case AE_SAME_HANDLER: + case AE_ALREADY_EXISTS: + + /* These exceptions are all OK */ + + status = AE_OK; + break; + + default: + + goto unlock_and_exit; + } + } + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_initialize_op_regions + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Execute _REG methods for all Operation Regions that have + * an installed default region handler. + * + ******************************************************************************/ + +acpi_status +acpi_ev_initialize_op_regions ( + void) +{ + acpi_status status; + acpi_native_uint i; + + + ACPI_FUNCTION_TRACE ("ev_initialize_op_regions"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Run the _REG methods for op_regions in each default address space + */ + for (i = 0; i < ACPI_NUM_DEFAULT_SPACES; i++) { + /* TBD: Make sure handler is the DEFAULT handler, otherwise + * _REG will have already been run. + */ + status = acpi_ev_execute_reg_methods (acpi_gbl_root_node, + acpi_gbl_default_address_spaces[i]); + } + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_execute_reg_method + * + * PARAMETERS: region_obj - Object structure + * Function - Passed to _REG: On (1) or Off (0) + * + * RETURN: Status + * + * DESCRIPTION: Execute _REG method for a region + * + ******************************************************************************/ + +acpi_status +acpi_ev_execute_reg_method ( + union acpi_operand_object *region_obj, + u32 function) +{ + struct acpi_parameter_info info; + union acpi_operand_object *params[3]; + union acpi_operand_object *region_obj2; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_execute_reg_method"); + + + region_obj2 = acpi_ns_get_secondary_object (region_obj); + if (!region_obj2) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + + if (region_obj2->extra.method_REG == NULL) { + return_ACPI_STATUS (AE_OK); + } + + /* + * The _REG method has two arguments: + * + * Arg0, Integer: Operation region space ID + * Same value as region_obj->Region.space_id + * Arg1, Integer: connection status + * 1 for connecting the handler, + * 0 for disconnecting the handler + * Passed as a parameter + */ + params[0] = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!params[0]) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + params[1] = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!params[1]) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Setup the parameter objects */ + + params[0]->integer.value = region_obj->region.space_id; + params[1]->integer.value = function; + params[2] = NULL; + + info.node = region_obj2->extra.method_REG; + info.parameters = params; + info.parameter_type = ACPI_PARAM_ARGS; + + /* Execute the method, no return value */ + + ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname ( + ACPI_TYPE_METHOD, info.node, NULL)); + status = acpi_ns_evaluate_by_handle (&info); + + acpi_ut_remove_reference (params[1]); + +cleanup: + acpi_ut_remove_reference (params[0]); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_address_space_dispatch + * + * PARAMETERS: region_obj - Internal region object + * Function - Read or Write operation + * Address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, 32, or 64) + * Value - Pointer to in or out value + * + * RETURN: Status + * + * DESCRIPTION: Dispatch an address space or operation region access to + * a previously installed handler. + * + ******************************************************************************/ + +acpi_status +acpi_ev_address_space_dispatch ( + union acpi_operand_object *region_obj, + u32 function, + acpi_physical_address address, + u32 bit_width, + void *value) +{ + acpi_status status; + acpi_status status2; + acpi_adr_space_handler handler; + acpi_adr_space_setup region_setup; + union acpi_operand_object *handler_desc; + union acpi_operand_object *region_obj2; + void *region_context = NULL; + + + ACPI_FUNCTION_TRACE ("ev_address_space_dispatch"); + + + region_obj2 = acpi_ns_get_secondary_object (region_obj); + if (!region_obj2) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + + /* Ensure that there is a handler associated with this region */ + + handler_desc = region_obj->region.handler; + if (!handler_desc) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "No handler for Region [%4.4s] (%p) [%s]\n", + acpi_ut_get_node_name (region_obj->region.node), + region_obj, acpi_ut_get_region_name (region_obj->region.space_id))); + + return_ACPI_STATUS (AE_NOT_EXIST); + } + + /* + * It may be the case that the region has never been initialized + * Some types of regions require special init code + */ + if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) { + /* + * This region has not been initialized yet, do it + */ + region_setup = handler_desc->address_space.setup; + if (!region_setup) { + /* No initialization routine, exit with error */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No init routine for region(%p) [%s]\n", + region_obj, acpi_ut_get_region_name (region_obj->region.space_id))); + return_ACPI_STATUS (AE_NOT_EXIST); + } + + /* + * We must exit the interpreter because the region setup will potentially + * execute control methods (e.g., _REG method for this region) + */ + acpi_ex_exit_interpreter (); + + status = region_setup (region_obj, ACPI_REGION_ACTIVATE, + handler_desc->address_space.context, ®ion_context); + + /* Re-enter the interpreter */ + + status2 = acpi_ex_enter_interpreter (); + if (ACPI_FAILURE (status2)) { + return_ACPI_STATUS (status2); + } + + /* Check for failure of the Region Setup */ + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Region Init: %s [%s]\n", + acpi_format_exception (status), + acpi_ut_get_region_name (region_obj->region.space_id))); + return_ACPI_STATUS (status); + } + + /* + * Region initialization may have been completed by region_setup + */ + if (!(region_obj->region.flags & AOPOBJ_SETUP_COMPLETE)) { + region_obj->region.flags |= AOPOBJ_SETUP_COMPLETE; + + if (region_obj2->extra.region_context) { + /* The handler for this region was already installed */ + + ACPI_MEM_FREE (region_context); + } + else { + /* + * Save the returned context for use in all accesses to + * this particular region + */ + region_obj2->extra.region_context = region_context; + } + } + } + + /* We have everything we need, we can invoke the address space handler */ + + handler = handler_desc->address_space.handler; + + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Handler %p (@%p) Address %8.8X%8.8X [%s]\n", + ®ion_obj->region.handler->address_space, handler, + ACPI_FORMAT_UINT64 (address), + acpi_ut_get_region_name (region_obj->region.space_id))); + + if (!(handler_desc->address_space.hflags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { + /* + * For handlers other than the default (supplied) handlers, we must + * exit the interpreter because the handler *might* block -- we don't + * know what it will do, so we can't hold the lock on the intepreter. + */ + acpi_ex_exit_interpreter(); + } + + /* Call the handler */ + + status = handler (function, address, bit_width, value, + handler_desc->address_space.context, + region_obj2->extra.region_context); + + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Handler for [%s] returned %s\n", + acpi_ut_get_region_name (region_obj->region.space_id), + acpi_format_exception (status))); + } + + if (!(handler_desc->address_space.hflags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED)) { + /* + * We just returned from a non-default handler, we must re-enter the + * interpreter + */ + status2 = acpi_ex_enter_interpreter (); + if (ACPI_FAILURE (status2)) { + return_ACPI_STATUS (status2); + } + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_detach_region + * + * PARAMETERS: region_obj - Region Object + * acpi_ns_is_locked - Namespace Region Already Locked? + * + * RETURN: None + * + * DESCRIPTION: Break the association between the handler and the region + * this is a two way association. + * + ******************************************************************************/ + +void +acpi_ev_detach_region( + union acpi_operand_object *region_obj, + u8 acpi_ns_is_locked) +{ + union acpi_operand_object *handler_obj; + union acpi_operand_object *obj_desc; + union acpi_operand_object **last_obj_ptr; + acpi_adr_space_setup region_setup; + void **region_context; + union acpi_operand_object *region_obj2; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_detach_region"); + + + region_obj2 = acpi_ns_get_secondary_object (region_obj); + if (!region_obj2) { + return_VOID; + } + region_context = ®ion_obj2->extra.region_context; + + /* Get the address handler from the region object */ + + handler_obj = region_obj->region.handler; + if (!handler_obj) { + /* This region has no handler, all done */ + + return_VOID; + } + + /* Find this region in the handler's list */ + + obj_desc = handler_obj->address_space.region_list; + last_obj_ptr = &handler_obj->address_space.region_list; + + while (obj_desc) { + /* Is this the correct Region? */ + + if (obj_desc == region_obj) { + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Removing Region %p from address handler %p\n", + region_obj, handler_obj)); + + /* This is it, remove it from the handler's list */ + + *last_obj_ptr = obj_desc->region.next; + obj_desc->region.next = NULL; /* Must clear field */ + + if (acpi_ns_is_locked) { + status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_VOID; + } + } + + /* Now stop region accesses by executing the _REG method */ + + status = acpi_ev_execute_reg_method (region_obj, 0); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "%s from region _REG, [%s]\n", + acpi_format_exception (status), + acpi_ut_get_region_name (region_obj->region.space_id))); + } + + if (acpi_ns_is_locked) { + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_VOID; + } + } + + /* Call the setup handler with the deactivate notification */ + + region_setup = handler_obj->address_space.setup; + status = region_setup (region_obj, ACPI_REGION_DEACTIVATE, + handler_obj->address_space.context, region_context); + + /* Init routine may fail, Just ignore errors */ + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "%s from region init, [%s]\n", + acpi_format_exception (status), + acpi_ut_get_region_name (region_obj->region.space_id))); + } + + region_obj->region.flags &= ~(AOPOBJ_SETUP_COMPLETE); + + /* + * Remove handler reference in the region + * + * NOTE: this doesn't mean that the region goes away + * The region is just inaccessible as indicated to + * the _REG method + * + * If the region is on the handler's list + * this better be the region's handler + */ + region_obj->region.handler = NULL; + acpi_ut_remove_reference (handler_obj); + + return_VOID; + } + + /* Walk the linked list of handlers */ + + last_obj_ptr = &obj_desc->region.next; + obj_desc = obj_desc->region.next; + } + + /* If we get here, the region was not in the handler's region list */ + + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Cannot remove region %p from address handler %p\n", + region_obj, handler_obj)); + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_attach_region + * + * PARAMETERS: handler_obj - Handler Object + * region_obj - Region Object + * acpi_ns_is_locked - Namespace Region Already Locked? + * + * RETURN: None + * + * DESCRIPTION: Create the association between the handler and the region + * this is a two way association. + * + ******************************************************************************/ + +acpi_status +acpi_ev_attach_region ( + union acpi_operand_object *handler_obj, + union acpi_operand_object *region_obj, + u8 acpi_ns_is_locked) +{ + + ACPI_FUNCTION_TRACE ("ev_attach_region"); + + + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Adding Region [%4.4s] %p to address handler %p [%s]\n", + acpi_ut_get_node_name (region_obj->region.node), + region_obj, handler_obj, + acpi_ut_get_region_name (region_obj->region.space_id))); + + /* Link this region to the front of the handler's list */ + + region_obj->region.next = handler_obj->address_space.region_list; + handler_obj->address_space.region_list = region_obj; + + /* Install the region's handler */ + + if (region_obj->region.handler) { + return_ACPI_STATUS (AE_ALREADY_EXISTS); + } + + region_obj->region.handler = handler_obj; + acpi_ut_add_reference (handler_obj); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_handler + * + * PARAMETERS: walk_namespace callback + * + * DESCRIPTION: This routine installs an address handler into objects that are + * of type Region or Device. + * + * If the Object is a Device, and the device has a handler of + * the same type then the search is terminated in that branch. + * + * This is because the existing handler is closer in proximity + * to any more regions than the one we are trying to install. + * + ******************************************************************************/ + +acpi_status +acpi_ev_install_handler ( + acpi_handle obj_handle, + u32 level, + void *context, + void **return_value) +{ + union acpi_operand_object *handler_obj; + union acpi_operand_object *next_handler_obj; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + acpi_status status; + + + ACPI_FUNCTION_NAME ("ev_install_handler"); + + + handler_obj = (union acpi_operand_object *) context; + + /* Parameter validation */ + + if (!handler_obj) { + return (AE_OK); + } + + /* Convert and validate the device handle */ + + node = acpi_ns_map_handle_to_node (obj_handle); + if (!node) { + return (AE_BAD_PARAMETER); + } + + /* + * We only care about regions.and objects + * that are allowed to have address space handlers + */ + if ((node->type != ACPI_TYPE_DEVICE) && + (node->type != ACPI_TYPE_REGION) && + (node != acpi_gbl_root_node)) { + return (AE_OK); + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + /* No object, just exit */ + + return (AE_OK); + } + + /* Devices are handled different than regions */ + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_DEVICE) { + /* Check if this Device already has a handler for this address space */ + + next_handler_obj = obj_desc->device.handler; + while (next_handler_obj) { + /* Found a handler, is it for the same address space? */ + + if (next_handler_obj->address_space.space_id == handler_obj->address_space.space_id) { + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Found handler for region [%s] in device %p(%p) handler %p\n", + acpi_ut_get_region_name (handler_obj->address_space.space_id), + obj_desc, next_handler_obj, handler_obj)); + + /* + * Since the object we found it on was a device, then it + * means that someone has already installed a handler for + * the branch of the namespace from this device on. Just + * bail out telling the walk routine to not traverse this + * branch. This preserves the scoping rule for handlers. + */ + return (AE_CTRL_DEPTH); + } + + /* Walk the linked list of handlers attached to this device */ + + next_handler_obj = next_handler_obj->address_space.next; + } + + /* + * As long as the device didn't have a handler for this + * space we don't care about it. We just ignore it and + * proceed. + */ + return (AE_OK); + } + + /* Object is a Region */ + + if (obj_desc->region.space_id != handler_obj->address_space.space_id) { + /* + * This region is for a different address space + * -- just ignore it + */ + return (AE_OK); + } + + /* + * Now we have a region and it is for the handler's address + * space type. + * + * First disconnect region for any previous handler (if any) + */ + acpi_ev_detach_region (obj_desc, FALSE); + + /* Connect the region to the new handler */ + + status = acpi_ev_attach_region (handler_obj, obj_desc, FALSE); + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_install_space_handler + * + * PARAMETERS: Node - Namespace node for the device + * space_id - The address space ID + * Handler - Address of the handler + * Setup - Address of the setup function + * Context - Value passed to the handler on each access + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for all op_regions of a given space_id. + * Assumes namespace is locked + * + ******************************************************************************/ + +acpi_status +acpi_ev_install_space_handler ( + struct acpi_namespace_node *node, + acpi_adr_space_type space_id, + acpi_adr_space_handler handler, + acpi_adr_space_setup setup, + void *context) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + acpi_status status; + acpi_object_type type; + u16 flags = 0; + + + ACPI_FUNCTION_TRACE ("ev_install_space_handler"); + + + /* + * This registration is valid for only the types below + * and the root. This is where the default handlers + * get placed. + */ + if ((node->type != ACPI_TYPE_DEVICE) && + (node->type != ACPI_TYPE_PROCESSOR) && + (node->type != ACPI_TYPE_THERMAL) && + (node != acpi_gbl_root_node)) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + if (handler == ACPI_DEFAULT_HANDLER) { + flags = ACPI_ADDR_HANDLER_DEFAULT_INSTALLED; + + switch (space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + handler = acpi_ex_system_memory_space_handler; + setup = acpi_ev_system_memory_region_setup; + break; + + case ACPI_ADR_SPACE_SYSTEM_IO: + handler = acpi_ex_system_io_space_handler; + setup = acpi_ev_io_space_region_setup; + break; + + case ACPI_ADR_SPACE_PCI_CONFIG: + handler = acpi_ex_pci_config_space_handler; + setup = acpi_ev_pci_config_region_setup; + break; + + case ACPI_ADR_SPACE_CMOS: + handler = acpi_ex_cmos_space_handler; + setup = acpi_ev_cmos_region_setup; + break; + + case ACPI_ADR_SPACE_PCI_BAR_TARGET: + handler = acpi_ex_pci_bar_space_handler; + setup = acpi_ev_pci_bar_region_setup; + break; + + case ACPI_ADR_SPACE_DATA_TABLE: + handler = acpi_ex_data_table_space_handler; + setup = NULL; + break; + + default: + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } + + /* If the caller hasn't specified a setup routine, use the default */ + + if (!setup) { + setup = acpi_ev_default_region_setup; + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object (node); + if (obj_desc) { + /* + * The attached device object already exists. + * Make sure the handler is not already installed. + */ + handler_obj = obj_desc->device.handler; + + /* Walk the handler list for this device */ + + while (handler_obj) { + /* Same space_id indicates a handler already installed */ + + if (handler_obj->address_space.space_id == space_id) { + if (handler_obj->address_space.handler == handler) { + /* + * It is (relatively) OK to attempt to install the SAME + * handler twice. This can easily happen with PCI_Config space. + */ + status = AE_SAME_HANDLER; + goto unlock_and_exit; + } + else { + /* A handler is already installed */ + + status = AE_ALREADY_EXISTS; + } + goto unlock_and_exit; + } + + /* Walk the linked list of handlers */ + + handler_obj = handler_obj->address_space.next; + } + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Creating object on Device %p while installing handler\n", node)); + + /* obj_desc does not exist, create one */ + + if (node->type == ACPI_TYPE_ANY) { + type = ACPI_TYPE_DEVICE; + } + else { + type = node->type; + } + + obj_desc = acpi_ut_create_internal_object (type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Init new descriptor */ + + obj_desc->common.type = (u8) type; + + /* Attach the new object to the Node */ + + status = acpi_ns_attach_object (node, obj_desc, type); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + } + + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Installing address handler for region %s(%X) on Device %4.4s %p(%p)\n", + acpi_ut_get_region_name (space_id), space_id, + acpi_ut_get_node_name (node), node, obj_desc)); + + /* + * Install the handler + * + * At this point there is no existing handler. + * Just allocate the object for the handler and link it + * into the list. + */ + handler_obj = acpi_ut_create_internal_object (ACPI_TYPE_LOCAL_ADDRESS_HANDLER); + if (!handler_obj) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Init handler obj */ + + handler_obj->address_space.space_id = (u8) space_id; + handler_obj->address_space.hflags = flags; + handler_obj->address_space.region_list = NULL; + handler_obj->address_space.node = node; + handler_obj->address_space.handler = handler; + handler_obj->address_space.context = context; + handler_obj->address_space.setup = setup; + + /* Install at head of Device.address_space list */ + + handler_obj->address_space.next = obj_desc->device.handler; + + /* + * The Device object is the first reference on the handler_obj. + * Each region that uses the handler adds a reference. + */ + obj_desc->device.handler = handler_obj; + + /* + * Walk the namespace finding all of the regions this + * handler will manage. + * + * Start at the device and search the branch toward + * the leaf nodes until either the leaf is encountered or + * a device is detected that has an address handler of the + * same type. + * + * In either case, back up and search down the remainder + * of the branch + */ + status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, node, ACPI_UINT32_MAX, + ACPI_NS_WALK_UNLOCK, acpi_ev_install_handler, + handler_obj, NULL); + +unlock_and_exit: + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_execute_reg_methods + * + * PARAMETERS: Node - Namespace node for the device + * space_id - The address space ID + * + * RETURN: Status + * + * DESCRIPTION: Run all _REG methods for the input Space ID; + * Note: assumes namespace is locked, or system init time. + * + ******************************************************************************/ + +acpi_status +acpi_ev_execute_reg_methods ( + struct acpi_namespace_node *node, + acpi_adr_space_type space_id) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_execute_reg_methods"); + + + /* + * Run all _REG methods for all Operation Regions for this + * space ID. This is a separate walk in order to handle any + * interdependencies between regions and _REG methods. (i.e. handlers + * must be installed for all regions of this Space ID before we + * can run any _REG methods) + */ + status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, node, ACPI_UINT32_MAX, + ACPI_NS_WALK_UNLOCK, acpi_ev_reg_run, + &space_id, NULL); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_reg_run + * + * PARAMETERS: walk_namespace callback + * + * DESCRIPTION: Run _REg method for region objects of the requested space_iD + * + ******************************************************************************/ + +acpi_status +acpi_ev_reg_run ( + acpi_handle obj_handle, + u32 level, + void *context, + void **return_value) +{ + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + acpi_adr_space_type space_id; + acpi_status status; + + + space_id = *ACPI_CAST_PTR (acpi_adr_space_type, context); + + /* Convert and validate the device handle */ + + node = acpi_ns_map_handle_to_node (obj_handle); + if (!node) { + return (AE_BAD_PARAMETER); + } + + /* + * We only care about regions.and objects + * that are allowed to have address space handlers + */ + if ((node->type != ACPI_TYPE_REGION) && + (node != acpi_gbl_root_node)) { + return (AE_OK); + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + /* No object, just exit */ + + return (AE_OK); + } + + /* Object is a Region */ + + if (obj_desc->region.space_id != space_id) { + /* + * This region is for a different address space + * -- just ignore it + */ + return (AE_OK); + } + + status = acpi_ev_execute_reg_method (obj_desc, 1); + return (status); +} + diff --git a/drivers/acpi/events/evrgnini.c b/drivers/acpi/events/evrgnini.c new file mode 100644 index 00000000000..4983a3378be --- /dev/null +++ b/drivers/acpi/events/evrgnini.c @@ -0,0 +1,580 @@ +/****************************************************************************** + * + * Module Name: evrgnini- ACPI address_space (op_region) init + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acevents.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evrgnini") + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_system_memory_region_setup + * + * PARAMETERS: Handle - Region we are interested in + * Function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling, a nop for now + * + ******************************************************************************/ + +acpi_status +acpi_ev_system_memory_region_setup ( + acpi_handle handle, + u32 function, + void *handler_context, + void **region_context) +{ + union acpi_operand_object *region_desc = (union acpi_operand_object *) handle; + struct acpi_mem_space_context *local_region_context; + + + ACPI_FUNCTION_TRACE ("ev_system_memory_region_setup"); + + + if (function == ACPI_REGION_DEACTIVATE) { + if (*region_context) { + ACPI_MEM_FREE (*region_context); + *region_context = NULL; + } + return_ACPI_STATUS (AE_OK); + } + + /* Create a new context */ + + local_region_context = ACPI_MEM_CALLOCATE (sizeof (struct acpi_mem_space_context)); + if (!(local_region_context)) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Save the region length and address for use in the handler */ + + local_region_context->length = region_desc->region.length; + local_region_context->address = region_desc->region.address; + + *region_context = local_region_context; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_io_space_region_setup + * + * PARAMETERS: Handle - Region we are interested in + * Function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling + * + ******************************************************************************/ + +acpi_status +acpi_ev_io_space_region_setup ( + acpi_handle handle, + u32 function, + void *handler_context, + void **region_context) +{ + ACPI_FUNCTION_TRACE ("ev_io_space_region_setup"); + + + if (function == ACPI_REGION_DEACTIVATE) { + *region_context = NULL; + } + else { + *region_context = handler_context; + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_pci_config_region_setup + * + * PARAMETERS: Handle - Region we are interested in + * Function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling + * + * MUTEX: Assumes namespace is not locked + * + ******************************************************************************/ + +acpi_status +acpi_ev_pci_config_region_setup ( + acpi_handle handle, + u32 function, + void *handler_context, + void **region_context) +{ + acpi_status status = AE_OK; + acpi_integer pci_value; + struct acpi_pci_id *pci_id = *region_context; + union acpi_operand_object *handler_obj; + struct acpi_namespace_node *parent_node; + struct acpi_namespace_node *pci_root_node; + union acpi_operand_object *region_obj = (union acpi_operand_object *) handle; + struct acpi_device_id object_hID; + + + ACPI_FUNCTION_TRACE ("ev_pci_config_region_setup"); + + + handler_obj = region_obj->region.handler; + if (!handler_obj) { + /* + * No installed handler. This shouldn't happen because the dispatch + * routine checks before we get here, but we check again just in case. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Attempting to init a region %p, with no handler\n", region_obj)); + return_ACPI_STATUS (AE_NOT_EXIST); + } + + *region_context = NULL; + if (function == ACPI_REGION_DEACTIVATE) { + if (pci_id) { + ACPI_MEM_FREE (pci_id); + } + return_ACPI_STATUS (status); + } + + parent_node = acpi_ns_get_parent_node (region_obj->region.node); + + /* + * Get the _SEG and _BBN values from the device upon which the handler + * is installed. + * + * We need to get the _SEG and _BBN objects relative to the PCI BUS device. + * This is the device the handler has been registered to handle. + */ + + /* + * If the address_space.Node is still pointing to the root, we need + * to scan upward for a PCI Root bridge and re-associate the op_region + * handlers with that device. + */ + if (handler_obj->address_space.node == acpi_gbl_root_node) { + /* Start search from the parent object */ + + pci_root_node = parent_node; + while (pci_root_node != acpi_gbl_root_node) { + status = acpi_ut_execute_HID (pci_root_node, &object_hID); + if (ACPI_SUCCESS (status)) { + /* Got a valid _HID, check if this is a PCI root */ + + if (!(ACPI_STRNCMP (object_hID.value, PCI_ROOT_HID_STRING, + sizeof (PCI_ROOT_HID_STRING)))) { + /* Install a handler for this PCI root bridge */ + + status = acpi_install_address_space_handler ((acpi_handle) pci_root_node, + ACPI_ADR_SPACE_PCI_CONFIG, + ACPI_DEFAULT_HANDLER, NULL, NULL); + if (ACPI_FAILURE (status)) { + if (status == AE_SAME_HANDLER) { + /* + * It is OK if the handler is already installed on the root + * bridge. Still need to return a context object for the + * new PCI_Config operation region, however. + */ + status = AE_OK; + } + else { + ACPI_REPORT_ERROR (( + "Could not install pci_config handler for Root Bridge %4.4s, %s\n", + acpi_ut_get_node_name (pci_root_node), acpi_format_exception (status))); + } + } + break; + } + } + + pci_root_node = acpi_ns_get_parent_node (pci_root_node); + } + + /* PCI root bridge not found, use namespace root node */ + } + else { + pci_root_node = handler_obj->address_space.node; + } + + /* + * If this region is now initialized, we are done. + * (install_address_space_handler could have initialized it) + */ + if (region_obj->region.flags & AOPOBJ_SETUP_COMPLETE) { + return_ACPI_STATUS (AE_OK); + } + + /* Region is still not initialized. Create a new context */ + + pci_id = ACPI_MEM_CALLOCATE (sizeof (struct acpi_pci_id)); + if (!pci_id) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* + * For PCI_Config space access, we need the segment, bus, + * device and function numbers. Acquire them here. + */ + + /* + * Get the PCI device and function numbers from the _ADR object + * contained in the parent's scope. + */ + status = acpi_ut_evaluate_numeric_object (METHOD_NAME__ADR, parent_node, &pci_value); + + /* + * The default is zero, and since the allocation above zeroed + * the data, just do nothing on failure. + */ + if (ACPI_SUCCESS (status)) { + pci_id->device = ACPI_HIWORD (ACPI_LODWORD (pci_value)); + pci_id->function = ACPI_LOWORD (ACPI_LODWORD (pci_value)); + } + + /* The PCI segment number comes from the _SEG method */ + + status = acpi_ut_evaluate_numeric_object (METHOD_NAME__SEG, pci_root_node, &pci_value); + if (ACPI_SUCCESS (status)) { + pci_id->segment = ACPI_LOWORD (pci_value); + } + + /* The PCI bus number comes from the _BBN method */ + + status = acpi_ut_evaluate_numeric_object (METHOD_NAME__BBN, pci_root_node, &pci_value); + if (ACPI_SUCCESS (status)) { + pci_id->bus = ACPI_LOWORD (pci_value); + } + + /* Complete this device's pci_id */ + + acpi_os_derive_pci_id (pci_root_node, region_obj->region.node, &pci_id); + + *region_context = pci_id; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_pci_bar_region_setup + * + * PARAMETERS: Handle - Region we are interested in + * Function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling + * + * MUTEX: Assumes namespace is not locked + * + ******************************************************************************/ + +acpi_status +acpi_ev_pci_bar_region_setup ( + acpi_handle handle, + u32 function, + void *handler_context, + void **region_context) +{ + ACPI_FUNCTION_TRACE ("ev_pci_bar_region_setup"); + + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_cmos_region_setup + * + * PARAMETERS: Handle - Region we are interested in + * Function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling + * + * MUTEX: Assumes namespace is not locked + * + ******************************************************************************/ + +acpi_status +acpi_ev_cmos_region_setup ( + acpi_handle handle, + u32 function, + void *handler_context, + void **region_context) +{ + ACPI_FUNCTION_TRACE ("ev_cmos_region_setup"); + + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_default_region_setup + * + * PARAMETERS: Handle - Region we are interested in + * Function - Start or stop + * handler_context - Address space handler context + * region_context - Region specific context + * + * RETURN: Status + * + * DESCRIPTION: Do any prep work for region handling + * + ******************************************************************************/ + +acpi_status +acpi_ev_default_region_setup ( + acpi_handle handle, + u32 function, + void *handler_context, + void **region_context) +{ + ACPI_FUNCTION_TRACE ("ev_default_region_setup"); + + + if (function == ACPI_REGION_DEACTIVATE) { + *region_context = NULL; + } + else { + *region_context = handler_context; + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_initialize_region + * + * PARAMETERS: region_obj - Region we are initializing + * acpi_ns_locked - Is namespace locked? + * + * RETURN: Status + * + * DESCRIPTION: Initializes the region, finds any _REG methods and saves them + * for execution at a later time + * + * Get the appropriate address space handler for a newly + * created region. + * + * This also performs address space specific initialization. For + * example, PCI regions must have an _ADR object that contains + * a PCI address in the scope of the definition. This address is + * required to perform an access to PCI config space. + * + ******************************************************************************/ + +acpi_status +acpi_ev_initialize_region ( + union acpi_operand_object *region_obj, + u8 acpi_ns_locked) +{ + union acpi_operand_object *handler_obj; + union acpi_operand_object *obj_desc; + acpi_adr_space_type space_id; + struct acpi_namespace_node *node; + acpi_status status; + struct acpi_namespace_node *method_node; + acpi_name *reg_name_ptr = (acpi_name *) METHOD_NAME__REG; + union acpi_operand_object *region_obj2; + + + ACPI_FUNCTION_TRACE_U32 ("ev_initialize_region", acpi_ns_locked); + + + if (!region_obj) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (region_obj->common.flags & AOPOBJ_OBJECT_INITIALIZED) { + return_ACPI_STATUS (AE_OK); + } + + region_obj2 = acpi_ns_get_secondary_object (region_obj); + if (!region_obj2) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + + node = acpi_ns_get_parent_node (region_obj->region.node); + space_id = region_obj->region.space_id; + + /* Setup defaults */ + + region_obj->region.handler = NULL; + region_obj2->extra.method_REG = NULL; + region_obj->common.flags &= ~(AOPOBJ_SETUP_COMPLETE); + region_obj->common.flags |= AOPOBJ_OBJECT_INITIALIZED; + + /* Find any "_REG" method associated with this region definition */ + + status = acpi_ns_search_node (*reg_name_ptr, node, + ACPI_TYPE_METHOD, &method_node); + if (ACPI_SUCCESS (status)) { + /* + * The _REG method is optional and there can be only one per region + * definition. This will be executed when the handler is attached + * or removed + */ + region_obj2->extra.method_REG = method_node; + } + + /* + * The following loop depends upon the root Node having no parent + * ie: acpi_gbl_root_node->parent_entry being set to NULL + */ + while (node) { + /* Check to see if a handler exists */ + + handler_obj = NULL; + obj_desc = acpi_ns_get_attached_object (node); + if (obj_desc) { + /* Can only be a handler if the object exists */ + + switch (node->type) { + case ACPI_TYPE_DEVICE: + + handler_obj = obj_desc->device.handler; + break; + + case ACPI_TYPE_PROCESSOR: + + handler_obj = obj_desc->processor.handler; + break; + + case ACPI_TYPE_THERMAL: + + handler_obj = obj_desc->thermal_zone.handler; + break; + + default: + /* Ignore other objects */ + break; + } + + while (handler_obj) { + /* Is this handler of the correct type? */ + + if (handler_obj->address_space.space_id == space_id) { + /* Found correct handler */ + + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Found handler %p for region %p in obj %p\n", + handler_obj, region_obj, obj_desc)); + + status = acpi_ev_attach_region (handler_obj, region_obj, + acpi_ns_locked); + + /* + * Tell all users that this region is usable by running the _REG + * method + */ + if (acpi_ns_locked) { + status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + status = acpi_ev_execute_reg_method (region_obj, 1); + + if (acpi_ns_locked) { + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + return_ACPI_STATUS (AE_OK); + } + + /* Try next handler in the list */ + + handler_obj = handler_obj->address_space.next; + } + } + + /* + * This node does not have the handler we need; + * Pop up one level + */ + node = acpi_ns_get_parent_node (node); + } + + /* If we get here, there is no handler for this region */ + + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "No handler for region_type %s(%X) (region_obj %p)\n", + acpi_ut_get_region_name (space_id), space_id, region_obj)); + + return_ACPI_STATUS (AE_NOT_EXIST); +} + diff --git a/drivers/acpi/events/evsci.c b/drivers/acpi/events/evsci.c new file mode 100644 index 00000000000..46b31995c82 --- /dev/null +++ b/drivers/acpi/events/evsci.c @@ -0,0 +1,199 @@ +/******************************************************************************* + * + * Module Name: evsci - System Control Interrupt configuration and + * legacy to ACPI mode state transition functions + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <acpi/acpi.h> +#include <acpi/acevents.h> + + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evsci") + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_sci_xrupt_handler + * + * PARAMETERS: Context - Calling Context + * + * RETURN: Status code indicates whether interrupt was handled. + * + * DESCRIPTION: Interrupt handler that will figure out what function or + * control method to call to deal with a SCI. + * + ******************************************************************************/ + +static u32 ACPI_SYSTEM_XFACE +acpi_ev_sci_xrupt_handler ( + void *context) +{ + struct acpi_gpe_xrupt_info *gpe_xrupt_list = context; + u32 interrupt_handled = ACPI_INTERRUPT_NOT_HANDLED; + + + ACPI_FUNCTION_TRACE("ev_sci_xrupt_handler"); + + + /* + * We are guaranteed by the ACPI CA initialization/shutdown code that + * if this interrupt handler is installed, ACPI is enabled. + */ + + /* + * Fixed Events: + * Check for and dispatch any Fixed Events that have occurred + */ + interrupt_handled |= acpi_ev_fixed_event_detect (); + + /* + * General Purpose Events: + * Check for and dispatch any GPEs that have occurred + */ + interrupt_handled |= acpi_ev_gpe_detect (gpe_xrupt_list); + + return_VALUE (interrupt_handled); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ev_gpe_xrupt_handler + * + * PARAMETERS: Context - Calling Context + * + * RETURN: Status code indicates whether interrupt was handled. + * + * DESCRIPTION: Handler for GPE Block Device interrupts + * + ******************************************************************************/ + +u32 ACPI_SYSTEM_XFACE +acpi_ev_gpe_xrupt_handler ( + void *context) +{ + struct acpi_gpe_xrupt_info *gpe_xrupt_list = context; + u32 interrupt_handled = ACPI_INTERRUPT_NOT_HANDLED; + + + ACPI_FUNCTION_TRACE("ev_gpe_xrupt_handler"); + + + /* + * We are guaranteed by the ACPI CA initialization/shutdown code that + * if this interrupt handler is installed, ACPI is enabled. + */ + + /* + * GPEs: + * Check for and dispatch any GPEs that have occurred + */ + interrupt_handled |= acpi_ev_gpe_detect (gpe_xrupt_list); + + return_VALUE (interrupt_handled); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_ev_install_sci_handler + * + * PARAMETERS: none + * + * RETURN: Status + * + * DESCRIPTION: Installs SCI handler. + * + ******************************************************************************/ + +u32 +acpi_ev_install_sci_handler (void) +{ + u32 status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ev_install_sci_handler"); + + + status = acpi_os_install_interrupt_handler ((u32) acpi_gbl_FADT->sci_int, + acpi_ev_sci_xrupt_handler, acpi_gbl_gpe_xrupt_list_head); + return_ACPI_STATUS (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_ev_remove_sci_handler + * + * PARAMETERS: none + * + * RETURN: E_OK if handler uninstalled OK, E_ERROR if handler was not + * installed to begin with + * + * DESCRIPTION: Remove the SCI interrupt handler. No further SCIs will be + * taken. + * + * Note: It doesn't seem important to disable all events or set the event + * enable registers to their original values. The OS should disable + * the SCI interrupt level when the handler is removed, so no more + * events will come in. + * + ******************************************************************************/ + +acpi_status +acpi_ev_remove_sci_handler (void) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ev_remove_sci_handler"); + + + /* Just let the OS remove the handler and disable the level */ + + status = acpi_os_remove_interrupt_handler ((u32) acpi_gbl_FADT->sci_int, + acpi_ev_sci_xrupt_handler); + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/events/evxface.c b/drivers/acpi/events/evxface.c new file mode 100644 index 00000000000..0bfec10a5f1 --- /dev/null +++ b/drivers/acpi/events/evxface.c @@ -0,0 +1,834 @@ +/****************************************************************************** + * + * Module Name: evxface - External interfaces for ACPI events + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acevents.h> +#include <acpi/acinterp.h> + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evxface") + + +/******************************************************************************* + * + * FUNCTION: acpi_install_exception_handler + * + * PARAMETERS: Handler - Pointer to the handler function for the + * event + * + * RETURN: Status + * + * DESCRIPTION: Saves the pointer to the handler function + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_install_exception_handler ( + acpi_exception_handler handler) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_install_exception_handler"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Don't allow two handlers. */ + + if (acpi_gbl_exception_handler) { + status = AE_ALREADY_EXISTS; + goto cleanup; + } + + /* Install the handler */ + + acpi_gbl_exception_handler = handler; + +cleanup: + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + return_ACPI_STATUS (status); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_install_fixed_event_handler + * + * PARAMETERS: Event - Event type to enable. + * Handler - Pointer to the handler function for the + * event + * Context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Saves the pointer to the handler function and then enables the + * event. + * + ******************************************************************************/ + +acpi_status +acpi_install_fixed_event_handler ( + u32 event, + acpi_event_handler handler, + void *context) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_install_fixed_event_handler"); + + + /* Parameter validation */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Don't allow two handlers. */ + + if (NULL != acpi_gbl_fixed_event_handlers[event].handler) { + status = AE_ALREADY_EXISTS; + goto cleanup; + } + + /* Install the handler before enabling the event */ + + acpi_gbl_fixed_event_handlers[event].handler = handler; + acpi_gbl_fixed_event_handlers[event].context = context; + + status = acpi_clear_event (event); + if (ACPI_SUCCESS(status)) + status = acpi_enable_event (event, 0); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Could not enable fixed event.\n")); + + /* Remove the handler */ + + acpi_gbl_fixed_event_handlers[event].handler = NULL; + acpi_gbl_fixed_event_handlers[event].context = NULL; + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Enabled fixed event %X, Handler=%p\n", event, handler)); + } + + +cleanup: + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_install_fixed_event_handler); + + +/******************************************************************************* + * + * FUNCTION: acpi_remove_fixed_event_handler + * + * PARAMETERS: Event - Event type to disable. + * Handler - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Disables the event and unregisters the event handler. + * + ******************************************************************************/ + +acpi_status +acpi_remove_fixed_event_handler ( + u32 event, + acpi_event_handler handler) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("acpi_remove_fixed_event_handler"); + + + /* Parameter validation */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Disable the event before removing the handler */ + + status = acpi_disable_event (event, 0); + + /* Always Remove the handler */ + + acpi_gbl_fixed_event_handlers[event].handler = NULL; + acpi_gbl_fixed_event_handlers[event].context = NULL; + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, + "Could not write to fixed event enable register.\n")); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Disabled fixed event %X.\n", event)); + } + + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_remove_fixed_event_handler); + + +/******************************************************************************* + * + * FUNCTION: acpi_install_notify_handler + * + * PARAMETERS: Device - The device for which notifies will be handled + * handler_type - The type of handler: + * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) + * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) + * ACPI_ALL_NOTIFY: both system and device + * Handler - Address of the handler + * Context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for notifies on an ACPI device + * + ******************************************************************************/ + +acpi_status +acpi_install_notify_handler ( + acpi_handle device, + u32 handler_type, + acpi_notify_handler handler, + void *context) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *notify_obj; + struct acpi_namespace_node *node; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_install_notify_handler"); + + + /* Parameter validation */ + + if ((!device) || + (!handler) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Convert and validate the device handle */ + + node = acpi_ns_map_handle_to_node (device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* + * Root Object: + * Registering a notify handler on the root object indicates that the + * caller wishes to receive notifications for all objects. Note that + * only one <external> global handler can be regsitered (per notify type). + */ + if (device == ACPI_ROOT_OBJECT) { + /* Make sure the handler is not already installed */ + + if (((handler_type & ACPI_SYSTEM_NOTIFY) && + acpi_gbl_system_notify.handler) || + ((handler_type & ACPI_DEVICE_NOTIFY) && + acpi_gbl_device_notify.handler)) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + + if (handler_type & ACPI_SYSTEM_NOTIFY) { + acpi_gbl_system_notify.node = node; + acpi_gbl_system_notify.handler = handler; + acpi_gbl_system_notify.context = context; + } + + if (handler_type & ACPI_DEVICE_NOTIFY) { + acpi_gbl_device_notify.node = node; + acpi_gbl_device_notify.handler = handler; + acpi_gbl_device_notify.context = context; + } + + /* Global notify handler installed */ + } + + /* + * All Other Objects: + * Caller will only receive notifications specific to the target object. + * Note that only certain object types can receive notifications. + */ + else { + /* Notifies allowed on this object? */ + + if (!acpi_ev_is_notify_object (node)) { + status = AE_TYPE; + goto unlock_and_exit; + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object (node); + if (obj_desc) { + /* Object exists - make sure there's no handler */ + + if (((handler_type & ACPI_SYSTEM_NOTIFY) && + obj_desc->common_notify.system_notify) || + ((handler_type & ACPI_DEVICE_NOTIFY) && + obj_desc->common_notify.device_notify)) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + } + else { + /* Create a new object */ + + obj_desc = acpi_ut_create_internal_object (node->type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* Attach new object to the Node */ + + status = acpi_ns_attach_object (device, obj_desc, node->type); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + } + + /* Install the handler */ + + notify_obj = acpi_ut_create_internal_object (ACPI_TYPE_LOCAL_NOTIFY); + if (!notify_obj) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + notify_obj->notify.node = node; + notify_obj->notify.handler = handler; + notify_obj->notify.context = context; + + if (handler_type & ACPI_SYSTEM_NOTIFY) { + obj_desc->common_notify.system_notify = notify_obj; + } + + if (handler_type & ACPI_DEVICE_NOTIFY) { + obj_desc->common_notify.device_notify = notify_obj; + } + + if (handler_type == ACPI_ALL_NOTIFY) { + /* Extra ref if installed in both */ + + acpi_ut_add_reference (notify_obj); + } + } + + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_install_notify_handler); + + +/******************************************************************************* + * + * FUNCTION: acpi_remove_notify_handler + * + * PARAMETERS: Device - The device for which notifies will be handled + * handler_type - The type of handler: + * ACPI_SYSTEM_NOTIFY: system_handler (00-7f) + * ACPI_DEVICE_NOTIFY: driver_handler (80-ff) + * ACPI_ALL_NOTIFY: both system and device + * Handler - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Remove a handler for notifies on an ACPI device + * + ******************************************************************************/ + +acpi_status +acpi_remove_notify_handler ( + acpi_handle device, + u32 handler_type, + acpi_notify_handler handler) +{ + union acpi_operand_object *notify_obj; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_remove_notify_handler"); + + + /* Parameter validation */ + + if ((!device) || + (!handler) || + (handler_type > ACPI_MAX_NOTIFY_HANDLER_TYPE)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Convert and validate the device handle */ + + node = acpi_ns_map_handle_to_node (device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Root Object */ + + if (device == ACPI_ROOT_OBJECT) { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Removing notify handler for ROOT object.\n")); + + if (((handler_type & ACPI_SYSTEM_NOTIFY) && + !acpi_gbl_system_notify.handler) || + ((handler_type & ACPI_DEVICE_NOTIFY) && + !acpi_gbl_device_notify.handler)) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + /* Make sure all deferred tasks are completed */ + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + acpi_os_wait_events_complete(NULL); + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (handler_type & ACPI_SYSTEM_NOTIFY) { + acpi_gbl_system_notify.node = NULL; + acpi_gbl_system_notify.handler = NULL; + acpi_gbl_system_notify.context = NULL; + } + + if (handler_type & ACPI_DEVICE_NOTIFY) { + acpi_gbl_device_notify.node = NULL; + acpi_gbl_device_notify.handler = NULL; + acpi_gbl_device_notify.context = NULL; + } + } + + /* All Other Objects */ + + else { + /* Notifies allowed on this object? */ + + if (!acpi_ev_is_notify_object (node)) { + status = AE_TYPE; + goto unlock_and_exit; + } + + /* Check for an existing internal object */ + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + /* Object exists - make sure there's an existing handler */ + + if (handler_type & ACPI_SYSTEM_NOTIFY) { + notify_obj = obj_desc->common_notify.system_notify; + if ((!notify_obj) || + (notify_obj->notify.handler != handler)) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + /* Make sure all deferred tasks are completed */ + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + acpi_os_wait_events_complete(NULL); + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Remove the handler */ + obj_desc->common_notify.system_notify = NULL; + acpi_ut_remove_reference (notify_obj); + } + + if (handler_type & ACPI_DEVICE_NOTIFY) { + notify_obj = obj_desc->common_notify.device_notify; + if ((!notify_obj) || + (notify_obj->notify.handler != handler)) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + /* Make sure all deferred tasks are completed */ + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + acpi_os_wait_events_complete(NULL); + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Remove the handler */ + obj_desc->common_notify.device_notify = NULL; + acpi_ut_remove_reference (notify_obj); + } + } + + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_remove_notify_handler); + + +/******************************************************************************* + * + * FUNCTION: acpi_install_gpe_handler + * + * PARAMETERS: gpe_number - The GPE number within the GPE block + * gpe_block - GPE block (NULL == FADT GPEs) + * Type - Whether this GPE should be treated as an + * edge- or level-triggered interrupt. + * Address - Address of the handler + * Context - Value passed to the handler on each GPE + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for a General Purpose Event. + * + ******************************************************************************/ + +acpi_status +acpi_install_gpe_handler ( + acpi_handle gpe_device, + u32 gpe_number, + u32 type, + acpi_event_handler address, + void *context) +{ + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_handler_info *handler; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_install_gpe_handler"); + + + /* Parameter validation */ + + if ((!address) || (type > ACPI_GPE_XRUPT_TYPE_MASK)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info (gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Make sure that there isn't a handler there already */ + + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) == ACPI_GPE_DISPATCH_HANDLER) { + status = AE_ALREADY_EXISTS; + goto unlock_and_exit; + } + + /* Allocate and init handler object */ + + handler = ACPI_MEM_CALLOCATE (sizeof (struct acpi_handler_info)); + if (!handler) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + handler->address = address; + handler->context = context; + handler->method_node = gpe_event_info->dispatch.method_node; + + /* Disable the GPE before installing the handler */ + + status = acpi_ev_disable_gpe (gpe_event_info); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* Install the handler */ + + acpi_os_acquire_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + gpe_event_info->dispatch.handler = handler; + + /* Setup up dispatch flags to indicate handler (vs. method) */ + + gpe_event_info->flags &= ~(ACPI_GPE_XRUPT_TYPE_MASK | ACPI_GPE_DISPATCH_MASK); /* Clear bits */ + gpe_event_info->flags |= (u8) (type | ACPI_GPE_DISPATCH_HANDLER); + + acpi_os_release_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_install_gpe_handler); + + +/******************************************************************************* + * + * FUNCTION: acpi_remove_gpe_handler + * + * PARAMETERS: gpe_number - The event to remove a handler + * gpe_block - GPE block (NULL == FADT GPEs) + * Address - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Remove a handler for a General Purpose acpi_event. + * + ******************************************************************************/ + +acpi_status +acpi_remove_gpe_handler ( + acpi_handle gpe_device, + u32 gpe_number, + acpi_event_handler address) +{ + struct acpi_gpe_event_info *gpe_event_info; + struct acpi_handler_info *handler; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_remove_gpe_handler"); + + + /* Parameter validation */ + + if (!address) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info (gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Make sure that a handler is indeed installed */ + + if ((gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) != ACPI_GPE_DISPATCH_HANDLER) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + /* Make sure that the installed handler is the same */ + + if (gpe_event_info->dispatch.handler->address != address) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Disable the GPE before removing the handler */ + + status = acpi_ev_disable_gpe (gpe_event_info); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* Make sure all deferred tasks are completed */ + + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + acpi_os_wait_events_complete(NULL); + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Remove the handler */ + + acpi_os_acquire_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + handler = gpe_event_info->dispatch.handler; + + /* Restore Method node (if any), set dispatch flags */ + + gpe_event_info->dispatch.method_node = handler->method_node; + gpe_event_info->flags &= ~ACPI_GPE_DISPATCH_MASK; /* Clear bits */ + if (handler->method_node) { + gpe_event_info->flags |= ACPI_GPE_DISPATCH_METHOD; + } + acpi_os_release_lock (acpi_gbl_gpe_lock, ACPI_NOT_ISR); + + /* Now we can free the handler object */ + + ACPI_MEM_FREE (handler); + + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_remove_gpe_handler); + + +/******************************************************************************* + * + * FUNCTION: acpi_acquire_global_lock + * + * PARAMETERS: Timeout - How long the caller is willing to wait + * out_handle - A handle to the lock if acquired + * + * RETURN: Status + * + * DESCRIPTION: Acquire the ACPI Global Lock + * + ******************************************************************************/ + +acpi_status +acpi_acquire_global_lock ( + u16 timeout, + u32 *handle) +{ + acpi_status status; + + + if (!handle) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ex_enter_interpreter (); + if (ACPI_FAILURE (status)) { + return (status); + } + + status = acpi_ev_acquire_global_lock (timeout); + acpi_ex_exit_interpreter (); + + if (ACPI_SUCCESS (status)) { + acpi_gbl_global_lock_handle++; + *handle = acpi_gbl_global_lock_handle; + } + + return (status); +} +EXPORT_SYMBOL(acpi_acquire_global_lock); + + +/******************************************************************************* + * + * FUNCTION: acpi_release_global_lock + * + * PARAMETERS: Handle - Returned from acpi_acquire_global_lock + * + * RETURN: Status + * + * DESCRIPTION: Release the ACPI Global Lock + * + ******************************************************************************/ + +acpi_status +acpi_release_global_lock ( + u32 handle) +{ + acpi_status status; + + + if (handle != acpi_gbl_global_lock_handle) { + return (AE_NOT_ACQUIRED); + } + + status = acpi_ev_release_global_lock (); + return (status); +} +EXPORT_SYMBOL(acpi_release_global_lock); + diff --git a/drivers/acpi/events/evxfevnt.c b/drivers/acpi/events/evxfevnt.c new file mode 100644 index 00000000000..fa8d5f25be6 --- /dev/null +++ b/drivers/acpi/events/evxfevnt.c @@ -0,0 +1,778 @@ +/****************************************************************************** + * + * Module Name: evxfevnt - External Interfaces, ACPI event disable/enable + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acevents.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evxfevnt") + + +/******************************************************************************* + * + * FUNCTION: acpi_enable + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Transfers the system into ACPI mode. + * + ******************************************************************************/ + +acpi_status +acpi_enable (void) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("acpi_enable"); + + + /* Make sure we have the FADT*/ + + if (!acpi_gbl_FADT) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "No FADT information present!\n")); + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + if (acpi_hw_get_mode() == ACPI_SYS_MODE_ACPI) { + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "System is already in ACPI mode\n")); + } + else { + /* Transition to ACPI mode */ + + status = acpi_hw_set_mode (ACPI_SYS_MODE_ACPI); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not transition to ACPI mode.\n")); + return_ACPI_STATUS (status); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "Transition to ACPI mode successful\n")); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_disable + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Transfers the system into LEGACY mode. + * + ******************************************************************************/ + +acpi_status +acpi_disable (void) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("acpi_disable"); + + + if (!acpi_gbl_FADT) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "No FADT information present!\n")); + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + if (acpi_hw_get_mode() == ACPI_SYS_MODE_LEGACY) { + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "System is already in legacy (non-ACPI) mode\n")); + } + else { + /* Transition to LEGACY mode */ + + status = acpi_hw_set_mode (ACPI_SYS_MODE_LEGACY); + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not exit ACPI mode to legacy mode")); + return_ACPI_STATUS (status); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "ACPI mode disabled\n")); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_enable_event + * + * PARAMETERS: Event - The fixed eventto be enabled + * Flags - Reserved + * + * RETURN: Status + * + * DESCRIPTION: Enable an ACPI event (fixed) + * + ******************************************************************************/ + +acpi_status +acpi_enable_event ( + u32 event, + u32 flags) +{ + acpi_status status = AE_OK; + u32 value; + + + ACPI_FUNCTION_TRACE ("acpi_enable_event"); + + + /* Decode the Fixed Event */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Enable the requested fixed event (by writing a one to the + * enable register bit) + */ + status = acpi_set_register (acpi_gbl_fixed_event_info[event].enable_register_id, + 1, ACPI_MTX_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Make sure that the hardware responded */ + + status = acpi_get_register (acpi_gbl_fixed_event_info[event].enable_register_id, + &value, ACPI_MTX_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (value != 1) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not enable %s event\n", acpi_ut_get_event_name (event))); + return_ACPI_STATUS (AE_NO_HARDWARE_RESPONSE); + } + + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_enable_event); + + +/******************************************************************************* + * + * FUNCTION: acpi_set_gpe_type + * + * PARAMETERS: gpe_device - Parent GPE Device + * gpe_number - GPE level within the GPE block + * Type - New GPE type + * + * RETURN: Status + * + * DESCRIPTION: Enable an ACPI event (general purpose) + * + ******************************************************************************/ + +acpi_status +acpi_set_gpe_type ( + acpi_handle gpe_device, + u32 gpe_number, + u8 type) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + + + ACPI_FUNCTION_TRACE ("acpi_set_gpe_type"); + + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info (gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + if ((gpe_event_info->flags & ACPI_GPE_TYPE_MASK) == type) { + return_ACPI_STATUS (AE_OK); + } + + /* Set the new type (will disable GPE if currently enabled) */ + + status = acpi_ev_set_gpe_type (gpe_event_info, type); + +unlock_and_exit: + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_set_gpe_type); + + +/******************************************************************************* + * + * FUNCTION: acpi_enable_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device + * gpe_number - GPE level within the GPE block + * Flags - Just enable, or also wake enable? + * Called from ISR or not + * + * RETURN: Status + * + * DESCRIPTION: Enable an ACPI event (general purpose) + * + ******************************************************************************/ + +acpi_status +acpi_enable_gpe ( + acpi_handle gpe_device, + u32 gpe_number, + u32 flags) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + + + ACPI_FUNCTION_TRACE ("acpi_enable_gpe"); + + + /* Use semaphore lock if not executing at interrupt level */ + + if (flags & ACPI_NOT_ISR) { + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info (gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Perform the enable */ + + status = acpi_ev_enable_gpe (gpe_event_info, TRUE); + +unlock_and_exit: + if (flags & ACPI_NOT_ISR) { + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + } + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_enable_gpe); + + +/******************************************************************************* + * + * FUNCTION: acpi_disable_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device + * gpe_number - GPE level within the GPE block + * Flags - Just disable, or also wake disable? + * Called from ISR or not + * + * RETURN: Status + * + * DESCRIPTION: Disable an ACPI event (general purpose) + * + ******************************************************************************/ + +acpi_status +acpi_disable_gpe ( + acpi_handle gpe_device, + u32 gpe_number, + u32 flags) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + + + ACPI_FUNCTION_TRACE ("acpi_disable_gpe"); + + + /* Use semaphore lock if not executing at interrupt level */ + + if (flags & ACPI_NOT_ISR) { + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info (gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ev_disable_gpe (gpe_event_info); + +unlock_and_exit: + if (flags & ACPI_NOT_ISR) { + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + } + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_disable_event + * + * PARAMETERS: Event - The fixed eventto be enabled + * Flags - Reserved + * + * RETURN: Status + * + * DESCRIPTION: Disable an ACPI event (fixed) + * + ******************************************************************************/ + +acpi_status +acpi_disable_event ( + u32 event, + u32 flags) +{ + acpi_status status = AE_OK; + u32 value; + + + ACPI_FUNCTION_TRACE ("acpi_disable_event"); + + + /* Decode the Fixed Event */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Disable the requested fixed event (by writing a zero to the + * enable register bit) + */ + status = acpi_set_register (acpi_gbl_fixed_event_info[event].enable_register_id, + 0, ACPI_MTX_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_get_register (acpi_gbl_fixed_event_info[event].enable_register_id, + &value, ACPI_MTX_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (value != 0) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not disable %s events\n", acpi_ut_get_event_name (event))); + return_ACPI_STATUS (AE_NO_HARDWARE_RESPONSE); + } + + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_disable_event); + + +/******************************************************************************* + * + * FUNCTION: acpi_clear_event + * + * PARAMETERS: Event - The fixed event to be cleared + * + * RETURN: Status + * + * DESCRIPTION: Clear an ACPI event (fixed) + * + ******************************************************************************/ + +acpi_status +acpi_clear_event ( + u32 event) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("acpi_clear_event"); + + + /* Decode the Fixed Event */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Clear the requested fixed event (By writing a one to the + * status register bit) + */ + status = acpi_set_register (acpi_gbl_fixed_event_info[event].status_register_id, + 1, ACPI_MTX_LOCK); + + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_clear_event); + + +/******************************************************************************* + * + * FUNCTION: acpi_clear_gpe + * + * PARAMETERS: gpe_device - Parent GPE Device + * gpe_number - GPE level within the GPE block + * Flags - Called from an ISR or not + * + * RETURN: Status + * + * DESCRIPTION: Clear an ACPI event (general purpose) + * + ******************************************************************************/ + +acpi_status +acpi_clear_gpe ( + acpi_handle gpe_device, + u32 gpe_number, + u32 flags) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + + + ACPI_FUNCTION_TRACE ("acpi_clear_gpe"); + + + /* Use semaphore lock if not executing at interrupt level */ + + if (flags & ACPI_NOT_ISR) { + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info (gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_hw_clear_gpe (gpe_event_info); + +unlock_and_exit: + if (flags & ACPI_NOT_ISR) { + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + } + return_ACPI_STATUS (status); +} + + +#ifdef ACPI_FUTURE_USAGE + +/******************************************************************************* + * + * FUNCTION: acpi_get_event_status + * + * PARAMETERS: Event - The fixed event + * Event Status - Where the current status of the event will + * be returned + * + * RETURN: Status + * + * DESCRIPTION: Obtains and returns the current status of the event + * + ******************************************************************************/ + +acpi_status +acpi_get_event_status ( + u32 event, + acpi_event_status *event_status) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("acpi_get_event_status"); + + + if (!event_status) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Decode the Fixed Event */ + + if (event > ACPI_EVENT_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Get the status of the requested fixed event */ + + status = acpi_get_register (acpi_gbl_fixed_event_info[event].status_register_id, + event_status, ACPI_MTX_LOCK); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_get_gpe_status + * + * PARAMETERS: gpe_device - Parent GPE Device + * gpe_number - GPE level within the GPE block + * Flags - Called from an ISR or not + * Event Status - Where the current status of the event will + * be returned + * + * RETURN: Status + * + * DESCRIPTION: Get status of an event (general purpose) + * + ******************************************************************************/ + +acpi_status +acpi_get_gpe_status ( + acpi_handle gpe_device, + u32 gpe_number, + u32 flags, + acpi_event_status *event_status) +{ + acpi_status status = AE_OK; + struct acpi_gpe_event_info *gpe_event_info; + + + ACPI_FUNCTION_TRACE ("acpi_get_gpe_status"); + + + /* Use semaphore lock if not executing at interrupt level */ + + if (flags & ACPI_NOT_ISR) { + status = acpi_ut_acquire_mutex (ACPI_MTX_EVENTS); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Ensure that we have a valid GPE number */ + + gpe_event_info = acpi_ev_get_gpe_event_info (gpe_device, gpe_number); + if (!gpe_event_info) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Obtain status on the requested GPE number */ + + status = acpi_hw_get_gpe_status (gpe_event_info, event_status); + +unlock_and_exit: + if (flags & ACPI_NOT_ISR) { + (void) acpi_ut_release_mutex (ACPI_MTX_EVENTS); + } + return_ACPI_STATUS (status); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_install_gpe_block + * + * PARAMETERS: gpe_device - Handle to the parent GPE Block Device + * gpe_block_address - Address and space_iD + * register_count - Number of GPE register pairs in the block + * interrupt_level - H/W interrupt for the block + * + * RETURN: Status + * + * DESCRIPTION: Create and Install a block of GPE registers + * + ******************************************************************************/ + +acpi_status +acpi_install_gpe_block ( + acpi_handle gpe_device, + struct acpi_generic_address *gpe_block_address, + u32 register_count, + u32 interrupt_level) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + struct acpi_gpe_block_info *gpe_block; + + + ACPI_FUNCTION_TRACE ("acpi_install_gpe_block"); + + + if ((!gpe_device) || + (!gpe_block_address) || + (!register_count)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + node = acpi_ns_map_handle_to_node (gpe_device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* + * For user-installed GPE Block Devices, the gpe_block_base_number + * is always zero + */ + status = acpi_ev_create_gpe_block (node, gpe_block_address, register_count, + 0, interrupt_level, &gpe_block); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* Get the device_object attached to the node */ + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + /* No object, create a new one */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_DEVICE); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + status = acpi_ns_attach_object (node, obj_desc, ACPI_TYPE_DEVICE); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + } + + /* Install the GPE block in the device_object */ + + obj_desc->device.gpe_block = gpe_block; + + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_install_gpe_block); + + +/******************************************************************************* + * + * FUNCTION: acpi_remove_gpe_block + * + * PARAMETERS: gpe_device - Handle to the parent GPE Block Device + * + * RETURN: Status + * + * DESCRIPTION: Remove a previously installed block of GPE registers + * + ******************************************************************************/ + +acpi_status +acpi_remove_gpe_block ( + acpi_handle gpe_device) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + struct acpi_namespace_node *node; + + + ACPI_FUNCTION_TRACE ("acpi_remove_gpe_block"); + + + if (!gpe_device) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + node = acpi_ns_map_handle_to_node (gpe_device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Get the device_object attached to the node */ + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc || + !obj_desc->device.gpe_block) { + return_ACPI_STATUS (AE_NULL_OBJECT); + } + + /* Delete the GPE block (but not the device_object) */ + + status = acpi_ev_delete_gpe_block (obj_desc->device.gpe_block); + if (ACPI_SUCCESS (status)) { + obj_desc->device.gpe_block = NULL; + } + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_remove_gpe_block); diff --git a/drivers/acpi/events/evxfregn.c b/drivers/acpi/events/evxfregn.c new file mode 100644 index 00000000000..d058587b342 --- /dev/null +++ b/drivers/acpi/events/evxfregn.c @@ -0,0 +1,247 @@ +/****************************************************************************** + * + * Module Name: evxfregn - External Interfaces, ACPI Operation Regions and + * Address Spaces. + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acevents.h> + +#define _COMPONENT ACPI_EVENTS + ACPI_MODULE_NAME ("evxfregn") + + +/******************************************************************************* + * + * FUNCTION: acpi_install_address_space_handler + * + * PARAMETERS: Device - Handle for the device + * space_id - The address space ID + * Handler - Address of the handler + * Setup - Address of the setup function + * Context - Value passed to the handler on each access + * + * RETURN: Status + * + * DESCRIPTION: Install a handler for all op_regions of a given space_id. + * + ******************************************************************************/ + +acpi_status +acpi_install_address_space_handler ( + acpi_handle device, + acpi_adr_space_type space_id, + acpi_adr_space_handler handler, + acpi_adr_space_setup setup, + void *context) +{ + struct acpi_namespace_node *node; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_install_address_space_handler"); + + + /* Parameter validation */ + + if (!device) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Convert and validate the device handle */ + + node = acpi_ns_map_handle_to_node (device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Install the handler for all Regions for this Space ID */ + + status = acpi_ev_install_space_handler (node, space_id, handler, setup, context); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* Run all _REG methods for this address space */ + + status = acpi_ev_execute_reg_methods (node, space_id); + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_install_address_space_handler); + + +/******************************************************************************* + * + * FUNCTION: acpi_remove_address_space_handler + * + * PARAMETERS: Device - Handle for the device + * space_id - The address space ID + * Handler - Address of the handler + * + * RETURN: Status + * + * DESCRIPTION: Remove a previously installed handler. + * + ******************************************************************************/ + +acpi_status +acpi_remove_address_space_handler ( + acpi_handle device, + acpi_adr_space_type space_id, + acpi_adr_space_handler handler) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *handler_obj; + union acpi_operand_object *region_obj; + union acpi_operand_object **last_obj_ptr; + struct acpi_namespace_node *node; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_remove_address_space_handler"); + + + /* Parameter validation */ + + if (!device) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Convert and validate the device handle */ + + node = acpi_ns_map_handle_to_node (device); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Make sure the internal object exists */ + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + status = AE_NOT_EXIST; + goto unlock_and_exit; + } + + /* Find the address handler the user requested */ + + handler_obj = obj_desc->device.handler; + last_obj_ptr = &obj_desc->device.handler; + while (handler_obj) { + /* We have a handler, see if user requested this one */ + + if (handler_obj->address_space.space_id == space_id) { + /* Matched space_id, first dereference this in the Regions */ + + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Removing address handler %p(%p) for region %s on Device %p(%p)\n", + handler_obj, handler, acpi_ut_get_region_name (space_id), + node, obj_desc)); + + region_obj = handler_obj->address_space.region_list; + + /* Walk the handler's region list */ + + while (region_obj) { + /* + * First disassociate the handler from the region. + * + * NOTE: this doesn't mean that the region goes away + * The region is just inaccessible as indicated to + * the _REG method + */ + acpi_ev_detach_region (region_obj, TRUE); + + /* + * Walk the list: Just grab the head because the + * detach_region removed the previous head. + */ + region_obj = handler_obj->address_space.region_list; + + } + + /* Remove this Handler object from the list */ + + *last_obj_ptr = handler_obj->address_space.next; + + /* Now we can delete the handler object */ + + acpi_ut_remove_reference (handler_obj); + goto unlock_and_exit; + } + + /* Walk the linked list of handlers */ + + last_obj_ptr = &handler_obj->address_space.next; + handler_obj = handler_obj->address_space.next; + } + + /* The handler does not exist */ + + ACPI_DEBUG_PRINT ((ACPI_DB_OPREGION, + "Unable to remove address handler %p for %s(%X), dev_node %p, obj %p\n", + handler, acpi_ut_get_region_name (space_id), space_id, node, obj_desc)); + + status = AE_NOT_EXIST; + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_remove_address_space_handler); + diff --git a/drivers/acpi/executer/Makefile b/drivers/acpi/executer/Makefile new file mode 100644 index 00000000000..e09998aa012 --- /dev/null +++ b/drivers/acpi/executer/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for all Linux ACPI interpreter subdirectories +# + +obj-y := exconfig.o exfield.o exnames.o exoparg6.o exresolv.o exstorob.o\ + exconvrt.o exfldio.o exoparg1.o exprep.o exresop.o exsystem.o\ + excreate.o exmisc.o exoparg2.o exregion.o exstore.o exutils.o \ + exdump.o exmutex.o exoparg3.o exresnte.o exstoren.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/executer/exconfig.c b/drivers/acpi/executer/exconfig.c new file mode 100644 index 00000000000..ac3c061967f --- /dev/null +++ b/drivers/acpi/executer/exconfig.c @@ -0,0 +1,487 @@ +/****************************************************************************** + * + * Module Name: exconfig - Namespace reconfiguration (Load/Unload opcodes) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> +#include <acpi/acevents.h> +#include <acpi/actables.h> +#include <acpi/acdispat.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exconfig") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_add_table + * + * PARAMETERS: Table - Pointer to raw table + * parent_node - Where to load the table (scope) + * ddb_handle - Where to return the table handle. + * + * RETURN: Status + * + * DESCRIPTION: Common function to Install and Load an ACPI table with a + * returned table handle. + * + ******************************************************************************/ + +acpi_status +acpi_ex_add_table ( + struct acpi_table_header *table, + struct acpi_namespace_node *parent_node, + union acpi_operand_object **ddb_handle) +{ + acpi_status status; + struct acpi_table_desc table_info; + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_TRACE ("ex_add_table"); + + + /* Create an object to be the table handle */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_LOCAL_REFERENCE); + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Install the new table into the local data structures */ + + ACPI_MEMSET (&table_info, 0, sizeof (struct acpi_table_desc)); + + table_info.type = ACPI_TABLE_SSDT; + table_info.pointer = table; + table_info.length = (acpi_size) table->length; + table_info.allocation = ACPI_MEM_ALLOCATED; + + status = acpi_tb_install_table (&table_info); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Add the table to the namespace */ + + status = acpi_ns_load_table (table_info.installed_desc, parent_node); + if (ACPI_FAILURE (status)) { + /* Uninstall table on error */ + + (void) acpi_tb_uninstall_table (table_info.installed_desc); + goto cleanup; + } + + /* Init the table handle */ + + obj_desc->reference.opcode = AML_LOAD_OP; + obj_desc->reference.object = table_info.installed_desc; + *ddb_handle = obj_desc; + return_ACPI_STATUS (AE_OK); + + +cleanup: + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_load_table_op + * + * PARAMETERS: walk_state - Current state with operands + * return_desc - Where to store the return object + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table + * + ******************************************************************************/ + +acpi_status +acpi_ex_load_table_op ( + struct acpi_walk_state *walk_state, + union acpi_operand_object **return_desc) +{ + acpi_status status; + union acpi_operand_object **operand = &walk_state->operands[0]; + struct acpi_table_header *table; + struct acpi_namespace_node *parent_node; + struct acpi_namespace_node *start_node; + struct acpi_namespace_node *parameter_node = NULL; + union acpi_operand_object *ddb_handle; + + + ACPI_FUNCTION_TRACE ("ex_load_table_op"); + + +#if 0 + /* + * Make sure that the signature does not match one of the tables that + * is already loaded. + */ + status = acpi_tb_match_signature (operand[0]->string.pointer, NULL); + if (status == AE_OK) { + /* Signature matched -- don't allow override */ + + return_ACPI_STATUS (AE_ALREADY_EXISTS); + } +#endif + + /* Find the ACPI table */ + + status = acpi_tb_find_table (operand[0]->string.pointer, + operand[1]->string.pointer, + operand[2]->string.pointer, &table); + if (ACPI_FAILURE (status)) { + if (status != AE_NOT_FOUND) { + return_ACPI_STATUS (status); + } + + /* Table not found, return an Integer=0 and AE_OK */ + + ddb_handle = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!ddb_handle) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + ddb_handle->integer.value = 0; + *return_desc = ddb_handle; + + return_ACPI_STATUS (AE_OK); + } + + /* Default nodes */ + + start_node = walk_state->scope_info->scope.node; + parent_node = acpi_gbl_root_node; + + /* root_path (optional parameter) */ + + if (operand[3]->string.length > 0) { + /* + * Find the node referenced by the root_path_string. This is the + * location within the namespace where the table will be loaded. + */ + status = acpi_ns_get_node_by_path (operand[3]->string.pointer, start_node, + ACPI_NS_SEARCH_PARENT, &parent_node); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* parameter_path (optional parameter) */ + + if (operand[4]->string.length > 0) { + if ((operand[4]->string.pointer[0] != '\\') && + (operand[4]->string.pointer[0] != '^')) { + /* + * Path is not absolute, so it will be relative to the node + * referenced by the root_path_string (or the NS root if omitted) + */ + start_node = parent_node; + } + + /* + * Find the node referenced by the parameter_path_string + */ + status = acpi_ns_get_node_by_path (operand[4]->string.pointer, start_node, + ACPI_NS_SEARCH_PARENT, ¶meter_node); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Load the table into the namespace */ + + status = acpi_ex_add_table (table, parent_node, &ddb_handle); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Parameter Data (optional) */ + + if (parameter_node) { + /* Store the parameter data into the optional parameter object */ + + status = acpi_ex_store (operand[5], ACPI_CAST_PTR (union acpi_operand_object, parameter_node), + walk_state); + if (ACPI_FAILURE (status)) { + (void) acpi_ex_unload_table (ddb_handle); + return_ACPI_STATUS (status); + } + } + + *return_desc = ddb_handle; + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_load_op + * + * PARAMETERS: obj_desc - Region or Field where the table will be + * obtained + * Target - Where a handle to the table will be stored + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table from a field or operation region + * + ******************************************************************************/ + +acpi_status +acpi_ex_load_op ( + union acpi_operand_object *obj_desc, + union acpi_operand_object *target, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_operand_object *ddb_handle; + union acpi_operand_object *buffer_desc = NULL; + struct acpi_table_header *table_ptr = NULL; + acpi_physical_address address; + struct acpi_table_header table_header; + u32 i; + + ACPI_FUNCTION_TRACE ("ex_load_op"); + + + /* Object can be either an op_region or a Field */ + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_REGION: + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Load from Region %p %s\n", + obj_desc, acpi_ut_get_object_type_name (obj_desc))); + + /* + * If the Region Address and Length have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_region_arguments (obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Get the base physical address of the region */ + + address = obj_desc->region.address; + + /* Get the table length from the table header */ + + table_header.length = 0; + for (i = 0; i < 8; i++) { + status = acpi_ev_address_space_dispatch (obj_desc, ACPI_READ, + (acpi_physical_address) (i + address), 8, + ((u8 *) &table_header) + i); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Sanity check the table length */ + + if (table_header.length < sizeof (struct acpi_table_header)) { + return_ACPI_STATUS (AE_BAD_HEADER); + } + + /* Allocate a buffer for the entire table */ + + table_ptr = ACPI_MEM_ALLOCATE (table_header.length); + if (!table_ptr) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Get the entire table from the op region */ + + for (i = 0; i < table_header.length; i++) { + status = acpi_ev_address_space_dispatch (obj_desc, ACPI_READ, + (acpi_physical_address) (i + address), 8, + ((u8 *) table_ptr + i)); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + } + break; + + + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Load from Field %p %s\n", + obj_desc, acpi_ut_get_object_type_name (obj_desc))); + + /* + * The length of the field must be at least as large as the table. + * Read the entire field and thus the entire table. Buffer is + * allocated during the read. + */ + status = acpi_ex_read_data_from_field (walk_state, obj_desc, &buffer_desc); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + table_ptr = ACPI_CAST_PTR (struct acpi_table_header, buffer_desc->buffer.pointer); + + /* Sanity check the table length */ + + if (table_ptr->length < sizeof (struct acpi_table_header)) { + return_ACPI_STATUS (AE_BAD_HEADER); + } + break; + + + default: + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* The table must be either an SSDT or a PSDT */ + + if ((!ACPI_STRNCMP (table_ptr->signature, + acpi_gbl_table_data[ACPI_TABLE_PSDT].signature, + acpi_gbl_table_data[ACPI_TABLE_PSDT].sig_length)) && + (!ACPI_STRNCMP (table_ptr->signature, + acpi_gbl_table_data[ACPI_TABLE_SSDT].signature, + acpi_gbl_table_data[ACPI_TABLE_SSDT].sig_length))) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Table has invalid signature [%4.4s], must be SSDT or PSDT\n", + table_ptr->signature)); + status = AE_BAD_SIGNATURE; + goto cleanup; + } + + /* Install the new table into the local data structures */ + + status = acpi_ex_add_table (table_ptr, acpi_gbl_root_node, &ddb_handle); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Store the ddb_handle into the Target operand */ + + status = acpi_ex_store (ddb_handle, target, walk_state); + if (ACPI_FAILURE (status)) { + (void) acpi_ex_unload_table (ddb_handle); + } + + return_ACPI_STATUS (status); + + +cleanup: + + if (buffer_desc) { + acpi_ut_remove_reference (buffer_desc); + } + else { + ACPI_MEM_FREE (table_ptr); + } + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_unload_table + * + * PARAMETERS: ddb_handle - Handle to a previously loaded table + * + * RETURN: Status + * + * DESCRIPTION: Unload an ACPI table + * + ******************************************************************************/ + +acpi_status +acpi_ex_unload_table ( + union acpi_operand_object *ddb_handle) +{ + acpi_status status = AE_OK; + union acpi_operand_object *table_desc = ddb_handle; + struct acpi_table_desc *table_info; + + + ACPI_FUNCTION_TRACE ("ex_unload_table"); + + + /* + * Validate the handle + * Although the handle is partially validated in acpi_ex_reconfiguration(), + * when it calls acpi_ex_resolve_operands(), the handle is more completely + * validated here. + */ + if ((!ddb_handle) || + (ACPI_GET_DESCRIPTOR_TYPE (ddb_handle) != ACPI_DESC_TYPE_OPERAND) || + (ACPI_GET_OBJECT_TYPE (ddb_handle) != ACPI_TYPE_LOCAL_REFERENCE)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Get the actual table descriptor from the ddb_handle */ + + table_info = (struct acpi_table_desc *) table_desc->reference.object; + + /* + * Delete the entire namespace under this table Node + * (Offset contains the table_id) + */ + acpi_ns_delete_namespace_by_owner (table_info->table_id); + + /* Delete the table itself */ + + (void) acpi_tb_uninstall_table (table_info->installed_desc); + + /* Delete the table descriptor (ddb_handle) */ + + acpi_ut_remove_reference (table_desc); + return_ACPI_STATUS (status); +} + diff --git a/drivers/acpi/executer/exconvrt.c b/drivers/acpi/executer/exconvrt.c new file mode 100644 index 00000000000..df7ba1219bf --- /dev/null +++ b/drivers/acpi/executer/exconvrt.c @@ -0,0 +1,708 @@ +/****************************************************************************** + * + * Module Name: exconvrt - Object conversion routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exconvrt") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_integer + * + * PARAMETERS: obj_desc - Object to be converted. Must be an + * Integer, Buffer, or String + * result_desc - Where the new Integer object is returned + * Flags - Used for string conversion + * + * RETURN: Status + * + * DESCRIPTION: Convert an ACPI Object to an integer. + * + ******************************************************************************/ + +acpi_status +acpi_ex_convert_to_integer ( + union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc, + u32 flags) +{ + union acpi_operand_object *return_desc; + u8 *pointer; + acpi_integer result; + u32 i; + u32 count; + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ex_convert_to_integer", obj_desc); + + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_INTEGER: + + /* No conversion necessary */ + + *result_desc = obj_desc; + return_ACPI_STATUS (AE_OK); + + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_STRING: + + /* Note: Takes advantage of common buffer/string fields */ + + pointer = obj_desc->buffer.pointer; + count = obj_desc->buffer.length; + break; + + default: + return_ACPI_STATUS (AE_TYPE); + } + + /* + * Convert the buffer/string to an integer. Note that both buffers and + * strings are treated as raw data - we don't convert ascii to hex for + * strings. + * + * There are two terminating conditions for the loop: + * 1) The size of an integer has been reached, or + * 2) The end of the buffer or string has been reached + */ + result = 0; + + /* + * String conversion is different than Buffer conversion + */ + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_STRING: + + /* + * Convert string to an integer - for most cases, the string must be + * hexadecimal as per the ACPI specification. The only exception (as + * of ACPI 3.0) is that the to_integer() operator allows both decimal + * and hexadecimal strings (hex prefixed with "0x"). + */ + status = acpi_ut_strtoul64 ((char *) pointer, flags, &result); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + break; + + + case ACPI_TYPE_BUFFER: + + /* Check for zero-length buffer */ + + if (!count) { + return_ACPI_STATUS (AE_AML_BUFFER_LIMIT); + } + + /* Transfer no more than an integer's worth of data */ + + if (count > acpi_gbl_integer_byte_width) { + count = acpi_gbl_integer_byte_width; + } + + /* + * Convert buffer to an integer - we simply grab enough raw data + * from the buffer to fill an integer + */ + for (i = 0; i < count; i++) { + /* + * Get next byte and shift it into the Result. + * Little endian is used, meaning that the first byte of the buffer + * is the LSB of the integer + */ + result |= (((acpi_integer) pointer[i]) << (i * 8)); + } + break; + + + default: + /* No other types can get here */ + break; + } + + /* + * Create a new integer + */ + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Save the Result */ + + return_desc->integer.value = result; + acpi_ex_truncate_for32bit_table (return_desc); + *result_desc = return_desc; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_buffer + * + * PARAMETERS: obj_desc - Object to be converted. Must be an + * Integer, Buffer, or String + * result_desc - Where the new buffer object is returned + * + * RETURN: Status + * + * DESCRIPTION: Convert an ACPI Object to a Buffer + * + ******************************************************************************/ + +acpi_status +acpi_ex_convert_to_buffer ( + union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc) +{ + union acpi_operand_object *return_desc; + u8 *new_buf; + + + ACPI_FUNCTION_TRACE_PTR ("ex_convert_to_buffer", obj_desc); + + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_BUFFER: + + /* No conversion necessary */ + + *result_desc = obj_desc; + return_ACPI_STATUS (AE_OK); + + + case ACPI_TYPE_INTEGER: + + /* + * Create a new Buffer object. + * Need enough space for one integer + */ + return_desc = acpi_ut_create_buffer_object (acpi_gbl_integer_byte_width); + if (!return_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Copy the integer to the buffer, LSB first */ + + new_buf = return_desc->buffer.pointer; + ACPI_MEMCPY (new_buf, + &obj_desc->integer.value, + acpi_gbl_integer_byte_width); + break; + + + case ACPI_TYPE_STRING: + + /* + * Create a new Buffer object + * Size will be the string length + * + * NOTE: Add one to the string length to include the null terminator. + * The ACPI spec is unclear on this subject, but there is existing + * ASL/AML code that depends on the null being transferred to the new + * buffer. + */ + return_desc = acpi_ut_create_buffer_object ((acpi_size) obj_desc->string.length + 1); + if (!return_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Copy the string to the buffer */ + + new_buf = return_desc->buffer.pointer; + ACPI_STRNCPY ((char *) new_buf, (char *) obj_desc->string.pointer, + obj_desc->string.length); + break; + + + default: + return_ACPI_STATUS (AE_TYPE); + } + + /* Mark buffer initialized */ + + return_desc->common.flags |= AOPOBJ_DATA_VALID; + *result_desc = return_desc; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_ascii + * + * PARAMETERS: Integer - Value to be converted + * Base - ACPI_STRING_DECIMAL or ACPI_STRING_HEX + * String - Where the string is returned + * data_width - Size of data item to be converted, in bytes + * + * RETURN: Actual string length + * + * DESCRIPTION: Convert an ACPI Integer to a hex or decimal string + * + ******************************************************************************/ + +u32 +acpi_ex_convert_to_ascii ( + acpi_integer integer, + u16 base, + u8 *string, + u8 data_width) +{ + acpi_integer digit; + acpi_native_uint i; + acpi_native_uint j; + acpi_native_uint k = 0; + acpi_native_uint hex_length; + acpi_native_uint decimal_length; + u32 remainder; + u8 supress_zeros; + + + ACPI_FUNCTION_ENTRY (); + + + switch (base) { + case 10: + + /* Setup max length for the decimal number */ + + switch (data_width) { + case 1: + decimal_length = ACPI_MAX8_DECIMAL_DIGITS; + break; + + case 4: + decimal_length = ACPI_MAX32_DECIMAL_DIGITS; + break; + + case 8: + default: + decimal_length = ACPI_MAX64_DECIMAL_DIGITS; + break; + } + + supress_zeros = TRUE; /* No leading zeros */ + remainder = 0; + + for (i = decimal_length; i > 0; i--) { + /* Divide by nth factor of 10 */ + + digit = integer; + for (j = 0; j < i; j++) { + (void) acpi_ut_short_divide (digit, 10, &digit, &remainder); + } + + /* Handle leading zeros */ + + if (remainder != 0) { + supress_zeros = FALSE; + } + + if (!supress_zeros) { + string[k] = (u8) (ACPI_ASCII_ZERO + remainder); + k++; + } + } + break; + + case 16: + + hex_length = ACPI_MUL_2 (data_width); /* 2 ascii hex chars per data byte */ + + for (i = 0, j = (hex_length-1); i < hex_length; i++, j--) { + /* Get one hex digit, most significant digits first */ + + string[k] = (u8) acpi_ut_hex_to_ascii_char (integer, ACPI_MUL_4 (j)); + k++; + } + break; + + default: + return (0); + } + + /* + * Since leading zeros are supressed, we must check for the case where + * the integer equals 0 + * + * Finally, null terminate the string and return the length + */ + if (!k) { + string [0] = ACPI_ASCII_ZERO; + k = 1; + } + + string [k] = 0; + return ((u32) k); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_string + * + * PARAMETERS: obj_desc - Object to be converted. Must be an + * Integer, Buffer, or String + * result_desc - Where the string object is returned + * Type - String flags (base and conversion type) + * + * RETURN: Status + * + * DESCRIPTION: Convert an ACPI Object to a string + * + ******************************************************************************/ + +acpi_status +acpi_ex_convert_to_string ( + union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc, + u32 type) +{ + union acpi_operand_object *return_desc; + u8 *new_buf; + u32 i; + u32 string_length = 0; + u16 base = 16; + u8 separator = ','; + + + ACPI_FUNCTION_TRACE_PTR ("ex_convert_to_string", obj_desc); + + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_STRING: + + /* No conversion necessary */ + + *result_desc = obj_desc; + return_ACPI_STATUS (AE_OK); + + + case ACPI_TYPE_INTEGER: + + switch (type) { + case ACPI_EXPLICIT_CONVERT_DECIMAL: + + /* Make room for maximum decimal number */ + + string_length = ACPI_MAX_DECIMAL_DIGITS; + base = 10; + break; + + default: + + /* Two hex string characters for each integer byte */ + + string_length = ACPI_MUL_2 (acpi_gbl_integer_byte_width); + break; + } + + /* + * Create a new String + * Need enough space for one ASCII integer (plus null terminator) + */ + return_desc = acpi_ut_create_string_object ((acpi_size) string_length); + if (!return_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + new_buf = return_desc->buffer.pointer; + + /* Convert integer to string */ + + string_length = acpi_ex_convert_to_ascii (obj_desc->integer.value, base, + new_buf, acpi_gbl_integer_byte_width); + + /* Null terminate at the correct place */ + + return_desc->string.length = string_length; + new_buf [string_length] = 0; + break; + + + case ACPI_TYPE_BUFFER: + + /* Setup string length, base, and separator */ + + switch (type) { + case ACPI_EXPLICIT_CONVERT_DECIMAL: /* Used by to_decimal_string operator */ + /* + * From ACPI: "If Data is a buffer, it is converted to a string of + * decimal values separated by commas." + */ + base = 10; + + /* + * Calculate the final string length. Individual string values + * are variable length (include separator for each) + */ + for (i = 0; i < obj_desc->buffer.length; i++) { + if (obj_desc->buffer.pointer[i] >= 100) { + string_length += 4; + } + else if (obj_desc->buffer.pointer[i] >= 10) { + string_length += 3; + } + else { + string_length += 2; + } + } + break; + + case ACPI_IMPLICIT_CONVERT_HEX: + /* + * From the ACPI spec: + *"The entire contents of the buffer are converted to a string of + * two-character hexadecimal numbers, each separated by a space." + */ + separator = ' '; + string_length = (obj_desc->buffer.length * 3); + break; + + case ACPI_EXPLICIT_CONVERT_HEX: /* Used by to_hex_string operator */ + /* + * From ACPI: "If Data is a buffer, it is converted to a string of + * hexadecimal values separated by commas." + */ + string_length = (obj_desc->buffer.length * 3); + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Perform the conversion. + * (-1 because of extra separator included in string_length from above) + */ + string_length--; + if (string_length > ACPI_MAX_STRING_CONVERSION) /* ACPI limit */ { + return_ACPI_STATUS (AE_AML_STRING_LIMIT); + } + + /* + * Create a new string object and string buffer + */ + return_desc = acpi_ut_create_string_object ((acpi_size) string_length); + if (!return_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + new_buf = return_desc->buffer.pointer; + + /* + * Convert buffer bytes to hex or decimal values + * (separated by commas or spaces) + */ + for (i = 0; i < obj_desc->buffer.length; i++) { + new_buf += acpi_ex_convert_to_ascii ( + (acpi_integer) obj_desc->buffer.pointer[i], base, + new_buf, 1); + *new_buf++ = separator; /* each separated by a comma or space */ + } + + /* Null terminate the string (overwrites final comma/space from above) */ + + new_buf--; + *new_buf = 0; + break; + + default: + return_ACPI_STATUS (AE_TYPE); + } + + *result_desc = return_desc; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_convert_to_target_type + * + * PARAMETERS: destination_type - Current type of the destination + * source_desc - Source object to be converted. + * result_desc - Where the converted object is returned + * walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Implements "implicit conversion" rules for storing an object. + * + ******************************************************************************/ + +acpi_status +acpi_ex_convert_to_target_type ( + acpi_object_type destination_type, + union acpi_operand_object *source_desc, + union acpi_operand_object **result_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ex_convert_to_target_type"); + + + /* Default behavior */ + + *result_desc = source_desc; + + /* + * If required by the target, + * perform implicit conversion on the source before we store it. + */ + switch (GET_CURRENT_ARG_TYPE (walk_state->op_info->runtime_args)) { + case ARGI_SIMPLE_TARGET: + case ARGI_FIXED_TARGET: + case ARGI_INTEGER_REF: /* Handles Increment, Decrement cases */ + + switch (destination_type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + /* + * Named field can always handle conversions + */ + break; + + default: + /* No conversion allowed for these types */ + + if (destination_type != ACPI_GET_OBJECT_TYPE (source_desc)) { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Explicit operator, will store (%s) over existing type (%s)\n", + acpi_ut_get_object_type_name (source_desc), + acpi_ut_get_type_name (destination_type))); + status = AE_TYPE; + } + } + break; + + + case ARGI_TARGETREF: + + switch (destination_type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + /* + * These types require an Integer operand. We can convert + * a Buffer or a String to an Integer if necessary. + */ + status = acpi_ex_convert_to_integer (source_desc, result_desc, + 16); + break; + + + case ACPI_TYPE_STRING: + + /* + * The operand must be a String. We can convert an + * Integer or Buffer if necessary + */ + status = acpi_ex_convert_to_string (source_desc, result_desc, + ACPI_IMPLICIT_CONVERT_HEX); + break; + + + case ACPI_TYPE_BUFFER: + + /* + * The operand must be a Buffer. We can convert an + * Integer or String if necessary + */ + status = acpi_ex_convert_to_buffer (source_desc, result_desc); + break; + + + default: + ACPI_REPORT_ERROR (("Bad destination type during conversion: %X\n", + destination_type)); + status = AE_AML_INTERNAL; + break; + } + break; + + + case ARGI_REFERENCE: + /* + * create_xxxx_field cases - we are storing the field object into the name + */ + break; + + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unknown Target type ID 0x%X Op %s dest_type %s\n", + GET_CURRENT_ARG_TYPE (walk_state->op_info->runtime_args), + walk_state->op_info->name, acpi_ut_get_type_name (destination_type))); + + ACPI_REPORT_ERROR (("Bad Target Type (ARGI): %X\n", + GET_CURRENT_ARG_TYPE (walk_state->op_info->runtime_args))) + status = AE_AML_INTERNAL; + } + + /* + * Source-to-Target conversion semantics: + * + * If conversion to the target type cannot be performed, then simply + * overwrite the target with the new object and type. + */ + if (status == AE_TYPE) { + status = AE_OK; + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/excreate.c b/drivers/acpi/executer/excreate.c new file mode 100644 index 00000000000..d94c260dac6 --- /dev/null +++ b/drivers/acpi/executer/excreate.c @@ -0,0 +1,646 @@ +/****************************************************************************** + * + * Module Name: excreate - Named object creation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> +#include <acpi/acevents.h> +#include <acpi/actables.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("excreate") + + +#ifndef ACPI_NO_METHOD_EXECUTION +/***************************************************************************** + * + * FUNCTION: acpi_ex_create_alias + * + * PARAMETERS: walk_state - Current state, contains operands + * + * RETURN: Status + * + * DESCRIPTION: Create a new named alias + * + ****************************************************************************/ + +acpi_status +acpi_ex_create_alias ( + struct acpi_walk_state *walk_state) +{ + struct acpi_namespace_node *target_node; + struct acpi_namespace_node *alias_node; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ex_create_alias"); + + + /* Get the source/alias operands (both namespace nodes) */ + + alias_node = (struct acpi_namespace_node *) walk_state->operands[0]; + target_node = (struct acpi_namespace_node *) walk_state->operands[1]; + + if ((target_node->type == ACPI_TYPE_LOCAL_ALIAS) || + (target_node->type == ACPI_TYPE_LOCAL_METHOD_ALIAS)) { + /* + * Dereference an existing alias so that we don't create a chain + * of aliases. With this code, we guarantee that an alias is + * always exactly one level of indirection away from the + * actual aliased name. + */ + target_node = ACPI_CAST_PTR (struct acpi_namespace_node, target_node->object); + } + + /* + * For objects that can never change (i.e., the NS node will + * permanently point to the same object), we can simply attach + * the object to the new NS node. For other objects (such as + * Integers, buffers, etc.), we have to point the Alias node + * to the original Node. + */ + switch (target_node->type) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_PACKAGE: + case ACPI_TYPE_BUFFER_FIELD: + + /* + * The new alias has the type ALIAS and points to the original + * NS node, not the object itself. This is because for these + * types, the object can change dynamically via a Store. + */ + alias_node->type = ACPI_TYPE_LOCAL_ALIAS; + alias_node->object = ACPI_CAST_PTR (union acpi_operand_object, target_node); + break; + + case ACPI_TYPE_METHOD: + + /* + * The new alias has the type ALIAS and points to the original + * NS node, not the object itself. This is because for these + * types, the object can change dynamically via a Store. + */ + alias_node->type = ACPI_TYPE_LOCAL_METHOD_ALIAS; + alias_node->object = ACPI_CAST_PTR (union acpi_operand_object, target_node); + break; + + default: + + /* Attach the original source object to the new Alias Node */ + + /* + * The new alias assumes the type of the target, and it points + * to the same object. The reference count of the object has an + * additional reference to prevent deletion out from under either the + * target node or the alias Node + */ + status = acpi_ns_attach_object (alias_node, + acpi_ns_get_attached_object (target_node), + target_node->type); + break; + } + + /* Since both operands are Nodes, we don't need to delete them */ + + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ex_create_event + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new event object + * + ****************************************************************************/ + +acpi_status +acpi_ex_create_event ( + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_TRACE ("ex_create_event"); + + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_EVENT); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Create the actual OS semaphore, with zero initial units -- meaning + * that the event is created in an unsignalled state + */ + status = acpi_os_create_semaphore (ACPI_NO_UNIT_LIMIT, 0, + &obj_desc->event.semaphore); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Attach object to the Node */ + + status = acpi_ns_attach_object ((struct acpi_namespace_node *) walk_state->operands[0], + obj_desc, ACPI_TYPE_EVENT); + +cleanup: + /* + * Remove local reference to the object (on error, will cause deletion + * of both object and semaphore if present.) + */ + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ex_create_mutex + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new mutex object + * + * Mutex (Name[0], sync_level[1]) + * + ****************************************************************************/ + +acpi_status +acpi_ex_create_mutex ( + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_TRACE_PTR ("ex_create_mutex", ACPI_WALK_OPERANDS); + + + /* Create the new mutex object */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_MUTEX); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Create the actual OS semaphore. + * One unit max to make it a mutex, with one initial unit to allow + * the mutex to be acquired. + */ + status = acpi_os_create_semaphore (1, 1, &obj_desc->mutex.semaphore); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Init object and attach to NS node */ + + obj_desc->mutex.sync_level = (u8) walk_state->operands[1]->integer.value; + obj_desc->mutex.node = (struct acpi_namespace_node *) walk_state->operands[0]; + + status = acpi_ns_attach_object (obj_desc->mutex.node, + obj_desc, ACPI_TYPE_MUTEX); + + +cleanup: + /* + * Remove local reference to the object (on error, will cause deletion + * of both object and semaphore if present.) + */ + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ex_create_region + * + * PARAMETERS: aml_start - Pointer to the region declaration AML + * aml_length - Max length of the declaration AML + * Operands - List of operands for the opcode + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new operation region object + * + ****************************************************************************/ + +acpi_status +acpi_ex_create_region ( + u8 *aml_start, + u32 aml_length, + u8 region_space, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + union acpi_operand_object *region_obj2; + + + ACPI_FUNCTION_TRACE ("ex_create_region"); + + + /* Get the Namespace Node */ + + node = walk_state->op->common.node; + + /* + * If the region object is already attached to this node, + * just return + */ + if (acpi_ns_get_attached_object (node)) { + return_ACPI_STATUS (AE_OK); + } + + /* + * Space ID must be one of the predefined IDs, or in the user-defined + * range + */ + if ((region_space >= ACPI_NUM_PREDEFINED_REGIONS) && + (region_space < ACPI_USER_REGION_BEGIN)) { + ACPI_REPORT_ERROR (("Invalid address_space type %X\n", region_space)); + return_ACPI_STATUS (AE_AML_INVALID_SPACE_ID); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "Region Type - %s (%X)\n", + acpi_ut_get_region_name (region_space), region_space)); + + /* Create the region descriptor */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_REGION); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Remember location in AML stream of address & length + * operands since they need to be evaluated at run time. + */ + region_obj2 = obj_desc->common.next_object; + region_obj2->extra.aml_start = aml_start; + region_obj2->extra.aml_length = aml_length; + + /* Init the region from the operands */ + + obj_desc->region.space_id = region_space; + obj_desc->region.address = 0; + obj_desc->region.length = 0; + obj_desc->region.node = node; + + /* Install the new region object in the parent Node */ + + status = acpi_ns_attach_object (node, obj_desc, ACPI_TYPE_REGION); + + +cleanup: + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ex_create_table_region + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new data_table_region object + * + ****************************************************************************/ + +acpi_status +acpi_ex_create_table_region ( + struct acpi_walk_state *walk_state) +{ + acpi_status status; + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *obj_desc; + struct acpi_namespace_node *node; + struct acpi_table_header *table; + union acpi_operand_object *region_obj2; + + + ACPI_FUNCTION_TRACE ("ex_create_table_region"); + + + /* Get the Node from the object stack */ + + node = walk_state->op->common.node; + + /* + * If the region object is already attached to this node, + * just return + */ + if (acpi_ns_get_attached_object (node)) { + return_ACPI_STATUS (AE_OK); + } + + /* Find the ACPI table */ + + status = acpi_tb_find_table (operand[1]->string.pointer, + operand[2]->string.pointer, + operand[3]->string.pointer, &table); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Create the region descriptor */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_REGION); + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + region_obj2 = obj_desc->common.next_object; + region_obj2->extra.region_context = NULL; + + /* Init the region from the operands */ + + obj_desc->region.space_id = REGION_DATA_TABLE; + obj_desc->region.address = (acpi_physical_address) ACPI_TO_INTEGER (table); + obj_desc->region.length = table->length; + obj_desc->region.node = node; + obj_desc->region.flags = AOPOBJ_DATA_VALID; + + /* Install the new region object in the parent Node */ + + status = acpi_ns_attach_object (node, obj_desc, ACPI_TYPE_REGION); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_ev_initialize_region (obj_desc, FALSE); + if (ACPI_FAILURE (status)) { + if (status == AE_NOT_EXIST) { + status = AE_OK; + } + else { + goto cleanup; + } + } + + obj_desc->region.flags |= AOPOBJ_SETUP_COMPLETE; + + +cleanup: + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ex_create_processor + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new processor object and populate the fields + * + * Processor (Name[0], cpu_iD[1], pblock_addr[2], pblock_length[3]) + * + ****************************************************************************/ + +acpi_status +acpi_ex_create_processor ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ex_create_processor", walk_state); + + + /* Create the processor object */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_PROCESSOR); + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* + * Initialize the processor object from the operands + */ + obj_desc->processor.proc_id = (u8) operand[1]->integer.value; + obj_desc->processor.address = (acpi_io_address) operand[2]->integer.value; + obj_desc->processor.length = (u8) operand[3]->integer.value; + + /* Install the processor object in the parent Node */ + + status = acpi_ns_attach_object ((struct acpi_namespace_node *) operand[0], + obj_desc, ACPI_TYPE_PROCESSOR); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ex_create_power_resource + * + * PARAMETERS: walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new power_resource object and populate the fields + * + * power_resource (Name[0], system_level[1], resource_order[2]) + * + ****************************************************************************/ + +acpi_status +acpi_ex_create_power_resource ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + acpi_status status; + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_TRACE_PTR ("ex_create_power_resource", walk_state); + + + /* Create the power resource object */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_POWER); + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Initialize the power object from the operands */ + + obj_desc->power_resource.system_level = (u8) operand[1]->integer.value; + obj_desc->power_resource.resource_order = (u16) operand[2]->integer.value; + + /* Install the power resource object in the parent Node */ + + status = acpi_ns_attach_object ((struct acpi_namespace_node *) operand[0], + obj_desc, ACPI_TYPE_POWER); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + +#endif + +/***************************************************************************** + * + * FUNCTION: acpi_ex_create_method + * + * PARAMETERS: aml_start - First byte of the method's AML + * aml_length - AML byte count for this method + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Create a new method object + * + ****************************************************************************/ + +acpi_status +acpi_ex_create_method ( + u8 *aml_start, + u32 aml_length, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *obj_desc; + acpi_status status; + u8 method_flags; + + + ACPI_FUNCTION_TRACE_PTR ("ex_create_method", walk_state); + + + /* Create a new method object */ + + obj_desc = acpi_ut_create_internal_object (ACPI_TYPE_METHOD); + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Save the method's AML pointer and length */ + + obj_desc->method.aml_start = aml_start; + obj_desc->method.aml_length = aml_length; + + /* + * Disassemble the method flags. Split off the Arg Count + * for efficiency + */ + method_flags = (u8) operand[1]->integer.value; + + obj_desc->method.method_flags = (u8) (method_flags & ~AML_METHOD_ARG_COUNT); + obj_desc->method.param_count = (u8) (method_flags & AML_METHOD_ARG_COUNT); + + /* + * Get the concurrency count. If required, a semaphore will be + * created for this method when it is parsed. + */ + if (acpi_gbl_all_methods_serialized) { + obj_desc->method.concurrency = 1; + obj_desc->method.method_flags |= AML_METHOD_SERIALIZED; + } + else if (method_flags & AML_METHOD_SERIALIZED) { + /* + * ACPI 1.0: Concurrency = 1 + * ACPI 2.0: Concurrency = (sync_level (in method declaration) + 1) + */ + obj_desc->method.concurrency = (u8) + (((method_flags & AML_METHOD_SYNCH_LEVEL) >> 4) + 1); + } + else { + obj_desc->method.concurrency = ACPI_INFINITE_CONCURRENCY; + } + + /* Attach the new object to the method Node */ + + status = acpi_ns_attach_object ((struct acpi_namespace_node *) operand[0], + obj_desc, ACPI_TYPE_METHOD); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + + /* Remove a reference to the operand */ + + acpi_ut_remove_reference (operand[1]); + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exdump.c b/drivers/acpi/executer/exdump.c new file mode 100644 index 00000000000..e2f7c32f28d --- /dev/null +++ b/drivers/acpi/executer/exdump.c @@ -0,0 +1,793 @@ +/****************************************************************************** + * + * Module Name: exdump - Interpreter debug output routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> +#include <acpi/acparser.h> + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exdump") + + +/* + * The following routines are used for debug output only + */ +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) + +/***************************************************************************** + * + * FUNCTION: acpi_ex_dump_operand + * + * PARAMETERS: *obj_desc - Pointer to entry to be dumped + * + * RETURN: None + * + * DESCRIPTION: Dump an operand object + * + ****************************************************************************/ + +void +acpi_ex_dump_operand ( + union acpi_operand_object *obj_desc, + u32 depth) +{ + u32 length; + u32 index; + + + ACPI_FUNCTION_NAME ("ex_dump_operand") + + + if (!((ACPI_LV_EXEC & acpi_dbg_level) && (_COMPONENT & acpi_dbg_layer))) { + return; + } + + if (!obj_desc) { + /* + * This could be a null element of a package + */ + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Null Object Descriptor\n")); + return; + } + + if (ACPI_GET_DESCRIPTOR_TYPE (obj_desc) == ACPI_DESC_TYPE_NAMED) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "%p is a NS Node: ", obj_desc)); + ACPI_DUMP_ENTRY (obj_desc, ACPI_LV_EXEC); + return; + } + + if (ACPI_GET_DESCRIPTOR_TYPE (obj_desc) != ACPI_DESC_TYPE_OPERAND) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "%p is not a node or operand object: [%s]\n", + obj_desc, acpi_ut_get_descriptor_name (obj_desc))); + ACPI_DUMP_BUFFER (obj_desc, sizeof (union acpi_operand_object)); + return; + } + + /* obj_desc is a valid object */ + + if (depth > 0) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "%*s[%u] %p ", + depth, " ", depth, obj_desc)); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "%p ", obj_desc)); + } + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_LOCAL_REFERENCE: + + switch (obj_desc->reference.opcode) { + case AML_DEBUG_OP: + + acpi_os_printf ("Reference: Debug\n"); + break; + + + case AML_NAME_OP: + + ACPI_DUMP_PATHNAME (obj_desc->reference.object, + "Reference: Name: ", ACPI_LV_INFO, _COMPONENT); + ACPI_DUMP_ENTRY (obj_desc->reference.object, ACPI_LV_INFO); + break; + + + case AML_INDEX_OP: + + acpi_os_printf ("Reference: Index %p\n", + obj_desc->reference.object); + break; + + + case AML_REF_OF_OP: + + acpi_os_printf ("Reference: (ref_of) %p\n", + obj_desc->reference.object); + break; + + + case AML_ARG_OP: + + acpi_os_printf ("Reference: Arg%d", + obj_desc->reference.offset); + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_INTEGER) { + /* Value is an Integer */ + + acpi_os_printf (" value is [%8.8X%8.8x]", + ACPI_FORMAT_UINT64 (obj_desc->integer.value)); + } + + acpi_os_printf ("\n"); + break; + + + case AML_LOCAL_OP: + + acpi_os_printf ("Reference: Local%d", + obj_desc->reference.offset); + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_INTEGER) { + + /* Value is an Integer */ + + acpi_os_printf (" value is [%8.8X%8.8x]", + ACPI_FORMAT_UINT64 (obj_desc->integer.value)); + } + + acpi_os_printf ("\n"); + break; + + + case AML_INT_NAMEPATH_OP: + + acpi_os_printf ("Reference.Node->Name %X\n", + obj_desc->reference.node->name.integer); + break; + + + default: + + /* Unknown opcode */ + + acpi_os_printf ("Unknown Reference opcode=%X\n", + obj_desc->reference.opcode); + break; + + } + break; + + + case ACPI_TYPE_BUFFER: + + acpi_os_printf ("Buffer len %X @ %p \n", + obj_desc->buffer.length, obj_desc->buffer.pointer); + + length = obj_desc->buffer.length; + if (length > 64) { + length = 64; + } + + /* Debug only -- dump the buffer contents */ + + if (obj_desc->buffer.pointer) { + acpi_os_printf ("Buffer Contents: "); + + for (index = 0; index < length; index++) { + acpi_os_printf (" %02x", obj_desc->buffer.pointer[index]); + } + acpi_os_printf ("\n"); + } + break; + + + case ACPI_TYPE_INTEGER: + + acpi_os_printf ("Integer %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (obj_desc->integer.value)); + break; + + + case ACPI_TYPE_PACKAGE: + + acpi_os_printf ("Package [Len %X] element_array %p\n", + obj_desc->package.count, obj_desc->package.elements); + + /* + * If elements exist, package element pointer is valid, + * and debug_level exceeds 1, dump package's elements. + */ + if (obj_desc->package.count && + obj_desc->package.elements && + acpi_dbg_level > 1) { + for (index = 0; index < obj_desc->package.count; index++) { + acpi_ex_dump_operand (obj_desc->package.elements[index], depth+1); + } + } + break; + + + case ACPI_TYPE_REGION: + + acpi_os_printf ("Region %s (%X)", + acpi_ut_get_region_name (obj_desc->region.space_id), + obj_desc->region.space_id); + + /* + * If the address and length have not been evaluated, + * don't print them. + */ + if (!(obj_desc->region.flags & AOPOBJ_DATA_VALID)) { + acpi_os_printf ("\n"); + } + else { + acpi_os_printf (" base %8.8X%8.8X Length %X\n", + ACPI_FORMAT_UINT64 (obj_desc->region.address), + obj_desc->region.length); + } + break; + + + case ACPI_TYPE_STRING: + + acpi_os_printf ("String length %X @ %p ", + obj_desc->string.length, obj_desc->string.pointer); + acpi_ut_print_string (obj_desc->string.pointer, ACPI_UINT8_MAX); + acpi_os_printf ("\n"); + break; + + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + acpi_os_printf ("bank_field\n"); + break; + + + case ACPI_TYPE_LOCAL_REGION_FIELD: + + acpi_os_printf ( + "region_field: Bits=%X acc_width=%X Lock=%X Update=%X at byte=%X bit=%X of below:\n", + obj_desc->field.bit_length, obj_desc->field.access_byte_width, + obj_desc->field.field_flags & AML_FIELD_LOCK_RULE_MASK, + obj_desc->field.field_flags & AML_FIELD_UPDATE_RULE_MASK, + obj_desc->field.base_byte_offset, obj_desc->field.start_field_bit_offset); + acpi_ex_dump_operand (obj_desc->field.region_obj, depth+1); + break; + + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + acpi_os_printf ("index_field\n"); + break; + + + case ACPI_TYPE_BUFFER_FIELD: + + acpi_os_printf ( + "buffer_field: %X bits at byte %X bit %X of \n", + obj_desc->buffer_field.bit_length, obj_desc->buffer_field.base_byte_offset, + obj_desc->buffer_field.start_field_bit_offset); + + if (!obj_desc->buffer_field.buffer_obj) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "*NULL* \n")); + } + else if (ACPI_GET_OBJECT_TYPE (obj_desc->buffer_field.buffer_obj) != ACPI_TYPE_BUFFER) { + acpi_os_printf ("*not a Buffer* \n"); + } + else { + acpi_ex_dump_operand (obj_desc->buffer_field.buffer_obj, depth+1); + } + break; + + + case ACPI_TYPE_EVENT: + + acpi_os_printf ("Event\n"); + break; + + + case ACPI_TYPE_METHOD: + + acpi_os_printf ( + "Method(%X) @ %p:%X\n", + obj_desc->method.param_count, + obj_desc->method.aml_start, obj_desc->method.aml_length); + break; + + + case ACPI_TYPE_MUTEX: + + acpi_os_printf ("Mutex\n"); + break; + + + case ACPI_TYPE_DEVICE: + + acpi_os_printf ("Device\n"); + break; + + + case ACPI_TYPE_POWER: + + acpi_os_printf ("Power\n"); + break; + + + case ACPI_TYPE_PROCESSOR: + + acpi_os_printf ("Processor\n"); + break; + + + case ACPI_TYPE_THERMAL: + + acpi_os_printf ("Thermal\n"); + break; + + + default: + /* Unknown Type */ + + acpi_os_printf ("Unknown Type %X\n", ACPI_GET_OBJECT_TYPE (obj_desc)); + break; + } + + return; +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ex_dump_operands + * + * PARAMETERS: Operands - Operand list + * interpreter_mode - Load or Exec + * Ident - Identification + * num_levels - # of stack entries to dump above line + * Note - Output notation + * module_name - Caller's module name + * line_number - Caller's invocation line number + * + * DESCRIPTION: Dump the object stack + * + ****************************************************************************/ + +void +acpi_ex_dump_operands ( + union acpi_operand_object **operands, + acpi_interpreter_mode interpreter_mode, + char *ident, + u32 num_levels, + char *note, + char *module_name, + u32 line_number) +{ + acpi_native_uint i; + + + ACPI_FUNCTION_NAME ("ex_dump_operands"); + + + if (!ident) { + ident = "?"; + } + + if (!note) { + note = "?"; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "************* Operand Stack Contents (Opcode [%s], %d Operands)\n", + ident, num_levels)); + + if (num_levels == 0) { + num_levels = 1; + } + + /* Dump the operand stack starting at the top */ + + for (i = 0; num_levels > 0; i--, num_levels--) { + acpi_ex_dump_operand (operands[i], 0); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "************* Stack dump from %s(%d), %s\n", + module_name, line_number, note)); + return; +} + + +#ifdef ACPI_FUTURE_USAGE + +/***************************************************************************** + * + * FUNCTION: acpi_ex_out* + * + * PARAMETERS: Title - Descriptive text + * Value - Value to be displayed + * + * DESCRIPTION: Object dump output formatting functions. These functions + * reduce the number of format strings required and keeps them + * all in one place for easy modification. + * + ****************************************************************************/ + +void +acpi_ex_out_string ( + char *title, + char *value) +{ + acpi_os_printf ("%20s : %s\n", title, value); +} + +void +acpi_ex_out_pointer ( + char *title, + void *value) +{ + acpi_os_printf ("%20s : %p\n", title, value); +} + +void +acpi_ex_out_integer ( + char *title, + u32 value) +{ + acpi_os_printf ("%20s : %X\n", title, value); +} + +void +acpi_ex_out_address ( + char *title, + acpi_physical_address value) +{ + +#if ACPI_MACHINE_WIDTH == 16 + acpi_os_printf ("%20s : %p\n", title, value); +#else + acpi_os_printf ("%20s : %8.8X%8.8X\n", title, ACPI_FORMAT_UINT64 (value)); +#endif +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ex_dump_node + * + * PARAMETERS: *Node - Descriptor to dump + * Flags - Force display + * + * DESCRIPTION: Dumps the members of the given.Node + * + ****************************************************************************/ + +void +acpi_ex_dump_node ( + struct acpi_namespace_node *node, + u32 flags) +{ + + ACPI_FUNCTION_ENTRY (); + + + if (!flags) { + if (!((ACPI_LV_OBJECTS & acpi_dbg_level) && (_COMPONENT & acpi_dbg_layer))) { + return; + } + } + + acpi_os_printf ("%20s : %4.4s\n", "Name", acpi_ut_get_node_name (node)); + acpi_ex_out_string ("Type", acpi_ut_get_type_name (node->type)); + acpi_ex_out_integer ("Flags", node->flags); + acpi_ex_out_integer ("Owner Id", node->owner_id); + acpi_ex_out_integer ("Reference Count", node->reference_count); + acpi_ex_out_pointer ("Attached Object", acpi_ns_get_attached_object (node)); + acpi_ex_out_pointer ("child_list", node->child); + acpi_ex_out_pointer ("next_peer", node->peer); + acpi_ex_out_pointer ("Parent", acpi_ns_get_parent_node (node)); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ex_dump_object_descriptor + * + * PARAMETERS: *Object - Descriptor to dump + * Flags - Force display + * + * DESCRIPTION: Dumps the members of the object descriptor given. + * + ****************************************************************************/ + +void +acpi_ex_dump_object_descriptor ( + union acpi_operand_object *obj_desc, + u32 flags) +{ + u32 i; + + + ACPI_FUNCTION_TRACE ("ex_dump_object_descriptor"); + + + if (!flags) { + if (!((ACPI_LV_OBJECTS & acpi_dbg_level) && (_COMPONENT & acpi_dbg_layer))) { + return_VOID; + } + } + + if (ACPI_GET_DESCRIPTOR_TYPE (obj_desc) == ACPI_DESC_TYPE_NAMED) { + acpi_ex_dump_node ((struct acpi_namespace_node *) obj_desc, flags); + acpi_os_printf ("\nAttached Object (%p):\n", + ((struct acpi_namespace_node *) obj_desc)->object); + acpi_ex_dump_object_descriptor ( + ((struct acpi_namespace_node *) obj_desc)->object, flags); + return_VOID; + } + + if (ACPI_GET_DESCRIPTOR_TYPE (obj_desc) != ACPI_DESC_TYPE_OPERAND) { + acpi_os_printf ( + "ex_dump_object_descriptor: %p is not an ACPI operand object: [%s]\n", + obj_desc, acpi_ut_get_descriptor_name (obj_desc)); + return_VOID; + } + + /* Common Fields */ + + acpi_ex_out_string ("Type", acpi_ut_get_object_type_name (obj_desc)); + acpi_ex_out_integer ("Reference Count", obj_desc->common.reference_count); + acpi_ex_out_integer ("Flags", obj_desc->common.flags); + + /* Object-specific Fields */ + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_INTEGER: + + acpi_os_printf ("%20s : %8.8X%8.8X\n", "Value", + ACPI_FORMAT_UINT64 (obj_desc->integer.value)); + break; + + + case ACPI_TYPE_STRING: + + acpi_ex_out_integer ("Length", obj_desc->string.length); + + acpi_os_printf ("%20s : %p ", "Pointer", obj_desc->string.pointer); + acpi_ut_print_string (obj_desc->string.pointer, ACPI_UINT8_MAX); + acpi_os_printf ("\n"); + break; + + + case ACPI_TYPE_BUFFER: + + acpi_ex_out_integer ("Length", obj_desc->buffer.length); + acpi_ex_out_pointer ("Pointer", obj_desc->buffer.pointer); + ACPI_DUMP_BUFFER (obj_desc->buffer.pointer, obj_desc->buffer.length); + break; + + + case ACPI_TYPE_PACKAGE: + + acpi_ex_out_integer ("Flags", obj_desc->package.flags); + acpi_ex_out_integer ("Count", obj_desc->package.count); + acpi_ex_out_pointer ("Elements", obj_desc->package.elements); + + /* Dump the package contents */ + + if (obj_desc->package.count > 0) { + acpi_os_printf ("\nPackage Contents:\n"); + for (i = 0; i < obj_desc->package.count; i++) { + acpi_os_printf ("[%.3d] %p", i, obj_desc->package.elements[i]); + if (obj_desc->package.elements[i]) { + acpi_os_printf (" %s", + acpi_ut_get_object_type_name (obj_desc->package.elements[i])); + } + acpi_os_printf ("\n"); + } + } + break; + + + case ACPI_TYPE_DEVICE: + + acpi_ex_out_pointer ("Handler", obj_desc->device.handler); + acpi_ex_out_pointer ("system_notify", obj_desc->device.system_notify); + acpi_ex_out_pointer ("device_notify", obj_desc->device.device_notify); + break; + + + case ACPI_TYPE_EVENT: + + acpi_ex_out_pointer ("Semaphore", obj_desc->event.semaphore); + break; + + + case ACPI_TYPE_METHOD: + + acpi_ex_out_integer ("param_count", obj_desc->method.param_count); + acpi_ex_out_integer ("Concurrency", obj_desc->method.concurrency); + acpi_ex_out_pointer ("Semaphore", obj_desc->method.semaphore); + acpi_ex_out_integer ("owning_id", obj_desc->method.owning_id); + acpi_ex_out_integer ("aml_length", obj_desc->method.aml_length); + acpi_ex_out_pointer ("aml_start", obj_desc->method.aml_start); + break; + + + case ACPI_TYPE_MUTEX: + + acpi_ex_out_integer ("sync_level", obj_desc->mutex.sync_level); + acpi_ex_out_pointer ("owner_thread", obj_desc->mutex.owner_thread); + acpi_ex_out_integer ("acquire_depth", obj_desc->mutex.acquisition_depth); + acpi_ex_out_pointer ("Semaphore", obj_desc->mutex.semaphore); + break; + + + case ACPI_TYPE_REGION: + + acpi_ex_out_integer ("space_id", obj_desc->region.space_id); + acpi_ex_out_integer ("Flags", obj_desc->region.flags); + acpi_ex_out_address ("Address", obj_desc->region.address); + acpi_ex_out_integer ("Length", obj_desc->region.length); + acpi_ex_out_pointer ("Handler", obj_desc->region.handler); + acpi_ex_out_pointer ("Next", obj_desc->region.next); + break; + + + case ACPI_TYPE_POWER: + + acpi_ex_out_integer ("system_level", obj_desc->power_resource.system_level); + acpi_ex_out_integer ("resource_order", obj_desc->power_resource.resource_order); + acpi_ex_out_pointer ("system_notify", obj_desc->power_resource.system_notify); + acpi_ex_out_pointer ("device_notify", obj_desc->power_resource.device_notify); + break; + + + case ACPI_TYPE_PROCESSOR: + + acpi_ex_out_integer ("Processor ID", obj_desc->processor.proc_id); + acpi_ex_out_integer ("Length", obj_desc->processor.length); + acpi_ex_out_address ("Address", (acpi_physical_address) obj_desc->processor.address); + acpi_ex_out_pointer ("system_notify", obj_desc->processor.system_notify); + acpi_ex_out_pointer ("device_notify", obj_desc->processor.device_notify); + acpi_ex_out_pointer ("Handler", obj_desc->processor.handler); + break; + + + case ACPI_TYPE_THERMAL: + + acpi_ex_out_pointer ("system_notify", obj_desc->thermal_zone.system_notify); + acpi_ex_out_pointer ("device_notify", obj_desc->thermal_zone.device_notify); + acpi_ex_out_pointer ("Handler", obj_desc->thermal_zone.handler); + break; + + + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + acpi_ex_out_integer ("field_flags", obj_desc->common_field.field_flags); + acpi_ex_out_integer ("access_byte_width",obj_desc->common_field.access_byte_width); + acpi_ex_out_integer ("bit_length", obj_desc->common_field.bit_length); + acpi_ex_out_integer ("fld_bit_offset", obj_desc->common_field.start_field_bit_offset); + acpi_ex_out_integer ("base_byte_offset", obj_desc->common_field.base_byte_offset); + acpi_ex_out_pointer ("parent_node", obj_desc->common_field.node); + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_BUFFER_FIELD: + acpi_ex_out_pointer ("buffer_obj", obj_desc->buffer_field.buffer_obj); + break; + + case ACPI_TYPE_LOCAL_REGION_FIELD: + acpi_ex_out_pointer ("region_obj", obj_desc->field.region_obj); + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + acpi_ex_out_integer ("Value", obj_desc->bank_field.value); + acpi_ex_out_pointer ("region_obj", obj_desc->bank_field.region_obj); + acpi_ex_out_pointer ("bank_obj", obj_desc->bank_field.bank_obj); + break; + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + acpi_ex_out_integer ("Value", obj_desc->index_field.value); + acpi_ex_out_pointer ("Index", obj_desc->index_field.index_obj); + acpi_ex_out_pointer ("Data", obj_desc->index_field.data_obj); + break; + + default: + /* All object types covered above */ + break; + } + break; + + + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_ex_out_integer ("target_type", obj_desc->reference.target_type); + acpi_ex_out_string ("Opcode", (acpi_ps_get_opcode_info (obj_desc->reference.opcode))->name); + acpi_ex_out_integer ("Offset", obj_desc->reference.offset); + acpi_ex_out_pointer ("obj_desc", obj_desc->reference.object); + acpi_ex_out_pointer ("Node", obj_desc->reference.node); + acpi_ex_out_pointer ("Where", obj_desc->reference.where); + break; + + + case ACPI_TYPE_LOCAL_ADDRESS_HANDLER: + + acpi_ex_out_integer ("space_id", obj_desc->address_space.space_id); + acpi_ex_out_pointer ("Next", obj_desc->address_space.next); + acpi_ex_out_pointer ("region_list", obj_desc->address_space.region_list); + acpi_ex_out_pointer ("Node", obj_desc->address_space.node); + acpi_ex_out_pointer ("Context", obj_desc->address_space.context); + break; + + + case ACPI_TYPE_LOCAL_NOTIFY: + + acpi_ex_out_pointer ("Node", obj_desc->notify.node); + acpi_ex_out_pointer ("Context", obj_desc->notify.context); + break; + + + case ACPI_TYPE_LOCAL_ALIAS: + case ACPI_TYPE_LOCAL_METHOD_ALIAS: + case ACPI_TYPE_LOCAL_EXTRA: + case ACPI_TYPE_LOCAL_DATA: + default: + + acpi_os_printf ( + "ex_dump_object_descriptor: Display not implemented for object type %s\n", + acpi_ut_get_object_type_name (obj_desc)); + break; + } + + return_VOID; +} + +#endif /* ACPI_FUTURE_USAGE */ + +#endif + diff --git a/drivers/acpi/executer/exfield.c b/drivers/acpi/executer/exfield.c new file mode 100644 index 00000000000..be7f2124fa0 --- /dev/null +++ b/drivers/acpi/executer/exfield.c @@ -0,0 +1,367 @@ +/****************************************************************************** + * + * Module Name: exfield - ACPI AML (p-code) execution - field manipulation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exfield") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_read_data_from_field + * + * PARAMETERS: walk_state - Current execution state + * obj_desc - The named field + * ret_buffer_desc - Where the return data object is stored + * + * RETURN: Status + * + * DESCRIPTION: Read from a named field. Returns either an Integer or a + * Buffer, depending on the size of the field. + * + ******************************************************************************/ + +acpi_status +acpi_ex_read_data_from_field ( + struct acpi_walk_state *walk_state, + union acpi_operand_object *obj_desc, + union acpi_operand_object **ret_buffer_desc) +{ + acpi_status status; + union acpi_operand_object *buffer_desc; + acpi_size length; + void *buffer; + u8 locked; + + + ACPI_FUNCTION_TRACE_PTR ("ex_read_data_from_field", obj_desc); + + + /* Parameter validation */ + + if (!obj_desc) { + return_ACPI_STATUS (AE_AML_NO_OPERAND); + } + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_BUFFER_FIELD) { + /* + * If the buffer_field arguments have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_buffer_field_arguments (obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + } + else if ((ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_LOCAL_REGION_FIELD) && + (obj_desc->field.region_obj->region.space_id == ACPI_ADR_SPACE_SMBUS)) { + /* + * This is an SMBus read. We must create a buffer to hold the data + * and directly access the region handler. + */ + buffer_desc = acpi_ut_create_buffer_object (ACPI_SMBUS_BUFFER_SIZE); + if (!buffer_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Lock entire transaction if requested */ + + locked = acpi_ex_acquire_global_lock (obj_desc->common_field.field_flags); + + /* + * Perform the read. + * Note: Smbus protocol value is passed in upper 16-bits of Function + */ + status = acpi_ex_access_region (obj_desc, 0, + ACPI_CAST_PTR (acpi_integer, buffer_desc->buffer.pointer), + ACPI_READ | (obj_desc->field.attribute << 16)); + acpi_ex_release_global_lock (locked); + goto exit; + } + + /* + * Allocate a buffer for the contents of the field. + * + * If the field is larger than the size of an acpi_integer, create + * a BUFFER to hold it. Otherwise, use an INTEGER. This allows + * the use of arithmetic operators on the returned value if the + * field size is equal or smaller than an Integer. + * + * Note: Field.length is in bits. + */ + length = (acpi_size) ACPI_ROUND_BITS_UP_TO_BYTES (obj_desc->field.bit_length); + if (length > acpi_gbl_integer_byte_width) { + /* Field is too large for an Integer, create a Buffer instead */ + + buffer_desc = acpi_ut_create_buffer_object (length); + if (!buffer_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + buffer = buffer_desc->buffer.pointer; + } + else { + /* Field will fit within an Integer (normal case) */ + + buffer_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!buffer_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + length = acpi_gbl_integer_byte_width; + buffer_desc->integer.value = 0; + buffer = &buffer_desc->integer.value; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "field_read [TO]: Obj %p, Type %X, Buf %p, byte_len %X\n", + obj_desc, ACPI_GET_OBJECT_TYPE (obj_desc), buffer, (u32) length)); + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "field_read [FROM]: bit_len %X, bit_off %X, byte_off %X\n", + obj_desc->common_field.bit_length, + obj_desc->common_field.start_field_bit_offset, + obj_desc->common_field.base_byte_offset)); + + /* Lock entire transaction if requested */ + + locked = acpi_ex_acquire_global_lock (obj_desc->common_field.field_flags); + + /* Read from the field */ + + status = acpi_ex_extract_from_field (obj_desc, buffer, (u32) length); + acpi_ex_release_global_lock (locked); + + +exit: + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (buffer_desc); + } + else if (ret_buffer_desc) { + *ret_buffer_desc = buffer_desc; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_write_data_to_field + * + * PARAMETERS: source_desc - Contains data to write + * obj_desc - The named field + * + * RETURN: Status + * + * DESCRIPTION: Write to a named field + * + ******************************************************************************/ + +acpi_status +acpi_ex_write_data_to_field ( + union acpi_operand_object *source_desc, + union acpi_operand_object *obj_desc, + union acpi_operand_object **result_desc) +{ + acpi_status status; + u32 length; + u32 required_length; + void *buffer; + void *new_buffer; + u8 locked; + union acpi_operand_object *buffer_desc; + + + ACPI_FUNCTION_TRACE_PTR ("ex_write_data_to_field", obj_desc); + + + /* Parameter validation */ + + if (!source_desc || !obj_desc) { + return_ACPI_STATUS (AE_AML_NO_OPERAND); + } + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_BUFFER_FIELD) { + /* + * If the buffer_field arguments have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_buffer_field_arguments (obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + } + else if ((ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_LOCAL_REGION_FIELD) && + (obj_desc->field.region_obj->region.space_id == ACPI_ADR_SPACE_SMBUS)) { + /* + * This is an SMBus write. We will bypass the entire field mechanism + * and handoff the buffer directly to the handler. + * + * Source must be a buffer of sufficient size (ACPI_SMBUS_BUFFER_SIZE). + */ + if (ACPI_GET_OBJECT_TYPE (source_desc) != ACPI_TYPE_BUFFER) { + ACPI_REPORT_ERROR (("SMBus write requires Buffer, found type %s\n", + acpi_ut_get_object_type_name (source_desc))); + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + if (source_desc->buffer.length < ACPI_SMBUS_BUFFER_SIZE) { + ACPI_REPORT_ERROR (("SMBus write requires Buffer of length %X, found length %X\n", + ACPI_SMBUS_BUFFER_SIZE, source_desc->buffer.length)); + return_ACPI_STATUS (AE_AML_BUFFER_LIMIT); + } + + buffer_desc = acpi_ut_create_buffer_object (ACPI_SMBUS_BUFFER_SIZE); + if (!buffer_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + buffer = buffer_desc->buffer.pointer; + ACPI_MEMCPY (buffer, source_desc->buffer.pointer, ACPI_SMBUS_BUFFER_SIZE); + + /* Lock entire transaction if requested */ + + locked = acpi_ex_acquire_global_lock (obj_desc->common_field.field_flags); + + /* + * Perform the write (returns status and perhaps data in the same buffer) + * Note: SMBus protocol type is passed in upper 16-bits of Function. + */ + status = acpi_ex_access_region (obj_desc, 0, + (acpi_integer *) buffer, + ACPI_WRITE | (obj_desc->field.attribute << 16)); + acpi_ex_release_global_lock (locked); + + *result_desc = buffer_desc; + return_ACPI_STATUS (status); + } + + /* + * Get a pointer to the data to be written + */ + switch (ACPI_GET_OBJECT_TYPE (source_desc)) { + case ACPI_TYPE_INTEGER: + buffer = &source_desc->integer.value; + length = sizeof (source_desc->integer.value); + break; + + case ACPI_TYPE_BUFFER: + buffer = source_desc->buffer.pointer; + length = source_desc->buffer.length; + break; + + case ACPI_TYPE_STRING: + buffer = source_desc->string.pointer; + length = source_desc->string.length; + break; + + default: + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* + * We must have a buffer that is at least as long as the field + * we are writing to. This is because individual fields are + * indivisible and partial writes are not supported -- as per + * the ACPI specification. + */ + new_buffer = NULL; + required_length = ACPI_ROUND_BITS_UP_TO_BYTES (obj_desc->common_field.bit_length); + + if (length < required_length) { + /* We need to create a new buffer */ + + new_buffer = ACPI_MEM_CALLOCATE (required_length); + if (!new_buffer) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* + * Copy the original data to the new buffer, starting + * at Byte zero. All unused (upper) bytes of the + * buffer will be 0. + */ + ACPI_MEMCPY ((char *) new_buffer, (char *) buffer, length); + buffer = new_buffer; + length = required_length; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "field_write [FROM]: Obj %p (%s:%X), Buf %p, byte_len %X\n", + source_desc, acpi_ut_get_type_name (ACPI_GET_OBJECT_TYPE (source_desc)), + ACPI_GET_OBJECT_TYPE (source_desc), buffer, length)); + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "field_write [TO]: Obj %p (%s:%X), bit_len %X, bit_off %X, byte_off %X\n", + obj_desc, acpi_ut_get_type_name (ACPI_GET_OBJECT_TYPE (obj_desc)), + ACPI_GET_OBJECT_TYPE (obj_desc), + obj_desc->common_field.bit_length, + obj_desc->common_field.start_field_bit_offset, + obj_desc->common_field.base_byte_offset)); + + /* Lock entire transaction if requested */ + + locked = acpi_ex_acquire_global_lock (obj_desc->common_field.field_flags); + + /* Write to the field */ + + status = acpi_ex_insert_into_field (obj_desc, buffer, length); + acpi_ex_release_global_lock (locked); + + /* Free temporary buffer if we used one */ + + if (new_buffer) { + ACPI_MEM_FREE (new_buffer); + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exfldio.c b/drivers/acpi/executer/exfldio.c new file mode 100644 index 00000000000..9d0f9d2e906 --- /dev/null +++ b/drivers/acpi/executer/exfldio.c @@ -0,0 +1,835 @@ +/****************************************************************************** + * + * Module Name: exfldio - Aml Field I/O + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> +#include <acpi/acevents.h> +#include <acpi/acdispat.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exfldio") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_setup_region + * + * PARAMETERS: *obj_desc - Field to be read or written + * field_datum_byte_offset - Byte offset of this datum within the + * parent field + * + * RETURN: Status + * + * DESCRIPTION: Common processing for acpi_ex_extract_from_field and + * acpi_ex_insert_into_field. Initialize the Region if necessary and + * validate the request. + * + ******************************************************************************/ + +acpi_status +acpi_ex_setup_region ( + union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset) +{ + acpi_status status = AE_OK; + union acpi_operand_object *rgn_desc; + + + ACPI_FUNCTION_TRACE_U32 ("ex_setup_region", field_datum_byte_offset); + + + rgn_desc = obj_desc->common_field.region_obj; + + /* We must have a valid region */ + + if (ACPI_GET_OBJECT_TYPE (rgn_desc) != ACPI_TYPE_REGION) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Needed Region, found type %X (%s)\n", + ACPI_GET_OBJECT_TYPE (rgn_desc), + acpi_ut_get_object_type_name (rgn_desc))); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* + * If the Region Address and Length have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(rgn_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_region_arguments (rgn_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + if (rgn_desc->region.space_id == ACPI_ADR_SPACE_SMBUS) { + /* SMBus has a non-linear address space */ + + return_ACPI_STATUS (AE_OK); + } + +#ifdef ACPI_UNDER_DEVELOPMENT + /* + * If the Field access is any_acc, we can now compute the optimal + * access (because we know know the length of the parent region) + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } +#endif + + /* + * Validate the request. The entire request from the byte offset for a + * length of one field datum (access width) must fit within the region. + * (Region length is specified in bytes) + */ + if (rgn_desc->region.length < (obj_desc->common_field.base_byte_offset + + field_datum_byte_offset + + obj_desc->common_field.access_byte_width)) { + if (acpi_gbl_enable_interpreter_slack) { + /* + * Slack mode only: We will go ahead and allow access to this + * field if it is within the region length rounded up to the next + * access width boundary. + */ + if (ACPI_ROUND_UP (rgn_desc->region.length, + obj_desc->common_field.access_byte_width) >= + (obj_desc->common_field.base_byte_offset + + (acpi_native_uint) obj_desc->common_field.access_byte_width + + field_datum_byte_offset)) { + return_ACPI_STATUS (AE_OK); + } + } + + if (rgn_desc->region.length < obj_desc->common_field.access_byte_width) { + /* + * This is the case where the access_type (acc_word, etc.) is wider + * than the region itself. For example, a region of length one + * byte, and a field with Dword access specified. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Field [%4.4s] access width (%d bytes) too large for region [%4.4s] (length %X)\n", + acpi_ut_get_node_name (obj_desc->common_field.node), + obj_desc->common_field.access_byte_width, + acpi_ut_get_node_name (rgn_desc->region.node), rgn_desc->region.length)); + } + + /* + * Offset rounded up to next multiple of field width + * exceeds region length, indicate an error + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Field [%4.4s] Base+Offset+Width %X+%X+%X is beyond end of region [%4.4s] (length %X)\n", + acpi_ut_get_node_name (obj_desc->common_field.node), + obj_desc->common_field.base_byte_offset, + field_datum_byte_offset, obj_desc->common_field.access_byte_width, + acpi_ut_get_node_name (rgn_desc->region.node), rgn_desc->region.length)); + + return_ACPI_STATUS (AE_AML_REGION_LIMIT); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_access_region + * + * PARAMETERS: *obj_desc - Field to be read + * field_datum_byte_offset - Byte offset of this datum within the + * parent field + * *Value - Where to store value (must at least + * the size of acpi_integer) + * Function - Read or Write flag plus other region- + * dependent flags + * + * RETURN: Status + * + * DESCRIPTION: Read or Write a single field datum to an Operation Region. + * + ******************************************************************************/ + +acpi_status +acpi_ex_access_region ( + union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset, + acpi_integer *value, + u32 function) +{ + acpi_status status; + union acpi_operand_object *rgn_desc; + acpi_physical_address address; + + + ACPI_FUNCTION_TRACE ("ex_access_region"); + + + /* + * Ensure that the region operands are fully evaluated and verify + * the validity of the request + */ + status = acpi_ex_setup_region (obj_desc, field_datum_byte_offset); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * The physical address of this field datum is: + * + * 1) The base of the region, plus + * 2) The base offset of the field, plus + * 3) The current offset into the field + */ + rgn_desc = obj_desc->common_field.region_obj; + address = rgn_desc->region.address + + obj_desc->common_field.base_byte_offset + + field_datum_byte_offset; + + if ((function & ACPI_IO_MASK) == ACPI_READ) { + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[READ]")); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "[WRITE]")); + } + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_BFIELD, + " Region [%s:%X], Width %X, byte_base %X, Offset %X at %8.8X%8.8X\n", + acpi_ut_get_region_name (rgn_desc->region.space_id), + rgn_desc->region.space_id, + obj_desc->common_field.access_byte_width, + obj_desc->common_field.base_byte_offset, + field_datum_byte_offset, + ACPI_FORMAT_UINT64 (address))); + + /* Invoke the appropriate address_space/op_region handler */ + + status = acpi_ev_address_space_dispatch (rgn_desc, function, + address, ACPI_MUL_8 (obj_desc->common_field.access_byte_width), value); + + if (ACPI_FAILURE (status)) { + if (status == AE_NOT_IMPLEMENTED) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Region %s(%X) not implemented\n", + acpi_ut_get_region_name (rgn_desc->region.space_id), + rgn_desc->region.space_id)); + } + else if (status == AE_NOT_EXIST) { + ACPI_REPORT_ERROR (( + "Region %s(%X) has no handler\n", + acpi_ut_get_region_name (rgn_desc->region.space_id), + rgn_desc->region.space_id)); + } + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_register_overflow + * + * PARAMETERS: *obj_desc - Register(Field) to be written + * Value - Value to be stored + * + * RETURN: TRUE if value overflows the field, FALSE otherwise + * + * DESCRIPTION: Check if a value is out of range of the field being written. + * Used to check if the values written to Index and Bank registers + * are out of range. Normally, the value is simply truncated + * to fit the field, but this case is most likely a serious + * coding error in the ASL. + * + ******************************************************************************/ + +u8 +acpi_ex_register_overflow ( + union acpi_operand_object *obj_desc, + acpi_integer value) +{ + + if (obj_desc->common_field.bit_length >= ACPI_INTEGER_BIT_SIZE) { + /* + * The field is large enough to hold the maximum integer, so we can + * never overflow it. + */ + return (FALSE); + } + + if (value >= ((acpi_integer) 1 << obj_desc->common_field.bit_length)) { + /* + * The Value is larger than the maximum value that can fit into + * the register. + */ + return (TRUE); + } + + /* The Value will fit into the field with no truncation */ + + return (FALSE); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_field_datum_io + * + * PARAMETERS: *obj_desc - Field to be read + * field_datum_byte_offset - Byte offset of this datum within the + * parent field + * *Value - Where to store value (must be 64 bits) + * read_write - Read or Write flag + * + * RETURN: Status + * + * DESCRIPTION: Read or Write a single datum of a field. The field_type is + * demultiplexed here to handle the different types of fields + * (buffer_field, region_field, index_field, bank_field) + * + ******************************************************************************/ + +acpi_status +acpi_ex_field_datum_io ( + union acpi_operand_object *obj_desc, + u32 field_datum_byte_offset, + acpi_integer *value, + u32 read_write) +{ + acpi_status status; + acpi_integer local_value; + + + ACPI_FUNCTION_TRACE_U32 ("ex_field_datum_io", field_datum_byte_offset); + + + if (read_write == ACPI_READ) { + if (!value) { + local_value = 0; + value = &local_value; /* To support reads without saving return value */ + } + + /* Clear the entire return buffer first, [Very Important!] */ + + *value = 0; + } + + /* + * The four types of fields are: + * + * buffer_field - Read/write from/to a Buffer + * region_field - Read/write from/to a Operation Region. + * bank_field - Write to a Bank Register, then read/write from/to an op_region + * index_field - Write to an Index Register, then read/write from/to a Data Register + */ + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_BUFFER_FIELD: + /* + * If the buffer_field arguments have not been previously evaluated, + * evaluate them now and save the results. + */ + if (!(obj_desc->common.flags & AOPOBJ_DATA_VALID)) { + status = acpi_ds_get_buffer_field_arguments (obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + if (read_write == ACPI_READ) { + /* + * Copy the data from the source buffer. + * Length is the field width in bytes. + */ + ACPI_MEMCPY (value, (obj_desc->buffer_field.buffer_obj)->buffer.pointer + + obj_desc->buffer_field.base_byte_offset + + field_datum_byte_offset, + obj_desc->common_field.access_byte_width); + } + else { + /* + * Copy the data to the target buffer. + * Length is the field width in bytes. + */ + ACPI_MEMCPY ((obj_desc->buffer_field.buffer_obj)->buffer.pointer + + obj_desc->buffer_field.base_byte_offset + + field_datum_byte_offset, + value, obj_desc->common_field.access_byte_width); + } + + status = AE_OK; + break; + + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + /* Ensure that the bank_value is not beyond the capacity of the register */ + + if (acpi_ex_register_overflow (obj_desc->bank_field.bank_obj, + (acpi_integer) obj_desc->bank_field.value)) { + return_ACPI_STATUS (AE_AML_REGISTER_LIMIT); + } + + /* + * For bank_fields, we must write the bank_value to the bank_register + * (itself a region_field) before we can access the data. + */ + status = acpi_ex_insert_into_field (obj_desc->bank_field.bank_obj, + &obj_desc->bank_field.value, + sizeof (obj_desc->bank_field.value)); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Now that the Bank has been selected, fall through to the + * region_field case and write the datum to the Operation Region + */ + + /*lint -fallthrough */ + + + case ACPI_TYPE_LOCAL_REGION_FIELD: + /* + * For simple region_fields, we just directly access the owning + * Operation Region. + */ + status = acpi_ex_access_region (obj_desc, field_datum_byte_offset, value, + read_write); + break; + + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + + /* Ensure that the index_value is not beyond the capacity of the register */ + + if (acpi_ex_register_overflow (obj_desc->index_field.index_obj, + (acpi_integer) obj_desc->index_field.value)) { + return_ACPI_STATUS (AE_AML_REGISTER_LIMIT); + } + + /* Write the index value to the index_register (itself a region_field) */ + + field_datum_byte_offset += obj_desc->index_field.value; + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Write to Index Register: Value %8.8X\n", + field_datum_byte_offset)); + + status = acpi_ex_insert_into_field (obj_desc->index_field.index_obj, + &field_datum_byte_offset, + sizeof (field_datum_byte_offset)); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "I/O to Data Register: value_ptr %p\n", + value)); + + if (read_write == ACPI_READ) { + /* Read the datum from the data_register */ + + status = acpi_ex_extract_from_field (obj_desc->index_field.data_obj, + value, sizeof (acpi_integer)); + } + else { + /* Write the datum to the data_register */ + + status = acpi_ex_insert_into_field (obj_desc->index_field.data_obj, + value, sizeof (acpi_integer)); + } + break; + + + default: + + ACPI_REPORT_ERROR (("Wrong object type in field I/O %X\n", + ACPI_GET_OBJECT_TYPE (obj_desc))); + status = AE_AML_INTERNAL; + break; + } + + if (ACPI_SUCCESS (status)) { + if (read_write == ACPI_READ) { + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Read %8.8X%8.8X, Width %d\n", + ACPI_FORMAT_UINT64 (*value), + obj_desc->common_field.access_byte_width)); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Value Written %8.8X%8.8X, Width %d\n", + ACPI_FORMAT_UINT64 (*value), + obj_desc->common_field.access_byte_width)); + } + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_write_with_update_rule + * + * PARAMETERS: *obj_desc - Field to be set + * Value - Value to store + * + * RETURN: Status + * + * DESCRIPTION: Apply the field update rule to a field write + * + ******************************************************************************/ + +acpi_status +acpi_ex_write_with_update_rule ( + union acpi_operand_object *obj_desc, + acpi_integer mask, + acpi_integer field_value, + u32 field_datum_byte_offset) +{ + acpi_status status = AE_OK; + acpi_integer merged_value; + acpi_integer current_value; + + + ACPI_FUNCTION_TRACE_U32 ("ex_write_with_update_rule", mask); + + + /* Start with the new bits */ + + merged_value = field_value; + + /* If the mask is all ones, we don't need to worry about the update rule */ + + if (mask != ACPI_INTEGER_MAX) { + /* Decode the update rule */ + + switch (obj_desc->common_field.field_flags & AML_FIELD_UPDATE_RULE_MASK) { + case AML_FIELD_UPDATE_PRESERVE: + /* + * Check if update rule needs to be applied (not if mask is all + * ones) The left shift drops the bits we want to ignore. + */ + if ((~mask << (ACPI_MUL_8 (sizeof (mask)) - + ACPI_MUL_8 (obj_desc->common_field.access_byte_width))) != 0) { + /* + * Read the current contents of the byte/word/dword containing + * the field, and merge with the new field value. + */ + status = acpi_ex_field_datum_io (obj_desc, field_datum_byte_offset, + ¤t_value, ACPI_READ); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + merged_value |= (current_value & ~mask); + } + break; + + case AML_FIELD_UPDATE_WRITE_AS_ONES: + + /* Set positions outside the field to all ones */ + + merged_value |= ~mask; + break; + + case AML_FIELD_UPDATE_WRITE_AS_ZEROS: + + /* Set positions outside the field to all zeros */ + + merged_value &= mask; + break; + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "write_with_update_rule: Unknown update_rule setting: %X\n", + (obj_desc->common_field.field_flags & AML_FIELD_UPDATE_RULE_MASK))); + return_ACPI_STATUS (AE_AML_OPERAND_VALUE); + } + } + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Mask %8.8X%8.8X, datum_offset %X, Width %X, Value %8.8X%8.8X, merged_value %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (mask), + field_datum_byte_offset, + obj_desc->common_field.access_byte_width, + ACPI_FORMAT_UINT64 (field_value), + ACPI_FORMAT_UINT64 (merged_value))); + + /* Write the merged value */ + + status = acpi_ex_field_datum_io (obj_desc, field_datum_byte_offset, + &merged_value, ACPI_WRITE); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_extract_from_field + * + * PARAMETERS: obj_desc - Field to be read + * Buffer - Where to store the field data + * buffer_length - Length of Buffer + * + * RETURN: Status + * + * DESCRIPTION: Retrieve the current value of the given field + * + ******************************************************************************/ + +acpi_status +acpi_ex_extract_from_field ( + union acpi_operand_object *obj_desc, + void *buffer, + u32 buffer_length) +{ + acpi_status status; + acpi_integer raw_datum; + acpi_integer merged_datum; + u32 field_offset = 0; + u32 buffer_offset = 0; + u32 buffer_tail_bits; + u32 datum_count; + u32 field_datum_count; + u32 i; + + + ACPI_FUNCTION_TRACE ("ex_extract_from_field"); + + + /* Validate target buffer and clear it */ + + if (buffer_length < ACPI_ROUND_BITS_UP_TO_BYTES ( + obj_desc->common_field.bit_length)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Field size %X (bits) is too large for buffer (%X)\n", + obj_desc->common_field.bit_length, buffer_length)); + + return_ACPI_STATUS (AE_BUFFER_OVERFLOW); + } + ACPI_MEMSET (buffer, 0, buffer_length); + + /* Compute the number of datums (access width data items) */ + + datum_count = ACPI_ROUND_UP_TO ( + obj_desc->common_field.bit_length, + obj_desc->common_field.access_bit_width); + field_datum_count = ACPI_ROUND_UP_TO ( + obj_desc->common_field.bit_length + + obj_desc->common_field.start_field_bit_offset, + obj_desc->common_field.access_bit_width); + + /* Priming read from the field */ + + status = acpi_ex_field_datum_io (obj_desc, field_offset, &raw_datum, ACPI_READ); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + merged_datum = raw_datum >> obj_desc->common_field.start_field_bit_offset; + + /* Read the rest of the field */ + + for (i = 1; i < field_datum_count; i++) { + /* Get next input datum from the field */ + + field_offset += obj_desc->common_field.access_byte_width; + status = acpi_ex_field_datum_io (obj_desc, field_offset, + &raw_datum, ACPI_READ); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Merge with previous datum if necessary */ + + merged_datum |= raw_datum << + (obj_desc->common_field.access_bit_width - obj_desc->common_field.start_field_bit_offset); + + if (i == datum_count) { + break; + } + + /* Write merged datum to target buffer */ + + ACPI_MEMCPY (((char *) buffer) + buffer_offset, &merged_datum, + ACPI_MIN(obj_desc->common_field.access_byte_width, + buffer_length - buffer_offset)); + + buffer_offset += obj_desc->common_field.access_byte_width; + merged_datum = raw_datum >> obj_desc->common_field.start_field_bit_offset; + } + + /* Mask off any extra bits in the last datum */ + + buffer_tail_bits = obj_desc->common_field.bit_length % obj_desc->common_field.access_bit_width; + if (buffer_tail_bits) { + merged_datum &= ACPI_MASK_BITS_ABOVE (buffer_tail_bits); + } + + /* Write the last datum to the buffer */ + + ACPI_MEMCPY (((char *) buffer) + buffer_offset, &merged_datum, + ACPI_MIN(obj_desc->common_field.access_byte_width, + buffer_length - buffer_offset)); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_insert_into_field + * + * PARAMETERS: obj_desc - Field to be written + * Buffer - Data to be written + * buffer_length - Length of Buffer + * + * RETURN: Status + * + * DESCRIPTION: Store the Buffer contents into the given field + * + ******************************************************************************/ + +acpi_status +acpi_ex_insert_into_field ( + union acpi_operand_object *obj_desc, + void *buffer, + u32 buffer_length) +{ + acpi_status status; + acpi_integer mask; + acpi_integer merged_datum; + acpi_integer raw_datum = 0; + u32 field_offset = 0; + u32 buffer_offset = 0; + u32 buffer_tail_bits; + u32 datum_count; + u32 field_datum_count; + u32 i; + + + ACPI_FUNCTION_TRACE ("ex_insert_into_field"); + + + /* Validate input buffer */ + + if (buffer_length < ACPI_ROUND_BITS_UP_TO_BYTES ( + obj_desc->common_field.bit_length)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Field size %X (bits) is too large for buffer (%X)\n", + obj_desc->common_field.bit_length, buffer_length)); + + return_ACPI_STATUS (AE_BUFFER_OVERFLOW); + } + + /* Compute the number of datums (access width data items) */ + + mask = ACPI_MASK_BITS_BELOW (obj_desc->common_field.start_field_bit_offset); + datum_count = ACPI_ROUND_UP_TO (obj_desc->common_field.bit_length, + obj_desc->common_field.access_bit_width); + field_datum_count = ACPI_ROUND_UP_TO (obj_desc->common_field.bit_length + + obj_desc->common_field.start_field_bit_offset, + obj_desc->common_field.access_bit_width); + + /* Get initial Datum from the input buffer */ + + ACPI_MEMCPY (&raw_datum, buffer, + ACPI_MIN(obj_desc->common_field.access_byte_width, + buffer_length - buffer_offset)); + + merged_datum = raw_datum << obj_desc->common_field.start_field_bit_offset; + + /* Write the entire field */ + + for (i = 1; i < field_datum_count; i++) { + /* Write merged datum to the target field */ + + merged_datum &= mask; + status = acpi_ex_write_with_update_rule (obj_desc, mask, merged_datum, field_offset); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Start new output datum by merging with previous input datum */ + + field_offset += obj_desc->common_field.access_byte_width; + merged_datum = raw_datum >> + (obj_desc->common_field.access_bit_width - obj_desc->common_field.start_field_bit_offset); + mask = ACPI_INTEGER_MAX; + + if (i == datum_count) { + break; + } + + /* Get the next input datum from the buffer */ + + buffer_offset += obj_desc->common_field.access_byte_width; + ACPI_MEMCPY (&raw_datum, ((char *) buffer) + buffer_offset, + ACPI_MIN(obj_desc->common_field.access_byte_width, + buffer_length - buffer_offset)); + merged_datum |= raw_datum << obj_desc->common_field.start_field_bit_offset; + } + + /* Mask off any extra bits in the last datum */ + + buffer_tail_bits = (obj_desc->common_field.bit_length + + obj_desc->common_field.start_field_bit_offset) % obj_desc->common_field.access_bit_width; + if (buffer_tail_bits) { + mask &= ACPI_MASK_BITS_ABOVE (buffer_tail_bits); + } + + /* Write the last datum to the field */ + + merged_datum &= mask; + status = acpi_ex_write_with_update_rule (obj_desc, mask, merged_datum, field_offset); + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exmisc.c b/drivers/acpi/executer/exmisc.c new file mode 100644 index 00000000000..b542dcd58c0 --- /dev/null +++ b/drivers/acpi/executer/exmisc.c @@ -0,0 +1,738 @@ + +/****************************************************************************** + * + * Module Name: exmisc - ACPI AML (p-code) execution - specific opcodes + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exmisc") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_get_object_reference + * + * PARAMETERS: obj_desc - Create a reference to this object + * return_desc - Where to store the reference + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Obtain and return a "reference" to the target object + * Common code for the ref_of_op and the cond_ref_of_op. + * + ******************************************************************************/ + +acpi_status +acpi_ex_get_object_reference ( + union acpi_operand_object *obj_desc, + union acpi_operand_object **return_desc, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *reference_obj; + union acpi_operand_object *referenced_obj; + + + ACPI_FUNCTION_TRACE_PTR ("ex_get_object_reference", obj_desc); + + + *return_desc = NULL; + + switch (ACPI_GET_DESCRIPTOR_TYPE (obj_desc)) { + case ACPI_DESC_TYPE_OPERAND: + + if (ACPI_GET_OBJECT_TYPE (obj_desc) != ACPI_TYPE_LOCAL_REFERENCE) { + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* + * Must be a reference to a Local or Arg + */ + switch (obj_desc->reference.opcode) { + case AML_LOCAL_OP: + case AML_ARG_OP: + case AML_DEBUG_OP: + + /* The referenced object is the pseudo-node for the local/arg */ + + referenced_obj = obj_desc->reference.object; + break; + + default: + + ACPI_REPORT_ERROR (("Unknown Reference opcode in get_reference %X\n", + obj_desc->reference.opcode)); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + break; + + + case ACPI_DESC_TYPE_NAMED: + + /* + * A named reference that has already been resolved to a Node + */ + referenced_obj = obj_desc; + break; + + + default: + + ACPI_REPORT_ERROR (("Invalid descriptor type in get_reference: %X\n", + ACPI_GET_DESCRIPTOR_TYPE (obj_desc))); + return_ACPI_STATUS (AE_TYPE); + } + + + /* Create a new reference object */ + + reference_obj = acpi_ut_create_internal_object (ACPI_TYPE_LOCAL_REFERENCE); + if (!reference_obj) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + reference_obj->reference.opcode = AML_REF_OF_OP; + reference_obj->reference.object = referenced_obj; + *return_desc = reference_obj; + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Object %p Type [%s], returning Reference %p\n", + obj_desc, acpi_ut_get_object_type_name (obj_desc), *return_desc)); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_concat_template + * + * PARAMETERS: Operand0 - First source object + * Operand1 - Second source object + * actual_return_desc - Where to place the return object + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Concatenate two resource templates + * + ******************************************************************************/ + +acpi_status +acpi_ex_concat_template ( + union acpi_operand_object *operand0, + union acpi_operand_object *operand1, + union acpi_operand_object **actual_return_desc, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *return_desc; + u8 *new_buf; + u8 *end_tag1; + u8 *end_tag2; + acpi_size length1; + acpi_size length2; + + + ACPI_FUNCTION_TRACE ("ex_concat_template"); + + + /* Find the end_tags in each resource template */ + + end_tag1 = acpi_ut_get_resource_end_tag (operand0); + end_tag2 = acpi_ut_get_resource_end_tag (operand1); + if (!end_tag1 || !end_tag2) { + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* Compute the length of each part */ + + length1 = ACPI_PTR_DIFF (end_tag1, operand0->buffer.pointer); + length2 = ACPI_PTR_DIFF (end_tag2, operand1->buffer.pointer) + + 2; /* Size of END_TAG */ + + /* Create a new buffer object for the result */ + + return_desc = acpi_ut_create_buffer_object (length1 + length2); + if (!return_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Copy the templates to the new descriptor */ + + new_buf = return_desc->buffer.pointer; + ACPI_MEMCPY (new_buf, operand0->buffer.pointer, length1); + ACPI_MEMCPY (new_buf + length1, operand1->buffer.pointer, length2); + + /* Compute the new checksum */ + + new_buf[return_desc->buffer.length - 1] = + acpi_ut_generate_checksum (return_desc->buffer.pointer, + (return_desc->buffer.length - 1)); + + /* Return the completed template descriptor */ + + *actual_return_desc = return_desc; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_concatenate + * + * PARAMETERS: Operand0 - First source object + * Operand1 - Second source object + * actual_return_desc - Where to place the return object + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Concatenate two objects OF THE SAME TYPE. + * + ******************************************************************************/ + +acpi_status +acpi_ex_do_concatenate ( + union acpi_operand_object *operand0, + union acpi_operand_object *operand1, + union acpi_operand_object **actual_return_desc, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *local_operand1 = operand1; + union acpi_operand_object *return_desc; + char *new_buf; + acpi_status status; + acpi_size new_length; + + + ACPI_FUNCTION_TRACE ("ex_do_concatenate"); + + + /* + * Convert the second operand if necessary. The first operand + * determines the type of the second operand, (See the Data Types + * section of the ACPI specification.) Both object types are + * guaranteed to be either Integer/String/Buffer by the operand + * resolution mechanism. + */ + switch (ACPI_GET_OBJECT_TYPE (operand0)) { + case ACPI_TYPE_INTEGER: + status = acpi_ex_convert_to_integer (operand1, &local_operand1, 16); + break; + + case ACPI_TYPE_STRING: + status = acpi_ex_convert_to_string (operand1, &local_operand1, + ACPI_IMPLICIT_CONVERT_HEX); + break; + + case ACPI_TYPE_BUFFER: + status = acpi_ex_convert_to_buffer (operand1, &local_operand1); + break; + + default: + ACPI_REPORT_ERROR (("Concat - invalid obj type: %X\n", + ACPI_GET_OBJECT_TYPE (operand0))); + status = AE_AML_INTERNAL; + } + + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* + * Both operands are now known to be the same object type + * (Both are Integer, String, or Buffer), and we can now perform the + * concatenation. + */ + + /* + * There are three cases to handle: + * + * 1) Two Integers concatenated to produce a new Buffer + * 2) Two Strings concatenated to produce a new String + * 3) Two Buffers concatenated to produce a new Buffer + */ + switch (ACPI_GET_OBJECT_TYPE (operand0)) { + case ACPI_TYPE_INTEGER: + + /* Result of two Integers is a Buffer */ + /* Need enough buffer space for two integers */ + + return_desc = acpi_ut_create_buffer_object ( + ACPI_MUL_2 (acpi_gbl_integer_byte_width)); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + new_buf = (char *) return_desc->buffer.pointer; + + /* Copy the first integer, LSB first */ + + ACPI_MEMCPY (new_buf, + &operand0->integer.value, + acpi_gbl_integer_byte_width); + + /* Copy the second integer (LSB first) after the first */ + + ACPI_MEMCPY (new_buf + acpi_gbl_integer_byte_width, + &local_operand1->integer.value, + acpi_gbl_integer_byte_width); + break; + + case ACPI_TYPE_STRING: + + /* Result of two Strings is a String */ + + new_length = (acpi_size) operand0->string.length + + (acpi_size) local_operand1->string.length; + if (new_length > ACPI_MAX_STRING_CONVERSION) { + status = AE_AML_STRING_LIMIT; + goto cleanup; + } + + return_desc = acpi_ut_create_string_object (new_length); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + new_buf = return_desc->string.pointer; + + /* Concatenate the strings */ + + ACPI_STRCPY (new_buf, + operand0->string.pointer); + ACPI_STRCPY (new_buf + operand0->string.length, + local_operand1->string.pointer); + break; + + case ACPI_TYPE_BUFFER: + + /* Result of two Buffers is a Buffer */ + + return_desc = acpi_ut_create_buffer_object ( + (acpi_size) operand0->buffer.length + + (acpi_size) local_operand1->buffer.length); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + new_buf = (char *) return_desc->buffer.pointer; + + /* Concatenate the buffers */ + + ACPI_MEMCPY (new_buf, + operand0->buffer.pointer, + operand0->buffer.length); + ACPI_MEMCPY (new_buf + operand0->buffer.length, + local_operand1->buffer.pointer, + local_operand1->buffer.length); + break; + + default: + + /* Invalid object type, should not happen here */ + + ACPI_REPORT_ERROR (("Concatenate - Invalid object type: %X\n", + ACPI_GET_OBJECT_TYPE (operand0))); + status =AE_AML_INTERNAL; + goto cleanup; + } + + *actual_return_desc = return_desc; + +cleanup: + if (local_operand1 != operand1) { + acpi_ut_remove_reference (local_operand1); + } + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_math_op + * + * PARAMETERS: Opcode - AML opcode + * Integer0 - Integer operand #0 + * Integer1 - Integer operand #1 + * + * RETURN: Integer result of the operation + * + * DESCRIPTION: Execute a math AML opcode. The purpose of having all of the + * math functions here is to prevent a lot of pointer dereferencing + * to obtain the operands. + * + ******************************************************************************/ + +acpi_integer +acpi_ex_do_math_op ( + u16 opcode, + acpi_integer integer0, + acpi_integer integer1) +{ + + ACPI_FUNCTION_ENTRY (); + + + switch (opcode) { + case AML_ADD_OP: /* Add (Integer0, Integer1, Result) */ + + return (integer0 + integer1); + + + case AML_BIT_AND_OP: /* And (Integer0, Integer1, Result) */ + + return (integer0 & integer1); + + + case AML_BIT_NAND_OP: /* NAnd (Integer0, Integer1, Result) */ + + return (~(integer0 & integer1)); + + + case AML_BIT_OR_OP: /* Or (Integer0, Integer1, Result) */ + + return (integer0 | integer1); + + + case AML_BIT_NOR_OP: /* NOr (Integer0, Integer1, Result) */ + + return (~(integer0 | integer1)); + + + case AML_BIT_XOR_OP: /* XOr (Integer0, Integer1, Result) */ + + return (integer0 ^ integer1); + + + case AML_MULTIPLY_OP: /* Multiply (Integer0, Integer1, Result) */ + + return (integer0 * integer1); + + + case AML_SHIFT_LEFT_OP: /* shift_left (Operand, shift_count, Result) */ + + return (integer0 << integer1); + + + case AML_SHIFT_RIGHT_OP: /* shift_right (Operand, shift_count, Result) */ + + return (integer0 >> integer1); + + + case AML_SUBTRACT_OP: /* Subtract (Integer0, Integer1, Result) */ + + return (integer0 - integer1); + + default: + + return (0); + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_logical_numeric_op + * + * PARAMETERS: Opcode - AML opcode + * Integer0 - Integer operand #0 + * Integer1 - Integer operand #1 + * logical_result - TRUE/FALSE result of the operation + * + * RETURN: Status + * + * DESCRIPTION: Execute a logical "Numeric" AML opcode. For these Numeric + * operators (LAnd and LOr), both operands must be integers. + * + * Note: cleanest machine code seems to be produced by the code + * below, rather than using statements of the form: + * Result = (Integer0 && Integer1); + * + ******************************************************************************/ + +acpi_status +acpi_ex_do_logical_numeric_op ( + u16 opcode, + acpi_integer integer0, + acpi_integer integer1, + u8 *logical_result) +{ + acpi_status status = AE_OK; + u8 local_result = FALSE; + + + ACPI_FUNCTION_TRACE ("ex_do_logical_numeric_op"); + + + switch (opcode) { + case AML_LAND_OP: /* LAnd (Integer0, Integer1) */ + + if (integer0 && integer1) { + local_result = TRUE; + } + break; + + case AML_LOR_OP: /* LOr (Integer0, Integer1) */ + + if (integer0 || integer1) { + local_result = TRUE; + } + break; + + default: + status = AE_AML_INTERNAL; + break; + } + + /* Return the logical result and status */ + + *logical_result = local_result; + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_logical_op + * + * PARAMETERS: Opcode - AML opcode + * Operand0 - operand #0 + * Operand1 - operand #1 + * logical_result - TRUE/FALSE result of the operation + * + * RETURN: Status + * + * DESCRIPTION: Execute a logical AML opcode. The purpose of having all of the + * functions here is to prevent a lot of pointer dereferencing + * to obtain the operands and to simplify the generation of the + * logical value. For the Numeric operators (LAnd and LOr), both + * operands must be integers. For the other logical operators, + * operands can be any combination of Integer/String/Buffer. The + * first operand determines the type to which the second operand + * will be converted. + * + * Note: cleanest machine code seems to be produced by the code + * below, rather than using statements of the form: + * Result = (Operand0 == Operand1); + * + ******************************************************************************/ + +acpi_status +acpi_ex_do_logical_op ( + u16 opcode, + union acpi_operand_object *operand0, + union acpi_operand_object *operand1, + u8 *logical_result) +{ + union acpi_operand_object *local_operand1 = operand1; + acpi_integer integer0; + acpi_integer integer1; + u32 length0; + u32 length1; + acpi_status status = AE_OK; + u8 local_result = FALSE; + int compare; + + + ACPI_FUNCTION_TRACE ("ex_do_logical_op"); + + + /* + * Convert the second operand if necessary. The first operand + * determines the type of the second operand, (See the Data Types + * section of the ACPI 3.0+ specification.) Both object types are + * guaranteed to be either Integer/String/Buffer by the operand + * resolution mechanism. + */ + switch (ACPI_GET_OBJECT_TYPE (operand0)) { + case ACPI_TYPE_INTEGER: + status = acpi_ex_convert_to_integer (operand1, &local_operand1, 16); + break; + + case ACPI_TYPE_STRING: + status = acpi_ex_convert_to_string (operand1, &local_operand1, + ACPI_IMPLICIT_CONVERT_HEX); + break; + + case ACPI_TYPE_BUFFER: + status = acpi_ex_convert_to_buffer (operand1, &local_operand1); + break; + + default: + status = AE_AML_INTERNAL; + break; + } + + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* + * Two cases: 1) Both Integers, 2) Both Strings or Buffers + */ + if (ACPI_GET_OBJECT_TYPE (operand0) == ACPI_TYPE_INTEGER) { + /* + * 1) Both operands are of type integer + * Note: local_operand1 may have changed above + */ + integer0 = operand0->integer.value; + integer1 = local_operand1->integer.value; + + switch (opcode) { + case AML_LEQUAL_OP: /* LEqual (Operand0, Operand1) */ + + if (integer0 == integer1) { + local_result = TRUE; + } + break; + + case AML_LGREATER_OP: /* LGreater (Operand0, Operand1) */ + + if (integer0 > integer1) { + local_result = TRUE; + } + break; + + case AML_LLESS_OP: /* LLess (Operand0, Operand1) */ + + if (integer0 < integer1) { + local_result = TRUE; + } + break; + + default: + status = AE_AML_INTERNAL; + break; + } + } + else { + /* + * 2) Both operands are Strings or both are Buffers + * Note: Code below takes advantage of common Buffer/String + * object fields. local_operand1 may have changed above. Use + * memcmp to handle nulls in buffers. + */ + length0 = operand0->buffer.length; + length1 = local_operand1->buffer.length; + + /* Lexicographic compare: compare the data bytes */ + + compare = ACPI_MEMCMP ((const char * ) operand0->buffer.pointer, + (const char * ) local_operand1->buffer.pointer, + (length0 > length1) ? length1 : length0); + + switch (opcode) { + case AML_LEQUAL_OP: /* LEqual (Operand0, Operand1) */ + + /* Length and all bytes must be equal */ + + if ((length0 == length1) && + (compare == 0)) { + /* Length and all bytes match ==> TRUE */ + + local_result = TRUE; + } + break; + + case AML_LGREATER_OP: /* LGreater (Operand0, Operand1) */ + + if (compare > 0) { + local_result = TRUE; + goto cleanup; /* TRUE */ + } + if (compare < 0) { + goto cleanup; /* FALSE */ + } + + /* Bytes match (to shortest length), compare lengths */ + + if (length0 > length1) { + local_result = TRUE; + } + break; + + case AML_LLESS_OP: /* LLess (Operand0, Operand1) */ + + if (compare > 0) { + goto cleanup; /* FALSE */ + } + if (compare < 0) { + local_result = TRUE; + goto cleanup; /* TRUE */ + } + + /* Bytes match (to shortest length), compare lengths */ + + if (length0 < length1) { + local_result = TRUE; + } + break; + + default: + status = AE_AML_INTERNAL; + break; + } + } + +cleanup: + + /* New object was created if implicit conversion performed - delete */ + + if (local_operand1 != operand1) { + acpi_ut_remove_reference (local_operand1); + } + + /* Return the logical result and status */ + + *logical_result = local_result; + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exmutex.c b/drivers/acpi/executer/exmutex.c new file mode 100644 index 00000000000..68c4bb1970a --- /dev/null +++ b/drivers/acpi/executer/exmutex.c @@ -0,0 +1,363 @@ + +/****************************************************************************** + * + * Module Name: exmutex - ASL Mutex Acquire/Release functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exmutex") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_unlink_mutex + * + * PARAMETERS: obj_desc - The mutex to be unlinked + * + * RETURN: Status + * + * DESCRIPTION: Remove a mutex from the "acquired_mutex" list + * + ******************************************************************************/ + +void +acpi_ex_unlink_mutex ( + union acpi_operand_object *obj_desc) +{ + struct acpi_thread_state *thread = obj_desc->mutex.owner_thread; + + + if (!thread) { + return; + } + + /* Doubly linked list */ + + if (obj_desc->mutex.next) { + (obj_desc->mutex.next)->mutex.prev = obj_desc->mutex.prev; + } + + if (obj_desc->mutex.prev) { + (obj_desc->mutex.prev)->mutex.next = obj_desc->mutex.next; + } + else { + thread->acquired_mutex_list = obj_desc->mutex.next; + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_link_mutex + * + * PARAMETERS: obj_desc - The mutex to be linked + * list_head - head of the "acquired_mutex" list + * + * RETURN: Status + * + * DESCRIPTION: Add a mutex to the "acquired_mutex" list for this walk + * + ******************************************************************************/ + +void +acpi_ex_link_mutex ( + union acpi_operand_object *obj_desc, + struct acpi_thread_state *thread) +{ + union acpi_operand_object *list_head; + + + list_head = thread->acquired_mutex_list; + + /* This object will be the first object in the list */ + + obj_desc->mutex.prev = NULL; + obj_desc->mutex.next = list_head; + + /* Update old first object to point back to this object */ + + if (list_head) { + list_head->mutex.prev = obj_desc; + } + + /* Update list head */ + + thread->acquired_mutex_list = obj_desc; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_acquire_mutex + * + * PARAMETERS: time_desc - The 'time to delay' object descriptor + * obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Acquire an AML mutex + * + ******************************************************************************/ + +acpi_status +acpi_ex_acquire_mutex ( + union acpi_operand_object *time_desc, + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ex_acquire_mutex", obj_desc); + + + if (!obj_desc) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Sanity check -- we must have a valid thread ID */ + + if (!walk_state->thread) { + ACPI_REPORT_ERROR (("Cannot acquire Mutex [%4.4s], null thread info\n", + acpi_ut_get_node_name (obj_desc->mutex.node))); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* + * Current Sync must be less than or equal to the sync level of the + * mutex. This mechanism provides some deadlock prevention + */ + if (walk_state->thread->current_sync_level > obj_desc->mutex.sync_level) { + ACPI_REPORT_ERROR (("Cannot acquire Mutex [%4.4s], incorrect sync_level\n", + acpi_ut_get_node_name (obj_desc->mutex.node))); + return_ACPI_STATUS (AE_AML_MUTEX_ORDER); + } + + /* Support for multiple acquires by the owning thread */ + + if (obj_desc->mutex.owner_thread) { + /* Special case for Global Lock, allow all threads */ + + if ((obj_desc->mutex.owner_thread->thread_id == walk_state->thread->thread_id) || + (obj_desc->mutex.semaphore == acpi_gbl_global_lock_semaphore)) { + /* + * The mutex is already owned by this thread, + * just increment the acquisition depth + */ + obj_desc->mutex.acquisition_depth++; + return_ACPI_STATUS (AE_OK); + } + } + + /* Acquire the mutex, wait if necessary */ + + status = acpi_ex_system_acquire_mutex (time_desc, obj_desc); + if (ACPI_FAILURE (status)) { + /* Includes failure from a timeout on time_desc */ + + return_ACPI_STATUS (status); + } + + /* Have the mutex: update mutex and walk info and save the sync_level */ + + obj_desc->mutex.owner_thread = walk_state->thread; + obj_desc->mutex.acquisition_depth = 1; + obj_desc->mutex.original_sync_level = walk_state->thread->current_sync_level; + + walk_state->thread->current_sync_level = obj_desc->mutex.sync_level; + + /* Link the mutex to the current thread for force-unlock at method exit */ + + acpi_ex_link_mutex (obj_desc, walk_state->thread); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_release_mutex + * + * PARAMETERS: obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Release a previously acquired Mutex. + * + ******************************************************************************/ + +acpi_status +acpi_ex_release_mutex ( + union acpi_operand_object *obj_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ex_release_mutex"); + + + if (!obj_desc) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* The mutex must have been previously acquired in order to release it */ + + if (!obj_desc->mutex.owner_thread) { + ACPI_REPORT_ERROR (("Cannot release Mutex [%4.4s], not acquired\n", + acpi_ut_get_node_name (obj_desc->mutex.node))); + return_ACPI_STATUS (AE_AML_MUTEX_NOT_ACQUIRED); + } + + /* Sanity check -- we must have a valid thread ID */ + + if (!walk_state->thread) { + ACPI_REPORT_ERROR (("Cannot release Mutex [%4.4s], null thread info\n", + acpi_ut_get_node_name (obj_desc->mutex.node))); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* + * The Mutex is owned, but this thread must be the owner. + * Special case for Global Lock, any thread can release + */ + if ((obj_desc->mutex.owner_thread->thread_id != walk_state->thread->thread_id) && + (obj_desc->mutex.semaphore != acpi_gbl_global_lock_semaphore)) { + ACPI_REPORT_ERROR (( + "Thread %X cannot release Mutex [%4.4s] acquired by thread %X\n", + walk_state->thread->thread_id, + acpi_ut_get_node_name (obj_desc->mutex.node), + obj_desc->mutex.owner_thread->thread_id)); + return_ACPI_STATUS (AE_AML_NOT_OWNER); + } + + /* + * The sync level of the mutex must be less than or + * equal to the current sync level + */ + if (obj_desc->mutex.sync_level > walk_state->thread->current_sync_level) { + ACPI_REPORT_ERROR (("Cannot release Mutex [%4.4s], incorrect sync_level\n", + acpi_ut_get_node_name (obj_desc->mutex.node))); + return_ACPI_STATUS (AE_AML_MUTEX_ORDER); + } + + /* Match multiple Acquires with multiple Releases */ + + obj_desc->mutex.acquisition_depth--; + if (obj_desc->mutex.acquisition_depth != 0) { + /* Just decrement the depth and return */ + + return_ACPI_STATUS (AE_OK); + } + + /* Unlink the mutex from the owner's list */ + + acpi_ex_unlink_mutex (obj_desc); + + /* Release the mutex */ + + status = acpi_ex_system_release_mutex (obj_desc); + + /* Update the mutex and walk state, restore sync_level before acquire */ + + obj_desc->mutex.owner_thread = NULL; + walk_state->thread->current_sync_level = obj_desc->mutex.original_sync_level; + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_release_all_mutexes + * + * PARAMETERS: mutex_list - Head of the mutex list + * + * RETURN: Status + * + * DESCRIPTION: Release all mutexes in the list + * + ******************************************************************************/ + +void +acpi_ex_release_all_mutexes ( + struct acpi_thread_state *thread) +{ + union acpi_operand_object *next = thread->acquired_mutex_list; + union acpi_operand_object *this; + acpi_status status; + + + ACPI_FUNCTION_ENTRY (); + + + /* Traverse the list of owned mutexes, releasing each one */ + + while (next) { + this = next; + next = this->mutex.next; + + this->mutex.acquisition_depth = 1; + this->mutex.prev = NULL; + this->mutex.next = NULL; + + /* Release the mutex */ + + status = acpi_ex_system_release_mutex (this); + if (ACPI_FAILURE (status)) { + continue; + } + + /* Mark mutex unowned */ + + this->mutex.owner_thread = NULL; + + /* Update Thread sync_level (Last mutex is the important one) */ + + thread->current_sync_level = this->mutex.original_sync_level; + } +} + + diff --git a/drivers/acpi/executer/exnames.c b/drivers/acpi/executer/exnames.c new file mode 100644 index 00000000000..7911c533c26 --- /dev/null +++ b/drivers/acpi/executer/exnames.c @@ -0,0 +1,427 @@ + +/****************************************************************************** + * + * Module Name: exnames - interpreter/scanner name load/execute + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exnames") + + +/* AML Package Length encodings */ + +#define ACPI_AML_PACKAGE_TYPE1 0x40 +#define ACPI_AML_PACKAGE_TYPE2 0x4000 +#define ACPI_AML_PACKAGE_TYPE3 0x400000 +#define ACPI_AML_PACKAGE_TYPE4 0x40000000 + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_allocate_name_string + * + * PARAMETERS: prefix_count - Count of parent levels. Special cases: + * (-1) = root, 0 = none + * num_name_segs - count of 4-character name segments + * + * RETURN: A pointer to the allocated string segment. This segment must + * be deleted by the caller. + * + * DESCRIPTION: Allocate a buffer for a name string. Ensure allocated name + * string is long enough, and set up prefix if any. + * + ******************************************************************************/ + +char * +acpi_ex_allocate_name_string ( + u32 prefix_count, + u32 num_name_segs) +{ + char *temp_ptr; + char *name_string; + u32 size_needed; + + ACPI_FUNCTION_TRACE ("ex_allocate_name_string"); + + + /* + * Allow room for all \ and ^ prefixes, all segments, and a multi_name_prefix. + * Also, one byte for the null terminator. + * This may actually be somewhat longer than needed. + */ + if (prefix_count == ACPI_UINT32_MAX) { + /* Special case for root */ + + size_needed = 1 + (ACPI_NAME_SIZE * num_name_segs) + 2 + 1; + } + else { + size_needed = prefix_count + (ACPI_NAME_SIZE * num_name_segs) + 2 + 1; + } + + /* + * Allocate a buffer for the name. + * This buffer must be deleted by the caller! + */ + name_string = ACPI_MEM_ALLOCATE (size_needed); + if (!name_string) { + ACPI_REPORT_ERROR (("ex_allocate_name_string: Could not allocate size %d\n", size_needed)); + return_PTR (NULL); + } + + temp_ptr = name_string; + + /* Set up Root or Parent prefixes if needed */ + + if (prefix_count == ACPI_UINT32_MAX) { + *temp_ptr++ = AML_ROOT_PREFIX; + } + else { + while (prefix_count--) { + *temp_ptr++ = AML_PARENT_PREFIX; + } + } + + + /* Set up Dual or Multi prefixes if needed */ + + if (num_name_segs > 2) { + /* Set up multi prefixes */ + + *temp_ptr++ = AML_MULTI_NAME_PREFIX_OP; + *temp_ptr++ = (char) num_name_segs; + } + else if (2 == num_name_segs) { + /* Set up dual prefixes */ + + *temp_ptr++ = AML_DUAL_NAME_PREFIX; + } + + /* + * Terminate string following prefixes. acpi_ex_name_segment() will + * append the segment(s) + */ + *temp_ptr = 0; + + return_PTR (name_string); +} + +/******************************************************************************* + * + * FUNCTION: acpi_ex_name_segment + * + * PARAMETERS: interpreter_mode - Current running mode (load1/Load2/Exec) + * + * RETURN: Status + * + * DESCRIPTION: Execute a name segment (4 bytes) + * + ******************************************************************************/ + +acpi_status +acpi_ex_name_segment ( + u8 **in_aml_address, + char *name_string) +{ + char *aml_address = (void *) *in_aml_address; + acpi_status status = AE_OK; + u32 index; + char char_buf[5]; + + + ACPI_FUNCTION_TRACE ("ex_name_segment"); + + + /* + * If first character is a digit, then we know that we aren't looking at a + * valid name segment + */ + char_buf[0] = *aml_address; + + if ('0' <= char_buf[0] && char_buf[0] <= '9') { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "leading digit: %c\n", char_buf[0])); + return_ACPI_STATUS (AE_CTRL_PENDING); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "Bytes from stream:\n")); + + for (index = 0; + (index < ACPI_NAME_SIZE) && (acpi_ut_valid_acpi_character (*aml_address)); + index++) { + char_buf[index] = *aml_address++; + ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "%c\n", char_buf[index])); + } + + + /* Valid name segment */ + + if (index == 4) { + /* Found 4 valid characters */ + + char_buf[4] = '\0'; + + if (name_string) { + ACPI_STRCAT (name_string, char_buf); + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Appended to - %s \n", name_string)); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "No Name string - %s \n", char_buf)); + } + } + else if (index == 0) { + /* + * First character was not a valid name character, + * so we are looking at something other than a name. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Leading character is not alpha: %02Xh (not a name)\n", + char_buf[0])); + status = AE_CTRL_PENDING; + } + else { + /* Segment started with one or more valid characters, but fewer than 4 */ + + status = AE_AML_BAD_NAME; + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Bad character %02x in name, at %p\n", + *aml_address, aml_address)); + } + + *in_aml_address = (u8 *) aml_address; + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_get_name_string + * + * PARAMETERS: data_type - Data type to be associated with this name + * + * RETURN: Status + * + * DESCRIPTION: Get a name, including any prefixes. + * + ******************************************************************************/ + +acpi_status +acpi_ex_get_name_string ( + acpi_object_type data_type, + u8 *in_aml_address, + char **out_name_string, + u32 *out_name_length) +{ + acpi_status status = AE_OK; + u8 *aml_address = in_aml_address; + char *name_string = NULL; + u32 num_segments; + u32 prefix_count = 0; + u8 has_prefix = FALSE; + + + ACPI_FUNCTION_TRACE_PTR ("ex_get_name_string", aml_address); + + + if (ACPI_TYPE_LOCAL_REGION_FIELD == data_type || + ACPI_TYPE_LOCAL_BANK_FIELD == data_type || + ACPI_TYPE_LOCAL_INDEX_FIELD == data_type) { + /* Disallow prefixes for types associated with field_unit names */ + + name_string = acpi_ex_allocate_name_string (0, 1); + if (!name_string) { + status = AE_NO_MEMORY; + } + else { + status = acpi_ex_name_segment (&aml_address, name_string); + } + } + else { + /* + * data_type is not a field name. + * Examine first character of name for root or parent prefix operators + */ + switch (*aml_address) { + case AML_ROOT_PREFIX: + + ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "root_prefix(\\) at %p\n", aml_address)); + + /* + * Remember that we have a root_prefix -- + * see comment in acpi_ex_allocate_name_string() + */ + aml_address++; + prefix_count = ACPI_UINT32_MAX; + has_prefix = TRUE; + break; + + + case AML_PARENT_PREFIX: + + /* Increment past possibly multiple parent prefixes */ + + do { + ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "parent_prefix (^) at %p\n", aml_address)); + + aml_address++; + prefix_count++; + + } while (*aml_address == AML_PARENT_PREFIX); + + has_prefix = TRUE; + break; + + + default: + + /* Not a prefix character */ + + break; + } + + + /* Examine first character of name for name segment prefix operator */ + + switch (*aml_address) { + case AML_DUAL_NAME_PREFIX: + + ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "dual_name_prefix at %p\n", aml_address)); + + aml_address++; + name_string = acpi_ex_allocate_name_string (prefix_count, 2); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + /* Indicate that we processed a prefix */ + + has_prefix = TRUE; + + status = acpi_ex_name_segment (&aml_address, name_string); + if (ACPI_SUCCESS (status)) { + status = acpi_ex_name_segment (&aml_address, name_string); + } + break; + + + case AML_MULTI_NAME_PREFIX_OP: + + ACPI_DEBUG_PRINT ((ACPI_DB_LOAD, "multi_name_prefix at %p\n", aml_address)); + + /* Fetch count of segments remaining in name path */ + + aml_address++; + num_segments = *aml_address; + + name_string = acpi_ex_allocate_name_string (prefix_count, num_segments); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + /* Indicate that we processed a prefix */ + + aml_address++; + has_prefix = TRUE; + + while (num_segments && + (status = acpi_ex_name_segment (&aml_address, name_string)) == AE_OK) { + num_segments--; + } + + break; + + + case 0: + + /* null_name valid as of 8-12-98 ASL/AML Grammar Update */ + + if (prefix_count == ACPI_UINT32_MAX) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "name_seg is \"\\\" followed by NULL\n")); + } + + /* Consume the NULL byte */ + + aml_address++; + name_string = acpi_ex_allocate_name_string (prefix_count, 0); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + break; + + + default: + + /* Name segment string */ + + name_string = acpi_ex_allocate_name_string (prefix_count, 1); + if (!name_string) { + status = AE_NO_MEMORY; + break; + } + + status = acpi_ex_name_segment (&aml_address, name_string); + break; + } + } + + if (AE_CTRL_PENDING == status && has_prefix) { + /* Ran out of segments after processing a prefix */ + + ACPI_REPORT_ERROR ( + ("ex_do_name: Malformed Name at %p\n", name_string)); + status = AE_AML_BAD_NAME; + } + + *out_name_string = name_string; + *out_name_length = (u32) (aml_address - in_aml_address); + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exoparg1.c b/drivers/acpi/executer/exoparg1.c new file mode 100644 index 00000000000..8482aefaf38 --- /dev/null +++ b/drivers/acpi/executer/exoparg1.c @@ -0,0 +1,1013 @@ + +/****************************************************************************** + * + * Module Name: exoparg1 - AML execution - opcodes with 1 argument + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exoparg1") + + +/*! + * Naming convention for AML interpreter execution routines. + * + * The routines that begin execution of AML opcodes are named with a common + * convention based upon the number of arguments, the number of target operands, + * and whether or not a value is returned: + * + * AcpiExOpcode_xA_yT_zR + * + * Where: + * + * xA - ARGUMENTS: The number of arguments (input operands) that are + * required for this opcode type (0 through 6 args). + * yT - TARGETS: The number of targets (output operands) that are required + * for this opcode type (0, 1, or 2 targets). + * zR - RETURN VALUE: Indicates whether this opcode type returns a value + * as the function return (0 or 1). + * + * The AcpiExOpcode* functions are called via the Dispatcher component with + * fully resolved operands. +!*/ + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_0A_0T_1R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute operator with no operands, one return value + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_0A_0T_1R ( + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *return_desc = NULL; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_0A_0T_1R", acpi_ps_get_opcode_name (walk_state->opcode)); + + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_TIMER_OP: /* Timer () */ + + /* Create a return object of type Integer */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + return_desc->integer.value = acpi_os_get_timer (); + break; + + default: /* Unknown opcode */ + + ACPI_REPORT_ERROR (("acpi_ex_opcode_0A_0T_1R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + break; + } + +cleanup: + + if (!walk_state->result_obj) { + walk_state->result_obj = return_desc; + } + + /* Delete return object on error */ + + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (return_desc); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_0T_0R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute Type 1 monadic operator with numeric operand on + * object stack + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_1A_0T_0R ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_1A_0T_0R", acpi_ps_get_opcode_name (walk_state->opcode)); + + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_RELEASE_OP: /* Release (mutex_object) */ + + status = acpi_ex_release_mutex (operand[0], walk_state); + break; + + + case AML_RESET_OP: /* Reset (event_object) */ + + status = acpi_ex_system_reset_event (operand[0]); + break; + + + case AML_SIGNAL_OP: /* Signal (event_object) */ + + status = acpi_ex_system_signal_event (operand[0]); + break; + + + case AML_SLEEP_OP: /* Sleep (msec_time) */ + + status = acpi_ex_system_do_suspend (operand[0]->integer.value); + break; + + + case AML_STALL_OP: /* Stall (usec_time) */ + + status = acpi_ex_system_do_stall ((u32) operand[0]->integer.value); + break; + + + case AML_UNLOAD_OP: /* Unload (Handle) */ + + status = acpi_ex_unload_table (operand[0]); + break; + + + default: /* Unknown opcode */ + + ACPI_REPORT_ERROR (("acpi_ex_opcode_1A_0T_0R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + break; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_1T_0R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with one argument, one target, and no + * return value. + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_1A_1T_0R ( + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object **operand = &walk_state->operands[0]; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_1A_1T_0R", acpi_ps_get_opcode_name (walk_state->opcode)); + + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_LOAD_OP: + + status = acpi_ex_load_op (operand[0], operand[1], walk_state); + break; + + default: /* Unknown opcode */ + + ACPI_REPORT_ERROR (("acpi_ex_opcode_1A_1T_0R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + +cleanup: + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_1T_1R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with one argument, one target, and a + * return value. + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_1A_1T_1R ( + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + union acpi_operand_object *return_desc2 = NULL; + u32 temp32; + u32 i; + acpi_integer power_of_ten; + acpi_integer digit; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_1A_1T_1R", acpi_ps_get_opcode_name (walk_state->opcode)); + + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_BIT_NOT_OP: + case AML_FIND_SET_LEFT_BIT_OP: + case AML_FIND_SET_RIGHT_BIT_OP: + case AML_FROM_BCD_OP: + case AML_TO_BCD_OP: + case AML_COND_REF_OF_OP: + + /* Create a return object of type Integer for these opcodes */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + switch (walk_state->opcode) { + case AML_BIT_NOT_OP: /* Not (Operand, Result) */ + + return_desc->integer.value = ~operand[0]->integer.value; + break; + + + case AML_FIND_SET_LEFT_BIT_OP: /* find_set_left_bit (Operand, Result) */ + + return_desc->integer.value = operand[0]->integer.value; + + /* + * Acpi specification describes Integer type as a little + * endian unsigned value, so this boundary condition is valid. + */ + for (temp32 = 0; return_desc->integer.value && + temp32 < ACPI_INTEGER_BIT_SIZE; ++temp32) { + return_desc->integer.value >>= 1; + } + + return_desc->integer.value = temp32; + break; + + + case AML_FIND_SET_RIGHT_BIT_OP: /* find_set_right_bit (Operand, Result) */ + + return_desc->integer.value = operand[0]->integer.value; + + /* + * The Acpi specification describes Integer type as a little + * endian unsigned value, so this boundary condition is valid. + */ + for (temp32 = 0; return_desc->integer.value && + temp32 < ACPI_INTEGER_BIT_SIZE; ++temp32) { + return_desc->integer.value <<= 1; + } + + /* Since the bit position is one-based, subtract from 33 (65) */ + + return_desc->integer.value = temp32 == 0 ? 0 : + (ACPI_INTEGER_BIT_SIZE + 1) - temp32; + break; + + + case AML_FROM_BCD_OP: /* from_bcd (BCDValue, Result) */ + + /* + * The 64-bit ACPI integer can hold 16 4-bit BCD characters + * (if table is 32-bit, integer can hold 8 BCD characters) + * Convert each 4-bit BCD value + */ + power_of_ten = 1; + return_desc->integer.value = 0; + digit = operand[0]->integer.value; + + /* Convert each BCD digit (each is one nybble wide) */ + + for (i = 0; (i < acpi_gbl_integer_nybble_width) && (digit > 0); i++) { + /* Get the least significant 4-bit BCD digit */ + + temp32 = ((u32) digit) & 0xF; + + /* Check the range of the digit */ + + if (temp32 > 9) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "BCD digit too large (not decimal): 0x%X\n", + temp32)); + + status = AE_AML_NUMERIC_OVERFLOW; + goto cleanup; + } + + /* Sum the digit into the result with the current power of 10 */ + + return_desc->integer.value += (((acpi_integer) temp32) * + power_of_ten); + + /* Shift to next BCD digit */ + + digit >>= 4; + + /* Next power of 10 */ + + power_of_ten *= 10; + } + break; + + + case AML_TO_BCD_OP: /* to_bcd (Operand, Result) */ + + return_desc->integer.value = 0; + digit = operand[0]->integer.value; + + /* Each BCD digit is one nybble wide */ + + for (i = 0; (i < acpi_gbl_integer_nybble_width) && (digit > 0); i++) { + (void) acpi_ut_short_divide (digit, 10, &digit, &temp32); + + /* Insert the BCD digit that resides in the remainder from above */ + + return_desc->integer.value |= (((acpi_integer) temp32) << + ACPI_MUL_4 (i)); + } + + /* Overflow if there is any data left in Digit */ + + if (digit > 0) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Integer too large to convert to BCD: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (operand[0]->integer.value))); + status = AE_AML_NUMERIC_OVERFLOW; + goto cleanup; + } + break; + + + case AML_COND_REF_OF_OP: /* cond_ref_of (source_object, Result) */ + + /* + * This op is a little strange because the internal return value is + * different than the return value stored in the result descriptor + * (There are really two return values) + */ + if ((struct acpi_namespace_node *) operand[0] == acpi_gbl_root_node) { + /* + * This means that the object does not exist in the namespace, + * return FALSE + */ + return_desc->integer.value = 0; + goto cleanup; + } + + /* Get the object reference, store it, and remove our reference */ + + status = acpi_ex_get_object_reference (operand[0], &return_desc2, walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_ex_store (return_desc2, operand[1], walk_state); + acpi_ut_remove_reference (return_desc2); + + /* The object exists in the namespace, return TRUE */ + + return_desc->integer.value = ACPI_INTEGER_MAX; + goto cleanup; + + + default: + /* No other opcodes get here */ + break; + } + break; + + + case AML_STORE_OP: /* Store (Source, Target) */ + + /* + * A store operand is typically a number, string, buffer or lvalue + * Be careful about deleting the source object, + * since the object itself may have been stored. + */ + status = acpi_ex_store (operand[0], operand[1], walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* It is possible that the Store already produced a return object */ + + if (!walk_state->result_obj) { + /* + * Normally, we would remove a reference on the Operand[0] parameter; + * But since it is being used as the internal return object + * (meaning we would normally increment it), the two cancel out, + * and we simply don't do anything. + */ + walk_state->result_obj = operand[0]; + walk_state->operands[0] = NULL; /* Prevent deletion */ + } + return_ACPI_STATUS (status); + + + /* + * ACPI 2.0 Opcodes + */ + case AML_COPY_OP: /* Copy (Source, Target) */ + + status = acpi_ut_copy_iobject_to_iobject (operand[0], &return_desc, + walk_state); + break; + + + case AML_TO_DECSTRING_OP: /* to_decimal_string (Data, Result) */ + + status = acpi_ex_convert_to_string (operand[0], &return_desc, + ACPI_EXPLICIT_CONVERT_DECIMAL); + if (return_desc == operand[0]) { + /* No conversion performed, add ref to handle return value */ + acpi_ut_add_reference (return_desc); + } + break; + + + case AML_TO_HEXSTRING_OP: /* to_hex_string (Data, Result) */ + + status = acpi_ex_convert_to_string (operand[0], &return_desc, + ACPI_EXPLICIT_CONVERT_HEX); + if (return_desc == operand[0]) { + /* No conversion performed, add ref to handle return value */ + acpi_ut_add_reference (return_desc); + } + break; + + + case AML_TO_BUFFER_OP: /* to_buffer (Data, Result) */ + + status = acpi_ex_convert_to_buffer (operand[0], &return_desc); + if (return_desc == operand[0]) { + /* No conversion performed, add ref to handle return value */ + acpi_ut_add_reference (return_desc); + } + break; + + + case AML_TO_INTEGER_OP: /* to_integer (Data, Result) */ + + status = acpi_ex_convert_to_integer (operand[0], &return_desc, + ACPI_ANY_BASE); + if (return_desc == operand[0]) { + /* No conversion performed, add ref to handle return value */ + acpi_ut_add_reference (return_desc); + } + break; + + + case AML_SHIFT_LEFT_BIT_OP: /* shift_left_bit (Source, bit_num) */ + case AML_SHIFT_RIGHT_BIT_OP: /* shift_right_bit (Source, bit_num) */ + + /* + * These are two obsolete opcodes + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "%s is obsolete and not implemented\n", + acpi_ps_get_opcode_name (walk_state->opcode))); + status = AE_SUPPORT; + goto cleanup; + + + default: /* Unknown opcode */ + + ACPI_REPORT_ERROR (("acpi_ex_opcode_1A_1T_1R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + if (ACPI_SUCCESS (status)) { + /* + * Store the return value computed above into the target object + */ + status = acpi_ex_store (return_desc, operand[1], walk_state); + } + + +cleanup: + + if (!walk_state->result_obj) { + walk_state->result_obj = return_desc; + } + + /* Delete return object on error */ + + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (return_desc); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_1A_0T_1R + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with one argument, no target, and a return value + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_1A_0T_1R ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *temp_desc; + union acpi_operand_object *return_desc = NULL; + acpi_status status = AE_OK; + u32 type; + acpi_integer value; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_1A_0T_1R", acpi_ps_get_opcode_name (walk_state->opcode)); + + + /* Examine the AML opcode */ + + switch (walk_state->opcode) { + case AML_LNOT_OP: /* LNot (Operand) */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Set result to ONES (TRUE) if Value == 0. Note: + * return_desc->Integer.Value is initially == 0 (FALSE) from above. + */ + if (!operand[0]->integer.value) { + return_desc->integer.value = ACPI_INTEGER_MAX; + } + break; + + + case AML_DECREMENT_OP: /* Decrement (Operand) */ + case AML_INCREMENT_OP: /* Increment (Operand) */ + + /* + * Create a new integer. Can't just get the base integer and + * increment it because it may be an Arg or Field. + */ + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Since we are expecting a Reference operand, it can be either a + * NS Node or an internal object. + */ + temp_desc = operand[0]; + if (ACPI_GET_DESCRIPTOR_TYPE (temp_desc) == ACPI_DESC_TYPE_OPERAND) { + /* Internal reference object - prevent deletion */ + + acpi_ut_add_reference (temp_desc); + } + + /* + * Convert the Reference operand to an Integer (This removes a + * reference on the Operand[0] object) + * + * NOTE: We use LNOT_OP here in order to force resolution of the + * reference operand to an actual integer. + */ + status = acpi_ex_resolve_operands (AML_LNOT_OP, &temp_desc, walk_state); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "%s: bad operand(s) %s\n", + acpi_ps_get_opcode_name (walk_state->opcode), + acpi_format_exception(status))); + + goto cleanup; + } + + /* + * temp_desc is now guaranteed to be an Integer object -- + * Perform the actual increment or decrement + */ + if (walk_state->opcode == AML_INCREMENT_OP) { + return_desc->integer.value = temp_desc->integer.value +1; + } + else { + return_desc->integer.value = temp_desc->integer.value -1; + } + + /* Finished with this Integer object */ + + acpi_ut_remove_reference (temp_desc); + + /* + * Store the result back (indirectly) through the original + * Reference object + */ + status = acpi_ex_store (return_desc, operand[0], walk_state); + break; + + + case AML_TYPE_OP: /* object_type (source_object) */ + + /* + * Note: The operand is not resolved at this point because we want to + * get the associated object, not its value. For example, we don't want + * to resolve a field_unit to its value, we want the actual field_unit + * object. + */ + + /* Get the type of the base object */ + + status = acpi_ex_resolve_multiple (walk_state, operand[0], &type, NULL); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + /* Allocate a descriptor to hold the type. */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + return_desc->integer.value = type; + break; + + + case AML_SIZE_OF_OP: /* size_of (source_object) */ + + /* + * Note: The operand is not resolved at this point because we want to + * get the associated object, not its value. + */ + + /* Get the base object */ + + status = acpi_ex_resolve_multiple (walk_state, operand[0], &type, &temp_desc); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* + * The type of the base object must be integer, buffer, string, or + * package. All others are not supported. + * + * NOTE: Integer is not specifically supported by the ACPI spec, + * but is supported implicitly via implicit operand conversion. + * rather than bother with conversion, we just use the byte width + * global (4 or 8 bytes). + */ + switch (type) { + case ACPI_TYPE_INTEGER: + value = acpi_gbl_integer_byte_width; + break; + + case ACPI_TYPE_BUFFER: + value = temp_desc->buffer.length; + break; + + case ACPI_TYPE_STRING: + value = temp_desc->string.length; + break; + + case ACPI_TYPE_PACKAGE: + value = temp_desc->package.count; + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "size_of - Operand is not Buf/Int/Str/Pkg - found type %s\n", + acpi_ut_get_type_name (type))); + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + + /* + * Now that we have the size of the object, create a result + * object to hold the value + */ + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + return_desc->integer.value = value; + break; + + + case AML_REF_OF_OP: /* ref_of (source_object) */ + + status = acpi_ex_get_object_reference (operand[0], &return_desc, walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + break; + + + case AML_DEREF_OF_OP: /* deref_of (obj_reference | String) */ + + /* Check for a method local or argument, or standalone String */ + + if (ACPI_GET_DESCRIPTOR_TYPE (operand[0]) != ACPI_DESC_TYPE_NAMED) { + switch (ACPI_GET_OBJECT_TYPE (operand[0])) { + case ACPI_TYPE_LOCAL_REFERENCE: + /* + * This is a deref_of (local_x | arg_x) + * + * Must resolve/dereference the local/arg reference first + */ + switch (operand[0]->reference.opcode) { + case AML_LOCAL_OP: + case AML_ARG_OP: + + /* Set Operand[0] to the value of the local/arg */ + + status = acpi_ds_method_data_get_value (operand[0]->reference.opcode, + operand[0]->reference.offset, walk_state, &temp_desc); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* + * Delete our reference to the input object and + * point to the object just retrieved + */ + acpi_ut_remove_reference (operand[0]); + operand[0] = temp_desc; + break; + + case AML_REF_OF_OP: + + /* Get the object to which the reference refers */ + + temp_desc = operand[0]->reference.object; + acpi_ut_remove_reference (operand[0]); + operand[0] = temp_desc; + break; + + default: + + /* Must be an Index op - handled below */ + break; + } + break; + + + case ACPI_TYPE_STRING: + + /* + * This is a deref_of (String). The string is a reference to a named ACPI object. + * + * 1) Find the owning Node + * 2) Dereference the node to an actual object. Could be a Field, so we nee + * to resolve the node to a value. + */ + status = acpi_ns_get_node_by_path (operand[0]->string.pointer, + walk_state->scope_info->scope.node, ACPI_NS_SEARCH_PARENT, + ACPI_CAST_INDIRECT_PTR (struct acpi_namespace_node, &return_desc)); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_ex_resolve_node_to_value ( + ACPI_CAST_INDIRECT_PTR (struct acpi_namespace_node, &return_desc), walk_state); + goto cleanup; + + + default: + + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + } + + /* Operand[0] may have changed from the code above */ + + if (ACPI_GET_DESCRIPTOR_TYPE (operand[0]) == ACPI_DESC_TYPE_NAMED) { + /* + * This is a deref_of (object_reference) + * Get the actual object from the Node (This is the dereference). + * -- This case may only happen when a local_x or arg_x is dereferenced above. + */ + return_desc = acpi_ns_get_attached_object ((struct acpi_namespace_node *) operand[0]); + } + else { + /* + * This must be a reference object produced by either the Index() or + * ref_of() operator + */ + switch (operand[0]->reference.opcode) { + case AML_INDEX_OP: + + /* + * The target type for the Index operator must be + * either a Buffer or a Package + */ + switch (operand[0]->reference.target_type) { + case ACPI_TYPE_BUFFER_FIELD: + + temp_desc = operand[0]->reference.object; + + /* + * Create a new object that contains one element of the + * buffer -- the element pointed to by the index. + * + * NOTE: index into a buffer is NOT a pointer to a + * sub-buffer of the main buffer, it is only a pointer to a + * single element (byte) of the buffer! + */ + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Since we are returning the value of the buffer at the + * indexed location, we don't need to add an additional + * reference to the buffer itself. + */ + return_desc->integer.value = + temp_desc->buffer.pointer[operand[0]->reference.offset]; + break; + + + case ACPI_TYPE_PACKAGE: + + /* + * Return the referenced element of the package. We must add + * another reference to the referenced object, however. + */ + return_desc = *(operand[0]->reference.where); + if (!return_desc) { + /* + * We can't return a NULL dereferenced value. This is + * an uninitialized package element and is thus a + * severe error. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "NULL package element obj %p\n", + operand[0])); + status = AE_AML_UNINITIALIZED_ELEMENT; + goto cleanup; + } + + acpi_ut_add_reference (return_desc); + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unknown Index target_type %X in obj %p\n", + operand[0]->reference.target_type, operand[0])); + status = AE_AML_OPERAND_TYPE; + goto cleanup; + } + break; + + + case AML_REF_OF_OP: + + return_desc = operand[0]->reference.object; + + if (ACPI_GET_DESCRIPTOR_TYPE (return_desc) == ACPI_DESC_TYPE_NAMED) { + + return_desc = acpi_ns_get_attached_object ((struct acpi_namespace_node *) return_desc); + } + + /* Add another reference to the object! */ + + acpi_ut_add_reference (return_desc); + break; + + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unknown opcode in ref(%p) - %X\n", + operand[0], operand[0]->reference.opcode)); + + status = AE_TYPE; + goto cleanup; + } + } + break; + + + default: + + ACPI_REPORT_ERROR (("acpi_ex_opcode_1A_0T_1R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (return_desc); + } + + walk_state->result_obj = return_desc; + return_ACPI_STATUS (status); +} + diff --git a/drivers/acpi/executer/exoparg2.c b/drivers/acpi/executer/exoparg2.c new file mode 100644 index 00000000000..8be4d80ceed --- /dev/null +++ b/drivers/acpi/executer/exoparg2.c @@ -0,0 +1,608 @@ +/****************************************************************************** + * + * Module Name: exoparg2 - AML execution - opcodes with 2 arguments + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/acinterp.h> +#include <acpi/acevents.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exoparg2") + + +/*! + * Naming convention for AML interpreter execution routines. + * + * The routines that begin execution of AML opcodes are named with a common + * convention based upon the number of arguments, the number of target operands, + * and whether or not a value is returned: + * + * AcpiExOpcode_xA_yT_zR + * + * Where: + * + * xA - ARGUMENTS: The number of arguments (input operands) that are + * required for this opcode type (1 through 6 args). + * yT - TARGETS: The number of targets (output operands) that are required + * for this opcode type (0, 1, or 2 targets). + * zR - RETURN VALUE: Indicates whether this opcode type returns a value + * as the function return (0 or 1). + * + * The AcpiExOpcode* functions are called via the Dispatcher component with + * fully resolved operands. +!*/ + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_2A_0T_0R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with two arguments, no target, and no return + * value. + * + * ALLOCATION: Deletes both operands + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_2A_0T_0R ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + struct acpi_namespace_node *node; + u32 value; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_2A_0T_0R", + acpi_ps_get_opcode_name (walk_state->opcode)); + + + /* Examine the opcode */ + + switch (walk_state->opcode) { + case AML_NOTIFY_OP: /* Notify (notify_object, notify_value) */ + + /* The first operand is a namespace node */ + + node = (struct acpi_namespace_node *) operand[0]; + + /* Second value is the notify value */ + + value = (u32) operand[1]->integer.value; + + /* Notifies allowed on this object? */ + + if (!acpi_ev_is_notify_object (node)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unexpected notify object type [%s]\n", + acpi_ut_get_type_name (node->type))); + + status = AE_AML_OPERAND_TYPE; + break; + } + +#ifdef ACPI_GPE_NOTIFY_CHECK + /* + * GPE method wake/notify check. Here, we want to ensure that we + * don't receive any "device_wake" Notifies from a GPE _Lxx or _Exx + * GPE method during system runtime. If we do, the GPE is marked + * as "wake-only" and disabled. + * + * 1) Is the Notify() value == device_wake? + * 2) Is this a GPE deferred method? (An _Lxx or _Exx method) + * 3) Did the original GPE happen at system runtime? + * (versus during wake) + * + * If all three cases are true, this is a wake-only GPE that should + * be disabled at runtime. + */ + if (value == 2) /* device_wake */ { + status = acpi_ev_check_for_wake_only_gpe (walk_state->gpe_event_info); + if (ACPI_FAILURE (status)) { + /* AE_WAKE_ONLY_GPE only error, means ignore this notify */ + + return_ACPI_STATUS (AE_OK) + } + } +#endif + + /* + * Dispatch the notify to the appropriate handler + * NOTE: the request is queued for execution after this method + * completes. The notify handlers are NOT invoked synchronously + * from this thread -- because handlers may in turn run other + * control methods. + */ + status = acpi_ev_queue_notify_request (node, value); + break; + + + default: + + ACPI_REPORT_ERROR (("acpi_ex_opcode_2A_0T_0R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_2A_2T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute a dyadic operator (2 operands) with 2 output targets + * and one implicit return value. + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_2A_2T_1R ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc1 = NULL; + union acpi_operand_object *return_desc2 = NULL; + acpi_status status; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_2A_2T_1R", + acpi_ps_get_opcode_name (walk_state->opcode)); + + + /* + * Execute the opcode + */ + switch (walk_state->opcode) { + case AML_DIVIDE_OP: /* Divide (Dividend, Divisor, remainder_result quotient_result) */ + + return_desc1 = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc1) { + status = AE_NO_MEMORY; + goto cleanup; + } + + return_desc2 = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc2) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Quotient to return_desc1, remainder to return_desc2 */ + + status = acpi_ut_divide (operand[0]->integer.value, + operand[1]->integer.value, + &return_desc1->integer.value, + &return_desc2->integer.value); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + break; + + + default: + + ACPI_REPORT_ERROR (("acpi_ex_opcode_2A_2T_1R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + + /* Store the results to the target reference operands */ + + status = acpi_ex_store (return_desc2, operand[2], walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_ex_store (return_desc1, operand[3], walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Return the remainder */ + + walk_state->result_obj = return_desc1; + + +cleanup: + /* + * Since the remainder is not returned indirectly, remove a reference to + * it. Only the quotient is returned indirectly. + */ + acpi_ut_remove_reference (return_desc2); + + if (ACPI_FAILURE (status)) { + /* Delete the return object */ + + acpi_ut_remove_reference (return_desc1); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_2A_1T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with two arguments, one target, and a return + * value. + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_2A_1T_1R ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + u32 index; + acpi_status status = AE_OK; + acpi_size length; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_2A_1T_1R", + acpi_ps_get_opcode_name (walk_state->opcode)); + + + /* + * Execute the opcode + */ + if (walk_state->op_info->flags & AML_MATH) { + /* All simple math opcodes (add, etc.) */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + return_desc->integer.value = acpi_ex_do_math_op (walk_state->opcode, + operand[0]->integer.value, + operand[1]->integer.value); + goto store_result_to_target; + } + + + switch (walk_state->opcode) { + case AML_MOD_OP: /* Mod (Dividend, Divisor, remainder_result (ACPI 2.0) */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* return_desc will contain the remainder */ + + status = acpi_ut_divide (operand[0]->integer.value, + operand[1]->integer.value, + NULL, + &return_desc->integer.value); + break; + + + case AML_CONCAT_OP: /* Concatenate (Data1, Data2, Result) */ + + status = acpi_ex_do_concatenate (operand[0], operand[1], + &return_desc, walk_state); + break; + + + case AML_TO_STRING_OP: /* to_string (Buffer, Length, Result) (ACPI 2.0) */ + + /* + * Input object is guaranteed to be a buffer at this point (it may have + * been converted.) Copy the raw buffer data to a new object of type String. + */ + + /* + * Get the length of the new string. It is the smallest of: + * 1) Length of the input buffer + * 2) Max length as specified in the to_string operator + * 3) Length of input buffer up to a zero byte (null terminator) + * + * NOTE: A length of zero is ok, and will create a zero-length, null + * terminated string. + */ + length = 0; + while ((length < operand[0]->buffer.length) && + (length < operand[1]->integer.value) && + (operand[0]->buffer.pointer[length])) { + length++; + if (length > ACPI_MAX_STRING_CONVERSION) { + status = AE_AML_STRING_LIMIT; + goto cleanup; + } + } + + /* Allocate a new string object */ + + return_desc = acpi_ut_create_string_object (length); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Copy the raw buffer data with no transform. NULL terminated already. */ + + ACPI_MEMCPY (return_desc->string.pointer, + operand[0]->buffer.pointer, length); + break; + + + case AML_CONCAT_RES_OP: /* concatenate_res_template (Buffer, Buffer, Result) (ACPI 2.0) */ + + status = acpi_ex_concat_template (operand[0], operand[1], + &return_desc, walk_state); + break; + + + case AML_INDEX_OP: /* Index (Source Index Result) */ + + /* Create the internal return object */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_LOCAL_REFERENCE); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + index = (u32) operand[1]->integer.value; + + /* + * At this point, the Source operand is a Package, Buffer, or String + */ + if (ACPI_GET_OBJECT_TYPE (operand[0]) == ACPI_TYPE_PACKAGE) { + /* Object to be indexed is a Package */ + + if (index >= operand[0]->package.count) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Index value (%X) beyond package end (%X)\n", + index, operand[0]->package.count)); + status = AE_AML_PACKAGE_LIMIT; + goto cleanup; + } + + return_desc->reference.target_type = ACPI_TYPE_PACKAGE; + return_desc->reference.object = operand[0]; + return_desc->reference.where = &operand[0]->package.elements [index]; + } + else { + /* Object to be indexed is a Buffer/String */ + + if (index >= operand[0]->buffer.length) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Index value (%X) beyond end of buffer (%X)\n", + index, operand[0]->buffer.length)); + status = AE_AML_BUFFER_LIMIT; + goto cleanup; + } + + return_desc->reference.target_type = ACPI_TYPE_BUFFER_FIELD; + return_desc->reference.object = operand[0]; + } + + /* + * Add a reference to the target package/buffer/string for the life + * of the index. + */ + acpi_ut_add_reference (operand[0]); + + /* Complete the Index reference object */ + + return_desc->reference.opcode = AML_INDEX_OP; + return_desc->reference.offset = index; + + /* Store the reference to the Target */ + + status = acpi_ex_store (return_desc, operand[2], walk_state); + + /* Return the reference */ + + walk_state->result_obj = return_desc; + goto cleanup; + + + default: + + ACPI_REPORT_ERROR (("acpi_ex_opcode_2A_1T_1R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + break; + } + + +store_result_to_target: + + if (ACPI_SUCCESS (status)) { + /* + * Store the result of the operation (which is now in return_desc) into + * the Target descriptor. + */ + status = acpi_ex_store (return_desc, operand[2], walk_state); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + if (!walk_state->result_obj) { + walk_state->result_obj = return_desc; + } + } + + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (return_desc); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_2A_0T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with 2 arguments, no target, and a return value + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_2A_0T_1R ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + acpi_status status = AE_OK; + u8 logical_result = FALSE; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_2A_0T_1R", + acpi_ps_get_opcode_name (walk_state->opcode)); + + + /* Create the internal return object */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* + * Execute the Opcode + */ + if (walk_state->op_info->flags & AML_LOGICAL_NUMERIC) /* logical_op (Operand0, Operand1) */ { + status = acpi_ex_do_logical_numeric_op (walk_state->opcode, + operand[0]->integer.value, operand[1]->integer.value, + &logical_result); + goto store_logical_result; + } + else if (walk_state->op_info->flags & AML_LOGICAL) /* logical_op (Operand0, Operand1) */ { + status = acpi_ex_do_logical_op (walk_state->opcode, operand[0], + operand[1], &logical_result); + goto store_logical_result; + } + + + switch (walk_state->opcode) { + case AML_ACQUIRE_OP: /* Acquire (mutex_object, Timeout) */ + + status = acpi_ex_acquire_mutex (operand[1], operand[0], walk_state); + if (status == AE_TIME) { + logical_result = TRUE; /* TRUE = Acquire timed out */ + status = AE_OK; + } + break; + + + case AML_WAIT_OP: /* Wait (event_object, Timeout) */ + + status = acpi_ex_system_wait_event (operand[1], operand[0]); + if (status == AE_TIME) { + logical_result = TRUE; /* TRUE, Wait timed out */ + status = AE_OK; + } + break; + + + default: + + ACPI_REPORT_ERROR (("acpi_ex_opcode_2A_0T_1R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + +store_logical_result: + /* + * Set return value to according to logical_result. logical TRUE (all ones) + * Default is FALSE (zero) + */ + if (logical_result) { + return_desc->integer.value = ACPI_INTEGER_MAX; + } + + walk_state->result_obj = return_desc; + + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (return_desc); + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exoparg3.c b/drivers/acpi/executer/exoparg3.c new file mode 100644 index 00000000000..29d0b167745 --- /dev/null +++ b/drivers/acpi/executer/exoparg3.c @@ -0,0 +1,256 @@ + +/****************************************************************************** + * + * Module Name: exoparg3 - AML execution - opcodes with 3 arguments + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exoparg3") + + +/*! + * Naming convention for AML interpreter execution routines. + * + * The routines that begin execution of AML opcodes are named with a common + * convention based upon the number of arguments, the number of target operands, + * and whether or not a value is returned: + * + * AcpiExOpcode_xA_yT_zR + * + * Where: + * + * xA - ARGUMENTS: The number of arguments (input operands) that are + * required for this opcode type (1 through 6 args). + * yT - TARGETS: The number of targets (output operands) that are required + * for this opcode type (0, 1, or 2 targets). + * zR - RETURN VALUE: Indicates whether this opcode type returns a value + * as the function return (0 or 1). + * + * The AcpiExOpcode* functions are called via the Dispatcher component with + * fully resolved operands. +!*/ + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_3A_0T_0R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute Triadic operator (3 operands) + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_3A_0T_0R ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + struct acpi_signal_fatal_info *fatal; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_3A_0T_0R", acpi_ps_get_opcode_name (walk_state->opcode)); + + + switch (walk_state->opcode) { + case AML_FATAL_OP: /* Fatal (fatal_type fatal_code fatal_arg) */ + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "fatal_op: Type %X Code %X Arg %X <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n", + (u32) operand[0]->integer.value, + (u32) operand[1]->integer.value, + (u32) operand[2]->integer.value)); + + fatal = ACPI_MEM_ALLOCATE (sizeof (struct acpi_signal_fatal_info)); + if (fatal) { + fatal->type = (u32) operand[0]->integer.value; + fatal->code = (u32) operand[1]->integer.value; + fatal->argument = (u32) operand[2]->integer.value; + } + + /* + * Always signal the OS! + */ + status = acpi_os_signal (ACPI_SIGNAL_FATAL, fatal); + + /* Might return while OS is shutting down, just continue */ + + ACPI_MEM_FREE (fatal); + break; + + + default: + + ACPI_REPORT_ERROR (("acpi_ex_opcode_3A_0T_0R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + +cleanup: + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_3A_1T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute Triadic operator (3 operands) + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_3A_1T_1R ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + char *buffer; + acpi_status status = AE_OK; + acpi_native_uint index; + acpi_size length; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_3A_1T_1R", acpi_ps_get_opcode_name (walk_state->opcode)); + + + switch (walk_state->opcode) { + case AML_MID_OP: /* Mid (Source[0], Index[1], Length[2], Result[3]) */ + + /* + * Create the return object. The Source operand is guaranteed to be + * either a String or a Buffer, so just use its type. + */ + return_desc = acpi_ut_create_internal_object (ACPI_GET_OBJECT_TYPE (operand[0])); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Get the Integer values from the objects */ + + index = (acpi_native_uint) operand[1]->integer.value; + length = (acpi_size) operand[2]->integer.value; + + /* + * If the index is beyond the length of the String/Buffer, or if the + * requested length is zero, return a zero-length String/Buffer + */ + if ((index < operand[0]->string.length) && + (length > 0)) { + /* Truncate request if larger than the actual String/Buffer */ + + if ((index + length) > + operand[0]->string.length) { + length = (acpi_size) operand[0]->string.length - index; + } + + /* Allocate a new buffer for the String/Buffer */ + + buffer = ACPI_MEM_CALLOCATE ((acpi_size) length + 1); + if (!buffer) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Copy the portion requested */ + + ACPI_MEMCPY (buffer, operand[0]->string.pointer + index, + length); + + /* Set the length of the new String/Buffer */ + + return_desc->string.pointer = buffer; + return_desc->string.length = (u32) length; + } + + /* Mark buffer initialized */ + + return_desc->buffer.flags |= AOPOBJ_DATA_VALID; + break; + + + default: + + ACPI_REPORT_ERROR (("acpi_ex_opcode_3A_0T_0R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + /* Store the result in the target */ + + status = acpi_ex_store (return_desc, operand[3], walk_state); + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (return_desc); + } + + /* Set the return object and exit */ + + if (!walk_state->result_obj) { + walk_state->result_obj = return_desc; + } + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exoparg6.c b/drivers/acpi/executer/exoparg6.c new file mode 100644 index 00000000000..d3262433162 --- /dev/null +++ b/drivers/acpi/executer/exoparg6.c @@ -0,0 +1,336 @@ + +/****************************************************************************** + * + * Module Name: exoparg6 - AML execution - opcodes with 6 arguments + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exoparg6") + + +/*! + * Naming convention for AML interpreter execution routines. + * + * The routines that begin execution of AML opcodes are named with a common + * convention based upon the number of arguments, the number of target operands, + * and whether or not a value is returned: + * + * AcpiExOpcode_xA_yT_zR + * + * Where: + * + * xA - ARGUMENTS: The number of arguments (input operands) that are + * required for this opcode type (1 through 6 args). + * yT - TARGETS: The number of targets (output operands) that are required + * for this opcode type (0, 1, or 2 targets). + * zR - RETURN VALUE: Indicates whether this opcode type returns a value + * as the function return (0 or 1). + * + * The AcpiExOpcode* functions are called via the Dispatcher component with + * fully resolved operands. +!*/ + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_do_match + * + * PARAMETERS: match_op - The AML match operand + * package_obj - Object from the target package + * match_obj - Object to be matched + * + * RETURN: TRUE if the match is successful, FALSE otherwise + * + * DESCRIPTION: Implements the low-level match for the ASL Match operator. + * Package elements will be implicitly converted to the type of + * the match object (Integer/Buffer/String). + * + ******************************************************************************/ + +u8 +acpi_ex_do_match ( + u32 match_op, + union acpi_operand_object *package_obj, + union acpi_operand_object *match_obj) +{ + u8 logical_result = TRUE; + acpi_status status; + + + /* + * Note: Since the package_obj/match_obj ordering is opposite to that of + * the standard logical operators, we have to reverse them when we call + * do_logical_op in order to make the implicit conversion rules work + * correctly. However, this means we have to flip the entire equation + * also. A bit ugly perhaps, but overall, better than fussing the + * parameters around at runtime, over and over again. + * + * Below, P[i] refers to the package element, M refers to the Match object. + */ + switch (match_op) { + case MATCH_MTR: + + /* Always true */ + + break; + + case MATCH_MEQ: + + /* + * True if equal: (P[i] == M) + * Change to: (M == P[i]) + */ + status = acpi_ex_do_logical_op (AML_LEQUAL_OP, match_obj, package_obj, + &logical_result); + if (ACPI_FAILURE (status)) { + return (FALSE); + } + break; + + case MATCH_MLE: + + /* + * True if less than or equal: (P[i] <= M) (P[i] not_greater than M) + * Change to: (M >= P[i]) (M not_less than P[i]) + */ + status = acpi_ex_do_logical_op (AML_LLESS_OP, match_obj, package_obj, + &logical_result); + if (ACPI_FAILURE (status)) { + return (FALSE); + } + logical_result = (u8) !logical_result; + break; + + case MATCH_MLT: + + /* + * True if less than: (P[i] < M) + * Change to: (M > P[i]) + */ + status = acpi_ex_do_logical_op (AML_LGREATER_OP, match_obj, package_obj, + &logical_result); + if (ACPI_FAILURE (status)) { + return (FALSE); + } + break; + + case MATCH_MGE: + + /* + * True if greater than or equal: (P[i] >= M) (P[i] not_less than M) + * Change to: (M <= P[i]) (M not_greater than P[i]) + */ + status = acpi_ex_do_logical_op (AML_LGREATER_OP, match_obj, package_obj, + &logical_result); + if (ACPI_FAILURE (status)) { + return (FALSE); + } + logical_result = (u8)!logical_result; + break; + + case MATCH_MGT: + + /* + * True if greater than: (P[i] > M) + * Change to: (M < P[i]) + */ + status = acpi_ex_do_logical_op (AML_LLESS_OP, match_obj, package_obj, + &logical_result); + if (ACPI_FAILURE (status)) { + return (FALSE); + } + break; + + default: + + /* Undefined */ + + return (FALSE); + } + + return logical_result; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_opcode_6A_0T_1R + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Execute opcode with 6 arguments, no target, and a return value + * + ******************************************************************************/ + +acpi_status +acpi_ex_opcode_6A_0T_1R ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object **operand = &walk_state->operands[0]; + union acpi_operand_object *return_desc = NULL; + acpi_status status = AE_OK; + u32 index; + union acpi_operand_object *this_element; + + + ACPI_FUNCTION_TRACE_STR ("ex_opcode_6A_0T_1R", acpi_ps_get_opcode_name (walk_state->opcode)); + + + switch (walk_state->opcode) { + case AML_MATCH_OP: + /* + * Match (search_pkg[0], match_op1[1], match_obj1[2], + * match_op2[3], match_obj2[4], start_index[5]) + */ + + /* Validate both Match Term Operators (MTR, MEQ, etc.) */ + + if ((operand[1]->integer.value > MAX_MATCH_OPERATOR) || + (operand[3]->integer.value > MAX_MATCH_OPERATOR)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Match operator out of range\n")); + status = AE_AML_OPERAND_VALUE; + goto cleanup; + } + + /* Get the package start_index, validate against the package length */ + + index = (u32) operand[5]->integer.value; + if (index >= (u32) operand[0]->package.count) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Index beyond package end\n")); + status = AE_AML_PACKAGE_LIMIT; + goto cleanup; + } + + /* Create an integer for the return value */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + status = AE_NO_MEMORY; + goto cleanup; + + } + + /* Default return value if no match found */ + + return_desc->integer.value = ACPI_INTEGER_MAX; + + /* + * Examine each element until a match is found. Both match conditions + * must be satisfied for a match to occur. Within the loop, + * "continue" signifies that the current element does not match + * and the next should be examined. + * + * Upon finding a match, the loop will terminate via "break" at + * the bottom. If it terminates "normally", match_value will be + * ACPI_INTEGER_MAX (Ones) (its initial value) indicating that no + * match was found. + */ + for ( ; index < operand[0]->package.count; index++) { + /* Get the current package element */ + + this_element = operand[0]->package.elements[index]; + + /* Treat any uninitialized (NULL) elements as non-matching */ + + if (!this_element) { + continue; + } + + /* + * Both match conditions must be satisfied. Execution of a continue + * (proceed to next iteration of enclosing for loop) signifies a + * non-match. + */ + if (!acpi_ex_do_match ((u32) operand[1]->integer.value, + this_element, operand[2])) { + continue; + } + + if (!acpi_ex_do_match ((u32) operand[3]->integer.value, + this_element, operand[4])) { + continue; + } + + /* Match found: Index is the return value */ + + return_desc->integer.value = index; + break; + } + break; + + + case AML_LOAD_TABLE_OP: + + status = acpi_ex_load_table_op (walk_state, &return_desc); + break; + + + default: + + ACPI_REPORT_ERROR (("acpi_ex_opcode_3A_0T_0R: Unknown opcode %X\n", + walk_state->opcode)); + status = AE_AML_BAD_OPCODE; + goto cleanup; + } + + + walk_state->result_obj = return_desc; + + +cleanup: + + /* Delete return object on error */ + + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (return_desc); + } + + return_ACPI_STATUS (status); +} diff --git a/drivers/acpi/executer/exprep.c b/drivers/acpi/executer/exprep.c new file mode 100644 index 00000000000..264ef3bba31 --- /dev/null +++ b/drivers/acpi/executer/exprep.c @@ -0,0 +1,530 @@ + +/****************************************************************************** + * + * Module Name: exprep - ACPI AML (p-code) execution - field prep utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exprep") + + +#ifdef ACPI_UNDER_DEVELOPMENT +/******************************************************************************* + * + * FUNCTION: acpi_ex_generate_access + * + * PARAMETERS: field_bit_offset - Start of field within parent region/buffer + * field_bit_length - Length of field in bits + * region_length - Length of parent in bytes + * + * RETURN: Field granularity (8, 16, 32 or 64) and + * byte_alignment (1, 2, 3, or 4) + * + * DESCRIPTION: Generate an optimal access width for fields defined with the + * any_acc keyword. + * + * NOTE: Need to have the region_length in order to check for boundary + * conditions (end-of-region). However, the region_length is a deferred + * operation. Therefore, to complete this implementation, the generation + * of this access width must be deferred until the region length has + * been evaluated. + * + ******************************************************************************/ + +static u32 +acpi_ex_generate_access ( + u32 field_bit_offset, + u32 field_bit_length, + u32 region_length) +{ + u32 field_byte_length; + u32 field_byte_offset; + u32 field_byte_end_offset; + u32 access_byte_width; + u32 field_start_offset; + u32 field_end_offset; + u32 minimum_access_width = 0xFFFFFFFF; + u32 minimum_accesses = 0xFFFFFFFF; + u32 accesses; + + + ACPI_FUNCTION_TRACE ("ex_generate_access"); + + + /* Round Field start offset and length to "minimal" byte boundaries */ + + field_byte_offset = ACPI_DIV_8 (ACPI_ROUND_DOWN (field_bit_offset, 8)); + field_byte_end_offset = ACPI_DIV_8 (ACPI_ROUND_UP (field_bit_length + field_bit_offset, 8)); + field_byte_length = field_byte_end_offset - field_byte_offset; + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Bit length %d, Bit offset %d\n", + field_bit_length, field_bit_offset)); + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Byte Length %d, Byte Offset %d, End Offset %d\n", + field_byte_length, field_byte_offset, field_byte_end_offset)); + + /* + * Iterative search for the maximum access width that is both aligned + * and does not go beyond the end of the region + * + * Start at byte_acc and work upwards to qword_acc max. (1,2,4,8 bytes) + */ + for (access_byte_width = 1; access_byte_width <= 8; access_byte_width <<= 1) { + /* + * 1) Round end offset up to next access boundary and make sure that this + * does not go beyond the end of the parent region. + * 2) When the Access width is greater than the field_byte_length, we are done. + * (This does not optimize for the perfectly aligned case yet). + */ + if (ACPI_ROUND_UP (field_byte_end_offset, access_byte_width) <= region_length) { + field_start_offset = ACPI_ROUND_DOWN (field_byte_offset, access_byte_width) / + access_byte_width; + field_end_offset = ACPI_ROUND_UP ((field_byte_length + field_byte_offset), + access_byte_width) / access_byte_width; + accesses = field_end_offset - field_start_offset; + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "access_width %d end is within region\n", access_byte_width)); + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Field Start %d, Field End %d -- requires %d accesses\n", + field_start_offset, field_end_offset, accesses)); + + /* Single access is optimal */ + + if (accesses <= 1) { + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Entire field can be accessed with one operation of size %d\n", + access_byte_width)); + return_VALUE (access_byte_width); + } + + /* + * Fits in the region, but requires more than one read/write. + * try the next wider access on next iteration + */ + if (accesses < minimum_accesses) { + minimum_accesses = accesses; + minimum_access_width = access_byte_width; + } + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "access_width %d end is NOT within region\n", access_byte_width)); + if (access_byte_width == 1) { + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Field goes beyond end-of-region!\n")); + return_VALUE (0); /* Field does not fit in the region at all */ + } + + /* This width goes beyond the end-of-region, back off to previous access */ + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Backing off to previous optimal access width of %d\n", + minimum_access_width)); + return_VALUE (minimum_access_width); + } + } + + /* Could not read/write field with one operation, just use max access width */ + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Cannot access field in one operation, using width 8\n")); + return_VALUE (8); +} +#endif /* ACPI_UNDER_DEVELOPMENT */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_decode_field_access + * + * PARAMETERS: Access - Encoded field access bits + * Length - Field length. + * + * RETURN: Field granularity (8, 16, 32 or 64) and + * byte_alignment (1, 2, 3, or 4) + * + * DESCRIPTION: Decode the access_type bits of a field definition. + * + ******************************************************************************/ + +static u32 +acpi_ex_decode_field_access ( + union acpi_operand_object *obj_desc, + u8 field_flags, + u32 *return_byte_alignment) +{ + u32 access; + u32 byte_alignment; + u32 bit_length; + + + ACPI_FUNCTION_TRACE ("ex_decode_field_access"); + + + access = (field_flags & AML_FIELD_ACCESS_TYPE_MASK); + + switch (access) { + case AML_FIELD_ACCESS_ANY: + +#ifdef ACPI_UNDER_DEVELOPMENT + byte_alignment = acpi_ex_generate_access (obj_desc->common_field.start_field_bit_offset, + obj_desc->common_field.bit_length, + 0xFFFFFFFF /* Temp until we pass region_length as param */); + bit_length = byte_alignment * 8; +#endif + + byte_alignment = 1; + bit_length = 8; + break; + + case AML_FIELD_ACCESS_BYTE: + case AML_FIELD_ACCESS_BUFFER: /* ACPI 2.0 (SMBus Buffer) */ + byte_alignment = 1; + bit_length = 8; + break; + + case AML_FIELD_ACCESS_WORD: + byte_alignment = 2; + bit_length = 16; + break; + + case AML_FIELD_ACCESS_DWORD: + byte_alignment = 4; + bit_length = 32; + break; + + case AML_FIELD_ACCESS_QWORD: /* ACPI 2.0 */ + byte_alignment = 8; + bit_length = 64; + break; + + default: + /* Invalid field access type */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unknown field access type %X\n", + access)); + return_VALUE (0); + } + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_BUFFER_FIELD) { + /* + * buffer_field access can be on any byte boundary, so the + * byte_alignment is always 1 byte -- regardless of any byte_alignment + * implied by the field access type. + */ + byte_alignment = 1; + } + + *return_byte_alignment = byte_alignment; + return_VALUE (bit_length); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_prep_common_field_object + * + * PARAMETERS: obj_desc - The field object + * field_flags - Access, lock_rule, and update_rule. + * The format of a field_flag is described + * in the ACPI specification + * field_bit_position - Field start position + * field_bit_length - Field length in number of bits + * + * RETURN: Status + * + * DESCRIPTION: Initialize the areas of the field object that are common + * to the various types of fields. Note: This is very "sensitive" + * code because we are solving the general case for field + * alignment. + * + ******************************************************************************/ + +acpi_status +acpi_ex_prep_common_field_object ( + union acpi_operand_object *obj_desc, + u8 field_flags, + u8 field_attribute, + u32 field_bit_position, + u32 field_bit_length) +{ + u32 access_bit_width; + u32 byte_alignment; + u32 nearest_byte_address; + + + ACPI_FUNCTION_TRACE ("ex_prep_common_field_object"); + + + /* + * Note: the structure being initialized is the + * ACPI_COMMON_FIELD_INFO; No structure fields outside of the common + * area are initialized by this procedure. + */ + obj_desc->common_field.field_flags = field_flags; + obj_desc->common_field.attribute = field_attribute; + obj_desc->common_field.bit_length = field_bit_length; + + /* + * Decode the access type so we can compute offsets. The access type gives + * two pieces of information - the width of each field access and the + * necessary byte_alignment (address granularity) of the access. + * + * For any_acc, the access_bit_width is the largest width that is both + * necessary and possible in an attempt to access the whole field in one + * I/O operation. However, for any_acc, the byte_alignment is always one + * byte. + * + * For all Buffer Fields, the byte_alignment is always one byte. + * + * For all other access types (Byte, Word, Dword, Qword), the Bitwidth is + * the same (equivalent) as the byte_alignment. + */ + access_bit_width = acpi_ex_decode_field_access (obj_desc, field_flags, + &byte_alignment); + if (!access_bit_width) { + return_ACPI_STATUS (AE_AML_OPERAND_VALUE); + } + + /* Setup width (access granularity) fields */ + + obj_desc->common_field.access_byte_width = (u8) + ACPI_DIV_8 (access_bit_width); /* 1, 2, 4, 8 */ + + obj_desc->common_field.access_bit_width = (u8) access_bit_width; + + /* + * base_byte_offset is the address of the start of the field within the + * region. It is the byte address of the first *datum* (field-width data + * unit) of the field. (i.e., the first datum that contains at least the + * first *bit* of the field.) + * + * Note: byte_alignment is always either equal to the access_bit_width or 8 + * (Byte access), and it defines the addressing granularity of the parent + * region or buffer. + */ + nearest_byte_address = + ACPI_ROUND_BITS_DOWN_TO_BYTES (field_bit_position); + obj_desc->common_field.base_byte_offset = (u32) + ACPI_ROUND_DOWN (nearest_byte_address, byte_alignment); + + /* + * start_field_bit_offset is the offset of the first bit of the field within + * a field datum. + */ + obj_desc->common_field.start_field_bit_offset = (u8) + (field_bit_position - ACPI_MUL_8 (obj_desc->common_field.base_byte_offset)); + + /* + * Does the entire field fit within a single field access element? (datum) + * (i.e., without crossing a datum boundary) + */ + if ((obj_desc->common_field.start_field_bit_offset + field_bit_length) <= + (u16) access_bit_width) { + obj_desc->common.flags |= AOPOBJ_SINGLE_DATUM; + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_prep_field_value + * + * PARAMETERS: Node - Owning Node + * region_node - Region in which field is being defined + * field_flags - Access, lock_rule, and update_rule. + * field_bit_position - Field start position + * field_bit_length - Field length in number of bits + * + * RETURN: Status + * + * DESCRIPTION: Construct an union acpi_operand_object of type def_field and + * connect it to the parent Node. + * + ******************************************************************************/ + +acpi_status +acpi_ex_prep_field_value ( + struct acpi_create_field_info *info) +{ + union acpi_operand_object *obj_desc; + u32 type; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ex_prep_field_value"); + + + /* Parameter validation */ + + if (info->field_type != ACPI_TYPE_LOCAL_INDEX_FIELD) { + if (!info->region_node) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null region_node\n")); + return_ACPI_STATUS (AE_AML_NO_OPERAND); + } + + type = acpi_ns_get_type (info->region_node); + if (type != ACPI_TYPE_REGION) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed Region, found type %X (%s)\n", + type, acpi_ut_get_type_name (type))); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + } + + /* Allocate a new field object */ + + obj_desc = acpi_ut_create_internal_object (info->field_type); + if (!obj_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Initialize areas of the object that are common to all fields */ + + obj_desc->common_field.node = info->field_node; + status = acpi_ex_prep_common_field_object (obj_desc, info->field_flags, + info->attribute, info->field_bit_position, info->field_bit_length); + if (ACPI_FAILURE (status)) { + acpi_ut_delete_object_desc (obj_desc); + return_ACPI_STATUS (status); + } + + /* Initialize areas of the object that are specific to the field type */ + + switch (info->field_type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + + obj_desc->field.region_obj = acpi_ns_get_attached_object (info->region_node); + + /* An additional reference for the container */ + + acpi_ut_add_reference (obj_desc->field.region_obj); + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "region_field: bit_off %X, Off %X, Gran %X, Region %p\n", + obj_desc->field.start_field_bit_offset, obj_desc->field.base_byte_offset, + obj_desc->field.access_byte_width, obj_desc->field.region_obj)); + break; + + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + obj_desc->bank_field.value = info->bank_value; + obj_desc->bank_field.region_obj = acpi_ns_get_attached_object (info->region_node); + obj_desc->bank_field.bank_obj = acpi_ns_get_attached_object (info->register_node); + + /* An additional reference for the attached objects */ + + acpi_ut_add_reference (obj_desc->bank_field.region_obj); + acpi_ut_add_reference (obj_desc->bank_field.bank_obj); + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "Bank Field: bit_off %X, Off %X, Gran %X, Region %p, bank_reg %p\n", + obj_desc->bank_field.start_field_bit_offset, + obj_desc->bank_field.base_byte_offset, + obj_desc->field.access_byte_width, + obj_desc->bank_field.region_obj, + obj_desc->bank_field.bank_obj)); + break; + + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + obj_desc->index_field.index_obj = acpi_ns_get_attached_object (info->register_node); + obj_desc->index_field.data_obj = acpi_ns_get_attached_object (info->data_register_node); + obj_desc->index_field.value = (u32) + (info->field_bit_position / ACPI_MUL_8 (obj_desc->field.access_byte_width)); + + if (!obj_desc->index_field.data_obj || !obj_desc->index_field.index_obj) { + ACPI_REPORT_ERROR (("Null Index Object during field prep\n")); + acpi_ut_delete_object_desc (obj_desc); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* An additional reference for the attached objects */ + + acpi_ut_add_reference (obj_desc->index_field.data_obj); + acpi_ut_add_reference (obj_desc->index_field.index_obj); + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, + "index_field: bit_off %X, Off %X, Value %X, Gran %X, Index %p, Data %p\n", + obj_desc->index_field.start_field_bit_offset, + obj_desc->index_field.base_byte_offset, + obj_desc->index_field.value, + obj_desc->field.access_byte_width, + obj_desc->index_field.index_obj, + obj_desc->index_field.data_obj)); + break; + + default: + /* No other types should get here */ + break; + } + + /* + * Store the constructed descriptor (obj_desc) into the parent Node, + * preserving the current type of that named_obj. + */ + status = acpi_ns_attach_object (info->field_node, obj_desc, + acpi_ns_get_type (info->field_node)); + + ACPI_DEBUG_PRINT ((ACPI_DB_BFIELD, "Set named_obj %p [%4.4s], obj_desc %p\n", + info->field_node, acpi_ut_get_node_name (info->field_node), obj_desc)); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + diff --git a/drivers/acpi/executer/exregion.c b/drivers/acpi/executer/exregion.c new file mode 100644 index 00000000000..7cfd0684c70 --- /dev/null +++ b/drivers/acpi/executer/exregion.c @@ -0,0 +1,528 @@ + +/****************************************************************************** + * + * Module Name: exregion - ACPI default op_region (address space) handlers + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exregion") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_memory_space_handler + * + * PARAMETERS: Function - Read or Write operation + * Address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the System Memory address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_memory_space_handler ( + u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer *value, + void *handler_context, + void *region_context) +{ + acpi_status status = AE_OK; + void *logical_addr_ptr = NULL; + struct acpi_mem_space_context *mem_info = region_context; + u32 length; + acpi_size window_size; +#ifndef ACPI_MISALIGNED_TRANSFERS + u32 remainder; +#endif + + ACPI_FUNCTION_TRACE ("ex_system_memory_space_handler"); + + + /* Validate and translate the bit width */ + + switch (bit_width) { + case 8: + length = 1; + break; + + case 16: + length = 2; + break; + + case 32: + length = 4; + break; + + case 64: + length = 8; + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid system_memory width %d\n", + bit_width)); + return_ACPI_STATUS (AE_AML_OPERAND_VALUE); + } + + +#ifndef ACPI_MISALIGNED_TRANSFERS + /* + * Hardware does not support non-aligned data transfers, we must verify + * the request. + */ + (void) acpi_ut_short_divide ((acpi_integer) address, length, NULL, &remainder); + if (remainder != 0) { + return_ACPI_STATUS (AE_AML_ALIGNMENT); + } +#endif + + /* + * Does the request fit into the cached memory mapping? + * Is 1) Address below the current mapping? OR + * 2) Address beyond the current mapping? + */ + if ((address < mem_info->mapped_physical_address) || + (((acpi_integer) address + length) > + ((acpi_integer) mem_info->mapped_physical_address + mem_info->mapped_length))) { + /* + * The request cannot be resolved by the current memory mapping; + * Delete the existing mapping and create a new one. + */ + if (mem_info->mapped_length) { + /* Valid mapping, delete it */ + + acpi_os_unmap_memory (mem_info->mapped_logical_address, + mem_info->mapped_length); + } + + /* + * Don't attempt to map memory beyond the end of the region, and + * constrain the maximum mapping size to something reasonable. + */ + window_size = (acpi_size) ((mem_info->address + mem_info->length) - address); + if (window_size > ACPI_SYSMEM_REGION_WINDOW_SIZE) { + window_size = ACPI_SYSMEM_REGION_WINDOW_SIZE; + } + + /* Create a new mapping starting at the address given */ + + status = acpi_os_map_memory (address, window_size, + (void **) &mem_info->mapped_logical_address); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not map memory at %8.8X%8.8X, size %X\n", + ACPI_FORMAT_UINT64 (address), (u32) window_size)); + mem_info->mapped_length = 0; + return_ACPI_STATUS (status); + } + + /* Save the physical address and mapping size */ + + mem_info->mapped_physical_address = address; + mem_info->mapped_length = window_size; + } + + /* + * Generate a logical pointer corresponding to the address we want to + * access + */ + logical_addr_ptr = mem_info->mapped_logical_address + + ((acpi_integer) address - (acpi_integer) mem_info->mapped_physical_address); + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "system_memory %d (%d width) Address=%8.8X%8.8X\n", function, bit_width, + ACPI_FORMAT_UINT64 (address))); + + /* + * Perform the memory read or write + * + * Note: For machines that do not support non-aligned transfers, the target + * address was checked for alignment above. We do not attempt to break the + * transfer up into smaller (byte-size) chunks because the AML specifically + * asked for a transfer width that the hardware may require. + */ + switch (function) { + case ACPI_READ: + + *value = 0; + switch (bit_width) { + case 8: + *value = (acpi_integer) *((u8 *) logical_addr_ptr); + break; + + case 16: + *value = (acpi_integer) *((u16 *) logical_addr_ptr); + break; + + case 32: + *value = (acpi_integer) *((u32 *) logical_addr_ptr); + break; + +#if ACPI_MACHINE_WIDTH != 16 + case 64: + *value = (acpi_integer) *((u64 *) logical_addr_ptr); + break; +#endif + default: + /* bit_width was already validated */ + break; + } + break; + + case ACPI_WRITE: + + switch (bit_width) { + case 8: + *(u8 *) logical_addr_ptr = (u8) *value; + break; + + case 16: + *(u16 *) logical_addr_ptr = (u16) *value; + break; + + case 32: + *(u32 *) logical_addr_ptr = (u32) *value; + break; + +#if ACPI_MACHINE_WIDTH != 16 + case 64: + *(u64 *) logical_addr_ptr = (u64) *value; + break; +#endif + + default: + /* bit_width was already validated */ + break; + } + break; + + default: + status = AE_BAD_PARAMETER; + break; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_io_space_handler + * + * PARAMETERS: Function - Read or Write operation + * Address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the System IO address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_io_space_handler ( + u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer *value, + void *handler_context, + void *region_context) +{ + acpi_status status = AE_OK; + u32 value32; + + + ACPI_FUNCTION_TRACE ("ex_system_io_space_handler"); + + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "system_iO %d (%d width) Address=%8.8X%8.8X\n", function, bit_width, + ACPI_FORMAT_UINT64 (address))); + + /* Decode the function parameter */ + + switch (function) { + case ACPI_READ: + + status = acpi_os_read_port ((acpi_io_address) address, &value32, bit_width); + *value = value32; + break; + + case ACPI_WRITE: + + status = acpi_os_write_port ((acpi_io_address) address, (u32) *value, bit_width); + break; + + default: + status = AE_BAD_PARAMETER; + break; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_pci_config_space_handler + * + * PARAMETERS: Function - Read or Write operation + * Address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the PCI Config address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_pci_config_space_handler ( + u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer *value, + void *handler_context, + void *region_context) +{ + acpi_status status = AE_OK; + struct acpi_pci_id *pci_id; + u16 pci_register; + + + ACPI_FUNCTION_TRACE ("ex_pci_config_space_handler"); + + + /* + * The arguments to acpi_os(Read|Write)pci_configuration are: + * + * pci_segment is the PCI bus segment range 0-31 + * pci_bus is the PCI bus number range 0-255 + * pci_device is the PCI device number range 0-31 + * pci_function is the PCI device function number + * pci_register is the Config space register range 0-255 bytes + * + * Value - input value for write, output address for read + * + */ + pci_id = (struct acpi_pci_id *) region_context; + pci_register = (u16) (u32) address; + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "pci_config %d (%d) Seg(%04x) Bus(%04x) Dev(%04x) Func(%04x) Reg(%04x)\n", + function, bit_width, pci_id->segment, pci_id->bus, pci_id->device, + pci_id->function, pci_register)); + + switch (function) { + case ACPI_READ: + + *value = 0; + status = acpi_os_read_pci_configuration (pci_id, pci_register, value, bit_width); + break; + + case ACPI_WRITE: + + status = acpi_os_write_pci_configuration (pci_id, pci_register, *value, bit_width); + break; + + default: + + status = AE_BAD_PARAMETER; + break; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_cmos_space_handler + * + * PARAMETERS: Function - Read or Write operation + * Address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the CMOS address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_cmos_space_handler ( + u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer *value, + void *handler_context, + void *region_context) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ex_cmos_space_handler"); + + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_pci_bar_space_handler + * + * PARAMETERS: Function - Read or Write operation + * Address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the PCI bar_target address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_pci_bar_space_handler ( + u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer *value, + void *handler_context, + void *region_context) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ex_pci_bar_space_handler"); + + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_data_table_space_handler + * + * PARAMETERS: Function - Read or Write operation + * Address - Where in the space to read or write + * bit_width - Field width in bits (8, 16, or 32) + * Value - Pointer to in or out value + * handler_context - Pointer to Handler's context + * region_context - Pointer to context specific to the + * accessed region + * + * RETURN: Status + * + * DESCRIPTION: Handler for the Data Table address space (Op Region) + * + ******************************************************************************/ + +acpi_status +acpi_ex_data_table_space_handler ( + u32 function, + acpi_physical_address address, + u32 bit_width, + acpi_integer *value, + void *handler_context, + void *region_context) +{ + acpi_status status = AE_OK; + u32 byte_width = ACPI_DIV_8 (bit_width); + u32 i; + char *logical_addr_ptr; + + + ACPI_FUNCTION_TRACE ("ex_data_table_space_handler"); + + + logical_addr_ptr = ACPI_PHYSADDR_TO_PTR (address); + + + /* Perform the memory read or write */ + + switch (function) { + case ACPI_READ: + + for (i = 0; i < byte_width; i++) { + ((char *) value) [i] = logical_addr_ptr[i]; + } + break; + + case ACPI_WRITE: + default: + + return_ACPI_STATUS (AE_SUPPORT); + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exresnte.c b/drivers/acpi/executer/exresnte.c new file mode 100644 index 00000000000..7936329a0e3 --- /dev/null +++ b/drivers/acpi/executer/exresnte.c @@ -0,0 +1,289 @@ + +/****************************************************************************** + * + * Module Name: exresnte - AML Interpreter object resolution + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exresnte") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_node_to_value + * + * PARAMETERS: object_ptr - Pointer to a location that contains + * a pointer to a NS node, and will receive a + * pointer to the resolved object. + * walk_state - Current state. Valid only if executing AML + * code. NULL if simply resolving an object + * + * RETURN: Status + * + * DESCRIPTION: Resolve a Namespace node to a valued object + * + * Note: for some of the data types, the pointer attached to the Node + * can be either a pointer to an actual internal object or a pointer into the + * AML stream itself. These types are currently: + * + * ACPI_TYPE_INTEGER + * ACPI_TYPE_STRING + * ACPI_TYPE_BUFFER + * ACPI_TYPE_MUTEX + * ACPI_TYPE_PACKAGE + * + ******************************************************************************/ + +acpi_status +acpi_ex_resolve_node_to_value ( + struct acpi_namespace_node **object_ptr, + struct acpi_walk_state *walk_state) + +{ + acpi_status status = AE_OK; + union acpi_operand_object *source_desc; + union acpi_operand_object *obj_desc = NULL; + struct acpi_namespace_node *node; + acpi_object_type entry_type; + + + ACPI_FUNCTION_TRACE ("ex_resolve_node_to_value"); + + + /* + * The stack pointer points to a struct acpi_namespace_node (Node). Get the + * object that is attached to the Node. + */ + node = *object_ptr; + source_desc = acpi_ns_get_attached_object (node); + entry_type = acpi_ns_get_type ((acpi_handle) node); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Entry=%p source_desc=%p [%s]\n", + node, source_desc, acpi_ut_get_type_name (entry_type))); + + if ((entry_type == ACPI_TYPE_LOCAL_ALIAS) || + (entry_type == ACPI_TYPE_LOCAL_METHOD_ALIAS)) { + /* There is always exactly one level of indirection */ + + node = ACPI_CAST_PTR (struct acpi_namespace_node, node->object); + source_desc = acpi_ns_get_attached_object (node); + entry_type = acpi_ns_get_type ((acpi_handle) node); + *object_ptr = node; + } + + /* + * Several object types require no further processing: + * 1) Devices rarely have an attached object, return the Node + * 2) Method locals and arguments have a pseudo-Node + */ + if (entry_type == ACPI_TYPE_DEVICE || + (node->flags & (ANOBJ_METHOD_ARG | ANOBJ_METHOD_LOCAL))) { + return_ACPI_STATUS (AE_OK); + } + + if (!source_desc) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No object attached to node %p\n", + node)); + return_ACPI_STATUS (AE_AML_NO_OPERAND); + } + + /* + * Action is based on the type of the Node, which indicates the type + * of the attached object or pointer + */ + switch (entry_type) { + case ACPI_TYPE_PACKAGE: + + if (ACPI_GET_OBJECT_TYPE (source_desc) != ACPI_TYPE_PACKAGE) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Object not a Package, type %s\n", + acpi_ut_get_object_type_name (source_desc))); + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + status = acpi_ds_get_package_arguments (source_desc); + if (ACPI_SUCCESS (status)) { + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference (obj_desc); + } + break; + + + case ACPI_TYPE_BUFFER: + + if (ACPI_GET_OBJECT_TYPE (source_desc) != ACPI_TYPE_BUFFER) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Object not a Buffer, type %s\n", + acpi_ut_get_object_type_name (source_desc))); + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + status = acpi_ds_get_buffer_arguments (source_desc); + if (ACPI_SUCCESS (status)) { + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference (obj_desc); + } + break; + + + case ACPI_TYPE_STRING: + + if (ACPI_GET_OBJECT_TYPE (source_desc) != ACPI_TYPE_STRING) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Object not a String, type %s\n", + acpi_ut_get_object_type_name (source_desc))); + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference (obj_desc); + break; + + + case ACPI_TYPE_INTEGER: + + if (ACPI_GET_OBJECT_TYPE (source_desc) != ACPI_TYPE_INTEGER) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Object not a Integer, type %s\n", + acpi_ut_get_object_type_name (source_desc))); + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference (obj_desc); + break; + + + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "field_read Node=%p source_desc=%p Type=%X\n", + node, source_desc, entry_type)); + + status = acpi_ex_read_data_from_field (walk_state, source_desc, &obj_desc); + break; + + /* + * For these objects, just return the object attached to the Node + */ + case ACPI_TYPE_MUTEX: + case ACPI_TYPE_METHOD: + case ACPI_TYPE_POWER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_THERMAL: + case ACPI_TYPE_EVENT: + case ACPI_TYPE_REGION: + + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference (obj_desc); + break; + + + /* TYPE_ANY is untyped, and thus there is no object associated with it */ + + case ACPI_TYPE_ANY: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Untyped entry %p, no attached object!\n", + node)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); /* Cannot be AE_TYPE */ + + + case ACPI_TYPE_LOCAL_REFERENCE: + + switch (source_desc->reference.opcode) { + case AML_LOAD_OP: + + /* This is a ddb_handle */ + /* Return an additional reference to the object */ + + obj_desc = source_desc; + acpi_ut_add_reference (obj_desc); + break; + + default: + /* No named references are allowed here */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unsupported Reference opcode %X (%s)\n", + source_desc->reference.opcode, + acpi_ps_get_opcode_name (source_desc->reference.opcode))); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + break; + + + /* Default case is for unknown types */ + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Node %p - Unknown object type %X\n", + node, entry_type)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + + } /* switch (entry_type) */ + + + /* Put the object descriptor on the stack */ + + *object_ptr = (void *) obj_desc; + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exresolv.c b/drivers/acpi/executer/exresolv.c new file mode 100644 index 00000000000..7be60491115 --- /dev/null +++ b/drivers/acpi/executer/exresolv.c @@ -0,0 +1,546 @@ + +/****************************************************************************** + * + * Module Name: exresolv - AML Interpreter object resolution + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/amlcode.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> +#include <acpi/acparser.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exresolv") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_to_value + * + * PARAMETERS: **stack_ptr - Points to entry on obj_stack, which can + * be either an (union acpi_operand_object *) + * or an acpi_handle. + * walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Convert Reference objects to values + * + ******************************************************************************/ + +acpi_status +acpi_ex_resolve_to_value ( + union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ex_resolve_to_value", stack_ptr); + + + if (!stack_ptr || !*stack_ptr) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Internal - null pointer\n")); + return_ACPI_STATUS (AE_AML_NO_OPERAND); + } + + /* + * The entity pointed to by the stack_ptr can be either + * 1) A valid union acpi_operand_object, or + * 2) A struct acpi_namespace_node (named_obj) + */ + if (ACPI_GET_DESCRIPTOR_TYPE (*stack_ptr) == ACPI_DESC_TYPE_OPERAND) { + status = acpi_ex_resolve_object_to_value (stack_ptr, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* + * Object on the stack may have changed if acpi_ex_resolve_object_to_value() + * was called (i.e., we can't use an _else_ here.) + */ + if (ACPI_GET_DESCRIPTOR_TYPE (*stack_ptr) == ACPI_DESC_TYPE_NAMED) { + status = acpi_ex_resolve_node_to_value ( + ACPI_CAST_INDIRECT_PTR (struct acpi_namespace_node, stack_ptr), + walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Resolved object %p\n", *stack_ptr)); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_object_to_value + * + * PARAMETERS: stack_ptr - Pointer to a stack location that contains a + * ptr to an internal object. + * walk_state - Current method state + * + * RETURN: Status + * + * DESCRIPTION: Retrieve the value from an internal object. The Reference type + * uses the associated AML opcode to determine the value. + * + ******************************************************************************/ + +acpi_status +acpi_ex_resolve_object_to_value ( + union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *stack_desc; + void *temp_node; + union acpi_operand_object *obj_desc; + u16 opcode; + + + ACPI_FUNCTION_TRACE ("ex_resolve_object_to_value"); + + + stack_desc = *stack_ptr; + + /* This is an union acpi_operand_object */ + + switch (ACPI_GET_OBJECT_TYPE (stack_desc)) { + case ACPI_TYPE_LOCAL_REFERENCE: + + opcode = stack_desc->reference.opcode; + + switch (opcode) { + case AML_NAME_OP: + + /* + * Convert indirect name ptr to a direct name ptr. + * Then, acpi_ex_resolve_node_to_value can be used to get the value + */ + temp_node = stack_desc->reference.object; + + /* Delete the Reference Object */ + + acpi_ut_remove_reference (stack_desc); + + /* Put direct name pointer onto stack and exit */ + + (*stack_ptr) = temp_node; + break; + + + case AML_LOCAL_OP: + case AML_ARG_OP: + + /* + * Get the local from the method's state info + * Note: this increments the local's object reference count + */ + status = acpi_ds_method_data_get_value (opcode, + stack_desc->reference.offset, walk_state, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Arg/Local %X] value_obj is %p\n", + stack_desc->reference.offset, obj_desc)); + + /* + * Now we can delete the original Reference Object and + * replace it with the resolved value + */ + acpi_ut_remove_reference (stack_desc); + *stack_ptr = obj_desc; + break; + + + case AML_INDEX_OP: + + switch (stack_desc->reference.target_type) { + case ACPI_TYPE_BUFFER_FIELD: + + /* Just return - leave the Reference on the stack */ + break; + + + case ACPI_TYPE_PACKAGE: + + obj_desc = *stack_desc->reference.where; + if (obj_desc) { + /* + * Valid obj descriptor, copy pointer to return value + * (i.e., dereference the package index) + * Delete the ref object, increment the returned object + */ + acpi_ut_remove_reference (stack_desc); + acpi_ut_add_reference (obj_desc); + *stack_ptr = obj_desc; + } + else { + /* + * A NULL object descriptor means an unitialized element of + * the package, can't dereference it + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Attempt to deref an Index to NULL pkg element Idx=%p\n", + stack_desc)); + status = AE_AML_UNINITIALIZED_ELEMENT; + } + break; + + + default: + + /* Invalid reference object */ + + ACPI_REPORT_ERROR (( + "During resolve, Unknown target_type %X in Index/Reference obj %p\n", + stack_desc->reference.target_type, stack_desc)); + status = AE_AML_INTERNAL; + break; + } + break; + + + case AML_REF_OF_OP: + case AML_DEBUG_OP: + case AML_LOAD_OP: + + /* Just leave the object as-is */ + + break; + + + default: + + ACPI_REPORT_ERROR (("During resolve, Unknown Reference opcode %X (%s) in %p\n", + opcode, acpi_ps_get_opcode_name (opcode), stack_desc)); + status = AE_AML_INTERNAL; + break; + } + break; + + + case ACPI_TYPE_BUFFER: + + status = acpi_ds_get_buffer_arguments (stack_desc); + break; + + + case ACPI_TYPE_PACKAGE: + + status = acpi_ds_get_package_arguments (stack_desc); + break; + + + /* + * These cases may never happen here, but just in case.. + */ + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "field_read source_desc=%p Type=%X\n", + stack_desc, ACPI_GET_OBJECT_TYPE (stack_desc))); + + status = acpi_ex_read_data_from_field (walk_state, stack_desc, &obj_desc); + *stack_ptr = (void *) obj_desc; + break; + + default: + break; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_multiple + * + * PARAMETERS: walk_state - Current state (contains AML opcode) + * Operand - Starting point for resolution + * return_type - Where the object type is returned + * return_desc - Where the resolved object is returned + * + * RETURN: Status + * + * DESCRIPTION: Return the base object and type. Traverse a reference list if + * necessary to get to the base object. + * + ******************************************************************************/ + +acpi_status +acpi_ex_resolve_multiple ( + struct acpi_walk_state *walk_state, + union acpi_operand_object *operand, + acpi_object_type *return_type, + union acpi_operand_object **return_desc) +{ + union acpi_operand_object *obj_desc = (void *) operand; + struct acpi_namespace_node *node; + acpi_object_type type; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_ex_resolve_multiple"); + + + /* + * Operand can be either a namespace node or an operand descriptor + */ + switch (ACPI_GET_DESCRIPTOR_TYPE (obj_desc)) { + case ACPI_DESC_TYPE_OPERAND: + type = obj_desc->common.type; + break; + + case ACPI_DESC_TYPE_NAMED: + type = ((struct acpi_namespace_node *) obj_desc)->type; + obj_desc = acpi_ns_get_attached_object ((struct acpi_namespace_node *) obj_desc); + + /* If we had an Alias node, use the attached object for type info */ + + if (type == ACPI_TYPE_LOCAL_ALIAS) { + type = ((struct acpi_namespace_node *) obj_desc)->type; + obj_desc = acpi_ns_get_attached_object ((struct acpi_namespace_node *) obj_desc); + } + break; + + default: + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + + /* + * If type is anything other than a reference, we are done + */ + if (type != ACPI_TYPE_LOCAL_REFERENCE) { + goto exit; + } + + /* + * For reference objects created via the ref_of or Index operators, + * we need to get to the base object (as per the ACPI specification + * of the object_type and size_of operators). This means traversing + * the list of possibly many nested references. + */ + while (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_LOCAL_REFERENCE) { + switch (obj_desc->reference.opcode) { + case AML_REF_OF_OP: + + /* Dereference the reference pointer */ + + node = obj_desc->reference.object; + + /* All "References" point to a NS node */ + + if (ACPI_GET_DESCRIPTOR_TYPE (node) != ACPI_DESC_TYPE_NAMED) { + ACPI_REPORT_ERROR (("acpi_ex_resolve_multiple: Not a NS node %p [%s]\n", + node, acpi_ut_get_descriptor_name (node))); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* Get the attached object */ + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + /* No object, use the NS node type */ + + type = acpi_ns_get_type (node); + goto exit; + } + + /* Check for circular references */ + + if (obj_desc == operand) { + return_ACPI_STATUS (AE_AML_CIRCULAR_REFERENCE); + } + break; + + + case AML_INDEX_OP: + + /* Get the type of this reference (index into another object) */ + + type = obj_desc->reference.target_type; + if (type != ACPI_TYPE_PACKAGE) { + goto exit; + } + + /* + * The main object is a package, we want to get the type + * of the individual package element that is referenced by + * the index. + * + * This could of course in turn be another reference object. + */ + obj_desc = *(obj_desc->reference.where); + if (!obj_desc) { + /* NULL package elements are allowed */ + + type = 0; /* Uninitialized */ + goto exit; + } + break; + + + case AML_INT_NAMEPATH_OP: + + /* Dereference the reference pointer */ + + node = obj_desc->reference.node; + + /* All "References" point to a NS node */ + + if (ACPI_GET_DESCRIPTOR_TYPE (node) != ACPI_DESC_TYPE_NAMED) { + ACPI_REPORT_ERROR (("acpi_ex_resolve_multiple: Not a NS node %p [%s]\n", + node, acpi_ut_get_descriptor_name (node))); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* Get the attached object */ + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + /* No object, use the NS node type */ + + type = acpi_ns_get_type (node); + goto exit; + } + + /* Check for circular references */ + + if (obj_desc == operand) { + return_ACPI_STATUS (AE_AML_CIRCULAR_REFERENCE); + } + break; + + + case AML_LOCAL_OP: + case AML_ARG_OP: + + if (return_desc) { + status = acpi_ds_method_data_get_value (obj_desc->reference.opcode, + obj_desc->reference.offset, walk_state, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + acpi_ut_remove_reference (obj_desc); + } + else { + status = acpi_ds_method_data_get_node (obj_desc->reference.opcode, + obj_desc->reference.offset, walk_state, &node); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + type = ACPI_TYPE_ANY; + goto exit; + } + } + break; + + + case AML_DEBUG_OP: + + /* The Debug Object is of type "debug_object" */ + + type = ACPI_TYPE_DEBUG_OBJECT; + goto exit; + + + default: + + ACPI_REPORT_ERROR (("acpi_ex_resolve_multiple: Unknown Reference subtype %X\n", + obj_desc->reference.opcode)); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + } + + /* + * Now we are guaranteed to have an object that has not been created + * via the ref_of or Index operators. + */ + type = ACPI_GET_OBJECT_TYPE (obj_desc); + + +exit: + /* Convert internal types to external types */ + + switch (type) { + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + type = ACPI_TYPE_FIELD_UNIT; + break; + + case ACPI_TYPE_LOCAL_SCOPE: + + /* Per ACPI Specification, Scope is untyped */ + + type = ACPI_TYPE_ANY; + break; + + default: + /* No change to Type required */ + break; + } + + *return_type = type; + if (return_desc) { + *return_desc = obj_desc; + } + return_ACPI_STATUS (AE_OK); +} + + diff --git a/drivers/acpi/executer/exresop.c b/drivers/acpi/executer/exresop.c new file mode 100644 index 00000000000..c92890220c3 --- /dev/null +++ b/drivers/acpi/executer/exresop.c @@ -0,0 +1,661 @@ + +/****************************************************************************** + * + * Module Name: exresop - AML Interpreter operand/object resolution + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/amlcode.h> +#include <acpi/acparser.h> +#include <acpi/acinterp.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exresop") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_check_object_type + * + * PARAMETERS: type_needed Object type needed + * this_type Actual object type + * Object Object pointer + * + * RETURN: Status + * + * DESCRIPTION: Check required type against actual type + * + ******************************************************************************/ + +acpi_status +acpi_ex_check_object_type ( + acpi_object_type type_needed, + acpi_object_type this_type, + void *object) +{ + ACPI_FUNCTION_NAME ("ex_check_object_type"); + + + if (type_needed == ACPI_TYPE_ANY) { + /* All types OK, so we don't perform any typechecks */ + + return (AE_OK); + } + + if (type_needed == ACPI_TYPE_LOCAL_REFERENCE) { + /* + * Allow the AML "Constant" opcodes (Zero, One, etc.) to be reference + * objects and thus allow them to be targets. (As per the ACPI + * specification, a store to a constant is a noop.) + */ + if ((this_type == ACPI_TYPE_INTEGER) && + (((union acpi_operand_object *) object)->common.flags & AOPOBJ_AML_CONSTANT)) { + return (AE_OK); + } + } + + if (type_needed != this_type) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed [%s], found [%s] %p\n", + acpi_ut_get_type_name (type_needed), + acpi_ut_get_type_name (this_type), object)); + + return (AE_AML_OPERAND_TYPE); + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_operands + * + * PARAMETERS: Opcode - Opcode being interpreted + * stack_ptr - Pointer to the operand stack to be + * resolved + * walk_state - Current state + * + * RETURN: Status + * + * DESCRIPTION: Convert multiple input operands to the types required by the + * target operator. + * + * Each 5-bit group in arg_types represents one required + * operand and indicates the required Type. The corresponding operand + * will be converted to the required type if possible, otherwise we + * abort with an exception. + * + ******************************************************************************/ + +acpi_status +acpi_ex_resolve_operands ( + u16 opcode, + union acpi_operand_object **stack_ptr, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *obj_desc; + acpi_status status = AE_OK; + u8 object_type; + void *temp_node; + u32 arg_types; + const struct acpi_opcode_info *op_info; + u32 this_arg_type; + acpi_object_type type_needed; + + + ACPI_FUNCTION_TRACE_U32 ("ex_resolve_operands", opcode); + + + op_info = acpi_ps_get_opcode_info (opcode); + if (op_info->class == AML_CLASS_UNKNOWN) { + return_ACPI_STATUS (AE_AML_BAD_OPCODE); + } + + arg_types = op_info->runtime_args; + if (arg_types == ARGI_INVALID_OPCODE) { + ACPI_REPORT_ERROR (("resolve_operands: %X is not a valid AML opcode\n", + opcode)); + + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Opcode %X [%s] required_operand_types=%8.8X \n", + opcode, op_info->name, arg_types)); + + /* + * Normal exit is with (arg_types == 0) at end of argument list. + * Function will return an exception from within the loop upon + * finding an entry which is not (or cannot be converted + * to) the required type; if stack underflows; or upon + * finding a NULL stack entry (which should not happen). + */ + while (GET_CURRENT_ARG_TYPE (arg_types)) { + if (!stack_ptr || !*stack_ptr) { + ACPI_REPORT_ERROR (("resolve_operands: Null stack entry at %p\n", + stack_ptr)); + + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* Extract useful items */ + + obj_desc = *stack_ptr; + + /* Decode the descriptor type */ + + switch (ACPI_GET_DESCRIPTOR_TYPE (obj_desc)) { + case ACPI_DESC_TYPE_NAMED: + + /* Node */ + + object_type = ((struct acpi_namespace_node *) obj_desc)->type; + break; + + + case ACPI_DESC_TYPE_OPERAND: + + /* ACPI internal object */ + + object_type = ACPI_GET_OBJECT_TYPE (obj_desc); + + /* Check for bad acpi_object_type */ + + if (!acpi_ut_valid_object_type (object_type)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Bad operand object type [%X]\n", + object_type)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + if (object_type == (u8) ACPI_TYPE_LOCAL_REFERENCE) { + /* + * Decode the Reference + */ + op_info = acpi_ps_get_opcode_info (opcode); + if (op_info->class == AML_CLASS_UNKNOWN) { + return_ACPI_STATUS (AE_AML_BAD_OPCODE); + } + + switch (obj_desc->reference.opcode) { + case AML_DEBUG_OP: + case AML_NAME_OP: + case AML_INDEX_OP: + case AML_REF_OF_OP: + case AML_ARG_OP: + case AML_LOCAL_OP: + case AML_LOAD_OP: /* ddb_handle from LOAD_OP or LOAD_TABLE_OP */ + + ACPI_DEBUG_ONLY_MEMBERS (ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Operand is a Reference, ref_opcode [%s]\n", + (acpi_ps_get_opcode_info (obj_desc->reference.opcode))->name))); + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Operand is a Reference, Unknown Reference Opcode %X [%s]\n", + obj_desc->reference.opcode, + (acpi_ps_get_opcode_info (obj_desc->reference.opcode))->name)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + } + break; + + + default: + + /* Invalid descriptor */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Invalid descriptor %p [%s]\n", + obj_desc, acpi_ut_get_descriptor_name (obj_desc))); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + + /* + * Get one argument type, point to the next + */ + this_arg_type = GET_CURRENT_ARG_TYPE (arg_types); + INCREMENT_ARG_LIST (arg_types); + + /* + * Handle cases where the object does not need to be + * resolved to a value + */ + switch (this_arg_type) { + case ARGI_REF_OR_STRING: /* Can be a String or Reference */ + + if ((ACPI_GET_DESCRIPTOR_TYPE (obj_desc) == ACPI_DESC_TYPE_OPERAND) && + (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_STRING)) { + /* + * String found - the string references a named object and must be + * resolved to a node + */ + goto next_operand; + } + + /* Else not a string - fall through to the normal Reference case below */ + /*lint -fallthrough */ + + case ARGI_REFERENCE: /* References: */ + case ARGI_INTEGER_REF: + case ARGI_OBJECT_REF: + case ARGI_DEVICE_REF: + case ARGI_TARGETREF: /* Allows implicit conversion rules before store */ + case ARGI_FIXED_TARGET: /* No implicit conversion before store to target */ + case ARGI_SIMPLE_TARGET: /* Name, Local, or Arg - no implicit conversion */ + + /* Need an operand of type ACPI_TYPE_LOCAL_REFERENCE */ + + if (ACPI_GET_DESCRIPTOR_TYPE (obj_desc) == ACPI_DESC_TYPE_NAMED) /* Node (name) ptr OK as-is */ { + goto next_operand; + } + + status = acpi_ex_check_object_type (ACPI_TYPE_LOCAL_REFERENCE, + object_type, obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (AML_NAME_OP == obj_desc->reference.opcode) { + /* + * Convert an indirect name ptr to direct name ptr and put + * it on the stack + */ + temp_node = obj_desc->reference.object; + acpi_ut_remove_reference (obj_desc); + (*stack_ptr) = temp_node; + } + goto next_operand; + + + case ARGI_DATAREFOBJ: /* Store operator only */ + + /* + * We don't want to resolve index_op reference objects during + * a store because this would be an implicit de_ref_of operation. + * Instead, we just want to store the reference object. + * -- All others must be resolved below. + */ + if ((opcode == AML_STORE_OP) && + (ACPI_GET_OBJECT_TYPE (*stack_ptr) == ACPI_TYPE_LOCAL_REFERENCE) && + ((*stack_ptr)->reference.opcode == AML_INDEX_OP)) { + goto next_operand; + } + break; + + default: + /* All cases covered above */ + break; + } + + + /* + * Resolve this object to a value + */ + status = acpi_ex_resolve_to_value (stack_ptr, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Get the resolved object */ + + obj_desc = *stack_ptr; + + /* + * Check the resulting object (value) type + */ + switch (this_arg_type) { + /* + * For the simple cases, only one type of resolved object + * is allowed + */ + case ARGI_MUTEX: + + /* Need an operand of type ACPI_TYPE_MUTEX */ + + type_needed = ACPI_TYPE_MUTEX; + break; + + case ARGI_EVENT: + + /* Need an operand of type ACPI_TYPE_EVENT */ + + type_needed = ACPI_TYPE_EVENT; + break; + + case ARGI_PACKAGE: /* Package */ + + /* Need an operand of type ACPI_TYPE_PACKAGE */ + + type_needed = ACPI_TYPE_PACKAGE; + break; + + case ARGI_ANYTYPE: + + /* Any operand type will do */ + + type_needed = ACPI_TYPE_ANY; + break; + + case ARGI_DDBHANDLE: + + /* Need an operand of type ACPI_TYPE_DDB_HANDLE */ + + type_needed = ACPI_TYPE_LOCAL_REFERENCE; + break; + + + /* + * The more complex cases allow multiple resolved object types + */ + case ARGI_INTEGER: /* Number */ + + /* + * Need an operand of type ACPI_TYPE_INTEGER, + * But we can implicitly convert from a STRING or BUFFER + * Aka - "Implicit Source Operand Conversion" + */ + status = acpi_ex_convert_to_integer (obj_desc, stack_ptr, 16); + if (ACPI_FAILURE (status)) { + if (status == AE_TYPE) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed [Integer/String/Buffer], found [%s] %p\n", + acpi_ut_get_object_type_name (obj_desc), obj_desc)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + return_ACPI_STATUS (status); + } + goto next_operand; + + + case ARGI_BUFFER: + + /* + * Need an operand of type ACPI_TYPE_BUFFER, + * But we can implicitly convert from a STRING or INTEGER + * Aka - "Implicit Source Operand Conversion" + */ + status = acpi_ex_convert_to_buffer (obj_desc, stack_ptr); + if (ACPI_FAILURE (status)) { + if (status == AE_TYPE) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed [Integer/String/Buffer], found [%s] %p\n", + acpi_ut_get_object_type_name (obj_desc), obj_desc)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + return_ACPI_STATUS (status); + } + goto next_operand; + + + case ARGI_STRING: + + /* + * Need an operand of type ACPI_TYPE_STRING, + * But we can implicitly convert from a BUFFER or INTEGER + * Aka - "Implicit Source Operand Conversion" + */ + status = acpi_ex_convert_to_string (obj_desc, stack_ptr, + ACPI_IMPLICIT_CONVERT_HEX); + if (ACPI_FAILURE (status)) { + if (status == AE_TYPE) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed [Integer/String/Buffer], found [%s] %p\n", + acpi_ut_get_object_type_name (obj_desc), obj_desc)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + return_ACPI_STATUS (status); + } + goto next_operand; + + + case ARGI_COMPUTEDATA: + + /* Need an operand of type INTEGER, STRING or BUFFER */ + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* Valid operand */ + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed [Integer/String/Buffer], found [%s] %p\n", + acpi_ut_get_object_type_name (obj_desc), obj_desc)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + goto next_operand; + + + case ARGI_BUFFER_OR_STRING: + + /* Need an operand of type STRING or BUFFER */ + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* Valid operand */ + break; + + case ACPI_TYPE_INTEGER: + + /* Highest priority conversion is to type Buffer */ + + status = acpi_ex_convert_to_buffer (obj_desc, stack_ptr); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed [Integer/String/Buffer], found [%s] %p\n", + acpi_ut_get_object_type_name (obj_desc), obj_desc)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + goto next_operand; + + + case ARGI_DATAOBJECT: + /* + * ARGI_DATAOBJECT is only used by the size_of operator. + * Need a buffer, string, package, or ref_of reference. + * + * The only reference allowed here is a direct reference to + * a namespace node. + */ + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_PACKAGE: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_LOCAL_REFERENCE: + + /* Valid operand */ + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed [Buffer/String/Package/Reference], found [%s] %p\n", + acpi_ut_get_object_type_name (obj_desc), obj_desc)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + goto next_operand; + + + case ARGI_COMPLEXOBJ: + + /* Need a buffer or package or (ACPI 2.0) String */ + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_PACKAGE: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* Valid operand */ + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed [Buffer/String/Package], found [%s] %p\n", + acpi_ut_get_object_type_name (obj_desc), obj_desc)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + goto next_operand; + + + case ARGI_REGION_OR_FIELD: + + /* Need an operand of type ACPI_TYPE_REGION or a FIELD in a region */ + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_REGION: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + /* Valid operand */ + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed [Region/region_field], found [%s] %p\n", + acpi_ut_get_object_type_name (obj_desc), obj_desc)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + goto next_operand; + + + case ARGI_DATAREFOBJ: + + /* Used by the Store() operator only */ + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_PACKAGE: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REFERENCE: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + case ACPI_TYPE_DDB_HANDLE: + + /* Valid operand */ + break; + + default: + + if (acpi_gbl_enable_interpreter_slack) { + /* + * Enable original behavior of Store(), allowing any and all + * objects as the source operand. The ACPI spec does not + * allow this, however. + */ + break; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Needed Integer/Buffer/String/Package/Ref/Ddb], found [%s] %p\n", + acpi_ut_get_object_type_name (obj_desc), obj_desc)); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + goto next_operand; + + + default: + + /* Unknown type */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Internal - Unknown ARGI (required operand) type %X\n", + this_arg_type)); + + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Make sure that the original object was resolved to the + * required object type (Simple cases only). + */ + status = acpi_ex_check_object_type (type_needed, + ACPI_GET_OBJECT_TYPE (*stack_ptr), *stack_ptr); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + +next_operand: + /* + * If more operands needed, decrement stack_ptr to point + * to next operand on stack + */ + if (GET_CURRENT_ARG_TYPE (arg_types)) { + stack_ptr--; + } + + } /* while (*Types) */ + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exstore.c b/drivers/acpi/executer/exstore.c new file mode 100644 index 00000000000..e0fc6aba125 --- /dev/null +++ b/drivers/acpi/executer/exstore.c @@ -0,0 +1,536 @@ + +/****************************************************************************** + * + * Module Name: exstore - AML Interpreter object store support + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exstore") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store + * + * PARAMETERS: *source_desc - Value to be stored + * *dest_desc - Where to store it. Must be an NS node + * or an union acpi_operand_object of type + * Reference; + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Store the value described by source_desc into the location + * described by dest_desc. Called by various interpreter + * functions to store the result of an operation into + * the destination operand -- not just simply the actual "Store" + * ASL operator. + * + ******************************************************************************/ + +acpi_status +acpi_ex_store ( + union acpi_operand_object *source_desc, + union acpi_operand_object *dest_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *ref_desc = dest_desc; + + + ACPI_FUNCTION_TRACE_PTR ("ex_store", dest_desc); + + + /* Validate parameters */ + + if (!source_desc || !dest_desc) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null parameter\n")); + return_ACPI_STATUS (AE_AML_NO_OPERAND); + } + + /* dest_desc can be either a namespace node or an ACPI object */ + + if (ACPI_GET_DESCRIPTOR_TYPE (dest_desc) == ACPI_DESC_TYPE_NAMED) { + /* + * Dest is a namespace node, + * Storing an object into a Named node. + */ + status = acpi_ex_store_object_to_node (source_desc, + (struct acpi_namespace_node *) dest_desc, walk_state, + ACPI_IMPLICIT_CONVERSION); + + return_ACPI_STATUS (status); + } + + /* Destination object must be a Reference or a Constant object */ + + switch (ACPI_GET_OBJECT_TYPE (dest_desc)) { + case ACPI_TYPE_LOCAL_REFERENCE: + break; + + case ACPI_TYPE_INTEGER: + + /* Allow stores to Constants -- a Noop as per ACPI spec */ + + if (dest_desc->common.flags & AOPOBJ_AML_CONSTANT) { + return_ACPI_STATUS (AE_OK); + } + + /*lint -fallthrough */ + + default: + + /* Destination is not a Reference object */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Target is not a Reference or Constant object - %s [%p]\n", + acpi_ut_get_object_type_name (dest_desc), dest_desc)); + + ACPI_DUMP_STACK_ENTRY (source_desc); + ACPI_DUMP_STACK_ENTRY (dest_desc); + ACPI_DUMP_OPERANDS (&dest_desc, ACPI_IMODE_EXECUTE, "ex_store", + 2, "Target is not a Reference or Constant object"); + + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* + * Examine the Reference opcode. These cases are handled: + * + * 1) Store to Name (Change the object associated with a name) + * 2) Store to an indexed area of a Buffer or Package + * 3) Store to a Method Local or Arg + * 4) Store to the debug object + */ + switch (ref_desc->reference.opcode) { + case AML_NAME_OP: + case AML_REF_OF_OP: + + /* Storing an object into a Name "container" */ + + status = acpi_ex_store_object_to_node (source_desc, ref_desc->reference.object, + walk_state, ACPI_IMPLICIT_CONVERSION); + break; + + + case AML_INDEX_OP: + + /* Storing to an Index (pointer into a packager or buffer) */ + + status = acpi_ex_store_object_to_index (source_desc, ref_desc, walk_state); + break; + + + case AML_LOCAL_OP: + case AML_ARG_OP: + + /* Store to a method local/arg */ + + status = acpi_ds_store_object_to_local (ref_desc->reference.opcode, + ref_desc->reference.offset, source_desc, walk_state); + break; + + + case AML_DEBUG_OP: + + /* + * Storing to the Debug object causes the value stored to be + * displayed and otherwise has no effect -- see ACPI Specification + */ + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "**** Write to Debug Object: Object %p %s ****:\n\n", + source_desc, acpi_ut_get_object_type_name (source_desc))); + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DEBUG_OBJECT, "[ACPI Debug] %s: ", + acpi_ut_get_object_type_name (source_desc))); + + if (!acpi_ut_valid_internal_object (source_desc)) { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DEBUG_OBJECT, + "%p, Invalid Internal Object!\n", source_desc)); + break; + } + + switch (ACPI_GET_OBJECT_TYPE (source_desc)) { + case ACPI_TYPE_INTEGER: + + if (acpi_gbl_integer_byte_width == 4) { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DEBUG_OBJECT, "0x%8.8X\n", + (u32) source_desc->integer.value)); + } + else { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DEBUG_OBJECT, "0x%8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (source_desc->integer.value))); + } + break; + + + case ACPI_TYPE_BUFFER: + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DEBUG_OBJECT, "[0x%.2X]", + (u32) source_desc->buffer.length)); + ACPI_DUMP_BUFFER (source_desc->buffer.pointer, + (source_desc->buffer.length < 32) ? source_desc->buffer.length : 32); + break; + + + case ACPI_TYPE_STRING: + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DEBUG_OBJECT, "[0x%.2X] \"%s\"\n", + source_desc->string.length, source_desc->string.pointer)); + break; + + + case ACPI_TYPE_PACKAGE: + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DEBUG_OBJECT, "[0x%.2X] Elements Ptr - %p\n", + source_desc->package.count, source_desc->package.elements)); + break; + + + default: + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_DEBUG_OBJECT, "%p\n", + source_desc)); + break; + } + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_EXEC, "\n")); + break; + + + default: + + ACPI_REPORT_ERROR (("ex_store: Unknown Reference opcode %X\n", + ref_desc->reference.opcode)); + ACPI_DUMP_ENTRY (ref_desc, ACPI_LV_ERROR); + + status = AE_AML_INTERNAL; + break; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_object_to_index + * + * PARAMETERS: *source_desc - Value to be stored + * *dest_desc - Named object to receive the value + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Store the object to indexed Buffer or Package element + * + ******************************************************************************/ + +acpi_status +acpi_ex_store_object_to_index ( + union acpi_operand_object *source_desc, + union acpi_operand_object *index_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_operand_object *obj_desc; + union acpi_operand_object *new_desc; + u8 value = 0; + u32 i; + + + ACPI_FUNCTION_TRACE ("ex_store_object_to_index"); + + + /* + * Destination must be a reference pointer, and + * must point to either a buffer or a package + */ + switch (index_desc->reference.target_type) { + case ACPI_TYPE_PACKAGE: + /* + * Storing to a package element. Copy the object and replace + * any existing object with the new object. No implicit + * conversion is performed. + * + * The object at *(index_desc->Reference.Where) is the + * element within the package that is to be modified. + * The parent package object is at index_desc->Reference.Object + */ + obj_desc = *(index_desc->reference.where); + + status = acpi_ut_copy_iobject_to_iobject (source_desc, &new_desc, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (obj_desc) { + /* Decrement reference count by the ref count of the parent package */ + + for (i = 0; i < ((union acpi_operand_object *) index_desc->reference.object)->common.reference_count; i++) { + acpi_ut_remove_reference (obj_desc); + } + } + + *(index_desc->reference.where) = new_desc; + + /* Increment reference count by the ref count of the parent package -1 */ + + for (i = 1; i < ((union acpi_operand_object *) index_desc->reference.object)->common.reference_count; i++) { + acpi_ut_add_reference (new_desc); + } + + break; + + + case ACPI_TYPE_BUFFER_FIELD: + + /* + * Store into a Buffer or String (not actually a real buffer_field) + * at a location defined by an Index. + * + * The first 8-bit element of the source object is written to the + * 8-bit Buffer location defined by the Index destination object, + * according to the ACPI 2.0 specification. + */ + + /* + * Make sure the target is a Buffer or String. An error should + * not happen here, since the reference_object was constructed + * by the INDEX_OP code. + */ + obj_desc = index_desc->reference.object; + if ((ACPI_GET_OBJECT_TYPE (obj_desc) != ACPI_TYPE_BUFFER) && + (ACPI_GET_OBJECT_TYPE (obj_desc) != ACPI_TYPE_STRING)) { + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* + * The assignment of the individual elements will be slightly + * different for each source type. + */ + switch (ACPI_GET_OBJECT_TYPE (source_desc)) { + case ACPI_TYPE_INTEGER: + + /* Use the least-significant byte of the integer */ + + value = (u8) (source_desc->integer.value); + break; + + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_STRING: + + /* Note: Takes advantage of common string/buffer fields */ + + value = source_desc->buffer.pointer[0]; + break; + + default: + + /* All other types are invalid */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Source must be Integer/Buffer/String type, not %s\n", + acpi_ut_get_object_type_name (source_desc))); + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* Store the source value into the target buffer byte */ + + obj_desc->buffer.pointer[index_desc->reference.offset] = value; + break; + + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Target is not a Package or buffer_field\n")); + status = AE_AML_OPERAND_TYPE; + break; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_object_to_node + * + * PARAMETERS: source_desc - Value to be stored + * Node - Named object to receive the value + * walk_state - Current walk state + * implicit_conversion - Perform implicit conversion (yes/no) + * + * RETURN: Status + * + * DESCRIPTION: Store the object to the named object. + * + * The Assignment of an object to a named object is handled here + * The value passed in will replace the current value (if any) + * with the input value. + * + * When storing into an object the data is converted to the + * target object type then stored in the object. This means + * that the target object type (for an initialized target) will + * not be changed by a store operation. + * + * Assumes parameters are already validated. + * + ******************************************************************************/ + +acpi_status +acpi_ex_store_object_to_node ( + union acpi_operand_object *source_desc, + struct acpi_namespace_node *node, + struct acpi_walk_state *walk_state, + u8 implicit_conversion) +{ + acpi_status status = AE_OK; + union acpi_operand_object *target_desc; + union acpi_operand_object *new_desc; + acpi_object_type target_type; + + + ACPI_FUNCTION_TRACE_PTR ("ex_store_object_to_node", source_desc); + + + /* + * Get current type of the node, and object attached to Node + */ + target_type = acpi_ns_get_type (node); + target_desc = acpi_ns_get_attached_object (node); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Storing %p(%s) into node %p(%s)\n", + source_desc, acpi_ut_get_object_type_name (source_desc), + node, acpi_ut_get_type_name (target_type))); + + /* + * Resolve the source object to an actual value + * (If it is a reference object) + */ + status = acpi_ex_resolve_object (&source_desc, target_type, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* If no implicit conversion, drop into the default case below */ + + if (!implicit_conversion) { + /* Force execution of default (no implicit conversion) */ + + target_type = ACPI_TYPE_ANY; + } + + /* + * Do the actual store operation + */ + switch (target_type) { + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + /* + * For fields, copy the source data to the target field. + */ + status = acpi_ex_write_data_to_field (source_desc, target_desc, &walk_state->result_obj); + break; + + + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* + * These target types are all of type Integer/String/Buffer, and + * therefore support implicit conversion before the store. + * + * Copy and/or convert the source object to a new target object + */ + status = acpi_ex_store_object_to_object (source_desc, target_desc, &new_desc, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (new_desc != target_desc) { + /* + * Store the new new_desc as the new value of the Name, and set + * the Name's type to that of the value being stored in it. + * source_desc reference count is incremented by attach_object. + * + * Note: This may change the type of the node if an explicit store + * has been performed such that the node/object type has been + * changed. + */ + status = acpi_ns_attach_object (node, new_desc, new_desc->common.type); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Store %s into %s via Convert/Attach\n", + acpi_ut_get_object_type_name (source_desc), + acpi_ut_get_object_type_name (new_desc))); + } + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Storing %s (%p) directly into node (%p), no implicit conversion\n", + acpi_ut_get_object_type_name (source_desc), source_desc, node)); + + /* No conversions for all other types. Just attach the source object */ + + status = acpi_ns_attach_object (node, source_desc, ACPI_GET_OBJECT_TYPE (source_desc)); + break; + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exstoren.c b/drivers/acpi/executer/exstoren.c new file mode 100644 index 00000000000..d3677feb07f --- /dev/null +++ b/drivers/acpi/executer/exstoren.c @@ -0,0 +1,306 @@ + +/****************************************************************************** + * + * Module Name: exstoren - AML Interpreter object store support, + * Store to Node (namespace object) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exstoren") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_resolve_object + * + * PARAMETERS: source_desc_ptr - Pointer to the source object + * target_type - Current type of the target + * walk_state - Current walk state + * + * RETURN: Status, resolved object in source_desc_ptr. + * + * DESCRIPTION: Resolve an object. If the object is a reference, dereference + * it and return the actual object in the source_desc_ptr. + * + ******************************************************************************/ + +acpi_status +acpi_ex_resolve_object ( + union acpi_operand_object **source_desc_ptr, + acpi_object_type target_type, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *source_desc = *source_desc_ptr; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ex_resolve_object"); + + + /* + * Ensure we have a Target that can be stored to + */ + switch (target_type) { + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + /* + * These cases all require only Integers or values that + * can be converted to Integers (Strings or Buffers) + */ + + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + + /* + * Stores into a Field/Region or into a Integer/Buffer/String + * are all essentially the same. This case handles the + * "interchangeable" types Integer, String, and Buffer. + */ + if (ACPI_GET_OBJECT_TYPE (source_desc) == ACPI_TYPE_LOCAL_REFERENCE) { + /* Resolve a reference object first */ + + status = acpi_ex_resolve_to_value (source_desc_ptr, walk_state); + if (ACPI_FAILURE (status)) { + break; + } + } + + /* For copy_object, no further validation necessary */ + + if (walk_state->opcode == AML_COPY_OP) { + break; + } + + /* + * Must have a Integer, Buffer, or String + */ + if ((ACPI_GET_OBJECT_TYPE (source_desc) != ACPI_TYPE_INTEGER) && + (ACPI_GET_OBJECT_TYPE (source_desc) != ACPI_TYPE_BUFFER) && + (ACPI_GET_OBJECT_TYPE (source_desc) != ACPI_TYPE_STRING) && + !((ACPI_GET_OBJECT_TYPE (source_desc) == ACPI_TYPE_LOCAL_REFERENCE) && (source_desc->reference.opcode == AML_LOAD_OP))) { + /* + * Conversion successful but still not a valid type + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Cannot assign type %s to %s (must be type Int/Str/Buf)\n", + acpi_ut_get_object_type_name (source_desc), + acpi_ut_get_type_name (target_type))); + status = AE_AML_OPERAND_TYPE; + } + break; + + + case ACPI_TYPE_LOCAL_ALIAS: + case ACPI_TYPE_LOCAL_METHOD_ALIAS: + + /* + * Aliases are resolved by acpi_ex_prep_operands + */ + ACPI_REPORT_ERROR (("Store into Alias - should never happen\n")); + status = AE_AML_INTERNAL; + break; + + + case ACPI_TYPE_PACKAGE: + default: + + /* + * All other types than Alias and the various Fields come here, + * including the untyped case - ACPI_TYPE_ANY. + */ + break; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_object_to_object + * + * PARAMETERS: source_desc - Object to store + * dest_desc - Object to receive a copy of the source + * new_desc - New object if dest_desc is obsoleted + * walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: "Store" an object to another object. This may include + * converting the source type to the target type (implicit + * conversion), and a copy of the value of the source to + * the target. + * + * The Assignment of an object to another (not named) object + * is handled here. + * The Source passed in will replace the current value (if any) + * with the input value. + * + * When storing into an object the data is converted to the + * target object type then stored in the object. This means + * that the target object type (for an initialized target) will + * not be changed by a store operation. + * + * This module allows destination types of Number, String, + * Buffer, and Package. + * + * Assumes parameters are already validated. NOTE: source_desc + * resolution (from a reference object) must be performed by + * the caller if necessary. + * + ******************************************************************************/ + +acpi_status +acpi_ex_store_object_to_object ( + union acpi_operand_object *source_desc, + union acpi_operand_object *dest_desc, + union acpi_operand_object **new_desc, + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *actual_src_desc; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_PTR ("ex_store_object_to_object", source_desc); + + + actual_src_desc = source_desc; + if (!dest_desc) { + /* + * There is no destination object (An uninitialized node or + * package element), so we can simply copy the source object + * creating a new destination object + */ + status = acpi_ut_copy_iobject_to_iobject (actual_src_desc, new_desc, walk_state); + return_ACPI_STATUS (status); + } + + if (ACPI_GET_OBJECT_TYPE (source_desc) != ACPI_GET_OBJECT_TYPE (dest_desc)) { + /* + * The source type does not match the type of the destination. + * Perform the "implicit conversion" of the source to the current type + * of the target as per the ACPI specification. + * + * If no conversion performed, actual_src_desc = source_desc. + * Otherwise, actual_src_desc is a temporary object to hold the + * converted object. + */ + status = acpi_ex_convert_to_target_type (ACPI_GET_OBJECT_TYPE (dest_desc), + source_desc, &actual_src_desc, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (source_desc == actual_src_desc) { + /* + * No conversion was performed. Return the source_desc as the + * new object. + */ + *new_desc = source_desc; + return_ACPI_STATUS (AE_OK); + } + } + + /* + * We now have two objects of identical types, and we can perform a + * copy of the *value* of the source object. + */ + switch (ACPI_GET_OBJECT_TYPE (dest_desc)) { + case ACPI_TYPE_INTEGER: + + dest_desc->integer.value = actual_src_desc->integer.value; + + /* Truncate value if we are executing from a 32-bit ACPI table */ + + acpi_ex_truncate_for32bit_table (dest_desc); + break; + + case ACPI_TYPE_STRING: + + status = acpi_ex_store_string_to_string (actual_src_desc, dest_desc); + break; + + case ACPI_TYPE_BUFFER: + + /* + * Note: There is different store behavior depending on the original + * source type + */ + status = acpi_ex_store_buffer_to_buffer (actual_src_desc, dest_desc); + break; + + case ACPI_TYPE_PACKAGE: + + status = acpi_ut_copy_iobject_to_iobject (actual_src_desc, &dest_desc, + walk_state); + break; + + default: + /* + * All other types come here. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Store into type %s not implemented\n", + acpi_ut_get_object_type_name (dest_desc))); + + status = AE_NOT_IMPLEMENTED; + break; + } + + if (actual_src_desc != source_desc) { + /* Delete the intermediate (temporary) source object */ + + acpi_ut_remove_reference (actual_src_desc); + } + + *new_desc = dest_desc; + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/executer/exstorob.c b/drivers/acpi/executer/exstorob.c new file mode 100644 index 00000000000..05e1ecae8d9 --- /dev/null +++ b/drivers/acpi/executer/exstorob.c @@ -0,0 +1,216 @@ + +/****************************************************************************** + * + * Module Name: exstorob - AML Interpreter object store support, store to object + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> + + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exstorob") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_buffer_to_buffer + * + * PARAMETERS: source_desc - Source object to copy + * target_desc - Destination object of the copy + * + * RETURN: Status + * + * DESCRIPTION: Copy a buffer object to another buffer object. + * + ******************************************************************************/ + +acpi_status +acpi_ex_store_buffer_to_buffer ( + union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc) +{ + u32 length; + u8 *buffer; + + + ACPI_FUNCTION_TRACE_PTR ("ex_store_buffer_to_buffer", source_desc); + + + /* We know that source_desc is a buffer by now */ + + buffer = (u8 *) source_desc->buffer.pointer; + length = source_desc->buffer.length; + + /* + * If target is a buffer of length zero or is a static buffer, + * allocate a new buffer of the proper length + */ + if ((target_desc->buffer.length == 0) || + (target_desc->common.flags & AOPOBJ_STATIC_POINTER)) { + target_desc->buffer.pointer = ACPI_MEM_ALLOCATE (length); + if (!target_desc->buffer.pointer) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + target_desc->buffer.length = length; + } + + /* Copy source buffer to target buffer */ + + if (length <= target_desc->buffer.length) { + /* Clear existing buffer and copy in the new one */ + + ACPI_MEMSET (target_desc->buffer.pointer, 0, target_desc->buffer.length); + ACPI_MEMCPY (target_desc->buffer.pointer, buffer, length); + +#ifdef ACPI_OBSOLETE_BEHAVIOR + /* + * NOTE: ACPI versions up to 3.0 specified that the buffer must be + * truncated if the string is smaller than the buffer. However, "other" + * implementations of ACPI never did this and thus became the defacto + * standard. ACPi 3.0_a changes this behavior such that the buffer + * is no longer truncated. + */ + + /* + * OBSOLETE BEHAVIOR: + * If the original source was a string, we must truncate the buffer, + * according to the ACPI spec. Integer-to-Buffer and Buffer-to-Buffer + * copy must not truncate the original buffer. + */ + if (original_src_type == ACPI_TYPE_STRING) { + /* Set the new length of the target */ + + target_desc->buffer.length = length; + } +#endif + } + else { + /* Truncate the source, copy only what will fit */ + + ACPI_MEMCPY (target_desc->buffer.pointer, buffer, target_desc->buffer.length); + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Truncating source buffer from %X to %X\n", + length, target_desc->buffer.length)); + } + + /* Copy flags */ + + target_desc->buffer.flags = source_desc->buffer.flags; + target_desc->common.flags &= ~AOPOBJ_STATIC_POINTER; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_store_string_to_string + * + * PARAMETERS: source_desc - Source object to copy + * target_desc - Destination object of the copy + * + * RETURN: Status + * + * DESCRIPTION: Copy a String object to another String object + * + ******************************************************************************/ + +acpi_status +acpi_ex_store_string_to_string ( + union acpi_operand_object *source_desc, + union acpi_operand_object *target_desc) +{ + u32 length; + u8 *buffer; + + + ACPI_FUNCTION_TRACE_PTR ("ex_store_string_to_string", source_desc); + + + /* We know that source_desc is a string by now */ + + buffer = (u8 *) source_desc->string.pointer; + length = source_desc->string.length; + + /* + * Replace existing string value if it will fit and the string + * pointer is not a static pointer (part of an ACPI table) + */ + if ((length < target_desc->string.length) && + (!(target_desc->common.flags & AOPOBJ_STATIC_POINTER))) { + /* + * String will fit in existing non-static buffer. + * Clear old string and copy in the new one + */ + ACPI_MEMSET (target_desc->string.pointer, 0, (acpi_size) target_desc->string.length + 1); + ACPI_MEMCPY (target_desc->string.pointer, buffer, length); + } + else { + /* + * Free the current buffer, then allocate a new buffer + * large enough to hold the value + */ + if (target_desc->string.pointer && + (!(target_desc->common.flags & AOPOBJ_STATIC_POINTER))) { + /* Only free if not a pointer into the DSDT */ + + ACPI_MEM_FREE (target_desc->string.pointer); + } + + target_desc->string.pointer = ACPI_MEM_CALLOCATE ((acpi_size) length + 1); + if (!target_desc->string.pointer) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + target_desc->common.flags &= ~AOPOBJ_STATIC_POINTER; + ACPI_MEMCPY (target_desc->string.pointer, buffer, length); + } + + /* Set the new target length */ + + target_desc->string.length = length; + return_ACPI_STATUS (AE_OK); +} + + diff --git a/drivers/acpi/executer/exsystem.c b/drivers/acpi/executer/exsystem.c new file mode 100644 index 00000000000..f92efc51289 --- /dev/null +++ b/drivers/acpi/executer/exsystem.c @@ -0,0 +1,378 @@ + +/****************************************************************************** + * + * Module Name: exsystem - Interface to OS services + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/acevents.h> + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exsystem") + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_wait_semaphore + * + * PARAMETERS: Semaphore - OSD semaphore to wait on + * Timeout - Max time to wait + * + * RETURN: Status + * + * DESCRIPTION: Implements a semaphore wait with a check to see if the + * semaphore is available immediately. If it is not, the + * interpreter is released. + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_wait_semaphore ( + acpi_handle semaphore, + u16 timeout) +{ + acpi_status status; + acpi_status status2; + + + ACPI_FUNCTION_TRACE ("ex_system_wait_semaphore"); + + + status = acpi_os_wait_semaphore (semaphore, 1, 0); + if (ACPI_SUCCESS (status)) { + return_ACPI_STATUS (status); + } + + if (status == AE_TIME) { + /* We must wait, so unlock the interpreter */ + + acpi_ex_exit_interpreter (); + + status = acpi_os_wait_semaphore (semaphore, 1, timeout); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "*** Thread awake after blocking, %s\n", + acpi_format_exception (status))); + + /* Reacquire the interpreter */ + + status2 = acpi_ex_enter_interpreter (); + if (ACPI_FAILURE (status2)) { + /* Report fatal error, could not acquire interpreter */ + + return_ACPI_STATUS (status2); + } + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_do_stall + * + * PARAMETERS: how_long - The amount of time to stall, + * in microseconds + * + * RETURN: Status + * + * DESCRIPTION: Suspend running thread for specified amount of time. + * Note: ACPI specification requires that Stall() does not + * relinquish the processor, and delays longer than 100 usec + * should use Sleep() instead. We allow stalls up to 255 usec + * for compatibility with other interpreters and existing BIOSs. + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_do_stall ( + u32 how_long) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_ENTRY (); + + + if (how_long > 255) /* 255 microseconds */ { + /* + * Longer than 255 usec, this is an error + * + * (ACPI specifies 100 usec as max, but this gives some slack in + * order to support existing BIOSs) + */ + ACPI_REPORT_ERROR (("Stall: Time parameter is too large (%d)\n", how_long)); + status = AE_AML_OPERAND_VALUE; + } + else { + acpi_os_stall (how_long); + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_do_suspend + * + * PARAMETERS: how_long - The amount of time to suspend, + * in milliseconds + * + * RETURN: None + * + * DESCRIPTION: Suspend running thread for specified amount of time. + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_do_suspend ( + acpi_integer how_long) +{ + acpi_status status; + + + ACPI_FUNCTION_ENTRY (); + + + /* Since this thread will sleep, we must release the interpreter */ + + acpi_ex_exit_interpreter (); + + acpi_os_sleep (how_long); + + /* And now we must get the interpreter again */ + + status = acpi_ex_enter_interpreter (); + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_acquire_mutex + * + * PARAMETERS: *time_desc - The 'time to delay' object descriptor + * *obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. This function will cause a lock to be generated + * for the Mutex pointed to by obj_desc. + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_acquire_mutex ( + union acpi_operand_object *time_desc, + union acpi_operand_object *obj_desc) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_PTR ("ex_system_acquire_mutex", obj_desc); + + + if (!obj_desc) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Support for the _GL_ Mutex object -- go get the global lock + */ + if (obj_desc->mutex.semaphore == acpi_gbl_global_lock_semaphore) { + status = acpi_ev_acquire_global_lock ((u16) time_desc->integer.value); + return_ACPI_STATUS (status); + } + + status = acpi_ex_system_wait_semaphore (obj_desc->mutex.semaphore, + (u16) time_desc->integer.value); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_release_mutex + * + * PARAMETERS: *obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. This operation is a request to release a + * previously acquired Mutex. If the Mutex variable is set then + * it will be decremented. + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_release_mutex ( + union acpi_operand_object *obj_desc) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ex_system_release_mutex"); + + + if (!obj_desc) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Support for the _GL_ Mutex object -- release the global lock + */ + if (obj_desc->mutex.semaphore == acpi_gbl_global_lock_semaphore) { + status = acpi_ev_release_global_lock (); + return_ACPI_STATUS (status); + } + + status = acpi_os_signal_semaphore (obj_desc->mutex.semaphore, 1); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_signal_event + * + * PARAMETERS: *obj_desc - The object descriptor for this op + * + * RETURN: AE_OK + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_signal_event ( + union acpi_operand_object *obj_desc) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ex_system_signal_event"); + + + if (obj_desc) { + status = acpi_os_signal_semaphore (obj_desc->event.semaphore, 1); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_wait_event + * + * PARAMETERS: *time_desc - The 'time to delay' object descriptor + * *obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Provides an access point to perform synchronization operations + * within the AML. This operation is a request to wait for an + * event. + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_wait_event ( + union acpi_operand_object *time_desc, + union acpi_operand_object *obj_desc) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ex_system_wait_event"); + + + if (obj_desc) { + status = acpi_ex_system_wait_semaphore (obj_desc->event.semaphore, + (u16) time_desc->integer.value); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_system_reset_event + * + * PARAMETERS: *obj_desc - The object descriptor for this op + * + * RETURN: Status + * + * DESCRIPTION: Reset an event to a known state. + * + ******************************************************************************/ + +acpi_status +acpi_ex_system_reset_event ( + union acpi_operand_object *obj_desc) +{ + acpi_status status = AE_OK; + void *temp_semaphore; + + + ACPI_FUNCTION_ENTRY (); + + + /* + * We are going to simply delete the existing semaphore and + * create a new one! + */ + status = acpi_os_create_semaphore (ACPI_NO_UNIT_LIMIT, 0, &temp_semaphore); + if (ACPI_SUCCESS (status)) { + (void) acpi_os_delete_semaphore (obj_desc->event.semaphore); + obj_desc->event.semaphore = temp_semaphore; + } + + return (status); +} + diff --git a/drivers/acpi/executer/exutils.c b/drivers/acpi/executer/exutils.c new file mode 100644 index 00000000000..40c6abb8b49 --- /dev/null +++ b/drivers/acpi/executer/exutils.c @@ -0,0 +1,378 @@ + +/****************************************************************************** + * + * Module Name: exutils - interpreter/scanner utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +/* + * DEFINE_AML_GLOBALS is tested in amlcode.h + * to determine whether certain global names should be "defined" or only + * "declared" in the current compilation. This enhances maintainability + * by enabling a single header file to embody all knowledge of the names + * in question. + * + * Exactly one module of any executable should #define DEFINE_GLOBALS + * before #including the header files which use this convention. The + * names in question will be defined and initialized in that module, + * and declared as extern in all other modules which #include those + * header files. + */ + +#define DEFINE_AML_GLOBALS + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/amlcode.h> +#include <acpi/acevents.h> + +#define _COMPONENT ACPI_EXECUTER + ACPI_MODULE_NAME ("exutils") + + +#ifndef ACPI_NO_METHOD_EXECUTION + +/******************************************************************************* + * + * FUNCTION: acpi_ex_enter_interpreter + * + * PARAMETERS: None + * + * DESCRIPTION: Enter the interpreter execution region. Failure to enter + * the interpreter region is a fatal system error + * + ******************************************************************************/ + +acpi_status +acpi_ex_enter_interpreter (void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE ("ex_enter_interpreter"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_EXECUTE); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not acquire interpreter mutex\n")); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_exit_interpreter + * + * PARAMETERS: None + * + * DESCRIPTION: Exit the interpreter execution region + * + * Cases where the interpreter is unlocked: + * 1) Completion of the execution of a control method + * 2) Method blocked on a Sleep() AML opcode + * 3) Method blocked on an Acquire() AML opcode + * 4) Method blocked on a Wait() AML opcode + * 5) Method blocked to acquire the global lock + * 6) Method blocked to execute a serialized control method that is + * already executing + * 7) About to invoke a user-installed opregion handler + * + ******************************************************************************/ + +void +acpi_ex_exit_interpreter (void) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ex_exit_interpreter"); + + + status = acpi_ut_release_mutex (ACPI_MTX_EXECUTE); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not release interpreter mutex\n")); + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_truncate_for32bit_table + * + * PARAMETERS: obj_desc - Object to be truncated + * + * RETURN: none + * + * DESCRIPTION: Truncate a number to 32-bits if the currently executing method + * belongs to a 32-bit ACPI table. + * + ******************************************************************************/ + +void +acpi_ex_truncate_for32bit_table ( + union acpi_operand_object *obj_desc) +{ + + ACPI_FUNCTION_ENTRY (); + + + /* + * Object must be a valid number and we must be executing + * a control method + */ + if ((!obj_desc) || + (ACPI_GET_OBJECT_TYPE (obj_desc) != ACPI_TYPE_INTEGER)) { + return; + } + + if (acpi_gbl_integer_byte_width == 4) { + /* + * We are running a method that exists in a 32-bit ACPI table. + * Truncate the value to 32 bits by zeroing out the upper 32-bit field + */ + obj_desc->integer.value &= (acpi_integer) ACPI_UINT32_MAX; + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_acquire_global_lock + * + * PARAMETERS: field_flags - Flags with Lock rule: + * always_lock or never_lock + * + * RETURN: TRUE/FALSE indicating whether the lock was actually acquired + * + * DESCRIPTION: Obtain the global lock and keep track of this fact via two + * methods. A global variable keeps the state of the lock, and + * the state is returned to the caller. + * + ******************************************************************************/ + +u8 +acpi_ex_acquire_global_lock ( + u32 field_flags) +{ + u8 locked = FALSE; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ex_acquire_global_lock"); + + + /* Only attempt lock if the always_lock bit is set */ + + if (field_flags & AML_FIELD_LOCK_RULE_MASK) { + /* We should attempt to get the lock, wait forever */ + + status = acpi_ev_acquire_global_lock (ACPI_WAIT_FOREVER); + if (ACPI_SUCCESS (status)) { + locked = TRUE; + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not acquire Global Lock, %s\n", + acpi_format_exception (status))); + } + } + + return_VALUE (locked); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_release_global_lock + * + * PARAMETERS: locked_by_me - Return value from corresponding call to + * acquire_global_lock. + * + * RETURN: Status + * + * DESCRIPTION: Release the global lock if it is locked. + * + ******************************************************************************/ + +void +acpi_ex_release_global_lock ( + u8 locked_by_me) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ex_release_global_lock"); + + + /* Only attempt unlock if the caller locked it */ + + if (locked_by_me) { + /* OK, now release the lock */ + + status = acpi_ev_release_global_lock (); + if (ACPI_FAILURE (status)) { + /* Report the error, but there isn't much else we can do */ + + ACPI_REPORT_ERROR (("Could not release ACPI Global Lock, %s\n", + acpi_format_exception (status))); + } + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_digits_needed + * + * PARAMETERS: Value - Value to be represented + * Base - Base of representation + * + * RETURN: the number of digits needed to represent Value in Base + * + ******************************************************************************/ + +u32 +acpi_ex_digits_needed ( + acpi_integer value, + u32 base) +{ + u32 num_digits; + acpi_integer current_value; + + + ACPI_FUNCTION_TRACE ("ex_digits_needed"); + + + /* acpi_integer is unsigned, so we don't worry about a '-' prefix */ + + if (value == 0) { + return_VALUE (1); + } + + current_value = value; + num_digits = 0; + + /* Count the digits in the requested base */ + + while (current_value) { + (void) acpi_ut_short_divide (current_value, base, ¤t_value, NULL); + num_digits++; + } + + return_VALUE (num_digits); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_eisa_id_to_string + * + * PARAMETERS: numeric_id - EISA ID to be converted + * out_string - Where to put the converted string (8 bytes) + * + * DESCRIPTION: Convert a numeric EISA ID to string representation + * + ******************************************************************************/ + +void +acpi_ex_eisa_id_to_string ( + u32 numeric_id, + char *out_string) +{ + u32 eisa_id; + + + ACPI_FUNCTION_ENTRY (); + + + /* Swap ID to big-endian to get contiguous bits */ + + eisa_id = acpi_ut_dword_byte_swap (numeric_id); + + out_string[0] = (char) ('@' + (((unsigned long) eisa_id >> 26) & 0x1f)); + out_string[1] = (char) ('@' + ((eisa_id >> 21) & 0x1f)); + out_string[2] = (char) ('@' + ((eisa_id >> 16) & 0x1f)); + out_string[3] = acpi_ut_hex_to_ascii_char ((acpi_integer) eisa_id, 12); + out_string[4] = acpi_ut_hex_to_ascii_char ((acpi_integer) eisa_id, 8); + out_string[5] = acpi_ut_hex_to_ascii_char ((acpi_integer) eisa_id, 4); + out_string[6] = acpi_ut_hex_to_ascii_char ((acpi_integer) eisa_id, 0); + out_string[7] = 0; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ex_unsigned_integer_to_string + * + * PARAMETERS: Value - Value to be converted + * out_string - Where to put the converted string (8 bytes) + * + * RETURN: Convert a number to string representation + * + ******************************************************************************/ + +void +acpi_ex_unsigned_integer_to_string ( + acpi_integer value, + char *out_string) +{ + u32 count; + u32 digits_needed; + u32 remainder; + + + ACPI_FUNCTION_ENTRY (); + + + digits_needed = acpi_ex_digits_needed (value, 10); + out_string[digits_needed] = 0; + + for (count = digits_needed; count > 0; count--) { + (void) acpi_ut_short_divide (value, 10, &value, &remainder); + out_string[count-1] = (char) ('0' + remainder);\ + } +} + +#endif diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c new file mode 100644 index 00000000000..14192ee55f8 --- /dev/null +++ b/drivers/acpi/fan.c @@ -0,0 +1,302 @@ +/* + * acpi_fan.c - ACPI Fan Driver ($Revision: 29 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define ACPI_FAN_COMPONENT 0x00200000 +#define ACPI_FAN_CLASS "fan" +#define ACPI_FAN_HID "PNP0C0B" +#define ACPI_FAN_DRIVER_NAME "ACPI Fan Driver" +#define ACPI_FAN_DEVICE_NAME "Fan" +#define ACPI_FAN_FILE_STATE "state" +#define ACPI_FAN_NOTIFY_STATUS 0x80 + +#define _COMPONENT ACPI_FAN_COMPONENT +ACPI_MODULE_NAME ("acpi_fan") + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION(ACPI_FAN_DRIVER_NAME); +MODULE_LICENSE("GPL"); + +static int acpi_fan_add (struct acpi_device *device); +static int acpi_fan_remove (struct acpi_device *device, int type); + +static struct acpi_driver acpi_fan_driver = { + .name = ACPI_FAN_DRIVER_NAME, + .class = ACPI_FAN_CLASS, + .ids = ACPI_FAN_HID, + .ops = { + .add = acpi_fan_add, + .remove = acpi_fan_remove, + }, +}; + +struct acpi_fan { + acpi_handle handle; +}; + + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_fan_dir; + + +static int +acpi_fan_read_state (struct seq_file *seq, void *offset) +{ + struct acpi_fan *fan = seq->private; + int state = 0; + + ACPI_FUNCTION_TRACE("acpi_fan_read_state"); + + if (fan) { + if (acpi_bus_get_power(fan->handle, &state)) + seq_printf(seq, "status: ERROR\n"); + else + seq_printf(seq, "status: %s\n", + !state?"on":"off"); + } + return_VALUE(0); +} + +static int acpi_fan_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_fan_read_state, PDE(inode)->data); +} + +static ssize_t +acpi_fan_write_state ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *ppos) +{ + int result = 0; + struct seq_file *m = (struct seq_file *)file->private_data; + struct acpi_fan *fan = (struct acpi_fan *) m->private; + char state_string[12] = {'\0'}; + + ACPI_FUNCTION_TRACE("acpi_fan_write_state"); + + if (!fan || (count > sizeof(state_string) - 1)) + return_VALUE(-EINVAL); + + if (copy_from_user(state_string, buffer, count)) + return_VALUE(-EFAULT); + + state_string[count] = '\0'; + + result = acpi_bus_set_power(fan->handle, + simple_strtoul(state_string, NULL, 0)); + if (result) + return_VALUE(result); + + return_VALUE(count); +} + +static struct file_operations acpi_fan_state_ops = { + .open = acpi_fan_state_open_fs, + .read = seq_read, + .write = acpi_fan_write_state, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int +acpi_fan_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_fan_add_fs"); + + if (!device) + return_VALUE(-EINVAL); + + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_fan_dir); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; + } + + /* 'status' [R/W] */ + entry = create_proc_entry(ACPI_FAN_FILE_STATE, + S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_FAN_FILE_STATE)); + else { + entry->proc_fops = &acpi_fan_state_ops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + return_VALUE(0); +} + + +static int +acpi_fan_remove_fs ( + struct acpi_device *device) +{ + ACPI_FUNCTION_TRACE("acpi_fan_remove_fs"); + + if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_FAN_FILE_STATE, + acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), acpi_fan_dir); + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static int +acpi_fan_add ( + struct acpi_device *device) +{ + int result = 0; + struct acpi_fan *fan = NULL; + int state = 0; + + ACPI_FUNCTION_TRACE("acpi_fan_add"); + + if (!device) + return_VALUE(-EINVAL); + + fan = kmalloc(sizeof(struct acpi_fan), GFP_KERNEL); + if (!fan) + return_VALUE(-ENOMEM); + memset(fan, 0, sizeof(struct acpi_fan)); + + fan->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_FAN_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_FAN_CLASS); + acpi_driver_data(device) = fan; + + result = acpi_bus_get_power(fan->handle, &state); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error reading power state\n")); + goto end; + } + + result = acpi_fan_add_fs(device); + if (result) + goto end; + + printk(KERN_INFO PREFIX "%s [%s] (%s)\n", + acpi_device_name(device), acpi_device_bid(device), + !device->power.state?"on":"off"); + +end: + if (result) + kfree(fan); + + return_VALUE(result); +} + + +static int +acpi_fan_remove ( + struct acpi_device *device, + int type) +{ + struct acpi_fan *fan = NULL; + + ACPI_FUNCTION_TRACE("acpi_fan_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + fan = (struct acpi_fan *) acpi_driver_data(device); + + acpi_fan_remove_fs(device); + + kfree(fan); + + return_VALUE(0); +} + + +static int __init +acpi_fan_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_fan_init"); + + acpi_fan_dir = proc_mkdir(ACPI_FAN_CLASS, acpi_root_dir); + if (!acpi_fan_dir) + return_VALUE(-ENODEV); + acpi_fan_dir->owner = THIS_MODULE; + + result = acpi_bus_register_driver(&acpi_fan_driver); + if (result < 0) { + remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + + +static void __exit +acpi_fan_exit (void) +{ + ACPI_FUNCTION_TRACE("acpi_fan_exit"); + + acpi_bus_unregister_driver(&acpi_fan_driver); + + remove_proc_entry(ACPI_FAN_CLASS, acpi_root_dir); + + return_VOID; +} + + +module_init(acpi_fan_init); +module_exit(acpi_fan_exit); + diff --git a/drivers/acpi/hardware/Makefile b/drivers/acpi/hardware/Makefile new file mode 100644 index 00000000000..438ad373b9a --- /dev/null +++ b/drivers/acpi/hardware/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for all Linux ACPI interpreter subdirectories +# + +obj-y := hwacpi.o hwgpe.o hwregs.o hwsleep.o + +obj-$(ACPI_FUTURE_USAGE) += hwtimer.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/hardware/hwacpi.c b/drivers/acpi/hardware/hwacpi.c new file mode 100644 index 00000000000..529e922bdc8 --- /dev/null +++ b/drivers/acpi/hardware/hwacpi.c @@ -0,0 +1,230 @@ + +/****************************************************************************** + * + * Module Name: hwacpi - ACPI Hardware Initialization/Mode Interface + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> + + +#define _COMPONENT ACPI_HARDWARE + ACPI_MODULE_NAME ("hwacpi") + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initialize and validate various ACPI registers + * + ******************************************************************************/ + +acpi_status +acpi_hw_initialize ( + void) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("hw_initialize"); + + + /* We must have the ACPI tables by the time we get here */ + + if (!acpi_gbl_FADT) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "A FADT is not loaded\n")); + + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + /* Sanity check the FADT for valid values */ + + status = acpi_ut_validate_fadt (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + return_ACPI_STATUS (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_set_mode + * + * PARAMETERS: Mode - SYS_MODE_ACPI or SYS_MODE_LEGACY + * + * RETURN: Status + * + * DESCRIPTION: Transitions the system into the requested mode. + * + ******************************************************************************/ + +acpi_status +acpi_hw_set_mode ( + u32 mode) +{ + + acpi_status status; + u32 retry; + + + ACPI_FUNCTION_TRACE ("hw_set_mode"); + + /* + * ACPI 2.0 clarified that if SMI_CMD in FADT is zero, + * system does not support mode transition. + */ + if (!acpi_gbl_FADT->smi_cmd) { + ACPI_REPORT_ERROR (("No SMI_CMD in FADT, mode transition failed.\n")); + return_ACPI_STATUS (AE_NO_HARDWARE_RESPONSE); + } + + /* + * ACPI 2.0 clarified the meaning of ACPI_ENABLE and ACPI_DISABLE + * in FADT: If it is zero, enabling or disabling is not supported. + * As old systems may have used zero for mode transition, + * we make sure both the numbers are zero to determine these + * transitions are not supported. + */ + if (!acpi_gbl_FADT->acpi_enable && !acpi_gbl_FADT->acpi_disable) { + ACPI_REPORT_ERROR (("No ACPI mode transition supported in this system (enable/disable both zero)\n")); + return_ACPI_STATUS (AE_OK); + } + + switch (mode) { + case ACPI_SYS_MODE_ACPI: + + /* BIOS should have disabled ALL fixed and GP events */ + + status = acpi_os_write_port (acpi_gbl_FADT->smi_cmd, + (u32) acpi_gbl_FADT->acpi_enable, 8); + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Attempting to enable ACPI mode\n")); + break; + + case ACPI_SYS_MODE_LEGACY: + + /* + * BIOS should clear all fixed status bits and restore fixed event + * enable bits to default + */ + status = acpi_os_write_port (acpi_gbl_FADT->smi_cmd, + (u32) acpi_gbl_FADT->acpi_disable, 8); + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Attempting to enable Legacy (non-ACPI) mode\n")); + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not write mode change, %s\n", acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + /* + * Some hardware takes a LONG time to switch modes. Give them 3 sec to + * do so, but allow faster systems to proceed more quickly. + */ + retry = 3000; + while (retry) { + if (acpi_hw_get_mode() == mode) { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Mode %X successfully enabled\n", mode)); + return_ACPI_STATUS (AE_OK); + } + acpi_os_stall(1000); + retry--; + } + + ACPI_REPORT_ERROR (("Hardware never changed modes\n")); + return_ACPI_STATUS (AE_NO_HARDWARE_RESPONSE); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_get_mode + * + * PARAMETERS: none + * + * RETURN: SYS_MODE_ACPI or SYS_MODE_LEGACY + * + * DESCRIPTION: Return current operating state of system. Determined by + * querying the SCI_EN bit. + * + ******************************************************************************/ + +u32 +acpi_hw_get_mode (void) +{ + acpi_status status; + u32 value; + + + ACPI_FUNCTION_TRACE ("hw_get_mode"); + + + /* + * ACPI 2.0 clarified that if SMI_CMD in FADT is zero, + * system does not support mode transition. + */ + if (!acpi_gbl_FADT->smi_cmd) { + return_VALUE (ACPI_SYS_MODE_ACPI); + } + + status = acpi_get_register (ACPI_BITREG_SCI_ENABLE, &value, ACPI_MTX_LOCK); + if (ACPI_FAILURE (status)) { + return_VALUE (ACPI_SYS_MODE_LEGACY); + } + + if (value) { + return_VALUE (ACPI_SYS_MODE_ACPI); + } + else { + return_VALUE (ACPI_SYS_MODE_LEGACY); + } +} diff --git a/drivers/acpi/hardware/hwgpe.c b/drivers/acpi/hardware/hwgpe.c new file mode 100644 index 00000000000..9ac1d639bf5 --- /dev/null +++ b/drivers/acpi/hardware/hwgpe.c @@ -0,0 +1,444 @@ + +/****************************************************************************** + * + * Module Name: hwgpe - Low level GPE enable/disable/clear functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <acpi/acpi.h> +#include <acpi/acevents.h> + +#define _COMPONENT ACPI_HARDWARE + ACPI_MODULE_NAME ("hwgpe") + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_write_gpe_enable_reg + * + * PARAMETERS: gpe_event_info - Info block for the GPE to be enabled + * + * RETURN: Status + * + * DESCRIPTION: Write a GPE enable register. Note: The bit for this GPE must + * already be cleared or set in the parent register + * enable_for_run mask. + * + ******************************************************************************/ + +acpi_status +acpi_hw_write_gpe_enable_reg ( + struct acpi_gpe_event_info *gpe_event_info) +{ + struct acpi_gpe_register_info *gpe_register_info; + acpi_status status; + + + ACPI_FUNCTION_ENTRY (); + + + /* Get the info block for the entire GPE register */ + + gpe_register_info = gpe_event_info->register_info; + if (!gpe_register_info) { + return (AE_NOT_EXIST); + } + + /* Write the entire GPE (runtime) enable register */ + + status = acpi_hw_low_level_write (8, gpe_register_info->enable_for_run, + &gpe_register_info->enable_address); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_clear_gpe + * + * PARAMETERS: gpe_event_info - Info block for the GPE to be cleared + * + * RETURN: Status + * + * DESCRIPTION: Clear the status bit for a single GPE. + * + ******************************************************************************/ + +acpi_status +acpi_hw_clear_gpe ( + struct acpi_gpe_event_info *gpe_event_info) +{ + acpi_status status; + + + ACPI_FUNCTION_ENTRY (); + + + /* + * Write a one to the appropriate bit in the status register to + * clear this GPE. + */ + status = acpi_hw_low_level_write (8, gpe_event_info->register_bit, + &gpe_event_info->register_info->status_address); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_get_gpe_status + * + * PARAMETERS: gpe_event_info - Info block for the GPE to queried + * event_status - Where the GPE status is returned + * + * RETURN: Status + * + * DESCRIPTION: Return the status of a single GPE. + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_hw_get_gpe_status ( + struct acpi_gpe_event_info *gpe_event_info, + acpi_event_status *event_status) +{ + u32 in_byte; + u8 register_bit; + struct acpi_gpe_register_info *gpe_register_info; + acpi_status status; + acpi_event_status local_event_status = 0; + + + ACPI_FUNCTION_ENTRY (); + + + if (!event_status) { + return (AE_BAD_PARAMETER); + } + + /* Get the info block for the entire GPE register */ + + gpe_register_info = gpe_event_info->register_info; + + /* Get the register bitmask for this GPE */ + + register_bit = gpe_event_info->register_bit; + + /* GPE currently enabled? (enabled for runtime?) */ + + if (register_bit & gpe_register_info->enable_for_run) { + local_event_status |= ACPI_EVENT_FLAG_ENABLED; + } + + /* GPE enabled for wake? */ + + if (register_bit & gpe_register_info->enable_for_wake) { + local_event_status |= ACPI_EVENT_FLAG_WAKE_ENABLED; + } + + /* GPE currently active (status bit == 1)? */ + + status = acpi_hw_low_level_read (8, &in_byte, &gpe_register_info->status_address); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + if (register_bit & in_byte) { + local_event_status |= ACPI_EVENT_FLAG_SET; + } + + /* Set return value */ + + (*event_status) = local_event_status; + + +unlock_and_exit: + return (status); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_disable_gpe_block + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Disable all GPEs within a GPE block + * + ******************************************************************************/ + +acpi_status +acpi_hw_disable_gpe_block ( + struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block) +{ + u32 i; + acpi_status status; + + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + /* Disable all GPEs in this register */ + + status = acpi_hw_low_level_write (8, 0x00, + &gpe_block->register_info[i].enable_address); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_clear_gpe_block + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Clear status bits for all GPEs within a GPE block + * + ******************************************************************************/ + +acpi_status +acpi_hw_clear_gpe_block ( + struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block) +{ + u32 i; + acpi_status status; + + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + /* Clear status on all GPEs in this register */ + + status = acpi_hw_low_level_write (8, 0xFF, + &gpe_block->register_info[i].status_address); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_enable_runtime_gpe_block + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Enable all "runtime" GPEs within a GPE block. (Includes + * combination wake/run GPEs.) + * + ******************************************************************************/ + +acpi_status +acpi_hw_enable_runtime_gpe_block ( + struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block) +{ + u32 i; + acpi_status status; + + + /* NOTE: assumes that all GPEs are currently disabled */ + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + if (!gpe_block->register_info[i].enable_for_run) { + continue; + } + + /* Enable all "runtime" GPEs in this register */ + + status = acpi_hw_low_level_write (8, gpe_block->register_info[i].enable_for_run, + &gpe_block->register_info[i].enable_address); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_enable_wakeup_gpe_block + * + * PARAMETERS: gpe_xrupt_info - GPE Interrupt info + * gpe_block - Gpe Block info + * + * RETURN: Status + * + * DESCRIPTION: Enable all "wake" GPEs within a GPE block. (Includes + * combination wake/run GPEs.) + * + ******************************************************************************/ + +acpi_status +acpi_hw_enable_wakeup_gpe_block ( + struct acpi_gpe_xrupt_info *gpe_xrupt_info, + struct acpi_gpe_block_info *gpe_block) +{ + u32 i; + acpi_status status; + + + /* Examine each GPE Register within the block */ + + for (i = 0; i < gpe_block->register_count; i++) { + if (!gpe_block->register_info[i].enable_for_wake) { + continue; + } + + /* Enable all "wake" GPEs in this register */ + + status = acpi_hw_low_level_write (8, gpe_block->register_info[i].enable_for_wake, + &gpe_block->register_info[i].enable_address); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_disable_all_gpes + * + * PARAMETERS: Flags - ACPI_NOT_ISR or ACPI_ISR + * + * RETURN: Status + * + * DESCRIPTION: Disable and clear all GPEs + * + ******************************************************************************/ + +acpi_status +acpi_hw_disable_all_gpes ( + u32 flags) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("hw_disable_all_gpes"); + + + status = acpi_ev_walk_gpe_list (acpi_hw_disable_gpe_block, flags); + status = acpi_ev_walk_gpe_list (acpi_hw_clear_gpe_block, flags); + return_ACPI_STATUS (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_enable_all_runtime_gpes + * + * PARAMETERS: Flags - ACPI_NOT_ISR or ACPI_ISR + * + * RETURN: Status + * + * DESCRIPTION: Enable all GPEs of the given type + * + ******************************************************************************/ + +acpi_status +acpi_hw_enable_all_runtime_gpes ( + u32 flags) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("hw_enable_all_runtime_gpes"); + + + status = acpi_ev_walk_gpe_list (acpi_hw_enable_runtime_gpe_block, flags); + return_ACPI_STATUS (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_enable_all_wakeup_gpes + * + * PARAMETERS: Flags - ACPI_NOT_ISR or ACPI_ISR + * + * RETURN: Status + * + * DESCRIPTION: Enable all GPEs of the given type + * + ******************************************************************************/ + +acpi_status +acpi_hw_enable_all_wakeup_gpes ( + u32 flags) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("hw_enable_all_wakeup_gpes"); + + + status = acpi_ev_walk_gpe_list (acpi_hw_enable_wakeup_gpe_block, flags); + return_ACPI_STATUS (status); +} + diff --git a/drivers/acpi/hardware/hwregs.c b/drivers/acpi/hardware/hwregs.c new file mode 100644 index 00000000000..91af0c2ddcf --- /dev/null +++ b/drivers/acpi/hardware/hwregs.c @@ -0,0 +1,850 @@ + +/******************************************************************************* + * + * Module Name: hwregs - Read/write access functions for the various ACPI + * control and status registers. + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acevents.h> + +#define _COMPONENT ACPI_HARDWARE + ACPI_MODULE_NAME ("hwregs") + + +/******************************************************************************* + * + * FUNCTION: acpi_hw_clear_acpi_status + * + * PARAMETERS: Flags - Lock the hardware or not + * + * RETURN: none + * + * DESCRIPTION: Clears all fixed and general purpose status bits + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED + * + ******************************************************************************/ + +acpi_status +acpi_hw_clear_acpi_status ( + u32 flags) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("hw_clear_acpi_status"); + + + ACPI_DEBUG_PRINT ((ACPI_DB_IO, "About to write %04X to %04X\n", + ACPI_BITMASK_ALL_FIXED_STATUS, + (u16) acpi_gbl_FADT->xpm1a_evt_blk.address)); + + if (flags & ACPI_MTX_LOCK) { + status = acpi_ut_acquire_mutex (ACPI_MTX_HARDWARE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1_STATUS, + ACPI_BITMASK_ALL_FIXED_STATUS); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* Clear the fixed events */ + + if (acpi_gbl_FADT->xpm1b_evt_blk.address) { + status = acpi_hw_low_level_write (16, ACPI_BITMASK_ALL_FIXED_STATUS, + &acpi_gbl_FADT->xpm1b_evt_blk); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + } + + /* Clear the GPE Bits in all GPE registers in all GPE blocks */ + + status = acpi_ev_walk_gpe_list (acpi_hw_clear_gpe_block, ACPI_ISR); + +unlock_and_exit: + if (flags & ACPI_MTX_LOCK) { + (void) acpi_ut_release_mutex (ACPI_MTX_HARDWARE); + } + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_get_sleep_type_data + * + * PARAMETERS: sleep_state - Numeric sleep state + * *sleep_type_a - Where SLP_TYPa is returned + * *sleep_type_b - Where SLP_TYPb is returned + * + * RETURN: Status - ACPI status + * + * DESCRIPTION: Obtain the SLP_TYPa and SLP_TYPb values for the requested sleep + * state. + * + ******************************************************************************/ + +acpi_status +acpi_get_sleep_type_data ( + u8 sleep_state, + u8 *sleep_type_a, + u8 *sleep_type_b) +{ + acpi_status status = AE_OK; + struct acpi_parameter_info info; + + + ACPI_FUNCTION_TRACE ("acpi_get_sleep_type_data"); + + + /* + * Validate parameters + */ + if ((sleep_state > ACPI_S_STATES_MAX) || + !sleep_type_a || !sleep_type_b) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Evaluate the namespace object containing the values for this state + */ + info.parameters = NULL; + status = acpi_ns_evaluate_by_name ((char *) acpi_gbl_sleep_state_names[sleep_state], + &info); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "%s while evaluating sleep_state [%s]\n", + acpi_format_exception (status), acpi_gbl_sleep_state_names[sleep_state])); + + return_ACPI_STATUS (status); + } + + /* Must have a return object */ + + if (!info.return_object) { + ACPI_REPORT_ERROR (("Missing Sleep State object\n")); + status = AE_NOT_EXIST; + } + + /* It must be of type Package */ + + else if (ACPI_GET_OBJECT_TYPE (info.return_object) != ACPI_TYPE_PACKAGE) { + ACPI_REPORT_ERROR (("Sleep State object not a Package\n")); + status = AE_AML_OPERAND_TYPE; + } + + /* The package must have at least two elements */ + + else if (info.return_object->package.count < 2) { + ACPI_REPORT_ERROR (("Sleep State package does not have at least two elements\n")); + status = AE_AML_NO_OPERAND; + } + + /* The first two elements must both be of type Integer */ + + else if ((ACPI_GET_OBJECT_TYPE (info.return_object->package.elements[0]) != ACPI_TYPE_INTEGER) || + (ACPI_GET_OBJECT_TYPE (info.return_object->package.elements[1]) != ACPI_TYPE_INTEGER)) { + ACPI_REPORT_ERROR (("Sleep State package elements are not both Integers (%s, %s)\n", + acpi_ut_get_object_type_name (info.return_object->package.elements[0]), + acpi_ut_get_object_type_name (info.return_object->package.elements[1]))); + status = AE_AML_OPERAND_TYPE; + } + else { + /* + * Valid _Sx_ package size, type, and value + */ + *sleep_type_a = (u8) (info.return_object->package.elements[0])->integer.value; + *sleep_type_b = (u8) (info.return_object->package.elements[1])->integer.value; + } + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "While evaluating sleep_state [%s], bad Sleep object %p type %s\n", + acpi_gbl_sleep_state_names[sleep_state], info.return_object, + acpi_ut_get_object_type_name (info.return_object))); + } + + acpi_ut_remove_reference (info.return_object); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_get_sleep_type_data); + + +/******************************************************************************* + * + * FUNCTION: acpi_hw_get_register_bit_mask + * + * PARAMETERS: register_id - Index of ACPI Register to access + * + * RETURN: The bit mask to be used when accessing the register + * + * DESCRIPTION: Map register_id into a register bit mask. + * + ******************************************************************************/ + +struct acpi_bit_register_info * +acpi_hw_get_bit_register_info ( + u32 register_id) +{ + ACPI_FUNCTION_NAME ("hw_get_bit_register_info"); + + + if (register_id > ACPI_BITREG_MAX) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid bit_register ID: %X\n", register_id)); + return (NULL); + } + + return (&acpi_gbl_bit_register_info[register_id]); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_get_register + * + * PARAMETERS: register_id - ID of ACPI bit_register to access + * return_value - Value that was read from the register + * Flags - Lock the hardware or not + * + * RETURN: Status and the value read from specified Register. Value + * returned is normalized to bit0 (is shifted all the way right) + * + * DESCRIPTION: ACPI bit_register read function. + * + ******************************************************************************/ + +acpi_status +acpi_get_register ( + u32 register_id, + u32 *return_value, + u32 flags) +{ + u32 register_value = 0; + struct acpi_bit_register_info *bit_reg_info; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_get_register"); + + + /* Get the info structure corresponding to the requested ACPI Register */ + + bit_reg_info = acpi_hw_get_bit_register_info (register_id); + if (!bit_reg_info) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (flags & ACPI_MTX_LOCK) { + status = acpi_ut_acquire_mutex (ACPI_MTX_HARDWARE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Read from the register */ + + status = acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, + bit_reg_info->parent_register, ®ister_value); + + if (flags & ACPI_MTX_LOCK) { + (void) acpi_ut_release_mutex (ACPI_MTX_HARDWARE); + } + + if (ACPI_SUCCESS (status)) { + /* Normalize the value that was read */ + + register_value = ((register_value & bit_reg_info->access_bit_mask) + >> bit_reg_info->bit_position); + + *return_value = register_value; + + ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Read value %8.8X register %X\n", + register_value, bit_reg_info->parent_register)); + } + + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_get_register); + + +/******************************************************************************* + * + * FUNCTION: acpi_set_register + * + * PARAMETERS: register_id - ID of ACPI bit_register to access + * Value - (only used on write) value to write to the + * Register, NOT pre-normalized to the bit pos + * Flags - Lock the hardware or not + * + * RETURN: Status + * + * DESCRIPTION: ACPI Bit Register write function. + * + ******************************************************************************/ + +acpi_status +acpi_set_register ( + u32 register_id, + u32 value, + u32 flags) +{ + u32 register_value = 0; + struct acpi_bit_register_info *bit_reg_info; + acpi_status status; + + + ACPI_FUNCTION_TRACE_U32 ("acpi_set_register", register_id); + + + /* Get the info structure corresponding to the requested ACPI Register */ + + bit_reg_info = acpi_hw_get_bit_register_info (register_id); + if (!bit_reg_info) { + ACPI_REPORT_ERROR (("Bad ACPI HW register_id: %X\n", register_id)); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (flags & ACPI_MTX_LOCK) { + status = acpi_ut_acquire_mutex (ACPI_MTX_HARDWARE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Always do a register read first so we can insert the new bits */ + + status = acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, + bit_reg_info->parent_register, ®ister_value); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* + * Decode the Register ID + * Register ID = [Register block ID] | [bit ID] + * + * Check bit ID to fine locate Register offset. + * Check Mask to determine Register offset, and then read-write. + */ + switch (bit_reg_info->parent_register) { + case ACPI_REGISTER_PM1_STATUS: + + /* + * Status Registers are different from the rest. Clear by + * writing 1, and writing 0 has no effect. So, the only relevant + * information is the single bit we're interested in, all others should + * be written as 0 so they will be left unchanged. + */ + value = ACPI_REGISTER_PREPARE_BITS (value, + bit_reg_info->bit_position, bit_reg_info->access_bit_mask); + if (value) { + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, + ACPI_REGISTER_PM1_STATUS, (u16) value); + register_value = 0; + } + break; + + + case ACPI_REGISTER_PM1_ENABLE: + + ACPI_REGISTER_INSERT_VALUE (register_value, bit_reg_info->bit_position, + bit_reg_info->access_bit_mask, value); + + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, + ACPI_REGISTER_PM1_ENABLE, (u16) register_value); + break; + + + case ACPI_REGISTER_PM1_CONTROL: + + /* + * Write the PM1 Control register. + * Note that at this level, the fact that there are actually TWO + * registers (A and B - and B may not exist) is abstracted. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_IO, "PM1 control: Read %X\n", register_value)); + + ACPI_REGISTER_INSERT_VALUE (register_value, bit_reg_info->bit_position, + bit_reg_info->access_bit_mask, value); + + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, + ACPI_REGISTER_PM1_CONTROL, (u16) register_value); + break; + + + case ACPI_REGISTER_PM2_CONTROL: + + status = acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, + ACPI_REGISTER_PM2_CONTROL, ®ister_value); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_IO, "PM2 control: Read %X from %8.8X%8.8X\n", + register_value, + ACPI_FORMAT_UINT64 (acpi_gbl_FADT->xpm2_cnt_blk.address))); + + ACPI_REGISTER_INSERT_VALUE (register_value, bit_reg_info->bit_position, + bit_reg_info->access_bit_mask, value); + + ACPI_DEBUG_PRINT ((ACPI_DB_IO, "About to write %4.4X to %8.8X%8.8X\n", + register_value, + ACPI_FORMAT_UINT64 (acpi_gbl_FADT->xpm2_cnt_blk.address))); + + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, + ACPI_REGISTER_PM2_CONTROL, (u8) (register_value)); + break; + + + default: + break; + } + + +unlock_and_exit: + + if (flags & ACPI_MTX_LOCK) { + (void) acpi_ut_release_mutex (ACPI_MTX_HARDWARE); + } + + /* Normalize the value that was read */ + + ACPI_DEBUG_EXEC (register_value = ((register_value & bit_reg_info->access_bit_mask) >> bit_reg_info->bit_position)); + + ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Set bits: %8.8X actual %8.8X register %X\n", + value, register_value, bit_reg_info->parent_register)); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_set_register); + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_register_read + * + * PARAMETERS: use_lock - Mutex hw access + * register_id - register_iD + Offset + * return_value - Value that was read from the register + * + * RETURN: Status and the value read. + * + * DESCRIPTION: Acpi register read function. Registers are read at the + * given offset. + * + ******************************************************************************/ + +acpi_status +acpi_hw_register_read ( + u8 use_lock, + u32 register_id, + u32 *return_value) +{ + u32 value1 = 0; + u32 value2 = 0; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("hw_register_read"); + + + if (ACPI_MTX_LOCK == use_lock) { + status = acpi_ut_acquire_mutex (ACPI_MTX_HARDWARE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + switch (register_id) { + case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ + + status = acpi_hw_low_level_read (16, &value1, &acpi_gbl_FADT->xpm1a_evt_blk); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* PM1B is optional */ + + status = acpi_hw_low_level_read (16, &value2, &acpi_gbl_FADT->xpm1b_evt_blk); + value1 |= value2; + break; + + + case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access */ + + status = acpi_hw_low_level_read (16, &value1, &acpi_gbl_xpm1a_enable); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* PM1B is optional */ + + status = acpi_hw_low_level_read (16, &value2, &acpi_gbl_xpm1b_enable); + value1 |= value2; + break; + + + case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ + + status = acpi_hw_low_level_read (16, &value1, &acpi_gbl_FADT->xpm1a_cnt_blk); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + status = acpi_hw_low_level_read (16, &value2, &acpi_gbl_FADT->xpm1b_cnt_blk); + value1 |= value2; + break; + + + case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ + + status = acpi_hw_low_level_read (8, &value1, &acpi_gbl_FADT->xpm2_cnt_blk); + break; + + + case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ + + status = acpi_hw_low_level_read (32, &value1, &acpi_gbl_FADT->xpm_tmr_blk); + break; + + case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ + + status = acpi_os_read_port (acpi_gbl_FADT->smi_cmd, &value1, 8); + break; + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown Register ID: %X\n", register_id)); + status = AE_BAD_PARAMETER; + break; + } + +unlock_and_exit: + if (ACPI_MTX_LOCK == use_lock) { + (void) acpi_ut_release_mutex (ACPI_MTX_HARDWARE); + } + + if (ACPI_SUCCESS (status)) { + *return_value = value1; + } + + return_ACPI_STATUS (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_register_write + * + * PARAMETERS: use_lock - Mutex hw access + * register_id - register_iD + Offset + * Value - The value to write + * + * RETURN: Status + * + * DESCRIPTION: Acpi register Write function. Registers are written at the + * given offset. + * + ******************************************************************************/ + +acpi_status +acpi_hw_register_write ( + u8 use_lock, + u32 register_id, + u32 value) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("hw_register_write"); + + + if (ACPI_MTX_LOCK == use_lock) { + status = acpi_ut_acquire_mutex (ACPI_MTX_HARDWARE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + switch (register_id) { + case ACPI_REGISTER_PM1_STATUS: /* 16-bit access */ + + status = acpi_hw_low_level_write (16, value, &acpi_gbl_FADT->xpm1a_evt_blk); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* PM1B is optional */ + + status = acpi_hw_low_level_write (16, value, &acpi_gbl_FADT->xpm1b_evt_blk); + break; + + + case ACPI_REGISTER_PM1_ENABLE: /* 16-bit access*/ + + status = acpi_hw_low_level_write (16, value, &acpi_gbl_xpm1a_enable); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* PM1B is optional */ + + status = acpi_hw_low_level_write (16, value, &acpi_gbl_xpm1b_enable); + break; + + + case ACPI_REGISTER_PM1_CONTROL: /* 16-bit access */ + + status = acpi_hw_low_level_write (16, value, &acpi_gbl_FADT->xpm1a_cnt_blk); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + status = acpi_hw_low_level_write (16, value, &acpi_gbl_FADT->xpm1b_cnt_blk); + break; + + + case ACPI_REGISTER_PM1A_CONTROL: /* 16-bit access */ + + status = acpi_hw_low_level_write (16, value, &acpi_gbl_FADT->xpm1a_cnt_blk); + break; + + + case ACPI_REGISTER_PM1B_CONTROL: /* 16-bit access */ + + status = acpi_hw_low_level_write (16, value, &acpi_gbl_FADT->xpm1b_cnt_blk); + break; + + + case ACPI_REGISTER_PM2_CONTROL: /* 8-bit access */ + + status = acpi_hw_low_level_write (8, value, &acpi_gbl_FADT->xpm2_cnt_blk); + break; + + + case ACPI_REGISTER_PM_TIMER: /* 32-bit access */ + + status = acpi_hw_low_level_write (32, value, &acpi_gbl_FADT->xpm_tmr_blk); + break; + + + case ACPI_REGISTER_SMI_COMMAND_BLOCK: /* 8-bit access */ + + /* SMI_CMD is currently always in IO space */ + + status = acpi_os_write_port (acpi_gbl_FADT->smi_cmd, value, 8); + break; + + + default: + status = AE_BAD_PARAMETER; + break; + } + +unlock_and_exit: + if (ACPI_MTX_LOCK == use_lock) { + (void) acpi_ut_release_mutex (ACPI_MTX_HARDWARE); + } + + return_ACPI_STATUS (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_low_level_read + * + * PARAMETERS: Width - 8, 16, or 32 + * Value - Where the value is returned + * Reg - GAS register structure + * + * RETURN: Status + * + * DESCRIPTION: Read from either memory or IO space. + * + ******************************************************************************/ + +acpi_status +acpi_hw_low_level_read ( + u32 width, + u32 *value, + struct acpi_generic_address *reg) +{ + u64 address; + acpi_status status; + + + ACPI_FUNCTION_NAME ("hw_low_level_read"); + + + /* + * Must have a valid pointer to a GAS structure, and + * a non-zero address within. However, don't return an error + * because the PM1A/B code must not fail if B isn't present. + */ + if (!reg) { + return (AE_OK); + } + + /* Get a local copy of the address. Handles possible alignment issues */ + + ACPI_MOVE_64_TO_64 (&address, ®->address); + if (!address) { + return (AE_OK); + } + *value = 0; + + /* + * Two address spaces supported: Memory or IO. + * PCI_Config is not supported here because the GAS struct is insufficient + */ + switch (reg->address_space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + + status = acpi_os_read_memory ( + (acpi_physical_address) address, + value, width); + break; + + + case ACPI_ADR_SPACE_SYSTEM_IO: + + status = acpi_os_read_port ((acpi_io_address) address, + value, width); + break; + + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unsupported address space: %X\n", reg->address_space_id)); + return (AE_BAD_PARAMETER); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Read: %8.8X width %2d from %8.8X%8.8X (%s)\n", + *value, width, + ACPI_FORMAT_UINT64 (address), + acpi_ut_get_region_name (reg->address_space_id))); + + return (status); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_hw_low_level_write + * + * PARAMETERS: Width - 8, 16, or 32 + * Value - To be written + * Reg - GAS register structure + * + * RETURN: Status + * + * DESCRIPTION: Write to either memory or IO space. + * + ******************************************************************************/ + +acpi_status +acpi_hw_low_level_write ( + u32 width, + u32 value, + struct acpi_generic_address *reg) +{ + u64 address; + acpi_status status; + + + ACPI_FUNCTION_NAME ("hw_low_level_write"); + + + /* + * Must have a valid pointer to a GAS structure, and + * a non-zero address within. However, don't return an error + * because the PM1A/B code must not fail if B isn't present. + */ + if (!reg) { + return (AE_OK); + } + + /* Get a local copy of the address. Handles possible alignment issues */ + + ACPI_MOVE_64_TO_64 (&address, ®->address); + if (!address) { + return (AE_OK); + } + + /* + * Two address spaces supported: Memory or IO. + * PCI_Config is not supported here because the GAS struct is insufficient + */ + switch (reg->address_space_id) { + case ACPI_ADR_SPACE_SYSTEM_MEMORY: + + status = acpi_os_write_memory ( + (acpi_physical_address) address, + value, width); + break; + + + case ACPI_ADR_SPACE_SYSTEM_IO: + + status = acpi_os_write_port ((acpi_io_address) address, + value, width); + break; + + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unsupported address space: %X\n", reg->address_space_id)); + return (AE_BAD_PARAMETER); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Wrote: %8.8X width %2d to %8.8X%8.8X (%s)\n", + value, width, + ACPI_FORMAT_UINT64 (address), + acpi_ut_get_region_name (reg->address_space_id))); + + return (status); +} diff --git a/drivers/acpi/hardware/hwsleep.c b/drivers/acpi/hardware/hwsleep.c new file mode 100644 index 00000000000..77b3e9a8550 --- /dev/null +++ b/drivers/acpi/hardware/hwsleep.c @@ -0,0 +1,582 @@ + +/****************************************************************************** + * + * Name: hwsleep.c - ACPI Hardware Sleep/Wake Interface + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> + +#define _COMPONENT ACPI_HARDWARE + ACPI_MODULE_NAME ("hwsleep") + + +#define METHOD_NAME__BFS "\\_BFS" +#define METHOD_NAME__GTS "\\_GTS" +#define METHOD_NAME__PTS "\\_PTS" +#define METHOD_NAME__SST "\\_SI._SST" +#define METHOD_NAME__WAK "\\_WAK" + +#define ACPI_SST_INDICATOR_OFF 0 +#define ACPI_SST_WORKING 1 +#define ACPI_SST_WAKING 2 +#define ACPI_SST_SLEEPING 3 +#define ACPI_SST_SLEEP_CONTEXT 4 + + +/****************************************************************************** + * + * FUNCTION: acpi_set_firmware_waking_vector + * + * PARAMETERS: physical_address - Physical address of ACPI real mode + * entry point. + * + * RETURN: Status + * + * DESCRIPTION: access function for d_firmware_waking_vector field in FACS + * + ******************************************************************************/ + +acpi_status +acpi_set_firmware_waking_vector ( + acpi_physical_address physical_address) +{ + + ACPI_FUNCTION_TRACE ("acpi_set_firmware_waking_vector"); + + + /* Set the vector */ + + if (acpi_gbl_common_fACS.vector_width == 32) { + *(ACPI_CAST_PTR (u32, acpi_gbl_common_fACS.firmware_waking_vector)) + = (u32) physical_address; + } + else { + *acpi_gbl_common_fACS.firmware_waking_vector + = physical_address; + } + + return_ACPI_STATUS (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_get_firmware_waking_vector + * + * PARAMETERS: *physical_address - Output buffer where contents of + * the firmware_waking_vector field of + * the FACS will be stored. + * + * RETURN: Status + * + * DESCRIPTION: Access function for firmware_waking_vector field in FACS + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_get_firmware_waking_vector ( + acpi_physical_address *physical_address) +{ + + ACPI_FUNCTION_TRACE ("acpi_get_firmware_waking_vector"); + + + if (!physical_address) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Get the vector */ + + if (acpi_gbl_common_fACS.vector_width == 32) { + *physical_address = (acpi_physical_address) + *(ACPI_CAST_PTR (u32, acpi_gbl_common_fACS.firmware_waking_vector)); + } + else { + *physical_address = + *acpi_gbl_common_fACS.firmware_waking_vector; + } + + return_ACPI_STATUS (AE_OK); +} +#endif + + +/****************************************************************************** + * + * FUNCTION: acpi_enter_sleep_state_prep + * + * PARAMETERS: sleep_state - Which sleep state to enter + * + * RETURN: Status + * + * DESCRIPTION: Prepare to enter a system sleep state (see ACPI 2.0 spec p 231) + * This function must execute with interrupts enabled. + * We break sleeping into 2 stages so that OSPM can handle + * various OS-specific tasks between the two steps. + * + ******************************************************************************/ + +acpi_status +acpi_enter_sleep_state_prep ( + u8 sleep_state) +{ + acpi_status status; + struct acpi_object_list arg_list; + union acpi_object arg; + + + ACPI_FUNCTION_TRACE ("acpi_enter_sleep_state_prep"); + + + /* + * _PSW methods could be run here to enable wake-on keyboard, LAN, etc. + */ + status = acpi_get_sleep_type_data (sleep_state, + &acpi_gbl_sleep_type_a, &acpi_gbl_sleep_type_b); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Setup parameter object */ + + arg_list.count = 1; + arg_list.pointer = &arg; + + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = sleep_state; + + /* Run the _PTS and _GTS methods */ + + status = acpi_evaluate_object (NULL, METHOD_NAME__PTS, &arg_list, NULL); + if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) { + return_ACPI_STATUS (status); + } + + status = acpi_evaluate_object (NULL, METHOD_NAME__GTS, &arg_list, NULL); + if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) { + return_ACPI_STATUS (status); + } + + /* Setup the argument to _SST */ + + switch (sleep_state) { + case ACPI_STATE_S0: + arg.integer.value = ACPI_SST_WORKING; + break; + + case ACPI_STATE_S1: + case ACPI_STATE_S2: + case ACPI_STATE_S3: + arg.integer.value = ACPI_SST_SLEEPING; + break; + + case ACPI_STATE_S4: + arg.integer.value = ACPI_SST_SLEEP_CONTEXT; + break; + + default: + arg.integer.value = ACPI_SST_INDICATOR_OFF; /* Default is indicator off */ + break; + } + + /* Set the system indicators to show the desired sleep state. */ + + status = acpi_evaluate_object (NULL, METHOD_NAME__SST, &arg_list, NULL); + if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) { + ACPI_REPORT_ERROR (("Method _SST failed, %s\n", acpi_format_exception (status))); + } + + return_ACPI_STATUS (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_enter_sleep_state + * + * PARAMETERS: sleep_state - Which sleep state to enter + * + * RETURN: Status + * + * DESCRIPTION: Enter a system sleep state (see ACPI 2.0 spec p 231) + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED + * + ******************************************************************************/ + +acpi_status asmlinkage +acpi_enter_sleep_state ( + u8 sleep_state) +{ + u32 PM1Acontrol; + u32 PM1Bcontrol; + struct acpi_bit_register_info *sleep_type_reg_info; + struct acpi_bit_register_info *sleep_enable_reg_info; + u32 in_value; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_enter_sleep_state"); + + + if ((acpi_gbl_sleep_type_a > ACPI_SLEEP_TYPE_MAX) || + (acpi_gbl_sleep_type_b > ACPI_SLEEP_TYPE_MAX)) { + ACPI_REPORT_ERROR (("Sleep values out of range: A=%X B=%X\n", + acpi_gbl_sleep_type_a, acpi_gbl_sleep_type_b)); + return_ACPI_STATUS (AE_AML_OPERAND_VALUE); + } + + sleep_type_reg_info = acpi_hw_get_bit_register_info (ACPI_BITREG_SLEEP_TYPE_A); + sleep_enable_reg_info = acpi_hw_get_bit_register_info (ACPI_BITREG_SLEEP_ENABLE); + + /* Clear wake status */ + + status = acpi_set_register (ACPI_BITREG_WAKE_STATUS, 1, ACPI_MTX_DO_NOT_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Clear all fixed and general purpose status bits */ + + status = acpi_hw_clear_acpi_status (ACPI_MTX_DO_NOT_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * 1) Disable/Clear all GPEs + * 2) Enable all wakeup GPEs + */ + status = acpi_hw_disable_all_gpes (ACPI_ISR); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + acpi_gbl_system_awake_and_running = FALSE; + + status = acpi_hw_enable_all_wakeup_gpes (ACPI_ISR); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Get current value of PM1A control */ + + status = acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1_CONTROL, &PM1Acontrol); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "Entering sleep state [S%d]\n", sleep_state)); + + /* Clear SLP_EN and SLP_TYP fields */ + + PM1Acontrol &= ~(sleep_type_reg_info->access_bit_mask | sleep_enable_reg_info->access_bit_mask); + PM1Bcontrol = PM1Acontrol; + + /* Insert SLP_TYP bits */ + + PM1Acontrol |= (acpi_gbl_sleep_type_a << sleep_type_reg_info->bit_position); + PM1Bcontrol |= (acpi_gbl_sleep_type_b << sleep_type_reg_info->bit_position); + + /* + * We split the writes of SLP_TYP and SLP_EN to workaround + * poorly implemented hardware. + */ + + /* Write #1: fill in SLP_TYP data */ + + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1A_CONTROL, PM1Acontrol); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1B_CONTROL, PM1Bcontrol); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Insert SLP_ENABLE bit */ + + PM1Acontrol |= sleep_enable_reg_info->access_bit_mask; + PM1Bcontrol |= sleep_enable_reg_info->access_bit_mask; + + /* Write #2: SLP_TYP + SLP_EN */ + + ACPI_FLUSH_CPU_CACHE (); + + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1A_CONTROL, PM1Acontrol); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1B_CONTROL, PM1Bcontrol); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (sleep_state > ACPI_STATE_S3) { + /* + * We wanted to sleep > S3, but it didn't happen (by virtue of the fact that + * we are still executing!) + * + * Wait ten seconds, then try again. This is to get S4/S5 to work on all machines. + * + * We wait so long to allow chipsets that poll this reg very slowly to + * still read the right value. Ideally, this block would go + * away entirely. + */ + acpi_os_stall (10000000); + + status = acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, ACPI_REGISTER_PM1_CONTROL, + sleep_enable_reg_info->access_bit_mask); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Wait until we enter sleep state */ + + do { + status = acpi_get_register (ACPI_BITREG_WAKE_STATUS, &in_value, ACPI_MTX_DO_NOT_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Spin until we wake */ + + } while (!in_value); + + return_ACPI_STATUS (AE_OK); +} +EXPORT_SYMBOL(acpi_enter_sleep_state); + + +/****************************************************************************** + * + * FUNCTION: acpi_enter_sleep_state_s4bios + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Perform a S4 bios request. + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED + * + ******************************************************************************/ + +acpi_status asmlinkage +acpi_enter_sleep_state_s4bios ( + void) +{ + u32 in_value; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_enter_sleep_state_s4bios"); + + + status = acpi_set_register (ACPI_BITREG_WAKE_STATUS, 1, ACPI_MTX_DO_NOT_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_hw_clear_acpi_status (ACPI_MTX_DO_NOT_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * 1) Disable/Clear all GPEs + * 2) Enable all wakeup GPEs + */ + status = acpi_hw_disable_all_gpes (ACPI_ISR); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + acpi_gbl_system_awake_and_running = FALSE; + + status = acpi_hw_enable_all_wakeup_gpes (ACPI_ISR); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + ACPI_FLUSH_CPU_CACHE (); + + status = acpi_os_write_port (acpi_gbl_FADT->smi_cmd, (u32) acpi_gbl_FADT->S4bios_req, 8); + + do { + acpi_os_stall(1000); + status = acpi_get_register (ACPI_BITREG_WAKE_STATUS, &in_value, ACPI_MTX_DO_NOT_LOCK); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } while (!in_value); + + return_ACPI_STATUS (AE_OK); +} +EXPORT_SYMBOL(acpi_enter_sleep_state_s4bios); + + +/****************************************************************************** + * + * FUNCTION: acpi_leave_sleep_state + * + * PARAMETERS: sleep_state - Which sleep state we just exited + * + * RETURN: Status + * + * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep + * Called with interrupts ENABLED. + * + ******************************************************************************/ + +acpi_status +acpi_leave_sleep_state ( + u8 sleep_state) +{ + struct acpi_object_list arg_list; + union acpi_object arg; + acpi_status status; + struct acpi_bit_register_info *sleep_type_reg_info; + struct acpi_bit_register_info *sleep_enable_reg_info; + u32 PM1Acontrol; + u32 PM1Bcontrol; + + + ACPI_FUNCTION_TRACE ("acpi_leave_sleep_state"); + + + /* + * Set SLP_TYPE and SLP_EN to state S0. + * This is unclear from the ACPI Spec, but it is required + * by some machines. + */ + status = acpi_get_sleep_type_data (ACPI_STATE_S0, + &acpi_gbl_sleep_type_a, &acpi_gbl_sleep_type_b); + if (ACPI_SUCCESS (status)) { + sleep_type_reg_info = acpi_hw_get_bit_register_info (ACPI_BITREG_SLEEP_TYPE_A); + sleep_enable_reg_info = acpi_hw_get_bit_register_info (ACPI_BITREG_SLEEP_ENABLE); + + /* Get current value of PM1A control */ + + status = acpi_hw_register_read (ACPI_MTX_DO_NOT_LOCK, + ACPI_REGISTER_PM1_CONTROL, &PM1Acontrol); + if (ACPI_SUCCESS (status)) { + /* Clear SLP_EN and SLP_TYP fields */ + + PM1Acontrol &= ~(sleep_type_reg_info->access_bit_mask | + sleep_enable_reg_info->access_bit_mask); + PM1Bcontrol = PM1Acontrol; + + /* Insert SLP_TYP bits */ + + PM1Acontrol |= (acpi_gbl_sleep_type_a << sleep_type_reg_info->bit_position); + PM1Bcontrol |= (acpi_gbl_sleep_type_b << sleep_type_reg_info->bit_position); + + /* Just ignore any errors */ + + (void) acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, + ACPI_REGISTER_PM1A_CONTROL, PM1Acontrol); + (void) acpi_hw_register_write (ACPI_MTX_DO_NOT_LOCK, + ACPI_REGISTER_PM1B_CONTROL, PM1Bcontrol); + } + } + + /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */ + + acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID; + + /* Setup parameter object */ + + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + + /* Ignore any errors from these methods */ + + arg.integer.value = ACPI_SST_WAKING; + status = acpi_evaluate_object (NULL, METHOD_NAME__SST, &arg_list, NULL); + if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) { + ACPI_REPORT_ERROR (("Method _SST failed, %s\n", acpi_format_exception (status))); + } + + arg.integer.value = sleep_state; + status = acpi_evaluate_object (NULL, METHOD_NAME__BFS, &arg_list, NULL); + if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) { + ACPI_REPORT_ERROR (("Method _BFS failed, %s\n", acpi_format_exception (status))); + } + + status = acpi_evaluate_object (NULL, METHOD_NAME__WAK, &arg_list, NULL); + if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) { + ACPI_REPORT_ERROR (("Method _WAK failed, %s\n", acpi_format_exception (status))); + } + /* TBD: _WAK "sometimes" returns stuff - do we want to look at it? */ + + /* + * Restore the GPEs: + * 1) Disable/Clear all GPEs + * 2) Enable all runtime GPEs + */ + status = acpi_hw_disable_all_gpes (ACPI_NOT_ISR); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + acpi_gbl_system_awake_and_running = TRUE; + + status = acpi_hw_enable_all_runtime_gpes (ACPI_NOT_ISR); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Enable power button */ + + (void) acpi_set_register(acpi_gbl_fixed_event_info[ACPI_EVENT_POWER_BUTTON].enable_register_id, + 1, ACPI_MTX_DO_NOT_LOCK); + (void) acpi_set_register(acpi_gbl_fixed_event_info[ACPI_EVENT_POWER_BUTTON].status_register_id, + 1, ACPI_MTX_DO_NOT_LOCK); + + arg.integer.value = ACPI_SST_WORKING; + status = acpi_evaluate_object (NULL, METHOD_NAME__SST, &arg_list, NULL); + if (ACPI_FAILURE (status) && status != AE_NOT_FOUND) { + ACPI_REPORT_ERROR (("Method _SST failed, %s\n", acpi_format_exception (status))); + } + + return_ACPI_STATUS (status); +} diff --git a/drivers/acpi/hardware/hwtimer.c b/drivers/acpi/hardware/hwtimer.c new file mode 100644 index 00000000000..1906167d729 --- /dev/null +++ b/drivers/acpi/hardware/hwtimer.c @@ -0,0 +1,203 @@ + +/****************************************************************************** + * + * Name: hwtimer.c - ACPI Power Management Timer Interface + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> + +#define _COMPONENT ACPI_HARDWARE + ACPI_MODULE_NAME ("hwtimer") + + +/****************************************************************************** + * + * FUNCTION: acpi_get_timer_resolution + * + * PARAMETERS: Resolution - Where the resolution is returned + * + * RETURN: Status and timer resolution + * + * DESCRIPTION: Obtains resolution of the ACPI PM Timer (24 or 32 bits). + * + ******************************************************************************/ + +acpi_status +acpi_get_timer_resolution ( + u32 *resolution) +{ + ACPI_FUNCTION_TRACE ("acpi_get_timer_resolution"); + + + if (!resolution) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (0 == acpi_gbl_FADT->tmr_val_ext) { + *resolution = 24; + } + else { + *resolution = 32; + } + + return_ACPI_STATUS (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_get_timer + * + * PARAMETERS: Ticks - Where the timer value is returned + * + * RETURN: Status and current ticks + * + * DESCRIPTION: Obtains current value of ACPI PM Timer (in ticks). + * + ******************************************************************************/ + +acpi_status +acpi_get_timer ( + u32 *ticks) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_get_timer"); + + + if (!ticks) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_hw_low_level_read (32, ticks, &acpi_gbl_FADT->xpm_tmr_blk); + + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_get_timer); + + +/****************************************************************************** + * + * FUNCTION: acpi_get_timer_duration + * + * PARAMETERS: start_ticks - Starting timestamp + * end_ticks - End timestamp + * time_elapsed - Where the elapsed time is returned + * + * RETURN: Status and time_elapsed + * + * DESCRIPTION: Computes the time elapsed (in microseconds) between two + * PM Timer time stamps, taking into account the possibility of + * rollovers, the timer resolution, and timer frequency. + * + * The PM Timer's clock ticks at roughly 3.6 times per + * _microsecond_, and its clock continues through Cx state + * transitions (unlike many CPU timestamp counters) -- making it + * a versatile and accurate timer. + * + * Note that this function accommodates only a single timer + * rollover. Thus for 24-bit timers, this function should only + * be used for calculating durations less than ~4.6 seconds + * (~20 minutes for 32-bit timers) -- calculations below: + * + * 2**24 Ticks / 3,600,000 Ticks/Sec = 4.66 sec + * 2**32 Ticks / 3,600,000 Ticks/Sec = 1193 sec or 19.88 minutes + * + ******************************************************************************/ + +acpi_status +acpi_get_timer_duration ( + u32 start_ticks, + u32 end_ticks, + u32 *time_elapsed) +{ + acpi_status status; + u32 delta_ticks; + acpi_integer quotient; + + + ACPI_FUNCTION_TRACE ("acpi_get_timer_duration"); + + + if (!time_elapsed) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Compute Tick Delta: + * Handle (max one) timer rollovers on 24-bit versus 32-bit timers. + */ + if (start_ticks < end_ticks) { + delta_ticks = end_ticks - start_ticks; + } + else if (start_ticks > end_ticks) { + if (0 == acpi_gbl_FADT->tmr_val_ext) { + /* 24-bit Timer */ + + delta_ticks = (((0x00FFFFFF - start_ticks) + end_ticks) & 0x00FFFFFF); + } + else { + /* 32-bit Timer */ + + delta_ticks = (0xFFFFFFFF - start_ticks) + end_ticks; + } + } + else /* start_ticks == end_ticks */ { + *time_elapsed = 0; + return_ACPI_STATUS (AE_OK); + } + + /* + * Compute Duration (Requires a 64-bit multiply and divide): + * + * time_elapsed = (delta_ticks * 1000000) / PM_TIMER_FREQUENCY; + */ + status = acpi_ut_short_divide (((u64) delta_ticks) * 1000000, + PM_TIMER_FREQUENCY, "ient, NULL); + + *time_elapsed = (u32) quotient; + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_get_timer_duration); + diff --git a/drivers/acpi/ibm_acpi.c b/drivers/acpi/ibm_acpi.c new file mode 100644 index 00000000000..0fb731a470d --- /dev/null +++ b/drivers/acpi/ibm_acpi.c @@ -0,0 +1,1242 @@ +/* + * ibm_acpi.c - IBM ThinkPad ACPI Extras + * + * + * Copyright (C) 2004 Borislav Deianov + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Changelog: + * + * 2004-08-09 0.1 initial release, support for X series + * 2004-08-14 0.2 support for T series, X20 + * bluetooth enable/disable + * hotkey events disabled by default + * removed fan control, currently useless + * 2004-08-17 0.3 support for R40 + * lcd off, brightness control + * thinklight on/off + * 2004-09-16 0.4 support for module parameters + * hotkey mask can be prefixed by 0x + * video output switching + * video expansion control + * ultrabay eject support + * removed lcd brightness/on/off control, didn't work + * 2004-10-18 0.5 thinklight support on A21e, G40, R32, T20, T21, X20 + * proc file format changed + * video_switch command + * experimental cmos control + * experimental led control + * experimental acpi sounds + * 2004-10-19 0.6 use acpi_bus_register_driver() to claim HKEY device + * 2004-10-23 0.7 fix module loading on A21e, A22p, T20, T21, X20 + * fix LED control on A21e + * 2004-11-08 0.8 fix init error case, don't return from a macro + * thanks to Chris Wright <chrisw@osdl.org> + */ + +#define IBM_VERSION "0.8" + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_drivers.h> +#include <acpi/acnamesp.h> + +#define IBM_NAME "ibm" +#define IBM_DESC "IBM ThinkPad ACPI Extras" +#define IBM_FILE "ibm_acpi" +#define IBM_URL "http://ibm-acpi.sf.net/" + +#define IBM_DIR IBM_NAME + +#define IBM_LOG IBM_FILE ": " +#define IBM_ERR KERN_ERR IBM_LOG +#define IBM_NOTICE KERN_NOTICE IBM_LOG +#define IBM_INFO KERN_INFO IBM_LOG +#define IBM_DEBUG KERN_DEBUG IBM_LOG + +#define IBM_MAX_ACPI_ARGS 3 + +#define __unused __attribute__ ((unused)) + +static int experimental; +module_param(experimental, int, 0); + +static acpi_handle root_handle = NULL; + +#define IBM_HANDLE(object, parent, paths...) \ + static acpi_handle object##_handle; \ + static acpi_handle *object##_parent = &parent##_handle; \ + static char *object##_paths[] = { paths } + +IBM_HANDLE(ec, root, + "\\_SB.PCI0.ISA.EC", /* A21e, A22p, T20, T21, X20 */ + "\\_SB.PCI0.LPC.EC", /* all others */ +); + +IBM_HANDLE(vid, root, + "\\_SB.PCI0.VID", /* A21e, G40, X30, X40 */ + "\\_SB.PCI0.AGP.VID", /* all others */ +); + +IBM_HANDLE(cmos, root, + "\\UCMS", /* R50, R50p, R51, T4x, X31, X40 */ + "\\CMOS", /* A3x, G40, R32, T23, T30, X22, X24, X30 */ + "\\CMS", /* R40, R40e */ +); /* A21e, A22p, T20, T21, X20 */ + +IBM_HANDLE(dock, root, + "\\_SB.GDCK", /* X30, X31, X40 */ + "\\_SB.PCI0.DOCK", /* A22p, T20, T21, X20 */ + "\\_SB.PCI0.PCI1.DOCK", /* all others */ +); /* A21e, G40, R32, R40, R40e */ + +IBM_HANDLE(bay, root, + "\\_SB.PCI0.IDE0.SCND.MSTR"); /* all except A21e */ +IBM_HANDLE(bayej, root, + "\\_SB.PCI0.IDE0.SCND.MSTR._EJ0"); /* all except A2x, A3x */ + +IBM_HANDLE(lght, root, "\\LGHT"); /* A21e, A22p, T20, T21, X20 */ +IBM_HANDLE(hkey, ec, "HKEY"); /* all */ +IBM_HANDLE(led, ec, "LED"); /* all except A21e, A22p, T20, T21, X20 */ +IBM_HANDLE(sysl, ec, "SYSL"); /* A21e, A22p, T20, T21, X20 */ +IBM_HANDLE(bled, ec, "BLED"); /* A22p, T20, T21, X20 */ +IBM_HANDLE(beep, ec, "BEEP"); /* all models */ + +struct ibm_struct { + char *name; + + char *hid; + struct acpi_driver *driver; + + int (*init) (struct ibm_struct *); + int (*read) (struct ibm_struct *, char *); + int (*write) (struct ibm_struct *, char *); + void (*exit) (struct ibm_struct *); + + void (*notify) (struct ibm_struct *, u32); + acpi_handle *handle; + int type; + struct acpi_device *device; + + int driver_registered; + int proc_created; + int init_called; + int notify_installed; + + int supported; + union { + struct { + int status; + int mask; + } hotkey; + struct { + int autoswitch; + } video; + } state; + + int experimental; +}; + +static struct proc_dir_entry *proc_dir = NULL; + +#define onoff(status,bit) ((status) & (1 << (bit)) ? "on" : "off") +#define enabled(status,bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") +#define strlencmp(a,b) (strncmp((a), (b), strlen(b))) + +static int acpi_evalf(acpi_handle handle, + void *res, char *method, char *fmt, ...) +{ + char *fmt0 = fmt; + struct acpi_object_list params; + union acpi_object in_objs[IBM_MAX_ACPI_ARGS]; + struct acpi_buffer result; + union acpi_object out_obj; + acpi_status status; + va_list ap; + char res_type; + int success; + int quiet; + + if (!*fmt) { + printk(IBM_ERR "acpi_evalf() called with empty format\n"); + return 0; + } + + if (*fmt == 'q') { + quiet = 1; + fmt++; + } else + quiet = 0; + + res_type = *(fmt++); + + params.count = 0; + params.pointer = &in_objs[0]; + + va_start(ap, fmt); + while (*fmt) { + char c = *(fmt++); + switch (c) { + case 'd': /* int */ + in_objs[params.count].integer.value = va_arg(ap, int); + in_objs[params.count++].type = ACPI_TYPE_INTEGER; + break; + /* add more types as needed */ + default: + printk(IBM_ERR "acpi_evalf() called " + "with invalid format character '%c'\n", c); + return 0; + } + } + va_end(ap); + + result.length = sizeof(out_obj); + result.pointer = &out_obj; + + status = acpi_evaluate_object(handle, method, ¶ms, &result); + + switch (res_type) { + case 'd': /* int */ + if (res) + *(int *)res = out_obj.integer.value; + success = status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER; + break; + case 'v': /* void */ + success = status == AE_OK; + break; + /* add more types as needed */ + default: + printk(IBM_ERR "acpi_evalf() called " + "with invalid format character '%c'\n", res_type); + return 0; + } + + if (!success && !quiet) + printk(IBM_ERR "acpi_evalf(%s, %s, ...) failed: %d\n", + method, fmt0, status); + + return success; +} + +static void __unused acpi_print_int(acpi_handle handle, char *method) +{ + int i; + + if (acpi_evalf(handle, &i, method, "d")) + printk(IBM_INFO "%s = 0x%x\n", method, i); + else + printk(IBM_ERR "error calling %s\n", method); +} + +static char *next_cmd(char **cmds) +{ + char *start = *cmds; + char *end; + + while ((end = strchr(start, ',')) && end == start) + start = end + 1; + + if (!end) + return NULL; + + *end = 0; + *cmds = end + 1; + return start; +} + +static int driver_init(struct ibm_struct *ibm) +{ + printk(IBM_INFO "%s v%s\n", IBM_DESC, IBM_VERSION); + printk(IBM_INFO "%s\n", IBM_URL); + + return 0; +} + +static int driver_read(struct ibm_struct *ibm, char *p) +{ + int len = 0; + + len += sprintf(p + len, "driver:\t\t%s\n", IBM_DESC); + len += sprintf(p + len, "version:\t%s\n", IBM_VERSION); + + return len; +} + +static int hotkey_get(struct ibm_struct *ibm, int *status, int *mask) +{ + if (!acpi_evalf(hkey_handle, status, "DHKC", "d")) + return -EIO; + if (ibm->supported) { + if (!acpi_evalf(hkey_handle, mask, "DHKN", "qd")) + return -EIO; + } else { + *mask = ibm->state.hotkey.mask; + } + return 0; +} + +static int hotkey_set(struct ibm_struct *ibm, int status, int mask) +{ + int i; + + if (!acpi_evalf(hkey_handle, NULL, "MHKC", "vd", status)) + return -EIO; + + if (!ibm->supported) + return 0; + + for (i=0; i<32; i++) { + int bit = ((1 << i) & mask) != 0; + if (!acpi_evalf(hkey_handle, NULL, "MHKM", "vdd", i+1, bit)) + return -EIO; + } + + return 0; +} + +static int hotkey_init(struct ibm_struct *ibm) +{ + int ret; + + ibm->supported = 1; + ret = hotkey_get(ibm, + &ibm->state.hotkey.status, + &ibm->state.hotkey.mask); + if (ret < 0) { + /* mask not supported on A21e, A22p, T20, T21, X20, X22, X24 */ + ibm->supported = 0; + ret = hotkey_get(ibm, + &ibm->state.hotkey.status, + &ibm->state.hotkey.mask); + } + + return ret; +} + +static int hotkey_read(struct ibm_struct *ibm, char *p) +{ + int status, mask; + int len = 0; + + if (hotkey_get(ibm, &status, &mask) < 0) + return -EIO; + + len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 0)); + if (ibm->supported) { + len += sprintf(p + len, "mask:\t\t0x%04x\n", mask); + len += sprintf(p + len, + "commands:\tenable, disable, reset, <mask>\n"); + } else { + len += sprintf(p + len, "mask:\t\tnot supported\n"); + len += sprintf(p + len, "commands:\tenable, disable, reset\n"); + } + + return len; +} + +static int hotkey_write(struct ibm_struct *ibm, char *buf) +{ + int status, mask; + char *cmd; + int do_cmd = 0; + + if (hotkey_get(ibm, &status, &mask) < 0) + return -ENODEV; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + status = 1; + } else if (strlencmp(cmd, "disable") == 0) { + status = 0; + } else if (strlencmp(cmd, "reset") == 0) { + status = ibm->state.hotkey.status; + mask = ibm->state.hotkey.mask; + } else if (sscanf(cmd, "0x%x", &mask) == 1) { + /* mask set */ + } else if (sscanf(cmd, "%x", &mask) == 1) { + /* mask set */ + } else + return -EINVAL; + do_cmd = 1; + } + + if (do_cmd && hotkey_set(ibm, status, mask) < 0) + return -EIO; + + return 0; +} + +static void hotkey_exit(struct ibm_struct *ibm) +{ + hotkey_set(ibm, ibm->state.hotkey.status, ibm->state.hotkey.mask); +} + +static void hotkey_notify(struct ibm_struct *ibm, u32 event) +{ + int hkey; + + if (acpi_evalf(hkey_handle, &hkey, "MHKP", "d")) + acpi_bus_generate_event(ibm->device, event, hkey); + else { + printk(IBM_ERR "unknown hotkey event %d\n", event); + acpi_bus_generate_event(ibm->device, event, 0); + } +} + +static int bluetooth_init(struct ibm_struct *ibm) +{ + /* bluetooth not supported on A21e, G40, T20, T21, X20 */ + ibm->supported = acpi_evalf(hkey_handle, NULL, "GBDC", "qv"); + + return 0; +} + +static int bluetooth_status(struct ibm_struct *ibm) +{ + int status; + + if (!ibm->supported || !acpi_evalf(hkey_handle, &status, "GBDC", "d")) + status = 0; + + return status; +} + +static int bluetooth_read(struct ibm_struct *ibm, char *p) +{ + int len = 0; + int status = bluetooth_status(ibm); + + if (!ibm->supported) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else if (!(status & 1)) + len += sprintf(p + len, "status:\t\tnot installed\n"); + else { + len += sprintf(p + len, "status:\t\t%s\n", enabled(status, 1)); + len += sprintf(p + len, "commands:\tenable, disable\n"); + } + + return len; +} + +static int bluetooth_write(struct ibm_struct *ibm, char *buf) +{ + int status = bluetooth_status(ibm); + char *cmd; + int do_cmd = 0; + + if (!ibm->supported) + return -EINVAL; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "enable") == 0) { + status |= 2; + } else if (strlencmp(cmd, "disable") == 0) { + status &= ~2; + } else + return -EINVAL; + do_cmd = 1; + } + + if (do_cmd && !acpi_evalf(hkey_handle, NULL, "SBDC", "vd", status)) + return -EIO; + + return 0; +} + +static int video_init(struct ibm_struct *ibm) +{ + if (!acpi_evalf(vid_handle, + &ibm->state.video.autoswitch, "^VDEE", "d")) + return -ENODEV; + + return 0; +} + +static int video_status(struct ibm_struct *ibm) +{ + int status = 0; + int i; + + acpi_evalf(NULL, NULL, "\\VUPS", "vd", 1); + if (acpi_evalf(NULL, &i, "\\VCDC", "d")) + status |= 0x02 * i; + + acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0); + if (acpi_evalf(NULL, &i, "\\VCDL", "d")) + status |= 0x01 * i; + if (acpi_evalf(NULL, &i, "\\VCDD", "d")) + status |= 0x08 * i; + + if (acpi_evalf(vid_handle, &i, "^VDEE", "d")) + status |= 0x10 * (i & 1); + + return status; +} + +static int video_read(struct ibm_struct *ibm, char *p) +{ + int status = video_status(ibm); + int len = 0; + + len += sprintf(p + len, "lcd:\t\t%s\n", enabled(status, 0)); + len += sprintf(p + len, "crt:\t\t%s\n", enabled(status, 1)); + len += sprintf(p + len, "dvi:\t\t%s\n", enabled(status, 3)); + len += sprintf(p + len, "auto:\t\t%s\n", enabled(status, 4)); + len += sprintf(p + len, "commands:\tlcd_enable, lcd_disable, " + "crt_enable, crt_disable\n"); + len += sprintf(p + len, "commands:\tdvi_enable, dvi_disable, " + "auto_enable, auto_disable\n"); + len += sprintf(p + len, "commands:\tvideo_switch, expand_toggle\n"); + + return len; +} + +static int video_write(struct ibm_struct *ibm, char *buf) +{ + char *cmd; + int enable, disable, status; + + enable = disable = 0; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "lcd_enable") == 0) { + enable |= 0x01; + } else if (strlencmp(cmd, "lcd_disable") == 0) { + disable |= 0x01; + } else if (strlencmp(cmd, "crt_enable") == 0) { + enable |= 0x02; + } else if (strlencmp(cmd, "crt_disable") == 0) { + disable |= 0x02; + } else if (strlencmp(cmd, "dvi_enable") == 0) { + enable |= 0x08; + } else if (strlencmp(cmd, "dvi_disable") == 0) { + disable |= 0x08; + } else if (strlencmp(cmd, "auto_enable") == 0) { + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) + return -EIO; + } else if (strlencmp(cmd, "auto_disable") == 0) { + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 0)) + return -EIO; + } else if (strlencmp(cmd, "video_switch") == 0) { + int autoswitch; + if (!acpi_evalf(vid_handle, &autoswitch, "^VDEE", "d")) + return -EIO; + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", 1)) + return -EIO; + if (!acpi_evalf(vid_handle, NULL, "VSWT", "v")) + return -EIO; + if (!acpi_evalf(vid_handle, NULL, "_DOS", "vd", + autoswitch)) + return -EIO; + } else if (strlencmp(cmd, "expand_toggle") == 0) { + if (!acpi_evalf(NULL, NULL, "\\VEXP", "v")) + return -EIO; + } else + return -EINVAL; + } + + if (enable || disable) { + status = (video_status(ibm) & 0x0f & ~disable) | enable; + if (!acpi_evalf(NULL, NULL, "\\VUPS", "vd", 0x80)) + return -EIO; + if (!acpi_evalf(NULL, NULL, "\\VSDS", "vdd", status, 1)) + return -EIO; + } + + return 0; +} + +static void video_exit(struct ibm_struct *ibm) +{ + acpi_evalf(vid_handle, NULL, "_DOS", "vd", + ibm->state.video.autoswitch); +} + +static int light_init(struct ibm_struct *ibm) +{ + /* kblt not supported on G40, R32, X20 */ + ibm->supported = acpi_evalf(ec_handle, NULL, "KBLT", "qv"); + + return 0; +} + +static int light_read(struct ibm_struct *ibm, char *p) +{ + int len = 0; + int status = 0; + + if (ibm->supported) { + if (!acpi_evalf(ec_handle, &status, "KBLT", "d")) + return -EIO; + len += sprintf(p + len, "status:\t\t%s\n", onoff(status, 0)); + } else + len += sprintf(p + len, "status:\t\tunknown\n"); + + len += sprintf(p + len, "commands:\ton, off\n"); + + return len; +} + +static int light_write(struct ibm_struct *ibm, char *buf) +{ + int cmos_cmd, lght_cmd; + char *cmd; + int success; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "on") == 0) { + cmos_cmd = 0x0c; + lght_cmd = 1; + } else if (strlencmp(cmd, "off") == 0) { + cmos_cmd = 0x0d; + lght_cmd = 0; + } else + return -EINVAL; + + success = cmos_handle ? + acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd) : + acpi_evalf(lght_handle, NULL, NULL, "vd", lght_cmd); + if (!success) + return -EIO; + } + + return 0; +} + +static int _sta(acpi_handle handle) +{ + int status; + + if (!handle || !acpi_evalf(handle, &status, "_STA", "d")) + status = 0; + + return status; +} + +#define dock_docked() (_sta(dock_handle) & 1) + +static int dock_read(struct ibm_struct *ibm, char *p) +{ + int len = 0; + int docked = dock_docked(); + + if (!dock_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else if (!docked) + len += sprintf(p + len, "status:\t\tundocked\n"); + else { + len += sprintf(p + len, "status:\t\tdocked\n"); + len += sprintf(p + len, "commands:\tdock, undock\n"); + } + + return len; +} + +static int dock_write(struct ibm_struct *ibm, char *buf) +{ + char *cmd; + + if (!dock_docked()) + return -EINVAL; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "undock") == 0) { + if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 0)) + return -EIO; + if (!acpi_evalf(dock_handle, NULL, "_EJ0", "vd", 1)) + return -EIO; + } else if (strlencmp(cmd, "dock") == 0) { + if (!acpi_evalf(dock_handle, NULL, "_DCK", "vd", 1)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +static void dock_notify(struct ibm_struct *ibm, u32 event) +{ + int docked = dock_docked(); + + if (event == 3 && docked) + acpi_bus_generate_event(ibm->device, event, 1); /* button */ + else if (event == 3 && !docked) + acpi_bus_generate_event(ibm->device, event, 2); /* undock */ + else if (event == 0 && docked) + acpi_bus_generate_event(ibm->device, event, 3); /* dock */ + else { + printk(IBM_ERR "unknown dock event %d, status %d\n", + event, _sta(dock_handle)); + acpi_bus_generate_event(ibm->device, event, 0); /* unknown */ + } +} + +#define bay_occupied() (_sta(bay_handle) & 1) + +static int bay_init(struct ibm_struct *ibm) +{ + /* bay not supported on A21e, A22p, A31, A31p, G40, R32, R40e */ + ibm->supported = bay_handle && bayej_handle && + acpi_evalf(bay_handle, NULL, "_STA", "qv"); + + return 0; +} + +static int bay_read(struct ibm_struct *ibm, char *p) +{ + int len = 0; + int occupied = bay_occupied(); + + if (!ibm->supported) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else if (!occupied) + len += sprintf(p + len, "status:\t\tunoccupied\n"); + else { + len += sprintf(p + len, "status:\t\toccupied\n"); + len += sprintf(p + len, "commands:\teject\n"); + } + + return len; +} + +static int bay_write(struct ibm_struct *ibm, char *buf) +{ + char *cmd; + + while ((cmd = next_cmd(&buf))) { + if (strlencmp(cmd, "eject") == 0) { + if (!ibm->supported || + !acpi_evalf(bay_handle, NULL, "_EJ0", "vd", 1)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +static void bay_notify(struct ibm_struct *ibm, u32 event) +{ + acpi_bus_generate_event(ibm->device, event, 0); +} + +static int cmos_read(struct ibm_struct *ibm, char *p) +{ + int len = 0; + + /* cmos not supported on A21e, A22p, T20, T21, X20 */ + if (!cmos_handle) + len += sprintf(p + len, "status:\t\tnot supported\n"); + else { + len += sprintf(p + len, "status:\t\tsupported\n"); + len += sprintf(p + len, "commands:\t<int>\n"); + } + + return len; +} + +static int cmos_write(struct ibm_struct *ibm, char *buf) +{ + char *cmd; + int cmos_cmd; + + if (!cmos_handle) + return -EINVAL; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%u", &cmos_cmd) == 1) { + /* cmos_cmd set */ + } else + return -EINVAL; + + if (!acpi_evalf(cmos_handle, NULL, NULL, "vd", cmos_cmd)) + return -EIO; + } + + return 0; +} + +static int led_read(struct ibm_struct *ibm, char *p) +{ + int len = 0; + + len += sprintf(p + len, "commands:\t" + "<int> on, <int> off, <int> blink\n"); + + return len; +} + +static int led_write(struct ibm_struct *ibm, char *buf) +{ + char *cmd; + unsigned int led; + int led_cmd, sysl_cmd, bled_a, bled_b; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%u", &led) != 1) + return -EINVAL; + + if (strstr(cmd, "blink")) { + led_cmd = 0xc0; + sysl_cmd = 2; + bled_a = 2; + bled_b = 1; + } else if (strstr(cmd, "on")) { + led_cmd = 0x80; + sysl_cmd = 1; + bled_a = 2; + bled_b = 0; + } else if (strstr(cmd, "off")) { + led_cmd = sysl_cmd = bled_a = bled_b = 0; + } else + return -EINVAL; + + if (led_handle) { + if (!acpi_evalf(led_handle, NULL, NULL, "vdd", + led, led_cmd)) + return -EIO; + } else if (led < 2) { + if (acpi_evalf(sysl_handle, NULL, NULL, "vdd", + led, sysl_cmd)) + return -EIO; + } else if (led == 2 && bled_handle) { + if (acpi_evalf(bled_handle, NULL, NULL, "vdd", + bled_a, bled_b)) + return -EIO; + } else + return -EINVAL; + } + + return 0; +} + +static int beep_read(struct ibm_struct *ibm, char *p) +{ + int len = 0; + + len += sprintf(p + len, "commands:\t<int>\n"); + + return len; +} + +static int beep_write(struct ibm_struct *ibm, char *buf) +{ + char *cmd; + int beep_cmd; + + while ((cmd = next_cmd(&buf))) { + if (sscanf(cmd, "%u", &beep_cmd) == 1) { + /* beep_cmd set */ + } else + return -EINVAL; + + if (!acpi_evalf(beep_handle, NULL, NULL, "vd", beep_cmd)) + return -EIO; + } + + return 0; +} + +static struct ibm_struct ibms[] = { + { + .name = "driver", + .init = driver_init, + .read = driver_read, + }, + { + .name = "hotkey", + .hid = "IBM0068", + .init = hotkey_init, + .read = hotkey_read, + .write = hotkey_write, + .exit = hotkey_exit, + .notify = hotkey_notify, + .handle = &hkey_handle, + .type = ACPI_DEVICE_NOTIFY, + }, + { + .name = "bluetooth", + .init = bluetooth_init, + .read = bluetooth_read, + .write = bluetooth_write, + }, + { + .name = "video", + .init = video_init, + .read = video_read, + .write = video_write, + .exit = video_exit, + }, + { + .name = "light", + .init = light_init, + .read = light_read, + .write = light_write, + }, + { + .name = "dock", + .read = dock_read, + .write = dock_write, + .notify = dock_notify, + .handle = &dock_handle, + .type = ACPI_SYSTEM_NOTIFY, + }, + { + .name = "bay", + .init = bay_init, + .read = bay_read, + .write = bay_write, + .notify = bay_notify, + .handle = &bay_handle, + .type = ACPI_SYSTEM_NOTIFY, + }, + { + .name = "cmos", + .read = cmos_read, + .write = cmos_write, + .experimental = 1, + }, + { + .name = "led", + .read = led_read, + .write = led_write, + .experimental = 1, + }, + { + .name = "beep", + .read = beep_read, + .write = beep_write, + .experimental = 1, + }, +}; +#define NUM_IBMS (sizeof(ibms)/sizeof(ibms[0])) + +static int dispatch_read(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + struct ibm_struct *ibm = (struct ibm_struct *)data; + int len; + + if (!ibm || !ibm->read) + return -EINVAL; + + len = ibm->read(ibm, page); + if (len < 0) + return len; + + if (len <= off + count) + *eof = 1; + *start = page + off; + len -= off; + if (len > count) + len = count; + if (len < 0) + len = 0; + + return len; +} + +static int dispatch_write(struct file *file, const char __user *userbuf, + unsigned long count, void *data) +{ + struct ibm_struct *ibm = (struct ibm_struct *)data; + char *kernbuf; + int ret; + + if (!ibm || !ibm->write) + return -EINVAL; + + kernbuf = kmalloc(count + 2, GFP_KERNEL); + if (!kernbuf) + return -ENOMEM; + + if (copy_from_user(kernbuf, userbuf, count)) { + kfree(kernbuf); + return -EFAULT; + } + + kernbuf[count] = 0; + strcat(kernbuf, ","); + ret = ibm->write(ibm, kernbuf); + if (ret == 0) + ret = count; + + kfree(kernbuf); + + return ret; +} + +static void dispatch_notify(acpi_handle handle, u32 event, void *data) +{ + struct ibm_struct *ibm = (struct ibm_struct *)data; + + if (!ibm || !ibm->notify) + return; + + ibm->notify(ibm, event); +} + +static int setup_notify(struct ibm_struct *ibm) +{ + acpi_status status; + int ret; + + if (!*ibm->handle) + return 0; + + ret = acpi_bus_get_device(*ibm->handle, &ibm->device); + if (ret < 0) { + printk(IBM_ERR "%s device not present\n", ibm->name); + return 0; + } + + acpi_driver_data(ibm->device) = ibm; + sprintf(acpi_device_class(ibm->device), "%s/%s", IBM_NAME, ibm->name); + + status = acpi_install_notify_handler(*ibm->handle, ibm->type, + dispatch_notify, ibm); + if (ACPI_FAILURE(status)) { + printk(IBM_ERR "acpi_install_notify_handler(%s) failed: %d\n", + ibm->name, status); + return -ENODEV; + } + + ibm->notify_installed = 1; + + return 0; +} + +static int device_add(struct acpi_device *device) +{ + return 0; +} + +static int register_driver(struct ibm_struct *ibm) +{ + int ret; + + ibm->driver = kmalloc(sizeof(struct acpi_driver), GFP_KERNEL); + if (!ibm->driver) { + printk(IBM_ERR "kmalloc(ibm->driver) failed\n"); + return -1; + } + + memset(ibm->driver, 0, sizeof(struct acpi_driver)); + sprintf(ibm->driver->name, "%s/%s", IBM_NAME, ibm->name); + ibm->driver->ids = ibm->hid; + ibm->driver->ops.add = &device_add; + + ret = acpi_bus_register_driver(ibm->driver); + if (ret < 0) { + printk(IBM_ERR "acpi_bus_register_driver(%s) failed: %d\n", + ibm->hid, ret); + kfree(ibm->driver); + } + + return ret; +} + +static int ibm_init(struct ibm_struct *ibm) +{ + int ret; + struct proc_dir_entry *entry; + + if (ibm->experimental && !experimental) + return 0; + + if (ibm->hid) { + ret = register_driver(ibm); + if (ret < 0) + return ret; + ibm->driver_registered = 1; + } + + if (ibm->init) { + ret = ibm->init(ibm); + if (ret != 0) + return ret; + ibm->init_called = 1; + } + + entry = create_proc_entry(ibm->name, S_IFREG | S_IRUGO | S_IWUSR, + proc_dir); + if (!entry) { + printk(IBM_ERR "unable to create proc entry %s\n", ibm->name); + return -ENODEV; + } + entry->owner = THIS_MODULE; + ibm->proc_created = 1; + + entry->data = ibm; + if (ibm->read) + entry->read_proc = &dispatch_read; + if (ibm->write) + entry->write_proc = &dispatch_write; + + if (ibm->notify) { + ret = setup_notify(ibm); + if (ret < 0) + return ret; + } + + return 0; +} + +static void ibm_exit(struct ibm_struct *ibm) +{ + if (ibm->notify_installed) + acpi_remove_notify_handler(*ibm->handle, ibm->type, + dispatch_notify); + + if (ibm->proc_created) + remove_proc_entry(ibm->name, proc_dir); + + if (ibm->init_called && ibm->exit) + ibm->exit(ibm); + + if (ibm->driver_registered) { + acpi_bus_unregister_driver(ibm->driver); + kfree(ibm->driver); + } +} + +static int ibm_handle_init(char *name, + acpi_handle *handle, acpi_handle parent, + char **paths, int num_paths, int required) +{ + int i; + acpi_status status; + + for (i=0; i<num_paths; i++) { + status = acpi_get_handle(parent, paths[i], handle); + if (ACPI_SUCCESS(status)) + return 0; + } + + *handle = NULL; + + if (required) { + printk(IBM_ERR "%s object not found\n", name); + return -1; + } + + return 0; +} + +#define IBM_HANDLE_INIT(object, required) \ + ibm_handle_init(#object, &object##_handle, *object##_parent, \ + object##_paths, sizeof(object##_paths)/sizeof(char*), required) + + +static int set_ibm_param(const char *val, struct kernel_param *kp) +{ + unsigned int i; + char arg_with_comma[32]; + + if (strlen(val) > 30) + return -ENOSPC; + + strcpy(arg_with_comma, val); + strcat(arg_with_comma, ","); + + for (i=0; i<NUM_IBMS; i++) + if (strcmp(ibms[i].name, kp->name) == 0) + return ibms[i].write(&ibms[i], arg_with_comma); + BUG(); + return -EINVAL; +} + +#define IBM_PARAM(feature) \ + module_param_call(feature, set_ibm_param, NULL, NULL, 0) + +static void acpi_ibm_exit(void) +{ + int i; + + for (i=NUM_IBMS-1; i>=0; i--) + ibm_exit(&ibms[i]); + + remove_proc_entry(IBM_DIR, acpi_root_dir); +} + +static int __init acpi_ibm_init(void) +{ + int ret, i; + + if (acpi_disabled) + return -ENODEV; + + /* these handles are required */ + if (IBM_HANDLE_INIT(ec, 1) < 0 || + IBM_HANDLE_INIT(hkey, 1) < 0 || + IBM_HANDLE_INIT(vid, 1) < 0 || + IBM_HANDLE_INIT(beep, 1) < 0) + return -ENODEV; + + /* these handles have alternatives */ + IBM_HANDLE_INIT(lght, 0); + if (IBM_HANDLE_INIT(cmos, !lght_handle) < 0) + return -ENODEV; + IBM_HANDLE_INIT(sysl, 0); + if (IBM_HANDLE_INIT(led, !sysl_handle) < 0) + return -ENODEV; + + /* these handles are not required */ + IBM_HANDLE_INIT(dock, 0); + IBM_HANDLE_INIT(bay, 0); + IBM_HANDLE_INIT(bayej, 0); + IBM_HANDLE_INIT(bled, 0); + + proc_dir = proc_mkdir(IBM_DIR, acpi_root_dir); + if (!proc_dir) { + printk(IBM_ERR "unable to create proc dir %s", IBM_DIR); + return -ENODEV; + } + proc_dir->owner = THIS_MODULE; + + for (i=0; i<NUM_IBMS; i++) { + ret = ibm_init(&ibms[i]); + if (ret < 0) { + acpi_ibm_exit(); + return ret; + } + } + + return 0; +} + +module_init(acpi_ibm_init); +module_exit(acpi_ibm_exit); + +MODULE_AUTHOR("Borislav Deianov"); +MODULE_DESCRIPTION(IBM_DESC); +MODULE_LICENSE("GPL"); + +IBM_PARAM(hotkey); +IBM_PARAM(bluetooth); +IBM_PARAM(video); +IBM_PARAM(light); +IBM_PARAM(dock); +IBM_PARAM(bay); +IBM_PARAM(cmos); +IBM_PARAM(led); +IBM_PARAM(beep); diff --git a/drivers/acpi/motherboard.c b/drivers/acpi/motherboard.c new file mode 100644 index 00000000000..61ea70742d4 --- /dev/null +++ b/drivers/acpi/motherboard.c @@ -0,0 +1,177 @@ +/* + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* Purpose: Prevent PCMCIA cards from using motherboard resources. */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/ioport.h> +#include <asm/io.h> + +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME ("acpi_motherboard") + +/* Dell use PNP0C01 instead of PNP0C02 */ +#define ACPI_MB_HID1 "PNP0C01" +#define ACPI_MB_HID2 "PNP0C02" + +/** + * Doesn't care about legacy IO ports, only IO ports beyond 0x1000 are reserved + * Doesn't care about the failure of 'request_region', since other may reserve + * the io ports as well + */ +#define IS_RESERVED_ADDR(base, len) \ + (((len) > 0) && ((base) > 0) && ((base) + (len) < IO_SPACE_LIMIT) \ + && ((base) + (len) > PCIBIOS_MIN_IO)) + +/* + * Clearing the flag (IORESOURCE_BUSY) allows drivers to use + * the io ports if they really know they can use it, while + * still preventing hotplug PCI devices from using it. + */ + +static acpi_status +acpi_reserve_io_ranges (struct acpi_resource *res, void *data) +{ + struct resource *requested_res = NULL; + + ACPI_FUNCTION_TRACE("acpi_reserve_io_ranges"); + + if (res->id == ACPI_RSTYPE_IO) { + struct acpi_resource_io *io_res = &res->data.io; + + if (io_res->min_base_address != io_res->max_base_address) + return_VALUE(AE_OK); + if (IS_RESERVED_ADDR(io_res->min_base_address, io_res->range_length)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Motherboard resources 0x%08x - 0x%08x\n", + io_res->min_base_address, + io_res->min_base_address + io_res->range_length)); + requested_res = request_region(io_res->min_base_address, + io_res->range_length, "motherboard"); + } + } else if (res->id == ACPI_RSTYPE_FIXED_IO) { + struct acpi_resource_fixed_io *fixed_io_res = &res->data.fixed_io; + + if (IS_RESERVED_ADDR(fixed_io_res->base_address, fixed_io_res->range_length)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Motherboard resources 0x%08x - 0x%08x\n", + fixed_io_res->base_address, + fixed_io_res->base_address + fixed_io_res->range_length)); + requested_res = request_region(fixed_io_res->base_address, + fixed_io_res->range_length, "motherboard"); + } + } else { + /* Memory mapped IO? */ + } + + if (requested_res) + requested_res->flags &= ~IORESOURCE_BUSY; + return_VALUE(AE_OK); +} + +static int acpi_motherboard_add (struct acpi_device *device) +{ + if (!device) + return -EINVAL; + acpi_walk_resources(device->handle, METHOD_NAME__CRS, + acpi_reserve_io_ranges, NULL); + + return 0; +} + +static struct acpi_driver acpi_motherboard_driver1 = { + .name = "motherboard", + .class = "", + .ids = ACPI_MB_HID1, + .ops = { + .add = acpi_motherboard_add, + }, +}; + +static struct acpi_driver acpi_motherboard_driver2 = { + .name = "motherboard", + .class = "", + .ids = ACPI_MB_HID2, + .ops = { + .add = acpi_motherboard_add, + }, +}; + +static void __init +acpi_reserve_resources (void) +{ + if (acpi_gbl_FADT->xpm1a_evt_blk.address && acpi_gbl_FADT->pm1_evt_len) + request_region(acpi_gbl_FADT->xpm1a_evt_blk.address, + acpi_gbl_FADT->pm1_evt_len, "PM1a_EVT_BLK"); + + if (acpi_gbl_FADT->xpm1b_evt_blk.address && acpi_gbl_FADT->pm1_evt_len) + request_region(acpi_gbl_FADT->xpm1b_evt_blk.address, + acpi_gbl_FADT->pm1_evt_len, "PM1b_EVT_BLK"); + + if (acpi_gbl_FADT->xpm1a_cnt_blk.address && acpi_gbl_FADT->pm1_cnt_len) + request_region(acpi_gbl_FADT->xpm1a_cnt_blk.address, + acpi_gbl_FADT->pm1_cnt_len, "PM1a_CNT_BLK"); + + if (acpi_gbl_FADT->xpm1b_cnt_blk.address && acpi_gbl_FADT->pm1_cnt_len) + request_region(acpi_gbl_FADT->xpm1b_cnt_blk.address, + acpi_gbl_FADT->pm1_cnt_len, "PM1b_CNT_BLK"); + + if (acpi_gbl_FADT->xpm_tmr_blk.address && acpi_gbl_FADT->pm_tm_len == 4) + request_region(acpi_gbl_FADT->xpm_tmr_blk.address, + 4, "PM_TMR"); + + if (acpi_gbl_FADT->xpm2_cnt_blk.address && acpi_gbl_FADT->pm2_cnt_len) + request_region(acpi_gbl_FADT->xpm2_cnt_blk.address, + acpi_gbl_FADT->pm2_cnt_len, "PM2_CNT_BLK"); + + /* Length of GPE blocks must be a non-negative multiple of 2 */ + + if (acpi_gbl_FADT->xgpe0_blk.address && acpi_gbl_FADT->gpe0_blk_len && + !(acpi_gbl_FADT->gpe0_blk_len & 0x1)) + request_region(acpi_gbl_FADT->xgpe0_blk.address, + acpi_gbl_FADT->gpe0_blk_len, "GPE0_BLK"); + + if (acpi_gbl_FADT->xgpe1_blk.address && acpi_gbl_FADT->gpe1_blk_len && + !(acpi_gbl_FADT->gpe1_blk_len & 0x1)) + request_region(acpi_gbl_FADT->xgpe1_blk.address, + acpi_gbl_FADT->gpe1_blk_len, "GPE1_BLK"); +} + +static int __init acpi_motherboard_init(void) +{ + acpi_bus_register_driver(&acpi_motherboard_driver1); + acpi_bus_register_driver(&acpi_motherboard_driver2); + /* + * Guarantee motherboard IO reservation first + * This module must run after scan.c + */ + if (!acpi_disabled) + acpi_reserve_resources (); + return 0; +} + +/** + * Reserve motherboard resources after PCI claim BARs, + * but before PCI assign resources for uninitialized PCI devices + */ +fs_initcall(acpi_motherboard_init); diff --git a/drivers/acpi/namespace/Makefile b/drivers/acpi/namespace/Makefile new file mode 100644 index 00000000000..3f63d364069 --- /dev/null +++ b/drivers/acpi/namespace/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for all Linux ACPI interpreter subdirectories +# + +obj-y := nsaccess.o nsload.o nssearch.o nsxfeval.o \ + nsalloc.o nseval.o nsnames.o nsutils.o nsxfname.o \ + nsdump.o nsinit.o nsobject.o nswalk.o nsxfobj.o \ + nsparse.o + +obj-$(ACPI_FUTURE_USAGE) += nsdumpdv.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/namespace/nsaccess.c b/drivers/acpi/namespace/nsaccess.c new file mode 100644 index 00000000000..1c0c12336c5 --- /dev/null +++ b/drivers/acpi/namespace/nsaccess.c @@ -0,0 +1,637 @@ +/******************************************************************************* + * + * Module Name: nsaccess - Top-level functions for accessing ACPI namespace + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> +#include <acpi/acdispat.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsaccess") + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_root_initialize + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Allocate and initialize the default root named objects + * + * MUTEX: Locks namespace for entire execution + * + ******************************************************************************/ + +acpi_status +acpi_ns_root_initialize (void) +{ + acpi_status status; + const struct acpi_predefined_names *init_val = NULL; + struct acpi_namespace_node *new_node; + union acpi_operand_object *obj_desc; + acpi_string val = NULL; + + + ACPI_FUNCTION_TRACE ("ns_root_initialize"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * The global root ptr is initially NULL, so a non-NULL value indicates + * that acpi_ns_root_initialize() has already been called; just return. + */ + if (acpi_gbl_root_node) { + status = AE_OK; + goto unlock_and_exit; + } + + /* + * Tell the rest of the subsystem that the root is initialized + * (This is OK because the namespace is locked) + */ + acpi_gbl_root_node = &acpi_gbl_root_node_struct; + + /* Enter the pre-defined names in the name table */ + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Entering predefined entries into namespace\n")); + + for (init_val = acpi_gbl_pre_defined_names; init_val->name; init_val++) { + /* _OSI is optional for now, will be permanent later */ + + if (!ACPI_STRCMP (init_val->name, "_OSI") && !acpi_gbl_create_osi_method) { + continue; + } + + status = acpi_ns_lookup (NULL, init_val->name, init_val->type, + ACPI_IMODE_LOAD_PASS2, ACPI_NS_NO_UPSEARCH, + NULL, &new_node); + + if (ACPI_FAILURE (status) || (!new_node)) /* Must be on same line for code converter */ { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not create predefined name %s, %s\n", + init_val->name, acpi_format_exception (status))); + } + + /* + * Name entered successfully. + * If entry in pre_defined_names[] specifies an + * initial value, create the initial value. + */ + if (init_val->val) { + status = acpi_os_predefined_override (init_val, &val); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not override predefined %s\n", + init_val->name)); + } + + if (!val) { + val = init_val->val; + } + + /* + * Entry requests an initial value, allocate a + * descriptor for it. + */ + obj_desc = acpi_ut_create_internal_object (init_val->type); + if (!obj_desc) { + status = AE_NO_MEMORY; + goto unlock_and_exit; + } + + /* + * Convert value string from table entry to + * internal representation. Only types actually + * used for initial values are implemented here. + */ + switch (init_val->type) { + case ACPI_TYPE_METHOD: + obj_desc->method.param_count = (u8) ACPI_TO_INTEGER (val); + obj_desc->common.flags |= AOPOBJ_DATA_VALID; + +#if defined (_ACPI_ASL_COMPILER) || defined (_ACPI_DUMP_App) + + /* + * i_aSL Compiler cheats by putting parameter count + * in the owner_iD + */ + new_node->owner_id = obj_desc->method.param_count; +#else + /* Mark this as a very SPECIAL method */ + + obj_desc->method.method_flags = AML_METHOD_INTERNAL_ONLY; + obj_desc->method.implementation = acpi_ut_osi_implementation; +#endif + break; + + case ACPI_TYPE_INTEGER: + + obj_desc->integer.value = ACPI_TO_INTEGER (val); + break; + + + case ACPI_TYPE_STRING: + + /* + * Build an object around the static string + */ + obj_desc->string.length = (u32) ACPI_STRLEN (val); + obj_desc->string.pointer = val; + obj_desc->common.flags |= AOPOBJ_STATIC_POINTER; + break; + + + case ACPI_TYPE_MUTEX: + + obj_desc->mutex.node = new_node; + obj_desc->mutex.sync_level = (u8) (ACPI_TO_INTEGER (val) - 1); + + if (ACPI_STRCMP (init_val->name, "_GL_") == 0) { + /* + * Create a counting semaphore for the + * global lock + */ + status = acpi_os_create_semaphore (ACPI_NO_UNIT_LIMIT, + 1, &obj_desc->mutex.semaphore); + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (obj_desc); + goto unlock_and_exit; + } + + /* + * We just created the mutex for the + * global lock, save it + */ + acpi_gbl_global_lock_semaphore = obj_desc->mutex.semaphore; + } + else { + /* Create a mutex */ + + status = acpi_os_create_semaphore (1, 1, + &obj_desc->mutex.semaphore); + if (ACPI_FAILURE (status)) { + acpi_ut_remove_reference (obj_desc); + goto unlock_and_exit; + } + } + break; + + + default: + + ACPI_REPORT_ERROR (("Unsupported initial type value %X\n", + init_val->type)); + acpi_ut_remove_reference (obj_desc); + obj_desc = NULL; + continue; + } + + /* Store pointer to value descriptor in the Node */ + + status = acpi_ns_attach_object (new_node, obj_desc, + ACPI_GET_OBJECT_TYPE (obj_desc)); + + /* Remove local reference to the object */ + + acpi_ut_remove_reference (obj_desc); + } + } + + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + + /* Save a handle to "_GPE", it is always present */ + + if (ACPI_SUCCESS (status)) { + status = acpi_ns_get_node_by_path ("\\_GPE", NULL, ACPI_NS_NO_UPSEARCH, + &acpi_gbl_fadt_gpe_device); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_lookup + * + * PARAMETERS: prefix_node - Search scope if name is not fully qualified + * Pathname - Search pathname, in internal format + * (as represented in the AML stream) + * Type - Type associated with name + * interpreter_mode - IMODE_LOAD_PASS2 => add name if not found + * Flags - Flags describing the search restrictions + * walk_state - Current state of the walk + * return_node - Where the Node is placed (if found + * or created successfully) + * + * RETURN: Status + * + * DESCRIPTION: Find or enter the passed name in the name space. + * Log an error if name not found in Exec mode. + * + * MUTEX: Assumes namespace is locked. + * + ******************************************************************************/ + +acpi_status +acpi_ns_lookup ( + union acpi_generic_state *scope_info, + char *pathname, + acpi_object_type type, + acpi_interpreter_mode interpreter_mode, + u32 flags, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node **return_node) +{ + acpi_status status; + char *path = pathname; + struct acpi_namespace_node *prefix_node; + struct acpi_namespace_node *current_node = NULL; + struct acpi_namespace_node *this_node = NULL; + u32 num_segments; + u32 num_carats; + acpi_name simple_name; + acpi_object_type type_to_check_for; + acpi_object_type this_search_type; + u32 search_parent_flag = ACPI_NS_SEARCH_PARENT; + u32 local_flags = flags & ~(ACPI_NS_ERROR_IF_FOUND | + ACPI_NS_SEARCH_PARENT); + + + ACPI_FUNCTION_TRACE ("ns_lookup"); + + + if (!return_node) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + acpi_gbl_ns_lookup_count++; + *return_node = ACPI_ENTRY_NOT_FOUND; + + if (!acpi_gbl_root_node) { + return_ACPI_STATUS (AE_NO_NAMESPACE); + } + + /* + * Get the prefix scope. + * A null scope means use the root scope + */ + if ((!scope_info) || + (!scope_info->scope.node)) { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Null scope prefix, using root node (%p)\n", + acpi_gbl_root_node)); + + prefix_node = acpi_gbl_root_node; + } + else { + prefix_node = scope_info->scope.node; + if (ACPI_GET_DESCRIPTOR_TYPE (prefix_node) != ACPI_DESC_TYPE_NAMED) { + ACPI_REPORT_ERROR (("ns_lookup: %p is not a namespace node [%s]\n", + prefix_node, acpi_ut_get_descriptor_name (prefix_node))); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* + * This node might not be a actual "scope" node (such as a + * Device/Method, etc.) It could be a Package or other object node. + * Backup up the tree to find the containing scope node. + */ + while (!acpi_ns_opens_scope (prefix_node->type) && + prefix_node->type != ACPI_TYPE_ANY) { + prefix_node = acpi_ns_get_parent_node (prefix_node); + } + } + + /* Save type TBD: may be no longer necessary */ + + type_to_check_for = type; + + /* + * Begin examination of the actual pathname + */ + if (!pathname) { + /* A Null name_path is allowed and refers to the root */ + + num_segments = 0; + this_node = acpi_gbl_root_node; + path = ""; + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Null Pathname (Zero segments), Flags=%X\n", flags)); + } + else { + /* + * Name pointer is valid (and must be in internal name format) + * + * Check for scope prefixes: + * + * As represented in the AML stream, a namepath consists of an + * optional scope prefix followed by a name segment part. + * + * If present, the scope prefix is either a Root Prefix (in + * which case the name is fully qualified), or one or more + * Parent Prefixes (in which case the name's scope is relative + * to the current scope). + */ + if (*path == (u8) AML_ROOT_PREFIX) { + /* Pathname is fully qualified, start from the root */ + + this_node = acpi_gbl_root_node; + search_parent_flag = ACPI_NS_NO_UPSEARCH; + + /* Point to name segment part */ + + path++; + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Path is absolute from root [%p]\n", this_node)); + } + else { + /* Pathname is relative to current scope, start there */ + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Searching relative to prefix scope [%4.4s] (%p)\n", + acpi_ut_get_node_name (prefix_node), prefix_node)); + + /* + * Handle multiple Parent Prefixes (carat) by just getting + * the parent node for each prefix instance. + */ + this_node = prefix_node; + num_carats = 0; + while (*path == (u8) AML_PARENT_PREFIX) { + /* Name is fully qualified, no search rules apply */ + + search_parent_flag = ACPI_NS_NO_UPSEARCH; + /* + * Point past this prefix to the name segment + * part or the next Parent Prefix + */ + path++; + + /* Backup to the parent node */ + + num_carats++; + this_node = acpi_ns_get_parent_node (this_node); + if (!this_node) { + /* Current scope has no parent scope */ + + ACPI_REPORT_ERROR ( + ("ACPI path has too many parent prefixes (^) - reached beyond root node\n")); + return_ACPI_STATUS (AE_NOT_FOUND); + } + } + + if (search_parent_flag == ACPI_NS_NO_UPSEARCH) { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Search scope is [%4.4s], path has %d carat(s)\n", + acpi_ut_get_node_name (this_node), num_carats)); + } + } + + /* + * Determine the number of ACPI name segments in this pathname. + * + * The segment part consists of either: + * - A Null name segment (0) + * - A dual_name_prefix followed by two 4-byte name segments + * - A multi_name_prefix followed by a byte indicating the + * number of segments and the segments themselves. + * - A single 4-byte name segment + * + * Examine the name prefix opcode, if any, to determine the number of + * segments. + */ + switch (*path) { + case 0: + /* + * Null name after a root or parent prefixes. We already + * have the correct target node and there are no name segments. + */ + num_segments = 0; + type = this_node->type; + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Prefix-only Pathname (Zero name segments), Flags=%X\n", + flags)); + break; + + case AML_DUAL_NAME_PREFIX: + + /* More than one name_seg, search rules do not apply */ + + search_parent_flag = ACPI_NS_NO_UPSEARCH; + + /* Two segments, point to first name segment */ + + num_segments = 2; + path++; + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Dual Pathname (2 segments, Flags=%X)\n", flags)); + break; + + case AML_MULTI_NAME_PREFIX_OP: + + /* More than one name_seg, search rules do not apply */ + + search_parent_flag = ACPI_NS_NO_UPSEARCH; + + /* Extract segment count, point to first name segment */ + + path++; + num_segments = (u32) (u8) *path; + path++; + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Multi Pathname (%d Segments, Flags=%X) \n", + num_segments, flags)); + break; + + default: + /* + * Not a Null name, no Dual or Multi prefix, hence there is + * only one name segment and Pathname is already pointing to it. + */ + num_segments = 1; + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Simple Pathname (1 segment, Flags=%X)\n", flags)); + break; + } + + ACPI_DEBUG_EXEC (acpi_ns_print_pathname (num_segments, path)); + } + + + /* + * Search namespace for each segment of the name. Loop through and + * verify (or add to the namespace) each name segment. + * + * The object type is significant only at the last name + * segment. (We don't care about the types along the path, only + * the type of the final target object.) + */ + this_search_type = ACPI_TYPE_ANY; + current_node = this_node; + while (num_segments && current_node) { + num_segments--; + if (!num_segments) { + /* + * This is the last segment, enable typechecking + */ + this_search_type = type; + + /* + * Only allow automatic parent search (search rules) if the caller + * requested it AND we have a single, non-fully-qualified name_seg + */ + if ((search_parent_flag != ACPI_NS_NO_UPSEARCH) && + (flags & ACPI_NS_SEARCH_PARENT)) { + local_flags |= ACPI_NS_SEARCH_PARENT; + } + + /* Set error flag according to caller */ + + if (flags & ACPI_NS_ERROR_IF_FOUND) { + local_flags |= ACPI_NS_ERROR_IF_FOUND; + } + } + + /* Extract one ACPI name from the front of the pathname */ + + ACPI_MOVE_32_TO_32 (&simple_name, path); + + /* Try to find the single (4 character) ACPI name */ + + status = acpi_ns_search_and_enter (simple_name, walk_state, current_node, + interpreter_mode, this_search_type, local_flags, &this_node); + if (ACPI_FAILURE (status)) { + if (status == AE_NOT_FOUND) { + /* Name not found in ACPI namespace */ + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Name [%4.4s] not found in scope [%4.4s] %p\n", + (char *) &simple_name, (char *) ¤t_node->name, + current_node)); + } + + *return_node = this_node; + return_ACPI_STATUS (status); + } + + /* + * Sanity typecheck of the target object: + * + * If 1) This is the last segment (num_segments == 0) + * 2) And we are looking for a specific type + * (Not checking for TYPE_ANY) + * 3) Which is not an alias + * 4) Which is not a local type (TYPE_SCOPE) + * 5) And the type of target object is known (not TYPE_ANY) + * 6) And target object does not match what we are looking for + * + * Then we have a type mismatch. Just warn and ignore it. + */ + if ((num_segments == 0) && + (type_to_check_for != ACPI_TYPE_ANY) && + (type_to_check_for != ACPI_TYPE_LOCAL_ALIAS) && + (type_to_check_for != ACPI_TYPE_LOCAL_METHOD_ALIAS) && + (type_to_check_for != ACPI_TYPE_LOCAL_SCOPE) && + (this_node->type != ACPI_TYPE_ANY) && + (this_node->type != type_to_check_for)) { + /* Complain about a type mismatch */ + + ACPI_REPORT_WARNING ( + ("ns_lookup: Type mismatch on %4.4s (%s), searching for (%s)\n", + (char *) &simple_name, acpi_ut_get_type_name (this_node->type), + acpi_ut_get_type_name (type_to_check_for))); + } + + /* + * If this is the last name segment and we are not looking for a + * specific type, but the type of found object is known, use that type + * to see if it opens a scope. + */ + if ((num_segments == 0) && (type == ACPI_TYPE_ANY)) { + type = this_node->type; + } + + /* Point to next name segment and make this node current */ + + path += ACPI_NAME_SIZE; + current_node = this_node; + } + + /* + * Always check if we need to open a new scope + */ + if (!(flags & ACPI_NS_DONT_OPEN_SCOPE) && (walk_state)) { + /* + * If entry is a type which opens a scope, push the new scope on the + * scope stack. + */ + if (acpi_ns_opens_scope (type)) { + status = acpi_ds_scope_stack_push (this_node, type, walk_state); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + } + + *return_node = this_node; + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/namespace/nsalloc.c b/drivers/acpi/namespace/nsalloc.c new file mode 100644 index 00000000000..bfd922c5c7d --- /dev/null +++ b/drivers/acpi/namespace/nsalloc.c @@ -0,0 +1,685 @@ +/******************************************************************************* + * + * Module Name: nsalloc - Namespace allocation and deletion utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsalloc") + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_create_node + * + * PARAMETERS: acpi_name - Name of the new node + * + * RETURN: None + * + * DESCRIPTION: Create a namespace node + * + ******************************************************************************/ + +struct acpi_namespace_node * +acpi_ns_create_node ( + u32 name) +{ + struct acpi_namespace_node *node; + + + ACPI_FUNCTION_TRACE ("ns_create_node"); + + + node = ACPI_MEM_CALLOCATE (sizeof (struct acpi_namespace_node)); + if (!node) { + return_PTR (NULL); + } + + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_NSNODE].total_allocated++); + + node->name.integer = name; + node->reference_count = 1; + ACPI_SET_DESCRIPTOR_TYPE (node, ACPI_DESC_TYPE_NAMED); + + return_PTR (node); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_node + * + * PARAMETERS: Node - Node to be deleted + * + * RETURN: None + * + * DESCRIPTION: Delete a namespace node + * + ******************************************************************************/ + +void +acpi_ns_delete_node ( + struct acpi_namespace_node *node) +{ + struct acpi_namespace_node *parent_node; + struct acpi_namespace_node *prev_node; + struct acpi_namespace_node *next_node; + + + ACPI_FUNCTION_TRACE_PTR ("ns_delete_node", node); + + + parent_node = acpi_ns_get_parent_node (node); + + prev_node = NULL; + next_node = parent_node->child; + + /* Find the node that is the previous peer in the parent's child list */ + + while (next_node != node) { + prev_node = next_node; + next_node = prev_node->peer; + } + + if (prev_node) { + /* Node is not first child, unlink it */ + + prev_node->peer = next_node->peer; + if (next_node->flags & ANOBJ_END_OF_PEER_LIST) { + prev_node->flags |= ANOBJ_END_OF_PEER_LIST; + } + } + else { + /* Node is first child (has no previous peer) */ + + if (next_node->flags & ANOBJ_END_OF_PEER_LIST) { + /* No peers at all */ + + parent_node->child = NULL; + } + else { /* Link peer list to parent */ + + parent_node->child = next_node->peer; + } + } + + + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_NSNODE].total_freed++); + + /* + * Detach an object if there is one then delete the node + */ + acpi_ns_detach_object (node); + ACPI_MEM_FREE (node); + return_VOID; +} + + +#ifdef ACPI_ALPHABETIC_NAMESPACE +/******************************************************************************* + * + * FUNCTION: acpi_ns_compare_names + * + * PARAMETERS: Name1 - First name to compare + * Name2 - Second name to compare + * + * RETURN: value from strncmp + * + * DESCRIPTION: Compare two ACPI names. Names that are prefixed with an + * underscore are forced to be alphabetically first. + * + ******************************************************************************/ + +int +acpi_ns_compare_names ( + char *name1, + char *name2) +{ + char reversed_name1[ACPI_NAME_SIZE]; + char reversed_name2[ACPI_NAME_SIZE]; + u32 i; + u32 j; + + + /* + * Replace all instances of "underscore" with a value that is smaller so + * that all names that are prefixed with underscore(s) are alphabetically + * first. + * + * Reverse the name bytewise so we can just do a 32-bit compare instead + * of a strncmp. + */ + for (i = 0, j= (ACPI_NAME_SIZE - 1); i < ACPI_NAME_SIZE; i++, j--) { + reversed_name1[j] = name1[i]; + if (name1[i] == '_') { + reversed_name1[j] = '*'; + } + + reversed_name2[j] = name2[i]; + if (name2[i] == '_') { + reversed_name2[j] = '*'; + } + } + + return (*(int *) reversed_name1 - *(int *) reversed_name2); +} +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_install_node + * + * PARAMETERS: walk_state - Current state of the walk + * parent_node - The parent of the new Node + * Node - The new Node to install + * Type - ACPI object type of the new Node + * + * RETURN: None + * + * DESCRIPTION: Initialize a new namespace node and install it amongst + * its peers. + * + * Note: Current namespace lookup is linear search. However, the + * nodes are linked in alphabetical order to 1) put all reserved + * names (start with underscore) first, and to 2) make a readable + * namespace dump. + * + ******************************************************************************/ + +void +acpi_ns_install_node ( + struct acpi_walk_state *walk_state, + struct acpi_namespace_node *parent_node, /* Parent */ + struct acpi_namespace_node *node, /* New Child*/ + acpi_object_type type) +{ + u16 owner_id = 0; + struct acpi_namespace_node *child_node; +#ifdef ACPI_ALPHABETIC_NAMESPACE + + struct acpi_namespace_node *previous_child_node; +#endif + + + ACPI_FUNCTION_TRACE ("ns_install_node"); + + + /* + * Get the owner ID from the Walk state + * The owner ID is used to track table deletion and + * deletion of objects created by methods + */ + if (walk_state) { + owner_id = walk_state->owner_id; + } + + /* Link the new entry into the parent and existing children */ + + child_node = parent_node->child; + if (!child_node) { + parent_node->child = node; + node->flags |= ANOBJ_END_OF_PEER_LIST; + node->peer = parent_node; + } + else { +#ifdef ACPI_ALPHABETIC_NAMESPACE + /* + * Walk the list whilst searching for the correct + * alphabetic placement. + */ + previous_child_node = NULL; + while (acpi_ns_compare_names (acpi_ut_get_node_name (child_node), acpi_ut_get_node_name (node)) < 0) { + if (child_node->flags & ANOBJ_END_OF_PEER_LIST) { + /* Last peer; Clear end-of-list flag */ + + child_node->flags &= ~ANOBJ_END_OF_PEER_LIST; + + /* This node is the new peer to the child node */ + + child_node->peer = node; + + /* This node is the new end-of-list */ + + node->flags |= ANOBJ_END_OF_PEER_LIST; + node->peer = parent_node; + break; + } + + /* Get next peer */ + + previous_child_node = child_node; + child_node = child_node->peer; + } + + /* Did the node get inserted at the end-of-list? */ + + if (!(node->flags & ANOBJ_END_OF_PEER_LIST)) { + /* + * Loop above terminated without reaching the end-of-list. + * Insert the new node at the current location + */ + if (previous_child_node) { + /* Insert node alphabetically */ + + node->peer = child_node; + previous_child_node->peer = node; + } + else { + /* Insert node alphabetically at start of list */ + + node->peer = child_node; + parent_node->child = node; + } + } +#else + while (!(child_node->flags & ANOBJ_END_OF_PEER_LIST)) { + child_node = child_node->peer; + } + + child_node->peer = node; + + /* Clear end-of-list flag */ + + child_node->flags &= ~ANOBJ_END_OF_PEER_LIST; + node->flags |= ANOBJ_END_OF_PEER_LIST; + node->peer = parent_node; +#endif + } + + /* Init the new entry */ + + node->owner_id = owner_id; + node->type = (u8) type; + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "%4.4s (%s) [Node %p Owner %X] added to %4.4s (%s) [Node %p]\n", + acpi_ut_get_node_name (node), acpi_ut_get_type_name (node->type), node, owner_id, + acpi_ut_get_node_name (parent_node), acpi_ut_get_type_name (parent_node->type), + parent_node)); + + /* + * Increment the reference count(s) of all parents up to + * the root! + */ + while ((node = acpi_ns_get_parent_node (node)) != NULL) { + node->reference_count++; + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_children + * + * PARAMETERS: parent_node - Delete this objects children + * + * RETURN: None. + * + * DESCRIPTION: Delete all children of the parent object. In other words, + * deletes a "scope". + * + ******************************************************************************/ + +void +acpi_ns_delete_children ( + struct acpi_namespace_node *parent_node) +{ + struct acpi_namespace_node *child_node; + struct acpi_namespace_node *next_node; + struct acpi_namespace_node *node; + u8 flags; + + + ACPI_FUNCTION_TRACE_PTR ("ns_delete_children", parent_node); + + + if (!parent_node) { + return_VOID; + } + + /* If no children, all done! */ + + child_node = parent_node->child; + if (!child_node) { + return_VOID; + } + + /* + * Deallocate all children at this level + */ + do { + /* Get the things we need */ + + next_node = child_node->peer; + flags = child_node->flags; + + /* Grandchildren should have all been deleted already */ + + if (child_node->child) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Found a grandchild! P=%p C=%p\n", + parent_node, child_node)); + } + + /* Now we can free this child object */ + + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_NSNODE].total_freed++); + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Object %p, Remaining %X\n", + child_node, acpi_gbl_current_node_count)); + + /* + * Detach an object if there is one, then free the child node + */ + acpi_ns_detach_object (child_node); + + /* + * Decrement the reference count(s) of all parents up to + * the root! (counts were incremented when the node was created) + */ + node = child_node; + while ((node = acpi_ns_get_parent_node (node)) != NULL) { + node->reference_count--; + } + + /* There should be only one reference remaining on this node */ + + if (child_node->reference_count != 1) { + ACPI_REPORT_WARNING (("Existing references (%d) on node being deleted (%p)\n", + child_node->reference_count, child_node)); + } + + /* Now we can delete the node */ + + ACPI_MEM_FREE (child_node); + + /* And move on to the next child in the list */ + + child_node = next_node; + + } while (!(flags & ANOBJ_END_OF_PEER_LIST)); + + + /* Clear the parent's child pointer */ + + parent_node->child = NULL; + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_namespace_subtree + * + * PARAMETERS: parent_node - Root of the subtree to be deleted + * + * RETURN: None. + * + * DESCRIPTION: Delete a subtree of the namespace. This includes all objects + * stored within the subtree. + * + ******************************************************************************/ + +void +acpi_ns_delete_namespace_subtree ( + struct acpi_namespace_node *parent_node) +{ + struct acpi_namespace_node *child_node = NULL; + u32 level = 1; + + + ACPI_FUNCTION_TRACE ("ns_delete_namespace_subtree"); + + + if (!parent_node) { + return_VOID; + } + + /* + * Traverse the tree of objects until we bubble back up + * to where we started. + */ + while (level > 0) { + /* Get the next node in this scope (NULL if none) */ + + child_node = acpi_ns_get_next_node (ACPI_TYPE_ANY, parent_node, + child_node); + if (child_node) { + /* Found a child node - detach any attached object */ + + acpi_ns_detach_object (child_node); + + /* Check if this node has any children */ + + if (acpi_ns_get_next_node (ACPI_TYPE_ANY, child_node, NULL)) { + /* + * There is at least one child of this node, + * visit the node + */ + level++; + parent_node = child_node; + child_node = NULL; + } + } + else { + /* + * No more children of this parent node. + * Move up to the grandparent. + */ + level--; + + /* + * Now delete all of the children of this parent + * all at the same time. + */ + acpi_ns_delete_children (parent_node); + + /* New "last child" is this parent node */ + + child_node = parent_node; + + /* Move up the tree to the grandparent */ + + parent_node = acpi_ns_get_parent_node (parent_node); + } + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_remove_reference + * + * PARAMETERS: Node - Named node whose reference count is to be + * decremented + * + * RETURN: None. + * + * DESCRIPTION: Remove a Node reference. Decrements the reference count + * of all parent Nodes up to the root. Any node along + * the way that reaches zero references is freed. + * + ******************************************************************************/ + +void +acpi_ns_remove_reference ( + struct acpi_namespace_node *node) +{ + struct acpi_namespace_node *parent_node; + struct acpi_namespace_node *this_node; + + + ACPI_FUNCTION_ENTRY (); + + + /* + * Decrement the reference count(s) of this node and all + * nodes up to the root, Delete anything with zero remaining references. + */ + this_node = node; + while (this_node) { + /* Prepare to move up to parent */ + + parent_node = acpi_ns_get_parent_node (this_node); + + /* Decrement the reference count on this node */ + + this_node->reference_count--; + + /* Delete the node if no more references */ + + if (!this_node->reference_count) { + /* Delete all children and delete the node */ + + acpi_ns_delete_children (this_node); + acpi_ns_delete_node (this_node); + } + + this_node = parent_node; + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_namespace_by_owner + * + * PARAMETERS: owner_id - All nodes with this owner will be deleted + * + * RETURN: Status + * + * DESCRIPTION: Delete entries within the namespace that are owned by a + * specific ID. Used to delete entire ACPI tables. All + * reference counts are updated. + * + ******************************************************************************/ + +void +acpi_ns_delete_namespace_by_owner ( + u16 owner_id) +{ + struct acpi_namespace_node *child_node; + struct acpi_namespace_node *deletion_node; + u32 level; + struct acpi_namespace_node *parent_node; + + + ACPI_FUNCTION_TRACE_U32 ("ns_delete_namespace_by_owner", owner_id); + + + parent_node = acpi_gbl_root_node; + child_node = NULL; + deletion_node = NULL; + level = 1; + + /* + * Traverse the tree of nodes until we bubble back up + * to where we started. + */ + while (level > 0) { + /* + * Get the next child of this parent node. When child_node is NULL, + * the first child of the parent is returned + */ + child_node = acpi_ns_get_next_node (ACPI_TYPE_ANY, parent_node, child_node); + + if (deletion_node) { + acpi_ns_remove_reference (deletion_node); + deletion_node = NULL; + } + + if (child_node) { + if (child_node->owner_id == owner_id) { + /* Found a matching child node - detach any attached object */ + + acpi_ns_detach_object (child_node); + } + + /* Check if this node has any children */ + + if (acpi_ns_get_next_node (ACPI_TYPE_ANY, child_node, NULL)) { + /* + * There is at least one child of this node, + * visit the node + */ + level++; + parent_node = child_node; + child_node = NULL; + } + else if (child_node->owner_id == owner_id) { + deletion_node = child_node; + } + } + else { + /* + * No more children of this parent node. + * Move up to the grandparent. + */ + level--; + if (level != 0) { + if (parent_node->owner_id == owner_id) { + deletion_node = parent_node; + } + } + + /* New "last child" is this parent node */ + + child_node = parent_node; + + /* Move up the tree to the grandparent */ + + parent_node = acpi_ns_get_parent_node (parent_node); + } + } + + return_VOID; +} + + diff --git a/drivers/acpi/namespace/nsdump.c b/drivers/acpi/namespace/nsdump.c new file mode 100644 index 00000000000..1f6af3eb6c9 --- /dev/null +++ b/drivers/acpi/namespace/nsdump.c @@ -0,0 +1,673 @@ +/****************************************************************************** + * + * Module Name: nsdump - table dumping routines for debug + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acparser.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsdump") + + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) + +/******************************************************************************* + * + * FUNCTION: acpi_ns_print_pathname + * + * PARAMETERS: num_segment - Number of ACPI name segments + * Pathname - The compressed (internal) path + * + * DESCRIPTION: Print an object's full namespace pathname + * + ******************************************************************************/ + +void +acpi_ns_print_pathname ( + u32 num_segments, + char *pathname) +{ + ACPI_FUNCTION_NAME ("ns_print_pathname"); + + + if (!(acpi_dbg_level & ACPI_LV_NAMES) || !(acpi_dbg_layer & ACPI_NAMESPACE)) { + return; + } + + /* Print the entire name */ + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "[")); + + while (num_segments) { + acpi_os_printf ("%4.4s", pathname); + pathname += ACPI_NAME_SIZE; + + num_segments--; + if (num_segments) { + acpi_os_printf ("."); + } + } + + acpi_os_printf ("]\n"); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_pathname + * + * PARAMETERS: Handle - Object + * Msg - Prefix message + * Level - Desired debug level + * Component - Caller's component ID + * + * DESCRIPTION: Print an object's full namespace pathname + * Manages allocation/freeing of a pathname buffer + * + ******************************************************************************/ + +void +acpi_ns_dump_pathname ( + acpi_handle handle, + char *msg, + u32 level, + u32 component) +{ + + ACPI_FUNCTION_TRACE ("ns_dump_pathname"); + + + /* Do this only if the requested debug level and component are enabled */ + + if (!(acpi_dbg_level & level) || !(acpi_dbg_layer & component)) { + return_VOID; + } + + /* Convert handle to a full pathname and print it (with supplied message) */ + + acpi_ns_print_node_pathname (handle, msg); + acpi_os_printf ("\n"); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_one_object + * + * PARAMETERS: Handle - Node to be dumped + * Level - Nesting level of the handle + * Context - Passed into walk_namespace + * + * DESCRIPTION: Dump a single Node + * This procedure is a user_function called by acpi_ns_walk_namespace. + * + ******************************************************************************/ + +acpi_status +acpi_ns_dump_one_object ( + acpi_handle obj_handle, + u32 level, + void *context, + void **return_value) +{ + struct acpi_walk_info *info = (struct acpi_walk_info *) context; + struct acpi_namespace_node *this_node; + union acpi_operand_object *obj_desc = NULL; + acpi_object_type obj_type; + acpi_object_type type; + u32 bytes_to_dump; + u32 dbg_level; + u32 i; + + + ACPI_FUNCTION_NAME ("ns_dump_one_object"); + + + /* Is output enabled? */ + + if (!(acpi_dbg_level & info->debug_level)) { + return (AE_OK); + } + + if (!obj_handle) { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Null object handle\n")); + return (AE_OK); + } + + this_node = acpi_ns_map_handle_to_node (obj_handle); + type = this_node->type; + + /* Check if the owner matches */ + + if ((info->owner_id != ACPI_UINT32_MAX) && + (info->owner_id != this_node->owner_id)) { + return (AE_OK); + } + + /* Indent the object according to the level */ + + acpi_os_printf ("%2d%*s", (u32) level - 1, (int) level * 2, " "); + + /* Check the node type and name */ + + if (type > ACPI_TYPE_LOCAL_MAX) { + ACPI_REPORT_WARNING (("Invalid ACPI Type %08X\n", type)); + } + + if (!acpi_ut_valid_acpi_name (this_node->name.integer)) { + ACPI_REPORT_WARNING (("Invalid ACPI Name %08X\n", + this_node->name.integer)); + } + + /* + * Now we can print out the pertinent information + */ + acpi_os_printf ("%4.4s %-12s %p ", + acpi_ut_get_node_name (this_node), acpi_ut_get_type_name (type), this_node); + + dbg_level = acpi_dbg_level; + acpi_dbg_level = 0; + obj_desc = acpi_ns_get_attached_object (this_node); + acpi_dbg_level = dbg_level; + + switch (info->display_type) { + case ACPI_DISPLAY_SUMMARY: + + if (!obj_desc) { + /* No attached object, we are done */ + + acpi_os_printf ("\n"); + return (AE_OK); + } + + switch (type) { + case ACPI_TYPE_PROCESSOR: + + acpi_os_printf ("ID %X Len %.4X Addr %p\n", + obj_desc->processor.proc_id, obj_desc->processor.length, + (char *) obj_desc->processor.address); + break; + + + case ACPI_TYPE_DEVICE: + + acpi_os_printf ("Notify Object: %p\n", obj_desc); + break; + + + case ACPI_TYPE_METHOD: + + acpi_os_printf ("Args %X Len %.4X Aml %p\n", + (u32) obj_desc->method.param_count, + obj_desc->method.aml_length, obj_desc->method.aml_start); + break; + + + case ACPI_TYPE_INTEGER: + + acpi_os_printf ("= %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (obj_desc->integer.value)); + break; + + + case ACPI_TYPE_PACKAGE: + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + acpi_os_printf ("Elements %.2X\n", + obj_desc->package.count); + } + else { + acpi_os_printf ("[Length not yet evaluated]\n"); + } + break; + + + case ACPI_TYPE_BUFFER: + + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + acpi_os_printf ("Len %.2X", + obj_desc->buffer.length); + + /* Dump some of the buffer */ + + if (obj_desc->buffer.length > 0) { + acpi_os_printf (" ="); + for (i = 0; (i < obj_desc->buffer.length && i < 12); i++) { + acpi_os_printf (" %.2hX", obj_desc->buffer.pointer[i]); + } + } + acpi_os_printf ("\n"); + } + else { + acpi_os_printf ("[Length not yet evaluated]\n"); + } + break; + + + case ACPI_TYPE_STRING: + + acpi_os_printf ("Len %.2X ", obj_desc->string.length); + acpi_ut_print_string (obj_desc->string.pointer, 32); + acpi_os_printf ("\n"); + break; + + + case ACPI_TYPE_REGION: + + acpi_os_printf ("[%s]", + acpi_ut_get_region_name (obj_desc->region.space_id)); + if (obj_desc->region.flags & AOPOBJ_DATA_VALID) { + acpi_os_printf (" Addr %8.8X%8.8X Len %.4X\n", + ACPI_FORMAT_UINT64 (obj_desc->region.address), + obj_desc->region.length); + } + else { + acpi_os_printf (" [Address/Length not yet evaluated]\n"); + } + break; + + + case ACPI_TYPE_LOCAL_REFERENCE: + + acpi_os_printf ("[%s]\n", + acpi_ps_get_opcode_name (obj_desc->reference.opcode)); + break; + + + case ACPI_TYPE_BUFFER_FIELD: + + if (obj_desc->buffer_field.buffer_obj && + obj_desc->buffer_field.buffer_obj->buffer.node) { + acpi_os_printf ("Buf [%4.4s]", + acpi_ut_get_node_name (obj_desc->buffer_field.buffer_obj->buffer.node)); + } + break; + + + case ACPI_TYPE_LOCAL_REGION_FIELD: + + acpi_os_printf ("Rgn [%4.4s]", + acpi_ut_get_node_name (obj_desc->common_field.region_obj->region.node)); + break; + + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + acpi_os_printf ("Rgn [%4.4s] Bnk [%4.4s]", + acpi_ut_get_node_name (obj_desc->common_field.region_obj->region.node), + acpi_ut_get_node_name (obj_desc->bank_field.bank_obj->common_field.node)); + break; + + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + acpi_os_printf ("Idx [%4.4s] Dat [%4.4s]", + acpi_ut_get_node_name (obj_desc->index_field.index_obj->common_field.node), + acpi_ut_get_node_name (obj_desc->index_field.data_obj->common_field.node)); + break; + + + case ACPI_TYPE_LOCAL_ALIAS: + case ACPI_TYPE_LOCAL_METHOD_ALIAS: + + acpi_os_printf ("Target %4.4s (%p)\n", + acpi_ut_get_node_name (obj_desc), obj_desc); + break; + + default: + + acpi_os_printf ("Object %p\n", obj_desc); + break; + } + + /* Common field handling */ + + switch (type) { + case ACPI_TYPE_BUFFER_FIELD: + case ACPI_TYPE_LOCAL_REGION_FIELD: + case ACPI_TYPE_LOCAL_BANK_FIELD: + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + acpi_os_printf (" Off %.3X Len %.2X Acc %.2hd\n", + (obj_desc->common_field.base_byte_offset * 8) + + obj_desc->common_field.start_field_bit_offset, + obj_desc->common_field.bit_length, + obj_desc->common_field.access_byte_width); + break; + + default: + break; + } + break; + + + case ACPI_DISPLAY_OBJECTS: + + acpi_os_printf ("O:%p", obj_desc); + if (!obj_desc) { + /* No attached object, we are done */ + + acpi_os_printf ("\n"); + return (AE_OK); + } + + acpi_os_printf ("(R%d)", + obj_desc->common.reference_count); + + switch (type) { + case ACPI_TYPE_METHOD: + + /* Name is a Method and its AML offset/length are set */ + + acpi_os_printf (" M:%p-%X\n", obj_desc->method.aml_start, + obj_desc->method.aml_length); + break; + + case ACPI_TYPE_INTEGER: + + acpi_os_printf (" I:%8.8X8.8%X\n", + ACPI_FORMAT_UINT64 (obj_desc->integer.value)); + break; + + case ACPI_TYPE_STRING: + + acpi_os_printf (" S:%p-%X\n", obj_desc->string.pointer, + obj_desc->string.length); + break; + + case ACPI_TYPE_BUFFER: + + acpi_os_printf (" B:%p-%X\n", obj_desc->buffer.pointer, + obj_desc->buffer.length); + break; + + default: + + acpi_os_printf ("\n"); + break; + } + break; + + + default: + acpi_os_printf ("\n"); + break; + } + + /* If debug turned off, done */ + + if (!(acpi_dbg_level & ACPI_LV_VALUES)) { + return (AE_OK); + } + + + /* If there is an attached object, display it */ + + dbg_level = acpi_dbg_level; + acpi_dbg_level = 0; + obj_desc = acpi_ns_get_attached_object (this_node); + acpi_dbg_level = dbg_level; + + /* Dump attached objects */ + + while (obj_desc) { + obj_type = ACPI_TYPE_INVALID; + acpi_os_printf (" Attached Object %p: ", obj_desc); + + /* Decode the type of attached object and dump the contents */ + + switch (ACPI_GET_DESCRIPTOR_TYPE (obj_desc)) { + case ACPI_DESC_TYPE_NAMED: + + acpi_os_printf ("(Ptr to Node)\n"); + bytes_to_dump = sizeof (struct acpi_namespace_node); + break; + + + case ACPI_DESC_TYPE_OPERAND: + + obj_type = ACPI_GET_OBJECT_TYPE (obj_desc); + + if (obj_type > ACPI_TYPE_LOCAL_MAX) { + acpi_os_printf ("(Ptr to ACPI Object type %X [UNKNOWN])\n", + obj_type); + bytes_to_dump = 32; + } + else { + acpi_os_printf ("(Ptr to ACPI Object type %s, %X)\n", + acpi_ut_get_type_name (obj_type), obj_type); + bytes_to_dump = sizeof (union acpi_operand_object); + } + break; + + + default: + + acpi_os_printf ( + "(String or Buffer ptr - not an object descriptor) [%s]\n", + acpi_ut_get_descriptor_name (obj_desc)); + bytes_to_dump = 16; + break; + } + + ACPI_DUMP_BUFFER (obj_desc, bytes_to_dump); + + /* If value is NOT an internal object, we are done */ + + if (ACPI_GET_DESCRIPTOR_TYPE (obj_desc) != ACPI_DESC_TYPE_OPERAND) { + goto cleanup; + } + + /* + * Valid object, get the pointer to next level, if any + */ + switch (obj_type) { + case ACPI_TYPE_STRING: + obj_desc = (void *) obj_desc->string.pointer; + break; + + case ACPI_TYPE_BUFFER: + obj_desc = (void *) obj_desc->buffer.pointer; + break; + + case ACPI_TYPE_BUFFER_FIELD: + obj_desc = (union acpi_operand_object *) obj_desc->buffer_field.buffer_obj; + break; + + case ACPI_TYPE_PACKAGE: + obj_desc = (void *) obj_desc->package.elements; + break; + + case ACPI_TYPE_METHOD: + obj_desc = (void *) obj_desc->method.aml_start; + break; + + case ACPI_TYPE_LOCAL_REGION_FIELD: + obj_desc = (void *) obj_desc->field.region_obj; + break; + + case ACPI_TYPE_LOCAL_BANK_FIELD: + obj_desc = (void *) obj_desc->bank_field.region_obj; + break; + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + obj_desc = (void *) obj_desc->index_field.index_obj; + break; + + default: + goto cleanup; + } + + obj_type = ACPI_TYPE_INVALID; /* Terminate loop after next pass */ + } + +cleanup: + acpi_os_printf ("\n"); + return (AE_OK); +} + + +#ifdef ACPI_FUTURE_USAGE + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_objects + * + * PARAMETERS: Type - Object type to be dumped + * max_depth - Maximum depth of dump. Use ACPI_UINT32_MAX + * for an effectively unlimited depth. + * owner_id - Dump only objects owned by this ID. Use + * ACPI_UINT32_MAX to match all owners. + * start_handle - Where in namespace to start/end search + * + * DESCRIPTION: Dump typed objects within the loaded namespace. + * Uses acpi_ns_walk_namespace in conjunction with acpi_ns_dump_one_object. + * + ******************************************************************************/ + +void +acpi_ns_dump_objects ( + acpi_object_type type, + u8 display_type, + u32 max_depth, + u32 owner_id, + acpi_handle start_handle) +{ + struct acpi_walk_info info; + + + ACPI_FUNCTION_ENTRY (); + + + info.debug_level = ACPI_LV_TABLES; + info.owner_id = owner_id; + info.display_type = display_type; + + (void) acpi_ns_walk_namespace (type, start_handle, max_depth, + ACPI_NS_WALK_NO_UNLOCK, acpi_ns_dump_one_object, + (void *) &info, NULL); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_tables + * + * PARAMETERS: search_base - Root of subtree to be dumped, or + * NS_ALL to dump the entire namespace + * max_depth - Maximum depth of dump. Use INT_MAX + * for an effectively unlimited depth. + * + * DESCRIPTION: Dump the name space, or a portion of it. + * + ******************************************************************************/ + +void +acpi_ns_dump_tables ( + acpi_handle search_base, + u32 max_depth) +{ + acpi_handle search_handle = search_base; + + + ACPI_FUNCTION_TRACE ("ns_dump_tables"); + + + if (!acpi_gbl_root_node) { + /* + * If the name space has not been initialized, + * there is nothing to dump. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_TABLES, "namespace not initialized!\n")); + return_VOID; + } + + if (ACPI_NS_ALL == search_base) { + /* entire namespace */ + + search_handle = acpi_gbl_root_node; + ACPI_DEBUG_PRINT ((ACPI_DB_TABLES, "\\\n")); + } + + acpi_ns_dump_objects (ACPI_TYPE_ANY, ACPI_DISPLAY_OBJECTS, max_depth, + ACPI_UINT32_MAX, search_handle); + return_VOID; +} + +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_entry + * + * PARAMETERS: Handle - Node to be dumped + * debug_level - Output level + * + * DESCRIPTION: Dump a single Node + * + ******************************************************************************/ + +void +acpi_ns_dump_entry ( + acpi_handle handle, + u32 debug_level) +{ + struct acpi_walk_info info; + + + ACPI_FUNCTION_ENTRY (); + + + info.debug_level = debug_level; + info.owner_id = ACPI_UINT32_MAX; + info.display_type = ACPI_DISPLAY_SUMMARY; + + (void) acpi_ns_dump_one_object (handle, 1, &info, NULL); +} + +#endif + diff --git a/drivers/acpi/namespace/nsdumpdv.c b/drivers/acpi/namespace/nsdumpdv.c new file mode 100644 index 00000000000..d30a59e6b07 --- /dev/null +++ b/drivers/acpi/namespace/nsdumpdv.c @@ -0,0 +1,146 @@ +/****************************************************************************** + * + * Module Name: nsdump - table dumping routines for debug + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsdumpdv") + + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_one_device + * + * PARAMETERS: Handle - Node to be dumped + * Level - Nesting level of the handle + * Context - Passed into walk_namespace + * + * DESCRIPTION: Dump a single Node that represents a device + * This procedure is a user_function called by acpi_ns_walk_namespace. + * + ******************************************************************************/ + +acpi_status +acpi_ns_dump_one_device ( + acpi_handle obj_handle, + u32 level, + void *context, + void **return_value) +{ + struct acpi_buffer buffer; + struct acpi_device_info *info; + acpi_status status; + u32 i; + + + ACPI_FUNCTION_NAME ("ns_dump_one_device"); + + + status = acpi_ns_dump_one_object (obj_handle, level, context, return_value); + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_get_object_info (obj_handle, &buffer); + if (ACPI_SUCCESS (status)) { + info = buffer.pointer; + for (i = 0; i < level; i++) { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_TABLES, " ")); + } + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_TABLES, + " HID: %s, ADR: %8.8X%8.8X, Status: %X\n", + info->hardware_id.value, ACPI_FORMAT_UINT64 (info->address), + info->current_status)); + ACPI_MEM_FREE (info); + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_dump_root_devices + * + * PARAMETERS: None + * + * DESCRIPTION: Dump all objects of type "device" + * + ******************************************************************************/ + +void +acpi_ns_dump_root_devices (void) +{ + acpi_handle sys_bus_handle; + acpi_status status; + + + ACPI_FUNCTION_NAME ("ns_dump_root_devices"); + + + /* Only dump the table if tracing is enabled */ + + if (!(ACPI_LV_TABLES & acpi_dbg_level)) { + return; + } + + status = acpi_get_handle(NULL, ACPI_NS_SYSTEM_BUS, &sys_bus_handle); + if (ACPI_FAILURE (status)) { + return; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_TABLES, + "Display of all devices in the namespace:\n")); + + status = acpi_ns_walk_namespace (ACPI_TYPE_DEVICE, sys_bus_handle, + ACPI_UINT32_MAX, ACPI_NS_WALK_NO_UNLOCK, + acpi_ns_dump_one_device, NULL, NULL); +} + +#endif + + diff --git a/drivers/acpi/namespace/nseval.c b/drivers/acpi/namespace/nseval.c new file mode 100644 index 00000000000..0d008d53657 --- /dev/null +++ b/drivers/acpi/namespace/nseval.c @@ -0,0 +1,487 @@ +/******************************************************************************* + * + * Module Name: nseval - Object evaluation interfaces -- includes control + * method lookup and execution. + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nseval") + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_evaluate_relative + * + * PARAMETERS: Pathname - Name of method to execute, If NULL, the + * handle is the object to execute + * Info - Method info block + * + * RETURN: Status + * + * DESCRIPTION: Find and execute the requested method using the handle as a + * scope + * + * MUTEX: Locks Namespace + * + ******************************************************************************/ + +acpi_status +acpi_ns_evaluate_relative ( + char *pathname, + struct acpi_parameter_info *info) +{ + acpi_status status; + struct acpi_namespace_node *node = NULL; + union acpi_generic_state *scope_info; + char *internal_path = NULL; + + + ACPI_FUNCTION_TRACE ("ns_evaluate_relative"); + + + /* + * Must have a valid object handle + */ + if (!info || !info->node) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Build an internal name string for the method */ + + status = acpi_ns_internalize_name (pathname, &internal_path); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + scope_info = acpi_ut_create_generic_state (); + if (!scope_info) { + goto cleanup1; + } + + /* Get the prefix handle and Node */ + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + info->node = acpi_ns_map_handle_to_node (info->node); + if (!info->node) { + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + status = AE_BAD_PARAMETER; + goto cleanup; + } + + /* Lookup the name in the namespace */ + + scope_info->scope.node = info->node; + status = acpi_ns_lookup (scope_info, internal_path, ACPI_TYPE_ANY, + ACPI_IMODE_EXECUTE, ACPI_NS_NO_UPSEARCH, NULL, + &node); + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Object [%s] not found [%s]\n", + pathname, acpi_format_exception (status))); + goto cleanup; + } + + /* + * Now that we have a handle to the object, we can attempt to evaluate it. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "%s [%p] Value %p\n", + pathname, node, acpi_ns_get_attached_object (node))); + + info->node = node; + status = acpi_ns_evaluate_by_handle (info); + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "*** Completed eval of object %s ***\n", + pathname)); + +cleanup: + acpi_ut_delete_generic_state (scope_info); + +cleanup1: + ACPI_MEM_FREE (internal_path); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_evaluate_by_name + * + * PARAMETERS: Pathname - Fully qualified pathname to the object + * Info - Contains: + * return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * Params - List of parameters to pass to the method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * + * RETURN: Status + * + * DESCRIPTION: Find and execute the requested method passing the given + * parameters + * + * MUTEX: Locks Namespace + * + ******************************************************************************/ + +acpi_status +acpi_ns_evaluate_by_name ( + char *pathname, + struct acpi_parameter_info *info) +{ + acpi_status status; + char *internal_path = NULL; + + + ACPI_FUNCTION_TRACE ("ns_evaluate_by_name"); + + + /* Build an internal name string for the method */ + + status = acpi_ns_internalize_name (pathname, &internal_path); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Lookup the name in the namespace */ + + status = acpi_ns_lookup (NULL, internal_path, ACPI_TYPE_ANY, + ACPI_IMODE_EXECUTE, ACPI_NS_NO_UPSEARCH, NULL, + &info->node); + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Object at [%s] was not found, status=%.4X\n", + pathname, status)); + goto cleanup; + } + + /* + * Now that we have a handle to the object, we can attempt to evaluate it. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "%s [%p] Value %p\n", + pathname, info->node, acpi_ns_get_attached_object (info->node))); + + status = acpi_ns_evaluate_by_handle (info); + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "*** Completed eval of object %s ***\n", + pathname)); + + +cleanup: + + /* Cleanup */ + + if (internal_path) { + ACPI_MEM_FREE (internal_path); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_evaluate_by_handle + * + * PARAMETERS: Handle - Method Node to execute + * Params - List of parameters to pass to the method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * param_type - Type of Parameter list + * return_object - Where to put method's return value (if + * any). If NULL, no value is returned. + * + * RETURN: Status + * + * DESCRIPTION: Execute the requested method passing the given parameters + * + * MUTEX: Locks Namespace + * + ******************************************************************************/ + +acpi_status +acpi_ns_evaluate_by_handle ( + struct acpi_parameter_info *info) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ns_evaluate_by_handle"); + + + /* Check if namespace has been initialized */ + + if (!acpi_gbl_root_node) { + return_ACPI_STATUS (AE_NO_NAMESPACE); + } + + /* Parameter Validation */ + + if (!info) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Initialize the return value to an invalid object */ + + info->return_object = NULL; + + /* Get the prefix handle and Node */ + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + info->node = acpi_ns_map_handle_to_node (info->node); + if (!info->node) { + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * For a method alias, we must grab the actual method node so that proper + * scoping context will be established before execution. + */ + if (acpi_ns_get_type (info->node) == ACPI_TYPE_LOCAL_METHOD_ALIAS) { + info->node = ACPI_CAST_PTR (struct acpi_namespace_node, info->node->object); + } + + /* + * Two major cases here: + * 1) The object is an actual control method -- execute it. + * 2) The object is not a method -- just return it's current value + * + * In both cases, the namespace is unlocked by the acpi_ns* procedure + */ + if (acpi_ns_get_type (info->node) == ACPI_TYPE_METHOD) { + /* + * Case 1) We have an actual control method to execute + */ + status = acpi_ns_execute_control_method (info); + } + else { + /* + * Case 2) Object is NOT a method, just return its current value + */ + status = acpi_ns_get_object_value (info); + } + + /* + * Check if there is a return value on the stack that must be dealt with + */ + if (status == AE_CTRL_RETURN_VALUE) { + /* Map AE_CTRL_RETURN_VALUE to AE_OK, we are done with it */ + + status = AE_OK; + } + + /* + * Namespace was unlocked by the handling acpi_ns* function, so we + * just return + */ + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_execute_control_method + * + * PARAMETERS: Info - Method info block (w/params) + * + * RETURN: Status + * + * DESCRIPTION: Execute the requested method passing the given parameters + * + * MUTEX: Assumes namespace is locked + * + ******************************************************************************/ + +acpi_status +acpi_ns_execute_control_method ( + struct acpi_parameter_info *info) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_TRACE ("ns_execute_control_method"); + + + /* Verify that there is a method associated with this object */ + + obj_desc = acpi_ns_get_attached_object (info->node); + if (!obj_desc) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No attached method object\n")); + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (AE_NULL_OBJECT); + } + + ACPI_DUMP_PATHNAME (info->node, "Execute Method:", + ACPI_LV_INFO, _COMPONENT); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Method at AML address %p Length %X\n", + obj_desc->method.aml_start + 1, obj_desc->method.aml_length - 1)); + + /* + * Unlock the namespace before execution. This allows namespace access + * via the external Acpi* interfaces while a method is being executed. + * However, any namespace deletion must acquire both the namespace and + * interpreter locks to ensure that no thread is using the portion of the + * namespace that is being deleted. + */ + status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Execute the method via the interpreter. The interpreter is locked + * here before calling into the AML parser + */ + status = acpi_ex_enter_interpreter (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_psx_execute (info); + acpi_ex_exit_interpreter (); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_object_value + * + * PARAMETERS: Info - Method info block (w/params) + * + * RETURN: Status + * + * DESCRIPTION: Return the current value of the object + * + * MUTEX: Assumes namespace is locked, leaves namespace unlocked + * + ******************************************************************************/ + +acpi_status +acpi_ns_get_object_value ( + struct acpi_parameter_info *info) +{ + acpi_status status = AE_OK; + struct acpi_namespace_node *resolved_node = info->node; + + + ACPI_FUNCTION_TRACE ("ns_get_object_value"); + + + /* + * Objects require additional resolution steps (e.g., the Node may be a + * field that must be read, etc.) -- we can't just grab the object out of + * the node. + */ + + /* + * Use resolve_node_to_value() to get the associated value. This call always + * deletes obj_desc (allocated above). + * + * NOTE: we can get away with passing in NULL for a walk state because + * obj_desc is guaranteed to not be a reference to either a method local or + * a method argument (because this interface can only be called from the + * acpi_evaluate external interface, never called from a running method.) + * + * Even though we do not directly invoke the interpreter for this, we must + * enter it because we could access an opregion. The opregion access code + * assumes that the interpreter is locked. + * + * We must release the namespace lock before entering the intepreter. + */ + status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_ex_enter_interpreter (); + if (ACPI_SUCCESS (status)) { + status = acpi_ex_resolve_node_to_value (&resolved_node, NULL); + /* + * If acpi_ex_resolve_node_to_value() succeeded, the return value was placed + * in resolved_node. + */ + acpi_ex_exit_interpreter (); + + if (ACPI_SUCCESS (status)) { + status = AE_CTRL_RETURN_VALUE; + info->return_object = ACPI_CAST_PTR + (union acpi_operand_object, resolved_node); + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Returning object %p [%s]\n", + info->return_object, + acpi_ut_get_object_type_name (info->return_object))); + } + } + + /* Namespace is unlocked */ + + return_ACPI_STATUS (status); +} + diff --git a/drivers/acpi/namespace/nsinit.c b/drivers/acpi/namespace/nsinit.c new file mode 100644 index 00000000000..4a46b380605 --- /dev/null +++ b/drivers/acpi/namespace/nsinit.c @@ -0,0 +1,441 @@ +/****************************************************************************** + * + * Module Name: nsinit - namespace initialization + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsinit") + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_initialize_objects + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Walk the entire namespace and perform any necessary + * initialization on the objects found therein + * + ******************************************************************************/ + +acpi_status +acpi_ns_initialize_objects ( + void) +{ + acpi_status status; + struct acpi_init_walk_info info; + + + ACPI_FUNCTION_TRACE ("ns_initialize_objects"); + + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "**** Starting initialization of namespace objects ****\n")); + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, + "Completing Region/Field/Buffer/Package initialization:")); + + /* Set all init info to zero */ + + ACPI_MEMSET (&info, 0, sizeof (struct acpi_init_walk_info)); + + /* Walk entire namespace from the supplied root */ + + status = acpi_walk_namespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, acpi_ns_init_one_object, + &info, NULL); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "walk_namespace failed! %s\n", + acpi_format_exception (status))); + } + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, + "\nInitialized %hd/%hd Regions %hd/%hd Fields %hd/%hd Buffers %hd/%hd Packages (%hd nodes)\n", + info.op_region_init, info.op_region_count, + info.field_init, info.field_count, + info.buffer_init, info.buffer_count, + info.package_init, info.package_count, info.object_count)); + + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "%hd Control Methods found\n", info.method_count)); + ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, + "%hd Op Regions found\n", info.op_region_count)); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_initialize_devices + * + * PARAMETERS: None + * + * RETURN: acpi_status + * + * DESCRIPTION: Walk the entire namespace and initialize all ACPI devices. + * This means running _INI on all present devices. + * + * Note: We install PCI config space handler on region access, + * not here. + * + ******************************************************************************/ + +acpi_status +acpi_ns_initialize_devices ( + void) +{ + acpi_status status; + struct acpi_device_walk_info info; + + + ACPI_FUNCTION_TRACE ("ns_initialize_devices"); + + + /* Init counters */ + + info.device_count = 0; + info.num_STA = 0; + info.num_INI = 0; + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, + "Executing all Device _STA and_INI methods:")); + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Walk namespace for all objects */ + + status = acpi_ns_walk_namespace (ACPI_TYPE_ANY, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, TRUE, acpi_ns_init_one_device, &info, NULL); + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "walk_namespace failed! %s\n", + acpi_format_exception (status))); + } + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, + "\n%hd Devices found containing: %hd _STA, %hd _INI methods\n", + info.device_count, info.num_STA, info.num_INI)); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_init_one_object + * + * PARAMETERS: obj_handle - Node + * Level - Current nesting level + * Context - Points to a init info struct + * return_value - Not used + * + * RETURN: Status + * + * DESCRIPTION: Callback from acpi_walk_namespace. Invoked for every object + * within the namespace. + * + * Currently, the only objects that require initialization are: + * 1) Methods + * 2) Op Regions + * + ******************************************************************************/ + +acpi_status +acpi_ns_init_one_object ( + acpi_handle obj_handle, + u32 level, + void *context, + void **return_value) +{ + acpi_object_type type; + acpi_status status; + struct acpi_init_walk_info *info = (struct acpi_init_walk_info *) context; + struct acpi_namespace_node *node = (struct acpi_namespace_node *) obj_handle; + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_NAME ("ns_init_one_object"); + + + info->object_count++; + + /* And even then, we are only interested in a few object types */ + + type = acpi_ns_get_type (obj_handle); + obj_desc = acpi_ns_get_attached_object (node); + if (!obj_desc) { + return (AE_OK); + } + + /* Increment counters for object types we are looking for */ + + switch (type) { + case ACPI_TYPE_REGION: + info->op_region_count++; + break; + + case ACPI_TYPE_BUFFER_FIELD: + info->field_count++; + break; + + case ACPI_TYPE_BUFFER: + info->buffer_count++; + break; + + case ACPI_TYPE_PACKAGE: + info->package_count++; + break; + + default: + + /* No init required, just exit now */ + return (AE_OK); + } + + /* + * If the object is already initialized, nothing else to do + */ + if (obj_desc->common.flags & AOPOBJ_DATA_VALID) { + return (AE_OK); + } + + /* + * Must lock the interpreter before executing AML code + */ + status = acpi_ex_enter_interpreter (); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* + * Each of these types can contain executable AML code within the + * declaration. + */ + switch (type) { + case ACPI_TYPE_REGION: + + info->op_region_init++; + status = acpi_ds_get_region_arguments (obj_desc); + break; + + case ACPI_TYPE_BUFFER_FIELD: + + info->field_init++; + status = acpi_ds_get_buffer_field_arguments (obj_desc); + break; + + case ACPI_TYPE_BUFFER: + + info->buffer_init++; + status = acpi_ds_get_buffer_arguments (obj_desc); + break; + + case ACPI_TYPE_PACKAGE: + + info->package_init++; + status = acpi_ds_get_package_arguments (obj_desc); + break; + + default: + /* No other types can get here */ + break; + } + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_ERROR, "\n")); + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not execute arguments for [%4.4s] (%s), %s\n", + acpi_ut_get_node_name (node), acpi_ut_get_type_name (type), + acpi_format_exception (status))); + } + + /* + * Print a dot for each object unless we are going to print the entire + * pathname + */ + if (!(acpi_dbg_level & ACPI_LV_INIT_NAMES)) { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, ".")); + } + + /* + * We ignore errors from above, and always return OK, since we don't want + * to abort the walk on any single error. + */ + acpi_ex_exit_interpreter (); + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_init_one_device + * + * PARAMETERS: acpi_walk_callback + * + * RETURN: acpi_status + * + * DESCRIPTION: This is called once per device soon after ACPI is enabled + * to initialize each device. It determines if the device is + * present, and if so, calls _INI. + * + ******************************************************************************/ + +acpi_status +acpi_ns_init_one_device ( + acpi_handle obj_handle, + u32 nesting_level, + void *context, + void **return_value) +{ + struct acpi_device_walk_info *info = (struct acpi_device_walk_info *) context; + struct acpi_parameter_info pinfo; + u32 flags; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ns_init_one_device"); + + + pinfo.parameters = NULL; + pinfo.parameter_type = ACPI_PARAM_ARGS; + + pinfo.node = acpi_ns_map_handle_to_node (obj_handle); + if (!pinfo.node) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * We will run _STA/_INI on Devices, Processors and thermal_zones only + */ + if ((pinfo.node->type != ACPI_TYPE_DEVICE) && + (pinfo.node->type != ACPI_TYPE_PROCESSOR) && + (pinfo.node->type != ACPI_TYPE_THERMAL)) { + return_ACPI_STATUS (AE_OK); + } + + if ((acpi_dbg_level <= ACPI_LV_ALL_EXCEPTIONS) && + (!(acpi_dbg_level & ACPI_LV_INFO))) { + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, ".")); + } + + info->device_count++; + + /* + * Run _STA to determine if we can run _INI on the device. + */ + ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname (ACPI_TYPE_METHOD, pinfo.node, "_STA")); + status = acpi_ut_execute_STA (pinfo.node, &flags); + + if (ACPI_FAILURE (status)) { + if (pinfo.node->type == ACPI_TYPE_DEVICE) { + /* Ignore error and move on to next device */ + + return_ACPI_STATUS (AE_OK); + } + + /* _STA is not required for Processor or thermal_zone objects */ + } + else { + info->num_STA++; + + if (!(flags & 0x01)) { + /* Don't look at children of a not present device */ + + return_ACPI_STATUS(AE_CTRL_DEPTH); + } + } + + /* + * The device is present. Run _INI. + */ + ACPI_DEBUG_EXEC (acpi_ut_display_init_pathname (ACPI_TYPE_METHOD, pinfo.node, "_INI")); + status = acpi_ns_evaluate_relative ("_INI", &pinfo); + if (ACPI_FAILURE (status)) { + /* No _INI (AE_NOT_FOUND) means device requires no initialization */ + + if (status != AE_NOT_FOUND) { + /* Ignore error and move on to next device */ + +#ifdef ACPI_DEBUG_OUTPUT + char *scope_name = acpi_ns_get_external_pathname (pinfo.node); + + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "%s._INI failed: %s\n", + scope_name, acpi_format_exception (status))); + + ACPI_MEM_FREE (scope_name); +#endif + } + + status = AE_OK; + } + else { + /* Delete any return object (especially if implicit_return is enabled) */ + + if (pinfo.return_object) { + acpi_ut_remove_reference (pinfo.return_object); + } + + /* Count of successful INIs */ + + info->num_INI++; + } + + if (acpi_gbl_init_handler) { + /* External initialization handler is present, call it */ + + status = acpi_gbl_init_handler (pinfo.node, ACPI_INIT_DEVICE_INI); + } + + return_ACPI_STATUS (status); +} diff --git a/drivers/acpi/namespace/nsload.c b/drivers/acpi/namespace/nsload.c new file mode 100644 index 00000000000..1d7aedf68a7 --- /dev/null +++ b/drivers/acpi/namespace/nsload.c @@ -0,0 +1,460 @@ +/****************************************************************************** + * + * Module Name: nsload - namespace loading/expanding/contracting procedures + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acdispat.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsload") + + +#ifndef ACPI_NO_METHOD_EXECUTION + +/******************************************************************************* + * + * FUNCTION: acpi_ns_load_table + * + * PARAMETERS: table_desc - Descriptor for table to be loaded + * Node - Owning NS node + * + * RETURN: Status + * + * DESCRIPTION: Load one ACPI table into the namespace + * + ******************************************************************************/ + +acpi_status +acpi_ns_load_table ( + struct acpi_table_desc *table_desc, + struct acpi_namespace_node *node) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ns_load_table"); + + + /* Check if table contains valid AML (must be DSDT, PSDT, SSDT, etc.) */ + + if (!(acpi_gbl_table_data[table_desc->type].flags & ACPI_TABLE_EXECUTABLE)) { + /* Just ignore this table */ + + return_ACPI_STATUS (AE_OK); + } + + /* Check validity of the AML start and length */ + + if (!table_desc->aml_start) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null AML pointer\n")); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "AML block at %p\n", + table_desc->aml_start)); + + /* Ignore table if there is no AML contained within */ + + if (!table_desc->aml_length) { + ACPI_REPORT_WARNING (("Zero-length AML block in table [%4.4s]\n", + table_desc->pointer->signature)); + return_ACPI_STATUS (AE_OK); + } + + /* + * Parse the table and load the namespace with all named + * objects found within. Control methods are NOT parsed + * at this time. In fact, the control methods cannot be + * parsed until the entire namespace is loaded, because + * if a control method makes a forward reference (call) + * to another control method, we can't continue parsing + * because we don't know how many arguments to parse next! + */ + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "**** Loading table into namespace ****\n")); + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_ns_parse_table (table_desc, node->child); + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Now we can parse the control methods. We always parse + * them here for a sanity check, and if configured for + * just-in-time parsing, we delete the control method + * parse trees. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "**** Begin Table Method Parsing and Object Initialization ****\n")); + + status = acpi_ds_initialize_objects (table_desc, node); + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "**** Completed Table Method Parsing and Object Initialization ****\n")); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_load_table_by_type + * + * PARAMETERS: table_type - Id of the table type to load + * + * RETURN: Status + * + * DESCRIPTION: Load an ACPI table or tables into the namespace. All tables + * of the given type are loaded. The mechanism allows this + * routine to be called repeatedly. + * + ******************************************************************************/ + +acpi_status +acpi_ns_load_table_by_type ( + acpi_table_type table_type) +{ + u32 i; + acpi_status status; + struct acpi_table_desc *table_desc; + + + ACPI_FUNCTION_TRACE ("ns_load_table_by_type"); + + + status = acpi_ut_acquire_mutex (ACPI_MTX_TABLES); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Table types supported are: + * DSDT (one), SSDT/PSDT (multiple) + */ + switch (table_type) { + case ACPI_TABLE_DSDT: + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Loading DSDT\n")); + + table_desc = acpi_gbl_table_lists[ACPI_TABLE_DSDT].next; + + /* If table already loaded into namespace, just return */ + + if (table_desc->loaded_into_namespace) { + goto unlock_and_exit; + } + + /* Now load the single DSDT */ + + status = acpi_ns_load_table (table_desc, acpi_gbl_root_node); + if (ACPI_SUCCESS (status)) { + table_desc->loaded_into_namespace = TRUE; + } + break; + + + case ACPI_TABLE_SSDT: + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Loading %d SSDTs\n", + acpi_gbl_table_lists[ACPI_TABLE_SSDT].count)); + + /* + * Traverse list of SSDT tables + */ + table_desc = acpi_gbl_table_lists[ACPI_TABLE_SSDT].next; + for (i = 0; i < acpi_gbl_table_lists[ACPI_TABLE_SSDT].count; i++) { + /* + * Only attempt to load table if it is not + * already loaded! + */ + if (!table_desc->loaded_into_namespace) { + status = acpi_ns_load_table (table_desc, acpi_gbl_root_node); + if (ACPI_FAILURE (status)) { + break; + } + + table_desc->loaded_into_namespace = TRUE; + } + + table_desc = table_desc->next; + } + break; + + + case ACPI_TABLE_PSDT: + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Loading %d PSDTs\n", + acpi_gbl_table_lists[ACPI_TABLE_PSDT].count)); + + /* + * Traverse list of PSDT tables + */ + table_desc = acpi_gbl_table_lists[ACPI_TABLE_PSDT].next; + + for (i = 0; i < acpi_gbl_table_lists[ACPI_TABLE_PSDT].count; i++) { + /* Only attempt to load table if it is not already loaded! */ + + if (!table_desc->loaded_into_namespace) { + status = acpi_ns_load_table (table_desc, acpi_gbl_root_node); + if (ACPI_FAILURE (status)) { + break; + } + + table_desc->loaded_into_namespace = TRUE; + } + + table_desc = table_desc->next; + } + break; + + + default: + status = AE_SUPPORT; + break; + } + + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_TABLES); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_load_namespace + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Load the name space from what ever is pointed to by DSDT. + * (DSDT points to either the BIOS or a buffer.) + * + ******************************************************************************/ + +acpi_status +acpi_ns_load_namespace ( + void) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_load_name_space"); + + + /* There must be at least a DSDT installed */ + + if (acpi_gbl_DSDT == NULL) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "DSDT is not in memory\n")); + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + /* + * Load the namespace. The DSDT is required, + * but the SSDT and PSDT tables are optional. + */ + status = acpi_ns_load_table_by_type (ACPI_TABLE_DSDT); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Ignore exceptions from these */ + + (void) acpi_ns_load_table_by_type (ACPI_TABLE_SSDT); + (void) acpi_ns_load_table_by_type (ACPI_TABLE_PSDT); + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_INIT, + "ACPI Namespace successfully loaded at root %p\n", + acpi_gbl_root_node)); + + return_ACPI_STATUS (status); +} + + +#ifdef ACPI_FUTURE_USAGE + +/******************************************************************************* + * + * FUNCTION: acpi_ns_delete_subtree + * + * PARAMETERS: start_handle - Handle in namespace where search begins + * + * RETURNS Status + * + * DESCRIPTION: Walks the namespace starting at the given handle and deletes + * all objects, entries, and scopes in the entire subtree. + * + * Namespace/Interpreter should be locked or the subsystem should + * be in shutdown before this routine is called. + * + ******************************************************************************/ + +acpi_status +acpi_ns_delete_subtree ( + acpi_handle start_handle) +{ + acpi_status status; + acpi_handle child_handle; + acpi_handle parent_handle; + acpi_handle next_child_handle; + acpi_handle dummy; + u32 level; + + + ACPI_FUNCTION_TRACE ("ns_delete_subtree"); + + + parent_handle = start_handle; + child_handle = NULL; + level = 1; + + /* + * Traverse the tree of objects until we bubble back up + * to where we started. + */ + while (level > 0) { + /* Attempt to get the next object in this scope */ + + status = acpi_get_next_object (ACPI_TYPE_ANY, parent_handle, + child_handle, &next_child_handle); + + child_handle = next_child_handle; + + /* Did we get a new object? */ + + if (ACPI_SUCCESS (status)) { + /* Check if this object has any children */ + + if (ACPI_SUCCESS (acpi_get_next_object (ACPI_TYPE_ANY, child_handle, + NULL, &dummy))) { + /* + * There is at least one child of this object, + * visit the object + */ + level++; + parent_handle = child_handle; + child_handle = NULL; + } + } + else { + /* + * No more children in this object, go back up to + * the object's parent + */ + level--; + + /* Delete all children now */ + + acpi_ns_delete_children (child_handle); + + child_handle = parent_handle; + status = acpi_get_parent (parent_handle, &parent_handle); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + } + + /* Now delete the starting object, and we are done */ + + acpi_ns_delete_node (child_handle); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_unload_name_space + * + * PARAMETERS: Handle - Root of namespace subtree to be deleted + * + * RETURN: Status + * + * DESCRIPTION: Shrinks the namespace, typically in response to an undocking + * event. Deletes an entire subtree starting from (and + * including) the given handle. + * + ******************************************************************************/ + +acpi_status +acpi_ns_unload_namespace ( + acpi_handle handle) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ns_unload_name_space"); + + + /* Parameter validation */ + + if (!acpi_gbl_root_node) { + return_ACPI_STATUS (AE_NO_NAMESPACE); + } + + if (!handle) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* This function does the real work */ + + status = acpi_ns_delete_subtree (handle); + + return_ACPI_STATUS (status); +} + +#endif /* ACPI_FUTURE_USAGE */ + +#endif + diff --git a/drivers/acpi/namespace/nsnames.c b/drivers/acpi/namespace/nsnames.c new file mode 100644 index 00000000000..b6f8f910eff --- /dev/null +++ b/drivers/acpi/namespace/nsnames.c @@ -0,0 +1,265 @@ +/******************************************************************************* + * + * Module Name: nsnames - Name manipulation and search + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsnames") + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_build_external_path + * + * PARAMETERS: Node - NS node whose pathname is needed + * Size - Size of the pathname + * *name_buffer - Where to return the pathname + * + * RETURN: Places the pathname into the name_buffer, in external format + * (name segments separated by path separators) + * + * DESCRIPTION: Generate a full pathaname + * + ******************************************************************************/ + +void +acpi_ns_build_external_path ( + struct acpi_namespace_node *node, + acpi_size size, + char *name_buffer) +{ + acpi_size index; + struct acpi_namespace_node *parent_node; + + + ACPI_FUNCTION_NAME ("ns_build_external_path"); + + + /* Special case for root */ + + index = size - 1; + if (index < ACPI_NAME_SIZE) { + name_buffer[0] = AML_ROOT_PREFIX; + name_buffer[1] = 0; + return; + } + + /* Store terminator byte, then build name backwards */ + + parent_node = node; + name_buffer[index] = 0; + + while ((index > ACPI_NAME_SIZE) && (parent_node != acpi_gbl_root_node)) { + index -= ACPI_NAME_SIZE; + + /* Put the name into the buffer */ + + ACPI_MOVE_32_TO_32 ((name_buffer + index), &parent_node->name); + parent_node = acpi_ns_get_parent_node (parent_node); + + /* Prefix name with the path separator */ + + index--; + name_buffer[index] = ACPI_PATH_SEPARATOR; + } + + /* Overwrite final separator with the root prefix character */ + + name_buffer[index] = AML_ROOT_PREFIX; + + if (index != 0) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not construct pathname; index=%X, size=%X, Path=%s\n", + (u32) index, (u32) size, &name_buffer[size])); + } + + return; +} + + +#ifdef ACPI_DEBUG_OUTPUT +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_external_pathname + * + * PARAMETERS: Node - NS node whose pathname is needed + * + * RETURN: Pointer to storage containing the fully qualified name of + * the node, In external format (name segments separated by path + * separators.) + * + * DESCRIPTION: Used for debug printing in acpi_ns_search_table(). + * + ******************************************************************************/ + +char * +acpi_ns_get_external_pathname ( + struct acpi_namespace_node *node) +{ + char *name_buffer; + acpi_size size; + + + ACPI_FUNCTION_TRACE_PTR ("ns_get_external_pathname", node); + + + /* Calculate required buffer size based on depth below root */ + + size = acpi_ns_get_pathname_length (node); + + /* Allocate a buffer to be returned to caller */ + + name_buffer = ACPI_MEM_CALLOCATE (size); + if (!name_buffer) { + ACPI_REPORT_ERROR (("ns_get_table_pathname: allocation failure\n")); + return_PTR (NULL); + } + + /* Build the path in the allocated buffer */ + + acpi_ns_build_external_path (node, size, name_buffer); + return_PTR (name_buffer); +} +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_pathname_length + * + * PARAMETERS: Node - Namespace node + * + * RETURN: Length of path, including prefix + * + * DESCRIPTION: Get the length of the pathname string for this node + * + ******************************************************************************/ + +acpi_size +acpi_ns_get_pathname_length ( + struct acpi_namespace_node *node) +{ + acpi_size size; + struct acpi_namespace_node *next_node; + + + ACPI_FUNCTION_ENTRY (); + + + /* + * Compute length of pathname as 5 * number of name segments. + * Go back up the parent tree to the root + */ + size = 0; + next_node = node; + + while (next_node && (next_node != acpi_gbl_root_node)) { + size += ACPI_PATH_SEGMENT_LENGTH; + next_node = acpi_ns_get_parent_node (next_node); + } + + if (!size) { + size = 1; /* Root node case */ + } + + return (size + 1); /* +1 for null string terminator */ +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_handle_to_pathname + * + * PARAMETERS: target_handle - Handle of named object whose name is + * to be found + * Buffer - Where the pathname is returned + * + * RETURN: Status, Buffer is filled with pathname if status is AE_OK + * + * DESCRIPTION: Build and return a full namespace pathname + * + ******************************************************************************/ + +acpi_status +acpi_ns_handle_to_pathname ( + acpi_handle target_handle, + struct acpi_buffer *buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + acpi_size required_size; + + + ACPI_FUNCTION_TRACE_PTR ("ns_handle_to_pathname", target_handle); + + + node = acpi_ns_map_handle_to_node (target_handle); + if (!node) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Determine size required for the caller buffer */ + + required_size = acpi_ns_get_pathname_length (node); + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer (buffer, required_size); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Build the path in the caller buffer */ + + acpi_ns_build_external_path (node, required_size, buffer->pointer); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "%s [%X] \n", + (char *) buffer->pointer, (u32) required_size)); + return_ACPI_STATUS (AE_OK); +} + + diff --git a/drivers/acpi/namespace/nsobject.c b/drivers/acpi/namespace/nsobject.c new file mode 100644 index 00000000000..4e41e66db61 --- /dev/null +++ b/drivers/acpi/namespace/nsobject.c @@ -0,0 +1,461 @@ +/******************************************************************************* + * + * Module Name: nsobject - Utilities for objects attached to namespace + * table entries + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsobject") + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_attach_object + * + * PARAMETERS: Node - Parent Node + * Object - Object to be attached + * Type - Type of object, or ACPI_TYPE_ANY if not + * known + * + * DESCRIPTION: Record the given object as the value associated with the + * name whose acpi_handle is passed. If Object is NULL + * and Type is ACPI_TYPE_ANY, set the name as having no value. + * Note: Future may require that the Node->Flags field be passed + * as a parameter. + * + * MUTEX: Assumes namespace is locked + * + ******************************************************************************/ + +acpi_status +acpi_ns_attach_object ( + struct acpi_namespace_node *node, + union acpi_operand_object *object, + acpi_object_type type) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *last_obj_desc; + acpi_object_type object_type = ACPI_TYPE_ANY; + + + ACPI_FUNCTION_TRACE ("ns_attach_object"); + + + /* + * Parameter validation + */ + if (!node) { + /* Invalid handle */ + + ACPI_REPORT_ERROR (("ns_attach_object: Null named_obj handle\n")); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (!object && (ACPI_TYPE_ANY != type)) { + /* Null object */ + + ACPI_REPORT_ERROR (("ns_attach_object: Null object, but type not ACPI_TYPE_ANY\n")); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (ACPI_GET_DESCRIPTOR_TYPE (node) != ACPI_DESC_TYPE_NAMED) { + /* Not a name handle */ + + ACPI_REPORT_ERROR (("ns_attach_object: Invalid handle %p [%s]\n", + node, acpi_ut_get_descriptor_name (node))); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Check if this object is already attached */ + + if (node->object == object) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Obj %p already installed in name_obj %p\n", + object, node)); + + return_ACPI_STATUS (AE_OK); + } + + /* If null object, we will just install it */ + + if (!object) { + obj_desc = NULL; + object_type = ACPI_TYPE_ANY; + } + + /* + * If the source object is a namespace Node with an attached object, + * we will use that (attached) object + */ + else if ((ACPI_GET_DESCRIPTOR_TYPE (object) == ACPI_DESC_TYPE_NAMED) && + ((struct acpi_namespace_node *) object)->object) { + /* + * Value passed is a name handle and that name has a + * non-null value. Use that name's value and type. + */ + obj_desc = ((struct acpi_namespace_node *) object)->object; + object_type = ((struct acpi_namespace_node *) object)->type; + } + + /* + * Otherwise, we will use the parameter object, but we must type + * it first + */ + else { + obj_desc = (union acpi_operand_object *) object; + + /* Use the given type */ + + object_type = type; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Installing %p into Node %p [%4.4s]\n", + obj_desc, node, acpi_ut_get_node_name (node))); + + /* Detach an existing attached object if present */ + + if (node->object) { + acpi_ns_detach_object (node); + } + + if (obj_desc) { + /* + * Must increment the new value's reference count + * (if it is an internal object) + */ + acpi_ut_add_reference (obj_desc); + + /* + * Handle objects with multiple descriptors - walk + * to the end of the descriptor list + */ + last_obj_desc = obj_desc; + while (last_obj_desc->common.next_object) { + last_obj_desc = last_obj_desc->common.next_object; + } + + /* Install the object at the front of the object list */ + + last_obj_desc->common.next_object = node->object; + } + + node->type = (u8) object_type; + node->object = obj_desc; + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_detach_object + * + * PARAMETERS: Node - An node whose object will be detached + * + * RETURN: None. + * + * DESCRIPTION: Detach/delete an object associated with a namespace node. + * if the object is an allocated object, it is freed. + * Otherwise, the field is simply cleared. + * + ******************************************************************************/ + +void +acpi_ns_detach_object ( + struct acpi_namespace_node *node) +{ + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_TRACE ("ns_detach_object"); + + + obj_desc = node->object; + + if (!obj_desc || + (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_LOCAL_DATA)) { + return_VOID; + } + + /* Clear the entry in all cases */ + + node->object = NULL; + if (ACPI_GET_DESCRIPTOR_TYPE (obj_desc) == ACPI_DESC_TYPE_OPERAND) { + node->object = obj_desc->common.next_object; + if (node->object && + (ACPI_GET_OBJECT_TYPE (node->object) != ACPI_TYPE_LOCAL_DATA)) { + node->object = node->object->common.next_object; + } + } + + /* Reset the node type to untyped */ + + node->type = ACPI_TYPE_ANY; + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Node %p [%4.4s] Object %p\n", + node, acpi_ut_get_node_name (node), obj_desc)); + + /* Remove one reference on the object (and all subobjects) */ + + acpi_ut_remove_reference (obj_desc); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_attached_object + * + * PARAMETERS: Node - Parent Node to be examined + * + * RETURN: Current value of the object field from the Node whose + * handle is passed + * + * DESCRIPTION: Obtain the object attached to a namespace node. + * + ******************************************************************************/ + +union acpi_operand_object * +acpi_ns_get_attached_object ( + struct acpi_namespace_node *node) +{ + ACPI_FUNCTION_TRACE_PTR ("ns_get_attached_object", node); + + + if (!node) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Null Node ptr\n")); + return_PTR (NULL); + } + + if (!node->object || + ((ACPI_GET_DESCRIPTOR_TYPE (node->object) != ACPI_DESC_TYPE_OPERAND) && + (ACPI_GET_DESCRIPTOR_TYPE (node->object) != ACPI_DESC_TYPE_NAMED)) || + (ACPI_GET_OBJECT_TYPE (node->object) == ACPI_TYPE_LOCAL_DATA)) { + return_PTR (NULL); + } + + return_PTR (node->object); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_secondary_object + * + * PARAMETERS: Node - Parent Node to be examined + * + * RETURN: Current value of the object field from the Node whose + * handle is passed. + * + * DESCRIPTION: Obtain a secondary object associated with a namespace node. + * + ******************************************************************************/ + +union acpi_operand_object * +acpi_ns_get_secondary_object ( + union acpi_operand_object *obj_desc) +{ + ACPI_FUNCTION_TRACE_PTR ("ns_get_secondary_object", obj_desc); + + + if ((!obj_desc) || + (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_LOCAL_DATA) || + (!obj_desc->common.next_object) || + (ACPI_GET_OBJECT_TYPE (obj_desc->common.next_object) == ACPI_TYPE_LOCAL_DATA)) { + return_PTR (NULL); + } + + return_PTR (obj_desc->common.next_object); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_attach_data + * + * PARAMETERS: Node - Namespace node + * Handler - Handler to be associated with the data + * Data - Data to be attached + * + * RETURN: Status + * + * DESCRIPTION: Low-level attach data. Create and attach a Data object. + * + ******************************************************************************/ + +acpi_status +acpi_ns_attach_data ( + struct acpi_namespace_node *node, + acpi_object_handler handler, + void *data) +{ + union acpi_operand_object *prev_obj_desc; + union acpi_operand_object *obj_desc; + union acpi_operand_object *data_desc; + + + /* We only allow one attachment per handler */ + + prev_obj_desc = NULL; + obj_desc = node->object; + while (obj_desc) { + if ((ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_LOCAL_DATA) && + (obj_desc->data.handler == handler)) { + return (AE_ALREADY_EXISTS); + } + + prev_obj_desc = obj_desc; + obj_desc = obj_desc->common.next_object; + } + + /* Create an internal object for the data */ + + data_desc = acpi_ut_create_internal_object (ACPI_TYPE_LOCAL_DATA); + if (!data_desc) { + return (AE_NO_MEMORY); + } + + data_desc->data.handler = handler; + data_desc->data.pointer = data; + + /* Install the data object */ + + if (prev_obj_desc) { + prev_obj_desc->common.next_object = data_desc; + } + else { + node->object = data_desc; + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_detach_data + * + * PARAMETERS: Node - Namespace node + * Handler - Handler associated with the data + * + * RETURN: Status + * + * DESCRIPTION: Low-level detach data. Delete the data node, but the caller + * is responsible for the actual data. + * + ******************************************************************************/ + +acpi_status +acpi_ns_detach_data ( + struct acpi_namespace_node *node, + acpi_object_handler handler) +{ + union acpi_operand_object *obj_desc; + union acpi_operand_object *prev_obj_desc; + + + prev_obj_desc = NULL; + obj_desc = node->object; + while (obj_desc) { + if ((ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_LOCAL_DATA) && + (obj_desc->data.handler == handler)) { + if (prev_obj_desc) { + prev_obj_desc->common.next_object = obj_desc->common.next_object; + } + else { + node->object = obj_desc->common.next_object; + } + + acpi_ut_remove_reference (obj_desc); + return (AE_OK); + } + + prev_obj_desc = obj_desc; + obj_desc = obj_desc->common.next_object; + } + + return (AE_NOT_FOUND); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_attached_data + * + * PARAMETERS: Node - Namespace node + * Handler - Handler associated with the data + * Data - Where the data is returned + * + * RETURN: Status + * + * DESCRIPTION: Low level interface to obtain data previously associated with + * a namespace node. + * + ******************************************************************************/ + +acpi_status +acpi_ns_get_attached_data ( + struct acpi_namespace_node *node, + acpi_object_handler handler, + void **data) +{ + union acpi_operand_object *obj_desc; + + + obj_desc = node->object; + while (obj_desc) { + if ((ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_LOCAL_DATA) && + (obj_desc->data.handler == handler)) { + *data = obj_desc->data.pointer; + return (AE_OK); + } + + obj_desc = obj_desc->common.next_object; + } + + return (AE_NOT_FOUND); +} + + diff --git a/drivers/acpi/namespace/nsparse.c b/drivers/acpi/namespace/nsparse.c new file mode 100644 index 00000000000..a0e13e8d376 --- /dev/null +++ b/drivers/acpi/namespace/nsparse.c @@ -0,0 +1,171 @@ +/****************************************************************************** + * + * Module Name: nsparse - namespace interface to AML parser + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acparser.h> +#include <acpi/acdispat.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsparse") + + +/******************************************************************************* + * + * FUNCTION: ns_one_complete_parse + * + * PARAMETERS: pass_number - 1 or 2 + * table_desc - The table to be parsed. + * + * RETURN: Status + * + * DESCRIPTION: Perform one complete parse of an ACPI/AML table. + * + ******************************************************************************/ + +acpi_status +acpi_ns_one_complete_parse ( + u32 pass_number, + struct acpi_table_desc *table_desc) +{ + union acpi_parse_object *parse_root; + acpi_status status; + struct acpi_walk_state *walk_state; + + + ACPI_FUNCTION_TRACE ("ns_one_complete_parse"); + + + /* Create and init a Root Node */ + + parse_root = acpi_ps_create_scope_op (); + if (!parse_root) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Create and initialize a new walk state */ + + walk_state = acpi_ds_create_walk_state (table_desc->table_id, + NULL, NULL, NULL); + if (!walk_state) { + acpi_ps_free_op (parse_root); + return_ACPI_STATUS (AE_NO_MEMORY); + } + + status = acpi_ds_init_aml_walk (walk_state, parse_root, NULL, + table_desc->aml_start, table_desc->aml_length, + NULL, pass_number); + if (ACPI_FAILURE (status)) { + acpi_ds_delete_walk_state (walk_state); + return_ACPI_STATUS (status); + } + + /* Parse the AML */ + + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "*PARSE* pass %d parse\n", pass_number)); + status = acpi_ps_parse_aml (walk_state); + + acpi_ps_delete_parse_tree (parse_root); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_parse_table + * + * PARAMETERS: table_desc - An ACPI table descriptor for table to parse + * start_node - Where to enter the table into the namespace + * + * RETURN: Status + * + * DESCRIPTION: Parse AML within an ACPI table and return a tree of ops + * + ******************************************************************************/ + +acpi_status +acpi_ns_parse_table ( + struct acpi_table_desc *table_desc, + struct acpi_namespace_node *start_node) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ns_parse_table"); + + + /* + * AML Parse, pass 1 + * + * In this pass, we load most of the namespace. Control methods + * are not parsed until later. A parse tree is not created. Instead, + * each Parser Op subtree is deleted when it is finished. This saves + * a great deal of memory, and allows a small cache of parse objects + * to service the entire parse. The second pass of the parse then + * performs another complete parse of the AML.. + */ + status = acpi_ns_one_complete_parse (1, table_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * AML Parse, pass 2 + * + * In this pass, we resolve forward references and other things + * that could not be completed during the first pass. + * Another complete parse of the AML is performed, but the + * overhead of this is compensated for by the fact that the + * parse objects are all cached. + */ + status = acpi_ns_one_complete_parse (2, table_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/namespace/nssearch.c b/drivers/acpi/namespace/nssearch.c new file mode 100644 index 00000000000..0e6dea23603 --- /dev/null +++ b/drivers/acpi/namespace/nssearch.c @@ -0,0 +1,381 @@ +/******************************************************************************* + * + * Module Name: nssearch - Namespace search + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nssearch") + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_search_node + * + * PARAMETERS: *target_name - Ascii ACPI name to search for + * *Node - Starting node where search will begin + * Type - Object type to match + * **return_node - Where the matched Named obj is returned + * + * RETURN: Status + * + * DESCRIPTION: Search a single level of the namespace. Performs a + * simple search of the specified level, and does not add + * entries or search parents. + * + * + * Named object lists are built (and subsequently dumped) in the + * order in which the names are encountered during the namespace load; + * + * All namespace searching is linear in this implementation, but + * could be easily modified to support any improved search + * algorithm. However, the linear search was chosen for simplicity + * and because the trees are small and the other interpreter + * execution overhead is relatively high. + * + ******************************************************************************/ + +acpi_status +acpi_ns_search_node ( + u32 target_name, + struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_namespace_node **return_node) +{ + struct acpi_namespace_node *next_node; + + + ACPI_FUNCTION_TRACE ("ns_search_node"); + + +#ifdef ACPI_DEBUG_OUTPUT + if (ACPI_LV_NAMES & acpi_dbg_level) { + char *scope_name; + + scope_name = acpi_ns_get_external_pathname (node); + if (scope_name) { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Searching %s (%p) For [%4.4s] (%s)\n", + scope_name, node, (char *) &target_name, + acpi_ut_get_type_name (type))); + + ACPI_MEM_FREE (scope_name); + } + } +#endif + + /* + * Search for name at this namespace level, which is to say that we + * must search for the name among the children of this object + */ + next_node = node->child; + while (next_node) { + /* Check for match against the name */ + + if (next_node->name.integer == target_name) { + /* Resolve a control method alias if any */ + + if (acpi_ns_get_type (next_node) == ACPI_TYPE_LOCAL_METHOD_ALIAS) { + next_node = ACPI_CAST_PTR (struct acpi_namespace_node, next_node->object); + } + + /* + * Found matching entry. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Name [%4.4s] (%s) %p found in scope [%4.4s] %p\n", + (char *) &target_name, acpi_ut_get_type_name (next_node->type), + next_node, acpi_ut_get_node_name (node), node)); + + *return_node = next_node; + return_ACPI_STATUS (AE_OK); + } + + /* + * The last entry in the list points back to the parent, + * so a flag is used to indicate the end-of-list + */ + if (next_node->flags & ANOBJ_END_OF_PEER_LIST) { + /* Searched entire list, we are done */ + + break; + } + + /* Didn't match name, move on to the next peer object */ + + next_node = next_node->peer; + } + + /* Searched entire namespace level, not found */ + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Name [%4.4s] (%s) not found in search in scope [%4.4s] %p first child %p\n", + (char *) &target_name, acpi_ut_get_type_name (type), + acpi_ut_get_node_name (node), node, node->child)); + + return_ACPI_STATUS (AE_NOT_FOUND); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_search_parent_tree + * + * PARAMETERS: *target_name - Ascii ACPI name to search for + * *Node - Starting node where search will begin + * Type - Object type to match + * **return_node - Where the matched Node is returned + * + * RETURN: Status + * + * DESCRIPTION: Called when a name has not been found in the current namespace + * level. Before adding it or giving up, ACPI scope rules require + * searching enclosing scopes in cases identified by acpi_ns_local(). + * + * "A name is located by finding the matching name in the current + * name space, and then in the parent name space. If the parent + * name space does not contain the name, the search continues + * recursively until either the name is found or the name space + * does not have a parent (the root of the name space). This + * indicates that the name is not found" (From ACPI Specification, + * section 5.3) + * + ******************************************************************************/ + +static acpi_status +acpi_ns_search_parent_tree ( + u32 target_name, + struct acpi_namespace_node *node, + acpi_object_type type, + struct acpi_namespace_node **return_node) +{ + acpi_status status; + struct acpi_namespace_node *parent_node; + + + ACPI_FUNCTION_TRACE ("ns_search_parent_tree"); + + + parent_node = acpi_ns_get_parent_node (node); + + /* + * If there is no parent (i.e., we are at the root) or type is "local", + * we won't be searching the parent tree. + */ + if (!parent_node) { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "[%4.4s] has no parent\n", + (char *) &target_name)); + return_ACPI_STATUS (AE_NOT_FOUND); + } + + if (acpi_ns_local (type)) { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "[%4.4s] type [%s] must be local to this scope (no parent search)\n", + (char *) &target_name, acpi_ut_get_type_name (type))); + return_ACPI_STATUS (AE_NOT_FOUND); + } + + /* Search the parent tree */ + + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Searching parent [%4.4s] for [%4.4s]\n", + acpi_ut_get_node_name (parent_node), (char *) &target_name)); + + /* + * Search parents until target is found or we have backed up to the root + */ + while (parent_node) { + /* + * Search parent scope. Use TYPE_ANY because we don't care about the + * object type at this point, we only care about the existence of + * the actual name we are searching for. Typechecking comes later. + */ + status = acpi_ns_search_node (target_name, parent_node, + ACPI_TYPE_ANY, return_node); + if (ACPI_SUCCESS (status)) { + return_ACPI_STATUS (status); + } + + /* + * Not found here, go up another level + * (until we reach the root) + */ + parent_node = acpi_ns_get_parent_node (parent_node); + } + + /* Not found in parent tree */ + + return_ACPI_STATUS (AE_NOT_FOUND); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_search_and_enter + * + * PARAMETERS: target_name - Ascii ACPI name to search for (4 chars) + * walk_state - Current state of the walk + * *Node - Starting node where search will begin + * interpreter_mode - Add names only in ACPI_MODE_LOAD_PASS_x. + * Otherwise,search only. + * Type - Object type to match + * Flags - Flags describing the search restrictions + * **return_node - Where the Node is returned + * + * RETURN: Status + * + * DESCRIPTION: Search for a name segment in a single namespace level, + * optionally adding it if it is not found. If the passed + * Type is not Any and the type previously stored in the + * entry was Any (i.e. unknown), update the stored type. + * + * In ACPI_IMODE_EXECUTE, search only. + * In other modes, search and add if not found. + * + ******************************************************************************/ + +acpi_status +acpi_ns_search_and_enter ( + u32 target_name, + struct acpi_walk_state *walk_state, + struct acpi_namespace_node *node, + acpi_interpreter_mode interpreter_mode, + acpi_object_type type, + u32 flags, + struct acpi_namespace_node **return_node) +{ + acpi_status status; + struct acpi_namespace_node *new_node; + + + ACPI_FUNCTION_TRACE ("ns_search_and_enter"); + + + /* Parameter validation */ + + if (!node || !target_name || !return_node) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Null param: Node %p Name %X return_node %p\n", + node, target_name, return_node)); + + ACPI_REPORT_ERROR (("ns_search_and_enter: Null parameter\n")); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Name must consist of printable characters */ + + if (!acpi_ut_valid_acpi_name (target_name)) { + ACPI_REPORT_ERROR (("ns_search_and_enter: Bad character in ACPI Name: %X\n", + target_name)); + return_ACPI_STATUS (AE_BAD_CHARACTER); + } + + /* Try to find the name in the namespace level specified by the caller */ + + *return_node = ACPI_ENTRY_NOT_FOUND; + status = acpi_ns_search_node (target_name, node, type, return_node); + if (status != AE_NOT_FOUND) { + /* + * If we found it AND the request specifies that a find is an error, + * return the error + */ + if ((status == AE_OK) && + (flags & ACPI_NS_ERROR_IF_FOUND)) { + status = AE_ALREADY_EXISTS; + } + + /* + * Either found it or there was an error + * -- finished either way + */ + return_ACPI_STATUS (status); + } + + /* + * The name was not found. If we are NOT performing the first pass + * (name entry) of loading the namespace, search the parent tree (all the + * way to the root if necessary.) We don't want to perform the parent + * search when the namespace is actually being loaded. We want to perform + * the search when namespace references are being resolved (load pass 2) + * and during the execution phase. + */ + if ((interpreter_mode != ACPI_IMODE_LOAD_PASS1) && + (flags & ACPI_NS_SEARCH_PARENT)) { + /* + * Not found at this level - search parent tree according to the + * ACPI specification + */ + status = acpi_ns_search_parent_tree (target_name, node, type, return_node); + if (ACPI_SUCCESS (status)) { + return_ACPI_STATUS (status); + } + } + + /* + * In execute mode, just search, never add names. Exit now. + */ + if (interpreter_mode == ACPI_IMODE_EXECUTE) { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "%4.4s Not found in %p [Not adding]\n", + (char *) &target_name, node)); + + return_ACPI_STATUS (AE_NOT_FOUND); + } + + /* Create the new named object */ + + new_node = acpi_ns_create_node (target_name); + if (!new_node) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Install the new object into the parent's list of children */ + + acpi_ns_install_node (walk_state, node, new_node, type); + *return_node = new_node; + + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/namespace/nsutils.c b/drivers/acpi/namespace/nsutils.c new file mode 100644 index 00000000000..75da76cc0b1 --- /dev/null +++ b/drivers/acpi/namespace/nsutils.c @@ -0,0 +1,1069 @@ +/****************************************************************************** + * + * Module Name: nsutils - Utilities for accessing ACPI namespace, accessing + * parents and siblings and Scope manipulation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/amlcode.h> +#include <acpi/actables.h> + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsutils") + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_report_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: None + * + * DESCRIPTION: Print warning message with full pathname + * + ******************************************************************************/ + +void +acpi_ns_report_error ( + char *module_name, + u32 line_number, + u32 component_id, + char *internal_name, + acpi_status lookup_status) +{ + acpi_status status; + char *name = NULL; + + + acpi_os_printf ("%8s-%04d: *** Error: Looking up ", + module_name, line_number); + + if (lookup_status == AE_BAD_CHARACTER) { + /* There is a non-ascii character in the name */ + + acpi_os_printf ("[0x%4.4X] (NON-ASCII)\n", + *(ACPI_CAST_PTR (u32, internal_name))); + } + else { + /* Convert path to external format */ + + status = acpi_ns_externalize_name (ACPI_UINT32_MAX, + internal_name, NULL, &name); + + /* Print target name */ + + if (ACPI_SUCCESS (status)) { + acpi_os_printf ("[%s]", name); + } + else { + acpi_os_printf ("[COULD NOT EXTERNALIZE NAME]"); + } + + if (name) { + ACPI_MEM_FREE (name); + } + } + + acpi_os_printf (" in namespace, %s\n", + acpi_format_exception (lookup_status)); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_report_method_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: None + * + * DESCRIPTION: Print warning message with full pathname + * + ******************************************************************************/ + +void +acpi_ns_report_method_error ( + char *module_name, + u32 line_number, + u32 component_id, + char *message, + struct acpi_namespace_node *prefix_node, + char *path, + acpi_status method_status) +{ + acpi_status status; + struct acpi_namespace_node *node = prefix_node; + + + if (path) { + status = acpi_ns_get_node_by_path (path, prefix_node, + ACPI_NS_NO_UPSEARCH, &node); + if (ACPI_FAILURE (status)) { + acpi_os_printf ("report_method_error: Could not get node\n"); + return; + } + } + + acpi_os_printf ("%8s-%04d: *** Error: ", module_name, line_number); + acpi_ns_print_node_pathname (node, message); + acpi_os_printf (", %s\n", acpi_format_exception (method_status)); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_print_node_pathname + * + * PARAMETERS: Node - Object + * Msg - Prefix message + * + * DESCRIPTION: Print an object's full namespace pathname + * Manages allocation/freeing of a pathname buffer + * + ******************************************************************************/ + +void +acpi_ns_print_node_pathname ( + struct acpi_namespace_node *node, + char *msg) +{ + struct acpi_buffer buffer; + acpi_status status; + + + if (!node) { + acpi_os_printf ("[NULL NAME]"); + return; + } + + /* Convert handle to full pathname and print it (with supplied message) */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + + status = acpi_ns_handle_to_pathname (node, &buffer); + if (ACPI_SUCCESS (status)) { + if (msg) { + acpi_os_printf ("%s ", msg); + } + + acpi_os_printf ("[%s] (Node %p)", (char *) buffer.pointer, node); + ACPI_MEM_FREE (buffer.pointer); + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_valid_root_prefix + * + * PARAMETERS: Prefix - Character to be checked + * + * RETURN: TRUE if a valid prefix + * + * DESCRIPTION: Check if a character is a valid ACPI Root prefix + * + ******************************************************************************/ + +u8 +acpi_ns_valid_root_prefix ( + char prefix) +{ + + return ((u8) (prefix == '\\')); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_valid_path_separator + * + * PARAMETERS: Sep - Character to be checked + * + * RETURN: TRUE if a valid path separator + * + * DESCRIPTION: Check if a character is a valid ACPI path separator + * + ******************************************************************************/ + +u8 +acpi_ns_valid_path_separator ( + char sep) +{ + + return ((u8) (sep == '.')); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_type + * + * PARAMETERS: Handle - Parent Node to be examined + * + * RETURN: Type field from Node whose handle is passed + * + ******************************************************************************/ + +acpi_object_type +acpi_ns_get_type ( + struct acpi_namespace_node *node) +{ + ACPI_FUNCTION_TRACE ("ns_get_type"); + + + if (!node) { + ACPI_REPORT_WARNING (("ns_get_type: Null Node input pointer\n")); + return_VALUE (ACPI_TYPE_ANY); + } + + return_VALUE ((acpi_object_type) node->type); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_local + * + * PARAMETERS: Type - A namespace object type + * + * RETURN: LOCAL if names must be found locally in objects of the + * passed type, 0 if enclosing scopes should be searched + * + ******************************************************************************/ + +u32 +acpi_ns_local ( + acpi_object_type type) +{ + ACPI_FUNCTION_TRACE ("ns_local"); + + + if (!acpi_ut_valid_object_type (type)) { + /* Type code out of range */ + + ACPI_REPORT_WARNING (("ns_local: Invalid Object Type\n")); + return_VALUE (ACPI_NS_NORMAL); + } + + return_VALUE ((u32) acpi_gbl_ns_properties[type] & ACPI_NS_LOCAL); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_internal_name_length + * + * PARAMETERS: Info - Info struct initialized with the + * external name pointer. + * + * RETURN: Status + * + * DESCRIPTION: Calculate the length of the internal (AML) namestring + * corresponding to the external (ASL) namestring. + * + ******************************************************************************/ + +void +acpi_ns_get_internal_name_length ( + struct acpi_namestring_info *info) +{ + char *next_external_char; + u32 i; + + + ACPI_FUNCTION_ENTRY (); + + + next_external_char = info->external_name; + info->num_carats = 0; + info->num_segments = 0; + info->fully_qualified = FALSE; + + /* + * For the internal name, the required length is 4 bytes per segment, plus + * 1 each for root_prefix, multi_name_prefix_op, segment count, trailing null + * (which is not really needed, but no there's harm in putting it there) + * + * strlen() + 1 covers the first name_seg, which has no path separator + */ + if (acpi_ns_valid_root_prefix (next_external_char[0])) { + info->fully_qualified = TRUE; + next_external_char++; + } + else { + /* + * Handle Carat prefixes + */ + while (*next_external_char == '^') { + info->num_carats++; + next_external_char++; + } + } + + /* + * Determine the number of ACPI name "segments" by counting the number of + * path separators within the string. Start with one segment since the + * segment count is [(# separators) + 1], and zero separators is ok. + */ + if (*next_external_char) { + info->num_segments = 1; + for (i = 0; next_external_char[i]; i++) { + if (acpi_ns_valid_path_separator (next_external_char[i])) { + info->num_segments++; + } + } + } + + info->length = (ACPI_NAME_SIZE * info->num_segments) + + 4 + info->num_carats; + + info->next_external_char = next_external_char; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_build_internal_name + * + * PARAMETERS: Info - Info struct fully initialized + * + * RETURN: Status + * + * DESCRIPTION: Construct the internal (AML) namestring + * corresponding to the external (ASL) namestring. + * + ******************************************************************************/ + +acpi_status +acpi_ns_build_internal_name ( + struct acpi_namestring_info *info) +{ + u32 num_segments = info->num_segments; + char *internal_name = info->internal_name; + char *external_name = info->next_external_char; + char *result = NULL; + acpi_native_uint i; + + + ACPI_FUNCTION_TRACE ("ns_build_internal_name"); + + + /* Setup the correct prefixes, counts, and pointers */ + + if (info->fully_qualified) { + internal_name[0] = '\\'; + + if (num_segments <= 1) { + result = &internal_name[1]; + } + else if (num_segments == 2) { + internal_name[1] = AML_DUAL_NAME_PREFIX; + result = &internal_name[2]; + } + else { + internal_name[1] = AML_MULTI_NAME_PREFIX_OP; + internal_name[2] = (char) num_segments; + result = &internal_name[3]; + } + } + else { + /* + * Not fully qualified. + * Handle Carats first, then append the name segments + */ + i = 0; + if (info->num_carats) { + for (i = 0; i < info->num_carats; i++) { + internal_name[i] = '^'; + } + } + + if (num_segments <= 1) { + result = &internal_name[i]; + } + else if (num_segments == 2) { + internal_name[i] = AML_DUAL_NAME_PREFIX; + result = &internal_name[(acpi_native_uint) (i+1)]; + } + else { + internal_name[i] = AML_MULTI_NAME_PREFIX_OP; + internal_name[(acpi_native_uint) (i+1)] = (char) num_segments; + result = &internal_name[(acpi_native_uint) (i+2)]; + } + } + + /* Build the name (minus path separators) */ + + for (; num_segments; num_segments--) { + for (i = 0; i < ACPI_NAME_SIZE; i++) { + if (acpi_ns_valid_path_separator (*external_name) || + (*external_name == 0)) { + /* Pad the segment with underscore(s) if segment is short */ + + result[i] = '_'; + } + else { + /* Convert the character to uppercase and save it */ + + result[i] = (char) ACPI_TOUPPER ((int) *external_name); + external_name++; + } + } + + /* Now we must have a path separator, or the pathname is bad */ + + if (!acpi_ns_valid_path_separator (*external_name) && + (*external_name != 0)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Move on the next segment */ + + external_name++; + result += ACPI_NAME_SIZE; + } + + /* Terminate the string */ + + *result = 0; + + if (info->fully_qualified) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Returning [%p] (abs) \"\\%s\"\n", + internal_name, internal_name)); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Returning [%p] (rel) \"%s\"\n", + internal_name, internal_name)); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_internalize_name + * + * PARAMETERS: *external_name - External representation of name + * **Converted Name - Where to return the resulting + * internal represention of the name + * + * RETURN: Status + * + * DESCRIPTION: Convert an external representation (e.g. "\_PR_.CPU0") + * to internal form (e.g. 5c 2f 02 5f 50 52 5f 43 50 55 30) + * + *******************************************************************************/ + +acpi_status +acpi_ns_internalize_name ( + char *external_name, + char **converted_name) +{ + char *internal_name; + struct acpi_namestring_info info; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ns_internalize_name"); + + + if ((!external_name) || + (*external_name == 0) || + (!converted_name)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Get the length of the new internal name */ + + info.external_name = external_name; + acpi_ns_get_internal_name_length (&info); + + /* We need a segment to store the internal name */ + + internal_name = ACPI_MEM_CALLOCATE (info.length); + if (!internal_name) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Build the name */ + + info.internal_name = internal_name; + status = acpi_ns_build_internal_name (&info); + if (ACPI_FAILURE (status)) { + ACPI_MEM_FREE (internal_name); + return_ACPI_STATUS (status); + } + + *converted_name = internal_name; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_externalize_name + * + * PARAMETERS: *internal_name - Internal representation of name + * **converted_name - Where to return the resulting + * external representation of name + * + * RETURN: Status + * + * DESCRIPTION: Convert internal name (e.g. 5c 2f 02 5f 50 52 5f 43 50 55 30) + * to its external form (e.g. "\_PR_.CPU0") + * + ******************************************************************************/ + +acpi_status +acpi_ns_externalize_name ( + u32 internal_name_length, + char *internal_name, + u32 *converted_name_length, + char **converted_name) +{ + acpi_native_uint names_index = 0; + acpi_native_uint num_segments = 0; + acpi_native_uint required_length; + acpi_native_uint prefix_length = 0; + acpi_native_uint i = 0; + acpi_native_uint j = 0; + + + ACPI_FUNCTION_TRACE ("ns_externalize_name"); + + + if (!internal_name_length || + !internal_name || + !converted_name) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Check for a prefix (one '\' | one or more '^'). + */ + switch (internal_name[0]) { + case '\\': + prefix_length = 1; + break; + + case '^': + for (i = 0; i < internal_name_length; i++) { + if (internal_name[i] == '^') { + prefix_length = i + 1; + } + else { + break; + } + } + + if (i == internal_name_length) { + prefix_length = i; + } + + break; + + default: + break; + } + + /* + * Check for object names. Note that there could be 0-255 of these + * 4-byte elements. + */ + if (prefix_length < internal_name_length) { + switch (internal_name[prefix_length]) { + case AML_MULTI_NAME_PREFIX_OP: + + /* <count> 4-byte names */ + + names_index = prefix_length + 2; + num_segments = (acpi_native_uint) (u8) + internal_name[(acpi_native_uint) (prefix_length + 1)]; + break; + + case AML_DUAL_NAME_PREFIX: + + /* Two 4-byte names */ + + names_index = prefix_length + 1; + num_segments = 2; + break; + + case 0: + + /* null_name */ + + names_index = 0; + num_segments = 0; + break; + + default: + + /* one 4-byte name */ + + names_index = prefix_length; + num_segments = 1; + break; + } + } + + /* + * Calculate the length of converted_name, which equals the length + * of the prefix, length of all object names, length of any required + * punctuation ('.') between object names, plus the NULL terminator. + */ + required_length = prefix_length + (4 * num_segments) + + ((num_segments > 0) ? (num_segments - 1) : 0) + 1; + + /* + * Check to see if we're still in bounds. If not, there's a problem + * with internal_name (invalid format). + */ + if (required_length > internal_name_length) { + ACPI_REPORT_ERROR (("ns_externalize_name: Invalid internal name\n")); + return_ACPI_STATUS (AE_BAD_PATHNAME); + } + + /* + * Build converted_name + */ + *converted_name = ACPI_MEM_CALLOCATE (required_length); + if (!(*converted_name)) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + j = 0; + + for (i = 0; i < prefix_length; i++) { + (*converted_name)[j++] = internal_name[i]; + } + + if (num_segments > 0) { + for (i = 0; i < num_segments; i++) { + if (i > 0) { + (*converted_name)[j++] = '.'; + } + + (*converted_name)[j++] = internal_name[names_index++]; + (*converted_name)[j++] = internal_name[names_index++]; + (*converted_name)[j++] = internal_name[names_index++]; + (*converted_name)[j++] = internal_name[names_index++]; + } + } + + if (converted_name_length) { + *converted_name_length = (u32) required_length; + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_map_handle_to_node + * + * PARAMETERS: Handle - Handle to be converted to an Node + * + * RETURN: A Name table entry pointer + * + * DESCRIPTION: Convert a namespace handle to a real Node + * + * Note: Real integer handles allow for more verification + * and keep all pointers within this subsystem. + * + ******************************************************************************/ + +struct acpi_namespace_node * +acpi_ns_map_handle_to_node ( + acpi_handle handle) +{ + + ACPI_FUNCTION_ENTRY (); + + + /* + * Simple implementation. + */ + if (!handle) { + return (NULL); + } + + if (handle == ACPI_ROOT_OBJECT) { + return (acpi_gbl_root_node); + } + + /* We can at least attempt to verify the handle */ + + if (ACPI_GET_DESCRIPTOR_TYPE (handle) != ACPI_DESC_TYPE_NAMED) { + return (NULL); + } + + return ((struct acpi_namespace_node *) handle); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_convert_entry_to_handle + * + * PARAMETERS: Node - Node to be converted to a Handle + * + * RETURN: A user handle + * + * DESCRIPTION: Convert a real Node to a namespace handle + * + ******************************************************************************/ + +acpi_handle +acpi_ns_convert_entry_to_handle ( + struct acpi_namespace_node *node) +{ + + + /* + * Simple implementation for now; + */ + return ((acpi_handle) node); + + +/* --------------------------------------------------- + + if (!Node) + { + return (NULL); + } + + if (Node == acpi_gbl_root_node) + { + return (ACPI_ROOT_OBJECT); + } + + + return ((acpi_handle) Node); +------------------------------------------------------*/ +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_terminate + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: free memory allocated for table storage. + * + ******************************************************************************/ + +void +acpi_ns_terminate (void) +{ + union acpi_operand_object *obj_desc; + + + ACPI_FUNCTION_TRACE ("ns_terminate"); + + + /* + * 1) Free the entire namespace -- all nodes and objects + * + * Delete all object descriptors attached to namepsace nodes + */ + acpi_ns_delete_namespace_subtree (acpi_gbl_root_node); + + /* Detach any objects attached to the root */ + + obj_desc = acpi_ns_get_attached_object (acpi_gbl_root_node); + if (obj_desc) { + acpi_ns_detach_object (acpi_gbl_root_node); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Namespace freed\n")); + + /* + * 2) Now we can delete the ACPI tables + */ + acpi_tb_delete_all_tables (); + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "ACPI Tables freed\n")); + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_opens_scope + * + * PARAMETERS: Type - A valid namespace type + * + * RETURN: NEWSCOPE if the passed type "opens a name scope" according + * to the ACPI specification, else 0 + * + ******************************************************************************/ + +u32 +acpi_ns_opens_scope ( + acpi_object_type type) +{ + ACPI_FUNCTION_TRACE_STR ("ns_opens_scope", acpi_ut_get_type_name (type)); + + + if (!acpi_ut_valid_object_type (type)) { + /* type code out of range */ + + ACPI_REPORT_WARNING (("ns_opens_scope: Invalid Object Type %X\n", type)); + return_VALUE (ACPI_NS_NORMAL); + } + + return_VALUE (((u32) acpi_gbl_ns_properties[type]) & ACPI_NS_NEWSCOPE); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_node_by_path + * + * PARAMETERS: *Pathname - Name to be found, in external (ASL) format. The + * \ (backslash) and ^ (carat) prefixes, and the + * . (period) to separate segments are supported. + * start_node - Root of subtree to be searched, or NS_ALL for the + * root of the name space. If Name is fully + * qualified (first s8 is '\'), the passed value + * of Scope will not be accessed. + * Flags - Used to indicate whether to perform upsearch or + * not. + * return_node - Where the Node is returned + * + * DESCRIPTION: Look up a name relative to a given scope and return the + * corresponding Node. NOTE: Scope can be null. + * + * MUTEX: Locks namespace + * + ******************************************************************************/ + +acpi_status +acpi_ns_get_node_by_path ( + char *pathname, + struct acpi_namespace_node *start_node, + u32 flags, + struct acpi_namespace_node **return_node) +{ + union acpi_generic_state scope_info; + acpi_status status; + char *internal_path = NULL; + + + ACPI_FUNCTION_TRACE_PTR ("ns_get_node_by_path", pathname); + + + if (pathname) { + /* Convert path to internal representation */ + + status = acpi_ns_internalize_name (pathname, &internal_path); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Must lock namespace during lookup */ + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Setup lookup scope (search starting point) */ + + scope_info.scope.node = start_node; + + /* Lookup the name in the namespace */ + + status = acpi_ns_lookup (&scope_info, internal_path, + ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + (flags | ACPI_NS_DONT_OPEN_SCOPE), + NULL, return_node); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "%s, %s\n", + internal_path, acpi_format_exception (status))); + } + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + +cleanup: + /* Cleanup */ + if (internal_path) { + ACPI_MEM_FREE (internal_path); + } + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_find_parent_name + * + * PARAMETERS: *child_node - Named Obj whose name is to be found + * + * RETURN: The ACPI name + * + * DESCRIPTION: Search for the given obj in its parent scope and return the + * name segment, or "????" if the parent name can't be found + * (which "should not happen"). + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_name +acpi_ns_find_parent_name ( + struct acpi_namespace_node *child_node) +{ + struct acpi_namespace_node *parent_node; + + + ACPI_FUNCTION_TRACE ("ns_find_parent_name"); + + + if (child_node) { + /* Valid entry. Get the parent Node */ + + parent_node = acpi_ns_get_parent_node (child_node); + if (parent_node) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Parent of %p [%4.4s] is %p [%4.4s]\n", + child_node, acpi_ut_get_node_name (child_node), + parent_node, acpi_ut_get_node_name (parent_node))); + + if (parent_node->name.integer) { + return_VALUE ((acpi_name) parent_node->name.integer); + } + } + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Unable to find parent of %p (%4.4s)\n", + child_node, acpi_ut_get_node_name (child_node))); + } + + return_VALUE (ACPI_UNKNOWN_NAME); +} +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_parent_node + * + * PARAMETERS: Node - Current table entry + * + * RETURN: Parent entry of the given entry + * + * DESCRIPTION: Obtain the parent entry for a given entry in the namespace. + * + ******************************************************************************/ + + +struct acpi_namespace_node * +acpi_ns_get_parent_node ( + struct acpi_namespace_node *node) +{ + ACPI_FUNCTION_ENTRY (); + + + if (!node) { + return (NULL); + } + + /* + * Walk to the end of this peer list. The last entry is marked with a flag + * and the peer pointer is really a pointer back to the parent. This saves + * putting a parent back pointer in each and every named object! + */ + while (!(node->flags & ANOBJ_END_OF_PEER_LIST)) { + node = node->peer; + } + + + return (node->peer); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_next_valid_node + * + * PARAMETERS: Node - Current table entry + * + * RETURN: Next valid Node in the linked node list. NULL if no more valid + * nodes. + * + * DESCRIPTION: Find the next valid node within a name table. + * Useful for implementing NULL-end-of-list loops. + * + ******************************************************************************/ + + +struct acpi_namespace_node * +acpi_ns_get_next_valid_node ( + struct acpi_namespace_node *node) +{ + + /* If we are at the end of this peer list, return NULL */ + + if (node->flags & ANOBJ_END_OF_PEER_LIST) { + return NULL; + } + + /* Otherwise just return the next peer */ + + return (node->peer); +} + + diff --git a/drivers/acpi/namespace/nswalk.c b/drivers/acpi/namespace/nswalk.c new file mode 100644 index 00000000000..4de2444df30 --- /dev/null +++ b/drivers/acpi/namespace/nswalk.c @@ -0,0 +1,289 @@ +/****************************************************************************** + * + * Module Name: nswalk - Functions for walking the ACPI namespace + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nswalk") + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_next_node + * + * PARAMETERS: Type - Type of node to be searched for + * parent_node - Parent node whose children we are + * getting + * child_node - Previous child that was found. + * The NEXT child will be returned + * + * RETURN: struct acpi_namespace_node - Pointer to the NEXT child or NULL if + * none is found. + * + * DESCRIPTION: Return the next peer node within the namespace. If Handle + * is valid, Scope is ignored. Otherwise, the first node + * within Scope is returned. + * + ******************************************************************************/ + +struct acpi_namespace_node * +acpi_ns_get_next_node ( + acpi_object_type type, + struct acpi_namespace_node *parent_node, + struct acpi_namespace_node *child_node) +{ + struct acpi_namespace_node *next_node = NULL; + + + ACPI_FUNCTION_ENTRY (); + + + if (!child_node) { + /* It's really the parent's _scope_ that we want */ + + if (parent_node->child) { + next_node = parent_node->child; + } + } + + else { + /* Start search at the NEXT node */ + + next_node = acpi_ns_get_next_valid_node (child_node); + } + + /* If any type is OK, we are done */ + + if (type == ACPI_TYPE_ANY) { + /* next_node is NULL if we are at the end-of-list */ + + return (next_node); + } + + /* Must search for the node -- but within this scope only */ + + while (next_node) { + /* If type matches, we are done */ + + if (next_node->type == type) { + return (next_node); + } + + /* Otherwise, move on to the next node */ + + next_node = acpi_ns_get_next_valid_node (next_node); + } + + /* Not found */ + + return (NULL); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_walk_namespace + * + * PARAMETERS: Type - acpi_object_type to search for + * start_node - Handle in namespace where search begins + * max_depth - Depth to which search is to reach + * unlock_before_callback- Whether to unlock the NS before invoking + * the callback routine + * user_function - Called when an object of "Type" is found + * Context - Passed to user function + * return_value - from the user_function if terminated early. + * Otherwise, returns NULL. + * RETURNS: Status + * + * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, + * starting (and ending) at the node specified by start_handle. + * The user_function is called whenever a node that matches + * the type parameter is found. If the user function returns + * a non-zero value, the search is terminated immediately and this + * value is returned to the caller. + * + * The point of this procedure is to provide a generic namespace + * walk routine that can be called from multiple places to + * provide multiple services; the User Function can be tailored + * to each task, whether it is a print function, a compare + * function, etc. + * + ******************************************************************************/ + +acpi_status +acpi_ns_walk_namespace ( + acpi_object_type type, + acpi_handle start_node, + u32 max_depth, + u8 unlock_before_callback, + acpi_walk_callback user_function, + void *context, + void **return_value) +{ + acpi_status status; + acpi_status mutex_status; + struct acpi_namespace_node *child_node; + struct acpi_namespace_node *parent_node; + acpi_object_type child_type; + u32 level; + + + ACPI_FUNCTION_TRACE ("ns_walk_namespace"); + + + /* Special case for the namespace Root Node */ + + if (start_node == ACPI_ROOT_OBJECT) { + start_node = acpi_gbl_root_node; + } + + /* Null child means "get first node" */ + + parent_node = start_node; + child_node = NULL; + child_type = ACPI_TYPE_ANY; + level = 1; + + /* + * Traverse the tree of nodes until we bubble back up to where we + * started. When Level is zero, the loop is done because we have + * bubbled up to (and passed) the original parent handle (start_entry) + */ + while (level > 0) { + /* Get the next node in this scope. Null if not found */ + + status = AE_OK; + child_node = acpi_ns_get_next_node (ACPI_TYPE_ANY, parent_node, child_node); + if (child_node) { + /* + * Found node, Get the type if we are not + * searching for ANY + */ + if (type != ACPI_TYPE_ANY) { + child_type = child_node->type; + } + + if (child_type == type) { + /* + * Found a matching node, invoke the user + * callback function + */ + if (unlock_before_callback) { + mutex_status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (mutex_status)) { + return_ACPI_STATUS (mutex_status); + } + } + + status = user_function (child_node, level, + context, return_value); + + if (unlock_before_callback) { + mutex_status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (mutex_status)) { + return_ACPI_STATUS (mutex_status); + } + } + + switch (status) { + case AE_OK: + case AE_CTRL_DEPTH: + + /* Just keep going */ + break; + + case AE_CTRL_TERMINATE: + + /* Exit now, with OK status */ + + return_ACPI_STATUS (AE_OK); + + default: + + /* All others are valid exceptions */ + + return_ACPI_STATUS (status); + } + } + + /* + * Depth first search: + * Attempt to go down another level in the namespace + * if we are allowed to. Don't go any further if we + * have reached the caller specified maximum depth + * or if the user function has specified that the + * maximum depth has been reached. + */ + if ((level < max_depth) && (status != AE_CTRL_DEPTH)) { + if (acpi_ns_get_next_node (ACPI_TYPE_ANY, child_node, NULL)) { + /* + * There is at least one child of this + * node, visit the onde + */ + level++; + parent_node = child_node; + child_node = NULL; + } + } + } + else { + /* + * No more children of this node (acpi_ns_get_next_node + * failed), go back upwards in the namespace tree to + * the node's parent. + */ + level--; + child_node = parent_node; + parent_node = acpi_ns_get_parent_node (parent_node); + } + } + + /* Complete walk, not terminated by user function */ + + return_ACPI_STATUS (AE_OK); +} + + diff --git a/drivers/acpi/namespace/nsxfeval.c b/drivers/acpi/namespace/nsxfeval.c new file mode 100644 index 00000000000..1dc995586cb --- /dev/null +++ b/drivers/acpi/namespace/nsxfeval.c @@ -0,0 +1,764 @@ +/******************************************************************************* + * + * Module Name: nsxfeval - Public interfaces to the ACPI subsystem + * ACPI Object evaluation interfaces + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acinterp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsxfeval") + + +/******************************************************************************* + * + * FUNCTION: acpi_evaluate_object_typed + * + * PARAMETERS: Handle - Object handle (optional) + * *Pathname - Object pathname (optional) + * **external_params - List of parameters to pass to method, + * terminated by NULL. May be NULL + * if no parameters are being passed. + * *return_buffer - Where to put method's return value (if + * any). If NULL, no value is returned. + * return_type - Expected type of return object + * + * RETURN: Status + * + * DESCRIPTION: Find and evaluate the given object, passing the given + * parameters if necessary. One of "Handle" or "Pathname" must + * be valid (non-null) + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_evaluate_object_typed ( + acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *external_params, + struct acpi_buffer *return_buffer, + acpi_object_type return_type) +{ + acpi_status status; + u8 must_free = FALSE; + + + ACPI_FUNCTION_TRACE ("acpi_evaluate_object_typed"); + + + /* Return buffer must be valid */ + + if (!return_buffer) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (return_buffer->length == ACPI_ALLOCATE_BUFFER) { + must_free = TRUE; + } + + /* Evaluate the object */ + + status = acpi_evaluate_object (handle, pathname, external_params, return_buffer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Type ANY means "don't care" */ + + if (return_type == ACPI_TYPE_ANY) { + return_ACPI_STATUS (AE_OK); + } + + if (return_buffer->length == 0) { + /* Error because caller specifically asked for a return value */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "No return value\n")); + + return_ACPI_STATUS (AE_NULL_OBJECT); + } + + /* Examine the object type returned from evaluate_object */ + + if (((union acpi_object *) return_buffer->pointer)->type == return_type) { + return_ACPI_STATUS (AE_OK); + } + + /* Return object type does not match requested type */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Incorrect return type [%s] requested [%s]\n", + acpi_ut_get_type_name (((union acpi_object *) return_buffer->pointer)->type), + acpi_ut_get_type_name (return_type))); + + if (must_free) { + /* Caller used ACPI_ALLOCATE_BUFFER, free the return buffer */ + + acpi_os_free (return_buffer->pointer); + return_buffer->pointer = NULL; + } + + return_buffer->length = 0; + return_ACPI_STATUS (AE_TYPE); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_evaluate_object + * + * PARAMETERS: Handle - Object handle (optional) + * Pathname - Object pathname (optional) + * external_params - List of parameters to pass to method, + * terminated by NULL. May be NULL + * if no parameters are being passed. + * return_buffer - Where to put method's return value (if + * any). If NULL, no value is returned. + * + * RETURN: Status + * + * DESCRIPTION: Find and evaluate the given object, passing the given + * parameters if necessary. One of "Handle" or "Pathname" must + * be valid (non-null) + * + ******************************************************************************/ + +acpi_status +acpi_evaluate_object ( + acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *external_params, + struct acpi_buffer *return_buffer) +{ + acpi_status status; + acpi_status status2; + struct acpi_parameter_info info; + acpi_size buffer_space_needed; + u32 i; + + + ACPI_FUNCTION_TRACE ("acpi_evaluate_object"); + + + info.node = handle; + info.parameters = NULL; + info.return_object = NULL; + info.parameter_type = ACPI_PARAM_ARGS; + + /* + * If there are parameters to be passed to the object + * (which must be a control method), the external objects + * must be converted to internal objects + */ + if (external_params && external_params->count) { + /* + * Allocate a new parameter block for the internal objects + * Add 1 to count to allow for null terminated internal list + */ + info.parameters = ACPI_MEM_CALLOCATE ( + ((acpi_size) external_params->count + 1) * + sizeof (void *)); + if (!info.parameters) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* + * Convert each external object in the list to an + * internal object + */ + for (i = 0; i < external_params->count; i++) { + status = acpi_ut_copy_eobject_to_iobject (&external_params->pointer[i], + &info.parameters[i]); + if (ACPI_FAILURE (status)) { + acpi_ut_delete_internal_object_list (info.parameters); + return_ACPI_STATUS (status); + } + } + info.parameters[external_params->count] = NULL; + } + + + /* + * Three major cases: + * 1) Fully qualified pathname + * 2) No handle, not fully qualified pathname (error) + * 3) Valid handle + */ + if ((pathname) && + (acpi_ns_valid_root_prefix (pathname[0]))) { + /* + * The path is fully qualified, just evaluate by name + */ + status = acpi_ns_evaluate_by_name (pathname, &info); + } + else if (!handle) { + /* + * A handle is optional iff a fully qualified pathname + * is specified. Since we've already handled fully + * qualified names above, this is an error + */ + if (!pathname) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Both Handle and Pathname are NULL\n")); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Handle is NULL and Pathname is relative\n")); + } + + status = AE_BAD_PARAMETER; + } + else { + /* + * We get here if we have a handle -- and if we have a + * pathname it is relative. The handle will be validated + * in the lower procedures + */ + if (!pathname) { + /* + * The null pathname case means the handle is for + * the actual object to be evaluated + */ + status = acpi_ns_evaluate_by_handle (&info); + } + else { + /* + * Both a Handle and a relative Pathname + */ + status = acpi_ns_evaluate_relative (pathname, &info); + } + } + + + /* + * If we are expecting a return value, and all went well above, + * copy the return value to an external object. + */ + if (return_buffer) { + if (!info.return_object) { + return_buffer->length = 0; + } + else { + if (ACPI_GET_DESCRIPTOR_TYPE (info.return_object) == ACPI_DESC_TYPE_NAMED) { + /* + * If we received a NS Node as a return object, this means that + * the object we are evaluating has nothing interesting to + * return (such as a mutex, etc.) We return an error because + * these types are essentially unsupported by this interface. + * We don't check up front because this makes it easier to add + * support for various types at a later date if necessary. + */ + status = AE_TYPE; + info.return_object = NULL; /* No need to delete a NS Node */ + return_buffer->length = 0; + } + + if (ACPI_SUCCESS (status)) { + /* + * Find out how large a buffer is needed + * to contain the returned object + */ + status = acpi_ut_get_object_size (info.return_object, + &buffer_space_needed); + if (ACPI_SUCCESS (status)) { + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer (return_buffer, buffer_space_needed); + if (ACPI_FAILURE (status)) { + /* + * Caller's buffer is too small or a new one can't be allocated + */ + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Needed buffer size %X, %s\n", + (u32) buffer_space_needed, + acpi_format_exception (status))); + } + else { + /* + * We have enough space for the object, build it + */ + status = acpi_ut_copy_iobject_to_eobject (info.return_object, + return_buffer); + } + } + } + } + } + + if (info.return_object) { + /* + * Delete the internal return object. NOTE: Interpreter + * must be locked to avoid race condition. + */ + status2 = acpi_ex_enter_interpreter (); + if (ACPI_SUCCESS (status2)) { + /* + * Delete the internal return object. (Or at least + * decrement the reference count by one) + */ + acpi_ut_remove_reference (info.return_object); + acpi_ex_exit_interpreter (); + } + } + + /* + * Free the input parameter list (if we created one), + */ + if (info.parameters) { + /* Free the allocated parameter block */ + + acpi_ut_delete_internal_object_list (info.parameters); + } + + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_evaluate_object); + + +/******************************************************************************* + * + * FUNCTION: acpi_walk_namespace + * + * PARAMETERS: Type - acpi_object_type to search for + * start_object - Handle in namespace where search begins + * max_depth - Depth to which search is to reach + * user_function - Called when an object of "Type" is found + * Context - Passed to user function + * return_value - Location where return value of + * user_function is put if terminated early + * + * RETURNS Return value from the user_function if terminated early. + * Otherwise, returns NULL. + * + * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, + * starting (and ending) at the object specified by start_handle. + * The user_function is called whenever an object that matches + * the type parameter is found. If the user function returns + * a non-zero value, the search is terminated immediately and this + * value is returned to the caller. + * + * The point of this procedure is to provide a generic namespace + * walk routine that can be called from multiple places to + * provide multiple services; the User Function can be tailored + * to each task, whether it is a print function, a compare + * function, etc. + * + ******************************************************************************/ + +acpi_status +acpi_walk_namespace ( + acpi_object_type type, + acpi_handle start_object, + u32 max_depth, + acpi_walk_callback user_function, + void *context, + void **return_value) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_walk_namespace"); + + + /* Parameter validation */ + + if ((type > ACPI_TYPE_EXTERNAL_MAX) || + (!max_depth) || + (!user_function)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Lock the namespace around the walk. + * The namespace will be unlocked/locked around each call + * to the user function - since this function + * must be allowed to make Acpi calls itself. + */ + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_ns_walk_namespace (type, start_object, max_depth, ACPI_NS_WALK_UNLOCK, + user_function, context, return_value); + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_walk_namespace); + + +/******************************************************************************* + * + * FUNCTION: acpi_ns_get_device_callback + * + * PARAMETERS: Callback from acpi_get_device + * + * RETURN: Status + * + * DESCRIPTION: Takes callbacks from walk_namespace and filters out all non- + * present devices, or if they specified a HID, it filters based + * on that. + * + ******************************************************************************/ + +static acpi_status +acpi_ns_get_device_callback ( + acpi_handle obj_handle, + u32 nesting_level, + void *context, + void **return_value) +{ + struct acpi_get_devices_info *info = context; + acpi_status status; + struct acpi_namespace_node *node; + u32 flags; + struct acpi_device_id hid; + struct acpi_compatible_id_list *cid; + acpi_native_uint i; + + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + node = acpi_ns_map_handle_to_node (obj_handle); + status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + if (!node) { + return (AE_BAD_PARAMETER); + } + + /* Run _STA to determine if device is present */ + + status = acpi_ut_execute_STA (node, &flags); + if (ACPI_FAILURE (status)) { + return (AE_CTRL_DEPTH); + } + + if (!(flags & 0x01)) { + /* Don't return at the device or children of the device if not there */ + + return (AE_CTRL_DEPTH); + } + + /* Filter based on device HID & CID */ + + if (info->hid != NULL) { + status = acpi_ut_execute_HID (node, &hid); + if (status == AE_NOT_FOUND) { + return (AE_OK); + } + else if (ACPI_FAILURE (status)) { + return (AE_CTRL_DEPTH); + } + + if (ACPI_STRNCMP (hid.value, info->hid, sizeof (hid.value)) != 0) { + /* Get the list of Compatible IDs */ + + status = acpi_ut_execute_CID (node, &cid); + if (status == AE_NOT_FOUND) { + return (AE_OK); + } + else if (ACPI_FAILURE (status)) { + return (AE_CTRL_DEPTH); + } + + /* Walk the CID list */ + + for (i = 0; i < cid->count; i++) { + if (ACPI_STRNCMP (cid->id[i].value, info->hid, + sizeof (struct acpi_compatible_id)) != 0) { + ACPI_MEM_FREE (cid); + return (AE_OK); + } + } + ACPI_MEM_FREE (cid); + } + } + + status = info->user_function (obj_handle, nesting_level, info->context, return_value); + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_get_devices + * + * PARAMETERS: HID - HID to search for. Can be NULL. + * user_function - Called when a matching object is found + * Context - Passed to user function + * return_value - Location where return value of + * user_function is put if terminated early + * + * RETURNS Return value from the user_function if terminated early. + * Otherwise, returns NULL. + * + * DESCRIPTION: Performs a modified depth-first walk of the namespace tree, + * starting (and ending) at the object specified by start_handle. + * The user_function is called whenever an object of type + * Device is found. If the user function returns + * a non-zero value, the search is terminated immediately and this + * value is returned to the caller. + * + * This is a wrapper for walk_namespace, but the callback performs + * additional filtering. Please see acpi_get_device_callback. + * + ******************************************************************************/ + +acpi_status +acpi_get_devices ( + char *HID, + acpi_walk_callback user_function, + void *context, + void **return_value) +{ + acpi_status status; + struct acpi_get_devices_info info; + + + ACPI_FUNCTION_TRACE ("acpi_get_devices"); + + + /* Parameter validation */ + + if (!user_function) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * We're going to call their callback from OUR callback, so we need + * to know what it is, and their context parameter. + */ + info.context = context; + info.user_function = user_function; + info.hid = HID; + + /* + * Lock the namespace around the walk. + * The namespace will be unlocked/locked around each call + * to the user function - since this function + * must be allowed to make Acpi calls itself. + */ + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_ns_walk_namespace (ACPI_TYPE_DEVICE, + ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, + ACPI_NS_WALK_UNLOCK, + acpi_ns_get_device_callback, &info, + return_value); + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_get_devices); + + +/******************************************************************************* + * + * FUNCTION: acpi_attach_data + * + * PARAMETERS: obj_handle - Namespace node + * Handler - Handler for this attachment + * Data - Pointer to data to be attached + * + * RETURN: Status + * + * DESCRIPTION: Attach arbitrary data and handler to a namespace node. + * + ******************************************************************************/ + +acpi_status +acpi_attach_data ( + acpi_handle obj_handle, + acpi_object_handler handler, + void *data) +{ + struct acpi_namespace_node *node; + acpi_status status; + + + /* Parameter validation */ + + if (!obj_handle || + !handler || + !data) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_map_handle_to_node (obj_handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ns_attach_data (node, handler, data); + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_detach_data + * + * PARAMETERS: obj_handle - Namespace node handle + * Handler - Handler used in call to acpi_attach_data + * + * RETURN: Status + * + * DESCRIPTION: Remove data that was previously attached to a node. + * + ******************************************************************************/ + +acpi_status +acpi_detach_data ( + acpi_handle obj_handle, + acpi_object_handler handler) +{ + struct acpi_namespace_node *node; + acpi_status status; + + + /* Parameter validation */ + + if (!obj_handle || + !handler) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_map_handle_to_node (obj_handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ns_detach_data (node, handler); + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_get_data + * + * PARAMETERS: obj_handle - Namespace node + * Handler - Handler used in call to attach_data + * Data - Where the data is returned + * + * RETURN: Status + * + * DESCRIPTION: Retrieve data that was previously attached to a namespace node. + * + ******************************************************************************/ + +acpi_status +acpi_get_data ( + acpi_handle obj_handle, + acpi_object_handler handler, + void **data) +{ + struct acpi_namespace_node *node; + acpi_status status; + + + /* Parameter validation */ + + if (!obj_handle || + !handler || + !data) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_map_handle_to_node (obj_handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + status = acpi_ns_get_attached_data (node, handler, data); + +unlock_and_exit: + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} + + diff --git a/drivers/acpi/namespace/nsxfname.c b/drivers/acpi/namespace/nsxfname.c new file mode 100644 index 00000000000..f2405efd1b9 --- /dev/null +++ b/drivers/acpi/namespace/nsxfname.c @@ -0,0 +1,369 @@ +/****************************************************************************** + * + * Module Name: nsxfname - Public interfaces to the ACPI subsystem + * ACPI Namespace oriented interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsxfname") + + +/****************************************************************************** + * + * FUNCTION: acpi_get_handle + * + * PARAMETERS: Parent - Object to search under (search scope). + * path_name - Pointer to an asciiz string containing the + * name + * ret_handle - Where the return handle is placed + * + * RETURN: Status + * + * DESCRIPTION: This routine will search for a caller specified name in the + * name space. The caller can restrict the search region by + * specifying a non NULL parent. The parent value is itself a + * namespace handle. + * + ******************************************************************************/ + +acpi_status +acpi_get_handle ( + acpi_handle parent, + acpi_string pathname, + acpi_handle *ret_handle) +{ + acpi_status status; + struct acpi_namespace_node *node = NULL; + struct acpi_namespace_node *prefix_node = NULL; + + + ACPI_FUNCTION_ENTRY (); + + + /* Parameter Validation */ + + if (!ret_handle || !pathname) { + return (AE_BAD_PARAMETER); + } + + /* Convert a parent handle to a prefix node */ + + if (parent) { + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + prefix_node = acpi_ns_map_handle_to_node (parent); + if (!prefix_node) { + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + } + + /* Special case for root, since we can't search for it */ + + if (ACPI_STRCMP (pathname, ACPI_NS_ROOT_PATH) == 0) { + *ret_handle = acpi_ns_convert_entry_to_handle (acpi_gbl_root_node); + return (AE_OK); + } + + /* + * Find the Node and convert to a handle + */ + status = acpi_ns_get_node_by_path (pathname, prefix_node, ACPI_NS_NO_UPSEARCH, + &node); + + *ret_handle = NULL; + if (ACPI_SUCCESS (status)) { + *ret_handle = acpi_ns_convert_entry_to_handle (node); + } + + return (status); +} +EXPORT_SYMBOL(acpi_get_handle); + + +/****************************************************************************** + * + * FUNCTION: acpi_get_name + * + * PARAMETERS: Handle - Handle to be converted to a pathname + * name_type - Full pathname or single segment + * Buffer - Buffer for returned path + * + * RETURN: Pointer to a string containing the fully qualified Name. + * + * DESCRIPTION: This routine returns the fully qualified name associated with + * the Handle parameter. This and the acpi_pathname_to_handle are + * complementary functions. + * + ******************************************************************************/ + +acpi_status +acpi_get_name ( + acpi_handle handle, + u32 name_type, + struct acpi_buffer *buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + + + /* Parameter validation */ + + if (name_type > ACPI_NAME_TYPE_MAX) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_validate_buffer (buffer); + if (ACPI_FAILURE (status)) { + return (status); + } + + if (name_type == ACPI_FULL_PATHNAME) { + /* Get the full pathname (From the namespace root) */ + + status = acpi_ns_handle_to_pathname (handle, buffer); + return (status); + } + + /* + * Wants the single segment ACPI name. + * Validate handle and convert to a namespace Node + */ + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + node = acpi_ns_map_handle_to_node (handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer (buffer, ACPI_PATH_SEGMENT_LENGTH); + if (ACPI_FAILURE (status)) { + goto unlock_and_exit; + } + + /* Just copy the ACPI name from the Node and zero terminate it */ + + ACPI_STRNCPY (buffer->pointer, acpi_ut_get_node_name (node), + ACPI_NAME_SIZE); + ((char *) buffer->pointer) [ACPI_NAME_SIZE] = 0; + status = AE_OK; + + +unlock_and_exit: + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} +EXPORT_SYMBOL(acpi_get_name); + + +/****************************************************************************** + * + * FUNCTION: acpi_get_object_info + * + * PARAMETERS: Handle - Object Handle + * Info - Where the info is returned + * + * RETURN: Status + * + * DESCRIPTION: Returns information about an object as gleaned from the + * namespace node and possibly by running several standard + * control methods (Such as in the case of a device.) + * + ******************************************************************************/ + +acpi_status +acpi_get_object_info ( + acpi_handle handle, + struct acpi_buffer *buffer) +{ + acpi_status status; + struct acpi_namespace_node *node; + struct acpi_device_info *info; + struct acpi_device_info *return_info; + struct acpi_compatible_id_list *cid_list = NULL; + acpi_size size; + + + /* Parameter validation */ + + if (!handle || !buffer) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_validate_buffer (buffer); + if (ACPI_FAILURE (status)) { + return (status); + } + + info = ACPI_MEM_CALLOCATE (sizeof (struct acpi_device_info)); + if (!info) { + return (AE_NO_MEMORY); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + node = acpi_ns_map_handle_to_node (handle); + if (!node) { + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + goto cleanup; + } + + /* Init return structure */ + + size = sizeof (struct acpi_device_info); + + info->type = node->type; + info->name = node->name.integer; + info->valid = 0; + + status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* If not a device, we are all done */ + + if (info->type == ACPI_TYPE_DEVICE) { + /* + * Get extra info for ACPI Devices objects only: + * Run the Device _HID, _UID, _CID, _STA, _ADR and _sx_d methods. + * + * Note: none of these methods are required, so they may or may + * not be present for this device. The Info->Valid bitfield is used + * to indicate which methods were found and ran successfully. + */ + + /* Execute the Device._HID method */ + + status = acpi_ut_execute_HID (node, &info->hardware_id); + if (ACPI_SUCCESS (status)) { + info->valid |= ACPI_VALID_HID; + } + + /* Execute the Device._UID method */ + + status = acpi_ut_execute_UID (node, &info->unique_id); + if (ACPI_SUCCESS (status)) { + info->valid |= ACPI_VALID_UID; + } + + /* Execute the Device._CID method */ + + status = acpi_ut_execute_CID (node, &cid_list); + if (ACPI_SUCCESS (status)) { + size += ((acpi_size) cid_list->count - 1) * + sizeof (struct acpi_compatible_id); + info->valid |= ACPI_VALID_CID; + } + + /* Execute the Device._STA method */ + + status = acpi_ut_execute_STA (node, &info->current_status); + if (ACPI_SUCCESS (status)) { + info->valid |= ACPI_VALID_STA; + } + + /* Execute the Device._ADR method */ + + status = acpi_ut_evaluate_numeric_object (METHOD_NAME__ADR, node, + &info->address); + if (ACPI_SUCCESS (status)) { + info->valid |= ACPI_VALID_ADR; + } + + /* Execute the Device._sx_d methods */ + + status = acpi_ut_execute_sxds (node, info->highest_dstates); + if (ACPI_SUCCESS (status)) { + info->valid |= ACPI_VALID_SXDS; + } + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer (buffer, size); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Populate the return buffer */ + + return_info = buffer->pointer; + ACPI_MEMCPY (return_info, info, sizeof (struct acpi_device_info)); + + if (cid_list) { + ACPI_MEMCPY (&return_info->compatibility_id, cid_list, cid_list->size); + } + + +cleanup: + ACPI_MEM_FREE (info); + if (cid_list) { + ACPI_MEM_FREE (cid_list); + } + return (status); +} +EXPORT_SYMBOL(acpi_get_object_info); + diff --git a/drivers/acpi/namespace/nsxfobj.c b/drivers/acpi/namespace/nsxfobj.c new file mode 100644 index 00000000000..19acf32674b --- /dev/null +++ b/drivers/acpi/namespace/nsxfobj.c @@ -0,0 +1,262 @@ +/******************************************************************************* + * + * Module Name: nsxfobj - Public interfaces to the ACPI subsystem + * ACPI Object oriented interfaces + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_NAMESPACE + ACPI_MODULE_NAME ("nsxfobj") + +/******************************************************************************* + * + * FUNCTION: acpi_get_type + * + * PARAMETERS: Handle - Handle of object whose type is desired + * *ret_type - Where the type will be placed + * + * RETURN: Status + * + * DESCRIPTION: This routine returns the type associatd with a particular handle + * + ******************************************************************************/ + +acpi_status +acpi_get_type ( + acpi_handle handle, + acpi_object_type *ret_type) +{ + struct acpi_namespace_node *node; + acpi_status status; + + + /* Parameter Validation */ + + if (!ret_type) { + return (AE_BAD_PARAMETER); + } + + /* + * Special case for the predefined Root Node + * (return type ANY) + */ + if (handle == ACPI_ROOT_OBJECT) { + *ret_type = ACPI_TYPE_ANY; + return (AE_OK); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_map_handle_to_node (handle); + if (!node) { + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return (AE_BAD_PARAMETER); + } + + *ret_type = node->type; + + + status = acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} +EXPORT_SYMBOL(acpi_get_type); + + +/******************************************************************************* + * + * FUNCTION: acpi_get_parent + * + * PARAMETERS: Handle - Handle of object whose parent is desired + * ret_handle - Where the parent handle will be placed + * + * RETURN: Status + * + * DESCRIPTION: Returns a handle to the parent of the object represented by + * Handle. + * + ******************************************************************************/ + +acpi_status +acpi_get_parent ( + acpi_handle handle, + acpi_handle *ret_handle) +{ + struct acpi_namespace_node *node; + acpi_status status; + + + if (!ret_handle) { + return (AE_BAD_PARAMETER); + } + + /* Special case for the predefined Root Node (no parent) */ + + if (handle == ACPI_ROOT_OBJECT) { + return (AE_NULL_ENTRY); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* Convert and validate the handle */ + + node = acpi_ns_map_handle_to_node (handle); + if (!node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + + /* Get the parent entry */ + + *ret_handle = + acpi_ns_convert_entry_to_handle (acpi_ns_get_parent_node (node)); + + /* Return exception if parent is null */ + + if (!acpi_ns_get_parent_node (node)) { + status = AE_NULL_ENTRY; + } + + +unlock_and_exit: + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} +EXPORT_SYMBOL(acpi_get_parent); + + +/******************************************************************************* + * + * FUNCTION: acpi_get_next_object + * + * PARAMETERS: Type - Type of object to be searched for + * Parent - Parent object whose children we are getting + * last_child - Previous child that was found. + * The NEXT child will be returned + * ret_handle - Where handle to the next object is placed + * + * RETURN: Status + * + * DESCRIPTION: Return the next peer object within the namespace. If Handle is + * valid, Scope is ignored. Otherwise, the first object within + * Scope is returned. + * + ******************************************************************************/ + +acpi_status +acpi_get_next_object ( + acpi_object_type type, + acpi_handle parent, + acpi_handle child, + acpi_handle *ret_handle) +{ + acpi_status status; + struct acpi_namespace_node *node; + struct acpi_namespace_node *parent_node = NULL; + struct acpi_namespace_node *child_node = NULL; + + + /* Parameter validation */ + + if (type > ACPI_TYPE_EXTERNAL_MAX) { + return (AE_BAD_PARAMETER); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_NAMESPACE); + if (ACPI_FAILURE (status)) { + return (status); + } + + /* If null handle, use the parent */ + + if (!child) { + /* Start search at the beginning of the specified scope */ + + parent_node = acpi_ns_map_handle_to_node (parent); + if (!parent_node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } + else { + /* Non-null handle, ignore the parent */ + /* Convert and validate the handle */ + + child_node = acpi_ns_map_handle_to_node (child); + if (!child_node) { + status = AE_BAD_PARAMETER; + goto unlock_and_exit; + } + } + + /* Internal function does the real work */ + + node = acpi_ns_get_next_node (type, parent_node, child_node); + if (!node) { + status = AE_NOT_FOUND; + goto unlock_and_exit; + } + + if (ret_handle) { + *ret_handle = acpi_ns_convert_entry_to_handle (node); + } + + +unlock_and_exit: + + (void) acpi_ut_release_mutex (ACPI_MTX_NAMESPACE); + return (status); +} +EXPORT_SYMBOL(acpi_get_next_object); + diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c new file mode 100644 index 00000000000..a82834b3275 --- /dev/null +++ b/drivers/acpi/numa.c @@ -0,0 +1,213 @@ +/* + * acpi_numa.c - ACPI NUMA support + * + * Copyright (C) 2002 Takayoshi Kochi <t-kochi@bq.jp.nec.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ +#include <linux/module.h> +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> +#include <acpi/acmacros.h> + +#define ACPI_NUMA 0x80000000 +#define _COMPONENT ACPI_NUMA + ACPI_MODULE_NAME ("numa") + +extern int __init acpi_table_parse_madt_family (enum acpi_table_id id, unsigned long madt_size, int entry_id, acpi_madt_entry_handler handler, unsigned int max_entries); + +void __init +acpi_table_print_srat_entry ( + acpi_table_entry_header *header) +{ + + ACPI_FUNCTION_NAME ("acpi_table_print_srat_entry"); + + if (!header) + return; + + switch (header->type) { + + case ACPI_SRAT_PROCESSOR_AFFINITY: +#ifdef ACPI_DEBUG_OUTPUT + { + struct acpi_table_processor_affinity *p = + (struct acpi_table_processor_affinity*) header; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SRAT Processor (id[0x%02x] eid[0x%02x]) in proximity domain %d %s\n", + p->apic_id, p->lsapic_eid, p->proximity_domain, + p->flags.enabled?"enabled":"disabled")); + } +#endif /* ACPI_DEBUG_OUTPUT */ + break; + + case ACPI_SRAT_MEMORY_AFFINITY: +#ifdef ACPI_DEBUG_OUTPUT + { + struct acpi_table_memory_affinity *p = + (struct acpi_table_memory_affinity*) header; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "SRAT Memory (0x%08x%08x length 0x%08x%08x type 0x%x) in proximity domain %d %s%s\n", + p->base_addr_hi, p->base_addr_lo, p->length_hi, p->length_lo, + p->memory_type, p->proximity_domain, + p->flags.enabled ? "enabled" : "disabled", + p->flags.hot_pluggable ? " hot-pluggable" : "")); + } +#endif /* ACPI_DEBUG_OUTPUT */ + break; + + default: + printk(KERN_WARNING PREFIX "Found unsupported SRAT entry (type = 0x%x)\n", + header->type); + break; + } +} + + +static int __init +acpi_parse_slit (unsigned long phys_addr, unsigned long size) +{ + struct acpi_table_slit *slit; + u32 localities; + + if (!phys_addr || !size) + return -EINVAL; + + slit = (struct acpi_table_slit *) __va(phys_addr); + + /* downcast just for %llu vs %lu for i386/ia64 */ + localities = (u32) slit->localities; + + acpi_numa_slit_init(slit); + + return 0; +} + + +static int __init +acpi_parse_processor_affinity ( + acpi_table_entry_header *header, + const unsigned long end) +{ + struct acpi_table_processor_affinity *processor_affinity; + + processor_affinity = (struct acpi_table_processor_affinity*) header; + if (!processor_affinity) + return -EINVAL; + + acpi_table_print_srat_entry(header); + + /* let architecture-dependent part to do it */ + acpi_numa_processor_affinity_init(processor_affinity); + + return 0; +} + + +static int __init +acpi_parse_memory_affinity ( + acpi_table_entry_header *header, + const unsigned long end) +{ + struct acpi_table_memory_affinity *memory_affinity; + + memory_affinity = (struct acpi_table_memory_affinity*) header; + if (!memory_affinity) + return -EINVAL; + + acpi_table_print_srat_entry(header); + + /* let architecture-dependent part to do it */ + acpi_numa_memory_affinity_init(memory_affinity); + + return 0; +} + + +static int __init +acpi_parse_srat (unsigned long phys_addr, unsigned long size) +{ + struct acpi_table_srat *srat; + + if (!phys_addr || !size) + return -EINVAL; + + srat = (struct acpi_table_srat *) __va(phys_addr); + + return 0; +} + + +int __init +acpi_table_parse_srat ( + enum acpi_srat_entry_id id, + acpi_madt_entry_handler handler, + unsigned int max_entries) +{ + return acpi_table_parse_madt_family(ACPI_SRAT, sizeof(struct acpi_table_srat), + id, handler, max_entries); +} + + +int __init +acpi_numa_init(void) +{ + int result; + + /* SRAT: Static Resource Affinity Table */ + result = acpi_table_parse(ACPI_SRAT, acpi_parse_srat); + + if (result > 0) { + result = acpi_table_parse_srat(ACPI_SRAT_PROCESSOR_AFFINITY, + acpi_parse_processor_affinity, + NR_CPUS); + result = acpi_table_parse_srat(ACPI_SRAT_MEMORY_AFFINITY, + acpi_parse_memory_affinity, + NR_NODE_MEMBLKS); // IA64 specific + } + + /* SLIT: System Locality Information Table */ + result = acpi_table_parse(ACPI_SLIT, acpi_parse_slit); + + acpi_numa_arch_fixup(); + return 0; +} + +int +acpi_get_pxm(acpi_handle h) +{ + unsigned long pxm; + acpi_status status; + acpi_handle handle; + acpi_handle phandle = h; + + do { + handle = phandle; + status = acpi_evaluate_integer(handle, "_PXM", NULL, &pxm); + if (ACPI_SUCCESS(status)) + return (int)pxm; + status = acpi_get_parent(handle, &phandle); + } while(ACPI_SUCCESS(status)); + return -1; +} +EXPORT_SYMBOL(acpi_get_pxm); diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c new file mode 100644 index 00000000000..5a9128de622 --- /dev/null +++ b/drivers/acpi/osl.c @@ -0,0 +1,1162 @@ +/* + * acpi_osl.c - OS-dependent functions ($Revision: 83 $) + * + * Copyright (C) 2000 Andrew Henroid + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/pci.h> +#include <linux/smp_lock.h> +#include <linux/interrupt.h> +#include <linux/kmod.h> +#include <linux/delay.h> +#include <linux/workqueue.h> +#include <linux/nmi.h> +#include <acpi/acpi.h> +#include <asm/io.h> +#include <acpi/acpi_bus.h> +#include <acpi/processor.h> +#include <asm/uaccess.h> + +#include <linux/efi.h> + + +#define _COMPONENT ACPI_OS_SERVICES +ACPI_MODULE_NAME ("osl") + +#define PREFIX "ACPI: " + +struct acpi_os_dpc +{ + acpi_osd_exec_callback function; + void *context; +}; + +#ifdef CONFIG_ACPI_CUSTOM_DSDT +#include CONFIG_ACPI_CUSTOM_DSDT_FILE +#endif + +#ifdef ENABLE_DEBUGGER +#include <linux/kdb.h> + +/* stuff for debugger support */ +int acpi_in_debugger; +EXPORT_SYMBOL(acpi_in_debugger); + +extern char line_buf[80]; +#endif /*ENABLE_DEBUGGER*/ + +static unsigned int acpi_irq_irq; +static acpi_osd_handler acpi_irq_handler; +static void *acpi_irq_context; +static struct workqueue_struct *kacpid_wq; + +acpi_status +acpi_os_initialize(void) +{ + return AE_OK; +} + +acpi_status +acpi_os_initialize1(void) +{ + /* + * Initialize PCI configuration space access, as we'll need to access + * it while walking the namespace (bus 0 and root bridges w/ _BBNs). + */ +#ifdef CONFIG_ACPI_PCI + if (!raw_pci_ops) { + printk(KERN_ERR PREFIX "Access to PCI configuration space unavailable\n"); + return AE_NULL_ENTRY; + } +#endif + kacpid_wq = create_singlethread_workqueue("kacpid"); + BUG_ON(!kacpid_wq); + + return AE_OK; +} + +acpi_status +acpi_os_terminate(void) +{ + if (acpi_irq_handler) { + acpi_os_remove_interrupt_handler(acpi_irq_irq, + acpi_irq_handler); + } + + destroy_workqueue(kacpid_wq); + + return AE_OK; +} + +void +acpi_os_printf(const char *fmt,...) +{ + va_list args; + va_start(args, fmt); + acpi_os_vprintf(fmt, args); + va_end(args); +} +EXPORT_SYMBOL(acpi_os_printf); + +void +acpi_os_vprintf(const char *fmt, va_list args) +{ + static char buffer[512]; + + vsprintf(buffer, fmt, args); + +#ifdef ENABLE_DEBUGGER + if (acpi_in_debugger) { + kdb_printf("%s", buffer); + } else { + printk("%s", buffer); + } +#else + printk("%s", buffer); +#endif +} + +void * +acpi_os_allocate(acpi_size size) +{ + return kmalloc(size, GFP_KERNEL); +} + +void +acpi_os_free(void *ptr) +{ + kfree(ptr); +} +EXPORT_SYMBOL(acpi_os_free); + +acpi_status +acpi_os_get_root_pointer(u32 flags, struct acpi_pointer *addr) +{ + if (efi_enabled) { + addr->pointer_type = ACPI_PHYSICAL_POINTER; + if (efi.acpi20) + addr->pointer.physical = + (acpi_physical_address) virt_to_phys(efi.acpi20); + else if (efi.acpi) + addr->pointer.physical = + (acpi_physical_address) virt_to_phys(efi.acpi); + else { + printk(KERN_ERR PREFIX "System description tables not found\n"); + return AE_NOT_FOUND; + } + } else { + if (ACPI_FAILURE(acpi_find_root_pointer(flags, addr))) { + printk(KERN_ERR PREFIX "System description tables not found\n"); + return AE_NOT_FOUND; + } + } + + return AE_OK; +} + +acpi_status +acpi_os_map_memory(acpi_physical_address phys, acpi_size size, void __iomem **virt) +{ + if (efi_enabled) { + if (EFI_MEMORY_WB & efi_mem_attributes(phys)) { + *virt = (void __iomem *) phys_to_virt(phys); + } else { + *virt = ioremap(phys, size); + } + } else { + if (phys > ULONG_MAX) { + printk(KERN_ERR PREFIX "Cannot map memory that high\n"); + return AE_BAD_PARAMETER; + } + /* + * ioremap checks to ensure this is in reserved space + */ + *virt = ioremap((unsigned long) phys, size); + } + + if (!*virt) + return AE_NO_MEMORY; + + return AE_OK; +} + +void +acpi_os_unmap_memory(void __iomem *virt, acpi_size size) +{ + iounmap(virt); +} + +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_os_get_physical_address(void *virt, acpi_physical_address *phys) +{ + if(!phys || !virt) + return AE_BAD_PARAMETER; + + *phys = virt_to_phys(virt); + + return AE_OK; +} +#endif + +#define ACPI_MAX_OVERRIDE_LEN 100 + +static char acpi_os_name[ACPI_MAX_OVERRIDE_LEN]; + +acpi_status +acpi_os_predefined_override (const struct acpi_predefined_names *init_val, + acpi_string *new_val) +{ + if (!init_val || !new_val) + return AE_BAD_PARAMETER; + + *new_val = NULL; + if (!memcmp (init_val->name, "_OS_", 4) && strlen(acpi_os_name)) { + printk(KERN_INFO PREFIX "Overriding _OS definition to '%s'\n", + acpi_os_name); + *new_val = acpi_os_name; + } + + return AE_OK; +} + +acpi_status +acpi_os_table_override (struct acpi_table_header *existing_table, + struct acpi_table_header **new_table) +{ + if (!existing_table || !new_table) + return AE_BAD_PARAMETER; + +#ifdef CONFIG_ACPI_CUSTOM_DSDT + if (strncmp(existing_table->signature, "DSDT", 4) == 0) + *new_table = (struct acpi_table_header*)AmlCode; + else + *new_table = NULL; +#else + *new_table = NULL; +#endif + return AE_OK; +} + +static irqreturn_t +acpi_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + return (*acpi_irq_handler)(acpi_irq_context) ? IRQ_HANDLED : IRQ_NONE; +} + +acpi_status +acpi_os_install_interrupt_handler(u32 gsi, acpi_osd_handler handler, void *context) +{ + unsigned int irq; + + /* + * Ignore the GSI from the core, and use the value in our copy of the + * FADT. It may not be the same if an interrupt source override exists + * for the SCI. + */ + gsi = acpi_fadt.sci_int; + if (acpi_gsi_to_irq(gsi, &irq) < 0) { + printk(KERN_ERR PREFIX "SCI (ACPI GSI %d) not registered\n", + gsi); + return AE_OK; + } + + acpi_irq_handler = handler; + acpi_irq_context = context; + if (request_irq(irq, acpi_irq, SA_SHIRQ, "acpi", acpi_irq)) { + printk(KERN_ERR PREFIX "SCI (IRQ%d) allocation failed\n", irq); + return AE_NOT_ACQUIRED; + } + acpi_irq_irq = irq; + + return AE_OK; +} + +acpi_status +acpi_os_remove_interrupt_handler(u32 irq, acpi_osd_handler handler) +{ + if (irq) { + free_irq(irq, acpi_irq); + acpi_irq_handler = NULL; + acpi_irq_irq = 0; + } + + return AE_OK; +} + +/* + * Running in interpreter thread context, safe to sleep + */ + +void +acpi_os_sleep(acpi_integer ms) +{ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(((signed long) ms * HZ) / 1000); +} +EXPORT_SYMBOL(acpi_os_sleep); + +void +acpi_os_stall(u32 us) +{ + while (us) { + u32 delay = 1000; + + if (delay > us) + delay = us; + udelay(delay); + touch_nmi_watchdog(); + us -= delay; + } +} +EXPORT_SYMBOL(acpi_os_stall); + +/* + * Support ACPI 3.0 AML Timer operand + * Returns 64-bit free-running, monotonically increasing timer + * with 100ns granularity + */ +u64 +acpi_os_get_timer (void) +{ + static u64 t; + +#ifdef CONFIG_HPET + /* TBD: use HPET if available */ +#endif + +#ifdef CONFIG_X86_PM_TIMER + /* TBD: default to PM timer if HPET was not available */ +#endif + if (!t) + printk(KERN_ERR PREFIX "acpi_os_get_timer() TBD\n"); + + return ++t; +} + +acpi_status +acpi_os_read_port( + acpi_io_address port, + u32 *value, + u32 width) +{ + u32 dummy; + + if (!value) + value = &dummy; + + switch (width) + { + case 8: + *(u8*) value = inb(port); + break; + case 16: + *(u16*) value = inw(port); + break; + case 32: + *(u32*) value = inl(port); + break; + default: + BUG(); + } + + return AE_OK; +} +EXPORT_SYMBOL(acpi_os_read_port); + +acpi_status +acpi_os_write_port( + acpi_io_address port, + u32 value, + u32 width) +{ + switch (width) + { + case 8: + outb(value, port); + break; + case 16: + outw(value, port); + break; + case 32: + outl(value, port); + break; + default: + BUG(); + } + + return AE_OK; +} +EXPORT_SYMBOL(acpi_os_write_port); + +acpi_status +acpi_os_read_memory( + acpi_physical_address phys_addr, + u32 *value, + u32 width) +{ + u32 dummy; + void __iomem *virt_addr; + int iomem = 0; + + if (efi_enabled) { + if (EFI_MEMORY_WB & efi_mem_attributes(phys_addr)) { + /* HACK ALERT! We can use readb/w/l on real memory too.. */ + virt_addr = (void __iomem *) phys_to_virt(phys_addr); + } else { + iomem = 1; + virt_addr = ioremap(phys_addr, width); + } + } else + virt_addr = (void __iomem *) phys_to_virt(phys_addr); + if (!value) + value = &dummy; + + switch (width) { + case 8: + *(u8*) value = readb(virt_addr); + break; + case 16: + *(u16*) value = readw(virt_addr); + break; + case 32: + *(u32*) value = readl(virt_addr); + break; + default: + BUG(); + } + + if (efi_enabled) { + if (iomem) + iounmap(virt_addr); + } + + return AE_OK; +} + +acpi_status +acpi_os_write_memory( + acpi_physical_address phys_addr, + u32 value, + u32 width) +{ + void __iomem *virt_addr; + int iomem = 0; + + if (efi_enabled) { + if (EFI_MEMORY_WB & efi_mem_attributes(phys_addr)) { + /* HACK ALERT! We can use writeb/w/l on real memory too */ + virt_addr = (void __iomem *) phys_to_virt(phys_addr); + } else { + iomem = 1; + virt_addr = ioremap(phys_addr, width); + } + } else + virt_addr = (void __iomem *) phys_to_virt(phys_addr); + + switch (width) { + case 8: + writeb(value, virt_addr); + break; + case 16: + writew(value, virt_addr); + break; + case 32: + writel(value, virt_addr); + break; + default: + BUG(); + } + + if (iomem) + iounmap(virt_addr); + + return AE_OK; +} + +#ifdef CONFIG_ACPI_PCI + +acpi_status +acpi_os_read_pci_configuration (struct acpi_pci_id *pci_id, u32 reg, void *value, u32 width) +{ + int result, size; + + if (!value) + return AE_BAD_PARAMETER; + + switch (width) { + case 8: + size = 1; + break; + case 16: + size = 2; + break; + case 32: + size = 4; + break; + default: + return AE_ERROR; + } + + BUG_ON(!raw_pci_ops); + + result = raw_pci_ops->read(pci_id->segment, pci_id->bus, + PCI_DEVFN(pci_id->device, pci_id->function), + reg, size, value); + + return (result ? AE_ERROR : AE_OK); +} +EXPORT_SYMBOL(acpi_os_read_pci_configuration); + +acpi_status +acpi_os_write_pci_configuration (struct acpi_pci_id *pci_id, u32 reg, acpi_integer value, u32 width) +{ + int result, size; + + switch (width) { + case 8: + size = 1; + break; + case 16: + size = 2; + break; + case 32: + size = 4; + break; + default: + return AE_ERROR; + } + + BUG_ON(!raw_pci_ops); + + result = raw_pci_ops->write(pci_id->segment, pci_id->bus, + PCI_DEVFN(pci_id->device, pci_id->function), + reg, size, value); + + return (result ? AE_ERROR : AE_OK); +} + +/* TODO: Change code to take advantage of driver model more */ +static void +acpi_os_derive_pci_id_2 ( + acpi_handle rhandle, /* upper bound */ + acpi_handle chandle, /* current node */ + struct acpi_pci_id **id, + int *is_bridge, + u8 *bus_number) +{ + acpi_handle handle; + struct acpi_pci_id *pci_id = *id; + acpi_status status; + unsigned long temp; + acpi_object_type type; + u8 tu8; + + acpi_get_parent(chandle, &handle); + if (handle != rhandle) { + acpi_os_derive_pci_id_2(rhandle, handle, &pci_id, is_bridge, bus_number); + + status = acpi_get_type(handle, &type); + if ( (ACPI_FAILURE(status)) || (type != ACPI_TYPE_DEVICE) ) + return; + + status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &temp); + if (ACPI_SUCCESS(status)) { + pci_id->device = ACPI_HIWORD (ACPI_LODWORD (temp)); + pci_id->function = ACPI_LOWORD (ACPI_LODWORD (temp)); + + if (*is_bridge) + pci_id->bus = *bus_number; + + /* any nicer way to get bus number of bridge ? */ + status = acpi_os_read_pci_configuration(pci_id, 0x0e, &tu8, 8); + if (ACPI_SUCCESS(status) && + ((tu8 & 0x7f) == 1 || (tu8 & 0x7f) == 2)) { + status = acpi_os_read_pci_configuration(pci_id, 0x18, &tu8, 8); + if (!ACPI_SUCCESS(status)) { + /* Certainly broken... FIX ME */ + return; + } + *is_bridge = 1; + pci_id->bus = tu8; + status = acpi_os_read_pci_configuration(pci_id, 0x19, &tu8, 8); + if (ACPI_SUCCESS(status)) { + *bus_number = tu8; + } + } else + *is_bridge = 0; + } + } +} + +void +acpi_os_derive_pci_id ( + acpi_handle rhandle, /* upper bound */ + acpi_handle chandle, /* current node */ + struct acpi_pci_id **id) +{ + int is_bridge = 1; + u8 bus_number = (*id)->bus; + + acpi_os_derive_pci_id_2(rhandle, chandle, id, &is_bridge, &bus_number); +} + +#else /*!CONFIG_ACPI_PCI*/ + +acpi_status +acpi_os_write_pci_configuration ( + struct acpi_pci_id *pci_id, + u32 reg, + acpi_integer value, + u32 width) +{ + return AE_SUPPORT; +} + +acpi_status +acpi_os_read_pci_configuration ( + struct acpi_pci_id *pci_id, + u32 reg, + void *value, + u32 width) +{ + return AE_SUPPORT; +} + +void +acpi_os_derive_pci_id ( + acpi_handle rhandle, /* upper bound */ + acpi_handle chandle, /* current node */ + struct acpi_pci_id **id) +{ +} + +#endif /*CONFIG_ACPI_PCI*/ + +static void +acpi_os_execute_deferred ( + void *context) +{ + struct acpi_os_dpc *dpc = NULL; + + ACPI_FUNCTION_TRACE ("os_execute_deferred"); + + dpc = (struct acpi_os_dpc *) context; + if (!dpc) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid (NULL) context.\n")); + return_VOID; + } + + dpc->function(dpc->context); + + kfree(dpc); + + return_VOID; +} + +acpi_status +acpi_os_queue_for_execution( + u32 priority, + acpi_osd_exec_callback function, + void *context) +{ + acpi_status status = AE_OK; + struct acpi_os_dpc *dpc; + struct work_struct *task; + + ACPI_FUNCTION_TRACE ("os_queue_for_execution"); + + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Scheduling function [%p(%p)] for deferred execution.\n", function, context)); + + if (!function) + return_ACPI_STATUS (AE_BAD_PARAMETER); + + /* + * Allocate/initialize DPC structure. Note that this memory will be + * freed by the callee. The kernel handles the tq_struct list in a + * way that allows us to also free its memory inside the callee. + * Because we may want to schedule several tasks with different + * parameters we can't use the approach some kernel code uses of + * having a static tq_struct. + * We can save time and code by allocating the DPC and tq_structs + * from the same memory. + */ + + dpc = kmalloc(sizeof(struct acpi_os_dpc)+sizeof(struct work_struct), GFP_ATOMIC); + if (!dpc) + return_ACPI_STATUS (AE_NO_MEMORY); + + dpc->function = function; + dpc->context = context; + + task = (void *)(dpc+1); + INIT_WORK(task, acpi_os_execute_deferred, (void*)dpc); + + if (!queue_work(kacpid_wq, task)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Call to queue_work() failed.\n")); + kfree(dpc); + status = AE_ERROR; + } + + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_os_queue_for_execution); + +void +acpi_os_wait_events_complete( + void *context) +{ + flush_workqueue(kacpid_wq); +} +EXPORT_SYMBOL(acpi_os_wait_events_complete); + +/* + * Allocate the memory for a spinlock and initialize it. + */ +acpi_status +acpi_os_create_lock ( + acpi_handle *out_handle) +{ + spinlock_t *lock_ptr; + + ACPI_FUNCTION_TRACE ("os_create_lock"); + + lock_ptr = acpi_os_allocate(sizeof(spinlock_t)); + + spin_lock_init(lock_ptr); + + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Creating spinlock[%p].\n", lock_ptr)); + + *out_handle = lock_ptr; + + return_ACPI_STATUS (AE_OK); +} + + +/* + * Deallocate the memory for a spinlock. + */ +void +acpi_os_delete_lock ( + acpi_handle handle) +{ + ACPI_FUNCTION_TRACE ("os_create_lock"); + + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Deleting spinlock[%p].\n", handle)); + + acpi_os_free(handle); + + return_VOID; +} + +/* + * Acquire a spinlock. + * + * handle is a pointer to the spinlock_t. + * flags is *not* the result of save_flags - it is an ACPI-specific flag variable + * that indicates whether we are at interrupt level. + */ +void +acpi_os_acquire_lock ( + acpi_handle handle, + u32 flags) +{ + ACPI_FUNCTION_TRACE ("os_acquire_lock"); + + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Acquiring spinlock[%p] from %s level\n", handle, + ((flags & ACPI_NOT_ISR) ? "non-interrupt" : "interrupt"))); + + if (flags & ACPI_NOT_ISR) + ACPI_DISABLE_IRQS(); + + spin_lock((spinlock_t *)handle); + + return_VOID; +} + + +/* + * Release a spinlock. See above. + */ +void +acpi_os_release_lock ( + acpi_handle handle, + u32 flags) +{ + ACPI_FUNCTION_TRACE ("os_release_lock"); + + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Releasing spinlock[%p] from %s level\n", handle, + ((flags & ACPI_NOT_ISR) ? "non-interrupt" : "interrupt"))); + + spin_unlock((spinlock_t *)handle); + + if (flags & ACPI_NOT_ISR) + ACPI_ENABLE_IRQS(); + + return_VOID; +} + + +acpi_status +acpi_os_create_semaphore( + u32 max_units, + u32 initial_units, + acpi_handle *handle) +{ + struct semaphore *sem = NULL; + + ACPI_FUNCTION_TRACE ("os_create_semaphore"); + + sem = acpi_os_allocate(sizeof(struct semaphore)); + if (!sem) + return_ACPI_STATUS (AE_NO_MEMORY); + memset(sem, 0, sizeof(struct semaphore)); + + sema_init(sem, initial_units); + + *handle = (acpi_handle*)sem; + + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Creating semaphore[%p|%d].\n", *handle, initial_units)); + + return_ACPI_STATUS (AE_OK); +} +EXPORT_SYMBOL(acpi_os_create_semaphore); + + +/* + * TODO: A better way to delete semaphores? Linux doesn't have a + * 'delete_semaphore()' function -- may result in an invalid + * pointer dereference for non-synchronized consumers. Should + * we at least check for blocked threads and signal/cancel them? + */ + +acpi_status +acpi_os_delete_semaphore( + acpi_handle handle) +{ + struct semaphore *sem = (struct semaphore*) handle; + + ACPI_FUNCTION_TRACE ("os_delete_semaphore"); + + if (!sem) + return_ACPI_STATUS (AE_BAD_PARAMETER); + + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Deleting semaphore[%p].\n", handle)); + + acpi_os_free(sem); sem = NULL; + + return_ACPI_STATUS (AE_OK); +} +EXPORT_SYMBOL(acpi_os_delete_semaphore); + + +/* + * TODO: The kernel doesn't have a 'down_timeout' function -- had to + * improvise. The process is to sleep for one scheduler quantum + * until the semaphore becomes available. Downside is that this + * may result in starvation for timeout-based waits when there's + * lots of semaphore activity. + * + * TODO: Support for units > 1? + */ +acpi_status +acpi_os_wait_semaphore( + acpi_handle handle, + u32 units, + u16 timeout) +{ + acpi_status status = AE_OK; + struct semaphore *sem = (struct semaphore*)handle; + int ret = 0; + + ACPI_FUNCTION_TRACE ("os_wait_semaphore"); + + if (!sem || (units < 1)) + return_ACPI_STATUS (AE_BAD_PARAMETER); + + if (units > 1) + return_ACPI_STATUS (AE_SUPPORT); + + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Waiting for semaphore[%p|%d|%d]\n", handle, units, timeout)); + + if (in_atomic()) + timeout = 0; + + switch (timeout) + { + /* + * No Wait: + * -------- + * A zero timeout value indicates that we shouldn't wait - just + * acquire the semaphore if available otherwise return AE_TIME + * (a.k.a. 'would block'). + */ + case 0: + if(down_trylock(sem)) + status = AE_TIME; + break; + + /* + * Wait Indefinitely: + * ------------------ + */ + case ACPI_WAIT_FOREVER: + down(sem); + break; + + /* + * Wait w/ Timeout: + * ---------------- + */ + default: + // TODO: A better timeout algorithm? + { + int i = 0; + static const int quantum_ms = 1000/HZ; + + ret = down_trylock(sem); + for (i = timeout; (i > 0 && ret < 0); i -= quantum_ms) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + ret = down_trylock(sem); + } + + if (ret != 0) + status = AE_TIME; + } + break; + } + + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Failed to acquire semaphore[%p|%d|%d], %s\n", + handle, units, timeout, acpi_format_exception(status))); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Acquired semaphore[%p|%d|%d]\n", handle, units, timeout)); + } + + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_os_wait_semaphore); + + +/* + * TODO: Support for units > 1? + */ +acpi_status +acpi_os_signal_semaphore( + acpi_handle handle, + u32 units) +{ + struct semaphore *sem = (struct semaphore *) handle; + + ACPI_FUNCTION_TRACE ("os_signal_semaphore"); + + if (!sem || (units < 1)) + return_ACPI_STATUS (AE_BAD_PARAMETER); + + if (units > 1) + return_ACPI_STATUS (AE_SUPPORT); + + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Signaling semaphore[%p|%d]\n", handle, units)); + + up(sem); + + return_ACPI_STATUS (AE_OK); +} +EXPORT_SYMBOL(acpi_os_signal_semaphore); + +#ifdef ACPI_FUTURE_USAGE +u32 +acpi_os_get_line(char *buffer) +{ + +#ifdef ENABLE_DEBUGGER + if (acpi_in_debugger) { + u32 chars; + + kdb_read(buffer, sizeof(line_buf)); + + /* remove the CR kdb includes */ + chars = strlen(buffer) - 1; + buffer[chars] = '\0'; + } +#endif + + return 0; +} +#endif /* ACPI_FUTURE_USAGE */ + +/* Assumes no unreadable holes inbetween */ +u8 +acpi_os_readable(void *ptr, acpi_size len) +{ +#if defined(__i386__) || defined(__x86_64__) + char tmp; + return !__get_user(tmp, (char __user *)ptr) && !__get_user(tmp, (char __user *)ptr + len - 1); +#endif + return 1; +} + +#ifdef ACPI_FUTURE_USAGE +u8 +acpi_os_writable(void *ptr, acpi_size len) +{ + /* could do dummy write (racy) or a kernel page table lookup. + The later may be difficult at early boot when kmap doesn't work yet. */ + return 1; +} +#endif + +u32 +acpi_os_get_thread_id (void) +{ + if (!in_atomic()) + return current->pid; + + return 0; +} + +acpi_status +acpi_os_signal ( + u32 function, + void *info) +{ + switch (function) + { + case ACPI_SIGNAL_FATAL: + printk(KERN_ERR PREFIX "Fatal opcode executed\n"); + break; + case ACPI_SIGNAL_BREAKPOINT: + /* + * AML Breakpoint + * ACPI spec. says to treat it as a NOP unless + * you are debugging. So if/when we integrate + * AML debugger into the kernel debugger its + * hook will go here. But until then it is + * not useful to print anything on breakpoints. + */ + break; + default: + break; + } + + return AE_OK; +} +EXPORT_SYMBOL(acpi_os_signal); + +static int __init +acpi_os_name_setup(char *str) +{ + char *p = acpi_os_name; + int count = ACPI_MAX_OVERRIDE_LEN-1; + + if (!str || !*str) + return 0; + + for (; count-- && str && *str; str++) { + if (isalnum(*str) || *str == ' ' || *str == ':') + *p++ = *str; + else if (*str == '\'' || *str == '"') + continue; + else + break; + } + *p = 0; + + return 1; + +} + +__setup("acpi_os_name=", acpi_os_name_setup); + +/* + * _OSI control + * empty string disables _OSI + * TBD additional string adds to _OSI + */ +static int __init +acpi_osi_setup(char *str) +{ + if (str == NULL || *str == '\0') { + printk(KERN_INFO PREFIX "_OSI method disabled\n"); + acpi_gbl_create_osi_method = FALSE; + } else + { + /* TBD */ + printk(KERN_ERR PREFIX "_OSI additional string ignored -- %s\n", str); + } + + return 1; +} + +__setup("acpi_osi=", acpi_osi_setup); + +/* enable serialization to combat AE_ALREADY_EXISTS errors */ +static int __init +acpi_serialize_setup(char *str) +{ + printk(KERN_INFO PREFIX "serialize enabled\n"); + + acpi_gbl_all_methods_serialized = TRUE; + + return 1; +} + +__setup("acpi_serialize", acpi_serialize_setup); + +/* + * Wake and Run-Time GPES are expected to be separate. + * We disable wake-GPEs at run-time to prevent spurious + * interrupts. + * + * However, if a system exists that shares Wake and + * Run-time events on the same GPE this flag is available + * to tell Linux to keep the wake-time GPEs enabled at run-time. + */ +static int __init +acpi_wake_gpes_always_on_setup(char *str) +{ + printk(KERN_INFO PREFIX "wake GPEs not disabled\n"); + + acpi_gbl_leave_wake_gpes_disabled = FALSE; + + return 1; +} + +__setup("acpi_wake_gpes_always_on", acpi_wake_gpes_always_on_setup); + +/* + * max_cstate is defined in the base kernel so modules can + * change it w/o depending on the state of the processor module. + */ +unsigned int max_cstate = ACPI_PROCESSOR_MAX_POWER; + + +EXPORT_SYMBOL(max_cstate); diff --git a/drivers/acpi/parser/Makefile b/drivers/acpi/parser/Makefile new file mode 100644 index 00000000000..bbdd286c660 --- /dev/null +++ b/drivers/acpi/parser/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for all Linux ACPI interpreter subdirectories +# + +obj-y := psargs.o psparse.o pstree.o pswalk.o \ + psopcode.o psscope.o psutils.o psxface.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/parser/psargs.c b/drivers/acpi/parser/psargs.c new file mode 100644 index 00000000000..b5d98895f6a --- /dev/null +++ b/drivers/acpi/parser/psargs.c @@ -0,0 +1,746 @@ +/****************************************************************************** + * + * Module Name: psargs - Parse AML opcode arguments + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_PARSER + ACPI_MODULE_NAME ("psargs") + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_package_length + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Decoded package length. On completion, the AML pointer points + * past the length byte or bytes. + * + * DESCRIPTION: Decode and return a package length field + * + ******************************************************************************/ + +u32 +acpi_ps_get_next_package_length ( + struct acpi_parse_state *parser_state) +{ + u32 encoded_length; + u32 length = 0; + + + ACPI_FUNCTION_TRACE ("ps_get_next_package_length"); + + + encoded_length = (u32) ACPI_GET8 (parser_state->aml); + parser_state->aml++; + + + switch (encoded_length >> 6) /* bits 6-7 contain encoding scheme */ { + case 0: /* 1-byte encoding (bits 0-5) */ + + length = (encoded_length & 0x3F); + break; + + + case 1: /* 2-byte encoding (next byte + bits 0-3) */ + + length = ((ACPI_GET8 (parser_state->aml) << 04) | + (encoded_length & 0x0F)); + parser_state->aml++; + break; + + + case 2: /* 3-byte encoding (next 2 bytes + bits 0-3) */ + + length = ((ACPI_GET8 (parser_state->aml + 1) << 12) | + (ACPI_GET8 (parser_state->aml) << 04) | + (encoded_length & 0x0F)); + parser_state->aml += 2; + break; + + + case 3: /* 4-byte encoding (next 3 bytes + bits 0-3) */ + + length = ((ACPI_GET8 (parser_state->aml + 2) << 20) | + (ACPI_GET8 (parser_state->aml + 1) << 12) | + (ACPI_GET8 (parser_state->aml) << 04) | + (encoded_length & 0x0F)); + parser_state->aml += 3; + break; + + default: + + /* Can't get here, only 2 bits / 4 cases */ + break; + } + + return_VALUE (length); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_package_end + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Pointer to end-of-package +1 + * + * DESCRIPTION: Get next package length and return a pointer past the end of + * the package. Consumes the package length field + * + ******************************************************************************/ + +u8 * +acpi_ps_get_next_package_end ( + struct acpi_parse_state *parser_state) +{ + u8 *start = parser_state->aml; + acpi_native_uint length; + + + ACPI_FUNCTION_TRACE ("ps_get_next_package_end"); + + + /* Function below changes parser_state->Aml */ + + length = (acpi_native_uint) acpi_ps_get_next_package_length (parser_state); + + return_PTR (start + length); /* end of package */ +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_namestring + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Pointer to the start of the name string (pointer points into + * the AML. + * + * DESCRIPTION: Get next raw namestring within the AML stream. Handles all name + * prefix characters. Set parser state to point past the string. + * (Name is consumed from the AML.) + * + ******************************************************************************/ + +char * +acpi_ps_get_next_namestring ( + struct acpi_parse_state *parser_state) +{ + u8 *start = parser_state->aml; + u8 *end = parser_state->aml; + + + ACPI_FUNCTION_TRACE ("ps_get_next_namestring"); + + + /* Handle multiple prefix characters */ + + while (acpi_ps_is_prefix_char (ACPI_GET8 (end))) { + /* Include prefix '\\' or '^' */ + + end++; + } + + /* Decode the path */ + + switch (ACPI_GET8 (end)) { + case 0: + + /* null_name */ + + if (end == start) { + start = NULL; + } + end++; + break; + + case AML_DUAL_NAME_PREFIX: + + /* Two name segments */ + + end += 1 + (2 * ACPI_NAME_SIZE); + break; + + case AML_MULTI_NAME_PREFIX_OP: + + /* Multiple name segments, 4 chars each */ + + end += 2 + ((acpi_size) ACPI_GET8 (end + 1) * ACPI_NAME_SIZE); + break; + + default: + + /* Single name segment */ + + end += ACPI_NAME_SIZE; + break; + } + + parser_state->aml = (u8*) end; + return_PTR ((char *) start); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_namepath + * + * PARAMETERS: parser_state - Current parser state object + * Arg - Where the namepath will be stored + * arg_count - If the namepath points to a control method + * the method's argument is returned here. + * method_call - Whether the namepath can possibly be the + * start of a method call + * + * RETURN: Status + * + * DESCRIPTION: Get next name (if method call, return # of required args). + * Names are looked up in the internal namespace to determine + * if the name represents a control method. If a method + * is found, the number of arguments to the method is returned. + * This information is critical for parsing to continue correctly. + * + ******************************************************************************/ + +acpi_status +acpi_ps_get_next_namepath ( + struct acpi_walk_state *walk_state, + struct acpi_parse_state *parser_state, + union acpi_parse_object *arg, + u8 method_call) +{ + char *path; + union acpi_parse_object *name_op; + acpi_status status = AE_OK; + union acpi_operand_object *method_desc; + struct acpi_namespace_node *node; + union acpi_generic_state scope_info; + + + ACPI_FUNCTION_TRACE ("ps_get_next_namepath"); + + + path = acpi_ps_get_next_namestring (parser_state); + + /* Null path case is allowed */ + + if (path) { + /* + * Lookup the name in the internal namespace + */ + scope_info.scope.node = NULL; + node = parser_state->start_node; + if (node) { + scope_info.scope.node = node; + } + + /* + * Lookup object. We don't want to add anything new to the namespace + * here, however. So we use MODE_EXECUTE. Allow searching of the + * parent tree, but don't open a new scope -- we just want to lookup the + * object (MUST BE mode EXECUTE to perform upsearch) + */ + status = acpi_ns_lookup (&scope_info, path, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, NULL, &node); + if (ACPI_SUCCESS (status) && method_call) { + if (node->type == ACPI_TYPE_METHOD) { + /* + * This name is actually a control method invocation + */ + method_desc = acpi_ns_get_attached_object (node); + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, + "Control Method - %p Desc %p Path=%p\n", + node, method_desc, path)); + + name_op = acpi_ps_alloc_op (AML_INT_NAMEPATH_OP); + if (!name_op) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Change arg into a METHOD CALL and attach name to it */ + + acpi_ps_init_op (arg, AML_INT_METHODCALL_OP); + name_op->common.value.name = path; + + /* Point METHODCALL/NAME to the METHOD Node */ + + name_op->common.node = node; + acpi_ps_append_arg (arg, name_op); + + if (!method_desc) { + ACPI_REPORT_ERROR (( + "ps_get_next_namepath: Control Method %p has no attached object\n", + node)); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, + "Control Method - %p Args %X\n", + node, method_desc->method.param_count)); + + /* Get the number of arguments to expect */ + + walk_state->arg_count = method_desc->method.param_count; + return_ACPI_STATUS (AE_OK); + } + + /* + * Else this is normal named object reference. + * Just init the NAMEPATH object with the pathname. + * (See code below) + */ + } + + if (ACPI_FAILURE (status)) { + /* + * 1) Any error other than NOT_FOUND is always severe + * 2) NOT_FOUND is only important if we are executing a method. + * 3) If executing a cond_ref_of opcode, NOT_FOUND is ok. + */ + if ((((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == ACPI_PARSE_EXECUTE) && + (status == AE_NOT_FOUND) && + (walk_state->op->common.aml_opcode != AML_COND_REF_OF_OP)) || + + (status != AE_NOT_FOUND)) { + ACPI_REPORT_NSERROR (path, status); + + acpi_os_printf ("search_node %p start_node %p return_node %p\n", + scope_info.scope.node, parser_state->start_node, node); + + + } + else { + /* + * We got a NOT_FOUND during table load or we encountered + * a cond_ref_of(x) where the target does not exist. + * -- either case is ok + */ + status = AE_OK; + } + } + } + + /* + * Regardless of success/failure above, + * Just initialize the Op with the pathname. + */ + acpi_ps_init_op (arg, AML_INT_NAMEPATH_OP); + arg->common.value.name = path; + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_simple_arg + * + * PARAMETERS: parser_state - Current parser state object + * arg_type - The argument type (AML_*_ARG) + * Arg - Where the argument is returned + * + * RETURN: None + * + * DESCRIPTION: Get the next simple argument (constant, string, or namestring) + * + ******************************************************************************/ + +void +acpi_ps_get_next_simple_arg ( + struct acpi_parse_state *parser_state, + u32 arg_type, + union acpi_parse_object *arg) +{ + + ACPI_FUNCTION_TRACE_U32 ("ps_get_next_simple_arg", arg_type); + + + switch (arg_type) { + case ARGP_BYTEDATA: + + acpi_ps_init_op (arg, AML_BYTE_OP); + arg->common.value.integer = (u32) ACPI_GET8 (parser_state->aml); + parser_state->aml++; + break; + + + case ARGP_WORDDATA: + + acpi_ps_init_op (arg, AML_WORD_OP); + + /* Get 2 bytes from the AML stream */ + + ACPI_MOVE_16_TO_32 (&arg->common.value.integer, parser_state->aml); + parser_state->aml += 2; + break; + + + case ARGP_DWORDDATA: + + acpi_ps_init_op (arg, AML_DWORD_OP); + + /* Get 4 bytes from the AML stream */ + + ACPI_MOVE_32_TO_32 (&arg->common.value.integer, parser_state->aml); + parser_state->aml += 4; + break; + + + case ARGP_QWORDDATA: + + acpi_ps_init_op (arg, AML_QWORD_OP); + + /* Get 8 bytes from the AML stream */ + + ACPI_MOVE_64_TO_64 (&arg->common.value.integer, parser_state->aml); + parser_state->aml += 8; + break; + + + case ARGP_CHARLIST: + + acpi_ps_init_op (arg, AML_STRING_OP); + arg->common.value.string = (char *) parser_state->aml; + + while (ACPI_GET8 (parser_state->aml) != '\0') { + parser_state->aml++; + } + parser_state->aml++; + break; + + + case ARGP_NAME: + case ARGP_NAMESTRING: + + acpi_ps_init_op (arg, AML_INT_NAMEPATH_OP); + arg->common.value.name = acpi_ps_get_next_namestring (parser_state); + break; + + + default: + + ACPI_REPORT_ERROR (("Invalid arg_type %X\n", arg_type)); + break; + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_field + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: A newly allocated FIELD op + * + * DESCRIPTION: Get next field (named_field, reserved_field, or access_field) + * + ******************************************************************************/ + +union acpi_parse_object * +acpi_ps_get_next_field ( + struct acpi_parse_state *parser_state) +{ + u32 aml_offset = (u32) ACPI_PTR_DIFF (parser_state->aml, + parser_state->aml_start); + union acpi_parse_object *field; + u16 opcode; + u32 name; + + + ACPI_FUNCTION_TRACE ("ps_get_next_field"); + + + /* determine field type */ + + switch (ACPI_GET8 (parser_state->aml)) { + default: + + opcode = AML_INT_NAMEDFIELD_OP; + break; + + case 0x00: + + opcode = AML_INT_RESERVEDFIELD_OP; + parser_state->aml++; + break; + + case 0x01: + + opcode = AML_INT_ACCESSFIELD_OP; + parser_state->aml++; + break; + } + + + /* Allocate a new field op */ + + field = acpi_ps_alloc_op (opcode); + if (!field) { + return_PTR (NULL); + } + + field->common.aml_offset = aml_offset; + + /* Decode the field type */ + + switch (opcode) { + case AML_INT_NAMEDFIELD_OP: + + /* Get the 4-character name */ + + ACPI_MOVE_32_TO_32 (&name, parser_state->aml); + acpi_ps_set_name (field, name); + parser_state->aml += ACPI_NAME_SIZE; + + /* Get the length which is encoded as a package length */ + + field->common.value.size = acpi_ps_get_next_package_length (parser_state); + break; + + + case AML_INT_RESERVEDFIELD_OP: + + /* Get the length which is encoded as a package length */ + + field->common.value.size = acpi_ps_get_next_package_length (parser_state); + break; + + + case AML_INT_ACCESSFIELD_OP: + + /* + * Get access_type and access_attrib and merge into the field Op + * access_type is first operand, access_attribute is second + */ + field->common.value.integer = (ACPI_GET8 (parser_state->aml) << 8); + parser_state->aml++; + field->common.value.integer |= ACPI_GET8 (parser_state->aml); + parser_state->aml++; + break; + + default: + + /* Opcode was set in previous switch */ + break; + } + + return_PTR (field); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_next_arg + * + * PARAMETERS: parser_state - Current parser state object + * arg_type - The argument type (AML_*_ARG) + * arg_count - If the argument points to a control method + * the method's argument is returned here. + * + * RETURN: Status, and an op object containing the next argument. + * + * DESCRIPTION: Get next argument (including complex list arguments that require + * pushing the parser stack) + * + ******************************************************************************/ + +acpi_status +acpi_ps_get_next_arg ( + struct acpi_walk_state *walk_state, + struct acpi_parse_state *parser_state, + u32 arg_type, + union acpi_parse_object **return_arg) +{ + union acpi_parse_object *arg = NULL; + union acpi_parse_object *prev = NULL; + union acpi_parse_object *field; + u32 subop; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_PTR ("ps_get_next_arg", parser_state); + + + switch (arg_type) { + case ARGP_BYTEDATA: + case ARGP_WORDDATA: + case ARGP_DWORDDATA: + case ARGP_CHARLIST: + case ARGP_NAME: + case ARGP_NAMESTRING: + + /* constants, strings, and namestrings are all the same size */ + + arg = acpi_ps_alloc_op (AML_BYTE_OP); + if (!arg) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + acpi_ps_get_next_simple_arg (parser_state, arg_type, arg); + break; + + + case ARGP_PKGLENGTH: + + /* Package length, nothing returned */ + + parser_state->pkg_end = acpi_ps_get_next_package_end (parser_state); + break; + + + case ARGP_FIELDLIST: + + if (parser_state->aml < parser_state->pkg_end) { + /* Non-empty list */ + + while (parser_state->aml < parser_state->pkg_end) { + field = acpi_ps_get_next_field (parser_state); + if (!field) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + if (prev) { + prev->common.next = field; + } + else { + arg = field; + } + + prev = field; + } + + /* Skip to End of byte data */ + + parser_state->aml = parser_state->pkg_end; + } + break; + + + case ARGP_BYTELIST: + + if (parser_state->aml < parser_state->pkg_end) { + /* Non-empty list */ + + arg = acpi_ps_alloc_op (AML_INT_BYTELIST_OP); + if (!arg) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Fill in bytelist data */ + + arg->common.value.size = (u32) ACPI_PTR_DIFF (parser_state->pkg_end, + parser_state->aml); + arg->named.data = parser_state->aml; + + /* Skip to End of byte data */ + + parser_state->aml = parser_state->pkg_end; + } + break; + + + case ARGP_TARGET: + case ARGP_SUPERNAME: + case ARGP_SIMPLENAME: + + subop = acpi_ps_peek_opcode (parser_state); + if (subop == 0 || + acpi_ps_is_leading_char (subop) || + acpi_ps_is_prefix_char (subop)) { + /* null_name or name_string */ + + arg = acpi_ps_alloc_op (AML_INT_NAMEPATH_OP); + if (!arg) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + status = acpi_ps_get_next_namepath (walk_state, parser_state, arg, 0); + } + else { + /* single complex argument, nothing returned */ + + walk_state->arg_count = 1; + } + break; + + + case ARGP_DATAOBJ: + case ARGP_TERMARG: + + /* single complex argument, nothing returned */ + + walk_state->arg_count = 1; + break; + + + case ARGP_DATAOBJLIST: + case ARGP_TERMLIST: + case ARGP_OBJLIST: + + if (parser_state->aml < parser_state->pkg_end) { + /* non-empty list of variable arguments, nothing returned */ + + walk_state->arg_count = ACPI_VAR_ARGS; + } + break; + + + default: + + ACPI_REPORT_ERROR (("Invalid arg_type: %X\n", arg_type)); + status = AE_AML_OPERAND_TYPE; + break; + } + + *return_arg = arg; + return_ACPI_STATUS (status); +} diff --git a/drivers/acpi/parser/psopcode.c b/drivers/acpi/parser/psopcode.c new file mode 100644 index 00000000000..03e33fedc11 --- /dev/null +++ b/drivers/acpi/parser/psopcode.c @@ -0,0 +1,778 @@ +/****************************************************************************** + * + * Module Name: psopcode - Parser/Interpreter opcode information table + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_PARSER + ACPI_MODULE_NAME ("psopcode") + + +#define _UNK 0x6B +/* + * Reserved ASCII characters. Do not use any of these for + * internal opcodes, since they are used to differentiate + * name strings from AML opcodes + */ +#define _ASC 0x6C +#define _NAM 0x6C +#define _PFX 0x6D +#define _UNKNOWN_OPCODE 0x02 /* An example unknown opcode */ + +#define MAX_EXTENDED_OPCODE 0x88 +#define NUM_EXTENDED_OPCODE (MAX_EXTENDED_OPCODE + 1) +#define MAX_INTERNAL_OPCODE +#define NUM_INTERNAL_OPCODE (MAX_INTERNAL_OPCODE + 1) + + +/******************************************************************************* + * + * NAME: acpi_gbl_aml_op_info + * + * DESCRIPTION: Opcode table. Each entry contains <opcode, type, name, operands> + * The name is a simple ascii string, the operand specifier is an + * ascii string with one letter per operand. The letter specifies + * the operand type. + * + ******************************************************************************/ + + +/* + * All AML opcodes and the parse-time arguments for each. Used by the AML parser Each list is compressed + * into a 32-bit number and stored in the master opcode table at the end of this file. + */ + + +#define ARGP_ACCESSFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_ACQUIRE_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_WORDDATA) +#define ARGP_ADD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_ALIAS_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_NAME) +#define ARGP_ARG0 ARG_NONE +#define ARGP_ARG1 ARG_NONE +#define ARGP_ARG2 ARG_NONE +#define ARGP_ARG3 ARG_NONE +#define ARGP_ARG4 ARG_NONE +#define ARGP_ARG5 ARG_NONE +#define ARGP_ARG6 ARG_NONE +#define ARGP_BANK_FIELD_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_TERMARG, ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_BIT_AND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NAND_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_NOT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_OR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BIT_XOR_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_BREAK_OP ARG_NONE +#define ARGP_BREAK_POINT_OP ARG_NONE +#define ARGP_BUFFER_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_BYTELIST) +#define ARGP_BYTE_OP ARGP_LIST1 (ARGP_BYTEDATA) +#define ARGP_BYTELIST_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_CONCAT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_CONCAT_RES_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_COND_REF_OF_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SUPERNAME) +#define ARGP_CONTINUE_OP ARG_NONE +#define ARGP_COPY_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_SIMPLENAME) +#define ARGP_CREATE_BIT_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_BYTE_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_DWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_FIELD_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_QWORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_CREATE_WORD_FIELD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_NAME) +#define ARGP_DATA_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_DEBUG_OP ARG_NONE +#define ARGP_DECREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_DEREF_OF_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_DEVICE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST) +#define ARGP_DIVIDE_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET, ARGP_TARGET) +#define ARGP_DWORD_OP ARGP_LIST1 (ARGP_DWORDDATA) +#define ARGP_ELSE_OP ARGP_LIST2 (ARGP_PKGLENGTH, ARGP_TERMLIST) +#define ARGP_EVENT_OP ARGP_LIST1 (ARGP_NAME) +#define ARGP_FATAL_OP ARGP_LIST3 (ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_TERMARG) +#define ARGP_FIELD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_FIND_SET_LEFT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_FIND_SET_RIGHT_BIT_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_FROM_BCD_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_IF_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST) +#define ARGP_INCREMENT_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_INDEX_FIELD_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAMESTRING, ARGP_NAMESTRING,ARGP_BYTEDATA, ARGP_FIELDLIST) +#define ARGP_INDEX_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_LAND_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LGREATER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LGREATEREQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LLESS_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LLESSEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LNOT_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_LNOTEQUAL_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LOAD_OP ARGP_LIST2 (ARGP_NAMESTRING, ARGP_SUPERNAME) +#define ARGP_LOAD_TABLE_OP ARGP_LIST6 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_LOCAL0 ARG_NONE +#define ARGP_LOCAL1 ARG_NONE +#define ARGP_LOCAL2 ARG_NONE +#define ARGP_LOCAL3 ARG_NONE +#define ARGP_LOCAL4 ARG_NONE +#define ARGP_LOCAL5 ARG_NONE +#define ARGP_LOCAL6 ARG_NONE +#define ARGP_LOCAL7 ARG_NONE +#define ARGP_LOR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_MATCH_OP ARGP_LIST6 (ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_METHOD_OP ARGP_LIST4 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMLIST) +#define ARGP_METHODCALL_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_MID_OP ARGP_LIST4 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MOD_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MULTIPLY_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_MUTEX_OP ARGP_LIST2 (ARGP_NAME, ARGP_BYTEDATA) +#define ARGP_NAME_OP ARGP_LIST2 (ARGP_NAME, ARGP_DATAOBJ) +#define ARGP_NAMEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_NAMEPATH_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_NOOP_OP ARG_NONE +#define ARGP_NOTIFY_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG) +#define ARGP_ONE_OP ARG_NONE +#define ARGP_ONES_OP ARG_NONE +#define ARGP_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_BYTEDATA, ARGP_DATAOBJLIST) +#define ARGP_POWER_RES_OP ARGP_LIST5 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_WORDDATA, ARGP_OBJLIST) +#define ARGP_PROCESSOR_OP ARGP_LIST6 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_BYTEDATA, ARGP_DWORDDATA, ARGP_BYTEDATA, ARGP_OBJLIST) +#define ARGP_QWORD_OP ARGP_LIST1 (ARGP_QWORDDATA) +#define ARGP_REF_OF_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_REGION_OP ARGP_LIST4 (ARGP_NAME, ARGP_BYTEDATA, ARGP_TERMARG, ARGP_TERMARG) +#define ARGP_RELEASE_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_RESERVEDFIELD_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_RESET_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_RETURN_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_REVISION_OP ARG_NONE +#define ARGP_SCOPE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_TERMLIST) +#define ARGP_SHIFT_LEFT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_SHIFT_RIGHT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_SIGNAL_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_SIZE_OF_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_SLEEP_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_STALL_OP ARGP_LIST1 (ARGP_TERMARG) +#define ARGP_STATICSTRING_OP ARGP_LIST1 (ARGP_NAMESTRING) +#define ARGP_STORE_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_SUPERNAME) +#define ARGP_STRING_OP ARGP_LIST1 (ARGP_CHARLIST) +#define ARGP_SUBTRACT_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_THERMAL_ZONE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_NAME, ARGP_OBJLIST) +#define ARGP_TIMER_OP ARG_NONE +#define ARGP_TO_BCD_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_BUFFER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_DEC_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_HEX_STR_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_INTEGER_OP ARGP_LIST2 (ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TO_STRING_OP ARGP_LIST3 (ARGP_TERMARG, ARGP_TERMARG, ARGP_TARGET) +#define ARGP_TYPE_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_UNLOAD_OP ARGP_LIST1 (ARGP_SUPERNAME) +#define ARGP_VAR_PACKAGE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_DATAOBJLIST) +#define ARGP_WAIT_OP ARGP_LIST2 (ARGP_SUPERNAME, ARGP_TERMARG) +#define ARGP_WHILE_OP ARGP_LIST3 (ARGP_PKGLENGTH, ARGP_TERMARG, ARGP_TERMLIST) +#define ARGP_WORD_OP ARGP_LIST1 (ARGP_WORDDATA) +#define ARGP_ZERO_OP ARG_NONE + + +/* + * All AML opcodes and the runtime arguments for each. Used by the AML interpreter Each list is compressed + * into a 32-bit number and stored in the master opcode table at the end of this file. + * + * (Used by prep_operands procedure and the ASL Compiler) + */ + + +#define ARGI_ACCESSFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_ACQUIRE_OP ARGI_LIST2 (ARGI_MUTEX, ARGI_INTEGER) +#define ARGI_ADD_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_ALIAS_OP ARGI_INVALID_OPCODE +#define ARGI_ARG0 ARG_NONE +#define ARGI_ARG1 ARG_NONE +#define ARGI_ARG2 ARG_NONE +#define ARGI_ARG3 ARG_NONE +#define ARGI_ARG4 ARG_NONE +#define ARGI_ARG5 ARG_NONE +#define ARGI_ARG6 ARG_NONE +#define ARGI_BANK_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_BIT_AND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_NAND_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_NOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_NOT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_OR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BIT_XOR_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_BREAK_OP ARG_NONE +#define ARGI_BREAK_POINT_OP ARG_NONE +#define ARGI_BUFFER_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_BYTE_OP ARGI_INVALID_OPCODE +#define ARGI_BYTELIST_OP ARGI_INVALID_OPCODE +#define ARGI_CONCAT_OP ARGI_LIST3 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA, ARGI_TARGETREF) +#define ARGI_CONCAT_RES_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_BUFFER, ARGI_TARGETREF) +#define ARGI_COND_REF_OF_OP ARGI_LIST2 (ARGI_OBJECT_REF, ARGI_TARGETREF) +#define ARGI_CONTINUE_OP ARGI_INVALID_OPCODE +#define ARGI_COPY_OP ARGI_LIST2 (ARGI_ANYTYPE, ARGI_SIMPLE_TARGET) +#define ARGI_CREATE_BIT_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_BYTE_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_DWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_FIELD_OP ARGI_LIST4 (ARGI_BUFFER, ARGI_INTEGER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_QWORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_CREATE_WORD_FIELD_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_REFERENCE) +#define ARGI_DATA_REGION_OP ARGI_LIST3 (ARGI_STRING, ARGI_STRING, ARGI_STRING) +#define ARGI_DEBUG_OP ARG_NONE +#define ARGI_DECREMENT_OP ARGI_LIST1 (ARGI_INTEGER_REF) +#define ARGI_DEREF_OF_OP ARGI_LIST1 (ARGI_REF_OR_STRING) +#define ARGI_DEVICE_OP ARGI_INVALID_OPCODE +#define ARGI_DIVIDE_OP ARGI_LIST4 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF, ARGI_TARGETREF) +#define ARGI_DWORD_OP ARGI_INVALID_OPCODE +#define ARGI_ELSE_OP ARGI_INVALID_OPCODE +#define ARGI_EVENT_OP ARGI_INVALID_OPCODE +#define ARGI_FATAL_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_FIND_SET_LEFT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_FIND_SET_RIGHT_BIT_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_FROM_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_IF_OP ARGI_INVALID_OPCODE +#define ARGI_INCREMENT_OP ARGI_LIST1 (ARGI_INTEGER_REF) +#define ARGI_INDEX_FIELD_OP ARGI_INVALID_OPCODE +#define ARGI_INDEX_OP ARGI_LIST3 (ARGI_COMPLEXOBJ, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_LAND_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_LEQUAL_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LGREATER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LGREATEREQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LLESS_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_COMPUTEDATA) +#define ARGI_LLESSEQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LNOT_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_LNOTEQUAL_OP ARGI_INVALID_OPCODE +#define ARGI_LOAD_OP ARGI_LIST2 (ARGI_REGION_OR_FIELD,ARGI_TARGETREF) +#define ARGI_LOAD_TABLE_OP ARGI_LIST6 (ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_STRING, ARGI_ANYTYPE) +#define ARGI_LOCAL0 ARG_NONE +#define ARGI_LOCAL1 ARG_NONE +#define ARGI_LOCAL2 ARG_NONE +#define ARGI_LOCAL3 ARG_NONE +#define ARGI_LOCAL4 ARG_NONE +#define ARGI_LOCAL5 ARG_NONE +#define ARGI_LOCAL6 ARG_NONE +#define ARGI_LOCAL7 ARG_NONE +#define ARGI_LOR_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_MATCH_OP ARGI_LIST6 (ARGI_PACKAGE, ARGI_INTEGER, ARGI_COMPUTEDATA, ARGI_INTEGER,ARGI_COMPUTEDATA,ARGI_INTEGER) +#define ARGI_METHOD_OP ARGI_INVALID_OPCODE +#define ARGI_METHODCALL_OP ARGI_INVALID_OPCODE +#define ARGI_MID_OP ARGI_LIST4 (ARGI_BUFFER_OR_STRING,ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_MOD_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_MULTIPLY_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_MUTEX_OP ARGI_INVALID_OPCODE +#define ARGI_NAME_OP ARGI_INVALID_OPCODE +#define ARGI_NAMEDFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_NAMEPATH_OP ARGI_INVALID_OPCODE +#define ARGI_NOOP_OP ARG_NONE +#define ARGI_NOTIFY_OP ARGI_LIST2 (ARGI_DEVICE_REF, ARGI_INTEGER) +#define ARGI_ONE_OP ARG_NONE +#define ARGI_ONES_OP ARG_NONE +#define ARGI_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_POWER_RES_OP ARGI_INVALID_OPCODE +#define ARGI_PROCESSOR_OP ARGI_INVALID_OPCODE +#define ARGI_QWORD_OP ARGI_INVALID_OPCODE +#define ARGI_REF_OF_OP ARGI_LIST1 (ARGI_OBJECT_REF) +#define ARGI_REGION_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_INTEGER) +#define ARGI_RELEASE_OP ARGI_LIST1 (ARGI_MUTEX) +#define ARGI_RESERVEDFIELD_OP ARGI_INVALID_OPCODE +#define ARGI_RESET_OP ARGI_LIST1 (ARGI_EVENT) +#define ARGI_RETURN_OP ARGI_INVALID_OPCODE +#define ARGI_REVISION_OP ARG_NONE +#define ARGI_SCOPE_OP ARGI_INVALID_OPCODE +#define ARGI_SHIFT_LEFT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_SHIFT_RIGHT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_SIGNAL_OP ARGI_LIST1 (ARGI_EVENT) +#define ARGI_SIZE_OF_OP ARGI_LIST1 (ARGI_DATAOBJECT) +#define ARGI_SLEEP_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_STALL_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_STATICSTRING_OP ARGI_INVALID_OPCODE +#define ARGI_STORE_OP ARGI_LIST2 (ARGI_DATAREFOBJ, ARGI_TARGETREF) +#define ARGI_STRING_OP ARGI_INVALID_OPCODE +#define ARGI_SUBTRACT_OP ARGI_LIST3 (ARGI_INTEGER, ARGI_INTEGER, ARGI_TARGETREF) +#define ARGI_THERMAL_ZONE_OP ARGI_INVALID_OPCODE +#define ARGI_TIMER_OP ARG_NONE +#define ARGI_TO_BCD_OP ARGI_LIST2 (ARGI_INTEGER, ARGI_FIXED_TARGET) +#define ARGI_TO_BUFFER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_DEC_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_HEX_STR_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_INTEGER_OP ARGI_LIST2 (ARGI_COMPUTEDATA,ARGI_FIXED_TARGET) +#define ARGI_TO_STRING_OP ARGI_LIST3 (ARGI_BUFFER, ARGI_INTEGER, ARGI_FIXED_TARGET) +#define ARGI_TYPE_OP ARGI_LIST1 (ARGI_ANYTYPE) +#define ARGI_UNLOAD_OP ARGI_LIST1 (ARGI_DDBHANDLE) +#define ARGI_VAR_PACKAGE_OP ARGI_LIST1 (ARGI_INTEGER) +#define ARGI_WAIT_OP ARGI_LIST2 (ARGI_EVENT, ARGI_INTEGER) +#define ARGI_WHILE_OP ARGI_INVALID_OPCODE +#define ARGI_WORD_OP ARGI_INVALID_OPCODE +#define ARGI_ZERO_OP ARG_NONE + + +/* + * Summary of opcode types/flags + */ + +/****************************************************************************** + + Opcodes that have associated namespace objects (AML_NSOBJECT flag) + + AML_SCOPE_OP + AML_DEVICE_OP + AML_THERMAL_ZONE_OP + AML_METHOD_OP + AML_POWER_RES_OP + AML_PROCESSOR_OP + AML_FIELD_OP + AML_INDEX_FIELD_OP + AML_BANK_FIELD_OP + AML_NAME_OP + AML_ALIAS_OP + AML_MUTEX_OP + AML_EVENT_OP + AML_REGION_OP + AML_CREATE_FIELD_OP + AML_CREATE_BIT_FIELD_OP + AML_CREATE_BYTE_FIELD_OP + AML_CREATE_WORD_FIELD_OP + AML_CREATE_DWORD_FIELD_OP + AML_CREATE_QWORD_FIELD_OP + AML_INT_NAMEDFIELD_OP + AML_INT_METHODCALL_OP + AML_INT_NAMEPATH_OP + + Opcodes that are "namespace" opcodes (AML_NSOPCODE flag) + + AML_SCOPE_OP + AML_DEVICE_OP + AML_THERMAL_ZONE_OP + AML_METHOD_OP + AML_POWER_RES_OP + AML_PROCESSOR_OP + AML_FIELD_OP + AML_INDEX_FIELD_OP + AML_BANK_FIELD_OP + AML_NAME_OP + AML_ALIAS_OP + AML_MUTEX_OP + AML_EVENT_OP + AML_REGION_OP + AML_INT_NAMEDFIELD_OP + + Opcodes that have an associated namespace node (AML_NSNODE flag) + + AML_SCOPE_OP + AML_DEVICE_OP + AML_THERMAL_ZONE_OP + AML_METHOD_OP + AML_POWER_RES_OP + AML_PROCESSOR_OP + AML_NAME_OP + AML_ALIAS_OP + AML_MUTEX_OP + AML_EVENT_OP + AML_REGION_OP + AML_CREATE_FIELD_OP + AML_CREATE_BIT_FIELD_OP + AML_CREATE_BYTE_FIELD_OP + AML_CREATE_WORD_FIELD_OP + AML_CREATE_DWORD_FIELD_OP + AML_CREATE_QWORD_FIELD_OP + AML_INT_NAMEDFIELD_OP + AML_INT_METHODCALL_OP + AML_INT_NAMEPATH_OP + + Opcodes that define named ACPI objects (AML_NAMED flag) + + AML_SCOPE_OP + AML_DEVICE_OP + AML_THERMAL_ZONE_OP + AML_METHOD_OP + AML_POWER_RES_OP + AML_PROCESSOR_OP + AML_NAME_OP + AML_ALIAS_OP + AML_MUTEX_OP + AML_EVENT_OP + AML_REGION_OP + AML_INT_NAMEDFIELD_OP + + Opcodes that contain executable AML as part of the definition that + must be deferred until needed + + AML_METHOD_OP + AML_VAR_PACKAGE_OP + AML_CREATE_FIELD_OP + AML_CREATE_BIT_FIELD_OP + AML_CREATE_BYTE_FIELD_OP + AML_CREATE_WORD_FIELD_OP + AML_CREATE_DWORD_FIELD_OP + AML_CREATE_QWORD_FIELD_OP + AML_REGION_OP + AML_BUFFER_OP + + Field opcodes + + AML_CREATE_FIELD_OP + AML_FIELD_OP + AML_INDEX_FIELD_OP + AML_BANK_FIELD_OP + + Field "Create" opcodes + + AML_CREATE_FIELD_OP + AML_CREATE_BIT_FIELD_OP + AML_CREATE_BYTE_FIELD_OP + AML_CREATE_WORD_FIELD_OP + AML_CREATE_DWORD_FIELD_OP + AML_CREATE_QWORD_FIELD_OP + +******************************************************************************/ + + +/* + * Master Opcode information table. A summary of everything we know about each opcode, all in one place. + */ + + +const struct acpi_opcode_info acpi_gbl_aml_op_info[AML_NUM_OPCODES] = +{ +/*! [Begin] no source code translation */ +/* Index Name Parser Args Interpreter Args ObjectType Class Type Flags */ + +/* 00 */ ACPI_OP ("Zero", ARGP_ZERO_OP, ARGI_ZERO_OP, ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), +/* 01 */ ACPI_OP ("One", ARGP_ONE_OP, ARGI_ONE_OP, ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), +/* 02 */ ACPI_OP ("Alias", ARGP_ALIAS_OP, ARGI_ALIAS_OP, ACPI_TYPE_LOCAL_ALIAS, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 03 */ ACPI_OP ("Name", ARGP_NAME_OP, ARGI_NAME_OP, ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 04 */ ACPI_OP ("ByteConst", ARGP_BYTE_OP, ARGI_BYTE_OP, ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, AML_CONSTANT), +/* 05 */ ACPI_OP ("WordConst", ARGP_WORD_OP, ARGI_WORD_OP, ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, AML_CONSTANT), +/* 06 */ ACPI_OP ("DwordConst", ARGP_DWORD_OP, ARGI_DWORD_OP, ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, AML_CONSTANT), +/* 07 */ ACPI_OP ("String", ARGP_STRING_OP, ARGI_STRING_OP, ACPI_TYPE_STRING, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, AML_CONSTANT), +/* 08 */ ACPI_OP ("Scope", ARGP_SCOPE_OP, ARGI_SCOPE_OP, ACPI_TYPE_LOCAL_SCOPE, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 09 */ ACPI_OP ("Buffer", ARGP_BUFFER_OP, ARGI_BUFFER_OP, ACPI_TYPE_BUFFER, AML_CLASS_CREATE, AML_TYPE_CREATE_OBJECT, AML_HAS_ARGS | AML_DEFER | AML_CONSTANT), +/* 0A */ ACPI_OP ("Package", ARGP_PACKAGE_OP, ARGI_PACKAGE_OP, ACPI_TYPE_PACKAGE, AML_CLASS_CREATE, AML_TYPE_CREATE_OBJECT, AML_HAS_ARGS | AML_DEFER | AML_CONSTANT), +/* 0B */ ACPI_OP ("Method", ARGP_METHOD_OP, ARGI_METHOD_OP, ACPI_TYPE_METHOD, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED | AML_DEFER), +/* 0C */ ACPI_OP ("Local0", ARGP_LOCAL0, ARGI_LOCAL0, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LOCAL_VARIABLE, 0), +/* 0D */ ACPI_OP ("Local1", ARGP_LOCAL1, ARGI_LOCAL1, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LOCAL_VARIABLE, 0), +/* 0E */ ACPI_OP ("Local2", ARGP_LOCAL2, ARGI_LOCAL2, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LOCAL_VARIABLE, 0), +/* 0F */ ACPI_OP ("Local3", ARGP_LOCAL3, ARGI_LOCAL3, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LOCAL_VARIABLE, 0), +/* 10 */ ACPI_OP ("Local4", ARGP_LOCAL4, ARGI_LOCAL4, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LOCAL_VARIABLE, 0), +/* 11 */ ACPI_OP ("Local5", ARGP_LOCAL5, ARGI_LOCAL5, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LOCAL_VARIABLE, 0), +/* 12 */ ACPI_OP ("Local6", ARGP_LOCAL6, ARGI_LOCAL6, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LOCAL_VARIABLE, 0), +/* 13 */ ACPI_OP ("Local7", ARGP_LOCAL7, ARGI_LOCAL7, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LOCAL_VARIABLE, 0), +/* 14 */ ACPI_OP ("Arg0", ARGP_ARG0, ARGI_ARG0, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_METHOD_ARGUMENT, 0), +/* 15 */ ACPI_OP ("Arg1", ARGP_ARG1, ARGI_ARG1, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_METHOD_ARGUMENT, 0), +/* 16 */ ACPI_OP ("Arg2", ARGP_ARG2, ARGI_ARG2, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_METHOD_ARGUMENT, 0), +/* 17 */ ACPI_OP ("Arg3", ARGP_ARG3, ARGI_ARG3, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_METHOD_ARGUMENT, 0), +/* 18 */ ACPI_OP ("Arg4", ARGP_ARG4, ARGI_ARG4, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_METHOD_ARGUMENT, 0), +/* 19 */ ACPI_OP ("Arg5", ARGP_ARG5, ARGI_ARG5, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_METHOD_ARGUMENT, 0), +/* 1A */ ACPI_OP ("Arg6", ARGP_ARG6, ARGI_ARG6, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_METHOD_ARGUMENT, 0), +/* 1B */ ACPI_OP ("Store", ARGP_STORE_OP, ARGI_STORE_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R), +/* 1C */ ACPI_OP ("RefOf", ARGP_REF_OF_OP, ARGI_REF_OF_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R), +/* 1D */ ACPI_OP ("Add", ARGP_ADD_OP, ARGI_ADD_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 1E */ ACPI_OP ("Concatenate", ARGP_CONCAT_OP, ARGI_CONCAT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), +/* 1F */ ACPI_OP ("Subtract", ARGP_SUBTRACT_OP, ARGI_SUBTRACT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 20 */ ACPI_OP ("Increment", ARGP_INCREMENT_OP, ARGI_INCREMENT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), +/* 21 */ ACPI_OP ("Decrement", ARGP_DECREMENT_OP, ARGI_DECREMENT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), +/* 22 */ ACPI_OP ("Multiply", ARGP_MULTIPLY_OP, ARGI_MULTIPLY_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 23 */ ACPI_OP ("Divide", ARGP_DIVIDE_OP, ARGI_DIVIDE_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_2T_1R, AML_FLAGS_EXEC_2A_2T_1R | AML_CONSTANT), +/* 24 */ ACPI_OP ("ShiftLeft", ARGP_SHIFT_LEFT_OP, ARGI_SHIFT_LEFT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 25 */ ACPI_OP ("ShiftRight", ARGP_SHIFT_RIGHT_OP, ARGI_SHIFT_RIGHT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 26 */ ACPI_OP ("And", ARGP_BIT_AND_OP, ARGI_BIT_AND_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 27 */ ACPI_OP ("NAnd", ARGP_BIT_NAND_OP, ARGI_BIT_NAND_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 28 */ ACPI_OP ("Or", ARGP_BIT_OR_OP, ARGI_BIT_OR_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 29 */ ACPI_OP ("NOr", ARGP_BIT_NOR_OP, ARGI_BIT_NOR_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 2A */ ACPI_OP ("XOr", ARGP_BIT_XOR_OP, ARGI_BIT_XOR_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_MATH | AML_CONSTANT), +/* 2B */ ACPI_OP ("Not", ARGP_BIT_NOT_OP, ARGI_BIT_NOT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 2C */ ACPI_OP ("FindSetLeftBit", ARGP_FIND_SET_LEFT_BIT_OP, ARGI_FIND_SET_LEFT_BIT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 2D */ ACPI_OP ("FindSetRightBit", ARGP_FIND_SET_RIGHT_BIT_OP,ARGI_FIND_SET_RIGHT_BIT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 2E */ ACPI_OP ("DerefOf", ARGP_DEREF_OF_OP, ARGI_DEREF_OF_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R), +/* 2F */ ACPI_OP ("Notify", ARGP_NOTIFY_OP, ARGI_NOTIFY_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_0R, AML_FLAGS_EXEC_2A_0T_0R), +/* 30 */ ACPI_OP ("SizeOf", ARGP_SIZE_OF_OP, ARGI_SIZE_OF_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE), +/* 31 */ ACPI_OP ("Index", ARGP_INDEX_OP, ARGI_INDEX_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R), +/* 32 */ ACPI_OP ("Match", ARGP_MATCH_OP, ARGI_MATCH_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_6A_0T_1R, AML_FLAGS_EXEC_6A_0T_1R | AML_CONSTANT), +/* 33 */ ACPI_OP ("CreateDWordField", ARGP_CREATE_DWORD_FIELD_OP,ARGI_CREATE_DWORD_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | AML_DEFER | AML_CREATE), +/* 34 */ ACPI_OP ("CreateWordField", ARGP_CREATE_WORD_FIELD_OP, ARGI_CREATE_WORD_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | AML_DEFER | AML_CREATE), +/* 35 */ ACPI_OP ("CreateByteField", ARGP_CREATE_BYTE_FIELD_OP, ARGI_CREATE_BYTE_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | AML_DEFER | AML_CREATE), +/* 36 */ ACPI_OP ("CreateBitField", ARGP_CREATE_BIT_FIELD_OP, ARGI_CREATE_BIT_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | AML_DEFER | AML_CREATE), +/* 37 */ ACPI_OP ("ObjectType", ARGP_TYPE_OP, ARGI_TYPE_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R | AML_NO_OPERAND_RESOLVE), +/* 38 */ ACPI_OP ("LAnd", ARGP_LAND_OP, ARGI_LAND_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | AML_CONSTANT), +/* 39 */ ACPI_OP ("LOr", ARGP_LOR_OP, ARGI_LOR_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL_NUMERIC | AML_CONSTANT), +/* 3A */ ACPI_OP ("LNot", ARGP_LNOT_OP, ARGI_LNOT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_1R, AML_FLAGS_EXEC_1A_0T_1R | AML_CONSTANT), +/* 3B */ ACPI_OP ("LEqual", ARGP_LEQUAL_OP, ARGI_LEQUAL_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), +/* 3C */ ACPI_OP ("LGreater", ARGP_LGREATER_OP, ARGI_LGREATER_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), +/* 3D */ ACPI_OP ("LLess", ARGP_LLESS_OP, ARGI_LLESS_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R | AML_LOGICAL | AML_CONSTANT), +/* 3E */ ACPI_OP ("If", ARGP_IF_OP, ARGI_IF_OP, ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), +/* 3F */ ACPI_OP ("Else", ARGP_ELSE_OP, ARGI_ELSE_OP, ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), +/* 40 */ ACPI_OP ("While", ARGP_WHILE_OP, ARGI_WHILE_OP, ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), +/* 41 */ ACPI_OP ("Noop", ARGP_NOOP_OP, ARGI_NOOP_OP, ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), +/* 42 */ ACPI_OP ("Return", ARGP_RETURN_OP, ARGI_RETURN_OP, ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, AML_HAS_ARGS), +/* 43 */ ACPI_OP ("Break", ARGP_BREAK_OP, ARGI_BREAK_OP, ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), +/* 44 */ ACPI_OP ("BreakPoint", ARGP_BREAK_POINT_OP, ARGI_BREAK_POINT_OP, ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), +/* 45 */ ACPI_OP ("Ones", ARGP_ONES_OP, ARGI_ONES_OP, ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, AML_CONSTANT), + +/* Prefixed opcodes (Two-byte opcodes with a prefix op) */ + +/* 46 */ ACPI_OP ("Mutex", ARGP_MUTEX_OP, ARGI_MUTEX_OP, ACPI_TYPE_MUTEX, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 47 */ ACPI_OP ("Event", ARGP_EVENT_OP, ARGI_EVENT_OP, ACPI_TYPE_EVENT, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED ), +/* 48 */ ACPI_OP ("CondRefOf", ARGP_COND_REF_OF_OP, ARGI_COND_REF_OF_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R), +/* 49 */ ACPI_OP ("CreateField", ARGP_CREATE_FIELD_OP, ARGI_CREATE_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | AML_DEFER | AML_FIELD | AML_CREATE), +/* 4A */ ACPI_OP ("Load", ARGP_LOAD_OP, ARGI_LOAD_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_0R, AML_FLAGS_EXEC_1A_1T_0R), +/* 4B */ ACPI_OP ("Stall", ARGP_STALL_OP, ARGI_STALL_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), +/* 4C */ ACPI_OP ("Sleep", ARGP_SLEEP_OP, ARGI_SLEEP_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), +/* 4D */ ACPI_OP ("Acquire", ARGP_ACQUIRE_OP, ARGI_ACQUIRE_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R), +/* 4E */ ACPI_OP ("Signal", ARGP_SIGNAL_OP, ARGI_SIGNAL_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), +/* 4F */ ACPI_OP ("Wait", ARGP_WAIT_OP, ARGI_WAIT_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_0T_1R, AML_FLAGS_EXEC_2A_0T_1R), +/* 50 */ ACPI_OP ("Reset", ARGP_RESET_OP, ARGI_RESET_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), +/* 51 */ ACPI_OP ("Release", ARGP_RELEASE_OP, ARGI_RELEASE_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), +/* 52 */ ACPI_OP ("FromBCD", ARGP_FROM_BCD_OP, ARGI_FROM_BCD_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 53 */ ACPI_OP ("ToBCD", ARGP_TO_BCD_OP, ARGI_TO_BCD_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 54 */ ACPI_OP ("Unload", ARGP_UNLOAD_OP, ARGI_UNLOAD_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_0T_0R, AML_FLAGS_EXEC_1A_0T_0R), +/* 55 */ ACPI_OP ("Revision", ARGP_REVISION_OP, ARGI_REVISION_OP, ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, 0), +/* 56 */ ACPI_OP ("Debug", ARGP_DEBUG_OP, ARGI_DEBUG_OP, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_CONSTANT, 0), +/* 57 */ ACPI_OP ("Fatal", ARGP_FATAL_OP, ARGI_FATAL_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_0T_0R, AML_FLAGS_EXEC_3A_0T_0R), +/* 58 */ ACPI_OP ("OperationRegion", ARGP_REGION_OP, ARGI_REGION_OP, ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_COMPLEX, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED | AML_DEFER), +/* 59 */ ACPI_OP ("Field", ARGP_FIELD_OP, ARGI_FIELD_OP, ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_FIELD), +/* 5A */ ACPI_OP ("Device", ARGP_DEVICE_OP, ARGI_DEVICE_OP, ACPI_TYPE_DEVICE, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 5B */ ACPI_OP ("Processor", ARGP_PROCESSOR_OP, ARGI_PROCESSOR_OP, ACPI_TYPE_PROCESSOR, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 5C */ ACPI_OP ("PowerResource", ARGP_POWER_RES_OP, ARGI_POWER_RES_OP, ACPI_TYPE_POWER, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 5D */ ACPI_OP ("ThermalZone", ARGP_THERMAL_ZONE_OP, ARGI_THERMAL_ZONE_OP, ACPI_TYPE_THERMAL, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 5E */ ACPI_OP ("IndexField", ARGP_INDEX_FIELD_OP, ARGI_INDEX_FIELD_OP, ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_FIELD), +/* 5F */ ACPI_OP ("BankField", ARGP_BANK_FIELD_OP, ARGI_BANK_FIELD_OP, ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_FIELD), + +/* Internal opcodes that map to invalid AML opcodes */ + +/* 60 */ ACPI_OP ("LNotEqual", ARGP_LNOTEQUAL_OP, ARGI_LNOTEQUAL_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), +/* 61 */ ACPI_OP ("LLessEqual", ARGP_LLESSEQUAL_OP, ARGI_LLESSEQUAL_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), +/* 62 */ ACPI_OP ("LGreaterEqual", ARGP_LGREATEREQUAL_OP, ARGI_LGREATEREQUAL_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), +/* 63 */ ACPI_OP ("-NamePath-", ARGP_NAMEPATH_OP, ARGI_NAMEPATH_OP, ACPI_TYPE_LOCAL_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, AML_NSOBJECT | AML_NSNODE ), +/* 64 */ ACPI_OP ("-MethodCall-", ARGP_METHODCALL_OP, ARGI_METHODCALL_OP, ACPI_TYPE_METHOD, AML_CLASS_METHOD_CALL, AML_TYPE_METHOD_CALL, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE), +/* 65 */ ACPI_OP ("-ByteList-", ARGP_BYTELIST_OP, ARGI_BYTELIST_OP, ACPI_TYPE_ANY, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, 0), +/* 66 */ ACPI_OP ("-ReservedField-", ARGP_RESERVEDFIELD_OP, ARGI_RESERVEDFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), +/* 67 */ ACPI_OP ("-NamedField-", ARGP_NAMEDFIELD_OP, ARGI_NAMEDFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED ), +/* 68 */ ACPI_OP ("-AccessField-", ARGP_ACCESSFIELD_OP, ARGI_ACCESSFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), +/* 69 */ ACPI_OP ("-StaticString", ARGP_STATICSTRING_OP, ARGI_STATICSTRING_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), +/* 6A */ ACPI_OP ("-Return Value-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, AML_CLASS_RETURN_VALUE, AML_TYPE_RETURN, AML_HAS_ARGS | AML_HAS_RETVAL), +/* 6B */ ACPI_OP ("-UNKNOWN_OP-", ARG_NONE, ARG_NONE, ACPI_TYPE_INVALID, AML_CLASS_UNKNOWN, AML_TYPE_BOGUS, AML_HAS_ARGS), +/* 6C */ ACPI_OP ("-ASCII_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, AML_CLASS_ASCII, AML_TYPE_BOGUS, AML_HAS_ARGS), +/* 6D */ ACPI_OP ("-PREFIX_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, AML_CLASS_PREFIX, AML_TYPE_BOGUS, AML_HAS_ARGS), + +/* ACPI 2.0 opcodes */ + +/* 6E */ ACPI_OP ("QwordConst", ARGP_QWORD_OP, ARGI_QWORD_OP, ACPI_TYPE_INTEGER, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, AML_CONSTANT), +/* 6F */ ACPI_OP ("Package /*Var*/", ARGP_VAR_PACKAGE_OP, ARGI_VAR_PACKAGE_OP, ACPI_TYPE_PACKAGE, AML_CLASS_CREATE, AML_TYPE_CREATE_OBJECT, AML_HAS_ARGS | AML_DEFER), +/* 70 */ ACPI_OP ("ConcatenateResTemplate", ARGP_CONCAT_RES_OP, ARGI_CONCAT_RES_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), +/* 71 */ ACPI_OP ("Mod", ARGP_MOD_OP, ARGI_MOD_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), +/* 72 */ ACPI_OP ("CreateQWordField", ARGP_CREATE_QWORD_FIELD_OP,ARGI_CREATE_QWORD_FIELD_OP, ACPI_TYPE_BUFFER_FIELD, AML_CLASS_CREATE, AML_TYPE_CREATE_FIELD, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE | AML_DEFER | AML_CREATE), +/* 73 */ ACPI_OP ("ToBuffer", ARGP_TO_BUFFER_OP, ARGI_TO_BUFFER_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 74 */ ACPI_OP ("ToDecimalString", ARGP_TO_DEC_STR_OP, ARGI_TO_DEC_STR_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 75 */ ACPI_OP ("ToHexString", ARGP_TO_HEX_STR_OP, ARGI_TO_HEX_STR_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 76 */ ACPI_OP ("ToInteger", ARGP_TO_INTEGER_OP, ARGI_TO_INTEGER_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R | AML_CONSTANT), +/* 77 */ ACPI_OP ("ToString", ARGP_TO_STRING_OP, ARGI_TO_STRING_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_2A_1T_1R, AML_FLAGS_EXEC_2A_1T_1R | AML_CONSTANT), +/* 78 */ ACPI_OP ("CopyObject", ARGP_COPY_OP, ARGI_COPY_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_1A_1T_1R, AML_FLAGS_EXEC_1A_1T_1R), +/* 79 */ ACPI_OP ("Mid", ARGP_MID_OP, ARGI_MID_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_3A_1T_1R, AML_FLAGS_EXEC_3A_1T_1R | AML_CONSTANT), +/* 7A */ ACPI_OP ("Continue", ARGP_CONTINUE_OP, ARGI_CONTINUE_OP, ACPI_TYPE_ANY, AML_CLASS_CONTROL, AML_TYPE_CONTROL, 0), +/* 7B */ ACPI_OP ("LoadTable", ARGP_LOAD_TABLE_OP, ARGI_LOAD_TABLE_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_6A_0T_1R, AML_FLAGS_EXEC_6A_0T_1R), +/* 7C */ ACPI_OP ("DataTableRegion", ARGP_DATA_REGION_OP, ARGI_DATA_REGION_OP, ACPI_TYPE_REGION, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_SIMPLE, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED), +/* 7D */ ACPI_OP ("[EvalSubTree]", ARGP_SCOPE_OP, ARGI_SCOPE_OP, ACPI_TYPE_ANY, AML_CLASS_NAMED_OBJECT, AML_TYPE_NAMED_NO_OBJ, AML_HAS_ARGS | AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE), + +/* ACPI 3.0 opcodes */ + +/* 7E */ ACPI_OP ("Timer", ARGP_TIMER_OP, ARGI_TIMER_OP, ACPI_TYPE_ANY, AML_CLASS_EXECUTE, AML_TYPE_EXEC_0A_0T_1R, AML_FLAGS_EXEC_0A_0T_1R) + +/*! [End] no source code translation !*/ +}; + +/* + * This table is directly indexed by the opcodes, and returns an + * index into the table above + */ +static const u8 acpi_gbl_short_op_index[256] = +{ +/* 0 1 2 3 4 5 6 7 */ +/* 8 9 A B C D E F */ +/* 0x00 */ 0x00, 0x01, _UNK, _UNK, _UNK, _UNK, 0x02, _UNK, +/* 0x08 */ 0x03, _UNK, 0x04, 0x05, 0x06, 0x07, 0x6E, _UNK, +/* 0x10 */ 0x08, 0x09, 0x0a, 0x6F, 0x0b, _UNK, _UNK, _UNK, +/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x20 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x28 */ _UNK, _UNK, _UNK, _UNK, _UNK, 0x63, _PFX, _PFX, +/* 0x30 */ 0x67, 0x66, 0x68, 0x65, 0x69, 0x64, 0x6A, 0x7D, +/* 0x38 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x40 */ _UNK, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, +/* 0x48 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, +/* 0x50 */ _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, _ASC, +/* 0x58 */ _ASC, _ASC, _ASC, _UNK, _PFX, _UNK, _PFX, _ASC, +/* 0x60 */ 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, +/* 0x68 */ 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, _UNK, +/* 0x70 */ 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, +/* 0x78 */ 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, +/* 0x80 */ 0x2b, 0x2c, 0x2d, 0x2e, 0x70, 0x71, 0x2f, 0x30, +/* 0x88 */ 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x72, +/* 0x90 */ 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x73, 0x74, +/* 0x98 */ 0x75, 0x76, _UNK, _UNK, 0x77, 0x78, 0x79, 0x7A, +/* 0xA0 */ 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x60, 0x61, +/* 0xA8 */ 0x62, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xB0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xB8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xC0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xC8 */ _UNK, _UNK, _UNK, _UNK, 0x44, _UNK, _UNK, _UNK, +/* 0xD0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xD8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xE0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xE8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xF0 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0xF8 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x45, +}; + +/* + * This table is indexed by the second opcode of the extended opcode + * pair. It returns an index into the opcode table (acpi_gbl_aml_op_info) + */ +static const u8 acpi_gbl_long_op_index[NUM_EXTENDED_OPCODE] = +{ +/* 0 1 2 3 4 5 6 7 */ +/* 8 9 A B C D E F */ +/* 0x00 */ _UNK, 0x46, 0x47, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x08 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x10 */ _UNK, _UNK, 0x48, 0x49, _UNK, _UNK, _UNK, _UNK, +/* 0x18 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, 0x7B, +/* 0x20 */ 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, +/* 0x28 */ 0x52, 0x53, 0x54, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x30 */ 0x55, 0x56, 0x57, 0x7e, _UNK, _UNK, _UNK, _UNK, +/* 0x38 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x40 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x48 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x50 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x58 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x60 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x68 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x70 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x78 */ _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, _UNK, +/* 0x80 */ 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, +/* 0x88 */ 0x7C, +}; + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_opcode_info + * + * PARAMETERS: Opcode - The AML opcode + * + * RETURN: A pointer to the info about the opcode. NULL if the opcode was + * not found in the table. + * + * DESCRIPTION: Find AML opcode description based on the opcode. + * NOTE: This procedure must ALWAYS return a valid pointer! + * + ******************************************************************************/ + +const struct acpi_opcode_info * +acpi_ps_get_opcode_info ( + u16 opcode) +{ + ACPI_FUNCTION_NAME ("ps_get_opcode_info"); + + + /* + * Detect normal 8-bit opcode or extended 16-bit opcode + */ + switch ((u8) (opcode >> 8)) { + case 0: + + /* Simple (8-bit) opcode: 0-255, can't index beyond table */ + + return (&acpi_gbl_aml_op_info [acpi_gbl_short_op_index [(u8) opcode]]); + + case AML_EXTOP: + + /* Extended (16-bit, prefix+opcode) opcode */ + + if (((u8) opcode) <= MAX_EXTENDED_OPCODE) { + return (&acpi_gbl_aml_op_info [acpi_gbl_long_op_index [(u8) opcode]]); + } + + /* Else fall through to error case below */ + /*lint -fallthrough */ + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown AML opcode [%4.4X]\n", opcode)); + break; + } + + + /* Default is "unknown opcode" */ + + return (&acpi_gbl_aml_op_info [_UNK]); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_opcode_name + * + * PARAMETERS: Opcode - The AML opcode + * + * RETURN: A pointer to the name of the opcode (ASCII String) + * Note: Never returns NULL. + * + * DESCRIPTION: Translate an opcode into a human-readable string + * + ******************************************************************************/ + +char * +acpi_ps_get_opcode_name ( + u16 opcode) +{ +#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUG_OUTPUT) + + const struct acpi_opcode_info *op; + + + op = acpi_ps_get_opcode_info (opcode); + + /* Always guaranteed to return a valid pointer */ + + return (op->name); + +#else + return ("AE_NOT_CONFIGURED"); + +#endif +} + diff --git a/drivers/acpi/parser/psparse.c b/drivers/acpi/parser/psparse.c new file mode 100644 index 00000000000..e79edb53cb3 --- /dev/null +++ b/drivers/acpi/parser/psparse.c @@ -0,0 +1,1266 @@ +/****************************************************************************** + * + * Module Name: psparse - Parser top level AML parse routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +/* + * Parse the AML and build an operation tree as most interpreters, + * like Perl, do. Parsing is done by hand rather than with a YACC + * generated parser to tightly constrain stack and dynamic memory + * usage. At the same time, parsing is kept flexible and the code + * fairly compact by parsing based on a list of AML opcode + * templates in aml_op_info[] + */ + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/acdispat.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> +#include <acpi/acinterp.h> + +#define _COMPONENT ACPI_PARSER + ACPI_MODULE_NAME ("psparse") + + +static u32 acpi_gbl_depth = 0; + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_opcode_size + * + * PARAMETERS: Opcode - An AML opcode + * + * RETURN: Size of the opcode, in bytes (1 or 2) + * + * DESCRIPTION: Get the size of the current opcode. + * + ******************************************************************************/ + +u32 +acpi_ps_get_opcode_size ( + u32 opcode) +{ + + /* Extended (2-byte) opcode if > 255 */ + + if (opcode > 0x00FF) { + return (2); + } + + /* Otherwise, just a single byte opcode */ + + return (1); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_peek_opcode + * + * PARAMETERS: parser_state - A parser state object + * + * RETURN: Status + * + * DESCRIPTION: Get next AML opcode (without incrementing AML pointer) + * + ******************************************************************************/ + +u16 +acpi_ps_peek_opcode ( + struct acpi_parse_state *parser_state) +{ + u8 *aml; + u16 opcode; + + + aml = parser_state->aml; + opcode = (u16) ACPI_GET8 (aml); + + + if (opcode == AML_EXTOP) { + /* Extended opcode */ + + aml++; + opcode = (u16) ((opcode << 8) | ACPI_GET8 (aml)); + } + + return (opcode); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_complete_this_op + * + * PARAMETERS: walk_state - Current State + * Op - Op to complete + * + * RETURN: None. + * + * DESCRIPTION: Perform any cleanup at the completion of an Op. + * + ******************************************************************************/ + +void +acpi_ps_complete_this_op ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op) +{ + union acpi_parse_object *prev; + union acpi_parse_object *next; + const struct acpi_opcode_info *parent_info; + union acpi_parse_object *replacement_op = NULL; + + + ACPI_FUNCTION_TRACE_PTR ("ps_complete_this_op", op); + + + /* Check for null Op, can happen if AML code is corrupt */ + + if (!op) { + return_VOID; + } + + /* Delete this op and the subtree below it if asked to */ + + if (((walk_state->parse_flags & ACPI_PARSE_TREE_MASK) != ACPI_PARSE_DELETE_TREE) || + (walk_state->op_info->class == AML_CLASS_ARGUMENT)) { + return_VOID; + } + + /* Make sure that we only delete this subtree */ + + if (op->common.parent) { + /* + * Check if we need to replace the operator and its subtree + * with a return value op (placeholder op) + */ + parent_info = acpi_ps_get_opcode_info (op->common.parent->common.aml_opcode); + + switch (parent_info->class) { + case AML_CLASS_CONTROL: + break; + + case AML_CLASS_CREATE: + + /* + * These opcodes contain term_arg operands. The current + * op must be replaced by a placeholder return op + */ + replacement_op = acpi_ps_alloc_op (AML_INT_RETURN_VALUE_OP); + if (!replacement_op) { + goto cleanup; + } + break; + + case AML_CLASS_NAMED_OBJECT: + + /* + * These opcodes contain term_arg operands. The current + * op must be replaced by a placeholder return op + */ + if ((op->common.parent->common.aml_opcode == AML_REGION_OP) || + (op->common.parent->common.aml_opcode == AML_DATA_REGION_OP) || + (op->common.parent->common.aml_opcode == AML_BUFFER_OP) || + (op->common.parent->common.aml_opcode == AML_PACKAGE_OP) || + (op->common.parent->common.aml_opcode == AML_VAR_PACKAGE_OP)) { + replacement_op = acpi_ps_alloc_op (AML_INT_RETURN_VALUE_OP); + if (!replacement_op) { + goto cleanup; + } + } + + if ((op->common.parent->common.aml_opcode == AML_NAME_OP) && + (walk_state->descending_callback != acpi_ds_exec_begin_op)) { + if ((op->common.aml_opcode == AML_BUFFER_OP) || + (op->common.aml_opcode == AML_PACKAGE_OP) || + (op->common.aml_opcode == AML_VAR_PACKAGE_OP)) { + replacement_op = acpi_ps_alloc_op (op->common.aml_opcode); + if (!replacement_op) { + goto cleanup; + } + + replacement_op->named.data = op->named.data; + replacement_op->named.length = op->named.length; + } + } + break; + + default: + replacement_op = acpi_ps_alloc_op (AML_INT_RETURN_VALUE_OP); + if (!replacement_op) { + goto cleanup; + } + } + + /* We must unlink this op from the parent tree */ + + prev = op->common.parent->common.value.arg; + if (prev == op) { + /* This op is the first in the list */ + + if (replacement_op) { + replacement_op->common.parent = op->common.parent; + replacement_op->common.value.arg = NULL; + replacement_op->common.node = op->common.node; + op->common.parent->common.value.arg = replacement_op; + replacement_op->common.next = op->common.next; + } + else { + op->common.parent->common.value.arg = op->common.next; + } + } + + /* Search the parent list */ + + else while (prev) { + /* Traverse all siblings in the parent's argument list */ + + next = prev->common.next; + if (next == op) { + if (replacement_op) { + replacement_op->common.parent = op->common.parent; + replacement_op->common.value.arg = NULL; + replacement_op->common.node = op->common.node; + prev->common.next = replacement_op; + replacement_op->common.next = op->common.next; + next = NULL; + } + else { + prev->common.next = op->common.next; + next = NULL; + } + } + + prev = next; + } + } + + +cleanup: + + /* Now we can actually delete the subtree rooted at op */ + + acpi_ps_delete_parse_tree (op); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_next_parse_state + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Status + * + * DESCRIPTION: Update the parser state based upon the return exception from + * the parser callback. + * + ******************************************************************************/ + +acpi_status +acpi_ps_next_parse_state ( + struct acpi_walk_state *walk_state, + union acpi_parse_object *op, + acpi_status callback_status) +{ + struct acpi_parse_state *parser_state = &walk_state->parser_state; + acpi_status status = AE_CTRL_PENDING; + + + ACPI_FUNCTION_TRACE_PTR ("ps_next_parse_state", op); + + + switch (callback_status) { + case AE_CTRL_TERMINATE: + + /* + * A control method was terminated via a RETURN statement. + * The walk of this method is complete. + */ + parser_state->aml = parser_state->aml_end; + status = AE_CTRL_TERMINATE; + break; + + + case AE_CTRL_BREAK: + + parser_state->aml = walk_state->aml_last_while; + walk_state->control_state->common.value = FALSE; + status = AE_CTRL_BREAK; + break; + + case AE_CTRL_CONTINUE: + + + parser_state->aml = walk_state->aml_last_while; + status = AE_CTRL_CONTINUE; + break; + + case AE_CTRL_PENDING: + + parser_state->aml = walk_state->aml_last_while; + break; + +#if 0 + case AE_CTRL_SKIP: + + parser_state->aml = parser_state->scope->parse_scope.pkg_end; + status = AE_OK; + break; +#endif + + case AE_CTRL_TRUE: + + /* + * Predicate of an IF was true, and we are at the matching ELSE. + * Just close out this package + */ + parser_state->aml = acpi_ps_get_next_package_end (parser_state); + break; + + + case AE_CTRL_FALSE: + + /* + * Either an IF/WHILE Predicate was false or we encountered a BREAK + * opcode. In both cases, we do not execute the rest of the + * package; We simply close out the parent (finishing the walk of + * this branch of the tree) and continue execution at the parent + * level. + */ + parser_state->aml = parser_state->scope->parse_scope.pkg_end; + + /* In the case of a BREAK, just force a predicate (if any) to FALSE */ + + walk_state->control_state->common.value = FALSE; + status = AE_CTRL_END; + break; + + + case AE_CTRL_TRANSFER: + + /* + * A method call (invocation) -- transfer control + */ + status = AE_CTRL_TRANSFER; + walk_state->prev_op = op; + walk_state->method_call_op = op; + walk_state->method_call_node = (op->common.value.arg)->common.node; + + /* Will return value (if any) be used by the caller? */ + + walk_state->return_used = acpi_ds_is_result_used (op, walk_state); + break; + + + default: + status = callback_status; + if ((callback_status & AE_CODE_MASK) == AE_CODE_CONTROL) { + status = AE_OK; + } + break; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_parse_loop + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Status + * + * DESCRIPTION: Parse AML (pointed to by the current parser state) and return + * a tree of ops. + * + ******************************************************************************/ + +acpi_status +acpi_ps_parse_loop ( + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + union acpi_parse_object *op = NULL; /* current op */ + union acpi_parse_object *arg = NULL; + union acpi_parse_object *pre_op = NULL; + struct acpi_parse_state *parser_state; + u8 *aml_op_start = NULL; + + + ACPI_FUNCTION_TRACE_PTR ("ps_parse_loop", walk_state); + + if (walk_state->descending_callback == NULL) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + parser_state = &walk_state->parser_state; + walk_state->arg_types = 0; + +#if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY)) + if (walk_state->walk_type & ACPI_WALK_METHOD_RESTART) { + /* We are restarting a preempted control method */ + + if (acpi_ps_has_completed_scope (parser_state)) { + /* + * We must check if a predicate to an IF or WHILE statement + * was just completed + */ + if ((parser_state->scope->parse_scope.op) && + ((parser_state->scope->parse_scope.op->common.aml_opcode == AML_IF_OP) || + (parser_state->scope->parse_scope.op->common.aml_opcode == AML_WHILE_OP)) && + (walk_state->control_state) && + (walk_state->control_state->common.state == + ACPI_CONTROL_PREDICATE_EXECUTING)) { + /* + * A predicate was just completed, get the value of the + * predicate and branch based on that value + */ + walk_state->op = NULL; + status = acpi_ds_get_predicate_value (walk_state, ACPI_TO_POINTER (TRUE)); + if (ACPI_FAILURE (status) && + ((status & AE_CODE_MASK) != AE_CODE_CONTROL)) { + if (status == AE_AML_NO_RETURN_VALUE) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Invoked method did not return a value, %s\n", + acpi_format_exception (status))); + + } + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "get_predicate Failed, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + status = acpi_ps_next_parse_state (walk_state, op, status); + } + + acpi_ps_pop_scope (parser_state, &op, + &walk_state->arg_types, &walk_state->arg_count); + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Popped scope, Op=%p\n", op)); + } + else if (walk_state->prev_op) { + /* We were in the middle of an op */ + + op = walk_state->prev_op; + walk_state->arg_types = walk_state->prev_arg_types; + } + } +#endif + + /* + * Iterative parsing loop, while there is more aml to process: + */ + while ((parser_state->aml < parser_state->aml_end) || (op)) { + aml_op_start = parser_state->aml; + if (!op) { + /* Get the next opcode from the AML stream */ + + walk_state->aml_offset = (u32) ACPI_PTR_DIFF (parser_state->aml, + parser_state->aml_start); + walk_state->opcode = acpi_ps_peek_opcode (parser_state); + + /* + * First cut to determine what we have found: + * 1) A valid AML opcode + * 2) A name string + * 3) An unknown/invalid opcode + */ + walk_state->op_info = acpi_ps_get_opcode_info (walk_state->opcode); + switch (walk_state->op_info->class) { + case AML_CLASS_ASCII: + case AML_CLASS_PREFIX: + /* + * Starts with a valid prefix or ASCII char, this is a name + * string. Convert the bare name string to a namepath. + */ + walk_state->opcode = AML_INT_NAMEPATH_OP; + walk_state->arg_types = ARGP_NAMESTRING; + break; + + case AML_CLASS_UNKNOWN: + + /* The opcode is unrecognized. Just skip unknown opcodes */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Found unknown opcode %X at AML address %p offset %X, ignoring\n", + walk_state->opcode, parser_state->aml, walk_state->aml_offset)); + + ACPI_DUMP_BUFFER (parser_state->aml, 128); + + /* Assume one-byte bad opcode */ + + parser_state->aml++; + continue; + + default: + + /* Found opcode info, this is a normal opcode */ + + parser_state->aml += acpi_ps_get_opcode_size (walk_state->opcode); + walk_state->arg_types = walk_state->op_info->parse_args; + break; + } + + /* Create Op structure and append to parent's argument list */ + + if (walk_state->op_info->flags & AML_NAMED) { + /* Allocate a new pre_op if necessary */ + + if (!pre_op) { + pre_op = acpi_ps_alloc_op (walk_state->opcode); + if (!pre_op) { + status = AE_NO_MEMORY; + goto close_this_op; + } + } + + pre_op->common.value.arg = NULL; + pre_op->common.aml_opcode = walk_state->opcode; + + /* + * Get and append arguments until we find the node that contains + * the name (the type ARGP_NAME). + */ + while (GET_CURRENT_ARG_TYPE (walk_state->arg_types) && + (GET_CURRENT_ARG_TYPE (walk_state->arg_types) != ARGP_NAME)) { + status = acpi_ps_get_next_arg (walk_state, parser_state, + GET_CURRENT_ARG_TYPE (walk_state->arg_types), &arg); + if (ACPI_FAILURE (status)) { + goto close_this_op; + } + + acpi_ps_append_arg (pre_op, arg); + INCREMENT_ARG_LIST (walk_state->arg_types); + } + + /* Make sure that we found a NAME and didn't run out of arguments */ + + if (!GET_CURRENT_ARG_TYPE (walk_state->arg_types)) { + status = AE_AML_NO_OPERAND; + goto close_this_op; + } + + /* We know that this arg is a name, move to next arg */ + + INCREMENT_ARG_LIST (walk_state->arg_types); + + /* + * Find the object. This will either insert the object into + * the namespace or simply look it up + */ + walk_state->op = NULL; + + status = walk_state->descending_callback (walk_state, &op); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "During name lookup/catalog, %s\n", + acpi_format_exception (status))); + goto close_this_op; + } + + if (op == NULL) { + continue; + } + + status = acpi_ps_next_parse_state (walk_state, op, status); + if (status == AE_CTRL_PENDING) { + status = AE_OK; + goto close_this_op; + } + + if (ACPI_FAILURE (status)) { + goto close_this_op; + } + + acpi_ps_append_arg (op, pre_op->common.value.arg); + acpi_gbl_depth++; + + if (op->common.aml_opcode == AML_REGION_OP) { + /* + * Defer final parsing of an operation_region body, + * because we don't have enough info in the first pass + * to parse it correctly (i.e., there may be method + * calls within the term_arg elements of the body.) + * + * However, we must continue parsing because + * the opregion is not a standalone package -- + * we don't know where the end is at this point. + * + * (Length is unknown until parse of the body complete) + */ + op->named.data = aml_op_start; + op->named.length = 0; + } + } + else { + /* Not a named opcode, just allocate Op and append to parent */ + + walk_state->op_info = acpi_ps_get_opcode_info (walk_state->opcode); + op = acpi_ps_alloc_op (walk_state->opcode); + if (!op) { + status = AE_NO_MEMORY; + goto close_this_op; + } + + if (walk_state->op_info->flags & AML_CREATE) { + /* + * Backup to beginning of create_xXXfield declaration + * body_length is unknown until we parse the body + */ + op->named.data = aml_op_start; + op->named.length = 0; + } + + acpi_ps_append_arg (acpi_ps_get_parent_scope (parser_state), op); + + if ((walk_state->descending_callback != NULL)) { + /* + * Find the object. This will either insert the object into + * the namespace or simply look it up + */ + walk_state->op = op; + + status = walk_state->descending_callback (walk_state, &op); + status = acpi_ps_next_parse_state (walk_state, op, status); + if (status == AE_CTRL_PENDING) { + status = AE_OK; + goto close_this_op; + } + + if (ACPI_FAILURE (status)) { + goto close_this_op; + } + } + } + + op->common.aml_offset = walk_state->aml_offset; + + if (walk_state->op_info) { + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, + "Opcode %4.4X [%s] Op %p Aml %p aml_offset %5.5X\n", + (u32) op->common.aml_opcode, walk_state->op_info->name, + op, parser_state->aml, op->common.aml_offset)); + } + } + + + /* Start arg_count at zero because we don't know if there are any args yet */ + + walk_state->arg_count = 0; + + if (walk_state->arg_types) /* Are there any arguments that must be processed? */ { + /* Get arguments */ + + switch (op->common.aml_opcode) { + case AML_BYTE_OP: /* AML_BYTEDATA_ARG */ + case AML_WORD_OP: /* AML_WORDDATA_ARG */ + case AML_DWORD_OP: /* AML_DWORDATA_ARG */ + case AML_QWORD_OP: /* AML_QWORDATA_ARG */ + case AML_STRING_OP: /* AML_ASCIICHARLIST_ARG */ + + /* Fill in constant or string argument directly */ + + acpi_ps_get_next_simple_arg (parser_state, + GET_CURRENT_ARG_TYPE (walk_state->arg_types), op); + break; + + case AML_INT_NAMEPATH_OP: /* AML_NAMESTRING_ARG */ + + status = acpi_ps_get_next_namepath (walk_state, parser_state, op, 1); + if (ACPI_FAILURE (status)) { + goto close_this_op; + } + + walk_state->arg_types = 0; + break; + + default: + + /* Op is not a constant or string, append each argument to the Op */ + + while (GET_CURRENT_ARG_TYPE (walk_state->arg_types) && + !walk_state->arg_count) { + walk_state->aml_offset = (u32) ACPI_PTR_DIFF (parser_state->aml, + parser_state->aml_start); + status = acpi_ps_get_next_arg (walk_state, parser_state, + GET_CURRENT_ARG_TYPE (walk_state->arg_types), &arg); + if (ACPI_FAILURE (status)) { + goto close_this_op; + } + + if (arg) { + arg->common.aml_offset = walk_state->aml_offset; + acpi_ps_append_arg (op, arg); + } + INCREMENT_ARG_LIST (walk_state->arg_types); + } + + /* Special processing for certain opcodes */ + + switch (op->common.aml_opcode) { + case AML_METHOD_OP: + + /* + * Skip parsing of control method + * because we don't have enough info in the first pass + * to parse it correctly. + * + * Save the length and address of the body + */ + op->named.data = parser_state->aml; + op->named.length = (u32) (parser_state->pkg_end - parser_state->aml); + + /* Skip body of method */ + + parser_state->aml = parser_state->pkg_end; + walk_state->arg_count = 0; + break; + + case AML_BUFFER_OP: + case AML_PACKAGE_OP: + case AML_VAR_PACKAGE_OP: + + if ((op->common.parent) && + (op->common.parent->common.aml_opcode == AML_NAME_OP) && + (walk_state->descending_callback != acpi_ds_exec_begin_op)) { + /* + * Skip parsing of Buffers and Packages + * because we don't have enough info in the first pass + * to parse them correctly. + */ + op->named.data = aml_op_start; + op->named.length = (u32) (parser_state->pkg_end - aml_op_start); + + /* Skip body */ + + parser_state->aml = parser_state->pkg_end; + walk_state->arg_count = 0; + } + break; + + case AML_WHILE_OP: + + if (walk_state->control_state) { + walk_state->control_state->control.package_end = parser_state->pkg_end; + } + break; + + default: + + /* No action for all other opcodes */ + break; + } + break; + } + } + + /* Check for arguments that need to be processed */ + + if (walk_state->arg_count) { + /* There are arguments (complex ones), push Op and prepare for argument */ + + status = acpi_ps_push_scope (parser_state, op, + walk_state->arg_types, walk_state->arg_count); + if (ACPI_FAILURE (status)) { + goto close_this_op; + } + op = NULL; + continue; + } + + /* All arguments have been processed -- Op is complete, prepare for next */ + + walk_state->op_info = acpi_ps_get_opcode_info (op->common.aml_opcode); + if (walk_state->op_info->flags & AML_NAMED) { + if (acpi_gbl_depth) { + acpi_gbl_depth--; + } + + if (op->common.aml_opcode == AML_REGION_OP) { + /* + * Skip parsing of control method or opregion body, + * because we don't have enough info in the first pass + * to parse them correctly. + * + * Completed parsing an op_region declaration, we now + * know the length. + */ + op->named.length = (u32) (parser_state->aml - op->named.data); + } + } + + if (walk_state->op_info->flags & AML_CREATE) { + /* + * Backup to beginning of create_xXXfield declaration (1 for + * Opcode) + * + * body_length is unknown until we parse the body + */ + op->named.length = (u32) (parser_state->aml - op->named.data); + } + + /* This op complete, notify the dispatcher */ + + if (walk_state->ascending_callback != NULL) { + walk_state->op = op; + walk_state->opcode = op->common.aml_opcode; + + status = walk_state->ascending_callback (walk_state); + status = acpi_ps_next_parse_state (walk_state, op, status); + if (status == AE_CTRL_PENDING) { + status = AE_OK; + goto close_this_op; + } + } + + +close_this_op: + /* + * Finished one argument of the containing scope + */ + parser_state->scope->parse_scope.arg_count--; + + /* Close this Op (will result in parse subtree deletion) */ + + acpi_ps_complete_this_op (walk_state, op); + op = NULL; + if (pre_op) { + acpi_ps_free_op (pre_op); + pre_op = NULL; + } + + switch (status) { + case AE_OK: + break; + + + case AE_CTRL_TRANSFER: + + /* + * We are about to transfer to a called method. + */ + walk_state->prev_op = op; + walk_state->prev_arg_types = walk_state->arg_types; + return_ACPI_STATUS (status); + + + case AE_CTRL_END: + + acpi_ps_pop_scope (parser_state, &op, + &walk_state->arg_types, &walk_state->arg_count); + + if (op) { + walk_state->op = op; + walk_state->op_info = acpi_ps_get_opcode_info (op->common.aml_opcode); + walk_state->opcode = op->common.aml_opcode; + + status = walk_state->ascending_callback (walk_state); + status = acpi_ps_next_parse_state (walk_state, op, status); + + acpi_ps_complete_this_op (walk_state, op); + op = NULL; + } + status = AE_OK; + break; + + + case AE_CTRL_BREAK: + case AE_CTRL_CONTINUE: + + /* Pop off scopes until we find the While */ + + while (!op || (op->common.aml_opcode != AML_WHILE_OP)) { + acpi_ps_pop_scope (parser_state, &op, + &walk_state->arg_types, &walk_state->arg_count); + } + + /* Close this iteration of the While loop */ + + walk_state->op = op; + walk_state->op_info = acpi_ps_get_opcode_info (op->common.aml_opcode); + walk_state->opcode = op->common.aml_opcode; + + status = walk_state->ascending_callback (walk_state); + status = acpi_ps_next_parse_state (walk_state, op, status); + + acpi_ps_complete_this_op (walk_state, op); + op = NULL; + + status = AE_OK; + break; + + + case AE_CTRL_TERMINATE: + + status = AE_OK; + + /* Clean up */ + do { + if (op) { + acpi_ps_complete_this_op (walk_state, op); + } + acpi_ps_pop_scope (parser_state, &op, + &walk_state->arg_types, &walk_state->arg_count); + + } while (op); + + return_ACPI_STATUS (status); + + + default: /* All other non-AE_OK status */ + + do { + if (op) { + acpi_ps_complete_this_op (walk_state, op); + } + acpi_ps_pop_scope (parser_state, &op, + &walk_state->arg_types, &walk_state->arg_count); + + } while (op); + + + /* + * TBD: Cleanup parse ops on error + */ +#if 0 + if (op == NULL) { + acpi_ps_pop_scope (parser_state, &op, + &walk_state->arg_types, &walk_state->arg_count); + } +#endif + walk_state->prev_op = op; + walk_state->prev_arg_types = walk_state->arg_types; + return_ACPI_STATUS (status); + } + + /* This scope complete? */ + + if (acpi_ps_has_completed_scope (parser_state)) { + acpi_ps_pop_scope (parser_state, &op, + &walk_state->arg_types, &walk_state->arg_count); + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Popped scope, Op=%p\n", op)); + } + else { + op = NULL; + } + + } /* while parser_state->Aml */ + + + /* + * Complete the last Op (if not completed), and clear the scope stack. + * It is easily possible to end an AML "package" with an unbounded number + * of open scopes (such as when several ASL blocks are closed with + * sequential closing braces). We want to terminate each one cleanly. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "AML package complete at Op %p\n", op)); + do { + if (op) { + if (walk_state->ascending_callback != NULL) { + walk_state->op = op; + walk_state->op_info = acpi_ps_get_opcode_info (op->common.aml_opcode); + walk_state->opcode = op->common.aml_opcode; + + status = walk_state->ascending_callback (walk_state); + status = acpi_ps_next_parse_state (walk_state, op, status); + if (status == AE_CTRL_PENDING) { + status = AE_OK; + goto close_this_op; + } + + if (status == AE_CTRL_TERMINATE) { + status = AE_OK; + + /* Clean up */ + do { + if (op) { + acpi_ps_complete_this_op (walk_state, op); + } + + acpi_ps_pop_scope (parser_state, &op, + &walk_state->arg_types, &walk_state->arg_count); + + } while (op); + + return_ACPI_STATUS (status); + } + + else if (ACPI_FAILURE (status)) { + acpi_ps_complete_this_op (walk_state, op); + return_ACPI_STATUS (status); + } + } + + acpi_ps_complete_this_op (walk_state, op); + } + + acpi_ps_pop_scope (parser_state, &op, &walk_state->arg_types, + &walk_state->arg_count); + + } while (op); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_parse_aml + * + * PARAMETERS: start_scope - The starting point of the parse. Becomes the + * root of the parsed op tree. + * Aml - Pointer to the raw AML code to parse + * aml_size - Length of the AML to parse + * + * + * RETURN: Status + * + * DESCRIPTION: Parse raw AML and return a tree of ops + * + ******************************************************************************/ + +acpi_status +acpi_ps_parse_aml ( + struct acpi_walk_state *walk_state) +{ + acpi_status status; + acpi_status terminate_status; + struct acpi_thread_state *thread; + struct acpi_thread_state *prev_walk_list = acpi_gbl_current_walk_list; + struct acpi_walk_state *previous_walk_state; + + + ACPI_FUNCTION_TRACE ("ps_parse_aml"); + + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Entered with walk_state=%p Aml=%p size=%X\n", + walk_state, walk_state->parser_state.aml, walk_state->parser_state.aml_size)); + + + /* Create and initialize a new thread state */ + + thread = acpi_ut_create_thread_state (); + if (!thread) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + walk_state->thread = thread; + acpi_ds_push_walk_state (walk_state, thread); + + /* + * This global allows the AML debugger to get a handle to the currently + * executing control method. + */ + acpi_gbl_current_walk_list = thread; + + /* + * Execute the walk loop as long as there is a valid Walk State. This + * handles nested control method invocations without recursion. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "State=%p\n", walk_state)); + + status = AE_OK; + while (walk_state) { + if (ACPI_SUCCESS (status)) { + /* + * The parse_loop executes AML until the method terminates + * or calls another method. + */ + status = acpi_ps_parse_loop (walk_state); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, + "Completed one call to walk loop, %s State=%p\n", + acpi_format_exception (status), walk_state)); + + if (status == AE_CTRL_TRANSFER) { + /* + * A method call was detected. + * Transfer control to the called control method + */ + status = acpi_ds_call_control_method (thread, walk_state, NULL); + + /* + * If the transfer to the new method method call worked, a new walk + * state was created -- get it + */ + walk_state = acpi_ds_get_current_walk_state (thread); + continue; + } + else if (status == AE_CTRL_TERMINATE) { + status = AE_OK; + } + else if ((status != AE_OK) && (walk_state->method_desc)) { + ACPI_REPORT_METHOD_ERROR ("Method execution failed", + walk_state->method_node, NULL, status); + + /* Check for possible multi-thread reentrancy problem */ + + if ((status == AE_ALREADY_EXISTS) && + (!walk_state->method_desc->method.semaphore)) { + /* + * This method is marked not_serialized, but it tried to create a named + * object, causing the second thread entrance to fail. We will workaround + * this by marking the method permanently as Serialized. + */ + walk_state->method_desc->method.method_flags |= AML_METHOD_SERIALIZED; + walk_state->method_desc->method.concurrency = 1; + } + } + + if (walk_state->method_desc) { + /* Decrement the thread count on the method parse tree */ + + if (walk_state->method_desc->method.thread_count) { + walk_state->method_desc->method.thread_count--; + } + } + + /* We are done with this walk, move on to the parent if any */ + + walk_state = acpi_ds_pop_walk_state (thread); + + /* Reset the current scope to the beginning of scope stack */ + + acpi_ds_scope_stack_clear (walk_state); + + /* + * If we just returned from the execution of a control method, + * there's lots of cleanup to do + */ + if ((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == ACPI_PARSE_EXECUTE) { + terminate_status = acpi_ds_terminate_control_method (walk_state); + if (ACPI_FAILURE (terminate_status)) { + ACPI_REPORT_ERROR (( + "Could not terminate control method properly\n")); + + /* Ignore error and continue */ + } + } + + /* Delete this walk state and all linked control states */ + + acpi_ps_cleanup_scope (&walk_state->parser_state); + + previous_walk_state = walk_state; + + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "return_value=%p, implicit_value=%p State=%p\n", + walk_state->return_desc, walk_state->implicit_return_obj, walk_state)); + + /* Check if we have restarted a preempted walk */ + + walk_state = acpi_ds_get_current_walk_state (thread); + if (walk_state) { + if (ACPI_SUCCESS (status)) { + /* + * There is another walk state, restart it. + * If the method return value is not used by the parent, + * The object is deleted + */ + if (!previous_walk_state->return_desc) { + status = acpi_ds_restart_control_method (walk_state, + previous_walk_state->implicit_return_obj); + } + else { + /* + * We have a valid return value, delete any implicit + * return value. + */ + acpi_ds_clear_implicit_return (previous_walk_state); + + status = acpi_ds_restart_control_method (walk_state, + previous_walk_state->return_desc); + } + if (ACPI_SUCCESS (status)) { + walk_state->walk_type |= ACPI_WALK_METHOD_RESTART; + } + } + else { + /* On error, delete any return object */ + + acpi_ut_remove_reference (previous_walk_state->return_desc); + } + } + + /* + * Just completed a 1st-level method, save the final internal return + * value (if any) + */ + else if (previous_walk_state->caller_return_desc) { + if (previous_walk_state->implicit_return_obj) { + *(previous_walk_state->caller_return_desc) = previous_walk_state->implicit_return_obj; + } + else { + /* NULL if no return value */ + + *(previous_walk_state->caller_return_desc) = previous_walk_state->return_desc; + } + } + else { + if (previous_walk_state->return_desc) { + /* Caller doesn't want it, must delete it */ + + acpi_ut_remove_reference (previous_walk_state->return_desc); + } + if (previous_walk_state->implicit_return_obj) { + /* Caller doesn't want it, must delete it */ + + acpi_ut_remove_reference (previous_walk_state->implicit_return_obj); + } + } + + acpi_ds_delete_walk_state (previous_walk_state); + } + + /* Normal exit */ + + acpi_ex_release_all_mutexes (thread); + acpi_ut_delete_generic_state (ACPI_CAST_PTR (union acpi_generic_state, thread)); + acpi_gbl_current_walk_list = prev_walk_list; + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/parser/psscope.c b/drivers/acpi/parser/psscope.c new file mode 100644 index 00000000000..dcbed49608b --- /dev/null +++ b/drivers/acpi/parser/psscope.c @@ -0,0 +1,290 @@ +/****************************************************************************** + * + * Module Name: psscope - Parser scope stack management routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> + +#define _COMPONENT ACPI_PARSER + ACPI_MODULE_NAME ("psscope") + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_parent_scope + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Pointer to an Op object + * + * DESCRIPTION: Get parent of current op being parsed + * + ******************************************************************************/ + +union acpi_parse_object * +acpi_ps_get_parent_scope ( + struct acpi_parse_state *parser_state) +{ + return (parser_state->scope->parse_scope.op); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_has_completed_scope + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Boolean, TRUE = scope completed. + * + * DESCRIPTION: Is parsing of current argument complete? Determined by + * 1) AML pointer is at or beyond the end of the scope + * 2) The scope argument count has reached zero. + * + ******************************************************************************/ + +u8 +acpi_ps_has_completed_scope ( + struct acpi_parse_state *parser_state) +{ + return ((u8) ((parser_state->aml >= parser_state->scope->parse_scope.arg_end || + !parser_state->scope->parse_scope.arg_count))); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_init_scope + * + * PARAMETERS: parser_state - Current parser state object + * Root - the Root Node of this new scope + * + * RETURN: Status + * + * DESCRIPTION: Allocate and init a new scope object + * + ******************************************************************************/ + +acpi_status +acpi_ps_init_scope ( + struct acpi_parse_state *parser_state, + union acpi_parse_object *root_op) +{ + union acpi_generic_state *scope; + + + ACPI_FUNCTION_TRACE_PTR ("ps_init_scope", root_op); + + + scope = acpi_ut_create_generic_state (); + if (!scope) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + scope->common.data_type = ACPI_DESC_TYPE_STATE_RPSCOPE; + scope->parse_scope.op = root_op; + scope->parse_scope.arg_count = ACPI_VAR_ARGS; + scope->parse_scope.arg_end = parser_state->aml_end; + scope->parse_scope.pkg_end = parser_state->aml_end; + + parser_state->scope = scope; + parser_state->start_op = root_op; + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_push_scope + * + * PARAMETERS: parser_state - Current parser state object + * Op - Current op to be pushed + * remaining_args - List of args remaining + * arg_count - Fixed or variable number of args + * + * RETURN: Status + * + * DESCRIPTION: Push current op to begin parsing its argument + * + ******************************************************************************/ + +acpi_status +acpi_ps_push_scope ( + struct acpi_parse_state *parser_state, + union acpi_parse_object *op, + u32 remaining_args, + u32 arg_count) +{ + union acpi_generic_state *scope; + + + ACPI_FUNCTION_TRACE_PTR ("ps_push_scope", op); + + + scope = acpi_ut_create_generic_state (); + if (!scope) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + scope->common.data_type = ACPI_DESC_TYPE_STATE_PSCOPE; + scope->parse_scope.op = op; + scope->parse_scope.arg_list = remaining_args; + scope->parse_scope.arg_count = arg_count; + scope->parse_scope.pkg_end = parser_state->pkg_end; + + /* Push onto scope stack */ + + acpi_ut_push_generic_state (&parser_state->scope, scope); + + if (arg_count == ACPI_VAR_ARGS) { + /* multiple arguments */ + + scope->parse_scope.arg_end = parser_state->pkg_end; + } + else { + /* single argument */ + + scope->parse_scope.arg_end = ACPI_TO_POINTER (ACPI_MAX_PTR); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_pop_scope + * + * PARAMETERS: parser_state - Current parser state object + * Op - Where the popped op is returned + * arg_list - Where the popped "next argument" is + * returned + * arg_count - Count of objects in arg_list + * + * RETURN: Status + * + * DESCRIPTION: Return to parsing a previous op + * + ******************************************************************************/ + +void +acpi_ps_pop_scope ( + struct acpi_parse_state *parser_state, + union acpi_parse_object **op, + u32 *arg_list, + u32 *arg_count) +{ + union acpi_generic_state *scope = parser_state->scope; + + + ACPI_FUNCTION_TRACE ("ps_pop_scope"); + + + /* + * Only pop the scope if there is in fact a next scope + */ + if (scope->common.next) { + scope = acpi_ut_pop_generic_state (&parser_state->scope); + + /* return to parsing previous op */ + + *op = scope->parse_scope.op; + *arg_list = scope->parse_scope.arg_list; + *arg_count = scope->parse_scope.arg_count; + parser_state->pkg_end = scope->parse_scope.pkg_end; + + /* All done with this scope state structure */ + + acpi_ut_delete_generic_state (scope); + } + else { + /* empty parse stack, prepare to fetch next opcode */ + + *op = NULL; + *arg_list = 0; + *arg_count = 0; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Popped Op %p Args %X\n", *op, *arg_count)); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_cleanup_scope + * + * PARAMETERS: parser_state - Current parser state object + * + * RETURN: Status + * + * DESCRIPTION: Destroy available list, remaining stack levels, and return + * root scope + * + ******************************************************************************/ + +void +acpi_ps_cleanup_scope ( + struct acpi_parse_state *parser_state) +{ + union acpi_generic_state *scope; + + + ACPI_FUNCTION_TRACE_PTR ("ps_cleanup_scope", parser_state); + + + if (!parser_state) { + return_VOID; + } + + /* Delete anything on the scope stack */ + + while (parser_state->scope) { + scope = acpi_ut_pop_generic_state (&parser_state->scope); + acpi_ut_delete_generic_state (scope); + } + + return_VOID; +} + diff --git a/drivers/acpi/parser/pstree.c b/drivers/acpi/parser/pstree.c new file mode 100644 index 00000000000..2140bd1ac10 --- /dev/null +++ b/drivers/acpi/parser/pstree.c @@ -0,0 +1,327 @@ +/****************************************************************************** + * + * Module Name: pstree - Parser op tree manipulation/traversal/search + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> + +#define _COMPONENT ACPI_PARSER + ACPI_MODULE_NAME ("pstree") + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_arg + * + * PARAMETERS: Op - Get an argument for this op + * Argn - Nth argument to get + * + * RETURN: The argument (as an Op object). NULL if argument does not exist + * + * DESCRIPTION: Get the specified op's argument. + * + ******************************************************************************/ + +union acpi_parse_object * +acpi_ps_get_arg ( + union acpi_parse_object *op, + u32 argn) +{ + union acpi_parse_object *arg = NULL; + const struct acpi_opcode_info *op_info; + + + ACPI_FUNCTION_ENTRY (); + + + /* Get the info structure for this opcode */ + + op_info = acpi_ps_get_opcode_info (op->common.aml_opcode); + if (op_info->class == AML_CLASS_UNKNOWN) { + /* Invalid opcode or ASCII character */ + + return (NULL); + } + + /* Check if this opcode requires argument sub-objects */ + + if (!(op_info->flags & AML_HAS_ARGS)) { + /* Has no linked argument objects */ + + return (NULL); + } + + /* Get the requested argument object */ + + arg = op->common.value.arg; + while (arg && argn) { + argn--; + arg = arg->common.next; + } + + return (arg); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_append_arg + * + * PARAMETERS: Op - Append an argument to this Op. + * Arg - Argument Op to append + * + * RETURN: None. + * + * DESCRIPTION: Append an argument to an op's argument list (a NULL arg is OK) + * + ******************************************************************************/ + +void +acpi_ps_append_arg ( + union acpi_parse_object *op, + union acpi_parse_object *arg) +{ + union acpi_parse_object *prev_arg; + const struct acpi_opcode_info *op_info; + + + ACPI_FUNCTION_ENTRY (); + + + if (!op) { + return; + } + + /* Get the info structure for this opcode */ + + op_info = acpi_ps_get_opcode_info (op->common.aml_opcode); + if (op_info->class == AML_CLASS_UNKNOWN) { + /* Invalid opcode */ + + ACPI_REPORT_ERROR (("ps_append_arg: Invalid AML Opcode: 0x%2.2X\n", + op->common.aml_opcode)); + return; + } + + /* Check if this opcode requires argument sub-objects */ + + if (!(op_info->flags & AML_HAS_ARGS)) { + /* Has no linked argument objects */ + + return; + } + + + /* Append the argument to the linked argument list */ + + if (op->common.value.arg) { + /* Append to existing argument list */ + + prev_arg = op->common.value.arg; + while (prev_arg->common.next) { + prev_arg = prev_arg->common.next; + } + prev_arg->common.next = arg; + } + + else { + /* No argument list, this will be the first argument */ + + op->common.value.arg = arg; + } + + + /* Set the parent in this arg and any args linked after it */ + + while (arg) { + arg->common.parent = op; + arg = arg->common.next; + } +} + + +#ifdef ACPI_FUTURE_USAGE + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_child + * + * PARAMETERS: Op - Get the child of this Op + * + * RETURN: Child Op, Null if none is found. + * + * DESCRIPTION: Get op's children or NULL if none + * + ******************************************************************************/ +union acpi_parse_object * +acpi_ps_get_child ( + union acpi_parse_object *op) +{ + union acpi_parse_object *child = NULL; + + + ACPI_FUNCTION_ENTRY (); + + + switch (op->common.aml_opcode) { + case AML_SCOPE_OP: + case AML_ELSE_OP: + case AML_DEVICE_OP: + case AML_THERMAL_ZONE_OP: + case AML_INT_METHODCALL_OP: + + child = acpi_ps_get_arg (op, 0); + break; + + + case AML_BUFFER_OP: + case AML_PACKAGE_OP: + case AML_METHOD_OP: + case AML_IF_OP: + case AML_WHILE_OP: + case AML_FIELD_OP: + + child = acpi_ps_get_arg (op, 1); + break; + + + case AML_POWER_RES_OP: + case AML_INDEX_FIELD_OP: + + child = acpi_ps_get_arg (op, 2); + break; + + + case AML_PROCESSOR_OP: + case AML_BANK_FIELD_OP: + + child = acpi_ps_get_arg (op, 3); + break; + + + default: + /* All others have no children */ + break; + } + + return (child); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_get_depth_next + * + * PARAMETERS: Origin - Root of subtree to search + * Op - Last (previous) Op that was found + * + * RETURN: Next Op found in the search. + * + * DESCRIPTION: Get next op in tree (walking the tree in depth-first order) + * Return NULL when reaching "origin" or when walking up from root + * + ******************************************************************************/ + +union acpi_parse_object * +acpi_ps_get_depth_next ( + union acpi_parse_object *origin, + union acpi_parse_object *op) +{ + union acpi_parse_object *next = NULL; + union acpi_parse_object *parent; + union acpi_parse_object *arg; + + + ACPI_FUNCTION_ENTRY (); + + + if (!op) { + return (NULL); + } + + /* look for an argument or child */ + + next = acpi_ps_get_arg (op, 0); + if (next) { + return (next); + } + + /* look for a sibling */ + + next = op->common.next; + if (next) { + return (next); + } + + /* look for a sibling of parent */ + + parent = op->common.parent; + + while (parent) { + arg = acpi_ps_get_arg (parent, 0); + while (arg && (arg != origin) && (arg != op)) { + arg = arg->common.next; + } + + if (arg == origin) { + /* reached parent of origin, end search */ + + return (NULL); + } + + if (parent->common.next) { + /* found sibling of parent */ + + return (parent->common.next); + } + + op = parent; + parent = parent->common.parent; + } + + return (next); +} + +#endif /* ACPI_FUTURE_USAGE */ + diff --git a/drivers/acpi/parser/psutils.c b/drivers/acpi/parser/psutils.c new file mode 100644 index 00000000000..b3597cb19f8 --- /dev/null +++ b/drivers/acpi/parser/psutils.c @@ -0,0 +1,309 @@ +/****************************************************************************** + * + * Module Name: psutils - Parser miscellaneous utilities (Parser only) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_PARSER + ACPI_MODULE_NAME ("psutils") + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_create_scope_op + * + * PARAMETERS: None + * + * RETURN: scope_op + * + * DESCRIPTION: Create a Scope and associated namepath op with the root name + * + ******************************************************************************/ + +union acpi_parse_object * +acpi_ps_create_scope_op ( + void) +{ + union acpi_parse_object *scope_op; + + + scope_op = acpi_ps_alloc_op (AML_SCOPE_OP); + if (!scope_op) { + return (NULL); + } + + + scope_op->named.name = ACPI_ROOT_NAME; + return (scope_op); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_init_op + * + * PARAMETERS: Op - A newly allocated Op object + * Opcode - Opcode to store in the Op + * + * RETURN: Status + * + * DESCRIPTION: Allocate an acpi_op, choose op type (and thus size) based on + * opcode + * + ******************************************************************************/ + +void +acpi_ps_init_op ( + union acpi_parse_object *op, + u16 opcode) +{ + ACPI_FUNCTION_ENTRY (); + + + op->common.data_type = ACPI_DESC_TYPE_PARSER; + op->common.aml_opcode = opcode; + + ACPI_DISASM_ONLY_MEMBERS (ACPI_STRNCPY (op->common.aml_op_name, + (acpi_ps_get_opcode_info (opcode))->name, sizeof (op->common.aml_op_name))); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_alloc_op + * + * PARAMETERS: Opcode - Opcode that will be stored in the new Op + * + * RETURN: Pointer to the new Op. + * + * DESCRIPTION: Allocate an acpi_op, choose op type (and thus size) based on + * opcode. A cache of opcodes is available for the pure + * GENERIC_OP, since this is by far the most commonly used. + * + ******************************************************************************/ + +union acpi_parse_object* +acpi_ps_alloc_op ( + u16 opcode) +{ + union acpi_parse_object *op; + const struct acpi_opcode_info *op_info; + u8 flags = ACPI_PARSEOP_GENERIC; + + + ACPI_FUNCTION_ENTRY (); + + + op_info = acpi_ps_get_opcode_info (opcode); + + /* Determine type of parse_op required */ + + if (op_info->flags & AML_DEFER) { + flags = ACPI_PARSEOP_DEFERRED; + } + else if (op_info->flags & AML_NAMED) { + flags = ACPI_PARSEOP_NAMED; + } + else if (opcode == AML_INT_BYTELIST_OP) { + flags = ACPI_PARSEOP_BYTELIST; + } + + /* Allocate the minimum required size object */ + + if (flags == ACPI_PARSEOP_GENERIC) { + /* The generic op (default) is by far the most common (16 to 1) */ + + op = acpi_ut_acquire_from_cache (ACPI_MEM_LIST_PSNODE); + } + else { + /* Extended parseop */ + + op = acpi_ut_acquire_from_cache (ACPI_MEM_LIST_PSNODE_EXT); + } + + /* Initialize the Op */ + + if (op) { + acpi_ps_init_op (op, opcode); + op->common.flags = flags; + } + + return (op); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_free_op + * + * PARAMETERS: Op - Op to be freed + * + * RETURN: None. + * + * DESCRIPTION: Free an Op object. Either put it on the GENERIC_OP cache list + * or actually free it. + * + ******************************************************************************/ + +void +acpi_ps_free_op ( + union acpi_parse_object *op) +{ + ACPI_FUNCTION_NAME ("ps_free_op"); + + + if (op->common.aml_opcode == AML_INT_RETURN_VALUE_OP) { + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Free retval op: %p\n", op)); + } + + if (op->common.flags & ACPI_PARSEOP_GENERIC) { + acpi_ut_release_to_cache (ACPI_MEM_LIST_PSNODE, op); + } + else { + acpi_ut_release_to_cache (ACPI_MEM_LIST_PSNODE_EXT, op); + } +} + + +#ifdef ACPI_ENABLE_OBJECT_CACHE +/******************************************************************************* + * + * FUNCTION: acpi_ps_delete_parse_cache + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Free all objects that are on the parse cache list. + * + ******************************************************************************/ + +void +acpi_ps_delete_parse_cache ( + void) +{ + ACPI_FUNCTION_TRACE ("ps_delete_parse_cache"); + + + acpi_ut_delete_generic_cache (ACPI_MEM_LIST_PSNODE); + acpi_ut_delete_generic_cache (ACPI_MEM_LIST_PSNODE_EXT); + return_VOID; +} +#endif + + +/******************************************************************************* + * + * FUNCTION: Utility functions + * + * DESCRIPTION: Low level character and object functions + * + ******************************************************************************/ + + +/* + * Is "c" a namestring lead character? + */ +u8 +acpi_ps_is_leading_char ( + u32 c) +{ + return ((u8) (c == '_' || (c >= 'A' && c <= 'Z'))); +} + + +/* + * Is "c" a namestring prefix character? + */ +u8 +acpi_ps_is_prefix_char ( + u32 c) +{ + return ((u8) (c == '\\' || c == '^')); +} + + +/* + * Get op's name (4-byte name segment) or 0 if unnamed + */ +#ifdef ACPI_FUTURE_USAGE +u32 +acpi_ps_get_name ( + union acpi_parse_object *op) +{ + + + /* The "generic" object has no name associated with it */ + + if (op->common.flags & ACPI_PARSEOP_GENERIC) { + return (0); + } + + /* Only the "Extended" parse objects have a name */ + + return (op->named.name); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/* + * Set op's name + */ +void +acpi_ps_set_name ( + union acpi_parse_object *op, + u32 name) +{ + + /* The "generic" object has no name associated with it */ + + if (op->common.flags & ACPI_PARSEOP_GENERIC) { + return; + } + + op->named.name = name; +} + diff --git a/drivers/acpi/parser/pswalk.c b/drivers/acpi/parser/pswalk.c new file mode 100644 index 00000000000..110d2ce917b --- /dev/null +++ b/drivers/acpi/parser/pswalk.c @@ -0,0 +1,115 @@ +/****************************************************************************** + * + * Module Name: pswalk - Parser routines to walk parsed op tree(s) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> + +#define _COMPONENT ACPI_PARSER + ACPI_MODULE_NAME ("pswalk") + + +/******************************************************************************* + * + * FUNCTION: acpi_ps_delete_parse_tree + * + * PARAMETERS: subtree_root - Root of tree (or subtree) to delete + * + * RETURN: None + * + * DESCRIPTION: Delete a portion of or an entire parse tree. + * + ******************************************************************************/ + +void +acpi_ps_delete_parse_tree ( + union acpi_parse_object *subtree_root) +{ + union acpi_parse_object *op = subtree_root; + union acpi_parse_object *next = NULL; + union acpi_parse_object *parent = NULL; + + + ACPI_FUNCTION_TRACE_PTR ("ps_delete_parse_tree", subtree_root); + + + /* Visit all nodes in the subtree */ + + while (op) { + /* Check if we are not ascending */ + + if (op != parent) { + /* Look for an argument or child of the current op */ + + next = acpi_ps_get_arg (op, 0); + if (next) { + /* Still going downward in tree (Op is not completed yet) */ + + op = next; + continue; + } + } + + /* + * No more children, this Op is complete. + */ + next = op->common.next; + parent = op->common.parent; + + acpi_ps_free_op (op); + + /* + * If we are back to the starting point, the walk is complete. + */ + if (op == subtree_root) { + return_VOID; + } + if (next) { + op = next; + } + else { + op = parent; + } + } + return_VOID; +} diff --git a/drivers/acpi/parser/psxface.c b/drivers/acpi/parser/psxface.c new file mode 100644 index 00000000000..b318ad24726 --- /dev/null +++ b/drivers/acpi/parser/psxface.c @@ -0,0 +1,243 @@ +/****************************************************************************** + * + * Module Name: psxface - Parser external interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acparser.h> +#include <acpi/acdispat.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_PARSER + ACPI_MODULE_NAME ("psxface") + + +/******************************************************************************* + * + * FUNCTION: acpi_psx_execute + * + * PARAMETERS: Info->Node - A method object containing both the AML + * address and length. + * **Params - List of parameters to pass to method, + * terminated by NULL. Params itself may be + * NULL if no parameters are being passed. + * **return_obj_desc - Return object from execution of the + * method. + * + * RETURN: Status + * + * DESCRIPTION: Execute a control method + * + ******************************************************************************/ + +acpi_status +acpi_psx_execute ( + struct acpi_parameter_info *info) +{ + acpi_status status; + union acpi_operand_object *obj_desc; + u32 i; + union acpi_parse_object *op; + struct acpi_walk_state *walk_state; + + + ACPI_FUNCTION_TRACE ("psx_execute"); + + + /* Validate the Node and get the attached object */ + + if (!info || !info->node) { + return_ACPI_STATUS (AE_NULL_ENTRY); + } + + obj_desc = acpi_ns_get_attached_object (info->node); + if (!obj_desc) { + return_ACPI_STATUS (AE_NULL_OBJECT); + } + + /* Init for new method, wait on concurrency semaphore */ + + status = acpi_ds_begin_method_execution (info->node, obj_desc, NULL); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if ((info->parameter_type == ACPI_PARAM_ARGS) && + (info->parameters)) { + /* + * The caller "owns" the parameters, so give each one an extra + * reference + */ + for (i = 0; info->parameters[i]; i++) { + acpi_ut_add_reference (info->parameters[i]); + } + } + + /* + * 1) Perform the first pass parse of the method to enter any + * named objects that it creates into the namespace + */ + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, + "**** Begin Method Parse **** Entry=%p obj=%p\n", + info->node, obj_desc)); + + /* Create and init a Root Node */ + + op = acpi_ps_create_scope_op (); + if (!op) { + status = AE_NO_MEMORY; + goto cleanup1; + } + + /* + * Get a new owner_id for objects created by this method. Namespace + * objects (such as Operation Regions) can be created during the + * first pass parse. + */ + obj_desc->method.owning_id = acpi_ut_allocate_owner_id (ACPI_OWNER_TYPE_METHOD); + + /* Create and initialize a new walk state */ + + walk_state = acpi_ds_create_walk_state (obj_desc->method.owning_id, + NULL, NULL, NULL); + if (!walk_state) { + status = AE_NO_MEMORY; + goto cleanup2; + } + + status = acpi_ds_init_aml_walk (walk_state, op, info->node, + obj_desc->method.aml_start, + obj_desc->method.aml_length, NULL, 1); + if (ACPI_FAILURE (status)) { + goto cleanup3; + } + + /* Parse the AML */ + + status = acpi_ps_parse_aml (walk_state); + acpi_ps_delete_parse_tree (op); + if (ACPI_FAILURE (status)) { + goto cleanup1; /* Walk state is already deleted */ + } + + /* + * 2) Execute the method. Performs second pass parse simultaneously + */ + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, + "**** Begin Method Execution **** Entry=%p obj=%p\n", + info->node, obj_desc)); + + /* Create and init a Root Node */ + + op = acpi_ps_create_scope_op (); + if (!op) { + status = AE_NO_MEMORY; + goto cleanup1; + } + + /* Init new op with the method name and pointer back to the NS node */ + + acpi_ps_set_name (op, info->node->name.integer); + op->common.node = info->node; + + /* Create and initialize a new walk state */ + + walk_state = acpi_ds_create_walk_state (0, NULL, NULL, NULL); + if (!walk_state) { + status = AE_NO_MEMORY; + goto cleanup2; + } + + status = acpi_ds_init_aml_walk (walk_state, op, info->node, + obj_desc->method.aml_start, + obj_desc->method.aml_length, info, 3); + if (ACPI_FAILURE (status)) { + goto cleanup3; + } + + /* + * The walk of the parse tree is where we actually execute the method + */ + status = acpi_ps_parse_aml (walk_state); + goto cleanup2; /* Walk state already deleted */ + + +cleanup3: + acpi_ds_delete_walk_state (walk_state); + +cleanup2: + acpi_ps_delete_parse_tree (op); + +cleanup1: + if ((info->parameter_type == ACPI_PARAM_ARGS) && + (info->parameters)) { + /* Take away the extra reference that we gave the parameters above */ + + for (i = 0; info->parameters[i]; i++) { + /* Ignore errors, just do them all */ + + (void) acpi_ut_update_object_reference (info->parameters[i], REF_DECREMENT); + } + } + + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * If the method has returned an object, signal this to the caller with + * a control exception code + */ + if (info->return_object) { + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Method returned obj_desc=%p\n", + info->return_object)); + ACPI_DUMP_STACK_ENTRY (info->return_object); + + status = AE_CTRL_RETURN_VALUE; + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c new file mode 100644 index 00000000000..5d19b39e9e2 --- /dev/null +++ b/drivers/acpi/pci_bind.c @@ -0,0 +1,384 @@ +/* + * pci_bind.c - ACPI PCI Device Binding ($Revision: 2 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/spinlock.h> +#include <linux/pm.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define _COMPONENT ACPI_PCI_COMPONENT +ACPI_MODULE_NAME ("pci_bind") + +struct acpi_pci_data { + struct acpi_pci_id id; + struct pci_bus *bus; + struct pci_dev *dev; +}; + + +void +acpi_pci_data_handler ( + acpi_handle handle, + u32 function, + void *context) +{ + ACPI_FUNCTION_TRACE("acpi_pci_data_handler"); + + /* TBD: Anything we need to do here? */ + + return_VOID; +} + + +/** + * acpi_os_get_pci_id + * ------------------ + * This function is used by the ACPI Interpreter (a.k.a. Core Subsystem) + * to resolve PCI information for ACPI-PCI devices defined in the namespace. + * This typically occurs when resolving PCI operation region information. + */ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_os_get_pci_id ( + acpi_handle handle, + struct acpi_pci_id *id) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_device *device = NULL; + struct acpi_pci_data *data = NULL; + + ACPI_FUNCTION_TRACE("acpi_os_get_pci_id"); + + if (!id) + return_ACPI_STATUS(AE_BAD_PARAMETER); + + result = acpi_bus_get_device(handle, &device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Invalid ACPI Bus context for device %s\n", + acpi_device_bid(device))); + return_ACPI_STATUS(AE_NOT_EXIST); + } + + status = acpi_get_data(handle, acpi_pci_data_handler, (void**) &data); + if (ACPI_FAILURE(status) || !data || !data->dev) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Invalid ACPI-PCI context for device %s\n", + acpi_device_bid(device))); + return_ACPI_STATUS(status); + } + + *id = data->id; + + /* + id->segment = data->id.segment; + id->bus = data->id.bus; + id->device = data->id.device; + id->function = data->id.function; + */ + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Device %s has PCI address %02x:%02x:%02x.%02x\n", + acpi_device_bid(device), id->segment, id->bus, + id->device, id->function)); + + return_ACPI_STATUS(AE_OK); +} +#endif /* ACPI_FUTURE_USAGE */ + + +int +acpi_pci_bind ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_pci_data *data = NULL; + struct acpi_pci_data *pdata = NULL; + char *pathname = NULL; + struct acpi_buffer buffer = {0, NULL}; + acpi_handle handle = NULL; + + ACPI_FUNCTION_TRACE("acpi_pci_bind"); + + if (!device || !device->parent) + return_VALUE(-EINVAL); + + pathname = kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); + if(!pathname) + return_VALUE(-ENOMEM); + memset(pathname, 0, ACPI_PATHNAME_MAX); + buffer.length = ACPI_PATHNAME_MAX; + buffer.pointer = pathname; + + data = kmalloc(sizeof(struct acpi_pci_data), GFP_KERNEL); + if (!data){ + kfree (pathname); + return_VALUE(-ENOMEM); + } + memset(data, 0, sizeof(struct acpi_pci_data)); + + acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI device [%s]...\n", + pathname)); + + /* + * Segment & Bus + * ------------- + * These are obtained via the parent device's ACPI-PCI context. + */ + status = acpi_get_data(device->parent->handle, acpi_pci_data_handler, + (void**) &pdata); + if (ACPI_FAILURE(status) || !pdata || !pdata->bus) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Invalid ACPI-PCI context for parent device %s\n", + acpi_device_bid(device->parent))); + result = -ENODEV; + goto end; + } + data->id.segment = pdata->id.segment; + data->id.bus = pdata->bus->number; + + /* + * Device & Function + * ----------------- + * These are simply obtained from the device's _ADR method. Note + * that a value of zero is valid. + */ + data->id.device = device->pnp.bus_address >> 16; + data->id.function = device->pnp.bus_address & 0xFFFF; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "...to %02x:%02x:%02x.%02x\n", + data->id.segment, data->id.bus, data->id.device, + data->id.function)); + + /* + * TBD: Support slot devices (e.g. function=0xFFFF). + */ + + /* + * Locate PCI Device + * ----------------- + * Locate matching device in PCI namespace. If it doesn't exist + * this typically means that the device isn't currently inserted + * (e.g. docking station, port replicator, etc.). + */ + data->dev = pci_find_slot(data->id.bus, PCI_DEVFN(data->id.device, data->id.function)); + if (!data->dev) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Device %02x:%02x:%02x.%02x not present in PCI namespace\n", + data->id.segment, data->id.bus, + data->id.device, data->id.function)); + result = -ENODEV; + goto end; + } + if (!data->dev->bus) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Device %02x:%02x:%02x.%02x has invalid 'bus' field\n", + data->id.segment, data->id.bus, + data->id.device, data->id.function)); + result = -ENODEV; + goto end; + } + + /* + * PCI Bridge? + * ----------- + * If so, set the 'bus' field and install the 'bind' function to + * facilitate callbacks for all of its children. + */ + if (data->dev->subordinate) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Device %02x:%02x:%02x.%02x is a PCI bridge\n", + data->id.segment, data->id.bus, + data->id.device, data->id.function)); + data->bus = data->dev->subordinate; + device->ops.bind = acpi_pci_bind; + device->ops.unbind = acpi_pci_unbind; + } + + /* + * Attach ACPI-PCI Context + * ----------------------- + * Thus binding the ACPI and PCI devices. + */ + status = acpi_attach_data(device->handle, acpi_pci_data_handler, data); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to attach ACPI-PCI context to device %s\n", + acpi_device_bid(device))); + result = -ENODEV; + goto end; + } + + /* + * PCI Routing Table + * ----------------- + * Evaluate and parse _PRT, if exists. This code is independent of + * PCI bridges (above) to allow parsing of _PRT objects within the + * scope of non-bridge devices. Note that _PRTs within the scope of + * a PCI bridge assume the bridge's subordinate bus number. + * + * TBD: Can _PRTs exist within the scope of non-bridge PCI devices? + */ + status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); + if (ACPI_SUCCESS(status)) { + if (data->bus) /* PCI-PCI bridge */ + acpi_pci_irq_add_prt(device->handle, data->id.segment, + data->bus->number); + else /* non-bridge PCI device */ + acpi_pci_irq_add_prt(device->handle, data->id.segment, + data->id.bus); + } + +end: + kfree(pathname); + if (result) + kfree(data); + + return_VALUE(result); +} + +int acpi_pci_unbind( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_pci_data *data = NULL; + char *pathname = NULL; + struct acpi_buffer buffer = {0, NULL}; + + ACPI_FUNCTION_TRACE("acpi_pci_unbind"); + + if (!device || !device->parent) + return_VALUE(-EINVAL); + + pathname = (char *) kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); + if(!pathname) + return_VALUE(-ENOMEM); + memset(pathname, 0, ACPI_PATHNAME_MAX); + + buffer.length = ACPI_PATHNAME_MAX; + buffer.pointer = pathname; + acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unbinding PCI device [%s]...\n", + pathname)); + kfree(pathname); + + status = acpi_get_data(device->handle, acpi_pci_data_handler, (void**)&data); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to get data from device %s\n", + acpi_device_bid(device))); + result = -ENODEV; + goto end; + } + + status = acpi_detach_data(device->handle, acpi_pci_data_handler); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to detach data from device %s\n", + acpi_device_bid(device))); + result = -ENODEV; + goto end; + } + if (data->dev->subordinate) { + acpi_pci_irq_del_prt(data->id.segment, data->bus->number); + } + kfree(data); + +end: + return_VALUE(result); +} + +int +acpi_pci_bind_root ( + struct acpi_device *device, + struct acpi_pci_id *id, + struct pci_bus *bus) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_pci_data *data = NULL; + char *pathname = NULL; + struct acpi_buffer buffer = {0, NULL}; + + ACPI_FUNCTION_TRACE("acpi_pci_bind_root"); + + pathname = (char *)kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); + if(!pathname) + return_VALUE(-ENOMEM); + memset(pathname, 0, ACPI_PATHNAME_MAX); + + buffer.length = ACPI_PATHNAME_MAX; + buffer.pointer = pathname; + + if (!device || !id || !bus){ + kfree(pathname); + return_VALUE(-EINVAL); + } + + data = kmalloc(sizeof(struct acpi_pci_data), GFP_KERNEL); + if (!data){ + kfree(pathname); + return_VALUE(-ENOMEM); + } + memset(data, 0, sizeof(struct acpi_pci_data)); + + data->id = *id; + data->bus = bus; + device->ops.bind = acpi_pci_bind; + device->ops.unbind = acpi_pci_unbind; + + acpi_get_name(device->handle, ACPI_FULL_PATHNAME, &buffer); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Binding PCI root bridge [%s] to " + "%02x:%02x\n", pathname, id->segment, id->bus)); + + status = acpi_attach_data(device->handle, acpi_pci_data_handler, data); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to attach ACPI-PCI context to device %s\n", + pathname)); + result = -ENODEV; + goto end; + } + +end: + kfree(pathname); + if (result != 0) + kfree(data); + + return_VALUE(result); +} diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c new file mode 100644 index 00000000000..12b0eea6340 --- /dev/null +++ b/drivers/acpi/pci_irq.c @@ -0,0 +1,518 @@ +/* + * pci_irq.c - ACPI PCI Interrupt Routing ($Revision: 11 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/config.h> + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/spinlock.h> +#include <linux/pm.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define _COMPONENT ACPI_PCI_COMPONENT +ACPI_MODULE_NAME ("pci_irq") + +static struct acpi_prt_list acpi_prt; +static DEFINE_SPINLOCK(acpi_prt_lock); + +/* -------------------------------------------------------------------------- + PCI IRQ Routing Table (PRT) Support + -------------------------------------------------------------------------- */ + +static struct acpi_prt_entry * +acpi_pci_irq_find_prt_entry ( + int segment, + int bus, + int device, + int pin) +{ + struct list_head *node = NULL; + struct acpi_prt_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_pci_irq_find_prt_entry"); + + if (!acpi_prt.count) + return_PTR(NULL); + + /* + * Parse through all PRT entries looking for a match on the specified + * PCI device's segment, bus, device, and pin (don't care about func). + * + */ + spin_lock(&acpi_prt_lock); + list_for_each(node, &acpi_prt.entries) { + entry = list_entry(node, struct acpi_prt_entry, node); + if ((segment == entry->id.segment) + && (bus == entry->id.bus) + && (device == entry->id.device) + && (pin == entry->pin)) { + spin_unlock(&acpi_prt_lock); + return_PTR(entry); + } + } + + spin_unlock(&acpi_prt_lock); + return_PTR(NULL); +} + + +static int +acpi_pci_irq_add_entry ( + acpi_handle handle, + int segment, + int bus, + struct acpi_pci_routing_table *prt) +{ + struct acpi_prt_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_pci_irq_add_entry"); + + if (!prt) + return_VALUE(-EINVAL); + + entry = kmalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL); + if (!entry) + return_VALUE(-ENOMEM); + memset(entry, 0, sizeof(struct acpi_prt_entry)); + + entry->id.segment = segment; + entry->id.bus = bus; + entry->id.device = (prt->address >> 16) & 0xFFFF; + entry->id.function = prt->address & 0xFFFF; + entry->pin = prt->pin; + + /* + * Type 1: Dynamic + * --------------- + * The 'source' field specifies the PCI interrupt link device used to + * configure the IRQ assigned to this slot|dev|pin. The 'source_index' + * indicates which resource descriptor in the resource template (of + * the link device) this interrupt is allocated from. + * + * NOTE: Don't query the Link Device for IRQ information at this time + * because Link Device enumeration may not have occurred yet + * (e.g. exists somewhere 'below' this _PRT entry in the ACPI + * namespace). + */ + if (prt->source[0]) { + acpi_get_handle(handle, prt->source, &entry->link.handle); + entry->link.index = prt->source_index; + } + /* + * Type 2: Static + * -------------- + * The 'source' field is NULL, and the 'source_index' field specifies + * the IRQ value, which is hardwired to specific interrupt inputs on + * the interrupt controller. + */ + else + entry->link.index = prt->source_index; + + ACPI_DEBUG_PRINT_RAW((ACPI_DB_INFO, + " %02X:%02X:%02X[%c] -> %s[%d]\n", + entry->id.segment, entry->id.bus, entry->id.device, + ('A' + entry->pin), prt->source, entry->link.index)); + + spin_lock(&acpi_prt_lock); + list_add_tail(&entry->node, &acpi_prt.entries); + acpi_prt.count++; + spin_unlock(&acpi_prt_lock); + + return_VALUE(0); +} + + +static void +acpi_pci_irq_del_entry ( + int segment, + int bus, + struct acpi_prt_entry *entry) +{ + if (segment == entry->id.segment && bus == entry->id.bus){ + acpi_prt.count--; + list_del(&entry->node); + kfree(entry); + } +} + + +int +acpi_pci_irq_add_prt ( + acpi_handle handle, + int segment, + int bus) +{ + acpi_status status = AE_OK; + char *pathname = NULL; + struct acpi_buffer buffer = {0, NULL}; + struct acpi_pci_routing_table *prt = NULL; + struct acpi_pci_routing_table *entry = NULL; + static int first_time = 1; + + ACPI_FUNCTION_TRACE("acpi_pci_irq_add_prt"); + + pathname = (char *) kmalloc(ACPI_PATHNAME_MAX, GFP_KERNEL); + if(!pathname) + return_VALUE(-ENOMEM); + memset(pathname, 0, ACPI_PATHNAME_MAX); + + if (first_time) { + acpi_prt.count = 0; + INIT_LIST_HEAD(&acpi_prt.entries); + first_time = 0; + } + + /* + * NOTE: We're given a 'handle' to the _PRT object's parent device + * (either a PCI root bridge or PCI-PCI bridge). + */ + + buffer.length = ACPI_PATHNAME_MAX; + buffer.pointer = pathname; + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + + printk(KERN_DEBUG "ACPI: PCI Interrupt Routing Table [%s._PRT]\n", + pathname); + + /* + * Evaluate this _PRT and add its entries to our global list (acpi_prt). + */ + + buffer.length = 0; + buffer.pointer = NULL; + kfree(pathname); + status = acpi_get_irq_routing_table(handle, &buffer); + if (status != AE_BUFFER_OVERFLOW) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRT [%s]\n", + acpi_format_exception(status))); + return_VALUE(-ENODEV); + } + + prt = kmalloc(buffer.length, GFP_KERNEL); + if (!prt){ + return_VALUE(-ENOMEM); + } + memset(prt, 0, buffer.length); + buffer.pointer = prt; + + status = acpi_get_irq_routing_table(handle, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRT [%s]\n", + acpi_format_exception(status))); + kfree(buffer.pointer); + return_VALUE(-ENODEV); + } + + entry = prt; + + while (entry && (entry->length > 0)) { + acpi_pci_irq_add_entry(handle, segment, bus, entry); + entry = (struct acpi_pci_routing_table *) + ((unsigned long) entry + entry->length); + } + + kfree(prt); + + return_VALUE(0); +} + +void +acpi_pci_irq_del_prt (int segment, int bus) +{ + struct list_head *node = NULL, *n = NULL; + struct acpi_prt_entry *entry = NULL; + + if (!acpi_prt.count) { + return; + } + + printk(KERN_DEBUG "ACPI: Delete PCI Interrupt Routing Table for %x:%x\n", + segment, bus); + spin_lock(&acpi_prt_lock); + list_for_each_safe(node, n, &acpi_prt.entries) { + entry = list_entry(node, struct acpi_prt_entry, node); + + acpi_pci_irq_del_entry(segment, bus, entry); + } + spin_unlock(&acpi_prt_lock); +} +/* -------------------------------------------------------------------------- + PCI Interrupt Routing Support + -------------------------------------------------------------------------- */ + +/* + * acpi_pci_irq_lookup + * success: return IRQ >= 0 + * failure: return -1 + */ +static int +acpi_pci_irq_lookup ( + struct pci_bus *bus, + int device, + int pin, + int *edge_level, + int *active_high_low, + char **link) +{ + struct acpi_prt_entry *entry = NULL; + int segment = pci_domain_nr(bus); + int bus_nr = bus->number; + int irq; + + ACPI_FUNCTION_TRACE("acpi_pci_irq_lookup"); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Searching for PRT entry for %02x:%02x:%02x[%c]\n", + segment, bus_nr, device, ('A' + pin))); + + entry = acpi_pci_irq_find_prt_entry(segment, bus_nr, device, pin); + if (!entry) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "PRT entry not found\n")); + return_VALUE(-1); + } + + if (entry->link.handle) { + irq = acpi_pci_link_get_irq(entry->link.handle, + entry->link.index, edge_level, active_high_low, link); + if (irq < 0) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ link routing entry\n")); + return_VALUE(-1); + } + } else { + irq = entry->link.index; + *edge_level = ACPI_LEVEL_SENSITIVE; + *active_high_low = ACPI_ACTIVE_LOW; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found IRQ %d\n", irq)); + + return_VALUE(irq); +} + +/* + * acpi_pci_irq_derive + * success: return IRQ >= 0 + * failure: return < 0 + */ +static int +acpi_pci_irq_derive ( + struct pci_dev *dev, + int pin, + int *edge_level, + int *active_high_low, + char **link) +{ + struct pci_dev *bridge = dev; + int irq = -1; + u8 bridge_pin = 0; + + ACPI_FUNCTION_TRACE("acpi_pci_irq_derive"); + + if (!dev) + return_VALUE(-EINVAL); + + /* + * Attempt to derive an IRQ for this device from a parent bridge's + * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). + */ + while (irq < 0 && bridge->bus->self) { + pin = (pin + PCI_SLOT(bridge->devfn)) % 4; + bridge = bridge->bus->self; + + if ((bridge->class >> 8) == PCI_CLASS_BRIDGE_CARDBUS) { + /* PC card has the same IRQ as its cardbridge */ + pci_read_config_byte(bridge, PCI_INTERRUPT_PIN, &bridge_pin); + if (!bridge_pin) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No interrupt pin configured for device %s\n", pci_name(bridge))); + return_VALUE(-1); + } + /* Pin is from 0 to 3 */ + bridge_pin --; + pin = bridge_pin; + } + + irq = acpi_pci_irq_lookup(bridge->bus, PCI_SLOT(bridge->devfn), + pin, edge_level, active_high_low, link); + } + + if (irq < 0) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to derive IRQ for device %s\n", pci_name(dev))); + return_VALUE(-1); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derive IRQ %d for device %s from %s\n", + irq, pci_name(dev), pci_name(bridge))); + + return_VALUE(irq); +} + +/* + * acpi_pci_irq_enable + * success: return 0 + * failure: return < 0 + */ + +int +acpi_pci_irq_enable ( + struct pci_dev *dev) +{ + int irq = 0; + u8 pin = 0; + int edge_level = ACPI_LEVEL_SENSITIVE; + int active_high_low = ACPI_ACTIVE_LOW; + extern int via_interrupt_line_quirk; + char *link = NULL; + + ACPI_FUNCTION_TRACE("acpi_pci_irq_enable"); + + if (!dev) + return_VALUE(-EINVAL); + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No interrupt pin configured for device %s\n", pci_name(dev))); + return_VALUE(0); + } + pin--; + + if (!dev->bus) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid (NULL) 'bus' field\n")); + return_VALUE(-ENODEV); + } + + /* + * First we check the PCI IRQ routing table (PRT) for an IRQ. PRT + * values override any BIOS-assigned IRQs set during boot. + */ + irq = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, + &edge_level, &active_high_low, &link); + + /* + * If no PRT entry was found, we'll try to derive an IRQ from the + * device's parent bridge. + */ + if (irq < 0) + irq = acpi_pci_irq_derive(dev, pin, &edge_level, + &active_high_low, &link); + + /* + * No IRQ known to the ACPI subsystem - maybe the BIOS / + * driver reported one, then use it. Exit in any case. + */ + if (irq < 0) { + printk(KERN_WARNING PREFIX "PCI Interrupt %s[%c]: no GSI", + pci_name(dev), ('A' + pin)); + /* Interrupt Line values above 0xF are forbidden */ + if (dev->irq >= 0 && (dev->irq <= 0xF)) { + printk(" - using IRQ %d\n", dev->irq); + return_VALUE(0); + } + else { + printk("\n"); + return_VALUE(0); + } + } + + if (via_interrupt_line_quirk) + pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq & 15); + + dev->irq = acpi_register_gsi(irq, edge_level, active_high_low); + + printk(KERN_INFO PREFIX "PCI Interrupt %s[%c] -> ", + pci_name(dev), 'A' + pin); + + if (link) + printk("Link [%s] -> ", link); + + printk("GSI %u (%s, %s) -> IRQ %d\n", irq, + (edge_level == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", + (active_high_low == ACPI_ACTIVE_LOW) ? "low" : "high", + dev->irq); + + return_VALUE(0); +} +EXPORT_SYMBOL(acpi_pci_irq_enable); + + +#ifdef CONFIG_ACPI_DEALLOCATE_IRQ +void +acpi_pci_irq_disable ( + struct pci_dev *dev) +{ + int gsi = 0; + u8 pin = 0; + int edge_level = ACPI_LEVEL_SENSITIVE; + int active_high_low = ACPI_ACTIVE_LOW; + + ACPI_FUNCTION_TRACE("acpi_pci_irq_disable"); + + if (!dev) + return_VOID; + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) + return_VOID; + pin--; + + if (!dev->bus) + return_VOID; + + /* + * First we check the PCI IRQ routing table (PRT) for an IRQ. + */ + gsi = acpi_pci_irq_lookup(dev->bus, PCI_SLOT(dev->devfn), pin, + &edge_level, &active_high_low, NULL); + /* + * If no PRT entry was found, we'll try to derive an IRQ from the + * device's parent bridge. + */ + if (gsi < 0) + gsi = acpi_pci_irq_derive(dev, pin, + &edge_level, &active_high_low, NULL); + if (gsi < 0) + return_VOID; + + /* + * TBD: It might be worth clearing dev->irq by magic constant + * (e.g. PCI_UNDEFINED_IRQ). + */ + + printk(KERN_INFO PREFIX "PCI interrupt for device %s disabled\n", + pci_name(dev)); + + acpi_unregister_gsi(gsi); + + return_VOID; +} +#endif /* CONFIG_ACPI_DEALLOCATE_IRQ */ diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c new file mode 100644 index 00000000000..520b28ad074 --- /dev/null +++ b/drivers/acpi/pci_link.c @@ -0,0 +1,904 @@ +/* + * pci_link.c - ACPI PCI Interrupt Link Device Driver ($Revision: 34 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2002 Dominik Brodowski <devel@brodo.de> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * TBD: + * 1. Support more than one IRQ resource entry per link device (index). + * 2. Implement start/stop mechanism and use ACPI Bus Driver facilities + * for IRQ management (e.g. start()->_SRS). + */ + +#include <linux/sysdev.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/spinlock.h> +#include <linux/pm.h> +#include <linux/pci.h> + +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define _COMPONENT ACPI_PCI_COMPONENT +ACPI_MODULE_NAME ("pci_link") + +#define ACPI_PCI_LINK_CLASS "pci_irq_routing" +#define ACPI_PCI_LINK_HID "PNP0C0F" +#define ACPI_PCI_LINK_DRIVER_NAME "ACPI PCI Interrupt Link Driver" +#define ACPI_PCI_LINK_DEVICE_NAME "PCI Interrupt Link" +#define ACPI_PCI_LINK_FILE_INFO "info" +#define ACPI_PCI_LINK_FILE_STATUS "state" + +#define ACPI_PCI_LINK_MAX_POSSIBLE 16 + +static int acpi_pci_link_add (struct acpi_device *device); +static int acpi_pci_link_remove (struct acpi_device *device, int type); + +static struct acpi_driver acpi_pci_link_driver = { + .name = ACPI_PCI_LINK_DRIVER_NAME, + .class = ACPI_PCI_LINK_CLASS, + .ids = ACPI_PCI_LINK_HID, + .ops = { + .add = acpi_pci_link_add, + .remove = acpi_pci_link_remove, + }, +}; + +struct acpi_pci_link_irq { + u8 active; /* Current IRQ */ + u8 edge_level; /* All IRQs */ + u8 active_high_low; /* All IRQs */ + u8 initialized; + u8 resource_type; + u8 possible_count; + u8 possible[ACPI_PCI_LINK_MAX_POSSIBLE]; +}; + +struct acpi_pci_link { + struct list_head node; + struct acpi_device *device; + acpi_handle handle; + struct acpi_pci_link_irq irq; +}; + +static struct { + int count; + struct list_head entries; +} acpi_link; + + +/* -------------------------------------------------------------------------- + PCI Link Device Management + -------------------------------------------------------------------------- */ + +/* + * set context (link) possible list from resource list + */ +static acpi_status +acpi_pci_link_check_possible ( + struct acpi_resource *resource, + void *context) +{ + struct acpi_pci_link *link = (struct acpi_pci_link *) context; + u32 i = 0; + + ACPI_FUNCTION_TRACE("acpi_pci_link_check_possible"); + + switch (resource->id) { + case ACPI_RSTYPE_START_DPF: + return_ACPI_STATUS(AE_OK); + case ACPI_RSTYPE_IRQ: + { + struct acpi_resource_irq *p = &resource->data.irq; + if (!p || !p->number_of_interrupts) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Blank IRQ resource\n")); + return_ACPI_STATUS(AE_OK); + } + for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_POSSIBLE); i++) { + if (!p->interrupts[i]) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ %d\n", p->interrupts[i])); + continue; + } + link->irq.possible[i] = p->interrupts[i]; + link->irq.possible_count++; + } + link->irq.edge_level = p->edge_level; + link->irq.active_high_low = p->active_high_low; + link->irq.resource_type = ACPI_RSTYPE_IRQ; + break; + } + case ACPI_RSTYPE_EXT_IRQ: + { + struct acpi_resource_ext_irq *p = &resource->data.extended_irq; + if (!p || !p->number_of_interrupts) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "Blank EXT IRQ resource\n")); + return_ACPI_STATUS(AE_OK); + } + for (i = 0; (i<p->number_of_interrupts && i<ACPI_PCI_LINK_MAX_POSSIBLE); i++) { + if (!p->interrupts[i]) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid IRQ %d\n", p->interrupts[i])); + continue; + } + link->irq.possible[i] = p->interrupts[i]; + link->irq.possible_count++; + } + link->irq.edge_level = p->edge_level; + link->irq.active_high_low = p->active_high_low; + link->irq.resource_type = ACPI_RSTYPE_EXT_IRQ; + break; + } + default: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Resource is not an IRQ entry\n")); + return_ACPI_STATUS(AE_OK); + } + + return_ACPI_STATUS(AE_CTRL_TERMINATE); +} + + +static int +acpi_pci_link_get_possible ( + struct acpi_pci_link *link) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE("acpi_pci_link_get_possible"); + + if (!link) + return_VALUE(-EINVAL); + + status = acpi_walk_resources(link->handle, METHOD_NAME__PRS, + acpi_pci_link_check_possible, link); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRS\n")); + return_VALUE(-ENODEV); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Found %d possible IRQs\n", link->irq.possible_count)); + + return_VALUE(0); +} + + +static acpi_status +acpi_pci_link_check_current ( + struct acpi_resource *resource, + void *context) +{ + int *irq = (int *) context; + + ACPI_FUNCTION_TRACE("acpi_pci_link_check_current"); + + switch (resource->id) { + case ACPI_RSTYPE_IRQ: + { + struct acpi_resource_irq *p = &resource->data.irq; + if (!p || !p->number_of_interrupts) { + /* + * IRQ descriptors may have no IRQ# bits set, + * particularly those those w/ _STA disabled + */ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Blank IRQ resource\n")); + return_ACPI_STATUS(AE_OK); + } + *irq = p->interrupts[0]; + break; + } + case ACPI_RSTYPE_EXT_IRQ: + { + struct acpi_resource_ext_irq *p = &resource->data.extended_irq; + if (!p || !p->number_of_interrupts) { + /* + * extended IRQ descriptors must + * return at least 1 IRQ + */ + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "Blank EXT IRQ resource\n")); + return_ACPI_STATUS(AE_OK); + } + *irq = p->interrupts[0]; + break; + } + default: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Resource isn't an IRQ\n")); + return_ACPI_STATUS(AE_OK); + } + return_ACPI_STATUS(AE_CTRL_TERMINATE); +} + +/* + * Run _CRS and set link->irq.active + * + * return value: + * 0 - success + * !0 - failure + */ +static int +acpi_pci_link_get_current ( + struct acpi_pci_link *link) +{ + int result = 0; + acpi_status status = AE_OK; + int irq = 0; + + ACPI_FUNCTION_TRACE("acpi_pci_link_get_current"); + + if (!link || !link->handle) + return_VALUE(-EINVAL); + + link->irq.active = 0; + + /* in practice, status disabled is meaningless, ignore it */ + if (acpi_strict) { + /* Query _STA, set link->device->status */ + result = acpi_bus_get_status(link->device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n")); + goto end; + } + + if (!link->device->status.enabled) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link disabled\n")); + return_VALUE(0); + } + } + + /* + * Query and parse _CRS to get the current IRQ assignment. + */ + + status = acpi_walk_resources(link->handle, METHOD_NAME__CRS, + acpi_pci_link_check_current, &irq); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _CRS\n")); + result = -ENODEV; + goto end; + } + + if (acpi_strict && !irq) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "_CRS returned 0\n")); + result = -ENODEV; + } + + link->irq.active = irq; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Link at IRQ %d \n", link->irq.active)); + +end: + return_VALUE(result); +} + +static int +acpi_pci_link_set ( + struct acpi_pci_link *link, + int irq) +{ + int result = 0; + acpi_status status = AE_OK; + struct { + struct acpi_resource res; + struct acpi_resource end; + } *resource; + struct acpi_buffer buffer = {0, NULL}; + + ACPI_FUNCTION_TRACE("acpi_pci_link_set"); + + if (!link || !irq) + return_VALUE(-EINVAL); + + resource = kmalloc( sizeof(*resource)+1, GFP_KERNEL); + if(!resource) + return_VALUE(-ENOMEM); + + memset(resource, 0, sizeof(*resource)+1); + buffer.length = sizeof(*resource) +1; + buffer.pointer = resource; + + switch(link->irq.resource_type) { + case ACPI_RSTYPE_IRQ: + resource->res.id = ACPI_RSTYPE_IRQ; + resource->res.length = sizeof(struct acpi_resource); + resource->res.data.irq.edge_level = link->irq.edge_level; + resource->res.data.irq.active_high_low = link->irq.active_high_low; + if (link->irq.edge_level == ACPI_EDGE_SENSITIVE) + resource->res.data.irq.shared_exclusive = ACPI_EXCLUSIVE; + else + resource->res.data.irq.shared_exclusive = ACPI_SHARED; + resource->res.data.irq.number_of_interrupts = 1; + resource->res.data.irq.interrupts[0] = irq; + break; + + case ACPI_RSTYPE_EXT_IRQ: + resource->res.id = ACPI_RSTYPE_EXT_IRQ; + resource->res.length = sizeof(struct acpi_resource); + resource->res.data.extended_irq.producer_consumer = ACPI_CONSUMER; + resource->res.data.extended_irq.edge_level = link->irq.edge_level; + resource->res.data.extended_irq.active_high_low = link->irq.active_high_low; + if (link->irq.edge_level == ACPI_EDGE_SENSITIVE) + resource->res.data.irq.shared_exclusive = ACPI_EXCLUSIVE; + else + resource->res.data.irq.shared_exclusive = ACPI_SHARED; + resource->res.data.extended_irq.number_of_interrupts = 1; + resource->res.data.extended_irq.interrupts[0] = irq; + /* ignore resource_source, it's optional */ + break; + default: + printk("ACPI BUG: resource_type %d\n", link->irq.resource_type); + result = -EINVAL; + goto end; + + } + resource->end.id = ACPI_RSTYPE_END_TAG; + + /* Attempt to set the resource */ + status = acpi_set_current_resources(link->handle, &buffer); + + /* check for total failure */ + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SRS\n")); + result = -ENODEV; + goto end; + } + + /* Query _STA, set device->status */ + result = acpi_bus_get_status(link->device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to read status\n")); + goto end; + } + if (!link->device->status.enabled) { + printk(KERN_WARNING PREFIX + "%s [%s] disabled and referenced, BIOS bug.\n", + acpi_device_name(link->device), + acpi_device_bid(link->device)); + } + + /* Query _CRS, set link->irq.active */ + result = acpi_pci_link_get_current(link); + if (result) { + goto end; + } + + /* + * Is current setting not what we set? + * set link->irq.active + */ + if (link->irq.active != irq) { + /* + * policy: when _CRS doesn't return what we just _SRS + * assume _SRS worked and override _CRS value. + */ + printk(KERN_WARNING PREFIX + "%s [%s] BIOS reported IRQ %d, using IRQ %d\n", + acpi_device_name(link->device), + acpi_device_bid(link->device), + link->irq.active, irq); + link->irq.active = irq; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Set IRQ %d\n", link->irq.active)); + +end: + kfree(resource); + return_VALUE(result); +} + + +/* -------------------------------------------------------------------------- + PCI Link IRQ Management + -------------------------------------------------------------------------- */ + +/* + * "acpi_irq_balance" (default in APIC mode) enables ACPI to use PIC Interrupt + * Link Devices to move the PIRQs around to minimize sharing. + * + * "acpi_irq_nobalance" (default in PIC mode) tells ACPI not to move any PIC IRQs + * that the BIOS has already set to active. This is necessary because + * ACPI has no automatic means of knowing what ISA IRQs are used. Note that + * if the BIOS doesn't set a Link Device active, ACPI needs to program it + * even if acpi_irq_nobalance is set. + * + * A tables of penalties avoids directing PCI interrupts to well known + * ISA IRQs. Boot params are available to over-ride the default table: + * + * List interrupts that are free for PCI use. + * acpi_irq_pci=n[,m] + * + * List interrupts that should not be used for PCI: + * acpi_irq_isa=n[,m] + * + * Note that PCI IRQ routers have a list of possible IRQs, + * which may not include the IRQs this table says are available. + * + * Since this heuristic can't tell the difference between a link + * that no device will attach to, vs. a link which may be shared + * by multiple active devices -- it is not optimal. + * + * If interrupt performance is that important, get an IO-APIC system + * with a pin dedicated to each device. Or for that matter, an MSI + * enabled system. + */ + +#define ACPI_MAX_IRQS 256 +#define ACPI_MAX_ISA_IRQ 16 + +#define PIRQ_PENALTY_PCI_AVAILABLE (0) +#define PIRQ_PENALTY_PCI_POSSIBLE (16*16) +#define PIRQ_PENALTY_PCI_USING (16*16*16) +#define PIRQ_PENALTY_ISA_TYPICAL (16*16*16*16) +#define PIRQ_PENALTY_ISA_USED (16*16*16*16*16) +#define PIRQ_PENALTY_ISA_ALWAYS (16*16*16*16*16*16) + +static int acpi_irq_penalty[ACPI_MAX_IRQS] = { + PIRQ_PENALTY_ISA_ALWAYS, /* IRQ0 timer */ + PIRQ_PENALTY_ISA_ALWAYS, /* IRQ1 keyboard */ + PIRQ_PENALTY_ISA_ALWAYS, /* IRQ2 cascade */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ3 serial */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ4 serial */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ5 sometimes SoundBlaster */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ6 */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ7 parallel, spurious */ + PIRQ_PENALTY_ISA_TYPICAL, /* IRQ8 rtc, sometimes */ + PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ9 PCI, often acpi */ + PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ10 PCI */ + PIRQ_PENALTY_PCI_AVAILABLE, /* IRQ11 PCI */ + PIRQ_PENALTY_ISA_USED, /* IRQ12 mouse */ + PIRQ_PENALTY_ISA_USED, /* IRQ13 fpe, sometimes */ + PIRQ_PENALTY_ISA_USED, /* IRQ14 ide0 */ + PIRQ_PENALTY_ISA_USED, /* IRQ15 ide1 */ + /* >IRQ15 */ +}; + +int __init +acpi_irq_penalty_init(void) +{ + struct list_head *node = NULL; + struct acpi_pci_link *link = NULL; + int i = 0; + + ACPI_FUNCTION_TRACE("acpi_irq_penalty_init"); + + /* + * Update penalties to facilitate IRQ balancing. + */ + list_for_each(node, &acpi_link.entries) { + + link = list_entry(node, struct acpi_pci_link, node); + if (!link) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); + continue; + } + + /* + * reflect the possible and active irqs in the penalty table -- + * useful for breaking ties. + */ + if (link->irq.possible_count) { + int penalty = PIRQ_PENALTY_PCI_POSSIBLE / link->irq.possible_count; + + for (i = 0; i < link->irq.possible_count; i++) { + if (link->irq.possible[i] < ACPI_MAX_ISA_IRQ) + acpi_irq_penalty[link->irq.possible[i]] += penalty; + } + + } else if (link->irq.active) { + acpi_irq_penalty[link->irq.active] += PIRQ_PENALTY_PCI_POSSIBLE; + } + } + /* Add a penalty for the SCI */ + acpi_irq_penalty[acpi_fadt.sci_int] += PIRQ_PENALTY_PCI_USING; + + return_VALUE(0); +} + +static int acpi_irq_balance; /* 0: static, 1: balance */ + +static int acpi_pci_link_allocate( + struct acpi_pci_link *link) +{ + int irq; + int i; + + ACPI_FUNCTION_TRACE("acpi_pci_link_allocate"); + + if (link->irq.initialized) + return_VALUE(0); + + /* + * search for active IRQ in list of possible IRQs. + */ + for (i = 0; i < link->irq.possible_count; ++i) { + if (link->irq.active == link->irq.possible[i]) + break; + } + /* + * forget active IRQ that is not in possible list + */ + if (i == link->irq.possible_count) { + if (acpi_strict) + printk(KERN_WARNING PREFIX "_CRS %d not found" + " in _PRS\n", link->irq.active); + link->irq.active = 0; + } + + /* + * if active found, use it; else pick entry from end of possible list. + */ + if (link->irq.active) { + irq = link->irq.active; + } else { + irq = link->irq.possible[link->irq.possible_count - 1]; + } + + if (acpi_irq_balance || !link->irq.active) { + /* + * Select the best IRQ. This is done in reverse to promote + * the use of IRQs 9, 10, 11, and >15. + */ + for (i = (link->irq.possible_count - 1); i >= 0; i--) { + if (acpi_irq_penalty[irq] > acpi_irq_penalty[link->irq.possible[i]]) + irq = link->irq.possible[i]; + } + } + + /* Attempt to enable the link device at this IRQ. */ + if (acpi_pci_link_set(link, irq)) { + printk(PREFIX "Unable to set IRQ for %s [%s] (likely buggy ACPI BIOS).\n" + "Try pci=noacpi or acpi=off\n", + acpi_device_name(link->device), + acpi_device_bid(link->device)); + return_VALUE(-ENODEV); + } else { + acpi_irq_penalty[link->irq.active] += PIRQ_PENALTY_PCI_USING; + printk(PREFIX "%s [%s] enabled at IRQ %d\n", + acpi_device_name(link->device), + acpi_device_bid(link->device), link->irq.active); + } + + link->irq.initialized = 1; + + return_VALUE(0); +} + +/* + * acpi_pci_link_get_irq + * success: return IRQ >= 0 + * failure: return -1 + */ + +int +acpi_pci_link_get_irq ( + acpi_handle handle, + int index, + int *edge_level, + int *active_high_low, + char **name) +{ + int result = 0; + struct acpi_device *device = NULL; + struct acpi_pci_link *link = NULL; + + ACPI_FUNCTION_TRACE("acpi_pci_link_get_irq"); + + result = acpi_bus_get_device(handle, &device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link device\n")); + return_VALUE(-1); + } + + link = (struct acpi_pci_link *) acpi_driver_data(device); + if (!link) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); + return_VALUE(-1); + } + + /* TBD: Support multiple index (IRQ) entries per Link Device */ + if (index) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid index %d\n", index)); + return_VALUE(-1); + } + + if (acpi_pci_link_allocate(link)) + return_VALUE(-1); + + if (!link->irq.active) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Link active IRQ is 0!\n")); + return_VALUE(-1); + } + + if (edge_level) *edge_level = link->irq.edge_level; + if (active_high_low) *active_high_low = link->irq.active_high_low; + if (name) *name = acpi_device_bid(link->device); + return_VALUE(link->irq.active); +} + + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static int +acpi_pci_link_add ( + struct acpi_device *device) +{ + int result = 0; + struct acpi_pci_link *link = NULL; + int i = 0; + int found = 0; + + ACPI_FUNCTION_TRACE("acpi_pci_link_add"); + + if (!device) + return_VALUE(-EINVAL); + + link = kmalloc(sizeof(struct acpi_pci_link), GFP_KERNEL); + if (!link) + return_VALUE(-ENOMEM); + memset(link, 0, sizeof(struct acpi_pci_link)); + + link->device = device; + link->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_PCI_LINK_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PCI_LINK_CLASS); + acpi_driver_data(device) = link; + + result = acpi_pci_link_get_possible(link); + if (result) + goto end; + + /* query and set link->irq.active */ + acpi_pci_link_get_current(link); + + printk(PREFIX "%s [%s] (IRQs", acpi_device_name(device), + acpi_device_bid(device)); + for (i = 0; i < link->irq.possible_count; i++) { + if (link->irq.active == link->irq.possible[i]) { + printk(" *%d", link->irq.possible[i]); + found = 1; + } + else + printk(" %d", link->irq.possible[i]); + } + + printk(")"); + + if (!found) + printk(" *%d", link->irq.active); + + if(!link->device->status.enabled) + printk(", disabled."); + + printk("\n"); + + /* TBD: Acquire/release lock */ + list_add_tail(&link->node, &acpi_link.entries); + acpi_link.count++; + +end: + /* disable all links -- to be activated on use */ + acpi_ut_evaluate_object(link->handle, "_DIS", 0, NULL); + + if (result) + kfree(link); + + return_VALUE(result); +} + + +static int +acpi_pci_link_resume ( + struct acpi_pci_link *link) +{ + ACPI_FUNCTION_TRACE("acpi_pci_link_resume"); + + if (link->irq.active && link->irq.initialized) + return_VALUE(acpi_pci_link_set(link, link->irq.active)); + else + return_VALUE(0); +} + + +static int +irqrouter_resume( + struct sys_device *dev) +{ + struct list_head *node = NULL; + struct acpi_pci_link *link = NULL; + + ACPI_FUNCTION_TRACE("irqrouter_resume"); + + list_for_each(node, &acpi_link.entries) { + + link = list_entry(node, struct acpi_pci_link, node); + if (!link) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid link context\n")); + continue; + } + + acpi_pci_link_resume(link); + } + return_VALUE(0); +} + + +static int +acpi_pci_link_remove ( + struct acpi_device *device, + int type) +{ + struct acpi_pci_link *link = NULL; + + ACPI_FUNCTION_TRACE("acpi_pci_link_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + link = (struct acpi_pci_link *) acpi_driver_data(device); + + /* TBD: Acquire/release lock */ + list_del(&link->node); + + kfree(link); + + return_VALUE(0); +} + +/* + * modify acpi_irq_penalty[] from cmdline + */ +static int __init acpi_irq_penalty_update(char *str, int used) +{ + int i; + + for (i = 0; i < 16; i++) { + int retval; + int irq; + + retval = get_option(&str,&irq); + + if (!retval) + break; /* no number found */ + + if (irq < 0) + continue; + + if (irq >= ACPI_MAX_IRQS) + continue; + + if (used) + acpi_irq_penalty[irq] += PIRQ_PENALTY_ISA_USED; + else + acpi_irq_penalty[irq] = PIRQ_PENALTY_PCI_AVAILABLE; + + if (retval != 2) /* no next number */ + break; + } + return 1; +} + +/* + * We'd like PNP to call this routine for the + * single ISA_USED value for each legacy device. + * But instead it calls us with each POSSIBLE setting. + * There is no ISA_POSSIBLE weight, so we simply use + * the (small) PCI_USING penalty. + */ +void acpi_penalize_isa_irq(int irq) +{ + acpi_irq_penalty[irq] += PIRQ_PENALTY_PCI_USING; +} + +/* + * Over-ride default table to reserve additional IRQs for use by ISA + * e.g. acpi_irq_isa=5 + * Useful for telling ACPI how not to interfere with your ISA sound card. + */ +static int __init acpi_irq_isa(char *str) +{ + return acpi_irq_penalty_update(str, 1); +} +__setup("acpi_irq_isa=", acpi_irq_isa); + +/* + * Over-ride default table to free additional IRQs for use by PCI + * e.g. acpi_irq_pci=7,15 + * Used for acpi_irq_balance to free up IRQs to reduce PCI IRQ sharing. + */ +static int __init acpi_irq_pci(char *str) +{ + return acpi_irq_penalty_update(str, 0); +} +__setup("acpi_irq_pci=", acpi_irq_pci); + +static int __init acpi_irq_nobalance_set(char *str) +{ + acpi_irq_balance = 0; + return 1; +} +__setup("acpi_irq_nobalance", acpi_irq_nobalance_set); + +int __init acpi_irq_balance_set(char *str) +{ + acpi_irq_balance = 1; + return 1; +} +__setup("acpi_irq_balance", acpi_irq_balance_set); + + +static struct sysdev_class irqrouter_sysdev_class = { + set_kset_name("irqrouter"), + .resume = irqrouter_resume, +}; + + +static struct sys_device device_irqrouter = { + .id = 0, + .cls = &irqrouter_sysdev_class, +}; + + +static int __init irqrouter_init_sysfs(void) +{ + int error; + + ACPI_FUNCTION_TRACE("irqrouter_init_sysfs"); + + if (acpi_disabled || acpi_noirq) + return_VALUE(0); + + error = sysdev_class_register(&irqrouter_sysdev_class); + if (!error) + error = sysdev_register(&device_irqrouter); + + return_VALUE(error); +} + +device_initcall(irqrouter_init_sysfs); + + +static int __init acpi_pci_link_init (void) +{ + ACPI_FUNCTION_TRACE("acpi_pci_link_init"); + + if (acpi_noirq) + return_VALUE(0); + + acpi_link.count = 0; + INIT_LIST_HEAD(&acpi_link.entries); + + if (acpi_bus_register_driver(&acpi_pci_link_driver) < 0) + return_VALUE(-ENODEV); + + return_VALUE(0); +} + +subsys_initcall(acpi_pci_link_init); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c new file mode 100644 index 00000000000..7e6b8e3b2ed --- /dev/null +++ b/drivers/acpi/pci_root.c @@ -0,0 +1,347 @@ +/* + * pci_root.c - ACPI PCI Root Bridge Driver ($Revision: 40 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/spinlock.h> +#include <linux/pm.h> +#include <linux/pci.h> +#include <linux/acpi.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define _COMPONENT ACPI_PCI_COMPONENT +ACPI_MODULE_NAME ("pci_root") + +#define ACPI_PCI_ROOT_CLASS "pci_bridge" +#define ACPI_PCI_ROOT_HID "PNP0A03" +#define ACPI_PCI_ROOT_DRIVER_NAME "ACPI PCI Root Bridge Driver" +#define ACPI_PCI_ROOT_DEVICE_NAME "PCI Root Bridge" + +static int acpi_pci_root_add (struct acpi_device *device); +static int acpi_pci_root_remove (struct acpi_device *device, int type); + +static struct acpi_driver acpi_pci_root_driver = { + .name = ACPI_PCI_ROOT_DRIVER_NAME, + .class = ACPI_PCI_ROOT_CLASS, + .ids = ACPI_PCI_ROOT_HID, + .ops = { + .add = acpi_pci_root_add, + .remove = acpi_pci_root_remove, + }, +}; + +struct acpi_pci_root { + struct list_head node; + acpi_handle handle; + struct acpi_pci_id id; + struct pci_bus *bus; +}; + +static LIST_HEAD(acpi_pci_roots); + +static struct acpi_pci_driver *sub_driver; + +int acpi_pci_register_driver(struct acpi_pci_driver *driver) +{ + int n = 0; + struct list_head *entry; + + struct acpi_pci_driver **pptr = &sub_driver; + while (*pptr) + pptr = &(*pptr)->next; + *pptr = driver; + + if (!driver->add) + return 0; + + list_for_each(entry, &acpi_pci_roots) { + struct acpi_pci_root *root; + root = list_entry(entry, struct acpi_pci_root, node); + driver->add(root->handle); + n++; + } + + return n; +} +EXPORT_SYMBOL(acpi_pci_register_driver); + +void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) +{ + struct list_head *entry; + + struct acpi_pci_driver **pptr = &sub_driver; + while (*pptr) { + if (*pptr != driver) + continue; + *pptr = (*pptr)->next; + break; + } + + if (!driver->remove) + return; + + list_for_each(entry, &acpi_pci_roots) { + struct acpi_pci_root *root; + root = list_entry(entry, struct acpi_pci_root, node); + driver->remove(root->handle); + } +} +EXPORT_SYMBOL(acpi_pci_unregister_driver); + +static acpi_status +get_root_bridge_busnr_callback (struct acpi_resource *resource, void *data) +{ + int *busnr = (int *)data; + struct acpi_resource_address64 address; + + if (resource->id != ACPI_RSTYPE_ADDRESS16 && + resource->id != ACPI_RSTYPE_ADDRESS32 && + resource->id != ACPI_RSTYPE_ADDRESS64) + return AE_OK; + + acpi_resource_to_address64(resource, &address); + if ((address.address_length > 0) && + (address.resource_type == ACPI_BUS_NUMBER_RANGE)) + *busnr = address.min_address_range; + + return AE_OK; +} + +static acpi_status +try_get_root_bridge_busnr(acpi_handle handle, int *busnum) +{ + acpi_status status; + + *busnum = -1; + status = acpi_walk_resources(handle, METHOD_NAME__CRS, get_root_bridge_busnr_callback, busnum); + if (ACPI_FAILURE(status)) + return status; + /* Check if we really get a bus number from _CRS */ + if (*busnum == -1) + return AE_ERROR; + return AE_OK; +} + +static int +acpi_pci_root_add ( + struct acpi_device *device) +{ + int result = 0; + struct acpi_pci_root *root = NULL; + struct acpi_pci_root *tmp; + acpi_status status = AE_OK; + unsigned long value = 0; + acpi_handle handle = NULL; + + ACPI_FUNCTION_TRACE("acpi_pci_root_add"); + + if (!device) + return_VALUE(-EINVAL); + + root = kmalloc(sizeof(struct acpi_pci_root), GFP_KERNEL); + if (!root) + return_VALUE(-ENOMEM); + memset(root, 0, sizeof(struct acpi_pci_root)); + + root->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_PCI_ROOT_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PCI_ROOT_CLASS); + acpi_driver_data(device) = root; + + /* + * TBD: Doesn't the bus driver automatically set this? + */ + device->ops.bind = acpi_pci_bind; + + /* + * Segment + * ------- + * Obtained via _SEG, if exists, otherwise assumed to be zero (0). + */ + status = acpi_evaluate_integer(root->handle, METHOD_NAME__SEG, NULL, + &value); + switch (status) { + case AE_OK: + root->id.segment = (u16) value; + break; + case AE_NOT_FOUND: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Assuming segment 0 (no _SEG)\n")); + root->id.segment = 0; + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _SEG\n")); + result = -ENODEV; + goto end; + } + + /* + * Bus + * --- + * Obtained via _BBN, if exists, otherwise assumed to be zero (0). + */ + status = acpi_evaluate_integer(root->handle, METHOD_NAME__BBN, NULL, + &value); + switch (status) { + case AE_OK: + root->id.bus = (u16) value; + break; + case AE_NOT_FOUND: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Assuming bus 0 (no _BBN)\n")); + root->id.bus = 0; + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _BBN\n")); + result = -ENODEV; + goto end; + } + + /* Some systems have wrong _BBN */ + list_for_each_entry(tmp, &acpi_pci_roots, node) { + if ((tmp->id.segment == root->id.segment) + && (tmp->id.bus == root->id.bus)) { + int bus = 0; + acpi_status status; + + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Wrong _BBN value, please reboot and using option 'pci=noacpi'\n")); + + status = try_get_root_bridge_busnr(root->handle, &bus); + if (ACPI_FAILURE(status)) + break; + if (bus != root->id.bus) { + printk(KERN_INFO PREFIX "PCI _CRS %d overrides _BBN 0\n", bus); + root->id.bus = bus; + } + break; + } + } + /* + * Device & Function + * ----------------- + * Obtained from _ADR (which has already been evaluated for us). + */ + root->id.device = device->pnp.bus_address >> 16; + root->id.function = device->pnp.bus_address & 0xFFFF; + + /* + * TBD: Need PCI interface for enumeration/configuration of roots. + */ + + /* TBD: Locking */ + list_add_tail(&root->node, &acpi_pci_roots); + + printk(KERN_INFO PREFIX "%s [%s] (%04x:%02x)\n", + acpi_device_name(device), acpi_device_bid(device), + root->id.segment, root->id.bus); + + /* + * Scan the Root Bridge + * -------------------- + * Must do this prior to any attempt to bind the root device, as the + * PCI namespace does not get created until this call is made (and + * thus the root bridge's pci_dev does not exist). + */ + root->bus = pci_acpi_scan_root(device, root->id.segment, root->id.bus); + if (!root->bus) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Bus %04x:%02x not present in PCI namespace\n", + root->id.segment, root->id.bus)); + result = -ENODEV; + goto end; + } + + /* + * Attach ACPI-PCI Context + * ----------------------- + * Thus binding the ACPI and PCI devices. + */ + result = acpi_pci_bind_root(device, &root->id, root->bus); + if (result) + goto end; + + /* + * PCI Routing Table + * ----------------- + * Evaluate and parse _PRT, if exists. + */ + status = acpi_get_handle(root->handle, METHOD_NAME__PRT, &handle); + if (ACPI_SUCCESS(status)) + result = acpi_pci_irq_add_prt(root->handle, root->id.segment, + root->id.bus); + +end: + if (result) + kfree(root); + + return_VALUE(result); +} + + +static int +acpi_pci_root_remove ( + struct acpi_device *device, + int type) +{ + struct acpi_pci_root *root = NULL; + + ACPI_FUNCTION_TRACE("acpi_pci_root_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + root = (struct acpi_pci_root *) acpi_driver_data(device); + + kfree(root); + + return_VALUE(0); +} + + +static int __init acpi_pci_root_init (void) +{ + ACPI_FUNCTION_TRACE("acpi_pci_root_init"); + + if (acpi_pci_disabled) + return_VALUE(0); + + /* DEBUG: + acpi_dbg_layer = ACPI_PCI_COMPONENT; + acpi_dbg_level = 0xFFFFFFFF; + */ + + if (acpi_bus_register_driver(&acpi_pci_root_driver) < 0) + return_VALUE(-ENODEV); + + return_VALUE(0); +} + +subsys_initcall(acpi_pci_root_init); + diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c new file mode 100644 index 00000000000..373a3a95bb4 --- /dev/null +++ b/drivers/acpi/power.c @@ -0,0 +1,692 @@ +/* + * acpi_power.c - ACPI Bus Power Management ($Revision: 39 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +/* + * ACPI power-managed devices may be controlled in two ways: + * 1. via "Device Specific (D-State) Control" + * 2. via "Power Resource Control". + * This module is used to manage devices relying on Power Resource Control. + * + * An ACPI "power resource object" describes a software controllable power + * plane, clock plane, or other resource used by a power managed device. + * A device may rely on multiple power resources, and a power resource + * may be shared by multiple devices. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define _COMPONENT ACPI_POWER_COMPONENT +ACPI_MODULE_NAME ("acpi_power") + +#define ACPI_POWER_COMPONENT 0x00800000 +#define ACPI_POWER_CLASS "power_resource" +#define ACPI_POWER_DRIVER_NAME "ACPI Power Resource Driver" +#define ACPI_POWER_DEVICE_NAME "Power Resource" +#define ACPI_POWER_FILE_INFO "info" +#define ACPI_POWER_FILE_STATUS "state" +#define ACPI_POWER_RESOURCE_STATE_OFF 0x00 +#define ACPI_POWER_RESOURCE_STATE_ON 0x01 +#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF + +static int acpi_power_add (struct acpi_device *device); +static int acpi_power_remove (struct acpi_device *device, int type); +static int acpi_power_open_fs(struct inode *inode, struct file *file); + +static struct acpi_driver acpi_power_driver = { + .name = ACPI_POWER_DRIVER_NAME, + .class = ACPI_POWER_CLASS, + .ids = ACPI_POWER_HID, + .ops = { + .add = acpi_power_add, + .remove = acpi_power_remove, + }, +}; + +struct acpi_power_resource +{ + acpi_handle handle; + acpi_bus_id name; + u32 system_level; + u32 order; + int state; + int references; +}; + +static struct list_head acpi_power_resource_list; + +static struct file_operations acpi_power_fops = { + .open = acpi_power_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* -------------------------------------------------------------------------- + Power Resource Management + -------------------------------------------------------------------------- */ + +static int +acpi_power_get_context ( + acpi_handle handle, + struct acpi_power_resource **resource) +{ + int result = 0; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_power_get_context"); + + if (!resource) + return_VALUE(-ENODEV); + + result = acpi_bus_get_device(handle, &device); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error getting context [%p]\n", + handle)); + return_VALUE(result); + } + + *resource = (struct acpi_power_resource *) acpi_driver_data(device); + if (!resource) + return_VALUE(-ENODEV); + + return_VALUE(0); +} + + +static int +acpi_power_get_state ( + struct acpi_power_resource *resource) +{ + acpi_status status = AE_OK; + unsigned long sta = 0; + + ACPI_FUNCTION_TRACE("acpi_power_get_state"); + + if (!resource) + return_VALUE(-EINVAL); + + status = acpi_evaluate_integer(resource->handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + if (sta & 0x01) + resource->state = ACPI_POWER_RESOURCE_STATE_ON; + else + resource->state = ACPI_POWER_RESOURCE_STATE_OFF; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n", + resource->name, resource->state?"on":"off")); + + return_VALUE(0); +} + + +static int +acpi_power_get_list_state ( + struct acpi_handle_list *list, + int *state) +{ + int result = 0; + struct acpi_power_resource *resource = NULL; + u32 i = 0; + + ACPI_FUNCTION_TRACE("acpi_power_get_list_state"); + + if (!list || !state) + return_VALUE(-EINVAL); + + /* The state of the list is 'on' IFF all resources are 'on'. */ + + for (i=0; i<list->count; i++) { + result = acpi_power_get_context(list->handles[i], &resource); + if (result) + return_VALUE(result); + result = acpi_power_get_state(resource); + if (result) + return_VALUE(result); + + *state = resource->state; + + if (*state != ACPI_POWER_RESOURCE_STATE_ON) + break; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n", + *state?"on":"off")); + + return_VALUE(result); +} + + +static int +acpi_power_on ( + acpi_handle handle) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_device *device = NULL; + struct acpi_power_resource *resource = NULL; + + ACPI_FUNCTION_TRACE("acpi_power_on"); + + result = acpi_power_get_context(handle, &resource); + if (result) + return_VALUE(result); + + resource->references++; + + if ((resource->references > 1) + || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n", + resource->name)); + return_VALUE(0); + } + + status = acpi_evaluate_object(resource->handle, "_ON", NULL, NULL); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + result = acpi_power_get_state(resource); + if (result) + return_VALUE(result); + if (resource->state != ACPI_POWER_RESOURCE_STATE_ON) + return_VALUE(-ENOEXEC); + + /* Update the power resource's _device_ power state */ + result = acpi_bus_get_device(resource->handle, &device); + if (result) + return_VALUE(result); + device->power.state = ACPI_STATE_D0; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", + resource->name)); + + return_VALUE(0); +} + + +static int +acpi_power_off_device ( + acpi_handle handle) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_device *device = NULL; + struct acpi_power_resource *resource = NULL; + + ACPI_FUNCTION_TRACE("acpi_power_off_device"); + + result = acpi_power_get_context(handle, &resource); + if (result) + return_VALUE(result); + + if (resource->references) + resource->references--; + + if (resource->references) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Resource [%s] is still in use, dereferencing\n", + device->pnp.bus_id)); + return_VALUE(0); + } + + if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", + device->pnp.bus_id)); + return_VALUE(0); + } + + status = acpi_evaluate_object(resource->handle, "_OFF", NULL, NULL); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + result = acpi_power_get_state(resource); + if (result) + return_VALUE(result); + if (resource->state != ACPI_POWER_RESOURCE_STATE_OFF) + return_VALUE(-ENOEXEC); + + /* Update the power resource's _device_ power state */ + result = acpi_bus_get_device(resource->handle, &device); + if (result) + return_VALUE(result); + device->power.state = ACPI_STATE_D3; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n", + resource->name)); + + return_VALUE(0); +} + +/* + * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229): + * 1. Power on the power resources required for the wakeup device + * 2. Enable _PSW (power state wake) for the device if present + */ +int acpi_enable_wakeup_device_power (struct acpi_device *dev) +{ + union acpi_object arg = {ACPI_TYPE_INTEGER}; + struct acpi_object_list arg_list = {1, &arg}; + acpi_status status = AE_OK; + int i; + int ret = 0; + + ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device_power"); + if (!dev || !dev->wakeup.flags.valid) + return_VALUE(-1); + + arg.integer.value = 1; + /* Open power resource */ + for (i = 0; i < dev->wakeup.resources.count; i++) { + ret = acpi_power_on(dev->wakeup.resources.handles[i]); + if (ret) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error transition power state\n")); + dev->wakeup.flags.valid = 0; + return_VALUE(-1); + } + } + + /* Execute PSW */ + status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL); + if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluate _PSW\n")); + dev->wakeup.flags.valid = 0; + ret = -1; + } + + return_VALUE(ret); +} + +/* + * Shutdown a wakeup device, counterpart of above method + * 1. Disable _PSW (power state wake) + * 2. Shutdown down the power resources + */ +int acpi_disable_wakeup_device_power (struct acpi_device *dev) +{ + union acpi_object arg = {ACPI_TYPE_INTEGER}; + struct acpi_object_list arg_list = {1, &arg}; + acpi_status status = AE_OK; + int i; + int ret = 0; + + ACPI_FUNCTION_TRACE("acpi_disable_wakeup_device_power"); + + if (!dev || !dev->wakeup.flags.valid) + return_VALUE(-1); + + arg.integer.value = 0; + /* Execute PSW */ + status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL); + if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluate _PSW\n")); + dev->wakeup.flags.valid = 0; + return_VALUE(-1); + } + + /* Close power resource */ + for (i = 0; i < dev->wakeup.resources.count; i++) { + ret = acpi_power_off_device(dev->wakeup.resources.handles[i]); + if (ret) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error transition power state\n")); + dev->wakeup.flags.valid = 0; + return_VALUE(-1); + } + } + + return_VALUE(ret); +} + +/* -------------------------------------------------------------------------- + Device Power Management + -------------------------------------------------------------------------- */ + +int +acpi_power_get_inferred_state ( + struct acpi_device *device) +{ + int result = 0; + struct acpi_handle_list *list = NULL; + int list_state = 0; + int i = 0; + + ACPI_FUNCTION_TRACE("acpi_power_get_inferred_state"); + + if (!device) + return_VALUE(-EINVAL); + + device->power.state = ACPI_STATE_UNKNOWN; + + /* + * We know a device's inferred power state when all the resources + * required for a given D-state are 'on'. + */ + for (i=ACPI_STATE_D0; i<ACPI_STATE_D3; i++) { + list = &device->power.states[i].resources; + if (list->count < 1) + continue; + + result = acpi_power_get_list_state(list, &list_state); + if (result) + return_VALUE(result); + + if (list_state == ACPI_POWER_RESOURCE_STATE_ON) { + device->power.state = i; + return_VALUE(0); + } + } + + device->power.state = ACPI_STATE_D3; + + return_VALUE(0); +} + + +int +acpi_power_transition ( + struct acpi_device *device, + int state) +{ + int result = 0; + struct acpi_handle_list *cl = NULL; /* Current Resources */ + struct acpi_handle_list *tl = NULL; /* Target Resources */ + int i = 0; + + ACPI_FUNCTION_TRACE("acpi_power_transition"); + + if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) + return_VALUE(-EINVAL); + + if ((device->power.state < ACPI_STATE_D0) || (device->power.state > ACPI_STATE_D3)) + return_VALUE(-ENODEV); + + cl = &device->power.states[device->power.state].resources; + tl = &device->power.states[state].resources; + + device->power.state = ACPI_STATE_UNKNOWN; + + if (!cl->count && !tl->count) { + result = -ENODEV; + goto end; + } + + /* TBD: Resources must be ordered. */ + + /* + * First we reference all power resources required in the target list + * (e.g. so the device doesn't lose power while transitioning). + */ + for (i=0; i<tl->count; i++) { + result = acpi_power_on(tl->handles[i]); + if (result) + goto end; + } + + /* + * Then we dereference all power resources used in the current list. + */ + for (i=0; i<cl->count; i++) { + result = acpi_power_off_device(cl->handles[i]); + if (result) + goto end; + } + + /* We shouldn't change the state till all above operations succeed */ + device->power.state = state; +end: + if (result) + ACPI_DEBUG_PRINT((ACPI_DB_WARN, + "Error transitioning device [%s] to D%d\n", + device->pnp.bus_id, state)); + + return_VALUE(result); +} + + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_power_dir; + +static int acpi_power_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_power_resource *resource = NULL; + + ACPI_FUNCTION_TRACE("acpi_power_seq_show"); + + resource = (struct acpi_power_resource *)seq->private; + + if (!resource) + goto end; + + seq_puts(seq, "state: "); + switch (resource->state) { + case ACPI_POWER_RESOURCE_STATE_ON: + seq_puts(seq, "on\n"); + break; + case ACPI_POWER_RESOURCE_STATE_OFF: + seq_puts(seq, "off\n"); + break; + default: + seq_puts(seq, "unknown\n"); + break; + } + + seq_printf(seq, "system level: S%d\n" + "order: %d\n" + "reference count: %d\n", + resource->system_level, + resource->order, + resource->references); + +end: + return_VALUE(0); +} + +static int acpi_power_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_power_seq_show, PDE(inode)->data); +} + +static int +acpi_power_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_power_add_fs"); + + if (!device) + return_VALUE(-EINVAL); + + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_power_dir); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + } + + /* 'status' [R] */ + entry = create_proc_entry(ACPI_POWER_FILE_STATUS, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_POWER_FILE_STATUS)); + else { + entry->proc_fops = &acpi_power_fops; + entry->data = acpi_driver_data(device); + } + + return_VALUE(0); +} + + +static int +acpi_power_remove_fs ( + struct acpi_device *device) +{ + ACPI_FUNCTION_TRACE("acpi_power_remove_fs"); + + if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_POWER_FILE_STATUS, + acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), acpi_power_dir); + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static int +acpi_power_add ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_power_resource *resource = NULL; + union acpi_object acpi_object; + struct acpi_buffer buffer = {sizeof(acpi_object), &acpi_object}; + + ACPI_FUNCTION_TRACE("acpi_power_add"); + + if (!device) + return_VALUE(-EINVAL); + + resource = kmalloc(sizeof(struct acpi_power_resource), GFP_KERNEL); + if (!resource) + return_VALUE(-ENOMEM); + memset(resource, 0, sizeof(struct acpi_power_resource)); + + resource->handle = device->handle; + strcpy(resource->name, device->pnp.bus_id); + strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_POWER_CLASS); + acpi_driver_data(device) = resource; + + /* Evalute the object to get the system level and resource order. */ + status = acpi_evaluate_object(resource->handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) { + result = -ENODEV; + goto end; + } + resource->system_level = acpi_object.power_resource.system_level; + resource->order = acpi_object.power_resource.resource_order; + + result = acpi_power_get_state(resource); + if (result) + goto end; + + switch (resource->state) { + case ACPI_POWER_RESOURCE_STATE_ON: + device->power.state = ACPI_STATE_D0; + break; + case ACPI_POWER_RESOURCE_STATE_OFF: + device->power.state = ACPI_STATE_D3; + break; + default: + device->power.state = ACPI_STATE_UNKNOWN; + break; + } + + result = acpi_power_add_fs(device); + if (result) + goto end; + + printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device), + acpi_device_bid(device), resource->state?"on":"off"); + +end: + if (result) + kfree(resource); + + return_VALUE(result); +} + + +static int +acpi_power_remove ( + struct acpi_device *device, + int type) +{ + struct acpi_power_resource *resource = NULL; + + ACPI_FUNCTION_TRACE("acpi_power_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + resource = (struct acpi_power_resource *) acpi_driver_data(device); + + acpi_power_remove_fs(device); + + kfree(resource); + + return_VALUE(0); +} + + +static int __init acpi_power_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_power_init"); + + if (acpi_disabled) + return_VALUE(0); + + INIT_LIST_HEAD(&acpi_power_resource_list); + + acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir); + if (!acpi_power_dir) + return_VALUE(-ENODEV); + + result = acpi_bus_register_driver(&acpi_power_driver); + if (result < 0) { + remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + +subsys_initcall(acpi_power_init); + diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c new file mode 100644 index 00000000000..f4778747e88 --- /dev/null +++ b/drivers/acpi/processor_core.c @@ -0,0 +1,989 @@ +/* + * acpi_processor.c - ACPI Processor Driver ($Revision: 71 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> + * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> + * - Added processor hotplug support + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * TBD: + * 1. Make # power states dynamic. + * 2. Support duty_cycle values that span bit 4. + * 3. Optimize by having scheduler determine business instead of + * having us try to calculate it here. + * 4. Need C1 timing -- must modify kernel (IRQ handler) to get this. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/pci.h> +#include <linux/pm.h> +#include <linux/cpufreq.h> +#include <linux/cpu.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/dmi.h> +#include <linux/moduleparam.h> + +#include <asm/io.h> +#include <asm/system.h> +#include <asm/cpu.h> +#include <asm/delay.h> +#include <asm/uaccess.h> +#include <asm/processor.h> +#include <asm/smp.h> +#include <asm/acpi.h> + +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> +#include <acpi/processor.h> + + +#define ACPI_PROCESSOR_COMPONENT 0x01000000 +#define ACPI_PROCESSOR_CLASS "processor" +#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" +#define ACPI_PROCESSOR_DEVICE_NAME "Processor" +#define ACPI_PROCESSOR_FILE_INFO "info" +#define ACPI_PROCESSOR_FILE_THROTTLING "throttling" +#define ACPI_PROCESSOR_FILE_LIMIT "limit" +#define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80 +#define ACPI_PROCESSOR_NOTIFY_POWER 0x81 + +#define ACPI_PROCESSOR_LIMIT_USER 0 +#define ACPI_PROCESSOR_LIMIT_THERMAL 1 + +#define ACPI_STA_PRESENT 0x00000001 + +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME ("acpi_processor") + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION(ACPI_PROCESSOR_DRIVER_NAME); +MODULE_LICENSE("GPL"); + + +static int acpi_processor_add (struct acpi_device *device); +static int acpi_processor_start (struct acpi_device *device); +static int acpi_processor_remove (struct acpi_device *device, int type); +static int acpi_processor_info_open_fs(struct inode *inode, struct file *file); +static void acpi_processor_notify ( acpi_handle handle, u32 event, void *data); +static acpi_status acpi_processor_hotadd_init(acpi_handle handle, int *p_cpu); +static int acpi_processor_handle_eject(struct acpi_processor *pr); + +static struct acpi_driver acpi_processor_driver = { + .name = ACPI_PROCESSOR_DRIVER_NAME, + .class = ACPI_PROCESSOR_CLASS, + .ids = ACPI_PROCESSOR_HID, + .ops = { + .add = acpi_processor_add, + .remove = acpi_processor_remove, + .start = acpi_processor_start, + }, +}; + +#define INSTALL_NOTIFY_HANDLER 1 +#define UNINSTALL_NOTIFY_HANDLER 2 + + +static struct file_operations acpi_processor_info_fops = { + .open = acpi_processor_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +struct acpi_processor *processors[NR_CPUS]; +struct acpi_processor_errata errata; + + +/* -------------------------------------------------------------------------- + Errata Handling + -------------------------------------------------------------------------- */ + +static int +acpi_processor_errata_piix4 ( + struct pci_dev *dev) +{ + u8 rev = 0; + u8 value1 = 0; + u8 value2 = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_errata_piix4"); + + if (!dev) + return_VALUE(-EINVAL); + + /* + * Note that 'dev' references the PIIX4 ACPI Controller. + */ + + pci_read_config_byte(dev, PCI_REVISION_ID, &rev); + + switch (rev) { + case 0: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 A-step\n")); + break; + case 1: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4 B-step\n")); + break; + case 2: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4E\n")); + break; + case 3: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found PIIX4M\n")); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unknown PIIX4\n")); + break; + } + + switch (rev) { + + case 0: /* PIIX4 A-step */ + case 1: /* PIIX4 B-step */ + /* + * See specification changes #13 ("Manual Throttle Duty Cycle") + * and #14 ("Enabling and Disabling Manual Throttle"), plus + * erratum #5 ("STPCLK# Deassertion Time") from the January + * 2002 PIIX4 specification update. Applies to only older + * PIIX4 models. + */ + errata.piix4.throttle = 1; + + case 2: /* PIIX4E */ + case 3: /* PIIX4M */ + /* + * See erratum #18 ("C3 Power State/BMIDE and Type-F DMA + * Livelock") from the January 2002 PIIX4 specification update. + * Applies to all PIIX4 models. + */ + + /* + * BM-IDE + * ------ + * Find the PIIX4 IDE Controller and get the Bus Master IDE + * Status register address. We'll use this later to read + * each IDE controller's DMA status to make sure we catch all + * DMA activity. + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB, + PCI_ANY_ID, PCI_ANY_ID, NULL); + if (dev) { + errata.piix4.bmisx = pci_resource_start(dev, 4); + pci_dev_put(dev); + } + + /* + * Type-F DMA + * ---------- + * Find the PIIX4 ISA Controller and read the Motherboard + * DMA controller's status to see if Type-F (Fast) DMA mode + * is enabled (bit 7) on either channel. Note that we'll + * disable C3 support if this is enabled, as some legacy + * devices won't operate well if fast DMA is disabled. + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, + PCI_ANY_ID, PCI_ANY_ID, NULL); + if (dev) { + pci_read_config_byte(dev, 0x76, &value1); + pci_read_config_byte(dev, 0x77, &value2); + if ((value1 & 0x80) || (value2 & 0x80)) + errata.piix4.fdma = 1; + pci_dev_put(dev); + } + + break; + } + + if (errata.piix4.bmisx) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Bus master activity detection (BM-IDE) erratum enabled\n")); + if (errata.piix4.fdma) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Type-F DMA livelock erratum (C3 disabled)\n")); + + return_VALUE(0); +} + + +int +acpi_processor_errata ( + struct acpi_processor *pr) +{ + int result = 0; + struct pci_dev *dev = NULL; + + ACPI_FUNCTION_TRACE("acpi_processor_errata"); + + if (!pr) + return_VALUE(-EINVAL); + + /* + * PIIX4 + */ + dev = pci_get_subsys(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, PCI_ANY_ID, PCI_ANY_ID, NULL); + if (dev) { + result = acpi_processor_errata_piix4(dev); + pci_dev_put(dev); + } + + return_VALUE(result); +} + + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_processor_dir = NULL; + +static int acpi_processor_info_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_processor *pr = (struct acpi_processor *)seq->private; + + ACPI_FUNCTION_TRACE("acpi_processor_info_seq_show"); + + if (!pr) + goto end; + + seq_printf(seq, "processor id: %d\n" + "acpi id: %d\n" + "bus mastering control: %s\n" + "power management: %s\n" + "throttling control: %s\n" + "limit interface: %s\n", + pr->id, + pr->acpi_id, + pr->flags.bm_control ? "yes" : "no", + pr->flags.power ? "yes" : "no", + pr->flags.throttling ? "yes" : "no", + pr->flags.limit ? "yes" : "no"); + +end: + return_VALUE(0); +} + +static int acpi_processor_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_processor_info_seq_show, + PDE(inode)->data); +} + + +static int +acpi_processor_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_processor_add_fs"); + + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_processor_dir); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + } + acpi_device_dir(device)->owner = THIS_MODULE; + + /* 'info' [R] */ + entry = create_proc_entry(ACPI_PROCESSOR_FILE_INFO, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_PROCESSOR_FILE_INFO)); + else { + entry->proc_fops = &acpi_processor_info_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'throttling' [R/W] */ + entry = create_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING, + S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_PROCESSOR_FILE_THROTTLING)); + else { + entry->proc_fops = &acpi_processor_throttling_fops; + entry->proc_fops->write = acpi_processor_write_throttling; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'limit' [R/W] */ + entry = create_proc_entry(ACPI_PROCESSOR_FILE_LIMIT, + S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_PROCESSOR_FILE_LIMIT)); + else { + entry->proc_fops = &acpi_processor_limit_fops; + entry->proc_fops->write = acpi_processor_write_limit; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + return_VALUE(0); +} + + +static int +acpi_processor_remove_fs ( + struct acpi_device *device) +{ + ACPI_FUNCTION_TRACE("acpi_processor_remove_fs"); + + if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_PROCESSOR_FILE_INFO,acpi_device_dir(device)); + remove_proc_entry(ACPI_PROCESSOR_FILE_THROTTLING, + acpi_device_dir(device)); + remove_proc_entry(ACPI_PROCESSOR_FILE_LIMIT,acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), acpi_processor_dir); + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + +/* Use the acpiid in MADT to map cpus in case of SMP */ +#ifndef CONFIG_SMP +#define convert_acpiid_to_cpu(acpi_id) (0xff) +#else + +#ifdef CONFIG_IA64 +#define arch_acpiid_to_apicid ia64_acpiid_to_sapicid +#define arch_cpu_to_apicid ia64_cpu_to_sapicid +#define ARCH_BAD_APICID (0xffff) +#else +#define arch_acpiid_to_apicid x86_acpiid_to_apicid +#define arch_cpu_to_apicid x86_cpu_to_apicid +#define ARCH_BAD_APICID (0xff) +#endif + +static u8 convert_acpiid_to_cpu(u8 acpi_id) +{ + u16 apic_id; + int i; + + apic_id = arch_acpiid_to_apicid[acpi_id]; + if (apic_id == ARCH_BAD_APICID) + return -1; + + for (i = 0; i < NR_CPUS; i++) { + if (arch_cpu_to_apicid[i] == apic_id) + return i; + } + return -1; +} +#endif + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static int +acpi_processor_get_info ( + struct acpi_processor *pr) +{ + acpi_status status = 0; + union acpi_object object = {0}; + struct acpi_buffer buffer = {sizeof(union acpi_object), &object}; + u8 cpu_index; + static int cpu0_initialized; + + ACPI_FUNCTION_TRACE("acpi_processor_get_info"); + + if (!pr) + return_VALUE(-EINVAL); + + if (num_online_cpus() > 1) + errata.smp = TRUE; + + acpi_processor_errata(pr); + + /* + * Check to see if we have bus mastering arbitration control. This + * is required for proper C3 usage (to maintain cache coherency). + */ + if (acpi_fadt.V1_pm2_cnt_blk && acpi_fadt.pm2_cnt_len) { + pr->flags.bm_control = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Bus mastering arbitration control present\n")); + } + else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No bus mastering arbitration control\n")); + + /* + * Evalute the processor object. Note that it is common on SMP to + * have the first (boot) processor with a valid PBLK address while + * all others have a NULL address. + */ + status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error evaluating processor object\n")); + return_VALUE(-ENODEV); + } + + /* + * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. + * >>> 'acpi_get_processor_id(acpi_id, &id)' in arch/xxx/acpi.c + */ + pr->acpi_id = object.processor.proc_id; + + cpu_index = convert_acpiid_to_cpu(pr->acpi_id); + + /* Handle UP system running SMP kernel, with no LAPIC in MADT */ + if ( !cpu0_initialized && (cpu_index == 0xff) && + (num_online_cpus() == 1)) { + cpu_index = 0; + } + + cpu0_initialized = 1; + + pr->id = cpu_index; + + /* + * Extra Processor objects may be enumerated on MP systems with + * less than the max # of CPUs. They should be ignored _iff + * they are physically not present. + */ + if (cpu_index >= NR_CPUS) { + if (ACPI_FAILURE(acpi_processor_hotadd_init(pr->handle, &pr->id))) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error getting cpuindex for acpiid 0x%x\n", + pr->acpi_id)); + return_VALUE(-ENODEV); + } + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id, + pr->acpi_id)); + + if (!object.processor.pblk_address) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n")); + else if (object.processor.pblk_length != 6) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid PBLK length [%d]\n", + object.processor.pblk_length)); + else { + pr->throttling.address = object.processor.pblk_address; + pr->throttling.duty_offset = acpi_fadt.duty_offset; + pr->throttling.duty_width = acpi_fadt.duty_width; + + pr->pblk = object.processor.pblk_address; + + /* + * We don't care about error returns - we just try to mark + * these reserved so that nobody else is confused into thinking + * that this region might be unused.. + * + * (In particular, allocating the IO range for Cardbus) + */ + request_region(pr->throttling.address, 6, "ACPI CPU throttle"); + } + +#ifdef CONFIG_CPU_FREQ + acpi_processor_ppc_has_changed(pr); +#endif + acpi_processor_get_throttling_info(pr); + acpi_processor_get_limit_info(pr); + + return_VALUE(0); +} + +static int +acpi_processor_start( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_processor *pr; + + ACPI_FUNCTION_TRACE("acpi_processor_start"); + + pr = acpi_driver_data(device); + + result = acpi_processor_get_info(pr); + if (result) { + /* Processor is physically not present */ + return_VALUE(0); + } + + BUG_ON((pr->id >= NR_CPUS) || (pr->id < 0)); + + processors[pr->id] = pr; + + result = acpi_processor_add_fs(device); + if (result) + goto end; + + status = acpi_install_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY, + acpi_processor_notify, pr); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing device notify handler\n")); + } + + acpi_processor_power_init(pr, device); + + if (pr->flags.throttling) { + printk(KERN_INFO PREFIX "%s [%s] (supports", + acpi_device_name(device), acpi_device_bid(device)); + printk(" %d throttling states", pr->throttling.state_count); + printk(")\n"); + } + +end: + + return_VALUE(result); +} + + + +static void +acpi_processor_notify ( + acpi_handle handle, + u32 event, + void *data) +{ + struct acpi_processor *pr = (struct acpi_processor *) data; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_processor_notify"); + + if (!pr) + return_VOID; + + if (acpi_bus_get_device(pr->handle, &device)) + return_VOID; + + switch (event) { + case ACPI_PROCESSOR_NOTIFY_PERFORMANCE: + acpi_processor_ppc_has_changed(pr); + acpi_bus_generate_event(device, event, + pr->performance_platform_limit); + break; + case ACPI_PROCESSOR_NOTIFY_POWER: + acpi_processor_cst_has_changed(pr); + acpi_bus_generate_event(device, event, 0); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + return_VOID; +} + + +static int +acpi_processor_add ( + struct acpi_device *device) +{ + struct acpi_processor *pr = NULL; + + ACPI_FUNCTION_TRACE("acpi_processor_add"); + + if (!device) + return_VALUE(-EINVAL); + + pr = kmalloc(sizeof(struct acpi_processor), GFP_KERNEL); + if (!pr) + return_VALUE(-ENOMEM); + memset(pr, 0, sizeof(struct acpi_processor)); + + pr->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_PROCESSOR_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_PROCESSOR_CLASS); + acpi_driver_data(device) = pr; + + return_VALUE(0); +} + + +static int +acpi_processor_remove ( + struct acpi_device *device, + int type) +{ + acpi_status status = AE_OK; + struct acpi_processor *pr = NULL; + + ACPI_FUNCTION_TRACE("acpi_processor_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + pr = (struct acpi_processor *) acpi_driver_data(device); + + if (pr->id >= NR_CPUS) { + kfree(pr); + return_VALUE(0); + } + + if (type == ACPI_BUS_REMOVAL_EJECT) { + if (acpi_processor_handle_eject(pr)) + return_VALUE(-EINVAL); + } + + acpi_processor_power_exit(pr, device); + + status = acpi_remove_notify_handler(pr->handle, ACPI_DEVICE_NOTIFY, + acpi_processor_notify); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error removing notify handler\n")); + } + + acpi_processor_remove_fs(device); + + processors[pr->id] = NULL; + + kfree(pr); + + return_VALUE(0); +} + +#ifdef CONFIG_ACPI_HOTPLUG_CPU +/**************************************************************************** + * Acpi processor hotplug support * + ****************************************************************************/ + +static int is_processor_present(acpi_handle handle); + +static int +is_processor_present( + acpi_handle handle) +{ + acpi_status status; + unsigned long sta = 0; + + ACPI_FUNCTION_TRACE("is_processor_present"); + + status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); + if (ACPI_FAILURE(status) || !(sta & ACPI_STA_PRESENT)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Processor Device is not present\n")); + return_VALUE(0); + } + return_VALUE(1); +} + + +static +int acpi_processor_device_add( + acpi_handle handle, + struct acpi_device **device) +{ + acpi_handle phandle; + struct acpi_device *pdev; + struct acpi_processor *pr; + + ACPI_FUNCTION_TRACE("acpi_processor_device_add"); + + if (acpi_get_parent(handle, &phandle)) { + return_VALUE(-ENODEV); + } + + if (acpi_bus_get_device(phandle, &pdev)) { + return_VALUE(-ENODEV); + } + + if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_PROCESSOR)) { + return_VALUE(-ENODEV); + } + + acpi_bus_scan(*device); + + pr = acpi_driver_data(*device); + if (!pr) + return_VALUE(-ENODEV); + + if ((pr->id >=0) && (pr->id < NR_CPUS)) { + kobject_hotplug(&(*device)->kobj, KOBJ_ONLINE); + } + return_VALUE(0); +} + + +static void +acpi_processor_hotplug_notify ( + acpi_handle handle, + u32 event, + void *data) +{ + struct acpi_processor *pr; + struct acpi_device *device = NULL; + int result; + + ACPI_FUNCTION_TRACE("acpi_processor_hotplug_notify"); + + switch (event) { + case ACPI_NOTIFY_BUS_CHECK: + case ACPI_NOTIFY_DEVICE_CHECK: + printk("Processor driver received %s event\n", + (event==ACPI_NOTIFY_BUS_CHECK)? + "ACPI_NOTIFY_BUS_CHECK":"ACPI_NOTIFY_DEVICE_CHECK"); + + if (!is_processor_present(handle)) + break; + + if (acpi_bus_get_device(handle, &device)) { + result = acpi_processor_device_add(handle, &device); + if (result) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to add the device\n")); + break; + } + + pr = acpi_driver_data(device); + if (!pr) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Driver data is NULL\n")); + break; + } + + if (pr->id >= 0 && (pr->id < NR_CPUS)) { + kobject_hotplug(&device->kobj, KOBJ_OFFLINE); + break; + } + + result = acpi_processor_start(device); + if ((!result) && ((pr->id >=0) && (pr->id < NR_CPUS))) { + kobject_hotplug(&device->kobj, KOBJ_ONLINE); + } else { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Device [%s] failed to start\n", + acpi_device_bid(device))); + } + break; + case ACPI_NOTIFY_EJECT_REQUEST: + ACPI_DEBUG_PRINT((ACPI_DB_INFO,"received ACPI_NOTIFY_EJECT_REQUEST\n")); + + if (acpi_bus_get_device(handle, &device)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"Device don't exist, dropping EJECT\n")); + break; + } + pr = acpi_driver_data(device); + if (!pr) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR,"Driver data is NULL, dropping EJECT\n")); + return_VOID; + } + + if ((pr->id < NR_CPUS) && (cpu_present(pr->id))) + kobject_hotplug(&device->kobj, KOBJ_OFFLINE); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + return_VOID; +} + +static acpi_status +processor_walk_namespace_cb(acpi_handle handle, + u32 lvl, + void *context, + void **rv) +{ + acpi_status status; + int *action = context; + acpi_object_type type = 0; + + status = acpi_get_type(handle, &type); + if (ACPI_FAILURE(status)) + return(AE_OK); + + if (type != ACPI_TYPE_PROCESSOR) + return(AE_OK); + + switch(*action) { + case INSTALL_NOTIFY_HANDLER: + acpi_install_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + acpi_processor_hotplug_notify, + NULL); + break; + case UNINSTALL_NOTIFY_HANDLER: + acpi_remove_notify_handler(handle, + ACPI_SYSTEM_NOTIFY, + acpi_processor_hotplug_notify); + break; + default: + break; + } + + return(AE_OK); +} + + +static acpi_status +acpi_processor_hotadd_init( + acpi_handle handle, + int *p_cpu) +{ + ACPI_FUNCTION_TRACE("acpi_processor_hotadd_init"); + + if (!is_processor_present(handle)) { + return_VALUE(AE_ERROR); + } + + if (acpi_map_lsapic(handle, p_cpu)) + return_VALUE(AE_ERROR); + + if (arch_register_cpu(*p_cpu)) { + acpi_unmap_lsapic(*p_cpu); + return_VALUE(AE_ERROR); + } + + return_VALUE(AE_OK); +} + + +static int +acpi_processor_handle_eject(struct acpi_processor *pr) +{ + if (cpu_online(pr->id)) { + return(-EINVAL); + } + arch_unregister_cpu(pr->id); + acpi_unmap_lsapic(pr->id); + return(0); +} +#else +static acpi_status +acpi_processor_hotadd_init( + acpi_handle handle, + int *p_cpu) +{ + return AE_ERROR; +} +static int +acpi_processor_handle_eject(struct acpi_processor *pr) +{ + return(-EINVAL); +} +#endif + + +static +void acpi_processor_install_hotplug_notify(void) +{ +#ifdef CONFIG_ACPI_HOTPLUG_CPU + int action = INSTALL_NOTIFY_HANDLER; + acpi_walk_namespace(ACPI_TYPE_PROCESSOR, + ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + processor_walk_namespace_cb, + &action, NULL); +#endif +} + + +static +void acpi_processor_uninstall_hotplug_notify(void) +{ +#ifdef CONFIG_ACPI_HOTPLUG_CPU + int action = UNINSTALL_NOTIFY_HANDLER; + acpi_walk_namespace(ACPI_TYPE_PROCESSOR, + ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, + processor_walk_namespace_cb, + &action, NULL); +#endif +} + +/* + * We keep the driver loaded even when ACPI is not running. + * This is needed for the powernow-k8 driver, that works even without + * ACPI, but needs symbols from this driver + */ + +static int __init +acpi_processor_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_init"); + + memset(&processors, 0, sizeof(processors)); + memset(&errata, 0, sizeof(errata)); + + acpi_processor_dir = proc_mkdir(ACPI_PROCESSOR_CLASS, acpi_root_dir); + if (!acpi_processor_dir) + return_VALUE(0); + acpi_processor_dir->owner = THIS_MODULE; + + result = acpi_bus_register_driver(&acpi_processor_driver); + if (result < 0) { + remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); + return_VALUE(0); + } + + acpi_processor_install_hotplug_notify(); + + acpi_thermal_cpufreq_init(); + + acpi_processor_ppc_init(); + + return_VALUE(0); +} + + +static void __exit +acpi_processor_exit (void) +{ + ACPI_FUNCTION_TRACE("acpi_processor_exit"); + + acpi_processor_ppc_exit(); + + acpi_thermal_cpufreq_exit(); + + acpi_processor_uninstall_hotplug_notify(); + + acpi_bus_unregister_driver(&acpi_processor_driver); + + remove_proc_entry(ACPI_PROCESSOR_CLASS, acpi_root_dir); + + return_VOID; +} + + +module_init(acpi_processor_init); +module_exit(acpi_processor_exit); + +EXPORT_SYMBOL(acpi_processor_set_thermal_limit); + +MODULE_ALIAS("processor"); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c new file mode 100644 index 00000000000..05a17812d52 --- /dev/null +++ b/drivers/acpi/processor_idle.c @@ -0,0 +1,1017 @@ +/* + * processor_idle - idle state submodule to the ACPI processor driver + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> + * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> + * - Added processor hotplug support + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/acpi.h> +#include <linux/dmi.h> +#include <linux/moduleparam.h> + +#include <asm/io.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_bus.h> +#include <acpi/processor.h> + +#define ACPI_PROCESSOR_COMPONENT 0x01000000 +#define ACPI_PROCESSOR_CLASS "processor" +#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME ("acpi_processor") + +#define ACPI_PROCESSOR_FILE_POWER "power" + +#define US_TO_PM_TIMER_TICKS(t) ((t * (PM_TIMER_FREQUENCY/1000)) / 1000) +#define C2_OVERHEAD 4 /* 1us (3.579 ticks per us) */ +#define C3_OVERHEAD 4 /* 1us (3.579 ticks per us) */ + +static void (*pm_idle_save)(void); +module_param(max_cstate, uint, 0644); + +static unsigned int nocst = 0; +module_param(nocst, uint, 0000); + +/* + * bm_history -- bit-mask with a bit per jiffy of bus-master activity + * 1000 HZ: 0xFFFFFFFF: 32 jiffies = 32ms + * 800 HZ: 0xFFFFFFFF: 32 jiffies = 40ms + * 100 HZ: 0x0000000F: 4 jiffies = 40ms + * reduce history for more aggressive entry into C3 + */ +static unsigned int bm_history = (HZ >= 800 ? 0xFFFFFFFF : ((1U << (HZ / 25)) - 1)); +module_param(bm_history, uint, 0644); +/* -------------------------------------------------------------------------- + Power Management + -------------------------------------------------------------------------- */ + +/* + * IBM ThinkPad R40e crashes mysteriously when going into C2 or C3. + * For now disable this. Probably a bug somewhere else. + * + * To skip this limit, boot/load with a large max_cstate limit. + */ +static int no_c2c3(struct dmi_system_id *id) +{ + if (max_cstate > ACPI_PROCESSOR_MAX_POWER) + return 0; + + printk(KERN_NOTICE PREFIX "%s detected - C2,C3 disabled." + " Override with \"processor.max_cstate=%d\"\n", id->ident, + ACPI_PROCESSOR_MAX_POWER + 1); + + max_cstate = 1; + + return 0; +} + + + + +static struct dmi_system_id __initdata processor_power_dmi_table[] = { + { no_c2c3, "IBM ThinkPad R40e", { + DMI_MATCH(DMI_BIOS_VENDOR,"IBM"), + DMI_MATCH(DMI_BIOS_VERSION,"1SET60WW") }}, + { no_c2c3, "Medion 41700", { + DMI_MATCH(DMI_BIOS_VENDOR,"Phoenix Technologies LTD"), + DMI_MATCH(DMI_BIOS_VERSION,"R01-A1J") }}, + {}, +}; + + +static inline u32 +ticks_elapsed ( + u32 t1, + u32 t2) +{ + if (t2 >= t1) + return (t2 - t1); + else if (!acpi_fadt.tmr_val_ext) + return (((0x00FFFFFF - t1) + t2) & 0x00FFFFFF); + else + return ((0xFFFFFFFF - t1) + t2); +} + + +static void +acpi_processor_power_activate ( + struct acpi_processor *pr, + struct acpi_processor_cx *new) +{ + struct acpi_processor_cx *old; + + if (!pr || !new) + return; + + old = pr->power.state; + + if (old) + old->promotion.count = 0; + new->demotion.count = 0; + + /* Cleanup from old state. */ + if (old) { + switch (old->type) { + case ACPI_STATE_C3: + /* Disable bus master reload */ + if (new->type != ACPI_STATE_C3) + acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 0, ACPI_MTX_DO_NOT_LOCK); + break; + } + } + + /* Prepare to use new state. */ + switch (new->type) { + case ACPI_STATE_C3: + /* Enable bus master reload */ + if (old->type != ACPI_STATE_C3) + acpi_set_register(ACPI_BITREG_BUS_MASTER_RLD, 1, ACPI_MTX_DO_NOT_LOCK); + break; + } + + pr->power.state = new; + + return; +} + + +static void acpi_processor_idle (void) +{ + struct acpi_processor *pr = NULL; + struct acpi_processor_cx *cx = NULL; + struct acpi_processor_cx *next_state = NULL; + int sleep_ticks = 0; + u32 t1, t2 = 0; + + pr = processors[_smp_processor_id()]; + if (!pr) + return; + + /* + * Interrupts must be disabled during bus mastering calculations and + * for C2/C3 transitions. + */ + local_irq_disable(); + + /* + * Check whether we truly need to go idle, or should + * reschedule: + */ + if (unlikely(need_resched())) { + local_irq_enable(); + return; + } + + cx = pr->power.state; + if (!cx) + goto easy_out; + + /* + * Check BM Activity + * ----------------- + * Check for bus mastering activity (if required), record, and check + * for demotion. + */ + if (pr->flags.bm_check) { + u32 bm_status = 0; + unsigned long diff = jiffies - pr->power.bm_check_timestamp; + + if (diff > 32) + diff = 32; + + while (diff) { + /* if we didn't get called, assume there was busmaster activity */ + diff--; + if (diff) + pr->power.bm_activity |= 0x1; + pr->power.bm_activity <<= 1; + } + + acpi_get_register(ACPI_BITREG_BUS_MASTER_STATUS, + &bm_status, ACPI_MTX_DO_NOT_LOCK); + if (bm_status) { + pr->power.bm_activity++; + acpi_set_register(ACPI_BITREG_BUS_MASTER_STATUS, + 1, ACPI_MTX_DO_NOT_LOCK); + } + /* + * PIIX4 Erratum #18: Note that BM_STS doesn't always reflect + * the true state of bus mastering activity; forcing us to + * manually check the BMIDEA bit of each IDE channel. + */ + else if (errata.piix4.bmisx) { + if ((inb_p(errata.piix4.bmisx + 0x02) & 0x01) + || (inb_p(errata.piix4.bmisx + 0x0A) & 0x01)) + pr->power.bm_activity++; + } + + pr->power.bm_check_timestamp = jiffies; + + /* + * Apply bus mastering demotion policy. Automatically demote + * to avoid a faulty transition. Note that the processor + * won't enter a low-power state during this call (to this + * funciton) but should upon the next. + * + * TBD: A better policy might be to fallback to the demotion + * state (use it for this quantum only) istead of + * demoting -- and rely on duration as our sole demotion + * qualification. This may, however, introduce DMA + * issues (e.g. floppy DMA transfer overrun/underrun). + */ + if (pr->power.bm_activity & cx->demotion.threshold.bm) { + local_irq_enable(); + next_state = cx->demotion.state; + goto end; + } + } + + cx->usage++; + + /* + * Sleep: + * ------ + * Invoke the current Cx state to put the processor to sleep. + */ + switch (cx->type) { + + case ACPI_STATE_C1: + /* + * Invoke C1. + * Use the appropriate idle routine, the one that would + * be used without acpi C-states. + */ + if (pm_idle_save) + pm_idle_save(); + else + safe_halt(); + /* + * TBD: Can't get time duration while in C1, as resumes + * go to an ISR rather than here. Need to instrument + * base interrupt handler. + */ + sleep_ticks = 0xFFFFFFFF; + break; + + case ACPI_STATE_C2: + /* Get start time (ticks) */ + t1 = inl(acpi_fadt.xpm_tmr_blk.address); + /* Invoke C2 */ + inb(cx->address); + /* Dummy op - must do something useless after P_LVL2 read */ + t2 = inl(acpi_fadt.xpm_tmr_blk.address); + /* Get end time (ticks) */ + t2 = inl(acpi_fadt.xpm_tmr_blk.address); + /* Re-enable interrupts */ + local_irq_enable(); + /* Compute time (ticks) that we were actually asleep */ + sleep_ticks = ticks_elapsed(t1, t2) - cx->latency_ticks - C2_OVERHEAD; + break; + + case ACPI_STATE_C3: + /* Disable bus master arbitration */ + acpi_set_register(ACPI_BITREG_ARB_DISABLE, 1, ACPI_MTX_DO_NOT_LOCK); + /* Get start time (ticks) */ + t1 = inl(acpi_fadt.xpm_tmr_blk.address); + /* Invoke C3 */ + inb(cx->address); + /* Dummy op - must do something useless after P_LVL3 read */ + t2 = inl(acpi_fadt.xpm_tmr_blk.address); + /* Get end time (ticks) */ + t2 = inl(acpi_fadt.xpm_tmr_blk.address); + /* Enable bus master arbitration */ + acpi_set_register(ACPI_BITREG_ARB_DISABLE, 0, ACPI_MTX_DO_NOT_LOCK); + /* Re-enable interrupts */ + local_irq_enable(); + /* Compute time (ticks) that we were actually asleep */ + sleep_ticks = ticks_elapsed(t1, t2) - cx->latency_ticks - C3_OVERHEAD; + break; + + default: + local_irq_enable(); + return; + } + + next_state = pr->power.state; + + /* + * Promotion? + * ---------- + * Track the number of longs (time asleep is greater than threshold) + * and promote when the count threshold is reached. Note that bus + * mastering activity may prevent promotions. + * Do not promote above max_cstate. + */ + if (cx->promotion.state && + ((cx->promotion.state - pr->power.states) <= max_cstate)) { + if (sleep_ticks > cx->promotion.threshold.ticks) { + cx->promotion.count++; + cx->demotion.count = 0; + if (cx->promotion.count >= cx->promotion.threshold.count) { + if (pr->flags.bm_check) { + if (!(pr->power.bm_activity & cx->promotion.threshold.bm)) { + next_state = cx->promotion.state; + goto end; + } + } + else { + next_state = cx->promotion.state; + goto end; + } + } + } + } + + /* + * Demotion? + * --------- + * Track the number of shorts (time asleep is less than time threshold) + * and demote when the usage threshold is reached. + */ + if (cx->demotion.state) { + if (sleep_ticks < cx->demotion.threshold.ticks) { + cx->demotion.count++; + cx->promotion.count = 0; + if (cx->demotion.count >= cx->demotion.threshold.count) { + next_state = cx->demotion.state; + goto end; + } + } + } + +end: + /* + * Demote if current state exceeds max_cstate + */ + if ((pr->power.state - pr->power.states) > max_cstate) { + if (cx->demotion.state) + next_state = cx->demotion.state; + } + + /* + * New Cx State? + * ------------- + * If we're going to start using a new Cx state we must clean up + * from the previous and prepare to use the new. + */ + if (next_state != pr->power.state) + acpi_processor_power_activate(pr, next_state); + + return; + + easy_out: + /* do C1 instead of busy loop */ + if (pm_idle_save) + pm_idle_save(); + else + safe_halt(); + return; +} + + +static int +acpi_processor_set_power_policy ( + struct acpi_processor *pr) +{ + unsigned int i; + unsigned int state_is_set = 0; + struct acpi_processor_cx *lower = NULL; + struct acpi_processor_cx *higher = NULL; + struct acpi_processor_cx *cx; + + ACPI_FUNCTION_TRACE("acpi_processor_set_power_policy"); + + if (!pr) + return_VALUE(-EINVAL); + + /* + * This function sets the default Cx state policy (OS idle handler). + * Our scheme is to promote quickly to C2 but more conservatively + * to C3. We're favoring C2 for its characteristics of low latency + * (quick response), good power savings, and ability to allow bus + * mastering activity. Note that the Cx state policy is completely + * customizable and can be altered dynamically. + */ + + /* startup state */ + for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) { + cx = &pr->power.states[i]; + if (!cx->valid) + continue; + + if (!state_is_set) + pr->power.state = cx; + state_is_set++; + break; + } + + if (!state_is_set) + return_VALUE(-ENODEV); + + /* demotion */ + for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) { + cx = &pr->power.states[i]; + if (!cx->valid) + continue; + + if (lower) { + cx->demotion.state = lower; + cx->demotion.threshold.ticks = cx->latency_ticks; + cx->demotion.threshold.count = 1; + if (cx->type == ACPI_STATE_C3) + cx->demotion.threshold.bm = bm_history; + } + + lower = cx; + } + + /* promotion */ + for (i = (ACPI_PROCESSOR_MAX_POWER - 1); i > 0; i--) { + cx = &pr->power.states[i]; + if (!cx->valid) + continue; + + if (higher) { + cx->promotion.state = higher; + cx->promotion.threshold.ticks = cx->latency_ticks; + if (cx->type >= ACPI_STATE_C2) + cx->promotion.threshold.count = 4; + else + cx->promotion.threshold.count = 10; + if (higher->type == ACPI_STATE_C3) + cx->promotion.threshold.bm = bm_history; + } + + higher = cx; + } + + return_VALUE(0); +} + + +static int acpi_processor_get_power_info_fadt (struct acpi_processor *pr) +{ + int i; + + ACPI_FUNCTION_TRACE("acpi_processor_get_power_info_fadt"); + + if (!pr) + return_VALUE(-EINVAL); + + if (!pr->pblk) + return_VALUE(-ENODEV); + + for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++) + memset(pr->power.states, 0, sizeof(struct acpi_processor_cx)); + + /* if info is obtained from pblk/fadt, type equals state */ + pr->power.states[ACPI_STATE_C1].type = ACPI_STATE_C1; + pr->power.states[ACPI_STATE_C2].type = ACPI_STATE_C2; + pr->power.states[ACPI_STATE_C3].type = ACPI_STATE_C3; + + /* the C0 state only exists as a filler in our array, + * and all processors need to support C1 */ + pr->power.states[ACPI_STATE_C0].valid = 1; + pr->power.states[ACPI_STATE_C1].valid = 1; + + /* determine C2 and C3 address from pblk */ + pr->power.states[ACPI_STATE_C2].address = pr->pblk + 4; + pr->power.states[ACPI_STATE_C3].address = pr->pblk + 5; + + /* determine latencies from FADT */ + pr->power.states[ACPI_STATE_C2].latency = acpi_fadt.plvl2_lat; + pr->power.states[ACPI_STATE_C3].latency = acpi_fadt.plvl3_lat; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "lvl2[0x%08x] lvl3[0x%08x]\n", + pr->power.states[ACPI_STATE_C2].address, + pr->power.states[ACPI_STATE_C3].address)); + + return_VALUE(0); +} + + +static int acpi_processor_get_power_info_cst (struct acpi_processor *pr) +{ + acpi_status status = 0; + acpi_integer count; + int i; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *cst; + + ACPI_FUNCTION_TRACE("acpi_processor_get_power_info_cst"); + + if (errata.smp) + return_VALUE(-ENODEV); + + if (nocst) + return_VALUE(-ENODEV); + + pr->power.count = 0; + for (i = 0; i < ACPI_PROCESSOR_MAX_POWER; i++) + memset(pr->power.states, 0, sizeof(struct acpi_processor_cx)); + + status = acpi_evaluate_object(pr->handle, "_CST", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No _CST, giving up\n")); + return_VALUE(-ENODEV); + } + + cst = (union acpi_object *) buffer.pointer; + + /* There must be at least 2 elements */ + if (!cst || (cst->type != ACPI_TYPE_PACKAGE) || cst->package.count < 2) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "not enough elements in _CST\n")); + status = -EFAULT; + goto end; + } + + count = cst->package.elements[0].integer.value; + + /* Validate number of power states. */ + if (count < 1 || count != cst->package.count - 1) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "count given by _CST is not valid\n")); + status = -EFAULT; + goto end; + } + + /* We support up to ACPI_PROCESSOR_MAX_POWER. */ + if (count > ACPI_PROCESSOR_MAX_POWER) { + printk(KERN_WARNING "Limiting number of power states to max (%d)\n", ACPI_PROCESSOR_MAX_POWER); + printk(KERN_WARNING "Please increase ACPI_PROCESSOR_MAX_POWER if needed.\n"); + count = ACPI_PROCESSOR_MAX_POWER; + } + + /* Tell driver that at least _CST is supported. */ + pr->flags.has_cst = 1; + + for (i = 1; i <= count; i++) { + union acpi_object *element; + union acpi_object *obj; + struct acpi_power_register *reg; + struct acpi_processor_cx cx; + + memset(&cx, 0, sizeof(cx)); + + element = (union acpi_object *) &(cst->package.elements[i]); + if (element->type != ACPI_TYPE_PACKAGE) + continue; + + if (element->package.count != 4) + continue; + + obj = (union acpi_object *) &(element->package.elements[0]); + + if (obj->type != ACPI_TYPE_BUFFER) + continue; + + reg = (struct acpi_power_register *) obj->buffer.pointer; + + if (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO && + (reg->space_id != ACPI_ADR_SPACE_FIXED_HARDWARE)) + continue; + + cx.address = (reg->space_id == ACPI_ADR_SPACE_FIXED_HARDWARE) ? + 0 : reg->address; + + /* There should be an easy way to extract an integer... */ + obj = (union acpi_object *) &(element->package.elements[1]); + if (obj->type != ACPI_TYPE_INTEGER) + continue; + + cx.type = obj->integer.value; + + if ((cx.type != ACPI_STATE_C1) && + (reg->space_id != ACPI_ADR_SPACE_SYSTEM_IO)) + continue; + + if ((cx.type < ACPI_STATE_C1) || + (cx.type > ACPI_STATE_C3)) + continue; + + obj = (union acpi_object *) &(element->package.elements[2]); + if (obj->type != ACPI_TYPE_INTEGER) + continue; + + cx.latency = obj->integer.value; + + obj = (union acpi_object *) &(element->package.elements[3]); + if (obj->type != ACPI_TYPE_INTEGER) + continue; + + cx.power = obj->integer.value; + + (pr->power.count)++; + memcpy(&(pr->power.states[pr->power.count]), &cx, sizeof(cx)); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d power states\n", pr->power.count)); + + /* Validate number of power states discovered */ + if (pr->power.count < 2) + status = -ENODEV; + +end: + acpi_os_free(buffer.pointer); + + return_VALUE(status); +} + + +static void acpi_processor_power_verify_c2(struct acpi_processor_cx *cx) +{ + ACPI_FUNCTION_TRACE("acpi_processor_get_power_verify_c2"); + + if (!cx->address) + return_VOID; + + /* + * C2 latency must be less than or equal to 100 + * microseconds. + */ + else if (cx->latency > ACPI_PROCESSOR_MAX_C2_LATENCY) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "latency too large [%d]\n", + cx->latency)); + return_VOID; + } + + /* We're (currently) only supporting C2 on UP */ + else if (errata.smp) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C2 not supported in SMP mode\n")); + return_VOID; + } + + /* + * Otherwise we've met all of our C2 requirements. + * Normalize the C2 latency to expidite policy + */ + cx->valid = 1; + cx->latency_ticks = US_TO_PM_TIMER_TICKS(cx->latency); + + return_VOID; +} + + +static void acpi_processor_power_verify_c3( + struct acpi_processor *pr, + struct acpi_processor_cx *cx) +{ + ACPI_FUNCTION_TRACE("acpi_processor_get_power_verify_c3"); + + if (!cx->address) + return_VOID; + + /* + * C3 latency must be less than or equal to 1000 + * microseconds. + */ + else if (cx->latency > ACPI_PROCESSOR_MAX_C3_LATENCY) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "latency too large [%d]\n", + cx->latency)); + return_VOID; + } + + /* bus mastering control is necessary */ + else if (!pr->flags.bm_control) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C3 support requires bus mastering control\n")); + return_VOID; + } + + /* We're (currently) only supporting C2 on UP */ + else if (errata.smp) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C3 not supported in SMP mode\n")); + return_VOID; + } + + /* + * PIIX4 Erratum #18: We don't support C3 when Type-F (fast) + * DMA transfers are used by any ISA device to avoid livelock. + * Note that we could disable Type-F DMA (as recommended by + * the erratum), but this is known to disrupt certain ISA + * devices thus we take the conservative approach. + */ + else if (errata.piix4.fdma) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "C3 not supported on PIIX4 with Type-F DMA\n")); + return_VOID; + } + + /* + * Otherwise we've met all of our C3 requirements. + * Normalize the C3 latency to expidite policy. Enable + * checking of bus mastering status (bm_check) so we can + * use this in our C3 policy + */ + cx->valid = 1; + cx->latency_ticks = US_TO_PM_TIMER_TICKS(cx->latency); + pr->flags.bm_check = 1; + + return_VOID; +} + + +static int acpi_processor_power_verify(struct acpi_processor *pr) +{ + unsigned int i; + unsigned int working = 0; + + for (i=1; i < ACPI_PROCESSOR_MAX_POWER; i++) { + struct acpi_processor_cx *cx = &pr->power.states[i]; + + switch (cx->type) { + case ACPI_STATE_C1: + cx->valid = 1; + break; + + case ACPI_STATE_C2: + acpi_processor_power_verify_c2(cx); + break; + + case ACPI_STATE_C3: + acpi_processor_power_verify_c3(pr, cx); + break; + } + + if (cx->valid) + working++; + } + + return (working); +} + +static int acpi_processor_get_power_info ( + struct acpi_processor *pr) +{ + unsigned int i; + int result; + + ACPI_FUNCTION_TRACE("acpi_processor_get_power_info"); + + /* NOTE: the idle thread may not be running while calling + * this function */ + + result = acpi_processor_get_power_info_cst(pr); + if ((result) || (acpi_processor_power_verify(pr) < 2)) { + result = acpi_processor_get_power_info_fadt(pr); + if (result) + return_VALUE(result); + + if (acpi_processor_power_verify(pr) < 2) + return_VALUE(-ENODEV); + } + + /* + * Set Default Policy + * ------------------ + * Now that we know which states are supported, set the default + * policy. Note that this policy can be changed dynamically + * (e.g. encourage deeper sleeps to conserve battery life when + * not on AC). + */ + result = acpi_processor_set_power_policy(pr); + if (result) + return_VALUE(result); + + /* + * if one state of type C2 or C3 is available, mark this + * CPU as being "idle manageable" + */ + for (i = 1; i < ACPI_PROCESSOR_MAX_POWER; i++) { + if (pr->power.states[i].valid) + pr->power.count = i; + if ((pr->power.states[i].valid) && + (pr->power.states[i].type >= ACPI_STATE_C2)) + pr->flags.power = 1; + } + + return_VALUE(0); +} + +int acpi_processor_cst_has_changed (struct acpi_processor *pr) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_cst_has_changed"); + + if (!pr) + return_VALUE(-EINVAL); + + if (errata.smp || nocst) { + return_VALUE(-ENODEV); + } + + if (!pr->flags.power_setup_done) + return_VALUE(-ENODEV); + + /* Fall back to the default idle loop */ + pm_idle = pm_idle_save; + synchronize_kernel(); + + pr->flags.power = 0; + result = acpi_processor_get_power_info(pr); + if ((pr->flags.power == 1) && (pr->flags.power_setup_done)) + pm_idle = acpi_processor_idle; + + return_VALUE(result); +} + +/* proc interface */ + +static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_processor *pr = (struct acpi_processor *)seq->private; + unsigned int i; + + ACPI_FUNCTION_TRACE("acpi_processor_power_seq_show"); + + if (!pr) + goto end; + + seq_printf(seq, "active state: C%zd\n" + "max_cstate: C%d\n" + "bus master activity: %08x\n", + pr->power.state ? pr->power.state - pr->power.states : 0, + max_cstate, + (unsigned)pr->power.bm_activity); + + seq_puts(seq, "states:\n"); + + for (i = 1; i <= pr->power.count; i++) { + seq_printf(seq, " %cC%d: ", + (&pr->power.states[i] == pr->power.state?'*':' '), i); + + if (!pr->power.states[i].valid) { + seq_puts(seq, "<not supported>\n"); + continue; + } + + switch (pr->power.states[i].type) { + case ACPI_STATE_C1: + seq_printf(seq, "type[C1] "); + break; + case ACPI_STATE_C2: + seq_printf(seq, "type[C2] "); + break; + case ACPI_STATE_C3: + seq_printf(seq, "type[C3] "); + break; + default: + seq_printf(seq, "type[--] "); + break; + } + + if (pr->power.states[i].promotion.state) + seq_printf(seq, "promotion[C%zd] ", + (pr->power.states[i].promotion.state - + pr->power.states)); + else + seq_puts(seq, "promotion[--] "); + + if (pr->power.states[i].demotion.state) + seq_printf(seq, "demotion[C%zd] ", + (pr->power.states[i].demotion.state - + pr->power.states)); + else + seq_puts(seq, "demotion[--] "); + + seq_printf(seq, "latency[%03d] usage[%08d]\n", + pr->power.states[i].latency, + pr->power.states[i].usage); + } + +end: + return_VALUE(0); +} + +static int acpi_processor_power_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_processor_power_seq_show, + PDE(inode)->data); +} + +static struct file_operations acpi_processor_power_fops = { + .open = acpi_processor_power_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +int acpi_processor_power_init(struct acpi_processor *pr, struct acpi_device *device) +{ + acpi_status status = 0; + static int first_run = 0; + struct proc_dir_entry *entry = NULL; + unsigned int i; + + ACPI_FUNCTION_TRACE("acpi_processor_power_init"); + + if (!first_run) { + dmi_check_system(processor_power_dmi_table); + if (max_cstate < ACPI_C_STATES_MAX) + printk(KERN_NOTICE "ACPI: processor limited to max C-state %d\n", max_cstate); + first_run++; + } + + if (!errata.smp && (pr->id == 0) && acpi_fadt.cst_cnt && !nocst) { + status = acpi_os_write_port(acpi_fadt.smi_cmd, acpi_fadt.cst_cnt, 8); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Notifying BIOS of _CST ability failed\n")); + } + } + + acpi_processor_get_power_info(pr); + + /* + * Install the idle handler if processor power management is supported. + * Note that we use previously set idle handler will be used on + * platforms that only support C1. + */ + if ((pr->flags.power) && (!boot_option_idle_override)) { + printk(KERN_INFO PREFIX "CPU%d (power states:", pr->id); + for (i = 1; i <= pr->power.count; i++) + if (pr->power.states[i].valid) + printk(" C%d[C%d]", i, pr->power.states[i].type); + printk(")\n"); + + if (pr->id == 0) { + pm_idle_save = pm_idle; + pm_idle = acpi_processor_idle; + } + } + + /* 'power' [R] */ + entry = create_proc_entry(ACPI_PROCESSOR_FILE_POWER, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_PROCESSOR_FILE_POWER)); + else { + entry->proc_fops = &acpi_processor_power_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + pr->flags.power_setup_done = 1; + + return_VALUE(0); +} + +int acpi_processor_power_exit(struct acpi_processor *pr, struct acpi_device *device) +{ + ACPI_FUNCTION_TRACE("acpi_processor_power_exit"); + + pr->flags.power_setup_done = 0; + + if (acpi_device_dir(device)) + remove_proc_entry(ACPI_PROCESSOR_FILE_POWER,acpi_device_dir(device)); + + /* Unregister the idle handler when processor #0 is removed. */ + if (pr->id == 0) { + pm_idle = pm_idle_save; + + /* + * We are about to unload the current idle thread pm callback + * (pm_idle), Wait for all processors to update cached/local + * copies of pm_idle before proceeding. + */ + cpu_idle_wait(); + } + + return_VALUE(0); +} diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c new file mode 100644 index 00000000000..a9a1a8fe319 --- /dev/null +++ b/drivers/acpi/processor_perflib.c @@ -0,0 +1,666 @@ +/* + * processor_perflib.c - ACPI Processor P-States Library ($Revision: 71 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> + * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> + * - Added processor hotplug support + * + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + */ + + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/cpufreq.h> + +#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +#include <asm/uaccess.h> +#endif + +#include <acpi/acpi_bus.h> +#include <acpi/processor.h> + + +#define ACPI_PROCESSOR_COMPONENT 0x01000000 +#define ACPI_PROCESSOR_CLASS "processor" +#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" +#define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME ("acpi_processor") + + +static DECLARE_MUTEX(performance_sem); + +/* + * _PPC support is implemented as a CPUfreq policy notifier: + * This means each time a CPUfreq driver registered also with + * the ACPI core is asked to change the speed policy, the maximum + * value is adjusted so that it is within the platform limit. + * + * Also, when a new platform limit value is detected, the CPUfreq + * policy is adjusted accordingly. + */ + +#define PPC_REGISTERED 1 +#define PPC_IN_USE 2 + +static int acpi_processor_ppc_status = 0; + +static int acpi_processor_ppc_notifier(struct notifier_block *nb, + unsigned long event, + void *data) +{ + struct cpufreq_policy *policy = data; + struct acpi_processor *pr; + unsigned int ppc = 0; + + down(&performance_sem); + + if (event != CPUFREQ_INCOMPATIBLE) + goto out; + + pr = processors[policy->cpu]; + if (!pr || !pr->performance) + goto out; + + ppc = (unsigned int) pr->performance_platform_limit; + if (!ppc) + goto out; + + if (ppc > pr->performance->state_count) + goto out; + + cpufreq_verify_within_limits(policy, 0, + pr->performance->states[ppc].core_frequency * 1000); + + out: + up(&performance_sem); + + return 0; +} + + +static struct notifier_block acpi_ppc_notifier_block = { + .notifier_call = acpi_processor_ppc_notifier, +}; + + +static int +acpi_processor_get_platform_limit ( + struct acpi_processor* pr) +{ + acpi_status status = 0; + unsigned long ppc = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_get_platform_limit"); + + if (!pr) + return_VALUE(-EINVAL); + + /* + * _PPC indicates the maximum state currently supported by the platform + * (e.g. 0 = states 0..n; 1 = states 1..n; etc. + */ + status = acpi_evaluate_integer(pr->handle, "_PPC", NULL, &ppc); + + if (status != AE_NOT_FOUND) + acpi_processor_ppc_status |= PPC_IN_USE; + + if(ACPI_FAILURE(status) && status != AE_NOT_FOUND) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PPC\n")); + return_VALUE(-ENODEV); + } + + pr->performance_platform_limit = (int) ppc; + + return_VALUE(0); +} + + +int acpi_processor_ppc_has_changed( + struct acpi_processor *pr) +{ + int ret = acpi_processor_get_platform_limit(pr); + if (ret < 0) + return (ret); + else + return cpufreq_update_policy(pr->id); +} + + +void acpi_processor_ppc_init(void) { + if (!cpufreq_register_notifier(&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER)) + acpi_processor_ppc_status |= PPC_REGISTERED; + else + printk(KERN_DEBUG "Warning: Processor Platform Limit not supported.\n"); +} + + +void acpi_processor_ppc_exit(void) { + if (acpi_processor_ppc_status & PPC_REGISTERED) + cpufreq_unregister_notifier(&acpi_ppc_notifier_block, CPUFREQ_POLICY_NOTIFIER); + + acpi_processor_ppc_status &= ~PPC_REGISTERED; +} + +/* + * when registering a cpufreq driver with this ACPI processor driver, the + * _PCT and _PSS structures are read out and written into struct + * acpi_processor_performance. + */ +static int acpi_processor_set_pdc (struct acpi_processor *pr) +{ + acpi_status status = AE_OK; + u32 arg0_buf[3]; + union acpi_object arg0 = {ACPI_TYPE_BUFFER}; + struct acpi_object_list no_object = {1, &arg0}; + struct acpi_object_list *pdc; + + ACPI_FUNCTION_TRACE("acpi_processor_set_pdc"); + + arg0.buffer.length = 12; + arg0.buffer.pointer = (u8 *) arg0_buf; + arg0_buf[0] = ACPI_PDC_REVISION_ID; + arg0_buf[1] = 0; + arg0_buf[2] = 0; + + pdc = (pr->performance->pdc) ? pr->performance->pdc : &no_object; + + status = acpi_evaluate_object(pr->handle, "_PDC", pdc, NULL); + + if ((ACPI_FAILURE(status)) && (pr->performance->pdc)) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Error evaluating _PDC, using legacy perf. control...\n")); + + return_VALUE(status); +} + + +static int +acpi_processor_get_performance_control ( + struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = 0; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *pct = NULL; + union acpi_object obj = {0}; + + ACPI_FUNCTION_TRACE("acpi_processor_get_performance_control"); + + status = acpi_evaluate_object(pr->handle, "_PCT", NULL, &buffer); + if(ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PCT\n")); + return_VALUE(-ENODEV); + } + + pct = (union acpi_object *) buffer.pointer; + if (!pct || (pct->type != ACPI_TYPE_PACKAGE) + || (pct->package.count != 2)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PCT data\n")); + result = -EFAULT; + goto end; + } + + /* + * control_register + */ + + obj = pct->package.elements[0]; + + if ((obj.type != ACPI_TYPE_BUFFER) + || (obj.buffer.length < sizeof(struct acpi_pct_register)) + || (obj.buffer.pointer == NULL)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Invalid _PCT data (control_register)\n")); + result = -EFAULT; + goto end; + } + memcpy(&pr->performance->control_register, obj.buffer.pointer, sizeof(struct acpi_pct_register)); + + + /* + * status_register + */ + + obj = pct->package.elements[1]; + + if ((obj.type != ACPI_TYPE_BUFFER) + || (obj.buffer.length < sizeof(struct acpi_pct_register)) + || (obj.buffer.pointer == NULL)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Invalid _PCT data (status_register)\n")); + result = -EFAULT; + goto end; + } + + memcpy(&pr->performance->status_register, obj.buffer.pointer, sizeof(struct acpi_pct_register)); + +end: + acpi_os_free(buffer.pointer); + + return_VALUE(result); +} + + +static int +acpi_processor_get_performance_states ( + struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_buffer format = {sizeof("NNNNNN"), "NNNNNN"}; + struct acpi_buffer state = {0, NULL}; + union acpi_object *pss = NULL; + int i; + + ACPI_FUNCTION_TRACE("acpi_processor_get_performance_states"); + + status = acpi_evaluate_object(pr->handle, "_PSS", NULL, &buffer); + if(ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PSS\n")); + return_VALUE(-ENODEV); + } + + pss = (union acpi_object *) buffer.pointer; + if (!pss || (pss->type != ACPI_TYPE_PACKAGE)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data\n")); + result = -EFAULT; + goto end; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d performance states\n", + pss->package.count)); + + pr->performance->state_count = pss->package.count; + pr->performance->states = kmalloc(sizeof(struct acpi_processor_px) * pss->package.count, GFP_KERNEL); + if (!pr->performance->states) { + result = -ENOMEM; + goto end; + } + + for (i = 0; i < pr->performance->state_count; i++) { + + struct acpi_processor_px *px = &(pr->performance->states[i]); + + state.length = sizeof(struct acpi_processor_px); + state.pointer = px; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Extracting state %d\n", i)); + + status = acpi_extract_package(&(pss->package.elements[i]), + &format, &state); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data\n")); + result = -EFAULT; + kfree(pr->performance->states); + goto end; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "State [%d]: core_frequency[%d] power[%d] transition_latency[%d] bus_master_latency[%d] control[0x%x] status[0x%x]\n", + i, + (u32) px->core_frequency, + (u32) px->power, + (u32) px->transition_latency, + (u32) px->bus_master_latency, + (u32) px->control, + (u32) px->status)); + + if (!px->core_frequency) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _PSS data: freq is zero\n")); + result = -EFAULT; + kfree(pr->performance->states); + goto end; + } + } + +end: + acpi_os_free(buffer.pointer); + + return_VALUE(result); +} + + +static int +acpi_processor_get_performance_info ( + struct acpi_processor *pr) +{ + int result = 0; + acpi_status status = AE_OK; + acpi_handle handle = NULL; + + ACPI_FUNCTION_TRACE("acpi_processor_get_performance_info"); + + if (!pr || !pr->performance || !pr->handle) + return_VALUE(-EINVAL); + + acpi_processor_set_pdc(pr); + + status = acpi_get_handle(pr->handle, "_PCT", &handle); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "ACPI-based processor performance control unavailable\n")); + return_VALUE(-ENODEV); + } + + result = acpi_processor_get_performance_control(pr); + if (result) + return_VALUE(result); + + result = acpi_processor_get_performance_states(pr); + if (result) + return_VALUE(result); + + result = acpi_processor_get_platform_limit(pr); + if (result) + return_VALUE(result); + + return_VALUE(0); +} + + +int acpi_processor_notify_smm(struct module *calling_module) { + acpi_status status; + static int is_done = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_notify_smm"); + + if (!(acpi_processor_ppc_status & PPC_REGISTERED)) + return_VALUE(-EBUSY); + + if (!try_module_get(calling_module)) + return_VALUE(-EINVAL); + + /* is_done is set to negative if an error occured, + * and to postitive if _no_ error occured, but SMM + * was already notified. This avoids double notification + * which might lead to unexpected results... + */ + if (is_done > 0) { + module_put(calling_module); + return_VALUE(0); + } + else if (is_done < 0) { + module_put(calling_module); + return_VALUE(is_done); + } + + is_done = -EIO; + + /* Can't write pstate_cnt to smi_cmd if either value is zero */ + if ((!acpi_fadt.smi_cmd) || + (!acpi_fadt.pstate_cnt)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "No SMI port or pstate_cnt\n")); + module_put(calling_module); + return_VALUE(0); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Writing pstate_cnt [0x%x] to smi_cmd [0x%x]\n", acpi_fadt.pstate_cnt, acpi_fadt.smi_cmd)); + + /* FADT v1 doesn't support pstate_cnt, many BIOS vendors use + * it anyway, so we need to support it... */ + if (acpi_fadt_is_v1) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Using v1.0 FADT reserved value for pstate_cnt\n")); + } + + status = acpi_os_write_port (acpi_fadt.smi_cmd, + (u32) acpi_fadt.pstate_cnt, 8); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Failed to write pstate_cnt [0x%x] to " + "smi_cmd [0x%x]\n", acpi_fadt.pstate_cnt, acpi_fadt.smi_cmd)); + module_put(calling_module); + return_VALUE(status); + } + + /* Success. If there's no _PPC, we need to fear nothing, so + * we can allow the cpufreq driver to be rmmod'ed. */ + is_done = 1; + + if (!(acpi_processor_ppc_status & PPC_IN_USE)) + module_put(calling_module); + + return_VALUE(0); +} +EXPORT_SYMBOL(acpi_processor_notify_smm); + + +#ifdef CONFIG_X86_ACPI_CPUFREQ_PROC_INTF +/* /proc/acpi/processor/../performance interface (DEPRECATED) */ + +static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_processor_perf_fops = { + .open = acpi_processor_perf_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int acpi_processor_perf_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_processor *pr = (struct acpi_processor *)seq->private; + int i; + + ACPI_FUNCTION_TRACE("acpi_processor_perf_seq_show"); + + if (!pr) + goto end; + + if (!pr->performance) { + seq_puts(seq, "<not supported>\n"); + goto end; + } + + seq_printf(seq, "state count: %d\n" + "active state: P%d\n", + pr->performance->state_count, + pr->performance->state); + + seq_puts(seq, "states:\n"); + for (i = 0; i < pr->performance->state_count; i++) + seq_printf(seq, " %cP%d: %d MHz, %d mW, %d uS\n", + (i == pr->performance->state?'*':' '), i, + (u32) pr->performance->states[i].core_frequency, + (u32) pr->performance->states[i].power, + (u32) pr->performance->states[i].transition_latency); + +end: + return_VALUE(0); +} + +static int acpi_processor_perf_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_processor_perf_seq_show, + PDE(inode)->data); +} + +static ssize_t +acpi_processor_write_performance ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *data) +{ + int result = 0; + struct seq_file *m = (struct seq_file *) file->private_data; + struct acpi_processor *pr = (struct acpi_processor *) m->private; + struct acpi_processor_performance *perf; + char state_string[12] = {'\0'}; + unsigned int new_state = 0; + struct cpufreq_policy policy; + + ACPI_FUNCTION_TRACE("acpi_processor_write_performance"); + + if (!pr || (count > sizeof(state_string) - 1)) + return_VALUE(-EINVAL); + + perf = pr->performance; + if (!perf) + return_VALUE(-EINVAL); + + if (copy_from_user(state_string, buffer, count)) + return_VALUE(-EFAULT); + + state_string[count] = '\0'; + new_state = simple_strtoul(state_string, NULL, 0); + + if (new_state >= perf->state_count) + return_VALUE(-EINVAL); + + cpufreq_get_policy(&policy, pr->id); + + policy.cpu = pr->id; + policy.min = perf->states[new_state].core_frequency * 1000; + policy.max = perf->states[new_state].core_frequency * 1000; + + result = cpufreq_set_policy(&policy); + if (result) + return_VALUE(result); + + return_VALUE(count); +} + +static void +acpi_cpufreq_add_file ( + struct acpi_processor *pr) +{ + struct proc_dir_entry *entry = NULL; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile"); + + if (acpi_bus_get_device(pr->handle, &device)) + return_VOID; + + /* add file 'performance' [R/W] */ + entry = create_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE, + S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_PROCESSOR_FILE_PERFORMANCE)); + else { + entry->proc_fops = &acpi_processor_perf_fops; + entry->proc_fops->write = acpi_processor_write_performance; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + return_VOID; +} + +static void +acpi_cpufreq_remove_file ( + struct acpi_processor *pr) +{ + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_cpufreq_addfile"); + + if (acpi_bus_get_device(pr->handle, &device)) + return_VOID; + + /* remove file 'performance' */ + remove_proc_entry(ACPI_PROCESSOR_FILE_PERFORMANCE, + acpi_device_dir(device)); + + return_VOID; +} + +#else +static void acpi_cpufreq_add_file (struct acpi_processor *pr) { return; } +static void acpi_cpufreq_remove_file (struct acpi_processor *pr) { return; } +#endif /* CONFIG_X86_ACPI_CPUFREQ_PROC_INTF */ + + +int +acpi_processor_register_performance ( + struct acpi_processor_performance * performance, + unsigned int cpu) +{ + struct acpi_processor *pr; + + ACPI_FUNCTION_TRACE("acpi_processor_register_performance"); + + if (!(acpi_processor_ppc_status & PPC_REGISTERED)) + return_VALUE(-EINVAL); + + down(&performance_sem); + + pr = processors[cpu]; + if (!pr) { + up(&performance_sem); + return_VALUE(-ENODEV); + } + + if (pr->performance) { + up(&performance_sem); + return_VALUE(-EBUSY); + } + + pr->performance = performance; + + if (acpi_processor_get_performance_info(pr)) { + pr->performance = NULL; + up(&performance_sem); + return_VALUE(-EIO); + } + + acpi_cpufreq_add_file(pr); + + up(&performance_sem); + return_VALUE(0); +} +EXPORT_SYMBOL(acpi_processor_register_performance); + + +void +acpi_processor_unregister_performance ( + struct acpi_processor_performance * performance, + unsigned int cpu) +{ + struct acpi_processor *pr; + + ACPI_FUNCTION_TRACE("acpi_processor_unregister_performance"); + + down(&performance_sem); + + pr = processors[cpu]; + if (!pr) { + up(&performance_sem); + return_VOID; + } + + kfree(pr->performance->states); + pr->performance = NULL; + + acpi_cpufreq_remove_file(pr); + + up(&performance_sem); + + return_VOID; +} +EXPORT_SYMBOL(acpi_processor_unregister_performance); diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c new file mode 100644 index 00000000000..12bd980a12e --- /dev/null +++ b/drivers/acpi/processor_thermal.c @@ -0,0 +1,406 @@ +/* + * processor_thermal.c - Passive cooling submodule of the ACPI processor driver + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> + * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> + * - Added processor hotplug support + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +#include <asm/uaccess.h> + +#include <acpi/acpi_bus.h> +#include <acpi/processor.h> +#include <acpi/acpi_drivers.h> + +#define ACPI_PROCESSOR_COMPONENT 0x01000000 +#define ACPI_PROCESSOR_CLASS "processor" +#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME ("acpi_processor") + + +/* -------------------------------------------------------------------------- + Limit Interface + -------------------------------------------------------------------------- */ + +static int +acpi_processor_apply_limit ( + struct acpi_processor* pr) +{ + int result = 0; + u16 px = 0; + u16 tx = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_apply_limit"); + + if (!pr) + return_VALUE(-EINVAL); + + if (!pr->flags.limit) + return_VALUE(-ENODEV); + + if (pr->flags.throttling) { + if (pr->limit.user.tx > tx) + tx = pr->limit.user.tx; + if (pr->limit.thermal.tx > tx) + tx = pr->limit.thermal.tx; + + result = acpi_processor_set_throttling(pr, tx); + if (result) + goto end; + } + + pr->limit.state.px = px; + pr->limit.state.tx = tx; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d] limit set to (P%d:T%d)\n", + pr->id, + pr->limit.state.px, + pr->limit.state.tx)); + +end: + if (result) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to set limit\n")); + + return_VALUE(result); +} + + +#ifdef CONFIG_CPU_FREQ + +/* If a passive cooling situation is detected, primarily CPUfreq is used, as it + * offers (in most cases) voltage scaling in addition to frequency scaling, and + * thus a cubic (instead of linear) reduction of energy. Also, we allow for + * _any_ cpufreq driver and not only the acpi-cpufreq driver. + */ + +static unsigned int cpufreq_thermal_reduction_pctg[NR_CPUS]; +static unsigned int acpi_thermal_cpufreq_is_init = 0; + + +static int cpu_has_cpufreq(unsigned int cpu) +{ + struct cpufreq_policy policy; + if (!acpi_thermal_cpufreq_is_init) + return -ENODEV; + if (!cpufreq_get_policy(&policy, cpu)) + return -ENODEV; + return 0; +} + + +static int acpi_thermal_cpufreq_increase(unsigned int cpu) +{ + if (!cpu_has_cpufreq(cpu)) + return -ENODEV; + + if (cpufreq_thermal_reduction_pctg[cpu] < 60) { + cpufreq_thermal_reduction_pctg[cpu] += 20; + cpufreq_update_policy(cpu); + return 0; + } + + return -ERANGE; +} + + +static int acpi_thermal_cpufreq_decrease(unsigned int cpu) +{ + if (!cpu_has_cpufreq(cpu)) + return -ENODEV; + + if (cpufreq_thermal_reduction_pctg[cpu] >= 20) { + cpufreq_thermal_reduction_pctg[cpu] -= 20; + cpufreq_update_policy(cpu); + return 0; + } + + return -ERANGE; +} + + +static int acpi_thermal_cpufreq_notifier( + struct notifier_block *nb, + unsigned long event, + void *data) +{ + struct cpufreq_policy *policy = data; + unsigned long max_freq = 0; + + if (event != CPUFREQ_ADJUST) + goto out; + + max_freq = (policy->cpuinfo.max_freq * (100 - cpufreq_thermal_reduction_pctg[policy->cpu])) / 100; + + cpufreq_verify_within_limits(policy, 0, max_freq); + + out: + return 0; +} + + +static struct notifier_block acpi_thermal_cpufreq_notifier_block = { + .notifier_call = acpi_thermal_cpufreq_notifier, +}; + + +void acpi_thermal_cpufreq_init(void) { + int i; + + for (i=0; i<NR_CPUS; i++) + cpufreq_thermal_reduction_pctg[i] = 0; + + i = cpufreq_register_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); + if (!i) + acpi_thermal_cpufreq_is_init = 1; +} + +void acpi_thermal_cpufreq_exit(void) { + if (acpi_thermal_cpufreq_is_init) + cpufreq_unregister_notifier(&acpi_thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); + + acpi_thermal_cpufreq_is_init = 0; +} + +#else /* ! CONFIG_CPU_FREQ */ + +static int acpi_thermal_cpufreq_increase(unsigned int cpu) { return -ENODEV; } +static int acpi_thermal_cpufreq_decrease(unsigned int cpu) { return -ENODEV; } + + +#endif + + +int +acpi_processor_set_thermal_limit ( + acpi_handle handle, + int type) +{ + int result = 0; + struct acpi_processor *pr = NULL; + struct acpi_device *device = NULL; + int tx = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_set_thermal_limit"); + + if ((type < ACPI_PROCESSOR_LIMIT_NONE) + || (type > ACPI_PROCESSOR_LIMIT_DECREMENT)) + return_VALUE(-EINVAL); + + result = acpi_bus_get_device(handle, &device); + if (result) + return_VALUE(result); + + pr = (struct acpi_processor *) acpi_driver_data(device); + if (!pr) + return_VALUE(-ENODEV); + + /* Thermal limits are always relative to the current Px/Tx state. */ + if (pr->flags.throttling) + pr->limit.thermal.tx = pr->throttling.state; + + /* + * Our default policy is to only use throttling at the lowest + * performance state. + */ + + tx = pr->limit.thermal.tx; + + switch (type) { + + case ACPI_PROCESSOR_LIMIT_NONE: + do { + result = acpi_thermal_cpufreq_decrease(pr->id); + } while (!result); + tx = 0; + break; + + case ACPI_PROCESSOR_LIMIT_INCREMENT: + /* if going up: P-states first, T-states later */ + + result = acpi_thermal_cpufreq_increase(pr->id); + if (!result) + goto end; + else if (result == -ERANGE) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "At maximum performance state\n")); + + if (pr->flags.throttling) { + if (tx == (pr->throttling.state_count - 1)) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "At maximum throttling state\n")); + else + tx++; + } + break; + + case ACPI_PROCESSOR_LIMIT_DECREMENT: + /* if going down: T-states first, P-states later */ + + if (pr->flags.throttling) { + if (tx == 0) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "At minimum throttling state\n")); + else { + tx--; + goto end; + } + } + + result = acpi_thermal_cpufreq_decrease(pr->id); + if (result == -ERANGE) + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "At minimum performance state\n")); + + break; + } + +end: + if (pr->flags.throttling) { + pr->limit.thermal.px = 0; + pr->limit.thermal.tx = tx; + + result = acpi_processor_apply_limit(pr); + if (result) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to set thermal limit\n")); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Thermal limit now (P%d:T%d)\n", + pr->limit.thermal.px, + pr->limit.thermal.tx)); + } else + result = 0; + + return_VALUE(result); +} + + +int +acpi_processor_get_limit_info ( + struct acpi_processor *pr) +{ + ACPI_FUNCTION_TRACE("acpi_processor_get_limit_info"); + + if (!pr) + return_VALUE(-EINVAL); + + if (pr->flags.throttling) + pr->flags.limit = 1; + + return_VALUE(0); +} + + +/* /proc interface */ + +static int acpi_processor_limit_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_processor *pr = (struct acpi_processor *)seq->private; + + ACPI_FUNCTION_TRACE("acpi_processor_limit_seq_show"); + + if (!pr) + goto end; + + if (!pr->flags.limit) { + seq_puts(seq, "<not supported>\n"); + goto end; + } + + seq_printf(seq, "active limit: P%d:T%d\n" + "user limit: P%d:T%d\n" + "thermal limit: P%d:T%d\n", + pr->limit.state.px, pr->limit.state.tx, + pr->limit.user.px, pr->limit.user.tx, + pr->limit.thermal.px, pr->limit.thermal.tx); + +end: + return_VALUE(0); +} + +static int acpi_processor_limit_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_processor_limit_seq_show, + PDE(inode)->data); +} + +ssize_t acpi_processor_write_limit ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *data) +{ + int result = 0; + struct seq_file *m = (struct seq_file *)file->private_data; + struct acpi_processor *pr = (struct acpi_processor *)m->private; + char limit_string[25] = {'\0'}; + int px = 0; + int tx = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_write_limit"); + + if (!pr || (count > sizeof(limit_string) - 1)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument\n")); + return_VALUE(-EINVAL); + } + + if (copy_from_user(limit_string, buffer, count)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data\n")); + return_VALUE(-EFAULT); + } + + limit_string[count] = '\0'; + + if (sscanf(limit_string, "%d:%d", &px, &tx) != 2) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format\n")); + return_VALUE(-EINVAL); + } + + if (pr->flags.throttling) { + if ((tx < 0) || (tx > (pr->throttling.state_count - 1))) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid tx\n")); + return_VALUE(-EINVAL); + } + pr->limit.user.tx = tx; + } + + result = acpi_processor_apply_limit(pr); + + return_VALUE(count); +} + + +struct file_operations acpi_processor_limit_fops = { + .open = acpi_processor_limit_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c new file mode 100644 index 00000000000..be9f569d39d --- /dev/null +++ b/drivers/acpi/processor_throttling.c @@ -0,0 +1,351 @@ +/* + * processor_throttling.c - Throttling submodule of the ACPI processor driver + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * Copyright (C) 2004 Dominik Brodowski <linux@brodo.de> + * Copyright (C) 2004 Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com> + * - Added processor hotplug support + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/cpufreq.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +#include <asm/io.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_bus.h> +#include <acpi/processor.h> + +#define ACPI_PROCESSOR_COMPONENT 0x01000000 +#define ACPI_PROCESSOR_CLASS "processor" +#define ACPI_PROCESSOR_DRIVER_NAME "ACPI Processor Driver" +#define _COMPONENT ACPI_PROCESSOR_COMPONENT +ACPI_MODULE_NAME ("acpi_processor") + + +/* -------------------------------------------------------------------------- + Throttling Control + -------------------------------------------------------------------------- */ + +static int +acpi_processor_get_throttling ( + struct acpi_processor *pr) +{ + int state = 0; + u32 value = 0; + u32 duty_mask = 0; + u32 duty_value = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_get_throttling"); + + if (!pr) + return_VALUE(-EINVAL); + + if (!pr->flags.throttling) + return_VALUE(-ENODEV); + + pr->throttling.state = 0; + + duty_mask = pr->throttling.state_count - 1; + + duty_mask <<= pr->throttling.duty_offset; + + local_irq_disable(); + + value = inl(pr->throttling.address); + + /* + * Compute the current throttling state when throttling is enabled + * (bit 4 is on). + */ + if (value & 0x10) { + duty_value = value & duty_mask; + duty_value >>= pr->throttling.duty_offset; + + if (duty_value) + state = pr->throttling.state_count-duty_value; + } + + pr->throttling.state = state; + + local_irq_enable(); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Throttling state is T%d (%d%% throttling applied)\n", + state, pr->throttling.states[state].performance)); + + return_VALUE(0); +} + + +int acpi_processor_set_throttling ( + struct acpi_processor *pr, + int state) +{ + u32 value = 0; + u32 duty_mask = 0; + u32 duty_value = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_set_throttling"); + + if (!pr) + return_VALUE(-EINVAL); + + if ((state < 0) || (state > (pr->throttling.state_count - 1))) + return_VALUE(-EINVAL); + + if (!pr->flags.throttling) + return_VALUE(-ENODEV); + + if (state == pr->throttling.state) + return_VALUE(0); + + /* + * Calculate the duty_value and duty_mask. + */ + if (state) { + duty_value = pr->throttling.state_count - state; + + duty_value <<= pr->throttling.duty_offset; + + /* Used to clear all duty_value bits */ + duty_mask = pr->throttling.state_count - 1; + + duty_mask <<= acpi_fadt.duty_offset; + duty_mask = ~duty_mask; + } + + local_irq_disable(); + + /* + * Disable throttling by writing a 0 to bit 4. Note that we must + * turn it off before you can change the duty_value. + */ + value = inl(pr->throttling.address); + if (value & 0x10) { + value &= 0xFFFFFFEF; + outl(value, pr->throttling.address); + } + + /* + * Write the new duty_value and then enable throttling. Note + * that a state value of 0 leaves throttling disabled. + */ + if (state) { + value &= duty_mask; + value |= duty_value; + outl(value, pr->throttling.address); + + value |= 0x00000010; + outl(value, pr->throttling.address); + } + + pr->throttling.state = state; + + local_irq_enable(); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Throttling state set to T%d (%d%%)\n", state, + (pr->throttling.states[state].performance?pr->throttling.states[state].performance/10:0))); + + return_VALUE(0); +} + + +int +acpi_processor_get_throttling_info ( + struct acpi_processor *pr) +{ + int result = 0; + int step = 0; + int i = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_get_throttling_info"); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "pblk_address[0x%08x] duty_offset[%d] duty_width[%d]\n", + pr->throttling.address, + pr->throttling.duty_offset, + pr->throttling.duty_width)); + + if (!pr) + return_VALUE(-EINVAL); + + /* TBD: Support ACPI 2.0 objects */ + + if (!pr->throttling.address) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling register\n")); + return_VALUE(0); + } + else if (!pr->throttling.duty_width) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No throttling states\n")); + return_VALUE(0); + } + /* TBD: Support duty_cycle values that span bit 4. */ + else if ((pr->throttling.duty_offset + + pr->throttling.duty_width) > 4) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "duty_cycle spans bit 4\n")); + return_VALUE(0); + } + + /* + * PIIX4 Errata: We don't support throttling on the original PIIX4. + * This shouldn't be an issue as few (if any) mobile systems ever + * used this part. + */ + if (errata.piix4.throttle) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Throttling not supported on PIIX4 A- or B-step\n")); + return_VALUE(0); + } + + pr->throttling.state_count = 1 << acpi_fadt.duty_width; + + /* + * Compute state values. Note that throttling displays a linear power/ + * performance relationship (at 50% performance the CPU will consume + * 50% power). Values are in 1/10th of a percent to preserve accuracy. + */ + + step = (1000 / pr->throttling.state_count); + + for (i=0; i<pr->throttling.state_count; i++) { + pr->throttling.states[i].performance = step * i; + pr->throttling.states[i].power = step * i; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d throttling states\n", + pr->throttling.state_count)); + + pr->flags.throttling = 1; + + /* + * Disable throttling (if enabled). We'll let subsequent policy (e.g. + * thermal) decide to lower performance if it so chooses, but for now + * we'll crank up the speed. + */ + + result = acpi_processor_get_throttling(pr); + if (result) + goto end; + + if (pr->throttling.state) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Disabling throttling (was T%d)\n", + pr->throttling.state)); + result = acpi_processor_set_throttling(pr, 0); + if (result) + goto end; + } + +end: + if (result) + pr->flags.throttling = 0; + + return_VALUE(result); +} + + +/* proc interface */ + +static int acpi_processor_throttling_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_processor *pr = (struct acpi_processor *)seq->private; + int i = 0; + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_processor_throttling_seq_show"); + + if (!pr) + goto end; + + if (!(pr->throttling.state_count > 0)) { + seq_puts(seq, "<not supported>\n"); + goto end; + } + + result = acpi_processor_get_throttling(pr); + + if (result) { + seq_puts(seq, "Could not determine current throttling state.\n"); + goto end; + } + + seq_printf(seq, "state count: %d\n" + "active state: T%d\n", + pr->throttling.state_count, + pr->throttling.state); + + seq_puts(seq, "states:\n"); + for (i = 0; i < pr->throttling.state_count; i++) + seq_printf(seq, " %cT%d: %02d%%\n", + (i == pr->throttling.state?'*':' '), i, + (pr->throttling.states[i].performance?pr->throttling.states[i].performance/10:0)); + +end: + return_VALUE(0); +} + +static int acpi_processor_throttling_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_processor_throttling_seq_show, + PDE(inode)->data); +} + +ssize_t acpi_processor_write_throttling ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *data) +{ + int result = 0; + struct seq_file *m = (struct seq_file *)file->private_data; + struct acpi_processor *pr = (struct acpi_processor *)m->private; + char state_string[12] = {'\0'}; + + ACPI_FUNCTION_TRACE("acpi_processor_write_throttling"); + + if (!pr || (count > sizeof(state_string) - 1)) + return_VALUE(-EINVAL); + + if (copy_from_user(state_string, buffer, count)) + return_VALUE(-EFAULT); + + state_string[count] = '\0'; + + result = acpi_processor_set_throttling(pr, + simple_strtoul(state_string, NULL, 0)); + if (result) + return_VALUE(result); + + return_VALUE(count); +} + +struct file_operations acpi_processor_throttling_fops = { + .open = acpi_processor_throttling_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; diff --git a/drivers/acpi/resources/Makefile b/drivers/acpi/resources/Makefile new file mode 100644 index 00000000000..2130b74170c --- /dev/null +++ b/drivers/acpi/resources/Makefile @@ -0,0 +1,10 @@ +# +# Makefile for all Linux ACPI interpreter subdirectories +# + +obj-y := rsaddr.o rscreate.o rsio.o rslist.o rsmisc.o rsxface.o \ + rscalc.o rsirq.o rsmemory.o rsutils.o + +obj-$(ACPI_FUTURE_USAGE) += rsdump.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/resources/rsaddr.c b/drivers/acpi/resources/rsaddr.c new file mode 100644 index 00000000000..4788c079735 --- /dev/null +++ b/drivers/acpi/resources/rsaddr.c @@ -0,0 +1,1225 @@ +/******************************************************************************* + * + * Module Name: rsaddr - Address resource descriptors (16/32/64) + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rsaddr") + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_address16_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_address16_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u8 *temp_ptr; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_address16); + u32 index; + u16 temp16; + u8 temp8; + + + ACPI_FUNCTION_TRACE ("rs_address16_resource"); + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + /* Validate minimum descriptor length */ + + if (temp16 < 13) { + return_ACPI_STATUS (AE_AML_BAD_RESOURCE_LENGTH); + } + + *bytes_consumed = temp16 + 3; + output_struct->id = ACPI_RSTYPE_ADDRESS16; + + /* + * Get the Resource Type (Byte3) + */ + buffer += 2; + temp8 = *buffer; + + /* Values 0-2 and 0xC0-0xFF are valid */ + + if ((temp8 > 2) && (temp8 < 0xC0)) { + return_ACPI_STATUS (AE_AML_INVALID_RESOURCE_TYPE); + } + + output_struct->data.address16.resource_type = temp8; + + /* + * Get the General Flags (Byte4) + */ + buffer += 1; + temp8 = *buffer; + + /* Producer / Consumer */ + + output_struct->data.address16.producer_consumer = temp8 & 0x01; + + /* Decode */ + + output_struct->data.address16.decode = (temp8 >> 1) & 0x01; + + /* Min Address Fixed */ + + output_struct->data.address16.min_address_fixed = (temp8 >> 2) & 0x01; + + /* Max Address Fixed */ + + output_struct->data.address16.max_address_fixed = (temp8 >> 3) & 0x01; + + /* + * Get the Type Specific Flags (Byte5) + */ + buffer += 1; + temp8 = *buffer; + + if (ACPI_MEMORY_RANGE == output_struct->data.address16.resource_type) { + output_struct->data.address16.attribute.memory.read_write_attribute = + (u16) (temp8 & 0x01); + output_struct->data.address16.attribute.memory.cache_attribute = + (u16) ((temp8 >> 1) & 0x03); + } + else { + if (ACPI_IO_RANGE == output_struct->data.address16.resource_type) { + output_struct->data.address16.attribute.io.range_attribute = + (u16) (temp8 & 0x03); + output_struct->data.address16.attribute.io.translation_attribute = + (u16) ((temp8 >> 4) & 0x03); + } + else { + /* BUS_NUMBER_RANGE == Address16.Data->resource_type */ + /* Nothing needs to be filled in */ + } + } + + /* + * Get Granularity (Bytes 6-7) + */ + buffer += 1; + ACPI_MOVE_16_TO_32 (&output_struct->data.address16.granularity, buffer); + + /* + * Get min_address_range (Bytes 8-9) + */ + buffer += 2; + ACPI_MOVE_16_TO_32 (&output_struct->data.address16.min_address_range, buffer); + + /* + * Get max_address_range (Bytes 10-11) + */ + buffer += 2; + ACPI_MOVE_16_TO_32 (&output_struct->data.address16.max_address_range, buffer); + + /* + * Get address_translation_offset (Bytes 12-13) + */ + buffer += 2; + ACPI_MOVE_16_TO_32 (&output_struct->data.address16.address_translation_offset, buffer); + + /* + * Get address_length (Bytes 14-15) + */ + buffer += 2; + ACPI_MOVE_16_TO_32 (&output_struct->data.address16.address_length, buffer); + + /* + * Resource Source Index (if present) + */ + buffer += 2; + + /* + * This will leave us pointing to the Resource Source Index + * If it is present, then save it off and calculate the + * pointer to where the null terminated string goes: + * Each Interrupt takes 32-bits + the 5 bytes of the + * stream that are default. + * + * Note: Some resource descriptors will have an additional null, so + * we add 1 to the length. + */ + if (*bytes_consumed > (16 + 1)) { + /* Dereference the Index */ + + temp8 = *buffer; + output_struct->data.address16.resource_source.index = (u32) temp8; + + /* Point to the String */ + + buffer += 1; + + /* Point the String pointer to the end of this structure */ + + output_struct->data.address16.resource_source.string_ptr = + (char *)((u8 * )output_struct + struct_size); + + temp_ptr = (u8 *) output_struct->data.address16.resource_source.string_ptr; + + /* Copy the string into the buffer */ + + index = 0; + + while (0x00 != *buffer) { + *temp_ptr = *buffer; + + temp_ptr += 1; + buffer += 1; + index += 1; + } + + /* + * Add the terminating null + */ + *temp_ptr = 0x00; + + output_struct->data.address16.resource_source.string_length = index + 1; + + /* + * In order for the struct_size to fall on a 32-bit boundary, + * calculate the length of the string and expand the + * struct_size to the next 32-bit boundary. + */ + temp8 = (u8) (index + 1); + struct_size += ACPI_ROUND_UP_to_32_bITS (temp8); + } + else { + output_struct->data.address16.resource_source.index = 0x00; + output_struct->data.address16.resource_source.string_length = 0; + output_struct->data.address16.resource_source.string_ptr = NULL; + } + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_address16_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_address16_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u8 *length_field; + u8 temp8; + char *temp_pointer = NULL; + acpi_size actual_bytes; + + + ACPI_FUNCTION_TRACE ("rs_address16_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x88; + buffer += 1; + + /* + * Save a pointer to the Length field - to be filled in later + */ + length_field = buffer; + buffer += 2; + + /* + * Set the Resource Type (Memory, Io, bus_number) + */ + temp8 = (u8) (linked_list->data.address16.resource_type & 0x03); + *buffer = temp8; + buffer += 1; + + /* + * Set the general flags + */ + temp8 = (u8) (linked_list->data.address16.producer_consumer & 0x01); + + temp8 |= (linked_list->data.address16.decode & 0x01) << 1; + temp8 |= (linked_list->data.address16.min_address_fixed & 0x01) << 2; + temp8 |= (linked_list->data.address16.max_address_fixed & 0x01) << 3; + + *buffer = temp8; + buffer += 1; + + /* + * Set the type specific flags + */ + temp8 = 0; + + if (ACPI_MEMORY_RANGE == linked_list->data.address16.resource_type) { + temp8 = (u8) + (linked_list->data.address16.attribute.memory.read_write_attribute & + 0x01); + + temp8 |= + (linked_list->data.address16.attribute.memory.cache_attribute & + 0x03) << 1; + } + else if (ACPI_IO_RANGE == linked_list->data.address16.resource_type) { + temp8 = (u8) + (linked_list->data.address16.attribute.io.range_attribute & + 0x03); + temp8 |= + (linked_list->data.address16.attribute.io.translation_attribute & + 0x03) << 4; + } + + *buffer = temp8; + buffer += 1; + + /* + * Set the address space granularity + */ + ACPI_MOVE_32_TO_16 (buffer, &linked_list->data.address16.granularity); + buffer += 2; + + /* + * Set the address range minimum + */ + ACPI_MOVE_32_TO_16 (buffer, &linked_list->data.address16.min_address_range); + buffer += 2; + + /* + * Set the address range maximum + */ + ACPI_MOVE_32_TO_16 (buffer, &linked_list->data.address16.max_address_range); + buffer += 2; + + /* + * Set the address translation offset + */ + ACPI_MOVE_32_TO_16 (buffer, &linked_list->data.address16.address_translation_offset); + buffer += 2; + + /* + * Set the address length + */ + ACPI_MOVE_32_TO_16 (buffer, &linked_list->data.address16.address_length); + buffer += 2; + + /* + * Resource Source Index and Resource Source are optional + */ + if (0 != linked_list->data.address16.resource_source.string_length) { + temp8 = (u8) linked_list->data.address16.resource_source.index; + + *buffer = temp8; + buffer += 1; + + temp_pointer = (char *) buffer; + + /* + * Copy the string + */ + ACPI_STRCPY (temp_pointer, + linked_list->data.address16.resource_source.string_ptr); + + /* + * Buffer needs to be set to the length of the sting + one for the + * terminating null + */ + buffer += (acpi_size)(ACPI_STRLEN (linked_list->data.address16.resource_source.string_ptr) + 1); + } + + /* + * Return the number of bytes consumed in this operation + */ + actual_bytes = ACPI_PTR_DIFF (buffer, *output_buffer); + *bytes_consumed = actual_bytes; + + /* + * Set the length field to the number of bytes consumed + * minus the header size (3 bytes) + */ + actual_bytes -= 3; + ACPI_MOVE_SIZE_TO_16 (length_field, &actual_bytes); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_address32_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_address32_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer; + struct acpi_resource *output_struct= (void *) *output_buffer; + u16 temp16; + u8 temp8; + u8 *temp_ptr; + acpi_size struct_size; + u32 index; + + + ACPI_FUNCTION_TRACE ("rs_address32_resource"); + + + buffer = byte_stream_buffer; + struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_address32); + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + /* Validate minimum descriptor length */ + + if (temp16 < 23) { + return_ACPI_STATUS (AE_AML_BAD_RESOURCE_LENGTH); + } + + *bytes_consumed = temp16 + 3; + output_struct->id = ACPI_RSTYPE_ADDRESS32; + + /* + * Get the Resource Type (Byte3) + */ + buffer += 2; + temp8 = *buffer; + + /* Values 0-2 and 0xC0-0xFF are valid */ + + if ((temp8 > 2) && (temp8 < 0xC0)) { + return_ACPI_STATUS (AE_AML_INVALID_RESOURCE_TYPE); + } + + output_struct->data.address32.resource_type = temp8; + + /* + * Get the General Flags (Byte4) + */ + buffer += 1; + temp8 = *buffer; + + /* + * Producer / Consumer + */ + output_struct->data.address32.producer_consumer = temp8 & 0x01; + + /* + * Decode + */ + output_struct->data.address32.decode = (temp8 >> 1) & 0x01; + + /* + * Min Address Fixed + */ + output_struct->data.address32.min_address_fixed = (temp8 >> 2) & 0x01; + + /* + * Max Address Fixed + */ + output_struct->data.address32.max_address_fixed = (temp8 >> 3) & 0x01; + + /* + * Get the Type Specific Flags (Byte5) + */ + buffer += 1; + temp8 = *buffer; + + if (ACPI_MEMORY_RANGE == output_struct->data.address32.resource_type) { + output_struct->data.address32.attribute.memory.read_write_attribute = + (u16) (temp8 & 0x01); + + output_struct->data.address32.attribute.memory.cache_attribute = + (u16) ((temp8 >> 1) & 0x03); + } + else { + if (ACPI_IO_RANGE == output_struct->data.address32.resource_type) { + output_struct->data.address32.attribute.io.range_attribute = + (u16) (temp8 & 0x03); + output_struct->data.address32.attribute.io.translation_attribute = + (u16) ((temp8 >> 4) & 0x03); + } + else { + /* BUS_NUMBER_RANGE == output_struct->Data.Address32.resource_type */ + /* Nothing needs to be filled in */ + } + } + + /* + * Get Granularity (Bytes 6-9) + */ + buffer += 1; + ACPI_MOVE_32_TO_32 (&output_struct->data.address32.granularity, buffer); + + /* + * Get min_address_range (Bytes 10-13) + */ + buffer += 4; + ACPI_MOVE_32_TO_32 (&output_struct->data.address32.min_address_range, buffer); + + /* + * Get max_address_range (Bytes 14-17) + */ + buffer += 4; + ACPI_MOVE_32_TO_32 (&output_struct->data.address32.max_address_range, buffer); + + /* + * Get address_translation_offset (Bytes 18-21) + */ + buffer += 4; + ACPI_MOVE_32_TO_32 (&output_struct->data.address32.address_translation_offset, buffer); + + /* + * Get address_length (Bytes 22-25) + */ + buffer += 4; + ACPI_MOVE_32_TO_32 (&output_struct->data.address32.address_length, buffer); + + /* + * Resource Source Index (if present) + */ + buffer += 4; + + /* + * This will leave us pointing to the Resource Source Index + * If it is present, then save it off and calculate the + * pointer to where the null terminated string goes: + * + * Note: Some resource descriptors will have an additional null, so + * we add 1 to the length. + */ + if (*bytes_consumed > (26 + 1)) { + /* Dereference the Index */ + + temp8 = *buffer; + output_struct->data.address32.resource_source.index = + (u32) temp8; + + /* Point to the String */ + + buffer += 1; + + /* Point the String pointer to the end of this structure */ + + output_struct->data.address32.resource_source.string_ptr = + (char *)((u8 *)output_struct + struct_size); + + temp_ptr = (u8 *) output_struct->data.address32.resource_source.string_ptr; + + /* Copy the string into the buffer */ + + index = 0; + while (0x00 != *buffer) { + *temp_ptr = *buffer; + + temp_ptr += 1; + buffer += 1; + index += 1; + } + + /* + * Add the terminating null + */ + *temp_ptr = 0x00; + output_struct->data.address32.resource_source.string_length = index + 1; + + /* + * In order for the struct_size to fall on a 32-bit boundary, + * calculate the length of the string and expand the + * struct_size to the next 32-bit boundary. + */ + temp8 = (u8) (index + 1); + struct_size += ACPI_ROUND_UP_to_32_bITS (temp8); + } + else { + output_struct->data.address32.resource_source.index = 0x00; + output_struct->data.address32.resource_source.string_length = 0; + output_struct->data.address32.resource_source.string_ptr = NULL; + } + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_address32_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_address32_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer; + u16 *length_field; + u8 temp8; + char *temp_pointer; + + + ACPI_FUNCTION_TRACE ("rs_address32_stream"); + + + buffer = *output_buffer; + + /* + * The descriptor field is static + */ + *buffer = 0x87; + buffer += 1; + + /* + * Set a pointer to the Length field - to be filled in later + */ + length_field = ACPI_CAST_PTR (u16, buffer); + buffer += 2; + + /* + * Set the Resource Type (Memory, Io, bus_number) + */ + temp8 = (u8) (linked_list->data.address32.resource_type & 0x03); + + *buffer = temp8; + buffer += 1; + + /* + * Set the general flags + */ + temp8 = (u8) (linked_list->data.address32.producer_consumer & 0x01); + temp8 |= (linked_list->data.address32.decode & 0x01) << 1; + temp8 |= (linked_list->data.address32.min_address_fixed & 0x01) << 2; + temp8 |= (linked_list->data.address32.max_address_fixed & 0x01) << 3; + + *buffer = temp8; + buffer += 1; + + /* + * Set the type specific flags + */ + temp8 = 0; + + if (ACPI_MEMORY_RANGE == linked_list->data.address32.resource_type) { + temp8 = (u8) + (linked_list->data.address32.attribute.memory.read_write_attribute & + 0x01); + + temp8 |= + (linked_list->data.address32.attribute.memory.cache_attribute & + 0x03) << 1; + } + else if (ACPI_IO_RANGE == linked_list->data.address32.resource_type) { + temp8 = (u8) + (linked_list->data.address32.attribute.io.range_attribute & + 0x03); + temp8 |= + (linked_list->data.address32.attribute.io.translation_attribute & + 0x03) << 4; + } + + *buffer = temp8; + buffer += 1; + + /* + * Set the address space granularity + */ + ACPI_MOVE_32_TO_32 (buffer, &linked_list->data.address32.granularity); + buffer += 4; + + /* + * Set the address range minimum + */ + ACPI_MOVE_32_TO_32 (buffer, &linked_list->data.address32.min_address_range); + buffer += 4; + + /* + * Set the address range maximum + */ + ACPI_MOVE_32_TO_32 (buffer, &linked_list->data.address32.max_address_range); + buffer += 4; + + /* + * Set the address translation offset + */ + ACPI_MOVE_32_TO_32 (buffer, &linked_list->data.address32.address_translation_offset); + buffer += 4; + + /* + * Set the address length + */ + ACPI_MOVE_32_TO_32 (buffer, &linked_list->data.address32.address_length); + buffer += 4; + + /* + * Resource Source Index and Resource Source are optional + */ + if (0 != linked_list->data.address32.resource_source.string_length) { + temp8 = (u8) linked_list->data.address32.resource_source.index; + + *buffer = temp8; + buffer += 1; + + temp_pointer = (char *) buffer; + + /* + * Copy the string + */ + ACPI_STRCPY (temp_pointer, + linked_list->data.address32.resource_source.string_ptr); + + /* + * Buffer needs to be set to the length of the sting + one for the + * terminating null + */ + buffer += (acpi_size)(ACPI_STRLEN (linked_list->data.address32.resource_source.string_ptr) + 1); + } + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + + /* + * Set the length field to the number of bytes consumed + * minus the header size (3 bytes) + */ + *length_field = (u16) (*bytes_consumed - 3); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_address64_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_address64_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u16 temp16; + u8 temp8; + u8 resource_type; + u8 *temp_ptr; + acpi_size struct_size; + u32 index; + + + ACPI_FUNCTION_TRACE ("rs_address64_resource"); + + + buffer = byte_stream_buffer; + struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_address64); + resource_type = *buffer; + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + /* Validate minimum descriptor length */ + + if (temp16 < 43) { + return_ACPI_STATUS (AE_AML_BAD_RESOURCE_LENGTH); + } + + *bytes_consumed = temp16 + 3; + output_struct->id = ACPI_RSTYPE_ADDRESS64; + + /* + * Get the Resource Type (Byte3) + */ + buffer += 2; + temp8 = *buffer; + + /* Values 0-2 and 0xC0-0xFF are valid */ + + if ((temp8 > 2) && (temp8 < 0xC0)) { + return_ACPI_STATUS (AE_AML_INVALID_RESOURCE_TYPE); + } + + output_struct->data.address64.resource_type = temp8; + + /* + * Get the General Flags (Byte4) + */ + buffer += 1; + temp8 = *buffer; + + /* + * Producer / Consumer + */ + output_struct->data.address64.producer_consumer = temp8 & 0x01; + + /* + * Decode + */ + output_struct->data.address64.decode = (temp8 >> 1) & 0x01; + + /* + * Min Address Fixed + */ + output_struct->data.address64.min_address_fixed = (temp8 >> 2) & 0x01; + + /* + * Max Address Fixed + */ + output_struct->data.address64.max_address_fixed = (temp8 >> 3) & 0x01; + + /* + * Get the Type Specific Flags (Byte5) + */ + buffer += 1; + temp8 = *buffer; + + if (ACPI_MEMORY_RANGE == output_struct->data.address64.resource_type) { + output_struct->data.address64.attribute.memory.read_write_attribute = + (u16) (temp8 & 0x01); + + output_struct->data.address64.attribute.memory.cache_attribute = + (u16) ((temp8 >> 1) & 0x03); + } + else { + if (ACPI_IO_RANGE == output_struct->data.address64.resource_type) { + output_struct->data.address64.attribute.io.range_attribute = + (u16) (temp8 & 0x03); + output_struct->data.address64.attribute.io.translation_attribute = + (u16) ((temp8 >> 4) & 0x03); + } + else { + /* BUS_NUMBER_RANGE == output_struct->Data.Address64.resource_type */ + /* Nothing needs to be filled in */ + } + } + + if (resource_type == ACPI_RDESC_TYPE_EXTENDED_ADDRESS_SPACE) { + /* Move past revision_id and Reserved byte */ + + buffer += 2; + } + + /* + * Get Granularity (Bytes 6-13) or (Bytes 8-15) + */ + buffer += 1; + ACPI_MOVE_64_TO_64 (&output_struct->data.address64.granularity, buffer); + + /* + * Get min_address_range (Bytes 14-21) or (Bytes 16-23) + */ + buffer += 8; + ACPI_MOVE_64_TO_64 (&output_struct->data.address64.min_address_range, buffer); + + /* + * Get max_address_range (Bytes 22-29) or (Bytes 24-31) + */ + buffer += 8; + ACPI_MOVE_64_TO_64 (&output_struct->data.address64.max_address_range, buffer); + + /* + * Get address_translation_offset (Bytes 30-37) or (Bytes 32-39) + */ + buffer += 8; + ACPI_MOVE_64_TO_64 (&output_struct->data.address64.address_translation_offset, buffer); + + /* + * Get address_length (Bytes 38-45) or (Bytes 40-47) + */ + buffer += 8; + ACPI_MOVE_64_TO_64 (&output_struct->data.address64.address_length, buffer); + + output_struct->data.address64.resource_source.index = 0x00; + output_struct->data.address64.resource_source.string_length = 0; + output_struct->data.address64.resource_source.string_ptr = NULL; + + if (resource_type == ACPI_RDESC_TYPE_EXTENDED_ADDRESS_SPACE) { + /* Get type_specific_attribute (Bytes 48-55) */ + + buffer += 8; + ACPI_MOVE_64_TO_64 (&output_struct->data.address64.type_specific_attributes, buffer); + } + else { + output_struct->data.address64.type_specific_attributes = 0; + + /* + * Resource Source Index (if present) + */ + buffer += 8; + + /* + * This will leave us pointing to the Resource Source Index + * If it is present, then save it off and calculate the + * pointer to where the null terminated string goes: + * Each Interrupt takes 32-bits + the 5 bytes of the + * stream that are default. + * + * Note: Some resource descriptors will have an additional null, so + * we add 1 to the length. + */ + if (*bytes_consumed > (46 + 1)) { + /* Dereference the Index */ + + temp8 = *buffer; + output_struct->data.address64.resource_source.index = + (u32) temp8; + + /* Point to the String */ + + buffer += 1; + + /* Point the String pointer to the end of this structure */ + + output_struct->data.address64.resource_source.string_ptr = + (char *)((u8 *)output_struct + struct_size); + + temp_ptr = (u8 *) output_struct->data.address64.resource_source.string_ptr; + + /* Copy the string into the buffer */ + + index = 0; + while (0x00 != *buffer) { + *temp_ptr = *buffer; + + temp_ptr += 1; + buffer += 1; + index += 1; + } + + /* + * Add the terminating null + */ + *temp_ptr = 0x00; + output_struct->data.address64.resource_source.string_length = index + 1; + + /* + * In order for the struct_size to fall on a 32-bit boundary, + * calculate the length of the string and expand the + * struct_size to the next 32-bit boundary. + */ + temp8 = (u8) (index + 1); + struct_size += ACPI_ROUND_UP_to_32_bITS (temp8); + } + } + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_address64_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_address64_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer; + u16 *length_field; + u8 temp8; + char *temp_pointer; + + + ACPI_FUNCTION_TRACE ("rs_address64_stream"); + + + buffer = *output_buffer; + + /* + * The descriptor field is static + */ + *buffer = 0x8A; + buffer += 1; + + /* + * Set a pointer to the Length field - to be filled in later + */ + length_field = ACPI_CAST_PTR (u16, buffer); + buffer += 2; + + /* + * Set the Resource Type (Memory, Io, bus_number) + */ + temp8 = (u8) (linked_list->data.address64.resource_type & 0x03); + + *buffer = temp8; + buffer += 1; + + /* + * Set the general flags + */ + temp8 = (u8) (linked_list->data.address64.producer_consumer & 0x01); + temp8 |= (linked_list->data.address64.decode & 0x01) << 1; + temp8 |= (linked_list->data.address64.min_address_fixed & 0x01) << 2; + temp8 |= (linked_list->data.address64.max_address_fixed & 0x01) << 3; + + *buffer = temp8; + buffer += 1; + + /* + * Set the type specific flags + */ + temp8 = 0; + + if (ACPI_MEMORY_RANGE == linked_list->data.address64.resource_type) { + temp8 = (u8) + (linked_list->data.address64.attribute.memory.read_write_attribute & + 0x01); + + temp8 |= + (linked_list->data.address64.attribute.memory.cache_attribute & + 0x03) << 1; + } + else if (ACPI_IO_RANGE == linked_list->data.address64.resource_type) { + temp8 = (u8) + (linked_list->data.address64.attribute.io.range_attribute & + 0x03); + temp8 |= + (linked_list->data.address64.attribute.io.range_attribute & + 0x03) << 4; + } + + *buffer = temp8; + buffer += 1; + + /* + * Set the address space granularity + */ + ACPI_MOVE_64_TO_64 (buffer, &linked_list->data.address64.granularity); + buffer += 8; + + /* + * Set the address range minimum + */ + ACPI_MOVE_64_TO_64 (buffer, &linked_list->data.address64.min_address_range); + buffer += 8; + + /* + * Set the address range maximum + */ + ACPI_MOVE_64_TO_64 (buffer, &linked_list->data.address64.max_address_range); + buffer += 8; + + /* + * Set the address translation offset + */ + ACPI_MOVE_64_TO_64 (buffer, &linked_list->data.address64.address_translation_offset); + buffer += 8; + + /* + * Set the address length + */ + ACPI_MOVE_64_TO_64 (buffer, &linked_list->data.address64.address_length); + buffer += 8; + + /* + * Resource Source Index and Resource Source are optional + */ + if (0 != linked_list->data.address64.resource_source.string_length) { + temp8 = (u8) linked_list->data.address64.resource_source.index; + + *buffer = temp8; + buffer += 1; + + temp_pointer = (char *) buffer; + + /* + * Copy the string + */ + ACPI_STRCPY (temp_pointer, linked_list->data.address64.resource_source.string_ptr); + + /* + * Buffer needs to be set to the length of the sting + one for the + * terminating null + */ + buffer += (acpi_size)(ACPI_STRLEN (linked_list->data.address64.resource_source.string_ptr) + 1); + } + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + + /* + * Set the length field to the number of bytes consumed + * minus the header size (3 bytes) + */ + *length_field = (u16) (*bytes_consumed - 3); + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/resources/rscalc.c b/drivers/acpi/resources/rscalc.c new file mode 100644 index 00000000000..8a5f0a52371 --- /dev/null +++ b/drivers/acpi/resources/rscalc.c @@ -0,0 +1,841 @@ +/******************************************************************************* + * + * Module Name: rscalc - Calculate stream and list lengths + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rscalc") + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_byte_stream_length + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * size_needed - u32 pointer of the size buffer needed + * to properly return the parsed data + * + * RETURN: Status + * + * DESCRIPTION: Takes the resource byte stream and parses it once, calculating + * the size buffer needed to hold the linked list that conveys + * the resource data. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_byte_stream_length ( + struct acpi_resource *linked_list, + acpi_size *size_needed) +{ + acpi_size byte_stream_size_needed = 0; + acpi_size segment_size; + u8 done = FALSE; + + + ACPI_FUNCTION_TRACE ("rs_get_byte_stream_length"); + + + while (!done) { + /* + * Init the variable that will hold the size to add to the total. + */ + segment_size = 0; + + switch (linked_list->id) { + case ACPI_RSTYPE_IRQ: + /* + * IRQ Resource + * For an IRQ Resource, Byte 3, although optional, will always be + * created - it holds IRQ information. + */ + segment_size = 4; + break; + + case ACPI_RSTYPE_DMA: + /* + * DMA Resource + * For this resource the size is static + */ + segment_size = 3; + break; + + case ACPI_RSTYPE_START_DPF: + /* + * Start Dependent Functions Resource + * For a start_dependent_functions Resource, Byte 1, although + * optional, will always be created. + */ + segment_size = 2; + break; + + case ACPI_RSTYPE_END_DPF: + /* + * End Dependent Functions Resource + * For this resource the size is static + */ + segment_size = 1; + break; + + case ACPI_RSTYPE_IO: + /* + * IO Port Resource + * For this resource the size is static + */ + segment_size = 8; + break; + + case ACPI_RSTYPE_FIXED_IO: + /* + * Fixed IO Port Resource + * For this resource the size is static + */ + segment_size = 4; + break; + + case ACPI_RSTYPE_VENDOR: + /* + * Vendor Defined Resource + * For a Vendor Specific resource, if the Length is between 1 and 7 + * it will be created as a Small Resource data type, otherwise it + * is a Large Resource data type. + */ + if (linked_list->data.vendor_specific.length > 7) { + segment_size = 3; + } + else { + segment_size = 1; + } + segment_size += linked_list->data.vendor_specific.length; + break; + + case ACPI_RSTYPE_END_TAG: + /* + * End Tag + * For this resource the size is static + */ + segment_size = 2; + done = TRUE; + break; + + case ACPI_RSTYPE_MEM24: + /* + * 24-Bit Memory Resource + * For this resource the size is static + */ + segment_size = 12; + break; + + case ACPI_RSTYPE_MEM32: + /* + * 32-Bit Memory Range Resource + * For this resource the size is static + */ + segment_size = 20; + break; + + case ACPI_RSTYPE_FIXED_MEM32: + /* + * 32-Bit Fixed Memory Resource + * For this resource the size is static + */ + segment_size = 12; + break; + + case ACPI_RSTYPE_ADDRESS16: + /* + * 16-Bit Address Resource + * The base size of this byte stream is 16. If a Resource Source + * string is not NULL, add 1 for the Index + the length of the null + * terminated string Resource Source + 1 for the null. + */ + segment_size = 16; + + if (linked_list->data.address16.resource_source.string_ptr) { + segment_size += linked_list->data.address16.resource_source.string_length; + segment_size++; + } + break; + + case ACPI_RSTYPE_ADDRESS32: + /* + * 32-Bit Address Resource + * The base size of this byte stream is 26. If a Resource + * Source string is not NULL, add 1 for the Index + the + * length of the null terminated string Resource Source + + * 1 for the null. + */ + segment_size = 26; + + if (linked_list->data.address32.resource_source.string_ptr) { + segment_size += linked_list->data.address32.resource_source.string_length; + segment_size++; + } + break; + + case ACPI_RSTYPE_ADDRESS64: + /* + * 64-Bit Address Resource + * The base size of this byte stream is 46. If a resource_source + * string is not NULL, add 1 for the Index + the length of the null + * terminated string Resource Source + 1 for the null. + */ + segment_size = 46; + + if (linked_list->data.address64.resource_source.string_ptr) { + segment_size += linked_list->data.address64.resource_source.string_length; + segment_size++; + } + break; + + case ACPI_RSTYPE_EXT_IRQ: + /* + * Extended IRQ Resource + * The base size of this byte stream is 9. This is for an Interrupt + * table length of 1. For each additional interrupt, add 4. + * If a Resource Source string is not NULL, add 1 for the + * Index + the length of the null terminated string + * Resource Source + 1 for the null. + */ + segment_size = 9 + + (((acpi_size) linked_list->data.extended_irq.number_of_interrupts - 1) * 4); + + if (linked_list->data.extended_irq.resource_source.string_ptr) { + segment_size += linked_list->data.extended_irq.resource_source.string_length; + segment_size++; + } + break; + + default: + /* + * If we get here, everything is out of sync, exit with error + */ + return_ACPI_STATUS (AE_AML_INVALID_RESOURCE_TYPE); + + } /* switch (linked_list->Id) */ + + /* + * Update the total + */ + byte_stream_size_needed += segment_size; + + /* + * Point to the next object + */ + linked_list = ACPI_PTR_ADD (struct acpi_resource, + linked_list, linked_list->length); + } + + /* + * This is the data the caller needs + */ + *size_needed = byte_stream_size_needed; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_list_length + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource byte stream + * byte_stream_buffer_length - Size of byte_stream_buffer + * size_needed - u32 pointer of the size buffer + * needed to properly return the + * parsed data + * + * RETURN: Status + * + * DESCRIPTION: Takes the resource byte stream and parses it once, calculating + * the size buffer needed to hold the linked list that conveys + * the resource data. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_list_length ( + u8 *byte_stream_buffer, + u32 byte_stream_buffer_length, + acpi_size *size_needed) +{ + u32 buffer_size = 0; + u32 bytes_parsed = 0; + u8 number_of_interrupts = 0; + u8 number_of_channels = 0; + u8 resource_type; + u32 structure_size; + u32 bytes_consumed; + u8 *buffer; + u8 temp8; + u16 temp16; + u8 index; + u8 additional_bytes; + + + ACPI_FUNCTION_TRACE ("rs_get_list_length"); + + + while (bytes_parsed < byte_stream_buffer_length) { + /* + * The next byte in the stream is the resource type + */ + resource_type = acpi_rs_get_resource_type (*byte_stream_buffer); + + switch (resource_type) { + case ACPI_RDESC_TYPE_MEMORY_24: + /* + * 24-Bit Memory Resource + */ + bytes_consumed = 12; + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_mem24); + break; + + + case ACPI_RDESC_TYPE_LARGE_VENDOR: + /* + * Vendor Defined Resource + */ + buffer = byte_stream_buffer; + ++buffer; + + ACPI_MOVE_16_TO_16 (&temp16, buffer); + bytes_consumed = temp16 + 3; + + /* + * Ensure a 32-bit boundary for the structure + */ + temp16 = (u16) ACPI_ROUND_UP_to_32_bITS (temp16); + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_vendor) + + (temp16 * sizeof (u8)); + break; + + + case ACPI_RDESC_TYPE_MEMORY_32: + /* + * 32-Bit Memory Range Resource + */ + bytes_consumed = 20; + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_mem32); + break; + + + case ACPI_RDESC_TYPE_FIXED_MEMORY_32: + /* + * 32-Bit Fixed Memory Resource + */ + bytes_consumed = 12; + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_fixed_mem32); + break; + + + case ACPI_RDESC_TYPE_EXTENDED_ADDRESS_SPACE: + /* + * 64-Bit Address Resource + */ + buffer = byte_stream_buffer; + + ++buffer; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + bytes_consumed = temp16 + 3; + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_address64); + break; + + + case ACPI_RDESC_TYPE_QWORD_ADDRESS_SPACE: + /* + * 64-Bit Address Resource + */ + buffer = byte_stream_buffer; + + ++buffer; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + bytes_consumed = temp16 + 3; + + /* + * Resource Source Index and Resource Source are optional elements. + * Check the length of the Bytestream. If it is greater than 43, + * that means that an Index exists and is followed by a null + * terminated string. Therefore, set the temp variable to the + * length minus the minimum byte stream length plus the byte for + * the Index to determine the size of the NULL terminated string. + */ + if (43 < temp16) { + temp8 = (u8) (temp16 - 44); + } + else { + temp8 = 0; + } + + /* + * Ensure a 64-bit boundary for the structure + */ + temp8 = (u8) ACPI_ROUND_UP_to_64_bITS (temp8); + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_address64) + + (temp8 * sizeof (u8)); + break; + + + case ACPI_RDESC_TYPE_DWORD_ADDRESS_SPACE: + /* + * 32-Bit Address Resource + */ + buffer = byte_stream_buffer; + + ++buffer; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + bytes_consumed = temp16 + 3; + + /* + * Resource Source Index and Resource Source are optional elements. + * Check the length of the Bytestream. If it is greater than 23, + * that means that an Index exists and is followed by a null + * terminated string. Therefore, set the temp variable to the + * length minus the minimum byte stream length plus the byte for + * the Index to determine the size of the NULL terminated string. + */ + if (23 < temp16) { + temp8 = (u8) (temp16 - 24); + } + else { + temp8 = 0; + } + + /* + * Ensure a 32-bit boundary for the structure + */ + temp8 = (u8) ACPI_ROUND_UP_to_32_bITS (temp8); + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_address32) + + (temp8 * sizeof (u8)); + break; + + + case ACPI_RDESC_TYPE_WORD_ADDRESS_SPACE: + /* + * 16-Bit Address Resource + */ + buffer = byte_stream_buffer; + + ++buffer; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + bytes_consumed = temp16 + 3; + + /* + * Resource Source Index and Resource Source are optional elements. + * Check the length of the Bytestream. If it is greater than 13, + * that means that an Index exists and is followed by a null + * terminated string. Therefore, set the temp variable to the + * length minus the minimum byte stream length plus the byte for + * the Index to determine the size of the NULL terminated string. + */ + if (13 < temp16) { + temp8 = (u8) (temp16 - 14); + } + else { + temp8 = 0; + } + + /* + * Ensure a 32-bit boundary for the structure + */ + temp8 = (u8) ACPI_ROUND_UP_to_32_bITS (temp8); + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_address16) + + (temp8 * sizeof (u8)); + break; + + + case ACPI_RDESC_TYPE_EXTENDED_XRUPT: + /* + * Extended IRQ + */ + buffer = byte_stream_buffer; + + ++buffer; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + bytes_consumed = temp16 + 3; + + /* + * Point past the length field and the Interrupt vector flags to + * save off the Interrupt table length to the Temp8 variable. + */ + buffer += 3; + temp8 = *buffer; + + /* + * To compensate for multiple interrupt numbers, add 4 bytes for + * each additional interrupts greater than 1 + */ + additional_bytes = (u8) ((temp8 - 1) * 4); + + /* + * Resource Source Index and Resource Source are optional elements. + * Check the length of the Bytestream. If it is greater than 9, + * that means that an Index exists and is followed by a null + * terminated string. Therefore, set the temp variable to the + * length minus the minimum byte stream length plus the byte for + * the Index to determine the size of the NULL terminated string. + */ + if (9 + additional_bytes < temp16) { + temp8 = (u8) (temp16 - (9 + additional_bytes)); + } + else { + temp8 = 0; + } + + /* + * Ensure a 32-bit boundary for the structure + */ + temp8 = (u8) ACPI_ROUND_UP_to_32_bITS (temp8); + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_ext_irq) + + (additional_bytes * sizeof (u8)) + + (temp8 * sizeof (u8)); + break; + + + case ACPI_RDESC_TYPE_IRQ_FORMAT: + /* + * IRQ Resource. + * Determine if it there are two or three trailing bytes + */ + buffer = byte_stream_buffer; + temp8 = *buffer; + + if(temp8 & 0x01) { + bytes_consumed = 4; + } + else { + bytes_consumed = 3; + } + + /* Point past the descriptor */ + + ++buffer; + + /* + * Look at the number of bits set + */ + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + for (index = 0; index < 16; index++) { + if (temp16 & 0x1) { + ++number_of_interrupts; + } + + temp16 >>= 1; + } + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_io) + + (number_of_interrupts * sizeof (u32)); + break; + + + case ACPI_RDESC_TYPE_DMA_FORMAT: + /* + * DMA Resource + */ + buffer = byte_stream_buffer; + bytes_consumed = 3; + + /* Point past the descriptor */ + + ++buffer; + + /* + * Look at the number of bits set + */ + temp8 = *buffer; + + for(index = 0; index < 8; index++) { + if(temp8 & 0x1) { + ++number_of_channels; + } + + temp8 >>= 1; + } + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_dma) + + (number_of_channels * sizeof (u32)); + break; + + + case ACPI_RDESC_TYPE_START_DEPENDENT: + /* + * Start Dependent Functions Resource + * Determine if it there are two or three trailing bytes + */ + buffer = byte_stream_buffer; + temp8 = *buffer; + + if(temp8 & 0x01) { + bytes_consumed = 2; + } + else { + bytes_consumed = 1; + } + + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_start_dpf); + break; + + + case ACPI_RDESC_TYPE_END_DEPENDENT: + /* + * End Dependent Functions Resource + */ + bytes_consumed = 1; + structure_size = ACPI_RESOURCE_LENGTH; + break; + + + case ACPI_RDESC_TYPE_IO_PORT: + /* + * IO Port Resource + */ + bytes_consumed = 8; + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_io); + break; + + + case ACPI_RDESC_TYPE_FIXED_IO_PORT: + /* + * Fixed IO Port Resource + */ + bytes_consumed = 4; + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_fixed_io); + break; + + + case ACPI_RDESC_TYPE_SMALL_VENDOR: + /* + * Vendor Specific Resource + */ + buffer = byte_stream_buffer; + + temp8 = *buffer; + temp8 = (u8) (temp8 & 0x7); + bytes_consumed = temp8 + 1; + + /* + * Ensure a 32-bit boundary for the structure + */ + temp8 = (u8) ACPI_ROUND_UP_to_32_bITS (temp8); + structure_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_vendor) + + (temp8 * sizeof (u8)); + break; + + + case ACPI_RDESC_TYPE_END_TAG: + /* + * End Tag + */ + bytes_consumed = 2; + structure_size = ACPI_RESOURCE_LENGTH; + byte_stream_buffer_length = bytes_parsed; + break; + + + default: + /* + * If we get here, everything is out of sync, + * exit with an error + */ + return_ACPI_STATUS (AE_AML_INVALID_RESOURCE_TYPE); + } + + /* + * Update the return value and counter + */ + buffer_size += (u32) ACPI_ALIGN_RESOURCE_SIZE (structure_size); + bytes_parsed += bytes_consumed; + + /* + * Set the byte stream to point to the next resource + */ + byte_stream_buffer += bytes_consumed; + } + + /* + * This is the data the caller needs + */ + *size_needed = buffer_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_pci_routing_table_length + * + * PARAMETERS: package_object - Pointer to the package object + * buffer_size_needed - u32 pointer of the size buffer + * needed to properly return the + * parsed data + * + * RETURN: Status + * + * DESCRIPTION: Given a package representing a PCI routing table, this + * calculates the size of the corresponding linked list of + * descriptions. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_pci_routing_table_length ( + union acpi_operand_object *package_object, + acpi_size *buffer_size_needed) +{ + u32 number_of_elements; + acpi_size temp_size_needed = 0; + union acpi_operand_object **top_object_list; + u32 index; + union acpi_operand_object *package_element; + union acpi_operand_object **sub_object_list; + u8 name_found; + u32 table_index; + + + ACPI_FUNCTION_TRACE ("rs_get_pci_routing_table_length"); + + + number_of_elements = package_object->package.count; + + /* + * Calculate the size of the return buffer. + * The base size is the number of elements * the sizes of the + * structures. Additional space for the strings is added below. + * The minus one is to subtract the size of the u8 Source[1] + * member because it is added below. + * + * But each PRT_ENTRY structure has a pointer to a string and + * the size of that string must be found. + */ + top_object_list = package_object->package.elements; + + for (index = 0; index < number_of_elements; index++) { + /* + * Dereference the sub-package + */ + package_element = *top_object_list; + + /* + * The sub_object_list will now point to an array of the + * four IRQ elements: Address, Pin, Source and source_index + */ + sub_object_list = package_element->package.elements; + + /* + * Scan the irq_table_elements for the Source Name String + */ + name_found = FALSE; + + for (table_index = 0; table_index < 4 && !name_found; table_index++) { + if ((ACPI_TYPE_STRING == ACPI_GET_OBJECT_TYPE (*sub_object_list)) || + ((ACPI_TYPE_LOCAL_REFERENCE == ACPI_GET_OBJECT_TYPE (*sub_object_list)) && + ((*sub_object_list)->reference.opcode == AML_INT_NAMEPATH_OP))) { + name_found = TRUE; + } + else { + /* + * Look at the next element + */ + sub_object_list++; + } + } + + temp_size_needed += (sizeof (struct acpi_pci_routing_table) - 4); + + /* + * Was a String type found? + */ + if (name_found) { + if (ACPI_GET_OBJECT_TYPE (*sub_object_list) == ACPI_TYPE_STRING) { + /* + * The length String.Length field does not include the + * terminating NULL, add 1 + */ + temp_size_needed += ((acpi_size) (*sub_object_list)->string.length + 1); + } + else { + temp_size_needed += acpi_ns_get_pathname_length ( + (*sub_object_list)->reference.node); + } + } + else { + /* + * If no name was found, then this is a NULL, which is + * translated as a u32 zero. + */ + temp_size_needed += sizeof (u32); + } + + /* Round up the size since each element must be aligned */ + + temp_size_needed = ACPI_ROUND_UP_to_64_bITS (temp_size_needed); + + /* + * Point to the next union acpi_operand_object + */ + top_object_list++; + } + + /* + * Adding an extra element to the end of the list, essentially a NULL terminator + */ + *buffer_size_needed = temp_size_needed + sizeof (struct acpi_pci_routing_table); + return_ACPI_STATUS (AE_OK); +} diff --git a/drivers/acpi/resources/rscreate.c b/drivers/acpi/resources/rscreate.c new file mode 100644 index 00000000000..a3a0cbfda68 --- /dev/null +++ b/drivers/acpi/resources/rscreate.c @@ -0,0 +1,428 @@ +/******************************************************************************* + * + * Module Name: rscreate - Create resource lists/tables + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> +#include <acpi/amlcode.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rscreate") + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_create_resource_list + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource byte stream + * output_buffer - Pointer to the user's buffer + * + * RETURN: Status - AE_OK if okay, else a valid acpi_status code + * If output_buffer is not large enough, output_buffer_length + * indicates how large output_buffer should be, else it + * indicates how may u8 elements of output_buffer are valid. + * + * DESCRIPTION: Takes the byte stream returned from a _CRS, _PRS control method + * execution and parses the stream to create a linked list + * of device resources. + * + ******************************************************************************/ + +acpi_status +acpi_rs_create_resource_list ( + union acpi_operand_object *byte_stream_buffer, + struct acpi_buffer *output_buffer) +{ + + acpi_status status; + u8 *byte_stream_start; + acpi_size list_size_needed = 0; + u32 byte_stream_buffer_length; + + + ACPI_FUNCTION_TRACE ("rs_create_resource_list"); + + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "byte_stream_buffer = %p\n", + byte_stream_buffer)); + + /* + * Params already validated, so we don't re-validate here + */ + byte_stream_buffer_length = byte_stream_buffer->buffer.length; + byte_stream_start = byte_stream_buffer->buffer.pointer; + + /* + * Pass the byte_stream_buffer into a module that can calculate + * the buffer size needed for the linked list + */ + status = acpi_rs_get_list_length (byte_stream_start, byte_stream_buffer_length, + &list_size_needed); + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Status=%X list_size_needed=%X\n", + status, (u32) list_size_needed)); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer (output_buffer, list_size_needed); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Do the conversion */ + + status = acpi_rs_byte_stream_to_list (byte_stream_start, byte_stream_buffer_length, + output_buffer->pointer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "output_buffer %p Length %X\n", + output_buffer->pointer, (u32) output_buffer->length)); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_create_pci_routing_table + * + * PARAMETERS: package_object - Pointer to an union acpi_operand_object + * package + * output_buffer - Pointer to the user's buffer + * + * RETURN: Status AE_OK if okay, else a valid acpi_status code. + * If the output_buffer is too small, the error will be + * AE_BUFFER_OVERFLOW and output_buffer->Length will point + * to the size buffer needed. + * + * DESCRIPTION: Takes the union acpi_operand_object package and creates a + * linked list of PCI interrupt descriptions + * + * NOTE: It is the caller's responsibility to ensure that the start of the + * output buffer is aligned properly (if necessary). + * + ******************************************************************************/ + +acpi_status +acpi_rs_create_pci_routing_table ( + union acpi_operand_object *package_object, + struct acpi_buffer *output_buffer) +{ + u8 *buffer; + union acpi_operand_object **top_object_list; + union acpi_operand_object **sub_object_list; + union acpi_operand_object *obj_desc; + acpi_size buffer_size_needed = 0; + u32 number_of_elements; + u32 index; + struct acpi_pci_routing_table *user_prt; + struct acpi_namespace_node *node; + acpi_status status; + struct acpi_buffer path_buffer; + + + ACPI_FUNCTION_TRACE ("rs_create_pci_routing_table"); + + + /* Params already validated, so we don't re-validate here */ + + /* + * Get the required buffer length + */ + status = acpi_rs_get_pci_routing_table_length (package_object, + &buffer_size_needed); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "buffer_size_needed = %X\n", + (u32) buffer_size_needed)); + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer (output_buffer, buffer_size_needed); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Loop through the ACPI_INTERNAL_OBJECTS - Each object + * should be a package that in turn contains an + * acpi_integer Address, a u8 Pin, a Name and a u8 source_index. + */ + top_object_list = package_object->package.elements; + number_of_elements = package_object->package.count; + buffer = output_buffer->pointer; + user_prt = ACPI_CAST_PTR (struct acpi_pci_routing_table, buffer); + + for (index = 0; index < number_of_elements; index++) { + /* + * Point user_prt past this current structure + * + * NOTE: On the first iteration, user_prt->Length will + * be zero because we cleared the return buffer earlier + */ + buffer += user_prt->length; + user_prt = ACPI_CAST_PTR (struct acpi_pci_routing_table, buffer); + + /* + * Fill in the Length field with the information we have at this point. + * The minus four is to subtract the size of the u8 Source[4] member + * because it is added below. + */ + user_prt->length = (sizeof (struct acpi_pci_routing_table) - 4); + + /* + * Each element of the top-level package must also be a package + */ + if (ACPI_GET_OBJECT_TYPE (*top_object_list) != ACPI_TYPE_PACKAGE) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "(PRT[%X]) Need sub-package, found %s\n", + index, acpi_ut_get_object_type_name (*top_object_list))); + return_ACPI_STATUS (AE_AML_OPERAND_TYPE); + } + + /* Each sub-package must be of length 4 */ + + if ((*top_object_list)->package.count != 4) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "(PRT[%X]) Need package of length 4, found length %d\n", + index, (*top_object_list)->package.count)); + return_ACPI_STATUS (AE_AML_PACKAGE_LIMIT); + } + + /* + * Dereference the sub-package. + * The sub_object_list will now point to an array of the four IRQ + * elements: [Address, Pin, Source, source_index] + */ + sub_object_list = (*top_object_list)->package.elements; + + /* + * 1) First subobject: Dereference the PRT.Address + */ + obj_desc = sub_object_list[0]; + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_INTEGER) { + user_prt->address = obj_desc->integer.value; + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "(PRT[%X].Address) Need Integer, found %s\n", + index, acpi_ut_get_object_type_name (obj_desc))); + return_ACPI_STATUS (AE_BAD_DATA); + } + + /* + * 2) Second subobject: Dereference the PRT.Pin + */ + obj_desc = sub_object_list[1]; + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_INTEGER) { + user_prt->pin = (u32) obj_desc->integer.value; + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "(PRT[%X].Pin) Need Integer, found %s\n", + index, acpi_ut_get_object_type_name (obj_desc))); + return_ACPI_STATUS (AE_BAD_DATA); + } + + /* + * 3) Third subobject: Dereference the PRT.source_name + */ + obj_desc = sub_object_list[2]; + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_LOCAL_REFERENCE: + + if (obj_desc->reference.opcode != AML_INT_NAMEPATH_OP) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "(PRT[%X].Source) Need name, found reference op %X\n", + index, obj_desc->reference.opcode)); + return_ACPI_STATUS (AE_BAD_DATA); + } + + node = obj_desc->reference.node; + + /* Use *remaining* length of the buffer as max for pathname */ + + path_buffer.length = output_buffer->length - + (u32) ((u8 *) user_prt->source - + (u8 *) output_buffer->pointer); + path_buffer.pointer = user_prt->source; + + status = acpi_ns_handle_to_pathname ((acpi_handle) node, &path_buffer); + + user_prt->length += (u32) ACPI_STRLEN (user_prt->source) + 1; /* include null terminator */ + break; + + + case ACPI_TYPE_STRING: + + ACPI_STRCPY (user_prt->source, obj_desc->string.pointer); + + /* Add to the Length field the length of the string (add 1 for terminator) */ + + user_prt->length += obj_desc->string.length + 1; + break; + + + case ACPI_TYPE_INTEGER: + /* + * If this is a number, then the Source Name is NULL, since the + * entire buffer was zeroed out, we can leave this alone. + * + * Add to the Length field the length of the u32 NULL + */ + user_prt->length += sizeof (u32); + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "(PRT[%X].Source) Need Ref/String/Integer, found %s\n", + index, acpi_ut_get_object_type_name (obj_desc))); + return_ACPI_STATUS (AE_BAD_DATA); + } + + /* Now align the current length */ + + user_prt->length = (u32) ACPI_ROUND_UP_to_64_bITS (user_prt->length); + + /* + * 4) Fourth subobject: Dereference the PRT.source_index + */ + obj_desc = sub_object_list[3]; + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_INTEGER) { + user_prt->source_index = (u32) obj_desc->integer.value; + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "(PRT[%X].source_index) Need Integer, found %s\n", + index, acpi_ut_get_object_type_name (obj_desc))); + return_ACPI_STATUS (AE_BAD_DATA); + } + + /* Point to the next union acpi_operand_object in the top level package */ + + top_object_list++; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "output_buffer %p Length %X\n", + output_buffer->pointer, (u32) output_buffer->length)); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_create_byte_stream + * + * PARAMETERS: linked_list_buffer - Pointer to the resource linked list + * output_buffer - Pointer to the user's buffer + * + * RETURN: Status AE_OK if okay, else a valid acpi_status code. + * If the output_buffer is too small, the error will be + * AE_BUFFER_OVERFLOW and output_buffer->Length will point + * to the size buffer needed. + * + * DESCRIPTION: Takes the linked list of device resources and + * creates a bytestream to be used as input for the + * _SRS control method. + * + ******************************************************************************/ + +acpi_status +acpi_rs_create_byte_stream ( + struct acpi_resource *linked_list_buffer, + struct acpi_buffer *output_buffer) +{ + acpi_status status; + acpi_size byte_stream_size_needed = 0; + + + ACPI_FUNCTION_TRACE ("rs_create_byte_stream"); + + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "linked_list_buffer = %p\n", + linked_list_buffer)); + + /* + * Params already validated, so we don't re-validate here + * + * Pass the linked_list_buffer into a module that calculates + * the buffer size needed for the byte stream. + */ + status = acpi_rs_get_byte_stream_length (linked_list_buffer, + &byte_stream_size_needed); + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "byte_stream_size_needed=%X, %s\n", + (u32) byte_stream_size_needed, acpi_format_exception (status))); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer (output_buffer, byte_stream_size_needed); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Do the conversion */ + + status = acpi_rs_list_to_byte_stream (linked_list_buffer, byte_stream_size_needed, + output_buffer->pointer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "output_buffer %p Length %X\n", + output_buffer->pointer, (u32) output_buffer->length)); + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/resources/rsdump.c b/drivers/acpi/resources/rsdump.c new file mode 100644 index 00000000000..eef1b1f2c68 --- /dev/null +++ b/drivers/acpi/resources/rsdump.c @@ -0,0 +1,1150 @@ +/******************************************************************************* + * + * Module Name: rsdump - Functions to display the resource structures. + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rsdump") + + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_irq + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_irq ( + union acpi_resource_data *data) +{ + struct acpi_resource_irq *irq_data = (struct acpi_resource_irq *) data; + u8 index = 0; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("IRQ Resource\n"); + + acpi_os_printf (" %s Triggered\n", + ACPI_LEVEL_SENSITIVE == irq_data->edge_level ? "Level" : "Edge"); + + acpi_os_printf (" Active %s\n", + ACPI_ACTIVE_LOW == irq_data->active_high_low ? "Low" : "High"); + + acpi_os_printf (" %s\n", + ACPI_SHARED == irq_data->shared_exclusive ? "Shared" : "Exclusive"); + + acpi_os_printf (" %X Interrupts ( ", irq_data->number_of_interrupts); + + for (index = 0; index < irq_data->number_of_interrupts; index++) { + acpi_os_printf ("%X ", irq_data->interrupts[index]); + } + + acpi_os_printf (")\n"); + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_dma + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_dma ( + union acpi_resource_data *data) +{ + struct acpi_resource_dma *dma_data = (struct acpi_resource_dma *) data; + u8 index = 0; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("DMA Resource\n"); + + switch (dma_data->type) { + case ACPI_COMPATIBILITY: + acpi_os_printf (" Compatibility mode\n"); + break; + + case ACPI_TYPE_A: + acpi_os_printf (" Type A\n"); + break; + + case ACPI_TYPE_B: + acpi_os_printf (" Type B\n"); + break; + + case ACPI_TYPE_F: + acpi_os_printf (" Type F\n"); + break; + + default: + acpi_os_printf (" Invalid DMA type\n"); + break; + } + + acpi_os_printf (" %sBus Master\n", + ACPI_BUS_MASTER == dma_data->bus_master ? "" : "Not a "); + + + switch (dma_data->transfer) { + case ACPI_TRANSFER_8: + acpi_os_printf (" 8-bit only transfer\n"); + break; + + case ACPI_TRANSFER_8_16: + acpi_os_printf (" 8 and 16-bit transfer\n"); + break; + + case ACPI_TRANSFER_16: + acpi_os_printf (" 16 bit only transfer\n"); + break; + + default: + acpi_os_printf (" Invalid transfer preference\n"); + break; + } + + acpi_os_printf (" Number of Channels: %X ( ", dma_data->number_of_channels); + + for (index = 0; index < dma_data->number_of_channels; index++) { + acpi_os_printf ("%X ", dma_data->channels[index]); + } + + acpi_os_printf (")\n"); + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_start_depend_fns + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_start_depend_fns ( + union acpi_resource_data *data) +{ + struct acpi_resource_start_dpf *sdf_data = (struct acpi_resource_start_dpf *) data; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("Start Dependent Functions Resource\n"); + + switch (sdf_data->compatibility_priority) { + case ACPI_GOOD_CONFIGURATION: + acpi_os_printf (" Good configuration\n"); + break; + + case ACPI_ACCEPTABLE_CONFIGURATION: + acpi_os_printf (" Acceptable configuration\n"); + break; + + case ACPI_SUB_OPTIMAL_CONFIGURATION: + acpi_os_printf (" Sub-optimal configuration\n"); + break; + + default: + acpi_os_printf (" Invalid compatibility priority\n"); + break; + } + + switch(sdf_data->performance_robustness) { + case ACPI_GOOD_CONFIGURATION: + acpi_os_printf (" Good configuration\n"); + break; + + case ACPI_ACCEPTABLE_CONFIGURATION: + acpi_os_printf (" Acceptable configuration\n"); + break; + + case ACPI_SUB_OPTIMAL_CONFIGURATION: + acpi_os_printf (" Sub-optimal configuration\n"); + break; + + default: + acpi_os_printf (" Invalid performance " + "robustness preference\n"); + break; + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_io + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_io ( + union acpi_resource_data *data) +{ + struct acpi_resource_io *io_data = (struct acpi_resource_io *) data; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("Io Resource\n"); + + acpi_os_printf (" %d bit decode\n", + ACPI_DECODE_16 == io_data->io_decode ? 16 : 10); + + acpi_os_printf (" Range minimum base: %08X\n", + io_data->min_base_address); + + acpi_os_printf (" Range maximum base: %08X\n", + io_data->max_base_address); + + acpi_os_printf (" Alignment: %08X\n", + io_data->alignment); + + acpi_os_printf (" Range Length: %08X\n", + io_data->range_length); + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_fixed_io + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_fixed_io ( + union acpi_resource_data *data) +{ + struct acpi_resource_fixed_io *fixed_io_data = (struct acpi_resource_fixed_io *) data; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("Fixed Io Resource\n"); + acpi_os_printf (" Range base address: %08X", + fixed_io_data->base_address); + + acpi_os_printf (" Range length: %08X", + fixed_io_data->range_length); + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_vendor_specific + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_vendor_specific ( + union acpi_resource_data *data) +{ + struct acpi_resource_vendor *vendor_data = (struct acpi_resource_vendor *) data; + u16 index = 0; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("Vendor Specific Resource\n"); + + acpi_os_printf (" Length: %08X\n", vendor_data->length); + + for (index = 0; index < vendor_data->length; index++) { + acpi_os_printf (" Byte %X: %08X\n", + index, vendor_data->reserved[index]); + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_memory24 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_memory24 ( + union acpi_resource_data *data) +{ + struct acpi_resource_mem24 *memory24_data = (struct acpi_resource_mem24 *) data; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("24-Bit Memory Range Resource\n"); + + acpi_os_printf (" Read%s\n", + ACPI_READ_WRITE_MEMORY == + memory24_data->read_write_attribute ? + "/Write" : " only"); + + acpi_os_printf (" Range minimum base: %08X\n", + memory24_data->min_base_address); + + acpi_os_printf (" Range maximum base: %08X\n", + memory24_data->max_base_address); + + acpi_os_printf (" Alignment: %08X\n", + memory24_data->alignment); + + acpi_os_printf (" Range length: %08X\n", + memory24_data->range_length); + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_memory32 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_memory32 ( + union acpi_resource_data *data) +{ + struct acpi_resource_mem32 *memory32_data = (struct acpi_resource_mem32 *) data; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("32-Bit Memory Range Resource\n"); + + acpi_os_printf (" Read%s\n", + ACPI_READ_WRITE_MEMORY == + memory32_data->read_write_attribute ? + "/Write" : " only"); + + acpi_os_printf (" Range minimum base: %08X\n", + memory32_data->min_base_address); + + acpi_os_printf (" Range maximum base: %08X\n", + memory32_data->max_base_address); + + acpi_os_printf (" Alignment: %08X\n", + memory32_data->alignment); + + acpi_os_printf (" Range length: %08X\n", + memory32_data->range_length); + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_fixed_memory32 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_fixed_memory32 ( + union acpi_resource_data *data) +{ + struct acpi_resource_fixed_mem32 *fixed_memory32_data = (struct acpi_resource_fixed_mem32 *) data; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("32-Bit Fixed Location Memory Range Resource\n"); + + acpi_os_printf (" Read%s\n", + ACPI_READ_WRITE_MEMORY == + fixed_memory32_data->read_write_attribute ? + "/Write" : " Only"); + + acpi_os_printf (" Range base address: %08X\n", + fixed_memory32_data->range_base_address); + + acpi_os_printf (" Range length: %08X\n", + fixed_memory32_data->range_length); + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_address16 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_address16 ( + union acpi_resource_data *data) +{ + struct acpi_resource_address16 *address16_data = (struct acpi_resource_address16 *) data; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("16-Bit Address Space Resource\n"); + acpi_os_printf (" Resource Type: "); + + switch (address16_data->resource_type) { + case ACPI_MEMORY_RANGE: + + acpi_os_printf ("Memory Range\n"); + + switch (address16_data->attribute.memory.cache_attribute) { + case ACPI_NON_CACHEABLE_MEMORY: + acpi_os_printf (" Type Specific: " + "Noncacheable memory\n"); + break; + + case ACPI_CACHABLE_MEMORY: + acpi_os_printf (" Type Specific: " + "Cacheable memory\n"); + break; + + case ACPI_WRITE_COMBINING_MEMORY: + acpi_os_printf (" Type Specific: " + "Write-combining memory\n"); + break; + + case ACPI_PREFETCHABLE_MEMORY: + acpi_os_printf (" Type Specific: " + "Prefetchable memory\n"); + break; + + default: + acpi_os_printf (" Type Specific: " + "Invalid cache attribute\n"); + break; + } + + acpi_os_printf (" Type Specific: Read%s\n", + ACPI_READ_WRITE_MEMORY == + address16_data->attribute.memory.read_write_attribute ? + "/Write" : " Only"); + break; + + case ACPI_IO_RANGE: + + acpi_os_printf ("I/O Range\n"); + + switch (address16_data->attribute.io.range_attribute) { + case ACPI_NON_ISA_ONLY_RANGES: + acpi_os_printf (" Type Specific: " + "Non-ISA Io Addresses\n"); + break; + + case ACPI_ISA_ONLY_RANGES: + acpi_os_printf (" Type Specific: " + "ISA Io Addresses\n"); + break; + + case ACPI_ENTIRE_RANGE: + acpi_os_printf (" Type Specific: " + "ISA and non-ISA Io Addresses\n"); + break; + + default: + acpi_os_printf (" Type Specific: " + "Invalid range attribute\n"); + break; + } + + acpi_os_printf (" Type Specific: %s Translation\n", + ACPI_SPARSE_TRANSLATION == + address16_data->attribute.io.translation_attribute ? + "Sparse" : "Dense"); + break; + + case ACPI_BUS_NUMBER_RANGE: + + acpi_os_printf ("Bus Number Range\n"); + break; + + default: + + acpi_os_printf ("0x%2.2X\n", address16_data->resource_type); + break; + } + + acpi_os_printf (" Resource %s\n", + ACPI_CONSUMER == address16_data->producer_consumer ? + "Consumer" : "Producer"); + + acpi_os_printf (" %s decode\n", + ACPI_SUB_DECODE == address16_data->decode ? + "Subtractive" : "Positive"); + + acpi_os_printf (" Min address is %s fixed\n", + ACPI_ADDRESS_FIXED == address16_data->min_address_fixed ? + "" : "not"); + + acpi_os_printf (" Max address is %s fixed\n", + ACPI_ADDRESS_FIXED == address16_data->max_address_fixed ? + "" : "not"); + + acpi_os_printf (" Granularity: %08X\n", + address16_data->granularity); + + acpi_os_printf (" Address range min: %08X\n", + address16_data->min_address_range); + + acpi_os_printf (" Address range max: %08X\n", + address16_data->max_address_range); + + acpi_os_printf (" Address translation offset: %08X\n", + address16_data->address_translation_offset); + + acpi_os_printf (" Address Length: %08X\n", + address16_data->address_length); + + if (0xFF != address16_data->resource_source.index) { + acpi_os_printf (" Resource Source Index: %X\n", + address16_data->resource_source.index); + acpi_os_printf (" Resource Source: %s\n", + address16_data->resource_source.string_ptr); + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_address32 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_address32 ( + union acpi_resource_data *data) +{ + struct acpi_resource_address32 *address32_data = (struct acpi_resource_address32 *) data; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("32-Bit Address Space Resource\n"); + + switch (address32_data->resource_type) { + case ACPI_MEMORY_RANGE: + + acpi_os_printf (" Resource Type: Memory Range\n"); + + switch (address32_data->attribute.memory.cache_attribute) { + case ACPI_NON_CACHEABLE_MEMORY: + acpi_os_printf (" Type Specific: " + "Noncacheable memory\n"); + break; + + case ACPI_CACHABLE_MEMORY: + acpi_os_printf (" Type Specific: " + "Cacheable memory\n"); + break; + + case ACPI_WRITE_COMBINING_MEMORY: + acpi_os_printf (" Type Specific: " + "Write-combining memory\n"); + break; + + case ACPI_PREFETCHABLE_MEMORY: + acpi_os_printf (" Type Specific: " + "Prefetchable memory\n"); + break; + + default: + acpi_os_printf (" Type Specific: " + "Invalid cache attribute\n"); + break; + } + + acpi_os_printf (" Type Specific: Read%s\n", + ACPI_READ_WRITE_MEMORY == + address32_data->attribute.memory.read_write_attribute ? + "/Write" : " Only"); + break; + + case ACPI_IO_RANGE: + + acpi_os_printf (" Resource Type: Io Range\n"); + + switch (address32_data->attribute.io.range_attribute) { + case ACPI_NON_ISA_ONLY_RANGES: + acpi_os_printf (" Type Specific: " + "Non-ISA Io Addresses\n"); + break; + + case ACPI_ISA_ONLY_RANGES: + acpi_os_printf (" Type Specific: " + "ISA Io Addresses\n"); + break; + + case ACPI_ENTIRE_RANGE: + acpi_os_printf (" Type Specific: " + "ISA and non-ISA Io Addresses\n"); + break; + + default: + acpi_os_printf (" Type Specific: " + "Invalid Range attribute"); + break; + } + + acpi_os_printf (" Type Specific: %s Translation\n", + ACPI_SPARSE_TRANSLATION == + address32_data->attribute.io.translation_attribute ? + "Sparse" : "Dense"); + break; + + case ACPI_BUS_NUMBER_RANGE: + + acpi_os_printf (" Resource Type: Bus Number Range\n"); + break; + + default: + + acpi_os_printf (" Resource Type: 0x%2.2X\n", address32_data->resource_type); + break; + } + + acpi_os_printf (" Resource %s\n", + ACPI_CONSUMER == address32_data->producer_consumer ? + "Consumer" : "Producer"); + + acpi_os_printf (" %s decode\n", + ACPI_SUB_DECODE == address32_data->decode ? + "Subtractive" : "Positive"); + + acpi_os_printf (" Min address is %s fixed\n", + ACPI_ADDRESS_FIXED == address32_data->min_address_fixed ? + "" : "not "); + + acpi_os_printf (" Max address is %s fixed\n", + ACPI_ADDRESS_FIXED == address32_data->max_address_fixed ? + "" : "not "); + + acpi_os_printf (" Granularity: %08X\n", + address32_data->granularity); + + acpi_os_printf (" Address range min: %08X\n", + address32_data->min_address_range); + + acpi_os_printf (" Address range max: %08X\n", + address32_data->max_address_range); + + acpi_os_printf (" Address translation offset: %08X\n", + address32_data->address_translation_offset); + + acpi_os_printf (" Address Length: %08X\n", + address32_data->address_length); + + if(0xFF != address32_data->resource_source.index) { + acpi_os_printf (" Resource Source Index: %X\n", + address32_data->resource_source.index); + acpi_os_printf (" Resource Source: %s\n", + address32_data->resource_source.string_ptr); + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_address64 + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_address64 ( + union acpi_resource_data *data) +{ + struct acpi_resource_address64 *address64_data = (struct acpi_resource_address64 *) data; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("64-Bit Address Space Resource\n"); + + switch (address64_data->resource_type) { + case ACPI_MEMORY_RANGE: + + acpi_os_printf (" Resource Type: Memory Range\n"); + + switch (address64_data->attribute.memory.cache_attribute) { + case ACPI_NON_CACHEABLE_MEMORY: + acpi_os_printf (" Type Specific: " + "Noncacheable memory\n"); + break; + + case ACPI_CACHABLE_MEMORY: + acpi_os_printf (" Type Specific: " + "Cacheable memory\n"); + break; + + case ACPI_WRITE_COMBINING_MEMORY: + acpi_os_printf (" Type Specific: " + "Write-combining memory\n"); + break; + + case ACPI_PREFETCHABLE_MEMORY: + acpi_os_printf (" Type Specific: " + "Prefetchable memory\n"); + break; + + default: + acpi_os_printf (" Type Specific: " + "Invalid cache attribute\n"); + break; + } + + acpi_os_printf (" Type Specific: Read%s\n", + ACPI_READ_WRITE_MEMORY == + address64_data->attribute.memory.read_write_attribute ? + "/Write" : " Only"); + break; + + case ACPI_IO_RANGE: + + acpi_os_printf (" Resource Type: Io Range\n"); + + switch (address64_data->attribute.io.range_attribute) { + case ACPI_NON_ISA_ONLY_RANGES: + acpi_os_printf (" Type Specific: " + "Non-ISA Io Addresses\n"); + break; + + case ACPI_ISA_ONLY_RANGES: + acpi_os_printf (" Type Specific: " + "ISA Io Addresses\n"); + break; + + case ACPI_ENTIRE_RANGE: + acpi_os_printf (" Type Specific: " + "ISA and non-ISA Io Addresses\n"); + break; + + default: + acpi_os_printf (" Type Specific: " + "Invalid Range attribute"); + break; + } + + acpi_os_printf (" Type Specific: %s Translation\n", + ACPI_SPARSE_TRANSLATION == + address64_data->attribute.io.translation_attribute ? + "Sparse" : "Dense"); + break; + + case ACPI_BUS_NUMBER_RANGE: + + acpi_os_printf (" Resource Type: Bus Number Range\n"); + break; + + default: + + acpi_os_printf (" Resource Type: 0x%2.2X\n", address64_data->resource_type); + break; + } + + acpi_os_printf (" Resource %s\n", + ACPI_CONSUMER == address64_data->producer_consumer ? + "Consumer" : "Producer"); + + acpi_os_printf (" %s decode\n", + ACPI_SUB_DECODE == address64_data->decode ? + "Subtractive" : "Positive"); + + acpi_os_printf (" Min address is %s fixed\n", + ACPI_ADDRESS_FIXED == address64_data->min_address_fixed ? + "" : "not "); + + acpi_os_printf (" Max address is %s fixed\n", + ACPI_ADDRESS_FIXED == address64_data->max_address_fixed ? + "" : "not "); + + acpi_os_printf (" Granularity: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (address64_data->granularity)); + + acpi_os_printf (" Address range min: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (address64_data->min_address_range)); + + acpi_os_printf (" Address range max: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (address64_data->max_address_range)); + + acpi_os_printf (" Address translation offset: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (address64_data->address_translation_offset)); + + acpi_os_printf (" Address Length: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (address64_data->address_length)); + + acpi_os_printf (" Type Specific Attributes: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (address64_data->type_specific_attributes)); + + if (0xFF != address64_data->resource_source.index) { + acpi_os_printf (" Resource Source Index: %X\n", + address64_data->resource_source.index); + acpi_os_printf (" Resource Source: %s\n", + address64_data->resource_source.string_ptr); + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_extended_irq + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Prints out the various members of the Data structure type. + * + ******************************************************************************/ + +void +acpi_rs_dump_extended_irq ( + union acpi_resource_data *data) +{ + struct acpi_resource_ext_irq *ext_irq_data = (struct acpi_resource_ext_irq *) data; + u8 index = 0; + + + ACPI_FUNCTION_ENTRY (); + + + acpi_os_printf ("Extended IRQ Resource\n"); + + acpi_os_printf (" Resource %s\n", + ACPI_CONSUMER == ext_irq_data->producer_consumer ? + "Consumer" : "Producer"); + + acpi_os_printf (" %s\n", + ACPI_LEVEL_SENSITIVE == ext_irq_data->edge_level ? + "Level" : "Edge"); + + acpi_os_printf (" Active %s\n", + ACPI_ACTIVE_LOW == ext_irq_data->active_high_low ? + "low" : "high"); + + acpi_os_printf (" %s\n", + ACPI_SHARED == ext_irq_data->shared_exclusive ? + "Shared" : "Exclusive"); + + acpi_os_printf (" Interrupts : %X ( ", + ext_irq_data->number_of_interrupts); + + for (index = 0; index < ext_irq_data->number_of_interrupts; index++) { + acpi_os_printf ("%X ", ext_irq_data->interrupts[index]); + } + + acpi_os_printf (")\n"); + + if(0xFF != ext_irq_data->resource_source.index) { + acpi_os_printf (" Resource Source Index: %X", + ext_irq_data->resource_source.index); + acpi_os_printf (" Resource Source: %s", + ext_irq_data->resource_source.string_ptr); + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_resource_list + * + * PARAMETERS: Data - pointer to the resource structure to dump. + * + * RETURN: None + * + * DESCRIPTION: Dispatches the structure to the correct dump routine. + * + ******************************************************************************/ + +void +acpi_rs_dump_resource_list ( + struct acpi_resource *resource) +{ + u8 count = 0; + u8 done = FALSE; + + + ACPI_FUNCTION_ENTRY (); + + + if (acpi_dbg_level & ACPI_LV_RESOURCES && _COMPONENT & acpi_dbg_layer) { + while (!done) { + acpi_os_printf ("Resource structure %X.\n", count++); + + switch (resource->id) { + case ACPI_RSTYPE_IRQ: + acpi_rs_dump_irq (&resource->data); + break; + + case ACPI_RSTYPE_DMA: + acpi_rs_dump_dma (&resource->data); + break; + + case ACPI_RSTYPE_START_DPF: + acpi_rs_dump_start_depend_fns (&resource->data); + break; + + case ACPI_RSTYPE_END_DPF: + acpi_os_printf ("end_dependent_functions Resource\n"); + /* acpi_rs_dump_end_dependent_functions (Resource->Data);*/ + break; + + case ACPI_RSTYPE_IO: + acpi_rs_dump_io (&resource->data); + break; + + case ACPI_RSTYPE_FIXED_IO: + acpi_rs_dump_fixed_io (&resource->data); + break; + + case ACPI_RSTYPE_VENDOR: + acpi_rs_dump_vendor_specific (&resource->data); + break; + + case ACPI_RSTYPE_END_TAG: + /*rs_dump_end_tag (Resource->Data);*/ + acpi_os_printf ("end_tag Resource\n"); + done = TRUE; + break; + + case ACPI_RSTYPE_MEM24: + acpi_rs_dump_memory24 (&resource->data); + break; + + case ACPI_RSTYPE_MEM32: + acpi_rs_dump_memory32 (&resource->data); + break; + + case ACPI_RSTYPE_FIXED_MEM32: + acpi_rs_dump_fixed_memory32 (&resource->data); + break; + + case ACPI_RSTYPE_ADDRESS16: + acpi_rs_dump_address16 (&resource->data); + break; + + case ACPI_RSTYPE_ADDRESS32: + acpi_rs_dump_address32 (&resource->data); + break; + + case ACPI_RSTYPE_ADDRESS64: + acpi_rs_dump_address64 (&resource->data); + break; + + case ACPI_RSTYPE_EXT_IRQ: + acpi_rs_dump_extended_irq (&resource->data); + break; + + default: + acpi_os_printf ("Invalid resource type\n"); + break; + + } + + resource = ACPI_PTR_ADD (struct acpi_resource, resource, resource->length); + } + } + + return; +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dump_irq_list + * + * PARAMETERS: Data - pointer to the routing table to dump. + * + * RETURN: None + * + * DESCRIPTION: Dispatches the structures to the correct dump routine. + * + ******************************************************************************/ + +void +acpi_rs_dump_irq_list ( + u8 *route_table) +{ + u8 *buffer = route_table; + u8 count = 0; + u8 done = FALSE; + struct acpi_pci_routing_table *prt_element; + + + ACPI_FUNCTION_ENTRY (); + + + if (acpi_dbg_level & ACPI_LV_RESOURCES && _COMPONENT & acpi_dbg_layer) { + prt_element = ACPI_CAST_PTR (struct acpi_pci_routing_table, buffer); + + while (!done) { + acpi_os_printf ("PCI IRQ Routing Table structure %X.\n", count++); + + acpi_os_printf (" Address: %8.8X%8.8X\n", + ACPI_FORMAT_UINT64 (prt_element->address)); + + acpi_os_printf (" Pin: %X\n", prt_element->pin); + + acpi_os_printf (" Source: %s\n", prt_element->source); + + acpi_os_printf (" source_index: %X\n", + prt_element->source_index); + + buffer += prt_element->length; + + prt_element = ACPI_CAST_PTR (struct acpi_pci_routing_table, buffer); + + if(0 == prt_element->length) { + done = TRUE; + } + } + } + + return; +} + +#endif + diff --git a/drivers/acpi/resources/rsio.c b/drivers/acpi/resources/rsio.c new file mode 100644 index 00000000000..972c746d37e --- /dev/null +++ b/drivers/acpi/resources/rsio.c @@ -0,0 +1,545 @@ +/******************************************************************************* + * + * Module Name: rsio - IO and DMA resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rsio") + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_io_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_io_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_io); + + + ACPI_FUNCTION_TRACE ("rs_io_resource"); + + + /* + * The number of bytes consumed are Constant + */ + *bytes_consumed = 8; + + output_struct->id = ACPI_RSTYPE_IO; + + /* + * Check Decode + */ + buffer += 1; + temp8 = *buffer; + + output_struct->data.io.io_decode = temp8 & 0x01; + + /* + * Check min_base Address + */ + buffer += 1; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + output_struct->data.io.min_base_address = temp16; + + /* + * Check max_base Address + */ + buffer += 2; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + output_struct->data.io.max_base_address = temp16; + + /* + * Check Base alignment + */ + buffer += 2; + temp8 = *buffer; + + output_struct->data.io.alignment = temp8; + + /* + * Check range_length + */ + buffer += 1; + temp8 = *buffer; + + output_struct->data.io.range_length = temp8; + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_fixed_io_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_fixed_io_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_fixed_io); + + + ACPI_FUNCTION_TRACE ("rs_fixed_io_resource"); + + + /* + * The number of bytes consumed are Constant + */ + *bytes_consumed = 4; + + output_struct->id = ACPI_RSTYPE_FIXED_IO; + + /* + * Check Range Base Address + */ + buffer += 1; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + output_struct->data.fixed_io.base_address = temp16; + + /* + * Check range_length + */ + buffer += 2; + temp8 = *buffer; + + output_struct->data.fixed_io.range_length = temp8; + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_io_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_io_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + ACPI_FUNCTION_TRACE ("rs_io_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x47; + buffer += 1; + + /* + * Io Information Byte + */ + temp8 = (u8) (linked_list->data.io.io_decode & 0x01); + + *buffer = temp8; + buffer += 1; + + /* + * Set the Range minimum base address + */ + temp16 = (u16) linked_list->data.io.min_base_address; + + ACPI_MOVE_16_TO_16 (buffer, &temp16); + buffer += 2; + + /* + * Set the Range maximum base address + */ + temp16 = (u16) linked_list->data.io.max_base_address; + + ACPI_MOVE_16_TO_16 (buffer, &temp16); + buffer += 2; + + /* + * Set the base alignment + */ + temp8 = (u8) linked_list->data.io.alignment; + + *buffer = temp8; + buffer += 1; + + /* + * Set the range length + */ + temp8 = (u8) linked_list->data.io.range_length; + + *buffer = temp8; + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_fixed_io_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_fixed_io_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + ACPI_FUNCTION_TRACE ("rs_fixed_io_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x4B; + + buffer += 1; + + /* + * Set the Range base address + */ + temp16 = (u16) linked_list->data.fixed_io.base_address; + + ACPI_MOVE_16_TO_16 (buffer, &temp16); + buffer += 2; + + /* + * Set the range length + */ + temp8 = (u8) linked_list->data.fixed_io.range_length; + + *buffer = temp8; + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dma_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_dma_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u8 temp8 = 0; + u8 index; + u8 i; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_dma); + + + ACPI_FUNCTION_TRACE ("rs_dma_resource"); + + + /* + * The number of bytes consumed are Constant + */ + *bytes_consumed = 3; + output_struct->id = ACPI_RSTYPE_DMA; + + /* + * Point to the 8-bits of Byte 1 + */ + buffer += 1; + temp8 = *buffer; + + /* Decode the DMA channel bits */ + + for (i = 0, index = 0; index < 8; index++) { + if ((temp8 >> index) & 0x01) { + output_struct->data.dma.channels[i] = index; + i++; + } + } + + /* Zero DMA channels is valid */ + + output_struct->data.dma.number_of_channels = i; + if (i > 0) { + /* + * Calculate the structure size based upon the number of interrupts + */ + struct_size += ((acpi_size) i - 1) * 4; + } + + /* + * Point to Byte 2 + */ + buffer += 1; + temp8 = *buffer; + + /* + * Check for transfer preference (Bits[1:0]) + */ + output_struct->data.dma.transfer = temp8 & 0x03; + + if (0x03 == output_struct->data.dma.transfer) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid DMA.Transfer preference (3)\n")); + return_ACPI_STATUS (AE_BAD_DATA); + } + + /* + * Get bus master preference (Bit[2]) + */ + output_struct->data.dma.bus_master = (temp8 >> 2) & 0x01; + + /* + * Get channel speed support (Bits[6:5]) + */ + output_struct->data.dma.type = (temp8 >> 5) & 0x03; + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_dma_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_dma_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + + + ACPI_FUNCTION_TRACE ("rs_dma_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x2A; + buffer += 1; + temp8 = 0; + + /* + * Loop through all of the Channels and set the mask bits + */ + for (index = 0; + index < linked_list->data.dma.number_of_channels; + index++) { + temp16 = (u16) linked_list->data.dma.channels[index]; + temp8 |= 0x1 << temp16; + } + + *buffer = temp8; + buffer += 1; + + /* + * Set the DMA Info + */ + temp8 = (u8) ((linked_list->data.dma.type & 0x03) << 5); + temp8 |= ((linked_list->data.dma.bus_master & 0x01) << 2); + temp8 |= (linked_list->data.dma.transfer & 0x03); + + *buffer = temp8; + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/resources/rsirq.c b/drivers/acpi/resources/rsirq.c new file mode 100644 index 00000000000..fd07a8702fb --- /dev/null +++ b/drivers/acpi/resources/rsirq.c @@ -0,0 +1,592 @@ +/******************************************************************************* + * + * Module Name: rsirq - IRQ resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rsirq") + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_irq_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_irq_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + u8 i; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_irq); + + + ACPI_FUNCTION_TRACE ("rs_irq_resource"); + + + /* + * The number of bytes consumed are contained in the descriptor + * (Bits:0-1) + */ + temp8 = *buffer; + *bytes_consumed = (temp8 & 0x03) + 1; + output_struct->id = ACPI_RSTYPE_IRQ; + + /* + * Point to the 16-bits of Bytes 1 and 2 + */ + buffer += 1; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + output_struct->data.irq.number_of_interrupts = 0; + + /* Decode the IRQ bits */ + + for (i = 0, index = 0; index < 16; index++) { + if ((temp16 >> index) & 0x01) { + output_struct->data.irq.interrupts[i] = index; + i++; + } + } + + /* Zero interrupts is valid */ + + output_struct->data.irq.number_of_interrupts = i; + if (i > 0) { + /* + * Calculate the structure size based upon the number of interrupts + */ + struct_size += ((acpi_size) i - 1) * 4; + } + + /* + * Point to Byte 3 if it is used + */ + if (4 == *bytes_consumed) { + buffer += 2; + temp8 = *buffer; + + /* + * Check for HE, LL interrupts + */ + switch (temp8 & 0x09) { + case 0x01: /* HE */ + output_struct->data.irq.edge_level = ACPI_EDGE_SENSITIVE; + output_struct->data.irq.active_high_low = ACPI_ACTIVE_HIGH; + break; + + case 0x08: /* LL */ + output_struct->data.irq.edge_level = ACPI_LEVEL_SENSITIVE; + output_struct->data.irq.active_high_low = ACPI_ACTIVE_LOW; + break; + + default: + /* + * Only _LL and _HE polarity/trigger interrupts + * are allowed (ACPI spec, section "IRQ Format") + * so 0x00 and 0x09 are illegal. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Invalid interrupt polarity/trigger in resource list, %X\n", temp8)); + return_ACPI_STATUS (AE_BAD_DATA); + } + + /* + * Check for sharable + */ + output_struct->data.irq.shared_exclusive = (temp8 >> 3) & 0x01; + } + else { + /* + * Assume Edge Sensitive, Active High, Non-Sharable + * per ACPI Specification + */ + output_struct->data.irq.edge_level = ACPI_EDGE_SENSITIVE; + output_struct->data.irq.active_high_low = ACPI_ACTIVE_HIGH; + output_struct->data.irq.shared_exclusive = ACPI_EXCLUSIVE; + } + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_irq_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_irq_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + u8 IRqinfo_byte_needed; + + + ACPI_FUNCTION_TRACE ("rs_irq_stream"); + + + /* + * The descriptor field is set based upon whether a third byte is + * needed to contain the IRQ Information. + */ + if (ACPI_EDGE_SENSITIVE == linked_list->data.irq.edge_level && + ACPI_ACTIVE_HIGH == linked_list->data.irq.active_high_low && + ACPI_EXCLUSIVE == linked_list->data.irq.shared_exclusive) { + *buffer = 0x22; + IRqinfo_byte_needed = FALSE; + } + else { + *buffer = 0x23; + IRqinfo_byte_needed = TRUE; + } + + buffer += 1; + temp16 = 0; + + /* + * Loop through all of the interrupts and set the mask bits + */ + for(index = 0; + index < linked_list->data.irq.number_of_interrupts; + index++) { + temp8 = (u8) linked_list->data.irq.interrupts[index]; + temp16 |= 0x1 << temp8; + } + + ACPI_MOVE_16_TO_16 (buffer, &temp16); + buffer += 2; + + /* + * Set the IRQ Info byte if needed. + */ + if (IRqinfo_byte_needed) { + temp8 = 0; + temp8 = (u8) ((linked_list->data.irq.shared_exclusive & + 0x01) << 4); + + if (ACPI_LEVEL_SENSITIVE == linked_list->data.irq.edge_level && + ACPI_ACTIVE_LOW == linked_list->data.irq.active_high_low) { + temp8 |= 0x08; + } + else { + temp8 |= 0x01; + } + + *buffer = temp8; + buffer += 1; + } + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_extended_irq_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_extended_irq_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 *temp_ptr; + u8 index; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_ext_irq); + + + ACPI_FUNCTION_TRACE ("rs_extended_irq_resource"); + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + /* Validate minimum descriptor length */ + + if (temp16 < 6) { + return_ACPI_STATUS (AE_AML_BAD_RESOURCE_LENGTH); + } + + *bytes_consumed = temp16 + 3; + output_struct->id = ACPI_RSTYPE_EXT_IRQ; + + /* + * Point to the Byte3 + */ + buffer += 2; + temp8 = *buffer; + + output_struct->data.extended_irq.producer_consumer = temp8 & 0x01; + + /* + * Check for Interrupt Mode + * + * The definition of an Extended IRQ changed between ACPI spec v1.0b + * and ACPI spec 2.0 (section 6.4.3.6 in both). + * + * - Edge/Level are defined opposite in the table vs the headers + */ + output_struct->data.extended_irq.edge_level = + (temp8 & 0x2) ? ACPI_EDGE_SENSITIVE : ACPI_LEVEL_SENSITIVE; + + /* + * Check Interrupt Polarity + */ + output_struct->data.extended_irq.active_high_low = (temp8 >> 2) & 0x1; + + /* + * Check for sharable + */ + output_struct->data.extended_irq.shared_exclusive = (temp8 >> 3) & 0x01; + + /* + * Point to Byte4 (IRQ Table length) + */ + buffer += 1; + temp8 = *buffer; + + /* Must have at least one IRQ */ + + if (temp8 < 1) { + return_ACPI_STATUS (AE_AML_BAD_RESOURCE_LENGTH); + } + + output_struct->data.extended_irq.number_of_interrupts = temp8; + + /* + * Add any additional structure size to properly calculate + * the next pointer at the end of this function + */ + struct_size += (temp8 - 1) * 4; + + /* + * Point to Byte5 (First IRQ Number) + */ + buffer += 1; + + /* + * Cycle through every IRQ in the table + */ + for (index = 0; index < temp8; index++) { + ACPI_MOVE_32_TO_32 ( + &output_struct->data.extended_irq.interrupts[index], buffer); + + /* Point to the next IRQ */ + + buffer += 4; + } + + /* + * This will leave us pointing to the Resource Source Index + * If it is present, then save it off and calculate the + * pointer to where the null terminated string goes: + * Each Interrupt takes 32-bits + the 5 bytes of the + * stream that are default. + * + * Note: Some resource descriptors will have an additional null, so + * we add 1 to the length. + */ + if (*bytes_consumed > + ((acpi_size) output_struct->data.extended_irq.number_of_interrupts * 4) + (5 + 1)) { + /* Dereference the Index */ + + temp8 = *buffer; + output_struct->data.extended_irq.resource_source.index = (u32) temp8; + + /* Point to the String */ + + buffer += 1; + + /* + * Point the String pointer to the end of this structure. + */ + output_struct->data.extended_irq.resource_source.string_ptr = + (char *)((char *) output_struct + struct_size); + + temp_ptr = (u8 *) output_struct->data.extended_irq.resource_source.string_ptr; + + /* Copy the string into the buffer */ + + index = 0; + while (0x00 != *buffer) { + *temp_ptr = *buffer; + + temp_ptr += 1; + buffer += 1; + index += 1; + } + + /* + * Add the terminating null + */ + *temp_ptr = 0x00; + output_struct->data.extended_irq.resource_source.string_length = index + 1; + + /* + * In order for the struct_size to fall on a 32-bit boundary, + * calculate the length of the string and expand the + * struct_size to the next 32-bit boundary. + */ + temp8 = (u8) (index + 1); + struct_size += ACPI_ROUND_UP_to_32_bITS (temp8); + } + else { + output_struct->data.extended_irq.resource_source.index = 0x00; + output_struct->data.extended_irq.resource_source.string_length = 0; + output_struct->data.extended_irq.resource_source.string_ptr = NULL; + } + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_extended_irq_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_extended_irq_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 *length_field; + u8 temp8 = 0; + u8 index; + char *temp_pointer = NULL; + + + ACPI_FUNCTION_TRACE ("rs_extended_irq_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x89; + buffer += 1; + + /* + * Set a pointer to the Length field - to be filled in later + */ + length_field = ACPI_CAST_PTR (u16, buffer); + buffer += 2; + + /* + * Set the Interrupt vector flags + */ + temp8 = (u8)(linked_list->data.extended_irq.producer_consumer & 0x01); + temp8 |= ((linked_list->data.extended_irq.shared_exclusive & 0x01) << 3); + + /* + * Set the Interrupt Mode + * + * The definition of an Extended IRQ changed between ACPI spec v1.0b + * and ACPI spec 2.0 (section 6.4.3.6 in both). This code does not + * implement the more restrictive definition of 1.0b + * + * - Edge/Level are defined opposite in the table vs the headers + */ + if (ACPI_EDGE_SENSITIVE == linked_list->data.extended_irq.edge_level) { + temp8 |= 0x2; + } + + /* + * Set the Interrupt Polarity + */ + temp8 |= ((linked_list->data.extended_irq.active_high_low & 0x1) << 2); + + *buffer = temp8; + buffer += 1; + + /* + * Set the Interrupt table length + */ + temp8 = (u8) linked_list->data.extended_irq.number_of_interrupts; + + *buffer = temp8; + buffer += 1; + + for (index = 0; index < linked_list->data.extended_irq.number_of_interrupts; + index++) { + ACPI_MOVE_32_TO_32 (buffer, + &linked_list->data.extended_irq.interrupts[index]); + buffer += 4; + } + + /* + * Resource Source Index and Resource Source are optional + */ + if (0 != linked_list->data.extended_irq.resource_source.string_length) { + *buffer = (u8) linked_list->data.extended_irq.resource_source.index; + buffer += 1; + + temp_pointer = (char *) buffer; + + /* + * Copy the string + */ + ACPI_STRCPY (temp_pointer, + linked_list->data.extended_irq.resource_source.string_ptr); + + /* + * Buffer needs to be set to the length of the sting + one for the + * terminating null + */ + buffer += (acpi_size)(ACPI_STRLEN (linked_list->data.extended_irq.resource_source.string_ptr) + 1); + } + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + + /* + * Set the length field to the number of bytes consumed + * minus the header size (3 bytes) + */ + *length_field = (u16) (*bytes_consumed - 3); + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/resources/rslist.c b/drivers/acpi/resources/rslist.c new file mode 100644 index 00000000000..e49c1e030f9 --- /dev/null +++ b/drivers/acpi/resources/rslist.c @@ -0,0 +1,518 @@ +/******************************************************************************* + * + * Module Name: rslist - Linked list utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rslist") + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_resource_type + * + * PARAMETERS: resource_start_byte - Byte 0 of a resource descriptor + * + * RETURN: The Resource Type (Name) with no extraneous bits + * + * DESCRIPTION: Extract the Resource Type/Name from the first byte of + * a resource descriptor. + * + ******************************************************************************/ + +u8 +acpi_rs_get_resource_type ( + u8 resource_start_byte) +{ + + ACPI_FUNCTION_ENTRY (); + + + /* + * Determine if this is a small or large resource + */ + switch (resource_start_byte & ACPI_RDESC_TYPE_MASK) { + case ACPI_RDESC_TYPE_SMALL: + + /* + * Small Resource Type -- Only bits 6:3 are valid + */ + return ((u8) (resource_start_byte & ACPI_RDESC_SMALL_MASK)); + + + case ACPI_RDESC_TYPE_LARGE: + + /* + * Large Resource Type -- All bits are valid + */ + return (resource_start_byte); + + + default: + /* No other types of resource descriptor */ + break; + } + + return (0xFF); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_byte_stream_to_list + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource byte stream + * byte_stream_buffer_length - Length of byte_stream_buffer + * output_buffer - Pointer to the buffer that will + * contain the output structures + * + * RETURN: Status + * + * DESCRIPTION: Takes the resource byte stream and parses it, creating a + * linked list of resources in the caller's output buffer + * + ******************************************************************************/ + +acpi_status +acpi_rs_byte_stream_to_list ( + u8 *byte_stream_buffer, + u32 byte_stream_buffer_length, + u8 *output_buffer) +{ + acpi_status status; + acpi_size bytes_parsed = 0; + u8 resource_type = 0; + acpi_size bytes_consumed = 0; + u8 *buffer = output_buffer; + acpi_size structure_size = 0; + u8 end_tag_processed = FALSE; + struct acpi_resource *resource; + + ACPI_FUNCTION_TRACE ("rs_byte_stream_to_list"); + + + while (bytes_parsed < byte_stream_buffer_length && + !end_tag_processed) { + /* + * The next byte in the stream is the resource type + */ + resource_type = acpi_rs_get_resource_type (*byte_stream_buffer); + + switch (resource_type) { + case ACPI_RDESC_TYPE_MEMORY_24: + /* + * 24-Bit Memory Resource + */ + status = acpi_rs_memory24_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_LARGE_VENDOR: + /* + * Vendor Defined Resource + */ + status = acpi_rs_vendor_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_MEMORY_32: + /* + * 32-Bit Memory Range Resource + */ + status = acpi_rs_memory32_range_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_FIXED_MEMORY_32: + /* + * 32-Bit Fixed Memory Resource + */ + status = acpi_rs_fixed_memory32_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_QWORD_ADDRESS_SPACE: + case ACPI_RDESC_TYPE_EXTENDED_ADDRESS_SPACE: + /* + * 64-Bit Address Resource + */ + status = acpi_rs_address64_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_DWORD_ADDRESS_SPACE: + /* + * 32-Bit Address Resource + */ + status = acpi_rs_address32_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_WORD_ADDRESS_SPACE: + /* + * 16-Bit Address Resource + */ + status = acpi_rs_address16_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_EXTENDED_XRUPT: + /* + * Extended IRQ + */ + status = acpi_rs_extended_irq_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_IRQ_FORMAT: + /* + * IRQ Resource + */ + status = acpi_rs_irq_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_DMA_FORMAT: + /* + * DMA Resource + */ + status = acpi_rs_dma_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_START_DEPENDENT: + /* + * Start Dependent Functions Resource + */ + status = acpi_rs_start_depend_fns_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_END_DEPENDENT: + /* + * End Dependent Functions Resource + */ + status = acpi_rs_end_depend_fns_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_IO_PORT: + /* + * IO Port Resource + */ + status = acpi_rs_io_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_FIXED_IO_PORT: + /* + * Fixed IO Port Resource + */ + status = acpi_rs_fixed_io_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_SMALL_VENDOR: + /* + * Vendor Specific Resource + */ + status = acpi_rs_vendor_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + case ACPI_RDESC_TYPE_END_TAG: + /* + * End Tag + */ + end_tag_processed = TRUE; + status = acpi_rs_end_tag_resource (byte_stream_buffer, + &bytes_consumed, &buffer, &structure_size); + break; + + + default: + /* + * Invalid/Unknown resource type + */ + status = AE_AML_INVALID_RESOURCE_TYPE; + break; + } + + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Update the return value and counter + */ + bytes_parsed += bytes_consumed; + + /* + * Set the byte stream to point to the next resource + */ + byte_stream_buffer += bytes_consumed; + + /* + * Set the Buffer to the next structure + */ + resource = ACPI_CAST_PTR (struct acpi_resource, buffer); + resource->length = (u32) ACPI_ALIGN_RESOURCE_SIZE (resource->length); + buffer += ACPI_ALIGN_RESOURCE_SIZE (structure_size); + + } /* end while */ + + /* + * Check the reason for exiting the while loop + */ + if (!end_tag_processed) { + return_ACPI_STATUS (AE_AML_NO_RESOURCE_END_TAG); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_list_to_byte_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * byte_steam_size_needed - Calculated size of the byte stream + * needed from calling + * acpi_rs_get_byte_stream_length() + * The size of the output_buffer is + * guaranteed to be >= + * byte_stream_size_needed + * output_buffer - Pointer to the buffer that will + * contain the byte stream + * + * RETURN: Status + * + * DESCRIPTION: Takes the resource linked list and parses it, creating a + * byte stream of resources in the caller's output buffer + * + ******************************************************************************/ + +acpi_status +acpi_rs_list_to_byte_stream ( + struct acpi_resource *linked_list, + acpi_size byte_stream_size_needed, + u8 *output_buffer) +{ + acpi_status status; + u8 *buffer = output_buffer; + acpi_size bytes_consumed = 0; + u8 done = FALSE; + + + ACPI_FUNCTION_TRACE ("rs_list_to_byte_stream"); + + + while (!done) { + switch (linked_list->id) { + case ACPI_RSTYPE_IRQ: + /* + * IRQ Resource + */ + status = acpi_rs_irq_stream (linked_list, &buffer, &bytes_consumed); + break; + + case ACPI_RSTYPE_DMA: + /* + * DMA Resource + */ + status = acpi_rs_dma_stream (linked_list, &buffer, &bytes_consumed); + break; + + case ACPI_RSTYPE_START_DPF: + /* + * Start Dependent Functions Resource + */ + status = acpi_rs_start_depend_fns_stream (linked_list, + &buffer, &bytes_consumed); + break; + + case ACPI_RSTYPE_END_DPF: + /* + * End Dependent Functions Resource + */ + status = acpi_rs_end_depend_fns_stream (linked_list, + &buffer, &bytes_consumed); + break; + + case ACPI_RSTYPE_IO: + /* + * IO Port Resource + */ + status = acpi_rs_io_stream (linked_list, &buffer, &bytes_consumed); + break; + + case ACPI_RSTYPE_FIXED_IO: + /* + * Fixed IO Port Resource + */ + status = acpi_rs_fixed_io_stream (linked_list, &buffer, &bytes_consumed); + break; + + case ACPI_RSTYPE_VENDOR: + /* + * Vendor Defined Resource + */ + status = acpi_rs_vendor_stream (linked_list, &buffer, &bytes_consumed); + break; + + case ACPI_RSTYPE_END_TAG: + /* + * End Tag + */ + status = acpi_rs_end_tag_stream (linked_list, &buffer, &bytes_consumed); + + /* + * An End Tag indicates the end of the Resource Template + */ + done = TRUE; + break; + + case ACPI_RSTYPE_MEM24: + /* + * 24-Bit Memory Resource + */ + status = acpi_rs_memory24_stream (linked_list, &buffer, &bytes_consumed); + break; + + case ACPI_RSTYPE_MEM32: + /* + * 32-Bit Memory Range Resource + */ + status = acpi_rs_memory32_range_stream (linked_list, &buffer, + &bytes_consumed); + break; + + case ACPI_RSTYPE_FIXED_MEM32: + /* + * 32-Bit Fixed Memory Resource + */ + status = acpi_rs_fixed_memory32_stream (linked_list, &buffer, + &bytes_consumed); + break; + + case ACPI_RSTYPE_ADDRESS16: + /* + * 16-Bit Address Descriptor Resource + */ + status = acpi_rs_address16_stream (linked_list, &buffer, + &bytes_consumed); + break; + + case ACPI_RSTYPE_ADDRESS32: + /* + * 32-Bit Address Descriptor Resource + */ + status = acpi_rs_address32_stream (linked_list, &buffer, + &bytes_consumed); + break; + + case ACPI_RSTYPE_ADDRESS64: + /* + * 64-Bit Address Descriptor Resource + */ + status = acpi_rs_address64_stream (linked_list, &buffer, + &bytes_consumed); + break; + + case ACPI_RSTYPE_EXT_IRQ: + /* + * Extended IRQ Resource + */ + status = acpi_rs_extended_irq_stream (linked_list, &buffer, + &bytes_consumed); + break; + + default: + /* + * If we get here, everything is out of sync, + * so exit with an error + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid descriptor type (%X) in resource list\n", + linked_list->id)); + status = AE_BAD_DATA; + break; + + } /* switch (linked_list->Id) */ + + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Set the Buffer to point to the open byte + */ + buffer += bytes_consumed; + + /* + * Point to the next object + */ + linked_list = ACPI_PTR_ADD (struct acpi_resource, + linked_list, linked_list->length); + } + + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/resources/rsmemory.c b/drivers/acpi/resources/rsmemory.c new file mode 100644 index 00000000000..7c935aecf07 --- /dev/null +++ b/drivers/acpi/resources/rsmemory.c @@ -0,0 +1,566 @@ +/******************************************************************************* + * + * Module Name: rsmem24 - Memory resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rsmemory") + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_memory24_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_memory24_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_mem24); + + + ACPI_FUNCTION_TRACE ("rs_memory24_resource"); + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + + ACPI_MOVE_16_TO_16 (&temp16, buffer); + buffer += 2; + *bytes_consumed = (acpi_size) temp16 + 3; + output_struct->id = ACPI_RSTYPE_MEM24; + + /* + * Check Byte 3 the Read/Write bit + */ + temp8 = *buffer; + buffer += 1; + output_struct->data.memory24.read_write_attribute = temp8 & 0x01; + + /* + * Get min_base_address (Bytes 4-5) + */ + ACPI_MOVE_16_TO_16 (&temp16, buffer); + buffer += 2; + output_struct->data.memory24.min_base_address = temp16; + + /* + * Get max_base_address (Bytes 6-7) + */ + ACPI_MOVE_16_TO_16 (&temp16, buffer); + buffer += 2; + output_struct->data.memory24.max_base_address = temp16; + + /* + * Get Alignment (Bytes 8-9) + */ + ACPI_MOVE_16_TO_16 (&temp16, buffer); + buffer += 2; + output_struct->data.memory24.alignment = temp16; + + /* + * Get range_length (Bytes 10-11) + */ + ACPI_MOVE_16_TO_16 (&temp16, buffer); + output_struct->data.memory24.range_length = temp16; + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_memory24_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_memory24_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + ACPI_FUNCTION_TRACE ("rs_memory24_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x81; + buffer += 1; + + /* + * The length field is static + */ + temp16 = 0x09; + ACPI_MOVE_16_TO_16 (buffer, &temp16); + buffer += 2; + + /* + * Set the Information Byte + */ + temp8 = (u8) (linked_list->data.memory24.read_write_attribute & 0x01); + *buffer = temp8; + buffer += 1; + + /* + * Set the Range minimum base address + */ + ACPI_MOVE_32_TO_16 (buffer, &linked_list->data.memory24.min_base_address); + buffer += 2; + + /* + * Set the Range maximum base address + */ + ACPI_MOVE_32_TO_16 (buffer, &linked_list->data.memory24.max_base_address); + buffer += 2; + + /* + * Set the base alignment + */ + ACPI_MOVE_32_TO_16 (buffer, &linked_list->data.memory24.alignment); + buffer += 2; + + /* + * Set the range length + */ + ACPI_MOVE_32_TO_16 (buffer, &linked_list->data.memory24.range_length); + buffer += 2; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_memory32_range_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_memory32_range_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_mem32); + + + ACPI_FUNCTION_TRACE ("rs_memory32_range_resource"); + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + + ACPI_MOVE_16_TO_16 (&temp16, buffer); + buffer += 2; + *bytes_consumed = (acpi_size) temp16 + 3; + + output_struct->id = ACPI_RSTYPE_MEM32; + + /* + * Point to the place in the output buffer where the data portion will + * begin. + * 1. Set the RESOURCE_DATA * Data to point to its own address, then + * 2. Set the pointer to the next address. + * + * NOTE: output_struct->Data is cast to u8, otherwise, this addition adds + * 4 * sizeof(RESOURCE_DATA) instead of 4 * sizeof(u8) + */ + + /* + * Check Byte 3 the Read/Write bit + */ + temp8 = *buffer; + buffer += 1; + + output_struct->data.memory32.read_write_attribute = temp8 & 0x01; + + /* + * Get min_base_address (Bytes 4-7) + */ + ACPI_MOVE_32_TO_32 (&output_struct->data.memory32.min_base_address, buffer); + buffer += 4; + + /* + * Get max_base_address (Bytes 8-11) + */ + ACPI_MOVE_32_TO_32 (&output_struct->data.memory32.max_base_address, buffer); + buffer += 4; + + /* + * Get Alignment (Bytes 12-15) + */ + ACPI_MOVE_32_TO_32 (&output_struct->data.memory32.alignment, buffer); + buffer += 4; + + /* + * Get range_length (Bytes 16-19) + */ + ACPI_MOVE_32_TO_32 (&output_struct->data.memory32.range_length, buffer); + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_fixed_memory32_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_fixed_memory32_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_fixed_mem32); + + + ACPI_FUNCTION_TRACE ("rs_fixed_memory32_resource"); + + + /* + * Point past the Descriptor to get the number of bytes consumed + */ + buffer += 1; + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + buffer += 2; + *bytes_consumed = (acpi_size) temp16 + 3; + + output_struct->id = ACPI_RSTYPE_FIXED_MEM32; + + /* + * Check Byte 3 the Read/Write bit + */ + temp8 = *buffer; + buffer += 1; + output_struct->data.fixed_memory32.read_write_attribute = temp8 & 0x01; + + /* + * Get range_base_address (Bytes 4-7) + */ + ACPI_MOVE_32_TO_32 (&output_struct->data.fixed_memory32.range_base_address, buffer); + buffer += 4; + + /* + * Get range_length (Bytes 8-11) + */ + ACPI_MOVE_32_TO_32 (&output_struct->data.fixed_memory32.range_length, buffer); + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_memory32_range_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_memory32_range_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + ACPI_FUNCTION_TRACE ("rs_memory32_range_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x85; + buffer += 1; + + /* + * The length field is static + */ + temp16 = 0x11; + + ACPI_MOVE_16_TO_16 (buffer, &temp16); + buffer += 2; + + /* + * Set the Information Byte + */ + temp8 = (u8) (linked_list->data.memory32.read_write_attribute & 0x01); + *buffer = temp8; + buffer += 1; + + /* + * Set the Range minimum base address + */ + ACPI_MOVE_32_TO_32 (buffer, &linked_list->data.memory32.min_base_address); + buffer += 4; + + /* + * Set the Range maximum base address + */ + ACPI_MOVE_32_TO_32 (buffer, &linked_list->data.memory32.max_base_address); + buffer += 4; + + /* + * Set the base alignment + */ + ACPI_MOVE_32_TO_32 (buffer, &linked_list->data.memory32.alignment); + buffer += 4; + + /* + * Set the range length + */ + ACPI_MOVE_32_TO_32 (buffer, &linked_list->data.memory32.range_length); + buffer += 4; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_fixed_memory32_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_fixed_memory32_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + + + ACPI_FUNCTION_TRACE ("rs_fixed_memory32_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x86; + buffer += 1; + + /* + * The length field is static + */ + temp16 = 0x09; + + ACPI_MOVE_16_TO_16 (buffer, &temp16); + buffer += 2; + + /* + * Set the Information Byte + */ + temp8 = (u8) (linked_list->data.fixed_memory32.read_write_attribute & 0x01); + *buffer = temp8; + buffer += 1; + + /* + * Set the Range base address + */ + ACPI_MOVE_32_TO_32 (buffer, + &linked_list->data.fixed_memory32.range_base_address); + buffer += 4; + + /* + * Set the range length + */ + ACPI_MOVE_32_TO_32 (buffer, + &linked_list->data.fixed_memory32.range_length); + buffer += 4; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/resources/rsmisc.c b/drivers/acpi/resources/rsmisc.c new file mode 100644 index 00000000000..d16be44b5df --- /dev/null +++ b/drivers/acpi/resources/rsmisc.c @@ -0,0 +1,597 @@ +/******************************************************************************* + * + * Module Name: rsmisc - Miscellaneous resource descriptors + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rsmisc") + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_end_tag_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_end_tag_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + struct acpi_resource *output_struct = (void *) *output_buffer; + acpi_size struct_size = ACPI_RESOURCE_LENGTH; + + + ACPI_FUNCTION_TRACE ("rs_end_tag_resource"); + + + /* + * The number of bytes consumed is static + */ + *bytes_consumed = 2; + + /* + * Fill out the structure + */ + output_struct->id = ACPI_RSTYPE_END_TAG; + + /* + * Set the Length parameter + */ + output_struct->length = 0; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_end_tag_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_end_tag_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u8 temp8 = 0; + + + ACPI_FUNCTION_TRACE ("rs_end_tag_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x79; + buffer += 1; + + /* + * Set the Checksum - zero means that the resource data is treated as if + * the checksum operation succeeded (ACPI Spec 1.0b Section 6.4.2.8) + */ + temp8 = 0; + + *buffer = temp8; + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_vendor_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_vendor_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_vendor); + + + ACPI_FUNCTION_TRACE ("rs_vendor_resource"); + + + /* + * Dereference the Descriptor to find if this is a large or small item. + */ + temp8 = *buffer; + + if (temp8 & 0x80) { + /* + * Large Item, point to the length field + */ + buffer += 1; + + /* Dereference */ + + ACPI_MOVE_16_TO_16 (&temp16, buffer); + + /* Calculate bytes consumed */ + + *bytes_consumed = (acpi_size) temp16 + 3; + + /* Point to the first vendor byte */ + + buffer += 2; + } + else { + /* + * Small Item, dereference the size + */ + temp16 = (u8)(*buffer & 0x07); + + /* Calculate bytes consumed */ + + *bytes_consumed = (acpi_size) temp16 + 1; + + /* Point to the first vendor byte */ + + buffer += 1; + } + + output_struct->id = ACPI_RSTYPE_VENDOR; + output_struct->data.vendor_specific.length = temp16; + + for (index = 0; index < temp16; index++) { + output_struct->data.vendor_specific.reserved[index] = *buffer; + buffer += 1; + } + + /* + * In order for the struct_size to fall on a 32-bit boundary, + * calculate the length of the vendor string and expand the + * struct_size to the next 32-bit boundary. + */ + struct_size += ACPI_ROUND_UP_to_32_bITS (temp16); + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_vendor_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_vendor_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u16 temp16 = 0; + u8 temp8 = 0; + u8 index; + + + ACPI_FUNCTION_TRACE ("rs_vendor_stream"); + + + /* + * Dereference the length to find if this is a large or small item. + */ + if(linked_list->data.vendor_specific.length > 7) { + /* + * Large Item, Set the descriptor field and length bytes + */ + *buffer = 0x84; + buffer += 1; + + temp16 = (u16) linked_list->data.vendor_specific.length; + + ACPI_MOVE_16_TO_16 (buffer, &temp16); + buffer += 2; + } + else { + /* + * Small Item, Set the descriptor field + */ + temp8 = 0x70; + temp8 |= (u8) linked_list->data.vendor_specific.length; + + *buffer = temp8; + buffer += 1; + } + + /* + * Loop through all of the Vendor Specific fields + */ + for (index = 0; index < linked_list->data.vendor_specific.length; index++) { + temp8 = linked_list->data.vendor_specific.reserved[index]; + + *buffer = temp8; + buffer += 1; + } + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_start_depend_fns_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_start_depend_fns_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + u8 *buffer = byte_stream_buffer; + struct acpi_resource *output_struct = (void *) *output_buffer; + u8 temp8 = 0; + acpi_size struct_size = ACPI_SIZEOF_RESOURCE (struct acpi_resource_start_dpf); + + + ACPI_FUNCTION_TRACE ("rs_start_depend_fns_resource"); + + + /* + * The number of bytes consumed are contained in the descriptor (Bits:0-1) + */ + temp8 = *buffer; + + *bytes_consumed = (temp8 & 0x01) + 1; + + output_struct->id = ACPI_RSTYPE_START_DPF; + + /* + * Point to Byte 1 if it is used + */ + if (2 == *bytes_consumed) { + buffer += 1; + temp8 = *buffer; + + /* + * Check Compatibility priority + */ + output_struct->data.start_dpf.compatibility_priority = temp8 & 0x03; + + if (3 == output_struct->data.start_dpf.compatibility_priority) { + return_ACPI_STATUS (AE_AML_BAD_RESOURCE_VALUE); + } + + /* + * Check Performance/Robustness preference + */ + output_struct->data.start_dpf.performance_robustness = (temp8 >> 2) & 0x03; + + if (3 == output_struct->data.start_dpf.performance_robustness) { + return_ACPI_STATUS (AE_AML_BAD_RESOURCE_VALUE); + } + } + else { + output_struct->data.start_dpf.compatibility_priority = + ACPI_ACCEPTABLE_CONFIGURATION; + + output_struct->data.start_dpf.performance_robustness = + ACPI_ACCEPTABLE_CONFIGURATION; + } + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_end_depend_fns_resource + * + * PARAMETERS: byte_stream_buffer - Pointer to the resource input byte + * stream + * bytes_consumed - Pointer to where the number of bytes + * consumed the byte_stream_buffer is + * returned + * output_buffer - Pointer to the return data buffer + * structure_size - Pointer to where the number of bytes + * in the return data struct is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the resource byte stream and fill out the appropriate + * structure pointed to by the output_buffer. Return the + * number of bytes consumed from the byte stream. + * + ******************************************************************************/ + +acpi_status +acpi_rs_end_depend_fns_resource ( + u8 *byte_stream_buffer, + acpi_size *bytes_consumed, + u8 **output_buffer, + acpi_size *structure_size) +{ + struct acpi_resource *output_struct = (void *) *output_buffer; + acpi_size struct_size = ACPI_RESOURCE_LENGTH; + + + ACPI_FUNCTION_TRACE ("rs_end_depend_fns_resource"); + + + /* + * The number of bytes consumed is static + */ + *bytes_consumed = 1; + + /* + * Fill out the structure + */ + output_struct->id = ACPI_RSTYPE_END_DPF; + + /* + * Set the Length parameter + */ + output_struct->length = (u32) struct_size; + + /* + * Return the final size of the structure + */ + *structure_size = struct_size; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_start_depend_fns_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - u32 pointer that is filled with + * the number of bytes of the + * output_buffer used + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_start_depend_fns_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + u8 temp8 = 0; + + + ACPI_FUNCTION_TRACE ("rs_start_depend_fns_stream"); + + + /* + * The descriptor field is set based upon whether a byte is needed + * to contain Priority data. + */ + if (ACPI_ACCEPTABLE_CONFIGURATION == + linked_list->data.start_dpf.compatibility_priority && + ACPI_ACCEPTABLE_CONFIGURATION == + linked_list->data.start_dpf.performance_robustness) { + *buffer = 0x30; + } + else { + *buffer = 0x31; + buffer += 1; + + /* + * Set the Priority Byte Definition + */ + temp8 = 0; + temp8 = (u8) ((linked_list->data.start_dpf.performance_robustness & + 0x03) << 2); + temp8 |= (linked_list->data.start_dpf.compatibility_priority & + 0x03); + *buffer = temp8; + } + + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_end_depend_fns_stream + * + * PARAMETERS: linked_list - Pointer to the resource linked list + * output_buffer - Pointer to the user's return buffer + * bytes_consumed - Pointer to where the number of bytes + * used in the output_buffer is returned + * + * RETURN: Status + * + * DESCRIPTION: Take the linked list resource structure and fills in the + * the appropriate bytes in a byte stream + * + ******************************************************************************/ + +acpi_status +acpi_rs_end_depend_fns_stream ( + struct acpi_resource *linked_list, + u8 **output_buffer, + acpi_size *bytes_consumed) +{ + u8 *buffer = *output_buffer; + + + ACPI_FUNCTION_TRACE ("rs_end_depend_fns_stream"); + + + /* + * The descriptor field is static + */ + *buffer = 0x38; + buffer += 1; + + /* + * Return the number of bytes consumed in this operation + */ + *bytes_consumed = ACPI_PTR_DIFF (buffer, *output_buffer); + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/resources/rsutils.c b/drivers/acpi/resources/rsutils.c new file mode 100644 index 00000000000..ee9ce13c053 --- /dev/null +++ b/drivers/acpi/resources/rsutils.c @@ -0,0 +1,356 @@ +/******************************************************************************* + * + * Module Name: rsutils - Utilities for the resource manager + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acresrc.h> + + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rsutils") + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_prt_method_data + * + * PARAMETERS: Handle - a handle to the containing object + * ret_buffer - a pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _PRT value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_prt_method_data ( + acpi_handle handle, + struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("rs_get_prt_method_data"); + + + /* Parameters guaranteed valid by caller */ + + /* + * Execute the method, no parameters + */ + status = acpi_ut_evaluate_object (handle, "_PRT", ACPI_BTYPE_PACKAGE, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Create a resource linked list from the byte stream buffer that comes + * back from the _CRS method execution. + */ + status = acpi_rs_create_pci_routing_table (obj_desc, ret_buffer); + + /* On exit, we must delete the object returned by evaluate_object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_crs_method_data + * + * PARAMETERS: Handle - a handle to the containing object + * ret_buffer - a pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _CRS value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_crs_method_data ( + acpi_handle handle, + struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("rs_get_crs_method_data"); + + + /* Parameters guaranteed valid by caller */ + + /* + * Execute the method, no parameters + */ + status = acpi_ut_evaluate_object (handle, "_CRS", ACPI_BTYPE_BUFFER, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the _CRS method + * execution. + */ + status = acpi_rs_create_resource_list (obj_desc, ret_buffer); + + /* on exit, we must delete the object returned by evaluate_object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_prs_method_data + * + * PARAMETERS: Handle - a handle to the containing object + * ret_buffer - a pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _PRS value of an object + * contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_rs_get_prs_method_data ( + acpi_handle handle, + struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("rs_get_prs_method_data"); + + + /* Parameters guaranteed valid by caller */ + + /* + * Execute the method, no parameters + */ + status = acpi_ut_evaluate_object (handle, "_PRS", ACPI_BTYPE_BUFFER, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the _CRS method + * execution. + */ + status = acpi_rs_create_resource_list (obj_desc, ret_buffer); + + /* on exit, we must delete the object returned by evaluate_object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_rs_get_method_data + * + * PARAMETERS: Handle - a handle to the containing object + * ret_buffer - a pointer to a buffer structure for the + * results + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the _CRS or _PRS value of an + * object contained in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_rs_get_method_data ( + acpi_handle handle, + char *path, + struct acpi_buffer *ret_buffer) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("rs_get_method_data"); + + + /* Parameters guaranteed valid by caller */ + + /* + * Execute the method, no parameters + */ + status = acpi_ut_evaluate_object (handle, path, ACPI_BTYPE_BUFFER, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Make the call to create a resource linked list from the + * byte stream buffer that comes back from the method + * execution. + */ + status = acpi_rs_create_resource_list (obj_desc, ret_buffer); + + /* On exit, we must delete the object returned by evaluate_object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_rs_set_srs_method_data + * + * PARAMETERS: Handle - a handle to the containing object + * in_buffer - a pointer to a buffer structure of the + * parameter + * + * RETURN: Status + * + * DESCRIPTION: This function is called to set the _SRS of an object contained + * in an object specified by the handle passed in + * + * If the function fails an appropriate status will be returned + * and the contents of the callers buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_rs_set_srs_method_data ( + acpi_handle handle, + struct acpi_buffer *in_buffer) +{ + struct acpi_parameter_info info; + union acpi_operand_object *params[2]; + acpi_status status; + struct acpi_buffer buffer; + + + ACPI_FUNCTION_TRACE ("rs_set_srs_method_data"); + + + /* Parameters guaranteed valid by caller */ + + /* + * The in_buffer parameter will point to a linked list of + * resource parameters. It needs to be formatted into a + * byte stream to be sent in as an input parameter to _SRS + * + * Convert the linked list into a byte stream + */ + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_rs_create_byte_stream (in_buffer->pointer, &buffer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Init the param object + */ + params[0] = acpi_ut_create_internal_object (ACPI_TYPE_BUFFER); + if (!params[0]) { + acpi_os_free (buffer.pointer); + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* + * Set up the parameter object + */ + params[0]->buffer.length = (u32) buffer.length; + params[0]->buffer.pointer = buffer.pointer; + params[0]->common.flags = AOPOBJ_DATA_VALID; + params[1] = NULL; + + info.node = handle; + info.parameters = params; + info.parameter_type = ACPI_PARAM_ARGS; + + /* + * Execute the method, no return value + */ + status = acpi_ns_evaluate_relative ("_SRS", &info); + if (ACPI_SUCCESS (status)) { + /* Delete any return object (especially if implicit_return is enabled) */ + + if (info.return_object) { + acpi_ut_remove_reference (info.return_object); + } + } + + /* + * Clean up and return the status from acpi_ns_evaluate_relative + */ + acpi_ut_remove_reference (params[0]); + return_ACPI_STATUS (status); +} + diff --git a/drivers/acpi/resources/rsxface.c b/drivers/acpi/resources/rsxface.c new file mode 100644 index 00000000000..a9cdcbeb343 --- /dev/null +++ b/drivers/acpi/resources/rsxface.c @@ -0,0 +1,437 @@ +/******************************************************************************* + * + * Module Name: rsxface - Public interfaces to the resource manager + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acresrc.h> + +#define _COMPONENT ACPI_RESOURCES + ACPI_MODULE_NAME ("rsxface") + + +/******************************************************************************* + * + * FUNCTION: acpi_get_irq_routing_table + * + * PARAMETERS: device_handle - a handle to the Bus device we are querying + * ret_buffer - a pointer to a buffer to receive the + * current resources for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the IRQ routing table for a + * specific bus. The caller must first acquire a handle for the + * desired bus. The routine table is placed in the buffer pointed + * to by the ret_buffer variable parameter. + * + * If the function fails an appropriate status will be returned + * and the value of ret_buffer is undefined. + * + * This function attempts to execute the _PRT method contained in + * the object indicated by the passed device_handle. + * + ******************************************************************************/ + +acpi_status +acpi_get_irq_routing_table ( + acpi_handle device_handle, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_get_irq_routing_table "); + + + /* + * Must have a valid handle and buffer, So we have to have a handle + * and a return buffer structure, and if there is a non-zero buffer length + * we also need a valid pointer in the buffer. If it's a zero buffer length, + * we'll be returning the needed buffer size, so keep going. + */ + if (!device_handle) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_validate_buffer (ret_buffer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_rs_get_prt_method_data (device_handle, ret_buffer); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_get_current_resources + * + * PARAMETERS: device_handle - a handle to the device object for the + * device we are querying + * ret_buffer - a pointer to a buffer to receive the + * current resources for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the current resources for a + * specific device. The caller must first acquire a handle for + * the desired device. The resource data is placed in the buffer + * pointed to by the ret_buffer variable parameter. + * + * If the function fails an appropriate status will be returned + * and the value of ret_buffer is undefined. + * + * This function attempts to execute the _CRS method contained in + * the object indicated by the passed device_handle. + * + ******************************************************************************/ + +acpi_status +acpi_get_current_resources ( + acpi_handle device_handle, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_get_current_resources"); + + + /* + * Must have a valid handle and buffer, So we have to have a handle + * and a return buffer structure, and if there is a non-zero buffer length + * we also need a valid pointer in the buffer. If it's a zero buffer length, + * we'll be returning the needed buffer size, so keep going. + */ + if (!device_handle) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_validate_buffer (ret_buffer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_rs_get_crs_method_data (device_handle, ret_buffer); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_get_current_resources); + + +/******************************************************************************* + * + * FUNCTION: acpi_get_possible_resources + * + * PARAMETERS: device_handle - a handle to the device object for the + * device we are querying + * ret_buffer - a pointer to a buffer to receive the + * resources for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get a list of the possible resources + * for a specific device. The caller must first acquire a handle + * for the desired device. The resource data is placed in the + * buffer pointed to by the ret_buffer variable. + * + * If the function fails an appropriate status will be returned + * and the value of ret_buffer is undefined. + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_get_possible_resources ( + acpi_handle device_handle, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_get_possible_resources"); + + + /* + * Must have a valid handle and buffer, So we have to have a handle + * and a return buffer structure, and if there is a non-zero buffer length + * we also need a valid pointer in the buffer. If it's a zero buffer length, + * we'll be returning the needed buffer size, so keep going. + */ + if (!device_handle) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_validate_buffer (ret_buffer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_rs_get_prs_method_data (device_handle, ret_buffer); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_get_possible_resources); +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_walk_resources + * + * PARAMETERS: device_handle - a handle to the device object for the + * device we are querying + * Path - method name of the resources we want + * (METHOD_NAME__CRS or METHOD_NAME__PRS) + * user_function - called for each resource + * Context - passed to user_function + * + * RETURN: Status + * + * DESCRIPTION: Retrieves the current or possible resource list for the + * specified device. The user_function is called once for + * each resource in the list. + * + ******************************************************************************/ + +acpi_status +acpi_walk_resources ( + acpi_handle device_handle, + char *path, + ACPI_WALK_RESOURCE_CALLBACK user_function, + void *context) +{ + acpi_status status; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + struct acpi_resource *resource; + struct acpi_resource *buffer_end; + + + ACPI_FUNCTION_TRACE ("acpi_walk_resources"); + + + if (!device_handle || + (ACPI_STRNCMP (path, METHOD_NAME__CRS, sizeof (METHOD_NAME__CRS)) && + ACPI_STRNCMP (path, METHOD_NAME__PRS, sizeof (METHOD_NAME__PRS)))) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_rs_get_method_data (device_handle, path, &buffer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Setup pointers */ + + resource = (struct acpi_resource *) buffer.pointer; + buffer_end = ACPI_CAST_PTR (struct acpi_resource, + ((u8 *) buffer.pointer + buffer.length)); + + /* Walk the resource list */ + + for (;;) { + if (!resource || resource->id == ACPI_RSTYPE_END_TAG) { + break; + } + + status = user_function (resource, context); + + switch (status) { + case AE_OK: + case AE_CTRL_DEPTH: + + /* Just keep going */ + + status = AE_OK; + break; + + case AE_CTRL_TERMINATE: + + /* Exit now, with OK stats */ + + status = AE_OK; + goto cleanup; + + default: + + /* All others are valid exceptions */ + + goto cleanup; + } + + /* Get the next resource descriptor */ + + resource = ACPI_NEXT_RESOURCE (resource); + + /* Check for end-of-buffer */ + + if (resource >= buffer_end) { + goto cleanup; + } + } + +cleanup: + + acpi_os_free (buffer.pointer); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_walk_resources); + + +/******************************************************************************* + * + * FUNCTION: acpi_set_current_resources + * + * PARAMETERS: device_handle - a handle to the device object for the + * device we are changing the resources of + * in_buffer - a pointer to a buffer containing the + * resources to be set for the device + * + * RETURN: Status + * + * DESCRIPTION: This function is called to set the current resources for a + * specific device. The caller must first acquire a handle for + * the desired device. The resource data is passed to the routine + * the buffer pointed to by the in_buffer variable. + * + ******************************************************************************/ + +acpi_status +acpi_set_current_resources ( + acpi_handle device_handle, + struct acpi_buffer *in_buffer) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_set_current_resources"); + + + /* + * Must have a valid handle and buffer + */ + if ((!device_handle) || + (!in_buffer) || + (!in_buffer->pointer) || + (!in_buffer->length)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_rs_set_srs_method_data (device_handle, in_buffer); + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_set_current_resources); + + +#define ACPI_COPY_FIELD(out, in, field) ((out)->field = (in)->field) +#define ACPI_COPY_ADDRESS(out, in) \ + ACPI_COPY_FIELD(out, in, resource_type); \ + ACPI_COPY_FIELD(out, in, producer_consumer); \ + ACPI_COPY_FIELD(out, in, decode); \ + ACPI_COPY_FIELD(out, in, min_address_fixed); \ + ACPI_COPY_FIELD(out, in, max_address_fixed); \ + ACPI_COPY_FIELD(out, in, attribute); \ + ACPI_COPY_FIELD(out, in, granularity); \ + ACPI_COPY_FIELD(out, in, min_address_range); \ + ACPI_COPY_FIELD(out, in, max_address_range); \ + ACPI_COPY_FIELD(out, in, address_translation_offset); \ + ACPI_COPY_FIELD(out, in, address_length); \ + ACPI_COPY_FIELD(out, in, resource_source); + +/****************************************************************************** + * + * FUNCTION: acpi_resource_to_address64 + * + * PARAMETERS: resource - Pointer to a resource + * out - Pointer to the users's return + * buffer (a struct + * struct acpi_resource_address64) + * + * RETURN: Status + * + * DESCRIPTION: If the resource is an address16, address32, or address64, + * copy it to the address64 return buffer. This saves the + * caller from having to duplicate code for different-sized + * addresses. + * + ******************************************************************************/ + +acpi_status +acpi_resource_to_address64 ( + struct acpi_resource *resource, + struct acpi_resource_address64 *out) +{ + struct acpi_resource_address16 *address16; + struct acpi_resource_address32 *address32; + + + switch (resource->id) { + case ACPI_RSTYPE_ADDRESS16: + + address16 = (struct acpi_resource_address16 *) &resource->data; + ACPI_COPY_ADDRESS(out, address16); + break; + + + case ACPI_RSTYPE_ADDRESS32: + + address32 = (struct acpi_resource_address32 *) &resource->data; + ACPI_COPY_ADDRESS(out, address32); + break; + + + case ACPI_RSTYPE_ADDRESS64: + + /* Simple copy for 64 bit source */ + + ACPI_MEMCPY (out, &resource->data, sizeof (struct acpi_resource_address64)); + break; + + + default: + return (AE_BAD_PARAMETER); + } + + return (AE_OK); +} +EXPORT_SYMBOL(acpi_resource_to_address64); + diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c new file mode 100644 index 00000000000..e7ca0662656 --- /dev/null +++ b/drivers/acpi/scan.c @@ -0,0 +1,1379 @@ +/* + * scan.c - support for transforming the ACPI namespace into individual objects + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/acpi.h> + +#include <acpi/acpi_drivers.h> +#include <acpi/acinterp.h> /* for acpi_ex_eisa_id_to_string() */ + + +#define _COMPONENT ACPI_BUS_COMPONENT +ACPI_MODULE_NAME ("scan") + +#define STRUCT_TO_INT(s) (*((int*)&s)) + +extern struct acpi_device *acpi_root; + + +#define ACPI_BUS_CLASS "system_bus" +#define ACPI_BUS_HID "ACPI_BUS" +#define ACPI_BUS_DRIVER_NAME "ACPI Bus Driver" +#define ACPI_BUS_DEVICE_NAME "System Bus" + +static LIST_HEAD(acpi_device_list); +DEFINE_SPINLOCK(acpi_device_lock); +LIST_HEAD(acpi_wakeup_device_list); + +static int +acpi_bus_trim(struct acpi_device *start, + int rmdevice); + +static void acpi_device_release(struct kobject * kobj) +{ + struct acpi_device * dev = container_of(kobj,struct acpi_device,kobj); + if (dev->pnp.cid_list) + kfree(dev->pnp.cid_list); + kfree(dev); +} + +struct acpi_device_attribute { + struct attribute attr; + ssize_t (*show)(struct acpi_device *, char *); + ssize_t (*store)(struct acpi_device *, const char *, size_t); +}; + +typedef void acpi_device_sysfs_files(struct kobject *, + const struct attribute *); + +static void setup_sys_fs_device_files(struct acpi_device *dev, + acpi_device_sysfs_files *func); + +#define create_sysfs_device_files(dev) \ + setup_sys_fs_device_files(dev, (acpi_device_sysfs_files *)&sysfs_create_file) +#define remove_sysfs_device_files(dev) \ + setup_sys_fs_device_files(dev, (acpi_device_sysfs_files *)&sysfs_remove_file) + + +#define to_acpi_device(n) container_of(n, struct acpi_device, kobj) +#define to_handle_attr(n) container_of(n, struct acpi_device_attribute, attr); + +static ssize_t acpi_device_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct acpi_device *device = to_acpi_device(kobj); + struct acpi_device_attribute *attribute = to_handle_attr(attr); + return attribute->show ? attribute->show(device, buf) : 0; +} +static ssize_t acpi_device_attr_store(struct kobject *kobj, + struct attribute *attr, const char *buf, size_t len) +{ + struct acpi_device *device = to_acpi_device(kobj); + struct acpi_device_attribute *attribute = to_handle_attr(attr); + return attribute->store ? attribute->store(device, buf, len) : len; +} + +static struct sysfs_ops acpi_device_sysfs_ops = { + .show = acpi_device_attr_show, + .store = acpi_device_attr_store, +}; + +static struct kobj_type ktype_acpi_ns = { + .sysfs_ops = &acpi_device_sysfs_ops, + .release = acpi_device_release, +}; + +static int namespace_hotplug(struct kset *kset, struct kobject *kobj, + char **envp, int num_envp, char *buffer, + int buffer_size) +{ + struct acpi_device *dev = to_acpi_device(kobj); + int i = 0; + int len = 0; + + if (!dev->driver) + return 0; + + if (add_hotplug_env_var(envp, num_envp, &i, buffer, buffer_size, &len, + "PHYSDEVDRIVER=%s", dev->driver->name)) + return -ENOMEM; + + envp[i] = NULL; + + return 0; +} + +static struct kset_hotplug_ops namespace_hotplug_ops = { + .hotplug = &namespace_hotplug, +}; + +static struct kset acpi_namespace_kset = { + .kobj = { + .name = "namespace", + }, + .subsys = &acpi_subsys, + .ktype = &ktype_acpi_ns, + .hotplug_ops = &namespace_hotplug_ops, +}; + + +static void acpi_device_register(struct acpi_device * device, struct acpi_device * parent) +{ + /* + * Linkage + * ------- + * Link this device to its parent and siblings. + */ + INIT_LIST_HEAD(&device->children); + INIT_LIST_HEAD(&device->node); + INIT_LIST_HEAD(&device->g_list); + INIT_LIST_HEAD(&device->wakeup_list); + + spin_lock(&acpi_device_lock); + if (device->parent) { + list_add_tail(&device->node, &device->parent->children); + list_add_tail(&device->g_list,&device->parent->g_list); + } else + list_add_tail(&device->g_list,&acpi_device_list); + if (device->wakeup.flags.valid) + list_add_tail(&device->wakeup_list,&acpi_wakeup_device_list); + spin_unlock(&acpi_device_lock); + + strlcpy(device->kobj.name,device->pnp.bus_id,KOBJ_NAME_LEN); + if (parent) + device->kobj.parent = &parent->kobj; + device->kobj.ktype = &ktype_acpi_ns; + device->kobj.kset = &acpi_namespace_kset; + kobject_register(&device->kobj); + create_sysfs_device_files(device); +} + +static int +acpi_device_unregister ( + struct acpi_device *device, + int type) +{ + spin_lock(&acpi_device_lock); + if (device->parent) { + list_del(&device->node); + list_del(&device->g_list); + } else + list_del(&device->g_list); + + list_del(&device->wakeup_list); + + spin_unlock(&acpi_device_lock); + + acpi_detach_data(device->handle, acpi_bus_data_handler); + remove_sysfs_device_files(device); + kobject_unregister(&device->kobj); + return 0; +} + +void +acpi_bus_data_handler ( + acpi_handle handle, + u32 function, + void *context) +{ + ACPI_FUNCTION_TRACE("acpi_bus_data_handler"); + + /* TBD */ + + return_VOID; +} + +static int +acpi_bus_get_power_flags ( + struct acpi_device *device) +{ + acpi_status status = 0; + acpi_handle handle = NULL; + u32 i = 0; + + ACPI_FUNCTION_TRACE("acpi_bus_get_power_flags"); + + /* + * Power Management Flags + */ + status = acpi_get_handle(device->handle, "_PSC", &handle); + if (ACPI_SUCCESS(status)) + device->power.flags.explicit_get = 1; + status = acpi_get_handle(device->handle, "_IRC", &handle); + if (ACPI_SUCCESS(status)) + device->power.flags.inrush_current = 1; + + /* + * Enumerate supported power management states + */ + for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3; i++) { + struct acpi_device_power_state *ps = &device->power.states[i]; + char object_name[5] = {'_','P','R','0'+i,'\0'}; + + /* Evaluate "_PRx" to se if power resources are referenced */ + acpi_evaluate_reference(device->handle, object_name, NULL, + &ps->resources); + if (ps->resources.count) { + device->power.flags.power_resources = 1; + ps->flags.valid = 1; + } + + /* Evaluate "_PSx" to see if we can do explicit sets */ + object_name[2] = 'S'; + status = acpi_get_handle(device->handle, object_name, &handle); + if (ACPI_SUCCESS(status)) { + ps->flags.explicit_set = 1; + ps->flags.valid = 1; + } + + /* State is valid if we have some power control */ + if (ps->resources.count || ps->flags.explicit_set) + ps->flags.valid = 1; + + ps->power = -1; /* Unknown - driver assigned */ + ps->latency = -1; /* Unknown - driver assigned */ + } + + /* Set defaults for D0 and D3 states (always valid) */ + device->power.states[ACPI_STATE_D0].flags.valid = 1; + device->power.states[ACPI_STATE_D0].power = 100; + device->power.states[ACPI_STATE_D3].flags.valid = 1; + device->power.states[ACPI_STATE_D3].power = 0; + + /* TBD: System wake support and resource requirements. */ + + device->power.state = ACPI_STATE_UNKNOWN; + + return_VALUE(0); +} + +int +acpi_match_ids ( + struct acpi_device *device, + char *ids) +{ + int error = 0; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + + if (device->flags.hardware_id) + if (strstr(ids, device->pnp.hardware_id)) + goto Done; + + if (device->flags.compatible_ids) { + struct acpi_compatible_id_list *cid_list = device->pnp.cid_list; + int i; + + /* compare multiple _CID entries against driver ids */ + for (i = 0; i < cid_list->count; i++) + { + if (strstr(ids, cid_list->id[i].value)) + goto Done; + } + } + error = -ENOENT; + + Done: + if (buffer.pointer) + acpi_os_free(buffer.pointer); + return error; +} + +static acpi_status +acpi_bus_extract_wakeup_device_power_package ( + struct acpi_device *device, + union acpi_object *package) +{ + int i = 0; + union acpi_object *element = NULL; + + if (!device || !package || (package->package.count < 2)) + return AE_BAD_PARAMETER; + + element = &(package->package.elements[0]); + if (!element) + return AE_BAD_PARAMETER; + if (element->type == ACPI_TYPE_PACKAGE) { + if ((element->package.count < 2) || + (element->package.elements[0].type != ACPI_TYPE_LOCAL_REFERENCE) || + (element->package.elements[1].type != ACPI_TYPE_INTEGER)) + return AE_BAD_DATA; + device->wakeup.gpe_device = element->package.elements[0].reference.handle; + device->wakeup.gpe_number = (u32)element->package.elements[1].integer.value; + }else if (element->type == ACPI_TYPE_INTEGER) { + device->wakeup.gpe_number = element->integer.value; + }else + return AE_BAD_DATA; + + element = &(package->package.elements[1]); + if (element->type != ACPI_TYPE_INTEGER) { + return AE_BAD_DATA; + } + device->wakeup.sleep_state = element->integer.value; + + if ((package->package.count - 2) > ACPI_MAX_HANDLES) { + return AE_NO_MEMORY; + } + device->wakeup.resources.count = package->package.count - 2; + for (i=0; i < device->wakeup.resources.count; i++) { + element = &(package->package.elements[i + 2]); + if (element->type != ACPI_TYPE_ANY ) { + return AE_BAD_DATA; + } + + device->wakeup.resources.handles[i] = element->reference.handle; + } + + return AE_OK; +} + +static int +acpi_bus_get_wakeup_device_flags ( + struct acpi_device *device) +{ + acpi_status status = 0; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *package = NULL; + + ACPI_FUNCTION_TRACE("acpi_bus_get_wakeup_flags"); + + /* _PRW */ + status = acpi_evaluate_object(device->handle, "_PRW", NULL, &buffer); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _PRW\n")); + goto end; + } + + package = (union acpi_object *) buffer.pointer; + status = acpi_bus_extract_wakeup_device_power_package(device, package); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _PRW package\n")); + goto end; + } + + acpi_os_free(buffer.pointer); + + device->wakeup.flags.valid = 1; + /* Power button, Lid switch always enable wakeup*/ + if (!acpi_match_ids(device, "PNP0C0D,PNP0C0C,PNP0C0E")) + device->wakeup.flags.run_wake = 1; + +end: + if (ACPI_FAILURE(status)) + device->flags.wake_capable = 0; + return_VALUE(0); +} + +/* -------------------------------------------------------------------------- + ACPI hotplug sysfs device file support + -------------------------------------------------------------------------- */ +static ssize_t acpi_eject_store(struct acpi_device *device, + const char *buf, size_t count); + +#define ACPI_DEVICE_ATTR(_name,_mode,_show,_store) \ +static struct acpi_device_attribute acpi_device_attr_##_name = \ + __ATTR(_name, _mode, _show, _store) + +ACPI_DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); + +/** + * setup_sys_fs_device_files - sets up the device files under device namespace + * @@dev: acpi_device object + * @@func: function pointer to create or destroy the device file + */ +static void +setup_sys_fs_device_files ( + struct acpi_device *dev, + acpi_device_sysfs_files *func) +{ + acpi_status status; + acpi_handle temp = NULL; + + /* + * If device has _EJ0, 'eject' file is created that is used to trigger + * hot-removal function from userland. + */ + status = acpi_get_handle(dev->handle, "_EJ0", &temp); + if (ACPI_SUCCESS(status)) + (*(func))(&dev->kobj,&acpi_device_attr_eject.attr); +} + +static int +acpi_eject_operation(acpi_handle handle, int lockable) +{ + struct acpi_object_list arg_list; + union acpi_object arg; + acpi_status status = AE_OK; + + /* + * TBD: evaluate _PS3? + */ + + if (lockable) { + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = 0; + acpi_evaluate_object(handle, "_LCK", &arg_list, NULL); + } + + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = 1; + + /* + * TBD: _EJD support. + */ + + status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); + if (ACPI_FAILURE(status)) { + return(-ENODEV); + } + + return(0); +} + + +static ssize_t +acpi_eject_store(struct acpi_device *device, const char *buf, size_t count) +{ + int result; + int ret = count; + int islockable; + acpi_status status; + acpi_handle handle; + acpi_object_type type = 0; + + if ((!count) || (buf[0] != '1')) { + return -EINVAL; + } + +#ifndef FORCE_EJECT + if (device->driver == NULL) { + ret = -ENODEV; + goto err; + } +#endif + status = acpi_get_type(device->handle, &type); + if (ACPI_FAILURE(status) || (!device->flags.ejectable) ) { + ret = -ENODEV; + goto err; + } + + islockable = device->flags.lockable; + handle = device->handle; + + if (type == ACPI_TYPE_PROCESSOR) + result = acpi_bus_trim(device, 0); + else + result = acpi_bus_trim(device, 1); + + if (!result) + result = acpi_eject_operation(handle, islockable); + + if (result) { + ret = -EBUSY; + } +err: + return ret; +} + + +/* -------------------------------------------------------------------------- + Performance Management + -------------------------------------------------------------------------- */ + +static int +acpi_bus_get_perf_flags ( + struct acpi_device *device) +{ + device->performance.state = ACPI_STATE_UNKNOWN; + return 0; +} + +/* -------------------------------------------------------------------------- + Driver Management + -------------------------------------------------------------------------- */ + +static LIST_HEAD(acpi_bus_drivers); +static DECLARE_MUTEX(acpi_bus_drivers_lock); + + +/** + * acpi_bus_match + * -------------- + * Checks the device's hardware (_HID) or compatible (_CID) ids to see if it + * matches the specified driver's criteria. + */ +static int +acpi_bus_match ( + struct acpi_device *device, + struct acpi_driver *driver) +{ + if (driver && driver->ops.match) + return driver->ops.match(device, driver); + return acpi_match_ids(device, driver->ids); +} + + +/** + * acpi_bus_driver_init + * -------------------- + * Used to initialize a device via its device driver. Called whenever a + * driver is bound to a device. Invokes the driver's add() and start() ops. + */ +static int +acpi_bus_driver_init ( + struct acpi_device *device, + struct acpi_driver *driver) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_bus_driver_init"); + + if (!device || !driver) + return_VALUE(-EINVAL); + + if (!driver->ops.add) + return_VALUE(-ENOSYS); + + result = driver->ops.add(device); + if (result) { + device->driver = NULL; + acpi_driver_data(device) = NULL; + return_VALUE(result); + } + + device->driver = driver; + + /* + * TBD - Configuration Management: Assign resources to device based + * upon possible configuration and currently allocated resources. + */ + + if (driver->ops.start) { + result = driver->ops.start(device); + if (result && driver->ops.remove) + driver->ops.remove(device, ACPI_BUS_REMOVAL_NORMAL); + return_VALUE(result); + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Driver successfully bound to device\n")); + + if (driver->ops.scan) { + driver->ops.scan(device); + } + + return_VALUE(0); +} + +static int acpi_driver_attach(struct acpi_driver * drv) +{ + struct list_head * node, * next; + int count = 0; + + ACPI_FUNCTION_TRACE("acpi_driver_attach"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_device_list) { + struct acpi_device * dev = container_of(node, struct acpi_device, g_list); + + if (dev->driver || !dev->status.present) + continue; + spin_unlock(&acpi_device_lock); + + if (!acpi_bus_match(dev, drv)) { + if (!acpi_bus_driver_init(dev, drv)) { + atomic_inc(&drv->references); + count++; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found driver [%s] for device [%s]\n", + drv->name, dev->pnp.bus_id)); + } + } + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); + return_VALUE(count); +} + +static int acpi_driver_detach(struct acpi_driver * drv) +{ + struct list_head * node, * next; + + ACPI_FUNCTION_TRACE("acpi_driver_detach"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node,next,&acpi_device_list) { + struct acpi_device * dev = container_of(node,struct acpi_device,g_list); + + if (dev->driver == drv) { + spin_unlock(&acpi_device_lock); + if (drv->ops.remove) + drv->ops.remove(dev,ACPI_BUS_REMOVAL_NORMAL); + spin_lock(&acpi_device_lock); + dev->driver = NULL; + dev->driver_data = NULL; + atomic_dec(&drv->references); + } + } + spin_unlock(&acpi_device_lock); + return_VALUE(0); +} + +/** + * acpi_bus_register_driver + * ------------------------ + * Registers a driver with the ACPI bus. Searches the namespace for all + * devices that match the driver's criteria and binds. Returns the + * number of devices that were claimed by the driver, or a negative + * error status for failure. + */ +int +acpi_bus_register_driver ( + struct acpi_driver *driver) +{ + int count; + + ACPI_FUNCTION_TRACE("acpi_bus_register_driver"); + + if (acpi_disabled) + return_VALUE(-ENODEV); + + if (!driver) + return_VALUE(-EINVAL); + + spin_lock(&acpi_device_lock); + list_add_tail(&driver->node, &acpi_bus_drivers); + spin_unlock(&acpi_device_lock); + count = acpi_driver_attach(driver); + + return_VALUE(count); +} +EXPORT_SYMBOL(acpi_bus_register_driver); + + +/** + * acpi_bus_unregister_driver + * -------------------------- + * Unregisters a driver with the ACPI bus. Searches the namespace for all + * devices that match the driver's criteria and unbinds. + */ +int +acpi_bus_unregister_driver ( + struct acpi_driver *driver) +{ + int error = 0; + + ACPI_FUNCTION_TRACE("acpi_bus_unregister_driver"); + + if (driver) { + acpi_driver_detach(driver); + + if (!atomic_read(&driver->references)) { + spin_lock(&acpi_device_lock); + list_del_init(&driver->node); + spin_unlock(&acpi_device_lock); + } + } else + error = -EINVAL; + return_VALUE(error); +} +EXPORT_SYMBOL(acpi_bus_unregister_driver); + +/** + * acpi_bus_find_driver + * -------------------- + * Parses the list of registered drivers looking for a driver applicable for + * the specified device. + */ +static int +acpi_bus_find_driver ( + struct acpi_device *device) +{ + int result = 0; + struct list_head * node, *next; + + ACPI_FUNCTION_TRACE("acpi_bus_find_driver"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node,next,&acpi_bus_drivers) { + struct acpi_driver * driver = container_of(node,struct acpi_driver,node); + + atomic_inc(&driver->references); + spin_unlock(&acpi_device_lock); + if (!acpi_bus_match(device, driver)) { + result = acpi_bus_driver_init(device, driver); + if (!result) + goto Done; + } + atomic_dec(&driver->references); + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); + + Done: + return_VALUE(result); +} + + +/* -------------------------------------------------------------------------- + Device Enumeration + -------------------------------------------------------------------------- */ + +static int +acpi_bus_get_flags ( + struct acpi_device *device) +{ + acpi_status status = AE_OK; + acpi_handle temp = NULL; + + ACPI_FUNCTION_TRACE("acpi_bus_get_flags"); + + /* Presence of _STA indicates 'dynamic_status' */ + status = acpi_get_handle(device->handle, "_STA", &temp); + if (ACPI_SUCCESS(status)) + device->flags.dynamic_status = 1; + + /* Presence of _CID indicates 'compatible_ids' */ + status = acpi_get_handle(device->handle, "_CID", &temp); + if (ACPI_SUCCESS(status)) + device->flags.compatible_ids = 1; + + /* Presence of _RMV indicates 'removable' */ + status = acpi_get_handle(device->handle, "_RMV", &temp); + if (ACPI_SUCCESS(status)) + device->flags.removable = 1; + + /* Presence of _EJD|_EJ0 indicates 'ejectable' */ + status = acpi_get_handle(device->handle, "_EJD", &temp); + if (ACPI_SUCCESS(status)) + device->flags.ejectable = 1; + else { + status = acpi_get_handle(device->handle, "_EJ0", &temp); + if (ACPI_SUCCESS(status)) + device->flags.ejectable = 1; + } + + /* Presence of _LCK indicates 'lockable' */ + status = acpi_get_handle(device->handle, "_LCK", &temp); + if (ACPI_SUCCESS(status)) + device->flags.lockable = 1; + + /* Presence of _PS0|_PR0 indicates 'power manageable' */ + status = acpi_get_handle(device->handle, "_PS0", &temp); + if (ACPI_FAILURE(status)) + status = acpi_get_handle(device->handle, "_PR0", &temp); + if (ACPI_SUCCESS(status)) + device->flags.power_manageable = 1; + + /* Presence of _PRW indicates wake capable */ + status = acpi_get_handle(device->handle, "_PRW", &temp); + if (ACPI_SUCCESS(status)) + device->flags.wake_capable = 1; + + /* TBD: Peformance management */ + + return_VALUE(0); +} + +static void acpi_device_get_busid(struct acpi_device * device, acpi_handle handle, int type) +{ + char bus_id[5] = {'?',0}; + struct acpi_buffer buffer = {sizeof(bus_id), bus_id}; + int i = 0; + + /* + * Bus ID + * ------ + * The device's Bus ID is simply the object name. + * TBD: Shouldn't this value be unique (within the ACPI namespace)? + */ + switch (type) { + case ACPI_BUS_TYPE_SYSTEM: + strcpy(device->pnp.bus_id, "ACPI"); + break; + case ACPI_BUS_TYPE_POWER_BUTTON: + strcpy(device->pnp.bus_id, "PWRF"); + break; + case ACPI_BUS_TYPE_SLEEP_BUTTON: + strcpy(device->pnp.bus_id, "SLPF"); + break; + default: + acpi_get_name(handle, ACPI_SINGLE_NAME, &buffer); + /* Clean up trailing underscores (if any) */ + for (i = 3; i > 1; i--) { + if (bus_id[i] == '_') + bus_id[i] = '\0'; + else + break; + } + strcpy(device->pnp.bus_id, bus_id); + break; + } +} + +static void acpi_device_set_id(struct acpi_device * device, struct acpi_device * parent, + acpi_handle handle, int type) +{ + struct acpi_device_info *info; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + char *hid = NULL; + char *uid = NULL; + struct acpi_compatible_id_list *cid_list = NULL; + acpi_status status; + + switch (type) { + case ACPI_BUS_TYPE_DEVICE: + status = acpi_get_object_info(handle, &buffer); + if (ACPI_FAILURE(status)) { + printk("%s: Error reading device info\n",__FUNCTION__); + return; + } + + info = buffer.pointer; + if (info->valid & ACPI_VALID_HID) + hid = info->hardware_id.value; + if (info->valid & ACPI_VALID_UID) + uid = info->unique_id.value; + if (info->valid & ACPI_VALID_CID) + cid_list = &info->compatibility_id; + if (info->valid & ACPI_VALID_ADR) { + device->pnp.bus_address = info->address; + device->flags.bus_address = 1; + } + break; + case ACPI_BUS_TYPE_POWER: + hid = ACPI_POWER_HID; + break; + case ACPI_BUS_TYPE_PROCESSOR: + hid = ACPI_PROCESSOR_HID; + break; + case ACPI_BUS_TYPE_SYSTEM: + hid = ACPI_SYSTEM_HID; + break; + case ACPI_BUS_TYPE_THERMAL: + hid = ACPI_THERMAL_HID; + break; + case ACPI_BUS_TYPE_POWER_BUTTON: + hid = ACPI_BUTTON_HID_POWERF; + break; + case ACPI_BUS_TYPE_SLEEP_BUTTON: + hid = ACPI_BUTTON_HID_SLEEPF; + break; + } + + /* + * \_SB + * ---- + * Fix for the system root bus device -- the only root-level device. + */ + if ((parent == ACPI_ROOT_OBJECT) && (type == ACPI_BUS_TYPE_DEVICE)) { + hid = ACPI_BUS_HID; + strcpy(device->pnp.device_name, ACPI_BUS_DEVICE_NAME); + strcpy(device->pnp.device_class, ACPI_BUS_CLASS); + } + + if (hid) { + strcpy(device->pnp.hardware_id, hid); + device->flags.hardware_id = 1; + } + if (uid) { + strcpy(device->pnp.unique_id, uid); + device->flags.unique_id = 1; + } + if (cid_list) { + device->pnp.cid_list = kmalloc(cid_list->size, GFP_KERNEL); + if (device->pnp.cid_list) + memcpy(device->pnp.cid_list, cid_list, cid_list->size); + else + printk(KERN_ERR "Memory allocation error\n"); + } + + acpi_os_free(buffer.pointer); +} + +static int acpi_device_set_context(struct acpi_device * device, int type) +{ + acpi_status status = AE_OK; + int result = 0; + /* + * Context + * ------- + * Attach this 'struct acpi_device' to the ACPI object. This makes + * resolutions from handle->device very efficient. Note that we need + * to be careful with fixed-feature devices as they all attach to the + * root object. + */ + if (type != ACPI_BUS_TYPE_POWER_BUTTON && + type != ACPI_BUS_TYPE_SLEEP_BUTTON) { + status = acpi_attach_data(device->handle, + acpi_bus_data_handler, device); + + if (ACPI_FAILURE(status)) { + printk("Error attaching device data\n"); + result = -ENODEV; + } + } + return result; +} + +static void acpi_device_get_debug_info(struct acpi_device * device, acpi_handle handle, int type) +{ +#ifdef CONFIG_ACPI_DEBUG_OUTPUT + char *type_string = NULL; + char name[80] = {'?','\0'}; + struct acpi_buffer buffer = {sizeof(name), name}; + + switch (type) { + case ACPI_BUS_TYPE_DEVICE: + type_string = "Device"; + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + break; + case ACPI_BUS_TYPE_POWER: + type_string = "Power Resource"; + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + break; + case ACPI_BUS_TYPE_PROCESSOR: + type_string = "Processor"; + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + break; + case ACPI_BUS_TYPE_SYSTEM: + type_string = "System"; + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + break; + case ACPI_BUS_TYPE_THERMAL: + type_string = "Thermal Zone"; + acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + break; + case ACPI_BUS_TYPE_POWER_BUTTON: + type_string = "Power Button"; + sprintf(name, "PWRB"); + break; + case ACPI_BUS_TYPE_SLEEP_BUTTON: + type_string = "Sleep Button"; + sprintf(name, "SLPB"); + break; + } + + printk(KERN_DEBUG "Found %s %s [%p]\n", type_string, name, handle); +#endif /*CONFIG_ACPI_DEBUG_OUTPUT*/ +} + + +static int +acpi_bus_remove ( + struct acpi_device *dev, + int rmdevice) +{ + int result = 0; + struct acpi_driver *driver; + + ACPI_FUNCTION_TRACE("acpi_bus_remove"); + + if (!dev) + return_VALUE(-EINVAL); + + driver = dev->driver; + + if ((driver) && (driver->ops.remove)) { + + if (driver->ops.stop) { + result = driver->ops.stop(dev, ACPI_BUS_REMOVAL_EJECT); + if (result) + return_VALUE(result); + } + + result = dev->driver->ops.remove(dev, ACPI_BUS_REMOVAL_EJECT); + if (result) { + return_VALUE(result); + } + + atomic_dec(&dev->driver->references); + dev->driver = NULL; + acpi_driver_data(dev) = NULL; + } + + if (!rmdevice) + return_VALUE(0); + + if (dev->flags.bus_address) { + if ((dev->parent) && (dev->parent->ops.unbind)) + dev->parent->ops.unbind(dev); + } + + acpi_device_unregister(dev, ACPI_BUS_REMOVAL_EJECT); + + return_VALUE(0); +} + + +int +acpi_bus_add ( + struct acpi_device **child, + struct acpi_device *parent, + acpi_handle handle, + int type) +{ + int result = 0; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_bus_add"); + + if (!child) + return_VALUE(-EINVAL); + + device = kmalloc(sizeof(struct acpi_device), GFP_KERNEL); + if (!device) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Memory allocation error\n")); + return_VALUE(-ENOMEM); + } + memset(device, 0, sizeof(struct acpi_device)); + + device->handle = handle; + device->parent = parent; + + acpi_device_get_busid(device,handle,type); + + /* + * Flags + * ----- + * Get prior to calling acpi_bus_get_status() so we know whether + * or not _STA is present. Note that we only look for object + * handles -- cannot evaluate objects until we know the device is + * present and properly initialized. + */ + result = acpi_bus_get_flags(device); + if (result) + goto end; + + /* + * Status + * ------ + * See if the device is present. We always assume that non-Device() + * objects (e.g. thermal zones, power resources, processors, etc.) are + * present, functioning, etc. (at least when parent object is present). + * Note that _STA has a different meaning for some objects (e.g. + * power resources) so we need to be careful how we use it. + */ + switch (type) { + case ACPI_BUS_TYPE_DEVICE: + result = acpi_bus_get_status(device); + if (ACPI_FAILURE(result) || !device->status.present) { + result = -ENOENT; + goto end; + } + break; + default: + STRUCT_TO_INT(device->status) = 0x0F; + break; + } + + /* + * Initialize Device + * ----------------- + * TBD: Synch with Core's enumeration/initialization process. + */ + + /* + * Hardware ID, Unique ID, & Bus Address + * ------------------------------------- + */ + acpi_device_set_id(device,parent,handle,type); + + /* + * Power Management + * ---------------- + */ + if (device->flags.power_manageable) { + result = acpi_bus_get_power_flags(device); + if (result) + goto end; + } + + /* + * Wakeup device management + *----------------------- + */ + if (device->flags.wake_capable) { + result = acpi_bus_get_wakeup_device_flags(device); + if (result) + goto end; + } + + /* + * Performance Management + * ---------------------- + */ + if (device->flags.performance_manageable) { + result = acpi_bus_get_perf_flags(device); + if (result) + goto end; + } + + if ((result = acpi_device_set_context(device,type))) + goto end; + + acpi_device_get_debug_info(device,handle,type); + + acpi_device_register(device,parent); + + /* + * Bind _ADR-Based Devices + * ----------------------- + * If there's a a bus address (_ADR) then we utilize the parent's + * 'bind' function (if exists) to bind the ACPI- and natively- + * enumerated device representations. + */ + if (device->flags.bus_address) { + if (device->parent && device->parent->ops.bind) + device->parent->ops.bind(device); + } + + /* + * Locate & Attach Driver + * ---------------------- + * If there's a hardware id (_HID) or compatible ids (_CID) we check + * to see if there's a driver installed for this kind of device. Note + * that drivers can install before or after a device is enumerated. + * + * TBD: Assumes LDM provides driver hot-plug capability. + */ + acpi_bus_find_driver(device); + +end: + if (!result) + *child = device; + else { + if (device->pnp.cid_list) + kfree(device->pnp.cid_list); + kfree(device); + } + + return_VALUE(result); +} +EXPORT_SYMBOL(acpi_bus_add); + + +int acpi_bus_scan (struct acpi_device *start) +{ + acpi_status status = AE_OK; + struct acpi_device *parent = NULL; + struct acpi_device *child = NULL; + acpi_handle phandle = NULL; + acpi_handle chandle = NULL; + acpi_object_type type = 0; + u32 level = 1; + + ACPI_FUNCTION_TRACE("acpi_bus_scan"); + + if (!start) + return_VALUE(-EINVAL); + + parent = start; + phandle = start->handle; + + /* + * Parse through the ACPI namespace, identify all 'devices', and + * create a new 'struct acpi_device' for each. + */ + while ((level > 0) && parent) { + + status = acpi_get_next_object(ACPI_TYPE_ANY, phandle, + chandle, &chandle); + + /* + * If this scope is exhausted then move our way back up. + */ + if (ACPI_FAILURE(status)) { + level--; + chandle = phandle; + acpi_get_parent(phandle, &phandle); + if (parent->parent) + parent = parent->parent; + continue; + } + + status = acpi_get_type(chandle, &type); + if (ACPI_FAILURE(status)) + continue; + + /* + * If this is a scope object then parse it (depth-first). + */ + if (type == ACPI_TYPE_LOCAL_SCOPE) { + level++; + phandle = chandle; + chandle = NULL; + continue; + } + + /* + * We're only interested in objects that we consider 'devices'. + */ + switch (type) { + case ACPI_TYPE_DEVICE: + type = ACPI_BUS_TYPE_DEVICE; + break; + case ACPI_TYPE_PROCESSOR: + type = ACPI_BUS_TYPE_PROCESSOR; + break; + case ACPI_TYPE_THERMAL: + type = ACPI_BUS_TYPE_THERMAL; + break; + case ACPI_TYPE_POWER: + type = ACPI_BUS_TYPE_POWER; + break; + default: + continue; + } + + status = acpi_bus_add(&child, parent, chandle, type); + if (ACPI_FAILURE(status)) + continue; + + /* + * If the device is present, enabled, and functioning then + * parse its scope (depth-first). Note that we need to + * represent absent devices to facilitate PnP notifications + * -- but only the subtree head (not all of its children, + * which will be enumerated when the parent is inserted). + * + * TBD: Need notifications and other detection mechanisms + * in place before we can fully implement this. + */ + if (child->status.present) { + status = acpi_get_next_object(ACPI_TYPE_ANY, chandle, + NULL, NULL); + if (ACPI_SUCCESS(status)) { + level++; + phandle = chandle; + chandle = NULL; + parent = child; + } + } + } + + return_VALUE(0); +} +EXPORT_SYMBOL(acpi_bus_scan); + + +static int +acpi_bus_trim(struct acpi_device *start, + int rmdevice) +{ + acpi_status status; + struct acpi_device *parent, *child; + acpi_handle phandle, chandle; + acpi_object_type type; + u32 level = 1; + int err = 0; + + parent = start; + phandle = start->handle; + child = chandle = NULL; + + while ((level > 0) && parent && (!err)) { + status = acpi_get_next_object(ACPI_TYPE_ANY, phandle, + chandle, &chandle); + + /* + * If this scope is exhausted then move our way back up. + */ + if (ACPI_FAILURE(status)) { + level--; + chandle = phandle; + acpi_get_parent(phandle, &phandle); + child = parent; + parent = parent->parent; + + if (level == 0) + err = acpi_bus_remove(child, rmdevice); + else + err = acpi_bus_remove(child, 1); + + continue; + } + + status = acpi_get_type(chandle, &type); + if (ACPI_FAILURE(status)) { + continue; + } + /* + * If there is a device corresponding to chandle then + * parse it (depth-first). + */ + if (acpi_bus_get_device(chandle, &child) == 0) { + level++; + phandle = chandle; + chandle = NULL; + parent = child; + } + continue; + } + return err; +} + +static int +acpi_bus_scan_fixed ( + struct acpi_device *root) +{ + int result = 0; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_bus_scan_fixed"); + + if (!root) + return_VALUE(-ENODEV); + + /* + * Enumerate all fixed-feature devices. + */ + if (acpi_fadt.pwr_button == 0) + result = acpi_bus_add(&device, acpi_root, + NULL, ACPI_BUS_TYPE_POWER_BUTTON); + + if (acpi_fadt.sleep_button == 0) + result = acpi_bus_add(&device, acpi_root, + NULL, ACPI_BUS_TYPE_SLEEP_BUTTON); + + return_VALUE(result); +} + + +static int __init acpi_scan_init(void) +{ + int result; + + ACPI_FUNCTION_TRACE("acpi_scan_init"); + + if (acpi_disabled) + return_VALUE(0); + + kset_register(&acpi_namespace_kset); + + /* + * Create the root device in the bus's device tree + */ + result = acpi_bus_add(&acpi_root, NULL, ACPI_ROOT_OBJECT, + ACPI_BUS_TYPE_SYSTEM); + if (result) + goto Done; + + /* + * Enumerate devices in the ACPI namespace. + */ + result = acpi_bus_scan_fixed(acpi_root); + if (!result) + result = acpi_bus_scan(acpi_root); + + if (result) + acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); + + Done: + return_VALUE(result); +} + +subsys_initcall(acpi_scan_init); diff --git a/drivers/acpi/sleep/Makefile b/drivers/acpi/sleep/Makefile new file mode 100644 index 00000000000..d6c017709c8 --- /dev/null +++ b/drivers/acpi/sleep/Makefile @@ -0,0 +1,5 @@ +obj-y := poweroff.o wakeup.o +obj-$(CONFIG_ACPI_SLEEP) += main.o +obj-$(CONFIG_ACPI_SLEEP_PROC_FS) += proc.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c new file mode 100644 index 00000000000..0a5d2a94131 --- /dev/null +++ b/drivers/acpi/sleep/main.c @@ -0,0 +1,234 @@ +/* + * sleep.c - ACPI sleep support. + * + * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com> + * Copyright (c) 2000-2003 Patrick Mochel + * Copyright (c) 2003 Open Source Development Lab + * + * This file is released under the GPLv2. + * + */ + +#include <linux/delay.h> +#include <linux/irq.h> +#include <linux/dmi.h> +#include <linux/device.h> +#include <linux/suspend.h> +#include <asm/io.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> +#include "sleep.h" + +u8 sleep_states[ACPI_S_STATE_COUNT]; + +static struct pm_ops acpi_pm_ops; + +extern void do_suspend_lowlevel_s4bios(void); +extern void do_suspend_lowlevel(void); + +static u32 acpi_suspend_states[] = { + [PM_SUSPEND_ON] = ACPI_STATE_S0, + [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, + [PM_SUSPEND_MEM] = ACPI_STATE_S3, + [PM_SUSPEND_DISK] = ACPI_STATE_S4, +}; + +static int init_8259A_after_S1; + +/** + * acpi_pm_prepare - Do preliminary suspend work. + * @pm_state: suspend state we're entering. + * + * Make sure we support the state. If we do, and we need it, set the + * firmware waking vector and do arch-specific nastiness to get the + * wakeup code to the waking vector. + */ + +static int acpi_pm_prepare(suspend_state_t pm_state) +{ + u32 acpi_state = acpi_suspend_states[pm_state]; + + if (!sleep_states[acpi_state]) + return -EPERM; + + /* do we have a wakeup address for S2 and S3? */ + /* Here, we support only S4BIOS, those we set the wakeup address */ + /* S4OS is only supported for now via swsusp.. */ + if (pm_state == PM_SUSPEND_MEM || pm_state == PM_SUSPEND_DISK) { + if (!acpi_wakeup_address) + return -EFAULT; + acpi_set_firmware_waking_vector( + (acpi_physical_address) virt_to_phys( + (void *)acpi_wakeup_address)); + } + ACPI_FLUSH_CPU_CACHE(); + acpi_enable_wakeup_device_prep(acpi_state); + acpi_enter_sleep_state_prep(acpi_state); + return 0; +} + + +/** + * acpi_pm_enter - Actually enter a sleep state. + * @pm_state: State we're entering. + * + * Flush caches and go to sleep. For STR or STD, we have to call + * arch-specific assembly, which in turn call acpi_enter_sleep_state(). + * It's unfortunate, but it works. Please fix if you're feeling frisky. + */ + +static int acpi_pm_enter(suspend_state_t pm_state) +{ + acpi_status status = AE_OK; + unsigned long flags = 0; + u32 acpi_state = acpi_suspend_states[pm_state]; + + ACPI_FLUSH_CPU_CACHE(); + + /* Do arch specific saving of state. */ + if (pm_state > PM_SUSPEND_STANDBY) { + int error = acpi_save_state_mem(); + if (error) + return error; + } + + + local_irq_save(flags); + acpi_enable_wakeup_device(acpi_state); + switch (pm_state) + { + case PM_SUSPEND_STANDBY: + barrier(); + status = acpi_enter_sleep_state(acpi_state); + break; + + case PM_SUSPEND_MEM: + do_suspend_lowlevel(); + break; + + case PM_SUSPEND_DISK: + if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM) + status = acpi_enter_sleep_state(acpi_state); + else + do_suspend_lowlevel_s4bios(); + break; + default: + return -EINVAL; + } + local_irq_restore(flags); + printk(KERN_DEBUG "Back to C!\n"); + + /* restore processor state + * We should only be here if we're coming back from STR or STD. + * And, in the case of the latter, the memory image should have already + * been loaded from disk. + */ + if (pm_state > PM_SUSPEND_STANDBY) + acpi_restore_state_mem(); + + + return ACPI_SUCCESS(status) ? 0 : -EFAULT; +} + + +/** + * acpi_pm_finish - Finish up suspend sequence. + * @pm_state: State we're coming out of. + * + * This is called after we wake back up (or if entering the sleep state + * failed). + */ + +static int acpi_pm_finish(suspend_state_t pm_state) +{ + u32 acpi_state = acpi_suspend_states[pm_state]; + + acpi_leave_sleep_state(acpi_state); + acpi_disable_wakeup_device(acpi_state); + + /* reset firmware waking vector */ + acpi_set_firmware_waking_vector((acpi_physical_address) 0); + + if (init_8259A_after_S1) { + printk("Broken toshiba laptop -> kicking interrupts\n"); + init_8259A(0); + } + return 0; +} + + +int acpi_suspend(u32 acpi_state) +{ + suspend_state_t states[] = { + [1] = PM_SUSPEND_STANDBY, + [3] = PM_SUSPEND_MEM, + [4] = PM_SUSPEND_DISK, + }; + + if (acpi_state <= 4 && states[acpi_state]) + return pm_suspend(states[acpi_state]); + return -EINVAL; +} + +static struct pm_ops acpi_pm_ops = { + .prepare = acpi_pm_prepare, + .enter = acpi_pm_enter, + .finish = acpi_pm_finish, +}; + + +/* + * Toshiba fails to preserve interrupts over S1, reinitialization + * of 8259 is needed after S1 resume. + */ +static int __init init_ints_after_s1(struct dmi_system_id *d) +{ + printk(KERN_WARNING "%s with broken S1 detected.\n", d->ident); + init_8259A_after_S1 = 1; + return 0; +} + +static struct dmi_system_id __initdata acpisleep_dmi_table[] = { + { + .callback = init_ints_after_s1, + .ident = "Toshiba Satellite 4030cdt", + .matches = { DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"), }, + }, + { }, +}; + +static int __init acpi_sleep_init(void) +{ + int i = 0; + + dmi_check_system(acpisleep_dmi_table); + + if (acpi_disabled) + return 0; + + printk(KERN_INFO PREFIX "(supports"); + for (i=0; i < ACPI_S_STATE_COUNT; i++) { + acpi_status status; + u8 type_a, type_b; + status = acpi_get_sleep_type_data(i, &type_a, &type_b); + if (ACPI_SUCCESS(status)) { + sleep_states[i] = 1; + printk(" S%d", i); + } + if (i == ACPI_STATE_S4) { + if (acpi_gbl_FACS->S4bios_f) { + sleep_states[i] = 1; + printk(" S4bios"); + acpi_pm_ops.pm_disk_mode = PM_DISK_FIRMWARE; + } + if (sleep_states[i]) + acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM; + } + } + printk(")\n"); + + pm_set_ops(&acpi_pm_ops); + return 0; +} + +late_initcall(acpi_sleep_init); diff --git a/drivers/acpi/sleep/poweroff.c b/drivers/acpi/sleep/poweroff.c new file mode 100644 index 00000000000..da237754ded --- /dev/null +++ b/drivers/acpi/sleep/poweroff.c @@ -0,0 +1,39 @@ +/* + * poweroff.c - ACPI handler for powering off the system. + * + * AKA S5, but it is independent of whether or not the kernel supports + * any other sleep support in the system. + */ + +#include <linux/pm.h> +#include <linux/init.h> +#include <acpi/acpi_bus.h> +#include <linux/sched.h> +#include "sleep.h" + +static void +acpi_power_off (void) +{ + printk("%s called\n",__FUNCTION__); + /* Some SMP machines only can poweroff in boot CPU */ + set_cpus_allowed(current, cpumask_of_cpu(0)); + acpi_wakeup_gpe_poweroff_prepare(); + acpi_enter_sleep_state_prep(ACPI_STATE_S5); + ACPI_DISABLE_IRQS(); + acpi_enter_sleep_state(ACPI_STATE_S5); +} + +static int acpi_poweroff_init(void) +{ + if (!acpi_disabled) { + u8 type_a, type_b; + acpi_status status; + + status = acpi_get_sleep_type_data(ACPI_STATE_S5, &type_a, &type_b); + if (ACPI_SUCCESS(status)) + pm_power_off = acpi_power_off; + } + return 0; +} + +late_initcall(acpi_poweroff_init); diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c new file mode 100644 index 00000000000..fd7c5a0649a --- /dev/null +++ b/drivers/acpi/sleep/proc.c @@ -0,0 +1,509 @@ +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/suspend.h> +#include <linux/bcd.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +#ifdef CONFIG_X86 +#include <linux/mc146818rtc.h> +#endif + +#include "sleep.h" + +#define ACPI_SYSTEM_FILE_SLEEP "sleep" +#define ACPI_SYSTEM_FILE_ALARM "alarm" +#define ACPI_SYSTEM_FILE_WAKEUP_DEVICE "wakeup" + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME ("sleep") + + +static int acpi_system_sleep_seq_show(struct seq_file *seq, void *offset) +{ + int i; + + ACPI_FUNCTION_TRACE("acpi_system_sleep_seq_show"); + + for (i = 0; i <= ACPI_STATE_S5; i++) { + if (sleep_states[i]) { + seq_printf(seq,"S%d ", i); + if (i == ACPI_STATE_S4 && acpi_gbl_FACS->S4bios_f) + seq_printf(seq, "S4bios "); + } + } + + seq_puts(seq, "\n"); + + return 0; +} + +static int acpi_system_sleep_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_system_sleep_seq_show, PDE(inode)->data); +} + +static ssize_t +acpi_system_write_sleep ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *ppos) +{ + char str[12]; + u32 state = 0; + int error = 0; + + if (count > sizeof(str) - 1) + goto Done; + memset(str,0,sizeof(str)); + if (copy_from_user(str, buffer, count)) + return -EFAULT; + + /* Check for S4 bios request */ + if (!strcmp(str,"4b")) { + error = acpi_suspend(4); + goto Done; + } + state = simple_strtoul(str, NULL, 0); +#ifdef CONFIG_SOFTWARE_SUSPEND + if (state == 4) { + error = software_suspend(); + goto Done; + } +#endif + error = acpi_suspend(state); + Done: + return error ? error : count; +} + +static int acpi_system_alarm_seq_show(struct seq_file *seq, void *offset) +{ + u32 sec, min, hr; + u32 day, mo, yr; + unsigned char rtc_control = 0; + unsigned long flags; + + ACPI_FUNCTION_TRACE("acpi_system_alarm_seq_show"); + + spin_lock_irqsave(&rtc_lock, flags); + + sec = CMOS_READ(RTC_SECONDS_ALARM); + min = CMOS_READ(RTC_MINUTES_ALARM); + hr = CMOS_READ(RTC_HOURS_ALARM); + rtc_control = CMOS_READ(RTC_CONTROL); + + /* If we ever get an FACP with proper values... */ + if (acpi_gbl_FADT->day_alrm) + /* ACPI spec: only low 6 its should be cared */ + day = CMOS_READ(acpi_gbl_FADT->day_alrm) & 0x3F; + else + day = CMOS_READ(RTC_DAY_OF_MONTH); + if (acpi_gbl_FADT->mon_alrm) + mo = CMOS_READ(acpi_gbl_FADT->mon_alrm); + else + mo = CMOS_READ(RTC_MONTH); + if (acpi_gbl_FADT->century) + yr = CMOS_READ(acpi_gbl_FADT->century) * 100 + CMOS_READ(RTC_YEAR); + else + yr = CMOS_READ(RTC_YEAR); + + spin_unlock_irqrestore(&rtc_lock, flags); + + if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BCD_TO_BIN(sec); + BCD_TO_BIN(min); + BCD_TO_BIN(hr); + BCD_TO_BIN(day); + BCD_TO_BIN(mo); + BCD_TO_BIN(yr); + } + + /* we're trusting the FADT (see above)*/ + if (!acpi_gbl_FADT->century) + /* If we're not trusting the FADT, we should at least make it + * right for _this_ century... ehm, what is _this_ century? + * + * TBD: + * ASAP: find piece of code in the kernel, e.g. star tracker driver, + * which we can trust to determine the century correctly. Atom + * watch driver would be nice, too... + * + * if that has not happened, change for first release in 2050: + * if (yr<50) + * yr += 2100; + * else + * yr += 2000; // current line of code + * + * if that has not happened either, please do on 2099/12/31:23:59:59 + * s/2000/2100 + * + */ + yr += 2000; + + seq_printf(seq,"%4.4u-", yr); + (mo > 12) ? seq_puts(seq, "**-") : seq_printf(seq, "%2.2u-", mo); + (day > 31) ? seq_puts(seq, "** ") : seq_printf(seq, "%2.2u ", day); + (hr > 23) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", hr); + (min > 59) ? seq_puts(seq, "**:") : seq_printf(seq, "%2.2u:", min); + (sec > 59) ? seq_puts(seq, "**\n") : seq_printf(seq, "%2.2u\n", sec); + + return 0; +} + +static int acpi_system_alarm_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_system_alarm_seq_show, PDE(inode)->data); +} + + +static int +get_date_field ( + char **p, + u32 *value) +{ + char *next = NULL; + char *string_end = NULL; + int result = -EINVAL; + + /* + * Try to find delimeter, only to insert null. The end of the + * string won't have one, but is still valid. + */ + next = strpbrk(*p, "- :"); + if (next) + *next++ = '\0'; + + *value = simple_strtoul(*p, &string_end, 10); + + /* Signal success if we got a good digit */ + if (string_end != *p) + result = 0; + + if (next) + *p = next; + + return result; +} + + +static ssize_t +acpi_system_write_alarm ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *ppos) +{ + int result = 0; + char alarm_string[30] = {'\0'}; + char *p = alarm_string; + u32 sec, min, hr, day, mo, yr; + int adjust = 0; + unsigned char rtc_control = 0; + + ACPI_FUNCTION_TRACE("acpi_system_write_alarm"); + + if (count > sizeof(alarm_string) - 1) + return_VALUE(-EINVAL); + + if (copy_from_user(alarm_string, buffer, count)) + return_VALUE(-EFAULT); + + alarm_string[count] = '\0'; + + /* check for time adjustment */ + if (alarm_string[0] == '+') { + p++; + adjust = 1; + } + + if ((result = get_date_field(&p, &yr))) + goto end; + if ((result = get_date_field(&p, &mo))) + goto end; + if ((result = get_date_field(&p, &day))) + goto end; + if ((result = get_date_field(&p, &hr))) + goto end; + if ((result = get_date_field(&p, &min))) + goto end; + if ((result = get_date_field(&p, &sec))) + goto end; + + if (sec > 59) { + min += 1; + sec -= 60; + } + if (min > 59) { + hr += 1; + min -= 60; + } + if (hr > 23) { + day += 1; + hr -= 24; + } + if (day > 31) { + mo += 1; + day -= 31; + } + if (mo > 12) { + yr += 1; + mo -= 12; + } + + spin_lock_irq(&rtc_lock); + + rtc_control = CMOS_READ(RTC_CONTROL); + if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(yr); + BIN_TO_BCD(mo); + BIN_TO_BCD(day); + BIN_TO_BCD(hr); + BIN_TO_BCD(min); + BIN_TO_BCD(sec); + } + + if (adjust) { + yr += CMOS_READ(RTC_YEAR); + mo += CMOS_READ(RTC_MONTH); + day += CMOS_READ(RTC_DAY_OF_MONTH); + hr += CMOS_READ(RTC_HOURS); + min += CMOS_READ(RTC_MINUTES); + sec += CMOS_READ(RTC_SECONDS); + } + + spin_unlock_irq(&rtc_lock); + + if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BCD_TO_BIN(yr); + BCD_TO_BIN(mo); + BCD_TO_BIN(day); + BCD_TO_BIN(hr); + BCD_TO_BIN(min); + BCD_TO_BIN(sec); + } + + if (sec > 59) { + min++; + sec -= 60; + } + if (min > 59) { + hr++; + min -= 60; + } + if (hr > 23) { + day++; + hr -= 24; + } + if (day > 31) { + mo++; + day -= 31; + } + if (mo > 12) { + yr++; + mo -= 12; + } + if (!(rtc_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) { + BIN_TO_BCD(yr); + BIN_TO_BCD(mo); + BIN_TO_BCD(day); + BIN_TO_BCD(hr); + BIN_TO_BCD(min); + BIN_TO_BCD(sec); + } + + spin_lock_irq(&rtc_lock); + /* + * Disable alarm interrupt before setting alarm timer or else + * when ACPI_EVENT_RTC is enabled, a spurious ACPI interrupt occurs + */ + rtc_control &= ~RTC_AIE; + CMOS_WRITE(rtc_control, RTC_CONTROL); + CMOS_READ(RTC_INTR_FLAGS); + + /* write the fields the rtc knows about */ + CMOS_WRITE(hr, RTC_HOURS_ALARM); + CMOS_WRITE(min, RTC_MINUTES_ALARM); + CMOS_WRITE(sec, RTC_SECONDS_ALARM); + + /* + * If the system supports an enhanced alarm it will have non-zero + * offsets into the CMOS RAM here -- which for some reason are pointing + * to the RTC area of memory. + */ + if (acpi_gbl_FADT->day_alrm) + CMOS_WRITE(day, acpi_gbl_FADT->day_alrm); + if (acpi_gbl_FADT->mon_alrm) + CMOS_WRITE(mo, acpi_gbl_FADT->mon_alrm); + if (acpi_gbl_FADT->century) + CMOS_WRITE(yr/100, acpi_gbl_FADT->century); + /* enable the rtc alarm interrupt */ + rtc_control |= RTC_AIE; + CMOS_WRITE(rtc_control, RTC_CONTROL); + CMOS_READ(RTC_INTR_FLAGS); + + spin_unlock_irq(&rtc_lock); + + acpi_clear_event(ACPI_EVENT_RTC); + acpi_enable_event(ACPI_EVENT_RTC, 0); + + *ppos += count; + + result = 0; +end: + return_VALUE(result ? result : count); +} + +extern struct list_head acpi_wakeup_device_list; +extern spinlock_t acpi_device_lock; + +static int +acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) +{ + struct list_head * node, * next; + + seq_printf(seq, "Device Sleep state Status\n"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list); + + if (!dev->wakeup.flags.valid) + continue; + spin_unlock(&acpi_device_lock); + if (dev->wakeup.flags.run_wake) + seq_printf(seq, "%4s %4d %8s\n", + dev->pnp.bus_id, (u32) dev->wakeup.sleep_state, + dev->wakeup.state.enabled ? "*enabled" : "*disabled"); + else + seq_printf(seq, "%4s %4d %8s\n", + dev->pnp.bus_id, (u32) dev->wakeup.sleep_state, + dev->wakeup.state.enabled ? "enabled" : "disabled"); + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); + return 0; +} + +static ssize_t +acpi_system_write_wakeup_device ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *ppos) +{ + struct list_head * node, * next; + char strbuf[5]; + char str[5] = ""; + int len = count; + struct acpi_device *found_dev = NULL; + + if (len > 4) len = 4; + + if (copy_from_user(strbuf, buffer, len)) + return -EFAULT; + strbuf[len] = '\0'; + sscanf(strbuf, "%s", str); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, struct acpi_device, wakeup_list); + if (!dev->wakeup.flags.valid) + continue; + + if (!strncmp(dev->pnp.bus_id, str, 4)) { + dev->wakeup.state.enabled = dev->wakeup.state.enabled ? 0:1; + found_dev = dev; + break; + } + } + if (found_dev) { + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + if ((dev != found_dev) && + (dev->wakeup.gpe_number == found_dev->wakeup.gpe_number) && + (dev->wakeup.gpe_device == found_dev->wakeup.gpe_device)) { + printk(KERN_WARNING "ACPI: '%s' and '%s' have the same GPE, " + "can't disable/enable one seperately\n", + dev->pnp.bus_id, found_dev->pnp.bus_id); + dev->wakeup.state.enabled = found_dev->wakeup.state.enabled; + } + } + } + spin_unlock(&acpi_device_lock); + return count; +} + +static int +acpi_system_wakeup_device_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_system_wakeup_device_seq_show, PDE(inode)->data); +} + +static struct file_operations acpi_system_wakeup_device_fops = { + .open = acpi_system_wakeup_device_open_fs, + .read = seq_read, + .write = acpi_system_write_wakeup_device, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_system_sleep_fops = { + .open = acpi_system_sleep_open_fs, + .read = seq_read, + .write = acpi_system_write_sleep, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_system_alarm_fops = { + .open = acpi_system_alarm_open_fs, + .read = seq_read, + .write = acpi_system_write_alarm, + .llseek = seq_lseek, + .release = single_release, +}; + + +static u32 rtc_handler(void * context) +{ + acpi_clear_event(ACPI_EVENT_RTC); + acpi_disable_event(ACPI_EVENT_RTC, 0); + + return ACPI_INTERRUPT_HANDLED; +} + +static int acpi_sleep_proc_init(void) +{ + struct proc_dir_entry *entry = NULL; + + if (acpi_disabled) + return 0; + + /* 'sleep' [R/W]*/ + entry = create_proc_entry(ACPI_SYSTEM_FILE_SLEEP, + S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); + if (entry) + entry->proc_fops = &acpi_system_sleep_fops; + + /* 'alarm' [R/W] */ + entry = create_proc_entry(ACPI_SYSTEM_FILE_ALARM, + S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); + if (entry) + entry->proc_fops = &acpi_system_alarm_fops; + + /* 'wakeup device' [R/W]*/ + entry = create_proc_entry(ACPI_SYSTEM_FILE_WAKEUP_DEVICE, + S_IFREG|S_IRUGO|S_IWUSR, acpi_root_dir); + if (entry) + entry->proc_fops = &acpi_system_wakeup_device_fops; + + acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL); + return 0; +} + +late_initcall(acpi_sleep_proc_init); diff --git a/drivers/acpi/sleep/sleep.h b/drivers/acpi/sleep/sleep.h new file mode 100644 index 00000000000..efd0001c6f0 --- /dev/null +++ b/drivers/acpi/sleep/sleep.h @@ -0,0 +1,8 @@ + +extern u8 sleep_states[]; +extern int acpi_suspend (u32 state); + +extern void acpi_enable_wakeup_device_prep(u8 sleep_state); +extern void acpi_enable_wakeup_device(u8 sleep_state); +extern void acpi_disable_wakeup_device(u8 sleep_state); +extern void acpi_wakeup_gpe_poweroff_prepare(void); diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/sleep/wakeup.c new file mode 100644 index 00000000000..d9b199969d5 --- /dev/null +++ b/drivers/acpi/sleep/wakeup.c @@ -0,0 +1,209 @@ +/* + * wakeup.c - support wakeup devices + * Copyright (C) 2004 Li Shaohua <shaohua.li@intel.com> + */ + +#include <linux/init.h> +#include <linux/acpi.h> +#include <acpi/acpi_drivers.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <acpi/acevents.h> +#include "sleep.h" + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME ("wakeup_devices") + +extern struct list_head acpi_wakeup_device_list; +extern spinlock_t acpi_device_lock; + +#ifdef CONFIG_ACPI_SLEEP +/** + * acpi_enable_wakeup_device_prep - prepare wakeup devices + * @sleep_state: ACPI state + * Enable all wakup devices power if the devices' wakeup level + * is higher than requested sleep level + */ + +void +acpi_enable_wakeup_device_prep( + u8 sleep_state) +{ + struct list_head * node, * next; + + ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device_prep"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + if (!dev->wakeup.flags.valid || + !dev->wakeup.state.enabled || + (sleep_state > (u32) dev->wakeup.sleep_state)) + continue; + + spin_unlock(&acpi_device_lock); + acpi_enable_wakeup_device_power(dev); + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); +} + +/** + * acpi_enable_wakeup_device - enable wakeup devices + * @sleep_state: ACPI state + * Enable all wakup devices's GPE + */ +void +acpi_enable_wakeup_device( + u8 sleep_state) +{ + struct list_head * node, * next; + + /* + * Caution: this routine must be invoked when interrupt is disabled + * Refer ACPI2.0: P212 + */ + ACPI_FUNCTION_TRACE("acpi_enable_wakeup_device"); + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + /* If users want to disable run-wake GPE, + * we only disable it for wake and leave it for runtime + */ + if (dev->wakeup.flags.run_wake && !dev->wakeup.state.enabled) { + spin_unlock(&acpi_device_lock); + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_GPE_TYPE_RUNTIME); + /* Re-enable it, since set_gpe_type will disable it */ + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_ISR); + spin_lock(&acpi_device_lock); + continue; + } + + if (!dev->wakeup.flags.valid || + !dev->wakeup.state.enabled || + (sleep_state > (u32) dev->wakeup.sleep_state)) + continue; + + spin_unlock(&acpi_device_lock); + /* run-wake GPE has been enabled */ + if (!dev->wakeup.flags.run_wake) + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_ISR); + dev->wakeup.state.active = 1; + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); +} + +/** + * acpi_disable_wakeup_device - disable devices' wakeup capability + * @sleep_state: ACPI state + * Disable all wakup devices's GPE and wakeup capability + */ +void +acpi_disable_wakeup_device ( + u8 sleep_state) +{ + struct list_head * node, * next; + + ACPI_FUNCTION_TRACE("acpi_disable_wakeup_device"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + if (dev->wakeup.flags.run_wake && !dev->wakeup.state.enabled) { + spin_unlock(&acpi_device_lock); + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); + /* Re-enable it, since set_gpe_type will disable it */ + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + spin_lock(&acpi_device_lock); + continue; + } + + if (!dev->wakeup.flags.valid || + !dev->wakeup.state.active || + (sleep_state > (u32) dev->wakeup.sleep_state)) + continue; + + spin_unlock(&acpi_device_lock); + acpi_disable_wakeup_device_power(dev); + /* Never disable run-wake GPE */ + if (!dev->wakeup.flags.run_wake) { + acpi_disable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + acpi_clear_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + } + dev->wakeup.state.active = 0; + spin_lock(&acpi_device_lock); + } + spin_unlock(&acpi_device_lock); +} + +static int __init acpi_wakeup_device_init(void) +{ + struct list_head * node, * next; + + if (acpi_disabled) + return 0; + printk("ACPI wakeup devices: \n"); + + spin_lock(&acpi_device_lock); + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + /* In case user doesn't load button driver */ + if (dev->wakeup.flags.run_wake && !dev->wakeup.state.enabled) { + spin_unlock(&acpi_device_lock); + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); + acpi_enable_gpe(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_NOT_ISR); + dev->wakeup.state.enabled = 1; + spin_lock(&acpi_device_lock); + } + printk("%4s ", dev->pnp.bus_id); + } + spin_unlock(&acpi_device_lock); + printk("\n"); + + return 0; +} + +late_initcall(acpi_wakeup_device_init); +#endif + +/* + * Disable all wakeup GPEs before power off. + * + * Since acpi_enter_sleep_state() will disable all + * RUNTIME GPEs, we simply mark all GPES that + * are not enabled for wakeup from S5 as RUNTIME. + */ +void acpi_wakeup_gpe_poweroff_prepare(void) +{ + struct list_head * node, * next; + + list_for_each_safe(node, next, &acpi_wakeup_device_list) { + struct acpi_device * dev = container_of(node, + struct acpi_device, wakeup_list); + + /* The GPE can wakeup system from S5, don't touch it */ + if ((u32)dev->wakeup.sleep_state == ACPI_STATE_S5) + continue; + /* acpi_set_gpe_type will automatically disable GPE */ + acpi_set_gpe_type(dev->wakeup.gpe_device, + dev->wakeup.gpe_number, ACPI_GPE_TYPE_RUNTIME); + } +} diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c new file mode 100644 index 00000000000..8925a6ca5f8 --- /dev/null +++ b/drivers/acpi/system.c @@ -0,0 +1,187 @@ +/* + * acpi_system.c - ACPI System Driver ($Revision: 63 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/proc_fs.h> +#include <linux/seq_file.h> +#include <linux/init.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_drivers.h> + + +#define _COMPONENT ACPI_SYSTEM_COMPONENT +ACPI_MODULE_NAME ("acpi_system") + +#define ACPI_SYSTEM_CLASS "system" +#define ACPI_SYSTEM_DRIVER_NAME "ACPI System Driver" +#define ACPI_SYSTEM_DEVICE_NAME "System" +#define ACPI_SYSTEM_FILE_INFO "info" +#define ACPI_SYSTEM_FILE_EVENT "event" +#define ACPI_SYSTEM_FILE_DSDT "dsdt" +#define ACPI_SYSTEM_FILE_FADT "fadt" + +extern FADT_DESCRIPTOR acpi_fadt; + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static int +acpi_system_read_info (struct seq_file *seq, void *offset) +{ + ACPI_FUNCTION_TRACE("acpi_system_read_info"); + + seq_printf(seq, "version: %x\n", ACPI_CA_VERSION); + return_VALUE(0); +} + +static int acpi_system_info_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_system_read_info, PDE(inode)->data); +} + +static struct file_operations acpi_system_info_ops = { + .open = acpi_system_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static ssize_t acpi_system_read_dsdt (struct file*, char __user *, size_t, loff_t*); + +static struct file_operations acpi_system_dsdt_ops = { + .read = acpi_system_read_dsdt, +}; + +static ssize_t +acpi_system_read_dsdt ( + struct file *file, + char __user *buffer, + size_t count, + loff_t *ppos) +{ + acpi_status status = AE_OK; + struct acpi_buffer dsdt = {ACPI_ALLOCATE_BUFFER, NULL}; + ssize_t res; + + ACPI_FUNCTION_TRACE("acpi_system_read_dsdt"); + + status = acpi_get_table(ACPI_TABLE_DSDT, 1, &dsdt); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + res = simple_read_from_buffer(buffer, count, ppos, + dsdt.pointer, dsdt.length); + acpi_os_free(dsdt.pointer); + + return_VALUE(res); +} + + +static ssize_t acpi_system_read_fadt (struct file*, char __user *, size_t, loff_t*); + +static struct file_operations acpi_system_fadt_ops = { + .read = acpi_system_read_fadt, +}; + +static ssize_t +acpi_system_read_fadt ( + struct file *file, + char __user *buffer, + size_t count, + loff_t *ppos) +{ + acpi_status status = AE_OK; + struct acpi_buffer fadt = {ACPI_ALLOCATE_BUFFER, NULL}; + ssize_t res; + + ACPI_FUNCTION_TRACE("acpi_system_read_fadt"); + + status = acpi_get_table(ACPI_TABLE_FADT, 1, &fadt); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + res = simple_read_from_buffer(buffer, count, ppos, + fadt.pointer, fadt.length); + acpi_os_free(fadt.pointer); + + return_VALUE(res); +} + + +static int __init acpi_system_init (void) +{ + struct proc_dir_entry *entry; + int error = 0; + char * name; + + ACPI_FUNCTION_TRACE("acpi_system_init"); + + if (acpi_disabled) + return_VALUE(0); + + /* 'info' [R] */ + name = ACPI_SYSTEM_FILE_INFO; + entry = create_proc_entry(name, + S_IRUGO, acpi_root_dir); + if (!entry) + goto Error; + else { + entry->proc_fops = &acpi_system_info_ops; + } + + /* 'dsdt' [R] */ + name = ACPI_SYSTEM_FILE_DSDT; + entry = create_proc_entry(name, S_IRUSR, acpi_root_dir); + if (entry) + entry->proc_fops = &acpi_system_dsdt_ops; + else + goto Error; + + /* 'fadt' [R] */ + name = ACPI_SYSTEM_FILE_FADT; + entry = create_proc_entry(name, S_IRUSR, acpi_root_dir); + if (entry) + entry->proc_fops = &acpi_system_fadt_ops; + else + goto Error; + + Done: + return_VALUE(error); + + Error: + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' proc fs entry\n", name)); + + remove_proc_entry(ACPI_SYSTEM_FILE_FADT, acpi_root_dir); + remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_root_dir); + remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_root_dir); + + error = -EFAULT; + goto Done; +} + + +subsys_initcall(acpi_system_init); diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c new file mode 100644 index 00000000000..fb64bd5d2e1 --- /dev/null +++ b/drivers/acpi/tables.c @@ -0,0 +1,609 @@ +/* + * acpi_tables.c - ACPI Boot-Time Table Parsing + * + * Copyright (C) 2001 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + */ + +#include <linux/config.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/smp.h> +#include <linux/string.h> +#include <linux/types.h> +#include <linux/irq.h> +#include <linux/errno.h> +#include <linux/acpi.h> +#include <linux/bootmem.h> + +#define PREFIX "ACPI: " + +#define ACPI_MAX_TABLES 256 + +static char *acpi_table_signatures[ACPI_TABLE_COUNT] = { + [ACPI_TABLE_UNKNOWN] = "????", + [ACPI_APIC] = "APIC", + [ACPI_BOOT] = "BOOT", + [ACPI_DBGP] = "DBGP", + [ACPI_DSDT] = "DSDT", + [ACPI_ECDT] = "ECDT", + [ACPI_ETDT] = "ETDT", + [ACPI_FADT] = "FACP", + [ACPI_FACS] = "FACS", + [ACPI_OEMX] = "OEM", + [ACPI_PSDT] = "PSDT", + [ACPI_SBST] = "SBST", + [ACPI_SLIT] = "SLIT", + [ACPI_SPCR] = "SPCR", + [ACPI_SRAT] = "SRAT", + [ACPI_SSDT] = "SSDT", + [ACPI_SPMI] = "SPMI", + [ACPI_HPET] = "HPET", + [ACPI_MCFG] = "MCFG", +}; + +static char *mps_inti_flags_polarity[] = { "dfl", "high", "res", "low" }; +static char *mps_inti_flags_trigger[] = { "dfl", "edge", "res", "level" }; + +/* System Description Table (RSDT/XSDT) */ +struct acpi_table_sdt { + unsigned long pa; + enum acpi_table_id id; + unsigned long size; +} __attribute__ ((packed)); + +static unsigned long sdt_pa; /* Physical Address */ +static unsigned long sdt_count; /* Table count */ + +static struct acpi_table_sdt sdt_entry[ACPI_MAX_TABLES]; + +void +acpi_table_print ( + struct acpi_table_header *header, + unsigned long phys_addr) +{ + char *name = NULL; + + if (!header) + return; + + /* Some table signatures aren't good table names */ + + if (!strncmp((char *) &header->signature, + acpi_table_signatures[ACPI_APIC], + sizeof(header->signature))) { + name = "MADT"; + } + else if (!strncmp((char *) &header->signature, + acpi_table_signatures[ACPI_FADT], + sizeof(header->signature))) { + name = "FADT"; + } + else + name = header->signature; + + printk(KERN_DEBUG PREFIX "%.4s (v%3.3d %6.6s %8.8s 0x%08x %.4s 0x%08x) @ 0x%p\n", + name, header->revision, header->oem_id, + header->oem_table_id, header->oem_revision, + header->asl_compiler_id, header->asl_compiler_revision, + (void *) phys_addr); +} + + +void +acpi_table_print_madt_entry ( + acpi_table_entry_header *header) +{ + if (!header) + return; + + switch (header->type) { + + case ACPI_MADT_LAPIC: + { + struct acpi_table_lapic *p = + (struct acpi_table_lapic*) header; + printk(KERN_INFO PREFIX "LAPIC (acpi_id[0x%02x] lapic_id[0x%02x] %s)\n", + p->acpi_id, p->id, p->flags.enabled?"enabled":"disabled"); + } + break; + + case ACPI_MADT_IOAPIC: + { + struct acpi_table_ioapic *p = + (struct acpi_table_ioapic*) header; + printk(KERN_INFO PREFIX "IOAPIC (id[0x%02x] address[0x%08x] gsi_base[%d])\n", + p->id, p->address, p->global_irq_base); + } + break; + + case ACPI_MADT_INT_SRC_OVR: + { + struct acpi_table_int_src_ovr *p = + (struct acpi_table_int_src_ovr*) header; + printk(KERN_INFO PREFIX "INT_SRC_OVR (bus %d bus_irq %d global_irq %d %s %s)\n", + p->bus, p->bus_irq, p->global_irq, + mps_inti_flags_polarity[p->flags.polarity], + mps_inti_flags_trigger[p->flags.trigger]); + if(p->flags.reserved) + printk(KERN_INFO PREFIX "INT_SRC_OVR unexpected reserved flags: 0x%x\n", + p->flags.reserved); + + } + break; + + case ACPI_MADT_NMI_SRC: + { + struct acpi_table_nmi_src *p = + (struct acpi_table_nmi_src*) header; + printk(KERN_INFO PREFIX "NMI_SRC (%s %s global_irq %d)\n", + mps_inti_flags_polarity[p->flags.polarity], + mps_inti_flags_trigger[p->flags.trigger], p->global_irq); + } + break; + + case ACPI_MADT_LAPIC_NMI: + { + struct acpi_table_lapic_nmi *p = + (struct acpi_table_lapic_nmi*) header; + printk(KERN_INFO PREFIX "LAPIC_NMI (acpi_id[0x%02x] %s %s lint[0x%x])\n", + p->acpi_id, + mps_inti_flags_polarity[p->flags.polarity], + mps_inti_flags_trigger[p->flags.trigger], p->lint); + } + break; + + case ACPI_MADT_LAPIC_ADDR_OVR: + { + struct acpi_table_lapic_addr_ovr *p = + (struct acpi_table_lapic_addr_ovr*) header; + printk(KERN_INFO PREFIX "LAPIC_ADDR_OVR (address[%p])\n", + (void *) (unsigned long) p->address); + } + break; + + case ACPI_MADT_IOSAPIC: + { + struct acpi_table_iosapic *p = + (struct acpi_table_iosapic*) header; + printk(KERN_INFO PREFIX "IOSAPIC (id[0x%x] address[%p] gsi_base[%d])\n", + p->id, (void *) (unsigned long) p->address, p->global_irq_base); + } + break; + + case ACPI_MADT_LSAPIC: + { + struct acpi_table_lsapic *p = + (struct acpi_table_lsapic*) header; + printk(KERN_INFO PREFIX "LSAPIC (acpi_id[0x%02x] lsapic_id[0x%02x] lsapic_eid[0x%02x] %s)\n", + p->acpi_id, p->id, p->eid, p->flags.enabled?"enabled":"disabled"); + } + break; + + case ACPI_MADT_PLAT_INT_SRC: + { + struct acpi_table_plat_int_src *p = + (struct acpi_table_plat_int_src*) header; + printk(KERN_INFO PREFIX "PLAT_INT_SRC (%s %s type[0x%x] id[0x%04x] eid[0x%x] iosapic_vector[0x%x] global_irq[0x%x]\n", + mps_inti_flags_polarity[p->flags.polarity], + mps_inti_flags_trigger[p->flags.trigger], + p->type, p->id, p->eid, p->iosapic_vector, p->global_irq); + } + break; + + default: + printk(KERN_WARNING PREFIX "Found unsupported MADT entry (type = 0x%x)\n", + header->type); + break; + } +} + + +static int +acpi_table_compute_checksum ( + void *table_pointer, + unsigned long length) +{ + u8 *p = (u8 *) table_pointer; + unsigned long remains = length; + unsigned long sum = 0; + + if (!p || !length) + return -EINVAL; + + while (remains--) + sum += *p++; + + return (sum & 0xFF); +} + +/* + * acpi_get_table_header_early() + * for acpi_blacklisted(), acpi_table_get_sdt() + */ +int __init +acpi_get_table_header_early ( + enum acpi_table_id id, + struct acpi_table_header **header) +{ + unsigned int i; + enum acpi_table_id temp_id; + + /* DSDT is different from the rest */ + if (id == ACPI_DSDT) + temp_id = ACPI_FADT; + else + temp_id = id; + + /* Locate the table. */ + + for (i = 0; i < sdt_count; i++) { + if (sdt_entry[i].id != temp_id) + continue; + *header = (void *) + __acpi_map_table(sdt_entry[i].pa, sdt_entry[i].size); + if (!*header) { + printk(KERN_WARNING PREFIX "Unable to map %s\n", + acpi_table_signatures[temp_id]); + return -ENODEV; + } + break; + } + + if (!*header) { + printk(KERN_WARNING PREFIX "%s not present\n", + acpi_table_signatures[id]); + return -ENODEV; + } + + /* Map the DSDT header via the pointer in the FADT */ + if (id == ACPI_DSDT) { + struct fadt_descriptor_rev2 *fadt = (struct fadt_descriptor_rev2 *) *header; + + if (fadt->revision == 3 && fadt->Xdsdt) { + *header = (void *) __acpi_map_table(fadt->Xdsdt, + sizeof(struct acpi_table_header)); + } else if (fadt->V1_dsdt) { + *header = (void *) __acpi_map_table(fadt->V1_dsdt, + sizeof(struct acpi_table_header)); + } else + *header = NULL; + + if (!*header) { + printk(KERN_WARNING PREFIX "Unable to map DSDT\n"); + return -ENODEV; + } + } + + return 0; +} + + +int __init +acpi_table_parse_madt_family ( + enum acpi_table_id id, + unsigned long madt_size, + int entry_id, + acpi_madt_entry_handler handler, + unsigned int max_entries) +{ + void *madt = NULL; + acpi_table_entry_header *entry; + unsigned int count = 0; + unsigned long madt_end; + unsigned int i; + + if (!handler) + return -EINVAL; + + /* Locate the MADT (if exists). There should only be one. */ + + for (i = 0; i < sdt_count; i++) { + if (sdt_entry[i].id != id) + continue; + madt = (void *) + __acpi_map_table(sdt_entry[i].pa, sdt_entry[i].size); + if (!madt) { + printk(KERN_WARNING PREFIX "Unable to map %s\n", + acpi_table_signatures[id]); + return -ENODEV; + } + break; + } + + if (!madt) { + printk(KERN_WARNING PREFIX "%s not present\n", + acpi_table_signatures[id]); + return -ENODEV; + } + + madt_end = (unsigned long) madt + sdt_entry[i].size; + + /* Parse all entries looking for a match. */ + + entry = (acpi_table_entry_header *) + ((unsigned long) madt + madt_size); + + while (((unsigned long) entry) + sizeof(acpi_table_entry_header) < madt_end) { + if (entry->type == entry_id && + (!max_entries || count++ < max_entries)) + if (handler(entry, madt_end)) + return -EINVAL; + + entry = (acpi_table_entry_header *) + ((unsigned long) entry + entry->length); + } + if (max_entries && count > max_entries) { + printk(KERN_WARNING PREFIX "[%s:0x%02x] ignored %i entries of " + "%i found\n", acpi_table_signatures[id], entry_id, + count - max_entries, count); + } + + return count; +} + + +int __init +acpi_table_parse_madt ( + enum acpi_madt_entry_id id, + acpi_madt_entry_handler handler, + unsigned int max_entries) +{ + return acpi_table_parse_madt_family(ACPI_APIC, sizeof(struct acpi_table_madt), + id, handler, max_entries); +} + + +int __init +acpi_table_parse ( + enum acpi_table_id id, + acpi_table_handler handler) +{ + int count = 0; + unsigned int i = 0; + + if (!handler) + return -EINVAL; + + for (i = 0; i < sdt_count; i++) { + if (sdt_entry[i].id != id) + continue; + count++; + if (count == 1) + handler(sdt_entry[i].pa, sdt_entry[i].size); + + else + printk(KERN_WARNING PREFIX "%d duplicate %s table ignored.\n", + count, acpi_table_signatures[id]); + } + + return count; +} + + +static int __init +acpi_table_get_sdt ( + struct acpi_table_rsdp *rsdp) +{ + struct acpi_table_header *header = NULL; + unsigned int i, id = 0; + + if (!rsdp) + return -EINVAL; + + /* First check XSDT (but only on ACPI 2.0-compatible systems) */ + + if ((rsdp->revision >= 2) && + (((struct acpi20_table_rsdp*)rsdp)->xsdt_address)) { + + struct acpi_table_xsdt *mapped_xsdt = NULL; + + sdt_pa = ((struct acpi20_table_rsdp*)rsdp)->xsdt_address; + + /* map in just the header */ + header = (struct acpi_table_header *) + __acpi_map_table(sdt_pa, sizeof(struct acpi_table_header)); + + if (!header) { + printk(KERN_WARNING PREFIX "Unable to map XSDT header\n"); + return -ENODEV; + } + + /* remap in the entire table before processing */ + mapped_xsdt = (struct acpi_table_xsdt *) + __acpi_map_table(sdt_pa, header->length); + if (!mapped_xsdt) { + printk(KERN_WARNING PREFIX "Unable to map XSDT\n"); + return -ENODEV; + } + header = &mapped_xsdt->header; + + if (strncmp(header->signature, "XSDT", 4)) { + printk(KERN_WARNING PREFIX "XSDT signature incorrect\n"); + return -ENODEV; + } + + if (acpi_table_compute_checksum(header, header->length)) { + printk(KERN_WARNING PREFIX "Invalid XSDT checksum\n"); + return -ENODEV; + } + + sdt_count = (header->length - sizeof(struct acpi_table_header)) >> 3; + if (sdt_count > ACPI_MAX_TABLES) { + printk(KERN_WARNING PREFIX "Truncated %lu XSDT entries\n", + (sdt_count - ACPI_MAX_TABLES)); + sdt_count = ACPI_MAX_TABLES; + } + + for (i = 0; i < sdt_count; i++) + sdt_entry[i].pa = (unsigned long) mapped_xsdt->entry[i]; + } + + /* Then check RSDT */ + + else if (rsdp->rsdt_address) { + + struct acpi_table_rsdt *mapped_rsdt = NULL; + + sdt_pa = rsdp->rsdt_address; + + /* map in just the header */ + header = (struct acpi_table_header *) + __acpi_map_table(sdt_pa, sizeof(struct acpi_table_header)); + if (!header) { + printk(KERN_WARNING PREFIX "Unable to map RSDT header\n"); + return -ENODEV; + } + + /* remap in the entire table before processing */ + mapped_rsdt = (struct acpi_table_rsdt *) + __acpi_map_table(sdt_pa, header->length); + if (!mapped_rsdt) { + printk(KERN_WARNING PREFIX "Unable to map RSDT\n"); + return -ENODEV; + } + header = &mapped_rsdt->header; + + if (strncmp(header->signature, "RSDT", 4)) { + printk(KERN_WARNING PREFIX "RSDT signature incorrect\n"); + return -ENODEV; + } + + if (acpi_table_compute_checksum(header, header->length)) { + printk(KERN_WARNING PREFIX "Invalid RSDT checksum\n"); + return -ENODEV; + } + + sdt_count = (header->length - sizeof(struct acpi_table_header)) >> 2; + if (sdt_count > ACPI_MAX_TABLES) { + printk(KERN_WARNING PREFIX "Truncated %lu RSDT entries\n", + (sdt_count - ACPI_MAX_TABLES)); + sdt_count = ACPI_MAX_TABLES; + } + + for (i = 0; i < sdt_count; i++) + sdt_entry[i].pa = (unsigned long) mapped_rsdt->entry[i]; + } + + else { + printk(KERN_WARNING PREFIX "No System Description Table (RSDT/XSDT) specified in RSDP\n"); + return -ENODEV; + } + + acpi_table_print(header, sdt_pa); + + for (i = 0; i < sdt_count; i++) { + + /* map in just the header */ + header = (struct acpi_table_header *) + __acpi_map_table(sdt_entry[i].pa, + sizeof(struct acpi_table_header)); + if (!header) + continue; + + /* remap in the entire table before processing */ + header = (struct acpi_table_header *) + __acpi_map_table(sdt_entry[i].pa, + header->length); + if (!header) + continue; + + acpi_table_print(header, sdt_entry[i].pa); + + if (acpi_table_compute_checksum(header, header->length)) { + printk(KERN_WARNING " >>> ERROR: Invalid checksum\n"); + continue; + } + + sdt_entry[i].size = header->length; + + for (id = 0; id < ACPI_TABLE_COUNT; id++) { + if (!strncmp((char *) &header->signature, + acpi_table_signatures[id], + sizeof(header->signature))) { + sdt_entry[i].id = id; + } + } + } + + /* + * The DSDT is *not* in the RSDT (why not? no idea.) but we want + * to print its info, because this is what people usually blacklist + * against. Unfortunately, we don't know the phys_addr, so just + * print 0. Maybe no one will notice. + */ + if(!acpi_get_table_header_early(ACPI_DSDT, &header)) + acpi_table_print(header, 0); + + return 0; +} + +/* + * acpi_table_init() + * + * find RSDP, find and checksum SDT/XSDT. + * checksum all tables, print SDT/XSDT + * + * result: sdt_entry[] is initialized + */ + +int __init +acpi_table_init (void) +{ + struct acpi_table_rsdp *rsdp = NULL; + unsigned long rsdp_phys = 0; + int result = 0; + + /* Locate and map the Root System Description Table (RSDP) */ + + rsdp_phys = acpi_find_rsdp(); + if (!rsdp_phys) { + printk(KERN_ERR PREFIX "Unable to locate RSDP\n"); + return -ENODEV; + } + + rsdp = (struct acpi_table_rsdp *) __va(rsdp_phys); + if (!rsdp) { + printk(KERN_WARNING PREFIX "Unable to map RSDP\n"); + return -ENODEV; + } + + printk(KERN_DEBUG PREFIX "RSDP (v%3.3d %6.6s ) @ 0x%p\n", + rsdp->revision, rsdp->oem_id, (void *) rsdp_phys); + + if (rsdp->revision < 2) + result = acpi_table_compute_checksum(rsdp, sizeof(struct acpi_table_rsdp)); + else + result = acpi_table_compute_checksum(rsdp, ((struct acpi20_table_rsdp *)rsdp)->length); + + if (result) { + printk(KERN_WARNING " >>> ERROR: Invalid checksum\n"); + return -ENODEV; + } + + /* Locate and map the System Description table (RSDT/XSDT) */ + + if (acpi_table_get_sdt(rsdp)) + return -ENODEV; + + return 0; +} diff --git a/drivers/acpi/tables/Makefile b/drivers/acpi/tables/Makefile new file mode 100644 index 00000000000..aa4c69594d9 --- /dev/null +++ b/drivers/acpi/tables/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for all Linux ACPI interpreter subdirectories +# + +obj-y := tbconvrt.o tbget.o tbrsdt.o tbxface.o \ + tbgetall.o tbinstal.o tbutils.o tbxfroot.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/tables/tbconvrt.c b/drivers/acpi/tables/tbconvrt.c new file mode 100644 index 00000000000..334327c1f66 --- /dev/null +++ b/drivers/acpi/tables/tbconvrt.c @@ -0,0 +1,564 @@ +/****************************************************************************** + * + * Module Name: tbconvrt - ACPI Table conversion utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/actables.h> + + +#define _COMPONENT ACPI_TABLES + ACPI_MODULE_NAME ("tbconvrt") + + +u8 acpi_fadt_is_v1; +EXPORT_SYMBOL(acpi_fadt_is_v1); + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_table_count + * + * PARAMETERS: RSDP - Pointer to the RSDP + * RSDT - Pointer to the RSDT/XSDT + * + * RETURN: The number of tables pointed to by the RSDT or XSDT. + * + * DESCRIPTION: Calculate the number of tables. Automatically handles either + * an RSDT or XSDT. + * + ******************************************************************************/ + +u32 +acpi_tb_get_table_count ( + struct rsdp_descriptor *RSDP, + struct acpi_table_header *RSDT) +{ + u32 pointer_size; + + + ACPI_FUNCTION_ENTRY (); + + + if (RSDP->revision < 2) { + pointer_size = sizeof (u32); + } + else { + pointer_size = sizeof (u64); + } + + /* + * Determine the number of tables pointed to by the RSDT/XSDT. + * This is defined by the ACPI Specification to be the number of + * pointers contained within the RSDT/XSDT. The size of the pointers + * is architecture-dependent. + */ + return ((RSDT->length - sizeof (struct acpi_table_header)) / pointer_size); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_convert_to_xsdt + * + * PARAMETERS: table_info - Info about the RSDT + * + * RETURN: Status + * + * DESCRIPTION: Convert an RSDT to an XSDT (internal common format) + * + ******************************************************************************/ + +acpi_status +acpi_tb_convert_to_xsdt ( + struct acpi_table_desc *table_info) +{ + acpi_size table_size; + u32 i; + XSDT_DESCRIPTOR *new_table; + + + ACPI_FUNCTION_ENTRY (); + + + /* Compute size of the converted XSDT */ + + table_size = ((acpi_size) acpi_gbl_rsdt_table_count * sizeof (u64)) + + sizeof (struct acpi_table_header); + + /* Allocate an XSDT */ + + new_table = ACPI_MEM_CALLOCATE (table_size); + if (!new_table) { + return (AE_NO_MEMORY); + } + + /* Copy the header and set the length */ + + ACPI_MEMCPY (new_table, table_info->pointer, sizeof (struct acpi_table_header)); + new_table->length = (u32) table_size; + + /* Copy the table pointers */ + + for (i = 0; i < acpi_gbl_rsdt_table_count; i++) { + if (acpi_gbl_RSDP->revision < 2) { + ACPI_STORE_ADDRESS (new_table->table_offset_entry[i], + (ACPI_CAST_PTR (struct rsdt_descriptor_rev1, table_info->pointer))->table_offset_entry[i]); + } + else { + new_table->table_offset_entry[i] = + (ACPI_CAST_PTR (XSDT_DESCRIPTOR, table_info->pointer))->table_offset_entry[i]; + } + } + + /* Delete the original table (either mapped or in a buffer) */ + + acpi_tb_delete_single_table (table_info); + + /* Point the table descriptor to the new table */ + + table_info->pointer = ACPI_CAST_PTR (struct acpi_table_header, new_table); + table_info->length = table_size; + table_info->allocation = ACPI_MEM_ALLOCATED; + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_tb_init_generic_address + * + * PARAMETERS: new_gas_struct - GAS struct to be initialized + * register_bit_width - Width of this register + * Address - Address of the register + * + * RETURN: None + * + * DESCRIPTION: Initialize a GAS structure. + * + ******************************************************************************/ + +static void +acpi_tb_init_generic_address ( + struct acpi_generic_address *new_gas_struct, + u8 register_bit_width, + acpi_physical_address address) +{ + + ACPI_STORE_ADDRESS (new_gas_struct->address, address); + + new_gas_struct->address_space_id = ACPI_ADR_SPACE_SYSTEM_IO; + new_gas_struct->register_bit_width = register_bit_width; + new_gas_struct->register_bit_offset = 0; + new_gas_struct->access_width = 0; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_convert_fadt1 + * + * PARAMETERS: local_fadt - Pointer to new FADT + * original_fadt - Pointer to old FADT + * + * RETURN: Populates local_fadt + * + * DESCRIPTION: Convert an ACPI 1.0 FADT to common internal format + * + ******************************************************************************/ + +static void +acpi_tb_convert_fadt1 ( + struct fadt_descriptor_rev2 *local_fadt, + struct fadt_descriptor_rev1 *original_fadt) +{ + + + /* ACPI 1.0 FACS */ + /* The BIOS stored FADT should agree with Revision 1.0 */ + acpi_fadt_is_v1 = 1; + + /* + * Copy the table header and the common part of the tables. + * + * The 2.0 table is an extension of the 1.0 table, so the entire 1.0 + * table can be copied first, then expand some fields to 64 bits. + */ + ACPI_MEMCPY (local_fadt, original_fadt, sizeof (struct fadt_descriptor_rev1)); + + /* Convert table pointers to 64-bit fields */ + + ACPI_STORE_ADDRESS (local_fadt->xfirmware_ctrl, local_fadt->V1_firmware_ctrl); + ACPI_STORE_ADDRESS (local_fadt->Xdsdt, local_fadt->V1_dsdt); + + /* + * System Interrupt Model isn't used in ACPI 2.0 (local_fadt->Reserved1 = 0;) + */ + + /* + * This field is set by the OEM to convey the preferred power management + * profile to OSPM. It doesn't have any 1.0 equivalence. Since we don't + * know what kind of 32-bit system this is, we will use "unspecified". + */ + local_fadt->prefer_PM_profile = PM_UNSPECIFIED; + + /* + * Processor Performance State Control. This is the value OSPM writes to + * the SMI_CMD register to assume processor performance state control + * responsibility. There isn't any equivalence in 1.0, but as many 1.x + * ACPI tables contain _PCT and _PSS we also keep this value, unless + * acpi_strict is set. + */ + if (acpi_strict) + local_fadt->pstate_cnt = 0; + + /* + * Support for the _CST object and C States change notification. + * This data item hasn't any 1.0 equivalence so leave it zero. + */ + local_fadt->cst_cnt = 0; + + /* + * FADT Rev 2 was an interim FADT released between ACPI 1.0 and ACPI 2.0. + * It primarily adds the FADT reset mechanism. + */ + if ((original_fadt->revision == 2) && + (original_fadt->length == sizeof (struct fadt_descriptor_rev2_minus))) { + /* + * Grab the entire generic address struct, plus the 1-byte reset value + * that immediately follows. + */ + ACPI_MEMCPY (&local_fadt->reset_register, + &(ACPI_CAST_PTR (struct fadt_descriptor_rev2_minus, original_fadt))->reset_register, + sizeof (struct acpi_generic_address) + 1); + } + else { + /* + * Since there isn't any equivalence in 1.0 and since it is highly + * likely that a 1.0 system has legacy support. + */ + local_fadt->iapc_boot_arch = BAF_LEGACY_DEVICES; + } + + /* + * Convert the V1.0 block addresses to V2.0 GAS structures + */ + acpi_tb_init_generic_address (&local_fadt->xpm1a_evt_blk, local_fadt->pm1_evt_len, + (acpi_physical_address) local_fadt->V1_pm1a_evt_blk); + acpi_tb_init_generic_address (&local_fadt->xpm1b_evt_blk, local_fadt->pm1_evt_len, + (acpi_physical_address) local_fadt->V1_pm1b_evt_blk); + acpi_tb_init_generic_address (&local_fadt->xpm1a_cnt_blk, local_fadt->pm1_cnt_len, + (acpi_physical_address) local_fadt->V1_pm1a_cnt_blk); + acpi_tb_init_generic_address (&local_fadt->xpm1b_cnt_blk, local_fadt->pm1_cnt_len, + (acpi_physical_address) local_fadt->V1_pm1b_cnt_blk); + acpi_tb_init_generic_address (&local_fadt->xpm2_cnt_blk, local_fadt->pm2_cnt_len, + (acpi_physical_address) local_fadt->V1_pm2_cnt_blk); + acpi_tb_init_generic_address (&local_fadt->xpm_tmr_blk, local_fadt->pm_tm_len, + (acpi_physical_address) local_fadt->V1_pm_tmr_blk); + acpi_tb_init_generic_address (&local_fadt->xgpe0_blk, 0, + (acpi_physical_address) local_fadt->V1_gpe0_blk); + acpi_tb_init_generic_address (&local_fadt->xgpe1_blk, 0, + (acpi_physical_address) local_fadt->V1_gpe1_blk); + + /* Create separate GAS structs for the PM1 Enable registers */ + + acpi_tb_init_generic_address (&acpi_gbl_xpm1a_enable, + (u8) ACPI_DIV_2 (acpi_gbl_FADT->pm1_evt_len), + (acpi_physical_address) (local_fadt->xpm1a_evt_blk.address + + ACPI_DIV_2 (acpi_gbl_FADT->pm1_evt_len))); + + /* PM1B is optional; leave null if not present */ + + if (local_fadt->xpm1b_evt_blk.address) { + acpi_tb_init_generic_address (&acpi_gbl_xpm1b_enable, + (u8) ACPI_DIV_2 (acpi_gbl_FADT->pm1_evt_len), + (acpi_physical_address) (local_fadt->xpm1b_evt_blk.address + + ACPI_DIV_2 (acpi_gbl_FADT->pm1_evt_len))); + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_convert_fadt2 + * + * PARAMETERS: local_fadt - Pointer to new FADT + * original_fadt - Pointer to old FADT + * + * RETURN: Populates local_fadt + * + * DESCRIPTION: Convert an ACPI 2.0 FADT to common internal format. + * Handles optional "X" fields. + * + ******************************************************************************/ + +static void +acpi_tb_convert_fadt2 ( + struct fadt_descriptor_rev2 *local_fadt, + struct fadt_descriptor_rev2 *original_fadt) +{ + + /* We have an ACPI 2.0 FADT but we must copy it to our local buffer */ + + ACPI_MEMCPY (local_fadt, original_fadt, sizeof (struct fadt_descriptor_rev2)); + + /* + * "X" fields are optional extensions to the original V1.0 fields, so + * we must selectively expand V1.0 fields if the corresponding X field + * is zero. + */ + if (!(local_fadt->xfirmware_ctrl)) { + ACPI_STORE_ADDRESS (local_fadt->xfirmware_ctrl, local_fadt->V1_firmware_ctrl); + } + + if (!(local_fadt->Xdsdt)) { + ACPI_STORE_ADDRESS (local_fadt->Xdsdt, local_fadt->V1_dsdt); + } + + if (!(local_fadt->xpm1a_evt_blk.address)) { + acpi_tb_init_generic_address (&local_fadt->xpm1a_evt_blk, + local_fadt->pm1_evt_len, (acpi_physical_address) local_fadt->V1_pm1a_evt_blk); + } + + if (!(local_fadt->xpm1b_evt_blk.address)) { + acpi_tb_init_generic_address (&local_fadt->xpm1b_evt_blk, + local_fadt->pm1_evt_len, (acpi_physical_address) local_fadt->V1_pm1b_evt_blk); + } + + if (!(local_fadt->xpm1a_cnt_blk.address)) { + acpi_tb_init_generic_address (&local_fadt->xpm1a_cnt_blk, + local_fadt->pm1_cnt_len, (acpi_physical_address) local_fadt->V1_pm1a_cnt_blk); + } + + if (!(local_fadt->xpm1b_cnt_blk.address)) { + acpi_tb_init_generic_address (&local_fadt->xpm1b_cnt_blk, + local_fadt->pm1_cnt_len, (acpi_physical_address) local_fadt->V1_pm1b_cnt_blk); + } + + if (!(local_fadt->xpm2_cnt_blk.address)) { + acpi_tb_init_generic_address (&local_fadt->xpm2_cnt_blk, + local_fadt->pm2_cnt_len, (acpi_physical_address) local_fadt->V1_pm2_cnt_blk); + } + + if (!(local_fadt->xpm_tmr_blk.address)) { + acpi_tb_init_generic_address (&local_fadt->xpm_tmr_blk, + local_fadt->pm_tm_len, (acpi_physical_address) local_fadt->V1_pm_tmr_blk); + } + + if (!(local_fadt->xgpe0_blk.address)) { + acpi_tb_init_generic_address (&local_fadt->xgpe0_blk, + 0, (acpi_physical_address) local_fadt->V1_gpe0_blk); + } + + if (!(local_fadt->xgpe1_blk.address)) { + acpi_tb_init_generic_address (&local_fadt->xgpe1_blk, + 0, (acpi_physical_address) local_fadt->V1_gpe1_blk); + } + + /* Create separate GAS structs for the PM1 Enable registers */ + + acpi_tb_init_generic_address (&acpi_gbl_xpm1a_enable, + (u8) ACPI_DIV_2 (acpi_gbl_FADT->pm1_evt_len), + (acpi_physical_address) (local_fadt->xpm1a_evt_blk.address + + ACPI_DIV_2 (acpi_gbl_FADT->pm1_evt_len))); + acpi_gbl_xpm1a_enable.address_space_id = local_fadt->xpm1a_evt_blk.address_space_id; + + /* PM1B is optional; leave null if not present */ + + if (local_fadt->xpm1b_evt_blk.address) { + acpi_tb_init_generic_address (&acpi_gbl_xpm1b_enable, + (u8) ACPI_DIV_2 (acpi_gbl_FADT->pm1_evt_len), + (acpi_physical_address) (local_fadt->xpm1b_evt_blk.address + + ACPI_DIV_2 (acpi_gbl_FADT->pm1_evt_len))); + acpi_gbl_xpm1b_enable.address_space_id = local_fadt->xpm1b_evt_blk.address_space_id; + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_convert_table_fadt + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Converts a BIOS supplied ACPI 1.0 FADT to a local + * ACPI 2.0 FADT. If the BIOS supplied a 2.0 FADT then it is simply + * copied to the local FADT. The ACPI CA software uses this + * local FADT. Thus a significant amount of special #ifdef + * type codeing is saved. + * + ******************************************************************************/ + +acpi_status +acpi_tb_convert_table_fadt (void) +{ + struct fadt_descriptor_rev2 *local_fadt; + struct acpi_table_desc *table_desc; + + + ACPI_FUNCTION_TRACE ("tb_convert_table_fadt"); + + + /* + * acpi_gbl_FADT is valid. Validate the FADT length. The table must be + * at least as long as the version 1.0 FADT + */ + if (acpi_gbl_FADT->length < sizeof (struct fadt_descriptor_rev1)) { + ACPI_REPORT_ERROR (("FADT is invalid, too short: 0x%X\n", acpi_gbl_FADT->length)); + return_ACPI_STATUS (AE_INVALID_TABLE_LENGTH); + } + + /* Allocate buffer for the ACPI 2.0(+) FADT */ + + local_fadt = ACPI_MEM_CALLOCATE (sizeof (struct fadt_descriptor_rev2)); + if (!local_fadt) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + if (acpi_gbl_FADT->revision >= FADT2_REVISION_ID) { + if (acpi_gbl_FADT->length < sizeof (struct fadt_descriptor_rev2)) { + /* Length is too short to be a V2.0 table */ + + ACPI_REPORT_WARNING (("Inconsistent FADT length (0x%X) and revision (0x%X), using FADT V1.0 portion of table\n", + acpi_gbl_FADT->length, acpi_gbl_FADT->revision)); + + acpi_tb_convert_fadt1 (local_fadt, (void *) acpi_gbl_FADT); + } + else { + /* Valid V2.0 table */ + + acpi_tb_convert_fadt2 (local_fadt, acpi_gbl_FADT); + } + } + else { + /* Valid V1.0 table */ + + acpi_tb_convert_fadt1 (local_fadt, (void *) acpi_gbl_FADT); + } + + /* + * Global FADT pointer will point to the new common V2.0 FADT + */ + acpi_gbl_FADT = local_fadt; + acpi_gbl_FADT->length = sizeof (FADT_DESCRIPTOR); + + /* Free the original table */ + + table_desc = acpi_gbl_table_lists[ACPI_TABLE_FADT].next; + acpi_tb_delete_single_table (table_desc); + + /* Install the new table */ + + table_desc->pointer = ACPI_CAST_PTR (struct acpi_table_header, acpi_gbl_FADT); + table_desc->allocation = ACPI_MEM_ALLOCATED; + table_desc->length = sizeof (struct fadt_descriptor_rev2); + + /* Dump the entire FADT */ + + ACPI_DEBUG_PRINT ((ACPI_DB_TABLES, + "Hex dump of common internal FADT, size %d (%X)\n", + acpi_gbl_FADT->length, acpi_gbl_FADT->length)); + ACPI_DUMP_BUFFER ((u8 *) (acpi_gbl_FADT), acpi_gbl_FADT->length); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_convert_table_facs + * + * PARAMETERS: table_info - Info for currently installed FACS + * + * RETURN: Status + * + * DESCRIPTION: Convert ACPI 1.0 and ACPI 2.0 FACS to a common internal + * table format. + * + ******************************************************************************/ + +acpi_status +acpi_tb_build_common_facs ( + struct acpi_table_desc *table_info) +{ + + ACPI_FUNCTION_TRACE ("tb_build_common_facs"); + + + /* Absolute minimum length is 24, but the ACPI spec says 64 */ + + if (acpi_gbl_FACS->length < 24) { + ACPI_REPORT_ERROR (("Invalid FACS table length: 0x%X\n", acpi_gbl_FACS->length)); + return_ACPI_STATUS (AE_INVALID_TABLE_LENGTH); + } + + if (acpi_gbl_FACS->length < 64) { + ACPI_REPORT_WARNING (("FACS is shorter than the ACPI specification allows: 0x%X, using anyway\n", + acpi_gbl_FACS->length)); + } + + /* Copy fields to the new FACS */ + + acpi_gbl_common_fACS.global_lock = &(acpi_gbl_FACS->global_lock); + + if ((acpi_gbl_RSDP->revision < 2) || + (acpi_gbl_FACS->length < 32) || + (!(acpi_gbl_FACS->xfirmware_waking_vector))) { + /* ACPI 1.0 FACS or short table or optional X_ field is zero */ + + acpi_gbl_common_fACS.firmware_waking_vector = ACPI_CAST_PTR (u64, &(acpi_gbl_FACS->firmware_waking_vector)); + acpi_gbl_common_fACS.vector_width = 32; + } + else { + /* ACPI 2.0 FACS with valid X_ field */ + + acpi_gbl_common_fACS.firmware_waking_vector = &acpi_gbl_FACS->xfirmware_waking_vector; + acpi_gbl_common_fACS.vector_width = 64; + } + + return_ACPI_STATUS (AE_OK); +} + + diff --git a/drivers/acpi/tables/tbget.c b/drivers/acpi/tables/tbget.c new file mode 100644 index 00000000000..896f3ddda62 --- /dev/null +++ b/drivers/acpi/tables/tbget.c @@ -0,0 +1,493 @@ +/****************************************************************************** + * + * Module Name: tbget - ACPI Table get* routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/actables.h> + + +#define _COMPONENT ACPI_TABLES + ACPI_MODULE_NAME ("tbget") + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_table + * + * PARAMETERS: Address - Address of table to retrieve. Can be + * Logical or Physical + * table_info - Where table info is returned + * + * RETURN: None + * + * DESCRIPTION: Get entire table of unknown size. + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_table ( + struct acpi_pointer *address, + struct acpi_table_desc *table_info) +{ + acpi_status status; + struct acpi_table_header header; + + + ACPI_FUNCTION_TRACE ("tb_get_table"); + + + /* + * Get the header in order to get signature and table size + */ + status = acpi_tb_get_table_header (address, &header); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Get the entire table */ + + status = acpi_tb_get_table_body (address, &header, table_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not get ACPI table (size %X), %s\n", + header.length, acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_table_header + * + * PARAMETERS: Address - Address of table to retrieve. Can be + * Logical or Physical + * return_header - Where the table header is returned + * + * RETURN: Status + * + * DESCRIPTION: Get an ACPI table header. Works in both physical or virtual + * addressing mode. Works with both physical or logical pointers. + * Table is either copied or mapped, depending on the pointer + * type and mode of the processor. + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_table_header ( + struct acpi_pointer *address, + struct acpi_table_header *return_header) +{ + acpi_status status = AE_OK; + struct acpi_table_header *header = NULL; + + + ACPI_FUNCTION_TRACE ("tb_get_table_header"); + + + /* + * Flags contains the current processor mode (Virtual or Physical addressing) + * The pointer_type is either Logical or Physical + */ + switch (address->pointer_type) { + case ACPI_PHYSMODE_PHYSPTR: + case ACPI_LOGMODE_LOGPTR: + + /* Pointer matches processor mode, copy the header */ + + ACPI_MEMCPY (return_header, address->pointer.logical, sizeof (struct acpi_table_header)); + break; + + + case ACPI_LOGMODE_PHYSPTR: + + /* Create a logical address for the physical pointer*/ + + status = acpi_os_map_memory (address->pointer.physical, sizeof (struct acpi_table_header), + (void *) &header); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not map memory at %8.8X%8.8X for length %X\n", + ACPI_FORMAT_UINT64 (address->pointer.physical), + sizeof (struct acpi_table_header))); + return_ACPI_STATUS (status); + } + + /* Copy header and delete mapping */ + + ACPI_MEMCPY (return_header, header, sizeof (struct acpi_table_header)); + acpi_os_unmap_memory (header, sizeof (struct acpi_table_header)); + break; + + + default: + + ACPI_REPORT_ERROR (("Invalid address flags %X\n", + address->pointer_type)); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_TABLES, "Table Signature: [%4.4s]\n", + return_header->signature)); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_table_body + * + * PARAMETERS: Address - Address of table to retrieve. Can be + * Logical or Physical + * Header - Header of the table to retrieve + * table_info - Where the table info is returned + * + * RETURN: Status + * + * DESCRIPTION: Get an entire ACPI table with support to allow the host OS to + * replace the table with a newer version (table override.) + * Works in both physical or virtual + * addressing mode. Works with both physical or logical pointers. + * Table is either copied or mapped, depending on the pointer + * type and mode of the processor. + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_table_body ( + struct acpi_pointer *address, + struct acpi_table_header *header, + struct acpi_table_desc *table_info) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("tb_get_table_body"); + + + if (!table_info || !address) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Attempt table override. + */ + status = acpi_tb_table_override (header, table_info); + if (ACPI_SUCCESS (status)) { + /* Table was overridden by the host OS */ + + return_ACPI_STATUS (status); + } + + /* No override, get the original table */ + + status = acpi_tb_get_this_table (address, header, table_info); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_table_override + * + * PARAMETERS: Header - Pointer to table header + * table_info - Return info if table is overridden + * + * RETURN: None + * + * DESCRIPTION: Attempts override of current table with a new one if provided + * by the host OS. + * + ******************************************************************************/ + +acpi_status +acpi_tb_table_override ( + struct acpi_table_header *header, + struct acpi_table_desc *table_info) +{ + struct acpi_table_header *new_table; + acpi_status status; + struct acpi_pointer address; + + + ACPI_FUNCTION_TRACE ("tb_table_override"); + + + /* + * The OSL will examine the header and decide whether to override this + * table. If it decides to override, a table will be returned in new_table, + * which we will then copy. + */ + status = acpi_os_table_override (header, &new_table); + if (ACPI_FAILURE (status)) { + /* Some severe error from the OSL, but we basically ignore it */ + + ACPI_REPORT_ERROR (("Could not override ACPI table, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + if (!new_table) { + /* No table override */ + + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + /* + * We have a new table to override the old one. Get a copy of + * the new one. We know that the new table has a logical pointer. + */ + address.pointer_type = ACPI_LOGICAL_POINTER | ACPI_LOGICAL_ADDRESSING; + address.pointer.logical = new_table; + + status = acpi_tb_get_this_table (&address, new_table, table_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not copy override ACPI table, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + /* Copy the table info */ + + ACPI_REPORT_INFO (("Table [%4.4s] replaced by host OS\n", + table_info->pointer->signature)); + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_this_table + * + * PARAMETERS: Address - Address of table to retrieve. Can be + * Logical or Physical + * Header - Header of the table to retrieve + * table_info - Where the table info is returned + * + * RETURN: Status + * + * DESCRIPTION: Get an entire ACPI table. Works in both physical or virtual + * addressing mode. Works with both physical or logical pointers. + * Table is either copied or mapped, depending on the pointer + * type and mode of the processor. + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_this_table ( + struct acpi_pointer *address, + struct acpi_table_header *header, + struct acpi_table_desc *table_info) +{ + struct acpi_table_header *full_table = NULL; + u8 allocation; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("tb_get_this_table"); + + + /* + * Flags contains the current processor mode (Virtual or Physical addressing) + * The pointer_type is either Logical or Physical + */ + switch (address->pointer_type) { + case ACPI_PHYSMODE_PHYSPTR: + case ACPI_LOGMODE_LOGPTR: + + /* Pointer matches processor mode, copy the table to a new buffer */ + + full_table = ACPI_MEM_ALLOCATE (header->length); + if (!full_table) { + ACPI_REPORT_ERROR (("Could not allocate table memory for [%4.4s] length %X\n", + header->signature, header->length)); + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Copy the entire table (including header) to the local buffer */ + + ACPI_MEMCPY (full_table, address->pointer.logical, header->length); + + /* Save allocation type */ + + allocation = ACPI_MEM_ALLOCATED; + break; + + + case ACPI_LOGMODE_PHYSPTR: + + /* + * Just map the table's physical memory + * into our address space. + */ + status = acpi_os_map_memory (address->pointer.physical, (acpi_size) header->length, + (void *) &full_table); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not map memory for table [%4.4s] at %8.8X%8.8X for length %X\n", + header->signature, + ACPI_FORMAT_UINT64 (address->pointer.physical), header->length)); + return (status); + } + + /* Save allocation type */ + + allocation = ACPI_MEM_MAPPED; + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Invalid address flags %X\n", + address->pointer_type)); + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * Validate checksum for _most_ tables, + * even the ones whose signature we don't recognize + */ + if (table_info->type != ACPI_TABLE_FACS) { + status = acpi_tb_verify_table_checksum (full_table); + +#if (!ACPI_CHECKSUM_ABORT) + if (ACPI_FAILURE (status)) { + /* Ignore the error if configuration says so */ + + status = AE_OK; + } +#endif + } + + /* Return values */ + + table_info->pointer = full_table; + table_info->length = (acpi_size) header->length; + table_info->allocation = allocation; + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Found table [%4.4s] at %8.8X%8.8X, mapped/copied to %p\n", + full_table->signature, + ACPI_FORMAT_UINT64 (address->pointer.physical), full_table)); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_table_ptr + * + * PARAMETERS: table_type - one of the defined table types + * Instance - Which table of this type + * table_ptr_loc - pointer to location to place the pointer for + * return + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get the pointer to an ACPI table. + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_table_ptr ( + acpi_table_type table_type, + u32 instance, + struct acpi_table_header **table_ptr_loc) +{ + struct acpi_table_desc *table_desc; + u32 i; + + + ACPI_FUNCTION_TRACE ("tb_get_table_ptr"); + + + if (!acpi_gbl_DSDT) { + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + if (table_type > ACPI_TABLE_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * For all table types (Single/Multiple), the first + * instance is always in the list head. + */ + if (instance == 1) { + /* Get the first */ + + *table_ptr_loc = NULL; + if (acpi_gbl_table_lists[table_type].next) { + *table_ptr_loc = acpi_gbl_table_lists[table_type].next->pointer; + } + return_ACPI_STATUS (AE_OK); + } + + /* + * Check for instance out of range + */ + if (instance > acpi_gbl_table_lists[table_type].count) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + + /* Walk the list to get the desired table + * Since the if (Instance == 1) check above checked for the + * first table, setting table_desc equal to the .Next member + * is actually pointing to the second table. Therefore, we + * need to walk from the 2nd table until we reach the Instance + * that the user is looking for and return its table pointer. + */ + table_desc = acpi_gbl_table_lists[table_type].next; + for (i = 2; i < instance; i++) { + table_desc = table_desc->next; + } + + /* We are now pointing to the requested table's descriptor */ + + *table_ptr_loc = table_desc->pointer; + + return_ACPI_STATUS (AE_OK); +} + diff --git a/drivers/acpi/tables/tbgetall.c b/drivers/acpi/tables/tbgetall.c new file mode 100644 index 00000000000..adc4270988b --- /dev/null +++ b/drivers/acpi/tables/tbgetall.c @@ -0,0 +1,313 @@ +/****************************************************************************** + * + * Module Name: tbgetall - Get all required ACPI tables + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/actables.h> + + +#define _COMPONENT ACPI_TABLES + ACPI_MODULE_NAME ("tbgetall") + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_primary_table + * + * PARAMETERS: Address - Physical address of table to retrieve + * *table_info - Where the table info is returned + * + * RETURN: Status + * + * DESCRIPTION: Maps the physical address of table into a logical address + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_primary_table ( + struct acpi_pointer *address, + struct acpi_table_desc *table_info) +{ + acpi_status status; + struct acpi_table_header header; + + + ACPI_FUNCTION_TRACE ("tb_get_primary_table"); + + + /* Ignore a NULL address in the RSDT */ + + if (!address->pointer.value) { + return_ACPI_STATUS (AE_OK); + } + + /* + * Get the header in order to get signature and table size + */ + status = acpi_tb_get_table_header (address, &header); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Clear the table_info */ + + ACPI_MEMSET (table_info, 0, sizeof (struct acpi_table_desc)); + + /* + * Check the table signature and make sure it is recognized. + * Also checks the header checksum + */ + table_info->pointer = &header; + status = acpi_tb_recognize_table (table_info, ACPI_TABLE_PRIMARY); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Get the entire table */ + + status = acpi_tb_get_table_body (address, &header, table_info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Install the table */ + + status = acpi_tb_install_table (table_info); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_secondary_table + * + * PARAMETERS: Address - Physical address of table to retrieve + * *table_info - Where the table info is returned + * + * RETURN: Status + * + * DESCRIPTION: Maps the physical address of table into a logical address + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_secondary_table ( + struct acpi_pointer *address, + acpi_string signature, + struct acpi_table_desc *table_info) +{ + acpi_status status; + struct acpi_table_header header; + + + ACPI_FUNCTION_TRACE_STR ("tb_get_secondary_table", signature); + + + /* Get the header in order to match the signature */ + + status = acpi_tb_get_table_header (address, &header); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Signature must match request */ + + if (ACPI_STRNCMP (header.signature, signature, ACPI_NAME_SIZE)) { + ACPI_REPORT_ERROR (("Incorrect table signature - wanted [%s] found [%4.4s]\n", + signature, header.signature)); + return_ACPI_STATUS (AE_BAD_SIGNATURE); + } + + /* + * Check the table signature and make sure it is recognized. + * Also checks the header checksum + */ + table_info->pointer = &header; + status = acpi_tb_recognize_table (table_info, ACPI_TABLE_SECONDARY); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Get the entire table */ + + status = acpi_tb_get_table_body (address, &header, table_info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Install the table */ + + status = acpi_tb_install_table (table_info); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_required_tables + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Load and validate tables other than the RSDT. The RSDT must + * already be loaded and validated. + * + * Get the minimum set of ACPI tables, namely: + * + * 1) FADT (via RSDT in loop below) + * 2) FACS (via FADT) + * 3) DSDT (via FADT) + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_required_tables ( + void) +{ + acpi_status status = AE_OK; + u32 i; + struct acpi_table_desc table_info; + struct acpi_pointer address; + + + ACPI_FUNCTION_TRACE ("tb_get_required_tables"); + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "%d ACPI tables in RSDT\n", + acpi_gbl_rsdt_table_count)); + + + address.pointer_type = acpi_gbl_table_flags | ACPI_LOGICAL_ADDRESSING; + + /* + * Loop through all table pointers found in RSDT. + * This will NOT include the FACS and DSDT - we must get + * them after the loop. + * + * The only tables we are interested in getting here is the FADT and + * any SSDTs. + */ + for (i = 0; i < acpi_gbl_rsdt_table_count; i++) { + /* Get the table address from the common internal XSDT */ + + address.pointer.value = acpi_gbl_XSDT->table_offset_entry[i]; + + /* + * Get the tables needed by this subsystem (FADT and any SSDTs). + * NOTE: All other tables are completely ignored at this time. + */ + status = acpi_tb_get_primary_table (&address, &table_info); + if ((status != AE_OK) && (status != AE_TABLE_NOT_SUPPORTED)) { + ACPI_REPORT_WARNING (("%s, while getting table at %8.8X%8.8X\n", + acpi_format_exception (status), + ACPI_FORMAT_UINT64 (address.pointer.value))); + } + } + + /* We must have a FADT to continue */ + + if (!acpi_gbl_FADT) { + ACPI_REPORT_ERROR (("No FADT present in RSDT/XSDT\n")); + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + /* + * Convert the FADT to a common format. This allows earlier revisions of the + * table to coexist with newer versions, using common access code. + */ + status = acpi_tb_convert_table_fadt (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not convert FADT to internal common format\n")); + return_ACPI_STATUS (status); + } + + /* + * Get the FACS (Pointed to by the FADT) + */ + address.pointer.value = acpi_gbl_FADT->xfirmware_ctrl; + + status = acpi_tb_get_secondary_table (&address, FACS_SIG, &table_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not get/install the FACS, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + /* + * Create the common FACS pointer table + * (Contains pointers to the original table) + */ + status = acpi_tb_build_common_facs (&table_info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Get/install the DSDT (Pointed to by the FADT) + */ + address.pointer.value = acpi_gbl_FADT->Xdsdt; + + status = acpi_tb_get_secondary_table (&address, DSDT_SIG, &table_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not get/install the DSDT\n")); + return_ACPI_STATUS (status); + } + + /* Set Integer Width (32/64) based upon DSDT revision */ + + acpi_ut_set_integer_width (acpi_gbl_DSDT->revision); + + /* Dump the entire DSDT */ + + ACPI_DEBUG_PRINT ((ACPI_DB_TABLES, + "Hex dump of entire DSDT, size %d (0x%X), Integer width = %d\n", + acpi_gbl_DSDT->length, acpi_gbl_DSDT->length, acpi_gbl_integer_bit_width)); + ACPI_DUMP_BUFFER ((u8 *) acpi_gbl_DSDT, acpi_gbl_DSDT->length); + + /* Always delete the RSDP mapping, we are done with it */ + + acpi_tb_delete_tables_by_type (ACPI_TABLE_RSDP); + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/tables/tbinstal.c b/drivers/acpi/tables/tbinstal.c new file mode 100644 index 00000000000..85d5bb01022 --- /dev/null +++ b/drivers/acpi/tables/tbinstal.c @@ -0,0 +1,553 @@ +/****************************************************************************** + * + * Module Name: tbinstal - ACPI table installation and removal + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/actables.h> + + +#define _COMPONENT ACPI_TABLES + ACPI_MODULE_NAME ("tbinstal") + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_match_signature + * + * PARAMETERS: Signature - Table signature to match + * table_info - Return data + * + * RETURN: Status + * + * DESCRIPTION: Compare signature against the list of "ACPI-subsystem-owned" + * tables (DSDT/FADT/SSDT, etc.) Returns the table_type_iD on match. + * + ******************************************************************************/ + +acpi_status +acpi_tb_match_signature ( + char *signature, + struct acpi_table_desc *table_info, + u8 search_type) +{ + acpi_native_uint i; + + + ACPI_FUNCTION_TRACE ("tb_match_signature"); + + + /* + * Search for a signature match among the known table types + */ + for (i = 0; i < NUM_ACPI_TABLE_TYPES; i++) { + if (!(acpi_gbl_table_data[i].flags & search_type)) { + continue; + } + + if (!ACPI_STRNCMP (signature, acpi_gbl_table_data[i].signature, + acpi_gbl_table_data[i].sig_length)) { + /* Found a signature match, return index if requested */ + + if (table_info) { + table_info->type = (u8) i; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Table [%4.4s] is an ACPI table consumed by the core subsystem\n", + (char *) acpi_gbl_table_data[i].signature)); + + return_ACPI_STATUS (AE_OK); + } + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Table [%4.4s] is not an ACPI table consumed by the core subsystem - ignored\n", + (char *) signature)); + + return_ACPI_STATUS (AE_TABLE_NOT_SUPPORTED); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_install_table + * + * PARAMETERS: table_info - Return value from acpi_tb_get_table_body + * + * RETURN: Status + * + * DESCRIPTION: Load and validate all tables other than the RSDT. The RSDT must + * already be loaded and validated. + * Install the table into the global data structs. + * + ******************************************************************************/ + +acpi_status +acpi_tb_install_table ( + struct acpi_table_desc *table_info) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE ("tb_install_table"); + + + /* Lock tables while installing */ + + status = acpi_ut_acquire_mutex (ACPI_MTX_TABLES); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not acquire table mutex for [%4.4s], %s\n", + table_info->pointer->signature, acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + /* Install the table into the global data structure */ + + status = acpi_tb_init_table_descriptor (table_info->type, table_info); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Could not install ACPI table [%4.4s], %s\n", + table_info->pointer->signature, acpi_format_exception (status))); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "%s located at %p\n", + acpi_gbl_table_data[table_info->type].name, table_info->pointer)); + + (void) acpi_ut_release_mutex (ACPI_MTX_TABLES); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_recognize_table + * + * PARAMETERS: table_info - Return value from acpi_tb_get_table_body + * + * RETURN: Status + * + * DESCRIPTION: Check a table signature for a match against known table types + * + * NOTE: All table pointers are validated as follows: + * 1) Table pointer must point to valid physical memory + * 2) Signature must be 4 ASCII chars, even if we don't recognize the + * name + * 3) Table must be readable for length specified in the header + * 4) Table checksum must be valid (with the exception of the FACS + * which has no checksum for some odd reason) + * + ******************************************************************************/ + +acpi_status +acpi_tb_recognize_table ( + struct acpi_table_desc *table_info, + u8 search_type) +{ + struct acpi_table_header *table_header; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("tb_recognize_table"); + + + /* Ensure that we have a valid table pointer */ + + table_header = (struct acpi_table_header *) table_info->pointer; + if (!table_header) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * We only "recognize" a limited number of ACPI tables -- namely, the + * ones that are used by the subsystem (DSDT, FADT, etc.) + * + * An AE_TABLE_NOT_SUPPORTED means that the table was not recognized. + * This can be any one of many valid ACPI tables, it just isn't one of + * the tables that is consumed by the core subsystem + */ + status = acpi_tb_match_signature (table_header->signature, table_info, search_type); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + status = acpi_tb_validate_table_header (table_header); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Return the table type and length via the info struct */ + + table_info->length = (acpi_size) table_header->length; + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_init_table_descriptor + * + * PARAMETERS: table_type - The type of the table + * table_info - A table info struct + * + * RETURN: None. + * + * DESCRIPTION: Install a table into the global data structs. + * + ******************************************************************************/ + +acpi_status +acpi_tb_init_table_descriptor ( + acpi_table_type table_type, + struct acpi_table_desc *table_info) +{ + struct acpi_table_list *list_head; + struct acpi_table_desc *table_desc; + + + ACPI_FUNCTION_TRACE_U32 ("tb_init_table_descriptor", table_type); + + + /* Allocate a descriptor for this table */ + + table_desc = ACPI_MEM_CALLOCATE (sizeof (struct acpi_table_desc)); + if (!table_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* + * Install the table into the global data structure + */ + list_head = &acpi_gbl_table_lists[table_type]; + + /* + * Two major types of tables: 1) Only one instance is allowed. This + * includes most ACPI tables such as the DSDT. 2) Multiple instances of + * the table are allowed. This includes SSDT and PSDTs. + */ + if (ACPI_IS_SINGLE_TABLE (acpi_gbl_table_data[table_type].flags)) { + /* + * Only one table allowed, and a table has alread been installed + * at this location, so return an error. + */ + if (list_head->next) { + ACPI_MEM_FREE (table_desc); + return_ACPI_STATUS (AE_ALREADY_EXISTS); + } + + table_desc->next = list_head->next; + list_head->next = table_desc; + + if (table_desc->next) { + table_desc->next->prev = table_desc; + } + + list_head->count++; + } + else { + /* + * Link the new table in to the list of tables of this type. + * Insert at the end of the list, order IS IMPORTANT. + * + * table_desc->Prev & Next are already NULL from calloc() + */ + list_head->count++; + + if (!list_head->next) { + list_head->next = table_desc; + } + else { + table_desc->next = list_head->next; + + while (table_desc->next->next) { + table_desc->next = table_desc->next->next; + } + + table_desc->next->next = table_desc; + table_desc->prev = table_desc->next; + table_desc->next = NULL; + } + } + + /* Finish initialization of the table descriptor */ + + table_desc->type = (u8) table_type; + table_desc->pointer = table_info->pointer; + table_desc->length = table_info->length; + table_desc->allocation = table_info->allocation; + table_desc->aml_start = (u8 *) (table_desc->pointer + 1), + table_desc->aml_length = (u32) (table_desc->length - + (u32) sizeof (struct acpi_table_header)); + table_desc->table_id = acpi_ut_allocate_owner_id (ACPI_OWNER_TYPE_TABLE); + table_desc->loaded_into_namespace = FALSE; + + /* + * Set the appropriate global pointer (if there is one) to point to the + * newly installed table + */ + if (acpi_gbl_table_data[table_type].global_ptr) { + *(acpi_gbl_table_data[table_type].global_ptr) = table_info->pointer; + } + + /* Return Data */ + + table_info->table_id = table_desc->table_id; + table_info->installed_desc = table_desc; + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_delete_all_tables + * + * PARAMETERS: None. + * + * RETURN: None. + * + * DESCRIPTION: Delete all internal ACPI tables + * + ******************************************************************************/ + +void +acpi_tb_delete_all_tables (void) +{ + acpi_table_type type; + + + /* + * Free memory allocated for ACPI tables + * Memory can either be mapped or allocated + */ + for (type = 0; type < NUM_ACPI_TABLE_TYPES; type++) { + acpi_tb_delete_tables_by_type (type); + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_delete_tables_by_type + * + * PARAMETERS: Type - The table type to be deleted + * + * RETURN: None. + * + * DESCRIPTION: Delete an internal ACPI table + * Locks the ACPI table mutex + * + ******************************************************************************/ + +void +acpi_tb_delete_tables_by_type ( + acpi_table_type type) +{ + struct acpi_table_desc *table_desc; + u32 count; + u32 i; + + + ACPI_FUNCTION_TRACE_U32 ("tb_delete_tables_by_type", type); + + + if (type > ACPI_TABLE_MAX) { + return_VOID; + } + + if (ACPI_FAILURE (acpi_ut_acquire_mutex (ACPI_MTX_TABLES))) { + return; + } + + /* Clear the appropriate "typed" global table pointer */ + + switch (type) { + case ACPI_TABLE_RSDP: + acpi_gbl_RSDP = NULL; + break; + + case ACPI_TABLE_DSDT: + acpi_gbl_DSDT = NULL; + break; + + case ACPI_TABLE_FADT: + acpi_gbl_FADT = NULL; + break; + + case ACPI_TABLE_FACS: + acpi_gbl_FACS = NULL; + break; + + case ACPI_TABLE_XSDT: + acpi_gbl_XSDT = NULL; + break; + + case ACPI_TABLE_SSDT: + case ACPI_TABLE_PSDT: + default: + break; + } + + /* + * Free the table + * 1) Get the head of the list + */ + table_desc = acpi_gbl_table_lists[type].next; + count = acpi_gbl_table_lists[type].count; + + /* + * 2) Walk the entire list, deleting both the allocated tables + * and the table descriptors + */ + for (i = 0; i < count; i++) { + table_desc = acpi_tb_uninstall_table (table_desc); + } + + (void) acpi_ut_release_mutex (ACPI_MTX_TABLES); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_delete_single_table + * + * PARAMETERS: table_info - A table info struct + * + * RETURN: None. + * + * DESCRIPTION: Low-level free for a single ACPI table. Handles cases where + * the table was allocated a buffer or was mapped. + * + ******************************************************************************/ + +void +acpi_tb_delete_single_table ( + struct acpi_table_desc *table_desc) +{ + + /* Must have a valid table descriptor and pointer */ + + if ((!table_desc) || + (!table_desc->pointer)) { + return; + } + + /* Valid table, determine type of memory allocation */ + + switch (table_desc->allocation) { + case ACPI_MEM_NOT_ALLOCATED: + break; + + case ACPI_MEM_ALLOCATED: + + ACPI_MEM_FREE (table_desc->pointer); + break; + + case ACPI_MEM_MAPPED: + + acpi_os_unmap_memory (table_desc->pointer, table_desc->length); + break; + + default: + break; + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_uninstall_table + * + * PARAMETERS: table_info - A table info struct + * + * RETURN: Pointer to the next table in the list (of same type) + * + * DESCRIPTION: Free the memory associated with an internal ACPI table that + * is either installed or has never been installed. + * Table mutex should be locked. + * + ******************************************************************************/ + +struct acpi_table_desc * +acpi_tb_uninstall_table ( + struct acpi_table_desc *table_desc) +{ + struct acpi_table_desc *next_desc; + + + ACPI_FUNCTION_TRACE_PTR ("tb_uninstall_table", table_desc); + + + if (!table_desc) { + return_PTR (NULL); + } + + /* Unlink the descriptor from the doubly linked list */ + + if (table_desc->prev) { + table_desc->prev->next = table_desc->next; + } + else { + /* Is first on list, update list head */ + + acpi_gbl_table_lists[table_desc->type].next = table_desc->next; + } + + if (table_desc->next) { + table_desc->next->prev = table_desc->prev; + } + + /* Free the memory allocated for the table itself */ + + acpi_tb_delete_single_table (table_desc); + + /* Free the table descriptor */ + + next_desc = table_desc->next; + ACPI_MEM_FREE (table_desc); + + /* Return pointer to the next descriptor */ + + return_PTR (next_desc); +} + + diff --git a/drivers/acpi/tables/tbrsdt.c b/drivers/acpi/tables/tbrsdt.c new file mode 100644 index 00000000000..9c6913238d5 --- /dev/null +++ b/drivers/acpi/tables/tbrsdt.c @@ -0,0 +1,324 @@ +/****************************************************************************** + * + * Module Name: tbrsdt - ACPI RSDT table utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/actables.h> + + +#define _COMPONENT ACPI_TABLES + ACPI_MODULE_NAME ("tbrsdt") + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_verify_rsdp + * + * PARAMETERS: Address - RSDP (Pointer to RSDT) + * + * RETURN: Status + * + * DESCRIPTION: Load and validate the RSDP (ptr) and RSDT (table) + * + ******************************************************************************/ + +acpi_status +acpi_tb_verify_rsdp ( + struct acpi_pointer *address) +{ + struct acpi_table_desc table_info; + acpi_status status; + struct rsdp_descriptor *rsdp; + + + ACPI_FUNCTION_TRACE ("tb_verify_rsdp"); + + + switch (address->pointer_type) { + case ACPI_LOGICAL_POINTER: + + rsdp = address->pointer.logical; + break; + + case ACPI_PHYSICAL_POINTER: + /* + * Obtain access to the RSDP structure + */ + status = acpi_os_map_memory (address->pointer.physical, sizeof (struct rsdp_descriptor), + (void *) &rsdp); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + break; + + default: + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* + * The signature and checksum must both be correct + */ + if (ACPI_STRNCMP ((char *) rsdp, RSDP_SIG, sizeof (RSDP_SIG)-1) != 0) { + /* Nope, BAD Signature */ + + status = AE_BAD_SIGNATURE; + goto cleanup; + } + + /* Check the standard checksum */ + + if (acpi_tb_checksum (rsdp, ACPI_RSDP_CHECKSUM_LENGTH) != 0) { + status = AE_BAD_CHECKSUM; + goto cleanup; + } + + /* Check extended checksum if table version >= 2 */ + + if (rsdp->revision >= 2) { + if (acpi_tb_checksum (rsdp, ACPI_RSDP_XCHECKSUM_LENGTH) != 0) { + status = AE_BAD_CHECKSUM; + goto cleanup; + } + } + + /* The RSDP supplied is OK */ + + table_info.pointer = ACPI_CAST_PTR (struct acpi_table_header, rsdp); + table_info.length = sizeof (struct rsdp_descriptor); + table_info.allocation = ACPI_MEM_MAPPED; + + /* Save the table pointers and allocation info */ + + status = acpi_tb_init_table_descriptor (ACPI_TABLE_RSDP, &table_info); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Save the RSDP in a global for easy access */ + + acpi_gbl_RSDP = ACPI_CAST_PTR (struct rsdp_descriptor, table_info.pointer); + return_ACPI_STATUS (status); + + + /* Error exit */ +cleanup: + + if (acpi_gbl_table_flags & ACPI_PHYSICAL_POINTER) { + acpi_os_unmap_memory (rsdp, sizeof (struct rsdp_descriptor)); + } + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_rsdt_address + * + * PARAMETERS: None + * + * RETURN: RSDT physical address + * + * DESCRIPTION: Extract the address of the RSDT or XSDT, depending on the + * version of the RSDP + * + ******************************************************************************/ + +void +acpi_tb_get_rsdt_address ( + struct acpi_pointer *out_address) +{ + + ACPI_FUNCTION_ENTRY (); + + + out_address->pointer_type = acpi_gbl_table_flags | ACPI_LOGICAL_ADDRESSING; + + /* + * For RSDP revision 0 or 1, we use the RSDT. + * For RSDP revision 2 (and above), we use the XSDT + */ + if (acpi_gbl_RSDP->revision < 2) { + out_address->pointer.value = acpi_gbl_RSDP->rsdt_physical_address; + } + else { + out_address->pointer.value = acpi_gbl_RSDP->xsdt_physical_address; + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_validate_rsdt + * + * PARAMETERS: table_ptr - Addressable pointer to the RSDT. + * + * RETURN: Status + * + * DESCRIPTION: Validate signature for the RSDT or XSDT + * + ******************************************************************************/ + +acpi_status +acpi_tb_validate_rsdt ( + struct acpi_table_header *table_ptr) +{ + int no_match; + + + ACPI_FUNCTION_NAME ("tb_validate_rsdt"); + + + /* + * For RSDP revision 0 or 1, we use the RSDT. + * For RSDP revision 2 and above, we use the XSDT + */ + if (acpi_gbl_RSDP->revision < 2) { + no_match = ACPI_STRNCMP ((char *) table_ptr, RSDT_SIG, + sizeof (RSDT_SIG) -1); + } + else { + no_match = ACPI_STRNCMP ((char *) table_ptr, XSDT_SIG, + sizeof (XSDT_SIG) -1); + } + + if (no_match) { + /* Invalid RSDT or XSDT signature */ + + ACPI_REPORT_ERROR (("Invalid signature where RSDP indicates RSDT/XSDT should be located\n")); + + ACPI_DUMP_BUFFER (acpi_gbl_RSDP, 20); + + ACPI_DEBUG_PRINT_RAW ((ACPI_DB_ERROR, + "RSDT/XSDT signature at %X (%p) is invalid\n", + acpi_gbl_RSDP->rsdt_physical_address, + (void *) (acpi_native_uint) acpi_gbl_RSDP->rsdt_physical_address)); + + if (acpi_gbl_RSDP->revision < 2) { + ACPI_REPORT_ERROR (("Looking for RSDT (RSDP->Rev < 2)\n")) + } + else { + ACPI_REPORT_ERROR (("Looking for XSDT (RSDP->Rev >= 2)\n")) + } + + ACPI_DUMP_BUFFER ((char *) table_ptr, 48); + + return (AE_BAD_SIGNATURE); + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_get_table_rsdt + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Load and validate the RSDP (ptr) and RSDT (table) + * + ******************************************************************************/ + +acpi_status +acpi_tb_get_table_rsdt ( + void) +{ + struct acpi_table_desc table_info; + acpi_status status; + struct acpi_pointer address; + + + ACPI_FUNCTION_TRACE ("tb_get_table_rsdt"); + + + /* Get the RSDT/XSDT via the RSDP */ + + acpi_tb_get_rsdt_address (&address); + + table_info.type = ACPI_TABLE_XSDT; + status = acpi_tb_get_table (&address, &table_info); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not get the RSDT/XSDT, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "RSDP located at %p, points to RSDT physical=%8.8X%8.8X \n", + acpi_gbl_RSDP, + ACPI_FORMAT_UINT64 (address.pointer.value))); + + /* Check the RSDT or XSDT signature */ + + status = acpi_tb_validate_rsdt (table_info.pointer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Get the number of tables defined in the RSDT or XSDT */ + + acpi_gbl_rsdt_table_count = acpi_tb_get_table_count (acpi_gbl_RSDP, table_info.pointer); + + /* Convert and/or copy to an XSDT structure */ + + status = acpi_tb_convert_to_xsdt (&table_info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Save the table pointers and allocation info */ + + status = acpi_tb_init_table_descriptor (ACPI_TABLE_XSDT, &table_info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + acpi_gbl_XSDT = ACPI_CAST_PTR (XSDT_DESCRIPTOR, table_info.pointer); + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "XSDT located at %p\n", acpi_gbl_XSDT)); + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/tables/tbutils.c b/drivers/acpi/tables/tbutils.c new file mode 100644 index 00000000000..fede5804c78 --- /dev/null +++ b/drivers/acpi/tables/tbutils.c @@ -0,0 +1,240 @@ +/****************************************************************************** + * + * Module Name: tbutils - Table manipulation utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/actables.h> + + +#define _COMPONENT ACPI_TABLES + ACPI_MODULE_NAME ("tbutils") + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_handle_to_object + * + * PARAMETERS: table_id - Id for which the function is searching + * table_desc - Pointer to return the matching table + * descriptor. + * + * RETURN: Search the tables to find one with a matching table_id and + * return a pointer to that table descriptor. + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_tb_handle_to_object ( + u16 table_id, + struct acpi_table_desc **return_table_desc) +{ + u32 i; + struct acpi_table_desc *table_desc; + + + ACPI_FUNCTION_NAME ("tb_handle_to_object"); + + + for (i = 0; i < ACPI_TABLE_MAX; i++) { + table_desc = acpi_gbl_table_lists[i].next; + while (table_desc) { + if (table_desc->table_id == table_id) { + *return_table_desc = table_desc; + return (AE_OK); + } + + table_desc = table_desc->next; + } + } + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "table_id=%X does not exist\n", table_id)); + return (AE_BAD_PARAMETER); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_validate_table_header + * + * PARAMETERS: table_header - Logical pointer to the table + * + * RETURN: Status + * + * DESCRIPTION: Check an ACPI table header for validity + * + * NOTE: Table pointers are validated as follows: + * 1) Table pointer must point to valid physical memory + * 2) Signature must be 4 ASCII chars, even if we don't recognize the + * name + * 3) Table must be readable for length specified in the header + * 4) Table checksum must be valid (with the exception of the FACS + * which has no checksum because it contains variable fields) + * + ******************************************************************************/ + +acpi_status +acpi_tb_validate_table_header ( + struct acpi_table_header *table_header) +{ + acpi_name signature; + + + ACPI_FUNCTION_NAME ("tb_validate_table_header"); + + + /* Verify that this is a valid address */ + + if (!acpi_os_readable (table_header, sizeof (struct acpi_table_header))) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Cannot read table header at %p\n", table_header)); + return (AE_BAD_ADDRESS); + } + + /* Ensure that the signature is 4 ASCII characters */ + + ACPI_MOVE_32_TO_32 (&signature, table_header->signature); + if (!acpi_ut_valid_acpi_name (signature)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Table signature at %p [%p] has invalid characters\n", + table_header, &signature)); + + ACPI_REPORT_WARNING (("Invalid table signature found: [%4.4s]\n", + (char *) &signature)); + ACPI_DUMP_BUFFER (table_header, sizeof (struct acpi_table_header)); + return (AE_BAD_SIGNATURE); + } + + /* Validate the table length */ + + if (table_header->length < sizeof (struct acpi_table_header)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Invalid length in table header %p name %4.4s\n", + table_header, (char *) &signature)); + + ACPI_REPORT_WARNING (("Invalid table header length (0x%X) found\n", + (u32) table_header->length)); + ACPI_DUMP_BUFFER (table_header, sizeof (struct acpi_table_header)); + return (AE_BAD_HEADER); + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_verify_table_checksum + * + * PARAMETERS: *table_header - ACPI table to verify + * + * RETURN: 8 bit checksum of table + * + * DESCRIPTION: Does an 8 bit checksum of table and returns status. A correct + * table should have a checksum of 0. + * + ******************************************************************************/ + +acpi_status +acpi_tb_verify_table_checksum ( + struct acpi_table_header *table_header) +{ + u8 checksum; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("tb_verify_table_checksum"); + + + /* Compute the checksum on the table */ + + checksum = acpi_tb_checksum (table_header, table_header->length); + + /* Return the appropriate exception */ + + if (checksum) { + ACPI_REPORT_WARNING (("Invalid checksum in table [%4.4s] (%02X, sum %02X is not zero)\n", + table_header->signature, (u32) table_header->checksum, (u32) checksum)); + + status = AE_BAD_CHECKSUM; + } + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_checksum + * + * PARAMETERS: Buffer - Buffer to checksum + * Length - Size of the buffer + * + * RETURNS 8 bit checksum of buffer + * + * DESCRIPTION: Computes an 8 bit checksum of the buffer(length) and returns it. + * + ******************************************************************************/ + +u8 +acpi_tb_checksum ( + void *buffer, + u32 length) +{ + const u8 *limit; + const u8 *rover; + u8 sum = 0; + + + if (buffer && length) { + /* Buffer and Length are valid */ + + limit = (u8 *) buffer + length; + + for (rover = buffer; rover < limit; rover++) { + sum = (u8) (sum + *rover); + } + } + return (sum); +} + + diff --git a/drivers/acpi/tables/tbxface.c b/drivers/acpi/tables/tbxface.c new file mode 100644 index 00000000000..7715043461c --- /dev/null +++ b/drivers/acpi/tables/tbxface.c @@ -0,0 +1,448 @@ +/****************************************************************************** + * + * Module Name: tbxface - Public interfaces to the ACPI subsystem + * ACPI table oriented interfaces + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/actables.h> + + +#define _COMPONENT ACPI_TABLES + ACPI_MODULE_NAME ("tbxface") + + +/******************************************************************************* + * + * FUNCTION: acpi_load_tables + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: This function is called to load the ACPI tables from the + * provided RSDT + * + ******************************************************************************/ + +acpi_status +acpi_load_tables (void) +{ + struct acpi_pointer rsdp_address; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_load_tables"); + + + /* Get the RSDP */ + + status = acpi_os_get_root_pointer (ACPI_LOGICAL_ADDRESSING, + &rsdp_address); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("acpi_load_tables: Could not get RSDP, %s\n", + acpi_format_exception (status))); + goto error_exit; + } + + /* Map and validate the RSDP */ + + acpi_gbl_table_flags = rsdp_address.pointer_type; + + status = acpi_tb_verify_rsdp (&rsdp_address); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("acpi_load_tables: RSDP Failed validation: %s\n", + acpi_format_exception (status))); + goto error_exit; + } + + /* Get the RSDT via the RSDP */ + + status = acpi_tb_get_table_rsdt (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("acpi_load_tables: Could not load RSDT: %s\n", + acpi_format_exception (status))); + goto error_exit; + } + + /* Now get the tables needed by this subsystem (FADT, DSDT, etc.) */ + + status = acpi_tb_get_required_tables (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("acpi_load_tables: Error getting required tables (DSDT/FADT/FACS): %s\n", + acpi_format_exception (status))); + goto error_exit; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INIT, "ACPI Tables successfully acquired\n")); + + + /* Load the namespace from the tables */ + + status = acpi_ns_load_namespace (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("acpi_load_tables: Could not load namespace: %s\n", + acpi_format_exception (status))); + goto error_exit; + } + + return_ACPI_STATUS (AE_OK); + + +error_exit: + ACPI_REPORT_ERROR (("acpi_load_tables: Could not load tables: %s\n", + acpi_format_exception (status))); + + return_ACPI_STATUS (status); +} + + +#ifdef ACPI_FUTURE_USAGE + +/******************************************************************************* + * + * FUNCTION: acpi_load_table + * + * PARAMETERS: table_ptr - pointer to a buffer containing the entire + * table to be loaded + * + * RETURN: Status + * + * DESCRIPTION: This function is called to load a table from the caller's + * buffer. The buffer must contain an entire ACPI Table including + * a valid header. The header fields will be verified, and if it + * is determined that the table is invalid, the call will fail. + * + ******************************************************************************/ + +acpi_status +acpi_load_table ( + struct acpi_table_header *table_ptr) +{ + acpi_status status; + struct acpi_table_desc table_info; + struct acpi_pointer address; + + + ACPI_FUNCTION_TRACE ("acpi_load_table"); + + + if (!table_ptr) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Copy the table to a local buffer */ + + address.pointer_type = ACPI_LOGICAL_POINTER | ACPI_LOGICAL_ADDRESSING; + address.pointer.logical = table_ptr; + + status = acpi_tb_get_table_body (&address, table_ptr, &table_info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Install the new table into the local data structures */ + + status = acpi_tb_install_table (&table_info); + if (ACPI_FAILURE (status)) { + /* Free table allocated by acpi_tb_get_table_body */ + + acpi_tb_delete_single_table (&table_info); + return_ACPI_STATUS (status); + } + + /* Convert the table to common format if necessary */ + + switch (table_info.type) { + case ACPI_TABLE_FADT: + + status = acpi_tb_convert_table_fadt (); + break; + + case ACPI_TABLE_FACS: + + status = acpi_tb_build_common_facs (&table_info); + break; + + default: + /* Load table into namespace if it contains executable AML */ + + status = acpi_ns_load_table (table_info.installed_desc, acpi_gbl_root_node); + break; + } + + if (ACPI_FAILURE (status)) { + /* Uninstall table and free the buffer */ + + (void) acpi_tb_uninstall_table (table_info.installed_desc); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_unload_table + * + * PARAMETERS: table_type - Type of table to be unloaded + * + * RETURN: Status + * + * DESCRIPTION: This routine is used to force the unload of a table + * + ******************************************************************************/ + +acpi_status +acpi_unload_table ( + acpi_table_type table_type) +{ + struct acpi_table_desc *table_desc; + + + ACPI_FUNCTION_TRACE ("acpi_unload_table"); + + + /* Parameter validation */ + + if (table_type > ACPI_TABLE_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + + /* Find all tables of the requested type */ + + table_desc = acpi_gbl_table_lists[table_type].next; + while (table_desc) { + /* + * Delete all namespace entries owned by this table. Note that these + * entries can appear anywhere in the namespace by virtue of the AML + * "Scope" operator. Thus, we need to track ownership by an ID, not + * simply a position within the hierarchy + */ + acpi_ns_delete_namespace_by_owner (table_desc->table_id); + + table_desc = table_desc->next; + } + + /* Delete (or unmap) all tables of this type */ + + acpi_tb_delete_tables_by_type (table_type); + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_get_table_header + * + * PARAMETERS: table_type - one of the defined table types + * Instance - the non zero instance of the table, allows + * support for multiple tables of the same type + * see acpi_gbl_acpi_table_flag + * out_table_header - pointer to the struct acpi_table_header if successful + * + * DESCRIPTION: This function is called to get an ACPI table header. The caller + * supplies an pointer to a data area sufficient to contain an ACPI + * struct acpi_table_header structure. + * + * The header contains a length field that can be used to determine + * the size of the buffer needed to contain the entire table. This + * function is not valid for the RSD PTR table since it does not + * have a standard header and is fixed length. + * + ******************************************************************************/ + +acpi_status +acpi_get_table_header ( + acpi_table_type table_type, + u32 instance, + struct acpi_table_header *out_table_header) +{ + struct acpi_table_header *tbl_ptr; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_get_table_header"); + + + if ((instance == 0) || + (table_type == ACPI_TABLE_RSDP) || + (!out_table_header)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Check the table type and instance */ + + if ((table_type > ACPI_TABLE_MAX) || + (ACPI_IS_SINGLE_TABLE (acpi_gbl_table_data[table_type].flags) && + instance > 1)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + + /* Get a pointer to the entire table */ + + status = acpi_tb_get_table_ptr (table_type, instance, &tbl_ptr); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * The function will return a NULL pointer if the table is not loaded + */ + if (tbl_ptr == NULL) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + + /* + * Copy the header to the caller's buffer + */ + ACPI_MEMCPY ((void *) out_table_header, (void *) tbl_ptr, + sizeof (struct acpi_table_header)); + + return_ACPI_STATUS (status); +} + + +#endif /* ACPI_FUTURE_USAGE */ + +/******************************************************************************* + * + * FUNCTION: acpi_get_table + * + * PARAMETERS: table_type - one of the defined table types + * Instance - the non zero instance of the table, allows + * support for multiple tables of the same type + * see acpi_gbl_acpi_table_flag + * ret_buffer - pointer to a structure containing a buffer to + * receive the table + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get an ACPI table. The caller + * supplies an out_buffer large enough to contain the entire ACPI + * table. The caller should call the acpi_get_table_header function + * first to determine the buffer size needed. Upon completion + * the out_buffer->Length field will indicate the number of bytes + * copied into the out_buffer->buf_ptr buffer. This table will be + * a complete table including the header. + * + ******************************************************************************/ + +acpi_status +acpi_get_table ( + acpi_table_type table_type, + u32 instance, + struct acpi_buffer *ret_buffer) +{ + struct acpi_table_header *tbl_ptr; + acpi_status status; + acpi_size table_length; + + + ACPI_FUNCTION_TRACE ("acpi_get_table"); + + + /* Parameter validation */ + + if (instance == 0) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_ut_validate_buffer (ret_buffer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Check the table type and instance */ + + if ((table_type > ACPI_TABLE_MAX) || + (ACPI_IS_SINGLE_TABLE (acpi_gbl_table_data[table_type].flags) && + instance > 1)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + + /* Get a pointer to the entire table */ + + status = acpi_tb_get_table_ptr (table_type, instance, &tbl_ptr); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * acpi_tb_get_table_ptr will return a NULL pointer if the + * table is not loaded. + */ + if (tbl_ptr == NULL) { + return_ACPI_STATUS (AE_NOT_EXIST); + } + + /* Get the table length */ + + if (table_type == ACPI_TABLE_RSDP) { + /* + * RSD PTR is the only "table" without a header + */ + table_length = sizeof (struct rsdp_descriptor); + } + else { + table_length = (acpi_size) tbl_ptr->length; + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer (ret_buffer, table_length); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Copy the table to the buffer */ + + ACPI_MEMCPY ((void *) ret_buffer->pointer, (void *) tbl_ptr, table_length); + return_ACPI_STATUS (AE_OK); +} +EXPORT_SYMBOL(acpi_get_table); + diff --git a/drivers/acpi/tables/tbxfroot.c b/drivers/acpi/tables/tbxfroot.c new file mode 100644 index 00000000000..6e8072ebbac --- /dev/null +++ b/drivers/acpi/tables/tbxfroot.c @@ -0,0 +1,606 @@ +/****************************************************************************** + * + * Module Name: tbxfroot - Find the root ACPI table (RSDT) + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/actables.h> + + +#define _COMPONENT ACPI_TABLES + ACPI_MODULE_NAME ("tbxfroot") + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_find_table + * + * PARAMETERS: Signature - String with ACPI table signature + * oem_id - String with the table OEM ID + * oem_table_id - String with the OEM Table ID. + * + * RETURN: Status + * + * DESCRIPTION: Find an ACPI table (in the RSDT/XSDT) that matches the + * Signature, OEM ID and OEM Table ID. + * + ******************************************************************************/ + +acpi_status +acpi_tb_find_table ( + char *signature, + char *oem_id, + char *oem_table_id, + struct acpi_table_header **table_ptr) +{ + acpi_status status; + struct acpi_table_header *table; + + + ACPI_FUNCTION_TRACE ("tb_find_table"); + + + /* Validate string lengths */ + + if ((ACPI_STRLEN (signature) > ACPI_NAME_SIZE) || + (ACPI_STRLEN (oem_id) > sizeof (table->oem_id)) || + (ACPI_STRLEN (oem_table_id) > sizeof (table->oem_table_id))) { + return_ACPI_STATUS (AE_AML_STRING_LIMIT); + } + + if (!ACPI_STRNCMP (signature, DSDT_SIG, ACPI_NAME_SIZE)) { + /* + * The DSDT pointer is contained in the FADT, not the RSDT. + * This code should suffice, because the only code that would perform + * a "find" on the DSDT is the data_table_region() AML opcode -- in + * which case, the DSDT is guaranteed to be already loaded. + * If this becomes insufficient, the FADT will have to be found first. + */ + if (!acpi_gbl_DSDT) { + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + table = acpi_gbl_DSDT; + } + else { + /* Find the table */ + + status = acpi_get_firmware_table (signature, 1, + ACPI_LOGICAL_ADDRESSING, &table); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Check oem_id and oem_table_id */ + + if ((oem_id[0] && ACPI_STRNCMP ( + oem_id, table->oem_id, sizeof (table->oem_id))) || + (oem_table_id[0] && ACPI_STRNCMP ( + oem_table_id, table->oem_table_id, sizeof (table->oem_table_id)))) { + return_ACPI_STATUS (AE_AML_NAME_NOT_FOUND); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_TABLES, "Found table [%4.4s]\n", table->signature)); + *table_ptr = table; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_get_firmware_table + * + * PARAMETERS: Signature - Any ACPI table signature + * Instance - the non zero instance of the table, allows + * support for multiple tables of the same type + * Flags - Physical/Virtual support + * table_pointer - Where a buffer containing the table is + * returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to get an ACPI table. A buffer is + * allocated for the table and returned in table_pointer. + * This table will be a complete table including the header. + * + ******************************************************************************/ + +acpi_status +acpi_get_firmware_table ( + acpi_string signature, + u32 instance, + u32 flags, + struct acpi_table_header **table_pointer) +{ + acpi_status status; + struct acpi_pointer address; + struct acpi_table_header *header = NULL; + struct acpi_table_desc *table_info = NULL; + struct acpi_table_desc *rsdt_info; + u32 table_count; + u32 i; + u32 j; + + + ACPI_FUNCTION_TRACE ("acpi_get_firmware_table"); + + + /* + * Ensure that at least the table manager is initialized. We don't + * require that the entire ACPI subsystem is up for this interface. + * If we have a buffer, we must have a length too + */ + if ((instance == 0) || + (!signature) || + (!table_pointer)) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Ensure that we have a RSDP */ + + if (!acpi_gbl_RSDP) { + /* Get the RSDP */ + + status = acpi_os_get_root_pointer (flags, &address); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "RSDP not found\n")); + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + /* Map and validate the RSDP */ + + if ((flags & ACPI_MEMORY_MODE) == ACPI_LOGICAL_ADDRESSING) { + status = acpi_os_map_memory (address.pointer.physical, sizeof (struct rsdp_descriptor), + (void *) &acpi_gbl_RSDP); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + else { + acpi_gbl_RSDP = address.pointer.logical; + } + + /* The signature and checksum must both be correct */ + + if (ACPI_STRNCMP ((char *) acpi_gbl_RSDP, RSDP_SIG, sizeof (RSDP_SIG)-1) != 0) { + /* Nope, BAD Signature */ + + return_ACPI_STATUS (AE_BAD_SIGNATURE); + } + + if (acpi_tb_checksum (acpi_gbl_RSDP, ACPI_RSDP_CHECKSUM_LENGTH) != 0) { + /* Nope, BAD Checksum */ + + return_ACPI_STATUS (AE_BAD_CHECKSUM); + } + } + + /* Get the RSDT address via the RSDP */ + + acpi_tb_get_rsdt_address (&address); + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "RSDP located at %p, RSDT physical=%8.8X%8.8X \n", + acpi_gbl_RSDP, + ACPI_FORMAT_UINT64 (address.pointer.value))); + + /* Insert processor_mode flags */ + + address.pointer_type |= flags; + + /* Get and validate the RSDT */ + + rsdt_info = ACPI_MEM_CALLOCATE (sizeof (struct acpi_table_desc)); + if (!rsdt_info) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + status = acpi_tb_get_table (&address, rsdt_info); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + status = acpi_tb_validate_rsdt (rsdt_info->pointer); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Allocate a scratch table header and table descriptor */ + + header = ACPI_MEM_ALLOCATE (sizeof (struct acpi_table_header)); + if (!header) { + status = AE_NO_MEMORY; + goto cleanup; + } + + table_info = ACPI_MEM_ALLOCATE (sizeof (struct acpi_table_desc)); + if (!table_info) { + status = AE_NO_MEMORY; + goto cleanup; + } + + /* Get the number of table pointers within the RSDT */ + + table_count = acpi_tb_get_table_count (acpi_gbl_RSDP, rsdt_info->pointer); + address.pointer_type = acpi_gbl_table_flags | flags; + + /* + * Search the RSDT/XSDT for the correct instance of the + * requested table + */ + for (i = 0, j = 0; i < table_count; i++) { + /* Get the next table pointer, handle RSDT vs. XSDT */ + + if (acpi_gbl_RSDP->revision < 2) { + address.pointer.value = (ACPI_CAST_PTR ( + RSDT_DESCRIPTOR, rsdt_info->pointer))->table_offset_entry[i]; + } + else { + address.pointer.value = (ACPI_CAST_PTR ( + XSDT_DESCRIPTOR, rsdt_info->pointer))->table_offset_entry[i]; + } + + /* Get the table header */ + + status = acpi_tb_get_table_header (&address, header); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + /* Compare table signatures and table instance */ + + if (!ACPI_STRNCMP (header->signature, signature, ACPI_NAME_SIZE)) { + /* An instance of the table was found */ + + j++; + if (j >= instance) { + /* Found the correct instance, get the entire table */ + + status = acpi_tb_get_table_body (&address, header, table_info); + if (ACPI_FAILURE (status)) { + goto cleanup; + } + + *table_pointer = table_info->pointer; + goto cleanup; + } + } + } + + /* Did not find the table */ + + status = AE_NOT_EXIST; + + +cleanup: + acpi_os_unmap_memory (rsdt_info->pointer, (acpi_size) rsdt_info->pointer->length); + ACPI_MEM_FREE (rsdt_info); + + if (header) { + ACPI_MEM_FREE (header); + } + if (table_info) { + ACPI_MEM_FREE (table_info); + } + return_ACPI_STATUS (status); +} +EXPORT_SYMBOL(acpi_get_firmware_table); + + +/* TBD: Move to a new file */ + +#if ACPI_MACHINE_WIDTH != 16 + +/******************************************************************************* + * + * FUNCTION: acpi_find_root_pointer + * + * PARAMETERS: **rsdp_address - Where to place the RSDP address + * Flags - Logical/Physical addressing + * + * RETURN: Status, Physical address of the RSDP + * + * DESCRIPTION: Find the RSDP + * + ******************************************************************************/ + +acpi_status +acpi_find_root_pointer ( + u32 flags, + struct acpi_pointer *rsdp_address) +{ + struct acpi_table_desc table_info; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_find_root_pointer"); + + + /* Get the RSDP */ + + status = acpi_tb_find_rsdp (&table_info, flags); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "RSDP structure not found, %s Flags=%X\n", + acpi_format_exception (status), flags)); + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } + + rsdp_address->pointer_type = ACPI_PHYSICAL_POINTER; + rsdp_address->pointer.physical = table_info.physical_address; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_scan_memory_for_rsdp + * + * PARAMETERS: start_address - Starting pointer for search + * Length - Maximum length to search + * + * RETURN: Pointer to the RSDP if found, otherwise NULL. + * + * DESCRIPTION: Search a block of memory for the RSDP signature + * + ******************************************************************************/ + +u8 * +acpi_tb_scan_memory_for_rsdp ( + u8 *start_address, + u32 length) +{ + u8 *mem_rover; + u8 *end_address; + u8 checksum; + + + ACPI_FUNCTION_TRACE ("tb_scan_memory_for_rsdp"); + + + end_address = start_address + length; + + /* Search from given start address for the requested length */ + + for (mem_rover = start_address; mem_rover < end_address; + mem_rover += ACPI_RSDP_SCAN_STEP) { + /* The signature and checksum must both be correct */ + + if (ACPI_STRNCMP ((char *) mem_rover, RSDP_SIG, sizeof (RSDP_SIG)-1) != 0) { + /* No signature match, keep looking */ + + continue; + } + + /* Signature matches, check the appropriate checksum */ + + if ((ACPI_CAST_PTR (struct rsdp_descriptor, mem_rover))->revision < 2) { + /* ACPI version 1.0 */ + + checksum = acpi_tb_checksum (mem_rover, ACPI_RSDP_CHECKSUM_LENGTH); + } + else { + /* Post ACPI 1.0, use extended_checksum */ + + checksum = acpi_tb_checksum (mem_rover, ACPI_RSDP_XCHECKSUM_LENGTH); + } + + if (checksum == 0) { + /* Checksum valid, we have found a valid RSDP */ + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "RSDP located at physical address %p\n", mem_rover)); + return_PTR (mem_rover); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Found an RSDP at physical address %p, but it has a bad checksum\n", + mem_rover)); + } + + /* Searched entire block, no RSDP was found */ + + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "Searched entire block, no valid RSDP was found.\n")); + return_PTR (NULL); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_tb_find_rsdp + * + * PARAMETERS: *table_info - Where the table info is returned + * Flags - Current memory mode (logical vs. + * physical addressing) + * + * RETURN: Status, RSDP physical address + * + * DESCRIPTION: search lower 1_mbyte of memory for the root system descriptor + * pointer structure. If it is found, set *RSDP to point to it. + * + * NOTE1: The RSDp must be either in the first 1_k of the Extended + * BIOS Data Area or between E0000 and FFFFF (From ACPI Spec.) + * Only a 32-bit physical address is necessary. + * + * NOTE2: This function is always available, regardless of the + * initialization state of the rest of ACPI. + * + ******************************************************************************/ + +acpi_status +acpi_tb_find_rsdp ( + struct acpi_table_desc *table_info, + u32 flags) +{ + u8 *table_ptr; + u8 *mem_rover; + u32 physical_address; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("tb_find_rsdp"); + + + /* + * Scan supports either 1) Logical addressing or 2) Physical addressing + */ + if ((flags & ACPI_MEMORY_MODE) == ACPI_LOGICAL_ADDRESSING) { + /* + * 1a) Get the location of the EBDA + */ + status = acpi_os_map_memory ((acpi_physical_address) ACPI_EBDA_PTR_LOCATION, + ACPI_EBDA_PTR_LENGTH, + (void *) &table_ptr); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not map memory at %8.8X for length %X\n", + ACPI_EBDA_PTR_LOCATION, ACPI_EBDA_PTR_LENGTH)); + return_ACPI_STATUS (status); + } + + ACPI_MOVE_16_TO_32 (&physical_address, table_ptr); + physical_address <<= 4; /* Convert segment to physical address */ + acpi_os_unmap_memory (table_ptr, ACPI_EBDA_PTR_LENGTH); + + /* EBDA present? */ + + if (physical_address > 0x400) { + /* + * 1b) Search EBDA paragraphs (EBDa is required to be a minimum of 1_k length) + */ + status = acpi_os_map_memory ((acpi_physical_address) physical_address, + ACPI_EBDA_WINDOW_SIZE, + (void *) &table_ptr); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not map memory at %8.8X for length %X\n", + physical_address, ACPI_EBDA_WINDOW_SIZE)); + return_ACPI_STATUS (status); + } + + mem_rover = acpi_tb_scan_memory_for_rsdp (table_ptr, ACPI_EBDA_WINDOW_SIZE); + acpi_os_unmap_memory (table_ptr, ACPI_EBDA_WINDOW_SIZE); + + if (mem_rover) { + /* Found it, return the physical address */ + + physical_address += ACPI_PTR_DIFF (mem_rover, table_ptr); + + table_info->physical_address = (acpi_physical_address) physical_address; + return_ACPI_STATUS (AE_OK); + } + } + + /* + * 2) Search upper memory: 16-byte boundaries in E0000h-FFFFFh + */ + status = acpi_os_map_memory ((acpi_physical_address) ACPI_HI_RSDP_WINDOW_BASE, + ACPI_HI_RSDP_WINDOW_SIZE, + (void *) &table_ptr); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Could not map memory at %8.8X for length %X\n", + ACPI_HI_RSDP_WINDOW_BASE, ACPI_HI_RSDP_WINDOW_SIZE)); + return_ACPI_STATUS (status); + } + + mem_rover = acpi_tb_scan_memory_for_rsdp (table_ptr, ACPI_HI_RSDP_WINDOW_SIZE); + acpi_os_unmap_memory (table_ptr, ACPI_HI_RSDP_WINDOW_SIZE); + + if (mem_rover) { + /* Found it, return the physical address */ + + physical_address = ACPI_HI_RSDP_WINDOW_BASE + ACPI_PTR_DIFF (mem_rover, table_ptr); + + table_info->physical_address = (acpi_physical_address) physical_address; + return_ACPI_STATUS (AE_OK); + } + } + + /* + * Physical addressing + */ + else { + /* + * 1a) Get the location of the EBDA + */ + ACPI_MOVE_16_TO_32 (&physical_address, ACPI_EBDA_PTR_LOCATION); + physical_address <<= 4; /* Convert segment to physical address */ + + /* EBDA present? */ + + if (physical_address > 0x400) { + /* + * 1b) Search EBDA paragraphs (EBDa is required to be a minimum of 1_k length) + */ + mem_rover = acpi_tb_scan_memory_for_rsdp (ACPI_PHYSADDR_TO_PTR (physical_address), + ACPI_EBDA_WINDOW_SIZE); + if (mem_rover) { + /* Found it, return the physical address */ + + table_info->physical_address = ACPI_TO_INTEGER (mem_rover); + return_ACPI_STATUS (AE_OK); + } + } + + /* + * 2) Search upper memory: 16-byte boundaries in E0000h-FFFFFh + */ + mem_rover = acpi_tb_scan_memory_for_rsdp (ACPI_PHYSADDR_TO_PTR (ACPI_HI_RSDP_WINDOW_BASE), + ACPI_HI_RSDP_WINDOW_SIZE); + if (mem_rover) { + /* Found it, return the physical address */ + + table_info->physical_address = ACPI_TO_INTEGER (mem_rover); + return_ACPI_STATUS (AE_OK); + } + } + + /* RSDP signature was not found */ + + return_ACPI_STATUS (AE_NOT_FOUND); +} + +#endif + diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c new file mode 100644 index 00000000000..79c3a686bc4 --- /dev/null +++ b/drivers/acpi/thermal.c @@ -0,0 +1,1445 @@ +/* + * acpi_thermal.c - ACPI Thermal Zone Driver ($Revision: 41 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * This driver fully implements the ACPI thermal policy as described in the + * ACPI 2.0 Specification. + * + * TBD: 1. Implement passive cooling hysteresis. + * 2. Enhance passive cooling (CPU) states/limit interface to support + * concepts of 'multiple limiters', upper/lower limits, etc. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <linux/sched.h> +#include <linux/kmod.h> +#include <linux/seq_file.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +#define ACPI_THERMAL_COMPONENT 0x04000000 +#define ACPI_THERMAL_CLASS "thermal_zone" +#define ACPI_THERMAL_DRIVER_NAME "ACPI Thermal Zone Driver" +#define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" +#define ACPI_THERMAL_FILE_STATE "state" +#define ACPI_THERMAL_FILE_TEMPERATURE "temperature" +#define ACPI_THERMAL_FILE_TRIP_POINTS "trip_points" +#define ACPI_THERMAL_FILE_COOLING_MODE "cooling_mode" +#define ACPI_THERMAL_FILE_POLLING_FREQ "polling_frequency" +#define ACPI_THERMAL_NOTIFY_TEMPERATURE 0x80 +#define ACPI_THERMAL_NOTIFY_THRESHOLDS 0x81 +#define ACPI_THERMAL_NOTIFY_DEVICES 0x82 +#define ACPI_THERMAL_NOTIFY_CRITICAL 0xF0 +#define ACPI_THERMAL_NOTIFY_HOT 0xF1 +#define ACPI_THERMAL_MODE_ACTIVE 0x00 +#define ACPI_THERMAL_MODE_PASSIVE 0x01 +#define ACPI_THERMAL_MODE_CRITICAL 0xff +#define ACPI_THERMAL_PATH_POWEROFF "/sbin/poweroff" + +#define ACPI_THERMAL_MAX_ACTIVE 10 +#define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 + +#define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10) +#define CELSIUS_TO_KELVIN(t) ((t+273)*10) + +#define _COMPONENT ACPI_THERMAL_COMPONENT +ACPI_MODULE_NAME ("acpi_thermal") + +MODULE_AUTHOR("Paul Diefenbaugh"); +MODULE_DESCRIPTION(ACPI_THERMAL_DRIVER_NAME); +MODULE_LICENSE("GPL"); + +static int tzp; +module_param(tzp, int, 0); +MODULE_PARM_DESC(tzp, "Thermal zone polling frequency, in 1/10 seconds.\n"); + + +static int acpi_thermal_add (struct acpi_device *device); +static int acpi_thermal_remove (struct acpi_device *device, int type); +static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file); +static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file); +static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file); +static ssize_t acpi_thermal_write_trip_points (struct file*,const char __user *,size_t,loff_t *); +static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file); +static ssize_t acpi_thermal_write_cooling_mode (struct file*,const char __user *,size_t,loff_t *); +static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file); +static ssize_t acpi_thermal_write_polling(struct file*,const char __user *,size_t,loff_t *); + +static struct acpi_driver acpi_thermal_driver = { + .name = ACPI_THERMAL_DRIVER_NAME, + .class = ACPI_THERMAL_CLASS, + .ids = ACPI_THERMAL_HID, + .ops = { + .add = acpi_thermal_add, + .remove = acpi_thermal_remove, + }, +}; + +struct acpi_thermal_state { + u8 critical:1; + u8 hot:1; + u8 passive:1; + u8 active:1; + u8 reserved:4; + int active_index; +}; + +struct acpi_thermal_state_flags { + u8 valid:1; + u8 enabled:1; + u8 reserved:6; +}; + +struct acpi_thermal_critical { + struct acpi_thermal_state_flags flags; + unsigned long temperature; +}; + +struct acpi_thermal_hot { + struct acpi_thermal_state_flags flags; + unsigned long temperature; +}; + +struct acpi_thermal_passive { + struct acpi_thermal_state_flags flags; + unsigned long temperature; + unsigned long tc1; + unsigned long tc2; + unsigned long tsp; + struct acpi_handle_list devices; +}; + +struct acpi_thermal_active { + struct acpi_thermal_state_flags flags; + unsigned long temperature; + struct acpi_handle_list devices; +}; + +struct acpi_thermal_trips { + struct acpi_thermal_critical critical; + struct acpi_thermal_hot hot; + struct acpi_thermal_passive passive; + struct acpi_thermal_active active[ACPI_THERMAL_MAX_ACTIVE]; +}; + +struct acpi_thermal_flags { + u8 cooling_mode:1; /* _SCP */ + u8 devices:1; /* _TZD */ + u8 reserved:6; +}; + +struct acpi_thermal { + acpi_handle handle; + acpi_bus_id name; + unsigned long temperature; + unsigned long last_temperature; + unsigned long polling_frequency; + u8 cooling_mode; + volatile u8 zombie; + struct acpi_thermal_flags flags; + struct acpi_thermal_state state; + struct acpi_thermal_trips trips; + struct acpi_handle_list devices; + struct timer_list timer; +}; + +static struct file_operations acpi_thermal_state_fops = { + .open = acpi_thermal_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_thermal_temp_fops = { + .open = acpi_thermal_temp_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_thermal_trip_fops = { + .open = acpi_thermal_trip_open_fs, + .read = seq_read, + .write = acpi_thermal_write_trip_points, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_thermal_cooling_fops = { + .open = acpi_thermal_cooling_open_fs, + .read = seq_read, + .write = acpi_thermal_write_cooling_mode, + .llseek = seq_lseek, + .release = single_release, +}; + +static struct file_operations acpi_thermal_polling_fops = { + .open = acpi_thermal_polling_open_fs, + .read = seq_read, + .write = acpi_thermal_write_polling, + .llseek = seq_lseek, + .release = single_release, +}; + +/* -------------------------------------------------------------------------- + Thermal Zone Management + -------------------------------------------------------------------------- */ + +static int +acpi_thermal_get_temperature ( + struct acpi_thermal *tz) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE("acpi_thermal_get_temperature"); + + if (!tz) + return_VALUE(-EINVAL); + + tz->last_temperature = tz->temperature; + + status = acpi_evaluate_integer(tz->handle, "_TMP", NULL, &tz->temperature); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Temperature is %lu dK\n", tz->temperature)); + + return_VALUE(0); +} + + +static int +acpi_thermal_get_polling_frequency ( + struct acpi_thermal *tz) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE("acpi_thermal_get_polling_frequency"); + + if (!tz) + return_VALUE(-EINVAL); + + status = acpi_evaluate_integer(tz->handle, "_TZP", NULL, &tz->polling_frequency); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency is %lu dS\n", tz->polling_frequency)); + + return_VALUE(0); +} + + +static int +acpi_thermal_set_polling ( + struct acpi_thermal *tz, + int seconds) +{ + ACPI_FUNCTION_TRACE("acpi_thermal_set_polling"); + + if (!tz) + return_VALUE(-EINVAL); + + tz->polling_frequency = seconds * 10; /* Convert value to deci-seconds */ + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Polling frequency set to %lu seconds\n", tz->polling_frequency)); + + return_VALUE(0); +} + + +static int +acpi_thermal_set_cooling_mode ( + struct acpi_thermal *tz, + int mode) +{ + acpi_status status = AE_OK; + union acpi_object arg0 = {ACPI_TYPE_INTEGER}; + struct acpi_object_list arg_list = {1, &arg0}; + acpi_handle handle = NULL; + + ACPI_FUNCTION_TRACE("acpi_thermal_set_cooling_mode"); + + if (!tz) + return_VALUE(-EINVAL); + + status = acpi_get_handle(tz->handle, "_SCP", &handle); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "_SCP not present\n")); + return_VALUE(-ENODEV); + } + + arg0.integer.value = mode; + + status = acpi_evaluate_object(handle, NULL, &arg_list, NULL); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + tz->cooling_mode = mode; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling mode [%s]\n", + mode?"passive":"active")); + + return_VALUE(0); +} + + +static int +acpi_thermal_get_trip_points ( + struct acpi_thermal *tz) +{ + acpi_status status = AE_OK; + int i = 0; + + ACPI_FUNCTION_TRACE("acpi_thermal_get_trip_points"); + + if (!tz) + return_VALUE(-EINVAL); + + /* Critical Shutdown (required) */ + + status = acpi_evaluate_integer(tz->handle, "_CRT", NULL, + &tz->trips.critical.temperature); + if (ACPI_FAILURE(status)) { + tz->trips.critical.flags.valid = 0; + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "No critical threshold\n")); + return_VALUE(-ENODEV); + } + else { + tz->trips.critical.flags.valid = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found critical threshold [%lu]\n", tz->trips.critical.temperature)); + } + + /* Critical Sleep (optional) */ + + status = acpi_evaluate_integer(tz->handle, "_HOT", NULL, &tz->trips.hot.temperature); + if (ACPI_FAILURE(status)) { + tz->trips.hot.flags.valid = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n")); + } + else { + tz->trips.hot.flags.valid = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n", tz->trips.hot.temperature)); + } + + /* Passive: Processors (optional) */ + + status = acpi_evaluate_integer(tz->handle, "_PSV", NULL, &tz->trips.passive.temperature); + if (ACPI_FAILURE(status)) { + tz->trips.passive.flags.valid = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n")); + } + else { + tz->trips.passive.flags.valid = 1; + + status = acpi_evaluate_integer(tz->handle, "_TC1", NULL, &tz->trips.passive.tc1); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + + status = acpi_evaluate_integer(tz->handle, "_TC2", NULL, &tz->trips.passive.tc2); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + + status = acpi_evaluate_integer(tz->handle, "_TSP", NULL, &tz->trips.passive.tsp); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + + status = acpi_evaluate_reference(tz->handle, "_PSL", NULL, &tz->trips.passive.devices); + if (ACPI_FAILURE(status)) + tz->trips.passive.flags.valid = 0; + + if (!tz->trips.passive.flags.valid) + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid passive threshold\n")); + else + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found passive threshold [%lu]\n", tz->trips.passive.temperature)); + } + + /* Active: Fans, etc. (optional) */ + + for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) { + + char name[5] = {'_','A','C',('0'+i),'\0'}; + + status = acpi_evaluate_integer(tz->handle, name, NULL, &tz->trips.active[i].temperature); + if (ACPI_FAILURE(status)) + break; + + name[2] = 'L'; + status = acpi_evaluate_reference(tz->handle, name, NULL, &tz->trips.active[i].devices); + if (ACPI_SUCCESS(status)) { + tz->trips.active[i].flags.valid = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found active threshold [%d]:[%lu]\n", i, tz->trips.active[i].temperature)); + } + else + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid active threshold [%d]\n", i)); + } + + return_VALUE(0); +} + + +static int +acpi_thermal_get_devices ( + struct acpi_thermal *tz) +{ + acpi_status status = AE_OK; + + ACPI_FUNCTION_TRACE("acpi_thermal_get_devices"); + + if (!tz) + return_VALUE(-EINVAL); + + status = acpi_evaluate_reference(tz->handle, "_TZD", NULL, &tz->devices); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + return_VALUE(0); +} + + +static int +acpi_thermal_call_usermode ( + char *path) +{ + char *argv[2] = {NULL, NULL}; + char *envp[3] = {NULL, NULL, NULL}; + + ACPI_FUNCTION_TRACE("acpi_thermal_call_usermode"); + + if (!path) + return_VALUE(-EINVAL); + + argv[0] = path; + + /* minimal command environment */ + envp[0] = "HOME=/"; + envp[1] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin"; + + call_usermodehelper(argv[0], argv, envp, 0); + + return_VALUE(0); +} + + +static int +acpi_thermal_critical ( + struct acpi_thermal *tz) +{ + int result = 0; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_thermal_critical"); + + if (!tz || !tz->trips.critical.flags.valid) + return_VALUE(-EINVAL); + + if (tz->temperature >= tz->trips.critical.temperature) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Critical trip point\n")); + tz->trips.critical.flags.enabled = 1; + } + else if (tz->trips.critical.flags.enabled) + tz->trips.critical.flags.enabled = 0; + + result = acpi_bus_get_device(tz->handle, &device); + if (result) + return_VALUE(result); + + printk(KERN_EMERG "Critical temperature reached (%ld C), shutting down.\n", KELVIN_TO_CELSIUS(tz->temperature)); + acpi_bus_generate_event(device, ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); + + acpi_thermal_call_usermode(ACPI_THERMAL_PATH_POWEROFF); + + return_VALUE(0); +} + + +static int +acpi_thermal_hot ( + struct acpi_thermal *tz) +{ + int result = 0; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_thermal_hot"); + + if (!tz || !tz->trips.hot.flags.valid) + return_VALUE(-EINVAL); + + if (tz->temperature >= tz->trips.hot.temperature) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Hot trip point\n")); + tz->trips.hot.flags.enabled = 1; + } + else if (tz->trips.hot.flags.enabled) + tz->trips.hot.flags.enabled = 0; + + result = acpi_bus_get_device(tz->handle, &device); + if (result) + return_VALUE(result); + + acpi_bus_generate_event(device, ACPI_THERMAL_NOTIFY_HOT, tz->trips.hot.flags.enabled); + + /* TBD: Call user-mode "sleep(S4)" function */ + + return_VALUE(0); +} + + +static int +acpi_thermal_passive ( + struct acpi_thermal *tz) +{ + int result = 0; + struct acpi_thermal_passive *passive = NULL; + int trend = 0; + int i = 0; + + ACPI_FUNCTION_TRACE("acpi_thermal_passive"); + + if (!tz || !tz->trips.passive.flags.valid) + return_VALUE(-EINVAL); + + passive = &(tz->trips.passive); + + /* + * Above Trip? + * ----------- + * Calculate the thermal trend (using the passive cooling equation) + * and modify the performance limit for all passive cooling devices + * accordingly. Note that we assume symmetry. + */ + if (tz->temperature >= passive->temperature) { + trend = (passive->tc1 * (tz->temperature - tz->last_temperature)) + (passive->tc2 * (tz->temperature - passive->temperature)); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "trend[%d]=(tc1[%lu]*(tmp[%lu]-last[%lu]))+(tc2[%lu]*(tmp[%lu]-psv[%lu]))\n", + trend, passive->tc1, tz->temperature, + tz->last_temperature, passive->tc2, + tz->temperature, passive->temperature)); + tz->trips.passive.flags.enabled = 1; + /* Heating up? */ + if (trend > 0) + for (i=0; i<passive->devices.count; i++) + acpi_processor_set_thermal_limit( + passive->devices.handles[i], + ACPI_PROCESSOR_LIMIT_INCREMENT); + /* Cooling off? */ + else if (trend < 0) + for (i=0; i<passive->devices.count; i++) + acpi_processor_set_thermal_limit( + passive->devices.handles[i], + ACPI_PROCESSOR_LIMIT_DECREMENT); + } + + /* + * Below Trip? + * ----------- + * Implement passive cooling hysteresis to slowly increase performance + * and avoid thrashing around the passive trip point. Note that we + * assume symmetry. + */ + else if (tz->trips.passive.flags.enabled) { + for (i=0; i<passive->devices.count; i++) + result = acpi_processor_set_thermal_limit( + passive->devices.handles[i], + ACPI_PROCESSOR_LIMIT_DECREMENT); + if (result == 1) { + tz->trips.passive.flags.enabled = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Disabling passive cooling (zone is cool)\n")); + } + } + + return_VALUE(0); +} + + +static int +acpi_thermal_active ( + struct acpi_thermal *tz) +{ + int result = 0; + struct acpi_thermal_active *active = NULL; + int i = 0; + int j = 0; + unsigned long maxtemp = 0; + + ACPI_FUNCTION_TRACE("acpi_thermal_active"); + + if (!tz) + return_VALUE(-EINVAL); + + for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) { + + active = &(tz->trips.active[i]); + if (!active || !active->flags.valid) + break; + + /* + * Above Threshold? + * ---------------- + * If not already enabled, turn ON all cooling devices + * associated with this active threshold. + */ + if (tz->temperature >= active->temperature) { + if (active->temperature > maxtemp) + tz->state.active_index = i, maxtemp = active->temperature; + if (!active->flags.enabled) { + for (j = 0; j < active->devices.count; j++) { + result = acpi_bus_set_power(active->devices.handles[j], ACPI_STATE_D0); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to turn cooling device [%p] 'on'\n", active->devices.handles[j])); + continue; + } + active->flags.enabled = 1; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling device [%p] now 'on'\n", active->devices.handles[j])); + } + } + } + /* + * Below Threshold? + * ---------------- + * Turn OFF all cooling devices associated with this + * threshold. + */ + else if (active->flags.enabled) { + for (j = 0; j < active->devices.count; j++) { + result = acpi_bus_set_power(active->devices.handles[j], ACPI_STATE_D3); + if (result) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Unable to turn cooling device [%p] 'off'\n", active->devices.handles[j])); + continue; + } + active->flags.enabled = 0; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cooling device [%p] now 'off'\n", active->devices.handles[j])); + } + } + } + + return_VALUE(0); +} + + +static void acpi_thermal_check (void *context); + +static void +acpi_thermal_run ( + unsigned long data) +{ + struct acpi_thermal *tz = (struct acpi_thermal *)data; + if (!tz->zombie) + acpi_os_queue_for_execution(OSD_PRIORITY_GPE, + acpi_thermal_check, (void *) data); +} + + +static void +acpi_thermal_check ( + void *data) +{ + int result = 0; + struct acpi_thermal *tz = (struct acpi_thermal *) data; + unsigned long sleep_time = 0; + int i = 0; + struct acpi_thermal_state state; + + ACPI_FUNCTION_TRACE("acpi_thermal_check"); + + if (!tz) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid (NULL) context.\n")); + return_VOID; + } + + state = tz->state; + + result = acpi_thermal_get_temperature(tz); + if (result) + return_VOID; + + memset(&tz->state, 0, sizeof(tz->state)); + + /* + * Check Trip Points + * ----------------- + * Compare the current temperature to the trip point values to see + * if we've entered one of the thermal policy states. Note that + * this function determines when a state is entered, but the + * individual policy decides when it is exited (e.g. hysteresis). + */ + if (tz->trips.critical.flags.valid) + state.critical |= (tz->temperature >= tz->trips.critical.temperature); + if (tz->trips.hot.flags.valid) + state.hot |= (tz->temperature >= tz->trips.hot.temperature); + if (tz->trips.passive.flags.valid) + state.passive |= (tz->temperature >= tz->trips.passive.temperature); + for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) + if (tz->trips.active[i].flags.valid) + state.active |= (tz->temperature >= tz->trips.active[i].temperature); + + /* + * Invoke Policy + * ------------- + * Separated from the above check to allow individual policy to + * determine when to exit a given state. + */ + if (state.critical) + acpi_thermal_critical(tz); + if (state.hot) + acpi_thermal_hot(tz); + if (state.passive) + acpi_thermal_passive(tz); + if (state.active) + acpi_thermal_active(tz); + + /* + * Calculate State + * --------------- + * Again, separated from the above two to allow independent policy + * decisions. + */ + if (tz->trips.critical.flags.enabled) + tz->state.critical = 1; + if (tz->trips.hot.flags.enabled) + tz->state.hot = 1; + if (tz->trips.passive.flags.enabled) + tz->state.passive = 1; + for (i=0; i<ACPI_THERMAL_MAX_ACTIVE; i++) + if (tz->trips.active[i].flags.enabled) + tz->state.active = 1; + + /* + * Calculate Sleep Time + * -------------------- + * If we're in the passive state, use _TSP's value. Otherwise + * use the default polling frequency (e.g. _TZP). If no polling + * frequency is specified then we'll wait forever (at least until + * a thermal event occurs). Note that _TSP and _TZD values are + * given in 1/10th seconds (we must covert to milliseconds). + */ + if (tz->state.passive) + sleep_time = tz->trips.passive.tsp * 100; + else if (tz->polling_frequency > 0) + sleep_time = tz->polling_frequency * 100; + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "%s: temperature[%lu] sleep[%lu]\n", + tz->name, tz->temperature, sleep_time)); + + /* + * Schedule Next Poll + * ------------------ + */ + if (!sleep_time) { + if (timer_pending(&(tz->timer))) + del_timer(&(tz->timer)); + } + else { + if (timer_pending(&(tz->timer))) + mod_timer(&(tz->timer), (HZ * sleep_time) / 1000); + else { + tz->timer.data = (unsigned long) tz; + tz->timer.function = acpi_thermal_run; + tz->timer.expires = jiffies + (HZ * sleep_time) / 1000; + add_timer(&(tz->timer)); + } + } + + return_VOID; +} + + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_thermal_dir; + +static int acpi_thermal_state_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + + ACPI_FUNCTION_TRACE("acpi_thermal_state_seq_show"); + + if (!tz) + goto end; + + seq_puts(seq, "state: "); + + if (!tz->state.critical && !tz->state.hot && !tz->state.passive && !tz->state.active) + seq_puts(seq, "ok\n"); + else { + if (tz->state.critical) + seq_puts(seq, "critical "); + if (tz->state.hot) + seq_puts(seq, "hot "); + if (tz->state.passive) + seq_puts(seq, "passive "); + if (tz->state.active) + seq_printf(seq, "active[%d]", tz->state.active_index); + seq_puts(seq, "\n"); + } + +end: + return_VALUE(0); +} + +static int acpi_thermal_state_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_thermal_state_seq_show, PDE(inode)->data); +} + + +static int acpi_thermal_temp_seq_show(struct seq_file *seq, void *offset) +{ + int result = 0; + struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + + ACPI_FUNCTION_TRACE("acpi_thermal_temp_seq_show"); + + if (!tz) + goto end; + + result = acpi_thermal_get_temperature(tz); + if (result) + goto end; + + seq_printf(seq, "temperature: %ld C\n", + KELVIN_TO_CELSIUS(tz->temperature)); + +end: + return_VALUE(0); +} + +static int acpi_thermal_temp_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_thermal_temp_seq_show, PDE(inode)->data); +} + + +static int acpi_thermal_trip_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + int i = 0; + int j = 0; + + ACPI_FUNCTION_TRACE("acpi_thermal_trip_seq_show"); + + if (!tz) + goto end; + + if (tz->trips.critical.flags.valid) + seq_printf(seq, "critical (S5): %ld C\n", + KELVIN_TO_CELSIUS(tz->trips.critical.temperature)); + + if (tz->trips.hot.flags.valid) + seq_printf(seq, "hot (S4): %ld C\n", + KELVIN_TO_CELSIUS(tz->trips.hot.temperature)); + + if (tz->trips.passive.flags.valid) { + seq_printf(seq, "passive: %ld C: tc1=%lu tc2=%lu tsp=%lu devices=", + KELVIN_TO_CELSIUS(tz->trips.passive.temperature), + tz->trips.passive.tc1, + tz->trips.passive.tc2, + tz->trips.passive.tsp); + for (j=0; j<tz->trips.passive.devices.count; j++) { + + seq_printf(seq, "0x%p ", tz->trips.passive.devices.handles[j]); + } + seq_puts(seq, "\n"); + } + + for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { + if (!(tz->trips.active[i].flags.valid)) + break; + seq_printf(seq, "active[%d]: %ld C: devices=", + i, KELVIN_TO_CELSIUS(tz->trips.active[i].temperature)); + for (j = 0; j < tz->trips.active[i].devices.count; j++) + seq_printf(seq, "0x%p ", + tz->trips.active[i].devices.handles[j]); + seq_puts(seq, "\n"); + } + +end: + return_VALUE(0); +} + +static int acpi_thermal_trip_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_thermal_trip_seq_show, PDE(inode)->data); +} + +static ssize_t +acpi_thermal_write_trip_points ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *ppos) +{ + struct seq_file *m = (struct seq_file *)file->private_data; + struct acpi_thermal *tz = (struct acpi_thermal *)m->private; + + char *limit_string; + int num, critical, hot, passive; + int *active; + int i = 0; + + ACPI_FUNCTION_TRACE("acpi_thermal_write_trip_points"); + + limit_string = kmalloc(ACPI_THERMAL_MAX_LIMIT_STR_LEN, GFP_KERNEL); + if(!limit_string) + return_VALUE(-ENOMEM); + + memset(limit_string, 0, ACPI_THERMAL_MAX_LIMIT_STR_LEN); + + active = kmalloc(ACPI_THERMAL_MAX_ACTIVE *sizeof(int), GFP_KERNEL); + if(!active) + return_VALUE(-ENOMEM); + + if (!tz || (count > ACPI_THERMAL_MAX_LIMIT_STR_LEN - 1)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid argument\n")); + count = -EINVAL; + goto end; + } + + if (copy_from_user(limit_string, buffer, count)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data\n")); + count = -EFAULT; + goto end; + } + + limit_string[count] = '\0'; + + num = sscanf(limit_string, "%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d", + &critical, &hot, &passive, + &active[0], &active[1], &active[2], &active[3], &active[4], + &active[5], &active[6], &active[7], &active[8], &active[9]); + if(!(num >=5 && num < (ACPI_THERMAL_MAX_ACTIVE + 3))) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data format\n")); + count = -EINVAL; + goto end; + } + + tz->trips.critical.temperature = CELSIUS_TO_KELVIN(critical); + tz->trips.hot.temperature = CELSIUS_TO_KELVIN(hot); + tz->trips.passive.temperature = CELSIUS_TO_KELVIN(passive); + for (i = 0; i < num - 3; i++) { + if (!(tz->trips.active[i].flags.valid)) + break; + tz->trips.active[i].temperature = CELSIUS_TO_KELVIN(active[i]); + } + +end: + kfree(active); + kfree(limit_string); + return_VALUE(count); +} + + +static int acpi_thermal_cooling_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + + ACPI_FUNCTION_TRACE("acpi_thermal_cooling_seq_show"); + + if (!tz) + goto end; + + if (!tz->flags.cooling_mode) { + seq_puts(seq, "<setting not supported>\n"); + } + + if ( tz->cooling_mode == ACPI_THERMAL_MODE_CRITICAL ) + seq_printf(seq, "cooling mode: critical\n"); + else + seq_printf(seq, "cooling mode: %s\n", + tz->cooling_mode?"passive":"active"); + +end: + return_VALUE(0); +} + +static int acpi_thermal_cooling_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_thermal_cooling_seq_show, + PDE(inode)->data); +} + +static ssize_t +acpi_thermal_write_cooling_mode ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *ppos) +{ + struct seq_file *m = (struct seq_file *)file->private_data; + struct acpi_thermal *tz = (struct acpi_thermal *)m->private; + int result = 0; + char mode_string[12] = {'\0'}; + + ACPI_FUNCTION_TRACE("acpi_thermal_write_cooling_mode"); + + if (!tz || (count > sizeof(mode_string) - 1)) + return_VALUE(-EINVAL); + + if (!tz->flags.cooling_mode) + return_VALUE(-ENODEV); + + if (copy_from_user(mode_string, buffer, count)) + return_VALUE(-EFAULT); + + mode_string[count] = '\0'; + + result = acpi_thermal_set_cooling_mode(tz, + simple_strtoul(mode_string, NULL, 0)); + if (result) + return_VALUE(result); + + acpi_thermal_check(tz); + + return_VALUE(count); +} + + +static int acpi_thermal_polling_seq_show(struct seq_file *seq, void *offset) +{ + struct acpi_thermal *tz = (struct acpi_thermal *)seq->private; + + ACPI_FUNCTION_TRACE("acpi_thermal_polling_seq_show"); + + if (!tz) + goto end; + + if (!tz->polling_frequency) { + seq_puts(seq, "<polling disabled>\n"); + goto end; + } + + seq_printf(seq, "polling frequency: %lu seconds\n", + (tz->polling_frequency / 10)); + +end: + return_VALUE(0); +} + +static int acpi_thermal_polling_open_fs(struct inode *inode, struct file *file) +{ + return single_open(file, acpi_thermal_polling_seq_show, + PDE(inode)->data); +} + +static ssize_t +acpi_thermal_write_polling ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *ppos) +{ + struct seq_file *m = (struct seq_file *)file->private_data; + struct acpi_thermal *tz = (struct acpi_thermal *)m->private; + int result = 0; + char polling_string[12] = {'\0'}; + int seconds = 0; + + ACPI_FUNCTION_TRACE("acpi_thermal_write_polling"); + + if (!tz || (count > sizeof(polling_string) - 1)) + return_VALUE(-EINVAL); + + if (copy_from_user(polling_string, buffer, count)) + return_VALUE(-EFAULT); + + polling_string[count] = '\0'; + + seconds = simple_strtoul(polling_string, NULL, 0); + + result = acpi_thermal_set_polling(tz, seconds); + if (result) + return_VALUE(result); + + acpi_thermal_check(tz); + + return_VALUE(count); +} + + +static int +acpi_thermal_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + + ACPI_FUNCTION_TRACE("acpi_thermal_add_fs"); + + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_thermal_dir); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; + } + + /* 'state' [R] */ + entry = create_proc_entry(ACPI_THERMAL_FILE_STATE, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_THERMAL_FILE_STATE)); + else { + entry->proc_fops = &acpi_thermal_state_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'temperature' [R] */ + entry = create_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE, + S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_THERMAL_FILE_TEMPERATURE)); + else { + entry->proc_fops = &acpi_thermal_temp_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'trip_points' [R/W] */ + entry = create_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS, + S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_THERMAL_FILE_TRIP_POINTS)); + else { + entry->proc_fops = &acpi_thermal_trip_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'cooling_mode' [R/W] */ + entry = create_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE, + S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_THERMAL_FILE_COOLING_MODE)); + else { + entry->proc_fops = &acpi_thermal_cooling_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'polling_frequency' [R/W] */ + entry = create_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ, + S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create '%s' fs entry\n", + ACPI_THERMAL_FILE_POLLING_FREQ)); + else { + entry->proc_fops = &acpi_thermal_polling_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + return_VALUE(0); +} + + +static int +acpi_thermal_remove_fs ( + struct acpi_device *device) +{ + ACPI_FUNCTION_TRACE("acpi_thermal_remove_fs"); + + if (acpi_device_dir(device)) { + remove_proc_entry(ACPI_THERMAL_FILE_POLLING_FREQ, + acpi_device_dir(device)); + remove_proc_entry(ACPI_THERMAL_FILE_COOLING_MODE, + acpi_device_dir(device)); + remove_proc_entry(ACPI_THERMAL_FILE_TRIP_POINTS, + acpi_device_dir(device)); + remove_proc_entry(ACPI_THERMAL_FILE_TEMPERATURE, + acpi_device_dir(device)); + remove_proc_entry(ACPI_THERMAL_FILE_STATE, + acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), acpi_thermal_dir); + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +static void +acpi_thermal_notify ( + acpi_handle handle, + u32 event, + void *data) +{ + struct acpi_thermal *tz = (struct acpi_thermal *) data; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_thermal_notify"); + + if (!tz) + return_VOID; + + if (acpi_bus_get_device(tz->handle, &device)) + return_VOID; + + switch (event) { + case ACPI_THERMAL_NOTIFY_TEMPERATURE: + acpi_thermal_check(tz); + break; + case ACPI_THERMAL_NOTIFY_THRESHOLDS: + acpi_thermal_get_trip_points(tz); + acpi_thermal_check(tz); + acpi_bus_generate_event(device, event, 0); + break; + case ACPI_THERMAL_NOTIFY_DEVICES: + if (tz->flags.devices) + acpi_thermal_get_devices(tz); + acpi_bus_generate_event(device, event, 0); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + return_VOID; +} + + +static int +acpi_thermal_get_info ( + struct acpi_thermal *tz) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_thermal_get_info"); + + if (!tz) + return_VALUE(-EINVAL); + + /* Get temperature [_TMP] (required) */ + result = acpi_thermal_get_temperature(tz); + if (result) + return_VALUE(result); + + /* Get trip points [_CRT, _PSV, etc.] (required) */ + result = acpi_thermal_get_trip_points(tz); + if (result) + return_VALUE(result); + + /* Set the cooling mode [_SCP] to active cooling (default) */ + result = acpi_thermal_set_cooling_mode(tz, ACPI_THERMAL_MODE_ACTIVE); + if (!result) + tz->flags.cooling_mode = 1; + else { + /* Oh,we have not _SCP method. + Generally show cooling_mode by _ACx, _PSV,spec 12.2*/ + tz->flags.cooling_mode = 0; + if ( tz->trips.active[0].flags.valid && tz->trips.passive.flags.valid ) { + if ( tz->trips.passive.temperature > tz->trips.active[0].temperature ) + tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE; + else + tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE; + } else if ( !tz->trips.active[0].flags.valid && tz->trips.passive.flags.valid ) { + tz->cooling_mode = ACPI_THERMAL_MODE_PASSIVE; + } else if ( tz->trips.active[0].flags.valid && !tz->trips.passive.flags.valid ) { + tz->cooling_mode = ACPI_THERMAL_MODE_ACTIVE; + } else { + /* _ACx and _PSV are optional, but _CRT is required */ + tz->cooling_mode = ACPI_THERMAL_MODE_CRITICAL; + } + } + + /* Get default polling frequency [_TZP] (optional) */ + if (tzp) + tz->polling_frequency = tzp; + else + acpi_thermal_get_polling_frequency(tz); + + /* Get devices in this thermal zone [_TZD] (optional) */ + result = acpi_thermal_get_devices(tz); + if (!result) + tz->flags.devices = 1; + + return_VALUE(0); +} + + +static int +acpi_thermal_add ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = AE_OK; + struct acpi_thermal *tz = NULL; + + ACPI_FUNCTION_TRACE("acpi_thermal_add"); + + if (!device) + return_VALUE(-EINVAL); + + tz = kmalloc(sizeof(struct acpi_thermal), GFP_KERNEL); + if (!tz) + return_VALUE(-ENOMEM); + memset(tz, 0, sizeof(struct acpi_thermal)); + + tz->handle = device->handle; + strcpy(tz->name, device->pnp.bus_id); + strcpy(acpi_device_name(device), ACPI_THERMAL_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); + acpi_driver_data(device) = tz; + + result = acpi_thermal_get_info(tz); + if (result) + goto end; + + result = acpi_thermal_add_fs(device); + if (result) + return_VALUE(result); + + init_timer(&tz->timer); + + acpi_thermal_check(tz); + + status = acpi_install_notify_handler(tz->handle, + ACPI_DEVICE_NOTIFY, acpi_thermal_notify, tz); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing notify handler\n")); + result = -ENODEV; + goto end; + } + + printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", + acpi_device_name(device), acpi_device_bid(device), + KELVIN_TO_CELSIUS(tz->temperature)); + +end: + if (result) { + acpi_thermal_remove_fs(device); + kfree(tz); + } + + return_VALUE(result); +} + + +static int +acpi_thermal_remove ( + struct acpi_device *device, + int type) +{ + acpi_status status = AE_OK; + struct acpi_thermal *tz = NULL; + + ACPI_FUNCTION_TRACE("acpi_thermal_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + tz = (struct acpi_thermal *) acpi_driver_data(device); + + /* avoid timer adding new defer task */ + tz->zombie = 1; + /* wait for running timer (on other CPUs) finish */ + del_timer_sync(&(tz->timer)); + /* synchronize deferred task */ + acpi_os_wait_events_complete(NULL); + /* deferred task may reinsert timer */ + del_timer_sync(&(tz->timer)); + + status = acpi_remove_notify_handler(tz->handle, + ACPI_DEVICE_NOTIFY, acpi_thermal_notify); + if (ACPI_FAILURE(status)) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error removing notify handler\n")); + + /* Terminate policy */ + if (tz->trips.passive.flags.valid + && tz->trips.passive.flags.enabled) { + tz->trips.passive.flags.enabled = 0; + acpi_thermal_passive(tz); + } + if (tz->trips.active[0].flags.valid + && tz->trips.active[0].flags.enabled) { + tz->trips.active[0].flags.enabled = 0; + acpi_thermal_active(tz); + } + + acpi_thermal_remove_fs(device); + + kfree(tz); + return_VALUE(0); +} + + +static int __init +acpi_thermal_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_thermal_init"); + + acpi_thermal_dir = proc_mkdir(ACPI_THERMAL_CLASS, acpi_root_dir); + if (!acpi_thermal_dir) + return_VALUE(-ENODEV); + acpi_thermal_dir->owner = THIS_MODULE; + + result = acpi_bus_register_driver(&acpi_thermal_driver); + if (result < 0) { + remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + + +static void __exit +acpi_thermal_exit (void) +{ + ACPI_FUNCTION_TRACE("acpi_thermal_exit"); + + acpi_bus_unregister_driver(&acpi_thermal_driver); + + remove_proc_entry(ACPI_THERMAL_CLASS, acpi_root_dir); + + return_VOID; +} + + +module_init(acpi_thermal_init); +module_exit(acpi_thermal_exit); diff --git a/drivers/acpi/toshiba_acpi.c b/drivers/acpi/toshiba_acpi.c new file mode 100644 index 00000000000..c84997c9f96 --- /dev/null +++ b/drivers/acpi/toshiba_acpi.c @@ -0,0 +1,575 @@ +/* + * toshiba_acpi.c - Toshiba Laptop ACPI Extras + * + * + * Copyright (C) 2002-2004 John Belmonte + * + * 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * The devolpment page for this driver is located at + * http://memebeam.org/toys/ToshibaAcpiDriver. + * + * Credits: + * Jonathan A. Buzzard - Toshiba HCI info, and critical tips on reverse + * engineering the Windows drivers + * Yasushi Nagato - changes for linux kernel 2.4 -> 2.5 + * Rob Miller - TV out and hotkeys help + * + * + * TODO + * + */ + +#define TOSHIBA_ACPI_VERSION "0.18" +#define PROC_INTERFACE_VERSION 1 + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/proc_fs.h> +#include <asm/uaccess.h> + +#include <acpi/acpi_drivers.h> + +MODULE_AUTHOR("John Belmonte"); +MODULE_DESCRIPTION("Toshiba Laptop ACPI Extras Driver"); +MODULE_LICENSE("GPL"); + +#define MY_LOGPREFIX "toshiba_acpi: " +#define MY_ERR KERN_ERR MY_LOGPREFIX +#define MY_NOTICE KERN_NOTICE MY_LOGPREFIX +#define MY_INFO KERN_INFO MY_LOGPREFIX + +/* Toshiba ACPI method paths */ +#define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM" +#define METHOD_HCI_1 "\\_SB_.VALD.GHCI" +#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI" +#define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX" + +/* Toshiba HCI interface definitions + * + * HCI is Toshiba's "Hardware Control Interface" which is supposed to + * be uniform across all their models. Ideally we would just call + * dedicated ACPI methods instead of using this primitive interface. + * However the ACPI methods seem to be incomplete in some areas (for + * example they allow setting, but not reading, the LCD brightness value), + * so this is still useful. + */ + +#define HCI_WORDS 6 + +/* operations */ +#define HCI_SET 0xff00 +#define HCI_GET 0xfe00 + +/* return codes */ +#define HCI_SUCCESS 0x0000 +#define HCI_FAILURE 0x1000 +#define HCI_NOT_SUPPORTED 0x8000 +#define HCI_EMPTY 0x8c00 + +/* registers */ +#define HCI_FAN 0x0004 +#define HCI_SYSTEM_EVENT 0x0016 +#define HCI_VIDEO_OUT 0x001c +#define HCI_HOTKEY_EVENT 0x001e +#define HCI_LCD_BRIGHTNESS 0x002a + +/* field definitions */ +#define HCI_LCD_BRIGHTNESS_BITS 3 +#define HCI_LCD_BRIGHTNESS_SHIFT (16-HCI_LCD_BRIGHTNESS_BITS) +#define HCI_LCD_BRIGHTNESS_LEVELS (1 << HCI_LCD_BRIGHTNESS_BITS) +#define HCI_VIDEO_OUT_LCD 0x1 +#define HCI_VIDEO_OUT_CRT 0x2 +#define HCI_VIDEO_OUT_TV 0x4 + +/* utility + */ + +static __inline__ void +_set_bit(u32* word, u32 mask, int value) +{ + *word = (*word & ~mask) | (mask * value); +} + +/* acpi interface wrappers + */ + +static int +is_valid_acpi_path(const char* methodName) +{ + acpi_handle handle; + acpi_status status; + + status = acpi_get_handle(NULL, (char*)methodName, &handle); + return !ACPI_FAILURE(status); +} + +static int +write_acpi_int(const char* methodName, int val) +{ + struct acpi_object_list params; + union acpi_object in_objs[1]; + acpi_status status; + + params.count = sizeof(in_objs)/sizeof(in_objs[0]); + params.pointer = in_objs; + in_objs[0].type = ACPI_TYPE_INTEGER; + in_objs[0].integer.value = val; + + status = acpi_evaluate_object(NULL, (char*)methodName, ¶ms, NULL); + return (status == AE_OK); +} + +#if 0 +static int +read_acpi_int(const char* methodName, int* pVal) +{ + struct acpi_buffer results; + union acpi_object out_objs[1]; + acpi_status status; + + results.length = sizeof(out_objs); + results.pointer = out_objs; + + status = acpi_evaluate_object(0, (char*)methodName, 0, &results); + *pVal = out_objs[0].integer.value; + + return (status == AE_OK) && (out_objs[0].type == ACPI_TYPE_INTEGER); +} +#endif + +static const char* method_hci /*= 0*/; + +/* Perform a raw HCI call. Here we don't care about input or output buffer + * format. + */ +static acpi_status +hci_raw(const u32 in[HCI_WORDS], u32 out[HCI_WORDS]) +{ + struct acpi_object_list params; + union acpi_object in_objs[HCI_WORDS]; + struct acpi_buffer results; + union acpi_object out_objs[HCI_WORDS+1]; + acpi_status status; + int i; + + params.count = HCI_WORDS; + params.pointer = in_objs; + for (i = 0; i < HCI_WORDS; ++i) { + in_objs[i].type = ACPI_TYPE_INTEGER; + in_objs[i].integer.value = in[i]; + } + + results.length = sizeof(out_objs); + results.pointer = out_objs; + + status = acpi_evaluate_object(NULL, (char*)method_hci, ¶ms, + &results); + if ((status == AE_OK) && (out_objs->package.count <= HCI_WORDS)) { + for (i = 0; i < out_objs->package.count; ++i) { + out[i] = out_objs->package.elements[i].integer.value; + } + } + + return status; +} + +/* common hci tasks (get or set one value) + * + * In addition to the ACPI status, the HCI system returns a result which + * may be useful (such as "not supported"). + */ + +static acpi_status +hci_write1(u32 reg, u32 in1, u32* result) +{ + u32 in[HCI_WORDS] = { HCI_SET, reg, in1, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status = hci_raw(in, out); + *result = (status == AE_OK) ? out[0] : HCI_FAILURE; + return status; +} + +static acpi_status +hci_read1(u32 reg, u32* out1, u32* result) +{ + u32 in[HCI_WORDS] = { HCI_GET, reg, 0, 0, 0, 0 }; + u32 out[HCI_WORDS]; + acpi_status status = hci_raw(in, out); + *out1 = out[2]; + *result = (status == AE_OK) ? out[0] : HCI_FAILURE; + return status; +} + +static struct proc_dir_entry* toshiba_proc_dir /*= 0*/; +static int force_fan; +static int last_key_event; +static int key_event_valid; + +typedef struct _ProcItem +{ + const char* name; + char* (*read_func)(char*); + unsigned long (*write_func)(const char*, unsigned long); +} ProcItem; + +/* proc file handlers + */ + +static int +dispatch_read(char* page, char** start, off_t off, int count, int* eof, + ProcItem* item) +{ + char* p = page; + int len; + + if (off == 0) + p = item->read_func(p); + + /* ISSUE: I don't understand this code */ + len = (p - page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + +static int +dispatch_write(struct file* file, const char __user * buffer, + unsigned long count, ProcItem* item) +{ + int result; + char* tmp_buffer; + + /* Arg buffer points to userspace memory, which can't be accessed + * directly. Since we're making a copy, zero-terminate the + * destination so that sscanf can be used on it safely. + */ + tmp_buffer = kmalloc(count + 1, GFP_KERNEL); + if (copy_from_user(tmp_buffer, buffer, count)) { + result = -EFAULT; + } + else { + tmp_buffer[count] = 0; + result = item->write_func(tmp_buffer, count); + } + kfree(tmp_buffer); + return result; +} + +static char* +read_lcd(char* p) +{ + u32 hci_result; + u32 value; + + hci_read1(HCI_LCD_BRIGHTNESS, &value, &hci_result); + if (hci_result == HCI_SUCCESS) { + value = value >> HCI_LCD_BRIGHTNESS_SHIFT; + p += sprintf(p, "brightness: %d\n", value); + p += sprintf(p, "brightness_levels: %d\n", + HCI_LCD_BRIGHTNESS_LEVELS); + } else { + printk(MY_ERR "Error reading LCD brightness\n"); + } + + return p; +} + +static unsigned long +write_lcd(const char* buffer, unsigned long count) +{ + int value; + u32 hci_result; + + if (sscanf(buffer, " brightness : %i", &value) == 1 && + value >= 0 && value < HCI_LCD_BRIGHTNESS_LEVELS) { + value = value << HCI_LCD_BRIGHTNESS_SHIFT; + hci_write1(HCI_LCD_BRIGHTNESS, value, &hci_result); + if (hci_result != HCI_SUCCESS) + return -EFAULT; + } else { + return -EINVAL; + } + + return count; +} + +static char* +read_video(char* p) +{ + u32 hci_result; + u32 value; + + hci_read1(HCI_VIDEO_OUT, &value, &hci_result); + if (hci_result == HCI_SUCCESS) { + int is_lcd = (value & HCI_VIDEO_OUT_LCD) ? 1 : 0; + int is_crt = (value & HCI_VIDEO_OUT_CRT) ? 1 : 0; + int is_tv = (value & HCI_VIDEO_OUT_TV ) ? 1 : 0; + p += sprintf(p, "lcd_out: %d\n", is_lcd); + p += sprintf(p, "crt_out: %d\n", is_crt); + p += sprintf(p, "tv_out: %d\n", is_tv); + } else { + printk(MY_ERR "Error reading video out status\n"); + } + + return p; +} + +static unsigned long +write_video(const char* buffer, unsigned long count) +{ + int value; + int remain = count; + int lcd_out = -1; + int crt_out = -1; + int tv_out = -1; + u32 hci_result; + int video_out; + + /* scan expression. Multiple expressions may be delimited with ; + * + * NOTE: to keep scanning simple, invalid fields are ignored + */ + while (remain) { + if (sscanf(buffer, " lcd_out : %i", &value) == 1) + lcd_out = value & 1; + else if (sscanf(buffer, " crt_out : %i", &value) == 1) + crt_out = value & 1; + else if (sscanf(buffer, " tv_out : %i", &value) == 1) + tv_out = value & 1; + /* advance to one character past the next ; */ + do { + ++buffer; + --remain; + } + while (remain && *(buffer-1) != ';'); + } + + hci_read1(HCI_VIDEO_OUT, &video_out, &hci_result); + if (hci_result == HCI_SUCCESS) { + int new_video_out = video_out; + if (lcd_out != -1) + _set_bit(&new_video_out, HCI_VIDEO_OUT_LCD, lcd_out); + if (crt_out != -1) + _set_bit(&new_video_out, HCI_VIDEO_OUT_CRT, crt_out); + if (tv_out != -1) + _set_bit(&new_video_out, HCI_VIDEO_OUT_TV, tv_out); + /* To avoid unnecessary video disruption, only write the new + * video setting if something changed. */ + if (new_video_out != video_out) + write_acpi_int(METHOD_VIDEO_OUT, new_video_out); + } else { + return -EFAULT; + } + + return count; +} + +static char* +read_fan(char* p) +{ + u32 hci_result; + u32 value; + + hci_read1(HCI_FAN, &value, &hci_result); + if (hci_result == HCI_SUCCESS) { + p += sprintf(p, "running: %d\n", (value > 0)); + p += sprintf(p, "force_on: %d\n", force_fan); + } else { + printk(MY_ERR "Error reading fan status\n"); + } + + return p; +} + +static unsigned long +write_fan(const char* buffer, unsigned long count) +{ + int value; + u32 hci_result; + + if (sscanf(buffer, " force_on : %i", &value) == 1 && + value >= 0 && value <= 1) { + hci_write1(HCI_FAN, value, &hci_result); + if (hci_result != HCI_SUCCESS) + return -EFAULT; + else + force_fan = value; + } else { + return -EINVAL; + } + + return count; +} + +static char* +read_keys(char* p) +{ + u32 hci_result; + u32 value; + + if (!key_event_valid) { + hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result); + if (hci_result == HCI_SUCCESS) { + key_event_valid = 1; + last_key_event = value; + } else if (hci_result == HCI_EMPTY) { + /* better luck next time */ + } else if (hci_result == HCI_NOT_SUPPORTED) { + /* This is a workaround for an unresolved issue on + * some machines where system events sporadically + * become disabled. */ + hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + printk(MY_NOTICE "Re-enabled hotkeys\n"); + } else { + printk(MY_ERR "Error reading hotkey status\n"); + goto end; + } + } + + p += sprintf(p, "hotkey_ready: %d\n", key_event_valid); + p += sprintf(p, "hotkey: 0x%04x\n", last_key_event); + +end: + return p; +} + +static unsigned long +write_keys(const char* buffer, unsigned long count) +{ + int value; + + if (sscanf(buffer, " hotkey_ready : %i", &value) == 1 && + value == 0) { + key_event_valid = 0; + } else { + return -EINVAL; + } + + return count; +} + +static char* +read_version(char* p) +{ + p += sprintf(p, "driver: %s\n", TOSHIBA_ACPI_VERSION); + p += sprintf(p, "proc_interface: %d\n", + PROC_INTERFACE_VERSION); + return p; +} + +/* proc and module init + */ + +#define PROC_TOSHIBA "toshiba" + +static ProcItem proc_items[] = +{ + { "lcd" , read_lcd , write_lcd }, + { "video" , read_video , write_video }, + { "fan" , read_fan , write_fan }, + { "keys" , read_keys , write_keys }, + { "version" , read_version , NULL }, + { NULL } +}; + +static acpi_status __init +add_device(void) +{ + struct proc_dir_entry* proc; + ProcItem* item; + + for (item = proc_items; item->name; ++item) + { + proc = create_proc_read_entry(item->name, + S_IFREG | S_IRUGO | S_IWUSR, + toshiba_proc_dir, (read_proc_t*)dispatch_read, item); + if (proc) + proc->owner = THIS_MODULE; + if (proc && item->write_func) + proc->write_proc = (write_proc_t*)dispatch_write; + } + + return AE_OK; +} + +static acpi_status __exit +remove_device(void) +{ + ProcItem* item; + + for (item = proc_items; item->name; ++item) + remove_proc_entry(item->name, toshiba_proc_dir); + return AE_OK; +} + +static int __init +toshiba_acpi_init(void) +{ + acpi_status status = AE_OK; + u32 hci_result; + + if (acpi_disabled) + return -ENODEV; + /* simple device detection: look for HCI method */ + if (is_valid_acpi_path(METHOD_HCI_1)) + method_hci = METHOD_HCI_1; + else if (is_valid_acpi_path(METHOD_HCI_2)) + method_hci = METHOD_HCI_2; + else + return -ENODEV; + + printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n", + TOSHIBA_ACPI_VERSION); + printk(MY_INFO " HCI method: %s\n", method_hci); + + force_fan = 0; + key_event_valid = 0; + + /* enable event fifo */ + hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result); + + toshiba_proc_dir = proc_mkdir(PROC_TOSHIBA, acpi_root_dir); + if (!toshiba_proc_dir) { + status = AE_ERROR; + } else { + toshiba_proc_dir->owner = THIS_MODULE; + status = add_device(); + if (ACPI_FAILURE(status)) + remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + } + + return (ACPI_SUCCESS(status)) ? 0 : -ENODEV; +} + +static void __exit +toshiba_acpi_exit(void) +{ + remove_device(); + + if (toshiba_proc_dir) + remove_proc_entry(PROC_TOSHIBA, acpi_root_dir); + + return; +} + +module_init(toshiba_acpi_init); +module_exit(toshiba_acpi_exit); diff --git a/drivers/acpi/utilities/Makefile b/drivers/acpi/utilities/Makefile new file mode 100644 index 00000000000..939c447dd52 --- /dev/null +++ b/drivers/acpi/utilities/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for all Linux ACPI interpreter subdirectories +# + +obj-y := utalloc.o utdebug.o uteval.o utinit.o utmisc.o utxface.o \ + utcopy.o utdelete.o utglobal.o utmath.o utobject.o + +EXTRA_CFLAGS += $(ACPI_CFLAGS) diff --git a/drivers/acpi/utilities/utalloc.c b/drivers/acpi/utilities/utalloc.c new file mode 100644 index 00000000000..3313439c4bc --- /dev/null +++ b/drivers/acpi/utilities/utalloc.c @@ -0,0 +1,988 @@ +/****************************************************************************** + * + * Module Name: utalloc - local cache and memory allocation routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utalloc") + + +/****************************************************************************** + * + * FUNCTION: acpi_ut_release_to_cache + * + * PARAMETERS: list_id - Memory list/cache ID + * Object - The object to be released + * + * RETURN: None + * + * DESCRIPTION: Release an object to the specified cache. If cache is full, + * the object is deleted. + * + ******************************************************************************/ + +void +acpi_ut_release_to_cache ( + u32 list_id, + void *object) +{ + struct acpi_memory_list *cache_info; + + + ACPI_FUNCTION_ENTRY (); + + + cache_info = &acpi_gbl_memory_lists[list_id]; + +#ifdef ACPI_ENABLE_OBJECT_CACHE + + /* If walk cache is full, just free this wallkstate object */ + + if (cache_info->cache_depth >= cache_info->max_cache_depth) { + ACPI_MEM_FREE (object); + ACPI_MEM_TRACKING (cache_info->total_freed++); + } + + /* Otherwise put this object back into the cache */ + + else { + if (ACPI_FAILURE (acpi_ut_acquire_mutex (ACPI_MTX_CACHES))) { + return; + } + + /* Mark the object as cached */ + + ACPI_MEMSET (object, 0xCA, cache_info->object_size); + ACPI_SET_DESCRIPTOR_TYPE (object, ACPI_DESC_TYPE_CACHED); + + /* Put the object at the head of the cache list */ + + * (ACPI_CAST_INDIRECT_PTR (char, &(((char *) object)[cache_info->link_offset]))) = cache_info->list_head; + cache_info->list_head = object; + cache_info->cache_depth++; + + (void) acpi_ut_release_mutex (ACPI_MTX_CACHES); + } + +#else + + /* Object cache is disabled; just free the object */ + + ACPI_MEM_FREE (object); + ACPI_MEM_TRACKING (cache_info->total_freed++); +#endif +} + + +/****************************************************************************** + * + * FUNCTION: acpi_ut_acquire_from_cache + * + * PARAMETERS: list_id - Memory list ID + * + * RETURN: A requested object. NULL if the object could not be + * allocated. + * + * DESCRIPTION: Get an object from the specified cache. If cache is empty, + * the object is allocated. + * + ******************************************************************************/ + +void * +acpi_ut_acquire_from_cache ( + u32 list_id) +{ + struct acpi_memory_list *cache_info; + void *object; + + + ACPI_FUNCTION_NAME ("ut_acquire_from_cache"); + + + cache_info = &acpi_gbl_memory_lists[list_id]; + +#ifdef ACPI_ENABLE_OBJECT_CACHE + + if (ACPI_FAILURE (acpi_ut_acquire_mutex (ACPI_MTX_CACHES))) { + return (NULL); + } + + ACPI_MEM_TRACKING (cache_info->cache_requests++); + + /* Check the cache first */ + + if (cache_info->list_head) { + /* There is an object available, use it */ + + object = cache_info->list_head; + cache_info->list_head = *(ACPI_CAST_INDIRECT_PTR (char, &(((char *) object)[cache_info->link_offset]))); + + ACPI_MEM_TRACKING (cache_info->cache_hits++); + cache_info->cache_depth--; + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "Object %p from %s\n", + object, acpi_gbl_memory_lists[list_id].list_name)); +#endif + + if (ACPI_FAILURE (acpi_ut_release_mutex (ACPI_MTX_CACHES))) { + return (NULL); + } + + /* Clear (zero) the previously used Object */ + + ACPI_MEMSET (object, 0, cache_info->object_size); + } + + else { + /* The cache is empty, create a new object */ + + /* Avoid deadlock with ACPI_MEM_CALLOCATE */ + + if (ACPI_FAILURE (acpi_ut_release_mutex (ACPI_MTX_CACHES))) { + return (NULL); + } + + object = ACPI_MEM_CALLOCATE (cache_info->object_size); + ACPI_MEM_TRACKING (cache_info->total_allocated++); + } + +#else + + /* Object cache is disabled; just allocate the object */ + + object = ACPI_MEM_CALLOCATE (cache_info->object_size); + ACPI_MEM_TRACKING (cache_info->total_allocated++); +#endif + + return (object); +} + + +#ifdef ACPI_ENABLE_OBJECT_CACHE +/****************************************************************************** + * + * FUNCTION: acpi_ut_delete_generic_cache + * + * PARAMETERS: list_id - Memory list ID + * + * RETURN: None + * + * DESCRIPTION: Free all objects within the requested cache. + * + ******************************************************************************/ + +void +acpi_ut_delete_generic_cache ( + u32 list_id) +{ + struct acpi_memory_list *cache_info; + char *next; + + + ACPI_FUNCTION_ENTRY (); + + + cache_info = &acpi_gbl_memory_lists[list_id]; + while (cache_info->list_head) { + /* Delete one cached state object */ + + next = *(ACPI_CAST_INDIRECT_PTR (char, &(((char *) cache_info->list_head)[cache_info->link_offset]))); + ACPI_MEM_FREE (cache_info->list_head); + + cache_info->list_head = next; + cache_info->cache_depth--; + } +} +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_validate_buffer + * + * PARAMETERS: Buffer - Buffer descriptor to be validated + * + * RETURN: Status + * + * DESCRIPTION: Perform parameter validation checks on an struct acpi_buffer + * + ******************************************************************************/ + +acpi_status +acpi_ut_validate_buffer ( + struct acpi_buffer *buffer) +{ + + /* Obviously, the structure pointer must be valid */ + + if (!buffer) { + return (AE_BAD_PARAMETER); + } + + /* Special semantics for the length */ + + if ((buffer->length == ACPI_NO_BUFFER) || + (buffer->length == ACPI_ALLOCATE_BUFFER) || + (buffer->length == ACPI_ALLOCATE_LOCAL_BUFFER)) { + return (AE_OK); + } + + /* Length is valid, the buffer pointer must be also */ + + if (!buffer->pointer) { + return (AE_BAD_PARAMETER); + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_initialize_buffer + * + * PARAMETERS: Buffer - Buffer to be validated + * required_length - Length needed + * + * RETURN: Status + * + * DESCRIPTION: Validate that the buffer is of the required length or + * allocate a new buffer. Returned buffer is always zeroed. + * + ******************************************************************************/ + +acpi_status +acpi_ut_initialize_buffer ( + struct acpi_buffer *buffer, + acpi_size required_length) +{ + acpi_status status = AE_OK; + + + switch (buffer->length) { + case ACPI_NO_BUFFER: + + /* Set the exception and returned the required length */ + + status = AE_BUFFER_OVERFLOW; + break; + + + case ACPI_ALLOCATE_BUFFER: + + /* Allocate a new buffer */ + + buffer->pointer = acpi_os_allocate (required_length); + if (!buffer->pointer) { + return (AE_NO_MEMORY); + } + + /* Clear the buffer */ + + ACPI_MEMSET (buffer->pointer, 0, required_length); + break; + + + case ACPI_ALLOCATE_LOCAL_BUFFER: + + /* Allocate a new buffer with local interface to allow tracking */ + + buffer->pointer = ACPI_MEM_CALLOCATE (required_length); + if (!buffer->pointer) { + return (AE_NO_MEMORY); + } + break; + + + default: + + /* Existing buffer: Validate the size of the buffer */ + + if (buffer->length < required_length) { + status = AE_BUFFER_OVERFLOW; + break; + } + + /* Clear the buffer */ + + ACPI_MEMSET (buffer->pointer, 0, required_length); + break; + } + + buffer->length = required_length; + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_allocate + * + * PARAMETERS: Size - Size of the allocation + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: Address of the allocated memory on success, NULL on failure. + * + * DESCRIPTION: The subsystem's equivalent of malloc. + * + ******************************************************************************/ + +void * +acpi_ut_allocate ( + acpi_size size, + u32 component, + char *module, + u32 line) +{ + void *allocation; + + + ACPI_FUNCTION_TRACE_U32 ("ut_allocate", size); + + + /* Check for an inadvertent size of zero bytes */ + + if (!size) { + _ACPI_REPORT_ERROR (module, line, component, + ("ut_allocate: Attempt to allocate zero bytes\n")); + size = 1; + } + + allocation = acpi_os_allocate (size); + if (!allocation) { + /* Report allocation error */ + + _ACPI_REPORT_ERROR (module, line, component, + ("ut_allocate: Could not allocate size %X\n", (u32) size)); + + return_PTR (NULL); + } + + return_PTR (allocation); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_callocate + * + * PARAMETERS: Size - Size of the allocation + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: Address of the allocated memory on success, NULL on failure. + * + * DESCRIPTION: Subsystem equivalent of calloc. + * + ******************************************************************************/ + +void * +acpi_ut_callocate ( + acpi_size size, + u32 component, + char *module, + u32 line) +{ + void *allocation; + + + ACPI_FUNCTION_TRACE_U32 ("ut_callocate", size); + + + /* Check for an inadvertent size of zero bytes */ + + if (!size) { + _ACPI_REPORT_ERROR (module, line, component, + ("ut_callocate: Attempt to allocate zero bytes\n")); + return_PTR (NULL); + } + + allocation = acpi_os_allocate (size); + if (!allocation) { + /* Report allocation error */ + + _ACPI_REPORT_ERROR (module, line, component, + ("ut_callocate: Could not allocate size %X\n", (u32) size)); + return_PTR (NULL); + } + + /* Clear the memory block */ + + ACPI_MEMSET (allocation, 0, size); + return_PTR (allocation); +} + + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS +/* + * These procedures are used for tracking memory leaks in the subsystem, and + * they get compiled out when the ACPI_DBG_TRACK_ALLOCATIONS is not set. + * + * Each memory allocation is tracked via a doubly linked list. Each + * element contains the caller's component, module name, function name, and + * line number. acpi_ut_allocate and acpi_ut_callocate call + * acpi_ut_track_allocation to add an element to the list; deletion + * occurs in the body of acpi_ut_free. + */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_allocate_and_track + * + * PARAMETERS: Size - Size of the allocation + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: Address of the allocated memory on success, NULL on failure. + * + * DESCRIPTION: The subsystem's equivalent of malloc. + * + ******************************************************************************/ + +void * +acpi_ut_allocate_and_track ( + acpi_size size, + u32 component, + char *module, + u32 line) +{ + struct acpi_debug_mem_block *allocation; + acpi_status status; + + + allocation = acpi_ut_allocate (size + sizeof (struct acpi_debug_mem_header), component, + module, line); + if (!allocation) { + return (NULL); + } + + status = acpi_ut_track_allocation (ACPI_MEM_LIST_GLOBAL, allocation, size, + ACPI_MEM_MALLOC, component, module, line); + if (ACPI_FAILURE (status)) { + acpi_os_free (allocation); + return (NULL); + } + + acpi_gbl_memory_lists[ACPI_MEM_LIST_GLOBAL].total_allocated++; + acpi_gbl_memory_lists[ACPI_MEM_LIST_GLOBAL].current_total_size += (u32) size; + + return ((void *) &allocation->user_space); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_callocate_and_track + * + * PARAMETERS: Size - Size of the allocation + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: Address of the allocated memory on success, NULL on failure. + * + * DESCRIPTION: Subsystem equivalent of calloc. + * + ******************************************************************************/ + +void * +acpi_ut_callocate_and_track ( + acpi_size size, + u32 component, + char *module, + u32 line) +{ + struct acpi_debug_mem_block *allocation; + acpi_status status; + + + allocation = acpi_ut_callocate (size + sizeof (struct acpi_debug_mem_header), component, + module, line); + if (!allocation) { + /* Report allocation error */ + + _ACPI_REPORT_ERROR (module, line, component, + ("ut_callocate: Could not allocate size %X\n", (u32) size)); + return (NULL); + } + + status = acpi_ut_track_allocation (ACPI_MEM_LIST_GLOBAL, allocation, size, + ACPI_MEM_CALLOC, component, module, line); + if (ACPI_FAILURE (status)) { + acpi_os_free (allocation); + return (NULL); + } + + acpi_gbl_memory_lists[ACPI_MEM_LIST_GLOBAL].total_allocated++; + acpi_gbl_memory_lists[ACPI_MEM_LIST_GLOBAL].current_total_size += (u32) size; + + return ((void *) &allocation->user_space); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_free_and_track + * + * PARAMETERS: Allocation - Address of the memory to deallocate + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: None + * + * DESCRIPTION: Frees the memory at Allocation + * + ******************************************************************************/ + +void +acpi_ut_free_and_track ( + void *allocation, + u32 component, + char *module, + u32 line) +{ + struct acpi_debug_mem_block *debug_block; + acpi_status status; + + + ACPI_FUNCTION_TRACE_PTR ("ut_free", allocation); + + + if (NULL == allocation) { + _ACPI_REPORT_ERROR (module, line, component, + ("acpi_ut_free: Attempt to delete a NULL address\n")); + + return_VOID; + } + + debug_block = ACPI_CAST_PTR (struct acpi_debug_mem_block, + (((char *) allocation) - sizeof (struct acpi_debug_mem_header))); + + acpi_gbl_memory_lists[ACPI_MEM_LIST_GLOBAL].total_freed++; + acpi_gbl_memory_lists[ACPI_MEM_LIST_GLOBAL].current_total_size -= debug_block->size; + + status = acpi_ut_remove_allocation (ACPI_MEM_LIST_GLOBAL, debug_block, + component, module, line); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not free memory, %s\n", + acpi_format_exception (status))); + } + + acpi_os_free (debug_block); + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "%p freed\n", allocation)); + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_find_allocation + * + * PARAMETERS: list_id - Memory list to search + * Allocation - Address of allocated memory + * + * RETURN: A list element if found; NULL otherwise. + * + * DESCRIPTION: Searches for an element in the global allocation tracking list. + * + ******************************************************************************/ + +struct acpi_debug_mem_block * +acpi_ut_find_allocation ( + u32 list_id, + void *allocation) +{ + struct acpi_debug_mem_block *element; + + + ACPI_FUNCTION_ENTRY (); + + + if (list_id > ACPI_MEM_LIST_MAX) { + return (NULL); + } + + element = acpi_gbl_memory_lists[list_id].list_head; + + /* Search for the address. */ + + while (element) { + if (element == allocation) { + return (element); + } + + element = element->next; + } + + return (NULL); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_track_allocation + * + * PARAMETERS: list_id - Memory list to search + * Allocation - Address of allocated memory + * Size - Size of the allocation + * alloc_type - MEM_MALLOC or MEM_CALLOC + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: None. + * + * DESCRIPTION: Inserts an element into the global allocation tracking list. + * + ******************************************************************************/ + +acpi_status +acpi_ut_track_allocation ( + u32 list_id, + struct acpi_debug_mem_block *allocation, + acpi_size size, + u8 alloc_type, + u32 component, + char *module, + u32 line) +{ + struct acpi_memory_list *mem_list; + struct acpi_debug_mem_block *element; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_PTR ("ut_track_allocation", allocation); + + + if (list_id > ACPI_MEM_LIST_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + mem_list = &acpi_gbl_memory_lists[list_id]; + status = acpi_ut_acquire_mutex (ACPI_MTX_MEMORY); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Search list for this address to make sure it is not already on the list. + * This will catch several kinds of problems. + */ + + element = acpi_ut_find_allocation (list_id, allocation); + if (element) { + ACPI_REPORT_ERROR (("ut_track_allocation: Allocation already present in list! (%p)\n", + allocation)); + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Element %p Address %p\n", element, allocation)); + + goto unlock_and_exit; + } + + /* Fill in the instance data. */ + + allocation->size = (u32) size; + allocation->alloc_type = alloc_type; + allocation->component = component; + allocation->line = line; + + ACPI_STRNCPY (allocation->module, module, ACPI_MAX_MODULE_NAME); + allocation->module[ACPI_MAX_MODULE_NAME-1] = 0; + + /* Insert at list head */ + + if (mem_list->list_head) { + ((struct acpi_debug_mem_block *)(mem_list->list_head))->previous = allocation; + } + + allocation->next = mem_list->list_head; + allocation->previous = NULL; + + mem_list->list_head = allocation; + + +unlock_and_exit: + status = acpi_ut_release_mutex (ACPI_MTX_MEMORY); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_remove_allocation + * + * PARAMETERS: list_id - Memory list to search + * Allocation - Address of allocated memory + * Component - Component type of caller + * Module - Source file name of caller + * Line - Line number of caller + * + * RETURN: + * + * DESCRIPTION: Deletes an element from the global allocation tracking list. + * + ******************************************************************************/ + +acpi_status +acpi_ut_remove_allocation ( + u32 list_id, + struct acpi_debug_mem_block *allocation, + u32 component, + char *module, + u32 line) +{ + struct acpi_memory_list *mem_list; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ut_remove_allocation"); + + + if (list_id > ACPI_MEM_LIST_MAX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + mem_list = &acpi_gbl_memory_lists[list_id]; + if (NULL == mem_list->list_head) { + /* No allocations! */ + + _ACPI_REPORT_ERROR (module, line, component, + ("ut_remove_allocation: Empty allocation list, nothing to free!\n")); + + return_ACPI_STATUS (AE_OK); + } + + status = acpi_ut_acquire_mutex (ACPI_MTX_MEMORY); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Unlink */ + + if (allocation->previous) { + (allocation->previous)->next = allocation->next; + } + else { + mem_list->list_head = allocation->next; + } + + if (allocation->next) { + (allocation->next)->previous = allocation->previous; + } + + /* Mark the segment as deleted */ + + ACPI_MEMSET (&allocation->user_space, 0xEA, allocation->size); + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Freeing size 0%X\n", allocation->size)); + + status = acpi_ut_release_mutex (ACPI_MTX_MEMORY); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_dump_allocation_info + * + * PARAMETERS: + * + * RETURN: None + * + * DESCRIPTION: Print some info about the outstanding allocations. + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +void +acpi_ut_dump_allocation_info ( + void) +{ +/* + struct acpi_memory_list *mem_list; +*/ + + ACPI_FUNCTION_TRACE ("ut_dump_allocation_info"); + +/* + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Current allocations", + mem_list->current_count, + ROUND_UP_TO_1K (mem_list->current_size))); + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Max concurrent allocations", + mem_list->max_concurrent_count, + ROUND_UP_TO_1K (mem_list->max_concurrent_size))); + + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Total (all) internal objects", + running_object_count, + ROUND_UP_TO_1K (running_object_size))); + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Total (all) allocations", + running_alloc_count, + ROUND_UP_TO_1K (running_alloc_size))); + + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Current Nodes", + acpi_gbl_current_node_count, + ROUND_UP_TO_1K (acpi_gbl_current_node_size))); + + ACPI_DEBUG_PRINT (TRACE_ALLOCATIONS | TRACE_TABLES, + ("%30s: %4d (%3d Kb)\n", "Max Nodes", + acpi_gbl_max_concurrent_node_count, + ROUND_UP_TO_1K ((acpi_gbl_max_concurrent_node_count * sizeof (struct acpi_namespace_node))))); +*/ + return_VOID; +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_dump_allocations + * + * PARAMETERS: Component - Component(s) to dump info for. + * Module - Module to dump info for. NULL means all. + * + * RETURN: None + * + * DESCRIPTION: Print a list of all outstanding allocations. + * + ******************************************************************************/ + +void +acpi_ut_dump_allocations ( + u32 component, + char *module) +{ + struct acpi_debug_mem_block *element; + union acpi_descriptor *descriptor; + u32 num_outstanding = 0; + + + ACPI_FUNCTION_TRACE ("ut_dump_allocations"); + + + /* + * Walk the allocation list. + */ + if (ACPI_FAILURE (acpi_ut_acquire_mutex (ACPI_MTX_MEMORY))) { + return; + } + + element = acpi_gbl_memory_lists[0].list_head; + while (element) { + if ((element->component & component) && + ((module == NULL) || (0 == ACPI_STRCMP (module, element->module)))) { + /* Ignore allocated objects that are in a cache */ + + descriptor = ACPI_CAST_PTR (union acpi_descriptor, &element->user_space); + if (descriptor->descriptor_id != ACPI_DESC_TYPE_CACHED) { + acpi_os_printf ("%p Len %04X %9.9s-%d [%s] ", + descriptor, element->size, element->module, + element->line, acpi_ut_get_descriptor_name (descriptor)); + + /* Most of the elements will be Operand objects. */ + + switch (ACPI_GET_DESCRIPTOR_TYPE (descriptor)) { + case ACPI_DESC_TYPE_OPERAND: + acpi_os_printf ("%12.12s R%hd", + acpi_ut_get_type_name (descriptor->object.common.type), + descriptor->object.common.reference_count); + break; + + case ACPI_DESC_TYPE_PARSER: + acpi_os_printf ("aml_opcode %04hX", + descriptor->op.asl.aml_opcode); + break; + + case ACPI_DESC_TYPE_NAMED: + acpi_os_printf ("%4.4s", + acpi_ut_get_node_name (&descriptor->node)); + break; + + default: + break; + } + + acpi_os_printf ( "\n"); + num_outstanding++; + } + } + element = element->next; + } + + (void) acpi_ut_release_mutex (ACPI_MTX_MEMORY); + + /* Print summary */ + + if (!num_outstanding) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "No outstanding allocations.\n")); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "%d(%X) Outstanding allocations\n", + num_outstanding, num_outstanding)); + } + + return_VOID; +} + + +#endif /* #ifdef ACPI_DBG_TRACK_ALLOCATIONS */ + diff --git a/drivers/acpi/utilities/utcopy.c b/drivers/acpi/utilities/utcopy.c new file mode 100644 index 00000000000..0fcd98bde0d --- /dev/null +++ b/drivers/acpi/utilities/utcopy.c @@ -0,0 +1,930 @@ +/****************************************************************************** + * + * Module Name: utcopy - Internal to external object translation utilities + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utcopy") + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_isimple_to_esimple + * + * PARAMETERS: *internal_object - Pointer to the object we are examining + * *Buffer - Where the object is returned + * *space_used - Where the data length is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to place a simple object in a user + * buffer. + * + * The buffer is assumed to have sufficient space for the object. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_isimple_to_esimple ( + union acpi_operand_object *internal_object, + union acpi_object *external_object, + u8 *data_space, + acpi_size *buffer_space_used) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ut_copy_isimple_to_esimple"); + + + *buffer_space_used = 0; + + /* + * Check for NULL object case (could be an uninitialized + * package element) + */ + if (!internal_object) { + return_ACPI_STATUS (AE_OK); + } + + /* Always clear the external object */ + + ACPI_MEMSET (external_object, 0, sizeof (union acpi_object)); + + /* + * In general, the external object will be the same type as + * the internal object + */ + external_object->type = ACPI_GET_OBJECT_TYPE (internal_object); + + /* However, only a limited number of external types are supported */ + + switch (ACPI_GET_OBJECT_TYPE (internal_object)) { + case ACPI_TYPE_STRING: + + external_object->string.pointer = (char *) data_space; + external_object->string.length = internal_object->string.length; + *buffer_space_used = ACPI_ROUND_UP_TO_NATIVE_WORD ((acpi_size) internal_object->string.length + 1); + + ACPI_MEMCPY ((void *) data_space, (void *) internal_object->string.pointer, + (acpi_size) internal_object->string.length + 1); + break; + + + case ACPI_TYPE_BUFFER: + + external_object->buffer.pointer = data_space; + external_object->buffer.length = internal_object->buffer.length; + *buffer_space_used = ACPI_ROUND_UP_TO_NATIVE_WORD (internal_object->string.length); + + ACPI_MEMCPY ((void *) data_space, (void *) internal_object->buffer.pointer, + internal_object->buffer.length); + break; + + + case ACPI_TYPE_INTEGER: + + external_object->integer.value = internal_object->integer.value; + break; + + + case ACPI_TYPE_LOCAL_REFERENCE: + + /* + * This is an object reference. Attempt to dereference it. + */ + switch (internal_object->reference.opcode) { + case AML_INT_NAMEPATH_OP: + + /* For namepath, return the object handle ("reference") */ + + default: + /* + * Use the object type of "Any" to indicate a reference + * to object containing a handle to an ACPI named object. + */ + external_object->type = ACPI_TYPE_ANY; + external_object->reference.handle = internal_object->reference.node; + break; + } + break; + + + case ACPI_TYPE_PROCESSOR: + + external_object->processor.proc_id = internal_object->processor.proc_id; + external_object->processor.pblk_address = internal_object->processor.address; + external_object->processor.pblk_length = internal_object->processor.length; + break; + + + case ACPI_TYPE_POWER: + + external_object->power_resource.system_level = + internal_object->power_resource.system_level; + + external_object->power_resource.resource_order = + internal_object->power_resource.resource_order; + break; + + + default: + /* + * There is no corresponding external object type + */ + return_ACPI_STATUS (AE_SUPPORT); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_ielement_to_eelement + * + * PARAMETERS: acpi_pkg_callback + * + * RETURN: Status + * + * DESCRIPTION: Copy one package element to another package element + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_ielement_to_eelement ( + u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state *state, + void *context) +{ + acpi_status status = AE_OK; + struct acpi_pkg_info *info = (struct acpi_pkg_info *) context; + acpi_size object_space; + u32 this_index; + union acpi_object *target_object; + + + ACPI_FUNCTION_ENTRY (); + + + this_index = state->pkg.index; + target_object = (union acpi_object *) + &((union acpi_object *)(state->pkg.dest_object))->package.elements[this_index]; + + switch (object_type) { + case ACPI_COPY_TYPE_SIMPLE: + + /* + * This is a simple or null object + */ + status = acpi_ut_copy_isimple_to_esimple (source_object, + target_object, info->free_space, &object_space); + if (ACPI_FAILURE (status)) { + return (status); + } + break; + + + case ACPI_COPY_TYPE_PACKAGE: + + /* + * Build the package object + */ + target_object->type = ACPI_TYPE_PACKAGE; + target_object->package.count = source_object->package.count; + target_object->package.elements = ACPI_CAST_PTR (union acpi_object, info->free_space); + + /* + * Pass the new package object back to the package walk routine + */ + state->pkg.this_target_obj = target_object; + + /* + * Save space for the array of objects (Package elements) + * update the buffer length counter + */ + object_space = ACPI_ROUND_UP_TO_NATIVE_WORD ( + (acpi_size) target_object->package.count * sizeof (union acpi_object)); + break; + + + default: + return (AE_BAD_PARAMETER); + } + + info->free_space += object_space; + info->length += object_space; + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_ipackage_to_epackage + * + * PARAMETERS: *internal_object - Pointer to the object we are returning + * *Buffer - Where the object is returned + * *space_used - Where the object length is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to place a package object in a user + * buffer. A package object by definition contains other objects. + * + * The buffer is assumed to have sufficient space for the object. + * The caller must have verified the buffer length needed using the + * acpi_ut_get_object_size function before calling this function. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_ipackage_to_epackage ( + union acpi_operand_object *internal_object, + u8 *buffer, + acpi_size *space_used) +{ + union acpi_object *external_object; + acpi_status status; + struct acpi_pkg_info info; + + + ACPI_FUNCTION_TRACE ("ut_copy_ipackage_to_epackage"); + + + /* + * First package at head of the buffer + */ + external_object = ACPI_CAST_PTR (union acpi_object, buffer); + + /* + * Free space begins right after the first package + */ + info.length = ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (union acpi_object)); + info.free_space = buffer + ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (union acpi_object)); + info.object_space = 0; + info.num_packages = 1; + + external_object->type = ACPI_GET_OBJECT_TYPE (internal_object); + external_object->package.count = internal_object->package.count; + external_object->package.elements = ACPI_CAST_PTR (union acpi_object, info.free_space); + + /* + * Leave room for an array of ACPI_OBJECTS in the buffer + * and move the free space past it + */ + info.length += (acpi_size) external_object->package.count * + ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (union acpi_object)); + info.free_space += external_object->package.count * + ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (union acpi_object)); + + status = acpi_ut_walk_package_tree (internal_object, external_object, + acpi_ut_copy_ielement_to_eelement, &info); + + *space_used = info.length; + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_iobject_to_eobject + * + * PARAMETERS: *internal_object - The internal object to be converted + * *buffer_ptr - Where the object is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to build an API object to be returned to + * the caller. + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_iobject_to_eobject ( + union acpi_operand_object *internal_object, + struct acpi_buffer *ret_buffer) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ut_copy_iobject_to_eobject"); + + + if (ACPI_GET_OBJECT_TYPE (internal_object) == ACPI_TYPE_PACKAGE) { + /* + * Package object: Copy all subobjects (including + * nested packages) + */ + status = acpi_ut_copy_ipackage_to_epackage (internal_object, + ret_buffer->pointer, &ret_buffer->length); + } + else { + /* + * Build a simple object (no nested objects) + */ + status = acpi_ut_copy_isimple_to_esimple (internal_object, + (union acpi_object *) ret_buffer->pointer, + ((u8 *) ret_buffer->pointer + + ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (union acpi_object))), + &ret_buffer->length); + /* + * build simple does not include the object size in the length + * so we add it in here + */ + ret_buffer->length += sizeof (union acpi_object); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_esimple_to_isimple + * + * PARAMETERS: *external_object - The external object to be converted + * *internal_object - Where the internal object is returned + * + * RETURN: Status + * + * DESCRIPTION: This function copies an external object to an internal one. + * NOTE: Pointers can be copied, we don't need to copy data. + * (The pointers have to be valid in our address space no matter + * what we do with them!) + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_esimple_to_isimple ( + union acpi_object *external_object, + union acpi_operand_object **ret_internal_object) +{ + union acpi_operand_object *internal_object; + + + ACPI_FUNCTION_TRACE ("ut_copy_esimple_to_isimple"); + + + /* + * Simple types supported are: String, Buffer, Integer + */ + switch (external_object->type) { + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + case ACPI_TYPE_INTEGER: + + internal_object = acpi_ut_create_internal_object ((u8) external_object->type); + if (!internal_object) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + break; + + default: + /* All other types are not supported */ + + return_ACPI_STATUS (AE_SUPPORT); + } + + + /* Must COPY string and buffer contents */ + + switch (external_object->type) { + case ACPI_TYPE_STRING: + + internal_object->string.pointer = + ACPI_MEM_CALLOCATE ((acpi_size) external_object->string.length + 1); + if (!internal_object->string.pointer) { + goto error_exit; + } + + ACPI_MEMCPY (internal_object->string.pointer, + external_object->string.pointer, + external_object->string.length); + + internal_object->string.length = external_object->string.length; + break; + + + case ACPI_TYPE_BUFFER: + + internal_object->buffer.pointer = + ACPI_MEM_CALLOCATE (external_object->buffer.length); + if (!internal_object->buffer.pointer) { + goto error_exit; + } + + ACPI_MEMCPY (internal_object->buffer.pointer, + external_object->buffer.pointer, + external_object->buffer.length); + + internal_object->buffer.length = external_object->buffer.length; + break; + + + case ACPI_TYPE_INTEGER: + + internal_object->integer.value = external_object->integer.value; + break; + + default: + /* Other types can't get here */ + break; + } + + *ret_internal_object = internal_object; + return_ACPI_STATUS (AE_OK); + + +error_exit: + acpi_ut_remove_reference (internal_object); + return_ACPI_STATUS (AE_NO_MEMORY); +} + + +#ifdef ACPI_FUTURE_IMPLEMENTATION + +/* Code to convert packages that are parameters to control methods */ + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_epackage_to_ipackage + * + * PARAMETERS: *internal_object - Pointer to the object we are returning + * *Buffer - Where the object is returned + * *space_used - Where the length of the object is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to place a package object in a user + * buffer. A package object by definition contains other objects. + * + * The buffer is assumed to have sufficient space for the object. + * The caller must have verified the buffer length needed using the + * acpi_ut_get_object_size function before calling this function. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_copy_epackage_to_ipackage ( + union acpi_operand_object *internal_object, + u8 *buffer, + u32 *space_used) +{ + u8 *free_space; + union acpi_object *external_object; + u32 length = 0; + u32 this_index; + u32 object_space = 0; + union acpi_operand_object *this_internal_obj; + union acpi_object *this_external_obj; + + + ACPI_FUNCTION_TRACE ("ut_copy_epackage_to_ipackage"); + + + /* + * First package at head of the buffer + */ + external_object = (union acpi_object *)buffer; + + /* + * Free space begins right after the first package + */ + free_space = buffer + sizeof(union acpi_object); + + + external_object->type = ACPI_GET_OBJECT_TYPE (internal_object); + external_object->package.count = internal_object->package.count; + external_object->package.elements = (union acpi_object *)free_space; + + /* + * Build an array of ACPI_OBJECTS in the buffer + * and move the free space past it + */ + free_space += external_object->package.count * sizeof(union acpi_object); + + + /* Call walk_package */ + +} + +#endif /* Future implementation */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_eobject_to_iobject + * + * PARAMETERS: *internal_object - The external object to be converted + * *buffer_ptr - Where the internal object is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: Converts an external object to an internal object. + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_eobject_to_iobject ( + union acpi_object *external_object, + union acpi_operand_object **internal_object) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ut_copy_eobject_to_iobject"); + + + if (external_object->type == ACPI_TYPE_PACKAGE) { + /* + * Packages as external input to control methods are not supported, + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Packages as parameters not implemented!\n")); + + return_ACPI_STATUS (AE_NOT_IMPLEMENTED); + } + + else { + /* + * Build a simple object (no nested objects) + */ + status = acpi_ut_copy_esimple_to_isimple (external_object, internal_object); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_simple_object + * + * PARAMETERS: source_desc - The internal object to be copied + * dest_desc - New target object + * + * RETURN: Status + * + * DESCRIPTION: Simple copy of one internal object to another. Reference count + * of the destination object is preserved. + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_simple_object ( + union acpi_operand_object *source_desc, + union acpi_operand_object *dest_desc) +{ + u16 reference_count; + union acpi_operand_object *next_object; + + + /* Save fields from destination that we don't want to overwrite */ + + reference_count = dest_desc->common.reference_count; + next_object = dest_desc->common.next_object; + + /* Copy the entire source object over the destination object*/ + + ACPI_MEMCPY ((char *) dest_desc, (char *) source_desc, + sizeof (union acpi_operand_object)); + + /* Restore the saved fields */ + + dest_desc->common.reference_count = reference_count; + dest_desc->common.next_object = next_object; + + /* Handle the objects with extra data */ + + switch (ACPI_GET_OBJECT_TYPE (dest_desc)) { + case ACPI_TYPE_BUFFER: + + dest_desc->buffer.node = NULL; + dest_desc->common.flags = source_desc->common.flags; + + /* + * Allocate and copy the actual buffer if and only if: + * 1) There is a valid buffer pointer + * 2) The buffer is not static (not in an ACPI table) (in this case, + * the actual pointer was already copied above) + */ + if ((source_desc->buffer.pointer) && + (!(source_desc->common.flags & AOPOBJ_STATIC_POINTER))) { + dest_desc->buffer.pointer = NULL; + + /* Create an actual buffer only if length > 0 */ + + if (source_desc->buffer.length) { + dest_desc->buffer.pointer = + ACPI_MEM_ALLOCATE (source_desc->buffer.length); + if (!dest_desc->buffer.pointer) { + return (AE_NO_MEMORY); + } + + /* Copy the actual buffer data */ + + ACPI_MEMCPY (dest_desc->buffer.pointer, + source_desc->buffer.pointer, + source_desc->buffer.length); + } + } + break; + + case ACPI_TYPE_STRING: + + /* + * Allocate and copy the actual string if and only if: + * 1) There is a valid string pointer + * 2) The string is not static (not in an ACPI table) (in this case, + * the actual pointer was already copied above) + */ + if ((source_desc->string.pointer) && + (!(source_desc->common.flags & AOPOBJ_STATIC_POINTER))) { + dest_desc->string.pointer = + ACPI_MEM_ALLOCATE ((acpi_size) source_desc->string.length + 1); + if (!dest_desc->string.pointer) { + return (AE_NO_MEMORY); + } + + ACPI_MEMCPY (dest_desc->string.pointer, source_desc->string.pointer, + (acpi_size) source_desc->string.length + 1); + } + break; + + case ACPI_TYPE_LOCAL_REFERENCE: + /* + * We copied the reference object, so we now must add a reference + * to the object pointed to by the reference + */ + acpi_ut_add_reference (source_desc->reference.object); + break; + + default: + /* Nothing to do for other simple objects */ + break; + } + + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_ielement_to_ielement + * + * PARAMETERS: acpi_pkg_callback + * + * RETURN: Status + * + * DESCRIPTION: Copy one package element to another package element + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_ielement_to_ielement ( + u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state *state, + void *context) +{ + acpi_status status = AE_OK; + u32 this_index; + union acpi_operand_object **this_target_ptr; + union acpi_operand_object *target_object; + + + ACPI_FUNCTION_ENTRY (); + + + this_index = state->pkg.index; + this_target_ptr = (union acpi_operand_object **) + &state->pkg.dest_object->package.elements[this_index]; + + switch (object_type) { + case ACPI_COPY_TYPE_SIMPLE: + + /* A null source object indicates a (legal) null package element */ + + if (source_object) { + /* + * This is a simple object, just copy it + */ + target_object = acpi_ut_create_internal_object ( + ACPI_GET_OBJECT_TYPE (source_object)); + if (!target_object) { + return (AE_NO_MEMORY); + } + + status = acpi_ut_copy_simple_object (source_object, target_object); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + *this_target_ptr = target_object; + } + else { + /* Pass through a null element */ + + *this_target_ptr = NULL; + } + break; + + + case ACPI_COPY_TYPE_PACKAGE: + + /* + * This object is a package - go down another nesting level + * Create and build the package object + */ + target_object = acpi_ut_create_internal_object (ACPI_TYPE_PACKAGE); + if (!target_object) { + return (AE_NO_MEMORY); + } + + target_object->package.count = source_object->package.count; + target_object->common.flags = source_object->common.flags; + + /* + * Create the object array + */ + target_object->package.elements = + ACPI_MEM_CALLOCATE (((acpi_size) source_object->package.count + 1) * + sizeof (void *)); + if (!target_object->package.elements) { + status = AE_NO_MEMORY; + goto error_exit; + } + + /* + * Pass the new package object back to the package walk routine + */ + state->pkg.this_target_obj = target_object; + + /* + * Store the object pointer in the parent package object + */ + *this_target_ptr = target_object; + break; + + + default: + return (AE_BAD_PARAMETER); + } + + return (status); + +error_exit: + acpi_ut_remove_reference (target_object); + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_ipackage_to_ipackage + * + * PARAMETERS: *source_obj - Pointer to the source package object + * *dest_obj - Where the internal object is returned + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to copy an internal package object + * into another internal package object. + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_ipackage_to_ipackage ( + union acpi_operand_object *source_obj, + union acpi_operand_object *dest_obj, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ut_copy_ipackage_to_ipackage"); + + + dest_obj->common.type = ACPI_GET_OBJECT_TYPE (source_obj); + dest_obj->common.flags = source_obj->common.flags; + dest_obj->package.count = source_obj->package.count; + + /* + * Create the object array and walk the source package tree + */ + dest_obj->package.elements = ACPI_MEM_CALLOCATE ( + ((acpi_size) source_obj->package.count + 1) * + sizeof (void *)); + if (!dest_obj->package.elements) { + ACPI_REPORT_ERROR ( + ("aml_build_copy_internal_package_object: Package allocation failure\n")); + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* + * Copy the package element-by-element by walking the package "tree". + * This handles nested packages of arbitrary depth. + */ + status = acpi_ut_walk_package_tree (source_obj, dest_obj, + acpi_ut_copy_ielement_to_ielement, walk_state); + if (ACPI_FAILURE (status)) { + /* On failure, delete the destination package object */ + + acpi_ut_remove_reference (dest_obj); + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_iobject_to_iobject + * + * PARAMETERS: walk_state - Current walk state + * source_desc - The internal object to be copied + * dest_desc - Where the copied object is returned + * + * RETURN: Status + * + * DESCRIPTION: Copy an internal object to a new internal object + * + ******************************************************************************/ + +acpi_status +acpi_ut_copy_iobject_to_iobject ( + union acpi_operand_object *source_desc, + union acpi_operand_object **dest_desc, + struct acpi_walk_state *walk_state) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("ut_copy_iobject_to_iobject"); + + + /* Create the top level object */ + + *dest_desc = acpi_ut_create_internal_object (ACPI_GET_OBJECT_TYPE (source_desc)); + if (!*dest_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Copy the object and possible subobjects */ + + if (ACPI_GET_OBJECT_TYPE (source_desc) == ACPI_TYPE_PACKAGE) { + status = acpi_ut_copy_ipackage_to_ipackage (source_desc, *dest_desc, + walk_state); + } + else { + status = acpi_ut_copy_simple_object (source_desc, *dest_desc); + } + + return_ACPI_STATUS (status); +} + + diff --git a/drivers/acpi/utilities/utdebug.c b/drivers/acpi/utilities/utdebug.c new file mode 100644 index 00000000000..985c5d045b7 --- /dev/null +++ b/drivers/acpi/utilities/utdebug.c @@ -0,0 +1,624 @@ +/****************************************************************************** + * + * Module Name: utdebug - Debug print routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utdebug") + + +#ifdef ACPI_DEBUG_OUTPUT + +static u32 acpi_gbl_prev_thread_id = 0xFFFFFFFF; +static char *acpi_gbl_fn_entry_str = "----Entry"; +static char *acpi_gbl_fn_exit_str = "----Exit-"; + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_init_stack_ptr_trace + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Save the current stack pointer + * + ****************************************************************************/ + +void +acpi_ut_init_stack_ptr_trace ( + void) +{ + u32 current_sp; + + + acpi_gbl_entry_stack_pointer = ACPI_PTR_DIFF (¤t_sp, NULL); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_track_stack_ptr + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Save the current stack pointer + * + ****************************************************************************/ + +void +acpi_ut_track_stack_ptr ( + void) +{ + acpi_size current_sp; + + + current_sp = ACPI_PTR_DIFF (¤t_sp, NULL); + + if (current_sp < acpi_gbl_lowest_stack_pointer) { + acpi_gbl_lowest_stack_pointer = current_sp; + } + + if (acpi_gbl_nesting_level > acpi_gbl_deepest_nesting) { + acpi_gbl_deepest_nesting = acpi_gbl_nesting_level; + } +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_debug_print + * + * PARAMETERS: debug_level - Requested debug print level + * proc_name - Caller's procedure name + * module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * component_id - Caller's component ID (for error output) + * + * Format - Printf format field + * ... - Optional printf arguments + * + * RETURN: None + * + * DESCRIPTION: Print error message with prefix consisting of the module name, + * line number, and component ID. + * + ****************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_debug_print ( + u32 requested_debug_level, + u32 line_number, + struct acpi_debug_print_info *dbg_info, + char *format, + ...) +{ + u32 thread_id; + va_list args; + + + /* + * Stay silent if the debug level or component ID is disabled + */ + if (!(requested_debug_level & acpi_dbg_level) || + !(dbg_info->component_id & acpi_dbg_layer)) { + return; + } + + /* + * Thread tracking and context switch notification + */ + thread_id = acpi_os_get_thread_id (); + + if (thread_id != acpi_gbl_prev_thread_id) { + if (ACPI_LV_THREADS & acpi_dbg_level) { + acpi_os_printf ("\n**** Context Switch from TID %X to TID %X ****\n\n", + acpi_gbl_prev_thread_id, thread_id); + } + + acpi_gbl_prev_thread_id = thread_id; + } + + /* + * Display the module name, current line number, thread ID (if requested), + * current procedure nesting level, and the current procedure name + */ + acpi_os_printf ("%8s-%04ld ", dbg_info->module_name, line_number); + + if (ACPI_LV_THREADS & acpi_dbg_level) { + acpi_os_printf ("[%04lX] ", thread_id); + } + + acpi_os_printf ("[%02ld] %-22.22s: ", acpi_gbl_nesting_level, dbg_info->proc_name); + + va_start (args, format); + acpi_os_vprintf (format, args); +} +EXPORT_SYMBOL(acpi_ut_debug_print); + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_debug_print_raw + * + * PARAMETERS: requested_debug_level - Requested debug print level + * line_number - Caller's line number + * dbg_info - Contains: + * proc_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * Format - Printf format field + * ... - Optional printf arguments + * + * RETURN: None + * + * DESCRIPTION: Print message with no headers. Has same interface as + * debug_print so that the same macros can be used. + * + ****************************************************************************/ + +void ACPI_INTERNAL_VAR_XFACE +acpi_ut_debug_print_raw ( + u32 requested_debug_level, + u32 line_number, + struct acpi_debug_print_info *dbg_info, + char *format, + ...) +{ + va_list args; + + + if (!(requested_debug_level & acpi_dbg_level) || + !(dbg_info->component_id & acpi_dbg_layer)) { + return; + } + + va_start (args, format); + acpi_os_vprintf (format, args); +} +EXPORT_SYMBOL(acpi_ut_debug_print_raw); + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_trace + * + * PARAMETERS: line_number - Caller's line number + * dbg_info - Contains: + * proc_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ****************************************************************************/ + +void +acpi_ut_trace ( + u32 line_number, + struct acpi_debug_print_info *dbg_info) +{ + + acpi_gbl_nesting_level++; + acpi_ut_track_stack_ptr (); + + acpi_ut_debug_print (ACPI_LV_FUNCTIONS, line_number, dbg_info, + "%s\n", acpi_gbl_fn_entry_str); +} +EXPORT_SYMBOL(acpi_ut_trace); + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_trace_ptr + * + * PARAMETERS: line_number - Caller's line number + * dbg_info - Contains: + * proc_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * Pointer - Pointer to display + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ****************************************************************************/ + +void +acpi_ut_trace_ptr ( + u32 line_number, + struct acpi_debug_print_info *dbg_info, + void *pointer) +{ + acpi_gbl_nesting_level++; + acpi_ut_track_stack_ptr (); + + acpi_ut_debug_print (ACPI_LV_FUNCTIONS, line_number, dbg_info, + "%s %p\n", acpi_gbl_fn_entry_str, pointer); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_trace_str + * + * PARAMETERS: line_number - Caller's line number + * dbg_info - Contains: + * proc_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * String - Additional string to display + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ****************************************************************************/ + +void +acpi_ut_trace_str ( + u32 line_number, + struct acpi_debug_print_info *dbg_info, + char *string) +{ + + acpi_gbl_nesting_level++; + acpi_ut_track_stack_ptr (); + + acpi_ut_debug_print (ACPI_LV_FUNCTIONS, line_number, dbg_info, + "%s %s\n", acpi_gbl_fn_entry_str, string); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_trace_u32 + * + * PARAMETERS: line_number - Caller's line number + * dbg_info - Contains: + * proc_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * Integer - Integer to display + * + * RETURN: None + * + * DESCRIPTION: Function entry trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ****************************************************************************/ + +void +acpi_ut_trace_u32 ( + u32 line_number, + struct acpi_debug_print_info *dbg_info, + u32 integer) +{ + + acpi_gbl_nesting_level++; + acpi_ut_track_stack_ptr (); + + acpi_ut_debug_print (ACPI_LV_FUNCTIONS, line_number, dbg_info, + "%s %08X\n", acpi_gbl_fn_entry_str, integer); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_exit + * + * PARAMETERS: line_number - Caller's line number + * dbg_info - Contains: + * proc_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level + * + ****************************************************************************/ + +void +acpi_ut_exit ( + u32 line_number, + struct acpi_debug_print_info *dbg_info) +{ + + acpi_ut_debug_print (ACPI_LV_FUNCTIONS, line_number, dbg_info, + "%s\n", acpi_gbl_fn_exit_str); + + acpi_gbl_nesting_level--; +} +EXPORT_SYMBOL(acpi_ut_exit); + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_status_exit + * + * PARAMETERS: line_number - Caller's line number + * dbg_info - Contains: + * proc_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * Status - Exit status code + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level. Prints exit status also. + * + ****************************************************************************/ + +void +acpi_ut_status_exit ( + u32 line_number, + struct acpi_debug_print_info *dbg_info, + acpi_status status) +{ + + if (ACPI_SUCCESS (status)) { + acpi_ut_debug_print (ACPI_LV_FUNCTIONS, line_number, dbg_info, + "%s %s\n", acpi_gbl_fn_exit_str, + acpi_format_exception (status)); + } + else { + acpi_ut_debug_print (ACPI_LV_FUNCTIONS, line_number, dbg_info, + "%s ****Exception****: %s\n", acpi_gbl_fn_exit_str, + acpi_format_exception (status)); + } + + acpi_gbl_nesting_level--; +} +EXPORT_SYMBOL(acpi_ut_status_exit); + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_value_exit + * + * PARAMETERS: line_number - Caller's line number + * dbg_info - Contains: + * proc_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * Value - Value to be printed with exit msg + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level. Prints exit value also. + * + ****************************************************************************/ + +void +acpi_ut_value_exit ( + u32 line_number, + struct acpi_debug_print_info *dbg_info, + acpi_integer value) +{ + + acpi_ut_debug_print (ACPI_LV_FUNCTIONS, line_number, dbg_info, + "%s %8.8X%8.8X\n", acpi_gbl_fn_exit_str, + ACPI_FORMAT_UINT64 (value)); + + acpi_gbl_nesting_level--; +} +EXPORT_SYMBOL(acpi_ut_value_exit); + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_ptr_exit + * + * PARAMETERS: line_number - Caller's line number + * dbg_info - Contains: + * proc_name - Caller's procedure name + * module_name - Caller's module name + * component_id - Caller's component ID + * Value - Value to be printed with exit msg + * + * RETURN: None + * + * DESCRIPTION: Function exit trace. Prints only if TRACE_FUNCTIONS bit is + * set in debug_level. Prints exit value also. + * + ****************************************************************************/ + +void +acpi_ut_ptr_exit ( + u32 line_number, + struct acpi_debug_print_info *dbg_info, + u8 *ptr) +{ + + acpi_ut_debug_print (ACPI_LV_FUNCTIONS, line_number, dbg_info, + "%s %p\n", acpi_gbl_fn_exit_str, ptr); + + acpi_gbl_nesting_level--; +} + +#endif + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_dump_buffer + * + * PARAMETERS: Buffer - Buffer to dump + * Count - Amount to dump, in bytes + * Display - BYTE, WORD, DWORD, or QWORD display + * component_iD - Caller's component ID + * + * RETURN: None + * + * DESCRIPTION: Generic dump buffer in both hex and ascii. + * + ****************************************************************************/ + +void +acpi_ut_dump_buffer ( + u8 *buffer, + u32 count, + u32 display, + u32 component_id) +{ + acpi_native_uint i = 0; + acpi_native_uint j; + u32 temp32; + u8 buf_char; + + + /* Only dump the buffer if tracing is enabled */ + + if (!((ACPI_LV_TABLES & acpi_dbg_level) && + (component_id & acpi_dbg_layer))) { + return; + } + + if ((count < 4) || (count & 0x01)) { + display = DB_BYTE_DISPLAY; + } + + acpi_os_printf ("\nOffset Value\n"); + + /* + * Nasty little dump buffer routine! + */ + while (i < count) { + /* Print current offset */ + + acpi_os_printf ("%05X ", (u32) i); + + /* Print 16 hex chars */ + + for (j = 0; j < 16;) { + if (i + j >= count) { + acpi_os_printf ("\n"); + return; + } + + /* Make sure that the s8 doesn't get sign-extended! */ + + switch (display) { + /* Default is BYTE display */ + + default: + + acpi_os_printf ("%02X ", + *((u8 *) &buffer[i + j])); + j += 1; + break; + + + case DB_WORD_DISPLAY: + + ACPI_MOVE_16_TO_32 (&temp32, &buffer[i + j]); + acpi_os_printf ("%04X ", temp32); + j += 2; + break; + + + case DB_DWORD_DISPLAY: + + ACPI_MOVE_32_TO_32 (&temp32, &buffer[i + j]); + acpi_os_printf ("%08X ", temp32); + j += 4; + break; + + + case DB_QWORD_DISPLAY: + + ACPI_MOVE_32_TO_32 (&temp32, &buffer[i + j]); + acpi_os_printf ("%08X", temp32); + + ACPI_MOVE_32_TO_32 (&temp32, &buffer[i + j + 4]); + acpi_os_printf ("%08X ", temp32); + j += 8; + break; + } + } + + /* + * Print the ASCII equivalent characters + * But watch out for the bad unprintable ones... + */ + for (j = 0; j < 16; j++) { + if (i + j >= count) { + acpi_os_printf ("\n"); + return; + } + + buf_char = buffer[i + j]; + if ((buf_char > 0x1F && buf_char < 0x2E) || + (buf_char > 0x2F && buf_char < 0x61) || + (buf_char > 0x60 && buf_char < 0x7F)) { + acpi_os_printf ("%c", buf_char); + } + else { + acpi_os_printf ("."); + } + } + + /* Done with that line. */ + + acpi_os_printf ("\n"); + i += 16; + } + + return; +} + diff --git a/drivers/acpi/utilities/utdelete.c b/drivers/acpi/utilities/utdelete.c new file mode 100644 index 00000000000..9a52ad52a23 --- /dev/null +++ b/drivers/acpi/utilities/utdelete.c @@ -0,0 +1,700 @@ +/******************************************************************************* + * + * Module Name: utdelete - object deletion and reference count utilities + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acinterp.h> +#include <acpi/acnamesp.h> +#include <acpi/acevents.h> +#include <acpi/amlcode.h> + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utdelete") + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_internal_obj + * + * PARAMETERS: *Object - Pointer to the list to be deleted + * + * RETURN: None + * + * DESCRIPTION: Low level object deletion, after reference counts have been + * updated (All reference counts, including sub-objects!) + * + ******************************************************************************/ + +void +acpi_ut_delete_internal_obj ( + union acpi_operand_object *object) +{ + void *obj_pointer = NULL; + union acpi_operand_object *handler_desc; + union acpi_operand_object *second_desc; + union acpi_operand_object *next_desc; + + + ACPI_FUNCTION_TRACE_PTR ("ut_delete_internal_obj", object); + + + if (!object) { + return_VOID; + } + + /* + * Must delete or free any pointers within the object that are not + * actual ACPI objects (for example, a raw buffer pointer). + */ + switch (ACPI_GET_OBJECT_TYPE (object)) { + case ACPI_TYPE_STRING: + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "**** String %p, ptr %p\n", + object, object->string.pointer)); + + /* Free the actual string buffer */ + + if (!(object->common.flags & AOPOBJ_STATIC_POINTER)) { + /* But only if it is NOT a pointer into an ACPI table */ + + obj_pointer = object->string.pointer; + } + break; + + + case ACPI_TYPE_BUFFER: + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "**** Buffer %p, ptr %p\n", + object, object->buffer.pointer)); + + /* Free the actual buffer */ + + if (!(object->common.flags & AOPOBJ_STATIC_POINTER)) { + /* But only if it is NOT a pointer into an ACPI table */ + + obj_pointer = object->buffer.pointer; + } + break; + + + case ACPI_TYPE_PACKAGE: + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, " **** Package of count %X\n", + object->package.count)); + + /* + * Elements of the package are not handled here, they are deleted + * separately + */ + + /* Free the (variable length) element pointer array */ + + obj_pointer = object->package.elements; + break; + + + case ACPI_TYPE_DEVICE: + + if (object->device.gpe_block) { + (void) acpi_ev_delete_gpe_block (object->device.gpe_block); + } + + /* Walk the handler list for this device */ + + handler_desc = object->device.handler; + while (handler_desc) { + next_desc = handler_desc->address_space.next; + acpi_ut_remove_reference (handler_desc); + handler_desc = next_desc; + } + break; + + + case ACPI_TYPE_MUTEX: + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "***** Mutex %p, Semaphore %p\n", + object, object->mutex.semaphore)); + + acpi_ex_unlink_mutex (object); + (void) acpi_os_delete_semaphore (object->mutex.semaphore); + break; + + + case ACPI_TYPE_EVENT: + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "***** Event %p, Semaphore %p\n", + object, object->event.semaphore)); + + (void) acpi_os_delete_semaphore (object->event.semaphore); + object->event.semaphore = NULL; + break; + + + case ACPI_TYPE_METHOD: + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "***** Method %p\n", object)); + + /* Delete the method semaphore if it exists */ + + if (object->method.semaphore) { + (void) acpi_os_delete_semaphore (object->method.semaphore); + object->method.semaphore = NULL; + } + break; + + + case ACPI_TYPE_REGION: + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "***** Region %p\n", object)); + + second_desc = acpi_ns_get_secondary_object (object); + if (second_desc) { + /* + * Free the region_context if and only if the handler is one of the + * default handlers -- and therefore, we created the context object + * locally, it was not created by an external caller. + */ + handler_desc = object->region.handler; + if (handler_desc) { + if (handler_desc->address_space.hflags & ACPI_ADDR_HANDLER_DEFAULT_INSTALLED) { + obj_pointer = second_desc->extra.region_context; + } + + acpi_ut_remove_reference (handler_desc); + } + + /* Now we can free the Extra object */ + + acpi_ut_delete_object_desc (second_desc); + } + break; + + + case ACPI_TYPE_BUFFER_FIELD: + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "***** Buffer Field %p\n", object)); + + second_desc = acpi_ns_get_secondary_object (object); + if (second_desc) { + acpi_ut_delete_object_desc (second_desc); + } + break; + + + default: + break; + } + + /* Free any allocated memory (pointer within the object) found above */ + + if (obj_pointer) { + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Deleting Object Subptr %p\n", + obj_pointer)); + ACPI_MEM_FREE (obj_pointer); + } + + /* Now the object can be safely deleted */ + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Deleting Object %p [%s]\n", + object, acpi_ut_get_object_type_name (object))); + + acpi_ut_delete_object_desc (object); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_internal_object_list + * + * PARAMETERS: *obj_list - Pointer to the list to be deleted + * + * RETURN: None + * + * DESCRIPTION: This function deletes an internal object list, including both + * simple objects and package objects + * + ******************************************************************************/ + +void +acpi_ut_delete_internal_object_list ( + union acpi_operand_object **obj_list) +{ + union acpi_operand_object **internal_obj; + + + ACPI_FUNCTION_TRACE ("ut_delete_internal_object_list"); + + + /* Walk the null-terminated internal list */ + + for (internal_obj = obj_list; *internal_obj; internal_obj++) { + acpi_ut_remove_reference (*internal_obj); + } + + /* Free the combined parameter pointer list and object array */ + + ACPI_MEM_FREE (obj_list); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_update_ref_count + * + * PARAMETERS: *Object - Object whose ref count is to be updated + * Action - What to do + * + * RETURN: New ref count + * + * DESCRIPTION: Modify the ref count and return it. + * + ******************************************************************************/ + +static void +acpi_ut_update_ref_count ( + union acpi_operand_object *object, + u32 action) +{ + u16 count; + u16 new_count; + + + ACPI_FUNCTION_NAME ("ut_update_ref_count"); + + + if (!object) { + return; + } + + count = object->common.reference_count; + new_count = count; + + /* + * Perform the reference count action (increment, decrement, or force delete) + */ + switch (action) { + + case REF_INCREMENT: + + new_count++; + object->common.reference_count = new_count; + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Obj %p Refs=%X, [Incremented]\n", + object, new_count)); + break; + + + case REF_DECREMENT: + + if (count < 1) { + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Obj %p Refs=%X, can't decrement! (Set to 0)\n", + object, new_count)); + + new_count = 0; + } + else { + new_count--; + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Obj %p Refs=%X, [Decremented]\n", + object, new_count)); + } + + if (ACPI_GET_OBJECT_TYPE (object) == ACPI_TYPE_METHOD) { + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Method Obj %p Refs=%X, [Decremented]\n", + object, new_count)); + } + + object->common.reference_count = new_count; + if (new_count == 0) { + acpi_ut_delete_internal_obj (object); + } + + break; + + + case REF_FORCE_DELETE: + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Obj %p Refs=%X, Force delete! (Set to 0)\n", + object, count)); + + new_count = 0; + object->common.reference_count = new_count; + acpi_ut_delete_internal_obj (object); + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown action (%X)\n", action)); + break; + } + + /* + * Sanity check the reference count, for debug purposes only. + * (A deleted object will have a huge reference count) + */ + if (count > ACPI_MAX_REFERENCE_COUNT) { + + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, + "**** Warning **** Large Reference Count (%X) in object %p\n\n", + count, object)); + } + + return; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_update_object_reference + * + * PARAMETERS: *Object - Increment ref count for this object + * and all sub-objects + * Action - Either REF_INCREMENT or REF_DECREMENT or + * REF_FORCE_DELETE + * + * RETURN: Status + * + * DESCRIPTION: Increment the object reference count + * + * Object references are incremented when: + * 1) An object is attached to a Node (namespace object) + * 2) An object is copied (all subobjects must be incremented) + * + * Object references are decremented when: + * 1) An object is detached from an Node + * + ******************************************************************************/ + +acpi_status +acpi_ut_update_object_reference ( + union acpi_operand_object *object, + u16 action) +{ + acpi_status status; + u32 i; + union acpi_generic_state *state_list = NULL; + union acpi_generic_state *state; + union acpi_operand_object *tmp; + + ACPI_FUNCTION_TRACE_PTR ("ut_update_object_reference", object); + + + /* Ignore a null object ptr */ + + if (!object) { + return_ACPI_STATUS (AE_OK); + } + + /* Make sure that this isn't a namespace handle */ + + if (ACPI_GET_DESCRIPTOR_TYPE (object) == ACPI_DESC_TYPE_NAMED) { + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "Object %p is NS handle\n", object)); + return_ACPI_STATUS (AE_OK); + } + + state = acpi_ut_create_update_state (object, action); + + while (state) { + object = state->update.object; + action = state->update.value; + acpi_ut_delete_generic_state (state); + + /* + * All sub-objects must have their reference count incremented also. + * Different object types have different subobjects. + */ + switch (ACPI_GET_OBJECT_TYPE (object)) { + case ACPI_TYPE_DEVICE: + + tmp = object->device.system_notify; + if (tmp && (tmp->common.reference_count <= 1) && action == REF_DECREMENT) + object->device.system_notify = NULL; + acpi_ut_update_ref_count (tmp, action); + + tmp = object->device.device_notify; + if (tmp && (tmp->common.reference_count <= 1) && action == REF_DECREMENT) + object->device.device_notify = NULL; + acpi_ut_update_ref_count (tmp, action); + + break; + + + case ACPI_TYPE_PACKAGE: + + /* + * We must update all the sub-objects of the package + * (Each of whom may have their own sub-objects, etc. + */ + for (i = 0; i < object->package.count; i++) { + /* + * Push each element onto the stack for later processing. + * Note: There can be null elements within the package, + * these are simply ignored + */ + status = acpi_ut_create_update_state_and_push ( + object->package.elements[i], action, &state_list); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + tmp = object->package.elements[i]; + if (tmp && (tmp->common.reference_count <= 1) && action == REF_DECREMENT) + object->package.elements[i] = NULL; + } + break; + + + case ACPI_TYPE_BUFFER_FIELD: + + status = acpi_ut_create_update_state_and_push ( + object->buffer_field.buffer_obj, action, &state_list); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + tmp = object->buffer_field.buffer_obj; + if ( tmp && (tmp->common.reference_count <= 1) && action == REF_DECREMENT) + object->buffer_field.buffer_obj = NULL; + break; + + + case ACPI_TYPE_LOCAL_REGION_FIELD: + + status = acpi_ut_create_update_state_and_push ( + object->field.region_obj, action, &state_list); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + tmp = object->field.region_obj; + if ( tmp && (tmp->common.reference_count <= 1) && action == REF_DECREMENT) + object->field.region_obj = NULL; + break; + + + case ACPI_TYPE_LOCAL_BANK_FIELD: + + status = acpi_ut_create_update_state_and_push ( + object->bank_field.bank_obj, action, &state_list); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + tmp = object->bank_field.bank_obj; + if ( tmp && (tmp->common.reference_count <= 1) && action == REF_DECREMENT) + object->bank_field.bank_obj = NULL; + + status = acpi_ut_create_update_state_and_push ( + object->bank_field.region_obj, action, &state_list); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + tmp = object->bank_field.region_obj; + if ( tmp && (tmp->common.reference_count <= 1) && action == REF_DECREMENT) + object->bank_field.region_obj = NULL; + break; + + + case ACPI_TYPE_LOCAL_INDEX_FIELD: + + status = acpi_ut_create_update_state_and_push ( + object->index_field.index_obj, action, &state_list); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + tmp = object->index_field.index_obj; + if ( tmp && (tmp->common.reference_count <= 1) && action == REF_DECREMENT) + object->index_field.index_obj = NULL; + + status = acpi_ut_create_update_state_and_push ( + object->index_field.data_obj, action, &state_list); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + + tmp = object->index_field.data_obj; + if ( tmp && (tmp->common.reference_count <= 1) && action == REF_DECREMENT) + object->index_field.data_obj = NULL; + break; + + + case ACPI_TYPE_LOCAL_REFERENCE: + + /* + * The target of an Index (a package, string, or buffer) must track + * changes to the ref count of the index. + */ + if (object->reference.opcode == AML_INDEX_OP) { + status = acpi_ut_create_update_state_and_push ( + object->reference.object, action, &state_list); + if (ACPI_FAILURE (status)) { + goto error_exit; + } + } + break; + + + case ACPI_TYPE_REGION: + default: + + /* No subobjects */ + break; + } + + /* + * Now we can update the count in the main object. This can only + * happen after we update the sub-objects in case this causes the + * main object to be deleted. + */ + acpi_ut_update_ref_count (object, action); + + /* Move on to the next object to be updated */ + + state = acpi_ut_pop_generic_state (&state_list); + } + + return_ACPI_STATUS (AE_OK); + + +error_exit: + + ACPI_REPORT_ERROR (("Could not update object reference count, %s\n", + acpi_format_exception (status))); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_add_reference + * + * PARAMETERS: *Object - Object whose reference count is to be + * incremented + * + * RETURN: None + * + * DESCRIPTION: Add one reference to an ACPI object + * + ******************************************************************************/ + +void +acpi_ut_add_reference ( + union acpi_operand_object *object) +{ + + ACPI_FUNCTION_TRACE_PTR ("ut_add_reference", object); + + + /* Ensure that we have a valid object */ + + if (!acpi_ut_valid_internal_object (object)) { + return_VOID; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, + "Obj %p Current Refs=%X [To Be Incremented]\n", + object, object->common.reference_count)); + + /* Increment the reference count */ + + (void) acpi_ut_update_object_reference (object, REF_INCREMENT); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_remove_reference + * + * PARAMETERS: *Object - Object whose ref count will be decremented + * + * RETURN: None + * + * DESCRIPTION: Decrement the reference count of an ACPI internal object + * + ******************************************************************************/ + +void +acpi_ut_remove_reference ( + union acpi_operand_object *object) +{ + + ACPI_FUNCTION_TRACE_PTR ("ut_remove_reference", object); + + + /* + * Allow a NULL pointer to be passed in, just ignore it. This saves + * each caller from having to check. Also, ignore NS nodes. + * + */ + if (!object || + (ACPI_GET_DESCRIPTOR_TYPE (object) == ACPI_DESC_TYPE_NAMED)) { + return_VOID; + } + + /* Ensure that we have a valid object */ + + if (!acpi_ut_valid_internal_object (object)) { + return_VOID; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, + "Obj %p Current Refs=%X [To Be Decremented]\n", + object, object->common.reference_count)); + + /* + * Decrement the reference count, and only actually delete the object + * if the reference count becomes 0. (Must also decrement the ref count + * of all subobjects!) + */ + (void) acpi_ut_update_object_reference (object, REF_DECREMENT); + return_VOID; +} + + diff --git a/drivers/acpi/utilities/uteval.c b/drivers/acpi/utilities/uteval.c new file mode 100644 index 00000000000..ead27d2c4d1 --- /dev/null +++ b/drivers/acpi/utilities/uteval.c @@ -0,0 +1,696 @@ +/****************************************************************************** + * + * Module Name: uteval - Object evaluation + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acinterp.h> + + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("uteval") + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_osi_implementation + * + * PARAMETERS: walk_state - Current walk state + * + * RETURN: Status + * + * DESCRIPTION: Implementation of _OSI predefined control method + * Supported = _OSI (String) + * + ******************************************************************************/ + +acpi_status +acpi_ut_osi_implementation ( + struct acpi_walk_state *walk_state) +{ + union acpi_operand_object *string_desc; + union acpi_operand_object *return_desc; + acpi_native_uint i; + + + ACPI_FUNCTION_TRACE ("ut_osi_implementation"); + + + /* Validate the string input argument */ + + string_desc = walk_state->arguments[0].object; + if (!string_desc || (string_desc->common.type != ACPI_TYPE_STRING)) { + return_ACPI_STATUS (AE_TYPE); + } + + /* Create a return object (Default value = 0) */ + + return_desc = acpi_ut_create_internal_object (ACPI_TYPE_INTEGER); + if (!return_desc) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Compare input string to table of supported strings */ + + for (i = 0; i < ACPI_NUM_OSI_STRINGS; i++) { + if (!ACPI_STRCMP (string_desc->string.pointer, + (char *) acpi_gbl_valid_osi_strings[i])) { + /* This string is supported */ + + return_desc->integer.value = 0xFFFFFFFF; + break; + } + } + + walk_state->return_desc = return_desc; + return_ACPI_STATUS (AE_CTRL_TERMINATE); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_evaluate_object + * + * PARAMETERS: prefix_node - Starting node + * Path - Path to object from starting node + * expected_return_types - Bitmap of allowed return types + * return_desc - Where a return value is stored + * + * RETURN: Status + * + * DESCRIPTION: Evaluates a namespace object and verifies the type of the + * return object. Common code that simplifies accessing objects + * that have required return objects of fixed types. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_evaluate_object ( + struct acpi_namespace_node *prefix_node, + char *path, + u32 expected_return_btypes, + union acpi_operand_object **return_desc) +{ + struct acpi_parameter_info info; + acpi_status status; + u32 return_btype; + + + ACPI_FUNCTION_TRACE ("ut_evaluate_object"); + + + info.node = prefix_node; + info.parameters = NULL; + info.parameter_type = ACPI_PARAM_ARGS; + + /* Evaluate the object/method */ + + status = acpi_ns_evaluate_relative (path, &info); + if (ACPI_FAILURE (status)) { + if (status == AE_NOT_FOUND) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[%4.4s.%s] was not found\n", + acpi_ut_get_node_name (prefix_node), path)); + } + else { + ACPI_REPORT_METHOD_ERROR ("Method execution failed", + prefix_node, path, status); + } + + return_ACPI_STATUS (status); + } + + /* Did we get a return object? */ + + if (!info.return_object) { + if (expected_return_btypes) { + ACPI_REPORT_METHOD_ERROR ("No object was returned from", + prefix_node, path, AE_NOT_EXIST); + + return_ACPI_STATUS (AE_NOT_EXIST); + } + + return_ACPI_STATUS (AE_OK); + } + + /* Map the return object type to the bitmapped type */ + + switch (ACPI_GET_OBJECT_TYPE (info.return_object)) { + case ACPI_TYPE_INTEGER: + return_btype = ACPI_BTYPE_INTEGER; + break; + + case ACPI_TYPE_BUFFER: + return_btype = ACPI_BTYPE_BUFFER; + break; + + case ACPI_TYPE_STRING: + return_btype = ACPI_BTYPE_STRING; + break; + + case ACPI_TYPE_PACKAGE: + return_btype = ACPI_BTYPE_PACKAGE; + break; + + default: + return_btype = 0; + break; + } + + if ((acpi_gbl_enable_interpreter_slack) && + (!expected_return_btypes)) { + /* + * We received a return object, but one was not expected. This can + * happen frequently if the "implicit return" feature is enabled. + * Just delete the return object and return AE_OK. + */ + acpi_ut_remove_reference (info.return_object); + return_ACPI_STATUS (AE_OK); + } + + /* Is the return object one of the expected types? */ + + if (!(expected_return_btypes & return_btype)) { + ACPI_REPORT_METHOD_ERROR ("Return object type is incorrect", + prefix_node, path, AE_TYPE); + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Type returned from %s was incorrect: %s, expected Btypes: %X\n", + path, acpi_ut_get_object_type_name (info.return_object), + expected_return_btypes)); + + /* On error exit, we must delete the return object */ + + acpi_ut_remove_reference (info.return_object); + return_ACPI_STATUS (AE_TYPE); + } + + /* Object type is OK, return it */ + + *return_desc = info.return_object; + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_evaluate_numeric_object + * + * PARAMETERS: *object_name - Object name to be evaluated + * device_node - Node for the device + * *Address - Where the value is returned + * + * RETURN: Status + * + * DESCRIPTION: Evaluates a numeric namespace object for a selected device + * and stores result in *Address. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_evaluate_numeric_object ( + char *object_name, + struct acpi_namespace_node *device_node, + acpi_integer *address) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ut_evaluate_numeric_object"); + + + status = acpi_ut_evaluate_object (device_node, object_name, + ACPI_BTYPE_INTEGER, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Get the returned Integer */ + + *address = obj_desc->integer.value; + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_copy_id_string + * + * PARAMETERS: Destination - Where to copy the string + * Source - Source string + * max_length - Length of the destination buffer + * + * RETURN: None + * + * DESCRIPTION: Copies an ID string for the _HID, _CID, and _UID methods. + * Performs removal of a leading asterisk if present -- workaround + * for a known issue on a bunch of machines. + * + ******************************************************************************/ + +static void +acpi_ut_copy_id_string ( + char *destination, + char *source, + acpi_size max_length) +{ + + + /* + * Workaround for ID strings that have a leading asterisk. This construct + * is not allowed by the ACPI specification (ID strings must be + * alphanumeric), but enough existing machines have this embedded in their + * ID strings that the following code is useful. + */ + if (*source == '*') { + source++; + } + + /* Do the actual copy */ + + ACPI_STRNCPY (destination, source, max_length); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_HID + * + * PARAMETERS: device_node - Node for the device + * *Hid - Where the HID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _HID control method that returns the hardware + * ID of the device. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_HID ( + struct acpi_namespace_node *device_node, + struct acpi_device_id *hid) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ut_execute_HID"); + + + status = acpi_ut_evaluate_object (device_node, METHOD_NAME__HID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_INTEGER) { + /* Convert the Numeric HID to string */ + + acpi_ex_eisa_id_to_string ((u32) obj_desc->integer.value, hid->value); + } + else { + /* Copy the String HID from the returned object */ + + acpi_ut_copy_id_string (hid->value, obj_desc->string.pointer, + sizeof (hid->value)); + } + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_translate_one_cid + * + * PARAMETERS: obj_desc - _CID object, must be integer or string + * one_cid - Where the CID string is returned + * + * RETURN: Status + * + * DESCRIPTION: Return a numeric or string _CID value as a string. + * (Compatible ID) + * + * NOTE: Assumes a maximum _CID string length of + * ACPI_MAX_CID_LENGTH. + * + ******************************************************************************/ + +static acpi_status +acpi_ut_translate_one_cid ( + union acpi_operand_object *obj_desc, + struct acpi_compatible_id *one_cid) +{ + + + switch (ACPI_GET_OBJECT_TYPE (obj_desc)) { + case ACPI_TYPE_INTEGER: + + /* Convert the Numeric CID to string */ + + acpi_ex_eisa_id_to_string ((u32) obj_desc->integer.value, one_cid->value); + return (AE_OK); + + case ACPI_TYPE_STRING: + + if (obj_desc->string.length > ACPI_MAX_CID_LENGTH) { + return (AE_AML_STRING_LIMIT); + } + + /* Copy the String CID from the returned object */ + + acpi_ut_copy_id_string (one_cid->value, obj_desc->string.pointer, + ACPI_MAX_CID_LENGTH); + return (AE_OK); + + default: + + return (AE_TYPE); + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_CID + * + * PARAMETERS: device_node - Node for the device + * *Cid - Where the CID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _CID control method that returns one or more + * compatible hardware IDs for the device. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_CID ( + struct acpi_namespace_node *device_node, + struct acpi_compatible_id_list **return_cid_list) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + u32 count; + u32 size; + struct acpi_compatible_id_list *cid_list; + acpi_native_uint i; + + + ACPI_FUNCTION_TRACE ("ut_execute_CID"); + + + /* Evaluate the _CID method for this device */ + + status = acpi_ut_evaluate_object (device_node, METHOD_NAME__CID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING | ACPI_BTYPE_PACKAGE, + &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Get the number of _CIDs returned */ + + count = 1; + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_PACKAGE) { + count = obj_desc->package.count; + } + + /* Allocate a worst-case buffer for the _CIDs */ + + size = (((count - 1) * sizeof (struct acpi_compatible_id)) + + sizeof (struct acpi_compatible_id_list)); + + cid_list = ACPI_MEM_CALLOCATE ((acpi_size) size); + if (!cid_list) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + /* Init CID list */ + + cid_list->count = count; + cid_list->size = size; + + /* + * A _CID can return either a single compatible ID or a package of compatible + * IDs. Each compatible ID can be one of the following: + * -- Number (32 bit compressed EISA ID) or + * -- String (PCI ID format, e.g. "PCI\VEN_vvvv&DEV_dddd&SUBSYS_ssssssss"). + */ + + /* The _CID object can be either a single CID or a package (list) of CIDs */ + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_PACKAGE) { + /* Translate each package element */ + + for (i = 0; i < count; i++) { + status = acpi_ut_translate_one_cid (obj_desc->package.elements[i], + &cid_list->id[i]); + if (ACPI_FAILURE (status)) { + break; + } + } + } + else { + /* Only one CID, translate to a string */ + + status = acpi_ut_translate_one_cid (obj_desc, cid_list->id); + } + + /* Cleanup on error */ + + if (ACPI_FAILURE (status)) { + ACPI_MEM_FREE (cid_list); + } + else { + *return_cid_list = cid_list; + } + + /* On exit, we must delete the _CID return object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_UID + * + * PARAMETERS: device_node - Node for the device + * *Uid - Where the UID is returned + * + * RETURN: Status + * + * DESCRIPTION: Executes the _UID control method that returns the hardware + * ID of the device. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_UID ( + struct acpi_namespace_node *device_node, + struct acpi_device_id *uid) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ut_execute_UID"); + + + status = acpi_ut_evaluate_object (device_node, METHOD_NAME__UID, + ACPI_BTYPE_INTEGER | ACPI_BTYPE_STRING, &obj_desc); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + if (ACPI_GET_OBJECT_TYPE (obj_desc) == ACPI_TYPE_INTEGER) { + /* Convert the Numeric UID to string */ + + acpi_ex_unsigned_integer_to_string (obj_desc->integer.value, uid->value); + } + else { + /* Copy the String UID from the returned object */ + + acpi_ut_copy_id_string (uid->value, obj_desc->string.pointer, + sizeof (uid->value)); + } + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_STA + * + * PARAMETERS: device_node - Node for the device + * *Flags - Where the status flags are returned + * + * RETURN: Status + * + * DESCRIPTION: Executes _STA for selected device and stores results in + * *Flags. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_STA ( + struct acpi_namespace_node *device_node, + u32 *flags) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ut_execute_STA"); + + + status = acpi_ut_evaluate_object (device_node, METHOD_NAME__STA, + ACPI_BTYPE_INTEGER, &obj_desc); + if (ACPI_FAILURE (status)) { + if (AE_NOT_FOUND == status) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "_STA on %4.4s was not found, assuming device is present\n", + acpi_ut_get_node_name (device_node))); + + *flags = 0x0F; + status = AE_OK; + } + + return_ACPI_STATUS (status); + } + + /* Extract the status flags */ + + *flags = (u32) obj_desc->integer.value; + + /* On exit, we must delete the return object */ + + acpi_ut_remove_reference (obj_desc); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_execute_Sxds + * + * PARAMETERS: device_node - Node for the device + * *Flags - Where the status flags are returned + * + * RETURN: Status + * + * DESCRIPTION: Executes _STA for selected device and stores results in + * *Flags. + * + * NOTE: Internal function, no parameter validation + * + ******************************************************************************/ + +acpi_status +acpi_ut_execute_sxds ( + struct acpi_namespace_node *device_node, + u8 *highest) +{ + union acpi_operand_object *obj_desc; + acpi_status status; + u32 i; + + + ACPI_FUNCTION_TRACE ("ut_execute_Sxds"); + + + for (i = 0; i < 4; i++) { + highest[i] = 0xFF; + status = acpi_ut_evaluate_object (device_node, + (char *) acpi_gbl_highest_dstate_names[i], + ACPI_BTYPE_INTEGER, &obj_desc); + if (ACPI_FAILURE (status)) { + if (status != AE_NOT_FOUND) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "%s on Device %4.4s, %s\n", + (char *) acpi_gbl_highest_dstate_names[i], + acpi_ut_get_node_name (device_node), + acpi_format_exception (status))); + + return_ACPI_STATUS (status); + } + } + else { + /* Extract the Dstate value */ + + highest[i] = (u8) obj_desc->integer.value; + + /* Delete the return object */ + + acpi_ut_remove_reference (obj_desc); + } + } + + return_ACPI_STATUS (AE_OK); +} diff --git a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/utilities/utglobal.c new file mode 100644 index 00000000000..25b0f8ae1bc --- /dev/null +++ b/drivers/acpi/utilities/utglobal.c @@ -0,0 +1,935 @@ +/****************************************************************************** + * + * Module Name: utglobal - Global variables for the ACPI subsystem + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#define DEFINE_ACPI_GLOBALS + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utglobal") + + +/****************************************************************************** + * + * FUNCTION: acpi_format_exception + * + * PARAMETERS: Status - The acpi_status code to be formatted + * + * RETURN: A string containing the exception text + * + * DESCRIPTION: This function translates an ACPI exception into an ASCII string. + * + ******************************************************************************/ + +const char * +acpi_format_exception ( + acpi_status status) +{ + const char *exception = "UNKNOWN_STATUS_CODE"; + acpi_status sub_status; + + + ACPI_FUNCTION_NAME ("format_exception"); + + + sub_status = (status & ~AE_CODE_MASK); + + switch (status & AE_CODE_MASK) { + case AE_CODE_ENVIRONMENTAL: + + if (sub_status <= AE_CODE_ENV_MAX) { + exception = acpi_gbl_exception_names_env [sub_status]; + break; + } + goto unknown; + + case AE_CODE_PROGRAMMER: + + if (sub_status <= AE_CODE_PGM_MAX) { + exception = acpi_gbl_exception_names_pgm [sub_status -1]; + break; + } + goto unknown; + + case AE_CODE_ACPI_TABLES: + + if (sub_status <= AE_CODE_TBL_MAX) { + exception = acpi_gbl_exception_names_tbl [sub_status -1]; + break; + } + goto unknown; + + case AE_CODE_AML: + + if (sub_status <= AE_CODE_AML_MAX) { + exception = acpi_gbl_exception_names_aml [sub_status -1]; + break; + } + goto unknown; + + case AE_CODE_CONTROL: + + if (sub_status <= AE_CODE_CTRL_MAX) { + exception = acpi_gbl_exception_names_ctrl [sub_status -1]; + break; + } + goto unknown; + + default: + goto unknown; + } + + + return ((const char *) exception); + +unknown: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unknown exception code: 0x%8.8X\n", status)); + return ((const char *) exception); +} + + +/****************************************************************************** + * + * Static global variable initialization. + * + ******************************************************************************/ + +/* + * We want the debug switches statically initialized so they + * are already set when the debugger is entered. + */ + +/* Debug switch - level and trace mask */ +u32 acpi_dbg_level = ACPI_DEBUG_DEFAULT; +EXPORT_SYMBOL(acpi_dbg_level); + +/* Debug switch - layer (component) mask */ + +u32 acpi_dbg_layer = ACPI_COMPONENT_DEFAULT | ACPI_ALL_DRIVERS; +EXPORT_SYMBOL(acpi_dbg_layer); +u32 acpi_gbl_nesting_level = 0; + + +/* Debugger globals */ + +u8 acpi_gbl_db_terminate_threads = FALSE; +u8 acpi_gbl_abort_method = FALSE; +u8 acpi_gbl_method_executing = FALSE; + +/* System flags */ + +u32 acpi_gbl_startup_flags = 0; + +/* System starts uninitialized */ + +u8 acpi_gbl_shutdown = TRUE; + +const u8 acpi_gbl_decode_to8bit [8] = {1,2,4,8,16,32,64,128}; + +const char *acpi_gbl_sleep_state_names[ACPI_S_STATE_COUNT] = +{ + "\\_S0_", + "\\_S1_", + "\\_S2_", + "\\_S3_", + "\\_S4_", + "\\_S5_" +}; + +const char *acpi_gbl_highest_dstate_names[4] = +{ + "_S1D", + "_S2D", + "_S3D", + "_S4D" +}; + +/* + * Strings supported by the _OSI predefined (internal) method. + * When adding strings, be sure to update ACPI_NUM_OSI_STRINGS. + */ +const char *acpi_gbl_valid_osi_strings[ACPI_NUM_OSI_STRINGS] = +{ + /* Operating System Vendor Strings */ + + "Linux", + "Windows 2000", + "Windows 2001", + "Windows 2001.1", + "Windows 2001 SP0", + "Windows 2001 SP1", + "Windows 2001 SP2", + "Windows 2001 SP3", + "Windows 2001 SP4", + + /* Feature Group Strings */ + + "Extended Address Space Descriptor" +}; + + +/****************************************************************************** + * + * Namespace globals + * + ******************************************************************************/ + + +/* + * Predefined ACPI Names (Built-in to the Interpreter) + * + * NOTES: + * 1) _SB_ is defined to be a device to allow \_SB_._INI to be run + * during the initialization sequence. + * 2) _TZ_ is defined to be a thermal zone in order to allow ASL code to + * perform a Notify() operation on it. + */ +const struct acpi_predefined_names acpi_gbl_pre_defined_names[] = +{ {"_GPE", ACPI_TYPE_LOCAL_SCOPE, NULL}, + {"_PR_", ACPI_TYPE_LOCAL_SCOPE, NULL}, + {"_SB_", ACPI_TYPE_DEVICE, NULL}, + {"_SI_", ACPI_TYPE_LOCAL_SCOPE, NULL}, + {"_TZ_", ACPI_TYPE_THERMAL, NULL}, + {"_REV", ACPI_TYPE_INTEGER, (char *) ACPI_CA_SUPPORT_LEVEL}, + {"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME}, + {"_GL_", ACPI_TYPE_MUTEX, (char *) 1}, + +#if !defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY) + {"_OSI", ACPI_TYPE_METHOD, (char *) 1}, +#endif + {NULL, ACPI_TYPE_ANY, NULL} /* Table terminator */ +}; + + +/* + * Properties of the ACPI Object Types, both internal and external. + * The table is indexed by values of acpi_object_type + */ +const u8 acpi_gbl_ns_properties[] = +{ + ACPI_NS_NORMAL, /* 00 Any */ + ACPI_NS_NORMAL, /* 01 Number */ + ACPI_NS_NORMAL, /* 02 String */ + ACPI_NS_NORMAL, /* 03 Buffer */ + ACPI_NS_NORMAL, /* 04 Package */ + ACPI_NS_NORMAL, /* 05 field_unit */ + ACPI_NS_NEWSCOPE, /* 06 Device */ + ACPI_NS_NORMAL, /* 07 Event */ + ACPI_NS_NEWSCOPE, /* 08 Method */ + ACPI_NS_NORMAL, /* 09 Mutex */ + ACPI_NS_NORMAL, /* 10 Region */ + ACPI_NS_NEWSCOPE, /* 11 Power */ + ACPI_NS_NEWSCOPE, /* 12 Processor */ + ACPI_NS_NEWSCOPE, /* 13 Thermal */ + ACPI_NS_NORMAL, /* 14 buffer_field */ + ACPI_NS_NORMAL, /* 15 ddb_handle */ + ACPI_NS_NORMAL, /* 16 Debug Object */ + ACPI_NS_NORMAL, /* 17 def_field */ + ACPI_NS_NORMAL, /* 18 bank_field */ + ACPI_NS_NORMAL, /* 19 index_field */ + ACPI_NS_NORMAL, /* 20 Reference */ + ACPI_NS_NORMAL, /* 21 Alias */ + ACPI_NS_NORMAL, /* 22 method_alias */ + ACPI_NS_NORMAL, /* 23 Notify */ + ACPI_NS_NORMAL, /* 24 Address Handler */ + ACPI_NS_NEWSCOPE | ACPI_NS_LOCAL, /* 25 Resource Desc */ + ACPI_NS_NEWSCOPE | ACPI_NS_LOCAL, /* 26 Resource Field */ + ACPI_NS_NEWSCOPE, /* 27 Scope */ + ACPI_NS_NORMAL, /* 28 Extra */ + ACPI_NS_NORMAL, /* 29 Data */ + ACPI_NS_NORMAL /* 30 Invalid */ +}; + + +/* Hex to ASCII conversion table */ + +static const char acpi_gbl_hex_to_ascii[] = + {'0','1','2','3','4','5','6','7', + '8','9','A','B','C','D','E','F'}; + +/***************************************************************************** + * + * FUNCTION: acpi_ut_hex_to_ascii_char + * + * PARAMETERS: Integer - Contains the hex digit + * Position - bit position of the digit within the + * integer + * + * RETURN: Ascii character + * + * DESCRIPTION: Convert a hex digit to an ascii character + * + ****************************************************************************/ + +char +acpi_ut_hex_to_ascii_char ( + acpi_integer integer, + u32 position) +{ + + return (acpi_gbl_hex_to_ascii[(integer >> position) & 0xF]); +} + + +/****************************************************************************** + * + * Table name globals + * + * NOTE: This table includes ONLY the ACPI tables that the subsystem consumes. + * it is NOT an exhaustive list of all possible ACPI tables. All ACPI tables + * that are not used by the subsystem are simply ignored. + * + * Do NOT add any table to this list that is not consumed directly by this + * subsystem. + * + ******************************************************************************/ + +struct acpi_table_list acpi_gbl_table_lists[NUM_ACPI_TABLE_TYPES]; + +struct acpi_table_support acpi_gbl_table_data[NUM_ACPI_TABLE_TYPES] = +{ + /*********** Name, Signature, Global typed pointer Signature size, Type How many allowed?, Contains valid AML? */ + + /* RSDP 0 */ {RSDP_NAME, RSDP_SIG, NULL, sizeof (RSDP_SIG)-1, ACPI_TABLE_ROOT | ACPI_TABLE_SINGLE}, + /* DSDT 1 */ {DSDT_SIG, DSDT_SIG, (void *) &acpi_gbl_DSDT, sizeof (DSDT_SIG)-1, ACPI_TABLE_SECONDARY| ACPI_TABLE_SINGLE | ACPI_TABLE_EXECUTABLE}, + /* FADT 2 */ {FADT_SIG, FADT_SIG, (void *) &acpi_gbl_FADT, sizeof (FADT_SIG)-1, ACPI_TABLE_PRIMARY | ACPI_TABLE_SINGLE}, + /* FACS 3 */ {FACS_SIG, FACS_SIG, (void *) &acpi_gbl_FACS, sizeof (FACS_SIG)-1, ACPI_TABLE_SECONDARY| ACPI_TABLE_SINGLE}, + /* PSDT 4 */ {PSDT_SIG, PSDT_SIG, NULL, sizeof (PSDT_SIG)-1, ACPI_TABLE_PRIMARY | ACPI_TABLE_MULTIPLE | ACPI_TABLE_EXECUTABLE}, + /* SSDT 5 */ {SSDT_SIG, SSDT_SIG, NULL, sizeof (SSDT_SIG)-1, ACPI_TABLE_PRIMARY | ACPI_TABLE_MULTIPLE | ACPI_TABLE_EXECUTABLE}, + /* XSDT 6 */ {XSDT_SIG, XSDT_SIG, NULL, sizeof (RSDT_SIG)-1, ACPI_TABLE_ROOT | ACPI_TABLE_SINGLE}, +}; + + +/****************************************************************************** + * + * Event and Hardware globals + * + ******************************************************************************/ + +struct acpi_bit_register_info acpi_gbl_bit_register_info[ACPI_NUM_BITREG] = +{ + /* Name Parent Register Register Bit Position Register Bit Mask */ + + /* ACPI_BITREG_TIMER_STATUS */ {ACPI_REGISTER_PM1_STATUS, ACPI_BITPOSITION_TIMER_STATUS, ACPI_BITMASK_TIMER_STATUS}, + /* ACPI_BITREG_BUS_MASTER_STATUS */ {ACPI_REGISTER_PM1_STATUS, ACPI_BITPOSITION_BUS_MASTER_STATUS, ACPI_BITMASK_BUS_MASTER_STATUS}, + /* ACPI_BITREG_GLOBAL_LOCK_STATUS */ {ACPI_REGISTER_PM1_STATUS, ACPI_BITPOSITION_GLOBAL_LOCK_STATUS, ACPI_BITMASK_GLOBAL_LOCK_STATUS}, + /* ACPI_BITREG_POWER_BUTTON_STATUS */ {ACPI_REGISTER_PM1_STATUS, ACPI_BITPOSITION_POWER_BUTTON_STATUS, ACPI_BITMASK_POWER_BUTTON_STATUS}, + /* ACPI_BITREG_SLEEP_BUTTON_STATUS */ {ACPI_REGISTER_PM1_STATUS, ACPI_BITPOSITION_SLEEP_BUTTON_STATUS, ACPI_BITMASK_SLEEP_BUTTON_STATUS}, + /* ACPI_BITREG_RT_CLOCK_STATUS */ {ACPI_REGISTER_PM1_STATUS, ACPI_BITPOSITION_RT_CLOCK_STATUS, ACPI_BITMASK_RT_CLOCK_STATUS}, + /* ACPI_BITREG_WAKE_STATUS */ {ACPI_REGISTER_PM1_STATUS, ACPI_BITPOSITION_WAKE_STATUS, ACPI_BITMASK_WAKE_STATUS}, + /* ACPI_BITREG_PCIEXP_WAKE_STATUS */ {ACPI_REGISTER_PM1_STATUS, ACPI_BITPOSITION_PCIEXP_WAKE_STATUS, ACPI_BITMASK_PCIEXP_WAKE_STATUS}, + + /* ACPI_BITREG_TIMER_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, ACPI_BITPOSITION_TIMER_ENABLE, ACPI_BITMASK_TIMER_ENABLE}, + /* ACPI_BITREG_GLOBAL_LOCK_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, ACPI_BITPOSITION_GLOBAL_LOCK_ENABLE, ACPI_BITMASK_GLOBAL_LOCK_ENABLE}, + /* ACPI_BITREG_POWER_BUTTON_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, ACPI_BITPOSITION_POWER_BUTTON_ENABLE, ACPI_BITMASK_POWER_BUTTON_ENABLE}, + /* ACPI_BITREG_SLEEP_BUTTON_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, ACPI_BITPOSITION_SLEEP_BUTTON_ENABLE, ACPI_BITMASK_SLEEP_BUTTON_ENABLE}, + /* ACPI_BITREG_RT_CLOCK_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, ACPI_BITPOSITION_RT_CLOCK_ENABLE, ACPI_BITMASK_RT_CLOCK_ENABLE}, + /* ACPI_BITREG_WAKE_ENABLE */ {ACPI_REGISTER_PM1_ENABLE, 0, 0}, + /* ACPI_BITREG_PCIEXP_WAKE_DISABLE */ {ACPI_REGISTER_PM1_ENABLE, ACPI_BITPOSITION_PCIEXP_WAKE_DISABLE, ACPI_BITMASK_PCIEXP_WAKE_DISABLE}, + + /* ACPI_BITREG_SCI_ENABLE */ {ACPI_REGISTER_PM1_CONTROL, ACPI_BITPOSITION_SCI_ENABLE, ACPI_BITMASK_SCI_ENABLE}, + /* ACPI_BITREG_BUS_MASTER_RLD */ {ACPI_REGISTER_PM1_CONTROL, ACPI_BITPOSITION_BUS_MASTER_RLD, ACPI_BITMASK_BUS_MASTER_RLD}, + /* ACPI_BITREG_GLOBAL_LOCK_RELEASE */ {ACPI_REGISTER_PM1_CONTROL, ACPI_BITPOSITION_GLOBAL_LOCK_RELEASE, ACPI_BITMASK_GLOBAL_LOCK_RELEASE}, + /* ACPI_BITREG_SLEEP_TYPE_A */ {ACPI_REGISTER_PM1_CONTROL, ACPI_BITPOSITION_SLEEP_TYPE_X, ACPI_BITMASK_SLEEP_TYPE_X}, + /* ACPI_BITREG_SLEEP_TYPE_B */ {ACPI_REGISTER_PM1_CONTROL, ACPI_BITPOSITION_SLEEP_TYPE_X, ACPI_BITMASK_SLEEP_TYPE_X}, + /* ACPI_BITREG_SLEEP_ENABLE */ {ACPI_REGISTER_PM1_CONTROL, ACPI_BITPOSITION_SLEEP_ENABLE, ACPI_BITMASK_SLEEP_ENABLE}, + + /* ACPI_BITREG_ARB_DIS */ {ACPI_REGISTER_PM2_CONTROL, ACPI_BITPOSITION_ARB_DISABLE, ACPI_BITMASK_ARB_DISABLE} +}; + + +struct acpi_fixed_event_info acpi_gbl_fixed_event_info[ACPI_NUM_FIXED_EVENTS] = +{ + /* ACPI_EVENT_PMTIMER */ {ACPI_BITREG_TIMER_STATUS, ACPI_BITREG_TIMER_ENABLE, ACPI_BITMASK_TIMER_STATUS, ACPI_BITMASK_TIMER_ENABLE}, + /* ACPI_EVENT_GLOBAL */ {ACPI_BITREG_GLOBAL_LOCK_STATUS, ACPI_BITREG_GLOBAL_LOCK_ENABLE, ACPI_BITMASK_GLOBAL_LOCK_STATUS, ACPI_BITMASK_GLOBAL_LOCK_ENABLE}, + /* ACPI_EVENT_POWER_BUTTON */ {ACPI_BITREG_POWER_BUTTON_STATUS, ACPI_BITREG_POWER_BUTTON_ENABLE, ACPI_BITMASK_POWER_BUTTON_STATUS, ACPI_BITMASK_POWER_BUTTON_ENABLE}, + /* ACPI_EVENT_SLEEP_BUTTON */ {ACPI_BITREG_SLEEP_BUTTON_STATUS, ACPI_BITREG_SLEEP_BUTTON_ENABLE, ACPI_BITMASK_SLEEP_BUTTON_STATUS, ACPI_BITMASK_SLEEP_BUTTON_ENABLE}, + /* ACPI_EVENT_RTC */ {ACPI_BITREG_RT_CLOCK_STATUS, ACPI_BITREG_RT_CLOCK_ENABLE, ACPI_BITMASK_RT_CLOCK_STATUS, ACPI_BITMASK_RT_CLOCK_ENABLE}, +}; + +/***************************************************************************** + * + * FUNCTION: acpi_ut_get_region_name + * + * PARAMETERS: None. + * + * RETURN: Status + * + * DESCRIPTION: Translate a Space ID into a name string (Debug only) + * + ****************************************************************************/ + +/* Region type decoding */ + +const char *acpi_gbl_region_types[ACPI_NUM_PREDEFINED_REGIONS] = +{ +/*! [Begin] no source code translation (keep these ASL Keywords as-is) */ + "SystemMemory", + "SystemIO", + "PCI_Config", + "EmbeddedControl", + "SMBus", + "CMOS", + "PCIBARTarget", + "DataTable" +/*! [End] no source code translation !*/ +}; + + +char * +acpi_ut_get_region_name ( + u8 space_id) +{ + + if (space_id >= ACPI_USER_REGION_BEGIN) + { + return ("user_defined_region"); + } + + else if (space_id >= ACPI_NUM_PREDEFINED_REGIONS) + { + return ("invalid_space_id"); + } + + return ((char *) acpi_gbl_region_types[space_id]); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_get_event_name + * + * PARAMETERS: None. + * + * RETURN: Status + * + * DESCRIPTION: Translate a Event ID into a name string (Debug only) + * + ****************************************************************************/ + +/* Event type decoding */ + +static const char *acpi_gbl_event_types[ACPI_NUM_FIXED_EVENTS] = +{ + "PM_Timer", + "global_lock", + "power_button", + "sleep_button", + "real_time_clock", +}; + + +char * +acpi_ut_get_event_name ( + u32 event_id) +{ + + if (event_id > ACPI_EVENT_MAX) + { + return ("invalid_event_iD"); + } + + return ((char *) acpi_gbl_event_types[event_id]); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_get_type_name + * + * PARAMETERS: None. + * + * RETURN: Status + * + * DESCRIPTION: Translate a Type ID into a name string (Debug only) + * + ****************************************************************************/ + +/* + * Elements of acpi_gbl_ns_type_names below must match + * one-to-one with values of acpi_object_type + * + * The type ACPI_TYPE_ANY (Untyped) is used as a "don't care" when searching; when + * stored in a table it really means that we have thus far seen no evidence to + * indicate what type is actually going to be stored for this entry. + */ +static const char acpi_gbl_bad_type[] = "UNDEFINED"; +#define TYPE_NAME_LENGTH 12 /* Maximum length of each string */ + +static const char *acpi_gbl_ns_type_names[] = /* printable names of ACPI types */ +{ + /* 00 */ "Untyped", + /* 01 */ "Integer", + /* 02 */ "String", + /* 03 */ "Buffer", + /* 04 */ "Package", + /* 05 */ "field_unit", + /* 06 */ "Device", + /* 07 */ "Event", + /* 08 */ "Method", + /* 09 */ "Mutex", + /* 10 */ "Region", + /* 11 */ "Power", + /* 12 */ "Processor", + /* 13 */ "Thermal", + /* 14 */ "buffer_field", + /* 15 */ "ddb_handle", + /* 16 */ "debug_object", + /* 17 */ "region_field", + /* 18 */ "bank_field", + /* 19 */ "index_field", + /* 20 */ "Reference", + /* 21 */ "Alias", + /* 22 */ "method_alias", + /* 23 */ "Notify", + /* 24 */ "addr_handler", + /* 25 */ "resource_desc", + /* 26 */ "resource_fld", + /* 27 */ "Scope", + /* 28 */ "Extra", + /* 29 */ "Data", + /* 30 */ "Invalid" +}; + + +char * +acpi_ut_get_type_name ( + acpi_object_type type) +{ + + if (type > ACPI_TYPE_INVALID) + { + return ((char *) acpi_gbl_bad_type); + } + + return ((char *) acpi_gbl_ns_type_names[type]); +} + + +char * +acpi_ut_get_object_type_name ( + union acpi_operand_object *obj_desc) +{ + + if (!obj_desc) + { + return ("[NULL Object Descriptor]"); + } + + return (acpi_ut_get_type_name (ACPI_GET_OBJECT_TYPE (obj_desc))); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_get_node_name + * + * PARAMETERS: Object - A namespace node + * + * RETURN: Pointer to a string + * + * DESCRIPTION: Validate the node and return the node's ACPI name. + * + ****************************************************************************/ + +char * +acpi_ut_get_node_name ( + void *object) +{ + struct acpi_namespace_node *node = (struct acpi_namespace_node *) object; + + + /* Must return a string of exactly 4 characters == ACPI_NAME_SIZE */ + + if (!object) + { + return ("NULL"); + } + + /* Check for Root node */ + + if ((object == ACPI_ROOT_OBJECT) || + (object == acpi_gbl_root_node)) + { + return ("\"\\\" "); + } + + /* Descriptor must be a namespace node */ + + if (node->descriptor != ACPI_DESC_TYPE_NAMED) + { + return ("####"); + } + + /* Name must be a valid ACPI name */ + + if (!acpi_ut_valid_acpi_name (* (u32 *) node->name.ascii)) + { + return ("????"); + } + + /* Return the name */ + + return (node->name.ascii); +} + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_get_descriptor_name + * + * PARAMETERS: Object - An ACPI object + * + * RETURN: Pointer to a string + * + * DESCRIPTION: Validate object and return the descriptor type + * + ****************************************************************************/ + +static const char *acpi_gbl_desc_type_names[] = /* printable names of descriptor types */ +{ + /* 00 */ "Invalid", + /* 01 */ "Cached", + /* 02 */ "State-Generic", + /* 03 */ "State-Update", + /* 04 */ "State-Package", + /* 05 */ "State-Control", + /* 06 */ "State-root_parse_scope", + /* 07 */ "State-parse_scope", + /* 08 */ "State-walk_scope", + /* 09 */ "State-Result", + /* 10 */ "State-Notify", + /* 11 */ "State-Thread", + /* 12 */ "Walk", + /* 13 */ "Parser", + /* 14 */ "Operand", + /* 15 */ "Node" +}; + + +char * +acpi_ut_get_descriptor_name ( + void *object) +{ + + if (!object) + { + return ("NULL OBJECT"); + } + + if (ACPI_GET_DESCRIPTOR_TYPE (object) > ACPI_DESC_TYPE_MAX) + { + return ((char *) acpi_gbl_bad_type); + } + + return ((char *) acpi_gbl_desc_type_names[ACPI_GET_DESCRIPTOR_TYPE (object)]); + +} + + +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) +/* + * Strings and procedures used for debug only + */ + +/***************************************************************************** + * + * FUNCTION: acpi_ut_get_mutex_name + * + * PARAMETERS: None. + * + * RETURN: Status + * + * DESCRIPTION: Translate a mutex ID into a name string (Debug only) + * + ****************************************************************************/ + +char * +acpi_ut_get_mutex_name ( + u32 mutex_id) +{ + + if (mutex_id > MAX_MUTEX) + { + return ("Invalid Mutex ID"); + } + + return (acpi_gbl_mutex_names[mutex_id]); +} + +#endif + + +/***************************************************************************** + * + * FUNCTION: acpi_ut_valid_object_type + * + * PARAMETERS: Type - Object type to be validated + * + * RETURN: TRUE if valid object type + * + * DESCRIPTION: Validate an object type + * + ****************************************************************************/ + +u8 +acpi_ut_valid_object_type ( + acpi_object_type type) +{ + + if (type > ACPI_TYPE_LOCAL_MAX) + { + /* Note: Assumes all TYPEs are contiguous (external/local) */ + + return (FALSE); + } + + return (TRUE); +} + + +/**************************************************************************** + * + * FUNCTION: acpi_ut_allocate_owner_id + * + * PARAMETERS: id_type - Type of ID (method or table) + * + * DESCRIPTION: Allocate a table or method owner id + * + ***************************************************************************/ + +acpi_owner_id +acpi_ut_allocate_owner_id ( + u32 id_type) +{ + acpi_owner_id owner_id = 0xFFFF; + + + ACPI_FUNCTION_TRACE ("ut_allocate_owner_id"); + + + if (ACPI_FAILURE (acpi_ut_acquire_mutex (ACPI_MTX_CACHES))) + { + return (0); + } + + switch (id_type) + { + case ACPI_OWNER_TYPE_TABLE: + + owner_id = acpi_gbl_next_table_owner_id; + acpi_gbl_next_table_owner_id++; + + /* Check for wraparound */ + + if (acpi_gbl_next_table_owner_id == ACPI_FIRST_METHOD_ID) + { + acpi_gbl_next_table_owner_id = ACPI_FIRST_TABLE_ID; + ACPI_REPORT_WARNING (("Table owner ID wraparound\n")); + } + break; + + + case ACPI_OWNER_TYPE_METHOD: + + owner_id = acpi_gbl_next_method_owner_id; + acpi_gbl_next_method_owner_id++; + + if (acpi_gbl_next_method_owner_id == ACPI_FIRST_TABLE_ID) + { + /* Check for wraparound */ + + acpi_gbl_next_method_owner_id = ACPI_FIRST_METHOD_ID; + } + break; + + default: + break; + } + + (void) acpi_ut_release_mutex (ACPI_MTX_CACHES); + return_VALUE (owner_id); +} + + +/**************************************************************************** + * + * FUNCTION: acpi_ut_init_globals + * + * PARAMETERS: none + * + * DESCRIPTION: Init library globals. All globals that require specific + * initialization should be initialized here! + * + ***************************************************************************/ + +void +acpi_ut_init_globals ( + void) +{ + u32 i; + + + ACPI_FUNCTION_TRACE ("ut_init_globals"); + + + /* Memory allocation and cache lists */ + + ACPI_MEMSET (acpi_gbl_memory_lists, 0, sizeof (struct acpi_memory_list) * ACPI_NUM_MEM_LISTS); + + acpi_gbl_memory_lists[ACPI_MEM_LIST_STATE].link_offset = (u16) ACPI_PTR_DIFF (&(((union acpi_generic_state *) NULL)->common.next), NULL); + acpi_gbl_memory_lists[ACPI_MEM_LIST_PSNODE].link_offset = (u16) ACPI_PTR_DIFF (&(((union acpi_parse_object *) NULL)->common.next), NULL); + acpi_gbl_memory_lists[ACPI_MEM_LIST_PSNODE_EXT].link_offset = (u16) ACPI_PTR_DIFF (&(((union acpi_parse_object *) NULL)->common.next), NULL); + acpi_gbl_memory_lists[ACPI_MEM_LIST_OPERAND].link_offset = (u16) ACPI_PTR_DIFF (&(((union acpi_operand_object *) NULL)->cache.next), NULL); + acpi_gbl_memory_lists[ACPI_MEM_LIST_WALK].link_offset = (u16) ACPI_PTR_DIFF (&(((struct acpi_walk_state *) NULL)->next), NULL); + + acpi_gbl_memory_lists[ACPI_MEM_LIST_NSNODE].object_size = sizeof (struct acpi_namespace_node); + acpi_gbl_memory_lists[ACPI_MEM_LIST_STATE].object_size = sizeof (union acpi_generic_state); + acpi_gbl_memory_lists[ACPI_MEM_LIST_PSNODE].object_size = sizeof (struct acpi_parse_obj_common); + acpi_gbl_memory_lists[ACPI_MEM_LIST_PSNODE_EXT].object_size = sizeof (struct acpi_parse_obj_named); + acpi_gbl_memory_lists[ACPI_MEM_LIST_OPERAND].object_size = sizeof (union acpi_operand_object); + acpi_gbl_memory_lists[ACPI_MEM_LIST_WALK].object_size = sizeof (struct acpi_walk_state); + + acpi_gbl_memory_lists[ACPI_MEM_LIST_STATE].max_cache_depth = ACPI_MAX_STATE_CACHE_DEPTH; + acpi_gbl_memory_lists[ACPI_MEM_LIST_PSNODE].max_cache_depth = ACPI_MAX_PARSE_CACHE_DEPTH; + acpi_gbl_memory_lists[ACPI_MEM_LIST_PSNODE_EXT].max_cache_depth = ACPI_MAX_EXTPARSE_CACHE_DEPTH; + acpi_gbl_memory_lists[ACPI_MEM_LIST_OPERAND].max_cache_depth = ACPI_MAX_OBJECT_CACHE_DEPTH; + acpi_gbl_memory_lists[ACPI_MEM_LIST_WALK].max_cache_depth = ACPI_MAX_WALK_CACHE_DEPTH; + + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_GLOBAL].list_name = "Global Memory Allocation"); + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_NSNODE].list_name = "Namespace Nodes"); + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_STATE].list_name = "State Object Cache"); + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_PSNODE].list_name = "Parse Node Cache"); + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_PSNODE_EXT].list_name = "Extended Parse Node Cache"); + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_OPERAND].list_name = "Operand Object Cache"); + ACPI_MEM_TRACKING (acpi_gbl_memory_lists[ACPI_MEM_LIST_WALK].list_name = "Tree Walk Node Cache"); + + /* ACPI table structure */ + + for (i = 0; i < NUM_ACPI_TABLE_TYPES; i++) + { + acpi_gbl_table_lists[i].next = NULL; + acpi_gbl_table_lists[i].count = 0; + } + + /* Mutex locked flags */ + + for (i = 0; i < NUM_MUTEX; i++) + { + acpi_gbl_mutex_info[i].mutex = NULL; + acpi_gbl_mutex_info[i].owner_id = ACPI_MUTEX_NOT_ACQUIRED; + acpi_gbl_mutex_info[i].use_count = 0; + } + + /* GPE support */ + + acpi_gbl_gpe_xrupt_list_head = NULL; + acpi_gbl_gpe_fadt_blocks[0] = NULL; + acpi_gbl_gpe_fadt_blocks[1] = NULL; + + /* Global notify handlers */ + + acpi_gbl_system_notify.handler = NULL; + acpi_gbl_device_notify.handler = NULL; + acpi_gbl_exception_handler = NULL; + acpi_gbl_init_handler = NULL; + + /* Global "typed" ACPI table pointers */ + + acpi_gbl_RSDP = NULL; + acpi_gbl_XSDT = NULL; + acpi_gbl_FACS = NULL; + acpi_gbl_FADT = NULL; + acpi_gbl_DSDT = NULL; + + /* Global Lock support */ + + acpi_gbl_global_lock_acquired = FALSE; + acpi_gbl_global_lock_thread_count = 0; + acpi_gbl_global_lock_handle = 0; + + /* Miscellaneous variables */ + + acpi_gbl_table_flags = ACPI_PHYSICAL_POINTER; + acpi_gbl_rsdp_original_location = 0; + acpi_gbl_cm_single_step = FALSE; + acpi_gbl_db_terminate_threads = FALSE; + acpi_gbl_shutdown = FALSE; + acpi_gbl_ns_lookup_count = 0; + acpi_gbl_ps_find_count = 0; + acpi_gbl_acpi_hardware_present = TRUE; + acpi_gbl_next_table_owner_id = ACPI_FIRST_TABLE_ID; + acpi_gbl_next_method_owner_id = ACPI_FIRST_METHOD_ID; + acpi_gbl_debugger_configuration = DEBUGGER_THREADING; + acpi_gbl_db_output_flags = ACPI_DB_CONSOLE_OUTPUT; + + /* Hardware oriented */ + + acpi_gbl_events_initialized = FALSE; + acpi_gbl_system_awake_and_running = TRUE; + + /* Namespace */ + + acpi_gbl_root_node = NULL; + + acpi_gbl_root_node_struct.name.integer = ACPI_ROOT_NAME; + acpi_gbl_root_node_struct.descriptor = ACPI_DESC_TYPE_NAMED; + acpi_gbl_root_node_struct.type = ACPI_TYPE_DEVICE; + acpi_gbl_root_node_struct.child = NULL; + acpi_gbl_root_node_struct.peer = NULL; + acpi_gbl_root_node_struct.object = NULL; + acpi_gbl_root_node_struct.flags = ANOBJ_END_OF_PEER_LIST; + + +#ifdef ACPI_DEBUG_OUTPUT + acpi_gbl_lowest_stack_pointer = ACPI_SIZE_MAX; +#endif + + return_VOID; +} + + diff --git a/drivers/acpi/utilities/utinit.c b/drivers/acpi/utilities/utinit.c new file mode 100644 index 00000000000..bdbadaf48d2 --- /dev/null +++ b/drivers/acpi/utilities/utinit.c @@ -0,0 +1,266 @@ +/****************************************************************************** + * + * Module Name: utinit - Common ACPI subsystem initialization + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/acevents.h> + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utinit") + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_fadt_register_error + * + * PARAMETERS: *register_name - Pointer to string identifying register + * Value - Actual register contents value + * acpi_test_spec_section - TDS section containing assertion + * acpi_assertion - Assertion number being tested + * + * RETURN: AE_BAD_VALUE + * + * DESCRIPTION: Display failure message and link failure to TDS assertion + * + ******************************************************************************/ + +static void +acpi_ut_fadt_register_error ( + char *register_name, + u32 value, + acpi_size offset) +{ + + ACPI_REPORT_WARNING ( + ("Invalid FADT value %s=%X at offset %X FADT=%p\n", + register_name, value, (u32) offset, acpi_gbl_FADT)); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_ut_validate_fadt + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Validate various ACPI registers in the FADT + * + ******************************************************************************/ + +acpi_status +acpi_ut_validate_fadt ( + void) +{ + + /* + * Verify Fixed ACPI Description Table fields, + * but don't abort on any problems, just display error + */ + if (acpi_gbl_FADT->pm1_evt_len < 4) { + acpi_ut_fadt_register_error ("PM1_EVT_LEN", + (u32) acpi_gbl_FADT->pm1_evt_len, + ACPI_FADT_OFFSET (pm1_evt_len)); + } + + if (!acpi_gbl_FADT->pm1_cnt_len) { + acpi_ut_fadt_register_error ("PM1_CNT_LEN", 0, + ACPI_FADT_OFFSET (pm1_cnt_len)); + } + + if (!acpi_gbl_FADT->xpm1a_evt_blk.address) { + acpi_ut_fadt_register_error ("X_PM1a_EVT_BLK", 0, + ACPI_FADT_OFFSET (xpm1a_evt_blk.address)); + } + + if (!acpi_gbl_FADT->xpm1a_cnt_blk.address) { + acpi_ut_fadt_register_error ("X_PM1a_CNT_BLK", 0, + ACPI_FADT_OFFSET (xpm1a_cnt_blk.address)); + } + + if (!acpi_gbl_FADT->xpm_tmr_blk.address) { + acpi_ut_fadt_register_error ("X_PM_TMR_BLK", 0, + ACPI_FADT_OFFSET (xpm_tmr_blk.address)); + } + + if ((acpi_gbl_FADT->xpm2_cnt_blk.address && + !acpi_gbl_FADT->pm2_cnt_len)) { + acpi_ut_fadt_register_error ("PM2_CNT_LEN", + (u32) acpi_gbl_FADT->pm2_cnt_len, + ACPI_FADT_OFFSET (pm2_cnt_len)); + } + + if (acpi_gbl_FADT->pm_tm_len < 4) { + acpi_ut_fadt_register_error ("PM_TM_LEN", + (u32) acpi_gbl_FADT->pm_tm_len, + ACPI_FADT_OFFSET (pm_tm_len)); + } + + /* Length of GPE blocks must be a multiple of 2 */ + + if (acpi_gbl_FADT->xgpe0_blk.address && + (acpi_gbl_FADT->gpe0_blk_len & 1)) { + acpi_ut_fadt_register_error ("(x)GPE0_BLK_LEN", + (u32) acpi_gbl_FADT->gpe0_blk_len, + ACPI_FADT_OFFSET (gpe0_blk_len)); + } + + if (acpi_gbl_FADT->xgpe1_blk.address && + (acpi_gbl_FADT->gpe1_blk_len & 1)) { + acpi_ut_fadt_register_error ("(x)GPE1_BLK_LEN", + (u32) acpi_gbl_FADT->gpe1_blk_len, + ACPI_FADT_OFFSET (gpe1_blk_len)); + } + + return (AE_OK); +} + + +/****************************************************************************** + * + * FUNCTION: acpi_ut_terminate + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: free global memory + * + ******************************************************************************/ + +void +acpi_ut_terminate (void) +{ + struct acpi_gpe_block_info *gpe_block; + struct acpi_gpe_block_info *next_gpe_block; + struct acpi_gpe_xrupt_info *gpe_xrupt_info; + struct acpi_gpe_xrupt_info *next_gpe_xrupt_info; + + + ACPI_FUNCTION_TRACE ("ut_terminate"); + + + /* Free global tables, etc. */ + + + /* Free global GPE blocks and related info structures */ + + gpe_xrupt_info = acpi_gbl_gpe_xrupt_list_head; + while (gpe_xrupt_info) { + gpe_block = gpe_xrupt_info->gpe_block_list_head; + while (gpe_block) { + next_gpe_block = gpe_block->next; + ACPI_MEM_FREE (gpe_block->event_info); + ACPI_MEM_FREE (gpe_block->register_info); + ACPI_MEM_FREE (gpe_block); + + gpe_block = next_gpe_block; + } + next_gpe_xrupt_info = gpe_xrupt_info->next; + ACPI_MEM_FREE (gpe_xrupt_info); + gpe_xrupt_info = next_gpe_xrupt_info; + } + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_subsystem_shutdown + * + * PARAMETERS: none + * + * RETURN: none + * + * DESCRIPTION: Shutdown the various subsystems. Don't delete the mutex + * objects here -- because the AML debugger may be still running. + * + ******************************************************************************/ + +void +acpi_ut_subsystem_shutdown (void) +{ + + ACPI_FUNCTION_TRACE ("ut_subsystem_shutdown"); + + /* Just exit if subsystem is already shutdown */ + + if (acpi_gbl_shutdown) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "ACPI Subsystem is already terminated\n")); + return_VOID; + } + + /* Subsystem appears active, go ahead and shut it down */ + + acpi_gbl_shutdown = TRUE; + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Shutting down ACPI Subsystem...\n")); + + /* Close the acpi_event Handling */ + + acpi_ev_terminate (); + + /* Close the Namespace */ + + acpi_ns_terminate (); + + /* Close the globals */ + + acpi_ut_terminate (); + + /* Purge the local caches */ + + (void) acpi_purge_cached_objects (); + + /* Debug only - display leftover memory allocation, if any */ + +#ifdef ACPI_DBG_TRACK_ALLOCATIONS + acpi_ut_dump_allocations (ACPI_UINT32_MAX, NULL); +#endif + + return_VOID; +} + + diff --git a/drivers/acpi/utilities/utmath.c b/drivers/acpi/utilities/utmath.c new file mode 100644 index 00000000000..2525c1a9354 --- /dev/null +++ b/drivers/acpi/utilities/utmath.c @@ -0,0 +1,333 @@ +/******************************************************************************* + * + * Module Name: utmath - Integer math support routines + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> + + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utmath") + +/* + * Support for double-precision integer divide. This code is included here + * in order to support kernel environments where the double-precision math + * library is not available. + */ + +#ifndef ACPI_USE_NATIVE_DIVIDE +/******************************************************************************* + * + * FUNCTION: acpi_ut_short_divide + * + * PARAMETERS: Dividend - 64-bit dividend + * Divisor - 32-bit divisor + * out_quotient - Pointer to where the quotient is returned + * out_remainder - Pointer to where the remainder is returned + * + * RETURN: Status (Checks for divide-by-zero) + * + * DESCRIPTION: Perform a short (maximum 64 bits divided by 32 bits) + * divide and modulo. The result is a 64-bit quotient and a + * 32-bit remainder. + * + ******************************************************************************/ + +acpi_status +acpi_ut_short_divide ( + acpi_integer dividend, + u32 divisor, + acpi_integer *out_quotient, + u32 *out_remainder) +{ + union uint64_overlay dividend_ovl; + union uint64_overlay quotient; + u32 remainder32; + + + ACPI_FUNCTION_TRACE ("ut_short_divide"); + + + /* Always check for a zero divisor */ + + if (divisor == 0) { + ACPI_REPORT_ERROR (("acpi_ut_short_divide: Divide by zero\n")); + return_ACPI_STATUS (AE_AML_DIVIDE_BY_ZERO); + } + + dividend_ovl.full = dividend; + + /* + * The quotient is 64 bits, the remainder is always 32 bits, + * and is generated by the second divide. + */ + ACPI_DIV_64_BY_32 (0, dividend_ovl.part.hi, divisor, + quotient.part.hi, remainder32); + ACPI_DIV_64_BY_32 (remainder32, dividend_ovl.part.lo, divisor, + quotient.part.lo, remainder32); + + /* Return only what was requested */ + + if (out_quotient) { + *out_quotient = quotient.full; + } + if (out_remainder) { + *out_remainder = remainder32; + } + + return_ACPI_STATUS (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_divide + * + * PARAMETERS: in_dividend - Dividend + * in_divisor - Divisor + * out_quotient - Pointer to where the quotient is returned + * out_remainder - Pointer to where the remainder is returned + * + * RETURN: Status (Checks for divide-by-zero) + * + * DESCRIPTION: Perform a divide and modulo. + * + ******************************************************************************/ + +acpi_status +acpi_ut_divide ( + acpi_integer in_dividend, + acpi_integer in_divisor, + acpi_integer *out_quotient, + acpi_integer *out_remainder) +{ + union uint64_overlay dividend; + union uint64_overlay divisor; + union uint64_overlay quotient; + union uint64_overlay remainder; + union uint64_overlay normalized_dividend; + union uint64_overlay normalized_divisor; + u32 partial1; + union uint64_overlay partial2; + union uint64_overlay partial3; + + + ACPI_FUNCTION_TRACE ("ut_divide"); + + + /* Always check for a zero divisor */ + + if (in_divisor == 0) { + ACPI_REPORT_ERROR (("acpi_ut_divide: Divide by zero\n")); + return_ACPI_STATUS (AE_AML_DIVIDE_BY_ZERO); + } + + divisor.full = in_divisor; + dividend.full = in_dividend; + if (divisor.part.hi == 0) { + /* + * 1) Simplest case is where the divisor is 32 bits, we can + * just do two divides + */ + remainder.part.hi = 0; + + /* + * The quotient is 64 bits, the remainder is always 32 bits, + * and is generated by the second divide. + */ + ACPI_DIV_64_BY_32 (0, dividend.part.hi, divisor.part.lo, + quotient.part.hi, partial1); + ACPI_DIV_64_BY_32 (partial1, dividend.part.lo, divisor.part.lo, + quotient.part.lo, remainder.part.lo); + } + + else { + /* + * 2) The general case where the divisor is a full 64 bits + * is more difficult + */ + quotient.part.hi = 0; + normalized_dividend = dividend; + normalized_divisor = divisor; + + /* Normalize the operands (shift until the divisor is < 32 bits) */ + + do { + ACPI_SHIFT_RIGHT_64 (normalized_divisor.part.hi, + normalized_divisor.part.lo); + ACPI_SHIFT_RIGHT_64 (normalized_dividend.part.hi, + normalized_dividend.part.lo); + + } while (normalized_divisor.part.hi != 0); + + /* Partial divide */ + + ACPI_DIV_64_BY_32 (normalized_dividend.part.hi, + normalized_dividend.part.lo, + normalized_divisor.part.lo, + quotient.part.lo, partial1); + + /* + * The quotient is always 32 bits, and simply requires adjustment. + * The 64-bit remainder must be generated. + */ + partial1 = quotient.part.lo * divisor.part.hi; + partial2.full = (acpi_integer) quotient.part.lo * divisor.part.lo; + partial3.full = (acpi_integer) partial2.part.hi + partial1; + + remainder.part.hi = partial3.part.lo; + remainder.part.lo = partial2.part.lo; + + if (partial3.part.hi == 0) { + if (partial3.part.lo >= dividend.part.hi) { + if (partial3.part.lo == dividend.part.hi) { + if (partial2.part.lo > dividend.part.lo) { + quotient.part.lo--; + remainder.full -= divisor.full; + } + } + else { + quotient.part.lo--; + remainder.full -= divisor.full; + } + } + + remainder.full = remainder.full - dividend.full; + remainder.part.hi = (u32) -((s32) remainder.part.hi); + remainder.part.lo = (u32) -((s32) remainder.part.lo); + + if (remainder.part.lo) { + remainder.part.hi--; + } + } + } + + /* Return only what was requested */ + + if (out_quotient) { + *out_quotient = quotient.full; + } + if (out_remainder) { + *out_remainder = remainder.full; + } + + return_ACPI_STATUS (AE_OK); +} + +#else + +/******************************************************************************* + * + * FUNCTION: acpi_ut_short_divide, acpi_ut_divide + * + * DESCRIPTION: Native versions of the ut_divide functions. Use these if either + * 1) The target is a 64-bit platform and therefore 64-bit + * integer math is supported directly by the machine. + * 2) The target is a 32-bit or 16-bit platform, and the + * double-precision integer math library is available to + * perform the divide. + * + ******************************************************************************/ + +acpi_status +acpi_ut_short_divide ( + acpi_integer in_dividend, + u32 divisor, + acpi_integer *out_quotient, + u32 *out_remainder) +{ + + ACPI_FUNCTION_TRACE ("ut_short_divide"); + + + /* Always check for a zero divisor */ + + if (divisor == 0) { + ACPI_REPORT_ERROR (("acpi_ut_short_divide: Divide by zero\n")); + return_ACPI_STATUS (AE_AML_DIVIDE_BY_ZERO); + } + + /* Return only what was requested */ + + if (out_quotient) { + *out_quotient = in_dividend / divisor; + } + if (out_remainder) { + *out_remainder = (u32) in_dividend % divisor; + } + + return_ACPI_STATUS (AE_OK); +} + +acpi_status +acpi_ut_divide ( + acpi_integer in_dividend, + acpi_integer in_divisor, + acpi_integer *out_quotient, + acpi_integer *out_remainder) +{ + ACPI_FUNCTION_TRACE ("ut_divide"); + + + /* Always check for a zero divisor */ + + if (in_divisor == 0) { + ACPI_REPORT_ERROR (("acpi_ut_divide: Divide by zero\n")); + return_ACPI_STATUS (AE_AML_DIVIDE_BY_ZERO); + } + + + /* Return only what was requested */ + + if (out_quotient) { + *out_quotient = in_dividend / in_divisor; + } + if (out_remainder) { + *out_remainder = in_dividend % in_divisor; + } + + return_ACPI_STATUS (AE_OK); +} + +#endif + + diff --git a/drivers/acpi/utilities/utmisc.c b/drivers/acpi/utilities/utmisc.c new file mode 100644 index 00000000000..f6598547389 --- /dev/null +++ b/drivers/acpi/utilities/utmisc.c @@ -0,0 +1,1516 @@ +/******************************************************************************* + * + * Module Name: utmisc - common utility procedures + * + ******************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> + + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utmisc") + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_print_string + * + * PARAMETERS: String - Null terminated ASCII string + * + * RETURN: None + * + * DESCRIPTION: Dump an ASCII string with support for ACPI-defined escape + * sequences. + * + ******************************************************************************/ + +void +acpi_ut_print_string ( + char *string, + u8 max_length) +{ + u32 i; + + + if (!string) { + acpi_os_printf ("<\"NULL STRING PTR\">"); + return; + } + + acpi_os_printf ("\""); + for (i = 0; string[i] && (i < max_length); i++) { + /* Escape sequences */ + + switch (string[i]) { + case 0x07: + acpi_os_printf ("\\a"); /* BELL */ + break; + + case 0x08: + acpi_os_printf ("\\b"); /* BACKSPACE */ + break; + + case 0x0C: + acpi_os_printf ("\\f"); /* FORMFEED */ + break; + + case 0x0A: + acpi_os_printf ("\\n"); /* LINEFEED */ + break; + + case 0x0D: + acpi_os_printf ("\\r"); /* CARRIAGE RETURN*/ + break; + + case 0x09: + acpi_os_printf ("\\t"); /* HORIZONTAL TAB */ + break; + + case 0x0B: + acpi_os_printf ("\\v"); /* VERTICAL TAB */ + break; + + case '\'': /* Single Quote */ + case '\"': /* Double Quote */ + case '\\': /* Backslash */ + acpi_os_printf ("\\%c", (int) string[i]); + break; + + default: + + /* Check for printable character or hex escape */ + + if (ACPI_IS_PRINT (string[i])) + { + /* This is a normal character */ + + acpi_os_printf ("%c", (int) string[i]); + } + else + { + /* All others will be Hex escapes */ + + acpi_os_printf ("\\x%2.2X", (s32) string[i]); + } + break; + } + } + acpi_os_printf ("\""); + + if (i == max_length && string[i]) { + acpi_os_printf ("..."); + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_dword_byte_swap + * + * PARAMETERS: Value - Value to be converted + * + * DESCRIPTION: Convert a 32-bit value to big-endian (swap the bytes) + * + ******************************************************************************/ + +u32 +acpi_ut_dword_byte_swap ( + u32 value) +{ + union { + u32 value; + u8 bytes[4]; + } out; + + union { + u32 value; + u8 bytes[4]; + } in; + + + ACPI_FUNCTION_ENTRY (); + + + in.value = value; + + out.bytes[0] = in.bytes[3]; + out.bytes[1] = in.bytes[2]; + out.bytes[2] = in.bytes[1]; + out.bytes[3] = in.bytes[0]; + + return (out.value); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_set_integer_width + * + * PARAMETERS: Revision From DSDT header + * + * RETURN: None + * + * DESCRIPTION: Set the global integer bit width based upon the revision + * of the DSDT. For Revision 1 and 0, Integers are 32 bits. + * For Revision 2 and above, Integers are 64 bits. Yes, this + * makes a difference. + * + ******************************************************************************/ + +void +acpi_ut_set_integer_width ( + u8 revision) +{ + + if (revision <= 1) { + acpi_gbl_integer_bit_width = 32; + acpi_gbl_integer_nybble_width = 8; + acpi_gbl_integer_byte_width = 4; + } + else { + acpi_gbl_integer_bit_width = 64; + acpi_gbl_integer_nybble_width = 16; + acpi_gbl_integer_byte_width = 8; + } +} + + +#ifdef ACPI_DEBUG_OUTPUT +/******************************************************************************* + * + * FUNCTION: acpi_ut_display_init_pathname + * + * PARAMETERS: obj_handle - Handle whose pathname will be displayed + * Path - Additional path string to be appended. + * (NULL if no extra path) + * + * RETURN: acpi_status + * + * DESCRIPTION: Display full pathname of an object, DEBUG ONLY + * + ******************************************************************************/ + +void +acpi_ut_display_init_pathname ( + u8 type, + struct acpi_namespace_node *obj_handle, + char *path) +{ + acpi_status status; + struct acpi_buffer buffer; + + + ACPI_FUNCTION_ENTRY (); + + + /* Only print the path if the appropriate debug level is enabled */ + + if (!(acpi_dbg_level & ACPI_LV_INIT_NAMES)) { + return; + } + + /* Get the full pathname to the node */ + + buffer.length = ACPI_ALLOCATE_LOCAL_BUFFER; + status = acpi_ns_handle_to_pathname (obj_handle, &buffer); + if (ACPI_FAILURE (status)) { + return; + } + + /* Print what we're doing */ + + switch (type) { + case ACPI_TYPE_METHOD: + acpi_os_printf ("Executing "); + break; + + default: + acpi_os_printf ("Initializing "); + break; + } + + /* Print the object type and pathname */ + + acpi_os_printf ("%-12s %s", acpi_ut_get_type_name (type), (char *) buffer.pointer); + + /* Extra path is used to append names like _STA, _INI, etc. */ + + if (path) { + acpi_os_printf (".%s", path); + } + acpi_os_printf ("\n"); + + ACPI_MEM_FREE (buffer.pointer); +} +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_valid_acpi_name + * + * PARAMETERS: Character - The character to be examined + * + * RETURN: 1 if Character may appear in a name, else 0 + * + * DESCRIPTION: Check for a valid ACPI name. Each character must be one of: + * 1) Upper case alpha + * 2) numeric + * 3) underscore + * + ******************************************************************************/ + +u8 +acpi_ut_valid_acpi_name ( + u32 name) +{ + char *name_ptr = (char *) &name; + char character; + acpi_native_uint i; + + + ACPI_FUNCTION_ENTRY (); + + + for (i = 0; i < ACPI_NAME_SIZE; i++) { + character = *name_ptr; + name_ptr++; + + if (!((character == '_') || + (character >= 'A' && character <= 'Z') || + (character >= '0' && character <= '9'))) { + return (FALSE); + } + } + + return (TRUE); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_valid_acpi_character + * + * PARAMETERS: Character - The character to be examined + * + * RETURN: 1 if Character may appear in a name, else 0 + * + * DESCRIPTION: Check for a printable character + * + ******************************************************************************/ + +u8 +acpi_ut_valid_acpi_character ( + char character) +{ + + ACPI_FUNCTION_ENTRY (); + + return ((u8) ((character == '_') || + (character >= 'A' && character <= 'Z') || + (character >= '0' && character <= '9'))); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_strtoul64 + * + * PARAMETERS: String - Null terminated string + * Base - Radix of the string: 10, 16, or ACPI_ANY_BASE + * ret_integer - Where the converted integer is returned + * + * RETURN: Status and Converted value + * + * DESCRIPTION: Convert a string into an unsigned value. + * NOTE: Does not support Octal strings, not needed. + * + ******************************************************************************/ + +acpi_status +acpi_ut_strtoul64 ( + char *string, + u32 base, + acpi_integer *ret_integer) +{ + u32 this_digit = 0; + acpi_integer return_value = 0; + acpi_integer quotient; + + + ACPI_FUNCTION_TRACE ("ut_stroul64"); + + + if ((!string) || !(*string)) { + goto error_exit; + } + + switch (base) { + case ACPI_ANY_BASE: + case 10: + case 16: + break; + + default: + /* Invalid Base */ + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + /* Skip over any white space in the buffer */ + + while (ACPI_IS_SPACE (*string) || *string == '\t') { + string++; + } + + /* + * If the input parameter Base is zero, then we need to + * determine if it is decimal or hexadecimal: + */ + if (base == 0) { + if ((*string == '0') && + (ACPI_TOLOWER (*(string + 1)) == 'x')) { + base = 16; + string += 2; + } + else { + base = 10; + } + } + + /* + * For hexadecimal base, skip over the leading + * 0 or 0x, if they are present. + */ + if ((base == 16) && + (*string == '0') && + (ACPI_TOLOWER (*(string + 1)) == 'x')) { + string += 2; + } + + /* Any string left? */ + + if (!(*string)) { + goto error_exit; + } + + /* Main loop: convert the string to a 64-bit integer */ + + while (*string) { + if (ACPI_IS_DIGIT (*string)) { + /* Convert ASCII 0-9 to Decimal value */ + + this_digit = ((u8) *string) - '0'; + } + else { + if (base == 10) { + /* Digit is out of range */ + + goto error_exit; + } + + this_digit = (u8) ACPI_TOUPPER (*string); + if (ACPI_IS_XDIGIT ((char) this_digit)) { + /* Convert ASCII Hex char to value */ + + this_digit = this_digit - 'A' + 10; + } + else { + /* + * We allow non-hex chars, just stop now, same as end-of-string. + * See ACPI spec, string-to-integer conversion. + */ + break; + } + } + + /* Divide the digit into the correct position */ + + (void) acpi_ut_short_divide ((ACPI_INTEGER_MAX - (acpi_integer) this_digit), + base, "ient, NULL); + if (return_value > quotient) { + goto error_exit; + } + + return_value *= base; + return_value += this_digit; + string++; + } + + /* All done, normal exit */ + + *ret_integer = return_value; + return_ACPI_STATUS (AE_OK); + + +error_exit: + /* Base was set/validated above */ + + if (base == 10) { + return_ACPI_STATUS (AE_BAD_DECIMAL_CONSTANT); + } + else { + return_ACPI_STATUS (AE_BAD_HEX_CONSTANT); + } +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_strupr + * + * PARAMETERS: src_string - The source string to convert to + * + * RETURN: src_string + * + * DESCRIPTION: Convert string to uppercase + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +char * +acpi_ut_strupr ( + char *src_string) +{ + char *string; + + + ACPI_FUNCTION_ENTRY (); + + + /* Walk entire string, uppercasing the letters */ + + for (string = src_string; *string; ) { + *string = (char) ACPI_TOUPPER (*string); + string++; + } + + return (src_string); +} +#endif /* ACPI_FUTURE_USAGE */ + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_mutex_initialize + * + * PARAMETERS: None. + * + * RETURN: Status + * + * DESCRIPTION: Create the system mutex objects. + * + ******************************************************************************/ + +acpi_status +acpi_ut_mutex_initialize ( + void) +{ + u32 i; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("ut_mutex_initialize"); + + + /* + * Create each of the predefined mutex objects + */ + for (i = 0; i < NUM_MUTEX; i++) { + status = acpi_ut_create_mutex (i); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + status = acpi_os_create_lock (&acpi_gbl_gpe_lock); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_mutex_terminate + * + * PARAMETERS: None. + * + * RETURN: None. + * + * DESCRIPTION: Delete all of the system mutex objects. + * + ******************************************************************************/ + +void +acpi_ut_mutex_terminate ( + void) +{ + u32 i; + + + ACPI_FUNCTION_TRACE ("ut_mutex_terminate"); + + + /* + * Delete each predefined mutex object + */ + for (i = 0; i < NUM_MUTEX; i++) { + (void) acpi_ut_delete_mutex (i); + } + + acpi_os_delete_lock (acpi_gbl_gpe_lock); + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_mutex + * + * PARAMETERS: mutex_iD - ID of the mutex to be created + * + * RETURN: Status + * + * DESCRIPTION: Create a mutex object. + * + ******************************************************************************/ + +acpi_status +acpi_ut_create_mutex ( + acpi_mutex_handle mutex_id) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_U32 ("ut_create_mutex", mutex_id); + + + if (mutex_id > MAX_MUTEX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + if (!acpi_gbl_mutex_info[mutex_id].mutex) { + status = acpi_os_create_semaphore (1, 1, + &acpi_gbl_mutex_info[mutex_id].mutex); + acpi_gbl_mutex_info[mutex_id].owner_id = ACPI_MUTEX_NOT_ACQUIRED; + acpi_gbl_mutex_info[mutex_id].use_count = 0; + } + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_mutex + * + * PARAMETERS: mutex_iD - ID of the mutex to be deleted + * + * RETURN: Status + * + * DESCRIPTION: Delete a mutex object. + * + ******************************************************************************/ + +acpi_status +acpi_ut_delete_mutex ( + acpi_mutex_handle mutex_id) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE_U32 ("ut_delete_mutex", mutex_id); + + + if (mutex_id > MAX_MUTEX) { + return_ACPI_STATUS (AE_BAD_PARAMETER); + } + + status = acpi_os_delete_semaphore (acpi_gbl_mutex_info[mutex_id].mutex); + + acpi_gbl_mutex_info[mutex_id].mutex = NULL; + acpi_gbl_mutex_info[mutex_id].owner_id = ACPI_MUTEX_NOT_ACQUIRED; + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_acquire_mutex + * + * PARAMETERS: mutex_iD - ID of the mutex to be acquired + * + * RETURN: Status + * + * DESCRIPTION: Acquire a mutex object. + * + ******************************************************************************/ + +acpi_status +acpi_ut_acquire_mutex ( + acpi_mutex_handle mutex_id) +{ + acpi_status status; + u32 this_thread_id; + + + ACPI_FUNCTION_NAME ("ut_acquire_mutex"); + + + if (mutex_id > MAX_MUTEX) { + return (AE_BAD_PARAMETER); + } + + this_thread_id = acpi_os_get_thread_id (); + +#ifdef ACPI_MUTEX_DEBUG + { + u32 i; + /* + * Mutex debug code, for internal debugging only. + * + * Deadlock prevention. Check if this thread owns any mutexes of value + * greater than or equal to this one. If so, the thread has violated + * the mutex ordering rule. This indicates a coding error somewhere in + * the ACPI subsystem code. + */ + for (i = mutex_id; i < MAX_MUTEX; i++) { + if (acpi_gbl_mutex_info[i].owner_id == this_thread_id) { + if (i == mutex_id) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Mutex [%s] already acquired by this thread [%X]\n", + acpi_ut_get_mutex_name (mutex_id), this_thread_id)); + + return (AE_ALREADY_ACQUIRED); + } + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Invalid acquire order: Thread %X owns [%s], wants [%s]\n", + this_thread_id, acpi_ut_get_mutex_name (i), + acpi_ut_get_mutex_name (mutex_id))); + + return (AE_ACQUIRE_DEADLOCK); + } + } + } +#endif + + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, + "Thread %X attempting to acquire Mutex [%s]\n", + this_thread_id, acpi_ut_get_mutex_name (mutex_id))); + + status = acpi_os_wait_semaphore (acpi_gbl_mutex_info[mutex_id].mutex, + 1, ACPI_WAIT_FOREVER); + if (ACPI_SUCCESS (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Thread %X acquired Mutex [%s]\n", + this_thread_id, acpi_ut_get_mutex_name (mutex_id))); + + acpi_gbl_mutex_info[mutex_id].use_count++; + acpi_gbl_mutex_info[mutex_id].owner_id = this_thread_id; + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Thread %X could not acquire Mutex [%s] %s\n", + this_thread_id, acpi_ut_get_mutex_name (mutex_id), + acpi_format_exception (status))); + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_release_mutex + * + * PARAMETERS: mutex_iD - ID of the mutex to be released + * + * RETURN: Status + * + * DESCRIPTION: Release a mutex object. + * + ******************************************************************************/ + +acpi_status +acpi_ut_release_mutex ( + acpi_mutex_handle mutex_id) +{ + acpi_status status; + u32 i; + u32 this_thread_id; + + + ACPI_FUNCTION_NAME ("ut_release_mutex"); + + + this_thread_id = acpi_os_get_thread_id (); + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, + "Thread %X releasing Mutex [%s]\n", this_thread_id, + acpi_ut_get_mutex_name (mutex_id))); + + if (mutex_id > MAX_MUTEX) { + return (AE_BAD_PARAMETER); + } + + /* + * Mutex must be acquired in order to release it! + */ + if (acpi_gbl_mutex_info[mutex_id].owner_id == ACPI_MUTEX_NOT_ACQUIRED) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Mutex [%s] is not acquired, cannot release\n", + acpi_ut_get_mutex_name (mutex_id))); + + return (AE_NOT_ACQUIRED); + } + + /* + * Deadlock prevention. Check if this thread owns any mutexes of value + * greater than this one. If so, the thread has violated the mutex + * ordering rule. This indicates a coding error somewhere in + * the ACPI subsystem code. + */ + for (i = mutex_id; i < MAX_MUTEX; i++) { + if (acpi_gbl_mutex_info[i].owner_id == this_thread_id) { + if (i == mutex_id) { + continue; + } + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Invalid release order: owns [%s], releasing [%s]\n", + acpi_ut_get_mutex_name (i), acpi_ut_get_mutex_name (mutex_id))); + + return (AE_RELEASE_DEADLOCK); + } + } + + /* Mark unlocked FIRST */ + + acpi_gbl_mutex_info[mutex_id].owner_id = ACPI_MUTEX_NOT_ACQUIRED; + + status = acpi_os_signal_semaphore (acpi_gbl_mutex_info[mutex_id].mutex, 1); + + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Thread %X could not release Mutex [%s] %s\n", + this_thread_id, acpi_ut_get_mutex_name (mutex_id), + acpi_format_exception (status))); + } + else { + ACPI_DEBUG_PRINT ((ACPI_DB_MUTEX, "Thread %X released Mutex [%s]\n", + this_thread_id, acpi_ut_get_mutex_name (mutex_id))); + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_update_state_and_push + * + * PARAMETERS: *Object - Object to be added to the new state + * Action - Increment/Decrement + * state_list - List the state will be added to + * + * RETURN: None + * + * DESCRIPTION: Create a new state and push it + * + ******************************************************************************/ + +acpi_status +acpi_ut_create_update_state_and_push ( + union acpi_operand_object *object, + u16 action, + union acpi_generic_state **state_list) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_ENTRY (); + + + /* Ignore null objects; these are expected */ + + if (!object) { + return (AE_OK); + } + + state = acpi_ut_create_update_state (object, action); + if (!state) { + return (AE_NO_MEMORY); + } + + acpi_ut_push_generic_state (state_list, state); + return (AE_OK); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_pkg_state_and_push + * + * PARAMETERS: *Object - Object to be added to the new state + * Action - Increment/Decrement + * state_list - List the state will be added to + * + * RETURN: None + * + * DESCRIPTION: Create a new state and push it + * + ******************************************************************************/ +#ifdef ACPI_FUTURE_USAGE +acpi_status +acpi_ut_create_pkg_state_and_push ( + void *internal_object, + void *external_object, + u16 index, + union acpi_generic_state **state_list) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_ENTRY (); + + + state = acpi_ut_create_pkg_state (internal_object, external_object, index); + if (!state) { + return (AE_NO_MEMORY); + } + + acpi_ut_push_generic_state (state_list, state); + return (AE_OK); +} +#endif /* ACPI_FUTURE_USAGE */ + +/******************************************************************************* + * + * FUNCTION: acpi_ut_push_generic_state + * + * PARAMETERS: list_head - Head of the state stack + * State - State object to push + * + * RETURN: Status + * + * DESCRIPTION: Push a state object onto a state stack + * + ******************************************************************************/ + +void +acpi_ut_push_generic_state ( + union acpi_generic_state **list_head, + union acpi_generic_state *state) +{ + ACPI_FUNCTION_TRACE ("ut_push_generic_state"); + + + /* Push the state object onto the front of the list (stack) */ + + state->common.next = *list_head; + *list_head = state; + + return_VOID; +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_pop_generic_state + * + * PARAMETERS: list_head - Head of the state stack + * + * RETURN: Status + * + * DESCRIPTION: Pop a state object from a state stack + * + ******************************************************************************/ + +union acpi_generic_state * +acpi_ut_pop_generic_state ( + union acpi_generic_state **list_head) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_TRACE ("ut_pop_generic_state"); + + + /* Remove the state object at the head of the list (stack) */ + + state = *list_head; + if (state) { + /* Update the list head */ + + *list_head = state->common.next; + } + + return_PTR (state); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_generic_state + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Create a generic state object. Attempt to obtain one from + * the global state cache; If none available, create a new one. + * + ******************************************************************************/ + +union acpi_generic_state * +acpi_ut_create_generic_state (void) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_ENTRY (); + + + state = acpi_ut_acquire_from_cache (ACPI_MEM_LIST_STATE); + + /* Initialize */ + + if (state) { + state->common.data_type = ACPI_DESC_TYPE_STATE; + } + + return (state); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_thread_state + * + * PARAMETERS: None + * + * RETURN: Thread State + * + * DESCRIPTION: Create a "Thread State" - a flavor of the generic state used + * to track per-thread info during method execution + * + ******************************************************************************/ + +struct acpi_thread_state * +acpi_ut_create_thread_state ( + void) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_TRACE ("ut_create_thread_state"); + + + /* Create the generic state object */ + + state = acpi_ut_create_generic_state (); + if (!state) { + return_PTR (NULL); + } + + /* Init fields specific to the update struct */ + + state->common.data_type = ACPI_DESC_TYPE_STATE_THREAD; + state->thread.thread_id = acpi_os_get_thread_id (); + + return_PTR ((struct acpi_thread_state *) state); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_update_state + * + * PARAMETERS: Object - Initial Object to be installed in the + * state + * Action - Update action to be performed + * + * RETURN: Status + * + * DESCRIPTION: Create an "Update State" - a flavor of the generic state used + * to update reference counts and delete complex objects such + * as packages. + * + ******************************************************************************/ + +union acpi_generic_state * +acpi_ut_create_update_state ( + union acpi_operand_object *object, + u16 action) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_TRACE_PTR ("ut_create_update_state", object); + + + /* Create the generic state object */ + + state = acpi_ut_create_generic_state (); + if (!state) { + return_PTR (NULL); + } + + /* Init fields specific to the update struct */ + + state->common.data_type = ACPI_DESC_TYPE_STATE_UPDATE; + state->update.object = object; + state->update.value = action; + + return_PTR (state); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_pkg_state + * + * PARAMETERS: Object - Initial Object to be installed in the + * state + * Action - Update action to be performed + * + * RETURN: Status + * + * DESCRIPTION: Create a "Package State" + * + ******************************************************************************/ + +union acpi_generic_state * +acpi_ut_create_pkg_state ( + void *internal_object, + void *external_object, + u16 index) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_TRACE_PTR ("ut_create_pkg_state", internal_object); + + + /* Create the generic state object */ + + state = acpi_ut_create_generic_state (); + if (!state) { + return_PTR (NULL); + } + + /* Init fields specific to the update struct */ + + state->common.data_type = ACPI_DESC_TYPE_STATE_PACKAGE; + state->pkg.source_object = (union acpi_operand_object *) internal_object; + state->pkg.dest_object = external_object; + state->pkg.index = index; + state->pkg.num_packages = 1; + + return_PTR (state); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_control_state + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Create a "Control State" - a flavor of the generic state used + * to support nested IF/WHILE constructs in the AML. + * + ******************************************************************************/ + +union acpi_generic_state * +acpi_ut_create_control_state ( + void) +{ + union acpi_generic_state *state; + + + ACPI_FUNCTION_TRACE ("ut_create_control_state"); + + + /* Create the generic state object */ + + state = acpi_ut_create_generic_state (); + if (!state) { + return_PTR (NULL); + } + + /* Init fields specific to the control struct */ + + state->common.data_type = ACPI_DESC_TYPE_STATE_CONTROL; + state->common.state = ACPI_CONTROL_CONDITIONAL_EXECUTING; + + return_PTR (state); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_generic_state + * + * PARAMETERS: State - The state object to be deleted + * + * RETURN: Status + * + * DESCRIPTION: Put a state object back into the global state cache. The object + * is not actually freed at this time. + * + ******************************************************************************/ + +void +acpi_ut_delete_generic_state ( + union acpi_generic_state *state) +{ + ACPI_FUNCTION_TRACE ("ut_delete_generic_state"); + + + acpi_ut_release_to_cache (ACPI_MEM_LIST_STATE, state); + return_VOID; +} + + +#ifdef ACPI_ENABLE_OBJECT_CACHE +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_generic_state_cache + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Purge the global state object cache. Used during subsystem + * termination. + * + ******************************************************************************/ + +void +acpi_ut_delete_generic_state_cache ( + void) +{ + ACPI_FUNCTION_TRACE ("ut_delete_generic_state_cache"); + + + acpi_ut_delete_generic_cache (ACPI_MEM_LIST_STATE); + return_VOID; +} +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_walk_package_tree + * + * PARAMETERS: obj_desc - The Package object on which to resolve refs + * + * RETURN: Status + * + * DESCRIPTION: Walk through a package + * + ******************************************************************************/ + +acpi_status +acpi_ut_walk_package_tree ( + union acpi_operand_object *source_object, + void *target_object, + acpi_pkg_callback walk_callback, + void *context) +{ + acpi_status status = AE_OK; + union acpi_generic_state *state_list = NULL; + union acpi_generic_state *state; + u32 this_index; + union acpi_operand_object *this_source_obj; + + + ACPI_FUNCTION_TRACE ("ut_walk_package_tree"); + + + state = acpi_ut_create_pkg_state (source_object, target_object, 0); + if (!state) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + + while (state) { + /* Get one element of the package */ + + this_index = state->pkg.index; + this_source_obj = (union acpi_operand_object *) + state->pkg.source_object->package.elements[this_index]; + + /* + * Check for: + * 1) An uninitialized package element. It is completely + * legal to declare a package and leave it uninitialized + * 2) Not an internal object - can be a namespace node instead + * 3) Any type other than a package. Packages are handled in else + * case below. + */ + if ((!this_source_obj) || + (ACPI_GET_DESCRIPTOR_TYPE (this_source_obj) != ACPI_DESC_TYPE_OPERAND) || + (ACPI_GET_OBJECT_TYPE (this_source_obj) != ACPI_TYPE_PACKAGE)) { + status = walk_callback (ACPI_COPY_TYPE_SIMPLE, this_source_obj, + state, context); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + state->pkg.index++; + while (state->pkg.index >= state->pkg.source_object->package.count) { + /* + * We've handled all of the objects at this level, This means + * that we have just completed a package. That package may + * have contained one or more packages itself. + * + * Delete this state and pop the previous state (package). + */ + acpi_ut_delete_generic_state (state); + state = acpi_ut_pop_generic_state (&state_list); + + /* Finished when there are no more states */ + + if (!state) { + /* + * We have handled all of the objects in the top level + * package just add the length of the package objects + * and exit + */ + return_ACPI_STATUS (AE_OK); + } + + /* + * Go back up a level and move the index past the just + * completed package object. + */ + state->pkg.index++; + } + } + else { + /* This is a subobject of type package */ + + status = walk_callback (ACPI_COPY_TYPE_PACKAGE, this_source_obj, + state, context); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Push the current state and create a new one + * The callback above returned a new target package object. + */ + acpi_ut_push_generic_state (&state_list, state); + state = acpi_ut_create_pkg_state (this_source_obj, + state->pkg.this_target_obj, 0); + if (!state) { + return_ACPI_STATUS (AE_NO_MEMORY); + } + } + } + + /* We should never get here */ + + return_ACPI_STATUS (AE_AML_INTERNAL); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_generate_checksum + * + * PARAMETERS: Buffer - Buffer to be scanned + * Length - number of bytes to examine + * + * RETURN: checksum + * + * DESCRIPTION: Generate a checksum on a raw buffer + * + ******************************************************************************/ + +u8 +acpi_ut_generate_checksum ( + u8 *buffer, + u32 length) +{ + u32 i; + signed char sum = 0; + + + for (i = 0; i < length; i++) { + sum = (signed char) (sum + buffer[i]); + } + + return ((u8) (0 - sum)); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_resource_end_tag + * + * PARAMETERS: obj_desc - The resource template buffer object + * + * RETURN: Pointer to the end tag + * + * DESCRIPTION: Find the END_TAG resource descriptor in a resource template + * + ******************************************************************************/ + + +u8 * +acpi_ut_get_resource_end_tag ( + union acpi_operand_object *obj_desc) +{ + u8 buffer_byte; + u8 *buffer; + u8 *end_buffer; + + + buffer = obj_desc->buffer.pointer; + end_buffer = buffer + obj_desc->buffer.length; + + while (buffer < end_buffer) { + buffer_byte = *buffer; + if (buffer_byte & ACPI_RDESC_TYPE_MASK) { + /* Large Descriptor - Length is next 2 bytes */ + + buffer += ((*(buffer+1) | (*(buffer+2) << 8)) + 3); + } + else { + /* Small Descriptor. End Tag will be found here */ + + if ((buffer_byte & ACPI_RDESC_SMALL_MASK) == ACPI_RDESC_TYPE_END_TAG) { + /* Found the end tag descriptor, all done. */ + + return (buffer); + } + + /* Length is in the header */ + + buffer += ((buffer_byte & 0x07) + 1); + } + } + + /* End tag not found */ + + return (NULL); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_report_error + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: None + * + * DESCRIPTION: Print error message + * + ******************************************************************************/ + +void +acpi_ut_report_error ( + char *module_name, + u32 line_number, + u32 component_id) +{ + + + acpi_os_printf ("%8s-%04d: *** Error: ", module_name, line_number); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_report_warning + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: None + * + * DESCRIPTION: Print warning message + * + ******************************************************************************/ + +void +acpi_ut_report_warning ( + char *module_name, + u32 line_number, + u32 component_id) +{ + + acpi_os_printf ("%8s-%04d: *** Warning: ", module_name, line_number); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_report_info + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * component_id - Caller's component ID (for error output) + * Message - Error message to use on failure + * + * RETURN: None + * + * DESCRIPTION: Print information message + * + ******************************************************************************/ + +void +acpi_ut_report_info ( + char *module_name, + u32 line_number, + u32 component_id) +{ + + acpi_os_printf ("%8s-%04d: *** Info: ", module_name, line_number); +} + + diff --git a/drivers/acpi/utilities/utobject.c b/drivers/acpi/utilities/utobject.c new file mode 100644 index 00000000000..9ee40a484e0 --- /dev/null +++ b/drivers/acpi/utilities/utobject.c @@ -0,0 +1,671 @@ +/****************************************************************************** + * + * Module Name: utobject - ACPI object create/delete/size/cache routines + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + + +#include <acpi/acpi.h> +#include <acpi/acnamesp.h> +#include <acpi/amlcode.h> + + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utobject") + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_internal_object_dbg + * + * PARAMETERS: module_name - Source file name of caller + * line_number - Line number of caller + * component_id - Component type of caller + * Type - ACPI Type of the new object + * + * RETURN: Object - The new object. Null on failure + * + * DESCRIPTION: Create and initialize a new internal object. + * + * NOTE: We always allocate the worst-case object descriptor because + * these objects are cached, and we want them to be + * one-size-satisifies-any-request. This in itself may not be + * the most memory efficient, but the efficiency of the object + * cache should more than make up for this! + * + ******************************************************************************/ + +union acpi_operand_object * +acpi_ut_create_internal_object_dbg ( + char *module_name, + u32 line_number, + u32 component_id, + acpi_object_type type) +{ + union acpi_operand_object *object; + union acpi_operand_object *second_object; + + + ACPI_FUNCTION_TRACE_STR ("ut_create_internal_object_dbg", acpi_ut_get_type_name (type)); + + + /* Allocate the raw object descriptor */ + + object = acpi_ut_allocate_object_desc_dbg (module_name, line_number, component_id); + if (!object) { + return_PTR (NULL); + } + + switch (type) { + case ACPI_TYPE_REGION: + case ACPI_TYPE_BUFFER_FIELD: + + /* These types require a secondary object */ + + second_object = acpi_ut_allocate_object_desc_dbg (module_name, line_number, component_id); + if (!second_object) { + acpi_ut_delete_object_desc (object); + return_PTR (NULL); + } + + second_object->common.type = ACPI_TYPE_LOCAL_EXTRA; + second_object->common.reference_count = 1; + + /* Link the second object to the first */ + + object->common.next_object = second_object; + break; + + default: + /* All others have no secondary object */ + break; + } + + /* Save the object type in the object descriptor */ + + object->common.type = (u8) type; + + /* Init the reference count */ + + object->common.reference_count = 1; + + /* Any per-type initialization should go here */ + + return_PTR (object); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_buffer_object + * + * PARAMETERS: buffer_size - Size of buffer to be created + * + * RETURN: Pointer to a new Buffer object + * + * DESCRIPTION: Create a fully initialized buffer object + * + ******************************************************************************/ + +union acpi_operand_object * +acpi_ut_create_buffer_object ( + acpi_size buffer_size) +{ + union acpi_operand_object *buffer_desc; + u8 *buffer = NULL; + + + ACPI_FUNCTION_TRACE_U32 ("ut_create_buffer_object", buffer_size); + + + /* Create a new Buffer object */ + + buffer_desc = acpi_ut_create_internal_object (ACPI_TYPE_BUFFER); + if (!buffer_desc) { + return_PTR (NULL); + } + + /* Create an actual buffer only if size > 0 */ + + if (buffer_size > 0) { + /* Allocate the actual buffer */ + + buffer = ACPI_MEM_CALLOCATE (buffer_size); + if (!buffer) { + ACPI_REPORT_ERROR (("create_buffer: could not allocate size %X\n", + (u32) buffer_size)); + acpi_ut_remove_reference (buffer_desc); + return_PTR (NULL); + } + } + + /* Complete buffer object initialization */ + + buffer_desc->buffer.flags |= AOPOBJ_DATA_VALID; + buffer_desc->buffer.pointer = buffer; + buffer_desc->buffer.length = (u32) buffer_size; + + /* Return the new buffer descriptor */ + + return_PTR (buffer_desc); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_create_string_object + * + * PARAMETERS: string_size - Size of string to be created. Does not + * include NULL terminator, this is added + * automatically. + * + * RETURN: Pointer to a new String object + * + * DESCRIPTION: Create a fully initialized string object + * + ******************************************************************************/ + +union acpi_operand_object * +acpi_ut_create_string_object ( + acpi_size string_size) +{ + union acpi_operand_object *string_desc; + char *string; + + + ACPI_FUNCTION_TRACE_U32 ("ut_create_string_object", string_size); + + + /* Create a new String object */ + + string_desc = acpi_ut_create_internal_object (ACPI_TYPE_STRING); + if (!string_desc) { + return_PTR (NULL); + } + + /* + * Allocate the actual string buffer -- (Size + 1) for NULL terminator. + * NOTE: Zero-length strings are NULL terminated + */ + string = ACPI_MEM_CALLOCATE (string_size + 1); + if (!string) { + ACPI_REPORT_ERROR (("create_string: could not allocate size %X\n", + (u32) string_size)); + acpi_ut_remove_reference (string_desc); + return_PTR (NULL); + } + + /* Complete string object initialization */ + + string_desc->string.pointer = string; + string_desc->string.length = (u32) string_size; + + /* Return the new string descriptor */ + + return_PTR (string_desc); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_valid_internal_object + * + * PARAMETERS: Object - Object to be validated + * + * RETURN: Validate a pointer to be an union acpi_operand_object + * + ******************************************************************************/ + +u8 +acpi_ut_valid_internal_object ( + void *object) +{ + + ACPI_FUNCTION_NAME ("ut_valid_internal_object"); + + + /* Check for a null pointer */ + + if (!object) { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "**** Null Object Ptr\n")); + return (FALSE); + } + + /* Check the descriptor type field */ + + switch (ACPI_GET_DESCRIPTOR_TYPE (object)) { + case ACPI_DESC_TYPE_OPERAND: + + /* The object appears to be a valid union acpi_operand_object */ + + return (TRUE); + + default: + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, + "%p is not not an ACPI operand obj [%s]\n", + object, acpi_ut_get_descriptor_name (object))); + break; + } + + return (FALSE); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_allocate_object_desc_dbg + * + * PARAMETERS: module_name - Caller's module name (for error output) + * line_number - Caller's line number (for error output) + * component_id - Caller's component ID (for error output) + * + * RETURN: Pointer to newly allocated object descriptor. Null on error + * + * DESCRIPTION: Allocate a new object descriptor. Gracefully handle + * error conditions. + * + ******************************************************************************/ + +void * +acpi_ut_allocate_object_desc_dbg ( + char *module_name, + u32 line_number, + u32 component_id) +{ + union acpi_operand_object *object; + + + ACPI_FUNCTION_TRACE ("ut_allocate_object_desc_dbg"); + + + object = acpi_ut_acquire_from_cache (ACPI_MEM_LIST_OPERAND); + if (!object) { + _ACPI_REPORT_ERROR (module_name, line_number, component_id, + ("Could not allocate an object descriptor\n")); + + return_PTR (NULL); + } + + /* Mark the descriptor type */ + + ACPI_SET_DESCRIPTOR_TYPE (object, ACPI_DESC_TYPE_OPERAND); + + ACPI_DEBUG_PRINT ((ACPI_DB_ALLOCATIONS, "%p Size %X\n", + object, (u32) sizeof (union acpi_operand_object))); + + return_PTR (object); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_object_desc + * + * PARAMETERS: Object - An Acpi internal object to be deleted + * + * RETURN: None. + * + * DESCRIPTION: Free an ACPI object descriptor or add it to the object cache + * + ******************************************************************************/ + +void +acpi_ut_delete_object_desc ( + union acpi_operand_object *object) +{ + ACPI_FUNCTION_TRACE_PTR ("ut_delete_object_desc", object); + + + /* Object must be an union acpi_operand_object */ + + if (ACPI_GET_DESCRIPTOR_TYPE (object) != ACPI_DESC_TYPE_OPERAND) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "%p is not an ACPI Operand object [%s]\n", object, + acpi_ut_get_descriptor_name (object))); + return_VOID; + } + + acpi_ut_release_to_cache (ACPI_MEM_LIST_OPERAND, object); + + return_VOID; +} + + +#ifdef ACPI_ENABLE_OBJECT_CACHE +/******************************************************************************* + * + * FUNCTION: acpi_ut_delete_object_cache + * + * PARAMETERS: None + * + * RETURN: None + * + * DESCRIPTION: Purge the global state object cache. Used during subsystem + * termination. + * + ******************************************************************************/ + +void +acpi_ut_delete_object_cache ( + void) +{ + ACPI_FUNCTION_TRACE ("ut_delete_object_cache"); + + + acpi_ut_delete_generic_cache (ACPI_MEM_LIST_OPERAND); + return_VOID; +} +#endif + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_simple_object_size + * + * PARAMETERS: *internal_object - Pointer to the object we are examining + * *obj_length - Where the length is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to determine the space required to + * contain a simple object for return to an external user. + * + * The length includes the object structure plus any additional + * needed space. + * + ******************************************************************************/ + +acpi_status +acpi_ut_get_simple_object_size ( + union acpi_operand_object *internal_object, + acpi_size *obj_length) +{ + acpi_size length; + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE_PTR ("ut_get_simple_object_size", internal_object); + + + /* Handle a null object (Could be a uninitialized package element -- which is legal) */ + + if (!internal_object) { + *obj_length = 0; + return_ACPI_STATUS (AE_OK); + } + + /* Start with the length of the Acpi object */ + + length = sizeof (union acpi_object); + + if (ACPI_GET_DESCRIPTOR_TYPE (internal_object) == ACPI_DESC_TYPE_NAMED) { + /* Object is a named object (reference), just return the length */ + + *obj_length = ACPI_ROUND_UP_TO_NATIVE_WORD (length); + return_ACPI_STATUS (status); + } + + /* + * The final length depends on the object type + * Strings and Buffers are packed right up against the parent object and + * must be accessed bytewise or there may be alignment problems on + * certain processors + */ + switch (ACPI_GET_OBJECT_TYPE (internal_object)) { + case ACPI_TYPE_STRING: + + length += (acpi_size) internal_object->string.length + 1; + break; + + + case ACPI_TYPE_BUFFER: + + length += (acpi_size) internal_object->buffer.length; + break; + + + case ACPI_TYPE_INTEGER: + case ACPI_TYPE_PROCESSOR: + case ACPI_TYPE_POWER: + + /* + * No extra data for these types + */ + break; + + + case ACPI_TYPE_LOCAL_REFERENCE: + + switch (internal_object->reference.opcode) { + case AML_INT_NAMEPATH_OP: + + /* + * Get the actual length of the full pathname to this object. + * The reference will be converted to the pathname to the object + */ + length += ACPI_ROUND_UP_TO_NATIVE_WORD (acpi_ns_get_pathname_length (internal_object->reference.node)); + break; + + default: + + /* + * No other reference opcodes are supported. + * Notably, Locals and Args are not supported, but this may be + * required eventually. + */ + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, + "Unsupported Reference opcode=%X in object %p\n", + internal_object->reference.opcode, internal_object)); + status = AE_TYPE; + break; + } + break; + + + default: + + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unsupported type=%X in object %p\n", + ACPI_GET_OBJECT_TYPE (internal_object), internal_object)); + status = AE_TYPE; + break; + } + + /* + * Account for the space required by the object rounded up to the next + * multiple of the machine word size. This keeps each object aligned + * on a machine word boundary. (preventing alignment faults on some + * machines.) + */ + *obj_length = ACPI_ROUND_UP_TO_NATIVE_WORD (length); + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_element_length + * + * PARAMETERS: acpi_pkg_callback + * + * RETURN: Status + * + * DESCRIPTION: Get the length of one package element. + * + ******************************************************************************/ + +acpi_status +acpi_ut_get_element_length ( + u8 object_type, + union acpi_operand_object *source_object, + union acpi_generic_state *state, + void *context) +{ + acpi_status status = AE_OK; + struct acpi_pkg_info *info = (struct acpi_pkg_info *) context; + acpi_size object_space; + + + switch (object_type) { + case ACPI_COPY_TYPE_SIMPLE: + + /* + * Simple object - just get the size (Null object/entry is handled + * here also) and sum it into the running package length + */ + status = acpi_ut_get_simple_object_size (source_object, &object_space); + if (ACPI_FAILURE (status)) { + return (status); + } + + info->length += object_space; + break; + + + case ACPI_COPY_TYPE_PACKAGE: + + /* Package object - nothing much to do here, let the walk handle it */ + + info->num_packages++; + state->pkg.this_target_obj = NULL; + break; + + + default: + + /* No other types allowed */ + + return (AE_BAD_PARAMETER); + } + + return (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_package_object_size + * + * PARAMETERS: *internal_object - Pointer to the object we are examining + * *obj_length - Where the length is returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to determine the space required to + * contain a package object for return to an external user. + * + * This is moderately complex since a package contains other + * objects including packages. + * + ******************************************************************************/ + +acpi_status +acpi_ut_get_package_object_size ( + union acpi_operand_object *internal_object, + acpi_size *obj_length) +{ + acpi_status status; + struct acpi_pkg_info info; + + + ACPI_FUNCTION_TRACE_PTR ("ut_get_package_object_size", internal_object); + + + info.length = 0; + info.object_space = 0; + info.num_packages = 1; + + status = acpi_ut_walk_package_tree (internal_object, NULL, + acpi_ut_get_element_length, &info); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * We have handled all of the objects in all levels of the package. + * just add the length of the package objects themselves. + * Round up to the next machine word. + */ + info.length += ACPI_ROUND_UP_TO_NATIVE_WORD (sizeof (union acpi_object)) * + (acpi_size) info.num_packages; + + /* Return the total package length */ + + *obj_length = info.length; + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_ut_get_object_size + * + * PARAMETERS: *internal_object - Pointer to the object we are examining + * *obj_length - Where the length will be returned + * + * RETURN: Status + * + * DESCRIPTION: This function is called to determine the space required to + * contain an object for return to an API user. + * + ******************************************************************************/ + +acpi_status +acpi_ut_get_object_size( + union acpi_operand_object *internal_object, + acpi_size *obj_length) +{ + acpi_status status; + + + ACPI_FUNCTION_ENTRY (); + + + if ((ACPI_GET_DESCRIPTOR_TYPE (internal_object) == ACPI_DESC_TYPE_OPERAND) && + (ACPI_GET_OBJECT_TYPE (internal_object) == ACPI_TYPE_PACKAGE)) { + status = acpi_ut_get_package_object_size (internal_object, obj_length); + } + else { + status = acpi_ut_get_simple_object_size (internal_object, obj_length); + } + + return (status); +} + + diff --git a/drivers/acpi/utilities/utxface.c b/drivers/acpi/utilities/utxface.c new file mode 100644 index 00000000000..97a91f3f06f --- /dev/null +++ b/drivers/acpi/utilities/utxface.c @@ -0,0 +1,525 @@ +/****************************************************************************** + * + * Module Name: utxface - External interfaces for "global" ACPI functions + * + *****************************************************************************/ + +/* + * Copyright (C) 2000 - 2005, R. Byron Moore + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * substantially similar to the "NO WARRANTY" disclaimer below + * ("Disclaimer") and any redistribution must be conditioned upon + * including a substantially similar Disclaimer requirement for further + * binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * Alternatively, this software may be distributed under the terms of the + * GNU General Public License ("GPL") version 2 as published by the Free + * Software Foundation. + * + * NO WARRANTY + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + */ + +#include <linux/module.h> + +#include <acpi/acpi.h> +#include <acpi/acevents.h> +#include <acpi/acnamesp.h> +#include <acpi/acparser.h> +#include <acpi/acdispat.h> +#include <acpi/acdebug.h> + +#define _COMPONENT ACPI_UTILITIES + ACPI_MODULE_NAME ("utxface") + + +/******************************************************************************* + * + * FUNCTION: acpi_initialize_subsystem + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Initializes all global variables. This is the first function + * called, so any early initialization belongs here. + * + ******************************************************************************/ + +acpi_status +acpi_initialize_subsystem ( + void) +{ + acpi_status status; + + ACPI_FUNCTION_TRACE ("acpi_initialize_subsystem"); + + + ACPI_DEBUG_EXEC (acpi_ut_init_stack_ptr_trace ()); + + + /* Initialize all globals used by the subsystem */ + + acpi_ut_init_globals (); + + /* Initialize the OS-Dependent layer */ + + status = acpi_os_initialize (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("OSD failed to initialize, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + /* Create the default mutex objects */ + + status = acpi_ut_mutex_initialize (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Global mutex creation failure, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + /* + * Initialize the namespace manager and + * the root of the namespace tree + */ + + status = acpi_ns_root_initialize (); + if (ACPI_FAILURE (status)) { + ACPI_REPORT_ERROR (("Namespace initialization failure, %s\n", + acpi_format_exception (status))); + return_ACPI_STATUS (status); + } + + + /* If configured, initialize the AML debugger */ + + ACPI_DEBUGGER_EXEC (status = acpi_db_initialize ()); + + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_enable_subsystem + * + * PARAMETERS: Flags - Init/enable Options + * + * RETURN: Status + * + * DESCRIPTION: Completes the subsystem initialization including hardware. + * Puts system into ACPI mode if it isn't already. + * + ******************************************************************************/ + +acpi_status +acpi_enable_subsystem ( + u32 flags) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("acpi_enable_subsystem"); + + + /* + * We must initialize the hardware before we can enable ACPI. + * The values from the FADT are validated here. + */ + if (!(flags & ACPI_NO_HARDWARE_INIT)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Initializing ACPI hardware\n")); + + status = acpi_hw_initialize (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Enable ACPI mode */ + + if (!(flags & ACPI_NO_ACPI_ENABLE)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Going into ACPI mode\n")); + + acpi_gbl_original_mode = acpi_hw_get_mode(); + + status = acpi_enable (); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "acpi_enable failed.\n")); + return_ACPI_STATUS (status); + } + } + + /* + * Install the default op_region handlers. These are installed unless + * other handlers have already been installed via the + * install_address_space_handler interface. + */ + if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Installing default address space handlers\n")); + + status = acpi_ev_install_region_handlers (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* + * Initialize ACPI Event handling (Fixed and General Purpose) + * + * NOTE: We must have the hardware AND events initialized before we can execute + * ANY control methods SAFELY. Any control method can require ACPI hardware + * support, so the hardware MUST be initialized before execution! + */ + if (!(flags & ACPI_NO_EVENT_INIT)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Initializing ACPI events\n")); + + status = acpi_ev_initialize_events (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* Install the SCI handler and Global Lock handler */ + + if (!(flags & ACPI_NO_HANDLER_INIT)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Installing SCI/GL handlers\n")); + + status = acpi_ev_install_xrupt_handlers (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + return_ACPI_STATUS (status); +} + +/******************************************************************************* + * + * FUNCTION: acpi_initialize_objects + * + * PARAMETERS: Flags - Init/enable Options + * + * RETURN: Status + * + * DESCRIPTION: Completes namespace initialization by initializing device + * objects and executing AML code for Regions, buffers, etc. + * + ******************************************************************************/ + +acpi_status +acpi_initialize_objects ( + u32 flags) +{ + acpi_status status = AE_OK; + + + ACPI_FUNCTION_TRACE ("acpi_initialize_objects"); + + + /* + * Run all _REG methods + * + * NOTE: Any objects accessed + * by the _REG methods will be automatically initialized, even if they + * contain executable AML (see call to acpi_ns_initialize_objects below). + */ + if (!(flags & ACPI_NO_ADDRESS_SPACE_INIT)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Executing _REG op_region methods\n")); + + status = acpi_ev_initialize_op_regions (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* + * Initialize the objects that remain uninitialized. This + * runs the executable AML that may be part of the declaration of these + * objects: operation_regions, buffer_fields, Buffers, and Packages. + */ + if (!(flags & ACPI_NO_OBJECT_INIT)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Completing Initialization of ACPI Objects\n")); + + status = acpi_ns_initialize_objects (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* + * Initialize all device objects in the namespace + * This runs the _STA and _INI methods. + */ + if (!(flags & ACPI_NO_DEVICE_INIT)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Initializing ACPI Devices\n")); + + status = acpi_ns_initialize_devices (); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + } + + /* + * Empty the caches (delete the cached objects) on the assumption that + * the table load filled them up more than they will be at runtime -- + * thus wasting non-paged memory. + */ + status = acpi_purge_cached_objects (); + + acpi_gbl_startup_flags |= ACPI_INITIALIZED_OK; + return_ACPI_STATUS (status); +} + + +/******************************************************************************* + * + * FUNCTION: acpi_terminate + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Shutdown the ACPI subsystem. Release all resources. + * + ******************************************************************************/ + +acpi_status +acpi_terminate (void) +{ + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_terminate"); + + + /* Terminate the AML Debugger if present */ + + ACPI_DEBUGGER_EXEC(acpi_gbl_db_terminate_threads = TRUE); + + /* Shutdown and free all resources */ + + acpi_ut_subsystem_shutdown (); + + + /* Free the mutex objects */ + + acpi_ut_mutex_terminate (); + + +#ifdef ACPI_DEBUGGER + + /* Shut down the debugger */ + + acpi_db_terminate (); +#endif + + /* Now we can shutdown the OS-dependent layer */ + + status = acpi_os_terminate (); + return_ACPI_STATUS (status); +} + + +#ifdef ACPI_FUTURE_USAGE + +/***************************************************************************** + * + * FUNCTION: acpi_subsystem_status + * + * PARAMETERS: None + * + * RETURN: Status of the ACPI subsystem + * + * DESCRIPTION: Other drivers that use the ACPI subsystem should call this + * before making any other calls, to ensure the subsystem initial- + * ized successfully. + * + ****************************************************************************/ + +acpi_status +acpi_subsystem_status (void) +{ + if (acpi_gbl_startup_flags & ACPI_INITIALIZED_OK) { + return (AE_OK); + } + else { + return (AE_ERROR); + } +} + + +/****************************************************************************** + * + * FUNCTION: acpi_get_system_info + * + * PARAMETERS: out_buffer - a pointer to a buffer to receive the + * resources for the device + * buffer_length - the number of bytes available in the buffer + * + * RETURN: Status - the status of the call + * + * DESCRIPTION: This function is called to get information about the current + * state of the ACPI subsystem. It will return system information + * in the out_buffer. + * + * If the function fails an appropriate status will be returned + * and the value of out_buffer is undefined. + * + ******************************************************************************/ + +acpi_status +acpi_get_system_info ( + struct acpi_buffer *out_buffer) +{ + struct acpi_system_info *info_ptr; + u32 i; + acpi_status status; + + + ACPI_FUNCTION_TRACE ("acpi_get_system_info"); + + + /* Parameter validation */ + + status = acpi_ut_validate_buffer (out_buffer); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* Validate/Allocate/Clear caller buffer */ + + status = acpi_ut_initialize_buffer (out_buffer, sizeof (struct acpi_system_info)); + if (ACPI_FAILURE (status)) { + return_ACPI_STATUS (status); + } + + /* + * Populate the return buffer + */ + info_ptr = (struct acpi_system_info *) out_buffer->pointer; + + info_ptr->acpi_ca_version = ACPI_CA_VERSION; + + /* System flags (ACPI capabilities) */ + + info_ptr->flags = ACPI_SYS_MODE_ACPI; + + /* Timer resolution - 24 or 32 bits */ + + if (!acpi_gbl_FADT) { + info_ptr->timer_resolution = 0; + } + else if (acpi_gbl_FADT->tmr_val_ext == 0) { + info_ptr->timer_resolution = 24; + } + else { + info_ptr->timer_resolution = 32; + } + + /* Clear the reserved fields */ + + info_ptr->reserved1 = 0; + info_ptr->reserved2 = 0; + + /* Current debug levels */ + + info_ptr->debug_layer = acpi_dbg_layer; + info_ptr->debug_level = acpi_dbg_level; + + /* Current status of the ACPI tables, per table type */ + + info_ptr->num_table_types = NUM_ACPI_TABLE_TYPES; + for (i = 0; i < NUM_ACPI_TABLE_TYPES; i++) { + info_ptr->table_info[i].count = acpi_gbl_table_lists[i].count; + } + + return_ACPI_STATUS (AE_OK); +} +EXPORT_SYMBOL(acpi_get_system_info); + + +/***************************************************************************** + * + * FUNCTION: acpi_install_initialization_handler + * + * PARAMETERS: Handler - Callback procedure + * + * RETURN: Status + * + * DESCRIPTION: Install an initialization handler + * + * TBD: When a second function is added, must save the Function also. + * + ****************************************************************************/ + +acpi_status +acpi_install_initialization_handler ( + acpi_init_handler handler, + u32 function) +{ + + if (!handler) { + return (AE_BAD_PARAMETER); + } + + if (acpi_gbl_init_handler) { + return (AE_ALREADY_EXISTS); + } + + acpi_gbl_init_handler = handler; + return AE_OK; +} + +#endif /* ACPI_FUTURE_USAGE */ + + +/***************************************************************************** + * + * FUNCTION: acpi_purge_cached_objects + * + * PARAMETERS: None + * + * RETURN: Status + * + * DESCRIPTION: Empty all caches (delete the cached objects) + * + ****************************************************************************/ + +acpi_status +acpi_purge_cached_objects (void) +{ + ACPI_FUNCTION_TRACE ("acpi_purge_cached_objects"); + + +#ifdef ACPI_ENABLE_OBJECT_CACHE + acpi_ut_delete_generic_state_cache (); + acpi_ut_delete_object_cache (); + acpi_ds_delete_walk_state_cache (); + acpi_ps_delete_parse_cache (); +#endif + + return_ACPI_STATUS (AE_OK); +} diff --git a/drivers/acpi/utils.c b/drivers/acpi/utils.c new file mode 100644 index 00000000000..1ce2047c380 --- /dev/null +++ b/drivers/acpi/utils.c @@ -0,0 +1,423 @@ +/* + * acpi_utils.c - ACPI Utility Functions ($Revision: 10 $) + * + * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> + * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + + +#define _COMPONENT ACPI_BUS_COMPONENT +ACPI_MODULE_NAME ("acpi_utils") + + +/* -------------------------------------------------------------------------- + Object Evaluation Helpers + -------------------------------------------------------------------------- */ + +#ifdef ACPI_DEBUG_OUTPUT +#define acpi_util_eval_error(h,p,s) {\ + char prefix[80] = {'\0'};\ + struct acpi_buffer buffer = {sizeof(prefix), prefix};\ + acpi_get_name(h, ACPI_FULL_PATHNAME, &buffer);\ + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Evaluate [%s.%s]: %s\n",\ + (char *) prefix, p, acpi_format_exception(s))); } +#else +#define acpi_util_eval_error(h,p,s) +#endif + + +acpi_status +acpi_extract_package ( + union acpi_object *package, + struct acpi_buffer *format, + struct acpi_buffer *buffer) +{ + u32 size_required = 0; + u32 tail_offset = 0; + char *format_string = NULL; + u32 format_count = 0; + u32 i = 0; + u8 *head = NULL; + u8 *tail = NULL; + + ACPI_FUNCTION_TRACE("acpi_extract_package"); + + if (!package || (package->type != ACPI_TYPE_PACKAGE) || (package->package.count < 1)) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid 'package' argument\n")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (!format || !format->pointer || (format->length < 1)) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid 'format' argument\n")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + if (!buffer) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid 'buffer' argument\n")); + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + format_count = (format->length/sizeof(char)) - 1; + if (format_count > package->package.count) { + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Format specifies more objects [%d] than exist in package [%d].", format_count, package->package.count)); + return_ACPI_STATUS(AE_BAD_DATA); + } + + format_string = (char*)format->pointer; + + /* + * Calculate size_required. + */ + for (i=0; i<format_count; i++) { + + union acpi_object *element = &(package->package.elements[i]); + + if (!element) { + return_ACPI_STATUS(AE_BAD_DATA); + } + + switch (element->type) { + + case ACPI_TYPE_INTEGER: + switch (format_string[i]) { + case 'N': + size_required += sizeof(acpi_integer); + tail_offset += sizeof(acpi_integer); + break; + case 'S': + size_required += sizeof(char*) + sizeof(acpi_integer) + sizeof(char); + tail_offset += sizeof(char*); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid package element [%d]: got number, expecing [%c].\n", i, format_string[i])); + return_ACPI_STATUS(AE_BAD_DATA); + break; + } + break; + + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + switch (format_string[i]) { + case 'S': + size_required += sizeof(char*) + (element->string.length * sizeof(char)) + sizeof(char); + tail_offset += sizeof(char*); + break; + case 'B': + size_required += sizeof(u8*) + (element->buffer.length * sizeof(u8)); + tail_offset += sizeof(u8*); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Invalid package element [%d] got string/buffer, expecing [%c].\n", i, format_string[i])); + return_ACPI_STATUS(AE_BAD_DATA); + break; + } + break; + + case ACPI_TYPE_PACKAGE: + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found unsupported element at index=%d\n", i)); + /* TBD: handle nested packages... */ + return_ACPI_STATUS(AE_SUPPORT); + break; + } + } + + /* + * Validate output buffer. + */ + if (buffer->length < size_required) { + buffer->length = size_required; + return_ACPI_STATUS(AE_BUFFER_OVERFLOW); + } + else if (buffer->length != size_required || !buffer->pointer) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + head = buffer->pointer; + tail = buffer->pointer + tail_offset; + + /* + * Extract package data. + */ + for (i=0; i<format_count; i++) { + + u8 **pointer = NULL; + union acpi_object *element = &(package->package.elements[i]); + + if (!element) { + return_ACPI_STATUS(AE_BAD_DATA); + } + + switch (element->type) { + + case ACPI_TYPE_INTEGER: + switch (format_string[i]) { + case 'N': + *((acpi_integer*)head) = element->integer.value; + head += sizeof(acpi_integer); + break; + case 'S': + pointer = (u8**)head; + *pointer = tail; + *((acpi_integer*)tail) = element->integer.value; + head += sizeof(acpi_integer*); + tail += sizeof(acpi_integer); + /* NULL terminate string */ + *tail = (char)0; + tail += sizeof(char); + break; + default: + /* Should never get here */ + break; + } + break; + + case ACPI_TYPE_STRING: + case ACPI_TYPE_BUFFER: + switch (format_string[i]) { + case 'S': + pointer = (u8**)head; + *pointer = tail; + memcpy(tail, element->string.pointer, element->string.length); + head += sizeof(char*); + tail += element->string.length * sizeof(char); + /* NULL terminate string */ + *tail = (char)0; + tail += sizeof(char); + break; + case 'B': + pointer = (u8**)head; + *pointer = tail; + memcpy(tail, element->buffer.pointer, element->buffer.length); + head += sizeof(u8*); + tail += element->buffer.length * sizeof(u8); + break; + default: + /* Should never get here */ + break; + } + break; + + case ACPI_TYPE_PACKAGE: + /* TBD: handle nested packages... */ + default: + /* Should never get here */ + break; + } + } + + return_ACPI_STATUS(AE_OK); +} +EXPORT_SYMBOL(acpi_extract_package); + + +acpi_status +acpi_evaluate_integer ( + acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *arguments, + unsigned long *data) +{ + acpi_status status = AE_OK; + union acpi_object *element; + struct acpi_buffer buffer = {0,NULL}; + + ACPI_FUNCTION_TRACE("acpi_evaluate_integer"); + + if (!data) + return_ACPI_STATUS(AE_BAD_PARAMETER); + + element = kmalloc(sizeof(union acpi_object), GFP_KERNEL); + if(!element) + return_ACPI_STATUS(AE_NO_MEMORY); + + memset(element, 0, sizeof(union acpi_object)); + buffer.length = sizeof(union acpi_object); + buffer.pointer = element; + status = acpi_evaluate_object(handle, pathname, arguments, &buffer); + if (ACPI_FAILURE(status)) { + acpi_util_eval_error(handle, pathname, status); + return_ACPI_STATUS(status); + } + + if (element->type != ACPI_TYPE_INTEGER) { + acpi_util_eval_error(handle, pathname, AE_BAD_DATA); + return_ACPI_STATUS(AE_BAD_DATA); + } + + *data = element->integer.value; + kfree(element); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%lu]\n", *data)); + + return_ACPI_STATUS(AE_OK); +} +EXPORT_SYMBOL(acpi_evaluate_integer); + + +#if 0 +acpi_status +acpi_evaluate_string ( + acpi_handle handle, + acpi_string pathname, + acpi_object_list *arguments, + acpi_string *data) +{ + acpi_status status = AE_OK; + acpi_object *element = NULL; + acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + + ACPI_FUNCTION_TRACE("acpi_evaluate_string"); + + if (!data) + return_ACPI_STATUS(AE_BAD_PARAMETER); + + status = acpi_evaluate_object(handle, pathname, arguments, &buffer); + if (ACPI_FAILURE(status)) { + acpi_util_eval_error(handle, pathname, status); + return_ACPI_STATUS(status); + } + + element = (acpi_object *) buffer.pointer; + + if ((element->type != ACPI_TYPE_STRING) + || (element->type != ACPI_TYPE_BUFFER) + || !element->string.length) { + acpi_util_eval_error(handle, pathname, AE_BAD_DATA); + return_ACPI_STATUS(AE_BAD_DATA); + } + + *data = kmalloc(element->string.length + 1, GFP_KERNEL); + if (!data) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Memory allocation error\n")); + return_VALUE(-ENOMEM); + } + memset(*data, 0, element->string.length + 1); + + memcpy(*data, element->string.pointer, element->string.length); + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Return value [%s]\n", *data)); + + acpi_os_free(buffer.pointer); + + return_ACPI_STATUS(AE_OK); +} +#endif + + +acpi_status +acpi_evaluate_reference ( + acpi_handle handle, + acpi_string pathname, + struct acpi_object_list *arguments, + struct acpi_handle_list *list) +{ + acpi_status status = AE_OK; + union acpi_object *package = NULL; + union acpi_object *element = NULL; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + u32 i = 0; + + ACPI_FUNCTION_TRACE("acpi_evaluate_reference"); + + if (!list) { + return_ACPI_STATUS(AE_BAD_PARAMETER); + } + + /* Evaluate object. */ + + status = acpi_evaluate_object(handle, pathname, arguments, &buffer); + if (ACPI_FAILURE(status)) + goto end; + + package = (union acpi_object *) buffer.pointer; + + if ((buffer.length == 0) || !package) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "No return object (len %X ptr %p)\n", + (unsigned)buffer.length, package)); + status = AE_BAD_DATA; + acpi_util_eval_error(handle, pathname, status); + goto end; + } + if (package->type != ACPI_TYPE_PACKAGE) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Expecting a [Package], found type %X\n", + package->type)); + status = AE_BAD_DATA; + acpi_util_eval_error(handle, pathname, status); + goto end; + } + if (!package->package.count) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "[Package] has zero elements (%p)\n", + package)); + status = AE_BAD_DATA; + acpi_util_eval_error(handle, pathname, status); + goto end; + } + + if (package->package.count > ACPI_MAX_HANDLES) { + return_ACPI_STATUS(AE_NO_MEMORY); + } + list->count = package->package.count; + + /* Extract package data. */ + + for (i = 0; i < list->count; i++) { + + element = &(package->package.elements[i]); + + if (element->type != ACPI_TYPE_ANY) { + status = AE_BAD_DATA; + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Expecting a [Reference] package element, found type %X\n", + element->type)); + acpi_util_eval_error(handle, pathname, status); + break; + } + + /* Get the acpi_handle. */ + + list->handles[i] = element->reference.handle; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found reference [%p]\n", + list->handles[i])); + } + +end: + if (ACPI_FAILURE(status)) { + list->count = 0; + //kfree(list->handles); + } + + acpi_os_free(buffer.pointer); + + return_ACPI_STATUS(status); +} +EXPORT_SYMBOL(acpi_evaluate_reference); + diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c new file mode 100644 index 00000000000..71fa1011715 --- /dev/null +++ b/drivers/acpi/video.c @@ -0,0 +1,1989 @@ +/* + * video.c - ACPI Video Driver ($Revision:$) + * + * Copyright (C) 2004 Luming Yu <luming.yu@intel.com> + * Copyright (C) 2004 Bruno Ducrot <ducrot@poupinou.org> + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/list.h> +#include <linux/proc_fs.h> +#include <linux/seq_file.h> + +#include <asm/uaccess.h> + +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> + +#define ACPI_VIDEO_COMPONENT 0x08000000 +#define ACPI_VIDEO_CLASS "video" +#define ACPI_VIDEO_DRIVER_NAME "ACPI Video Driver" +#define ACPI_VIDEO_BUS_NAME "Video Bus" +#define ACPI_VIDEO_DEVICE_NAME "Video Device" +#define ACPI_VIDEO_NOTIFY_SWITCH 0x80 +#define ACPI_VIDEO_NOTIFY_PROBE 0x81 +#define ACPI_VIDEO_NOTIFY_CYCLE 0x82 +#define ACPI_VIDEO_NOTIFY_NEXT_OUTPUT 0x83 +#define ACPI_VIDEO_NOTIFY_PREV_OUTPUT 0x84 + +#define ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS 0x82 +#define ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS 0x83 +#define ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS 0x84 +#define ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS 0x85 +#define ACPI_VIDEO_NOTIFY_DISPLAY_OFF 0x86 + + +#define ACPI_VIDEO_HEAD_INVALID (~0u - 1) +#define ACPI_VIDEO_HEAD_END (~0u) + + +#define _COMPONENT ACPI_VIDEO_COMPONENT +ACPI_MODULE_NAME ("acpi_video") + +MODULE_AUTHOR("Bruno Ducrot"); +MODULE_DESCRIPTION(ACPI_VIDEO_DRIVER_NAME); +MODULE_LICENSE("GPL"); + +static int acpi_video_bus_add (struct acpi_device *device); +static int acpi_video_bus_remove (struct acpi_device *device, int type); +static int acpi_video_bus_match (struct acpi_device *device, struct acpi_driver *driver); + +static struct acpi_driver acpi_video_bus = { + .name = ACPI_VIDEO_DRIVER_NAME, + .class = ACPI_VIDEO_CLASS, + .ops = { + .add = acpi_video_bus_add, + .remove = acpi_video_bus_remove, + .match = acpi_video_bus_match, + }, +}; + +struct acpi_video_bus_flags { + u8 multihead:1; /* can switch video heads */ + u8 rom:1; /* can retrieve a video rom */ + u8 post:1; /* can configure the head to */ + u8 reserved:5; +}; + +struct acpi_video_bus_cap { + u8 _DOS:1; /*Enable/Disable output switching*/ + u8 _DOD:1; /*Enumerate all devices attached to display adapter*/ + u8 _ROM:1; /*Get ROM Data*/ + u8 _GPD:1; /*Get POST Device*/ + u8 _SPD:1; /*Set POST Device*/ + u8 _VPO:1; /*Video POST Options*/ + u8 reserved:2; +}; + +struct acpi_video_device_attrib{ + u32 display_index:4; /* A zero-based instance of the Display*/ + u32 display_port_attachment:4; /*This field differenates displays type*/ + u32 display_type:4; /*Describe the specific type in use*/ + u32 vendor_specific:4; /*Chipset Vendor Specifi*/ + u32 bios_can_detect:1; /*BIOS can detect the device*/ + u32 depend_on_vga:1; /*Non-VGA output device whose power is related to + the VGA device.*/ + u32 pipe_id:3; /*For VGA multiple-head devices.*/ + u32 reserved:10; /*Must be 0*/ + u32 device_id_scheme:1; /*Device ID Scheme*/ +}; + +struct acpi_video_enumerated_device { + union { + u32 int_val; + struct acpi_video_device_attrib attrib; + } value; + struct acpi_video_device *bind_info; +}; + +struct acpi_video_bus { + acpi_handle handle; + u8 dos_setting; + struct acpi_video_enumerated_device *attached_array; + u8 attached_count; + struct acpi_video_bus_cap cap; + struct acpi_video_bus_flags flags; + struct semaphore sem; + struct list_head video_device_list; + struct proc_dir_entry *dir; +}; + +struct acpi_video_device_flags { + u8 crt:1; + u8 lcd:1; + u8 tvout:1; + u8 bios:1; + u8 unknown:1; + u8 reserved:3; +}; + +struct acpi_video_device_cap { + u8 _ADR:1; /*Return the unique ID */ + u8 _BCL:1; /*Query list of brightness control levels supported*/ + u8 _BCM:1; /*Set the brightness level*/ + u8 _DDC:1; /*Return the EDID for this device*/ + u8 _DCS:1; /*Return status of output device*/ + u8 _DGS:1; /*Query graphics state*/ + u8 _DSS:1; /*Device state set*/ + u8 _reserved:1; +}; + +struct acpi_video_device_brightness { + int curr; + int count; + int *levels; +}; + +struct acpi_video_device { + acpi_handle handle; + unsigned long device_id; + struct acpi_video_device_flags flags; + struct acpi_video_device_cap cap; + struct list_head entry; + struct acpi_video_bus *video; + struct acpi_device *dev; + struct acpi_video_device_brightness *brightness; +}; + + +/* bus */ +static int acpi_video_bus_info_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_video_bus_info_fops = { + .open = acpi_video_bus_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int acpi_video_bus_ROM_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_video_bus_ROM_fops = { + .open = acpi_video_bus_ROM_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int acpi_video_bus_POST_info_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_video_bus_POST_info_fops = { + .open = acpi_video_bus_POST_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int acpi_video_bus_POST_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_video_bus_POST_fops = { + .open = acpi_video_bus_POST_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + + +static int acpi_video_bus_DOS_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_video_bus_DOS_fops = { + .open = acpi_video_bus_DOS_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* device */ +static int acpi_video_device_info_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_video_device_info_fops = { + .open = acpi_video_device_info_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int acpi_video_device_state_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_video_device_state_fops = { + .open = acpi_video_device_state_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int acpi_video_device_brightness_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_video_device_brightness_fops = { + .open = acpi_video_device_brightness_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static int acpi_video_device_EDID_open_fs(struct inode *inode, struct file *file); +static struct file_operations acpi_video_device_EDID_fops = { + .open = acpi_video_device_EDID_open_fs, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static char device_decode[][30] = { + "motherboard VGA device", + "PCI VGA device", + "AGP VGA device", + "UNKNOWN", +}; + +static void acpi_video_device_notify ( acpi_handle handle, u32 event, void *data); +static void acpi_video_device_rebind( struct acpi_video_bus *video); +static void acpi_video_device_bind( struct acpi_video_bus *video, struct acpi_video_device *device); +static int acpi_video_device_enumerate(struct acpi_video_bus *video); +static int acpi_video_switch_output( struct acpi_video_bus *video, int event); +static int acpi_video_get_next_level( struct acpi_video_device *device, u32 level_current,u32 event); +static void acpi_video_switch_brightness ( struct acpi_video_device *device, int event); + + +/* -------------------------------------------------------------------------- + Video Management + -------------------------------------------------------------------------- */ + +/* device */ + +static int +acpi_video_device_query ( + struct acpi_video_device *device, + unsigned long *state) +{ + int status; + ACPI_FUNCTION_TRACE("acpi_video_device_query"); + status = acpi_evaluate_integer(device->handle, "_DGS", NULL, state); + + return_VALUE(status); +} + +static int +acpi_video_device_get_state ( + struct acpi_video_device *device, + unsigned long *state) +{ + int status; + + ACPI_FUNCTION_TRACE("acpi_video_device_get_state"); + + status = acpi_evaluate_integer(device->handle, "_DCS", NULL, state); + + return_VALUE(status); +} + +static int +acpi_video_device_set_state ( + struct acpi_video_device *device, + int state) +{ + int status; + union acpi_object arg0 = {ACPI_TYPE_INTEGER}; + struct acpi_object_list args = {1, &arg0}; + + ACPI_FUNCTION_TRACE("acpi_video_device_set_state"); + + arg0.integer.value = state; + status = acpi_evaluate_integer(device->handle, "_DSS", &args, NULL); + + return_VALUE(status); +} + +static int +acpi_video_device_lcd_query_levels ( + struct acpi_video_device *device, + union acpi_object **levels) +{ + int status; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; + + + ACPI_FUNCTION_TRACE("acpi_video_device_lcd_query_levels"); + + *levels = NULL; + + status = acpi_evaluate_object(device->handle, "_BCL", NULL, &buffer); + if (!ACPI_SUCCESS(status)) + return_VALUE(status); + obj = (union acpi_object *) buffer.pointer; + if (!obj && (obj->type != ACPI_TYPE_PACKAGE)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _BCL data\n")); + status = -EFAULT; + goto err; + } + + *levels = obj; + + return_VALUE(0); + +err: + if (buffer.pointer) + kfree(buffer.pointer); + + return_VALUE(status); +} + +static int +acpi_video_device_lcd_set_level ( + struct acpi_video_device *device, + int level) +{ + int status; + union acpi_object arg0 = {ACPI_TYPE_INTEGER}; + struct acpi_object_list args = {1, &arg0}; + + ACPI_FUNCTION_TRACE("acpi_video_device_lcd_set_level"); + + arg0.integer.value = level; + status = acpi_evaluate_object(device->handle, "_BCM", &args, NULL); + + printk(KERN_DEBUG "set_level status: %x\n", status); + return_VALUE(status); +} + +static int +acpi_video_device_lcd_get_level_current ( + struct acpi_video_device *device, + unsigned long *level) +{ + int status; + ACPI_FUNCTION_TRACE("acpi_video_device_lcd_get_level_current"); + + status = acpi_evaluate_integer(device->handle, "_BQC", NULL, level); + + return_VALUE(status); +} + +static int +acpi_video_device_EDID ( + struct acpi_video_device *device, + union acpi_object **edid, + ssize_t length) +{ + int status; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; + union acpi_object arg0 = {ACPI_TYPE_INTEGER}; + struct acpi_object_list args = {1, &arg0}; + + ACPI_FUNCTION_TRACE("acpi_video_device_get_EDID"); + + *edid = NULL; + + if (!device) + return_VALUE(-ENODEV); + if (length == 128) + arg0.integer.value = 1; + else if (length == 256) + arg0.integer.value = 2; + else + return_VALUE(-EINVAL); + + status = acpi_evaluate_object(device->handle, "_DDC", &args, &buffer); + if (ACPI_FAILURE(status)) + return_VALUE(-ENODEV); + + obj = (union acpi_object *) buffer.pointer; + + if (obj && obj->type == ACPI_TYPE_BUFFER) + *edid = obj; + else { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _DDC data\n")); + status = -EFAULT; + kfree(obj); + } + + return_VALUE(status); +} + + +/* bus */ + +static int +acpi_video_bus_set_POST ( + struct acpi_video_bus *video, + unsigned long option) +{ + int status; + unsigned long tmp; + union acpi_object arg0 = {ACPI_TYPE_INTEGER}; + struct acpi_object_list args = {1, &arg0}; + + ACPI_FUNCTION_TRACE("acpi_video_bus_set_POST"); + + arg0.integer.value = option; + + status = acpi_evaluate_integer(video->handle, "_SPD", &args, &tmp); + if (ACPI_SUCCESS(status)) + status = tmp ? (-EINVAL):(AE_OK); + + return_VALUE(status); +} + +static int +acpi_video_bus_get_POST ( + struct acpi_video_bus *video, + unsigned long *id) +{ + int status; + + ACPI_FUNCTION_TRACE("acpi_video_bus_get_POST"); + + status = acpi_evaluate_integer(video->handle, "_GPD", NULL, id); + + return_VALUE(status); +} + +static int +acpi_video_bus_POST_options ( + struct acpi_video_bus *video, + unsigned long *options) +{ + int status; + ACPI_FUNCTION_TRACE("acpi_video_bus_POST_options"); + + status = acpi_evaluate_integer(video->handle, "_VPO", NULL, options); + *options &= 3; + + return_VALUE(status); +} + +/* + * Arg: + * video : video bus device pointer + * bios_flag : + * 0. The system BIOS should NOT automatically switch(toggle) + * the active display output. + * 1. The system BIOS should automatically switch (toggle) the + * active display output. No swich event. + * 2. The _DGS value should be locked. + * 3. The system BIOS should not automatically switch (toggle) the + * active display output, but instead generate the display switch + * event notify code. + * lcd_flag : + * 0. The system BIOS should automatically control the brightness level + * of the LCD, when the power changes from AC to DC + * 1. The system BIOS should NOT automatically control the brightness + * level of the LCD, when the power changes from AC to DC. + * Return Value: + * -1 wrong arg. + */ + +static int +acpi_video_bus_DOS( + struct acpi_video_bus *video, + int bios_flag, + int lcd_flag) +{ + acpi_integer status = 0; + union acpi_object arg0 = {ACPI_TYPE_INTEGER}; + struct acpi_object_list args = {1, &arg0}; + + ACPI_FUNCTION_TRACE("acpi_video_bus_DOS"); + + if (bios_flag < 0 || bios_flag >3 || lcd_flag < 0 || lcd_flag > 1){ + status = -1; + goto Failed; + } + arg0.integer.value = (lcd_flag << 2) | bios_flag; + video->dos_setting = arg0.integer.value; + acpi_evaluate_object(video->handle, "_DOS", &args, NULL); + +Failed: + return_VALUE(status); +} + +/* + * Arg: + * device : video output device (LCD, CRT, ..) + * + * Return Value: + * None + * + * Find out all required AML method defined under the output + * device. + */ + +static void +acpi_video_device_find_cap (struct acpi_video_device *device) +{ + acpi_integer status; + acpi_handle h_dummy1; + int i; + union acpi_object *obj = NULL; + struct acpi_video_device_brightness *br = NULL; + + ACPI_FUNCTION_TRACE("acpi_video_device_find_cap"); + + memset( &device->cap, 0, 4); + + if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_ADR", &h_dummy1))) { + device->cap._ADR = 1; + } + if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_BCL", &h_dummy1))) { + device->cap._BCL= 1; + } + if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_BCM", &h_dummy1))) { + device->cap._BCM= 1; + } + if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DDC", &h_dummy1))) { + device->cap._DDC= 1; + } + if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DCS", &h_dummy1))) { + device->cap._DCS = 1; + } + if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DGS", &h_dummy1))) { + device->cap._DGS = 1; + } + if( ACPI_SUCCESS(acpi_get_handle(device->handle, "_DSS", &h_dummy1))) { + device->cap._DSS = 1; + } + + status = acpi_video_device_lcd_query_levels(device, &obj); + + if (obj && obj->type == ACPI_TYPE_PACKAGE && obj->package.count >= 2) { + int count = 0; + union acpi_object *o; + + br = kmalloc(sizeof &br, GFP_KERNEL); + if (!br) { + printk(KERN_ERR "can't allocate memory\n"); + } else { + memset(br, 0, sizeof &br); + br->levels = kmalloc(obj->package.count * sizeof &br->levels, GFP_KERNEL); + if (!br->levels) + goto out; + + for (i = 0; i < obj->package.count; i++) { + o = (union acpi_object *) &obj->package.elements[i]; + if (o->type != ACPI_TYPE_INTEGER) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid data\n")); + continue; + } + br->levels[count] = (u32) o->integer.value; + count++; + } +out: + if (count < 2) { + if (br->levels) + kfree(br->levels); + kfree(br); + } else { + br->count = count; + device->brightness = br; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "found %d brightness levels\n", count)); + } + } + } + + if (obj) + kfree(obj); + + return_VOID; +} + +/* + * Arg: + * device : video output device (VGA) + * + * Return Value: + * None + * + * Find out all required AML method defined under the video bus device. + */ + +static void +acpi_video_bus_find_cap (struct acpi_video_bus *video) +{ + acpi_handle h_dummy1; + + memset(&video->cap ,0, 4); + if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_DOS", &h_dummy1))) { + video->cap._DOS = 1; + } + if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_DOD", &h_dummy1))) { + video->cap._DOD = 1; + } + if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_ROM", &h_dummy1))) { + video->cap._ROM = 1; + } + if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_GPD", &h_dummy1))) { + video->cap._GPD = 1; + } + if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_SPD", &h_dummy1))) { + video->cap._SPD = 1; + } + if( ACPI_SUCCESS(acpi_get_handle(video->handle, "_VPO", &h_dummy1))) { + video->cap._VPO = 1; + } +} + +/* + * Check whether the video bus device has required AML method to + * support the desired features + */ + +static int +acpi_video_bus_check ( + struct acpi_video_bus *video) +{ + acpi_status status = -ENOENT; + + + ACPI_FUNCTION_TRACE("acpi_video_bus_check"); + + if (!video) + return_VALUE(-EINVAL); + + /* Since there is no HID, CID and so on for VGA driver, we have + * to check well known required nodes. + */ + + /* Does this device able to support video switching ? */ + if(video->cap._DOS){ + video->flags.multihead = 1; + status = 0; + } + + /* Does this device able to retrieve a retrieve a video ROM ? */ + if(video->cap._ROM){ + video->flags.rom = 1; + status = 0; + } + + /* Does this device able to configure which video device to POST ? */ + if(video->cap._GPD && video->cap._SPD && video->cap._VPO){ + video->flags.post = 1; + status = 0; + } + + return_VALUE(status); +} + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ + +static struct proc_dir_entry *acpi_video_dir; + +/* video devices */ + +static int +acpi_video_device_info_seq_show ( + struct seq_file *seq, + void *offset) +{ + struct acpi_video_device *dev = (struct acpi_video_device *) seq->private; + + ACPI_FUNCTION_TRACE("acpi_video_device_info_seq_show"); + + if (!dev) + goto end; + + seq_printf(seq, "device_id: 0x%04x\n", (u32) dev->device_id); + seq_printf(seq, "type: "); + if (dev->flags.crt) + seq_printf(seq, "CRT\n"); + else if (dev->flags.lcd) + seq_printf(seq, "LCD\n"); + else if (dev->flags.tvout) + seq_printf(seq, "TVOUT\n"); + else + seq_printf(seq, "UNKNOWN\n"); + + seq_printf(seq,"known by bios: %s\n", + dev->flags.bios ? "yes":"no"); + +end: + return_VALUE(0); +} + +static int +acpi_video_device_info_open_fs ( + struct inode *inode, + struct file *file) +{ + return single_open(file, acpi_video_device_info_seq_show, + PDE(inode)->data); +} + +static int +acpi_video_device_state_seq_show ( + struct seq_file *seq, + void *offset) +{ + int status; + struct acpi_video_device *dev = (struct acpi_video_device *) seq->private; + unsigned long state; + + ACPI_FUNCTION_TRACE("acpi_video_device_state_seq_show"); + + if (!dev) + goto end; + + status = acpi_video_device_get_state(dev, &state); + seq_printf(seq, "state: "); + if (ACPI_SUCCESS(status)) + seq_printf(seq, "0x%02lx\n", state); + else + seq_printf(seq, "<not supported>\n"); + + status = acpi_video_device_query(dev, &state); + seq_printf(seq, "query: "); + if (ACPI_SUCCESS(status)) + seq_printf(seq, "0x%02lx\n", state); + else + seq_printf(seq, "<not supported>\n"); + +end: + return_VALUE(0); +} + +static int +acpi_video_device_state_open_fs ( + struct inode *inode, + struct file *file) +{ + return single_open(file, acpi_video_device_state_seq_show, + PDE(inode)->data); +} + +static ssize_t +acpi_video_device_write_state ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *data) +{ + int status; + struct seq_file *m = (struct seq_file *) file->private_data; + struct acpi_video_device *dev = (struct acpi_video_device *) m->private; + char str[12] = {0}; + u32 state = 0; + + ACPI_FUNCTION_TRACE("acpi_video_device_write_state"); + + if (!dev || count + 1 > sizeof str) + return_VALUE(-EINVAL); + + if (copy_from_user(str, buffer, count)) + return_VALUE(-EFAULT); + + str[count] = 0; + state = simple_strtoul(str, NULL, 0); + state &= ((1ul<<31) | (1ul<<30) | (1ul<<0)); + + status = acpi_video_device_set_state(dev, state); + + if (status) + return_VALUE(-EFAULT); + + return_VALUE(count); +} + +static int +acpi_video_device_brightness_seq_show ( + struct seq_file *seq, + void *offset) +{ + struct acpi_video_device *dev = (struct acpi_video_device *) seq->private; + int i; + + ACPI_FUNCTION_TRACE("acpi_video_device_brightness_seq_show"); + + if (!dev || !dev->brightness) { + seq_printf(seq, "<not supported>\n"); + return_VALUE(0); + } + + seq_printf(seq, "levels: "); + for (i = 0; i < dev->brightness->count; i++) + seq_printf(seq, " %d", dev->brightness->levels[i]); + seq_printf(seq, "\ncurrent: %d\n", dev->brightness->curr); + + return_VALUE(0); +} + +static int +acpi_video_device_brightness_open_fs ( + struct inode *inode, + struct file *file) +{ + return single_open(file, acpi_video_device_brightness_seq_show, + PDE(inode)->data); +} + +static ssize_t +acpi_video_device_write_brightness ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *data) +{ + struct seq_file *m = (struct seq_file *) file->private_data; + struct acpi_video_device *dev = (struct acpi_video_device *) m->private; + char str[4] = {0}; + unsigned int level = 0; + int i; + + ACPI_FUNCTION_TRACE("acpi_video_device_write_brightness"); + + if (!dev || count + 1 > sizeof str) + return_VALUE(-EINVAL); + + if (copy_from_user(str, buffer, count)) + return_VALUE(-EFAULT); + + str[count] = 0; + level = simple_strtoul(str, NULL, 0); + + if (level > 100) + return_VALUE(-EFAULT); + + /* validate though the list of available levels */ + for (i = 0; i < dev->brightness->count; i++) + if (level == dev->brightness->levels[i]) { + if (ACPI_SUCCESS(acpi_video_device_lcd_set_level(dev, level))) + dev->brightness->curr = level; + break; + } + + return_VALUE(count); +} + +static int +acpi_video_device_EDID_seq_show ( + struct seq_file *seq, + void *offset) +{ + struct acpi_video_device *dev = (struct acpi_video_device *) seq->private; + int status; + int i; + union acpi_object *edid = NULL; + + ACPI_FUNCTION_TRACE("acpi_video_device_EDID_seq_show"); + + if (!dev) + goto out; + + status = acpi_video_device_EDID (dev, &edid, 128); + if (ACPI_FAILURE(status)) { + status = acpi_video_device_EDID (dev, &edid, 256); + } + + if (ACPI_FAILURE(status)) { + goto out; + } + + if (edid && edid->type == ACPI_TYPE_BUFFER) { + for (i = 0; i < edid->buffer.length; i++) + seq_putc(seq, edid->buffer.pointer[i]); + } + +out: + if (!edid) + seq_printf(seq, "<not supported>\n"); + else + kfree(edid); + + return_VALUE(0); +} + +static int +acpi_video_device_EDID_open_fs ( + struct inode *inode, + struct file *file) +{ + return single_open(file, acpi_video_device_EDID_seq_show, + PDE(inode)->data); +} + + +static int +acpi_video_device_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + struct acpi_video_device *vid_dev; + + ACPI_FUNCTION_TRACE("acpi_video_device_add_fs"); + + if (!device) + return_VALUE(-ENODEV); + + vid_dev = (struct acpi_video_device *) acpi_driver_data(device); + if (!vid_dev) + return_VALUE(-ENODEV); + + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + vid_dev->video->dir); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + acpi_device_dir(device)->owner = THIS_MODULE; + } + + /* 'info' [R] */ + entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create 'info' fs entry\n")); + else { + entry->proc_fops = &acpi_video_device_info_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'state' [R/W] */ + entry = create_proc_entry("state", S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create 'state' fs entry\n")); + else { + entry->proc_fops = &acpi_video_device_state_fops; + entry->proc_fops->write = acpi_video_device_write_state; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'brightness' [R/W] */ + entry = create_proc_entry("brightness", S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create 'brightness' fs entry\n")); + else { + entry->proc_fops = &acpi_video_device_brightness_fops; + entry->proc_fops->write = acpi_video_device_write_brightness; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'EDID' [R] */ + entry = create_proc_entry("EDID", S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Unable to create 'brightness' fs entry\n")); + else { + entry->proc_fops = &acpi_video_device_EDID_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + return_VALUE(0); +} + +static int +acpi_video_device_remove_fs ( + struct acpi_device *device) +{ + struct acpi_video_device *vid_dev; + ACPI_FUNCTION_TRACE("acpi_video_device_remove_fs"); + + vid_dev = (struct acpi_video_device *) acpi_driver_data(device); + if (!vid_dev || !vid_dev->video || !vid_dev->video->dir) + return_VALUE(-ENODEV); + + if (acpi_device_dir(device)) { + remove_proc_entry("info", acpi_device_dir(device)); + remove_proc_entry("state", acpi_device_dir(device)); + remove_proc_entry("brightness", acpi_device_dir(device)); + remove_proc_entry("EDID", acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), + vid_dev->video->dir); + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + + +/* video bus */ +static int +acpi_video_bus_info_seq_show ( + struct seq_file *seq, + void *offset) +{ + struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; + + ACPI_FUNCTION_TRACE("acpi_video_bus_info_seq_show"); + + if (!video) + goto end; + + seq_printf(seq, "Switching heads: %s\n", + video->flags.multihead ? "yes":"no"); + seq_printf(seq, "Video ROM: %s\n", + video->flags.rom ? "yes":"no"); + seq_printf(seq, "Device to be POSTed on boot: %s\n", + video->flags.post ? "yes":"no"); + +end: + return_VALUE(0); +} + +static int +acpi_video_bus_info_open_fs ( + struct inode *inode, + struct file *file) +{ + return single_open(file, acpi_video_bus_info_seq_show, PDE(inode)->data); +} + +static int +acpi_video_bus_ROM_seq_show ( + struct seq_file *seq, + void *offset) +{ + struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; + + ACPI_FUNCTION_TRACE("acpi_video_bus_ROM_seq_show"); + + if (!video) + goto end; + + printk(KERN_INFO PREFIX "Please implement %s\n", __FUNCTION__); + seq_printf(seq, "<TODO>\n"); + +end: + return_VALUE(0); +} + +static int +acpi_video_bus_ROM_open_fs ( + struct inode *inode, + struct file *file) +{ + return single_open(file, acpi_video_bus_ROM_seq_show, PDE(inode)->data); +} + +static int +acpi_video_bus_POST_info_seq_show ( + struct seq_file *seq, + void *offset) +{ + struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; + unsigned long options; + int status; + + ACPI_FUNCTION_TRACE("acpi_video_bus_POST_info_seq_show"); + + if (!video) + goto end; + + status = acpi_video_bus_POST_options(video, &options); + if (ACPI_SUCCESS(status)) { + if (!(options & 1)) { + printk(KERN_WARNING PREFIX "The motherboard VGA device is not listed as a possible POST device.\n"); + printk(KERN_WARNING PREFIX "This indicate a BIOS bug. Please contact the manufacturer.\n"); + } + printk("%lx\n", options); + seq_printf(seq, "can POST: <intgrated video>"); + if (options & 2) + seq_printf(seq, " <PCI video>"); + if (options & 4) + seq_printf(seq, " <AGP video>"); + seq_putc(seq, '\n'); + } else + seq_printf(seq, "<not supported>\n"); +end: + return_VALUE(0); +} + +static int +acpi_video_bus_POST_info_open_fs ( + struct inode *inode, + struct file *file) +{ + return single_open(file, acpi_video_bus_POST_info_seq_show, PDE(inode)->data); +} + +static int +acpi_video_bus_POST_seq_show ( + struct seq_file *seq, + void *offset) +{ + struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; + int status; + unsigned long id; + + ACPI_FUNCTION_TRACE("acpi_video_bus_POST_seq_show"); + + if (!video) + goto end; + + status = acpi_video_bus_get_POST (video, &id); + if (!ACPI_SUCCESS(status)) { + seq_printf(seq, "<not supported>\n"); + goto end; + } + seq_printf(seq, "device posted is <%s>\n", device_decode[id & 3]); + +end: + return_VALUE(0); +} + +static int +acpi_video_bus_DOS_seq_show ( + struct seq_file *seq, + void *offset) +{ + struct acpi_video_bus *video = (struct acpi_video_bus *) seq->private; + + ACPI_FUNCTION_TRACE("acpi_video_bus_DOS_seq_show"); + + seq_printf(seq, "DOS setting: <%d>\n", video->dos_setting ); + + return_VALUE(0); +} + +static int +acpi_video_bus_POST_open_fs ( + struct inode *inode, + struct file *file) +{ + return single_open(file, acpi_video_bus_POST_seq_show, PDE(inode)->data); +} + +static int +acpi_video_bus_DOS_open_fs ( + struct inode *inode, + struct file *file) +{ + return single_open(file, acpi_video_bus_DOS_seq_show, PDE(inode)->data); +} + +static ssize_t +acpi_video_bus_write_POST ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *data) +{ + int status; + struct seq_file *m = (struct seq_file *) file->private_data; + struct acpi_video_bus *video = (struct acpi_video_bus *) m->private; + char str[12] = {0}; + unsigned long opt, options; + + ACPI_FUNCTION_TRACE("acpi_video_bus_write_POST"); + + + if (!video || count + 1 > sizeof str) + return_VALUE(-EINVAL); + + status = acpi_video_bus_POST_options(video, &options); + if (!ACPI_SUCCESS(status)) + return_VALUE(-EINVAL); + + if (copy_from_user(str, buffer, count)) + return_VALUE(-EFAULT); + + str[count] = 0; + opt = strtoul(str, NULL, 0); + if (opt > 3) + return_VALUE(-EFAULT); + + /* just in case an OEM 'forget' the motherboard... */ + options |= 1; + + if (options & (1ul << opt)) { + status = acpi_video_bus_set_POST (video, opt); + if (!ACPI_SUCCESS(status)) + return_VALUE(-EFAULT); + + } + + + return_VALUE(count); +} + +static ssize_t +acpi_video_bus_write_DOS ( + struct file *file, + const char __user *buffer, + size_t count, + loff_t *data) +{ + int status; + struct seq_file *m = (struct seq_file *) file->private_data; + struct acpi_video_bus *video = (struct acpi_video_bus *) m->private; + char str[12] = {0}; + unsigned long opt; + + ACPI_FUNCTION_TRACE("acpi_video_bus_write_DOS"); + + + if (!video || count + 1 > sizeof str) + return_VALUE(-EINVAL); + + if (copy_from_user(str, buffer, count)) + return_VALUE(-EFAULT); + + str[count] = 0; + opt = strtoul(str, NULL, 0); + if (opt > 7) + return_VALUE(-EFAULT); + + status = acpi_video_bus_DOS (video, opt & 0x3, (opt & 0x4)>>2); + + if (!ACPI_SUCCESS(status)) + return_VALUE(-EFAULT); + + return_VALUE(count); +} + +static int +acpi_video_bus_add_fs ( + struct acpi_device *device) +{ + struct proc_dir_entry *entry = NULL; + struct acpi_video_bus *video; + + ACPI_FUNCTION_TRACE("acpi_video_bus_add_fs"); + + video = (struct acpi_video_bus *) acpi_driver_data(device); + + if (!acpi_device_dir(device)) { + acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), + acpi_video_dir); + if (!acpi_device_dir(device)) + return_VALUE(-ENODEV); + video->dir = acpi_device_dir(device); + acpi_device_dir(device)->owner = THIS_MODULE; + } + + /* 'info' [R] */ + entry = create_proc_entry("info", S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'info' fs entry\n")); + else { + entry->proc_fops = &acpi_video_bus_info_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'ROM' [R] */ + entry = create_proc_entry("ROM", S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'ROM' fs entry\n")); + else { + entry->proc_fops = &acpi_video_bus_ROM_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'POST_info' [R] */ + entry = create_proc_entry("POST_info", S_IRUGO, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'POST_info' fs entry\n")); + else { + entry->proc_fops = &acpi_video_bus_POST_info_fops; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'POST' [R/W] */ + entry = create_proc_entry("POST", S_IFREG|S_IRUGO|S_IRUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'POST' fs entry\n")); + else { + entry->proc_fops = &acpi_video_bus_POST_fops; + entry->proc_fops->write = acpi_video_bus_write_POST; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + /* 'DOS' [R/W] */ + entry = create_proc_entry("DOS", S_IFREG|S_IRUGO|S_IRUSR, acpi_device_dir(device)); + if (!entry) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unable to create 'DOS' fs entry\n")); + else { + entry->proc_fops = &acpi_video_bus_DOS_fops; + entry->proc_fops->write = acpi_video_bus_write_DOS; + entry->data = acpi_driver_data(device); + entry->owner = THIS_MODULE; + } + + return_VALUE(0); +} + +static int +acpi_video_bus_remove_fs ( + struct acpi_device *device) +{ + struct acpi_video_bus *video; + + ACPI_FUNCTION_TRACE("acpi_video_bus_remove_fs"); + + video = (struct acpi_video_bus *) acpi_driver_data(device); + + if (acpi_device_dir(device)) { + remove_proc_entry("info", acpi_device_dir(device)); + remove_proc_entry("ROM", acpi_device_dir(device)); + remove_proc_entry("POST_info", acpi_device_dir(device)); + remove_proc_entry("POST", acpi_device_dir(device)); + remove_proc_entry("DOS", acpi_device_dir(device)); + remove_proc_entry(acpi_device_bid(device), + acpi_video_dir); + acpi_device_dir(device) = NULL; + } + + return_VALUE(0); +} + +/* -------------------------------------------------------------------------- + Driver Interface + -------------------------------------------------------------------------- */ + +/* device interface */ + +static int +acpi_video_bus_get_one_device ( + struct acpi_device *device, + struct acpi_video_bus *video) +{ + unsigned long device_id; + int status, result; + struct acpi_video_device *data; + + ACPI_FUNCTION_TRACE("acpi_video_bus_get_one_device"); + + if (!device || !video) + return_VALUE(-EINVAL); + + status = acpi_evaluate_integer(device->handle, "_ADR", NULL, &device_id); + if (ACPI_SUCCESS(status)) { + + data = kmalloc(sizeof(struct acpi_video_device), GFP_KERNEL); + if (!data) + return_VALUE(-ENOMEM); + + memset(data, 0, sizeof(struct acpi_video_device)); + + data->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_VIDEO_DEVICE_NAME); + strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); + acpi_driver_data(device) = data; + + data->device_id = device_id; + data->video = video; + data->dev = device; + + switch (device_id & 0xffff) { + case 0x0100: + data->flags.crt = 1; + break; + case 0x0400: + data->flags.lcd = 1; + break; + case 0x0200: + data->flags.tvout = 1; + break; + default: + data->flags.unknown = 1; + break; + } + + acpi_video_device_bind(video, data); + acpi_video_device_find_cap(data); + + status = acpi_install_notify_handler(data->handle, + ACPI_DEVICE_NOTIFY, acpi_video_device_notify, data); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing notify handler\n")); + result = -ENODEV; + goto end; + } + + down(&video->sem); + list_add_tail(&data->entry, &video->video_device_list); + up(&video->sem); + + acpi_video_device_add_fs(device); + + return_VALUE(0); + } + +end: + return_VALUE(-ENOENT); +} + +/* + * Arg: + * video : video bus device + * + * Return: + * none + * + * Enumerate the video device list of the video bus, + * bind the ids with the corresponding video devices + * under the video bus. + */ + +static void +acpi_video_device_rebind( struct acpi_video_bus *video) +{ + struct list_head * node, * next; + list_for_each_safe(node, next, &video->video_device_list) { + struct acpi_video_device * dev = container_of(node, struct acpi_video_device, entry); + acpi_video_device_bind( video, dev); + } +} + +/* + * Arg: + * video : video bus device + * device : video output device under the video + * bus + * + * Return: + * none + * + * Bind the ids with the corresponding video devices + * under the video bus. + */ + +static void +acpi_video_device_bind( struct acpi_video_bus *video, + struct acpi_video_device *device) +{ + int i; + ACPI_FUNCTION_TRACE("acpi_video_device_bind"); + +#define IDS_VAL(i) video->attached_array[i].value.int_val +#define IDS_BIND(i) video->attached_array[i].bind_info + + for (i = 0; IDS_VAL(i) != ACPI_VIDEO_HEAD_INVALID && + i < video->attached_count; i++) { + if (device->device_id == (IDS_VAL(i)& 0xffff)) { + IDS_BIND(i) = device; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "device_bind %d\n", i)); + } + } +#undef IDS_VAL +#undef IDS_BIND +} + +/* + * Arg: + * video : video bus device + * + * Return: + * < 0 : error + * + * Call _DOD to enumerate all devices attached to display adapter + * + */ + +static int acpi_video_device_enumerate(struct acpi_video_bus *video) +{ + int status; + int count; + int i; + struct acpi_video_enumerated_device *active_device_list; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *dod = NULL; + union acpi_object *obj; + + ACPI_FUNCTION_TRACE("acpi_video_device_enumerate"); + + status = acpi_evaluate_object(video->handle, "_DOD", NULL, &buffer); + if (!ACPI_SUCCESS(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _DOD\n")); + return_VALUE(status); + } + + dod = (union acpi_object *) buffer.pointer; + if (!dod || (dod->type != ACPI_TYPE_PACKAGE)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _DOD data\n")); + status = -EFAULT; + goto out; + } + + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found %d video heads in _DOD\n", + dod->package.count)); + + active_device_list= kmalloc( + (1+dod->package.count)*sizeof(struct acpi_video_enumerated_device), + GFP_KERNEL); + + if (!active_device_list) { + status = -ENOMEM; + goto out; + } + + count = 0; + for (i = 0; i < dod->package.count; i++) { + obj = (union acpi_object *) &dod->package.elements[i]; + + if (obj->type != ACPI_TYPE_INTEGER) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Invalid _DOD data\n")); + active_device_list[i].value.int_val = ACPI_VIDEO_HEAD_INVALID; + } + active_device_list[i].value.int_val = obj->integer.value; + active_device_list[i].bind_info = NULL; + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "dod element[%d] = %d\n", i, (int) obj->integer.value)); + count++; + } + active_device_list[count].value.int_val = ACPI_VIDEO_HEAD_END; + + if(video->attached_array) + kfree(video->attached_array); + + video->attached_array = active_device_list; + video->attached_count = count; +out: + acpi_os_free(buffer.pointer); + return_VALUE(status); +} + +/* + * Arg: + * video : video bus device + * event : Nontify Event + * + * Return: + * < 0 : error + * + * 1. Find out the current active output device. + * 2. Identify the next output device to switch + * 3. call _DSS to do actual switch. + */ + +static int +acpi_video_switch_output( + struct acpi_video_bus *video, + int event) +{ + struct list_head * node, * next; + struct acpi_video_device *dev=NULL; + struct acpi_video_device *dev_next=NULL; + struct acpi_video_device *dev_prev=NULL; + unsigned long state; + int status = 0; + + ACPI_FUNCTION_TRACE("acpi_video_switch_output"); + + list_for_each_safe(node, next, &video->video_device_list) { + struct acpi_video_device * dev = container_of(node, struct acpi_video_device, entry); + status = acpi_video_device_get_state(dev, &state); + if (state & 0x2){ + dev_next = container_of(node->next, struct acpi_video_device, entry); + dev_prev = container_of(node->prev, struct acpi_video_device, entry); + goto out; + } + } + dev_next = container_of(node->next, struct acpi_video_device, entry); + dev_prev = container_of(node->prev, struct acpi_video_device, entry); +out: + switch (event) { + case ACPI_VIDEO_NOTIFY_CYCLE: + case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: + acpi_video_device_set_state(dev, 0); + acpi_video_device_set_state(dev_next, 0x80000001); + break; + case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: + acpi_video_device_set_state(dev, 0); + acpi_video_device_set_state(dev_prev, 0x80000001); + default: + break; + } + + return_VALUE(status); +} + +static int +acpi_video_get_next_level( + struct acpi_video_device *device, + u32 level_current, + u32 event) +{ + /*Fix me*/ + return level_current; +} + + +static void +acpi_video_switch_brightness ( + struct acpi_video_device *device, + int event) +{ + unsigned long level_current, level_next; + acpi_video_device_lcd_get_level_current(device, &level_current); + level_next = acpi_video_get_next_level(device, level_current, event); + acpi_video_device_lcd_set_level(device, level_next); +} + +static int +acpi_video_bus_get_devices ( + struct acpi_video_bus *video, + struct acpi_device *device) +{ + int status = 0; + struct list_head *node, *next; + + ACPI_FUNCTION_TRACE("acpi_video_get_devices"); + + acpi_video_device_enumerate(video); + + list_for_each_safe(node, next, &device->children) { + struct acpi_device *dev = list_entry(node, struct acpi_device, node); + + if (!dev) + continue; + + status = acpi_video_bus_get_one_device(dev, video); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Cant attach device\n")); + continue; + } + + } + return_VALUE(status); +} + +static int +acpi_video_bus_put_one_device( + struct acpi_video_device *device) +{ + struct acpi_video_bus *video; + + ACPI_FUNCTION_TRACE("acpi_video_bus_put_one_device"); + + if (!device || !device->video) + return_VALUE(-ENOENT); + + video = device->video; + + down(&video->sem); + list_del(&device->entry); + up(&video->sem); + acpi_video_device_remove_fs(device->dev); + + return_VALUE(0); +} + +static int +acpi_video_bus_put_devices ( + struct acpi_video_bus *video) +{ + int status; + struct list_head *node, *next; + + ACPI_FUNCTION_TRACE("acpi_video_bus_put_devices"); + + list_for_each_safe(node, next, &video->video_device_list) { + struct acpi_video_device *data = list_entry(node, struct acpi_video_device, entry); + if (!data) + continue; + + status = acpi_video_bus_put_one_device(data); + if(ACPI_FAILURE(status)) + printk(KERN_WARNING PREFIX "hhuuhhuu bug in acpi video driver.\n"); + + if (data->brightness) + kfree(data->brightness); + + kfree(data); + } + + return_VALUE(0); +} + +/* acpi_video interface */ + +static int +acpi_video_bus_start_devices( + struct acpi_video_bus *video) +{ + return acpi_video_bus_DOS(video, 1, 0); +} + +static int +acpi_video_bus_stop_devices( + struct acpi_video_bus *video) +{ + return acpi_video_bus_DOS(video, 0, 1); +} + +static void +acpi_video_bus_notify ( + acpi_handle handle, + u32 event, + void *data) +{ + struct acpi_video_bus *video = (struct acpi_video_bus *) data; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_video_bus_notify"); + printk("video bus notify\n"); + + if (!video) + return_VOID; + + if (acpi_bus_get_device(handle, &device)) + return_VOID; + + switch (event) { + case ACPI_VIDEO_NOTIFY_SWITCH: /* User request that a switch occur, + * most likely via hotkey. */ + acpi_bus_generate_event(device, event, 0); + break; + + case ACPI_VIDEO_NOTIFY_PROBE: /* User plug or remove a video + * connector. */ + acpi_video_device_enumerate(video); + acpi_video_device_rebind(video); + acpi_video_switch_output(video, event); + acpi_bus_generate_event(device, event, 0); + break; + + case ACPI_VIDEO_NOTIFY_CYCLE: /* Cycle Display output hotkey pressed.*/ + case ACPI_VIDEO_NOTIFY_NEXT_OUTPUT: /* Next Display output hotkey pressed. */ + case ACPI_VIDEO_NOTIFY_PREV_OUTPUT: /* previous Display output hotkey pressed. */ + acpi_video_switch_output(video, event); + acpi_bus_generate_event(device, event, 0); + break; + + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + + return_VOID; +} + +static void +acpi_video_device_notify ( + acpi_handle handle, + u32 event, + void *data) +{ + struct acpi_video_device *video_device = (struct acpi_video_device *) data; + struct acpi_device *device = NULL; + + ACPI_FUNCTION_TRACE("acpi_video_device_notify"); + + printk("video device notify\n"); + if (!video_device) + return_VOID; + + if (acpi_bus_get_device(handle, &device)) + return_VOID; + + switch (event) { + case ACPI_VIDEO_NOTIFY_SWITCH: /* change in status (cycle output device) */ + case ACPI_VIDEO_NOTIFY_PROBE: /* change in status (output device status) */ + acpi_bus_generate_event(device, event, 0); + break; + case ACPI_VIDEO_NOTIFY_CYCLE_BRIGHTNESS: /* Cycle brightness */ + case ACPI_VIDEO_NOTIFY_INC_BRIGHTNESS: /* Increase brightness */ + case ACPI_VIDEO_NOTIFY_DEC_BRIGHTNESS: /* Decrease brightness */ + case ACPI_VIDEO_NOTIFY_ZERO_BRIGHTNESS: /* zero brightnesss */ + case ACPI_VIDEO_NOTIFY_DISPLAY_OFF: /* display device off */ + acpi_video_switch_brightness (video_device, event); + acpi_bus_generate_event(device, event, 0); + break; + default: + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Unsupported event [0x%x]\n", event)); + break; + } + return_VOID; +} + +static int +acpi_video_bus_add ( + struct acpi_device *device) +{ + int result = 0; + acpi_status status = 0; + struct acpi_video_bus *video = NULL; + + ACPI_FUNCTION_TRACE("acpi_video_bus_add"); + + if (!device) + return_VALUE(-EINVAL); + + video = kmalloc(sizeof(struct acpi_video_bus), GFP_KERNEL); + if (!video) + return_VALUE(-ENOMEM); + memset(video, 0, sizeof(struct acpi_video_bus)); + + video->handle = device->handle; + strcpy(acpi_device_name(device), ACPI_VIDEO_BUS_NAME); + strcpy(acpi_device_class(device), ACPI_VIDEO_CLASS); + acpi_driver_data(device) = video; + + acpi_video_bus_find_cap(video); + result = acpi_video_bus_check(video); + if (result) + goto end; + + result = acpi_video_bus_add_fs(device); + if (result) + goto end; + + init_MUTEX(&video->sem); + INIT_LIST_HEAD(&video->video_device_list); + + acpi_video_bus_get_devices(video, device); + acpi_video_bus_start_devices(video); + + status = acpi_install_notify_handler(video->handle, + ACPI_DEVICE_NOTIFY, acpi_video_bus_notify, video); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error installing notify handler\n")); + result = -ENODEV; + goto end; + } + + printk(KERN_INFO PREFIX "%s [%s] (multi-head: %s rom: %s post: %s)\n", + ACPI_VIDEO_DEVICE_NAME, acpi_device_bid(device), + video->flags.multihead ? "yes":"no", + video->flags.rom ? "yes":"no", + video->flags.post ? "yes":"no"); + +end: + if (result) { + acpi_video_bus_remove_fs(device); + kfree(video); + } + + return_VALUE(result); +} + +static int +acpi_video_bus_remove ( + struct acpi_device *device, + int type) +{ + acpi_status status = 0; + struct acpi_video_bus *video = NULL; + + ACPI_FUNCTION_TRACE("acpi_video_bus_remove"); + + if (!device || !acpi_driver_data(device)) + return_VALUE(-EINVAL); + + video = (struct acpi_video_bus *) acpi_driver_data(device); + + acpi_video_bus_stop_devices(video); + + status = acpi_remove_notify_handler(video->handle, + ACPI_DEVICE_NOTIFY, acpi_video_bus_notify); + if (ACPI_FAILURE(status)) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, + "Error removing notify handler\n")); + + acpi_video_bus_put_devices(video); + acpi_video_bus_remove_fs(device); + + if (video->attached_array) + kfree(video->attached_array); + kfree(video); + + return_VALUE(0); +} + + +static int +acpi_video_bus_match ( + struct acpi_device *device, + struct acpi_driver *driver) +{ + acpi_handle h_dummy1; + acpi_handle h_dummy2; + acpi_handle h_dummy3; + + ACPI_FUNCTION_TRACE("acpi_video_bus_match"); + + if (!device || !driver) + return_VALUE(-EINVAL); + + /* Since there is no HID, CID for ACPI Video drivers, we have + * to check well known required nodes for each feature we support. + */ + + /* Does this device able to support video switching ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy1)) && + ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy2))) + return_VALUE(0); + + /* Does this device able to retrieve a video ROM ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy1))) + return_VALUE(0); + + /* Does this device able to configure which video head to be POSTed ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy1)) && + ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy2)) && + ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy3))) + return_VALUE(0); + + + return_VALUE(-ENODEV); +} + + +static int __init +acpi_video_init (void) +{ + int result = 0; + + ACPI_FUNCTION_TRACE("acpi_video_init"); + + /* + acpi_dbg_level = 0xFFFFFFFF; + acpi_dbg_layer = 0x08000000; + */ + + acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); + if (!acpi_video_dir) + return_VALUE(-ENODEV); + acpi_video_dir->owner = THIS_MODULE; + + result = acpi_bus_register_driver(&acpi_video_bus); + if (result < 0) { + remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); + return_VALUE(-ENODEV); + } + + return_VALUE(0); +} + +static void __exit +acpi_video_exit (void) +{ + ACPI_FUNCTION_TRACE("acpi_video_exit"); + + acpi_bus_unregister_driver(&acpi_video_bus); + + remove_proc_entry(ACPI_VIDEO_CLASS, acpi_root_dir); + + return_VOID; +} + +module_init(acpi_video_init); +module_exit(acpi_video_exit); |