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