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