/** * 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 #include #include #include #include #include #include #define DBUS_MAX_NAME_LEN 256 const char *objectmapper_service_name = "xyz.openbmc_project.ObjectMapper"; const char *objectmapper_object_name = "/xyz/openbmc_project/object_mapper"; const char *objectmapper_intf_name = "xyz.openbmc_project.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, "ss", 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_set_property(bus, connection, obj_path, "xyz.openbmc_project.Control.FanSpeed", "Target", &bus_error, "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 "xyz.openbmc_projects.Sensors" */ int read_dbus_sensor(sd_bus *bus, const char *obj_path, const char *property) { 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_get_property(bus, connection, obj_path, "xyz.openbmc_project.Sensor.Value", property, &bus_error, &response, "i"); 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, "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 /xyz/openbmc_project/sensors/fan_tach/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), "/xyz/openbmc_project/sensors/fan_tach/fan%d_0", 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 Higher 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 */ snprintf(obj_path, sizeof(obj_path), "/xyz/openbmc_project/sensors/fan_tach/fan%d_0", fan_id); fan_tach_H = read_dbus_sensor(bus, obj_path, "MaxValue"); fan_tach_L = read_dbus_sensor(bus, obj_path, "MinValue"); /* 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), "/xyz/openbmc_project/inventory/system/chassis/motherboard/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_set_property(bus, connection, obj_path, "xyz.openbmc_project.Inventory.Item", "Present", &bus_error, "b", val); 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 specified\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; }