#include #include #include #include #include #include #include #include #include #include #include "msgq.h" #include "tun.h" #include "spi.h" static pthread_t worker_thd; static pthread_t rtscts_thd; static pthread_t msg_handler_thd; static pthread_t tun_handler_thd; static pthread_mutex_t mutex; static pthread_cond_t cond; static MSGQueue spi_bufq; static unsigned char *spi_bufq_array[SPI_SIZE_BUFQ]; static MSGQueue spi_msgq_in; static unsigned char *spi_msgq_in_array[SPI_SIZE_MSGQ_IN]; static MSGQueue spi_msgq_out; static unsigned char *spi_msgq_out_array[SPI_SIZE_MSGQ_OUT]; static uint32_t spi_speed = SPI_SPEED; static int spi_fd; static volatile int spi_cts; struct gpiod_line_request *request = NULL; static void _spi_wait4cts(void) { pthread_mutex_lock(&mutex); while (!spi_cts) { pthread_cond_wait(&cond, &mutex); } spi_cts = 0; pthread_mutex_unlock(&mutex); } static int _spi_xchg(unsigned char *buffer) { int rv; uint16_t len_tx; uint16_t len_rx; struct spi_ioc_transfer tr; memset(&tr, 0, sizeof(tr)); tr.tx_buf = (unsigned long)buffer; tr.rx_buf = (unsigned long)buffer; tr.speed_hz = spi_speed; len_tx = (uint16_t)buffer[1] << 8; len_tx |= (uint16_t)buffer[2] & 0xFF; if (len_tx > SPI_MTU) return SPI_ERR; if (buffer[0]) { len_tx += SPI_SIZE_HDR; // esp32 dma workaraund if (len_tx < 8) { len_tx = 8; } else if (len_tx % 4 != 0) { len_tx = (len_tx / 4 + 1) * 4; } tr.len = len_tx; } else { /* nothing to send, reset esp32 spi transaction */ tr.len = 1; _spi_wait4cts(); rv = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr); if (rv < 0) return SPI_ERR_MSG; /* receive SPI_SIZE_RECEIVE bytes in first transaction (estimate) */ len_tx = SPI_SIZE_RECEIVE + SPI_SIZE_HDR; tr.len = len_tx; buffer[1] = 0; buffer[2] = 0; } _spi_wait4cts(); rv = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr); if (rv < 0) return SPI_ERR_MSG; len_rx = (uint16_t)buffer[1] << 8; len_rx |= (uint16_t)buffer[2] & 0xFF; if (len_rx > SPI_MTU) return SPI_ERR; len_rx += SPI_SIZE_HDR; if (len_rx > len_tx) { tr.tx_buf = (unsigned long)NULL; tr.rx_buf = (unsigned long)(buffer + len_tx); len_tx = len_rx - len_tx; // esp32 dma workaraund if (len_tx < 8) { len_tx = 8; } else if (len_tx % 4 != 0) { len_tx = (len_tx / 4 + 1) * 4; } tr.len = len_tx; _spi_wait4cts(); rv = ioctl(spi_fd, SPI_IOC_MESSAGE(1), &tr); if (rv < 0) return SPI_ERR_MSG; } return SPI_OK; } static void *worker(void *arg) { MSGQueue *bufq = &spi_bufq; MSGQueue *msgq_in = &spi_msgq_in; MSGQueue *msgq_out = &spi_msgq_out; int rv; unsigned char *buffer; while (1) { pthread_mutex_lock(&msgq_out->mutex); buffer = msgq_pop(msgq_out); if ((buffer == NULL) && (gpiod_line_request_get_value(request, SPI_GPIO_RTS) == GPIOD_LINE_VALUE_INACTIVE)) { pthread_mutex_lock(&bufq->mutex); buffer = msgq_pop(bufq); pthread_mutex_unlock(&bufq->mutex); } if (buffer == NULL) { pthread_cond_wait(&msgq_out->cond, &msgq_out->mutex); buffer = msgq_pop(msgq_out); } pthread_mutex_unlock(&msgq_out->mutex); if (buffer) { rv = _spi_xchg(buffer); if (rv || (buffer[0] == 0)) { buffer[0] = 0; buffer[1] = 0; buffer[2] = 0; pthread_mutex_lock(&bufq->mutex); msgq_push(bufq, buffer); pthread_mutex_unlock(&bufq->mutex); } else { pthread_mutex_lock(&msgq_in->mutex); rv = msgq_push(msgq_in, buffer); if (rv == MSGQ_OK) pthread_cond_signal(&msgq_in->cond); pthread_mutex_unlock(&msgq_in->mutex); if (rv) { pthread_mutex_lock(&bufq->mutex); msgq_push(bufq, buffer); pthread_mutex_unlock(&bufq->mutex); } } } } return NULL; } static void *rtscts_handler(void *arg) { MSGQueue *msgq_out = &spi_msgq_out; struct gpiod_edge_event_buffer *event_buffer; struct gpiod_edge_event *event; int rv; event_buffer = gpiod_edge_event_buffer_new(1); while (1) { rv = gpiod_line_request_read_edge_events(request, event_buffer, 1); if (rv != 1) continue; event = gpiod_edge_event_buffer_get_event(event_buffer, 0); switch (gpiod_edge_event_get_line_offset(event)) { case SPI_GPIO_RTS: { pthread_mutex_lock(&msgq_out->mutex); pthread_cond_signal(&msgq_out->cond); pthread_mutex_unlock(&msgq_out->mutex); break; } case SPI_GPIO_CTS: { pthread_mutex_lock(&mutex); spi_cts = 1; pthread_cond_signal(&cond); pthread_mutex_unlock(&mutex); break; } } } gpiod_edge_event_buffer_free(event_buffer); return NULL; } static void *msg_handler(void *arg) { MSGQueue *bufq = &spi_bufq; MSGQueue *msgq_in = &spi_msgq_in; unsigned char *buffer; unsigned char mtype; uint16_t len; int rv; while (1) { pthread_mutex_lock(&msgq_in->mutex); buffer = msgq_pop(msgq_in); if (buffer == NULL) { pthread_cond_wait(&msgq_in->cond, &msgq_in->mutex); buffer = msgq_pop(msgq_in); } pthread_mutex_unlock(&msgq_in->mutex); if (buffer) { mtype = buffer[0]; len = (uint16_t)buffer[1] << 8; len |= (uint16_t)buffer[2] & 0xFF; switch (mtype) { case SPI_MTYPE_TUN: tun_write(buffer + SPI_SIZE_HDR, len); break; } buffer[0] = 0; buffer[1] = 0; buffer[2] = 0; pthread_mutex_lock(&bufq->mutex); msgq_push(bufq, buffer); pthread_mutex_unlock(&bufq->mutex); } } return NULL; } static void *tun_handler(void *arg) { unsigned char *buffer; ssize_t len; while (1) { buffer = spi_alloc(); if (buffer == NULL) continue; len = tun_read(buffer + SPI_SIZE_HDR, SPI_SIZE_BUF - SPI_SIZE_HDR); if (len < 0) { perror("tun read"); continue; } spi_xchg(SPI_MTYPE_TUN, buffer, len); } return NULL; } unsigned char *spi_alloc(void) { MSGQueue *bufq = &spi_bufq; unsigned char *buffer; pthread_mutex_lock(&bufq->mutex); buffer = msgq_pop(bufq); pthread_mutex_unlock(&bufq->mutex); return buffer; } void spi_free(unsigned char *buffer) { MSGQueue *bufq = &spi_bufq; buffer[0] = 0; buffer[1] = 0; buffer[2] = 0; pthread_mutex_lock(&bufq->mutex); msgq_push(bufq, buffer); pthread_mutex_unlock(&bufq->mutex); } int spi_xchg(unsigned char mtype, unsigned char *buffer, uint16_t len) { MSGQueue *bufq = &spi_bufq; MSGQueue *msgq_out = &spi_msgq_out; int rv; buffer[0] = mtype; buffer[1] = len >> 8; buffer[2] = len & 0xFF; pthread_mutex_lock(&msgq_out->mutex); rv = msgq_push(msgq_out, buffer); if (rv == MSGQ_OK) pthread_cond_signal(&msgq_out->cond); pthread_mutex_unlock(&msgq_out->mutex); if (rv) { pthread_mutex_lock(&bufq->mutex); msgq_push(bufq, buffer); pthread_mutex_unlock(&bufq->mutex); } return rv; } int gpio_init(void) { struct gpiod_chip *chip = NULL; struct gpiod_line_settings *line_settings = NULL; struct gpiod_line_config *line_cfg = NULL; struct gpiod_request_config *req_cfg = NULL; int rv; unsigned int line_offsets[2] = { SPI_GPIO_RTS, SPI_GPIO_CTS }; chip = gpiod_chip_open(SPI_GPIO_DEV); if (chip == NULL) goto gpio_init_fin; line_settings = gpiod_line_settings_new(); if (line_settings == NULL) goto gpio_init_fin; gpiod_line_settings_set_direction(line_settings, GPIOD_LINE_DIRECTION_INPUT); gpiod_line_settings_set_edge_detection(line_settings, GPIOD_LINE_EDGE_FALLING); gpiod_line_settings_set_bias(line_settings, GPIOD_LINE_BIAS_PULL_UP); line_cfg = gpiod_line_config_new(); if (line_cfg == NULL) goto gpio_init_fin; rv = gpiod_line_config_add_line_settings(line_cfg, line_offsets, 2, line_settings); if (rv) goto gpio_init_fin; req_cfg = gpiod_request_config_new(); if (req_cfg == NULL) goto gpio_init_fin; gpiod_request_config_set_consumer(req_cfg, "rts-cts"); request = gpiod_chip_request_lines(chip, req_cfg, line_cfg); gpio_init_fin: rv = (request ? SPI_OK : (chip ? SPI_ERR : SPI_ERR_OPEN)); if (req_cfg) gpiod_request_config_free(req_cfg); if (line_cfg) gpiod_line_config_free(line_cfg); if (line_settings) gpiod_line_settings_free(line_settings); if (chip) gpiod_chip_close(chip); return rv; } int spi_init(void) { unsigned char *buffer; int rv, i; spi_fd = open(SPI_DEV, O_RDWR); if (spi_fd < 0) return SPI_ERR_OPEN; rv = ioctl(spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &spi_speed); if (rv == -1) return SPI_ERR; rv = msgq_init(&spi_bufq, spi_bufq_array, SPI_SIZE_BUFQ); assert(rv == MSGQ_OK); rv = msgq_init(&spi_msgq_in, spi_msgq_in_array, SPI_SIZE_MSGQ_IN); assert(rv == MSGQ_OK); rv = msgq_init(&spi_msgq_out, spi_msgq_out_array, SPI_SIZE_MSGQ_OUT); assert(rv == MSGQ_OK); for (i=0; i