button.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
/*
 *  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 38
#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

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

#define ACPI_BUTTON_SUBCLASS_POWER	"power"
L
Len Brown 已提交
45
#define ACPI_BUTTON_HID_POWER		"PNP0C0C"
L
Linus Torvalds 已提交
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
#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
64
ACPI_MODULE_NAME("button");
L
Linus Torvalds 已提交
65

66
MODULE_AUTHOR("Paul Diefenbaugh");
L
Linus Torvalds 已提交
67 68 69
MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME);
MODULE_LICENSE("GPL");

L
Len Brown 已提交
70 71
static int acpi_button_add(struct acpi_device *device);
static int acpi_button_remove(struct acpi_device *device, int type);
72 73
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 已提交
74 75

static struct acpi_driver acpi_button_driver = {
L
Len Brown 已提交
76 77
	.name = ACPI_BUTTON_DRIVER_NAME,
	.class = ACPI_BUTTON_CLASS,
78
	.ids = "button_power,button_sleep,PNP0C0D,PNP0C0C,PNP0C0E",
L
Len Brown 已提交
79 80 81
	.ops = {
		.add = acpi_button_add,
		.remove = acpi_button_remove,
82
	},
L
Linus Torvalds 已提交
83 84 85
};

struct acpi_button {
L
Len Brown 已提交
86
	struct acpi_device *device;	/* Fixed button kludge */
87 88 89
	unsigned int type;
	struct input_dev *input;
	char phys[32];			/* for input device */
L
Len Brown 已提交
90
	unsigned long pushed;
L
Linus Torvalds 已提交
91 92
};

93
static const struct file_operations acpi_button_info_fops = {
L
Len Brown 已提交
94 95 96 97
	.open = acpi_button_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
98 99
};

100
static const struct file_operations acpi_button_state_fops = {
L
Len Brown 已提交
101 102 103 104
	.open = acpi_button_state_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
105
};
L
Len Brown 已提交
106

107 108 109 110
/* --------------------------------------------------------------------------
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
111
static struct proc_dir_entry *acpi_button_dir;
112 113 114

static int acpi_button_info_seq_show(struct seq_file *seq, void *offset)
{
115
	struct acpi_button *button = seq->private;
116 117

	if (!button || !button->device)
118
		return 0;
119

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

123
	return 0;
124 125 126 127 128 129
}

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 已提交
130

131 132
static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
{
133
	struct acpi_button *button = seq->private;
L
Len Brown 已提交
134 135
	acpi_status status;
	unsigned long state;
136 137

	if (!button || !button->device)
138
		return 0;
139

140
	status = acpi_evaluate_integer(button->device->handle, "_LID", NULL, &state);
141 142 143
	seq_printf(seq, "state:      %s\n",
		   ACPI_FAILURE(status) ? "unsupported" :
			(state ? "open" : "closed"));
144
	return 0;
145 146 147 148 149 150 151 152 153 154 155
}

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 已提交
156
static int acpi_button_add_fs(struct acpi_device *device)
157
{
L
Len Brown 已提交
158
	struct proc_dir_entry *entry = NULL;
159
	struct acpi_button *button;
160 161

	if (!device || !acpi_driver_data(device))
162
		return -EINVAL;
163 164 165 166 167 168 169

	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 已提交
170 171
			acpi_power_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER,
						    acpi_button_dir);
172 173 174 175 176
		entry = acpi_power_dir;
		break;
	case ACPI_BUTTON_TYPE_SLEEP:
	case ACPI_BUTTON_TYPE_SLEEPF:
		if (!acpi_sleep_dir)
L
Len Brown 已提交
177 178
			acpi_sleep_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP,
						    acpi_button_dir);
179 180 181 182
		entry = acpi_sleep_dir;
		break;
	case ACPI_BUTTON_TYPE_LID:
		if (!acpi_lid_dir)
L
Len Brown 已提交
183 184
			acpi_lid_dir = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID,
						  acpi_button_dir);
185 186 187 188 189
		entry = acpi_lid_dir;
		break;
	}

	if (!entry)
190
		return -ENODEV;
191 192 193 194
	entry->owner = THIS_MODULE;

	acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry);
	if (!acpi_device_dir(device))
195
		return -ENODEV;
196 197 198 199
	acpi_device_dir(device)->owner = THIS_MODULE;

	/* 'info' [R] */
	entry = create_proc_entry(ACPI_BUTTON_FILE_INFO,
L
Len Brown 已提交
200
				  S_IRUGO, acpi_device_dir(device));
201
	if (!entry)
202
		return -ENODEV;
203 204 205 206 207 208 209 210 211
	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 已提交
212
					  S_IRUGO, acpi_device_dir(device));
213
		if (!entry)
214
			return -ENODEV;
215 216 217 218 219 220 221
		else {
			entry->proc_fops = &acpi_button_state_fops;
			entry->data = acpi_driver_data(device);
			entry->owner = THIS_MODULE;
		}
	}

222
	return 0;
223 224
}

L
Len Brown 已提交
225
static int acpi_button_remove_fs(struct acpi_device *device)
226
{
227
	struct acpi_button *button = acpi_driver_data(device);
228 229 230 231

	if (acpi_device_dir(device)) {
		if (button->type == ACPI_BUTTON_TYPE_LID)
			remove_proc_entry(ACPI_BUTTON_FILE_STATE,
L
Len Brown 已提交
232
					  acpi_device_dir(device));
233
		remove_proc_entry(ACPI_BUTTON_FILE_INFO,
L
Len Brown 已提交
234
				  acpi_device_dir(device));
235 236

		remove_proc_entry(acpi_device_bid(device),
L
Len Brown 已提交
237
				  acpi_device_dir(device)->parent);
238 239 240
		acpi_device_dir(device) = NULL;
	}

241
	return 0;
242 243
}

L
Linus Torvalds 已提交
244 245 246 247
/* --------------------------------------------------------------------------
                                Driver Interface
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
248
static void acpi_button_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
249
{
250 251
	struct acpi_button *button = data;
	struct input_dev *input;
L
Linus Torvalds 已提交
252 253

	if (!button || !button->device)
254
		return;
L
Linus Torvalds 已提交
255 256 257

	switch (event) {
	case ACPI_BUTTON_NOTIFY_STATUS:
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
		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 已提交
278 279
		acpi_bus_generate_event(button->device, event,
					++button->pushed);
L
Linus Torvalds 已提交
280 281 282
		break;
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
283
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
284 285 286
		break;
	}

287
	return;
L
Linus Torvalds 已提交
288 289
}

L
Len Brown 已提交
290
static acpi_status acpi_button_notify_fixed(void *data)
L
Linus Torvalds 已提交
291
{
292
	struct acpi_button *button = data;
L
Linus Torvalds 已提交
293

294
	if (!button)
295
		return AE_BAD_PARAMETER;
L
Linus Torvalds 已提交
296

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

299
	return AE_OK;
L
Linus Torvalds 已提交
300 301
}

302
static int acpi_button_install_notify_handlers(struct acpi_button *button)
L
Linus Torvalds 已提交
303
{
304
	acpi_status status;
L
Linus Torvalds 已提交
305

306 307 308 309 310 311 312 313 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
	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 已提交
354 355

	if (!device)
356
		return -EINVAL;
L
Linus Torvalds 已提交
357

358
	button = kzalloc(sizeof(struct acpi_button), GFP_KERNEL);
L
Linus Torvalds 已提交
359
	if (!button)
360
		return -ENOMEM;
L
Linus Torvalds 已提交
361 362 363 364

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

365 366 367 368 369 370
	button->input = input = input_allocate_device();
	if (!input) {
		error = -ENOMEM;
		goto err_free_button;
	}

L
Linus Torvalds 已提交
371 372 373 374 375 376
	/*
	 * 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 已提交
377 378
		strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_POWER);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
379
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
L
Len Brown 已提交
380
	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) {
L
Linus Torvalds 已提交
381 382
		button->type = ACPI_BUTTON_TYPE_POWERF;
		strcpy(acpi_device_name(device),
L
Len Brown 已提交
383 384
		       ACPI_BUTTON_DEVICE_NAME_POWERF);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
385
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
L
Len Brown 已提交
386
	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) {
L
Linus Torvalds 已提交
387
		button->type = ACPI_BUTTON_TYPE_SLEEP;
L
Len Brown 已提交
388 389
		strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_SLEEP);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
390
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
L
Len Brown 已提交
391
	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) {
L
Linus Torvalds 已提交
392 393
		button->type = ACPI_BUTTON_TYPE_SLEEPF;
		strcpy(acpi_device_name(device),
L
Len Brown 已提交
394 395
		       ACPI_BUTTON_DEVICE_NAME_SLEEPF);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
396
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
L
Len Brown 已提交
397
	} else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) {
L
Linus Torvalds 已提交
398
		button->type = ACPI_BUTTON_TYPE_LID;
L
Len Brown 已提交
399 400
		strcpy(acpi_device_name(device), ACPI_BUTTON_DEVICE_NAME_LID);
		sprintf(acpi_device_class(device), "%s/%s",
L
Linus Torvalds 已提交
401
			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
L
Len Brown 已提交
402
	} else {
403 404
		printk(KERN_ERR PREFIX "Unsupported hid [%s]\n",
			    acpi_device_hid(device));
405 406
		error = -ENODEV;
		goto err_free_input;
L
Linus Torvalds 已提交
407 408
	}

409 410 411 412 413 414 415 416 417 418 419 420 421 422 423
	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;
424

L
Linus Torvalds 已提交
425
	switch (button->type) {
426
	case ACPI_BUTTON_TYPE_POWER:
L
Linus Torvalds 已提交
427
	case ACPI_BUTTON_TYPE_POWERF:
428 429
		input->evbit[0] = BIT(EV_KEY);
		set_bit(KEY_POWER, input->keybit);
L
Linus Torvalds 已提交
430
		break;
431 432

	case ACPI_BUTTON_TYPE_SLEEP:
L
Linus Torvalds 已提交
433
	case ACPI_BUTTON_TYPE_SLEEPF:
434 435
		input->evbit[0] = BIT(EV_KEY);
		set_bit(KEY_SLEEP, input->keybit);
L
Linus Torvalds 已提交
436
		break;
437 438 439 440

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

444 445 446
	error = input_register_device(input);
	if (error)
		goto err_remove_handlers;
L
Linus Torvalds 已提交
447 448 449

	if (device->wakeup.flags.valid) {
		/* Button's GPE is run-wake GPE */
L
Len Brown 已提交
450 451 452 453 454
		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 已提交
455 456 457
		device->wakeup.state.enabled = 1;
	}

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

461
	return 0;
L
Linus Torvalds 已提交
462

463 464 465 466 467 468 469 470 471
 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 已提交
472 473
}

L
Len Brown 已提交
474
static int acpi_button_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
475
{
476
	struct acpi_button *button;
L
Linus Torvalds 已提交
477 478

	if (!device || !acpi_driver_data(device))
479
		return -EINVAL;
L
Linus Torvalds 已提交
480 481 482

	button = acpi_driver_data(device);

483
	acpi_button_remove_notify_handlers(button);
L
Len Brown 已提交
484
	acpi_button_remove_fs(device);
485
	input_unregister_device(button->input);
L
Linus Torvalds 已提交
486 487
	kfree(button);

488
	return 0;
L
Linus Torvalds 已提交
489 490
}

L
Len Brown 已提交
491
static int __init acpi_button_init(void)
L
Linus Torvalds 已提交
492
{
493
	int result;
L
Linus Torvalds 已提交
494

495 496
	acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
	if (!acpi_button_dir)
497
		return -ENODEV;
498
	acpi_button_dir->owner = THIS_MODULE;
L
Linus Torvalds 已提交
499 500
	result = acpi_bus_register_driver(&acpi_button_driver);
	if (result < 0) {
501
		remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
502
		return -ENODEV;
L
Linus Torvalds 已提交
503 504
	}

505
	return 0;
L
Linus Torvalds 已提交
506 507
}

L
Len Brown 已提交
508
static void __exit acpi_button_exit(void)
L
Linus Torvalds 已提交
509 510 511
{
	acpi_bus_unregister_driver(&acpi_button_driver);

L
Len Brown 已提交
512
	if (acpi_power_dir)
513 514 515 516 517 518
		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 已提交
519 520 521 522
}

module_init(acpi_button_init);
module_exit(acpi_button_exit);