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/export.h>
  10#include <linux/init.h>
  11#include <linux/list.h>
  12#include <linux/device.h>
  13#include <linux/slab.h>
  14#include <linux/rwsem.h>
  15#include <linux/acpi.h>
  16
  17#include "internal.h"
  18
  19#define ACPI_GLUE_DEBUG 0
  20#if ACPI_GLUE_DEBUG
  21#define DBG(fmt, ...)                                           \
  22        printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__)
  23#else
  24#define DBG(fmt, ...)                                           \
  25do {                                                            \
  26        if (0)                                                  \
  27                printk(KERN_DEBUG PREFIX fmt, ##__VA_ARGS__);   \
  28} while (0)
  29#endif
  30static LIST_HEAD(bus_type_list);
  31static DECLARE_RWSEM(bus_type_sem);
  32
  33#define PHYSICAL_NODE_STRING "physical_node"
  34
  35int register_acpi_bus_type(struct acpi_bus_type *type)
  36{
  37        if (acpi_disabled)
  38                return -ENODEV;
  39        if (type && type->bus && type->find_device) {
  40                down_write(&bus_type_sem);
  41                list_add_tail(&type->list, &bus_type_list);
  42                up_write(&bus_type_sem);
  43                printk(KERN_INFO PREFIX "bus type %s registered\n",
  44                       type->bus->name);
  45                return 0;
  46        }
  47        return -ENODEV;
  48}
  49EXPORT_SYMBOL_GPL(register_acpi_bus_type);
  50
  51int unregister_acpi_bus_type(struct acpi_bus_type *type)
  52{
  53        if (acpi_disabled)
  54                return 0;
  55        if (type) {
  56                down_write(&bus_type_sem);
  57                list_del_init(&type->list);
  58                up_write(&bus_type_sem);
  59                printk(KERN_INFO PREFIX "ACPI bus type %s unregistered\n",
  60                       type->bus->name);
  61                return 0;
  62        }
  63        return -ENODEV;
  64}
  65EXPORT_SYMBOL_GPL(unregister_acpi_bus_type);
  66
  67static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type)
  68{
  69        struct acpi_bus_type *tmp, *ret = NULL;
  70
  71        down_read(&bus_type_sem);
  72        list_for_each_entry(tmp, &bus_type_list, list) {
  73                if (tmp->bus == type) {
  74                        ret = tmp;
  75                        break;
  76                }
  77        }
  78        up_read(&bus_type_sem);
  79        return ret;
  80}
  81
  82static int acpi_find_bridge_device(struct device *dev, acpi_handle * handle)
  83{
  84        struct acpi_bus_type *tmp;
  85        int ret = -ENODEV;
  86
  87        down_read(&bus_type_sem);
  88        list_for_each_entry(tmp, &bus_type_list, list) {
  89                if (tmp->find_bridge && !tmp->find_bridge(dev, handle)) {
  90                        ret = 0;
  91                        break;
  92                }
  93        }
  94        up_read(&bus_type_sem);
  95        return ret;
  96}
  97
  98/* Get device's handler per its address under its parent */
  99struct acpi_find_child {
 100        acpi_handle handle;
 101        u64 address;
 102};
 103
 104static acpi_status
 105do_acpi_find_child(acpi_handle handle, u32 lvl, void *context, void **rv)
 106{
 107        acpi_status status;
 108        struct acpi_device_info *info;
 109        struct acpi_find_child *find = context;
 110
 111        status = acpi_get_object_info(handle, &info);
 112        if (ACPI_SUCCESS(status)) {
 113                if ((info->address == find->address)
 114                        && (info->valid & ACPI_VALID_ADR))
 115                        find->handle = handle;
 116                kfree(info);
 117        }
 118        return AE_OK;
 119}
 120
 121acpi_handle acpi_get_child(acpi_handle parent, u64 address)
 122{
 123        struct acpi_find_child find = { NULL, address };
 124
 125        if (!parent)
 126                return NULL;
 127        acpi_walk_namespace(ACPI_TYPE_DEVICE, parent,
 128                            1, do_acpi_find_child, NULL, &find, NULL);
 129        return find.handle;
 130}
 131
 132EXPORT_SYMBOL(acpi_get_child);
 133
 134static int acpi_bind_one(struct device *dev, acpi_handle handle)
 135{
 136        struct acpi_device *acpi_dev;
 137        acpi_status status;
 138        struct acpi_device_physical_node *physical_node, *pn;
 139        char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2];
 140        int retval = -EINVAL;
 141
 142        if (ACPI_HANDLE(dev)) {
 143                if (handle) {
 144                        dev_warn(dev, "ACPI handle is already set\n");
 145                        return -EINVAL;
 146                } else {
 147                        handle = ACPI_HANDLE(dev);
 148                }
 149        }
 150        if (!handle)
 151                return -EINVAL;
 152
 153        get_device(dev);
 154        status = acpi_bus_get_device(handle, &acpi_dev);
 155        if (ACPI_FAILURE(status))
 156                goto err;
 157
 158        physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
 159        if (!physical_node) {
 160                retval = -ENOMEM;
 161                goto err;
 162        }
 163
 164        mutex_lock(&acpi_dev->physical_node_lock);
 165
 166        /* Sanity check. */
 167        list_for_each_entry(pn, &acpi_dev->physical_node_list, node)
 168                if (pn->dev == dev) {
 169                        dev_warn(dev, "Already associated with ACPI node\n");
 170                        goto err_free;
 171                }
 172
 173        /* allocate physical node id according to physical_node_id_bitmap */
 174        physical_node->node_id =
 175                find_first_zero_bit(acpi_dev->physical_node_id_bitmap,
 176                ACPI_MAX_PHYSICAL_NODE);
 177        if (physical_node->node_id >= ACPI_MAX_PHYSICAL_NODE) {
 178                retval = -ENOSPC;
 179                goto err_free;
 180        }
 181
 182        set_bit(physical_node->node_id, acpi_dev->physical_node_id_bitmap);
 183        physical_node->dev = dev;
 184        list_add_tail(&physical_node->node, &acpi_dev->physical_node_list);
 185        acpi_dev->physical_node_count++;
 186
 187        mutex_unlock(&acpi_dev->physical_node_lock);
 188
 189        if (!ACPI_HANDLE(dev))
 190                ACPI_HANDLE_SET(dev, acpi_dev->handle);
 191
 192        if (!physical_node->node_id)
 193                strcpy(physical_node_name, PHYSICAL_NODE_STRING);
 194        else
 195                sprintf(physical_node_name,
 196                        "physical_node%d", physical_node->node_id);
 197        retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
 198                        physical_node_name);
 199        retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
 200                "firmware_node");
 201
 202        if (acpi_dev->wakeup.flags.valid)
 203                device_set_wakeup_capable(dev, true);
 204
 205        return 0;
 206
 207 err:
 208        ACPI_HANDLE_SET(dev, NULL);
 209        put_device(dev);
 210        return retval;
 211
 212 err_free:
 213        mutex_unlock(&acpi_dev->physical_node_lock);
 214        kfree(physical_node);
 215        goto err;
 216}
 217
 218static int acpi_unbind_one(struct device *dev)
 219{
 220        struct acpi_device_physical_node *entry;
 221        struct acpi_device *acpi_dev;
 222        acpi_status status;
 223        struct list_head *node, *next;
 224
 225        if (!ACPI_HANDLE(dev))
 226                return 0;
 227
 228        status = acpi_bus_get_device(ACPI_HANDLE(dev), &acpi_dev);
 229        if (ACPI_FAILURE(status))
 230                goto err;
 231
 232        mutex_lock(&acpi_dev->physical_node_lock);
 233        list_for_each_safe(node, next, &acpi_dev->physical_node_list) {
 234                char physical_node_name[sizeof(PHYSICAL_NODE_STRING) + 2];
 235
 236                entry = list_entry(node, struct acpi_device_physical_node,
 237                        node);
 238                if (entry->dev != dev)
 239                        continue;
 240
 241                list_del(node);
 242                clear_bit(entry->node_id, acpi_dev->physical_node_id_bitmap);
 243
 244                acpi_dev->physical_node_count--;
 245
 246                if (!entry->node_id)
 247                        strcpy(physical_node_name, PHYSICAL_NODE_STRING);
 248                else
 249                        sprintf(physical_node_name,
 250                                "physical_node%d", entry->node_id);
 251
 252                sysfs_remove_link(&acpi_dev->dev.kobj, physical_node_name);
 253                sysfs_remove_link(&dev->kobj, "firmware_node");
 254                ACPI_HANDLE_SET(dev, NULL);
 255                /* acpi_bind_one increase refcnt by one */
 256                put_device(dev);
 257                kfree(entry);
 258        }
 259        mutex_unlock(&acpi_dev->physical_node_lock);
 260
 261        return 0;
 262
 263err:
 264        dev_err(dev, "Oops, 'acpi_handle' corrupt\n");
 265        return -EINVAL;
 266}
 267
 268static int acpi_platform_notify(struct device *dev)
 269{
 270        struct acpi_bus_type *type;
 271        acpi_handle handle;
 272        int ret = -EINVAL;
 273
 274        ret = acpi_bind_one(dev, NULL);
 275        if (!ret)
 276                goto out;
 277
 278        if (!dev->bus || !dev->parent) {
 279                /* bridge devices genernally haven't bus or parent */
 280                ret = acpi_find_bridge_device(dev, &handle);
 281                goto end;
 282        }
 283        type = acpi_get_bus_type(dev->bus);
 284        if (!type) {
 285                DBG("No ACPI bus support for %s\n", dev_name(dev));
 286                ret = -EINVAL;
 287                goto end;
 288        }
 289        if ((ret = type->find_device(dev, &handle)) != 0)
 290                DBG("Can't get handler for %s\n", dev_name(dev));
 291 end:
 292        if (!ret)
 293                acpi_bind_one(dev, handle);
 294
 295 out:
 296#if ACPI_GLUE_DEBUG
 297        if (!ret) {
 298                struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 299
 300                acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer);
 301                DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
 302                kfree(buffer.pointer);
 303        } else
 304                DBG("Device %s -> No ACPI support\n", dev_name(dev));
 305#endif
 306
 307        return ret;
 308}
 309
 310static int acpi_platform_notify_remove(struct device *dev)
 311{
 312        acpi_unbind_one(dev);
 313        return 0;
 314}
 315
 316int __init init_acpi_device_notify(void)
 317{
 318        if (platform_notify || platform_notify_remove) {
 319                printk(KERN_ERR PREFIX "Can't use platform_notify\n");
 320                return 0;
 321        }
 322        platform_notify = acpi_platform_notify;
 323        platform_notify_remove = acpi_platform_notify_remove;
 324        return 0;
 325}
 326
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.