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