linux/drivers/pnp/pnpacpi/core.c
<<
>>
Prefs
   1/*
   2 * pnpacpi -- PnP ACPI driver
   3 *
   4 * Copyright (c) 2004 Matthieu Castet <castet.matthieu@free.fr>
   5 * Copyright (c) 2004 Li Shaohua <shaohua.li@intel.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms of the GNU General Public License as published by the
   9 * Free Software Foundation; either version 2, or (at your option) any
  10 * later version.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  15 * General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  20 */
  21
  22#include <linux/acpi.h>
  23#include <linux/pnp.h>
  24#include <linux/slab.h>
  25#include <linux/mod_devicetable.h>
  26#include <acpi/acpi_bus.h>
  27
  28#include "../base.h"
  29#include "pnpacpi.h"
  30
  31static int num;
  32
  33/* We need only to blacklist devices that have already an acpi driver that
  34 * can't use pnp layer. We don't need to blacklist device that are directly
  35 * used by the kernel (PCI root, ...), as it is harmless and there were
  36 * already present in pnpbios. But there is an exception for devices that
  37 * have irqs (PIC, Timer) because we call acpi_register_gsi.
  38 * Finally, only devices that have a CRS method need to be in this list.
  39 */
  40static struct acpi_device_id excluded_id_list[] __initdata = {
  41        {"PNP0C09", 0},         /* EC */
  42        {"PNP0C0F", 0},         /* Link device */
  43        {"PNP0000", 0},         /* PIC */
  44        {"PNP0100", 0},         /* Timer */
  45        {"", 0},
  46};
  47
  48static inline int __init is_exclusive_device(struct acpi_device *dev)
  49{
  50        return (!acpi_match_device_ids(dev, excluded_id_list));
  51}
  52
  53/*
  54 * Compatible Device IDs
  55 */
  56#define TEST_HEX(c) \
  57        if (!(('0' <= (c) && (c) <= '9') || ('A' <= (c) && (c) <= 'F'))) \
  58                return 0
  59#define TEST_ALPHA(c) \
  60        if (!('@' <= (c) || (c) <= 'Z')) \
  61                return 0
  62static int __init ispnpidacpi(const char *id)
  63{
  64        TEST_ALPHA(id[0]);
  65        TEST_ALPHA(id[1]);
  66        TEST_ALPHA(id[2]);
  67        TEST_HEX(id[3]);
  68        TEST_HEX(id[4]);
  69        TEST_HEX(id[5]);
  70        TEST_HEX(id[6]);
  71        if (id[7] != '\0')
  72                return 0;
  73        return 1;
  74}
  75
  76static int pnpacpi_get_resources(struct pnp_dev *dev)
  77{
  78        pnp_dbg(&dev->dev, "get resources\n");
  79        return pnpacpi_parse_allocated_resource(dev);
  80}
  81
  82static int pnpacpi_set_resources(struct pnp_dev *dev)
  83{
  84        struct acpi_device *acpi_dev;
  85        acpi_handle handle;
  86        struct acpi_buffer buffer;
  87        int ret;
  88
  89        pnp_dbg(&dev->dev, "set resources\n");
  90
  91        handle = DEVICE_ACPI_HANDLE(&dev->dev);
  92        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
  93                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
  94                return -ENODEV;
  95        }
  96
  97        ret = pnpacpi_build_resource_template(dev, &buffer);
  98        if (ret)
  99                return ret;
 100        ret = pnpacpi_encode_resources(dev, &buffer);
 101        if (ret) {
 102                kfree(buffer.pointer);
 103                return ret;
 104        }
 105        if (ACPI_FAILURE(acpi_set_current_resources(handle, &buffer)))
 106                ret = -EINVAL;
 107        else if (acpi_bus_power_manageable(handle))
 108                ret = acpi_bus_set_power(handle, ACPI_STATE_D0);
 109        kfree(buffer.pointer);
 110        return ret;
 111}
 112
 113static int pnpacpi_disable_resources(struct pnp_dev *dev)
 114{
 115        struct acpi_device *acpi_dev;
 116        acpi_handle handle;
 117        int ret;
 118
 119        dev_dbg(&dev->dev, "disable resources\n");
 120
 121        handle = DEVICE_ACPI_HANDLE(&dev->dev);
 122        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
 123                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 124                return 0;
 125        }
 126
 127        /* acpi_unregister_gsi(pnp_irq(dev, 0)); */
 128        ret = 0;
 129        if (acpi_bus_power_manageable(handle))
 130                acpi_bus_set_power(handle, ACPI_STATE_D3);
 131                /* continue even if acpi_bus_set_power() fails */
 132        if (ACPI_FAILURE(acpi_evaluate_object(handle, "_DIS", NULL, NULL)))
 133                ret = -ENODEV;
 134        return ret;
 135}
 136
 137#ifdef CONFIG_ACPI_SLEEP
 138static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
 139{
 140        struct acpi_device *acpi_dev;
 141        acpi_handle handle;
 142
 143        handle = DEVICE_ACPI_HANDLE(&dev->dev);
 144        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
 145                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 146                return false;
 147        }
 148
 149        return acpi_bus_can_wakeup(handle);
 150}
 151
 152static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 153{
 154        struct acpi_device *acpi_dev;
 155        acpi_handle handle;
 156        int error = 0;
 157
 158        handle = DEVICE_ACPI_HANDLE(&dev->dev);
 159        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
 160                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 161                return 0;
 162        }
 163
 164        if (device_can_wakeup(&dev->dev)) {
 165                error = acpi_pm_device_sleep_wake(&dev->dev,
 166                                device_may_wakeup(&dev->dev));
 167                if (error)
 168                        return error;
 169        }
 170
 171        if (acpi_bus_power_manageable(handle)) {
 172                int power_state = acpi_pm_device_sleep_state(&dev->dev, NULL);
 173
 174                if (power_state < 0)
 175                        power_state = (state.event == PM_EVENT_ON) ?
 176                                        ACPI_STATE_D0 : ACPI_STATE_D3;
 177
 178                /*
 179                 * acpi_bus_set_power() often fails (keyboard port can't be
 180                 * powered-down?), and in any case, our return value is ignored
 181                 * by pnp_bus_suspend().  Hence we don't revert the wakeup
 182                 * setting if the set_power fails.
 183                 */
 184                error = acpi_bus_set_power(handle, power_state);
 185        }
 186
 187        return error;
 188}
 189
 190static int pnpacpi_resume(struct pnp_dev *dev)
 191{
 192        struct acpi_device *acpi_dev;
 193        acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
 194        int error = 0;
 195
 196        if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
 197                dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 198                return -ENODEV;
 199        }
 200
 201        if (device_may_wakeup(&dev->dev))
 202                acpi_pm_device_sleep_wake(&dev->dev, false);
 203
 204        if (acpi_bus_power_manageable(handle))
 205                error = acpi_bus_set_power(handle, ACPI_STATE_D0);
 206
 207        return error;
 208}
 209#endif
 210
 211struct pnp_protocol pnpacpi_protocol = {
 212        .name    = "Plug and Play ACPI",
 213        .get     = pnpacpi_get_resources,
 214        .set     = pnpacpi_set_resources,
 215        .disable = pnpacpi_disable_resources,
 216#ifdef CONFIG_ACPI_SLEEP
 217        .can_wakeup = pnpacpi_can_wakeup,
 218        .suspend = pnpacpi_suspend,
 219        .resume = pnpacpi_resume,
 220#endif
 221};
 222EXPORT_SYMBOL(pnpacpi_protocol);
 223
 224static char *__init pnpacpi_get_id(struct acpi_device *device)
 225{
 226        struct acpi_hardware_id *id;
 227
 228        list_for_each_entry(id, &device->pnp.ids, list) {
 229                if (ispnpidacpi(id->id))
 230                        return id->id;
 231        }
 232
 233        return NULL;
 234}
 235
 236static int __init pnpacpi_add_device(struct acpi_device *device)
 237{
 238        acpi_handle temp = NULL;
 239        acpi_status status;
 240        struct pnp_dev *dev;
 241        char *pnpid;
 242        struct acpi_hardware_id *id;
 243
 244        /*
 245         * If a PnPacpi device is not present , the device
 246         * driver should not be loaded.
 247         */
 248        status = acpi_get_handle(device->handle, "_CRS", &temp);
 249        if (ACPI_FAILURE(status))
 250                return 0;
 251
 252        pnpid = pnpacpi_get_id(device);
 253        if (!pnpid)
 254                return 0;
 255
 256        if (is_exclusive_device(device) || !device->status.present)
 257                return 0;
 258
 259        dev = pnp_alloc_dev(&pnpacpi_protocol, num, pnpid);
 260        if (!dev)
 261                return -ENOMEM;
 262
 263        dev->data = device;
 264        /* .enabled means the device can decode the resources */
 265        dev->active = device->status.enabled;
 266        status = acpi_get_handle(device->handle, "_SRS", &temp);
 267        if (ACPI_SUCCESS(status))
 268                dev->capabilities |= PNP_CONFIGURABLE;
 269        dev->capabilities |= PNP_READ;
 270        if (device->flags.dynamic_status && (dev->capabilities & PNP_CONFIGURABLE))
 271                dev->capabilities |= PNP_WRITE;
 272        if (device->flags.removable)
 273                dev->capabilities |= PNP_REMOVABLE;
 274        status = acpi_get_handle(device->handle, "_DIS", &temp);
 275        if (ACPI_SUCCESS(status))
 276                dev->capabilities |= PNP_DISABLE;
 277
 278        if (strlen(acpi_device_name(device)))
 279                strncpy(dev->name, acpi_device_name(device), sizeof(dev->name));
 280        else
 281                strncpy(dev->name, acpi_device_bid(device), sizeof(dev->name));
 282
 283        if (dev->active)
 284                pnpacpi_parse_allocated_resource(dev);
 285
 286        if (dev->capabilities & PNP_CONFIGURABLE)
 287                pnpacpi_parse_resource_option_data(dev);
 288
 289        list_for_each_entry(id, &device->pnp.ids, list) {
 290                if (!strcmp(id->id, pnpid))
 291                        continue;
 292                if (!ispnpidacpi(id->id))
 293                        continue;
 294                pnp_add_id(dev, id->id);
 295        }
 296
 297        /* clear out the damaged flags */
 298        if (!dev->active)
 299                pnp_init_resources(dev);
 300        pnp_add_device(dev);
 301        num++;
 302
 303        return AE_OK;
 304}
 305
 306static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
 307                                                     u32 lvl, void *context,
 308                                                     void **rv)
 309{
 310        struct acpi_device *device;
 311
 312        if (!acpi_bus_get_device(handle, &device))
 313                pnpacpi_add_device(device);
 314        else
 315                return AE_CTRL_DEPTH;
 316        return AE_OK;
 317}
 318
 319static int __init acpi_pnp_match(struct device *dev, void *_pnp)
 320{
 321        struct acpi_device *acpi = to_acpi_device(dev);
 322        struct pnp_dev *pnp = _pnp;
 323
 324        /* true means it matched */
 325        return !acpi_get_physical_device(acpi->handle)
 326            && compare_pnp_id(pnp->id, acpi_device_hid(acpi));
 327}
 328
 329static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle)
 330{
 331        struct device *adev;
 332        struct acpi_device *acpi;
 333
 334        adev = bus_find_device(&acpi_bus_type, NULL,
 335                               to_pnp_dev(dev), acpi_pnp_match);
 336        if (!adev)
 337                return -ENODEV;
 338
 339        acpi = to_acpi_device(adev);
 340        *handle = acpi->handle;
 341        put_device(adev);
 342        return 0;
 343}
 344
 345/* complete initialization of a PNPACPI device includes having
 346 * pnpdev->dev.archdata.acpi_handle point to its ACPI sibling.
 347 */
 348static struct acpi_bus_type __initdata acpi_pnp_bus = {
 349        .bus         = &pnp_bus_type,
 350        .find_device = acpi_pnp_find_device,
 351};
 352
 353int pnpacpi_disabled __initdata;
 354static int __init pnpacpi_init(void)
 355{
 356        if (acpi_disabled || pnpacpi_disabled) {
 357                printk(KERN_INFO "pnp: PnP ACPI: disabled\n");
 358                return 0;
 359        }
 360        printk(KERN_INFO "pnp: PnP ACPI init\n");
 361        pnp_register_protocol(&pnpacpi_protocol);
 362        register_acpi_bus_type(&acpi_pnp_bus);
 363        acpi_get_devices(NULL, pnpacpi_add_device_handler, NULL, NULL);
 364        printk(KERN_INFO "pnp: PnP ACPI: found %d devices\n", num);
 365        unregister_acpi_bus_type(&acpi_pnp_bus);
 366        pnp_platform_devices = 1;
 367        return 0;
 368}
 369
 370fs_initcall(pnpacpi_init);
 371
 372static int __init pnpacpi_setup(char *str)
 373{
 374        if (str == NULL)
 375                return 1;
 376        if (!strncmp(str, "off", 3))
 377                pnpacpi_disabled = 1;
 378        return 1;
 379}
 380
 381__setup("pnpacpi=", pnpacpi_setup);
 382