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