Glue USB and net socket to GDB thread
This commit is contained in:
parent
c8427b1cf8
commit
027bd85bd6
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
|
@ -9,6 +9,7 @@ set(SOURCES
|
|||
"network.c"
|
||||
"network-http.c"
|
||||
"network-uart.c"
|
||||
"network-gdb.c"
|
||||
)
|
||||
|
||||
set(INCLUDES
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
Loading…
Reference in New Issue