Commit 5cc0710f authored by Tao Ren's avatar Tao Ren Committed by Felipe Balbi
Browse files

usb: gadget: aspeed: support multiple language strings



This patch introduces a link list to store string descriptors with
different languages, and "ast_vhub_rep_string" function is also improved
to support multiple language usb strings.

Signed-off-by: default avatarTao Ren <rentao.bupt@gmail.com>
Signed-off-by: default avatarFelipe Balbi <balbi@kernel.org>
parent a23be4ed
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -408,7 +408,9 @@ static int ast_vhub_probe(struct platform_device *pdev)
		goto err;

	/* Init hub emulation */
	ast_vhub_init_hub(vhub);
	rc = ast_vhub_init_hub(vhub);
	if (rc)
		goto err;

	/* Initialize HW */
	ast_vhub_init_hw(vhub);
+126 −14
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@
#define KERNEL_VER	bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))

enum {
	AST_VHUB_STR_INDEX_MAX = 4,
	AST_VHUB_STR_MANUF = 3,
	AST_VHUB_STR_PRODUCT = 2,
	AST_VHUB_STR_SERIAL = 1,
@@ -310,23 +311,77 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
	return ast_vhub_reply(ep, NULL, len);
}

static struct usb_gadget_strings*
ast_vhub_str_of_container(struct usb_gadget_string_container *container)
{
	return (struct usb_gadget_strings *)container->stash;
}

static int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf,
				      size_t size)
{
	int rc, hdr_len, nlangs, max_langs;
	struct usb_gadget_strings *lang_str;
	struct usb_gadget_string_container *container;
	struct usb_string_descriptor *sdesc = buf;

	nlangs = 0;
	hdr_len = sizeof(struct usb_descriptor_header);
	max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]);
	list_for_each_entry(container, &vhub->vhub_str_desc, list) {
		if (nlangs >= max_langs)
			break;

		lang_str = ast_vhub_str_of_container(container);
		sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language);
	}

	rc = hdr_len + nlangs * sizeof(sdesc->wData[0]);
	sdesc->bLength = rc;
	sdesc->bDescriptorType = USB_DT_STRING;

	return rc;
}

static struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub,
							 u16 lang_id)
{
	struct usb_gadget_strings *lang_str;
	struct usb_gadget_string_container *container;

	list_for_each_entry(container, &vhub->vhub_str_desc, list) {
		lang_str = ast_vhub_str_of_container(container);
		if (lang_str->language == lang_id)
			return lang_str;
	}

	return NULL;
}

static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
			       u8 string_id, u16 lang_id,
			       u16 len)
{
	int rc = usb_gadget_get_string(&ep->vhub->vhub_str_desc,
					string_id, ep->buf);
	int rc;
	u8 buf[256];
	struct ast_vhub *vhub = ep->vhub;
	struct usb_gadget_strings *lang_str;

	/*
	 * This should never happen unless we put too big strings in
	 * the array above
	 */
	BUG_ON(rc >= AST_VHUB_EP0_MAX_PACKET);
	if (string_id == 0) {
		rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf));
	} else {
		lang_str = ast_vhub_lookup_string(vhub, lang_id);
		if (!lang_str)
			return std_req_stall;

	if (rc < 0)
		rc = usb_gadget_get_string(lang_str, string_id, buf);
	}

	if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET)
		return std_req_stall;

	/* Shoot it from the EP buffer */
	memcpy(ep->buf, buf, rc);
	return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
}

@@ -832,8 +887,64 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub)
	writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
}

static void ast_vhub_init_desc(struct ast_vhub *vhub)
static struct usb_gadget_string_container*
ast_vhub_str_container_alloc(struct ast_vhub *vhub)
{
	unsigned int size;
	struct usb_string *str_array;
	struct usb_gadget_strings *lang_str;
	struct usb_gadget_string_container *container;

	size = sizeof(*container);
	size += sizeof(struct usb_gadget_strings);
	size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX;
	container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL);
	if (!container)
		return ERR_PTR(-ENOMEM);

	lang_str = ast_vhub_str_of_container(container);
	str_array = (struct usb_string *)(lang_str + 1);
	lang_str->strings = str_array;
	return container;
}

static void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest,
				   const struct usb_gadget_strings *src)
{
	struct usb_string *src_array = src->strings;
	struct usb_string *dest_array = dest->strings;

	dest->language = src->language;
	if (src_array && dest_array) {
		do {
			*dest_array = *src_array;
			dest_array++;
			src_array++;
		} while (src_array->s);
	}
}

static int ast_vhub_str_alloc_add(struct ast_vhub *vhub,
				  const struct usb_gadget_strings *src_str)
{
	struct usb_gadget_strings *dest_str;
	struct usb_gadget_string_container *container;

	container = ast_vhub_str_container_alloc(vhub);
	if (IS_ERR(container))
		return PTR_ERR(container);

	dest_str = ast_vhub_str_of_container(container);
	ast_vhub_str_deep_copy(dest_str, src_str);
	list_add_tail(&container->list, &vhub->vhub_str_desc);

	return 0;
}

static int ast_vhub_init_desc(struct ast_vhub *vhub)
{
	int ret;

	/* Initialize vhub Device Descriptor. */
	memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
		sizeof(vhub->vhub_dev_desc));
@@ -848,15 +959,16 @@ static void ast_vhub_init_desc(struct ast_vhub *vhub)
	vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;

	/* Initialize vhub String Descriptors. */
	memcpy(&vhub->vhub_str_desc, &ast_vhub_strings,
		sizeof(vhub->vhub_str_desc));
	INIT_LIST_HEAD(&vhub->vhub_str_desc);
	ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings);

	return ret;
}

void ast_vhub_init_hub(struct ast_vhub *vhub)
int ast_vhub_init_hub(struct ast_vhub *vhub)
{
	vhub->speed = USB_SPEED_UNKNOWN;
	INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);

	ast_vhub_init_desc(vhub);
	return ast_vhub_init_desc(vhub);
}
+2 −2
Original line number Diff line number Diff line
@@ -421,7 +421,7 @@ struct ast_vhub {
	struct usb_device_descriptor	vhub_dev_desc;
	struct ast_vhub_full_cdesc	vhub_conf_desc;
	struct usb_hub_descriptor	vhub_hub_desc;
	struct usb_gadget_strings	vhub_str_desc;
	struct list_head		vhub_str_desc;
};

/* Standard request handlers result codes */
@@ -531,7 +531,7 @@ int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...);
			       __VA_ARGS__)

/* hub.c */
void ast_vhub_init_hub(struct ast_vhub *vhub);
int ast_vhub_init_hub(struct ast_vhub *vhub);
enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
					 struct usb_ctrlrequest *crq);
enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,