Glue USB and net socket to GDB thread

This commit is contained in:
DrZlo13 2021-11-30 14:38:57 +10:00
parent c8427b1cf8
commit 027bd85bd6
8 changed files with 353 additions and 78 deletions

View File

@ -2,36 +2,94 @@
#include <stddef.h>
#include <stdbool.h>
#include <esp_log.h>
#include <freertos/FreeRTOS.h>
#include <freertos/stream_buffer.h>
size_t usb_cdc_rx(uint8_t* buffer, size_t size);
size_t usb_cdc_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout);
#define GDB_TX_BUFFER_SIZE 4096
#define GDB_RX_BUFFER_SIZE 4096
#define GDB_RX_PACKET_MAX_SIZE 64
#define TAG "gdb-glue"
typedef struct {
StreamBufferHandle_t rx_stream;
bool rx_stream_full;
uint8_t tx_buffer[GDB_TX_BUFFER_SIZE];
size_t tx_buffer_index;
} GDBGlue;
static GDBGlue gdb_glue;
/* GDB socket */
bool network_gdb_connected(void);
void network_gdb_send(uint8_t* buffer, size_t size);
/* USB-CDC */
void usb_cdc_tx_char(uint8_t c, bool flush);
unsigned char gdb_if_getchar_to(int timeout) {
const uint8_t buffer_size = 1;
uint8_t buffer[buffer_size];
size_t gdb_glue_get_free_size(void) {
return xStreamBufferSpacesAvailable(gdb_glue.rx_stream);
}
size_t cnt = usb_cdc_rx_with_timeout(buffer, buffer_size, timeout);
if(cnt) {
return buffer[buffer_size - 1];
} else {
void gdb_glue_receive(uint8_t* buffer, size_t size) {
size_t ret = xStreamBufferSend(gdb_glue.rx_stream, buffer, size, portMAX_DELAY);
ESP_ERROR_CHECK(ret != size);
}
bool gdb_glue_can_receive() {
uint16_t max_len = xStreamBufferSpacesAvailable(gdb_glue.rx_stream);
bool can_receive = true;
if(max_len <= 0) {
gdb_glue.rx_stream_full = true;
ESP_LOGE(TAG, "Stream is full");
can_receive = false;
};
return can_receive;
}
size_t gdb_glue_get_packet_size() {
return GDB_RX_PACKET_MAX_SIZE;
}
void gdb_glue_init(void) {
gdb_glue.rx_stream = xStreamBufferCreate(GDB_RX_BUFFER_SIZE, 1);
gdb_glue.rx_stream_full = false;
gdb_glue.tx_buffer_index = 0;
}
unsigned char gdb_if_getchar_to(int timeout) {
uint8_t data;
size_t received = xStreamBufferReceive(gdb_glue.rx_stream, &data, sizeof(uint8_t), timeout);
if(received == 0) {
return -1;
}
if(gdb_glue.rx_stream_full &&
xStreamBufferSpacesAvailable(gdb_glue.rx_stream) >= GDB_RX_PACKET_MAX_SIZE) {
gdb_glue.rx_stream_full = false;
ESP_LOGW(TAG, "Stream freed");
}
return data;
}
unsigned char gdb_if_getchar(void) {
const uint8_t buffer_size = 1;
uint8_t buffer[buffer_size];
size_t cnt = usb_cdc_rx(buffer, buffer_size);
if(cnt) {
return buffer[buffer_size - 1];
} else {
return -1;
}
return gdb_if_getchar_to(portMAX_DELAY);
}
void gdb_if_putchar(unsigned char c, int flush) {
usb_cdc_tx_char(c, flush);
if(network_gdb_connected()) {
gdb_glue.tx_buffer[gdb_glue.tx_buffer_index] = c;
gdb_glue.tx_buffer_index++;
if(gdb_glue.tx_buffer_index == GDB_TX_BUFFER_SIZE || flush) {
network_gdb_send(gdb_glue.tx_buffer, gdb_glue.tx_buffer_index);
gdb_glue.tx_buffer_index = 0;
}
} else {
// Not sure why, but I could not get it to work with buffer
usb_cdc_tx_char(c, flush);
}
}

View File

@ -0,0 +1,33 @@
#pragma once
#include <stdlib.h>
#include <stdint.h>
/**
* Init gdb stream glue
*/
void gdb_glue_init(void);
/**
* Get free size of rx stream
* @return size_t
*/
size_t gdb_glue_get_free_size(void);
/**
* Put data to rx stream
* @param buffer data
* @param size data size
*/
void gdb_glue_receive(uint8_t* buffer, size_t size);
/**
*
* @return bool
*/
bool gdb_glue_can_receive();
/**
* Get gdb packet size
* @return size_t
*/
size_t gdb_glue_get_packet_size();

View File

@ -9,6 +9,7 @@ set(SOURCES
"network.c"
"network-http.c"
"network-uart.c"
"network-gdb.c"
)
set(INCLUDES

View File

@ -13,6 +13,9 @@
#include "network.h"
#include "network-http.h"
#include "network-uart.h"
#include "network-gdb.h"
#include <gdb-glue.h>
static const char* TAG = "main";
@ -49,6 +52,8 @@ void tcp_web_log(void);
void app_main(void) {
ESP_LOGI(TAG, "start");
gdb_glue_init();
led_init();
led_set_blue(255);
@ -56,6 +61,7 @@ void app_main(void) {
network_init();
network_http_server_init();
network_uart_server_init();
network_gdb_server_init();
usb_cdc_init();

170
main/network-gdb.c Normal file
View File

@ -0,0 +1,170 @@
#include <string.h>
#include <sys/param.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/stream_buffer.h>
#include <esp_system.h>
#include <esp_wifi.h>
#include <esp_event.h>
#include <esp_log.h>
#include <nvs_flash.h>
#include <esp_netif.h>
#include <lwip/err.h>
#include <lwip/sockets.h>
#include <lwip/sys.h>
#include <lwip/netdb.h>
#include "led.h"
#include "delay.h"
#include "network-gdb.h"
#include <gdb-glue.h>
#define PORT 2345
#define KEEPALIVE_IDLE 5
#define KEEPALIVE_INTERVAL 5
#define KEEPALIVE_COUNT 3
#define TAG "network-gdb"
typedef struct {
bool connected;
int socket_id;
} NetworkGDB;
static NetworkGDB network_gdb;
bool network_gdb_connected(void) {
return network_gdb.connected;
}
void network_gdb_send(uint8_t* buffer, size_t size) {
int to_write = size;
while(to_write > 0) {
int written = send(network_gdb.socket_id, buffer + (size - to_write), to_write, 0);
to_write -= written;
}
};
void receive_and_send_to_gdb(void) {
size_t rx_size = SIZE_MAX;
size_t gdb_packet_size = gdb_glue_get_packet_size();
uint8_t* buffer_rx = malloc(gdb_packet_size);
do {
if(gdb_glue_can_receive()) {
size_t max_len = gdb_glue_get_free_size();
if(max_len > gdb_packet_size) max_len = gdb_packet_size;
rx_size = recv(network_gdb.socket_id, buffer_rx, max_len, 0);
if(rx_size > 0) {
gdb_glue_receive(buffer_rx, rx_size);
}
} else {
delay(10);
}
} while(rx_size > 0);
free(buffer_rx);
}
static void network_gdb_server_task(void* pvParameters) {
char addr_str[128];
int addr_family = (int)pvParameters;
int ip_protocol = 0;
int keepAlive = 1;
int keepIdle = KEEPALIVE_IDLE;
int keepInterval = KEEPALIVE_INTERVAL;
int keepCount = KEEPALIVE_COUNT;
network_gdb.connected = false;
struct sockaddr_storage dest_addr;
if(addr_family == AF_INET) {
struct sockaddr_in* dest_addr_ip4 = (struct sockaddr_in*)&dest_addr;
dest_addr_ip4->sin_addr.s_addr = htonl(INADDR_ANY);
dest_addr_ip4->sin_family = AF_INET;
dest_addr_ip4->sin_port = htons(PORT);
ip_protocol = IPPROTO_IP;
}
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
if(listen_sock < 0) {
ESP_LOGE(TAG, "Unable to create socket: errno %d", errno);
vTaskDelete(NULL);
return;
}
int opt = 1;
setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
ESP_LOGI(TAG, "Socket created");
int err = bind(listen_sock, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
if(err != 0) {
ESP_LOGE(TAG, "Socket unable to bind: errno %d", errno);
ESP_LOGE(TAG, "IPPROTO: %d", addr_family);
goto CLEAN_UP;
}
ESP_LOGI(TAG, "Socket bound, port %d", PORT);
err = listen(listen_sock, 1);
if(err != 0) {
ESP_LOGE(TAG, "Error occurred during listen: errno %d", errno);
goto CLEAN_UP;
}
while(1) {
ESP_LOGI(TAG, "Socket listening");
struct sockaddr_storage source_addr; // Large enough for both IPv4 or IPv6
socklen_t addr_len = sizeof(source_addr);
int sock = accept(listen_sock, (struct sockaddr*)&source_addr, &addr_len);
if(sock < 0) {
ESP_LOGE(TAG, "Unable to accept connection: errno %d", errno);
break;
}
// Set tcp keepalive option
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, &keepAlive, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &keepIdle, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keepIdle, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keepInterval, sizeof(int));
setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keepCount, sizeof(int));
// Convert ip address to string
if(source_addr.ss_family == PF_INET) {
inet_ntoa_r(
((struct sockaddr_in*)&source_addr)->sin_addr, addr_str, sizeof(addr_str) - 1);
}
ESP_LOGI(TAG, "Socket accepted ip address: %s", addr_str);
led_set_blue(255);
delay(10);
led_set_blue(0);
network_gdb.socket_id = sock;
network_gdb.connected = true;
receive_and_send_to_gdb();
network_gdb.connected = false;
network_gdb.socket_id = -1;
led_set_blue(255);
delay(10);
led_set_blue(0);
shutdown(sock, 0);
close(sock);
}
CLEAN_UP:
close(listen_sock);
vTaskDelete(NULL);
}
void network_gdb_server_init(void) {
network_gdb.connected = false;
network_gdb.socket_id = -1;
esp_wifi_set_ps(WIFI_PS_NONE);
xTaskCreate(network_gdb_server_task, "network_gdb_server", 4096, (void*)AF_INET, 5, NULL);
}

29
main/network-gdb.h Normal file
View File

@ -0,0 +1,29 @@
/**
* @file network-gdb.h
* @author Sergey Gavrilov (who.just.the.doctor@gmail.com)
* @version 1.0
* @date 2021-11-25
*
* GDB server API
*/
#pragma once
#include <stdint.h>
/**
* Start GDB server
*/
void network_gdb_server_init(void);
/**
* Checks if someone is connected to the GDB server
* @return bool
*/
bool network_gdb_connected(void);
/**
* Send data
* @param buffer data
* @param size data size
*/
void network_gdb_send(uint8_t* buffer, size_t size);

View File

@ -14,90 +14,63 @@
#include <tinyusb.h>
#include <tusb_cdc_acm.h>
#include <sdkconfig.h>
#include <driver/gpio.h>
#include "usb-cdc.h"
#include "led.h"
#include "delay.h"
#include <driver/gpio.h>
#include <gdb-glue.h>
#define USB_DN_PIN (19)
#define USB_DP_PIN (20)
#define CDC_USB_DEV (TINYUSB_USBDEV_0)
static const char* TAG = "usb-cdc";
static uint8_t buffer_rx[CONFIG_USB_CDC_RX_BUFSIZE + 1];
static uint8_t buffer_rx[CONFIG_USB_CDC_RX_BUFSIZE];
typedef struct {
volatile bool connected;
volatile bool dtr;
volatile bool rts;
} USBCDC;
StreamBufferHandle_t rx_stream;
volatile bool rx_stream_full;
} FuriHalVcp;
static FuriHalVcp furi_hal_vcp;
size_t usb_cdc_rx(uint8_t* buffer, size_t size) {
return usb_cdc_rx_with_timeout(buffer, size, portMAX_DELAY);
}
size_t usb_cdc_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout) {
size_t received = xStreamBufferReceive(furi_hal_vcp.rx_stream, buffer, size, timeout);
if(furi_hal_vcp.rx_stream_full &&
xStreamBufferSpacesAvailable(furi_hal_vcp.rx_stream) >= CONFIG_USB_CDC_RX_BUFSIZE) {
furi_hal_vcp.rx_stream_full = false;
ESP_LOGW(TAG, "Stream freed");
}
return received;
}
static USBCDC usb_cdc;
void usb_cdc_tx_char(uint8_t c, bool flush) {
// TinyUSB implements buffering, no buffering is required here
tinyusb_cdcacm_write_queue(TINYUSB_USBDEV_0, &c, 1);
tinyusb_cdcacm_write_queue(CDC_USB_DEV, &c, 1);
if(flush) {
// SOME GDB MAGIC
// We need to send an empty packet for some hosts to accept this as a complete transfer.
uint8_t zero_byte = 0;
tinyusb_cdcacm_write_queue(TINYUSB_USBDEV_0, &zero_byte, 1);
tinyusb_cdcacm_write_queue(CDC_USB_DEV, &zero_byte, 1);
// TODO: timeout size
ESP_ERROR_CHECK_WITHOUT_ABORT(tinyusb_cdcacm_write_flush(TINYUSB_USBDEV_0, 1000));
ESP_ERROR_CHECK_WITHOUT_ABORT(tinyusb_cdcacm_write_flush(CDC_USB_DEV, 1000));
}
}
void usb_cdc_rx_callback(int itf, cdcacm_event_t* event) {
uint16_t max_len = xStreamBufferSpacesAvailable(furi_hal_vcp.rx_stream);
if(max_len > 0) {
if(gdb_glue_can_receive()) {
size_t max_len = gdb_glue_get_free_size();
if(max_len > CONFIG_USB_CDC_RX_BUFSIZE) max_len = CONFIG_USB_CDC_RX_BUFSIZE;
size_t rx_size = 0;
esp_err_t err = tinyusb_cdcacm_read(itf, buffer_rx, max_len, &rx_size);
if(err == ESP_OK) {
if(rx_size > 0) {
size_t ret =
xStreamBufferSend(furi_hal_vcp.rx_stream, buffer_rx, rx_size, portMAX_DELAY);
ESP_ERROR_CHECK(ret != rx_size);
// buffer_rx[rx_size] = '\0';
// printf("%s", buffer_rx);
gdb_glue_receive(buffer_rx, rx_size);
}
} else {
ESP_LOGE(TAG, "Read error");
}
} else {
furi_hal_vcp.rx_stream_full = true;
ESP_LOGE(TAG, "Stream is full");
};
}
}
void usb_cdc_line_state_changed_callback(int itf, cdcacm_event_t* event) {
furi_hal_vcp.dtr = event->line_state_changed_data.dtr;
furi_hal_vcp.rts = event->line_state_changed_data.rts;
usb_cdc.dtr = event->line_state_changed_data.dtr;
usb_cdc.rts = event->line_state_changed_data.rts;
ESP_LOGI(TAG, "Line state changed! dtr:%d, rst:%d", furi_hal_vcp.dtr, furi_hal_vcp.rts);
ESP_LOGI(TAG, "Line state changed! dtr:%d, rst:%d", usb_cdc.dtr, usb_cdc.rts);
}
void usb_cdc_line_coding_changed_callback(int itf, cdcacm_event_t* event) {
@ -123,55 +96,53 @@ void usb_cdc_line_coding_changed_callback(int itf, cdcacm_event_t* event) {
void tud_mount_cb(void) {
ESP_LOGI(TAG, "Mount");
if(!furi_hal_vcp.connected) {
if(!usb_cdc.connected) {
led_set_blue(255);
delay(10);
led_set_blue(0);
}
furi_hal_vcp.connected = true;
usb_cdc.connected = true;
}
void tud_umount_cb(void) {
ESP_LOGI(TAG, "Unmount");
if(furi_hal_vcp.connected) {
if(usb_cdc.connected) {
led_set_blue(255);
delay(10);
led_set_blue(0);
}
furi_hal_vcp.connected = false;
usb_cdc.connected = false;
}
void tud_resume_cb(void) {
ESP_LOGI(TAG, "Resume");
if(furi_hal_vcp.connected) {
if(usb_cdc.connected) {
led_set_blue(255);
delay(10);
led_set_blue(0);
}
furi_hal_vcp.connected = true;
usb_cdc.connected = true;
}
void tud_suspend_cb(bool remote_wakeup_en) {
ESP_LOGI(TAG, "Suspend");
if(furi_hal_vcp.connected) {
if(usb_cdc.connected) {
led_set_blue(255);
delay(10);
led_set_blue(0);
}
furi_hal_vcp.connected = false;
usb_cdc.connected = false;
}
void usb_cdc_init(void) {
furi_hal_vcp.connected = false;
furi_hal_vcp.rx_stream = xStreamBufferCreate((CONFIG_USB_CDC_RX_BUFSIZE * 16), 1);
furi_hal_vcp.rx_stream_full = false;
usb_cdc.connected = false;
ESP_LOGI(TAG, "init");
@ -198,7 +169,7 @@ void usb_cdc_init(void) {
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
tinyusb_config_cdcacm_t amc_cfg = {
.usb_dev = TINYUSB_USBDEV_0,
.usb_dev = CDC_USB_DEV,
.cdc_port = TINYUSB_CDC_ACM_0,
.rx_unread_buf_sz = 64,
.callback_rx = &usb_cdc_rx_callback,

View File

@ -1,7 +1,14 @@
#pragma once
/**
* Init usb subsystem
*/
void usb_cdc_init(void);
size_t usb_cdc_rx(uint8_t* buffer, size_t size);
size_t usb_cdc_rx_with_timeout(uint8_t* buffer, size_t size, uint32_t timeout);
void usb_cdc_tx_char(uint8_t c, bool flush);
/**
* Send data
* @param buffer data
* @param size data size
* @param flush
*/
void usb_cdc_send(uint8_t* buffer, size_t size, bool flush);