All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Lothar Waßmann" <LW@KARO-electronics.de>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH v1 2/7] usb: gadget: add SDP driver
Date: Tue, 8 Aug 2017 12:42:17 +0200	[thread overview]
Message-ID: <20170808124217.11edd6bb@karo-electronics.de> (raw)
In-Reply-To: <20170804233813.6136-3-stefan@agner.ch>

Hi,

On Fri,  4 Aug 2017 16:38:08 -0700 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
> and reading registers.
> 
> Signed-off-by: Stefan Agner <stefan.agner@toradex.com>
> ---
> 
>  drivers/usb/gadget/Kconfig  |   7 +
>  drivers/usb/gadget/Makefile |   1 +
>  drivers/usb/gadget/f_sdp.c  | 723 ++++++++++++++++++++++++++++++++++++++++++++
>  include/sdp.h               |  16 +
>  4 files changed, 747 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..eb89695aaf
> --- /dev/null
> +++ b/drivers/usb/gadget/f_sdp.c
> @@ -0,0 +1,723 @@
> +/*
> + * f_sdp.c -- USB HID Serial Download Protocol
> + *
> + * Copyright (C) 2016 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_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 (Poiter?) */
> +		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: %08x\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%x len: 0x%x\n", w_value, len);
>
s/0x%x/0x%04x/g ?

> +	debug("req_type: 0x%x ctrl->bRequest: 0x%x sdp->state: %d\n",
s/0x%x/0x%02x/g ?

[...]
> +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 a IMX image\n");
<nit>
s/a IMX/an IMX/'

> +		return SDP_ERROR_IMXHEADER;
> +	}
> +
> +	printf("Jumping to 0x%08x\n", headerv2->entry);
> +	entry = (void *)headerv2->entry;
> +	entry();
> +
> +	/* The image probably never returns hence we wont reach that point */
s/wont/won't/
</nit>

[...]
> +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: 0x%p gadget:0x%p gadget->ep0: 0x%p\n", __func__,
>
The string printed with '%p' already contains a '0x' prefix.


Lothar Waßmann
-- 
___________________________________________________________

Ka-Ro electronics GmbH | Pascalstraße 22 | D - 52076 Aachen
Phone: +49 2408 1402-0 | Fax: +49 2408 1402-10
Geschäftsführer: Matthias Kaussen
Handelsregistereintrag: Amtsgericht Aachen, HRB 4996

www.karo-electronics.de | info at karo-electronics.de
___________________________________________________________

  reply	other threads:[~2017-08-08 10:42 UTC|newest]

Thread overview: 30+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-08-04 23:38 [U-Boot] [PATCH v1 0/7] imx: add USB Serial Download Protocol (SDP) support Stefan Agner
2017-08-04 23:38 ` [U-Boot] [PATCH v1 1/7] imx: move imximage header to common location Stefan Agner
2017-08-08 22:09   ` Łukasz Majewski
2017-08-04 23:38 ` [U-Boot] [PATCH v1 2/7] usb: gadget: add SDP driver Stefan Agner
2017-08-08 10:42   ` Lothar Waßmann [this message]
2017-08-08 22:16   ` Łukasz Majewski
2017-08-10  8:14   ` Stefano Babic
2017-08-15 21:54     ` Stefan Agner
2017-08-16  9:49       ` Stefano Babic
2017-08-04 23:38 ` [U-Boot] [PATCH v1 3/7] usb: gadget: sdp: extend images compatible for jumps Stefan Agner
2017-08-08 22:17   ` Łukasz Majewski
2017-08-10  8:15   ` Stefano Babic
2017-08-04 23:38 ` [U-Boot] [PATCH v1 4/7] cmd: add sdp command Stefan Agner
2017-08-08 22:19   ` Łukasz Majewski
2017-08-10  8:16   ` Stefano Babic
2017-08-04 23:38 ` [U-Boot] [PATCH v1 5/7] spl: add serial download protocol (SDP) support Stefan Agner
2017-08-08 10:43   ` Lothar Waßmann
2017-08-08 21:23     ` Stefan Agner
2017-08-09  0:59   ` Stefan Agner
2017-08-10  7:56     ` Stefano Babic
2017-08-10  8:17   ` Stefano Babic
2017-08-04 23:38 ` [U-Boot] [PATCH v1 6/7] apalis/colibri_imx6: use independent USB PID for SPL Stefan Agner
2017-08-04 23:38 ` [U-Boot] [PATCH v1 7/7] apalis/colibri_imx6: enable SDP by default Stefan Agner
2017-08-06 15:19 ` [U-Boot] [PATCH v1 0/7] imx: add USB Serial Download Protocol (SDP) support Eric Nelson
2017-08-07 18:06   ` Stefan Agner
2017-08-08  9:15     ` Stefano Babic
2017-08-08 12:05       ` Marcel Ziswiler
2017-08-08 17:35       ` Stefan Agner
2017-08-08 23:25     ` Eric Nelson
2017-08-08 22:08 ` Łukasz Majewski

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20170808124217.11edd6bb@karo-electronics.de \
    --to=lw@karo-electronics.de \
    --cc=u-boot@lists.denx.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.