xref: /haiku/src/add-ons/kernel/drivers/power/acpi_button/acpi_button.cpp (revision 52c4471a3024d2eb81fe88e2c3982b9f8daa5e56)
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 	TRACE("Device found, hid: %s, fixed: %d\n", hid, device->fixed);
110 	if (strcmp(hid, "PNP0C0C") == 0 || strcmp(hid, "ACPI_FPB") == 0)
111 		device->type = ACPI_EVENT_POWER_BUTTON;
112 	else if (strcmp(hid, "PNP0C0E") == 0 || strcmp(hid, "ACPI_FSB") == 0)
113 		device->type = ACPI_EVENT_SLEEP_BUTTON;
114 	else
115 		return B_ERROR;
116 	device->last_status = 0;
117 	device->select_pool = NULL;
118 
119 	if (device->fixed) {
120 		sAcpi->reset_fixed_event(device->type);
121 		TRACE("Installing fixed handler for type %" B_PRIu32 "\n",
122 			device->type);
123 		if (sAcpi->install_fixed_event_handler(device->type,
124 			acpi_button_fixed_handler, device) != B_OK) {
125 			ERROR("can't install fixed handler\n");
126 		}
127 	} else {
128 		TRACE("Installing notify handler for type %" B_PRIu32 "\n",
129 			device->type);
130 		if (device->acpi->install_notify_handler(device->acpi_cookie,
131 			ACPI_DEVICE_NOTIFY, acpi_button_notify_handler, device) != B_OK) {
132 			ERROR("can't install notify handler\n");
133 		}
134 	}
135 
136 
137 	*cookie = device;
138 	return B_OK;
139 }
140 
141 
142 static void
143 acpi_button_uninit_device(void *_cookie)
144 {
145 	acpi_button_device_info *device = (acpi_button_device_info *)_cookie;
146 	if (device->fixed) {
147 		sAcpi->remove_fixed_event_handler(device->type,
148 			acpi_button_fixed_handler);
149 	} else {
150 		device->acpi->remove_notify_handler(device->acpi_cookie,
151 			ACPI_DEVICE_NOTIFY, acpi_button_notify_handler);
152 	}
153 	free(device);
154 }
155 
156 
157 static status_t
158 acpi_button_open(void *_cookie, const char *path, int flags, void** cookie)
159 {
160 	acpi_button_device_info *device = (acpi_button_device_info *)_cookie;
161 
162 	if (device->fixed)
163 		sAcpi->enable_fixed_event(device->type);
164 
165 	*cookie = device;
166 	return B_OK;
167 }
168 
169 
170 static status_t
171 acpi_button_read(void* _cookie, off_t position, void *buffer, size_t* num_bytes)
172 {
173 	acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
174 	if (*num_bytes < sizeof(uint8))
175 		return B_IO_ERROR;
176 
177 	if (user_memcpy(buffer, &device->last_status, sizeof(uint8)) < B_OK)
178 		return B_BAD_ADDRESS;
179 	device->last_status = 0;
180 
181 	*num_bytes = 1;
182 	return B_OK;
183 }
184 
185 
186 static status_t
187 acpi_button_write(void* cookie, off_t position, const void* buffer,
188 	size_t* num_bytes)
189 {
190 	return B_ERROR;
191 }
192 
193 
194 static status_t
195 acpi_button_control(void* _cookie, uint32 op, void* arg, size_t len)
196 {
197 	return B_ERROR;
198 }
199 
200 
201 static status_t
202 acpi_button_select(void *_cookie, uint8 event, selectsync *sync)
203 {
204 	acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
205 
206 	if (event != B_SELECT_READ)
207 		return B_BAD_VALUE;
208 
209 	// add the event to the pool
210 	status_t error = add_select_sync_pool_entry(&device->select_pool, sync,
211 		event);
212 	if (error != B_OK) {
213 		ERROR("add_select_sync_pool_entry() failed: %" B_PRIx32 "\n", error);
214 		return error;
215 	}
216 
217 	if (device->last_status != 0)
218 		notify_select_event(sync, event);
219 
220 	return B_OK;
221 }
222 
223 
224 static status_t
225 acpi_button_deselect(void *_cookie, uint8 event, selectsync *sync)
226 {
227 	acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
228 
229 	if (event != B_SELECT_READ)
230 		return B_BAD_VALUE;
231 
232 	return remove_select_sync_pool_entry(&device->select_pool, sync, event);
233 }
234 
235 
236 static status_t
237 acpi_button_close (void* cookie)
238 {
239 	return B_OK;
240 }
241 
242 
243 static status_t
244 acpi_button_free (void* cookie)
245 {
246 	return B_OK;
247 }
248 
249 
250 //	#pragma mark - driver module API
251 
252 
253 static float
254 acpi_button_support(device_node *parent)
255 {
256 	const char *bus;
257 	uint32 device_type;
258 	const char *hid;
259 
260 	// make sure parent is really the ACPI bus manager
261 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
262 		return -1;
263 
264 	if (strcmp(bus, "acpi"))
265 		return 0.0;
266 
267 	// check whether it's really a device
268 	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
269 		&device_type, false) != B_OK || device_type != ACPI_TYPE_DEVICE) {
270 		return 0.0;
271 	}
272 
273 	// check whether it's a button device
274 	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
275 		false) != B_OK || (strcmp(hid, "PNP0C0C") != 0
276 			&& strcmp(hid, "ACPI_FPB") != 0 && strcmp(hid, "PNP0C0E") != 0
277 			&& strcmp(hid, "ACPI_FSB") != 0)) {
278 		return 0.0;
279 	}
280 
281 	TRACE("acpi_button_support button device found: %s\n", hid);
282 
283 	return 0.6;
284 }
285 
286 
287 static status_t
288 acpi_button_register_device(device_node *node)
289 {
290 	device_attr attrs[] = {
291 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI Button" }},
292 		{ NULL }
293 	};
294 
295 	return sDeviceManager->register_node(node, ACPI_BUTTON_MODULE_NAME, attrs,
296 		NULL, NULL);
297 }
298 
299 
300 static status_t
301 acpi_button_init_driver(device_node *node, void **_driverCookie)
302 {
303 	*_driverCookie = node;
304 	return B_OK;
305 }
306 
307 
308 static void
309 acpi_button_uninit_driver(void *driverCookie)
310 {
311 }
312 
313 
314 static status_t
315 acpi_button_register_child_devices(void *_cookie)
316 {
317 	device_node *node = (device_node*)_cookie;
318 	device_node *parent = sDeviceManager->get_parent_node(node);
319 	const char *hid;
320 	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
321 		false) != B_OK) {
322 		sDeviceManager->put_node(parent);
323 		return B_ERROR;
324 	}
325 
326 	sDeviceManager->put_node(parent);
327 
328 	status_t status = B_ERROR;
329 	if (strcmp(hid, "PNP0C0C") == 0) {
330 		status = sDeviceManager->publish_device(node,
331 			"power/button/power", ACPI_BUTTON_DEVICE_MODULE_NAME);
332 	} else if (strcmp(hid, "ACPI_FPB") == 0) {
333 		status = sDeviceManager->publish_device(node,
334 			"power/button/power_fixed", ACPI_BUTTON_DEVICE_MODULE_NAME);
335 	} else if (strcmp(hid, "PNP0C0E") == 0) {
336 		status = sDeviceManager->publish_device(node, "power/button/sleep",
337 			ACPI_BUTTON_DEVICE_MODULE_NAME);
338 	} else if ( strcmp(hid, "ACPI_FSB") == 0) {
339 		status = sDeviceManager->publish_device(node,
340 			"power/button/sleep_fixed", ACPI_BUTTON_DEVICE_MODULE_NAME);
341 	}
342 
343 	return status;
344 }
345 
346 
347 module_dependency module_dependencies[] = {
348 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
349 	{ B_ACPI_MODULE_NAME, (module_info **)&sAcpi },
350 	{}
351 };
352 
353 
354 driver_module_info acpi_button_driver_module = {
355 	{
356 		ACPI_BUTTON_MODULE_NAME,
357 		0,
358 		NULL
359 	},
360 
361 	acpi_button_support,
362 	acpi_button_register_device,
363 	acpi_button_init_driver,
364 	acpi_button_uninit_driver,
365 	acpi_button_register_child_devices,
366 	NULL,	// rescan
367 	NULL,	// removed
368 };
369 
370 
371 struct device_module_info acpi_button_device_module = {
372 	{
373 		ACPI_BUTTON_DEVICE_MODULE_NAME,
374 		0,
375 		NULL
376 	},
377 
378 	acpi_button_init_device,
379 	acpi_button_uninit_device,
380 	NULL,
381 
382 	acpi_button_open,
383 	acpi_button_close,
384 	acpi_button_free,
385 	acpi_button_read,
386 	acpi_button_write,
387 	NULL,
388 	acpi_button_control,
389 	acpi_button_select,
390 	acpi_button_deselect
391 };
392 
393 module_info *modules[] = {
394 	(module_info *)&acpi_button_driver_module,
395 	(module_info *)&acpi_button_device_module,
396 	NULL
397 };
398