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