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