From 49f1f60714bb1efb691f7677eefb404de14173b7 Mon Sep 17 00:00:00 2001 From: Chanwoo Choi Date: Thu, 22 Mar 2018 18:34:13 +0900 Subject: [PATCH] thermal: Add new Thermal Monitor module PASS can monitor the various h/w resource such as CPU/GPU and so on. The thermal information is one of them. Require the thermal monitoring and then notify the meaningful data of thermal. So, Thermal Monitor is required. Thermal Monitor supports the configuration file in order to define the thermal scenarios (e.g., ReleaseAction, ShutdownAction) according to the user definition. It is very flexible to define the multiple thermal scenarios. Thermal Monitor monitors the thermal information using following two methods: - uevent-based thermal monitoring (interrupt method) - timer-baesd thermal monitoring (polling method) [d-bus interface] - d-bus interface: "org.tizen.system.pass.monitor.thermal" - d-bus method : "thermal_scenario" - configuration : /etc/pass/pass-thermal.conf Change-Id: I63c138d33350f6e17f9ad9bb73bd837b4f2a278e Signed-off-by: Chanwoo Choi --- CMakeLists.txt | 11 ++ include/pass/gdbus-util.h | 4 + scripts/thermal-dbus.xml | 11 ++ src/core/gdbus-util.c | 10 ++ src/thermal/thermal-parser.c | 163 ++++++++++++++++++++++++ src/thermal/thermal.c | 286 +++++++++++++++++++++++++++++++++++++++++++ src/thermal/thermal.h | 40 ++++++ 7 files changed, 525 insertions(+) create mode 100644 scripts/thermal-dbus.xml create mode 100644 src/thermal/thermal-parser.c create mode 100644 src/thermal/thermal.c create mode 100644 src/thermal/thermal.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 26cab5a..b57486a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -74,6 +74,8 @@ SET(SRCS src/pass/pass-pmqos.c src/pmqos/pmqos.c src/pmqos/pmqos-parser.c + src/thermal/thermal.c + src/thermal/thermal-parser.c src/hal/hal.c src/core/common.c src/core/config-parser.c @@ -85,6 +87,8 @@ SET(SRCS src/pass/pass-dbus-stub.c #Generated by a custom command 'gdbus-codegen' below src/pmqos/pmqos-dbus-stub.c + #Generated by a custom command 'gdbus-codegen' below + src/thermal/thermal-dbus-stub.c ) INCLUDE_DIRECTORIES(${CMAKE_CURRENT_SOURCE_DIR}) @@ -150,6 +154,13 @@ ADD_CUSTOM_COMMAND( --output-directory src/pmqos/ scripts/pmqos-dbus.xml COMMENT "Generating GDBus stub for pmqos interface") +ADD_CUSTOM_COMMAND( + OUTPUT src/thermal/thermal-dbus-stub.c + COMMAND gdbus-codegen --interface-prefix org.tizen. + --generate-c-code thermal-dbus-stub + --output-directory src/thermal/ + scripts/thermal-dbus.xml + COMMENT "Generating GDBus stub for thermal interface") ADD_EXECUTABLE(${PROJECT_NAME} ${SRCS}) TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${pkgs_LDFLAGS} "-ldl" "-lm") diff --git a/include/pass/gdbus-util.h b/include/pass/gdbus-util.h index f6be0b9..eaa09ea 100644 --- a/include/pass/gdbus-util.h +++ b/include/pass/gdbus-util.h @@ -25,6 +25,7 @@ #include "pass/pass-dbus-stub.h" #include "pmqos/pmqos-dbus-stub.h" +#include "thermal/thermal-dbus-stub.h" #define SYSTEMD_DBUS_NAME "org.freedesktop.systemd1" #define SYSTEMD_DBUS_OBJECT_PATH "/org/freedesktop/systemd1" @@ -59,6 +60,9 @@ SystemPassCore *pass_gdbus_get_instance_core(void); void pass_gdbus_put_instance_core(SystemPassCore **instance); SystemPassPmqos *pass_gdbus_get_instance_pmqos(void); void pass_gdbus_put_instance_pmqos(SystemPassPmqos **instance); +SystemPassMonitorThermal *pass_gdbus_get_instance_thermal(void); +void pass_gdbus_put_instance_thermal(SystemPassMonitorThermal **instance); + int pass_gdbus_get_system_connection(void); void pass_gdbus_put_system_connection(void); #endif /* __GDBUS_UTIL_H__ */ diff --git a/scripts/thermal-dbus.xml b/scripts/thermal-dbus.xml new file mode 100644 index 0000000..0b4b6f1 --- /dev/null +++ b/scripts/thermal-dbus.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/core/gdbus-util.c b/src/core/gdbus-util.c index 6a672db..d18137c 100644 --- a/src/core/gdbus-util.c +++ b/src/core/gdbus-util.c @@ -253,6 +253,16 @@ void pass_gdbus_put_instance_pmqos(SystemPassPmqos **instance) put_instance((gpointer *)instance); } +SystemPassMonitorThermal *pass_gdbus_get_instance_thermal(void) +{ + return system_pass_monitor_thermal_skeleton_new(); +} + +void pass_gdbus_put_instance_thermal(SystemPassMonitorThermal **instance) +{ + put_instance((gpointer *)instance); +} + int pass_gdbus_get_system_connection(void) { GError *error = NULL; diff --git a/src/thermal/thermal-parser.c b/src/thermal/thermal-parser.c new file mode 100644 index 0000000..3dab8b5 --- /dev/null +++ b/src/thermal/thermal-parser.c @@ -0,0 +1,163 @@ +/* + * PASS (Power Aware System Service) - Parser for Thermal Monitor + * + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * based on src/pmqos/pmqos-parser.c + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include "thermal.h" + +#define MAX_NUM_OF_SCENARIOS 255 + +static bool is_supported(const char *value) +{ + assert(value); + + if (MATCH(value, "yes")) + return true; + return false; +} + +static int thermal_parse_scenario(struct parse_result *result, void *user_data, + unsigned int index) +{ + struct thermal_scenario *scenarios = (struct thermal_scenario *)user_data; + + assert(result); + assert(result->section && result->name && result->value); + + /* Parse 'thermal' section */ + if (MATCH(result->section, "thermal")) { + if (MATCH(result->name, "thermal_support")) + scenarios->support = is_supported(result->value); + else if (MATCH(result->name, "thermal_number_of_scenario")) { + int num = atoi(result->value); + + if (num > MAX_NUM_OF_SCENARIOS) + return -EINVAL; + + if (num > 0) { + scenarios->list = calloc(num, + sizeof(struct scenario)); + if (!scenarios->list) { + _E("failed to allocate scenario memory"); + return -errno; + } + + scenarios->num = num; + } + } + } + + if (!scenarios->support || !scenarios->num) + return 0; + + if (index > scenarios->num) + return 0; + + /* Parse 'Scenario' section */ + if (MATCH(result->name, "name")) + snprintf(scenarios->list[index].name, + strlen(result->value) + 1, "%s", result->value); + else if (MATCH(result->name, "support")) + scenarios->list[index].support = is_supported(result->value); + + return 0; +} + +static int thermal_load_config(struct parse_result *result, void *user_data) +{ + struct thermal_scenario *scenarios + = (struct thermal_scenario *)user_data; + char name[NAME_MAX]; + int ret; + static int index; + + if (!result) + return 0; + + if (!result->section || !result->name || !result->value) + return 0; + + /* Parsing 'thermal' section */ + if (MATCH(result->section, "thermal")) { + ret = thermal_parse_scenario(result, user_data, -1); + if (ret < 0) { + _E("failed to parse [thermal] section : %d", ret); + return ret; + } + goto out; + } + + /* Parsing 'thermal.scenario' section */ + for (index = 0; index < scenarios->num; ++index) { + snprintf(name, sizeof(name), "thermal.scenario%d", index); + + if (MATCH(result->section, name)) { + ret = thermal_parse_scenario(result, user_data, index); + if (ret < 0) { + _E("failed to parse [thermal.scenario%d] section : %d", + index, ret); + return ret; + } + goto out; + } + } + +out: + return 0; +} + +int thermal_put_scenario(struct thermal_scenario *scenarios) +{ + if (!scenarios) + return -EINVAL; + + if (scenarios->num > 0 && scenarios->list) { + scenarios->num = 0; + free(scenarios->list); + scenarios->list = NULL; + } + + return 0; +} + +int thermal_get_scenario(const char *path, struct thermal_scenario *scenarios) +{ + int ret; + + /* Initialize the variables before parsing the pass-thermal.conf */ + scenarios->num = 0; + + /* get configuration file */ + ret = config_parse(path, thermal_load_config, scenarios); + if (ret < 0) { + thermal_put_scenario(scenarios); + return ret; + } + + return 0; +} diff --git a/src/thermal/thermal.c b/src/thermal/thermal.c new file mode 100644 index 0000000..274635f --- /dev/null +++ b/src/thermal/thermal.c @@ -0,0 +1,286 @@ +/* + * PASS (Power Aware System Service) - Thermal Monitor + * + * Copyright (c) 2018 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 +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "thermal.h" + +#define THERMAL_CONF_PATH "/etc/pass/pass-thermal.conf" + +#define DBUS_THERMAL_I_START_HANDLER "handle_start" +#define DBUS_THERMAL_I_STOP_HANDLER "handle_stop" +#define DBUS_THERMAL_INTERFACE "org.tizen.system.pass.monitor.thermal" +#define DBUS_THERMAL_PATH "/Org/Tizen/System/Pass/Monitor/Thermal" +#define DBUS_THERMAL_METHOD "thermal_scenario" + +static SystemPassMonitorThermal *g_gdbus_instance = NULL; +static struct thermal_scenario *g_thermal = NULL; + +static void thermal_free(void) +{ + int ret; + + ret = thermal_put_scenario(g_thermal); + if (ret < 0) + _E("failed to pet Thermal Monitor scenario\n"); + + free(g_thermal); + g_thermal = NULL; +} + +static int thermal_init_done(void *data, void *user_data) +{ + int *booting_done; + int ret; + int i; + + /* + * As a callback function for the booting-done event, it is notified + * with the result of the booting_finished() function by using the + * first argument, void *data. When it is successfully booted up, an int + * value, 1 is passed with the first argument. + */ + booting_done = (int *)data; + if (!(*booting_done)) + return -EINVAL; + + if (g_thermal) + return 0; + + g_thermal = calloc(1, sizeof(struct thermal_scenario)); + if (!g_thermal) { + _E("failed to allocate the memory for thermal monitor\n"); + return -ENOMEM; + } + + ret = thermal_get_scenario(THERMAL_CONF_PATH, g_thermal); + if (ret < 0) { + _E("failed to get Thermal Monitor scenario\n"); + return ret; + } + + if (!g_thermal->support || g_thermal->num <= 0) { + ret = thermal_put_scenario(g_thermal); + if (ret < 0) + _E("failed to pet Thermal Monitor scenario\n"); + return ret; + } + + for (i = 0; i < g_thermal->num; i++) + if (g_thermal->list[i].support) + _I("Support \'%s\' scenario", g_thermal->list[i].name); + + return 0; +} + +static gboolean dbus_cb_thermal_start(SystemPassMonitorThermal *obj, + GDBusMethodInvocation *invoc, gpointer user_data) +{ + int ret = 0; + int booting_done = 1; + + if (g_thermal) + _I("Thermal Monitor is already started\n"); + else + ret = thermal_init_done(&booting_done, NULL); + + g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", ret)); + + if (!ret) + return TRUE; + + _E("failed to initialize Thermal Monitor of the daemon " + "in dbus callback for a start message\n"); + return FALSE; +} + +static gboolean dbus_cb_thermal_stop(SystemPassMonitorThermal *obj, + GDBusMethodInvocation *invoc, gpointer user_data) +{ + if (!g_thermal) + _I("Thermal Monitor is already stopped\n"); + else + thermal_free(); + + g_dbus_method_invocation_return_value(invoc, g_variant_new("(i)", 0)); + + return TRUE; +} + + +static struct pass_gdbus_signal_info g_gdbus_signal_infos[] = { + { + .handler = DBUS_THERMAL_I_START_HANDLER, + .cb = G_CALLBACK(dbus_cb_thermal_start), + .cb_data = NULL, + .ret_id = 0, + }, { + .handler = DBUS_THERMAL_I_STOP_HANDLER, + .cb = G_CALLBACK(dbus_cb_thermal_stop), + .cb_data = NULL, + .ret_id = 0, + }, +}; + +/* + * data: thermal action string + * user_data: instance of struct pass_resource + */ +static int thermal_notifier_cb(void *data, void *user_data) +{ + GVariant *gvar; + int ret; + int i; + + if (!g_thermal || !g_thermal->support || g_thermal->num <= 0) { + _I("Thermal Monitor is stopped\n"); + return 0; + } + + if (!data) + return -EINVAL; + + /* Find the available thermal scenario */ + for (i = 0; i < g_thermal->num; i++) { + if (!g_thermal->list[i].support) + continue; + if (!strncmp(g_thermal->list[i].name, data, strlen(data))) + break; + } + + /* If there is no available thermal scenario, just return */ + if (i >= g_thermal->num) { + _I("Not supported \'%s\' scenario", data); + return 0; + } + + /* If there is available thermal scenario, send the broadcast signal */ + gvar = g_variant_new("(s)", data); + ret = pass_gdbus_send_broadcast_signal(DBUS_THERMAL_PATH, + DBUS_THERMAL_INTERFACE, + DBUS_THERMAL_METHOD, + gvar); + if (ret < 0) { + _E("failed to send broadcast signal of thermal monitor(%d)\n", + ret); + } + + return 0; +} + +static void thermal_init(void *data) +{ + /* This is the case of daemon creation: DO NOTHING */ + return; +} + +static void thermal_exit(void *data) +{ + unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, + thermal_init_done, NULL); + + pass_gdbus_disconnect_signal(g_gdbus_instance, + ARRAY_SIZE(g_gdbus_signal_infos), g_gdbus_signal_infos); + pass_gdbus_put_instance_thermal(&g_gdbus_instance); + + thermal_free(); +} + +static int thermal_probe(void *data) +{ + int ret = 0; + + /* Initialize the d-bus interface for Thermal Monitor */ + g_gdbus_instance = pass_gdbus_get_instance_thermal(); + if (!g_gdbus_instance) { + _E("failed to get a dbus instance for the %s interface\n", + DBUS_THERMAL_INTERFACE); + return -ENOSYS; + } + + ret = pass_gdbus_connect_signal(g_gdbus_instance, + ARRAY_SIZE(g_gdbus_signal_infos), g_gdbus_signal_infos); + if (ret < 0) { + _E("failed to register callbacks as the dbus method invocation " + "handlers\n"); + ret = -ENOSYS; + goto out; + } + + ret = pass_gdbus_export_interface(g_gdbus_instance, DBUS_THERMAL_PATH); + if (ret < 0) { + _E("failed to export the dbus interface '%s' " + "at the object path '%s'\n", + DBUS_THERMAL_INTERFACE, + DBUS_THERMAL_PATH); + ret = -ENOSYS; + goto out_disconnect; + } + + /* + * Register a notifier for the booting-done event. The actual + * initialization of the daemon is performed by this notifier after + * booting is completely done. + */ + ret = register_notifier(DEVICE_NOTIFIER_BOOTING_DONE, + thermal_init_done, NULL); + if (ret < 0) { + _E("failed to register a callback function \ + for the booting-done event (%d)\n", ret); + goto out_disconnect; + } + + /* Register DEVICE_NOTIFIER_THERMAL */ + ret = register_notifier(DEVICE_NOTIFIER_THERMAL, thermal_notifier_cb, + NULL); + if (ret < 0) { + _E("failed to register notifier for thermal monitor (%d)\n", + ret); + goto out_booting_done; + } + + return 0; + +out_booting_done: + unregister_notifier(DEVICE_NOTIFIER_BOOTING_DONE, + thermal_init_done, NULL); +out_disconnect: + pass_gdbus_disconnect_signal(g_gdbus_instance, + ARRAY_SIZE(g_gdbus_signal_infos), g_gdbus_signal_infos); +out: + pass_gdbus_put_instance_thermal(&g_gdbus_instance); + + return ret; +} + +static const struct device_ops thermal_device_ops = { + .name = "thermal", + .probe = thermal_probe, + .init = thermal_init, + .exit = thermal_exit, +}; +DEVICE_OPS_REGISTER(&thermal_device_ops) diff --git a/src/thermal/thermal.h b/src/thermal/thermal.h new file mode 100644 index 0000000..9da0cdc --- /dev/null +++ b/src/thermal/thermal.h @@ -0,0 +1,40 @@ +/* + * PASS (Power Aware System Service) - Thermal Monitor + * + * Copyright (c) 2018 Samsung Electronics Co., Ltd. + * + * based on src/thermal/thermal-parser.c + * + * 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. + */ + +#ifndef __THERMAL_H__ +#define __THERMAL_H__ + +#include +#include + +struct thermal_scenario { + struct scenario { + char name[NAME_MAX]; + bool support; + } *list; + + int num; + bool support; +}; + +int thermal_get_scenario(const char *path, struct thermal_scenario *scenarios); +int thermal_put_scenario(struct thermal_scenario *scenarios); + +#endif /* __THERMAL_H__ */ -- 2.7.4