battery.c 24.1 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 117 118 119 120 121 122
enum acpi_battery_files{
	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 {
132
	struct mutex mutex;
V
Vladimir Lebedev 已提交
133
	struct acpi_device *device;
L
Linus Torvalds 已提交
134
	struct acpi_battery_flags flags;
135 136
	struct acpi_buffer bif_data;
	struct acpi_buffer bst_data;
L
Len Brown 已提交
137
	unsigned long alarm;
138
	unsigned long update_time[ACPI_BATTERY_NUMFILES];
L
Linus Torvalds 已提交
139 140
};

141
inline int acpi_battery_present(struct acpi_battery *battery)
142
{
143 144 145 146 147 148 149 150
	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;
151 152
}

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

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

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

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

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) {
189
		kfree(data->pointer);
190 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
		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 已提交
221
{
L
Len Brown 已提交
222 223 224 225 226 227 228
	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;
229 230
	struct acpi_buffer *data = NULL;
	struct acpi_battery_info *bif = NULL;
L
Linus Torvalds 已提交
231

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

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

237
	/* Evaluate _BIF */
L
Linus Torvalds 已提交
238

V
Vladimir Lebedev 已提交
239 240 241
	status =
	    acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", NULL,
				 &buffer);
L
Linus Torvalds 已提交
242
	if (ACPI_FAILURE(status)) {
243
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF"));
244
		return -ENODEV;
L
Linus Torvalds 已提交
245 246
	}

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

249 250
	data = &battery->bif_data;

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

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

259
      end:
L
Linus Torvalds 已提交
260

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

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

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

271
static int acpi_battery_get_state(struct acpi_battery *battery)
L
Linus Torvalds 已提交
272
{
L
Len Brown 已提交
273 274 275 276 277 278 279
	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;
280
	struct acpi_buffer *data = NULL;
L
Linus Torvalds 已提交
281

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

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

287
	/* Evaluate _BST */
L
Linus Torvalds 已提交
288

V
Vladimir Lebedev 已提交
289 290 291
	status =
	    acpi_evaluate_object(acpi_battery_handle(battery), "_BST", NULL,
				 &buffer);
L
Linus Torvalds 已提交
292
	if (ACPI_FAILURE(status)) {
293
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
294
		return -ENODEV;
L
Linus Torvalds 已提交
295 296
	}

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

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

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

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

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

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

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

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

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

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

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

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

	arg0.integer.value = alarm;

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

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

	battery->alarm = alarm;

349
	return 0;
L
Linus Torvalds 已提交
350 351
}

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

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

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

375
      end:
L
Linus Torvalds 已提交
376

377 378
	return result;
}
L
Linus Torvalds 已提交
379

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

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

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

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

398 399 400 401 402 403 404
		acpi_battery_init_alarm(battery);
	}

	return result;
}

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

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

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

434
      end:
L
Linus Torvalds 已提交
435

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

438
	*update_result_ptr = update_result;
L
Linus Torvalds 已提交
439

440
	return result;
L
Linus Torvalds 已提交
441 442
}

443
static void acpi_battery_notify_update(struct acpi_battery *battery)
444
{
445 446
	acpi_battery_get_status(battery);

447
	if (battery->flags.init_update) {
448 449 450
		return;
	}

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

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

L
Len Brown 已提交
467
static struct proc_dir_entry *acpi_battery_dir;
468

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

475
	if (result)
L
Linus Torvalds 已提交
476 477
		goto end;

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

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

492 493 494
	/* Battery Units */

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

L
Linus Torvalds 已提交
496 497 498 499
	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 已提交
500
			   (u32) bif->design_capacity, units);
L
Linus Torvalds 已提交
501 502 503 504 505

	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 已提交
506
			   (u32) bif->last_full_capacity, units);
L
Linus Torvalds 已提交
507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523

	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 已提交
524
			   (u32) bif->design_voltage);
L
Linus Torvalds 已提交
525
	seq_printf(seq, "design capacity warning: %d %sh\n",
L
Len Brown 已提交
526
		   (u32) bif->design_capacity_warning, units);
L
Linus Torvalds 已提交
527
	seq_printf(seq, "design capacity low:     %d %sh\n",
L
Len Brown 已提交
528
		   (u32) bif->design_capacity_low, units);
L
Linus Torvalds 已提交
529
	seq_printf(seq, "capacity granularity 1:  %d %sh\n",
L
Len Brown 已提交
530
		   (u32) bif->battery_capacity_granularity_1, units);
L
Linus Torvalds 已提交
531
	seq_printf(seq, "capacity granularity 2:  %d %sh\n",
L
Len Brown 已提交
532 533 534 535 536 537 538
		   (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 已提交
539

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

	return result;
}

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

552
	if (result)
L
Linus Torvalds 已提交
553 554
		goto end;

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

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

569 570 571 572
	/* Battery Units */

	units = acpi_battery_power_units(battery);

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

L
Len Brown 已提交
578 579 580 581
	if ((bst->state & 0x01) && (bst->state & 0x02)) {
		seq_printf(seq,
			   "charging state:          charging/discharging\n");
	} else if (bst->state & 0x01)
L
Linus Torvalds 已提交
582 583 584 585 586 587 588 589 590 591 592
		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 已提交
593
			   (u32) bst->present_rate, units);
L
Linus Torvalds 已提交
594 595 596 597 598

	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 已提交
599
			   (u32) bst->remaining_capacity, units);
L
Linus Torvalds 已提交
600 601 602 603 604

	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 已提交
605
			   (u32) bst->present_voltage);
L
Linus Torvalds 已提交
606

L
Len Brown 已提交
607
      end:
L
Linus Torvalds 已提交
608

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

	return result;
}

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

621
	if (result)
L
Linus Torvalds 已提交
622 623
		goto end;

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

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

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

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

L
Len Brown 已提交
639
      end:
640 641 642 643 644 645 646

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

	return result;
}

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

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

661
	mutex_lock(&battery->mutex);
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 691 692
	if (!result)
		result = count;

693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708
	mutex_unlock(&battery->mutex);

	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},
};
709

710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736
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;

	mutex_lock(&battery->mutex);

	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;
	mutex_unlock(&battery->mutex);
737
	return result;
L
Linus Torvalds 已提交
738 739
}

740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
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 已提交
765 766 767 768 769
static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
{
	return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
}

770 771 772 773 774 775 776 777 778
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 已提交
779 780 781 782
	.open = acpi_battery_info_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
783
	.owner = THIS_MODULE,
784 785 786 787 788 789
	},
	},
	{
	.name = "state",
	.mode = S_IRUGO,
	.ops = {
L
Len Brown 已提交
790 791 792 793
	.open = acpi_battery_state_open_fs,
	.read = seq_read,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
794
	.owner = THIS_MODULE,
795 796 797 798 799 800
	},
	},
	{
	.name = "alarm",
	.mode = S_IFREG | S_IRUGO | S_IWUSR,
	.ops = {
L
Len Brown 已提交
801 802 803 804 805
	.open = acpi_battery_alarm_open_fs,
	.read = seq_read,
	.write = acpi_battery_write_alarm,
	.llseek = seq_lseek,
	.release = single_release,
L
Linus Torvalds 已提交
806
	.owner = THIS_MODULE,
807 808
	},
	},
L
Linus Torvalds 已提交
809 810
};

L
Len Brown 已提交
811
static int acpi_battery_add_fs(struct acpi_device *device)
L
Linus Torvalds 已提交
812
{
L
Len Brown 已提交
813
	struct proc_dir_entry *entry = NULL;
814
	int i;
L
Linus Torvalds 已提交
815 816 817

	if (!acpi_device_dir(device)) {
		acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
L
Len Brown 已提交
818
						     acpi_battery_dir);
L
Linus Torvalds 已提交
819
		if (!acpi_device_dir(device))
820
			return -ENODEV;
L
Linus Torvalds 已提交
821 822 823
		acpi_device_dir(device)->owner = THIS_MODULE;
	}

824 825 826 827 828 829 830 831 832 833
	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 已提交
834 835
	}

836
	return 0;
L
Linus Torvalds 已提交
837 838
}

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

851
	return 0;
L
Linus Torvalds 已提交
852 853 854 855 856 857
}

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

L
Len Brown 已提交
858
static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
L
Linus Torvalds 已提交
859
{
860
	struct acpi_battery *battery = data;
L
Len Brown 已提交
861
	struct acpi_device *device = NULL;
L
Linus Torvalds 已提交
862 863

	if (!battery)
864
		return;
L
Linus Torvalds 已提交
865

866
	device = battery->device;
L
Linus Torvalds 已提交
867 868 869 870

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

884
	return;
L
Linus Torvalds 已提交
885 886
}

L
Len Brown 已提交
887
static int acpi_battery_add(struct acpi_device *device)
L
Linus Torvalds 已提交
888
{
L
Len Brown 已提交
889 890 891
	int result = 0;
	acpi_status status = 0;
	struct acpi_battery *battery = NULL;
L
Linus Torvalds 已提交
892 893

	if (!device)
894
		return -EINVAL;
L
Linus Torvalds 已提交
895

896
	battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
L
Linus Torvalds 已提交
897
	if (!battery)
898
		return -ENOMEM;
L
Linus Torvalds 已提交
899

900 901
	mutex_init(&battery->mutex);

902
	mutex_lock(&battery->mutex);
903

904
	battery->device = device;
L
Linus Torvalds 已提交
905 906 907 908
	strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
	acpi_driver_data(device) = battery;

909
	result = acpi_battery_get_status(battery);
L
Linus Torvalds 已提交
910 911 912
	if (result)
		goto end;

913
	battery->flags.init_update = 1;
914

L
Linus Torvalds 已提交
915 916 917 918
	result = acpi_battery_add_fs(device);
	if (result)
		goto end;

919
	status = acpi_install_notify_handler(device->handle,
920
					     ACPI_ALL_NOTIFY,
L
Len Brown 已提交
921
					     acpi_battery_notify, battery);
L
Linus Torvalds 已提交
922
	if (ACPI_FAILURE(status)) {
923
		ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler"));
L
Linus Torvalds 已提交
924 925 926 927 928
		result = -ENODEV;
		goto end;
	}

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

      end:
933

L
Linus Torvalds 已提交
934 935 936 937 938
	if (result) {
		acpi_battery_remove_fs(device);
		kfree(battery);
	}

939
	mutex_unlock(&battery->mutex);
940

941
	return result;
L
Linus Torvalds 已提交
942 943
}

L
Len Brown 已提交
944
static int acpi_battery_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
945
{
L
Len Brown 已提交
946 947
	acpi_status status = 0;
	struct acpi_battery *battery = NULL;
L
Linus Torvalds 已提交
948 949

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

952
	battery = acpi_driver_data(device);
L
Linus Torvalds 已提交
953

954
	mutex_lock(&battery->mutex);
955

956
	status = acpi_remove_notify_handler(device->handle,
957
					    ACPI_ALL_NOTIFY,
L
Len Brown 已提交
958
					    acpi_battery_notify);
L
Linus Torvalds 已提交
959 960 961

	acpi_battery_remove_fs(device);

962
	kfree(battery->bif_data.pointer);
963

964
	kfree(battery->bst_data.pointer);
965

966
	mutex_unlock(&battery->mutex);
967 968 969

	mutex_destroy(&battery->mutex);

L
Linus Torvalds 已提交
970 971
	kfree(battery);

972
	return 0;
L
Linus Torvalds 已提交
973 974
}

975
/* this is needed to learn about changes made in suspended state */
976
static int acpi_battery_resume(struct acpi_device *device)
977 978 979 980 981 982 983
{
	struct acpi_battery *battery;

	if (!device)
		return -EINVAL;

	battery = device->driver_data;
984

985
	battery->flags.init_update = 1;
986 987

	return 0;
988 989
}

L
Len Brown 已提交
990
static int __init acpi_battery_init(void)
L
Linus Torvalds 已提交
991
{
992
	int result;
L
Linus Torvalds 已提交
993

P
Pavel Machek 已提交
994 995 996
	if (acpi_disabled)
		return -ENODEV;

997
	acpi_battery_dir = acpi_lock_battery_dir();
L
Linus Torvalds 已提交
998
	if (!acpi_battery_dir)
999
		return -ENODEV;
L
Linus Torvalds 已提交
1000 1001 1002

	result = acpi_bus_register_driver(&acpi_battery_driver);
	if (result < 0) {
1003
		acpi_unlock_battery_dir(acpi_battery_dir);
1004
		return -ENODEV;
L
Linus Torvalds 已提交
1005 1006
	}

1007
	return 0;
L
Linus Torvalds 已提交
1008 1009
}

L
Len Brown 已提交
1010
static void __exit acpi_battery_exit(void)
L
Linus Torvalds 已提交
1011 1012 1013
{
	acpi_bus_unregister_driver(&acpi_battery_driver);

1014
	acpi_unlock_battery_dir(acpi_battery_dir);
L
Linus Torvalds 已提交
1015

1016
	return;
L
Linus Torvalds 已提交
1017 1018 1019 1020
}

module_init(acpi_battery_init);
module_exit(acpi_battery_exit);