Skip to content

Commit 2f4c5d2

Browse files
committed
Merge branch 'feat/remove-unecessray-condition-in-usj-read' into 'master'
fix(driver): remove unecessary if conditions in the read function Closes IDF-13166 See merge request espressif/esp-idf!39523
2 parents bc34abb + 740762c commit 2f4c5d2

File tree

8 files changed

+323
-66
lines changed

8 files changed

+323
-66
lines changed

components/esp_driver_usb_serial_jtag/include/driver/usb_serial_jtag_select.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
/*
3-
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
3+
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
44
*
55
* SPDX-License-Identifier: Apache-2.0
66
*/
@@ -27,6 +27,13 @@ typedef void (*usj_select_notif_callback_t)(usj_select_notif_t usb_serial_jtag_s
2727
*/
2828
void usb_serial_jtag_set_select_notif_callback(usj_select_notif_callback_t usb_serial_jtag_select_notif_callback);
2929

30+
/**
31+
* @brief Return the number of bytes available for reading
32+
*
33+
* @return the number of bytes available for reading in the buffer
34+
*/
35+
size_t usb_serial_jtag_get_read_bytes_available(void);
36+
3037
/**
3138
* @brief Return the readiness status of the driver for read operation
3239
*

components/esp_driver_usb_serial_jtag/src/usb_serial_jtag.c

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -366,12 +366,23 @@ void usb_serial_jtag_set_select_notif_callback(usj_select_notif_callback_t usj_s
366366
}
367367
}
368368

369-
bool usb_serial_jtag_read_ready(void)
369+
size_t usb_serial_jtag_get_read_bytes_available(void)
370370
{
371371
// sign the the driver is read ready is that data is waiting in the RX ringbuffer
372-
UBaseType_t items_waiting = 0;
373-
vRingbufferGetInfo(p_usb_serial_jtag_obj->rx_ring_buf, NULL, NULL, NULL, NULL, &items_waiting);
374-
return items_waiting != 0;
372+
UBaseType_t bytes_available = 0;
373+
if (usb_serial_jtag_is_driver_installed()) {
374+
vRingbufferGetInfo(p_usb_serial_jtag_obj->rx_ring_buf, NULL, NULL, NULL, NULL, &bytes_available);
375+
if (bytes_available <= 0) {
376+
return 0;
377+
}
378+
}
379+
380+
return (size_t)bytes_available;
381+
}
382+
383+
bool usb_serial_jtag_read_ready(void)
384+
{
385+
return usb_serial_jtag_get_read_bytes_available() != 0;
375386
}
376387

377388
bool usb_serial_jtag_write_ready(void)

components/esp_driver_usb_serial_jtag/src/usb_serial_jtag_vfs.c

Lines changed: 59 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -226,40 +226,69 @@ static void usb_serial_jtag_return_char(int fd, int c)
226226

227227
static ssize_t usb_serial_jtag_read(int fd, void* data, size_t size)
228228
{
229+
assert(fd == USJ_LOCAL_FD);
229230
char *data_c = (char *) data;
230231
size_t received = 0;
232+
size_t available_size = 0;
233+
int c = NONE; // store the read char
231234
_lock_acquire_recursive(&s_ctx.read_lock);
232-
while (received < size) {
233-
int c = usb_serial_jtag_read_char(fd);
234-
if (c == '\r') {
235-
if (s_ctx.rx_mode == ESP_LINE_ENDINGS_CR) {
236-
c = '\n';
237-
} else if (s_ctx.rx_mode == ESP_LINE_ENDINGS_CRLF) {
238-
/* look ahead */
239-
int c2 = usb_serial_jtag_read_char(fd);
240-
if (c2 == NONE) {
241-
/* could not look ahead, put the current character back */
242-
usb_serial_jtag_return_char(fd, c);
243-
break;
244-
}
245-
if (c2 == '\n') {
246-
/* this was \r\n sequence. discard \r, return \n */
235+
236+
// if blocking read, wait for data to be available
237+
if (!s_ctx.non_blocking) {
238+
c = usb_serial_jtag_read_char(fd);
239+
}
240+
241+
// find the actual fetch size
242+
available_size += usb_serial_jtag_get_read_bytes_available();
243+
if (c != NONE) {
244+
available_size++;
245+
}
246+
if (s_ctx.peek_char != NONE) {
247+
available_size++;
248+
}
249+
size_t fetch_size = MIN(available_size, size);
250+
251+
if (fetch_size > 0) {
252+
do {
253+
if (c == NONE) { // for non-O_NONBLOCK mode, there is already a pre-fetched char
254+
c = usb_serial_jtag_read_char(fd);
255+
}
256+
assert(c != NONE);
257+
258+
if (c == '\r') {
259+
if (s_ctx.rx_mode == ESP_LINE_ENDINGS_CR) {
247260
c = '\n';
248-
} else {
249-
/* \r followed by something else. put the second char back,
250-
* it will be processed on next iteration. return \r now.
251-
*/
252-
usb_serial_jtag_return_char(fd, c2);
261+
} else if (s_ctx.rx_mode == ESP_LINE_ENDINGS_CRLF) {
262+
/* look ahead */
263+
int c2 = usb_serial_jtag_read_char(fd);
264+
fetch_size--;
265+
if (c2 == NONE) {
266+
/* could not look ahead, put the current character back */
267+
usb_serial_jtag_return_char(fd, c);
268+
c = NONE;
269+
break;
270+
}
271+
if (c2 == '\n') {
272+
/* this was \r\n sequence. discard \r, return \n */
273+
c = '\n';
274+
} else {
275+
/* \r followed by something else. put the second char back,
276+
* it will be processed on next iteration. return \r now.
277+
*/
278+
usb_serial_jtag_return_char(fd, c2);
279+
fetch_size++;
280+
}
253281
}
254282
}
255-
} else if (c == NONE) {
256-
break;
257-
}
258-
data_c[received] = (char) c;
259-
++received;
260-
if (c == '\n') {
261-
break;
262-
}
283+
284+
data_c[received] = (char) c;
285+
++received;
286+
c = NONE;
287+
} while (received < fetch_size);
288+
}
289+
290+
if (c != NONE) { // fetched, but not used
291+
usb_serial_jtag_return_char(fd, c);
263292
}
264293
_lock_release_recursive(&s_ctx.read_lock);
265294
if (received > 0) {
@@ -455,7 +484,7 @@ static esp_err_t usb_serial_jtag_start_select(int nfds, fd_set *readfds, fd_set
455484
bool trigger_select = false;
456485

457486
// check if the select should return instantly if the bus is read ready
458-
if (FD_ISSET(USJ_LOCAL_FD, &args->readfds_orig) && usb_serial_jtag_read_ready()) {
487+
if (FD_ISSET(USJ_LOCAL_FD, &args->readfds_orig) && (usb_serial_jtag_get_read_bytes_available() > 0)) {
459488
// signal immediately when data is buffered
460489
FD_SET(USJ_LOCAL_FD, readfds);
461490
trigger_select = true;

components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/main/test_vfs_usb_serial_jtag.c

Lines changed: 85 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -23,11 +23,6 @@
2323
#include "test_utils.h"
2424
#include "sdkconfig.h"
2525

26-
struct task_arg_t {
27-
FILE* stream;
28-
SemaphoreHandle_t done;
29-
};
30-
3126
static int read_bytes_with_select(FILE *stream, void *buf, size_t buf_len, struct timeval tv)
3227
{
3328

@@ -74,7 +69,7 @@ static int read_bytes_with_select(FILE *stream, void *buf, size_t buf_len, struc
7469
* write calls. At the end of the test, the variable is check to make sure the select returned
7570
* for each of the write calls.
7671
*/
77-
TEST_CASE("test select read, write and timeout", "[usb_serial_jtag]")
72+
TEST_CASE("test select read, write and timeout", "[usb_serial_jtag vfs]")
7873
{
7974
struct timeval tv;
8075
tv.tv_sec = 1;
@@ -128,3 +123,86 @@ TEST_CASE("test select read, write and timeout", "[usb_serial_jtag]")
128123
usb_serial_jtag_vfs_use_nonblocking();
129124
usb_serial_jtag_driver_uninstall();
130125
}
126+
127+
/* Test that the read function does not return prematurely when receiving a new line character
128+
*/
129+
TEST_CASE("read does not return on new line character", "[usb serial jtag vfs]")
130+
{
131+
// Send a string with length less than the read requested length
132+
const char in_buffer[] = "!(@*#&(!*@&#((SDasdkjhad\nce"; // read should not early return on \n
133+
const size_t in_buffer_len = sizeof(in_buffer);
134+
135+
const size_t out_buffer_len = in_buffer_len - 1; // don't compare the null character at the end of in_buffer string
136+
char out_buffer[out_buffer_len] = {};
137+
138+
usb_serial_jtag_driver_config_t usj_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
139+
ESP_ERROR_CHECK(usb_serial_jtag_driver_install(&usj_config));
140+
usb_serial_jtag_vfs_use_driver();
141+
142+
usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_LF);
143+
usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_LF);
144+
145+
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
146+
fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK);
147+
148+
// trigger the test environment to send the test message
149+
char ready_msg[] = "ready to receive\n";
150+
write(fileno(stdout), ready_msg, sizeof(ready_msg));
151+
152+
// wait for the string to be sent and buffered
153+
vTaskDelay(pdMS_TO_TICKS(500));
154+
155+
char *out_buffer_ptr = out_buffer;
156+
size_t bytes_read = 0;
157+
do {
158+
int nread = read(STDIN_FILENO, out_buffer_ptr, out_buffer_len);
159+
printf("%d\n", nread);
160+
if (nread > 0) {
161+
out_buffer_ptr += nread;
162+
bytes_read += nread;
163+
}
164+
} while (bytes_read < in_buffer_len);
165+
166+
// string compare
167+
for (size_t i = 0; i < out_buffer_len; i++) {
168+
TEST_ASSERT_EQUAL(in_buffer[i], out_buffer[i]);
169+
}
170+
171+
usb_serial_jtag_vfs_use_nonblocking();
172+
fcntl(STDIN_FILENO, F_SETFL, flags);
173+
usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_CRLF);
174+
usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
175+
ESP_ERROR_CHECK(usb_serial_jtag_driver_uninstall());
176+
vTaskDelay(2); // wait for tasks to exit
177+
}
178+
179+
TEST_CASE("blocking read returns with available data", "[usb serial jtag vfs]")
180+
{
181+
const size_t out_buffer_len = 32;
182+
char out_buffer[out_buffer_len] = {};
183+
184+
usb_serial_jtag_driver_config_t usj_config = USB_SERIAL_JTAG_DRIVER_CONFIG_DEFAULT();
185+
ESP_ERROR_CHECK(usb_serial_jtag_driver_install(&usj_config));
186+
usb_serial_jtag_vfs_use_driver();
187+
188+
usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_LF);
189+
usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_LF);
190+
191+
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
192+
fcntl(STDIN_FILENO, F_SETFL, flags & (~O_NONBLOCK));
193+
194+
// trigger the test environment to send the test message
195+
char ready_msg[] = "ready to receive\n";
196+
write(fileno(stdout), ready_msg, sizeof(ready_msg));
197+
198+
const int nread = read(STDIN_FILENO, out_buffer, out_buffer_len);
199+
TEST_ASSERT(nread > 0);
200+
TEST_ASSERT(nread < out_buffer_len);
201+
202+
usb_serial_jtag_vfs_use_nonblocking();
203+
fcntl(STDIN_FILENO, F_SETFL, flags);
204+
usb_serial_jtag_vfs_set_rx_line_endings(ESP_LINE_ENDINGS_CRLF);
205+
usb_serial_jtag_vfs_set_tx_line_endings(ESP_LINE_ENDINGS_CRLF);
206+
ESP_ERROR_CHECK(usb_serial_jtag_driver_uninstall());
207+
vTaskDelay(2); // wait for tasks to exit
208+
}

components/esp_driver_usb_serial_jtag/test_apps/usb_serial_jtag_vfs/pytest_usb_serial_jtag_vfs.py

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,46 @@
1515
)
1616
@pytest.mark.parametrize('test_message', ['test123456789!@#%^&*'])
1717
@idf_parametrize('target', ['esp32s3', 'esp32c3', 'esp32c6', 'esp32h2'], indirect=['target'])
18-
def test_usj_vfs_release(dut: Dut, test_message: list) -> None:
18+
def test_usj_vfs_select(dut: Dut, test_message: list) -> None:
1919
dut.expect_exact('Press ENTER to see the list of tests')
2020
dut.write('"test select read, write and timeout"')
2121
dut.expect_exact('select timed out', timeout=2)
2222
dut.write(test_message)
2323
dut.expect_exact(test_message, timeout=2)
2424
dut.expect(r'\d{1} Tests 0 Failures 0 Ignored', timeout=10)
25+
26+
27+
@pytest.mark.usb_serial_jtag
28+
@pytest.mark.parametrize(
29+
'port, flash_port, config',
30+
[
31+
pytest.param('/dev/serial_ports/ttyACM-esp32', '/dev/serial_ports/ttyUSB-esp32', 'release'),
32+
],
33+
indirect=True,
34+
)
35+
@pytest.mark.parametrize('test_message', ['!(@*#&(!*@&#((SDasdkjhad\nce'])
36+
@idf_parametrize('target', ['esp32s3', 'esp32c3', 'esp32c6', 'esp32h2'], indirect=['target'])
37+
def test_usj_vfs_read_return(dut: Dut, test_message: list) -> None:
38+
dut.expect_exact('Press ENTER to see the list of tests')
39+
dut.write('"read does not return on new line character"')
40+
dut.expect_exact('ready to receive', timeout=2)
41+
dut.write(test_message)
42+
dut.expect(r'\d{1} Tests 0 Failures 0 Ignored', timeout=10)
43+
44+
45+
@pytest.mark.usb_serial_jtag
46+
@pytest.mark.parametrize(
47+
'port, flash_port, config',
48+
[
49+
pytest.param('/dev/serial_ports/ttyACM-esp32', '/dev/serial_ports/ttyUSB-esp32', 'release'),
50+
],
51+
indirect=True,
52+
)
53+
@pytest.mark.parametrize('test_message', ['testdata'])
54+
@idf_parametrize('target', ['esp32s3', 'esp32c3', 'esp32c6', 'esp32h2'], indirect=['target'])
55+
def test_usj_vfs_read_blocking(dut: Dut, test_message: list) -> None:
56+
dut.expect_exact('Press ENTER to see the list of tests')
57+
dut.write('"blocking read returns with available data"')
58+
dut.expect_exact('ready to receive', timeout=2)
59+
dut.write(test_message)
60+
dut.expect(r'\d{1} Tests 0 Failures 0 Ignored', timeout=10)

0 commit comments

Comments
 (0)