[PATCH u-boot-marvell 04/10] tools: kwboot: Use separate thread for sending boot message pattern
Stefan Roese
sr at denx.de
Fri Mar 4 08:49:14 CET 2022
On 3/2/22 11:49, Pali Rohár wrote:
> After BootROM successfully detects boot message pattern on UART it waits
> until host stop sending data on UART. For example Armada 385 BootROM
> requires that host does not send anything on UART at least 24 ms. If host
> is still sending something then BootROM waits (possibly infinitely).
>
> BootROM successfully detects boot message pattern if it receives it in
> small period of time after power on.
>
> So to ensure that host put BootROM into UART boot mode, host must send
> continuous stream of boot message pattern with a small gap (for A385 at
> least 24 ms) after series of pattern. But this gap cannot be too often or
> too long to ensure that it does not cover whole BootROM time window when it
> is detecting for boot message pattern.
>
> Therefore it is needed to do following steps in cycle without any delay:
> 1. send series of boot message pattern over UART
> 2. wait until kernel transmit all data
> 3. sleep small period of time
>
> At the same time, host needs to monitor input queue, data received on the
> UART and checking if it contains NAK byte by which BootROM informs that
> xmodem transfer is ready.
>
> But it is not possible to wait until kernel transmit all data on UART and
> at the same time in the one process to also wait for input data. This is
> limitation of POSIX tty API and also by linux kernel that it does not
> provide asynchronous function for waiting until all data are transmitted.
> There is only synchronous variant tcdrain().
>
> So to correctly implement this handshake on systems with linux kernel, it
> is needed to use tcdrain() in separate thread.
>
> Implement sending of boot message pattern in one thread and reading of
> reply in the main thread. Use pthread library for threads.
>
> This change makes UART booting on Armada 385 more reliable. It is possible
> to start kwboot and power on board after minute and kwboot correctly put
> board into UART boot mode.
>
> Old implementation without separate thread has an issue that it read just
> one byte from UART input queue and then it send 128 message pattern to the
> output queue. If some noise was on UART then kwboot was not able to read
> BootROM response as its input queue was just overflowed and kwboot was
> sending more data than receiving.
>
> This change basically fixed above issue too.
>
> Signed-off-by: Pali Rohár <pali at kernel.org>
Reviewed-by: Stefan Roese <sr at denx.de>
Tested-by: Stefan Roese <sr at denx.de>
Thanks,
Stefan
> ---
> tools/Makefile | 3 ++
> tools/kwboot.c | 120 +++++++++++++++++++++++++++++++++++++++++--------
> 2 files changed, 104 insertions(+), 19 deletions(-)
>
> diff --git a/tools/Makefile b/tools/Makefile
> index df941e0dca8d..c4a06dd9ba36 100644
> --- a/tools/Makefile
> +++ b/tools/Makefile
> @@ -199,6 +199,9 @@ hostprogs-$(CONFIG_EXYNOS5250) += mkexynosspl
> hostprogs-$(CONFIG_EXYNOS5420) += mkexynosspl
> HOSTCFLAGS_mkexynosspl.o := -pedantic
>
> +HOSTCFLAGS_kwboot.o += -pthread
> +HOSTLDLIBS_kwboot += -pthread
> +
> ifdtool-objs := $(LIBFDT_OBJS) ifdtool.o
> hostprogs-$(CONFIG_X86) += ifdtool
>
> diff --git a/tools/kwboot.c b/tools/kwboot.c
> index 4e2acb52458a..9fd90b9bec71 100644
> --- a/tools/kwboot.c
> +++ b/tools/kwboot.c
> @@ -28,6 +28,7 @@
> #include <stdint.h>
> #include <time.h>
> #include <sys/stat.h>
> +#include <pthread.h>
>
> #ifdef __linux__
> #include "termios_linux.h"
> @@ -717,37 +718,120 @@ out:
> return rc;
> }
>
> +static void *
> +kwboot_msg_write_handler(void *arg)
> +{
> + int tty = *(int *)((void **)arg)[0];
> + const void *msg = ((void **)arg)[1];
> + int rsp_timeo = msg_rsp_timeo;
> + int i, dummy_oldtype;
> +
> + /* allow to cancel this thread at any time */
> + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &dummy_oldtype);
> +
> + while (1) {
> + /* write 128 samples of message pattern into the output queue without waiting */
> + for (i = 0; i < 128; i++) {
> + if (kwboot_tty_send(tty, msg, 8, 1) < 0) {
> + perror("\nFailed to send message pattern");
> + exit(1);
> + }
> + }
> + /* wait until output queue is transmitted and then make pause */
> + if (tcdrain(tty) < 0) {
> + perror("\nFailed to send message pattern");
> + exit(1);
> + }
> + /* BootROM requires pause on UART after it detects message pattern */
> + usleep(rsp_timeo * 1000);
> + }
> +}
> +
> +static int
> +kwboot_msg_start_thread(pthread_t *thread, int *tty, void *msg)
> +{
> + void *arg[2];
> + int rc;
> +
> + arg[0] = tty;
> + arg[1] = msg;
> + rc = pthread_create(thread, NULL, kwboot_msg_write_handler, arg);
> + if (rc) {
> + errno = rc;
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static int
> +kwboot_msg_stop_thread(pthread_t thread)
> +{
> + int rc;
> +
> + rc = pthread_cancel(thread);
> + if (rc) {
> + errno = rc;
> + return -1;
> + }
> +
> + rc = pthread_join(thread, NULL);
> + if (rc) {
> + errno = rc;
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> static int
> kwboot_bootmsg(int tty)
> {
> struct kwboot_block block;
> - int rc;
> + pthread_t write_thread;
> + int rc, err;
> char c;
> - int count;
> -
> - kwboot_printv("Sending boot message. Please reboot the target...");
>
> - do {
> - rc = tcflush(tty, TCIOFLUSH);
> - if (rc)
> - break;
> + /* flush input and output queue */
> + tcflush(tty, TCIOFLUSH);
>
> - for (count = 0; count < 128; count++) {
> - rc = kwboot_tty_send(tty, kwboot_msg_boot, sizeof(kwboot_msg_boot), 0);
> - if (rc)
> - break;
> - }
> + rc = kwboot_msg_start_thread(&write_thread, &tty, kwboot_msg_boot);
> + if (rc) {
> + perror("Failed to start write thread");
> + return rc;
> + }
>
> - rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
> + kwboot_printv("Sending boot message. Please reboot the target...");
>
> + err = 0;
> + while (1) {
> kwboot_spinner();
>
> - } while (rc || c != NAK);
> + rc = kwboot_tty_recv(tty, &c, 1, msg_rsp_timeo);
> + if (rc && errno == ETIMEDOUT) {
> + continue;
> + } else if (rc) {
> + err = errno;
> + break;
> + }
> +
> + if (c == NAK)
> + break;
> + }
>
> kwboot_printv("\n");
>
> - if (rc)
> + rc = kwboot_msg_stop_thread(write_thread);
> + if (rc) {
> + perror("Failed to stop write thread");
> return rc;
> + }
> +
> + if (err) {
> + errno = err;
> + perror("Failed to read response for boot message pattern");
> + return -1;
> + }
>
> /*
> * At this stage we have sent more boot message patterns and BootROM
> @@ -1873,10 +1957,8 @@ main(int argc, char **argv)
> }
> } else if (bootmsg) {
> rc = kwboot_bootmsg(tty);
> - if (rc) {
> - perror("bootmsg");
> + if (rc)
> goto out;
> - }
> }
>
> if (img) {
Viele Grüße,
Stefan Roese
--
DENX Software Engineering GmbH, Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-51 Fax: (+49)-8142-66989-80 Email: sr at denx.de
More information about the U-Boot
mailing list