xref: /haiku/src/add-ons/kernel/drivers/power/acpi_lid/acpi_lid.cpp (revision 9c274ccd098ee3b2674efde2d1582d4e0c68d878)
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, &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 
71 static void
72 acpi_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 
90 static status_t
91 acpi_lid_init_device(void *driverCookie, void **cookie)
92 {
93 	*cookie = driverCookie;
94 	return B_OK;
95 }
96 
97 
98 static void
99 acpi_lid_uninit_device(void *_cookie)
100 {
101 
102 }
103 
104 
105 static status_t
106 acpi_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 
114 static status_t
115 acpi_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 
135 static status_t
136 acpi_lid_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes)
137 {
138 	return B_ERROR;
139 }
140 
141 
142 static status_t
143 acpi_lid_control(void* _cookie, uint32 op, void* arg, size_t len)
144 {
145 	return B_ERROR;
146 }
147 
148 
149 static status_t
150 acpi_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 
172 static status_t
173 acpi_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 
184 static status_t
185 acpi_lid_close (void* cookie)
186 {
187 	return B_OK;
188 }
189 
190 
191 static status_t
192 acpi_lid_free (void* cookie)
193 {
194 	return B_OK;
195 }
196 
197 
198 //	#pragma mark - driver module API
199 
200 
201 static float
202 acpi_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 
234 static status_t
235 acpi_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 
247 static status_t
248 acpi_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 
280 static void
281 acpi_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 
292 static status_t
293 acpi_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 
312 module_dependency module_dependencies[] = {
313 	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
314 	{}
315 };
316 
317 
318 driver_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 
335 struct 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 
357 module_info *modules[] = {
358 	(module_info *)&acpi_lid_driver_module,
359 	(module_info *)&acpi_lid_device_module,
360 	NULL
361 };
362