linux/drivers/pci/hotplug/pci_hotplug_core.c
<<
>>
Prefs
   1/*
   2 * PCI HotPlug Controller Core
   3 *
   4 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
   5 * Copyright (C) 2001-2002 IBM Corp.
   6 *
   7 * All rights reserved.
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License, or (at
  12 * your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful, but
  15 * WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  17 * NON INFRINGEMENT.  See the GNU General Public License for more
  18 * details.
  19 *
  20 * You should have received a copy of the GNU General Public License
  21 * along with this program; if not, write to the Free Software
  22 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  23 *
  24 * Send feedback to <kristen.c.accardi@intel.com>
  25 *
  26 * Authors:
  27 *   Greg Kroah-Hartman <greg@kroah.com>
  28 *   Scott Murray <scottm@somanetworks.com>
  29 */
  30
  31#include <linux/module.h>       /* try_module_get & module_put */
  32#include <linux/moduleparam.h>
  33#include <linux/kernel.h>
  34#include <linux/types.h>
  35#include <linux/list.h>
  36#include <linux/kobject.h>
  37#include <linux/sysfs.h>
  38#include <linux/pagemap.h>
  39#include <linux/init.h>
  40#include <linux/mount.h>
  41#include <linux/namei.h>
  42#include <linux/mutex.h>
  43#include <linux/pci.h>
  44#include <linux/pci_hotplug.h>
  45#include <linux/uaccess.h>
  46#include "../pci.h"
  47#include "cpci_hotplug.h"
  48
  49#define MY_NAME "pci_hotplug"
  50
  51#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)
  52#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
  53#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
  54#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
  55
  56/* local variables */
  57static bool debug;
  58
  59static LIST_HEAD(pci_hotplug_slot_list);
  60static DEFINE_MUTEX(pci_hp_mutex);
  61
  62/* Weee, fun with macros... */
  63#define GET_STATUS(name, type)  \
  64static int get_##name(struct hotplug_slot *slot, type *value)           \
  65{                                                                       \
  66        struct hotplug_slot_ops *ops = slot->ops;                       \
  67        int retval = 0;                                                 \
  68        if (!try_module_get(ops->owner))                                \
  69                return -ENODEV;                                         \
  70        if (ops->get_##name)                                            \
  71                retval = ops->get_##name(slot, value);                  \
  72        else                                                            \
  73                *value = slot->info->name;                              \
  74        module_put(ops->owner);                                         \
  75        return retval;                                                  \
  76}
  77
  78GET_STATUS(power_status, u8)
  79GET_STATUS(attention_status, u8)
  80GET_STATUS(latch_status, u8)
  81GET_STATUS(adapter_status, u8)
  82
  83static ssize_t power_read_file(struct pci_slot *pci_slot, char *buf)
  84{
  85        int retval;
  86        u8 value;
  87
  88        retval = get_power_status(pci_slot->hotplug, &value);
  89        if (retval)
  90                return retval;
  91
  92        return sprintf(buf, "%d\n", value);
  93}
  94
  95static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
  96                                size_t count)
  97{
  98        struct hotplug_slot *slot = pci_slot->hotplug;
  99        unsigned long lpower;
 100        u8 power;
 101        int retval = 0;
 102
 103        lpower = simple_strtoul(buf, NULL, 10);
 104        power = (u8)(lpower & 0xff);
 105        dbg("power = %d\n", power);
 106
 107        if (!try_module_get(slot->ops->owner)) {
 108                retval = -ENODEV;
 109                goto exit;
 110        }
 111        switch (power) {
 112        case 0:
 113                if (slot->ops->disable_slot)
 114                        retval = slot->ops->disable_slot(slot);
 115                break;
 116
 117        case 1:
 118                if (slot->ops->enable_slot)
 119                        retval = slot->ops->enable_slot(slot);
 120                break;
 121
 122        default:
 123                err("Illegal value specified for power\n");
 124                retval = -EINVAL;
 125        }
 126        module_put(slot->ops->owner);
 127
 128exit:
 129        if (retval)
 130                return retval;
 131        return count;
 132}
 133
 134static struct pci_slot_attribute hotplug_slot_attr_power = {
 135        .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
 136        .show = power_read_file,
 137        .store = power_write_file
 138};
 139
 140static ssize_t attention_read_file(struct pci_slot *pci_slot, char *buf)
 141{
 142        int retval;
 143        u8 value;
 144
 145        retval = get_attention_status(pci_slot->hotplug, &value);
 146        if (retval)
 147                return retval;
 148
 149        return sprintf(buf, "%d\n", value);
 150}
 151
 152static ssize_t attention_write_file(struct pci_slot *pci_slot, const char *buf,
 153                                    size_t count)
 154{
 155        struct hotplug_slot_ops *ops = pci_slot->hotplug->ops;
 156        unsigned long lattention;
 157        u8 attention;
 158        int retval = 0;
 159
 160        lattention = simple_strtoul(buf, NULL, 10);
 161        attention = (u8)(lattention & 0xff);
 162        dbg(" - attention = %d\n", attention);
 163
 164        if (!try_module_get(ops->owner)) {
 165                retval = -ENODEV;
 166                goto exit;
 167        }
 168        if (ops->set_attention_status)
 169                retval = ops->set_attention_status(pci_slot->hotplug, attention);
 170        module_put(ops->owner);
 171
 172exit:
 173        if (retval)
 174                return retval;
 175        return count;
 176}
 177
 178static struct pci_slot_attribute hotplug_slot_attr_attention = {
 179        .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
 180        .show = attention_read_file,
 181        .store = attention_write_file
 182};
 183
 184static ssize_t latch_read_file(struct pci_slot *pci_slot, char *buf)
 185{
 186        int retval;
 187        u8 value;
 188
 189        retval = get_latch_status(pci_slot->hotplug, &value);
 190        if (retval)
 191                return retval;
 192
 193        return sprintf(buf, "%d\n", value);
 194}
 195
 196static struct pci_slot_attribute hotplug_slot_attr_latch = {
 197        .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
 198        .show = latch_read_file,
 199};
 200
 201static ssize_t presence_read_file(struct pci_slot *pci_slot, char *buf)
 202{
 203        int retval;
 204        u8 value;
 205
 206        retval = get_adapter_status(pci_slot->hotplug, &value);
 207        if (retval)
 208                return retval;
 209
 210        return sprintf(buf, "%d\n", value);
 211}
 212
 213static struct pci_slot_attribute hotplug_slot_attr_presence = {
 214        .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
 215        .show = presence_read_file,
 216};
 217
 218static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
 219                               size_t count)
 220{
 221        struct hotplug_slot *slot = pci_slot->hotplug;
 222        unsigned long ltest;
 223        u32 test;
 224        int retval = 0;
 225
 226        ltest = simple_strtoul(buf, NULL, 10);
 227        test = (u32)(ltest & 0xffffffff);
 228        dbg("test = %d\n", test);
 229
 230        if (!try_module_get(slot->ops->owner)) {
 231                retval = -ENODEV;
 232                goto exit;
 233        }
 234        if (slot->ops->hardware_test)
 235                retval = slot->ops->hardware_test(slot, test);
 236        module_put(slot->ops->owner);
 237
 238exit:
 239        if (retval)
 240                return retval;
 241        return count;
 242}
 243
 244static struct pci_slot_attribute hotplug_slot_attr_test = {
 245        .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
 246        .store = test_write_file
 247};
 248
 249static bool has_power_file(struct pci_slot *pci_slot)
 250{
 251        struct hotplug_slot *slot = pci_slot->hotplug;
 252
 253        if ((!slot) || (!slot->ops))
 254                return false;
 255        if ((slot->ops->enable_slot) ||
 256            (slot->ops->disable_slot) ||
 257            (slot->ops->get_power_status))
 258                return true;
 259        return false;
 260}
 261
 262static bool has_attention_file(struct pci_slot *pci_slot)
 263{
 264        struct hotplug_slot *slot = pci_slot->hotplug;
 265
 266        if ((!slot) || (!slot->ops))
 267                return false;
 268        if ((slot->ops->set_attention_status) ||
 269            (slot->ops->get_attention_status))
 270                return true;
 271        return false;
 272}
 273
 274static bool has_latch_file(struct pci_slot *pci_slot)
 275{
 276        struct hotplug_slot *slot = pci_slot->hotplug;
 277
 278        if ((!slot) || (!slot->ops))
 279                return false;
 280        if (slot->ops->get_latch_status)
 281                return true;
 282        return false;
 283}
 284
 285static bool has_adapter_file(struct pci_slot *pci_slot)
 286{
 287        struct hotplug_slot *slot = pci_slot->hotplug;
 288
 289        if ((!slot) || (!slot->ops))
 290                return false;
 291        if (slot->ops->get_adapter_status)
 292                return true;
 293        return false;
 294}
 295
 296static bool has_test_file(struct pci_slot *pci_slot)
 297{
 298        struct hotplug_slot *slot = pci_slot->hotplug;
 299
 300        if ((!slot) || (!slot->ops))
 301                return false;
 302        if (slot->ops->hardware_test)
 303                return true;
 304        return false;
 305}
 306
 307static int fs_add_slot(struct pci_slot *pci_slot)
 308{
 309        int retval = 0;
 310
 311        /* Create symbolic link to the hotplug driver module */
 312        pci_hp_create_module_link(pci_slot);
 313
 314        if (has_power_file(pci_slot)) {
 315                retval = sysfs_create_file(&pci_slot->kobj,
 316                                           &hotplug_slot_attr_power.attr);
 317                if (retval)
 318                        goto exit_power;
 319        }
 320
 321        if (has_attention_file(pci_slot)) {
 322                retval = sysfs_create_file(&pci_slot->kobj,
 323                                           &hotplug_slot_attr_attention.attr);
 324                if (retval)
 325                        goto exit_attention;
 326        }
 327
 328        if (has_latch_file(pci_slot)) {
 329                retval = sysfs_create_file(&pci_slot->kobj,
 330                                           &hotplug_slot_attr_latch.attr);
 331                if (retval)
 332                        goto exit_latch;
 333        }
 334
 335        if (has_adapter_file(pci_slot)) {
 336                retval = sysfs_create_file(&pci_slot->kobj,
 337                                           &hotplug_slot_attr_presence.attr);
 338                if (retval)
 339                        goto exit_adapter;
 340        }
 341
 342        if (has_test_file(pci_slot)) {
 343                retval = sysfs_create_file(&pci_slot->kobj,
 344                                           &hotplug_slot_attr_test.attr);
 345                if (retval)
 346                        goto exit_test;
 347        }
 348
 349        goto exit;
 350
 351exit_test:
 352        if (has_adapter_file(pci_slot))
 353                sysfs_remove_file(&pci_slot->kobj,
 354                                  &hotplug_slot_attr_presence.attr);
 355exit_adapter:
 356        if (has_latch_file(pci_slot))
 357                sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
 358exit_latch:
 359        if (has_attention_file(pci_slot))
 360                sysfs_remove_file(&pci_slot->kobj,
 361                                  &hotplug_slot_attr_attention.attr);
 362exit_attention:
 363        if (has_power_file(pci_slot))
 364                sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
 365exit_power:
 366        pci_hp_remove_module_link(pci_slot);
 367exit:
 368        return retval;
 369}
 370
 371static void fs_remove_slot(struct pci_slot *pci_slot)
 372{
 373        if (has_power_file(pci_slot))
 374                sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_power.attr);
 375
 376        if (has_attention_file(pci_slot))
 377                sysfs_remove_file(&pci_slot->kobj,
 378                                  &hotplug_slot_attr_attention.attr);
 379
 380        if (has_latch_file(pci_slot))
 381                sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_latch.attr);
 382
 383        if (has_adapter_file(pci_slot))
 384                sysfs_remove_file(&pci_slot->kobj,
 385                                  &hotplug_slot_attr_presence.attr);
 386
 387        if (has_test_file(pci_slot))
 388                sysfs_remove_file(&pci_slot->kobj, &hotplug_slot_attr_test.attr);
 389
 390        pci_hp_remove_module_link(pci_slot);
 391}
 392
 393static struct hotplug_slot *get_slot_from_name(const char *name)
 394{
 395        struct hotplug_slot *slot;
 396
 397        list_for_each_entry(slot, &pci_hotplug_slot_list, slot_list) {
 398                if (strcmp(hotplug_slot_name(slot), name) == 0)
 399                        return slot;
 400        }
 401        return NULL;
 402}
 403
 404/**
 405 * __pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
 406 * @bus: bus this slot is on
 407 * @slot: pointer to the &struct hotplug_slot to register
 408 * @devnr: device number
 409 * @name: name registered with kobject core
 410 * @owner: caller module owner
 411 * @mod_name: caller module name
 412 *
 413 * Registers a hotplug slot with the pci hotplug subsystem, which will allow
 414 * userspace interaction to the slot.
 415 *
 416 * Returns 0 if successful, anything else for an error.
 417 */
 418int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
 419                      int devnr, const char *name,
 420                      struct module *owner, const char *mod_name)
 421{
 422        int result;
 423        struct pci_slot *pci_slot;
 424
 425        if (slot == NULL)
 426                return -ENODEV;
 427        if ((slot->info == NULL) || (slot->ops == NULL))
 428                return -EINVAL;
 429        if (slot->release == NULL) {
 430                dbg("Why are you trying to register a hotplug slot without a proper release function?\n");
 431                return -EINVAL;
 432        }
 433
 434        slot->ops->owner = owner;
 435        slot->ops->mod_name = mod_name;
 436
 437        mutex_lock(&pci_hp_mutex);
 438        /*
 439         * No problems if we call this interface from both ACPI_PCI_SLOT
 440         * driver and call it here again. If we've already created the
 441         * pci_slot, the interface will simply bump the refcount.
 442         */
 443        pci_slot = pci_create_slot(bus, devnr, name, slot);
 444        if (IS_ERR(pci_slot)) {
 445                result = PTR_ERR(pci_slot);
 446                goto out;
 447        }
 448
 449        slot->pci_slot = pci_slot;
 450        pci_slot->hotplug = slot;
 451
 452        list_add(&slot->slot_list, &pci_hotplug_slot_list);
 453
 454        result = fs_add_slot(pci_slot);
 455        kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
 456        dbg("Added slot %s to the list\n", name);
 457out:
 458        mutex_unlock(&pci_hp_mutex);
 459        return result;
 460}
 461EXPORT_SYMBOL_GPL(__pci_hp_register);
 462
 463/**
 464 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
 465 * @slot: pointer to the &struct hotplug_slot to deregister
 466 *
 467 * The @slot must have been registered with the pci hotplug subsystem
 468 * previously with a call to pci_hp_register().
 469 *
 470 * Returns 0 if successful, anything else for an error.
 471 */
 472int pci_hp_deregister(struct hotplug_slot *slot)
 473{
 474        struct hotplug_slot *temp;
 475        struct pci_slot *pci_slot;
 476
 477        if (!slot)
 478                return -ENODEV;
 479
 480        mutex_lock(&pci_hp_mutex);
 481        temp = get_slot_from_name(hotplug_slot_name(slot));
 482        if (temp != slot) {
 483                mutex_unlock(&pci_hp_mutex);
 484                return -ENODEV;
 485        }
 486
 487        list_del(&slot->slot_list);
 488
 489        pci_slot = slot->pci_slot;
 490        fs_remove_slot(pci_slot);
 491        dbg("Removed slot %s from the list\n", hotplug_slot_name(slot));
 492
 493        slot->release(slot);
 494        pci_slot->hotplug = NULL;
 495        pci_destroy_slot(pci_slot);
 496        mutex_unlock(&pci_hp_mutex);
 497
 498        return 0;
 499}
 500EXPORT_SYMBOL_GPL(pci_hp_deregister);
 501
 502/**
 503 * pci_hp_change_slot_info - changes the slot's information structure in the core
 504 * @slot: pointer to the slot whose info has changed
 505 * @info: pointer to the info copy into the slot's info structure
 506 *
 507 * @slot must have been registered with the pci
 508 * hotplug subsystem previously with a call to pci_hp_register().
 509 *
 510 * Returns 0 if successful, anything else for an error.
 511 */
 512int pci_hp_change_slot_info(struct hotplug_slot *slot,
 513                            struct hotplug_slot_info *info)
 514{
 515        if (!slot || !info)
 516                return -ENODEV;
 517
 518        memcpy(slot->info, info, sizeof(struct hotplug_slot_info));
 519
 520        return 0;
 521}
 522EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
 523
 524static int __init pci_hotplug_init(void)
 525{
 526        int result;
 527
 528        result = cpci_hotplug_init(debug);
 529        if (result) {
 530                err("cpci_hotplug_init with error %d\n", result);
 531                return result;
 532        }
 533
 534        return result;
 535}
 536device_initcall(pci_hotplug_init);
 537
 538/*
 539 * not really modular, but the easiest way to keep compat with existing
 540 * bootargs behaviour is to continue using module_param here.
 541 */
 542module_param(debug, bool, 0644);
 543MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
 544