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 51
/*
 *  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_HID		"PNP0C0A"
#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
52 53 54 55 56 57 58

#define ACPI_BATTERY_UPDATE_TIME	0

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

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

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

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

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

70 71 72
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 已提交
73 74
static int acpi_battery_add(struct acpi_device *device);
static int acpi_battery_remove(struct acpi_device *device, int type);
75
static int acpi_battery_resume(struct acpi_device *device);
L
Linus Torvalds 已提交
76 77

static struct acpi_driver acpi_battery_driver = {
L
Len Brown 已提交
78
	.name = "battery",
L
Len Brown 已提交
79 80 81 82
	.class = ACPI_BATTERY_CLASS,
	.ids = ACPI_BATTERY_HID,
	.ops = {
		.add = acpi_battery_add,
83
		.resume = acpi_battery_resume,
L
Len Brown 已提交
84 85
		.remove = acpi_battery_remove,
		},
L
Linus Torvalds 已提交
86 87
};

88
struct acpi_battery_state {
L
Len Brown 已提交
89 90 91 92
	acpi_integer state;
	acpi_integer present_rate;
	acpi_integer remaining_capacity;
	acpi_integer present_voltage;
L
Linus Torvalds 已提交
93 94 95
};

struct acpi_battery_info {
L
Len Brown 已提交
96 97 98 99 100 101 102 103 104 105 106 107 108
	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 已提交
109 110
};

111 112 113 114 115 116 117
enum acpi_battery_files{
	ACPI_BATTERY_INFO = 0,
	ACPI_BATTERY_STATE,
	ACPI_BATTERY_ALARM,
	ACPI_BATTERY_NUMFILES,
};

L
Linus Torvalds 已提交
118
struct acpi_battery_flags {
119 120 121
	u8 battery_present_prev;
	u8 alarm_present;
	u8 init_update;
122
	u8 update[ACPI_BATTERY_NUMFILES];
123
	u8 power_unit;
L
Linus Torvalds 已提交
124 125 126
};

struct acpi_battery {
127
	struct mutex mutex;
V
Vladimir Lebedev 已提交
128
	struct acpi_device *device;
L
Linus Torvalds 已提交
129
	struct acpi_battery_flags flags;
130 131
	struct acpi_buffer bif_data;
	struct acpi_buffer bst_data;
L
Len Brown 已提交
132
	unsigned long alarm;
133
	unsigned long update_time[ACPI_BATTERY_NUMFILES];
L
Linus Torvalds 已提交
134 135
};

136
inline int acpi_battery_present(struct acpi_battery *battery)
137
{
138 139 140 141 142 143 144 145
	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;
146 147
}

148
inline acpi_handle acpi_battery_handle(struct acpi_battery *battery)
149
{
150
	return battery->device->handle;
151 152
}

153 154 155 156
/* --------------------------------------------------------------------------
                               Battery Management
   -------------------------------------------------------------------------- */

157 158 159 160 161 162
static void acpi_battery_check_result(struct acpi_battery *battery, int result)
{
	if (!battery)
		return;

	if (result) {
163
		battery->flags.init_update = 1;
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183
	}
}

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) {
184
		kfree(data->pointer);
185 186 187 188 189 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
		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 已提交
216
{
L
Len Brown 已提交
217 218 219 220 221 222 223
	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;
224 225
	struct acpi_buffer *data = NULL;
	struct acpi_battery_info *bif = NULL;
L
Linus Torvalds 已提交
226

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

229 230
	if (!acpi_battery_present(battery))
		return 0;
L
Linus Torvalds 已提交
231

232
	/* Evaluate _BIF */
L
Linus Torvalds 已提交
233

V
Vladimir Lebedev 已提交
234 235 236
	status =
	    acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", NULL,
				 &buffer);
L
Linus Torvalds 已提交
237
	if (ACPI_FAILURE(status)) {
238
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF"));
239
		return -ENODEV;
L
Linus Torvalds 已提交
240 241
	}

242
	package = buffer.pointer;
L
Linus Torvalds 已提交
243

244 245
	data = &battery->bif_data;

L
Linus Torvalds 已提交
246 247
	/* Extract Package Data */

V
Vladimir Lebedev 已提交
248 249 250
	result =
	    acpi_battery_extract_package(battery, package, &format, data,
					 "_BIF");
251
	if (result)
L
Linus Torvalds 已提交
252 253
		goto end;

254
      end:
L
Linus Torvalds 已提交
255

256
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
257

258 259 260 261
	if (!result) {
		bif = data->pointer;
		battery->flags.power_unit = bif->power_unit;
	}
L
Linus Torvalds 已提交
262

263
	return result;
L
Linus Torvalds 已提交
264 265
}

266
static int acpi_battery_get_state(struct acpi_battery *battery)
L
Linus Torvalds 已提交
267
{
L
Len Brown 已提交
268 269 270 271 272 273 274
	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;
275
	struct acpi_buffer *data = NULL;
L
Linus Torvalds 已提交
276

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

279 280
	if (!acpi_battery_present(battery))
		return 0;
L
Linus Torvalds 已提交
281

282
	/* Evaluate _BST */
L
Linus Torvalds 已提交
283

V
Vladimir Lebedev 已提交
284 285 286
	status =
	    acpi_evaluate_object(acpi_battery_handle(battery), "_BST", NULL,
				 &buffer);
L
Linus Torvalds 已提交
287
	if (ACPI_FAILURE(status)) {
288
		ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
289
		return -ENODEV;
L
Linus Torvalds 已提交
290 291
	}

292
	package = buffer.pointer;
L
Linus Torvalds 已提交
293

294
	data = &battery->bst_data;
L
Linus Torvalds 已提交
295

296
	/* Extract Package Data */
L
Linus Torvalds 已提交
297

V
Vladimir Lebedev 已提交
298 299 300
	result =
	    acpi_battery_extract_package(battery, package, &format, data,
					 "_BST");
301
	if (result)
L
Linus Torvalds 已提交
302 303
		goto end;

304
      end:
305
	kfree(buffer.pointer);
L
Linus Torvalds 已提交
306

307 308
	return result;
}
L
Linus Torvalds 已提交
309

310 311
static int acpi_battery_get_alarm(struct acpi_battery *battery)
{
312
	battery->update_time[ACPI_BATTERY_ALARM] = get_seconds();
L
Linus Torvalds 已提交
313

314
	return 0;
L
Linus Torvalds 已提交
315 316
}

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

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

326 327
	if (!acpi_battery_present(battery))
		return -ENODEV;
L
Linus Torvalds 已提交
328

329
	if (!battery->flags.alarm_present)
330
		return -ENODEV;
L
Linus Torvalds 已提交
331 332 333

	arg0.integer.value = alarm;

V
Vladimir Lebedev 已提交
334 335 336
	status =
	    acpi_evaluate_object(acpi_battery_handle(battery), "_BTP",
				 &arg_list, NULL);
L
Linus Torvalds 已提交
337
	if (ACPI_FAILURE(status))
338
		return -ENODEV;
L
Linus Torvalds 已提交
339 340 341 342 343

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

	battery->alarm = alarm;

344
	return 0;
L
Linus Torvalds 已提交
345 346
}

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

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

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

370
      end:
L
Linus Torvalds 已提交
371

372 373
	return result;
}
L
Linus Torvalds 已提交
374

375 376 377
static int acpi_battery_init_update(struct acpi_battery *battery)
{
	int result = 0;
L
Linus Torvalds 已提交
378

379 380 381
	result = acpi_battery_get_status(battery);
	if (result)
		return result;
L
Linus Torvalds 已提交
382

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

385 386 387 388 389
	if (acpi_battery_present(battery)) {
		result = acpi_battery_get_info(battery);
		if (result)
			return result;
		result = acpi_battery_get_state(battery);
L
Linus Torvalds 已提交
390
		if (result)
391
			return result;
L
Linus Torvalds 已提交
392

393 394 395 396 397 398 399
		acpi_battery_init_alarm(battery);
	}

	return result;
}

static int acpi_battery_update(struct acpi_battery *battery,
V
Vladimir Lebedev 已提交
400
			       int update, int *update_result_ptr)
401 402 403
{
	int result = 0;
	int update_result = ACPI_BATTERY_NONE_UPDATE;
L
Linus Torvalds 已提交
404

405 406 407
	if (!acpi_battery_present(battery)) {
		update = 1;
	}
L
Linus Torvalds 已提交
408

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

429
      end:
L
Linus Torvalds 已提交
430

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

433
	*update_result_ptr = update_result;
L
Linus Torvalds 已提交
434

435
	return result;
L
Linus Torvalds 已提交
436 437
}

438
static void acpi_battery_notify_update(struct acpi_battery *battery)
439
{
440 441
	acpi_battery_get_status(battery);

442
	if (battery->flags.init_update) {
443 444 445
		return;
	}

446 447 448 449 450
	if ((!battery->flags.battery_present_prev &
	     acpi_battery_present(battery)) ||
	    (battery->flags.battery_present_prev &
	     !acpi_battery_present(battery))) {
		battery->flags.init_update = 1;
451
	} else {
452 453 454
		battery->flags.update[ACPI_BATTERY_INFO] = 1;
		battery->flags.update[ACPI_BATTERY_STATE] = 1;
		battery->flags.update[ACPI_BATTERY_ALARM] = 1;
455 456 457
	}
}

L
Linus Torvalds 已提交
458 459 460 461
/* --------------------------------------------------------------------------
                              FS Interface (/proc)
   -------------------------------------------------------------------------- */

L
Len Brown 已提交
462
static struct proc_dir_entry *acpi_battery_dir;
463

464
static int acpi_battery_print_info(struct seq_file *seq, int result)
L
Linus Torvalds 已提交
465
{
466
	struct acpi_battery *battery = seq->private;
L
Linus Torvalds 已提交
467
	struct acpi_battery_info *bif = NULL;
L
Len Brown 已提交
468
	char *units = "?";
L
Linus Torvalds 已提交
469

470
	if (result)
L
Linus Torvalds 已提交
471 472
		goto end;

473
	if (acpi_battery_present(battery))
L
Linus Torvalds 已提交
474 475 476 477 478 479
		seq_printf(seq, "present:                 yes\n");
	else {
		seq_printf(seq, "present:                 no\n");
		goto end;
	}

480 481 482 483
	bif = battery->bif_data.pointer;
	if (!bif) {
		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BIF buffer is NULL"));
		result = -ENODEV;
L
Linus Torvalds 已提交
484 485 486
		goto end;
	}

487 488 489
	/* Battery Units */

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

L
Linus Torvalds 已提交
491 492 493 494
	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 已提交
495
			   (u32) bif->design_capacity, units);
L
Linus Torvalds 已提交
496 497 498 499 500

	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 已提交
501
			   (u32) bif->last_full_capacity, units);
L
Linus Torvalds 已提交
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518

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

535 536 537 538 539 540
	if (result)
		seq_printf(seq, "ERROR: Unable to read battery info\n");

	return result;
}

541
static int acpi_battery_print_state(struct seq_file *seq, int result)
L
Linus Torvalds 已提交
542
{
543
	struct acpi_battery *battery = seq->private;
544
	struct acpi_battery_state *bst = NULL;
L
Len Brown 已提交
545
	char *units = "?";
L
Linus Torvalds 已提交
546

547
	if (result)
L
Linus Torvalds 已提交
548 549
		goto end;

550
	if (acpi_battery_present(battery))
L
Linus Torvalds 已提交
551 552 553 554 555 556
		seq_printf(seq, "present:                 yes\n");
	else {
		seq_printf(seq, "present:                 no\n");
		goto end;
	}

557 558 559 560
	bst = battery->bst_data.pointer;
	if (!bst) {
		ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BST buffer is NULL"));
		result = -ENODEV;
L
Linus Torvalds 已提交
561 562 563
		goto end;
	}

564 565 566 567
	/* Battery Units */

	units = acpi_battery_power_units(battery);

L
Linus Torvalds 已提交
568 569 570 571 572
	if (!(bst->state & 0x04))
		seq_printf(seq, "capacity state:          ok\n");
	else
		seq_printf(seq, "capacity state:          critical\n");

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

	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 已提交
594
			   (u32) bst->remaining_capacity, units);
L
Linus Torvalds 已提交
595 596 597 598 599

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

L
Len Brown 已提交
602
      end:
L
Linus Torvalds 已提交
603

604 605 606 607 608 609 610
	if (result) {
		seq_printf(seq, "ERROR: Unable to read battery state\n");
	}

	return result;
}

611
static int acpi_battery_print_alarm(struct seq_file *seq, int result)
L
Linus Torvalds 已提交
612
{
613
	struct acpi_battery *battery = seq->private;
L
Len Brown 已提交
614
	char *units = "?";
L
Linus Torvalds 已提交
615

616
	if (result)
L
Linus Torvalds 已提交
617 618
		goto end;

619
	if (!acpi_battery_present(battery)) {
L
Linus Torvalds 已提交
620 621 622 623 624
		seq_printf(seq, "present:                 no\n");
		goto end;
	}

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

626
	units = acpi_battery_power_units(battery);
L
Linus Torvalds 已提交
627 628 629 630 631

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

L
Len Brown 已提交
634
      end:
635 636 637 638 639 640 641

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

	return result;
}

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

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

656
	mutex_lock(&battery->mutex);
657

658 659 660 661 662
	result = acpi_battery_update(battery, 1, &update_result);
	if (result) {
		result = -ENODEV;
		goto end;
	}
L
Linus Torvalds 已提交
663

664 665 666 667 668 669 670 671 672
	if (!acpi_battery_present(battery)) {
		result = -ENODEV;
		goto end;
	}

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

L
Linus Torvalds 已提交
674 675
	alarm_string[count] = '\0';

L
Len Brown 已提交
676 677
	result = acpi_battery_set_alarm(battery,
					simple_strtoul(alarm_string, NULL, 0));
L
Linus Torvalds 已提交
678
	if (result)
679 680 681 682 683
		goto end;

      end:

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

685 686 687
	if (!result)
		result = count;

688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703
	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},
};
704

705 706 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;

	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);
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 896
	mutex_init(&battery->mutex);

897
	mutex_lock(&battery->mutex);
898

899
	battery->device = device;
L
Linus Torvalds 已提交
900 901 902 903
	strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
	strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
	acpi_driver_data(device) = battery;

904
	result = acpi_battery_get_status(battery);
L
Linus Torvalds 已提交
905 906 907
	if (result)
		goto end;

908
	battery->flags.init_update = 1;
909

L
Linus Torvalds 已提交
910 911 912 913
	result = acpi_battery_add_fs(device);
	if (result)
		goto end;

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

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

      end:
928

L
Linus Torvalds 已提交
929 930 931 932 933
	if (result) {
		acpi_battery_remove_fs(device);
		kfree(battery);
	}

934
	mutex_unlock(&battery->mutex);
935

936
	return result;
L
Linus Torvalds 已提交
937 938
}

L
Len Brown 已提交
939
static int acpi_battery_remove(struct acpi_device *device, int type)
L
Linus Torvalds 已提交
940
{
L
Len Brown 已提交
941 942
	acpi_status status = 0;
	struct acpi_battery *battery = NULL;
L
Linus Torvalds 已提交
943 944

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

947
	battery = acpi_driver_data(device);
L
Linus Torvalds 已提交
948

949
	mutex_lock(&battery->mutex);
950

951
	status = acpi_remove_notify_handler(device->handle,
952
					    ACPI_ALL_NOTIFY,
L
Len Brown 已提交
953
					    acpi_battery_notify);
L
Linus Torvalds 已提交
954 955 956

	acpi_battery_remove_fs(device);

957
	kfree(battery->bif_data.pointer);
958

959
	kfree(battery->bst_data.pointer);
960

961
	mutex_unlock(&battery->mutex);
962 963 964

	mutex_destroy(&battery->mutex);

L
Linus Torvalds 已提交
965 966
	kfree(battery);

967
	return 0;
L
Linus Torvalds 已提交
968 969
}

970
/* this is needed to learn about changes made in suspended state */
971
static int acpi_battery_resume(struct acpi_device *device)
972 973 974 975 976 977 978
{
	struct acpi_battery *battery;

	if (!device)
		return -EINVAL;

	battery = device->driver_data;
979

980
	battery->flags.init_update = 1;
981 982

	return 0;
983 984
}

L
Len Brown 已提交
985
static int __init acpi_battery_init(void)
L
Linus Torvalds 已提交
986
{
987
	int result;
L
Linus Torvalds 已提交
988

P
Pavel Machek 已提交
989 990 991
	if (acpi_disabled)
		return -ENODEV;

992
	acpi_battery_dir = acpi_lock_battery_dir();
L
Linus Torvalds 已提交
993
	if (!acpi_battery_dir)
994
		return -ENODEV;
L
Linus Torvalds 已提交
995 996 997

	result = acpi_bus_register_driver(&acpi_battery_driver);
	if (result < 0) {
998
		acpi_unlock_battery_dir(acpi_battery_dir);
999
		return -ENODEV;
L
Linus Torvalds 已提交
1000 1001
	}

1002
	return 0;
L
Linus Torvalds 已提交
1003 1004
}

L
Len Brown 已提交
1005
static void __exit acpi_battery_exit(void)
L
Linus Torvalds 已提交
1006 1007 1008
{
	acpi_bus_unregister_driver(&acpi_battery_driver);

1009
	acpi_unlock_battery_dir(acpi_battery_dir);
L
Linus Torvalds 已提交
1010

1011
	return;
L
Linus Torvalds 已提交
1012 1013 1014 1015
}

module_init(acpi_battery_init);
module_exit(acpi_battery_exit);