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
acpi_lid_read_status(acpi_lid_device_info * device)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
acpi_lid_notify_handler(acpi_handle _device,uint32 value,void * context)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
acpi_lid_init_device(void * driverCookie,void ** cookie)91 acpi_lid_init_device(void *driverCookie, void **cookie)
92 {
93 *cookie = driverCookie;
94 return B_OK;
95 }
96
97
98 static void
acpi_lid_uninit_device(void * _cookie)99 acpi_lid_uninit_device(void *_cookie)
100 {
101
102 }
103
104
105 static status_t
acpi_lid_open(void * _cookie,const char * path,int flags,void ** cookie)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
acpi_lid_read(void * _cookie,off_t position,void * buf,size_t * num_bytes)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
acpi_lid_write(void * cookie,off_t position,const void * buffer,size_t * num_bytes)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
acpi_lid_control(void * _cookie,uint32 op,void * arg,size_t len)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
acpi_lid_select(void * _cookie,uint8 event,selectsync * sync)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
acpi_lid_deselect(void * _cookie,uint8 event,selectsync * sync)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
acpi_lid_close(void * cookie)185 acpi_lid_close (void* cookie)
186 {
187 return B_OK;
188 }
189
190
191 static status_t
acpi_lid_free(void * cookie)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
acpi_lid_support(device_node * parent)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
acpi_lid_register_device(device_node * node)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
acpi_lid_init_driver(device_node * node,void ** _driverCookie)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
acpi_lid_uninit_driver(void * driverCookie)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
acpi_lid_register_child_devices(void * _cookie)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