xref: /haiku/src/add-ons/kernel/drivers/power/acpi_button/acpi_button.cpp (revision 529cd177b573aaba391c8adc9c9f5ad76a14bf81)
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 *buf, size_t* num_bytes)
172 {
173 	acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
174 	if (*num_bytes < 1)
175 		return B_IO_ERROR;
176 
177 	*((uint8 *)(buf)) = device->last_status;
178 	device->last_status = 0;
179 
180 	*num_bytes = 1;
181 	return B_OK;
182 }
183 
184 
185 static status_t
186 acpi_button_write(void* cookie, off_t position, const void* buffer,
187 	size_t* num_bytes)
188 {
189 	return B_ERROR;
190 }
191 
192 
193 static status_t
194 acpi_button_control(void* _cookie, uint32 op, void* arg, size_t len)
195 {
196 	return B_ERROR;
197 }
198 
199 
200 static status_t
201 acpi_button_select(void *_cookie, uint8 event, selectsync *sync)
202 {
203 	acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
204 
205 	if (event != B_SELECT_READ)
206 		return B_BAD_VALUE;
207 
208 	// add the event to the pool
209 	status_t error = add_select_sync_pool_entry(&device->select_pool, sync,
210 		event);
211 	if (error != B_OK) {
212 		ERROR("add_select_sync_pool_entry() failed: %#lx\n", error);
213 		return error;
214 	}
215 
216 	if (device->last_status != 0)
217 		notify_select_event(sync, event);
218 
219 	return B_OK;
220 }
221 
222 
223 static status_t
224 acpi_button_deselect(void *_cookie, uint8 event, selectsync *sync)
225 {
226 	acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
227 
228 	if (event != B_SELECT_READ)
229 		return B_BAD_VALUE;
230 
231 	return remove_select_sync_pool_entry(&device->select_pool, sync, event);
232 }
233 
234 
235 static status_t
236 acpi_button_close (void* cookie)
237 {
238 	return B_OK;
239 }
240 
241 
242 static status_t
243 acpi_button_free (void* cookie)
244 {
245 	return B_OK;
246 }
247 
248 
249 //	#pragma mark - driver module API
250 
251 
252 static float
253 acpi_button_support(device_node *parent)
254 {
255 	const char *bus;
256 	uint32 device_type;
257 	const char *hid;
258 
259 	// make sure parent is really the ACPI bus manager
260 	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
261 		return -1;
262 
263 	if (strcmp(bus, "acpi"))
264 		return 0.0;
265 
266 	// check whether it's really a device
267 	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
268 		&device_type, false) != B_OK || device_type != ACPI_TYPE_DEVICE) {
269 		return 0.0;
270 	}
271 
272 	// check whether it's a button device
273 	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
274 		false) != B_OK || (strcmp(hid, "PNP0C0C") != 0
275 			&& strcmp(hid, "ACPI_FPB") != 0 && strcmp(hid, "PNP0C0E") != 0
276 			&& strcmp(hid, "ACPI_FSB") != 0)) {
277 		return 0.0;
278 	}
279 
280 	TRACE("acpi_button_support button device found: %s\n", hid);
281 
282 	return 0.6;
283 }
284 
285 
286 static status_t
287 acpi_button_register_device(device_node *node)
288 {
289 	device_attr attrs[] = {
290 		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI Button" }},
291 		{ NULL }
292 	};
293 
294 	return sDeviceManager->register_node(node, ACPI_BUTTON_MODULE_NAME, attrs,
295 		NULL, NULL);
296 }
297 
298 
299 static status_t
300 acpi_button_init_driver(device_node *node, void **_driverCookie)
301 {
302 	*_driverCookie = node;
303 	return B_OK;
304 }
305 
306 
307 static void
308 acpi_button_uninit_driver(void *driverCookie)
309 {
310 }
311 
312 
313 static status_t
314 acpi_button_register_child_devices(void *_cookie)
315 {
316 	device_node *node = (device_node*)_cookie;
317 	device_node *parent = sDeviceManager->get_parent_node(node);
318 	const char *hid;
319 	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
320 		false) != B_OK) {
321 		sDeviceManager->put_node(parent);
322 		return B_ERROR;
323 	}
324 
325 	sDeviceManager->put_node(parent);
326 
327 	status_t status = B_ERROR;
328 	if (strcmp(hid, "PNP0C0C") == 0) {
329 		status = sDeviceManager->publish_device(node,
330 			"power/button/power", ACPI_BUTTON_DEVICE_MODULE_NAME);
331 	} else if (strcmp(hid, "ACPI_FPB") == 0) {
332 		status = sDeviceManager->publish_device(node,
333 			"power/button/power_fixed", ACPI_BUTTON_DEVICE_MODULE_NAME);
334 	} else if (strcmp(hid, "PNP0C0E") == 0) {
335 		status = sDeviceManager->publish_device(node, "power/button/sleep",
336 			ACPI_BUTTON_DEVICE_MODULE_NAME);
337 	} else if ( strcmp(hid, "ACPI_FSB") == 0) {
338 		status = sDeviceManager->publish_device(node,
339 			"power/button/sleep_fixed", ACPI_BUTTON_DEVICE_MODULE_NAME);
340 	}
341 
342 	return status;
343 }
344 
345 
346 module_dependency module_dependencies[] = {
347 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
348 	{ B_ACPI_MODULE_NAME, (module_info **)&sAcpi },
349 	{}
350 };
351 
352 
353 driver_module_info acpi_button_driver_module = {
354 	{
355 		ACPI_BUTTON_MODULE_NAME,
356 		0,
357 		NULL
358 	},
359 
360 	acpi_button_support,
361 	acpi_button_register_device,
362 	acpi_button_init_driver,
363 	acpi_button_uninit_driver,
364 	acpi_button_register_child_devices,
365 	NULL,	// rescan
366 	NULL,	// removed
367 };
368 
369 
370 struct device_module_info acpi_button_device_module = {
371 	{
372 		ACPI_BUTTON_DEVICE_MODULE_NAME,
373 		0,
374 		NULL
375 	},
376 
377 	acpi_button_init_device,
378 	acpi_button_uninit_device,
379 	NULL,
380 
381 	acpi_button_open,
382 	acpi_button_close,
383 	acpi_button_free,
384 	acpi_button_read,
385 	acpi_button_write,
386 	NULL,
387 	acpi_button_control,
388 	acpi_button_select,
389 	acpi_button_deselect
390 };
391 
392 module_info *modules[] = {
393 	(module_info *)&acpi_button_driver_module,
394 	(module_info *)&acpi_button_device_module,
395 	NULL
396 };
397