linux/drivers/acpi/glue.c
<<
>>
Prefs
   1/*
   2 * Link physical devices with ACPI devices support
   3 *
   4 * Copyright (c) 2005 David Shaohua Li <shaohua.li@intel.com>
   5 * Copyright (c) 2005 Intel Corp.
   6 *
   7 * This file is released under the GPLv2.
   8 */
   9#include <linux/init.h>
  10#include <linux/list.h>
  11#include <linux/device.h>
  12#include <linux/rwsem.h>
  13#include <linux/acpi.h>
  14
  15#include "internal.h"
  16
  17#define ACPI_GLUE_DEBUG 0
  18#if ACPI_GLUE_DEBUG
  19#define DBG(x...) printk(PREFIX x)
  20#else
  21#define DBG(x...) do { } while(0)
  22#endif
  23static LIST_HEAD(bus_type_list);
  24static DECLARE_RWSEM(bus_type_sem);
  25
  26int register_acpi_bus_type(struct acpi_bus_type *type)
  27{
  28        if (acpi_disabled)
  29                return -ENODEV;
  30        if (type && type->bus && type->find_device) {
  31                down_write(&bus_type_sem);
  32                list_add_tail(&type->list, &bus_type_list);
  33                up_write(&bus_type_sem);
  34                printk(KERN_INFO PREFIX "bus type %s registered\n",
  35                       type->bus->name);
  36                return 0;
  37        }
  38        return -ENODEV;
  39}
  40
  41int unregister_acpi_bus_type(struct acpi_bus_type *type)
  42{
  43        if (acpi_disabled)
  44                return 0;
  45        if (type) {
  46                down_write(&bus_type_sem);
  47                list_del_init(&type->list);
  48                up_write(&bus_type_sem);
  49                printk(KERN_INFO PREFIX "ACPI bus type %s unregistered\n",
  50                       type->bus->name);
  51                return 0;
  52        }
  53        return -ENODEV;
  54}
  55
  56static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
  57{
  58        struct acpi_bus_type *tmp, *ret = NULL;
  59
  60        down_read(&bus_type_sem);
  61        list_for_each_entry(tmp, &bus_type_list, list) {
  62                if (tmp->bus == type) {
  63                        ret = tmp;
  64                        break;
  65                }
  66        }
  67        up_read(&bus_type_sem);
  68        return ret;
  69}
  70
  71static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle)
  72{
  73        struct acpi_bus_type *tmp;
  74        int ret = -ENODEV;
  75
  76        down_read(&bus_type_sem);
  77        list_for_each_entry(tmp, &bus_type_list, list) {
  78                if (tmp->find_bridge && !tmp->find_bridge(dev, handle)) {
  79                        ret = 0;
  80                        break;
  81                }
  82        }
  83        up_read(&bus_type_sem);
  84        return ret;
  85}
  86
  87/* Get device's handler per its address under its parent */
  88struct acpi_find_child {
  89        acpi_handle handle;
  90        acpi_integer address;
  91};
  92
  93static acpi_status
  94do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv)
  95{
  96        acpi_status status;
  97        struct acpi_device_info *info;
  98        struct acpi_find_child *find = context;
  99
 100        status = acpi_get_object_info(handle, &info);
 101        if (ACPI_SUCCESS(status)) {
 102                if (info->address == find->address)
 103                        find->handle = handle;
 104                kfree(info);
 105        }
 106        return AE_OK;
 107}
 108
 109acpi_handle acpi_get_child(acpi_handle parent, acpi_integer address)
 110{
 111        struct acpi_find_child find = { NULL, address };
 112
 113        if (!parent)
 114                return NULL;
 115        acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
 116                            1, do_acpi_find_child, &find, NULL);
 117        return find.handle;
 118}
 119
 120EXPORT_SYMBOL(acpi_get_child);
 121
 122/* Link ACPI devices with physical devices */
 123static void acpi_glue_data_handler(acpi_handle handle,
 124                                   void *context)
 125{
 126        /* we provide an empty handler */
 127}
 128
 129/* Note: a success call will increase reference count by one */
 130struct device *acpi_get_physical_device(acpi_handle handle)
 131{
 132        acpi_status status;
 133        struct device *dev;
 134
 135        status = acpi_get_data(handle, acpi_glue_data_handler, (void **)&dev);
 136        if (ACPI_SUCCESS(status))
 137                return get_device(dev);
 138        return NULL;
 139}
 140
 141EXPORT_SYMBOL(acpi_get_physical_device);
 142
 143static int acpi_bind_one(struct device *dev, acpi_handle handle)
 144{
 145        struct acpi_device *acpi_dev;
 146        acpi_status status;
 147
 148        if (dev->archdata.acpi_handle) {
 149                dev_warn(dev, "Drivers changed 'acpi_handle'\n");
 150                return -EINVAL;
 151        }
 152        get_device(dev);
 153        status = acpi_attach_data(handle, acpi_glue_data_handler, dev);
 154        if (ACPI_FAILURE(status)) {
 155                put_device(dev);
 156                return -EINVAL;
 157        }
 158        dev->archdata.acpi_handle = handle;
 159
 160        status = acpi_bus_get_device(handle, &acpi_dev);
 161        if (!ACPI_FAILURE(status)) {
 162                int ret;
 163
 164                ret = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
 165                                "firmware_node");
 166                ret = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
 167                                "physical_node");
 168                if (acpi_dev->wakeup.flags.valid) {
 169                        device_set_wakeup_capable(dev, true);
 170                        device_set_wakeup_enable(dev,
 171                                                acpi_dev->wakeup.state.enabled);
 172                }
 173        }
 174
 175        return 0;
 176}
 177
 178static int acpi_unbind_one(struct device *dev)
 179{
 180        if (!dev->archdata.acpi_handle)
 181                return 0;
 182        if (dev == acpi_get_physical_device(dev->archdata.acpi_handle)) {
 183                struct acpi_device *acpi_dev;
 184
 185                /* acpi_get_physical_device increase refcnt by one */
 186                put_device(dev);
 187
 188                if (!acpi_bus_get_device(dev->archdata.acpi_handle,
 189                                        &acpi_dev)) {
 190                        sysfs_remove_link(&dev->kobj, "firmware_node");
 191                        sysfs_remove_link(&acpi_dev->dev.kobj, "physical_node");
 192                }
 193
 194                acpi_detach_data(dev->archdata.acpi_handle,
 195                                 acpi_glue_data_handler);
 196                dev->archdata.acpi_handle = NULL;
 197                /* acpi_bind_one increase refcnt by one */
 198                put_device(dev);
 199        } else {
 200                dev_err(dev, "Oops, 'acpi_handle' corrupt\n");
 201        }
 202        return 0;
 203}
 204
 205static int acpi_platform_notify(struct device *dev)
 206{
 207        struct acpi_bus_type *type;
 208        acpi_handle handle;
 209        int ret = -EINVAL;
 210
 211        if (!dev->bus || !dev->parent) {
 212                /* bridge devices genernally haven't bus or parent */
 213                ret = acpi_find_bridge_device(dev, &handle);
 214                goto end;
 215        }
 216        type = acpi_get_bus_type(dev->bus);
 217        if (!type) {
 218                DBG("No ACPI bus support for %s\n", dev_name(dev));
 219                ret = -EINVAL;
 220                goto end;
 221        }
 222        if ((ret = type->find_device(dev, &handle)) != 0)
 223                DBG("Can't get handler for %s\n", dev_name(dev));
 224      end:
 225        if (!ret)
 226                acpi_bind_one(dev, handle);
 227
 228#if ACPI_GLUE_DEBUG
 229        if (!ret) {
 230                struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 231
 232                acpi_get_name(dev->archdata.acpi_handle,
 233                              ACPI_FULL_PATHNAME, &buffer);
 234                DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
 235                kfree(buffer.pointer);
 236        } else
 237                DBG("Device %s -> No ACPI support\n", dev_name(dev));
 238#endif
 239
 240        return ret;
 241}
 242
 243static int acpi_platform_notify_remove(struct device *dev)
 244{
 245        acpi_unbind_one(dev);
 246        return 0;
 247}
 248
 249int __init init_acpi_device_notify(void)
 250{
 251        if (platform_notify || platform_notify_remove) {
 252                printk(KERN_ERR PREFIX "Can't use platform_notify\n");
 253                return 0;
 254        }
 255        platform_notify = acpi_platform_notify;
 256        platform_notify_remove = acpi_platform_notify_remove;
 257        return 0;
 258}
 259