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