xref: /linux/drivers/acpi/button.c (revision 20d0021394c1b070bf04b22c5bc8fdb437edd4c5)
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 <acpi/acpi_bus.h>
30 #include <acpi/acpi_drivers.h>
31 
32 
33 #define ACPI_BUTTON_COMPONENT		0x00080000
34 #define ACPI_BUTTON_DRIVER_NAME		"ACPI Button Driver"
35 #define ACPI_BUTTON_CLASS		"button"
36 #define ACPI_BUTTON_NOTIFY_STATUS	0x80
37 
38 #define ACPI_BUTTON_SUBCLASS_POWER	"power"
39 #define ACPI_BUTTON_HID_POWER		"PNP0C0C"
40 #define ACPI_BUTTON_DEVICE_NAME_POWER	"Power Button (CM)"
41 #define ACPI_BUTTON_DEVICE_NAME_POWERF	"Power Button (FF)"
42 #define ACPI_BUTTON_TYPE_POWER		0x01
43 #define ACPI_BUTTON_TYPE_POWERF		0x02
44 
45 #define ACPI_BUTTON_SUBCLASS_SLEEP	"sleep"
46 #define ACPI_BUTTON_HID_SLEEP		"PNP0C0E"
47 #define ACPI_BUTTON_DEVICE_NAME_SLEEP	"Sleep Button (CM)"
48 #define ACPI_BUTTON_DEVICE_NAME_SLEEPF	"Sleep Button (FF)"
49 #define ACPI_BUTTON_TYPE_SLEEP		0x03
50 #define ACPI_BUTTON_TYPE_SLEEPF		0x04
51 
52 #define ACPI_BUTTON_SUBCLASS_LID	"lid"
53 #define ACPI_BUTTON_HID_LID		"PNP0C0D"
54 #define ACPI_BUTTON_DEVICE_NAME_LID	"Lid Switch"
55 #define ACPI_BUTTON_TYPE_LID		0x05
56 
57 #define _COMPONENT		ACPI_BUTTON_COMPONENT
58 ACPI_MODULE_NAME		("acpi_button")
59 
60 MODULE_AUTHOR("Paul Diefenbaugh");
61 MODULE_DESCRIPTION(ACPI_BUTTON_DRIVER_NAME);
62 MODULE_LICENSE("GPL");
63 
64 
65 static int acpi_button_add (struct acpi_device *device);
66 static int acpi_button_remove (struct acpi_device *device, int type);
67 
68 static struct acpi_driver acpi_button_driver = {
69 	.name =		ACPI_BUTTON_DRIVER_NAME,
70 	.class =	ACPI_BUTTON_CLASS,
71 	.ids =		"ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E",
72 	.ops =		{
73 				.add =		acpi_button_add,
74 				.remove =	acpi_button_remove,
75 			},
76 };
77 
78 struct acpi_button {
79 	acpi_handle		handle;
80 	struct acpi_device	*device;	/* Fixed button kludge */
81 	u8			type;
82 	unsigned long		pushed;
83 };
84 
85 /* --------------------------------------------------------------------------
86                                 Driver Interface
87    -------------------------------------------------------------------------- */
88 
89 static void
90 acpi_button_notify (
91 	acpi_handle		handle,
92 	u32			event,
93 	void			*data)
94 {
95 	struct acpi_button	*button = (struct acpi_button *) data;
96 
97 	ACPI_FUNCTION_TRACE("acpi_button_notify");
98 
99 	if (!button || !button->device)
100 		return_VOID;
101 
102 	switch (event) {
103 	case ACPI_BUTTON_NOTIFY_STATUS:
104 		acpi_bus_generate_event(button->device, event, ++button->pushed);
105 		break;
106 	default:
107 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
108 			"Unsupported event [0x%x]\n", event));
109 		break;
110 	}
111 
112 	return_VOID;
113 }
114 
115 
116 static acpi_status
117 acpi_button_notify_fixed (
118 	void			*data)
119 {
120 	struct acpi_button	*button = (struct acpi_button *) data;
121 
122 	ACPI_FUNCTION_TRACE("acpi_button_notify_fixed");
123 
124 	BUG_ON(!button);
125 
126 	acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button);
127 
128 	return_ACPI_STATUS(AE_OK);
129 }
130 
131 
132 static int
133 acpi_button_add (
134 	struct acpi_device	*device)
135 {
136 	int			result = 0;
137 	acpi_status		status = AE_OK;
138 	struct acpi_button	*button = NULL;
139 
140 	ACPI_FUNCTION_TRACE("acpi_button_add");
141 
142 	if (!device)
143 		return_VALUE(-EINVAL);
144 
145 	button = kmalloc(sizeof(struct acpi_button), GFP_KERNEL);
146 	if (!button)
147 		return_VALUE(-ENOMEM);
148 	memset(button, 0, sizeof(struct acpi_button));
149 
150 	button->device = device;
151 	button->handle = device->handle;
152 	acpi_driver_data(device) = button;
153 
154 	/*
155 	 * Determine the button type (via hid), as fixed-feature buttons
156 	 * need to be handled a bit differently than generic-space.
157 	 */
158 	if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) {
159 		button->type = ACPI_BUTTON_TYPE_POWER;
160 		strcpy(acpi_device_name(device),
161 			ACPI_BUTTON_DEVICE_NAME_POWER);
162 		sprintf(acpi_device_class(device), "%s/%s",
163 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
164 	}
165 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) {
166 		button->type = ACPI_BUTTON_TYPE_POWERF;
167 		strcpy(acpi_device_name(device),
168 			ACPI_BUTTON_DEVICE_NAME_POWERF);
169 		sprintf(acpi_device_class(device), "%s/%s",
170 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
171 	}
172 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) {
173 		button->type = ACPI_BUTTON_TYPE_SLEEP;
174 		strcpy(acpi_device_name(device),
175 			ACPI_BUTTON_DEVICE_NAME_SLEEP);
176 		sprintf(acpi_device_class(device), "%s/%s",
177 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
178 	}
179 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) {
180 		button->type = ACPI_BUTTON_TYPE_SLEEPF;
181 		strcpy(acpi_device_name(device),
182 			ACPI_BUTTON_DEVICE_NAME_SLEEPF);
183 		sprintf(acpi_device_class(device), "%s/%s",
184 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
185 	}
186 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) {
187 		button->type = ACPI_BUTTON_TYPE_LID;
188 		strcpy(acpi_device_name(device),
189 			ACPI_BUTTON_DEVICE_NAME_LID);
190 		sprintf(acpi_device_class(device), "%s/%s",
191 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
192 	}
193 	else {
194 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n",
195 			acpi_device_hid(device)));
196 		result = -ENODEV;
197 		goto end;
198 	}
199 
200 	switch (button->type) {
201 	case ACPI_BUTTON_TYPE_POWERF:
202 		status = acpi_install_fixed_event_handler (
203 			ACPI_EVENT_POWER_BUTTON,
204 			acpi_button_notify_fixed,
205 			button);
206 		break;
207 	case ACPI_BUTTON_TYPE_SLEEPF:
208 		status = acpi_install_fixed_event_handler (
209 			ACPI_EVENT_SLEEP_BUTTON,
210 			acpi_button_notify_fixed,
211 			button);
212 		break;
213 	default:
214 		status = acpi_install_notify_handler (
215 			button->handle,
216 			ACPI_DEVICE_NOTIFY,
217 			acpi_button_notify,
218 			button);
219 		break;
220 	}
221 
222 	if (ACPI_FAILURE(status)) {
223 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
224 			"Error installing notify handler\n"));
225 		result = -ENODEV;
226 		goto end;
227 	}
228 
229 	if (device->wakeup.flags.valid) {
230 		/* Button's GPE is run-wake GPE */
231 		acpi_set_gpe_type(device->wakeup.gpe_device,
232 			device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN);
233 		acpi_enable_gpe(device->wakeup.gpe_device,
234 			device->wakeup.gpe_number, ACPI_NOT_ISR);
235 		device->wakeup.state.enabled = 1;
236 	}
237 
238 	printk(KERN_INFO PREFIX "%s [%s]\n",
239 		acpi_device_name(device), acpi_device_bid(device));
240 
241 end:
242 	if (result) {
243 		kfree(button);
244 	}
245 
246 	return_VALUE(result);
247 }
248 
249 
250 static int
251 acpi_button_remove (struct acpi_device *device, int type)
252 {
253 	acpi_status		status = 0;
254 	struct acpi_button	*button = NULL;
255 
256 	ACPI_FUNCTION_TRACE("acpi_button_remove");
257 
258 	if (!device || !acpi_driver_data(device))
259 		return_VALUE(-EINVAL);
260 
261 	button = acpi_driver_data(device);
262 
263 	/* Unregister for device notifications. */
264 	switch (button->type) {
265 	case ACPI_BUTTON_TYPE_POWERF:
266 		status = acpi_remove_fixed_event_handler(
267 			ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed);
268 		break;
269 	case ACPI_BUTTON_TYPE_SLEEPF:
270 		status = acpi_remove_fixed_event_handler(
271 			ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed);
272 		break;
273 	default:
274 		status = acpi_remove_notify_handler(button->handle,
275 			ACPI_DEVICE_NOTIFY, acpi_button_notify);
276 		break;
277 	}
278 
279 	if (ACPI_FAILURE(status))
280 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
281 			"Error removing notify handler\n"));
282 
283 	kfree(button);
284 
285 	return_VALUE(0);
286 }
287 
288 
289 static int __init
290 acpi_button_init (void)
291 {
292 	int			result = 0;
293 
294 	ACPI_FUNCTION_TRACE("acpi_button_init");
295 
296 	result = acpi_bus_register_driver(&acpi_button_driver);
297 	if (result < 0) {
298 		return_VALUE(-ENODEV);
299 	}
300 
301 	return_VALUE(0);
302 }
303 
304 static void __exit
305 acpi_button_exit (void)
306 {
307 	ACPI_FUNCTION_TRACE("acpi_button_exit");
308 
309 	acpi_bus_unregister_driver(&acpi_button_driver);
310 
311 	return_VOID;
312 }
313 
314 module_init(acpi_button_init);
315 module_exit(acpi_button_exit);
316