Tizen 2.1 base
[platform/core/system/power-manager.git] / pm_battery.c
1 /*
2  * power-manager
3  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16 */
17
18 #include <glib.h>
19 #include <stdbool.h>
20 #include <stdio.h>
21 #include <stdlib.h>
22
23 #include "pm_core.h"
24 #include "pm_device_plugin.h"
25 #include "pm_battery.h"
26
27 #define CHARGING_STATE(x)       ((x) & CHRGR_FLAG)
28 #define FULL_CAPACITY_RAW       (10000)
29 #define FULL_CAPACITY           (100)
30 #define BATTERY_FULL_THRESHOLD  (98)
31 #define MAX_COUNT_UNCHARGING    (5)
32 #define MAX_COUNT_CHARGING      (5)
33 #define PRINT_ALL_BATT_NODE(x)  /*print_all_batt_node(x)*/
34
35 int (*get_battery_capacity)();
36
37 enum state_b {
38         B_UNCHARGING = 0,
39         B_CHARGING = 1,
40         B_END = 2
41 };
42
43 typedef struct _batt_node {
44         time_t clock;
45         int capacity;
46         struct _batt_node *preview;
47         struct _batt_node *next;
48 } Batt_node;
49
50 enum state_a {
51         A_TIMETOEMPTY = 0,
52         A_TIMETOFULL = 1,
53         A_END = 2
54 };
55
56 static int timeout_id = 0;
57 static int noti_fd = 0;
58
59 static Batt_node *batt_head[B_END];
60 static Batt_node *batt_tail[B_END];
61 static int MAX_VALUE_COUNT[B_END] = {MAX_COUNT_UNCHARGING, MAX_COUNT_CHARGING};
62 static double avg_factor[B_END] = {-1.0, -1.0};
63 static int full_capacity = 0;
64 static int old_capacity = 0;
65 static int charging_state = 0;
66 static int multiply_value[B_END] = {-1, 1};
67
68 static void print_all_batt_node(enum state_b b_index)
69 {
70         Batt_node *node = NULL;
71         int cnt = 0;
72
73         LOGINFO("print_all_batt_node [%d]", b_index);
74
75         if(b_index < 0 || b_index >= B_END)
76                 return;
77
78         if(batt_head[b_index] == NULL)
79                 return;
80
81         node = batt_head[b_index];
82         while(node != NULL) {
83                 cnt++;
84                 LOGINFO("[%d] capacity %5d, time %s", cnt, node->capacity,
85                                 ctime(&node->clock));
86                 node = node->next;
87         }
88 }
89
90 static int check_value_validity(enum state_b b_index,time_t clock,int capacity)
91 {
92         time_t old_clock = 0;
93         int old_capacity = 0;
94         int capadiff = 0;
95
96         if(b_index < 0 || b_index >= B_END)
97                 return -1;
98
99         if(batt_head[b_index] == NULL)
100                 return 0;
101
102         old_capacity = batt_head[b_index]->capacity;
103
104         if(system_wakeup_flag == true) {
105                 LOGERR("check value validity : invalid cuz system suspend!");
106                 system_wakeup_flag = false;
107                 return -1;
108         }
109         /* capacity */
110         capadiff = capacity - old_capacity;
111         if((capadiff * multiply_value[b_index]) <= 0) {
112                 LOGERR("check value validity : capadiff(%d) wrong!", capadiff);
113                 return -1;
114         }
115         return 0;
116 }
117
118 static int add_batt_node(enum state_b b_index, time_t clock, int capacity)
119 {
120         Batt_node *node = NULL;
121
122         PRINT_ALL_BATT_NODE(b_index);
123
124         if(b_index < 0 || b_index >= B_END)
125                 return -1;
126
127         node = (Batt_node *) malloc(sizeof(Batt_node));
128         if(node == NULL) {
129                 LOGERR("Not enough memory, add battery node fail!");
130                 return -1;
131         }
132
133         node->clock = clock;
134         node->capacity = capacity;
135
136         if(batt_head[b_index] == NULL && batt_tail[b_index] == NULL) {
137                 batt_head[b_index] = batt_tail[b_index] = node;
138                 node->preview = NULL;
139                 node->next = NULL;
140         } else {
141                 node->next = batt_head[b_index];
142                 node->preview = NULL;
143                 batt_head[b_index]->preview = node;
144                 batt_head[b_index] = node;
145         }
146         PRINT_ALL_BATT_NODE(b_index);
147         return 0;
148 }
149
150 static int reap_batt_node(enum state_b b_index, int max_count)
151 {
152         Batt_node *node = NULL;
153         Batt_node *tmp = NULL;
154         int cnt = 0;
155
156         PRINT_ALL_BATT_NODE(b_index);
157
158         if(b_index < 0 || b_index >= B_END)
159                 return -1;
160
161         if(max_count <= 0)
162                 return -1;
163
164         node = batt_head[b_index];
165
166         while(node != NULL) {
167                 if(cnt >= max_count) break;
168                 cnt++;
169                 node = node->next;
170         }
171
172         if(node != NULL && node != batt_tail[b_index]) {
173                 batt_tail[b_index] = node;
174                 node = node->next;
175                 batt_tail[b_index]->next = NULL;
176                 while(node != NULL) {
177                         tmp = node;
178                         node = node->next;
179                         free(tmp);
180                 }
181         }
182         PRINT_ALL_BATT_NODE(b_index);
183         return 0;
184 }
185
186 static int del_all_batt_node(enum state_b b_index)
187 {
188         Batt_node *node = NULL;
189
190         PRINT_ALL_BATT_NODE(b_index);
191
192         if(b_index < 0 || b_index >= B_END)
193                 return -1;
194         if(batt_head[b_index] == NULL)
195                 return 0;
196
197         while(batt_head[b_index] != NULL) {
198                 node = batt_head[b_index];
199                 batt_head[b_index] = batt_head[b_index]->next;
200                 free(node);
201         }
202         batt_tail[b_index] = NULL;
203         PRINT_ALL_BATT_NODE(b_index);
204         return 0;
205 }
206
207 static float update_factor(enum state_b b_index)
208 {
209         Batt_node *node = NULL;
210         double factor = 0.0;
211         double total_factor = 0.0;
212         int cnt = 0;
213         double timediff = 0.0;
214         double capadiff = 0.0;
215
216         if(b_index < 0 || b_index >= B_END)
217                 return 0;
218
219         if(batt_head[b_index] == NULL || batt_head[b_index]->next == NULL)
220                 return  avg_factor[b_index];
221
222         node = batt_head[b_index];
223         while(1) {
224                 timediff = difftime(node->clock, node->next->clock);
225                 capadiff = node->capacity - node->next->capacity;
226                 if(capadiff < 0)
227                         capadiff*=(-1);
228                 if(capadiff != 0)
229                         factor = timediff / capadiff;
230                 total_factor += factor;
231
232                 node = node->next;
233                 cnt++;
234
235                 /*LOGINFO("[%d] timediff(%lf) / capadiff(%lf) = factor(%lf)",
236                         cnt, timediff, capadiff, factor);*/
237                 factor = 0.0;
238
239                 if(node == NULL || node->next == NULL)
240                         break;
241                 if(cnt >= MAX_VALUE_COUNT[b_index]) {
242                         reap_batt_node(b_index, MAX_VALUE_COUNT[b_index]);
243                         break;
244                 }
245         }
246         LOGINFO(" sum = %lf", total_factor);
247         total_factor /= (float)cnt;
248         LOGINFO(" avg_factor = %lf", total_factor);
249
250         return total_factor;
251 }
252
253 static void update_time(enum state_a a_index, int seconds)
254 {
255         int clock;
256
257         if(a_index < 0 || a_index >= A_END)
258                 return;
259
260         if(seconds <= 0)
261                 return;
262
263         switch(a_index) {
264                 case A_TIMETOFULL:
265                         vconf_set_int(VCONFKEY_PM_BATTERY_TIMETOFULL,
266                                 seconds);
267                         LOGINFO("update time[%d,%d]", a_index, seconds);
268                         break;
269                 case A_TIMETOEMPTY:
270                         vconf_set_int(VCONFKEY_PM_BATTERY_TIMETOEMPTY,
271                                 seconds);
272                         LOGINFO("update time[%d,%d]", a_index, seconds);
273                         break;
274         }
275 }
276
277 void battinfo_calculation()
278 {
279         time_t clock;
280         int capacity = 0;
281         int estimated_time = 0;
282         int tmp = 0;
283
284         capacity = get_battery_capacity();
285
286         if(capacity <= 0)
287                 return;
288         if(capacity == old_capacity)
289                 return;
290
291         old_capacity = capacity;
292
293         if(get_charging_status(&tmp) == 0)
294                 charging_state = (tmp > 0 ? TRUE : FALSE);
295
296         clock = time(NULL);
297         if(charging_state == TRUE) {
298                 del_all_batt_node(B_UNCHARGING);
299                 if((capacity * 100 / full_capacity)
300                                 >= BATTERY_FULL_THRESHOLD) {
301                         if(battery_charge_full()) {
302                                 del_all_batt_node(B_CHARGING);
303                                 LOGINFO("battery fully charged!");
304                                 update_time(A_TIMETOFULL, 0);
305                                 return;
306                         }
307                 }
308                 if(batt_head[B_CHARGING] == NULL) {
309                         add_batt_node(B_CHARGING, clock, capacity);
310                 } else {
311                         add_batt_node(B_CHARGING, clock, capacity);
312                         avg_factor[B_CHARGING] = update_factor(B_CHARGING);
313                 }
314                 estimated_time = (float)(full_capacity - capacity) *
315                                 avg_factor[B_CHARGING];
316                 update_time(A_TIMETOFULL, estimated_time);
317         } else {
318                 del_all_batt_node(B_CHARGING);
319                 if(system_wakeup_flag == true) {
320                         del_all_batt_node(B_UNCHARGING);
321                         system_wakeup_flag = false;
322                 }
323                 if(batt_head[B_UNCHARGING] == NULL) {
324                         add_batt_node(B_UNCHARGING, clock, capacity);
325                 } else {
326                         add_batt_node(B_UNCHARGING, clock, capacity);
327                         avg_factor[B_UNCHARGING] = update_factor(B_UNCHARGING);
328                 }
329                 estimated_time = (float)capacity * avg_factor[B_UNCHARGING];
330                 update_time(A_TIMETOEMPTY, estimated_time);
331         }
332 }
333
334 static gboolean battinfo_cb(gpointer data)
335 {
336         battinfo_calculation();
337         return TRUE;
338 }
339
340 static int init_battery_func()
341 {
342         int ret = -1;
343         int value = -1;
344
345         ret = battery_capacity_raw(&value);
346         if(ret >= 0) {
347                 get_battery_capacity = battery_capacity_raw;
348                 full_capacity = FULL_CAPACITY_RAW;
349                 LOGINFO("init_battery_func : full capacity(%d)", full_capacity);
350                 return 0;
351         }
352
353         ret = battery_capacity(&value);
354         if(ret >= 0) {
355                 get_battery_capacity = battery_capacity;
356                 full_capacity = FULL_CAPACITY;
357                 LOGINFO("init_battery_func : full capacity(%d)", full_capacity);
358                 return 0;
359         }
360
361         LOGERR("init_battery_func : fail to get battery info!");
362         return -1;
363 }
364
365 int start_battinfo_gathering(int timeout)
366 {
367         int ret;
368
369         LOGINFO("Start battery gathering!");
370
371         if(timeout < 0) {
372                 LOGERR("invalid timeout value [%d]!", timeout);
373                 return -1;
374         }
375         if(init_battery_func() != 0)
376                 return -1;
377
378         old_capacity = 0;
379         battinfo_calculation();
380
381         if(timeout > 0) {
382                 /* Using g_timer for gathering battery info */
383                 timeout_id = g_timeout_add_full(G_PRIORITY_DEFAULT, timeout,
384                                         (GSourceFunc)battinfo_cb, NULL, NULL);
385         } else if(timeout == 0) {
386                 /* Using heynoti from system-server(udev)
387                                         for gathering battery info */
388                 if((noti_fd = heynoti_init()) < 0) {
389                         LOGERR("heynoti init failed!");
390                         return -1;
391                 }
392                 ret = heynoti_subscribe(noti_fd, "device_charge_chgdet",
393                                 (void *)battinfo_calculation, (void *)NULL);
394                 if(ret != 0) {
395                         LOGERR("heynoti subscribe fail!");
396                         return -1;
397                 }
398
399                 ret = heynoti_attach_handler(noti_fd);
400                 if(ret != 0) {
401                         LOGERR("heynoti attach handler fail!");
402                         return -1;
403                 }
404         }
405         return 0;
406 }
407
408 void end_battinfo_gathering()
409 {
410         LOGINFO("End battery gathering!");
411
412         if(timeout_id > 0) {
413                 g_source_remove(timeout_id);
414                 timeout_id = 0;
415         }
416         if(noti_fd > 0) {
417                 heynoti_close(noti_fd);
418                 noti_fd = 0;
419         }
420
421         del_all_batt_node(B_UNCHARGING);
422         del_all_batt_node(B_CHARGING);
423 }
424