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