1184610Salfred/* $FreeBSD$ */
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:
263229103Shselasky					ed->bInterval = 4;	/* 1 ms */
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
377235000Shselasky	temp->bInterfaceNumber = 0xFF;
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
848247090Shselasky	if (desc == NULL)
849184610Salfred		return (USB_ERR_INVAL);
850247090Shselasky
851184610Salfred	/* get bus methods */
852184610Salfred	methods = udev->bus->methods;
853184610Salfred
854247090Shselasky	if (methods->get_hw_ep_profile == NULL)
855184610Salfred		return (USB_ERR_INVAL);
856247090Shselasky
857184610Salfred	if (desc->bDescriptorType == UDESC_DEVICE) {
858184610Salfred
859247090Shselasky		if (desc->bLength < sizeof(*dd))
860184610Salfred			return (USB_ERR_INVAL);
861247090Shselasky
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	}
908247090Shselasky	if (desc->bDescriptorType != UDESC_CONFIG)
909184610Salfred		return (USB_ERR_INVAL);
910247090Shselasky	if (desc->bLength < sizeof(*(ues->cd)))
911184610Salfred		return (USB_ERR_INVAL);
912184610Salfred
913247090Shselasky	ues = udev->scratch.hw_ep_scratch;
914247090Shselasky
915229080Shselasky	memset(ues, 0, sizeof(*ues));
916184610Salfred
917184610Salfred	ues->ep_max = ues->ep;
918184610Salfred	ues->cd = (void *)desc;
919184610Salfred	ues->methods = methods;
920184610Salfred	ues->udev = udev;
921184610Salfred
922184610Salfred	/* Get all the endpoints we need */
923184610Salfred
924194228Sthompsa	if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 0) ||
925194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_INTERRUPT, 0) ||
926194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_CONTROL, 0) ||
927194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_BULK, 0)) {
928184610Salfred		DPRINTFN(0, "Could not get needs\n");
929184610Salfred		return (USB_ERR_INVAL);
930184610Salfred	}
931184610Salfred	for (ep = ues->ep; ep != ues->ep_max; ep++) {
932184610Salfred
933184610Salfred		while (ep->needs_in || ep->needs_out) {
934184610Salfred
935184610Salfred			/*
936184610Salfred		         * First try to use a simplex endpoint.
937184610Salfred		         * Then try to use a duplex endpoint.
938184610Salfred		         */
939194228Sthompsa			if (usb_hw_ep_find_match(ues, ep, 1) &&
940194228Sthompsa			    usb_hw_ep_find_match(ues, ep, 0)) {
941184610Salfred				DPRINTFN(0, "Could not find match\n");
942184610Salfred				return (USB_ERR_INVAL);
943184610Salfred			}
944184610Salfred		}
945184610Salfred	}
946184610Salfred
947184610Salfred	ues->ep_max = ues->ep;
948184610Salfred
949184610Salfred	/* Update all endpoint addresses */
950184610Salfred
951194228Sthompsa	if (usb_hw_ep_get_needs(ues, UE_ISOCHRONOUS, 1) ||
952194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_INTERRUPT, 1) ||
953194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_CONTROL, 1) ||
954194228Sthompsa	    usb_hw_ep_get_needs(ues, UE_BULK, 1)) {
955184610Salfred		DPRINTFN(0, "Could not update endpoint address\n");
956184610Salfred		return (USB_ERR_INVAL);
957184610Salfred	}
958184610Salfred	return (0);			/* success */
959184610Salfred}
960184610Salfred
961184610Salfred/*------------------------------------------------------------------------*
962194228Sthompsa *	usb_temp_get_tdd
963184610Salfred *
964184610Salfred * Returns:
965184610Salfred *  NULL: No USB template device descriptor found.
966184610Salfred *  Else: Pointer to the USB template device descriptor.
967184610Salfred *------------------------------------------------------------------------*/
968192984Sthompsastatic const struct usb_temp_device_desc *
969194228Sthompsausb_temp_get_tdd(struct usb_device *udev)
970184610Salfred{
971194228Sthompsa	if (udev->usb_template_ptr == NULL) {
972184610Salfred		return (NULL);
973184610Salfred	}
974194228Sthompsa	return (udev->usb_template_ptr->tdd);
975184610Salfred}
976184610Salfred
977184610Salfred/*------------------------------------------------------------------------*
978194228Sthompsa *	usb_temp_get_device_desc
979184610Salfred *
980184610Salfred * Returns:
981184610Salfred *  NULL: No USB device descriptor found.
982184610Salfred *  Else: Pointer to USB device descriptor.
983184610Salfred *------------------------------------------------------------------------*/
984184610Salfredstatic void *
985194228Sthompsausb_temp_get_device_desc(struct usb_device *udev)
986184610Salfred{
987192984Sthompsa	struct usb_device_descriptor *dd;
988184610Salfred
989194228Sthompsa	if (udev->usb_template_ptr == NULL) {
990184610Salfred		return (NULL);
991184610Salfred	}
992194228Sthompsa	dd = &udev->usb_template_ptr->udd;
993184610Salfred	if (dd->bDescriptorType != UDESC_DEVICE) {
994184610Salfred		/* sanity check failed */
995184610Salfred		return (NULL);
996184610Salfred	}
997184610Salfred	return (dd);
998184610Salfred}
999184610Salfred
1000184610Salfred/*------------------------------------------------------------------------*
1001194228Sthompsa *	usb_temp_get_qualifier_desc
1002184610Salfred *
1003184610Salfred * Returns:
1004184610Salfred *  NULL: No USB device_qualifier descriptor found.
1005184610Salfred *  Else: Pointer to USB device_qualifier descriptor.
1006184610Salfred *------------------------------------------------------------------------*/
1007184610Salfredstatic void *
1008194228Sthompsausb_temp_get_qualifier_desc(struct usb_device *udev)
1009184610Salfred{
1010192984Sthompsa	struct usb_device_qualifier *dq;
1011184610Salfred
1012194228Sthompsa	if (udev->usb_template_ptr == NULL) {
1013184610Salfred		return (NULL);
1014184610Salfred	}
1015194228Sthompsa	dq = &udev->usb_template_ptr->udq;
1016184610Salfred	if (dq->bDescriptorType != UDESC_DEVICE_QUALIFIER) {
1017184610Salfred		/* sanity check failed */
1018184610Salfred		return (NULL);
1019184610Salfred	}
1020184610Salfred	return (dq);
1021184610Salfred}
1022184610Salfred
1023184610Salfred/*------------------------------------------------------------------------*
1024194228Sthompsa *	usb_temp_get_config_desc
1025184610Salfred *
1026184610Salfred * Returns:
1027184610Salfred *  NULL: No USB config descriptor found.
1028184610Salfred *  Else: Pointer to USB config descriptor having index "index".
1029184610Salfred *------------------------------------------------------------------------*/
1030184610Salfredstatic void *
1031194228Sthompsausb_temp_get_config_desc(struct usb_device *udev,
1032184610Salfred    uint16_t *pLength, uint8_t index)
1033184610Salfred{
1034192984Sthompsa	struct usb_device_descriptor *dd;
1035192984Sthompsa	struct usb_config_descriptor *cd;
1036184610Salfred	uint16_t temp;
1037184610Salfred
1038194228Sthompsa	if (udev->usb_template_ptr == NULL) {
1039184610Salfred		return (NULL);
1040184610Salfred	}
1041194228Sthompsa	dd = &udev->usb_template_ptr->udd;
1042194228Sthompsa	cd = (void *)(udev->usb_template_ptr + 1);
1043184610Salfred
1044184610Salfred	if (index >= dd->bNumConfigurations) {
1045184610Salfred		/* out of range */
1046184610Salfred		return (NULL);
1047184610Salfred	}
1048184610Salfred	while (index--) {
1049184610Salfred		if (cd->bDescriptorType != UDESC_CONFIG) {
1050184610Salfred			/* sanity check failed */
1051184610Salfred			return (NULL);
1052184610Salfred		}
1053184610Salfred		temp = UGETW(cd->wTotalLength);
1054184610Salfred		cd = USB_ADD_BYTES(cd, temp);
1055184610Salfred	}
1056184610Salfred
1057184610Salfred	if (pLength) {
1058184610Salfred		*pLength = UGETW(cd->wTotalLength);
1059184610Salfred	}
1060184610Salfred	return (cd);
1061184610Salfred}
1062184610Salfred
1063184610Salfred/*------------------------------------------------------------------------*
1064194228Sthompsa *	usb_temp_get_vendor_desc
1065184610Salfred *
1066184610Salfred * Returns:
1067184610Salfred *  NULL: No vendor descriptor found.
1068184610Salfred *  Else: Pointer to a vendor descriptor.
1069184610Salfred *------------------------------------------------------------------------*/
1070184610Salfredstatic const void *
1071194228Sthompsausb_temp_get_vendor_desc(struct usb_device *udev,
1072205030Sthompsa    const struct usb_device_request *req, uint16_t *plen)
1073184610Salfred{
1074192984Sthompsa	const struct usb_temp_device_desc *tdd;
1075184610Salfred
1076194228Sthompsa	tdd = usb_temp_get_tdd(udev);
1077184610Salfred	if (tdd == NULL) {
1078184610Salfred		return (NULL);
1079184610Salfred	}
1080184610Salfred	if (tdd->getVendorDesc == NULL) {
1081184610Salfred		return (NULL);
1082184610Salfred	}
1083205030Sthompsa	return ((tdd->getVendorDesc) (req, plen));
1084184610Salfred}
1085184610Salfred
1086184610Salfred/*------------------------------------------------------------------------*
1087194228Sthompsa *	usb_temp_get_string_desc
1088184610Salfred *
1089184610Salfred * Returns:
1090184610Salfred *  NULL: No string descriptor found.
1091184610Salfred *  Else: Pointer to a string descriptor.
1092184610Salfred *------------------------------------------------------------------------*/
1093184610Salfredstatic const void *
1094194228Sthompsausb_temp_get_string_desc(struct usb_device *udev,
1095184610Salfred    uint16_t lang_id, uint8_t string_index)
1096184610Salfred{
1097192984Sthompsa	const struct usb_temp_device_desc *tdd;
1098184610Salfred
1099194228Sthompsa	tdd = usb_temp_get_tdd(udev);
1100184610Salfred	if (tdd == NULL) {
1101184610Salfred		return (NULL);
1102184610Salfred	}
1103184610Salfred	if (tdd->getStringDesc == NULL) {
1104184610Salfred		return (NULL);
1105184610Salfred	}
1106184610Salfred	return ((tdd->getStringDesc) (lang_id, string_index));
1107184610Salfred}
1108184610Salfred
1109184610Salfred/*------------------------------------------------------------------------*
1110194228Sthompsa *	usb_temp_get_hub_desc
1111184610Salfred *
1112184610Salfred * Returns:
1113184610Salfred *  NULL: No USB HUB descriptor found.
1114184610Salfred *  Else: Pointer to a USB HUB descriptor.
1115184610Salfred *------------------------------------------------------------------------*/
1116184610Salfredstatic const void *
1117194228Sthompsausb_temp_get_hub_desc(struct usb_device *udev)
1118184610Salfred{
1119184610Salfred	return (NULL);			/* needs to be implemented */
1120184610Salfred}
1121184610Salfred
1122184610Salfred/*------------------------------------------------------------------------*
1123194228Sthompsa *	usb_temp_get_desc
1124184610Salfred *
1125184610Salfred * This function is a demultiplexer for local USB device side control
1126184610Salfred * endpoint requests.
1127184610Salfred *------------------------------------------------------------------------*/
1128193045Sthompsastatic usb_error_t
1129194228Sthompsausb_temp_get_desc(struct usb_device *udev, struct usb_device_request *req,
1130184610Salfred    const void **pPtr, uint16_t *pLength)
1131184610Salfred{
1132184610Salfred	const uint8_t *buf;
1133184610Salfred	uint16_t len;
1134184610Salfred
1135184610Salfred	buf = NULL;
1136184610Salfred	len = 0;
1137184610Salfred
1138184610Salfred	switch (req->bmRequestType) {
1139184610Salfred	case UT_READ_DEVICE:
1140184610Salfred		switch (req->bRequest) {
1141184610Salfred		case UR_GET_DESCRIPTOR:
1142184610Salfred			goto tr_handle_get_descriptor;
1143184610Salfred		default:
1144184610Salfred			goto tr_stalled;
1145184610Salfred		}
1146184610Salfred	case UT_READ_CLASS_DEVICE:
1147184610Salfred		switch (req->bRequest) {
1148184610Salfred		case UR_GET_DESCRIPTOR:
1149184610Salfred			goto tr_handle_get_class_descriptor;
1150184610Salfred		default:
1151184610Salfred			goto tr_stalled;
1152184610Salfred		}
1153184610Salfred	default:
1154184610Salfred		goto tr_stalled;
1155184610Salfred	}
1156184610Salfred
1157184610Salfredtr_handle_get_descriptor:
1158184610Salfred	switch (req->wValue[1]) {
1159184610Salfred	case UDESC_DEVICE:
1160184610Salfred		if (req->wValue[0]) {
1161184610Salfred			goto tr_stalled;
1162184610Salfred		}
1163194228Sthompsa		buf = usb_temp_get_device_desc(udev);
1164184610Salfred		goto tr_valid;
1165184610Salfred	case UDESC_DEVICE_QUALIFIER:
1166184610Salfred		if (udev->speed != USB_SPEED_HIGH) {
1167184610Salfred			goto tr_stalled;
1168184610Salfred		}
1169184610Salfred		if (req->wValue[0]) {
1170184610Salfred			goto tr_stalled;
1171184610Salfred		}
1172194228Sthompsa		buf = usb_temp_get_qualifier_desc(udev);
1173184610Salfred		goto tr_valid;
1174184610Salfred	case UDESC_OTHER_SPEED_CONFIGURATION:
1175184610Salfred		if (udev->speed != USB_SPEED_HIGH) {
1176184610Salfred			goto tr_stalled;
1177184610Salfred		}
1178184610Salfred	case UDESC_CONFIG:
1179194228Sthompsa		buf = usb_temp_get_config_desc(udev,
1180184610Salfred		    &len, req->wValue[0]);
1181184610Salfred		goto tr_valid;
1182184610Salfred	case UDESC_STRING:
1183194228Sthompsa		buf = usb_temp_get_string_desc(udev,
1184184610Salfred		    UGETW(req->wIndex), req->wValue[0]);
1185184610Salfred		goto tr_valid;
1186184610Salfred	default:
1187184610Salfred		goto tr_stalled;
1188184610Salfred	}
1189184610Salfred
1190184610Salfredtr_handle_get_class_descriptor:
1191184610Salfred	if (req->wValue[0]) {
1192184610Salfred		goto tr_stalled;
1193184610Salfred	}
1194194228Sthompsa	buf = usb_temp_get_hub_desc(udev);
1195184610Salfred	goto tr_valid;
1196184610Salfred
1197184610Salfredtr_valid:
1198205030Sthompsa	if (buf == NULL)
1199184610Salfred		goto tr_stalled;
1200205030Sthompsa	if (len == 0)
1201184610Salfred		len = buf[0];
1202184610Salfred	*pPtr = buf;
1203184610Salfred	*pLength = len;
1204191402Sthompsa	return (0);	/* success */
1205184610Salfred
1206184610Salfredtr_stalled:
1207205030Sthompsa	/* try to get a vendor specific descriptor */
1208205030Sthompsa	len = 0;
1209205030Sthompsa	buf = usb_temp_get_vendor_desc(udev, req, &len);
1210205030Sthompsa	if (buf != NULL)
1211205030Sthompsa		goto tr_valid;
1212184610Salfred	*pPtr = NULL;
1213184610Salfred	*pLength = 0;
1214191402Sthompsa	return (0);	/* we ignore failures */
1215184610Salfred}
1216184610Salfred
1217184610Salfred/*------------------------------------------------------------------------*
1218192984Sthompsa *	usb_temp_setup
1219184610Salfred *
1220184610Salfred * This function generates USB descriptors according to the given USB
1221184610Salfred * template device descriptor. It will also try to figure out the best
1222184610Salfred * matching endpoint addresses using the hardware endpoint profiles.
1223184610Salfred *
1224184610Salfred * Returns:
1225184610Salfred *    0: Success
1226184610Salfred * Else: Failure
1227184610Salfred *------------------------------------------------------------------------*/
1228205030Sthompsausb_error_t
1229192984Sthompsausb_temp_setup(struct usb_device *udev,
1230192984Sthompsa    const struct usb_temp_device_desc *tdd)
1231184610Salfred{
1232192984Sthompsa	struct usb_temp_setup *uts;
1233184610Salfred	void *buf;
1234247090Shselasky	usb_error_t error;
1235184610Salfred	uint8_t n;
1236247090Shselasky	uint8_t do_unlock;
1237184610Salfred
1238247090Shselasky	/* be NULL safe */
1239247090Shselasky	if (tdd == NULL)
1240184610Salfred		return (0);
1241184610Salfred
1242247090Shselasky	/* Protect scratch area */
1243247090Shselasky	do_unlock = usbd_enum_lock(udev);
1244247090Shselasky
1245247090Shselasky	uts = udev->scratch.temp_setup;
1246247090Shselasky
1247229080Shselasky	memset(uts, 0, sizeof(*uts));
1248184610Salfred
1249192500Sthompsa	uts->usb_speed = udev->speed;
1250184610Salfred	uts->self_powered = udev->flags.self_powered;
1251184610Salfred
1252184610Salfred	/* first pass */
1253184610Salfred
1254194228Sthompsa	usb_make_device_desc(uts, tdd);
1255184610Salfred
1256184610Salfred	if (uts->err) {
1257184610Salfred		/* some error happened */
1258247090Shselasky		goto done;
1259184610Salfred	}
1260184610Salfred	/* sanity check */
1261184610Salfred	if (uts->size == 0) {
1262247090Shselasky		uts->err = USB_ERR_INVAL;
1263247090Shselasky		goto done;
1264184610Salfred	}
1265184610Salfred	/* allocate zeroed memory */
1266184610Salfred	uts->buf = malloc(uts->size, M_USB, M_WAITOK | M_ZERO);
1267247090Shselasky	/*
1268247090Shselasky	 * Allow malloc() to return NULL regardless of M_WAITOK flag.
1269247090Shselasky	 * This helps when porting the software to non-FreeBSD
1270247090Shselasky	 * systems.
1271247090Shselasky	 */
1272184610Salfred	if (uts->buf == NULL) {
1273184610Salfred		/* could not allocate memory */
1274247090Shselasky		uts->err = USB_ERR_NOMEM;
1275247090Shselasky		goto done;
1276184610Salfred	}
1277184610Salfred	/* second pass */
1278184610Salfred
1279184610Salfred	uts->size = 0;
1280184610Salfred
1281194228Sthompsa	usb_make_device_desc(uts, tdd);
1282184610Salfred
1283184610Salfred	/*
1284184610Salfred	 * Store a pointer to our descriptors:
1285184610Salfred	 */
1286194228Sthompsa	udev->usb_template_ptr = uts->buf;
1287184610Salfred
1288184610Salfred	if (uts->err) {
1289184610Salfred		/* some error happened during second pass */
1290247090Shselasky		goto done;
1291184610Salfred	}
1292184610Salfred	/*
1293184610Salfred	 * Resolve all endpoint addresses !
1294184610Salfred	 */
1295194228Sthompsa	buf = usb_temp_get_device_desc(udev);
1296194228Sthompsa	uts->err = usb_hw_ep_resolve(udev, buf);
1297184610Salfred	if (uts->err) {
1298184610Salfred		DPRINTFN(0, "Could not resolve endpoints for "
1299184610Salfred		    "Device Descriptor, error = %s\n",
1300194228Sthompsa		    usbd_errstr(uts->err));
1301247090Shselasky		goto done;
1302184610Salfred	}
1303184610Salfred	for (n = 0;; n++) {
1304184610Salfred
1305194228Sthompsa		buf = usb_temp_get_config_desc(udev, NULL, n);
1306184610Salfred		if (buf == NULL) {
1307184610Salfred			break;
1308184610Salfred		}
1309194228Sthompsa		uts->err = usb_hw_ep_resolve(udev, buf);
1310184610Salfred		if (uts->err) {
1311184610Salfred			DPRINTFN(0, "Could not resolve endpoints for "
1312184610Salfred			    "Config Descriptor %u, error = %s\n", n,
1313194228Sthompsa			    usbd_errstr(uts->err));
1314247090Shselasky			goto done;
1315184610Salfred		}
1316184610Salfred	}
1317247090Shselaskydone:
1318247090Shselasky	error = uts->err;
1319247090Shselasky	if (error)
1320247090Shselasky		usb_temp_unsetup(udev);
1321247090Shselasky	if (do_unlock)
1322247090Shselasky		usbd_enum_unlock(udev);
1323247090Shselasky	return (error);
1324184610Salfred}
1325184610Salfred
1326184610Salfred/*------------------------------------------------------------------------*
1327194228Sthompsa *	usb_temp_unsetup
1328184610Salfred *
1329184610Salfred * This function frees any memory associated with the currently
1330184610Salfred * setup template, if any.
1331184610Salfred *------------------------------------------------------------------------*/
1332205030Sthompsavoid
1333194228Sthompsausb_temp_unsetup(struct usb_device *udev)
1334184610Salfred{
1335194228Sthompsa	if (udev->usb_template_ptr) {
1336184610Salfred
1337194228Sthompsa		free(udev->usb_template_ptr, M_USB);
1338184610Salfred
1339194228Sthompsa		udev->usb_template_ptr = NULL;
1340184610Salfred	}
1341184610Salfred}
1342184610Salfred
1343193045Sthompsastatic usb_error_t
1344194228Sthompsausb_temp_setup_by_index(struct usb_device *udev, uint16_t index)
1345184610Salfred{
1346193045Sthompsa	usb_error_t err;
1347184610Salfred
1348184610Salfred	switch (index) {
1349223467Shselasky	case USB_TEMP_MSC:
1350194228Sthompsa		err = usb_temp_setup(udev, &usb_template_msc);
1351184610Salfred		break;
1352223467Shselasky	case USB_TEMP_CDCE:
1353194228Sthompsa		err = usb_temp_setup(udev, &usb_template_cdce);
1354184610Salfred		break;
1355223467Shselasky	case USB_TEMP_MTP:
1356194228Sthompsa		err = usb_temp_setup(udev, &usb_template_mtp);
1357184610Salfred		break;
1358223467Shselasky	case USB_TEMP_MODEM:
1359223467Shselasky		err = usb_temp_setup(udev, &usb_template_modem);
1360223467Shselasky		break;
1361223467Shselasky	case USB_TEMP_AUDIO:
1362223467Shselasky		err = usb_temp_setup(udev, &usb_template_audio);
1363223467Shselasky		break;
1364223467Shselasky	case USB_TEMP_KBD:
1365223467Shselasky		err = usb_temp_setup(udev, &usb_template_kbd);
1366223467Shselasky		break;
1367223467Shselasky	case USB_TEMP_MOUSE:
1368223467Shselasky		err = usb_temp_setup(udev, &usb_template_mouse);
1369223467Shselasky		break;
1370184610Salfred	default:
1371184610Salfred		return (USB_ERR_INVAL);
1372184610Salfred	}
1373184610Salfred
1374184610Salfred	return (err);
1375184610Salfred}
1376184610Salfred
1377184610Salfredstatic void
1378194228Sthompsausb_temp_init(void *arg)
1379184610Salfred{
1380184610Salfred	/* register our functions */
1381194228Sthompsa	usb_temp_get_desc_p = &usb_temp_get_desc;
1382194228Sthompsa	usb_temp_setup_by_index_p = &usb_temp_setup_by_index;
1383194228Sthompsa	usb_temp_unsetup_p = &usb_temp_unsetup;
1384184610Salfred}
1385184610Salfred
1386194228SthompsaSYSINIT(usb_temp_init, SI_SUB_LOCK, SI_ORDER_FIRST, usb_temp_init, NULL);
1387194228SthompsaSYSUNINIT(usb_temp_unload, SI_SUB_LOCK, SI_ORDER_ANY, usb_temp_unload, NULL);
1388