pci_hotplug_core.c 17.1 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 40
#include <linux/pagemap.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/pci.h>
41
#include <linux/pci_hotplug.h>
L
Linus Torvalds 已提交
42
#include <asm/uaccess.h>
A
Alex Chiang 已提交
43
#include "../pci.h"
L
Linus Torvalds 已提交
44 45 46

#define MY_NAME	"pci_hotplug"

47
#define dbg(fmt, arg...) do { if (debug) printk(KERN_DEBUG "%s: %s: " fmt , MY_NAME , __func__ , ## arg); } while (0)
L
Linus Torvalds 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#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);
A
Alex Chiang 已提交
64
static DEFINE_SPINLOCK(pci_hotplug_slot_list_lock);
L
Linus Torvalds 已提交
65 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

/* 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;							\
105
	if (!try_module_get(ops->owner))				\
106 107 108 109 110 111
		return -ENODEV;						\
	if (ops->get_##name)						\
		retval = ops->get_##name(slot, value);			\
	else								\
		*value = slot->info->name;				\
	module_put(ops->owner);						\
L
Linus Torvalds 已提交
112 113 114 115 116 117 118 119 120 121
	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 已提交
122
static ssize_t power_read_file(struct pci_slot *slot, char *buf)
L
Linus Torvalds 已提交
123 124 125 126
{
	int retval;
	u8 value;

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

A
Alex Chiang 已提交
135
static ssize_t power_write_file(struct pci_slot *pci_slot, const char *buf,
L
Linus Torvalds 已提交
136 137
		size_t count)
{
A
Alex Chiang 已提交
138
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
139 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
	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 已提交
174
static struct pci_slot_attribute hotplug_slot_attr_power = {
L
Linus Torvalds 已提交
175 176 177 178 179
	.attr = {.name = "power", .mode = S_IFREG | S_IRUGO | S_IWUSR},
	.show = power_read_file,
	.store = power_write_file
};

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

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

exit:
	return retval;
}

A
Alex Chiang 已提交
194
static ssize_t attention_write_file(struct pci_slot *slot, const char *buf,
L
Linus Torvalds 已提交
195 196
		size_t count)
{
A
Alex Chiang 已提交
197
	struct hotplug_slot_ops *ops = slot->hotplug->ops;
L
Linus Torvalds 已提交
198 199 200 201 202 203 204 205
	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 已提交
206
	if (!try_module_get(ops->owner)) {
L
Linus Torvalds 已提交
207 208 209
		retval = -ENODEV;
		goto exit;
	}
A
Alex Chiang 已提交
210 211 212
	if (ops->set_attention_status)
		retval = ops->set_attention_status(slot->hotplug, attention);
	module_put(ops->owner);
L
Linus Torvalds 已提交
213 214 215 216 217 218 219

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

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

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

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

exit:
	return retval;
}

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

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

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

exit:
	return retval;
}

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

static char *unknown_speed = "Unknown bus speed";

A
Alex Chiang 已提交
266
static ssize_t max_bus_speed_read_file(struct pci_slot *slot, char *buf)
L
Linus Torvalds 已提交
267 268 269 270 271
{
	char *speed_string;
	int retval;
	enum pci_bus_speed value;
	
A
Alex Chiang 已提交
272
	retval = get_max_bus_speed(slot->hotplug, &value);
L
Linus Torvalds 已提交
273 274 275 276 277 278 279 280 281 282 283 284 285 286
	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 已提交
287
static struct pci_slot_attribute hotplug_slot_attr_max_bus_speed = {
L
Linus Torvalds 已提交
288 289 290 291
	.attr = {.name = "max_bus_speed", .mode = S_IFREG | S_IRUGO},
	.show = max_bus_speed_read_file,
};

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

A
Alex Chiang 已提交
298
	retval = get_cur_bus_speed(slot->hotplug, &value);
L
Linus Torvalds 已提交
299 300 301 302 303 304 305 306 307 308 309 310 311 312
	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 已提交
313
static struct pci_slot_attribute hotplug_slot_attr_cur_bus_speed = {
L
Linus Torvalds 已提交
314 315 316 317
	.attr = {.name = "cur_bus_speed", .mode = S_IFREG | S_IRUGO},
	.show = cur_bus_speed_read_file,
};

A
Alex Chiang 已提交
318
static ssize_t test_write_file(struct pci_slot *pci_slot, const char *buf,
L
Linus Torvalds 已提交
319 320
		size_t count)
{
A
Alex Chiang 已提交
321
	struct hotplug_slot *slot = pci_slot->hotplug;
L
Linus Torvalds 已提交
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
	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 已提交
344
static struct pci_slot_attribute hotplug_slot_attr_test = {
L
Linus Torvalds 已提交
345 346 347 348
	.attr = {.name = "test", .mode = S_IFREG | S_IRUGO | S_IWUSR},
	.store = test_write_file
};

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

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

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

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

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

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

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

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

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

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

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

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

453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
	if (has_max_bus_speed_file(slot) == 0) {
		retval = sysfs_create_file(&slot->kobj,
					   &hotplug_slot_attr_max_bus_speed.attr);
		if (retval)
			goto exit_max_speed;
	}

	if (has_cur_bus_speed_file(slot) == 0) {
		retval = sysfs_create_file(&slot->kobj,
					   &hotplug_slot_attr_cur_bus_speed.attr);
		if (retval)
			goto exit_cur_speed;
	}

	if (has_test_file(slot) == 0) {
		retval = sysfs_create_file(&slot->kobj,
					   &hotplug_slot_attr_test.attr);
		if (retval)
			goto exit_test;
	}

	goto exit;

exit_test:
L
Linus Torvalds 已提交
477
	if (has_cur_bus_speed_file(slot) == 0)
478
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
L
Linus Torvalds 已提交
479

480 481 482
exit_cur_speed:
	if (has_max_bus_speed_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
L
Linus Torvalds 已提交
483

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501
exit_max_speed:
	if (has_adapter_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);

exit_adapter:
	if (has_latch_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);

exit_latch:
	if (has_attention_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);

exit_attention:
	if (has_power_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
exit_power:
exit:
	return retval;
L
Linus Torvalds 已提交
502 503
}

A
Alex Chiang 已提交
504
static void fs_remove_slot(struct pci_slot *slot)
L
Linus Torvalds 已提交
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532
{
	if (has_power_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);

	if (has_attention_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);

	if (has_latch_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);

	if (has_adapter_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);

	if (has_max_bus_speed_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);

	if (has_cur_bus_speed_file(slot) == 0)
		sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);

	if (has_test_file(slot) == 0)
		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;

A
Alex Chiang 已提交
533
	spin_lock(&pci_hotplug_slot_list_lock);
L
Linus Torvalds 已提交
534 535 536
	list_for_each (tmp, &pci_hotplug_slot_list) {
		slot = list_entry (tmp, struct hotplug_slot, slot_list);
		if (strcmp(slot->name, name) == 0)
A
Alex Chiang 已提交
537
			goto out;
L
Linus Torvalds 已提交
538
	}
A
Alex Chiang 已提交
539 540 541 542
	slot = NULL;
out:
	spin_unlock(&pci_hotplug_slot_list_lock);
	return slot;
L
Linus Torvalds 已提交
543 544 545 546
}

/**
 * pci_hp_register - register a hotplug_slot with the PCI hotplug subsystem
547
 * @bus: bus this slot is on
L
Linus Torvalds 已提交
548
 * @slot: pointer to the &struct hotplug_slot to register
549
 * @slot_nr: slot number
L
Linus Torvalds 已提交
550 551 552 553 554 555
 *
 * 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.
 */
A
Alex Chiang 已提交
556
int pci_hp_register(struct hotplug_slot *slot, struct pci_bus *bus, int slot_nr)
L
Linus Torvalds 已提交
557 558
{
	int result;
A
Alex Chiang 已提交
559
	struct pci_slot *pci_slot;
L
Linus Torvalds 已提交
560 561 562 563 564 565

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

571
	/* Check if we have already registered a slot with the same name. */
572
	if (get_slot_from_name(slot->name))
573 574
		return -EEXIST;

575 576 577 578 579
	/*
	 * 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.
	 */
A
Alex Chiang 已提交
580 581 582 583 584 585 586 587
	pci_slot = pci_create_slot(bus, slot_nr, slot->name);
	if (IS_ERR(pci_slot))
		return PTR_ERR(pci_slot);

	if (pci_slot->hotplug) {
		dbg("%s: already claimed\n", __func__);
		pci_destroy_slot(pci_slot);
		return -EBUSY;
L
Linus Torvalds 已提交
588
	}
589

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

593 594 595 596 597 598 599 600 601 602 603
	/*
	 * Allow pcihp drivers to override the ACPI_PCI_SLOT name.
	 */
	if (strcmp(kobject_name(&pci_slot->kobj), slot->name)) {
		result = kobject_rename(&pci_slot->kobj, slot->name);
		if (result) {
			pci_destroy_slot(pci_slot);
			return result;
		}
	}

A
Alex Chiang 已提交
604 605 606 607 608 609 610 611
	spin_lock(&pci_hotplug_slot_list_lock);
	list_add(&slot->slot_list, &pci_hotplug_slot_list);
	spin_unlock(&pci_hotplug_slot_list_lock);

	result = fs_add_slot(pci_slot);
	kobject_uevent(&pci_slot->kobj, KOBJ_ADD);
	dbg("Added slot %s to the list\n", slot->name);

L
Linus Torvalds 已提交
612 613 614 615 616 617

	return result;
}

/**
 * pci_hp_deregister - deregister a hotplug_slot with the PCI hotplug subsystem
618
 * @hotplug: pointer to the &struct hotplug_slot to deregister
L
Linus Torvalds 已提交
619 620 621 622 623 624
 *
 * 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 已提交
625
int pci_hp_deregister(struct hotplug_slot *hotplug)
L
Linus Torvalds 已提交
626 627
{
	struct hotplug_slot *temp;
A
Alex Chiang 已提交
628
	struct pci_slot *slot;
L
Linus Torvalds 已提交
629

A
Alex Chiang 已提交
630
	if (!hotplug)
L
Linus Torvalds 已提交
631 632
		return -ENODEV;

A
Alex Chiang 已提交
633 634
	temp = get_slot_from_name(hotplug->name);
	if (temp != hotplug)
L
Linus Torvalds 已提交
635 636
		return -ENODEV;

A
Alex Chiang 已提交
637 638 639 640 641 642 643 644 645 646 647 648
	spin_lock(&pci_hotplug_slot_list_lock);
	list_del(&hotplug->slot_list);
	spin_unlock(&pci_hotplug_slot_list_lock);

	slot = hotplug->pci_slot;
	fs_remove_slot(slot);
	dbg("Removed slot %s from the list\n", hotplug->name);

	hotplug->release(hotplug);
	slot->hotplug = NULL;
	pci_destroy_slot(slot);

L
Linus Torvalds 已提交
649 650 651 652 653
	return 0;
}

/**
 * pci_hp_change_slot_info - changes the slot's information structure in the core
654
 * @hotplug: pointer to the slot whose info has changed
L
Linus Torvalds 已提交
655 656 657 658 659 660 661
 * @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 已提交
662
int __must_check pci_hp_change_slot_info(struct hotplug_slot *hotplug,
663
					 struct hotplug_slot_info *info)
L
Linus Torvalds 已提交
664
{
A
Alex Chiang 已提交
665 666
	struct pci_slot *slot;
	if (!hotplug || !info)
L
Linus Torvalds 已提交
667
		return -ENODEV;
A
Alex Chiang 已提交
668
	slot = hotplug->pci_slot;
L
Linus Torvalds 已提交
669

A
Alex Chiang 已提交
670
	memcpy(hotplug->info, info, sizeof(struct hotplug_slot_info));
L
Linus Torvalds 已提交
671 672 673 674 675 676 677

	return 0;
}

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

L
Linus Torvalds 已提交
679 680 681
	result = cpci_hotplug_init(debug);
	if (result) {
		err ("cpci_hotplug_init with error %d\n", result);
A
Alex Chiang 已提交
682
		goto err_cpci;
L
Linus Torvalds 已提交
683 684 685
	}

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

A
Alex Chiang 已提交
687
err_cpci:
L
Linus Torvalds 已提交
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707
	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);