diff --git a/api/mraa.h b/api/mraa.h index 70fde77..446d867 100644 --- a/api/mraa.h +++ b/api/mraa.h @@ -34,6 +34,7 @@ extern "C" { #include "mraa/i2c.h" #include "mraa/uart.h" #include "mraa/uart_ow.h" +#include "mraa/led.h" #ifdef __cplusplus } diff --git a/api/mraa.hpp b/api/mraa.hpp index 41685a5..61da0f8 100644 --- a/api/mraa.hpp +++ b/api/mraa.hpp @@ -31,3 +31,4 @@ #include "mraa/i2c.hpp" #include "mraa/spi.hpp" #include "mraa/uart.hpp" +#include "mraa/led.hpp" diff --git a/api/mraa/led.h b/api/mraa/led.h new file mode 100644 index 0000000..1d3c4a5 --- /dev/null +++ b/api/mraa/led.h @@ -0,0 +1,111 @@ +/* + * Author: Manivannan Sadhasivam + * Copyright (c) 2017 Linaro Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +/** + * @file + * @brief LED module + * + * LED is the Light Emitting Diode interface to libmraa. It is used to + * access the on board LED's via sysfs. + * + */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "common.h" +#include + +/** + * Opaque pointer definition to the internal struct _led + */ +typedef struct _led* mraa_led_context; + +/** + * Initialise led_context, based on led function name. + * The structure of LED entry in sysfs is "devicename:colour:function" + * This api expects only one unique LED identifier which would be + * "function" name most often. For instance, `mraa_led_init("user4");` + * + * @param led Name of the LED + * @returns LED context or NULL + */ +mraa_led_context mraa_led_init(const char* led); + +/** + * Set LED brightness + * + * @param dev LED context + * @param value Integer value to write + * @returns Result of operation + */ +mraa_result_t mraa_led_set_brightness(mraa_led_context dev, int value); + +/** + * Read LED brightness + * + * @param dev LED context + * @returns Brightness value + */ +int mraa_led_read_brightness(mraa_led_context dev); + +/** + * Read LED maximum brightness + * + * @param dev LED context + * @returns Maximum brightness value + */ +int mraa_led_read_max_brightness(mraa_led_context dev); + +/** + * Set LED trigger + * + * @param dev LED context + * @param trigger Type of trigger to set + * @returns Result of operation + */ +mraa_result_t mraa_led_set_trigger(mraa_led_context dev, const char* trigger); + +/** + * Clear active LED trigger + * + * @param dev LED context + * @returns Result of operation + */ +mraa_result_t mraa_led_clear_trigger(mraa_led_context dev); + +/** + * Close LED file descriptors and free the context memory + * + * @param dev LED context + * @returns Result of operation + */ +mraa_result_t mraa_led_close(mraa_led_context dev); + +#ifdef __cplusplus +} +#endif diff --git a/api/mraa/led.hpp b/api/mraa/led.hpp new file mode 100644 index 0000000..ca66e63 --- /dev/null +++ b/api/mraa/led.hpp @@ -0,0 +1,139 @@ +/* + * Author: Manivannan Sadhasivam + * Copyright (c) 2017 Linaro Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#pragma once + +#include "led.h" +#include "types.hpp" +#include + +namespace mraa +{ + +/** + * @brief API to Light Emitting Diode + * + * This file defines the LED interface for libmraa + * + */ +class Led +{ + public: + /** + * Instantiates an LED object + * + * @param led LED fuction name to use + */ + Led(const char* led) + { + m_led = mraa_led_init(led); + + if (m_led == NULL) { + throw std::invalid_argument("Invalid LED name specified"); + } + } + + /** + * LED Constructor, takes a pointer to a LED context and initialises + * the LED class + * + * @param led_context void * to LED context + */ + Led(void* led_context) + { + m_led = (mraa_led_context) led_context; + if (m_led == NULL) { + throw std::invalid_argument("Invalid LED name specified"); + } + } + + /** + * LED object destructor + */ + ~Led() + { + mraa_led_close(m_led); + } + + /** + * Set LED brightness value + * + * @param value Value to set LED brightness + * @return Result of operation + */ + Result + setBrightness(int value) + { + return (Result) mraa_led_set_brightness(m_led, value); + } + + /** + * Read LED brightness value + * + * @return LED brightness value + */ + int + readBrightness() + { + return mraa_led_read_brightness(m_led); + } + + /** + * Read LED maximum brightness value + * + * @return LED max brightness value + */ + int + readMaxBrightness() + { + return mraa_led_read_max_brightness(m_led); + } + + /** + * Set LED trigger + * + * @param trigger Type of trigger to set + * @return Result of operation + */ + Result + trigger(const char* trigger) + { + return (Result) mraa_led_set_trigger(m_led, trigger); + } + + /** + * Clear active LED trigger + * + * @return Result of operation + */ + Result + clearTrigger() + { + return (Result) mraa_led_clear_trigger(m_led); + } + + private: + mraa_led_context m_led; +}; +} diff --git a/include/mraa_internal_types.h b/include/mraa_internal_types.h index 1df79f8..ab54888 100644 --- a/include/mraa_internal_types.h +++ b/include/mraa_internal_types.h @@ -241,6 +241,20 @@ struct _iio { }; #endif +/** + * A structure representing an LED device + */ +struct _led { + /*@{*/ + int count; /**< total LED count in a platform */ + char *led_name; /**< LED name */ + char led_path[64]; /**< sysfs path of the LED */ + int trig_fd; /**< trigger file descriptor */ + int bright_fd; /**< brightness file descriptor */ + int max_bright_fd; /**< maximum brightness file descriptor */ + /*@}*/ +}; + /** * A bitfield representing the capabilities of a pin. */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e54b22c..910224c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -28,6 +28,7 @@ set (mraa_LIB_SRCS_NOAUTO ${PROJECT_SOURCE_DIR}/src/spi/spi.c ${PROJECT_SOURCE_DIR}/src/aio/aio.c ${PROJECT_SOURCE_DIR}/src/uart/uart.c + ${PROJECT_SOURCE_DIR}/src/led/led.c ${mraa_LIB_SRCS_NOAUTO} ) @@ -236,7 +237,7 @@ mraa_create_install_pkgconfig (mraa.pc ${LIB_INSTALL_DIR}/pkgconfig) if (DOXYGEN_FOUND) set (CMAKE_SWIG_FLAGS -DDOXYGEN=${DOXYGEN_FOUND}) - set (DOCCLASSES aio gpio i2c pwm spi uart) + set (DOCCLASSES aio gpio i2c pwm spi uart led) # CPP class headers foreach (_file ${DOCCLASSES}) add_custom_command (OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${_file}_class_doc.i diff --git a/src/led/led.c b/src/led/led.c new file mode 100644 index 0000000..eded9e5 --- /dev/null +++ b/src/led/led.c @@ -0,0 +1,380 @@ +/* + * Author: Manivannan Sadhasivam + * Copyright (c) 2017 Linaro Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include "led.h" +#include "mraa_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SYSFS_CLASS_LED "/sys/class/leds" +#define MAX_SIZE 64 + +static mraa_result_t +mraa_led_get_trigfd(mraa_led_context dev) +{ + char buf[MAX_SIZE]; + + snprintf(buf, MAX_SIZE, "%s/%s", dev->led_path, "trigger"); + dev->trig_fd = open(buf, O_RDWR); + if (dev->trig_fd == -1) { + syslog(LOG_ERR, "led: trigger: Failed to open 'trigger': %s", strerror(errno)); + return MRAA_ERROR_INVALID_RESOURCE; + } + + return MRAA_SUCCESS; +} + +static mraa_result_t +mraa_led_get_brightfd(mraa_led_context dev) +{ + char buf[MAX_SIZE]; + + snprintf(buf, MAX_SIZE, "%s/%s", dev->led_path, "brightness"); + dev->bright_fd = open(buf, O_RDWR); + if (dev->bright_fd == -1) { + syslog(LOG_ERR, "led: brightness: Failed to open 'brightness': %s", strerror(errno)); + return MRAA_ERROR_INVALID_RESOURCE; + } + + return MRAA_SUCCESS; +} + +static mraa_result_t +mraa_led_get_maxbrightfd(mraa_led_context dev) +{ + char buf[MAX_SIZE]; + + snprintf(buf, MAX_SIZE, "%s/%s", dev->led_path, "max_brightness"); + dev->max_bright_fd = open(buf, O_RDONLY); + if (dev->max_bright_fd == -1) { + syslog(LOG_ERR, "led: max_brightness: Failed to open 'max_brightness': %s", strerror(errno)); + return MRAA_ERROR_INVALID_RESOURCE; + } + + return MRAA_SUCCESS; +} + +static mraa_led_context +mraa_led_init_internal(const char* led) +{ + DIR* dir; + struct dirent* entry; + int cnt = 0; + + mraa_led_context dev = (mraa_led_context) calloc(1, sizeof(struct _led)); + if (dev == NULL) { + syslog(LOG_CRIT, "led: init: Failed to allocate memory for context"); + return NULL; + } + + dev->led_name = NULL; + dev->trig_fd = -1; + dev->bright_fd = -1; + dev->max_bright_fd = -1; + + if ((dir = opendir(SYSFS_CLASS_LED)) != NULL) { + /* get the led name from sysfs path */ + while ((entry = readdir(dir)) != NULL) { + if (strstr((const char*) entry->d_name, led)) { + dev->led_name = (char*) entry->d_name; + } + cnt++; + } + } + dev->count = cnt; + if (dev->led_name == NULL) { + syslog(LOG_CRIT, "led: init: unknown device specified"); + closedir(dir); + free(dev); + return NULL; + } + + closedir(dir); + return dev; +} + +mraa_led_context +mraa_led_init(const char* led) +{ + mraa_led_context dev = NULL; + char directory[MAX_SIZE]; + struct stat dir; + + if (plat == NULL) { + syslog(LOG_ERR, "led: init: platform not initialised"); + return NULL; + } + + if (led == NULL) { + syslog(LOG_ERR, "led: init: invalid device specified"); + return NULL; + } + + dev = mraa_led_init_internal(led); + if (dev == NULL) { + return NULL; + } + + snprintf(directory, MAX_SIZE, "%s/%s", SYSFS_CLASS_LED, dev->led_name); + if (stat(directory, &dir) == 0 && S_ISDIR(dir.st_mode)) { + syslog(LOG_NOTICE, "led: init: current user don't have access rights for using LED %s", dev->led_name); + } + strncpy(dev->led_path, (const char*) directory, sizeof(directory)); + + return dev; +} + +mraa_result_t +mraa_led_set_brightness(mraa_led_context dev, int value) +{ + char buf[MAX_SIZE]; + int length; + + if (dev == NULL) { + syslog(LOG_ERR, "led: set_brightness: context is invalid"); + return MRAA_ERROR_INVALID_HANDLE; + } + + if (dev->trig_fd != -1) { + close(dev->trig_fd); + dev->trig_fd = -1; + } + + if (dev->max_bright_fd != -1) { + close(dev->max_bright_fd); + dev->max_bright_fd = -1; + } + + if (dev->bright_fd == -1) { + if (mraa_led_get_brightfd(dev) != MRAA_SUCCESS) { + return MRAA_ERROR_INVALID_RESOURCE; + } + } + + if (lseek(dev->bright_fd, 0, SEEK_SET) == -1) { + syslog(LOG_ERR, "led: set_brightness: Failed to lseek 'brightness': %s", strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } + + length = snprintf(buf, sizeof(buf), "%d", value); + if (write(dev->bright_fd, buf, length * sizeof(char)) == -1) { + syslog(LOG_ERR, "led: set_brightness: Failed to write 'brightness': %s", strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } + + return MRAA_SUCCESS; +} + +int +mraa_led_read_brightness(mraa_led_context dev) +{ + char buf[3]; + + if (dev == NULL) { + syslog(LOG_ERR, "led: read_brightness: context is invalid"); + return MRAA_ERROR_INVALID_HANDLE; + } + + if (dev->trig_fd != -1) { + close(dev->trig_fd); + dev->trig_fd = -1; + } + + if (dev->max_bright_fd != -1) { + close(dev->max_bright_fd); + dev->max_bright_fd = -1; + } + + if (dev->bright_fd == -1) { + if (mraa_led_get_brightfd(dev) != MRAA_SUCCESS) { + return MRAA_ERROR_INVALID_RESOURCE; + } + } else { + lseek(dev->bright_fd, 0, SEEK_SET); + } + + if (read(dev->bright_fd, buf, 3 * sizeof(char)) == -1) { + syslog(LOG_ERR, "led: read_brightness: Failed to read 'brightness': %s", strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } + lseek(dev->bright_fd, 0, SEEK_SET); + + return (int) atoi(buf); +} + +int +mraa_led_read_max_brightness(mraa_led_context dev) +{ + char buf[3]; + + if (dev == NULL) { + syslog(LOG_ERR, "led: read_max_brightness: context is invalid"); + return MRAA_ERROR_INVALID_HANDLE; + } + + if (dev->trig_fd != -1) { + close(dev->trig_fd); + dev->trig_fd = -1; + } + + if (dev->bright_fd != -1) { + close(dev->bright_fd); + dev->bright_fd = -1; + } + + if (dev->max_bright_fd == -1) { + if (mraa_led_get_maxbrightfd(dev) != MRAA_SUCCESS) { + return MRAA_ERROR_INVALID_RESOURCE; + } + } else { + lseek(dev->max_bright_fd, 0, SEEK_SET); + } + + if (read(dev->max_bright_fd, buf, 3 * sizeof(char)) == -1) { + syslog(LOG_ERR, "led: read_max_brightness: Failed to read 'max_brightness': %s", strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } + lseek(dev->max_bright_fd, 0, SEEK_SET); + + return (int) atoi(buf); +} + +mraa_result_t +mraa_led_set_trigger(mraa_led_context dev, const char* trigger) +{ + char buf[MAX_SIZE]; + int length; + + if (dev == NULL) { + syslog(LOG_ERR, "led: set_trigger: context is invalid"); + return MRAA_ERROR_INVALID_HANDLE; + } + + if (dev->bright_fd != -1) { + close(dev->bright_fd); + dev->bright_fd = -1; + } + + if (dev->max_bright_fd != -1) { + close(dev->max_bright_fd); + dev->max_bright_fd = -1; + } + + if (trigger == NULL) { + syslog(LOG_ERR, "led: trigger: invalid trigger specified"); + return MRAA_ERROR_INVALID_RESOURCE; + } + + if (dev->trig_fd == -1) { + if (mraa_led_get_trigfd(dev) != MRAA_SUCCESS) { + return MRAA_ERROR_INVALID_RESOURCE; + } + } + + if (lseek(dev->trig_fd, 0, SEEK_SET) == -1) { + syslog(LOG_ERR, "led: set_trigger: Failed to lseek 'trigger': %s", strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } + + length = snprintf(buf, sizeof(buf), "%s", trigger); + if (write(dev->trig_fd, buf, length * sizeof(char)) == -1) { + syslog(LOG_ERR, "led: set_trigger: Failed to write 'trigger': %s", strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } + + return MRAA_SUCCESS; +} + +mraa_result_t +mraa_led_clear_trigger(mraa_led_context dev) +{ + char buf[1] = { '0' }; + + if (dev == NULL) { + syslog(LOG_ERR, "led: clear_trigger: context is invalid"); + return MRAA_ERROR_INVALID_HANDLE; + } + + if (dev->trig_fd != -1) { + close(dev->trig_fd); + dev->trig_fd = -1; + } + + if (dev->max_bright_fd != -1) { + close(dev->max_bright_fd); + dev->max_bright_fd = -1; + } + + if (dev->bright_fd == -1) { + if (mraa_led_get_brightfd(dev) != MRAA_SUCCESS) { + return MRAA_ERROR_INVALID_RESOURCE; + } + } + + if (lseek(dev->bright_fd, 0, SEEK_SET) == -1) { + syslog(LOG_ERR, "led: clear_trigger: Failed to lseek 'brightness': %s", strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } + + /* writing 0 to brightness clears trigger */ + if (write(dev->bright_fd, buf, 1) == -1) { + syslog(LOG_ERR, "led: clear_trigger: Failed to write 'brightness': %s", strerror(errno)); + return MRAA_ERROR_UNSPECIFIED; + } + + return MRAA_SUCCESS; +} + +mraa_result_t +mraa_led_close(mraa_led_context dev) +{ + if (dev == NULL) { + syslog(LOG_ERR, "led: close: context is invalid"); + return MRAA_ERROR_INVALID_HANDLE; + } + + if (dev->bright_fd != -1) { + close(dev->bright_fd); + } + + if (dev->trig_fd != -1) { + close(dev->trig_fd); + } + + if (dev->max_bright_fd != -1) { + close(dev->max_bright_fd); + } + + free(dev); + + return MRAA_SUCCESS; +} diff --git a/src/mraa.i b/src/mraa.i index 2a81f6a..a458e4f 100644 --- a/src/mraa.i +++ b/src/mraa.i @@ -13,6 +13,7 @@ #include "spi.hpp" #include "aio.hpp" #include "uart.hpp" + #include "led.hpp" %} %exception { @@ -34,6 +35,7 @@ %template (spiFromDesc) mraa::initIo; %template (i2cFromDesc) mraa::initIo; %template (pwmFromDesc) mraa::initIo; +%template (ledFromDesc) mraa::initIo; %ignore Aio(void* aio_context); %ignore Pwm(void* pwm_context); @@ -41,6 +43,7 @@ %ignore Spi(void* spi_context); %ignore I2c(void* i2c_context); %ignore Gpio(void* gpio_context); +%ignore Led(void* led_context); %ignore Gpio::nop(uv_work_t* req); %ignore Gpio::v8isr(uv_work_t* req); @@ -59,3 +62,5 @@ %include "aio.hpp" %include "uart.hpp" + +%include "led.hpp"