core.c 14.2 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
/* 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/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>
63
#include <linux/freezer.h>
64
#include <linux/kthread.h>
L
Linus Torvalds 已提交
65 66 67 68 69 70

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

71
#include "../base.h"
L
Linus Torvalds 已提交
72 73 74 75 76 77 78 79
#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 (!(envp = kcalloc(20, sizeof(char *), GFP_KERNEL)))
L
Linus Torvalds 已提交
109
		return -ENOMEM;
110
	if (!(buf = kzalloc(256, GFP_KERNEL))) {
B
Bjorn Helgaas 已提交
111
		kfree(envp);
L
Linus Torvalds 已提交
112 113 114
		return -ENOMEM;
	}

B
Bjorn Helgaas 已提交
115 116 117 118
	/* 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 已提交
119 120 121
	argv[0] = "/sbin/pnpbios";
	argv[1] = "dock";
	argv[2] = NULL;
L
Linus Torvalds 已提交
122 123

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

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

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

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

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

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

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

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

169 170
		if (try_to_freeze())
			continue;
L
Linus Torvalds 已提交
171 172 173

		status = pnp_bios_dock_station_info(&now);

B
Bjorn Helgaas 已提交
174
		switch (status) {
L
Linus Torvalds 已提交
175 176 177
			/*
			 * No dock to manage
			 */
B
Bjorn Helgaas 已提交
178 179 180 181 182 183 184 185 186 187 188
		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 已提交
189
		}
B
Bjorn Helgaas 已提交
190 191
		if (d != docked) {
			if (pnp_dock_event(d, &now) == 0) {
L
Linus Torvalds 已提交
192 193
				docked = d;
#if 0
B
Bjorn Helgaas 已提交
194 195 196
				printk(KERN_INFO
				       "PnPBIOS: Docking station %stached\n",
				       docked ? "at" : "de");
L
Linus Torvalds 已提交
197 198 199 200 201 202 203
#endif
			}
		}
	}
	complete_and_exit(&unload_sem, 0);
}

B
Bjorn Helgaas 已提交
204
#endif				/* CONFIG_HOTPLUG */
L
Linus Torvalds 已提交
205

206
static int pnpbios_get_resources(struct pnp_dev *dev)
L
Linus Torvalds 已提交
207 208
{
	u8 nodenum = dev->number;
B
Bjorn Helgaas 已提交
209
	struct pnp_bios_node *node;
L
Linus Torvalds 已提交
210

B
Bjorn Helgaas 已提交
211
	if (!pnpbios_is_dynamic(dev))
L
Linus Torvalds 已提交
212 213
		return -EPERM;

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

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

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

237
	dev_dbg(&dev->dev, "set resources\n");
238
	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
L
Linus Torvalds 已提交
239 240
	if (!node)
		return -1;
B
Bjorn Helgaas 已提交
241
	if (pnp_bios_get_dev_node(&nodenum, (char)PNPMODE_DYNAMIC, node)) {
L
Linus Torvalds 已提交
242 243 244
		kfree(node);
		return -ENODEV;
	}
245
	if (pnpbios_write_resources_to_node(dev, node) < 0) {
L
Linus Torvalds 已提交
246 247 248 249 250 251 252 253 254 255
		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 已提交
256
static void pnpbios_zero_data_stream(struct pnp_bios_node *node)
L
Linus Torvalds 已提交
257
{
B
Bjorn Helgaas 已提交
258 259
	unsigned char *p = (char *)node->data;
	unsigned char *end = (char *)(node->data + node->size);
L
Linus Torvalds 已提交
260 261
	unsigned int len;
	int i;
B
Bjorn Helgaas 已提交
262

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

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

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

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

B
Bjorn Helgaas 已提交
294
	if (pnp_bios_get_dev_node(&nodenum, (char)PNPMODE_DYNAMIC, node)) {
L
Linus Torvalds 已提交
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
		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 已提交
310 311 312
	.name = "Plug and Play BIOS",
	.get = pnpbios_get_resources,
	.set = pnpbios_set_resources,
L
Linus Torvalds 已提交
313 314 315
	.disable = pnpbios_disable_resources,
};

316
static int __init insert_device(struct pnp_bios_node *node)
L
Linus Torvalds 已提交
317
{
B
Bjorn Helgaas 已提交
318
	struct list_head *pos;
319
	struct pnp_dev *dev;
L
Linus Torvalds 已提交
320 321 322
	char id[8];

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

B
Bjorn Helgaas 已提交
329
	pnp_eisa_id_to_string(node->eisa_id & PNP_EISA_ID_MASK, id);
B
Bjorn Helgaas 已提交
330 331
	dev = pnp_alloc_dev(&pnpbios_protocol, node->handle, id);
	if (!dev)
L
Linus Torvalds 已提交
332
		return -1;
333

L
Linus Torvalds 已提交
334 335 336 337 338
	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 已提交
339
	if (!(dev->flags & PNPBIOS_NO_DISABLE) && pnpbios_is_dynamic(dev))
L
Linus Torvalds 已提交
340 341 342 343 344 345 346 347 348
		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;

	/* clear out the damaged flags */
	if (!dev->active)
349
		pnp_init_resources(dev);
L
Linus Torvalds 已提交
350 351 352 353 354 355 356 357 358 359 360 361 362 363

	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;

364
	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
L
Linus Torvalds 已提交
365 366 367
	if (!node)
		return;

B
Bjorn Helgaas 已提交
368
	for (nodenum = 0; nodenum < 0xff;) {
L
Linus Torvalds 已提交
369 370 371 372 373
		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 已提交
374 375
			if (pnp_bios_get_dev_node
			    (&nodenum, (char)PNPMODE_DYNAMIC, node))
L
Linus Torvalds 已提交
376 377
				break;
		} else {
B
Bjorn Helgaas 已提交
378 379
			if (pnp_bios_get_dev_node
			    (&nodenum, (char)PNPMODE_STATIC, node))
L
Linus Torvalds 已提交
380 381 382
				break;
		}
		nodes_got++;
383
		if (insert_device(node) == 0)
L
Linus Torvalds 已提交
384 385
			devs++;
		if (nodenum <= thisnodenum) {
B
Bjorn Helgaas 已提交
386 387 388 389
			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 已提交
390 391 392 393 394
			break;
		}
	}
	kfree(node);

B
Bjorn Helgaas 已提交
395 396 397
	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 已提交
398 399 400 401 402 403 404 405
}

/*
 *
 * INIT AND EXIT
 *
 */

B
Bjorn Helgaas 已提交
406 407
static int pnpbios_disabled;
int pnpbios_dont_use_current_config;
L
Linus Torvalds 已提交
408 409 410 411 412 413 414

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

	while ((str != NULL) && (*str != '\0')) {
		if (strncmp(str, "off", 3) == 0)
B
Bjorn Helgaas 已提交
415
			pnpbios_disabled = 1;
L
Linus Torvalds 已提交
416
		if (strncmp(str, "on", 2) == 0)
B
Bjorn Helgaas 已提交
417
			pnpbios_disabled = 0;
L
Linus Torvalds 已提交
418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444
		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 已提交
445
	 * Search the defined area (0xf0000-0xffff0) for a valid PnP BIOS
L
Linus Torvalds 已提交
446 447 448
	 * structure and, if one is found, sets up the selectors and
	 * entry points
	 */
B
Bjorn Helgaas 已提交
449 450
	for (check = (union pnp_bios_install_struct *)__va(0xf0000);
	     check < (union pnp_bios_install_struct *)__va(0xffff0);
L
Linus Torvalds 已提交
451 452 453
	     check = (void *)check + 16) {
		if (check->fields.signature != PNP_SIGNATURE)
			continue;
B
Bjorn Helgaas 已提交
454 455 456
		printk(KERN_INFO
		       "PnPBIOS: Found PnP BIOS installation structure at 0x%p\n",
		       check);
L
Linus Torvalds 已提交
457 458
		length = check->fields.length;
		if (!length) {
B
Bjorn Helgaas 已提交
459 460
			printk(KERN_ERR
			       "PnPBIOS: installation structure is invalid, skipping\n");
L
Linus Torvalds 已提交
461 462 463 464 465
			continue;
		}
		for (sum = 0, i = 0; i < length; i++)
			sum += check->chars[i];
		if (sum) {
B
Bjorn Helgaas 已提交
466 467
			printk(KERN_ERR
			       "PnPBIOS: installation structure is corrupted, skipping\n");
L
Linus Torvalds 已提交
468 469 470
			continue;
		}
		if (check->fields.version < 0x10) {
B
Bjorn Helgaas 已提交
471 472
			printk(KERN_WARNING
			       "PnPBIOS: PnP BIOS version %d.%d is not supported\n",
L
Linus Torvalds 已提交
473 474 475 476
			       check->fields.version >> 4,
			       check->fields.version & 15);
			continue;
		}
B
Bjorn Helgaas 已提交
477 478 479
		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 已提交
480 481 482 483 484 485 486 487 488 489
		       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;
}

490
static int __init exploding_pnp_bios(const struct dmi_system_id *d)
L
Linus Torvalds 已提交
491 492 493 494 495
{
	printk(KERN_WARNING "%s detected. Disabling PnPBIOS\n", d->ident);
	return 0;
}

496
static struct dmi_system_id pnpbios_dmi_table[] __initdata = {
B
Bjorn Helgaas 已提交
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515
	{			/* 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 已提交
516 517 518 519 520 521
};

static int __init pnpbios_init(void)
{
	int ret;

522 523 524 525
#if defined(CONFIG_PPC_MERGE)
	if (check_legacy_ioport(PNPBIOS_BASE))
		return -ENODEV;
#endif
526 527
	if (pnpbios_disabled || dmi_check_system(pnpbios_dmi_table) ||
	    paravirt_enabled()) {
L
Linus Torvalds 已提交
528 529 530 531 532 533 534 535 536
		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 已提交
537
#endif				/* CONFIG_ACPI */
L
Linus Torvalds 已提交
538 539 540 541 542 543 544 545 546 547 548

	/* 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 已提交
549 550
		printk(KERN_ERR
		       "PnPBIOS: Unable to get node info.  Aborting.\n");
L
Linus Torvalds 已提交
551 552 553 554 555 556
		return ret;
	}

	/* register with the pnp layer */
	ret = pnp_register_protocol(&pnpbios_protocol);
	if (ret) {
B
Bjorn Helgaas 已提交
557 558
		printk(KERN_ERR
		       "PnPBIOS: Unable to register driver.  Aborting.\n");
L
Linus Torvalds 已提交
559 560 561 562 563 564 565 566 567 568 569
		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();

570
	pnp_platform_devices = 1;
L
Linus Torvalds 已提交
571 572 573
	return 0;
}

574
fs_initcall(pnpbios_init);
L
Linus Torvalds 已提交
575 576 577

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

580 581 582 583
#if defined(CONFIG_PPC_MERGE)
	if (check_legacy_ioport(PNPBIOS_BASE))
		return 0;
#endif
L
Linus Torvalds 已提交
584 585 586 587
	if (pnpbios_disabled)
		return 0;
#ifdef CONFIG_HOTPLUG
	init_completion(&unload_sem);
588 589
	task = kthread_run(pnp_dock_thread, NULL, "kpnpbiosd");
	if (!IS_ERR(task))
L
Linus Torvalds 已提交
590 591 592 593 594 595 596 597 598
		unloading = 0;
#endif
	return 0;
}

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

EXPORT_SYMBOL(pnpbios_protocol);