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