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