core.c 13.9 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

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

70
#include "../base.h"
L
Linus Torvalds 已提交
71 72 73 74 75 76 77 78
#include "pnpbios.h"

/*
 *
 * PnP BIOS INTERFACE
 *
 */

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

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

struct pnp_dev_node_info node_info;

/*
 *
 * DOCKING FUNCTIONS
 *
 */

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 已提交
101
	char *argv[3], **envp, *buf, *scratch;
L
Linus Torvalds 已提交
102 103
	int i = 0, value;

B
Bjorn Helgaas 已提交
104
	if (!(envp = kcalloc(20, sizeof(char *), GFP_KERNEL)))
L
Linus Torvalds 已提交
105
		return -ENOMEM;
106
	if (!(buf = kzalloc(256, GFP_KERNEL))) {
B
Bjorn Helgaas 已提交
107
		kfree(envp);
L
Linus Torvalds 已提交
108 109 110
		return -ENOMEM;
	}

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

	/* minimal command environment */
B
Bjorn Helgaas 已提交
120 121
	envp[i++] = "HOME=/";
	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
L
Linus Torvalds 已提交
122 123 124

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

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

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

B
Bjorn Helgaas 已提交
142
	value = call_usermodehelper(argv [0], argv, envp, UMH_WAIT_EXEC);
B
Bjorn Helgaas 已提交
143 144
	kfree(buf);
	kfree(envp);
L
Linus Torvalds 已提交
145 146 147 148 149 150
	return 0;
}

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

156
	set_freezable();
157
	while (1) {
L
Linus Torvalds 已提交
158
		int status;
B
Bjorn Helgaas 已提交
159

L
Linus Torvalds 已提交
160 161 162 163 164
		/*
		 * Poll every 2 seconds
		 */
		msleep_interruptible(2000);

165 166
		if (try_to_freeze())
			continue;
L
Linus Torvalds 已提交
167 168 169

		status = pnp_bios_dock_station_info(&now);

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

200
static int pnpbios_get_resources(struct pnp_dev *dev)
L
Linus Torvalds 已提交
201 202
{
	u8 nodenum = dev->number;
B
Bjorn Helgaas 已提交
203
	struct pnp_bios_node *node;
L
Linus Torvalds 已提交
204

B
Bjorn Helgaas 已提交
205
	if (!pnpbios_is_dynamic(dev))
L
Linus Torvalds 已提交
206 207
		return -EPERM;

B
Bjorn Helgaas 已提交
208
	pnp_dbg(&dev->dev, "get resources\n");
209
	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
L
Linus Torvalds 已提交
210 211
	if (!node)
		return -1;
B
Bjorn Helgaas 已提交
212
	if (pnp_bios_get_dev_node(&nodenum, (char)PNPMODE_DYNAMIC, node)) {
L
Linus Torvalds 已提交
213 214 215
		kfree(node);
		return -ENODEV;
	}
216
	pnpbios_read_resources_from_node(dev, node);
L
Linus Torvalds 已提交
217 218 219 220 221
	dev->active = pnp_is_active(dev);
	kfree(node);
	return 0;
}

222
static int pnpbios_set_resources(struct pnp_dev *dev)
L
Linus Torvalds 已提交
223 224
{
	u8 nodenum = dev->number;
B
Bjorn Helgaas 已提交
225
	struct pnp_bios_node *node;
L
Linus Torvalds 已提交
226 227 228 229 230
	int ret;

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

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

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

static int pnpbios_disable_resources(struct pnp_dev *dev)
{
B
Bjorn Helgaas 已提交
277
	struct pnp_bios_node *node;
L
Linus Torvalds 已提交
278 279 280
	u8 nodenum = dev->number;
	int ret;

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

284
	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
L
Linus Torvalds 已提交
285 286 287
	if (!node)
		return -ENOMEM;

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

310
static int __init insert_device(struct pnp_bios_node *node)
L
Linus Torvalds 已提交
311
{
B
Bjorn Helgaas 已提交
312
	struct list_head *pos;
313
	struct pnp_dev *dev;
L
Linus Torvalds 已提交
314 315 316
	char id[8];

	/* check if the device is already added */
B
Bjorn Helgaas 已提交
317
	list_for_each(pos, &pnpbios_protocol.devices) {
318 319
		dev = list_entry(pos, struct pnp_dev, protocol_list);
		if (dev->number == node->handle)
L
Linus Torvalds 已提交
320 321 322
			return -1;
	}

B
Bjorn Helgaas 已提交
323
	pnp_eisa_id_to_string(node->eisa_id & PNP_EISA_ID_MASK, id);
B
Bjorn Helgaas 已提交
324 325
	dev = pnp_alloc_dev(&pnpbios_protocol, node->handle, id);
	if (!dev)
L
Linus Torvalds 已提交
326
		return -1;
327

L
Linus Torvalds 已提交
328 329 330 331 332
	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 已提交
333
	if (!(dev->flags & PNPBIOS_NO_DISABLE) && pnpbios_is_dynamic(dev))
L
Linus Torvalds 已提交
334 335 336 337 338 339 340 341 342
		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)
343
		pnp_init_resources(dev);
L
Linus Torvalds 已提交
344 345 346 347 348 349 350 351 352 353 354 355 356 357

	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;

358
	node = kzalloc(node_info.max_node_size, GFP_KERNEL);
L
Linus Torvalds 已提交
359 360 361
	if (!node)
		return;

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

B
Bjorn Helgaas 已提交
389 390 391
	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 已提交
392 393 394 395 396 397 398 399
}

/*
 *
 * INIT AND EXIT
 *
 */

B
Bjorn Helgaas 已提交
400 401
static int pnpbios_disabled;
int pnpbios_dont_use_current_config;
L
Linus Torvalds 已提交
402 403 404 405 406 407 408

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

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

484
static int __init exploding_pnp_bios(const struct dmi_system_id *d)
L
Linus Torvalds 已提交
485 486 487 488 489
{
	printk(KERN_WARNING "%s detected. Disabling PnPBIOS\n", d->ident);
	return 0;
}

490
static struct dmi_system_id pnpbios_dmi_table[] __initdata = {
B
Bjorn Helgaas 已提交
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509
	{			/* 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 已提交
510 511 512 513 514 515
};

static int __init pnpbios_init(void)
{
	int ret;

516 517
	if (pnpbios_disabled || dmi_check_system(pnpbios_dmi_table) ||
	    paravirt_enabled()) {
L
Linus Torvalds 已提交
518 519 520 521 522 523 524 525 526
		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 已提交
527
#endif				/* CONFIG_ACPI */
L
Linus Torvalds 已提交
528 529 530 531 532 533 534 535 536 537 538

	/* 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 已提交
539 540
		printk(KERN_ERR
		       "PnPBIOS: Unable to get node info.  Aborting.\n");
L
Linus Torvalds 已提交
541 542 543 544 545 546
		return ret;
	}

	/* register with the pnp layer */
	ret = pnp_register_protocol(&pnpbios_protocol);
	if (ret) {
B
Bjorn Helgaas 已提交
547 548
		printk(KERN_ERR
		       "PnPBIOS: Unable to register driver.  Aborting.\n");
L
Linus Torvalds 已提交
549 550 551 552 553 554 555 556 557 558 559
		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();

560
	pnp_platform_devices = 1;
L
Linus Torvalds 已提交
561 562 563
	return 0;
}

564
fs_initcall(pnpbios_init);
L
Linus Torvalds 已提交
565 566 567

static int __init pnpbios_thread_init(void)
{
568
	struct task_struct *task;
P
Paul Bolle 已提交
569

L
Linus Torvalds 已提交
570 571
	if (pnpbios_disabled)
		return 0;
572 573 574 575 576 577

	init_completion(&unload_sem);
	task = kthread_run(pnp_dock_thread, NULL, "kpnpbiosd");
	if (IS_ERR(task))
		return PTR_ERR(task);

L
Linus Torvalds 已提交
578 579 580 581 582 583 584
	return 0;
}

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

EXPORT_SYMBOL(pnpbios_protocol);