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