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