diff --git a/README.md b/README.md index fe7dedd..813d0df 100644 --- a/README.md +++ b/README.md @@ -111,6 +111,65 @@ int main(void) { [Go to LED documentation.](docs/led.md) + + +### BackLight + +``` c +#include +#include +#include + +#include "backlight.h" + +int main(void) { + backlight_t *bl; + unsigned int max_brightness; + unsigned int bl_val; + + backlight = backlight_new(); + + /* Open sgm3735-backlight backlight device on /sys/class/backlight */ + if (backlight_open(backlight, "sgm3735-backlight") < 0) { + fprintf(stderr, "backlight_open(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + /* Turn on BACKLIGHT (set max brightness) */ + if (backlight_write(backlight, true) < 0) { + fprintf(stderr, "backlight_write(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + /* Get max brightness */ + if (backlight_get_max_brightness(backlight, &max_brightness) < 0) { + fprintf(stderr, "backlight_get_max_brightness(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + /* Set half brightness */ + if (backlight_set_brightness(backlight, max_brightness / 2) < 0) { + fprintf(stderr, "backlight_set_brightness(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + /* Set 10 % brightness */ + bl_val = max_brightness * 10 / 100; + if (backlight_set_brightness(backlight, bl_val) < 0) { + fprintf(stderr, "backlight_set_brightness(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + backlight_close(backlight); + + backlight_free(backlight); + + return 0; +} +``` + +[Go to BackLight documentation.](docs/backlight.md) + ### PWM ``` c diff --git a/docs/backlight.md b/docs/backlight.md new file mode 100644 index 0000000..4624ac2 --- /dev/null +++ b/docs/backlight.md @@ -0,0 +1,235 @@ +### NAME + +BACKLIGHT wrapper functions for Linux userspace sysfs backlights. + +### SYNOPSIS + +``` c +#include + +/* Primary Functions */ +backlight_t *backlight_new(void); +int backlight_open(backlight_t *bl, const char *name); +int backlight_read(backlight_t *bl, bool *value); +int backlight_write(backlight_t *bl, bool value); +int backlight_close(backlight_t *bl); +void backlight_free(backlight_t *bl); + +/* Getters */ +int backlight_get_brightness(backlight_t *bl, unsigned int *brightness); +int backlight_get_max_brightness(backlight_t *bl, unsigned int *max_brightness); + +/* Setters */ +int backlight_set_brightness(backlight_t *bl, unsigned int brightness); + +/* Miscellaneous */ +int backlight_name(backlight_t *bl, char *str, size_t len); +int backlight_tostring(backlight_t *bl, char *str, size_t len); + +/* Error Handling */ +int backlight_errno(backlight_t *bl); +const char *backlight_errmsg(backlight_t *bl); +``` + +### DESCRIPTION + +``` c +backlight_t *backlight_new(void); +``` +Allocate an BACKLIGHT handle. + +Returns a valid handle on success, or NULL on failure. + +------ + +``` c +int backlight_open(backlight_t *bl, const char *name); +``` +Open the sysfs BACKLIGHT with the specified name. + +`backlight` should be a valid pointer to an allocated BACKLIGHT handle structure. + +Returns 0 on success, or a negative [BACKLIGHT error code](#return-value) on failure. + +------ + +``` c +int backlight_read(backlight_t *bl, bool *value); +``` +Read the state of the BACKLIGHT into `value`, where `true` is non-zero brightness, and `false` is zero brightness. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. `value` should be a pointer to an allocated bool. + +Returns 0 on success, or a negative [BACKLIGHT error code](#return-value) on failure. + +------ + +``` c +int backlight_write(backlight_t *bl, bool value); +``` +Write the state of the BACKLIGHT to `value`, where `true` is max brightness, and `false` is zero brightness. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. + +Returns 0 on success, or a negative [BACKLIGHT error code](#return-value) on failure. + +------ + +``` c +int backlight_close(backlight_t *bl); +``` +Close the BACKLIGHT. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. + +Returns 0 on success, or a negative [BACKLIGHT error code](#return-value) on failure. + +------ + +``` c +void backlight_free(backlight_t *bl); +``` +Free an BACKLIGHT handle. + +------ + +``` c +int backlight_get_brightness(backlight_t *bl, unsigned int *brightness); +``` +Get the brightness of the BACKLIGHT. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. + +Returns 0 on success, or a negative [BACKLIGHT error code](#return-value) on failure. + +------ + +``` c +int backlight_get_max_brightness(backlight_t *bl, unsigned int *max_brightness); +``` +Get the max brightness of the BACKLIGHT. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. + +Returns 0 on success, or a negative [BACKLIGHT error code](#return-value) on failure. + +------ + +``` c +int backlight_set_brightness(backlight_t *bl, unsigned int brightness); +``` +Set the brightness of the BACKLIGHT. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. + +Returns 0 on success, or a negative [BACKLIGHT error code](#return-value) on failure. + +------ + +``` c +int backlight_name(backlight_t *bl, char *str, size_t len); +``` +Return the name of the sysfs BACKLIGHT. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. + +Returns 0 on success, or a negative [BACKLIGHT error code](#return-value) on failure. + +------ + +``` c +int backlight_tostring(backlight_t *bl, char *str, size_t len); +``` +Return a string representation of the BACKLIGHT handle. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. + +This function behaves and returns like `snprintf()`. + +------ + +``` c +int backlight_errno(backlight_t *bl); +``` +Return the libc errno of the last failure that occurred. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. + +------ + +``` c +const char *backlight_errmsg(backlight_t *bl); +``` +Return a human readable error message of the last failure that occurred. + +`backlight` should be a valid pointer to an BACKLIGHT handle opened with `backlight_open()`. + +### RETURN VALUE + +The periphery BACKLIGHT functions return 0 on success or one of the negative error codes below on failure. + +The libc errno of the failure in an underlying libc library call can be obtained with the `backlight_errno()` helper function. A human readable error message can be obtained with the `backlight_errmsg()` helper function. + +| Error Code | Description | +|-----------------------|-----------------------------------| +| `BL_ERROR_ARG` | Invalid arguments | +| `BL_ERROR_OPEN` | Opening BACKLIGHT | +| `BL_ERROR_QUERY` | Querying BACKLIGHT attributes | +| `BL_ERROR_IO` | Reading/writing BACKLIGHT brightness | +| `BL_ERROR_CLOSE` | Closing BACKLIGHT | + +### EXAMPLE + +``` c +#include +#include +#include + +#include "backlight.h" + +int main(void) { + backlight_t *bl; + unsigned int max_brightness; + unsigned int bl_val; + + backlight = backlight_new(); + + /* Open sgm3735-backlight backlight device on /sys/class/backlight */ + if (backlight_open(backlight, "sgm3735-backlight") < 0) { + fprintf(stderr, "backlight_open(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + /* Turn on BACKLIGHT (set max brightness) */ + if (backlight_write(backlight, true) < 0) { + fprintf(stderr, "backlight_write(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + /* Get max brightness */ + if (backlight_get_max_brightness(backlight, &max_brightness) < 0) { + fprintf(stderr, "backlight_get_max_brightness(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + /* Set half brightness */ + if (backlight_set_brightness(backlight, max_brightness / 2) < 0) { + fprintf(stderr, "backlight_set_brightness(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + /* Set 10 % brightness */ + bl_val = max_brightness * 10 / 100; + if (backlight_set_brightness(backlight, bl_val) < 0) { + fprintf(stderr, "backlight_set_brightness(): %s\n", backlight_errmsg(backlight)); + exit(1); + } + + backlight_close(backlight); + + backlight_free(backlight); + + return 0; +} +``` + diff --git a/src/backlight.c b/src/backlight.c new file mode 100644 index 0000000..2a54745 --- /dev/null +++ b/src/backlight.c @@ -0,0 +1,224 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "backlight.h" + +/*===================== + * private + *====================*/ +static int _backlight_error(backlight_t *bl, int code, int c_errno, const char *fmt, ...) { + va_list ap; + + bl->error.c_errno = c_errno; + + va_start(ap, fmt); + vsnprintf(bl->error.errmsg, sizeof(bl->error.errmsg), fmt, ap); + va_end(ap); + + /* Tack on strerror() and errno */ + if (c_errno) { + char buf[64]; + strerror_r(c_errno, buf, sizeof(buf)); + snprintf(bl->error.errmsg+strlen(bl->error.errmsg), sizeof(bl->error.errmsg)-strlen(bl->error.errmsg), ": %s [errno %d]", buf, c_errno); + } + + return code; +} + +/*===================== + * Getter + *====================*/ + +int backlight_get_brightness(backlight_t *bl, unsigned int *brightness) { + char backlight_path[P_PATH_MAX]; + char buf[16]; + int fd, ret; + + snprintf(backlight_path, sizeof(backlight_path), DEV_CLASS_PREFIX"/%s/brightness", bl->name); + + if ((fd = open(backlight_path, O_RDONLY)) < 0) + return _backlight_error(bl, BL_ERROR_IO, errno, "Opening BACKLIGHT 'brightness'"); + + if ((ret = read(fd, buf, sizeof(buf))) < 0) { + int errsv = errno; + close(fd); + return _backlight_error(bl, BL_ERROR_IO, errsv, "Reading BACKLIGHT 'brightness'"); + } + + if (close(fd) < 0) + return _backlight_error(bl, BL_ERROR_IO, errno, "Closing BACKLIGHT 'brightness'"); + + /* Null-terminate over newline */ + buf[ret] = '\0'; + + *brightness = strtoul(buf, NULL, 10); + + return 0; +} + +int backlight_get_max_brightness(backlight_t *bl, unsigned int *max_brightness) { + char backlight_path[P_PATH_MAX]; + char buf[16]; + int fd, ret; + + snprintf(backlight_path, sizeof(backlight_path), DEV_CLASS_PREFIX"/%s/max_brightness", bl->name); + + if ((fd = open(backlight_path, O_RDONLY)) < 0) + return _backlight_error(bl, BL_ERROR_QUERY, errno, "Opening BACKLIGHT 'max_brightness'"); + + if ((ret = read(fd, buf, sizeof(buf))) < 0) { + int errsv = errno; + close(fd); + return _backlight_error(bl, BL_ERROR_QUERY, errsv, "Reading BACKLIGHT 'max_brightness'"); + } + + if (close(fd) < 0) + return _backlight_error(bl, BL_ERROR_QUERY, errno, "Closing BACKLIGHT 'max_brightness'"); + + /* Null-terminate over newline */ + buf[ret] = '\0'; + + bl->max_brightness = strtoul(buf, NULL, 10); + + *max_brightness = bl->max_brightness; + + return 0; +} + + +/*===================== + * Setter + *====================*/ +int backlight_set_brightness(backlight_t *bl, unsigned int brightness) { + char backlight_path[P_PATH_MAX]; + char buf[16]; + int fd, len; + + if (brightness > bl->max_brightness) + return _backlight_error(bl, BL_ERROR_ARG, 0, "Brightness out of bounds (max is %u)", bl->max_brightness); + + snprintf(backlight_path, sizeof(backlight_path), DEV_CLASS_PREFIX"/%s/brightness", bl->name); + + if ((fd = open(backlight_path, O_WRONLY)) < 0) + return _backlight_error(bl, BL_ERROR_IO, errno, "Opening BACKLIGHT 'brightness'"); + + len = snprintf(buf, sizeof(buf), "%u\n", brightness); + + if (write(fd, buf, len) < 0) { + int errsv = errno; + close(fd); + return _backlight_error(bl, BL_ERROR_IO, errsv, "Writing BACKLIGHT 'brightness'"); + } + + if (close(fd) < 0) + return _backlight_error(bl, BL_ERROR_IO, errno, "Closing BACKLIGHT 'brightness'"); + + return 0; +} + +/*===================== + * Primary Functions + *====================*/ +backlight_t *backlight_new(void) { + backlight_t *bl = calloc(1, sizeof(backlight_t)); + if (bl == NULL) + return NULL; + + return bl; +} + +int backlight_open(backlight_t *bl, const char *name) { + char backlight_path[P_PATH_MAX]; + int fd, ret; + + snprintf(backlight_path, sizeof(backlight_path), DEV_CLASS_PREFIX"/%s/brightness", name); + + if ((fd = open(backlight_path, O_RDWR)) < 0) + return _backlight_error(bl, BL_ERROR_OPEN, errno, "Opening BACKLIGHT: opening 'brightness'"); + + close(fd); + + strncpy(bl->name, name, sizeof(bl->name) - 1); + bl->name[sizeof(bl->name) - 1] = '\0'; + + if ((ret = backlight_get_max_brightness(bl, &bl->max_brightness)) < 0) + return ret; + + return 0; +} + +int backlight_close(backlight_t *bl) { + (void)bl; + return 0; +} + +int backlight_read(backlight_t *bl, bool *value) { + int ret; + unsigned int brightness; + + if ((ret = backlight_get_brightness(bl, &brightness)) < 0) + return ret; + + *value = brightness != 0; + + return 0; +} + +int backlight_write(backlight_t *bl, bool value) { + return backlight_set_brightness(bl, value ? bl->max_brightness : 0); +} + +void backlight_free(backlight_t *bl) { + free(bl); +} + +/*===================== + * Miscellaneous + *====================*/ +int backlight_name(backlight_t *bl, char *str, size_t len) { + if (!len) + return 0; + + strncpy(str, bl->name, len - 1); + str[len - 1] = '\0'; + + return 0; +} + +int backlight_tostring(backlight_t *bl, char *str, size_t len) { + unsigned int brightness; + char brightness_str[16]; + unsigned int max_brightness; + char max_brightness_str[16]; + + if (backlight_get_brightness(bl, &brightness) < 0) + strcpy(brightness_str, ""); + else + snprintf(brightness_str, sizeof(brightness_str), "%u", brightness); + + if (backlight_get_max_brightness(bl, &max_brightness) < 0) + strcpy(max_brightness_str, ""); + else + snprintf(max_brightness_str, sizeof(max_brightness_str), "%u", max_brightness); + + return snprintf(str, len, "Backlight %s (brightness=%s, max_brightness=%s)", bl->name, brightness_str, max_brightness_str); +} + +/*===================== + * Error Handling + *====================*/ +int backlight_errno(backlight_t *bl) { + return bl->error.c_errno; +} + +const char *backlight_errmsg(backlight_t *bl) { + return bl->error.errmsg; +} diff --git a/src/backlight.h b/src/backlight.h new file mode 100644 index 0000000..3aec55f --- /dev/null +++ b/src/backlight.h @@ -0,0 +1,70 @@ +/* + * c-periphery + * https://github.com/vsergeev/c-periphery + * License: MIT + */ +#ifndef _PERIPHERY_BACKLIGHT_H +#define _PERIPHERY_BACKLIGHT_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + + +#define P_PATH_MAX 256 +#define DEV_CLASS_PREFIX "/sys/class/backlight" + +enum backlight_error_code { + BL_ERROR_ARG = -1, /* Invalid arguments */ + BL_ERROR_OPEN = -2, /* Opening backlight */ + BL_ERROR_QUERY = -3, /* Querying backlight attributes */ + BL_ERROR_IO = -4, /* Reading/writing backlight brightness */ + BL_ERROR_CLOSE = -5, /* Closing backlight */ +}; + +struct backlight_handle { + char name[64]; + unsigned int max_brightness; + + struct { + int c_errno; + char errmsg[96]; + } error; +}; + +typedef struct backlight_handle backlight_t; + +/* Primary Functions */ +backlight_t *backlight_new(void); +int backlight_open(backlight_t *bl, const char *name); +int backlight_close(backlight_t *bl); +int backlight_read(backlight_t *bl, bool *value); +int backlight_write(backlight_t *bl, bool value); +void backlight_free(backlight_t *bl); + + +/* Getters */ +int backlight_get_brightness(backlight_t *bl, unsigned int *brightness); +int backlight_get_max_brightness(backlight_t *bl, unsigned int *max_brightness); + +/* Setters */ +int backlight_set_brightness(backlight_t *bl, unsigned int brightness); + +/* Miscellaneous */ +int backlight_name(backlight_t *bl, char *str, size_t len); +int backlight_tostring(backlight_t *bl, char *str, size_t len); + +/* Error Handling */ +int backlight_errno(backlight_t *bl); +const char *backlight_errmsg(backlight_t *bl); + +#ifdef __cplusplus +}/*extern "C"*/ +#endif + +#endif //_PERIPHERY_BACKLIGHT_H + diff --git a/tests/test_backlight.c b/tests/test_backlight.c new file mode 100644 index 0000000..9e19884 --- /dev/null +++ b/tests/test_backlight.c @@ -0,0 +1,182 @@ +/* + * c-periphery + * https://github.com/vsergeev/c-periphery + * License: MIT + */ + +#include "test.h" + +#include +#include +#include +#include + +#include "../src/backlight.h" + +const char *device; + +void test_arguments(void) { + ptest(); + + /* No real argument validation needed in the BACKLIGHT wrapper */ +} + +void test_open_config_close(void) { + backlight_t *backlight; + char name[64]; + unsigned int max_brightness; + unsigned int brightness; + bool value; + + ptest(); + + /* Allocate BACKLIGHT */ + backlight = backlight_new(); + passert(backlight != NULL); + + /* Open non-existent BACKLIGHT */ + passert(backlight_open(backlight, "nonexistent") == BL_ERROR_OPEN); + + /* Open legitimate BACKLIGHT */ + passert(backlight_open(backlight, device) == 0); + + + /* Check properties */ + passert(backlight_name(backlight, name, sizeof(name)) == 0); + passert(strcmp(name, device) == 0); + /* Check max brightness */ + passert(backlight_get_max_brightness(backlight, &max_brightness) == 0); + passert(max_brightness > 0); + + /* Check setting invalid brightness */ + passert(backlight_set_brightness(backlight, max_brightness + 1) == BL_ERROR_ARG); + + /* Write true, read true, check brightness is max */ + passert(backlight_write(backlight, true) == 0); + usleep(10000); + passert(backlight_read(backlight, &value) == 0); + passert(value == true); + passert(backlight_get_brightness(backlight, &brightness) == 0); + passert(brightness == max_brightness); + + /* Write false, read false, check brightness is zero */ + passert(backlight_write(backlight, false) == 0); + usleep(10000); + passert(backlight_read(backlight, &value) == 0); + passert(value == false); + passert(backlight_get_brightness(backlight, &brightness) == 0); + passert(brightness == 0); + + /* Set brightness to 0, check brightness */ + passert(backlight_set_brightness(backlight, 0) == 0); + sleep(1); + passert(backlight_get_brightness(backlight, &brightness) == 0); + passert(brightness == 0); + + /* Set brightness to max_brightness, check brightness */ + passert(backlight_set_brightness(backlight, max_brightness) == 0); + sleep(1); + passert(backlight_get_brightness(backlight, &brightness) == 0); + passert(brightness >= max_brightness); + + + passert(backlight_close(backlight) == 0); + + /* Free BACKLIGHT */ + backlight_free(backlight); +} + +void test_loopback(void) { + ptest(); +} + +bool getc_yes(void) { + char buf[4]; + fgets(buf, sizeof(buf), stdin); + return (buf[0] == 'y' || buf[0] == 'Y'); +} + +void test_interactive(void) { + char str[256]; + backlight_t *backlight; + unsigned int bl_val; + unsigned int max_brightness; + + ptest(); + + /* Allocate BACKLIGHT */ + backlight = backlight_new(); + passert(backlight != NULL); + + passert(backlight_open(backlight, device) == 0); + + printf("Starting interactive test...\n"); + printf("Press enter to continue...\n"); + getc(stdin); + + /* Check tostring */ + passert(backlight_tostring(backlight, str, sizeof(str)) > 0); + printf("BACKLIGHT description: %s\n", str); + printf("BACKLIGHT description looks OK? y/n\n"); + passert(getc_yes()); + + /* Turn BACKLIGHT off */ + passert(backlight_write(backlight, false) == 0); + printf("BACKLIGHT is off? y/n\n"); + passert(getc_yes()); + + /* Turn BACKLIGHT on */ + passert(backlight_write(backlight, true) == 0); + printf("BACKLIGHT is on? y/n\n"); + passert(getc_yes()); + + /* Check max brightness */ + passert(backlight_get_max_brightness(backlight, &max_brightness) == 0); + passert(max_brightness > 0); + + /* Turn BACKLIGHT off */ + bl_val = max_brightness * 10 / 100; + passert(backlight_set_brightness(backlight, bl_val) == 0); + printf("Has the brightness changed (10%%)? y/n\n"); + passert(getc_yes()); + + /* Turn BACKLIGHT on */ + bl_val = max_brightness * 80 / 100; + passert(backlight_set_brightness(backlight, bl_val) == 0); + printf("Has the brightness changed (80%%)? y/n\n"); + passert(getc_yes()); + + passert(backlight_close(backlight) == 0); + + /* Free BACKLIGHT */ + backlight_free(backlight); +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "Usage: %s \n\n", argv[0]); + fprintf(stderr, "[1/4] Arguments test: No requirements.\n"); + fprintf(stderr, "[2/4] Open/close test: BACKLIGHT should be real.\n"); + fprintf(stderr, "[3/4] Loopback test: No test.\n"); + fprintf(stderr, "[4/4] Interactive test: BACKLIGHT should be observed.\n\n"); + fprintf(stderr, "Please first verify your backlight device. (ls /sys/class/backlight):\n"); + fprintf(stderr, "Example command :\n"); + fprintf(stderr, " %s sgm3735-backlight\n\n", argv[0]); + exit(1); + } + + device = argv[1]; + + test_arguments(); + printf(" " STR_OK " Arguments test passed.\n\n"); + test_open_config_close(); + printf(" " STR_OK " Open/close test passed.\n\n"); + test_loopback(); + printf(" " STR_OK " Loopback test passed.\n\n"); + test_interactive(); + printf(" " STR_OK " Interactive test passed.\n\n"); + + printf("All tests passed!\n"); + return 0; +} +