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 */ 53static 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 62static LIST_HEAD(pci_hotplug_slot_list); 63 64struct kset pci_hotplug_slots_subsys; 65 66static 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 74static 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 82static struct sysfs_ops hotplug_slot_sysfs_ops = { 83 .show = hotplug_slot_attr_show, 84 .store = hotplug_slot_attr_store, 85}; 86 87static 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 94static struct kobj_type hotplug_slot_ktype = { 95 .sysfs_ops = &hotplug_slot_sysfs_ops, 96 .release = &hotplug_slot_release, 97}; 98 99decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL); 100 101/* these strings match up with the values in pci_bus_speed */ 102static 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 127extern int cpci_hotplug_init(int debug); 128extern void cpci_hotplug_exit(void); 129#else 130static inline int cpci_hotplug_init(int debug) { return 0; } 131static inline void cpci_hotplug_exit(void) { } 132#endif 133 134/* Weee, fun with macros... */ 135#define GET_STATUS(name,type) \ 136static 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 150GET_STATUS(power_status, u8) 151GET_STATUS(attention_status, u8) 152GET_STATUS(latch_status, u8) 153GET_STATUS(adapter_status, u8) 154GET_STATUS(address, u32) 155GET_STATUS(max_bus_speed, enum pci_bus_speed) 156GET_STATUS(cur_bus_speed, enum pci_bus_speed) 157 158static 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); 167exit: 168 return retval; 169} 170 171static 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 203exit: 204 if (retval) 205 return retval; 206 return count; 207} 208 209static 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 215static 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 225exit: 226 return retval; 227} 228 229static 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 248exit: 249 if (retval) 250 return retval; 251 return count; 252} 253 254static 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 260static 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 270exit: 271 return retval; 272} 273 274static struct hotplug_slot_attribute hotplug_slot_attr_latch = { 275 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO}, 276 .show = latch_read_file, 277}; 278 279static 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 289exit: 290 return retval; 291} 292 293static struct hotplug_slot_attribute hotplug_slot_attr_presence = { 294 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO}, 295 .show = presence_read_file, 296}; 297 298static 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 311exit: 312 return retval; 313} 314 315static struct hotplug_slot_attribute hotplug_slot_attr_address = { 316 .attr = {.name = "address", .mode = S_IFREG | S_IRUGO}, 317 .show = address_read_file, 318}; 319 320static char *unknown_speed = "Unknown bus speed"; 321 322static 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 339exit: 340 return retval; 341} 342 343static 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 348static 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 365exit: 366 return retval; 367} 368 369static 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 374static 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 393exit: 394 if (retval) 395 return retval; 396 return count; 397} 398 399static 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 404static 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 415static 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 425static 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 434static 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 443static 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 452static 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 461static 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 470static 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 479static 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 540exit_test: 541 if (has_cur_bus_speed_file(slot) == 0) 542 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr); 543 544exit_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 548exit_max_speed: 549 if (has_address_file(slot) == 0) 550 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr); 551 552exit_address: 553 if (has_adapter_file(slot) == 0) 554 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr); 555 556exit_adapter: 557 if (has_latch_file(slot) == 0) 558 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr); 559 560exit_latch: 561 if (has_attention_file(slot) == 0) 562 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr); 563 564exit_attention: 565 if (has_power_file(slot) == 0) 566 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr); 567exit_power: 568exit: 569 return retval; 570} 571 572static 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 599static 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 */ 621int 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 */ 660int 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 */ 689int __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 /* 698 * check all fields in the info structure, and update timestamps 699 * for the files referring to the fields that have now changed. 700 */ 701 if ((has_power_file(slot) == 0) && 702 (slot->info->power_status != info->power_status)) { 703 retval = sysfs_update_file(&slot->kobj, 704 &hotplug_slot_attr_power.attr); 705 if (retval) 706 return retval; 707 } 708 709 if ((has_attention_file(slot) == 0) && 710 (slot->info->attention_status != info->attention_status)) { 711 retval = sysfs_update_file(&slot->kobj, 712 &hotplug_slot_attr_attention.attr); 713 if (retval) 714 return retval; 715 } 716 717 if ((has_latch_file(slot) == 0) && 718 (slot->info->latch_status != info->latch_status)) { 719 retval = sysfs_update_file(&slot->kobj, 720 &hotplug_slot_attr_latch.attr); 721 if (retval) 722 return retval; 723 } 724 725 if ((has_adapter_file(slot) == 0) && 726 (slot->info->adapter_status != info->adapter_status)) { 727 retval = sysfs_update_file(&slot->kobj, 728 &hotplug_slot_attr_presence.attr); 729 if (retval) 730 return retval; 731 } 732 733 if ((has_address_file(slot) == 0) && 734 (slot->info->address != info->address)) { 735 retval = sysfs_update_file(&slot->kobj, 736 &hotplug_slot_attr_address.attr); 737 if (retval) 738 return retval; 739 } 740 741 if ((has_max_bus_speed_file(slot) == 0) && 742 (slot->info->max_bus_speed != info->max_bus_speed)) { 743 retval = sysfs_update_file(&slot->kobj, 744 &hotplug_slot_attr_max_bus_speed.attr); 745 if (retval) 746 return retval; 747 } 748 749 if ((has_cur_bus_speed_file(slot) == 0) && 750 (slot->info->cur_bus_speed != info->cur_bus_speed)) { 751 retval = sysfs_update_file(&slot->kobj, 752 &hotplug_slot_attr_cur_bus_speed.attr); 753 if (retval) 754 return retval; 755 } 756 757 memcpy (slot->info, info, sizeof (struct hotplug_slot_info)); 758 759 return 0; 760} 761 762static int __init pci_hotplug_init (void) 763{ 764 int result; 765 766 kobj_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys); 767 result = subsystem_register(&pci_hotplug_slots_subsys); 768 if (result) { 769 err("Register subsys with error %d\n", result); 770 goto exit; 771 } 772 result = cpci_hotplug_init(debug); 773 if (result) { 774 err ("cpci_hotplug_init with error %d\n", result); 775 goto err_subsys; 776 } 777 778 info (DRIVER_DESC " version: " DRIVER_VERSION "\n"); 779 goto exit; 780 781err_subsys: 782 subsystem_unregister(&pci_hotplug_slots_subsys); 783exit: 784 return result; 785} 786 787static void __exit pci_hotplug_exit (void) 788{ 789 cpci_hotplug_exit(); 790 subsystem_unregister(&pci_hotplug_slots_subsys); 791} 792 793module_init(pci_hotplug_init); 794module_exit(pci_hotplug_exit); 795 796MODULE_AUTHOR(DRIVER_AUTHOR); 797MODULE_DESCRIPTION(DRIVER_DESC); 798MODULE_LICENSE("GPL"); 799module_param(debug, bool, 0644); 800MODULE_PARM_DESC(debug, "Debugging mode enabled or not"); 801 802EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys); 803EXPORT_SYMBOL_GPL(pci_hp_register); 804EXPORT_SYMBOL_GPL(pci_hp_deregister); 805EXPORT_SYMBOL_GPL(pci_hp_change_slot_info); 806