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 *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 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 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 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 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 237 acpi_button_close (void* cookie) 238 { 239 return B_OK; 240 } 241 242 243 static status_t 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 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 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 301 acpi_button_init_driver(device_node *node, void **_driverCookie) 302 { 303 *_driverCookie = node; 304 return B_OK; 305 } 306 307 308 static void 309 acpi_button_uninit_driver(void *driverCookie) 310 { 311 } 312 313 314 static status_t 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