1 /** 2 * Copyright © 2016 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #include <stdio.h> 17 #include <stdlib.h> 18 #include <limits.h> 19 #include <errno.h> 20 #include <string.h> 21 #include <getopt.h> 22 #include <systemd/sd-bus.h> 23 24 #define DBUS_MAX_NAME_LEN 256 25 26 const char *objectmapper_service_name = "xyz.openbmc_project.ObjectMapper"; 27 const char *objectmapper_object_name = "/xyz/openbmc_project/object_mapper"; 28 const char *objectmapper_intf_name = "xyz.openbmc_project.ObjectMapper"; 29 30 typedef struct { 31 int fan_num; 32 int cpu_num; 33 int core_num; 34 int dimm_num; 35 sd_bus *bus; 36 char sensor_service[DBUS_MAX_NAME_LEN]; 37 char inventory_service[DBUS_MAX_NAME_LEN]; 38 } fan_info_t; 39 40 /* Get an object's bus name from ObjectMapper */ 41 int get_connection(sd_bus *bus, char *connection, const char *obj_path) 42 { 43 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 44 sd_bus_message *m = NULL; 45 char *temp_buf = NULL, *intf = NULL; 46 int rc; 47 48 rc = sd_bus_call_method(bus, 49 objectmapper_service_name, 50 objectmapper_object_name, 51 objectmapper_intf_name, 52 "GetObject", 53 &bus_error, 54 &m, 55 "ss", 56 obj_path, 57 ""); 58 if (rc < 0) { 59 fprintf(stderr, 60 "Failed to GetObject: %s\n", bus_error.message); 61 goto finish; 62 } 63 64 /* Get the key, aka, the bus name */ 65 sd_bus_message_read(m, "a{sas}", 1, &temp_buf, 1, &intf); 66 strncpy(connection, temp_buf, DBUS_MAX_NAME_LEN); 67 68 finish: 69 sd_bus_error_free(&bus_error); 70 sd_bus_message_unref(m); 71 sd_bus_flush(bus); 72 73 return rc; 74 } 75 76 77 int set_dbus_sensor(sd_bus *bus, const char *obj_path, int val) 78 { 79 char connection[DBUS_MAX_NAME_LEN]; 80 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 81 sd_bus_message *response = NULL; 82 int rc; 83 84 if (!bus || !obj_path) 85 return -1; 86 87 rc = get_connection(bus, connection, obj_path); 88 if (rc < 0) { 89 fprintf(stderr, 90 "fanctl: Failed to get bus name for %s\n", obj_path); 91 goto finish; 92 } 93 94 rc = sd_bus_set_property(bus, 95 connection, 96 obj_path, 97 "xyz.openbmc_project.Control.FanSpeed", 98 "Target", 99 &bus_error, 100 "i", 101 val); 102 if (rc < 0) 103 fprintf(stderr, 104 "fanctl: Failed to set sensor %s:[%s]\n", 105 obj_path, strerror(-rc)); 106 107 finish: 108 sd_bus_error_free(&bus_error); 109 sd_bus_message_unref(response); 110 sd_bus_flush(bus); 111 112 return rc; 113 } 114 115 /* Read sensor value from "xyz.openbmc_projects.Sensors" */ 116 int read_dbus_sensor(sd_bus *bus, const char *obj_path, const char *property) 117 { 118 char connection[DBUS_MAX_NAME_LEN]; 119 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 120 sd_bus_message *response = NULL; 121 int rc; 122 int val = 0; 123 124 if (!bus || !obj_path) 125 return 0; 126 127 rc = get_connection(bus, connection, obj_path); 128 if (rc < 0) { 129 val = 0; 130 fprintf(stderr, 131 "fanctl: Failed to get bus name for %s\n", obj_path); 132 goto finish; 133 } 134 135 rc = sd_bus_get_property(bus, 136 connection, 137 obj_path, 138 "xyz.openbmc_project.Sensor.Value", 139 property, 140 &bus_error, 141 &response, 142 "i"); 143 if (rc < 0) { 144 val = 0; 145 fprintf(stderr, 146 "fanctl: Failed to read sensor value from %s:[%s]\n", 147 obj_path, strerror(-rc)); 148 goto finish; 149 } 150 151 rc = sd_bus_message_read(response, "i", &val); 152 if (rc < 0) { 153 val = 0; 154 fprintf(stderr, 155 "fanctl: Failed to parse sensor value " 156 "response message from %s:[%s]\n", 157 obj_path, strerror(-rc)); 158 } 159 160 finish: 161 sd_bus_error_free(&bus_error); 162 sd_bus_message_unref(response); 163 sd_bus_flush(bus); 164 165 return val; 166 } 167 168 /* set fan speed with /xyz/openbmc_project/sensors/fan_tach/fan* object */ 169 static int fan_set_speed(sd_bus *bus, int fan_id, uint8_t fan_speed) 170 { 171 char obj_path[DBUS_MAX_NAME_LEN]; 172 int rc; 173 174 if (!bus) 175 return -1; 176 177 snprintf(obj_path, sizeof(obj_path), 178 "/xyz/openbmc_project/sensors/fan_tach/fan%d_0", fan_id); 179 rc = set_dbus_sensor(bus, obj_path, fan_speed); 180 if (rc < 0) 181 fprintf(stderr, "fanctl: Failed to set fan[%d] speed[%d]\n", 182 fan_id, fan_speed); 183 184 return rc; 185 } 186 187 static int fan_set_max_speed(fan_info_t *info) 188 { 189 int i; 190 int rc = -1; 191 192 if (!info) 193 return -1; 194 for (i = 0; i < info->fan_num; i++) { 195 rc = fan_set_speed(info->bus, i, 255); 196 if (rc < 0) 197 break; 198 fprintf(stderr, "fanctl: Set fan%d to max speed\n", i); 199 } 200 201 return rc; 202 } 203 /* 204 * FAN_TACH_OFFSET is specific to Barreleye. 205 * Barreleye uses NTC7904D HW Monitor as Fan tachometoer. 206 * The 13-bit FANIN value is made up Higher part: [12:5], 207 * and Lower part: [4:0], which are read from two sensors. 208 * see: https://www.nuvoton.com/resource-files/NCT7904D_Datasheet_V1.44.pdf 209 */ 210 #define FAN_TACH_OFFSET 5 211 static int fan_get_speed(sd_bus *bus, int fan_id) 212 { 213 int fan_tach_H = 0, fan_tach_L = 0; 214 char obj_path[DBUS_MAX_NAME_LEN]; 215 int fan_speed; 216 217 /* get fan tach */ 218 snprintf(obj_path, sizeof(obj_path), 219 "/xyz/openbmc_project/sensors/fan_tach/fan%d_0", fan_id); 220 fan_tach_H = read_dbus_sensor(bus, obj_path, "MaxValue"); 221 fan_tach_L = read_dbus_sensor(bus, obj_path, "MinValue"); 222 223 /* invalid sensor value is -1 */ 224 if (fan_tach_H <= 0 || fan_tach_L <= 0) 225 fan_speed = 0; 226 else 227 fan_speed = fan_tach_H << FAN_TACH_OFFSET | fan_tach_L; 228 229 fprintf(stderr, "fan%d speed: %d\n", fan_id, fan_speed); 230 return fan_speed; 231 } 232 233 /* set Fan Inventory 'Present' status */ 234 int fan_set_present(sd_bus *bus, int fan_id, int val) 235 { 236 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 237 sd_bus_message *response = NULL; 238 int rc; 239 char obj_path[DBUS_MAX_NAME_LEN]; 240 char connection[DBUS_MAX_NAME_LEN]; 241 242 snprintf(obj_path, sizeof(obj_path), 243 "/xyz/openbmc_project/inventory/system/chassis/motherboard/fan%d", 244 fan_id); 245 246 rc = get_connection(bus, connection, obj_path); 247 if (rc < 0) { 248 fprintf(stderr, 249 "fanctl: Failed to get bus name for %s\n", obj_path); 250 goto finish; 251 } 252 253 rc = sd_bus_set_property(bus, 254 connection, 255 obj_path, 256 "xyz.openbmc_project.Inventory.Item", 257 "Present", 258 &bus_error, 259 "b", 260 val); 261 if(rc < 0) 262 fprintf(stderr, 263 "fanctl: Failed to update fan presence via dbus: %s\n", 264 bus_error.message); 265 266 fprintf(stderr, "fanctl: Set fan%d present status to: %s\n", 267 fan_id, (val == 1 ? "True" : "False")); 268 269 finish: 270 sd_bus_error_free(&bus_error); 271 sd_bus_message_unref(response); 272 sd_bus_flush(bus); 273 274 return rc; 275 } 276 277 /* 278 * Update Fan Invertory 'Present' status by first reading fan speed. 279 * If fan speed is '0', the fan is considerred not 'Present'. 280 */ 281 static int fan_update_present(fan_info_t *info) 282 { 283 int i; 284 int rc = -1; 285 int fan_speed; 286 287 if (!info) 288 return -1; 289 290 for (i = 0; i < info->fan_num; i++) { 291 fan_speed = fan_get_speed(info->bus, i); 292 if (fan_speed > 0) 293 rc = fan_set_present(info->bus, i, 1); 294 else 295 rc = fan_set_present(info->bus, i, 0); 296 297 if (rc < 0) { 298 fprintf(stderr, 299 "fanctl: Failed to set fan present status\n"); 300 break; 301 } 302 } 303 304 return rc; 305 } 306 /* 307 * Router function for any FAN operations that come via dbus 308 */ 309 static int fan_function_router(sd_bus_message *msg, void *user_data, 310 sd_bus_error *ret_error) 311 { 312 /* Generic error reporter. */ 313 int rc = -1; 314 fan_info_t *info = user_data; 315 316 /* Get the Operation. */ 317 const char *fan_function = sd_bus_message_get_member(msg); 318 if (fan_function == NULL) { 319 fprintf(stderr, "fanctl: Null FAN function specified\n"); 320 return sd_bus_reply_method_return(msg, "i", rc); 321 } 322 323 /* Route the user action to appropriate handlers. */ 324 if ((strcmp(fan_function, "setMax") == 0)) { 325 rc = fan_set_max_speed(info); 326 return sd_bus_reply_method_return(msg, "i", rc); 327 } 328 if ((strcmp(fan_function, "updatePresent") == 0)) { 329 rc = fan_update_present(info); 330 return sd_bus_reply_method_return(msg, "i", rc); 331 } 332 333 return sd_bus_reply_method_return(msg, "i", rc); 334 } 335 336 /* Dbus Services offered by this FAN controller */ 337 static const sd_bus_vtable fan_control_vtable[] = 338 { 339 SD_BUS_VTABLE_START(0), 340 SD_BUS_METHOD("setMax", "", "i", &fan_function_router, 341 SD_BUS_VTABLE_UNPRIVILEGED), 342 SD_BUS_METHOD("updatePresent", "", "i", &fan_function_router, 343 SD_BUS_VTABLE_UNPRIVILEGED), 344 SD_BUS_VTABLE_END, 345 }; 346 347 int start_fan_services(fan_info_t *info) 348 { 349 /* Generic error reporter. */ 350 int rc = -1; 351 /* slot where we are offering the FAN dbus service. */ 352 sd_bus_slot *fan_slot = NULL; 353 const char *fan_object = "/org/openbmc/control/fans"; 354 355 info->bus = NULL; 356 /* Get a hook onto system bus. */ 357 rc = sd_bus_open_system(&info->bus); 358 if (rc < 0) { 359 fprintf(stderr,"fanctl: Error opening system bus.\n"); 360 return rc; 361 } 362 363 /* Install the object */ 364 rc = sd_bus_add_object_vtable(info->bus, 365 &fan_slot, 366 fan_object, /* object path */ 367 "org.openbmc.control.Fans", /* interface name */ 368 fan_control_vtable, 369 info); 370 if (rc < 0) { 371 fprintf(stderr, "fanctl: Failed to add object to dbus: %s\n", 372 strerror(-rc)); 373 return rc; 374 } 375 376 /* If we had success in adding the providers, request for a bus name. */ 377 rc = sd_bus_request_name(info->bus, 378 "org.openbmc.control.Fans", 0); 379 if (rc < 0) { 380 fprintf(stderr, "fanctl: Failed to acquire service name: %s\n", 381 strerror(-rc)); 382 return rc; 383 } 384 385 for (;;) { 386 /* Process requests */ 387 rc = sd_bus_process(info->bus, NULL); 388 if (rc < 0) { 389 fprintf(stderr, "fanctl: Failed to process bus: %s\n", 390 strerror(-rc)); 391 break; 392 } 393 if (rc > 0) { 394 continue; 395 } 396 397 rc = sd_bus_wait(info->bus, (uint64_t) - 1); 398 if (rc < 0) { 399 fprintf(stderr, "fanctl: Failed to wait on bus: %s\n", 400 strerror(-rc)); 401 break; 402 } 403 } 404 405 sd_bus_slot_unref(fan_slot); 406 sd_bus_unref(info->bus); 407 408 return rc; 409 } 410 411 static int str_to_int(char *str) 412 { 413 long val; 414 char *temp; 415 416 val = strtol(str, &temp, 10); 417 if (temp == str || *temp != '\0' || 418 ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE)) 419 return -1; 420 if (val < 0) 421 return -1; 422 423 return (int)val; 424 } 425 426 static int parse_argument(int argc, char **argv, fan_info_t *info) 427 { 428 int c; 429 struct option long_options[] = 430 { 431 {"fan_num", required_argument, 0, 'f'}, 432 {"core_num", required_argument, 0, 'c'}, 433 {"cpu_num", required_argument, 0, 'p'}, 434 {"dimm_num", required_argument, 0, 'd'}, 435 {0, 0, 0, 0} 436 }; 437 438 while (1) { 439 c = getopt_long (argc, argv, "c:d:f:p:", long_options, NULL); 440 441 /* Detect the end of the options. */ 442 if (c == -1) 443 break; 444 445 switch (c) { 446 case 'f': 447 info->fan_num = str_to_int(optarg); 448 if (info->fan_num == -1) { 449 fprintf(stderr, "fanctl: Wrong fan_num: %s\n", optarg); 450 return -1; 451 } 452 break; 453 case 'c': 454 info->core_num = str_to_int(optarg); 455 if (info->core_num == -1) { 456 fprintf(stderr, "fanctl: Wrong core_num: %s\n", optarg); 457 return -1; 458 } 459 break; 460 case 'p': 461 info->cpu_num = str_to_int(optarg); 462 if (info->cpu_num == -1) { 463 fprintf(stderr, "fanctl: Wrong cpu_num: %s\n", optarg); 464 return -1; 465 } 466 break; 467 case 'd': 468 info->dimm_num = str_to_int(optarg); 469 if (info->dimm_num == -1) { 470 fprintf(stderr, "fanctl: Wrong dimm_num: %s\n", optarg); 471 return -1; 472 } 473 break; 474 default: 475 fprintf(stderr, "fanctl: Wrong argument\n"); 476 return -1; 477 } 478 } 479 480 return 0; 481 } 482 483 int main(int argc, char **argv) 484 { 485 int rc = 0; 486 fan_info_t fan_info; 487 488 memset(&fan_info, 0, sizeof(fan_info)); 489 rc = parse_argument(argc, argv, &fan_info); 490 if (rc < 0) { 491 fprintf(stderr, "fanctl: Error parse argument\n"); 492 return rc; 493 } 494 /* This call is not supposed to return. If it does, then an error */ 495 rc = start_fan_services(&fan_info); 496 if (rc < 0) { 497 fprintf(stderr, "fanctl: Error starting FAN Services. Exiting"); 498 } 499 500 return rc; 501 } 502