blob: fc9c50ab521978627d33e583898761e786561b43 [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 Brownc2b6705b2007-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;
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300126 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 Lebedev9ea7d572007-02-20 15:48:06 +0300244 status =
245 acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", NULL,
246 &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400248 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400249 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700250 }
251
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200252 package = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300254 data = &battery->bif_data;
255
Linus Torvalds1da177e2005-04-16 15:20:36 -0700256 /* Extract Package Data */
257
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300258 result =
259 acpi_battery_extract_package(battery, package, &format, data,
260 "_BIF");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300261 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700263
Len Brown4be44fc2005-08-05 00:44:28 -0400264 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700265
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300266 if (buffer.pointer) {
267 kfree(buffer.pointer);
268 }
269
270 if (!result) {
271 bif = data->pointer;
272 battery->flags.power_unit = bif->power_unit;
273 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274
Patrick Mocheld550d982006-06-27 00:41:40 -0400275 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276}
277
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300278static int acpi_battery_get_state(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279{
Len Brown4be44fc2005-08-05 00:44:28 -0400280 int result = 0;
281 acpi_status status = 0;
282 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
283 struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BST),
284 ACPI_BATTERY_FORMAT_BST
285 };
Len Brown4be44fc2005-08-05 00:44:28 -0400286 union acpi_object *package = NULL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300287 struct acpi_buffer *data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700288
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300289 battery->state_update_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300291 if (!acpi_battery_present(battery))
292 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
294 /* Evalute _BST */
295
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300296 status =
297 acpi_evaluate_object(acpi_battery_handle(battery), "_BST", NULL,
298 &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400300 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400301 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 }
303
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200304 package = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700305
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300306 data = &battery->bst_data;
307
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308 /* Extract Package Data */
309
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300310 result =
311 acpi_battery_extract_package(battery, package, &format, data,
312 "_BST");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300313 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700315
Len Brown4be44fc2005-08-05 00:44:28 -0400316 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300317 if (buffer.pointer) {
318 kfree(buffer.pointer);
319 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700320
Patrick Mocheld550d982006-06-27 00:41:40 -0400321 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322}
323
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300324static int acpi_battery_get_alarm(struct acpi_battery *battery)
325{
326 battery->alarm_update_time = get_seconds();
327
328 return 0;
329}
330
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300331static int acpi_battery_set_alarm(struct acpi_battery *battery,
332 unsigned long alarm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700333{
Len Brown4be44fc2005-08-05 00:44:28 -0400334 acpi_status status = 0;
335 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
336 struct acpi_object_list arg_list = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300338 battery->alarm_update_time = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300340 if (!acpi_battery_present(battery))
341 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700342
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300343 if (!acpi_battery_alarm_present(battery))
Patrick Mocheld550d982006-06-27 00:41:40 -0400344 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345
346 arg0.integer.value = alarm;
347
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300348 status =
349 acpi_evaluate_object(acpi_battery_handle(battery), "_BTP",
350 &arg_list, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700351 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400352 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
354 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm));
355
356 battery->alarm = alarm;
357
Patrick Mocheld550d982006-06-27 00:41:40 -0400358 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700359}
360
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300361static int acpi_battery_init_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362{
Len Brown4be44fc2005-08-05 00:44:28 -0400363 int result = 0;
364 acpi_status status = AE_OK;
365 acpi_handle handle = NULL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300366 struct acpi_battery_info *bif = battery->bif_data.pointer;
367 unsigned long alarm = battery->alarm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300369 /* See if alarms are supported, and if so, set default */
Len Brown4be44fc2005-08-05 00:44:28 -0400370
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300371 status = acpi_get_handle(acpi_battery_handle(battery), "_BTP", &handle);
372 if (ACPI_SUCCESS(status)) {
373 acpi_battery_alarm_present(battery) = 1;
374 if (!alarm && bif) {
375 alarm = bif->design_capacity_warning;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700376 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300377 result = acpi_battery_set_alarm(battery, alarm);
378 if (result)
379 goto end;
380 } else {
381 acpi_battery_alarm_present(battery) = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700382 }
383
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300384 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700385
Patrick Mocheld550d982006-06-27 00:41:40 -0400386 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700387}
388
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300389static int acpi_battery_init_update(struct acpi_battery *battery)
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500390{
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300391 int result = 0;
392
393 result = acpi_battery_get_status(battery);
394 if (result)
395 return result;
396
397 acpi_battery_present_prev(battery) = acpi_battery_present(battery);
398
399 if (acpi_battery_present(battery)) {
400 result = acpi_battery_get_info(battery);
401 if (result)
402 return result;
403 result = acpi_battery_get_state(battery);
404 if (result)
405 return result;
406
407 acpi_battery_init_alarm(battery);
408 }
409
410 return result;
411}
412
413static int acpi_battery_update(struct acpi_battery *battery,
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300414 int update, int *update_result_ptr)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300415{
416 int result = 0;
417 int update_result = ACPI_BATTERY_NONE_UPDATE;
418
419 if (!acpi_battery_present(battery)) {
420 update = 1;
421 }
422
423 if (acpi_battery_init_update_flag(battery)) {
424 result = acpi_battery_init_update(battery);
425 if (result)
426 goto end;;
427 update_result = ACPI_BATTERY_INIT_UPDATE;
428 } else if (update) {
429 result = acpi_battery_get_status(battery);
430 if (result)
431 goto end;;
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300432 if (acpi_battery_inserted(battery)
433 || acpi_battery_removed(battery)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300434 result = acpi_battery_init_update(battery);
435 if (result)
436 goto end;;
437 update_result = ACPI_BATTERY_INIT_UPDATE;
438 } else {
439 update_result = ACPI_BATTERY_EASY_UPDATE;
440 }
441 }
442
443 end:
444
445 acpi_battery_init_update_flag(battery) = (result != 0);
446
447 *update_result_ptr = update_result;
448
449 return result;
450}
451
452static void acpi_battery_notify_update(struct acpi_battery *battery)
453{
454 acpi_battery_get_status(battery);
455
456 if (acpi_battery_init_update_flag(battery)) {
457 return;
458 }
459
460 if (acpi_battery_inserted(battery) || acpi_battery_removed(battery)) {
461 acpi_battery_init_update_flag(battery) = 1;
462 } else {
463 acpi_battery_info_update_flag(battery) = 1;
464 acpi_battery_state_update_flag(battery) = 1;
465 acpi_battery_alarm_update_flag(battery) = 1;
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500466 }
467}
468
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469/* --------------------------------------------------------------------------
470 FS Interface (/proc)
471 -------------------------------------------------------------------------- */
472
Len Brown4be44fc2005-08-05 00:44:28 -0400473static struct proc_dir_entry *acpi_battery_dir;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300474
475static int acpi_battery_read_info_print(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700476{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200477 struct acpi_battery *battery = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700478 struct acpi_battery_info *bif = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400479 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700480
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300481 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700482 goto end;
483
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300484 if (acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700485 seq_printf(seq, "present: yes\n");
486 else {
487 seq_printf(seq, "present: no\n");
488 goto end;
489 }
490
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300491 bif = battery->bif_data.pointer;
492 if (!bif) {
493 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BIF buffer is NULL"));
494 result = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700495 goto end;
496 }
497
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300498 /* Battery Units */
499
500 units = acpi_battery_power_units(battery);
Len Brown4be44fc2005-08-05 00:44:28 -0400501
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502 if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
503 seq_printf(seq, "design capacity: unknown\n");
504 else
505 seq_printf(seq, "design capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400506 (u32) bif->design_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700507
508 if (bif->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
509 seq_printf(seq, "last full capacity: unknown\n");
510 else
511 seq_printf(seq, "last full capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400512 (u32) bif->last_full_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700513
514 switch ((u32) bif->battery_technology) {
515 case 0:
516 seq_printf(seq, "battery technology: non-rechargeable\n");
517 break;
518 case 1:
519 seq_printf(seq, "battery technology: rechargeable\n");
520 break;
521 default:
522 seq_printf(seq, "battery technology: unknown\n");
523 break;
524 }
525
526 if (bif->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
527 seq_printf(seq, "design voltage: unknown\n");
528 else
529 seq_printf(seq, "design voltage: %d mV\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400530 (u32) bif->design_voltage);
Len Brown4be44fc2005-08-05 00:44:28 -0400531 seq_printf(seq, "design capacity warning: %d %sh\n",
532 (u32) bif->design_capacity_warning, units);
533 seq_printf(seq, "design capacity low: %d %sh\n",
534 (u32) bif->design_capacity_low, units);
535 seq_printf(seq, "capacity granularity 1: %d %sh\n",
536 (u32) bif->battery_capacity_granularity_1, units);
537 seq_printf(seq, "capacity granularity 2: %d %sh\n",
538 (u32) bif->battery_capacity_granularity_2, units);
539 seq_printf(seq, "model number: %s\n", bif->model_number);
540 seq_printf(seq, "serial number: %s\n", bif->serial_number);
541 seq_printf(seq, "battery type: %s\n", bif->battery_type);
542 seq_printf(seq, "OEM info: %s\n", bif->oem_info);
543
544 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300546 if (result)
547 seq_printf(seq, "ERROR: Unable to read battery info\n");
548
549 return result;
550}
551
552static int acpi_battery_read_info(struct seq_file *seq, void *offset)
553{
554 struct acpi_battery *battery = seq->private;
555 int result = 0;
556 int update_result = ACPI_BATTERY_NONE_UPDATE;
557 int update = 0;
558
559 acpi_battery_mutex_lock(battery);
560
561 update = (get_seconds() - battery->info_update_time >= update_time);
562 update = (update | acpi_battery_info_update_flag(battery));
563
564 result = acpi_battery_update(battery, update, &update_result);
565 if (result)
566 goto end;
567
568 /* Battery Info (_BIF) */
569
570 if (update_result == ACPI_BATTERY_EASY_UPDATE) {
571 result = acpi_battery_get_info(battery);
572 if (result)
573 goto end;
574 }
575
576 end:
577
578 result = acpi_battery_read_info_print(seq, result);
579
580 acpi_battery_check_result(battery, result);
581
582 acpi_battery_info_update_flag(battery) = result;
583
584 acpi_battery_mutex_unlock(battery);
585
586 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700587}
588
589static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
590{
591 return single_open(file, acpi_battery_read_info, PDE(inode)->data);
592}
593
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300594static int acpi_battery_read_state_print(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200596 struct acpi_battery *battery = seq->private;
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +0300597 struct acpi_battery_state *bst = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400598 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700599
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300600 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 goto end;
602
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300603 if (acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700604 seq_printf(seq, "present: yes\n");
605 else {
606 seq_printf(seq, "present: no\n");
607 goto end;
608 }
609
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300610 bst = battery->bst_data.pointer;
611 if (!bst) {
612 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BST buffer is NULL"));
613 result = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700614 goto end;
615 }
616
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300617 /* Battery Units */
618
619 units = acpi_battery_power_units(battery);
620
Linus Torvalds1da177e2005-04-16 15:20:36 -0700621 if (!(bst->state & 0x04))
622 seq_printf(seq, "capacity state: ok\n");
623 else
624 seq_printf(seq, "capacity state: critical\n");
625
Len Brown4be44fc2005-08-05 00:44:28 -0400626 if ((bst->state & 0x01) && (bst->state & 0x02)) {
627 seq_printf(seq,
628 "charging state: charging/discharging\n");
Len Brown4be44fc2005-08-05 00:44:28 -0400629 } else if (bst->state & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700630 seq_printf(seq, "charging state: discharging\n");
631 else if (bst->state & 0x02)
632 seq_printf(seq, "charging state: charging\n");
633 else {
634 seq_printf(seq, "charging state: charged\n");
635 }
636
637 if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN)
638 seq_printf(seq, "present rate: unknown\n");
639 else
640 seq_printf(seq, "present rate: %d %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400641 (u32) bst->present_rate, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642
643 if (bst->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
644 seq_printf(seq, "remaining capacity: unknown\n");
645 else
646 seq_printf(seq, "remaining capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400647 (u32) bst->remaining_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648
649 if (bst->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
650 seq_printf(seq, "present voltage: unknown\n");
651 else
652 seq_printf(seq, "present voltage: %d mV\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400653 (u32) bst->present_voltage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700654
Len Brown4be44fc2005-08-05 00:44:28 -0400655 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300657 if (result) {
658 seq_printf(seq, "ERROR: Unable to read battery state\n");
659 }
660
661 return result;
662}
663
664static int acpi_battery_read_state(struct seq_file *seq, void *offset)
665{
666 struct acpi_battery *battery = seq->private;
667 int result = 0;
668 int update_result = ACPI_BATTERY_NONE_UPDATE;
669 int update = 0;
670
671 acpi_battery_mutex_lock(battery);
672
673 update = (get_seconds() - battery->state_update_time >= update_time);
674 update = (update | acpi_battery_state_update_flag(battery));
675
676 result = acpi_battery_update(battery, update, &update_result);
677 if (result)
678 goto end;
679
680 /* Battery State (_BST) */
681
682 if (update_result == ACPI_BATTERY_EASY_UPDATE) {
683 result = acpi_battery_get_state(battery);
684 if (result)
685 goto end;
686 }
687
688 end:
689
690 result = acpi_battery_read_state_print(seq, result);
691
692 acpi_battery_check_result(battery, result);
693
694 acpi_battery_state_update_flag(battery) = result;
695
696 acpi_battery_mutex_unlock(battery);
697
698 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699}
700
701static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
702{
703 return single_open(file, acpi_battery_read_state, PDE(inode)->data);
704}
705
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300706static int acpi_battery_read_alarm_print(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700707{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200708 struct acpi_battery *battery = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400709 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300711 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700712 goto end;
713
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300714 if (!acpi_battery_present(battery)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 seq_printf(seq, "present: no\n");
716 goto end;
717 }
718
719 /* Battery Units */
Len Brown4be44fc2005-08-05 00:44:28 -0400720
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300721 units = acpi_battery_power_units(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722
723 seq_printf(seq, "alarm: ");
724 if (!battery->alarm)
725 seq_printf(seq, "unsupported\n");
726 else
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300727 seq_printf(seq, "%lu %sh\n", battery->alarm, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728
Len Brown4be44fc2005-08-05 00:44:28 -0400729 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300730
731 if (result)
732 seq_printf(seq, "ERROR: Unable to read battery alarm\n");
733
734 return result;
735}
736
737static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
738{
739 struct acpi_battery *battery = seq->private;
740 int result = 0;
741 int update_result = ACPI_BATTERY_NONE_UPDATE;
742 int update = 0;
743
744 acpi_battery_mutex_lock(battery);
745
746 update = (get_seconds() - battery->alarm_update_time >= update_time);
747 update = (update | acpi_battery_alarm_update_flag(battery));
748
749 result = acpi_battery_update(battery, update, &update_result);
750 if (result)
751 goto end;
752
753 /* Battery Alarm */
754
755 if (update_result == ACPI_BATTERY_EASY_UPDATE) {
756 result = acpi_battery_get_alarm(battery);
757 if (result)
758 goto end;
759 }
760
761 end:
762
763 result = acpi_battery_read_alarm_print(seq, result);
764
765 acpi_battery_check_result(battery, result);
766
767 acpi_battery_alarm_update_flag(battery) = result;
768
769 acpi_battery_mutex_unlock(battery);
770
771 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772}
773
Linus Torvalds1da177e2005-04-16 15:20:36 -0700774static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400775acpi_battery_write_alarm(struct file *file,
776 const char __user * buffer,
777 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778{
Len Brown4be44fc2005-08-05 00:44:28 -0400779 int result = 0;
780 char alarm_string[12] = { '\0' };
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200781 struct seq_file *m = file->private_data;
782 struct acpi_battery *battery = m->private;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300783 int update_result = ACPI_BATTERY_NONE_UPDATE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700784
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 if (!battery || (count > sizeof(alarm_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -0400786 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300788 acpi_battery_mutex_lock(battery);
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500789
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300790 result = acpi_battery_update(battery, 1, &update_result);
791 if (result) {
792 result = -ENODEV;
793 goto end;
794 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700795
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300796 if (!acpi_battery_present(battery)) {
797 result = -ENODEV;
798 goto end;
799 }
800
801 if (copy_from_user(alarm_string, buffer, count)) {
802 result = -EFAULT;
803 goto end;
804 }
Len Brown4be44fc2005-08-05 00:44:28 -0400805
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 alarm_string[count] = '\0';
807
Len Brown4be44fc2005-08-05 00:44:28 -0400808 result = acpi_battery_set_alarm(battery,
809 simple_strtoul(alarm_string, NULL, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300811 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300813 end:
814
815 acpi_battery_check_result(battery, result);
816
817 if (!result)
818 result = count;
819
820 acpi_battery_mutex_unlock(battery);
821
822 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823}
824
825static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
826{
827 return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
828}
829
Arjan van de Vend7508032006-07-04 13:06:00 -0400830static const struct file_operations acpi_battery_info_ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400831 .open = acpi_battery_info_open_fs,
832 .read = seq_read,
833 .llseek = seq_lseek,
834 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 .owner = THIS_MODULE,
836};
837
Arjan van de Vend7508032006-07-04 13:06:00 -0400838static const struct file_operations acpi_battery_state_ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400839 .open = acpi_battery_state_open_fs,
840 .read = seq_read,
841 .llseek = seq_lseek,
842 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700843 .owner = THIS_MODULE,
844};
845
Arjan van de Vend7508032006-07-04 13:06:00 -0400846static const struct file_operations acpi_battery_alarm_ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400847 .open = acpi_battery_alarm_open_fs,
848 .read = seq_read,
849 .write = acpi_battery_write_alarm,
850 .llseek = seq_lseek,
851 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700852 .owner = THIS_MODULE,
853};
854
Len Brown4be44fc2005-08-05 00:44:28 -0400855static int acpi_battery_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856{
Len Brown4be44fc2005-08-05 00:44:28 -0400857 struct proc_dir_entry *entry = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858
Linus Torvalds1da177e2005-04-16 15:20:36 -0700859 if (!acpi_device_dir(device)) {
860 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -0400861 acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400863 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700864 acpi_device_dir(device)->owner = THIS_MODULE;
865 }
866
867 /* 'info' [R] */
868 entry = create_proc_entry(ACPI_BATTERY_FILE_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400869 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700870 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400871 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 else {
Len Brown4be44fc2005-08-05 00:44:28 -0400873 entry->proc_fops = &acpi_battery_info_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874 entry->data = acpi_driver_data(device);
875 entry->owner = THIS_MODULE;
876 }
877
878 /* 'status' [R] */
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +0300879 entry = create_proc_entry(ACPI_BATTERY_FILE_STATE,
Len Brown4be44fc2005-08-05 00:44:28 -0400880 S_IRUGO, acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400882 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883 else {
884 entry->proc_fops = &acpi_battery_state_ops;
885 entry->data = acpi_driver_data(device);
886 entry->owner = THIS_MODULE;
887 }
888
889 /* 'alarm' [R/W] */
890 entry = create_proc_entry(ACPI_BATTERY_FILE_ALARM,
Len Brown4be44fc2005-08-05 00:44:28 -0400891 S_IFREG | S_IRUGO | S_IWUSR,
892 acpi_device_dir(device));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700893 if (!entry)
Patrick Mocheld550d982006-06-27 00:41:40 -0400894 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895 else {
896 entry->proc_fops = &acpi_battery_alarm_ops;
897 entry->data = acpi_driver_data(device);
898 entry->owner = THIS_MODULE;
899 }
900
Patrick Mocheld550d982006-06-27 00:41:40 -0400901 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700902}
903
Len Brown4be44fc2005-08-05 00:44:28 -0400904static int acpi_battery_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700906 if (acpi_device_dir(device)) {
907 remove_proc_entry(ACPI_BATTERY_FILE_ALARM,
908 acpi_device_dir(device));
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +0300909 remove_proc_entry(ACPI_BATTERY_FILE_STATE,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 acpi_device_dir(device));
911 remove_proc_entry(ACPI_BATTERY_FILE_INFO,
912 acpi_device_dir(device));
913
914 remove_proc_entry(acpi_device_bid(device), acpi_battery_dir);
915 acpi_device_dir(device) = NULL;
916 }
917
Patrick Mocheld550d982006-06-27 00:41:40 -0400918 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919}
920
Linus Torvalds1da177e2005-04-16 15:20:36 -0700921/* --------------------------------------------------------------------------
922 Driver Interface
923 -------------------------------------------------------------------------- */
924
Len Brown4be44fc2005-08-05 00:44:28 -0400925static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200927 struct acpi_battery *battery = data;
Len Brown4be44fc2005-08-05 00:44:28 -0400928 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400931 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700932
Patrick Mochel145def82006-05-19 16:54:39 -0400933 device = battery->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934
935 switch (event) {
936 case ACPI_BATTERY_NOTIFY_STATUS:
937 case ACPI_BATTERY_NOTIFY_INFO:
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400938 case ACPI_NOTIFY_BUS_CHECK:
939 case ACPI_NOTIFY_DEVICE_CHECK:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300940 acpi_battery_mutex_lock(battery);
941 device = battery->device;
942 acpi_battery_notify_update(battery);
943 acpi_battery_mutex_unlock(battery);
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300944 acpi_bus_generate_event(device, event,
945 acpi_battery_present(battery));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946 break;
947 default:
948 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400949 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700950 break;
951 }
952
Patrick Mocheld550d982006-06-27 00:41:40 -0400953 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954}
955
Len Brown4be44fc2005-08-05 00:44:28 -0400956static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700957{
Len Brown4be44fc2005-08-05 00:44:28 -0400958 int result = 0;
959 acpi_status status = 0;
960 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700961
Linus Torvalds1da177e2005-04-16 15:20:36 -0700962 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400963 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964
Burman Yan36bcbec2006-12-19 12:56:11 -0800965 battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400967 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300969 mutex_init(&battery->mutex);
970
971 acpi_battery_mutex_lock(battery);
972
Patrick Mochel145def82006-05-19 16:54:39 -0400973 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
975 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
976 acpi_driver_data(device) = battery;
977
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300978 result = acpi_battery_get_status(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700979 if (result)
980 goto end;
981
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300982 acpi_battery_init_update_flag(battery) = 1;
983
Linus Torvalds1da177e2005-04-16 15:20:36 -0700984 result = acpi_battery_add_fs(device);
985 if (result)
986 goto end;
987
Patrick Mochel3b073ec2006-05-19 16:54:41 -0400988 status = acpi_install_notify_handler(device->handle,
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400989 ACPI_ALL_NOTIFY,
Len Brown4be44fc2005-08-05 00:44:28 -0400990 acpi_battery_notify, battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700991 if (ACPI_FAILURE(status)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300992 ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 result = -ENODEV;
994 goto end;
995 }
996
997 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400998 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
999 device->status.battery_present ? "present" : "absent");
1000
1001 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +03001002
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003 if (result) {
1004 acpi_battery_remove_fs(device);
1005 kfree(battery);
1006 }
1007
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +03001008 acpi_battery_mutex_unlock(battery);
1009
Patrick Mocheld550d982006-06-27 00:41:40 -04001010 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001011}
1012
Len Brown4be44fc2005-08-05 00:44:28 -04001013static int acpi_battery_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014{
Len Brown4be44fc2005-08-05 00:44:28 -04001015 acpi_status status = 0;
1016 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001017
Linus Torvalds1da177e2005-04-16 15:20:36 -07001018 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -04001019 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001020
Jan Engelhardt50dd0962006-10-01 00:28:50 +02001021 battery = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001022
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +03001023 acpi_battery_mutex_lock(battery);
1024
Patrick Mochel3b073ec2006-05-19 16:54:41 -04001025 status = acpi_remove_notify_handler(device->handle,
Vladimir Lebedev9fdae722006-06-27 04:49:00 -04001026 ACPI_ALL_NOTIFY,
Len Brown4be44fc2005-08-05 00:44:28 -04001027 acpi_battery_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001028
1029 acpi_battery_remove_fs(device);
1030
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +03001031 if (battery->bif_data.pointer)
1032 kfree(battery->bif_data.pointer);
1033
1034 if (battery->bst_data.pointer)
1035 kfree(battery->bst_data.pointer);
1036
1037 acpi_battery_mutex_unlock(battery);
1038
1039 mutex_destroy(&battery->mutex);
1040
Linus Torvalds1da177e2005-04-16 15:20:36 -07001041 kfree(battery);
1042
Patrick Mocheld550d982006-06-27 00:41:40 -04001043 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001044}
1045
Jiri Kosina34c44152006-10-10 14:20:41 -07001046/* this is needed to learn about changes made in suspended state */
Patrick Mochel5d9464a2006-12-07 20:56:27 +08001047static int acpi_battery_resume(struct acpi_device *device)
Jiri Kosina34c44152006-10-10 14:20:41 -07001048{
1049 struct acpi_battery *battery;
1050
1051 if (!device)
1052 return -EINVAL;
1053
1054 battery = device->driver_data;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +03001055
1056 acpi_battery_init_update_flag(battery) = 1;
1057
1058 return 0;
Jiri Kosina34c44152006-10-10 14:20:41 -07001059}
1060
Len Brown4be44fc2005-08-05 00:44:28 -04001061static int __init acpi_battery_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001062{
Rich Townsend3f86b832006-07-01 11:36:54 -04001063 int result;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001064
Pavel Machek4d8316d2006-08-14 22:37:22 -07001065 if (acpi_disabled)
1066 return -ENODEV;
1067
Rich Townsend3f86b832006-07-01 11:36:54 -04001068 acpi_battery_dir = acpi_lock_battery_dir();
Linus Torvalds1da177e2005-04-16 15:20:36 -07001069 if (!acpi_battery_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -04001070 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001071
1072 result = acpi_bus_register_driver(&acpi_battery_driver);
1073 if (result < 0) {
Rich Townsend3f86b832006-07-01 11:36:54 -04001074 acpi_unlock_battery_dir(acpi_battery_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -04001075 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001076 }
1077
Patrick Mocheld550d982006-06-27 00:41:40 -04001078 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001079}
1080
Len Brown4be44fc2005-08-05 00:44:28 -04001081static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001082{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001083 acpi_bus_unregister_driver(&acpi_battery_driver);
1084
Rich Townsend3f86b832006-07-01 11:36:54 -04001085 acpi_unlock_battery_dir(acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001086
Patrick Mocheld550d982006-06-27 00:41:40 -04001087 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001088}
1089
Linus Torvalds1da177e2005-04-16 15:20:36 -07001090module_init(acpi_battery_init);
1091module_exit(acpi_battery_exit);