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