summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/hal-backend-power.c775
-rw-r--r--src/sysfs.c129
-rw-r--r--src/sysfs.h6
3 files changed, 910 insertions, 0 deletions
diff --git a/src/hal-backend-power.c b/src/hal-backend-power.c
new file mode 100644
index 0000000..c4f52ad
--- /dev/null
+++ b/src/hal-backend-power.c
@@ -0,0 +1,775 @@
+/*
+ * HAL backend for hal-api-power which is used for PASS daemon
+ *
+ * Copyright (c) 2021 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <hal/hal-power-interface.h>
+
+#include "sysfs.h"
+
+#define BUFF_MAX 255
+
+/*************************
+ * H/W Resource Defintions
+ */
+#define CPUFREQ_PATH_PREFIX "/sys/devices/system/cpu/"
+#define CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX "/cpufreq/scaling_governor"
+#define CPUFREQ_AVAIL_GOVERNOR_PATH_SUFFIX "/cpufreq/scaling_available_governors"
+
+/*
+ * The cpuinfo_cur_freq indicates the actual operating CPU freqeuncy
+ * and scaling_cur_freq is the CPU frequency set by the CPUFREQ policy.
+ */
+#define CPUFREQ_CURR_FREQ_PATH_SUFFIX "/cpufreq/cpuinfo_cur_freq"
+#define CPUFREQ_AVAILABLE_MIN_FREQ_PATH_SUFFIX "/cpufreq/cpuinfo_min_freq"
+#define CPUFREQ_AVAILABLE_MAX_FREQ_PATH_SUFFIX "/cpufreq/cpuinfo_max_freq"
+#define CPUFREQ_MIN_FREQ_PATH_SUFFIX "/cpufreq/scaling_min_freq"
+#define CPUFREQ_MAX_FREQ_PATH_SUFFIX "/cpufreq/scaling_max_freq"
+#define CPUFREQ_UP_THRESHOLD_PATH_SUFFIX "/cpufreq/ondemand/up_threshold"
+
+#define CPU_ONLINE_PATH_PREFIX "/sys/devices/system/cpu/cpu"
+#define CPU_ONLINE_PATH_SUFFIX "/online"
+#define CPU_ONLINE_STATE_ON 1
+#define CPU_ONLINE_STATE_OFF 0
+
+#define TMU_PATH_PREFIX "/sys/class/thermal/"
+#define TMU_TEMP_PATH_SUFFIX "/temp"
+#define TMU_POLICY_PATH_SUFFIX "/policy"
+#define TMU_MAX_TEMP_MASK 0xFFFFF
+
+#define DEVFREQ_BUS_PATH_PREFIX "/sys/class/devfreq/"
+#define DEVFREQ_BUS_CURR_GOVERNOR_PATH_SUFFIX "/governor"
+#define DEVFREQ_BUS_CURR_FREQ_PATH_SUFFIX "/cur_freq"
+#define DEVFREQ_BUS_MIN_FREQ_PATH_SUFFIX "/min_freq"
+#define DEVFREQ_BUS_MAX_FREQ_PATH_SUFFIX "/max_freq"
+#define DEVFREQ_BUS_AVAILABLE_FREQ_PATH_SUFFIX "/available_frequencies"
+
+#define FAULT_AROUND_BYTES_PATH "/sys/kernel/debug/fault_around_bytes"
+
+/*************************************************
+ * HAL backend implementation for CPU H/W Resource
+ */
+static int cpufreq_dvfs_get_curr_governor(char *res_name, char *governor)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name) || (!governor))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX);
+
+ ret = sysfs_read_str(path, governor, BUFF_MAX);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cpufreq_dvfs_set_curr_governor(char *res_name, char *governor)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name) || (!governor))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_CURR_GOVERNOR_PATH_SUFFIX);
+
+ ret = sysfs_write_str(path, governor);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cpufreq_dvfs_get_curr_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ int freq, ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_CURR_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &freq);
+ if (ret < 0)
+ return ret;
+
+ return freq;
+}
+
+static int cpufreq_dvfs_get_min_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ int freq, ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_MIN_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &freq);
+ if (ret < 0)
+ return ret;
+
+ return freq;
+}
+
+static int cpufreq_dvfs_set_min_freq(char *res_name, int freq)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name) || (freq < 0))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_MIN_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_write_int(path, freq);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int cpufreq_dvfs_get_max_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ int freq, ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_MAX_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &freq);
+ if (ret < 0)
+ return ret;
+
+ return freq;
+}
+
+static int cpufreq_dvfs_set_max_freq(char *res_name, int freq)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name) || (freq < 0))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_MAX_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_write_int(path, freq);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int cpufreq_dvfs_get_available_min_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ int val, ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_AVAILABLE_MIN_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+
+static int cpufreq_dvfs_get_available_max_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ int val, ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_AVAILABLE_MAX_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int cpufreq_dvfs_get_up_threshold(char *res_name)
+{
+ char path[PATH_MAX];
+ int val, ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_UP_THRESHOLD_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &val);
+ if (ret < 0)
+ return ret;
+
+ return val;
+}
+
+static int cpufreq_dvfs_set_up_threshold(char *res_name, int up_threshold)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name) || (up_threshold < 0))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ CPUFREQ_PATH_PREFIX,
+ res_name,
+ CPUFREQ_UP_THRESHOLD_PATH_SUFFIX);
+
+ ret = sysfs_write_int(path, up_threshold);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct pass_resource_dvfs_ops cpufreq_dvfs_ops = {
+ .get_curr_governor = cpufreq_dvfs_get_curr_governor,
+ .set_curr_governor = cpufreq_dvfs_set_curr_governor,
+ .get_curr_freq = cpufreq_dvfs_get_curr_freq,
+ .get_min_freq = cpufreq_dvfs_get_min_freq,
+ .set_min_freq = cpufreq_dvfs_set_min_freq,
+ .get_max_freq = cpufreq_dvfs_get_max_freq,
+ .set_max_freq = cpufreq_dvfs_set_max_freq,
+ .get_available_min_freq = cpufreq_dvfs_get_available_min_freq,
+ .get_available_max_freq = cpufreq_dvfs_get_available_max_freq,
+ .get_up_threshold = cpufreq_dvfs_get_up_threshold,
+ .set_up_threshold = cpufreq_dvfs_set_up_threshold,
+};
+
+static int cpu_hotplug_get_online_state(char *res_name, int cpu)
+{
+ char path[PATH_MAX];
+ int ret, online;
+
+ if ((!res_name))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%d%s",
+ CPU_ONLINE_PATH_PREFIX,
+ cpu,
+ CPU_ONLINE_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &online);
+ if (ret < 0)
+ return ret;
+
+ return online;
+}
+
+static int cpu_hotplug_set_online_state(char *res_name, int cpu, int on)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name))
+ return -EINVAL;
+ if ((on != CPU_ONLINE_STATE_ON) && (on != CPU_ONLINE_STATE_OFF))
+ return -EINVAL;
+
+ /*
+ * NOTE: Exynos SoC series cannot turn off the CPU0
+ * because of h/w design. To prevent the critical problem,
+ * if someone try to turn off the CPU0, just return without any
+ * opertaion.
+ */
+ if (on == 0 && cpu == 0) {
+ return 0;
+ }
+
+ snprintf(path, PATH_MAX, "%s%d%s",
+ CPU_ONLINE_PATH_PREFIX,
+ cpu,
+ CPU_ONLINE_PATH_SUFFIX);
+
+ ret = sysfs_write_int(path, on);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct pass_resource_hotplug_ops cpu_hotplus_ops = {
+ .get_online_state = cpu_hotplug_get_online_state,
+ .set_online_state = cpu_hotplug_set_online_state,
+ .get_online_min_num = NULL,
+ .set_online_min_num = NULL,
+ .get_online_max_num = NULL,
+ .set_online_max_num = NULL,
+};
+
+static int tmu_get_temp(char *res_thermal_name)
+{
+ char path[PATH_MAX];
+ int temp, ret;
+
+ if (!res_thermal_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ TMU_PATH_PREFIX,
+ res_thermal_name,
+ TMU_TEMP_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &temp);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Thermal framework provides the current temperature
+ * as five digits interger like 54430 when temperature is 54.430
+ * degrees centigrade. But, Thermal Monitor in Tizen usually
+ * use two digits interger without decimal point.
+ * So that round temperature value. It constraints the maximume
+ * temperature as 1048 degrees centigrade for preventing integer
+ * overflow. Usually, the embedded device never over 1000 degrees
+ * centigrade.
+ */
+ return (((temp & TMU_MAX_TEMP_MASK) + 500) / 1000);
+}
+
+static int tmu_get_policy(char *res_thermal_name, char *policy)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_thermal_name) || (!policy))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ TMU_PATH_PREFIX,
+ res_thermal_name,
+ TMU_POLICY_PATH_SUFFIX);
+
+ ret = sysfs_read_str(path, policy, BUFF_MAX);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct pass_resource_tmu_ops tmu_ops = {
+ .get_temp = tmu_get_temp,
+ .get_policy = tmu_get_policy,
+};
+
+/*****************************************************
+ * HAL backend implementation for BUS/GPU H/W Resource
+ */
+static int devfreq_dvfs_get_curr_governor(char *res_name, char *governor)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name) || (!governor))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ DEVFREQ_BUS_PATH_PREFIX,
+ res_name,
+ DEVFREQ_BUS_CURR_GOVERNOR_PATH_SUFFIX);
+
+ ret = sysfs_read_str(path, governor, BUFF_MAX);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int devfreq_dvfs_set_curr_governor(char *res_name, char *governor)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name) || (!governor))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ DEVFREQ_BUS_PATH_PREFIX,
+ res_name,
+ DEVFREQ_BUS_CURR_GOVERNOR_PATH_SUFFIX);
+
+ ret = sysfs_write_str(path, governor);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int devfreq_dvfs_get_curr_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ int freq, ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ DEVFREQ_BUS_PATH_PREFIX,
+ res_name,
+ DEVFREQ_BUS_CURR_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &freq);
+ if (ret < 0)
+ return ret;
+
+ return freq;
+}
+
+static int devfreq_dvfs_get_min_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ int freq, ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ DEVFREQ_BUS_PATH_PREFIX,
+ res_name,
+ DEVFREQ_BUS_MIN_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &freq);
+ if (ret < 0)
+ return ret;
+
+ return freq;
+}
+
+static int devfreq_dvfs_set_min_freq(char *res_name, int freq)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name) || (freq < 0))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ DEVFREQ_BUS_PATH_PREFIX,
+ res_name,
+ DEVFREQ_BUS_MIN_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_write_int(path, freq);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int devfreq_dvfs_get_max_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ int freq, ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ DEVFREQ_BUS_PATH_PREFIX,
+ res_name,
+ DEVFREQ_BUS_MAX_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_int(path, &freq);
+ if (ret < 0)
+ return ret;
+
+ return freq;
+}
+
+static int devfreq_dvfs_set_max_freq(char *res_name, int freq)
+{
+ char path[PATH_MAX];
+ int ret;
+
+ if ((!res_name) || (freq < 0))
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ DEVFREQ_BUS_PATH_PREFIX,
+ res_name,
+ DEVFREQ_BUS_MAX_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_write_int(path, freq);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static int devfreq_dvfs_get_available_min_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ char buf[MAX_BUF_SIZE + 1];
+ char *p;
+ int ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ DEVFREQ_BUS_PATH_PREFIX,
+ res_name,
+ DEVFREQ_BUS_AVAILABLE_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_str(path, buf, MAX_BUF_SIZE);
+ if (ret < 0)
+ return ret;
+
+ p = strchr(buf, ' ');
+ if (p)
+ *p = '\0';
+
+ ret = strtoul(buf, NULL, 10);
+ if (!ret)
+ return -EINVAL;
+
+ return ret;
+}
+
+static int devfreq_dvfs_get_available_max_freq(char *res_name)
+{
+ char path[PATH_MAX];
+ char buf[MAX_BUF_SIZE + 1];
+ char *p;
+ int ret;
+
+ if (!res_name)
+ return -EINVAL;
+
+ snprintf(path, PATH_MAX, "%s%s%s",
+ DEVFREQ_BUS_PATH_PREFIX,
+ res_name,
+ DEVFREQ_BUS_AVAILABLE_FREQ_PATH_SUFFIX);
+
+ ret = sysfs_read_str(path, buf, MAX_BUF_SIZE);
+ if (ret < 0)
+ return ret;
+
+ p = strrchr(buf, ' ');
+ p = p ? p + 1 : buf;
+
+ ret = strtoul(p, NULL, 10);
+ if (!ret)
+ return -EINVAL;
+
+ return ret;
+}
+
+static struct pass_resource_dvfs_ops devfreq_dvfs_ops = {
+ .get_curr_governor = devfreq_dvfs_get_curr_governor,
+ .set_curr_governor = devfreq_dvfs_set_curr_governor,
+ .get_curr_freq = devfreq_dvfs_get_curr_freq,
+ .get_min_freq = devfreq_dvfs_get_min_freq,
+ .set_min_freq = devfreq_dvfs_set_min_freq,
+ .get_max_freq = devfreq_dvfs_get_max_freq,
+ .set_max_freq = devfreq_dvfs_set_max_freq,
+ .get_available_min_freq = devfreq_dvfs_get_available_min_freq,
+ .get_available_max_freq = devfreq_dvfs_get_available_max_freq,
+};
+
+/****************************************************
+ * HAL backend implementation for Memory H/W Resource
+ */
+static int memory_get_fault_around_bytes(char *res_name)
+{
+ int ret, fault_around_bytes;
+
+ if (!res_name)
+ return -EINVAL;
+
+ ret = sysfs_read_int(FAULT_AROUND_BYTES_PATH, &fault_around_bytes);
+ if (ret < 0)
+ return ret;
+
+ return fault_around_bytes;
+}
+
+static int memory_set_fault_around_bytes(char *res_name,
+ int fault_around_bytes)
+{
+ int ret;
+
+ if ((!res_name) || (fault_around_bytes <= 0))
+ return -EINVAL;
+
+ ret = sysfs_write_int(FAULT_AROUND_BYTES_PATH, fault_around_bytes);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/************************
+ * HAL backend power data
+ */
+static int power_init(void **data)
+{
+ hal_backend_power_funcs *power_funcs = NULL;
+ struct pass_resource_cpu *cpu = NULL;
+ struct pass_resource_bus *bus = NULL;
+ struct pass_resource_gpu *gpu = NULL;
+ struct pass_resource_memory *memory = NULL;
+ int ret;
+
+ /* Allocate memory */
+ power_funcs = calloc(1, sizeof(hal_backend_power_funcs));
+ if (!power_funcs)
+ return -ENOMEM;
+
+ cpu = calloc(1, sizeof(struct pass_resource_cpu));
+ if (!cpu) {
+ ret = -ENOMEM;
+ goto err_funcs;
+ }
+
+ bus = calloc(1, sizeof(struct pass_resource_bus));
+ if (!bus) {
+ ret = -ENOMEM;
+ goto err_cpu;
+ }
+
+ gpu = calloc(1, sizeof(struct pass_resource_gpu));
+ if (!gpu) {
+ ret = -ENOMEM;
+ goto err_bus;
+ }
+
+ memory = calloc(1, sizeof(struct pass_resource_memory));
+ if (!memory) {
+ ret = -ENOMEM;
+ goto err_gpu;
+ }
+
+ /* Initialize each h/w resource */
+ cpu->dvfs = cpufreq_dvfs_ops;
+ cpu->hotplug = cpu_hotplus_ops;
+ cpu->tmu = tmu_ops;
+
+ bus->dvfs = devfreq_dvfs_ops;
+ bus->tmu = tmu_ops;
+
+ gpu->dvfs = devfreq_dvfs_ops;
+ gpu->tmu = tmu_ops;
+
+ memory->get_fault_around_bytes = memory_get_fault_around_bytes;
+ memory->set_fault_around_bytes = memory_set_fault_around_bytes;
+
+ /* Initialize hal_backend_power_funcs */
+ power_funcs->cpu = cpu;
+ power_funcs->bus = bus;
+ power_funcs->gpu = gpu;
+ power_funcs->memory = memory;
+
+ *data = (void *)power_funcs;
+
+ return 0;
+
+err_gpu:
+ if (gpu)
+ free(gpu);
+err_bus:
+ if (bus)
+ free(bus);
+err_cpu:
+ if (cpu)
+ free(cpu);
+err_funcs:
+ free(power_funcs);
+
+ return ret;
+}
+
+static int power_exit(void *data)
+{
+ hal_backend_power_funcs *funcs;
+
+ if (!data)
+ return -EINVAL;
+
+ funcs = (hal_backend_power_funcs *)data;
+
+ if (funcs->cpu)
+ free(funcs->cpu);
+ if (funcs->bus)
+ free(funcs->bus);
+ if (funcs->gpu)
+ free(funcs->gpu);
+ if (funcs->memory)
+ free(funcs->memory);
+
+ free(funcs);
+
+ return 0;
+}
+
+hal_backend hal_backend_power_data = {
+ .name = "power-cpu",
+ .vendor = "",
+ .abi_version = HAL_ABI_VERSION_TIZEN_6_5,
+ .init = power_init,
+ .exit = power_exit,
+};
diff --git a/src/sysfs.c b/src/sysfs.c
new file mode 100644
index 0000000..23768ce
--- /dev/null
+++ b/src/sysfs.c
@@ -0,0 +1,129 @@
+/*
+ * PASS (Power Aware System Service)
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "sysfs.h"
+
+static int sysfs_read_buf(char *path, char *buf, int len)
+{
+ int r, fd;
+
+ if ((!path) || (!buf) || (len < 0))
+ return -EINVAL;
+
+ fd = open(path, O_RDONLY);
+ if (fd == -1)
+ return -ENOENT;
+
+ r = read(fd, buf, len);
+ close(fd);
+
+ if (r < 0)
+ return r;
+ if (r > len)
+ return -EIO;
+
+ buf[r] = '\0';
+
+ return 0;
+}
+
+static int sysfs_write_buf(char *path, char *buf)
+{
+ int w, fd;
+
+ if ((!path) || (!buf))
+ return -EINVAL;
+
+ fd = open(path, O_WRONLY);
+ if (fd == -1)
+ return -ENOENT;
+
+ w = write(fd, buf, strlen(buf));
+ close(fd);
+
+ if (w < 0)
+ return w;
+
+ return 0;
+}
+
+int sysfs_read_int(char *path, int *val)
+{
+ char buf[MAX_BUF_SIZE + 1];
+ int r;
+
+ if ((!path) || (!val))
+ return -EINVAL;
+
+ r = sysfs_read_buf(path, buf, MAX_BUF_SIZE);
+ if (r < 0)
+ return r;
+
+ *val = atoi(buf);
+ return 0;
+}
+
+int sysfs_read_str(char *path, char *str, int len)
+{
+ int r;
+
+ if ((!path) || (!str) || (len <= 0))
+ return -EINVAL;
+
+ r = sysfs_read_buf(path, str, len);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+int sysfs_write_int(char *path, int val)
+{
+ char buf[MAX_BUF_SIZE + 1];
+ int w;
+
+ if (!path)
+ return -EINVAL;
+
+ snprintf(buf, MAX_BUF_SIZE, "%d", val);
+ w = sysfs_write_buf(path, buf);
+ if (w < 0)
+ return w;
+
+ return 0;
+}
+
+int sysfs_write_str(char *path, char *str)
+{
+ int w;
+
+ if ((!path) || (!str))
+ return -EINVAL;
+
+ w = sysfs_write_buf(path, str);
+ if (w < 0)
+ return w;
+
+ return 0;
+}
diff --git a/src/sysfs.h b/src/sysfs.h
new file mode 100644
index 0000000..174804f
--- /dev/null
+++ b/src/sysfs.h
@@ -0,0 +1,6 @@
+#define MAX_BUF_SIZE 255
+
+int sysfs_read_int(char *path, int *val);
+int sysfs_read_str(char *path, char *str, int len);
+int sysfs_write_int(char *path, int val);
+int sysfs_write_str(char *path, char *str);