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 */ 54 static int 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 63 static LIST_HEAD(pci_hotplug_slot_list); 64 static DEFINE_MUTEX(pci_hp_mutex); 65 66 #ifdef CONFIG_HOTPLUG_PCI_CPCI 67 extern int cpci_hotplug_init(int debug); 68 extern void cpci_hotplug_exit(void); 69 #else 70 static inline int cpci_hotplug_init(int debug) { return 0; } 71 static inline void cpci_hotplug_exit(void) { } 72 #endif 73 74 /* Weee, fun with macros... */ 75 #define GET_STATUS(name,type) \ 76 static 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 90 GET_STATUS(power_status, u8) 91 GET_STATUS(attention_status, u8) 92 GET_STATUS(latch_status, u8) 93 GET_STATUS(adapter_status, u8) 94 95 static 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); 104 exit: 105 return retval; 106 } 107 108 static 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 141 exit: 142 if (retval) 143 return retval; 144 return count; 145 } 146 147 static 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 153 static 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 163 exit: 164 return retval; 165 } 166 167 static 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 187 exit: 188 if (retval) 189 return retval; 190 return count; 191 } 192 193 static 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 199 static 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 209 exit: 210 return retval; 211 } 212 213 static struct pci_slot_attribute hotplug_slot_attr_latch = { 214 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO}, 215 .show = latch_read_file, 216 }; 217 218 static 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 228 exit: 229 return retval; 230 } 231 232 static struct pci_slot_attribute hotplug_slot_attr_presence = { 233 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO}, 234 .show = presence_read_file, 235 }; 236 237 static 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 257 exit: 258 if (retval) 259 return retval; 260 return count; 261 } 262 263 static 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 268 static 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 280 static 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 291 static 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 301 static 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 311 static 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 321 static 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 365 exit_test: 366 if (has_adapter_file(slot)) 367 sysfs_remove_file(&slot->kobj, 368 &hotplug_slot_attr_presence.attr); 369 exit_adapter: 370 if (has_latch_file(slot)) 371 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); 372 exit_latch: 373 if (has_attention_file(slot)) 374 sysfs_remove_file(&slot->kobj, 375 &hotplug_slot_attr_attention.attr); 376 exit_attention: 377 if (has_power_file(slot)) 378 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); 379 exit_power: 380 pci_hp_remove_module_link(slot); 381 exit: 382 return retval; 383 } 384 385 static 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 407 static 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 */ 434 int __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); 474 out: 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 */ 488 int 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 */ 527 int __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 540 static 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 552 err_cpci: 553 return result; 554 } 555 556 static void __exit pci_hotplug_exit (void) 557 { 558 cpci_hotplug_exit(); 559 } 560 561 module_init(pci_hotplug_init); 562 module_exit(pci_hotplug_exit); 563 564 MODULE_AUTHOR(DRIVER_AUTHOR); 565 MODULE_DESCRIPTION(DRIVER_DESC); 566 MODULE_LICENSE("GPL"); 567 module_param(debug, bool, 0644); 568 MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 569 570 EXPORT_SYMBOL_GPL(__pci_hp_register); 571 EXPORT_SYMBOL_GPL(pci_hp_deregister); 572 EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); 573