blob: cad932de383d02c3f3426f3784a9ff2190a1e4b2 [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"
Linus Torvalds1da177e2005-04-16 15:20:36 -070046#define ACPI_BATTERY_NOTIFY_STATUS 0x80
47#define ACPI_BATTERY_NOTIFY_INFO 0x81
48#define ACPI_BATTERY_UNITS_WATTS "mW"
49#define ACPI_BATTERY_UNITS_AMPS "mA"
50
Linus Torvalds1da177e2005-04-16 15:20:36 -070051#define _COMPONENT ACPI_BATTERY_COMPONENT
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030052
53#define ACPI_BATTERY_UPDATE_TIME 0
54
55#define ACPI_BATTERY_NONE_UPDATE 0
56#define ACPI_BATTERY_EASY_UPDATE 1
57#define ACPI_BATTERY_INIT_UPDATE 2
58
Len Brownf52fd662007-02-12 22:42:12 -050059ACPI_MODULE_NAME("battery");
Linus Torvalds1da177e2005-04-16 15:20:36 -070060
Len Brownf52fd662007-02-12 22:42:12 -050061MODULE_AUTHOR("Paul Diefenbaugh");
Len Brown7cda93e2007-02-12 23:50:02 -050062MODULE_DESCRIPTION("ACPI Battery Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070063MODULE_LICENSE("GPL");
64
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030065static unsigned int update_time = ACPI_BATTERY_UPDATE_TIME;
66
67/* 0 - every time, > 0 - by update_time */
68module_param(update_time, uint, 0644);
69
Rich Townsend3f86b832006-07-01 11:36:54 -040070extern struct proc_dir_entry *acpi_lock_battery_dir(void);
71extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
72
Len Brown4be44fc2005-08-05 00:44:28 -040073static int acpi_battery_add(struct acpi_device *device);
74static int acpi_battery_remove(struct acpi_device *device, int type);
Patrick Mochel5d9464a2006-12-07 20:56:27 +080075static int acpi_battery_resume(struct acpi_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070076
77static struct acpi_driver acpi_battery_driver = {
Len Brownc2b6705b2007-02-12 23:33:40 -050078 .name = "battery",
Len Brown4be44fc2005-08-05 00:44:28 -040079 .class = ACPI_BATTERY_CLASS,
80 .ids = ACPI_BATTERY_HID,
81 .ops = {
82 .add = acpi_battery_add,
Jiri Kosina34c44152006-10-10 14:20:41 -070083 .resume = acpi_battery_resume,
Len Brown4be44fc2005-08-05 00:44:28 -040084 .remove = acpi_battery_remove,
85 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070086};
87
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +030088struct acpi_battery_state {
Len Brown4be44fc2005-08-05 00:44:28 -040089 acpi_integer state;
90 acpi_integer present_rate;
91 acpi_integer remaining_capacity;
92 acpi_integer present_voltage;
Linus Torvalds1da177e2005-04-16 15:20:36 -070093};
94
95struct acpi_battery_info {
Len Brown4be44fc2005-08-05 00:44:28 -040096 acpi_integer power_unit;
97 acpi_integer design_capacity;
98 acpi_integer last_full_capacity;
99 acpi_integer battery_technology;
100 acpi_integer design_voltage;
101 acpi_integer design_capacity_warning;
102 acpi_integer design_capacity_low;
103 acpi_integer battery_capacity_granularity_1;
104 acpi_integer battery_capacity_granularity_2;
105 acpi_string model_number;
106 acpi_string serial_number;
107 acpi_string battery_type;
108 acpi_string oem_info;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700109};
110
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400111enum acpi_battery_files{
112 ACPI_BATTERY_INFO = 0,
113 ACPI_BATTERY_STATE,
114 ACPI_BATTERY_ALARM,
115 ACPI_BATTERY_NUMFILES,
116};
117
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118struct acpi_battery_flags {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300119 u8 battery_present_prev;
120 u8 alarm_present;
121 u8 init_update;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400122 u8 update[ACPI_BATTERY_NUMFILES];
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300123 u8 power_unit;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124};
125
126struct acpi_battery {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300127 struct mutex mutex;
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300128 struct acpi_device *device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129 struct acpi_battery_flags flags;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300130 struct acpi_buffer bif_data;
131 struct acpi_buffer bst_data;
Len Brown4be44fc2005-08-05 00:44:28 -0400132 unsigned long alarm;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400133 unsigned long update_time[ACPI_BATTERY_NUMFILES];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700134};
135
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400136inline int acpi_battery_present(struct acpi_battery *battery)
137{
138 return battery->device->status.battery_present;
139}
140inline char *acpi_battery_power_units(struct acpi_battery *battery)
141{
142 if (battery->flags.power_unit)
143 return ACPI_BATTERY_UNITS_AMPS;
144 else
145 return ACPI_BATTERY_UNITS_WATTS;
146}
147
148inline acpi_handle acpi_battery_handle(struct acpi_battery *battery)
149{
150 return battery->device->handle;
151}
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300152
Linus Torvalds1da177e2005-04-16 15:20:36 -0700153/* --------------------------------------------------------------------------
154 Battery Management
155 -------------------------------------------------------------------------- */
156
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300157static void acpi_battery_check_result(struct acpi_battery *battery, int result)
158{
159 if (!battery)
160 return;
161
162 if (result) {
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400163 battery->flags.init_update = 1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300164 }
165}
166
167static int acpi_battery_extract_package(struct acpi_battery *battery,
168 union acpi_object *package,
169 struct acpi_buffer *format,
170 struct acpi_buffer *data,
171 char *package_name)
172{
173 acpi_status status = AE_OK;
174 struct acpi_buffer data_null = { 0, NULL };
175
176 status = acpi_extract_package(package, format, &data_null);
177 if (status != AE_BUFFER_OVERFLOW) {
178 ACPI_EXCEPTION((AE_INFO, status, "Extracting size %s",
179 package_name));
180 return -ENODEV;
181 }
182
183 if (data_null.length != data->length) {
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400184 kfree(data->pointer);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300185 data->pointer = kzalloc(data_null.length, GFP_KERNEL);
186 if (!data->pointer) {
187 ACPI_EXCEPTION((AE_INFO, AE_NO_MEMORY, "kzalloc()"));
188 return -ENOMEM;
189 }
190 data->length = data_null.length;
191 }
192
193 status = acpi_extract_package(package, format, data);
194 if (ACPI_FAILURE(status)) {
195 ACPI_EXCEPTION((AE_INFO, status, "Extracting %s",
196 package_name));
197 return -ENODEV;
198 }
199
200 return 0;
201}
202
203static int acpi_battery_get_status(struct acpi_battery *battery)
204{
205 int result = 0;
206
207 result = acpi_bus_get_status(battery->device);
208 if (result) {
209 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "Evaluating _STA"));
210 return -ENODEV;
211 }
212 return result;
213}
214
215static int acpi_battery_get_info(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216{
Len Brown4be44fc2005-08-05 00:44:28 -0400217 int result = 0;
218 acpi_status status = 0;
219 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
220 struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BIF),
221 ACPI_BATTERY_FORMAT_BIF
222 };
Len Brown4be44fc2005-08-05 00:44:28 -0400223 union acpi_object *package = NULL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300224 struct acpi_buffer *data = NULL;
225 struct acpi_battery_info *bif = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400227 battery->update_time[ACPI_BATTERY_INFO] = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700228
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300229 if (!acpi_battery_present(battery))
230 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700231
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400232 /* Evaluate _BIF */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300234 status =
235 acpi_evaluate_object(acpi_battery_handle(battery), "_BIF", NULL,
236 &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700237 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400238 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400239 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700240 }
241
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200242 package = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700243
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300244 data = &battery->bif_data;
245
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 /* Extract Package Data */
247
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300248 result =
249 acpi_battery_extract_package(battery, package, &format, data,
250 "_BIF");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300251 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700252 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Len Brown4be44fc2005-08-05 00:44:28 -0400254 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400256 kfree(buffer.pointer);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300257
258 if (!result) {
259 bif = data->pointer;
260 battery->flags.power_unit = bif->power_unit;
261 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262
Patrick Mocheld550d982006-06-27 00:41:40 -0400263 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264}
265
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300266static int acpi_battery_get_state(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267{
Len Brown4be44fc2005-08-05 00:44:28 -0400268 int result = 0;
269 acpi_status status = 0;
270 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
271 struct acpi_buffer format = { sizeof(ACPI_BATTERY_FORMAT_BST),
272 ACPI_BATTERY_FORMAT_BST
273 };
Len Brown4be44fc2005-08-05 00:44:28 -0400274 union acpi_object *package = NULL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300275 struct acpi_buffer *data = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700276
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400277 battery->update_time[ACPI_BATTERY_STATE] = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300279 if (!acpi_battery_present(battery))
280 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400282 /* Evaluate _BST */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300284 status =
285 acpi_evaluate_object(acpi_battery_handle(battery), "_BST", NULL,
286 &buffer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400288 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400289 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 }
291
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200292 package = buffer.pointer;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300294 data = &battery->bst_data;
295
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 /* Extract Package Data */
297
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300298 result =
299 acpi_battery_extract_package(battery, package, &format, data,
300 "_BST");
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300301 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700303
Len Brown4be44fc2005-08-05 00:44:28 -0400304 end:
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400305 kfree(buffer.pointer);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306
Patrick Mocheld550d982006-06-27 00:41:40 -0400307 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700308}
309
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300310static int acpi_battery_get_alarm(struct acpi_battery *battery)
311{
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400312 battery->update_time[ACPI_BATTERY_ALARM] = get_seconds();
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300313
314 return 0;
315}
316
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300317static int acpi_battery_set_alarm(struct acpi_battery *battery,
318 unsigned long alarm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700319{
Len Brown4be44fc2005-08-05 00:44:28 -0400320 acpi_status status = 0;
321 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
322 struct acpi_object_list arg_list = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400324 battery->update_time[ACPI_BATTERY_ALARM] = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700325
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300326 if (!acpi_battery_present(battery))
327 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400329 if (!battery->flags.alarm_present)
Patrick Mocheld550d982006-06-27 00:41:40 -0400330 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
332 arg0.integer.value = alarm;
333
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300334 status =
335 acpi_evaluate_object(acpi_battery_handle(battery), "_BTP",
336 &arg_list, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400338 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700339
340 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm));
341
342 battery->alarm = alarm;
343
Patrick Mocheld550d982006-06-27 00:41:40 -0400344 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345}
346
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300347static int acpi_battery_init_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700348{
Len Brown4be44fc2005-08-05 00:44:28 -0400349 int result = 0;
350 acpi_status status = AE_OK;
351 acpi_handle handle = NULL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300352 struct acpi_battery_info *bif = battery->bif_data.pointer;
353 unsigned long alarm = battery->alarm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700354
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300355 /* See if alarms are supported, and if so, set default */
Len Brown4be44fc2005-08-05 00:44:28 -0400356
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300357 status = acpi_get_handle(acpi_battery_handle(battery), "_BTP", &handle);
358 if (ACPI_SUCCESS(status)) {
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400359 battery->flags.alarm_present = 1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300360 if (!alarm && bif) {
361 alarm = bif->design_capacity_warning;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300363 result = acpi_battery_set_alarm(battery, alarm);
364 if (result)
365 goto end;
366 } else {
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400367 battery->flags.alarm_present = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 }
369
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300370 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371
Patrick Mocheld550d982006-06-27 00:41:40 -0400372 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700373}
374
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300375static int acpi_battery_init_update(struct acpi_battery *battery)
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500376{
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300377 int result = 0;
378
379 result = acpi_battery_get_status(battery);
380 if (result)
381 return result;
382
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400383 battery->flags.battery_present_prev = acpi_battery_present(battery);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300384
385 if (acpi_battery_present(battery)) {
386 result = acpi_battery_get_info(battery);
387 if (result)
388 return result;
389 result = acpi_battery_get_state(battery);
390 if (result)
391 return result;
392
393 acpi_battery_init_alarm(battery);
394 }
395
396 return result;
397}
398
399static int acpi_battery_update(struct acpi_battery *battery,
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300400 int update, int *update_result_ptr)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300401{
402 int result = 0;
403 int update_result = ACPI_BATTERY_NONE_UPDATE;
404
405 if (!acpi_battery_present(battery)) {
406 update = 1;
407 }
408
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400409 if (battery->flags.init_update) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300410 result = acpi_battery_init_update(battery);
411 if (result)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400412 goto end;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300413 update_result = ACPI_BATTERY_INIT_UPDATE;
414 } else if (update) {
415 result = acpi_battery_get_status(battery);
416 if (result)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400417 goto end;
418 if ((!battery->flags.battery_present_prev & acpi_battery_present(battery))
419 || (battery->flags.battery_present_prev & !acpi_battery_present(battery))) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300420 result = acpi_battery_init_update(battery);
421 if (result)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400422 goto end;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300423 update_result = ACPI_BATTERY_INIT_UPDATE;
424 } else {
425 update_result = ACPI_BATTERY_EASY_UPDATE;
426 }
427 }
428
429 end:
430
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400431 battery->flags.init_update = (result != 0);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300432
433 *update_result_ptr = update_result;
434
435 return result;
436}
437
438static void acpi_battery_notify_update(struct acpi_battery *battery)
439{
440 acpi_battery_get_status(battery);
441
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400442 if (battery->flags.init_update) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300443 return;
444 }
445
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400446 if ((!battery->flags.battery_present_prev &
447 acpi_battery_present(battery)) ||
448 (battery->flags.battery_present_prev &
449 !acpi_battery_present(battery))) {
450 battery->flags.init_update = 1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300451 } else {
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400452 battery->flags.update[ACPI_BATTERY_INFO] = 1;
453 battery->flags.update[ACPI_BATTERY_STATE] = 1;
454 battery->flags.update[ACPI_BATTERY_ALARM] = 1;
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500455 }
456}
457
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458/* --------------------------------------------------------------------------
459 FS Interface (/proc)
460 -------------------------------------------------------------------------- */
461
Len Brown4be44fc2005-08-05 00:44:28 -0400462static struct proc_dir_entry *acpi_battery_dir;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300463
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400464static int acpi_battery_print_info(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700465{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200466 struct acpi_battery *battery = seq->private;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700467 struct acpi_battery_info *bif = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400468 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700469
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300470 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700471 goto end;
472
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300473 if (acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700474 seq_printf(seq, "present: yes\n");
475 else {
476 seq_printf(seq, "present: no\n");
477 goto end;
478 }
479
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300480 bif = battery->bif_data.pointer;
481 if (!bif) {
482 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BIF buffer is NULL"));
483 result = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700484 goto end;
485 }
486
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300487 /* Battery Units */
488
489 units = acpi_battery_power_units(battery);
Len Brown4be44fc2005-08-05 00:44:28 -0400490
Linus Torvalds1da177e2005-04-16 15:20:36 -0700491 if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
492 seq_printf(seq, "design capacity: unknown\n");
493 else
494 seq_printf(seq, "design capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400495 (u32) bif->design_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700496
497 if (bif->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
498 seq_printf(seq, "last full capacity: unknown\n");
499 else
500 seq_printf(seq, "last full capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400501 (u32) bif->last_full_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502
503 switch ((u32) bif->battery_technology) {
504 case 0:
505 seq_printf(seq, "battery technology: non-rechargeable\n");
506 break;
507 case 1:
508 seq_printf(seq, "battery technology: rechargeable\n");
509 break;
510 default:
511 seq_printf(seq, "battery technology: unknown\n");
512 break;
513 }
514
515 if (bif->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
516 seq_printf(seq, "design voltage: unknown\n");
517 else
518 seq_printf(seq, "design voltage: %d mV\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400519 (u32) bif->design_voltage);
Len Brown4be44fc2005-08-05 00:44:28 -0400520 seq_printf(seq, "design capacity warning: %d %sh\n",
521 (u32) bif->design_capacity_warning, units);
522 seq_printf(seq, "design capacity low: %d %sh\n",
523 (u32) bif->design_capacity_low, units);
524 seq_printf(seq, "capacity granularity 1: %d %sh\n",
525 (u32) bif->battery_capacity_granularity_1, units);
526 seq_printf(seq, "capacity granularity 2: %d %sh\n",
527 (u32) bif->battery_capacity_granularity_2, units);
528 seq_printf(seq, "model number: %s\n", bif->model_number);
529 seq_printf(seq, "serial number: %s\n", bif->serial_number);
530 seq_printf(seq, "battery type: %s\n", bif->battery_type);
531 seq_printf(seq, "OEM info: %s\n", bif->oem_info);
532
533 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700534
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300535 if (result)
536 seq_printf(seq, "ERROR: Unable to read battery info\n");
537
538 return result;
539}
540
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400541static int acpi_battery_print_state(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700542{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200543 struct acpi_battery *battery = seq->private;
Vladimir Lebedeva1f0eff2007-02-20 15:48:06 +0300544 struct acpi_battery_state *bst = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400545 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300547 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700548 goto end;
549
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300550 if (acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551 seq_printf(seq, "present: yes\n");
552 else {
553 seq_printf(seq, "present: no\n");
554 goto end;
555 }
556
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300557 bst = battery->bst_data.pointer;
558 if (!bst) {
559 ACPI_EXCEPTION((AE_INFO, AE_ERROR, "BST buffer is NULL"));
560 result = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700561 goto end;
562 }
563
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300564 /* Battery Units */
565
566 units = acpi_battery_power_units(battery);
567
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 if (!(bst->state & 0x04))
569 seq_printf(seq, "capacity state: ok\n");
570 else
571 seq_printf(seq, "capacity state: critical\n");
572
Len Brown4be44fc2005-08-05 00:44:28 -0400573 if ((bst->state & 0x01) && (bst->state & 0x02)) {
574 seq_printf(seq,
575 "charging state: charging/discharging\n");
Len Brown4be44fc2005-08-05 00:44:28 -0400576 } else if (bst->state & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 seq_printf(seq, "charging state: discharging\n");
578 else if (bst->state & 0x02)
579 seq_printf(seq, "charging state: charging\n");
580 else {
581 seq_printf(seq, "charging state: charged\n");
582 }
583
584 if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN)
585 seq_printf(seq, "present rate: unknown\n");
586 else
587 seq_printf(seq, "present rate: %d %s\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400588 (u32) bst->present_rate, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589
590 if (bst->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
591 seq_printf(seq, "remaining capacity: unknown\n");
592 else
593 seq_printf(seq, "remaining capacity: %d %sh\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400594 (u32) bst->remaining_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595
596 if (bst->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
597 seq_printf(seq, "present voltage: unknown\n");
598 else
599 seq_printf(seq, "present voltage: %d mV\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400600 (u32) bst->present_voltage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601
Len Brown4be44fc2005-08-05 00:44:28 -0400602 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300604 if (result) {
605 seq_printf(seq, "ERROR: Unable to read battery state\n");
606 }
607
608 return result;
609}
610
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400611static int acpi_battery_print_alarm(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700612{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200613 struct acpi_battery *battery = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400614 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700615
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300616 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700617 goto end;
618
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300619 if (!acpi_battery_present(battery)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 seq_printf(seq, "present: no\n");
621 goto end;
622 }
623
624 /* Battery Units */
Len Brown4be44fc2005-08-05 00:44:28 -0400625
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300626 units = acpi_battery_power_units(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627
628 seq_printf(seq, "alarm: ");
629 if (!battery->alarm)
630 seq_printf(seq, "unsupported\n");
631 else
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300632 seq_printf(seq, "%lu %sh\n", battery->alarm, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700633
Len Brown4be44fc2005-08-05 00:44:28 -0400634 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300635
636 if (result)
637 seq_printf(seq, "ERROR: Unable to read battery alarm\n");
638
639 return result;
640}
641
Linus Torvalds1da177e2005-04-16 15:20:36 -0700642static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400643acpi_battery_write_alarm(struct file *file,
644 const char __user * buffer,
645 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646{
Len Brown4be44fc2005-08-05 00:44:28 -0400647 int result = 0;
648 char alarm_string[12] = { '\0' };
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200649 struct seq_file *m = file->private_data;
650 struct acpi_battery *battery = m->private;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300651 int update_result = ACPI_BATTERY_NONE_UPDATE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 if (!battery || (count > sizeof(alarm_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -0400654 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700655
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400656 mutex_lock(&battery->mutex);
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500657
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300658 result = acpi_battery_update(battery, 1, &update_result);
659 if (result) {
660 result = -ENODEV;
661 goto end;
662 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300664 if (!acpi_battery_present(battery)) {
665 result = -ENODEV;
666 goto end;
667 }
668
669 if (copy_from_user(alarm_string, buffer, count)) {
670 result = -EFAULT;
671 goto end;
672 }
Len Brown4be44fc2005-08-05 00:44:28 -0400673
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 alarm_string[count] = '\0';
675
Len Brown4be44fc2005-08-05 00:44:28 -0400676 result = acpi_battery_set_alarm(battery,
677 simple_strtoul(alarm_string, NULL, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300679 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300681 end:
682
683 acpi_battery_check_result(battery, result);
684
685 if (!result)
686 result = count;
687
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400688 mutex_unlock(&battery->mutex);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300689
690 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700691}
692
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400693typedef int(*print_func)(struct seq_file *seq, int result);
694typedef int(*get_func)(struct acpi_battery *battery);
695
696static struct acpi_read_mux {
697 print_func print;
698 get_func get;
699} acpi_read_funcs[ACPI_BATTERY_NUMFILES] = {
700 {.get = acpi_battery_get_info, .print = acpi_battery_print_info},
701 {.get = acpi_battery_get_state, .print = acpi_battery_print_state},
702 {.get = acpi_battery_get_alarm, .print = acpi_battery_print_alarm},
703};
704
705static int acpi_battery_read(int fid, struct seq_file *seq)
706{
707 struct acpi_battery *battery = seq->private;
708 int result = 0;
709 int update_result = ACPI_BATTERY_NONE_UPDATE;
710 int update = 0;
711
712 mutex_lock(&battery->mutex);
713
714 update = (get_seconds() - battery->update_time[fid] >= update_time);
715 update = (update | battery->flags.update[fid]);
716
717 result = acpi_battery_update(battery, update, &update_result);
718 if (result)
719 goto end;
720
721 if (update_result == ACPI_BATTERY_EASY_UPDATE) {
722 result = acpi_read_funcs[fid].get(battery);
723 if (result)
724 goto end;
725 }
726
727 end:
728 result = acpi_read_funcs[fid].print(seq, result);
729 acpi_battery_check_result(battery, result);
730 battery->flags.update[fid] = result;
731 mutex_unlock(&battery->mutex);
732 return result;
733}
734
735static int acpi_battery_read_info(struct seq_file *seq, void *offset)
736{
737 return acpi_battery_read(ACPI_BATTERY_INFO, seq);
738}
739
740static int acpi_battery_read_state(struct seq_file *seq, void *offset)
741{
742 return acpi_battery_read(ACPI_BATTERY_STATE, seq);
743}
744
745static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
746{
747 return acpi_battery_read(ACPI_BATTERY_ALARM, seq);
748}
749
750static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
751{
752 return single_open(file, acpi_battery_read_info, PDE(inode)->data);
753}
754
755static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
756{
757 return single_open(file, acpi_battery_read_state, PDE(inode)->data);
758}
759
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
761{
762 return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
763}
764
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400765static struct battery_file {
766 struct file_operations ops;
767 mode_t mode;
768 char *name;
769} acpi_battery_file[] = {
770 {
771 .name = "info",
772 .mode = S_IRUGO,
773 .ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400774 .open = acpi_battery_info_open_fs,
775 .read = seq_read,
776 .llseek = seq_lseek,
777 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 .owner = THIS_MODULE,
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400779 },
780 },
781 {
782 .name = "state",
783 .mode = S_IRUGO,
784 .ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400785 .open = acpi_battery_state_open_fs,
786 .read = seq_read,
787 .llseek = seq_lseek,
788 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789 .owner = THIS_MODULE,
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400790 },
791 },
792 {
793 .name = "alarm",
794 .mode = S_IFREG | S_IRUGO | S_IWUSR,
795 .ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400796 .open = acpi_battery_alarm_open_fs,
797 .read = seq_read,
798 .write = acpi_battery_write_alarm,
799 .llseek = seq_lseek,
800 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 .owner = THIS_MODULE,
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400802 },
803 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804};
805
Len Brown4be44fc2005-08-05 00:44:28 -0400806static int acpi_battery_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807{
Len Brown4be44fc2005-08-05 00:44:28 -0400808 struct proc_dir_entry *entry = NULL;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400809 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700810
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 if (!acpi_device_dir(device)) {
812 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -0400813 acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400815 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 acpi_device_dir(device)->owner = THIS_MODULE;
817 }
818
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400819 for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) {
820 entry = create_proc_entry(acpi_battery_file[i].name,
821 acpi_battery_file[i].mode, acpi_device_dir(device));
822 if (!entry)
823 return -ENODEV;
824 else {
825 entry->proc_fops = &acpi_battery_file[i].ops;
826 entry->data = acpi_driver_data(device);
827 entry->owner = THIS_MODULE;
828 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829 }
830
Patrick Mocheld550d982006-06-27 00:41:40 -0400831 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700832}
833
Len Brown4be44fc2005-08-05 00:44:28 -0400834static int acpi_battery_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835{
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400836 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837 if (acpi_device_dir(device)) {
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400838 for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) {
839 remove_proc_entry(acpi_battery_file[i].name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840 acpi_device_dir(device));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400841 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700842 remove_proc_entry(acpi_device_bid(device), acpi_battery_dir);
843 acpi_device_dir(device) = NULL;
844 }
845
Patrick Mocheld550d982006-06-27 00:41:40 -0400846 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700847}
848
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849/* --------------------------------------------------------------------------
850 Driver Interface
851 -------------------------------------------------------------------------- */
852
Len Brown4be44fc2005-08-05 00:44:28 -0400853static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700854{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200855 struct acpi_battery *battery = data;
Len Brown4be44fc2005-08-05 00:44:28 -0400856 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700857
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400859 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700860
Patrick Mochel145def82006-05-19 16:54:39 -0400861 device = battery->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862
863 switch (event) {
864 case ACPI_BATTERY_NOTIFY_STATUS:
865 case ACPI_BATTERY_NOTIFY_INFO:
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400866 case ACPI_NOTIFY_BUS_CHECK:
867 case ACPI_NOTIFY_DEVICE_CHECK:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300868 device = battery->device;
869 acpi_battery_notify_update(battery);
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300870 acpi_bus_generate_event(device, event,
871 acpi_battery_present(battery));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872 break;
873 default:
874 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400875 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700876 break;
877 }
878
Patrick Mocheld550d982006-06-27 00:41:40 -0400879 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880}
881
Len Brown4be44fc2005-08-05 00:44:28 -0400882static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883{
Len Brown4be44fc2005-08-05 00:44:28 -0400884 int result = 0;
885 acpi_status status = 0;
886 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700887
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400889 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700890
Burman Yan36bcbec2006-12-19 12:56:11 -0800891 battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400893 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300895 mutex_init(&battery->mutex);
896
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400897 mutex_lock(&battery->mutex);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300898
Patrick Mochel145def82006-05-19 16:54:39 -0400899 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700900 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
901 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
902 acpi_driver_data(device) = battery;
903
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300904 result = acpi_battery_get_status(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905 if (result)
906 goto end;
907
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400908 battery->flags.init_update = 1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300909
Linus Torvalds1da177e2005-04-16 15:20:36 -0700910 result = acpi_battery_add_fs(device);
911 if (result)
912 goto end;
913
Patrick Mochel3b073ec2006-05-19 16:54:41 -0400914 status = acpi_install_notify_handler(device->handle,
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400915 ACPI_ALL_NOTIFY,
Len Brown4be44fc2005-08-05 00:44:28 -0400916 acpi_battery_notify, battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700917 if (ACPI_FAILURE(status)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300918 ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700919 result = -ENODEV;
920 goto end;
921 }
922
923 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400924 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
925 device->status.battery_present ? "present" : "absent");
926
927 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300928
Linus Torvalds1da177e2005-04-16 15:20:36 -0700929 if (result) {
930 acpi_battery_remove_fs(device);
931 kfree(battery);
932 }
933
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400934 mutex_unlock(&battery->mutex);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300935
Patrick Mocheld550d982006-06-27 00:41:40 -0400936 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937}
938
Len Brown4be44fc2005-08-05 00:44:28 -0400939static int acpi_battery_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700940{
Len Brown4be44fc2005-08-05 00:44:28 -0400941 acpi_status status = 0;
942 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943
Linus Torvalds1da177e2005-04-16 15:20:36 -0700944 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400945 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700946
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200947 battery = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400949 mutex_lock(&battery->mutex);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300950
Patrick Mochel3b073ec2006-05-19 16:54:41 -0400951 status = acpi_remove_notify_handler(device->handle,
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400952 ACPI_ALL_NOTIFY,
Len Brown4be44fc2005-08-05 00:44:28 -0400953 acpi_battery_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700954
955 acpi_battery_remove_fs(device);
956
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400957 kfree(battery->bif_data.pointer);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300958
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400959 kfree(battery->bst_data.pointer);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300960
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400961 mutex_unlock(&battery->mutex);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300962
963 mutex_destroy(&battery->mutex);
964
Linus Torvalds1da177e2005-04-16 15:20:36 -0700965 kfree(battery);
966
Patrick Mocheld550d982006-06-27 00:41:40 -0400967 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968}
969
Jiri Kosina34c44152006-10-10 14:20:41 -0700970/* this is needed to learn about changes made in suspended state */
Patrick Mochel5d9464a2006-12-07 20:56:27 +0800971static int acpi_battery_resume(struct acpi_device *device)
Jiri Kosina34c44152006-10-10 14:20:41 -0700972{
973 struct acpi_battery *battery;
974
975 if (!device)
976 return -EINVAL;
977
978 battery = device->driver_data;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300979
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400980 battery->flags.init_update = 1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300981
982 return 0;
Jiri Kosina34c44152006-10-10 14:20:41 -0700983}
984
Len Brown4be44fc2005-08-05 00:44:28 -0400985static int __init acpi_battery_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700986{
Rich Townsend3f86b832006-07-01 11:36:54 -0400987 int result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700988
Pavel Machek4d8316d2006-08-14 22:37:22 -0700989 if (acpi_disabled)
990 return -ENODEV;
991
Rich Townsend3f86b832006-07-01 11:36:54 -0400992 acpi_battery_dir = acpi_lock_battery_dir();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700993 if (!acpi_battery_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -0400994 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700995
996 result = acpi_bus_register_driver(&acpi_battery_driver);
997 if (result < 0) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400998 acpi_unlock_battery_dir(acpi_battery_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -0400999 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001000 }
1001
Patrick Mocheld550d982006-06-27 00:41:40 -04001002 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001003}
1004
Len Brown4be44fc2005-08-05 00:44:28 -04001005static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -07001006{
Linus Torvalds1da177e2005-04-16 15:20:36 -07001007 acpi_bus_unregister_driver(&acpi_battery_driver);
1008
Rich Townsend3f86b832006-07-01 11:36:54 -04001009 acpi_unlock_battery_dir(acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -07001010
Patrick Mocheld550d982006-06-27 00:41:40 -04001011 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -07001012}
1013
Linus Torvalds1da177e2005-04-16 15:20:36 -07001014module_init(acpi_battery_init);
1015module_exit(acpi_battery_exit);