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