xref: /freebsd/sys/dev/usb/usb_request.c (revision 0e777d84572d1c3e216f75d960f4957ae5fa5148)
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 
71b850ecc1SAndrew Thompson #ifdef 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;
7402ac6454SAndrew Thompson 
759360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_poll_delay, CTLFLAG_RW,
76a593f6b8SAndrew Thompson     &usb_pr_poll_delay, 0, "USB port reset poll delay in ms");
779360ae40SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, pr_recovery_delay, CTLFLAG_RW,
78a593f6b8SAndrew Thompson     &usb_pr_recovery_delay, 0, "USB port reset recovery delay in ms");
79f6980be8SAndrew Thompson 
80f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
81f6980be8SAndrew Thompson /* The following structures are used in connection to fault injection. */
82f6980be8SAndrew Thompson struct usb_ctrl_debug {
83f6980be8SAndrew Thompson 	int bus_index;		/* target bus */
84f6980be8SAndrew Thompson 	int dev_index;		/* target address */
85f6980be8SAndrew Thompson 	int ds_fail;		/* fail data stage */
86f6980be8SAndrew Thompson 	int ss_fail;		/* fail data stage */
87f6980be8SAndrew Thompson 	int ds_delay;		/* data stage delay in ms */
88f6980be8SAndrew Thompson 	int ss_delay;		/* status stage delay in ms */
89f6980be8SAndrew Thompson 	int bmRequestType_value;
90f6980be8SAndrew Thompson 	int bRequest_value;
91f6980be8SAndrew Thompson };
92f6980be8SAndrew Thompson 
93f6980be8SAndrew Thompson struct usb_ctrl_debug_bits {
94f6980be8SAndrew Thompson 	uint16_t ds_delay;
95f6980be8SAndrew Thompson 	uint16_t ss_delay;
96f6980be8SAndrew Thompson 	uint8_t ds_fail:1;
97f6980be8SAndrew Thompson 	uint8_t ss_fail:1;
98f6980be8SAndrew Thompson 	uint8_t enabled:1;
99f6980be8SAndrew Thompson };
100f6980be8SAndrew Thompson 
101f6980be8SAndrew Thompson /* The default is to disable fault injection. */
102f6980be8SAndrew Thompson 
103f6980be8SAndrew Thompson static struct usb_ctrl_debug usb_ctrl_debug = {
104f6980be8SAndrew Thompson 	.bus_index = -1,
105f6980be8SAndrew Thompson 	.dev_index = -1,
106f6980be8SAndrew Thompson 	.bmRequestType_value = -1,
107f6980be8SAndrew Thompson 	.bRequest_value = -1,
108f6980be8SAndrew Thompson };
109f6980be8SAndrew Thompson 
110f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RW,
111f6980be8SAndrew Thompson     &usb_ctrl_debug.bus_index, 0, "USB controller index to fail");
112f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RW,
113f6980be8SAndrew Thompson     &usb_ctrl_debug.dev_index, 0, "USB device address to fail");
114f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RW,
115f6980be8SAndrew Thompson     &usb_ctrl_debug.ds_fail, 0, "USB fail data stage");
116f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RW,
117f6980be8SAndrew Thompson     &usb_ctrl_debug.ss_fail, 0, "USB fail status stage");
118f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RW,
119f6980be8SAndrew Thompson     &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms");
120f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RW,
121f6980be8SAndrew Thompson     &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms");
122f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RW,
123f6980be8SAndrew Thompson     &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail");
124f6980be8SAndrew Thompson SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RW,
125f6980be8SAndrew Thompson     &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail");
126f6980be8SAndrew Thompson 
127f6980be8SAndrew Thompson /*------------------------------------------------------------------------*
128f6980be8SAndrew Thompson  *	usbd_get_debug_bits
129f6980be8SAndrew Thompson  *
130f6980be8SAndrew Thompson  * This function is only useful in USB host mode.
131f6980be8SAndrew Thompson  *------------------------------------------------------------------------*/
132f6980be8SAndrew Thompson static void
133f6980be8SAndrew Thompson usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req,
134f6980be8SAndrew Thompson     struct usb_ctrl_debug_bits *dbg)
135f6980be8SAndrew Thompson {
136f6980be8SAndrew Thompson 	int temp;
137f6980be8SAndrew Thompson 
138f6980be8SAndrew Thompson 	memset(dbg, 0, sizeof(*dbg));
139f6980be8SAndrew Thompson 
140f6980be8SAndrew Thompson 	/* Compute data stage delay */
141f6980be8SAndrew Thompson 
142f6980be8SAndrew Thompson 	temp = usb_ctrl_debug.ds_delay;
143f6980be8SAndrew Thompson 	if (temp < 0)
144f6980be8SAndrew Thompson 		temp = 0;
145f6980be8SAndrew Thompson 	else if (temp > (16*1024))
146f6980be8SAndrew Thompson 		temp = (16*1024);
147f6980be8SAndrew Thompson 
148f6980be8SAndrew Thompson 	dbg->ds_delay = temp;
149f6980be8SAndrew Thompson 
150f6980be8SAndrew Thompson 	/* Compute status stage delay */
151f6980be8SAndrew Thompson 
152f6980be8SAndrew Thompson 	temp = usb_ctrl_debug.ss_delay;
153f6980be8SAndrew Thompson 	if (temp < 0)
154f6980be8SAndrew Thompson 		temp = 0;
155f6980be8SAndrew Thompson 	else if (temp > (16*1024))
156f6980be8SAndrew Thompson 		temp = (16*1024);
157f6980be8SAndrew Thompson 
158f6980be8SAndrew Thompson 	dbg->ss_delay = temp;
159f6980be8SAndrew Thompson 
160f6980be8SAndrew Thompson 	/* Check if this control request should be failed */
161f6980be8SAndrew Thompson 
162f6980be8SAndrew Thompson 	if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index)
163f6980be8SAndrew Thompson 		return;
164f6980be8SAndrew Thompson 
165f6980be8SAndrew Thompson 	if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index)
166f6980be8SAndrew Thompson 		return;
167f6980be8SAndrew Thompson 
168f6980be8SAndrew Thompson 	temp = usb_ctrl_debug.bmRequestType_value;
169f6980be8SAndrew Thompson 
170f6980be8SAndrew Thompson 	if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255))
171f6980be8SAndrew Thompson 		return;
172f6980be8SAndrew Thompson 
173f6980be8SAndrew Thompson 	temp = usb_ctrl_debug.bRequest_value;
174f6980be8SAndrew Thompson 
175f6980be8SAndrew Thompson 	if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255))
176f6980be8SAndrew Thompson 		return;
177f6980be8SAndrew Thompson 
178f6980be8SAndrew Thompson 	temp = usb_ctrl_debug.ds_fail;
179f6980be8SAndrew Thompson 	if (temp)
180f6980be8SAndrew Thompson 		dbg->ds_fail = 1;
181f6980be8SAndrew Thompson 
182f6980be8SAndrew Thompson 	temp = usb_ctrl_debug.ss_fail;
183f6980be8SAndrew Thompson 	if (temp)
184f6980be8SAndrew Thompson 		dbg->ss_fail = 1;
185f6980be8SAndrew Thompson 
186f6980be8SAndrew Thompson 	dbg->enabled = 1;
187f6980be8SAndrew Thompson }
188f6980be8SAndrew Thompson #endif	/* USB_REQ_DEBUG */
189f6980be8SAndrew Thompson #endif	/* USB_DEBUG */
19002ac6454SAndrew Thompson 
19102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
192a593f6b8SAndrew Thompson  *	usbd_do_request_callback
19302ac6454SAndrew Thompson  *
19402ac6454SAndrew Thompson  * This function is the USB callback for generic USB Host control
19502ac6454SAndrew Thompson  * transfers.
19602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
19702ac6454SAndrew Thompson void
198ed6d949aSAndrew Thompson usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error)
19902ac6454SAndrew Thompson {
20002ac6454SAndrew Thompson 	;				/* workaround for a bug in "indent" */
20102ac6454SAndrew Thompson 
20202ac6454SAndrew Thompson 	DPRINTF("st=%u\n", USB_GET_STATE(xfer));
20302ac6454SAndrew Thompson 
20402ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
20502ac6454SAndrew Thompson 	case USB_ST_SETUP:
206a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
20702ac6454SAndrew Thompson 		break;
20802ac6454SAndrew Thompson 	default:
20991cd9240SAndrew Thompson 		cv_signal(&xfer->xroot->udev->ctrlreq_cv);
21002ac6454SAndrew Thompson 		break;
21102ac6454SAndrew Thompson 	}
21202ac6454SAndrew Thompson }
21302ac6454SAndrew Thompson 
21402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
215a593f6b8SAndrew Thompson  *	usb_do_clear_stall_callback
21602ac6454SAndrew Thompson  *
21702ac6454SAndrew Thompson  * This function is the USB callback for generic clear stall requests.
21802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
21902ac6454SAndrew Thompson void
220ed6d949aSAndrew Thompson usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
22102ac6454SAndrew Thompson {
222760bc48eSAndrew Thompson 	struct usb_device_request req;
223760bc48eSAndrew Thompson 	struct usb_device *udev;
224ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
225ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep_end;
226ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep_first;
22763521bbcSAndrew Thompson 	uint8_t to;
22802ac6454SAndrew Thompson 
22902ac6454SAndrew Thompson 	udev = xfer->xroot->udev;
23002ac6454SAndrew Thompson 
23102ac6454SAndrew Thompson 	USB_BUS_LOCK(udev->bus);
23202ac6454SAndrew Thompson 
233ae60fdfbSAndrew Thompson 	/* round robin endpoint clear stall */
23402ac6454SAndrew Thompson 
235ae60fdfbSAndrew Thompson 	ep = udev->ep_curr;
236ae60fdfbSAndrew Thompson 	ep_end = udev->endpoints + udev->endpoints_max;
237ae60fdfbSAndrew Thompson 	ep_first = udev->endpoints;
238ae60fdfbSAndrew Thompson 	to = udev->endpoints_max;
239115df0b6SAndrew Thompson 
24002ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
24102ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
242ae60fdfbSAndrew Thompson 		if (ep == NULL)
243115df0b6SAndrew Thompson 			goto tr_setup;		/* device was unconfigured */
244ae60fdfbSAndrew Thompson 		if (ep->edesc &&
245ae60fdfbSAndrew Thompson 		    ep->is_stalled) {
246ae60fdfbSAndrew Thompson 			ep->toggle_next = 0;
247ae60fdfbSAndrew Thompson 			ep->is_stalled = 0;
248963169b4SHans Petter Selasky 			/* some hardware needs a callback to clear the data toggle */
249963169b4SHans Petter Selasky 			usbd_clear_stall_locked(udev, ep);
25002ac6454SAndrew Thompson 			/* start up the current or next transfer, if any */
251a593f6b8SAndrew Thompson 			usb_command_wrapper(&ep->endpoint_q,
252ae60fdfbSAndrew Thompson 			    ep->endpoint_q.curr);
25302ac6454SAndrew Thompson 		}
254ae60fdfbSAndrew Thompson 		ep++;
25502ac6454SAndrew Thompson 
25602ac6454SAndrew Thompson 	case USB_ST_SETUP:
25702ac6454SAndrew Thompson tr_setup:
258115df0b6SAndrew Thompson 		if (to == 0)
259ae60fdfbSAndrew Thompson 			break;			/* no endpoints - nothing to do */
260ae60fdfbSAndrew Thompson 		if ((ep < ep_first) || (ep >= ep_end))
261ae60fdfbSAndrew Thompson 			ep = ep_first;	/* endpoint wrapped around */
262ae60fdfbSAndrew Thompson 		if (ep->edesc &&
263ae60fdfbSAndrew Thompson 		    ep->is_stalled) {
26402ac6454SAndrew Thompson 
26502ac6454SAndrew Thompson 			/* setup a clear-stall packet */
26602ac6454SAndrew Thompson 
26702ac6454SAndrew Thompson 			req.bmRequestType = UT_WRITE_ENDPOINT;
26802ac6454SAndrew Thompson 			req.bRequest = UR_CLEAR_FEATURE;
26902ac6454SAndrew Thompson 			USETW(req.wValue, UF_ENDPOINT_HALT);
270ae60fdfbSAndrew Thompson 			req.wIndex[0] = ep->edesc->bEndpointAddress;
27102ac6454SAndrew Thompson 			req.wIndex[1] = 0;
27202ac6454SAndrew Thompson 			USETW(req.wLength, 0);
27302ac6454SAndrew Thompson 
27402ac6454SAndrew Thompson 			/* copy in the transfer */
27502ac6454SAndrew Thompson 
276a593f6b8SAndrew Thompson 			usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
27702ac6454SAndrew Thompson 
27802ac6454SAndrew Thompson 			/* set length */
279ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
28002ac6454SAndrew Thompson 			xfer->nframes = 1;
28102ac6454SAndrew Thompson 			USB_BUS_UNLOCK(udev->bus);
28202ac6454SAndrew Thompson 
283a593f6b8SAndrew Thompson 			usbd_transfer_submit(xfer);
28402ac6454SAndrew Thompson 
28502ac6454SAndrew Thompson 			USB_BUS_LOCK(udev->bus);
28602ac6454SAndrew Thompson 			break;
28702ac6454SAndrew Thompson 		}
288ae60fdfbSAndrew Thompson 		ep++;
289115df0b6SAndrew Thompson 		to--;
29002ac6454SAndrew Thompson 		goto tr_setup;
29102ac6454SAndrew Thompson 
29202ac6454SAndrew Thompson 	default:
29302ac6454SAndrew Thompson 		if (xfer->error == USB_ERR_CANCELLED) {
29402ac6454SAndrew Thompson 			break;
29502ac6454SAndrew Thompson 		}
29602ac6454SAndrew Thompson 		goto tr_setup;
29702ac6454SAndrew Thompson 	}
29802ac6454SAndrew Thompson 
299ae60fdfbSAndrew Thompson 	/* store current endpoint */
300ae60fdfbSAndrew Thompson 	udev->ep_curr = ep;
30102ac6454SAndrew Thompson 	USB_BUS_UNLOCK(udev->bus);
30202ac6454SAndrew Thompson }
30302ac6454SAndrew Thompson 
304e0a69b51SAndrew Thompson static usb_handle_req_t *
305a593f6b8SAndrew Thompson usbd_get_hr_func(struct usb_device *udev)
306459d369eSAndrew Thompson {
307459d369eSAndrew Thompson 	/* figure out if there is a Handle Request function */
308f29a0724SAndrew Thompson 	if (udev->flags.usb_mode == USB_MODE_DEVICE)
309a593f6b8SAndrew Thompson 		return (usb_temp_get_desc_p);
310459d369eSAndrew Thompson 	else if (udev->parent_hub == NULL)
311459d369eSAndrew Thompson 		return (udev->bus->methods->roothub_exec);
312459d369eSAndrew Thompson 	else
313459d369eSAndrew Thompson 		return (NULL);
314459d369eSAndrew Thompson }
315459d369eSAndrew Thompson 
31602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
317a593f6b8SAndrew Thompson  *	usbd_do_request_flags and usbd_do_request
31802ac6454SAndrew Thompson  *
31902ac6454SAndrew Thompson  * Description of arguments passed to these functions:
32002ac6454SAndrew Thompson  *
321760bc48eSAndrew Thompson  * "udev" - this is the "usb_device" structure pointer on which the
32202ac6454SAndrew Thompson  * request should be performed. It is possible to call this function
32302ac6454SAndrew Thompson  * in both Host Side mode and Device Side mode.
32402ac6454SAndrew Thompson  *
32502ac6454SAndrew Thompson  * "mtx" - if this argument is non-NULL the mutex pointed to by it
32602ac6454SAndrew Thompson  * will get dropped and picked up during the execution of this
32702ac6454SAndrew Thompson  * function, hence this function sometimes needs to sleep. If this
32802ac6454SAndrew Thompson  * argument is NULL it has no effect.
32902ac6454SAndrew Thompson  *
33002ac6454SAndrew Thompson  * "req" - this argument must always be non-NULL and points to an
33102ac6454SAndrew Thompson  * 8-byte structure holding the USB request to be done. The USB
33202ac6454SAndrew Thompson  * request structure has a bit telling the direction of the USB
33302ac6454SAndrew Thompson  * request, if it is a read or a write.
33402ac6454SAndrew Thompson  *
33502ac6454SAndrew Thompson  * "data" - if the "wLength" part of the structure pointed to by "req"
33602ac6454SAndrew Thompson  * is non-zero this argument must point to a valid kernel buffer which
33702ac6454SAndrew Thompson  * can hold at least "wLength" bytes. If "wLength" is zero "data" can
33802ac6454SAndrew Thompson  * be NULL.
33902ac6454SAndrew Thompson  *
34002ac6454SAndrew Thompson  * "flags" - here is a list of valid flags:
34102ac6454SAndrew Thompson  *
34202ac6454SAndrew Thompson  *  o USB_SHORT_XFER_OK: allows the data transfer to be shorter than
34302ac6454SAndrew Thompson  *  specified
34402ac6454SAndrew Thompson  *
34502ac6454SAndrew Thompson  *  o USB_DELAY_STATUS_STAGE: allows the status stage to be performed
34602ac6454SAndrew Thompson  *  at a later point in time. This is tunable by the "hw.usb.ss_delay"
34702ac6454SAndrew Thompson  *  sysctl. This flag is mostly useful for debugging.
34802ac6454SAndrew Thompson  *
34902ac6454SAndrew Thompson  *  o USB_USER_DATA_PTR: treat the "data" pointer like a userland
35002ac6454SAndrew Thompson  *  pointer.
35102ac6454SAndrew Thompson  *
35202ac6454SAndrew Thompson  * "actlen" - if non-NULL the actual transfer length will be stored in
35302ac6454SAndrew Thompson  * the 16-bit unsigned integer pointed to by "actlen". This
35402ac6454SAndrew Thompson  * information is mostly useful when the "USB_SHORT_XFER_OK" flag is
35502ac6454SAndrew Thompson  * used.
35602ac6454SAndrew Thompson  *
35702ac6454SAndrew Thompson  * "timeout" - gives the timeout for the control transfer in
35802ac6454SAndrew Thompson  * milliseconds. A "timeout" value less than 50 milliseconds is
35902ac6454SAndrew Thompson  * treated like a 50 millisecond timeout. A "timeout" value greater
36002ac6454SAndrew Thompson  * than 30 seconds is treated like a 30 second timeout. This USB stack
36102ac6454SAndrew Thompson  * does not allow control requests without a timeout.
36202ac6454SAndrew Thompson  *
36302ac6454SAndrew Thompson  * NOTE: This function is thread safe. All calls to
364a593f6b8SAndrew Thompson  * "usbd_do_request_flags" will be serialised by the use of an
36502ac6454SAndrew Thompson  * internal "sx_lock".
36602ac6454SAndrew Thompson  *
36702ac6454SAndrew Thompson  * Returns:
36802ac6454SAndrew Thompson  *    0: Success
36902ac6454SAndrew Thompson  * Else: Failure
37002ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
371e0a69b51SAndrew Thompson usb_error_t
372a593f6b8SAndrew Thompson usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
373760bc48eSAndrew Thompson     struct usb_device_request *req, void *data, uint16_t flags,
374e0a69b51SAndrew Thompson     uint16_t *actlen, usb_timeout_t timeout)
37502ac6454SAndrew Thompson {
376f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
377f6980be8SAndrew Thompson 	struct usb_ctrl_debug_bits dbg;
378f6980be8SAndrew Thompson #endif
379e0a69b51SAndrew Thompson 	usb_handle_req_t *hr_func;
380760bc48eSAndrew Thompson 	struct usb_xfer *xfer;
38102ac6454SAndrew Thompson 	const void *desc;
38202ac6454SAndrew Thompson 	int err = 0;
383e0a69b51SAndrew Thompson 	usb_ticks_t start_ticks;
384e0a69b51SAndrew Thompson 	usb_ticks_t delta_ticks;
385e0a69b51SAndrew Thompson 	usb_ticks_t max_ticks;
38602ac6454SAndrew Thompson 	uint16_t length;
38702ac6454SAndrew Thompson 	uint16_t temp;
388f6980be8SAndrew Thompson 	uint16_t acttemp;
3892df1e9a6SAndrew Thompson 	uint8_t enum_locked;
39002ac6454SAndrew Thompson 
39102ac6454SAndrew Thompson 	if (timeout < 50) {
39202ac6454SAndrew Thompson 		/* timeout is too small */
39302ac6454SAndrew Thompson 		timeout = 50;
39402ac6454SAndrew Thompson 	}
39502ac6454SAndrew Thompson 	if (timeout > 30000) {
39602ac6454SAndrew Thompson 		/* timeout is too big */
39702ac6454SAndrew Thompson 		timeout = 30000;
39802ac6454SAndrew Thompson 	}
39902ac6454SAndrew Thompson 	length = UGETW(req->wLength);
40002ac6454SAndrew Thompson 
4012df1e9a6SAndrew Thompson 	enum_locked = usbd_enum_is_locked(udev);
4022df1e9a6SAndrew Thompson 
40302ac6454SAndrew Thompson 	DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x "
40402ac6454SAndrew Thompson 	    "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n",
40502ac6454SAndrew Thompson 	    udev, req->bmRequestType, req->bRequest,
40602ac6454SAndrew Thompson 	    req->wValue[1], req->wValue[0],
40702ac6454SAndrew Thompson 	    req->wIndex[1], req->wIndex[0],
40802ac6454SAndrew Thompson 	    req->wLength[1], req->wLength[0]);
40902ac6454SAndrew Thompson 
410bd216778SAndrew Thompson 	/* Check if the device is still alive */
411bd216778SAndrew Thompson 	if (udev->state < USB_STATE_POWERED) {
412bd216778SAndrew Thompson 		DPRINTF("usb device has gone\n");
413bd216778SAndrew Thompson 		return (USB_ERR_NOT_CONFIGURED);
414bd216778SAndrew Thompson 	}
415bd216778SAndrew Thompson 
41602ac6454SAndrew Thompson 	/*
41702ac6454SAndrew Thompson 	 * Set "actlen" to a known value in case the caller does not
41802ac6454SAndrew Thompson 	 * check the return value:
41902ac6454SAndrew Thompson 	 */
42039307315SAndrew Thompson 	if (actlen)
42102ac6454SAndrew Thompson 		*actlen = 0;
42239307315SAndrew Thompson 
423bdc081c6SAndrew Thompson #if (USB_HAVE_USER_IO == 0)
424bdc081c6SAndrew Thompson 	if (flags & USB_USER_DATA_PTR)
425bdc081c6SAndrew Thompson 		return (USB_ERR_INVAL);
426bdc081c6SAndrew Thompson #endif
4272df1e9a6SAndrew Thompson 	if ((mtx != NULL) && (mtx != &Giant)) {
42802ac6454SAndrew Thompson 		mtx_unlock(mtx);
42902ac6454SAndrew Thompson 		mtx_assert(mtx, MA_NOTOWNED);
43002ac6454SAndrew Thompson 	}
4312df1e9a6SAndrew Thompson 
4322df1e9a6SAndrew Thompson 	/*
4332df1e9a6SAndrew Thompson 	 * We need to allow suspend and resume at this point, else the
4342df1e9a6SAndrew Thompson 	 * control transfer will timeout if the device is suspended!
4352df1e9a6SAndrew Thompson 	 */
4362df1e9a6SAndrew Thompson 	if (enum_locked)
4372df1e9a6SAndrew Thompson 		usbd_sr_unlock(udev);
4382df1e9a6SAndrew Thompson 
43902ac6454SAndrew Thompson 	/*
44002ac6454SAndrew Thompson 	 * Grab the default sx-lock so that serialisation
44102ac6454SAndrew Thompson 	 * is achieved when multiple threads are involved:
44202ac6454SAndrew Thompson 	 */
44391cd9240SAndrew Thompson 	sx_xlock(&udev->ctrl_sx);
44402ac6454SAndrew Thompson 
445a593f6b8SAndrew Thompson 	hr_func = usbd_get_hr_func(udev);
44639307315SAndrew Thompson 
447459d369eSAndrew Thompson 	if (hr_func != NULL) {
448459d369eSAndrew Thompson 		DPRINTF("Handle Request function is set\n");
44939307315SAndrew Thompson 
450459d369eSAndrew Thompson 		desc = NULL;
451459d369eSAndrew Thompson 		temp = 0;
452459d369eSAndrew Thompson 
453459d369eSAndrew Thompson 		if (!(req->bmRequestType & UT_READ)) {
45439307315SAndrew Thompson 			if (length != 0) {
455459d369eSAndrew Thompson 				DPRINTFN(1, "The handle request function "
456459d369eSAndrew Thompson 				    "does not support writing data!\n");
45739307315SAndrew Thompson 				err = USB_ERR_INVAL;
45839307315SAndrew Thompson 				goto done;
45939307315SAndrew Thompson 			}
46039307315SAndrew Thompson 		}
461459d369eSAndrew Thompson 
462459d369eSAndrew Thompson 		/* The root HUB code needs the BUS lock locked */
46339307315SAndrew Thompson 
46439307315SAndrew Thompson 		USB_BUS_LOCK(udev->bus);
465459d369eSAndrew Thompson 		err = (hr_func) (udev, req, &desc, &temp);
46639307315SAndrew Thompson 		USB_BUS_UNLOCK(udev->bus);
46739307315SAndrew Thompson 
46839307315SAndrew Thompson 		if (err)
46939307315SAndrew Thompson 			goto done;
47039307315SAndrew Thompson 
471459d369eSAndrew Thompson 		if (length > temp) {
47239307315SAndrew Thompson 			if (!(flags & USB_SHORT_XFER_OK)) {
47339307315SAndrew Thompson 				err = USB_ERR_SHORT_XFER;
47439307315SAndrew Thompson 				goto done;
47539307315SAndrew Thompson 			}
476459d369eSAndrew Thompson 			length = temp;
47739307315SAndrew Thompson 		}
47839307315SAndrew Thompson 		if (actlen)
47939307315SAndrew Thompson 			*actlen = length;
48039307315SAndrew Thompson 
48139307315SAndrew Thompson 		if (length > 0) {
48239307315SAndrew Thompson #if USB_HAVE_USER_IO
48339307315SAndrew Thompson 			if (flags & USB_USER_DATA_PTR) {
484459d369eSAndrew Thompson 				if (copyout(desc, data, length)) {
48539307315SAndrew Thompson 					err = USB_ERR_INVAL;
48639307315SAndrew Thompson 					goto done;
48739307315SAndrew Thompson 				}
48839307315SAndrew Thompson 			} else
48939307315SAndrew Thompson #endif
490459d369eSAndrew Thompson 				bcopy(desc, data, length);
49139307315SAndrew Thompson 		}
492459d369eSAndrew Thompson 		goto done;		/* success */
49339307315SAndrew Thompson 	}
49439307315SAndrew Thompson 
49502ac6454SAndrew Thompson 	/*
49602ac6454SAndrew Thompson 	 * Setup a new USB transfer or use the existing one, if any:
49702ac6454SAndrew Thompson 	 */
4985b3bb704SAndrew Thompson 	usbd_ctrl_transfer_setup(udev);
49902ac6454SAndrew Thompson 
5005b3bb704SAndrew Thompson 	xfer = udev->ctrl_xfer[0];
50102ac6454SAndrew Thompson 	if (xfer == NULL) {
50202ac6454SAndrew Thompson 		/* most likely out of memory */
50302ac6454SAndrew Thompson 		err = USB_ERR_NOMEM;
50402ac6454SAndrew Thompson 		goto done;
50502ac6454SAndrew Thompson 	}
506f6980be8SAndrew Thompson 
507f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
508f6980be8SAndrew Thompson 	/* Get debug bits */
509f6980be8SAndrew Thompson 	usbd_get_debug_bits(udev, req, &dbg);
510f6980be8SAndrew Thompson 
511f6980be8SAndrew Thompson 	/* Check for fault injection */
512f6980be8SAndrew Thompson 	if (dbg.enabled)
513f6980be8SAndrew Thompson 		flags |= USB_DELAY_STATUS_STAGE;
514f6980be8SAndrew Thompson #endif
51502ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
51602ac6454SAndrew Thompson 
5174eae601eSAndrew Thompson 	if (flags & USB_DELAY_STATUS_STAGE)
51802ac6454SAndrew Thompson 		xfer->flags.manual_status = 1;
5194eae601eSAndrew Thompson 	else
52002ac6454SAndrew Thompson 		xfer->flags.manual_status = 0;
5214eae601eSAndrew Thompson 
5224eae601eSAndrew Thompson 	if (flags & USB_SHORT_XFER_OK)
5234eae601eSAndrew Thompson 		xfer->flags.short_xfer_ok = 1;
5244eae601eSAndrew Thompson 	else
5254eae601eSAndrew Thompson 		xfer->flags.short_xfer_ok = 0;
52602ac6454SAndrew Thompson 
52702ac6454SAndrew Thompson 	xfer->timeout = timeout;
52802ac6454SAndrew Thompson 
52902ac6454SAndrew Thompson 	start_ticks = ticks;
53002ac6454SAndrew Thompson 
53102ac6454SAndrew Thompson 	max_ticks = USB_MS_TO_TICKS(timeout);
53202ac6454SAndrew Thompson 
533a593f6b8SAndrew Thompson 	usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req));
53402ac6454SAndrew Thompson 
535ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_len(xfer, 0, sizeof(*req));
53602ac6454SAndrew Thompson 
53702ac6454SAndrew Thompson 	while (1) {
53802ac6454SAndrew Thompson 		temp = length;
539f6980be8SAndrew Thompson 		if (temp > usbd_xfer_max_len(xfer)) {
540ed6d949aSAndrew Thompson 			temp = usbd_xfer_max_len(xfer);
54102ac6454SAndrew Thompson 		}
542f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
543f6980be8SAndrew Thompson 		if (xfer->flags.manual_status) {
544f6980be8SAndrew Thompson 			if (usbd_xfer_frame_len(xfer, 0) != 0) {
545f6980be8SAndrew Thompson 				/* Execute data stage separately */
546f6980be8SAndrew Thompson 				temp = 0;
547f6980be8SAndrew Thompson 			} else if (temp > 0) {
548f6980be8SAndrew Thompson 				if (dbg.ds_fail) {
549f6980be8SAndrew Thompson 					err = USB_ERR_INVAL;
550f6980be8SAndrew Thompson 					break;
551f6980be8SAndrew Thompson 				}
552f6980be8SAndrew Thompson 				if (dbg.ds_delay > 0) {
553f6980be8SAndrew Thompson 					usb_pause_mtx(
554f6980be8SAndrew Thompson 					    xfer->xroot->xfer_mtx,
555f6980be8SAndrew Thompson 				            USB_MS_TO_TICKS(dbg.ds_delay));
556f6980be8SAndrew Thompson 					/* make sure we don't time out */
557f6980be8SAndrew Thompson 					start_ticks = ticks;
558f6980be8SAndrew Thompson 				}
559f6980be8SAndrew Thompson 			}
560f6980be8SAndrew Thompson 		}
561f6980be8SAndrew Thompson #endif
562ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 1, temp);
56302ac6454SAndrew Thompson 
56402ac6454SAndrew Thompson 		if (temp > 0) {
56502ac6454SAndrew Thompson 			if (!(req->bmRequestType & UT_READ)) {
566bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO
56702ac6454SAndrew Thompson 				if (flags & USB_USER_DATA_PTR) {
56802ac6454SAndrew Thompson 					USB_XFER_UNLOCK(xfer);
569a593f6b8SAndrew Thompson 					err = usbd_copy_in_user(xfer->frbuffers + 1,
57002ac6454SAndrew Thompson 					    0, data, temp);
57102ac6454SAndrew Thompson 					USB_XFER_LOCK(xfer);
57202ac6454SAndrew Thompson 					if (err) {
57302ac6454SAndrew Thompson 						err = USB_ERR_INVAL;
57402ac6454SAndrew Thompson 						break;
57502ac6454SAndrew Thompson 					}
576bdc081c6SAndrew Thompson 				} else
577bdc081c6SAndrew Thompson #endif
578a593f6b8SAndrew Thompson 					usbd_copy_in(xfer->frbuffers + 1,
579bdc081c6SAndrew Thompson 					    0, data, temp);
58002ac6454SAndrew Thompson 			}
581f6980be8SAndrew Thompson 			usbd_xfer_set_frames(xfer, 2);
58202ac6454SAndrew Thompson 		} else {
583f6980be8SAndrew Thompson 			if (usbd_xfer_frame_len(xfer, 0) == 0) {
58402ac6454SAndrew Thompson 				if (xfer->flags.manual_status) {
585f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
586f6980be8SAndrew Thompson 					if (dbg.ss_fail) {
587f6980be8SAndrew Thompson 						err = USB_ERR_INVAL;
588f6980be8SAndrew Thompson 						break;
58902ac6454SAndrew Thompson 					}
590f6980be8SAndrew Thompson 					if (dbg.ss_delay > 0) {
591a593f6b8SAndrew Thompson 						usb_pause_mtx(
59202ac6454SAndrew Thompson 						    xfer->xroot->xfer_mtx,
593f6980be8SAndrew Thompson 						    USB_MS_TO_TICKS(dbg.ss_delay));
594f6980be8SAndrew Thompson 						/* make sure we don't time out */
595f6980be8SAndrew Thompson 						start_ticks = ticks;
59602ac6454SAndrew Thompson 					}
59702ac6454SAndrew Thompson #endif
59802ac6454SAndrew Thompson 					xfer->flags.manual_status = 0;
59902ac6454SAndrew Thompson 				} else {
60002ac6454SAndrew Thompson 					break;
60102ac6454SAndrew Thompson 				}
60202ac6454SAndrew Thompson 			}
603f6980be8SAndrew Thompson 			usbd_xfer_set_frames(xfer, 1);
60402ac6454SAndrew Thompson 		}
60502ac6454SAndrew Thompson 
606a593f6b8SAndrew Thompson 		usbd_transfer_start(xfer);
60702ac6454SAndrew Thompson 
608a593f6b8SAndrew Thompson 		while (usbd_transfer_pending(xfer)) {
60991cd9240SAndrew Thompson 			cv_wait(&udev->ctrlreq_cv,
61002ac6454SAndrew Thompson 			    xfer->xroot->xfer_mtx);
61102ac6454SAndrew Thompson 		}
61202ac6454SAndrew Thompson 
61302ac6454SAndrew Thompson 		err = xfer->error;
61402ac6454SAndrew Thompson 
61502ac6454SAndrew Thompson 		if (err) {
61602ac6454SAndrew Thompson 			break;
61702ac6454SAndrew Thompson 		}
61802ac6454SAndrew Thompson 
619f6980be8SAndrew Thompson 		/* get actual length of DATA stage */
620f6980be8SAndrew Thompson 
621f6980be8SAndrew Thompson 		if (xfer->aframes < 2) {
622f6980be8SAndrew Thompson 			acttemp = 0;
62302ac6454SAndrew Thompson 		} else {
624f6980be8SAndrew Thompson 			acttemp = usbd_xfer_frame_len(xfer, 1);
62502ac6454SAndrew Thompson 		}
62602ac6454SAndrew Thompson 
62702ac6454SAndrew Thompson 		/* check for short packet */
62802ac6454SAndrew Thompson 
629f6980be8SAndrew Thompson 		if (temp > acttemp) {
630f6980be8SAndrew Thompson 			temp = acttemp;
63102ac6454SAndrew Thompson 			length = temp;
63202ac6454SAndrew Thompson 		}
63302ac6454SAndrew Thompson 		if (temp > 0) {
63402ac6454SAndrew Thompson 			if (req->bmRequestType & UT_READ) {
635bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO
63602ac6454SAndrew Thompson 				if (flags & USB_USER_DATA_PTR) {
63702ac6454SAndrew Thompson 					USB_XFER_UNLOCK(xfer);
638a593f6b8SAndrew Thompson 					err = usbd_copy_out_user(xfer->frbuffers + 1,
63902ac6454SAndrew Thompson 					    0, data, temp);
64002ac6454SAndrew Thompson 					USB_XFER_LOCK(xfer);
64102ac6454SAndrew Thompson 					if (err) {
64202ac6454SAndrew Thompson 						err = USB_ERR_INVAL;
64302ac6454SAndrew Thompson 						break;
64402ac6454SAndrew Thompson 					}
645bdc081c6SAndrew Thompson 				} else
646bdc081c6SAndrew Thompson #endif
647a593f6b8SAndrew Thompson 					usbd_copy_out(xfer->frbuffers + 1,
64802ac6454SAndrew Thompson 					    0, data, temp);
64902ac6454SAndrew Thompson 			}
65002ac6454SAndrew Thompson 		}
65102ac6454SAndrew Thompson 		/*
65202ac6454SAndrew Thompson 		 * Clear "frlengths[0]" so that we don't send the setup
65302ac6454SAndrew Thompson 		 * packet again:
65402ac6454SAndrew Thompson 		 */
655ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 0, 0);
65602ac6454SAndrew Thompson 
65702ac6454SAndrew Thompson 		/* update length and data pointer */
65802ac6454SAndrew Thompson 		length -= temp;
65902ac6454SAndrew Thompson 		data = USB_ADD_BYTES(data, temp);
66002ac6454SAndrew Thompson 
66102ac6454SAndrew Thompson 		if (actlen) {
66202ac6454SAndrew Thompson 			(*actlen) += temp;
66302ac6454SAndrew Thompson 		}
66402ac6454SAndrew Thompson 		/* check for timeout */
66502ac6454SAndrew Thompson 
66602ac6454SAndrew Thompson 		delta_ticks = ticks - start_ticks;
66702ac6454SAndrew Thompson 		if (delta_ticks > max_ticks) {
66802ac6454SAndrew Thompson 			if (!err) {
66902ac6454SAndrew Thompson 				err = USB_ERR_TIMEOUT;
67002ac6454SAndrew Thompson 			}
67102ac6454SAndrew Thompson 		}
67202ac6454SAndrew Thompson 		if (err) {
67302ac6454SAndrew Thompson 			break;
67402ac6454SAndrew Thompson 		}
67502ac6454SAndrew Thompson 	}
67602ac6454SAndrew Thompson 
67702ac6454SAndrew Thompson 	if (err) {
67802ac6454SAndrew Thompson 		/*
67902ac6454SAndrew Thompson 		 * Make sure that the control endpoint is no longer
68002ac6454SAndrew Thompson 		 * blocked in case of a non-transfer related error:
68102ac6454SAndrew Thompson 		 */
682a593f6b8SAndrew Thompson 		usbd_transfer_stop(xfer);
68302ac6454SAndrew Thompson 	}
68402ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
68502ac6454SAndrew Thompson 
68602ac6454SAndrew Thompson done:
68791cd9240SAndrew Thompson 	sx_xunlock(&udev->ctrl_sx);
68802ac6454SAndrew Thompson 
6892df1e9a6SAndrew Thompson 	if (enum_locked)
6902df1e9a6SAndrew Thompson 		usbd_sr_lock(udev);
6912df1e9a6SAndrew Thompson 
6922df1e9a6SAndrew Thompson 	if ((mtx != NULL) && (mtx != &Giant))
69302ac6454SAndrew Thompson 		mtx_lock(mtx);
6942df1e9a6SAndrew Thompson 
695e0a69b51SAndrew Thompson 	return ((usb_error_t)err);
69602ac6454SAndrew Thompson }
69702ac6454SAndrew Thompson 
69802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
699a593f6b8SAndrew Thompson  *	usbd_do_request_proc - factored out code
70002ac6454SAndrew Thompson  *
70102ac6454SAndrew Thompson  * This function is factored out code. It does basically the same like
702a593f6b8SAndrew Thompson  * usbd_do_request_flags, except it will check the status of the
70302ac6454SAndrew Thompson  * passed process argument before doing the USB request. If the
70402ac6454SAndrew Thompson  * process is draining the USB_ERR_IOERROR code will be returned. It
70502ac6454SAndrew Thompson  * is assumed that the mutex associated with the process is locked
70602ac6454SAndrew Thompson  * when calling this function.
70702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
708e0a69b51SAndrew Thompson usb_error_t
709a593f6b8SAndrew Thompson usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc,
710760bc48eSAndrew Thompson     struct usb_device_request *req, void *data, uint16_t flags,
711e0a69b51SAndrew Thompson     uint16_t *actlen, usb_timeout_t timeout)
71202ac6454SAndrew Thompson {
713e0a69b51SAndrew Thompson 	usb_error_t err;
71402ac6454SAndrew Thompson 	uint16_t len;
71502ac6454SAndrew Thompson 
71602ac6454SAndrew Thompson 	/* get request data length */
71702ac6454SAndrew Thompson 	len = UGETW(req->wLength);
71802ac6454SAndrew Thompson 
71902ac6454SAndrew Thompson 	/* check if the device is being detached */
720a593f6b8SAndrew Thompson 	if (usb_proc_is_gone(pproc)) {
72102ac6454SAndrew Thompson 		err = USB_ERR_IOERROR;
72202ac6454SAndrew Thompson 		goto done;
72302ac6454SAndrew Thompson 	}
72402ac6454SAndrew Thompson 
72502ac6454SAndrew Thompson 	/* forward the USB request */
726a593f6b8SAndrew Thompson 	err = usbd_do_request_flags(udev, pproc->up_mtx,
72702ac6454SAndrew Thompson 	    req, data, flags, actlen, timeout);
72802ac6454SAndrew Thompson 
72902ac6454SAndrew Thompson done:
73002ac6454SAndrew Thompson 	/* on failure we zero the data */
73102ac6454SAndrew Thompson 	/* on short packet we zero the unused data */
73202ac6454SAndrew Thompson 	if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) {
73302ac6454SAndrew Thompson 		if (err)
73402ac6454SAndrew Thompson 			memset(data, 0, len);
73502ac6454SAndrew Thompson 		else if (actlen && *actlen != len)
73602ac6454SAndrew Thompson 			memset(((uint8_t *)data) + *actlen, 0, len - *actlen);
73702ac6454SAndrew Thompson 	}
73802ac6454SAndrew Thompson 	return (err);
73902ac6454SAndrew Thompson }
74002ac6454SAndrew Thompson 
74102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
742a593f6b8SAndrew Thompson  *	usbd_req_reset_port
74302ac6454SAndrew Thompson  *
744cbb75751SHans Petter Selasky  * This function will instruct a USB HUB to perform a reset sequence
74502ac6454SAndrew Thompson  * on the specified port number.
74602ac6454SAndrew Thompson  *
74702ac6454SAndrew Thompson  * Returns:
74802ac6454SAndrew Thompson  *    0: Success. The USB device should now be at address zero.
74902ac6454SAndrew Thompson  * Else: Failure. No USB device is present and the USB port should be
75002ac6454SAndrew Thompson  *       disabled.
75102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
752e0a69b51SAndrew Thompson usb_error_t
753a593f6b8SAndrew Thompson usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
75402ac6454SAndrew Thompson {
755760bc48eSAndrew Thompson 	struct usb_port_status ps;
756e0a69b51SAndrew Thompson 	usb_error_t err;
75702ac6454SAndrew Thompson 	uint16_t n;
75802ac6454SAndrew Thompson 
759b850ecc1SAndrew Thompson #ifdef USB_DEBUG
76002ac6454SAndrew Thompson 	uint16_t pr_poll_delay;
76102ac6454SAndrew Thompson 	uint16_t pr_recovery_delay;
76202ac6454SAndrew Thompson 
76302ac6454SAndrew Thompson #endif
764a593f6b8SAndrew Thompson 	err = usbd_req_set_port_feature(udev, mtx, port, UHF_PORT_RESET);
76502ac6454SAndrew Thompson 	if (err) {
76602ac6454SAndrew Thompson 		goto done;
76702ac6454SAndrew Thompson 	}
768b850ecc1SAndrew Thompson #ifdef USB_DEBUG
76902ac6454SAndrew Thompson 	/* range check input parameters */
770a593f6b8SAndrew Thompson 	pr_poll_delay = usb_pr_poll_delay;
77102ac6454SAndrew Thompson 	if (pr_poll_delay < 1) {
77202ac6454SAndrew Thompson 		pr_poll_delay = 1;
77302ac6454SAndrew Thompson 	} else if (pr_poll_delay > 1000) {
77402ac6454SAndrew Thompson 		pr_poll_delay = 1000;
77502ac6454SAndrew Thompson 	}
776a593f6b8SAndrew Thompson 	pr_recovery_delay = usb_pr_recovery_delay;
77702ac6454SAndrew Thompson 	if (pr_recovery_delay > 1000) {
77802ac6454SAndrew Thompson 		pr_recovery_delay = 1000;
77902ac6454SAndrew Thompson 	}
78002ac6454SAndrew Thompson #endif
78102ac6454SAndrew Thompson 	n = 0;
78202ac6454SAndrew Thompson 	while (1) {
783b850ecc1SAndrew Thompson #ifdef USB_DEBUG
78402ac6454SAndrew Thompson 		/* wait for the device to recover from reset */
785a593f6b8SAndrew Thompson 		usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay));
78602ac6454SAndrew Thompson 		n += pr_poll_delay;
78702ac6454SAndrew Thompson #else
78802ac6454SAndrew Thompson 		/* wait for the device to recover from reset */
789a593f6b8SAndrew Thompson 		usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY));
79002ac6454SAndrew Thompson 		n += USB_PORT_RESET_DELAY;
79102ac6454SAndrew Thompson #endif
792a593f6b8SAndrew Thompson 		err = usbd_req_get_port_status(udev, mtx, &ps, port);
79302ac6454SAndrew Thompson 		if (err) {
79402ac6454SAndrew Thompson 			goto done;
79502ac6454SAndrew Thompson 		}
796*0e777d84SHans Petter Selasky 		/* if the device disappeared, just give up */
797*0e777d84SHans Petter Selasky 		if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) {
798*0e777d84SHans Petter Selasky 			goto done;
799*0e777d84SHans Petter Selasky 		}
80002ac6454SAndrew Thompson 		/* check if reset is complete */
80102ac6454SAndrew Thompson 		if (UGETW(ps.wPortChange) & UPS_C_PORT_RESET) {
80202ac6454SAndrew Thompson 			break;
80302ac6454SAndrew Thompson 		}
80402ac6454SAndrew Thompson 		/* check for timeout */
80502ac6454SAndrew Thompson 		if (n > 1000) {
80602ac6454SAndrew Thompson 			n = 0;
80702ac6454SAndrew Thompson 			break;
80802ac6454SAndrew Thompson 		}
80902ac6454SAndrew Thompson 	}
81002ac6454SAndrew Thompson 
81102ac6454SAndrew Thompson 	/* clear port reset first */
812a593f6b8SAndrew Thompson 	err = usbd_req_clear_port_feature(
81302ac6454SAndrew Thompson 	    udev, mtx, port, UHF_C_PORT_RESET);
81402ac6454SAndrew Thompson 	if (err) {
81502ac6454SAndrew Thompson 		goto done;
81602ac6454SAndrew Thompson 	}
81702ac6454SAndrew Thompson 	/* check for timeout */
81802ac6454SAndrew Thompson 	if (n == 0) {
81902ac6454SAndrew Thompson 		err = USB_ERR_TIMEOUT;
82002ac6454SAndrew Thompson 		goto done;
82102ac6454SAndrew Thompson 	}
822b850ecc1SAndrew Thompson #ifdef USB_DEBUG
82302ac6454SAndrew Thompson 	/* wait for the device to recover from reset */
824a593f6b8SAndrew Thompson 	usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay));
82502ac6454SAndrew Thompson #else
82602ac6454SAndrew Thompson 	/* wait for the device to recover from reset */
827a593f6b8SAndrew Thompson 	usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY));
82802ac6454SAndrew Thompson #endif
82902ac6454SAndrew Thompson 
83002ac6454SAndrew Thompson done:
83102ac6454SAndrew Thompson 	DPRINTFN(2, "port %d reset returning error=%s\n",
832a593f6b8SAndrew Thompson 	    port, usbd_errstr(err));
83302ac6454SAndrew Thompson 	return (err);
83402ac6454SAndrew Thompson }
83502ac6454SAndrew Thompson 
83602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
837cbb75751SHans Petter Selasky  *	usbd_req_warm_reset_port
838cbb75751SHans Petter Selasky  *
839cbb75751SHans Petter Selasky  * This function will instruct an USB HUB to perform a warm reset
840cbb75751SHans Petter Selasky  * sequence on the specified port number. This kind of reset is not
841cbb75751SHans Petter Selasky  * mandatory for LOW-, FULL- and HIGH-speed USB HUBs and is targeted
842cbb75751SHans Petter Selasky  * for SUPER-speed USB HUBs.
843cbb75751SHans Petter Selasky  *
844cbb75751SHans Petter Selasky  * Returns:
845cbb75751SHans Petter Selasky  *    0: Success. The USB device should now be available again.
846cbb75751SHans Petter Selasky  * Else: Failure. No USB device is present and the USB port should be
847cbb75751SHans Petter Selasky  *       disabled.
848cbb75751SHans Petter Selasky  *------------------------------------------------------------------------*/
849cbb75751SHans Petter Selasky usb_error_t
850cbb75751SHans Petter Selasky usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
851cbb75751SHans Petter Selasky {
852cbb75751SHans Petter Selasky 	struct usb_port_status ps;
853cbb75751SHans Petter Selasky 	usb_error_t err;
854cbb75751SHans Petter Selasky 	uint16_t n;
855cbb75751SHans Petter Selasky 
856cbb75751SHans Petter Selasky #ifdef USB_DEBUG
857cbb75751SHans Petter Selasky 	uint16_t pr_poll_delay;
858cbb75751SHans Petter Selasky 	uint16_t pr_recovery_delay;
859cbb75751SHans Petter Selasky 
860cbb75751SHans Petter Selasky #endif
861cbb75751SHans Petter Selasky 	err = usbd_req_set_port_feature(udev, mtx, port, UHF_BH_PORT_RESET);
862cbb75751SHans Petter Selasky 	if (err) {
863cbb75751SHans Petter Selasky 		goto done;
864cbb75751SHans Petter Selasky 	}
865cbb75751SHans Petter Selasky #ifdef USB_DEBUG
866cbb75751SHans Petter Selasky 	/* range check input parameters */
867cbb75751SHans Petter Selasky 	pr_poll_delay = usb_pr_poll_delay;
868cbb75751SHans Petter Selasky 	if (pr_poll_delay < 1) {
869cbb75751SHans Petter Selasky 		pr_poll_delay = 1;
870cbb75751SHans Petter Selasky 	} else if (pr_poll_delay > 1000) {
871cbb75751SHans Petter Selasky 		pr_poll_delay = 1000;
872cbb75751SHans Petter Selasky 	}
873cbb75751SHans Petter Selasky 	pr_recovery_delay = usb_pr_recovery_delay;
874cbb75751SHans Petter Selasky 	if (pr_recovery_delay > 1000) {
875cbb75751SHans Petter Selasky 		pr_recovery_delay = 1000;
876cbb75751SHans Petter Selasky 	}
877cbb75751SHans Petter Selasky #endif
878cbb75751SHans Petter Selasky 	n = 0;
879cbb75751SHans Petter Selasky 	while (1) {
880cbb75751SHans Petter Selasky #ifdef USB_DEBUG
881cbb75751SHans Petter Selasky 		/* wait for the device to recover from reset */
882cbb75751SHans Petter Selasky 		usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_poll_delay));
883cbb75751SHans Petter Selasky 		n += pr_poll_delay;
884cbb75751SHans Petter Selasky #else
885cbb75751SHans Petter Selasky 		/* wait for the device to recover from reset */
886cbb75751SHans Petter Selasky 		usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_DELAY));
887cbb75751SHans Petter Selasky 		n += USB_PORT_RESET_DELAY;
888cbb75751SHans Petter Selasky #endif
889cbb75751SHans Petter Selasky 		err = usbd_req_get_port_status(udev, mtx, &ps, port);
890cbb75751SHans Petter Selasky 		if (err) {
891cbb75751SHans Petter Selasky 			goto done;
892cbb75751SHans Petter Selasky 		}
893cbb75751SHans Petter Selasky 		/* if the device disappeared, just give up */
894cbb75751SHans Petter Selasky 		if (!(UGETW(ps.wPortStatus) & UPS_CURRENT_CONNECT_STATUS)) {
895cbb75751SHans Petter Selasky 			goto done;
896cbb75751SHans Petter Selasky 		}
897cbb75751SHans Petter Selasky 		/* check if reset is complete */
898cbb75751SHans Petter Selasky 		if (UGETW(ps.wPortChange) & UPS_C_BH_PORT_RESET) {
899cbb75751SHans Petter Selasky 			break;
900cbb75751SHans Petter Selasky 		}
901cbb75751SHans Petter Selasky 		/* check for timeout */
902cbb75751SHans Petter Selasky 		if (n > 1000) {
903cbb75751SHans Petter Selasky 			n = 0;
904cbb75751SHans Petter Selasky 			break;
905cbb75751SHans Petter Selasky 		}
906cbb75751SHans Petter Selasky 	}
907cbb75751SHans Petter Selasky 
908cbb75751SHans Petter Selasky 	/* clear port reset first */
909cbb75751SHans Petter Selasky 	err = usbd_req_clear_port_feature(
910cbb75751SHans Petter Selasky 	    udev, mtx, port, UHF_C_BH_PORT_RESET);
911cbb75751SHans Petter Selasky 	if (err) {
912cbb75751SHans Petter Selasky 		goto done;
913cbb75751SHans Petter Selasky 	}
914cbb75751SHans Petter Selasky 	/* check for timeout */
915cbb75751SHans Petter Selasky 	if (n == 0) {
916cbb75751SHans Petter Selasky 		err = USB_ERR_TIMEOUT;
917cbb75751SHans Petter Selasky 		goto done;
918cbb75751SHans Petter Selasky 	}
919cbb75751SHans Petter Selasky #ifdef USB_DEBUG
920cbb75751SHans Petter Selasky 	/* wait for the device to recover from reset */
921cbb75751SHans Petter Selasky 	usb_pause_mtx(mtx, USB_MS_TO_TICKS(pr_recovery_delay));
922cbb75751SHans Petter Selasky #else
923cbb75751SHans Petter Selasky 	/* wait for the device to recover from reset */
924cbb75751SHans Petter Selasky 	usb_pause_mtx(mtx, USB_MS_TO_TICKS(USB_PORT_RESET_RECOVERY));
925cbb75751SHans Petter Selasky #endif
926cbb75751SHans Petter Selasky 
927cbb75751SHans Petter Selasky done:
928cbb75751SHans Petter Selasky 	DPRINTFN(2, "port %d warm reset returning error=%s\n",
929cbb75751SHans Petter Selasky 	    port, usbd_errstr(err));
930cbb75751SHans Petter Selasky 	return (err);
931cbb75751SHans Petter Selasky }
932cbb75751SHans Petter Selasky 
933cbb75751SHans Petter Selasky /*------------------------------------------------------------------------*
934a593f6b8SAndrew Thompson  *	usbd_req_get_desc
93502ac6454SAndrew Thompson  *
93602ac6454SAndrew Thompson  * This function can be used to retrieve USB descriptors. It contains
93702ac6454SAndrew Thompson  * some additional logic like zeroing of missing descriptor bytes and
93802ac6454SAndrew Thompson  * retrying an USB descriptor in case of failure. The "min_len"
93902ac6454SAndrew Thompson  * argument specifies the minimum descriptor length. The "max_len"
94002ac6454SAndrew Thompson  * argument specifies the maximum descriptor length. If the real
94102ac6454SAndrew Thompson  * descriptor length is less than the minimum length the missing
94216589beaSAndrew Thompson  * byte(s) will be zeroed. The type field, the second byte of the USB
94316589beaSAndrew Thompson  * descriptor, will get forced to the correct type. If the "actlen"
94416589beaSAndrew Thompson  * pointer is non-NULL, the actual length of the transfer will get
94516589beaSAndrew Thompson  * stored in the 16-bit unsigned integer which it is pointing to. The
94616589beaSAndrew Thompson  * first byte of the descriptor will not get updated. If the "actlen"
94716589beaSAndrew Thompson  * pointer is NULL the first byte of the descriptor will get updated
94816589beaSAndrew Thompson  * to reflect the actual length instead. If "min_len" is not equal to
94916589beaSAndrew Thompson  * "max_len" then this function will try to retrive the beginning of
95016589beaSAndrew Thompson  * the descriptor and base the maximum length on the first byte of the
95116589beaSAndrew Thompson  * descriptor.
95202ac6454SAndrew Thompson  *
95302ac6454SAndrew Thompson  * Returns:
95402ac6454SAndrew Thompson  *    0: Success
95502ac6454SAndrew Thompson  * Else: Failure
95602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
957e0a69b51SAndrew Thompson usb_error_t
958a593f6b8SAndrew Thompson usbd_req_get_desc(struct usb_device *udev,
95916589beaSAndrew Thompson     struct mtx *mtx, uint16_t *actlen, void *desc,
96002ac6454SAndrew Thompson     uint16_t min_len, uint16_t max_len,
96102ac6454SAndrew Thompson     uint16_t id, uint8_t type, uint8_t index,
96202ac6454SAndrew Thompson     uint8_t retries)
96302ac6454SAndrew Thompson {
964760bc48eSAndrew Thompson 	struct usb_device_request req;
96502ac6454SAndrew Thompson 	uint8_t *buf;
966e0a69b51SAndrew Thompson 	usb_error_t err;
96702ac6454SAndrew Thompson 
96802ac6454SAndrew Thompson 	DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n",
96902ac6454SAndrew Thompson 	    id, type, index, max_len);
97002ac6454SAndrew Thompson 
97102ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_DEVICE;
97202ac6454SAndrew Thompson 	req.bRequest = UR_GET_DESCRIPTOR;
97302ac6454SAndrew Thompson 	USETW2(req.wValue, type, index);
97402ac6454SAndrew Thompson 	USETW(req.wIndex, id);
97502ac6454SAndrew Thompson 
97602ac6454SAndrew Thompson 	while (1) {
97702ac6454SAndrew Thompson 
97802ac6454SAndrew Thompson 		if ((min_len < 2) || (max_len < 2)) {
97902ac6454SAndrew Thompson 			err = USB_ERR_INVAL;
98002ac6454SAndrew Thompson 			goto done;
98102ac6454SAndrew Thompson 		}
98202ac6454SAndrew Thompson 		USETW(req.wLength, min_len);
98302ac6454SAndrew Thompson 
984a593f6b8SAndrew Thompson 		err = usbd_do_request_flags(udev, mtx, &req,
98502ac6454SAndrew Thompson 		    desc, 0, NULL, 1000);
98602ac6454SAndrew Thompson 
98702ac6454SAndrew Thompson 		if (err) {
98802ac6454SAndrew Thompson 			if (!retries) {
98902ac6454SAndrew Thompson 				goto done;
99002ac6454SAndrew Thompson 			}
99102ac6454SAndrew Thompson 			retries--;
99202ac6454SAndrew Thompson 
993a593f6b8SAndrew Thompson 			usb_pause_mtx(mtx, hz / 5);
99402ac6454SAndrew Thompson 
99502ac6454SAndrew Thompson 			continue;
99602ac6454SAndrew Thompson 		}
99702ac6454SAndrew Thompson 		buf = desc;
99802ac6454SAndrew Thompson 
99902ac6454SAndrew Thompson 		if (min_len == max_len) {
100002ac6454SAndrew Thompson 
100116589beaSAndrew Thompson 			/* enforce correct length */
100216589beaSAndrew Thompson 			if ((buf[0] > min_len) && (actlen == NULL))
100302ac6454SAndrew Thompson 				buf[0] = min_len;
100416589beaSAndrew Thompson 
100516589beaSAndrew Thompson 			/* enforce correct type */
100602ac6454SAndrew Thompson 			buf[1] = type;
100702ac6454SAndrew Thompson 
100802ac6454SAndrew Thompson 			goto done;
100902ac6454SAndrew Thompson 		}
101002ac6454SAndrew Thompson 		/* range check */
101102ac6454SAndrew Thompson 
101202ac6454SAndrew Thompson 		if (max_len > buf[0]) {
101302ac6454SAndrew Thompson 			max_len = buf[0];
101402ac6454SAndrew Thompson 		}
101502ac6454SAndrew Thompson 		/* zero minimum data */
101602ac6454SAndrew Thompson 
101702ac6454SAndrew Thompson 		while (min_len > max_len) {
101802ac6454SAndrew Thompson 			min_len--;
101902ac6454SAndrew Thompson 			buf[min_len] = 0;
102002ac6454SAndrew Thompson 		}
102102ac6454SAndrew Thompson 
102202ac6454SAndrew Thompson 		/* set new minimum length */
102302ac6454SAndrew Thompson 
102402ac6454SAndrew Thompson 		min_len = max_len;
102502ac6454SAndrew Thompson 	}
102602ac6454SAndrew Thompson done:
102716589beaSAndrew Thompson 	if (actlen != NULL) {
102816589beaSAndrew Thompson 		if (err)
102916589beaSAndrew Thompson 			*actlen = 0;
103016589beaSAndrew Thompson 		else
103116589beaSAndrew Thompson 			*actlen = min_len;
103216589beaSAndrew Thompson 	}
103302ac6454SAndrew Thompson 	return (err);
103402ac6454SAndrew Thompson }
103502ac6454SAndrew Thompson 
103602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1037a593f6b8SAndrew Thompson  *	usbd_req_get_string_any
103802ac6454SAndrew Thompson  *
103902ac6454SAndrew Thompson  * This function will return the string given by "string_index"
104002ac6454SAndrew Thompson  * using the first language ID. The maximum length "len" includes
104102ac6454SAndrew Thompson  * the terminating zero. The "len" argument should be twice as
104202ac6454SAndrew Thompson  * big pluss 2 bytes, compared with the actual maximum string length !
104302ac6454SAndrew Thompson  *
104402ac6454SAndrew Thompson  * Returns:
104502ac6454SAndrew Thompson  *    0: Success
104602ac6454SAndrew Thompson  * Else: Failure
104702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1048e0a69b51SAndrew Thompson usb_error_t
1049a593f6b8SAndrew Thompson usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf,
105002ac6454SAndrew Thompson     uint16_t len, uint8_t string_index)
105102ac6454SAndrew Thompson {
105202ac6454SAndrew Thompson 	char *s;
105302ac6454SAndrew Thompson 	uint8_t *temp;
105402ac6454SAndrew Thompson 	uint16_t i;
105502ac6454SAndrew Thompson 	uint16_t n;
105602ac6454SAndrew Thompson 	uint16_t c;
105702ac6454SAndrew Thompson 	uint8_t swap;
1058e0a69b51SAndrew Thompson 	usb_error_t err;
105902ac6454SAndrew Thompson 
106002ac6454SAndrew Thompson 	if (len == 0) {
106102ac6454SAndrew Thompson 		/* should not happen */
106202ac6454SAndrew Thompson 		return (USB_ERR_NORMAL_COMPLETION);
106302ac6454SAndrew Thompson 	}
106402ac6454SAndrew Thompson 	if (string_index == 0) {
106502ac6454SAndrew Thompson 		/* this is the language table */
106602ac6454SAndrew Thompson 		buf[0] = 0;
106702ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
106802ac6454SAndrew Thompson 	}
106902ac6454SAndrew Thompson 	if (udev->flags.no_strings) {
107002ac6454SAndrew Thompson 		buf[0] = 0;
107102ac6454SAndrew Thompson 		return (USB_ERR_STALLED);
107202ac6454SAndrew Thompson 	}
1073a593f6b8SAndrew Thompson 	err = usbd_req_get_string_desc
107402ac6454SAndrew Thompson 	    (udev, mtx, buf, len, udev->langid, string_index);
107502ac6454SAndrew Thompson 	if (err) {
107602ac6454SAndrew Thompson 		buf[0] = 0;
107702ac6454SAndrew Thompson 		return (err);
107802ac6454SAndrew Thompson 	}
107902ac6454SAndrew Thompson 	temp = (uint8_t *)buf;
108002ac6454SAndrew Thompson 
108102ac6454SAndrew Thompson 	if (temp[0] < 2) {
108202ac6454SAndrew Thompson 		/* string length is too short */
108302ac6454SAndrew Thompson 		buf[0] = 0;
108402ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
108502ac6454SAndrew Thompson 	}
108602ac6454SAndrew Thompson 	/* reserve one byte for terminating zero */
108702ac6454SAndrew Thompson 	len--;
108802ac6454SAndrew Thompson 
108902ac6454SAndrew Thompson 	/* find maximum length */
109002ac6454SAndrew Thompson 	s = buf;
109102ac6454SAndrew Thompson 	n = (temp[0] / 2) - 1;
109202ac6454SAndrew Thompson 	if (n > len) {
109302ac6454SAndrew Thompson 		n = len;
109402ac6454SAndrew Thompson 	}
109502ac6454SAndrew Thompson 	/* skip descriptor header */
109602ac6454SAndrew Thompson 	temp += 2;
109702ac6454SAndrew Thompson 
109802ac6454SAndrew Thompson 	/* reset swap state */
109902ac6454SAndrew Thompson 	swap = 3;
110002ac6454SAndrew Thompson 
110102ac6454SAndrew Thompson 	/* convert and filter */
110202ac6454SAndrew Thompson 	for (i = 0; (i != n); i++) {
110302ac6454SAndrew Thompson 		c = UGETW(temp + (2 * i));
110402ac6454SAndrew Thompson 
110502ac6454SAndrew Thompson 		/* convert from Unicode, handle buggy strings */
110602ac6454SAndrew Thompson 		if (((c & 0xff00) == 0) && (swap & 1)) {
110702ac6454SAndrew Thompson 			/* Little Endian, default */
110802ac6454SAndrew Thompson 			*s = c;
110902ac6454SAndrew Thompson 			swap = 1;
111002ac6454SAndrew Thompson 		} else if (((c & 0x00ff) == 0) && (swap & 2)) {
111102ac6454SAndrew Thompson 			/* Big Endian */
111202ac6454SAndrew Thompson 			*s = c >> 8;
111302ac6454SAndrew Thompson 			swap = 2;
111402ac6454SAndrew Thompson 		} else {
111502ac6454SAndrew Thompson 			/* silently skip bad character */
111602ac6454SAndrew Thompson 			continue;
111702ac6454SAndrew Thompson 		}
111802ac6454SAndrew Thompson 
111902ac6454SAndrew Thompson 		/*
1120b64cf89fSHans Petter Selasky 		 * Filter by default - We only allow alphanumerical
1121b64cf89fSHans Petter Selasky 		 * and a few more to avoid any problems with scripts
1122b64cf89fSHans Petter Selasky 		 * and daemons.
112302ac6454SAndrew Thompson 		 */
1124b64cf89fSHans Petter Selasky 		if (isalpha(*s) ||
1125b64cf89fSHans Petter Selasky 		    isdigit(*s) ||
1126b64cf89fSHans Petter Selasky 		    *s == '-' ||
1127b64cf89fSHans Petter Selasky 		    *s == '+' ||
1128b64cf89fSHans Petter Selasky 		    *s == ' ' ||
1129b64cf89fSHans Petter Selasky 		    *s == '.' ||
1130b64cf89fSHans Petter Selasky 		    *s == ',') {
1131b64cf89fSHans Petter Selasky 			/* allowed */
113202ac6454SAndrew Thompson 			s++;
113302ac6454SAndrew Thompson 		}
1134b64cf89fSHans Petter Selasky 		/* silently skip bad character */
1135b64cf89fSHans Petter Selasky 	}
113602ac6454SAndrew Thompson 	*s = 0;				/* zero terminate resulting string */
113702ac6454SAndrew Thompson 	return (USB_ERR_NORMAL_COMPLETION);
113802ac6454SAndrew Thompson }
113902ac6454SAndrew Thompson 
114002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1141a593f6b8SAndrew Thompson  *	usbd_req_get_string_desc
114202ac6454SAndrew Thompson  *
114302ac6454SAndrew Thompson  * If you don't know the language ID, consider using
1144a593f6b8SAndrew Thompson  * "usbd_req_get_string_any()".
114502ac6454SAndrew Thompson  *
114602ac6454SAndrew Thompson  * Returns:
114702ac6454SAndrew Thompson  *    0: Success
114802ac6454SAndrew Thompson  * Else: Failure
114902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1150e0a69b51SAndrew Thompson usb_error_t
1151a593f6b8SAndrew Thompson usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc,
115202ac6454SAndrew Thompson     uint16_t max_len, uint16_t lang_id,
115302ac6454SAndrew Thompson     uint8_t string_index)
115402ac6454SAndrew Thompson {
1155a593f6b8SAndrew Thompson 	return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id,
115602ac6454SAndrew Thompson 	    UDESC_STRING, string_index, 0));
115702ac6454SAndrew Thompson }
115802ac6454SAndrew Thompson 
115902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1160a593f6b8SAndrew Thompson  *	usbd_req_get_config_desc_ptr
11617efaaa9aSAndrew Thompson  *
11627efaaa9aSAndrew Thompson  * This function is used in device side mode to retrieve the pointer
11637efaaa9aSAndrew Thompson  * to the generated config descriptor. This saves allocating space for
11647efaaa9aSAndrew Thompson  * an additional config descriptor when setting the configuration.
11657efaaa9aSAndrew Thompson  *
11667efaaa9aSAndrew Thompson  * Returns:
11677efaaa9aSAndrew Thompson  *    0: Success
11687efaaa9aSAndrew Thompson  * Else: Failure
11697efaaa9aSAndrew Thompson  *------------------------------------------------------------------------*/
1170e0a69b51SAndrew Thompson usb_error_t
1171a593f6b8SAndrew Thompson usbd_req_get_descriptor_ptr(struct usb_device *udev,
1172760bc48eSAndrew Thompson     struct usb_config_descriptor **ppcd, uint16_t wValue)
11737efaaa9aSAndrew Thompson {
1174760bc48eSAndrew Thompson 	struct usb_device_request req;
1175e0a69b51SAndrew Thompson 	usb_handle_req_t *hr_func;
1176459d369eSAndrew Thompson 	const void *ptr;
1177459d369eSAndrew Thompson 	uint16_t len;
1178e0a69b51SAndrew Thompson 	usb_error_t err;
11797efaaa9aSAndrew Thompson 
118063521bbcSAndrew Thompson 	req.bmRequestType = UT_READ_DEVICE;
11817efaaa9aSAndrew Thompson 	req.bRequest = UR_GET_DESCRIPTOR;
1182459d369eSAndrew Thompson 	USETW(req.wValue, wValue);
11837efaaa9aSAndrew Thompson 	USETW(req.wIndex, 0);
11847efaaa9aSAndrew Thompson 	USETW(req.wLength, 0);
11857efaaa9aSAndrew Thompson 
1186459d369eSAndrew Thompson 	ptr = NULL;
1187459d369eSAndrew Thompson 	len = 0;
11887efaaa9aSAndrew Thompson 
1189a593f6b8SAndrew Thompson 	hr_func = usbd_get_hr_func(udev);
1190459d369eSAndrew Thompson 
1191459d369eSAndrew Thompson 	if (hr_func == NULL)
1192459d369eSAndrew Thompson 		err = USB_ERR_INVAL;
1193459d369eSAndrew Thompson 	else {
1194459d369eSAndrew Thompson 		USB_BUS_LOCK(udev->bus);
1195459d369eSAndrew Thompson 		err = (hr_func) (udev, &req, &ptr, &len);
1196459d369eSAndrew Thompson 		USB_BUS_UNLOCK(udev->bus);
1197459d369eSAndrew Thompson 	}
1198459d369eSAndrew Thompson 
1199459d369eSAndrew Thompson 	if (err)
1200459d369eSAndrew Thompson 		ptr = NULL;
1201459d369eSAndrew Thompson 	else if (ptr == NULL)
1202459d369eSAndrew Thompson 		err = USB_ERR_INVAL;
1203459d369eSAndrew Thompson 
1204760bc48eSAndrew Thompson 	*ppcd = __DECONST(struct usb_config_descriptor *, ptr);
1205459d369eSAndrew Thompson 
1206459d369eSAndrew Thompson 	return (err);
12077efaaa9aSAndrew Thompson }
12087efaaa9aSAndrew Thompson 
12097efaaa9aSAndrew Thompson /*------------------------------------------------------------------------*
1210a593f6b8SAndrew Thompson  *	usbd_req_get_config_desc
121102ac6454SAndrew Thompson  *
121202ac6454SAndrew Thompson  * Returns:
121302ac6454SAndrew Thompson  *    0: Success
121402ac6454SAndrew Thompson  * Else: Failure
121502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1216e0a69b51SAndrew Thompson usb_error_t
1217a593f6b8SAndrew Thompson usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx,
1218760bc48eSAndrew Thompson     struct usb_config_descriptor *d, uint8_t conf_index)
121902ac6454SAndrew Thompson {
1220e0a69b51SAndrew Thompson 	usb_error_t err;
122102ac6454SAndrew Thompson 
122202ac6454SAndrew Thompson 	DPRINTFN(4, "confidx=%d\n", conf_index);
122302ac6454SAndrew Thompson 
1224a593f6b8SAndrew Thompson 	err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d),
122502ac6454SAndrew Thompson 	    sizeof(*d), 0, UDESC_CONFIG, conf_index, 0);
122602ac6454SAndrew Thompson 	if (err) {
122702ac6454SAndrew Thompson 		goto done;
122802ac6454SAndrew Thompson 	}
122902ac6454SAndrew Thompson 	/* Extra sanity checking */
123002ac6454SAndrew Thompson 	if (UGETW(d->wTotalLength) < sizeof(*d)) {
123102ac6454SAndrew Thompson 		err = USB_ERR_INVAL;
123202ac6454SAndrew Thompson 	}
123302ac6454SAndrew Thompson done:
123402ac6454SAndrew Thompson 	return (err);
123502ac6454SAndrew Thompson }
123602ac6454SAndrew Thompson 
123702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1238a593f6b8SAndrew Thompson  *	usbd_req_get_config_desc_full
123902ac6454SAndrew Thompson  *
124002ac6454SAndrew Thompson  * This function gets the complete USB configuration descriptor and
124102ac6454SAndrew Thompson  * ensures that "wTotalLength" is correct.
124202ac6454SAndrew Thompson  *
124302ac6454SAndrew Thompson  * Returns:
124402ac6454SAndrew Thompson  *    0: Success
124502ac6454SAndrew Thompson  * Else: Failure
124602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1247e0a69b51SAndrew Thompson usb_error_t
1248a593f6b8SAndrew Thompson usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx,
1249760bc48eSAndrew Thompson     struct usb_config_descriptor **ppcd, struct malloc_type *mtype,
125002ac6454SAndrew Thompson     uint8_t index)
125102ac6454SAndrew Thompson {
1252760bc48eSAndrew Thompson 	struct usb_config_descriptor cd;
1253760bc48eSAndrew Thompson 	struct usb_config_descriptor *cdesc;
125402ac6454SAndrew Thompson 	uint16_t len;
1255e0a69b51SAndrew Thompson 	usb_error_t err;
125602ac6454SAndrew Thompson 
125702ac6454SAndrew Thompson 	DPRINTFN(4, "index=%d\n", index);
125802ac6454SAndrew Thompson 
125902ac6454SAndrew Thompson 	*ppcd = NULL;
126002ac6454SAndrew Thompson 
1261a593f6b8SAndrew Thompson 	err = usbd_req_get_config_desc(udev, mtx, &cd, index);
126202ac6454SAndrew Thompson 	if (err) {
126302ac6454SAndrew Thompson 		return (err);
126402ac6454SAndrew Thompson 	}
126502ac6454SAndrew Thompson 	/* get full descriptor */
126602ac6454SAndrew Thompson 	len = UGETW(cd.wTotalLength);
126702ac6454SAndrew Thompson 	if (len < sizeof(*cdesc)) {
126802ac6454SAndrew Thompson 		/* corrupt descriptor */
126902ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
127002ac6454SAndrew Thompson 	}
127102ac6454SAndrew Thompson 	cdesc = malloc(len, mtype, M_WAITOK);
127202ac6454SAndrew Thompson 	if (cdesc == NULL) {
127302ac6454SAndrew Thompson 		return (USB_ERR_NOMEM);
127402ac6454SAndrew Thompson 	}
1275a593f6b8SAndrew Thompson 	err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0,
127602ac6454SAndrew Thompson 	    UDESC_CONFIG, index, 3);
127702ac6454SAndrew Thompson 	if (err) {
127802ac6454SAndrew Thompson 		free(cdesc, mtype);
127902ac6454SAndrew Thompson 		return (err);
128002ac6454SAndrew Thompson 	}
128102ac6454SAndrew Thompson 	/* make sure that the device is not fooling us: */
128202ac6454SAndrew Thompson 	USETW(cdesc->wTotalLength, len);
128302ac6454SAndrew Thompson 
128402ac6454SAndrew Thompson 	*ppcd = cdesc;
128502ac6454SAndrew Thompson 
128602ac6454SAndrew Thompson 	return (0);			/* success */
128702ac6454SAndrew Thompson }
128802ac6454SAndrew Thompson 
128902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1290a593f6b8SAndrew Thompson  *	usbd_req_get_device_desc
129102ac6454SAndrew Thompson  *
129202ac6454SAndrew Thompson  * Returns:
129302ac6454SAndrew Thompson  *    0: Success
129402ac6454SAndrew Thompson  * Else: Failure
129502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1296e0a69b51SAndrew Thompson usb_error_t
1297a593f6b8SAndrew Thompson usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx,
1298760bc48eSAndrew Thompson     struct usb_device_descriptor *d)
129902ac6454SAndrew Thompson {
130002ac6454SAndrew Thompson 	DPRINTFN(4, "\n");
1301a593f6b8SAndrew Thompson 	return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d),
130202ac6454SAndrew Thompson 	    sizeof(*d), 0, UDESC_DEVICE, 0, 3));
130302ac6454SAndrew Thompson }
130402ac6454SAndrew Thompson 
130502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1306a593f6b8SAndrew Thompson  *	usbd_req_get_alt_interface_no
130702ac6454SAndrew Thompson  *
130802ac6454SAndrew Thompson  * Returns:
130902ac6454SAndrew Thompson  *    0: Success
131002ac6454SAndrew Thompson  * Else: Failure
131102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1312e0a69b51SAndrew Thompson usb_error_t
1313a593f6b8SAndrew Thompson usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
131402ac6454SAndrew Thompson     uint8_t *alt_iface_no, uint8_t iface_index)
131502ac6454SAndrew Thompson {
1316a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1317760bc48eSAndrew Thompson 	struct usb_device_request req;
131802ac6454SAndrew Thompson 
1319bd73b187SAlfred Perlstein 	if ((iface == NULL) || (iface->idesc == NULL))
132002ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
1321bd73b187SAlfred Perlstein 
132202ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_INTERFACE;
132302ac6454SAndrew Thompson 	req.bRequest = UR_GET_INTERFACE;
132402ac6454SAndrew Thompson 	USETW(req.wValue, 0);
132502ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
132602ac6454SAndrew Thompson 	req.wIndex[1] = 0;
132702ac6454SAndrew Thompson 	USETW(req.wLength, 1);
1328a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, alt_iface_no));
132902ac6454SAndrew Thompson }
133002ac6454SAndrew Thompson 
133102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1332a593f6b8SAndrew Thompson  *	usbd_req_set_alt_interface_no
133302ac6454SAndrew Thompson  *
133402ac6454SAndrew Thompson  * Returns:
133502ac6454SAndrew Thompson  *    0: Success
133602ac6454SAndrew Thompson  * Else: Failure
133702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1338e0a69b51SAndrew Thompson usb_error_t
1339a593f6b8SAndrew Thompson usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
134002ac6454SAndrew Thompson     uint8_t iface_index, uint8_t alt_no)
134102ac6454SAndrew Thompson {
1342a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1343760bc48eSAndrew Thompson 	struct usb_device_request req;
134402ac6454SAndrew Thompson 
1345bd73b187SAlfred Perlstein 	if ((iface == NULL) || (iface->idesc == NULL))
134602ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
1347bd73b187SAlfred Perlstein 
134802ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_INTERFACE;
134902ac6454SAndrew Thompson 	req.bRequest = UR_SET_INTERFACE;
135002ac6454SAndrew Thompson 	req.wValue[0] = alt_no;
135102ac6454SAndrew Thompson 	req.wValue[1] = 0;
135202ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
135302ac6454SAndrew Thompson 	req.wIndex[1] = 0;
135402ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1355a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
135602ac6454SAndrew Thompson }
135702ac6454SAndrew Thompson 
135802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1359a593f6b8SAndrew Thompson  *	usbd_req_get_device_status
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_device_status(struct usb_device *udev, struct mtx *mtx,
1367760bc48eSAndrew Thompson     struct usb_status *st)
136802ac6454SAndrew Thompson {
1369760bc48eSAndrew Thompson 	struct usb_device_request req;
137002ac6454SAndrew Thompson 
137102ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_DEVICE;
137202ac6454SAndrew Thompson 	req.bRequest = UR_GET_STATUS;
137302ac6454SAndrew Thompson 	USETW(req.wValue, 0);
137402ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
137502ac6454SAndrew Thompson 	USETW(req.wLength, sizeof(*st));
1376a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, st));
137702ac6454SAndrew Thompson }
137802ac6454SAndrew Thompson 
137902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1380a593f6b8SAndrew Thompson  *	usbd_req_get_hub_descriptor
138102ac6454SAndrew Thompson  *
138202ac6454SAndrew Thompson  * Returns:
138302ac6454SAndrew Thompson  *    0: Success
138402ac6454SAndrew Thompson  * Else: Failure
138502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1386e0a69b51SAndrew Thompson usb_error_t
1387a593f6b8SAndrew Thompson usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx,
1388760bc48eSAndrew Thompson     struct usb_hub_descriptor *hd, uint8_t nports)
138902ac6454SAndrew Thompson {
1390760bc48eSAndrew Thompson 	struct usb_device_request req;
139102ac6454SAndrew Thompson 	uint16_t len = (nports + 7 + (8 * 8)) / 8;
139202ac6454SAndrew Thompson 
139302ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_CLASS_DEVICE;
139402ac6454SAndrew Thompson 	req.bRequest = UR_GET_DESCRIPTOR;
139502ac6454SAndrew Thompson 	USETW2(req.wValue, UDESC_HUB, 0);
139602ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
139702ac6454SAndrew Thompson 	USETW(req.wLength, len);
1398a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, hd));
139902ac6454SAndrew Thompson }
140002ac6454SAndrew Thompson 
140102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1402963169b4SHans Petter Selasky  *	usbd_req_get_ss_hub_descriptor
1403963169b4SHans Petter Selasky  *
1404963169b4SHans Petter Selasky  * Returns:
1405963169b4SHans Petter Selasky  *    0: Success
1406963169b4SHans Petter Selasky  * Else: Failure
1407963169b4SHans Petter Selasky  *------------------------------------------------------------------------*/
1408963169b4SHans Petter Selasky usb_error_t
1409963169b4SHans Petter Selasky usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx,
1410963169b4SHans Petter Selasky     struct usb_hub_ss_descriptor *hd, uint8_t nports)
1411963169b4SHans Petter Selasky {
1412963169b4SHans Petter Selasky 	struct usb_device_request req;
1413963169b4SHans Petter Selasky 	uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8);
1414963169b4SHans Petter Selasky 
1415963169b4SHans Petter Selasky 	req.bmRequestType = UT_READ_CLASS_DEVICE;
1416963169b4SHans Petter Selasky 	req.bRequest = UR_GET_DESCRIPTOR;
1417963169b4SHans Petter Selasky 	USETW2(req.wValue, UDESC_SS_HUB, 0);
1418963169b4SHans Petter Selasky 	USETW(req.wIndex, 0);
1419963169b4SHans Petter Selasky 	USETW(req.wLength, len);
1420963169b4SHans Petter Selasky 	return (usbd_do_request(udev, mtx, &req, hd));
1421963169b4SHans Petter Selasky }
1422963169b4SHans Petter Selasky 
1423963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
1424a593f6b8SAndrew Thompson  *	usbd_req_get_hub_status
142502ac6454SAndrew Thompson  *
142602ac6454SAndrew Thompson  * Returns:
142702ac6454SAndrew Thompson  *    0: Success
142802ac6454SAndrew Thompson  * Else: Failure
142902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1430e0a69b51SAndrew Thompson usb_error_t
1431a593f6b8SAndrew Thompson usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx,
1432760bc48eSAndrew Thompson     struct usb_hub_status *st)
143302ac6454SAndrew Thompson {
1434760bc48eSAndrew Thompson 	struct usb_device_request req;
143502ac6454SAndrew Thompson 
143602ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_CLASS_DEVICE;
143702ac6454SAndrew Thompson 	req.bRequest = UR_GET_STATUS;
143802ac6454SAndrew Thompson 	USETW(req.wValue, 0);
143902ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
1440760bc48eSAndrew Thompson 	USETW(req.wLength, sizeof(struct usb_hub_status));
1441a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, st));
144202ac6454SAndrew Thompson }
144302ac6454SAndrew Thompson 
144402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1445a593f6b8SAndrew Thompson  *	usbd_req_set_address
144602ac6454SAndrew Thompson  *
144702ac6454SAndrew Thompson  * This function is used to set the address for an USB device. After
144802ac6454SAndrew Thompson  * port reset the USB device will respond at address zero.
144902ac6454SAndrew Thompson  *
145002ac6454SAndrew Thompson  * Returns:
145102ac6454SAndrew Thompson  *    0: Success
145202ac6454SAndrew Thompson  * Else: Failure
145302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1454e0a69b51SAndrew Thompson usb_error_t
1455a593f6b8SAndrew Thompson usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr)
145602ac6454SAndrew Thompson {
1457760bc48eSAndrew Thompson 	struct usb_device_request req;
1458963169b4SHans Petter Selasky 	usb_error_t err;
145902ac6454SAndrew Thompson 
146002ac6454SAndrew Thompson 	DPRINTFN(6, "setting device address=%d\n", addr);
146102ac6454SAndrew Thompson 
146202ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_DEVICE;
146302ac6454SAndrew Thompson 	req.bRequest = UR_SET_ADDRESS;
146402ac6454SAndrew Thompson 	USETW(req.wValue, addr);
146502ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
146602ac6454SAndrew Thompson 	USETW(req.wLength, 0);
146702ac6454SAndrew Thompson 
1468963169b4SHans Petter Selasky 	err = USB_ERR_INVAL;
1469963169b4SHans Petter Selasky 
1470963169b4SHans Petter Selasky 	/* check if USB controller handles set address */
1471963169b4SHans Petter Selasky 	if (udev->bus->methods->set_address != NULL)
1472963169b4SHans Petter Selasky 		err = (udev->bus->methods->set_address) (udev, mtx, addr);
1473963169b4SHans Petter Selasky 
1474963169b4SHans Petter Selasky 	if (err != USB_ERR_INVAL)
1475963169b4SHans Petter Selasky 		goto done;
1476963169b4SHans Petter Selasky 
147702ac6454SAndrew Thompson 	/* Setting the address should not take more than 1 second ! */
1478963169b4SHans Petter Selasky 	err = usbd_do_request_flags(udev, mtx, &req, NULL,
1479963169b4SHans Petter Selasky 	    USB_DELAY_STATUS_STAGE, NULL, 1000);
1480963169b4SHans Petter Selasky 
1481963169b4SHans Petter Selasky done:
1482963169b4SHans Petter Selasky 	/* allow device time to set new address */
1483963169b4SHans Petter Selasky 	usb_pause_mtx(mtx,
1484963169b4SHans Petter Selasky 	    USB_MS_TO_TICKS(USB_SET_ADDRESS_SETTLE));
1485963169b4SHans Petter Selasky 
1486963169b4SHans Petter Selasky 	return (err);
148702ac6454SAndrew Thompson }
148802ac6454SAndrew Thompson 
148902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1490a593f6b8SAndrew Thompson  *	usbd_req_get_port_status
149102ac6454SAndrew Thompson  *
149202ac6454SAndrew Thompson  * Returns:
149302ac6454SAndrew Thompson  *    0: Success
149402ac6454SAndrew Thompson  * Else: Failure
149502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1496e0a69b51SAndrew Thompson usb_error_t
1497a593f6b8SAndrew Thompson usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx,
1498760bc48eSAndrew Thompson     struct usb_port_status *ps, uint8_t port)
149902ac6454SAndrew Thompson {
1500760bc48eSAndrew Thompson 	struct usb_device_request req;
150102ac6454SAndrew Thompson 
150202ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_CLASS_OTHER;
150302ac6454SAndrew Thompson 	req.bRequest = UR_GET_STATUS;
150402ac6454SAndrew Thompson 	USETW(req.wValue, 0);
150502ac6454SAndrew Thompson 	req.wIndex[0] = port;
150602ac6454SAndrew Thompson 	req.wIndex[1] = 0;
150702ac6454SAndrew Thompson 	USETW(req.wLength, sizeof *ps);
1508a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, ps));
150902ac6454SAndrew Thompson }
151002ac6454SAndrew Thompson 
151102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1512a593f6b8SAndrew Thompson  *	usbd_req_clear_hub_feature
151302ac6454SAndrew Thompson  *
151402ac6454SAndrew Thompson  * Returns:
151502ac6454SAndrew Thompson  *    0: Success
151602ac6454SAndrew Thompson  * Else: Failure
151702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1518e0a69b51SAndrew Thompson usb_error_t
1519a593f6b8SAndrew Thompson usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx,
152002ac6454SAndrew Thompson     uint16_t sel)
152102ac6454SAndrew Thompson {
1522760bc48eSAndrew Thompson 	struct usb_device_request req;
152302ac6454SAndrew Thompson 
152402ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
152502ac6454SAndrew Thompson 	req.bRequest = UR_CLEAR_FEATURE;
152602ac6454SAndrew Thompson 	USETW(req.wValue, sel);
152702ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
152802ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1529a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
153002ac6454SAndrew Thompson }
153102ac6454SAndrew Thompson 
153202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1533a593f6b8SAndrew Thompson  *	usbd_req_set_hub_feature
153402ac6454SAndrew Thompson  *
153502ac6454SAndrew Thompson  * Returns:
153602ac6454SAndrew Thompson  *    0: Success
153702ac6454SAndrew Thompson  * Else: Failure
153802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1539e0a69b51SAndrew Thompson usb_error_t
1540a593f6b8SAndrew Thompson usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx,
154102ac6454SAndrew Thompson     uint16_t sel)
154202ac6454SAndrew Thompson {
1543760bc48eSAndrew Thompson 	struct usb_device_request req;
154402ac6454SAndrew Thompson 
154502ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
154602ac6454SAndrew Thompson 	req.bRequest = UR_SET_FEATURE;
154702ac6454SAndrew Thompson 	USETW(req.wValue, sel);
154802ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
154902ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1550a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
155102ac6454SAndrew Thompson }
155202ac6454SAndrew Thompson 
155302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1554963169b4SHans Petter Selasky  *	usbd_req_set_hub_u1_timeout
1555963169b4SHans Petter Selasky  *
1556963169b4SHans Petter Selasky  * Returns:
1557963169b4SHans Petter Selasky  *    0: Success
1558963169b4SHans Petter Selasky  * Else: Failure
1559963169b4SHans Petter Selasky  *------------------------------------------------------------------------*/
1560963169b4SHans Petter Selasky usb_error_t
1561963169b4SHans Petter Selasky usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx,
1562963169b4SHans Petter Selasky     uint8_t port, uint8_t timeout)
1563963169b4SHans Petter Selasky {
1564963169b4SHans Petter Selasky 	struct usb_device_request req;
1565963169b4SHans Petter Selasky 
1566963169b4SHans Petter Selasky 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
1567963169b4SHans Petter Selasky 	req.bRequest = UR_SET_FEATURE;
1568963169b4SHans Petter Selasky 	USETW(req.wValue, UHF_PORT_U1_TIMEOUT);
1569963169b4SHans Petter Selasky 	req.wIndex[0] = port;
1570963169b4SHans Petter Selasky 	req.wIndex[1] = timeout;
1571963169b4SHans Petter Selasky 	USETW(req.wLength, 0);
1572963169b4SHans Petter Selasky 	return (usbd_do_request(udev, mtx, &req, 0));
1573963169b4SHans Petter Selasky }
1574963169b4SHans Petter Selasky 
1575963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
1576963169b4SHans Petter Selasky  *	usbd_req_set_hub_u2_timeout
1577963169b4SHans Petter Selasky  *
1578963169b4SHans Petter Selasky  * Returns:
1579963169b4SHans Petter Selasky  *    0: Success
1580963169b4SHans Petter Selasky  * Else: Failure
1581963169b4SHans Petter Selasky  *------------------------------------------------------------------------*/
1582963169b4SHans Petter Selasky usb_error_t
1583963169b4SHans Petter Selasky usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx,
1584963169b4SHans Petter Selasky     uint8_t port, uint8_t timeout)
1585963169b4SHans Petter Selasky {
1586963169b4SHans Petter Selasky 	struct usb_device_request req;
1587963169b4SHans Petter Selasky 
1588963169b4SHans Petter Selasky 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
1589963169b4SHans Petter Selasky 	req.bRequest = UR_SET_FEATURE;
1590963169b4SHans Petter Selasky 	USETW(req.wValue, UHF_PORT_U2_TIMEOUT);
1591963169b4SHans Petter Selasky 	req.wIndex[0] = port;
1592963169b4SHans Petter Selasky 	req.wIndex[1] = timeout;
1593963169b4SHans Petter Selasky 	USETW(req.wLength, 0);
1594963169b4SHans Petter Selasky 	return (usbd_do_request(udev, mtx, &req, 0));
1595963169b4SHans Petter Selasky }
1596963169b4SHans Petter Selasky 
1597963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
1598963169b4SHans Petter Selasky  *	usbd_req_set_hub_depth
1599963169b4SHans Petter Selasky  *
1600963169b4SHans Petter Selasky  * Returns:
1601963169b4SHans Petter Selasky  *    0: Success
1602963169b4SHans Petter Selasky  * Else: Failure
1603963169b4SHans Petter Selasky  *------------------------------------------------------------------------*/
1604963169b4SHans Petter Selasky usb_error_t
1605963169b4SHans Petter Selasky usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx,
1606963169b4SHans Petter Selasky     uint16_t depth)
1607963169b4SHans Petter Selasky {
1608963169b4SHans Petter Selasky 	struct usb_device_request req;
1609963169b4SHans Petter Selasky 
1610963169b4SHans Petter Selasky 	req.bmRequestType = UT_WRITE_CLASS_DEVICE;
1611963169b4SHans Petter Selasky 	req.bRequest = UR_SET_HUB_DEPTH;
1612963169b4SHans Petter Selasky 	USETW(req.wValue, depth);
1613963169b4SHans Petter Selasky 	USETW(req.wIndex, 0);
1614963169b4SHans Petter Selasky 	USETW(req.wLength, 0);
1615963169b4SHans Petter Selasky 	return (usbd_do_request(udev, mtx, &req, 0));
1616963169b4SHans Petter Selasky }
1617963169b4SHans Petter Selasky 
1618963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
1619a593f6b8SAndrew Thompson  *	usbd_req_clear_port_feature
162002ac6454SAndrew Thompson  *
162102ac6454SAndrew Thompson  * Returns:
162202ac6454SAndrew Thompson  *    0: Success
162302ac6454SAndrew Thompson  * Else: Failure
162402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1625e0a69b51SAndrew Thompson usb_error_t
1626a593f6b8SAndrew Thompson usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx,
162702ac6454SAndrew Thompson     uint8_t port, uint16_t sel)
162802ac6454SAndrew Thompson {
1629760bc48eSAndrew Thompson 	struct usb_device_request req;
163002ac6454SAndrew Thompson 
163102ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
163202ac6454SAndrew Thompson 	req.bRequest = UR_CLEAR_FEATURE;
163302ac6454SAndrew Thompson 	USETW(req.wValue, sel);
163402ac6454SAndrew Thompson 	req.wIndex[0] = port;
163502ac6454SAndrew Thompson 	req.wIndex[1] = 0;
163602ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1637a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
163802ac6454SAndrew Thompson }
163902ac6454SAndrew Thompson 
164002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1641a593f6b8SAndrew Thompson  *	usbd_req_set_port_feature
164202ac6454SAndrew Thompson  *
164302ac6454SAndrew Thompson  * Returns:
164402ac6454SAndrew Thompson  *    0: Success
164502ac6454SAndrew Thompson  * Else: Failure
164602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1647e0a69b51SAndrew Thompson usb_error_t
1648a593f6b8SAndrew Thompson usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx,
164902ac6454SAndrew Thompson     uint8_t port, uint16_t sel)
165002ac6454SAndrew Thompson {
1651760bc48eSAndrew Thompson 	struct usb_device_request req;
165202ac6454SAndrew Thompson 
165302ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_OTHER;
165402ac6454SAndrew Thompson 	req.bRequest = UR_SET_FEATURE;
165502ac6454SAndrew Thompson 	USETW(req.wValue, sel);
165602ac6454SAndrew Thompson 	req.wIndex[0] = port;
165702ac6454SAndrew Thompson 	req.wIndex[1] = 0;
165802ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1659a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
166002ac6454SAndrew Thompson }
166102ac6454SAndrew Thompson 
166202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1663a593f6b8SAndrew Thompson  *	usbd_req_set_protocol
166402ac6454SAndrew Thompson  *
166502ac6454SAndrew Thompson  * Returns:
166602ac6454SAndrew Thompson  *    0: Success
166702ac6454SAndrew Thompson  * Else: Failure
166802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1669e0a69b51SAndrew Thompson usb_error_t
1670a593f6b8SAndrew Thompson usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx,
167102ac6454SAndrew Thompson     uint8_t iface_index, uint16_t report)
167202ac6454SAndrew Thompson {
1673a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1674760bc48eSAndrew Thompson 	struct usb_device_request req;
167502ac6454SAndrew Thompson 
167602ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
167702ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
167802ac6454SAndrew Thompson 	}
167902ac6454SAndrew Thompson 	DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n",
168002ac6454SAndrew Thompson 	    iface, report, iface->idesc->bInterfaceNumber);
168102ac6454SAndrew Thompson 
168202ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
168302ac6454SAndrew Thompson 	req.bRequest = UR_SET_PROTOCOL;
168402ac6454SAndrew Thompson 	USETW(req.wValue, report);
168502ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
168602ac6454SAndrew Thompson 	req.wIndex[1] = 0;
168702ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1688a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
168902ac6454SAndrew Thompson }
169002ac6454SAndrew Thompson 
169102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1692a593f6b8SAndrew Thompson  *	usbd_req_set_report
169302ac6454SAndrew Thompson  *
169402ac6454SAndrew Thompson  * Returns:
169502ac6454SAndrew Thompson  *    0: Success
169602ac6454SAndrew Thompson  * Else: Failure
169702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1698e0a69b51SAndrew Thompson usb_error_t
1699a593f6b8SAndrew Thompson usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len,
170002ac6454SAndrew Thompson     uint8_t iface_index, uint8_t type, uint8_t id)
170102ac6454SAndrew Thompson {
1702a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1703760bc48eSAndrew Thompson 	struct usb_device_request req;
170402ac6454SAndrew Thompson 
170502ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
170602ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
170702ac6454SAndrew Thompson 	}
170802ac6454SAndrew Thompson 	DPRINTFN(5, "len=%d\n", len);
170902ac6454SAndrew Thompson 
171002ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
171102ac6454SAndrew Thompson 	req.bRequest = UR_SET_REPORT;
171202ac6454SAndrew Thompson 	USETW2(req.wValue, type, id);
171302ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
171402ac6454SAndrew Thompson 	req.wIndex[1] = 0;
171502ac6454SAndrew Thompson 	USETW(req.wLength, len);
1716a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, data));
171702ac6454SAndrew Thompson }
171802ac6454SAndrew Thompson 
171902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1720a593f6b8SAndrew Thompson  *	usbd_req_get_report
172102ac6454SAndrew Thompson  *
172202ac6454SAndrew Thompson  * Returns:
172302ac6454SAndrew Thompson  *    0: Success
172402ac6454SAndrew Thompson  * Else: Failure
172502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1726e0a69b51SAndrew Thompson usb_error_t
1727a593f6b8SAndrew Thompson usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data,
172802ac6454SAndrew Thompson     uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id)
172902ac6454SAndrew Thompson {
1730a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1731760bc48eSAndrew Thompson 	struct usb_device_request req;
173202ac6454SAndrew Thompson 
173302ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL) || (id == 0)) {
173402ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
173502ac6454SAndrew Thompson 	}
173602ac6454SAndrew Thompson 	DPRINTFN(5, "len=%d\n", len);
173702ac6454SAndrew Thompson 
173802ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_CLASS_INTERFACE;
173902ac6454SAndrew Thompson 	req.bRequest = UR_GET_REPORT;
174002ac6454SAndrew Thompson 	USETW2(req.wValue, type, id);
174102ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
174202ac6454SAndrew Thompson 	req.wIndex[1] = 0;
174302ac6454SAndrew Thompson 	USETW(req.wLength, len);
1744a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, data));
174502ac6454SAndrew Thompson }
174602ac6454SAndrew Thompson 
174702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1748a593f6b8SAndrew Thompson  *	usbd_req_set_idle
174902ac6454SAndrew Thompson  *
175002ac6454SAndrew Thompson  * Returns:
175102ac6454SAndrew Thompson  *    0: Success
175202ac6454SAndrew Thompson  * Else: Failure
175302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1754e0a69b51SAndrew Thompson usb_error_t
1755a593f6b8SAndrew Thompson usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx,
175602ac6454SAndrew Thompson     uint8_t iface_index, uint8_t duration, uint8_t id)
175702ac6454SAndrew Thompson {
1758a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1759760bc48eSAndrew Thompson 	struct usb_device_request req;
176002ac6454SAndrew Thompson 
176102ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
176202ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
176302ac6454SAndrew Thompson 	}
176402ac6454SAndrew Thompson 	DPRINTFN(5, "%d %d\n", duration, id);
176502ac6454SAndrew Thompson 
176602ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
176702ac6454SAndrew Thompson 	req.bRequest = UR_SET_IDLE;
176802ac6454SAndrew Thompson 	USETW2(req.wValue, duration, id);
176902ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
177002ac6454SAndrew Thompson 	req.wIndex[1] = 0;
177102ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1772a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
177302ac6454SAndrew Thompson }
177402ac6454SAndrew Thompson 
177502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1776a593f6b8SAndrew Thompson  *	usbd_req_get_report_descriptor
177702ac6454SAndrew Thompson  *
177802ac6454SAndrew Thompson  * Returns:
177902ac6454SAndrew Thompson  *    0: Success
178002ac6454SAndrew Thompson  * Else: Failure
178102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1782e0a69b51SAndrew Thompson usb_error_t
1783a593f6b8SAndrew Thompson usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx,
178402ac6454SAndrew Thompson     void *d, uint16_t size, uint8_t iface_index)
178502ac6454SAndrew Thompson {
1786a593f6b8SAndrew Thompson 	struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1787760bc48eSAndrew Thompson 	struct usb_device_request req;
178802ac6454SAndrew Thompson 
178902ac6454SAndrew Thompson 	if ((iface == NULL) || (iface->idesc == NULL)) {
179002ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
179102ac6454SAndrew Thompson 	}
179202ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_INTERFACE;
179302ac6454SAndrew Thompson 	req.bRequest = UR_GET_DESCRIPTOR;
179402ac6454SAndrew Thompson 	USETW2(req.wValue, UDESC_REPORT, 0);	/* report id should be 0 */
179502ac6454SAndrew Thompson 	req.wIndex[0] = iface->idesc->bInterfaceNumber;
179602ac6454SAndrew Thompson 	req.wIndex[1] = 0;
179702ac6454SAndrew Thompson 	USETW(req.wLength, size);
1798a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, d));
179902ac6454SAndrew Thompson }
180002ac6454SAndrew Thompson 
180102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1802a593f6b8SAndrew Thompson  *	usbd_req_set_config
180302ac6454SAndrew Thompson  *
180402ac6454SAndrew Thompson  * This function is used to select the current configuration number in
180502ac6454SAndrew Thompson  * both USB device side mode and USB host side mode. When setting the
180602ac6454SAndrew Thompson  * configuration the function of the interfaces can change.
180702ac6454SAndrew Thompson  *
180802ac6454SAndrew Thompson  * Returns:
180902ac6454SAndrew Thompson  *    0: Success
181002ac6454SAndrew Thompson  * Else: Failure
181102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1812e0a69b51SAndrew Thompson usb_error_t
1813a593f6b8SAndrew Thompson usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf)
181402ac6454SAndrew Thompson {
1815760bc48eSAndrew Thompson 	struct usb_device_request req;
181602ac6454SAndrew Thompson 
181702ac6454SAndrew Thompson 	DPRINTF("setting config %d\n", conf);
181802ac6454SAndrew Thompson 
181902ac6454SAndrew Thompson 	/* do "set configuration" request */
182002ac6454SAndrew Thompson 
182102ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_DEVICE;
182202ac6454SAndrew Thompson 	req.bRequest = UR_SET_CONFIG;
182302ac6454SAndrew Thompson 	req.wValue[0] = conf;
182402ac6454SAndrew Thompson 	req.wValue[1] = 0;
182502ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
182602ac6454SAndrew Thompson 	USETW(req.wLength, 0);
1827a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
182802ac6454SAndrew Thompson }
182902ac6454SAndrew Thompson 
183002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1831a593f6b8SAndrew Thompson  *	usbd_req_get_config
183202ac6454SAndrew Thompson  *
183302ac6454SAndrew Thompson  * Returns:
183402ac6454SAndrew Thompson  *    0: Success
183502ac6454SAndrew Thompson  * Else: Failure
183602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1837e0a69b51SAndrew Thompson usb_error_t
1838a593f6b8SAndrew Thompson usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf)
183902ac6454SAndrew Thompson {
1840760bc48eSAndrew Thompson 	struct usb_device_request req;
184102ac6454SAndrew Thompson 
184202ac6454SAndrew Thompson 	req.bmRequestType = UT_READ_DEVICE;
184302ac6454SAndrew Thompson 	req.bRequest = UR_GET_CONFIG;
184402ac6454SAndrew Thompson 	USETW(req.wValue, 0);
184502ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
184602ac6454SAndrew Thompson 	USETW(req.wLength, 1);
1847a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, pconf));
184802ac6454SAndrew Thompson }
184902ac6454SAndrew Thompson 
185002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1851963169b4SHans Petter Selasky  *	usbd_setup_device_desc
1852963169b4SHans Petter Selasky  *------------------------------------------------------------------------*/
1853963169b4SHans Petter Selasky usb_error_t
1854963169b4SHans Petter Selasky usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx)
1855963169b4SHans Petter Selasky {
1856963169b4SHans Petter Selasky 	usb_error_t err;
1857963169b4SHans Petter Selasky 
1858963169b4SHans Petter Selasky 	/*
1859963169b4SHans Petter Selasky 	 * Get the first 8 bytes of the device descriptor !
1860963169b4SHans Petter Selasky 	 *
1861963169b4SHans Petter Selasky 	 * NOTE: "usbd_do_request()" will check the device descriptor
1862963169b4SHans Petter Selasky 	 * next time we do a request to see if the maximum packet size
1863963169b4SHans Petter Selasky 	 * changed! The 8 first bytes of the device descriptor
1864963169b4SHans Petter Selasky 	 * contains the maximum packet size to use on control endpoint
1865963169b4SHans Petter Selasky 	 * 0. If this value is different from "USB_MAX_IPACKET" a new
1866963169b4SHans Petter Selasky 	 * USB control request will be setup!
1867963169b4SHans Petter Selasky 	 */
1868963169b4SHans Petter Selasky 	switch (udev->speed) {
1869963169b4SHans Petter Selasky 	case USB_SPEED_FULL:
1870963169b4SHans Petter Selasky 	case USB_SPEED_LOW:
1871963169b4SHans Petter Selasky 		err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc,
1872963169b4SHans Petter Selasky 		    USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
1873963169b4SHans Petter Selasky 		if (err != 0) {
1874963169b4SHans Petter Selasky 			DPRINTFN(0, "getting device descriptor "
1875963169b4SHans Petter Selasky 			    "at addr %d failed, %s\n", udev->address,
1876963169b4SHans Petter Selasky 			    usbd_errstr(err));
1877963169b4SHans Petter Selasky 			return (err);
1878963169b4SHans Petter Selasky 		}
1879963169b4SHans Petter Selasky 		break;
1880963169b4SHans Petter Selasky 	default:
1881963169b4SHans Petter Selasky 		DPRINTF("Minimum MaxPacketSize is large enough "
1882963169b4SHans Petter Selasky 		    "to hold the complete device descriptor\n");
1883963169b4SHans Petter Selasky 		break;
1884963169b4SHans Petter Selasky 	}
1885963169b4SHans Petter Selasky 
1886963169b4SHans Petter Selasky 	/* get the full device descriptor */
1887963169b4SHans Petter Selasky 	err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
1888963169b4SHans Petter Selasky 
1889963169b4SHans Petter Selasky 	/* try one more time, if error */
1890963169b4SHans Petter Selasky 	if (err)
1891963169b4SHans Petter Selasky 		err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
1892963169b4SHans Petter Selasky 
1893963169b4SHans Petter Selasky 	if (err) {
1894963169b4SHans Petter Selasky 		DPRINTF("addr=%d, getting full desc failed\n",
1895963169b4SHans Petter Selasky 		    udev->address);
1896963169b4SHans Petter Selasky 		return (err);
1897963169b4SHans Petter Selasky 	}
1898963169b4SHans Petter Selasky 
1899963169b4SHans Petter Selasky 	DPRINTF("adding unit addr=%d, rev=%02x, class=%d, "
1900963169b4SHans Petter Selasky 	    "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
1901963169b4SHans Petter Selasky 	    udev->address, UGETW(udev->ddesc.bcdUSB),
1902963169b4SHans Petter Selasky 	    udev->ddesc.bDeviceClass,
1903963169b4SHans Petter Selasky 	    udev->ddesc.bDeviceSubClass,
1904963169b4SHans Petter Selasky 	    udev->ddesc.bDeviceProtocol,
1905963169b4SHans Petter Selasky 	    udev->ddesc.bMaxPacketSize,
1906963169b4SHans Petter Selasky 	    udev->ddesc.bLength,
1907963169b4SHans Petter Selasky 	    udev->speed);
1908963169b4SHans Petter Selasky 
1909963169b4SHans Petter Selasky 	return (err);
1910963169b4SHans Petter Selasky }
1911963169b4SHans Petter Selasky 
1912963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
1913a593f6b8SAndrew Thompson  *	usbd_req_re_enumerate
191402ac6454SAndrew Thompson  *
191502ac6454SAndrew Thompson  * NOTE: After this function returns the hardware is in the
191602ac6454SAndrew Thompson  * unconfigured state! The application is responsible for setting a
191702ac6454SAndrew Thompson  * new configuration.
191802ac6454SAndrew Thompson  *
191902ac6454SAndrew Thompson  * Returns:
192002ac6454SAndrew Thompson  *    0: Success
192102ac6454SAndrew Thompson  * Else: Failure
192202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
1923e0a69b51SAndrew Thompson usb_error_t
1924a593f6b8SAndrew Thompson usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
192502ac6454SAndrew Thompson {
1926760bc48eSAndrew Thompson 	struct usb_device *parent_hub;
1927e0a69b51SAndrew Thompson 	usb_error_t err;
192802ac6454SAndrew Thompson 	uint8_t old_addr;
192902ac6454SAndrew Thompson 	uint8_t do_retry = 1;
193002ac6454SAndrew Thompson 
1931f29a0724SAndrew Thompson 	if (udev->flags.usb_mode != USB_MODE_HOST) {
193202ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
193302ac6454SAndrew Thompson 	}
193402ac6454SAndrew Thompson 	old_addr = udev->address;
193502ac6454SAndrew Thompson 	parent_hub = udev->parent_hub;
193602ac6454SAndrew Thompson 	if (parent_hub == NULL) {
193702ac6454SAndrew Thompson 		return (USB_ERR_INVAL);
193802ac6454SAndrew Thompson 	}
193902ac6454SAndrew Thompson retry:
1940a593f6b8SAndrew Thompson 	err = usbd_req_reset_port(parent_hub, mtx, udev->port_no);
194102ac6454SAndrew Thompson 	if (err) {
194203797f33SAndrew Thompson 		DPRINTFN(0, "addr=%d, port reset failed, %s\n",
1943a593f6b8SAndrew Thompson 		    old_addr, usbd_errstr(err));
194402ac6454SAndrew Thompson 		goto done;
194502ac6454SAndrew Thompson 	}
1946963169b4SHans Petter Selasky 
194702ac6454SAndrew Thompson 	/*
194802ac6454SAndrew Thompson 	 * After that the port has been reset our device should be at
194902ac6454SAndrew Thompson 	 * address zero:
195002ac6454SAndrew Thompson 	 */
195102ac6454SAndrew Thompson 	udev->address = USB_START_ADDR;
195202ac6454SAndrew Thompson 
195302ac6454SAndrew Thompson 	/* reset "bMaxPacketSize" */
195402ac6454SAndrew Thompson 	udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET;
195502ac6454SAndrew Thompson 
1956963169b4SHans Petter Selasky 	/* reset USB state */
1957963169b4SHans Petter Selasky 	usb_set_device_state(udev, USB_STATE_POWERED);
1958963169b4SHans Petter Selasky 
195902ac6454SAndrew Thompson 	/*
196002ac6454SAndrew Thompson 	 * Restore device address:
196102ac6454SAndrew Thompson 	 */
1962a593f6b8SAndrew Thompson 	err = usbd_req_set_address(udev, mtx, old_addr);
196302ac6454SAndrew Thompson 	if (err) {
196402ac6454SAndrew Thompson 		/* XXX ignore any errors! */
196503797f33SAndrew Thompson 		DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n",
1966a593f6b8SAndrew Thompson 		    old_addr, usbd_errstr(err));
196702ac6454SAndrew Thompson 	}
1968963169b4SHans Petter Selasky 	/*
1969963169b4SHans Petter Selasky 	 * Restore device address, if the controller driver did not
1970963169b4SHans Petter Selasky 	 * set a new one:
1971963169b4SHans Petter Selasky 	 */
1972963169b4SHans Petter Selasky 	if (udev->address == USB_START_ADDR)
197302ac6454SAndrew Thompson 		udev->address = old_addr;
197402ac6454SAndrew Thompson 
1975963169b4SHans Petter Selasky 	/* setup the device descriptor and the initial "wMaxPacketSize" */
1976963169b4SHans Petter Selasky 	err = usbd_setup_device_desc(udev, mtx);
197702ac6454SAndrew Thompson 
197802ac6454SAndrew Thompson done:
197902ac6454SAndrew Thompson 	if (err && do_retry) {
198002ac6454SAndrew Thompson 		/* give the USB firmware some time to load */
1981a593f6b8SAndrew Thompson 		usb_pause_mtx(mtx, hz / 2);
198202ac6454SAndrew Thompson 		/* no more retries after this retry */
198302ac6454SAndrew Thompson 		do_retry = 0;
198402ac6454SAndrew Thompson 		/* try again */
198502ac6454SAndrew Thompson 		goto retry;
198602ac6454SAndrew Thompson 	}
198702ac6454SAndrew Thompson 	/* restore address */
1988963169b4SHans Petter Selasky 	if (udev->address == USB_START_ADDR)
198902ac6454SAndrew Thompson 		udev->address = old_addr;
1990963169b4SHans Petter Selasky 	/* update state, if successful */
1991963169b4SHans Petter Selasky 	if (err == 0)
1992963169b4SHans Petter Selasky 		usb_set_device_state(udev, USB_STATE_ADDRESSED);
199302ac6454SAndrew Thompson 	return (err);
199402ac6454SAndrew Thompson }
199502ac6454SAndrew Thompson 
199602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1997a593f6b8SAndrew Thompson  *	usbd_req_clear_device_feature
199802ac6454SAndrew Thompson  *
199902ac6454SAndrew Thompson  * Returns:
200002ac6454SAndrew Thompson  *    0: Success
200102ac6454SAndrew Thompson  * Else: Failure
200202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
2003e0a69b51SAndrew Thompson usb_error_t
2004a593f6b8SAndrew Thompson usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx,
200502ac6454SAndrew Thompson     uint16_t sel)
200602ac6454SAndrew Thompson {
2007760bc48eSAndrew Thompson 	struct usb_device_request req;
200802ac6454SAndrew Thompson 
200902ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_DEVICE;
201002ac6454SAndrew Thompson 	req.bRequest = UR_CLEAR_FEATURE;
201102ac6454SAndrew Thompson 	USETW(req.wValue, sel);
201202ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
201302ac6454SAndrew Thompson 	USETW(req.wLength, 0);
2014a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
201502ac6454SAndrew Thompson }
201602ac6454SAndrew Thompson 
201702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2018a593f6b8SAndrew Thompson  *	usbd_req_set_device_feature
201902ac6454SAndrew Thompson  *
202002ac6454SAndrew Thompson  * Returns:
202102ac6454SAndrew Thompson  *    0: Success
202202ac6454SAndrew Thompson  * Else: Failure
202302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
2024e0a69b51SAndrew Thompson usb_error_t
2025a593f6b8SAndrew Thompson usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx,
202602ac6454SAndrew Thompson     uint16_t sel)
202702ac6454SAndrew Thompson {
2028760bc48eSAndrew Thompson 	struct usb_device_request req;
202902ac6454SAndrew Thompson 
203002ac6454SAndrew Thompson 	req.bmRequestType = UT_WRITE_DEVICE;
203102ac6454SAndrew Thompson 	req.bRequest = UR_SET_FEATURE;
203202ac6454SAndrew Thompson 	USETW(req.wValue, sel);
203302ac6454SAndrew Thompson 	USETW(req.wIndex, 0);
203402ac6454SAndrew Thompson 	USETW(req.wLength, 0);
2035a593f6b8SAndrew Thompson 	return (usbd_do_request(udev, mtx, &req, 0));
203602ac6454SAndrew Thompson }
2037