1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <errno.h> 4 #include <string.h> 5 #include <dirent.h> 6 #include <systemd/sd-bus.h> 7 8 static int led_stable_state_function(const char *, const char *); 9 static int led_default_blink(const char *, const char *); 10 static int read_led(const char *, const char *, void *, const size_t); 11 static int led_custom_blink(const char *, sd_bus_message *); 12 13 /* 14 * These are control files that are present for each led under 15 *'/sys/class/leds/<led_name>/' which are used to trigger action 16 * on the respective leds by writing predefined data. 17 */ 18 const char *power_ctrl = "brightness"; 19 const char *blink_ctrl = "trigger"; 20 const char *duty_on = "delay_on"; 21 const char *duty_off = "delay_off"; 22 23 /* 24 * -------------------------------------------------- 25 * Given the dbus path, returns the name of the LED 26 * -------------------------------------------------- 27 */ 28 char * 29 get_led_name(const char *dbus_path) 30 { 31 char *led_name = NULL; 32 33 /* Get the led name from /org/openbmc/control/led/<name> */ 34 led_name = strrchr(dbus_path, '/'); 35 if(led_name) 36 { 37 led_name++; 38 } 39 40 return led_name; 41 } 42 43 /* 44 * ------------------------------------------------------------------------- 45 * Writes the 'on / off / blink' trigger to leds. 46 * ------------------------------------------------------------------------- 47 */ 48 int 49 write_to_led(const char *name, const char *ctrl_file, const char *value) 50 { 51 /* Generic error reporter. */ 52 int rc = -1; 53 54 /* To get /sys/class/leds/<name>/<control file> */ 55 char led_path[128] = {0}; 56 57 int len = 0; 58 len = snprintf(led_path, sizeof(led_path), 59 "/sys/class/leds/%s/%s",name, ctrl_file); 60 if(len >= sizeof(led_path)) 61 { 62 fprintf(stderr, "Error. LED path is too long. :[%d]\n",len); 63 return rc; 64 } 65 66 FILE *fp = fopen(led_path,"w"); 67 if(fp == NULL) 68 { 69 fprintf(stderr,"Error:[%s] opening:[%s]\n",strerror(errno),led_path); 70 return rc; 71 } 72 73 rc = fwrite(value, strlen(value), 1, fp); 74 if(rc != 1) 75 { 76 fprintf(stderr, "Error:[%s] writing to :[%s]\n",strerror(errno),led_path); 77 } 78 79 fclose(fp); 80 81 /* When we get here, rc would be what it was from writing to the file */ 82 return (rc == 1) ? 0 : -1; 83 } 84 85 /* 86 * ---------------------------------------------------------------- 87 * Router function for any LED operations that come via dbus 88 *---------------------------------------------------------------- 89 */ 90 static int 91 led_function_router(sd_bus_message *msg, void *user_data, 92 sd_bus_error *ret_error) 93 { 94 /* Generic error reporter. */ 95 int rc = -1; 96 97 /* Extract the led name from the full dbus path */ 98 const char *led_path = sd_bus_message_get_path(msg); 99 if(led_path == NULL) 100 { 101 fprintf(stderr, "Error. LED path is empty"); 102 return sd_bus_reply_method_return(msg, "i", rc); 103 } 104 105 char *led_name = get_led_name(led_path); 106 if(led_name == NULL) 107 { 108 fprintf(stderr, "Invalid LED name for path :[%s]\n",led_path); 109 return sd_bus_reply_method_return(msg, "i", rc); 110 } 111 112 /* Now that we have the LED name, get the Operation. */ 113 const char *led_function = sd_bus_message_get_member(msg); 114 if(led_function == NULL) 115 { 116 fprintf(stderr, "Null LED function specificed for : [%s]\n",led_name); 117 return sd_bus_reply_method_return(msg, "i", rc); 118 } 119 120 /* Route the user action to appropriate handlers. */ 121 if( (strcmp(led_function, "setOn") == 0) || 122 (strcmp(led_function, "setOff") == 0)) 123 { 124 rc = led_stable_state_function(led_name, led_function); 125 return sd_bus_reply_method_return(msg, "i", rc); 126 } 127 else if( (strcmp(led_function, "setBlinkFast") == 0) || 128 (strcmp(led_function, "setBlinkSlow") == 0)) 129 { 130 rc = led_default_blink(led_name, led_function); 131 return sd_bus_reply_method_return(msg, "i", rc); 132 } 133 else if(strcmp(led_function, "BlinkCustom") == 0) 134 { 135 rc = led_custom_blink(led_name, msg); 136 return sd_bus_reply_method_return(msg, "i", rc); 137 } 138 else if(strcmp(led_function, "GetLedState") == 0) 139 { 140 char value_str[10] = {0}; 141 const char *led_state = NULL; 142 143 rc = read_led(led_name, power_ctrl, value_str, sizeof(value_str)-1); 144 if(rc >= 0) 145 { 146 /* LED is active HI */ 147 led_state = strtoul(value_str, NULL, 0) ? "On" : "Off"; 148 } 149 return sd_bus_reply_method_return(msg, "is", rc, led_state); 150 } 151 else 152 { 153 fprintf(stderr,"Invalid LED function:[%s]\n",led_function); 154 } 155 156 return sd_bus_reply_method_return(msg, "i", rc); 157 } 158 159 /* 160 * -------------------------------------------------------------- 161 * Turn On or Turn Off the LED 162 * -------------------------------------------------------------- 163 */ 164 static int 165 led_stable_state_function(const char *led_name, const char *led_function) 166 { 167 /* Generic error reporter. */ 168 int rc = -1; 169 170 const char *value = NULL; 171 if(strcmp(led_function, "setOff") == 0) 172 { 173 /* LED active low */ 174 value = "0"; 175 } 176 else if(strcmp(led_function, "setOn") == 0) 177 { 178 value = "255"; 179 } 180 else 181 { 182 fprintf(stderr,"Invalid LED stable state operation:[%s] \n",led_function); 183 return rc; 184 } 185 186 /* 187 * Before doing anything, need to turn off the blinking 188 * if there is one in progress by writing 'none' to trigger 189 */ 190 rc = write_to_led(led_name, blink_ctrl, "none"); 191 if(rc < 0) 192 { 193 fprintf(stderr,"Error disabling blink. Function:[%s]\n", led_function); 194 return rc; 195 } 196 197 /* 198 * Open the brightness file and write corresponding values. 199 */ 200 rc = write_to_led(led_name, power_ctrl, value); 201 if(rc < 0) 202 { 203 fprintf(stderr,"Error driving LED. Function:[%s]\n", led_function); 204 } 205 206 return rc; 207 } 208 209 //----------------------------------------------------------------------------------- 210 // Given the on and off duration, applies the action on the specified LED. 211 //----------------------------------------------------------------------------------- 212 int 213 blink_led(const char *led_name, const char *on_duration, const char *off_duration) 214 { 215 /* Generic error reporter */ 216 int rc = -1; 217 218 /* Protocol demands that 'timer' be echoed to 'trigger' */ 219 rc = write_to_led(led_name, blink_ctrl, "timer"); 220 if(rc < 0) 221 { 222 fprintf(stderr,"Error writing timer to Led:[%s]\n", led_name); 223 return rc; 224 } 225 226 /* 227 * After writing 'timer to 'trigger', 2 new files get generated namely 228 *'delay_on' and 'delay_off' which are telling the time duration for a 229 * particular LED on and off. 230 */ 231 rc = write_to_led(led_name, duty_on, on_duration); 232 if(rc < 0) 233 { 234 fprintf(stderr,"Error writing [%s] to delay_on:[%s]\n",on_duration,led_name); 235 return rc; 236 } 237 238 rc = write_to_led(led_name, duty_off, off_duration); 239 if(rc < 0) 240 { 241 fprintf(stderr,"Error writing [%s] to delay_off:[%s]\n",off_duration,led_name); 242 } 243 244 return rc; 245 } 246 247 /* 248 * ---------------------------------------------------- 249 * Default blink action on the LED. 250 * ---------------------------------------------------- 251 */ 252 static int 253 led_default_blink(const char *led_name, const char *blink_type) 254 { 255 /* Generic error reporter */ 256 int rc = -1; 257 258 /* How long the LED needs to be in on and off state while blinking */ 259 const char *on_duration = NULL; 260 const char *off_duration = NULL; 261 if(strcmp(blink_type, "setBlinkSlow") == 0) 262 { 263 //*Delay 900 millisec before 'on' and delay 900 millisec before off */ 264 on_duration = "900"; 265 off_duration = "900"; 266 } 267 else if(strcmp(blink_type, "setBlinkFast") == 0) 268 { 269 /* Delay 200 millisec before 'on' and delay 200 millisec before off */ 270 on_duration = "200"; 271 off_duration = "200"; 272 } 273 else 274 { 275 fprintf(stderr,"Invalid blink operation:[%s]\n",blink_type); 276 return rc; 277 } 278 279 rc = blink_led(led_name, on_duration, off_duration); 280 281 return rc; 282 } 283 284 /* 285 * ------------------------------------------------- 286 * Blinks at user defined 'on' and 'off' intervals. 287 * ------------------------------------------------- 288 */ 289 static int 290 led_custom_blink(const char *led_name, sd_bus_message *msg) 291 { 292 /* Generic error reporter. */ 293 int rc = -1; 294 int led_len = 0; 295 296 /* User supplied 'on' and 'off' duration converted into string */ 297 char on_duration[32] = {0}; 298 char off_duration[32] = {0}; 299 300 /* User supplied 'on' and 'off' duration */ 301 uint32_t user_input_on = 0; 302 uint32_t user_input_off = 0; 303 304 /* Extract values into 'ss' ( string, string) */ 305 rc = sd_bus_message_read(msg, "uu", &user_input_on, &user_input_off); 306 if(rc < 0) 307 { 308 fprintf(stderr, "Failed to read 'on' and 'off' duration.[%s]\n", strerror(-rc)); 309 } 310 else 311 { 312 /* 313 * Converting user supplied integer arguments into string as required by 314 * sys interface. The top level REST will make sure that an error is 315 * thrown right away on invalid inputs. However, REST is allowing the 316 * unsigned decimal and floating numbers but when its received here, its 317 * received as decimal so no input validation needed. 318 */ 319 led_len = snprintf(on_duration, sizeof(on_duration), 320 "%d",user_input_on); 321 if(led_len >= sizeof(on_duration)) 322 { 323 fprintf(stderr, "Error. Blink ON duration is too long. :[%d]\n",led_len); 324 return rc; 325 } 326 327 led_len = snprintf(off_duration, sizeof(off_duration), 328 "%d",user_input_off); 329 if(led_len >= sizeof(off_duration)) 330 { 331 fprintf(stderr, "Error. Blink OFF duration is too long. :[%d]\n",led_len); 332 return rc; 333 } 334 335 /* We are good here.*/ 336 rc = blink_led(led_name, on_duration, off_duration); 337 } 338 return rc; 339 } 340 341 /* 342 * --------------------------------------------------------------- 343 * Gets the current value of passed in LED file 344 * Mainly used for reading 'brightness' 345 * NOTE : It is the responsibility of the caller to allocate 346 * sufficient space for buffer. This will read upto user supplied 347 * size -or- entire contents of file whichever is smaller 348 * ---------------------------------------------------------------- 349 */ 350 static int 351 read_led(const char *name, const char *ctrl_file, 352 void *value, const size_t len) 353 { 354 /* Generic error reporter. */ 355 int rc = -1; 356 int count = 0; 357 358 if(value == NULL || len <= 0) 359 { 360 fprintf(stderr, "Invalid buffer passed to LED read\n"); 361 return rc; 362 } 363 364 /* To get /sys/class/leds/<name>/<control file> */ 365 char led_path[128] = {0}; 366 367 int led_len = 0; 368 led_len = snprintf(led_path, sizeof(led_path), 369 "/sys/class/leds/%s/%s",name, ctrl_file); 370 if(led_len >= sizeof(led_path)) 371 { 372 fprintf(stderr, "Error. LED path is too long. :[%d]\n",led_len); 373 return rc; 374 } 375 376 FILE *fp = fopen(led_path,"rb"); 377 if(fp == NULL) 378 { 379 fprintf(stderr,"Error:[%s] opening:[%s]\n",strerror(errno),led_path); 380 return rc; 381 } 382 383 char *sysfs_value = (char *)value; 384 while(!feof(fp) && (count < len)) 385 { 386 sysfs_value[count++] = fgetc(fp); 387 } 388 389 fclose(fp); 390 return 0; 391 } 392 393 /* 394 * ----------------------------------------------- 395 * Dbus Services offered by this LED controller 396 * ----------------------------------------------- 397 */ 398 static const sd_bus_vtable led_control_vtable[] = 399 { 400 SD_BUS_VTABLE_START(0), 401 SD_BUS_METHOD("setOn", "", "i", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED), 402 SD_BUS_METHOD("setOff", "", "i", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED), 403 SD_BUS_METHOD("setBlinkFast", "", "i", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED), 404 SD_BUS_METHOD("setBlinkSlow", "", "i", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED), 405 SD_BUS_METHOD("GetLedState", "", "is", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED), 406 SD_BUS_METHOD("BlinkCustom", "uu", "i", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED), 407 SD_BUS_VTABLE_END, 408 }; 409 410 /* 411 * --------------------------------------------- 412 * Interested in all files except standard ones 413 * --------------------------------------------- 414 */ 415 int 416 led_select(const struct dirent *entry) 417 { 418 if( (strcmp(entry->d_name, ".") == 0) || 419 (strcmp(entry->d_name, "..") == 0)) 420 { 421 return 0; 422 } 423 return 1; 424 } 425 426 /* 427 * ------------------------------------------------ 428 * Called as part of setting up skeleton services. 429 * ----------------------------------------------- 430 */ 431 int 432 start_led_services() 433 { 434 static const char *led_dbus_root = "/org/openbmc/control/led"; 435 436 /* Generic error reporter. */ 437 int rc = -1; 438 int num_leds = 0; 439 int count_leds = 0; 440 441 /* Bus and slot where we are offering the LED dbus service. */ 442 sd_bus *bus_type = NULL; 443 sd_bus_slot *led_slot = NULL; 444 445 /* For walking '/sys/class/leds/' looking for names of LED.*/ 446 struct dirent **led_list; 447 448 /* Get a hook onto system bus. */ 449 rc = sd_bus_open_system(&bus_type); 450 if(rc < 0) 451 { 452 fprintf(stderr,"Error opening system bus.\n"); 453 return rc; 454 } 455 456 count_leds = num_leds = scandir("/sys/class/leds/", 457 &led_list, led_select, alphasort); 458 if(num_leds <= 0) 459 { 460 fprintf(stderr,"No LEDs present in the system\n"); 461 462 sd_bus_slot_unref(led_slot); 463 sd_bus_unref(bus_type); 464 return rc; 465 } 466 467 /* Install a freedesktop object manager */ 468 rc = sd_bus_add_object_manager(bus_type, NULL, led_dbus_root); 469 if(rc < 0) { 470 fprintf(stderr, "Failed to add object to dbus: %s\n", 471 strerror(-rc)); 472 473 sd_bus_slot_unref(led_slot); 474 sd_bus_unref(bus_type); 475 return rc; 476 } 477 478 /* Fully qualified Dbus object for a particular LED */ 479 char led_object[128] = {0}; 480 int len = 0; 481 482 /* For each led present, announce the service on dbus. */ 483 while(num_leds--) 484 { 485 memset(led_object, 0x0, sizeof(led_object)); 486 487 len = snprintf(led_object, sizeof(led_object), "%s%s%s", 488 led_dbus_root, "/", led_list[num_leds]->d_name); 489 490 if(len >= sizeof(led_object)) 491 { 492 fprintf(stderr, "Error. LED object is too long:[%d]\n",len); 493 rc = -1; 494 break; 495 } 496 497 /* Install the object */ 498 rc = sd_bus_add_object_vtable(bus_type, 499 &led_slot, 500 led_object, /* object path */ 501 "org.openbmc.Led", /* interface name */ 502 led_control_vtable, 503 NULL); 504 505 if(rc < 0) 506 { 507 fprintf(stderr, "Failed to add object to dbus: %s\n", strerror(-rc)); 508 break; 509 } 510 511 rc = sd_bus_emit_object_added(bus_type, led_object); 512 513 if(rc < 0) 514 { 515 fprintf(stderr, "Failed to emit InterfacesAdded " 516 "signal: %s\n", strerror(-rc)); 517 break; 518 } 519 } 520 521 /* Done with all registration. */ 522 while(count_leds > 0) 523 { 524 free(led_list[--count_leds]); 525 } 526 free(led_list); 527 528 /* If we had success in adding the providers, request for a bus name. */ 529 if(rc >= 0) 530 { 531 /* Take one in OpenBmc */ 532 rc = sd_bus_request_name(bus_type, "org.openbmc.control.led", 0); 533 if(rc < 0) 534 { 535 fprintf(stderr, "Failed to acquire service name: %s\n", strerror(-rc)); 536 } 537 else 538 { 539 for(;;) 540 { 541 /* Process requests */ 542 rc = sd_bus_process(bus_type, NULL); 543 if(rc < 0) 544 { 545 fprintf(stderr, "Failed to process bus: %s\n", strerror(-rc)); 546 break; 547 } 548 if(rc > 0) 549 { 550 continue; 551 } 552 553 rc = sd_bus_wait(bus_type, (uint64_t) - 1); 554 if(rc < 0) 555 { 556 fprintf(stderr, "Failed to wait on bus: %s\n", strerror(-rc)); 557 break; 558 } 559 } 560 } 561 } 562 sd_bus_slot_unref(led_slot); 563 sd_bus_unref(bus_type); 564 565 return rc; 566 } 567 568 int 569 main(void) 570 { 571 int rc = 0; 572 573 /* This call is not supposed to return. If it does, then an error */ 574 rc = start_led_services(); 575 if(rc < 0) 576 { 577 fprintf(stderr, "Error starting LED Services. Exiting"); 578 } 579 580 return rc; 581 } 582