4 * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 #include <device-node.h>
24 #include "core/list.h"
26 #include "core/devices.h"
27 #include "core/edbus-handler.h"
28 #include "core/common.h"
29 #include "core/device-notifier.h"
30 #include "proc/proc-handler.h"
32 #define SET_MAX_FREQ "set_max_frequency"
33 #define SET_MIN_FREQ "set_min_frequency"
34 #define SET_FREQ_LEN 17
36 #define RELEASE_MAX_FREQ "release_max_frequency"
37 #define RELEASE_MIN_FREQ "release_min_frequency"
38 #define RELEASE_FREQ_LEN 21
40 #define POWER_SAVING_CPU_FREQ_RATE (0.7)
42 #define DEFAULT_MAX_CPU_FREQ 1200000
43 #define DEFAULT_MIN_CPU_FREQ 100000
50 static int max_cpu_freq_limit = -1;
51 static int min_cpu_freq_limit = -1;
52 static int cur_max_cpu_freq = INT_MAX;
53 static int cur_min_cpu_freq = INT_MIN;
54 static int power_saving_freq = -1;
56 static dd_list *max_cpu_freq_list;
57 static dd_list *min_cpu_freq_list;
59 static int cpu_number_limit = -1;
60 static int cur_cpu_number = INT_MAX;
61 static dd_list *cpu_number_list;
63 struct cpu_freq_entry {
68 struct cpu_number_entry {
73 static int is_entry_enable(int pid)
75 char pid_path[PATH_MAX];
77 snprintf(pid_path, PATH_MAX, "/proc/%d", pid);
78 if (access(pid_path, F_OK) < 0) {
85 static int write_min_cpu_freq(int freq)
89 ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_SCALING_MIN_FREQ, freq);
91 _E("set cpufreq min freq write error: %s", strerror(errno));
98 static int write_max_cpu_freq(int freq)
102 ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_SCALING_MAX_FREQ, freq);
104 _E("set cpufreq max freq write error: %s", strerror(errno));
111 static int remove_entry_from_min_cpu_freq_list(int pid)
114 struct cpu_freq_entry *entry;
116 cur_min_cpu_freq = INT_MIN;
118 DD_LIST_FOREACH(min_cpu_freq_list, tmp, entry) {
119 if ((!is_entry_enable(entry->pid)) || (entry->pid == pid)) {
120 DD_LIST_REMOVE(min_cpu_freq_list, entry);
124 if (entry->freq > cur_min_cpu_freq) {
125 cur_min_cpu_freq = entry->freq;
132 static int remove_entry_from_max_cpu_freq_list(int pid)
135 struct cpu_freq_entry *entry;
137 cur_max_cpu_freq = INT_MAX;
139 DD_LIST_FOREACH(max_cpu_freq_list, tmp, entry) {
140 if ((!is_entry_enable(entry->pid)) || (entry->pid == pid)) {
141 DD_LIST_REMOVE(max_cpu_freq_list, entry);
145 if (entry->freq < cur_max_cpu_freq) {
146 cur_max_cpu_freq = entry->freq;
153 int release_max_frequency_action(int argc, char **argv)
160 r = remove_entry_from_max_cpu_freq_list(atoi(argv[0]));
162 _E("Remove entry failed");
166 if (cur_max_cpu_freq == INT_MAX)
167 cur_max_cpu_freq = max_cpu_freq_limit;
169 r = write_max_cpu_freq(cur_max_cpu_freq);
171 _E("Write freq failed");
178 int release_min_frequency_action(int argc, char **argv)
185 r = remove_entry_from_min_cpu_freq_list(atoi(argv[0]));
187 _E("Remove entry failed");
191 if (cur_min_cpu_freq == INT_MIN)
192 cur_min_cpu_freq = min_cpu_freq_limit;
194 r = write_min_cpu_freq(cur_min_cpu_freq);
196 _E("Write entry failed");
203 static int add_entry_to_max_cpu_freq_list(int pid, int freq)
206 struct cpu_freq_entry *entry;
208 r = remove_entry_from_max_cpu_freq_list(pid);
210 _E("Remove duplicated entry failed");
213 entry = malloc(sizeof(struct cpu_freq_entry));
222 DD_LIST_PREPEND(max_cpu_freq_list, entry);
223 if (!max_cpu_freq_list) {
224 _E("eina_list_prepend failed");
227 if (freq < cur_max_cpu_freq) {
228 cur_max_cpu_freq = freq;
233 static int add_entry_to_min_cpu_freq_list(int pid, int freq)
236 struct cpu_freq_entry *entry;
238 r = remove_entry_from_min_cpu_freq_list(pid);
240 _E("Remove duplicated entry failed");
243 entry = malloc(sizeof(struct cpu_freq_entry));
252 DD_LIST_PREPEND(min_cpu_freq_list, entry);
253 if (!min_cpu_freq_list) {
254 _E("eina_list_prepend failed");
257 if (freq > cur_min_cpu_freq) {
258 cur_min_cpu_freq = freq;
263 int set_max_frequency_action(int argc, char **argv)
270 r = add_entry_to_max_cpu_freq_list(atoi(argv[0]), atoi(argv[1]));
272 _E("Add entry failed");
276 r = write_max_cpu_freq(cur_max_cpu_freq);
278 _E("Write entry failed");
285 int set_min_frequency_action(int argc, char **argv)
292 r = add_entry_to_min_cpu_freq_list(atoi(argv[0]), atoi(argv[1]));
294 _E("Add entry failed");
298 r = write_min_cpu_freq(cur_min_cpu_freq);
300 _E("Write entry failed");
307 static int power_saving_cpu_cb(keynode_t *key_nodes, void *data)
311 int power_saving_cpu_stat = -1;
313 power_saving_cpu_stat = vconf_keynode_get_bool(key_nodes);
314 if (power_saving_cpu_stat == 1) {
316 ret = add_entry_to_max_cpu_freq_list(getpid(), power_saving_freq);
318 _E("Add entry failed");
322 ret = remove_entry_from_max_cpu_freq_list(getpid());
324 _E("Remove entry failed");
327 if (cur_max_cpu_freq == INT_MAX)
328 cur_max_cpu_freq = max_cpu_freq_limit;
330 ret = write_max_cpu_freq(cur_max_cpu_freq);
334 device_notify(DEVICE_NOTIFIER_PMQOS_POWERSAVING, (void*)val);
338 static void set_emergency_limit(void)
342 ret = vconf_get_int(VCONFKEY_SETAPPL_PSMODE, &val);
344 _E("failed to get vconf key");
347 if (val == SETTING_PSMODE_EMERGENCY) {
348 val = EMERGENCY_LOCK;
349 device_notify(DEVICE_NOTIFIER_PMQOS_EMERGENCY, (void*)val);
353 static int emergency_cpu_cb(keynode_t *key_nodes, void *data)
357 val = vconf_keynode_get_int(key_nodes);
358 if (val == SETTING_PSMODE_EMERGENCY)
359 val = EMERGENCY_LOCK;
361 val = EMERGENCY_UNLOCK;
363 device_notify(DEVICE_NOTIFIER_PMQOS_EMERGENCY, (void*)val);
367 static void set_freq_limit(void)
371 int power_saving_stat = -1;
372 int power_saving_cpu_stat = -1;
374 ret = device_get_property(DEVICE_TYPE_CPU, PROP_CPU_CPUINFO_MAX_FREQ,
375 &max_cpu_freq_limit);
377 _E("get cpufreq cpuinfo max readerror: %s", strerror(errno));
378 max_cpu_freq_limit = DEFAULT_MAX_CPU_FREQ;
381 ret = device_get_property(DEVICE_TYPE_CPU, PROP_CPU_CPUINFO_MIN_FREQ,
382 &min_cpu_freq_limit);
384 _E("get cpufreq cpuinfo min readerror: %s", strerror(errno));
385 min_cpu_freq_limit = DEFAULT_MIN_CPU_FREQ;
387 power_saving_freq = (int)(max_cpu_freq_limit * POWER_SAVING_CPU_FREQ_RATE);
388 _I("max(%d) , ps(%d), min(%d)",
393 ret = vconf_get_bool(VCONFKEY_SETAPPL_PWRSV_CUSTMODE_CPU,
394 &power_saving_cpu_stat);
396 _E("failed to get vconf key");
399 if (power_saving_cpu_stat != 1)
402 ret = add_entry_to_max_cpu_freq_list(getpid(), power_saving_freq);
404 _E("Add entry failed");
407 ret = write_max_cpu_freq(cur_max_cpu_freq);
409 _E("Write entry failed");
412 device_notify(DEVICE_NOTIFIER_PMQOS_POWERSAVING, (void*)val);
415 static void set_cpu_number_limit(void)
417 device_get_property(DEVICE_TYPE_CPU, PROP_CPU_ENABLE_MAX_NUMBER,
421 static int write_cpu_number(int number)
425 ret = device_set_property(DEVICE_TYPE_CPU, PROP_CPU_ENABLE_MAX_NUMBER,
428 _E("set cpu number max write error: %s", strerror(errno));
435 static int remove_entry_from_cpu_number_list(int pid)
438 struct cpu_number_entry *entry;
440 cur_cpu_number = INT_MAX;
442 DD_LIST_FOREACH(cpu_number_list, tmp, entry) {
443 if ((!is_entry_enable(entry->pid)) || (entry->pid == pid)) {
444 DD_LIST_REMOVE(cpu_number_list, entry);
448 if (entry->number < cur_cpu_number) {
449 cur_cpu_number = entry->number;
456 static int add_entry_to_cpu_number_list(int pid, int number)
459 struct cpu_number_entry *entry;
461 r = remove_entry_from_cpu_number_list(pid);
463 _E("Remove duplicated entry failed");
468 entry = malloc(sizeof(struct cpu_number_entry));
475 entry->number = number;
477 DD_LIST_PREPEND(cpu_number_list, entry);
478 if (!cpu_number_list) {
479 _E("eina_list_prepend failed");
482 if (number < cur_cpu_number) {
483 cur_cpu_number = number;
488 static int booting_done(void *data)
491 set_emergency_limit();
495 static DBusMessage *dbus_cpu_handler(E_DBus_Object *obj, DBusMessage *msg)
498 DBusMessageIter iter;
506 dbus_error_init(&err);
508 if (!dbus_message_get_args(msg, &err,
509 DBUS_TYPE_STRING, &type_str,
510 DBUS_TYPE_INT32, &argc,
511 DBUS_TYPE_STRING, &argv[0],
512 DBUS_TYPE_STRING, &argv[1], DBUS_TYPE_INVALID)) {
513 _E("there is no message");
519 _E("message is invalid!");
524 pid = get_edbus_sender_pid(msg);
525 if (kill(pid, 0) == -1) {
526 _E("%d process does not exist, dbus ignored!", pid);
531 if (!strncmp(type_str, SET_MAX_FREQ, SET_FREQ_LEN))
532 ret = set_max_frequency_action(argc, (char **)&argv);
533 else if (!strncmp(type_str, SET_MIN_FREQ, SET_FREQ_LEN))
534 ret = set_min_frequency_action(argc, (char **)&argv);
535 else if (!strncmp(type_str, RELEASE_MAX_FREQ, RELEASE_FREQ_LEN))
536 ret = release_max_frequency_action(argc, (char **)&argv);
537 else if (!strncmp(type_str, RELEASE_MIN_FREQ, RELEASE_FREQ_LEN))
538 ret = release_min_frequency_action(argc, (char **)&argv);
540 reply = dbus_message_new_method_return(msg);
541 dbus_message_iter_init_append(reply, &iter);
542 dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &ret);
547 static const struct edbus_method edbus_methods[] = {
548 { SET_MAX_FREQ, "siss", "i", dbus_cpu_handler },
549 { SET_MIN_FREQ, "siss", "i", dbus_cpu_handler },
550 { RELEASE_MAX_FREQ, "siss", "i", dbus_cpu_handler },
551 { RELEASE_MIN_FREQ, "siss", "i", dbus_cpu_handler },
554 static void cpu_init(void *data)
558 register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, booting_done);
559 ret = register_edbus_method(DEVICED_PATH_SYSNOTI, edbus_methods, ARRAY_SIZE(edbus_methods));
561 _E("fail to init edbus method(%d)", ret);
562 set_cpu_number_limit();
564 vconf_notify_key_changed(VCONFKEY_SETAPPL_PWRSV_CUSTMODE_CPU, (void *)power_saving_cpu_cb, NULL);
565 vconf_notify_key_changed(VCONFKEY_SETAPPL_PSMODE, (void *)emergency_cpu_cb, NULL);
568 static const struct device_ops cpu_device_ops = {
569 .priority = DEVICE_PRIORITY_NORMAL,
574 DEVICE_OPS_REGISTER(&cpu_device_ops)