mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
Merge #19037
19037: sys/usb, pkg/tinyusb: move USB board reset from highlevel STDIO to CDC ACM r=dylad a=gschorcht ### Contribution description The USB board reset function `usb_board_reset_coding_cb` can be used on any CDC-ACM interface, even if the CDC ACM interface is not used as high-level STDIO. Therefore, this PR provides the following changes: - The call of the board reset function `usb_board_reset_coding_cb` from USBUS stack has been moved from the STDIO CDC ACM implementation to the CDC ACM implementation and is thus a feature of any USBUS CDC ACM interface which does not necessarily have to be used as highlevel STDIO. - The call of the board reset function `usb_board_reset_coding_cb` from tinyUSB stack been moved from module `tinyusb_stdio_cdc_acm` to module `tinyusb_contrib` and is compiled in if the `tinyusb_class_cdc` module is used together the `tinyusb_device` module. Thus, it is now a feature of the tinyUSB CDC ACM interface, which does not necessarily have to be used as highlevel STDIO. - The `usb_board_reset` module defines the `usb_board_reset_in_bootloader` function as a weak symbol to be used when reset in bootloader if no real implementation of this function is compiled in and the `riotboot_reset` module is not used. It only prints an error message that the reset in bootloader is not supported. This is necessary if the module `usb_board_reset` is used to be able to restart the board with an application via a USB CDC ACM interface, but the board's bootloader does not support the reset in bootloader feature. - A test application has been added that either uses the highlevel STDIO `stdio_acm_cdc` or creates a CDC-ACM interface to enable board resets via USB. If the `usbus_dfu` module is used, it also initializes the DFU interface to be able to work together with the `riotboot_dfu` bootloader. ### Testing procedure 1. Use a board with a bootloader that supports the reset in bootloader via USB, but don't use the highlevel STDIO to check that it works with `usbus_cdc_acm`, for example: ```python USEMODULE=stdio_uart BOARD=arduino-mkr1000 make -C tests/usb_board_reset flash ``` After reset in application with command ```python stty -F /dev/ttyACM0 raw ispeed 600 ospeed 600 cs8 -cstopb ignpar eol 255 eof 255 ``` command `dmesg` should give an output like the following with RIOT's test VID/PID: ```python dmesg [1745182.057403] usb 1-4.1.2: new full-speed USB device number 69 using xhci_hcd [1745182.160386] usb 1-4.1.2: New USB device found, idVendor=1209, idProduct=7d01, bcdDevice= 1.00 [1745182.160390] usb 1-4.1.2: New USB device strings: Mfr=3, Product=2, SerialNumber=4 [1745182.160392] usb 1-4.1.2: Product: arduino-mkr1000 [1745182.160393] usb 1-4.1.2: Manufacturer: RIOT-os.org [1745182.160395] usb 1-4.1.2: SerialNumber: 6B6C2CA5229020D8 [1745182.170982] cdc_acm 1-4.1.2:1.0: ttyACM0: USB ACM device ``` After reset in bootloader with command ```python stty -F /dev/ttyACM0 raw ispeed 1200 ospeed 1200 cs8 -cstopb ignpar eol 255 eof 255 ``` command `dmesg` should give an output like the following with vendor VID/PID: ```python [1746220.443792] usb 1-4.1.2: new full-speed USB device number 70 using xhci_hcd [1746220.544705] usb 1-4.1.2: New USB device found, idVendor=2341, idProduct=024e, bcdDevice= 2.00 [1746220.544708] usb 1-4.1.2: New USB device strings: Mfr=0, Product=0, SerialNumber=0 [1746220.553471] cdc_acm 1-4.1.2:1.0: ttyACM0: USB ACM device ``` 2. Test the same as in 1., but this time use the highlevel STDIO to check that there is no regression and it still works with `stdio_cdc_acm`, for example: ```python BOARD=arduino-mkr1000 make -C tests/usb_board_reset flash ``` 3. Use a board that supports `riotboot_dfu` but doesn't use the highlevel STDIO and flash the `riotboot_dfu` bootloader, for example: ```python BOARD=stm32f429i-disc1 make -C bootloaders/riotboot_dfu flash term ``` Once the bootloader is flashed, command `dfu-util --list` should give something like the following: ```python Found DFU: [1209:7d02] ver=0100, devnum=14, cfg=1, intf=0, path="1-2", alt=1, name="RIOT-OS Slot 1", serial="6591620BCB270283" Found DFU: [1209:7d02] ver=0100, devnum=14, cfg=1, intf=0, path="1-2", alt=0, name="RIOT-OS Slot 0", serial="6591620BCB270283" ``` If the output gives only ```python Found Runtime: [1209:7d00] ver=0100, devnum=123, cfg=1, intf=0, path="1-2", alt=0, name="RIOT-OS bootloader", serial="6591620BCB270283" ``` an application is already running in DFU Runtime mode. Use `dfu-util -e` to restart it in bootloader DFU mode. Then flash the test application, for example: ```python FEATURES_REQUIRED=riotboot USEMODULE='usbus_dfu riotboot_reset' \ BOARD=stm32f429i-disc1 make -C tests/usbus_board_reset PROGRAMMER=dfu-util riotboot/flash-slot0 ``` Once the test application is flashed, command `dfu-util --list` should give: ```python Found Runtime: [1209:7d00] ver=0100, devnum=123, cfg=1, intf=0, path="1-2", alt=0, name="RIOT-OS bootloader", serial="6591620BCB270283" ``` Now, use command ```python stty -F /dev/ttyACM1 raw ispeed 600 ospeed 600 cs8 -cstopb ignpar eol 255 eof 255 ``` to restart the board in application. Command `dfu-util --list` should give again the following: ```python Found Runtime: [1209:7d00] ver=0100, devnum=123, cfg=1, intf=0, path="1-2", alt=0, name="RIOT-OS bootloader", serial="6591620BCB270283" ``` That is, the application is running in DFU Runtime mode. Then use command ```python stty -F /dev/ttyACM1 raw ispeed 1200 ospeed 1200 cs8 -cstopb ignpar eol 255 eof 255 ``` to restart the board in bootloader DFU mode. Command `dfu-util --list` should now give the following: ```python Found DFU: [1209:7d02] ver=0100, devnum=50, cfg=1, intf=0, path="1-2", alt=1, name="RIOT-OS Slot 1", serial="7D156425A950A8EB" Found DFU: [1209:7d02] ver=0100, devnum=50, cfg=1, intf=0, path="1-2", alt=0, name="RIOT-OS Slot 0", serial="7D156425A950A8EB" ``` That is, the bootloader is in DFU mode and another application can be flash. 4. After a hard reset of the board under 3., try the commands `reboot` and `bootloader`. 5. To check the same for tinyUSB, use the existing tinyUSB application with a CDC ACM interface and add module `usb_board_reset`, for example: ```python USEMODULE=usb_board_reset BOARD=stm32f429i-disc1 make -C tests/pkg_tinyusb_cdc_msc flash term ``` After flashing, it should be possible to restart the application with command: ```python stty -F /dev/ttyACM1 raw ispeed 600 ospeed 600 cs8 -cstopb ignpar eol 255 eof 255 ``` When using command ```python stty -F /dev/ttyACM1 raw ispeed 1200 ospeed 1200 cs8 -cstopb ignpar eol 255 eof 255 ``` the following error message should be shown in terminal ```python [cdc-acm] reset in bootloader is not supported ``` ### Issues/PRs references Co-authored-by: Gunar Schorcht <gunar@schorcht.net>
This commit is contained in:
commit
e51d8285f3
@ -6,7 +6,7 @@
|
||||
|
||||
config MODULE_BOARDS_COMMON_SAMDX1-ARDUINO-BOOTLOADER
|
||||
bool
|
||||
default y if MODULE_STDIO_CDC_ACM
|
||||
default y if MODULE_USBUS_CDC_ACM
|
||||
imply MODULE_USB_BOARD_RESET
|
||||
depends on TEST_KCONFIG
|
||||
help
|
||||
|
@ -1,7 +1,7 @@
|
||||
include $(RIOTBOARD)/common/makefiles/stdio_cdc_acm.dep.mk
|
||||
|
||||
# USB Board reset only works if CDC ACM is used
|
||||
ifneq (,$(filter stdio_cdc_acm,$(USEMODULE)))
|
||||
ifneq (,$(filter usbus_cdc_acm,$(USEMODULE)))
|
||||
USEMODULE += boards_common_samdx1-arduino-bootloader
|
||||
USEMODULE += usb_board_reset
|
||||
endif
|
||||
|
@ -32,11 +32,6 @@
|
||||
#include "vfs.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_USB_BOARD_RESET
|
||||
#include "usb_board_reset_internal.h"
|
||||
#include "class/cdc/cdc.h"
|
||||
#endif
|
||||
|
||||
static mutex_t data_lock = MUTEX_INIT_LOCKED;
|
||||
|
||||
void stdio_init(void)
|
||||
@ -81,21 +76,3 @@ void tud_cdc_rx_cb(uint8_t itf)
|
||||
|
||||
mutex_unlock(&data_lock);
|
||||
}
|
||||
|
||||
#ifdef MODULE_USB_BOARD_RESET
|
||||
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
|
||||
{
|
||||
(void)itf;
|
||||
assert(p_line_coding != NULL);
|
||||
|
||||
/* The first parameter is the USBUS CDC ACM device, but this is
|
||||
* not used in `usb_board_reset_coding_cb`. Therefore we can simply
|
||||
* reuse this callback function in tinyUSB without any problems. */
|
||||
usb_board_reset_coding_cb(NULL,
|
||||
p_line_coding->bit_rate,
|
||||
p_line_coding->data_bits,
|
||||
p_line_coding->parity,
|
||||
p_line_coding->stop_bits);
|
||||
}
|
||||
#endif
|
||||
|
31
pkg/tinyusb/contrib/tinyusb_board_reset.c
Normal file
31
pkg/tinyusb/contrib/tinyusb_board_reset.c
Normal file
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
#if MODULE_TINYUSB_DEVICE && MODULE_TINYUSB_CLASS_CDC && MODULE_USB_BOARD_RESET
|
||||
|
||||
#include "usb_board_reset_internal.h"
|
||||
#include "class/cdc/cdc.h"
|
||||
|
||||
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const* p_line_coding)
|
||||
{
|
||||
(void)itf;
|
||||
assert(p_line_coding != NULL);
|
||||
|
||||
/* The first parameter is the USBUS CDC ACM device, but this is
|
||||
* not used in `usb_board_reset_coding_cb`. Therefore we can simply
|
||||
* reuse this callback function in tinyUSB without any problems. */
|
||||
usb_board_reset_coding_cb(NULL,
|
||||
p_line_coding->bit_rate,
|
||||
p_line_coding->data_bits,
|
||||
p_line_coding->parity,
|
||||
p_line_coding->stop_bits);
|
||||
}
|
||||
|
||||
#else
|
||||
typedef int dont_be_pedantic;
|
||||
#endif
|
@ -29,6 +29,8 @@
|
||||
#include "usb/usbus/cdc/acm.h"
|
||||
#include "usb/usbus/control.h"
|
||||
|
||||
#include "usb_board_reset_internal.h"
|
||||
|
||||
#define ENABLE_DEBUG 0
|
||||
#include "debug.h"
|
||||
|
||||
@ -259,7 +261,7 @@ static int _control_handler(usbus_t *usbus, usbus_handler_t *handler,
|
||||
usbus_cdcacm_device_t *cdcacm = (usbus_cdcacm_device_t*)handler;
|
||||
switch (setup->request) {
|
||||
case USB_CDC_MGNT_REQUEST_SET_LINE_CODING:
|
||||
if (!(cdcacm->coding_cb)) {
|
||||
if (!(cdcacm->coding_cb) && !IS_USED(MODULE_USB_BOARD_RESET)) {
|
||||
/* Line coding not supported, return STALL */
|
||||
DEBUG("CDCACM: line coding not supported\n");
|
||||
return -1;
|
||||
@ -279,6 +281,12 @@ static int _control_handler(usbus_t *usbus, usbus_handler_t *handler,
|
||||
sizeof(usb_req_cdcacm_coding_t), len);
|
||||
return -1;
|
||||
}
|
||||
if (IS_USED(MODULE_USB_BOARD_RESET)) {
|
||||
/* call board reset function first if reset is received */
|
||||
usb_board_reset_coding_cb(cdcacm, coding->baud,
|
||||
coding->databits, coding->parity,
|
||||
coding->format);
|
||||
}
|
||||
if (cdcacm->coding_cb) {
|
||||
DEBUG("Setting line coding to baud rate %" PRIu32 ", "
|
||||
"%u data bits, parity value %u, stop bit value %u\n",
|
||||
|
@ -34,10 +34,6 @@
|
||||
#include "vfs.h"
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_USB_BOARD_RESET
|
||||
#include "usb_board_reset_internal.h"
|
||||
#endif
|
||||
|
||||
static usbus_cdcacm_device_t cdcacm;
|
||||
static uint8_t _cdc_tx_buf_mem[CONFIG_USBUS_CDC_ACM_STDIO_BUF_SIZE];
|
||||
static uint8_t _cdc_rx_buf_mem[CONFIG_USBUS_CDC_ACM_STDIO_BUF_SIZE];
|
||||
@ -91,7 +87,4 @@ void usb_cdc_acm_stdio_init(usbus_t *usbus)
|
||||
{
|
||||
usbus_cdc_acm_init(usbus, &cdcacm, _cdc_acm_rx_pipe, NULL,
|
||||
_cdc_tx_buf_mem, sizeof(_cdc_tx_buf_mem));
|
||||
#ifdef MODULE_USB_BOARD_RESET
|
||||
usbus_cdc_acm_set_coding_cb(&cdcacm, usb_board_reset_coding_cb);
|
||||
#endif
|
||||
}
|
||||
|
@ -8,4 +8,4 @@
|
||||
config MODULE_USB_BOARD_RESET
|
||||
bool "Trigger a board reset via USB CDC ACM"
|
||||
depends on TEST_KCONFIG
|
||||
depends on MODULE_USBUS_CDC_ACM || MODULE_STDIO_TINYUSB_CDC_ACM
|
||||
depends on MODULE_USBUS_CDC_ACM || (MODULE_TINYUSB_DEVICE && MODULE_TINYUSB_CLASS_CDC)
|
||||
|
@ -47,11 +47,11 @@ int usb_board_reset_coding_cb(usbus_cdcacm_device_t *cdcacm,
|
||||
(void)stop;
|
||||
switch (baud) {
|
||||
case RESET_IN_BOOTLOADER_TRIGGER_BAUDRATE:
|
||||
LOG_DEBUG("[cdc-acm-stdio] reset in bootloader");
|
||||
LOG_DEBUG("[cdc-acm] reset in bootloader\n");
|
||||
usb_board_reset_in_bootloader();
|
||||
break;
|
||||
case RESET_IN_APPLICATION_TRIGGER_BAUDRATE:
|
||||
LOG_DEBUG("[cdc-acm-stdio] reset in application");
|
||||
LOG_DEBUG("[cdc-acm] reset in application\n");
|
||||
usb_board_reset_in_application();
|
||||
break;
|
||||
default:
|
||||
@ -62,6 +62,22 @@ int usb_board_reset_coding_cb(usbus_cdcacm_device_t *cdcacm,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef MODULE_RIOTBOOT_RESET
|
||||
/*
|
||||
* Definition of a function as weak symbol for reset in bootloader which
|
||||
* prints an error message if no real implementation is compiled in and
|
||||
* the `riotboot_reset` module is not used. This is required if the module
|
||||
* `usb_board_reset` is used to restart the board with an application via
|
||||
* an USB CDC ACM interface, but the board's bootloader does not support
|
||||
* a reset in the bootloader.
|
||||
*/
|
||||
__attribute__((weak))
|
||||
void usb_board_reset_in_bootloader(void)
|
||||
{
|
||||
LOG_ERROR("[cdc-acm] reset in bootloader is not supported\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
void usb_board_reset_in_application(void)
|
||||
{
|
||||
pm_reboot();
|
||||
|
17
tests/usbus_board_reset/Makefile
Normal file
17
tests/usbus_board_reset/Makefile
Normal file
@ -0,0 +1,17 @@
|
||||
include ../Makefile.tests_common
|
||||
|
||||
FEATURES_REQUIRED += periph_usbdev
|
||||
|
||||
USEMODULE += app_metadata
|
||||
USEMODULE += ps
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_cmds_default
|
||||
USEMODULE += usb_board_reset
|
||||
USEMODULE += usbus_cdc_acm
|
||||
|
||||
DISABLE_MODULE += auto_init_usbus
|
||||
|
||||
USB_VID ?= $(USB_VID_TESTING)
|
||||
USB_PID ?= $(USB_PID_TESTING)
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
48
tests/usbus_board_reset/README.md
Normal file
48
tests/usbus_board_reset/README.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Overview
|
||||
|
||||
This test represents a simple shell application that can be used to test
|
||||
the USB board reset function (module `usb_board_reset`) via the USB CDC ACM
|
||||
interface.
|
||||
|
||||
It can be used to reset the board to restart either the application or the
|
||||
bootloader if supported using the `usb_board_reset` module.
|
||||
|
||||
The test application requires that the board provides USB peripherals
|
||||
(feature `periph_usbdev`).
|
||||
|
||||
# Usage
|
||||
|
||||
Once the test application is flashed, the board should be detected. The output
|
||||
of command
|
||||
```
|
||||
dmesg
|
||||
```
|
||||
should look like the following and should show the USB DC ACM interface:
|
||||
```
|
||||
[1745182.057403] usb 1-4.1.2: new full-speed USB device number 69 using xhci_hcd
|
||||
[1745182.160386] usb 1-4.1.2: New USB device found, idVendor=1209, idProduct=7d01, bcdDevice= 1.00
|
||||
[1745182.160390] usb 1-4.1.2: New USB device strings: Mfr=3, Product=2, SerialNumber=4
|
||||
[1745182.160392] usb 1-4.1.2: Product: arduino-mkr1000
|
||||
[1745182.160393] usb 1-4.1.2: Manufacturer: RIOT-os.org
|
||||
[1745182.160395] usb 1-4.1.2: SerialNumber: 6B6C2CA5229020D8
|
||||
[1745182.170982] cdc_acm 1-4.1.2:1.0: ttyACM0: USB ACM device
|
||||
```
|
||||
*Note*: The interface `ttyACM0` could be different depending on which other
|
||||
USB CDC ACM devices are already in use.
|
||||
|
||||
For boards that use the USB CDC ACM interface as STDIO (module `stdio_cdc_acm`),
|
||||
this interface is used for the test application. Otherwise the test application
|
||||
creates a simple USB CDC ACM interface without any functionality except the
|
||||
USB board reset function.
|
||||
|
||||
When the USB CDC ACM interface is initialized, the `stty` command can be used
|
||||
to reset the board, for example:
|
||||
```
|
||||
stty -F /dev/ttyACM0 raw ispeed 600 ospeed 600 cs8 -cstopb ignpar eol 255 eof 255
|
||||
```
|
||||
should reset the board and restart the application, while
|
||||
```
|
||||
stty -F /dev/ttyACM0 raw ispeed 1200 ospeed 1200 cs8 -cstopb ignpar eol 255 eof 255
|
||||
```
|
||||
should reset the board and start the bootloader. The latter requires that the
|
||||
bootloader supports this.
|
9
tests/usbus_board_reset/app.config.test
Normal file
9
tests/usbus_board_reset/app.config.test
Normal file
@ -0,0 +1,9 @@
|
||||
CONFIG_MODULE_APP_METADATA=y
|
||||
CONFIG_MODULE_PS=y
|
||||
CONFIG_MODULE_SHELL=y
|
||||
CONFIG_MODULE_SHELL_CMDS_DEFAULT=y
|
||||
CONFIG_MODULE_USB_BOARD_RESET=y
|
||||
CONFIG_MODULE_USBUS=y
|
||||
CONFIG_MODULE_USBUS_CDC_ACM=y
|
||||
|
||||
CONFIG_MODULE_AUTO_INIT_USBUS=n
|
65
tests/usbus_board_reset/main.c
Normal file
65
tests/usbus_board_reset/main.c
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (C) 2022 Gunar Schorcht
|
||||
*
|
||||
* This file is subject to the terms and conditions of the GNU Lesser
|
||||
* General Public License v2.1. See the file LICENSE in the top level
|
||||
* directory for more details.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief Simple shell application to test the USB board reset function
|
||||
*
|
||||
* @author Gunar Schorcht <gunar@schorcht.net>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "shell.h"
|
||||
|
||||
#include "usb/usbus.h"
|
||||
#include "usb/usbus/cdc/acm.h"
|
||||
|
||||
#ifdef MODULE_USBUS_DFU
|
||||
#include "usb/usbus/dfu.h"
|
||||
static usbus_dfu_device_t _dfu;
|
||||
#endif
|
||||
|
||||
static char line_buf[SHELL_DEFAULT_BUFSIZE];
|
||||
|
||||
static usbus_t _usbus;
|
||||
static char _stack[USBUS_STACKSIZE];
|
||||
|
||||
static void _init(void)
|
||||
{
|
||||
usbus_init(&_usbus, usbdev_get_ctx(0));
|
||||
|
||||
#ifdef MODULE_STDIO_CDC_ACM
|
||||
/* if stdio_cdc_acm is used, initialize it */
|
||||
void usb_cdc_acm_stdio_init(usbus_t *_usbus);
|
||||
usb_cdc_acm_stdio_init(&_usbus);
|
||||
#else
|
||||
/* otherwise create a device CDC ACM and initialize it */
|
||||
static usbus_cdcacm_device_t _cdcacm;
|
||||
/* buffer is required in usbus_cdc_acm_init, use only a single character */
|
||||
static uint8_t _cdcacm_buf;
|
||||
usbus_cdc_acm_init(&_usbus, &_cdcacm, NULL, NULL, &_cdcacm_buf, 1);
|
||||
#endif
|
||||
|
||||
#ifdef MODULE_USBUS_DFU
|
||||
usbus_dfu_init(&_usbus, &_dfu, USB_DFU_PROTOCOL_RUNTIME_MODE);
|
||||
#endif
|
||||
|
||||
usbus_create(_stack, USBUS_STACKSIZE, USBUS_PRIO, USBUS_TNAME, &_usbus);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
_init();
|
||||
|
||||
shell_run(NULL, line_buf, ARRAY_SIZE(line_buf));
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue
Block a user