merge with master
[platform/core/system/system-server.git] / ss_cpu_handler.c
1 /*
2  * Copyright (c) 2012 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <fcntl.h>
19
20 #include "device-node.h"
21 #include "ss_log.h"
22 #include "include/ss_data.h"
23 #include "vconf.h"
24
25 #define DEFAULT_MAX_CPU_FREQ            1200000
26 #define DEFAULT_MIN_CPU_FREQ            100000
27 #define POWER_SAVING_CPUFREQ            800000
28
29 static int max_cpu_freq_limit = -1;
30 static int min_cpu_freq_limit = -1;
31 static int cur_max_cpu_freq = INT_MAX;
32 static int cur_min_cpu_freq = INT_MIN;
33
34 static Eina_List *max_cpu_freq_list;
35 static Eina_List *min_cpu_freq_list;
36
37 struct cpu_freq_entry {
38         int pid;
39         int freq;
40 };
41
42 static void __set_freq_limit();
43 static int __is_entry_enble(int pid);
44 static int __remove_entry_from_max_cpu_freq_list(int pid);
45 static int __remove_entry_from_min_cpu_freq_list(int pid);
46 static int __add_entry_to_max_cpu_freq_list(int pid, int freq);
47 static int __add_entry_to_min_cpu_freq_list(int pid, int freq);
48 static int __write_max_cpu_freq(int freq);
49 static int __write_min_cpu_freq(int freq);
50
51 int set_max_frequency_action(int argc, char **argv)
52 {
53         int r = -1;
54         
55         if (argc < 2)
56                 return -1;
57
58         r = __add_entry_to_max_cpu_freq_list(atoi(argv[0]), atoi(argv[1]));
59         if (r < 0) {
60                 PRT_TRACE_ERR("Add entry failed");
61                 return -1;
62         }
63
64         r = __write_max_cpu_freq(cur_max_cpu_freq);
65         if (r < 0) {
66                 PRT_TRACE_ERR("Write entry failed");
67                 return -1;
68         }
69
70         return 0;
71 }
72
73 int set_min_frequency_action(int argc, char **argv)
74 {
75         int r = -1;
76         
77         if (argc < 2)
78                 return -1;
79
80         r = __add_entry_to_min_cpu_freq_list(atoi(argv[0]), atoi(argv[1]));
81         if (r < 0) {
82                 PRT_TRACE_ERR("Add entry failed");
83                 return -1;
84         }
85         
86         r = __write_min_cpu_freq(cur_min_cpu_freq);
87         if (r < 0) {
88                 PRT_TRACE_ERR("Write entry failed");
89                 return -1;
90         }
91
92         return 0;
93 }
94
95 int release_max_frequency_action(int argc, char **argv)
96 {
97         int r = -1;
98         if (argc < 1)
99                 return -1;
100         
101         r = __remove_entry_from_max_cpu_freq_list(atoi(argv[0]));
102         if (r < 0) {
103                 PRT_TRACE_ERR("Remove entry failed");
104                 return -1;
105         }
106
107         if (cur_max_cpu_freq == INT_MAX)
108                 cur_max_cpu_freq = max_cpu_freq_limit;
109
110         r = __write_max_cpu_freq(cur_max_cpu_freq);
111         if (r < 0) {
112                 PRT_TRACE_ERR("Write freq failed");
113                 return -1;
114         }
115
116         return 0;
117 }
118
119 int release_min_frequency_action(int argc, char **argv)
120 {
121         int r = -1;
122
123         if (argc < 1)
124                 return -1;
125
126         r = __remove_entry_from_min_cpu_freq_list(atoi(argv[0]));
127         if (r < 0) {
128                 PRT_TRACE_ERR("Remove entry failed");
129                 return -1;
130         }
131
132         if (cur_min_cpu_freq == INT_MIN)
133                 cur_min_cpu_freq = min_cpu_freq_limit;
134
135         r = __write_min_cpu_freq(cur_min_cpu_freq);
136         if (r < 0) {
137                 PRT_TRACE_ERR("Write entry failed");
138                 return -1;
139         }
140
141         return 0;
142 }
143
144 static int power_saving_cb(keynode_t *key_nodes, void *data)
145 {
146         int ret = -1;
147         int power_saving_stat = -1;
148         int power_saving_cpu_stat = -1;
149
150         power_saving_stat = vconf_keynode_get_bool(key_nodes);
151         if (power_saving_stat == 1) {
152                 if (vconf_get_bool(VCONFKEY_SETAPPL_PWRSV_CUSTMODE_CPU, &power_saving_cpu_stat) == 0) {
153                         if (power_saving_cpu_stat == 1) {
154                                 ret = __add_entry_to_max_cpu_freq_list(getpid(), POWER_SAVING_CPUFREQ);
155                                 if (ret < 0) {
156                                         PRT_TRACE_ERR("Add entry failed");
157                                         return -1;
158                                 }
159                         }
160                 } else {
161                         PRT_TRACE_ERR("failed to get vconf key");
162                         return -1;
163                 }
164         } else {
165                 ret = __remove_entry_from_max_cpu_freq_list(getpid());
166                 if (ret < 0) {
167                         PRT_TRACE_ERR("Remove entry failed");
168                         return -1;
169                 }
170                 if (cur_max_cpu_freq == INT_MIN)
171                         cur_max_cpu_freq = max_cpu_freq_limit;
172         }
173         ret = __write_max_cpu_freq(cur_max_cpu_freq);
174         if (ret < 0) {
175                 PRT_TRACE_ERR("Write failed");
176                 return -1;
177         }
178
179         return 0;
180 }
181
182 static int power_saving_cpu_cb(keynode_t *key_nodes, void *data)
183 {
184         int ret = -1;
185         int power_saving_stat = -1;
186         int power_saving_cpu_stat = -1;
187
188         if (vconf_get_bool(VCONFKEY_SETAPPL_PWRSV_SYSMODE_STATUS, &power_saving_stat) == 0) {
189                 if (power_saving_stat == 1) {
190                         power_saving_cpu_stat = vconf_keynode_get_bool(key_nodes);
191                         if (power_saving_cpu_stat == 1) {
192                                 ret = __add_entry_to_max_cpu_freq_list(getpid(), POWER_SAVING_CPUFREQ);
193                                 if (ret < 0) {
194                                         PRT_TRACE_ERR("Add entry failed");
195                                         return -1;
196                                 }
197                         } else {
198                                 ret = __remove_entry_from_max_cpu_freq_list(getpid());
199                                 if (ret < 0) {
200                                         PRT_TRACE_ERR("Remove entry failed");
201                                         return -1;
202                                 }
203                                 if (cur_max_cpu_freq == INT_MAX)
204                                         cur_max_cpu_freq = max_cpu_freq_limit;
205                         }
206                         ret = __write_max_cpu_freq(cur_max_cpu_freq);
207                         if (ret < 0) {
208                                 PRT_TRACE_ERR("Write failed");
209                                 return -1;
210                         }
211                 }
212         } else {
213                 PRT_TRACE_ERR("failed to get vconf key");
214                 return -1;
215         }
216
217         return 0;
218 }
219
220 int ss_cpu_handler_init(void)
221 {
222         __set_freq_limit();
223         
224         ss_action_entry_add_internal(PREDEF_SET_MAX_FREQUENCY, set_max_frequency_action, NULL, NULL);
225         ss_action_entry_add_internal(PREDEF_SET_MIN_FREQUENCY, set_min_frequency_action, NULL, NULL);
226         ss_action_entry_add_internal(PREDEF_RELEASE_MAX_FREQUENCY, release_max_frequency_action, NULL, NULL);
227         ss_action_entry_add_internal(PREDEF_RELEASE_MIN_FREQUENCY, release_min_frequency_action, NULL, NULL);
228
229         vconf_notify_key_changed(VCONFKEY_SETAPPL_PWRSV_SYSMODE_STATUS, (void *)power_saving_cb, NULL);
230         vconf_notify_key_changed(VCONFKEY_SETAPPL_PWRSV_CUSTMODE_CPU, (void *)power_saving_cpu_cb, NULL);
231
232         return 0;
233 }
234
235 static void __set_freq_limit()
236 {
237         int ret;
238
239         ret = device_get_property(DEVICE_TYPE_CPU, PROP_CPU_CPUINFO_MAX_FREQ, &max_cpu_freq_limit);
240         if (ret < 0) {
241                 PRT_TRACE_ERR("get cpufreq cpuinfo max readerror: %s", strerror(errno));
242                 max_cpu_freq_limit = DEFAULT_MAX_CPU_FREQ;
243         }
244
245         ret = device_get_property(DEVICE_TYPE_CPU, PROP_CPU_CPUINFO_MIN_FREQ, &min_cpu_freq_limit);
246         if (ret < 0) {
247                 PRT_TRACE_ERR("get cpufreq cpuinfo min readerror: %s", strerror(errno));
248                 min_cpu_freq_limit = DEFAULT_MIN_CPU_FREQ;
249         }
250
251         /* check power saving */
252         int power_saving_stat = -1;
253         int power_saving_cpu_stat = -1;
254         if (vconf_get_bool(VCONFKEY_SETAPPL_PWRSV_SYSMODE_STATUS, &power_saving_stat) == 0) {
255                 if (power_saving_stat == 1) {
256                         if (vconf_get_bool(VCONFKEY_SETAPPL_PWRSV_CUSTMODE_CPU, &power_saving_cpu_stat) == 0) {
257                                 if (power_saving_cpu_stat == 1) {
258                                         ret = __add_entry_to_max_cpu_freq_list(getpid(), POWER_SAVING_CPUFREQ);
259                                         if (ret < 0) {
260                                                 PRT_TRACE_ERR("Add entry failed");
261                                                 return;
262                                         }
263                                         ret = __write_max_cpu_freq(cur_max_cpu_freq);
264                                         if (ret < 0) {
265                                                 PRT_TRACE_ERR("Write entry failed");
266                                                 return;
267                                         }
268                                 }
269                         } else {
270                                 PRT_TRACE_ERR("failed to get vconf key");
271                         }
272                 }
273         } else {
274                 PRT_TRACE_ERR("failed to get vconf key");
275         }
276 }
277
278 static int __is_entry_enable(int pid)
279 {
280         char pid_path[PATH_MAX];
281         
282         snprintf(pid_path, PATH_MAX, "/proc/%d", pid);
283         if (access(pid_path, F_OK) < 0) {
284                 return 0;
285         }
286
287         return 1;
288 }
289
290 static int __remove_entry_from_max_cpu_freq_list(int pid)
291 {
292         Eina_List *tmp;
293         Eina_List *tmp_next;
294         struct cpu_freq_entry *entry;
295
296         cur_max_cpu_freq = INT_MAX;
297
298         EINA_LIST_FOREACH_SAFE(max_cpu_freq_list, tmp, tmp_next, entry) {
299                 if (entry != NULL) {
300                         if ((!__is_entry_enable(entry->pid)) || (entry->pid == pid)) {
301                                 max_cpu_freq_list = eina_list_remove(max_cpu_freq_list, entry);
302                                 free(entry);
303                                 continue;
304                         }
305
306                         if (entry->freq < cur_max_cpu_freq) {
307                                 cur_max_cpu_freq = entry->freq;
308                         }
309                 }
310         }
311
312         return 0;
313 }
314
315 static int __remove_entry_from_min_cpu_freq_list(int pid)
316 {
317         Eina_List *tmp;
318         Eina_List *tmp_next;
319         struct cpu_freq_entry *entry;
320
321         cur_min_cpu_freq = INT_MIN;
322
323         EINA_LIST_FOREACH_SAFE(min_cpu_freq_list, tmp, tmp_next, entry) {
324                 if (entry != NULL) {
325                         if ((!__is_entry_enable(entry->pid)) || (entry->pid == pid)) {
326                                 min_cpu_freq_list = eina_list_remove(min_cpu_freq_list, entry);
327                                 free(entry);
328                                 continue;
329                         }
330
331                         if (entry->freq > cur_min_cpu_freq) {
332                                 cur_min_cpu_freq = entry->freq;
333                         }
334
335                 }
336         }
337
338         return 0;
339 }
340
341 static int __add_entry_to_max_cpu_freq_list(int pid, int freq)
342 {
343         int r = -1;
344         struct cpu_freq_entry *entry;
345         
346         r = __remove_entry_from_max_cpu_freq_list(pid);
347         if (r < 0) {
348                 PRT_TRACE_ERR("Remove duplicated entry failed");
349         }
350
351         if (freq < cur_max_cpu_freq) {
352                 cur_max_cpu_freq = freq;
353         }
354
355         entry = malloc(sizeof(struct cpu_freq_entry));
356         if (!entry) {
357                 PRT_TRACE_ERR("Malloc failed");
358                 return -1;
359         }
360         
361         entry->pid = pid;
362         entry->freq = freq;
363
364         max_cpu_freq_list = eina_list_prepend(max_cpu_freq_list, entry);
365         if (!max_cpu_freq_list) {
366                 PRT_TRACE_ERR("eina_list_prepend failed");
367                 return -1;
368         }
369
370         return 0;
371 }
372
373 static int __add_entry_to_min_cpu_freq_list(int pid, int freq)
374 {
375         int r = -1;
376         struct cpu_freq_entry *entry;
377         
378         r = __remove_entry_from_min_cpu_freq_list(pid);
379         if (r < 0) {
380                 PRT_TRACE_ERR("Remove duplicated entry failed");
381         }
382
383         if (freq > cur_min_cpu_freq) {
384                 cur_min_cpu_freq = freq;
385         }
386
387         entry = malloc(sizeof(struct cpu_freq_entry));
388         if (!entry) {
389                 PRT_TRACE_ERR("Malloc failed");
390                 return -1;
391         }
392         
393         entry->pid = pid;
394         entry->freq = freq;
395
396         min_cpu_freq_list = eina_list_prepend(min_cpu_freq_list, entry);
397         if (!min_cpu_freq_list) {
398                 PRT_TRACE_ERR("eina_list_prepend failed");
399                 return -1;
400         }
401         
402         return 0;
403 }
404
405 static int __write_max_cpu_freq(int freq)
406 {
407         int ret;
408
409         ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_SCALING_MAX_FREQ, freq);
410         if (ret < 0) {
411                 PRT_TRACE_ERR("set cpufreq max freq write error: %s", strerror(errno));
412                 return -1;
413         }
414         
415         return 0;
416 }
417
418 static int __write_min_cpu_freq(int freq)
419 {
420         int ret;
421
422         ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_SCALING_MIN_FREQ, freq);
423         if (ret < 0) {
424                 PRT_TRACE_ERR("set cpufreq min freq write error: %s", strerror(errno));
425                 return -1;
426         }
427         
428         return 0;
429 }