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/slab.h> 37 #include <linux/init.h> 38 #include <linux/mount.h> 39 #include <linux/namei.h> 40 #include <linux/pci.h> 41 #include <linux/pci_hotplug.h> 42 #include <asm/uaccess.h> 43 44 #define MY_NAME "pci_hotplug" 45 46 #define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0) 47 #define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg) 48 #define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg) 49 #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) 50 51 52 /* local variables */ 53 static int debug; 54 55 #define DRIVER_VERSION "0.5" 56 #define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>" 57 #define DRIVER_DESC "PCI Hot Plug PCI Core" 58 59 60 ////////////////////////////////////////////////////////////////// 61 62 static LIST_HEAD(pci_hotplug_slot_list); 63 64 struct kset pci_hotplug_slots_subsys; 65 66 static ssize_t hotplug_slot_attr_show(struct kobject *kobj, 67 struct attribute *attr, char *buf) 68 { 69 struct hotplug_slot *slot = to_hotplug_slot(kobj); 70 struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr); 71 return attribute->show ? attribute->show(slot, buf) : -EIO; 72 } 73 74 static ssize_t hotplug_slot_attr_store(struct kobject *kobj, 75 struct attribute *attr, const char *buf, size_t len) 76 { 77 struct hotplug_slot *slot = to_hotplug_slot(kobj); 78 struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr); 79 return attribute->store ? attribute->store(slot, buf, len) : -EIO; 80 } 81 82 static struct sysfs_ops hotplug_slot_sysfs_ops = { 83 .show = hotplug_slot_attr_show, 84 .store = hotplug_slot_attr_store, 85 }; 86 87 static void hotplug_slot_release(struct kobject *kobj) 88 { 89 struct hotplug_slot *slot = to_hotplug_slot(kobj); 90 if (slot->release) 91 slot->release(slot); 92 } 93 94 static struct kobj_type hotplug_slot_ktype = { 95 .sysfs_ops = &hotplug_slot_sysfs_ops, 96 .release = &hotplug_slot_release, 97 }; 98 99 decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL); 100 101 /* these strings match up with the values in pci_bus_speed */ 102 static char *pci_bus_speed_strings[] = { 103 "33 MHz PCI", /* 0x00 */ 104 "66 MHz PCI", /* 0x01 */ 105 "66 MHz PCIX", /* 0x02 */ 106 "100 MHz PCIX", /* 0x03 */ 107 "133 MHz PCIX", /* 0x04 */ 108 NULL, /* 0x05 */ 109 NULL, /* 0x06 */ 110 NULL, /* 0x07 */ 111 NULL, /* 0x08 */ 112 "66 MHz PCIX 266", /* 0x09 */ 113 "100 MHz PCIX 266", /* 0x0a */ 114 "133 MHz PCIX 266", /* 0x0b */ 115 NULL, /* 0x0c */ 116 NULL, /* 0x0d */ 117 NULL, /* 0x0e */ 118 NULL, /* 0x0f */ 119 NULL, /* 0x10 */ 120 "66 MHz PCIX 533", /* 0x11 */ 121 "100 MHz PCIX 533", /* 0x12 */ 122 "133 MHz PCIX 533", /* 0x13 */ 123 "25 GBps PCI-E", /* 0x14 */ 124 }; 125 126 #ifdef CONFIG_HOTPLUG_PCI_CPCI 127 extern int cpci_hotplug_init(int debug); 128 extern void cpci_hotplug_exit(void); 129 #else 130 static inline int cpci_hotplug_init(int debug) { return 0; } 131 static inline void cpci_hotplug_exit(void) { } 132 #endif 133 134 /* Weee, fun with macros... */ 135 #define GET_STATUS(name,type) \ 136 static int get_##name (struct hotplug_slot *slot, type *value) \ 137 { \ 138 struct hotplug_slot_ops *ops = slot->ops; \ 139 int retval = 0; \ 140 if (try_module_get(ops->owner)) { \ 141 if (ops->get_##name) \ 142 retval = ops->get_##name (slot, value); \ 143 else \ 144 *value = slot->info->name; \ 145 module_put(ops->owner); \ 146 } \ 147 return retval; \ 148 } 149 150 GET_STATUS(power_status, u8) 151 GET_STATUS(attention_status, u8) 152 GET_STATUS(latch_status, u8) 153 GET_STATUS(adapter_status, u8) 154 GET_STATUS(address, u32) 155 GET_STATUS(max_bus_speed, enum pci_bus_speed) 156 GET_STATUS(cur_bus_speed, enum pci_bus_speed) 157 158 static ssize_t power_read_file (struct hotplug_slot *slot, char *buf) 159 { 160 int retval; 161 u8 value; 162 163 retval = get_power_status (slot, &value); 164 if (retval) 165 goto exit; 166 retval = sprintf (buf, "%d\n", value); 167 exit: 168 return retval; 169 } 170 171 static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf, 172 size_t count) 173 { 174 unsigned long lpower; 175 u8 power; 176 int retval = 0; 177 178 lpower = simple_strtoul (buf, NULL, 10); 179 power = (u8)(lpower & 0xff); 180 dbg ("power = %d\n", power); 181 182 if (!try_module_get(slot->ops->owner)) { 183 retval = -ENODEV; 184 goto exit; 185 } 186 switch (power) { 187 case 0: 188 if (slot->ops->disable_slot) 189 retval = slot->ops->disable_slot(slot); 190 break; 191 192 case 1: 193 if (slot->ops->enable_slot) 194 retval = slot->ops->enable_slot(slot); 195 break; 196 197 default: 198 err ("Illegal value specified for power\n"); 199 retval = -EINVAL; 200 } 201 module_put(slot->ops->owner); 202 203 exit: 204 if (retval) 205 return retval; 206 return count; 207 } 208 209 static struct hotplug_slot_attribute hotplug_slot_attr_power = { 210 .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 211 .show = power_read_file, 212 .store = power_write_file 213 }; 214 215 static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf) 216 { 217 int retval; 218 u8 value; 219 220 retval = get_attention_status (slot, &value); 221 if (retval) 222 goto exit; 223 retval = sprintf (buf, "%d\n", value); 224 225 exit: 226 return retval; 227 } 228 229 static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf, 230 size_t count) 231 { 232 unsigned long lattention; 233 u8 attention; 234 int retval = 0; 235 236 lattention = simple_strtoul (buf, NULL, 10); 237 attention = (u8)(lattention & 0xff); 238 dbg (" - attention = %d\n", attention); 239 240 if (!try_module_get(slot->ops->owner)) { 241 retval = -ENODEV; 242 goto exit; 243 } 244 if (slot->ops->set_attention_status) 245 retval = slot->ops->set_attention_status(slot, attention); 246 module_put(slot->ops->owner); 247 248 exit: 249 if (retval) 250 return retval; 251 return count; 252 } 253 254 static struct hotplug_slot_attribute hotplug_slot_attr_attention = { 255 .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 256 .show = attention_read_file, 257 .store = attention_write_file 258 }; 259 260 static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf) 261 { 262 int retval; 263 u8 value; 264 265 retval = get_latch_status (slot, &value); 266 if (retval) 267 goto exit; 268 retval = sprintf (buf, "%d\n", value); 269 270 exit: 271 return retval; 272 } 273 274 static struct hotplug_slot_attribute hotplug_slot_attr_latch = { 275 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO}, 276 .show = latch_read_file, 277 }; 278 279 static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf) 280 { 281 int retval; 282 u8 value; 283 284 retval = get_adapter_status (slot, &value); 285 if (retval) 286 goto exit; 287 retval = sprintf (buf, "%d\n", value); 288 289 exit: 290 return retval; 291 } 292 293 static struct hotplug_slot_attribute hotplug_slot_attr_presence = { 294 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO}, 295 .show = presence_read_file, 296 }; 297 298 static ssize_t address_read_file (struct hotplug_slot *slot, char *buf) 299 { 300 int retval; 301 u32 address; 302 303 retval = get_address (slot, &address); 304 if (retval) 305 goto exit; 306 retval = sprintf (buf, "%04x:%02x:%02x\n", 307 (address >> 16) & 0xffff, 308 (address >> 8) & 0xff, 309 address & 0xff); 310 311 exit: 312 return retval; 313 } 314 315 static struct hotplug_slot_attribute hotplug_slot_attr_address = { 316 .attr = {.name = "address", .mode = S_IFREG | S_IRUGO}, 317 .show = address_read_file, 318 }; 319 320 static char *unknown_speed = "Unknown bus speed"; 321 322 static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf) 323 { 324 char *speed_string; 325 int retval; 326 enum pci_bus_speed value; 327 328 retval = get_max_bus_speed (slot, &value); 329 if (retval) 330 goto exit; 331 332 if (value == PCI_SPEED_UNKNOWN) 333 speed_string = unknown_speed; 334 else 335 speed_string = pci_bus_speed_strings[value]; 336 337 retval = sprintf (buf, "%s\n", speed_string); 338 339 exit: 340 return retval; 341 } 342 343 static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = { 344 .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO}, 345 .show = max_bus_speed_read_file, 346 }; 347 348 static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf) 349 { 350 char *speed_string; 351 int retval; 352 enum pci_bus_speed value; 353 354 retval = get_cur_bus_speed (slot, &value); 355 if (retval) 356 goto exit; 357 358 if (value == PCI_SPEED_UNKNOWN) 359 speed_string = unknown_speed; 360 else 361 speed_string = pci_bus_speed_strings[value]; 362 363 retval = sprintf (buf, "%s\n", speed_string); 364 365 exit: 366 return retval; 367 } 368 369 static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = { 370 .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO}, 371 .show = cur_bus_speed_read_file, 372 }; 373 374 static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf, 375 size_t count) 376 { 377 unsigned long ltest; 378 u32 test; 379 int retval = 0; 380 381 ltest = simple_strtoul (buf, NULL, 10); 382 test = (u32)(ltest & 0xffffffff); 383 dbg ("test = %d\n", test); 384 385 if (!try_module_get(slot->ops->owner)) { 386 retval = -ENODEV; 387 goto exit; 388 } 389 if (slot->ops->hardware_test) 390 retval = slot->ops->hardware_test(slot, test); 391 module_put(slot->ops->owner); 392 393 exit: 394 if (retval) 395 return retval; 396 return count; 397 } 398 399 static struct hotplug_slot_attribute hotplug_slot_attr_test = { 400 .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR}, 401 .store = test_write_file 402 }; 403 404 static int has_power_file (struct hotplug_slot *slot) 405 { 406 if ((!slot) || (!slot->ops)) 407 return -ENODEV; 408 if ((slot->ops->enable_slot) || 409 (slot->ops->disable_slot) || 410 (slot->ops->get_power_status)) 411 return 0; 412 return -ENOENT; 413 } 414 415 static int has_attention_file (struct hotplug_slot *slot) 416 { 417 if ((!slot) || (!slot->ops)) 418 return -ENODEV; 419 if ((slot->ops->set_attention_status) || 420 (slot->ops->get_attention_status)) 421 return 0; 422 return -ENOENT; 423 } 424 425 static int has_latch_file (struct hotplug_slot *slot) 426 { 427 if ((!slot) || (!slot->ops)) 428 return -ENODEV; 429 if (slot->ops->get_latch_status) 430 return 0; 431 return -ENOENT; 432 } 433 434 static int has_adapter_file (struct hotplug_slot *slot) 435 { 436 if ((!slot) || (!slot->ops)) 437 return -ENODEV; 438 if (slot->ops->get_adapter_status) 439 return 0; 440 return -ENOENT; 441 } 442 443 static int has_address_file (struct hotplug_slot *slot) 444 { 445 if ((!slot) || (!slot->ops)) 446 return -ENODEV; 447 if (slot->ops->get_address) 448 return 0; 449 return -ENOENT; 450 } 451 452 static int has_max_bus_speed_file (struct hotplug_slot *slot) 453 { 454 if ((!slot) || (!slot->ops)) 455 return -ENODEV; 456 if (slot->ops->get_max_bus_speed) 457 return 0; 458 return -ENOENT; 459 } 460 461 static int has_cur_bus_speed_file (struct hotplug_slot *slot) 462 { 463 if ((!slot) || (!slot->ops)) 464 return -ENODEV; 465 if (slot->ops->get_cur_bus_speed) 466 return 0; 467 return -ENOENT; 468 } 469 470 static int has_test_file (struct hotplug_slot *slot) 471 { 472 if ((!slot) || (!slot->ops)) 473 return -ENODEV; 474 if (slot->ops->hardware_test) 475 return 0; 476 return -ENOENT; 477 } 478 479 static int fs_add_slot (struct hotplug_slot *slot) 480 { 481 int retval = 0; 482 483 if (has_power_file(slot) == 0) { 484 retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr); 485 if (retval) 486 goto exit_power; 487 } 488 489 if (has_attention_file(slot) == 0) { 490 retval = sysfs_create_file(&slot->kobj, 491 &hotplug_slot_attr_attention.attr); 492 if (retval) 493 goto exit_attention; 494 } 495 496 if (has_latch_file(slot) == 0) { 497 retval = sysfs_create_file(&slot->kobj, 498 &hotplug_slot_attr_latch.attr); 499 if (retval) 500 goto exit_latch; 501 } 502 503 if (has_adapter_file(slot) == 0) { 504 retval = sysfs_create_file(&slot->kobj, 505 &hotplug_slot_attr_presence.attr); 506 if (retval) 507 goto exit_adapter; 508 } 509 510 if (has_address_file(slot) == 0) { 511 retval = sysfs_create_file(&slot->kobj, 512 &hotplug_slot_attr_address.attr); 513 if (retval) 514 goto exit_address; 515 } 516 517 if (has_max_bus_speed_file(slot) == 0) { 518 retval = sysfs_create_file(&slot->kobj, 519 &hotplug_slot_attr_max_bus_speed.attr); 520 if (retval) 521 goto exit_max_speed; 522 } 523 524 if (has_cur_bus_speed_file(slot) == 0) { 525 retval = sysfs_create_file(&slot->kobj, 526 &hotplug_slot_attr_cur_bus_speed.attr); 527 if (retval) 528 goto exit_cur_speed; 529 } 530 531 if (has_test_file(slot) == 0) { 532 retval = sysfs_create_file(&slot->kobj, 533 &hotplug_slot_attr_test.attr); 534 if (retval) 535 goto exit_test; 536 } 537 538 goto exit; 539 540 exit_test: 541 if (has_cur_bus_speed_file(slot) == 0) 542 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); 543 544 exit_cur_speed: 545 if (has_max_bus_speed_file(slot) == 0) 546 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); 547 548 exit_max_speed: 549 if (has_address_file(slot) == 0) 550 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr); 551 552 exit_address: 553 if (has_adapter_file(slot) == 0) 554 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); 555 556 exit_adapter: 557 if (has_latch_file(slot) == 0) 558 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); 559 560 exit_latch: 561 if (has_attention_file(slot) == 0) 562 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr); 563 564 exit_attention: 565 if (has_power_file(slot) == 0) 566 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); 567 exit_power: 568 exit: 569 return retval; 570 } 571 572 static void fs_remove_slot (struct hotplug_slot *slot) 573 { 574 if (has_power_file(slot) == 0) 575 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); 576 577 if (has_attention_file(slot) == 0) 578 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr); 579 580 if (has_latch_file(slot) == 0) 581 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); 582 583 if (has_adapter_file(slot) == 0) 584 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); 585 586 if (has_address_file(slot) == 0) 587 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr); 588 589 if (has_max_bus_speed_file(slot) == 0) 590 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr); 591 592 if (has_cur_bus_speed_file(slot) == 0) 593 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); 594 595 if (has_test_file(slot) == 0) 596 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr); 597 } 598 599 static struct hotplug_slot *get_slot_from_name (const char *name) 600 { 601 struct hotplug_slot *slot; 602 struct list_head *tmp; 603 604 list_for_each (tmp, &pci_hotplug_slot_list) { 605 slot = list_entry (tmp, struct hotplug_slot, slot_list); 606 if (strcmp(slot->name, name) == 0) 607 return slot; 608 } 609 return NULL; 610 } 611 612 /** 613 * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem 614 * @slot: pointer to the &struct hotplug_slot to register 615 * 616 * Registers a hotplug slot with the pci hotplug subsystem, which will allow 617 * userspace interaction to the slot. 618 * 619 * Returns 0 if successful, anything else for an error. 620 */ 621 int pci_hp_register (struct hotplug_slot *slot) 622 { 623 int result; 624 625 if (slot == NULL) 626 return -ENODEV; 627 if ((slot->info == NULL) || (slot->ops == NULL)) 628 return -EINVAL; 629 if (slot->release == NULL) { 630 dbg("Why are you trying to register a hotplug slot" 631 "without a proper release function?\n"); 632 return -EINVAL; 633 } 634 635 kobject_set_name(&slot->kobj, "%s", slot->name); 636 kobj_set_kset_s(slot, pci_hotplug_slots_subsys); 637 638 /* this can fail if we have already registered a slot with the same name */ 639 if (kobject_register(&slot->kobj)) { 640 err("Unable to register kobject"); 641 return -EINVAL; 642 } 643 644 list_add (&slot->slot_list, &pci_hotplug_slot_list); 645 646 result = fs_add_slot (slot); 647 dbg ("Added slot %s to the list\n", slot->name); 648 return result; 649 } 650 651 /** 652 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem 653 * @slot: pointer to the &struct hotplug_slot to deregister 654 * 655 * The @slot must have been registered with the pci hotplug subsystem 656 * previously with a call to pci_hp_register(). 657 * 658 * Returns 0 if successful, anything else for an error. 659 */ 660 int pci_hp_deregister (struct hotplug_slot *slot) 661 { 662 struct hotplug_slot *temp; 663 664 if (slot == NULL) 665 return -ENODEV; 666 667 temp = get_slot_from_name (slot->name); 668 if (temp != slot) { 669 return -ENODEV; 670 } 671 list_del (&slot->slot_list); 672 673 fs_remove_slot (slot); 674 dbg ("Removed slot %s from the list\n", slot->name); 675 kobject_unregister(&slot->kobj); 676 return 0; 677 } 678 679 /** 680 * pci_hp_change_slot_info - changes the slot's information structure in the core 681 * @slot: pointer to the slot whose info has changed 682 * @info: pointer to the info copy into the slot's info structure 683 * 684 * @slot must have been registered with the pci 685 * hotplug subsystem previously with a call to pci_hp_register(). 686 * 687 * Returns 0 if successful, anything else for an error. 688 */ 689 int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot, 690 struct hotplug_slot_info *info) 691 { 692 int retval; 693 694 if ((slot == NULL) || (info == NULL)) 695 return -ENODEV; 696 697 memcpy (slot->info, info, sizeof (struct hotplug_slot_info)); 698 699 return 0; 700 } 701 702 static int __init pci_hotplug_init (void) 703 { 704 int result; 705 706 kobj_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys); 707 result = subsystem_register(&pci_hotplug_slots_subsys); 708 if (result) { 709 err("Register subsys with error %d\n", result); 710 goto exit; 711 } 712 result = cpci_hotplug_init(debug); 713 if (result) { 714 err ("cpci_hotplug_init with error %d\n", result); 715 goto err_subsys; 716 } 717 718 info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); 719 goto exit; 720 721 err_subsys: 722 subsystem_unregister(&pci_hotplug_slots_subsys); 723 exit: 724 return result; 725 } 726 727 static void __exit pci_hotplug_exit (void) 728 { 729 cpci_hotplug_exit(); 730 subsystem_unregister(&pci_hotplug_slots_subsys); 731 } 732 733 module_init(pci_hotplug_init); 734 module_exit(pci_hotplug_exit); 735 736 MODULE_AUTHOR(DRIVER_AUTHOR); 737 MODULE_DESCRIPTION(DRIVER_DESC); 738 MODULE_LICENSE("GPL"); 739 module_param(debug, bool, 0644); 740 MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 741 742 EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys); 743 EXPORT_SYMBOL_GPL(pci_hp_register); 744 EXPORT_SYMBOL_GPL(pci_hp_deregister); 745 EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); 746