usb_template.c revision 184610
187139Smarkm/* $FreeBSD: head/sys/dev/usb2/template/usb2_template.c 184610 2008-11-04 02:31:03Z alfred $ */
229088Smarkm/*-
329088Smarkm * Copyright (c) 2007 Hans Petter Selasky. All rights reserved.
429088Smarkm *
529088Smarkm * Redistribution and use in source and binary forms, with or without
629088Smarkm * modification, are permitted provided that the following conditions
729088Smarkm * are met:
829088Smarkm * 1. Redistributions of source code must retain the above copyright
929088Smarkm *    notice, this list of conditions and the following disclaimer.
1029088Smarkm * 2. Redistributions in binary form must reproduce the above copyright
1129088Smarkm *    notice, this list of conditions and the following disclaimer in the
1229088Smarkm *    documentation and/or other materials provided with the distribution.
1329088Smarkm *
1429088Smarkm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1529088Smarkm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1629088Smarkm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1729088Smarkm * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1829088Smarkm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1929088Smarkm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2029088Smarkm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2129088Smarkm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2229088Smarkm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2329088Smarkm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2429088Smarkm * SUCH DAMAGE.
2529088Smarkm */
2629088Smarkm
2729088Smarkm/*
2829088Smarkm * This file contains sub-routines to build up USB descriptors from
2929088Smarkm * USB templates.
3029088Smarkm */
3129088Smarkm
3229088Smarkm#include <dev/usb2/include/usb2_standard.h>
3329088Smarkm#include <dev/usb2/include/usb2_cdc.h>
34114630Sobrien#include <dev/usb2/include/usb2_mfunc.h>
3529088Smarkm#include <dev/usb2/include/usb2_defs.h>
3629181Smarkm#include <dev/usb2/include/usb2_error.h>
3731622Scharnier
38114630Sobrien#define	USB_DEBUG_VAR usb2_debug
39114630Sobrien
40114630Sobrien#include <dev/usb2/core/usb2_core.h>
4129088Smarkm#include <dev/usb2/core/usb2_busdma.h>
4287139Smarkm#include <dev/usb2/core/usb2_process.h>
4387139Smarkm#include <dev/usb2/core/usb2_debug.h>
4487139Smarkm#include <dev/usb2/core/usb2_parse.h>
4587139Smarkm#include <dev/usb2/core/usb2_device.h>
4687139Smarkm#include <dev/usb2/core/usb2_dynamic.h>
4787139Smarkm
4829088Smarkm#include <dev/usb2/controller/usb2_controller.h>
4929088Smarkm#include <dev/usb2/controller/usb2_bus.h>
5029088Smarkm
5187139Smarkm#include <dev/usb2/template/usb2_template.h>
5229088Smarkm
5329088SmarkmMODULE_DEPEND(usb2_template, usb2_core, 1, 1, 1);
5429088SmarkmMODULE_VERSION(usb2_template, 1);
5587139Smarkm
5629181Smarkm/* function prototypes */
5729181Smarkm
5829088Smarkmstatic void usb2_make_raw_desc(struct usb2_temp_setup *temp, const uint8_t *raw);
5929088Smarkmstatic void usb2_make_endpoint_desc(struct usb2_temp_setup *temp, const struct usb2_temp_endpoint_desc *ted);
6029181Smarkmstatic void usb2_make_interface_desc(struct usb2_temp_setup *temp, const struct usb2_temp_interface_desc *tid);
6129181Smarkmstatic void usb2_make_config_desc(struct usb2_temp_setup *temp, const struct usb2_temp_config_desc *tcd);
6229181Smarkmstatic void usb2_make_device_desc(struct usb2_temp_setup *temp, const struct usb2_temp_device_desc *tdd);
6387139Smarkmstatic uint8_t usb2_hw_ep_match(const struct usb2_hw_ep_profile *pf, uint8_t ep_type, uint8_t ep_dir_in);
6429181Smarkmstatic uint8_t usb2_hw_ep_find_match(struct usb2_hw_ep_scratch *ues, struct usb2_hw_ep_scratch_sub *ep, uint8_t is_simplex);
6529181Smarkmstatic uint8_t usb2_hw_ep_get_needs(struct usb2_hw_ep_scratch *ues, uint8_t ep_type, uint8_t is_complete);
6629181Smarkmstatic usb2_error_t usb2_hw_ep_resolve(struct usb2_device *udev, struct usb2_descriptor *desc);
6729181Smarkmstatic const struct usb2_temp_device_desc *usb2_temp_get_tdd(struct usb2_device *udev);
6887139Smarkmstatic void *usb2_temp_get_device_desc(struct usb2_device *udev);
6929181Smarkmstatic void *usb2_temp_get_qualifier_desc(struct usb2_device *udev);
7029088Smarkmstatic void *usb2_temp_get_config_desc(struct usb2_device *udev, uint16_t *pLength, uint8_t index);
7187139Smarkmstatic const void *usb2_temp_get_string_desc(struct usb2_device *udev, uint16_t lang_id, uint8_t string_index);
7287139Smarkmstatic const void *usb2_temp_get_vendor_desc(struct usb2_device *udev, const struct usb2_device_request *req);
7329181Smarkmstatic const void *usb2_temp_get_hub_desc(struct usb2_device *udev);
7429088Smarkmstatic void usb2_temp_get_desc(struct usb2_device *udev, struct usb2_device_request *req, const void **pPtr, uint16_t *pLength);
7529088Smarkmstatic usb2_error_t usb2_temp_setup(struct usb2_device *udev, const struct usb2_temp_device_desc *tdd);
7629088Smarkmstatic void usb2_temp_unsetup(struct usb2_device *udev);
7729088Smarkmstatic usb2_error_t usb2_temp_setup_by_index(struct usb2_device *udev, uint16_t index);
7829088Smarkmstatic void usb2_temp_init(void *arg);
7929088Smarkm
8029088Smarkm/*------------------------------------------------------------------------*
8129088Smarkm *	usb2_make_raw_desc
8229088Smarkm *
8329088Smarkm * This function will insert a raw USB descriptor into the generated
8429088Smarkm * USB configuration.
8529088Smarkm *------------------------------------------------------------------------*/
8629088Smarkmstatic void
8729088Smarkmusb2_make_raw_desc(struct usb2_temp_setup *temp,
8829088Smarkm    const uint8_t *raw)
8929088Smarkm{
9029088Smarkm	void *dst;
9129088Smarkm	uint8_t len;
9229088Smarkm
9329088Smarkm	/*
9429088Smarkm         * The first byte of any USB descriptor gives the length.
9529088Smarkm         */
9629088Smarkm	if (raw) {
9729088Smarkm		len = raw[0];
9829088Smarkm		if (temp->buf) {
9929088Smarkm			dst = USB_ADD_BYTES(temp->buf, temp->size);
10029088Smarkm			bcopy(raw, dst, len);
10129088Smarkm
10229088Smarkm			/* check if we have got a CDC union descriptor */
10329088Smarkm
10429088Smarkm			if ((raw[0] >= sizeof(struct usb2_cdc_union_descriptor)) &&
10529088Smarkm			    (raw[1] == UDESC_CS_INTERFACE) &&
10629088Smarkm			    (raw[2] == UDESCSUB_CDC_UNION)) {
10729088Smarkm				struct usb2_cdc_union_descriptor *ud = (void *)dst;
10829088Smarkm
10929088Smarkm				/* update the interface numbers */
11029088Smarkm
11129088Smarkm				ud->bMasterInterface +=
11229088Smarkm				    temp->bInterfaceNumber;
11329088Smarkm				ud->bSlaveInterface[0] +=
11429088Smarkm				    temp->bInterfaceNumber;
11529088Smarkm			}
11629088Smarkm		}
11729088Smarkm		temp->size += len;
11829088Smarkm	}
11929088Smarkm	return;
12029088Smarkm}
12129088Smarkm
12229088Smarkm/*------------------------------------------------------------------------*
12329088Smarkm *	usb2_make_endpoint_desc
12429088Smarkm *
12529088Smarkm * This function will generate an USB endpoint descriptor from the
12629088Smarkm * given USB template endpoint descriptor, which will be inserted into
12729088Smarkm * the USB configuration.
12829088Smarkm *------------------------------------------------------------------------*/
12929088Smarkmstatic void
13029088Smarkmusb2_make_endpoint_desc(struct usb2_temp_setup *temp,
13129088Smarkm    const struct usb2_temp_endpoint_desc *ted)
13229088Smarkm{
13329088Smarkm	struct usb2_endpoint_descriptor *ed;
13429088Smarkm	const void **rd;
13529088Smarkm	uint16_t old_size;
13629088Smarkm	uint16_t mps;
13729088Smarkm	uint8_t ea = 0;			/* Endpoint Address */
13829088Smarkm	uint8_t et = 0;			/* Endpiont Type */
13929088Smarkm
14031622Scharnier	/* Reserve memory */
14131622Scharnier	old_size = temp->size;
14231622Scharnier	temp->size += sizeof(*ed);
14387155Smarkm
14487155Smarkm	/* Scan all Raw Descriptors first */
14587155Smarkm
14631622Scharnier	rd = ted->ppRawDesc;
14729088Smarkm	if (rd) {
14829088Smarkm		while (*rd) {
14929088Smarkm			usb2_make_raw_desc(temp, *rd);
15029088Smarkm			rd++;
15129088Smarkm		}
15229088Smarkm	}
15329088Smarkm	if (ted->pPacketSize == NULL) {
15429088Smarkm		/* not initialized */
15529088Smarkm		temp->err = USB_ERR_INVAL;
15629088Smarkm		return;
15729088Smarkm	}
15887139Smarkm	mps = ted->pPacketSize->mps[temp->usb2_speed];
15987139Smarkm	if (mps == 0) {
16029088Smarkm		/* not initialized */
16129088Smarkm		temp->err = USB_ERR_INVAL;
16229088Smarkm		return;
16329088Smarkm	} else if (mps == UE_ZERO_MPS) {
16429088Smarkm		/* escape for Zero Max Packet Size */
16529088Smarkm		mps = 0;
16629088Smarkm	}
16729088Smarkm	ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT));
16829088Smarkm	et = (ted->bmAttributes & UE_XFERTYPE);
16929088Smarkm
17029088Smarkm	/*
17129088Smarkm	 * Fill out the real USB endpoint descriptor
17229088Smarkm	 * in case there is a buffer present:
17329088Smarkm	 */
17429088Smarkm	if (temp->buf) {
17587139Smarkm		ed = USB_ADD_BYTES(temp->buf, old_size);
17687139Smarkm		ed->bLength = sizeof(*ed);
17729088Smarkm		ed->bDescriptorType = UDESC_ENDPOINT;
17829088Smarkm		ed->bEndpointAddress = ea;
17929088Smarkm		ed->bmAttributes = ted->bmAttributes;
18029088Smarkm		USETW(ed->wMaxPacketSize, mps);
18129088Smarkm
18229088Smarkm		/* setup bInterval parameter */
18329088Smarkm
18429088Smarkm		if (ted->pIntervals &&
18587139Smarkm		    ted->pIntervals->bInterval[temp->usb2_speed]) {
18687139Smarkm			ed->bInterval =
18729088Smarkm			    ted->pIntervals->bInterval[temp->usb2_speed];
18829088Smarkm		} else {
18929088Smarkm			switch (et) {
19029088Smarkm			case UE_BULK:
19129088Smarkm			case UE_CONTROL:
19229088Smarkm				ed->bInterval = 0;	/* not used */
19329088Smarkm				break;
19429088Smarkm			case UE_INTERRUPT:
19529088Smarkm				switch (temp->usb2_speed) {
19629088Smarkm				case USB_SPEED_LOW:
19729088Smarkm				case USB_SPEED_FULL:
19829088Smarkm					ed->bInterval = 1;	/* 1 ms */
19929088Smarkm					break;
20029088Smarkm				default:
20129088Smarkm					ed->bInterval = 8;	/* 8*125 us */
20229088Smarkm					break;
20329088Smarkm				}
20429088Smarkm				break;
20529088Smarkm			default:	/* UE_ISOCHRONOUS */
20629088Smarkm				switch (temp->usb2_speed) {
20729088Smarkm				case USB_SPEED_LOW:
20829088Smarkm				case USB_SPEED_FULL:
20929088Smarkm					ed->bInterval = 1;	/* 1 ms */
21029088Smarkm					break;
21129088Smarkm				default:
21229088Smarkm					ed->bInterval = 1;	/* 125 us */
21329088Smarkm					break;
21429088Smarkm				}
21529088Smarkm				break;
21629088Smarkm			}
21729088Smarkm		}
21829088Smarkm	}
21929088Smarkm	temp->bNumEndpoints++;
22029088Smarkm	return;
22129088Smarkm}
22287139Smarkm
22387139Smarkm/*------------------------------------------------------------------------*
22429088Smarkm *	usb2_make_interface_desc
22529088Smarkm *
22629088Smarkm * This function will generate an USB interface descriptor from the
22729088Smarkm * given USB template interface descriptor, which will be inserted
22829088Smarkm * into the USB configuration.
22929088Smarkm *------------------------------------------------------------------------*/
23029088Smarkmstatic void
23129088Smarkmusb2_make_interface_desc(struct usb2_temp_setup *temp,
23229088Smarkm    const struct usb2_temp_interface_desc *tid)
23329088Smarkm{
23429088Smarkm	struct usb2_interface_descriptor *id;
23529088Smarkm	const struct usb2_temp_endpoint_desc **ted;
23629088Smarkm	const void **rd;
23729088Smarkm	uint16_t old_size;
23829088Smarkm
23929088Smarkm	/* Reserve memory */
24029088Smarkm
24129088Smarkm	old_size = temp->size;
24229088Smarkm	temp->size += sizeof(*id);
24329088Smarkm
24429088Smarkm	/* Update interface and alternate interface numbers */
24529088Smarkm
24629088Smarkm	if (tid->isAltInterface == 0) {
24729088Smarkm		temp->bAlternateSetting = 0;
24829088Smarkm		temp->bInterfaceNumber++;
24929088Smarkm	} else {
25029088Smarkm		temp->bAlternateSetting++;
25129088Smarkm	}
25229088Smarkm
25329088Smarkm	/* Scan all Raw Descriptors first */
25429088Smarkm
25529088Smarkm	rd = tid->ppRawDesc;
25629088Smarkm
25729088Smarkm	if (rd) {
25829088Smarkm		while (*rd) {
25929088Smarkm			usb2_make_raw_desc(temp, *rd);
26029088Smarkm			rd++;
26129088Smarkm		}
26229088Smarkm	}
26329088Smarkm	/* Reset some counters */
26429088Smarkm
26529088Smarkm	temp->bNumEndpoints = 0;
26629088Smarkm
26729088Smarkm	/* Scan all Endpoint Descriptors second */
26829088Smarkm
26929088Smarkm	ted = tid->ppEndpoints;
27029088Smarkm	if (ted) {
27129088Smarkm		while (*ted) {
27229088Smarkm			usb2_make_endpoint_desc(temp, *ted);
27329088Smarkm			ted++;
27429088Smarkm		}
27529088Smarkm	}
27629088Smarkm	/*
27729088Smarkm	 * Fill out the real USB interface descriptor
27829088Smarkm	 * in case there is a buffer present:
27929088Smarkm	 */
28029088Smarkm	if (temp->buf) {
28129088Smarkm		id = USB_ADD_BYTES(temp->buf, old_size);
28229088Smarkm		id->bLength = sizeof(*id);
28329088Smarkm		id->bDescriptorType = UDESC_INTERFACE;
28429088Smarkm		id->bInterfaceNumber = temp->bInterfaceNumber;
28529088Smarkm		id->bAlternateSetting = temp->bAlternateSetting;
28629088Smarkm		id->bNumEndpoints = temp->bNumEndpoints;
28729088Smarkm		id->bInterfaceClass = tid->bInterfaceClass;
28829088Smarkm		id->bInterfaceSubClass = tid->bInterfaceSubClass;
28929088Smarkm		id->bInterfaceProtocol = tid->bInterfaceProtocol;
29029088Smarkm		id->iInterface = tid->iInterface;
29129088Smarkm	}
29229088Smarkm	return;
29329088Smarkm}
29429088Smarkm
29529088Smarkm/*------------------------------------------------------------------------*
29629088Smarkm *	usb2_make_config_desc
29729088Smarkm *
29829088Smarkm * This function will generate an USB config descriptor from the given
29929088Smarkm * USB template config descriptor, which will be inserted into the USB
30087139Smarkm * configuration.
30187139Smarkm *------------------------------------------------------------------------*/
30287139Smarkmstatic void
30329088Smarkmusb2_make_config_desc(struct usb2_temp_setup *temp,
30429088Smarkm    const struct usb2_temp_config_desc *tcd)
30529088Smarkm{
30629088Smarkm	struct usb2_config_descriptor *cd;
30729088Smarkm	const struct usb2_temp_interface_desc **tid;
30829088Smarkm	uint16_t old_size;
30929088Smarkm
31029088Smarkm	/* Reserve memory */
31129088Smarkm
31229088Smarkm	old_size = temp->size;
31329088Smarkm	temp->size += sizeof(*cd);
31429088Smarkm
31529088Smarkm	/* Reset some counters */
31629088Smarkm
31729088Smarkm	temp->bInterfaceNumber = 0 - 1;
31829088Smarkm	temp->bAlternateSetting = 0;
31929088Smarkm
32029088Smarkm	/* Scan all the USB interfaces */
32129088Smarkm
32229088Smarkm	tid = tcd->ppIfaceDesc;
32329088Smarkm	if (tid) {
32429088Smarkm		while (*tid) {
32529088Smarkm			usb2_make_interface_desc(temp, *tid);
32629088Smarkm			tid++;
32729088Smarkm		}
32829088Smarkm	}
32929088Smarkm	/*
33029088Smarkm	 * Fill out the real USB config descriptor
33129088Smarkm	 * in case there is a buffer present:
33229088Smarkm	 */
33329088Smarkm	if (temp->buf) {
33429088Smarkm		cd = USB_ADD_BYTES(temp->buf, old_size);
33529088Smarkm
33629088Smarkm		/* compute total size */
33729088Smarkm		old_size = temp->size - old_size;
33829088Smarkm
33929088Smarkm		cd->bLength = sizeof(*cd);
34029088Smarkm		cd->bDescriptorType = UDESC_CONFIG;
34129088Smarkm		USETW(cd->wTotalLength, old_size);
34229088Smarkm		cd->bNumInterface = temp->bInterfaceNumber + 1;
34329088Smarkm		cd->bConfigurationValue = temp->bConfigurationValue;
34429088Smarkm		cd->iConfiguration = tcd->iConfiguration;
34529088Smarkm		cd->bmAttributes = tcd->bmAttributes;
34629088Smarkm		cd->bMaxPower = tcd->bMaxPower;
34729088Smarkm		cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED);
34829088Smarkm
34929088Smarkm		if (temp->self_powered) {
35029088Smarkm			cd->bmAttributes |= UC_SELF_POWERED;
35129088Smarkm		} else {
35229088Smarkm			cd->bmAttributes &= ~UC_SELF_POWERED;
35329088Smarkm		}
35429088Smarkm	}
35529088Smarkm	return;
35629088Smarkm}
35729088Smarkm
35829088Smarkm/*------------------------------------------------------------------------*
35929088Smarkm *	usb2_make_device_desc
36029088Smarkm *
36129088Smarkm * This function will generate an USB device descriptor from the
36229088Smarkm * given USB template device descriptor.
36329088Smarkm *------------------------------------------------------------------------*/
36429088Smarkmstatic void
36529088Smarkmusb2_make_device_desc(struct usb2_temp_setup *temp,
36629088Smarkm    const struct usb2_temp_device_desc *tdd)
36729088Smarkm{
36829088Smarkm	struct usb2_temp_data *utd;
36929088Smarkm	const struct usb2_temp_config_desc **tcd;
37029088Smarkm	uint16_t old_size;
37129088Smarkm
37229088Smarkm	/* Reserve memory */
37329088Smarkm
37429088Smarkm	old_size = temp->size;
37529088Smarkm	temp->size += sizeof(*utd);
37629088Smarkm
37729088Smarkm	/* Scan all the USB configs */
37829088Smarkm
37929088Smarkm	temp->bConfigurationValue = 1;
38029088Smarkm	tcd = tdd->ppConfigDesc;
38129088Smarkm	if (tcd) {
38229088Smarkm		while (*tcd) {
38329088Smarkm			usb2_make_config_desc(temp, *tcd);
38429088Smarkm			temp->bConfigurationValue++;
38529088Smarkm			tcd++;
38629088Smarkm		}
38729088Smarkm	}
38829088Smarkm	/*
38929088Smarkm	 * Fill out the real USB device descriptor
39029088Smarkm	 * in case there is a buffer present:
39129088Smarkm	 */
39229088Smarkm
39329088Smarkm	if (temp->buf) {
39429088Smarkm		utd = USB_ADD_BYTES(temp->buf, old_size);
39587139Smarkm
39681965Smarkm		/* Store a pointer to our template device descriptor */
39729088Smarkm		utd->tdd = tdd;
39887139Smarkm
39987139Smarkm		/* Fill out USB device descriptor */
40029088Smarkm		utd->udd.bLength = sizeof(utd->udd);
40187139Smarkm		utd->udd.bDescriptorType = UDESC_DEVICE;
40287139Smarkm		utd->udd.bDeviceClass = tdd->bDeviceClass;
40387139Smarkm		utd->udd.bDeviceSubClass = tdd->bDeviceSubClass;
40487139Smarkm		utd->udd.bDeviceProtocol = tdd->bDeviceProtocol;
40529088Smarkm		USETW(utd->udd.idVendor, tdd->idVendor);
40681965Smarkm		USETW(utd->udd.idProduct, tdd->idProduct);
40781965Smarkm		USETW(utd->udd.bcdDevice, tdd->bcdDevice);
40829088Smarkm		utd->udd.iManufacturer = tdd->iManufacturer;
40929088Smarkm		utd->udd.iProduct = tdd->iProduct;
41029088Smarkm		utd->udd.iSerialNumber = tdd->iSerialNumber;
41129181Smarkm		utd->udd.bNumConfigurations = temp->bConfigurationValue - 1;
41229088Smarkm
41329088Smarkm		/*
41429088Smarkm		 * Fill out the USB device qualifier. Pretend that we
41529088Smarkm		 * don't support any other speeds by setting
41629088Smarkm		 * "bNumConfigurations" equal to zero. That saves us
41729088Smarkm		 * generating an extra set of configuration
41829088Smarkm		 * descriptors.
41929088Smarkm		 */
42029088Smarkm		utd->udq.bLength = sizeof(utd->udq);
42129088Smarkm		utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER;
42229088Smarkm		utd->udq.bDeviceClass = tdd->bDeviceClass;
42329181Smarkm		utd->udq.bDeviceSubClass = tdd->bDeviceSubClass;
42487139Smarkm		utd->udq.bDeviceProtocol = tdd->bDeviceProtocol;
42529088Smarkm		utd->udq.bNumConfigurations = 0;
42629088Smarkm		USETW(utd->udq.bcdUSB, 0x0200);
42729088Smarkm		utd->udq.bMaxPacketSize0 = 0;
42829088Smarkm
42929088Smarkm		switch (temp->usb2_speed) {
43029088Smarkm		case USB_SPEED_LOW:
43129088Smarkm			USETW(utd->udd.bcdUSB, 0x0110);
43229088Smarkm			utd->udd.bMaxPacketSize = 8;
43329088Smarkm			break;
43429088Smarkm		case USB_SPEED_FULL:
43529088Smarkm			USETW(utd->udd.bcdUSB, 0x0110);
43629088Smarkm			utd->udd.bMaxPacketSize = 32;
43729088Smarkm			break;
43829088Smarkm		case USB_SPEED_HIGH:
43929088Smarkm			USETW(utd->udd.bcdUSB, 0x0200);
44029088Smarkm			utd->udd.bMaxPacketSize = 64;
44129088Smarkm			break;
44229088Smarkm		case USB_SPEED_VARIABLE:
44329088Smarkm			USETW(utd->udd.bcdUSB, 0x0250);
44429088Smarkm			utd->udd.bMaxPacketSize = 255;	/* 512 bytes */
44529088Smarkm			break;
44629088Smarkm		default:
44729088Smarkm			temp->err = USB_ERR_INVAL;
44829088Smarkm			break;
44929088Smarkm		}
45029088Smarkm	}
45129088Smarkm	return;
45229088Smarkm}
45329088Smarkm
45429088Smarkm/*------------------------------------------------------------------------*
45529088Smarkm *	usb2_hw_ep_match
45629088Smarkm *
45729088Smarkm * Return values:
45829088Smarkm *    0: The endpoint profile does not match the criterias
45929088Smarkm * Else: The endpoint profile matches the criterias
46087139Smarkm *------------------------------------------------------------------------*/
46187139Smarkmstatic uint8_t
46229088Smarkmusb2_hw_ep_match(const struct usb2_hw_ep_profile *pf,
46329088Smarkm    uint8_t ep_type, uint8_t ep_dir_in)
46429088Smarkm{
46529088Smarkm	if (ep_type == UE_CONTROL) {
46629088Smarkm		/* special */
46729088Smarkm		return (pf->support_control);
46829088Smarkm	}
46929088Smarkm	if ((pf->support_in && ep_dir_in) ||
47087139Smarkm	    (pf->support_out && !ep_dir_in)) {
47187139Smarkm		if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) ||
47229088Smarkm		    (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) ||
47329088Smarkm		    (pf->support_bulk && (ep_type == UE_BULK))) {
47429088Smarkm			return (1);
47529088Smarkm		}
47629088Smarkm	}
47729088Smarkm	return (0);
47829088Smarkm}
47929088Smarkm
48029088Smarkm/*------------------------------------------------------------------------*
48129088Smarkm *	usb2_hw_ep_find_match
48229088Smarkm *
48329088Smarkm * This function is used to find the best matching endpoint profile
48429088Smarkm * for and endpoint belonging to an USB descriptor.
48529088Smarkm *
48629088Smarkm * Return values:
48729088Smarkm *    0: Success. Got a match.
48887139Smarkm * Else: Failure. No match.
48987139Smarkm *------------------------------------------------------------------------*/
49029088Smarkmstatic uint8_t
49129088Smarkmusb2_hw_ep_find_match(struct usb2_hw_ep_scratch *ues,
49229088Smarkm    struct usb2_hw_ep_scratch_sub *ep, uint8_t is_simplex)
49329088Smarkm{
49429088Smarkm	const struct usb2_hw_ep_profile *pf;
49529088Smarkm	uint16_t distance;
49629088Smarkm	uint16_t temp;
49729088Smarkm	uint16_t max_frame_size;
49887139Smarkm	uint8_t n;
49987139Smarkm	uint8_t best_n;
50029088Smarkm	uint8_t dir_in;
50129088Smarkm	uint8_t dir_out;
50229088Smarkm
50329088Smarkm	distance = 0xFFFF;
50429088Smarkm	best_n = 0;
50529088Smarkm
50629088Smarkm	if ((!ep->needs_in) && (!ep->needs_out)) {
50729088Smarkm		return (0);		/* we are done */
50887139Smarkm	}
50987139Smarkm	if (ep->needs_ep_type == UE_CONTROL) {
51029088Smarkm		dir_in = 1;
51129088Smarkm		dir_out = 1;
51229088Smarkm	} else {
51329088Smarkm		if (ep->needs_in) {
51429088Smarkm			dir_in = 1;
51529088Smarkm			dir_out = 0;
51629088Smarkm		} else {
51729088Smarkm			dir_in = 0;
51829088Smarkm			dir_out = 1;
51929088Smarkm		}
52029088Smarkm	}
52129088Smarkm
52287139Smarkm	for (n = 1; n != (USB_EP_MAX / 2); n++) {
52387139Smarkm
52429088Smarkm		/* get HW endpoint profile */
52529088Smarkm		(ues->methods->get_hw_ep_profile) (ues->udev, &pf, n);
52629088Smarkm		if (pf == NULL) {
52729088Smarkm			/* end of profiles */
52829088Smarkm			break;
52929088Smarkm		}
53029088Smarkm		/* check if IN-endpoint is reserved */
53129088Smarkm		if (dir_in || pf->is_simplex) {
53229088Smarkm			if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) {
53329088Smarkm				/* mismatch */
53429088Smarkm				continue;
53529088Smarkm			}
53629088Smarkm		}
53729088Smarkm		/* check if OUT-endpoint is reserved */
53887139Smarkm		if (dir_out || pf->is_simplex) {
53987139Smarkm			if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) {
54029088Smarkm				/* mismatch */
54129088Smarkm				continue;
54229088Smarkm			}
54329088Smarkm		}
54429088Smarkm		/* check simplex */
54529088Smarkm		if (pf->is_simplex == is_simplex) {
54629088Smarkm			/* mismatch */
54729088Smarkm			continue;
54887139Smarkm		}
54987139Smarkm		/* check if HW endpoint matches */
55087139Smarkm		if (!usb2_hw_ep_match(pf, ep->needs_ep_type, dir_in)) {
55187139Smarkm			/* mismatch */
55229088Smarkm			continue;
55329088Smarkm		}
55429088Smarkm		/* get maximum frame size */
55529088Smarkm		if (dir_in)
55629088Smarkm			max_frame_size = pf->max_in_frame_size;
55729088Smarkm		else
55829088Smarkm			max_frame_size = pf->max_out_frame_size;
55929088Smarkm
56029088Smarkm		/* check if we have a matching profile */
56129088Smarkm		if (max_frame_size >= ep->max_frame_size) {
56229088Smarkm			temp = (max_frame_size - ep->max_frame_size);
56329088Smarkm			if (distance > temp) {
56429088Smarkm				distance = temp;
56529088Smarkm				best_n = n;
56687139Smarkm				ep->pf = pf;
56729088Smarkm			}
56887139Smarkm		}
56987139Smarkm	}
57029088Smarkm
57129088Smarkm	/* see if we got a match */
57229088Smarkm	if (best_n != 0) {
57329088Smarkm		/* get the correct profile */
57429088Smarkm		pf = ep->pf;
57529088Smarkm
57629088Smarkm		/* reserve IN-endpoint */
57729088Smarkm		if (dir_in) {
57829088Smarkm			ues->bmInAlloc[best_n / 8] |=
57929088Smarkm			    (1 << (best_n % 8));
58029088Smarkm			ep->hw_endpoint_in = best_n | UE_DIR_IN;
58129088Smarkm			ep->needs_in = 0;
58229088Smarkm		}
58329088Smarkm		/* reserve OUT-endpoint */
58429088Smarkm		if (dir_out) {
58587139Smarkm			ues->bmOutAlloc[best_n / 8] |=
58687139Smarkm			    (1 << (best_n % 8));
58729088Smarkm			ep->hw_endpoint_out = best_n | UE_DIR_OUT;
58829088Smarkm			ep->needs_out = 0;
58929088Smarkm		}
59029088Smarkm		return (0);		/* got a match */
59129088Smarkm	}
59229088Smarkm	return (1);			/* failure */
59329088Smarkm}
59429088Smarkm
59529088Smarkm/*------------------------------------------------------------------------*
59629088Smarkm *	usb2_hw_ep_get_needs
59729088Smarkm *
59829088Smarkm * This function will figure out the type and number of endpoints
59929088Smarkm * which are needed for an USB configuration.
60029088Smarkm *
60129088Smarkm * Return values:
60229088Smarkm *    0: Success.
60329088Smarkm * Else: Failure.
60429088Smarkm *------------------------------------------------------------------------*/
60529088Smarkmstatic uint8_t
60687139Smarkmusb2_hw_ep_get_needs(struct usb2_hw_ep_scratch *ues,
60787139Smarkm    uint8_t ep_type, uint8_t is_complete)
60829088Smarkm{
60929088Smarkm	const struct usb2_hw_ep_profile *pf;
61029088Smarkm	struct usb2_hw_ep_scratch_sub *ep_iface;
61129088Smarkm	struct usb2_hw_ep_scratch_sub *ep_curr;
61229088Smarkm	struct usb2_hw_ep_scratch_sub *ep_max;
61329088Smarkm	struct usb2_hw_ep_scratch_sub *ep_end;
61429088Smarkm	struct usb2_descriptor *desc;
61529088Smarkm	struct usb2_interface_descriptor *id;
61687139Smarkm	struct usb2_endpoint_descriptor *ed;
61787139Smarkm	uint16_t wMaxPacketSize;
61829088Smarkm	uint16_t temp;
61929088Smarkm	uint8_t speed;
62029088Smarkm	uint8_t ep_no;
62129088Smarkm
62229088Smarkm	ep_iface = ues->ep_max;
62329088Smarkm	ep_curr = ues->ep_max;
62429088Smarkm	ep_end = ues->ep + USB_EP_MAX;
62529088Smarkm	ep_max = ues->ep_max;
62629088Smarkm	desc = NULL;
62787139Smarkm	speed = usb2_get_speed(ues->udev);
62887139Smarkm
62929088Smarkmrepeat:
63029088Smarkm
63129088Smarkm	while ((desc = usb2_desc_foreach(ues->cd, desc))) {
63229088Smarkm
63329088Smarkm		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
63429088Smarkm		    (desc->bLength >= sizeof(*id))) {
63529088Smarkm
63629088Smarkm			id = (void *)desc;
63787139Smarkm
63887139Smarkm			if (id->bAlternateSetting == 0) {
63929088Smarkm				/* going forward */
64029088Smarkm				ep_iface = ep_max;
64129088Smarkm			} else {
64229088Smarkm				/* reset */
64329088Smarkm				ep_curr = ep_iface;
64429088Smarkm			}
64529088Smarkm		}
64629088Smarkm		if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
64787139Smarkm		    (desc->bLength >= sizeof(*ed))) {
64887139Smarkm
64929088Smarkm			ed = (void *)desc;
65029088Smarkm
65129088Smarkm			goto handle_endpoint_desc;
65229088Smarkm		}
65329088Smarkm	}
65429088Smarkm	ues->ep_max = ep_max;
65529088Smarkm	return (0);
65629088Smarkm
65729088Smarkmhandle_endpoint_desc:
65829088Smarkm	temp = (ed->bmAttributes & UE_XFERTYPE);
65929088Smarkm
66029088Smarkm	if (temp == ep_type) {
66129088Smarkm
66229088Smarkm		if (ep_curr == ep_end) {
66387139Smarkm			/* too many endpoints */
66487139Smarkm			return (1);	/* failure */
66529088Smarkm		}
66629088Smarkm		wMaxPacketSize = UGETW(ed->wMaxPacketSize);
66729088Smarkm		if ((wMaxPacketSize & 0xF800) &&
66829088Smarkm		    (speed == USB_SPEED_HIGH)) {
66929088Smarkm			/* handle packet multiplier */
67029088Smarkm			temp = (wMaxPacketSize >> 11) & 3;
67129088Smarkm			wMaxPacketSize &= 0x7FF;
67229088Smarkm			if (temp == 1) {
67329088Smarkm				wMaxPacketSize *= 2;
67429088Smarkm			} else {
67529088Smarkm				wMaxPacketSize *= 3;
67629088Smarkm			}
67729088Smarkm		}
67887139Smarkm		/*
67987139Smarkm		 * Check if we have a fixed endpoint number, else the
68029088Smarkm		 * endpoint number is allocated dynamically:
68129088Smarkm		 */
68229088Smarkm		ep_no = (ed->bEndpointAddress & UE_ADDR);
68329088Smarkm		if (ep_no != 0) {
68429088Smarkm
68529088Smarkm			/* get HW endpoint profile */
68629088Smarkm			(ues->methods->get_hw_ep_profile)
68729088Smarkm			    (ues->udev, &pf, ep_no);
68829088Smarkm			if (pf == NULL) {
68929088Smarkm				/* HW profile does not exist - failure */
69029088Smarkm				DPRINTFN(0, "Endpoint profile %u "
69129088Smarkm				    "does not exist\n", ep_no);
69229088Smarkm				return (1);
69387139Smarkm			}
69487139Smarkm			/* reserve fixed endpoint number */
69529088Smarkm			if (ep_type == UE_CONTROL) {
69629088Smarkm				ues->bmInAlloc[ep_no / 8] |=
69729088Smarkm				    (1 << (ep_no % 8));
69829088Smarkm				ues->bmOutAlloc[ep_no / 8] |=
69929088Smarkm				    (1 << (ep_no % 8));
70029088Smarkm				if ((pf->max_in_frame_size < wMaxPacketSize) ||
70129088Smarkm				    (pf->max_out_frame_size < wMaxPacketSize)) {
70229088Smarkm					DPRINTFN(0, "Endpoint profile %u "
70329088Smarkm					    "has too small buffer!\n", ep_no);
70429088Smarkm					return (1);
70529088Smarkm				}
70629088Smarkm			} else if (ed->bEndpointAddress & UE_DIR_IN) {
70729088Smarkm				ues->bmInAlloc[ep_no / 8] |=
70829088Smarkm				    (1 << (ep_no % 8));
70929088Smarkm				if (pf->max_in_frame_size < wMaxPacketSize) {
71029088Smarkm					DPRINTFN(0, "Endpoint profile %u "
71129088Smarkm					    "has too small buffer!\n", ep_no);
71229088Smarkm					return (1);
71329088Smarkm				}
71429088Smarkm			} else {
71529088Smarkm				ues->bmOutAlloc[ep_no / 8] |=
71629088Smarkm				    (1 << (ep_no % 8));
71729088Smarkm				if (pf->max_out_frame_size < wMaxPacketSize) {
71829088Smarkm					DPRINTFN(0, "Endpoint profile %u "
71929088Smarkm					    "has too small buffer!\n", ep_no);
72029088Smarkm					return (1);
72129088Smarkm				}
72287139Smarkm			}
72387139Smarkm		} else if (is_complete) {
72429088Smarkm
72529088Smarkm			/* check if we have enough buffer space */
72629088Smarkm			if (wMaxPacketSize >
72729088Smarkm			    ep_curr->max_frame_size) {
72829088Smarkm				return (1);	/* failure */
72929088Smarkm			}
73029088Smarkm			if (ed->bEndpointAddress & UE_DIR_IN) {
73129088Smarkm				ed->bEndpointAddress =
73229088Smarkm				    ep_curr->hw_endpoint_in;
73329088Smarkm			} else {
73429088Smarkm				ed->bEndpointAddress =
73529088Smarkm				    ep_curr->hw_endpoint_out;
73629088Smarkm			}
73729088Smarkm
73829088Smarkm		} else {
73929088Smarkm
74087139Smarkm			/* compute the maximum frame size */
74187139Smarkm			if (ep_curr->max_frame_size < wMaxPacketSize) {
74229088Smarkm				ep_curr->max_frame_size = wMaxPacketSize;
74329088Smarkm			}
74429088Smarkm			if (temp == UE_CONTROL) {
74529088Smarkm				ep_curr->needs_in = 1;
74629088Smarkm				ep_curr->needs_out = 1;
74729088Smarkm			} else {
74829088Smarkm				if (ed->bEndpointAddress & UE_DIR_IN) {
74929088Smarkm					ep_curr->needs_in = 1;
75029088Smarkm				} else {
75129088Smarkm					ep_curr->needs_out = 1;
75229088Smarkm				}
75329088Smarkm			}
75429088Smarkm			ep_curr->needs_ep_type = ep_type;
75529088Smarkm		}
75629088Smarkm
75729088Smarkm		ep_curr++;
75829088Smarkm		if (ep_max < ep_curr) {
75929088Smarkm			ep_max = ep_curr;
76029088Smarkm		}
76129088Smarkm	}
76229088Smarkm	goto repeat;
76329088Smarkm}
76487139Smarkm
76587139Smarkm/*------------------------------------------------------------------------*
76629088Smarkm *	usb2_hw_ep_resolve
76729088Smarkm *
76829088Smarkm * This function will try to resolve endpoint requirements by the
76929088Smarkm * given endpoint profiles that the USB hardware reports.
77029088Smarkm *
77129088Smarkm * Return values:
77229088Smarkm *    0: Success
77329088Smarkm * Else: Failure
77429088Smarkm *------------------------------------------------------------------------*/
77529088Smarkmstatic usb2_error_t
77629088Smarkmusb2_hw_ep_resolve(struct usb2_device *udev,
77729088Smarkm    struct usb2_descriptor *desc)
77829088Smarkm{
77929088Smarkm	struct usb2_hw_ep_scratch *ues;
78029088Smarkm	struct usb2_hw_ep_scratch_sub *ep;
78129088Smarkm	const struct usb2_hw_ep_profile *pf;
78229088Smarkm	struct usb2_bus_methods *methods;
78329088Smarkm	struct usb2_device_descriptor *dd;
78429088Smarkm	uint16_t mps;
78529088Smarkm
78629088Smarkm	if (desc == NULL) {
78729088Smarkm		return (USB_ERR_INVAL);
78829088Smarkm	}
78929088Smarkm	/* get bus methods */
79029088Smarkm	methods = udev->bus->methods;
79129088Smarkm
79229088Smarkm	if (methods->get_hw_ep_profile == NULL) {
79329088Smarkm		return (USB_ERR_INVAL);
79429088Smarkm	}
79529088Smarkm	if (desc->bDescriptorType == UDESC_DEVICE) {
79629088Smarkm
79729088Smarkm		if (desc->bLength < sizeof(*dd)) {
79829088Smarkm			return (USB_ERR_INVAL);
79929088Smarkm		}
80029088Smarkm		dd = (void *)desc;
80129088Smarkm
80229088Smarkm		/* get HW control endpoint 0 profile */
80329088Smarkm		(methods->get_hw_ep_profile) (udev, &pf, 0);
80429088Smarkm		if (pf == NULL) {
80529088Smarkm			return (USB_ERR_INVAL);
80629088Smarkm		}
80729088Smarkm		if (!usb2_hw_ep_match(pf, UE_CONTROL, 0)) {
80829088Smarkm			DPRINTFN(0, "Endpoint 0 does not "
80929088Smarkm			    "support control\n");
81029088Smarkm			return (USB_ERR_INVAL);
81129088Smarkm		}
81229088Smarkm		mps = dd->bMaxPacketSize;
81329088Smarkm
81429088Smarkm		if (udev->speed == USB_SPEED_FULL) {
81529088Smarkm			/*
81629088Smarkm			 * We can optionally choose another packet size !
81729088Smarkm			 */
81829088Smarkm			while (1) {
81929088Smarkm				/* check if "mps" is ok */
82029088Smarkm				if (pf->max_in_frame_size >= mps) {
82129088Smarkm					break;
82231622Scharnier				}
82329088Smarkm				/* reduce maximum packet size */
82487139Smarkm				mps /= 2;
82587139Smarkm
82629088Smarkm				/* check if "mps" is too small */
82729088Smarkm				if (mps < 8) {
82887139Smarkm					return (USB_ERR_INVAL);
82929088Smarkm				}
83029088Smarkm			}
83129088Smarkm
83229088Smarkm			dd->bMaxPacketSize = mps;
83329088Smarkm
83429088Smarkm		} else {
83531622Scharnier			/* We only have one choice */
83629088Smarkm			if (mps == 255) {
83731622Scharnier				mps = 512;
83829088Smarkm			}
83929088Smarkm			/* Check if we support the specified wMaxPacketSize */
84087139Smarkm			if (pf->max_in_frame_size < mps) {
84187139Smarkm				return (USB_ERR_INVAL);
84229088Smarkm			}
84329088Smarkm		}
84487139Smarkm		return (0);		/* success */
84529088Smarkm	}
84629088Smarkm	if (desc->bDescriptorType != UDESC_CONFIG) {
84729088Smarkm		return (USB_ERR_INVAL);
84829088Smarkm	}
84929088Smarkm	if (desc->bLength < sizeof(*(ues->cd))) {
85029088Smarkm		return (USB_ERR_INVAL);
85129088Smarkm	}
85229088Smarkm	ues = udev->bus->scratch[0].hw_ep_scratch;
85329088Smarkm
85429088Smarkm	bzero(ues, sizeof(*ues));
85529088Smarkm
85629088Smarkm	ues->ep_max = ues->ep;
85729088Smarkm	ues->cd = (void *)desc;
85829088Smarkm	ues->methods = methods;
85929088Smarkm	ues->udev = udev;
86031622Scharnier
86129088Smarkm	/* Get all the endpoints we need */
86287139Smarkm
86387139Smarkm	if (usb2_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) ||
86429088Smarkm	    usb2_hw_ep_get_needs(ues, UE_INTERRUPT, 0) ||
86587139Smarkm	    usb2_hw_ep_get_needs(ues, UE_CONTROL, 0) ||
86629181Smarkm	    usb2_hw_ep_get_needs(ues, UE_BULK, 0)) {
86729088Smarkm		DPRINTFN(0, "Could not get needs\n");
86829088Smarkm		return (USB_ERR_INVAL);
86929088Smarkm	}
87029088Smarkm	for (ep = ues->ep; ep != ues->ep_max; ep++) {
87129088Smarkm
87229088Smarkm		while (ep->needs_in || ep->needs_out) {
87329088Smarkm
87429088Smarkm			/*
87529088Smarkm		         * First try to use a simplex endpoint.
87629088Smarkm		         * Then try to use a duplex endpoint.
87729088Smarkm		         */
87829088Smarkm			if (usb2_hw_ep_find_match(ues, ep, 1) &&
87929088Smarkm			    usb2_hw_ep_find_match(ues, ep, 0)) {
88029088Smarkm				DPRINTFN(0, "Could not find match\n");
88129088Smarkm				return (USB_ERR_INVAL);
88229181Smarkm			}
88329088Smarkm		}
88429088Smarkm	}
88529088Smarkm
88629088Smarkm	ues->ep_max = ues->ep;
88729088Smarkm
88829181Smarkm	/* Update all endpoint addresses */
88929088Smarkm
89029088Smarkm	if (usb2_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) ||
89129088Smarkm	    usb2_hw_ep_get_needs(ues, UE_INTERRUPT, 1) ||
89229088Smarkm	    usb2_hw_ep_get_needs(ues, UE_CONTROL, 1) ||
89329088Smarkm	    usb2_hw_ep_get_needs(ues, UE_BULK, 1)) {
89429088Smarkm		DPRINTFN(0, "Could not update endpoint address\n");
89529088Smarkm		return (USB_ERR_INVAL);
89629088Smarkm	}
89729088Smarkm	return (0);			/* success */
89829088Smarkm}
89929088Smarkm
90029088Smarkm/*------------------------------------------------------------------------*
90129088Smarkm *	usb2_temp_get_tdd
90229088Smarkm *
90329088Smarkm * Returns:
90429088Smarkm *  NULL: No USB template device descriptor found.
90529088Smarkm *  Else: Pointer to the USB template device descriptor.
90629088Smarkm *------------------------------------------------------------------------*/
90729088Smarkmstatic const struct usb2_temp_device_desc *
90829088Smarkmusb2_temp_get_tdd(struct usb2_device *udev)
90929088Smarkm{
91029088Smarkm	if (udev->usb2_template_ptr == NULL) {
91129088Smarkm		return (NULL);
91229088Smarkm	}
91329088Smarkm	return (udev->usb2_template_ptr->tdd);
91429088Smarkm}
91529088Smarkm
91629088Smarkm/*------------------------------------------------------------------------*
91729088Smarkm *	usb2_temp_get_device_desc
91829088Smarkm *
91929088Smarkm * Returns:
92029088Smarkm *  NULL: No USB device descriptor found.
92129088Smarkm *  Else: Pointer to USB device descriptor.
92229088Smarkm *------------------------------------------------------------------------*/
92329088Smarkmstatic void *
92429088Smarkmusb2_temp_get_device_desc(struct usb2_device *udev)
92529088Smarkm{
92629088Smarkm	struct usb2_device_descriptor *dd;
92729088Smarkm
92829088Smarkm	if (udev->usb2_template_ptr == NULL) {
92929088Smarkm		return (NULL);
93029088Smarkm	}
93129088Smarkm	dd = &udev->usb2_template_ptr->udd;
93229088Smarkm	if (dd->bDescriptorType != UDESC_DEVICE) {
93329088Smarkm		/* sanity check failed */
93429181Smarkm		return (NULL);
93529181Smarkm	}
93629088Smarkm	return (dd);
93729088Smarkm}
93829088Smarkm
93929088Smarkm/*------------------------------------------------------------------------*
94029088Smarkm *	usb2_temp_get_qualifier_desc
94129088Smarkm *
94229088Smarkm * Returns:
94329088Smarkm *  NULL: No USB device_qualifier descriptor found.
94429088Smarkm *  Else: Pointer to USB device_qualifier descriptor.
94529088Smarkm *------------------------------------------------------------------------*/
94629088Smarkmstatic void *
94729088Smarkmusb2_temp_get_qualifier_desc(struct usb2_device *udev)
94829088Smarkm{
94987139Smarkm	struct usb2_device_qualifier *dq;
95087139Smarkm
95129088Smarkm	if (udev->usb2_template_ptr == NULL) {
95229088Smarkm		return (NULL);
95329088Smarkm	}
95429088Smarkm	dq = &udev->usb2_template_ptr->udq;
95529088Smarkm	if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) {
95629088Smarkm		/* sanity check failed */
95729088Smarkm		return (NULL);
95829088Smarkm	}
95929088Smarkm	return (dq);
96029088Smarkm}
96187139Smarkm
96229088Smarkm/*------------------------------------------------------------------------*
96329088Smarkm *	usb2_temp_get_config_desc
96429088Smarkm *
96529088Smarkm * Returns:
96629088Smarkm *  NULL: No USB config descriptor found.
96729088Smarkm *  Else: Pointer to USB config descriptor having index "index".
96829088Smarkm *------------------------------------------------------------------------*/
96929088Smarkmstatic void *
97029088Smarkmusb2_temp_get_config_desc(struct usb2_device *udev,
97187139Smarkm    uint16_t *pLength, uint8_t index)
97287139Smarkm{
97329088Smarkm	struct usb2_device_descriptor *dd;
97487139Smarkm	struct usb2_config_descriptor *cd;
97529088Smarkm	uint16_t temp;
97629088Smarkm
97729088Smarkm	if (udev->usb2_template_ptr == NULL) {
97829088Smarkm		return (NULL);
97929088Smarkm	}
98087139Smarkm	dd = &udev->usb2_template_ptr->udd;
98187139Smarkm	cd = (void *)(udev->usb2_template_ptr + 1);
98229088Smarkm
98387139Smarkm	if (index >= dd->bNumConfigurations) {
98429088Smarkm		/* out of range */
98529088Smarkm		return (NULL);
98629088Smarkm	}
98729088Smarkm	while (index--) {
98829088Smarkm		if (cd->bDescriptorType != UDESC_CONFIG) {
98929088Smarkm			/* sanity check failed */
99029088Smarkm			return (NULL);
99129088Smarkm		}
99229088Smarkm		temp = UGETW(cd->wTotalLength);
99329088Smarkm		cd = USB_ADD_BYTES(cd, temp);
99429088Smarkm	}
99529088Smarkm
99629088Smarkm	if (pLength) {
99729088Smarkm		*pLength = UGETW(cd->wTotalLength);
99829088Smarkm	}
99929088Smarkm	return (cd);
100029088Smarkm}
100187139Smarkm
100287139Smarkm/*------------------------------------------------------------------------*
100329088Smarkm *	usb2_temp_get_vendor_desc
100487139Smarkm *
100529088Smarkm * Returns:
100687139Smarkm *  NULL: No vendor descriptor found.
100729088Smarkm *  Else: Pointer to a vendor descriptor.
100829088Smarkm *------------------------------------------------------------------------*/
100929088Smarkmstatic const void *
101029088Smarkmusb2_temp_get_vendor_desc(struct usb2_device *udev,
101129088Smarkm    const struct usb2_device_request *req)
101229088Smarkm{
101329088Smarkm	const struct usb2_temp_device_desc *tdd;
101429088Smarkm
101529088Smarkm	tdd = usb2_temp_get_tdd(udev);
101629088Smarkm	if (tdd == NULL) {
101729088Smarkm		return (NULL);
101829088Smarkm	}
101929088Smarkm	if (tdd->getVendorDesc == NULL) {
102029088Smarkm		return (NULL);
102187139Smarkm	}
102229088Smarkm	return ((tdd->getVendorDesc) (req));
102329088Smarkm}
102429088Smarkm
102529088Smarkm/*------------------------------------------------------------------------*
102629088Smarkm *	usb2_temp_get_string_desc
102787139Smarkm *
102887139Smarkm * Returns:
102929088Smarkm *  NULL: No string descriptor found.
103029088Smarkm *  Else: Pointer to a string descriptor.
103129088Smarkm *------------------------------------------------------------------------*/
103229088Smarkmstatic const void *
103329181Smarkmusb2_temp_get_string_desc(struct usb2_device *udev,
103429088Smarkm    uint16_t lang_id, uint8_t string_index)
103529088Smarkm{
103629088Smarkm	const struct usb2_temp_device_desc *tdd;
103729088Smarkm
103829088Smarkm	tdd = usb2_temp_get_tdd(udev);
103929088Smarkm	if (tdd == NULL) {
104029088Smarkm		return (NULL);
104129088Smarkm	}
104229088Smarkm	if (tdd->getStringDesc == NULL) {
104329088Smarkm		return (NULL);
104429088Smarkm	}
104529088Smarkm	return ((tdd->getStringDesc) (lang_id, string_index));
104629088Smarkm}
104787139Smarkm
104887139Smarkm/*------------------------------------------------------------------------*
104987139Smarkm *	usb2_temp_get_hub_desc
105087139Smarkm *
105129088Smarkm * Returns:
105229088Smarkm *  NULL: No USB HUB descriptor found.
105387139Smarkm *  Else: Pointer to a USB HUB descriptor.
105487139Smarkm *------------------------------------------------------------------------*/
105587139Smarkmstatic const void *
105687139Smarkmusb2_temp_get_hub_desc(struct usb2_device *udev)
105729088Smarkm{
105829088Smarkm	return (NULL);			/* needs to be implemented */
105929088Smarkm}
106029088Smarkm
106129088Smarkm/*------------------------------------------------------------------------*
106229088Smarkm *	usb2_temp_get_desc
106329088Smarkm *
106429088Smarkm * This function is a demultiplexer for local USB device side control
106529088Smarkm * endpoint requests.
106629088Smarkm *------------------------------------------------------------------------*/
106729088Smarkmstatic void
106829088Smarkmusb2_temp_get_desc(struct usb2_device *udev, struct usb2_device_request *req,
106929088Smarkm    const void **pPtr, uint16_t *pLength)
107029088Smarkm{
107187139Smarkm	const uint8_t *buf;
107287139Smarkm	uint16_t len;
107329088Smarkm
107429088Smarkm	buf = NULL;
107529088Smarkm	len = 0;
107629088Smarkm
107729088Smarkm	switch (req->bmRequestType) {
107829088Smarkm	case UT_READ_DEVICE:
107929088Smarkm		switch (req->bRequest) {
108029088Smarkm		case UR_GET_DESCRIPTOR:
108129088Smarkm			goto tr_handle_get_descriptor;
108229088Smarkm		default:
108387139Smarkm			goto tr_stalled;
108429088Smarkm		}
108529088Smarkm		break;
108629088Smarkm	case UT_READ_CLASS_DEVICE:
108729088Smarkm		switch (req->bRequest) {
108829088Smarkm		case UR_GET_DESCRIPTOR:
108929088Smarkm			goto tr_handle_get_class_descriptor;
109029088Smarkm		default:
109129088Smarkm			goto tr_stalled;
109229088Smarkm		}
109329088Smarkm		break;
109429088Smarkm	case UT_READ_VENDOR_DEVICE:
109529088Smarkm	case UT_READ_VENDOR_OTHER:
109629088Smarkm		buf = usb2_temp_get_vendor_desc(udev, req);
109729088Smarkm		goto tr_valid;
109829088Smarkm	default:
109929088Smarkm		goto tr_stalled;
110029088Smarkm	}
110129088Smarkm
110229088Smarkmtr_handle_get_descriptor:
110329088Smarkm	switch (req->wValue[1]) {
110429088Smarkm	case UDESC_DEVICE:
110529088Smarkm		if (req->wValue[0]) {
110629088Smarkm			goto tr_stalled;
110729088Smarkm		}
110829088Smarkm		buf = usb2_temp_get_device_desc(udev);
110929088Smarkm		goto tr_valid;
111029088Smarkm	case UDESC_DEVICE_QUALIFIER:
111187139Smarkm		if (udev->speed != USB_SPEED_HIGH) {
111229088Smarkm			goto tr_stalled;
111329088Smarkm		}
111429088Smarkm		if (req->wValue[0]) {
111529181Smarkm			goto tr_stalled;
111629088Smarkm		}
111729088Smarkm		buf = usb2_temp_get_qualifier_desc(udev);
111829088Smarkm		goto tr_valid;
111929088Smarkm	case UDESC_OTHER_SPEED_CONFIGURATION:
112029088Smarkm		if (udev->speed != USB_SPEED_HIGH) {
112129088Smarkm			goto tr_stalled;
112229088Smarkm		}
112329088Smarkm	case UDESC_CONFIG:
112429088Smarkm		buf = usb2_temp_get_config_desc(udev,
112529088Smarkm		    &len, req->wValue[0]);
112629088Smarkm		goto tr_valid;
112729088Smarkm	case UDESC_STRING:
112887139Smarkm		buf = usb2_temp_get_string_desc(udev,
112929088Smarkm		    UGETW(req->wIndex), req->wValue[0]);
113029088Smarkm		goto tr_valid;
113129088Smarkm	default:
113229088Smarkm		goto tr_stalled;
113329088Smarkm	}
113429088Smarkm	goto tr_stalled;
113529088Smarkm
113629088Smarkmtr_handle_get_class_descriptor:
113729088Smarkm	if (req->wValue[0]) {
113829088Smarkm		goto tr_stalled;
113929088Smarkm	}
114029088Smarkm	buf = usb2_temp_get_hub_desc(udev);
114129088Smarkm	goto tr_valid;
114229088Smarkm
114329088Smarkmtr_valid:
114429088Smarkm	if (buf == NULL) {
114529088Smarkm		goto tr_stalled;
114629088Smarkm	}
114729088Smarkm	if (len == 0) {
114829088Smarkm		len = buf[0];
114929088Smarkm	}
115029088Smarkm	*pPtr = buf;
115129088Smarkm	*pLength = len;
115229088Smarkm	return;
115329088Smarkm
115429088Smarkmtr_stalled:
115529088Smarkm	*pPtr = NULL;
115629088Smarkm	*pLength = 0;
115729088Smarkm	return;
115829088Smarkm}
115929088Smarkm
116029088Smarkm/*------------------------------------------------------------------------*
116129088Smarkm *	usb2_temp_setup
116232688Simp *
116332688Simp * This function generates USB descriptors according to the given USB
116429088Smarkm * template device descriptor. It will also try to figure out the best
116529088Smarkm * matching endpoint addresses using the hardware endpoint profiles.
116629088Smarkm *
116729088Smarkm * Returns:
116829088Smarkm *    0: Success
116929088Smarkm * Else: Failure
117029088Smarkm *------------------------------------------------------------------------*/
117129088Smarkmstatic usb2_error_t
117229088Smarkmusb2_temp_setup(struct usb2_device *udev,
117329088Smarkm    const struct usb2_temp_device_desc *tdd)
117429088Smarkm{
117529088Smarkm	struct usb2_temp_setup *uts;
117629088Smarkm	void *buf;
117729088Smarkm	uint8_t n;
117829088Smarkm
117929088Smarkm	if (tdd == NULL) {
118029088Smarkm		/* be NULL safe */
118129088Smarkm		return (0);
118229088Smarkm	}
118329088Smarkm	uts = udev->bus->scratch[0].temp_setup;
118429088Smarkm
118529181Smarkm	bzero(uts, sizeof(*uts));
118629088Smarkm
118729088Smarkm	uts->usb2_speed = udev->speed;
118829088Smarkm	uts->self_powered = udev->flags.self_powered;
118929088Smarkm
119029088Smarkm	/* first pass */
119129088Smarkm
119229181Smarkm	usb2_make_device_desc(uts, tdd);
119329088Smarkm
119429088Smarkm	if (uts->err) {
119529088Smarkm		/* some error happened */
119687139Smarkm		return (uts->err);
119729088Smarkm	}
119829088Smarkm	/* sanity check */
119929088Smarkm	if (uts->size == 0) {
120029088Smarkm		return (USB_ERR_INVAL);
120129088Smarkm	}
120229088Smarkm	/* allocate zeroed memory */
120329088Smarkm	uts->buf = malloc(uts->size, M_USB, M_WAITOK | M_ZERO);
120429088Smarkm	if (uts->buf == NULL) {
120529088Smarkm		/* could not allocate memory */
120629088Smarkm		return (USB_ERR_NOMEM);
120729088Smarkm	}
120829088Smarkm	/* second pass */
120929088Smarkm
121029088Smarkm	uts->size = 0;
121129088Smarkm
121229088Smarkm	usb2_make_device_desc(uts, tdd);
121387139Smarkm
121487139Smarkm	/*
121529088Smarkm	 * Store a pointer to our descriptors:
121629088Smarkm	 */
121729088Smarkm	udev->usb2_template_ptr = uts->buf;
121887139Smarkm
121929088Smarkm	if (uts->err) {
122029088Smarkm		/* some error happened during second pass */
122129181Smarkm		goto error;
122229181Smarkm	}
122329181Smarkm	/*
122429181Smarkm	 * Resolve all endpoint addresses !
122529181Smarkm	 */
122631622Scharnier	buf = usb2_temp_get_device_desc(udev);
122729181Smarkm	uts->err = usb2_hw_ep_resolve(udev, buf);
122829088Smarkm	if (uts->err) {
122929088Smarkm		DPRINTFN(0, "Could not resolve endpoints for "
123029088Smarkm		    "Device Descriptor, error = %s\n",
123187139Smarkm		    usb2_errstr(uts->err));
123287139Smarkm		goto error;
123329088Smarkm	}
123487139Smarkm	for (n = 0;; n++) {
123529088Smarkm
123629088Smarkm		buf = usb2_temp_get_config_desc(udev, NULL, n);
123729088Smarkm		if (buf == NULL) {
123829088Smarkm			break;
123929088Smarkm		}
124029088Smarkm		uts->err = usb2_hw_ep_resolve(udev, buf);
124129088Smarkm		if (uts->err) {
124229088Smarkm			DPRINTFN(0, "Could not resolve endpoints for "
124329088Smarkm			    "Config Descriptor %u, error = %s\n", n,
124429088Smarkm			    usb2_errstr(uts->err));
124529088Smarkm			goto error;
124629088Smarkm		}
124729088Smarkm	}
124887267Smarkm	return (uts->err);
124929088Smarkm
125087267Smarkmerror:
125187267Smarkm	usb2_temp_unsetup(udev);
125229088Smarkm	return (uts->err);
125329088Smarkm}
125429088Smarkm
125587267Smarkm/*------------------------------------------------------------------------*
125629088Smarkm *	usb2_temp_unsetup
125787139Smarkm *
125829088Smarkm * This function frees any memory associated with the currently
125929088Smarkm * setup template, if any.
126029088Smarkm *------------------------------------------------------------------------*/
126129088Smarkmstatic void
126229088Smarkmusb2_temp_unsetup(struct usb2_device *udev)
126329088Smarkm{
126429088Smarkm	if (udev->usb2_template_ptr) {
126569825Sassar
126629088Smarkm		free(udev->usb2_template_ptr, M_USB);
126787139Smarkm
126887139Smarkm		udev->usb2_template_ptr = NULL;
126929088Smarkm	}
127087139Smarkm	return;
127169825Sassar}
127269825Sassar
127369825Sassarstatic usb2_error_t
127429088Smarkmusb2_temp_setup_by_index(struct usb2_device *udev, uint16_t index)
127587139Smarkm{
127669825Sassar	usb2_error_t err;
127769825Sassar
127869825Sassar	switch (index) {
127969825Sassar	case 0:
128069825Sassar		err = usb2_temp_setup(udev, &usb2_template_msc);
128169825Sassar		break;
128269825Sassar	case 1:
128369825Sassar		err = usb2_temp_setup(udev, &usb2_template_cdce);
128469825Sassar		break;
128569825Sassar	case 2:
128669825Sassar		err = usb2_temp_setup(udev, &usb2_template_mtp);
128769825Sassar		break;
128869825Sassar	default:
128969825Sassar		return (USB_ERR_INVAL);
129069825Sassar	}
129169825Sassar
129287139Smarkm	return (err);
129369825Sassar}
129469825Sassar
129569825Sassarstatic void
129669825Sassarusb2_temp_init(void *arg)
129769825Sassar{
129869825Sassar	/* register our functions */
129969825Sassar	usb2_temp_get_desc_p = &usb2_temp_get_desc;
130087139Smarkm	usb2_temp_setup_by_index_p = &usb2_temp_setup_by_index;
130169825Sassar	usb2_temp_unsetup_p = &usb2_temp_unsetup;
130269825Sassar	return;
130369825Sassar}
130469825Sassar
130569825SassarSYSINIT(usb2_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb2_temp_init, NULL);
130669825SassarSYSUNINIT(usb2_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb2_temp_unload, NULL);
130729088Smarkm