xref: /reactos/drivers/bus/acpi/busmgr/button.c (revision bbabe248)
1 /*
2  *  acpi_button.c - ACPI Button Driver ($Revision: 29 $)
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 <precomp.h>
27 
28 #define NDEBUG
29 #include <debug.h>
30 
31 #define _COMPONENT		ACPI_BUTTON_COMPONENT
32 ACPI_MODULE_NAME		("acpi_button")
33 
34 
35 static int acpi_button_add (struct acpi_device *device);
36 static int acpi_button_remove (struct acpi_device *device, int type);
37 
38 static struct acpi_driver acpi_button_driver = {
39     {0,0},
40     ACPI_BUTTON_DRIVER_NAME,
41     ACPI_BUTTON_CLASS,
42     0,
43     0,
44     "ACPI_FPB,ACPI_FSB,PNP0C0D,PNP0C0C,PNP0C0E",
45     {acpi_button_add,acpi_button_remove}
46 };
47 
48 struct acpi_button {
49 	ACPI_HANDLE		handle;
50 	struct acpi_device	*device;	/* Fixed button kludge */
51 	UINT8			type;
52 	unsigned long		pushed;
53 };
54 
55 struct acpi_device *power_button;
56 struct acpi_device *sleep_button;
57 struct acpi_device *lid_button;
58 
59 /* --------------------------------------------------------------------------
60                                 Driver Interface
61    -------------------------------------------------------------------------- */
62 
63 void
64 acpi_button_notify (
65 	ACPI_HANDLE		handle,
66 	UINT32			event,
67 	void			*data)
68 {
69 	struct acpi_button	*button = (struct acpi_button *) data;
70 
71 	ACPI_FUNCTION_TRACE("acpi_button_notify");
72 
73 	if (!button || !button->device)
74 		return_VOID;
75 
76 	switch (event) {
77 	case ACPI_BUTTON_NOTIFY_STATUS:
78 		acpi_bus_generate_event(button->device, event, ++button->pushed);
79 		break;
80 	default:
81 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
82 			"Unsupported event [0x%x]\n", event));
83 		break;
84 	}
85 
86 	return_VOID;
87 }
88 
89 
90 ACPI_STATUS
91 acpi_button_notify_fixed (
92 	void			*data)
93 {
94 	struct acpi_button	*button = (struct acpi_button *) data;
95 
96 	ACPI_FUNCTION_TRACE("acpi_button_notify_fixed");
97 
98 	if (!button)
99 		return_ACPI_STATUS(AE_BAD_PARAMETER);
100 
101 	acpi_button_notify(button->handle, ACPI_BUTTON_NOTIFY_STATUS, button);
102 
103 	return_ACPI_STATUS(AE_OK);
104 }
105 
106 
107 static int
108 acpi_button_add (
109 	struct acpi_device	*device)
110 {
111 	int			result = 0;
112 	ACPI_STATUS		status = AE_OK;
113 	struct acpi_button	*button = NULL;
114 
115 	ACPI_FUNCTION_TRACE("acpi_button_add");
116 
117 	if (!device)
118 		return_VALUE(-1);
119 
120 	button = ExAllocatePoolWithTag(NonPagedPool,sizeof(struct acpi_button), 'IPCA');
121 	if (!button)
122 		return_VALUE(-4);
123 	memset(button, 0, sizeof(struct acpi_button));
124 
125 	button->device = device;
126 	button->handle = device->handle;
127 	acpi_driver_data(device) = button;
128 
129 	/*
130 	 * Determine the button type (via hid), as fixed-feature buttons
131 	 * need to be handled a bit differently than generic-space.
132 	 */
133 	if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWER)) {
134 		button->type = ACPI_BUTTON_TYPE_POWER;
135 		sprintf(acpi_device_name(device), "%s",
136 			ACPI_BUTTON_DEVICE_NAME_POWER);
137 		sprintf(acpi_device_class(device), "%s/%s",
138 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
139 	}
140 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_POWERF)) {
141 		button->type = ACPI_BUTTON_TYPE_POWERF;
142 		sprintf(acpi_device_name(device), "%s",
143 			ACPI_BUTTON_DEVICE_NAME_POWERF);
144 		sprintf(acpi_device_class(device), "%s/%s",
145 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_POWER);
146 	}
147 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEP)) {
148 		button->type = ACPI_BUTTON_TYPE_SLEEP;
149 		sprintf(acpi_device_name(device), "%s",
150 			ACPI_BUTTON_DEVICE_NAME_SLEEP);
151 		sprintf(acpi_device_class(device), "%s/%s",
152 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
153 	}
154 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_SLEEPF)) {
155 		button->type = ACPI_BUTTON_TYPE_SLEEPF;
156 		sprintf(acpi_device_name(device), "%s",
157 			ACPI_BUTTON_DEVICE_NAME_SLEEPF);
158 		sprintf(acpi_device_class(device), "%s/%s",
159 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_SLEEP);
160 	}
161 	else if (!strcmp(acpi_device_hid(device), ACPI_BUTTON_HID_LID)) {
162 		button->type = ACPI_BUTTON_TYPE_LID;
163 		sprintf(acpi_device_name(device), "%s",
164 			ACPI_BUTTON_DEVICE_NAME_LID);
165 		sprintf(acpi_device_class(device), "%s/%s",
166 			ACPI_BUTTON_CLASS, ACPI_BUTTON_SUBCLASS_LID);
167 	}
168 	else {
169 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Unsupported hid [%s]\n",
170 			acpi_device_hid(device)));
171 		result = -15;
172 		goto end;
173 	}
174 
175 	/*
176 	 * Ensure only one button of each type is used.
177 	 */
178 	switch (button->type) {
179 	case ACPI_BUTTON_TYPE_POWER:
180 	case ACPI_BUTTON_TYPE_POWERF:
181 		if (!power_button)
182 			power_button = device;
183 		else {
184 			ExFreePoolWithTag(button, 'IPCA');
185 			return_VALUE(-15);
186 		}
187 		break;
188 	case ACPI_BUTTON_TYPE_SLEEP:
189 	case ACPI_BUTTON_TYPE_SLEEPF:
190 		if (!sleep_button)
191 			sleep_button = device;
192 		else {
193 			ExFreePoolWithTag(button, 'IPCA');
194 			return_VALUE(-15);
195 		}
196 		break;
197 	case ACPI_BUTTON_TYPE_LID:
198 		if (!lid_button)
199 			lid_button = device;
200 		else {
201 			ExFreePoolWithTag(button, 'IPCA');
202 			return_VALUE(-15);
203 		}
204 		break;
205 	}
206 
207 	switch (button->type) {
208 	case ACPI_BUTTON_TYPE_POWERF:
209 		status = AcpiInstallFixedEventHandler (
210 			ACPI_EVENT_POWER_BUTTON,
211 			acpi_button_notify_fixed,
212 			button);
213 		break;
214 	case ACPI_BUTTON_TYPE_SLEEPF:
215 		status = AcpiInstallFixedEventHandler (
216 			ACPI_EVENT_SLEEP_BUTTON,
217 			acpi_button_notify_fixed,
218 			button);
219 		break;
220 	case ACPI_BUTTON_TYPE_LID:
221 		status = AcpiInstallFixedEventHandler (
222 			ACPI_BUTTON_TYPE_LID,
223 			acpi_button_notify_fixed,
224 			button);
225 		break;
226 	default:
227 		status = AcpiInstallNotifyHandler (
228 			button->handle,
229 			ACPI_DEVICE_NOTIFY,
230 			acpi_button_notify,
231 			button);
232 		break;
233 	}
234 
235 	if (ACPI_FAILURE(status)) {
236 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
237 			"Error installing notify handler\n"));
238 		result = -15;
239 		goto end;
240 	}
241 
242 	DPRINT("%s [%s]\n",
243 		acpi_device_name(device), acpi_device_bid(device));
244 
245 end:
246 	if (result) {
247 		ExFreePoolWithTag(button, 'IPCA');
248 	}
249 
250 	return_VALUE(result);
251 }
252 
253 
254 static int
255 acpi_button_remove (struct acpi_device *device, int type)
256 {
257 	ACPI_STATUS		status = 0;
258 	struct acpi_button	*button = NULL;
259 
260 	ACPI_FUNCTION_TRACE("acpi_button_remove");
261 
262 	if (!device || !acpi_driver_data(device))
263 		return_VALUE(-1);
264 
265 	button = acpi_driver_data(device);
266 
267 	/* Unregister for device notifications. */
268 	switch (button->type) {
269 	case ACPI_BUTTON_TYPE_POWERF:
270 		status = AcpiRemoveFixedEventHandler(
271 			ACPI_EVENT_POWER_BUTTON, acpi_button_notify_fixed);
272 		break;
273 	case ACPI_BUTTON_TYPE_SLEEPF:
274 		status = AcpiRemoveFixedEventHandler(
275 			ACPI_EVENT_SLEEP_BUTTON, acpi_button_notify_fixed);
276 		break;
277 	case ACPI_BUTTON_TYPE_LID:
278 		status = AcpiRemoveFixedEventHandler(
279 			ACPI_BUTTON_TYPE_LID, acpi_button_notify_fixed);
280 		break;
281 	default:
282 		status = AcpiRemoveNotifyHandler(button->handle,
283 			ACPI_DEVICE_NOTIFY, acpi_button_notify);
284 		break;
285 	}
286 
287 	if (ACPI_FAILURE(status))
288 		ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
289 			"Error removing notify handler\n"));
290 
291 	ExFreePoolWithTag(button, 'IPCA');
292 
293 	return_VALUE(0);
294 }
295 
296 
297 int
298 acpi_button_init (void)
299 {
300 	int			result = 0;
301 
302 	ACPI_FUNCTION_TRACE("acpi_button_init");
303 
304 	result = acpi_bus_register_driver(&acpi_button_driver);
305 	if (result < 0) {
306 		return_VALUE(-15);
307 	}
308 
309 	return_VALUE(0);
310 }
311 
312 
313 void
314 acpi_button_exit (void)
315 {
316 	ACPI_FUNCTION_TRACE("acpi_button_exit");
317 
318 	acpi_bus_unregister_driver(&acpi_button_driver);
319 
320 	return_VOID;
321 }
322 
323 
324