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 "s", 56 obj_path); 57 if (rc < 0) { 58 fprintf(stderr, 59 "Failed to GetObject: %s\n", bus_error.message); 60 goto finish; 61 } 62 63 /* Get the key, aka, the bus name */ 64 sd_bus_message_read(m, "a{sas}", 1, &temp_buf, 1, &intf); 65 strncpy(connection, temp_buf, DBUS_MAX_NAME_LEN); 66 67 finish: 68 sd_bus_error_free(&bus_error); 69 sd_bus_message_unref(m); 70 sd_bus_flush(bus); 71 72 return rc; 73 } 74 75 76 int set_dbus_sensor(sd_bus *bus, const char *obj_path, int val) 77 { 78 char connection[DBUS_MAX_NAME_LEN]; 79 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 80 sd_bus_message *response = NULL; 81 int rc; 82 83 if (!bus || !obj_path) 84 return -1; 85 86 rc = get_connection(bus, connection, obj_path); 87 if (rc < 0) { 88 fprintf(stderr, 89 "fanctl: Failed to get bus name for %s\n", obj_path); 90 goto finish; 91 } 92 93 rc = sd_bus_call_method(bus, 94 connection, 95 obj_path, 96 "org.openbmc.SensorValue", 97 "setValue", 98 &bus_error, 99 &response, 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 "org.openbmc.Sensors" */ 116 int read_dbus_sensor(sd_bus *bus, const char *obj_path) 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_call_method(bus, 136 connection, 137 obj_path, 138 "org.openbmc.SensorValue", 139 "getValue", 140 &bus_error, 141 &response, 142 NULL); 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, "v","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 /org/openbmc/sensors/speed/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 "/org/openbmc/sensors/speed/fan%d", 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 Higer 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 /* The object path is specific to Barreleye */ 219 snprintf(obj_path, sizeof(obj_path), 220 "/org/openbmc/sensors/tach/fan%dH", fan_id); 221 fan_tach_H = read_dbus_sensor(bus, obj_path); 222 snprintf(obj_path, sizeof(obj_path), 223 "/org/openbmc/sensors/tach/fan%dL", fan_id); 224 fan_tach_L = read_dbus_sensor(bus, obj_path); 225 226 /* invalid sensor value is -1 */ 227 if (fan_tach_H <= 0 || fan_tach_L <= 0) 228 fan_speed = 0; 229 else 230 fan_speed = fan_tach_H << FAN_TACH_OFFSET | fan_tach_L; 231 232 fprintf(stderr, "fan%d speed: %d\n", fan_id, fan_speed); 233 return fan_speed; 234 } 235 236 /* set Fan Inventory 'Present' status */ 237 int fan_set_present(sd_bus *bus, int fan_id, int val) 238 { 239 sd_bus_error bus_error = SD_BUS_ERROR_NULL; 240 sd_bus_message *response = NULL; 241 int rc; 242 char obj_path[DBUS_MAX_NAME_LEN]; 243 char connection[DBUS_MAX_NAME_LEN]; 244 245 snprintf(obj_path, sizeof(obj_path), 246 "/org/openbmc/inventory/system/chassis/fan%d", fan_id); 247 248 rc = get_connection(bus, connection, obj_path); 249 if (rc < 0) { 250 fprintf(stderr, 251 "fanctl: Failed to get bus name for %s\n", obj_path); 252 goto finish; 253 } 254 255 rc = sd_bus_call_method(bus, 256 connection, 257 obj_path, 258 "org.openbmc.InventoryItem", 259 "setPresent", 260 &bus_error, 261 &response, 262 "s", 263 (val == 1 ? "True" : "False")); 264 if(rc < 0) 265 fprintf(stderr, 266 "fanctl: Failed to update fan presence via dbus: %s\n", 267 bus_error.message); 268 269 fprintf(stderr, "fanctl: Set fan%d present status to: %s\n", 270 fan_id, (val == 1 ? "True" : "False")); 271 272 finish: 273 sd_bus_error_free(&bus_error); 274 sd_bus_message_unref(response); 275 sd_bus_flush(bus); 276 277 return rc; 278 } 279 280 /* 281 * Update Fan Invertory 'Present' status by first reading fan speed. 282 * If fan speed is '0', the fan is considerred not 'Present'. 283 */ 284 static int fan_update_present(fan_info_t *info) 285 { 286 int i; 287 int rc = -1; 288 int fan_speed; 289 290 if (!info) 291 return -1; 292 293 for (i = 0; i < info->fan_num; i++) { 294 fan_speed = fan_get_speed(info->bus, i); 295 if (fan_speed > 0) 296 rc = fan_set_present(info->bus, i, 1); 297 else 298 rc = fan_set_present(info->bus, i, 0); 299 300 if (rc < 0) { 301 fprintf(stderr, 302 "fanctl: Failed to set fan present status\n"); 303 break; 304 } 305 } 306 307 return rc; 308 } 309 /* 310 * Router function for any FAN operations that come via dbus 311 */ 312 static int fan_function_router(sd_bus_message *msg, void *user_data, 313 sd_bus_error *ret_error) 314 { 315 /* Generic error reporter. */ 316 int rc = -1; 317 fan_info_t *info = user_data; 318 319 /* Get the Operation. */ 320 const char *fan_function = sd_bus_message_get_member(msg); 321 if (fan_function == NULL) { 322 fprintf(stderr, "fanctl: Null FAN function specificed\n"); 323 return sd_bus_reply_method_return(msg, "i", rc); 324 } 325 326 /* Route the user action to appropriate handlers. */ 327 if ((strcmp(fan_function, "setMax") == 0)) { 328 rc = fan_set_max_speed(info); 329 return sd_bus_reply_method_return(msg, "i", rc); 330 } 331 if ((strcmp(fan_function, "updatePresent") == 0)) { 332 rc = fan_update_present(info); 333 return sd_bus_reply_method_return(msg, "i", rc); 334 } 335 336 return sd_bus_reply_method_return(msg, "i", rc); 337 } 338 339 /* Dbus Services offered by this FAN controller */ 340 static const sd_bus_vtable fan_control_vtable[] = 341 { 342 SD_BUS_VTABLE_START(0), 343 SD_BUS_METHOD("setMax", "", "i", &fan_function_router, 344 SD_BUS_VTABLE_UNPRIVILEGED), 345 SD_BUS_METHOD("updatePresent", "", "i", &fan_function_router, 346 SD_BUS_VTABLE_UNPRIVILEGED), 347 SD_BUS_VTABLE_END, 348 }; 349 350 int start_fan_services(fan_info_t *info) 351 { 352 /* Generic error reporter. */ 353 int rc = -1; 354 /* slot where we are offering the FAN dbus service. */ 355 sd_bus_slot *fan_slot = NULL; 356 const char *fan_object = "/org/openbmc/control/fans"; 357 358 info->bus = NULL; 359 /* Get a hook onto system bus. */ 360 rc = sd_bus_open_system(&info->bus); 361 if (rc < 0) { 362 fprintf(stderr,"fanctl: Error opening system bus.\n"); 363 return rc; 364 } 365 366 /* Install the object */ 367 rc = sd_bus_add_object_vtable(info->bus, 368 &fan_slot, 369 fan_object, /* object path */ 370 "org.openbmc.control.Fans", /* interface name */ 371 fan_control_vtable, 372 info); 373 if (rc < 0) { 374 fprintf(stderr, "fanctl: Failed to add object to dbus: %s\n", 375 strerror(-rc)); 376 return rc; 377 } 378 379 /* If we had success in adding the providers, request for a bus name. */ 380 rc = sd_bus_request_name(info->bus, 381 "org.openbmc.control.Fans", 0); 382 if (rc < 0) { 383 fprintf(stderr, "fanctl: Failed to acquire service name: %s\n", 384 strerror(-rc)); 385 return rc; 386 } 387 388 for (;;) { 389 /* Process requests */ 390 rc = sd_bus_process(info->bus, NULL); 391 if (rc < 0) { 392 fprintf(stderr, "fanctl: Failed to process bus: %s\n", 393 strerror(-rc)); 394 break; 395 } 396 if (rc > 0) { 397 continue; 398 } 399 400 rc = sd_bus_wait(info->bus, (uint64_t) - 1); 401 if (rc < 0) { 402 fprintf(stderr, "fanctl: Failed to wait on bus: %s\n", 403 strerror(-rc)); 404 break; 405 } 406 } 407 408 sd_bus_slot_unref(fan_slot); 409 sd_bus_unref(info->bus); 410 411 return rc; 412 } 413 414 static int str_to_int(char *str) 415 { 416 long val; 417 char *temp; 418 419 val = strtol(str, &temp, 10); 420 if (temp == str || *temp != '\0' || 421 ((val == LONG_MIN || val == LONG_MAX) && errno == ERANGE)) 422 return -1; 423 if (val < 0) 424 return -1; 425 426 return (int)val; 427 } 428 429 static int parse_argument(int argc, char **argv, fan_info_t *info) 430 { 431 int c; 432 struct option long_options[] = 433 { 434 {"fan_num", required_argument, 0, 'f'}, 435 {"core_num", required_argument, 0, 'c'}, 436 {"cpu_num", required_argument, 0, 'p'}, 437 {"dimm_num", required_argument, 0, 'd'}, 438 {0, 0, 0, 0} 439 }; 440 441 while (1) { 442 c = getopt_long (argc, argv, "c:d:f:p:", long_options, NULL); 443 444 /* Detect the end of the options. */ 445 if (c == -1) 446 break; 447 448 switch (c) { 449 case 'f': 450 info->fan_num = str_to_int(optarg); 451 if (info->fan_num == -1) { 452 fprintf(stderr, "fanctl: Wrong fan_num: %s\n", optarg); 453 return -1; 454 } 455 break; 456 case 'c': 457 info->core_num = str_to_int(optarg); 458 if (info->core_num == -1) { 459 fprintf(stderr, "fanctl: Wrong core_num: %s\n", optarg); 460 return -1; 461 } 462 break; 463 case 'p': 464 info->cpu_num = str_to_int(optarg); 465 if (info->cpu_num == -1) { 466 fprintf(stderr, "fanctl: Wrong cpu_num: %s\n", optarg); 467 return -1; 468 } 469 break; 470 case 'd': 471 info->dimm_num = str_to_int(optarg); 472 if (info->dimm_num == -1) { 473 fprintf(stderr, "fanctl: Wrong dimm_num: %s\n", optarg); 474 return -1; 475 } 476 break; 477 default: 478 fprintf(stderr, "fanctl: Wrong argument\n"); 479 return -1; 480 } 481 } 482 483 return 0; 484 } 485 486 int main(int argc, char **argv) 487 { 488 int rc = 0; 489 fan_info_t fan_info; 490 491 memset(&fan_info, 0, sizeof(fan_info)); 492 rc = parse_argument(argc, argv, &fan_info); 493 if (rc < 0) { 494 fprintf(stderr, "fanctl: Error parse argument\n"); 495 return rc; 496 } 497 /* This call is not supposed to return. If it does, then an error */ 498 rc = start_fan_services(&fan_info); 499 if (rc < 0) { 500 fprintf(stderr, "fanctl: Error starting FAN Services. Exiting"); 501 } 502 503 return rc; 504 } 505