button.c 14.3 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
/*
 *  acpi_button.c - ACPI Button Driver ($Revision: 30 $)
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.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 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.  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.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
29 30 31
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
32
#include <linux/input.h>
L
Linus Torvalds 已提交
33 34 35 36 37
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

#define ACPI_BUTTON_COMPONENT		0x00080000
#define ACPI_BUTTON_CLASS		"button"
38 39 40
#define ACPI_BUTTON_FILE_INFO		"info"
#define ACPI_BUTTON_FILE_STATE		"state"
#define ACPI_BUTTON_TYPE_UNKNOWN	0x00
L
Linus Torvalds 已提交
41 42 43
#define ACPI_BUTTON_NOTIFY_STATUS	0x80

#define ACPI_BUTTON_SUBCLASS_POWER	"power"
L
Len Brown 已提交
44
#define ACPI_BUTTON_HID_POWER		"PNP0C0C"
L
Linus Torvalds 已提交
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
#define ACPI_BUTTON_DEVICE_NAME_POWER	"Power Button (CM)"
#define ACPI_BUTTON_DEVICE_NAME_POWERF	"Power Button (FF)"
#define ACPI_BUTTON_TYPE_POWER		0x01
#define ACPI_BUTTON_TYPE_POWERF		0x02

#define ACPI_BUTTON_SUBCLASS_SLEEP	"sleep"
#define ACPI_BUTTON_HID_SLEEP		"PNP0C0E"
#define ACPI_BUTTON_DEVICE_NAME_SLEEP	"Sleep Button (CM)"
#define ACPI_BUTTON_DEVICE_NAME_SLEEPF	"Sleep Button (FF)"
#define ACPI_BUTTON_TYPE_SLEEP		0x03
#define ACPI_BUTTON_TYPE_SLEEPF		0x04

#define ACPI_BUTTON_SUBCLASS_LID	"lid"
#define ACPI_BUTTON_HID_LID		"PNP0C0D"
#define ACPI_BUTTON_DEVICE_NAME_LID	"Lid Switch"
#define ACPI_BUTTON_TYPE_LID		0x05

#define _COMPONENT		ACPI_BUTTON_COMPONENT
63
ACPI_MODULE_NAME("button");
L
Linus Torvalds 已提交
64

65
MODULE_AUTHOR("Paul Diefenbaugh");
66
MODULE_DESCRIPTION("ACPI Button Driver");
L
Linus Torvalds 已提交
67 68
MODULE_LICENSE("GPL");

69 70 71 72 73 74 75 76 77 78
static const struct acpi_device_id button_device_ids[] = {
	{ACPI_BUTTON_HID_LID,    0},
	{ACPI_BUTTON_HID_SLEEP,  0},
	{ACPI_BUTTON_HID_SLEEPF, 0},
	{ACPI_BUTTON_HID_POWER,  0},
	{ACPI_BUTTON_HID_POWERF, 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, button_device_ids);

L
Len Brown 已提交
79 80
static int acpi_button_add(struct acpi_device *device);
static int acpi_button_remove(struct acpi_device *device, int type);
81 82
static int acpi_button_info_open_fs(struct inode *inode, struct file *file);
static int acpi_button_state_open_fs(struct inode *inode, struct file *file);
L
Linus Torvalds 已提交
83 84

static struct acpi_driver acpi_button_driver = {
L
Len Brown 已提交
85
	.name = "button",
L
Len Brown 已提交
86
	.class = ACPI_BUTTON_CLASS,
87
	.ids = button_device_ids,
L
Len Brown 已提交
88 89 90
	.ops = {
		.add = acpi_button_add,
		.remove = acpi_button_remove,
91
	},
L
Linus Torvalds 已提交
92 93 94
};

struct acpi_button {
L
Len Brown 已提交
95
	struct acpi_device *device;	/* Fixed button kludge */
96 97 98
	unsigned int type;
	struct input_dev *input;
	char phys[32];			/* for input device */
L
Len Brown 已提交
99
	unsigned long pushed;
L
Linus Torvalds 已提交
100 101
};

102
static const struct file_operations acpi_button_info_fops = {
L
Len Brown 已提交
103 104 105 106
	.open = acpi_button_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
107 108
};

109
static const struct file_operations acpi_button_state_fops = {
L
Len Brown 已提交
110 111 112 113
	.open = acpi_button_state_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
114
};
L
Len Brown 已提交
115

116 117 118 119
/* --------------------------------------------------------------------------
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
120
static struct proc_dir_entry *acpi_button_dir;
121 122 123

static int acpi_button_info_seq_show(struct seq_file *seq, void *offset)
{
124
	struct acpi_button *button = seq->private;
125 126

	if (!button || !button->device)
127
		return 0;
128

L
Len Brown 已提交
129 130
	seq_printf(seq, "type:                    %s\n",
		   acpi_device_name(button->device));
131

132
	return 0;
133 134 135 136 137 138
}

static int acpi_button_info_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_button_info_seq_show, PDE(inode)->data);
}
L
Len Brown 已提交
139

140 141
static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
{
142
	struct acpi_button *button = seq->private;
L
Len Brown 已提交
143 144
	acpi_status status;
	unsigned long state;
145 146

	if (!button || !button->device)
147
		return 0;
148

149
	status = acpi_evaluate_integer(button->device->handle, "_LID", NULL, &state);
150 151 152
	seq_printf(seq, "state:      %s\n",
		   ACPI_FAILURE(status) ? "unsupported" :
			(state ? "open" : "closed"));
153
	return 0;
154 155 156 157 158 159 160 161 162 163 164
}

static int acpi_button_state_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_button_state_seq_show, PDE(inode)->data);
}

static struct proc_dir_entry *acpi_power_dir;
static struct proc_dir_entry *acpi_sleep_dir;
static struct proc_dir_entry *acpi_lid_dir;

L
Len Brown 已提交
165
static int acpi_button_add_fs(struct acpi_device *device)
166
{
L
Len Brown 已提交
167
	struct proc_dir_entry *entry = NULL;
168
	struct acpi_button *button;
169 170

	if (!device || !acpi_driver_data(device))
171
		return -EINVAL;
172 173 174 175 176 177 178

	button = acpi_driver_data(device);

	switch (button->type) {
	case ACPI_BUTTON_TYPE_POWER:
	case ACPI_BUTTON_TYPE_POWERF:
		if (!acpi_power_dir)
L
Len Brown 已提交
179 180
			acpi_power_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER,
						    acpi_button_dir);
181 182 183 184 185
		entry = acpi_power_dir;
		break;
	case ACPI_BUTTON_TYPE_SLEEP:
	case ACPI_BUTTON_TYPE_SLEEPF:
		if (!acpi_sleep_dir)
L
Len Brown 已提交
186 187
			acpi_sleep_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP,
						    acpi_button_dir);
188 189 190 191
		entry = acpi_sleep_dir;
		break;
	case ACPI_BUTTON_TYPE_LID:
		if (!acpi_lid_dir)
L
Len Brown 已提交
192 193
			acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID,
						  acpi_button_dir);
194 195 196 197 198
		entry = acpi_lid_dir;
		break;
	}

	if (!entry)
199
		return -ENODEV;
200 201 202 203
	entry->owner = THIS_MODULE;

	acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry);
	if (!acpi_device_dir(device))
204
		return -ENODEV;
205 206 207 208
	acpi_device_dir(device)->owner = THIS_MODULE;

	/* 'info' [R] */
	entry = create_proc_entry(ACPI_BUTTON_FILE_INFO,
L
Len Brown 已提交
209
				  S_IRUGO, acpi_device_dir(device));
210
	if (!entry)
211
		return -ENODEV;
212 213 214 215 216 217 218 219 220
	else {
		entry->proc_fops = &acpi_button_info_fops;
		entry->data = acpi_driver_data(device);
		entry->owner = THIS_MODULE;
	}

	/* show lid state [R] */
	if (button->type == ACPI_BUTTON_TYPE_LID) {
		entry = create_proc_entry(ACPI_BUTTON_FILE_STATE,
L
Len Brown 已提交
221
					  S_IRUGO, acpi_device_dir(device));
222
		if (!entry)
223
			return -ENODEV;
224 225 226 227 228 229 230
		else {
			entry->proc_fops = &acpi_button_state_fops;
			entry->data = acpi_driver_data(device);
			entry->owner = THIS_MODULE;
		}
	}

231
	return 0;
232 233
}

L
Len Brown 已提交
234
static int acpi_button_remove_fs(struct acpi_device *device)
235
{
236
	struct acpi_button *button = acpi_driver_data(device);
237 238 239 240

	if (acpi_device_dir(device)) {
		if (button->type == ACPI_BUTTON_TYPE_LID)
			remove_proc_entry(ACPI_BUTTON_FILE_STATE,
L
Len Brown 已提交
241
					  acpi_device_dir(device));
242
		remove_proc_entry(ACPI_BUTTON_FILE_INFO,
L
Len Brown 已提交
243
				  acpi_device_dir(device));
244 245

		remove_proc_entry(acpi_device_bid(device),
L
Len Brown 已提交
246
				  acpi_device_dir(device)->parent);
247 248 249
		acpi_device_dir(device) = NULL;
	}

250
	return 0;
251 252
}

L
Linus Torvalds 已提交
253 254 255 256
/* --------------------------------------------------------------------------
                                Driver Interface
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
257
static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
258
{
259 260
	struct acpi_button *button = data;
	struct input_dev *input;
L
Linus Torvalds 已提交
261 262

	if (!button || !button->device)
263
		return;
L
Linus Torvalds 已提交
264 265 266

	switch (event) {
	case ACPI_BUTTON_NOTIFY_STATUS:
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
		input = button->input;

		if (button->type == ACPI_BUTTON_TYPE_LID) {
			struct acpi_handle *handle = button->device->handle;
			unsigned long state;

			if (!ACPI_FAILURE(acpi_evaluate_integer(handle, "_LID",
								NULL, &state)))
				input_report_switch(input, SW_LID, !state);

		} else {
			int keycode = test_bit(KEY_SLEEP, input->keybit) ?
						KEY_SLEEP : KEY_POWER;

			input_report_key(input, keycode, 1);
			input_sync(input);
			input_report_key(input, keycode, 0);
		}
		input_sync(input);

L
Len Brown 已提交
287 288
		acpi_bus_generate_event(button->device, event,
					++button->pushed);
L
Linus Torvalds 已提交
289 290 291
		break;
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
292
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
293 294 295
		break;
	}

296
	return;
L
Linus Torvalds 已提交
297 298
}

L
Len Brown 已提交
299
static acpi_status acpi_button_notify_fixed(void *data)
L
Linus Torvalds 已提交
300
{
301
	struct acpi_button *button = data;
L
Linus Torvalds 已提交
302

303
	if (!button)
304
		return AE_BAD_PARAMETER;
L
Linus Torvalds 已提交
305

306
	acpi_button_notify(button->device->handle, ACPI_BUTTON_NOTIFY_STATUS, button);
L
Linus Torvalds 已提交
307

308
	return AE_OK;
L
Linus Torvalds 已提交
309 310
}

311
static int acpi_button_install_notify_handlers(struct acpi_button *button)
L
Linus Torvalds 已提交
312
{
313
	acpi_status status;
L
Linus Torvalds 已提交
314

315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362
	switch (button->type) {
	case ACPI_BUTTON_TYPE_POWERF:
		status =
		    acpi_install_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
						     acpi_button_notify_fixed,
						     button);
		break;
	case ACPI_BUTTON_TYPE_SLEEPF:
		status =
		    acpi_install_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
						     acpi_button_notify_fixed,
						     button);
		break;
	default:
		status = acpi_install_notify_handler(button->device->handle,
						     ACPI_DEVICE_NOTIFY,
						     acpi_button_notify,
						     button);
		break;
	}

	return ACPI_FAILURE(status) ? -ENODEV : 0;
}

static void acpi_button_remove_notify_handlers(struct acpi_button *button)
{
	switch (button->type) {
	case ACPI_BUTTON_TYPE_POWERF:
		acpi_remove_fixed_event_handler(ACPI_EVENT_POWER_BUTTON,
						acpi_button_notify_fixed);
		break;
	case ACPI_BUTTON_TYPE_SLEEPF:
		acpi_remove_fixed_event_handler(ACPI_EVENT_SLEEP_BUTTON,
						acpi_button_notify_fixed);
		break;
	default:
		acpi_remove_notify_handler(button->device->handle,
					   ACPI_DEVICE_NOTIFY,
					   acpi_button_notify);
		break;
	}
}

static int acpi_button_add(struct acpi_device *device)
{
	int error;
	struct acpi_button *button;
	struct input_dev *input;
L
Linus Torvalds 已提交
363 364

	if (!device)
365
		return -EINVAL;
L
Linus Torvalds 已提交
366

367
	button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
L
Linus Torvalds 已提交
368
	if (!button)
369
		return -ENOMEM;
L
Linus Torvalds 已提交
370 371 372 373

	button->device = device;
	acpi_driver_data(device) = button;

374 375 376 377 378 379
	button->input = input = input_allocate_device();
	if (!input) {
		error = -ENOMEM;
		goto err_free_button;
	}

L
Linus Torvalds 已提交
380 381 382 383 384 385
	/*
	 * Determine the button type (via hid), as fixed-feature buttons
	 * need to be handled a bit differently than generic-space.
	 */
	if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) {
		button->type = ACPI_BUTTON_TYPE_POWER;
L
Len Brown 已提交
386 387
		strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_POWER);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
388
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
L
Len Brown 已提交
389
	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) {
L
Linus Torvalds 已提交
390 391
		button->type = ACPI_BUTTON_TYPE_POWERF;
		strcpy(acpi_device_name(device),
L
Len Brown 已提交
392 393
		       ACPI_BUTTON_DEVICE_NAME_POWERF);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
394
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
L
Len Brown 已提交
395
	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) {
L
Linus Torvalds 已提交
396
		button->type = ACPI_BUTTON_TYPE_SLEEP;
L
Len Brown 已提交
397 398
		strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_SLEEP);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
399
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
L
Len Brown 已提交
400
	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) {
L
Linus Torvalds 已提交
401 402
		button->type = ACPI_BUTTON_TYPE_SLEEPF;
		strcpy(acpi_device_name(device),
L
Len Brown 已提交
403 404
		       ACPI_BUTTON_DEVICE_NAME_SLEEPF);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
405
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
L
Len Brown 已提交
406
	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) {
L
Linus Torvalds 已提交
407
		button->type = ACPI_BUTTON_TYPE_LID;
L
Len Brown 已提交
408 409
		strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_LID);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
410
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
L
Len Brown 已提交
411
	} else {
412 413
		printk(KERN_ERR PREFIX "Unsupported hid [%s]\n",
			    acpi_device_hid(device));
414 415
		error = -ENODEV;
		goto err_free_input;
L
Linus Torvalds 已提交
416 417
	}

418 419 420 421 422 423 424 425 426 427 428 429 430 431 432
	error = acpi_button_add_fs(device);
	if (error)
		goto err_free_input;

	error = acpi_button_install_notify_handlers(button);
	if (error)
		goto err_remove_fs;

	snprintf(button->phys, sizeof(button->phys),
		 "%s/button/input0", acpi_device_hid(device));

	input->name = acpi_device_name(device);
	input->phys = button->phys;
	input->id.bustype = BUS_HOST;
	input->id.product = button->type;
433

L
Linus Torvalds 已提交
434
	switch (button->type) {
435
	case ACPI_BUTTON_TYPE_POWER:
L
Linus Torvalds 已提交
436
	case ACPI_BUTTON_TYPE_POWERF:
437 438
		input->evbit[0] = BIT(EV_KEY);
		set_bit(KEY_POWER, input->keybit);
L
Linus Torvalds 已提交
439
		break;
440 441

	case ACPI_BUTTON_TYPE_SLEEP:
L
Linus Torvalds 已提交
442
	case ACPI_BUTTON_TYPE_SLEEPF:
443 444
		input->evbit[0] = BIT(EV_KEY);
		set_bit(KEY_SLEEP, input->keybit);
L
Linus Torvalds 已提交
445
		break;
446 447 448 449

	case ACPI_BUTTON_TYPE_LID:
		input->evbit[0] = BIT(EV_SW);
		set_bit(SW_LID, input->swbit);
L
Linus Torvalds 已提交
450 451 452
		break;
	}

453 454 455
	error = input_register_device(input);
	if (error)
		goto err_remove_handlers;
L
Linus Torvalds 已提交
456 457 458

	if (device->wakeup.flags.valid) {
		/* Button's GPE is run-wake GPE */
L
Len Brown 已提交
459 460 461 462 463
		acpi_set_gpe_type(device->wakeup.gpe_device,
				  device->wakeup.gpe_number,
				  ACPI_GPE_TYPE_WAKE_RUN);
		acpi_enable_gpe(device->wakeup.gpe_device,
				device->wakeup.gpe_number, ACPI_NOT_ISR);
L
Linus Torvalds 已提交
464 465 466
		device->wakeup.state.enabled = 1;
	}

L
Len Brown 已提交
467 468
	printk(KERN_INFO PREFIX "%s [%s]\n",
	       acpi_device_name(device), acpi_device_bid(device));
L
Linus Torvalds 已提交
469

470
	return 0;
L
Linus Torvalds 已提交
471

472 473 474 475 476 477 478 479 480
 err_remove_handlers:
	acpi_button_remove_notify_handlers(button);
 err_remove_fs:
	acpi_button_remove_fs(device);
 err_free_input:
	input_free_device(input);
 err_free_button:
	kfree(button);
	return error;
L
Linus Torvalds 已提交
481 482
}

L
Len Brown 已提交
483
static int acpi_button_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
484
{
485
	struct acpi_button *button;
L
Linus Torvalds 已提交
486 487

	if (!device || !acpi_driver_data(device))
488
		return -EINVAL;
L
Linus Torvalds 已提交
489 490 491

	button = acpi_driver_data(device);

492
	acpi_button_remove_notify_handlers(button);
L
Len Brown 已提交
493
	acpi_button_remove_fs(device);
494
	input_unregister_device(button->input);
L
Linus Torvalds 已提交
495 496
	kfree(button);

497
	return 0;
L
Linus Torvalds 已提交
498 499
}

L
Len Brown 已提交
500
static int __init acpi_button_init(void)
L
Linus Torvalds 已提交
501
{
502
	int result;
L
Linus Torvalds 已提交
503

504 505
	acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
	if (!acpi_button_dir)
506
		return -ENODEV;
507
	acpi_button_dir->owner = THIS_MODULE;
L
Linus Torvalds 已提交
508 509
	result = acpi_bus_register_driver(&acpi_button_driver);
	if (result < 0) {
510
		remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
511
		return -ENODEV;
L
Linus Torvalds 已提交
512 513
	}

514
	return 0;
L
Linus Torvalds 已提交
515 516
}

L
Len Brown 已提交
517
static void __exit acpi_button_exit(void)
L
Linus Torvalds 已提交
518 519 520
{
	acpi_bus_unregister_driver(&acpi_button_driver);

L
Len Brown 已提交
521
	if (acpi_power_dir)
522 523 524 525 526 527
		remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER, acpi_button_dir);
	if (acpi_sleep_dir)
		remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP, acpi_button_dir);
	if (acpi_lid_dir)
		remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID, acpi_button_dir);
	remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
L
Linus Torvalds 已提交
528 529 530 531
}

module_init(acpi_button_init);
module_exit(acpi_button_exit);