blob: af3e0ba9c6a9ad9dec6c13e8362c82238599ba09 [file] [log] [blame]
Brad Bishop77390492016-04-13 10:47:19 -04001#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
Brad Bishop0c82c602016-04-13 13:36:49 -04008static int led_stable_state_function(const char *, const char *);
9static int led_default_blink(const char *, const char *);
10static int read_led(const char *, const char *, void *, const size_t);
11static int led_custom_blink(const char *, sd_bus_message *);
12
Brad Bishop77390492016-04-13 10:47:19 -040013/*
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 */
18const char *power_ctrl = "brightness";
19const char *blink_ctrl = "trigger";
20const char *duty_on = "delay_on";
21const char *duty_off = "delay_off";
22
23/*
24 * --------------------------------------------------
25 * Given the dbus path, returns the name of the LED
26 * --------------------------------------------------
27 */
28char *
29get_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 */
48int
49write_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 */
90static int
91led_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 {
Gunnar Mills9d572eb2018-04-08 14:30:41 -0500116 fprintf(stderr, "Null LED function specified for : [%s]\n",led_name);
Brad Bishop77390492016-04-13 10:47:19 -0400117 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 */
Brad Bishop0c82c602016-04-13 13:36:49 -0400164static int
165led_stable_state_function(const char *led_name, const char *led_function)
Brad Bishop77390492016-04-13 10:47:19 -0400166{
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//-----------------------------------------------------------------------------------
212int
213blink_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 */
Brad Bishop0c82c602016-04-13 13:36:49 -0400252static int
253led_default_blink(const char *led_name, const char *blink_type)
Brad Bishop77390492016-04-13 10:47:19 -0400254{
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 */
Brad Bishop0c82c602016-04-13 13:36:49 -0400289static int
Brad Bishop77390492016-04-13 10:47:19 -0400290led_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
Gunnar Millsaa4c3102017-10-25 20:50:47 -0500346 * sufficient space for buffer. This will read up to user supplied
Brad Bishop77390492016-04-13 10:47:19 -0400347 * size -or- entire contents of file whichever is smaller
348 * ----------------------------------------------------------------
349 */
Brad Bishop0c82c602016-04-13 13:36:49 -0400350static int
Brad Bishop77390492016-04-13 10:47:19 -0400351read_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 */
398static 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 */
415int
416led_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 */
431int
432start_led_services()
433{
Brad Bishop58e694d2016-04-13 16:04:32 -0400434 static const char *led_dbus_root = "/org/openbmc/control/led";
435
Brad Bishop77390492016-04-13 10:47:19 -0400436 /* 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
Brad Bishop58e694d2016-04-13 16:04:32 -0400467 /* 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
Brad Bishop77390492016-04-13 10:47:19 -0400478 /* 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
Brad Bishop58e694d2016-04-13 16:04:32 -0400487 len = snprintf(led_object, sizeof(led_object), "%s%s%s",
488 led_dbus_root, "/", led_list[num_leds]->d_name);
Brad Bishop77390492016-04-13 10:47:19 -0400489
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 }
Brad Bishop58e694d2016-04-13 16:04:32 -0400510
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 }
Brad Bishop77390492016-04-13 10:47:19 -0400519 }
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. */
Brad Bishop58e694d2016-04-13 16:04:32 -0400529 if(rc >= 0)
Brad Bishop77390492016-04-13 10:47:19 -0400530 {
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
568int
569main(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}