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