xref: /freebsd/sys/dev/usb/usb_request.c (revision bd73b187141aae3e07333c211664612120f6e294)
102ac6454SAndrew Thompson /* $FreeBSD$ */
202ac6454SAndrew Thompson /*-
302ac6454SAndrew Thompson  * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
402ac6454SAndrew Thompson  * Copyright (c) 1998 Lennart Augustsson. All rights reserved.
502ac6454SAndrew Thompson  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
602ac6454SAndrew Thompson  *
702ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
802ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
902ac6454SAndrew Thompson  * are met:
1002ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1102ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1202ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1302ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1402ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1502ac6454SAndrew Thompson  *
1602ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1702ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1802ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1902ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2002ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2102ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2202ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2302ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2402ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2502ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2602ac6454SAndrew Thompson  * SUCH DAMAGE.
2702ac6454SAndrew Thompson  */
2802ac6454SAndrew Thompson 
29ed6d949aSAndrew Thompson #include <sys/stdint.h>
30ed6d949aSAndrew Thompson #include <sys/stddef.h>
31ed6d949aSAndrew Thompson #include <sys/param.h>
32ed6d949aSAndrew Thompson #include <sys/queue.h>
33ed6d949aSAndrew Thompson #include <sys/types.h>
34ed6d949aSAndrew Thompson #include <sys/systm.h>
35ed6d949aSAndrew Thompson #include <sys/kernel.h>
36ed6d949aSAndrew Thompson #include <sys/bus.h>
37ed6d949aSAndrew Thompson #include <sys/linker_set.h>
38ed6d949aSAndrew Thompson #include <sys/module.h>
39ed6d949aSAndrew Thompson #include <sys/lock.h>
40ed6d949aSAndrew Thompson #include <sys/mutex.h>
41ed6d949aSAndrew Thompson #include <sys/condvar.h>
42ed6d949aSAndrew Thompson #include <sys/sysctl.h>
43ed6d949aSAndrew Thompson #include <sys/sx.h>
44ed6d949aSAndrew Thompson #include <sys/unistd.h>
45ed6d949aSAndrew Thompson #include <sys/callout.h>
46ed6d949aSAndrew Thompson #include <sys/malloc.h>
47ed6d949aSAndrew Thompson #include <sys/priv.h>
48ed6d949aSAndrew Thompson 
4902ac6454SAndrew Thompson #include <dev/usb/usb.h>
50ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
51ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
5202ac6454SAndrew Thompson #include <dev/usb/usb_ioctl.h>
5302ac6454SAndrew Thompson #include <dev/usb/usbhid.h>
5402ac6454SAndrew Thompson 
55a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_debug
5602ac6454SAndrew Thompson 
5702ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
5802ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
5902ac6454SAndrew Thompson #include <dev/usb/usb_request.h>
6002ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
6102ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h>
6202ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
6302ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
6402ac6454SAndrew Thompson #include <dev/usb/usb_util.h>
6502ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
6602ac6454SAndrew Thompson 
6702ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
6802ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
6902ac6454SAndrew Thompson #include <sys/ctype.h>
7002ac6454SAndrew Thompson 
7102ac6454SAndrew Thompson #if USB_DEBUG
72a593f6b8SAndrew Thompson static int usb_pr_poll_delay = USB_PORT_RESET_DELAY;
73a593f6b8SAndrew Thompson static int usb_pr_recovery_delay = USB_PORT_RESET_RECOVERY;
74a593f6b8SAndrew Thompson static int usb_ss_delay = 0;
7502ac6454SAndrew Thompson 
769360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_poll_delay, CTLFLAG_RW,
77a593f6b8SAndrew Thompson     &usb_pr_poll_delay, 0, "USB port reset poll delay in ms");
789360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_recovery_delay, CTLFLAG_RW,
79a593f6b8SAndrew Thompson     &usb_pr_recovery_delay, 0, "USB port reset recovery delay in ms");
809360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ss_delay, CTLFLAG_RW,
81a593f6b8SAndrew Thompson     &usb_ss_delay, 0, "USB status stage delay in ms");
8202ac6454SAndrew Thompson #endif
8302ac6454SAndrew Thompson 
8402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
85a593f6b8SAndrew Thompson  *	usbd_do_request_callback
8602ac6454SAndrew Thompson  *
8702ac6454SAndrew Thompson  * This function is the USB callback for generic USB Host control
8802ac6454SAndrew Thompson  * transfers.
8902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
9002ac6454SAndrew Thompson void
91ed6d949aSAndrew Thompson usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error)
9202ac6454SAndrew Thompson {
9302ac6454SAndrew Thompson 	;				/* workaround for a bug in "indent" */
9402ac6454SAndrew Thompson 
9502ac6454SAndrew Thompson 	DPRINTF("st=%u\n", USB_GET_STATE(xfer));
9602ac6454SAndrew Thompson 
9702ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9802ac6454SAndrew Thompson 	case USB_ST_SETUP:
99a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
10002ac6454SAndrew Thompson 		break;
10102ac6454SAndrew Thompson 	default:
1028437751dSAndrew Thompson 		cv_signal(xfer->xroot->udev->default_cv);
10302ac6454SAndrew Thompson 		break;
10402ac6454SAndrew Thompson 	}
10502ac6454SAndrew Thompson }
10602ac6454SAndrew Thompson 
10702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
108a593f6b8SAndrew Thompson  *	usb_do_clear_stall_callback
10902ac6454SAndrew Thompson  *
11002ac6454SAndrew Thompson  * This function is the USB callback for generic clear stall requests.
11102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
11202ac6454SAndrew Thompson void
113ed6d949aSAndrew Thompson usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
11402ac6454SAndrew Thompson {
115760bc48eSAndrew Thompson 	struct usb_device_request req;
116760bc48eSAndrew Thompson 	struct usb_device *udev;
117ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
118ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep_end;
119ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep_first;
12063521bbcSAndrew Thompson 	uint8_t to;
12102ac6454SAndrew Thompson 
12202ac6454SAndrew Thompson 	udev = xfer->xroot->udev;
12302ac6454SAndrew Thompson 
12402ac6454SAndrew Thompson 	USB_BUS_LOCK(udev->bus);
12502ac6454SAndrew Thompson 
126ae60fdfbSAndrew Thompson 	/* round robin endpoint clear stall */
12702ac6454SAndrew Thompson 
128ae60fdfbSAndrew Thompson 	ep = udev->ep_curr;
129ae60fdfbSAndrew Thompson 	ep_end = udev->endpoints + udev->endpoints_max;
130ae60fdfbSAndrew Thompson 	ep_first = udev->endpoints;
131ae60fdfbSAndrew Thompson 	to = udev->endpoints_max;
132115df0b6SAndrew Thompson 
13302ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
13402ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
135ae60fdfbSAndrew Thompson 		if (ep == NULL)
136115df0b6SAndrew Thompson 			goto tr_setup;		/* device was unconfigured */
137ae60fdfbSAndrew Thompson 		if (ep->edesc &&
138ae60fdfbSAndrew Thompson 		    ep->is_stalled) {
139ae60fdfbSAndrew Thompson 			ep->toggle_next = 0;
140ae60fdfbSAndrew Thompson 			ep->is_stalled = 0;
14102ac6454SAndrew Thompson 			/* start up the current or next transfer, if any */
142a593f6b8SAndrew Thompson 			usb_command_wrapper(&ep->endpoint_q,
143ae60fdfbSAndrew Thompson 			    ep->endpoint_q.curr);
14402ac6454SAndrew Thompson 		}
145ae60fdfbSAndrew Thompson 		ep++;
14602ac6454SAndrew Thompson 
14702ac6454SAndrew Thompson 	case USB_ST_SETUP:
14802ac6454SAndrew Thompson tr_setup:
149115df0b6SAndrew Thompson 		if (to == 0)
150ae60fdfbSAndrew Thompson 			break;			/* no endpoints - nothing to do */
151ae60fdfbSAndrew Thompson 		if ((ep < ep_first) || (ep >= ep_end))
152ae60fdfbSAndrew Thompson 			ep = ep_first;	/* endpoint wrapped around */
153ae60fdfbSAndrew Thompson 		if (ep->edesc &&
154ae60fdfbSAndrew Thompson 		    ep->is_stalled) {
15502ac6454SAndrew Thompson 
15602ac6454SAndrew Thompson 			/* setup a clear-stall packet */
15702ac6454SAndrew Thompson 
15802ac6454SAndrew Thompson 			req.bmRequestType = UT_WRITE_ENDPOINT;
15902ac6454SAndrew Thompson 			req.bRequest = UR_CLEAR_FEATURE;
16002ac6454SAndrew Thompson 			USETW(req.wValue, UF_ENDPOINT_HALT);
161ae60fdfbSAndrew Thompson 			req.wIndex[0] = ep->edesc->bEndpointAddress;
16202ac6454SAndrew Thompson 			req.wIndex[1] = 0;
16302ac6454SAndrew Thompson 			USETW(req.wLength, 0);
16402ac6454SAndrew Thompson 
16502ac6454SAndrew Thompson 			/* copy in the transfer */
16602ac6454SAndrew Thompson 
167a593f6b8SAndrew Thompson 			usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
16802ac6454SAndrew Thompson 
16902ac6454SAndrew Thompson 			/* set length */
170ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
17102ac6454SAndrew Thompson 			xfer->nframes = 1;
17202ac6454SAndrew Thompson 			USB_BUS_UNLOCK(udev->bus);
17302ac6454SAndrew Thompson 
174a593f6b8SAndrew Thompson 			usbd_transfer_submit(xfer);
17502ac6454SAndrew Thompson 
17602ac6454SAndrew Thompson 			USB_BUS_LOCK(udev->bus);
17702ac6454SAndrew Thompson 			break;
17802ac6454SAndrew Thompson 		}
179ae60fdfbSAndrew Thompson 		ep++;
180115df0b6SAndrew Thompson 		to--;
18102ac6454SAndrew Thompson 		goto tr_setup;
18202ac6454SAndrew Thompson 
18302ac6454SAndrew Thompson 	default:
18402ac6454SAndrew Thompson 		if (xfer->error == USB_ERR_CANCELLED) {
18502ac6454SAndrew Thompson 			break;
18602ac6454SAndrew Thompson 		}
18702ac6454SAndrew Thompson 		goto tr_setup;
18802ac6454SAndrew Thompson 	}
18902ac6454SAndrew Thompson 
190ae60fdfbSAndrew Thompson 	/* store current endpoint */
191ae60fdfbSAndrew Thompson 	udev->ep_curr = ep;
19202ac6454SAndrew Thompson 	USB_BUS_UNLOCK(udev->bus);
19302ac6454SAndrew Thompson }
19402ac6454SAndrew Thompson 
195e0a69b51SAndrew Thompson static usb_handle_req_t *
196a593f6b8SAndrew Thompson usbd_get_hr_func(struct usb_device *udev)
197459d369eSAndrew Thompson {
198459d369eSAndrew Thompson 	/* figure out if there is a Handle Request function */
199f29a0724SAndrew Thompson 	if (udev->flags.usb_mode == USB_MODE_DEVICE)
200a593f6b8SAndrew Thompson 		return (usb_temp_get_desc_p);
201459d369eSAndrew Thompson 	else if (udev->parent_hub == NULL)
202459d369eSAndrew Thompson 		return (udev->bus->methods->roothub_exec);
203459d369eSAndrew Thompson 	else
204459d369eSAndrew Thompson 		return (NULL);
205459d369eSAndrew Thompson }
206459d369eSAndrew Thompson 
20702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
208a593f6b8SAndrew Thompson  *	usbd_do_request_flags and usbd_do_request
20902ac6454SAndrew Thompson  *
21002ac6454SAndrew Thompson  * Description of arguments passed to these functions:
21102ac6454SAndrew Thompson  *
212760bc48eSAndrew Thompson  * "udev" - this is the "usb_device" structure pointer on which the
21302ac6454SAndrew Thompson  * request should be performed. It is possible to call this function
21402ac6454SAndrew Thompson  * in both Host Side mode and Device Side mode.
21502ac6454SAndrew Thompson  *
21602ac6454SAndrew Thompson  * "mtx" - if this argument is non-NULL the mutex pointed to by it
21702ac6454SAndrew Thompson  * will get dropped and picked up during the execution of this
21802ac6454SAndrew Thompson  * function, hence this function sometimes needs to sleep. If this
21902ac6454SAndrew Thompson  * argument is NULL it has no effect.
22002ac6454SAndrew Thompson  *
22102ac6454SAndrew Thompson  * "req" - this argument must always be non-NULL and points to an
22202ac6454SAndrew Thompson  * 8-byte structure holding the USB request to be done. The USB
22302ac6454SAndrew Thompson  * request structure has a bit telling the direction of the USB
22402ac6454SAndrew Thompson  * request, if it is a read or a write.
22502ac6454SAndrew Thompson  *
22602ac6454SAndrew Thompson  * "data" - if the "wLength" part of the structure pointed to by "req"
22702ac6454SAndrew Thompson  * is non-zero this argument must point to a valid kernel buffer which
22802ac6454SAndrew Thompson  * can hold at least "wLength" bytes. If "wLength" is zero "data" can
22902ac6454SAndrew Thompson  * be NULL.
23002ac6454SAndrew Thompson  *
23102ac6454SAndrew Thompson  * "flags" - here is a list of valid flags:
23202ac6454SAndrew Thompson  *
23302ac6454SAndrew Thompson  *  o USB_SHORT_XFER_OK: allows the data transfer to be shorter than
23402ac6454SAndrew Thompson  *  specified
23502ac6454SAndrew Thompson  *
23602ac6454SAndrew Thompson  *  o USB_DELAY_STATUS_STAGE: allows the status stage to be performed
23702ac6454SAndrew Thompson  *  at a later point in time. This is tunable by the "hw.usb.ss_delay"
23802ac6454SAndrew Thompson  *  sysctl. This flag is mostly useful for debugging.
23902ac6454SAndrew Thompson  *
24002ac6454SAndrew Thompson  *  o USB_USER_DATA_PTR: treat the "data" pointer like a userland
24102ac6454SAndrew Thompson  *  pointer.
24202ac6454SAndrew Thompson  *
24302ac6454SAndrew Thompson  * "actlen" - if non-NULL the actual transfer length will be stored in
24402ac6454SAndrew Thompson  * the 16-bit unsigned integer pointed to by "actlen". This
24502ac6454SAndrew Thompson  * information is mostly useful when the "USB_SHORT_XFER_OK" flag is
24602ac6454SAndrew Thompson  * used.
24702ac6454SAndrew Thompson  *
24802ac6454SAndrew Thompson  * "timeout" - gives the timeout for the control transfer in
24902ac6454SAndrew Thompson  * milliseconds. A "timeout" value less than 50 milliseconds is
25002ac6454SAndrew Thompson  * treated like a 50 millisecond timeout. A "timeout" value greater
25102ac6454SAndrew Thompson  * than 30 seconds is treated like a 30 second timeout. This USB stack
25202ac6454SAndrew Thompson  * does not allow control requests without a timeout.
25302ac6454SAndrew Thompson  *
25402ac6454SAndrew Thompson  * NOTE: This function is thread safe. All calls to
255a593f6b8SAndrew Thompson  * "usbd_do_request_flags" will be serialised by the use of an
25602ac6454SAndrew Thompson  * internal "sx_lock".
25702ac6454SAndrew Thompson  *
25802ac6454SAndrew Thompson  * Returns:
25902ac6454SAndrew Thompson  *    0: Success
26002ac6454SAndrew Thompson  * Else: Failure
26102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
262e0a69b51SAndrew Thompson usb_error_t
263a593f6b8SAndrew Thompson usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
264760bc48eSAndrew Thompson     struct usb_device_request *req, void *data, uint16_t flags,
265e0a69b51SAndrew Thompson     uint16_t *actlen, usb_timeout_t timeout)
26602ac6454SAndrew Thompson {
267e0a69b51SAndrew Thompson 	usb_handle_req_t *hr_func;
268760bc48eSAndrew Thompson 	struct usb_xfer *xfer;
26902ac6454SAndrew Thompson 	const void *desc;
27002ac6454SAndrew Thompson 	int err = 0;
271e0a69b51SAndrew Thompson 	usb_ticks_t start_ticks;
272e0a69b51SAndrew Thompson 	usb_ticks_t delta_ticks;
273e0a69b51SAndrew Thompson 	usb_ticks_t max_ticks;
27402ac6454SAndrew Thompson 	uint16_t length;
27502ac6454SAndrew Thompson 	uint16_t temp;
27602ac6454SAndrew Thompson 
27702ac6454SAndrew Thompson 	if (timeout < 50) {
27802ac6454SAndrew Thompson 		/* timeout is too small */
27902ac6454SAndrew Thompson 		timeout = 50;
28002ac6454SAndrew Thompson 	}
28102ac6454SAndrew Thompson 	if (timeout > 30000) {
28202ac6454SAndrew Thompson 		/* timeout is too big */
28302ac6454SAndrew Thompson 		timeout = 30000;
28402ac6454SAndrew Thompson 	}
28502ac6454SAndrew Thompson 	length = UGETW(req->wLength);
28602ac6454SAndrew Thompson 
28702ac6454SAndrew Thompson 	DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x "
28802ac6454SAndrew Thompson 	    "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n",
28902ac6454SAndrew Thompson 	    udev, req->bmRequestType, req->bRequest,
29002ac6454SAndrew Thompson 	    req->wValue[1], req->wValue[0],
29102ac6454SAndrew Thompson 	    req->wIndex[1], req->wIndex[0],
29202ac6454SAndrew Thompson 	    req->wLength[1], req->wLength[0]);
29302ac6454SAndrew Thompson 
294bd216778SAndrew Thompson 	/* Check if the device is still alive */
295bd216778SAndrew Thompson 	if (udev->state < USB_STATE_POWERED) {
296bd216778SAndrew Thompson 		DPRINTF("usb device has gone\n");
297bd216778SAndrew Thompson 		return (USB_ERR_NOT_CONFIGURED);
298bd216778SAndrew Thompson 	}
299bd216778SAndrew Thompson 
30002ac6454SAndrew Thompson 	/*
30102ac6454SAndrew Thompson 	 * Set "actlen" to a known value in case the caller does not
30202ac6454SAndrew Thompson 	 * check the return value:
30302ac6454SAndrew Thompson 	 */
30439307315SAndrew Thompson 	if (actlen)
30502ac6454SAndrew Thompson 		*actlen = 0;
30639307315SAndrew Thompson 
307bdc081c6SAndrew Thompson #if (USB_HAVE_USER_IO == 0)
308bdc081c6SAndrew Thompson 	if (flags & USB_USER_DATA_PTR)
309bdc081c6SAndrew Thompson 		return (USB_ERR_INVAL);
310bdc081c6SAndrew Thompson #endif
31102ac6454SAndrew Thompson 	if (mtx) {
31202ac6454SAndrew Thompson 		mtx_unlock(mtx);
31302ac6454SAndrew Thompson 		if (mtx != &Giant) {
31402ac6454SAndrew Thompson 			mtx_assert(mtx, MA_NOTOWNED);
31502ac6454SAndrew Thompson 		}
31602ac6454SAndrew Thompson 	}
31702ac6454SAndrew Thompson 	/*
31802ac6454SAndrew Thompson 	 * Grab the default sx-lock so that serialisation
31902ac6454SAndrew Thompson 	 * is achieved when multiple threads are involved:
32002ac6454SAndrew Thompson 	 */
32102ac6454SAndrew Thompson 
32202ac6454SAndrew Thompson 	sx_xlock(udev->default_sx);
32302ac6454SAndrew Thompson 
324a593f6b8SAndrew Thompson 	hr_func = usbd_get_hr_func(udev);
32539307315SAndrew Thompson 
326459d369eSAndrew Thompson 	if (hr_func != NULL) {
327459d369eSAndrew Thompson 		DPRINTF("Handle Request function is set\n");
32839307315SAndrew Thompson 
329459d369eSAndrew Thompson 		desc = NULL;
330459d369eSAndrew Thompson 		temp = 0;
331459d369eSAndrew Thompson 
332459d369eSAndrew Thompson 		if (!(req->bmRequestType & UT_READ)) {
33339307315SAndrew Thompson 			if (length != 0) {
334459d369eSAndrew Thompson 				DPRINTFN(1, "The handle request function "
335459d369eSAndrew Thompson 				    "does not support writing data!\n");
33639307315SAndrew Thompson 				err = USB_ERR_INVAL;
33739307315SAndrew Thompson 				goto done;
33839307315SAndrew Thompson 			}
33939307315SAndrew Thompson 		}
340459d369eSAndrew Thompson 
341459d369eSAndrew Thompson 		/* The root HUB code needs the BUS lock locked */
34239307315SAndrew Thompson 
34339307315SAndrew Thompson 		USB_BUS_LOCK(udev->bus);
344459d369eSAndrew Thompson 		err = (hr_func) (udev, req, &desc, &temp);
34539307315SAndrew Thompson 		USB_BUS_UNLOCK(udev->bus);
34639307315SAndrew Thompson 
34739307315SAndrew Thompson 		if (err)
34839307315SAndrew Thompson 			goto done;
34939307315SAndrew Thompson 
350459d369eSAndrew Thompson 		if (length > temp) {
35139307315SAndrew Thompson 			if (!(flags & USB_SHORT_XFER_OK)) {
35239307315SAndrew Thompson 				err = USB_ERR_SHORT_XFER;
35339307315SAndrew Thompson 				goto done;
35439307315SAndrew Thompson 			}
355459d369eSAndrew Thompson 			length = temp;
35639307315SAndrew Thompson 		}
35739307315SAndrew Thompson 		if (actlen)
35839307315SAndrew Thompson 			*actlen = length;
35939307315SAndrew Thompson 
36039307315SAndrew Thompson 		if (length > 0) {
36139307315SAndrew Thompson #if USB_HAVE_USER_IO
36239307315SAndrew Thompson 			if (flags & USB_USER_DATA_PTR) {
363459d369eSAndrew Thompson 				if (copyout(desc, data, length)) {
36439307315SAndrew Thompson 					err = USB_ERR_INVAL;
36539307315SAndrew Thompson 					goto done;
36639307315SAndrew Thompson 				}
36739307315SAndrew Thompson 			} else
36839307315SAndrew Thompson #endif
369459d369eSAndrew Thompson 				bcopy(desc, data, length);
37039307315SAndrew Thompson 		}
371459d369eSAndrew Thompson 		goto done;		/* success */
37239307315SAndrew Thompson 	}
37339307315SAndrew Thompson 
37402ac6454SAndrew Thompson 	/*
37502ac6454SAndrew Thompson 	 * Setup a new USB transfer or use the existing one, if any:
37602ac6454SAndrew Thompson 	 */
377a593f6b8SAndrew Thompson 	usbd_default_transfer_setup(udev);
37802ac6454SAndrew Thompson 
37902ac6454SAndrew Thompson 	xfer = udev->default_xfer[0];
38002ac6454SAndrew Thompson 	if (xfer == NULL) {
38102ac6454SAndrew Thompson 		/* most likely out of memory */
38202ac6454SAndrew Thompson 		err = USB_ERR_NOMEM;
38302ac6454SAndrew Thompson 		goto done;
38402ac6454SAndrew Thompson 	}
38502ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
38602ac6454SAndrew Thompson 
3874eae601eSAndrew Thompson 	if (flags & USB_DELAY_STATUS_STAGE)
38802ac6454SAndrew Thompson 		xfer->flags.manual_status = 1;
3894eae601eSAndrew Thompson 	else
39002ac6454SAndrew Thompson 		xfer->flags.manual_status = 0;
3914eae601eSAndrew Thompson 
3924eae601eSAndrew Thompson 	if (flags & USB_SHORT_XFER_OK)
3934eae601eSAndrew Thompson 		xfer->flags.short_xfer_ok = 1;
3944eae601eSAndrew Thompson 	else
3954eae601eSAndrew Thompson 		xfer->flags.short_xfer_ok = 0;
39602ac6454SAndrew Thompson 
39702ac6454SAndrew Thompson 	xfer->timeout = timeout;
39802ac6454SAndrew Thompson 
39902ac6454SAndrew Thompson 	start_ticks = ticks;
40002ac6454SAndrew Thompson 
40102ac6454SAndrew Thompson 	max_ticks = USB_MS_TO_TICKS(timeout);
40202ac6454SAndrew Thompson 
403a593f6b8SAndrew Thompson 	usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req));
40402ac6454SAndrew Thompson 
405ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_len(xfer, 0, sizeof(*req));
40602ac6454SAndrew Thompson 	xfer->nframes = 2;
40702ac6454SAndrew Thompson 
40802ac6454SAndrew Thompson 	while (1) {
40902ac6454SAndrew Thompson 		temp = length;
41002ac6454SAndrew Thompson 		if (temp > xfer->max_data_length) {
411ed6d949aSAndrew Thompson 			temp = usbd_xfer_max_len(xfer);
41202ac6454SAndrew Thompson 		}
413ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 1, temp);
41402ac6454SAndrew Thompson 
41502ac6454SAndrew Thompson 		if (temp > 0) {
41602ac6454SAndrew Thompson 			if (!(req->bmRequestType & UT_READ)) {
417bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO
41802ac6454SAndrew Thompson 				if (flags & USB_USER_DATA_PTR) {
41902ac6454SAndrew Thompson 					USB_XFER_UNLOCK(xfer);
420a593f6b8SAndrew Thompson 					err = usbd_copy_in_user(xfer->frbuffers + 1,
42102ac6454SAndrew Thompson 					    0, data, temp);
42202ac6454SAndrew Thompson 					USB_XFER_LOCK(xfer);
42302ac6454SAndrew Thompson 					if (err) {
42402ac6454SAndrew Thompson 						err = USB_ERR_INVAL;
42502ac6454SAndrew Thompson 						break;
42602ac6454SAndrew Thompson 					}
427bdc081c6SAndrew Thompson 				} else
428bdc081c6SAndrew Thompson #endif
429a593f6b8SAndrew Thompson 					usbd_copy_in(xfer->frbuffers + 1,
430bdc081c6SAndrew Thompson 					    0, data, temp);
43102ac6454SAndrew Thompson 			}
43202ac6454SAndrew Thompson 			xfer->nframes = 2;
43302ac6454SAndrew Thompson 		} else {
43402ac6454SAndrew Thompson 			if (xfer->frlengths[0] == 0) {
43502ac6454SAndrew Thompson 				if (xfer->flags.manual_status) {
43602ac6454SAndrew Thompson #if USB_DEBUG
43702ac6454SAndrew Thompson 					int temp;
43802ac6454SAndrew Thompson 
439a593f6b8SAndrew Thompson 					temp = usb_ss_delay;
44002ac6454SAndrew Thompson 					if (temp > 5000) {
44102ac6454SAndrew Thompson 						temp = 5000;
44202ac6454SAndrew Thompson 					}
44302ac6454SAndrew Thompson 					if (temp > 0) {
444a593f6b8SAndrew Thompson 						usb_pause_mtx(
44502ac6454SAndrew Thompson 						    xfer->xroot->xfer_mtx,
44602ac6454SAndrew Thompson 						    USB_MS_TO_TICKS(temp));
44702ac6454SAndrew Thompson 					}
44802ac6454SAndrew Thompson #endif
44902ac6454SAndrew Thompson 					xfer->flags.manual_status = 0;
45002ac6454SAndrew Thompson 				} else {
45102ac6454SAndrew Thompson 					break;
45202ac6454SAndrew Thompson 				}
45302ac6454SAndrew Thompson 			}
45402ac6454SAndrew Thompson 			xfer->nframes = 1;
45502ac6454SAndrew Thompson 		}
45602ac6454SAndrew Thompson 
457a593f6b8SAndrew Thompson 		usbd_transfer_start(xfer);
45802ac6454SAndrew Thompson 
459a593f6b8SAndrew Thompson 		while (usbd_transfer_pending(xfer)) {
4608437751dSAndrew Thompson 			cv_wait(udev->default_cv,
46102ac6454SAndrew Thompson 			    xfer->xroot->xfer_mtx);
46202ac6454SAndrew Thompson 		}
46302ac6454SAndrew Thompson 
46402ac6454SAndrew Thompson 		err = xfer->error;
46502ac6454SAndrew Thompson 
46602ac6454SAndrew Thompson 		if (err) {
46702ac6454SAndrew Thompson 			break;
46802ac6454SAndrew Thompson 		}
46902ac6454SAndrew Thompson 		/* subtract length of SETUP packet, if any */
47002ac6454SAndrew Thompson 
47102ac6454SAndrew Thompson 		if (xfer->aframes > 0) {
47202ac6454SAndrew Thompson 			xfer->actlen -= xfer->frlengths[0];
47302ac6454SAndrew Thompson 		} else {
47402ac6454SAndrew Thompson 			xfer->actlen = 0;
47502ac6454SAndrew Thompson 		}
47602ac6454SAndrew Thompson 
47702ac6454SAndrew Thompson 		/* check for short packet */
47802ac6454SAndrew Thompson 
47902ac6454SAndrew Thompson 		if (temp > xfer->actlen) {
48002ac6454SAndrew Thompson 			temp = xfer->actlen;
48102ac6454SAndrew Thompson 			length = temp;
48202ac6454SAndrew Thompson 		}
48302ac6454SAndrew Thompson 		if (temp > 0) {
48402ac6454SAndrew Thompson 			if (req->bmRequestType & UT_READ) {
485bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO
48602ac6454SAndrew Thompson 				if (flags & USB_USER_DATA_PTR) {
48702ac6454SAndrew Thompson 					USB_XFER_UNLOCK(xfer);
488a593f6b8SAndrew Thompson 					err = usbd_copy_out_user(xfer->frbuffers + 1,
48902ac6454SAndrew Thompson 					    0, data, temp);
49002ac6454SAndrew Thompson 					USB_XFER_LOCK(xfer);
49102ac6454SAndrew Thompson 					if (err) {
49202ac6454SAndrew Thompson 						err = USB_ERR_INVAL;
49302ac6454SAndrew Thompson 						break;
49402ac6454SAndrew Thompson 					}
495bdc081c6SAndrew Thompson 				} else
496bdc081c6SAndrew Thompson #endif
497a593f6b8SAndrew Thompson 					usbd_copy_out(xfer->frbuffers + 1,
49802ac6454SAndrew Thompson 					    0, data, temp);
49902ac6454SAndrew Thompson 			}
50002ac6454SAndrew Thompson 		}
50102ac6454SAndrew Thompson 		/*
50202ac6454SAndrew Thompson 		 * Clear "frlengths[0]" so that we don't send the setup
50302ac6454SAndrew Thompson 		 * packet again:
50402ac6454SAndrew Thompson 		 */
505ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, 0);
50602ac6454SAndrew Thompson 
50702ac6454SAndrew Thompson 		/* update length and data pointer */
50802ac6454SAndrew Thompson 		length -= temp;
50902ac6454SAndrew Thompson 		data = USB_ADD_BYTES(data, temp);
51002ac6454SAndrew Thompson 
51102ac6454SAndrew Thompson 		if (actlen) {
51202ac6454SAndrew Thompson 			(*actlen) += temp;
51302ac6454SAndrew Thompson 		}
51402ac6454SAndrew Thompson 		/* check for timeout */
51502ac6454SAndrew Thompson 
51602ac6454SAndrew Thompson 		delta_ticks = ticks - start_ticks;
51702ac6454SAndrew Thompson 		if (delta_ticks > max_ticks) {
51802ac6454SAndrew Thompson 			if (!err) {
51902ac6454SAndrew Thompson 				err = USB_ERR_TIMEOUT;
52002ac6454SAndrew Thompson 			}
52102ac6454SAndrew Thompson 		}
52202ac6454SAndrew Thompson 		if (err) {
52302ac6454SAndrew Thompson 			break;
52402ac6454SAndrew Thompson 		}
52502ac6454SAndrew Thompson 	}
52602ac6454SAndrew Thompson 
52702ac6454SAndrew Thompson 	if (err) {
52802ac6454SAndrew Thompson 		/*
52902ac6454SAndrew Thompson 		 * Make sure that the control endpoint is no longer
53002ac6454SAndrew Thompson 		 * blocked in case of a non-transfer related error:
53102ac6454SAndrew Thompson 		 */
532a593f6b8SAndrew Thompson 		usbd_transfer_stop(xfer);
53302ac6454SAndrew Thompson 	}
53402ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
53502ac6454SAndrew Thompson 
53602ac6454SAndrew Thompson done:
53702ac6454SAndrew Thompson 	sx_xunlock(udev->default_sx);
53802ac6454SAndrew Thompson 
53902ac6454SAndrew Thompson 	if (mtx) {
54002ac6454SAndrew Thompson 		mtx_lock(mtx);
54102ac6454SAndrew Thompson 	}
542e0a69b51SAndrew Thompson 	return ((usb_error_t)err);
54302ac6454SAndrew Thompson }
54402ac6454SAndrew Thompson 
54502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
546a593f6b8SAndrew Thompson  *	usbd_do_request_proc - factored out code
54702ac6454SAndrew Thompson  *
54802ac6454SAndrew Thompson  * This function is factored out code. It does basically the same like
549a593f6b8SAndrew Thompson  * usbd_do_request_flags, except it will check the status of the
55002ac6454SAndrew Thompson  * passed process argument before doing the USB request. If the
55102ac6454SAndrew Thompson  * process is draining the USB_ERR_IOERROR code will be returned. It
55202ac6454SAndrew Thompson  * is assumed that the mutex associated with the process is locked
55302ac6454SAndrew Thompson  * when calling this function.
55402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
555e0a69b51SAndrew Thompson usb_error_t
556a593f6b8SAndrew Thompson usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc,
557760bc48eSAndrew Thompson     struct usb_device_request *req, void *data, uint16_t flags,
558e0a69b51SAndrew Thompson     uint16_t *actlen, usb_timeout_t timeout)
55902ac6454SAndrew Thompson {
560e0a69b51SAndrew Thompson 	usb_error_t err;
56102ac6454SAndrew Thompson 	uint16_t len;
56202ac6454SAndrew Thompson 
56302ac6454SAndrew Thompson 	/* get request data length */
56402ac6454SAndrew Thompson 	len = UGETW(req->wLength);
56502ac6454SAndrew Thompson 
56602ac6454SAndrew Thompson 	/* check if the device is being detached */
567a593f6b8SAndrew Thompson 	if (usb_proc_is_gone(pproc)) {
56802ac6454SAndrew Thompson 		err = USB_ERR_IOERROR;
56902ac6454SAndrew Thompson 		goto done;
57002ac6454SAndrew Thompson 	}
57102ac6454SAndrew Thompson 
57202ac6454SAndrew Thompson 	/* forward the USB request */
573a593f6b8SAndrew Thompson 	err = usbd_do_request_flags(udev, pproc->up_mtx,
57402ac6454SAndrew Thompson 	    req, data, flags, actlen, timeout);
57502ac6454SAndrew Thompson 
57602ac6454SAndrew Thompson done:
57702ac6454SAndrew Thompson 	/* on failure we zero the data */
57802ac6454SAndrew Thompson 	/* on short packet we zero the unused data */
57902ac6454SAndrew Thompson 	if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) {
58002ac6454SAndrew Thompson 		if (err)
58102ac6454SAndrew Thompson 			memset(data, 0, len);
58202ac6454SAndrew Thompson 		else if (actlen && *actlen != len)
58302ac6454SAndrew Thompson 			memset(((uint8_t *)data) + *actlen, 0, len - *actlen);
58402ac6454SAndrew Thompson 	}
58502ac6454SAndrew Thompson 	return (err);
58602ac6454SAndrew Thompson }
58702ac6454SAndrew Thompson 
58802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
589a593f6b8SAndrew Thompson  *	usbd_req_reset_port
59002ac6454SAndrew Thompson  *
59102ac6454SAndrew Thompson  * This function will instruct an USB HUB to perform a reset sequence
59202ac6454SAndrew Thompson  * on the specified port number.
59302ac6454SAndrew Thompson  *
59402ac6454SAndrew Thompson  * Returns:
59502ac6454SAndrew Thompson  *    0: Success. The USB device should now be at address zero.
59602ac6454SAndrew Thompson  * Else: Failure. No USB device is present and the USB port should be
59702ac6454SAndrew Thompson  *       disabled.
59802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
599e0a69b51SAndrew Thompson usb_error_t
600a593f6b8SAndrew Thompson usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
60102ac6454SAndrew Thompson {
602760bc48eSAndrew Thompson 	struct usb_port_status ps;
603e0a69b51SAndrew Thompson 	usb_error_t err;
60402ac6454SAndrew Thompson 	uint16_t n;
60502ac6454SAndrew Thompson 
60602ac6454SAndrew Thompson #if USB_DEBUG
60702ac6454SAndrew Thompson 	uint16_t pr_poll_delay;
60802ac6454SAndrew Thompson 	uint16_t pr_recovery_delay;
60902ac6454SAndrew Thompson 
61002ac6454SAndrew Thompson #endif
611a593f6b8SAndrew Thompson 	err = usbd_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET);
61202ac6454SAndrew Thompson 	if (err) {
61302ac6454SAndrew Thompson 		goto done;
61402ac6454SAndrew Thompson 	}
61502ac6454SAndrew Thompson #if USB_DEBUG
61602ac6454SAndrew Thompson 	/* range check input parameters */
617a593f6b8SAndrew Thompson 	pr_poll_delay = usb_pr_poll_delay;
61802ac6454SAndrew Thompson 	if (pr_poll_delay < 1) {
61902ac6454SAndrew Thompson 		pr_poll_delay = 1;
62002ac6454SAndrew Thompson 	} else if (pr_poll_delay > 1000) {
62102ac6454SAndrew Thompson 		pr_poll_delay = 1000;
62202ac6454SAndrew Thompson 	}
623a593f6b8SAndrew Thompson 	pr_recovery_delay = usb_pr_recovery_delay;
62402ac6454SAndrew Thompson 	if (pr_recovery_delay > 1000) {
62502ac6454SAndrew Thompson 		pr_recovery_delay = 1000;
62602ac6454SAndrew Thompson 	}
62702ac6454SAndrew Thompson #endif
62802ac6454SAndrew Thompson 	n = 0;
62902ac6454SAndrew Thompson 	while (1) {
63002ac6454SAndrew Thompson #if USB_DEBUG
63102ac6454SAndrew Thompson 		/* wait for the device to recover from reset */
632a593f6b8SAndrew Thompson 		usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay));
63302ac6454SAndrew Thompson 		n += pr_poll_delay;
63402ac6454SAndrew Thompson #else
63502ac6454SAndrew Thompson 		/* wait for the device to recover from reset */
636a593f6b8SAndrew Thompson 		usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY));
63702ac6454SAndrew Thompson 		n += USB_PORT_RESET_DELAY;
63802ac6454SAndrew Thompson #endif
639a593f6b8SAndrew Thompson 		err = usbd_req_get_port_status(udev, mtx, &ps, port);
64002ac6454SAndrew Thompson 		if (err) {
64102ac6454SAndrew Thompson 			goto done;
64202ac6454SAndrew Thompson 		}
64302ac6454SAndrew Thompson 		/* if the device disappeared, just give up */
64402ac6454SAndrew Thompson 		if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) {
64502ac6454SAndrew Thompson 			goto done;
64602ac6454SAndrew Thompson 		}
64702ac6454SAndrew Thompson 		/* check if reset is complete */
64802ac6454SAndrew Thompson 		if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) {
64902ac6454SAndrew Thompson 			break;
65002ac6454SAndrew Thompson 		}
65102ac6454SAndrew Thompson 		/* check for timeout */
65202ac6454SAndrew Thompson 		if (n > 1000) {
65302ac6454SAndrew Thompson 			n = 0;
65402ac6454SAndrew Thompson 			break;
65502ac6454SAndrew Thompson 		}
65602ac6454SAndrew Thompson 	}
65702ac6454SAndrew Thompson 
65802ac6454SAndrew Thompson 	/* clear port reset first */
659a593f6b8SAndrew Thompson 	err = usbd_req_clear_port_feature(
66002ac6454SAndrew Thompson 	    udev, mtx, port, UHF_C_PORT_RESET);
66102ac6454SAndrew Thompson 	if (err) {
66202ac6454SAndrew Thompson 		goto done;
66302ac6454SAndrew Thompson 	}
66402ac6454SAndrew Thompson 	/* check for timeout */
66502ac6454SAndrew Thompson 	if (n == 0) {
66602ac6454SAndrew Thompson 		err = USB_ERR_TIMEOUT;
66702ac6454SAndrew Thompson 		goto done;
66802ac6454SAndrew Thompson 	}
66902ac6454SAndrew Thompson #if USB_DEBUG
67002ac6454SAndrew Thompson 	/* wait for the device to recover from reset */
671a593f6b8SAndrew Thompson 	usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay));
67202ac6454SAndrew Thompson #else
67302ac6454SAndrew Thompson 	/* wait for the device to recover from reset */
674a593f6b8SAndrew Thompson 	usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY));
67502ac6454SAndrew Thompson #endif
67602ac6454SAndrew Thompson 
67702ac6454SAndrew Thompson done:
67802ac6454SAndrew Thompson 	DPRINTFN(2, "port %d reset returning error=%s\n",
679a593f6b8SAndrew Thompson 	    port, usbd_errstr(err));
68002ac6454SAndrew Thompson 	return (err);
68102ac6454SAndrew Thompson }
68202ac6454SAndrew Thompson 
68302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
684a593f6b8SAndrew Thompson  *	usbd_req_get_desc
68502ac6454SAndrew Thompson  *
68602ac6454SAndrew Thompson  * This function can be used to retrieve USB descriptors. It contains
68702ac6454SAndrew Thompson  * some additional logic like zeroing of missing descriptor bytes and
68802ac6454SAndrew Thompson  * retrying an USB descriptor in case of failure. The "min_len"
68902ac6454SAndrew Thompson  * argument specifies the minimum descriptor length. The "max_len"
69002ac6454SAndrew Thompson  * argument specifies the maximum descriptor length. If the real
69102ac6454SAndrew Thompson  * descriptor length is less than the minimum length the missing
69216589beaSAndrew Thompson  * byte(s) will be zeroed. The type field, the second byte of the USB
69316589beaSAndrew Thompson  * descriptor, will get forced to the correct type. If the "actlen"
69416589beaSAndrew Thompson  * pointer is non-NULL, the actual length of the transfer will get
69516589beaSAndrew Thompson  * stored in the 16-bit unsigned integer which it is pointing to. The
69616589beaSAndrew Thompson  * first byte of the descriptor will not get updated. If the "actlen"
69716589beaSAndrew Thompson  * pointer is NULL the first byte of the descriptor will get updated
69816589beaSAndrew Thompson  * to reflect the actual length instead. If "min_len" is not equal to
69916589beaSAndrew Thompson  * "max_len" then this function will try to retrive the beginning of
70016589beaSAndrew Thompson  * the descriptor and base the maximum length on the first byte of the
70116589beaSAndrew Thompson  * descriptor.
70202ac6454SAndrew Thompson  *
70302ac6454SAndrew Thompson  * Returns:
70402ac6454SAndrew Thompson  *    0: Success
70502ac6454SAndrew Thompson  * Else: Failure
70602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
707e0a69b51SAndrew Thompson usb_error_t
708a593f6b8SAndrew Thompson usbd_req_get_desc(struct usb_device *udev,
70916589beaSAndrew Thompson     struct mtx *mtx, uint16_t *actlen, void *desc,
71002ac6454SAndrew Thompson     uint16_t min_len, uint16_t max_len,
71102ac6454SAndrew Thompson     uint16_t id, uint8_t type, uint8_t index,
71202ac6454SAndrew Thompson     uint8_t retries)
71302ac6454SAndrew Thompson {
714760bc48eSAndrew Thompson 	struct usb_device_request req;
71502ac6454SAndrew Thompson 	uint8_t *buf;
716e0a69b51SAndrew Thompson 	usb_error_t err;
71702ac6454SAndrew Thompson 
71802ac6454SAndrew Thompson 	DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n",
71902ac6454SAndrew Thompson 	    id, type, index, max_len);
72002ac6454SAndrew Thompson 
72102ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_DEVICE;
72202ac6454SAndrew Thompson 	req.bRequest = UR_GET_DESCRIPTOR;
72302ac6454SAndrew Thompson 	USETW2(req.wValue, type, index);
72402ac6454SAndrew Thompson 	USETW(req.wIndex, id);
72502ac6454SAndrew Thompson 
72602ac6454SAndrew Thompson 	while (1) {
72702ac6454SAndrew Thompson 
72802ac6454SAndrew Thompson 		if ((min_len < 2) || (max_len < 2)) {
72902ac6454SAndrew Thompson 			err = USB_ERR_INVAL;
73002ac6454SAndrew Thompson 			goto done;
73102ac6454SAndrew Thompson 		}
73202ac6454SAndrew Thompson 		USETW(req.wLength, min_len);
73302ac6454SAndrew Thompson 
734a593f6b8SAndrew Thompson 		err = usbd_do_request_flags(udev, mtx, &req,
73502ac6454SAndrew Thompson 		    desc, 0, NULL, 1000);
73602ac6454SAndrew Thompson 
73702ac6454SAndrew Thompson 		if (err) {
73802ac6454SAndrew Thompson 			if (!retries) {
73902ac6454SAndrew Thompson 				goto done;
74002ac6454SAndrew Thompson 			}
74102ac6454SAndrew Thompson 			retries--;
74202ac6454SAndrew Thompson 
743a593f6b8SAndrew Thompson 			usb_pause_mtx(mtx, hz / 5);
74402ac6454SAndrew Thompson 
74502ac6454SAndrew Thompson 			continue;
74602ac6454SAndrew Thompson 		}
74702ac6454SAndrew Thompson 		buf = desc;
74802ac6454SAndrew Thompson 
74902ac6454SAndrew Thompson 		if (min_len == max_len) {
75002ac6454SAndrew Thompson 
75116589beaSAndrew Thompson 			/* enforce correct length */
75216589beaSAndrew Thompson 			if ((buf[0] > min_len) && (actlen == NULL))
75302ac6454SAndrew Thompson 				buf[0] = min_len;
75416589beaSAndrew Thompson 
75516589beaSAndrew Thompson 			/* enforce correct type */
75602ac6454SAndrew Thompson 			buf[1] = type;
75702ac6454SAndrew Thompson 
75802ac6454SAndrew Thompson 			goto done;
75902ac6454SAndrew Thompson 		}
76002ac6454SAndrew Thompson 		/* range check */
76102ac6454SAndrew Thompson 
76202ac6454SAndrew Thompson 		if (max_len > buf[0]) {
76302ac6454SAndrew Thompson 			max_len = buf[0];
76402ac6454SAndrew Thompson 		}
76502ac6454SAndrew Thompson 		/* zero minimum data */
76602ac6454SAndrew Thompson 
76702ac6454SAndrew Thompson 		while (min_len > max_len) {
76802ac6454SAndrew Thompson 			min_len--;
76902ac6454SAndrew Thompson 			buf[min_len] = 0;
77002ac6454SAndrew Thompson 		}
77102ac6454SAndrew Thompson 
77202ac6454SAndrew Thompson 		/* set new minimum length */
77302ac6454SAndrew Thompson 
77402ac6454SAndrew Thompson 		min_len = max_len;
77502ac6454SAndrew Thompson 	}
77602ac6454SAndrew Thompson done:
77716589beaSAndrew Thompson 	if (actlen != NULL) {
77816589beaSAndrew Thompson 		if (err)
77916589beaSAndrew Thompson 			*actlen = 0;
78016589beaSAndrew Thompson 		else
78116589beaSAndrew Thompson 			*actlen = min_len;
78216589beaSAndrew Thompson 	}
78302ac6454SAndrew Thompson 	return (err);
78402ac6454SAndrew Thompson }
78502ac6454SAndrew Thompson 
78602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
787a593f6b8SAndrew Thompson  *	usbd_req_get_string_any
78802ac6454SAndrew Thompson  *
78902ac6454SAndrew Thompson  * This function will return the string given by "string_index"
79002ac6454SAndrew Thompson  * using the first language ID. The maximum length "len" includes
79102ac6454SAndrew Thompson  * the terminating zero. The "len" argument should be twice as
79202ac6454SAndrew Thompson  * big pluss 2 bytes, compared with the actual maximum string length !
79302ac6454SAndrew Thompson  *
79402ac6454SAndrew Thompson  * Returns:
79502ac6454SAndrew Thompson  *    0: Success
79602ac6454SAndrew Thompson  * Else: Failure
79702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
798e0a69b51SAndrew Thompson usb_error_t
799a593f6b8SAndrew Thompson usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf,
80002ac6454SAndrew Thompson     uint16_t len, uint8_t string_index)
80102ac6454SAndrew Thompson {
80202ac6454SAndrew Thompson 	char *s;
80302ac6454SAndrew Thompson 	uint8_t *temp;
80402ac6454SAndrew Thompson 	uint16_t i;
80502ac6454SAndrew Thompson 	uint16_t n;
80602ac6454SAndrew Thompson 	uint16_t c;
80702ac6454SAndrew Thompson 	uint8_t swap;
808e0a69b51SAndrew Thompson 	usb_error_t err;
80902ac6454SAndrew Thompson 
81002ac6454SAndrew Thompson 	if (len == 0) {
81102ac6454SAndrew Thompson 		/* should not happen */
81202ac6454SAndrew Thompson 		return (USB_ERR_NORMAL_COMPLETION);
81302ac6454SAndrew Thompson 	}
81402ac6454SAndrew Thompson 	if (string_index == 0) {
81502ac6454SAndrew Thompson 		/* this is the language table */
81602ac6454SAndrew Thompson 		buf[0] = 0;
81702ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
81802ac6454SAndrew Thompson 	}
81902ac6454SAndrew Thompson 	if (udev->flags.no_strings) {
82002ac6454SAndrew Thompson 		buf[0] = 0;
82102ac6454SAndrew Thompson 		return (USB_ERR_STALLED);
82202ac6454SAndrew Thompson 	}
823a593f6b8SAndrew Thompson 	err = usbd_req_get_string_desc
82402ac6454SAndrew Thompson 	    (udev, mtx, buf, len, udev->langid, string_index);
82502ac6454SAndrew Thompson 	if (err) {
82602ac6454SAndrew Thompson 		buf[0] = 0;
82702ac6454SAndrew Thompson 		return (err);
82802ac6454SAndrew Thompson 	}
82902ac6454SAndrew Thompson 	temp = (uint8_t *)buf;
83002ac6454SAndrew Thompson 
83102ac6454SAndrew Thompson 	if (temp[0] < 2) {
83202ac6454SAndrew Thompson 		/* string length is too short */
83302ac6454SAndrew Thompson 		buf[0] = 0;
83402ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
83502ac6454SAndrew Thompson 	}
83602ac6454SAndrew Thompson 	/* reserve one byte for terminating zero */
83702ac6454SAndrew Thompson 	len--;
83802ac6454SAndrew Thompson 
83902ac6454SAndrew Thompson 	/* find maximum length */
84002ac6454SAndrew Thompson 	s = buf;
84102ac6454SAndrew Thompson 	n = (temp[0] / 2) - 1;
84202ac6454SAndrew Thompson 	if (n > len) {
84302ac6454SAndrew Thompson 		n = len;
84402ac6454SAndrew Thompson 	}
84502ac6454SAndrew Thompson 	/* skip descriptor header */
84602ac6454SAndrew Thompson 	temp += 2;
84702ac6454SAndrew Thompson 
84802ac6454SAndrew Thompson 	/* reset swap state */
84902ac6454SAndrew Thompson 	swap = 3;
85002ac6454SAndrew Thompson 
85102ac6454SAndrew Thompson 	/* convert and filter */
85202ac6454SAndrew Thompson 	for (i = 0; (i != n); i++) {
85302ac6454SAndrew Thompson 		c = UGETW(temp + (2 * i));
85402ac6454SAndrew Thompson 
85502ac6454SAndrew Thompson 		/* convert from Unicode, handle buggy strings */
85602ac6454SAndrew Thompson 		if (((c & 0xff00) == 0) && (swap & 1)) {
85702ac6454SAndrew Thompson 			/* Little Endian, default */
85802ac6454SAndrew Thompson 			*s = c;
85902ac6454SAndrew Thompson 			swap = 1;
86002ac6454SAndrew Thompson 		} else if (((c & 0x00ff) == 0) && (swap & 2)) {
86102ac6454SAndrew Thompson 			/* Big Endian */
86202ac6454SAndrew Thompson 			*s = c >> 8;
86302ac6454SAndrew Thompson 			swap = 2;
86402ac6454SAndrew Thompson 		} else {
86502ac6454SAndrew Thompson 			/* silently skip bad character */
86602ac6454SAndrew Thompson 			continue;
86702ac6454SAndrew Thompson 		}
86802ac6454SAndrew Thompson 
86902ac6454SAndrew Thompson 		/*
87002ac6454SAndrew Thompson 		 * Filter by default - we don't allow greater and less than
87102ac6454SAndrew Thompson 		 * signs because they might confuse the dmesg printouts!
87202ac6454SAndrew Thompson 		 */
87302ac6454SAndrew Thompson 		if ((*s == '<') || (*s == '>') || (!isprint(*s))) {
87402ac6454SAndrew Thompson 			/* silently skip bad character */
87502ac6454SAndrew Thompson 			continue;
87602ac6454SAndrew Thompson 		}
87702ac6454SAndrew Thompson 		s++;
87802ac6454SAndrew Thompson 	}
87902ac6454SAndrew Thompson 	*s = 0;				/* zero terminate resulting string */
88002ac6454SAndrew Thompson 	return (USB_ERR_NORMAL_COMPLETION);
88102ac6454SAndrew Thompson }
88202ac6454SAndrew Thompson 
88302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
884a593f6b8SAndrew Thompson  *	usbd_req_get_string_desc
88502ac6454SAndrew Thompson  *
88602ac6454SAndrew Thompson  * If you don't know the language ID, consider using
887a593f6b8SAndrew Thompson  * "usbd_req_get_string_any()".
88802ac6454SAndrew Thompson  *
88902ac6454SAndrew Thompson  * Returns:
89002ac6454SAndrew Thompson  *    0: Success
89102ac6454SAndrew Thompson  * Else: Failure
89202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
893e0a69b51SAndrew Thompson usb_error_t
894a593f6b8SAndrew Thompson usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc,
89502ac6454SAndrew Thompson     uint16_t max_len, uint16_t lang_id,
89602ac6454SAndrew Thompson     uint8_t string_index)
89702ac6454SAndrew Thompson {
898a593f6b8SAndrew Thompson 	return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id,
89902ac6454SAndrew Thompson 	    UDESC_STRING, string_index, 0));
90002ac6454SAndrew Thompson }
90102ac6454SAndrew Thompson 
90202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
903a593f6b8SAndrew Thompson  *	usbd_req_get_config_desc_ptr
9047efaaa9aSAndrew Thompson  *
9057efaaa9aSAndrew Thompson  * This function is used in device side mode to retrieve the pointer
9067efaaa9aSAndrew Thompson  * to the generated config descriptor. This saves allocating space for
9077efaaa9aSAndrew Thompson  * an additional config descriptor when setting the configuration.
9087efaaa9aSAndrew Thompson  *
9097efaaa9aSAndrew Thompson  * Returns:
9107efaaa9aSAndrew Thompson  *    0: Success
9117efaaa9aSAndrew Thompson  * Else: Failure
9127efaaa9aSAndrew Thompson  *------------------------------------------------------------------------*/
913e0a69b51SAndrew Thompson usb_error_t
914a593f6b8SAndrew Thompson usbd_req_get_descriptor_ptr(struct usb_device *udev,
915760bc48eSAndrew Thompson     struct usb_config_descriptor **ppcd, uint16_t wValue)
9167efaaa9aSAndrew Thompson {
917760bc48eSAndrew Thompson 	struct usb_device_request req;
918e0a69b51SAndrew Thompson 	usb_handle_req_t *hr_func;
919459d369eSAndrew Thompson 	const void *ptr;
920459d369eSAndrew Thompson 	uint16_t len;
921e0a69b51SAndrew Thompson 	usb_error_t err;
9227efaaa9aSAndrew Thompson 
92363521bbcSAndrew Thompson 	req.bmRequestType = UT_READ_DEVICE;
9247efaaa9aSAndrew Thompson 	req.bRequest = UR_GET_DESCRIPTOR;
925459d369eSAndrew Thompson 	USETW(req.wValue, wValue);
9267efaaa9aSAndrew Thompson 	USETW(req.wIndex, 0);
9277efaaa9aSAndrew Thompson 	USETW(req.wLength, 0);
9287efaaa9aSAndrew Thompson 
929459d369eSAndrew Thompson 	ptr = NULL;
930459d369eSAndrew Thompson 	len = 0;
9317efaaa9aSAndrew Thompson 
932a593f6b8SAndrew Thompson 	hr_func = usbd_get_hr_func(udev);
933459d369eSAndrew Thompson 
934459d369eSAndrew Thompson 	if (hr_func == NULL)
935459d369eSAndrew Thompson 		err = USB_ERR_INVAL;
936459d369eSAndrew Thompson 	else {
937459d369eSAndrew Thompson 		USB_BUS_LOCK(udev->bus);
938459d369eSAndrew Thompson 		err = (hr_func) (udev, &req, &ptr, &len);
939459d369eSAndrew Thompson 		USB_BUS_UNLOCK(udev->bus);
940459d369eSAndrew Thompson 	}
941459d369eSAndrew Thompson 
942459d369eSAndrew Thompson 	if (err)
943459d369eSAndrew Thompson 		ptr = NULL;
944459d369eSAndrew Thompson 	else if (ptr == NULL)
945459d369eSAndrew Thompson 		err = USB_ERR_INVAL;
946459d369eSAndrew Thompson 
947760bc48eSAndrew Thompson 	*ppcd = __DECONST(struct usb_config_descriptor *, ptr);
948459d369eSAndrew Thompson 
949459d369eSAndrew Thompson 	return (err);
9507efaaa9aSAndrew Thompson }
9517efaaa9aSAndrew Thompson 
9527efaaa9aSAndrew Thompson /*------------------------------------------------------------------------*
953a593f6b8SAndrew Thompson  *	usbd_req_get_config_desc
95402ac6454SAndrew Thompson  *
95502ac6454SAndrew Thompson  * Returns:
95602ac6454SAndrew Thompson  *    0: Success
95702ac6454SAndrew Thompson  * Else: Failure
95802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
959e0a69b51SAndrew Thompson usb_error_t
960a593f6b8SAndrew Thompson usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx,
961760bc48eSAndrew Thompson     struct usb_config_descriptor *d, uint8_t conf_index)
96202ac6454SAndrew Thompson {
963e0a69b51SAndrew Thompson 	usb_error_t err;
96402ac6454SAndrew Thompson 
96502ac6454SAndrew Thompson 	DPRINTFN(4, "confidx=%d\n", conf_index);
96602ac6454SAndrew Thompson 
967a593f6b8SAndrew Thompson 	err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d),
96802ac6454SAndrew Thompson 	    sizeof(*d), 0, UDESC_CONFIG, conf_index, 0);
96902ac6454SAndrew Thompson 	if (err) {
97002ac6454SAndrew Thompson 		goto done;
97102ac6454SAndrew Thompson 	}
97202ac6454SAndrew Thompson 	/* Extra sanity checking */
97302ac6454SAndrew Thompson 	if (UGETW(d->wTotalLength) < sizeof(*d)) {
97402ac6454SAndrew Thompson 		err = USB_ERR_INVAL;
97502ac6454SAndrew Thompson 	}
97602ac6454SAndrew Thompson done:
97702ac6454SAndrew Thompson 	return (err);
97802ac6454SAndrew Thompson }
97902ac6454SAndrew Thompson 
98002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
981a593f6b8SAndrew Thompson  *	usbd_req_get_config_desc_full
98202ac6454SAndrew Thompson  *
98302ac6454SAndrew Thompson  * This function gets the complete USB configuration descriptor and
98402ac6454SAndrew Thompson  * ensures that "wTotalLength" is correct.
98502ac6454SAndrew Thompson  *
98602ac6454SAndrew Thompson  * Returns:
98702ac6454SAndrew Thompson  *    0: Success
98802ac6454SAndrew Thompson  * Else: Failure
98902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
990e0a69b51SAndrew Thompson usb_error_t
991a593f6b8SAndrew Thompson usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx,
992760bc48eSAndrew Thompson     struct usb_config_descriptor **ppcd, struct malloc_type *mtype,
99302ac6454SAndrew Thompson     uint8_t index)
99402ac6454SAndrew Thompson {
995760bc48eSAndrew Thompson 	struct usb_config_descriptor cd;
996760bc48eSAndrew Thompson 	struct usb_config_descriptor *cdesc;
99702ac6454SAndrew Thompson 	uint16_t len;
998e0a69b51SAndrew Thompson 	usb_error_t err;
99902ac6454SAndrew Thompson 
100002ac6454SAndrew Thompson 	DPRINTFN(4, "index=%d\n", index);
100102ac6454SAndrew Thompson 
100202ac6454SAndrew Thompson 	*ppcd = NULL;
100302ac6454SAndrew Thompson 
1004a593f6b8SAndrew Thompson 	err = usbd_req_get_config_desc(udev, mtx, &cd, index);
100502ac6454SAndrew Thompson 	if (err) {
100602ac6454SAndrew Thompson 		return (err);
100702ac6454SAndrew Thompson 	}
100802ac6454SAndrew Thompson 	/* get full descriptor */
100902ac6454SAndrew Thompson 	len = UGETW(cd.wTotalLength);
101002ac6454SAndrew Thompson 	if (len < sizeof(*cdesc)) {
101102ac6454SAndrew Thompson 		/* corrupt descriptor */
101202ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
101302ac6454SAndrew Thompson 	}
101402ac6454SAndrew Thompson 	cdesc = malloc(len, mtype, M_WAITOK);
101502ac6454SAndrew Thompson 	if (cdesc == NULL) {
101602ac6454SAndrew Thompson 		return (USB_ERR_NOMEM);
101702ac6454SAndrew Thompson 	}
1018a593f6b8SAndrew Thompson 	err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0,
101902ac6454SAndrew Thompson 	    UDESC_CONFIG, index, 3);
102002ac6454SAndrew Thompson 	if (err) {
102102ac6454SAndrew Thompson 		free(cdesc, mtype);
102202ac6454SAndrew Thompson 		return (err);
102302ac6454SAndrew Thompson 	}
102402ac6454SAndrew Thompson 	/* make sure that the device is not fooling us: */
102502ac6454SAndrew Thompson 	USETW(cdesc->wTotalLength, len);
102602ac6454SAndrew Thompson 
102702ac6454SAndrew Thompson 	*ppcd = cdesc;
102802ac6454SAndrew Thompson 
102902ac6454SAndrew Thompson 	return (0);			/* success */
103002ac6454SAndrew Thompson }
103102ac6454SAndrew Thompson 
103202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1033a593f6b8SAndrew Thompson  *	usbd_req_get_device_desc
103402ac6454SAndrew Thompson  *
103502ac6454SAndrew Thompson  * Returns:
103602ac6454SAndrew Thompson  *    0: Success
103702ac6454SAndrew Thompson  * Else: Failure
103802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1039e0a69b51SAndrew Thompson usb_error_t
1040a593f6b8SAndrew Thompson usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx,
1041760bc48eSAndrew Thompson     struct usb_device_descriptor *d)
104202ac6454SAndrew Thompson {
104302ac6454SAndrew Thompson 	DPRINTFN(4, "\n");
1044a593f6b8SAndrew Thompson 	return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d),
104502ac6454SAndrew Thompson 	    sizeof(*d), 0, UDESC_DEVICE, 0, 3));
104602ac6454SAndrew Thompson }
104702ac6454SAndrew Thompson 
104802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1049a593f6b8SAndrew Thompson  *	usbd_req_get_alt_interface_no
105002ac6454SAndrew Thompson  *
105102ac6454SAndrew Thompson  * Returns:
105202ac6454SAndrew Thompson  *    0: Success
105302ac6454SAndrew Thompson  * Else: Failure
105402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1055e0a69b51SAndrew Thompson usb_error_t
1056a593f6b8SAndrew Thompson usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
105702ac6454SAndrew Thompson     uint8_t *alt_iface_no, uint8_t iface_index)
105802ac6454SAndrew Thompson {
1059a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1060760bc48eSAndrew Thompson 	struct usb_device_request req;
106102ac6454SAndrew Thompson 
1062bd73b187SAlfred Perlstein 	if ((iface == NULL) || (iface->idesc == NULL))
106302ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
1064bd73b187SAlfred Perlstein 
106502ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_INTERFACE;
106602ac6454SAndrew Thompson 	req.bRequest = UR_GET_INTERFACE;
106702ac6454SAndrew Thompson 	USETW(req.wValue, 0);
106802ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
106902ac6454SAndrew Thompson 	req.wIndex[1] = 0;
107002ac6454SAndrew Thompson 	USETW(req.wLength, 1);
1071a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, alt_iface_no));
107202ac6454SAndrew Thompson }
107302ac6454SAndrew Thompson 
107402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1075a593f6b8SAndrew Thompson  *	usbd_req_set_alt_interface_no
107602ac6454SAndrew Thompson  *
107702ac6454SAndrew Thompson  * Returns:
107802ac6454SAndrew Thompson  *    0: Success
107902ac6454SAndrew Thompson  * Else: Failure
108002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1081e0a69b51SAndrew Thompson usb_error_t
1082a593f6b8SAndrew Thompson usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
108302ac6454SAndrew Thompson     uint8_t iface_index, uint8_t alt_no)
108402ac6454SAndrew Thompson {
1085a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1086760bc48eSAndrew Thompson 	struct usb_device_request req;
108702ac6454SAndrew Thompson 
1088bd73b187SAlfred Perlstein 	if ((iface == NULL) || (iface->idesc == NULL))
108902ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
1090bd73b187SAlfred Perlstein 
109102ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_INTERFACE;
109202ac6454SAndrew Thompson 	req.bRequest = UR_SET_INTERFACE;
109302ac6454SAndrew Thompson 	req.wValue[0] = alt_no;
109402ac6454SAndrew Thompson 	req.wValue[1] = 0;
109502ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
109602ac6454SAndrew Thompson 	req.wIndex[1] = 0;
109702ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1098a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
109902ac6454SAndrew Thompson }
110002ac6454SAndrew Thompson 
110102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1102a593f6b8SAndrew Thompson  *	usbd_req_get_device_status
110302ac6454SAndrew Thompson  *
110402ac6454SAndrew Thompson  * Returns:
110502ac6454SAndrew Thompson  *    0: Success
110602ac6454SAndrew Thompson  * Else: Failure
110702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1108e0a69b51SAndrew Thompson usb_error_t
1109a593f6b8SAndrew Thompson usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx,
1110760bc48eSAndrew Thompson     struct usb_status *st)
111102ac6454SAndrew Thompson {
1112760bc48eSAndrew Thompson 	struct usb_device_request req;
111302ac6454SAndrew Thompson 
111402ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_DEVICE;
111502ac6454SAndrew Thompson 	req.bRequest = UR_GET_STATUS;
111602ac6454SAndrew Thompson 	USETW(req.wValue, 0);
111702ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
111802ac6454SAndrew Thompson 	USETW(req.wLength, sizeof(*st));
1119a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, st));
112002ac6454SAndrew Thompson }
112102ac6454SAndrew Thompson 
112202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1123a593f6b8SAndrew Thompson  *	usbd_req_get_hub_descriptor
112402ac6454SAndrew Thompson  *
112502ac6454SAndrew Thompson  * Returns:
112602ac6454SAndrew Thompson  *    0: Success
112702ac6454SAndrew Thompson  * Else: Failure
112802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1129e0a69b51SAndrew Thompson usb_error_t
1130a593f6b8SAndrew Thompson usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx,
1131760bc48eSAndrew Thompson     struct usb_hub_descriptor *hd, uint8_t nports)
113202ac6454SAndrew Thompson {
1133760bc48eSAndrew Thompson 	struct usb_device_request req;
113402ac6454SAndrew Thompson 	uint16_t len = (nports + 7 + (8 * 8)) / 8;
113502ac6454SAndrew Thompson 
113602ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_CLASS_DEVICE;
113702ac6454SAndrew Thompson 	req.bRequest = UR_GET_DESCRIPTOR;
113802ac6454SAndrew Thompson 	USETW2(req.wValue, UDESC_HUB, 0);
113902ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
114002ac6454SAndrew Thompson 	USETW(req.wLength, len);
1141a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, hd));
114202ac6454SAndrew Thompson }
114302ac6454SAndrew Thompson 
114402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1145a593f6b8SAndrew Thompson  *	usbd_req_get_hub_status
114602ac6454SAndrew Thompson  *
114702ac6454SAndrew Thompson  * Returns:
114802ac6454SAndrew Thompson  *    0: Success
114902ac6454SAndrew Thompson  * Else: Failure
115002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1151e0a69b51SAndrew Thompson usb_error_t
1152a593f6b8SAndrew Thompson usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx,
1153760bc48eSAndrew Thompson     struct usb_hub_status *st)
115402ac6454SAndrew Thompson {
1155760bc48eSAndrew Thompson 	struct usb_device_request req;
115602ac6454SAndrew Thompson 
115702ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_CLASS_DEVICE;
115802ac6454SAndrew Thompson 	req.bRequest = UR_GET_STATUS;
115902ac6454SAndrew Thompson 	USETW(req.wValue, 0);
116002ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
1161760bc48eSAndrew Thompson 	USETW(req.wLength, sizeof(struct usb_hub_status));
1162a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, st));
116302ac6454SAndrew Thompson }
116402ac6454SAndrew Thompson 
116502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1166a593f6b8SAndrew Thompson  *	usbd_req_set_address
116702ac6454SAndrew Thompson  *
116802ac6454SAndrew Thompson  * This function is used to set the address for an USB device. After
116902ac6454SAndrew Thompson  * port reset the USB device will respond at address zero.
117002ac6454SAndrew Thompson  *
117102ac6454SAndrew Thompson  * Returns:
117202ac6454SAndrew Thompson  *    0: Success
117302ac6454SAndrew Thompson  * Else: Failure
117402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1175e0a69b51SAndrew Thompson usb_error_t
1176a593f6b8SAndrew Thompson usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr)
117702ac6454SAndrew Thompson {
1178760bc48eSAndrew Thompson 	struct usb_device_request req;
117902ac6454SAndrew Thompson 
118002ac6454SAndrew Thompson 	DPRINTFN(6, "setting device address=%d\n", addr);
118102ac6454SAndrew Thompson 
118202ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_DEVICE;
118302ac6454SAndrew Thompson 	req.bRequest = UR_SET_ADDRESS;
118402ac6454SAndrew Thompson 	USETW(req.wValue, addr);
118502ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
118602ac6454SAndrew Thompson 	USETW(req.wLength, 0);
118702ac6454SAndrew Thompson 
118802ac6454SAndrew Thompson 	/* Setting the address should not take more than 1 second ! */
1189a593f6b8SAndrew Thompson 	return (usbd_do_request_flags(udev, mtx, &req, NULL,
119002ac6454SAndrew Thompson 	    USB_DELAY_STATUS_STAGE, NULL, 1000));
119102ac6454SAndrew Thompson }
119202ac6454SAndrew Thompson 
119302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1194a593f6b8SAndrew Thompson  *	usbd_req_get_port_status
119502ac6454SAndrew Thompson  *
119602ac6454SAndrew Thompson  * Returns:
119702ac6454SAndrew Thompson  *    0: Success
119802ac6454SAndrew Thompson  * Else: Failure
119902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1200e0a69b51SAndrew Thompson usb_error_t
1201a593f6b8SAndrew Thompson usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx,
1202760bc48eSAndrew Thompson     struct usb_port_status *ps, uint8_t port)
120302ac6454SAndrew Thompson {
1204760bc48eSAndrew Thompson 	struct usb_device_request req;
120502ac6454SAndrew Thompson 
120602ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_CLASS_OTHER;
120702ac6454SAndrew Thompson 	req.bRequest = UR_GET_STATUS;
120802ac6454SAndrew Thompson 	USETW(req.wValue, 0);
120902ac6454SAndrew Thompson 	req.wIndex[0] = port;
121002ac6454SAndrew Thompson 	req.wIndex[1] = 0;
121102ac6454SAndrew Thompson 	USETW(req.wLength, sizeof *ps);
1212a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, ps));
121302ac6454SAndrew Thompson }
121402ac6454SAndrew Thompson 
121502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1216a593f6b8SAndrew Thompson  *	usbd_req_clear_hub_feature
121702ac6454SAndrew Thompson  *
121802ac6454SAndrew Thompson  * Returns:
121902ac6454SAndrew Thompson  *    0: Success
122002ac6454SAndrew Thompson  * Else: Failure
122102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1222e0a69b51SAndrew Thompson usb_error_t
1223a593f6b8SAndrew Thompson usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx,
122402ac6454SAndrew Thompson     uint16_t sel)
122502ac6454SAndrew Thompson {
1226760bc48eSAndrew Thompson 	struct usb_device_request req;
122702ac6454SAndrew Thompson 
122802ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
122902ac6454SAndrew Thompson 	req.bRequest = UR_CLEAR_FEATURE;
123002ac6454SAndrew Thompson 	USETW(req.wValue, sel);
123102ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
123202ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1233a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
123402ac6454SAndrew Thompson }
123502ac6454SAndrew Thompson 
123602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1237a593f6b8SAndrew Thompson  *	usbd_req_set_hub_feature
123802ac6454SAndrew Thompson  *
123902ac6454SAndrew Thompson  * Returns:
124002ac6454SAndrew Thompson  *    0: Success
124102ac6454SAndrew Thompson  * Else: Failure
124202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1243e0a69b51SAndrew Thompson usb_error_t
1244a593f6b8SAndrew Thompson usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx,
124502ac6454SAndrew Thompson     uint16_t sel)
124602ac6454SAndrew Thompson {
1247760bc48eSAndrew Thompson 	struct usb_device_request req;
124802ac6454SAndrew Thompson 
124902ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
125002ac6454SAndrew Thompson 	req.bRequest = UR_SET_FEATURE;
125102ac6454SAndrew Thompson 	USETW(req.wValue, sel);
125202ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
125302ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1254a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
125502ac6454SAndrew Thompson }
125602ac6454SAndrew Thompson 
125702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1258a593f6b8SAndrew Thompson  *	usbd_req_clear_port_feature
125902ac6454SAndrew Thompson  *
126002ac6454SAndrew Thompson  * Returns:
126102ac6454SAndrew Thompson  *    0: Success
126202ac6454SAndrew Thompson  * Else: Failure
126302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1264e0a69b51SAndrew Thompson usb_error_t
1265a593f6b8SAndrew Thompson usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx,
126602ac6454SAndrew Thompson     uint8_t port, uint16_t sel)
126702ac6454SAndrew Thompson {
1268760bc48eSAndrew Thompson 	struct usb_device_request req;
126902ac6454SAndrew Thompson 
127002ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
127102ac6454SAndrew Thompson 	req.bRequest = UR_CLEAR_FEATURE;
127202ac6454SAndrew Thompson 	USETW(req.wValue, sel);
127302ac6454SAndrew Thompson 	req.wIndex[0] = port;
127402ac6454SAndrew Thompson 	req.wIndex[1] = 0;
127502ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1276a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
127702ac6454SAndrew Thompson }
127802ac6454SAndrew Thompson 
127902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1280a593f6b8SAndrew Thompson  *	usbd_req_set_port_feature
128102ac6454SAndrew Thompson  *
128202ac6454SAndrew Thompson  * Returns:
128302ac6454SAndrew Thompson  *    0: Success
128402ac6454SAndrew Thompson  * Else: Failure
128502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1286e0a69b51SAndrew Thompson usb_error_t
1287a593f6b8SAndrew Thompson usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx,
128802ac6454SAndrew Thompson     uint8_t port, uint16_t sel)
128902ac6454SAndrew Thompson {
1290760bc48eSAndrew Thompson 	struct usb_device_request req;
129102ac6454SAndrew Thompson 
129202ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
129302ac6454SAndrew Thompson 	req.bRequest = UR_SET_FEATURE;
129402ac6454SAndrew Thompson 	USETW(req.wValue, sel);
129502ac6454SAndrew Thompson 	req.wIndex[0] = port;
129602ac6454SAndrew Thompson 	req.wIndex[1] = 0;
129702ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1298a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
129902ac6454SAndrew Thompson }
130002ac6454SAndrew Thompson 
130102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1302a593f6b8SAndrew Thompson  *	usbd_req_set_protocol
130302ac6454SAndrew Thompson  *
130402ac6454SAndrew Thompson  * Returns:
130502ac6454SAndrew Thompson  *    0: Success
130602ac6454SAndrew Thompson  * Else: Failure
130702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1308e0a69b51SAndrew Thompson usb_error_t
1309a593f6b8SAndrew Thompson usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx,
131002ac6454SAndrew Thompson     uint8_t iface_index, uint16_t report)
131102ac6454SAndrew Thompson {
1312a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1313760bc48eSAndrew Thompson 	struct usb_device_request req;
131402ac6454SAndrew Thompson 
131502ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
131602ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
131702ac6454SAndrew Thompson 	}
131802ac6454SAndrew Thompson 	DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n",
131902ac6454SAndrew Thompson 	    iface, report, iface->idesc->bInterfaceNumber);
132002ac6454SAndrew Thompson 
132102ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
132202ac6454SAndrew Thompson 	req.bRequest = UR_SET_PROTOCOL;
132302ac6454SAndrew Thompson 	USETW(req.wValue, report);
132402ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
132502ac6454SAndrew Thompson 	req.wIndex[1] = 0;
132602ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1327a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
132802ac6454SAndrew Thompson }
132902ac6454SAndrew Thompson 
133002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1331a593f6b8SAndrew Thompson  *	usbd_req_set_report
133202ac6454SAndrew Thompson  *
133302ac6454SAndrew Thompson  * Returns:
133402ac6454SAndrew Thompson  *    0: Success
133502ac6454SAndrew Thompson  * Else: Failure
133602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1337e0a69b51SAndrew Thompson usb_error_t
1338a593f6b8SAndrew Thompson usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len,
133902ac6454SAndrew Thompson     uint8_t iface_index, uint8_t type, uint8_t id)
134002ac6454SAndrew Thompson {
1341a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1342760bc48eSAndrew Thompson 	struct usb_device_request req;
134302ac6454SAndrew Thompson 
134402ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
134502ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
134602ac6454SAndrew Thompson 	}
134702ac6454SAndrew Thompson 	DPRINTFN(5, "len=%d\n", len);
134802ac6454SAndrew Thompson 
134902ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
135002ac6454SAndrew Thompson 	req.bRequest = UR_SET_REPORT;
135102ac6454SAndrew Thompson 	USETW2(req.wValue, type, id);
135202ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
135302ac6454SAndrew Thompson 	req.wIndex[1] = 0;
135402ac6454SAndrew Thompson 	USETW(req.wLength, len);
1355a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, data));
135602ac6454SAndrew Thompson }
135702ac6454SAndrew Thompson 
135802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1359a593f6b8SAndrew Thompson  *	usbd_req_get_report
136002ac6454SAndrew Thompson  *
136102ac6454SAndrew Thompson  * Returns:
136202ac6454SAndrew Thompson  *    0: Success
136302ac6454SAndrew Thompson  * Else: Failure
136402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1365e0a69b51SAndrew Thompson usb_error_t
1366a593f6b8SAndrew Thompson usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data,
136702ac6454SAndrew Thompson     uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id)
136802ac6454SAndrew Thompson {
1369a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1370760bc48eSAndrew Thompson 	struct usb_device_request req;
137102ac6454SAndrew Thompson 
137202ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL) || (id == 0)) {
137302ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
137402ac6454SAndrew Thompson 	}
137502ac6454SAndrew Thompson 	DPRINTFN(5, "len=%d\n", len);
137602ac6454SAndrew Thompson 
137702ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
137802ac6454SAndrew Thompson 	req.bRequest = UR_GET_REPORT;
137902ac6454SAndrew Thompson 	USETW2(req.wValue, type, id);
138002ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
138102ac6454SAndrew Thompson 	req.wIndex[1] = 0;
138202ac6454SAndrew Thompson 	USETW(req.wLength, len);
1383a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, data));
138402ac6454SAndrew Thompson }
138502ac6454SAndrew Thompson 
138602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1387a593f6b8SAndrew Thompson  *	usbd_req_set_idle
138802ac6454SAndrew Thompson  *
138902ac6454SAndrew Thompson  * Returns:
139002ac6454SAndrew Thompson  *    0: Success
139102ac6454SAndrew Thompson  * Else: Failure
139202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1393e0a69b51SAndrew Thompson usb_error_t
1394a593f6b8SAndrew Thompson usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx,
139502ac6454SAndrew Thompson     uint8_t iface_index, uint8_t duration, uint8_t id)
139602ac6454SAndrew Thompson {
1397a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1398760bc48eSAndrew Thompson 	struct usb_device_request req;
139902ac6454SAndrew Thompson 
140002ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
140102ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
140202ac6454SAndrew Thompson 	}
140302ac6454SAndrew Thompson 	DPRINTFN(5, "%d %d\n", duration, id);
140402ac6454SAndrew Thompson 
140502ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
140602ac6454SAndrew Thompson 	req.bRequest = UR_SET_IDLE;
140702ac6454SAndrew Thompson 	USETW2(req.wValue, duration, id);
140802ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
140902ac6454SAndrew Thompson 	req.wIndex[1] = 0;
141002ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1411a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
141202ac6454SAndrew Thompson }
141302ac6454SAndrew Thompson 
141402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1415a593f6b8SAndrew Thompson  *	usbd_req_get_report_descriptor
141602ac6454SAndrew Thompson  *
141702ac6454SAndrew Thompson  * Returns:
141802ac6454SAndrew Thompson  *    0: Success
141902ac6454SAndrew Thompson  * Else: Failure
142002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1421e0a69b51SAndrew Thompson usb_error_t
1422a593f6b8SAndrew Thompson usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx,
142302ac6454SAndrew Thompson     void *d, uint16_t size, uint8_t iface_index)
142402ac6454SAndrew Thompson {
1425a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1426760bc48eSAndrew Thompson 	struct usb_device_request req;
142702ac6454SAndrew Thompson 
142802ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
142902ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
143002ac6454SAndrew Thompson 	}
143102ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_INTERFACE;
143202ac6454SAndrew Thompson 	req.bRequest = UR_GET_DESCRIPTOR;
143302ac6454SAndrew Thompson 	USETW2(req.wValue, UDESC_REPORT, 0);	/* report id should be 0 */
143402ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
143502ac6454SAndrew Thompson 	req.wIndex[1] = 0;
143602ac6454SAndrew Thompson 	USETW(req.wLength, size);
1437a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, d));
143802ac6454SAndrew Thompson }
143902ac6454SAndrew Thompson 
144002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1441a593f6b8SAndrew Thompson  *	usbd_req_set_config
144202ac6454SAndrew Thompson  *
144302ac6454SAndrew Thompson  * This function is used to select the current configuration number in
144402ac6454SAndrew Thompson  * both USB device side mode and USB host side mode. When setting the
144502ac6454SAndrew Thompson  * configuration the function of the interfaces can change.
144602ac6454SAndrew Thompson  *
144702ac6454SAndrew Thompson  * Returns:
144802ac6454SAndrew Thompson  *    0: Success
144902ac6454SAndrew Thompson  * Else: Failure
145002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1451e0a69b51SAndrew Thompson usb_error_t
1452a593f6b8SAndrew Thompson usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf)
145302ac6454SAndrew Thompson {
1454760bc48eSAndrew Thompson 	struct usb_device_request req;
145502ac6454SAndrew Thompson 
145602ac6454SAndrew Thompson 	DPRINTF("setting config %d\n", conf);
145702ac6454SAndrew Thompson 
145802ac6454SAndrew Thompson 	/* do "set configuration" request */
145902ac6454SAndrew Thompson 
146002ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_DEVICE;
146102ac6454SAndrew Thompson 	req.bRequest = UR_SET_CONFIG;
146202ac6454SAndrew Thompson 	req.wValue[0] = conf;
146302ac6454SAndrew Thompson 	req.wValue[1] = 0;
146402ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
146502ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1466a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
146702ac6454SAndrew Thompson }
146802ac6454SAndrew Thompson 
146902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1470a593f6b8SAndrew Thompson  *	usbd_req_get_config
147102ac6454SAndrew Thompson  *
147202ac6454SAndrew Thompson  * Returns:
147302ac6454SAndrew Thompson  *    0: Success
147402ac6454SAndrew Thompson  * Else: Failure
147502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1476e0a69b51SAndrew Thompson usb_error_t
1477a593f6b8SAndrew Thompson usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf)
147802ac6454SAndrew Thompson {
1479760bc48eSAndrew Thompson 	struct usb_device_request req;
148002ac6454SAndrew Thompson 
148102ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_DEVICE;
148202ac6454SAndrew Thompson 	req.bRequest = UR_GET_CONFIG;
148302ac6454SAndrew Thompson 	USETW(req.wValue, 0);
148402ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
148502ac6454SAndrew Thompson 	USETW(req.wLength, 1);
1486a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, pconf));
148702ac6454SAndrew Thompson }
148802ac6454SAndrew Thompson 
148902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1490a593f6b8SAndrew Thompson  *	usbd_req_re_enumerate
149102ac6454SAndrew Thompson  *
149202ac6454SAndrew Thompson  * NOTE: After this function returns the hardware is in the
149302ac6454SAndrew Thompson  * unconfigured state! The application is responsible for setting a
149402ac6454SAndrew Thompson  * new configuration.
149502ac6454SAndrew Thompson  *
149602ac6454SAndrew Thompson  * Returns:
149702ac6454SAndrew Thompson  *    0: Success
149802ac6454SAndrew Thompson  * Else: Failure
149902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1500e0a69b51SAndrew Thompson usb_error_t
1501a593f6b8SAndrew Thompson usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
150202ac6454SAndrew Thompson {
1503760bc48eSAndrew Thompson 	struct usb_device *parent_hub;
1504e0a69b51SAndrew Thompson 	usb_error_t err;
150502ac6454SAndrew Thompson 	uint8_t old_addr;
150602ac6454SAndrew Thompson 	uint8_t do_retry = 1;
150702ac6454SAndrew Thompson 
1508f29a0724SAndrew Thompson 	if (udev->flags.usb_mode != USB_MODE_HOST) {
150902ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
151002ac6454SAndrew Thompson 	}
151102ac6454SAndrew Thompson 	old_addr = udev->address;
151202ac6454SAndrew Thompson 	parent_hub = udev->parent_hub;
151302ac6454SAndrew Thompson 	if (parent_hub == NULL) {
151402ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
151502ac6454SAndrew Thompson 	}
151602ac6454SAndrew Thompson retry:
1517a593f6b8SAndrew Thompson 	err = usbd_req_reset_port(parent_hub, mtx, udev->port_no);
151802ac6454SAndrew Thompson 	if (err) {
151903797f33SAndrew Thompson 		DPRINTFN(0, "addr=%d, port reset failed, %s\n",
1520a593f6b8SAndrew Thompson 		    old_addr, usbd_errstr(err));
152102ac6454SAndrew Thompson 		goto done;
152202ac6454SAndrew Thompson 	}
152302ac6454SAndrew Thompson 	/*
152402ac6454SAndrew Thompson 	 * After that the port has been reset our device should be at
152502ac6454SAndrew Thompson 	 * address zero:
152602ac6454SAndrew Thompson 	 */
152702ac6454SAndrew Thompson 	udev->address = USB_START_ADDR;
152802ac6454SAndrew Thompson 
152902ac6454SAndrew Thompson 	/* reset "bMaxPacketSize" */
153002ac6454SAndrew Thompson 	udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET;
153102ac6454SAndrew Thompson 
153202ac6454SAndrew Thompson 	/*
153302ac6454SAndrew Thompson 	 * Restore device address:
153402ac6454SAndrew Thompson 	 */
1535a593f6b8SAndrew Thompson 	err = usbd_req_set_address(udev, mtx, old_addr);
153602ac6454SAndrew Thompson 	if (err) {
153702ac6454SAndrew Thompson 		/* XXX ignore any errors! */
153803797f33SAndrew Thompson 		DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n",
1539a593f6b8SAndrew Thompson 		    old_addr, usbd_errstr(err));
154002ac6454SAndrew Thompson 	}
154102ac6454SAndrew Thompson 	/* restore device address */
154202ac6454SAndrew Thompson 	udev->address = old_addr;
154302ac6454SAndrew Thompson 
154402ac6454SAndrew Thompson 	/* allow device time to set new address */
1545a593f6b8SAndrew Thompson 	usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE));
154602ac6454SAndrew Thompson 
154702ac6454SAndrew Thompson 	/* get the device descriptor */
1548a593f6b8SAndrew Thompson 	err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc,
154902ac6454SAndrew Thompson 	    USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
155002ac6454SAndrew Thompson 	if (err) {
155102ac6454SAndrew Thompson 		DPRINTFN(0, "getting device descriptor "
155203797f33SAndrew Thompson 		    "at addr %d failed, %s!\n", udev->address,
1553a593f6b8SAndrew Thompson 		    usbd_errstr(err));
155402ac6454SAndrew Thompson 		goto done;
155502ac6454SAndrew Thompson 	}
155602ac6454SAndrew Thompson 	/* get the full device descriptor */
1557a593f6b8SAndrew Thompson 	err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
155802ac6454SAndrew Thompson 	if (err) {
155902ac6454SAndrew Thompson 		DPRINTFN(0, "addr=%d, getting device "
156003797f33SAndrew Thompson 		    "descriptor failed, %s!\n", old_addr,
1561a593f6b8SAndrew Thompson 		    usbd_errstr(err));
156202ac6454SAndrew Thompson 		goto done;
156302ac6454SAndrew Thompson 	}
156402ac6454SAndrew Thompson done:
156502ac6454SAndrew Thompson 	if (err && do_retry) {
156602ac6454SAndrew Thompson 		/* give the USB firmware some time to load */
1567a593f6b8SAndrew Thompson 		usb_pause_mtx(mtx, hz / 2);
156802ac6454SAndrew Thompson 		/* no more retries after this retry */
156902ac6454SAndrew Thompson 		do_retry = 0;
157002ac6454SAndrew Thompson 		/* try again */
157102ac6454SAndrew Thompson 		goto retry;
157202ac6454SAndrew Thompson 	}
157302ac6454SAndrew Thompson 	/* restore address */
157402ac6454SAndrew Thompson 	udev->address = old_addr;
157502ac6454SAndrew Thompson 	return (err);
157602ac6454SAndrew Thompson }
157702ac6454SAndrew Thompson 
157802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1579a593f6b8SAndrew Thompson  *	usbd_req_clear_device_feature
158002ac6454SAndrew Thompson  *
158102ac6454SAndrew Thompson  * Returns:
158202ac6454SAndrew Thompson  *    0: Success
158302ac6454SAndrew Thompson  * Else: Failure
158402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1585e0a69b51SAndrew Thompson usb_error_t
1586a593f6b8SAndrew Thompson usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx,
158702ac6454SAndrew Thompson     uint16_t sel)
158802ac6454SAndrew Thompson {
1589760bc48eSAndrew Thompson 	struct usb_device_request req;
159002ac6454SAndrew Thompson 
159102ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_DEVICE;
159202ac6454SAndrew Thompson 	req.bRequest = UR_CLEAR_FEATURE;
159302ac6454SAndrew Thompson 	USETW(req.wValue, sel);
159402ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
159502ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1596a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
159702ac6454SAndrew Thompson }
159802ac6454SAndrew Thompson 
159902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1600a593f6b8SAndrew Thompson  *	usbd_req_set_device_feature
160102ac6454SAndrew Thompson  *
160202ac6454SAndrew Thompson  * Returns:
160302ac6454SAndrew Thompson  *    0: Success
160402ac6454SAndrew Thompson  * Else: Failure
160502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1606e0a69b51SAndrew Thompson usb_error_t
1607a593f6b8SAndrew Thompson usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx,
160802ac6454SAndrew Thompson     uint16_t sel)
160902ac6454SAndrew Thompson {
1610760bc48eSAndrew Thompson 	struct usb_device_request req;
161102ac6454SAndrew Thompson 
161202ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_DEVICE;
161302ac6454SAndrew Thompson 	req.bRequest = UR_SET_FEATURE;
161402ac6454SAndrew Thompson 	USETW(req.wValue, sel);
161502ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
161602ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1617a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
161802ac6454SAndrew Thompson }
1619