xref: /openbmc/linux/drivers/acpi/button.c (revision 1da177e4)
1 /*
2  *  acpi_button.c - ACPI Button Driver ($Revision: 30 $)
3  *
4  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
6  *
7  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or (at
12  *  your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful, but
15  *  WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License along
20  *  with this program; if not, write to the Free Software Foundation, Inc.,
21  *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22  *
23  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24  */
25 
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/types.h>
30 #include <linux/proc_fs.h>
31 #include <linux/seq_file.h>
32 #include <acpi/acpi_bus.h>
33 #include <acpi/acpi_drivers.h>
34 
35 
36 #define ACPI_BUTTON_COMPONENT		0x00080000
37 #define ACPI_BUTTON_DRIVER_NAME		"ACPI Button Driver"
38 #define ACPI_BUTTON_CLASS		"button"
39 #define ACPI_BUTTON_FILE_INFO		"info"
40 #define ACPI_BUTTON_FILE_STATE		"state"
41 #define ACPI_BUTTON_TYPE_UNKNOWN	0x00
42 #define ACPI_BUTTON_NOTIFY_STATUS	0x80
43 
44 #define ACPI_BUTTON_SUBCLASS_POWER	"power"
45 #define ACPI_BUTTON_HID_POWER		"PNP0C0C"
46 #define ACPI_BUTTON_DEVICE_NAME_POWER	"Power Button (CM)"
47 #define ACPI_BUTTON_DEVICE_NAME_POWERF	"Power Button (FF)"
48 #define ACPI_BUTTON_TYPE_POWER		0x01
49 #define ACPI_BUTTON_TYPE_POWERF		0x02
50 
51 #define ACPI_BUTTON_SUBCLASS_SLEEP	"sleep"
52 #define ACPI_BUTTON_HID_SLEEP		"PNP0C0E"
53 #define ACPI_BUTTON_DEVICE_NAME_SLEEP	"Sleep Button (CM)"
54 #define ACPI_BUTTON_DEVICE_NAME_SLEEPF	"Sleep Button (FF)"
55 #define ACPI_BUTTON_TYPE_SLEEP		0x03
56 #define ACPI_BUTTON_TYPE_SLEEPF		0x04
57 
58 #define ACPI_BUTTON_SUBCLASS_LID	"lid"
59 #define ACPI_BUTTON_HID_LID		"PNP0C0D"
60 #define ACPI_BUTTON_DEVICE_NAME_LID	"Lid Switch"
61 #define ACPI_BUTTON_TYPE_LID		0x05
62 
63 #define _COMPONENT		ACPI_BUTTON_COMPONENT
64 ACPI_MODULE_NAME		("acpi_button")
65 
66 MODULE_AUTHOR("Paul Diefenbaugh");
67 MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME);
68 MODULE_LICENSE("GPL");
69 
70 
71 static int acpi_button_add (struct acpi_device *device);
72 static int acpi_button_remove (struct acpi_device *device, int type);
73 static int acpi_button_info_open_fs(struct inode *inode, struct file *file);
74 static int acpi_button_state_open_fs(struct inode *inode, struct file *file);
75 
76 static struct acpi_driver acpi_button_driver = {
77 	.name =		ACPI_BUTTON_DRIVER_NAME,
78 	.class =	ACPI_BUTTON_CLASS,
79 	.ids =		"ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E",
80 	.ops =		{
81 				.add =		acpi_button_add,
82 				.remove =	acpi_button_remove,
83 			},
84 };
85 
86 struct acpi_button {
87 	acpi_handle		handle;
88 	struct acpi_device	*device;	/* Fixed button kludge */
89 	u8			type;
90 	unsigned long		pushed;
91 };
92 
93 static struct file_operations acpi_button_info_fops = {
94 	.open		= acpi_button_info_open_fs,
95 	.read		= seq_read,
96 	.llseek		= seq_lseek,
97 	.release	= single_release,
98 };
99 
100 static struct file_operations acpi_button_state_fops = {
101 	.open		= acpi_button_state_open_fs,
102 	.read		= seq_read,
103 	.llseek		= seq_lseek,
104 	.release	= single_release,
105 };
106 /* --------------------------------------------------------------------------
107                               FS Interface (/proc)
108    -------------------------------------------------------------------------- */
109 
110 static struct proc_dir_entry	*acpi_button_dir;
111 
112 static int acpi_button_info_seq_show(struct seq_file *seq, void *offset)
113 {
114 	struct acpi_button	*button = (struct acpi_button *) seq->private;
115 
116 	ACPI_FUNCTION_TRACE("acpi_button_info_seq_show");
117 
118 	if (!button || !button->device)
119 		return_VALUE(0);
120 
121 	seq_printf(seq, "type:                    %s\n",
122 		acpi_device_name(button->device));
123 
124 	return_VALUE(0);
125 }
126 
127 static int acpi_button_info_open_fs(struct inode *inode, struct file *file)
128 {
129 	return single_open(file, acpi_button_info_seq_show, PDE(inode)->data);
130 }
131 
132 static int acpi_button_state_seq_show(struct seq_file *seq, void *offset)
133 {
134 	struct acpi_button	*button = (struct acpi_button *) seq->private;
135 	acpi_status		status;
136 	unsigned long		state;
137 
138 	ACPI_FUNCTION_TRACE("acpi_button_state_seq_show");
139 
140 	if (!button || !button->device)
141 		return_VALUE(0);
142 
143 	status = acpi_evaluate_integer(button->handle,"_LID",NULL,&state);
144 	if (ACPI_FAILURE(status)) {
145 		seq_printf(seq, "state:      unsupported\n");
146 	}
147 	else{
148 		seq_printf(seq, "state:      %s\n", (state ? "open" : "closed"));
149 	}
150 
151 	return_VALUE(0);
152 }
153 
154 static int acpi_button_state_open_fs(struct inode *inode, struct file *file)
155 {
156 	return single_open(file, acpi_button_state_seq_show, PDE(inode)->data);
157 }
158 
159 static int
160 acpi_button_add_fs (
161 	struct acpi_device	*device)
162 {
163 	struct proc_dir_entry	*entry = NULL;
164 	struct acpi_button	*button = NULL;
165 
166 	ACPI_FUNCTION_TRACE("acpi_button_add_fs");
167 
168 	if (!device || !acpi_driver_data(device))
169 		return_VALUE(-EINVAL);
170 
171 	button = acpi_driver_data(device);
172 
173 	switch (button->type) {
174 	case ACPI_BUTTON_TYPE_POWER:
175 	case ACPI_BUTTON_TYPE_POWERF:
176 			entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_POWER,
177 				acpi_button_dir);
178 		break;
179 	case ACPI_BUTTON_TYPE_SLEEP:
180 	case ACPI_BUTTON_TYPE_SLEEPF:
181 			entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_SLEEP,
182 				acpi_button_dir);
183 		break;
184 	case ACPI_BUTTON_TYPE_LID:
185 			entry = proc_mkdir(ACPI_BUTTON_SUBCLASS_LID,
186 				acpi_button_dir);
187 		break;
188 	}
189 
190 	if (!entry)
191 		return_VALUE(-ENODEV);
192 	entry->owner = THIS_MODULE;
193 
194 	acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), entry);
195 	if (!acpi_device_dir(device))
196 		return_VALUE(-ENODEV);
197 	acpi_device_dir(device)->owner = THIS_MODULE;
198 
199 	/* 'info' [R] */
200 	entry = create_proc_entry(ACPI_BUTTON_FILE_INFO,
201 		S_IRUGO, acpi_device_dir(device));
202 	if (!entry)
203 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
204 			"Unable to create '%s' fs entry\n",
205 			ACPI_BUTTON_FILE_INFO));
206 	else {
207 		entry->proc_fops = &acpi_button_info_fops;
208 		entry->data = acpi_driver_data(device);
209 		entry->owner = THIS_MODULE;
210 	}
211 
212 	/* show lid state [R] */
213 	if (button->type == ACPI_BUTTON_TYPE_LID) {
214 		entry = create_proc_entry(ACPI_BUTTON_FILE_STATE,
215 			S_IRUGO, acpi_device_dir(device));
216 		if (!entry)
217 			ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
218 				"Unable to create '%s' fs entry\n",
219 				ACPI_BUTTON_FILE_INFO));
220 		else {
221 			entry->proc_fops = &acpi_button_state_fops;
222 			entry->data = acpi_driver_data(device);
223 			entry->owner = THIS_MODULE;
224 		}
225 	}
226 
227 	return_VALUE(0);
228 }
229 
230 
231 static int
232 acpi_button_remove_fs (
233 	struct acpi_device	*device)
234 {
235 	struct acpi_button	*button = NULL;
236 
237 	ACPI_FUNCTION_TRACE("acpi_button_remove_fs");
238 
239 	button = acpi_driver_data(device);
240 	if (acpi_device_dir(device)) {
241 		if (button->type == ACPI_BUTTON_TYPE_LID)
242 			remove_proc_entry(ACPI_BUTTON_FILE_STATE,
243 					     acpi_device_dir(device));
244 		remove_proc_entry(ACPI_BUTTON_FILE_INFO,
245 				     acpi_device_dir(device));
246 
247 		remove_proc_entry(acpi_device_bid(device),
248 				     acpi_device_dir(device)->parent);
249 
250 
251 		switch (button->type) {
252 			case ACPI_BUTTON_TYPE_POWER:
253 			case ACPI_BUTTON_TYPE_POWERF:
254 				remove_proc_entry(ACPI_BUTTON_SUBCLASS_POWER,
255 					acpi_button_dir);
256 				break;
257 			case ACPI_BUTTON_TYPE_SLEEP:
258 			case ACPI_BUTTON_TYPE_SLEEPF:
259 				remove_proc_entry(ACPI_BUTTON_SUBCLASS_SLEEP,
260 					acpi_button_dir);
261 				break;
262 			case ACPI_BUTTON_TYPE_LID:
263 				remove_proc_entry(ACPI_BUTTON_SUBCLASS_LID,
264 					acpi_button_dir);
265 				break;
266 		}
267 		acpi_device_dir(device) = NULL;
268 	}
269 
270 	return_VALUE(0);
271 }
272 
273 
274 /* --------------------------------------------------------------------------
275                                 Driver Interface
276    -------------------------------------------------------------------------- */
277 
278 static void
279 acpi_button_notify (
280 	acpi_handle		handle,
281 	u32			event,
282 	void			*data)
283 {
284 	struct acpi_button	*button = (struct acpi_button *) data;
285 
286 	ACPI_FUNCTION_TRACE("acpi_button_notify");
287 
288 	if (!button || !button->device)
289 		return_VOID;
290 
291 	switch (event) {
292 	case ACPI_BUTTON_NOTIFY_STATUS:
293 		acpi_bus_generate_event(button->device, event, ++button->pushed);
294 		break;
295 	default:
296 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
297 			"Unsupported event [0x%x]\n", event));
298 		break;
299 	}
300 
301 	return_VOID;
302 }
303 
304 
305 static acpi_status
306 acpi_button_notify_fixed (
307 	void			*data)
308 {
309 	struct acpi_button	*button = (struct acpi_button *) data;
310 
311 	ACPI_FUNCTION_TRACE("acpi_button_notify_fixed");
312 
313 	if (!button)
314 		return_ACPI_STATUS(AE_BAD_PARAMETER);
315 
316 	acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button);
317 
318 	return_ACPI_STATUS(AE_OK);
319 }
320 
321 
322 static int
323 acpi_button_add (
324 	struct acpi_device	*device)
325 {
326 	int			result = 0;
327 	acpi_status		status = AE_OK;
328 	struct acpi_button	*button = NULL;
329 
330 	static struct acpi_device *power_button;
331 	static struct acpi_device *sleep_button;
332 	static struct acpi_device *lid_button;
333 
334 	ACPI_FUNCTION_TRACE("acpi_button_add");
335 
336 	if (!device)
337 		return_VALUE(-EINVAL);
338 
339 	button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL);
340 	if (!button)
341 		return_VALUE(-ENOMEM);
342 	memset(button, 0, sizeof(struct acpi_button));
343 
344 	button->device = device;
345 	button->handle = device->handle;
346 	acpi_driver_data(device) = button;
347 
348 	/*
349 	 * Determine the button type (via hid), as fixed-feature buttons
350 	 * need to be handled a bit differently than generic-space.
351 	 */
352 	if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) {
353 		button->type = ACPI_BUTTON_TYPE_POWER;
354 		strcpy(acpi_device_name(device),
355 			ACPI_BUTTON_DEVICE_NAME_POWER);
356 		sprintf(acpi_device_class(device), "%s/%s",
357 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
358 	}
359 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) {
360 		button->type = ACPI_BUTTON_TYPE_POWERF;
361 		strcpy(acpi_device_name(device),
362 			ACPI_BUTTON_DEVICE_NAME_POWERF);
363 		sprintf(acpi_device_class(device), "%s/%s",
364 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
365 	}
366 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) {
367 		button->type = ACPI_BUTTON_TYPE_SLEEP;
368 		strcpy(acpi_device_name(device),
369 			ACPI_BUTTON_DEVICE_NAME_SLEEP);
370 		sprintf(acpi_device_class(device), "%s/%s",
371 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
372 	}
373 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) {
374 		button->type = ACPI_BUTTON_TYPE_SLEEPF;
375 		strcpy(acpi_device_name(device),
376 			ACPI_BUTTON_DEVICE_NAME_SLEEPF);
377 		sprintf(acpi_device_class(device), "%s/%s",
378 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
379 	}
380 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) {
381 		button->type = ACPI_BUTTON_TYPE_LID;
382 		strcpy(acpi_device_name(device),
383 			ACPI_BUTTON_DEVICE_NAME_LID);
384 		sprintf(acpi_device_class(device), "%s/%s",
385 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
386 	}
387 	else {
388 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n",
389 			acpi_device_hid(device)));
390 		result = -ENODEV;
391 		goto end;
392 	}
393 
394 	/*
395 	 * Ensure only one button of each type is used.
396 	 */
397 	switch (button->type) {
398 	case ACPI_BUTTON_TYPE_POWER:
399 	case ACPI_BUTTON_TYPE_POWERF:
400 		if (!power_button)
401 			power_button = device;
402 		else {
403 			kfree(button);
404 			return_VALUE(-ENODEV);
405 		}
406 		break;
407 	case ACPI_BUTTON_TYPE_SLEEP:
408 	case ACPI_BUTTON_TYPE_SLEEPF:
409 		if (!sleep_button)
410 			sleep_button = device;
411 		else {
412 			kfree(button);
413 			return_VALUE(-ENODEV);
414 		}
415 		break;
416 	case ACPI_BUTTON_TYPE_LID:
417 		if (!lid_button)
418 			lid_button = device;
419 		else {
420 			kfree(button);
421 			return_VALUE(-ENODEV);
422 		}
423 		break;
424 	}
425 
426 	result = acpi_button_add_fs(device);
427 	if (result)
428 		goto end;
429 
430 	switch (button->type) {
431 	case ACPI_BUTTON_TYPE_POWERF:
432 		status = acpi_install_fixed_event_handler (
433 			ACPI_EVENT_POWER_BUTTON,
434 			acpi_button_notify_fixed,
435 			button);
436 		break;
437 	case ACPI_BUTTON_TYPE_SLEEPF:
438 		status = acpi_install_fixed_event_handler (
439 			ACPI_EVENT_SLEEP_BUTTON,
440 			acpi_button_notify_fixed,
441 			button);
442 		break;
443 	default:
444 		status = acpi_install_notify_handler (
445 			button->handle,
446 			ACPI_DEVICE_NOTIFY,
447 			acpi_button_notify,
448 			button);
449 		break;
450 	}
451 
452 	if (ACPI_FAILURE(status)) {
453 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
454 			"Error installing notify handler\n"));
455 		result = -ENODEV;
456 		goto end;
457 	}
458 
459 	if (device->wakeup.flags.valid) {
460 		/* Button's GPE is run-wake GPE */
461 		acpi_set_gpe_type(device->wakeup.gpe_device,
462 			device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN);
463 		acpi_enable_gpe(device->wakeup.gpe_device,
464 			device->wakeup.gpe_number, ACPI_NOT_ISR);
465 		device->wakeup.state.enabled = 1;
466 	}
467 
468 	printk(KERN_INFO PREFIX "%s [%s]\n",
469 		acpi_device_name(device), acpi_device_bid(device));
470 
471 end:
472 	if (result) {
473 		acpi_button_remove_fs(device);
474 		kfree(button);
475 	}
476 
477 	return_VALUE(result);
478 }
479 
480 
481 static int
482 acpi_button_remove (struct acpi_device *device, int type)
483 {
484 	acpi_status		status = 0;
485 	struct acpi_button	*button = NULL;
486 
487 	ACPI_FUNCTION_TRACE("acpi_button_remove");
488 
489 	if (!device || !acpi_driver_data(device))
490 		return_VALUE(-EINVAL);
491 
492 	button = acpi_driver_data(device);
493 
494 	/* Unregister for device notifications. */
495 	switch (button->type) {
496 	case ACPI_BUTTON_TYPE_POWERF:
497 		status = acpi_remove_fixed_event_handler(
498 			ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed);
499 		break;
500 	case ACPI_BUTTON_TYPE_SLEEPF:
501 		status = acpi_remove_fixed_event_handler(
502 			ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed);
503 		break;
504 	default:
505 		status = acpi_remove_notify_handler(button->handle,
506 			ACPI_DEVICE_NOTIFY, acpi_button_notify);
507 		break;
508 	}
509 
510 	if (ACPI_FAILURE(status))
511 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
512 			"Error removing notify handler\n"));
513 
514 	acpi_button_remove_fs(device);
515 
516 	kfree(button);
517 
518 	return_VALUE(0);
519 }
520 
521 
522 static int __init
523 acpi_button_init (void)
524 {
525 	int			result = 0;
526 
527 	ACPI_FUNCTION_TRACE("acpi_button_init");
528 
529 	acpi_button_dir = proc_mkdir(ACPI_BUTTON_CLASS, acpi_root_dir);
530 	if (!acpi_button_dir)
531 		return_VALUE(-ENODEV);
532 	acpi_button_dir->owner = THIS_MODULE;
533 
534 	result = acpi_bus_register_driver(&acpi_button_driver);
535 	if (result < 0) {
536 		remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
537 		return_VALUE(-ENODEV);
538 	}
539 
540 	return_VALUE(0);
541 }
542 
543 
544 static void __exit
545 acpi_button_exit (void)
546 {
547 	ACPI_FUNCTION_TRACE("acpi_button_exit");
548 
549 	acpi_bus_unregister_driver(&acpi_button_driver);
550 
551 	remove_proc_entry(ACPI_BUTTON_CLASS, acpi_root_dir);
552 
553 	return_VOID;
554 }
555 
556 
557 module_init(acpi_button_init);
558 module_exit(acpi_button_exit);
559