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