linux: sanitise ACPI battery data
[olsrd.git] / src / linux / apm.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004, Andreas Tonnesen(andreto@olsr.org)
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 /*
43  * Much of the ACPI code is taken from Florian Schaefers
44  * Acpi-Power Enlightenment epplet
45  */
46
47 #include "apm.h"
48 #include "defs.h"
49 #include <stdio.h>
50 #include <string.h>
51 #include <stdlib.h>
52
53 /* APM related stuff */
54
55 #define APM_PROC "/proc/apm"
56
57 struct linux_apm_info {
58   char driver_version[10];
59   int apm_version_major;
60   int apm_version_minor;
61   int apm_flags;
62   int ac_line_status;
63   int battery_status;
64   int battery_flags;
65   int battery_percentage;
66   int battery_time;
67   int using_minutes;
68 };
69
70 /* ACPI related stuff */
71 static const char *const acpi_info[] = {
72   "/proc/acpi/battery/0/info",
73   "/proc/acpi/battery/1/info",
74   "/proc/acpi/battery/BATA/info",
75   "/proc/acpi/battery/BAT0/info",
76   "/proc/acpi/battery/BAT1/info"
77 };
78
79 static const char *const acpi_state[] = {
80   "/proc/acpi/battery/0/status",
81   "/proc/acpi/battery/1/status",
82   "/proc/acpi/battery/BATA/state",
83   "/proc/acpi/battery/BAT0/state",
84   "/proc/acpi/battery/BAT1/state"
85 };
86
87 #define ACPI_BT_CNT  ARRAYSIZE(acpi_state)
88
89 static const char *const acpi_ac[] = {
90   "/proc/acpi/ac_adapter/0/status",
91   "/proc/acpi/ac_adapter/AC/state",
92   "/proc/acpi/ac_adapter/ACAD/state"
93 };
94
95 #define ACPI_AC_CNT  ARRAYSIZE(acpi_ac)
96
97 #define USE_APM    1
98 #define USE_ACPI   2
99
100 static int method;
101
102 static int fd_index;
103
104 static int ac_power_on;
105
106 /* Prototypes */
107
108 static int apm_read_apm(struct olsr_apm_info *);
109
110 static int apm_read_acpi(struct olsr_apm_info *);
111
112 static int acpi_probe(void);
113
114 int
115 apm_init(void)
116 {
117   struct olsr_apm_info ainfo;
118
119   method = -1;
120   OLSR_PRINTF(3, "Initializing APM\n");
121
122   if ((((fd_index = acpi_probe()) >= 0) || ac_power_on) && apm_read_acpi(&ainfo))
123     method = USE_ACPI;
124   else if (apm_read_apm(&ainfo))
125     method = USE_APM;
126
127   if (method != -1)
128     apm_printinfo(&ainfo);
129
130   return method;
131 }
132
133 void
134 apm_printinfo(struct olsr_apm_info *ainfo)
135 {
136   OLSR_PRINTF(5, "APM info:\n\tAC status %d\n\tBattery percentage %d%%\n\tBattery time left %d mins\n\n", ainfo->ac_line_status,
137               ainfo->battery_percentage, ainfo->battery_time_left);
138
139   ainfo = NULL;                 /* squelch compiler warnings */
140 }
141
142 int
143 apm_read(struct olsr_apm_info *ainfo)
144 {
145   switch (method) {
146   case USE_APM:
147     return apm_read_apm(ainfo);
148   case USE_ACPI:
149     return apm_read_acpi(ainfo);
150   default:
151     break;
152   }
153   return 0;
154 }
155
156 static int
157 apm_read_apm(struct olsr_apm_info *ainfo)
158 {
159   char buffer[100];
160   char units[10];
161   FILE *apm_procfile;
162   struct linux_apm_info lainfo;
163
164   /* Open procfile */
165   if ((apm_procfile = fopen(APM_PROC, "r")) == NULL)
166     return 0;
167
168   if (fgets(buffer, sizeof(buffer), apm_procfile) == NULL) {
169     fclose(apm_procfile);
170     /* Try re-opening the file */
171     if ((apm_procfile = fopen(APM_PROC, "r")) == NULL)
172       return 0;
173
174     if (fgets(buffer, sizeof(buffer), apm_procfile) == NULL) {
175       /* Giving up */
176       fprintf(stderr, "OLSRD: Could not read APM info - setting willingness to default");
177       fclose(apm_procfile);
178       return 0;
179     }
180   }
181   fclose(apm_procfile);
182
183   //printf("READ: %s\n", buffer);
184
185   /* Get the info */
186   sscanf(buffer, "%10s %d.%d %x %x %x %x %d%% %d %10s\n", lainfo.driver_version, &lainfo.apm_version_major, &lainfo.apm_version_minor,
187          &lainfo.apm_flags, &lainfo.ac_line_status, &lainfo.battery_status, &lainfo.battery_flags, &lainfo.battery_percentage,
188          &lainfo.battery_time, units);
189
190   lainfo.using_minutes = strncmp(units, "min", 3) ? 0 : 1;
191
192   /*
193    * Should take care of old APM type info here
194    */
195
196   /*
197    * Fix possible percentage error
198    */
199   if (lainfo.battery_percentage > 100)
200     lainfo.battery_percentage = -1;
201
202   /* Fill the provided struct */
203
204   if (lainfo.ac_line_status)
205     ainfo->ac_line_status = OLSR_AC_POWERED;
206   else
207     ainfo->ac_line_status = OLSR_BATTERY_POWERED;
208
209   ainfo->battery_percentage = lainfo.battery_percentage;
210   ainfo->battery_time_left = lainfo.battery_time;
211
212   return 1;
213 }
214
215 static int
216 apm_read_acpi(struct olsr_apm_info *ainfo)
217 {
218   FILE *fd;
219   int bat_max = 5000;                  /* Find some sane value */
220   int bat_val = 0;
221
222   /* reporbe in case ac status changed */
223   fd_index = acpi_probe();
224
225   /* No battery was found */
226   if (fd_index < 0) {
227     /* but we have ac */
228     if (ac_power_on) {
229       ainfo->ac_line_status = OLSR_AC_POWERED;
230
231       ainfo->battery_percentage = -1;
232
233       return 1;
234     }
235
236     /* not enough info */
237     return 0;
238   }
239
240   /* Get maxvalue */
241   if ((fd = fopen(acpi_info[fd_index], "r")) == NULL)
242     return 0;
243
244   for (;;) {
245     char s1[32], s2[32], s3[32], s4[32], inbuff[127];
246     if (fgets(inbuff, sizeof(inbuff), fd) == NULL)
247       break;
248
249     sscanf(inbuff, "%32s %32s %32s %32s", s1, s2, s3, s4);
250     if (!strcasecmp(s2, "full"))
251       bat_max = atoi(s4);
252   }
253   fclose(fd);
254
255   if ((fd = fopen(acpi_state[fd_index], "r")) == NULL)
256     return 0;
257
258   /* Extract battery status */
259   for (;;) {
260     char s1[32], s2[32], s3[32], s4[32], inbuff[127];
261     if (fgets(inbuff, sizeof(inbuff), fd) == NULL)
262       break;
263     sscanf(inbuff, "%32s %32s %32s %32s", s1, s2, s3, s4);
264
265     /* find remaining juice */
266     if (!strcasecmp(s1, "Remaining"))
267       bat_val = atoi(s3);
268   }
269   fclose(fd);
270
271   ainfo->ac_line_status = ac_power_on ? OLSR_AC_POWERED : OLSR_BATTERY_POWERED;
272
273   /* sanitise ACPI battery data */
274   bat_max = abs(bat_max);
275   bat_val = abs(bat_val);
276   if (bat_val > bat_max) {
277     bat_val = bat_max;
278   }
279
280   if (bat_max == 0) {
281     /* protection against stupid acpi data */
282     ainfo->battery_percentage = 0;
283   }
284   else {
285     ainfo->battery_percentage = (bat_val >= bat_max) ? 100 : (bat_val * 100 / bat_max);
286   }
287
288   return 1;
289 }
290
291 static int
292 acpi_probe(void)
293 {
294   unsigned int i;
295
296   /* First check for AC power */
297   ac_power_on = 0;
298
299   for (i = 0; i < ACPI_AC_CNT; i++) {
300     char s1[32], s2[32];
301     int rc;
302     FILE *fd = fopen(acpi_ac[i], "r");
303
304     /* Try opening the info file */
305     if (fd == NULL)
306       continue;
307
308     /* Extract info */
309     rc = fscanf(fd, "%32s %32s", s1, s2);
310
311     /* Close info entry */
312     fclose(fd);
313
314     if (rc < 2)
315       continue;
316
317     /* Running on AC power */
318     if (!strcasecmp(s2, "on-line")) {
319
320       /* ac power enabled */
321       ac_power_on = 1;
322
323       break;
324     }
325   }
326
327   /* Only checking the first found battery entry... */
328   for (i = 0; i < ACPI_BT_CNT; i++) {
329     char s1[32], s2[32];
330     int rc;
331     FILE *fd = fopen(acpi_info[i], "r");
332
333     /* Try opening the info file */
334     if (fd == NULL)
335       continue;
336
337     /* Extract info */
338     rc = fscanf(fd, "%32s %32s", s1, s2);
339
340     /* Close info entry */
341     fclose(fd);
342
343     if (rc < 2)
344       continue;
345
346     /* Check if battery is present */
347     if ((!strcasecmp(s1, "present:")) && (!strcasecmp(s2, "no")))
348       continue;
349
350     /* Open the corresponding state file */
351     if ((fd = fopen(acpi_state[i], "r")) == NULL)
352       continue;
353
354     fclose(fd);
355     return i;
356   }
357
358   /* No battery found */
359   return -1;
360 }
361
362 /*
363  * Local Variables:
364  * c-basic-offset: 2
365  * indent-tabs-mode: nil
366  * End:
367  */