diff options
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/Kconfig | 37 | ||||
-rw-r--r-- | drivers/acpi/Makefile | 5 | ||||
-rw-r--r-- | drivers/acpi/battery.c | 4 | ||||
-rw-r--r-- | drivers/acpi/bay.c | 490 | ||||
-rw-r--r-- | drivers/acpi/bus.c | 2 | ||||
-rw-r--r-- | drivers/acpi/button.c | 2 | ||||
-rw-r--r-- | drivers/acpi/container.c | 6 | ||||
-rw-r--r-- | drivers/acpi/debug.c | 62 | ||||
-rw-r--r-- | drivers/acpi/dock.c | 16 | ||||
-rw-r--r-- | drivers/acpi/fan.c | 8 | ||||
-rw-r--r-- | drivers/acpi/glue.c | 123 | ||||
-rw-r--r-- | drivers/acpi/motherboard.c | 191 | ||||
-rw-r--r-- | drivers/acpi/osl.c | 48 | ||||
-rw-r--r-- | drivers/acpi/pci_root.c | 38 | ||||
-rw-r--r-- | drivers/acpi/processor_core.c | 8 | ||||
-rw-r--r-- | drivers/acpi/scan.c | 1261 | ||||
-rw-r--r-- | drivers/acpi/system.c | 24 | ||||
-rw-r--r-- | drivers/acpi/thermal.c | 4 | ||||
-rw-r--r-- | drivers/acpi/video.c | 166 |
19 files changed, 1419 insertions, 1076 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index f4f000abc4e..20eacc2c9e0 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -3,6 +3,7 @@ # menu "ACPI (Advanced Configuration and Power Interface) Support" + depends on !X86_NUMAQ depends on !X86_VISWS depends on !IA64_HP_SIM depends on IA64 || X86 @@ -77,6 +78,20 @@ config ACPI_SLEEP_PROC_SLEEP Create /proc/acpi/sleep Deprecated by /sys/power/state +config ACPI_PROCFS + bool "Procfs interface (deprecated)" + depends on ACPI + default y + ---help--- + Procfs interface for ACPI is made optional for back-compatible. + As the same functions are duplicated in sysfs interface + and this proc interface will be removed some time later, + it's marked as deprecated. + ( /proc/acpi/debug_layer && debug_level are deprecated by + /sys/module/acpi/parameters/debug_layer && debug_level. + /proc/acpi/info is deprecated by + /sys/module/acpi/parameters/acpica_version ) + config ACPI_AC tristate "AC Adapter" depends on X86 @@ -107,7 +122,7 @@ config ACPI_BUTTON config ACPI_VIDEO tristate "Video" - depends on X86 + depends on X86 && BACKLIGHT_CLASS_DEVICE help This driver implement the ACPI Extensions For Display Adapters for integrated graphics devices on motherboard, as specified in @@ -139,6 +154,13 @@ config ACPI_DOCK help This driver adds support for ACPI controlled docking stations +config ACPI_BAY + tristate "Removable Drive Bay (EXPERIMENTAL)" + depends on EXPERIMENTAL + help + This driver adds support for ACPI controlled removable drive + bays such as the IBM ultrabay or the Dell Module Bay. + config ACPI_PROCESSOR tristate "Processor" default y @@ -186,19 +208,22 @@ config ACPI_ASUS 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) - + available on the above page (acpi4asus-user@lists.sourceforge.net). + + NOTE: This driver is deprecated and will probably be removed soon, + use asus-laptop instead. + config ACPI_IBM tristate "IBM ThinkPad Laptop Extras" depends on X86 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index bce7ca27b42..856c32bccac 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -37,13 +37,15 @@ endif obj-y += sleep/ obj-y += bus.o glue.o +obj-y += scan.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_DOCK) += dock.o -obj-$(CONFIG_ACPI_VIDEO) += video.o +obj-$(CONFIG_ACPI_BAY) += bay.o +obj-$(CONFIG_ACPI_VIDEO) += video.o obj-$(CONFIG_ACPI_HOTKEY) += hotkey.o obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o obj-$(CONFIG_ACPI_POWER) += power.o @@ -56,7 +58,6 @@ 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-y += scan.o motherboard.o obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o obj-y += cm_sbs.o obj-$(CONFIG_ACPI_SBS) += i2c_ec.o sbs.o diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index 5f43e0d1489..2f4521a48fe 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -64,7 +64,7 @@ extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir); static int acpi_battery_add(struct acpi_device *device); static int acpi_battery_remove(struct acpi_device *device, int type); -static int acpi_battery_resume(struct acpi_device *device, int status); +static int acpi_battery_resume(struct acpi_device *device); static struct acpi_driver acpi_battery_driver = { .name = ACPI_BATTERY_DRIVER_NAME, @@ -753,7 +753,7 @@ static int acpi_battery_remove(struct acpi_device *device, int type) } /* this is needed to learn about changes made in suspended state */ -static int acpi_battery_resume(struct acpi_device *device, int state) +static int acpi_battery_resume(struct acpi_device *device) { struct acpi_battery *battery; diff --git a/drivers/acpi/bay.c b/drivers/acpi/bay.c new file mode 100644 index 00000000000..667fa1dfa1a --- /dev/null +++ b/drivers/acpi/bay.c @@ -0,0 +1,490 @@ +/* + * bay.c - ACPI removable drive bay driver + * + * Copyright (C) 2006 Kristen Carlson Accardi <kristen.c.accardi@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/notifier.h> +#include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> +#include <linux/seq_file.h> +#include <asm/uaccess.h> +#include <linux/platform_device.h> + +#define ACPI_BAY_DRIVER_NAME "ACPI Removable Drive Bay Driver" + +ACPI_MODULE_NAME("bay") +MODULE_AUTHOR("Kristen Carlson Accardi"); +MODULE_DESCRIPTION(ACPI_BAY_DRIVER_NAME); +MODULE_LICENSE("GPL"); +#define ACPI_BAY_CLASS "bay" +#define ACPI_BAY_COMPONENT 0x10000000 +#define _COMPONENT ACPI_BAY_COMPONENT +#define bay_dprintk(h,s) {\ + char prefix[80] = {'\0'};\ + struct acpi_buffer buffer = {sizeof(prefix), prefix};\ + acpi_get_name(h, ACPI_FULL_PATHNAME, &buffer);\ + printk(KERN_DEBUG PREFIX "%s: %s\n", prefix, s); } +static void bay_notify(acpi_handle handle, u32 event, void *data); +static int acpi_bay_add(struct acpi_device *device); +static int acpi_bay_remove(struct acpi_device *device, int type); + +static struct acpi_driver acpi_bay_driver = { + .name = ACPI_BAY_DRIVER_NAME, + .class = ACPI_BAY_CLASS, + .ids = ACPI_BAY_HID, + .ops = { + .add = acpi_bay_add, + .remove = acpi_bay_remove, + }, +}; + +struct bay { + acpi_handle handle; + char *name; + struct list_head list; + struct platform_device *pdev; +}; + +static LIST_HEAD(drive_bays); + + +/***************************************************************************** + * Drive Bay functions * + *****************************************************************************/ +/** + * is_ejectable - see if a device is ejectable + * @handle: acpi handle of the device + * + * If an acpi object has a _EJ0 method, then it is ejectable + */ +static int is_ejectable(acpi_handle handle) +{ + acpi_status status; + acpi_handle tmp; + + status = acpi_get_handle(handle, "_EJ0", &tmp); + if (ACPI_FAILURE(status)) + return 0; + return 1; +} + +/** + * bay_present - see if the bay device is present + * @bay: the drive bay + * + * execute the _STA method. + */ +static int bay_present(struct bay *bay) +{ + unsigned long sta; + acpi_status status; + + if (bay) { + status = acpi_evaluate_integer(bay->handle, "_STA", NULL, &sta); + if (ACPI_SUCCESS(status) && sta) + return 1; + } + return 0; +} + +/** + * eject_device - respond to an eject request + * @handle - the device to eject + * + * Call this devices _EJ0 method. + */ +static void eject_device(acpi_handle handle) +{ + struct acpi_object_list arg_list; + union acpi_object arg; + + bay_dprintk(handle, "Ejecting device"); + + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = 1; + + if (ACPI_FAILURE(acpi_evaluate_object(handle, "_EJ0", + &arg_list, NULL))) + pr_debug("Failed to evaluate _EJ0!\n"); +} + +/* + * show_present - read method for "present" file in sysfs + */ +static ssize_t show_present(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct bay *bay = dev_get_drvdata(dev); + return snprintf(buf, PAGE_SIZE, "%d\n", bay_present(bay)); + +} +DEVICE_ATTR(present, S_IRUGO, show_present, NULL); + +/* + * write_eject - write method for "eject" file in sysfs + */ +static ssize_t write_eject(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct bay *bay = dev_get_drvdata(dev); + + if (!count) + return -EINVAL; + + eject_device(bay->handle); + return count; +} +DEVICE_ATTR(eject, S_IWUSR, NULL, write_eject); + +/** + * is_ata - see if a device is an ata device + * @handle: acpi handle of the device + * + * If an acpi object has one of 4 ATA ACPI methods defined, + * then it is an ATA device + */ +static int is_ata(acpi_handle handle) +{ + acpi_handle tmp; + + if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) || + (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) || + (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) || + (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp)))) + return 1; + + return 0; +} + +/** + * parent_is_ata(acpi_handle handle) + * + */ +static int parent_is_ata(acpi_handle handle) +{ + acpi_handle phandle; + + if (acpi_get_parent(handle, &phandle)) + return 0; + + return is_ata(phandle); +} + +/** + * is_ejectable_bay - see if a device is an ejectable drive bay + * @handle: acpi handle of the device + * + * If an acpi object is ejectable and has one of the ACPI ATA + * methods defined, then we can safely call it an ejectable + * drive bay + */ +static int is_ejectable_bay(acpi_handle handle) +{ + if ((is_ata(handle) || parent_is_ata(handle)) && is_ejectable(handle)) + return 1; + return 0; +} + +/** + * eject_removable_drive - try to eject this drive + * @dev : the device structure of the drive + * + * If a device is a removable drive that requires an _EJ0 method + * to be executed in order to safely remove from the system, do + * it. ATM - always returns success + */ +int eject_removable_drive(struct device *dev) +{ + acpi_handle handle = DEVICE_ACPI_HANDLE(dev); + + if (handle) { + bay_dprintk(handle, "Got device handle"); + if (is_ejectable_bay(handle)) + eject_device(handle); + } else { + printk("No acpi handle for device\n"); + } + + /* should I return an error code? */ + return 0; +} +EXPORT_SYMBOL_GPL(eject_removable_drive); + +static int acpi_bay_add(struct acpi_device *device) +{ + bay_dprintk(device->handle, "adding bay device"); + strcpy(acpi_device_name(device), "Dockable Bay"); + strcpy(acpi_device_class(device), "bay"); + return 0; +} + +static int acpi_bay_add_fs(struct bay *bay) +{ + int ret; + struct device *dev = &bay->pdev->dev; + + ret = device_create_file(dev, &dev_attr_present); + if (ret) + goto add_fs_err; + ret = device_create_file(dev, &dev_attr_eject); + if (ret) { + device_remove_file(dev, &dev_attr_present); + goto add_fs_err; + } + return 0; + + add_fs_err: + bay_dprintk(bay->handle, "Error adding sysfs files\n"); + return ret; +} + +static void acpi_bay_remove_fs(struct bay *bay) +{ + struct device *dev = &bay->pdev->dev; + + /* cleanup sysfs */ + device_remove_file(dev, &dev_attr_present); + device_remove_file(dev, &dev_attr_eject); +} + +static int bay_is_dock_device(acpi_handle handle) +{ + acpi_handle parent; + + acpi_get_parent(handle, &parent); + + /* if the device or it's parent is dependent on the + * dock, then we are a dock device + */ + return (is_dock_device(handle) || is_dock_device(parent)); +} + +static int bay_add(acpi_handle handle, int id) +{ + acpi_status status; + struct bay *new_bay; + struct platform_device *pdev; + struct acpi_buffer nbuffer = {ACPI_ALLOCATE_BUFFER, NULL}; + acpi_get_name(handle, ACPI_FULL_PATHNAME, &nbuffer); + + bay_dprintk(handle, "Adding notify handler"); + + /* + * Initialize bay device structure + */ + new_bay = kzalloc(GFP_ATOMIC, sizeof(*new_bay)); + INIT_LIST_HEAD(&new_bay->list); + new_bay->handle = handle; + new_bay->name = (char *)nbuffer.pointer; + + /* initialize platform device stuff */ + pdev = platform_device_register_simple(ACPI_BAY_CLASS, id, NULL, 0); + if (pdev == NULL) { + printk(KERN_ERR PREFIX "Error registering bay device\n"); + goto bay_add_err; + } + new_bay->pdev = pdev; + platform_set_drvdata(pdev, new_bay); + + if (acpi_bay_add_fs(new_bay)) { + platform_device_unregister(new_bay->pdev); + goto bay_add_err; + } + + /* register for events on this device */ + status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY, + bay_notify, new_bay); + if (ACPI_FAILURE(status)) { + printk(KERN_ERR PREFIX "Error installing bay notify handler\n"); + } + + /* if we are on a dock station, we should register for dock + * notifications. + */ + if (bay_is_dock_device(handle)) { + bay_dprintk(handle, "Is dependent on dock\n"); + register_hotplug_dock_device(handle, bay_notify, new_bay); + } + list_add(&new_bay->list, &drive_bays); + printk(KERN_INFO PREFIX "Bay [%s] Added\n", new_bay->name); + return 0; + +bay_add_err: + kfree(new_bay->name); + kfree(new_bay); + return -ENODEV; +} + +static int acpi_bay_remove(struct acpi_device *device, int type) +{ + /*** FIXME: do something here */ + return 0; +} + +/** + * bay_create_acpi_device - add new devices to acpi + * @handle - handle of the device to add + * + * This function will create a new acpi_device for the given + * handle if one does not exist already. This should cause + * acpi to scan for drivers for the given devices, and call + * matching driver's add routine. + * + * Returns a pointer to the acpi_device corresponding to the handle. + */ +static struct acpi_device * bay_create_acpi_device(acpi_handle handle) +{ + struct acpi_device *device = NULL; + struct acpi_device *parent_device; + acpi_handle parent; + int ret; + + bay_dprintk(handle, "Trying to get device"); + if (acpi_bus_get_device(handle, &device)) { + /* + * no device created for this object, + * so we should create one. + */ + bay_dprintk(handle, "No device for handle"); + acpi_get_parent(handle, &parent); + if (acpi_bus_get_device(parent, &parent_device)) + parent_device = NULL; + + ret = acpi_bus_add(&device, parent_device, handle, + ACPI_BUS_TYPE_DEVICE); + if (ret) { + pr_debug("error adding bus, %x\n", + -ret); + return NULL; + } + } + return device; +} + +/** + * bay_notify - act upon an acpi bay notification + * @handle: the bay handle + * @event: the acpi event + * @data: our driver data struct + * + */ +static void bay_notify(acpi_handle handle, u32 event, void *data) +{ + struct acpi_device *dev; + + bay_dprintk(handle, "Bay event"); + + switch(event) { + case ACPI_NOTIFY_BUS_CHECK: + printk("Bus Check\n"); + case ACPI_NOTIFY_DEVICE_CHECK: + printk("Device Check\n"); + dev = bay_create_acpi_device(handle); + if (dev) + acpi_bus_generate_event(dev, event, 0); + else + printk("No device for generating event\n"); + /* wouldn't it be a good idea to just rescan SATA + * right here? + */ + break; + case ACPI_NOTIFY_EJECT_REQUEST: + printk("Eject request\n"); + dev = bay_create_acpi_device(handle); + if (dev) + acpi_bus_generate_event(dev, event, 0); + else + printk("No device for generating eventn"); + + /* wouldn't it be a good idea to just call the + * eject_device here if we were a SATA device? + */ + break; + default: + printk("unknown event %d\n", event); + } +} + +static acpi_status +find_bay(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + int *count = (int *)context; + + /* + * there could be more than one ejectable bay. + * so, just return AE_OK always so that every object + * will be checked. + */ + if (is_ejectable_bay(handle)) { + bay_dprintk(handle, "found ejectable bay"); + if (!bay_add(handle, *count)) + (*count)++; + } + return AE_OK; +} + +static int __init bay_init(void) +{ + int bays = 0; + + INIT_LIST_HEAD(&drive_bays); + + /* look for dockable drive bays */ + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_bay, &bays, NULL); + + if (bays) + if ((acpi_bus_register_driver(&acpi_bay_driver) < 0)) + printk(KERN_ERR "Unable to register bay driver\n"); + + if (!bays) + return -ENODEV; + + return 0; +} + +static void __exit bay_exit(void) +{ + struct bay *bay, *tmp; + + list_for_each_entry_safe(bay, tmp, &drive_bays, list) { + if (is_dock_device(bay->handle)) + unregister_hotplug_dock_device(bay->handle); + acpi_bay_remove_fs(bay); + acpi_remove_notify_handler(bay->handle, ACPI_SYSTEM_NOTIFY, + bay_notify); + platform_device_unregister(bay->pdev); + kfree(bay->name); + kfree(bay); + } + + acpi_bus_unregister_driver(&acpi_bay_driver); +} + +postcore_initcall(bay_init); +module_exit(bay_exit); + diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index 15d677e6cee..c26468da429 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -192,7 +192,7 @@ int acpi_bus_set_power(acpi_handle handle, int state) if (!device->flags.power_manageable) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device `[%s]' is not power manageable\n", - device->kobj.name)); + device->dev.kobj.name)); return -ENODEV; } /* diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index ac860583c20..c726612fafb 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -75,7 +75,7 @@ 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", + .ids = "button_power,button_sleep,PNP0C0D,PNP0C0C,PNP0C0E", .ops = { .add = acpi_button_add, .remove = acpi_button_remove, diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 0a1863ec91f..69a68fd394c 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -167,7 +167,7 @@ static void container_notify_cb(acpi_handle handle, u32 type, void *context) if (ACPI_FAILURE(status) || !device) { result = container_device_add(&device, handle); if (!result) - kobject_uevent(&device->kobj, + kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); else printk("Failed to add container\n"); @@ -175,13 +175,13 @@ static void container_notify_cb(acpi_handle handle, u32 type, void *context) } else { if (ACPI_SUCCESS(status)) { /* device exist and this is a remove request */ - kobject_uevent(&device->kobj, KOBJ_OFFLINE); + kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); } } break; case ACPI_NOTIFY_EJECT_REQUEST: if (!acpi_bus_get_device(handle, &device) && device) { - kobject_uevent(&device->kobj, KOBJ_OFFLINE); + kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); } break; default: diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index 35c6af8a83c..d48f65a8f65 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -13,14 +13,11 @@ #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); +#define MODULE_PARAM_PREFIX "acpi." struct acpi_dlayer { const char *name; @@ -86,6 +83,60 @@ static const struct acpi_dlevel acpi_debug_levels[] = { ACPI_DEBUG_INIT(ACPI_LV_EVENTS), }; +/* -------------------------------------------------------------------------- + FS Interface (/sys) + -------------------------------------------------------------------------- */ +static int param_get_debug_layer(char *buffer, struct kernel_param *kp) { + int result = 0; + int i; + + result = sprintf(buffer, "%-25s\tHex SET\n", "Description"); + + for(i = 0; i <ARRAY_SIZE(acpi_debug_layers); i++) { + result += sprintf(buffer+result, "%-25s\t0x%08lX [%c]\n", + acpi_debug_layers[i].name, + acpi_debug_layers[i].value, + (acpi_dbg_layer & acpi_debug_layers[i].value) ? '*' : ' '); + } + result += sprintf(buffer+result, "%-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 ? ' ' : '-'); + result += sprintf(buffer+result, "--\ndebug_layer = 0x%08X ( * = enabled)\n", acpi_dbg_layer); + + return result; +} + +static int param_get_debug_level(char *buffer, struct kernel_param *kp) { + int result = 0; + int i; + + result = sprintf(buffer, "%-25s\tHex SET\n", "Description"); + + for (i = 0; i < ARRAY_SIZE(acpi_debug_levels); i++) { + result += sprintf(buffer+result, "%-25s\t0x%08lX [%c]\n", + acpi_debug_levels[i].name, + acpi_debug_levels[i].value, + (acpi_dbg_level & acpi_debug_levels[i]. + value) ? '*' : ' '); + } + result += sprintf(buffer+result, "--\ndebug_level = 0x%08X (* = enabled)\n", + acpi_dbg_level); + + return result; +} + +module_param_call(debug_layer, param_set_uint, param_get_debug_layer, &acpi_dbg_layer, 0644); +module_param_call(debug_level, param_set_uint, param_get_debug_level, &acpi_dbg_level, 0644); + +/* -------------------------------------------------------------------------- + FS Interface (/proc) + -------------------------------------------------------------------------- */ +#ifdef CONFIG_ACPI_PROCFS +#define ACPI_SYSTEM_FILE_DEBUG_LAYER "debug_layer" +#define ACPI_SYSTEM_FILE_DEBUG_LEVEL "debug_level" + static int acpi_system_read_debug(char *page, char **start, off_t off, int count, int *eof, void *data) @@ -221,3 +272,4 @@ static int __init acpi_debug_init(void) } subsys_initcall(acpi_debug_init); +#endif diff --git a/drivers/acpi/dock.c b/drivers/acpi/dock.c index 90990a4b652..688e83a1690 100644 --- a/drivers/acpi/dock.c +++ b/drivers/acpi/dock.c @@ -615,20 +615,28 @@ static acpi_status find_dock_devices(acpi_handle handle, u32 lvl, void *context, void **rv) { acpi_status status; - acpi_handle tmp; + acpi_handle tmp, parent; struct dock_station *ds = context; struct dock_dependent_device *dd; status = acpi_bus_get_ejd(handle, &tmp); - if (ACPI_FAILURE(status)) - return AE_OK; + if (ACPI_FAILURE(status)) { + /* try the parent device as well */ + status = acpi_get_parent(handle, &parent); + if (ACPI_FAILURE(status)) + goto fdd_out; + /* see if parent is dependent on dock */ + status = acpi_bus_get_ejd(parent, &tmp); + if (ACPI_FAILURE(status)) + goto fdd_out; + } if (tmp == ds->handle) { dd = alloc_dock_dependent_device(handle); if (dd) add_dock_dependent_device(ds, dd); } - +fdd_out: return AE_OK; } diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index f305a826ca2..af22fdf7341 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -48,8 +48,8 @@ MODULE_LICENSE("GPL"); static int acpi_fan_add(struct acpi_device *device); static int acpi_fan_remove(struct acpi_device *device, int type); -static int acpi_fan_suspend(struct acpi_device *device, int state); -static int acpi_fan_resume(struct acpi_device *device, int state); +static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state); +static int acpi_fan_resume(struct acpi_device *device); static struct acpi_driver acpi_fan_driver = { .name = ACPI_FAN_DRIVER_NAME, @@ -237,7 +237,7 @@ static int acpi_fan_remove(struct acpi_device *device, int type) return 0; } -static int acpi_fan_suspend(struct acpi_device *device, int state) +static int acpi_fan_suspend(struct acpi_device *device, pm_message_t state) { if (!device) return -EINVAL; @@ -247,7 +247,7 @@ static int acpi_fan_suspend(struct acpi_device *device, int state) return AE_OK; } -static int acpi_fan_resume(struct acpi_device *device, int state) +static int acpi_fan_resume(struct acpi_device *device) { int result = 0; int power_state = 0; diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 8a0324b43e5..7b6c9ff9beb 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -86,129 +86,6 @@ static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle) return ret; } -/* Get PCI root bridge's handle from its segment and bus number */ -struct acpi_find_pci_root { - unsigned int seg; - unsigned int bus; - acpi_handle handle; -}; - -static acpi_status -do_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) -{ - unsigned long *busnr = data; - struct acpi_resource_address64 address; - - if (resource->type != ACPI_RESOURCE_TYPE_ADDRESS16 && - resource->type != ACPI_RESOURCE_TYPE_ADDRESS32 && - resource->type != ACPI_RESOURCE_TYPE_ADDRESS64) - return AE_OK; - - acpi_resource_to_address64(resource, &address); - if ((address.address_length > 0) && - (address.resource_type == ACPI_BUS_NUMBER_RANGE)) - *busnr = address.minimum; - - return AE_OK; -} - -static int get_root_bridge_busnr(acpi_handle handle) -{ - acpi_status status; - unsigned long bus, bbn; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - - status = acpi_evaluate_integer(handle, METHOD_NAME__BBN, NULL, - &bbn); - if (status == AE_NOT_FOUND) { - /* Assume bus = 0 */ - printk(KERN_INFO PREFIX - "Assume root bridge [%s] bus is 0\n", - (char *)buffer.pointer); - status = AE_OK; - bbn = 0; - } - if (ACPI_FAILURE(status)) { - bbn = -ENODEV; - goto exit; - } - if (bbn > 0) - goto exit; - - /* _BBN in some systems return 0 for all root bridges */ - bus = -1; - status = acpi_walk_resources(handle, METHOD_NAME__CRS, - do_root_bridge_busnr_callback, &bus); - /* If _CRS failed, we just use _BBN */ - if (ACPI_FAILURE(status) || (bus == -1)) - goto exit; - /* We select _CRS */ - if (bbn != bus) { - printk(KERN_INFO PREFIX - "_BBN and _CRS returns different value for %s. Select _CRS\n", - (char *)buffer.pointer); - bbn = bus; - } - exit: - kfree(buffer.pointer); - return (int)bbn; -} - -static acpi_status -find_pci_rootbridge(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - struct acpi_find_pci_root *find = (struct acpi_find_pci_root *)context; - unsigned long seg, bus; - acpi_status status; - int tmp; - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - - acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - - status = acpi_evaluate_integer(handle, METHOD_NAME__SEG, NULL, &seg); - if (status == AE_NOT_FOUND) { - /* Assume seg = 0 */ - status = AE_OK; - seg = 0; - } - if (ACPI_FAILURE(status)) { - status = AE_CTRL_DEPTH; - goto exit; - } - - tmp = get_root_bridge_busnr(handle); - if (tmp < 0) { - printk(KERN_ERR PREFIX - "Find root bridge failed for %s\n", - (char *)buffer.pointer); - status = AE_CTRL_DEPTH; - goto exit; - } - bus = tmp; - - if (seg == find->seg && bus == find->bus) - { - find->handle = handle; - status = AE_CTRL_TERMINATE; - } - else - status = AE_OK; - exit: - kfree(buffer.pointer); - return status; -} - -acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus) -{ - struct acpi_find_pci_root find = { seg, bus, NULL }; - - acpi_get_devices(PCI_ROOT_HID_STRING, find_pci_rootbridge, &find, NULL); - return find.handle; -} -EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); - /* Get device's handler per its address under its parent */ struct acpi_find_child { acpi_handle handle; diff --git a/drivers/acpi/motherboard.c b/drivers/acpi/motherboard.c deleted file mode 100644 index b61107b0526..00000000000 --- a/drivers/acpi/motherboard.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * 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. - */ - -/* - * When CONFIG_PNP is enabled, pnp/system.c binds to PNP0C01 - * and PNP0C02, redundant with acpi_reserve_io_ranges(). - * But acpi_reserve_io_ranges() is necessary for !CONFIG_PNP. - */ -static acpi_status acpi_reserve_io_ranges(struct acpi_resource *res, void *data) -{ - struct resource *requested_res = NULL; - - - if (res->type == ACPI_RESOURCE_TYPE_IO) { - struct acpi_resource_io *io_res = &res->data.io; - - if (io_res->minimum != io_res->maximum) - return AE_OK; - if (IS_RESERVED_ADDR - (io_res->minimum, io_res->address_length)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Motherboard resources 0x%08x - 0x%08x\n", - io_res->minimum, - io_res->minimum + - io_res->address_length)); - requested_res = - request_region(io_res->minimum, - io_res->address_length, "motherboard"); - } - } else if (res->type == ACPI_RESOURCE_TYPE_FIXED_IO) { - struct acpi_resource_fixed_io *fixed_io_res = - &res->data.fixed_io; - - if (IS_RESERVED_ADDR - (fixed_io_res->address, fixed_io_res->address_length)) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Motherboard resources 0x%08x - 0x%08x\n", - fixed_io_res->address, - fixed_io_res->address + - fixed_io_res->address_length)); - requested_res = - request_region(fixed_io_res->address, - fixed_io_res->address_length, - "motherboard"); - } - } else { - /* Memory mapped IO? */ - } - - if (requested_res) - requested_res->flags &= ~IORESOURCE_BUSY; - return 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_request_region (struct acpi_generic_address *addr, - unsigned int length, char *desc) -{ - if (!addr->address || !length) - return; - - if (addr->space_id == ACPI_ADR_SPACE_SYSTEM_IO) - request_region(addr->address, length, desc); - else if (addr->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) - request_mem_region(addr->address, length, desc); -} - -static void __init acpi_reserve_resources(void) -{ - acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, - acpi_gbl_FADT.pm1_event_length, "ACPI PM1a_EVT_BLK"); - - acpi_request_region(&acpi_gbl_FADT.xpm1b_event_block, - acpi_gbl_FADT.pm1_event_length, "ACPI PM1b_EVT_BLK"); - - acpi_request_region(&acpi_gbl_FADT.xpm1a_control_block, - acpi_gbl_FADT.pm1_control_length, "ACPI PM1a_CNT_BLK"); - - acpi_request_region(&acpi_gbl_FADT.xpm1b_control_block, - acpi_gbl_FADT.pm1_control_length, "ACPI PM1b_CNT_BLK"); - - if (acpi_gbl_FADT.pm_timer_length == 4) - acpi_request_region(&acpi_gbl_FADT.xpm_timer_block, 4, "ACPI PM_TMR"); - - acpi_request_region(&acpi_gbl_FADT.xpm2_control_block, - acpi_gbl_FADT.pm2_control_length, "ACPI PM2_CNT_BLK"); - - /* Length of GPE blocks must be a non-negative multiple of 2 */ - - if (!(acpi_gbl_FADT.gpe0_block_length & 0x1)) - acpi_request_region(&acpi_gbl_FADT.xgpe0_block, - acpi_gbl_FADT.gpe0_block_length, "ACPI GPE0_BLK"); - - if (!(acpi_gbl_FADT.gpe1_block_length & 0x1)) - acpi_request_region(&acpi_gbl_FADT.xgpe1_block, - acpi_gbl_FADT.gpe1_block_length, "ACPI 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/osl.c b/drivers/acpi/osl.c index a28f5b8972b..0f6f3bcbc8e 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -76,6 +76,54 @@ static acpi_osd_handler acpi_irq_handler; static void *acpi_irq_context; static struct workqueue_struct *kacpid_wq; +static void __init acpi_request_region (struct acpi_generic_address *addr, + unsigned int length, char *desc) +{ + struct resource *res; + + if (!addr->address || !length) + return; + + if (addr->space_id == ACPI_ADR_SPACE_SYSTEM_IO) + res = request_region(addr->address, length, desc); + else if (addr->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) + res = request_mem_region(addr->address, length, desc); +} + +static int __init acpi_reserve_resources(void) +{ + acpi_request_region(&acpi_gbl_FADT.xpm1a_event_block, acpi_gbl_FADT.pm1_event_length, + "ACPI PM1a_EVT_BLK"); + + acpi_request_region(&acpi_gbl_FADT.xpm1b_event_block, acpi_gbl_FADT.pm1_event_length, + "ACPI PM1b_EVT_BLK"); + + acpi_request_region(&acpi_gbl_FADT.xpm1a_control_block, acpi_gbl_FADT.pm1_control_length, + "ACPI PM1a_CNT_BLK"); + + acpi_request_region(&acpi_gbl_FADT.xpm1b_control_block, acpi_gbl_FADT.pm1_control_length, + "ACPI PM1b_CNT_BLK"); + + if (acpi_gbl_FADT.pm_timer_length == 4) + acpi_request_region(&acpi_gbl_FADT.xpm_timer_block, 4, "ACPI PM_TMR"); + + acpi_request_region(&acpi_gbl_FADT.xpm2_control_block, acpi_gbl_FADT.pm2_control_length, + "ACPI PM2_CNT_BLK"); + + /* Length of GPE blocks must be a non-negative multiple of 2 */ + + if (!(acpi_gbl_FADT.gpe0_block_length & 0x1)) + acpi_request_region(&acpi_gbl_FADT.xgpe0_block, + acpi_gbl_FADT.gpe0_block_length, "ACPI GPE0_BLK"); + + if (!(acpi_gbl_FADT.gpe1_block_length & 0x1)) + acpi_request_region(&acpi_gbl_FADT.xgpe1_block, + acpi_gbl_FADT.gpe1_block_length, "ACPI GPE1_BLK"); + + return 0; +} +device_initcall(acpi_reserve_resources); + acpi_status acpi_os_initialize(void) { return AE_OK; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index a860efa2c56..4ecf701687e 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -117,6 +117,19 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) EXPORT_SYMBOL(acpi_pci_unregister_driver); +acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus) +{ + struct acpi_pci_root *tmp; + + list_for_each_entry(tmp, &acpi_pci_roots, node) { + if ((tmp->id.segment == (u16) seg) && (tmp->id.bus == (u16) bus)) + return tmp->device->handle; + } + return NULL; +} + +EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); + static acpi_status get_root_bridge_busnr_callback(struct acpi_resource *resource, void *data) { @@ -152,6 +165,21 @@ static acpi_status try_get_root_bridge_busnr(acpi_handle handle, int *busnum) return AE_OK; } +static void acpi_pci_bridge_scan(struct acpi_device *device) +{ + int status; + struct acpi_device *child = NULL; + + if (device->flags.bus_address) + if (device->parent && device->parent->ops.bind) { + status = device->parent->ops.bind(device); + if (!status) { + list_for_each_entry(child, &device->children, node) + acpi_pci_bridge_scan(child); + } + } +} + static int acpi_pci_root_add(struct acpi_device *device) { int result = 0; @@ -160,6 +188,7 @@ static int acpi_pci_root_add(struct acpi_device *device) acpi_status status = AE_OK; unsigned long value = 0; acpi_handle handle = NULL; + struct acpi_device *child; if (!device) @@ -175,9 +204,6 @@ static int acpi_pci_root_add(struct acpi_device *device) 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; /* @@ -299,6 +325,12 @@ static int acpi_pci_root_add(struct acpi_device *device) result = acpi_pci_irq_add_prt(device->handle, root->id.segment, root->id.bus); + /* + * Scan and bind all _ADR-Based Devices + */ + list_for_each_entry(child, &device->children, node) + acpi_pci_bridge_scan(child); + end: if (result) { if (!list_empty(&root->node)) diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index eacf9a25201..0079bc51082 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -814,7 +814,7 @@ int acpi_processor_device_add(acpi_handle handle, struct acpi_device **device) return -ENODEV; if ((pr->id >= 0) && (pr->id < NR_CPUS)) { - kobject_uevent(&(*device)->kobj, KOBJ_ONLINE); + kobject_uevent(&(*device)->dev.kobj, KOBJ_ONLINE); } return 0; } @@ -852,13 +852,13 @@ acpi_processor_hotplug_notify(acpi_handle handle, u32 event, void *data) } if (pr->id >= 0 && (pr->id < NR_CPUS)) { - kobject_uevent(&device->kobj, KOBJ_OFFLINE); + kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); break; } result = acpi_processor_start(device); if ((!result) && ((pr->id >= 0) && (pr->id < NR_CPUS))) { - kobject_uevent(&device->kobj, KOBJ_ONLINE); + kobject_uevent(&device->dev.kobj, KOBJ_ONLINE); } else { printk(KERN_ERR PREFIX "Device [%s] failed to start\n", acpi_device_bid(device)); @@ -881,7 +881,7 @@ acpi_processor_hotplug_notify(acpi_handle handle, u32 event, void *data) } if ((pr->id < NR_CPUS) && (cpu_present(pr->id))) - kobject_uevent(&device->kobj, KOBJ_OFFLINE); + kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE); break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 0de45866464..64f26db10c8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -21,101 +21,305 @@ extern struct acpi_device *acpi_root; #define ACPI_BUS_DEVICE_NAME "System Bus" static LIST_HEAD(acpi_device_list); +static LIST_HEAD(acpi_bus_id_list); DEFINE_SPINLOCK(acpi_device_lock); LIST_HEAD(acpi_wakeup_device_list); +struct acpi_device_bus_id{ + char bus_id[15]; + unsigned int instance_no; + struct list_head node; +}; +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 void acpi_device_release(struct kobject *kobj) +static ssize_t +acpi_eject_store(struct device *d, struct device_attribute *attr, + const char *buf, size_t count) { - struct acpi_device *dev = container_of(kobj, struct acpi_device, kobj); - kfree(dev->pnp.cid_list); - kfree(dev); + int result; + int ret = count; + int islockable; + acpi_status status; + acpi_handle handle; + acpi_object_type type = 0; + struct acpi_device *acpi_device = to_acpi_device(d); + + if ((!count) || (buf[0] != '1')) { + return -EINVAL; + } +#ifndef FORCE_EJECT + if (acpi_device->driver == NULL) { + ret = -ENODEV; + goto err; + } +#endif + status = acpi_get_type(acpi_device->handle, &type); + if (ACPI_FAILURE(status) || (!acpi_device->flags.ejectable)) { + ret = -ENODEV; + goto err; + } + + islockable = acpi_device->flags.lockable; + handle = acpi_device->handle; + + result = acpi_bus_trim(acpi_device, 1); + + if (!result) + result = acpi_eject_operation(handle, islockable); + + if (result) { + ret = -EBUSY; + } + err: + return ret; } -struct acpi_device_attribute { - struct attribute attr; - ssize_t(*show) (struct acpi_device *, char *); - ssize_t(*store) (struct acpi_device *, const char *, size_t); -}; +static DEVICE_ATTR(eject, 0200, NULL, acpi_eject_store); + +static ssize_t +acpi_device_hid_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + + return sprintf(buf, "%s\n", acpi_dev->pnp.hardware_id); +} +static DEVICE_ATTR(hid, 0444, acpi_device_hid_show, NULL); + +static ssize_t +acpi_device_path_show(struct device *dev, struct device_attribute *attr, char *buf) { + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL}; + int result; + + result = acpi_get_name(acpi_dev->handle, ACPI_FULL_PATHNAME, &path); + if(result) + goto end; + + result = sprintf(buf, "%s\n", (char*)path.pointer); + kfree(path.pointer); + end: + return result; +} +static DEVICE_ATTR(path, 0444, acpi_device_path_show, NULL); + +static int acpi_device_setup_files(struct acpi_device *dev) +{ + acpi_status status; + acpi_handle temp; + int result = 0; + + /* + * Devices gotten from FADT don't have a "path" attribute + */ + if(dev->handle) { + result = device_create_file(&dev->dev, &dev_attr_path); + if(result) + goto end; + } -typedef void acpi_device_sysfs_files(struct kobject *, - const struct attribute *); + if(dev->flags.hardware_id) { + result = device_create_file(&dev->dev, &dev_attr_hid); + if(result) + goto end; + } -static void setup_sys_fs_device_files(struct acpi_device *dev, - acpi_device_sysfs_files * func); + /* + * 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)) + result = device_create_file(&dev->dev, &dev_attr_eject); + end: + return result; +} -#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) +static void acpi_device_remove_files(struct acpi_device *dev) +{ + acpi_status status; + acpi_handle temp; -#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); + /* + * 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)) + device_remove_file(&dev->dev, &dev_attr_eject); -static ssize_t acpi_device_attr_show(struct kobject *kobj, - struct attribute *attr, char *buf) + if(dev->flags.hardware_id) + device_remove_file(&dev->dev, &dev_attr_hid); + if(dev->handle) + device_remove_file(&dev->dev, &dev_attr_path); +} +/* -------------------------------------------------------------------------- + ACPI Bus operations + -------------------------------------------------------------------------- */ +static void acpi_device_release(struct device *dev) { - struct acpi_device *device = to_acpi_device(kobj); - struct acpi_device_attribute *attribute = to_handle_attr(attr); - return attribute->show ? attribute->show(device, buf) : -EIO; + struct acpi_device *acpi_dev = to_acpi_device(dev); + + kfree(acpi_dev->pnp.cid_list); + kfree(acpi_dev); } -static ssize_t acpi_device_attr_store(struct kobject *kobj, - struct attribute *attr, const char *buf, - size_t len) + +static int acpi_device_suspend(struct device *dev, pm_message_t state) { - 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) : -EIO; + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = acpi_dev->driver; + + if (acpi_drv && acpi_drv->ops.suspend) + return acpi_drv->ops.suspend(acpi_dev, state); + return 0; } -static struct sysfs_ops acpi_device_sysfs_ops = { - .show = acpi_device_attr_show, - .store = acpi_device_attr_store, -}; +static int acpi_device_resume(struct device *dev) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = acpi_dev->driver; -static struct kobj_type ktype_acpi_ns = { - .sysfs_ops = &acpi_device_sysfs_ops, - .release = acpi_device_release, -}; + if (acpi_drv && acpi_drv->ops.resume) + return acpi_drv->ops.resume(acpi_dev); + return 0; +} -static int namespace_uevent(struct kset *kset, struct kobject *kobj, - char **envp, int num_envp, char *buffer, - int buffer_size) +static int acpi_bus_match(struct device *dev, struct device_driver *drv) { - struct acpi_device *dev = to_acpi_device(kobj); - int i = 0; - int len = 0; + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = to_acpi_driver(drv); - if (!dev->driver) - return 0; + return !acpi_match_ids(acpi_dev, acpi_drv->ids); +} - if (add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, - "PHYSDEVDRIVER=%s", dev->driver->name)) +static int acpi_device_uevent(struct device *dev, char **envp, int num_envp, + char *buffer, int buffer_size) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + int i = 0, length = 0, ret = 0; + + if (acpi_dev->flags.hardware_id) + ret = add_uevent_var(envp, num_envp, &i, + buffer, buffer_size, &length, + "HWID=%s", acpi_dev->pnp.hardware_id); + if (ret) return -ENOMEM; + if (acpi_dev->flags.compatible_ids) { + int j; + struct acpi_compatible_id_list *cid_list; + + cid_list = acpi_dev->pnp.cid_list; + + for (j = 0; j < cid_list->count; j++) { + ret = add_uevent_var(envp, num_envp, &i, buffer, + buffer_size, &length, "COMPTID=%s", + cid_list->id[j].value); + if (ret) + return -ENOMEM; + } + } envp[i] = NULL; + return 0; +} + +static int acpi_bus_driver_init(struct acpi_device *, struct acpi_driver *); +static int acpi_start_single_object(struct acpi_device *); +static int acpi_device_probe(struct device * dev) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = to_acpi_driver(dev->driver); + int ret; + + ret = acpi_bus_driver_init(acpi_dev, acpi_drv); + if (!ret) { + if (acpi_dev->bus_ops.acpi_op_start) + acpi_start_single_object(acpi_dev); + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Found driver [%s] for device [%s]\n", + acpi_drv->name, acpi_dev->pnp.bus_id)); + get_device(dev); + } + return ret; +} +static int acpi_device_remove(struct device * dev) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = acpi_dev->driver; + + if (acpi_drv) { + if (acpi_drv->ops.stop) + acpi_drv->ops.stop(acpi_dev, acpi_dev->removal_type); + if (acpi_drv->ops.remove) + acpi_drv->ops.remove(acpi_dev, acpi_dev->removal_type); + } + acpi_dev->driver = NULL; + acpi_driver_data(dev) = NULL; + + put_device(dev); return 0; } -static struct kset_uevent_ops namespace_uevent_ops = { - .uevent = &namespace_uevent, -}; +static void acpi_device_shutdown(struct device *dev) +{ + struct acpi_device *acpi_dev = to_acpi_device(dev); + struct acpi_driver *acpi_drv = acpi_dev->driver; + + if (acpi_drv && acpi_drv->ops.shutdown) + acpi_drv->ops.shutdown(acpi_dev); -static struct kset acpi_namespace_kset = { - .kobj = { - .name = "namespace", - }, - .subsys = &acpi_subsys, - .ktype = &ktype_acpi_ns, - .uevent_ops = &namespace_uevent_ops, + return ; +} + +static struct bus_type acpi_bus_type = { + .name = "acpi", + .suspend = acpi_device_suspend, + .resume = acpi_device_resume, + .shutdown = acpi_device_shutdown, + .match = acpi_bus_match, + .probe = acpi_device_probe, + .remove = acpi_device_remove, + .uevent = acpi_device_uevent, }; -static void acpi_device_register(struct acpi_device *device, +static int acpi_device_register(struct acpi_device *device, struct acpi_device *parent) { - int err; - + int result; + struct acpi_device_bus_id *acpi_device_bus_id, *new_bus_id; + int found = 0; /* * Linkage * ------- @@ -126,7 +330,33 @@ static void acpi_device_register(struct acpi_device *device, INIT_LIST_HEAD(&device->g_list); INIT_LIST_HEAD(&device->wakeup_list); + new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); + if (!new_bus_id) { + printk(KERN_ERR PREFIX "Memory allocation error\n"); + return -ENOMEM; + } + spin_lock(&acpi_device_lock); + /* + * Find suitable bus_id and instance number in acpi_bus_id_list + * If failed, create one and link it into acpi_bus_id_list + */ + list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) { + if(!strcmp(acpi_device_bus_id->bus_id, device->flags.hardware_id? device->pnp.hardware_id : "device")) { + acpi_device_bus_id->instance_no ++; + found = 1; + kfree(new_bus_id); + break; + } + } + if(!found) { + acpi_device_bus_id = new_bus_id; + strcpy(acpi_device_bus_id->bus_id, device->flags.hardware_id ? device->pnp.hardware_id : "device"); + acpi_device_bus_id->instance_no = 0; + list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); + } + sprintf(device->dev.bus_id, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no); + if (device->parent) { list_add_tail(&device->node, &device->parent->children); list_add_tail(&device->g_list, &device->parent->g_list); @@ -136,16 +366,33 @@ static void acpi_device_register(struct acpi_device *device, 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; - err = kobject_register(&device->kobj); - if (err < 0) - printk(KERN_WARNING "%s: kobject_register error: %d\n", - __FUNCTION__, err); - create_sysfs_device_files(device); + if (device->parent) + device->dev.parent = &parent->dev; + device->dev.bus = &acpi_bus_type; + device_initialize(&device->dev); + device->dev.release = &acpi_device_release; + result = device_add(&device->dev); + if(result) { + printk("Error adding device %s", device->dev.bus_id); + goto end; + } + + result = acpi_device_setup_files(device); + if(result) + ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error creating sysfs interface for device %s\n", device->dev.bus_id)); + + device->removal_type = ACPI_BUS_REMOVAL_NORMAL; + return 0; + end: + 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); + return result; } static void acpi_device_unregister(struct acpi_device *device, int type) @@ -158,81 +405,143 @@ static void acpi_device_unregister(struct acpi_device *device, int type) 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); + + acpi_device_remove_files(device); + device_unregister(&device->dev); } -void acpi_bus_data_handler(acpi_handle handle, u32 function, void *context) +/* -------------------------------------------------------------------------- + Driver Management + -------------------------------------------------------------------------- */ +/** + * acpi_bus_driver_init - add a device to a driver + * @device: the device to add and initialize + * @driver: driver for the device + * + * Used to initialize a device via its device driver. Called whenever a + * driver is bound to a device. Invokes the driver's add() ops. + */ +static int +acpi_bus_driver_init(struct acpi_device *device, struct acpi_driver *driver) { + int result = 0; - /* TBD */ - return; -} + if (!device || !driver) + return -EINVAL; -static int acpi_bus_get_power_flags(struct acpi_device *device) -{ - acpi_status status = 0; - acpi_handle handle = NULL; - u32 i = 0; + if (!driver->ops.add) + return -ENOSYS; + result = driver->ops.add(device); + if (result) { + device->driver = NULL; + acpi_driver_data(device) = NULL; + return result; + } - /* - * 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; + device->driver = driver; /* - * Enumerate supported power management states + * TBD - Configuration Management: Assign resources to device based + * upon possible configuration and currently allocated resources. */ - 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; - } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, + "Driver successfully bound to device\n")); + return 0; +} - /* 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; - } +static int acpi_start_single_object(struct acpi_device *device) +{ + int result = 0; + struct acpi_driver *driver; - /* 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 */ + if (!(driver = device->driver)) + return 0; + + if (driver->ops.start) { + result = driver->ops.start(device); + if (result && driver->ops.remove) + driver->ops.remove(device, ACPI_BUS_REMOVAL_NORMAL); } - /* 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; + return result; +} - /* TBD: System wake support and resource requirements. */ +/** + * acpi_bus_register_driver - register a driver with the ACPI bus + * @driver: driver being registered + * + * Registers a driver with the ACPI bus. Searches the namespace for all + * devices that match the driver's criteria and binds. Returns zero for + * success or a negative error status for failure. + */ +int acpi_bus_register_driver(struct acpi_driver *driver) +{ + int ret; - device->power.state = ACPI_STATE_UNKNOWN; + if (acpi_disabled) + return -ENODEV; + driver->drv.name = driver->name; + driver->drv.bus = &acpi_bus_type; + driver->drv.owner = driver->owner; - return 0; + ret = driver_register(&driver->drv); + return ret; +} + +EXPORT_SYMBOL(acpi_bus_register_driver); + +/** + * acpi_bus_unregister_driver - unregisters a driver with the APIC bus + * @driver: driver to unregister + * + * Unregisters a driver with the ACPI bus. Searches the namespace for all + * devices that match the driver's criteria and unbinds. + */ +void acpi_bus_unregister_driver(struct acpi_driver *driver) +{ + driver_unregister(&driver->drv); +} + +EXPORT_SYMBOL(acpi_bus_unregister_driver); + +/* -------------------------------------------------------------------------- + Device Enumeration + -------------------------------------------------------------------------- */ +acpi_status +acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) +{ + acpi_status status; + acpi_handle tmp; + struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; + union acpi_object *obj; + + status = acpi_get_handle(handle, "_EJD", &tmp); + if (ACPI_FAILURE(status)) + return status; + + status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer); + if (ACPI_SUCCESS(status)) { + obj = buffer.pointer; + status = acpi_get_handle(NULL, obj->string.pointer, ejd); + kfree(buffer.pointer); + } + return status; +} +EXPORT_SYMBOL_GPL(acpi_bus_get_ejd); + +void acpi_bus_data_handler(acpi_handle handle, u32 function, void *context) +{ + + /* TBD */ + + return; } int acpi_match_ids(struct acpi_device *device, char *ids) @@ -254,6 +563,12 @@ int acpi_match_ids(struct acpi_device *device, char *ids) return -ENOENT; } +static int acpi_bus_get_perf_flags(struct acpi_device *device) +{ + device->performance.state = ACPI_STATE_UNKNOWN; + return 0; +} + static acpi_status acpi_bus_extract_wakeup_device_power_package(struct acpi_device *device, union acpi_object *package) @@ -338,359 +653,66 @@ static int acpi_bus_get_wakeup_device_flags(struct acpi_device *device) return 0; } -/* -------------------------------------------------------------------------- - ACPI 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) +static int acpi_bus_get_power_flags(struct acpi_device *device) { - 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); - } + acpi_status status = 0; + acpi_handle handle = NULL; + u32 i = 0; - arg_list.count = 1; - arg_list.pointer = &arg; - arg.type = ACPI_TYPE_INTEGER; - arg.integer.value = 1; /* - * TBD: _EJD support. + * Power Management Flags */ - - 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; - - 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); - -/** - * acpi_bus_match - match device IDs to driver's supported IDs - * @device: the device that we are trying to match to a driver - * @driver: driver whose device id table is being checked - * - * 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 - add a device to a driver - * @device: the device to add and initialize - * @driver: driver for the device - * - * 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; - - - if (!device || !driver) - return -EINVAL; - - if (!driver->ops.add) - return -ENOSYS; - - result = driver->ops.add(device); - if (result) { - device->driver = NULL; - acpi_driver_data(device) = NULL; - return result; - } - - device->driver = driver; + 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; /* - * TBD - Configuration Management: Assign resources to device based - * upon possible configuration and currently allocated resources. + * 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' }; - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Driver successfully bound to device\n")); - return 0; -} - -static int acpi_start_single_object(struct acpi_device *device) -{ - int result = 0; - struct acpi_driver *driver; - - - if (!(driver = device->driver)) - return 0; - - if (driver->ops.start) { - result = driver->ops.start(device); - if (result && driver->ops.remove) - driver->ops.remove(device, ACPI_BUS_REMOVAL_NORMAL); - } - - return result; -} - -static void acpi_driver_attach(struct acpi_driver *drv) -{ - struct list_head *node, *next; - - - 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)) { - acpi_start_single_object(dev); - atomic_inc(&drv->references); - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Found driver [%s] for device [%s]\n", - drv->name, dev->pnp.bus_id)); - } + /* 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; } - spin_lock(&acpi_device_lock); - } - spin_unlock(&acpi_device_lock); -} - -static void acpi_driver_detach(struct acpi_driver *drv) -{ - struct list_head *node, *next; - - 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); + /* 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; } - } - spin_unlock(&acpi_device_lock); -} - -/** - * acpi_bus_register_driver - register a driver with the ACPI bus - * @driver: driver being registered - * - * Registers a driver with the ACPI bus. Searches the namespace for all - * devices that match the driver's criteria and binds. Returns zero for - * success or a negative error status for failure. - */ -int acpi_bus_register_driver(struct acpi_driver *driver) -{ - - if (acpi_disabled) - return -ENODEV; - - spin_lock(&acpi_device_lock); - list_add_tail(&driver->node, &acpi_bus_drivers); - spin_unlock(&acpi_device_lock); - acpi_driver_attach(driver); - - return 0; -} - -EXPORT_SYMBOL(acpi_bus_register_driver); - -/** - * acpi_bus_unregister_driver - unregisters a driver with the APIC bus - * @driver: driver to unregister - * - * Unregisters a driver with the ACPI bus. Searches the namespace for all - * devices that match the driver's criteria and unbinds. - */ -void acpi_bus_unregister_driver(struct acpi_driver *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); - } - return; -} - -EXPORT_SYMBOL(acpi_bus_unregister_driver); - -/** - * acpi_bus_find_driver - check if there is a driver installed for the device - * @device: device that we are trying to find a supporting driver for - * - * 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; + /* State is valid if we have some power control */ + if (ps->resources.count || ps->flags.explicit_set) + ps->flags.valid = 1; - 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); + ps->power = -1; /* Unknown - driver assigned */ + ps->latency = -1; /* Unknown - driver assigned */ } - spin_unlock(&acpi_device_lock); - - Done: - return result; -} -/* -------------------------------------------------------------------------- - Device Enumeration - -------------------------------------------------------------------------- */ + /* 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; -acpi_status -acpi_bus_get_ejd(acpi_handle handle, acpi_handle *ejd) -{ - acpi_status status; - acpi_handle tmp; - struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; - union acpi_object *obj; + /* TBD: System wake support and resource requirements. */ - status = acpi_get_handle(handle, "_EJD", &tmp); - if (ACPI_FAILURE(status)) - return status; + device->power.state = ACPI_STATE_UNKNOWN; - status = acpi_evaluate_object(handle, "_EJD", NULL, &buffer); - if (ACPI_SUCCESS(status)) { - obj = buffer.pointer; - status = acpi_get_handle(NULL, obj->string.pointer, ejd); - kfree(buffer.pointer); - } - return status; + return 0; } -EXPORT_SYMBOL_GPL(acpi_bus_get_ejd); - static int acpi_bus_get_flags(struct acpi_device *device) { @@ -782,6 +804,75 @@ static void acpi_device_get_busid(struct acpi_device *device, } } +static int +acpi_video_bus_match(struct acpi_device *device) +{ + acpi_handle h_dummy1; + acpi_handle h_dummy2; + acpi_handle h_dummy3; + + + if (!device) + return -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 0; + + /* Does this device able to retrieve a video ROM ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy1))) + return 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 0; + + return -ENODEV; +} + +/* + * acpi_bay_match - see if a device is an ejectable driver bay + * + * If an acpi object is ejectable and has one of the ACPI ATA methods defined, + * then we can safely call it an ejectable drive bay + */ +static int acpi_bay_match(struct acpi_device *device){ + acpi_status status; + acpi_handle handle; + acpi_handle tmp; + acpi_handle phandle; + + handle = device->handle; + + status = acpi_get_handle(handle, "_EJ0", &tmp); + if (ACPI_FAILURE(status)) + return -ENODEV; + + if ((ACPI_SUCCESS(acpi_get_handle(handle, "_GTF", &tmp))) || + (ACPI_SUCCESS(acpi_get_handle(handle, "_GTM", &tmp))) || + (ACPI_SUCCESS(acpi_get_handle(handle, "_STM", &tmp))) || + (ACPI_SUCCESS(acpi_get_handle(handle, "_SDD", &tmp)))) + return 0; + + if (acpi_get_parent(handle, &phandle)) + return -ENODEV; + + if ((ACPI_SUCCESS(acpi_get_handle(phandle, "_GTF", &tmp))) || + (ACPI_SUCCESS(acpi_get_handle(phandle, "_GTM", &tmp))) || + (ACPI_SUCCESS(acpi_get_handle(phandle, "_STM", &tmp))) || + (ACPI_SUCCESS(acpi_get_handle(phandle, "_SDD", &tmp)))) + return 0; + + return -ENODEV; +} + static void acpi_device_set_id(struct acpi_device *device, struct acpi_device *parent, acpi_handle handle, int type) @@ -812,6 +903,16 @@ static void acpi_device_set_id(struct acpi_device *device, device->pnp.bus_address = info->address; device->flags.bus_address = 1; } + + if(!(info->valid & (ACPI_VALID_HID | ACPI_VALID_CID))){ + status = acpi_video_bus_match(device); + if(ACPI_SUCCESS(status)) + hid = ACPI_VIDEO_HID; + + status = acpi_bay_match(device); + if (ACPI_SUCCESS(status)) + hid = ACPI_BAY_HID; + } break; case ACPI_BUS_TYPE_POWER: hid = ACPI_POWER_HID; @@ -888,86 +989,24 @@ static int acpi_device_set_context(struct acpi_device *device, int type) 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; - - if (!dev) return -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 result; - } - - result = dev->driver->ops.remove(dev, ACPI_BUS_REMOVAL_EJECT); - if (result) { - return result; - } - - atomic_dec(&dev->driver->references); - dev->driver = NULL; - acpi_driver_data(dev) = NULL; - } + dev->removal_type = ACPI_BUS_REMOVAL_EJECT; + device_release_driver(&dev->dev); if (!rmdevice) return 0; + /* + * unbind _ADR-Based Devices when hot removal + */ 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 0; @@ -975,7 +1014,8 @@ static int acpi_bus_remove(struct acpi_device *dev, int rmdevice) static int acpi_add_single_object(struct acpi_device **child, - struct acpi_device *parent, acpi_handle handle, int type) + struct acpi_device *parent, acpi_handle handle, int type, + struct acpi_bus_ops *ops) { int result = 0; struct acpi_device *device = NULL; @@ -992,6 +1032,8 @@ acpi_add_single_object(struct acpi_device **child, device->handle = handle; device->parent = parent; + device->bus_ops = *ops; /* workround for not call .start */ + acpi_device_get_busid(device, handle, type); @@ -1076,33 +1118,16 @@ acpi_add_single_object(struct acpi_device **child, if ((result = acpi_device_set_context(device, type))) goto end; - acpi_device_get_debug_info(device, handle, type); - - acpi_device_register(device, parent); + result = 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. + * Bind _ADR-Based Devices when hot add */ 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; @@ -1188,14 +1213,14 @@ static int acpi_bus_scan(struct acpi_device *start, struct acpi_bus_ops *ops) if (ops->acpi_op_add) status = acpi_add_single_object(&child, parent, - chandle, type); + chandle, type, ops); else status = acpi_bus_get_device(chandle, &child); if (ACPI_FAILURE(status)) continue; - if (ops->acpi_op_start) { + if (ops->acpi_op_start && !(ops->acpi_op_add)) { status = acpi_start_single_object(child); if (ACPI_FAILURE(status)) continue; @@ -1233,13 +1258,13 @@ acpi_bus_add(struct acpi_device **child, int result; struct acpi_bus_ops ops; + memset(&ops, 0, sizeof(ops)); + ops.acpi_op_add = 1; - result = acpi_add_single_object(child, parent, handle, type); - if (!result) { - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_add = 1; + result = acpi_add_single_object(child, parent, handle, type, &ops); + if (!result) result = acpi_bus_scan(*child, &ops); - } + return result; } @@ -1325,127 +1350,35 @@ static int acpi_bus_scan_fixed(struct acpi_device *root) { int result = 0; struct acpi_device *device = NULL; - + struct acpi_bus_ops ops; if (!root) return -ENODEV; + memset(&ops, 0, sizeof(ops)); + ops.acpi_op_add = 1; + ops.acpi_op_start = 1; + /* * Enumerate all fixed-feature devices. */ if ((acpi_gbl_FADT.flags & ACPI_FADT_POWER_BUTTON) == 0) { result = acpi_add_single_object(&device, acpi_root, NULL, - ACPI_BUS_TYPE_POWER_BUTTON); - if (!result) - result = acpi_start_single_object(device); + ACPI_BUS_TYPE_POWER_BUTTON, + &ops); } if ((acpi_gbl_FADT.flags & ACPI_FADT_SLEEP_BUTTON) == 0) { result = acpi_add_single_object(&device, acpi_root, NULL, - ACPI_BUS_TYPE_SLEEP_BUTTON); - if (!result) - result = acpi_start_single_object(device); + ACPI_BUS_TYPE_SLEEP_BUTTON, + &ops); } return result; } - -static inline struct acpi_device * to_acpi_dev(struct device * dev) -{ - return container_of(dev, struct acpi_device, dev); -} - - -static int root_suspend(struct acpi_device * acpi_dev, pm_message_t state) -{ - struct acpi_device * dev, * next; - int result; - - spin_lock(&acpi_device_lock); - list_for_each_entry_safe_reverse(dev, next, &acpi_device_list, g_list) { - if (dev->driver && dev->driver->ops.suspend) { - spin_unlock(&acpi_device_lock); - result = dev->driver->ops.suspend(dev, 0); - if (result) { - printk(KERN_ERR PREFIX "[%s - %s] Suspend failed: %d\n", - acpi_device_name(dev), - acpi_device_bid(dev), result); - } - spin_lock(&acpi_device_lock); - } - } - spin_unlock(&acpi_device_lock); - return 0; -} - - -static int acpi_device_suspend(struct device * dev, pm_message_t state) -{ - struct acpi_device * acpi_dev = to_acpi_dev(dev); - - /* - * For now, we should only register 1 generic device - - * the ACPI root device - and from there, we walk the - * tree of ACPI devices to suspend each one using the - * ACPI driver methods. - */ - if (acpi_dev->handle == ACPI_ROOT_OBJECT) - root_suspend(acpi_dev, state); - return 0; -} - - - -static int root_resume(struct acpi_device * acpi_dev) -{ - struct acpi_device * dev, * next; - int result; - - spin_lock(&acpi_device_lock); - list_for_each_entry_safe(dev, next, &acpi_device_list, g_list) { - if (dev->driver && dev->driver->ops.resume) { - spin_unlock(&acpi_device_lock); - result = dev->driver->ops.resume(dev, 0); - if (result) { - printk(KERN_ERR PREFIX "[%s - %s] resume failed: %d\n", - acpi_device_name(dev), - acpi_device_bid(dev), result); - } - spin_lock(&acpi_device_lock); - } - } - spin_unlock(&acpi_device_lock); - return 0; -} - - -static int acpi_device_resume(struct device * dev) -{ - struct acpi_device * acpi_dev = to_acpi_dev(dev); - - /* - * For now, we should only register 1 generic device - - * the ACPI root device - and from there, we walk the - * tree of ACPI devices to resume each one using the - * ACPI driver methods. - */ - if (acpi_dev->handle == ACPI_ROOT_OBJECT) - root_resume(acpi_dev); - return 0; -} - - -static struct bus_type acpi_bus_type = { - .name = "acpi", - .suspend = acpi_device_suspend, - .resume = acpi_device_resume, -}; - - - static int __init acpi_scan_init(void) { int result; @@ -1455,9 +1388,9 @@ static int __init acpi_scan_init(void) if (acpi_disabled) return 0; - result = kset_register(&acpi_namespace_kset); - if (result < 0) - printk(KERN_ERR PREFIX "kset_register error: %d\n", result); + memset(&ops, 0, sizeof(ops)); + ops.acpi_op_add = 1; + ops.acpi_op_start = 1; result = bus_register(&acpi_bus_type); if (result) { @@ -1469,32 +1402,16 @@ static int __init acpi_scan_init(void) * Create the root device in the bus's device tree */ result = acpi_add_single_object(&acpi_root, NULL, ACPI_ROOT_OBJECT, - ACPI_BUS_TYPE_SYSTEM); + ACPI_BUS_TYPE_SYSTEM, &ops); if (result) goto Done; - result = acpi_start_single_object(acpi_root); - if (result) - goto Done; - - acpi_root->dev.bus = &acpi_bus_type; - snprintf(acpi_root->dev.bus_id, BUS_ID_SIZE, "%s", acpi_bus_type.name); - result = device_register(&acpi_root->dev); - if (result) { - /* We don't want to quit even if we failed to add suspend/resume */ - printk(KERN_ERR PREFIX "Could not register device\n"); - } - /* * Enumerate devices in the ACPI namespace. */ result = acpi_bus_scan_fixed(acpi_root); - if (!result) { - memset(&ops, 0, sizeof(ops)); - ops.acpi_op_add = 1; - ops.acpi_op_start = 1; + if (!result) result = acpi_bus_scan(acpi_root, &ops); - } if (result) acpi_device_unregister(acpi_root, ACPI_BUS_REMOVAL_NORMAL); diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 2d425d84582..7147b0bdab0 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -32,6 +32,11 @@ #define _COMPONENT ACPI_SYSTEM_COMPONENT ACPI_MODULE_NAME("acpi_system") +#ifdef MODULE_PARAM_PREFIX +#undef MODULE_PARAM_PREFIX +#endif +#define MODULE_PARAM_PREFIX "acpi." + #define ACPI_SYSTEM_CLASS "system" #define ACPI_SYSTEM_DRIVER_NAME "ACPI System Driver" #define ACPI_SYSTEM_DEVICE_NAME "System" @@ -40,9 +45,23 @@ ACPI_MODULE_NAME("acpi_system") #define ACPI_SYSTEM_FILE_DSDT "dsdt" #define ACPI_SYSTEM_FILE_FADT "fadt" +/* + * Make ACPICA version work as module param + */ +static int param_get_acpica_version(char *buffer, struct kernel_param *kp) { + int result; + + result = sprintf(buffer, "%x", ACPI_CA_VERSION); + + return result; +} + +module_param_call(acpica_version, NULL, param_get_acpica_version, NULL, 0444); + /* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ +#ifdef CONFIG_ACPI_PROCFS static int acpi_system_read_info(struct seq_file *seq, void *offset) { @@ -62,6 +81,7 @@ static const struct file_operations acpi_system_info_ops = { .llseek = seq_lseek, .release = single_release, }; +#endif static ssize_t acpi_system_read_dsdt(struct file *, char __user *, size_t, loff_t *); @@ -125,6 +145,7 @@ static int __init acpi_system_init(void) if (acpi_disabled) return 0; +#ifdef CONFIG_ACPI_PROCFS /* 'info' [R] */ name = ACPI_SYSTEM_FILE_INFO; entry = create_proc_entry(name, S_IRUGO, acpi_root_dir); @@ -133,6 +154,7 @@ static int __init acpi_system_init(void) else { entry->proc_fops = &acpi_system_info_ops; } +#endif /* 'dsdt' [R] */ name = ACPI_SYSTEM_FILE_DSDT; @@ -156,7 +178,9 @@ static int __init acpi_system_init(void) Error: remove_proc_entry(ACPI_SYSTEM_FILE_FADT, acpi_root_dir); remove_proc_entry(ACPI_SYSTEM_FILE_DSDT, acpi_root_dir); +#ifdef CONFIG_ACPI_PROCFS remove_proc_entry(ACPI_SYSTEM_FILE_INFO, acpi_root_dir); +#endif error = -EFAULT; goto Done; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 40ddb4dd963..f76d3168c2b 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -82,7 +82,7 @@ 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_resume(struct acpi_device *device, int state); +static int acpi_thermal_resume(struct acpi_device *device); 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); @@ -1353,7 +1353,7 @@ static int acpi_thermal_remove(struct acpi_device *device, int type) return 0; } -static int acpi_thermal_resume(struct acpi_device *device, int state) +static int acpi_thermal_resume(struct acpi_device *device) { struct acpi_thermal *tz = NULL; int i; diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index 3d54680d033..e0b97add8c6 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -32,6 +32,7 @@ #include <linux/proc_fs.h> #include <linux/seq_file.h> +#include <linux/backlight.h> #include <asm/uaccess.h> #include <acpi/acpi_bus.h> @@ -56,6 +57,12 @@ #define ACPI_VIDEO_HEAD_INVALID (~0u - 1) #define ACPI_VIDEO_HEAD_END (~0u) +#define MAX_NAME_LEN 20 + +#define ACPI_VIDEO_DISPLAY_CRT 1 +#define ACPI_VIDEO_DISPLAY_TV 2 +#define ACPI_VIDEO_DISPLAY_DVI 3 +#define ACPI_VIDEO_DISPLAY_LCD 4 #define _COMPONENT ACPI_VIDEO_COMPONENT ACPI_MODULE_NAME("acpi_video") @@ -66,16 +73,14 @@ 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, + .ids = ACPI_VIDEO_HID, .ops = { .add = acpi_video_bus_add, .remove = acpi_video_bus_remove, - .match = acpi_video_bus_match, }, }; @@ -133,20 +138,21 @@ struct acpi_video_device_flags { u8 crt:1; u8 lcd:1; u8 tvout:1; + u8 dvi:1; u8 bios:1; u8 unknown:1; - u8 reserved:3; + u8 reserved:2; }; 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 _BQC:1; /* Get current 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 { @@ -163,6 +169,8 @@ struct acpi_video_device { struct acpi_video_bus *video; struct acpi_device *dev; struct acpi_video_device_brightness *brightness; + struct backlight_device *backlight; + struct backlight_properties *data; }; /* bus */ @@ -257,11 +265,35 @@ 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_device_lcd_set_level(struct acpi_video_device *device, + int level); +static int acpi_video_device_lcd_get_level_current( + struct acpi_video_device *device, + unsigned long *level); 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); +/*backlight device sysfs support*/ +static int acpi_video_get_brightness(struct backlight_device *bd) +{ + unsigned long cur_level; + struct acpi_video_device *vd = + (struct acpi_video_device *)class_get_devdata(&bd->class_dev); + acpi_video_device_lcd_get_level_current(vd, &cur_level); + return (int) cur_level; +} + +static int acpi_video_set_brightness(struct backlight_device *bd) +{ + int request_level = bd->props->brightness; + struct acpi_video_device *vd = + (struct acpi_video_device *)class_get_devdata(&bd->class_dev); + acpi_video_device_lcd_set_level(vd, request_level); + return 0; +} + /* -------------------------------------------------------------------------- Video Management -------------------------------------------------------------------------- */ @@ -499,6 +531,7 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) acpi_integer status; acpi_handle h_dummy1; int i; + u32 max_level = 0; union acpi_object *obj = NULL; struct acpi_video_device_brightness *br = NULL; @@ -514,6 +547,8 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_BCM", &h_dummy1))) { device->cap._BCM = 1; } + if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle,"_BQC",&h_dummy1))) + device->cap._BQC = 1; if (ACPI_SUCCESS(acpi_get_handle(device->dev->handle, "_DDC", &h_dummy1))) { device->cap._DDC = 1; } @@ -550,6 +585,8 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) continue; } br->levels[count] = (u32) o->integer.value; + if (br->levels[count] > max_level) + max_level = br->levels[count]; count++; } out: @@ -568,6 +605,37 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) kfree(obj); + if (device->cap._BCL && device->cap._BCM && device->cap._BQC){ + unsigned long tmp; + static int count = 0; + char *name; + struct backlight_properties *acpi_video_data; + + name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); + if (!name) + return; + + acpi_video_data = kzalloc( + sizeof(struct backlight_properties), + GFP_KERNEL); + if (!acpi_video_data){ + kfree(name); + return; + } + acpi_video_data->owner = THIS_MODULE; + acpi_video_data->get_brightness = + acpi_video_get_brightness; + acpi_video_data->update_status = + acpi_video_set_brightness; + sprintf(name, "acpi_video%d", count++); + device->data = acpi_video_data; + acpi_video_data->max_brightness = max_level; + acpi_video_device_lcd_get_level_current(device, &tmp); + acpi_video_data->brightness = (int)tmp; + device->backlight = backlight_device_register(name, + NULL, device, acpi_video_data); + kfree(name); + } return; } @@ -668,6 +736,8 @@ static int acpi_video_device_info_seq_show(struct seq_file *seq, void *offset) seq_printf(seq, "LCD\n"); else if (dev->flags.tvout) seq_printf(seq, "TVOUT\n"); + else if (dev->flags.dvi) + seq_printf(seq, "DVI\n"); else seq_printf(seq, "UNKNOWN\n"); @@ -1242,6 +1312,16 @@ static int acpi_video_bus_remove_fs(struct acpi_device *device) -------------------------------------------------------------------------- */ /* device interface */ +static struct acpi_video_device_attrib* +acpi_video_get_device_attr(struct acpi_video_bus *video, unsigned long device_id) +{ + int count; + + for(count = 0; count < video->attached_count; count++) + if((video->attached_array[count].value.int_val & 0xffff) == device_id) + return &(video->attached_array[count].value.attrib); + return NULL; +} static int acpi_video_bus_get_one_device(struct acpi_device *device, @@ -1250,7 +1330,7 @@ acpi_video_bus_get_one_device(struct acpi_device *device, unsigned long device_id; int status; struct acpi_video_device *data; - + struct acpi_video_device_attrib* attribute; if (!device || !video) return -EINVAL; @@ -1271,20 +1351,30 @@ acpi_video_bus_get_one_device(struct acpi_device *device, 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: + attribute = acpi_video_get_device_attr(video, device_id); + + if((attribute != NULL) && attribute->device_id_scheme) { + switch (attribute->display_type) { + case ACPI_VIDEO_DISPLAY_CRT: + data->flags.crt = 1; + break; + case ACPI_VIDEO_DISPLAY_TV: + data->flags.tvout = 1; + break; + case ACPI_VIDEO_DISPLAY_DVI: + data->flags.dvi = 1; + break; + case ACPI_VIDEO_DISPLAY_LCD: + data->flags.lcd = 1; + break; + default: + data->flags.unknown = 1; + break; + } + if(attribute->bios_can_detect) + data->flags.bios = 1; + } else data->flags.unknown = 1; - break; - } acpi_video_device_bind(video, data); acpi_video_device_find_cap(data); @@ -1588,7 +1678,10 @@ static int acpi_video_bus_put_one_device(struct acpi_video_device *device) status = acpi_remove_notify_handler(device->dev->handle, ACPI_DEVICE_NOTIFY, acpi_video_device_notify); - + if (device->backlight){ + backlight_device_unregister(device->backlight); + kfree(device->data); + } return 0; } @@ -1790,39 +1883,6 @@ static int acpi_video_bus_remove(struct acpi_device *device, int type) return 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; - - - if (!device || !driver) - return -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 0; - - /* Does this device able to retrieve a video ROM ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy1))) - return 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 0; - - return -ENODEV; -} - static int __init acpi_video_init(void) { int result = 0; |