rohfle
Leaving DFU mode without flashing in dfu-util

As I do not have JTAG, I use DFU mode to flash the Olimex STM32-E407 via USB. The problems with doing this are:

  • Moving the B_0 / B_1 jumper every time you want to flash or not flash
  • The BOOT header with serial is directly next to the DFU jumper. With serial wires plugged in it becomes a hassle to toggle the jumper.

And these problems become pretty darn annoying after a while. So I started digging through the dfu-util source code.

Well it turns out that dfu-util has a leave command. You can use it by adding :leave after the address like so:

sudo dfu-util -a 0 -s 0x08000000:leave -D BUILD/OLIMEX_STM32E407_F407ZG/GCC_ARM/ethernet-example.bin

The device automatically leaves DFU mode after flashing is complete and runs the program. No rearranging jumpers required! To flash the firmware again, just unplug / replug the USB cable to get the board back into DFU mode.

However there is no way to restart the program:

  • When you push the RESET button on the board it jumps back into DFU mode
  • If you plug the board in, you have to flash the board to get the program to run again

Flashing all the time just to run the program is also pretty tiresome. But what if we could use the leave command without flashing the board?

Turns out it was fairly easy to implement in dfu-util. The command I came up with is as follows:

sudo dfu-util -a 0 -s 0x08000000 -L

This -L mode will execute the address defined by the -s argument without erasing or flashing the device.

I have submitted the patch upstream, but here is the diff in the meantime (or download it here)

From 65934d80f26ff9cbdf70958b9166f184252220a4 Mon Sep 17 00:00:00 2001
From: Rohan Fletcher <rohfle@gmail.com>
Date: Wed, 9 Oct 2019 08:25:21 +1300
Subject: [PATCH] Added: dfu leave without programming mode (-L)

---
 src/dfu_util.h |  3 ++-
 src/dfuse.c    | 18 ++++++++++++++++--
 src/dfuse.h    |  1 +
 src/main.c     | 19 +++++++++++++++++--
 4 files changed, 36 insertions(+), 5 deletions(-)

diff --git a/src/dfu_util.h b/src/dfu_util.h
index c4e0375..dccf12d 100644
--- a/src/dfu_util.h
+++ b/src/dfu_util.h
@@ -11,7 +11,8 @@ enum mode {
 	MODE_LIST,
 	MODE_DETACH,
 	MODE_UPLOAD,
-	MODE_DOWNLOAD
+	MODE_DOWNLOAD,
+	MODE_LEAVE
 };

 extern struct dfu_if *dfu_root;
diff --git a/src/dfuse.c b/src/dfuse.c
index f2d1763..2d14bfd 100644
--- a/src/dfuse.c
+++ b/src/dfuse.c
@@ -1,6 +1,6 @@
 /*
  * DfuSe specific functions
- *
+ *
  * This implements the ST Microsystems DFU extensions (DfuSe)
  * as per the DfuSe 1.1a specification (ST documents AN3156, AN2606)
  * The DfuSe file format is described in ST document UM0391.
@@ -305,6 +305,20 @@ int dfuse_dnload_chunk(struct dfu_if *dif, unsigned char *data, int size,
 	return bytes_sent;
 }

+int dfuse_do_leave(struct dfu_if *dif, const char *dfuse_options) {
+	if (dfuse_options)
+		dfuse_parse_options(dfuse_options);
+	mem_layout = parse_memory_layout((char *)dif->alt_name);
+	if (!mem_layout) {
+		errx(EX_IOERR, "Failed to parse memory layout");
+	}
+
+	dfuse_special_command(dif, dfuse_address, SET_ADDRESS);
+	dfuse_dnload_chunk(dif, NULL, 0, 2); /* Zero-size */
+	return 0;
+
+}
+
 int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd,
 		    const char *dfuse_options)
 {
@@ -485,7 +499,7 @@ int dfuse_dnload_element(struct dfu_if *dif, unsigned int dwElementAddress,
 		} else {
 			dfu_progress_bar("Download", p, dwElementSize);
 		}
-		
+
 		dfuse_special_command(dif, address, SET_ADDRESS);

 		/* transaction = 2 for no address offset */
diff --git a/src/dfuse.h b/src/dfuse.h
index ed1108c..3654674 100644
--- a/src/dfuse.h
+++ b/src/dfuse.h
@@ -31,5 +31,6 @@ int dfuse_do_upload(struct dfu_if *dif, int xfer_size, int fd,
 		    const char *dfuse_options);
 int dfuse_do_dnload(struct dfu_if *dif, int xfer_size, struct dfu_file *file,
 		    const char *dfuse_options);
+int dfuse_do_leave(struct dfu_if *dif, const char *dfuse_options);

 #endif /* DFUSE_H */
diff --git a/src/main.c b/src/main.c
index 41893d8..4ace1dd 100644
--- a/src/main.c
+++ b/src/main.c
@@ -163,7 +163,8 @@ static void help(void)
 		"  -h --help\t\t\tPrint this help message\n"
 		"  -V --version\t\t\tPrint the version number\n"
 		"  -v --verbose\t\t\tPrint verbose debug statements\n"
-		"  -l --list\t\t\tList currently attached DFU capable devices\n");
+		"  -l --list\t\t\tList currently attached DFU capable devices\n"
+		"  -L --leave\t\t\tLeave DFU mode without flashing and run program at address\n");
 	fprintf(stderr, "  -e --detach\t\t\tDetach currently attached DFU capable devices\n"
 		"  -E --detach-delay seconds\tTime to wait before reopening a device after detach\n"
 		"  -d --device <vendor>:<product>[,<vendor_dfu>:<product_dfu>]\n"
@@ -201,6 +202,7 @@ static struct option opts[] = {
 	{ "help", 0, 0, 'h' },
 	{ "version", 0, 0, 'V' },
 	{ "verbose", 0, 0, 'v' },
+	{ "leave", 0, 0, 'L' },
 	{ "list", 0, 0, 'l' },
 	{ "detach", 0, 0, 'e' },
 	{ "detach-delay", 1, 0, 'E' },
@@ -249,7 +251,7 @@ int main(int argc, char **argv)

 	while (1) {
 		int c, option_index = 0;
-		c = getopt_long(argc, argv, "hVvleE:d:p:c:i:a:S:t:U:D:Rs:Z:w", opts,
+		c = getopt_long(argc, argv, "hVvLleE:d:p:c:i:a:S:t:U:D:Rs:Z:w", opts,
 				&option_index);
 		if (c == -1)
 			break;
@@ -264,6 +266,9 @@ int main(int argc, char **argv)
 		case 'v':
 			verbose++;
 			break;
+		case 'L':
+			mode = MODE_LEAVE;
+			break;
 		case 'l':
 			mode = MODE_LIST;
 			break;
@@ -638,6 +643,16 @@ status_again:
 	}

 	switch (mode) {
+	case MODE_LEAVE:
+		if (dfuse_device || dfuse_options || file.bcdDFU == 0x11a) {
+			if (dfuse_do_leave(dfu_root, dfuse_options) < 0)
+				exit(1);
+		} else {
+			errx(EX_IOERR, "Non-dfuse leave unsupported");
+			exit(1);
+		}
+		break;
+
 	case MODE_UPLOAD:
 		/* open for "exclusive" writing */
 		fd = open(file.name, O_WRONLY | O_BINARY | O_CREAT | O_EXCL | O_TRUNC, 0666);
--
2.17.1