battery.c 24.0 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
/*
 *  acpi_battery.c - ACPI Battery Driver ($Revision: 37 $)
 *
 *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
 *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or (at
 *  your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful, but
 *  WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License along
 *  with this program; if not, write to the Free Software Foundation, Inc.,
 *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
 *
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 */

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <asm/uaccess.h>

#include <acpi/acpi_bus.h>
#include <acpi/acpi_drivers.h>

#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF

#define ACPI_BATTERY_FORMAT_BIF	"NNNNNNNNNSSSS"
#define ACPI_BATTERY_FORMAT_BST	"NNNN"

#define ACPI_BATTERY_COMPONENT		0x00040000
#define ACPI_BATTERY_CLASS		"battery"
#define ACPI_BATTERY_DEVICE_NAME	"Battery"
#define ACPI_BATTERY_NOTIFY_STATUS	0x80
#define ACPI_BATTERY_NOTIFY_INFO	0x81
#define ACPI_BATTERY_UNITS_WATTS	"mW"
#define ACPI_BATTERY_UNITS_AMPS		"mA"

#define _COMPONENT		ACPI_BATTERY_COMPONENT
51 52 53 54 55 56 57

#define ACPI_BATTERY_UPDATE_TIME	0

#define ACPI_BATTERY_NONE_UPDATE	0
#define ACPI_BATTERY_EASY_UPDATE	1
#define ACPI_BATTERY_INIT_UPDATE	2

58
ACPI_MODULE_NAME("battery");
L
Linus Torvalds 已提交
59

60
MODULE_AUTHOR("Paul Diefenbaugh");
61
MODULE_DESCRIPTION("ACPI Battery Driver");
L
Linus Torvalds 已提交
62 63
MODULE_LICENSE("GPL");

64 65 66 67 68
static unsigned int update_time = ACPI_BATTERY_UPDATE_TIME;

/* 0 - every time, > 0 - by update_time */
module_param(update_time, uint, 0644);

69 70 71
extern struct proc_dir_entry *acpi_lock_battery_dir(void);
extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);

L
Len Brown 已提交
72 73
static int acpi_battery_add(struct acpi_device *device);
static int acpi_battery_remove(struct acpi_device *device, int type);
74
static int acpi_battery_resume(struct acpi_device *device);
L
Linus Torvalds 已提交
75

76 77 78 79 80 81
static const struct acpi_device_id battery_device_ids[] = {
	{"PNP0C0A", 0},
	{"", 0},
};
MODULE_DEVICE_TABLE(acpi, battery_device_ids);

L
Linus Torvalds 已提交
82
static struct acpi_driver acpi_battery_driver = {
L
Len Brown 已提交
83
	.name = "battery",
L
Len Brown 已提交
84
	.class = ACPI_BATTERY_CLASS,
85
	.ids = battery_device_ids,
L
Len Brown 已提交
86 87
	.ops = {
		.add = acpi_battery_add,
88
		.resume = acpi_battery_resume,
L
Len Brown 已提交
89 90
		.remove = acpi_battery_remove,
		},
L
Linus Torvalds 已提交
91 92
};

93
struct acpi_battery_state {
L
Len Brown 已提交
94 95 96 97
	acpi_integer state;
	acpi_integer present_rate;
	acpi_integer remaining_capacity;
	acpi_integer present_voltage;
L
Linus Torvalds 已提交
98 99 100
};

struct acpi_battery_info {
L
Len Brown 已提交
101 102 103 104 105 106 107 108 109 110 111 112 113
	acpi_integer power_unit;
	acpi_integer design_capacity;
	acpi_integer last_full_capacity;
	acpi_integer battery_technology;
	acpi_integer design_voltage;
	acpi_integer design_capacity_warning;
	acpi_integer design_capacity_low;
	acpi_integer battery_capacity_granularity_1;
	acpi_integer battery_capacity_granularity_2;
	acpi_string model_number;
	acpi_string serial_number;
	acpi_string battery_type;
	acpi_string oem_info;
L
Linus Torvalds 已提交
114 115
};

116
enum acpi_battery_files {
117 118 119 120 121 122
	ACPI_BATTERY_INFO = 0,
	ACPI_BATTERY_STATE,
	ACPI_BATTERY_ALARM,
	ACPI_BATTERY_NUMFILES,
};

L
Linus Torvalds 已提交
123
struct acpi_battery_flags {
124 125 126
	u8 battery_present_prev;
	u8 alarm_present;
	u8 init_update;
127
	u8 update[ACPI_BATTERY_NUMFILES];
128
	u8 power_unit;
L
Linus Torvalds 已提交
129 130 131
};

struct acpi_battery {
V
Vladimir Lebedev 已提交
132
	struct acpi_device *device;
L
Linus Torvalds 已提交
133
	struct acpi_battery_flags flags;
134 135
	struct acpi_buffer bif_data;
	struct acpi_buffer bst_data;
136
	struct mutex lock;
L
Len Brown 已提交
137
	unsigned long alarm;
138
	unsigned long update_time[ACPI_BATTERY_NUMFILES];
139

L
Linus Torvalds 已提交
140 141
};

142
inline int acpi_battery_present(struct acpi_battery *battery)
143
{
144 145 146 147 148 149 150 151
	return battery->device->status.battery_present;
}
inline char *acpi_battery_power_units(struct acpi_battery *battery)
{
	if (battery->flags.power_unit)
		return ACPI_BATTERY_UNITS_AMPS;
	else
		return ACPI_BATTERY_UNITS_WATTS;
152 153
}

154
inline acpi_handle acpi_battery_handle(struct acpi_battery *battery)
155
{
156
	return battery->device->handle;
157 158
}

159 160 161 162
/* --------------------------------------------------------------------------
                               Battery Management
   -------------------------------------------------------------------------- */

163 164 165 166 167 168
static void acpi_battery_check_result(struct acpi_battery *battery, int result)
{
	if (!battery)
		return;

	if (result) {
169
		battery->flags.init_update = 1;
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
	}
}

static int acpi_battery_extract_package(struct acpi_battery *battery,
					union acpi_object *package,
					struct acpi_buffer *format,
					struct acpi_buffer *data,
					char *package_name)
{
	acpi_status status = AE_OK;
	struct acpi_buffer data_null = { 0, NULL };

	status = acpi_extract_package(package, format, &data_null);
	if (status != AE_BUFFER_OVERFLOW) {
		ACPI_EXCEPTION((AE_INFO, status, "Extracting size %s",
				package_name));
		return -ENODEV;
	}

	if (data_null.length != data->length) {
190
		kfree(data->pointer);
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
		data->pointer = kzalloc(data_null.length, GFP_KERNEL);
		if (!data->pointer) {
			ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, "kzalloc()"));
			return -ENOMEM;
		}
		data->length = data_null.length;
	}

	status = acpi_extract_package(package, format, data);
	if (ACPI_FAILURE(status)) {
		ACPI_EXCEPTION((AE_INFO, status, "Extracting %s",
				package_name));
		return -ENODEV;
	}

	return 0;
}

static int acpi_battery_get_status(struct acpi_battery *battery)
{
	int result = 0;

	result = acpi_bus_get_status(battery->device);
	if (result) {
		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA"));
		return -ENODEV;
	}
	return result;
}

static int acpi_battery_get_info(struct acpi_battery *battery)
L
Linus Torvalds 已提交
222
{
L
Len Brown 已提交
223 224 225 226 227 228 229
	int result = 0;
	acpi_status status = 0;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BIF),
		ACPI_BATTERY_FORMAT_BIF
	};
	union acpi_object *package = NULL;
230 231
	struct acpi_buffer *data = NULL;
	struct acpi_battery_info *bif = NULL;
L
Linus Torvalds 已提交
232

233
	battery->update_time[ACPI_BATTERY_INFO] = get_seconds();
L
Linus Torvalds 已提交
234

235 236
	if (!acpi_battery_present(battery))
		return 0;
L
Linus Torvalds 已提交
237

238
	/* Evaluate _BIF */
239 240 241 242
	mutex_lock(&battery->lock);
	status = acpi_evaluate_object(acpi_battery_handle(battery), "_BIF",
				      NULL, &buffer);
	mutex_unlock(&battery->lock);
L
Linus Torvalds 已提交
243
	if (ACPI_FAILURE(status)) {
244
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF"));
245
		return -ENODEV;
L
Linus Torvalds 已提交
246 247
	}

248
	package = buffer.pointer;
L
Linus Torvalds 已提交
249

250 251
	data = &battery->bif_data;

L
Linus Torvalds 已提交
252 253
	/* Extract Package Data */

V
Vladimir Lebedev 已提交
254 255 256
	result =
	    acpi_battery_extract_package(battery, package, &format, data,
					 "_BIF");
257
	if (result)
L
Linus Torvalds 已提交
258 259
		goto end;

260
      end:
L
Linus Torvalds 已提交
261

262
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
263

264 265 266 267
	if (!result) {
		bif = data->pointer;
		battery->flags.power_unit = bif->power_unit;
	}
L
Linus Torvalds 已提交
268

269
	return result;
L
Linus Torvalds 已提交
270 271
}

272
static int acpi_battery_get_state(struct acpi_battery *battery)
L
Linus Torvalds 已提交
273
{
L
Len Brown 已提交
274 275 276 277 278 279 280
	int result = 0;
	acpi_status status = 0;
	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
	struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BST),
		ACPI_BATTERY_FORMAT_BST
	};
	union acpi_object *package = NULL;
281
	struct acpi_buffer *data = NULL;
L
Linus Torvalds 已提交
282

283
	battery->update_time[ACPI_BATTERY_STATE] = get_seconds();
L
Linus Torvalds 已提交
284

285 286
	if (!acpi_battery_present(battery))
		return 0;
L
Linus Torvalds 已提交
287

288
	/* Evaluate _BST */
289 290 291 292
	mutex_lock(&battery->lock);
	status = acpi_evaluate_object(acpi_battery_handle(battery), "_BST",
				      NULL, &buffer);
	mutex_unlock(&battery->lock);
L
Linus Torvalds 已提交
293
	if (ACPI_FAILURE(status)) {
294
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
295
		return -ENODEV;
L
Linus Torvalds 已提交
296 297
	}

298
	package = buffer.pointer;
L
Linus Torvalds 已提交
299

300
	data = &battery->bst_data;
L
Linus Torvalds 已提交
301

302
	/* Extract Package Data */
L
Linus Torvalds 已提交
303

V
Vladimir Lebedev 已提交
304 305 306
	result =
	    acpi_battery_extract_package(battery, package, &format, data,
					 "_BST");
307
	if (result)
L
Linus Torvalds 已提交
308 309
		goto end;

310
      end:
311
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
312

313 314
	return result;
}
L
Linus Torvalds 已提交
315

316 317
static int acpi_battery_get_alarm(struct acpi_battery *battery)
{
318
	battery->update_time[ACPI_BATTERY_ALARM] = get_seconds();
L
Linus Torvalds 已提交
319

320
	return 0;
L
Linus Torvalds 已提交
321 322
}

V
Vladimir Lebedev 已提交
323 324
static int acpi_battery_set_alarm(struct acpi_battery *battery,
				  unsigned long alarm)
L
Linus Torvalds 已提交
325
{
L
Len Brown 已提交
326 327 328
	acpi_status status = 0;
	union acpi_object arg0 = { ACPI_TYPE_INTEGER };
	struct acpi_object_list arg_list = { 1, &arg0 };
L
Linus Torvalds 已提交
329

330
	battery->update_time[ACPI_BATTERY_ALARM] = get_seconds();
L
Linus Torvalds 已提交
331

332 333
	if (!acpi_battery_present(battery))
		return -ENODEV;
L
Linus Torvalds 已提交
334

335
	if (!battery->flags.alarm_present)
336
		return -ENODEV;
L
Linus Torvalds 已提交
337 338 339

	arg0.integer.value = alarm;

340 341
	mutex_lock(&battery->lock);
	status = acpi_evaluate_object(acpi_battery_handle(battery), "_BTP",
V
Vladimir Lebedev 已提交
342
				 &arg_list, NULL);
343
	mutex_unlock(&battery->lock);
L
Linus Torvalds 已提交
344
	if (ACPI_FAILURE(status))
345
		return -ENODEV;
L
Linus Torvalds 已提交
346 347 348 349 350

	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm));

	battery->alarm = alarm;

351
	return 0;
L
Linus Torvalds 已提交
352 353
}

354
static int acpi_battery_init_alarm(struct acpi_battery *battery)
L
Linus Torvalds 已提交
355
{
L
Len Brown 已提交
356 357 358
	int result = 0;
	acpi_status status = AE_OK;
	acpi_handle handle = NULL;
359 360
	struct acpi_battery_info *bif = battery->bif_data.pointer;
	unsigned long alarm = battery->alarm;
L
Len Brown 已提交
361

362
	/* See if alarms are supported, and if so, set default */
L
Linus Torvalds 已提交
363

364 365
	status = acpi_get_handle(acpi_battery_handle(battery), "_BTP", &handle);
	if (ACPI_SUCCESS(status)) {
366
		battery->flags.alarm_present = 1;
367 368 369 370 371 372 373
		if (!alarm && bif) {
			alarm = bif->design_capacity_warning;
		}
		result = acpi_battery_set_alarm(battery, alarm);
		if (result)
			goto end;
	} else {
374
		battery->flags.alarm_present = 0;
375
	}
L
Linus Torvalds 已提交
376

377
      end:
L
Linus Torvalds 已提交
378

379 380
	return result;
}
L
Linus Torvalds 已提交
381

382 383 384
static int acpi_battery_init_update(struct acpi_battery *battery)
{
	int result = 0;
L
Linus Torvalds 已提交
385

386 387 388
	result = acpi_battery_get_status(battery);
	if (result)
		return result;
L
Linus Torvalds 已提交
389

390
	battery->flags.battery_present_prev = acpi_battery_present(battery);
L
Linus Torvalds 已提交
391

392 393 394 395 396
	if (acpi_battery_present(battery)) {
		result = acpi_battery_get_info(battery);
		if (result)
			return result;
		result = acpi_battery_get_state(battery);
L
Linus Torvalds 已提交
397
		if (result)
398
			return result;
L
Linus Torvalds 已提交
399

400 401 402 403 404 405 406
		acpi_battery_init_alarm(battery);
	}

	return result;
}

static int acpi_battery_update(struct acpi_battery *battery,
V
Vladimir Lebedev 已提交
407
			       int update, int *update_result_ptr)
408 409 410
{
	int result = 0;
	int update_result = ACPI_BATTERY_NONE_UPDATE;
L
Linus Torvalds 已提交
411

412 413 414
	if (!acpi_battery_present(battery)) {
		update = 1;
	}
L
Linus Torvalds 已提交
415

416
	if (battery->flags.init_update) {
417 418
		result = acpi_battery_init_update(battery);
		if (result)
419
			goto end;
420 421 422 423
		update_result = ACPI_BATTERY_INIT_UPDATE;
	} else if (update) {
		result = acpi_battery_get_status(battery);
		if (result)
424 425 426
			goto end;
		if ((!battery->flags.battery_present_prev & acpi_battery_present(battery))
		    || (battery->flags.battery_present_prev & !acpi_battery_present(battery))) {
427 428
			result = acpi_battery_init_update(battery);
			if (result)
429
				goto end;
430 431 432
			update_result = ACPI_BATTERY_INIT_UPDATE;
		} else {
			update_result = ACPI_BATTERY_EASY_UPDATE;
L
Linus Torvalds 已提交
433 434 435
		}
	}

436
      end:
L
Linus Torvalds 已提交
437

438
	battery->flags.init_update = (result != 0);
L
Linus Torvalds 已提交
439

440
	*update_result_ptr = update_result;
L
Linus Torvalds 已提交
441

442
	return result;
L
Linus Torvalds 已提交
443 444
}

445
static void acpi_battery_notify_update(struct acpi_battery *battery)
446
{
447 448
	acpi_battery_get_status(battery);

449
	if (battery->flags.init_update) {
450 451 452
		return;
	}

453 454 455 456 457
	if ((!battery->flags.battery_present_prev &
	     acpi_battery_present(battery)) ||
	    (battery->flags.battery_present_prev &
	     !acpi_battery_present(battery))) {
		battery->flags.init_update = 1;
458
	} else {
459 460 461
		battery->flags.update[ACPI_BATTERY_INFO] = 1;
		battery->flags.update[ACPI_BATTERY_STATE] = 1;
		battery->flags.update[ACPI_BATTERY_ALARM] = 1;
462 463 464
	}
}

L
Linus Torvalds 已提交
465 466 467 468
/* --------------------------------------------------------------------------
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
469
static struct proc_dir_entry *acpi_battery_dir;
470

471
static int acpi_battery_print_info(struct seq_file *seq, int result)
L
Linus Torvalds 已提交
472
{
473
	struct acpi_battery *battery = seq->private;
L
Linus Torvalds 已提交
474
	struct acpi_battery_info *bif = NULL;
L
Len Brown 已提交
475
	char *units = "?";
L
Linus Torvalds 已提交
476

477
	if (result)
L
Linus Torvalds 已提交
478 479
		goto end;

480
	if (acpi_battery_present(battery))
L
Linus Torvalds 已提交
481 482 483 484 485 486
		seq_printf(seq, "present:                 yes\n");
	else {
		seq_printf(seq, "present:                 no\n");
		goto end;
	}

487 488 489 490
	bif = battery->bif_data.pointer;
	if (!bif) {
		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BIF buffer is NULL"));
		result = -ENODEV;
L
Linus Torvalds 已提交
491 492 493
		goto end;
	}

494 495 496
	/* Battery Units */

	units = acpi_battery_power_units(battery);
L
Len Brown 已提交
497

L
Linus Torvalds 已提交
498 499 500 501
	if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
		seq_printf(seq, "design capacity:         unknown\n");
	else
		seq_printf(seq, "design capacity:         %d %sh\n",
L
Len Brown 已提交
502
			   (u32) bif->design_capacity, units);
L
Linus Torvalds 已提交
503 504 505 506 507

	if (bif->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
		seq_printf(seq, "last full capacity:      unknown\n");
	else
		seq_printf(seq, "last full capacity:      %d %sh\n",
L
Len Brown 已提交
508
			   (u32) bif->last_full_capacity, units);
L
Linus Torvalds 已提交
509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525

	switch ((u32) bif->battery_technology) {
	case 0:
		seq_printf(seq, "battery technology:      non-rechargeable\n");
		break;
	case 1:
		seq_printf(seq, "battery technology:      rechargeable\n");
		break;
	default:
		seq_printf(seq, "battery technology:      unknown\n");
		break;
	}

	if (bif->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
		seq_printf(seq, "design voltage:          unknown\n");
	else
		seq_printf(seq, "design voltage:          %d mV\n",
L
Len Brown 已提交
526
			   (u32) bif->design_voltage);
L
Linus Torvalds 已提交
527
	seq_printf(seq, "design capacity warning: %d %sh\n",
L
Len Brown 已提交
528
		   (u32) bif->design_capacity_warning, units);
L
Linus Torvalds 已提交
529
	seq_printf(seq, "design capacity low:     %d %sh\n",
L
Len Brown 已提交
530
		   (u32) bif->design_capacity_low, units);
L
Linus Torvalds 已提交
531
	seq_printf(seq, "capacity granularity 1:  %d %sh\n",
L
Len Brown 已提交
532
		   (u32) bif->battery_capacity_granularity_1, units);
L
Linus Torvalds 已提交
533
	seq_printf(seq, "capacity granularity 2:  %d %sh\n",
L
Len Brown 已提交
534 535 536 537 538 539 540
		   (u32) bif->battery_capacity_granularity_2, units);
	seq_printf(seq, "model number:            %s\n", bif->model_number);
	seq_printf(seq, "serial number:           %s\n", bif->serial_number);
	seq_printf(seq, "battery type:            %s\n", bif->battery_type);
	seq_printf(seq, "OEM info:                %s\n", bif->oem_info);

      end:
L
Linus Torvalds 已提交
541

542 543 544 545 546 547
	if (result)
		seq_printf(seq, "ERROR: Unable to read battery info\n");

	return result;
}

548
static int acpi_battery_print_state(struct seq_file *seq, int result)
L
Linus Torvalds 已提交
549
{
550
	struct acpi_battery *battery = seq->private;
551
	struct acpi_battery_state *bst = NULL;
L
Len Brown 已提交
552
	char *units = "?";
L
Linus Torvalds 已提交
553

554
	if (result)
L
Linus Torvalds 已提交
555 556
		goto end;

557
	if (acpi_battery_present(battery))
L
Linus Torvalds 已提交
558 559 560 561 562 563
		seq_printf(seq, "present:                 yes\n");
	else {
		seq_printf(seq, "present:                 no\n");
		goto end;
	}

564 565 566 567
	bst = battery->bst_data.pointer;
	if (!bst) {
		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BST buffer is NULL"));
		result = -ENODEV;
L
Linus Torvalds 已提交
568 569 570
		goto end;
	}

571 572 573 574
	/* Battery Units */

	units = acpi_battery_power_units(battery);

L
Linus Torvalds 已提交
575 576 577 578 579
	if (!(bst->state & 0x04))
		seq_printf(seq, "capacity state:          ok\n");
	else
		seq_printf(seq, "capacity state:          critical\n");

L
Len Brown 已提交
580 581 582 583
	if ((bst->state & 0x01) && (bst->state & 0x02)) {
		seq_printf(seq,
			   "charging state:          charging/discharging\n");
	} else if (bst->state & 0x01)
L
Linus Torvalds 已提交
584 585 586 587 588 589 590 591 592 593 594
		seq_printf(seq, "charging state:          discharging\n");
	else if (bst->state & 0x02)
		seq_printf(seq, "charging state:          charging\n");
	else {
		seq_printf(seq, "charging state:          charged\n");
	}

	if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN)
		seq_printf(seq, "present rate:            unknown\n");
	else
		seq_printf(seq, "present rate:            %d %s\n",
L
Len Brown 已提交
595
			   (u32) bst->present_rate, units);
L
Linus Torvalds 已提交
596 597 598 599 600

	if (bst->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
		seq_printf(seq, "remaining capacity:      unknown\n");
	else
		seq_printf(seq, "remaining capacity:      %d %sh\n",
L
Len Brown 已提交
601
			   (u32) bst->remaining_capacity, units);
L
Linus Torvalds 已提交
602 603 604 605 606

	if (bst->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
		seq_printf(seq, "present voltage:         unknown\n");
	else
		seq_printf(seq, "present voltage:         %d mV\n",
L
Len Brown 已提交
607
			   (u32) bst->present_voltage);
L
Linus Torvalds 已提交
608

L
Len Brown 已提交
609
      end:
L
Linus Torvalds 已提交
610

611 612 613 614 615 616 617
	if (result) {
		seq_printf(seq, "ERROR: Unable to read battery state\n");
	}

	return result;
}

618
static int acpi_battery_print_alarm(struct seq_file *seq, int result)
L
Linus Torvalds 已提交
619
{
620
	struct acpi_battery *battery = seq->private;
L
Len Brown 已提交
621
	char *units = "?";
L
Linus Torvalds 已提交
622

623
	if (result)
L
Linus Torvalds 已提交
624 625
		goto end;

626
	if (!acpi_battery_present(battery)) {
L
Linus Torvalds 已提交
627 628 629 630 631
		seq_printf(seq, "present:                 no\n");
		goto end;
	}

	/* Battery Units */
L
Len Brown 已提交
632

633
	units = acpi_battery_power_units(battery);
L
Linus Torvalds 已提交
634 635 636 637 638

	seq_printf(seq, "alarm:                   ");
	if (!battery->alarm)
		seq_printf(seq, "unsupported\n");
	else
639
		seq_printf(seq, "%lu %sh\n", battery->alarm, units);
L
Linus Torvalds 已提交
640

L
Len Brown 已提交
641
      end:
642 643 644 645 646 647 648

	if (result)
		seq_printf(seq, "ERROR: Unable to read battery alarm\n");

	return result;
}

L
Linus Torvalds 已提交
649
static ssize_t
L
Len Brown 已提交
650 651 652
acpi_battery_write_alarm(struct file *file,
			 const char __user * buffer,
			 size_t count, loff_t * ppos)
L
Linus Torvalds 已提交
653
{
L
Len Brown 已提交
654 655
	int result = 0;
	char alarm_string[12] = { '\0' };
656 657
	struct seq_file *m = file->private_data;
	struct acpi_battery *battery = m->private;
658
	int update_result = ACPI_BATTERY_NONE_UPDATE;
L
Linus Torvalds 已提交
659 660

	if (!battery || (count > sizeof(alarm_string) - 1))
661
		return -EINVAL;
L
Linus Torvalds 已提交
662

663 664 665 666 667
	result = acpi_battery_update(battery, 1, &update_result);
	if (result) {
		result = -ENODEV;
		goto end;
	}
L
Linus Torvalds 已提交
668

669 670 671 672 673 674 675 676 677
	if (!acpi_battery_present(battery)) {
		result = -ENODEV;
		goto end;
	}

	if (copy_from_user(alarm_string, buffer, count)) {
		result = -EFAULT;
		goto end;
	}
L
Len Brown 已提交
678

L
Linus Torvalds 已提交
679 680
	alarm_string[count] = '\0';

L
Len Brown 已提交
681 682
	result = acpi_battery_set_alarm(battery,
					simple_strtoul(alarm_string, NULL, 0));
L
Linus Torvalds 已提交
683
	if (result)
684 685 686 687 688
		goto end;

      end:

	acpi_battery_check_result(battery, result);
L
Linus Torvalds 已提交
689

690
	if (!result)
691
		return count;
692 693 694 695 696 697 698 699 700 701 702 703 704 705 706

	return result;
}

typedef int(*print_func)(struct seq_file *seq, int result);
typedef int(*get_func)(struct acpi_battery *battery);

static struct acpi_read_mux {
	print_func print;
	get_func get;
} acpi_read_funcs[ACPI_BATTERY_NUMFILES] = {
	{.get = acpi_battery_get_info, .print = acpi_battery_print_info},
	{.get = acpi_battery_get_state, .print = acpi_battery_print_state},
	{.get = acpi_battery_get_alarm, .print = acpi_battery_print_alarm},
};
707

708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731
static int acpi_battery_read(int fid, struct seq_file *seq)
{
	struct acpi_battery *battery = seq->private;
	int result = 0;
	int update_result = ACPI_BATTERY_NONE_UPDATE;
	int update = 0;

	update = (get_seconds() - battery->update_time[fid] >= update_time);
	update = (update | battery->flags.update[fid]);

	result = acpi_battery_update(battery, update, &update_result);
	if (result)
		goto end;

	if (update_result == ACPI_BATTERY_EASY_UPDATE) {
		result = acpi_read_funcs[fid].get(battery);
		if (result)
			goto end;
	}

      end:
	result = acpi_read_funcs[fid].print(seq, result);
	acpi_battery_check_result(battery, result);
	battery->flags.update[fid] = result;
732
	return result;
L
Linus Torvalds 已提交
733 734
}

735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
static int acpi_battery_read_info(struct seq_file *seq, void *offset)
{
	return acpi_battery_read(ACPI_BATTERY_INFO, seq);
}

static int acpi_battery_read_state(struct seq_file *seq, void *offset)
{
	return acpi_battery_read(ACPI_BATTERY_STATE, seq);
}

static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
{
	return acpi_battery_read(ACPI_BATTERY_ALARM, seq);
}

static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_battery_read_info, PDE(inode)->data);
}

static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_battery_read_state, PDE(inode)->data);
}

L
Linus Torvalds 已提交
760 761 762 763 764
static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
}

765 766 767 768 769 770 771 772 773
static struct battery_file {
	struct file_operations ops;
	mode_t mode;
	char *name;
} acpi_battery_file[] = {
	{
	.name = "info",
	.mode = S_IRUGO,
	.ops = {
L
Len Brown 已提交
774 775 776 777
	.open = acpi_battery_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
778
	.owner = THIS_MODULE,
779 780 781 782 783 784
	},
	},
	{
	.name = "state",
	.mode = S_IRUGO,
	.ops = {
L
Len Brown 已提交
785 786 787 788
	.open = acpi_battery_state_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
789
	.owner = THIS_MODULE,
790 791 792 793 794 795
	},
	},
	{
	.name = "alarm",
	.mode = S_IFREG | S_IRUGO | S_IWUSR,
	.ops = {
L
Len Brown 已提交
796 797 798 799 800
	.open = acpi_battery_alarm_open_fs,
	.read = seq_read,
	.write = acpi_battery_write_alarm,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
801
	.owner = THIS_MODULE,
802 803
	},
	},
L
Linus Torvalds 已提交
804 805
};

L
Len Brown 已提交
806
static int acpi_battery_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
807
{
L
Len Brown 已提交
808
	struct proc_dir_entry *entry = NULL;
809
	int i;
L
Linus Torvalds 已提交
810 811 812

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
813
						     acpi_battery_dir);
L
Linus Torvalds 已提交
814
		if (!acpi_device_dir(device))
815
			return -ENODEV;
L
Linus Torvalds 已提交
816 817 818
		acpi_device_dir(device)->owner = THIS_MODULE;
	}

819 820 821 822 823 824 825 826 827 828
	for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) {
		entry = create_proc_entry(acpi_battery_file[i].name,
				  acpi_battery_file[i].mode, acpi_device_dir(device));
		if (!entry)
			return -ENODEV;
		else {
			entry->proc_fops = &acpi_battery_file[i].ops;
			entry->data = acpi_driver_data(device);
			entry->owner = THIS_MODULE;
		}
L
Linus Torvalds 已提交
829 830
	}

831
	return 0;
L
Linus Torvalds 已提交
832 833
}

L
Len Brown 已提交
834
static int acpi_battery_remove_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
835
{
836
	int i;
L
Linus Torvalds 已提交
837
	if (acpi_device_dir(device)) {
838 839
		for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) {
			remove_proc_entry(acpi_battery_file[i].name,
L
Linus Torvalds 已提交
840
				  acpi_device_dir(device));
841
		}
L
Linus Torvalds 已提交
842 843 844 845
		remove_proc_entry(acpi_device_bid(device), acpi_battery_dir);
		acpi_device_dir(device) = NULL;
	}

846
	return 0;
L
Linus Torvalds 已提交
847 848 849 850 851 852
}

/* --------------------------------------------------------------------------
                                 Driver Interface
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
853
static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
854
{
855
	struct acpi_battery *battery = data;
L
Len Brown 已提交
856
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
857 858

	if (!battery)
859
		return;
L
Linus Torvalds 已提交
860

861
	device = battery->device;
L
Linus Torvalds 已提交
862 863 864 865

	switch (event) {
	case ACPI_BATTERY_NOTIFY_STATUS:
	case ACPI_BATTERY_NOTIFY_INFO:
866 867
	case ACPI_NOTIFY_BUS_CHECK:
	case ACPI_NOTIFY_DEVICE_CHECK:
868 869
		device = battery->device;
		acpi_battery_notify_update(battery);
V
Vladimir Lebedev 已提交
870 871
		acpi_bus_generate_event(device, event,
					acpi_battery_present(battery));
L
Linus Torvalds 已提交
872 873 874
		break;
	default:
		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
L
Len Brown 已提交
875
				  "Unsupported event [0x%x]\n", event));
L
Linus Torvalds 已提交
876 877 878
		break;
	}

879
	return;
L
Linus Torvalds 已提交
880 881
}

L
Len Brown 已提交
882
static int acpi_battery_add(struct acpi_device *device)
L
Linus Torvalds 已提交
883
{
L
Len Brown 已提交
884 885 886
	int result = 0;
	acpi_status status = 0;
	struct acpi_battery *battery = NULL;
L
Linus Torvalds 已提交
887 888

	if (!device)
889
		return -EINVAL;
L
Linus Torvalds 已提交
890

891
	battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
L
Linus Torvalds 已提交
892
	if (!battery)
893
		return -ENOMEM;
L
Linus Torvalds 已提交
894

895
	mutex_init(&battery->lock);
896
	battery->device = device;
L
Linus Torvalds 已提交
897 898 899 900
	strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
	acpi_driver_data(device) = battery;

901
	result = acpi_battery_get_status(battery);
L
Linus Torvalds 已提交
902 903 904
	if (result)
		goto end;

905
	battery->flags.init_update = 1;
906

L
Linus Torvalds 已提交
907 908 909 910
	result = acpi_battery_add_fs(device);
	if (result)
		goto end;

911
	status = acpi_install_notify_handler(device->handle,
912
					     ACPI_ALL_NOTIFY,
L
Len Brown 已提交
913
					     acpi_battery_notify, battery);
L
Linus Torvalds 已提交
914
	if (ACPI_FAILURE(status)) {
915
		ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler"));
L
Linus Torvalds 已提交
916 917 918 919 920
		result = -ENODEV;
		goto end;
	}

	printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
L
Len Brown 已提交
921 922 923 924
	       ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
	       device->status.battery_present ? "present" : "absent");

      end:
925

L
Linus Torvalds 已提交
926 927 928 929 930
	if (result) {
		acpi_battery_remove_fs(device);
		kfree(battery);
	}

931

932
	return result;
L
Linus Torvalds 已提交
933 934
}

L
Len Brown 已提交
935
static int acpi_battery_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
936
{
L
Len Brown 已提交
937 938
	acpi_status status = 0;
	struct acpi_battery *battery = NULL;
L
Linus Torvalds 已提交
939 940

	if (!device || !acpi_driver_data(device))
941
		return -EINVAL;
L
Linus Torvalds 已提交
942

943
	battery = acpi_driver_data(device);
L
Linus Torvalds 已提交
944

945
	status = acpi_remove_notify_handler(device->handle,
946
					    ACPI_ALL_NOTIFY,
L
Len Brown 已提交
947
					    acpi_battery_notify);
L
Linus Torvalds 已提交
948 949 950

	acpi_battery_remove_fs(device);

951
	kfree(battery->bif_data.pointer);
952

953
	kfree(battery->bst_data.pointer);
954

955
	mutex_destroy(&battery->lock);
956

L
Linus Torvalds 已提交
957 958
	kfree(battery);

959
	return 0;
L
Linus Torvalds 已提交
960 961
}

962
/* this is needed to learn about changes made in suspended state */
963
static int acpi_battery_resume(struct acpi_device *device)
964 965 966 967 968 969 970
{
	struct acpi_battery *battery;

	if (!device)
		return -EINVAL;

	battery = device->driver_data;
971

972
	battery->flags.init_update = 1;
973 974

	return 0;
975 976
}

L
Len Brown 已提交
977
static int __init acpi_battery_init(void)
L
Linus Torvalds 已提交
978
{
979
	int result;
L
Linus Torvalds 已提交
980

P
Pavel Machek 已提交
981 982 983
	if (acpi_disabled)
		return -ENODEV;

984
	acpi_battery_dir = acpi_lock_battery_dir();
L
Linus Torvalds 已提交
985
	if (!acpi_battery_dir)
986
		return -ENODEV;
L
Linus Torvalds 已提交
987 988 989

	result = acpi_bus_register_driver(&acpi_battery_driver);
	if (result < 0) {
990
		acpi_unlock_battery_dir(acpi_battery_dir);
991
		return -ENODEV;
L
Linus Torvalds 已提交
992 993
	}

994
	return 0;
L
Linus Torvalds 已提交
995 996
}

L
Len Brown 已提交
997
static void __exit acpi_battery_exit(void)
L
Linus Torvalds 已提交
998 999 1000
{
	acpi_bus_unregister_driver(&acpi_battery_driver);

1001
	acpi_unlock_battery_dir(acpi_battery_dir);
L
Linus Torvalds 已提交
1002

1003
	return;
L
Linus Torvalds 已提交
1004 1005 1006 1007
}

module_init(acpi_battery_init);
module_exit(acpi_battery_exit);