[U-Boot] [PATCH v2 0/8] imx: add USB Serial Download Protocol (SDP) support

From: Stefan Agner stefan.agner@toradex.com
This series adds NXP's Serial Download Protocol (SDP) support via USB for SPL/U-Boot. It allows to download U-Boot via USB from a (recovered) SPL using the same tools used to download SPL itself (specifically imx_usb, but also sb_loader seems to work).
The idea has been brought up when the first targets started to make use of SPL for DDR initialization, see: https://lists.denx.de/pipermail/u-boot/2015-July/220330.html
The initial SDP implementation (patch 2) requires the payload to have the imx specific headers (hence the move of the imx header file in patch 1).
Patch 3 extends image header support beyond the SDP specification, specifically implements also support for U-Boot headers. This allows to use the same SPL/U-Boot binaries for recovery as used on the regular boot device (SD/eMMC). For that to work also the host side imx_usb tools needed an extension, currently available here:
https://github.com/toradex/imx_loader/tree/imx_usb_batch_mode_refactored
(in case this patchset gets accepted in U-Boot, I plan to push these imx_usb changes upstream as well)
The full patchset allows to download SPL and U-Boot over USB to a target in recovery mode using the same usb_imx utility. Refer to the new README.sdp for details how to use usb_imx in combination with this implementation.
Changes in v2: - Changed function signature of sdp_init/sdp_handle to allow specifying which USB controller should be used. - Use #defines for security mode - Improved types used in format strings - Changed function signature of sdp_init/sdp_handle - Use BOOT_DEVICE_BOARD
Stefan Agner (8): imx: move imximage header to common location usb: gadget: add SDP driver usb: gadget: sdp: extend images compatible for jumps cmd: add sdp command spl: add serial download protocol (SDP) support doc: add Serial Download Protocol documentation apalis/colibri_imx6: use independent USB PID for SPL apalis/colibri_imx6: enable SDP by default
board/toradex/apalis_imx6/apalis_imx6.c | 13 + board/toradex/colibri_imx6/colibri_imx6.c | 13 + cmd/Kconfig | 7 + cmd/Makefile | 1 + cmd/usb_gadget_sdp.c | 50 ++ common/spl/Kconfig | 6 + common/spl/Makefile | 1 + common/spl/spl_sdp.c | 37 ++ configs/apalis_imx6_defconfig | 4 + configs/colibri_imx6_defconfig | 4 + doc/README.sdp | 100 ++++ drivers/usb/gadget/Kconfig | 7 + drivers/usb/gadget/Makefile | 2 + drivers/usb/gadget/f_sdp.c | 737 ++++++++++++++++++++++++++++++ {tools => include}/imximage.h | 0 include/sdp.h | 16 + 16 files changed, 998 insertions(+) create mode 100644 cmd/usb_gadget_sdp.c create mode 100644 common/spl/spl_sdp.c create mode 100644 doc/README.sdp create mode 100644 drivers/usb/gadget/f_sdp.c rename {tools => include}/imximage.h (100%) create mode 100644 include/sdp.h

From: Stefan Agner stefan.agner@toradex.com
Move the imximage.h header file to a common location so we can make use of it from U-Boot too.
Signed-off-by: Stefan Agner stefan.agner@toradex.com Reviewed-by: Łukasz Majewski lukma@denx.de ---
Changes in v2: None
{tools => include}/imximage.h | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {tools => include}/imximage.h (100%)
diff --git a/tools/imximage.h b/include/imximage.h similarity index 100% rename from tools/imximage.h rename to include/imximage.h

From: Stefan Agner stefan.agner@toradex.com
Add SDP (Serial Downloader Protocol) implementation for U-Boot. The protocol is used in NXP SoC's boot ROM and allows to download program images. Beside that, it can also be used to read/write registers and download complete Device Configuration Data (DCD) sets. This basic implementation supports downloading images with the imx header format reading and writing registers.
Signed-off-by: Stefan Agner stefan.agner@toradex.com ---
Changes in v2: - Changed function signature of sdp_init/sdp_handle to allow specifying which USB controller should be used. - Use #defines for security mode - Improved types used in format strings
drivers/usb/gadget/Kconfig | 7 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_sdp.c | 721 ++++++++++++++++++++++++++++++++++++++++++++ include/sdp.h | 16 + 4 files changed, 745 insertions(+) create mode 100644 drivers/usb/gadget/f_sdp.c create mode 100644 include/sdp.h
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 261ed128ac..225b66bc95 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -103,6 +103,13 @@ config USB_GADGET_DOWNLOAD
if USB_GADGET_DOWNLOAD
+config USB_FUNCTION_SDP + bool "Enable USB SDP (Serial Download Protocol)" + help + Enable Serial Download Protocol (SDP) device support in U-Boot. This + allows to download images into memory and execute (jump to) them + using the same protocol as implemented by the i.MX family's boot ROM. + config G_DNL_MANUFACTURER string "Vendor name of USB device"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5e316a7cff..6a007d1bcb 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o obj-$(CONFIG_USB_FUNCTION_DFU) += f_dfu.o obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o +obj-$(CONFIG_USB_FUNCTION_SDP) += f_sdp.o endif endif ifdef CONFIG_USB_ETHER diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c new file mode 100644 index 0000000000..9d82abcd69 --- /dev/null +++ b/drivers/usb/gadget/f_sdp.c @@ -0,0 +1,721 @@ +/* + * f_sdp.c -- USB HID Serial Download Protocol + * + * Copyright (C) 2017 Toradex + * Author: Stefan Agner stefan.agner@toradex.com + * + * This file implements the Serial Download Protocol (SDP) as specified in + * the i.MX 6 Reference Manual. The SDP is a USB HID based protocol and + * allows to download images directly to memory. The implementation + * works with the imx_loader (imx_usb) USB client software on host side. + * + * Not all commands are implemented, e.g. WRITE_REGISTER, DCD_WRITE and + * SKIP_DCD_HEADER are only stubs. + * + * Parts of the implementation are based on f_dfu and f_thor. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <errno.h> +#include <common.h> +#include <console.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h> + +#include <asm/io.h> +#include <g_dnl.h> +#include <sdp.h> +#include <imximage.h> + +#define HID_REPORT_ID_MASK 0x000000ff + +/* + * HID class requests + */ +#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_GET_IDLE 0x02 +#define HID_REQ_GET_PROTOCOL 0x03 +#define HID_REQ_SET_REPORT 0x09 +#define HID_REQ_SET_IDLE 0x0A +#define HID_REQ_SET_PROTOCOL 0x0B + +#define HID_USAGE_PAGE_LEN 76 + +struct hid_report { + u8 usage_page[HID_USAGE_PAGE_LEN]; +} __packed; + +#define SDP_READ_REGISTER 0x0101 +#define SDP_WRITE_REGISTER 0x0202 +#define SDP_WRITE_FILE 0x0404 +#define SDP_ERROR_STATUS 0x0505 +#define SDP_DCD_WRITE 0x0a0a +#define SDP_JUMP_ADDRESS 0x0b0b +#define SDP_SKIP_DCD_HEADER 0x0c0c + +#define SDP_SECURITY_CLOSED 0x12343412 +#define SDP_SECURITY_OPEN 0x56787856 + +#define SDP_WRITE_FILE_COMPLETE 0x88888888 +#define SDP_WRITE_REGISTER_COMPLETE 0x128A8A12 +#define SDP_SKIP_DCD_HEADER_COMPLETE 0x900DD009 +#define SDP_ERROR_IMXHEADER 0x000a0533 + +#define SDP_COMMAND_LEN 16 + +struct sdp_command { + u16 cmd; + u32 addr; + u8 format; + u32 cnt; + u32 data; + u8 rsvd; +} __packed; + +enum sdp_state { + SDP_STATE_IDLE, + SDP_STATE_RX_DCD_DATA, + SDP_STATE_RX_FILE_DATA, + SDP_STATE_TX_SEC_CONF, + SDP_STATE_TX_SEC_CONF_BUSY, + SDP_STATE_TX_REGISTER, + SDP_STATE_TX_REGISTER_BUSY, + SDP_STATE_TX_STATUS, + SDP_STATE_TX_STATUS_BUSY, + SDP_STATE_JUMP, +}; + +struct f_sdp { + struct usb_function usb_function; + + struct usb_descriptor_header **function; + + u8 altsetting; + enum sdp_state state; + enum sdp_state next_state; + u32 dnl_address; + u32 dnl_bytes_remaining; + u32 jmp_address; + bool always_send_status; + u32 error_status; + + /* EP0 request */ + struct usb_request *req; + + /* EP1 IN */ + struct usb_ep *in_ep; + struct usb_request *in_req; + + bool configuration_done; +}; + +static struct f_sdp *sdp_func; + +static inline struct f_sdp *func_to_sdp(struct usb_function *f) +{ + return container_of(f, struct f_sdp, usb_function); +} + +static struct usb_interface_descriptor sdp_intf_runtime = { + .bLength = sizeof(sdp_intf_runtime), + .bDescriptorType = USB_DT_INTERFACE, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HID, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + /* .iInterface = DYNAMIC */ +}; + +/* HID configuration */ +static struct usb_class_hid_descriptor sdp_hid_desc = { + .bLength = sizeof(sdp_hid_desc), + .bDescriptorType = USB_DT_CS_DEVICE, + + .bcdCDC = __constant_cpu_to_le16(0x0110), + .bCountryCode = 0, + .bNumDescriptors = 1, + + .bDescriptorType0 = USB_DT_HID_REPORT, + .wDescriptorLength0 = HID_USAGE_PAGE_LEN, +}; + +static struct usb_endpoint_descriptor in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/ + + .bEndpointAddress = 1 | USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = 64, + .bInterval = 1, +}; + +static struct usb_descriptor_header *sdp_runtime_descs[] = { + (struct usb_descriptor_header *)&sdp_intf_runtime, + (struct usb_descriptor_header *)&sdp_hid_desc, + (struct usb_descriptor_header *)&in_desc, + NULL, +}; + +/* This is synchronized with what the SoC implementation reports */ +static struct hid_report sdp_hid_report = { + .usage_page = { + 0x06, 0x00, 0xff, /* Usage Page */ + 0x09, 0x01, /* Usage (Pointer?) */ + 0xa1, 0x01, /* Collection */ + + 0x85, 0x01, /* Report ID */ + 0x19, 0x01, /* Usage Minimum */ + 0x29, 0x01, /* Usage Maximum */ + 0x15, 0x00, /* Local Minimum */ + 0x26, 0xFF, 0x00, /* Local Maximum? */ + 0x75, 0x08, /* Report Size */ + 0x95, 0x10, /* Report Count */ + 0x91, 0x02, /* Output Data */ + + 0x85, 0x02, /* Report ID */ + 0x19, 0x01, /* Usage Minimum */ + 0x29, 0x01, /* Usage Maximum */ + 0x15, 0x00, /* Local Minimum */ + 0x26, 0xFF, 0x00, /* Local Maximum? */ + 0x75, 0x80, /* Report Size 128 */ + 0x95, 0x40, /* Report Count */ + 0x91, 0x02, /* Output Data */ + + 0x85, 0x03, /* Report ID */ + 0x19, 0x01, /* Usage Minimum */ + 0x29, 0x01, /* Usage Maximum */ + 0x15, 0x00, /* Local Minimum */ + 0x26, 0xFF, 0x00, /* Local Maximum? */ + 0x75, 0x08, /* Report Size 8 */ + 0x95, 0x04, /* Report Count */ + 0x81, 0x02, /* Input Data */ + + 0x85, 0x04, /* Report ID */ + 0x19, 0x01, /* Usage Minimum */ + 0x29, 0x01, /* Usage Maximum */ + 0x15, 0x00, /* Local Minimum */ + 0x26, 0xFF, 0x00, /* Local Maximum? */ + 0x75, 0x08, /* Report Size 8 */ + 0x95, 0x40, /* Report Count */ + 0x81, 0x02, /* Input Data */ + 0xc0 + }, +}; + +static const char sdp_name[] = "Serial Downloader Protocol"; + +/* + * static strings, in UTF-8 + */ +static struct usb_string strings_sdp_generic[] = { + [0].s = sdp_name, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab_sdp_generic = { + .language = 0x0409, /* en-us */ + .strings = strings_sdp_generic, +}; + +static struct usb_gadget_strings *sdp_generic_strings[] = { + &stringtab_sdp_generic, + NULL, +}; + +static void sdp_rx_command_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_sdp *sdp = req->context; + int status = req->status; + u8 *data = req->buf; + u8 report = data[0]; + + if (status != 0) { + error("Status: %d", status); + return; + } + + if (report != 1) { + error("Unexpected report %d", report); + return; + } + + struct sdp_command *cmd = req->buf + 1; + + debug("%s: command: %04x, addr: %08x, cnt: %u\n", + __func__, be16_to_cpu(cmd->cmd), + be32_to_cpu(cmd->addr), be32_to_cpu(cmd->cnt)); + + switch (be16_to_cpu(cmd->cmd)) { + case SDP_READ_REGISTER: + sdp->always_send_status = false; + sdp->error_status = 0x0; + + sdp->state = SDP_STATE_TX_SEC_CONF; + sdp->dnl_address = be32_to_cpu(cmd->addr); + sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt); + sdp->next_state = SDP_STATE_TX_REGISTER; + printf("Reading %d registers at 0x%08x... ", + sdp->dnl_bytes_remaining, sdp->dnl_address); + break; + case SDP_WRITE_FILE: + sdp->always_send_status = true; + sdp->error_status = SDP_WRITE_FILE_COMPLETE; + + sdp->state = SDP_STATE_RX_FILE_DATA; + sdp->dnl_address = be32_to_cpu(cmd->addr); + sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt); + sdp->next_state = SDP_STATE_IDLE; + + printf("Downloading file of size %d to 0x%08x... ", + sdp->dnl_bytes_remaining, sdp->dnl_address); + + break; + case SDP_ERROR_STATUS: + sdp->always_send_status = true; + sdp->error_status = 0; + + sdp->state = SDP_STATE_TX_SEC_CONF; + sdp->next_state = SDP_STATE_IDLE; + break; + case SDP_DCD_WRITE: + sdp->always_send_status = true; + sdp->error_status = SDP_WRITE_REGISTER_COMPLETE; + + sdp->state = SDP_STATE_RX_DCD_DATA; + sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt); + sdp->next_state = SDP_STATE_IDLE; + break; + case SDP_JUMP_ADDRESS: + sdp->always_send_status = false; + sdp->error_status = 0; + + sdp->jmp_address = be32_to_cpu(cmd->addr); + sdp->state = SDP_STATE_TX_SEC_CONF; + sdp->next_state = SDP_STATE_JUMP; + break; + case SDP_SKIP_DCD_HEADER: + sdp->always_send_status = true; + sdp->error_status = SDP_SKIP_DCD_HEADER_COMPLETE; + + /* Ignore command, DCD not supported anyway */ + sdp->state = SDP_STATE_TX_SEC_CONF; + sdp->next_state = SDP_STATE_IDLE; + break; + default: + error("Unknown command: %04x\n", be16_to_cpu(cmd->cmd)); + } +} + +static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_sdp *sdp = req->context; + int status = req->status; + u8 *data = req->buf; + u8 report = data[0]; + int datalen = req->length - 1; + + if (status != 0) { + error("Status: %d", status); + return; + } + + if (report != 2) { + error("Unexpected report %d", report); + return; + } + + if (sdp->dnl_bytes_remaining < datalen) { + /* + * Some USB stacks require to send a complete buffer as + * specified in the HID descriptor. This leads to longer + * transfers than the file length, no problem for us. + */ + sdp->dnl_bytes_remaining = 0; + } else { + sdp->dnl_bytes_remaining -= datalen; + } + + if (sdp->state == SDP_STATE_RX_FILE_DATA) { + memcpy((void *)sdp->dnl_address, req->buf + 1, datalen); + sdp->dnl_address += datalen; + } + + if (sdp->dnl_bytes_remaining) + return; + + printf("done\n"); + + switch (sdp->state) { + case SDP_STATE_RX_FILE_DATA: + sdp->state = SDP_STATE_TX_SEC_CONF; + break; + case SDP_STATE_RX_DCD_DATA: + sdp->state = SDP_STATE_TX_SEC_CONF; + break; + default: + error("Invalid state: %d", sdp->state); + } +} + +static void sdp_tx_complete(struct usb_ep *ep, struct usb_request *req) +{ + struct f_sdp *sdp = req->context; + int status = req->status; + + if (status != 0) { + error("Status: %d", status); + return; + } + + switch (sdp->state) { + case SDP_STATE_TX_SEC_CONF_BUSY: + /* Not all commands require status report */ + if (sdp->always_send_status || sdp->error_status) + sdp->state = SDP_STATE_TX_STATUS; + else + sdp->state = sdp->next_state; + + break; + case SDP_STATE_TX_STATUS_BUSY: + sdp->state = sdp->next_state; + break; + case SDP_STATE_TX_REGISTER_BUSY: + if (sdp->dnl_bytes_remaining) + sdp->state = SDP_STATE_TX_REGISTER; + else + sdp->state = SDP_STATE_IDLE; + break; + default: + error("Wrong State: %d", sdp->state); + sdp->state = SDP_STATE_IDLE; + break; + } + debug("%s complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); +} + +static int sdp_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{ + struct usb_gadget *gadget = f->config->cdev->gadget; + struct usb_request *req = f->config->cdev->req; + struct f_sdp *sdp = f->config->cdev->req->context; + u16 len = le16_to_cpu(ctrl->wLength); + u16 w_value = le16_to_cpu(ctrl->wValue); + int value = 0; + u8 req_type = ctrl->bRequestType & USB_TYPE_MASK; + + debug("w_value: 0x%04x len: 0x%04x\n", w_value, len); + debug("req_type: 0x%02x ctrl->bRequest: 0x%02x sdp->state: %d\n", + req_type, ctrl->bRequest, sdp->state); + + if (req_type == USB_TYPE_STANDARD) { + if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) { + /* Send HID report descriptor */ + value = min(len, (u16) sizeof(sdp_hid_report)); + memcpy(req->buf, &sdp_hid_report, value); + sdp->configuration_done = true; + } + } + + if (req_type == USB_TYPE_CLASS) { + int report = w_value & HID_REPORT_ID_MASK; + + /* HID (SDP) request */ + switch (ctrl->bRequest) { + case HID_REQ_SET_REPORT: + switch (report) { + case 1: + value = SDP_COMMAND_LEN + 1; + req->complete = sdp_rx_command_complete; + break; + case 2: + value = len; + req->complete = sdp_rx_data_complete; + break; + } + } + } + + if (value >= 0) { + req->length = value; + req->zero = value < len; + value = usb_ep_queue(gadget->ep0, req, 0); + if (value < 0) { + debug("ep_queue --> %d\n", value); + req->status = 0; + } + } + + return value; +} + +static int sdp_bind(struct usb_configuration *c, struct usb_function *f) +{ + struct usb_gadget *gadget = c->cdev->gadget; + struct usb_composite_dev *cdev = c->cdev; + struct f_sdp *sdp = func_to_sdp(f); + int rv = 0, id; + + id = usb_interface_id(c, f); + if (id < 0) + return id; + sdp_intf_runtime.bInterfaceNumber = id; + + struct usb_ep *ep; + + /* allocate instance-specific endpoints */ + ep = usb_ep_autoconfig(gadget, &in_desc); + if (!ep) { + rv = -ENODEV; + goto error; + } + + sdp->in_ep = ep; /* Store IN EP for enabling @ setup */ + + cdev->req->context = sdp; + +error: + return rv; +} + +static void sdp_unbind(struct usb_configuration *c, struct usb_function *f) +{ + free(sdp_func); + sdp_func = NULL; +} + +static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{ + struct usb_request *req; + + req = usb_ep_alloc_request(ep, 0); + if (!req) + return req; + + req->length = length; + req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length); + if (!req->buf) { + usb_ep_free_request(ep, req); + req = NULL; + } + + return req; +} + + +static struct usb_request *sdp_start_ep(struct usb_ep *ep) +{ + struct usb_request *req; + + req = alloc_ep_req(ep, 64); + debug("%s: ep:%p req:%p\n", __func__, ep, req); + + if (!req) + return NULL; + + memset(req->buf, 0, req->length); + req->complete = sdp_tx_complete; + + return req; +} +static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{ + struct f_sdp *sdp = func_to_sdp(f); + struct usb_composite_dev *cdev = f->config->cdev; + int result; + + debug("%s: intf: %d alt: %d\n", __func__, intf, alt); + + result = usb_ep_enable(sdp->in_ep, &in_desc); + if (result) + return result; + sdp->in_req = sdp_start_ep(sdp->in_ep); + sdp->in_req->context = sdp; + + sdp->in_ep->driver_data = cdev; /* claim */ + + sdp->altsetting = alt; + sdp->state = SDP_STATE_IDLE; + + return 0; +} + +static int sdp_get_alt(struct usb_function *f, unsigned intf) +{ + struct f_sdp *sdp = func_to_sdp(f); + + return sdp->altsetting; +} + +static void sdp_disable(struct usb_function *f) +{ + struct f_sdp *sdp = func_to_sdp(f); + + usb_ep_disable(sdp->in_ep); + + if (sdp->in_req) { + free(sdp->in_req); + sdp->in_req = NULL; + } +} + +static int sdp_bind_config(struct usb_configuration *c) +{ + int status; + + if (!sdp_func) { + sdp_func = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*sdp_func)); + if (!sdp_func) + return -ENOMEM; + } + + memset(sdp_func, 0, sizeof(*sdp_func)); + + sdp_func->usb_function.name = "sdp"; + sdp_func->usb_function.hs_descriptors = sdp_runtime_descs; + sdp_func->usb_function.descriptors = sdp_runtime_descs; + sdp_func->usb_function.bind = sdp_bind; + sdp_func->usb_function.unbind = sdp_unbind; + sdp_func->usb_function.set_alt = sdp_set_alt; + sdp_func->usb_function.get_alt = sdp_get_alt; + sdp_func->usb_function.disable = sdp_disable; + sdp_func->usb_function.strings = sdp_generic_strings; + sdp_func->usb_function.setup = sdp_setup; + + status = usb_add_function(c, &sdp_func->usb_function); + + return status; +} + +int sdp_init(int controller_index) +{ + printf("SDP: initialize...\n"); + while (!sdp_func->configuration_done) { + if (ctrlc()) { + puts("\rCTRL+C - Operation aborted.\n"); + return 1; + } + usb_gadget_handle_interrupts(controller_index); + } + + return 0; +} + +static u32 sdp_jump_imxheader(void *address) +{ + flash_header_v2_t *headerv2 = address; + ulong (*entry)(void); + + if (headerv2->header.tag != IVT_HEADER_TAG) { + printf("Header Tag is not an IMX image\n"); + return SDP_ERROR_IMXHEADER; + } + + printf("Jumping to 0x%08x\n", headerv2->entry); + entry = (void *)headerv2->entry; + entry(); + + /* The image probably never returns hence we won't reach that point */ + return 0; +} + +static void sdp_handle_in_ep(void) +{ + u8 *data = sdp_func->in_req->buf; + u32 status; + int datalen; + + switch (sdp_func->state) { + case SDP_STATE_TX_SEC_CONF: + debug("Report 3: HAB security\n"); + data[0] = 3; + + status = SDP_SECURITY_OPEN; + memcpy(&data[1], &status, 4); + sdp_func->in_req->length = 5; + usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0); + sdp_func->state = SDP_STATE_TX_SEC_CONF_BUSY; + break; + + case SDP_STATE_TX_STATUS: + debug("Report 4: Status\n"); + data[0] = 4; + + memcpy(&data[1], &sdp_func->error_status, 4); + sdp_func->in_req->length = 65; + usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0); + sdp_func->state = SDP_STATE_TX_STATUS_BUSY; + break; + case SDP_STATE_TX_REGISTER: + debug("Report 4: Register Values\n"); + data[0] = 4; + + datalen = sdp_func->dnl_bytes_remaining; + + if (datalen > 64) + datalen = 64; + + memcpy(&data[1], (void *)sdp_func->dnl_address, datalen); + sdp_func->in_req->length = 65; + + sdp_func->dnl_bytes_remaining -= datalen; + sdp_func->dnl_address += datalen; + + usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0); + sdp_func->state = SDP_STATE_TX_REGISTER_BUSY; + break; + case SDP_STATE_JUMP: + printf("Checking imxheader at 0x%08x\n", f_sdp->jmp_address); + status = sdp_jump_imxheader((void *)f_sdp->jmp_address); + + sdp_func->next_state = SDP_STATE_IDLE; + sdp_func->error_status = status; + + /* Only send Report 4 if there was an error */ + if (status) + sdp_func->state = SDP_STATE_TX_STATUS; + else + sdp_func->state = SDP_STATE_IDLE; + break; + default: + break; + }; +} + +void sdp_handle(int controller_index) +{ + printf("SDP: handle requests...\n"); + while (1) { + if (ctrlc()) { + puts("\rCTRL+C - Operation aborted.\n"); + return; + } + + usb_gadget_handle_interrupts(controller_index); + + sdp_handle_in_ep(); + } +} + +int sdp_add(struct usb_configuration *c) +{ + int id; + + id = usb_string_id(c->cdev); + if (id < 0) + return id; + strings_sdp_generic[0].id = id; + sdp_intf_runtime.iInterface = id; + + debug("%s: cdev: %p gadget: %p gadget->ep0: %p\n", __func__, + c->cdev, c->cdev->gadget, c->cdev->gadget->ep0); + + return sdp_bind_config(c); +} + +DECLARE_GADGET_BIND_CALLBACK(usb_dnl_sdp, sdp_add); diff --git a/include/sdp.h b/include/sdp.h new file mode 100644 index 0000000000..f476bab8f1 --- /dev/null +++ b/include/sdp.h @@ -0,0 +1,16 @@ +/* + * sdp.h - Serial Download Protocol + * + * Copyright (C) 2017 Toradex + * Author: Stefan Agner stefan.agner@toradex.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __SDP_H_ +#define __SDP_H_ + +int sdp_init(int controller_index); +void sdp_handle(int controller_index); + +#endif /* __SDP_H_ */

On 08/16/2017 08:00 PM, Stefan Agner wrote:
From: Stefan Agner stefan.agner@toradex.com
Add SDP (Serial Downloader Protocol) implementation for U-Boot. The protocol is used in NXP SoC's boot ROM and allows to download program images. Beside that, it can also be used to read/write registers and download complete Device Configuration Data (DCD) sets. This basic implementation supports downloading images with the imx header format reading and writing registers.
Signed-off-by: Stefan Agner stefan.agner@toradex.com
Reviewed-by: Łukasz Majewski lukma@denx.de
Changes in v2:
- Changed function signature of sdp_init/sdp_handle to allow specifying which USB controller should be used.
- Use #defines for security mode
- Improved types used in format strings
drivers/usb/gadget/Kconfig | 7 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_sdp.c | 721 ++++++++++++++++++++++++++++++++++++++++++++ include/sdp.h | 16 + 4 files changed, 745 insertions(+) create mode 100644 drivers/usb/gadget/f_sdp.c create mode 100644 include/sdp.h
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 261ed128ac..225b66bc95 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -103,6 +103,13 @@ config USB_GADGET_DOWNLOAD
if USB_GADGET_DOWNLOAD
+config USB_FUNCTION_SDP
- bool "Enable USB SDP (Serial Download Protocol)"
- help
Enable Serial Download Protocol (SDP) device support in U-Boot. This
allows to download images into memory and execute (jump to) them
using the same protocol as implemented by the i.MX family's boot ROM.
config G_DNL_MANUFACTURER string "Vendor name of USB device"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5e316a7cff..6a007d1bcb 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o obj-$(CONFIG_USB_FUNCTION_DFU) += f_dfu.o obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o +obj-$(CONFIG_USB_FUNCTION_SDP) += f_sdp.o endif endif ifdef CONFIG_USB_ETHER diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c new file mode 100644 index 0000000000..9d82abcd69 --- /dev/null +++ b/drivers/usb/gadget/f_sdp.c @@ -0,0 +1,721 @@ +/*
- f_sdp.c -- USB HID Serial Download Protocol
- Copyright (C) 2017 Toradex
- Author: Stefan Agner stefan.agner@toradex.com
- This file implements the Serial Download Protocol (SDP) as specified in
- the i.MX 6 Reference Manual. The SDP is a USB HID based protocol and
- allows to download images directly to memory. The implementation
- works with the imx_loader (imx_usb) USB client software on host side.
- Not all commands are implemented, e.g. WRITE_REGISTER, DCD_WRITE and
- SKIP_DCD_HEADER are only stubs.
- Parts of the implementation are based on f_dfu and f_thor.
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <errno.h> +#include <common.h> +#include <console.h> +#include <malloc.h>
+#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h>
+#include <asm/io.h> +#include <g_dnl.h> +#include <sdp.h> +#include <imximage.h>
+#define HID_REPORT_ID_MASK 0x000000ff
+/*
- HID class requests
- */
+#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_GET_IDLE 0x02 +#define HID_REQ_GET_PROTOCOL 0x03 +#define HID_REQ_SET_REPORT 0x09 +#define HID_REQ_SET_IDLE 0x0A +#define HID_REQ_SET_PROTOCOL 0x0B
+#define HID_USAGE_PAGE_LEN 76
+struct hid_report {
- u8 usage_page[HID_USAGE_PAGE_LEN];
+} __packed;
+#define SDP_READ_REGISTER 0x0101 +#define SDP_WRITE_REGISTER 0x0202 +#define SDP_WRITE_FILE 0x0404 +#define SDP_ERROR_STATUS 0x0505 +#define SDP_DCD_WRITE 0x0a0a +#define SDP_JUMP_ADDRESS 0x0b0b +#define SDP_SKIP_DCD_HEADER 0x0c0c
+#define SDP_SECURITY_CLOSED 0x12343412 +#define SDP_SECURITY_OPEN 0x56787856
+#define SDP_WRITE_FILE_COMPLETE 0x88888888 +#define SDP_WRITE_REGISTER_COMPLETE 0x128A8A12 +#define SDP_SKIP_DCD_HEADER_COMPLETE 0x900DD009 +#define SDP_ERROR_IMXHEADER 0x000a0533
+#define SDP_COMMAND_LEN 16
+struct sdp_command {
- u16 cmd;
- u32 addr;
- u8 format;
- u32 cnt;
- u32 data;
- u8 rsvd;
+} __packed;
+enum sdp_state {
- SDP_STATE_IDLE,
- SDP_STATE_RX_DCD_DATA,
- SDP_STATE_RX_FILE_DATA,
- SDP_STATE_TX_SEC_CONF,
- SDP_STATE_TX_SEC_CONF_BUSY,
- SDP_STATE_TX_REGISTER,
- SDP_STATE_TX_REGISTER_BUSY,
- SDP_STATE_TX_STATUS,
- SDP_STATE_TX_STATUS_BUSY,
- SDP_STATE_JUMP,
+};
+struct f_sdp {
- struct usb_function usb_function;
- struct usb_descriptor_header **function;
- u8 altsetting;
- enum sdp_state state;
- enum sdp_state next_state;
- u32 dnl_address;
- u32 dnl_bytes_remaining;
- u32 jmp_address;
- bool always_send_status;
- u32 error_status;
- /* EP0 request */
- struct usb_request *req;
- /* EP1 IN */
- struct usb_ep *in_ep;
- struct usb_request *in_req;
- bool configuration_done;
+};
+static struct f_sdp *sdp_func;
+static inline struct f_sdp *func_to_sdp(struct usb_function *f) +{
- return container_of(f, struct f_sdp, usb_function);
+}
+static struct usb_interface_descriptor sdp_intf_runtime = {
- .bLength = sizeof(sdp_intf_runtime),
- .bDescriptorType = USB_DT_INTERFACE,
- .bAlternateSetting = 0,
- .bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_HID,
- .bInterfaceSubClass = 0,
- .bInterfaceProtocol = 0,
- /* .iInterface = DYNAMIC */
+};
+/* HID configuration */ +static struct usb_class_hid_descriptor sdp_hid_desc = {
- .bLength = sizeof(sdp_hid_desc),
- .bDescriptorType = USB_DT_CS_DEVICE,
- .bcdCDC = __constant_cpu_to_le16(0x0110),
- .bCountryCode = 0,
- .bNumDescriptors = 1,
- .bDescriptorType0 = USB_DT_HID_REPORT,
- .wDescriptorLength0 = HID_USAGE_PAGE_LEN,
+};
+static struct usb_endpoint_descriptor in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
- .bEndpointAddress = 1 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = 64,
- .bInterval = 1,
+};
+static struct usb_descriptor_header *sdp_runtime_descs[] = {
- (struct usb_descriptor_header *)&sdp_intf_runtime,
- (struct usb_descriptor_header *)&sdp_hid_desc,
- (struct usb_descriptor_header *)&in_desc,
- NULL,
+};
+/* This is synchronized with what the SoC implementation reports */ +static struct hid_report sdp_hid_report = {
- .usage_page = {
0x06, 0x00, 0xff, /* Usage Page */
0x09, 0x01, /* Usage (Pointer?) */
0xa1, 0x01, /* Collection */
0x85, 0x01, /* Report ID */
0x19, 0x01, /* Usage Minimum */
0x29, 0x01, /* Usage Maximum */
0x15, 0x00, /* Local Minimum */
0x26, 0xFF, 0x00, /* Local Maximum? */
0x75, 0x08, /* Report Size */
0x95, 0x10, /* Report Count */
0x91, 0x02, /* Output Data */
0x85, 0x02, /* Report ID */
0x19, 0x01, /* Usage Minimum */
0x29, 0x01, /* Usage Maximum */
0x15, 0x00, /* Local Minimum */
0x26, 0xFF, 0x00, /* Local Maximum? */
0x75, 0x80, /* Report Size 128 */
0x95, 0x40, /* Report Count */
0x91, 0x02, /* Output Data */
0x85, 0x03, /* Report ID */
0x19, 0x01, /* Usage Minimum */
0x29, 0x01, /* Usage Maximum */
0x15, 0x00, /* Local Minimum */
0x26, 0xFF, 0x00, /* Local Maximum? */
0x75, 0x08, /* Report Size 8 */
0x95, 0x04, /* Report Count */
0x81, 0x02, /* Input Data */
0x85, 0x04, /* Report ID */
0x19, 0x01, /* Usage Minimum */
0x29, 0x01, /* Usage Maximum */
0x15, 0x00, /* Local Minimum */
0x26, 0xFF, 0x00, /* Local Maximum? */
0x75, 0x08, /* Report Size 8 */
0x95, 0x40, /* Report Count */
0x81, 0x02, /* Input Data */
0xc0
- },
+};
+static const char sdp_name[] = "Serial Downloader Protocol";
+/*
- static strings, in UTF-8
- */
+static struct usb_string strings_sdp_generic[] = {
- [0].s = sdp_name,
- { } /* end of list */
+};
+static struct usb_gadget_strings stringtab_sdp_generic = {
- .language = 0x0409, /* en-us */
- .strings = strings_sdp_generic,
+};
+static struct usb_gadget_strings *sdp_generic_strings[] = {
- &stringtab_sdp_generic,
- NULL,
+};
+static void sdp_rx_command_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct f_sdp *sdp = req->context;
- int status = req->status;
- u8 *data = req->buf;
- u8 report = data[0];
- if (status != 0) {
error("Status: %d", status);
return;
- }
- if (report != 1) {
error("Unexpected report %d", report);
return;
- }
- struct sdp_command *cmd = req->buf + 1;
- debug("%s: command: %04x, addr: %08x, cnt: %u\n",
__func__, be16_to_cpu(cmd->cmd),
be32_to_cpu(cmd->addr), be32_to_cpu(cmd->cnt));
- switch (be16_to_cpu(cmd->cmd)) {
- case SDP_READ_REGISTER:
sdp->always_send_status = false;
sdp->error_status = 0x0;
sdp->state = SDP_STATE_TX_SEC_CONF;
sdp->dnl_address = be32_to_cpu(cmd->addr);
sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
sdp->next_state = SDP_STATE_TX_REGISTER;
printf("Reading %d registers at 0x%08x... ",
sdp->dnl_bytes_remaining, sdp->dnl_address);
break;
- case SDP_WRITE_FILE:
sdp->always_send_status = true;
sdp->error_status = SDP_WRITE_FILE_COMPLETE;
sdp->state = SDP_STATE_RX_FILE_DATA;
sdp->dnl_address = be32_to_cpu(cmd->addr);
sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
sdp->next_state = SDP_STATE_IDLE;
printf("Downloading file of size %d to 0x%08x... ",
sdp->dnl_bytes_remaining, sdp->dnl_address);
break;
- case SDP_ERROR_STATUS:
sdp->always_send_status = true;
sdp->error_status = 0;
sdp->state = SDP_STATE_TX_SEC_CONF;
sdp->next_state = SDP_STATE_IDLE;
break;
- case SDP_DCD_WRITE:
sdp->always_send_status = true;
sdp->error_status = SDP_WRITE_REGISTER_COMPLETE;
sdp->state = SDP_STATE_RX_DCD_DATA;
sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
sdp->next_state = SDP_STATE_IDLE;
break;
- case SDP_JUMP_ADDRESS:
sdp->always_send_status = false;
sdp->error_status = 0;
sdp->jmp_address = be32_to_cpu(cmd->addr);
sdp->state = SDP_STATE_TX_SEC_CONF;
sdp->next_state = SDP_STATE_JUMP;
break;
- case SDP_SKIP_DCD_HEADER:
sdp->always_send_status = true;
sdp->error_status = SDP_SKIP_DCD_HEADER_COMPLETE;
/* Ignore command, DCD not supported anyway */
sdp->state = SDP_STATE_TX_SEC_CONF;
sdp->next_state = SDP_STATE_IDLE;
break;
- default:
error("Unknown command: %04x\n", be16_to_cpu(cmd->cmd));
- }
+}
+static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct f_sdp *sdp = req->context;
- int status = req->status;
- u8 *data = req->buf;
- u8 report = data[0];
- int datalen = req->length - 1;
- if (status != 0) {
error("Status: %d", status);
return;
- }
- if (report != 2) {
error("Unexpected report %d", report);
return;
- }
- if (sdp->dnl_bytes_remaining < datalen) {
/*
* Some USB stacks require to send a complete buffer as
* specified in the HID descriptor. This leads to longer
* transfers than the file length, no problem for us.
*/
sdp->dnl_bytes_remaining = 0;
- } else {
sdp->dnl_bytes_remaining -= datalen;
- }
- if (sdp->state == SDP_STATE_RX_FILE_DATA) {
memcpy((void *)sdp->dnl_address, req->buf + 1, datalen);
sdp->dnl_address += datalen;
- }
- if (sdp->dnl_bytes_remaining)
return;
- printf("done\n");
- switch (sdp->state) {
- case SDP_STATE_RX_FILE_DATA:
sdp->state = SDP_STATE_TX_SEC_CONF;
break;
- case SDP_STATE_RX_DCD_DATA:
sdp->state = SDP_STATE_TX_SEC_CONF;
break;
- default:
error("Invalid state: %d", sdp->state);
- }
+}
+static void sdp_tx_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct f_sdp *sdp = req->context;
- int status = req->status;
- if (status != 0) {
error("Status: %d", status);
return;
- }
- switch (sdp->state) {
- case SDP_STATE_TX_SEC_CONF_BUSY:
/* Not all commands require status report */
if (sdp->always_send_status || sdp->error_status)
sdp->state = SDP_STATE_TX_STATUS;
else
sdp->state = sdp->next_state;
break;
- case SDP_STATE_TX_STATUS_BUSY:
sdp->state = sdp->next_state;
break;
- case SDP_STATE_TX_REGISTER_BUSY:
if (sdp->dnl_bytes_remaining)
sdp->state = SDP_STATE_TX_REGISTER;
else
sdp->state = SDP_STATE_IDLE;
break;
- default:
error("Wrong State: %d", sdp->state);
sdp->state = SDP_STATE_IDLE;
break;
- }
- debug("%s complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
+}
+static int sdp_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{
- struct usb_gadget *gadget = f->config->cdev->gadget;
- struct usb_request *req = f->config->cdev->req;
- struct f_sdp *sdp = f->config->cdev->req->context;
- u16 len = le16_to_cpu(ctrl->wLength);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- int value = 0;
- u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
- debug("w_value: 0x%04x len: 0x%04x\n", w_value, len);
- debug("req_type: 0x%02x ctrl->bRequest: 0x%02x sdp->state: %d\n",
req_type, ctrl->bRequest, sdp->state);
- if (req_type == USB_TYPE_STANDARD) {
if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) {
/* Send HID report descriptor */
value = min(len, (u16) sizeof(sdp_hid_report));
memcpy(req->buf, &sdp_hid_report, value);
sdp->configuration_done = true;
}
- }
- if (req_type == USB_TYPE_CLASS) {
int report = w_value & HID_REPORT_ID_MASK;
/* HID (SDP) request */
switch (ctrl->bRequest) {
case HID_REQ_SET_REPORT:
switch (report) {
case 1:
value = SDP_COMMAND_LEN + 1;
req->complete = sdp_rx_command_complete;
break;
case 2:
value = len;
req->complete = sdp_rx_data_complete;
break;
}
}
- }
- if (value >= 0) {
req->length = value;
req->zero = value < len;
value = usb_ep_queue(gadget->ep0, req, 0);
if (value < 0) {
debug("ep_queue --> %d\n", value);
req->status = 0;
}
- }
- return value;
+}
+static int sdp_bind(struct usb_configuration *c, struct usb_function *f) +{
- struct usb_gadget *gadget = c->cdev->gadget;
- struct usb_composite_dev *cdev = c->cdev;
- struct f_sdp *sdp = func_to_sdp(f);
- int rv = 0, id;
- id = usb_interface_id(c, f);
- if (id < 0)
return id;
- sdp_intf_runtime.bInterfaceNumber = id;
- struct usb_ep *ep;
- /* allocate instance-specific endpoints */
- ep = usb_ep_autoconfig(gadget, &in_desc);
- if (!ep) {
rv = -ENODEV;
goto error;
- }
- sdp->in_ep = ep; /* Store IN EP for enabling @ setup */
- cdev->req->context = sdp;
+error:
- return rv;
+}
+static void sdp_unbind(struct usb_configuration *c, struct usb_function *f) +{
- free(sdp_func);
- sdp_func = NULL;
+}
+static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{
- struct usb_request *req;
- req = usb_ep_alloc_request(ep, 0);
- if (!req)
return req;
- req->length = length;
- req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length);
- if (!req->buf) {
usb_ep_free_request(ep, req);
req = NULL;
- }
- return req;
+}
+static struct usb_request *sdp_start_ep(struct usb_ep *ep) +{
- struct usb_request *req;
- req = alloc_ep_req(ep, 64);
- debug("%s: ep:%p req:%p\n", __func__, ep, req);
- if (!req)
return NULL;
- memset(req->buf, 0, req->length);
- req->complete = sdp_tx_complete;
- return req;
+} +static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{
- struct f_sdp *sdp = func_to_sdp(f);
- struct usb_composite_dev *cdev = f->config->cdev;
- int result;
- debug("%s: intf: %d alt: %d\n", __func__, intf, alt);
- result = usb_ep_enable(sdp->in_ep, &in_desc);
- if (result)
return result;
- sdp->in_req = sdp_start_ep(sdp->in_ep);
- sdp->in_req->context = sdp;
- sdp->in_ep->driver_data = cdev; /* claim */
- sdp->altsetting = alt;
- sdp->state = SDP_STATE_IDLE;
- return 0;
+}
+static int sdp_get_alt(struct usb_function *f, unsigned intf) +{
- struct f_sdp *sdp = func_to_sdp(f);
- return sdp->altsetting;
+}
+static void sdp_disable(struct usb_function *f) +{
- struct f_sdp *sdp = func_to_sdp(f);
- usb_ep_disable(sdp->in_ep);
- if (sdp->in_req) {
free(sdp->in_req);
sdp->in_req = NULL;
- }
+}
+static int sdp_bind_config(struct usb_configuration *c) +{
- int status;
- if (!sdp_func) {
sdp_func = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*sdp_func));
if (!sdp_func)
return -ENOMEM;
- }
- memset(sdp_func, 0, sizeof(*sdp_func));
- sdp_func->usb_function.name = "sdp";
- sdp_func->usb_function.hs_descriptors = sdp_runtime_descs;
- sdp_func->usb_function.descriptors = sdp_runtime_descs;
- sdp_func->usb_function.bind = sdp_bind;
- sdp_func->usb_function.unbind = sdp_unbind;
- sdp_func->usb_function.set_alt = sdp_set_alt;
- sdp_func->usb_function.get_alt = sdp_get_alt;
- sdp_func->usb_function.disable = sdp_disable;
- sdp_func->usb_function.strings = sdp_generic_strings;
- sdp_func->usb_function.setup = sdp_setup;
- status = usb_add_function(c, &sdp_func->usb_function);
- return status;
+}
+int sdp_init(int controller_index) +{
- printf("SDP: initialize...\n");
- while (!sdp_func->configuration_done) {
if (ctrlc()) {
puts("\rCTRL+C - Operation aborted.\n");
return 1;
}
usb_gadget_handle_interrupts(controller_index);
- }
- return 0;
+}
+static u32 sdp_jump_imxheader(void *address) +{
- flash_header_v2_t *headerv2 = address;
- ulong (*entry)(void);
- if (headerv2->header.tag != IVT_HEADER_TAG) {
printf("Header Tag is not an IMX image\n");
return SDP_ERROR_IMXHEADER;
- }
- printf("Jumping to 0x%08x\n", headerv2->entry);
- entry = (void *)headerv2->entry;
- entry();
- /* The image probably never returns hence we won't reach that point */
- return 0;
+}
+static void sdp_handle_in_ep(void) +{
- u8 *data = sdp_func->in_req->buf;
- u32 status;
- int datalen;
- switch (sdp_func->state) {
- case SDP_STATE_TX_SEC_CONF:
debug("Report 3: HAB security\n");
data[0] = 3;
status = SDP_SECURITY_OPEN;
memcpy(&data[1], &status, 4);
sdp_func->in_req->length = 5;
usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
sdp_func->state = SDP_STATE_TX_SEC_CONF_BUSY;
break;
- case SDP_STATE_TX_STATUS:
debug("Report 4: Status\n");
data[0] = 4;
memcpy(&data[1], &sdp_func->error_status, 4);
sdp_func->in_req->length = 65;
usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
sdp_func->state = SDP_STATE_TX_STATUS_BUSY;
break;
- case SDP_STATE_TX_REGISTER:
debug("Report 4: Register Values\n");
data[0] = 4;
datalen = sdp_func->dnl_bytes_remaining;
if (datalen > 64)
datalen = 64;
memcpy(&data[1], (void *)sdp_func->dnl_address, datalen);
sdp_func->in_req->length = 65;
sdp_func->dnl_bytes_remaining -= datalen;
sdp_func->dnl_address += datalen;
usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
sdp_func->state = SDP_STATE_TX_REGISTER_BUSY;
break;
- case SDP_STATE_JUMP:
printf("Checking imxheader at 0x%08x\n", f_sdp->jmp_address);
status = sdp_jump_imxheader((void *)f_sdp->jmp_address);
sdp_func->next_state = SDP_STATE_IDLE;
sdp_func->error_status = status;
/* Only send Report 4 if there was an error */
if (status)
sdp_func->state = SDP_STATE_TX_STATUS;
else
sdp_func->state = SDP_STATE_IDLE;
break;
- default:
break;
- };
+}
+void sdp_handle(int controller_index) +{
- printf("SDP: handle requests...\n");
- while (1) {
if (ctrlc()) {
puts("\rCTRL+C - Operation aborted.\n");
return;
}
usb_gadget_handle_interrupts(controller_index);
sdp_handle_in_ep();
- }
+}
+int sdp_add(struct usb_configuration *c) +{
- int id;
- id = usb_string_id(c->cdev);
- if (id < 0)
return id;
- strings_sdp_generic[0].id = id;
- sdp_intf_runtime.iInterface = id;
- debug("%s: cdev: %p gadget: %p gadget->ep0: %p\n", __func__,
c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
- return sdp_bind_config(c);
+}
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_sdp, sdp_add); diff --git a/include/sdp.h b/include/sdp.h new file mode 100644 index 0000000000..f476bab8f1 --- /dev/null +++ b/include/sdp.h @@ -0,0 +1,16 @@ +/*
- sdp.h - Serial Download Protocol
- Copyright (C) 2017 Toradex
- Author: Stefan Agner stefan.agner@toradex.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __SDP_H_ +#define __SDP_H_
+int sdp_init(int controller_index); +void sdp_handle(int controller_index);
+#endif /* __SDP_H_ */

On 16/08/2017 20:00, Stefan Agner wrote:
From: Stefan Agner stefan.agner@toradex.com
Add SDP (Serial Downloader Protocol) implementation for U-Boot. The protocol is used in NXP SoC's boot ROM and allows to download program images. Beside that, it can also be used to read/write registers and download complete Device Configuration Data (DCD) sets. This basic implementation supports downloading images with the imx header format reading and writing registers.
Signed-off-by: Stefan Agner stefan.agner@toradex.com
Reviewed by : Stefano Babic ysbabic@denx.de>
Marek, this is related to USB. Anyway, I will prefer that the whole patchset will be merged at once, and not split between u-boot-imx and u-boot-usb. If you agree (and after the patchset is ok for you, of course !), I will propose that I will merge the whole patchset into u-boot-imx to avoid breakages.
Best regards, Stefano
Changes in v2:
- Changed function signature of sdp_init/sdp_handle to allow specifying which USB controller should be used.
- Use #defines for security mode
- Improved types used in format strings
drivers/usb/gadget/Kconfig | 7 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/f_sdp.c | 721 ++++++++++++++++++++++++++++++++++++++++++++ include/sdp.h | 16 + 4 files changed, 745 insertions(+) create mode 100644 drivers/usb/gadget/f_sdp.c create mode 100644 include/sdp.h
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 261ed128ac..225b66bc95 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -103,6 +103,13 @@ config USB_GADGET_DOWNLOAD
if USB_GADGET_DOWNLOAD
+config USB_FUNCTION_SDP
- bool "Enable USB SDP (Serial Download Protocol)"
- help
Enable Serial Download Protocol (SDP) device support in U-Boot. This
allows to download images into memory and execute (jump to) them
using the same protocol as implemented by the i.MX family's boot ROM.
config G_DNL_MANUFACTURER string "Vendor name of USB device"
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5e316a7cff..6a007d1bcb 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o obj-$(CONFIG_USB_FUNCTION_DFU) += f_dfu.o obj-$(CONFIG_USB_FUNCTION_MASS_STORAGE) += f_mass_storage.o obj-$(CONFIG_USB_FUNCTION_FASTBOOT) += f_fastboot.o +obj-$(CONFIG_USB_FUNCTION_SDP) += f_sdp.o endif endif ifdef CONFIG_USB_ETHER diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c new file mode 100644 index 0000000000..9d82abcd69 --- /dev/null +++ b/drivers/usb/gadget/f_sdp.c @@ -0,0 +1,721 @@ +/*
- f_sdp.c -- USB HID Serial Download Protocol
- Copyright (C) 2017 Toradex
- Author: Stefan Agner stefan.agner@toradex.com
- This file implements the Serial Download Protocol (SDP) as specified in
- the i.MX 6 Reference Manual. The SDP is a USB HID based protocol and
- allows to download images directly to memory. The implementation
- works with the imx_loader (imx_usb) USB client software on host side.
- Not all commands are implemented, e.g. WRITE_REGISTER, DCD_WRITE and
- SKIP_DCD_HEADER are only stubs.
- Parts of the implementation are based on f_dfu and f_thor.
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <errno.h> +#include <common.h> +#include <console.h> +#include <malloc.h>
+#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/usb/composite.h>
+#include <asm/io.h> +#include <g_dnl.h> +#include <sdp.h> +#include <imximage.h>
+#define HID_REPORT_ID_MASK 0x000000ff
+/*
- HID class requests
- */
+#define HID_REQ_GET_REPORT 0x01 +#define HID_REQ_GET_IDLE 0x02 +#define HID_REQ_GET_PROTOCOL 0x03 +#define HID_REQ_SET_REPORT 0x09 +#define HID_REQ_SET_IDLE 0x0A +#define HID_REQ_SET_PROTOCOL 0x0B
+#define HID_USAGE_PAGE_LEN 76
+struct hid_report {
- u8 usage_page[HID_USAGE_PAGE_LEN];
+} __packed;
+#define SDP_READ_REGISTER 0x0101 +#define SDP_WRITE_REGISTER 0x0202 +#define SDP_WRITE_FILE 0x0404 +#define SDP_ERROR_STATUS 0x0505 +#define SDP_DCD_WRITE 0x0a0a +#define SDP_JUMP_ADDRESS 0x0b0b +#define SDP_SKIP_DCD_HEADER 0x0c0c
+#define SDP_SECURITY_CLOSED 0x12343412 +#define SDP_SECURITY_OPEN 0x56787856
+#define SDP_WRITE_FILE_COMPLETE 0x88888888 +#define SDP_WRITE_REGISTER_COMPLETE 0x128A8A12 +#define SDP_SKIP_DCD_HEADER_COMPLETE 0x900DD009 +#define SDP_ERROR_IMXHEADER 0x000a0533
+#define SDP_COMMAND_LEN 16
+struct sdp_command {
- u16 cmd;
- u32 addr;
- u8 format;
- u32 cnt;
- u32 data;
- u8 rsvd;
+} __packed;
+enum sdp_state {
- SDP_STATE_IDLE,
- SDP_STATE_RX_DCD_DATA,
- SDP_STATE_RX_FILE_DATA,
- SDP_STATE_TX_SEC_CONF,
- SDP_STATE_TX_SEC_CONF_BUSY,
- SDP_STATE_TX_REGISTER,
- SDP_STATE_TX_REGISTER_BUSY,
- SDP_STATE_TX_STATUS,
- SDP_STATE_TX_STATUS_BUSY,
- SDP_STATE_JUMP,
+};
+struct f_sdp {
- struct usb_function usb_function;
- struct usb_descriptor_header **function;
- u8 altsetting;
- enum sdp_state state;
- enum sdp_state next_state;
- u32 dnl_address;
- u32 dnl_bytes_remaining;
- u32 jmp_address;
- bool always_send_status;
- u32 error_status;
- /* EP0 request */
- struct usb_request *req;
- /* EP1 IN */
- struct usb_ep *in_ep;
- struct usb_request *in_req;
- bool configuration_done;
+};
+static struct f_sdp *sdp_func;
+static inline struct f_sdp *func_to_sdp(struct usb_function *f) +{
- return container_of(f, struct f_sdp, usb_function);
+}
+static struct usb_interface_descriptor sdp_intf_runtime = {
- .bLength = sizeof(sdp_intf_runtime),
- .bDescriptorType = USB_DT_INTERFACE,
- .bAlternateSetting = 0,
- .bNumEndpoints = 1,
- .bInterfaceClass = USB_CLASS_HID,
- .bInterfaceSubClass = 0,
- .bInterfaceProtocol = 0,
- /* .iInterface = DYNAMIC */
+};
+/* HID configuration */ +static struct usb_class_hid_descriptor sdp_hid_desc = {
- .bLength = sizeof(sdp_hid_desc),
- .bDescriptorType = USB_DT_CS_DEVICE,
- .bcdCDC = __constant_cpu_to_le16(0x0110),
- .bCountryCode = 0,
- .bNumDescriptors = 1,
- .bDescriptorType0 = USB_DT_HID_REPORT,
- .wDescriptorLength0 = HID_USAGE_PAGE_LEN,
+};
+static struct usb_endpoint_descriptor in_desc = {
- .bLength = USB_DT_ENDPOINT_SIZE,
- .bDescriptorType = USB_DT_ENDPOINT, /*USB_DT_CS_ENDPOINT*/
- .bEndpointAddress = 1 | USB_DIR_IN,
- .bmAttributes = USB_ENDPOINT_XFER_INT,
- .wMaxPacketSize = 64,
- .bInterval = 1,
+};
+static struct usb_descriptor_header *sdp_runtime_descs[] = {
- (struct usb_descriptor_header *)&sdp_intf_runtime,
- (struct usb_descriptor_header *)&sdp_hid_desc,
- (struct usb_descriptor_header *)&in_desc,
- NULL,
+};
+/* This is synchronized with what the SoC implementation reports */ +static struct hid_report sdp_hid_report = {
- .usage_page = {
0x06, 0x00, 0xff, /* Usage Page */
0x09, 0x01, /* Usage (Pointer?) */
0xa1, 0x01, /* Collection */
0x85, 0x01, /* Report ID */
0x19, 0x01, /* Usage Minimum */
0x29, 0x01, /* Usage Maximum */
0x15, 0x00, /* Local Minimum */
0x26, 0xFF, 0x00, /* Local Maximum? */
0x75, 0x08, /* Report Size */
0x95, 0x10, /* Report Count */
0x91, 0x02, /* Output Data */
0x85, 0x02, /* Report ID */
0x19, 0x01, /* Usage Minimum */
0x29, 0x01, /* Usage Maximum */
0x15, 0x00, /* Local Minimum */
0x26, 0xFF, 0x00, /* Local Maximum? */
0x75, 0x80, /* Report Size 128 */
0x95, 0x40, /* Report Count */
0x91, 0x02, /* Output Data */
0x85, 0x03, /* Report ID */
0x19, 0x01, /* Usage Minimum */
0x29, 0x01, /* Usage Maximum */
0x15, 0x00, /* Local Minimum */
0x26, 0xFF, 0x00, /* Local Maximum? */
0x75, 0x08, /* Report Size 8 */
0x95, 0x04, /* Report Count */
0x81, 0x02, /* Input Data */
0x85, 0x04, /* Report ID */
0x19, 0x01, /* Usage Minimum */
0x29, 0x01, /* Usage Maximum */
0x15, 0x00, /* Local Minimum */
0x26, 0xFF, 0x00, /* Local Maximum? */
0x75, 0x08, /* Report Size 8 */
0x95, 0x40, /* Report Count */
0x81, 0x02, /* Input Data */
0xc0
- },
+};
+static const char sdp_name[] = "Serial Downloader Protocol";
+/*
- static strings, in UTF-8
- */
+static struct usb_string strings_sdp_generic[] = {
- [0].s = sdp_name,
- { } /* end of list */
+};
+static struct usb_gadget_strings stringtab_sdp_generic = {
- .language = 0x0409, /* en-us */
- .strings = strings_sdp_generic,
+};
+static struct usb_gadget_strings *sdp_generic_strings[] = {
- &stringtab_sdp_generic,
- NULL,
+};
+static void sdp_rx_command_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct f_sdp *sdp = req->context;
- int status = req->status;
- u8 *data = req->buf;
- u8 report = data[0];
- if (status != 0) {
error("Status: %d", status);
return;
- }
- if (report != 1) {
error("Unexpected report %d", report);
return;
- }
- struct sdp_command *cmd = req->buf + 1;
- debug("%s: command: %04x, addr: %08x, cnt: %u\n",
__func__, be16_to_cpu(cmd->cmd),
be32_to_cpu(cmd->addr), be32_to_cpu(cmd->cnt));
- switch (be16_to_cpu(cmd->cmd)) {
- case SDP_READ_REGISTER:
sdp->always_send_status = false;
sdp->error_status = 0x0;
sdp->state = SDP_STATE_TX_SEC_CONF;
sdp->dnl_address = be32_to_cpu(cmd->addr);
sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
sdp->next_state = SDP_STATE_TX_REGISTER;
printf("Reading %d registers at 0x%08x... ",
sdp->dnl_bytes_remaining, sdp->dnl_address);
break;
- case SDP_WRITE_FILE:
sdp->always_send_status = true;
sdp->error_status = SDP_WRITE_FILE_COMPLETE;
sdp->state = SDP_STATE_RX_FILE_DATA;
sdp->dnl_address = be32_to_cpu(cmd->addr);
sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
sdp->next_state = SDP_STATE_IDLE;
printf("Downloading file of size %d to 0x%08x... ",
sdp->dnl_bytes_remaining, sdp->dnl_address);
break;
- case SDP_ERROR_STATUS:
sdp->always_send_status = true;
sdp->error_status = 0;
sdp->state = SDP_STATE_TX_SEC_CONF;
sdp->next_state = SDP_STATE_IDLE;
break;
- case SDP_DCD_WRITE:
sdp->always_send_status = true;
sdp->error_status = SDP_WRITE_REGISTER_COMPLETE;
sdp->state = SDP_STATE_RX_DCD_DATA;
sdp->dnl_bytes_remaining = be32_to_cpu(cmd->cnt);
sdp->next_state = SDP_STATE_IDLE;
break;
- case SDP_JUMP_ADDRESS:
sdp->always_send_status = false;
sdp->error_status = 0;
sdp->jmp_address = be32_to_cpu(cmd->addr);
sdp->state = SDP_STATE_TX_SEC_CONF;
sdp->next_state = SDP_STATE_JUMP;
break;
- case SDP_SKIP_DCD_HEADER:
sdp->always_send_status = true;
sdp->error_status = SDP_SKIP_DCD_HEADER_COMPLETE;
/* Ignore command, DCD not supported anyway */
sdp->state = SDP_STATE_TX_SEC_CONF;
sdp->next_state = SDP_STATE_IDLE;
break;
- default:
error("Unknown command: %04x\n", be16_to_cpu(cmd->cmd));
- }
+}
+static void sdp_rx_data_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct f_sdp *sdp = req->context;
- int status = req->status;
- u8 *data = req->buf;
- u8 report = data[0];
- int datalen = req->length - 1;
- if (status != 0) {
error("Status: %d", status);
return;
- }
- if (report != 2) {
error("Unexpected report %d", report);
return;
- }
- if (sdp->dnl_bytes_remaining < datalen) {
/*
* Some USB stacks require to send a complete buffer as
* specified in the HID descriptor. This leads to longer
* transfers than the file length, no problem for us.
*/
sdp->dnl_bytes_remaining = 0;
- } else {
sdp->dnl_bytes_remaining -= datalen;
- }
- if (sdp->state == SDP_STATE_RX_FILE_DATA) {
memcpy((void *)sdp->dnl_address, req->buf + 1, datalen);
sdp->dnl_address += datalen;
- }
- if (sdp->dnl_bytes_remaining)
return;
- printf("done\n");
- switch (sdp->state) {
- case SDP_STATE_RX_FILE_DATA:
sdp->state = SDP_STATE_TX_SEC_CONF;
break;
- case SDP_STATE_RX_DCD_DATA:
sdp->state = SDP_STATE_TX_SEC_CONF;
break;
- default:
error("Invalid state: %d", sdp->state);
- }
+}
+static void sdp_tx_complete(struct usb_ep *ep, struct usb_request *req) +{
- struct f_sdp *sdp = req->context;
- int status = req->status;
- if (status != 0) {
error("Status: %d", status);
return;
- }
- switch (sdp->state) {
- case SDP_STATE_TX_SEC_CONF_BUSY:
/* Not all commands require status report */
if (sdp->always_send_status || sdp->error_status)
sdp->state = SDP_STATE_TX_STATUS;
else
sdp->state = sdp->next_state;
break;
- case SDP_STATE_TX_STATUS_BUSY:
sdp->state = sdp->next_state;
break;
- case SDP_STATE_TX_REGISTER_BUSY:
if (sdp->dnl_bytes_remaining)
sdp->state = SDP_STATE_TX_REGISTER;
else
sdp->state = SDP_STATE_IDLE;
break;
- default:
error("Wrong State: %d", sdp->state);
sdp->state = SDP_STATE_IDLE;
break;
- }
- debug("%s complete --> %d, %d/%d\n", ep->name,
status, req->actual, req->length);
+}
+static int sdp_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) +{
- struct usb_gadget *gadget = f->config->cdev->gadget;
- struct usb_request *req = f->config->cdev->req;
- struct f_sdp *sdp = f->config->cdev->req->context;
- u16 len = le16_to_cpu(ctrl->wLength);
- u16 w_value = le16_to_cpu(ctrl->wValue);
- int value = 0;
- u8 req_type = ctrl->bRequestType & USB_TYPE_MASK;
- debug("w_value: 0x%04x len: 0x%04x\n", w_value, len);
- debug("req_type: 0x%02x ctrl->bRequest: 0x%02x sdp->state: %d\n",
req_type, ctrl->bRequest, sdp->state);
- if (req_type == USB_TYPE_STANDARD) {
if (ctrl->bRequest == USB_REQ_GET_DESCRIPTOR) {
/* Send HID report descriptor */
value = min(len, (u16) sizeof(sdp_hid_report));
memcpy(req->buf, &sdp_hid_report, value);
sdp->configuration_done = true;
}
- }
- if (req_type == USB_TYPE_CLASS) {
int report = w_value & HID_REPORT_ID_MASK;
/* HID (SDP) request */
switch (ctrl->bRequest) {
case HID_REQ_SET_REPORT:
switch (report) {
case 1:
value = SDP_COMMAND_LEN + 1;
req->complete = sdp_rx_command_complete;
break;
case 2:
value = len;
req->complete = sdp_rx_data_complete;
break;
}
}
- }
- if (value >= 0) {
req->length = value;
req->zero = value < len;
value = usb_ep_queue(gadget->ep0, req, 0);
if (value < 0) {
debug("ep_queue --> %d\n", value);
req->status = 0;
}
- }
- return value;
+}
+static int sdp_bind(struct usb_configuration *c, struct usb_function *f) +{
- struct usb_gadget *gadget = c->cdev->gadget;
- struct usb_composite_dev *cdev = c->cdev;
- struct f_sdp *sdp = func_to_sdp(f);
- int rv = 0, id;
- id = usb_interface_id(c, f);
- if (id < 0)
return id;
- sdp_intf_runtime.bInterfaceNumber = id;
- struct usb_ep *ep;
- /* allocate instance-specific endpoints */
- ep = usb_ep_autoconfig(gadget, &in_desc);
- if (!ep) {
rv = -ENODEV;
goto error;
- }
- sdp->in_ep = ep; /* Store IN EP for enabling @ setup */
- cdev->req->context = sdp;
+error:
- return rv;
+}
+static void sdp_unbind(struct usb_configuration *c, struct usb_function *f) +{
- free(sdp_func);
- sdp_func = NULL;
+}
+static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length) +{
- struct usb_request *req;
- req = usb_ep_alloc_request(ep, 0);
- if (!req)
return req;
- req->length = length;
- req->buf = memalign(CONFIG_SYS_CACHELINE_SIZE, length);
- if (!req->buf) {
usb_ep_free_request(ep, req);
req = NULL;
- }
- return req;
+}
+static struct usb_request *sdp_start_ep(struct usb_ep *ep) +{
- struct usb_request *req;
- req = alloc_ep_req(ep, 64);
- debug("%s: ep:%p req:%p\n", __func__, ep, req);
- if (!req)
return NULL;
- memset(req->buf, 0, req->length);
- req->complete = sdp_tx_complete;
- return req;
+} +static int sdp_set_alt(struct usb_function *f, unsigned intf, unsigned alt) +{
- struct f_sdp *sdp = func_to_sdp(f);
- struct usb_composite_dev *cdev = f->config->cdev;
- int result;
- debug("%s: intf: %d alt: %d\n", __func__, intf, alt);
- result = usb_ep_enable(sdp->in_ep, &in_desc);
- if (result)
return result;
- sdp->in_req = sdp_start_ep(sdp->in_ep);
- sdp->in_req->context = sdp;
- sdp->in_ep->driver_data = cdev; /* claim */
- sdp->altsetting = alt;
- sdp->state = SDP_STATE_IDLE;
- return 0;
+}
+static int sdp_get_alt(struct usb_function *f, unsigned intf) +{
- struct f_sdp *sdp = func_to_sdp(f);
- return sdp->altsetting;
+}
+static void sdp_disable(struct usb_function *f) +{
- struct f_sdp *sdp = func_to_sdp(f);
- usb_ep_disable(sdp->in_ep);
- if (sdp->in_req) {
free(sdp->in_req);
sdp->in_req = NULL;
- }
+}
+static int sdp_bind_config(struct usb_configuration *c) +{
- int status;
- if (!sdp_func) {
sdp_func = memalign(CONFIG_SYS_CACHELINE_SIZE, sizeof(*sdp_func));
if (!sdp_func)
return -ENOMEM;
- }
- memset(sdp_func, 0, sizeof(*sdp_func));
- sdp_func->usb_function.name = "sdp";
- sdp_func->usb_function.hs_descriptors = sdp_runtime_descs;
- sdp_func->usb_function.descriptors = sdp_runtime_descs;
- sdp_func->usb_function.bind = sdp_bind;
- sdp_func->usb_function.unbind = sdp_unbind;
- sdp_func->usb_function.set_alt = sdp_set_alt;
- sdp_func->usb_function.get_alt = sdp_get_alt;
- sdp_func->usb_function.disable = sdp_disable;
- sdp_func->usb_function.strings = sdp_generic_strings;
- sdp_func->usb_function.setup = sdp_setup;
- status = usb_add_function(c, &sdp_func->usb_function);
- return status;
+}
+int sdp_init(int controller_index) +{
- printf("SDP: initialize...\n");
- while (!sdp_func->configuration_done) {
if (ctrlc()) {
puts("\rCTRL+C - Operation aborted.\n");
return 1;
}
usb_gadget_handle_interrupts(controller_index);
- }
- return 0;
+}
+static u32 sdp_jump_imxheader(void *address) +{
- flash_header_v2_t *headerv2 = address;
- ulong (*entry)(void);
- if (headerv2->header.tag != IVT_HEADER_TAG) {
printf("Header Tag is not an IMX image\n");
return SDP_ERROR_IMXHEADER;
- }
- printf("Jumping to 0x%08x\n", headerv2->entry);
- entry = (void *)headerv2->entry;
- entry();
- /* The image probably never returns hence we won't reach that point */
- return 0;
+}
+static void sdp_handle_in_ep(void) +{
- u8 *data = sdp_func->in_req->buf;
- u32 status;
- int datalen;
- switch (sdp_func->state) {
- case SDP_STATE_TX_SEC_CONF:
debug("Report 3: HAB security\n");
data[0] = 3;
status = SDP_SECURITY_OPEN;
memcpy(&data[1], &status, 4);
sdp_func->in_req->length = 5;
usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
sdp_func->state = SDP_STATE_TX_SEC_CONF_BUSY;
break;
- case SDP_STATE_TX_STATUS:
debug("Report 4: Status\n");
data[0] = 4;
memcpy(&data[1], &sdp_func->error_status, 4);
sdp_func->in_req->length = 65;
usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
sdp_func->state = SDP_STATE_TX_STATUS_BUSY;
break;
- case SDP_STATE_TX_REGISTER:
debug("Report 4: Register Values\n");
data[0] = 4;
datalen = sdp_func->dnl_bytes_remaining;
if (datalen > 64)
datalen = 64;
memcpy(&data[1], (void *)sdp_func->dnl_address, datalen);
sdp_func->in_req->length = 65;
sdp_func->dnl_bytes_remaining -= datalen;
sdp_func->dnl_address += datalen;
usb_ep_queue(sdp_func->in_ep, sdp_func->in_req, 0);
sdp_func->state = SDP_STATE_TX_REGISTER_BUSY;
break;
- case SDP_STATE_JUMP:
printf("Checking imxheader at 0x%08x\n", f_sdp->jmp_address);
status = sdp_jump_imxheader((void *)f_sdp->jmp_address);
sdp_func->next_state = SDP_STATE_IDLE;
sdp_func->error_status = status;
/* Only send Report 4 if there was an error */
if (status)
sdp_func->state = SDP_STATE_TX_STATUS;
else
sdp_func->state = SDP_STATE_IDLE;
break;
- default:
break;
- };
+}
+void sdp_handle(int controller_index) +{
- printf("SDP: handle requests...\n");
- while (1) {
if (ctrlc()) {
puts("\rCTRL+C - Operation aborted.\n");
return;
}
usb_gadget_handle_interrupts(controller_index);
sdp_handle_in_ep();
- }
+}
+int sdp_add(struct usb_configuration *c) +{
- int id;
- id = usb_string_id(c->cdev);
- if (id < 0)
return id;
- strings_sdp_generic[0].id = id;
- sdp_intf_runtime.iInterface = id;
- debug("%s: cdev: %p gadget: %p gadget->ep0: %p\n", __func__,
c->cdev, c->cdev->gadget, c->cdev->gadget->ep0);
- return sdp_bind_config(c);
+}
+DECLARE_GADGET_BIND_CALLBACK(usb_dnl_sdp, sdp_add); diff --git a/include/sdp.h b/include/sdp.h new file mode 100644 index 0000000000..f476bab8f1 --- /dev/null +++ b/include/sdp.h @@ -0,0 +1,16 @@ +/*
- sdp.h - Serial Download Protocol
- Copyright (C) 2017 Toradex
- Author: Stefan Agner stefan.agner@toradex.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#ifndef __SDP_H_ +#define __SDP_H_
+int sdp_init(int controller_index); +void sdp_handle(int controller_index);
+#endif /* __SDP_H_ */

On 08/17/2017 03:49 PM, Stefano Babic wrote:
On 16/08/2017 20:00, Stefan Agner wrote:
From: Stefan Agner stefan.agner@toradex.com
Add SDP (Serial Downloader Protocol) implementation for U-Boot. The protocol is used in NXP SoC's boot ROM and allows to download program images. Beside that, it can also be used to read/write registers and download complete Device Configuration Data (DCD) sets. This basic implementation supports downloading images with the imx header format reading and writing registers.
Signed-off-by: Stefan Agner stefan.agner@toradex.com
Reviewed by : Stefano Babic ysbabic@denx.de>
Marek, this is related to USB. Anyway, I will prefer that the whole patchset will be merged at once, and not split between u-boot-imx and u-boot-usb. If you agree (and after the patchset is ok for you, of course !), I will propose that I will merge the whole patchset into u-boot-imx to avoid breakages.
This is gadget stuff, so it's Lukasz's call, not mine.

On 17/08/2017 15:54, Marek Vasut wrote:
On 08/17/2017 03:49 PM, Stefano Babic wrote:
On 16/08/2017 20:00, Stefan Agner wrote:
From: Stefan Agner stefan.agner@toradex.com
Add SDP (Serial Downloader Protocol) implementation for U-Boot. The protocol is used in NXP SoC's boot ROM and allows to download program images. Beside that, it can also be used to read/write registers and download complete Device Configuration Data (DCD) sets. This basic implementation supports downloading images with the imx header format reading and writing registers.
Signed-off-by: Stefan Agner stefan.agner@toradex.com
Reviewed by : Stefano Babic ysbabic@denx.de>
Marek, this is related to USB. Anyway, I will prefer that the whole patchset will be merged at once, and not split between u-boot-imx and u-boot-usb. If you agree (and after the patchset is ok for you, of course !), I will propose that I will merge the whole patchset into u-boot-imx to avoid breakages.
This is gadget stuff, so it's Lukasz's call, not mine.
Sorry for noise, you're right.
Lukasz, is it ok for you ?
Regards, Stefano

On 08/17/2017 03:54 PM, Marek Vasut wrote:
On 08/17/2017 03:49 PM, Stefano Babic wrote:
On 16/08/2017 20:00, Stefan Agner wrote:
From: Stefan Agner stefan.agner@toradex.com
Add SDP (Serial Downloader Protocol) implementation for U-Boot. The protocol is used in NXP SoC's boot ROM and allows to download program images. Beside that, it can also be used to read/write registers and download complete Device Configuration Data (DCD) sets. This basic implementation supports downloading images with the imx header format reading and writing registers.
Signed-off-by: Stefan Agner stefan.agner@toradex.com
Reviewed by : Stefano Babic ysbabic@denx.de>
Marek, this is related to USB. Anyway, I will prefer that the whole patchset will be merged at once, and not split between u-boot-imx and u-boot-usb. If you agree (and after the patchset is ok for you, of course !), I will propose that I will merge the whole patchset into u-boot-imx to avoid breakages.
This is gadget stuff, so it's Lukasz's call, not mine.
I'm fine with this approach (as I've already reviewed those patches).
Stefano, please merge it to your's -imx tree.

From: Stefan Agner stefan.agner@toradex.com
Support U-Boot images in SPL so that u-boot.img files can be directly downloaded and executed. Furthermore support U-Boot scripts download and execution in full U-Boot so that custom recovery actions can be downloaded from the host in a third step.
Signed-off-by: Stefan Agner stefan.agner@toradex.com Reviewed-by: Łukasz Majewski lukma@denx.de Reviewed-by: Stefano Babic sbabic@denx.de ---
Changes in v2: None
drivers/usb/gadget/f_sdp.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/gadget/f_sdp.c b/drivers/usb/gadget/f_sdp.c index 9d82abcd69..0fae66beab 100644 --- a/drivers/usb/gadget/f_sdp.c +++ b/drivers/usb/gadget/f_sdp.c @@ -29,6 +29,8 @@ #include <asm/io.h> #include <g_dnl.h> #include <sdp.h> +#include <spl.h> +#include <image.h> #include <imximage.h>
#define HID_REPORT_ID_MASK 0x000000ff @@ -670,8 +672,22 @@ static void sdp_handle_in_ep(void) sdp_func->state = SDP_STATE_TX_REGISTER_BUSY; break; case SDP_STATE_JUMP: - printf("Checking imxheader at 0x%08x\n", f_sdp->jmp_address); - status = sdp_jump_imxheader((void *)f_sdp->jmp_address); + printf("Jumping to header at 0x%08x\n", sdp_func->jmp_address); + status = sdp_jump_imxheader((void *)sdp_func->jmp_address); + + /* If imx header fails, try some U-Boot specific headers */ + if (status) { +#ifdef CONFIG_SPL_BUILD + /* In SPL, allow jumps to U-Boot images */ + struct spl_image_info spl_image = {}; + spl_parse_image_header(&spl_image, + (struct image_header *)sdp_func->jmp_address); + jump_to_image_no_args(&spl_image); +#else + /* In U-Boot, allow jumps to scripts */ + source(sdp_func->jmp_address, "script@1"); +#endif + }
sdp_func->next_state = SDP_STATE_IDLE; sdp_func->error_status = status;

From: Stefan Agner stefan.agner@toradex.com
Add a new command to start USB Serial Download Protocol (SDP) state machine.
Signed-off-by: Stefan Agner stefan.agner@toradex.com Reviewed-by: Łukasz Majewski lukma@denx.de Reviewed-by: Stefano Babic sbabic@denx.de ---
Changes in v2: - Changed function signature of sdp_init/sdp_handle
cmd/Kconfig | 7 +++++++ cmd/Makefile | 1 + cmd/usb_gadget_sdp.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 cmd/usb_gadget_sdp.c
diff --git a/cmd/Kconfig b/cmd/Kconfig index 42d955c96a..d6d130edfa 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -895,6 +895,13 @@ config CMD_USB help USB support.
+config CMD_USB_SDP + bool "sdp" + select USB_FUNCTION_SDP + help + Enables the command "sdp" which is used to have U-Boot emulating the + Serial Download Protocol (SDP) via USB. + config CMD_USB_MASS_STORAGE bool "UMS usb mass storage" help diff --git a/cmd/Makefile b/cmd/Makefile index 13c86f8fcc..4ec0e175cd 100644 --- a/cmd/Makefile +++ b/cmd/Makefile @@ -131,6 +131,7 @@ obj-$(CONFIG_CMD_FASTBOOT) += fastboot.o obj-$(CONFIG_CMD_FS_UUID) += fs_uuid.o
obj-$(CONFIG_CMD_USB_MASS_STORAGE) += usb_mass_storage.o +obj-$(CONFIG_CMD_USB_SDP) += usb_gadget_sdp.o obj-$(CONFIG_CMD_THOR_DOWNLOAD) += thordown.o obj-$(CONFIG_CMD_XIMG) += ximg.o obj-$(CONFIG_CMD_YAFFS2) += yaffs2.o diff --git a/cmd/usb_gadget_sdp.c b/cmd/usb_gadget_sdp.c new file mode 100644 index 0000000000..b1d8b2858e --- /dev/null +++ b/cmd/usb_gadget_sdp.c @@ -0,0 +1,50 @@ +/* + * cmd_sdp.c -- sdp command + * + * Copyright (C) 2016 Toradex + * Author: Stefan Agner stefan.agner@toradex.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <g_dnl.h> +#include <sdp.h> +#include <usb.h> + +static int do_sdp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + int ret = CMD_RET_FAILURE; + + if (argc < 2) + return CMD_RET_USAGE; + + char *usb_controller = argv[1]; + int controller_index = simple_strtoul(usb_controller, NULL, 0); + board_usb_init(controller_index, USB_INIT_DEVICE); + + g_dnl_clear_detach(); + g_dnl_register("usb_dnl_sdp"); + + ret = sdp_init(controller_index); + if (ret) { + error("SDP init failed: %d", ret); + goto exit; + } + + /* This command typically does not return but jumps to an image */ + sdp_handle(controller_index); + error("SDP ended"); + +exit: + g_dnl_unregister(); + board_usb_cleanup(controller_index, USB_INIT_DEVICE); + + return ret; +} + +U_BOOT_CMD(sdp, 2, 1, do_sdp, + "Serial Downloader Protocol", + "<USB_controller>\n" + " - serial downloader protocol via <USB_controller>\n" +);

From: Stefan Agner stefan.agner@toradex.com
Add USB serial download protocol support to SPL. If the SoC started in recovery mode the SPL will immediately switch to SDP and wait for further downloads/commands from the host side.
Signed-off-by: Stefan Agner stefan.agner@toradex.com Reviewed-by: Stefano Babic sbabic@denx.de ---
Changes in v2: - Changed function signature of sdp_init/sdp_handle - Use BOOT_DEVICE_BOARD
common/spl/Kconfig | 6 ++++++ common/spl/Makefile | 1 + common/spl/spl_sdp.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/Makefile | 1 + 4 files changed, 45 insertions(+) create mode 100644 common/spl/spl_sdp.c
diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 5176857506..b3436b3c28 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -668,6 +668,12 @@ config SPL_DFU_RAM
endchoice
+config SPL_USB_SDP_SUPPORT + bool "Support SDP (Serial Download Protocol)" + help + Enable Serial Download Protocol (SDP) device support in SPL. This + allows to download images into memory and execute (jump to) them + using the same protocol as implemented by the i.MX family's boot ROM. endif
config SPL_WATCHDOG_SUPPORT diff --git a/common/spl/Makefile b/common/spl/Makefile index 112b3e6022..6255d4f73c 100644 --- a/common/spl/Makefile +++ b/common/spl/Makefile @@ -30,4 +30,5 @@ obj-$(CONFIG_$(SPL_TPL_)SATA_SUPPORT) += spl_sata.o obj-$(CONFIG_$(SPL_TPL_)DFU_SUPPORT) += spl_dfu.o obj-$(CONFIG_$(SPL_TPL_)SPI_LOAD) += spl_spi.o obj-$(CONFIG_$(SPL_TPL_)RAM_SUPPORT) += spl_ram.o +obj-$(CONFIG_$(SPL_TPL_)USB_SDP_SUPPORT) += spl_sdp.o endif diff --git a/common/spl/spl_sdp.c b/common/spl/spl_sdp.c new file mode 100644 index 0000000000..350bcdb056 --- /dev/null +++ b/common/spl/spl_sdp.c @@ -0,0 +1,37 @@ +/* + * (C) Copyright 2016 Toradex + * Author: Stefan Agner stefan.agner@toradex.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <spl.h> +#include <usb.h> +#include <g_dnl.h> +#include <sdp.h> + +DECLARE_GLOBAL_DATA_PTR; + +static int spl_sdp_load_image(struct spl_image_info *spl_image, + struct spl_boot_device *bootdev) +{ + int ret; + const int controller_index = 0; + + g_dnl_clear_detach(); + g_dnl_register("usb_dnl_sdp"); + + ret = sdp_init(controller_index); + if (ret) { + error("SDP init failed: %d", ret); + return -ENODEV; + } + + /* This command typically does not return but jumps to an image */ + sdp_handle(controller_index); + error("SDP ended"); + + return -EINVAL; +} +SPL_LOAD_IMAGE_METHOD("USB SDP", 0, BOOT_DEVICE_BOARD, spl_sdp_load_image); diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 6a007d1bcb..7258099c1c 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_USB_GADGET_SUPPORT) += g_dnl.o obj-$(CONFIG_SPL_DFU_SUPPORT) += f_dfu.o +obj-$(CONFIG_SPL_USB_SDP_SUPPORT) += f_sdp.o endif
# new USB gadget layer dependencies

On 08/16/2017 08:00 PM, Stefan Agner wrote:
From: Stefan Agner stefan.agner@toradex.com
Add USB serial download protocol support to SPL. If the SoC started in recovery mode the SPL will immediately switch to SDP and wait for further downloads/commands from the host side.
Reviewed-by: Łukasz Majewski lukma@denx.de
Signed-off-by: Stefan Agner stefan.agner@toradex.com Reviewed-by: Stefano Babic sbabic@denx.de
Changes in v2:
- Changed function signature of sdp_init/sdp_handle
- Use BOOT_DEVICE_BOARD
common/spl/Kconfig | 6 ++++++ common/spl/Makefile | 1 + common/spl/spl_sdp.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/usb/gadget/Makefile | 1 + 4 files changed, 45 insertions(+) create mode 100644 common/spl/spl_sdp.c
diff --git a/common/spl/Kconfig b/common/spl/Kconfig index 5176857506..b3436b3c28 100644 --- a/common/spl/Kconfig +++ b/common/spl/Kconfig @@ -668,6 +668,12 @@ config SPL_DFU_RAM
endchoice
+config SPL_USB_SDP_SUPPORT
- bool "Support SDP (Serial Download Protocol)"
- help
Enable Serial Download Protocol (SDP) device support in SPL. This
allows to download images into memory and execute (jump to) them
using the same protocol as implemented by the i.MX family's boot ROM.
endif
config SPL_WATCHDOG_SUPPORT diff --git a/common/spl/Makefile b/common/spl/Makefile index 112b3e6022..6255d4f73c 100644 --- a/common/spl/Makefile +++ b/common/spl/Makefile @@ -30,4 +30,5 @@ obj-$(CONFIG_$(SPL_TPL_)SATA_SUPPORT) += spl_sata.o obj-$(CONFIG_$(SPL_TPL_)DFU_SUPPORT) += spl_dfu.o obj-$(CONFIG_$(SPL_TPL_)SPI_LOAD) += spl_spi.o obj-$(CONFIG_$(SPL_TPL_)RAM_SUPPORT) += spl_ram.o +obj-$(CONFIG_$(SPL_TPL_)USB_SDP_SUPPORT) += spl_sdp.o endif diff --git a/common/spl/spl_sdp.c b/common/spl/spl_sdp.c new file mode 100644 index 0000000000..350bcdb056 --- /dev/null +++ b/common/spl/spl_sdp.c @@ -0,0 +1,37 @@ +/*
- (C) Copyright 2016 Toradex
- Author: Stefan Agner stefan.agner@toradex.com
- SPDX-License-Identifier: GPL-2.0+
- */
+#include <common.h> +#include <spl.h> +#include <usb.h> +#include <g_dnl.h> +#include <sdp.h>
+DECLARE_GLOBAL_DATA_PTR;
+static int spl_sdp_load_image(struct spl_image_info *spl_image,
struct spl_boot_device *bootdev)
+{
- int ret;
- const int controller_index = 0;
- g_dnl_clear_detach();
- g_dnl_register("usb_dnl_sdp");
- ret = sdp_init(controller_index);
- if (ret) {
error("SDP init failed: %d", ret);
return -ENODEV;
- }
- /* This command typically does not return but jumps to an image */
- sdp_handle(controller_index);
- error("SDP ended");
- return -EINVAL;
+} +SPL_LOAD_IMAGE_METHOD("USB SDP", 0, BOOT_DEVICE_BOARD, spl_sdp_load_image); diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 6a007d1bcb..7258099c1c 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_USB_ETHER) += epautoconf.o config.o usbstring.o ifdef CONFIG_SPL_BUILD obj-$(CONFIG_SPL_USB_GADGET_SUPPORT) += g_dnl.o obj-$(CONFIG_SPL_DFU_SUPPORT) += f_dfu.o +obj-$(CONFIG_SPL_USB_SDP_SUPPORT) += f_sdp.o endif
# new USB gadget layer dependencies

From: Stefan Agner stefan.agner@toradex.com
Document the U-Boot Serial Download Protocol implementation and some typical use cases.
Signed-off-by: Stefan Agner stefan.agner@toradex.com --- This ended up to be almost more a imx_usb documentation. But there is really not much to document from a U-Boot side since actual usage heavily depends on host side tooling...
This assumes that the necessary changes will get merged upstream: https://github.com/toradex/imx_loader/tree/imx_usb_batch_mode_refactored
Changes in v2: None
doc/README.sdp | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 doc/README.sdp
diff --git a/doc/README.sdp b/doc/README.sdp new file mode 100644 index 0000000000..9b438c0746 --- /dev/null +++ b/doc/README.sdp @@ -0,0 +1,100 @@ +------------- +SDP in U-Boot +------------- + +SDP stands for serial download protocol. It is the protocol used in NXP's +i.MX SoCs ROM Serial Downloader and provides means to download a program +image to the chip over USB and UART serial connection. + +The implementation in U-Boot uses the USB Downloader Gadget (g_dnl) to +provide a SDP implementation over USB. This allows to download program +images to the target in SPL/U-Boot using the same protocol/tooling the +SoC's recovery mechanism is using. + +The SDP protocol over USB is a USB HID class protocol. USB HID class +protocols allow to access a USB device without OS specific drivers. The +U-Boot implementation has primarly been tested using the open source +imx_loader utility (https://github.com/toradex/imx_loader). + +The host side utilities are typically capable to interpret the i.MX +specific image header (see doc/README.imximage). There are extensions +for imx_loader's imx_usb utility which allow to interpret the U-Boot +specific legacy image format (see mkimage(1)). Also the U-Boot side +support beside the i.MX specific header the U-Boot legacy header. + +Usage +----- + +This implementation can be started in U-Boot using the sdp command +(CONFIG_CMD_USB_SDP) or in SPL if Serial Downloader boot mode has been +detected (CONFIG_SPL_USB_SDP_SUPPORT). + +A typical use case is downloading full U-Boot after SPL has been +downloaded through the boot ROM's Serial Downloader. Using boot mode +detection the SPL will run the SDP implementation automatically in +this case: + + # imx_usb SPL + +Targets Serial Console: + + Trying to boot from USB SDP + SDP: initialize... + SDP: handle requests... + +At this point the SPL reenumerated as a new HID device and emulating +the boot ROM's SDP protocol. The USB VID/PID will depend on standard +U-Boot configurations CONFIG_G_DNL_(VENDOR|PRODUCT)_NUM. Make sure +imx_usb is aware of the USB VID/PID for your device by adding a +configuration entry in imx_usb.conf: + + 0x1b67:0x4fff, mx6_usb_sdp_spl.conf + +And the device specific configuration file mx6_usb_sdp_spl.conf: + + mx6_spl_sdp + hid,uboot_header,1024,0x910000,0x10000000,1G,0x00900000,0x40000 + +This allows to download the regular U-Boot with legacy image headers +(u-boot.img) using a second invocation of imx_usb: + + # imx_usb u-boot.img + +Furthermore, when U-Boot is running the sdp command can be used to +download and run scripts: + + # imx_usb script.scr + +imx_usb configuration files can be also used to download multiple +files and of arbitrary types, e.g. + + mx6_usb_sdp_uboot + hid,1024,0x10000000,1G,0x00907000,0x31000 + full.itb:load 0x12100000 + boot.scr:load 0x12000000,jump 0x12000000 + +There is also a batch mode which allows imx_usb to handle multiple +consecutive reenumerations by adding multiple VID/PID specifications +in imx_usb.conf: + + 0x15a2:0x0061, mx6_usb_rom.conf, 0x1b67:0x4fff, mx6_usb_sdp_spl.conf + +In this mode the file to download (imx_usb job) needs to be specified +in the configuration files. + +mx6_usb_rom.conf: + + mx6_qsb + hid,1024,0x910000,0x10000000,1G,0x00900000,0x40000 + SPL:jump header2 + +mx6_usb_sdp_spl.conf: + + mx6_spl_sdp + hid,uboot_header,1024,0x10000000,1G,0x00907000,0x31000 + u-boot.img:jump header2 + +With that SPL and U-Boot can be downloaded with a single invocation +of imx_usb without arguments: + + # imx_usb

Hi Stefan,
From: Stefan Agner stefan.agner@toradex.com
Document the U-Boot Serial Download Protocol implementation and some typical use cases.
Thanks for providing the description :-)
Reviewed-by: Łukasz Majewski lukma@denx.de
Signed-off-by: Stefan Agner stefan.agner@toradex.com
This ended up to be almost more a imx_usb documentation. But there is really not much to document from a U-Boot side since actual usage heavily depends on host side tooling...
This assumes that the necessary changes will get merged upstream: https://github.com/toradex/imx_loader/tree/imx_usb_batch_mode_refactored
Changes in v2: None
doc/README.sdp | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 doc/README.sdp
diff --git a/doc/README.sdp b/doc/README.sdp new file mode 100644 index 0000000000..9b438c0746 --- /dev/null +++ b/doc/README.sdp @@ -0,0 +1,100 @@ +------------- +SDP in U-Boot +-------------
+SDP stands for serial download protocol. It is the protocol used in NXP's +i.MX SoCs ROM Serial Downloader and provides means to download a program +image to the chip over USB and UART serial connection.
+The implementation in U-Boot uses the USB Downloader Gadget (g_dnl) to +provide a SDP implementation over USB. This allows to download program +images to the target in SPL/U-Boot using the same protocol/tooling the +SoC's recovery mechanism is using.
+The SDP protocol over USB is a USB HID class protocol. USB HID class +protocols allow to access a USB device without OS specific drivers. The +U-Boot implementation has primarly been tested using the open source +imx_loader utility (https://github.com/toradex/imx_loader).
+The host side utilities are typically capable to interpret the i.MX +specific image header (see doc/README.imximage). There are extensions +for imx_loader's imx_usb utility which allow to interpret the U-Boot +specific legacy image format (see mkimage(1)). Also the U-Boot side +support beside the i.MX specific header the U-Boot legacy header.
+Usage +-----
+This implementation can be started in U-Boot using the sdp command +(CONFIG_CMD_USB_SDP) or in SPL if Serial Downloader boot mode has been +detected (CONFIG_SPL_USB_SDP_SUPPORT).
+A typical use case is downloading full U-Boot after SPL has been +downloaded through the boot ROM's Serial Downloader. Using boot mode +detection the SPL will run the SDP implementation automatically in +this case:
- # imx_usb SPL
+Targets Serial Console:
- Trying to boot from USB SDP
- SDP: initialize...
- SDP: handle requests...
+At this point the SPL reenumerated as a new HID device and emulating +the boot ROM's SDP protocol. The USB VID/PID will depend on standard +U-Boot configurations CONFIG_G_DNL_(VENDOR|PRODUCT)_NUM. Make sure +imx_usb is aware of the USB VID/PID for your device by adding a +configuration entry in imx_usb.conf:
- 0x1b67:0x4fff, mx6_usb_sdp_spl.conf
+And the device specific configuration file mx6_usb_sdp_spl.conf:
- mx6_spl_sdp
- hid,uboot_header,1024,0x910000,0x10000000,1G,0x00900000,0x40000
+This allows to download the regular U-Boot with legacy image headers +(u-boot.img) using a second invocation of imx_usb:
- # imx_usb u-boot.img
+Furthermore, when U-Boot is running the sdp command can be used to +download and run scripts:
- # imx_usb script.scr
+imx_usb configuration files can be also used to download multiple +files and of arbitrary types, e.g.
- mx6_usb_sdp_uboot
- hid,1024,0x10000000,1G,0x00907000,0x31000
- full.itb:load 0x12100000
- boot.scr:load 0x12000000,jump 0x12000000
+There is also a batch mode which allows imx_usb to handle multiple +consecutive reenumerations by adding multiple VID/PID specifications +in imx_usb.conf:
- 0x15a2:0x0061, mx6_usb_rom.conf, 0x1b67:0x4fff, mx6_usb_sdp_spl.conf
+In this mode the file to download (imx_usb job) needs to be specified +in the configuration files.
+mx6_usb_rom.conf:
- mx6_qsb
- hid,1024,0x910000,0x10000000,1G,0x00900000,0x40000
- SPL:jump header2
+mx6_usb_sdp_spl.conf:
- mx6_spl_sdp
- hid,uboot_header,1024,0x10000000,1G,0x00907000,0x31000
- u-boot.img:jump header2
+With that SPL and U-Boot can be downloaded with a single invocation +of imx_usb without arguments:
- # imx_usb

From: Stefan Agner stefan.agner@toradex.com
Use a completely independent USB Product ID for SPL. This allows to differentiate a SDP running in SPL and SDP running in a U-Boot which could not read the config block successfully.
Signed-off-by: Stefan Agner stefan.agner@toradex.com Acked-by: Max Krummenacher max.krummenacher@toradex.com ---
Changes in v2: None
board/toradex/apalis_imx6/apalis_imx6.c | 13 +++++++++++++ board/toradex/colibri_imx6/colibri_imx6.c | 13 +++++++++++++ 2 files changed, 26 insertions(+)
diff --git a/board/toradex/apalis_imx6/apalis_imx6.c b/board/toradex/apalis_imx6/apalis_imx6.c index 8e5613cb12..edaca5d346 100644 --- a/board/toradex/apalis_imx6/apalis_imx6.c +++ b/board/toradex/apalis_imx6/apalis_imx6.c @@ -28,6 +28,7 @@ #include <dm/platform_data/serial_mxc.h> #include <dm/platdata.h> #include <fsl_esdhc.h> +#include <g_dnl.h> #include <i2c.h> #include <imx_thermal.h> #include <linux/errno.h> @@ -1233,6 +1234,18 @@ void reset_cpu(ulong addr) { }
+#ifdef CONFIG_SPL_USB_GADGET_SUPPORT +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{ + unsigned short usb_pid; + + usb_pid = TORADEX_USB_PRODUCT_NUM_OFFSET + 0xfff; + put_unaligned(usb_pid, &dev->idProduct); + + return 0; +} +#endif + #endif
static struct mxc_serial_platdata mxc_serial_plat = { diff --git a/board/toradex/colibri_imx6/colibri_imx6.c b/board/toradex/colibri_imx6/colibri_imx6.c index cbf7aa952a..0cc958a0a8 100644 --- a/board/toradex/colibri_imx6/colibri_imx6.c +++ b/board/toradex/colibri_imx6/colibri_imx6.c @@ -28,6 +28,7 @@ #include <dm/platform_data/serial_mxc.h> #include <dm/platdata.h> #include <fsl_esdhc.h> +#include <g_dnl.h> #include <i2c.h> #include <imx_thermal.h> #include <linux/errno.h> @@ -1118,6 +1119,18 @@ void reset_cpu(ulong addr) { }
+#ifdef CONFIG_SPL_USB_GADGET_SUPPORT +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{ + unsigned short usb_pid; + + usb_pid = TORADEX_USB_PRODUCT_NUM_OFFSET + 0xfff; + put_unaligned(usb_pid, &dev->idProduct); + + return 0; +} +#endif + #endif
static struct mxc_serial_platdata mxc_serial_plat = {

On 08/16/2017 08:00 PM, Stefan Agner wrote:
From: Stefan Agner stefan.agner@toradex.com
Use a completely independent USB Product ID for SPL. This allows to differentiate a SDP running in SPL and SDP running in a U-Boot which could not read the config block successfully.
Reviewed-by: Łukasz Majewski lukma@denx.de
Signed-off-by: Stefan Agner stefan.agner@toradex.com Acked-by: Max Krummenacher max.krummenacher@toradex.com
Changes in v2: None
board/toradex/apalis_imx6/apalis_imx6.c | 13 +++++++++++++ board/toradex/colibri_imx6/colibri_imx6.c | 13 +++++++++++++ 2 files changed, 26 insertions(+)
diff --git a/board/toradex/apalis_imx6/apalis_imx6.c b/board/toradex/apalis_imx6/apalis_imx6.c index 8e5613cb12..edaca5d346 100644 --- a/board/toradex/apalis_imx6/apalis_imx6.c +++ b/board/toradex/apalis_imx6/apalis_imx6.c @@ -28,6 +28,7 @@ #include <dm/platform_data/serial_mxc.h> #include <dm/platdata.h> #include <fsl_esdhc.h> +#include <g_dnl.h> #include <i2c.h> #include <imx_thermal.h> #include <linux/errno.h> @@ -1233,6 +1234,18 @@ void reset_cpu(ulong addr) { }
+#ifdef CONFIG_SPL_USB_GADGET_SUPPORT +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{
- unsigned short usb_pid;
- usb_pid = TORADEX_USB_PRODUCT_NUM_OFFSET + 0xfff;
- put_unaligned(usb_pid, &dev->idProduct);
- return 0;
+} +#endif
#endif
static struct mxc_serial_platdata mxc_serial_plat = { diff --git a/board/toradex/colibri_imx6/colibri_imx6.c b/board/toradex/colibri_imx6/colibri_imx6.c index cbf7aa952a..0cc958a0a8 100644 --- a/board/toradex/colibri_imx6/colibri_imx6.c +++ b/board/toradex/colibri_imx6/colibri_imx6.c @@ -28,6 +28,7 @@ #include <dm/platform_data/serial_mxc.h> #include <dm/platdata.h> #include <fsl_esdhc.h> +#include <g_dnl.h> #include <i2c.h> #include <imx_thermal.h> #include <linux/errno.h> @@ -1118,6 +1119,18 @@ void reset_cpu(ulong addr) { }
+#ifdef CONFIG_SPL_USB_GADGET_SUPPORT +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{
- unsigned short usb_pid;
- usb_pid = TORADEX_USB_PRODUCT_NUM_OFFSET + 0xfff;
- put_unaligned(usb_pid, &dev->idProduct);
- return 0;
+} +#endif
#endif
static struct mxc_serial_platdata mxc_serial_plat = {

From: Stefan Agner stefan.agner@toradex.com
Enable Serial Download Protocol (SDP) in SPL and U-Boot. This is useful to make use of imx_usb to download the complete U-Boot (u-boot.img) after SPL has been downloaded. The U-Boot command sdp allows to enumerate as SDP capable device again, e.g. to download a Linux kernel and/or U-Boot script.
Signed-off-by: Stefan Agner stefan.agner@toradex.com Acked-by: Max Krummenacher max.krummenacher@toradex.com ---
Changes in v2: None
configs/apalis_imx6_defconfig | 4 ++++ configs/colibri_imx6_defconfig | 4 ++++ 2 files changed, 8 insertions(+)
diff --git a/configs/apalis_imx6_defconfig b/configs/apalis_imx6_defconfig index 468a1fcac7..8f87f1d34c 100644 --- a/configs/apalis_imx6_defconfig +++ b/configs/apalis_imx6_defconfig @@ -17,6 +17,9 @@ CONFIG_BOARD_EARLY_INIT_F=y CONFIG_SPL=y CONFIG_SPL_DMA_SUPPORT=y CONFIG_SPL_I2C_SUPPORT=y +CONFIG_SPL_USB_HOST_SUPPORT=y +CONFIG_SPL_USB_GADGET_SUPPORT=y +CONFIG_SPL_USB_SDP_SUPPORT=y CONFIG_HUSH_PARSER=y CONFIG_SYS_PROMPT="Apalis iMX6 # " CONFIG_CMD_BOOTZ=y @@ -33,6 +36,7 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y CONFIG_CMD_USB=y +CONFIG_CMD_USB_SDP=y CONFIG_CMD_USB_MASS_STORAGE=y CONFIG_CMD_DHCP=y CONFIG_CMD_MII=y diff --git a/configs/colibri_imx6_defconfig b/configs/colibri_imx6_defconfig index edbd87fe19..beb74b5ae5 100644 --- a/configs/colibri_imx6_defconfig +++ b/configs/colibri_imx6_defconfig @@ -17,6 +17,9 @@ CONFIG_BOARD_EARLY_INIT_F=y CONFIG_SPL=y CONFIG_SPL_DMA_SUPPORT=y CONFIG_SPL_I2C_SUPPORT=y +CONFIG_SPL_USB_HOST_SUPPORT=y +CONFIG_SPL_USB_GADGET_SUPPORT=y +CONFIG_SPL_USB_SDP_SUPPORT=y CONFIG_HUSH_PARSER=y CONFIG_SYS_PROMPT="Colibri iMX6 # " CONFIG_CMD_BOOTZ=y @@ -33,6 +36,7 @@ CONFIG_CMD_GPIO=y CONFIG_CMD_I2C=y CONFIG_CMD_MMC=y CONFIG_CMD_USB=y +CONFIG_CMD_USB_SDP=y CONFIG_CMD_USB_MASS_STORAGE=y CONFIG_CMD_DHCP=y CONFIG_CMD_MII=y

Hi Stefan,
On 16/08/2017 20:00, Stefan Agner wrote:
From: Stefan Agner stefan.agner@toradex.com
This series adds NXP's Serial Download Protocol (SDP) support via USB for SPL/U-Boot. It allows to download U-Boot via USB from a (recovered) SPL using the same tools used to download SPL itself (specifically imx_usb, but also sb_loader seems to work).
The idea has been brought up when the first targets started to make use of SPL for DDR initialization, see: https://lists.denx.de/pipermail/u-boot/2015-July/220330.html
The initial SDP implementation (patch 2) requires the payload to have the imx specific headers (hence the move of the imx header file in patch 1).
Patch 3 extends image header support beyond the SDP specification, specifically implements also support for U-Boot headers. This allows to use the same SPL/U-Boot binaries for recovery as used on the regular boot device (SD/eMMC). For that to work also the host side imx_usb tools needed an extension, currently available here:
https://github.com/toradex/imx_loader/tree/imx_usb_batch_mode_refactored
(in case this patchset gets accepted in U-Boot, I plan to push these imx_usb changes upstream as well)
The full patchset allows to download SPL and U-Boot over USB to a target in recovery mode using the same usb_imx utility. Refer to the new README.sdp for details how to use usb_imx in combination with this implementation.
Changes in v2:
- Changed function signature of sdp_init/sdp_handle to allow specifying which USB controller should be used.
- Use #defines for security mode
- Improved types used in format strings
- Changed function signature of sdp_init/sdp_handle
- Use BOOT_DEVICE_BOARD
Stefan Agner (8): imx: move imximage header to common location usb: gadget: add SDP driver usb: gadget: sdp: extend images compatible for jumps cmd: add sdp command spl: add serial download protocol (SDP) support doc: add Serial Download Protocol documentation apalis/colibri_imx6: use independent USB PID for SPL apalis/colibri_imx6: enable SDP by default
I have merged the whole series to u-boot-imx. Many thanks for your effort, this was in the TODO list for i.MX since a long time !
Best regards, Stefano Babic

On Wed, Aug 23, 2017 at 5:43 AM, Stefano Babic sbabic@denx.de wrote:
I have merged the whole series to u-boot-imx. Many thanks for your effort, this was in the TODO list for i.MX since a long time !
Yes, very nice job, Stefan!
I will be using it soon on mx6sabresd, thanks!

Hi Stefan,
On Wed, Aug 16, 2017 at 3:00 PM, Stefan Agner stefan@agner.ch wrote:
From: Stefan Agner stefan.agner@toradex.com
This series adds NXP's Serial Download Protocol (SDP) support via USB for SPL/U-Boot. It allows to download U-Boot via USB from a (recovered) SPL using the same tools used to download SPL itself (specifically imx_usb, but also sb_loader seems to work).
The idea has been brought up when the first targets started to make use of SPL for DDR initialization, see: https://lists.denx.de/pipermail/u-boot/2015-July/220330.html
The initial SDP implementation (patch 2) requires the payload to have the imx specific headers (hence the move of the imx header file in patch 1).
Patch 3 extends image header support beyond the SDP specification, specifically implements also support for U-Boot headers. This allows to use the same SPL/U-Boot binaries for recovery as used on the regular boot device (SD/eMMC). For that to work also the host side imx_usb tools needed an extension, currently available here:
https://github.com/toradex/imx_loader/tree/imx_usb_batch_mode_refactored
(in case this patchset gets accepted in U-Boot, I plan to push these imx_usb changes upstream as well)
The full patchset allows to download SPL and U-Boot over USB to a target in recovery mode using the same usb_imx utility. Refer to the new README.sdp for details how to use usb_imx in combination with this implementation.
I am trying to use this feature on a imx6qsabresd board.
Here are the changes I made (against u-boot-imx latest tree):
diff --git a/board/freescale/mx6sabresd/mx6sabresd.c b/board/freescale/mx6sabresd/mx6sabresd.c index 5b50bc8..6ca2485 100644 --- a/board/freescale/mx6sabresd/mx6sabresd.c +++ b/board/freescale/mx6sabresd/mx6sabresd.c @@ -29,6 +29,7 @@ #include <power/pfuze100_pmic.h> #include "../common/pfuze.h" #include <usb.h> +#include <g_dnl.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -1033,6 +1034,19 @@ static void spl_dram_init(void) ddr_init(mx6dl_dcd_table, ARRAY_SIZE(mx6dl_dcd_table)); }
+#ifdef CONFIG_SPL_USB_GADGET_SUPPORT +#define FSL_USB_PRODUCT_NUM_OFFSET 0xa4a5 +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{ + unsigned short usb_pid; + + usb_pid = FSL_USB_PRODUCT_NUM_OFFSET + 0xff; + put_unaligned(usb_pid, &dev->idProduct); + + return 0; +} +#endif + void board_init_f(ulong dummy) { /* DDR initialization */ diff --git a/configs/mx6sabresd_defconfig b/configs/mx6sabresd_defconfig index 7949c5e..1c08639 100644 --- a/configs/mx6sabresd_defconfig +++ b/configs/mx6sabresd_defconfig @@ -19,6 +19,9 @@ CONFIG_SPL=y CONFIG_SPL_EXT_SUPPORT=y CONFIG_SPL_I2C_SUPPORT=y CONFIG_SPL_OS_BOOT=y +CONFIG_SPL_USB_HOST_SUPPORT=y +CONFIG_SPL_USB_GADGET_SUPPORT=y +CONFIG_SPL_USB_SDP_SUPPORT=y CONFIG_HUSH_PARSER=y CONFIG_CMD_BOOTZ=y # CONFIG_CMD_IMLS is not set @@ -32,6 +35,7 @@ CONFIG_CMD_PART=y CONFIG_CMD_PCI=y CONFIG_CMD_SF=y CONFIG_CMD_USB=y +CONFIG_CMD_USB_SDP=y CONFIG_CMD_DHCP=y CONFIG_CMD_MII=y CONFIG_CMD_PING=y
Then I use the imx_usb tool from https://github.com/toradex/imx_loader/tree/imx_usb_batch_mode_refactored
I remove the SD card, so that the board goes to SDP and then run:
sudo ./imx_usb SPL
and then I see in the board console:
U-Boot SPL 2017.09-rc2-36996-g63af4b0-dirty (Sep 01 2017 - 13:42:55) Trying to boot from MMC1 MMC: no card present mmc_init: -123, time 1 spl: mmc init failed with error: -123 SPL: failed to boot from all boot devices ### ERROR ### Please RESET the board ###
I was expecting to see:
Trying to boot from USB SDP SDP: initialize... SDP: handle requests...
as per the README.sdp
Any ideas as to what I am missing?
Thanks

On 2017-09-01 09:48, Fabio Estevam wrote:
Hi Stefan,
On Wed, Aug 16, 2017 at 3:00 PM, Stefan Agner stefan@agner.ch wrote:
From: Stefan Agner stefan.agner@toradex.com
This series adds NXP's Serial Download Protocol (SDP) support via USB for SPL/U-Boot. It allows to download U-Boot via USB from a (recovered) SPL using the same tools used to download SPL itself (specifically imx_usb, but also sb_loader seems to work).
The idea has been brought up when the first targets started to make use of SPL for DDR initialization, see: https://lists.denx.de/pipermail/u-boot/2015-July/220330.html
The initial SDP implementation (patch 2) requires the payload to have the imx specific headers (hence the move of the imx header file in patch 1).
Patch 3 extends image header support beyond the SDP specification, specifically implements also support for U-Boot headers. This allows to use the same SPL/U-Boot binaries for recovery as used on the regular boot device (SD/eMMC). For that to work also the host side imx_usb tools needed an extension, currently available here:
https://github.com/toradex/imx_loader/tree/imx_usb_batch_mode_refactored
(in case this patchset gets accepted in U-Boot, I plan to push these imx_usb changes upstream as well)
The full patchset allows to download SPL and U-Boot over USB to a target in recovery mode using the same usb_imx utility. Refer to the new README.sdp for details how to use usb_imx in combination with this implementation.
I am trying to use this feature on a imx6qsabresd board.
Here are the changes I made (against u-boot-imx latest tree):
diff --git a/board/freescale/mx6sabresd/mx6sabresd.c b/board/freescale/mx6sabresd/mx6sabresd.c index 5b50bc8..6ca2485 100644 --- a/board/freescale/mx6sabresd/mx6sabresd.c +++ b/board/freescale/mx6sabresd/mx6sabresd.c @@ -29,6 +29,7 @@ #include <power/pfuze100_pmic.h> #include "../common/pfuze.h" #include <usb.h> +#include <g_dnl.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -1033,6 +1034,19 @@ static void spl_dram_init(void) ddr_init(mx6dl_dcd_table, ARRAY_SIZE(mx6dl_dcd_table)); }
+#ifdef CONFIG_SPL_USB_GADGET_SUPPORT +#define FSL_USB_PRODUCT_NUM_OFFSET 0xa4a5 +int g_dnl_bind_fixup(struct usb_device_descriptor *dev, const char *name) +{
unsigned short usb_pid;
usb_pid = FSL_USB_PRODUCT_NUM_OFFSET + 0xff;
put_unaligned(usb_pid, &dev->idProduct);
return 0;
+} +#endif
void board_init_f(ulong dummy) { /* DDR initialization */ diff --git a/configs/mx6sabresd_defconfig b/configs/mx6sabresd_defconfig index 7949c5e..1c08639 100644 --- a/configs/mx6sabresd_defconfig +++ b/configs/mx6sabresd_defconfig @@ -19,6 +19,9 @@ CONFIG_SPL=y CONFIG_SPL_EXT_SUPPORT=y CONFIG_SPL_I2C_SUPPORT=y CONFIG_SPL_OS_BOOT=y +CONFIG_SPL_USB_HOST_SUPPORT=y +CONFIG_SPL_USB_GADGET_SUPPORT=y +CONFIG_SPL_USB_SDP_SUPPORT=y CONFIG_HUSH_PARSER=y CONFIG_CMD_BOOTZ=y # CONFIG_CMD_IMLS is not set @@ -32,6 +35,7 @@ CONFIG_CMD_PART=y CONFIG_CMD_PCI=y CONFIG_CMD_SF=y CONFIG_CMD_USB=y +CONFIG_CMD_USB_SDP=y CONFIG_CMD_DHCP=y CONFIG_CMD_MII=y CONFIG_CMD_PING=y
Then I use the imx_usb tool from https://github.com/toradex/imx_loader/tree/imx_usb_batch_mode_refactored
I remove the SD card, so that the board goes to SDP and then run:
sudo ./imx_usb SPL
and then I see in the board console:
U-Boot SPL 2017.09-rc2-36996-g63af4b0-dirty (Sep 01 2017 - 13:42:55) Trying to boot from MMC1 MMC: no card present mmc_init: -123, time 1 spl: mmc init failed with error: -123 SPL: failed to boot from all boot devices ### ERROR ### Please RESET the board ###
I was expecting to see:
Trying to boot from USB SDP SDP: initialize... SDP: handle requests...
as per the README.sdp
Any ideas as to what I am missing?
That is the issue I am describing here: https://lists.denx.de/pipermail/u-boot/2017-August/301952.html
Either enter recovery mode explicitly (by setting the BMODE pins or using the bmode command)
Or you can use the USB PHY method which indicates whether the ROM used serial downloader. We use this in our downstream U-Boot (NXP used that method to go into "mgfr" mode...) http://git.toradex.com/cgit/u-boot-toradex.git/commit/?h=2016.11-toradex-nex... http://git.toradex.com/cgit/u-boot-toradex.git/commit/?h=2016.11-toradex-nex...
-- Stefan

Hi Stefan,
On Fri, Sep 1, 2017 at 3:19 PM, Stefan Agner stefan@agner.ch wrote:
That is the issue I am describing here: https://lists.denx.de/pipermail/u-boot/2017-August/301952.html
Either enter recovery mode explicitly (by setting the BMODE pins or using the bmode command)
On imx6qsabresd it is not possible to put the boot pins into serial download mode.
Or you can use the USB PHY method which indicates whether the ROM used serial downloader. We use this in our downstream U-Boot (NXP used that method to go into "mgfr" mode...) http://git.toradex.com/cgit/u-boot-toradex.git/commit/?h=2016.11-toradex-nex... http://git.toradex.com/cgit/u-boot-toradex.git/commit/?h=2016.11-toradex-nex...
I have tested this method and it works, thanks.
Do you plan to usptream this method?
Thanks

On Fri, Sep 1, 2017 at 3:54 PM, Fabio Estevam festevam@gmail.com wrote:
I have tested this method and it works, thanks.
Do you plan to usptream this method?
Or I can also put your patch as part of my series that adds SDP support for imx6qsabresd if you prefer.

On September 1, 2017 12:25:44 PM PDT, Fabio Estevam festevam@gmail.com wrote:
On Fri, Sep 1, 2017 at 3:54 PM, Fabio Estevam festevam@gmail.com wrote:
I have tested this method and it works, thanks.
Do you plan to usptream this method?
Or I can also put your patch as part of my series that adds SDP support for imx6qsabresd if you prefer.
Let me do a separate, I guess it could be controversial...
-- Stefan

Hi everyone,
I managed to get SDP protocol to work with on my custom i.MX6 board, so big thanks to Stefan for your work!
I'm now trying to load swupdate ramdisk with u-boot sdp command, without success. Is it a possible use case ?
Here my steps:
In u-boot: => sdp 0
On Host, $ imx_usb
config file <C:\Users\vpr\IMXUSBLOADER2\delivery\imx_usb.conf> vid=0x15a2 pid=0x0061 file_name=mx6_usb_rom.conf -> vid=0x0525 pid=0xb4a4 file_name=mx6_usb_sdp_spl.conf vid=0x0525 pid=0xa4a5 file_name=mx6_usb_uboot.conf config file <C:\Users\vpr\IMXUSBLOADER2\delivery\mx6_usb_uboot.conf> parse C:\Users\vpr\IMXUSBLOADER2\delivery\mx6_usb_uboot.conf Trying to open device vid=0x0525 pid=0xa4a5......................... Could not open device vid=0x0525 pid=0xa4a5
VID/PID in imx_usb.conf are correctly detected, and correct configuration file is launched (mx6_usb_uboot.conf), it contains:
mx6_usb_sdp_uboot hid,1024,0x10000000,1G,0x00907000,0x31000 test.txt:load 0x12100000
My imx_usb.conf is the following:
#vid:pid, config_file 0x15a2:0x0061, mx6_usb_rom.conf, 0x0525:0xb4a4, mx6_usb_sdp_spl.conf 0x0525:0xa4a5, mx6_usb_uboot.conf
Am I missing something, Thanks, Vincent
2017-09-01 22:09 GMT+02:00 Stefan Agner stefan@agner.ch:
On September 1, 2017 12:25:44 PM PDT, Fabio Estevam festevam@gmail.com wrote:
On Fri, Sep 1, 2017 at 3:54 PM, Fabio Estevam festevam@gmail.com wrote:
I have tested this method and it works, thanks.
Do you plan to usptream this method?
Or I can also put your patch as part of my series that adds SDP support for imx6qsabresd if you prefer.
Let me do a separate, I guess it could be controversial...
-- Stefan -- Sent from my Android device with K-9 Mail. Please excuse my brevity. _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

Hi Vincent,
On 2017-09-08 01:27, Vincent Prince wrote:
Hi everyone,
I managed to get SDP protocol to work with on my custom i.MX6 board, so big thanks to Stefan for your work!
I'm now trying to load swupdate ramdisk with u-boot sdp command, without success. Is it a possible use case ?
Yes it should work, we use the U-Boot sdp command to download a squashfs based ramdisk in our Toradex Easy Installer: http://developer.toradex.com/software/toradex-easy-installer
Here my steps:
In u-boot: => sdp 0
On Host, $ imx_usb
config file <C:\Users\vpr\IMXUSBLOADER2\delivery\imx_usb.conf> vid=0x15a2 pid=0x0061 file_name=mx6_usb_rom.conf -> vid=0x0525 pid=0xb4a4 file_name=mx6_usb_sdp_spl.conf vid=0x0525 pid=0xa4a5 file_name=mx6_usb_uboot.conf config file <C:\Users\vpr\IMXUSBLOADER2\delivery\mx6_usb_uboot.conf> parse C:\Users\vpr\IMXUSBLOADER2\delivery\mx6_usb_uboot.conf Trying to open device vid=0x0525 pid=0xa4a5......................... Could not open device vid=0x0525 pid=0xa4a5
Is VID/PID showing up in Device Manager?
We did saw some problems with certain USB host implementation, so it might be a device dependency... I would also try a different machine/maybe Linux machine...
VID/PID in imx_usb.conf are correctly detected, and correct configuration file is launched (mx6_usb_uboot.conf), it contains:
mx6_usb_sdp_uboot hid,1024,0x10000000,1G,0x00907000,0x31000 test.txt:load 0x12100000
My imx_usb.conf is the following:
#vid:pid, config_file 0x15a2:0x0061, mx6_usb_rom.conf, 0x0525:0xb4a4, mx6_usb_sdp_spl.conf 0x0525:0xa4a5, mx6_usb_uboot.conf
Yeah assuming you use the Toradex branch of imx_loader currently this should work.
Our script looks like this: mx6_usb_sdp_uboot #hid/bulk,[old_header,]max packet size, {ram start, ram size}(repeat valid ram areas) hid,1024,0x10000000,1G,0x00907000,0x31000 #Load complete FIT image to $ramdisk_addr_r tezi.itb:load 0x12100000 #Load script to $loadaddr and jump to it boot-sdp.scr:load 0x12000000,jump 0x12000000
-- Stefan
Am I missing something, Thanks, Vincent
2017-09-01 22:09 GMT+02:00 Stefan Agner stefan@agner.ch:
On September 1, 2017 12:25:44 PM PDT, Fabio Estevam festevam@gmail.com wrote:
On Fri, Sep 1, 2017 at 3:54 PM, Fabio Estevam festevam@gmail.com wrote:
I have tested this method and it works, thanks.
Do you plan to usptream this method?
Or I can also put your patch as part of my series that adds SDP support for imx6qsabresd if you prefer.
Let me do a separate, I guess it could be controversial...
-- Stefan -- Sent from my Android device with K-9 Mail. Please excuse my brevity. _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

Hi Stefan,
It was probably a Windows bug as it is almost working now. I was wondering why sdp command is expecting a jump ? Is it necessary to jump after USB transfer, or we should download anything we want and jump if asked ?
Regards, Vincent
2017-09-08 18:37 GMT+02:00 Stefan Agner stefan@agner.ch:
Hi Vincent,
On 2017-09-08 01:27, Vincent Prince wrote:
Hi everyone,
I managed to get SDP protocol to work with on my custom i.MX6 board, so big thanks to Stefan for your work!
I'm now trying to load swupdate ramdisk with u-boot sdp command, without success. Is it a possible use case ?
Yes it should work, we use the U-Boot sdp command to download a squashfs based ramdisk in our Toradex Easy Installer: http://developer.toradex.com/software/toradex-easy-installer
Here my steps:
In u-boot: => sdp 0
On Host, $ imx_usb
config file <C:\Users\vpr\IMXUSBLOADER2\delivery\imx_usb.conf> vid=0x15a2 pid=0x0061 file_name=mx6_usb_rom.conf -> vid=0x0525 pid=0xb4a4 file_name=mx6_usb_sdp_spl.conf vid=0x0525 pid=0xa4a5 file_name=mx6_usb_uboot.conf config file <C:\Users\vpr\IMXUSBLOADER2\delivery\mx6_usb_uboot.conf> parse C:\Users\vpr\IMXUSBLOADER2\delivery\mx6_usb_uboot.conf Trying to open device vid=0x0525 pid=0xa4a5......................... Could not open device vid=0x0525 pid=0xa4a5
Is VID/PID showing up in Device Manager?
We did saw some problems with certain USB host implementation, so it might be a device dependency... I would also try a different machine/maybe Linux machine...
VID/PID in imx_usb.conf are correctly detected, and correct configuration file is launched (mx6_usb_uboot.conf), it contains:
mx6_usb_sdp_uboot hid,1024,0x10000000,1G,0x00907000,0x31000 test.txt:load 0x12100000
My imx_usb.conf is the following:
#vid:pid, config_file 0x15a2:0x0061, mx6_usb_rom.conf, 0x0525:0xb4a4, mx6_usb_sdp_spl.conf 0x0525:0xa4a5, mx6_usb_uboot.conf
Yeah assuming you use the Toradex branch of imx_loader currently this should work.
Our script looks like this: mx6_usb_sdp_uboot #hid/bulk,[old_header,]max packet size, {ram start, ram size}(repeat valid ram areas) hid,1024,0x10000000,1G,0x00907000,0x31000 #Load complete FIT image to $ramdisk_addr_r tezi.itb:load 0x12100000 #Load script to $loadaddr and jump to it boot-sdp.scr:load 0x12000000,jump 0x12000000
-- Stefan
Am I missing something, Thanks, Vincent
2017-09-01 22:09 GMT+02:00 Stefan Agner stefan@agner.ch:
On September 1, 2017 12:25:44 PM PDT, Fabio Estevam festevam@gmail.com wrote:
On Fri, Sep 1, 2017 at 3:54 PM, Fabio Estevam festevam@gmail.com wrote:
I have tested this method and it works, thanks.
Do you plan to usptream this method?
Or I can also put your patch as part of my series that adds SDP support for imx6qsabresd if you prefer.
Let me do a separate, I guess it could be controversial...
-- Stefan -- Sent from my Android device with K-9 Mail. Please excuse my brevity. _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot

On 2017-09-11 07:15, Vincent Prince wrote:
Hi Stefan,
It was probably a Windows bug as it is almost working now.
Yeah with Windows the multiple re-enumeration is sometimes a bit strange, it leads to rather long running driver search on first connect.
I was wondering why sdp command is expecting a jump ? Is it necessary to jump after USB transfer, or we should download anything we want and jump if asked ?
You don't have to jump, you can also go back to the console using Ctrl+C. The jump just allows you to remotely continue booting with a specific script...
-- Stefan
Regards, Vincent
2017-09-08 18:37 GMT+02:00 Stefan Agner stefan@agner.ch:
Hi Vincent,
On 2017-09-08 01:27, Vincent Prince wrote:
Hi everyone,
I managed to get SDP protocol to work with on my custom i.MX6 board, so big thanks to Stefan for your work!
I'm now trying to load swupdate ramdisk with u-boot sdp command, without success. Is it a possible use case ?
Yes it should work, we use the U-Boot sdp command to download a squashfs based ramdisk in our Toradex Easy Installer: http://developer.toradex.com/software/toradex-easy-installer
Here my steps:
In u-boot: => sdp 0
On Host, $ imx_usb
config file <C:\Users\vpr\IMXUSBLOADER2\delivery\imx_usb.conf> vid=0x15a2 pid=0x0061 file_name=mx6_usb_rom.conf -> vid=0x0525 pid=0xb4a4 file_name=mx6_usb_sdp_spl.conf vid=0x0525 pid=0xa4a5 file_name=mx6_usb_uboot.conf config file <C:\Users\vpr\IMXUSBLOADER2\delivery\mx6_usb_uboot.conf> parse C:\Users\vpr\IMXUSBLOADER2\delivery\mx6_usb_uboot.conf Trying to open device vid=0x0525 pid=0xa4a5......................... Could not open device vid=0x0525 pid=0xa4a5
Is VID/PID showing up in Device Manager?
We did saw some problems with certain USB host implementation, so it might be a device dependency... I would also try a different machine/maybe Linux machine...
VID/PID in imx_usb.conf are correctly detected, and correct configuration file is launched (mx6_usb_uboot.conf), it contains:
mx6_usb_sdp_uboot hid,1024,0x10000000,1G,0x00907000,0x31000 test.txt:load 0x12100000
My imx_usb.conf is the following:
#vid:pid, config_file 0x15a2:0x0061, mx6_usb_rom.conf, 0x0525:0xb4a4, mx6_usb_sdp_spl.conf 0x0525:0xa4a5, mx6_usb_uboot.conf
Yeah assuming you use the Toradex branch of imx_loader currently this should work.
Our script looks like this: mx6_usb_sdp_uboot #hid/bulk,[old_header,]max packet size, {ram start, ram size}(repeat valid ram areas) hid,1024,0x10000000,1G,0x00907000,0x31000 #Load complete FIT image to $ramdisk_addr_r tezi.itb:load 0x12100000 #Load script to $loadaddr and jump to it boot-sdp.scr:load 0x12000000,jump 0x12000000
-- Stefan
Am I missing something, Thanks, Vincent
2017-09-01 22:09 GMT+02:00 Stefan Agner stefan@agner.ch:
On September 1, 2017 12:25:44 PM PDT, Fabio Estevam festevam@gmail.com wrote:
On Fri, Sep 1, 2017 at 3:54 PM, Fabio Estevam festevam@gmail.com wrote:
I have tested this method and it works, thanks.
Do you plan to usptream this method?
Or I can also put your patch as part of my series that adds SDP support for imx6qsabresd if you prefer.
Let me do a separate, I guess it could be controversial...
-- Stefan -- Sent from my Android device with K-9 Mail. Please excuse my brevity. _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de https://lists.denx.de/listinfo/u-boot
participants (6)
-
Fabio Estevam
-
Marek Vasut
-
Stefan Agner
-
Stefano Babic
-
Vincent Prince
-
Łukasz Majewski