xref: /haiku/src/add-ons/kernel/drivers/power/acpi_lid/acpi_lid.cpp (revision 02354704729d38c3b078c696adc1bbbd33cbcf72)
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 
38 static device_manager_info *sDeviceManager;
39 
40 
41 typedef 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 
51 static void
52 acpi_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 
72 static void
73 acpi_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 
91 static status_t
92 acpi_lid_init_device(void *driverCookie, void **cookie)
93 {
94 	*cookie = driverCookie;
95 	return B_OK;
96 }
97 
98 
99 static void
100 acpi_lid_uninit_device(void *_cookie)
101 {
102 
103 }
104 
105 
106 static status_t
107 acpi_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 
115 static status_t
116 acpi_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 
129 static status_t
130 acpi_lid_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
131 {
132 	return B_ERROR;
133 }
134 
135 
136 static status_t
137 acpi_lid_control(void* _cookie, uint32 op, void* arg, size_t len)
138 {
139 	return B_ERROR;
140 }
141 
142 
143 static status_t
144 acpi_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 
166 static status_t
167 acpi_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 
178 static status_t
179 acpi_lid_close (void* cookie)
180 {
181 	return B_OK;
182 }
183 
184 
185 static status_t
186 acpi_lid_free (void* cookie)
187 {
188 	return B_OK;
189 }
190 
191 
192 //	#pragma mark - driver module API
193 
194 
195 static float
196 acpi_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 
228 static status_t
229 acpi_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 
241 static status_t
242 acpi_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 
274 static void
275 acpi_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 
286 static status_t
287 acpi_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 
306 module_dependency module_dependencies[] = {
307 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
308 	{}
309 };
310 
311 
312 driver_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 
329 struct 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 
351 module_info *modules[] = {
352 	(module_info *)&acpi_lid_driver_module,
353 	(module_info *)&acpi_lid_device_module,
354 	NULL
355 };
356