277 lines
6.2 KiB
C
277 lines
6.2 KiB
C
/*
|
|
* ser2net - A program for allowing telnet connection to serial ports
|
|
* Copyright (C) 2001-2016 Corey Minyard <minyard@acm.org>
|
|
* Copyright (C) 2015 I2SE GmbH <info@i2se.com>
|
|
* Copyright (C) 2016 Michael Heimpold <mhei@heimpold.de>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/* This file contains a LED driver for Linux's sysfs based LEDs. */
|
|
|
|
#ifdef USE_SYSFS_LED_FEATURE
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
#include <sys/types.h>
|
|
#include <syslog.h>
|
|
#include "led.h"
|
|
|
|
#define SYSFS_LED_BASE "/sys/class/leds"
|
|
|
|
#define BUFSIZE 4096
|
|
|
|
struct led_sysfs_s
|
|
{
|
|
char *device;
|
|
int state;
|
|
int duration;
|
|
};
|
|
|
|
static int
|
|
led_is_trigger_missing(const char *led)
|
|
{
|
|
char *buffer, *trigger;
|
|
int fd, c;
|
|
|
|
buffer = malloc(BUFSIZE);
|
|
if (!buffer)
|
|
return -1;
|
|
|
|
snprintf(buffer, BUFSIZE, "%s/%s/trigger", SYSFS_LED_BASE, led);
|
|
|
|
if ((fd = open(buffer, O_RDONLY)) == -1) {
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
if ((c = read(fd, buffer, BUFSIZE)) <= 0) {
|
|
free(buffer);
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
if (close(fd) < 0) {
|
|
free(buffer);
|
|
return -1;
|
|
}
|
|
|
|
buffer[c] = '\0';
|
|
trigger = strstr(buffer, "transient");
|
|
free(buffer);
|
|
return trigger == NULL;
|
|
}
|
|
|
|
static int
|
|
led_write(const char *led, const char *property, const char *buf)
|
|
{
|
|
char filename[255];
|
|
int fd;
|
|
|
|
snprintf(filename, sizeof(filename), "%s/%s/%s", SYSFS_LED_BASE, led, property);
|
|
|
|
if ((fd = open(filename, O_WRONLY | O_TRUNC)) == -1)
|
|
return -1;
|
|
|
|
if (write(fd, buf, strlen(buf)) != strlen(buf)) {
|
|
close(fd);
|
|
return -1;
|
|
}
|
|
|
|
return close(fd);
|
|
}
|
|
|
|
static int
|
|
led_sysfs_init(struct led_s *led, char *parameters, int lineno)
|
|
{
|
|
struct led_sysfs_s *drv_data = NULL;
|
|
char *str1, *str2, *token, *subtoken;
|
|
char *saveptr1, *saveptr2;
|
|
char *key, *value;
|
|
int i;
|
|
|
|
drv_data = calloc(1, sizeof(*drv_data));
|
|
if (!drv_data) {
|
|
syslog(LOG_ERR,
|
|
"Out of memory handling LED %s on line %d.",
|
|
led->name, lineno);
|
|
return -1;
|
|
}
|
|
|
|
/* preset to detect default and/or wrong user input */
|
|
drv_data->state = -1;
|
|
|
|
/* parse parameter key=value pairs - seperated by whitespace */
|
|
for (str1 = parameters; ; str1 = NULL) {
|
|
token = strtok_r(str1, " \t", &saveptr1);
|
|
if (!token)
|
|
break;
|
|
|
|
/* parse single key=value pair */
|
|
for (i = 0, str2 = token; ; i++, str2 = NULL) {
|
|
subtoken = strtok_r(str2, "=", &saveptr2);
|
|
if (!subtoken)
|
|
break;
|
|
|
|
if (i == 0)
|
|
key = subtoken;
|
|
|
|
if (i == 1) {
|
|
value = subtoken;
|
|
|
|
if (strcasecmp(key, "device") == 0) {
|
|
/* if 'device' is given more than once, last wins */
|
|
if (drv_data->device)
|
|
free(drv_data->device);
|
|
|
|
drv_data->device = strdup(value);
|
|
if (!drv_data->device) {
|
|
syslog(LOG_ERR,
|
|
"Out of memory handling LED '%s' on line %d.",
|
|
led->name, lineno);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
if (strcasecmp(key, "duration") == 0)
|
|
drv_data->duration = atoi(value);
|
|
|
|
if (strcasecmp(key, "state") == 0)
|
|
drv_data->state = atoi(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!drv_data->device) {
|
|
syslog(LOG_ERR,
|
|
"LED '%s': parameter 'device' required, but missing on line %d.",
|
|
led->name, lineno);
|
|
free(drv_data);
|
|
return -1;
|
|
}
|
|
|
|
if (drv_data->duration < 0) {
|
|
syslog(LOG_ERR,
|
|
"LED '%s': invalid duration, using default on line %d.",
|
|
led->name, lineno);
|
|
drv_data->duration = 10;
|
|
}
|
|
if (drv_data->duration == 0)
|
|
drv_data->duration = 10;
|
|
|
|
|
|
if (drv_data->state == -1)
|
|
drv_data->state = 1;
|
|
if (drv_data->state < 0 || drv_data->state > 1) {
|
|
syslog(LOG_ERR,
|
|
"LED '%s': invalid state, using default on line %d.",
|
|
led->name, lineno);
|
|
drv_data->state = 1;
|
|
}
|
|
|
|
led->drv_data = (void *)drv_data;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
led_sysfs_free(struct led_s *led)
|
|
{
|
|
struct led_sysfs_s *ctx = (struct led_sysfs_s *)led->drv_data;
|
|
|
|
free(ctx->device);
|
|
free(ctx);
|
|
|
|
led->drv_data = NULL;
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
led_sysfs_configure(void *led_driver_data)
|
|
{
|
|
struct led_sysfs_s *ctx = (struct led_sysfs_s *)led_driver_data;
|
|
char buffer[255];
|
|
int rv = 0;
|
|
|
|
/* check whether we can enable the transient trigger for this led */
|
|
rv = led_is_trigger_missing(ctx->device);
|
|
if (rv != 0)
|
|
return rv;
|
|
|
|
/*
|
|
* switch to transient trigger, this will kick creation of additional
|
|
* property file in sysfs
|
|
*/
|
|
rv = led_write(ctx->device, "trigger", "transient");
|
|
if (rv)
|
|
return rv;
|
|
|
|
/* pre-configure the trigger for our needs */
|
|
snprintf(buffer, sizeof(buffer), "%d", ctx->duration);
|
|
rv |= led_write(ctx->device, "duration", buffer);
|
|
|
|
snprintf(buffer, sizeof(buffer), "%d", ctx->state);
|
|
rv |= led_write(ctx->device, "state", buffer);
|
|
|
|
return rv;
|
|
}
|
|
|
|
static int
|
|
led_sysfs_flash(void *led_driver_data)
|
|
{
|
|
struct led_sysfs_s *ctx = (struct led_sysfs_s *)led_driver_data;
|
|
|
|
return led_write(ctx->device, "activate", "1");
|
|
}
|
|
|
|
static int
|
|
led_sysfs_deconfigure(void *led_driver_data)
|
|
{
|
|
struct led_sysfs_s *ctx = (struct led_sysfs_s *)led_driver_data;
|
|
int rv = 0;
|
|
|
|
|
|
rv |= led_write(ctx->device, "trigger", "none");
|
|
rv |= led_write(ctx->device, "brightness", "0");
|
|
|
|
return rv;
|
|
}
|
|
|
|
static struct led_driver_s led_sysfs_driver = {
|
|
.name = "sysfs",
|
|
|
|
.init = led_sysfs_init,
|
|
.free = led_sysfs_free,
|
|
|
|
.configure = led_sysfs_configure,
|
|
.flash = led_sysfs_flash,
|
|
.deconfigure = led_sysfs_deconfigure,
|
|
};
|
|
|
|
int
|
|
led_sysfs_register(void)
|
|
{
|
|
return led_driver_register(&led_sysfs_driver);
|
|
}
|
|
#else
|
|
int
|
|
led_sysfs_register(void)
|
|
{
|
|
return 0;
|
|
}
|
|
#endif
|