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
34static device_manager_info *sDeviceManager;
35static struct acpi_module_info *sAcpi;
36
37
38typedef 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
49static void
50acpi_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
67static uint32
68acpi_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
82static status_t
83acpi_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
142static void
143acpi_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
157static status_t
158acpi_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
170static status_t
171acpi_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
186static status_t
187acpi_button_write(void* cookie, off_t position, const void* buffer,
188	size_t* num_bytes)
189{
190	return B_ERROR;
191}
192
193
194static status_t
195acpi_button_control(void* _cookie, uint32 op, void* arg, size_t len)
196{
197	return B_ERROR;
198}
199
200
201static status_t
202acpi_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
224static status_t
225acpi_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
236static status_t
237acpi_button_close (void* cookie)
238{
239	return B_OK;
240}
241
242
243static status_t
244acpi_button_free (void* cookie)
245{
246	return B_OK;
247}
248
249
250//	#pragma mark - driver module API
251
252
253static float
254acpi_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
287static status_t
288acpi_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
300static status_t
301acpi_button_init_driver(device_node *node, void **_driverCookie)
302{
303	*_driverCookie = node;
304	return B_OK;
305}
306
307
308static void
309acpi_button_uninit_driver(void *driverCookie)
310{
311}
312
313
314static status_t
315acpi_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
347module_dependency module_dependencies[] = {
348	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
349	{ B_ACPI_MODULE_NAME, (module_info **)&sAcpi },
350	{}
351};
352
353
354driver_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
371struct 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
393module_info *modules[] = {
394	(module_info *)&acpi_button_driver_module,
395	(module_info *)&acpi_button_device_module,
396	NULL
397};
398