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