blob: de506f39d3bd799d72e7a5c87934342c32dccdb1 [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
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#define ACPI_BATTERY_COMPONENT 0x00040000
40#define ACPI_BATTERY_CLASS "battery"
Linus Torvalds1da177e2005-04-16 15:20:36 -070041#define ACPI_BATTERY_DEVICE_NAME "Battery"
Linus Torvalds1da177e2005-04-16 15:20:36 -070042#define ACPI_BATTERY_NOTIFY_STATUS 0x80
43#define ACPI_BATTERY_NOTIFY_INFO 0x81
44#define ACPI_BATTERY_UNITS_WATTS "mW"
45#define ACPI_BATTERY_UNITS_AMPS "mA"
46
Linus Torvalds1da177e2005-04-16 15:20:36 -070047#define _COMPONENT ACPI_BATTERY_COMPONENT
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030048
49#define ACPI_BATTERY_UPDATE_TIME 0
50
51#define ACPI_BATTERY_NONE_UPDATE 0
52#define ACPI_BATTERY_EASY_UPDATE 1
53#define ACPI_BATTERY_INIT_UPDATE 2
54
Len Brownf52fd662007-02-12 22:42:12 -050055ACPI_MODULE_NAME("battery");
Linus Torvalds1da177e2005-04-16 15:20:36 -070056
Len Brownf52fd662007-02-12 22:42:12 -050057MODULE_AUTHOR("Paul Diefenbaugh");
Len Brown7cda93e2007-02-12 23:50:02 -050058MODULE_DESCRIPTION("ACPI Battery Driver");
Linus Torvalds1da177e2005-04-16 15:20:36 -070059MODULE_LICENSE("GPL");
60
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +030061static unsigned int update_time = ACPI_BATTERY_UPDATE_TIME;
62
63/* 0 - every time, > 0 - by update_time */
64module_param(update_time, uint, 0644);
65
Rich Townsend3f86b832006-07-01 11:36:54 -040066extern struct proc_dir_entry *acpi_lock_battery_dir(void);
67extern void *acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir);
68
Len Brown4be44fc2005-08-05 00:44:28 -040069static int acpi_battery_add(struct acpi_device *device);
70static int acpi_battery_remove(struct acpi_device *device, int type);
Patrick Mochel5d9464a2006-12-07 20:56:27 +080071static int acpi_battery_resume(struct acpi_device *device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070072
Thomas Renninger1ba90e32007-07-23 14:44:41 +020073static const struct acpi_device_id battery_device_ids[] = {
74 {"PNP0C0A", 0},
75 {"", 0},
76};
77MODULE_DEVICE_TABLE(acpi, battery_device_ids);
78
Linus Torvalds1da177e2005-04-16 15:20:36 -070079static struct acpi_driver acpi_battery_driver = {
Len Brownc2b67052007-02-12 23:33:40 -050080 .name = "battery",
Len Brown4be44fc2005-08-05 00:44:28 -040081 .class = ACPI_BATTERY_CLASS,
Thomas Renninger1ba90e32007-07-23 14:44:41 +020082 .ids = battery_device_ids,
Len Brown4be44fc2005-08-05 00:44:28 -040083 .ops = {
84 .add = acpi_battery_add,
Jiri Kosina34c44152006-10-10 14:20:41 -070085 .resume = acpi_battery_resume,
Len Brown4be44fc2005-08-05 00:44:28 -040086 .remove = acpi_battery_remove,
87 },
Linus Torvalds1da177e2005-04-16 15:20:36 -070088};
89
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040090enum acpi_battery_files {
Alexey Starikovskiy78490d82007-05-11 13:18:55 -040091 ACPI_BATTERY_INFO = 0,
92 ACPI_BATTERY_STATE,
93 ACPI_BATTERY_ALARM,
94 ACPI_BATTERY_NUMFILES,
95};
96
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +040097struct acpi_battery {
98 struct acpi_device *device;
99 struct mutex lock;
100 unsigned long alarm;
101 unsigned long update_time[ACPI_BATTERY_NUMFILES];
102 int state;
103 int present_rate;
104 int remaining_capacity;
105 int present_voltage;
106 int power_unit;
107 int design_capacity;
108 int last_full_capacity;
109 int technology;
110 int design_voltage;
111 int design_capacity_warning;
112 int design_capacity_low;
113 int capacity_granularity_1;
114 int capacity_granularity_2;
115 char model_number[32];
116 char serial_number[32];
117 char type[32];
118 char oem_info[32];
119 u8 present_prev;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300120 u8 alarm_present;
121 u8 init_update;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400122 u8 update[ACPI_BATTERY_NUMFILES];
Linus Torvalds1da177e2005-04-16 15:20:36 -0700123};
124
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400125inline int acpi_battery_present(struct acpi_battery *battery)
126{
127 return battery->device->status.battery_present;
128}
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400129
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400130inline char *acpi_battery_power_units(struct acpi_battery *battery)
131{
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400132 if (battery->power_unit)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400133 return ACPI_BATTERY_UNITS_AMPS;
134 else
135 return ACPI_BATTERY_UNITS_WATTS;
136}
137
138inline acpi_handle acpi_battery_handle(struct acpi_battery *battery)
139{
140 return battery->device->handle;
141}
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300142
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143/* --------------------------------------------------------------------------
144 Battery Management
145 -------------------------------------------------------------------------- */
146
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300147static void acpi_battery_check_result(struct acpi_battery *battery, int result)
148{
149 if (!battery)
150 return;
151
152 if (result) {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400153 battery->init_update = 1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300154 }
155}
156
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400157struct acpi_offsets {
158 size_t offset; /* offset inside struct acpi_sbs_battery */
159 u8 mode; /* int or string? */
160};
161
162static struct acpi_offsets state_offsets[] = {
163 {offsetof(struct acpi_battery, state), 0},
164 {offsetof(struct acpi_battery, present_rate), 0},
165 {offsetof(struct acpi_battery, remaining_capacity), 0},
166 {offsetof(struct acpi_battery, present_voltage), 0},
167};
168
169static struct acpi_offsets info_offsets[] = {
170 {offsetof(struct acpi_battery, power_unit), 0},
171 {offsetof(struct acpi_battery, design_capacity), 0},
172 {offsetof(struct acpi_battery, last_full_capacity), 0},
173 {offsetof(struct acpi_battery, technology), 0},
174 {offsetof(struct acpi_battery, design_voltage), 0},
175 {offsetof(struct acpi_battery, design_capacity_warning), 0},
176 {offsetof(struct acpi_battery, design_capacity_low), 0},
177 {offsetof(struct acpi_battery, capacity_granularity_1), 0},
178 {offsetof(struct acpi_battery, capacity_granularity_2), 0},
179 {offsetof(struct acpi_battery, model_number), 1},
180 {offsetof(struct acpi_battery, serial_number), 1},
181 {offsetof(struct acpi_battery, type), 1},
182 {offsetof(struct acpi_battery, oem_info), 1},
183};
184
185static int extract_package(struct acpi_battery *battery,
186 union acpi_object *package,
187 struct acpi_offsets *offsets, int num)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300188{
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400189 int i, *x;
190 union acpi_object *element;
191 if (package->type != ACPI_TYPE_PACKAGE)
192 return -EFAULT;
193 for (i = 0; i < num; ++i) {
194 if (package->package.count <= i)
195 return -EFAULT;
196 element = &package->package.elements[i];
197 if (offsets[i].mode) {
198 if (element->type != ACPI_TYPE_STRING &&
199 element->type != ACPI_TYPE_BUFFER)
200 return -EFAULT;
201 strncpy((u8 *)battery + offsets[i].offset,
202 element->string.pointer, 32);
203 } else {
204 if (element->type != ACPI_TYPE_INTEGER)
205 return -EFAULT;
206 x = (int *)((u8 *)battery + offsets[i].offset);
207 *x = element->integer.value;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300208 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300209 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300210 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 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400231 battery->update_time[ACPI_BATTERY_INFO] = get_seconds();
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300232 if (!acpi_battery_present(battery))
233 return 0;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400234 mutex_lock(&battery->lock);
235 status = acpi_evaluate_object(acpi_battery_handle(battery), "_BIF",
236 NULL, &buffer);
237 mutex_unlock(&battery->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700238 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400239 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BIF"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400240 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 }
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400242 result = extract_package(battery, buffer.pointer,
243 info_offsets, ARRAY_SIZE(info_offsets));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400244 kfree(buffer.pointer);
Patrick Mocheld550d982006-06-27 00:41:40 -0400245 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246}
247
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300248static int acpi_battery_get_state(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700249{
Len Brown4be44fc2005-08-05 00:44:28 -0400250 int result = 0;
251 acpi_status status = 0;
252 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700253
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400254 battery->update_time[ACPI_BATTERY_STATE] = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700255
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300256 if (!acpi_battery_present(battery))
257 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700258
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400259 mutex_lock(&battery->lock);
260 status = acpi_evaluate_object(acpi_battery_handle(battery), "_BST",
261 NULL, &buffer);
262 mutex_unlock(&battery->lock);
Len Brown5b31d892007-08-15 00:19:26 -0400263
Linus Torvalds1da177e2005-04-16 15:20:36 -0700264 if (ACPI_FAILURE(status)) {
Thomas Renningera6fc6722006-06-26 23:58:43 -0400265 ACPI_EXCEPTION((AE_INFO, status, "Evaluating _BST"));
Patrick Mocheld550d982006-06-27 00:41:40 -0400266 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700267 }
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400268 result = extract_package(battery, buffer.pointer,
269 state_offsets, ARRAY_SIZE(state_offsets));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400270 kfree(buffer.pointer);
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_alarm(struct acpi_battery *battery)
275{
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400276 battery->update_time[ACPI_BATTERY_ALARM] = get_seconds();
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300277
278 return 0;
279}
280
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300281static int acpi_battery_set_alarm(struct acpi_battery *battery,
282 unsigned long alarm)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
Len Brown4be44fc2005-08-05 00:44:28 -0400284 acpi_status status = 0;
285 union acpi_object arg0 = { ACPI_TYPE_INTEGER };
286 struct acpi_object_list arg_list = { 1, &arg0 };
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400288 battery->update_time[ACPI_BATTERY_ALARM] = get_seconds();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700289
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300290 if (!acpi_battery_present(battery))
291 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400293 if (!battery->alarm_present)
Patrick Mocheld550d982006-06-27 00:41:40 -0400294 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 arg0.integer.value = alarm;
297
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400298 mutex_lock(&battery->lock);
299 status = acpi_evaluate_object(acpi_battery_handle(battery), "_BTP",
300 &arg_list, NULL);
301 mutex_unlock(&battery->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400303 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
305 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm));
306
307 battery->alarm = alarm;
308
Patrick Mocheld550d982006-06-27 00:41:40 -0400309 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310}
311
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300312static int acpi_battery_init_alarm(struct acpi_battery *battery)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313{
Len Brown4be44fc2005-08-05 00:44:28 -0400314 int result = 0;
315 acpi_status status = AE_OK;
316 acpi_handle handle = NULL;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300317 unsigned long alarm = battery->alarm;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300319 /* See if alarms are supported, and if so, set default */
Len Brown4be44fc2005-08-05 00:44:28 -0400320
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300321 status = acpi_get_handle(acpi_battery_handle(battery), "_BTP", &handle);
322 if (ACPI_SUCCESS(status)) {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400323 battery->alarm_present = 1;
324 if (!alarm) {
325 alarm = battery->design_capacity_warning;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700326 }
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300327 result = acpi_battery_set_alarm(battery, alarm);
328 if (result)
329 goto end;
330 } else {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400331 battery->alarm_present = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 }
333
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300334 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700335
Patrick Mocheld550d982006-06-27 00:41:40 -0400336 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337}
338
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300339static int acpi_battery_init_update(struct acpi_battery *battery)
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500340{
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300341 int result = 0;
342
343 result = acpi_battery_get_status(battery);
344 if (result)
345 return result;
346
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400347 battery->present_prev = acpi_battery_present(battery);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300348
349 if (acpi_battery_present(battery)) {
350 result = acpi_battery_get_info(battery);
351 if (result)
352 return result;
353 result = acpi_battery_get_state(battery);
354 if (result)
355 return result;
356
357 acpi_battery_init_alarm(battery);
358 }
359
360 return result;
361}
362
363static int acpi_battery_update(struct acpi_battery *battery,
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300364 int update, int *update_result_ptr)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300365{
366 int result = 0;
367 int update_result = ACPI_BATTERY_NONE_UPDATE;
368
369 if (!acpi_battery_present(battery)) {
370 update = 1;
371 }
372
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400373 if (battery->init_update) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300374 result = acpi_battery_init_update(battery);
375 if (result)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400376 goto end;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300377 update_result = ACPI_BATTERY_INIT_UPDATE;
378 } else if (update) {
379 result = acpi_battery_get_status(battery);
380 if (result)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400381 goto end;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400382 if ((!battery->present_prev & acpi_battery_present(battery))
383 || (battery->present_prev & !acpi_battery_present(battery))) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300384 result = acpi_battery_init_update(battery);
385 if (result)
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400386 goto end;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300387 update_result = ACPI_BATTERY_INIT_UPDATE;
388 } else {
389 update_result = ACPI_BATTERY_EASY_UPDATE;
390 }
391 }
392
393 end:
394
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400395 battery->init_update = (result != 0);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300396
397 *update_result_ptr = update_result;
398
399 return result;
400}
401
402static void acpi_battery_notify_update(struct acpi_battery *battery)
403{
404 acpi_battery_get_status(battery);
405
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400406 if (battery->init_update) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300407 return;
408 }
409
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400410 if ((!battery->present_prev &
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400411 acpi_battery_present(battery)) ||
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400412 (battery->present_prev &
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400413 !acpi_battery_present(battery))) {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400414 battery->init_update = 1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300415 } else {
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400416 battery->update[ACPI_BATTERY_INFO] = 1;
417 battery->update[ACPI_BATTERY_STATE] = 1;
418 battery->update[ACPI_BATTERY_ALARM] = 1;
Vladimir Lebedev4bd35cd2007-02-10 01:43:48 -0500419 }
420}
421
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422/* --------------------------------------------------------------------------
423 FS Interface (/proc)
424 -------------------------------------------------------------------------- */
425
Len Brown4be44fc2005-08-05 00:44:28 -0400426static struct proc_dir_entry *acpi_battery_dir;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300427
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400428static int acpi_battery_print_info(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700429{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200430 struct acpi_battery *battery = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400431 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700432
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300433 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700434 goto end;
435
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300436 if (acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700437 seq_printf(seq, "present: yes\n");
438 else {
439 seq_printf(seq, "present: no\n");
440 goto end;
441 }
442
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300443 /* Battery Units */
444
445 units = acpi_battery_power_units(battery);
Len Brown4be44fc2005-08-05 00:44:28 -0400446
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400447 if (battery->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700448 seq_printf(seq, "design capacity: unknown\n");
449 else
450 seq_printf(seq, "design capacity: %d %sh\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400451 (u32) battery->design_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700452
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400453 if (battery->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 seq_printf(seq, "last full capacity: unknown\n");
455 else
456 seq_printf(seq, "last full capacity: %d %sh\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400457 (u32) battery->last_full_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700458
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400459 switch ((u32) battery->technology) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 case 0:
461 seq_printf(seq, "battery technology: non-rechargeable\n");
462 break;
463 case 1:
464 seq_printf(seq, "battery technology: rechargeable\n");
465 break;
466 default:
467 seq_printf(seq, "battery technology: unknown\n");
468 break;
469 }
470
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400471 if (battery->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700472 seq_printf(seq, "design voltage: unknown\n");
473 else
474 seq_printf(seq, "design voltage: %d mV\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400475 (u32) battery->design_voltage);
Len Brown4be44fc2005-08-05 00:44:28 -0400476 seq_printf(seq, "design capacity warning: %d %sh\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400477 (u32) battery->design_capacity_warning, units);
Len Brown4be44fc2005-08-05 00:44:28 -0400478 seq_printf(seq, "design capacity low: %d %sh\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400479 (u32) battery->design_capacity_low, units);
Len Brown4be44fc2005-08-05 00:44:28 -0400480 seq_printf(seq, "capacity granularity 1: %d %sh\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400481 (u32) battery->capacity_granularity_1, units);
Len Brown4be44fc2005-08-05 00:44:28 -0400482 seq_printf(seq, "capacity granularity 2: %d %sh\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400483 (u32) battery->capacity_granularity_2, units);
484 seq_printf(seq, "model number: %s\n", battery->model_number);
485 seq_printf(seq, "serial number: %s\n", battery->serial_number);
486 seq_printf(seq, "battery type: %s\n", battery->type);
487 seq_printf(seq, "OEM info: %s\n", battery->oem_info);
Len Brown4be44fc2005-08-05 00:44:28 -0400488
489 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700490
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300491 if (result)
492 seq_printf(seq, "ERROR: Unable to read battery info\n");
493
494 return result;
495}
496
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400497static int acpi_battery_print_state(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700498{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200499 struct acpi_battery *battery = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400500 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300502 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700503 goto end;
504
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300505 if (acpi_battery_present(battery))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700506 seq_printf(seq, "present: yes\n");
507 else {
508 seq_printf(seq, "present: no\n");
509 goto end;
510 }
511
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300512 /* Battery Units */
513
514 units = acpi_battery_power_units(battery);
515
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400516 if (!(battery->state & 0x04))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 seq_printf(seq, "capacity state: ok\n");
518 else
519 seq_printf(seq, "capacity state: critical\n");
520
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400521 if ((battery->state & 0x01) && (battery->state & 0x02)) {
Len Brown4be44fc2005-08-05 00:44:28 -0400522 seq_printf(seq,
523 "charging state: charging/discharging\n");
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400524 } else if (battery->state & 0x01)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 seq_printf(seq, "charging state: discharging\n");
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400526 else if (battery->state & 0x02)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 seq_printf(seq, "charging state: charging\n");
528 else {
529 seq_printf(seq, "charging state: charged\n");
530 }
531
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400532 if (battery->present_rate == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700533 seq_printf(seq, "present rate: unknown\n");
534 else
535 seq_printf(seq, "present rate: %d %s\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400536 (u32) battery->present_rate, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400538 if (battery->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 seq_printf(seq, "remaining capacity: unknown\n");
540 else
541 seq_printf(seq, "remaining capacity: %d %sh\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400542 (u32) battery->remaining_capacity, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700543
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400544 if (battery->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700545 seq_printf(seq, "present voltage: unknown\n");
546 else
547 seq_printf(seq, "present voltage: %d mV\n",
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400548 (u32) battery->present_voltage);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700549
Len Brown4be44fc2005-08-05 00:44:28 -0400550 end:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700551
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300552 if (result) {
553 seq_printf(seq, "ERROR: Unable to read battery state\n");
554 }
555
556 return result;
557}
558
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400559static int acpi_battery_print_alarm(struct seq_file *seq, int result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700560{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200561 struct acpi_battery *battery = seq->private;
Len Brown4be44fc2005-08-05 00:44:28 -0400562 char *units = "?";
Linus Torvalds1da177e2005-04-16 15:20:36 -0700563
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300564 if (result)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 goto end;
566
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300567 if (!acpi_battery_present(battery)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 seq_printf(seq, "present: no\n");
569 goto end;
570 }
571
572 /* Battery Units */
Len Brown4be44fc2005-08-05 00:44:28 -0400573
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300574 units = acpi_battery_power_units(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575
576 seq_printf(seq, "alarm: ");
577 if (!battery->alarm)
578 seq_printf(seq, "unsupported\n");
579 else
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300580 seq_printf(seq, "%lu %sh\n", battery->alarm, units);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581
Len Brown4be44fc2005-08-05 00:44:28 -0400582 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300583
584 if (result)
585 seq_printf(seq, "ERROR: Unable to read battery alarm\n");
586
587 return result;
588}
589
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590static ssize_t
Len Brown4be44fc2005-08-05 00:44:28 -0400591acpi_battery_write_alarm(struct file *file,
592 const char __user * buffer,
593 size_t count, loff_t * ppos)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700594{
Len Brown4be44fc2005-08-05 00:44:28 -0400595 int result = 0;
596 char alarm_string[12] = { '\0' };
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200597 struct seq_file *m = file->private_data;
598 struct acpi_battery *battery = m->private;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300599 int update_result = ACPI_BATTERY_NONE_UPDATE;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600
Linus Torvalds1da177e2005-04-16 15:20:36 -0700601 if (!battery || (count > sizeof(alarm_string) - 1))
Patrick Mocheld550d982006-06-27 00:41:40 -0400602 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300604 result = acpi_battery_update(battery, 1, &update_result);
605 if (result) {
606 result = -ENODEV;
607 goto end;
608 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300610 if (!acpi_battery_present(battery)) {
611 result = -ENODEV;
612 goto end;
613 }
614
615 if (copy_from_user(alarm_string, buffer, count)) {
616 result = -EFAULT;
617 goto end;
618 }
Len Brown4be44fc2005-08-05 00:44:28 -0400619
Linus Torvalds1da177e2005-04-16 15:20:36 -0700620 alarm_string[count] = '\0';
621
Len Brown4be44fc2005-08-05 00:44:28 -0400622 result = acpi_battery_set_alarm(battery,
623 simple_strtoul(alarm_string, NULL, 0));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700624 if (result)
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300625 goto end;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700626
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300627 end:
628
629 acpi_battery_check_result(battery, result);
630
631 if (!result)
Len Brown5b31d892007-08-15 00:19:26 -0400632 result = count;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300633 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700634}
635
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400636typedef int(*print_func)(struct seq_file *seq, int result);
637typedef int(*get_func)(struct acpi_battery *battery);
638
639static struct acpi_read_mux {
640 print_func print;
641 get_func get;
642} acpi_read_funcs[ACPI_BATTERY_NUMFILES] = {
643 {.get = acpi_battery_get_info, .print = acpi_battery_print_info},
644 {.get = acpi_battery_get_state, .print = acpi_battery_print_state},
645 {.get = acpi_battery_get_alarm, .print = acpi_battery_print_alarm},
646};
647
648static int acpi_battery_read(int fid, struct seq_file *seq)
649{
650 struct acpi_battery *battery = seq->private;
651 int result = 0;
652 int update_result = ACPI_BATTERY_NONE_UPDATE;
653 int update = 0;
654
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400655 update = (get_seconds() - battery->update_time[fid] >= update_time);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400656 update = (update | battery->update[fid]);
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400657
658 result = acpi_battery_update(battery, update, &update_result);
659 if (result)
660 goto end;
661
662 if (update_result == ACPI_BATTERY_EASY_UPDATE) {
663 result = acpi_read_funcs[fid].get(battery);
664 if (result)
665 goto end;
666 }
667
668 end:
669 result = acpi_read_funcs[fid].print(seq, result);
670 acpi_battery_check_result(battery, result);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400671 battery->update[fid] = result;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400672 return result;
673}
674
675static int acpi_battery_read_info(struct seq_file *seq, void *offset)
676{
677 return acpi_battery_read(ACPI_BATTERY_INFO, seq);
678}
679
680static int acpi_battery_read_state(struct seq_file *seq, void *offset)
681{
682 return acpi_battery_read(ACPI_BATTERY_STATE, seq);
683}
684
685static int acpi_battery_read_alarm(struct seq_file *seq, void *offset)
686{
687 return acpi_battery_read(ACPI_BATTERY_ALARM, seq);
688}
689
690static int acpi_battery_info_open_fs(struct inode *inode, struct file *file)
691{
692 return single_open(file, acpi_battery_read_info, PDE(inode)->data);
693}
694
695static int acpi_battery_state_open_fs(struct inode *inode, struct file *file)
696{
697 return single_open(file, acpi_battery_read_state, PDE(inode)->data);
698}
699
Linus Torvalds1da177e2005-04-16 15:20:36 -0700700static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file)
701{
702 return single_open(file, acpi_battery_read_alarm, PDE(inode)->data);
703}
704
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400705static struct battery_file {
706 struct file_operations ops;
707 mode_t mode;
708 char *name;
709} acpi_battery_file[] = {
710 {
711 .name = "info",
712 .mode = S_IRUGO,
713 .ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400714 .open = acpi_battery_info_open_fs,
715 .read = seq_read,
716 .llseek = seq_lseek,
717 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700718 .owner = THIS_MODULE,
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400719 },
720 },
721 {
722 .name = "state",
723 .mode = S_IRUGO,
724 .ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400725 .open = acpi_battery_state_open_fs,
726 .read = seq_read,
727 .llseek = seq_lseek,
728 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 .owner = THIS_MODULE,
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400730 },
731 },
732 {
733 .name = "alarm",
734 .mode = S_IFREG | S_IRUGO | S_IWUSR,
735 .ops = {
Len Brown4be44fc2005-08-05 00:44:28 -0400736 .open = acpi_battery_alarm_open_fs,
737 .read = seq_read,
738 .write = acpi_battery_write_alarm,
739 .llseek = seq_lseek,
740 .release = single_release,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741 .owner = THIS_MODULE,
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400742 },
743 },
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744};
745
Len Brown4be44fc2005-08-05 00:44:28 -0400746static int acpi_battery_add_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700747{
Len Brown4be44fc2005-08-05 00:44:28 -0400748 struct proc_dir_entry *entry = NULL;
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400749 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 if (!acpi_device_dir(device)) {
752 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
Len Brown4be44fc2005-08-05 00:44:28 -0400753 acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 if (!acpi_device_dir(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400755 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700756 acpi_device_dir(device)->owner = THIS_MODULE;
757 }
758
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400759 for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) {
760 entry = create_proc_entry(acpi_battery_file[i].name,
761 acpi_battery_file[i].mode, acpi_device_dir(device));
762 if (!entry)
763 return -ENODEV;
764 else {
765 entry->proc_fops = &acpi_battery_file[i].ops;
766 entry->data = acpi_driver_data(device);
767 entry->owner = THIS_MODULE;
768 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700769 }
770
Patrick Mocheld550d982006-06-27 00:41:40 -0400771 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772}
773
Len Brown4be44fc2005-08-05 00:44:28 -0400774static int acpi_battery_remove_fs(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775{
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400776 int i;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777 if (acpi_device_dir(device)) {
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400778 for (i = 0; i < ACPI_BATTERY_NUMFILES; ++i) {
779 remove_proc_entry(acpi_battery_file[i].name,
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780 acpi_device_dir(device));
Alexey Starikovskiy78490d82007-05-11 13:18:55 -0400781 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700782 remove_proc_entry(acpi_device_bid(device), acpi_battery_dir);
783 acpi_device_dir(device) = NULL;
784 }
785
Patrick Mocheld550d982006-06-27 00:41:40 -0400786 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700787}
788
Linus Torvalds1da177e2005-04-16 15:20:36 -0700789/* --------------------------------------------------------------------------
790 Driver Interface
791 -------------------------------------------------------------------------- */
792
Len Brown4be44fc2005-08-05 00:44:28 -0400793static void acpi_battery_notify(acpi_handle handle, u32 event, void *data)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794{
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200795 struct acpi_battery *battery = data;
Len Brown4be44fc2005-08-05 00:44:28 -0400796 struct acpi_device *device = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400799 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700800
Patrick Mochel145def82006-05-19 16:54:39 -0400801 device = battery->device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802
803 switch (event) {
804 case ACPI_BATTERY_NOTIFY_STATUS:
805 case ACPI_BATTERY_NOTIFY_INFO:
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400806 case ACPI_NOTIFY_BUS_CHECK:
807 case ACPI_NOTIFY_DEVICE_CHECK:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300808 device = battery->device;
809 acpi_battery_notify_update(battery);
Len Brown14e04fb2007-08-23 15:20:26 -0400810 acpi_bus_generate_proc_event(device, event,
Vladimir Lebedev9ea7d572007-02-20 15:48:06 +0300811 acpi_battery_present(battery));
Zhang Rui962ce8c2007-08-23 01:24:31 +0800812 acpi_bus_generate_netlink_event(device->pnp.device_class,
813 device->dev.bus_id, event,
814 acpi_battery_present(battery));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700815 break;
816 default:
817 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
Len Brown4be44fc2005-08-05 00:44:28 -0400818 "Unsupported event [0x%x]\n", event));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 break;
820 }
821
Patrick Mocheld550d982006-06-27 00:41:40 -0400822 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823}
824
Len Brown4be44fc2005-08-05 00:44:28 -0400825static int acpi_battery_add(struct acpi_device *device)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826{
Len Brown4be44fc2005-08-05 00:44:28 -0400827 int result = 0;
828 acpi_status status = 0;
829 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Linus Torvalds1da177e2005-04-16 15:20:36 -0700831 if (!device)
Patrick Mocheld550d982006-06-27 00:41:40 -0400832 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Burman Yan36bcbec2006-12-19 12:56:11 -0800834 battery = kzalloc(sizeof(struct acpi_battery), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700835 if (!battery)
Patrick Mocheld550d982006-06-27 00:41:40 -0400836 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700837
Patrick Mochel145def82006-05-19 16:54:39 -0400838 battery->device = device;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700839 strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME);
840 strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS);
841 acpi_driver_data(device) = battery;
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400842 mutex_init(&battery->lock);
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300843 result = acpi_battery_get_status(battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700844 if (result)
845 goto end;
846
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400847 battery->init_update = 1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300848
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 result = acpi_battery_add_fs(device);
850 if (result)
851 goto end;
852
Patrick Mochel3b073ec2006-05-19 16:54:41 -0400853 status = acpi_install_notify_handler(device->handle,
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400854 ACPI_ALL_NOTIFY,
Len Brown4be44fc2005-08-05 00:44:28 -0400855 acpi_battery_notify, battery);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856 if (ACPI_FAILURE(status)) {
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300857 ACPI_EXCEPTION((AE_INFO, status, "Installing notify handler"));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 result = -ENODEV;
859 goto end;
860 }
861
862 printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n",
Len Brown4be44fc2005-08-05 00:44:28 -0400863 ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device),
864 device->status.battery_present ? "present" : "absent");
865
866 end:
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300867
Linus Torvalds1da177e2005-04-16 15:20:36 -0700868 if (result) {
869 acpi_battery_remove_fs(device);
870 kfree(battery);
871 }
872
Patrick Mocheld550d982006-06-27 00:41:40 -0400873 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700874}
875
Len Brown4be44fc2005-08-05 00:44:28 -0400876static int acpi_battery_remove(struct acpi_device *device, int type)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700877{
Len Brown4be44fc2005-08-05 00:44:28 -0400878 acpi_status status = 0;
879 struct acpi_battery *battery = NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700880
Linus Torvalds1da177e2005-04-16 15:20:36 -0700881 if (!device || !acpi_driver_data(device))
Patrick Mocheld550d982006-06-27 00:41:40 -0400882 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700883
Jan Engelhardt50dd0962006-10-01 00:28:50 +0200884 battery = acpi_driver_data(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885
Patrick Mochel3b073ec2006-05-19 16:54:41 -0400886 status = acpi_remove_notify_handler(device->handle,
Vladimir Lebedev9fdae722006-06-27 04:49:00 -0400887 ACPI_ALL_NOTIFY,
Len Brown4be44fc2005-08-05 00:44:28 -0400888 acpi_battery_notify);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700889
890 acpi_battery_remove_fs(device);
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400891 mutex_destroy(&battery->lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700892 kfree(battery);
893
Patrick Mocheld550d982006-06-27 00:41:40 -0400894 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700895}
896
Jiri Kosina34c44152006-10-10 14:20:41 -0700897/* this is needed to learn about changes made in suspended state */
Patrick Mochel5d9464a2006-12-07 20:56:27 +0800898static int acpi_battery_resume(struct acpi_device *device)
Jiri Kosina34c44152006-10-10 14:20:41 -0700899{
900 struct acpi_battery *battery;
901
902 if (!device)
903 return -EINVAL;
904
905 battery = device->driver_data;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300906
Alexey Starikovskiy038fdea2007-09-26 19:42:46 +0400907 battery->init_update = 1;
Vladimir Lebedevb6ce4082007-02-20 15:48:06 +0300908
909 return 0;
Jiri Kosina34c44152006-10-10 14:20:41 -0700910}
911
Len Brown4be44fc2005-08-05 00:44:28 -0400912static int __init acpi_battery_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700913{
Rich Townsend3f86b832006-07-01 11:36:54 -0400914 int result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700915
Pavel Machek4d8316d2006-08-14 22:37:22 -0700916 if (acpi_disabled)
917 return -ENODEV;
918
Rich Townsend3f86b832006-07-01 11:36:54 -0400919 acpi_battery_dir = acpi_lock_battery_dir();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700920 if (!acpi_battery_dir)
Patrick Mocheld550d982006-06-27 00:41:40 -0400921 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
923 result = acpi_bus_register_driver(&acpi_battery_driver);
924 if (result < 0) {
Rich Townsend3f86b832006-07-01 11:36:54 -0400925 acpi_unlock_battery_dir(acpi_battery_dir);
Patrick Mocheld550d982006-06-27 00:41:40 -0400926 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700927 }
928
Patrick Mocheld550d982006-06-27 00:41:40 -0400929 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700930}
931
Len Brown4be44fc2005-08-05 00:44:28 -0400932static void __exit acpi_battery_exit(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700933{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 acpi_bus_unregister_driver(&acpi_battery_driver);
935
Rich Townsend3f86b832006-07-01 11:36:54 -0400936 acpi_unlock_battery_dir(acpi_battery_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
Patrick Mocheld550d982006-06-27 00:41:40 -0400938 return;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700939}
940
Linus Torvalds1da177e2005-04-16 15:20:36 -0700941module_init(acpi_battery_init);
942module_exit(acpi_battery_exit);