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#define PHYSICAL_NODE_NAME_SIZE (sizeof(PHYSICAL_NODE_STRING) + 10)
  35
  36int register_acpi_bus_type(struct acpi_bus_type *type)
  37{
  38        if (acpi_disabled)
  39                return -ENODEV;
  40        if (type && type->match && type->find_device) {
  41                down_write(&bus_type_sem);
  42                list_add_tail(&type->list, &bus_type_list);
  43                up_write(&bus_type_sem);
  44                printk(KERN_INFO PREFIX "bus type %s registered\n", type->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 "bus type %s unregistered\n",
  60                       type->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 device *dev)
  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->match(dev)) {
  74                        ret = tmp;
  75                        break;
  76                }
  77        }
  78        up_read(&bus_type_sem);
  79        return ret;
  80}
  81
  82#define FIND_CHILD_MIN_SCORE    1
  83#define FIND_CHILD_MAX_SCORE    2
  84
  85static acpi_status acpi_dev_present(acpi_handle handle, u32 lvl_not_used,
  86                                  void *not_used, void **ret_p)
  87{
  88        struct acpi_device *adev = NULL;
  89
  90        acpi_bus_get_device(handle, &adev);
  91        if (adev) {
  92                *ret_p = handle;
  93                return AE_CTRL_TERMINATE;
  94        }
  95        return AE_OK;
  96}
  97
  98static int do_find_child_checks(acpi_handle handle, bool is_bridge)
  99{
 100        bool sta_present = true;
 101        unsigned long long sta;
 102        acpi_status status;
 103
 104        status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
 105        if (status == AE_NOT_FOUND)
 106                sta_present = false;
 107        else if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_ENABLED))
 108                return -ENODEV;
 109
 110        if (is_bridge) {
 111                void *test = NULL;
 112
 113                /* Check if this object has at least one child device. */
 114                acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
 115                                    acpi_dev_present, NULL, NULL, &test);
 116                if (!test)
 117                        return -ENODEV;
 118        }
 119        return sta_present ? FIND_CHILD_MAX_SCORE : FIND_CHILD_MIN_SCORE;
 120}
 121
 122struct find_child_context {
 123        u64 addr;
 124        bool is_bridge;
 125        acpi_handle ret;
 126        int ret_score;
 127};
 128
 129static acpi_status do_find_child(acpi_handle handle, u32 lvl_not_used,
 130                                 void *data, void **not_used)
 131{
 132        struct find_child_context *context = data;
 133        unsigned long long addr;
 134        acpi_status status;
 135        int score;
 136
 137        status = acpi_evaluate_integer(handle, METHOD_NAME__ADR, NULL, &addr);
 138        if (ACPI_FAILURE(status) || addr != context->addr)
 139                return AE_OK;
 140
 141        if (!context->ret) {
 142                /* This is the first matching object.  Save its handle. */
 143                context->ret = handle;
 144                return AE_OK;
 145        }
 146        /*
 147         * There is more than one matching object with the same _ADR value.
 148         * That really is unexpected, so we are kind of beyond the scope of the
 149         * spec here.  We have to choose which one to return, though.
 150         *
 151         * First, check if the previously found object is good enough and return
 152         * its handle if so.  Second, check the same for the object that we've
 153         * just found.
 154         */
 155        if (!context->ret_score) {
 156                score = do_find_child_checks(context->ret, context->is_bridge);
 157                if (score == FIND_CHILD_MAX_SCORE)
 158                        return AE_CTRL_TERMINATE;
 159                else
 160                        context->ret_score = score;
 161        }
 162        score = do_find_child_checks(handle, context->is_bridge);
 163        if (score == FIND_CHILD_MAX_SCORE) {
 164                context->ret = handle;
 165                return AE_CTRL_TERMINATE;
 166        } else if (score > context->ret_score) {
 167                context->ret = handle;
 168                context->ret_score = score;
 169        }
 170        return AE_OK;
 171}
 172
 173acpi_handle acpi_find_child(acpi_handle parent, u64 addr, bool is_bridge)
 174{
 175        if (parent) {
 176                struct find_child_context context = {
 177                        .addr = addr,
 178                        .is_bridge = is_bridge,
 179                };
 180
 181                acpi_walk_namespace(ACPI_TYPE_DEVICE, parent, 1, do_find_child,
 182                                    NULL, &context, NULL);
 183                return context.ret;
 184        }
 185        return NULL;
 186}
 187EXPORT_SYMBOL_GPL(acpi_find_child);
 188
 189static void acpi_physnode_link_name(char *buf, unsigned int node_id)
 190{
 191        if (node_id > 0)
 192                snprintf(buf, PHYSICAL_NODE_NAME_SIZE,
 193                         PHYSICAL_NODE_STRING "%u", node_id);
 194        else
 195                strcpy(buf, PHYSICAL_NODE_STRING);
 196}
 197
 198int acpi_bind_one(struct device *dev, acpi_handle handle)
 199{
 200        struct acpi_device *acpi_dev;
 201        acpi_status status;
 202        struct acpi_device_physical_node *physical_node, *pn;
 203        char physical_node_name[PHYSICAL_NODE_NAME_SIZE];
 204        struct list_head *physnode_list;
 205        unsigned int node_id;
 206        int retval = -EINVAL;
 207
 208        if (ACPI_HANDLE(dev)) {
 209                if (handle) {
 210                        dev_warn(dev, "ACPI handle is already set\n");
 211                        return -EINVAL;
 212                } else {
 213                        handle = ACPI_HANDLE(dev);
 214                }
 215        }
 216        if (!handle)
 217                return -EINVAL;
 218
 219        get_device(dev);
 220        status = acpi_bus_get_device(handle, &acpi_dev);
 221        if (ACPI_FAILURE(status))
 222                goto err;
 223
 224        physical_node = kzalloc(sizeof(*physical_node), GFP_KERNEL);
 225        if (!physical_node) {
 226                retval = -ENOMEM;
 227                goto err;
 228        }
 229
 230        mutex_lock(&acpi_dev->physical_node_lock);
 231
 232        /*
 233         * Keep the list sorted by node_id so that the IDs of removed nodes can
 234         * be recycled easily.
 235         */
 236        physnode_list = &acpi_dev->physical_node_list;
 237        node_id = 0;
 238        list_for_each_entry(pn, &acpi_dev->physical_node_list, node) {
 239                /* Sanity check. */
 240                if (pn->dev == dev) {
 241                        mutex_unlock(&acpi_dev->physical_node_lock);
 242
 243                        dev_warn(dev, "Already associated with ACPI node\n");
 244                        kfree(physical_node);
 245                        if (ACPI_HANDLE(dev) != handle)
 246                                goto err;
 247
 248                        put_device(dev);
 249                        return 0;
 250                }
 251                if (pn->node_id == node_id) {
 252                        physnode_list = &pn->node;
 253                        node_id++;
 254                }
 255        }
 256
 257        physical_node->node_id = node_id;
 258        physical_node->dev = dev;
 259        list_add(&physical_node->node, physnode_list);
 260        acpi_dev->physical_node_count++;
 261
 262        if (!ACPI_HANDLE(dev))
 263                ACPI_HANDLE_SET(dev, acpi_dev->handle);
 264
 265        acpi_physnode_link_name(physical_node_name, node_id);
 266        retval = sysfs_create_link(&acpi_dev->dev.kobj, &dev->kobj,
 267                                   physical_node_name);
 268        if (retval)
 269                dev_err(&acpi_dev->dev, "Failed to create link %s (%d)\n",
 270                        physical_node_name, retval);
 271
 272        retval = sysfs_create_link(&dev->kobj, &acpi_dev->dev.kobj,
 273                                   "firmware_node");
 274        if (retval)
 275                dev_err(dev, "Failed to create link firmware_node (%d)\n",
 276                        retval);
 277
 278        mutex_unlock(&acpi_dev->physical_node_lock);
 279
 280        if (acpi_dev->wakeup.flags.valid)
 281                device_set_wakeup_capable(dev, true);
 282
 283        return 0;
 284
 285 err:
 286        ACPI_HANDLE_SET(dev, NULL);
 287        put_device(dev);
 288        return retval;
 289}
 290EXPORT_SYMBOL_GPL(acpi_bind_one);
 291
 292int acpi_unbind_one(struct device *dev)
 293{
 294        struct acpi_device_physical_node *entry;
 295        struct acpi_device *acpi_dev;
 296        acpi_status status;
 297
 298        if (!ACPI_HANDLE(dev))
 299                return 0;
 300
 301        status = acpi_bus_get_device(ACPI_HANDLE(dev), &acpi_dev);
 302        if (ACPI_FAILURE(status)) {
 303                dev_err(dev, "Oops, ACPI handle corrupt in %s()\n", __func__);
 304                return -EINVAL;
 305        }
 306
 307        mutex_lock(&acpi_dev->physical_node_lock);
 308
 309        list_for_each_entry(entry, &acpi_dev->physical_node_list, node)
 310                if (entry->dev == dev) {
 311                        char physnode_name[PHYSICAL_NODE_NAME_SIZE];
 312
 313                        list_del(&entry->node);
 314                        acpi_dev->physical_node_count--;
 315
 316                        acpi_physnode_link_name(physnode_name, entry->node_id);
 317                        sysfs_remove_link(&acpi_dev->dev.kobj, physnode_name);
 318                        sysfs_remove_link(&dev->kobj, "firmware_node");
 319                        ACPI_HANDLE_SET(dev, NULL);
 320                        /* acpi_bind_one() increase refcnt by one. */
 321                        put_device(dev);
 322                        kfree(entry);
 323                        break;
 324                }
 325
 326        mutex_unlock(&acpi_dev->physical_node_lock);
 327        return 0;
 328}
 329EXPORT_SYMBOL_GPL(acpi_unbind_one);
 330
 331static int acpi_platform_notify(struct device *dev)
 332{
 333        struct acpi_bus_type *type = acpi_get_bus_type(dev);
 334        acpi_handle handle;
 335        int ret;
 336
 337        ret = acpi_bind_one(dev, NULL);
 338        if (ret && type) {
 339                ret = type->find_device(dev, &handle);
 340                if (ret) {
 341                        DBG("Unable to get handle for %s\n", dev_name(dev));
 342                        goto out;
 343                }
 344                ret = acpi_bind_one(dev, handle);
 345                if (ret)
 346                        goto out;
 347        }
 348
 349        if (type && type->setup)
 350                type->setup(dev);
 351
 352 out:
 353#if ACPI_GLUE_DEBUG
 354        if (!ret) {
 355                struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
 356
 357                acpi_get_name(ACPI_HANDLE(dev), ACPI_FULL_PATHNAME, &buffer);
 358                DBG("Device %s -> %s\n", dev_name(dev), (char *)buffer.pointer);
 359                kfree(buffer.pointer);
 360        } else
 361                DBG("Device %s -> No ACPI support\n", dev_name(dev));
 362#endif
 363
 364        return ret;
 365}
 366
 367static int acpi_platform_notify_remove(struct device *dev)
 368{
 369        struct acpi_bus_type *type;
 370
 371        type = acpi_get_bus_type(dev);
 372        if (type && type->cleanup)
 373                type->cleanup(dev);
 374
 375        acpi_unbind_one(dev);
 376        return 0;
 377}
 378
 379int __init init_acpi_device_notify(void)
 380{
 381        if (platform_notify || platform_notify_remove) {
 382                printk(KERN_ERR PREFIX "Can't use platform_notify\n");
 383                return 0;
 384        }
 385        platform_notify = acpi_platform_notify;
 386        platform_notify_remove = acpi_platform_notify_remove;
 387        return 0;
 388}
 389
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.