blob: 3ceda38edbc2df3c91e76a29c95f0cf4492c4b22 [file] [log] [blame]
Norman James772232e2015-10-18 14:43:28 -05001#include <stdio.h>
Adriana Kobylak9c751042016-02-09 13:44:32 -06002#include <stdlib.h>
3#include <errno.h>
4#include <string.h>
5#include <dirent.h>
6#include <systemd/sd-bus.h>
Norman James772232e2015-10-18 14:43:28 -05007
Adriana Kobylak9c751042016-02-09 13:44:32 -06008/*
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";
Norman James772232e2015-10-18 14:43:28 -050017
Adriana Kobylak9c751042016-02-09 13:44:32 -060018/*
19 * --------------------------------------------------
20 * Given the dbus path, returns the name of the LED
21 * --------------------------------------------------
22 */
23char *get_led_name(const char *dbus_path)
24{
25 char *led_name = NULL;
Norman James772232e2015-10-18 14:43:28 -050026
Adriana Kobylak9c751042016-02-09 13:44:32 -060027 /* Get the led name from /org/openbmc/control/led/<name> */
28 led_name = strrchr(dbus_path, '/');
29 if(led_name)
30 {
31 led_name++;
32 }
Norman James772232e2015-10-18 14:43:28 -050033
Adriana Kobylak9c751042016-02-09 13:44:32 -060034 return led_name;
35}
36
37/*
38 * -------------------------------------------------------------------------
39 * Writes the 'on / off / blink' trigger to leds.
40 * -------------------------------------------------------------------------
41 */
42int write_to_led(const char *name, const char *ctrl_file, const char *value)
43{
44 /* Generic error reporter. */
45 int rc = -1;
46
47 /* To get /sys/class/leds/<name>/<control file> */
48 char led_path[128] = {0};
49
50 int len = 0;
51 len = snprintf(led_path, sizeof(led_path),
52 "/sys/class/leds/%s/%s",name, ctrl_file);
53 if(len >= sizeof(led_path))
54 {
55 fprintf(stderr, "Error. LED path is too long. :[%d]\n",len);
56 return rc;
57 }
58
59 FILE *fp = fopen(led_path,"w");
60 if(fp == NULL)
61 {
62 perror("Error:");
63 fprintf(stderr,"Error opening:[%s]\n",led_path);
64 return rc;
65 }
66
67 rc = fwrite(value, strlen(value), 1, fp);
68 if(rc != 1)
69 {
70 perror("Error:");
71 fprintf(stderr, "Error writing to :[%s]\n",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 led_function_router(sd_bus_message *msg, void *user_data,
86 sd_bus_error *ret_error)
87{
88 /* Generic error reporter. */
89 int rc = -1;
90
91 /* Extract the led name from the full dbus path */
92 const char *led_path = sd_bus_message_get_path(msg);
93 if(led_path == NULL)
94 {
95 fprintf(stderr, "Error. LED path is empty");
96 return sd_bus_reply_method_return(msg, "i", rc);
97 }
98
99 char *led_name = get_led_name(led_path);
100 if(led_name == NULL)
101 {
102 fprintf(stderr, "Invalid LED name for path :[%s]\n",led_path);
103 return sd_bus_reply_method_return(msg, "i", rc);
104 }
105
106 /* Now that we have the LED name, get the Operation. */
107 const char *led_function = sd_bus_message_get_member(msg);
108 if(led_function == NULL)
109 {
110 fprintf(stderr, "Null LED function specificed for : [%s]\n",led_name);
111 return sd_bus_reply_method_return(msg, "i", rc);
112 }
113
114 /* Route the user action to appropriate handlers. */
115 if( (strcmp(led_function, "setOn") == 0) ||
116 (strcmp(led_function, "setOff") == 0))
117 {
118 rc = led_stable_state_function(led_name, led_function);
119 return sd_bus_reply_method_return(msg, "i", rc);
120 }
121 else if( (strcmp(led_function, "setBlinkFast") == 0) ||
122 (strcmp(led_function, "setBlinkSlow") == 0))
123 {
124 rc = led_default_blink(led_name, led_function);
125 return sd_bus_reply_method_return(msg, "i", rc);
126 }
127 else if(strcmp(led_function, "GetLedState") == 0)
128 {
129 char value_str[10] = {0};
130 const char *led_state = NULL;
131
132 rc = read_led(led_name, power_ctrl, value_str, sizeof(value_str));
133 if(rc >= 0)
134 {
135 /* LED is active low */
136 led_state = strtoul(value_str, NULL, 0) ? "Off" : "On";
137 }
138 return sd_bus_reply_method_return(msg, "is", rc, led_state);
139 }
140 else
141 {
142 fprintf(stderr,"Invalid LED function:[%s]\n",led_function);
143 }
144
145 return sd_bus_reply_method_return(msg, "i", rc);
146}
147
148/*
149 * --------------------------------------------------------------
150 * Turn On or Turn Off the LED
151 * --------------------------------------------------------------
152 */
153int led_stable_state_function(char *led_name, char *led_function)
154{
155 /* Generic error reporter. */
156 int rc = -1;
157
158 const char *value = NULL;
159 if(strcmp(led_function, "setOn") == 0)
160 {
161 /* LED active low */
162 value = "0";
163 }
164 else if(strcmp(led_function, "setOff") == 0)
165 {
166 value = "255";
167 }
168 else
169 {
170 fprintf(stderr,"Invalid LED stable state operation:[%s] \n",led_function);
171 return rc;
172 }
173
174 /*
175 * Before doing anything, need to turn off the blinking
176 * if there is one in progress by writing 'none' to trigger
177 */
178 rc = write_to_led(led_name, blink_ctrl, "none");
179 if(rc < 0)
180 {
181 fprintf(stderr,"Error disabling blink. Function:[%s]\n", led_function);
182 return rc;
183 }
184
185 /*
186 * Open the brightness file and write corresponding values.
187 */
188 rc = write_to_led(led_name, power_ctrl, value);
189 if(rc < 0)
190 {
191 fprintf(stderr,"Error driving LED. Function:[%s]\n", led_function);
192 }
193
194 return rc;
195}
196
197//-----------------------------------------------------------------------------------
198// Given the on and off duration, applies the action on the specified LED.
199//-----------------------------------------------------------------------------------
200int blink_led(const char *led_name, const char *on_duration, const char *off_duration)
201{
202 /* Generic error reporter */
203 int rc = -1;
204
205 /* Protocol demands that 'timer' be echoed to 'trigger' */
206 rc = write_to_led(led_name, blink_ctrl, "timer");
207 if(rc < 0)
208 {
209 fprintf(stderr,"Error writing timer to Led:[%s]\n", led_name);
210 return rc;
211 }
212
213 /*
214 * After writing 'timer to 'trigger', 2 new files get generated namely
215 *'delay_on' and 'delay_off' which are telling the time duration for a
216 * particular LED on and off.
217 */
218 rc = write_to_led(led_name, duty_on, on_duration);
219 if(rc < 0)
220 {
221 fprintf(stderr,"Error writing [%s] to delay_on:[%s]\n",on_duration,led_name);
222 return rc;
223 }
224
225 rc = write_to_led(led_name, duty_off, off_duration);
226 if(rc < 0)
227 {
228 fprintf(stderr,"Error writing [%s] to delay_off:[%s]\n",off_duration,led_name);
229 }
230
231 return rc;
232}
233
234/*
235 * ----------------------------------------------------
236 * Default blink action on the LED.
237 * ----------------------------------------------------
238 */
239int led_default_blink(char *led_name, char *blink_type)
240{
241 /* Generic error reporter */
242 int rc = -1;
243
244 /* How long the LED needs to be in on and off state while blinking */
245 const char *on_duration = NULL;
246 const char *off_duration = NULL;
247 if(strcmp(blink_type, "setBlinkSlow") == 0)
248 {
249 //*Delay 900 millisec before 'on' and delay 900 millisec before off */
250 on_duration = "900";
251 off_duration = "900";
252 }
253 else if(strcmp(blink_type, "setBlinkFast") == 0)
254 {
255 /* Delay 200 millisec before 'on' and delay 200 millisec before off */
256 on_duration = "200";
257 off_duration = "200";
258 }
259 else
260 {
261 fprintf(stderr,"Invalid blink operation:[%s]\n",blink_type);
262 return rc;
263 }
264
265 rc = blink_led(led_name, on_duration, off_duration);
266
267 return rc;
268}
269
270/*
271 * ---------------------------------------------------------------
272 * Gets the current value of passed in LED file
273 * Mainly used for reading 'brightness'
274 * NOTE : It is the responsibility of the caller to allocate
275 * sufficient space for buffer. This will read upto user supplied
276 * size -or- entire contents of file whichever is smaller
277 * ----------------------------------------------------------------
278 */
279int read_led(const char *name, const char *ctrl_file,
280 void *value, const size_t len)
281{
282 /* Generic error reporter. */
283 int rc = -1;
284 int count = 0;
285
286 if(value == NULL || len <= 0)
287 {
288 fprintf(stderr, "Invalid buffer passed to LED read\n");
289 return rc;
290 }
291
292 /* To get /sys/class/leds/<name>/<control file> */
293 char led_path[128] = {0};
294
295 int led_len = 0;
296 led_len = snprintf(led_path, sizeof(led_path),
297 "/sys/class/leds/%s/%s",name, ctrl_file);
298 if(led_len >= sizeof(led_path))
299 {
300 fprintf(stderr, "Error. LED path is too long. :[%d]\n",led_len);
301 return rc;
302 }
303
304 FILE *fp = fopen(led_path,"rb");
305 if(fp == NULL)
306 {
307 perror("Error:");
308 fprintf(stderr,"Error opening:[%s]\n",led_path);
309 return rc;
310 }
311
312 char *sysfs_value = (char *)value;
313 while(!feof(fp) && (count < len))
314 {
315 sysfs_value[count++] = fgetc(fp);
316 }
317 sysfs_value[count]='\0';
318
319 fclose(fp);
320 return 0;
321}
322
323/*
324 * -----------------------------------------------
325 * Dbus Services offered by this LED controller
326 * -----------------------------------------------
327 */
328static const sd_bus_vtable led_control_vtable[] =
329{
330 SD_BUS_VTABLE_START(0),
331 SD_BUS_METHOD("setOn", "", "i", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED),
332 SD_BUS_METHOD("setOff", "", "i", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED),
333 SD_BUS_METHOD("setBlinkFast", "", "i", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED),
334 SD_BUS_METHOD("setBlinkSlow", "", "i", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED),
335 SD_BUS_METHOD("GetLedState", "", "is", &led_function_router, SD_BUS_VTABLE_UNPRIVILEGED),
336 SD_BUS_VTABLE_END,
Norman James772232e2015-10-18 14:43:28 -0500337};
338
Adriana Kobylak9c751042016-02-09 13:44:32 -0600339/*
340 * ---------------------------------------------
341 * Interested in all files except standard ones
342 * ---------------------------------------------
343 */
344int led_select(const struct dirent *entry)
Norman James772232e2015-10-18 14:43:28 -0500345{
Adriana Kobylak9c751042016-02-09 13:44:32 -0600346 if( (strcmp(entry->d_name, ".") == 0) ||
347 (strcmp(entry->d_name, "..") == 0))
348 {
349 return 0;
350 }
351 return 1;
Norman James772232e2015-10-18 14:43:28 -0500352}
353
Adriana Kobylak9c751042016-02-09 13:44:32 -0600354/*
355 * ------------------------------------------------
356 * Called as part of setting up skeleton services.
357 * -----------------------------------------------
358 */
359int start_led_services()
Norman James772232e2015-10-18 14:43:28 -0500360{
Adriana Kobylak9c751042016-02-09 13:44:32 -0600361 /* Generic error reporter. */
362 int rc = -1;
363 int num_leds = 0;
364 int count_leds = 0;
365
366 /* Bus and slot where we are offering the LED dbus service. */
367 sd_bus *bus_type = NULL;
368 sd_bus_slot *led_slot = NULL;
369
370 /* For walking '/sys/class/leds/' looking for names of LED.*/
371 struct dirent **led_list;
372
373 /* Get a hook onto system bus. */
374 rc = sd_bus_open_system(&bus_type);
375 if(rc < 0)
376 {
377 fprintf(stderr,"Error opening system bus.\n");
378 return rc;
379 }
380
381 count_leds = num_leds = scandir("/sys/class/leds/",
382 &led_list, led_select, alphasort);
383 if(num_leds <= 0)
384 {
385 fprintf(stderr,"No LEDs present in the system\n");
386
387 sd_bus_slot_unref(led_slot);
388 sd_bus_unref(bus_type);
389 return rc;
390 }
391
392 /* Fully qualified Dbus object for a particular LED */
393 char led_object[128] = {0};
394 int len = 0;
395
396 /* For each led present, announce the service on dbus. */
397 while(num_leds--)
398 {
399 memset(led_object, 0x0, sizeof(led_object));
400
401 len = snprintf(led_object, sizeof(led_object), "%s%s",
402 "/org/openbmc/control/led/", led_list[num_leds]->d_name);
403
404 if(len >= sizeof(led_object))
405 {
406 fprintf(stderr, "Error. LED object is too long:[%d]\n",len);
407 rc = -1;
408 break;
409 }
410
411 /* Install the object */
412 rc = sd_bus_add_object_vtable(bus_type,
413 &led_slot,
414 led_object, /* object path */
415 "org.openbmc.Led", /* interface name */
416 led_control_vtable,
417 NULL);
418
419 if (rc < 0)
420 {
421 fprintf(stderr, "Failed to add object to dbus: %s\n", strerror(-rc));
422 break;
423 }
424 }
425
426 /* Done with all registration. */
427 while (count_leds > 0)
428 {
429 free(led_list[--count_leds]);
430 if(count_leds == 0)
431 {
432 free(led_list);
433 }
434 }
435
436 /* If we had success in adding the providers, request for a bus name. */
437 if(rc == 0)
438 {
439 /* Take one in OpenBmc */
440 rc = sd_bus_request_name(bus_type, "org.openbmc.control.led", 0);
441 if (rc < 0)
442 {
443 fprintf(stderr, "Failed to acquire service name: %s\n", strerror(-rc));
444 }
445 else
446 {
447 for (;;)
448 {
449 /* Process requests */
450 rc = sd_bus_process(bus_type, NULL);
451 if (rc < 0)
452 {
453 fprintf(stderr, "Failed to process bus: %s\n", strerror(-rc));
454 break;
455 }
456 if (rc > 0)
457 {
458 continue;
459 }
460
461 rc = sd_bus_wait(bus_type, (uint64_t) - 1);
462 if (rc < 0)
463 {
464 fprintf(stderr, "Failed to wait on bus: %s\n", strerror(-rc));
465 break;
466 }
467 }
468 }
469 }
470 sd_bus_slot_unref(led_slot);
471 sd_bus_unref(bus_type);
472
473 return rc;
Norman James772232e2015-10-18 14:43:28 -0500474}
475
Adriana Kobylak9c751042016-02-09 13:44:32 -0600476int main(void)
Norman James877dd412015-10-31 17:33:25 -0500477{
Adriana Kobylak9c751042016-02-09 13:44:32 -0600478 int rc = 0;
Norman James877dd412015-10-31 17:33:25 -0500479
Adriana Kobylak9c751042016-02-09 13:44:32 -0600480 /* This call is not supposed to return. If it does, then an error */
481 rc = start_led_services();
482 if(rc < 0)
483 {
484 fprintf(stderr, "Error starting LED Services. Exiting");
485 }
Norman James772232e2015-10-18 14:43:28 -0500486
Adriana Kobylak9c751042016-02-09 13:44:32 -0600487 return rc;
Norman James772232e2015-10-18 14:43:28 -0500488}