[U-Boot] [PATCH v1 2/7] usb: gadget: add SDP driver

Stefano Babic sbabic at denx.de
Wed Aug 16 09:49:12 UTC 2017


Hi Stefan,

On 15/08/2017 23:54, Stefan Agner wrote:
>>
>> It looks like that I am again out of sync with documentation. Where is
>> defined SDP_SKIP_DCD_HEADER ? It is undefined for MX6Q/D, Solo and DL.
>>
> 
> This is only available in newer SoC's e.g. i.MX 7.

ok

> 
> It allows to skip the DCD header in a downloaded image. Since the DCD
> header is anyway ignored by this SDP implementation, the command is kind
> of useless. I still think it is a good idea to have the command type
> define for completeness... 

I agree with you - I just wanted to understand.

>And I think also some SDP host side
> implementation might issue the command...

That's ok, thanks for clarification.

>>
>> It is fine, but I am just missing if this is a use case. DCD is
>> interpreted by boot ROM, and we are here already over in SPL.
>>
> 
> I also don't have a use case currently, but it is rather cheap so why
> don't?

Yes, that's fine.

> 
> Note that the SDP_READ_REGISTER command is actually used by the
> sb_loader to do some verification whether the image got correctly
> downloaded... But I don't think it required DCD_WRITE for something, but
> I would have to retest.
> 
>>> +	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 */
>>
>> Right - we load a file, we do not need a DCD.
>>
>>> +		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);
>>> +	debug("req_type: 0x%x ctrl->bRequest: 0x%x 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(void)
>>> +{
>>> +	printf("SDP: initialize...\n");
>>> +	while (!sdp_func->configuration_done) {
>>> +		if (ctrlc()) {
>>> +			puts("\rCTRL+C - Operation aborted.\n");
>>> +			return 0;
>>> +		}
>>> +		usb_gadget_handle_interrupts(0);
>>> +	}
>>> +
>>> +	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 a 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 wont 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;
>>> +
>>> +		data[1] = 0x56;
>>> +		data[2] = 0x78;
>>> +		data[3] = 0x78;
>>> +		data[4] = 0x56;
>>
>> I am quite lost here - can you explain what are these magic numbers, and
>> maybe add a comment for it (or self explaining defines) ?
>>
> 
> Yeah protocol specific magic number:
> HAB security
> configuration. Device
> sends 0x12343412 in
> closed mode and
> 0x56787856 in open
> mode.
> 
> 
> We always assume open. Not sure what kind of implication that can have,
> I think imx_usb basically just prints out what the device says.
> 
> Will create proper defines.

Thanks.

Best regards,
Stefano

-- 
=====================================================================
DENX Software Engineering GmbH,      Managing Director: Wolfgang Denk
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: +49-8142-66989-53 Fax: +49-8142-66989-80 Email: sbabic at denx.de
=====================================================================


More information about the U-Boot mailing list