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