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