/* * Copyright (c) 2017 Digi International Inc. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * ======================================================================= */ #include #include #include #include #include #include "device_control.h" /*------------------------------------------------------------------------------ D E F I N I T I O N S ------------------------------------------------------------------------------*/ #define MAX_LENGTH 256 #define FILE_CPU_TEMP "/sys/class/thermal/thermal_zone0/temp" #define FILE_CPU_LOAD "/proc/stat" #define PATH_GPIO_FILES "/sys/class/gpio/" #define EXPORT_FILE PATH_GPIO_FILES "export" #define GPIO_DIR_FILE_FORMAT PATH_GPIO_FILES "gpio%d/direction" #define GPIO_VALUE_FILE_FORMAT PATH_GPIO_FILES "gpio%d/value" /*------------------------------------------------------------------------------ F U N C T I O N D E C L A R A T I O N S ------------------------------------------------------------------------------*/ static int export_gpio(int gpio); static int is_gpio_exported( int gpio); static long read_file(const char * path, char *buffer, long bytes_to_read); static int write_file(const char *const path, const char *const format, ...); /*------------------------------------------------------------------------------ F U N C T I O N D E F I N I T I O N S ------------------------------------------------------------------------------*/ /* * get_cpu_load() - Get the CPU load of the system * * Return: The CPU load in %, -1 if the value is not available. */ double get_cpu_load(void) { static unsigned long long last_work = 0, last_total = 0; char data[MAX_LENGTH] = {0}; unsigned long long int fields[10]; unsigned long long work = 0, total = 0; double usage = -1; int i, result; if (read_file(FILE_CPU_LOAD, data, MAX_LENGTH) <= 0) { IOT_ERROR("Error reading cpu load file"); return usage; } result = sscanf(data, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &fields[0], &fields[1], &fields[2], &fields[3], &fields[4], &fields[5], &fields[6], &fields[7], &fields[8], &fields[9]); if (result < 4) { IOT_ERROR("CPU load not enough fields error"); return usage; } for (i = 0; i < 3; i++) { work += fields[i]; } for (i = 0; i < result; i++) { total += fields[i]; } if (last_work == 0 && last_total == 0) { /* The first time report 0%. */ usage = 0; } else { unsigned long long diff_work = work - last_work; unsigned long long diff_total = total - last_total; usage = diff_work * 100.0 / diff_total; } last_total = total; last_work = work; return usage; } /* * get_cpu_temp() - Get the CPU temperature of the system * * Return: The CPU temperature in C, -1 if error. */ double get_cpu_temp(void) { char file_data[MAX_LENGTH] = {0}; double temperature ; int result; if (read_file(FILE_CPU_TEMP, file_data, MAX_LENGTH) <= 0) { IOT_ERROR("Error reading CPU temperature file"); return -1; } result = sscanf(file_data, "%lf", &temperature); if (result < 1) { IOT_ERROR("Error getting CPU temperature: not enough fields"); return -1; } return temperature / 1000; } /** * init_gpio() - Export and initialize a GPIO * * @gpio: GPIO kernel number * * The provided GPIO is exported and configured as input with edge "none". * * Return: 0 if the GPIO was initialized successfully, -1 otherwise. */ int init_gpio(int gpio) { int error = 0; if (gpio < 0) { IOT_ERROR("Bad GPIO number '%d'", gpio); return -1; } /* Export the GPIO. */ if (export_gpio(gpio) != 0) { IOT_ERROR("Cannot initialize GPIO %d", gpio); return -1; } /* Configure GPIO as input. */ error = set_gpio_direction(gpio, INPUT); if (error != 0) IOT_ERROR("Cannot initialize GPIO %d", gpio); return error; } /** * set_gpio_value() - Set GPIO value, if it is configured as an output * * @gpio: GPIO kernel number. * @value: 1 to set to high, 0 to low. * * Return: 0 if the GPIO was successfully set, -1 otherwise. */ int set_gpio_value(int gpio, unsigned int value) { char gpio_path[MAX_LENGTH] = {0}; if (gpio < 0) { IOT_ERROR("Bad GPIO number '%d'", gpio); return -1; } snprintf(gpio_path, MAX_LENGTH, GPIO_VALUE_FILE_FORMAT, gpio); if (write_file(gpio_path, "%d", value) != 0) { IOT_ERROR("Error setting GPIO %d value to %s (%d)", gpio, value ? "high" : "low", value); return -1; } return 0; } /** * set_gpio_direction() - Set GPIO direction, input or output * * @gpio: GPIO kernel number. * @out: 1 to configure as out, 0 as input. * * Return: 0 if the GPIO was successfully configured, -1 otherwise. */ int set_gpio_direction(int gpio, unsigned int out) { char gpio_path[MAX_LENGTH] = {0}; char buf[MAX_LENGTH] = {0}; IOT_DEBUG("GPIO %d as %s", gpio, out ? "Output" : "Input"); if (gpio < 0) { IOT_ERROR("Bad GPIO number '%d'", gpio); return -1; } snprintf(gpio_path, MAX_LENGTH, GPIO_DIR_FILE_FORMAT, gpio); if (out) strcpy(buf, "out"); else strcpy(buf, "in"); if (write_file(gpio_path, "%s", buf) != 0) { IOT_ERROR("Error setting GPIO %d direction to '%s'", gpio, out ? "Output" : "Input"); return -1; } return 0; } /** * export_gpio() - Export a GPIO * * @gpio: GPIO kernel number. * * Return: 0 if the GPIO was successfully exported, -1 otherwise. */ static int export_gpio(int gpio) { if (gpio < 0) { IOT_ERROR("Bad GPIO number '%d'", gpio); return -1; } if (is_gpio_exported(gpio)) return 0; if (write_file(EXPORT_FILE, "%d", gpio) != 0) { IOT_ERROR("GPIO %d not exported, error writing to 'export' file", gpio); return -1; } /* Check if the GPIO was really exported. */ if (!is_gpio_exported(gpio)) { IOT_ERROR("GPIO %d not exported", gpio); return -1; } return 0; } /** * is_gpio_exported() - Check if a GPIO is exported * * @gpio: GPIO kernel number. * * Return: 1 if the GPIO is exported, 0 otherwise. */ static int is_gpio_exported( int gpio) { char dir_path[MAX_LENGTH] = {0}; DIR *dir = NULL; snprintf(dir_path, MAX_LENGTH, PATH_GPIO_FILES "gpio%d", gpio); dir = opendir(dir_path); if (dir) { closedir(dir); return 1; } else { return 0; } } /** * read_file() - Read the given file and returns its contents * * @path: Absolute path of the file to read. * @buffer: Buffer to store the contents of the file. * @bytes_to_read: The number of bytes to read (including the terminating * null byte). * * Return: The number of read bytes (including the terminating null byte), -1 on * error. */ static long read_file(const char * path, char *buffer, long bytes_to_read) { FILE *fd = NULL; long read_size = 0; if (access(path, R_OK) != 0) { IOT_DEBUG("File cannot be read: %s", path); return -1; } if ((fd = fopen(path, "rb")) == NULL) { IOT_DEBUG("fopen error: %s", path); return -1; } if (fgets(buffer, bytes_to_read, fd) == NULL) { IOT_DEBUG("fgets error: %s", path); read_size = -1; } else { read_size = strlen(buffer) + 1; } fclose(fd); return read_size; } /** * write_file() - Write data to a file * * @path: Absolute path of the file to be written. * @format: String that contains the text to be written to the file. * * Return: 0 if the file was written successfully, -1 otherwise. */ static int write_file(const char *const path, const char *const format, ...) { va_list args; FILE *f = NULL; int len, error = 0; if (access(path, W_OK) != 0) { IOT_DEBUG("File cannot be written: %s", path); return -1; } va_start(args, format); f = fopen(path, "w"); if (f == NULL) { IOT_DEBUG("fopen error: %s", path); error = -1; goto done; } len = vfprintf(f, format, args); if (len < 0) { IOT_DEBUG("vfprintf error: %s", path); error = -1; } fsync(fileno(f)); fclose(f); done: va_end(args); return error; }