core.c 14.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 24 25 26 27 28 29 30 31 32 33 34
/*
 * pnpbios -- PnP BIOS driver
 *
 * This driver provides access to Plug-'n'-Play services provided by
 * the PnP BIOS firmware, described in the following documents:
 *   Plug and Play BIOS Specification, Version 1.0A, 5 May 1994
 *   Plug and Play BIOS Clarification Paper, 6 October 1994
 *     Compaq Computer Corporation, Phoenix Technologies Ltd., Intel Corp.
 * 
 * Originally (C) 1998 Christian Schmidt <schmidt@digadd.de>
 * Modifications (C) 1998 Tom Lees <tom@lpsg.demon.co.uk>
 * Minor reorganizations by David Hinds <dahinds@users.sourceforge.net>
 * Further modifications (C) 2001, 2002 by:
 *   Alan Cox <alan@redhat.com>
 *   Thomas Hood
 *   Brian Gerst <bgerst@didntduck.org>
 *
 * Ported to the PnP Layer and several additional improvements (C) 2002
 * by Adam Belay <ambx1@neo.rr.com>
 *
 * 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, 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.  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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
B
Bjorn Helgaas 已提交
35

L
Linus Torvalds 已提交
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
/* Change Log
 *
 * Adam Belay - <ambx1@neo.rr.com> - March 16, 2003
 * rev 1.01	Only call pnp_bios_dev_node_info once
 *		Added pnpbios_print_status
 *		Added several new error messages and info messages
 *		Added pnpbios_interface_attach_device
 *		integrated core and proc init system
 *		Introduced PNPMODE flags
 *		Removed some useless includes
 */

#include <linux/types.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/linkage.h>
#include <linux/kernel.h>
#include <linux/pnpbios.h>
#include <linux/device.h>
#include <linux/pnp.h>
#include <linux/mm.h>
#include <linux/smp.h>
#include <linux/slab.h>
#include <linux/completion.h>
#include <linux/spinlock.h>
#include <linux/dmi.h>
#include <linux/delay.h>
#include <linux/acpi.h>
64
#include <linux/freezer.h>
65
#include <linux/kthread.h>
L
Linus Torvalds 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79

#include <asm/page.h>
#include <asm/desc.h>
#include <asm/system.h>
#include <asm/byteorder.h>

#include "pnpbios.h"

/*
 *
 * PnP BIOS INTERFACE
 *
 */

B
Bjorn Helgaas 已提交
80
static union pnp_bios_install_struct *pnp_bios_install = NULL;
L
Linus Torvalds 已提交
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

int pnp_bios_present(void)
{
	return (pnp_bios_install != NULL);
}

struct pnp_dev_node_info node_info;

/*
 *
 * DOCKING FUNCTIONS
 *
 */

#ifdef CONFIG_HOTPLUG

static int unloading = 0;
static struct completion unload_sem;

/*
 * (Much of this belongs in a shared routine somewhere)
 */
static int pnp_dock_event(int dock, struct pnp_docking_station_info *info)
{
B
Bjorn Helgaas 已提交
105
	char *argv[3], **envp, *buf, *scratch;
L
Linus Torvalds 已提交
106 107
	int i = 0, value;

B
Bjorn Helgaas 已提交
108
	if (!current->fs->root)
L
Linus Torvalds 已提交
109
		return -EAGAIN;
B
Bjorn Helgaas 已提交
110
	if (!(envp = kcalloc(20, sizeof(char *), GFP_KERNEL)))
L
Linus Torvalds 已提交
111
		return -ENOMEM;
112
	if (!(buf = kzalloc(256, GFP_KERNEL))) {
B
Bjorn Helgaas 已提交
113
		kfree(envp);
L
Linus Torvalds 已提交
114 115 116
		return -ENOMEM;
	}

B
Bjorn Helgaas 已提交
117 118 119 120
	/* FIXME: if there are actual users of this, it should be
	 * integrated into the driver core and use the usual infrastructure
	 * like sysfs and uevents
	 */
B
Bjorn Helgaas 已提交
121 122 123
	argv[0] = "/sbin/pnpbios";
	argv[1] = "dock";
	argv[2] = NULL;
L
Linus Torvalds 已提交
124 125

	/* minimal command environment */
B
Bjorn Helgaas 已提交
126 127
	envp[i++] = "HOME=/";
	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
L
Linus Torvalds 已提交
128 129 130

#ifdef	DEBUG
	/* hint that policy agent should enter no-stdout debug mode */
B
Bjorn Helgaas 已提交
131
	envp[i++] = "DEBUG=kernel";
L
Linus Torvalds 已提交
132 133 134 135 136 137 138
#endif
	/* extensible set of named bus-specific parameters,
	 * supporting multiple driver selection algorithms.
	 */
	scratch = buf;

	/* action:  add, remove */
B
Bjorn Helgaas 已提交
139 140
	envp[i++] = scratch;
	scratch += sprintf(scratch, "ACTION=%s", dock ? "add" : "remove") + 1;
L
Linus Torvalds 已提交
141 142

	/* Report the ident for the dock */
B
Bjorn Helgaas 已提交
143 144 145
	envp[i++] = scratch;
	scratch += sprintf(scratch, "DOCK=%x/%x/%x",
			   info->location_id, info->serial, info->capabilities);
L
Linus Torvalds 已提交
146
	envp[i] = NULL;
B
Bjorn Helgaas 已提交
147

B
Bjorn Helgaas 已提交
148
	value = call_usermodehelper(argv [0], argv, envp, UMH_WAIT_EXEC);
B
Bjorn Helgaas 已提交
149 150
	kfree(buf);
	kfree(envp);
L
Linus Torvalds 已提交
151 152 153 154 155 156
	return 0;
}

/*
 * Poll the PnP docking at regular intervals
 */
B
Bjorn Helgaas 已提交
157
static int pnp_dock_thread(void *unused)
L
Linus Torvalds 已提交
158 159 160
{
	static struct pnp_docking_station_info now;
	int docked = -1, d = 0;
B
Bjorn Helgaas 已提交
161

162
	set_freezable();
B
Bjorn Helgaas 已提交
163
	while (!unloading) {
L
Linus Torvalds 已提交
164
		int status;
B
Bjorn Helgaas 已提交
165

L
Linus Torvalds 已提交
166 167 168 169 170
		/*
		 * Poll every 2 seconds
		 */
		msleep_interruptible(2000);

171 172
		if (try_to_freeze())
			continue;
L
Linus Torvalds 已提交
173 174 175

		status = pnp_bios_dock_station_info(&now);

B
Bjorn Helgaas 已提交
176
		switch (status) {
L
Linus Torvalds 已提交
177 178 179
			/*
			 * No dock to manage
			 */
B
Bjorn Helgaas 已提交
180 181 182 183 184 185 186 187 188 189 190
		case PNP_FUNCTION_NOT_SUPPORTED:
			complete_and_exit(&unload_sem, 0);
		case PNP_SYSTEM_NOT_DOCKED:
			d = 0;
			break;
		case PNP_SUCCESS:
			d = 1;
			break;
		default:
			pnpbios_print_status("pnp_dock_thread", status);
			continue;
L
Linus Torvalds 已提交
191
		}
B
Bjorn Helgaas 已提交
192 193
		if (d != docked) {
			if (pnp_dock_event(d, &now) == 0) {
L
Linus Torvalds 已提交
194 195
				docked = d;
#if 0
B
Bjorn Helgaas 已提交
196 197 198
				printk(KERN_INFO
				       "PnPBIOS: Docking station %stached\n",
				       docked ? "at" : "de");
L
Linus Torvalds 已提交
199 200 201 202 203 204 205
#endif
			}
		}
	}
	complete_and_exit(&unload_sem, 0);
}

B
Bjorn Helgaas 已提交
206
#endif				/* CONFIG_HOTPLUG */
L
Linus Torvalds 已提交
207

B
Bjorn Helgaas 已提交
208 209
static int pnpbios_get_resources(struct pnp_dev *dev,
				 struct pnp_resource_table *res)
L
Linus Torvalds 已提交
210 211
{
	u8 nodenum = dev->number;
B
Bjorn Helgaas 已提交
212
	struct pnp_bios_node *node;
L
Linus Torvalds 已提交
213

B
Bjorn Helgaas 已提交
214
	if (!pnpbios_is_dynamic(dev))
L
Linus Torvalds 已提交
215 216
		return -EPERM;

217
	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
L
Linus Torvalds 已提交
218 219
	if (!node)
		return -1;
B
Bjorn Helgaas 已提交
220
	if (pnp_bios_get_dev_node(&nodenum, (char)PNPMODE_DYNAMIC, node)) {
L
Linus Torvalds 已提交
221 222 223 224 225 226 227 228 229
		kfree(node);
		return -ENODEV;
	}
	pnpbios_read_resources_from_node(res, node);
	dev->active = pnp_is_active(dev);
	kfree(node);
	return 0;
}

B
Bjorn Helgaas 已提交
230 231
static int pnpbios_set_resources(struct pnp_dev *dev,
				 struct pnp_resource_table *res)
L
Linus Torvalds 已提交
232 233
{
	u8 nodenum = dev->number;
B
Bjorn Helgaas 已提交
234
	struct pnp_bios_node *node;
L
Linus Torvalds 已提交
235 236 237 238 239
	int ret;

	if (!pnpbios_is_dynamic(dev))
		return -EPERM;

240
	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
L
Linus Torvalds 已提交
241 242
	if (!node)
		return -1;
B
Bjorn Helgaas 已提交
243
	if (pnp_bios_get_dev_node(&nodenum, (char)PNPMODE_DYNAMIC, node)) {
L
Linus Torvalds 已提交
244 245 246
		kfree(node);
		return -ENODEV;
	}
B
Bjorn Helgaas 已提交
247
	if (pnpbios_write_resources_to_node(res, node) < 0) {
L
Linus Torvalds 已提交
248 249 250 251 252 253 254 255 256 257
		kfree(node);
		return -1;
	}
	ret = pnp_bios_set_dev_node(node->handle, (char)PNPMODE_DYNAMIC, node);
	kfree(node);
	if (ret > 0)
		ret = -1;
	return ret;
}

B
Bjorn Helgaas 已提交
258
static void pnpbios_zero_data_stream(struct pnp_bios_node *node)
L
Linus Torvalds 已提交
259
{
B
Bjorn Helgaas 已提交
260 261
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
L
Linus Torvalds 已提交
262 263
	unsigned int len;
	int i;
B
Bjorn Helgaas 已提交
264

L
Linus Torvalds 已提交
265
	while ((char *)p < (char *)end) {
B
Bjorn Helgaas 已提交
266
		if (p[0] & 0x80) {	/* large tag */
L
Linus Torvalds 已提交
267 268 269
			len = (p[2] << 8) | p[1];
			p += 3;
		} else {
B
Bjorn Helgaas 已提交
270
			if (((p[0] >> 3) & 0x0f) == 0x0f)
L
Linus Torvalds 已提交
271 272 273 274 275 276 277 278
				return;
			len = p[0] & 0x07;
			p += 1;
		}
		for (i = 0; i < len; i++)
			p[i] = 0;
		p += len;
	}
B
Bjorn Helgaas 已提交
279 280
	printk(KERN_ERR
	       "PnPBIOS: Resource structure did not contain an end tag.\n");
L
Linus Torvalds 已提交
281 282 283 284
}

static int pnpbios_disable_resources(struct pnp_dev *dev)
{
B
Bjorn Helgaas 已提交
285
	struct pnp_bios_node *node;
L
Linus Torvalds 已提交
286 287 288
	u8 nodenum = dev->number;
	int ret;

B
Bjorn Helgaas 已提交
289
	if (dev->flags & PNPBIOS_NO_DISABLE || !pnpbios_is_dynamic(dev))
L
Linus Torvalds 已提交
290 291
		return -EPERM;

292
	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
L
Linus Torvalds 已提交
293 294 295
	if (!node)
		return -ENOMEM;

B
Bjorn Helgaas 已提交
296
	if (pnp_bios_get_dev_node(&nodenum, (char)PNPMODE_DYNAMIC, node)) {
L
Linus Torvalds 已提交
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
		kfree(node);
		return -ENODEV;
	}
	pnpbios_zero_data_stream(node);

	ret = pnp_bios_set_dev_node(dev->number, (char)PNPMODE_DYNAMIC, node);
	kfree(node);
	if (ret > 0)
		ret = -1;
	return ret;
}

/* PnP Layer support */

struct pnp_protocol pnpbios_protocol = {
B
Bjorn Helgaas 已提交
312 313 314
	.name = "Plug and Play BIOS",
	.get = pnpbios_get_resources,
	.set = pnpbios_set_resources,
L
Linus Torvalds 已提交
315 316 317
	.disable = pnpbios_disable_resources,
};

B
Bjorn Helgaas 已提交
318
static int insert_device(struct pnp_dev *dev, struct pnp_bios_node *node)
L
Linus Torvalds 已提交
319
{
B
Bjorn Helgaas 已提交
320 321
	struct list_head *pos;
	struct pnp_dev *pnp_dev;
L
Linus Torvalds 已提交
322 323 324 325 326
	struct pnp_id *dev_id;
	char id[8];

	/* check if the device is already added */
	dev->number = node->handle;
B
Bjorn Helgaas 已提交
327
	list_for_each(pos, &pnpbios_protocol.devices) {
L
Linus Torvalds 已提交
328 329 330 331 332 333
		pnp_dev = list_entry(pos, struct pnp_dev, protocol_list);
		if (dev->number == pnp_dev->number)
			return -1;
	}

	/* set the initial values for the PnP device */
334
	dev_id = kzalloc(sizeof(struct pnp_id), GFP_KERNEL);
L
Linus Torvalds 已提交
335 336
	if (!dev_id)
		return -1;
B
Bjorn Helgaas 已提交
337 338
	pnpid32_to_pnpid(node->eisa_id, id);
	memcpy(dev_id->id, id, 7);
L
Linus Torvalds 已提交
339 340 341 342 343 344
	pnp_add_id(dev_id, dev);
	pnpbios_parse_data_stream(dev, node);
	dev->active = pnp_is_active(dev);
	dev->flags = node->flags;
	if (!(dev->flags & PNPBIOS_NO_CONFIG))
		dev->capabilities |= PNP_CONFIGURABLE;
O
Ondrej Zary 已提交
345
	if (!(dev->flags & PNPBIOS_NO_DISABLE) && pnpbios_is_dynamic(dev))
L
Linus Torvalds 已提交
346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371
		dev->capabilities |= PNP_DISABLE;
	dev->capabilities |= PNP_READ;
	if (pnpbios_is_dynamic(dev))
		dev->capabilities |= PNP_WRITE;
	if (dev->flags & PNPBIOS_REMOVABLE)
		dev->capabilities |= PNP_REMOVABLE;
	dev->protocol = &pnpbios_protocol;

	/* clear out the damaged flags */
	if (!dev->active)
		pnp_init_resource_table(&dev->res);

	pnp_add_device(dev);
	pnpbios_interface_attach_device(node);

	return 0;
}

static void __init build_devlist(void)
{
	u8 nodenum;
	unsigned int nodes_got = 0;
	unsigned int devs = 0;
	struct pnp_bios_node *node;
	struct pnp_dev *dev;

372
	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
L
Linus Torvalds 已提交
373 374 375
	if (!node)
		return;

B
Bjorn Helgaas 已提交
376
	for (nodenum = 0; nodenum < 0xff;) {
L
Linus Torvalds 已提交
377 378 379 380 381
		u8 thisnodenum = nodenum;
		/* eventually we will want to use PNPMODE_STATIC here but for now
		 * dynamic will help us catch buggy bioses to add to the blacklist.
		 */
		if (!pnpbios_dont_use_current_config) {
B
Bjorn Helgaas 已提交
382 383
			if (pnp_bios_get_dev_node
			    (&nodenum, (char)PNPMODE_DYNAMIC, node))
L
Linus Torvalds 已提交
384 385
				break;
		} else {
B
Bjorn Helgaas 已提交
386 387
			if (pnp_bios_get_dev_node
			    (&nodenum, (char)PNPMODE_STATIC, node))
L
Linus Torvalds 已提交
388 389 390
				break;
		}
		nodes_got++;
B
Bjorn Helgaas 已提交
391
		dev = kzalloc(sizeof(struct pnp_dev), GFP_KERNEL);
L
Linus Torvalds 已提交
392 393
		if (!dev)
			break;
B
Bjorn Helgaas 已提交
394
		if (insert_device(dev, node) < 0)
L
Linus Torvalds 已提交
395 396 397 398
			kfree(dev);
		else
			devs++;
		if (nodenum <= thisnodenum) {
B
Bjorn Helgaas 已提交
399 400 401 402
			printk(KERN_ERR
			       "PnPBIOS: build_devlist: Node number 0x%x is out of sequence following node 0x%x. Aborting.\n",
			       (unsigned int)nodenum,
			       (unsigned int)thisnodenum);
L
Linus Torvalds 已提交
403 404 405 406 407
			break;
		}
	}
	kfree(node);

B
Bjorn Helgaas 已提交
408 409 410
	printk(KERN_INFO
	       "PnPBIOS: %i node%s reported by PnP BIOS; %i recorded by driver\n",
	       nodes_got, nodes_got != 1 ? "s" : "", devs);
L
Linus Torvalds 已提交
411 412 413 414 415 416 417 418
}

/*
 *
 * INIT AND EXIT
 *
 */

B
Bjorn Helgaas 已提交
419 420
static int pnpbios_disabled;
int pnpbios_dont_use_current_config;
L
Linus Torvalds 已提交
421 422 423 424 425 426 427

static int __init pnpbios_setup(char *str)
{
	int invert;

	while ((str != NULL) && (*str != '\0')) {
		if (strncmp(str, "off", 3) == 0)
B
Bjorn Helgaas 已提交
428
			pnpbios_disabled = 1;
L
Linus Torvalds 已提交
429
		if (strncmp(str, "on", 2) == 0)
B
Bjorn Helgaas 已提交
430
			pnpbios_disabled = 0;
L
Linus Torvalds 已提交
431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457
		invert = (strncmp(str, "no-", 3) == 0);
		if (invert)
			str += 3;
		if (strncmp(str, "curr", 4) == 0)
			pnpbios_dont_use_current_config = invert;
		str = strchr(str, ',');
		if (str != NULL)
			str += strspn(str, ", \t");
	}

	return 1;
}

__setup("pnpbios=", pnpbios_setup);

/* PnP BIOS signature: "$PnP" */
#define PNP_SIGNATURE   (('$' << 0) + ('P' << 8) + ('n' << 16) + ('P' << 24))

static int __init pnpbios_probe_system(void)
{
	union pnp_bios_install_struct *check;
	u8 sum;
	int length, i;

	printk(KERN_INFO "PnPBIOS: Scanning system for PnP BIOS support...\n");

	/*
B
Bjorn Helgaas 已提交
458
	 * Search the defined area (0xf0000-0xffff0) for a valid PnP BIOS
L
Linus Torvalds 已提交
459 460 461
	 * structure and, if one is found, sets up the selectors and
	 * entry points
	 */
B
Bjorn Helgaas 已提交
462 463
	for (check = (union pnp_bios_install_struct *)__va(0xf0000);
	     check < (union pnp_bios_install_struct *)__va(0xffff0);
L
Linus Torvalds 已提交
464 465 466
	     check = (void *)check + 16) {
		if (check->fields.signature != PNP_SIGNATURE)
			continue;
B
Bjorn Helgaas 已提交
467 468 469
		printk(KERN_INFO
		       "PnPBIOS: Found PnP BIOS installation structure at 0x%p\n",
		       check);
L
Linus Torvalds 已提交
470 471
		length = check->fields.length;
		if (!length) {
B
Bjorn Helgaas 已提交
472 473
			printk(KERN_ERR
			       "PnPBIOS: installation structure is invalid, skipping\n");
L
Linus Torvalds 已提交
474 475 476 477 478
			continue;
		}
		for (sum = 0, i = 0; i < length; i++)
			sum += check->chars[i];
		if (sum) {
B
Bjorn Helgaas 已提交
479 480
			printk(KERN_ERR
			       "PnPBIOS: installation structure is corrupted, skipping\n");
L
Linus Torvalds 已提交
481 482 483
			continue;
		}
		if (check->fields.version < 0x10) {
B
Bjorn Helgaas 已提交
484 485
			printk(KERN_WARNING
			       "PnPBIOS: PnP BIOS version %d.%d is not supported\n",
L
Linus Torvalds 已提交
486 487 488 489
			       check->fields.version >> 4,
			       check->fields.version & 15);
			continue;
		}
B
Bjorn Helgaas 已提交
490 491 492
		printk(KERN_INFO
		       "PnPBIOS: PnP BIOS version %d.%d, entry 0x%x:0x%x, dseg 0x%x\n",
		       check->fields.version >> 4, check->fields.version & 15,
L
Linus Torvalds 已提交
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508
		       check->fields.pm16cseg, check->fields.pm16offset,
		       check->fields.pm16dseg);
		pnp_bios_install = check;
		return 1;
	}

	printk(KERN_INFO "PnPBIOS: PnP BIOS support was not detected.\n");
	return 0;
}

static int __init exploding_pnp_bios(struct dmi_system_id *d)
{
	printk(KERN_WARNING "%s detected. Disabling PnPBIOS\n", d->ident);
	return 0;
}

509
static struct dmi_system_id pnpbios_dmi_table[] __initdata = {
B
Bjorn Helgaas 已提交
510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528
	{			/* PnPBIOS GPF on boot */
	 .callback = exploding_pnp_bios,
	 .ident = "Higraded P14H",
	 .matches = {
		     DMI_MATCH(DMI_BIOS_VENDOR, "American Megatrends Inc."),
		     DMI_MATCH(DMI_BIOS_VERSION, "07.00T"),
		     DMI_MATCH(DMI_SYS_VENDOR, "Higraded"),
		     DMI_MATCH(DMI_PRODUCT_NAME, "P14H"),
		     },
	 },
	{			/* PnPBIOS GPF on boot */
	 .callback = exploding_pnp_bios,
	 .ident = "ASUS P4P800",
	 .matches = {
		     DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
		     DMI_MATCH(DMI_BOARD_NAME, "P4P800"),
		     },
	 },
	{}
L
Linus Torvalds 已提交
529 530 531 532 533 534
};

static int __init pnpbios_init(void)
{
	int ret;

535 536 537 538
#if defined(CONFIG_PPC_MERGE)
	if (check_legacy_ioport(PNPBIOS_BASE))
		return -ENODEV;
#endif
539 540
	if (pnpbios_disabled || dmi_check_system(pnpbios_dmi_table) ||
	    paravirt_enabled()) {
L
Linus Torvalds 已提交
541 542 543 544 545 546 547 548 549
		printk(KERN_INFO "PnPBIOS: Disabled\n");
		return -ENODEV;
	}
#ifdef CONFIG_PNPACPI
	if (!acpi_disabled && !pnpacpi_disabled) {
		pnpbios_disabled = 1;
		printk(KERN_INFO "PnPBIOS: Disabled by ACPI PNP\n");
		return -ENODEV;
	}
B
Bjorn Helgaas 已提交
550
#endif				/* CONFIG_ACPI */
L
Linus Torvalds 已提交
551 552 553 554 555 556 557 558 559 560 561

	/* scan the system for pnpbios support */
	if (!pnpbios_probe_system())
		return -ENODEV;

	/* make preparations for bios calls */
	pnpbios_calls_init(pnp_bios_install);

	/* read the node info */
	ret = pnp_bios_dev_node_info(&node_info);
	if (ret) {
B
Bjorn Helgaas 已提交
562 563
		printk(KERN_ERR
		       "PnPBIOS: Unable to get node info.  Aborting.\n");
L
Linus Torvalds 已提交
564 565 566 567 568 569
		return ret;
	}

	/* register with the pnp layer */
	ret = pnp_register_protocol(&pnpbios_protocol);
	if (ret) {
B
Bjorn Helgaas 已提交
570 571
		printk(KERN_ERR
		       "PnPBIOS: Unable to register driver.  Aborting.\n");
L
Linus Torvalds 已提交
572 573 574 575 576 577 578 579 580 581 582
		return ret;
	}

	/* start the proc interface */
	ret = pnpbios_proc_init();
	if (ret)
		printk(KERN_ERR "PnPBIOS: Failed to create proc interface.\n");

	/* scan for pnpbios devices */
	build_devlist();

583
	pnp_platform_devices = 1;
L
Linus Torvalds 已提交
584 585 586 587 588 589 590
	return 0;
}

subsys_initcall(pnpbios_init);

static int __init pnpbios_thread_init(void)
{
591
	struct task_struct *task;
B
Bjorn Helgaas 已提交
592

593 594 595 596
#if defined(CONFIG_PPC_MERGE)
	if (check_legacy_ioport(PNPBIOS_BASE))
		return 0;
#endif
L
Linus Torvalds 已提交
597 598 599 600
	if (pnpbios_disabled)
		return 0;
#ifdef CONFIG_HOTPLUG
	init_completion(&unload_sem);
601 602
	task = kthread_run(pnp_dock_thread, NULL, "kpnpbiosd");
	if (!IS_ERR(task))
L
Linus Torvalds 已提交
603 604 605 606 607 608 609 610 611
		unloading = 0;
#endif
	return 0;
}

/* Start the kernel thread later: */
module_init(pnpbios_thread_init);

EXPORT_SYMBOL(pnpbios_protocol);