pci_hotplug_core.c 16.6 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
/*
 * PCI HotPlug Controller Core
 *
 * Copyright (C) 2001-2002 Greg Kroah-Hartman (greg@kroah.com)
 * Copyright (C) 2001-2002 IBM Corp.
 *
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
 * NON INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
24
 * Send feedback to <kristen.c.accardi@intel.com>
L
Linus Torvalds 已提交
25 26 27 28 29 30 31 32
 *
 */

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/list.h>
33 34
#include <linux/kobject.h>
#include <linux/sysfs.h>
L
Linus Torvalds 已提交
35 36 37 38 39
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mount.h>
#include <linux/namei.h>
40
#include <linux/mutex.h>
L
Linus Torvalds 已提交
41
#include <linux/pci.h>
42
#include <linux/pci_hotplug.h>
L
Linus Torvalds 已提交
43
#include <asm/uaccess.h>
A
Alex Chiang 已提交
44
#include "../pci.h"
L
Linus Torvalds 已提交
45 46 47

#define MY_NAME	"pci_hotplug"

48
#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
L
Linus Torvalds 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
#define err(format, arg...) printk(KERN_ERR "%s: " format , MY_NAME , ## arg)
#define info(format, arg...) printk(KERN_INFO "%s: " format , MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg)


/* local variables */
static int debug;

#define DRIVER_VERSION	"0.5"
#define DRIVER_AUTHOR	"Greg Kroah-Hartman <greg@kroah.com>, Scott Murray <scottm@somanetworks.com>"
#define DRIVER_DESC	"PCI Hot Plug PCI Core"


//////////////////////////////////////////////////////////////////

static LIST_HEAD(pci_hotplug_slot_list);
65
static DEFINE_MUTEX(pci_hp_mutex);
L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105

/* these strings match up with the values in pci_bus_speed */
static char *pci_bus_speed_strings[] = {
	"33 MHz PCI",		/* 0x00 */
	"66 MHz PCI",		/* 0x01 */
	"66 MHz PCIX", 		/* 0x02 */
	"100 MHz PCIX",		/* 0x03 */
	"133 MHz PCIX",		/* 0x04 */
	NULL,			/* 0x05 */
	NULL,			/* 0x06 */
	NULL,			/* 0x07 */
	NULL,			/* 0x08 */
	"66 MHz PCIX 266",	/* 0x09 */
	"100 MHz PCIX 266",	/* 0x0a */
	"133 MHz PCIX 266",	/* 0x0b */
	NULL,			/* 0x0c */
	NULL,			/* 0x0d */
	NULL,			/* 0x0e */
	NULL,			/* 0x0f */
	NULL,			/* 0x10 */
	"66 MHz PCIX 533",	/* 0x11 */
	"100 MHz PCIX 533",	/* 0x12 */
	"133 MHz PCIX 533",	/* 0x13 */
	"25 GBps PCI-E",	/* 0x14 */
};

#ifdef CONFIG_HOTPLUG_PCI_CPCI
extern int cpci_hotplug_init(int debug);
extern void cpci_hotplug_exit(void);
#else
static inline int cpci_hotplug_init(int debug) { return 0; }
static inline void cpci_hotplug_exit(void) { }
#endif

/* Weee, fun with macros... */
#define GET_STATUS(name,type)	\
static int get_##name (struct hotplug_slot *slot, type *value)		\
{									\
	struct hotplug_slot_ops *ops = slot->ops;			\
	int retval = 0;							\
106
	if (!try_module_get(ops->owner))				\
107 108 109 110 111 112
		return -ENODEV;						\
	if (ops->get_##name)						\
		retval = ops->get_##name(slot, value);			\
	else								\
		*value = slot->info->name;				\
	module_put(ops->owner);						\
L
Linus Torvalds 已提交
113 114 115 116 117 118 119 120 121 122
	return retval;							\
}

GET_STATUS(power_status, u8)
GET_STATUS(attention_status, u8)
GET_STATUS(latch_status, u8)
GET_STATUS(adapter_status, u8)
GET_STATUS(max_bus_speed, enum pci_bus_speed)
GET_STATUS(cur_bus_speed, enum pci_bus_speed)

A
Alex Chiang 已提交
123
static ssize_t power_read_file(struct pci_slot *slot, char *buf)
L
Linus Torvalds 已提交
124 125 126 127
{
	int retval;
	u8 value;

A
Alex Chiang 已提交
128
	retval = get_power_status(slot->hotplug, &value);
L
Linus Torvalds 已提交
129 130 131 132 133 134 135
	if (retval)
		goto exit;
	retval = sprintf (buf, "%d\n", value);
exit:
	return retval;
}

A
Alex Chiang 已提交
136
static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
L
Linus Torvalds 已提交
137 138
		size_t count)
{
A
Alex Chiang 已提交
139
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
	unsigned long lpower;
	u8 power;
	int retval = 0;

	lpower = simple_strtoul (buf, NULL, 10);
	power = (u8)(lpower & 0xff);
	dbg ("power = %d\n", power);

	if (!try_module_get(slot->ops->owner)) {
		retval = -ENODEV;
		goto exit;
	}
	switch (power) {
		case 0:
			if (slot->ops->disable_slot)
				retval = slot->ops->disable_slot(slot);
			break;

		case 1:
			if (slot->ops->enable_slot)
				retval = slot->ops->enable_slot(slot);
			break;

		default:
			err ("Illegal value specified for power\n");
			retval = -EINVAL;
	}
	module_put(slot->ops->owner);

exit:	
	if (retval)
		return retval;
	return count;
}

A
Alex Chiang 已提交
175
static struct pci_slot_attribute hotplug_slot_attr_power = {
L
Linus Torvalds 已提交
176 177 178 179 180
	.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
	.show = power_read_file,
	.store = power_write_file
};

A
Alex Chiang 已提交
181
static ssize_t attention_read_file(struct pci_slot *slot, char *buf)
L
Linus Torvalds 已提交
182 183 184 185
{
	int retval;
	u8 value;

A
Alex Chiang 已提交
186
	retval = get_attention_status(slot->hotplug, &value);
L
Linus Torvalds 已提交
187 188
	if (retval)
		goto exit;
A
Alex Chiang 已提交
189
	retval = sprintf(buf, "%d\n", value);
L
Linus Torvalds 已提交
190 191 192 193 194

exit:
	return retval;
}

A
Alex Chiang 已提交
195
static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
L
Linus Torvalds 已提交
196 197
		size_t count)
{
A
Alex Chiang 已提交
198
	struct hotplug_slot_ops *ops = slot->hotplug->ops;
L
Linus Torvalds 已提交
199 200 201 202 203 204 205 206
	unsigned long lattention;
	u8 attention;
	int retval = 0;

	lattention = simple_strtoul (buf, NULL, 10);
	attention = (u8)(lattention & 0xff);
	dbg (" - attention = %d\n", attention);

A
Alex Chiang 已提交
207
	if (!try_module_get(ops->owner)) {
L
Linus Torvalds 已提交
208 209 210
		retval = -ENODEV;
		goto exit;
	}
A
Alex Chiang 已提交
211 212 213
	if (ops->set_attention_status)
		retval = ops->set_attention_status(slot->hotplug, attention);
	module_put(ops->owner);
L
Linus Torvalds 已提交
214 215 216 217 218 219 220

exit:	
	if (retval)
		return retval;
	return count;
}

A
Alex Chiang 已提交
221
static struct pci_slot_attribute hotplug_slot_attr_attention = {
L
Linus Torvalds 已提交
222 223 224 225 226
	.attr = {.name = "attention", .mode = S_IFREG | S_IRUGO | S_IWUSR},
	.show = attention_read_file,
	.store = attention_write_file
};

A
Alex Chiang 已提交
227
static ssize_t latch_read_file(struct pci_slot *slot, char *buf)
L
Linus Torvalds 已提交
228 229 230 231
{
	int retval;
	u8 value;

A
Alex Chiang 已提交
232
	retval = get_latch_status(slot->hotplug, &value);
L
Linus Torvalds 已提交
233 234 235 236 237 238 239 240
	if (retval)
		goto exit;
	retval = sprintf (buf, "%d\n", value);

exit:
	return retval;
}

A
Alex Chiang 已提交
241
static struct pci_slot_attribute hotplug_slot_attr_latch = {
L
Linus Torvalds 已提交
242 243 244 245
	.attr = {.name = "latch", .mode = S_IFREG | S_IRUGO},
	.show = latch_read_file,
};

A
Alex Chiang 已提交
246
static ssize_t presence_read_file(struct pci_slot *slot, char *buf)
L
Linus Torvalds 已提交
247 248 249 250
{
	int retval;
	u8 value;

A
Alex Chiang 已提交
251
	retval = get_adapter_status(slot->hotplug, &value);
L
Linus Torvalds 已提交
252 253 254 255 256 257 258 259
	if (retval)
		goto exit;
	retval = sprintf (buf, "%d\n", value);

exit:
	return retval;
}

A
Alex Chiang 已提交
260
static struct pci_slot_attribute hotplug_slot_attr_presence = {
L
Linus Torvalds 已提交
261 262 263 264 265 266
	.attr = {.name = "adapter", .mode = S_IFREG | S_IRUGO},
	.show = presence_read_file,
};

static char *unknown_speed = "Unknown bus speed";

A
Alex Chiang 已提交
267
static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
L
Linus Torvalds 已提交
268 269 270 271 272
{
	char *speed_string;
	int retval;
	enum pci_bus_speed value;
	
A
Alex Chiang 已提交
273
	retval = get_max_bus_speed(slot->hotplug, &value);
L
Linus Torvalds 已提交
274 275 276 277 278 279 280 281 282 283 284 285 286 287
	if (retval)
		goto exit;

	if (value == PCI_SPEED_UNKNOWN)
		speed_string = unknown_speed;
	else
		speed_string = pci_bus_speed_strings[value];
	
	retval = sprintf (buf, "%s\n", speed_string);

exit:
	return retval;
}

A
Alex Chiang 已提交
288
static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
L
Linus Torvalds 已提交
289 290 291 292
	.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
	.show = max_bus_speed_read_file,
};

A
Alex Chiang 已提交
293
static ssize_t cur_bus_speed_read_file(struct pci_slot *slot, char *buf)
L
Linus Torvalds 已提交
294 295 296 297 298
{
	char *speed_string;
	int retval;
	enum pci_bus_speed value;

A
Alex Chiang 已提交
299
	retval = get_cur_bus_speed(slot->hotplug, &value);
L
Linus Torvalds 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312 313
	if (retval)
		goto exit;

	if (value == PCI_SPEED_UNKNOWN)
		speed_string = unknown_speed;
	else
		speed_string = pci_bus_speed_strings[value];
	
	retval = sprintf (buf, "%s\n", speed_string);

exit:
	return retval;
}

A
Alex Chiang 已提交
314
static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
L
Linus Torvalds 已提交
315 316 317 318
	.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
	.show = cur_bus_speed_read_file,
};

A
Alex Chiang 已提交
319
static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
L
Linus Torvalds 已提交
320 321
		size_t count)
{
A
Alex Chiang 已提交
322
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344
	unsigned long ltest;
	u32 test;
	int retval = 0;

	ltest = simple_strtoul (buf, NULL, 10);
	test = (u32)(ltest & 0xffffffff);
	dbg ("test = %d\n", test);

	if (!try_module_get(slot->ops->owner)) {
		retval = -ENODEV;
		goto exit;
	}
	if (slot->ops->hardware_test)
		retval = slot->ops->hardware_test(slot, test);
	module_put(slot->ops->owner);

exit:	
	if (retval)
		return retval;
	return count;
}

A
Alex Chiang 已提交
345
static struct pci_slot_attribute hotplug_slot_attr_test = {
L
Linus Torvalds 已提交
346 347 348 349
	.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
	.store = test_write_file
};

350
static bool has_power_file(struct pci_slot *pci_slot)
L
Linus Torvalds 已提交
351
{
A
Alex Chiang 已提交
352
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
353
	if ((!slot) || (!slot->ops))
354
		return false;
L
Linus Torvalds 已提交
355 356 357
	if ((slot->ops->enable_slot) ||
	    (slot->ops->disable_slot) ||
	    (slot->ops->get_power_status))
358 359
		return true;
	return false;
L
Linus Torvalds 已提交
360 361
}

362
static bool has_attention_file(struct pci_slot *pci_slot)
L
Linus Torvalds 已提交
363
{
A
Alex Chiang 已提交
364
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
365
	if ((!slot) || (!slot->ops))
366
		return false;
L
Linus Torvalds 已提交
367 368
	if ((slot->ops->set_attention_status) ||
	    (slot->ops->get_attention_status))
369 370
		return true;
	return false;
L
Linus Torvalds 已提交
371 372
}

373
static bool has_latch_file(struct pci_slot *pci_slot)
L
Linus Torvalds 已提交
374
{
A
Alex Chiang 已提交
375
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
376
	if ((!slot) || (!slot->ops))
377
		return false;
L
Linus Torvalds 已提交
378
	if (slot->ops->get_latch_status)
379 380
		return true;
	return false;
L
Linus Torvalds 已提交
381 382
}

383
static bool has_adapter_file(struct pci_slot *pci_slot)
L
Linus Torvalds 已提交
384
{
A
Alex Chiang 已提交
385
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
386
	if ((!slot) || (!slot->ops))
387
		return false;
L
Linus Torvalds 已提交
388
	if (slot->ops->get_adapter_status)
389 390
		return true;
	return false;
L
Linus Torvalds 已提交
391 392
}

393
static bool has_max_bus_speed_file(struct pci_slot *pci_slot)
L
Linus Torvalds 已提交
394
{
A
Alex Chiang 已提交
395
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
396
	if ((!slot) || (!slot->ops))
397
		return false;
L
Linus Torvalds 已提交
398
	if (slot->ops->get_max_bus_speed)
399 400
		return true;
	return false;
L
Linus Torvalds 已提交
401 402
}

403
static bool has_cur_bus_speed_file(struct pci_slot *pci_slot)
L
Linus Torvalds 已提交
404
{
A
Alex Chiang 已提交
405
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
406
	if ((!slot) || (!slot->ops))
407
		return false;
L
Linus Torvalds 已提交
408
	if (slot->ops->get_cur_bus_speed)
409 410
		return true;
	return false;
L
Linus Torvalds 已提交
411 412
}

413
static bool has_test_file(struct pci_slot *pci_slot)
L
Linus Torvalds 已提交
414
{
A
Alex Chiang 已提交
415
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
416
	if ((!slot) || (!slot->ops))
417
		return false;
L
Linus Torvalds 已提交
418
	if (slot->ops->hardware_test)
419 420
		return true;
	return false;
L
Linus Torvalds 已提交
421 422
}

A
Alex Chiang 已提交
423
static int fs_add_slot(struct pci_slot *slot)
L
Linus Torvalds 已提交
424
{
425
	int retval = 0;
L
Linus Torvalds 已提交
426

427 428 429
	if (has_power_file(slot)) {
		retval = sysfs_create_file(&slot->kobj,
					   &hotplug_slot_attr_power.attr);
430 431 432
		if (retval)
			goto exit_power;
	}
L
Linus Torvalds 已提交
433

434
	if (has_attention_file(slot)) {
435 436 437 438 439
		retval = sysfs_create_file(&slot->kobj,
					   &hotplug_slot_attr_attention.attr);
		if (retval)
			goto exit_attention;
	}
L
Linus Torvalds 已提交
440

441
	if (has_latch_file(slot)) {
442 443 444 445 446
		retval = sysfs_create_file(&slot->kobj,
					   &hotplug_slot_attr_latch.attr);
		if (retval)
			goto exit_latch;
	}
L
Linus Torvalds 已提交
447

448
	if (has_adapter_file(slot)) {
449 450 451 452 453
		retval = sysfs_create_file(&slot->kobj,
					   &hotplug_slot_attr_presence.attr);
		if (retval)
			goto exit_adapter;
	}
L
Linus Torvalds 已提交
454

455
	if (has_max_bus_speed_file(slot)) {
456
		retval = sysfs_create_file(&slot->kobj,
457
					&hotplug_slot_attr_max_bus_speed.attr);
458 459 460 461
		if (retval)
			goto exit_max_speed;
	}

462
	if (has_cur_bus_speed_file(slot)) {
463
		retval = sysfs_create_file(&slot->kobj,
464
					&hotplug_slot_attr_cur_bus_speed.attr);
465 466 467 468
		if (retval)
			goto exit_cur_speed;
	}

469
	if (has_test_file(slot)) {
470 471 472 473 474 475 476 477 478
		retval = sysfs_create_file(&slot->kobj,
					   &hotplug_slot_attr_test.attr);
		if (retval)
			goto exit_test;
	}

	goto exit;

exit_test:
479 480 481
	if (has_cur_bus_speed_file(slot))
		sysfs_remove_file(&slot->kobj,
				  &hotplug_slot_attr_cur_bus_speed.attr);
482
exit_cur_speed:
483 484 485
	if (has_max_bus_speed_file(slot))
		sysfs_remove_file(&slot->kobj,
				  &hotplug_slot_attr_max_bus_speed.attr);
486
exit_max_speed:
487 488 489
	if (has_adapter_file(slot))
		sysfs_remove_file(&slot->kobj,
				  &hotplug_slot_attr_presence.attr);
490
exit_adapter:
491
	if (has_latch_file(slot))
492 493
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
exit_latch:
494 495 496
	if (has_attention_file(slot))
		sysfs_remove_file(&slot->kobj,
				  &hotplug_slot_attr_attention.attr);
497
exit_attention:
498
	if (has_power_file(slot))
499 500 501 502
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
exit_power:
exit:
	return retval;
L
Linus Torvalds 已提交
503 504
}

A
Alex Chiang 已提交
505
static void fs_remove_slot(struct pci_slot *slot)
L
Linus Torvalds 已提交
506
{
507
	if (has_power_file(slot))
L
Linus Torvalds 已提交
508 509
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);

510 511 512
	if (has_attention_file(slot))
		sysfs_remove_file(&slot->kobj,
				  &hotplug_slot_attr_attention.attr);
L
Linus Torvalds 已提交
513

514
	if (has_latch_file(slot))
L
Linus Torvalds 已提交
515 516
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);

517 518 519
	if (has_adapter_file(slot))
		sysfs_remove_file(&slot->kobj,
				  &hotplug_slot_attr_presence.attr);
L
Linus Torvalds 已提交
520

521 522 523
	if (has_max_bus_speed_file(slot))
		sysfs_remove_file(&slot->kobj,
				  &hotplug_slot_attr_max_bus_speed.attr);
L
Linus Torvalds 已提交
524

525 526 527
	if (has_cur_bus_speed_file(slot))
		sysfs_remove_file(&slot->kobj,
				  &hotplug_slot_attr_cur_bus_speed.attr);
L
Linus Torvalds 已提交
528

529
	if (has_test_file(slot))
L
Linus Torvalds 已提交
530 531 532 533 534 535 536 537 538 539
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_test.attr);
}

static struct hotplug_slot *get_slot_from_name (const char *name)
{
	struct hotplug_slot *slot;
	struct list_head *tmp;

	list_for_each (tmp, &pci_hotplug_slot_list) {
		slot = list_entry (tmp, struct hotplug_slot, slot_list);
A
Alex Chiang 已提交
540
		if (strcmp(hotplug_slot_name(slot), name) == 0)
541
			return slot;
L
Linus Torvalds 已提交
542
	}
543
	return NULL;
L
Linus Torvalds 已提交
544 545 546 547
}

/**
 * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
548
 * @bus: bus this slot is on
L
Linus Torvalds 已提交
549
 * @slot: pointer to the &struct hotplug_slot to register
550
 * @slot_nr: slot number
551
 * @name: name registered with kobject core
L
Linus Torvalds 已提交
552 553 554 555 556 557
 *
 * Registers a hotplug slot with the pci hotplug subsystem, which will allow
 * userspace interaction to the slot.
 *
 * Returns 0 if successful, anything else for an error.
 */
558 559
int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr,
			const char *name)
L
Linus Torvalds 已提交
560 561
{
	int result;
A
Alex Chiang 已提交
562
	struct pci_slot *pci_slot;
L
Linus Torvalds 已提交
563 564 565 566 567 568

	if (slot == NULL)
		return -ENODEV;
	if ((slot->info == NULL) || (slot->ops == NULL))
		return -EINVAL;
	if (slot->release == NULL) {
569
		dbg("Why are you trying to register a hotplug slot "
L
Linus Torvalds 已提交
570 571 572 573
		    "without a proper release function?\n");
		return -EINVAL;
	}

574 575
	mutex_lock(&pci_hp_mutex);

576 577 578 579 580
	/*
	 * No problems if we call this interface from both ACPI_PCI_SLOT
	 * driver and call it here again. If we've already created the
	 * pci_slot, the interface will simply bump the refcount.
	 */
581
	pci_slot = pci_create_slot(bus, slot_nr, name, slot);
582 583
	if (IS_ERR(pci_slot)) {
		result = PTR_ERR(pci_slot);
A
Alex Chiang 已提交
584
		goto out;
L
Linus Torvalds 已提交
585
	}
586

A
Alex Chiang 已提交
587 588 589 590 591 592 593
	slot->pci_slot = pci_slot;
	pci_slot->hotplug = slot;

	list_add(&slot->slot_list, &pci_hotplug_slot_list);

	result = fs_add_slot(pci_slot);
	kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
594
	dbg("Added slot %s to the list\n", name);
595 596
out:
	mutex_unlock(&pci_hp_mutex);
L
Linus Torvalds 已提交
597 598 599 600 601
	return result;
}

/**
 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
602
 * @hotplug: pointer to the &struct hotplug_slot to deregister
L
Linus Torvalds 已提交
603 604 605 606 607 608
 *
 * The @slot must have been registered with the pci hotplug subsystem
 * previously with a call to pci_hp_register().
 *
 * Returns 0 if successful, anything else for an error.
 */
A
Alex Chiang 已提交
609
int pci_hp_deregister(struct hotplug_slot *hotplug)
L
Linus Torvalds 已提交
610 611
{
	struct hotplug_slot *temp;
A
Alex Chiang 已提交
612
	struct pci_slot *slot;
L
Linus Torvalds 已提交
613

A
Alex Chiang 已提交
614
	if (!hotplug)
L
Linus Torvalds 已提交
615 616
		return -ENODEV;

617
	mutex_lock(&pci_hp_mutex);
A
Alex Chiang 已提交
618
	temp = get_slot_from_name(hotplug_slot_name(hotplug));
619 620
	if (temp != hotplug) {
		mutex_unlock(&pci_hp_mutex);
L
Linus Torvalds 已提交
621
		return -ENODEV;
622
	}
L
Linus Torvalds 已提交
623

A
Alex Chiang 已提交
624 625 626 627
	list_del(&hotplug->slot_list);

	slot = hotplug->pci_slot;
	fs_remove_slot(slot);
A
Alex Chiang 已提交
628
	dbg("Removed slot %s from the list\n", hotplug_slot_name(hotplug));
A
Alex Chiang 已提交
629 630 631 632

	hotplug->release(hotplug);
	slot->hotplug = NULL;
	pci_destroy_slot(slot);
633
	mutex_unlock(&pci_hp_mutex);
A
Alex Chiang 已提交
634

L
Linus Torvalds 已提交
635 636 637 638 639
	return 0;
}

/**
 * pci_hp_change_slot_info - changes the slot's information structure in the core
640
 * @hotplug: pointer to the slot whose info has changed
L
Linus Torvalds 已提交
641 642 643 644 645 646 647
 * @info: pointer to the info copy into the slot's info structure
 *
 * @slot must have been registered with the pci 
 * hotplug subsystem previously with a call to pci_hp_register().
 *
 * Returns 0 if successful, anything else for an error.
 */
A
Alex Chiang 已提交
648
int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
649
					 struct hotplug_slot_info *info)
L
Linus Torvalds 已提交
650
{
A
Alex Chiang 已提交
651 652
	struct pci_slot *slot;
	if (!hotplug || !info)
L
Linus Torvalds 已提交
653
		return -ENODEV;
A
Alex Chiang 已提交
654
	slot = hotplug->pci_slot;
L
Linus Torvalds 已提交
655

A
Alex Chiang 已提交
656
	memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));
L
Linus Torvalds 已提交
657 658 659 660 661 662 663

	return 0;
}

static int __init pci_hotplug_init (void)
{
	int result;
664

L
Linus Torvalds 已提交
665 666 667
	result = cpci_hotplug_init(debug);
	if (result) {
		err ("cpci_hotplug_init with error %d\n", result);
A
Alex Chiang 已提交
668
		goto err_cpci;
L
Linus Torvalds 已提交
669 670 671
	}

	info (DRIVER_DESC " version: " DRIVER_VERSION "\n");
672

A
Alex Chiang 已提交
673
err_cpci:
L
Linus Torvalds 已提交
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
	return result;
}

static void __exit pci_hotplug_exit (void)
{
	cpci_hotplug_exit();
}

module_init(pci_hotplug_init);
module_exit(pci_hotplug_exit);

MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debugging mode enabled or not");

EXPORT_SYMBOL_GPL(pci_hp_register);
EXPORT_SYMBOL_GPL(pci_hp_deregister);
EXPORT_SYMBOL_GPL(pci_hp_change_slot_info);