1 /* 2 * Copyright 2009, Haiku, Inc. All Rights Reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Clemens Zeidler, haiku@clemens-zeidler.de 7 * Alexander von Gluck, kallisti5@unixzen.com 8 */ 9 10 11 #include <ACPI.h> 12 #include <condition_variable.h> 13 #include <Drivers.h> 14 #include <Errors.h> 15 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <string.h> 19 20 #include "device/power_managment.h" 21 22 23 struct battery_driver_cookie { 24 device_node* node; 25 acpi_device_module_info* acpi; 26 acpi_device acpi_cookie; 27 }; 28 29 30 struct battery_device_cookie { 31 battery_driver_cookie* driver_cookie; 32 int32 stop_watching; 33 }; 34 35 36 #define ACPI_BATTERY_DRIVER_NAME "drivers/power/acpi_battery/driver_v1" 37 #define ACPI_BATTERY_DEVICE_NAME "drivers/power/acpi_battery/device_v1" 38 39 /* Base Namespace devices are published to */ 40 #define ACPI_BATTERY_BASENAME "power/acpi_battery/%d" 41 42 // name of pnp generator of path ids 43 #define ACPI_BATTERY_PATHID_GENERATOR "acpi_battery/path_id" 44 45 #define ACPI_NAME_BATTERY "PNP0C0A" 46 47 //#define TRACE_BATTERY 48 #ifdef TRACE_BATTERY 49 # define TRACE(x...) dprintf("acpi_battery: "x) 50 #else 51 # define TRACE(x...) 52 #endif 53 54 55 static device_manager_info *sDeviceManager; 56 static ConditionVariable sBatteryCondition; 57 58 59 status_t 60 ReadBatteryStatus(battery_driver_cookie* cookie, 61 acpi_battery_info* batteryStatus) 62 { 63 status_t status = B_ERROR; 64 65 acpi_data buffer; 66 buffer.pointer = NULL; 67 buffer.length = ACPI_ALLOCATE_BUFFER; 68 69 acpi_object_type* object; 70 acpi_object_type* pointer; 71 72 status = cookie->acpi->evaluate_method(cookie->acpi_cookie, "_BST", NULL, 73 &buffer); 74 if (status != B_OK) 75 goto exit; 76 77 object = (acpi_object_type*)buffer.pointer; 78 if (object->object_type != ACPI_TYPE_PACKAGE 79 || object->package.count < 4) { 80 status = B_ERROR; 81 goto exit; 82 } 83 84 pointer = object->package.objects; 85 batteryStatus->state = (pointer->object_type == ACPI_TYPE_INTEGER) 86 ? pointer->integer.integer : BATTERY_CRITICAL_STATE; 87 88 pointer++; 89 batteryStatus->current_rate = (pointer->object_type == ACPI_TYPE_INTEGER) 90 ? pointer->integer.integer : -1; 91 92 pointer++; 93 batteryStatus->capacity = (pointer->object_type == ACPI_TYPE_INTEGER) 94 ? pointer->integer.integer : -1; 95 96 pointer++; 97 batteryStatus->voltage = (pointer->object_type == ACPI_TYPE_INTEGER) 98 ? pointer->integer.integer : -1; 99 100 /* If key values are all < 0, it is likely that the battery slot is empty 101 * or the battery is damaged. Set BATTERY_CRITICAL_STATE 102 */ 103 if (batteryStatus->voltage < 0 104 && batteryStatus->current_rate < 0 105 && batteryStatus->capacity < 0) { 106 batteryStatus->state = BATTERY_CRITICAL_STATE; 107 } 108 109 110 exit: 111 free(buffer.pointer); 112 return status; 113 } 114 115 116 status_t 117 ReadBatteryInfo(battery_driver_cookie* cookie, 118 acpi_extended_battery_info* batteryInfo) 119 { 120 acpi_data buffer; 121 buffer.pointer = NULL; 122 buffer.length = ACPI_ALLOCATE_BUFFER; 123 124 acpi_object_type* object; 125 acpi_object_type* pointer; 126 127 status_t status = cookie->acpi->evaluate_method(cookie->acpi_cookie, 128 "_BIF", NULL, &buffer); 129 if (status != B_OK) 130 goto exit; 131 132 object = (acpi_object_type*)buffer.pointer; 133 if (object->object_type != ACPI_TYPE_PACKAGE 134 || object->package.count < 13) { 135 status = B_ERROR; 136 goto exit; 137 } 138 139 pointer = object->package.objects; 140 batteryInfo->power_unit = (pointer->object_type == ACPI_TYPE_INTEGER) 141 ? pointer->integer.integer : -1; 142 143 pointer++; 144 batteryInfo->design_capacity = (pointer->object_type == ACPI_TYPE_INTEGER) 145 ? pointer->integer.integer : -1; 146 147 pointer++; 148 batteryInfo->last_full_charge = (pointer->object_type == ACPI_TYPE_INTEGER) 149 ? pointer->integer.integer : -1; 150 151 pointer++; 152 batteryInfo->technology = (pointer->object_type == ACPI_TYPE_INTEGER) 153 ? pointer->integer.integer : -1; 154 155 pointer++; 156 batteryInfo->design_voltage = (pointer->object_type == ACPI_TYPE_INTEGER) 157 ? pointer->integer.integer : -1; 158 159 pointer++; 160 batteryInfo->design_capacity_warning = 161 (pointer->object_type == ACPI_TYPE_INTEGER) 162 ? pointer->integer.integer : -1; 163 164 pointer++; 165 batteryInfo->design_capacity_low = 166 (pointer->object_type == ACPI_TYPE_INTEGER) 167 ? pointer->integer.integer : -1; 168 169 pointer++; 170 batteryInfo->capacity_granularity_1 = 171 (pointer->object_type == ACPI_TYPE_INTEGER) 172 ? pointer->integer.integer : -1; 173 174 pointer++; 175 batteryInfo->capacity_granularity_2 = 176 (pointer->object_type == ACPI_TYPE_INTEGER) 177 ? pointer->integer.integer : -1; 178 179 pointer++; 180 strlcpy(batteryInfo->model_number, 181 (pointer->object_type == ACPI_TYPE_STRING) 182 ? pointer->string.string : "", sizeof(batteryInfo->model_number)); 183 184 pointer++; 185 strlcpy(batteryInfo->serial_number, 186 (pointer->object_type == ACPI_TYPE_STRING) 187 ? pointer->string.string : "", sizeof(batteryInfo->serial_number)); 188 189 pointer++; 190 strlcpy(batteryInfo->type, (pointer->object_type == ACPI_TYPE_STRING) 191 ? pointer->string.string : "", sizeof(batteryInfo->type)); 192 193 pointer++; 194 strlcpy(batteryInfo->oem_info, (pointer->object_type == ACPI_TYPE_STRING) 195 ? pointer->string.string : "", sizeof(batteryInfo->oem_info)); 196 197 exit: 198 free(buffer.pointer); 199 return status; 200 } 201 202 203 int 204 EstimatedRuntime(battery_driver_cookie* cookie, acpi_battery_info* info) 205 { 206 status_t status = B_ERROR; 207 208 acpi_object_type argument; 209 argument.object_type = ACPI_TYPE_INTEGER; 210 argument.integer.integer = info->current_rate; 211 212 acpi_objects arguments; 213 arguments.count = 1; 214 arguments.pointer = &argument; 215 216 acpi_object_type object; 217 218 acpi_data buffer; 219 buffer.pointer = &object; 220 buffer.length = sizeof(object); 221 222 acpi_object_type* returnObject; 223 224 status = cookie->acpi->evaluate_method(cookie->acpi_cookie, "_BTM", 225 &arguments, &buffer); 226 if (status != B_OK) 227 return -1; 228 229 returnObject = (acpi_object_type*)buffer.pointer; 230 231 if (returnObject->object_type != ACPI_TYPE_INTEGER) 232 return -1; 233 234 int result = returnObject->integer.integer; 235 236 return result; 237 } 238 239 240 void 241 battery_notify_handler(acpi_handle device, uint32 value, void *context) 242 { 243 TRACE("battery_notify_handler event 0x%x\n", int(value)); 244 sBatteryCondition.NotifyAll(); 245 } 246 247 248 void 249 TraceBatteryInfo(acpi_extended_battery_info* batteryInfo) 250 { 251 TRACE("BIF power unit %i\n", batteryInfo->power_unit); 252 TRACE("BIF design capacity %i\n", batteryInfo->design_capacity); 253 TRACE("BIF last full charge %i\n", batteryInfo->last_full_charge); 254 TRACE("BIF technology %i\n", batteryInfo->technology); 255 TRACE("BIF design voltage %i\n", batteryInfo->design_voltage); 256 TRACE("BIF design capacity warning %i\n", batteryInfo->design_capacity_warning); 257 TRACE("BIF design capacity low %i\n", batteryInfo->design_capacity_low); 258 TRACE("BIF capacity granularity 1 %i\n", batteryInfo->capacity_granularity_1); 259 TRACE("BIF capacity granularity 2 %i\n", batteryInfo->capacity_granularity_2); 260 TRACE("BIF model number %s\n", batteryInfo->model_number); 261 TRACE("BIF serial number %s\n", batteryInfo->serial_number); 262 TRACE("BIF type %s\n", batteryInfo->type); 263 TRACE("BIF oem info %s\n", batteryInfo->oem_info); 264 } 265 266 267 // #pragma mark - device module API 268 269 270 static status_t 271 acpi_battery_init_device(void *driverCookie, void **cookie) 272 { 273 *cookie = driverCookie; 274 return B_OK; 275 } 276 277 278 static void 279 acpi_battery_uninit_device(void *_cookie) 280 { 281 282 } 283 284 285 static status_t 286 acpi_battery_open(void *initCookie, const char *path, int flags, void** cookie) 287 { 288 battery_device_cookie *device; 289 device = (battery_device_cookie*)calloc(1, sizeof(battery_device_cookie)); 290 if (device == NULL) 291 return B_NO_MEMORY; 292 293 device->driver_cookie = (battery_driver_cookie*)initCookie; 294 device->stop_watching = 0; 295 296 *cookie = device; 297 298 return B_OK; 299 } 300 301 302 static status_t 303 acpi_battery_close(void* cookie) 304 { 305 return B_OK; 306 } 307 308 309 static status_t 310 acpi_battery_read(void* _cookie, off_t position, void *buffer, size_t* numBytes) 311 { 312 if (*numBytes < 1) 313 return B_IO_ERROR; 314 315 battery_device_cookie *device = (battery_device_cookie*)_cookie; 316 317 acpi_battery_info batteryStatus; 318 ReadBatteryStatus(device->driver_cookie, &batteryStatus); 319 320 acpi_extended_battery_info batteryInfo; 321 ReadBatteryInfo(device->driver_cookie, &batteryInfo); 322 323 if (position == 0) { 324 size_t max_len = *numBytes; 325 char *str = (char *)buffer; 326 327 snprintf(str, max_len, "Battery Status:\n"); 328 max_len-= strlen(str); 329 str += strlen(str); 330 331 snprintf(str, max_len, " State %i, Current Rate %i, Capacity %i, " 332 "Voltage %i\n", batteryStatus.state, batteryStatus.current_rate, 333 batteryStatus.capacity, batteryStatus.voltage); 334 max_len-= strlen(str); 335 str += strlen(str); 336 337 snprintf(str, max_len, "\nBattery Info:\n"); 338 max_len-= strlen(str); 339 str += strlen(str); 340 341 snprintf(str, max_len, " Power Unit %i, Design Capacity %i, " 342 "Last Full Charge %i, Technology %i\n", batteryInfo.power_unit, 343 batteryInfo.design_capacity, batteryInfo.last_full_charge, 344 batteryInfo.technology); 345 max_len-= strlen(str); 346 str += strlen(str); 347 snprintf(str, max_len, " Design Voltage %i, Design Capacity Warning %i, " 348 "Design Capacity Low %i, Capacity Granularity1 %i, " 349 "Capacity Granularity1 %i\n", batteryInfo.design_voltage, 350 batteryInfo.design_capacity_warning, batteryInfo.design_capacity_low, 351 batteryInfo.capacity_granularity_1, batteryInfo.capacity_granularity_1); 352 max_len-= strlen(str); 353 str += strlen(str); 354 snprintf(str, max_len, " Model Number %s, Serial Number %s, " 355 "Type %s, OEM Info %s\n", batteryInfo.model_number, 356 batteryInfo.serial_number, batteryInfo.type, batteryInfo.oem_info); 357 max_len-= strlen(str); 358 str += strlen(str); 359 360 *numBytes = strlen((char *)buffer); 361 } else 362 *numBytes = 0; 363 364 return B_OK; 365 } 366 367 368 static status_t 369 acpi_battery_write(void* cookie, off_t position, const void* buffer, size_t* numBytes) 370 { 371 return B_ERROR; 372 } 373 374 375 static status_t 376 acpi_battery_control(void* _cookie, uint32 op, void* arg, size_t len) 377 { 378 battery_device_cookie* device = (battery_device_cookie*)_cookie; 379 status_t err; 380 381 switch (op) { 382 case IDENTIFY_DEVICE: { 383 if (len < sizeof(uint32)) 384 return B_BAD_VALUE; 385 386 uint32 magicId = kMagicACPIBatteryID; 387 return user_memcpy(arg, &magicId, sizeof(magicId)); 388 } 389 390 case GET_BATTERY_INFO: { 391 if (len < sizeof(acpi_battery_info)) 392 return B_BAD_VALUE; 393 394 acpi_battery_info batteryInfo; 395 err = ReadBatteryStatus(device->driver_cookie, &batteryInfo); 396 if (err != B_OK) 397 return err; 398 return user_memcpy(arg, &batteryInfo, sizeof(batteryInfo)); 399 } 400 401 case GET_EXTENDED_BATTERY_INFO: { 402 if (len < sizeof(acpi_extended_battery_info)) 403 return B_BAD_VALUE; 404 405 acpi_extended_battery_info extBatteryInfo; 406 err = ReadBatteryInfo(device->driver_cookie, &extBatteryInfo); 407 if (err != B_OK) 408 return err; 409 return user_memcpy(arg, &extBatteryInfo, sizeof(extBatteryInfo)); 410 } 411 412 case WATCH_BATTERY: 413 sBatteryCondition.Wait(); 414 if (atomic_get(&(device->stop_watching))) { 415 atomic_set(&(device->stop_watching), 0); 416 return B_ERROR; 417 } 418 return B_OK; 419 420 case STOP_WATCHING_BATTERY: 421 atomic_set(&(device->stop_watching), 1); 422 sBatteryCondition.NotifyAll(); 423 return B_OK; 424 } 425 426 return B_DEV_INVALID_IOCTL; 427 } 428 429 430 static status_t 431 acpi_battery_free(void* cookie) 432 { 433 battery_device_cookie* device = (battery_device_cookie*)cookie; 434 free(device); 435 return B_OK; 436 } 437 438 439 // #pragma mark - driver module API 440 441 442 static float 443 acpi_battery_support(device_node *parent) 444 { 445 // make sure parent is really the ACPI bus manager 446 const char *bus; 447 if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) 448 return -1; 449 450 if (strcmp(bus, "acpi")) 451 return 0.0; 452 453 // check whether it's really a device 454 uint32 device_type; 455 if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, 456 &device_type, false) != B_OK 457 || device_type != ACPI_TYPE_DEVICE) { 458 return 0.0; 459 } 460 461 // check whether it's a battery device 462 const char *name; 463 if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &name, 464 false) != B_OK || strcmp(name, ACPI_NAME_BATTERY)) { 465 return 0.0; 466 } 467 468 return 0.6; 469 } 470 471 472 static status_t 473 acpi_battery_register_device(device_node *node) 474 { 475 device_attr attrs[] = { 476 { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI Battery" }}, 477 { NULL } 478 }; 479 480 return sDeviceManager->register_node(node, ACPI_BATTERY_DRIVER_NAME, attrs, 481 NULL, NULL); 482 } 483 484 485 static status_t 486 acpi_battery_init_driver(device_node *node, void **driverCookie) 487 { 488 battery_driver_cookie *device; 489 device = (battery_driver_cookie *)calloc(1, sizeof(battery_driver_cookie)); 490 if (device == NULL) 491 return B_NO_MEMORY; 492 493 *driverCookie = device; 494 495 device->node = node; 496 497 device_node *parent; 498 parent = sDeviceManager->get_parent_node(node); 499 sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi, 500 (void **)&device->acpi_cookie); 501 sDeviceManager->put_node(parent); 502 503 // install notify handler 504 device->acpi->install_notify_handler(device->acpi_cookie, 505 ACPI_ALL_NOTIFY, battery_notify_handler, device); 506 507 return B_OK; 508 } 509 510 511 static void 512 acpi_battery_uninit_driver(void *driverCookie) 513 { 514 TRACE("acpi_battery_uninit_driver\n"); 515 battery_driver_cookie *device = (battery_driver_cookie*)driverCookie; 516 517 device->acpi->remove_notify_handler(device->acpi_cookie, 518 ACPI_ALL_NOTIFY, battery_notify_handler); 519 520 free(device); 521 } 522 523 524 static status_t 525 acpi_battery_register_child_devices(void *cookie) 526 { 527 battery_driver_cookie *device = (battery_driver_cookie*)cookie; 528 529 int pathID = sDeviceManager->create_id(ACPI_BATTERY_PATHID_GENERATOR); 530 if (pathID < 0) { 531 TRACE("register_child_devices: couldn't create a path_id\n"); 532 return B_ERROR; 533 } 534 535 char name[128]; 536 snprintf(name, sizeof(name), ACPI_BATTERY_BASENAME, pathID); 537 538 return sDeviceManager->publish_device(device->node, name, 539 ACPI_BATTERY_DEVICE_NAME); 540 } 541 542 543 544 545 546 547 module_dependency module_dependencies[] = { 548 { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, 549 {} 550 }; 551 552 553 driver_module_info acpi_battery_driver_module = { 554 { 555 ACPI_BATTERY_DRIVER_NAME, 556 0, 557 NULL 558 }, 559 560 acpi_battery_support, 561 acpi_battery_register_device, 562 acpi_battery_init_driver, 563 acpi_battery_uninit_driver, 564 acpi_battery_register_child_devices, 565 NULL, // rescan 566 NULL, // removed 567 }; 568 569 570 struct device_module_info acpi_battery_device_module = { 571 { 572 ACPI_BATTERY_DEVICE_NAME, 573 0, 574 NULL 575 }, 576 577 acpi_battery_init_device, 578 acpi_battery_uninit_device, 579 NULL, 580 581 acpi_battery_open, 582 acpi_battery_close, 583 acpi_battery_free, 584 acpi_battery_read, 585 acpi_battery_write, 586 NULL, 587 acpi_battery_control, 588 589 NULL, 590 NULL 591 }; 592 593 module_info *modules[] = { 594 (module_info *)&acpi_battery_driver_module, 595 (module_info *)&acpi_battery_device_module, 596 NULL 597 }; 598