mirror of
https://github.com/pikvm/ustreamer.git
synced 2026-02-28 12:46:32 +00:00
Merge pull request #46 from pikvm/libgpiod
moved from wiringpi to libgpiod
This commit is contained in:
121
src/gpio/gpio.c
121
src/gpio/gpio.c
@@ -22,8 +22,121 @@
|
||||
|
||||
#include "gpio.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
int gpio_pin_prog_running;
|
||||
int gpio_pin_stream_online;
|
||||
int gpio_pin_has_http_clients;
|
||||
int gpio_pin_workers_busy_at;
|
||||
#include <pthread.h>
|
||||
#include <gpiod.h>
|
||||
|
||||
#include "../tools.h"
|
||||
#include "../logging.h"
|
||||
#include "../threading.h"
|
||||
|
||||
|
||||
struct gpio_t gpio = {
|
||||
.path = "/dev/gpiochip0",
|
||||
.consumer_prefix = "ustreamer",
|
||||
|
||||
# define MAKE_OUTPUT(_role) { \
|
||||
.pin = -1, \
|
||||
.role = _role, \
|
||||
.consumer = NULL, \
|
||||
.line = NULL, \
|
||||
.state = false \
|
||||
}
|
||||
|
||||
.prog_running = MAKE_OUTPUT("prog-running"),
|
||||
.stream_online = MAKE_OUTPUT("stream-online"),
|
||||
.has_http_clients = MAKE_OUTPUT("has-http-clients"),
|
||||
|
||||
# undef MAKE_OUTPUT
|
||||
|
||||
// mutex uninitialized
|
||||
.chip = NULL
|
||||
};
|
||||
|
||||
|
||||
static void _gpio_output_init(struct gpio_output_t *output);
|
||||
static void _gpio_output_destroy(struct gpio_output_t *output);
|
||||
|
||||
|
||||
void gpio_init(void) {
|
||||
assert(gpio.chip == NULL);
|
||||
if (
|
||||
gpio.prog_running.pin >= 0
|
||||
|| gpio.stream_online.pin >= 0
|
||||
|| gpio.has_http_clients.pin >= 0
|
||||
) {
|
||||
A_MUTEX_INIT(&gpio.mutex);
|
||||
LOG_INFO("GPIO: Using chip device: %s", gpio.path);
|
||||
if ((gpio.chip = gpiod_chip_open(gpio.path)) != NULL) {
|
||||
_gpio_output_init(&gpio.prog_running);
|
||||
_gpio_output_init(&gpio.stream_online);
|
||||
_gpio_output_init(&gpio.has_http_clients);
|
||||
} else {
|
||||
LOG_PERROR("GPIO: Can't initialize chip device %s", gpio.path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gpio_destroy(void) {
|
||||
_gpio_output_destroy(&gpio.prog_running);
|
||||
_gpio_output_destroy(&gpio.stream_online);
|
||||
_gpio_output_destroy(&gpio.has_http_clients);
|
||||
if (gpio.chip) {
|
||||
gpiod_chip_close(gpio.chip);
|
||||
gpio.chip = NULL;
|
||||
A_MUTEX_DESTROY(&gpio.mutex);
|
||||
}
|
||||
}
|
||||
|
||||
int gpio_inner_set(struct gpio_output_t *output, bool state) {
|
||||
int retval = 0;
|
||||
|
||||
assert(gpio.chip);
|
||||
assert(output->line);
|
||||
assert(output->state != state); // Must be checked in macro for the performance
|
||||
A_MUTEX_LOCK(&gpio.mutex);
|
||||
|
||||
if (gpiod_line_set_value(output->line, (int)state) < 0) { \
|
||||
LOG_PERROR("GPIO: Can't write value %d to line %s (will be disabled)", state, output->consumer); \
|
||||
_gpio_output_destroy(output);
|
||||
retval = -1;
|
||||
}
|
||||
|
||||
A_MUTEX_UNLOCK(&gpio.mutex);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void _gpio_output_init(struct gpio_output_t *output) {
|
||||
assert(gpio.chip);
|
||||
assert(output->line == NULL);
|
||||
|
||||
A_CALLOC(output->consumer, strlen(gpio.consumer_prefix) + strlen(output->role) + 16);
|
||||
sprintf(output->consumer, "%s::%s", gpio.consumer_prefix, output->role);
|
||||
|
||||
if (output->pin >= 0) {
|
||||
if ((output->line = gpiod_chip_get_line(gpio.chip, output->pin)) != NULL) {
|
||||
if (gpiod_line_request_output(output->line, output->consumer, 0) < 0) {
|
||||
LOG_PERROR("GPIO: Can't request pin=%d as %s", output->pin, output->consumer);
|
||||
_gpio_output_destroy(output);
|
||||
}
|
||||
} else {
|
||||
LOG_PERROR("GPIO: Can't get pin=%d as %s", output->pin, output->consumer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _gpio_output_destroy(struct gpio_output_t *output) {
|
||||
if (output->line) {
|
||||
gpiod_line_release(output->line);
|
||||
output->line = NULL;
|
||||
}
|
||||
if (output->consumer) {
|
||||
free(output->consumer);
|
||||
output->consumer = NULL;
|
||||
}
|
||||
output->state = false;
|
||||
}
|
||||
|
||||
@@ -22,75 +22,62 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <wiringPi.h>
|
||||
#include <pthread.h>
|
||||
#include <gpiod.h>
|
||||
|
||||
#include "../tools.h"
|
||||
#include "../logging.h"
|
||||
|
||||
|
||||
extern int gpio_pin_prog_running;
|
||||
extern int gpio_pin_stream_online;
|
||||
extern int gpio_pin_has_http_clients;
|
||||
extern int gpio_pin_workers_busy_at;
|
||||
struct gpio_output_t {
|
||||
int pin;
|
||||
const char *role;
|
||||
char *consumer;
|
||||
struct gpiod_line *line;
|
||||
bool state;
|
||||
};
|
||||
|
||||
struct gpio_t {
|
||||
char *path;
|
||||
char *consumer_prefix;
|
||||
|
||||
struct gpio_output_t prog_running;
|
||||
struct gpio_output_t stream_online;
|
||||
struct gpio_output_t has_http_clients;
|
||||
|
||||
pthread_mutex_t mutex;
|
||||
struct gpiod_chip *chip;
|
||||
};
|
||||
|
||||
|
||||
#define GPIO_INIT { \
|
||||
gpio_pin_prog_running = -1; \
|
||||
gpio_pin_stream_online = -1; \
|
||||
gpio_pin_has_http_clients = -1; \
|
||||
gpio_pin_workers_busy_at = -1; \
|
||||
}
|
||||
extern struct gpio_t gpio;
|
||||
|
||||
#define GPIO_INIT_PIN(_role, _offset) _gpio_init_pin(#_role, gpio_pin_##_role, _offset)
|
||||
|
||||
INLINE void _gpio_init_pin(const char *role, int base, unsigned offset) {
|
||||
if (base >= 0) {
|
||||
pinMode(base + offset, OUTPUT);
|
||||
if (offset == 0) {
|
||||
LOG_INFO("GPIO: Using pin %d as %s", base, role);
|
||||
} else {
|
||||
LOG_INFO("GPIO: Using pin %d+%u as %s", base, offset, role);
|
||||
}
|
||||
}
|
||||
}
|
||||
void gpio_init(void);
|
||||
void gpio_destroy(void);
|
||||
int gpio_inner_set(struct gpio_output_t *output, bool state);
|
||||
|
||||
#define GPIO_INIT_PINOUT { \
|
||||
if ( \
|
||||
gpio_pin_prog_running >= 0 \
|
||||
|| gpio_pin_stream_online >= 0 \
|
||||
|| gpio_pin_has_http_clients >= 0 \
|
||||
|| gpio_pin_workers_busy_at >= 0 \
|
||||
) { \
|
||||
LOG_INFO("GPIO: Using wiringPi"); \
|
||||
if (wiringPiSetupGpio() < 0) { \
|
||||
LOG_PERROR("GPIO: Can't initialize wiringPi"); \
|
||||
exit(1); \
|
||||
} else { \
|
||||
GPIO_INIT_PIN(prog_running, 0); \
|
||||
GPIO_INIT_PIN(stream_online, 0); \
|
||||
GPIO_INIT_PIN(has_http_clients, 0); \
|
||||
GPIO_INIT_PIN(workers_busy_at, 0); \
|
||||
|
||||
#define SET_STATE(_output, _state) { \
|
||||
if (_output.line && _output.state != _state) { \
|
||||
if (!gpio_inner_set(&_output, _state)) { \
|
||||
_output.state = _state; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define GPIO_SET_STATE(_role, _offset, _state) _gpio_set_state(#_role, gpio_pin_##_role, _offset, _state)
|
||||
|
||||
INLINE void _gpio_set_state(const char *role, int base, unsigned offset, int state) {
|
||||
if (base >= 0) {
|
||||
if (offset == 0) {
|
||||
LOG_DEBUG("GPIO: Writing %d to pin %d (%s)", state, base, role);
|
||||
} else {
|
||||
LOG_DEBUG("GPIO: Writing %d to pin %d+%u (%s)", state, base, offset, role);
|
||||
}
|
||||
digitalWrite(base + offset, state);
|
||||
}
|
||||
INLINE void gpio_set_prog_running(bool state) {
|
||||
SET_STATE(gpio.prog_running, state);
|
||||
}
|
||||
|
||||
#define GPIO_SET_LOW(_role) GPIO_SET_STATE(_role, 0, LOW)
|
||||
#define GPIO_SET_HIGH(_role) GPIO_SET_STATE(_role, 0, HIGH)
|
||||
INLINE void gpio_set_stream_online(bool state) {
|
||||
SET_STATE(gpio.stream_online, state);
|
||||
}
|
||||
|
||||
#define GPIO_SET_LOW_AT(_role, _offset) GPIO_SET_STATE(_role, _offset, LOW)
|
||||
#define GPIO_SET_HIGH_AT(_role, _offset) GPIO_SET_STATE(_role, _offset, HIGH)
|
||||
INLINE void gpio_set_has_http_clients(bool state) {
|
||||
SET_STATE(gpio.has_http_clients, state);
|
||||
}
|
||||
|
||||
#undef SET_STATE
|
||||
|
||||
@@ -550,7 +550,7 @@ static void _http_callback_stream(struct evhttp_request *request, void *v_server
|
||||
}
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_HIGH(has_http_clients);
|
||||
gpio_set_has_http_clients(true);
|
||||
# endif
|
||||
}
|
||||
|
||||
@@ -724,7 +724,7 @@ static void _http_callback_stream_error(UNUSED struct bufferevent *buf_event, UN
|
||||
}
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_LOW(has_http_clients);
|
||||
gpio_set_has_http_clients(false);
|
||||
# endif
|
||||
}
|
||||
|
||||
|
||||
18
src/main.c
18
src/main.c
@@ -31,6 +31,7 @@
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdbool.h>
|
||||
#include <signal.h>
|
||||
|
||||
#include <pthread.h>
|
||||
@@ -114,10 +115,6 @@ int main(int argc, char *argv[]) {
|
||||
A_THREAD_RENAME("main");
|
||||
options = options_init(argc, argv);
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_INIT;
|
||||
# endif
|
||||
|
||||
dev = device_init();
|
||||
encoder = encoder_init();
|
||||
stream = stream_init(dev, encoder);
|
||||
@@ -125,7 +122,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if ((exit_code = options_parse(options, dev, encoder, server)) == 0) {
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_INIT_PINOUT;
|
||||
gpio_init();
|
||||
# endif
|
||||
|
||||
_install_signal_handlers();
|
||||
@@ -140,7 +137,7 @@ int main(int argc, char *argv[]) {
|
||||
|
||||
if ((exit_code = http_server_listen(server)) == 0) {
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_HIGH(prog_running);
|
||||
gpio_set_prog_running(true);
|
||||
# endif
|
||||
|
||||
A_THREAD_CREATE(&stream_loop_tid, _stream_loop_thread, NULL);
|
||||
@@ -148,6 +145,11 @@ int main(int argc, char *argv[]) {
|
||||
A_THREAD_JOIN(server_loop_tid);
|
||||
A_THREAD_JOIN(stream_loop_tid);
|
||||
}
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
gpio_set_prog_running(false);
|
||||
gpio_destroy();
|
||||
# endif
|
||||
}
|
||||
|
||||
http_server_destroy(server);
|
||||
@@ -155,10 +157,6 @@ int main(int argc, char *argv[]) {
|
||||
encoder_destroy(encoder);
|
||||
device_destroy(dev);
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_LOW(prog_running);
|
||||
# endif
|
||||
|
||||
options_destroy(options);
|
||||
if (exit_code == 0) {
|
||||
LOG_INFO("Bye-bye");
|
||||
|
||||
@@ -103,10 +103,11 @@ enum _OPT_VALUES {
|
||||
_O_SERVER_TIMEOUT,
|
||||
|
||||
#ifdef WITH_GPIO
|
||||
_O_GPIO_DEVICE,
|
||||
_O_GPIO_CONSUMER_PREFIX,
|
||||
_O_GPIO_PROG_RUNNING,
|
||||
_O_GPIO_STREAM_ONLINE,
|
||||
_O_GPIO_HAS_HTTP_CLIENTS,
|
||||
_O_GPIO_WORKERS_BUSY_AT,
|
||||
#endif
|
||||
|
||||
#ifdef HAS_PDEATHSIG
|
||||
@@ -179,10 +180,11 @@ static const struct option _LONG_OPTS[] = {
|
||||
{"server-timeout", required_argument, NULL, _O_SERVER_TIMEOUT},
|
||||
|
||||
#ifdef WITH_GPIO
|
||||
{"gpio-device", required_argument, NULL, _O_GPIO_DEVICE},
|
||||
{"gpio-consumer-prefix", required_argument, NULL, _O_GPIO_CONSUMER_PREFIX},
|
||||
{"gpio-prog-running", required_argument, NULL, _O_GPIO_PROG_RUNNING},
|
||||
{"gpio-stream-online", required_argument, NULL, _O_GPIO_STREAM_ONLINE},
|
||||
{"gpio-has-http-clients", required_argument, NULL, _O_GPIO_HAS_HTTP_CLIENTS},
|
||||
{"gpio-workers-busy-at", required_argument, NULL, _O_GPIO_WORKERS_BUSY_AT},
|
||||
#endif
|
||||
|
||||
#ifdef HAS_PDEATHSIG
|
||||
@@ -400,10 +402,11 @@ int options_parse(struct options_t *options, struct device_t *dev, struct encode
|
||||
case _O_SERVER_TIMEOUT: OPT_NUMBER("--server-timeout", server->timeout, 1, 60, 0);
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
case _O_GPIO_PROG_RUNNING: OPT_NUMBER("--gpio-prog-running", gpio_pin_prog_running, 0, 256, 0);
|
||||
case _O_GPIO_STREAM_ONLINE: OPT_NUMBER("--gpio-stream-online", gpio_pin_stream_online, 0, 256, 0);
|
||||
case _O_GPIO_HAS_HTTP_CLIENTS: OPT_NUMBER("--gpio-has-http-clients", gpio_pin_has_http_clients, 0, 256, 0);
|
||||
case _O_GPIO_WORKERS_BUSY_AT: OPT_NUMBER("--gpio-workers-busy-at", gpio_pin_workers_busy_at, 0, 256, 0);
|
||||
case _O_GPIO_DEVICE: OPT_SET(gpio.path, optarg);
|
||||
case _O_GPIO_CONSUMER_PREFIX: OPT_SET(gpio.consumer_prefix, optarg);
|
||||
case _O_GPIO_PROG_RUNNING: OPT_NUMBER("--gpio-prog-running", gpio.prog_running.pin, 0, 256, 0);
|
||||
case _O_GPIO_STREAM_ONLINE: OPT_NUMBER("--gpio-stream-online", gpio.stream_online.pin, 0, 256, 0);
|
||||
case _O_GPIO_HAS_HTTP_CLIENTS: OPT_NUMBER("--gpio-has-http-clients", gpio.has_http_clients.pin, 0, 256, 0);
|
||||
# endif
|
||||
|
||||
# ifdef HAS_PDEATHSIG
|
||||
@@ -646,11 +649,11 @@ static void _help(struct device_t *dev, struct encoder_t *encoder, struct http_s
|
||||
#ifdef WITH_GPIO
|
||||
printf("GPIO options:\n");
|
||||
printf("═════════════\n");
|
||||
printf(" --gpio-device </dev/path> ───── Path to GPIO character device. Default: %s.\n\n", gpio.path);
|
||||
printf(" --gpio-consumer-prefix <str> ── Consumer prefix for GPIO outputs. Default: %s.\n\n", gpio.consumer_prefix);
|
||||
printf(" --gpio-prog-running <pin> ───── Set 1 on GPIO pin while uStreamer is running. Default: disabled.\n\n");
|
||||
printf(" --gpio-stream-online <pin> ──── Set 1 while streaming. Default: disabled\n\n");
|
||||
printf(" --gpio-has-http-clients <pin> ─ Set 1 while stream has at least one client. Default: disabled.\n\n");
|
||||
printf(" --gpio-workers-busy-at <pin> ── Set 1 on (pin + N) while worker with number N has a job.\n");
|
||||
printf(" The worker's numbering starts from 0. Default: disabled\n\n");
|
||||
#endif
|
||||
#if (defined(HAS_PDEATHSIG) || defined(WITH_SETPROCTITLE))
|
||||
printf("Process options:\n");
|
||||
|
||||
21
src/stream.c
21
src/stream.c
@@ -185,7 +185,7 @@ void stream_loop(struct stream_t *stream) {
|
||||
|
||||
} else if (selected == 0) {
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_LOW(stream_online);
|
||||
gpio_set_stream_online(false);
|
||||
# endif
|
||||
|
||||
if (stream->dev->persistent) {
|
||||
@@ -207,7 +207,7 @@ void stream_loop(struct stream_t *stream) {
|
||||
LOG_DEBUG("Frame is ready");
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_HIGH(stream_online);
|
||||
gpio_set_stream_online(true);
|
||||
# endif
|
||||
|
||||
int buf_index;
|
||||
@@ -288,7 +288,7 @@ void stream_loop(struct stream_t *stream) {
|
||||
device_close(stream->dev);
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_LOW(stream_online);
|
||||
gpio_set_stream_online(false);
|
||||
# endif
|
||||
}
|
||||
}
|
||||
@@ -431,17 +431,9 @@ static void *_worker_thread(void *v_worker) {
|
||||
A_THREAD_RENAME("worker-%u", worker->number);
|
||||
LOG_DEBUG("Hello! I am a worker #%u ^_^", worker->number);
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_INIT_PIN(workers_busy_at, worker->number);
|
||||
# endif
|
||||
|
||||
while (!atomic_load(worker->proc_stop) && !atomic_load(worker->workers_stop)) {
|
||||
LOG_DEBUG("Worker %u waiting for a new job ...", worker->number);
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_LOW_AT(workers_busy_at, worker->number);
|
||||
# endif
|
||||
|
||||
A_MUTEX_LOCK(&worker->has_job_mutex);
|
||||
A_COND_WAIT_TRUE(atomic_load(&worker->has_job), &worker->has_job_cond, &worker->has_job_mutex);
|
||||
A_MUTEX_UNLOCK(&worker->has_job_mutex);
|
||||
@@ -451,10 +443,6 @@ static void *_worker_thread(void *v_worker) {
|
||||
|
||||
LOG_DEBUG("Worker %u compressing JPEG from buffer %u ...", worker->number, worker->buf_index);
|
||||
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_HIGH_AT(workers_busy_at, worker->number);
|
||||
# endif
|
||||
|
||||
worker->job_failed = (bool)encoder_compress_buffer(worker->encoder, worker->dev, worker->number, worker->buf_index);
|
||||
|
||||
if (device_release_buffer(worker->dev, worker->buf_index) == 0) {
|
||||
@@ -483,9 +471,6 @@ static void *_worker_thread(void *v_worker) {
|
||||
}
|
||||
|
||||
LOG_DEBUG("Bye-bye (worker %u)", worker->number);
|
||||
# ifdef WITH_GPIO
|
||||
GPIO_SET_LOW_AT(workers_busy_at, worker->number);
|
||||
# endif
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user