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 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 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 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 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 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 171 acpi_button_read(void* _cookie, off_t position, void *buf, size_t* num_bytes) 172 { 173 acpi_button_device_info* device = (acpi_button_device_info*)_cookie; 174 if (*num_bytes < 1) 175 return B_IO_ERROR; 176 177 *((uint8 *)(buf)) = device->last_status; 178 device->last_status = 0; 179 180 *num_bytes = 1; 181 return B_OK; 182 } 183 184 185 static status_t 186 acpi_button_write(void* cookie, off_t position, const void* buffer, 187 size_t* num_bytes) 188 { 189 return B_ERROR; 190 } 191 192 193 static status_t 194 acpi_button_control(void* _cookie, uint32 op, void* arg, size_t len) 195 { 196 return B_ERROR; 197 } 198 199 200 static status_t 201 acpi_button_select(void *_cookie, uint8 event, selectsync *sync) 202 { 203 acpi_button_device_info* device = (acpi_button_device_info*)_cookie; 204 205 if (event != B_SELECT_READ) 206 return B_BAD_VALUE; 207 208 // add the event to the pool 209 status_t error = add_select_sync_pool_entry(&device->select_pool, sync, 210 event); 211 if (error != B_OK) { 212 ERROR("add_select_sync_pool_entry() failed: %#lx\n", error); 213 return error; 214 } 215 216 if (device->last_status != 0) 217 notify_select_event(sync, event); 218 219 return B_OK; 220 } 221 222 223 static status_t 224 acpi_button_deselect(void *_cookie, uint8 event, selectsync *sync) 225 { 226 acpi_button_device_info* device = (acpi_button_device_info*)_cookie; 227 228 if (event != B_SELECT_READ) 229 return B_BAD_VALUE; 230 231 return remove_select_sync_pool_entry(&device->select_pool, sync, event); 232 } 233 234 235 static status_t 236 acpi_button_close (void* cookie) 237 { 238 return B_OK; 239 } 240 241 242 static status_t 243 acpi_button_free (void* cookie) 244 { 245 return B_OK; 246 } 247 248 249 // #pragma mark - driver module API 250 251 252 static float 253 acpi_button_support(device_node *parent) 254 { 255 const char *bus; 256 uint32 device_type; 257 const char *hid; 258 259 // make sure parent is really the ACPI bus manager 260 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 261 return -1; 262 263 if (strcmp(bus, "acpi")) 264 return 0.0; 265 266 // check whether it's really a device 267 if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, 268 &device_type, false) != B_OK || device_type != ACPI_TYPE_DEVICE) { 269 return 0.0; 270 } 271 272 // check whether it's a button device 273 if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, 274 false) != B_OK || (strcmp(hid, "PNP0C0C") != 0 275 && strcmp(hid, "ACPI_FPB") != 0 && strcmp(hid, "PNP0C0E") != 0 276 && strcmp(hid, "ACPI_FSB") != 0)) { 277 return 0.0; 278 } 279 280 TRACE("acpi_button_support button device found: %s\n", hid); 281 282 return 0.6; 283 } 284 285 286 static status_t 287 acpi_button_register_device(device_node *node) 288 { 289 device_attr attrs[] = { 290 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI Button" }}, 291 { NULL } 292 }; 293 294 return sDeviceManager->register_node(node, ACPI_BUTTON_MODULE_NAME, attrs, 295 NULL, NULL); 296 } 297 298 299 static status_t 300 acpi_button_init_driver(device_node *node, void **_driverCookie) 301 { 302 *_driverCookie = node; 303 return B_OK; 304 } 305 306 307 static void 308 acpi_button_uninit_driver(void *driverCookie) 309 { 310 } 311 312 313 static status_t 314 acpi_button_register_child_devices(void *_cookie) 315 { 316 device_node *node = (device_node*)_cookie; 317 device_node *parent = sDeviceManager->get_parent_node(node); 318 const char *hid; 319 if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, 320 false) != B_OK) { 321 sDeviceManager->put_node(parent); 322 return B_ERROR; 323 } 324 325 sDeviceManager->put_node(parent); 326 327 status_t status = B_ERROR; 328 if (strcmp(hid, "PNP0C0C") == 0) { 329 status = sDeviceManager->publish_device(node, 330 "power/button/power", ACPI_BUTTON_DEVICE_MODULE_NAME); 331 } else if (strcmp(hid, "ACPI_FPB") == 0) { 332 status = sDeviceManager->publish_device(node, 333 "power/button/power_fixed", ACPI_BUTTON_DEVICE_MODULE_NAME); 334 } else if (strcmp(hid, "PNP0C0E") == 0) { 335 status = sDeviceManager->publish_device(node, "power/button/sleep", 336 ACPI_BUTTON_DEVICE_MODULE_NAME); 337 } else if ( strcmp(hid, "ACPI_FSB") == 0) { 338 status = sDeviceManager->publish_device(node, 339 "power/button/sleep_fixed", ACPI_BUTTON_DEVICE_MODULE_NAME); 340 } 341 342 return status; 343 } 344 345 346 module_dependency module_dependencies[] = { 347 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, 348 { B_ACPI_MODULE_NAME, (module_info **)&sAcpi }, 349 {} 350 }; 351 352 353 driver_module_info acpi_button_driver_module = { 354 { 355 ACPI_BUTTON_MODULE_NAME, 356 0, 357 NULL 358 }, 359 360 acpi_button_support, 361 acpi_button_register_device, 362 acpi_button_init_driver, 363 acpi_button_uninit_driver, 364 acpi_button_register_child_devices, 365 NULL, // rescan 366 NULL, // removed 367 }; 368 369 370 struct device_module_info acpi_button_device_module = { 371 { 372 ACPI_BUTTON_DEVICE_MODULE_NAME, 373 0, 374 NULL 375 }, 376 377 acpi_button_init_device, 378 acpi_button_uninit_device, 379 NULL, 380 381 acpi_button_open, 382 acpi_button_close, 383 acpi_button_free, 384 acpi_button_read, 385 acpi_button_write, 386 NULL, 387 acpi_button_control, 388 acpi_button_select, 389 acpi_button_deselect 390 }; 391 392 module_info *modules[] = { 393 (module_info *)&acpi_button_driver_module, 394 (module_info *)&acpi_button_device_module, 395 NULL 396 }; 397