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