summaryrefslogtreecommitdiffstats
path: root/fanctl
diff options
context:
space:
mode:
authorYi Li <adamliyi@msn.com>2016-08-03 11:09:55 +0800
committerPatrick Williams <patrick@stwcx.xyz>2016-10-07 02:56:12 +0000
commit2f3213f9e0ede2f9a58bf2e912d5205300ce4bb2 (patch)
treebce9776bca9b09e1e744e09155e1a70fe09d32fd /fanctl
parent3205742d8f5371c12e376ec2b387dda81663213e (diff)
downloadtalos-skeleton-2f3213f9e0ede2f9a58bf2e912d5205300ce4bb2.tar.gz
talos-skeleton-2f3213f9e0ede2f9a58bf2e912d5205300ce4bb2.zip
Add fanctl to skeleton
fanctl replaces pyfanctl. It provides fan control service with dbus object "/org/openbmc/control/fans". Currently there are two methods: setMax() to set all fans' speed to maximum, and updatePresent() to update the "Present" status of inventory objects: "/org/openbmc/inventory/system/chassis/fan*". In future, more methods will be added, like setting fan speed based on temperature sensor reading. Fixes openbmc/openbmc#93 Change-Id: Ic65089f5d0538cd5e17cfcd9f27e652ae6080ec5 Signed-off-by: Yi Li <adamliyi@msn.com>
Diffstat (limited to 'fanctl')
-rw-r--r--fanctl/Makefile3
-rw-r--r--fanctl/fan_control.c504
2 files changed, 507 insertions, 0 deletions
diff --git a/fanctl/Makefile b/fanctl/Makefile
new file mode 100644
index 0000000..a02b1a6
--- /dev/null
+++ b/fanctl/Makefile
@@ -0,0 +1,3 @@
+BINS=fan_control
+include ../sdbus.mk
+include ../rules.mk
diff --git a/fanctl/fan_control.c b/fanctl/fan_control.c
new file mode 100644
index 0000000..54b96b1
--- /dev/null
+++ b/fanctl/fan_control.c
@@ -0,0 +1,504 @@
+/**
+ * Copyright © 2016 IBM Corporation
+ *
+ * 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 <stdlib.h>
+#include <limits.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <systemd/sd-bus.h>
+
+#define DBUS_MAX_NAME_LEN 256
+
+const char *objectmapper_service_name = "org.openbmc.ObjectMapper";
+const char *objectmapper_object_name = "/org/openbmc/ObjectMapper";
+const char *objectmapper_intf_name = "org.openbmc.ObjectMapper";
+
+typedef struct {
+ int fan_num;
+ int cpu_num;
+ int core_num;
+ int dimm_num;
+ sd_bus *bus;
+ char sensor_service[DBUS_MAX_NAME_LEN];
+ char inventory_service[DBUS_MAX_NAME_LEN];
+} fan_info_t;
+
+/* Get an object's bus name from ObjectMapper */
+int get_connection(sd_bus *bus, char *connection, const char *obj_path)
+{
+ sd_bus_error bus_error = SD_BUS_ERROR_NULL;
+ sd_bus_message *m = NULL;
+ char *temp_buf = NULL, *intf = NULL;
+ int rc;
+
+ rc = sd_bus_call_method(bus,
+ objectmapper_service_name,
+ objectmapper_object_name,
+ objectmapper_intf_name,
+ "GetObject",
+ &bus_error,
+ &m,
+ "s",
+ obj_path);
+ if (rc < 0) {
+ fprintf(stderr,
+ "Failed to GetObject: %s\n", bus_error.message);
+ goto finish;
+ }
+
+ /* Get the key, aka, the bus name */
+ sd_bus_message_read(m, "a{sas}", 1, &temp_buf, 1, &intf);
+ strncpy(connection, temp_buf, DBUS_MAX_NAME_LEN);
+
+finish:
+ sd_bus_error_free(&bus_error);
+ sd_bus_message_unref(m);
+ sd_bus_flush(bus);
+
+ return rc;
+}
+
+
+int set_dbus_sensor(sd_bus *bus, const char *obj_path, int val)
+{
+ char connection[DBUS_MAX_NAME_LEN];
+ sd_bus_error bus_error = SD_BUS_ERROR_NULL;
+ sd_bus_message *response = NULL;
+ int rc;
+
+ if (!bus || !obj_path)
+ return -1;
+
+ rc = get_connection(bus, connection, obj_path);
+ if (rc < 0) {
+ fprintf(stderr,
+ "fanctl: Failed to get bus name for %s\n", obj_path);
+ goto finish;
+ }
+
+ rc = sd_bus_call_method(bus,
+ connection,
+ obj_path,
+ "org.openbmc.SensorValue",
+ "setValue",
+ &bus_error,
+ &response,
+ "i",
+ val);
+ if (rc < 0)
+ fprintf(stderr,
+ "fanctl: Failed to set sensor %s:[%s]\n",
+ obj_path, strerror(-rc));
+
+finish:
+ sd_bus_error_free(&bus_error);
+ sd_bus_message_unref(response);
+ sd_bus_flush(bus);
+
+ return rc;
+}
+
+/* Read sensor value from "org.openbmc.Sensors" */
+int read_dbus_sensor(sd_bus *bus, const char *obj_path)
+{
+ char connection[DBUS_MAX_NAME_LEN];
+ sd_bus_error bus_error = SD_BUS_ERROR_NULL;
+ sd_bus_message *response = NULL;
+ int rc;
+ int val = 0;
+
+ if (!bus || !obj_path)
+ return 0;
+
+ rc = get_connection(bus, connection, obj_path);
+ if (rc < 0) {
+ val = 0;
+ fprintf(stderr,
+ "fanctl: Failed to get bus name for %s\n", obj_path);
+ goto finish;
+ }
+
+ rc = sd_bus_call_method(bus,
+ connection,
+ obj_path,
+ "org.openbmc.SensorValue",
+ "getValue",
+ &bus_error,
+ &response,
+ NULL);
+ if (rc < 0) {
+ val = 0;
+ fprintf(stderr,
+ "fanctl: Failed to read sensor value from %s:[%s]\n",
+ obj_path, strerror(-rc));
+ goto finish;
+ }
+
+ rc = sd_bus_message_read(response, "v","i", &val);
+ if (rc < 0) {
+ val = 0;
+ fprintf(stderr,
+ "fanctl: Failed to parse sensor value "
+ "response message from %s:[%s]\n",
+ obj_path, strerror(-rc));
+ }
+
+finish:
+ sd_bus_error_free(&bus_error);
+ sd_bus_message_unref(response);
+ sd_bus_flush(bus);
+
+ return val;
+}
+
+/* set fan speed with /org/openbmc/sensors/speed/fan* object */
+static int fan_set_speed(sd_bus *bus, int fan_id, uint8_t fan_speed)
+{
+ char obj_path[DBUS_MAX_NAME_LEN];
+ int rc;
+
+ if (!bus)
+ return -1;
+
+ snprintf(obj_path, sizeof(obj_path),
+ "/org/openbmc/sensors/speed/fan%d", fan_id);
+ rc = set_dbus_sensor(bus, obj_path, fan_speed);
+ if (rc < 0)
+ fprintf(stderr, "fanctl: Failed to set fan[%d] speed[%d]\n",
+ fan_id, fan_speed);
+
+ return rc;
+}
+
+static int fan_set_max_speed(fan_info_t *info)
+{
+ int i;
+ int rc = -1;
+
+ if (!info)
+ return -1;
+ for (i = 0; i < info->fan_num; i++) {
+ rc = fan_set_speed(info->bus, i, 255);
+ if (rc < 0)
+ break;
+ fprintf(stderr, "fanctl: Set fan%d to max speed\n", i);
+ }
+
+ return rc;
+}
+/*
+ * FAN_TACH_OFFSET is specific to Barreleye.
+ * Barreleye uses NTC7904D HW Monitor as Fan tachometoer.
+ * The 13-bit FANIN value is made up Higer part: [12:5],
+ * and Lower part: [4:0], which are read from two sensors.
+ * see: https://www.nuvoton.com/resource-files/NCT7904D_Datasheet_V1.44.pdf
+ */
+#define FAN_TACH_OFFSET 5
+static int fan_get_speed(sd_bus *bus, int fan_id)
+{
+ int fan_tach_H = 0, fan_tach_L = 0;
+ char obj_path[DBUS_MAX_NAME_LEN];
+ int fan_speed;
+
+ /* get fan tach */
+ /* The object path is specific to Barreleye */
+ snprintf(obj_path, sizeof(obj_path),
+ "/org/openbmc/sensors/tach/fan%dH", fan_id);
+ fan_tach_H = read_dbus_sensor(bus, obj_path);
+ snprintf(obj_path, sizeof(obj_path),
+ "/org/openbmc/sensors/tach/fan%dL", fan_id);
+ fan_tach_L = read_dbus_sensor(bus, obj_path);
+
+ /* invalid sensor value is -1 */
+ if (fan_tach_H <= 0 || fan_tach_L <= 0)
+ fan_speed = 0;
+ else
+ fan_speed = fan_tach_H << FAN_TACH_OFFSET | fan_tach_L;
+
+ fprintf(stderr, "fan%d speed: %d\n", fan_id, fan_speed);
+ return fan_speed;
+}
+
+/* set Fan Inventory 'Present' status */
+int fan_set_present(sd_bus *bus, int fan_id, int val)
+{
+ sd_bus_error bus_error = SD_BUS_ERROR_NULL;
+ sd_bus_message *response = NULL;
+ int rc;
+ char obj_path[DBUS_MAX_NAME_LEN];
+ char connection[DBUS_MAX_NAME_LEN];
+
+ snprintf(obj_path, sizeof(obj_path),
+ "/org/openbmc/inventory/system/chassis/fan%d", fan_id);
+
+ rc = get_connection(bus, connection, obj_path);
+ if (rc < 0) {
+ fprintf(stderr,
+ "fanctl: Failed to get bus name for %s\n", obj_path);
+ goto finish;
+ }
+
+ rc = sd_bus_call_method(bus,
+ connection,
+ obj_path,
+ "org.openbmc.InventoryItem",
+ "setPresent",
+ &bus_error,
+ &response,
+ "s",
+ (val == 1 ? "True" : "False"));
+ if(rc < 0)
+ fprintf(stderr,
+ "fanctl: Failed to update fan presence via dbus: %s\n",
+ bus_error.message);
+
+ fprintf(stderr, "fanctl: Set fan%d present status to: %s\n",
+ fan_id, (val == 1 ? "True" : "False"));
+
+finish:
+ sd_bus_error_free(&bus_error);
+ sd_bus_message_unref(response);
+ sd_bus_flush(bus);
+
+ return rc;
+}
+
+/*
+ * Update Fan Invertory 'Present' status by first reading fan speed.
+ * If fan speed is '0', the fan is considerred not 'Present'.
+ */
+static int fan_update_present(fan_info_t *info)
+{
+ int i;
+ int rc = -1;
+ int fan_speed;
+
+ if (!info)
+ return -1;
+
+ for (i = 0; i < info->fan_num; i++) {
+ fan_speed = fan_get_speed(info->bus, i);
+ if (fan_speed > 0)
+ rc = fan_set_present(info->bus, i, 1);
+ else
+ rc = fan_set_present(info->bus, i, 0);
+
+ if (rc < 0) {
+ fprintf(stderr,
+ "fanctl: Failed to set fan present status\n");
+ break;
+ }
+ }
+
+ return rc;
+}
+/*
+ * Router function for any FAN operations that come via dbus
+ */
+static int fan_function_router(sd_bus_message *msg, void *user_data,
+ sd_bus_error *ret_error)
+{
+ /* Generic error reporter. */
+ int rc = -1;
+ fan_info_t *info = user_data;
+
+ /* Get the Operation. */
+ const char *fan_function = sd_bus_message_get_member(msg);
+ if (fan_function == NULL) {
+ fprintf(stderr, "fanctl: Null FAN function specificed\n");
+ return sd_bus_reply_method_return(msg, "i", rc);
+ }
+
+ /* Route the user action to appropriate handlers. */
+ if ((strcmp(fan_function, "setMax") == 0)) {
+ rc = fan_set_max_speed(info);
+ return sd_bus_reply_method_return(msg, "i", rc);
+ }
+ if ((strcmp(fan_function, "updatePresent") == 0)) {
+ rc = fan_update_present(info);
+ return sd_bus_reply_method_return(msg, "i", rc);
+ }
+
+ return sd_bus_reply_method_return(msg, "i", rc);
+}
+
+/* Dbus Services offered by this FAN controller */
+static const sd_bus_vtable fan_control_vtable[] =
+{
+ SD_BUS_VTABLE_START(0),
+ SD_BUS_METHOD("setMax", "", "i", &fan_function_router,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_METHOD("updatePresent", "", "i", &fan_function_router,
+ SD_BUS_VTABLE_UNPRIVILEGED),
+ SD_BUS_VTABLE_END,
+};
+
+int start_fan_services(fan_info_t *info)
+{
+ /* Generic error reporter. */
+ int rc = -1;
+ /* slot where we are offering the FAN dbus service. */
+ sd_bus_slot *fan_slot = NULL;
+ const char *fan_object = "/org/openbmc/control/fans";
+
+ info->bus = NULL;
+ /* Get a hook onto system bus. */
+ rc = sd_bus_open_system(&info->bus);
+ if (rc < 0) {
+ fprintf(stderr,"fanctl: Error opening system bus.\n");
+ return rc;
+ }
+
+ /* Install the object */
+ rc = sd_bus_add_object_vtable(info->bus,
+ &fan_slot,
+ fan_object, /* object path */
+ "org.openbmc.control.Fans", /* interface name */
+ fan_control_vtable,
+ info);
+ if (rc < 0) {
+ fprintf(stderr, "fanctl: Failed to add object to dbus: %s\n",
+ strerror(-rc));
+ return rc;
+ }
+
+ /* If we had success in adding the providers, request for a bus name. */
+ rc = sd_bus_request_name(info->bus,
+ "org.openbmc.control.Fans", 0);
+ if (rc < 0) {
+ fprintf(stderr, "fanctl: Failed to acquire service name: %s\n",
+ strerror(-rc));
+ return rc;
+ }
+
+ for (;;) {
+ /* Process requests */
+ rc = sd_bus_process(info->bus, NULL);
+ if (rc < 0) {
+ fprintf(stderr, "fanctl: Failed to process bus: %s\n",
+ strerror(-rc));
+ break;
+ }
+ if (rc > 0) {
+ continue;
+ }
+
+ rc = sd_bus_wait(info->bus, (uint64_t) - 1);
+ if (rc < 0) {
+ fprintf(stderr, "fanctl: Failed to wait on bus: %s\n",
+ strerror(-rc));
+ break;
+ }
+ }
+
+ sd_bus_slot_unref(fan_slot);
+ sd_bus_unref(info->bus);
+
+ return rc;
+}
+
+static int str_to_int(char *str)
+{
+ long val;
+ char *temp;
+
+ val = strtol(str, &temp, 10);
+ if (temp == str || *temp != '\0' ||
+ ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE))
+ return -1;
+ if (val < 0)
+ return -1;
+
+ return (int)val;
+}
+
+static int parse_argument(int argc, char **argv, fan_info_t *info)
+{
+ int c;
+ struct option long_options[] =
+ {
+ {"fan_num", required_argument, 0, 'f'},
+ {"core_num", required_argument, 0, 'c'},
+ {"cpu_num", required_argument, 0, 'p'},
+ {"dimm_num", required_argument, 0, 'd'},
+ {0, 0, 0, 0}
+ };
+
+ while (1) {
+ c = getopt_long (argc, argv, "c:d:f:p:", long_options, NULL);
+
+ /* Detect the end of the options. */
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'f':
+ info->fan_num = str_to_int(optarg);
+ if (info->fan_num == -1) {
+ fprintf(stderr, "fanctl: Wrong fan_num: %s\n", optarg);
+ return -1;
+ }
+ break;
+ case 'c':
+ info->core_num = str_to_int(optarg);
+ if (info->core_num == -1) {
+ fprintf(stderr, "fanctl: Wrong core_num: %s\n", optarg);
+ return -1;
+ }
+ break;
+ case 'p':
+ info->cpu_num = str_to_int(optarg);
+ if (info->cpu_num == -1) {
+ fprintf(stderr, "fanctl: Wrong cpu_num: %s\n", optarg);
+ return -1;
+ }
+ break;
+ case 'd':
+ info->dimm_num = str_to_int(optarg);
+ if (info->dimm_num == -1) {
+ fprintf(stderr, "fanctl: Wrong dimm_num: %s\n", optarg);
+ return -1;
+ }
+ break;
+ default:
+ fprintf(stderr, "fanctl: Wrong argument\n");
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ int rc = 0;
+ fan_info_t fan_info;
+
+ memset(&fan_info, 0, sizeof(fan_info));
+ rc = parse_argument(argc, argv, &fan_info);
+ if (rc < 0) {
+ fprintf(stderr, "fanctl: Error parse argument\n");
+ return rc;
+ }
+ /* This call is not supposed to return. If it does, then an error */
+ rc = start_fan_services(&fan_info);
+ if (rc < 0) {
+ fprintf(stderr, "fanctl: Error starting FAN Services. Exiting");
+ }
+
+ return rc;
+}
OpenPOWER on IntegriCloud