blob: ba34ca611d71c33058732c7237ad98e68e9091a4 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * acpi_battery.c - ACPI Battery Driver ($Revision: 37 $)
3 *
4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
6 *
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or (at
12 * your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22 *
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24 */
25
26#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/types.h>
30#include <linux/proc_fs.h>
31#include <linux/seq_file.h>
32#include <asm/uaccess.h>
33
34#include <acpi/acpi_bus.h>
35#include <acpi/acpi_drivers.h>
36
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF
38
39#define ACPI_BATTERY_FORMAT_BIF "NNNNNNNNNSSSS"
40#define ACPI_BATTERY_FORMAT_BST "NNNN"
41
42#define ACPI_BATTERY_COMPONENT 0x00040000
43#define ACPI_BATTERY_CLASS "battery"
44#define ACPI_BATTERY_HID "PNP0C0A"
45#define ACPI_BATTERY_DRIVER_NAME "ACPI Battery Driver"
46#define ACPI_BATTERY_DEVICE_NAME "Battery"
47#define ACPI_BATTERY_FILE_INFO "info"
48#define ACPI_BATTERY_FILE_STATUS "state"
49#define ACPI_BATTERY_FILE_ALARM "alarm"
50#define ACPI_BATTERY_NOTIFY_STATUS 0x80
51#define ACPI_BATTERY_NOTIFY_INFO 0x81
52#define ACPI_BATTERY_UNITS_WATTS "mW"
53#define ACPI_BATTERY_UNITS_AMPS "mA"
54
Linus Torvalds1da177e2005-04-16 15:20:36 -070055#define _COMPONENT ACPI_BATTERY_COMPONENT
Len Brown4be44fc2005-08-05 00:44:28 -040056ACPI_MODULE_NAME("acpi_battery")
Linus Torvalds1da177e2005-04-16 15:20:36 -070057
Len Brown4be44fc2005-08-05 00:44:28 -040058 MODULE_AUTHOR("Paul Diefenbaugh");
Linus Torvalds1da177e2005-04-16 15:20:36 -070059MODULE_DESCRIPTION(ACPI_BATTERY_DRIVER_NAME);
60MODULE_LICENSE("GPL");
61
Len Brown4be44fc2005-08-05 00:44:28 -040062static int acpi_battery_add(struct acpi_device *device);
63static int acpi_battery_remove(struct acpi_device *device, int type);
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
65static struct acpi_driver acpi_battery_driver = {
Len Brown4be44fc2005-08-05 00:44:28 -040066 .name = ACPI_BATTERY_DRIVER_NAME,
67 .class = ACPI_BATTERY_CLASS,
68 .ids = ACPI_BATTERY_HID,
69 .ops = {
70 .add = acpi_battery_add,
71 .remove = acpi_battery_remove,
72 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070073};
74
75struct acpi_battery_status {
Len Brown4be44fc2005-08-05 00:44:28 -040076 acpi_integer state;
77 acpi_integer present_rate;
78 acpi_integer remaining_capacity;
79 acpi_integer present_voltage;
Linus Torvalds1da177e2005-04-16 15:20:36 -070080};
81
82struct acpi_battery_info {
Len Brown4be44fc2005-08-05 00:44:28 -040083 acpi_integer power_unit;
84 acpi_integer design_capacity;
85 acpi_integer last_full_capacity;
86 acpi_integer battery_technology;
87 acpi_integer design_voltage;
88 acpi_integer design_capacity_warning;
89 acpi_integer design_capacity_low;
90 acpi_integer battery_capacity_granularity_1;
91 acpi_integer battery_capacity_granularity_2;
92 acpi_string model_number;
93 acpi_string serial_number;
94 acpi_string battery_type;
95 acpi_string oem_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096};
97
98struct acpi_battery_flags {
Len Brown4be44fc2005-08-05 00:44:28 -040099 u8 present:1; /* Bay occupied? */
100 u8 power_unit:1; /* 0=watts, 1=apms */
101 u8 alarm:1; /* _BTP present? */
102 u8 reserved:5;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103};
104
105struct acpi_battery_trips {
Len Brown4be44fc2005-08-05 00:44:28 -0400106 unsigned long warning;
107 unsigned long low;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108};
109
110struct acpi_battery {
Len Brown4be44fc2005-08-05 00:44:28 -0400111 acpi_handle handle;
Patrick Mochel145def82006-05-19 16:54:39 -0400112 struct acpi_device * device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 struct acpi_battery_flags flags;
114 struct acpi_battery_trips trips;
Len Brown4be44fc2005-08-05 00:44:28 -0400115 unsigned long alarm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116 struct acpi_battery_info *info;
117};
118
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119/* --------------------------------------------------------------------------
120 Battery Management
121 -------------------------------------------------------------------------- */
122
123static int
Len Brown4be44fc2005-08-05 00:44:28 -0400124acpi_battery_get_info(struct acpi_battery *battery,
125 struct acpi_battery_info **bif)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126{
Len Brown4be44fc2005-08-05 00:44:28 -0400127 int result = 0;
128 acpi_status status = 0;
129 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
130 struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BIF),
131 ACPI_BATTERY_FORMAT_BIF
132 };
133 struct acpi_buffer data = { 0, NULL };
134 union acpi_object *package = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136
137 if (!battery || !bif)
Patrick Mocheld550d982006-06-27 00:41:40 -0400138 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700139
140 /* Evalute _BIF */
141
142 status = acpi_evaluate_object(battery->handle, "_BIF", NULL, &buffer);
143 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400144 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400145 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146 }
147
Len Brown4be44fc2005-08-05 00:44:28 -0400148 package = (union acpi_object *)buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149
150 /* Extract Package Data */
151
152 status = acpi_extract_package(package, &format, &data);
153 if (status != AE_BUFFER_OVERFLOW) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400154 ACPI_EXCEPTION((AE_INFO, status, "Extracting _BIF"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 result = -ENODEV;
156 goto end;
157 }
158
159 data.pointer = kmalloc(data.length, GFP_KERNEL);
160 if (!data.pointer) {
161 result = -ENOMEM;
162 goto end;
163 }
164 memset(data.pointer, 0, data.length);
165
166 status = acpi_extract_package(package, &format, &data);
167 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400168 ACPI_EXCEPTION((AE_INFO, status, "Extracting _BIF"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700169 kfree(data.pointer);
170 result = -ENODEV;
171 goto end;
172 }
173
Len Brown4be44fc2005-08-05 00:44:28 -0400174 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 acpi_os_free(buffer.pointer);
176
177 if (!result)
Len Brown4be44fc2005-08-05 00:44:28 -0400178 (*bif) = (struct acpi_battery_info *)data.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700179
Patrick Mocheld550d982006-06-27 00:41:40 -0400180 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181}
182
183static int
Len Brown4be44fc2005-08-05 00:44:28 -0400184acpi_battery_get_status(struct acpi_battery *battery,
185 struct acpi_battery_status **bst)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186{
Len Brown4be44fc2005-08-05 00:44:28 -0400187 int result = 0;
188 acpi_status status = 0;
189 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
190 struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BST),
191 ACPI_BATTERY_FORMAT_BST
192 };
193 struct acpi_buffer data = { 0, NULL };
194 union acpi_object *package = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700195
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196
197 if (!battery || !bst)
Patrick Mocheld550d982006-06-27 00:41:40 -0400198 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199
200 /* Evalute _BST */
201
202 status = acpi_evaluate_object(battery->handle, "_BST", NULL, &buffer);
203 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400204 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400205 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700206 }
207
Len Brown4be44fc2005-08-05 00:44:28 -0400208 package = (union acpi_object *)buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700209
210 /* Extract Package Data */
211
212 status = acpi_extract_package(package, &format, &data);
213 if (status != AE_BUFFER_OVERFLOW) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400214 ACPI_EXCEPTION((AE_INFO, status, "Extracting _BST"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700215 result = -ENODEV;
216 goto end;
217 }
218
219 data.pointer = kmalloc(data.length, GFP_KERNEL);
220 if (!data.pointer) {
221 result = -ENOMEM;
222 goto end;
223 }
224 memset(data.pointer, 0, data.length);
225
226 status = acpi_extract_package(package, &format, &data);
227 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400228 ACPI_EXCEPTION((AE_INFO, status, "Extracting _BST"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700229 kfree(data.pointer);
230 result = -ENODEV;
231 goto end;
232 }
233
Len Brown4be44fc2005-08-05 00:44:28 -0400234 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700235 acpi_os_free(buffer.pointer);
236
237 if (!result)
Len Brown4be44fc2005-08-05 00:44:28 -0400238 (*bst) = (struct acpi_battery_status *)data.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700239
Patrick Mocheld550d982006-06-27 00:41:40 -0400240 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241}
242
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243static int
Len Brown4be44fc2005-08-05 00:44:28 -0400244acpi_battery_set_alarm(struct acpi_battery *battery, unsigned long alarm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245{
Len Brown4be44fc2005-08-05 00:44:28 -0400246 acpi_status status = 0;
247 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
248 struct acpi_object_list arg_list = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250
251 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400252 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
254 if (!battery->flags.alarm)
Patrick Mocheld550d982006-06-27 00:41:40 -0400255 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256
257 arg0.integer.value = alarm;
258
259 status = acpi_evaluate_object(battery->handle, "_BTP", &arg_list, NULL);
260 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400261 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
263 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm));
264
265 battery->alarm = alarm;
266
Patrick Mocheld550d982006-06-27 00:41:40 -0400267 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700268}
269
Len Brown4be44fc2005-08-05 00:44:28 -0400270static int acpi_battery_check(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700271{
Len Brown4be44fc2005-08-05 00:44:28 -0400272 int result = 0;
273 acpi_status status = AE_OK;
274 acpi_handle handle = NULL;
275 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276 struct acpi_battery_info *bif = NULL;
277
Len Brown4be44fc2005-08-05 00:44:28 -0400278
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400280 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
Patrick Mochel145def82006-05-19 16:54:39 -0400282 device = battery->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
284 result = acpi_bus_get_status(device);
285 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400286 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287
288 /* Insertion? */
289
290 if (!battery->flags.present && device->status.battery_present) {
291
292 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery inserted\n"));
293
294 /* Evalute _BIF to get certain static information */
295
296 result = acpi_battery_get_info(battery, &bif);
297 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400298 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299
300 battery->flags.power_unit = bif->power_unit;
301 battery->trips.warning = bif->design_capacity_warning;
302 battery->trips.low = bif->design_capacity_low;
303 kfree(bif);
304
305 /* See if alarms are supported, and if so, set default */
306
307 status = acpi_get_handle(battery->handle, "_BTP", &handle);
308 if (ACPI_SUCCESS(status)) {
309 battery->flags.alarm = 1;
310 acpi_battery_set_alarm(battery, battery->trips.warning);
311 }
312 }
313
314 /* Removal? */
315
316 else if (battery->flags.present && !device->status.battery_present) {
317 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery removed\n"));
318 }
319
320 battery->flags.present = device->status.battery_present;
321
Patrick Mocheld550d982006-06-27 00:41:40 -0400322 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323}
324
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325/* --------------------------------------------------------------------------
326 FS Interface (/proc)
327 -------------------------------------------------------------------------- */
328
Len Brown4be44fc2005-08-05 00:44:28 -0400329static struct proc_dir_entry *acpi_battery_dir;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330static int acpi_battery_read_info(struct seq_file *seq, void *offset)
331{
Len Brown4be44fc2005-08-05 00:44:28 -0400332 int result = 0;
333 struct acpi_battery *battery = (struct acpi_battery *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700334 struct acpi_battery_info *bif = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400335 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
338 if (!battery)
339 goto end;
340
341 if (battery->flags.present)
342 seq_printf(seq, "present: yes\n");
343 else {
344 seq_printf(seq, "present: no\n");
345 goto end;
346 }
347
348 /* Battery Info (_BIF) */
349
350 result = acpi_battery_get_info(battery, &bif);
351 if (result || !bif) {
352 seq_printf(seq, "ERROR: Unable to read battery information\n");
353 goto end;
354 }
355
Len Brown4be44fc2005-08-05 00:44:28 -0400356 units =
357 bif->
358 power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS;
359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
361 seq_printf(seq, "design capacity: unknown\n");
362 else
363 seq_printf(seq, "design capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400364 (u32) bif->design_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365
366 if (bif->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
367 seq_printf(seq, "last full capacity: unknown\n");
368 else
369 seq_printf(seq, "last full capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400370 (u32) bif->last_full_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
372 switch ((u32) bif->battery_technology) {
373 case 0:
374 seq_printf(seq, "battery technology: non-rechargeable\n");
375 break;
376 case 1:
377 seq_printf(seq, "battery technology: rechargeable\n");
378 break;
379 default:
380 seq_printf(seq, "battery technology: unknown\n");
381 break;
382 }
383
384 if (bif->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
385 seq_printf(seq, "design voltage: unknown\n");
386 else
387 seq_printf(seq, "design voltage: %d mV\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400388 (u32) bif->design_voltage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700389
Len Brown4be44fc2005-08-05 00:44:28 -0400390 seq_printf(seq, "design capacity warning: %d %sh\n",
391 (u32) bif->design_capacity_warning, units);
392 seq_printf(seq, "design capacity low: %d %sh\n",
393 (u32) bif->design_capacity_low, units);
394 seq_printf(seq, "capacity granularity 1: %d %sh\n",
395 (u32) bif->battery_capacity_granularity_1, units);
396 seq_printf(seq, "capacity granularity 2: %d %sh\n",
397 (u32) bif->battery_capacity_granularity_2, units);
398 seq_printf(seq, "model number: %s\n", bif->model_number);
399 seq_printf(seq, "serial number: %s\n", bif->serial_number);
400 seq_printf(seq, "battery type: %s\n", bif->battery_type);
401 seq_printf(seq, "OEM info: %s\n", bif->oem_info);
402
403 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 kfree(bif);
405
Patrick Mocheld550d982006-06-27 00:41:40 -0400406 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407}
408
409static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
410{
411 return single_open(file, acpi_battery_read_info, PDE(inode)->data);
412}
413
Len Brown4be44fc2005-08-05 00:44:28 -0400414static int acpi_battery_read_state(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415{
Len Brown4be44fc2005-08-05 00:44:28 -0400416 int result = 0;
417 struct acpi_battery *battery = (struct acpi_battery *)seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 struct acpi_battery_status *bst = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400419 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700420
Linus Torvalds1da177e2005-04-16 15:20:36 -0700421
422 if (!battery)
423 goto end;
424
425 if (battery->flags.present)
426 seq_printf(seq, "present: yes\n");
427 else {
428 seq_printf(seq, "present: no\n");
429 goto end;
430 }
431
432 /* Battery Units */
433
Len Brown4be44fc2005-08-05 00:44:28 -0400434 units =
435 battery->flags.
436 power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437
438 /* Battery Status (_BST) */
439
440 result = acpi_battery_get_status(battery, &bst);
441 if (result || !bst) {
442 seq_printf(seq, "ERROR: Unable to read battery status\n");
443 goto end;
444 }
445
446 if (!(bst->state & 0x04))
447 seq_printf(seq, "capacity state: ok\n");
448 else
449 seq_printf(seq, "capacity state: critical\n");
450
Len Brown4be44fc2005-08-05 00:44:28 -0400451 if ((bst->state & 0x01) && (bst->state & 0x02)) {
452 seq_printf(seq,
453 "charging state: charging/discharging\n");
Len Brown4be44fc2005-08-05 00:44:28 -0400454 } else if (bst->state & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455 seq_printf(seq, "charging state: discharging\n");
456 else if (bst->state & 0x02)
457 seq_printf(seq, "charging state: charging\n");
458 else {
459 seq_printf(seq, "charging state: charged\n");
460 }
461
462 if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN)
463 seq_printf(seq, "present rate: unknown\n");
464 else
465 seq_printf(seq, "present rate: %d %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400466 (u32) bst->present_rate, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467
468 if (bst->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
469 seq_printf(seq, "remaining capacity: unknown\n");
470 else
471 seq_printf(seq, "remaining capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400472 (u32) bst->remaining_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473
474 if (bst->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
475 seq_printf(seq, "present voltage: unknown\n");
476 else
477 seq_printf(seq, "present voltage: %d mV\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400478 (u32) bst->present_voltage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700479
Len Brown4be44fc2005-08-05 00:44:28 -0400480 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700481 kfree(bst);
482
Patrick Mocheld550d982006-06-27 00:41:40 -0400483 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484}
485
486static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
487{
488 return single_open(file, acpi_battery_read_state, PDE(inode)->data);
489}
490
Len Brown4be44fc2005-08-05 00:44:28 -0400491static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700492{
Len Brown4be44fc2005-08-05 00:44:28 -0400493 struct acpi_battery *battery = (struct acpi_battery *)seq->private;
494 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
497 if (!battery)
498 goto end;
499
500 if (!battery->flags.present) {
501 seq_printf(seq, "present: no\n");
502 goto end;
503 }
504
505 /* Battery Units */
Len Brown4be44fc2005-08-05 00:44:28 -0400506
507 units =
508 battery->flags.
509 power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700510
511 /* Battery Alarm */
512
513 seq_printf(seq, "alarm: ");
514 if (!battery->alarm)
515 seq_printf(seq, "unsupported\n");
516 else
517 seq_printf(seq, "%d %sh\n", (u32) battery->alarm, units);
518
Len Brown4be44fc2005-08-05 00:44:28 -0400519 end:
Patrick Mocheld550d982006-06-27 00:41:40 -0400520 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521}
522
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400524acpi_battery_write_alarm(struct file *file,
525 const char __user * buffer,
526 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527{
Len Brown4be44fc2005-08-05 00:44:28 -0400528 int result = 0;
529 char alarm_string[12] = { '\0' };
530 struct seq_file *m = (struct seq_file *)file->private_data;
531 struct acpi_battery *battery = (struct acpi_battery *)m->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700532
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
534 if (!battery || (count > sizeof(alarm_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -0400535 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700536
537 if (!battery->flags.present)
Patrick Mocheld550d982006-06-27 00:41:40 -0400538 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539
540 if (copy_from_user(alarm_string, buffer, count))
Patrick Mocheld550d982006-06-27 00:41:40 -0400541 return -EFAULT;
Len Brown4be44fc2005-08-05 00:44:28 -0400542
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543 alarm_string[count] = '\0';
544
Len Brown4be44fc2005-08-05 00:44:28 -0400545 result = acpi_battery_set_alarm(battery,
546 simple_strtoul(alarm_string, NULL, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400548 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Patrick Mocheld550d982006-06-27 00:41:40 -0400550 return count;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551}
552
553static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
554{
555 return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
556}
557
558static struct file_operations acpi_battery_info_ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400559 .open = acpi_battery_info_open_fs,
560 .read = seq_read,
561 .llseek = seq_lseek,
562 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563 .owner = THIS_MODULE,
564};
565
566static struct file_operations acpi_battery_state_ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400567 .open = acpi_battery_state_open_fs,
568 .read = seq_read,
569 .llseek = seq_lseek,
570 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700571 .owner = THIS_MODULE,
572};
573
574static struct file_operations acpi_battery_alarm_ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400575 .open = acpi_battery_alarm_open_fs,
576 .read = seq_read,
577 .write = acpi_battery_write_alarm,
578 .llseek = seq_lseek,
579 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700580 .owner = THIS_MODULE,
581};
582
Len Brown4be44fc2005-08-05 00:44:28 -0400583static int acpi_battery_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584{
Len Brown4be44fc2005-08-05 00:44:28 -0400585 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
588 if (!acpi_device_dir(device)) {
589 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -0400590 acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400592 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 acpi_device_dir(device)->owner = THIS_MODULE;
594 }
595
596 /* 'info' [R] */
597 entry = create_proc_entry(ACPI_BATTERY_FILE_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400598 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400600 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 else {
Len Brown4be44fc2005-08-05 00:44:28 -0400602 entry->proc_fops = &acpi_battery_info_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 entry->data = acpi_driver_data(device);
604 entry->owner = THIS_MODULE;
605 }
606
607 /* 'status' [R] */
608 entry = create_proc_entry(ACPI_BATTERY_FILE_STATUS,
Len Brown4be44fc2005-08-05 00:44:28 -0400609 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400611 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612 else {
613 entry->proc_fops = &acpi_battery_state_ops;
614 entry->data = acpi_driver_data(device);
615 entry->owner = THIS_MODULE;
616 }
617
618 /* 'alarm' [R/W] */
619 entry = create_proc_entry(ACPI_BATTERY_FILE_ALARM,
Len Brown4be44fc2005-08-05 00:44:28 -0400620 S_IFREG | S_IRUGO | S_IWUSR,
621 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700622 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400623 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 else {
625 entry->proc_fops = &acpi_battery_alarm_ops;
626 entry->data = acpi_driver_data(device);
627 entry->owner = THIS_MODULE;
628 }
629
Patrick Mocheld550d982006-06-27 00:41:40 -0400630 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700631}
632
Len Brown4be44fc2005-08-05 00:44:28 -0400633static int acpi_battery_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700635
636 if (acpi_device_dir(device)) {
637 remove_proc_entry(ACPI_BATTERY_FILE_ALARM,
638 acpi_device_dir(device));
639 remove_proc_entry(ACPI_BATTERY_FILE_STATUS,
640 acpi_device_dir(device));
641 remove_proc_entry(ACPI_BATTERY_FILE_INFO,
642 acpi_device_dir(device));
643
644 remove_proc_entry(acpi_device_bid(device), acpi_battery_dir);
645 acpi_device_dir(device) = NULL;
646 }
647
Patrick Mocheld550d982006-06-27 00:41:40 -0400648 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649}
650
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651/* --------------------------------------------------------------------------
652 Driver Interface
653 -------------------------------------------------------------------------- */
654
Len Brown4be44fc2005-08-05 00:44:28 -0400655static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656{
Len Brown4be44fc2005-08-05 00:44:28 -0400657 struct acpi_battery *battery = (struct acpi_battery *)data;
658 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659
Linus Torvalds1da177e2005-04-16 15:20:36 -0700660
661 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400662 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Patrick Mochel145def82006-05-19 16:54:39 -0400664 device = battery->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700665
666 switch (event) {
667 case ACPI_BATTERY_NOTIFY_STATUS:
668 case ACPI_BATTERY_NOTIFY_INFO:
669 acpi_battery_check(battery);
670 acpi_bus_generate_event(device, event, battery->flags.present);
671 break;
672 default:
673 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400674 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 break;
676 }
677
Patrick Mocheld550d982006-06-27 00:41:40 -0400678 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679}
680
Len Brown4be44fc2005-08-05 00:44:28 -0400681static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682{
Len Brown4be44fc2005-08-05 00:44:28 -0400683 int result = 0;
684 acpi_status status = 0;
685 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700686
Len Brown4be44fc2005-08-05 00:44:28 -0400687
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400689 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
691 battery = kmalloc(sizeof(struct acpi_battery), GFP_KERNEL);
692 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400693 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700694 memset(battery, 0, sizeof(struct acpi_battery));
695
696 battery->handle = device->handle;
Patrick Mochel145def82006-05-19 16:54:39 -0400697 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
699 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
700 acpi_driver_data(device) = battery;
701
702 result = acpi_battery_check(battery);
703 if (result)
704 goto end;
705
706 result = acpi_battery_add_fs(device);
707 if (result)
708 goto end;
709
710 status = acpi_install_notify_handler(battery->handle,
Len Brown4be44fc2005-08-05 00:44:28 -0400711 ACPI_DEVICE_NOTIFY,
712 acpi_battery_notify, battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 if (ACPI_FAILURE(status)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700714 result = -ENODEV;
715 goto end;
716 }
717
718 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400719 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
720 device->status.battery_present ? "present" : "absent");
721
722 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 if (result) {
724 acpi_battery_remove_fs(device);
725 kfree(battery);
726 }
727
Patrick Mocheld550d982006-06-27 00:41:40 -0400728 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729}
730
Len Brown4be44fc2005-08-05 00:44:28 -0400731static int acpi_battery_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700732{
Len Brown4be44fc2005-08-05 00:44:28 -0400733 acpi_status status = 0;
734 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
737 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400738 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739
Len Brown4be44fc2005-08-05 00:44:28 -0400740 battery = (struct acpi_battery *)acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
742 status = acpi_remove_notify_handler(battery->handle,
Len Brown4be44fc2005-08-05 00:44:28 -0400743 ACPI_DEVICE_NOTIFY,
744 acpi_battery_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745
746 acpi_battery_remove_fs(device);
747
748 kfree(battery);
749
Patrick Mocheld550d982006-06-27 00:41:40 -0400750 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751}
752
Len Brown4be44fc2005-08-05 00:44:28 -0400753static int __init acpi_battery_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754{
Len Brown4be44fc2005-08-05 00:44:28 -0400755 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757
758 acpi_battery_dir = proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir);
759 if (!acpi_battery_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -0400760 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761 acpi_battery_dir->owner = THIS_MODULE;
762
763 result = acpi_bus_register_driver(&acpi_battery_driver);
764 if (result < 0) {
765 remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -0400766 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 }
768
Patrick Mocheld550d982006-06-27 00:41:40 -0400769 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700770}
771
Len Brown4be44fc2005-08-05 00:44:28 -0400772static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774
775 acpi_bus_unregister_driver(&acpi_battery_driver);
776
777 remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir);
778
Patrick Mocheld550d982006-06-27 00:41:40 -0400779 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780}
781
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782module_init(acpi_battery_init);
783module_exit(acpi_battery_exit);