diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/acpi/battery.c |
Linux-2.6.12-rc2v2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/acpi/battery.c')
-rw-r--r-- | drivers/acpi/battery.c | 846 |
1 files changed, 846 insertions, 0 deletions
diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c new file mode 100644 index 000000000000..c55feca9b7d5 --- /dev/null +++ b/drivers/acpi/battery.c | |||
@@ -0,0 +1,846 @@ | |||
1 | /* | ||
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 | |||
37 | |||
38 | #define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF | ||
39 | |||
40 | #define ACPI_BATTERY_FORMAT_BIF "NNNNNNNNNSSSS" | ||
41 | #define ACPI_BATTERY_FORMAT_BST "NNNN" | ||
42 | |||
43 | #define ACPI_BATTERY_COMPONENT 0x00040000 | ||
44 | #define ACPI_BATTERY_CLASS "battery" | ||
45 | #define ACPI_BATTERY_HID "PNP0C0A" | ||
46 | #define ACPI_BATTERY_DRIVER_NAME "ACPI Battery Driver" | ||
47 | #define ACPI_BATTERY_DEVICE_NAME "Battery" | ||
48 | #define ACPI_BATTERY_FILE_INFO "info" | ||
49 | #define ACPI_BATTERY_FILE_STATUS "state" | ||
50 | #define ACPI_BATTERY_FILE_ALARM "alarm" | ||
51 | #define ACPI_BATTERY_NOTIFY_STATUS 0x80 | ||
52 | #define ACPI_BATTERY_NOTIFY_INFO 0x81 | ||
53 | #define ACPI_BATTERY_UNITS_WATTS "mW" | ||
54 | #define ACPI_BATTERY_UNITS_AMPS "mA" | ||
55 | |||
56 | |||
57 | #define _COMPONENT ACPI_BATTERY_COMPONENT | ||
58 | ACPI_MODULE_NAME ("acpi_battery") | ||
59 | |||
60 | MODULE_AUTHOR("Paul Diefenbaugh"); | ||
61 | MODULE_DESCRIPTION(ACPI_BATTERY_DRIVER_NAME); | ||
62 | MODULE_LICENSE("GPL"); | ||
63 | |||
64 | static int acpi_battery_add (struct acpi_device *device); | ||
65 | static int acpi_battery_remove (struct acpi_device *device, int type); | ||
66 | |||
67 | static struct acpi_driver acpi_battery_driver = { | ||
68 | .name = ACPI_BATTERY_DRIVER_NAME, | ||
69 | .class = ACPI_BATTERY_CLASS, | ||
70 | .ids = ACPI_BATTERY_HID, | ||
71 | .ops = { | ||
72 | .add = acpi_battery_add, | ||
73 | .remove = acpi_battery_remove, | ||
74 | }, | ||
75 | }; | ||
76 | |||
77 | struct acpi_battery_status { | ||
78 | acpi_integer state; | ||
79 | acpi_integer present_rate; | ||
80 | acpi_integer remaining_capacity; | ||
81 | acpi_integer present_voltage; | ||
82 | }; | ||
83 | |||
84 | struct acpi_battery_info { | ||
85 | acpi_integer power_unit; | ||
86 | acpi_integer design_capacity; | ||
87 | acpi_integer last_full_capacity; | ||
88 | acpi_integer battery_technology; | ||
89 | acpi_integer design_voltage; | ||
90 | acpi_integer design_capacity_warning; | ||
91 | acpi_integer design_capacity_low; | ||
92 | acpi_integer battery_capacity_granularity_1; | ||
93 | acpi_integer battery_capacity_granularity_2; | ||
94 | acpi_string model_number; | ||
95 | acpi_string serial_number; | ||
96 | acpi_string battery_type; | ||
97 | acpi_string oem_info; | ||
98 | }; | ||
99 | |||
100 | struct acpi_battery_flags { | ||
101 | u8 present:1; /* Bay occupied? */ | ||
102 | u8 power_unit:1; /* 0=watts, 1=apms */ | ||
103 | u8 alarm:1; /* _BTP present? */ | ||
104 | u8 reserved:5; | ||
105 | }; | ||
106 | |||
107 | struct acpi_battery_trips { | ||
108 | unsigned long warning; | ||
109 | unsigned long low; | ||
110 | }; | ||
111 | |||
112 | struct acpi_battery { | ||
113 | acpi_handle handle; | ||
114 | struct acpi_battery_flags flags; | ||
115 | struct acpi_battery_trips trips; | ||
116 | unsigned long alarm; | ||
117 | struct acpi_battery_info *info; | ||
118 | }; | ||
119 | |||
120 | |||
121 | /* -------------------------------------------------------------------------- | ||
122 | Battery Management | ||
123 | -------------------------------------------------------------------------- */ | ||
124 | |||
125 | static int | ||
126 | acpi_battery_get_info ( | ||
127 | struct acpi_battery *battery, | ||
128 | struct acpi_battery_info **bif) | ||
129 | { | ||
130 | int result = 0; | ||
131 | acpi_status status = 0; | ||
132 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
133 | struct acpi_buffer format = {sizeof(ACPI_BATTERY_FORMAT_BIF), | ||
134 | ACPI_BATTERY_FORMAT_BIF}; | ||
135 | struct acpi_buffer data = {0, NULL}; | ||
136 | union acpi_object *package = NULL; | ||
137 | |||
138 | ACPI_FUNCTION_TRACE("acpi_battery_get_info"); | ||
139 | |||
140 | if (!battery || !bif) | ||
141 | return_VALUE(-EINVAL); | ||
142 | |||
143 | /* Evalute _BIF */ | ||
144 | |||
145 | status = acpi_evaluate_object(battery->handle, "_BIF", NULL, &buffer); | ||
146 | if (ACPI_FAILURE(status)) { | ||
147 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _BIF\n")); | ||
148 | return_VALUE(-ENODEV); | ||
149 | } | ||
150 | |||
151 | package = (union acpi_object *) buffer.pointer; | ||
152 | |||
153 | /* Extract Package Data */ | ||
154 | |||
155 | status = acpi_extract_package(package, &format, &data); | ||
156 | if (status != AE_BUFFER_OVERFLOW) { | ||
157 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _BIF\n")); | ||
158 | result = -ENODEV; | ||
159 | goto end; | ||
160 | } | ||
161 | |||
162 | data.pointer = kmalloc(data.length, GFP_KERNEL); | ||
163 | if (!data.pointer) { | ||
164 | result = -ENOMEM; | ||
165 | goto end; | ||
166 | } | ||
167 | memset(data.pointer, 0, data.length); | ||
168 | |||
169 | status = acpi_extract_package(package, &format, &data); | ||
170 | if (ACPI_FAILURE(status)) { | ||
171 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _BIF\n")); | ||
172 | kfree(data.pointer); | ||
173 | result = -ENODEV; | ||
174 | goto end; | ||
175 | } | ||
176 | |||
177 | end: | ||
178 | acpi_os_free(buffer.pointer); | ||
179 | |||
180 | if (!result) | ||
181 | (*bif) = (struct acpi_battery_info *) data.pointer; | ||
182 | |||
183 | return_VALUE(result); | ||
184 | } | ||
185 | |||
186 | static int | ||
187 | acpi_battery_get_status ( | ||
188 | struct acpi_battery *battery, | ||
189 | struct acpi_battery_status **bst) | ||
190 | { | ||
191 | int result = 0; | ||
192 | acpi_status status = 0; | ||
193 | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | ||
194 | struct acpi_buffer format = {sizeof(ACPI_BATTERY_FORMAT_BST), | ||
195 | ACPI_BATTERY_FORMAT_BST}; | ||
196 | struct acpi_buffer data = {0, NULL}; | ||
197 | union acpi_object *package = NULL; | ||
198 | |||
199 | ACPI_FUNCTION_TRACE("acpi_battery_get_status"); | ||
200 | |||
201 | if (!battery || !bst) | ||
202 | return_VALUE(-EINVAL); | ||
203 | |||
204 | /* Evalute _BST */ | ||
205 | |||
206 | status = acpi_evaluate_object(battery->handle, "_BST", NULL, &buffer); | ||
207 | if (ACPI_FAILURE(status)) { | ||
208 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error evaluating _BST\n")); | ||
209 | return_VALUE(-ENODEV); | ||
210 | } | ||
211 | |||
212 | package = (union acpi_object *) buffer.pointer; | ||
213 | |||
214 | /* Extract Package Data */ | ||
215 | |||
216 | status = acpi_extract_package(package, &format, &data); | ||
217 | if (status != AE_BUFFER_OVERFLOW) { | ||
218 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _BST\n")); | ||
219 | result = -ENODEV; | ||
220 | goto end; | ||
221 | } | ||
222 | |||
223 | data.pointer = kmalloc(data.length, GFP_KERNEL); | ||
224 | if (!data.pointer) { | ||
225 | result = -ENOMEM; | ||
226 | goto end; | ||
227 | } | ||
228 | memset(data.pointer, 0, data.length); | ||
229 | |||
230 | status = acpi_extract_package(package, &format, &data); | ||
231 | if (ACPI_FAILURE(status)) { | ||
232 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Error extracting _BST\n")); | ||
233 | kfree(data.pointer); | ||
234 | result = -ENODEV; | ||
235 | goto end; | ||
236 | } | ||
237 | |||
238 | end: | ||
239 | acpi_os_free(buffer.pointer); | ||
240 | |||
241 | if (!result) | ||
242 | (*bst) = (struct acpi_battery_status *) data.pointer; | ||
243 | |||
244 | return_VALUE(result); | ||
245 | } | ||
246 | |||
247 | |||
248 | static int | ||
249 | acpi_battery_set_alarm ( | ||
250 | struct acpi_battery *battery, | ||
251 | unsigned long alarm) | ||
252 | { | ||
253 | acpi_status status = 0; | ||
254 | union acpi_object arg0 = {ACPI_TYPE_INTEGER}; | ||
255 | struct acpi_object_list arg_list = {1, &arg0}; | ||
256 | |||
257 | ACPI_FUNCTION_TRACE("acpi_battery_set_alarm"); | ||
258 | |||
259 | if (!battery) | ||
260 | return_VALUE(-EINVAL); | ||
261 | |||
262 | if (!battery->flags.alarm) | ||
263 | return_VALUE(-ENODEV); | ||
264 | |||
265 | arg0.integer.value = alarm; | ||
266 | |||
267 | status = acpi_evaluate_object(battery->handle, "_BTP", &arg_list, NULL); | ||
268 | if (ACPI_FAILURE(status)) | ||
269 | return_VALUE(-ENODEV); | ||
270 | |||
271 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Alarm set to %d\n", (u32) alarm)); | ||
272 | |||
273 | battery->alarm = alarm; | ||
274 | |||
275 | return_VALUE(0); | ||
276 | } | ||
277 | |||
278 | |||
279 | static int | ||
280 | acpi_battery_check ( | ||
281 | struct acpi_battery *battery) | ||
282 | { | ||
283 | int result = 0; | ||
284 | acpi_status status = AE_OK; | ||
285 | acpi_handle handle = NULL; | ||
286 | struct acpi_device *device = NULL; | ||
287 | struct acpi_battery_info *bif = NULL; | ||
288 | |||
289 | ACPI_FUNCTION_TRACE("acpi_battery_check"); | ||
290 | |||
291 | if (!battery) | ||
292 | return_VALUE(-EINVAL); | ||
293 | |||
294 | result = acpi_bus_get_device(battery->handle, &device); | ||
295 | if (result) | ||
296 | return_VALUE(result); | ||
297 | |||
298 | result = acpi_bus_get_status(device); | ||
299 | if (result) | ||
300 | return_VALUE(result); | ||
301 | |||
302 | /* Insertion? */ | ||
303 | |||
304 | if (!battery->flags.present && device->status.battery_present) { | ||
305 | |||
306 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery inserted\n")); | ||
307 | |||
308 | /* Evalute _BIF to get certain static information */ | ||
309 | |||
310 | result = acpi_battery_get_info(battery, &bif); | ||
311 | if (result) | ||
312 | return_VALUE(result); | ||
313 | |||
314 | battery->flags.power_unit = bif->power_unit; | ||
315 | battery->trips.warning = bif->design_capacity_warning; | ||
316 | battery->trips.low = bif->design_capacity_low; | ||
317 | kfree(bif); | ||
318 | |||
319 | /* See if alarms are supported, and if so, set default */ | ||
320 | |||
321 | status = acpi_get_handle(battery->handle, "_BTP", &handle); | ||
322 | if (ACPI_SUCCESS(status)) { | ||
323 | battery->flags.alarm = 1; | ||
324 | acpi_battery_set_alarm(battery, battery->trips.warning); | ||
325 | } | ||
326 | } | ||
327 | |||
328 | /* Removal? */ | ||
329 | |||
330 | else if (battery->flags.present && !device->status.battery_present) { | ||
331 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Battery removed\n")); | ||
332 | } | ||
333 | |||
334 | battery->flags.present = device->status.battery_present; | ||
335 | |||
336 | return_VALUE(result); | ||
337 | } | ||
338 | |||
339 | |||
340 | /* -------------------------------------------------------------------------- | ||
341 | FS Interface (/proc) | ||
342 | -------------------------------------------------------------------------- */ | ||
343 | |||
344 | static struct proc_dir_entry *acpi_battery_dir; | ||
345 | static int acpi_battery_read_info(struct seq_file *seq, void *offset) | ||
346 | { | ||
347 | int result = 0; | ||
348 | struct acpi_battery *battery = (struct acpi_battery *) seq->private; | ||
349 | struct acpi_battery_info *bif = NULL; | ||
350 | char *units = "?"; | ||
351 | |||
352 | ACPI_FUNCTION_TRACE("acpi_battery_read_info"); | ||
353 | |||
354 | if (!battery) | ||
355 | goto end; | ||
356 | |||
357 | if (battery->flags.present) | ||
358 | seq_printf(seq, "present: yes\n"); | ||
359 | else { | ||
360 | seq_printf(seq, "present: no\n"); | ||
361 | goto end; | ||
362 | } | ||
363 | |||
364 | /* Battery Info (_BIF) */ | ||
365 | |||
366 | result = acpi_battery_get_info(battery, &bif); | ||
367 | if (result || !bif) { | ||
368 | seq_printf(seq, "ERROR: Unable to read battery information\n"); | ||
369 | goto end; | ||
370 | } | ||
371 | |||
372 | units = bif->power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; | ||
373 | |||
374 | if (bif->design_capacity == ACPI_BATTERY_VALUE_UNKNOWN) | ||
375 | seq_printf(seq, "design capacity: unknown\n"); | ||
376 | else | ||
377 | seq_printf(seq, "design capacity: %d %sh\n", | ||
378 | (u32) bif->design_capacity, units); | ||
379 | |||
380 | if (bif->last_full_capacity == ACPI_BATTERY_VALUE_UNKNOWN) | ||
381 | seq_printf(seq, "last full capacity: unknown\n"); | ||
382 | else | ||
383 | seq_printf(seq, "last full capacity: %d %sh\n", | ||
384 | (u32) bif->last_full_capacity, units); | ||
385 | |||
386 | switch ((u32) bif->battery_technology) { | ||
387 | case 0: | ||
388 | seq_printf(seq, "battery technology: non-rechargeable\n"); | ||
389 | break; | ||
390 | case 1: | ||
391 | seq_printf(seq, "battery technology: rechargeable\n"); | ||
392 | break; | ||
393 | default: | ||
394 | seq_printf(seq, "battery technology: unknown\n"); | ||
395 | break; | ||
396 | } | ||
397 | |||
398 | if (bif->design_voltage == ACPI_BATTERY_VALUE_UNKNOWN) | ||
399 | seq_printf(seq, "design voltage: unknown\n"); | ||
400 | else | ||
401 | seq_printf(seq, "design voltage: %d mV\n", | ||
402 | (u32) bif->design_voltage); | ||
403 | |||
404 | seq_printf(seq, "design capacity warning: %d %sh\n", | ||
405 | (u32) bif->design_capacity_warning, units); | ||
406 | seq_printf(seq, "design capacity low: %d %sh\n", | ||
407 | (u32) bif->design_capacity_low, units); | ||
408 | seq_printf(seq, "capacity granularity 1: %d %sh\n", | ||
409 | (u32) bif->battery_capacity_granularity_1, units); | ||
410 | seq_printf(seq, "capacity granularity 2: %d %sh\n", | ||
411 | (u32) bif->battery_capacity_granularity_2, units); | ||
412 | seq_printf(seq, "model number: %s\n", | ||
413 | bif->model_number); | ||
414 | seq_printf(seq, "serial number: %s\n", | ||
415 | bif->serial_number); | ||
416 | seq_printf(seq, "battery type: %s\n", | ||
417 | bif->battery_type); | ||
418 | seq_printf(seq, "OEM info: %s\n", | ||
419 | bif->oem_info); | ||
420 | |||
421 | end: | ||
422 | kfree(bif); | ||
423 | |||
424 | return_VALUE(0); | ||
425 | } | ||
426 | |||
427 | static int acpi_battery_info_open_fs(struct inode *inode, struct file *file) | ||
428 | { | ||
429 | return single_open(file, acpi_battery_read_info, PDE(inode)->data); | ||
430 | } | ||
431 | |||
432 | |||
433 | static int | ||
434 | acpi_battery_read_state (struct seq_file *seq, void *offset) | ||
435 | { | ||
436 | int result = 0; | ||
437 | struct acpi_battery *battery = (struct acpi_battery *) seq->private; | ||
438 | struct acpi_battery_status *bst = NULL; | ||
439 | char *units = "?"; | ||
440 | |||
441 | ACPI_FUNCTION_TRACE("acpi_battery_read_state"); | ||
442 | |||
443 | if (!battery) | ||
444 | goto end; | ||
445 | |||
446 | if (battery->flags.present) | ||
447 | seq_printf(seq, "present: yes\n"); | ||
448 | else { | ||
449 | seq_printf(seq, "present: no\n"); | ||
450 | goto end; | ||
451 | } | ||
452 | |||
453 | /* Battery Units */ | ||
454 | |||
455 | units = battery->flags.power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; | ||
456 | |||
457 | /* Battery Status (_BST) */ | ||
458 | |||
459 | result = acpi_battery_get_status(battery, &bst); | ||
460 | if (result || !bst) { | ||
461 | seq_printf(seq, "ERROR: Unable to read battery status\n"); | ||
462 | goto end; | ||
463 | } | ||
464 | |||
465 | if (!(bst->state & 0x04)) | ||
466 | seq_printf(seq, "capacity state: ok\n"); | ||
467 | else | ||
468 | seq_printf(seq, "capacity state: critical\n"); | ||
469 | |||
470 | if ((bst->state & 0x01) && (bst->state & 0x02)){ | ||
471 | seq_printf(seq, "charging state: charging/discharging\n"); | ||
472 | ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, | ||
473 | "Battery Charging and Discharging?\n")); | ||
474 | } | ||
475 | else if (bst->state & 0x01) | ||
476 | seq_printf(seq, "charging state: discharging\n"); | ||
477 | else if (bst->state & 0x02) | ||
478 | seq_printf(seq, "charging state: charging\n"); | ||
479 | else { | ||
480 | seq_printf(seq, "charging state: charged\n"); | ||
481 | } | ||
482 | |||
483 | if (bst->present_rate == ACPI_BATTERY_VALUE_UNKNOWN) | ||
484 | seq_printf(seq, "present rate: unknown\n"); | ||
485 | else | ||
486 | seq_printf(seq, "present rate: %d %s\n", | ||
487 | (u32) bst->present_rate, units); | ||
488 | |||
489 | if (bst->remaining_capacity == ACPI_BATTERY_VALUE_UNKNOWN) | ||
490 | seq_printf(seq, "remaining capacity: unknown\n"); | ||
491 | else | ||
492 | seq_printf(seq, "remaining capacity: %d %sh\n", | ||
493 | (u32) bst->remaining_capacity, units); | ||
494 | |||
495 | if (bst->present_voltage == ACPI_BATTERY_VALUE_UNKNOWN) | ||
496 | seq_printf(seq, "present voltage: unknown\n"); | ||
497 | else | ||
498 | seq_printf(seq, "present voltage: %d mV\n", | ||
499 | (u32) bst->present_voltage); | ||
500 | |||
501 | end: | ||
502 | kfree(bst); | ||
503 | |||
504 | return_VALUE(0); | ||
505 | } | ||
506 | |||
507 | static int acpi_battery_state_open_fs(struct inode *inode, struct file *file) | ||
508 | { | ||
509 | return single_open(file, acpi_battery_read_state, PDE(inode)->data); | ||
510 | } | ||
511 | |||
512 | |||
513 | static int | ||
514 | acpi_battery_read_alarm (struct seq_file *seq, void *offset) | ||
515 | { | ||
516 | struct acpi_battery *battery = (struct acpi_battery *) seq->private; | ||
517 | char *units = "?"; | ||
518 | |||
519 | ACPI_FUNCTION_TRACE("acpi_battery_read_alarm"); | ||
520 | |||
521 | if (!battery) | ||
522 | goto end; | ||
523 | |||
524 | if (!battery->flags.present) { | ||
525 | seq_printf(seq, "present: no\n"); | ||
526 | goto end; | ||
527 | } | ||
528 | |||
529 | /* Battery Units */ | ||
530 | |||
531 | units = battery->flags.power_unit ? ACPI_BATTERY_UNITS_AMPS : ACPI_BATTERY_UNITS_WATTS; | ||
532 | |||
533 | /* Battery Alarm */ | ||
534 | |||
535 | seq_printf(seq, "alarm: "); | ||
536 | if (!battery->alarm) | ||
537 | seq_printf(seq, "unsupported\n"); | ||
538 | else | ||
539 | seq_printf(seq, "%d %sh\n", (u32) battery->alarm, units); | ||
540 | |||
541 | end: | ||
542 | return_VALUE(0); | ||
543 | } | ||
544 | |||
545 | |||
546 | static ssize_t | ||
547 | acpi_battery_write_alarm ( | ||
548 | struct file *file, | ||
549 | const char __user *buffer, | ||
550 | size_t count, | ||
551 | loff_t *ppos) | ||
552 | { | ||
553 | int result = 0; | ||
554 | char alarm_string[12] = {'\0'}; | ||
555 | struct seq_file *m = (struct seq_file *)file->private_data; | ||
556 | struct acpi_battery *battery = (struct acpi_battery *)m->private; | ||
557 | |||
558 | ACPI_FUNCTION_TRACE("acpi_battery_write_alarm"); | ||
559 | |||
560 | if (!battery || (count > sizeof(alarm_string) - 1)) | ||
561 | return_VALUE(-EINVAL); | ||
562 | |||
563 | if (!battery->flags.present) | ||
564 | return_VALUE(-ENODEV); | ||
565 | |||
566 | if (copy_from_user(alarm_string, buffer, count)) | ||
567 | return_VALUE(-EFAULT); | ||
568 | |||
569 | alarm_string[count] = '\0'; | ||
570 | |||
571 | result = acpi_battery_set_alarm(battery, | ||
572 | simple_strtoul(alarm_string, NULL, 0)); | ||
573 | if (result) | ||
574 | return_VALUE(result); | ||
575 | |||
576 | return_VALUE(count); | ||
577 | } | ||
578 | |||
579 | static int acpi_battery_alarm_open_fs(struct inode *inode, struct file *file) | ||
580 | { | ||
581 | return single_open(file, acpi_battery_read_alarm, PDE(inode)->data); | ||
582 | } | ||
583 | |||
584 | static struct file_operations acpi_battery_info_ops = { | ||
585 | .open = acpi_battery_info_open_fs, | ||
586 | .read = seq_read, | ||
587 | .llseek = seq_lseek, | ||
588 | .release = single_release, | ||
589 | .owner = THIS_MODULE, | ||
590 | }; | ||
591 | |||
592 | static struct file_operations acpi_battery_state_ops = { | ||
593 | .open = acpi_battery_state_open_fs, | ||
594 | .read = seq_read, | ||
595 | .llseek = seq_lseek, | ||
596 | .release = single_release, | ||
597 | .owner = THIS_MODULE, | ||
598 | }; | ||
599 | |||
600 | static struct file_operations acpi_battery_alarm_ops = { | ||
601 | .open = acpi_battery_alarm_open_fs, | ||
602 | .read = seq_read, | ||
603 | .write = acpi_battery_write_alarm, | ||
604 | .llseek = seq_lseek, | ||
605 | .release = single_release, | ||
606 | .owner = THIS_MODULE, | ||
607 | }; | ||
608 | |||
609 | static int | ||
610 | acpi_battery_add_fs ( | ||
611 | struct acpi_device *device) | ||
612 | { | ||
613 | struct proc_dir_entry *entry = NULL; | ||
614 | |||
615 | ACPI_FUNCTION_TRACE("acpi_battery_add_fs"); | ||
616 | |||
617 | if (!acpi_device_dir(device)) { | ||
618 | acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), | ||
619 | acpi_battery_dir); | ||
620 | if (!acpi_device_dir(device)) | ||
621 | return_VALUE(-ENODEV); | ||
622 | acpi_device_dir(device)->owner = THIS_MODULE; | ||
623 | } | ||
624 | |||
625 | /* 'info' [R] */ | ||
626 | entry = create_proc_entry(ACPI_BATTERY_FILE_INFO, | ||
627 | S_IRUGO, acpi_device_dir(device)); | ||
628 | if (!entry) | ||
629 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
630 | "Unable to create '%s' fs entry\n", | ||
631 | ACPI_BATTERY_FILE_INFO)); | ||
632 | else { | ||
633 | entry->proc_fops = &acpi_battery_info_ops; | ||
634 | entry->data = acpi_driver_data(device); | ||
635 | entry->owner = THIS_MODULE; | ||
636 | } | ||
637 | |||
638 | /* 'status' [R] */ | ||
639 | entry = create_proc_entry(ACPI_BATTERY_FILE_STATUS, | ||
640 | S_IRUGO, acpi_device_dir(device)); | ||
641 | if (!entry) | ||
642 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
643 | "Unable to create '%s' fs entry\n", | ||
644 | ACPI_BATTERY_FILE_STATUS)); | ||
645 | else { | ||
646 | entry->proc_fops = &acpi_battery_state_ops; | ||
647 | entry->data = acpi_driver_data(device); | ||
648 | entry->owner = THIS_MODULE; | ||
649 | } | ||
650 | |||
651 | /* 'alarm' [R/W] */ | ||
652 | entry = create_proc_entry(ACPI_BATTERY_FILE_ALARM, | ||
653 | S_IFREG|S_IRUGO|S_IWUSR, acpi_device_dir(device)); | ||
654 | if (!entry) | ||
655 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
656 | "Unable to create '%s' fs entry\n", | ||
657 | ACPI_BATTERY_FILE_ALARM)); | ||
658 | else { | ||
659 | entry->proc_fops = &acpi_battery_alarm_ops; | ||
660 | entry->data = acpi_driver_data(device); | ||
661 | entry->owner = THIS_MODULE; | ||
662 | } | ||
663 | |||
664 | return_VALUE(0); | ||
665 | } | ||
666 | |||
667 | |||
668 | static int | ||
669 | acpi_battery_remove_fs ( | ||
670 | struct acpi_device *device) | ||
671 | { | ||
672 | ACPI_FUNCTION_TRACE("acpi_battery_remove_fs"); | ||
673 | |||
674 | if (acpi_device_dir(device)) { | ||
675 | remove_proc_entry(ACPI_BATTERY_FILE_ALARM, | ||
676 | acpi_device_dir(device)); | ||
677 | remove_proc_entry(ACPI_BATTERY_FILE_STATUS, | ||
678 | acpi_device_dir(device)); | ||
679 | remove_proc_entry(ACPI_BATTERY_FILE_INFO, | ||
680 | acpi_device_dir(device)); | ||
681 | |||
682 | remove_proc_entry(acpi_device_bid(device), acpi_battery_dir); | ||
683 | acpi_device_dir(device) = NULL; | ||
684 | } | ||
685 | |||
686 | return_VALUE(0); | ||
687 | } | ||
688 | |||
689 | |||
690 | /* -------------------------------------------------------------------------- | ||
691 | Driver Interface | ||
692 | -------------------------------------------------------------------------- */ | ||
693 | |||
694 | static void | ||
695 | acpi_battery_notify ( | ||
696 | acpi_handle handle, | ||
697 | u32 event, | ||
698 | void *data) | ||
699 | { | ||
700 | struct acpi_battery *battery = (struct acpi_battery *) data; | ||
701 | struct acpi_device *device = NULL; | ||
702 | |||
703 | ACPI_FUNCTION_TRACE("acpi_battery_notify"); | ||
704 | |||
705 | if (!battery) | ||
706 | return_VOID; | ||
707 | |||
708 | if (acpi_bus_get_device(handle, &device)) | ||
709 | return_VOID; | ||
710 | |||
711 | switch (event) { | ||
712 | case ACPI_BATTERY_NOTIFY_STATUS: | ||
713 | case ACPI_BATTERY_NOTIFY_INFO: | ||
714 | acpi_battery_check(battery); | ||
715 | acpi_bus_generate_event(device, event, battery->flags.present); | ||
716 | break; | ||
717 | default: | ||
718 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
719 | "Unsupported event [0x%x]\n", event)); | ||
720 | break; | ||
721 | } | ||
722 | |||
723 | return_VOID; | ||
724 | } | ||
725 | |||
726 | |||
727 | static int | ||
728 | acpi_battery_add ( | ||
729 | struct acpi_device *device) | ||
730 | { | ||
731 | int result = 0; | ||
732 | acpi_status status = 0; | ||
733 | struct acpi_battery *battery = NULL; | ||
734 | |||
735 | ACPI_FUNCTION_TRACE("acpi_battery_add"); | ||
736 | |||
737 | if (!device) | ||
738 | return_VALUE(-EINVAL); | ||
739 | |||
740 | battery = kmalloc(sizeof(struct acpi_battery), GFP_KERNEL); | ||
741 | if (!battery) | ||
742 | return_VALUE(-ENOMEM); | ||
743 | memset(battery, 0, sizeof(struct acpi_battery)); | ||
744 | |||
745 | battery->handle = device->handle; | ||
746 | strcpy(acpi_device_name(device), ACPI_BATTERY_DEVICE_NAME); | ||
747 | strcpy(acpi_device_class(device), ACPI_BATTERY_CLASS); | ||
748 | acpi_driver_data(device) = battery; | ||
749 | |||
750 | result = acpi_battery_check(battery); | ||
751 | if (result) | ||
752 | goto end; | ||
753 | |||
754 | result = acpi_battery_add_fs(device); | ||
755 | if (result) | ||
756 | goto end; | ||
757 | |||
758 | status = acpi_install_notify_handler(battery->handle, | ||
759 | ACPI_DEVICE_NOTIFY, acpi_battery_notify, battery); | ||
760 | if (ACPI_FAILURE(status)) { | ||
761 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
762 | "Error installing notify handler\n")); | ||
763 | result = -ENODEV; | ||
764 | goto end; | ||
765 | } | ||
766 | |||
767 | printk(KERN_INFO PREFIX "%s Slot [%s] (battery %s)\n", | ||
768 | ACPI_BATTERY_DEVICE_NAME, acpi_device_bid(device), | ||
769 | device->status.battery_present?"present":"absent"); | ||
770 | |||
771 | end: | ||
772 | if (result) { | ||
773 | acpi_battery_remove_fs(device); | ||
774 | kfree(battery); | ||
775 | } | ||
776 | |||
777 | return_VALUE(result); | ||
778 | } | ||
779 | |||
780 | |||
781 | static int | ||
782 | acpi_battery_remove ( | ||
783 | struct acpi_device *device, | ||
784 | int type) | ||
785 | { | ||
786 | acpi_status status = 0; | ||
787 | struct acpi_battery *battery = NULL; | ||
788 | |||
789 | ACPI_FUNCTION_TRACE("acpi_battery_remove"); | ||
790 | |||
791 | if (!device || !acpi_driver_data(device)) | ||
792 | return_VALUE(-EINVAL); | ||
793 | |||
794 | battery = (struct acpi_battery *) acpi_driver_data(device); | ||
795 | |||
796 | status = acpi_remove_notify_handler(battery->handle, | ||
797 | ACPI_DEVICE_NOTIFY, acpi_battery_notify); | ||
798 | if (ACPI_FAILURE(status)) | ||
799 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
800 | "Error removing notify handler\n")); | ||
801 | |||
802 | acpi_battery_remove_fs(device); | ||
803 | |||
804 | kfree(battery); | ||
805 | |||
806 | return_VALUE(0); | ||
807 | } | ||
808 | |||
809 | |||
810 | static int __init | ||
811 | acpi_battery_init (void) | ||
812 | { | ||
813 | int result = 0; | ||
814 | |||
815 | ACPI_FUNCTION_TRACE("acpi_battery_init"); | ||
816 | |||
817 | acpi_battery_dir = proc_mkdir(ACPI_BATTERY_CLASS, acpi_root_dir); | ||
818 | if (!acpi_battery_dir) | ||
819 | return_VALUE(-ENODEV); | ||
820 | acpi_battery_dir->owner = THIS_MODULE; | ||
821 | |||
822 | result = acpi_bus_register_driver(&acpi_battery_driver); | ||
823 | if (result < 0) { | ||
824 | remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); | ||
825 | return_VALUE(-ENODEV); | ||
826 | } | ||
827 | |||
828 | return_VALUE(0); | ||
829 | } | ||
830 | |||
831 | |||
832 | static void __exit | ||
833 | acpi_battery_exit (void) | ||
834 | { | ||
835 | ACPI_FUNCTION_TRACE("acpi_battery_exit"); | ||
836 | |||
837 | acpi_bus_unregister_driver(&acpi_battery_driver); | ||
838 | |||
839 | remove_proc_entry(ACPI_BATTERY_CLASS, acpi_root_dir); | ||
840 | |||
841 | return_VOID; | ||
842 | } | ||
843 | |||
844 | |||
845 | module_init(acpi_battery_init); | ||
846 | module_exit(acpi_battery_exit); | ||