The OMAP3 supports uploading the first stage bootloader via USB.
The ROM leaves the MUSB controller enabled and it can then be used
to upload a 2nd stage image. This patch adds the omap3-usb-loader tool
and the necessary barebox support to upload the 2nd stage image.

The omap usb loader tool is downloaded from 
https://github.com/grant-h/omap_loader
and changed to also accept CHSETTINGS images.

Signed-off-by: Sascha Hauer <[email protected]>
---
 arch/arm/mach-omap/Kconfig                      |  15 +
 arch/arm/mach-omap/Makefile                     |   2 +
 arch/arm/mach-omap/include/mach/omap3-generic.h |   2 +
 arch/arm/mach-omap/omap3_xload_usb.c            | 185 +++++
 arch/arm/mach-omap/xload.c                      |   8 +-
 scripts/Makefile                                |   3 +
 scripts/omap3-usb-loader.c                      | 921 ++++++++++++++++++++++++
 7 files changed, 1134 insertions(+), 2 deletions(-)
 create mode 100644 arch/arm/mach-omap/omap3_xload_usb.c
 create mode 100644 scripts/omap3-usb-loader.c

diff --git a/arch/arm/mach-omap/Kconfig b/arch/arm/mach-omap/Kconfig
index bc00d5b..c5c8a69 100644
--- a/arch/arm/mach-omap/Kconfig
+++ b/arch/arm/mach-omap/Kconfig
@@ -128,6 +128,21 @@ config OMAP4_USBBOOT
          You need the utility program omap4_usbboot to boot from USB.
          Please read omap4_usb_booting.txt for more information.
 
+config OMAP3_USBBOOT
+       bool "enable booting from USB"
+       depends on ARCH_OMAP3
+       help
+         Say Y here if you want to be able to boot the 2nd stage via USB. This
+         works by transferring the 2nd stage image using the MUSB controller
+         which is already initialized by the ROM code. Use the omap3-usb-loader
+         tool selectable below to upload images.
+
+config OMAP3_USB_LOADER
+       bool "enable omap3 USB loader host tool"
+       depends on ARCH_OMAP3
+       help
+         Say Y here to build the omap3 usb loader tool.
+
 config OMAP_SERIALBOOT
        bool "enable booting from serial"
        select XYMODEM
diff --git a/arch/arm/mach-omap/Makefile b/arch/arm/mach-omap/Makefile
index bef1d05..8265649 100644
--- a/arch/arm/mach-omap/Makefile
+++ b/arch/arm/mach-omap/Makefile
@@ -31,6 +31,8 @@ obj-$(CONFIG_OMAP_GPMC) += gpmc.o devices-gpmc-nand.o
 obj-$(CONFIG_SHELL_NONE) += xload.o
 obj-$(CONFIG_MFD_TWL6030) += omap4_twl6030_mmc.o
 obj-$(CONFIG_OMAP4_USBBOOT) += omap4_rom_usb.o
+obj-$(CONFIG_OMAP3_USBBOOT) += omap3_xload_usb.o
+pbl-$(CONFIG_OMAP3_USBBOOT) += omap3_xload_usb.o
 obj-$(CONFIG_CMD_BOOT_ORDER) += boot_order.o
 obj-$(CONFIG_BAREBOX_UPDATE_AM33XX_SPI_NOR_MLO) += am33xx_bbu_spi_mlo.o
 obj-$(CONFIG_BAREBOX_UPDATE_AM33XX_NAND) += am33xx_bbu_nand.o
diff --git a/arch/arm/mach-omap/include/mach/omap3-generic.h 
b/arch/arm/mach-omap/include/mach/omap3-generic.h
index 7db0838..ab53b98 100644
--- a/arch/arm/mach-omap/include/mach/omap3-generic.h
+++ b/arch/arm/mach-omap/include/mach/omap3-generic.h
@@ -29,4 +29,6 @@ void __noreturn omap3_reset_cpu(unsigned long addr);
 int omap3_init(void);
 int omap3_devices_init(void);
 
+void *omap3_xload_boot_usb(void);
+
 #endif /* __MACH_OMAP3_GENERIC_H */
diff --git a/arch/arm/mach-omap/omap3_xload_usb.c 
b/arch/arm/mach-omap/omap3_xload_usb.c
new file mode 100644
index 0000000..e7dc21e
--- /dev/null
+++ b/arch/arm/mach-omap/omap3_xload_usb.c
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2015 Sascha Hauer <[email protected]>, Pengutronix
+ *
+ * Based on a patch by:
+ *
+ * Copyright (C) 2011 Rick Bronson
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <common.h>
+#include <io.h>
+#include <malloc.h>
+#include <mach/omap3-silicon.h>
+#include <mach/omap3-generic.h>
+
+static void __iomem *omap3_usb_base = (void __iomem *)OMAP3_MUSB0_BASE;
+
+#define OMAP34XX_USB_EP(n)         (omap3_usb_base + 0x100 + 0x10 * (n))
+
+#define MUSB_RXCSR             0x06
+#define MUSB_RXCOUNT           0x08
+#define MUSB_TXCSR             0x02
+#define MUSB_FIFOSIZE          0x0F
+#define OMAP34XX_USB_RXCSR(n)      (OMAP34XX_USB_EP(n) + MUSB_RXCSR)
+#define OMAP34XX_USB_RXCOUNT(n)    (OMAP34XX_USB_EP(n) + MUSB_RXCOUNT)
+#define OMAP34XX_USB_TXCSR(n)      (OMAP34XX_USB_EP(n) + MUSB_TXCSR)
+#define OMAP34XX_USB_FIFOSIZE(n)   (OMAP34XX_USB_EP(n) + MUSB_FIFOSIZE)
+#define OMAP34XX_USB_FIFO(n)       (omap3_usb_base + 0x20 + ((n) * 4))
+
+/* memory mapped registers */
+#define BULK_ENDPOINT 1
+#define MUSB_RXCSR_RXPKTRDY            0x0001
+#define MUSB_TXCSR_TXPKTRDY            0x0001
+
+#define PACK4(a,b,c,d)     (((d) << 24) | ((c) << 16) | ((b) << 8) | (a))
+#define USBLOAD_CMD_FILE PACK4('U', 'S', 'B', 's')  /* send file size */
+#define USBLOAD_CMD_JUMP PACK4('U', 'S', 'B', 'j')  /* go where I tell you */
+#define USBLOAD_CMD_FILE_REQ PACK4('U', 'S', 'B', 'f')  /* file request */
+#define USBLOAD_CMD_ECHO_SZ PACK4('U', 'S', 'B', 'n')  /* echo file size */
+#define USBLOAD_CMD_REPORT_SZ PACK4('U', 'S', 'B', 'o')  /* report file size */
+#define USBLOAD_CMD_MESSAGE PACK4('U', 'S', 'B', 'm')  /* message for debug */
+
+static int usb_send(unsigned char *buffer, unsigned int buffer_size)
+{
+       unsigned int cntr;
+       u16 txcsr;
+       void __iomem *reg = (void *)OMAP34XX_USB_TXCSR(BULK_ENDPOINT);
+       void __iomem *bulk_fifo = (void *)OMAP34XX_USB_FIFO(BULK_ENDPOINT);
+
+       txcsr = readw(reg);
+
+       if (txcsr & MUSB_TXCSR_TXPKTRDY)
+               return 0;
+
+       for (cntr = 0; cntr < buffer_size; cntr++)
+               writeb(buffer[cntr], bulk_fifo);
+
+       txcsr = readw(reg);
+       txcsr |= MUSB_TXCSR_TXPKTRDY;
+       writew(txcsr, reg);
+
+       return buffer_size;
+}
+
+static int usb_recv(u8 *buffer)
+{
+       int cntr;
+       u16 count = 0;
+       u16 rxcsr;
+       void __iomem *reg = (void *)OMAP34XX_USB_RXCSR(BULK_ENDPOINT);
+       void __iomem *bulk_fifo = (void *)OMAP34XX_USB_FIFO(BULK_ENDPOINT);
+
+       rxcsr = readw(reg);
+
+       if (!(rxcsr & MUSB_RXCSR_RXPKTRDY))
+               return 0;
+
+       count = readw((void *)OMAP34XX_USB_RXCOUNT(BULK_ENDPOINT));
+       for (cntr = 0; cntr < count; cntr++)
+               *buffer++ = readb(bulk_fifo);
+
+       /* Clear the RXPKTRDY bit */
+       rxcsr = readw(reg);
+       rxcsr &= ~MUSB_RXCSR_RXPKTRDY;
+       writew(rxcsr, reg);
+
+       return count;
+}
+
+static unsigned char usb_outbuffer[64];
+
+static void usb_msg(unsigned int cmd, const char *msg)
+{
+       unsigned char *p_char = usb_outbuffer;
+
+       *(int *)p_char = cmd;
+
+       p_char += sizeof(cmd);
+
+       if (msg) {
+               while (*msg)
+                       *p_char++= *msg++;
+               *p_char++= 0;
+       }
+
+       usb_send(usb_outbuffer, p_char - usb_outbuffer);
+}
+
+static void usb_code(unsigned int cmd, u32 code)
+{
+       unsigned int *p_int = (unsigned int *)usb_outbuffer;
+
+       *p_int++ = cmd;
+       *p_int++ = code;
+       usb_send (usb_outbuffer, ((unsigned char *) p_int) - usb_outbuffer);
+}
+
+void *omap3_xload_boot_usb(void)
+{
+       int res;
+       void *buf;
+       u32 *buf32;
+       u32 total;
+       void *addr;
+       u32 bytes;
+       int size;
+       int cntr;
+       void *fn;
+       u8 __buf[512];
+
+       buf32 = buf = __buf;
+
+       usb_msg (USBLOAD_CMD_FILE_REQ, "file req");
+       for (cntr = 0; cntr < 10000000; cntr++)  {
+               size = usb_recv(buf);
+               if (!size)
+                       continue;
+
+               switch (buf32[0]) {
+               case USBLOAD_CMD_FILE:
+                       pr_debug ("USBLOAD_CMD_FILE total = %d size = 0x%x addr 
= 0x%x\n",
+                                       res, buf32[1], buf32[2]);
+                       total = buf32[1];  /* get size and address */
+                       addr = (void *)buf32[2];
+                       usb_code(USBLOAD_CMD_ECHO_SZ, total);
+
+                       bytes = 0;
+
+                       while (bytes < total) {
+                               size = usb_recv(buf);
+                               memcpy(addr, buf, size);
+                               addr += size;
+                               bytes += size;
+                       }
+
+                       usb_code(USBLOAD_CMD_REPORT_SZ, total);  /* tell him we 
got this many bytes */
+                       usb_msg (USBLOAD_CMD_FILE_REQ, "file req");  /* see if 
they have another file for us */
+                       break;
+               case USBLOAD_CMD_JUMP:
+                       pr_debug("USBLOAD_CMD_JUMP total = %d addr = 0x%x val = 
0x%x\n",
+                                       res, buf32[0], buf32[1]);
+                       fn = (void *)buf32[1];
+                       goto out;
+               default:
+                       break;
+               }
+       }
+
+       fn = NULL;
+out:
+
+       return fn;
+}
diff --git a/arch/arm/mach-omap/xload.c b/arch/arm/mach-omap/xload.c
index 85c9120..4a0714e 100644
--- a/arch/arm/mach-omap/xload.c
+++ b/arch/arm/mach-omap/xload.c
@@ -14,6 +14,7 @@
 #include <xymodem.h>
 #include <mach/generic.h>
 #include <mach/am33xx-generic.h>
+#include <mach/omap3-generic.h>
 #include <net.h>
 #include <environment.h>
 #include <dhcp.h>
@@ -284,13 +285,16 @@ static __noreturn int omap_xload(void)
                func = omap_xload_boot_mmc();
                break;
        case BOOTSOURCE_USB:
-               if (IS_ENABLED(CONFIG_FS_OMAP4_USBBOOT)) {
+               if (IS_ENABLED(CONFIG_OMAP3_USBBOOT) && cpu_is_omap3()) {
+                       printf("booting from USB\n");
+                       func = omap3_xload_boot_usb();
+               } else if (IS_ENABLED(CONFIG_FS_OMAP4_USBBOOT)) {
                        printf("booting from USB\n");
                        func = omap4_xload_boot_usb();
-                       break;
                } else {
                        printf("booting from USB not enabled\n");
                }
+               break;
        case BOOTSOURCE_NAND:
                printf("booting from NAND\n");
                func = omap_xload_boot_nand(barebox_part->nand_offset,
diff --git a/scripts/Makefile b/scripts/Makefile
index 74c2213..a3f6222 100644
--- a/scripts/Makefile
+++ b/scripts/Makefile
@@ -24,6 +24,9 @@ HOSTLOADLIBES_mxsimage  = `pkg-config --libs openssl`
 HOSTCFLAGS_mxs-usb-loader.o = `pkg-config --cflags libusb-1.0`
 HOSTLOADLIBES_mxs-usb-loader  = `pkg-config --libs libusb-1.0`
 hostprogs-$(CONFIG_ARCH_MXS_USBLOADER)  += mxs-usb-loader
+HOSTCFLAGS_omap3-usb-loader.o = `pkg-config --cflags libusb-1.0`
+HOSTLOADLIBES_omap3-usb-loader  = `pkg-config --libs libusb-1.0`
+hostprogs-$(CONFIG_OMAP3_USB_LOADER)  += omap3-usb-loader
 
 subdir-y                       += mod
 subdir-$(CONFIG_OMAP4_USBBOOT) += omap4_usbboot
diff --git a/scripts/omap3-usb-loader.c b/scripts/omap3-usb-loader.c
new file mode 100644
index 0000000..edf6043
--- /dev/null
+++ b/scripts/omap3-usb-loader.c
@@ -0,0 +1,921 @@
+/*
+ * OMAP Loader, a USB uploader application targeted at OMAP3 processors
+ * Copyright (C) 2008 Martin Mueller <[email protected]>
+ * Copyright (C) 2014 Grant Hernandez <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <string.h>
+#include <stdbool.h>
+
+/*
+ * Reasons for the name change: this is a complete rewrite of
+ * the unversioned omap3_usbload so to lower ambiguity the name was changed.
+ * The GPLv2 license specifies rewrites as derived work.
+ */
+#define PROG_NAME "OMAP Loader"
+#define VERSION "1.0.0"
+
+#if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#define OMAP_IS_BIG_ENDIAN
+#endif
+
+#ifdef OMAP_IS_BIG_ENDIAN
+#include <arpa/inet.h>
+#endif
+
+#include <unistd.h>            /* for usleep and friends */
+#include <getopt.h>
+#include <errno.h>
+#include <libgen.h>            /* for basename */
+
+#include <libusb-1.0/libusb.h>         /* the main event */
+
+/* Device specific defines (OMAP)
+ * Primary source: http://www.ti.com/lit/pdf/sprugn4
+ * Section 26.4.5 "Peripheral Booting"
+ */
+#define OMAP_BASE_ADDRESS 0x40200000
+#define OMAP_PERIPH_BOOT 0xF0030002
+#define OMAP_VENDOR_ID 0x0451
+#define OMAP_PRODUCT_ID 0xD00E
+/* TODO: dynamically discover these endpoints */
+#define OMAP_USB_BULK_IN 0x81
+#define OMAP_USB_BULK_OUT 0x01
+#define OMAP_ASIC_ID_LEN 69
+
+#ifdef OMAP_IS_BIG_ENDIAN
+#define cpu_to_le32(v) (((v & 0xff) << 24) | ((v & 0xff00) << 8) | \
+    ((v & 0xff0000) >> 8) | ((v & 0xff000000) >> 24))
+#define le32_to_cpu(v) cpu_to_le32(v)
+#else
+#define cpu_to_le32(v) (v)
+#define le32_to_cpu(v) (v)
+#endif
+
+/*
+ * taken from x-loader/drivers/usb/usb.c
+ * All credit to Martin Mueller
+ */
+#define PACK4(a,b,c,d) (((d)<<24) | ((c)<<16) | ((b)<<8) | (a))
+#define USBLOAD_CMD_FILE PACK4('U','S','B','s')                /* file size 
request */
+#define USBLOAD_CMD_FILE_REQ PACK4('U','S','B','f')    /* file size resp */
+#define USBLOAD_CMD_JUMP PACK4('U','S','B','j')                /* execute code 
here */
+#define USBLOAD_CMD_ECHO_SZ PACK4('U','S','B','n')     /* file size confirm to 
*/
+#define USBLOAD_CMD_REPORT_SZ PACK4('U','S','B','o')   /* confirm full file */
+#define USBLOAD_CMD_MESSAGE PACK4('U','S','B','m')     /* debug message */
+
+/* USB transfer characteristics */
+#define USB_MAX_WAIT 5000
+#define USB_TIMEOUT 1000
+#define USB_MAX_TIMEOUTS (USB_MAX_WAIT/USB_TIMEOUT)
+
+/* stores the data and attributes of a file to upload in to memory */
+struct file_upload {
+       size_t size;
+       void *data;
+       uint32_t addr;
+       char *path;
+       char *basename;
+};
+
+/* stores all of the arguments read in by getopt in main() */
+struct arg_state {
+       struct file_upload **files;
+       int numfiles;
+       uint32_t jumptarget;
+       uint16_t vendor, product;
+};
+
+static int g_verbose = 0;
+
+static void log_error(char *fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       fprintf(stdout, "[-] ");
+       vfprintf(stdout, fmt, va);
+       va_end(va);
+}
+
+static void log_info(char *fmt, ...)
+{
+       va_list va;
+
+       va_start(va, fmt);
+       fprintf(stdout, "[+] ");
+       vfprintf(stdout, fmt, va);
+       va_end(va);
+}
+
+static bool omap_usb_read(libusb_device_handle *handle, unsigned char *data,
+             int length, int *actuallength)
+{
+       int ret = 0;
+       int iter = 0;
+       int sizeleft = length;
+
+       if (!actuallength)
+               return false;
+
+       while (sizeleft > 0) {
+               int actualread = 0;
+               int readamt = sizeleft;
+
+               ret = libusb_bulk_transfer(handle, OMAP_USB_BULK_IN, data + 
iter,
+                                        readamt, &actualread, USB_TIMEOUT);
+
+               if (ret == LIBUSB_ERROR_TIMEOUT) {
+                       sizeleft -= actualread;
+                       iter += actualread;
+
+                       /* we got some data, lets cut our losses and stop here 
*/
+                       if (iter > 0)
+                               break;
+
+                       log_error("device timed out while transfering in %d 
bytes (got %d)\n",
+                                       length, iter);
+
+                       return false;
+               } else if (ret == LIBUSB_SUCCESS) {
+                       /* we cant trust actualRead on anything but a timeout 
or success */
+                       sizeleft -= actualread;
+                       iter += actualread;
+
+                       /* stop at the first sign of data */
+                       if (iter > 0)
+                               break;
+               } else {
+                       log_error("fatal transfer error (BULK_IN) for %d bytes 
(got %d): %s\n",
+                                       length, iter, libusb_error_name(ret));
+                       return false;
+               }
+       }
+
+       *actuallength = iter;
+
+       return true;
+}
+
+static bool omap_usb_write(libusb_device_handle *handle, void *data, int 
length)
+{
+       int ret = 0;
+       int numtimeouts = 0;
+       int iter = 0;
+       int sizeleft = length;
+
+       while (sizeleft > 0) {
+               int actualwrite = 0;
+               int writeamt = sizeleft > 512 ? 512 : sizeleft;
+
+               ret = libusb_bulk_transfer(handle, OMAP_USB_BULK_OUT, data + 
iter,
+                                        writeamt, &actualwrite, USB_TIMEOUT);
+
+               if (ret == LIBUSB_ERROR_TIMEOUT) {
+                       numtimeouts++;
+                       sizeleft -= actualwrite;
+                       iter += actualwrite;
+
+                       /* build in some reliablity */
+                       if (numtimeouts > USB_MAX_TIMEOUTS) {
+                               log_error("device timed out while transfering 
out %d bytes (%d made it)\n",
+                                               length, iter);
+                               return false;
+                       }
+               } else if (ret == LIBUSB_SUCCESS) {
+                       /* we cant trust actualWrite on anything but a timeout 
or success */
+                       sizeleft -= actualwrite;
+                       iter += actualwrite;
+               } else {
+                       log_error("fatal transfer error (BULK_OUT) for %d bytes 
(%d made it): %s\n",
+                                       length, iter, libusb_error_name(ret));
+                       return false;
+               }
+       }
+
+       return true;
+}
+
+static unsigned char *omap_usb_get_string(libusb_device_handle *handle, 
uint8_t idx)
+{
+       unsigned char *data = NULL;
+       int len = 0;
+       int ret = 0;
+
+       if (!handle)
+               return NULL;
+
+       while (true) {
+               if (!len || ret < 0) {
+                       len += 256;
+                       data = realloc(data, len);
+
+                       if (!data)
+                               return NULL;
+               }
+
+               ret = libusb_get_string_descriptor_ascii(handle, idx, data, 
len);
+
+               /* we can still recover... */
+               if (ret < 0) {
+                       if (ret == LIBUSB_ERROR_INVALID_PARAM)
+                               continue;       /* try again with an increased 
size */
+
+                       log_error("failed to lookup string index %hhu: %s\n",
+                                 idx, libusb_error_name(ret));
+
+                       /* unrecoverable */
+                       free(data);
+                       return NULL;
+               } else {
+                       /* we got something! */
+                       break;
+               }
+       }
+
+       return data;
+}
+
+uint16_t omap_products[] = {
+       0xd009,
+       0xd00f,
+};
+
+static libusb_device_handle *omap_usb_open(libusb_context *ctx, uint16_t 
vendor, uint16_t product)
+{
+       libusb_device **devlist;
+       libusb_device_handle *handle;
+       struct libusb_device_descriptor desc;
+       ssize_t count, i;
+       int ret;
+
+       log_info("scanning for USB device matching %04hx:%04hx...\n",
+                vendor, product);
+
+       while (1) {
+               if ((count = libusb_get_device_list(ctx, &devlist)) < 0) {
+                       log_error("failed to gather USB device list: %s\n",
+                                 libusb_error_name(count));
+                       return NULL;
+               }
+
+               for (i = 0; i < count; i++) {
+                       ret = libusb_get_device_descriptor(devlist[i], &desc);
+                       if (ret < 0) {
+                               log_error("failed to get USB device descriptor: 
%s\n",
+                                               libusb_error_name(ret));
+                               libusb_free_device_list(devlist, 1);
+                               return NULL;
+                       }
+
+                       if (desc.idVendor != vendor)
+                               continue;
+
+                       if (product) {
+                               if (desc.idProduct != product)
+                                       continue;
+                               goto found;
+                       }
+
+                       for (i = 0; i < sizeof(omap_products) / 
sizeof(uint16_t); i++)
+                               if (desc.idProduct == omap_products[i]) {
+                                       product = desc.idProduct;
+                                       goto found;
+                               }
+               }
+
+               libusb_free_device_list(devlist, 1);
+
+               /* nothing found yet. have a 10ms nap */
+               usleep(10000);
+       }
+found:
+       ret = libusb_open(devlist[i], &handle);
+       if (ret < 0) {
+               log_error("failed to open USB device %04hx:%04hx: %s\n",
+                               vendor, product, libusb_error_name(ret));
+               libusb_free_device_list(devlist, 1);
+               return NULL;
+       }
+
+       ret = libusb_claim_interface(handle, 0);
+       if (ret) {
+               printf("Claim failed\n");
+               return NULL;
+       }
+
+       /* grab the manufacturer and product strings for printing */
+       unsigned char *mfgstr = omap_usb_get_string(handle, desc.iManufacturer);
+       unsigned char *prodstr = omap_usb_get_string(handle, desc.iProduct);
+
+       log_info("successfully opened %04hx:%04hx (", vendor, product);
+
+       if (mfgstr) {
+               fprintf(stdout, prodstr ? "%s " : "%s", mfgstr);
+               free(mfgstr);
+       }
+
+       if (prodstr) {
+               fprintf(stdout, "%s", prodstr);
+               free(prodstr);
+       }
+
+       fprintf(stdout, ")\n");
+
+       return handle;
+}
+
+static unsigned char *read_file(char *path, size_t *readamt)
+{
+       FILE *fp = fopen(path, "rb");
+
+       if (!fp) {
+               log_error("failed to open file \'%s\': %s\n", path,
+                         strerror(errno));
+               return NULL;
+       }
+
+       unsigned char *data = NULL;
+       size_t allocsize = 0;
+       size_t iter = 0;
+
+       while (1) {
+               if (iter >= iter) {
+                       allocsize += 1024;
+                       data = realloc(data, allocsize);
+                       if (!data)
+                               return NULL;
+               }
+
+               size_t readsize = allocsize - iter;
+               size_t ret = fread(data + iter, sizeof (unsigned char), 
readsize, fp);
+
+               iter += ret;
+
+               if (ret != readsize) {
+                       if (feof(fp)) {
+                               break;
+                       } else if (ferror(fp)) {
+                               log_error("error file reading file \'%s\': 
%s\n",
+                                               path, strerror(errno));
+                               free(data);
+                               return NULL;
+                       }
+               }
+       }
+
+       /* trim the allocation down to size */
+       data = realloc(data, iter);
+       *readamt = iter;
+
+       return data;
+}
+
+static int transfer_first_stage(libusb_device_handle * handle, struct 
arg_state *args)
+{
+       unsigned char *buffer = NULL;
+       uint32_t cmd = 0;
+       uint32_t filelen = 0;
+       int bufsize = 0x200;
+       int transLen = 0;
+       int i;
+       void *data;
+       uint32_t *dbuf;
+
+       struct file_upload *file = args->files[0];
+
+       /* TODO determine buffer size based on endpoint */
+       buffer = calloc(bufsize, sizeof (unsigned char));
+       filelen = cpu_to_le32(file->size);
+
+       data = file->data;
+       dbuf = data;
+
+       if (le32toh(dbuf[5]) == 0x45534843) {
+               int chsettingssize = 512 + 2 * sizeof(uint32_t);
+
+               log_info("CHSETTINGS image detected. Skipping header\n");
+
+               data += chsettingssize;
+               filelen -= chsettingssize;
+       }
+
+       /* read the ASIC ID */
+       if (!omap_usb_read(handle, buffer, bufsize, &transLen)) {
+               log_error("failed to read ASIC ID from USB connection. "
+                         "Check your USB device!\n");
+               goto fail;
+       }
+
+       if (transLen != OMAP_ASIC_ID_LEN) {
+               log_error("got some ASIC ID, but it's not the right length, %d "
+                         "(expected %d)\n", transLen, OMAP_ASIC_ID_LEN);
+               goto fail;
+       }
+
+       /* optionally, print some ASIC ID info */
+       if (g_verbose) {
+               char *fields[] =
+                   { "Num Subblocks", "Device ID Info", "Reserved",
+                       "Ident Data", "Reserved", "CRC (4 bytes)"
+               };
+               int fieldLen[] = { 1, 7, 4, 23, 23, 11 };
+               int field = 0;
+               int nextSep = 0;
+
+               log_info("got ASIC ID - ");
+
+               for (i = 0; i < transLen; i++) {
+                       if (i == nextSep) {
+                               fprintf(stdout, "%s%s ",
+                                       (field > 0) ? ", " : "", fields[field]);
+                               nextSep += fieldLen[field];
+                               field++;
+
+                               fprintf(stdout, "[");
+                       }
+
+                       fprintf(stdout, "%02x", buffer[i]);
+
+                       if (i + 1 == nextSep)
+                               fprintf(stdout, "]");
+               }
+
+               fprintf(stdout, "\n");
+       }
+
+       /* send the peripheral boot command */
+       cmd = cpu_to_le32(OMAP_PERIPH_BOOT);
+
+       if (!omap_usb_write(handle, (unsigned char *) &cmd, sizeof (cmd))) {
+               log_error("failed to send the peripheral boot command 0x%08x\n",
+                         OMAP_PERIPH_BOOT);
+               goto fail;
+       }
+
+       /* send the length of the first file (little endian) */
+       if (!omap_usb_write
+           (handle, (unsigned char *) &filelen, sizeof (filelen))) {
+               log_error("failed to length specifier of %u to OMAP BootROM\n",
+                         filelen);
+               goto fail;
+       }
+
+       /* send the file! */
+       if (!omap_usb_write(handle, data, filelen)) {
+               log_error("failed to send file \'%s\' (size %u)\n",
+                         file->basename, filelen);
+               goto fail;
+       }
+
+       free(buffer);
+       return 1;
+
+      fail:
+       free(buffer);
+       return 0;
+}
+
+static int transfer_other_files(libusb_device_handle *handle, struct arg_state 
*args)
+{
+       uint32_t *buffer = NULL;
+       int bufsize = 128 * sizeof (*buffer);
+       int numfailures = 0;    /* build in some reliablity */
+       int maxfailures = 3;
+       int transLen = 0;
+       int curfile = 1;        /* skip the first file */
+       size_t len;
+
+       buffer = calloc(bufsize, sizeof(unsigned char));
+
+       /* handle the state machine for the X-Loader */
+       while (curfile < args->numfiles) {
+               uint32_t opcode = 0;
+               uint8_t *extra = NULL;
+               struct file_upload *f = args->files[curfile];
+
+               /* read the opcode from xloader ID */
+               if (!omap_usb_read
+                   (handle, (unsigned char *) buffer, bufsize, &transLen)) {
+                       numfailures++;
+
+                       if (numfailures >= maxfailures) {
+                               log_error("failed to read command from 
X-Loader\n");
+                               goto fail;
+                       }
+
+                       /* sleep a bit */
+                       usleep(2000 * 1000);    /* 2s */
+                       continue;       /* try the opcode read again */
+               }
+
+               if (transLen < 8) {
+                       log_error("failed to recieve enough data for the 
opcode\n");
+                       goto fail;
+               }
+
+               /* extract the opcode and extra data pointer */
+               opcode = le32_to_cpu(buffer[0]);
+               extra = (uint8_t *)buffer;
+
+               switch (opcode) {
+               case USBLOAD_CMD_FILE_REQ:
+                       /* X-loader is requesting a file to be sent */
+                       /* send the opcode, size, and addr */
+                       buffer[0] = cpu_to_le32(USBLOAD_CMD_FILE);
+                       buffer[1] = cpu_to_le32(f->size);
+                       buffer[2] = cpu_to_le32(f->addr);
+
+                       if (!omap_usb_write(handle, (unsigned char *)buffer, 
sizeof(*buffer) * 3)) {
+                               log_error("failed to send load file command to 
the X-loader\n");
+                               goto fail;
+                       }
+
+                       if (g_verbose) {
+                               log_info("uploading \'%s\' (size %zu) to 
0x%08x\n",
+                                               f->basename, f->size, f->addr);
+                       }
+
+                       break;
+               case USBLOAD_CMD_ECHO_SZ:
+                       /* X-loader confirms the size to recieve */
+                       if (buffer[1] != f->size) {
+                               log_error
+                                   ("X-loader failed to recieve the right file 
size for "
+                                    "file \'%s\' (got %u, expected %zu)\n",
+                                    f->basename, buffer[1], f->size);
+                               goto fail;
+                       }
+
+                       /* upload the raw file data */
+                       if (!omap_usb_write(handle, f->data, f->size)) {
+                               log_error
+                                   ("failed to send file \'%s\' to the 
X-loader\n",
+                                    f->basename);
+                               goto fail;
+                       }
+
+                       break;
+               case USBLOAD_CMD_REPORT_SZ:
+                       /* X-loader confirms the amount of data it recieved */
+                       if (buffer[1] != f->size) {
+                               log_error
+                                   ("X-loader failed to recieve the right 
amount of data for "
+                                    "file \'%s\' (got %u, expected %zu)\n",
+                                    f->basename, buffer[1], f->size);
+                               goto fail;
+                       }
+
+                       curfile++;      /* move on to the next file */
+                       break;
+               case USBLOAD_CMD_MESSAGE:
+                       /* X-loader debug message */
+                       len = strlen((char *)extra);
+
+                       if (len > (bufsize - sizeof (opcode) - 1))
+                               log_error("X-loader debug message not NUL 
terminated (size %zu)\n",
+                                    len);
+                       else
+                               fprintf(stdout, "X-loader Debug: %s\n", extra);
+                       break;
+               default:
+                       log_error("unknown X-Loader opcode 0x%08X (%c%c%c%c)\n",
+                                 opcode, extra[0], extra[1], extra[2],
+                                 extra[3]);
+                       goto fail;
+               }
+       }
+
+       /* we're done uploading files to X-loader send the jump command */
+       buffer[0] = cpu_to_le32(USBLOAD_CMD_JUMP);
+       buffer[1] = cpu_to_le32(args->jumptarget);
+
+       if (!omap_usb_write
+           (handle, (unsigned char *) buffer, sizeof (*buffer) * 2)) {
+               log_error
+                   ("failed to send the final jump command to the X-loader. "
+                    "Target was 0x%08x\n", args->jumptarget);
+               goto fail;
+       }
+
+       if (g_verbose)
+               log_info("jumping to address 0x%08x\n", args->jumptarget);
+
+       free(buffer);
+       return 1;
+
+      fail:
+       free(buffer);
+       return 0;
+}
+
+static int process_args(struct arg_state *args)
+{
+       int i;
+
+       /* For each file, load it in to memory
+        * TODO: defer this until transfer time (save memory and pipeline IO)
+        */
+
+       for (i = 0; i < args->numfiles; i++) {
+               struct file_upload *f = args->files[i];
+
+               f->data = read_file(f->path, &f->size);
+
+               if (!f->data) {
+                       return 1;
+               }
+       }
+
+       if (g_verbose > 0) {
+               for (i = 0; i < args->numfiles; i++) {
+                       struct file_upload *f = args->files[i];
+
+                       printf("File \'%s\' at 0x%08x, size %zu\n",
+                              f->basename, f->addr, f->size);
+               }
+       }
+
+       libusb_context *ctx;
+       libusb_device_handle *dev;
+       int ret;
+
+       if ((ret = libusb_init(&ctx)) < 0) {
+               log_error("failed to initialize libusb context: %s\n",
+                         libusb_error_name(ret));
+               return ret;
+       }
+
+       dev = omap_usb_open(ctx, args->vendor, args->product);
+
+       if (!dev) {
+               libusb_exit(ctx);
+               return 1;
+       }
+
+       /* Communicate with the TI BootROM directly
+        * - retrieve ASIC ID
+        * - start peripheral boot
+        * - upload first file
+        * - execute first file
+        */
+       if (!transfer_first_stage(dev, args)) {
+               log_error("failed to transfer the first stage file \'%s\'\n",
+                         args->files[0]->basename);
+               goto fail;
+       }
+
+       /* Note: this is a race between the target's processor getting X-loader
+        * running and our processor. If we fail to communicate with the 
X-loader,
+        * it's possible that it hasn't been fully initialized. I'm not going 
to put
+        * some stupid, arbitrary sleep value here. The transfer_other_files 
function
+        * should be robust enough to handle some errors.
+        */
+
+       /* If we are passed one file, assume that the user just wants to
+        * upload some initial code with no X-loader chaining
+        */
+       if (args->numfiles > 1) {
+               if (!transfer_other_files(dev, args)) {
+                       log_error
+                           ("failed to transfer the additional files in to 
memory\n");
+                       goto fail;
+               }
+       }
+
+       log_info("successfully transfered %d %s\n", args->numfiles,
+                (args->numfiles > 1) ? "files" : "file");
+
+       /* safely close our USB handle and context */
+       libusb_close(dev);
+       libusb_exit(ctx);
+       return 0;
+
+fail:
+       libusb_close(dev);
+       libusb_exit(ctx);
+
+       return 1;
+}
+
+/* getopt configuration */
+static int do_version = 0;
+static const char *const short_opt = "f:a:j:i:p:vh";
+static const struct option long_opt[] = {
+       {"file", 1, NULL, 'f'},
+       {"addr", 1, NULL, 'a'},
+       {"jump", 1, NULL, 'j'},
+       {"vendor", 1, NULL, 'i'},
+       {"product", 1, NULL, 'p'},
+       {"verbose", 0, NULL, 'v'},
+       {"help", 0, NULL, 'h'},
+       {"version", 0, &do_version, 1},
+       {NULL, 0, NULL, 0}
+};
+
+static void usage(char *exe)
+{
+       printf(
+"Usage: %s [options] -f first-stage [-f file -a addr]...\n"
+"Options:\n"
+"  -f, --file      Provide the filename of a binary file to be\n"
+"                  uploaded. The first file specified is uploaded to\n"
+"                  the fixed address 0x%08x as defined by the manual.\n"
+"                  Additional files must be followed by an address\n"
+"                  argument (-a).\n"
+"  -a, --addr      The address to load the prior file at.\n"
+"  -j, --jump      Specify the address to jump to after loading all\n"
+"                  of the files in to memory.\n"
+"  -i, --vendor    Override the default vendor ID to poll for\n"
+"                  (default 0x%04x).\n"
+"  -p, --product   Poll for specific product id. Default: All known OMAP 
chips\n"
+"  -h, --help      Display this message.\n"
+"  -v, --verbose   Enable verbose output.\n"
+"\n"
+"Description:\n"
+"  %s's basic usage is to upload an arbitrary file in to the memory\n"
+"  of a TI OMAP3 compatible processor. This program directly\n"
+"  communicates with the TI BootROM in order to upload a first stage\n"
+"  payload, in most cases, TI's X-Loader. Using a compatible X-Loader\n"
+"  will enable the upload of any file to any part in device memory.\n"
+"\n"
+"Examples:\n"
+"  Uploading a compatible X-Loader, U-Boot, Kernel, and RAMDisk, then 
jumping\n"
+"  to the U-Boot image for further bootloading:\n"
+"    %s -f x-load.bin -f u-boot.bin -a 0x80200000 -f uImage -a 0x80800000 \\\n"
+"       -f uRamdisk -a 0x81000000 -j 0x80200000\n"
+"  Uploading arbitrary code to be executed (doesn't have to be X-loader):\n"
+"    %s -f exec_me.bin\n"
+"  Trying to debug an upload issue using verbose output:\n"
+"    %s -v -f exec_me.bin -f file1.bin -a 0xdeadbeef -j 0xabad1dea\n"
+"\n"
+"Authors:\n"
+"  Grant Hernandez <[email protected]> - rewrite of omap3_usbload 
to\n"
+"    use the newer libusb 1.0\n"
+"  Martin Mueller <[email protected]> - initial code (omap3_usbload)\n"
+"    and X-Loader patch\n",
+       exe, OMAP_BASE_ADDRESS, OMAP_VENDOR_ID, PROG_NAME, exe, exe, exe
+       );
+}
+
+static void license(void)
+{
+       printf(
+"Copyright (C) 2008 Martin Mueller <[email protected]>\n"
+"Copyright (C) 2014 Grant Hernandez <[email protected]>\n"
+"License GPLv2: GNU GPL version 2 or later 
<http://gnu.org/licenses/gpl.html>.\n"
+"This is free software: you are free to change and redistribute it.\n"
+"There is NO WARRANTY, to the extent permitted by law.\n"
+       );
+}
+
+int main(int argc, char *argv[])
+{
+       int opt;
+       bool gotfile = false;
+       bool gotjump = false;
+       int filecount = 0;
+       char *exe = NULL;
+
+       /* temporary local file object */
+       struct file_upload file;
+       /* total arg state */
+       struct arg_state *args = calloc(1, sizeof (*args));
+
+       if (argc < 1) {
+               log_error("invalid arguments (no argv[0])\n");
+               return 1;
+       }
+
+       exe = argv[0];
+
+       fprintf(stdout, "%s %s\n", PROG_NAME, VERSION);
+
+       /* set the default vendor and product */
+       args->vendor = OMAP_VENDOR_ID;
+       args->product = 0;
+
+       while ((opt = getopt_long(argc, argv, short_opt, long_opt, NULL)) != 
-1) {
+               switch (opt) {
+               case 0:
+                       if (do_version) {
+                               license();
+                               return 0;
+                       }
+                       break;
+               case 'f':
+                       if (gotfile) {
+                               log_error("missing address argument (-a) for 
file \'%s\'\n",
+                                               file.path);
+                               usage(exe);
+                               return 1;
+                       }
+
+                       file.path = strdup(optarg);
+
+                       /* necessary to be sure that we own all the memory
+                          and that the path input can be modified */
+                       char *tmpPath = strdup(file.path);
+                       file.basename = strdup(basename(tmpPath));
+                       free(tmpPath);
+
+                       filecount++;
+
+                       /* the first file gets uploaded to a fixed address
+                          as specified by the technical reference manual */
+                       if (filecount == 1) {
+                               file.addr = OMAP_BASE_ADDRESS;
+
+                               /* commit the file object with the processor 
specified base address */
+                               args->files = realloc(args->files, filecount);
+                               args->numfiles = filecount;
+                               args->files[filecount - 1] = malloc(sizeof 
(file));
+                               memcpy(args->files[filecount - 1], &file, 
sizeof (file));
+                       } else {
+                               /* commit only after an address is specified */
+                               gotfile = true;
+                       }
+                       break;
+               case 'a':
+                       if (!gotfile) {
+                               log_error
+                                   ("missing file argument (-f) before address 
\'%s\'\n",
+                                    optarg);
+                               usage(exe);
+                               return 1;
+                       }
+
+                       /* passing 0 to strtoul enables detection of the 0x 
prefix with
+                          base-10 fallback if missing */
+                       file.addr = strtoul(optarg, NULL, 0);
+
+                       /* commit the file object */
+                       args->files = realloc(args->files, filecount);
+                       args->numfiles = filecount;
+                       args->files[filecount - 1] = malloc(sizeof(file));
+                       memcpy(args->files[filecount - 1], &file, sizeof(file));
+
+                       gotfile = false;
+                       break;
+               case 'j':
+                       args->jumptarget = strtoul(optarg, NULL, 0);
+                       gotjump = true;
+                       break;
+               case 'i':
+                       args->vendor = (uint16_t)strtoul(optarg, NULL, 0);
+                       break;
+               case 'p':
+                       args->product = (uint16_t)strtoul(optarg, NULL, 0);
+                       break;
+               case 'v':
+                       g_verbose++;
+                       break;
+               case 'h':
+                       usage(exe);
+                       return 0;
+               default:
+                       usage(exe);
+                       return 1;
+               }
+       }
+
+       if (gotfile) {
+               log_error("got file \'%s\', but no address!\n", file.path);
+               usage(exe);
+               return 1;
+       }
+
+       if (args->numfiles <= 0) {
+               log_error("at least one file needs to be specified\n");
+               usage(exe);
+               return 1;
+       }
+
+       if (args->numfiles == 1 && gotjump) {
+               log_info
+                   ("WARNING: jump target 0x%08x specified, but will never be 
taken "
+                    "(more than one file required)\n", args->jumptarget);
+       } else if (args->numfiles > 1 && !gotjump) {
+               log_info
+                   ("WARNING: no jump target specified. Defaulting to the 
first "
+                    "file's (\'%s\') address 0x%08x\n",
+                    args->files[1]->basename, args->files[1]->addr);
+               args->jumptarget = args->files[1]->addr;
+       }
+
+       return process_args(args);
+}
-- 
2.1.4


_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to