blob: f3b0024e57606b02ad35e41a7e69b6cef2b14edf [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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070045#define ACPI_BATTERY_DEVICE_NAME "Battery"
46#define ACPI_BATTERY_FILE_INFO "info"
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +030047#define ACPI_BATTERY_FILE_STATE "state"
Linus Torvalds1da177e2005-04-16 15:20:36 -070048#define ACPI_BATTERY_FILE_ALARM "alarm"
49#define ACPI_BATTERY_NOTIFY_STATUS 0x80
50#define ACPI_BATTERY_NOTIFY_INFO 0x81
51#define ACPI_BATTERY_UNITS_WATTS "mW"
52#define ACPI_BATTERY_UNITS_AMPS "mA"
53
Linus Torvalds1da177e2005-04-16 15:20:36 -070054#define _COMPONENT ACPI_BATTERY_COMPONENT
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030055
56#define ACPI_BATTERY_UPDATE_TIME 0
57
58#define ACPI_BATTERY_NONE_UPDATE 0
59#define ACPI_BATTERY_EASY_UPDATE 1
60#define ACPI_BATTERY_INIT_UPDATE 2
61
Len Brownf52fd662007-02-12 22:42:12 -050062ACPI_MODULE_NAME("battery");
Linus Torvalds1da177e2005-04-16 15:20:36 -070063
Len Brownf52fd662007-02-12 22:42:12 -050064MODULE_AUTHOR("Paul Diefenbaugh");
Len Brown7cda93e2007-02-12 23:50:02 -050065MODULE_DESCRIPTION("ACPI Battery Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070066MODULE_LICENSE("GPL");
67
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030068static unsigned int update_time = ACPI_BATTERY_UPDATE_TIME;
69
70/* 0 - every time, > 0 - by update_time */
71module_param(update_time, uint, 0644);
72
Rich Townsend3f86b832006-07-01 11:36:54 -040073extern struct proc_dir_entry *acpi_lock_battery_dir(void);
74extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
75
Len Brown4be44fc2005-08-05 00:44:28 -040076static int acpi_battery_add(struct acpi_device *device);
77static int acpi_battery_remove(struct acpi_device *device, int type);
Patrick Mochel5d9464a2006-12-07 20:56:27 +080078static int acpi_battery_resume(struct acpi_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
80static struct acpi_driver acpi_battery_driver = {
Len Brownc2b67052007-02-12 23:33:40 -050081 .name = "battery",
Len Brown4be44fc2005-08-05 00:44:28 -040082 .class = ACPI_BATTERY_CLASS,
83 .ids = ACPI_BATTERY_HID,
84 .ops = {
85 .add = acpi_battery_add,
Jiri Kosina34c44152006-10-10 14:20:41 -070086 .resume = acpi_battery_resume,
Len Brown4be44fc2005-08-05 00:44:28 -040087 .remove = acpi_battery_remove,
88 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070089};
90
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +030091struct acpi_battery_state {
Len Brown4be44fc2005-08-05 00:44:28 -040092 acpi_integer state;
93 acpi_integer present_rate;
94 acpi_integer remaining_capacity;
95 acpi_integer present_voltage;
Linus Torvalds1da177e2005-04-16 15:20:36 -070096};
97
98struct acpi_battery_info {
Len Brown4be44fc2005-08-05 00:44:28 -040099 acpi_integer power_unit;
100 acpi_integer design_capacity;
101 acpi_integer last_full_capacity;
102 acpi_integer battery_technology;
103 acpi_integer design_voltage;
104 acpi_integer design_capacity_warning;
105 acpi_integer design_capacity_low;
106 acpi_integer battery_capacity_granularity_1;
107 acpi_integer battery_capacity_granularity_2;
108 acpi_string model_number;
109 acpi_string serial_number;
110 acpi_string battery_type;
111 acpi_string oem_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112};
113
114struct acpi_battery_flags {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300115 u8 battery_present_prev;
116 u8 alarm_present;
117 u8 init_update;
118 u8 info_update;
119 u8 state_update;
120 u8 alarm_update;
121 u8 power_unit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122};
123
124struct acpi_battery {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300125 struct mutex mutex;
Patrick Mochel145def82006-05-19 16:54:39 -0400126 struct acpi_device * device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700127 struct acpi_battery_flags flags;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300128 struct acpi_buffer bif_data;
129 struct acpi_buffer bst_data;
Len Brown4be44fc2005-08-05 00:44:28 -0400130 unsigned long alarm;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300131 unsigned long info_update_time;
132 unsigned long state_update_time;
133 unsigned long alarm_update_time;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134};
135
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300136#define acpi_battery_present(battery) battery->device->status.battery_present
137#define acpi_battery_present_prev(battery) battery->flags.battery_present_prev
138#define acpi_battery_alarm_present(battery) battery->flags.alarm_present
139#define acpi_battery_init_update_flag(battery) battery->flags.init_update
140#define acpi_battery_info_update_flag(battery) battery->flags.info_update
141#define acpi_battery_state_update_flag(battery) battery->flags.state_update
142#define acpi_battery_alarm_update_flag(battery) battery->flags.alarm_update
143#define acpi_battery_power_units(battery) battery->flags.power_unit ? \
144 ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS
145#define acpi_battery_handle(battery) battery->device->handle
146#define acpi_battery_inserted(battery) (!acpi_battery_present_prev(battery) & acpi_battery_present(battery))
147#define acpi_battery_removed(battery) (acpi_battery_present_prev(battery) & !acpi_battery_present(battery))
148#define acpi_battery_bid(battery) acpi_device_bid(battery->device)
149#define acpi_battery_status_str(battery) acpi_battery_present(battery) ? "present" : "absent"
150
Linus Torvalds1da177e2005-04-16 15:20:36 -0700151/* --------------------------------------------------------------------------
152 Battery Management
153 -------------------------------------------------------------------------- */
154
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300155static void acpi_battery_mutex_lock(struct acpi_battery *battery)
156{
157 mutex_lock(&battery->mutex);
158}
159
160static void acpi_battery_mutex_unlock(struct acpi_battery *battery)
161{
162 mutex_unlock(&battery->mutex);
163}
164
165static void acpi_battery_check_result(struct acpi_battery *battery, int result)
166{
167 if (!battery)
168 return;
169
170 if (result) {
171 acpi_battery_init_update_flag(battery) = 1;
172 }
173}
174
175static int acpi_battery_extract_package(struct acpi_battery *battery,
176 union acpi_object *package,
177 struct acpi_buffer *format,
178 struct acpi_buffer *data,
179 char *package_name)
180{
181 acpi_status status = AE_OK;
182 struct acpi_buffer data_null = { 0, NULL };
183
184 status = acpi_extract_package(package, format, &data_null);
185 if (status != AE_BUFFER_OVERFLOW) {
186 ACPI_EXCEPTION((AE_INFO, status, "Extracting size %s",
187 package_name));
188 return -ENODEV;
189 }
190
191 if (data_null.length != data->length) {
192 if (data->pointer) {
193 kfree(data->pointer);
194 }
195 data->pointer = kzalloc(data_null.length, GFP_KERNEL);
196 if (!data->pointer) {
197 ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, "kzalloc()"));
198 return -ENOMEM;
199 }
200 data->length = data_null.length;
201 }
202
203 status = acpi_extract_package(package, format, data);
204 if (ACPI_FAILURE(status)) {
205 ACPI_EXCEPTION((AE_INFO, status, "Extracting %s",
206 package_name));
207 return -ENODEV;
208 }
209
210 return 0;
211}
212
213static int acpi_battery_get_status(struct acpi_battery *battery)
214{
215 int result = 0;
216
217 result = acpi_bus_get_status(battery->device);
218 if (result) {
219 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA"));
220 return -ENODEV;
221 }
222 return result;
223}
224
225static int acpi_battery_get_info(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226{
Len Brown4be44fc2005-08-05 00:44:28 -0400227 int result = 0;
228 acpi_status status = 0;
229 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
230 struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BIF),
231 ACPI_BATTERY_FORMAT_BIF
232 };
Len Brown4be44fc2005-08-05 00:44:28 -0400233 union acpi_object *package = NULL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300234 struct acpi_buffer *data = NULL;
235 struct acpi_battery_info *bif = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700236
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300237 battery->info_update_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300239 if (!acpi_battery_present(battery))
240 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241
242 /* Evalute _BIF */
243
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300244 status = acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400246 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400247 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 }
249
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200250 package = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700251
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300252 data = &battery->bif_data;
253
Linus Torvalds1da177e2005-04-16 15:20:36 -0700254 /* Extract Package Data */
255
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300256 result = acpi_battery_extract_package(battery, package, &format, data, "_BIF");
257 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700259
Len Brown4be44fc2005-08-05 00:44:28 -0400260 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300262 if (buffer.pointer) {
263 kfree(buffer.pointer);
264 }
265
266 if (!result) {
267 bif = data->pointer;
268 battery->flags.power_unit = bif->power_unit;
269 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700270
Patrick Mocheld550d982006-06-27 00:41:40 -0400271 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700272}
273
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300274static int acpi_battery_get_state(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275{
Len Brown4be44fc2005-08-05 00:44:28 -0400276 int result = 0;
277 acpi_status status = 0;
278 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
279 struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BST),
280 ACPI_BATTERY_FORMAT_BST
281 };
Len Brown4be44fc2005-08-05 00:44:28 -0400282 union acpi_object *package = NULL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300283 struct acpi_buffer *data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700284
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300285 battery->state_update_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700286
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300287 if (!acpi_battery_present(battery))
288 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
290 /* Evalute _BST */
291
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300292 status = acpi_evaluate_object(acpi_battery_handle(battery), "_BST", NULL, &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400294 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400295 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 }
297
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200298 package = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300300 data = &battery->bst_data;
301
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 /* Extract Package Data */
303
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300304 result = acpi_battery_extract_package(battery, package, &format, data, "_BST");
305 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307
Len Brown4be44fc2005-08-05 00:44:28 -0400308 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300309 if (buffer.pointer) {
310 kfree(buffer.pointer);
311 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700312
Patrick Mocheld550d982006-06-27 00:41:40 -0400313 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314}
315
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300316static int acpi_battery_get_alarm(struct acpi_battery *battery)
317{
318 battery->alarm_update_time = get_seconds();
319
320 return 0;
321}
322
323static int acpi_battery_set_alarm(struct acpi_battery *battery, unsigned long alarm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700324{
Len Brown4be44fc2005-08-05 00:44:28 -0400325 acpi_status status = 0;
326 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
327 struct acpi_object_list arg_list = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300329 battery->alarm_update_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300331 if (!acpi_battery_present(battery))
332 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300334 if (!acpi_battery_alarm_present(battery))
Patrick Mocheld550d982006-06-27 00:41:40 -0400335 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700336
337 arg0.integer.value = alarm;
338
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300339 status = acpi_evaluate_object(acpi_battery_handle(battery), "_BTP", &arg_list, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400341 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
343 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm));
344
345 battery->alarm = alarm;
346
Patrick Mocheld550d982006-06-27 00:41:40 -0400347 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348}
349
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300350static int acpi_battery_init_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351{
Len Brown4be44fc2005-08-05 00:44:28 -0400352 int result = 0;
353 acpi_status status = AE_OK;
354 acpi_handle handle = NULL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300355 struct acpi_battery_info *bif = battery->bif_data.pointer;
356 unsigned long alarm = battery->alarm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700357
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300358 /* See if alarms are supported, and if so, set default */
Len Brown4be44fc2005-08-05 00:44:28 -0400359
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300360 status = acpi_get_handle(acpi_battery_handle(battery), "_BTP", &handle);
361 if (ACPI_SUCCESS(status)) {
362 acpi_battery_alarm_present(battery) = 1;
363 if (!alarm && bif) {
364 alarm = bif->design_capacity_warning;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700365 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300366 result = acpi_battery_set_alarm(battery, alarm);
367 if (result)
368 goto end;
369 } else {
370 acpi_battery_alarm_present(battery) = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371 }
372
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300373 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374
Patrick Mocheld550d982006-06-27 00:41:40 -0400375 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376}
377
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300378static int acpi_battery_init_update(struct acpi_battery *battery)
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500379{
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300380 int result = 0;
381
382 result = acpi_battery_get_status(battery);
383 if (result)
384 return result;
385
386 acpi_battery_present_prev(battery) = acpi_battery_present(battery);
387
388 if (acpi_battery_present(battery)) {
389 result = acpi_battery_get_info(battery);
390 if (result)
391 return result;
392 result = acpi_battery_get_state(battery);
393 if (result)
394 return result;
395
396 acpi_battery_init_alarm(battery);
397 }
398
399 return result;
400}
401
402static int acpi_battery_update(struct acpi_battery *battery,
403 int update, int *update_result_ptr)
404{
405 int result = 0;
406 int update_result = ACPI_BATTERY_NONE_UPDATE;
407
408 if (!acpi_battery_present(battery)) {
409 update = 1;
410 }
411
412 if (acpi_battery_init_update_flag(battery)) {
413 result = acpi_battery_init_update(battery);
414 if (result)
415 goto end;;
416 update_result = ACPI_BATTERY_INIT_UPDATE;
417 } else if (update) {
418 result = acpi_battery_get_status(battery);
419 if (result)
420 goto end;;
421 if (acpi_battery_inserted(battery) || acpi_battery_removed(battery)) {
422 result = acpi_battery_init_update(battery);
423 if (result)
424 goto end;;
425 update_result = ACPI_BATTERY_INIT_UPDATE;
426 } else {
427 update_result = ACPI_BATTERY_EASY_UPDATE;
428 }
429 }
430
431 end:
432
433 acpi_battery_init_update_flag(battery) = (result != 0);
434
435 *update_result_ptr = update_result;
436
437 return result;
438}
439
440static void acpi_battery_notify_update(struct acpi_battery *battery)
441{
442 acpi_battery_get_status(battery);
443
444 if (acpi_battery_init_update_flag(battery)) {
445 return;
446 }
447
448 if (acpi_battery_inserted(battery) || acpi_battery_removed(battery)) {
449 acpi_battery_init_update_flag(battery) = 1;
450 } else {
451 acpi_battery_info_update_flag(battery) = 1;
452 acpi_battery_state_update_flag(battery) = 1;
453 acpi_battery_alarm_update_flag(battery) = 1;
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500454 }
455}
456
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457/* --------------------------------------------------------------------------
458 FS Interface (/proc)
459 -------------------------------------------------------------------------- */
460
Len Brown4be44fc2005-08-05 00:44:28 -0400461static struct proc_dir_entry *acpi_battery_dir;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300462
463static int acpi_battery_read_info_print(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200465 struct acpi_battery *battery = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700466 struct acpi_battery_info *bif = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400467 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700468
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300469 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700470 goto end;
471
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300472 if (acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700473 seq_printf(seq, "present: yes\n");
474 else {
475 seq_printf(seq, "present: no\n");
476 goto end;
477 }
478
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300479 bif = battery->bif_data.pointer;
480 if (!bif) {
481 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BIF buffer is NULL"));
482 result = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700483 goto end;
484 }
485
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300486 /* Battery Units */
487
488 units = acpi_battery_power_units(battery);
Len Brown4be44fc2005-08-05 00:44:28 -0400489
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490 if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
491 seq_printf(seq, "design capacity: unknown\n");
492 else
493 seq_printf(seq, "design capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400494 (u32) bif->design_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495
496 if (bif->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
497 seq_printf(seq, "last full capacity: unknown\n");
498 else
499 seq_printf(seq, "last full capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400500 (u32) bif->last_full_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
502 switch ((u32) bif->battery_technology) {
503 case 0:
504 seq_printf(seq, "battery technology: non-rechargeable\n");
505 break;
506 case 1:
507 seq_printf(seq, "battery technology: rechargeable\n");
508 break;
509 default:
510 seq_printf(seq, "battery technology: unknown\n");
511 break;
512 }
513
514 if (bif->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
515 seq_printf(seq, "design voltage: unknown\n");
516 else
517 seq_printf(seq, "design voltage: %d mV\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400518 (u32) bif->design_voltage);
Len Brown4be44fc2005-08-05 00:44:28 -0400519 seq_printf(seq, "design capacity warning: %d %sh\n",
520 (u32) bif->design_capacity_warning, units);
521 seq_printf(seq, "design capacity low: %d %sh\n",
522 (u32) bif->design_capacity_low, units);
523 seq_printf(seq, "capacity granularity 1: %d %sh\n",
524 (u32) bif->battery_capacity_granularity_1, units);
525 seq_printf(seq, "capacity granularity 2: %d %sh\n",
526 (u32) bif->battery_capacity_granularity_2, units);
527 seq_printf(seq, "model number: %s\n", bif->model_number);
528 seq_printf(seq, "serial number: %s\n", bif->serial_number);
529 seq_printf(seq, "battery type: %s\n", bif->battery_type);
530 seq_printf(seq, "OEM info: %s\n", bif->oem_info);
531
532 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300534 if (result)
535 seq_printf(seq, "ERROR: Unable to read battery info\n");
536
537 return result;
538}
539
540static int acpi_battery_read_info(struct seq_file *seq, void *offset)
541{
542 struct acpi_battery *battery = seq->private;
543 int result = 0;
544 int update_result = ACPI_BATTERY_NONE_UPDATE;
545 int update = 0;
546
547 acpi_battery_mutex_lock(battery);
548
549 update = (get_seconds() - battery->info_update_time >= update_time);
550 update = (update | acpi_battery_info_update_flag(battery));
551
552 result = acpi_battery_update(battery, update, &update_result);
553 if (result)
554 goto end;
555
556 /* Battery Info (_BIF) */
557
558 if (update_result == ACPI_BATTERY_EASY_UPDATE) {
559 result = acpi_battery_get_info(battery);
560 if (result)
561 goto end;
562 }
563
564 end:
565
566 result = acpi_battery_read_info_print(seq, result);
567
568 acpi_battery_check_result(battery, result);
569
570 acpi_battery_info_update_flag(battery) = result;
571
572 acpi_battery_mutex_unlock(battery);
573
574 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575}
576
577static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
578{
579 return single_open(file, acpi_battery_read_info, PDE(inode)->data);
580}
581
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300582static int acpi_battery_read_state_print(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200584 struct acpi_battery *battery = seq->private;
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +0300585 struct acpi_battery_state *bst = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400586 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300588 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 goto end;
590
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300591 if (acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700592 seq_printf(seq, "present: yes\n");
593 else {
594 seq_printf(seq, "present: no\n");
595 goto end;
596 }
597
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300598 bst = battery->bst_data.pointer;
599 if (!bst) {
600 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BST buffer is NULL"));
601 result = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 goto end;
603 }
604
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300605 /* Battery Units */
606
607 units = acpi_battery_power_units(battery);
608
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609 if (!(bst->state & 0x04))
610 seq_printf(seq, "capacity state: ok\n");
611 else
612 seq_printf(seq, "capacity state: critical\n");
613
Len Brown4be44fc2005-08-05 00:44:28 -0400614 if ((bst->state & 0x01) && (bst->state & 0x02)) {
615 seq_printf(seq,
616 "charging state: charging/discharging\n");
Len Brown4be44fc2005-08-05 00:44:28 -0400617 } else if (bst->state & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700618 seq_printf(seq, "charging state: discharging\n");
619 else if (bst->state & 0x02)
620 seq_printf(seq, "charging state: charging\n");
621 else {
622 seq_printf(seq, "charging state: charged\n");
623 }
624
625 if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN)
626 seq_printf(seq, "present rate: unknown\n");
627 else
628 seq_printf(seq, "present rate: %d %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400629 (u32) bst->present_rate, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630
631 if (bst->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
632 seq_printf(seq, "remaining capacity: unknown\n");
633 else
634 seq_printf(seq, "remaining capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400635 (u32) bst->remaining_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636
637 if (bst->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
638 seq_printf(seq, "present voltage: unknown\n");
639 else
640 seq_printf(seq, "present voltage: %d mV\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400641 (u32) bst->present_voltage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
Len Brown4be44fc2005-08-05 00:44:28 -0400643 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700644
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300645 if (result) {
646 seq_printf(seq, "ERROR: Unable to read battery state\n");
647 }
648
649 return result;
650}
651
652static int acpi_battery_read_state(struct seq_file *seq, void *offset)
653{
654 struct acpi_battery *battery = seq->private;
655 int result = 0;
656 int update_result = ACPI_BATTERY_NONE_UPDATE;
657 int update = 0;
658
659 acpi_battery_mutex_lock(battery);
660
661 update = (get_seconds() - battery->state_update_time >= update_time);
662 update = (update | acpi_battery_state_update_flag(battery));
663
664 result = acpi_battery_update(battery, update, &update_result);
665 if (result)
666 goto end;
667
668 /* Battery State (_BST) */
669
670 if (update_result == ACPI_BATTERY_EASY_UPDATE) {
671 result = acpi_battery_get_state(battery);
672 if (result)
673 goto end;
674 }
675
676 end:
677
678 result = acpi_battery_read_state_print(seq, result);
679
680 acpi_battery_check_result(battery, result);
681
682 acpi_battery_state_update_flag(battery) = result;
683
684 acpi_battery_mutex_unlock(battery);
685
686 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687}
688
689static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
690{
691 return single_open(file, acpi_battery_read_state, PDE(inode)->data);
692}
693
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300694static int acpi_battery_read_alarm_print(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200696 struct acpi_battery *battery = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400697 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300699 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700 goto end;
701
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300702 if (!acpi_battery_present(battery)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 seq_printf(seq, "present: no\n");
704 goto end;
705 }
706
707 /* Battery Units */
Len Brown4be44fc2005-08-05 00:44:28 -0400708
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300709 units = acpi_battery_power_units(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
711 seq_printf(seq, "alarm: ");
712 if (!battery->alarm)
713 seq_printf(seq, "unsupported\n");
714 else
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300715 seq_printf(seq, "%lu %sh\n", battery->alarm, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716
Len Brown4be44fc2005-08-05 00:44:28 -0400717 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300718
719 if (result)
720 seq_printf(seq, "ERROR: Unable to read battery alarm\n");
721
722 return result;
723}
724
725static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
726{
727 struct acpi_battery *battery = seq->private;
728 int result = 0;
729 int update_result = ACPI_BATTERY_NONE_UPDATE;
730 int update = 0;
731
732 acpi_battery_mutex_lock(battery);
733
734 update = (get_seconds() - battery->alarm_update_time >= update_time);
735 update = (update | acpi_battery_alarm_update_flag(battery));
736
737 result = acpi_battery_update(battery, update, &update_result);
738 if (result)
739 goto end;
740
741 /* Battery Alarm */
742
743 if (update_result == ACPI_BATTERY_EASY_UPDATE) {
744 result = acpi_battery_get_alarm(battery);
745 if (result)
746 goto end;
747 }
748
749 end:
750
751 result = acpi_battery_read_alarm_print(seq, result);
752
753 acpi_battery_check_result(battery, result);
754
755 acpi_battery_alarm_update_flag(battery) = result;
756
757 acpi_battery_mutex_unlock(battery);
758
759 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760}
761
Linus Torvalds1da177e2005-04-16 15:20:36 -0700762static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400763acpi_battery_write_alarm(struct file *file,
764 const char __user * buffer,
765 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766{
Len Brown4be44fc2005-08-05 00:44:28 -0400767 int result = 0;
768 char alarm_string[12] = { '\0' };
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200769 struct seq_file *m = file->private_data;
770 struct acpi_battery *battery = m->private;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300771 int update_result = ACPI_BATTERY_NONE_UPDATE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772
Linus Torvalds1da177e2005-04-16 15:20:36 -0700773
774 if (!battery || (count > sizeof(alarm_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -0400775 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700776
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300777 acpi_battery_mutex_lock(battery);
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500778
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300779 result = acpi_battery_update(battery, 1, &update_result);
780 if (result) {
781 result = -ENODEV;
782 goto end;
783 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300785 if (!acpi_battery_present(battery)) {
786 result = -ENODEV;
787 goto end;
788 }
789
790 if (copy_from_user(alarm_string, buffer, count)) {
791 result = -EFAULT;
792 goto end;
793 }
Len Brown4be44fc2005-08-05 00:44:28 -0400794
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795 alarm_string[count] = '\0';
796
Len Brown4be44fc2005-08-05 00:44:28 -0400797 result = acpi_battery_set_alarm(battery,
798 simple_strtoul(alarm_string, NULL, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700799 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300800 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300802 end:
803
804 acpi_battery_check_result(battery, result);
805
806 if (!result)
807 result = count;
808
809 acpi_battery_mutex_unlock(battery);
810
811 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812}
813
814static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
815{
816 return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
817}
818
Arjan van de Vend7508032006-07-04 13:06:00 -0400819static const struct file_operations acpi_battery_info_ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400820 .open = acpi_battery_info_open_fs,
821 .read = seq_read,
822 .llseek = seq_lseek,
823 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824 .owner = THIS_MODULE,
825};
826
Arjan van de Vend7508032006-07-04 13:06:00 -0400827static const struct file_operations acpi_battery_state_ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400828 .open = acpi_battery_state_open_fs,
829 .read = seq_read,
830 .llseek = seq_lseek,
831 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832 .owner = THIS_MODULE,
833};
834
Arjan van de Vend7508032006-07-04 13:06:00 -0400835static const struct file_operations acpi_battery_alarm_ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400836 .open = acpi_battery_alarm_open_fs,
837 .read = seq_read,
838 .write = acpi_battery_write_alarm,
839 .llseek = seq_lseek,
840 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 .owner = THIS_MODULE,
842};
843
Len Brown4be44fc2005-08-05 00:44:28 -0400844static int acpi_battery_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845{
Len Brown4be44fc2005-08-05 00:44:28 -0400846 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847
Linus Torvalds1da177e2005-04-16 15:20:36 -0700848
849 if (!acpi_device_dir(device)) {
850 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -0400851 acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400853 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854 acpi_device_dir(device)->owner = THIS_MODULE;
855 }
856
857 /* 'info' [R] */
858 entry = create_proc_entry(ACPI_BATTERY_FILE_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400859 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400861 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 else {
Len Brown4be44fc2005-08-05 00:44:28 -0400863 entry->proc_fops = &acpi_battery_info_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 entry->data = acpi_driver_data(device);
865 entry->owner = THIS_MODULE;
866 }
867
868 /* 'status' [R] */
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +0300869 entry = create_proc_entry(ACPI_BATTERY_FILE_STATE,
Len Brown4be44fc2005-08-05 00:44:28 -0400870 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700871 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400872 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700873 else {
874 entry->proc_fops = &acpi_battery_state_ops;
875 entry->data = acpi_driver_data(device);
876 entry->owner = THIS_MODULE;
877 }
878
879 /* 'alarm' [R/W] */
880 entry = create_proc_entry(ACPI_BATTERY_FILE_ALARM,
Len Brown4be44fc2005-08-05 00:44:28 -0400881 S_IFREG | S_IRUGO | S_IWUSR,
882 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400884 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 else {
886 entry->proc_fops = &acpi_battery_alarm_ops;
887 entry->data = acpi_driver_data(device);
888 entry->owner = THIS_MODULE;
889 }
890
Patrick Mocheld550d982006-06-27 00:41:40 -0400891 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892}
893
Len Brown4be44fc2005-08-05 00:44:28 -0400894static int acpi_battery_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700896 if (acpi_device_dir(device)) {
897 remove_proc_entry(ACPI_BATTERY_FILE_ALARM,
898 acpi_device_dir(device));
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +0300899 remove_proc_entry(ACPI_BATTERY_FILE_STATE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 acpi_device_dir(device));
901 remove_proc_entry(ACPI_BATTERY_FILE_INFO,
902 acpi_device_dir(device));
903
904 remove_proc_entry(acpi_device_bid(device), acpi_battery_dir);
905 acpi_device_dir(device) = NULL;
906 }
907
Patrick Mocheld550d982006-06-27 00:41:40 -0400908 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700909}
910
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911/* --------------------------------------------------------------------------
912 Driver Interface
913 -------------------------------------------------------------------------- */
914
Len Brown4be44fc2005-08-05 00:44:28 -0400915static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700916{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200917 struct acpi_battery *battery = data;
Len Brown4be44fc2005-08-05 00:44:28 -0400918 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920
921 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400922 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700923
Patrick Mochel145def82006-05-19 16:54:39 -0400924 device = battery->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700925
926 switch (event) {
927 case ACPI_BATTERY_NOTIFY_STATUS:
928 case ACPI_BATTERY_NOTIFY_INFO:
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400929 case ACPI_NOTIFY_BUS_CHECK:
930 case ACPI_NOTIFY_DEVICE_CHECK:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300931 acpi_battery_mutex_lock(battery);
932 device = battery->device;
933 acpi_battery_notify_update(battery);
934 acpi_battery_mutex_unlock(battery);
935 acpi_bus_generate_event(device, event, acpi_battery_present(battery));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700936 break;
937 default:
938 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400939 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940 break;
941 }
942
Patrick Mocheld550d982006-06-27 00:41:40 -0400943 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944}
945
Len Brown4be44fc2005-08-05 00:44:28 -0400946static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947{
Len Brown4be44fc2005-08-05 00:44:28 -0400948 int result = 0;
949 acpi_status status = 0;
950 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700951
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400953 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
Burman Yan36bcbec2006-12-19 12:56:11 -0800955 battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700956 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400957 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700958
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300959 mutex_init(&battery->mutex);
960
961 acpi_battery_mutex_lock(battery);
962
Patrick Mochel145def82006-05-19 16:54:39 -0400963 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
965 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
966 acpi_driver_data(device) = battery;
967
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300968 result = acpi_battery_get_status(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 if (result)
970 goto end;
971
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300972 acpi_battery_init_update_flag(battery) = 1;
973
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 result = acpi_battery_add_fs(device);
975 if (result)
976 goto end;
977
Patrick Mochel3b073ec2006-05-19 16:54:41 -0400978 status = acpi_install_notify_handler(device->handle,
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400979 ACPI_ALL_NOTIFY,
Len Brown4be44fc2005-08-05 00:44:28 -0400980 acpi_battery_notify, battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700981 if (ACPI_FAILURE(status)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300982 ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700983 result = -ENODEV;
984 goto end;
985 }
986
987 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400988 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
989 device->status.battery_present ? "present" : "absent");
990
991 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300992
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 if (result) {
994 acpi_battery_remove_fs(device);
995 kfree(battery);
996 }
997
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300998 acpi_battery_mutex_unlock(battery);
999
Patrick Mocheld550d982006-06-27 00:41:40 -04001000 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001001}
1002
Len Brown4be44fc2005-08-05 00:44:28 -04001003static int acpi_battery_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001004{
Len Brown4be44fc2005-08-05 00:44:28 -04001005 acpi_status status = 0;
1006 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007
Linus Torvalds1da177e2005-04-16 15:20:36 -07001008 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001009 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001011 battery = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +03001013 acpi_battery_mutex_lock(battery);
1014
Patrick Mochel3b073ec2006-05-19 16:54:41 -04001015 status = acpi_remove_notify_handler(device->handle,
Vladimir Lebedev9fdae722006-06-27 04:49:00 -04001016 ACPI_ALL_NOTIFY,
Len Brown4be44fc2005-08-05 00:44:28 -04001017 acpi_battery_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018
1019 acpi_battery_remove_fs(device);
1020
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +03001021 if (battery->bif_data.pointer)
1022 kfree(battery->bif_data.pointer);
1023
1024 if (battery->bst_data.pointer)
1025 kfree(battery->bst_data.pointer);
1026
1027 acpi_battery_mutex_unlock(battery);
1028
1029 mutex_destroy(&battery->mutex);
1030
Linus Torvalds1da177e2005-04-16 15:20:36 -07001031 kfree(battery);
1032
Patrick Mocheld550d982006-06-27 00:41:40 -04001033 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001034}
1035
Jiri Kosina34c44152006-10-10 14:20:41 -07001036/* this is needed to learn about changes made in suspended state */
Patrick Mochel5d9464a2006-12-07 20:56:27 +08001037static int acpi_battery_resume(struct acpi_device *device)
Jiri Kosina34c44152006-10-10 14:20:41 -07001038{
1039 struct acpi_battery *battery;
1040
1041 if (!device)
1042 return -EINVAL;
1043
1044 battery = device->driver_data;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +03001045
1046 acpi_battery_init_update_flag(battery) = 1;
1047
1048 return 0;
Jiri Kosina34c44152006-10-10 14:20:41 -07001049}
1050
Len Brown4be44fc2005-08-05 00:44:28 -04001051static int __init acpi_battery_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001052{
Rich Townsend3f86b832006-07-01 11:36:54 -04001053 int result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001054
Pavel Machek4d8316d2006-08-14 22:37:22 -07001055 if (acpi_disabled)
1056 return -ENODEV;
1057
Rich Townsend3f86b832006-07-01 11:36:54 -04001058 acpi_battery_dir = acpi_lock_battery_dir();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001059 if (!acpi_battery_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001060 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001061
1062 result = acpi_bus_register_driver(&acpi_battery_driver);
1063 if (result < 0) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001064 acpi_unlock_battery_dir(acpi_battery_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04001065 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001066 }
1067
Patrick Mocheld550d982006-06-27 00:41:40 -04001068 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069}
1070
Len Brown4be44fc2005-08-05 00:44:28 -04001071static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001072{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001073 acpi_bus_unregister_driver(&acpi_battery_driver);
1074
Rich Townsend3f86b832006-07-01 11:36:54 -04001075 acpi_unlock_battery_dir(acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076
Patrick Mocheld550d982006-06-27 00:41:40 -04001077 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001078}
1079
Linus Torvalds1da177e2005-04-16 15:20:36 -07001080module_init(acpi_battery_init);
1081module_exit(acpi_battery_exit);