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