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
33static device_manager_info *sDeviceManager;
34static ConditionVariable sBatteryCondition;
35
36
37status_t
38ReadBatteryStatus(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
87exit:
88	free(buffer.pointer);
89	return status;
90}
91
92
93status_t
94ReadBatteryInfo(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
176exit:
177	free(buffer.pointer);
178	return status;
179}
180
181
182int
183EstimatedRuntime(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
219void
220battery_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
227void
228TraceBatteryInfo(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
246static status_t
247acpi_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
263static status_t
264acpi_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
273static status_t
274acpi_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
332static status_t
333acpi_battery_write(void* cookie, off_t position, const void* buffer, size_t* numBytes)
334{
335	return B_ERROR;
336}
337
338
339status_t
340acpi_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
394static status_t
395acpi_battery_free(void* cookie)
396{
397	return B_OK;
398}
399
400
401//	#pragma mark - driver module API
402
403
404static float
405acpi_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
434static status_t
435acpi_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
448static status_t
449acpi_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
474static void
475acpi_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
487static status_t
488acpi_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
506static status_t
507acpi_battery_init_device(void *driverCookie, void **cookie)
508{
509	*cookie = driverCookie;
510	return B_OK;
511}
512
513
514static void
515acpi_battery_uninit_device(void *_cookie)
516{
517
518}
519
520
521
522module_dependency module_dependencies[] = {
523	{ B_DEVICE_MANAGER_MODULE_NAME, (module_info **)&sDeviceManager },
524	{}
525};
526
527
528driver_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
545struct 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
568module_info *modules[] = {
569	(module_info *)&acpi_battery_driver_module,
570	(module_info *)&acpi_battery_device_module,
571	NULL
572};
573