blob: bd433ef6bfc66ffd4381f6e9714debc98d370751 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
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 *
Kristen Carlson Accardifb5f4d72006-09-29 10:30:27 -070024 * Send feedback to <kristen.c.accardi@intel.com>
Linus Torvalds1da177e2005-04-16 15:20:36 -070025 *
26 */
27
Linus Torvalds1da177e2005-04-16 15:20:36 -070028#include <linux/module.h>
29#include <linux/moduleparam.h>
30#include <linux/kernel.h>
31#include <linux/types.h>
32#include <linux/list.h>
Greg Kroah-Hartman7a54f252006-10-13 20:05:19 -070033#include <linux/kobject.h>
34#include <linux/sysfs.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070035#include <linux/pagemap.h>
36#include <linux/slab.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#include <linux/init.h>
38#include <linux/mount.h>
39#include <linux/namei.h>
40#include <linux/pci.h>
Greg Kroah-Hartman7a54f252006-10-13 20:05:19 -070041#include <linux/pci_hotplug.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#include <asm/uaccess.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070043
44#define MY_NAME "pci_hotplug"
45
46#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __FUNCTION__ , ## arg); } while (0)
47#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
48#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
49#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)
50
51
52/* local variables */
53static int debug;
54
55#define DRIVER_VERSION "0.5"
56#define DRIVER_AUTHOR "Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
57#define DRIVER_DESC "PCI Hot Plug PCI Core"
58
59
60//////////////////////////////////////////////////////////////////
61
62static LIST_HEAD(pci_hotplug_slot_list);
63
Greg Kroah-Hartman823bccf2007-04-13 13:15:19 -070064struct kset pci_hotplug_slots_subsys;
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
66static ssize_t hotplug_slot_attr_show(struct kobject *kobj,
67 struct attribute *attr, char *buf)
68{
69 struct hotplug_slot *slot = to_hotplug_slot(kobj);
70 struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
Dmitry Torokhovfc7e4822005-04-29 01:26:27 -050071 return attribute->show ? attribute->show(slot, buf) : -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070072}
73
74static ssize_t hotplug_slot_attr_store(struct kobject *kobj,
75 struct attribute *attr, const char *buf, size_t len)
76{
77 struct hotplug_slot *slot = to_hotplug_slot(kobj);
78 struct hotplug_slot_attribute *attribute = to_hotplug_attr(attr);
Dmitry Torokhovfc7e4822005-04-29 01:26:27 -050079 return attribute->store ? attribute->store(slot, buf, len) : -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080}
81
82static struct sysfs_ops hotplug_slot_sysfs_ops = {
83 .show = hotplug_slot_attr_show,
84 .store = hotplug_slot_attr_store,
85};
86
87static void hotplug_slot_release(struct kobject *kobj)
88{
89 struct hotplug_slot *slot = to_hotplug_slot(kobj);
90 if (slot->release)
91 slot->release(slot);
92}
93
94static struct kobj_type hotplug_slot_ktype = {
95 .sysfs_ops = &hotplug_slot_sysfs_ops,
96 .release = &hotplug_slot_release,
97};
98
99decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
100
101/* these strings match up with the values in pci_bus_speed */
102static char *pci_bus_speed_strings[] = {
103 "33 MHz PCI", /* 0x00 */
104 "66 MHz PCI", /* 0x01 */
105 "66 MHz PCIX", /* 0x02 */
106 "100 MHz PCIX", /* 0x03 */
107 "133 MHz PCIX", /* 0x04 */
108 NULL, /* 0x05 */
109 NULL, /* 0x06 */
110 NULL, /* 0x07 */
111 NULL, /* 0x08 */
112 "66 MHz PCIX 266", /* 0x09 */
113 "100 MHz PCIX 266", /* 0x0a */
114 "133 MHz PCIX 266", /* 0x0b */
115 NULL, /* 0x0c */
116 NULL, /* 0x0d */
117 NULL, /* 0x0e */
118 NULL, /* 0x0f */
119 NULL, /* 0x10 */
120 "66 MHz PCIX 533", /* 0x11 */
121 "100 MHz PCIX 533", /* 0x12 */
122 "133 MHz PCIX 533", /* 0x13 */
123 "25 GBps PCI-E", /* 0x14 */
124};
125
126#ifdef CONFIG_HOTPLUG_PCI_CPCI
127extern int cpci_hotplug_init(int debug);
128extern void cpci_hotplug_exit(void);
129#else
130static inline int cpci_hotplug_init(int debug) { return 0; }
131static inline void cpci_hotplug_exit(void) { }
132#endif
133
134/* Weee, fun with macros... */
135#define GET_STATUS(name,type) \
136static int get_##name (struct hotplug_slot *slot, type *value) \
137{ \
138 struct hotplug_slot_ops *ops = slot->ops; \
139 int retval = 0; \
140 if (try_module_get(ops->owner)) { \
141 if (ops->get_##name) \
142 retval = ops->get_##name (slot, value); \
143 else \
144 *value = slot->info->name; \
145 module_put(ops->owner); \
146 } \
147 return retval; \
148}
149
150GET_STATUS(power_status, u8)
151GET_STATUS(attention_status, u8)
152GET_STATUS(latch_status, u8)
153GET_STATUS(adapter_status, u8)
154GET_STATUS(address, u32)
155GET_STATUS(max_bus_speed, enum pci_bus_speed)
156GET_STATUS(cur_bus_speed, enum pci_bus_speed)
157
158static ssize_t power_read_file (struct hotplug_slot *slot, char *buf)
159{
160 int retval;
161 u8 value;
162
163 retval = get_power_status (slot, &value);
164 if (retval)
165 goto exit;
166 retval = sprintf (buf, "%d\n", value);
167exit:
168 return retval;
169}
170
171static ssize_t power_write_file (struct hotplug_slot *slot, const char *buf,
172 size_t count)
173{
174 unsigned long lpower;
175 u8 power;
176 int retval = 0;
177
178 lpower = simple_strtoul (buf, NULL, 10);
179 power = (u8)(lpower & 0xff);
180 dbg ("power = %d\n", power);
181
182 if (!try_module_get(slot->ops->owner)) {
183 retval = -ENODEV;
184 goto exit;
185 }
186 switch (power) {
187 case 0:
188 if (slot->ops->disable_slot)
189 retval = slot->ops->disable_slot(slot);
190 break;
191
192 case 1:
193 if (slot->ops->enable_slot)
194 retval = slot->ops->enable_slot(slot);
195 break;
196
197 default:
198 err ("Illegal value specified for power\n");
199 retval = -EINVAL;
200 }
201 module_put(slot->ops->owner);
202
203exit:
204 if (retval)
205 return retval;
206 return count;
207}
208
209static struct hotplug_slot_attribute hotplug_slot_attr_power = {
210 .attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
211 .show = power_read_file,
212 .store = power_write_file
213};
214
215static ssize_t attention_read_file (struct hotplug_slot *slot, char *buf)
216{
217 int retval;
218 u8 value;
219
220 retval = get_attention_status (slot, &value);
221 if (retval)
222 goto exit;
223 retval = sprintf (buf, "%d\n", value);
224
225exit:
226 return retval;
227}
228
229static ssize_t attention_write_file (struct hotplug_slot *slot, const char *buf,
230 size_t count)
231{
232 unsigned long lattention;
233 u8 attention;
234 int retval = 0;
235
236 lattention = simple_strtoul (buf, NULL, 10);
237 attention = (u8)(lattention & 0xff);
238 dbg (" - attention = %d\n", attention);
239
240 if (!try_module_get(slot->ops->owner)) {
241 retval = -ENODEV;
242 goto exit;
243 }
244 if (slot->ops->set_attention_status)
245 retval = slot->ops->set_attention_status(slot, attention);
246 module_put(slot->ops->owner);
247
248exit:
249 if (retval)
250 return retval;
251 return count;
252}
253
254static struct hotplug_slot_attribute hotplug_slot_attr_attention = {
255 .attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
256 .show = attention_read_file,
257 .store = attention_write_file
258};
259
260static ssize_t latch_read_file (struct hotplug_slot *slot, char *buf)
261{
262 int retval;
263 u8 value;
264
265 retval = get_latch_status (slot, &value);
266 if (retval)
267 goto exit;
268 retval = sprintf (buf, "%d\n", value);
269
270exit:
271 return retval;
272}
273
274static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
275 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
276 .show = latch_read_file,
277};
278
279static ssize_t presence_read_file (struct hotplug_slot *slot, char *buf)
280{
281 int retval;
282 u8 value;
283
284 retval = get_adapter_status (slot, &value);
285 if (retval)
286 goto exit;
287 retval = sprintf (buf, "%d\n", value);
288
289exit:
290 return retval;
291}
292
293static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
294 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
295 .show = presence_read_file,
296};
297
298static ssize_t address_read_file (struct hotplug_slot *slot, char *buf)
299{
300 int retval;
301 u32 address;
302
303 retval = get_address (slot, &address);
304 if (retval)
305 goto exit;
306 retval = sprintf (buf, "%04x:%02x:%02x\n",
307 (address >> 16) & 0xffff,
308 (address >> 8) & 0xff,
309 address & 0xff);
310
311exit:
312 return retval;
313}
314
315static struct hotplug_slot_attribute hotplug_slot_attr_address = {
316 .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
317 .show = address_read_file,
318};
319
320static char *unknown_speed = "Unknown bus speed";
321
322static ssize_t max_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
323{
324 char *speed_string;
325 int retval;
326 enum pci_bus_speed value;
327
328 retval = get_max_bus_speed (slot, &value);
329 if (retval)
330 goto exit;
331
332 if (value == PCI_SPEED_UNKNOWN)
333 speed_string = unknown_speed;
334 else
335 speed_string = pci_bus_speed_strings[value];
336
337 retval = sprintf (buf, "%s\n", speed_string);
338
339exit:
340 return retval;
341}
342
343static struct hotplug_slot_attribute hotplug_slot_attr_max_bus_speed = {
344 .attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
345 .show = max_bus_speed_read_file,
346};
347
348static ssize_t cur_bus_speed_read_file (struct hotplug_slot *slot, char *buf)
349{
350 char *speed_string;
351 int retval;
352 enum pci_bus_speed value;
353
354 retval = get_cur_bus_speed (slot, &value);
355 if (retval)
356 goto exit;
357
358 if (value == PCI_SPEED_UNKNOWN)
359 speed_string = unknown_speed;
360 else
361 speed_string = pci_bus_speed_strings[value];
362
363 retval = sprintf (buf, "%s\n", speed_string);
364
365exit:
366 return retval;
367}
368
369static struct hotplug_slot_attribute hotplug_slot_attr_cur_bus_speed = {
370 .attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
371 .show = cur_bus_speed_read_file,
372};
373
374static ssize_t test_write_file (struct hotplug_slot *slot, const char *buf,
375 size_t count)
376{
377 unsigned long ltest;
378 u32 test;
379 int retval = 0;
380
381 ltest = simple_strtoul (buf, NULL, 10);
382 test = (u32)(ltest & 0xffffffff);
383 dbg ("test = %d\n", test);
384
385 if (!try_module_get(slot->ops->owner)) {
386 retval = -ENODEV;
387 goto exit;
388 }
389 if (slot->ops->hardware_test)
390 retval = slot->ops->hardware_test(slot, test);
391 module_put(slot->ops->owner);
392
393exit:
394 if (retval)
395 return retval;
396 return count;
397}
398
399static struct hotplug_slot_attribute hotplug_slot_attr_test = {
400 .attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
401 .store = test_write_file
402};
403
404static int has_power_file (struct hotplug_slot *slot)
405{
406 if ((!slot) || (!slot->ops))
407 return -ENODEV;
408 if ((slot->ops->enable_slot) ||
409 (slot->ops->disable_slot) ||
410 (slot->ops->get_power_status))
411 return 0;
412 return -ENOENT;
413}
414
415static int has_attention_file (struct hotplug_slot *slot)
416{
417 if ((!slot) || (!slot->ops))
418 return -ENODEV;
419 if ((slot->ops->set_attention_status) ||
420 (slot->ops->get_attention_status))
421 return 0;
422 return -ENOENT;
423}
424
425static int has_latch_file (struct hotplug_slot *slot)
426{
427 if ((!slot) || (!slot->ops))
428 return -ENODEV;
429 if (slot->ops->get_latch_status)
430 return 0;
431 return -ENOENT;
432}
433
434static int has_adapter_file (struct hotplug_slot *slot)
435{
436 if ((!slot) || (!slot->ops))
437 return -ENODEV;
438 if (slot->ops->get_adapter_status)
439 return 0;
440 return -ENOENT;
441}
442
443static int has_address_file (struct hotplug_slot *slot)
444{
445 if ((!slot) || (!slot->ops))
446 return -ENODEV;
447 if (slot->ops->get_address)
448 return 0;
449 return -ENOENT;
450}
451
452static int has_max_bus_speed_file (struct hotplug_slot *slot)
453{
454 if ((!slot) || (!slot->ops))
455 return -ENODEV;
456 if (slot->ops->get_max_bus_speed)
457 return 0;
458 return -ENOENT;
459}
460
461static int has_cur_bus_speed_file (struct hotplug_slot *slot)
462{
463 if ((!slot) || (!slot->ops))
464 return -ENODEV;
465 if (slot->ops->get_cur_bus_speed)
466 return 0;
467 return -ENOENT;
468}
469
470static int has_test_file (struct hotplug_slot *slot)
471{
472 if ((!slot) || (!slot->ops))
473 return -ENODEV;
474 if (slot->ops->hardware_test)
475 return 0;
476 return -ENOENT;
477}
478
479static int fs_add_slot (struct hotplug_slot *slot)
480{
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700481 int retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700483 if (has_power_file(slot) == 0) {
484 retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
485 if (retval)
486 goto exit_power;
487 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700488
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700489 if (has_attention_file(slot) == 0) {
490 retval = sysfs_create_file(&slot->kobj,
491 &hotplug_slot_attr_attention.attr);
492 if (retval)
493 goto exit_attention;
494 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700496 if (has_latch_file(slot) == 0) {
497 retval = sysfs_create_file(&slot->kobj,
498 &hotplug_slot_attr_latch.attr);
499 if (retval)
500 goto exit_latch;
501 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700503 if (has_adapter_file(slot) == 0) {
504 retval = sysfs_create_file(&slot->kobj,
505 &hotplug_slot_attr_presence.attr);
506 if (retval)
507 goto exit_adapter;
508 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700509
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700510 if (has_address_file(slot) == 0) {
511 retval = sysfs_create_file(&slot->kobj,
512 &hotplug_slot_attr_address.attr);
513 if (retval)
514 goto exit_address;
515 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700517 if (has_max_bus_speed_file(slot) == 0) {
518 retval = sysfs_create_file(&slot->kobj,
519 &hotplug_slot_attr_max_bus_speed.attr);
520 if (retval)
521 goto exit_max_speed;
522 }
523
524 if (has_cur_bus_speed_file(slot) == 0) {
525 retval = sysfs_create_file(&slot->kobj,
526 &hotplug_slot_attr_cur_bus_speed.attr);
527 if (retval)
528 goto exit_cur_speed;
529 }
530
531 if (has_test_file(slot) == 0) {
532 retval = sysfs_create_file(&slot->kobj,
533 &hotplug_slot_attr_test.attr);
534 if (retval)
535 goto exit_test;
536 }
537
538 goto exit;
539
540exit_test:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700541 if (has_cur_bus_speed_file(slot) == 0)
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700542 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700544exit_cur_speed:
545 if (has_max_bus_speed_file(slot) == 0)
546 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700548exit_max_speed:
549 if (has_address_file(slot) == 0)
550 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
551
552exit_address:
553 if (has_adapter_file(slot) == 0)
554 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
555
556exit_adapter:
557 if (has_latch_file(slot) == 0)
558 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
559
560exit_latch:
561 if (has_attention_file(slot) == 0)
562 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
563
564exit_attention:
565 if (has_power_file(slot) == 0)
566 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
567exit_power:
568exit:
569 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700570}
571
572static void fs_remove_slot (struct hotplug_slot *slot)
573{
574 if (has_power_file(slot) == 0)
575 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
576
577 if (has_attention_file(slot) == 0)
578 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
579
580 if (has_latch_file(slot) == 0)
581 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
582
583 if (has_adapter_file(slot) == 0)
584 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
585
586 if (has_address_file(slot) == 0)
587 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
588
589 if (has_max_bus_speed_file(slot) == 0)
590 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
591
592 if (has_cur_bus_speed_file(slot) == 0)
593 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
594
595 if (has_test_file(slot) == 0)
596 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
597}
598
599static struct hotplug_slot *get_slot_from_name (const char *name)
600{
601 struct hotplug_slot *slot;
602 struct list_head *tmp;
603
604 list_for_each (tmp, &pci_hotplug_slot_list) {
605 slot = list_entry (tmp, struct hotplug_slot, slot_list);
606 if (strcmp(slot->name, name) == 0)
607 return slot;
608 }
609 return NULL;
610}
611
612/**
613 * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
614 * @slot: pointer to the &struct hotplug_slot to register
615 *
616 * Registers a hotplug slot with the pci hotplug subsystem, which will allow
617 * userspace interaction to the slot.
618 *
619 * Returns 0 if successful, anything else for an error.
620 */
621int pci_hp_register (struct hotplug_slot *slot)
622{
623 int result;
624
625 if (slot == NULL)
626 return -ENODEV;
627 if ((slot->info == NULL) || (slot->ops == NULL))
628 return -EINVAL;
629 if (slot->release == NULL) {
630 dbg("Why are you trying to register a hotplug slot"
631 "without a proper release function?\n");
632 return -EINVAL;
633 }
634
635 kobject_set_name(&slot->kobj, "%s", slot->name);
636 kobj_set_kset_s(slot, pci_hotplug_slots_subsys);
637
638 /* this can fail if we have already registered a slot with the same name */
639 if (kobject_register(&slot->kobj)) {
640 err("Unable to register kobject");
641 return -EINVAL;
642 }
643
644 list_add (&slot->slot_list, &pci_hotplug_slot_list);
645
646 result = fs_add_slot (slot);
647 dbg ("Added slot %s to the list\n", slot->name);
648 return result;
649}
650
651/**
652 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
653 * @slot: pointer to the &struct hotplug_slot to deregister
654 *
655 * The @slot must have been registered with the pci hotplug subsystem
656 * previously with a call to pci_hp_register().
657 *
658 * Returns 0 if successful, anything else for an error.
659 */
660int pci_hp_deregister (struct hotplug_slot *slot)
661{
662 struct hotplug_slot *temp;
663
664 if (slot == NULL)
665 return -ENODEV;
666
667 temp = get_slot_from_name (slot->name);
668 if (temp != slot) {
669 return -ENODEV;
670 }
671 list_del (&slot->slot_list);
672
673 fs_remove_slot (slot);
674 dbg ("Removed slot %s from the list\n", slot->name);
675 kobject_unregister(&slot->kobj);
676 return 0;
677}
678
679/**
680 * pci_hp_change_slot_info - changes the slot's information structure in the core
681 * @slot: pointer to the slot whose info has changed
682 * @info: pointer to the info copy into the slot's info structure
683 *
684 * @slot must have been registered with the pci
685 * hotplug subsystem previously with a call to pci_hp_register().
686 *
687 * Returns 0 if successful, anything else for an error.
688 */
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700689int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
690 struct hotplug_slot_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691{
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700692 int retval;
693
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 if ((slot == NULL) || (info == NULL))
695 return -ENODEV;
696
697 /*
698 * check all fields in the info structure, and update timestamps
699 * for the files referring to the fields that have now changed.
700 */
701 if ((has_power_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700702 (slot->info->power_status != info->power_status)) {
703 retval = sysfs_update_file(&slot->kobj,
704 &hotplug_slot_attr_power.attr);
705 if (retval)
706 return retval;
707 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700708
709 if ((has_attention_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700710 (slot->info->attention_status != info->attention_status)) {
711 retval = sysfs_update_file(&slot->kobj,
712 &hotplug_slot_attr_attention.attr);
713 if (retval)
714 return retval;
715 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
717 if ((has_latch_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700718 (slot->info->latch_status != info->latch_status)) {
719 retval = sysfs_update_file(&slot->kobj,
720 &hotplug_slot_attr_latch.attr);
721 if (retval)
722 return retval;
723 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700724
725 if ((has_adapter_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700726 (slot->info->adapter_status != info->adapter_status)) {
727 retval = sysfs_update_file(&slot->kobj,
728 &hotplug_slot_attr_presence.attr);
729 if (retval)
730 return retval;
731 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732
733 if ((has_address_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700734 (slot->info->address != info->address)) {
735 retval = sysfs_update_file(&slot->kobj,
736 &hotplug_slot_attr_address.attr);
737 if (retval)
738 return retval;
739 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
741 if ((has_max_bus_speed_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700742 (slot->info->max_bus_speed != info->max_bus_speed)) {
743 retval = sysfs_update_file(&slot->kobj,
744 &hotplug_slot_attr_max_bus_speed.attr);
745 if (retval)
746 return retval;
747 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700748
749 if ((has_cur_bus_speed_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700750 (slot->info->cur_bus_speed != info->cur_bus_speed)) {
751 retval = sysfs_update_file(&slot->kobj,
752 &hotplug_slot_attr_cur_bus_speed.attr);
753 if (retval)
754 return retval;
755 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
757 memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
758
759 return 0;
760}
761
762static int __init pci_hotplug_init (void)
763{
764 int result;
765
Greg Kroah-Hartman823bccf2007-04-13 13:15:19 -0700766 kobj_set_kset_s(&pci_hotplug_slots_subsys, pci_bus_type.subsys);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 result = subsystem_register(&pci_hotplug_slots_subsys);
768 if (result) {
769 err("Register subsys with error %d\n", result);
770 goto exit;
771 }
772 result = cpci_hotplug_init(debug);
773 if (result) {
774 err ("cpci_hotplug_init with error %d\n", result);
775 goto err_subsys;
776 }
777
778 info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
779 goto exit;
780
781err_subsys:
782 subsystem_unregister(&pci_hotplug_slots_subsys);
783exit:
784 return result;
785}
786
787static void __exit pci_hotplug_exit (void)
788{
789 cpci_hotplug_exit();
790 subsystem_unregister(&pci_hotplug_slots_subsys);
791}
792
793module_init(pci_hotplug_init);
794module_exit(pci_hotplug_exit);
795
796MODULE_AUTHOR(DRIVER_AUTHOR);
797MODULE_DESCRIPTION(DRIVER_DESC);
798MODULE_LICENSE("GPL");
799module_param(debug, bool, 0644);
800MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
801
802EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
803EXPORT_SYMBOL_GPL(pci_hp_register);
804EXPORT_SYMBOL_GPL(pci_hp_deregister);
805EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);