2 * cmbatt.c - Control Method Battery driver
4 * Copyright (C) 2000 Andrew Grover
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Brendan Burns <bburns@wso.williams.edu> 2000-11-15
23 * - added proc battery interface
24 * - parse returned data from _BST and _BIF
25 * Andy Grover <andrew.grover@intel.com> 2000-12-8
26 * - improved proc interface
29 #include <linux/kernel.h>
30 #include <linux/types.h>
31 #include <linux/proc_fs.h>
35 #define _COMPONENT OS_DEPENDENT
36 MODULE_NAME ("cmbatt")
38 /* ACPI-specific defines */
39 #define ACPI_CMBATT_HID "PNP0C0A"
40 #define ACPI_BATT_PRESENT 0x10
41 #define ACPI_BATT_UNKNOWN 0xFFFFFFFF
43 /* driver-specific defines */
44 #define MAX_CM_BATTERIES 0x8
45 #define MAX_BATT_STRLEN 0x20
51 u32 last_full_capacity
;
52 u32 battery_technology
;
54 u32 design_capacity_warning
;
55 u32 design_capacity_low
;
56 u32 battery_capacity_granularity_1
;
57 u32 battery_capacity_granularity_2
;
59 char model_number
[MAX_BATT_STRLEN
];
60 char serial_number
[MAX_BATT_STRLEN
];
61 char battery_type
[MAX_BATT_STRLEN
];
62 char oem_info
[MAX_BATT_STRLEN
];
71 struct cmbatt_info info
;
78 u32 remaining_capacity
;
82 static u32 batt_count
= 0;
84 static struct cmbatt_context batt_list
[MAX_CM_BATTERIES
];
87 acpi_get_battery_status(ACPI_HANDLE handle
, struct cmbatt_status
*result
)
96 /* determine buffer length needed */
97 if (acpi_evaluate_object(handle
, "_BST", NULL
, &buf
) != AE_BUFFER_OVERFLOW
) {
98 printk(KERN_ERR
"Cmbatt: Could not get battery status struct length\n");
102 buf
.pointer
= kmalloc(buf
.length
, GFP_KERNEL
);
107 if (!ACPI_SUCCESS(acpi_evaluate_object(handle
, "_BST", NULL
, &buf
))) {
108 printk(KERN_ERR
"Cmbatt: Could not get battery status\n");
113 obj
= (ACPI_OBJECT
*) buf
.pointer
;
114 objs
= obj
->package
.elements
;
116 result
->state
= objs
[0].number
.value
;
117 result
->present_rate
= objs
[1].number
.value
;
118 result
->remaining_capacity
= objs
[2].number
.value
;
119 result
->present_voltage
= objs
[3].number
.value
;
127 acpi_get_battery_info(ACPI_HANDLE handle
, struct cmbatt_info
*result
)
136 /* determine the length of the data */
137 if (acpi_evaluate_object(handle
, "_BIF", NULL
, &buf
) != AE_BUFFER_OVERFLOW
) {
138 printk(KERN_ERR
"Cmbatt: Could not get battery info struct length\n");
142 buf
.pointer
= kmalloc(buf
.length
, GFP_KERNEL
);
147 if (!ACPI_SUCCESS(acpi_evaluate_object(handle
, "_BIF", NULL
, &buf
))) {
148 printk(KERN_ERR
"Cmbatt: Could not get battery info\n");
153 obj
= (ACPI_OBJECT
*) buf
.pointer
;
154 objs
= obj
->package
.elements
;
156 result
->power_unit
=objs
[0].number
.value
;
157 result
->design_capacity
=objs
[1].number
.value
;
158 result
->last_full_capacity
=objs
[2].number
.value
;
159 result
->battery_technology
=objs
[3].number
.value
;
160 result
->design_voltage
=objs
[4].number
.value
;
161 result
->design_capacity_warning
=objs
[5].number
.value
;
162 result
->design_capacity_low
=objs
[6].number
.value
;
163 result
->battery_capacity_granularity_1
=objs
[7].number
.value
;
164 result
->battery_capacity_granularity_2
=objs
[8].number
.value
;
166 /* BUG: trailing NULL issue */
167 strncpy(result
->model_number
, objs
[9].string
.pointer
, MAX_BATT_STRLEN
-1);
168 strncpy(result
->serial_number
, objs
[10].string
.pointer
, MAX_BATT_STRLEN
-1);
169 strncpy(result
->battery_type
, objs
[11].string
.pointer
, MAX_BATT_STRLEN
-1);
170 strncpy(result
->oem_info
, objs
[12].string
.pointer
, MAX_BATT_STRLEN
-1);
178 * We found a device with the correct HID
181 acpi_found_cmbatt(ACPI_HANDLE handle
, u32 level
, void *ctx
, void **value
)
183 ACPI_DEVICE_INFO info
;
185 if (batt_count
>= MAX_CM_BATTERIES
) {
186 printk(KERN_ERR
"Cmbatt: MAX_CM_BATTERIES exceeded\n");
190 if (!ACPI_SUCCESS(acpi_get_object_info(handle
, &info
))) {
191 printk(KERN_ERR
"Cmbatt: Could not get battery object info\n");
195 if (info
.valid
& ACPI_VALID_UID
) {
196 strncpy(batt_list
[batt_count
].UID
, info
.unique_id
, 9);
198 else if (batt_count
> 1) {
199 printk(KERN_WARNING
"Cmbatt: No UID but more than 1 battery\n");
202 if (!(info
.valid
& ACPI_VALID_STA
)) {
203 printk(KERN_ERR
"Cmbatt: Battery _STA invalid\n");
207 if (!(info
.current_status
& ACPI_BATT_PRESENT
)) {
208 printk(KERN_INFO
"Cmbatt: Battery socket %d empty\n", batt_count
);
209 batt_list
[batt_count
].is_present
= FALSE
;
212 printk(KERN_INFO
"Cmbatt: Battery socket %d occupied\n", batt_count
);
213 batt_list
[batt_count
].is_present
= TRUE
;
214 if (acpi_get_battery_info(handle
, &batt_list
[batt_count
].info
) != AE_OK
) {
215 printk(KERN_ERR
"acpi_get_battery_info failed\n");
219 batt_list
[batt_count
].power_unit
= (batt_list
[batt_count
].info
.power_unit
) ? "mA" : "mW";
222 batt_list
[batt_count
].handle
= handle
;
230 proc_read_batt_info(char *page
, char **start
, off_t off
,
231 int count
, int *eof
, void *data
)
233 struct cmbatt_info
*info
;
234 u32 batt_num
= (u32
) data
;
238 info
= &batt_list
[batt_num
].info
;
240 /* don't get info more than once for a single proc read */
244 if (!batt_list
[batt_num
].is_present
) {
245 p
+= sprintf(p
, "battery %d not present\n", batt_num
);
249 if (info
->last_full_capacity
== ACPI_BATT_UNKNOWN
)
250 p
+= sprintf(p
, "Unknown last full capacity\n");
252 p
+= sprintf(p
, "Last Full Capacity %x %s /hr\n",
253 info
->last_full_capacity
, batt_list
[batt_num
].power_unit
);
255 if (info
->design_capacity
== ACPI_BATT_UNKNOWN
)
256 p
+= sprintf(p
, "Unknown Design Capacity\n");
258 p
+= sprintf(p
, "Design Capacity %x %s /hr\n",
259 info
->design_capacity
, batt_list
[batt_num
].power_unit
);
261 if (info
->battery_technology
)
262 p
+= sprintf(p
, "Secondary Battery Technology\n");
264 p
+= sprintf(p
, "Primary Battery Technology\n");
266 if (info
->design_voltage
== ACPI_BATT_UNKNOWN
)
267 p
+= sprintf(p
, "Unknown Design Voltage\n");
269 p
+= sprintf(p
, "Design Voltage %x mV\n",
270 info
->design_voltage
);
272 p
+= sprintf(p
, "Design Capacity Warning %d\n",
273 info
->design_capacity_warning
);
274 p
+= sprintf(p
, "Design Capacity Low %d\n",
275 info
->design_capacity_low
);
276 p
+= sprintf(p
, "Battery Capacity Granularity 1 %d\n",
277 info
->battery_capacity_granularity_1
);
278 p
+= sprintf(p
, "Battery Capacity Granularity 2 %d\n",
279 info
->battery_capacity_granularity_2
);
280 p
+= sprintf(p
, "model number %s\nserial number %s\nbattery type %s\nOEM info %s\n",
281 info
->model_number
,info
->serial_number
,
282 info
->battery_type
,info
->oem_info
);
285 if (len
<= off
+count
) *eof
= 1;
288 if (len
>count
) len
= count
;
294 proc_read_batt_status(char *page
, char **start
, off_t off
,
295 int count
, int *eof
, void *data
)
297 struct cmbatt_status status
;
298 u32 batt_num
= (u32
) data
;
302 /* don't get status more than once for a single proc read */
306 if (!batt_list
[batt_num
].is_present
) {
307 p
+= sprintf(p
, "battery %d not present\n", batt_num
);
311 printk("getting batt status\n");
313 if (acpi_get_battery_status(batt_list
[batt_num
].handle
, &status
) != AE_OK
) {
314 printk(KERN_ERR
"Cmbatt: acpi_get_battery_status failed\n");
318 p
+= sprintf(p
, "Remaining Capacity: %x\n", status
.remaining_capacity
);
320 if (status
.state
& 0x1)
321 p
+= sprintf(p
, "Battery discharging\n");
322 if (status
.state
& 0x2)
323 p
+= sprintf(p
, "Battery charging\n");
324 if (status
.state
& 0x4)
325 p
+= sprintf(p
, "Battery critically low\n");
327 if (status
.present_rate
== ACPI_BATT_UNKNOWN
)
328 p
+= sprintf(p
, "Battery rate unknown\n");
330 p
+= sprintf(p
, "Battery rate %x\n",
331 status
.present_rate
);
333 if (status
.remaining_capacity
== ACPI_BATT_UNKNOWN
)
334 p
+= sprintf(p
, "Battery capacity unknown\n");
336 p
+= sprintf(p
, "Battery capacity %x %s\n",
337 status
.remaining_capacity
, batt_list
[batt_num
].power_unit
);
339 if (status
.present_voltage
== ACPI_BATT_UNKNOWN
)
340 p
+= sprintf(p
, "Battery voltage unknown\n");
342 p
+= sprintf(p
, "Battery voltage %x volts\n",
343 status
.present_voltage
);
348 if (len
<= off
+count
) *eof
= 1;
351 if (len
>count
) len
= count
;
359 acpi_cmbatt_init(void)
363 acpi_get_devices(ACPI_CMBATT_HID
,
368 for (i
= 0; i
< batt_count
; i
++) {
372 sprintf(batt_name
, "power/batt%d_info", i
);
373 create_proc_read_entry(batt_name
, 0, NULL
,
374 proc_read_batt_info
, (void *) i
);
376 sprintf(batt_name
, "power/batt%d_status", i
);
377 create_proc_read_entry(batt_name
, 0, NULL
,
378 proc_read_batt_status
, (void *) i
);
386 acpi_cmbatt_terminate(void)
390 for (i
= 0; i
< batt_count
; i
++) {
394 sprintf(batt_name
, "power/batt%d_info", i
);
395 remove_proc_entry(batt_name
, NULL
);
397 sprintf(batt_name
, "power/batt%d_status", i
);
398 remove_proc_entry(batt_name
, NULL
);