1/*
2 * Copyright 2008-2013, J��r��me Duval, korli@users.berlios.de.
3 *
4 * Distributed under the terms of the MIT License.
5 */
6
7
8#include <ACPI.h>
9
10#include <fs/select_sync_pool.h>
11
12#include <stdio.h>
13#include <stdlib.h>
14#include <string.h>
15
16
17#define ACPI_LID_MODULE_NAME "drivers/power/acpi_lid/driver_v1"
18
19#define ACPI_LID_DEVICE_MODULE_NAME "drivers/power/acpi_lid/device_v1"
20
21#define ACPI_NOTIFY_STATUS_CHANGED		0x80
22
23/* Base Namespace devices are published to */
24#define ACPI_LID_BASENAME "power/acpi_lid/%d"
25
26// name of pnp generator of path ids
27#define ACPI_LID_PATHID_GENERATOR "acpi_lid/path_id"
28
29//#define TRACE_LID
30#ifdef TRACE_LID
31#	define TRACE(x...) dprintf("acpi_lid: " x)
32#else
33#	define TRACE(x...)
34#endif
35#define ERROR(x...)	dprintf("acpi_lid: " x)
36
37
38static device_manager_info *sDeviceManager;
39
40
41typedef struct acpi_ns_device_info {
42	device_node *node;
43	acpi_device_module_info *acpi;
44	acpi_device acpi_cookie;
45	uint8 last_status;
46	bool updated;
47	select_sync_pool* select_pool;
48} acpi_lid_device_info;
49
50
51static void
52acpi_lid_read_status(acpi_lid_device_info *device)
53{
54	acpi_data buf;
55	buf.pointer = NULL;
56	buf.length = ACPI_ALLOCATE_BUFFER;
57	if (device->acpi->evaluate_method(device->acpi_cookie, "_LID", NULL,
58			&buf) != B_OK
59		|| buf.pointer == NULL
60		|| ((acpi_object_type*)buf.pointer)->object_type != ACPI_TYPE_INTEGER) {
61		ERROR("couldn't get status\n");
62	} else {
63		acpi_object_type* object = (acpi_object_type*)buf.pointer;
64		device->last_status = object->integer.integer;
65		device->updated = true;
66		free(buf.pointer);
67		TRACE("status %d\n", device->last_status);
68	}
69}
70
71
72static void
73acpi_lid_notify_handler(acpi_handle _device, uint32 value, void *context)
74{
75	acpi_lid_device_info *device = (acpi_lid_device_info *)context;
76	if (value == ACPI_NOTIFY_STATUS_CHANGED) {
77		TRACE("status changed\n");
78		acpi_lid_read_status(device);
79		if (device->select_pool != NULL)
80			notify_select_event_pool(device->select_pool, B_SELECT_READ);
81	} else {
82		ERROR("unknown notification\n");
83	}
84
85}
86
87
88//	#pragma mark - device module API
89
90
91static status_t
92acpi_lid_init_device(void *driverCookie, void **cookie)
93{
94	*cookie = driverCookie;
95	return B_OK;
96}
97
98
99static void
100acpi_lid_uninit_device(void *_cookie)
101{
102
103}
104
105
106static status_t
107acpi_lid_open(void *_cookie, const char *path, int flags, void** cookie)
108{
109	acpi_lid_device_info *device = (acpi_lid_device_info *)_cookie;
110	*cookie = device;
111	return B_OK;
112}
113
114
115static status_t
116acpi_lid_read(void* _cookie, off_t position, void *buf, size_t* num_bytes)
117{
118	acpi_lid_device_info* device = (acpi_lid_device_info*)_cookie;
119	if (*num_bytes < 1)
120		return B_IO_ERROR;
121
122	*((uint8 *)(buf)) = device->last_status;
123	*num_bytes = 1;
124	device->updated = false;
125	return B_OK;
126}
127
128
129static status_t
130acpi_lid_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
131{
132	return B_ERROR;
133}
134
135
136static status_t
137acpi_lid_control(void* _cookie, uint32 op, void* arg, size_t len)
138{
139	return B_ERROR;
140}
141
142
143static status_t
144acpi_lid_select(void *_cookie, uint8 event, selectsync *sync)
145{
146	acpi_lid_device_info* device = (acpi_lid_device_info*)_cookie;
147
148	if (event != B_SELECT_READ)
149		return B_BAD_VALUE;
150
151	// add the event to the pool
152	status_t error = add_select_sync_pool_entry(&device->select_pool, sync,
153		event);
154	if (error != B_OK) {
155		ERROR("add_select_sync_pool_entry() failed: %#lx\n", error);
156		return error;
157	}
158
159	if (device->updated)
160		notify_select_event(sync, event);
161
162	return B_OK;
163}
164
165
166static status_t
167acpi_lid_deselect(void *_cookie, uint8 event, selectsync *sync)
168{
169	acpi_lid_device_info* device = (acpi_lid_device_info*)_cookie;
170
171	if (event != B_SELECT_READ)
172		return B_BAD_VALUE;
173
174	return remove_select_sync_pool_entry(&device->select_pool, sync, event);
175}
176
177
178static status_t
179acpi_lid_close (void* cookie)
180{
181	return B_OK;
182}
183
184
185static status_t
186acpi_lid_free (void* cookie)
187{
188	return B_OK;
189}
190
191
192//	#pragma mark - driver module API
193
194
195static float
196acpi_lid_support(device_node *parent)
197{
198	const char *bus;
199	uint32 device_type;
200	const char *hid;
201
202	// make sure parent is really the ACPI bus manager
203	if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
204		return -1;
205
206	if (strcmp(bus, "acpi"))
207		return 0.0;
208
209	// check whether it's really a device
210	if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
211			&device_type, false) != B_OK
212		|| device_type != ACPI_TYPE_DEVICE) {
213		return 0.0;
214	}
215
216	// check whether it's a lid device
217	if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
218		false) != B_OK || strcmp(hid, "PNP0C0D")) {
219		return 0.0;
220	}
221
222	dprintf("acpi_lid_support lid device found\n");
223
224	return 0.6;
225}
226
227
228static status_t
229acpi_lid_register_device(device_node *node)
230{
231	device_attr attrs[] = {
232		{ B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI Lid" }},
233		{ NULL }
234	};
235
236	return sDeviceManager->register_node(node, ACPI_LID_MODULE_NAME, attrs,
237		NULL, NULL);
238}
239
240
241static status_t
242acpi_lid_init_driver(device_node *node, void **_driverCookie)
243{
244	acpi_lid_device_info *device;
245	device_node *parent;
246	status_t status;
247
248	device = (acpi_lid_device_info *)calloc(1, sizeof(*device));
249	if (device == NULL)
250		return B_NO_MEMORY;
251
252	device->node = node;
253
254	parent = sDeviceManager->get_parent_node(node);
255	sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi,
256		(void **)&device->acpi_cookie);
257	sDeviceManager->put_node(parent);
258
259	status = device->acpi->install_notify_handler(device->acpi_cookie, ACPI_DEVICE_NOTIFY,
260		acpi_lid_notify_handler, device);
261	if (status != B_OK) {
262		ERROR("can't install notify handler\n");
263	}
264
265	device->last_status = 0;
266	device->updated = false;
267	device->select_pool = NULL;
268
269	*_driverCookie = device;
270	return B_OK;
271}
272
273
274static void
275acpi_lid_uninit_driver(void *driverCookie)
276{
277	acpi_lid_device_info *device = (acpi_lid_device_info *)driverCookie;
278
279	device->acpi->remove_notify_handler(device->acpi_cookie, ACPI_DEVICE_NOTIFY,
280		acpi_lid_notify_handler);
281
282	free(device);
283}
284
285
286static status_t
287acpi_lid_register_child_devices(void *_cookie)
288{
289	acpi_lid_device_info *device = (acpi_lid_device_info *)_cookie;
290	int path_id;
291	char name[128];
292
293	path_id = sDeviceManager->create_id(ACPI_LID_PATHID_GENERATOR);
294	if (path_id < 0) {
295		ERROR("register_child_devices: couldn't create a path_id\n");
296		return B_ERROR;
297	}
298
299	snprintf(name, sizeof(name), ACPI_LID_BASENAME, path_id);
300
301	return sDeviceManager->publish_device(device->node, name,
302		ACPI_LID_DEVICE_MODULE_NAME);
303}
304
305
306module_dependency module_dependencies[] = {
307	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
308	{}
309};
310
311
312driver_module_info acpi_lid_driver_module = {
313	{
314		ACPI_LID_MODULE_NAME,
315		0,
316		NULL
317	},
318
319	acpi_lid_support,
320	acpi_lid_register_device,
321	acpi_lid_init_driver,
322	acpi_lid_uninit_driver,
323	acpi_lid_register_child_devices,
324	NULL,	// rescan
325	NULL,	// removed
326};
327
328
329struct device_module_info acpi_lid_device_module = {
330	{
331		ACPI_LID_DEVICE_MODULE_NAME,
332		0,
333		NULL
334	},
335
336	acpi_lid_init_device,
337	acpi_lid_uninit_device,
338	NULL,
339
340	acpi_lid_open,
341	acpi_lid_close,
342	acpi_lid_free,
343	acpi_lid_read,
344	acpi_lid_write,
345	NULL,
346	acpi_lid_control,
347	acpi_lid_select,
348	acpi_lid_deselect
349};
350
351module_info *modules[] = {
352	(module_info *)&acpi_lid_driver_module,
353	(module_info *)&acpi_lid_device_module,
354	NULL
355};
356