xref: /rk3399_rockchip-uboot/drivers/usb/gadget/epautoconf.c (revision b2fb47f1873ae812ce33129996a22b11a36d0aa9)
123cd1385SRemy Bohmer /*
223cd1385SRemy Bohmer  * epautoconf.c -- endpoint autoconfiguration for usb gadget drivers
323cd1385SRemy Bohmer  *
423cd1385SRemy Bohmer  * Copyright (C) 2004 David Brownell
523cd1385SRemy Bohmer  *
623cd1385SRemy Bohmer  * This program is free software; you can redistribute it and/or modify
723cd1385SRemy Bohmer  * it under the terms of the GNU General Public License as published by
823cd1385SRemy Bohmer  * the Free Software Foundation; either version 2 of the License, or
923cd1385SRemy Bohmer  * (at your option) any later version.
1023cd1385SRemy Bohmer  *
1123cd1385SRemy Bohmer  * This program is distributed in the hope that it will be useful,
1223cd1385SRemy Bohmer  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1323cd1385SRemy Bohmer  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1423cd1385SRemy Bohmer  * GNU General Public License for more details.
1523cd1385SRemy Bohmer  *
1623cd1385SRemy Bohmer  * You should have received a copy of the GNU General Public License
1723cd1385SRemy Bohmer  * along with this program; if not, write to the Free Software
1823cd1385SRemy Bohmer  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
1923cd1385SRemy Bohmer  *
2023cd1385SRemy Bohmer  * Ported to U-boot by: Thomas Smits <ts.smits@gmail.com> and
2123cd1385SRemy Bohmer  *                      Remy Bohmer <linux@bohmer.net>
2223cd1385SRemy Bohmer  */
2323cd1385SRemy Bohmer 
2423cd1385SRemy Bohmer #include <common.h>
2523cd1385SRemy Bohmer #include <linux/usb/ch9.h>
2623cd1385SRemy Bohmer #include <asm/errno.h>
2723cd1385SRemy Bohmer #include <linux/usb/gadget.h>
28*b2fb47f1STom Rini #include <asm/unaligned.h>
2923cd1385SRemy Bohmer #include "gadget_chips.h"
3023cd1385SRemy Bohmer 
3123cd1385SRemy Bohmer #define isdigit(c)      ('0' <= (c) && (c) <= '9')
3223cd1385SRemy Bohmer 
3323cd1385SRemy Bohmer /* we must assign addresses for configurable endpoints (like net2280) */
3423cd1385SRemy Bohmer static unsigned epnum;
3523cd1385SRemy Bohmer 
366142e0aeSVitaly Kuzmichev /* #define MANY_ENDPOINTS */
3723cd1385SRemy Bohmer #ifdef MANY_ENDPOINTS
3823cd1385SRemy Bohmer /* more than 15 configurable endpoints */
3923cd1385SRemy Bohmer static unsigned in_epnum;
4023cd1385SRemy Bohmer #endif
4123cd1385SRemy Bohmer 
4223cd1385SRemy Bohmer 
4323cd1385SRemy Bohmer /*
4423cd1385SRemy Bohmer  * This should work with endpoints from controller drivers sharing the
4523cd1385SRemy Bohmer  * same endpoint naming convention.  By example:
4623cd1385SRemy Bohmer  *
4723cd1385SRemy Bohmer  *	- ep1, ep2, ... address is fixed, not direction or type
4823cd1385SRemy Bohmer  *	- ep1in, ep2out, ... address and direction are fixed, not type
4923cd1385SRemy Bohmer  *	- ep1-bulk, ep2-bulk, ... address and type are fixed, not direction
5023cd1385SRemy Bohmer  *	- ep1in-bulk, ep2out-iso, ... all three are fixed
5123cd1385SRemy Bohmer  *	- ep-* ... no functionality restrictions
5223cd1385SRemy Bohmer  *
5323cd1385SRemy Bohmer  * Type suffixes are "-bulk", "-iso", or "-int".  Numbers are decimal.
5423cd1385SRemy Bohmer  * Less common restrictions are implied by gadget_is_*().
5523cd1385SRemy Bohmer  *
5623cd1385SRemy Bohmer  * NOTE:  each endpoint is unidirectional, as specified by its USB
5723cd1385SRemy Bohmer  * descriptor; and isn't specific to a configuration or altsetting.
5823cd1385SRemy Bohmer  */
596142e0aeSVitaly Kuzmichev static int ep_matches(
6023cd1385SRemy Bohmer 	struct usb_gadget		*gadget,
6123cd1385SRemy Bohmer 	struct usb_ep			*ep,
6223cd1385SRemy Bohmer 	struct usb_endpoint_descriptor	*desc
6323cd1385SRemy Bohmer )
6423cd1385SRemy Bohmer {
6523cd1385SRemy Bohmer 	u8		type;
6623cd1385SRemy Bohmer 	const char	*tmp;
6723cd1385SRemy Bohmer 	u16		max;
6823cd1385SRemy Bohmer 
6923cd1385SRemy Bohmer 	/* endpoint already claimed? */
7023cd1385SRemy Bohmer 	if (NULL != ep->driver_data)
7123cd1385SRemy Bohmer 		return 0;
7223cd1385SRemy Bohmer 
7323cd1385SRemy Bohmer 	/* only support ep0 for portable CONTROL traffic */
7423cd1385SRemy Bohmer 	type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
7523cd1385SRemy Bohmer 	if (USB_ENDPOINT_XFER_CONTROL == type)
7623cd1385SRemy Bohmer 		return 0;
7723cd1385SRemy Bohmer 
7823cd1385SRemy Bohmer 	/* some other naming convention */
7923cd1385SRemy Bohmer 	if ('e' != ep->name[0])
8023cd1385SRemy Bohmer 		return 0;
8123cd1385SRemy Bohmer 
8223cd1385SRemy Bohmer 	/* type-restriction:  "-iso", "-bulk", or "-int".
8323cd1385SRemy Bohmer 	 * direction-restriction:  "in", "out".
8423cd1385SRemy Bohmer 	 */
8523cd1385SRemy Bohmer 	if ('-' != ep->name[2]) {
8623cd1385SRemy Bohmer 		tmp = strrchr(ep->name, '-');
8723cd1385SRemy Bohmer 		if (tmp) {
8823cd1385SRemy Bohmer 			switch (type) {
8923cd1385SRemy Bohmer 			case USB_ENDPOINT_XFER_INT:
9023cd1385SRemy Bohmer 				/* bulk endpoints handle interrupt transfers,
9123cd1385SRemy Bohmer 				 * except the toggle-quirky iso-synch kind
9223cd1385SRemy Bohmer 				 */
936142e0aeSVitaly Kuzmichev 				if ('s' == tmp[2])	/* == "-iso" */
9423cd1385SRemy Bohmer 					return 0;
9523cd1385SRemy Bohmer 				/* for now, avoid PXA "interrupt-in";
9623cd1385SRemy Bohmer 				 * it's documented as never using DATA1.
9723cd1385SRemy Bohmer 				 */
9823cd1385SRemy Bohmer 				if (gadget_is_pxa(gadget)
9923cd1385SRemy Bohmer 						&& 'i' == tmp[1])
10023cd1385SRemy Bohmer 					return 0;
10123cd1385SRemy Bohmer 				break;
10223cd1385SRemy Bohmer 			case USB_ENDPOINT_XFER_BULK:
1036142e0aeSVitaly Kuzmichev 				if ('b' != tmp[1])	/* != "-bulk" */
10423cd1385SRemy Bohmer 					return 0;
10523cd1385SRemy Bohmer 				break;
10623cd1385SRemy Bohmer 			case USB_ENDPOINT_XFER_ISOC:
1076142e0aeSVitaly Kuzmichev 				if ('s' != tmp[2])	/* != "-iso" */
10823cd1385SRemy Bohmer 					return 0;
10923cd1385SRemy Bohmer 			}
11023cd1385SRemy Bohmer 		} else {
11123cd1385SRemy Bohmer 			tmp = ep->name + strlen(ep->name);
11223cd1385SRemy Bohmer 		}
11323cd1385SRemy Bohmer 
11423cd1385SRemy Bohmer 		/* direction-restriction:  "..in-..", "out-.." */
11523cd1385SRemy Bohmer 		tmp--;
11623cd1385SRemy Bohmer 		if (!isdigit(*tmp)) {
11723cd1385SRemy Bohmer 			if (desc->bEndpointAddress & USB_DIR_IN) {
11823cd1385SRemy Bohmer 				if ('n' != *tmp)
11923cd1385SRemy Bohmer 					return 0;
12023cd1385SRemy Bohmer 			} else {
12123cd1385SRemy Bohmer 				if ('t' != *tmp)
12223cd1385SRemy Bohmer 					return 0;
12323cd1385SRemy Bohmer 			}
12423cd1385SRemy Bohmer 		}
12523cd1385SRemy Bohmer 	}
12623cd1385SRemy Bohmer 
12723cd1385SRemy Bohmer 	/* endpoint maxpacket size is an input parameter, except for bulk
12823cd1385SRemy Bohmer 	 * where it's an output parameter representing the full speed limit.
12923cd1385SRemy Bohmer 	 * the usb spec fixes high speed bulk maxpacket at 512 bytes.
13023cd1385SRemy Bohmer 	 */
131*b2fb47f1STom Rini 	max = 0x7ff & le16_to_cpu(get_unaligned(&desc->wMaxPacketSize));
13223cd1385SRemy Bohmer 	switch (type) {
13323cd1385SRemy Bohmer 	case USB_ENDPOINT_XFER_INT:
13423cd1385SRemy Bohmer 		/* INT:  limit 64 bytes full speed, 1024 high speed */
13523cd1385SRemy Bohmer 		if (!gadget->is_dualspeed && max > 64)
13623cd1385SRemy Bohmer 			return 0;
13723cd1385SRemy Bohmer 		/* FALLTHROUGH */
13823cd1385SRemy Bohmer 
13923cd1385SRemy Bohmer 	case USB_ENDPOINT_XFER_ISOC:
14023cd1385SRemy Bohmer 		/* ISO:  limit 1023 bytes full speed, 1024 high speed */
14123cd1385SRemy Bohmer 		if (ep->maxpacket < max)
14223cd1385SRemy Bohmer 			return 0;
14323cd1385SRemy Bohmer 		if (!gadget->is_dualspeed && max > 1023)
14423cd1385SRemy Bohmer 			return 0;
14523cd1385SRemy Bohmer 
14623cd1385SRemy Bohmer 		/* BOTH:  "high bandwidth" works only at high speed */
147*b2fb47f1STom Rini 		if ((get_unaligned(&desc->wMaxPacketSize) &
148*b2fb47f1STom Rini 					__constant_cpu_to_le16(3<<11))) {
14923cd1385SRemy Bohmer 			if (!gadget->is_dualspeed)
15023cd1385SRemy Bohmer 				return 0;
15123cd1385SRemy Bohmer 			/* configure your hardware with enough buffering!! */
15223cd1385SRemy Bohmer 		}
15323cd1385SRemy Bohmer 		break;
15423cd1385SRemy Bohmer 	}
15523cd1385SRemy Bohmer 
15623cd1385SRemy Bohmer 	/* MATCH!! */
15723cd1385SRemy Bohmer 
15823cd1385SRemy Bohmer 	/* report address */
15923cd1385SRemy Bohmer 	if (isdigit(ep->name[2])) {
160c0ef5131SVitaly Kuzmichev 		u8	num = simple_strtoul(&ep->name[2], NULL, 10);
16123cd1385SRemy Bohmer 		desc->bEndpointAddress |= num;
16223cd1385SRemy Bohmer #ifdef	MANY_ENDPOINTS
16323cd1385SRemy Bohmer 	} else if (desc->bEndpointAddress & USB_DIR_IN) {
16423cd1385SRemy Bohmer 		if (++in_epnum > 15)
16523cd1385SRemy Bohmer 			return 0;
16623cd1385SRemy Bohmer 		desc->bEndpointAddress = USB_DIR_IN | in_epnum;
16723cd1385SRemy Bohmer #endif
16823cd1385SRemy Bohmer 	} else {
16923cd1385SRemy Bohmer 		if (++epnum > 15)
17023cd1385SRemy Bohmer 			return 0;
17123cd1385SRemy Bohmer 		desc->bEndpointAddress |= epnum;
17223cd1385SRemy Bohmer 	}
17323cd1385SRemy Bohmer 
17423cd1385SRemy Bohmer 	/* report (variable) full speed bulk maxpacket */
17523cd1385SRemy Bohmer 	if (USB_ENDPOINT_XFER_BULK == type) {
17623cd1385SRemy Bohmer 		int size = ep->maxpacket;
17723cd1385SRemy Bohmer 
17823cd1385SRemy Bohmer 		/* min() doesn't work on bitfields with gcc-3.5 */
17923cd1385SRemy Bohmer 		if (size > 64)
18023cd1385SRemy Bohmer 			size = 64;
181*b2fb47f1STom Rini 		put_unaligned(cpu_to_le16(size), &desc->wMaxPacketSize);
18223cd1385SRemy Bohmer 	}
18323cd1385SRemy Bohmer 	return 1;
18423cd1385SRemy Bohmer }
18523cd1385SRemy Bohmer 
18623cd1385SRemy Bohmer static struct usb_ep *
18723cd1385SRemy Bohmer find_ep(struct usb_gadget *gadget, const char *name)
18823cd1385SRemy Bohmer {
18923cd1385SRemy Bohmer 	struct usb_ep	*ep;
19023cd1385SRemy Bohmer 
19123cd1385SRemy Bohmer 	list_for_each_entry(ep, &gadget->ep_list, ep_list) {
19223cd1385SRemy Bohmer 		if (0 == strcmp(ep->name, name))
19323cd1385SRemy Bohmer 			return ep;
19423cd1385SRemy Bohmer 	}
19523cd1385SRemy Bohmer 	return NULL;
19623cd1385SRemy Bohmer }
19723cd1385SRemy Bohmer 
19823cd1385SRemy Bohmer /**
19923cd1385SRemy Bohmer  * usb_ep_autoconfig - choose an endpoint matching the descriptor
20023cd1385SRemy Bohmer  * @gadget: The device to which the endpoint must belong.
20123cd1385SRemy Bohmer  * @desc: Endpoint descriptor, with endpoint direction and transfer mode
20223cd1385SRemy Bohmer  *	initialized.  For periodic transfers, the maximum packet
20323cd1385SRemy Bohmer  *	size must also be initialized.  This is modified on success.
20423cd1385SRemy Bohmer  *
20523cd1385SRemy Bohmer  * By choosing an endpoint to use with the specified descriptor, this
20623cd1385SRemy Bohmer  * routine simplifies writing gadget drivers that work with multiple
20723cd1385SRemy Bohmer  * USB device controllers.  The endpoint would be passed later to
20823cd1385SRemy Bohmer  * usb_ep_enable(), along with some descriptor.
20923cd1385SRemy Bohmer  *
21023cd1385SRemy Bohmer  * That second descriptor won't always be the same as the first one.
21123cd1385SRemy Bohmer  * For example, isochronous endpoints can be autoconfigured for high
21223cd1385SRemy Bohmer  * bandwidth, and then used in several lower bandwidth altsettings.
21323cd1385SRemy Bohmer  * Also, high and full speed descriptors will be different.
21423cd1385SRemy Bohmer  *
21523cd1385SRemy Bohmer  * Be sure to examine and test the results of autoconfiguration on your
21623cd1385SRemy Bohmer  * hardware.  This code may not make the best choices about how to use the
21723cd1385SRemy Bohmer  * USB controller, and it can't know all the restrictions that may apply.
21823cd1385SRemy Bohmer  * Some combinations of driver and hardware won't be able to autoconfigure.
21923cd1385SRemy Bohmer  *
22023cd1385SRemy Bohmer  * On success, this returns an un-claimed usb_ep, and modifies the endpoint
22123cd1385SRemy Bohmer  * descriptor bEndpointAddress.  For bulk endpoints, the wMaxPacket value
22223cd1385SRemy Bohmer  * is initialized as if the endpoint were used at full speed.  To prevent
22323cd1385SRemy Bohmer  * the endpoint from being returned by a later autoconfig call, claim it
22423cd1385SRemy Bohmer  * by assigning ep->driver_data to some non-null value.
22523cd1385SRemy Bohmer  *
22623cd1385SRemy Bohmer  * On failure, this returns a null endpoint descriptor.
22723cd1385SRemy Bohmer  */
22823cd1385SRemy Bohmer struct usb_ep *usb_ep_autoconfig(
22923cd1385SRemy Bohmer 	struct usb_gadget		*gadget,
23023cd1385SRemy Bohmer 	struct usb_endpoint_descriptor	*desc
23123cd1385SRemy Bohmer )
23223cd1385SRemy Bohmer {
23323cd1385SRemy Bohmer 	struct usb_ep	*ep;
23423cd1385SRemy Bohmer 	u8		type;
23523cd1385SRemy Bohmer 
23623cd1385SRemy Bohmer 	type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
23723cd1385SRemy Bohmer 
23823cd1385SRemy Bohmer 	/* First, apply chip-specific "best usage" knowledge.
23923cd1385SRemy Bohmer 	 * This might make a good usb_gadget_ops hook ...
24023cd1385SRemy Bohmer 	 */
24123cd1385SRemy Bohmer 	if (gadget_is_net2280(gadget) && type == USB_ENDPOINT_XFER_INT) {
24223cd1385SRemy Bohmer 		/* ep-e, ep-f are PIO with only 64 byte fifos */
24323cd1385SRemy Bohmer 		ep = find_ep(gadget, "ep-e");
24423cd1385SRemy Bohmer 		if (ep && ep_matches(gadget, ep, desc))
24523cd1385SRemy Bohmer 			return ep;
24623cd1385SRemy Bohmer 		ep = find_ep(gadget, "ep-f");
24723cd1385SRemy Bohmer 		if (ep && ep_matches(gadget, ep, desc))
24823cd1385SRemy Bohmer 			return ep;
24923cd1385SRemy Bohmer 
25023cd1385SRemy Bohmer 	} else if (gadget_is_goku(gadget)) {
25123cd1385SRemy Bohmer 		if (USB_ENDPOINT_XFER_INT == type) {
25223cd1385SRemy Bohmer 			/* single buffering is enough */
25323cd1385SRemy Bohmer 			ep = find_ep(gadget, "ep3-bulk");
25423cd1385SRemy Bohmer 			if (ep && ep_matches(gadget, ep, desc))
25523cd1385SRemy Bohmer 				return ep;
25623cd1385SRemy Bohmer 		} else if (USB_ENDPOINT_XFER_BULK == type
25723cd1385SRemy Bohmer 				&& (USB_DIR_IN & desc->bEndpointAddress)) {
25823cd1385SRemy Bohmer 			/* DMA may be available */
25923cd1385SRemy Bohmer 			ep = find_ep(gadget, "ep2-bulk");
26023cd1385SRemy Bohmer 			if (ep && ep_matches(gadget, ep, desc))
26123cd1385SRemy Bohmer 				return ep;
26223cd1385SRemy Bohmer 		}
26323cd1385SRemy Bohmer 
26423cd1385SRemy Bohmer 	} else if (gadget_is_sh(gadget) && USB_ENDPOINT_XFER_INT == type) {
26523cd1385SRemy Bohmer 		/* single buffering is enough; maybe 8 byte fifo is too */
26623cd1385SRemy Bohmer 		ep = find_ep(gadget, "ep3in-bulk");
26723cd1385SRemy Bohmer 		if (ep && ep_matches(gadget, ep, desc))
26823cd1385SRemy Bohmer 			return ep;
26923cd1385SRemy Bohmer 
27023cd1385SRemy Bohmer 	} else if (gadget_is_mq11xx(gadget) && USB_ENDPOINT_XFER_INT == type) {
27123cd1385SRemy Bohmer 		ep = find_ep(gadget, "ep1-bulk");
27223cd1385SRemy Bohmer 		if (ep && ep_matches(gadget, ep, desc))
27323cd1385SRemy Bohmer 			return ep;
27423cd1385SRemy Bohmer 	}
27523cd1385SRemy Bohmer 
27623cd1385SRemy Bohmer 	/* Second, look at endpoints until an unclaimed one looks usable */
27723cd1385SRemy Bohmer 	list_for_each_entry(ep, &gadget->ep_list, ep_list) {
27823cd1385SRemy Bohmer 		if (ep_matches(gadget, ep, desc))
27923cd1385SRemy Bohmer 			return ep;
28023cd1385SRemy Bohmer 	}
28123cd1385SRemy Bohmer 
28223cd1385SRemy Bohmer 	/* Fail */
28323cd1385SRemy Bohmer 	return NULL;
28423cd1385SRemy Bohmer }
28523cd1385SRemy Bohmer 
28623cd1385SRemy Bohmer /**
28723cd1385SRemy Bohmer  * usb_ep_autoconfig_reset - reset endpoint autoconfig state
28823cd1385SRemy Bohmer  * @gadget: device for which autoconfig state will be reset
28923cd1385SRemy Bohmer  *
29023cd1385SRemy Bohmer  * Use this for devices where one configuration may need to assign
29123cd1385SRemy Bohmer  * endpoint resources very differently from the next one.  It clears
29223cd1385SRemy Bohmer  * state such as ep->driver_data and the record of assigned endpoints
29323cd1385SRemy Bohmer  * used by usb_ep_autoconfig().
29423cd1385SRemy Bohmer  */
29523cd1385SRemy Bohmer void usb_ep_autoconfig_reset(struct usb_gadget *gadget)
29623cd1385SRemy Bohmer {
29723cd1385SRemy Bohmer 	struct usb_ep	*ep;
29823cd1385SRemy Bohmer 
29923cd1385SRemy Bohmer 	list_for_each_entry(ep, &gadget->ep_list, ep_list) {
30023cd1385SRemy Bohmer 		ep->driver_data = NULL;
30123cd1385SRemy Bohmer 	}
30223cd1385SRemy Bohmer #ifdef	MANY_ENDPOINTS
30323cd1385SRemy Bohmer 	in_epnum = 0;
30423cd1385SRemy Bohmer #endif
30523cd1385SRemy Bohmer 	epnum = 0;
30623cd1385SRemy Bohmer }
307