usb_template.c revision 229080
1184610Salfred/* $FreeBSD: stable/9/sys/dev/usb/template/usb_template.c 229080 2011-12-31 13:07:09Z hselasky $ */
2184610Salfred/*-
3184610Salfred * Copyright (c) 2007 Hans Petter Selasky. All rights reserved.
4184610Salfred *
5184610Salfred * Redistribution and use in source and binary forms, with or without
6184610Salfred * modification, are permitted provided that the following conditions
7184610Salfred * are met:
8184610Salfred * 1. Redistributions of source code must retain the above copyright
9184610Salfred *    notice, this list of conditions and the following disclaimer.
10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer in the
12184610Salfred *    documentation and/or other materials provided with the distribution.
13184610Salfred *
14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24184610Salfred * SUCH DAMAGE.
25184610Salfred */
26184610Salfred
27184610Salfred/*
28184610Salfred * This file contains sub-routines to build up USB descriptors from
29184610Salfred * USB templates.
30184610Salfred */
31184610Salfred
32194677Sthompsa#include <sys/stdint.h>
33194677Sthompsa#include <sys/stddef.h>
34194677Sthompsa#include <sys/param.h>
35194677Sthompsa#include <sys/queue.h>
36194677Sthompsa#include <sys/types.h>
37194677Sthompsa#include <sys/systm.h>
38194677Sthompsa#include <sys/kernel.h>
39194677Sthompsa#include <sys/bus.h>
40194677Sthompsa#include <sys/module.h>
41194677Sthompsa#include <sys/lock.h>
42194677Sthompsa#include <sys/mutex.h>
43194677Sthompsa#include <sys/condvar.h>
44194677Sthompsa#include <sys/sysctl.h>
45194677Sthompsa#include <sys/sx.h>
46194677Sthompsa#include <sys/unistd.h>
47194677Sthompsa#include <sys/callout.h>
48194677Sthompsa#include <sys/malloc.h>
49194677Sthompsa#include <sys/priv.h>
50194677Sthompsa
51188942Sthompsa#include <dev/usb/usb.h>
52223467Shselasky#include <dev/usb/usb_ioctl.h>
53194677Sthompsa#include <dev/usb/usbdi.h>
54194677Sthompsa#include <dev/usb/usbdi_util.h>
55194677Sthompsa#include "usbdevs.h"
56194677Sthompsa
57188942Sthompsa#include <dev/usb/usb_cdc.h>
58188942Sthompsa#include <dev/usb/usb_core.h>
59194677Sthompsa#include <dev/usb/usb_dynamic.h>
60188942Sthompsa#include <dev/usb/usb_busdma.h>
61188942Sthompsa#include <dev/usb/usb_process.h>
62188942Sthompsa#include <dev/usb/usb_device.h>
63184610Salfred
64194677Sthompsa#define	USB_DEBUG_VAR usb_debug
65194677Sthompsa#include <dev/usb/usb_debug.h>
66194677Sthompsa
67188942Sthompsa#include <dev/usb/usb_controller.h>
68188942Sthompsa#include <dev/usb/usb_bus.h>
69188942Sthompsa#include <dev/usb/template/usb_template.h>
70184610Salfred
71188942SthompsaMODULE_DEPEND(usb_template, usb, 1, 1, 1);
72188942SthompsaMODULE_VERSION(usb_template, 1);
73184610Salfred
74184610Salfred/* function prototypes */
75184610Salfred
76194228Sthompsastatic void	usb_make_raw_desc(struct usb_temp_setup *, const uint8_t *);
77194228Sthompsastatic void	usb_make_endpoint_desc(struct usb_temp_setup *,
78192984Sthompsa		    const struct usb_temp_endpoint_desc *);
79194228Sthompsastatic void	usb_make_interface_desc(struct usb_temp_setup *,
80192984Sthompsa		    const struct usb_temp_interface_desc *);
81194228Sthompsastatic void	usb_make_config_desc(struct usb_temp_setup *,
82192984Sthompsa		    const struct usb_temp_config_desc *);
83194228Sthompsastatic void	usb_make_device_desc(struct usb_temp_setup *,
84192984Sthompsa		    const struct usb_temp_device_desc *);
85194228Sthompsastatic uint8_t	usb_hw_ep_match(const struct usb_hw_ep_profile *, uint8_t,
86185948Sthompsa		    uint8_t);
87194228Sthompsastatic uint8_t	usb_hw_ep_find_match(struct usb_hw_ep_scratch *,
88192984Sthompsa		    struct usb_hw_ep_scratch_sub *, uint8_t);
89194228Sthompsastatic uint8_t	usb_hw_ep_get_needs(struct usb_hw_ep_scratch *, uint8_t,
90185948Sthompsa		    uint8_t);
91194228Sthompsastatic usb_error_t usb_hw_ep_resolve(struct usb_device *,
92192984Sthompsa		    struct usb_descriptor *);
93194228Sthompsastatic const struct usb_temp_device_desc *usb_temp_get_tdd(struct usb_device *);
94194228Sthompsastatic void	*usb_temp_get_device_desc(struct usb_device *);
95194228Sthompsastatic void	*usb_temp_get_qualifier_desc(struct usb_device *);
96194228Sthompsastatic void	*usb_temp_get_config_desc(struct usb_device *, uint16_t *,
97185948Sthompsa		    uint8_t);
98194228Sthompsastatic const void *usb_temp_get_string_desc(struct usb_device *, uint16_t,
99185948Sthompsa		    uint8_t);
100194228Sthompsastatic const void *usb_temp_get_vendor_desc(struct usb_device *,
101205030Sthompsa		    const struct usb_device_request *, uint16_t *plen);
102194228Sthompsastatic const void *usb_temp_get_hub_desc(struct usb_device *);
103194228Sthompsastatic usb_error_t usb_temp_get_desc(struct usb_device *,
104192984Sthompsa		    struct usb_device_request *, const void **, uint16_t *);
105194228Sthompsastatic usb_error_t usb_temp_setup_by_index(struct usb_device *,
106185948Sthompsa		    uint16_t index);
107194228Sthompsastatic void	usb_temp_init(void *);
108184610Salfred
109184610Salfred/*------------------------------------------------------------------------*
110194228Sthompsa *	usb_make_raw_desc
111184610Salfred *
112184610Salfred * This function will insert a raw USB descriptor into the generated
113184610Salfred * USB configuration.
114184610Salfred *------------------------------------------------------------------------*/
115184610Salfredstatic void
116194228Sthompsausb_make_raw_desc(struct usb_temp_setup *temp,
117184610Salfred    const uint8_t *raw)
118184610Salfred{
119184610Salfred	void *dst;
120184610Salfred	uint8_t len;
121184610Salfred
122184610Salfred	/*
123184610Salfred         * The first byte of any USB descriptor gives the length.
124184610Salfred         */
125184610Salfred	if (raw) {
126184610Salfred		len = raw[0];
127184610Salfred		if (temp->buf) {
128184610Salfred			dst = USB_ADD_BYTES(temp->buf, temp->size);
129218475Shselasky			memcpy(dst, raw, len);
130184610Salfred
131184610Salfred			/* check if we have got a CDC union descriptor */
132184610Salfred
133192984Sthompsa			if ((raw[0] >= sizeof(struct usb_cdc_union_descriptor)) &&
134184610Salfred			    (raw[1] == UDESC_CS_INTERFACE) &&
135184610Salfred			    (raw[2] == UDESCSUB_CDC_UNION)) {
136192984Sthompsa				struct usb_cdc_union_descriptor *ud = (void *)dst;
137184610Salfred
138184610Salfred				/* update the interface numbers */
139184610Salfred
140184610Salfred				ud->bMasterInterface +=
141184610Salfred				    temp->bInterfaceNumber;
142184610Salfred				ud->bSlaveInterface[0] +=
143184610Salfred				    temp->bInterfaceNumber;
144184610Salfred			}
145223467Shselasky
146223467Shselasky			/* check if we have got an interface association descriptor */
147223467Shselasky
148223467Shselasky			if ((raw[0] >= sizeof(struct usb_interface_assoc_descriptor)) &&
149223467Shselasky			    (raw[1] == UDESC_IFACE_ASSOC)) {
150223467Shselasky				struct usb_interface_assoc_descriptor *iad = (void *)dst;
151223467Shselasky
152223467Shselasky				/* update the interface number */
153223467Shselasky
154223467Shselasky				iad->bFirstInterface +=
155223467Shselasky				    temp->bInterfaceNumber;
156223467Shselasky			}
157223467Shselasky
158223467Shselasky			/* check if we have got a call management descriptor */
159223467Shselasky
160223467Shselasky			if ((raw[0] >= sizeof(struct usb_cdc_cm_descriptor)) &&
161223467Shselasky			    (raw[1] == UDESC_CS_INTERFACE) &&
162223467Shselasky			    (raw[2] == UDESCSUB_CDC_CM)) {
163223467Shselasky				struct usb_cdc_cm_descriptor *ccd = (void *)dst;
164223467Shselasky
165223467Shselasky				/* update the interface number */
166223467Shselasky
167223467Shselasky				ccd->bDataInterface +=
168223467Shselasky				    temp->bInterfaceNumber;
169223467Shselasky			}
170184610Salfred		}
171184610Salfred		temp->size += len;
172184610Salfred	}
173184610Salfred}
174184610Salfred
175184610Salfred/*------------------------------------------------------------------------*
176194228Sthompsa *	usb_make_endpoint_desc
177184610Salfred *
178184610Salfred * This function will generate an USB endpoint descriptor from the
179184610Salfred * given USB template endpoint descriptor, which will be inserted into
180184610Salfred * the USB configuration.
181184610Salfred *------------------------------------------------------------------------*/
182184610Salfredstatic void
183194228Sthompsausb_make_endpoint_desc(struct usb_temp_setup *temp,
184192984Sthompsa    const struct usb_temp_endpoint_desc *ted)
185184610Salfred{
186192984Sthompsa	struct usb_endpoint_descriptor *ed;
187184610Salfred	const void **rd;
188184610Salfred	uint16_t old_size;
189184610Salfred	uint16_t mps;
190205033Sthompsa	uint8_t ea;			/* Endpoint Address */
191205033Sthompsa	uint8_t et;			/* Endpiont Type */
192184610Salfred
193184610Salfred	/* Reserve memory */
194184610Salfred	old_size = temp->size;
195184610Salfred
196205033Sthompsa	ea = (ted->bEndpointAddress & (UE_ADDR | UE_DIR_IN | UE_DIR_OUT));
197205033Sthompsa	et = (ted->bmAttributes & UE_XFERTYPE);
198205033Sthompsa
199205033Sthompsa	if (et == UE_ISOCHRONOUS) {
200205033Sthompsa		/* account for extra byte fields */
201205033Sthompsa		temp->size += sizeof(*ed) + 2;
202205033Sthompsa	} else {
203205033Sthompsa		temp->size += sizeof(*ed);
204205033Sthompsa	}
205205033Sthompsa
206184610Salfred	/* Scan all Raw Descriptors first */
207184610Salfred	rd = ted->ppRawDesc;
208184610Salfred	if (rd) {
209184610Salfred		while (*rd) {
210194228Sthompsa			usb_make_raw_desc(temp, *rd);
211184610Salfred			rd++;
212184610Salfred		}
213184610Salfred	}
214184610Salfred	if (ted->pPacketSize == NULL) {
215184610Salfred		/* not initialized */
216184610Salfred		temp->err = USB_ERR_INVAL;
217184610Salfred		return;
218184610Salfred	}
219192500Sthompsa	mps = ted->pPacketSize->mps[temp->usb_speed];
220184610Salfred	if (mps == 0) {
221184610Salfred		/* not initialized */
222184610Salfred		temp->err = USB_ERR_INVAL;
223184610Salfred		return;
224184610Salfred	} else if (mps == UE_ZERO_MPS) {
225184610Salfred		/* escape for Zero Max Packet Size */
226184610Salfred		mps = 0;
227184610Salfred	}
228184610Salfred
229184610Salfred	/*
230184610Salfred	 * Fill out the real USB endpoint descriptor
231184610Salfred	 * in case there is a buffer present:
232184610Salfred	 */
233184610Salfred	if (temp->buf) {
234184610Salfred		ed = USB_ADD_BYTES(temp->buf, old_size);
235205033Sthompsa		if (et == UE_ISOCHRONOUS)
236205033Sthompsa			ed->bLength = sizeof(*ed) + 2;
237205033Sthompsa		else
238205033Sthompsa			ed->bLength = sizeof(*ed);
239184610Salfred		ed->bDescriptorType = UDESC_ENDPOINT;
240184610Salfred		ed->bEndpointAddress = ea;
241184610Salfred		ed->bmAttributes = ted->bmAttributes;
242184610Salfred		USETW(ed->wMaxPacketSize, mps);
243184610Salfred
244184610Salfred		/* setup bInterval parameter */
245184610Salfred
246184610Salfred		if (ted->pIntervals &&
247192500Sthompsa		    ted->pIntervals->bInterval[temp->usb_speed]) {
248184610Salfred			ed->bInterval =
249192500Sthompsa			    ted->pIntervals->bInterval[temp->usb_speed];
250184610Salfred		} else {
251184610Salfred			switch (et) {
252184610Salfred			case UE_BULK:
253184610Salfred			case UE_CONTROL:
254184610Salfred				ed->bInterval = 0;	/* not used */
255184610Salfred				break;
256184610Salfred			case UE_INTERRUPT:
257192500Sthompsa				switch (temp->usb_speed) {
258184610Salfred				case USB_SPEED_LOW:
259184610Salfred				case USB_SPEED_FULL:
260184610Salfred					ed->bInterval = 1;	/* 1 ms */
261184610Salfred					break;
262184610Salfred				default:
263184610Salfred					ed->bInterval = 8;	/* 8*125 us */
264184610Salfred					break;
265184610Salfred				}
266184610Salfred				break;
267184610Salfred			default:	/* UE_ISOCHRONOUS */
268192500Sthompsa				switch (temp->usb_speed) {
269184610Salfred				case USB_SPEED_LOW:
270184610Salfred				case USB_SPEED_FULL:
271184610Salfred					ed->bInterval = 1;	/* 1 ms */
272184610Salfred					break;
273184610Salfred				default:
274184610Salfred					ed->bInterval = 1;	/* 125 us */
275184610Salfred					break;
276184610Salfred				}
277184610Salfred				break;
278184610Salfred			}
279184610Salfred		}
280184610Salfred	}
281184610Salfred	temp->bNumEndpoints++;
282184610Salfred}
283184610Salfred
284184610Salfred/*------------------------------------------------------------------------*
285194228Sthompsa *	usb_make_interface_desc
286184610Salfred *
287184610Salfred * This function will generate an USB interface descriptor from the
288184610Salfred * given USB template interface descriptor, which will be inserted
289184610Salfred * into the USB configuration.
290184610Salfred *------------------------------------------------------------------------*/
291184610Salfredstatic void
292194228Sthompsausb_make_interface_desc(struct usb_temp_setup *temp,
293192984Sthompsa    const struct usb_temp_interface_desc *tid)
294184610Salfred{
295192984Sthompsa	struct usb_interface_descriptor *id;
296192984Sthompsa	const struct usb_temp_endpoint_desc **ted;
297184610Salfred	const void **rd;
298184610Salfred	uint16_t old_size;
299184610Salfred
300184610Salfred	/* Reserve memory */
301184610Salfred
302184610Salfred	old_size = temp->size;
303184610Salfred	temp->size += sizeof(*id);
304184610Salfred
305184610Salfred	/* Update interface and alternate interface numbers */
306184610Salfred
307184610Salfred	if (tid->isAltInterface == 0) {
308184610Salfred		temp->bAlternateSetting = 0;
309184610Salfred		temp->bInterfaceNumber++;
310184610Salfred	} else {
311184610Salfred		temp->bAlternateSetting++;
312184610Salfred	}
313184610Salfred
314184610Salfred	/* Scan all Raw Descriptors first */
315184610Salfred
316184610Salfred	rd = tid->ppRawDesc;
317184610Salfred
318184610Salfred	if (rd) {
319184610Salfred		while (*rd) {
320194228Sthompsa			usb_make_raw_desc(temp, *rd);
321184610Salfred			rd++;
322184610Salfred		}
323184610Salfred	}
324184610Salfred	/* Reset some counters */
325184610Salfred
326184610Salfred	temp->bNumEndpoints = 0;
327184610Salfred
328184610Salfred	/* Scan all Endpoint Descriptors second */
329184610Salfred
330184610Salfred	ted = tid->ppEndpoints;
331184610Salfred	if (ted) {
332184610Salfred		while (*ted) {
333194228Sthompsa			usb_make_endpoint_desc(temp, *ted);
334184610Salfred			ted++;
335184610Salfred		}
336184610Salfred	}
337184610Salfred	/*
338184610Salfred	 * Fill out the real USB interface descriptor
339184610Salfred	 * in case there is a buffer present:
340184610Salfred	 */
341184610Salfred	if (temp->buf) {
342184610Salfred		id = USB_ADD_BYTES(temp->buf, old_size);
343184610Salfred		id->bLength = sizeof(*id);
344184610Salfred		id->bDescriptorType = UDESC_INTERFACE;
345184610Salfred		id->bInterfaceNumber = temp->bInterfaceNumber;
346184610Salfred		id->bAlternateSetting = temp->bAlternateSetting;
347184610Salfred		id->bNumEndpoints = temp->bNumEndpoints;
348184610Salfred		id->bInterfaceClass = tid->bInterfaceClass;
349184610Salfred		id->bInterfaceSubClass = tid->bInterfaceSubClass;
350184610Salfred		id->bInterfaceProtocol = tid->bInterfaceProtocol;
351184610Salfred		id->iInterface = tid->iInterface;
352184610Salfred	}
353184610Salfred}
354184610Salfred
355184610Salfred/*------------------------------------------------------------------------*
356194228Sthompsa *	usb_make_config_desc
357184610Salfred *
358184610Salfred * This function will generate an USB config descriptor from the given
359184610Salfred * USB template config descriptor, which will be inserted into the USB
360184610Salfred * configuration.
361184610Salfred *------------------------------------------------------------------------*/
362184610Salfredstatic void
363194228Sthompsausb_make_config_desc(struct usb_temp_setup *temp,
364192984Sthompsa    const struct usb_temp_config_desc *tcd)
365184610Salfred{
366192984Sthompsa	struct usb_config_descriptor *cd;
367192984Sthompsa	const struct usb_temp_interface_desc **tid;
368184610Salfred	uint16_t old_size;
369184610Salfred
370184610Salfred	/* Reserve memory */
371184610Salfred
372184610Salfred	old_size = temp->size;
373184610Salfred	temp->size += sizeof(*cd);
374184610Salfred
375184610Salfred	/* Reset some counters */
376184610Salfred
377184610Salfred	temp->bInterfaceNumber = 0 - 1;
378184610Salfred	temp->bAlternateSetting = 0;
379184610Salfred
380184610Salfred	/* Scan all the USB interfaces */
381184610Salfred
382184610Salfred	tid = tcd->ppIfaceDesc;
383184610Salfred	if (tid) {
384184610Salfred		while (*tid) {
385194228Sthompsa			usb_make_interface_desc(temp, *tid);
386184610Salfred			tid++;
387184610Salfred		}
388184610Salfred	}
389184610Salfred	/*
390184610Salfred	 * Fill out the real USB config descriptor
391184610Salfred	 * in case there is a buffer present:
392184610Salfred	 */
393184610Salfred	if (temp->buf) {
394184610Salfred		cd = USB_ADD_BYTES(temp->buf, old_size);
395184610Salfred
396184610Salfred		/* compute total size */
397184610Salfred		old_size = temp->size - old_size;
398184610Salfred
399184610Salfred		cd->bLength = sizeof(*cd);
400184610Salfred		cd->bDescriptorType = UDESC_CONFIG;
401184610Salfred		USETW(cd->wTotalLength, old_size);
402184610Salfred		cd->bNumInterface = temp->bInterfaceNumber + 1;
403184610Salfred		cd->bConfigurationValue = temp->bConfigurationValue;
404184610Salfred		cd->iConfiguration = tcd->iConfiguration;
405184610Salfred		cd->bmAttributes = tcd->bmAttributes;
406184610Salfred		cd->bMaxPower = tcd->bMaxPower;
407184610Salfred		cd->bmAttributes |= (UC_REMOTE_WAKEUP | UC_BUS_POWERED);
408184610Salfred
409184610Salfred		if (temp->self_powered) {
410184610Salfred			cd->bmAttributes |= UC_SELF_POWERED;
411184610Salfred		} else {
412184610Salfred			cd->bmAttributes &= ~UC_SELF_POWERED;
413184610Salfred		}
414184610Salfred	}
415184610Salfred}
416184610Salfred
417184610Salfred/*------------------------------------------------------------------------*
418194228Sthompsa *	usb_make_device_desc
419184610Salfred *
420184610Salfred * This function will generate an USB device descriptor from the
421184610Salfred * given USB template device descriptor.
422184610Salfred *------------------------------------------------------------------------*/
423184610Salfredstatic void
424194228Sthompsausb_make_device_desc(struct usb_temp_setup *temp,
425192984Sthompsa    const struct usb_temp_device_desc *tdd)
426184610Salfred{
427192984Sthompsa	struct usb_temp_data *utd;
428192984Sthompsa	const struct usb_temp_config_desc **tcd;
429184610Salfred	uint16_t old_size;
430184610Salfred
431184610Salfred	/* Reserve memory */
432184610Salfred
433184610Salfred	old_size = temp->size;
434184610Salfred	temp->size += sizeof(*utd);
435184610Salfred
436184610Salfred	/* Scan all the USB configs */
437184610Salfred
438184610Salfred	temp->bConfigurationValue = 1;
439184610Salfred	tcd = tdd->ppConfigDesc;
440184610Salfred	if (tcd) {
441184610Salfred		while (*tcd) {
442194228Sthompsa			usb_make_config_desc(temp, *tcd);
443184610Salfred			temp->bConfigurationValue++;
444184610Salfred			tcd++;
445184610Salfred		}
446184610Salfred	}
447184610Salfred	/*
448184610Salfred	 * Fill out the real USB device descriptor
449184610Salfred	 * in case there is a buffer present:
450184610Salfred	 */
451184610Salfred
452184610Salfred	if (temp->buf) {
453184610Salfred		utd = USB_ADD_BYTES(temp->buf, old_size);
454184610Salfred
455184610Salfred		/* Store a pointer to our template device descriptor */
456184610Salfred		utd->tdd = tdd;
457184610Salfred
458184610Salfred		/* Fill out USB device descriptor */
459184610Salfred		utd->udd.bLength = sizeof(utd->udd);
460184610Salfred		utd->udd.bDescriptorType = UDESC_DEVICE;
461184610Salfred		utd->udd.bDeviceClass = tdd->bDeviceClass;
462184610Salfred		utd->udd.bDeviceSubClass = tdd->bDeviceSubClass;
463184610Salfred		utd->udd.bDeviceProtocol = tdd->bDeviceProtocol;
464184610Salfred		USETW(utd->udd.idVendor, tdd->idVendor);
465184610Salfred		USETW(utd->udd.idProduct, tdd->idProduct);
466184610Salfred		USETW(utd->udd.bcdDevice, tdd->bcdDevice);
467184610Salfred		utd->udd.iManufacturer = tdd->iManufacturer;
468184610Salfred		utd->udd.iProduct = tdd->iProduct;
469184610Salfred		utd->udd.iSerialNumber = tdd->iSerialNumber;
470184610Salfred		utd->udd.bNumConfigurations = temp->bConfigurationValue - 1;
471184610Salfred
472184610Salfred		/*
473184610Salfred		 * Fill out the USB device qualifier. Pretend that we
474184610Salfred		 * don't support any other speeds by setting
475184610Salfred		 * "bNumConfigurations" equal to zero. That saves us
476184610Salfred		 * generating an extra set of configuration
477184610Salfred		 * descriptors.
478184610Salfred		 */
479184610Salfred		utd->udq.bLength = sizeof(utd->udq);
480184610Salfred		utd->udq.bDescriptorType = UDESC_DEVICE_QUALIFIER;
481184610Salfred		utd->udq.bDeviceClass = tdd->bDeviceClass;
482184610Salfred		utd->udq.bDeviceSubClass = tdd->bDeviceSubClass;
483184610Salfred		utd->udq.bDeviceProtocol = tdd->bDeviceProtocol;
484184610Salfred		utd->udq.bNumConfigurations = 0;
485184610Salfred		USETW(utd->udq.bcdUSB, 0x0200);
486184610Salfred		utd->udq.bMaxPacketSize0 = 0;
487184610Salfred
488192500Sthompsa		switch (temp->usb_speed) {
489184610Salfred		case USB_SPEED_LOW:
490184610Salfred			USETW(utd->udd.bcdUSB, 0x0110);
491184610Salfred			utd->udd.bMaxPacketSize = 8;
492184610Salfred			break;
493184610Salfred		case USB_SPEED_FULL:
494184610Salfred			USETW(utd->udd.bcdUSB, 0x0110);
495184610Salfred			utd->udd.bMaxPacketSize = 32;
496184610Salfred			break;
497184610Salfred		case USB_SPEED_HIGH:
498184610Salfred			USETW(utd->udd.bcdUSB, 0x0200);
499184610Salfred			utd->udd.bMaxPacketSize = 64;
500184610Salfred			break;
501184610Salfred		case USB_SPEED_VARIABLE:
502184610Salfred			USETW(utd->udd.bcdUSB, 0x0250);
503184610Salfred			utd->udd.bMaxPacketSize = 255;	/* 512 bytes */
504184610Salfred			break;
505223467Shselasky		case USB_SPEED_SUPER:
506223467Shselasky			USETW(utd->udd.bcdUSB, 0x0300);
507223467Shselasky			utd->udd.bMaxPacketSize = 9;	/* 2**9 = 512 bytes */
508223467Shselasky			break;
509184610Salfred		default:
510184610Salfred			temp->err = USB_ERR_INVAL;
511184610Salfred			break;
512184610Salfred		}
513184610Salfred	}
514184610Salfred}
515184610Salfred
516184610Salfred/*------------------------------------------------------------------------*
517194228Sthompsa *	usb_hw_ep_match
518184610Salfred *
519184610Salfred * Return values:
520184610Salfred *    0: The endpoint profile does not match the criterias
521184610Salfred * Else: The endpoint profile matches the criterias
522184610Salfred *------------------------------------------------------------------------*/
523184610Salfredstatic uint8_t
524194228Sthompsausb_hw_ep_match(const struct usb_hw_ep_profile *pf,
525184610Salfred    uint8_t ep_type, uint8_t ep_dir_in)
526184610Salfred{
527184610Salfred	if (ep_type == UE_CONTROL) {
528184610Salfred		/* special */
529184610Salfred		return (pf->support_control);
530184610Salfred	}
531184610Salfred	if ((pf->support_in && ep_dir_in) ||
532184610Salfred	    (pf->support_out && !ep_dir_in)) {
533184610Salfred		if ((pf->support_interrupt && (ep_type == UE_INTERRUPT)) ||
534184610Salfred		    (pf->support_isochronous && (ep_type == UE_ISOCHRONOUS)) ||
535184610Salfred		    (pf->support_bulk && (ep_type == UE_BULK))) {
536184610Salfred			return (1);
537184610Salfred		}
538184610Salfred	}
539184610Salfred	return (0);
540184610Salfred}
541184610Salfred
542184610Salfred/*------------------------------------------------------------------------*
543194228Sthompsa *	usb_hw_ep_find_match
544184610Salfred *
545184610Salfred * This function is used to find the best matching endpoint profile
546184610Salfred * for and endpoint belonging to an USB descriptor.
547184610Salfred *
548184610Salfred * Return values:
549184610Salfred *    0: Success. Got a match.
550184610Salfred * Else: Failure. No match.
551184610Salfred *------------------------------------------------------------------------*/
552184610Salfredstatic uint8_t
553194228Sthompsausb_hw_ep_find_match(struct usb_hw_ep_scratch *ues,
554192984Sthompsa    struct usb_hw_ep_scratch_sub *ep, uint8_t is_simplex)
555184610Salfred{
556192984Sthompsa	const struct usb_hw_ep_profile *pf;
557184610Salfred	uint16_t distance;
558184610Salfred	uint16_t temp;
559184610Salfred	uint16_t max_frame_size;
560184610Salfred	uint8_t n;
561184610Salfred	uint8_t best_n;
562184610Salfred	uint8_t dir_in;
563184610Salfred	uint8_t dir_out;
564184610Salfred
565184610Salfred	distance = 0xFFFF;
566184610Salfred	best_n = 0;
567184610Salfred
568184610Salfred	if ((!ep->needs_in) && (!ep->needs_out)) {
569184610Salfred		return (0);		/* we are done */
570184610Salfred	}
571184610Salfred	if (ep->needs_ep_type == UE_CONTROL) {
572184610Salfred		dir_in = 1;
573184610Salfred		dir_out = 1;
574184610Salfred	} else {
575184610Salfred		if (ep->needs_in) {
576184610Salfred			dir_in = 1;
577184610Salfred			dir_out = 0;
578184610Salfred		} else {
579184610Salfred			dir_in = 0;
580184610Salfred			dir_out = 1;
581184610Salfred		}
582184610Salfred	}
583184610Salfred
584184610Salfred	for (n = 1; n != (USB_EP_MAX / 2); n++) {
585184610Salfred
586184610Salfred		/* get HW endpoint profile */
587184610Salfred		(ues->methods->get_hw_ep_profile) (ues->udev, &pf, n);
588184610Salfred		if (pf == NULL) {
589184610Salfred			/* end of profiles */
590184610Salfred			break;
591184610Salfred		}
592184610Salfred		/* check if IN-endpoint is reserved */
593184610Salfred		if (dir_in || pf->is_simplex) {
594184610Salfred			if (ues->bmInAlloc[n / 8] & (1 << (n % 8))) {
595184610Salfred				/* mismatch */
596184610Salfred				continue;
597184610Salfred			}
598184610Salfred		}
599184610Salfred		/* check if OUT-endpoint is reserved */
600184610Salfred		if (dir_out || pf->is_simplex) {
601184610Salfred			if (ues->bmOutAlloc[n / 8] & (1 << (n % 8))) {
602184610Salfred				/* mismatch */
603184610Salfred				continue;
604184610Salfred			}
605184610Salfred		}
606184610Salfred		/* check simplex */
607184610Salfred		if (pf->is_simplex == is_simplex) {
608184610Salfred			/* mismatch */
609184610Salfred			continue;
610184610Salfred		}
611184610Salfred		/* check if HW endpoint matches */
612194228Sthompsa		if (!usb_hw_ep_match(pf, ep->needs_ep_type, dir_in)) {
613184610Salfred			/* mismatch */
614184610Salfred			continue;
615184610Salfred		}
616184610Salfred		/* get maximum frame size */
617184610Salfred		if (dir_in)
618184610Salfred			max_frame_size = pf->max_in_frame_size;
619184610Salfred		else
620184610Salfred			max_frame_size = pf->max_out_frame_size;
621184610Salfred
622184610Salfred		/* check if we have a matching profile */
623184610Salfred		if (max_frame_size >= ep->max_frame_size) {
624184610Salfred			temp = (max_frame_size - ep->max_frame_size);
625184610Salfred			if (distance > temp) {
626184610Salfred				distance = temp;
627184610Salfred				best_n = n;
628184610Salfred				ep->pf = pf;
629184610Salfred			}
630184610Salfred		}
631184610Salfred	}
632184610Salfred
633184610Salfred	/* see if we got a match */
634184610Salfred	if (best_n != 0) {
635184610Salfred		/* get the correct profile */
636184610Salfred		pf = ep->pf;
637184610Salfred
638184610Salfred		/* reserve IN-endpoint */
639184610Salfred		if (dir_in) {
640184610Salfred			ues->bmInAlloc[best_n / 8] |=
641184610Salfred			    (1 << (best_n % 8));
642184610Salfred			ep->hw_endpoint_in = best_n | UE_DIR_IN;
643184610Salfred			ep->needs_in = 0;
644184610Salfred		}
645184610Salfred		/* reserve OUT-endpoint */
646184610Salfred		if (dir_out) {
647184610Salfred			ues->bmOutAlloc[best_n / 8] |=
648184610Salfred			    (1 << (best_n % 8));
649184610Salfred			ep->hw_endpoint_out = best_n | UE_DIR_OUT;
650184610Salfred			ep->needs_out = 0;
651184610Salfred		}
652184610Salfred		return (0);		/* got a match */
653184610Salfred	}
654184610Salfred	return (1);			/* failure */
655184610Salfred}
656184610Salfred
657184610Salfred/*------------------------------------------------------------------------*
658194228Sthompsa *	usb_hw_ep_get_needs
659184610Salfred *
660184610Salfred * This function will figure out the type and number of endpoints
661184610Salfred * which are needed for an USB configuration.
662184610Salfred *
663184610Salfred * Return values:
664184610Salfred *    0: Success.
665184610Salfred * Else: Failure.
666184610Salfred *------------------------------------------------------------------------*/
667184610Salfredstatic uint8_t
668194228Sthompsausb_hw_ep_get_needs(struct usb_hw_ep_scratch *ues,
669184610Salfred    uint8_t ep_type, uint8_t is_complete)
670184610Salfred{
671192984Sthompsa	const struct usb_hw_ep_profile *pf;
672192984Sthompsa	struct usb_hw_ep_scratch_sub *ep_iface;
673192984Sthompsa	struct usb_hw_ep_scratch_sub *ep_curr;
674192984Sthompsa	struct usb_hw_ep_scratch_sub *ep_max;
675192984Sthompsa	struct usb_hw_ep_scratch_sub *ep_end;
676192984Sthompsa	struct usb_descriptor *desc;
677192984Sthompsa	struct usb_interface_descriptor *id;
678192984Sthompsa	struct usb_endpoint_descriptor *ed;
679192500Sthompsa	enum usb_dev_speed speed;
680184610Salfred	uint16_t wMaxPacketSize;
681184610Salfred	uint16_t temp;
682184610Salfred	uint8_t ep_no;
683184610Salfred
684184610Salfred	ep_iface = ues->ep_max;
685184610Salfred	ep_curr = ues->ep_max;
686184610Salfred	ep_end = ues->ep + USB_EP_MAX;
687184610Salfred	ep_max = ues->ep_max;
688184610Salfred	desc = NULL;
689194228Sthompsa	speed = usbd_get_speed(ues->udev);
690184610Salfred
691184610Salfredrepeat:
692184610Salfred
693194228Sthompsa	while ((desc = usb_desc_foreach(ues->cd, desc))) {
694184610Salfred
695184610Salfred		if ((desc->bDescriptorType == UDESC_INTERFACE) &&
696184610Salfred		    (desc->bLength >= sizeof(*id))) {
697184610Salfred
698184610Salfred			id = (void *)desc;
699184610Salfred
700184610Salfred			if (id->bAlternateSetting == 0) {
701184610Salfred				/* going forward */
702184610Salfred				ep_iface = ep_max;
703184610Salfred			} else {
704184610Salfred				/* reset */
705184610Salfred				ep_curr = ep_iface;
706184610Salfred			}
707184610Salfred		}
708184610Salfred		if ((desc->bDescriptorType == UDESC_ENDPOINT) &&
709184610Salfred		    (desc->bLength >= sizeof(*ed))) {
710184610Salfred
711184610Salfred			ed = (void *)desc;
712184610Salfred
713184610Salfred			goto handle_endpoint_desc;
714184610Salfred		}
715184610Salfred	}
716184610Salfred	ues->ep_max = ep_max;
717184610Salfred	return (0);
718184610Salfred
719184610Salfredhandle_endpoint_desc:
720184610Salfred	temp = (ed->bmAttributes & UE_XFERTYPE);
721184610Salfred
722184610Salfred	if (temp == ep_type) {
723184610Salfred
724184610Salfred		if (ep_curr == ep_end) {
725184610Salfred			/* too many endpoints */
726184610Salfred			return (1);	/* failure */
727184610Salfred		}
728184610Salfred		wMaxPacketSize = UGETW(ed->wMaxPacketSize);
729184610Salfred		if ((wMaxPacketSize & 0xF800) &&
730184610Salfred		    (speed == USB_SPEED_HIGH)) {
731184610Salfred			/* handle packet multiplier */
732184610Salfred			temp = (wMaxPacketSize >> 11) & 3;
733184610Salfred			wMaxPacketSize &= 0x7FF;
734184610Salfred			if (temp == 1) {
735184610Salfred				wMaxPacketSize *= 2;
736184610Salfred			} else {
737184610Salfred				wMaxPacketSize *= 3;
738184610Salfred			}
739184610Salfred		}
740184610Salfred		/*
741184610Salfred		 * Check if we have a fixed endpoint number, else the
742184610Salfred		 * endpoint number is allocated dynamically:
743184610Salfred		 */
744184610Salfred		ep_no = (ed->bEndpointAddress & UE_ADDR);
745184610Salfred		if (ep_no != 0) {
746184610Salfred
747184610Salfred			/* get HW endpoint profile */
748184610Salfred			(ues->methods->get_hw_ep_profile)
749184610Salfred			    (ues->udev, &pf, ep_no);
750184610Salfred			if (pf == NULL) {
751184610Salfred				/* HW profile does not exist - failure */
752184610Salfred				DPRINTFN(0, "Endpoint profile %u "
753184610Salfred				    "does not exist\n", ep_no);
754184610Salfred				return (1);
755184610Salfred			}
756184610Salfred			/* reserve fixed endpoint number */
757184610Salfred			if (ep_type == UE_CONTROL) {
758184610Salfred				ues->bmInAlloc[ep_no / 8] |=
759184610Salfred				    (1 << (ep_no % 8));
760184610Salfred				ues->bmOutAlloc[ep_no / 8] |=
761184610Salfred				    (1 << (ep_no % 8));
762184610Salfred				if ((pf->max_in_frame_size < wMaxPacketSize) ||
763184610Salfred				    (pf->max_out_frame_size < wMaxPacketSize)) {
764184610Salfred					DPRINTFN(0, "Endpoint profile %u "
765199816Sthompsa					    "has too small buffer\n", ep_no);
766184610Salfred					return (1);
767184610Salfred				}
768184610Salfred			} else if (ed->bEndpointAddress & UE_DIR_IN) {
769184610Salfred				ues->bmInAlloc[ep_no / 8] |=
770184610Salfred				    (1 << (ep_no % 8));
771184610Salfred				if (pf->max_in_frame_size < wMaxPacketSize) {
772184610Salfred					DPRINTFN(0, "Endpoint profile %u "
773199816Sthompsa					    "has too small buffer\n", ep_no);
774184610Salfred					return (1);
775184610Salfred				}
776184610Salfred			} else {
777184610Salfred				ues->bmOutAlloc[ep_no / 8] |=
778184610Salfred				    (1 << (ep_no % 8));
779184610Salfred				if (pf->max_out_frame_size < wMaxPacketSize) {
780184610Salfred					DPRINTFN(0, "Endpoint profile %u "
781199816Sthompsa					    "has too small buffer\n", ep_no);
782184610Salfred					return (1);
783184610Salfred				}
784184610Salfred			}
785184610Salfred		} else if (is_complete) {
786184610Salfred
787184610Salfred			/* check if we have enough buffer space */
788184610Salfred			if (wMaxPacketSize >
789184610Salfred			    ep_curr->max_frame_size) {
790184610Salfred				return (1);	/* failure */
791184610Salfred			}
792184610Salfred			if (ed->bEndpointAddress & UE_DIR_IN) {
793184610Salfred				ed->bEndpointAddress =
794184610Salfred				    ep_curr->hw_endpoint_in;
795184610Salfred			} else {
796184610Salfred				ed->bEndpointAddress =
797184610Salfred				    ep_curr->hw_endpoint_out;
798184610Salfred			}
799184610Salfred
800184610Salfred		} else {
801184610Salfred
802184610Salfred			/* compute the maximum frame size */
803184610Salfred			if (ep_curr->max_frame_size < wMaxPacketSize) {
804184610Salfred				ep_curr->max_frame_size = wMaxPacketSize;
805184610Salfred			}
806184610Salfred			if (temp == UE_CONTROL) {
807184610Salfred				ep_curr->needs_in = 1;
808184610Salfred				ep_curr->needs_out = 1;
809184610Salfred			} else {
810184610Salfred				if (ed->bEndpointAddress & UE_DIR_IN) {
811184610Salfred					ep_curr->needs_in = 1;
812184610Salfred				} else {
813184610Salfred					ep_curr->needs_out = 1;
814184610Salfred				}
815184610Salfred			}
816184610Salfred			ep_curr->needs_ep_type = ep_type;
817184610Salfred		}
818184610Salfred
819184610Salfred		ep_curr++;
820184610Salfred		if (ep_max < ep_curr) {
821184610Salfred			ep_max = ep_curr;
822184610Salfred		}
823184610Salfred	}
824184610Salfred	goto repeat;
825184610Salfred}
826184610Salfred
827184610Salfred/*------------------------------------------------------------------------*
828194228Sthompsa *	usb_hw_ep_resolve
829184610Salfred *
830184610Salfred * This function will try to resolve endpoint requirements by the
831184610Salfred * given endpoint profiles that the USB hardware reports.
832184610Salfred *
833184610Salfred * Return values:
834184610Salfred *    0: Success
835184610Salfred * Else: Failure
836184610Salfred *------------------------------------------------------------------------*/
837193045Sthompsastatic usb_error_t
838194228Sthompsausb_hw_ep_resolve(struct usb_device *udev,
839192984Sthompsa    struct usb_descriptor *desc)
840184610Salfred{
841192984Sthompsa	struct usb_hw_ep_scratch *ues;
842192984Sthompsa	struct usb_hw_ep_scratch_sub *ep;
843192984Sthompsa	const struct usb_hw_ep_profile *pf;
844192984Sthompsa	struct usb_bus_methods *methods;
845192984Sthompsa	struct usb_device_descriptor *dd;
846184610Salfred	uint16_t mps;
847184610Salfred
848184610Salfred	if (desc == NULL) {
849184610Salfred		return (USB_ERR_INVAL);
850184610Salfred	}
851184610Salfred	/* get bus methods */
852184610Salfred	methods = udev->bus->methods;
853184610Salfred
854184610Salfred	if (methods->get_hw_ep_profile == NULL) {
855184610Salfred		return (USB_ERR_INVAL);
856184610Salfred	}
857184610Salfred	if (desc->bDescriptorType == UDESC_DEVICE) {
858184610Salfred
859184610Salfred		if (desc->bLength < sizeof(*dd)) {
860184610Salfred			return (USB_ERR_INVAL);
861184610Salfred		}
862184610Salfred		dd = (void *)desc;
863184610Salfred
864184610Salfred		/* get HW control endpoint 0 profile */
865184610Salfred		(methods->get_hw_ep_profile) (udev, &pf, 0);
866184610Salfred		if (pf == NULL) {
867184610Salfred			return (USB_ERR_INVAL);
868184610Salfred		}
869194228Sthompsa		if (!usb_hw_ep_match(pf, UE_CONTROL, 0)) {
870184610Salfred			DPRINTFN(0, "Endpoint 0 does not "
871184610Salfred			    "support control\n");
872184610Salfred			return (USB_ERR_INVAL);
873184610Salfred		}
874184610Salfred		mps = dd->bMaxPacketSize;
875184610Salfred
876184610Salfred		if (udev->speed == USB_SPEED_FULL) {
877184610Salfred			/*
878184610Salfred			 * We can optionally choose another packet size !
879184610Salfred			 */
880184610Salfred			while (1) {
881184610Salfred				/* check if "mps" is ok */
882184610Salfred				if (pf->max_in_frame_size >= mps) {
883184610Salfred					break;
884184610Salfred				}
885184610Salfred				/* reduce maximum packet size */
886184610Salfred				mps /= 2;
887184610Salfred
888184610Salfred				/* check if "mps" is too small */
889184610Salfred				if (mps < 8) {
890184610Salfred					return (USB_ERR_INVAL);
891184610Salfred				}
892184610Salfred			}
893184610Salfred
894184610Salfred			dd->bMaxPacketSize = mps;
895184610Salfred
896184610Salfred		} else {
897184610Salfred			/* We only have one choice */
898184610Salfred			if (mps == 255) {
899184610Salfred				mps = 512;
900184610Salfred			}
901184610Salfred			/* Check if we support the specified wMaxPacketSize */
902184610Salfred			if (pf->max_in_frame_size < mps) {
903184610Salfred				return (USB_ERR_INVAL);
904184610Salfred			}
905184610Salfred		}
906184610Salfred		return (0);		/* success */
907184610Salfred	}
908184610Salfred	if (desc->bDescriptorType != UDESC_CONFIG) {
909184610Salfred		return (USB_ERR_INVAL);
910184610Salfred	}
911184610Salfred	if (desc->bLength < sizeof(*(ues->cd))) {
912184610Salfred		return (USB_ERR_INVAL);
913184610Salfred	}
914184610Salfred	ues = udev->bus->scratch[0].hw_ep_scratch;
915184610Salfred
916229080Shselasky	memset(ues, 0, sizeof(*ues));
917184610Salfred
918184610Salfred	ues->ep_max = ues->ep;
919184610Salfred	ues->cd = (void *)desc;
920184610Salfred	ues->methods = methods;
921184610Salfred	ues->udev = udev;
922184610Salfred
923184610Salfred	/* Get all the endpoints we need */
924184610Salfred
925194228Sthompsa	if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) ||
926194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_INTERRUPT, 0) ||
927194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_CONTROL, 0) ||
928194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_BULK, 0)) {
929184610Salfred		DPRINTFN(0, "Could not get needs\n");
930184610Salfred		return (USB_ERR_INVAL);
931184610Salfred	}
932184610Salfred	for (ep = ues->ep; ep != ues->ep_max; ep++) {
933184610Salfred
934184610Salfred		while (ep->needs_in || ep->needs_out) {
935184610Salfred
936184610Salfred			/*
937184610Salfred		         * First try to use a simplex endpoint.
938184610Salfred		         * Then try to use a duplex endpoint.
939184610Salfred		         */
940194228Sthompsa			if (usb_hw_ep_find_match(ues, ep, 1) &&
941194228Sthompsa			    usb_hw_ep_find_match(ues, ep, 0)) {
942184610Salfred				DPRINTFN(0, "Could not find match\n");
943184610Salfred				return (USB_ERR_INVAL);
944184610Salfred			}
945184610Salfred		}
946184610Salfred	}
947184610Salfred
948184610Salfred	ues->ep_max = ues->ep;
949184610Salfred
950184610Salfred	/* Update all endpoint addresses */
951184610Salfred
952194228Sthompsa	if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) ||
953194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_INTERRUPT, 1) ||
954194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_CONTROL, 1) ||
955194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_BULK, 1)) {
956184610Salfred		DPRINTFN(0, "Could not update endpoint address\n");
957184610Salfred		return (USB_ERR_INVAL);
958184610Salfred	}
959184610Salfred	return (0);			/* success */
960184610Salfred}
961184610Salfred
962184610Salfred/*------------------------------------------------------------------------*
963194228Sthompsa *	usb_temp_get_tdd
964184610Salfred *
965184610Salfred * Returns:
966184610Salfred *  NULL: No USB template device descriptor found.
967184610Salfred *  Else: Pointer to the USB template device descriptor.
968184610Salfred *------------------------------------------------------------------------*/
969192984Sthompsastatic const struct usb_temp_device_desc *
970194228Sthompsausb_temp_get_tdd(struct usb_device *udev)
971184610Salfred{
972194228Sthompsa	if (udev->usb_template_ptr == NULL) {
973184610Salfred		return (NULL);
974184610Salfred	}
975194228Sthompsa	return (udev->usb_template_ptr->tdd);
976184610Salfred}
977184610Salfred
978184610Salfred/*------------------------------------------------------------------------*
979194228Sthompsa *	usb_temp_get_device_desc
980184610Salfred *
981184610Salfred * Returns:
982184610Salfred *  NULL: No USB device descriptor found.
983184610Salfred *  Else: Pointer to USB device descriptor.
984184610Salfred *------------------------------------------------------------------------*/
985184610Salfredstatic void *
986194228Sthompsausb_temp_get_device_desc(struct usb_device *udev)
987184610Salfred{
988192984Sthompsa	struct usb_device_descriptor *dd;
989184610Salfred
990194228Sthompsa	if (udev->usb_template_ptr == NULL) {
991184610Salfred		return (NULL);
992184610Salfred	}
993194228Sthompsa	dd = &udev->usb_template_ptr->udd;
994184610Salfred	if (dd->bDescriptorType != UDESC_DEVICE) {
995184610Salfred		/* sanity check failed */
996184610Salfred		return (NULL);
997184610Salfred	}
998184610Salfred	return (dd);
999184610Salfred}
1000184610Salfred
1001184610Salfred/*------------------------------------------------------------------------*
1002194228Sthompsa *	usb_temp_get_qualifier_desc
1003184610Salfred *
1004184610Salfred * Returns:
1005184610Salfred *  NULL: No USB device_qualifier descriptor found.
1006184610Salfred *  Else: Pointer to USB device_qualifier descriptor.
1007184610Salfred *------------------------------------------------------------------------*/
1008184610Salfredstatic void *
1009194228Sthompsausb_temp_get_qualifier_desc(struct usb_device *udev)
1010184610Salfred{
1011192984Sthompsa	struct usb_device_qualifier *dq;
1012184610Salfred
1013194228Sthompsa	if (udev->usb_template_ptr == NULL) {
1014184610Salfred		return (NULL);
1015184610Salfred	}
1016194228Sthompsa	dq = &udev->usb_template_ptr->udq;
1017184610Salfred	if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) {
1018184610Salfred		/* sanity check failed */
1019184610Salfred		return (NULL);
1020184610Salfred	}
1021184610Salfred	return (dq);
1022184610Salfred}
1023184610Salfred
1024184610Salfred/*------------------------------------------------------------------------*
1025194228Sthompsa *	usb_temp_get_config_desc
1026184610Salfred *
1027184610Salfred * Returns:
1028184610Salfred *  NULL: No USB config descriptor found.
1029184610Salfred *  Else: Pointer to USB config descriptor having index "index".
1030184610Salfred *------------------------------------------------------------------------*/
1031184610Salfredstatic void *
1032194228Sthompsausb_temp_get_config_desc(struct usb_device *udev,
1033184610Salfred    uint16_t *pLength, uint8_t index)
1034184610Salfred{
1035192984Sthompsa	struct usb_device_descriptor *dd;
1036192984Sthompsa	struct usb_config_descriptor *cd;
1037184610Salfred	uint16_t temp;
1038184610Salfred
1039194228Sthompsa	if (udev->usb_template_ptr == NULL) {
1040184610Salfred		return (NULL);
1041184610Salfred	}
1042194228Sthompsa	dd = &udev->usb_template_ptr->udd;
1043194228Sthompsa	cd = (void *)(udev->usb_template_ptr + 1);
1044184610Salfred
1045184610Salfred	if (index >= dd->bNumConfigurations) {
1046184610Salfred		/* out of range */
1047184610Salfred		return (NULL);
1048184610Salfred	}
1049184610Salfred	while (index--) {
1050184610Salfred		if (cd->bDescriptorType != UDESC_CONFIG) {
1051184610Salfred			/* sanity check failed */
1052184610Salfred			return (NULL);
1053184610Salfred		}
1054184610Salfred		temp = UGETW(cd->wTotalLength);
1055184610Salfred		cd = USB_ADD_BYTES(cd, temp);
1056184610Salfred	}
1057184610Salfred
1058184610Salfred	if (pLength) {
1059184610Salfred		*pLength = UGETW(cd->wTotalLength);
1060184610Salfred	}
1061184610Salfred	return (cd);
1062184610Salfred}
1063184610Salfred
1064184610Salfred/*------------------------------------------------------------------------*
1065194228Sthompsa *	usb_temp_get_vendor_desc
1066184610Salfred *
1067184610Salfred * Returns:
1068184610Salfred *  NULL: No vendor descriptor found.
1069184610Salfred *  Else: Pointer to a vendor descriptor.
1070184610Salfred *------------------------------------------------------------------------*/
1071184610Salfredstatic const void *
1072194228Sthompsausb_temp_get_vendor_desc(struct usb_device *udev,
1073205030Sthompsa    const struct usb_device_request *req, uint16_t *plen)
1074184610Salfred{
1075192984Sthompsa	const struct usb_temp_device_desc *tdd;
1076184610Salfred
1077194228Sthompsa	tdd = usb_temp_get_tdd(udev);
1078184610Salfred	if (tdd == NULL) {
1079184610Salfred		return (NULL);
1080184610Salfred	}
1081184610Salfred	if (tdd->getVendorDesc == NULL) {
1082184610Salfred		return (NULL);
1083184610Salfred	}
1084205030Sthompsa	return ((tdd->getVendorDesc) (req, plen));
1085184610Salfred}
1086184610Salfred
1087184610Salfred/*------------------------------------------------------------------------*
1088194228Sthompsa *	usb_temp_get_string_desc
1089184610Salfred *
1090184610Salfred * Returns:
1091184610Salfred *  NULL: No string descriptor found.
1092184610Salfred *  Else: Pointer to a string descriptor.
1093184610Salfred *------------------------------------------------------------------------*/
1094184610Salfredstatic const void *
1095194228Sthompsausb_temp_get_string_desc(struct usb_device *udev,
1096184610Salfred    uint16_t lang_id, uint8_t string_index)
1097184610Salfred{
1098192984Sthompsa	const struct usb_temp_device_desc *tdd;
1099184610Salfred
1100194228Sthompsa	tdd = usb_temp_get_tdd(udev);
1101184610Salfred	if (tdd == NULL) {
1102184610Salfred		return (NULL);
1103184610Salfred	}
1104184610Salfred	if (tdd->getStringDesc == NULL) {
1105184610Salfred		return (NULL);
1106184610Salfred	}
1107184610Salfred	return ((tdd->getStringDesc) (lang_id, string_index));
1108184610Salfred}
1109184610Salfred
1110184610Salfred/*------------------------------------------------------------------------*
1111194228Sthompsa *	usb_temp_get_hub_desc
1112184610Salfred *
1113184610Salfred * Returns:
1114184610Salfred *  NULL: No USB HUB descriptor found.
1115184610Salfred *  Else: Pointer to a USB HUB descriptor.
1116184610Salfred *------------------------------------------------------------------------*/
1117184610Salfredstatic const void *
1118194228Sthompsausb_temp_get_hub_desc(struct usb_device *udev)
1119184610Salfred{
1120184610Salfred	return (NULL);			/* needs to be implemented */
1121184610Salfred}
1122184610Salfred
1123184610Salfred/*------------------------------------------------------------------------*
1124194228Sthompsa *	usb_temp_get_desc
1125184610Salfred *
1126184610Salfred * This function is a demultiplexer for local USB device side control
1127184610Salfred * endpoint requests.
1128184610Salfred *------------------------------------------------------------------------*/
1129193045Sthompsastatic usb_error_t
1130194228Sthompsausb_temp_get_desc(struct usb_device *udev, struct usb_device_request *req,
1131184610Salfred    const void **pPtr, uint16_t *pLength)
1132184610Salfred{
1133184610Salfred	const uint8_t *buf;
1134184610Salfred	uint16_t len;
1135184610Salfred
1136184610Salfred	buf = NULL;
1137184610Salfred	len = 0;
1138184610Salfred
1139184610Salfred	switch (req->bmRequestType) {
1140184610Salfred	case UT_READ_DEVICE:
1141184610Salfred		switch (req->bRequest) {
1142184610Salfred		case UR_GET_DESCRIPTOR:
1143184610Salfred			goto tr_handle_get_descriptor;
1144184610Salfred		default:
1145184610Salfred			goto tr_stalled;
1146184610Salfred		}
1147184610Salfred	case UT_READ_CLASS_DEVICE:
1148184610Salfred		switch (req->bRequest) {
1149184610Salfred		case UR_GET_DESCRIPTOR:
1150184610Salfred			goto tr_handle_get_class_descriptor;
1151184610Salfred		default:
1152184610Salfred			goto tr_stalled;
1153184610Salfred		}
1154184610Salfred	default:
1155184610Salfred		goto tr_stalled;
1156184610Salfred	}
1157184610Salfred
1158184610Salfredtr_handle_get_descriptor:
1159184610Salfred	switch (req->wValue[1]) {
1160184610Salfred	case UDESC_DEVICE:
1161184610Salfred		if (req->wValue[0]) {
1162184610Salfred			goto tr_stalled;
1163184610Salfred		}
1164194228Sthompsa		buf = usb_temp_get_device_desc(udev);
1165184610Salfred		goto tr_valid;
1166184610Salfred	case UDESC_DEVICE_QUALIFIER:
1167184610Salfred		if (udev->speed != USB_SPEED_HIGH) {
1168184610Salfred			goto tr_stalled;
1169184610Salfred		}
1170184610Salfred		if (req->wValue[0]) {
1171184610Salfred			goto tr_stalled;
1172184610Salfred		}
1173194228Sthompsa		buf = usb_temp_get_qualifier_desc(udev);
1174184610Salfred		goto tr_valid;
1175184610Salfred	case UDESC_OTHER_SPEED_CONFIGURATION:
1176184610Salfred		if (udev->speed != USB_SPEED_HIGH) {
1177184610Salfred			goto tr_stalled;
1178184610Salfred		}
1179184610Salfred	case UDESC_CONFIG:
1180194228Sthompsa		buf = usb_temp_get_config_desc(udev,
1181184610Salfred		    &len, req->wValue[0]);
1182184610Salfred		goto tr_valid;
1183184610Salfred	case UDESC_STRING:
1184194228Sthompsa		buf = usb_temp_get_string_desc(udev,
1185184610Salfred		    UGETW(req->wIndex), req->wValue[0]);
1186184610Salfred		goto tr_valid;
1187184610Salfred	default:
1188184610Salfred		goto tr_stalled;
1189184610Salfred	}
1190184610Salfred
1191184610Salfredtr_handle_get_class_descriptor:
1192184610Salfred	if (req->wValue[0]) {
1193184610Salfred		goto tr_stalled;
1194184610Salfred	}
1195194228Sthompsa	buf = usb_temp_get_hub_desc(udev);
1196184610Salfred	goto tr_valid;
1197184610Salfred
1198184610Salfredtr_valid:
1199205030Sthompsa	if (buf == NULL)
1200184610Salfred		goto tr_stalled;
1201205030Sthompsa	if (len == 0)
1202184610Salfred		len = buf[0];
1203184610Salfred	*pPtr = buf;
1204184610Salfred	*pLength = len;
1205191402Sthompsa	return (0);	/* success */
1206184610Salfred
1207184610Salfredtr_stalled:
1208205030Sthompsa	/* try to get a vendor specific descriptor */
1209205030Sthompsa	len = 0;
1210205030Sthompsa	buf = usb_temp_get_vendor_desc(udev, req, &len);
1211205030Sthompsa	if (buf != NULL)
1212205030Sthompsa		goto tr_valid;
1213184610Salfred	*pPtr = NULL;
1214184610Salfred	*pLength = 0;
1215191402Sthompsa	return (0);	/* we ignore failures */
1216184610Salfred}
1217184610Salfred
1218184610Salfred/*------------------------------------------------------------------------*
1219192984Sthompsa *	usb_temp_setup
1220184610Salfred *
1221184610Salfred * This function generates USB descriptors according to the given USB
1222184610Salfred * template device descriptor. It will also try to figure out the best
1223184610Salfred * matching endpoint addresses using the hardware endpoint profiles.
1224184610Salfred *
1225184610Salfred * Returns:
1226184610Salfred *    0: Success
1227184610Salfred * Else: Failure
1228184610Salfred *------------------------------------------------------------------------*/
1229205030Sthompsausb_error_t
1230192984Sthompsausb_temp_setup(struct usb_device *udev,
1231192984Sthompsa    const struct usb_temp_device_desc *tdd)
1232184610Salfred{
1233192984Sthompsa	struct usb_temp_setup *uts;
1234184610Salfred	void *buf;
1235184610Salfred	uint8_t n;
1236184610Salfred
1237184610Salfred	if (tdd == NULL) {
1238184610Salfred		/* be NULL safe */
1239184610Salfred		return (0);
1240184610Salfred	}
1241184610Salfred	uts = udev->bus->scratch[0].temp_setup;
1242184610Salfred
1243229080Shselasky	memset(uts, 0, sizeof(*uts));
1244184610Salfred
1245192500Sthompsa	uts->usb_speed = udev->speed;
1246184610Salfred	uts->self_powered = udev->flags.self_powered;
1247184610Salfred
1248184610Salfred	/* first pass */
1249184610Salfred
1250194228Sthompsa	usb_make_device_desc(uts, tdd);
1251184610Salfred
1252184610Salfred	if (uts->err) {
1253184610Salfred		/* some error happened */
1254184610Salfred		return (uts->err);
1255184610Salfred	}
1256184610Salfred	/* sanity check */
1257184610Salfred	if (uts->size == 0) {
1258184610Salfred		return (USB_ERR_INVAL);
1259184610Salfred	}
1260184610Salfred	/* allocate zeroed memory */
1261184610Salfred	uts->buf = malloc(uts->size, M_USB, M_WAITOK | M_ZERO);
1262184610Salfred	if (uts->buf == NULL) {
1263184610Salfred		/* could not allocate memory */
1264184610Salfred		return (USB_ERR_NOMEM);
1265184610Salfred	}
1266184610Salfred	/* second pass */
1267184610Salfred
1268184610Salfred	uts->size = 0;
1269184610Salfred
1270194228Sthompsa	usb_make_device_desc(uts, tdd);
1271184610Salfred
1272184610Salfred	/*
1273184610Salfred	 * Store a pointer to our descriptors:
1274184610Salfred	 */
1275194228Sthompsa	udev->usb_template_ptr = uts->buf;
1276184610Salfred
1277184610Salfred	if (uts->err) {
1278184610Salfred		/* some error happened during second pass */
1279184610Salfred		goto error;
1280184610Salfred	}
1281184610Salfred	/*
1282184610Salfred	 * Resolve all endpoint addresses !
1283184610Salfred	 */
1284194228Sthompsa	buf = usb_temp_get_device_desc(udev);
1285194228Sthompsa	uts->err = usb_hw_ep_resolve(udev, buf);
1286184610Salfred	if (uts->err) {
1287184610Salfred		DPRINTFN(0, "Could not resolve endpoints for "
1288184610Salfred		    "Device Descriptor, error = %s\n",
1289194228Sthompsa		    usbd_errstr(uts->err));
1290184610Salfred		goto error;
1291184610Salfred	}
1292184610Salfred	for (n = 0;; n++) {
1293184610Salfred
1294194228Sthompsa		buf = usb_temp_get_config_desc(udev, NULL, n);
1295184610Salfred		if (buf == NULL) {
1296184610Salfred			break;
1297184610Salfred		}
1298194228Sthompsa		uts->err = usb_hw_ep_resolve(udev, buf);
1299184610Salfred		if (uts->err) {
1300184610Salfred			DPRINTFN(0, "Could not resolve endpoints for "
1301184610Salfred			    "Config Descriptor %u, error = %s\n", n,
1302194228Sthompsa			    usbd_errstr(uts->err));
1303184610Salfred			goto error;
1304184610Salfred		}
1305184610Salfred	}
1306184610Salfred	return (uts->err);
1307184610Salfred
1308184610Salfrederror:
1309194228Sthompsa	usb_temp_unsetup(udev);
1310184610Salfred	return (uts->err);
1311184610Salfred}
1312184610Salfred
1313184610Salfred/*------------------------------------------------------------------------*
1314194228Sthompsa *	usb_temp_unsetup
1315184610Salfred *
1316184610Salfred * This function frees any memory associated with the currently
1317184610Salfred * setup template, if any.
1318184610Salfred *------------------------------------------------------------------------*/
1319205030Sthompsavoid
1320194228Sthompsausb_temp_unsetup(struct usb_device *udev)
1321184610Salfred{
1322194228Sthompsa	if (udev->usb_template_ptr) {
1323184610Salfred
1324194228Sthompsa		free(udev->usb_template_ptr, M_USB);
1325184610Salfred
1326194228Sthompsa		udev->usb_template_ptr = NULL;
1327184610Salfred	}
1328184610Salfred}
1329184610Salfred
1330193045Sthompsastatic usb_error_t
1331194228Sthompsausb_temp_setup_by_index(struct usb_device *udev, uint16_t index)
1332184610Salfred{
1333193045Sthompsa	usb_error_t err;
1334184610Salfred
1335184610Salfred	switch (index) {
1336223467Shselasky	case USB_TEMP_MSC:
1337194228Sthompsa		err = usb_temp_setup(udev, &usb_template_msc);
1338184610Salfred		break;
1339223467Shselasky	case USB_TEMP_CDCE:
1340194228Sthompsa		err = usb_temp_setup(udev, &usb_template_cdce);
1341184610Salfred		break;
1342223467Shselasky	case USB_TEMP_MTP:
1343194228Sthompsa		err = usb_temp_setup(udev, &usb_template_mtp);
1344184610Salfred		break;
1345223467Shselasky	case USB_TEMP_MODEM:
1346223467Shselasky		err = usb_temp_setup(udev, &usb_template_modem);
1347223467Shselasky		break;
1348223467Shselasky	case USB_TEMP_AUDIO:
1349223467Shselasky		err = usb_temp_setup(udev, &usb_template_audio);
1350223467Shselasky		break;
1351223467Shselasky	case USB_TEMP_KBD:
1352223467Shselasky		err = usb_temp_setup(udev, &usb_template_kbd);
1353223467Shselasky		break;
1354223467Shselasky	case USB_TEMP_MOUSE:
1355223467Shselasky		err = usb_temp_setup(udev, &usb_template_mouse);
1356223467Shselasky		break;
1357184610Salfred	default:
1358184610Salfred		return (USB_ERR_INVAL);
1359184610Salfred	}
1360184610Salfred
1361184610Salfred	return (err);
1362184610Salfred}
1363184610Salfred
1364184610Salfredstatic void
1365194228Sthompsausb_temp_init(void *arg)
1366184610Salfred{
1367184610Salfred	/* register our functions */
1368194228Sthompsa	usb_temp_get_desc_p = &usb_temp_get_desc;
1369194228Sthompsa	usb_temp_setup_by_index_p = &usb_temp_setup_by_index;
1370194228Sthompsa	usb_temp_unsetup_p = &usb_temp_unsetup;
1371184610Salfred}
1372184610Salfred
1373194228SthompsaSYSINIT(usb_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_temp_init, NULL);
1374194228SthompsaSYSUNINIT(usb_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_temp_unload, NULL);
1375