--- /dev/null
+/*
+ * 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 <stdio.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <assert.h>
+#include <errno.h>
+#include <string.h>
+
+#include <pass/log.h>
+#include <pass/config-parser.h>
+
+#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;
+}
--- /dev/null
+/*
+ * 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 <glib.h>
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+
+#include <pass/common.h>
+#include <pass/devices.h>
+#include <pass/device-notifier.h>
+#include <pass/gdbus-util.h>
+#include <pass/log.h>
+
+#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)