diff options
author | Yi Li <adamliyi@msn.com> | 2016-08-03 11:09:55 +0800 |
---|---|---|
committer | Patrick Williams <patrick@stwcx.xyz> | 2016-10-07 02:56:12 +0000 |
commit | 2f3213f9e0ede2f9a58bf2e912d5205300ce4bb2 (patch) | |
tree | bce9776bca9b09e1e744e09155e1a70fe09d32fd /fanctl | |
parent | 3205742d8f5371c12e376ec2b387dda81663213e (diff) | |
download | talos-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/Makefile | 3 | ||||
-rw-r--r-- | fanctl/fan_control.c | 504 |
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; +} |