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