xref: /haiku/src/add-ons/kernel/drivers/power/acpi_button/acpi_button.cpp (revision 37fedaf8494b34aad811abcc49e79aa32943f880)
1 /*
2  * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3  * Copyright 2005, Nathan Whitehorn.
4  *
5  * Distributed under the terms of the MIT License.
6  *
7  * ACPI Button Driver, used to get info on power buttons, etc.
8  */
9 
10 
11 #include <ACPI.h>
12 
13 #include <fs/select_sync_pool.h>
14 
15 #include <stdlib.h>
16 #include <string.h>
17 
18 
19 #define ACPI_BUTTON_MODULE_NAME "drivers/power/acpi_button/driver_v1"
20 
21 #define ACPI_BUTTON_DEVICE_MODULE_NAME "drivers/power/acpi_button/device_v1"
22 
23 #define ACPI_NOTIFY_BUTTON_SLEEP		0x80
24 #define ACPI_NOTIFY_BUTTON_WAKEUP		0x2
25 
26 //#define TRACE_BUTTON
27 #ifdef TRACE_BUTTON
28 #	define TRACE(x...) dprintf("acpi_button: "x)
29 #else
30 #	define TRACE(x...)
31 #endif
32 #define ERROR(x...)	dprintf("acpi_button: "x)
33 
34 static device_manager_info *sDeviceManager;
35 static struct acpi_module_info *sAcpi;
36 
37 
38 typedef struct acpi_ns_device_info {
39 	device_node *node;
40 	acpi_device_module_info *acpi;
41 	acpi_device acpi_cookie;
42 	uint32 type;
43 	bool fixed;
44 	uint8 last_status;
45 	select_sync_pool* select_pool;
46 } acpi_button_device_info;
47 
48 
49 static void
50 acpi_button_notify_handler(acpi_handle _device, uint32 value, void *context)
51 {
52 	acpi_button_device_info *device = (acpi_button_device_info *)context;
53 	if (value == ACPI_NOTIFY_BUTTON_SLEEP) {
54 		TRACE("sleep\n");
55 		device->last_status = 1;
56 		if (device->select_pool != NULL)
57 			notify_select_event_pool(device->select_pool, B_SELECT_READ);
58 	} else if (value == ACPI_NOTIFY_BUTTON_WAKEUP) {
59 		TRACE("wakeup\n");
60 	} else {
61 		ERROR("unknown notification\n");
62 	}
63 
64 }
65 
66 
67 static uint32
68 acpi_button_fixed_handler(void *context)
69 {
70 	acpi_button_device_info *device = (acpi_button_device_info *)context;
71 	TRACE("sleep\n");
72 	device->last_status = 1;
73 	if (device->select_pool != NULL)
74 		notify_select_event_pool(device->select_pool, B_SELECT_READ);
75 	return B_OK;
76 }
77 
78 
79 //	#pragma mark - device module API
80 
81 
82 static status_t
83 acpi_button_init_device(void *_cookie, void **cookie)
84 {
85 	device_node *node = (device_node *)_cookie;
86 	acpi_button_device_info *device;
87 	device_node *parent;
88 
89 	device = (acpi_button_device_info *)calloc(1, sizeof(*device));
90 	if (device == NULL)
91 		return B_NO_MEMORY;
92 
93 	device->node = node;
94 
95 	parent = sDeviceManager->get_parent_node(node);
96 	sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi,
97 		(void **)&device->acpi_cookie);
98 
99 	const char *hid;
100 	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
101 		false) != B_OK) {
102 		sDeviceManager->put_node(parent);
103 		return B_ERROR;
104 	}
105 
106 	sDeviceManager->put_node(parent);
107 
108 	device->fixed = strcmp(hid, "PNP0C0C") != 0 && strcmp(hid, "PNP0C0E") != 0;
109 	if (strcmp(hid, "PNP0C0C") == 0 || strcmp(hid, "ACPI_FPB") == 0)
110 		device->type = ACPI_EVENT_POWER_BUTTON;
111 	else if (strcmp(hid, "PNP0C0E") == 0 || strcmp(hid, "ACPI_FSB") == 0)
112 		device->type = ACPI_EVENT_SLEEP_BUTTON;
113 	else
114 		return B_ERROR;
115 	device->last_status = 0;
116 	device->select_pool = NULL;
117 
118 	if (device->fixed) {
119 		sAcpi->reset_fixed_event(device->type);
120 		if (sAcpi->install_fixed_event_handler(device->type,
121 			acpi_button_fixed_handler, device) != B_OK) {
122 			ERROR("can't install notify handler\n");
123 		}
124 	} else {
125 		if (device->acpi->install_notify_handler(device->acpi_cookie,
126 			ACPI_DEVICE_NOTIFY, acpi_button_notify_handler, device) != B_OK) {
127 			ERROR("can't install notify handler\n");
128 		}
129 	}
130 
131 
132 	*cookie = device;
133 	return B_OK;
134 }
135 
136 
137 static void
138 acpi_button_uninit_device(void *_cookie)
139 {
140 	acpi_button_device_info *device = (acpi_button_device_info *)_cookie;
141 	if (device->fixed) {
142 		sAcpi->remove_fixed_event_handler(device->type,
143 			acpi_button_fixed_handler);
144 	} else {
145 		device->acpi->remove_notify_handler(device->acpi_cookie,
146 			ACPI_DEVICE_NOTIFY, acpi_button_notify_handler);
147 	}
148 	free(device);
149 }
150 
151 
152 static status_t
153 acpi_button_open(void *_cookie, const char *path, int flags, void** cookie)
154 {
155 	acpi_button_device_info *device = (acpi_button_device_info *)_cookie;
156 
157 	sAcpi->enable_fixed_event(device->type);
158 
159 	*cookie = device;
160 	return B_OK;
161 }
162 
163 
164 static status_t
165 acpi_button_read(void* _cookie, off_t position, void *buf, size_t* num_bytes)
166 {
167 	acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
168 	if (*num_bytes < 1)
169 		return B_IO_ERROR;
170 
171 	*((uint8 *)(buf)) = device->last_status;
172 	device->last_status = 0;
173 
174 	*num_bytes = 1;
175 	return B_OK;
176 }
177 
178 
179 static status_t
180 acpi_button_write(void* cookie, off_t position, const void* buffer,
181 	size_t* num_bytes)
182 {
183 	return B_ERROR;
184 }
185 
186 
187 static status_t
188 acpi_button_control(void* _cookie, uint32 op, void* arg, size_t len)
189 {
190 	return B_ERROR;
191 }
192 
193 
194 static status_t
195 acpi_button_select(void *_cookie, uint8 event, selectsync *sync)
196 {
197 	acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
198 
199 	if (event != B_SELECT_READ)
200 		return B_BAD_VALUE;
201 
202 	// add the event to the pool
203 	status_t error = add_select_sync_pool_entry(&device->select_pool, sync,
204 		event);
205 	if (error != B_OK) {
206 		ERROR("add_select_sync_pool_entry() failed: %#lx\n", error);
207 		return error;
208 	}
209 
210 	if (device->last_status != 0)
211 		notify_select_event(sync, event);
212 
213 	return B_OK;
214 }
215 
216 
217 static status_t
218 acpi_button_deselect(void *_cookie, uint8 event, selectsync *sync)
219 {
220 	acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
221 
222 	if (event != B_SELECT_READ)
223 		return B_BAD_VALUE;
224 
225 	return remove_select_sync_pool_entry(&device->select_pool, sync, event);
226 }
227 
228 
229 static status_t
230 acpi_button_close (void* cookie)
231 {
232 	return B_OK;
233 }
234 
235 
236 static status_t
237 acpi_button_free (void* cookie)
238 {
239 	return B_OK;
240 }
241 
242 
243 //	#pragma mark - driver module API
244 
245 
246 static float
247 acpi_button_support(device_node *parent)
248 {
249 	const char *bus;
250 	uint32 device_type;
251 	const char *hid;
252 
253 	// make sure parent is really the ACPI bus manager
254 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
255 		return -1;
256 
257 	if (strcmp(bus, "acpi"))
258 		return 0.0;
259 
260 	// check whether it's really a device
261 	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
262 		&device_type, false) != B_OK || device_type != ACPI_TYPE_DEVICE) {
263 		return 0.0;
264 	}
265 
266 	// check whether it's a button device
267 	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
268 		false) != B_OK || (strcmp(hid, "PNP0C0C") != 0
269 			&& strcmp(hid, "ACPI_FPB") != 0 && strcmp(hid, "PNP0C0E") != 0
270 			&& strcmp(hid, "ACPI_FSB") != 0)) {
271 		return 0.0;
272 	}
273 
274 	TRACE("acpi_button_support button device found\n");
275 
276 	return 0.6;
277 }
278 
279 
280 static status_t
281 acpi_button_register_device(device_node *node)
282 {
283 	device_attr attrs[] = {
284 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI Button" }},
285 		{ NULL }
286 	};
287 
288 	return sDeviceManager->register_node(node, ACPI_BUTTON_MODULE_NAME, attrs,
289 		NULL, NULL);
290 }
291 
292 
293 static status_t
294 acpi_button_init_driver(device_node *node, void **_driverCookie)
295 {
296 	*_driverCookie = node;
297 	return B_OK;
298 }
299 
300 
301 static void
302 acpi_button_uninit_driver(void *driverCookie)
303 {
304 }
305 
306 
307 static status_t
308 acpi_button_register_child_devices(void *_cookie)
309 {
310 	device_node *node = (device_node*)_cookie;
311 	device_node *parent = sDeviceManager->get_parent_node(node);
312 	const char *hid;
313 	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
314 		false) != B_OK) {
315 		sDeviceManager->put_node(parent);
316 		return B_ERROR;
317 	}
318 
319 	sDeviceManager->put_node(parent);
320 
321 	status_t status = B_ERROR;
322 	if (strcmp(hid, "PNP0C0C") == 0 || strcmp(hid, "ACPI_FPB") == 0) {
323 		status = sDeviceManager->publish_device(node,
324 			"power/button/power", ACPI_BUTTON_DEVICE_MODULE_NAME);
325 	} else if (strcmp(hid, "PNP0C0E") == 0 || strcmp(hid, "ACPI_FSB") == 0) {
326 		status = sDeviceManager->publish_device(node, "power/button/sleep",
327 			ACPI_BUTTON_DEVICE_MODULE_NAME);
328 	}
329 
330 	return status;
331 }
332 
333 
334 module_dependency module_dependencies[] = {
335 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
336 	{ B_ACPI_MODULE_NAME, (module_info **)&sAcpi },
337 	{}
338 };
339 
340 
341 driver_module_info acpi_button_driver_module = {
342 	{
343 		ACPI_BUTTON_MODULE_NAME,
344 		0,
345 		NULL
346 	},
347 
348 	acpi_button_support,
349 	acpi_button_register_device,
350 	acpi_button_init_driver,
351 	acpi_button_uninit_driver,
352 	acpi_button_register_child_devices,
353 	NULL,	// rescan
354 	NULL,	// removed
355 };
356 
357 
358 struct device_module_info acpi_button_device_module = {
359 	{
360 		ACPI_BUTTON_DEVICE_MODULE_NAME,
361 		0,
362 		NULL
363 	},
364 
365 	acpi_button_init_device,
366 	acpi_button_uninit_device,
367 	NULL,
368 
369 	acpi_button_open,
370 	acpi_button_close,
371 	acpi_button_free,
372 	acpi_button_read,
373 	acpi_button_write,
374 	NULL,
375 	acpi_button_control,
376 	acpi_button_select,
377 	acpi_button_deselect
378 };
379 
380 module_info *modules[] = {
381 	(module_info *)&acpi_button_driver_module,
382 	(module_info *)&acpi_button_device_module,
383 	NULL
384 };
385