Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for the USB CDC Serial JTAG console #3536

Open
wants to merge 6 commits into
base: dev-esp32
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
278 changes: 216 additions & 62 deletions components/platform/platform.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@
#include "task/task.h"
#include "linput.h"

#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
#include "driver/usb_serial_jtag.h"
#include "esp_vfs_usb_serial_jtag.h"
#include "esp_vfs_dev.h"
#include <fcntl.h>
#include <errno.h>
#endif

int platform_init (void)
{
platform_ws2812_init();
Expand All @@ -34,6 +42,16 @@ int platform_gpio_output_exists( unsigned gpio ) { return GPIO_IS_VALID_OUTPUT_G
#define PLATFORM_UART_EVENT_RX (UART_EVENT_MAX + 3)
#define PLATFORM_UART_EVENT_BREAK (UART_EVENT_MAX + 4)

#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
#define ADJUST_IDR(id,retval) if (id-- == 0) return retval
#define ADJUST_ID(id) if (id-- == 0) return
#define UNADJUST_ID(id) id++
#else
#define ADJUST_IDR(id, retval)
#define ADJUST_ID(id)
#define UNADJUST_ID(id)
#endif

typedef struct {
unsigned rx_buf_sz;
unsigned tx_buf_sz;
Expand Down Expand Up @@ -65,65 +83,126 @@ uart_status_t uart_status[NUM_UART];
task_handle_t uart_event_task_id = 0;
SemaphoreHandle_t sem = NULL;

#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
task_handle_t usbcdc_event_task_id = 0;
static uart_status_t usbcdc_status;
SemaphoreHandle_t usbcdc_sem = NULL;
#endif

extern bool uart_has_on_data_cb(unsigned id);
extern bool uart_on_data_cb(unsigned id, const char *buf, size_t len);
extern bool uart_on_error_cb(unsigned id, const char *buf, size_t len);

void uart_event_task( task_param_t param, task_prio_t prio ) {
static void handle_uart_data(unsigned int id, uart_event_post_t *post, uart_status_t *us) {
if (id == CONFIG_ESP_CONSOLE_UART_NUM && run_input) {
size_t i = 0;
while (i < post->size) {
unsigned used = feed_lua_input(post->data + i, post->size - i);
i += used;
}
}
if (uart_has_on_data_cb(id)) {
size_t i = 0;
while (i < post->size)
{
char ch = post->data[i];
us->line_buffer[us->line_position] = ch;
us->line_position++;

uint16_t need_len = us->need_len;
int16_t end_char = us->end_char;
size_t max_wanted =
(end_char >= 0 && need_len == 0) ? LUA_MAXINPUT : need_len;
bool at_end = (us->line_position >= max_wanted);
bool end_char_found =
(end_char >= 0 && (uint8_t)ch == (uint8_t)end_char);
if (at_end || end_char_found) {
uart_on_data_cb(id, us->line_buffer, us->line_position);
us->line_position = 0;
}
++i;
}
}
}

void uart_event_task(task_param_t param, task_prio_t prio)
{
uart_event_post_t *post = (uart_event_post_t *)param;
unsigned id = post->id;
uart_status_t *us = &uart_status[id];
UNADJUST_ID(id);
xSemaphoreGive(sem);
if(post->type == PLATFORM_UART_EVENT_DATA) {
if (id == CONFIG_ESP_CONSOLE_UART_NUM && run_input) {
size_t i = 0;
while (i < post->size)
{
unsigned used = feed_lua_input(post->data + i, post->size - i);
i += used;
}
}
if (uart_has_on_data_cb(id)) {
size_t i = 0;
while (i < post->size)
{
char ch = post->data[i];
us->line_buffer[us->line_position] = ch;
us->line_position++;

uint16_t need_len = us->need_len;
int16_t end_char = us->end_char;
size_t max_wanted =
(end_char >= 0 && need_len == 0) ? LUA_MAXINPUT : need_len;
bool at_end = (us->line_position >= max_wanted);
bool end_char_found =
(end_char >= 0 && (uint8_t)ch == (uint8_t)end_char);
if (at_end || end_char_found) {
uart_on_data_cb(id, us->line_buffer, us->line_position);
us->line_position = 0;
}
++i;
}
}
if (post->type == PLATFORM_UART_EVENT_DATA)
{
handle_uart_data(id, post, us);
free(post->data);
} else {
}
else
{
const char *err;
switch(post->type) {
case PLATFORM_UART_EVENT_OOM:
err = "out_of_memory";
break;
case PLATFORM_UART_EVENT_BREAK:
err = "break";
break;
case PLATFORM_UART_EVENT_RX:
default:
err = "rx_error";
switch (post->type)
{
case PLATFORM_UART_EVENT_OOM:
err = "out_of_memory";
break;
case PLATFORM_UART_EVENT_BREAK:
err = "break";
break;
case PLATFORM_UART_EVENT_RX:
default:
err = "rx_error";
}
uart_on_error_cb(id, err, strlen(err));
}
free(post);
}

#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
void usbcdc_event_task(task_param_t param, task_prio_t prio)
{
uart_event_post_t *post = (uart_event_post_t *)param;
xSemaphoreGive(usbcdc_sem);
handle_uart_data(0, post, &usbcdc_status);
free(post);
}

static void task_usbcdc(void *pvParameters) {
// 4 chosen as a number smaller than the number of nodemcu task slots
// available, to make it unlikely we encounter a task_post failing
if (usbcdc_sem == NULL)
usbcdc_sem = xSemaphoreCreateCounting(4, 4);

uart_event_post_t *post = NULL;

for (;;) {
if (post == NULL) {
post = (uart_event_post_t *)malloc(sizeof(uart_event_post_t) + 64 * sizeof(char));
if (post == NULL) {
ESP_LOGE(UART_TAG, "Can not alloc memory in task_usbcdc()");
// reboot here?
continue;
}
post->data = (void *)(post + 1);
post->type = PLATFORM_UART_EVENT_DATA;
}

int len = usb_serial_jtag_read_bytes(post->data, 64, portMAX_DELAY);

if (len > 0) {
post->size = len;
xSemaphoreTake(usbcdc_sem, portMAX_DELAY);
if (!task_post_medium(usbcdc_event_task_id, (task_param_t)post))
{
ESP_LOGE(UART_TAG, "Task event overrun in task_usbcdc()");
xSemaphoreGive(usbcdc_sem);
} else {
post = NULL;
}
}
}
}
#endif

static void task_uart( void *pvParameters ){
unsigned id = (unsigned)pvParameters;

Expand Down Expand Up @@ -212,6 +291,7 @@ static void task_uart( void *pvParameters ){
// pins must not be null for non-console uart
uint32_t platform_uart_setup( unsigned id, uint32_t baud, int databits, int parity, int stopbits, uart_pins_t* pins )
{
ADJUST_IDR(id, baud);
int flow_control = UART_HW_FLOWCTRL_DISABLE;
if (pins != NULL) {
if(pins->flow_control & PLATFORM_UART_FLOW_CTS) flow_control |= UART_HW_FLOWCTRL_CTS;
Expand Down Expand Up @@ -265,23 +345,25 @@ uint32_t platform_uart_setup( unsigned id, uint32_t baud, int databits, int pari

void platform_uart_setmode(unsigned id, unsigned mode)
{
uart_mode_t uartMode;

switch(mode)
{
case PLATFORM_UART_MODE_IRDA:
uartMode = UART_MODE_IRDA; break;
case PLATFORM_UART_MODE_RS485_COLLISION_DETECT:
uartMode = UART_MODE_RS485_COLLISION_DETECT; break;
case PLATFORM_UART_MODE_RS485_APP_CONTROL:
uartMode = UART_MODE_RS485_APP_CTRL; break;
case PLATFORM_UART_MODE_HALF_DUPLEX:
uartMode = UART_MODE_RS485_HALF_DUPLEX; break;
case PLATFORM_UART_MODE_UART:
default:
uartMode = UART_MODE_UART; break;
}
uart_set_mode(id, uartMode);
uart_mode_t uartMode;

ADJUST_IDR(id,);

switch(mode)
{
case PLATFORM_UART_MODE_IRDA:
uartMode = UART_MODE_IRDA; break;
case PLATFORM_UART_MODE_RS485_COLLISION_DETECT:
uartMode = UART_MODE_RS485_COLLISION_DETECT; break;
case PLATFORM_UART_MODE_RS485_APP_CONTROL:
uartMode = UART_MODE_RS485_APP_CTRL; break;
case PLATFORM_UART_MODE_HALF_DUPLEX:
uartMode = UART_MODE_RS485_HALF_DUPLEX; break;
case PLATFORM_UART_MODE_UART:
default:
uartMode = UART_MODE_UART; break;
}
uart_set_mode(id, uartMode);
}

void platform_uart_send_multi( unsigned id, const char *data, size_t len )
Expand All @@ -292,6 +374,7 @@ void platform_uart_send_multi( unsigned id, const char *data, size_t len )
putchar (data[ i ]);
}
} else {
ADJUST_ID(id);
uart_write_bytes(id, data, len);
}
}
Expand All @@ -300,16 +383,20 @@ void platform_uart_send( unsigned id, uint8_t data )
{
if (id == CONFIG_ESP_CONSOLE_UART_NUM)
putchar (data);
else
else {
ADJUST_ID(id);
uart_write_bytes(id, (const char *)&data, 1);
}
}

void platform_uart_flush( unsigned id )
{
if (id == CONFIG_ESP_CONSOLE_UART_NUM)
fflush (stdout);
else
else {
ADJUST_ID(id);
uart_tx_flush(id);
}
}


Expand All @@ -318,6 +405,58 @@ int platform_uart_start( unsigned id )
if(uart_event_task_id == 0)
uart_event_task_id = task_get_id( uart_event_task );

#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
if (id == 0) {
if (usbcdc_event_task_id == 0) {
usbcdc_event_task_id = task_get_id(usbcdc_event_task);


/* Disable buffering on stdin */
setvbuf(stdin, NULL, _IONBF, 0);

/* Minicom, screen, idf_monitor, esplorer send CR when ENTER key is pressed */
esp_vfs_dev_usb_serial_jtag_set_rx_line_endings(ESP_LINE_ENDINGS_CR);
pjsg marked this conversation as resolved.
Show resolved Hide resolved
/* Move the caret to the beginning of the next line on '\n' */
esp_vfs_dev_usb_serial_jtag_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);

/* Enable blocking mode on stdin and stdout */
fcntl(fileno(stdout), F_SETFL, 0);
fcntl(fileno(stdin), F_SETFL, 0);

usb_serial_jtag_driver_config_t usb_serial_jtag_config;
usb_serial_jtag_config.rx_buffer_size = 1024;
usb_serial_jtag_config.tx_buffer_size = 1024;

esp_err_t ret = ESP_OK;
/* Install USB-SERIAL-JTAG driver for interrupt-driven reads and writes */
ret = usb_serial_jtag_driver_install(&usb_serial_jtag_config);
if (ret != ESP_OK)
{
return -1;
}

/* Tell vfs to use usb-serial-jtag driver */
esp_vfs_usb_serial_jtag_use_driver();

usbcdc_status.line_buffer = malloc(LUA_MAXINPUT);
usbcdc_status.line_position = 0;
if(usbcdc_status.line_buffer == NULL) {
return -1;
}

const char *pcName = "usbcdc";
if(xTaskCreate(task_usbcdc, pcName, 4096, (void*)id, ESP_TASK_MAIN_PRIO + 1, &usbcdc_status.taskHandle) != pdPASS) {
free(usbcdc_status.line_buffer);
usbcdc_status.line_buffer = NULL;
return -1;
}
}
return 0;
}
#endif

ADJUST_IDR(id, -1);

uart_status_t *us = & uart_status[id];

esp_err_t ret = uart_driver_install(id, uart_buf_sz_cfg[id].rx_buf_sz, uart_buf_sz_cfg[id].tx_buf_sz, 3, & us->queue, 0);
Expand Down Expand Up @@ -349,6 +488,7 @@ void platform_uart_stop( unsigned id )
if (id == CONFIG_ESP_CONSOLE_UART_NUM)
;
else {
ADJUST_IDR(id,);
uart_status_t *us = & uart_status[id];
uart_driver_delete(id);
if(us->line_buffer) free(us->line_buffer);
Expand All @@ -361,6 +501,19 @@ void platform_uart_stop( unsigned id )
int platform_uart_get_config(unsigned id, uint32_t *baudp, uint32_t *databitsp, uint32_t *parityp, uint32_t *stopbitsp) {
int err;

#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
if (id == 0) {
// Return dummy values
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it not be more honest to just return an error code here instead of dummy values?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was nervous that it might frighten some code that thinks that it ought to be able to get the config....

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good code should be able to handle jump scares! :D

Come to think of it though, isn't the USB device accepting serial configuration settings? And shouldn't we return those (even if they aren't being used)?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It accepts settings from the PC side, but there isn't any way to get those from the ESP32 side (as far as I can tell). I think that the CDC device is hardware and just exposes a few registers to look like a UART. In particular, on the C3 I know that you cannot reprogram the USB to like anything except a CDC device. I don't think that is true for all variants of the ESP32.

*baudp = 115200;
*databitsp = 8;
*parityp = PLATFORM_UART_PARITY_NONE;
*stopbitsp = PLATFORM_UART_STOPBITS_1;
return 0;
}
#endif

ADJUST_IDR(id, -1);

err = uart_get_baudrate(id, baudp);
if (err != ESP_OK) return -1;
*baudp &= 0xFFFFFFFE; // round down
Expand Down Expand Up @@ -410,6 +563,7 @@ int platform_uart_get_config(unsigned id, uint32_t *baudp, uint32_t *databitsp,

int platform_uart_set_wakeup_threshold(unsigned id, unsigned threshold)
{
ADJUST_IDR(id, 0);
esp_err_t err = uart_set_wakeup_threshold(id, threshold);
return (err == ESP_OK) ? 0 : -1;
}
Expand Down