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 if (strcmp(hid, "PNP0C0C") == 0 || strcmp(hid, "ACPI_FPB") == 0) 110 device->type = ACPI_EVENT_POWER_BUTTON; 111 else if (strcmp(hid, "PNP0C0E") == 0 || strcmp(hid, "ACPI_FSB") == 0) 112 device->type = ACPI_EVENT_SLEEP_BUTTON; 113 else 114 return B_ERROR; 115 device->last_status = 0; 116 device->select_pool = NULL; 117 118 if (device->fixed) { 119 sAcpi->reset_fixed_event(device->type); 120 if (sAcpi->install_fixed_event_handler(device->type, 121 acpi_button_fixed_handler, device) != B_OK) { 122 ERROR("can't install notify handler\n"); 123 } 124 } else { 125 if (device->acpi->install_notify_handler(device->acpi_cookie, 126 ACPI_DEVICE_NOTIFY, acpi_button_notify_handler, device) != B_OK) { 127 ERROR("can't install notify handler\n"); 128 } 129 } 130 131 132 *cookie = device; 133 return B_OK; 134 } 135 136 137 static void 138 acpi_button_uninit_device(void *_cookie) 139 { 140 acpi_button_device_info *device = (acpi_button_device_info *)_cookie; 141 if (device->fixed) { 142 sAcpi->remove_fixed_event_handler(device->type, 143 acpi_button_fixed_handler); 144 } else { 145 device->acpi->remove_notify_handler(device->acpi_cookie, 146 ACPI_DEVICE_NOTIFY, acpi_button_notify_handler); 147 } 148 free(device); 149 } 150 151 152 static status_t 153 acpi_button_open(void *_cookie, const char *path, int flags, void** cookie) 154 { 155 acpi_button_device_info *device = (acpi_button_device_info *)_cookie; 156 157 sAcpi->enable_fixed_event(device->type); 158 159 *cookie = device; 160 return B_OK; 161 } 162 163 164 static status_t 165 acpi_button_read(void* _cookie, off_t position, void *buf, size_t* num_bytes) 166 { 167 acpi_button_device_info* device = (acpi_button_device_info*)_cookie; 168 if (*num_bytes < 1) 169 return B_IO_ERROR; 170 171 *((uint8 *)(buf)) = device->last_status; 172 device->last_status = 0; 173 174 *num_bytes = 1; 175 return B_OK; 176 } 177 178 179 static status_t 180 acpi_button_write(void* cookie, off_t position, const void* buffer, 181 size_t* num_bytes) 182 { 183 return B_ERROR; 184 } 185 186 187 static status_t 188 acpi_button_control(void* _cookie, uint32 op, void* arg, size_t len) 189 { 190 return B_ERROR; 191 } 192 193 194 static status_t 195 acpi_button_select(void *_cookie, uint8 event, selectsync *sync) 196 { 197 acpi_button_device_info* device = (acpi_button_device_info*)_cookie; 198 199 if (event != B_SELECT_READ) 200 return B_BAD_VALUE; 201 202 // add the event to the pool 203 status_t error = add_select_sync_pool_entry(&device->select_pool, sync, 204 event); 205 if (error != B_OK) { 206 ERROR("add_select_sync_pool_entry() failed: %#lx\n", error); 207 return error; 208 } 209 210 if (device->last_status != 0) 211 notify_select_event(sync, event); 212 213 return B_OK; 214 } 215 216 217 static status_t 218 acpi_button_deselect(void *_cookie, uint8 event, selectsync *sync) 219 { 220 acpi_button_device_info* device = (acpi_button_device_info*)_cookie; 221 222 if (event != B_SELECT_READ) 223 return B_BAD_VALUE; 224 225 return remove_select_sync_pool_entry(&device->select_pool, sync, event); 226 } 227 228 229 static status_t 230 acpi_button_close (void* cookie) 231 { 232 return B_OK; 233 } 234 235 236 static status_t 237 acpi_button_free (void* cookie) 238 { 239 return B_OK; 240 } 241 242 243 // #pragma mark - driver module API 244 245 246 static float 247 acpi_button_support(device_node *parent) 248 { 249 const char *bus; 250 uint32 device_type; 251 const char *hid; 252 253 // make sure parent is really the ACPI bus manager 254 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 255 return -1; 256 257 if (strcmp(bus, "acpi")) 258 return 0.0; 259 260 // check whether it's really a device 261 if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, 262 &device_type, false) != B_OK || device_type != ACPI_TYPE_DEVICE) { 263 return 0.0; 264 } 265 266 // check whether it's a button device 267 if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, 268 false) != B_OK || (strcmp(hid, "PNP0C0C") != 0 269 && strcmp(hid, "ACPI_FPB") != 0 && strcmp(hid, "PNP0C0E") != 0 270 && strcmp(hid, "ACPI_FSB") != 0)) { 271 return 0.0; 272 } 273 274 TRACE("acpi_button_support button device found\n"); 275 276 return 0.6; 277 } 278 279 280 static status_t 281 acpi_button_register_device(device_node *node) 282 { 283 device_attr attrs[] = { 284 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI Button" }}, 285 { NULL } 286 }; 287 288 return sDeviceManager->register_node(node, ACPI_BUTTON_MODULE_NAME, attrs, 289 NULL, NULL); 290 } 291 292 293 static status_t 294 acpi_button_init_driver(device_node *node, void **_driverCookie) 295 { 296 *_driverCookie = node; 297 return B_OK; 298 } 299 300 301 static void 302 acpi_button_uninit_driver(void *driverCookie) 303 { 304 } 305 306 307 static status_t 308 acpi_button_register_child_devices(void *_cookie) 309 { 310 device_node *node = (device_node*)_cookie; 311 device_node *parent = sDeviceManager->get_parent_node(node); 312 const char *hid; 313 if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, 314 false) != B_OK) { 315 sDeviceManager->put_node(parent); 316 return B_ERROR; 317 } 318 319 sDeviceManager->put_node(parent); 320 321 status_t status = B_ERROR; 322 if (strcmp(hid, "PNP0C0C") == 0 || strcmp(hid, "ACPI_FPB") == 0) { 323 status = sDeviceManager->publish_device(node, 324 "power/button/power", ACPI_BUTTON_DEVICE_MODULE_NAME); 325 } else if (strcmp(hid, "PNP0C0E") == 0 || strcmp(hid, "ACPI_FSB") == 0) { 326 status = sDeviceManager->publish_device(node, "power/button/sleep", 327 ACPI_BUTTON_DEVICE_MODULE_NAME); 328 } 329 330 return status; 331 } 332 333 334 module_dependency module_dependencies[] = { 335 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, 336 { B_ACPI_MODULE_NAME, (module_info **)&sAcpi }, 337 {} 338 }; 339 340 341 driver_module_info acpi_button_driver_module = { 342 { 343 ACPI_BUTTON_MODULE_NAME, 344 0, 345 NULL 346 }, 347 348 acpi_button_support, 349 acpi_button_register_device, 350 acpi_button_init_driver, 351 acpi_button_uninit_driver, 352 acpi_button_register_child_devices, 353 NULL, // rescan 354 NULL, // removed 355 }; 356 357 358 struct device_module_info acpi_button_device_module = { 359 { 360 ACPI_BUTTON_DEVICE_MODULE_NAME, 361 0, 362 NULL 363 }, 364 365 acpi_button_init_device, 366 acpi_button_uninit_device, 367 NULL, 368 369 acpi_button_open, 370 acpi_button_close, 371 acpi_button_free, 372 acpi_button_read, 373 acpi_button_write, 374 NULL, 375 acpi_button_control, 376 acpi_button_select, 377 acpi_button_deselect 378 }; 379 380 module_info *modules[] = { 381 (module_info *)&acpi_button_driver_module, 382 (module_info *)&acpi_button_device_module, 383 NULL 384 }; 385