blob: fa666d0cc48968e188adb2a8a6de78233f04e783 [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>
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 */
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);
65
66struct subsystem pci_hotplug_slots_subsys;
67
68static 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);
Dmitry Torokhovfc7e4822005-04-29 01:26:27 -050073 return attribute->show ? attribute->show(slot, buf) : -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070074}
75
76static 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);
Dmitry Torokhovfc7e4822005-04-29 01:26:27 -050081 return attribute->store ? attribute->store(slot, buf, len) : -EIO;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082}
83
84static struct sysfs_ops hotplug_slot_sysfs_ops = {
85 .show = hotplug_slot_attr_show,
86 .store = hotplug_slot_attr_store,
87};
88
89static 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
96static struct kobj_type hotplug_slot_ktype = {
97 .sysfs_ops = &hotplug_slot_sysfs_ops,
98 .release = &hotplug_slot_release,
99};
100
101decl_subsys_name(pci_hotplug_slots, slots, &hotplug_slot_ktype, NULL);
102
103/* these strings match up with the values in pci_bus_speed */
104static 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
129extern int cpci_hotplug_init(int debug);
130extern void cpci_hotplug_exit(void);
131#else
132static inline int cpci_hotplug_init(int debug) { return 0; }
133static inline void cpci_hotplug_exit(void) { }
134#endif
135
136/* Weee, fun with macros... */
137#define GET_STATUS(name,type) \
138static 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
152GET_STATUS(power_status, u8)
153GET_STATUS(attention_status, u8)
154GET_STATUS(latch_status, u8)
155GET_STATUS(adapter_status, u8)
156GET_STATUS(address, u32)
157GET_STATUS(max_bus_speed, enum pci_bus_speed)
158GET_STATUS(cur_bus_speed, enum pci_bus_speed)
159
160static 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);
169exit:
170 return retval;
171}
172
173static 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
205exit:
206 if (retval)
207 return retval;
208 return count;
209}
210
211static 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
217static 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
227exit:
228 return retval;
229}
230
231static 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
250exit:
251 if (retval)
252 return retval;
253 return count;
254}
255
256static 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
262static 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
272exit:
273 return retval;
274}
275
276static struct hotplug_slot_attribute hotplug_slot_attr_latch = {
277 .attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
278 .show = latch_read_file,
279};
280
281static 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
291exit:
292 return retval;
293}
294
295static struct hotplug_slot_attribute hotplug_slot_attr_presence = {
296 .attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
297 .show = presence_read_file,
298};
299
300static 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
313exit:
314 return retval;
315}
316
317static struct hotplug_slot_attribute hotplug_slot_attr_address = {
318 .attr = {.name = "address", .mode = S_IFREG | S_IRUGO},
319 .show = address_read_file,
320};
321
322static char *unknown_speed = "Unknown bus speed";
323
324static 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
341exit:
342 return retval;
343}
344
345static 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
350static 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
367exit:
368 return retval;
369}
370
371static 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
376static 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
395exit:
396 if (retval)
397 return retval;
398 return count;
399}
400
401static 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
406static 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
417static 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
427static 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
436static 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
445static 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
454static 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
463static 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
472static 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
481static int fs_add_slot (struct hotplug_slot *slot)
482{
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700483 int retval = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700485 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700491 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700497
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700498 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700504
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700505 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700511
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700512 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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700519 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
542exit_test:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 if (has_cur_bus_speed_file(slot) == 0)
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700544 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700546exit_cur_speed:
547 if (has_max_bus_speed_file(slot) == 0)
548 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700550exit_max_speed:
551 if (has_address_file(slot) == 0)
552 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
553
554exit_address:
555 if (has_adapter_file(slot) == 0)
556 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
557
558exit_adapter:
559 if (has_latch_file(slot) == 0)
560 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
561
562exit_latch:
563 if (has_attention_file(slot) == 0)
564 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
565
566exit_attention:
567 if (has_power_file(slot) == 0)
568 sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
569exit_power:
570exit:
571 return retval;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700572}
573
574static 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
601static 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 */
623int 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 */
662int 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 */
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700691int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
692 struct hotplug_slot_info *info)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700693{
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700694 int retval;
695
Linus Torvalds1da177e2005-04-16 15:20:36 -0700696 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) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700704 (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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
711 if ((has_attention_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700712 (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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718
719 if ((has_latch_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700720 (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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700726
727 if ((has_adapter_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700728 (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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700734
735 if ((has_address_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700736 (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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
743 if ((has_max_bus_speed_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700744 (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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
751 if ((has_cur_bus_speed_file(slot) == 0) &&
Greg Kroah-Hartman660a0e82006-08-28 11:43:25 -0700752 (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 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
759 memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
760
761 return 0;
762}
763
764static 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
783err_subsys:
784 subsystem_unregister(&pci_hotplug_slots_subsys);
785exit:
786 return result;
787}
788
789static void __exit pci_hotplug_exit (void)
790{
791 cpci_hotplug_exit();
792 subsystem_unregister(&pci_hotplug_slots_subsys);
793}
794
795module_init(pci_hotplug_init);
796module_exit(pci_hotplug_exit);
797
798MODULE_AUTHOR(DRIVER_AUTHOR);
799MODULE_DESCRIPTION(DRIVER_DESC);
800MODULE_LICENSE("GPL");
801module_param(debug, bool, 0644);
802MODULE_PARM_DESC(debug, "Debugging mode enabled or not");
803
804EXPORT_SYMBOL_GPL(pci_hotplug_slots_subsys);
805EXPORT_SYMBOL_GPL(pci_hp_register);
806EXPORT_SYMBOL_GPL(pci_hp_deregister);
807EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);