Skip to content

Commit 51bae98

Browse files
committed
Merge branch 'bugfix/uart_vfs_read_behavior_v5.4' into 'release/v5.4'
fix(uart_vfs): read() now aligned to POSIX defined behavior (v5.4) See merge request espressif/esp-idf!35392
2 parents 2ad251b + 43175ff commit 51bae98

File tree

2 files changed

+182
-40
lines changed

2 files changed

+182
-40
lines changed

components/esp_driver_uart/src/uart_vfs.c

Lines changed: 79 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,18 @@
5353
typedef void (*tx_func_t)(int, int);
5454
// UART read bytes function type
5555
typedef int (*rx_func_t)(int);
56+
// UART get available received bytes function type
57+
typedef size_t (*get_available_data_len_func_t)(int);
5658

57-
// Basic functions for sending and receiving bytes over UART
59+
// Basic functions for sending, receiving bytes, and get available data length over UART
5860
static void uart_tx_char(int fd, int c);
5961
static int uart_rx_char(int fd);
62+
static size_t uart_get_avail_data_len(int fd);
6063

61-
// Functions for sending and receiving bytes which use UART driver
64+
// Functions for sending, receiving bytes, and get available data length which use UART driver
6265
static void uart_tx_char_via_driver(int fd, int c);
6366
static int uart_rx_char_via_driver(int fd);
67+
static size_t uart_get_avail_data_len_via_driver(int fd);
6468

6569
typedef struct {
6670
// Pointers to UART peripherals
@@ -82,6 +86,8 @@ typedef struct {
8286
tx_func_t tx_func;
8387
// Functions used to read bytes from UART. Default to "basic" functions.
8488
rx_func_t rx_func;
89+
// Function used to get available data bytes from UART. Default to "basic" functions.
90+
get_available_data_len_func_t get_avail_data_len_func;
8591
} uart_vfs_context_t;
8692

8793
#define VFS_CTX_DEFAULT_VAL(uart_dev) (uart_vfs_context_t) {\
@@ -91,6 +97,7 @@ typedef struct {
9197
.rx_mode = DEFAULT_RX_MODE,\
9298
.tx_func = uart_tx_char,\
9399
.rx_func = uart_rx_char,\
100+
.get_avail_data_len_func = uart_get_avail_data_len,\
94101
}
95102

96103
//If the context should be dynamically initialized, remove this structure
@@ -162,6 +169,19 @@ static int uart_open(const char *path, int flags, int mode)
162169
return fd;
163170
}
164171

172+
size_t uart_get_avail_data_len(int fd)
173+
{
174+
uart_dev_t* uart = s_ctx[fd]->uart;
175+
return uart_ll_get_rxfifo_len(uart);
176+
}
177+
178+
size_t uart_get_avail_data_len_via_driver(int fd)
179+
{
180+
size_t buffered_size = 0;
181+
uart_get_buffered_data_len(fd, &buffered_size);
182+
return buffered_size;
183+
}
184+
165185
static void uart_tx_char(int fd, int c)
166186
{
167187
uart_dev_t* uart = s_ctx[fd]->uart;
@@ -253,38 +273,65 @@ static ssize_t uart_read(int fd, void* data, size_t size)
253273
assert(fd >= 0 && fd < 3);
254274
char *data_c = (char *) data;
255275
size_t received = 0;
276+
size_t available_size = 0;
277+
int c = NONE; // store the read char
256278
_lock_acquire_recursive(&s_ctx[fd]->read_lock);
257-
while (received < size) {
258-
int c = uart_read_char(fd);
259-
if (c == '\r') {
260-
if (s_ctx[fd]->rx_mode == ESP_LINE_ENDINGS_CR) {
261-
c = '\n';
262-
} else if (s_ctx[fd]->rx_mode == ESP_LINE_ENDINGS_CRLF) {
263-
/* look ahead */
264-
int c2 = uart_read_char(fd);
265-
if (c2 == NONE) {
266-
/* could not look ahead, put the current character back */
267-
uart_return_char(fd, c);
268-
break;
269-
}
270-
if (c2 == '\n') {
271-
/* this was \r\n sequence. discard \r, return \n */
279+
280+
if (!s_ctx[fd]->non_blocking) {
281+
c = uart_read_char(fd); // blocking until data available for non-O_NONBLOCK mode
282+
}
283+
284+
// find the actual fetch size
285+
available_size += s_ctx[fd]->get_avail_data_len_func(fd);
286+
if (c != NONE) {
287+
available_size++;
288+
}
289+
if (s_ctx[fd]->peek_char != NONE) {
290+
available_size++;
291+
}
292+
size_t fetch_size = MIN(available_size, size);
293+
294+
if (fetch_size > 0) {
295+
do {
296+
if (c == NONE) { // for non-O_NONBLOCK mode, there is already a pre-fetched char
297+
c = uart_read_char(fd);
298+
}
299+
assert(c != NONE);
300+
301+
if (c == '\r') {
302+
if (s_ctx[fd]->rx_mode == ESP_LINE_ENDINGS_CR) {
272303
c = '\n';
273-
} else {
274-
/* \r followed by something else. put the second char back,
275-
* it will be processed on next iteration. return \r now.
276-
*/
277-
uart_return_char(fd, c2);
304+
} else if (s_ctx[fd]->rx_mode == ESP_LINE_ENDINGS_CRLF) {
305+
/* look ahead */
306+
int c2 = uart_read_char(fd);
307+
fetch_size--;
308+
if (c2 == NONE) {
309+
/* could not look ahead, put the current character back */
310+
uart_return_char(fd, c);
311+
c = NONE;
312+
break;
313+
}
314+
if (c2 == '\n') {
315+
/* this was \r\n sequence. discard \r, return \n */
316+
c = '\n';
317+
} else {
318+
/* \r followed by something else. put the second char back,
319+
* it will be processed on next iteration. return \r now.
320+
*/
321+
uart_return_char(fd, c2);
322+
fetch_size++;
323+
}
278324
}
279325
}
280-
} else if (c == NONE) {
281-
break;
282-
}
283-
data_c[received] = (char) c;
284-
++received;
285-
if (c == '\n') {
286-
break;
287-
}
326+
327+
data_c[received] = (char) c;
328+
++received;
329+
c = NONE;
330+
} while (received < fetch_size);
331+
}
332+
333+
if (c != NONE) { // fetched, but not used
334+
uart_return_char(fd, c);
288335
}
289336
_lock_release_recursive(&s_ctx[fd]->read_lock);
290337
if (received > 0) {
@@ -1078,6 +1125,7 @@ void uart_vfs_dev_use_nonblocking(int uart_num)
10781125
_lock_acquire_recursive(&s_ctx[uart_num]->write_lock);
10791126
s_ctx[uart_num]->tx_func = uart_tx_char;
10801127
s_ctx[uart_num]->rx_func = uart_rx_char;
1128+
s_ctx[uart_num]->get_avail_data_len_func = uart_get_avail_data_len;
10811129
_lock_release_recursive(&s_ctx[uart_num]->write_lock);
10821130
_lock_release_recursive(&s_ctx[uart_num]->read_lock);
10831131
}
@@ -1088,6 +1136,7 @@ void uart_vfs_dev_use_driver(int uart_num)
10881136
_lock_acquire_recursive(&s_ctx[uart_num]->write_lock);
10891137
s_ctx[uart_num]->tx_func = uart_tx_char_via_driver;
10901138
s_ctx[uart_num]->rx_func = uart_rx_char_via_driver;
1139+
s_ctx[uart_num]->get_avail_data_len_func = uart_get_avail_data_len_via_driver;
10911140
_lock_release_recursive(&s_ctx[uart_num]->write_lock);
10921141
_lock_release_recursive(&s_ctx[uart_num]->read_lock);
10931142
}

components/esp_driver_uart/test_apps/uart_vfs/main/test_vfs_uart.c

Lines changed: 103 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ TEST_CASE("CRs are removed from the stdin correctly", "[vfs_uart]")
7474
uart_vfs_dev_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
7575

7676
flush_stdin_stdout();
77+
78+
// A test case with no use of uart driver
79+
// For non-uart-driver-involved uart vfs, all reads are non-blocking
80+
// If no data at the moment, read() returns directly;
81+
// If there is data available at the moment, read() also returns directly with the currently available size
82+
7783
const char* send_str = "1234567890\n\r123\r\n4\n";
7884
/* with CONFIG_NEWLIB_STDOUT_ADDCR, the following will be sent on the wire.
7985
* (last character of each part is marked with a hat)
@@ -133,30 +139,46 @@ struct read_task_arg_t {
133139

134140
struct write_task_arg_t {
135141
const char* str;
142+
size_t str_len;
136143
SemaphoreHandle_t done;
137144
};
138145

139-
static void read_task_fn(void* varg)
146+
static void read_blocking_task_fn(void* varg)
140147
{
141148
struct read_task_arg_t* parg = (struct read_task_arg_t*) varg;
142-
parg->out_buffer[0] = 0;
149+
memset(parg->out_buffer, 0, parg->out_buffer_len);
143150

144151
fgets(parg->out_buffer, parg->out_buffer_len, stdin);
145152
xSemaphoreGive(parg->done);
146153
vTaskDelete(NULL);
147154
}
148155

156+
static void read_non_blocking_task_fn(void* varg)
157+
{
158+
struct read_task_arg_t* parg = (struct read_task_arg_t*) varg;
159+
memset(parg->out_buffer, 0, parg->out_buffer_len);
160+
char *ptr = parg->out_buffer;
161+
162+
while (fgets(ptr, parg->out_buffer_len, stdin) != NULL) {
163+
while (*ptr != 0) {
164+
ptr++;
165+
}
166+
}
167+
xSemaphoreGive(parg->done);
168+
vTaskDelete(NULL);
169+
}
170+
149171
static void write_task_fn(void* varg)
150172
{
151173
struct write_task_arg_t* parg = (struct write_task_arg_t*) varg;
152-
fwrite_str_loopback(parg->str, strlen(parg->str));
174+
fwrite_str_loopback(parg->str, parg->str_len);
153175
xSemaphoreGive(parg->done);
154176
vTaskDelete(NULL);
155177
}
156178

157-
TEST_CASE("can write to UART while another task is reading", "[vfs_uart]")
179+
TEST_CASE("read with uart driver (blocking)", "[vfs_uart]")
158180
{
159-
char out_buffer[32];
181+
char out_buffer[32] = {};
160182
size_t out_buffer_len = sizeof(out_buffer);
161183

162184
struct read_task_arg_t read_arg = {
@@ -165,8 +187,12 @@ TEST_CASE("can write to UART while another task is reading", "[vfs_uart]")
165187
.done = xSemaphoreCreateBinary()
166188
};
167189

190+
// Send a string with length less than the read requested length
191+
const char in_buffer[] = "!(@*#&(!*@&#((SDasdkjhadsl\n";
192+
size_t in_buffer_len = sizeof(in_buffer);
168193
struct write_task_arg_t write_arg = {
169-
.str = "!(@*#&(!*@&#((SDasdkjhadsl\n",
194+
.str = in_buffer,
195+
.str_len = in_buffer_len,
170196
.done = xSemaphoreCreateBinary()
171197
};
172198

@@ -176,14 +202,18 @@ TEST_CASE("can write to UART while another task is reading", "[vfs_uart]")
176202
256, 0, 0, NULL, 0));
177203
uart_vfs_dev_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
178204

179-
xTaskCreate(&read_task_fn, "vfs_read", 4096, &read_arg, 5, NULL);
180-
vTaskDelay(10);
205+
// Start the read task first, it will block until data incoming
206+
xTaskCreate(&read_blocking_task_fn, "vfs_read", 4096, &read_arg, 5, NULL);
207+
208+
int res = xSemaphoreTake(read_arg.done, 100 / portTICK_PERIOD_MS);
209+
TEST_ASSERT_FALSE(res);
210+
181211
xTaskCreate(&write_task_fn, "vfs_write", 4096, &write_arg, 6, NULL);
182212

183-
int res = xSemaphoreTake(write_arg.done, 100 / portTICK_PERIOD_MS);
213+
res = xSemaphoreTake(write_arg.done, 100 / portTICK_PERIOD_MS);
184214
TEST_ASSERT(res);
185215

186-
res = xSemaphoreTake(read_arg.done, 100 / portTICK_PERIOD_MS);
216+
res = xSemaphoreTake(read_arg.done, 100 / portTICK_PERIOD_MS); // read() returns with currently available size
187217
TEST_ASSERT(res);
188218

189219
TEST_ASSERT_EQUAL(0, strcmp(write_arg.str, read_arg.out_buffer));
@@ -195,6 +225,69 @@ TEST_CASE("can write to UART while another task is reading", "[vfs_uart]")
195225
vTaskDelay(2); // wait for tasks to exit
196226
}
197227

228+
TEST_CASE("read with uart driver (non-blocking)", "[vfs_uart]")
229+
{
230+
char out_buffer[32] = {};
231+
size_t out_buffer_len = sizeof(out_buffer);
232+
233+
struct read_task_arg_t read_arg = {
234+
.out_buffer = out_buffer,
235+
.out_buffer_len = out_buffer_len,
236+
.done = xSemaphoreCreateBinary()
237+
};
238+
239+
// Send a string with length less than the read requested length
240+
const char in_buffer[] = "!(@*#&(!*@&#((SDasdkjhad\nce"; // read should not early return on \n
241+
size_t in_buffer_len = sizeof(in_buffer);
242+
struct write_task_arg_t write_arg = {
243+
.str = in_buffer,
244+
.str_len = in_buffer_len,
245+
.done = xSemaphoreCreateBinary()
246+
};
247+
248+
flush_stdin_stdout();
249+
250+
ESP_ERROR_CHECK(uart_driver_install(CONFIG_ESP_CONSOLE_UART_NUM,
251+
256, 0, 0, NULL, 0));
252+
uart_vfs_dev_use_driver(CONFIG_ESP_CONSOLE_UART_NUM);
253+
254+
uart_vfs_dev_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_LF);
255+
uart_vfs_dev_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_LF);
256+
257+
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
258+
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
259+
260+
// If start the read task first, it will return immediately
261+
xTaskCreate(&read_non_blocking_task_fn, "vfs_read", 4096, &read_arg, 5, NULL);
262+
263+
int res = xSemaphoreTake(read_arg.done, 100 / portTICK_PERIOD_MS);
264+
TEST_ASSERT(res);
265+
266+
xTaskCreate(&write_task_fn, "vfs_write", 4096, &write_arg, 6, NULL);
267+
vTaskDelay(10);
268+
xTaskCreate(&read_non_blocking_task_fn, "vfs_read", 4096, &read_arg, 5, NULL);
269+
270+
res = xSemaphoreTake(write_arg.done, 100 / portTICK_PERIOD_MS);
271+
TEST_ASSERT(res);
272+
273+
res = xSemaphoreTake(read_arg.done, 1000 / portTICK_PERIOD_MS); // read() returns with currently available size
274+
TEST_ASSERT(res);
275+
276+
// string compare
277+
for (int i = 0; i < in_buffer_len; i++) {
278+
TEST_ASSERT_EQUAL(in_buffer[i], out_buffer[i]);
279+
}
280+
281+
uart_vfs_dev_use_nonblocking(CONFIG_ESP_CONSOLE_UART_NUM);
282+
fcntl(STDIN_FILENO, F_SETFL, flags);
283+
uart_vfs_dev_port_set_rx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
284+
uart_vfs_dev_port_set_tx_line_endings(CONFIG_ESP_CONSOLE_UART_NUM, ESP_LINE_ENDINGS_CRLF);
285+
uart_driver_delete(CONFIG_ESP_CONSOLE_UART_NUM);
286+
vSemaphoreDelete(read_arg.done);
287+
vSemaphoreDelete(write_arg.done);
288+
vTaskDelay(2); // wait for tasks to exit
289+
}
290+
198291
TEST_CASE("fcntl supported in UART VFS", "[vfs_uart]")
199292
{
200293
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);

0 commit comments

Comments
 (0)