1 /*
2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2005, Nathan Whitehorn.
4 *
5 * Distributed under the terms of the MIT License.
6 *
7 * ACPI Button Driver, used to get info on power buttons, etc.
8 */
9
10
11 #include <ACPI.h>
12
13 #include <fs/select_sync_pool.h>
14
15 #include <stdlib.h>
16 #include <string.h>
17
18
19 #define ACPI_BUTTON_MODULE_NAME "drivers/power/acpi_button/driver_v1"
20
21 #define ACPI_BUTTON_DEVICE_MODULE_NAME "drivers/power/acpi_button/device_v1"
22
23 #define ACPI_NOTIFY_BUTTON_SLEEP 0x80
24 #define ACPI_NOTIFY_BUTTON_WAKEUP 0x2
25
26 //#define TRACE_BUTTON
27 #ifdef TRACE_BUTTON
28 # define TRACE(x...) dprintf("acpi_button: " x)
29 #else
30 # define TRACE(x...)
31 #endif
32 #define ERROR(x...) dprintf("acpi_button: " x)
33
34 static device_manager_info *sDeviceManager;
35 static struct acpi_module_info *sAcpi;
36
37
38 typedef struct acpi_ns_device_info {
39 device_node *node;
40 acpi_device_module_info *acpi;
41 acpi_device acpi_cookie;
42 uint32 type;
43 bool fixed;
44 uint8 last_status;
45 select_sync_pool* select_pool;
46 } acpi_button_device_info;
47
48
49 static void
acpi_button_notify_handler(acpi_handle _device,uint32 value,void * context)50 acpi_button_notify_handler(acpi_handle _device, uint32 value, void *context)
51 {
52 acpi_button_device_info *device = (acpi_button_device_info *)context;
53 if (value == ACPI_NOTIFY_BUTTON_SLEEP) {
54 TRACE("sleep\n");
55 device->last_status = 1;
56 if (device->select_pool != NULL)
57 notify_select_event_pool(device->select_pool, B_SELECT_READ);
58 } else if (value == ACPI_NOTIFY_BUTTON_WAKEUP) {
59 TRACE("wakeup\n");
60 } else {
61 ERROR("unknown notification\n");
62 }
63
64 }
65
66
67 static uint32
acpi_button_fixed_handler(void * context)68 acpi_button_fixed_handler(void *context)
69 {
70 acpi_button_device_info *device = (acpi_button_device_info *)context;
71 TRACE("sleep\n");
72 device->last_status = 1;
73 if (device->select_pool != NULL)
74 notify_select_event_pool(device->select_pool, B_SELECT_READ);
75 return B_OK;
76 }
77
78
79 // #pragma mark - device module API
80
81
82 static status_t
acpi_button_init_device(void * _cookie,void ** cookie)83 acpi_button_init_device(void *_cookie, void **cookie)
84 {
85 device_node *node = (device_node *)_cookie;
86 acpi_button_device_info *device;
87 device_node *parent;
88
89 device = (acpi_button_device_info *)calloc(1, sizeof(*device));
90 if (device == NULL)
91 return B_NO_MEMORY;
92
93 device->node = node;
94
95 parent = sDeviceManager->get_parent_node(node);
96 sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi,
97 (void **)&device->acpi_cookie);
98
99 const char *hid;
100 if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
101 false) != B_OK) {
102 sDeviceManager->put_node(parent);
103 return B_ERROR;
104 }
105
106 sDeviceManager->put_node(parent);
107
108 device->fixed = strcmp(hid, "PNP0C0C") != 0 && strcmp(hid, "PNP0C0E") != 0;
109 TRACE("Device found, hid: %s, fixed: %d\n", hid, device->fixed);
110 if (strcmp(hid, "PNP0C0C") == 0 || strcmp(hid, "ACPI_FPB") == 0)
111 device->type = ACPI_EVENT_POWER_BUTTON;
112 else if (strcmp(hid, "PNP0C0E") == 0 || strcmp(hid, "ACPI_FSB") == 0)
113 device->type = ACPI_EVENT_SLEEP_BUTTON;
114 else
115 return B_ERROR;
116 device->last_status = 0;
117 device->select_pool = NULL;
118
119 if (device->fixed) {
120 sAcpi->reset_fixed_event(device->type);
121 TRACE("Installing fixed handler for type %" B_PRIu32 "\n",
122 device->type);
123 if (sAcpi->install_fixed_event_handler(device->type,
124 acpi_button_fixed_handler, device) != B_OK) {
125 ERROR("can't install fixed handler\n");
126 }
127 } else {
128 TRACE("Installing notify handler for type %" B_PRIu32 "\n",
129 device->type);
130 if (device->acpi->install_notify_handler(device->acpi_cookie,
131 ACPI_DEVICE_NOTIFY, acpi_button_notify_handler, device) != B_OK) {
132 ERROR("can't install notify handler\n");
133 }
134 }
135
136
137 *cookie = device;
138 return B_OK;
139 }
140
141
142 static void
acpi_button_uninit_device(void * _cookie)143 acpi_button_uninit_device(void *_cookie)
144 {
145 acpi_button_device_info *device = (acpi_button_device_info *)_cookie;
146 if (device->fixed) {
147 sAcpi->remove_fixed_event_handler(device->type,
148 acpi_button_fixed_handler);
149 } else {
150 device->acpi->remove_notify_handler(device->acpi_cookie,
151 ACPI_DEVICE_NOTIFY, acpi_button_notify_handler);
152 }
153 free(device);
154 }
155
156
157 static status_t
acpi_button_open(void * _cookie,const char * path,int flags,void ** cookie)158 acpi_button_open(void *_cookie, const char *path, int flags, void** cookie)
159 {
160 acpi_button_device_info *device = (acpi_button_device_info *)_cookie;
161
162 if (device->fixed)
163 sAcpi->enable_fixed_event(device->type);
164
165 *cookie = device;
166 return B_OK;
167 }
168
169
170 static status_t
acpi_button_read(void * _cookie,off_t position,void * buffer,size_t * num_bytes)171 acpi_button_read(void* _cookie, off_t position, void *buffer, size_t* num_bytes)
172 {
173 acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
174 if (*num_bytes < sizeof(uint8))
175 return B_IO_ERROR;
176
177 if (user_memcpy(buffer, &device->last_status, sizeof(uint8)) < B_OK)
178 return B_BAD_ADDRESS;
179 device->last_status = 0;
180
181 *num_bytes = 1;
182 return B_OK;
183 }
184
185
186 static status_t
acpi_button_write(void * cookie,off_t position,const void * buffer,size_t * num_bytes)187 acpi_button_write(void* cookie, off_t position, const void* buffer,
188 size_t* num_bytes)
189 {
190 return B_ERROR;
191 }
192
193
194 static status_t
acpi_button_control(void * _cookie,uint32 op,void * arg,size_t len)195 acpi_button_control(void* _cookie, uint32 op, void* arg, size_t len)
196 {
197 return B_ERROR;
198 }
199
200
201 static status_t
acpi_button_select(void * _cookie,uint8 event,selectsync * sync)202 acpi_button_select(void *_cookie, uint8 event, selectsync *sync)
203 {
204 acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
205
206 if (event != B_SELECT_READ)
207 return B_BAD_VALUE;
208
209 // add the event to the pool
210 status_t error = add_select_sync_pool_entry(&device->select_pool, sync,
211 event);
212 if (error != B_OK) {
213 ERROR("add_select_sync_pool_entry() failed: %" B_PRIx32 "\n", error);
214 return error;
215 }
216
217 if (device->last_status != 0)
218 notify_select_event(sync, event);
219
220 return B_OK;
221 }
222
223
224 static status_t
acpi_button_deselect(void * _cookie,uint8 event,selectsync * sync)225 acpi_button_deselect(void *_cookie, uint8 event, selectsync *sync)
226 {
227 acpi_button_device_info* device = (acpi_button_device_info*)_cookie;
228
229 if (event != B_SELECT_READ)
230 return B_BAD_VALUE;
231
232 return remove_select_sync_pool_entry(&device->select_pool, sync, event);
233 }
234
235
236 static status_t
acpi_button_close(void * cookie)237 acpi_button_close (void* cookie)
238 {
239 return B_OK;
240 }
241
242
243 static status_t
acpi_button_free(void * cookie)244 acpi_button_free (void* cookie)
245 {
246 return B_OK;
247 }
248
249
250 // #pragma mark - driver module API
251
252
253 static float
acpi_button_support(device_node * parent)254 acpi_button_support(device_node *parent)
255 {
256 const char *bus;
257 uint32 device_type;
258 const char *hid;
259
260 // make sure parent is really the ACPI bus manager
261 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false))
262 return -1;
263
264 if (strcmp(bus, "acpi"))
265 return 0.0;
266
267 // check whether it's really a device
268 if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM,
269 &device_type, false) != B_OK || device_type != ACPI_TYPE_DEVICE) {
270 return 0.0;
271 }
272
273 // check whether it's a button device
274 if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
275 false) != B_OK || (strcmp(hid, "PNP0C0C") != 0
276 && strcmp(hid, "ACPI_FPB") != 0 && strcmp(hid, "PNP0C0E") != 0
277 && strcmp(hid, "ACPI_FSB") != 0)) {
278 return 0.0;
279 }
280
281 TRACE("acpi_button_support button device found: %s\n", hid);
282
283 return 0.6;
284 }
285
286
287 static status_t
acpi_button_register_device(device_node * node)288 acpi_button_register_device(device_node *node)
289 {
290 device_attr attrs[] = {
291 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { .string = "ACPI Button" }},
292 { NULL }
293 };
294
295 return sDeviceManager->register_node(node, ACPI_BUTTON_MODULE_NAME, attrs,
296 NULL, NULL);
297 }
298
299
300 static status_t
acpi_button_init_driver(device_node * node,void ** _driverCookie)301 acpi_button_init_driver(device_node *node, void **_driverCookie)
302 {
303 *_driverCookie = node;
304 return B_OK;
305 }
306
307
308 static void
acpi_button_uninit_driver(void * driverCookie)309 acpi_button_uninit_driver(void *driverCookie)
310 {
311 }
312
313
314 static status_t
acpi_button_register_child_devices(void * _cookie)315 acpi_button_register_child_devices(void *_cookie)
316 {
317 device_node *node = (device_node*)_cookie;
318 device_node *parent = sDeviceManager->get_parent_node(node);
319 const char *hid;
320 if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid,
321 false) != B_OK) {
322 sDeviceManager->put_node(parent);
323 return B_ERROR;
324 }
325
326 sDeviceManager->put_node(parent);
327
328 status_t status = B_ERROR;
329 if (strcmp(hid, "PNP0C0C") == 0) {
330 status = sDeviceManager->publish_device(node,
331 "power/button/power", ACPI_BUTTON_DEVICE_MODULE_NAME);
332 } else if (strcmp(hid, "ACPI_FPB") == 0) {
333 status = sDeviceManager->publish_device(node,
334 "power/button/power_fixed", ACPI_BUTTON_DEVICE_MODULE_NAME);
335 } else if (strcmp(hid, "PNP0C0E") == 0) {
336 status = sDeviceManager->publish_device(node, "power/button/sleep",
337 ACPI_BUTTON_DEVICE_MODULE_NAME);
338 } else if ( strcmp(hid, "ACPI_FSB") == 0) {
339 status = sDeviceManager->publish_device(node,
340 "power/button/sleep_fixed", ACPI_BUTTON_DEVICE_MODULE_NAME);
341 }
342
343 return status;
344 }
345
346
347 module_dependency module_dependencies[] = {
348 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
349 { B_ACPI_MODULE_NAME, (module_info **)&sAcpi },
350 {}
351 };
352
353
354 driver_module_info acpi_button_driver_module = {
355 {
356 ACPI_BUTTON_MODULE_NAME,
357 0,
358 NULL
359 },
360
361 acpi_button_support,
362 acpi_button_register_device,
363 acpi_button_init_driver,
364 acpi_button_uninit_driver,
365 acpi_button_register_child_devices,
366 NULL, // rescan
367 NULL, // removed
368 };
369
370
371 struct device_module_info acpi_button_device_module = {
372 {
373 ACPI_BUTTON_DEVICE_MODULE_NAME,
374 0,
375 NULL
376 },
377
378 acpi_button_init_device,
379 acpi_button_uninit_device,
380 NULL,
381
382 acpi_button_open,
383 acpi_button_close,
384 acpi_button_free,
385 acpi_button_read,
386 acpi_button_write,
387 NULL,
388 acpi_button_control,
389 acpi_button_select,
390 acpi_button_deselect
391 };
392
393 module_info *modules[] = {
394 (module_info *)&acpi_button_driver_module,
395 (module_info *)&acpi_button_device_module,
396 NULL
397 };
398