Skip to content

Commit 32c17b4

Browse files
committed
fix(uart_vfs): read() now aligned to POSIX defined behavior
- For blocking mode, block until data available - Return with the bytes available in the file at the time, it should not block until reaching the requested size And read() should not realy return on the newline character Closes espressif#14155
1 parent 4143156 commit 32c17b4

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) {
@@ -1061,6 +1108,7 @@ void uart_vfs_dev_use_nonblocking(int uart_num)
10611108
_lock_acquire_recursive(&s_ctx[uart_num]->write_lock);
10621109
s_ctx[uart_num]->tx_func = uart_tx_char;
10631110
s_ctx[uart_num]->rx_func = uart_rx_char;
1111+
s_ctx[uart_num]->get_avail_data_len_func = uart_get_avail_data_len;
10641112
_lock_release_recursive(&s_ctx[uart_num]->write_lock);
10651113
_lock_release_recursive(&s_ctx[uart_num]->read_lock);
10661114
}
@@ -1071,6 +1119,7 @@ void uart_vfs_dev_use_driver(int uart_num)
10711119
_lock_acquire_recursive(&s_ctx[uart_num]->write_lock);
10721120
s_ctx[uart_num]->tx_func = uart_tx_char_via_driver;
10731121
s_ctx[uart_num]->rx_func = uart_rx_char_via_driver;
1122+
s_ctx[uart_num]->get_avail_data_len_func = uart_get_avail_data_len_via_driver;
10741123
_lock_release_recursive(&s_ctx[uart_num]->write_lock);
10751124
_lock_release_recursive(&s_ctx[uart_num]->read_lock);
10761125
}

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)