1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+ 223cd1385SRemy Bohmer /* 323cd1385SRemy Bohmer * usb/gadget/config.c -- simplify building config descriptors 423cd1385SRemy Bohmer * 523cd1385SRemy Bohmer * Copyright (C) 2003 David Brownell 623cd1385SRemy Bohmer * 7a187559eSBin Meng * Ported to U-Boot by: Thomas Smits <ts.smits@gmail.com> and 823cd1385SRemy Bohmer * Remy Bohmer <linux@bohmer.net> 923cd1385SRemy Bohmer */ 1023cd1385SRemy Bohmer 1123cd1385SRemy Bohmer #include <common.h> 12898d686eSTroy Kisky #include <asm/unaligned.h> 131221ce45SMasahiro Yamada #include <linux/errno.h> 1423cd1385SRemy Bohmer #include <linux/list.h> 1523cd1385SRemy Bohmer #include <linux/string.h> 1623cd1385SRemy Bohmer 1723cd1385SRemy Bohmer #include <linux/usb/ch9.h> 1823cd1385SRemy Bohmer #include <linux/usb/gadget.h> 1923cd1385SRemy Bohmer 2023cd1385SRemy Bohmer 2123cd1385SRemy Bohmer /** 2223cd1385SRemy Bohmer * usb_descriptor_fillbuf - fill buffer with descriptors 2323cd1385SRemy Bohmer * @buf: Buffer to be filled 2423cd1385SRemy Bohmer * @buflen: Size of buf 2523cd1385SRemy Bohmer * @src: Array of descriptor pointers, terminated by null pointer. 2623cd1385SRemy Bohmer * 2723cd1385SRemy Bohmer * Copies descriptors into the buffer, returning the length or a 2823cd1385SRemy Bohmer * negative error code if they can't all be copied. Useful when 2923cd1385SRemy Bohmer * assembling descriptors for an associated set of interfaces used 3023cd1385SRemy Bohmer * as part of configuring a composite device; or in other cases where 3123cd1385SRemy Bohmer * sets of descriptors need to be marshaled. 3223cd1385SRemy Bohmer */ 3323cd1385SRemy Bohmer int 3423cd1385SRemy Bohmer usb_descriptor_fillbuf(void *buf, unsigned buflen, 3523cd1385SRemy Bohmer const struct usb_descriptor_header **src) 3623cd1385SRemy Bohmer { 3723cd1385SRemy Bohmer u8 *dest = buf; 3823cd1385SRemy Bohmer 3923cd1385SRemy Bohmer if (!src) 4023cd1385SRemy Bohmer return -EINVAL; 4123cd1385SRemy Bohmer 4223cd1385SRemy Bohmer /* fill buffer from src[] until null descriptor ptr */ 4323cd1385SRemy Bohmer for (; NULL != *src; src++) { 4423cd1385SRemy Bohmer unsigned len = (*src)->bLength; 4523cd1385SRemy Bohmer 4623cd1385SRemy Bohmer if (len > buflen) 4723cd1385SRemy Bohmer return -EINVAL; 4823cd1385SRemy Bohmer memcpy(dest, *src, len); 4923cd1385SRemy Bohmer buflen -= len; 5023cd1385SRemy Bohmer dest += len; 5123cd1385SRemy Bohmer } 5223cd1385SRemy Bohmer return dest - (u8 *)buf; 5323cd1385SRemy Bohmer } 5423cd1385SRemy Bohmer 5523cd1385SRemy Bohmer 5623cd1385SRemy Bohmer /** 5723cd1385SRemy Bohmer * usb_gadget_config_buf - builts a complete configuration descriptor 5823cd1385SRemy Bohmer * @config: Header for the descriptor, including characteristics such 5923cd1385SRemy Bohmer * as power requirements and number of interfaces. 6023cd1385SRemy Bohmer * @desc: Null-terminated vector of pointers to the descriptors (interface, 6123cd1385SRemy Bohmer * endpoint, etc) defining all functions in this device configuration. 6223cd1385SRemy Bohmer * @buf: Buffer for the resulting configuration descriptor. 6323cd1385SRemy Bohmer * @length: Length of buffer. If this is not big enough to hold the 6423cd1385SRemy Bohmer * entire configuration descriptor, an error code will be returned. 6523cd1385SRemy Bohmer * 6623cd1385SRemy Bohmer * This copies descriptors into the response buffer, building a descriptor 6723cd1385SRemy Bohmer * for that configuration. It returns the buffer length or a negative 6823cd1385SRemy Bohmer * status code. The config.wTotalLength field is set to match the length 6923cd1385SRemy Bohmer * of the result, but other descriptor fields (including power usage and 7023cd1385SRemy Bohmer * interface count) must be set by the caller. 7123cd1385SRemy Bohmer * 7223cd1385SRemy Bohmer * Gadget drivers could use this when constructing a config descriptor 7323cd1385SRemy Bohmer * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the 7423cd1385SRemy Bohmer * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed. 7523cd1385SRemy Bohmer */ 7623cd1385SRemy Bohmer int usb_gadget_config_buf( 7723cd1385SRemy Bohmer const struct usb_config_descriptor *config, 7823cd1385SRemy Bohmer void *buf, 7923cd1385SRemy Bohmer unsigned length, 8023cd1385SRemy Bohmer const struct usb_descriptor_header **desc 8123cd1385SRemy Bohmer ) 8223cd1385SRemy Bohmer { 8323cd1385SRemy Bohmer struct usb_config_descriptor *cp = buf; 8423cd1385SRemy Bohmer int len; 8523cd1385SRemy Bohmer 8623cd1385SRemy Bohmer /* config descriptor first */ 8723cd1385SRemy Bohmer if (length < USB_DT_CONFIG_SIZE || !desc) 8823cd1385SRemy Bohmer return -EINVAL; 89898d686eSTroy Kisky /* config need not be aligned */ 90898d686eSTroy Kisky memcpy(cp, config, sizeof(*cp)); 9123cd1385SRemy Bohmer 9223cd1385SRemy Bohmer /* then interface/endpoint/class/vendor/... */ 9323cd1385SRemy Bohmer len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf, 9423cd1385SRemy Bohmer length - USB_DT_CONFIG_SIZE, desc); 9523cd1385SRemy Bohmer if (len < 0) 9623cd1385SRemy Bohmer return len; 9723cd1385SRemy Bohmer len += USB_DT_CONFIG_SIZE; 9823cd1385SRemy Bohmer if (len > 0xffff) 9923cd1385SRemy Bohmer return -EINVAL; 10023cd1385SRemy Bohmer 10123cd1385SRemy Bohmer /* patch up the config descriptor */ 10223cd1385SRemy Bohmer cp->bLength = USB_DT_CONFIG_SIZE; 10323cd1385SRemy Bohmer cp->bDescriptorType = USB_DT_CONFIG; 104898d686eSTroy Kisky put_unaligned_le16(len, &cp->wTotalLength); 10523cd1385SRemy Bohmer cp->bmAttributes |= USB_CONFIG_ATT_ONE; 10623cd1385SRemy Bohmer return len; 10723cd1385SRemy Bohmer } 108