1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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
55static 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
64static LIST_HEAD(pci_hotplug_slot_list);
65static DEFINE_MUTEX(pci_hp_mutex);
66
67
68static char *pci_bus_speed_strings[] = {
69 "33 MHz PCI",
70 "66 MHz PCI",
71 "66 MHz PCIX",
72 "100 MHz PCIX",
73 "133 MHz PCIX",
74 NULL,
75 NULL,
76 NULL,
77 NULL,
78 "66 MHz PCIX 266",
79 "100 MHz PCIX 266",
80 "133 MHz PCIX 266",
81 NULL,
82 NULL,
83 NULL,
84 NULL,
85 NULL,
86 "66 MHz PCIX 533",
87 "100 MHz PCIX 533",
88 "133 MHz PCIX 533",
89 "2.5 GT/s PCI-E",
90 "5.0 GT/s PCI-E",
91};
92
93#ifdef CONFIG_HOTPLUG_PCI_CPCI
94extern int cpci_hotplug_init(int debug);
95extern void cpci_hotplug_exit(void);
96#else
97static inline int cpci_hotplug_init(int debug) { return 0; }
98static inline void cpci_hotplug_exit(void) { }
99#endif
100
101
102#define GET_STATUS(name,type) \
103static int get_##name (struct hotplug_slot *slot, type *value) \
104{ \
105 struct hotplug_slot_ops *ops = slot->ops; \
106 int retval = 0; \
107 if (!try_module_get(ops->owner)) \
108 return -ENODEV; \
109 if (ops->get_##name) \
110 retval = ops->get_##name(slot, value); \
111 else \
112 *value = slot->info->name; \
113 module_put(ops->owner); \
114 return retval; \
115}
116
117GET_STATUS(power_status, u8)
118GET_STATUS(attention_status, u8)
119GET_STATUS(latch_status, u8)
120GET_STATUS(adapter_status, u8)
121GET_STATUS(max_bus_speed, enum pci_bus_speed)
122GET_STATUS(cur_bus_speed, enum pci_bus_speed)
123
124static ssize_t power_read_file(struct pci_slot *slot, char *buf)
125{
126 int retval;
127 u8 value;
128
129 retval = get_power_status(slot->hotplug, &value);
130 if (retval)
131 goto exit;
132 retval = sprintf (buf, "%d\n", value);
133exit:
134 return retval;
135}
136
137static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
138 size_t count)
139{
140 struct hotplug_slot *slot = pci_slot->hotplug;
141 unsigned long lpower;
142 u8 power;
143 int retval = 0;
144
145 lpower = simple_strtoul (buf, NULL, 10);
146 power = (u8)(lpower & 0xff);
147 dbg ("power = %d\n", power);
148
149 if (!try_module_get(slot->ops->owner)) {
150 retval = -ENODEV;
151 goto exit;
152 }
153 switch (power) {
154 case 0:
155 if (slot->ops->disable_slot)
156 retval = slot->ops->disable_slot(slot);
157 break;
158
159 case 1:
160 if (slot->ops->enable_slot)
161 retval = slot->ops->enable_slot(slot);
162 break;
163
164 default:
165 err ("Illegal value specified for power\n");
166 retval = -EINVAL;
167 }
168 module_put(slot->ops->owner);
169
170exit:
171 if (retval)
172 return retval;
173 return count;
174}
175
176static struct pci_slot_attribute hotplug_slot_attr_power = {
177 .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
178 .show = power_read_file,
179 .store = power_write_file
180};
181
182static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
183{
184 int retval;
185 u8 value;
186
187 retval = get_attention_status(slot->hotplug, &value);
188 if (retval)
189 goto exit;
190 retval = sprintf(buf, "%d\n", value);
191
192exit:
193 return retval;
194}
195
196static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
197 size_t count)
198{
199 struct hotplug_slot_ops *ops = slot->hotplug->ops;
200 unsigned long lattention;
201 u8 attention;
202 int retval = 0;
203
204 lattention = simple_strtoul (buf, NULL, 10);
205 attention = (u8)(lattention & 0xff);
206 dbg (" - attention = %d\n", attention);
207
208 if (!try_module_get(ops->owner)) {
209 retval = -ENODEV;
210 goto exit;
211 }
212 if (ops->set_attention_status)
213 retval = ops->set_attention_status(slot->hotplug, attention);
214 module_put(ops->owner);
215
216exit:
217 if (retval)
218 return retval;
219 return count;
220}
221
222static struct pci_slot_attribute hotplug_slot_attr_attention = {
223 .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
224 .show = attention_read_file,
225 .store = attention_write_file
226};
227
228static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
229{
230 int retval;
231 u8 value;
232
233 retval = get_latch_status(slot->hotplug, &value);
234 if (retval)
235 goto exit;
236 retval = sprintf (buf, "%d\n", value);
237
238exit:
239 return retval;
240}
241
242static struct pci_slot_attribute hotplug_slot_attr_latch = {
243 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
244 .show = latch_read_file,
245};
246
247static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
248{
249 int retval;
250 u8 value;
251
252 retval = get_adapter_status(slot->hotplug, &value);
253 if (retval)
254 goto exit;
255 retval = sprintf (buf, "%d\n", value);
256
257exit:
258 return retval;
259}
260
261static struct pci_slot_attribute hotplug_slot_attr_presence = {
262 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
263 .show = presence_read_file,
264};
265
266static char *unknown_speed = "Unknown bus speed";
267
268static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
269{
270 char *speed_string;
271 int retval;
272 enum pci_bus_speed value;
273
274 retval = get_max_bus_speed(slot->hotplug, &value);
275 if (retval)
276 goto exit;
277
278 if (value == PCI_SPEED_UNKNOWN)
279 speed_string = unknown_speed;
280 else
281 speed_string = pci_bus_speed_strings[value];
282
283 retval = sprintf (buf, "%s\n", speed_string);
284
285exit:
286 return retval;
287}
288
289static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
290 .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
291 .show = max_bus_speed_read_file,
292};
293
294static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
295{
296 char *speed_string;
297 int retval;
298 enum pci_bus_speed value;
299
300 retval = get_cur_bus_speed(slot->hotplug, &value);
301 if (retval)
302 goto exit;
303
304 if (value == PCI_SPEED_UNKNOWN)
305 speed_string = unknown_speed;
306 else
307 speed_string = pci_bus_speed_strings[value];
308
309 retval = sprintf (buf, "%s\n", speed_string);
310
311exit:
312 return retval;
313}
314
315static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
316 .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
317 .show = cur_bus_speed_read_file,
318};
319
320static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
321 size_t count)
322{
323 struct hotplug_slot *slot = pci_slot->hotplug;
324 unsigned long ltest;
325 u32 test;
326 int retval = 0;
327
328 ltest = simple_strtoul (buf, NULL, 10);
329 test = (u32)(ltest & 0xffffffff);
330 dbg ("test = %d\n", test);
331
332 if (!try_module_get(slot->ops->owner)) {
333 retval = -ENODEV;
334 goto exit;
335 }
336 if (slot->ops->hardware_test)
337 retval = slot->ops->hardware_test(slot, test);
338 module_put(slot->ops->owner);
339
340exit:
341 if (retval)
342 return retval;
343 return count;
344}
345
346static struct pci_slot_attribute hotplug_slot_attr_test = {
347 .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
348 .store = test_write_file
349};
350
351static bool has_power_file(struct pci_slot *pci_slot)
352{
353 struct hotplug_slot *slot = pci_slot->hotplug;
354 if ((!slot) || (!slot->ops))
355 return false;
356 if ((slot->ops->enable_slot) ||
357 (slot->ops->disable_slot) ||
358 (slot->ops->get_power_status))
359 return true;
360 return false;
361}
362
363static bool has_attention_file(struct pci_slot *pci_slot)
364{
365 struct hotplug_slot *slot = pci_slot->hotplug;
366 if ((!slot) || (!slot->ops))
367 return false;
368 if ((slot->ops->set_attention_status) ||
369 (slot->ops->get_attention_status))
370 return true;
371 return false;
372}
373
374static bool has_latch_file(struct pci_slot *pci_slot)
375{
376 struct hotplug_slot *slot = pci_slot->hotplug;
377 if ((!slot) || (!slot->ops))
378 return false;
379 if (slot->ops->get_latch_status)
380 return true;
381 return false;
382}
383
384static bool has_adapter_file(struct pci_slot *pci_slot)
385{
386 struct hotplug_slot *slot = pci_slot->hotplug;
387 if ((!slot) || (!slot->ops))
388 return false;
389 if (slot->ops->get_adapter_status)
390 return true;
391 return false;
392}
393
394static bool has_max_bus_speed_file(struct pci_slot *pci_slot)
395{
396 struct hotplug_slot *slot = pci_slot->hotplug;
397 if ((!slot) || (!slot->ops))
398 return false;
399 if (slot->ops->get_max_bus_speed)
400 return true;
401 return false;
402}
403
404static bool has_cur_bus_speed_file(struct pci_slot *pci_slot)
405{
406 struct hotplug_slot *slot = pci_slot->hotplug;
407 if ((!slot) || (!slot->ops))
408 return false;
409 if (slot->ops->get_cur_bus_speed)
410 return true;
411 return false;
412}
413
414static bool has_test_file(struct pci_slot *pci_slot)
415{
416 struct hotplug_slot *slot = pci_slot->hotplug;
417 if ((!slot) || (!slot->ops))
418 return false;
419 if (slot->ops->hardware_test)
420 return true;
421 return false;
422}
423
424static int fs_add_slot(struct pci_slot *slot)
425{
426 int retval = 0;
427
428
429 pci_hp_create_module_link(slot);
430
431 if (has_power_file(slot)) {
432 retval = sysfs_create_file(&slot->kobj,
433 &hotplug_slot_attr_power.attr);
434 if (retval)
435 goto exit_power;
436 }
437
438 if (has_attention_file(slot)) {
439 retval = sysfs_create_file(&slot->kobj,
440 &hotplug_slot_attr_attention.attr);
441 if (retval)
442 goto exit_attention;
443 }
444
445 if (has_latch_file(slot)) {
446 retval = sysfs_create_file(&slot->kobj,
447 &hotplug_slot_attr_latch.attr);
448 if (retval)
449 goto exit_latch;
450 }
451
452 if (has_adapter_file(slot)) {
453 retval = sysfs_create_file(&slot->kobj,
454 &hotplug_slot_attr_presence.attr);
455 if (retval)
456 goto exit_adapter;
457 }
458
459 if (has_max_bus_speed_file(slot)) {
460 retval = sysfs_create_file(&slot->kobj,
461 &hotplug_slot_attr_max_bus_speed.attr);
462 if (retval)
463 goto exit_max_speed;
464 }
465
466 if (has_cur_bus_speed_file(slot)) {
467 retval = sysfs_create_file(&slot->kobj,
468 &hotplug_slot_attr_cur_bus_speed.attr);
469 if (retval)
470 goto exit_cur_speed;
471 }
472
473 if (has_test_file(slot)) {
474 retval = sysfs_create_file(&slot->kobj,
475 &hotplug_slot_attr_test.attr);
476 if (retval)
477 goto exit_test;
478 }
479
480 goto exit;
481
482exit_test:
483 if (has_cur_bus_speed_file(slot))
484 sysfs_remove_file(&slot->kobj,
485 &hotplug_slot_attr_cur_bus_speed.attr);
486exit_cur_speed:
487 if (has_max_bus_speed_file(slot))
488 sysfs_remove_file(&slot->kobj,
489 &hotplug_slot_attr_max_bus_speed.attr);
490exit_max_speed:
491 if (has_adapter_file(slot))
492 sysfs_remove_file(&slot->kobj,
493 &hotplug_slot_attr_presence.attr);
494exit_adapter:
495 if (has_latch_file(slot))
496 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
497exit_latch:
498 if (has_attention_file(slot))
499 sysfs_remove_file(&slot->kobj,
500 &hotplug_slot_attr_attention.attr);
501exit_attention:
502 if (has_power_file(slot))
503 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
504exit_power:
505 pci_hp_remove_module_link(slot);
506exit:
507 return retval;
508}
509
510static void fs_remove_slot(struct pci_slot *slot)
511{
512 if (has_power_file(slot))
513 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
514
515 if (has_attention_file(slot))
516 sysfs_remove_file(&slot->kobj,
517 &hotplug_slot_attr_attention.attr);
518
519 if (has_latch_file(slot))
520 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
521
522 if (has_adapter_file(slot))
523 sysfs_remove_file(&slot->kobj,
524 &hotplug_slot_attr_presence.attr);
525
526 if (has_max_bus_speed_file(slot))
527 sysfs_remove_file(&slot->kobj,
528 &hotplug_slot_attr_max_bus_speed.attr);
529
530 if (has_cur_bus_speed_file(slot))
531 sysfs_remove_file(&slot->kobj,
532 &hotplug_slot_attr_cur_bus_speed.attr);
533
534 if (has_test_file(slot))
535 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
536
537 pci_hp_remove_module_link(slot);
538}
539
540static struct hotplug_slot *get_slot_from_name (const char *name)
541{
542 struct hotplug_slot *slot;
543 struct list_head *tmp;
544
545 list_for_each (tmp, &pci_hotplug_slot_list) {
546 slot = list_entry (tmp, struct hotplug_slot, slot_list);
547 if (strcmp(hotplug_slot_name(slot), name) == 0)
548 return slot;
549 }
550 return NULL;
551}
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567int __pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus,
568 int devnr, const char *name,
569 struct module *owner, const char *mod_name)
570{
571 int result;
572 struct pci_slot *pci_slot;
573
574 if (slot == NULL)
575 return -ENODEV;
576 if ((slot->info == NULL) || (slot->ops == NULL))
577 return -EINVAL;
578 if (slot->release == NULL) {
579 dbg("Why are you trying to register a hotplug slot "
580 "without a proper release function?\n");
581 return -EINVAL;
582 }
583
584 slot->ops->owner = owner;
585 slot->ops->mod_name = mod_name;
586
587 mutex_lock(&pci_hp_mutex);
588
589
590
591
592
593 pci_slot = pci_create_slot(bus, devnr, name, slot);
594 if (IS_ERR(pci_slot)) {
595 result = PTR_ERR(pci_slot);
596 goto out;
597 }
598
599 slot->pci_slot = pci_slot;
600 pci_slot->hotplug = slot;
601
602 list_add(&slot->slot_list, &pci_hotplug_slot_list);
603
604 result = fs_add_slot(pci_slot);
605 kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
606 dbg("Added slot %s to the list\n", name);
607out:
608 mutex_unlock(&pci_hp_mutex);
609 return result;
610}
611
612
613
614
615
616
617
618
619
620
621int pci_hp_deregister(struct hotplug_slot *hotplug)
622{
623 struct hotplug_slot *temp;
624 struct pci_slot *slot;
625
626 if (!hotplug)
627 return -ENODEV;
628
629 mutex_lock(&pci_hp_mutex);
630 temp = get_slot_from_name(hotplug_slot_name(hotplug));
631 if (temp != hotplug) {
632 mutex_unlock(&pci_hp_mutex);
633 return -ENODEV;
634 }
635
636 list_del(&hotplug->slot_list);
637
638 slot = hotplug->pci_slot;
639 fs_remove_slot(slot);
640 dbg("Removed slot %s from the list\n", hotplug_slot_name(hotplug));
641
642 hotplug->release(hotplug);
643 slot->hotplug = NULL;
644 pci_destroy_slot(slot);
645 mutex_unlock(&pci_hp_mutex);
646
647 return 0;
648}
649
650
651
652
653
654
655
656
657
658
659
660int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
661 struct hotplug_slot_info *info)
662{
663 struct pci_slot *slot;
664 if (!hotplug || !info)
665 return -ENODEV;
666 slot = hotplug->pci_slot;
667
668 memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));
669
670 return 0;
671}
672
673static int __init pci_hotplug_init (void)
674{
675 int result;
676
677 result = cpci_hotplug_init(debug);
678 if (result) {
679 err ("cpci_hotplug_init with error %d\n", result);
680 goto err_cpci;
681 }
682
683 info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
684
685err_cpci:
686 return result;
687}
688
689static void __exit pci_hotplug_exit (void)
690{
691 cpci_hotplug_exit();
692}
693
694module_init(pci_hotplug_init);
695module_exit(pci_hotplug_exit);
696
697MODULE_AUTHOR(DRIVER_AUTHOR);
698MODULE_DESCRIPTION(DRIVER_DESC);
699MODULE_LICENSE("GPL");
700module_param(debug, bool, 0644);
701MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
702
703EXPORT_SYMBOL_GPL(__pci_hp_register);
704EXPORT_SYMBOL_GPL(pci_hp_deregister);
705EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);
706