| | 1 | /* |
| | 2 | * Copyright 2008, Haiku, Inc. All Rights Reserved. |
| | 3 | * |
| | 4 | * Distributed under the terms of the MIT License. |
| | 5 | */ |
| | 6 | |
| | 7 | #include <KernelExport.h> |
| | 8 | #include <Drivers.h> |
| | 9 | #include <Errors.h> |
| | 10 | #include <string.h> |
| | 11 | |
| | 12 | #include <stdio.h> |
| | 13 | #include <stdlib.h> |
| | 14 | |
| | 15 | #include <ACPI.h> |
| | 16 | #include "acpi_battery.h" |
| | 17 | |
| | 18 | #define ACPI_BATTERY_MODULE_NAME "drivers/power/acpi_battery/driver_v1" |
| | 19 | |
| | 20 | #define ACPI_BATTERY_DEVICE_MODULE_NAME "drivers/power/acpi_battery/device_v1" |
| | 21 | |
| | 22 | /* Base Namespace devices are published to */ |
| | 23 | #define ACPI_BATTERY_BASENAME "power/acpi_battery/%d" |
| | 24 | |
| | 25 | // name of pnp generator of path ids |
| | 26 | #define ACPI_BATTERY_PATHID_GENERATOR "acpi_battery/path_id" |
| | 27 | |
| | 28 | static device_manager_info *sDeviceManager; |
| | 29 | |
| | 30 | typedef struct acpi_ns_device_info { |
| | 31 | device_node *node; |
| | 32 | acpi_device_module_info *acpi; |
| | 33 | acpi_device acpi_cookie; |
| | 34 | } acpi_battery_device_info; |
| | 35 | |
| | 36 | static inline char *acpi_battery_units(struct acpi_battery_type *battery) |
| | 37 | { |
| | 38 | return (battery->power_unit)?"mA":"mW"; |
| | 39 | } |
| | 40 | |
| | 41 | static int acpi_extract_package(struct acpi_battery_type *battery, |
| | 42 | acpi_object_type *package, |
| | 43 | struct acpi_offsets *offsets, int num) |
| | 44 | { |
| | 45 | int i; |
| | 46 | acpi_object_type *element; |
| | 47 | |
| | 48 | if (package->object_type != ACPI_TYPE_PACKAGE) |
| | 49 | return B_ERROR; |
| | 50 | |
| | 51 | for (i = 0; i < num; ++i) { |
| | 52 | if (package->data.package.count <= i) |
| | 53 | return B_ERROR; |
| | 54 | |
| | 55 | element = &package->data.package.objects[i]; |
| | 56 | if (offsets[i].mode) { |
| | 57 | uint8 *ptr = (uint8 *)battery + offsets[i].offset; |
| | 58 | if (element->object_type == ACPI_TYPE_STRING || |
| | 59 | element->object_type == ACPI_TYPE_BUFFER) |
| | 60 | strncpy(ptr, element->data.string.string, 32); |
| | 61 | else if (element->object_type == ACPI_TYPE_INTEGER) { |
| | 62 | strncpy(ptr, (uint8 *)&element->data.integer, |
| | 63 | sizeof(uint32)); |
| | 64 | ptr[sizeof(uint32)] = 0; |
| | 65 | } else |
| | 66 | *ptr = 0; /* don't have value */ |
| | 67 | } else { |
| | 68 | int *x = (int *)((uint8 *)battery + offsets[i].offset); |
| | 69 | *x = (element->object_type == ACPI_TYPE_INTEGER) ? |
| | 70 | element->data.integer : -1; |
| | 71 | } |
| | 72 | } |
| | 73 | return B_OK; |
| | 74 | } |
| | 75 | |
| | 76 | static status_t |
| | 77 | acpi_battery_init_device(void *_cookie, void **cookie) |
| | 78 | { |
| | 79 | device_node *node = (device_node *)_cookie; |
| | 80 | acpi_battery_device_info *device; |
| | 81 | device_node *parent; |
| | 82 | |
| | 83 | device = (acpi_battery_device_info *)calloc(1, sizeof(*device)); |
| | 84 | if (device == NULL) |
| | 85 | return B_NO_MEMORY; |
| | 86 | |
| | 87 | device->node = node; |
| | 88 | |
| | 89 | parent = sDeviceManager->get_parent_node(node); |
| | 90 | sDeviceManager->get_driver(parent, (driver_module_info **)&device->acpi, |
| | 91 | (void **)&device->acpi_cookie); |
| | 92 | sDeviceManager->put_node(parent); |
| | 93 | |
| | 94 | *cookie = device; |
| | 95 | return B_OK; |
| | 96 | } |
| | 97 | |
| | 98 | |
| | 99 | static void |
| | 100 | acpi_battery_uninit_device(void *_cookie) |
| | 101 | { |
| | 102 | acpi_battery_device_info *device = (acpi_battery_device_info *)_cookie; |
| | 103 | free(device); |
| | 104 | } |
| | 105 | |
| | 106 | |
| | 107 | static status_t |
| | 108 | acpi_battery_open(void *_cookie, const char *path, int flags, void** cookie) |
| | 109 | { |
| | 110 | acpi_battery_device_info *device = (acpi_battery_device_info *)_cookie; |
| | 111 | *cookie = device; |
| | 112 | return B_OK; |
| | 113 | } |
| | 114 | |
| | 115 | |
| | 116 | static status_t |
| | 117 | acpi_battery_control(void* _cookie, uint32 op, void* arg, size_t len) |
| | 118 | { |
| | 119 | acpi_battery_device_info* device = (acpi_battery_device_info*)_cookie; |
| | 120 | status_t err = B_ERROR; |
| | 121 | |
| | 122 | acpi_battery_type *att = NULL; |
| | 123 | |
| | 124 | size_t bufsize = sizeof(acpi_object_type); |
| | 125 | acpi_object_type buf; |
| | 126 | |
| | 127 | switch (op) { |
| | 128 | case drvOpGetBatteryState: { |
| | 129 | dprintf("acpi_battery: GetBatteryState() "); |
| | 130 | att = (acpi_battery_type *)arg; |
| | 131 | |
| | 132 | err = device->acpi->evaluate_method(device->acpi_cookie, "_BST", &buf, bufsize, NULL, 0); |
| | 133 | |
| | 134 | if (err != B_OK) |
| | 135 | break; |
| | 136 | |
| | 137 | err = acpi_extract_package(att, &buf, state_offsets, ARRAY_SIZE(state_offsets)); |
| | 138 | break; |
| | 139 | } |
| | 140 | case drvOpGetBatteryInfo: { |
| | 141 | dprintf("acpi_battery: GetBatteryInfo() "); |
| | 142 | att = (acpi_battery_type *)arg; |
| | 143 | |
| | 144 | err = device->acpi->evaluate_method(device->acpi_cookie, "_BIF", &buf, bufsize, NULL, 0); |
| | 145 | |
| | 146 | if (err != B_OK) |
| | 147 | break; |
| | 148 | |
| | 149 | err = acpi_extract_package(att, &buf, info_offsets, ARRAY_SIZE(info_offsets)); |
| | 150 | break; |
| | 151 | } |
| | 152 | } |
| | 153 | |
| | 154 | if (err == B_OK) |
| | 155 | dprintf("OK\n"); |
| | 156 | else |
| | 157 | dprintf("ERROR!\n"); |
| | 158 | |
| | 159 | return err; |
| | 160 | } |
| | 161 | |
| | 162 | |
| | 163 | static status_t |
| | 164 | acpi_battery_read(void* _cookie, off_t position, void *buf, size_t* num_bytes) |
| | 165 | { |
| | 166 | acpi_battery_device_info* device = (acpi_battery_device_info*)_cookie; |
| | 167 | acpi_battery_type battery; |
| | 168 | |
| | 169 | if (*num_bytes < 1) |
| | 170 | return B_IO_ERROR; |
| | 171 | |
| | 172 | if (position == 0) { |
| | 173 | size_t max_len = *num_bytes; |
| | 174 | char *str = (char *)buf; |
| | 175 | |
| | 176 | dprintf("acpi_battery: read()\n"); |
| | 177 | |
| | 178 | acpi_battery_control(device, drvOpGetBatteryInfo, &battery, 0); |
| | 179 | acpi_battery_control(device, drvOpGetBatteryState, &battery, 0); |
| | 180 | |
| | 181 | snprintf(str, max_len, "Capacity state: %s\n", |
| | 182 | (battery.state & 0x04) ? "critical" : "ok"); |
| | 183 | |
| | 184 | max_len -= strlen(str); |
| | 185 | str += strlen(str); |
| | 186 | |
| | 187 | if ((battery.state & 0x01) && (battery.state & 0x02)) |
| | 188 | snprintf(str, max_len, "Charging state: charging/discharging\n"); |
| | 189 | else if (battery.state & 0x01) |
| | 190 | snprintf(str, max_len, "Charging state: discharging\n"); |
| | 191 | else if (battery.state & 0x02) |
| | 192 | snprintf(str, max_len, "Charging state: charging\n"); |
| | 193 | else |
| | 194 | snprintf(str, max_len, "Charging state: charged\n"); |
| | 195 | |
| | 196 | max_len -= strlen(str); |
| | 197 | str += strlen(str); |
| | 198 | |
| | 199 | if (battery.current_now == ACPI_BATTERY_VALUE_UNKNOWN) |
| | 200 | snprintf(str, max_len, "Present rate: unknown\n"); |
| | 201 | else |
| | 202 | snprintf(str, max_len, "Present rate: %d %s\n", |
| | 203 | battery.current_now, acpi_battery_units(&battery)); |
| | 204 | |
| | 205 | max_len -= strlen(str); |
| | 206 | str += strlen(str); |
| | 207 | |
| | 208 | if (battery.capacity_now == ACPI_BATTERY_VALUE_UNKNOWN) |
| | 209 | snprintf(str, max_len, "Remaining capacity: unknown\n"); |
| | 210 | else |
| | 211 | snprintf(str, max_len, "Remaining capacity: %d %sh\n", |
| | 212 | battery.capacity_now, acpi_battery_units(&battery)); |
| | 213 | |
| | 214 | max_len -= strlen(str); |
| | 215 | str += strlen(str); |
| | 216 | |
| | 217 | if (battery.voltage_now == ACPI_BATTERY_VALUE_UNKNOWN) |
| | 218 | snprintf(str, max_len, "Present voltage: unknown\n"); |
| | 219 | else |
| | 220 | snprintf(str, max_len, "Present voltage: %d mV\n", |
| | 221 | battery.voltage_now); |
| | 222 | |
| | 223 | max_len -= strlen(str); |
| | 224 | str += strlen(str); |
| | 225 | |
| | 226 | //*num_bytes = strlen((char *)buf); |
| | 227 | |
| | 228 | if (battery.design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) |
| | 229 | snprintf(str, max_len, "Design capacity: unknown\n"); |
| | 230 | else |
| | 231 | snprintf(str, max_len, "Design capacity: %d %sh\n", |
| | 232 | battery.design_capacity, acpi_battery_units(&battery)); |
| | 233 | |
| | 234 | max_len -= strlen(str); |
| | 235 | str += strlen(str); |
| | 236 | |
| | 237 | if (battery.full_charge_capacity == ACPI_BATTERY_VALUE_UNKNOWN) |
| | 238 | snprintf(str, max_len, "Last full capacity: unknown\n"); |
| | 239 | else |
| | 240 | snprintf(str, max_len, "Last full capacity: %d %sh\n", |
| | 241 | battery.full_charge_capacity, acpi_battery_units(&battery)); |
| | 242 | |
| | 243 | max_len -= strlen(str); |
| | 244 | str += strlen(str); |
| | 245 | |
| | 246 | snprintf(str, max_len, "Battery technology: %srechareable\n", |
| | 247 | (!battery.technology)?"non-":""); |
| | 248 | |
| | 249 | max_len -= strlen(str); |
| | 250 | str += strlen(str); |
| | 251 | |
| | 252 | if (battery.design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) |
| | 253 | snprintf(str, max_len, "Design voltage: unknown\n"); |
| | 254 | else |
| | 255 | snprintf(str, max_len, "Design voltage: %d mV\n", |
| | 256 | battery.design_voltage); |
| | 257 | |
| | 258 | max_len -= strlen(str); |
| | 259 | str += strlen(str); |
| | 260 | |
| | 261 | snprintf(str, max_len, "Design capacity warning: %d %sh\n", |
| | 262 | battery.design_capacity_warning, acpi_battery_units(&battery)); |
| | 263 | |
| | 264 | max_len -= strlen(str); |
| | 265 | str += strlen(str); |
| | 266 | |
| | 267 | snprintf(str, max_len, "Design capacity low: %d %sh\n", |
| | 268 | battery.design_capacity_low, acpi_battery_units(&battery)); |
| | 269 | |
| | 270 | max_len -= strlen(str); |
| | 271 | str += strlen(str); |
| | 272 | |
| | 273 | snprintf(str, max_len, "Capacity granularity 1: %d %sh\n", |
| | 274 | battery.capacity_granularity_1, acpi_battery_units(&battery)); |
| | 275 | |
| | 276 | max_len -= strlen(str); |
| | 277 | str += strlen(str); |
| | 278 | |
| | 279 | snprintf(str, max_len, "Capacity granularity 2: %d %sh\n", |
| | 280 | battery.capacity_granularity_2, acpi_battery_units(&battery)); |
| | 281 | |
| | 282 | max_len -= strlen(str); |
| | 283 | str += strlen(str); |
| | 284 | |
| | 285 | snprintf(str, max_len, "Model number: %s\n", battery.model_number); |
| | 286 | |
| | 287 | max_len -= strlen(str); |
| | 288 | str += strlen(str); |
| | 289 | |
| | 290 | snprintf(str, max_len, "Serial number: %s\n", battery.serial_number); |
| | 291 | |
| | 292 | max_len -= strlen(str); |
| | 293 | str += strlen(str); |
| | 294 | |
| | 295 | snprintf(str, max_len, "Battery type: %s\n", battery.type); |
| | 296 | |
| | 297 | max_len -= strlen(str); |
| | 298 | str += strlen(str); |
| | 299 | |
| | 300 | snprintf(str, max_len, "OEM info: %s\n", battery.oem_info); |
| | 301 | |
| | 302 | *num_bytes = strlen((char *)buf); |
| | 303 | |
| | 304 | } else { |
| | 305 | *num_bytes = 0; |
| | 306 | } |
| | 307 | |
| | 308 | return B_OK; |
| | 309 | } |
| | 310 | |
| | 311 | |
| | 312 | static status_t |
| | 313 | acpi_battery_write(void* cookie, off_t position, const void* buffer, size_t* num_bytes) |
| | 314 | { |
| | 315 | return B_ERROR; |
| | 316 | } |
| | 317 | |
| | 318 | |
| | 319 | static status_t |
| | 320 | acpi_battery_close (void* cookie) |
| | 321 | { |
| | 322 | return B_OK; |
| | 323 | } |
| | 324 | |
| | 325 | |
| | 326 | static status_t |
| | 327 | acpi_battery_free (void* cookie) |
| | 328 | { |
| | 329 | return B_OK; |
| | 330 | } |
| | 331 | |
| | 332 | |
| | 333 | // #pragma mark - driver module API |
| | 334 | |
| | 335 | |
| | 336 | static float |
| | 337 | acpi_battery_support(device_node *parent) |
| | 338 | { |
| | 339 | const char *bus; |
| | 340 | uint32 device_type; |
| | 341 | const char *hid; |
| | 342 | |
| | 343 | dprintf("acpi_battery_support\n"); |
| | 344 | |
| | 345 | // make sure parent is really the ACPI bus manager |
| | 346 | if (sDeviceManager->get_attr_string(parent, B_DEVICE_BUS, &bus, false)) |
| | 347 | { |
| | 348 | dprintf("acpi_battery: this is not an ACPI bus manager\n"); |
| | 349 | return -1; |
| | 350 | } |
| | 351 | |
| | 352 | if (strcmp(bus, "acpi")) |
| | 353 | { |
| | 354 | dprintf("acpi_battery: bus acpi != %s\n", bus); |
| | 355 | return 0.0; |
| | 356 | } |
| | 357 | |
| | 358 | // check whether it's really a device |
| | 359 | if (sDeviceManager->get_attr_uint32(parent, ACPI_DEVICE_TYPE_ITEM, &device_type, false) != B_OK |
| | 360 | || device_type != ACPI_TYPE_DEVICE) { |
| | 361 | dprintf("acpi_battery: this is not an ACPI device\n"); |
| | 362 | return 0.0; |
| | 363 | } |
| | 364 | |
| | 365 | if (sDeviceManager->get_attr_string(parent, ACPI_DEVICE_HID_ITEM, &hid, false) != B_OK |
| | 366 | || strcmp(hid, "PNP0C0A")) { |
| | 367 | dprintf("acpi_battery: not a battery %s != PNP0C0A\n", hid); |
| | 368 | return 0.0; |
| | 369 | } |
| | 370 | |
| | 371 | dprintf("acpi_battery: got it man!\n"); |
| | 372 | |
| | 373 | return 0.6; |
| | 374 | } |
| | 375 | |
| | 376 | |
| | 377 | static status_t |
| | 378 | acpi_battery_register_device(device_node *node) |
| | 379 | { |
| | 380 | device_attr attrs[] = { |
| | 381 | { B_DEVICE_PRETTY_NAME, B_STRING_TYPE, { string: "ACPI Battery" }}, |
| | 382 | { NULL } |
| | 383 | }; |
| | 384 | |
| | 385 | return sDeviceManager->register_node(node, ACPI_BATTERY_MODULE_NAME, attrs, NULL, NULL); |
| | 386 | } |
| | 387 | |
| | 388 | |
| | 389 | static status_t |
| | 390 | acpi_battery_init_driver(device_node *node, void **_driverCookie) |
| | 391 | { |
| | 392 | *_driverCookie = node; |
| | 393 | return B_OK; |
| | 394 | } |
| | 395 | |
| | 396 | |
| | 397 | static void |
| | 398 | acpi_battery_uninit_driver(void *driverCookie) |
| | 399 | { |
| | 400 | } |
| | 401 | |
| | 402 | |
| | 403 | static status_t |
| | 404 | acpi_battery_register_child_devices(void *_cookie) |
| | 405 | { |
| | 406 | device_node *node = _cookie; |
| | 407 | int path_id; |
| | 408 | char name[128]; |
| | 409 | |
| | 410 | dprintf("acpi_battery_register_child_devices\n"); |
| | 411 | |
| | 412 | path_id = sDeviceManager->create_id(ACPI_BATTERY_PATHID_GENERATOR); |
| | 413 | if (path_id < 0) { |
| | 414 | dprintf("acpi_battery_register_child_devices: couldn't create a path_id\n"); |
| | 415 | return B_ERROR; |
| | 416 | } |
| | 417 | |
| | 418 | snprintf(name, sizeof(name), ACPI_BATTERY_BASENAME, path_id); |
| | 419 | |
| | 420 | /* TODO: register the other device (use state and info) */ |
| | 421 | |
| | 422 | return sDeviceManager->publish_device(node, name, ACPI_BATTERY_DEVICE_MODULE_NAME); |
| | 423 | } |
| | 424 | |
| | 425 | |
| | 426 | module_dependency module_dependencies[] = { |
| | 427 | { B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager }, |
| | 428 | {} |
| | 429 | }; |
| | 430 | |
| | 431 | |
| | 432 | driver_module_info acpi_battery_driver_module = { |
| | 433 | { |
| | 434 | ACPI_BATTERY_MODULE_NAME, |
| | 435 | 0, |
| | 436 | NULL |
| | 437 | }, |
| | 438 | |
| | 439 | acpi_battery_support, |
| | 440 | acpi_battery_register_device, |
| | 441 | acpi_battery_init_driver, |
| | 442 | acpi_battery_uninit_driver, |
| | 443 | acpi_battery_register_child_devices, |
| | 444 | NULL, // rescan |
| | 445 | NULL, // removed |
| | 446 | }; |
| | 447 | |
| | 448 | |
| | 449 | struct device_module_info acpi_battery_device_module = { |
| | 450 | { |
| | 451 | ACPI_BATTERY_DEVICE_MODULE_NAME, |
| | 452 | 0, |
| | 453 | NULL |
| | 454 | }, |
| | 455 | |
| | 456 | acpi_battery_init_device, |
| | 457 | acpi_battery_uninit_device, |
| | 458 | NULL, |
| | 459 | |
| | 460 | acpi_battery_open, |
| | 461 | acpi_battery_close, |
| | 462 | acpi_battery_free, |
| | 463 | acpi_battery_read, |
| | 464 | acpi_battery_write, |
| | 465 | NULL, |
| | 466 | acpi_battery_control, |
| | 467 | |
| | 468 | NULL, |
| | 469 | NULL |
| | 470 | }; |
| | 471 | |
| | 472 | module_info *modules[] = { |
| | 473 | (module_info *)&acpi_battery_driver_module, |
| | 474 | (module_info *)&acpi_battery_device_module, |
| | 475 | NULL |
| | 476 | }; |