diff options
Diffstat (limited to 'drivers/acpi/scan.c')
-rw-r--r-- | drivers/acpi/scan.c | 1220 |
1 files changed, 591 insertions, 629 deletions
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 2e0fc8c3b58..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); + } -static void acpi_device_release(struct kobject *kobj) + arg_list.count = 1; + arg_list.pointer = &arg; + arg.type = ACPI_TYPE_INTEGER; + arg.integer.value = 1; + + /* + * TBD: _EJD support. + */ + + status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL); + if (ACPI_FAILURE(status)) { + return (-ENODEV); + } + + return (0); +} + +static ssize_t +acpi_eject_store(struct 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); -typedef void acpi_device_sysfs_files(struct kobject *, - const struct attribute *); +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; -static void setup_sys_fs_device_files(struct acpi_device *dev, - acpi_device_sysfs_files * func); + /* + * 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; + } + + if(dev->flags.hardware_id) { + result = device_create_file(&dev->dev, &dev_attr_hid); + if(result) + goto end; + } + + /* + * 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; @@ -890,41 +991,22 @@ static int acpi_device_set_context(struct acpi_device *device, int type) 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; @@ -932,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; @@ -949,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); @@ -1033,31 +1118,16 @@ acpi_add_single_object(struct acpi_device **child, if ((result = acpi_device_set_context(device, type))) goto end; - 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; @@ -1143,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; @@ -1188,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; } @@ -1280,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_fadt.pwr_button == 0) { + 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_fadt.sleep_button == 0) { + 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; @@ -1410,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) { @@ -1424,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); |