102ac6454SAndrew Thompson /*-
2*4d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
402ac6454SAndrew Thompson * Copyright (c) 1998 The NetBSD Foundation, Inc. All rights reserved.
502ac6454SAndrew Thompson * Copyright (c) 1998 Lennart Augustsson. All rights reserved.
6b8b3f4fdSHans Petter Selasky * Copyright (c) 2008-2020 Hans Petter Selasky. All rights reserved.
702ac6454SAndrew Thompson *
802ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without
902ac6454SAndrew Thompson * modification, are permitted provided that the following conditions
1002ac6454SAndrew Thompson * are met:
1102ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright
1202ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer.
1302ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright
1402ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the
1502ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution.
1602ac6454SAndrew Thompson *
1702ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1802ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1902ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2002ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2102ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2202ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2302ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2402ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2502ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2602ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2702ac6454SAndrew Thompson * SUCH DAMAGE.
2802ac6454SAndrew Thompson */
2902ac6454SAndrew Thompson
30d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE
31d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE
32d2b99310SHans Petter Selasky #else
33ed6d949aSAndrew Thompson #include <sys/stdint.h>
34ed6d949aSAndrew Thompson #include <sys/stddef.h>
35ed6d949aSAndrew Thompson #include <sys/param.h>
36ed6d949aSAndrew Thompson #include <sys/queue.h>
37ed6d949aSAndrew Thompson #include <sys/types.h>
38ed6d949aSAndrew Thompson #include <sys/systm.h>
39ed6d949aSAndrew Thompson #include <sys/kernel.h>
40ed6d949aSAndrew Thompson #include <sys/bus.h>
41ed6d949aSAndrew Thompson #include <sys/module.h>
42ed6d949aSAndrew Thompson #include <sys/lock.h>
43ed6d949aSAndrew Thompson #include <sys/mutex.h>
44ed6d949aSAndrew Thompson #include <sys/condvar.h>
45ed6d949aSAndrew Thompson #include <sys/sysctl.h>
46ed6d949aSAndrew Thompson #include <sys/sx.h>
47ed6d949aSAndrew Thompson #include <sys/unistd.h>
48ed6d949aSAndrew Thompson #include <sys/callout.h>
49ed6d949aSAndrew Thompson #include <sys/malloc.h>
50ed6d949aSAndrew Thompson #include <sys/priv.h>
51ed6d949aSAndrew Thompson
5202ac6454SAndrew Thompson #include <dev/usb/usb.h>
53ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
54ed6d949aSAndrew Thompson #include <dev/usb/usbdi_util.h>
5502ac6454SAndrew Thompson #include <dev/usb/usbhid.h>
5602ac6454SAndrew Thompson
57a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug
5802ac6454SAndrew Thompson
5902ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
6002ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
6102ac6454SAndrew Thompson #include <dev/usb/usb_request.h>
6202ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
6302ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h>
6402ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
6502ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
6602ac6454SAndrew Thompson #include <dev/usb/usb_util.h>
6702ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
6802ac6454SAndrew Thompson
6902ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
7002ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
7102ac6454SAndrew Thompson #include <sys/ctype.h>
72d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */
7302ac6454SAndrew Thompson
749465dbebSHans Petter Selasky static int usb_no_cs_fail;
759465dbebSHans Petter Selasky
76ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, no_cs_fail, CTLFLAG_RWTUN,
779465dbebSHans Petter Selasky &usb_no_cs_fail, 0, "USB clear stall failures are ignored, if set");
789465dbebSHans Petter Selasky
7923de050bSHans Petter Selasky static int usb_full_ddesc;
8023de050bSHans Petter Selasky
81ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, full_ddesc, CTLFLAG_RWTUN,
8223de050bSHans Petter Selasky &usb_full_ddesc, 0, "USB always read complete device descriptor, if set");
8323de050bSHans Petter Selasky
84b850ecc1SAndrew Thompson #ifdef USB_DEBUG
85f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
86f6980be8SAndrew Thompson /* The following structures are used in connection to fault injection. */
87f6980be8SAndrew Thompson struct usb_ctrl_debug {
88f6980be8SAndrew Thompson int bus_index; /* target bus */
89f6980be8SAndrew Thompson int dev_index; /* target address */
90f6980be8SAndrew Thompson int ds_fail; /* fail data stage */
912a4e4c67SHans Petter Selasky int ss_fail; /* fail status stage */
92f6980be8SAndrew Thompson int ds_delay; /* data stage delay in ms */
93f6980be8SAndrew Thompson int ss_delay; /* status stage delay in ms */
94f6980be8SAndrew Thompson int bmRequestType_value;
95f6980be8SAndrew Thompson int bRequest_value;
96f6980be8SAndrew Thompson };
97f6980be8SAndrew Thompson
98f6980be8SAndrew Thompson struct usb_ctrl_debug_bits {
99f6980be8SAndrew Thompson uint16_t ds_delay;
100f6980be8SAndrew Thompson uint16_t ss_delay;
101f6980be8SAndrew Thompson uint8_t ds_fail:1;
102f6980be8SAndrew Thompson uint8_t ss_fail:1;
103f6980be8SAndrew Thompson uint8_t enabled:1;
104f6980be8SAndrew Thompson };
105f6980be8SAndrew Thompson
106f6980be8SAndrew Thompson /* The default is to disable fault injection. */
107f6980be8SAndrew Thompson
108f6980be8SAndrew Thompson static struct usb_ctrl_debug usb_ctrl_debug = {
109f6980be8SAndrew Thompson .bus_index = -1,
110f6980be8SAndrew Thompson .dev_index = -1,
111f6980be8SAndrew Thompson .bmRequestType_value = -1,
112f6980be8SAndrew Thompson .bRequest_value = -1,
113f6980be8SAndrew Thompson };
114f6980be8SAndrew Thompson
115ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_bus_fail, CTLFLAG_RWTUN,
116f6980be8SAndrew Thompson &usb_ctrl_debug.bus_index, 0, "USB controller index to fail");
117ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_dev_fail, CTLFLAG_RWTUN,
118f6980be8SAndrew Thompson &usb_ctrl_debug.dev_index, 0, "USB device address to fail");
119ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_fail, CTLFLAG_RWTUN,
120f6980be8SAndrew Thompson &usb_ctrl_debug.ds_fail, 0, "USB fail data stage");
121ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_fail, CTLFLAG_RWTUN,
122f6980be8SAndrew Thompson &usb_ctrl_debug.ss_fail, 0, "USB fail status stage");
123ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ds_delay, CTLFLAG_RWTUN,
124f6980be8SAndrew Thompson &usb_ctrl_debug.ds_delay, 0, "USB data stage delay in ms");
125ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_ss_delay, CTLFLAG_RWTUN,
126f6980be8SAndrew Thompson &usb_ctrl_debug.ss_delay, 0, "USB status stage delay in ms");
127ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rt_fail, CTLFLAG_RWTUN,
128f6980be8SAndrew Thompson &usb_ctrl_debug.bmRequestType_value, 0, "USB bmRequestType to fail");
129ece4b0bdSHans Petter Selasky SYSCTL_INT(_hw_usb, OID_AUTO, ctrl_rv_fail, CTLFLAG_RWTUN,
130f6980be8SAndrew Thompson &usb_ctrl_debug.bRequest_value, 0, "USB bRequest to fail");
131f6980be8SAndrew Thompson
132f6980be8SAndrew Thompson /*------------------------------------------------------------------------*
133f6980be8SAndrew Thompson * usbd_get_debug_bits
134f6980be8SAndrew Thompson *
135f6980be8SAndrew Thompson * This function is only useful in USB host mode.
136f6980be8SAndrew Thompson *------------------------------------------------------------------------*/
137f6980be8SAndrew Thompson static void
usbd_get_debug_bits(struct usb_device * udev,struct usb_device_request * req,struct usb_ctrl_debug_bits * dbg)138f6980be8SAndrew Thompson usbd_get_debug_bits(struct usb_device *udev, struct usb_device_request *req,
139f6980be8SAndrew Thompson struct usb_ctrl_debug_bits *dbg)
140f6980be8SAndrew Thompson {
141f6980be8SAndrew Thompson int temp;
142f6980be8SAndrew Thompson
143f6980be8SAndrew Thompson memset(dbg, 0, sizeof(*dbg));
144f6980be8SAndrew Thompson
145f6980be8SAndrew Thompson /* Compute data stage delay */
146f6980be8SAndrew Thompson
147f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_delay;
148f6980be8SAndrew Thompson if (temp < 0)
149f6980be8SAndrew Thompson temp = 0;
150f6980be8SAndrew Thompson else if (temp > (16*1024))
151f6980be8SAndrew Thompson temp = (16*1024);
152f6980be8SAndrew Thompson
153f6980be8SAndrew Thompson dbg->ds_delay = temp;
154f6980be8SAndrew Thompson
155f6980be8SAndrew Thompson /* Compute status stage delay */
156f6980be8SAndrew Thompson
157f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_delay;
158f6980be8SAndrew Thompson if (temp < 0)
159f6980be8SAndrew Thompson temp = 0;
160f6980be8SAndrew Thompson else if (temp > (16*1024))
161f6980be8SAndrew Thompson temp = (16*1024);
162f6980be8SAndrew Thompson
163f6980be8SAndrew Thompson dbg->ss_delay = temp;
164f6980be8SAndrew Thompson
165f6980be8SAndrew Thompson /* Check if this control request should be failed */
166f6980be8SAndrew Thompson
167f6980be8SAndrew Thompson if (usbd_get_bus_index(udev) != usb_ctrl_debug.bus_index)
168f6980be8SAndrew Thompson return;
169f6980be8SAndrew Thompson
170f6980be8SAndrew Thompson if (usbd_get_device_index(udev) != usb_ctrl_debug.dev_index)
171f6980be8SAndrew Thompson return;
172f6980be8SAndrew Thompson
173f6980be8SAndrew Thompson temp = usb_ctrl_debug.bmRequestType_value;
174f6980be8SAndrew Thompson
175f6980be8SAndrew Thompson if ((temp != req->bmRequestType) && (temp >= 0) && (temp <= 255))
176f6980be8SAndrew Thompson return;
177f6980be8SAndrew Thompson
178f6980be8SAndrew Thompson temp = usb_ctrl_debug.bRequest_value;
179f6980be8SAndrew Thompson
180f6980be8SAndrew Thompson if ((temp != req->bRequest) && (temp >= 0) && (temp <= 255))
181f6980be8SAndrew Thompson return;
182f6980be8SAndrew Thompson
183f6980be8SAndrew Thompson temp = usb_ctrl_debug.ds_fail;
184f6980be8SAndrew Thompson if (temp)
185f6980be8SAndrew Thompson dbg->ds_fail = 1;
186f6980be8SAndrew Thompson
187f6980be8SAndrew Thompson temp = usb_ctrl_debug.ss_fail;
188f6980be8SAndrew Thompson if (temp)
189f6980be8SAndrew Thompson dbg->ss_fail = 1;
190f6980be8SAndrew Thompson
191f6980be8SAndrew Thompson dbg->enabled = 1;
192f6980be8SAndrew Thompson }
193f6980be8SAndrew Thompson #endif /* USB_REQ_DEBUG */
194f6980be8SAndrew Thompson #endif /* USB_DEBUG */
19502ac6454SAndrew Thompson
19602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
197a593f6b8SAndrew Thompson * usbd_do_request_callback
19802ac6454SAndrew Thompson *
19902ac6454SAndrew Thompson * This function is the USB callback for generic USB Host control
20002ac6454SAndrew Thompson * transfers.
20102ac6454SAndrew Thompson *------------------------------------------------------------------------*/
20202ac6454SAndrew Thompson void
usbd_do_request_callback(struct usb_xfer * xfer,usb_error_t error)203ed6d949aSAndrew Thompson usbd_do_request_callback(struct usb_xfer *xfer, usb_error_t error)
20402ac6454SAndrew Thompson {
20502ac6454SAndrew Thompson ; /* workaround for a bug in "indent" */
20602ac6454SAndrew Thompson
20702ac6454SAndrew Thompson DPRINTF("st=%u\n", USB_GET_STATE(xfer));
20802ac6454SAndrew Thompson
20902ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) {
21002ac6454SAndrew Thompson case USB_ST_SETUP:
211a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
21202ac6454SAndrew Thompson break;
21302ac6454SAndrew Thompson default:
21491cd9240SAndrew Thompson cv_signal(&xfer->xroot->udev->ctrlreq_cv);
21502ac6454SAndrew Thompson break;
21602ac6454SAndrew Thompson }
21702ac6454SAndrew Thompson }
21802ac6454SAndrew Thompson
21902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
220a593f6b8SAndrew Thompson * usb_do_clear_stall_callback
22102ac6454SAndrew Thompson *
22202ac6454SAndrew Thompson * This function is the USB callback for generic clear stall requests.
22302ac6454SAndrew Thompson *------------------------------------------------------------------------*/
22402ac6454SAndrew Thompson void
usb_do_clear_stall_callback(struct usb_xfer * xfer,usb_error_t error)225ed6d949aSAndrew Thompson usb_do_clear_stall_callback(struct usb_xfer *xfer, usb_error_t error)
22602ac6454SAndrew Thompson {
227760bc48eSAndrew Thompson struct usb_device_request req;
228760bc48eSAndrew Thompson struct usb_device *udev;
229ae60fdfbSAndrew Thompson struct usb_endpoint *ep;
230ae60fdfbSAndrew Thompson struct usb_endpoint *ep_end;
231ae60fdfbSAndrew Thompson struct usb_endpoint *ep_first;
232a5cf1aaaSHans Petter Selasky usb_stream_t x;
23363521bbcSAndrew Thompson uint8_t to;
23402ac6454SAndrew Thompson
23502ac6454SAndrew Thompson udev = xfer->xroot->udev;
23602ac6454SAndrew Thompson
23702ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus);
23802ac6454SAndrew Thompson
239ae60fdfbSAndrew Thompson /* round robin endpoint clear stall */
24002ac6454SAndrew Thompson
241ae60fdfbSAndrew Thompson ep = udev->ep_curr;
242ae60fdfbSAndrew Thompson ep_end = udev->endpoints + udev->endpoints_max;
243ae60fdfbSAndrew Thompson ep_first = udev->endpoints;
244ae60fdfbSAndrew Thompson to = udev->endpoints_max;
245115df0b6SAndrew Thompson
24602ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) {
24702ac6454SAndrew Thompson case USB_ST_TRANSFERRED:
2489465dbebSHans Petter Selasky tr_transferred:
2499eb0d702SHans Petter Selasky /* reset error counter */
2509eb0d702SHans Petter Selasky udev->clear_stall_errors = 0;
2519eb0d702SHans Petter Selasky
252ae60fdfbSAndrew Thompson if (ep == NULL)
253115df0b6SAndrew Thompson goto tr_setup; /* device was unconfigured */
254ae60fdfbSAndrew Thompson if (ep->edesc &&
255ae60fdfbSAndrew Thompson ep->is_stalled) {
256ae60fdfbSAndrew Thompson ep->toggle_next = 0;
257ae60fdfbSAndrew Thompson ep->is_stalled = 0;
258963169b4SHans Petter Selasky /* some hardware needs a callback to clear the data toggle */
259963169b4SHans Petter Selasky usbd_clear_stall_locked(udev, ep);
260a5cf1aaaSHans Petter Selasky for (x = 0; x != USB_MAX_EP_STREAMS; x++) {
261a5cf1aaaSHans Petter Selasky /* start the current or next transfer, if any */
262a5cf1aaaSHans Petter Selasky usb_command_wrapper(&ep->endpoint_q[x],
263a5cf1aaaSHans Petter Selasky ep->endpoint_q[x].curr);
264a5cf1aaaSHans Petter Selasky }
26502ac6454SAndrew Thompson }
266ae60fdfbSAndrew Thompson ep++;
26702ac6454SAndrew Thompson
26802ac6454SAndrew Thompson case USB_ST_SETUP:
26902ac6454SAndrew Thompson tr_setup:
270115df0b6SAndrew Thompson if (to == 0)
271ae60fdfbSAndrew Thompson break; /* no endpoints - nothing to do */
272ae60fdfbSAndrew Thompson if ((ep < ep_first) || (ep >= ep_end))
273ae60fdfbSAndrew Thompson ep = ep_first; /* endpoint wrapped around */
274ae60fdfbSAndrew Thompson if (ep->edesc &&
275ae60fdfbSAndrew Thompson ep->is_stalled) {
27602ac6454SAndrew Thompson /* setup a clear-stall packet */
27702ac6454SAndrew Thompson
27802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_ENDPOINT;
27902ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE;
28002ac6454SAndrew Thompson USETW(req.wValue, UF_ENDPOINT_HALT);
281ae60fdfbSAndrew Thompson req.wIndex[0] = ep->edesc->bEndpointAddress;
28202ac6454SAndrew Thompson req.wIndex[1] = 0;
28302ac6454SAndrew Thompson USETW(req.wLength, 0);
28402ac6454SAndrew Thompson
28502ac6454SAndrew Thompson /* copy in the transfer */
28602ac6454SAndrew Thompson
287a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, &req, sizeof(req));
28802ac6454SAndrew Thompson
28902ac6454SAndrew Thompson /* set length */
290ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(req));
29102ac6454SAndrew Thompson xfer->nframes = 1;
29202ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus);
29302ac6454SAndrew Thompson
294a593f6b8SAndrew Thompson usbd_transfer_submit(xfer);
29502ac6454SAndrew Thompson
29602ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus);
29702ac6454SAndrew Thompson break;
29802ac6454SAndrew Thompson }
299ae60fdfbSAndrew Thompson ep++;
300115df0b6SAndrew Thompson to--;
30102ac6454SAndrew Thompson goto tr_setup;
30202ac6454SAndrew Thompson
30302ac6454SAndrew Thompson default:
3049eb0d702SHans Petter Selasky if (error == USB_ERR_CANCELLED)
30502ac6454SAndrew Thompson break;
3069eb0d702SHans Petter Selasky
3079eb0d702SHans Petter Selasky DPRINTF("Clear stall failed.\n");
3089465dbebSHans Petter Selasky
3099465dbebSHans Petter Selasky /*
3109465dbebSHans Petter Selasky * Some VMs like VirtualBox always return failure on
3119465dbebSHans Petter Selasky * clear-stall which we sometimes should just ignore.
3129465dbebSHans Petter Selasky */
3139465dbebSHans Petter Selasky if (usb_no_cs_fail)
3149465dbebSHans Petter Selasky goto tr_transferred;
3154e2d8cd3SHans Petter Selasky
3164e2d8cd3SHans Petter Selasky /*
3174e2d8cd3SHans Petter Selasky * Some non-compliant USB devices do not implement the
3184e2d8cd3SHans Petter Selasky * clear endpoint halt feature. Silently ignore such
3194e2d8cd3SHans Petter Selasky * devices, when they at least respond correctly
3204e2d8cd3SHans Petter Selasky * passing up a valid STALL PID packet.
3214e2d8cd3SHans Petter Selasky */
3224e2d8cd3SHans Petter Selasky if (error == USB_ERR_STALLED)
3234e2d8cd3SHans Petter Selasky goto tr_transferred;
3244e2d8cd3SHans Petter Selasky
3259eb0d702SHans Petter Selasky if (udev->clear_stall_errors == USB_CS_RESET_LIMIT)
3269eb0d702SHans Petter Selasky goto tr_setup;
3279eb0d702SHans Petter Selasky
3289eb0d702SHans Petter Selasky if (error == USB_ERR_TIMEOUT) {
3299eb0d702SHans Petter Selasky udev->clear_stall_errors = USB_CS_RESET_LIMIT;
3309eb0d702SHans Petter Selasky DPRINTF("Trying to re-enumerate.\n");
3319eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev);
3329eb0d702SHans Petter Selasky } else {
3339eb0d702SHans Petter Selasky udev->clear_stall_errors++;
3349eb0d702SHans Petter Selasky if (udev->clear_stall_errors == USB_CS_RESET_LIMIT) {
3359eb0d702SHans Petter Selasky DPRINTF("Trying to re-enumerate.\n");
3369eb0d702SHans Petter Selasky usbd_start_re_enumerate(udev);
3379eb0d702SHans Petter Selasky }
33802ac6454SAndrew Thompson }
33902ac6454SAndrew Thompson goto tr_setup;
34002ac6454SAndrew Thompson }
34102ac6454SAndrew Thompson
342ae60fdfbSAndrew Thompson /* store current endpoint */
343ae60fdfbSAndrew Thompson udev->ep_curr = ep;
34402ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus);
34502ac6454SAndrew Thompson }
34602ac6454SAndrew Thompson
347e0a69b51SAndrew Thompson static usb_handle_req_t *
usbd_get_hr_func(struct usb_device * udev)348a593f6b8SAndrew Thompson usbd_get_hr_func(struct usb_device *udev)
349459d369eSAndrew Thompson {
350459d369eSAndrew Thompson /* figure out if there is a Handle Request function */
351f29a0724SAndrew Thompson if (udev->flags.usb_mode == USB_MODE_DEVICE)
352a593f6b8SAndrew Thompson return (usb_temp_get_desc_p);
353459d369eSAndrew Thompson else if (udev->parent_hub == NULL)
354459d369eSAndrew Thompson return (udev->bus->methods->roothub_exec);
355459d369eSAndrew Thompson else
356459d369eSAndrew Thompson return (NULL);
357459d369eSAndrew Thompson }
358459d369eSAndrew Thompson
35902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
360a593f6b8SAndrew Thompson * usbd_do_request_flags and usbd_do_request
36102ac6454SAndrew Thompson *
36202ac6454SAndrew Thompson * Description of arguments passed to these functions:
36302ac6454SAndrew Thompson *
364760bc48eSAndrew Thompson * "udev" - this is the "usb_device" structure pointer on which the
36502ac6454SAndrew Thompson * request should be performed. It is possible to call this function
36602ac6454SAndrew Thompson * in both Host Side mode and Device Side mode.
36702ac6454SAndrew Thompson *
36802ac6454SAndrew Thompson * "mtx" - if this argument is non-NULL the mutex pointed to by it
36902ac6454SAndrew Thompson * will get dropped and picked up during the execution of this
37002ac6454SAndrew Thompson * function, hence this function sometimes needs to sleep. If this
37102ac6454SAndrew Thompson * argument is NULL it has no effect.
37202ac6454SAndrew Thompson *
37302ac6454SAndrew Thompson * "req" - this argument must always be non-NULL and points to an
37402ac6454SAndrew Thompson * 8-byte structure holding the USB request to be done. The USB
37502ac6454SAndrew Thompson * request structure has a bit telling the direction of the USB
37602ac6454SAndrew Thompson * request, if it is a read or a write.
37702ac6454SAndrew Thompson *
37802ac6454SAndrew Thompson * "data" - if the "wLength" part of the structure pointed to by "req"
37902ac6454SAndrew Thompson * is non-zero this argument must point to a valid kernel buffer which
38002ac6454SAndrew Thompson * can hold at least "wLength" bytes. If "wLength" is zero "data" can
38102ac6454SAndrew Thompson * be NULL.
38202ac6454SAndrew Thompson *
38302ac6454SAndrew Thompson * "flags" - here is a list of valid flags:
38402ac6454SAndrew Thompson *
38502ac6454SAndrew Thompson * o USB_SHORT_XFER_OK: allows the data transfer to be shorter than
38602ac6454SAndrew Thompson * specified
38702ac6454SAndrew Thompson *
38802ac6454SAndrew Thompson * o USB_DELAY_STATUS_STAGE: allows the status stage to be performed
38902ac6454SAndrew Thompson * at a later point in time. This is tunable by the "hw.usb.ss_delay"
39002ac6454SAndrew Thompson * sysctl. This flag is mostly useful for debugging.
39102ac6454SAndrew Thompson *
39202ac6454SAndrew Thompson * o USB_USER_DATA_PTR: treat the "data" pointer like a userland
39302ac6454SAndrew Thompson * pointer.
39402ac6454SAndrew Thompson *
39502ac6454SAndrew Thompson * "actlen" - if non-NULL the actual transfer length will be stored in
39602ac6454SAndrew Thompson * the 16-bit unsigned integer pointed to by "actlen". This
39702ac6454SAndrew Thompson * information is mostly useful when the "USB_SHORT_XFER_OK" flag is
39802ac6454SAndrew Thompson * used.
39902ac6454SAndrew Thompson *
40002ac6454SAndrew Thompson * "timeout" - gives the timeout for the control transfer in
40102ac6454SAndrew Thompson * milliseconds. A "timeout" value less than 50 milliseconds is
40202ac6454SAndrew Thompson * treated like a 50 millisecond timeout. A "timeout" value greater
40302ac6454SAndrew Thompson * than 30 seconds is treated like a 30 second timeout. This USB stack
40402ac6454SAndrew Thompson * does not allow control requests without a timeout.
40502ac6454SAndrew Thompson *
406a18a7a41SHans Petter Selasky * NOTE: This function is thread safe. All calls to "usbd_do_request_flags"
407a18a7a41SHans Petter Selasky * will be serialized by the use of the USB device enumeration lock.
40802ac6454SAndrew Thompson *
40902ac6454SAndrew Thompson * Returns:
41002ac6454SAndrew Thompson * 0: Success
41102ac6454SAndrew Thompson * Else: Failure
41202ac6454SAndrew Thompson *------------------------------------------------------------------------*/
413e0a69b51SAndrew Thompson usb_error_t
usbd_do_request_flags(struct usb_device * udev,struct mtx * mtx,struct usb_device_request * req,void * data,uint16_t flags,uint16_t * actlen,usb_timeout_t timeout)414a593f6b8SAndrew Thompson usbd_do_request_flags(struct usb_device *udev, struct mtx *mtx,
415760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags,
416e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout)
41702ac6454SAndrew Thompson {
418f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
419f6980be8SAndrew Thompson struct usb_ctrl_debug_bits dbg;
420f6980be8SAndrew Thompson #endif
421e0a69b51SAndrew Thompson usb_handle_req_t *hr_func;
422760bc48eSAndrew Thompson struct usb_xfer *xfer;
42302ac6454SAndrew Thompson const void *desc;
42402ac6454SAndrew Thompson int err = 0;
425e0a69b51SAndrew Thompson usb_ticks_t start_ticks;
426e0a69b51SAndrew Thompson usb_ticks_t delta_ticks;
427e0a69b51SAndrew Thompson usb_ticks_t max_ticks;
42802ac6454SAndrew Thompson uint16_t length;
42902ac6454SAndrew Thompson uint16_t temp;
430f6980be8SAndrew Thompson uint16_t acttemp;
431a18a7a41SHans Petter Selasky uint8_t do_unlock;
43202ac6454SAndrew Thompson
43302ac6454SAndrew Thompson if (timeout < 50) {
43402ac6454SAndrew Thompson /* timeout is too small */
43502ac6454SAndrew Thompson timeout = 50;
43602ac6454SAndrew Thompson }
43702ac6454SAndrew Thompson if (timeout > 30000) {
43802ac6454SAndrew Thompson /* timeout is too big */
43902ac6454SAndrew Thompson timeout = 30000;
44002ac6454SAndrew Thompson }
44102ac6454SAndrew Thompson length = UGETW(req->wLength);
44202ac6454SAndrew Thompson
44302ac6454SAndrew Thompson DPRINTFN(5, "udev=%p bmRequestType=0x%02x bRequest=0x%02x "
44402ac6454SAndrew Thompson "wValue=0x%02x%02x wIndex=0x%02x%02x wLength=0x%02x%02x\n",
44502ac6454SAndrew Thompson udev, req->bmRequestType, req->bRequest,
44602ac6454SAndrew Thompson req->wValue[1], req->wValue[0],
44702ac6454SAndrew Thompson req->wIndex[1], req->wIndex[0],
44802ac6454SAndrew Thompson req->wLength[1], req->wLength[0]);
44902ac6454SAndrew Thompson
450bd216778SAndrew Thompson /* Check if the device is still alive */
451bd216778SAndrew Thompson if (udev->state < USB_STATE_POWERED) {
452bd216778SAndrew Thompson DPRINTF("usb device has gone\n");
453bd216778SAndrew Thompson return (USB_ERR_NOT_CONFIGURED);
454bd216778SAndrew Thompson }
455bd216778SAndrew Thompson
45602ac6454SAndrew Thompson /*
45702ac6454SAndrew Thompson * Set "actlen" to a known value in case the caller does not
45802ac6454SAndrew Thompson * check the return value:
45902ac6454SAndrew Thompson */
46039307315SAndrew Thompson if (actlen)
46102ac6454SAndrew Thompson *actlen = 0;
46239307315SAndrew Thompson
463bdc081c6SAndrew Thompson #if (USB_HAVE_USER_IO == 0)
464bdc081c6SAndrew Thompson if (flags & USB_USER_DATA_PTR)
465bdc081c6SAndrew Thompson return (USB_ERR_INVAL);
466bdc081c6SAndrew Thompson #endif
4672df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant)) {
4680eb8d462SHans Petter Selasky USB_MTX_UNLOCK(mtx);
4690eb8d462SHans Petter Selasky USB_MTX_ASSERT(mtx, MA_NOTOWNED);
47002ac6454SAndrew Thompson }
4712df1e9a6SAndrew Thompson
4722df1e9a6SAndrew Thompson /*
47364cb5e2aSHans Petter Selasky * Serialize access to this function:
474a18a7a41SHans Petter Selasky */
47564cb5e2aSHans Petter Selasky do_unlock = usbd_ctrl_lock(udev);
4762df1e9a6SAndrew Thompson
477a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev);
47839307315SAndrew Thompson
479459d369eSAndrew Thompson if (hr_func != NULL) {
480459d369eSAndrew Thompson DPRINTF("Handle Request function is set\n");
48139307315SAndrew Thompson
482459d369eSAndrew Thompson desc = NULL;
483459d369eSAndrew Thompson temp = 0;
484459d369eSAndrew Thompson
485459d369eSAndrew Thompson if (!(req->bmRequestType & UT_READ)) {
48639307315SAndrew Thompson if (length != 0) {
487459d369eSAndrew Thompson DPRINTFN(1, "The handle request function "
488459d369eSAndrew Thompson "does not support writing data!\n");
48939307315SAndrew Thompson err = USB_ERR_INVAL;
49039307315SAndrew Thompson goto done;
49139307315SAndrew Thompson }
49239307315SAndrew Thompson }
493459d369eSAndrew Thompson
494459d369eSAndrew Thompson /* The root HUB code needs the BUS lock locked */
49539307315SAndrew Thompson
49639307315SAndrew Thompson USB_BUS_LOCK(udev->bus);
497459d369eSAndrew Thompson err = (hr_func) (udev, req, &desc, &temp);
49839307315SAndrew Thompson USB_BUS_UNLOCK(udev->bus);
49939307315SAndrew Thompson
50039307315SAndrew Thompson if (err)
50139307315SAndrew Thompson goto done;
50239307315SAndrew Thompson
503459d369eSAndrew Thompson if (length > temp) {
50439307315SAndrew Thompson if (!(flags & USB_SHORT_XFER_OK)) {
50539307315SAndrew Thompson err = USB_ERR_SHORT_XFER;
50639307315SAndrew Thompson goto done;
50739307315SAndrew Thompson }
508459d369eSAndrew Thompson length = temp;
50939307315SAndrew Thompson }
51039307315SAndrew Thompson if (actlen)
51139307315SAndrew Thompson *actlen = length;
51239307315SAndrew Thompson
51339307315SAndrew Thompson if (length > 0) {
51439307315SAndrew Thompson #if USB_HAVE_USER_IO
51539307315SAndrew Thompson if (flags & USB_USER_DATA_PTR) {
516459d369eSAndrew Thompson if (copyout(desc, data, length)) {
51739307315SAndrew Thompson err = USB_ERR_INVAL;
51839307315SAndrew Thompson goto done;
51939307315SAndrew Thompson }
52039307315SAndrew Thompson } else
52139307315SAndrew Thompson #endif
522271ae033SHans Petter Selasky memcpy(data, desc, length);
52339307315SAndrew Thompson }
524459d369eSAndrew Thompson goto done; /* success */
52539307315SAndrew Thompson }
52639307315SAndrew Thompson
52702ac6454SAndrew Thompson /*
52802ac6454SAndrew Thompson * Setup a new USB transfer or use the existing one, if any:
52902ac6454SAndrew Thompson */
5305b3bb704SAndrew Thompson usbd_ctrl_transfer_setup(udev);
53102ac6454SAndrew Thompson
5325b3bb704SAndrew Thompson xfer = udev->ctrl_xfer[0];
53302ac6454SAndrew Thompson if (xfer == NULL) {
53402ac6454SAndrew Thompson /* most likely out of memory */
53502ac6454SAndrew Thompson err = USB_ERR_NOMEM;
53602ac6454SAndrew Thompson goto done;
53702ac6454SAndrew Thompson }
538f6980be8SAndrew Thompson
539f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
540f6980be8SAndrew Thompson /* Get debug bits */
541f6980be8SAndrew Thompson usbd_get_debug_bits(udev, req, &dbg);
542f6980be8SAndrew Thompson
543f6980be8SAndrew Thompson /* Check for fault injection */
544f6980be8SAndrew Thompson if (dbg.enabled)
545f6980be8SAndrew Thompson flags |= USB_DELAY_STATUS_STAGE;
546f6980be8SAndrew Thompson #endif
54702ac6454SAndrew Thompson USB_XFER_LOCK(xfer);
54802ac6454SAndrew Thompson
5494eae601eSAndrew Thompson if (flags & USB_DELAY_STATUS_STAGE)
55002ac6454SAndrew Thompson xfer->flags.manual_status = 1;
5514eae601eSAndrew Thompson else
55202ac6454SAndrew Thompson xfer->flags.manual_status = 0;
5534eae601eSAndrew Thompson
5544eae601eSAndrew Thompson if (flags & USB_SHORT_XFER_OK)
5554eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 1;
5564eae601eSAndrew Thompson else
5574eae601eSAndrew Thompson xfer->flags.short_xfer_ok = 0;
55802ac6454SAndrew Thompson
55902ac6454SAndrew Thompson xfer->timeout = timeout;
56002ac6454SAndrew Thompson
56102ac6454SAndrew Thompson start_ticks = ticks;
56202ac6454SAndrew Thompson
56302ac6454SAndrew Thompson max_ticks = USB_MS_TO_TICKS(timeout);
56402ac6454SAndrew Thompson
565a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers, 0, req, sizeof(*req));
56602ac6454SAndrew Thompson
567ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(*req));
56802ac6454SAndrew Thompson
56902ac6454SAndrew Thompson while (1) {
57002ac6454SAndrew Thompson temp = length;
571f6980be8SAndrew Thompson if (temp > usbd_xfer_max_len(xfer)) {
572ed6d949aSAndrew Thompson temp = usbd_xfer_max_len(xfer);
57302ac6454SAndrew Thompson }
574f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
575f6980be8SAndrew Thompson if (xfer->flags.manual_status) {
576f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) != 0) {
577f6980be8SAndrew Thompson /* Execute data stage separately */
578f6980be8SAndrew Thompson temp = 0;
579f6980be8SAndrew Thompson } else if (temp > 0) {
580f6980be8SAndrew Thompson if (dbg.ds_fail) {
581f6980be8SAndrew Thompson err = USB_ERR_INVAL;
582f6980be8SAndrew Thompson break;
583f6980be8SAndrew Thompson }
584f6980be8SAndrew Thompson if (dbg.ds_delay > 0) {
585f6980be8SAndrew Thompson usb_pause_mtx(
586f6980be8SAndrew Thompson xfer->xroot->xfer_mtx,
587f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ds_delay));
588f6980be8SAndrew Thompson /* make sure we don't time out */
589f6980be8SAndrew Thompson start_ticks = ticks;
590f6980be8SAndrew Thompson }
591f6980be8SAndrew Thompson }
592f6980be8SAndrew Thompson }
593f6980be8SAndrew Thompson #endif
594ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, temp);
59502ac6454SAndrew Thompson
59602ac6454SAndrew Thompson if (temp > 0) {
59702ac6454SAndrew Thompson if (!(req->bmRequestType & UT_READ)) {
598bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO
59902ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) {
60002ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer);
601a593f6b8SAndrew Thompson err = usbd_copy_in_user(xfer->frbuffers + 1,
60202ac6454SAndrew Thompson 0, data, temp);
60302ac6454SAndrew Thompson USB_XFER_LOCK(xfer);
60402ac6454SAndrew Thompson if (err) {
60502ac6454SAndrew Thompson err = USB_ERR_INVAL;
60602ac6454SAndrew Thompson break;
60702ac6454SAndrew Thompson }
608bdc081c6SAndrew Thompson } else
609bdc081c6SAndrew Thompson #endif
610a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers + 1,
611bdc081c6SAndrew Thompson 0, data, temp);
61202ac6454SAndrew Thompson }
613f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 2);
61402ac6454SAndrew Thompson } else {
615f6980be8SAndrew Thompson if (usbd_xfer_frame_len(xfer, 0) == 0) {
61602ac6454SAndrew Thompson if (xfer->flags.manual_status) {
617f6980be8SAndrew Thompson #ifdef USB_REQ_DEBUG
618f6980be8SAndrew Thompson if (dbg.ss_fail) {
619f6980be8SAndrew Thompson err = USB_ERR_INVAL;
620f6980be8SAndrew Thompson break;
62102ac6454SAndrew Thompson }
622f6980be8SAndrew Thompson if (dbg.ss_delay > 0) {
623a593f6b8SAndrew Thompson usb_pause_mtx(
62402ac6454SAndrew Thompson xfer->xroot->xfer_mtx,
625f6980be8SAndrew Thompson USB_MS_TO_TICKS(dbg.ss_delay));
626f6980be8SAndrew Thompson /* make sure we don't time out */
627f6980be8SAndrew Thompson start_ticks = ticks;
62802ac6454SAndrew Thompson }
62902ac6454SAndrew Thompson #endif
63002ac6454SAndrew Thompson xfer->flags.manual_status = 0;
63102ac6454SAndrew Thompson } else {
63202ac6454SAndrew Thompson break;
63302ac6454SAndrew Thompson }
63402ac6454SAndrew Thompson }
635f6980be8SAndrew Thompson usbd_xfer_set_frames(xfer, 1);
63602ac6454SAndrew Thompson }
63702ac6454SAndrew Thompson
638a593f6b8SAndrew Thompson usbd_transfer_start(xfer);
63902ac6454SAndrew Thompson
640a593f6b8SAndrew Thompson while (usbd_transfer_pending(xfer)) {
64191cd9240SAndrew Thompson cv_wait(&udev->ctrlreq_cv,
64202ac6454SAndrew Thompson xfer->xroot->xfer_mtx);
64302ac6454SAndrew Thompson }
64402ac6454SAndrew Thompson
64502ac6454SAndrew Thompson err = xfer->error;
64602ac6454SAndrew Thompson
64702ac6454SAndrew Thompson if (err) {
64802ac6454SAndrew Thompson break;
64902ac6454SAndrew Thompson }
65002ac6454SAndrew Thompson
651f6980be8SAndrew Thompson /* get actual length of DATA stage */
652f6980be8SAndrew Thompson
653f6980be8SAndrew Thompson if (xfer->aframes < 2) {
654f6980be8SAndrew Thompson acttemp = 0;
65502ac6454SAndrew Thompson } else {
656f6980be8SAndrew Thompson acttemp = usbd_xfer_frame_len(xfer, 1);
65702ac6454SAndrew Thompson }
65802ac6454SAndrew Thompson
65902ac6454SAndrew Thompson /* check for short packet */
66002ac6454SAndrew Thompson
661f6980be8SAndrew Thompson if (temp > acttemp) {
662f6980be8SAndrew Thompson temp = acttemp;
66302ac6454SAndrew Thompson length = temp;
66402ac6454SAndrew Thompson }
66502ac6454SAndrew Thompson if (temp > 0) {
66602ac6454SAndrew Thompson if (req->bmRequestType & UT_READ) {
667bdc081c6SAndrew Thompson #if USB_HAVE_USER_IO
66802ac6454SAndrew Thompson if (flags & USB_USER_DATA_PTR) {
66902ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer);
670a593f6b8SAndrew Thompson err = usbd_copy_out_user(xfer->frbuffers + 1,
67102ac6454SAndrew Thompson 0, data, temp);
67202ac6454SAndrew Thompson USB_XFER_LOCK(xfer);
67302ac6454SAndrew Thompson if (err) {
67402ac6454SAndrew Thompson err = USB_ERR_INVAL;
67502ac6454SAndrew Thompson break;
67602ac6454SAndrew Thompson }
677bdc081c6SAndrew Thompson } else
678bdc081c6SAndrew Thompson #endif
679a593f6b8SAndrew Thompson usbd_copy_out(xfer->frbuffers + 1,
68002ac6454SAndrew Thompson 0, data, temp);
68102ac6454SAndrew Thompson }
68202ac6454SAndrew Thompson }
68302ac6454SAndrew Thompson /*
68402ac6454SAndrew Thompson * Clear "frlengths[0]" so that we don't send the setup
68502ac6454SAndrew Thompson * packet again:
68602ac6454SAndrew Thompson */
687ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, 0);
68802ac6454SAndrew Thompson
68902ac6454SAndrew Thompson /* update length and data pointer */
69002ac6454SAndrew Thompson length -= temp;
69102ac6454SAndrew Thompson data = USB_ADD_BYTES(data, temp);
69202ac6454SAndrew Thompson
69302ac6454SAndrew Thompson if (actlen) {
69402ac6454SAndrew Thompson (*actlen) += temp;
69502ac6454SAndrew Thompson }
69602ac6454SAndrew Thompson /* check for timeout */
69702ac6454SAndrew Thompson
69802ac6454SAndrew Thompson delta_ticks = ticks - start_ticks;
69902ac6454SAndrew Thompson if (delta_ticks > max_ticks) {
70002ac6454SAndrew Thompson if (!err) {
70102ac6454SAndrew Thompson err = USB_ERR_TIMEOUT;
70202ac6454SAndrew Thompson }
70302ac6454SAndrew Thompson }
70402ac6454SAndrew Thompson if (err) {
70502ac6454SAndrew Thompson break;
70602ac6454SAndrew Thompson }
70702ac6454SAndrew Thompson }
70802ac6454SAndrew Thompson
70902ac6454SAndrew Thompson if (err) {
71002ac6454SAndrew Thompson /*
71102ac6454SAndrew Thompson * Make sure that the control endpoint is no longer
71202ac6454SAndrew Thompson * blocked in case of a non-transfer related error:
71302ac6454SAndrew Thompson */
714a593f6b8SAndrew Thompson usbd_transfer_stop(xfer);
71502ac6454SAndrew Thompson }
71602ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer);
71702ac6454SAndrew Thompson
71802ac6454SAndrew Thompson done:
719a18a7a41SHans Petter Selasky if (do_unlock)
72064cb5e2aSHans Petter Selasky usbd_ctrl_unlock(udev);
721a18a7a41SHans Petter Selasky
7222df1e9a6SAndrew Thompson if ((mtx != NULL) && (mtx != &Giant))
7230eb8d462SHans Petter Selasky USB_MTX_LOCK(mtx);
7242df1e9a6SAndrew Thompson
725a0d53e0bSHans Petter Selasky switch (err) {
726a0d53e0bSHans Petter Selasky case USB_ERR_NORMAL_COMPLETION:
727a0d53e0bSHans Petter Selasky case USB_ERR_SHORT_XFER:
728a0d53e0bSHans Petter Selasky case USB_ERR_STALLED:
729a0d53e0bSHans Petter Selasky case USB_ERR_CANCELLED:
730a0d53e0bSHans Petter Selasky break;
731a0d53e0bSHans Petter Selasky default:
7322dea6902SHans Petter Selasky DPRINTF("error=%s - waiting a bit for TT cleanup\n",
7332dea6902SHans Petter Selasky usbd_errstr(err));
734a0d53e0bSHans Petter Selasky usb_pause_mtx(mtx, hz / 16);
735a0d53e0bSHans Petter Selasky break;
736a0d53e0bSHans Petter Selasky }
737e0a69b51SAndrew Thompson return ((usb_error_t)err);
73802ac6454SAndrew Thompson }
73902ac6454SAndrew Thompson
74002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
741a593f6b8SAndrew Thompson * usbd_do_request_proc - factored out code
74202ac6454SAndrew Thompson *
74302ac6454SAndrew Thompson * This function is factored out code. It does basically the same like
744a593f6b8SAndrew Thompson * usbd_do_request_flags, except it will check the status of the
74502ac6454SAndrew Thompson * passed process argument before doing the USB request. If the
74602ac6454SAndrew Thompson * process is draining the USB_ERR_IOERROR code will be returned. It
74702ac6454SAndrew Thompson * is assumed that the mutex associated with the process is locked
74802ac6454SAndrew Thompson * when calling this function.
74902ac6454SAndrew Thompson *------------------------------------------------------------------------*/
750e0a69b51SAndrew Thompson usb_error_t
usbd_do_request_proc(struct usb_device * udev,struct usb_process * pproc,struct usb_device_request * req,void * data,uint16_t flags,uint16_t * actlen,usb_timeout_t timeout)751a593f6b8SAndrew Thompson usbd_do_request_proc(struct usb_device *udev, struct usb_process *pproc,
752760bc48eSAndrew Thompson struct usb_device_request *req, void *data, uint16_t flags,
753e0a69b51SAndrew Thompson uint16_t *actlen, usb_timeout_t timeout)
75402ac6454SAndrew Thompson {
755e0a69b51SAndrew Thompson usb_error_t err;
75602ac6454SAndrew Thompson uint16_t len;
75702ac6454SAndrew Thompson
75802ac6454SAndrew Thompson /* get request data length */
75902ac6454SAndrew Thompson len = UGETW(req->wLength);
76002ac6454SAndrew Thompson
76102ac6454SAndrew Thompson /* check if the device is being detached */
762a593f6b8SAndrew Thompson if (usb_proc_is_gone(pproc)) {
76302ac6454SAndrew Thompson err = USB_ERR_IOERROR;
76402ac6454SAndrew Thompson goto done;
76502ac6454SAndrew Thompson }
76602ac6454SAndrew Thompson
76702ac6454SAndrew Thompson /* forward the USB request */
768a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, pproc->up_mtx,
76902ac6454SAndrew Thompson req, data, flags, actlen, timeout);
77002ac6454SAndrew Thompson
77102ac6454SAndrew Thompson done:
77202ac6454SAndrew Thompson /* on failure we zero the data */
77302ac6454SAndrew Thompson /* on short packet we zero the unused data */
77402ac6454SAndrew Thompson if ((len != 0) && (req->bmRequestType & UE_DIR_IN)) {
77502ac6454SAndrew Thompson if (err)
77602ac6454SAndrew Thompson memset(data, 0, len);
77702ac6454SAndrew Thompson else if (actlen && *actlen != len)
77802ac6454SAndrew Thompson memset(((uint8_t *)data) + *actlen, 0, len - *actlen);
77902ac6454SAndrew Thompson }
78002ac6454SAndrew Thompson return (err);
78102ac6454SAndrew Thompson }
78202ac6454SAndrew Thompson
78302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
784a593f6b8SAndrew Thompson * usbd_req_reset_port
78502ac6454SAndrew Thompson *
786cbb75751SHans Petter Selasky * This function will instruct a USB HUB to perform a reset sequence
78702ac6454SAndrew Thompson * on the specified port number.
78802ac6454SAndrew Thompson *
78902ac6454SAndrew Thompson * Returns:
79002ac6454SAndrew Thompson * 0: Success. The USB device should now be at address zero.
79102ac6454SAndrew Thompson * Else: Failure. No USB device is present and the USB port should be
79202ac6454SAndrew Thompson * disabled.
79302ac6454SAndrew Thompson *------------------------------------------------------------------------*/
794e0a69b51SAndrew Thompson usb_error_t
usbd_req_reset_port(struct usb_device * udev,struct mtx * mtx,uint8_t port)795a593f6b8SAndrew Thompson usbd_req_reset_port(struct usb_device *udev, struct mtx *mtx, uint8_t port)
79602ac6454SAndrew Thompson {
797760bc48eSAndrew Thompson struct usb_port_status ps;
798e0a69b51SAndrew Thompson usb_error_t err;
79902ac6454SAndrew Thompson uint16_t n;
80093ee6e85SHans Petter Selasky uint16_t status;
80193ee6e85SHans Petter Selasky uint16_t change;
80202ac6454SAndrew Thompson
80393ee6e85SHans Petter Selasky DPRINTF("\n");
80493ee6e85SHans Petter Selasky
805f6bd103eSHans Petter Selasky /* clear any leftover port reset changes first */
806f6bd103eSHans Petter Selasky usbd_req_clear_port_feature(
807f6bd103eSHans Petter Selasky udev, mtx, port, UHF_C_PORT_RESET);
808f6bd103eSHans Petter Selasky
809f6bd103eSHans Petter Selasky /* assert port reset on the given port */
810f6bd103eSHans Petter Selasky err = usbd_req_set_port_feature(
811f6bd103eSHans Petter Selasky udev, mtx, port, UHF_PORT_RESET);
812f6bd103eSHans Petter Selasky
813f6bd103eSHans Petter Selasky /* check for errors */
814f6bd103eSHans Petter Selasky if (err)
81502ac6454SAndrew Thompson goto done;
81602ac6454SAndrew Thompson n = 0;
81702ac6454SAndrew Thompson while (1) {
81802ac6454SAndrew Thompson /* wait for the device to recover from reset */
81937506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay));
82037506412SHans Petter Selasky n += usb_port_reset_delay;
821a593f6b8SAndrew Thompson err = usbd_req_get_port_status(udev, mtx, &ps, port);
82293ee6e85SHans Petter Selasky if (err)
82302ac6454SAndrew Thompson goto done;
82493ee6e85SHans Petter Selasky
825f6bd103eSHans Petter Selasky status = UGETW(ps.wPortStatus);
826f6bd103eSHans Petter Selasky change = UGETW(ps.wPortChange);
827f6bd103eSHans Petter Selasky
8280e777d84SHans Petter Selasky /* if the device disappeared, just give up */
829f6bd103eSHans Petter Selasky if (!(status & UPS_CURRENT_CONNECT_STATUS))
8300e777d84SHans Petter Selasky goto done;
831f6bd103eSHans Petter Selasky
83202ac6454SAndrew Thompson /* check if reset is complete */
833f6bd103eSHans Petter Selasky if (change & UPS_C_PORT_RESET)
83402ac6454SAndrew Thompson break;
835f6bd103eSHans Petter Selasky
836f6bd103eSHans Petter Selasky /*
837f6bd103eSHans Petter Selasky * Some Virtual Machines like VirtualBox 4.x fail to
838f6bd103eSHans Petter Selasky * generate a port reset change event. Check if reset
839f6bd103eSHans Petter Selasky * is no longer asserted.
840f6bd103eSHans Petter Selasky */
841f6bd103eSHans Petter Selasky if (!(status & UPS_RESET))
842f6bd103eSHans Petter Selasky break;
843f6bd103eSHans Petter Selasky
84402ac6454SAndrew Thompson /* check for timeout */
84502ac6454SAndrew Thompson if (n > 1000) {
84602ac6454SAndrew Thompson n = 0;
84702ac6454SAndrew Thompson break;
84802ac6454SAndrew Thompson }
84902ac6454SAndrew Thompson }
85002ac6454SAndrew Thompson
85102ac6454SAndrew Thompson /* clear port reset first */
852a593f6b8SAndrew Thompson err = usbd_req_clear_port_feature(
85302ac6454SAndrew Thompson udev, mtx, port, UHF_C_PORT_RESET);
85493ee6e85SHans Petter Selasky if (err)
85502ac6454SAndrew Thompson goto done;
85693ee6e85SHans Petter Selasky
85702ac6454SAndrew Thompson /* check for timeout */
85802ac6454SAndrew Thompson if (n == 0) {
85902ac6454SAndrew Thompson err = USB_ERR_TIMEOUT;
86002ac6454SAndrew Thompson goto done;
86102ac6454SAndrew Thompson }
86202ac6454SAndrew Thompson /* wait for the device to recover from reset */
86337506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery));
86402ac6454SAndrew Thompson
86502ac6454SAndrew Thompson done:
86602ac6454SAndrew Thompson DPRINTFN(2, "port %d reset returning error=%s\n",
867a593f6b8SAndrew Thompson port, usbd_errstr(err));
86802ac6454SAndrew Thompson return (err);
86902ac6454SAndrew Thompson }
87002ac6454SAndrew Thompson
87102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
872cbb75751SHans Petter Selasky * usbd_req_warm_reset_port
873cbb75751SHans Petter Selasky *
874cbb75751SHans Petter Selasky * This function will instruct an USB HUB to perform a warm reset
875cbb75751SHans Petter Selasky * sequence on the specified port number. This kind of reset is not
876cbb75751SHans Petter Selasky * mandatory for LOW-, FULL- and HIGH-speed USB HUBs and is targeted
877cbb75751SHans Petter Selasky * for SUPER-speed USB HUBs.
878cbb75751SHans Petter Selasky *
879cbb75751SHans Petter Selasky * Returns:
880cbb75751SHans Petter Selasky * 0: Success. The USB device should now be available again.
881cbb75751SHans Petter Selasky * Else: Failure. No USB device is present and the USB port should be
882cbb75751SHans Petter Selasky * disabled.
883cbb75751SHans Petter Selasky *------------------------------------------------------------------------*/
884cbb75751SHans Petter Selasky usb_error_t
usbd_req_warm_reset_port(struct usb_device * udev,struct mtx * mtx,uint8_t port)88593ee6e85SHans Petter Selasky usbd_req_warm_reset_port(struct usb_device *udev, struct mtx *mtx,
88693ee6e85SHans Petter Selasky uint8_t port)
887cbb75751SHans Petter Selasky {
888cbb75751SHans Petter Selasky struct usb_port_status ps;
889cbb75751SHans Petter Selasky usb_error_t err;
890cbb75751SHans Petter Selasky uint16_t n;
89193ee6e85SHans Petter Selasky uint16_t status;
89293ee6e85SHans Petter Selasky uint16_t change;
893cbb75751SHans Petter Selasky
89493ee6e85SHans Petter Selasky DPRINTF("\n");
89593ee6e85SHans Petter Selasky
89693ee6e85SHans Petter Selasky err = usbd_req_get_port_status(udev, mtx, &ps, port);
89793ee6e85SHans Petter Selasky if (err)
898cbb75751SHans Petter Selasky goto done;
89993ee6e85SHans Petter Selasky
90093ee6e85SHans Petter Selasky status = UGETW(ps.wPortStatus);
90193ee6e85SHans Petter Selasky
90293ee6e85SHans Petter Selasky switch (UPS_PORT_LINK_STATE_GET(status)) {
90393ee6e85SHans Petter Selasky case UPS_PORT_LS_U3:
90493ee6e85SHans Petter Selasky case UPS_PORT_LS_COMP_MODE:
90593ee6e85SHans Petter Selasky case UPS_PORT_LS_LOOPBACK:
90693ee6e85SHans Petter Selasky case UPS_PORT_LS_SS_INA:
90793ee6e85SHans Petter Selasky break;
90893ee6e85SHans Petter Selasky default:
90993ee6e85SHans Petter Selasky DPRINTF("Wrong state for warm reset\n");
91093ee6e85SHans Petter Selasky return (0);
911cbb75751SHans Petter Selasky }
91293ee6e85SHans Petter Selasky
91393ee6e85SHans Petter Selasky /* clear any leftover warm port reset changes first */
91493ee6e85SHans Petter Selasky usbd_req_clear_port_feature(udev, mtx,
91593ee6e85SHans Petter Selasky port, UHF_C_BH_PORT_RESET);
91693ee6e85SHans Petter Selasky
91793ee6e85SHans Petter Selasky /* set warm port reset */
91893ee6e85SHans Petter Selasky err = usbd_req_set_port_feature(udev, mtx,
91993ee6e85SHans Petter Selasky port, UHF_BH_PORT_RESET);
92093ee6e85SHans Petter Selasky if (err)
92193ee6e85SHans Petter Selasky goto done;
92293ee6e85SHans Petter Selasky
923cbb75751SHans Petter Selasky n = 0;
924cbb75751SHans Petter Selasky while (1) {
925cbb75751SHans Petter Selasky /* wait for the device to recover from reset */
92637506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_delay));
92737506412SHans Petter Selasky n += usb_port_reset_delay;
928cbb75751SHans Petter Selasky err = usbd_req_get_port_status(udev, mtx, &ps, port);
92993ee6e85SHans Petter Selasky if (err)
930cbb75751SHans Petter Selasky goto done;
93193ee6e85SHans Petter Selasky
93293ee6e85SHans Petter Selasky status = UGETW(ps.wPortStatus);
93393ee6e85SHans Petter Selasky change = UGETW(ps.wPortChange);
93493ee6e85SHans Petter Selasky
935cbb75751SHans Petter Selasky /* if the device disappeared, just give up */
93693ee6e85SHans Petter Selasky if (!(status & UPS_CURRENT_CONNECT_STATUS))
937cbb75751SHans Petter Selasky goto done;
93893ee6e85SHans Petter Selasky
939cbb75751SHans Petter Selasky /* check if reset is complete */
94093ee6e85SHans Petter Selasky if (change & UPS_C_BH_PORT_RESET)
941cbb75751SHans Petter Selasky break;
94293ee6e85SHans Petter Selasky
943cbb75751SHans Petter Selasky /* check for timeout */
944cbb75751SHans Petter Selasky if (n > 1000) {
945cbb75751SHans Petter Selasky n = 0;
946cbb75751SHans Petter Selasky break;
947cbb75751SHans Petter Selasky }
948cbb75751SHans Petter Selasky }
949cbb75751SHans Petter Selasky
950cbb75751SHans Petter Selasky /* clear port reset first */
951cbb75751SHans Petter Selasky err = usbd_req_clear_port_feature(
952cbb75751SHans Petter Selasky udev, mtx, port, UHF_C_BH_PORT_RESET);
95393ee6e85SHans Petter Selasky if (err)
954cbb75751SHans Petter Selasky goto done;
95593ee6e85SHans Petter Selasky
956cbb75751SHans Petter Selasky /* check for timeout */
957cbb75751SHans Petter Selasky if (n == 0) {
958cbb75751SHans Petter Selasky err = USB_ERR_TIMEOUT;
959cbb75751SHans Petter Selasky goto done;
960cbb75751SHans Petter Selasky }
961cbb75751SHans Petter Selasky /* wait for the device to recover from reset */
96237506412SHans Petter Selasky usb_pause_mtx(mtx, USB_MS_TO_TICKS(usb_port_reset_recovery));
963cbb75751SHans Petter Selasky
964cbb75751SHans Petter Selasky done:
965cbb75751SHans Petter Selasky DPRINTFN(2, "port %d warm reset returning error=%s\n",
966cbb75751SHans Petter Selasky port, usbd_errstr(err));
967cbb75751SHans Petter Selasky return (err);
968cbb75751SHans Petter Selasky }
969cbb75751SHans Petter Selasky
970cbb75751SHans Petter Selasky /*------------------------------------------------------------------------*
971a593f6b8SAndrew Thompson * usbd_req_get_desc
97202ac6454SAndrew Thompson *
97302ac6454SAndrew Thompson * This function can be used to retrieve USB descriptors. It contains
97402ac6454SAndrew Thompson * some additional logic like zeroing of missing descriptor bytes and
97502ac6454SAndrew Thompson * retrying an USB descriptor in case of failure. The "min_len"
97602ac6454SAndrew Thompson * argument specifies the minimum descriptor length. The "max_len"
97702ac6454SAndrew Thompson * argument specifies the maximum descriptor length. If the real
97802ac6454SAndrew Thompson * descriptor length is less than the minimum length the missing
97916589beaSAndrew Thompson * byte(s) will be zeroed. The type field, the second byte of the USB
98016589beaSAndrew Thompson * descriptor, will get forced to the correct type. If the "actlen"
98116589beaSAndrew Thompson * pointer is non-NULL, the actual length of the transfer will get
98216589beaSAndrew Thompson * stored in the 16-bit unsigned integer which it is pointing to. The
98316589beaSAndrew Thompson * first byte of the descriptor will not get updated. If the "actlen"
98416589beaSAndrew Thompson * pointer is NULL the first byte of the descriptor will get updated
98516589beaSAndrew Thompson * to reflect the actual length instead. If "min_len" is not equal to
98616589beaSAndrew Thompson * "max_len" then this function will try to retrive the beginning of
98716589beaSAndrew Thompson * the descriptor and base the maximum length on the first byte of the
98816589beaSAndrew Thompson * descriptor.
98902ac6454SAndrew Thompson *
99002ac6454SAndrew Thompson * Returns:
99102ac6454SAndrew Thompson * 0: Success
99202ac6454SAndrew Thompson * Else: Failure
99302ac6454SAndrew Thompson *------------------------------------------------------------------------*/
994e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_desc(struct usb_device * udev,struct mtx * mtx,uint16_t * actlen,void * desc,uint16_t min_len,uint16_t max_len,uint16_t id,uint8_t type,uint8_t index,uint8_t retries)995a593f6b8SAndrew Thompson usbd_req_get_desc(struct usb_device *udev,
99616589beaSAndrew Thompson struct mtx *mtx, uint16_t *actlen, void *desc,
99702ac6454SAndrew Thompson uint16_t min_len, uint16_t max_len,
99802ac6454SAndrew Thompson uint16_t id, uint8_t type, uint8_t index,
99902ac6454SAndrew Thompson uint8_t retries)
100002ac6454SAndrew Thompson {
1001760bc48eSAndrew Thompson struct usb_device_request req;
1002b774480cSHans Petter Selasky uint8_t *buf = desc;
1003e0a69b51SAndrew Thompson usb_error_t err;
100402ac6454SAndrew Thompson
100502ac6454SAndrew Thompson DPRINTFN(4, "id=%d, type=%d, index=%d, max_len=%d\n",
100602ac6454SAndrew Thompson id, type, index, max_len);
100702ac6454SAndrew Thompson
100802ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE;
100902ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR;
101002ac6454SAndrew Thompson USETW2(req.wValue, type, index);
101102ac6454SAndrew Thompson USETW(req.wIndex, id);
101202ac6454SAndrew Thompson
101302ac6454SAndrew Thompson while (1) {
101402ac6454SAndrew Thompson if ((min_len < 2) || (max_len < 2)) {
101502ac6454SAndrew Thompson err = USB_ERR_INVAL;
101602ac6454SAndrew Thompson goto done;
101702ac6454SAndrew Thompson }
101802ac6454SAndrew Thompson USETW(req.wLength, min_len);
101902ac6454SAndrew Thompson
1020a593f6b8SAndrew Thompson err = usbd_do_request_flags(udev, mtx, &req,
10212dea6902SHans Petter Selasky desc, 0, NULL, 1000 /* ms */);
102202ac6454SAndrew Thompson
1023b774480cSHans Petter Selasky if (err != 0 && err != USB_ERR_TIMEOUT &&
1024b774480cSHans Petter Selasky min_len != max_len) {
1025b774480cSHans Petter Selasky /* clear descriptor data */
1026b774480cSHans Petter Selasky memset(desc, 0, max_len);
1027b774480cSHans Petter Selasky
1028b774480cSHans Petter Selasky /* try to read full descriptor length */
1029b774480cSHans Petter Selasky USETW(req.wLength, max_len);
1030b774480cSHans Petter Selasky
1031b774480cSHans Petter Selasky err = usbd_do_request_flags(udev, mtx, &req,
10322dea6902SHans Petter Selasky desc, USB_SHORT_XFER_OK, NULL, 1000 /* ms */);
1033b774480cSHans Petter Selasky
1034b774480cSHans Petter Selasky if (err == 0) {
1035b774480cSHans Petter Selasky /* verify length */
1036b774480cSHans Petter Selasky if (buf[0] > max_len)
1037b774480cSHans Petter Selasky buf[0] = max_len;
1038b774480cSHans Petter Selasky else if (buf[0] < 2)
1039b774480cSHans Petter Selasky err = USB_ERR_INVAL;
1040b774480cSHans Petter Selasky
1041b774480cSHans Petter Selasky min_len = buf[0];
1042b774480cSHans Petter Selasky
1043b774480cSHans Petter Selasky /* enforce descriptor type */
1044b774480cSHans Petter Selasky buf[1] = type;
1045b774480cSHans Petter Selasky goto done;
1046b774480cSHans Petter Selasky }
1047b774480cSHans Petter Selasky }
1048b774480cSHans Petter Selasky
104902ac6454SAndrew Thompson if (err) {
105002ac6454SAndrew Thompson if (!retries) {
105102ac6454SAndrew Thompson goto done;
105202ac6454SAndrew Thompson }
105302ac6454SAndrew Thompson retries--;
105402ac6454SAndrew Thompson
1055a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 5);
105602ac6454SAndrew Thompson
105702ac6454SAndrew Thompson continue;
105802ac6454SAndrew Thompson }
105902ac6454SAndrew Thompson
106002ac6454SAndrew Thompson if (min_len == max_len) {
106116589beaSAndrew Thompson /* enforce correct length */
106216589beaSAndrew Thompson if ((buf[0] > min_len) && (actlen == NULL))
106302ac6454SAndrew Thompson buf[0] = min_len;
106416589beaSAndrew Thompson
106516589beaSAndrew Thompson /* enforce correct type */
106602ac6454SAndrew Thompson buf[1] = type;
106702ac6454SAndrew Thompson
106802ac6454SAndrew Thompson goto done;
106902ac6454SAndrew Thompson }
107002ac6454SAndrew Thompson /* range check */
107102ac6454SAndrew Thompson
107202ac6454SAndrew Thompson if (max_len > buf[0]) {
107302ac6454SAndrew Thompson max_len = buf[0];
107402ac6454SAndrew Thompson }
107502ac6454SAndrew Thompson /* zero minimum data */
107602ac6454SAndrew Thompson
107702ac6454SAndrew Thompson while (min_len > max_len) {
107802ac6454SAndrew Thompson min_len--;
107902ac6454SAndrew Thompson buf[min_len] = 0;
108002ac6454SAndrew Thompson }
108102ac6454SAndrew Thompson
108202ac6454SAndrew Thompson /* set new minimum length */
108302ac6454SAndrew Thompson
108402ac6454SAndrew Thompson min_len = max_len;
108502ac6454SAndrew Thompson }
108602ac6454SAndrew Thompson done:
108716589beaSAndrew Thompson if (actlen != NULL) {
108816589beaSAndrew Thompson if (err)
108916589beaSAndrew Thompson *actlen = 0;
109016589beaSAndrew Thompson else
109116589beaSAndrew Thompson *actlen = min_len;
109216589beaSAndrew Thompson }
109302ac6454SAndrew Thompson return (err);
109402ac6454SAndrew Thompson }
109502ac6454SAndrew Thompson
109602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1097a593f6b8SAndrew Thompson * usbd_req_get_string_any
109802ac6454SAndrew Thompson *
109902ac6454SAndrew Thompson * This function will return the string given by "string_index"
110002ac6454SAndrew Thompson * using the first language ID. The maximum length "len" includes
110102ac6454SAndrew Thompson * the terminating zero. The "len" argument should be twice as
110202ac6454SAndrew Thompson * big pluss 2 bytes, compared with the actual maximum string length !
110302ac6454SAndrew Thompson *
110402ac6454SAndrew Thompson * Returns:
110502ac6454SAndrew Thompson * 0: Success
110602ac6454SAndrew Thompson * Else: Failure
110702ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1108e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_string_any(struct usb_device * udev,struct mtx * mtx,char * buf,uint16_t len,uint8_t string_index)1109a593f6b8SAndrew Thompson usbd_req_get_string_any(struct usb_device *udev, struct mtx *mtx, char *buf,
111002ac6454SAndrew Thompson uint16_t len, uint8_t string_index)
111102ac6454SAndrew Thompson {
111202ac6454SAndrew Thompson char *s;
111302ac6454SAndrew Thompson uint8_t *temp;
111402ac6454SAndrew Thompson uint16_t i;
111502ac6454SAndrew Thompson uint16_t n;
111602ac6454SAndrew Thompson uint16_t c;
111702ac6454SAndrew Thompson uint8_t swap;
1118e0a69b51SAndrew Thompson usb_error_t err;
111902ac6454SAndrew Thompson
112002ac6454SAndrew Thompson if (len == 0) {
112102ac6454SAndrew Thompson /* should not happen */
112202ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION);
112302ac6454SAndrew Thompson }
112402ac6454SAndrew Thompson if (string_index == 0) {
112502ac6454SAndrew Thompson /* this is the language table */
112602ac6454SAndrew Thompson buf[0] = 0;
112702ac6454SAndrew Thompson return (USB_ERR_INVAL);
112802ac6454SAndrew Thompson }
112902ac6454SAndrew Thompson if (udev->flags.no_strings) {
113002ac6454SAndrew Thompson buf[0] = 0;
113102ac6454SAndrew Thompson return (USB_ERR_STALLED);
113202ac6454SAndrew Thompson }
1133a593f6b8SAndrew Thompson err = usbd_req_get_string_desc
113402ac6454SAndrew Thompson (udev, mtx, buf, len, udev->langid, string_index);
113502ac6454SAndrew Thompson if (err) {
113602ac6454SAndrew Thompson buf[0] = 0;
113702ac6454SAndrew Thompson return (err);
113802ac6454SAndrew Thompson }
113902ac6454SAndrew Thompson temp = (uint8_t *)buf;
114002ac6454SAndrew Thompson
114102ac6454SAndrew Thompson if (temp[0] < 2) {
114202ac6454SAndrew Thompson /* string length is too short */
114302ac6454SAndrew Thompson buf[0] = 0;
114402ac6454SAndrew Thompson return (USB_ERR_INVAL);
114502ac6454SAndrew Thompson }
114602ac6454SAndrew Thompson /* reserve one byte for terminating zero */
114702ac6454SAndrew Thompson len--;
114802ac6454SAndrew Thompson
114902ac6454SAndrew Thompson /* find maximum length */
115002ac6454SAndrew Thompson s = buf;
115102ac6454SAndrew Thompson n = (temp[0] / 2) - 1;
115202ac6454SAndrew Thompson if (n > len) {
115302ac6454SAndrew Thompson n = len;
115402ac6454SAndrew Thompson }
115502ac6454SAndrew Thompson /* skip descriptor header */
115602ac6454SAndrew Thompson temp += 2;
115702ac6454SAndrew Thompson
115802ac6454SAndrew Thompson /* reset swap state */
115902ac6454SAndrew Thompson swap = 3;
116002ac6454SAndrew Thompson
116102ac6454SAndrew Thompson /* convert and filter */
116202ac6454SAndrew Thompson for (i = 0; (i != n); i++) {
116302ac6454SAndrew Thompson c = UGETW(temp + (2 * i));
116402ac6454SAndrew Thompson
116502ac6454SAndrew Thompson /* convert from Unicode, handle buggy strings */
116602ac6454SAndrew Thompson if (((c & 0xff00) == 0) && (swap & 1)) {
116702ac6454SAndrew Thompson /* Little Endian, default */
116802ac6454SAndrew Thompson *s = c;
116902ac6454SAndrew Thompson swap = 1;
117002ac6454SAndrew Thompson } else if (((c & 0x00ff) == 0) && (swap & 2)) {
117102ac6454SAndrew Thompson /* Big Endian */
117202ac6454SAndrew Thompson *s = c >> 8;
117302ac6454SAndrew Thompson swap = 2;
117402ac6454SAndrew Thompson } else {
117502ac6454SAndrew Thompson /* silently skip bad character */
117602ac6454SAndrew Thompson continue;
117702ac6454SAndrew Thompson }
117802ac6454SAndrew Thompson
117902ac6454SAndrew Thompson /*
1180b64cf89fSHans Petter Selasky * Filter by default - We only allow alphanumerical
1181b64cf89fSHans Petter Selasky * and a few more to avoid any problems with scripts
1182b64cf89fSHans Petter Selasky * and daemons.
118302ac6454SAndrew Thompson */
1184b64cf89fSHans Petter Selasky if (isalpha(*s) ||
1185b64cf89fSHans Petter Selasky isdigit(*s) ||
1186b64cf89fSHans Petter Selasky *s == '-' ||
1187b64cf89fSHans Petter Selasky *s == '+' ||
1188b64cf89fSHans Petter Selasky *s == ' ' ||
1189b64cf89fSHans Petter Selasky *s == '.' ||
119060737149SEdward Tomasz Napierala *s == ',' ||
119160737149SEdward Tomasz Napierala *s == ':' ||
119260737149SEdward Tomasz Napierala *s == '/' ||
119360737149SEdward Tomasz Napierala *s == '(' ||
119460737149SEdward Tomasz Napierala *s == ')') {
1195b64cf89fSHans Petter Selasky /* allowed */
119602ac6454SAndrew Thompson s++;
119702ac6454SAndrew Thompson }
1198b64cf89fSHans Petter Selasky /* silently skip bad character */
1199b64cf89fSHans Petter Selasky }
120002ac6454SAndrew Thompson *s = 0; /* zero terminate resulting string */
120102ac6454SAndrew Thompson return (USB_ERR_NORMAL_COMPLETION);
120202ac6454SAndrew Thompson }
120302ac6454SAndrew Thompson
120402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1205a593f6b8SAndrew Thompson * usbd_req_get_string_desc
120602ac6454SAndrew Thompson *
120702ac6454SAndrew Thompson * If you don't know the language ID, consider using
1208a593f6b8SAndrew Thompson * "usbd_req_get_string_any()".
120902ac6454SAndrew Thompson *
121002ac6454SAndrew Thompson * Returns:
121102ac6454SAndrew Thompson * 0: Success
121202ac6454SAndrew Thompson * Else: Failure
121302ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1214e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_string_desc(struct usb_device * udev,struct mtx * mtx,void * sdesc,uint16_t max_len,uint16_t lang_id,uint8_t string_index)1215a593f6b8SAndrew Thompson usbd_req_get_string_desc(struct usb_device *udev, struct mtx *mtx, void *sdesc,
121602ac6454SAndrew Thompson uint16_t max_len, uint16_t lang_id,
121702ac6454SAndrew Thompson uint8_t string_index)
121802ac6454SAndrew Thompson {
1219a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, sdesc, 2, max_len, lang_id,
122002ac6454SAndrew Thompson UDESC_STRING, string_index, 0));
122102ac6454SAndrew Thompson }
122202ac6454SAndrew Thompson
122302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1224a593f6b8SAndrew Thompson * usbd_req_get_config_desc_ptr
12257efaaa9aSAndrew Thompson *
12267efaaa9aSAndrew Thompson * This function is used in device side mode to retrieve the pointer
12277efaaa9aSAndrew Thompson * to the generated config descriptor. This saves allocating space for
12287efaaa9aSAndrew Thompson * an additional config descriptor when setting the configuration.
12297efaaa9aSAndrew Thompson *
12307efaaa9aSAndrew Thompson * Returns:
12317efaaa9aSAndrew Thompson * 0: Success
12327efaaa9aSAndrew Thompson * Else: Failure
12337efaaa9aSAndrew Thompson *------------------------------------------------------------------------*/
1234e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_descriptor_ptr(struct usb_device * udev,struct usb_config_descriptor ** ppcd,uint16_t wValue)1235a593f6b8SAndrew Thompson usbd_req_get_descriptor_ptr(struct usb_device *udev,
1236760bc48eSAndrew Thompson struct usb_config_descriptor **ppcd, uint16_t wValue)
12377efaaa9aSAndrew Thompson {
1238760bc48eSAndrew Thompson struct usb_device_request req;
1239e0a69b51SAndrew Thompson usb_handle_req_t *hr_func;
1240459d369eSAndrew Thompson const void *ptr;
1241459d369eSAndrew Thompson uint16_t len;
1242e0a69b51SAndrew Thompson usb_error_t err;
12437efaaa9aSAndrew Thompson
124463521bbcSAndrew Thompson req.bmRequestType = UT_READ_DEVICE;
12457efaaa9aSAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR;
1246459d369eSAndrew Thompson USETW(req.wValue, wValue);
12477efaaa9aSAndrew Thompson USETW(req.wIndex, 0);
12487efaaa9aSAndrew Thompson USETW(req.wLength, 0);
12497efaaa9aSAndrew Thompson
1250459d369eSAndrew Thompson ptr = NULL;
1251459d369eSAndrew Thompson len = 0;
12527efaaa9aSAndrew Thompson
1253a593f6b8SAndrew Thompson hr_func = usbd_get_hr_func(udev);
1254459d369eSAndrew Thompson
1255459d369eSAndrew Thompson if (hr_func == NULL)
1256459d369eSAndrew Thompson err = USB_ERR_INVAL;
1257459d369eSAndrew Thompson else {
1258459d369eSAndrew Thompson USB_BUS_LOCK(udev->bus);
1259459d369eSAndrew Thompson err = (hr_func) (udev, &req, &ptr, &len);
1260459d369eSAndrew Thompson USB_BUS_UNLOCK(udev->bus);
1261459d369eSAndrew Thompson }
1262459d369eSAndrew Thompson
1263459d369eSAndrew Thompson if (err)
1264459d369eSAndrew Thompson ptr = NULL;
1265459d369eSAndrew Thompson else if (ptr == NULL)
1266459d369eSAndrew Thompson err = USB_ERR_INVAL;
1267459d369eSAndrew Thompson
1268760bc48eSAndrew Thompson *ppcd = __DECONST(struct usb_config_descriptor *, ptr);
1269459d369eSAndrew Thompson
1270459d369eSAndrew Thompson return (err);
12717efaaa9aSAndrew Thompson }
12727efaaa9aSAndrew Thompson
12737efaaa9aSAndrew Thompson /*------------------------------------------------------------------------*
1274a593f6b8SAndrew Thompson * usbd_req_get_config_desc
127502ac6454SAndrew Thompson *
127602ac6454SAndrew Thompson * Returns:
127702ac6454SAndrew Thompson * 0: Success
127802ac6454SAndrew Thompson * Else: Failure
127902ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1280e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_config_desc(struct usb_device * udev,struct mtx * mtx,struct usb_config_descriptor * d,uint8_t conf_index)1281a593f6b8SAndrew Thompson usbd_req_get_config_desc(struct usb_device *udev, struct mtx *mtx,
1282760bc48eSAndrew Thompson struct usb_config_descriptor *d, uint8_t conf_index)
128302ac6454SAndrew Thompson {
1284e0a69b51SAndrew Thompson usb_error_t err;
128502ac6454SAndrew Thompson
128602ac6454SAndrew Thompson DPRINTFN(4, "confidx=%d\n", conf_index);
128702ac6454SAndrew Thompson
1288a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d),
128902ac6454SAndrew Thompson sizeof(*d), 0, UDESC_CONFIG, conf_index, 0);
129002ac6454SAndrew Thompson if (err) {
129102ac6454SAndrew Thompson goto done;
129202ac6454SAndrew Thompson }
129302ac6454SAndrew Thompson /* Extra sanity checking */
12946d917491SHans Petter Selasky if (UGETW(d->wTotalLength) < (uint16_t)sizeof(*d)) {
129502ac6454SAndrew Thompson err = USB_ERR_INVAL;
129602ac6454SAndrew Thompson }
129702ac6454SAndrew Thompson done:
129802ac6454SAndrew Thompson return (err);
129902ac6454SAndrew Thompson }
130002ac6454SAndrew Thompson
130102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
13022c79a775SHans Petter Selasky * usbd_alloc_config_desc
13032c79a775SHans Petter Selasky *
13042c79a775SHans Petter Selasky * This function is used to allocate a zeroed configuration
13052c79a775SHans Petter Selasky * descriptor.
13062c79a775SHans Petter Selasky *
13072c79a775SHans Petter Selasky * Returns:
13082c79a775SHans Petter Selasky * NULL: Failure
13092c79a775SHans Petter Selasky * Else: Success
13102c79a775SHans Petter Selasky *------------------------------------------------------------------------*/
13112c79a775SHans Petter Selasky void *
usbd_alloc_config_desc(struct usb_device * udev,uint32_t size)13122c79a775SHans Petter Selasky usbd_alloc_config_desc(struct usb_device *udev, uint32_t size)
13132c79a775SHans Petter Selasky {
13142c79a775SHans Petter Selasky if (size > USB_CONFIG_MAX) {
13152c79a775SHans Petter Selasky DPRINTF("Configuration descriptor too big\n");
13162c79a775SHans Petter Selasky return (NULL);
13172c79a775SHans Petter Selasky }
13182c79a775SHans Petter Selasky #if (USB_HAVE_FIXED_CONFIG == 0)
13192c79a775SHans Petter Selasky return (malloc(size, M_USBDEV, M_ZERO | M_WAITOK));
13202c79a775SHans Petter Selasky #else
13212c79a775SHans Petter Selasky memset(udev->config_data, 0, sizeof(udev->config_data));
13222c79a775SHans Petter Selasky return (udev->config_data);
13232c79a775SHans Petter Selasky #endif
13242c79a775SHans Petter Selasky }
13252c79a775SHans Petter Selasky
13262c79a775SHans Petter Selasky /*------------------------------------------------------------------------*
13272c79a775SHans Petter Selasky * usbd_alloc_config_desc
13282c79a775SHans Petter Selasky *
13292c79a775SHans Petter Selasky * This function is used to free a configuration descriptor.
13302c79a775SHans Petter Selasky *------------------------------------------------------------------------*/
13312c79a775SHans Petter Selasky void
usbd_free_config_desc(struct usb_device * udev,void * ptr)13322c79a775SHans Petter Selasky usbd_free_config_desc(struct usb_device *udev, void *ptr)
13332c79a775SHans Petter Selasky {
13342c79a775SHans Petter Selasky #if (USB_HAVE_FIXED_CONFIG == 0)
13352c79a775SHans Petter Selasky free(ptr, M_USBDEV);
13362c79a775SHans Petter Selasky #endif
13372c79a775SHans Petter Selasky }
13382c79a775SHans Petter Selasky
13392c79a775SHans Petter Selasky /*------------------------------------------------------------------------*
1340a593f6b8SAndrew Thompson * usbd_req_get_config_desc_full
134102ac6454SAndrew Thompson *
134202ac6454SAndrew Thompson * This function gets the complete USB configuration descriptor and
13432c79a775SHans Petter Selasky * ensures that "wTotalLength" is correct. The returned configuration
13442c79a775SHans Petter Selasky * descriptor is freed by calling "usbd_free_config_desc()".
134502ac6454SAndrew Thompson *
134602ac6454SAndrew Thompson * Returns:
134702ac6454SAndrew Thompson * 0: Success
134802ac6454SAndrew Thompson * Else: Failure
134902ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1350e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_config_desc_full(struct usb_device * udev,struct mtx * mtx,struct usb_config_descriptor ** ppcd,uint8_t index)1351a593f6b8SAndrew Thompson usbd_req_get_config_desc_full(struct usb_device *udev, struct mtx *mtx,
13522c79a775SHans Petter Selasky struct usb_config_descriptor **ppcd, uint8_t index)
135302ac6454SAndrew Thompson {
1354760bc48eSAndrew Thompson struct usb_config_descriptor cd;
1355760bc48eSAndrew Thompson struct usb_config_descriptor *cdesc;
13565b0752bbSHans Petter Selasky uint32_t len;
1357e0a69b51SAndrew Thompson usb_error_t err;
135802ac6454SAndrew Thompson
135902ac6454SAndrew Thompson DPRINTFN(4, "index=%d\n", index);
136002ac6454SAndrew Thompson
136102ac6454SAndrew Thompson *ppcd = NULL;
136202ac6454SAndrew Thompson
1363a593f6b8SAndrew Thompson err = usbd_req_get_config_desc(udev, mtx, &cd, index);
13645b0752bbSHans Petter Selasky if (err)
136502ac6454SAndrew Thompson return (err);
13665b0752bbSHans Petter Selasky
136702ac6454SAndrew Thompson /* get full descriptor */
136802ac6454SAndrew Thompson len = UGETW(cd.wTotalLength);
13695b0752bbSHans Petter Selasky if (len < (uint32_t)sizeof(*cdesc)) {
137002ac6454SAndrew Thompson /* corrupt descriptor */
137102ac6454SAndrew Thompson return (USB_ERR_INVAL);
13725b0752bbSHans Petter Selasky } else if (len > USB_CONFIG_MAX) {
13735b0752bbSHans Petter Selasky DPRINTF("Configuration descriptor was truncated\n");
13745b0752bbSHans Petter Selasky len = USB_CONFIG_MAX;
137502ac6454SAndrew Thompson }
13762c79a775SHans Petter Selasky cdesc = usbd_alloc_config_desc(udev, len);
13775b0752bbSHans Petter Selasky if (cdesc == NULL)
137802ac6454SAndrew Thompson return (USB_ERR_NOMEM);
1379a593f6b8SAndrew Thompson err = usbd_req_get_desc(udev, mtx, NULL, cdesc, len, len, 0,
138002ac6454SAndrew Thompson UDESC_CONFIG, index, 3);
138102ac6454SAndrew Thompson if (err) {
13822c79a775SHans Petter Selasky usbd_free_config_desc(udev, cdesc);
138302ac6454SAndrew Thompson return (err);
138402ac6454SAndrew Thompson }
138502ac6454SAndrew Thompson /* make sure that the device is not fooling us: */
138602ac6454SAndrew Thompson USETW(cdesc->wTotalLength, len);
138702ac6454SAndrew Thompson
138802ac6454SAndrew Thompson *ppcd = cdesc;
138902ac6454SAndrew Thompson
139002ac6454SAndrew Thompson return (0); /* success */
139102ac6454SAndrew Thompson }
139202ac6454SAndrew Thompson
139302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1394a593f6b8SAndrew Thompson * usbd_req_get_device_desc
139502ac6454SAndrew Thompson *
139602ac6454SAndrew Thompson * Returns:
139702ac6454SAndrew Thompson * 0: Success
139802ac6454SAndrew Thompson * Else: Failure
139902ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1400e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_device_desc(struct usb_device * udev,struct mtx * mtx,struct usb_device_descriptor * d)1401a593f6b8SAndrew Thompson usbd_req_get_device_desc(struct usb_device *udev, struct mtx *mtx,
1402760bc48eSAndrew Thompson struct usb_device_descriptor *d)
140302ac6454SAndrew Thompson {
140402ac6454SAndrew Thompson DPRINTFN(4, "\n");
1405a593f6b8SAndrew Thompson return (usbd_req_get_desc(udev, mtx, NULL, d, sizeof(*d),
140602ac6454SAndrew Thompson sizeof(*d), 0, UDESC_DEVICE, 0, 3));
140702ac6454SAndrew Thompson }
140802ac6454SAndrew Thompson
140902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1410a593f6b8SAndrew Thompson * usbd_req_get_alt_interface_no
141102ac6454SAndrew Thompson *
141202ac6454SAndrew Thompson * Returns:
141302ac6454SAndrew Thompson * 0: Success
141402ac6454SAndrew Thompson * Else: Failure
141502ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1416e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_alt_interface_no(struct usb_device * udev,struct mtx * mtx,uint8_t * alt_iface_no,uint8_t iface_index)1417a593f6b8SAndrew Thompson usbd_req_get_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
141802ac6454SAndrew Thompson uint8_t *alt_iface_no, uint8_t iface_index)
141902ac6454SAndrew Thompson {
1420a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1421760bc48eSAndrew Thompson struct usb_device_request req;
142202ac6454SAndrew Thompson
1423bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL))
142402ac6454SAndrew Thompson return (USB_ERR_INVAL);
1425bd73b187SAlfred Perlstein
142602ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE;
142702ac6454SAndrew Thompson req.bRequest = UR_GET_INTERFACE;
142802ac6454SAndrew Thompson USETW(req.wValue, 0);
142902ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber;
143002ac6454SAndrew Thompson req.wIndex[1] = 0;
143102ac6454SAndrew Thompson USETW(req.wLength, 1);
1432a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, alt_iface_no));
143302ac6454SAndrew Thompson }
143402ac6454SAndrew Thompson
143502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1436a593f6b8SAndrew Thompson * usbd_req_set_alt_interface_no
143702ac6454SAndrew Thompson *
143802ac6454SAndrew Thompson * Returns:
143902ac6454SAndrew Thompson * 0: Success
144002ac6454SAndrew Thompson * Else: Failure
144102ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1442e0a69b51SAndrew Thompson usb_error_t
usbd_req_set_alt_interface_no(struct usb_device * udev,struct mtx * mtx,uint8_t iface_index,uint8_t alt_no)1443a593f6b8SAndrew Thompson usbd_req_set_alt_interface_no(struct usb_device *udev, struct mtx *mtx,
144402ac6454SAndrew Thompson uint8_t iface_index, uint8_t alt_no)
144502ac6454SAndrew Thompson {
1446a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1447760bc48eSAndrew Thompson struct usb_device_request req;
1448b8b3f4fdSHans Petter Selasky usb_error_t err;
144902ac6454SAndrew Thompson
1450bd73b187SAlfred Perlstein if ((iface == NULL) || (iface->idesc == NULL))
145102ac6454SAndrew Thompson return (USB_ERR_INVAL);
1452bd73b187SAlfred Perlstein
145302ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_INTERFACE;
145402ac6454SAndrew Thompson req.bRequest = UR_SET_INTERFACE;
145502ac6454SAndrew Thompson req.wValue[0] = alt_no;
145602ac6454SAndrew Thompson req.wValue[1] = 0;
145702ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber;
145802ac6454SAndrew Thompson req.wIndex[1] = 0;
145902ac6454SAndrew Thompson USETW(req.wLength, 0);
1460b8b3f4fdSHans Petter Selasky err = usbd_do_request(udev, mtx, &req, 0);
1461b8b3f4fdSHans Petter Selasky if (err == USB_ERR_STALLED && iface->num_altsetting == 1) {
1462b8b3f4fdSHans Petter Selasky /*
1463b8b3f4fdSHans Petter Selasky * The USB specification chapter 9.4.10 says that USB
1464b8b3f4fdSHans Petter Selasky * devices having only one alternate setting are
1465b8b3f4fdSHans Petter Selasky * allowed to STALL this request. Ignore this failure.
1466b8b3f4fdSHans Petter Selasky */
1467b8b3f4fdSHans Petter Selasky err = 0;
1468b8b3f4fdSHans Petter Selasky DPRINTF("Setting default alternate number failed. (ignored)\n");
1469b8b3f4fdSHans Petter Selasky }
1470b8b3f4fdSHans Petter Selasky return (err);
147102ac6454SAndrew Thompson }
147202ac6454SAndrew Thompson
147302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1474a593f6b8SAndrew Thompson * usbd_req_get_device_status
147502ac6454SAndrew Thompson *
147602ac6454SAndrew Thompson * Returns:
147702ac6454SAndrew Thompson * 0: Success
147802ac6454SAndrew Thompson * Else: Failure
147902ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1480e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_device_status(struct usb_device * udev,struct mtx * mtx,struct usb_status * st)1481a593f6b8SAndrew Thompson usbd_req_get_device_status(struct usb_device *udev, struct mtx *mtx,
1482760bc48eSAndrew Thompson struct usb_status *st)
148302ac6454SAndrew Thompson {
1484760bc48eSAndrew Thompson struct usb_device_request req;
148502ac6454SAndrew Thompson
148602ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE;
148702ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS;
148802ac6454SAndrew Thompson USETW(req.wValue, 0);
148902ac6454SAndrew Thompson USETW(req.wIndex, 0);
149002ac6454SAndrew Thompson USETW(req.wLength, sizeof(*st));
1491a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st));
149202ac6454SAndrew Thompson }
149302ac6454SAndrew Thompson
149402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1495a593f6b8SAndrew Thompson * usbd_req_get_hub_descriptor
149602ac6454SAndrew Thompson *
149702ac6454SAndrew Thompson * Returns:
149802ac6454SAndrew Thompson * 0: Success
149902ac6454SAndrew Thompson * Else: Failure
150002ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1501e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_hub_descriptor(struct usb_device * udev,struct mtx * mtx,struct usb_hub_descriptor * hd,uint8_t nports)1502a593f6b8SAndrew Thompson usbd_req_get_hub_descriptor(struct usb_device *udev, struct mtx *mtx,
1503760bc48eSAndrew Thompson struct usb_hub_descriptor *hd, uint8_t nports)
150402ac6454SAndrew Thompson {
1505760bc48eSAndrew Thompson struct usb_device_request req;
150602ac6454SAndrew Thompson uint16_t len = (nports + 7 + (8 * 8)) / 8;
150702ac6454SAndrew Thompson
150802ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE;
150902ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR;
151002ac6454SAndrew Thompson USETW2(req.wValue, UDESC_HUB, 0);
151102ac6454SAndrew Thompson USETW(req.wIndex, 0);
151202ac6454SAndrew Thompson USETW(req.wLength, len);
1513a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, hd));
151402ac6454SAndrew Thompson }
151502ac6454SAndrew Thompson
151602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1517963169b4SHans Petter Selasky * usbd_req_get_ss_hub_descriptor
1518963169b4SHans Petter Selasky *
1519963169b4SHans Petter Selasky * Returns:
1520963169b4SHans Petter Selasky * 0: Success
1521963169b4SHans Petter Selasky * Else: Failure
1522963169b4SHans Petter Selasky *------------------------------------------------------------------------*/
1523963169b4SHans Petter Selasky usb_error_t
usbd_req_get_ss_hub_descriptor(struct usb_device * udev,struct mtx * mtx,struct usb_hub_ss_descriptor * hd,uint8_t nports)1524963169b4SHans Petter Selasky usbd_req_get_ss_hub_descriptor(struct usb_device *udev, struct mtx *mtx,
1525963169b4SHans Petter Selasky struct usb_hub_ss_descriptor *hd, uint8_t nports)
1526963169b4SHans Petter Selasky {
1527963169b4SHans Petter Selasky struct usb_device_request req;
1528963169b4SHans Petter Selasky uint16_t len = sizeof(*hd) - 32 + 1 + ((nports + 7) / 8);
1529963169b4SHans Petter Selasky
1530963169b4SHans Petter Selasky req.bmRequestType = UT_READ_CLASS_DEVICE;
1531963169b4SHans Petter Selasky req.bRequest = UR_GET_DESCRIPTOR;
1532963169b4SHans Petter Selasky USETW2(req.wValue, UDESC_SS_HUB, 0);
1533963169b4SHans Petter Selasky USETW(req.wIndex, 0);
1534963169b4SHans Petter Selasky USETW(req.wLength, len);
1535963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, hd));
1536963169b4SHans Petter Selasky }
1537963169b4SHans Petter Selasky
1538963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
1539a593f6b8SAndrew Thompson * usbd_req_get_hub_status
154002ac6454SAndrew Thompson *
154102ac6454SAndrew Thompson * Returns:
154202ac6454SAndrew Thompson * 0: Success
154302ac6454SAndrew Thompson * Else: Failure
154402ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1545e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_hub_status(struct usb_device * udev,struct mtx * mtx,struct usb_hub_status * st)1546a593f6b8SAndrew Thompson usbd_req_get_hub_status(struct usb_device *udev, struct mtx *mtx,
1547760bc48eSAndrew Thompson struct usb_hub_status *st)
154802ac6454SAndrew Thompson {
1549760bc48eSAndrew Thompson struct usb_device_request req;
155002ac6454SAndrew Thompson
155102ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_DEVICE;
155202ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS;
155302ac6454SAndrew Thompson USETW(req.wValue, 0);
155402ac6454SAndrew Thompson USETW(req.wIndex, 0);
1555760bc48eSAndrew Thompson USETW(req.wLength, sizeof(struct usb_hub_status));
1556a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, st));
155702ac6454SAndrew Thompson }
155802ac6454SAndrew Thompson
155902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1560a593f6b8SAndrew Thompson * usbd_req_set_address
156102ac6454SAndrew Thompson *
156202ac6454SAndrew Thompson * This function is used to set the address for an USB device. After
156302ac6454SAndrew Thompson * port reset the USB device will respond at address zero.
156402ac6454SAndrew Thompson *
156502ac6454SAndrew Thompson * Returns:
156602ac6454SAndrew Thompson * 0: Success
156702ac6454SAndrew Thompson * Else: Failure
156802ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1569e0a69b51SAndrew Thompson usb_error_t
usbd_req_set_address(struct usb_device * udev,struct mtx * mtx,uint16_t addr)1570a593f6b8SAndrew Thompson usbd_req_set_address(struct usb_device *udev, struct mtx *mtx, uint16_t addr)
157102ac6454SAndrew Thompson {
1572760bc48eSAndrew Thompson struct usb_device_request req;
1573963169b4SHans Petter Selasky usb_error_t err;
157402ac6454SAndrew Thompson
157502ac6454SAndrew Thompson DPRINTFN(6, "setting device address=%d\n", addr);
157602ac6454SAndrew Thompson
157702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE;
157802ac6454SAndrew Thompson req.bRequest = UR_SET_ADDRESS;
157902ac6454SAndrew Thompson USETW(req.wValue, addr);
158002ac6454SAndrew Thompson USETW(req.wIndex, 0);
158102ac6454SAndrew Thompson USETW(req.wLength, 0);
158202ac6454SAndrew Thompson
1583963169b4SHans Petter Selasky err = USB_ERR_INVAL;
1584963169b4SHans Petter Selasky
1585963169b4SHans Petter Selasky /* check if USB controller handles set address */
1586963169b4SHans Petter Selasky if (udev->bus->methods->set_address != NULL)
1587963169b4SHans Petter Selasky err = (udev->bus->methods->set_address) (udev, mtx, addr);
1588963169b4SHans Petter Selasky
1589963169b4SHans Petter Selasky if (err != USB_ERR_INVAL)
1590963169b4SHans Petter Selasky goto done;
1591963169b4SHans Petter Selasky
159202ac6454SAndrew Thompson /* Setting the address should not take more than 1 second ! */
1593963169b4SHans Petter Selasky err = usbd_do_request_flags(udev, mtx, &req, NULL,
1594963169b4SHans Petter Selasky USB_DELAY_STATUS_STAGE, NULL, 1000);
1595963169b4SHans Petter Selasky
1596963169b4SHans Petter Selasky done:
1597963169b4SHans Petter Selasky /* allow device time to set new address */
1598963169b4SHans Petter Selasky usb_pause_mtx(mtx,
159937506412SHans Petter Selasky USB_MS_TO_TICKS(usb_set_address_settle));
1600963169b4SHans Petter Selasky
1601963169b4SHans Petter Selasky return (err);
160202ac6454SAndrew Thompson }
160302ac6454SAndrew Thompson
160402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1605a593f6b8SAndrew Thompson * usbd_req_get_port_status
160602ac6454SAndrew Thompson *
160702ac6454SAndrew Thompson * Returns:
160802ac6454SAndrew Thompson * 0: Success
160902ac6454SAndrew Thompson * Else: Failure
161002ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1611e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_port_status(struct usb_device * udev,struct mtx * mtx,struct usb_port_status * ps,uint8_t port)1612a593f6b8SAndrew Thompson usbd_req_get_port_status(struct usb_device *udev, struct mtx *mtx,
1613760bc48eSAndrew Thompson struct usb_port_status *ps, uint8_t port)
161402ac6454SAndrew Thompson {
1615760bc48eSAndrew Thompson struct usb_device_request req;
161602ac6454SAndrew Thompson
161702ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_OTHER;
161802ac6454SAndrew Thompson req.bRequest = UR_GET_STATUS;
161902ac6454SAndrew Thompson USETW(req.wValue, 0);
162002ac6454SAndrew Thompson req.wIndex[0] = port;
162102ac6454SAndrew Thompson req.wIndex[1] = 0;
16224604b6a1SHans Petter Selasky USETW(req.wLength, sizeof(*ps));
16234604b6a1SHans Petter Selasky
16244604b6a1SHans Petter Selasky return (usbd_do_request_flags(udev, mtx, &req, ps, 0, NULL, 1000));
162502ac6454SAndrew Thompson }
162602ac6454SAndrew Thompson
162702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1628a593f6b8SAndrew Thompson * usbd_req_clear_hub_feature
162902ac6454SAndrew Thompson *
163002ac6454SAndrew Thompson * Returns:
163102ac6454SAndrew Thompson * 0: Success
163202ac6454SAndrew Thompson * Else: Failure
163302ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1634e0a69b51SAndrew Thompson usb_error_t
usbd_req_clear_hub_feature(struct usb_device * udev,struct mtx * mtx,uint16_t sel)1635a593f6b8SAndrew Thompson usbd_req_clear_hub_feature(struct usb_device *udev, struct mtx *mtx,
163602ac6454SAndrew Thompson uint16_t sel)
163702ac6454SAndrew Thompson {
1638760bc48eSAndrew Thompson struct usb_device_request req;
163902ac6454SAndrew Thompson
164002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE;
164102ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE;
164202ac6454SAndrew Thompson USETW(req.wValue, sel);
164302ac6454SAndrew Thompson USETW(req.wIndex, 0);
164402ac6454SAndrew Thompson USETW(req.wLength, 0);
1645a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0));
164602ac6454SAndrew Thompson }
164702ac6454SAndrew Thompson
164802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1649a593f6b8SAndrew Thompson * usbd_req_set_hub_feature
165002ac6454SAndrew Thompson *
165102ac6454SAndrew Thompson * Returns:
165202ac6454SAndrew Thompson * 0: Success
165302ac6454SAndrew Thompson * Else: Failure
165402ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1655e0a69b51SAndrew Thompson usb_error_t
usbd_req_set_hub_feature(struct usb_device * udev,struct mtx * mtx,uint16_t sel)1656a593f6b8SAndrew Thompson usbd_req_set_hub_feature(struct usb_device *udev, struct mtx *mtx,
165702ac6454SAndrew Thompson uint16_t sel)
165802ac6454SAndrew Thompson {
1659760bc48eSAndrew Thompson struct usb_device_request req;
166002ac6454SAndrew Thompson
166102ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_DEVICE;
166202ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE;
166302ac6454SAndrew Thompson USETW(req.wValue, sel);
166402ac6454SAndrew Thompson USETW(req.wIndex, 0);
166502ac6454SAndrew Thompson USETW(req.wLength, 0);
1666a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0));
166702ac6454SAndrew Thompson }
166802ac6454SAndrew Thompson
166902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1670963169b4SHans Petter Selasky * usbd_req_set_hub_u1_timeout
1671963169b4SHans Petter Selasky *
1672963169b4SHans Petter Selasky * Returns:
1673963169b4SHans Petter Selasky * 0: Success
1674963169b4SHans Petter Selasky * Else: Failure
1675963169b4SHans Petter Selasky *------------------------------------------------------------------------*/
1676963169b4SHans Petter Selasky usb_error_t
usbd_req_set_hub_u1_timeout(struct usb_device * udev,struct mtx * mtx,uint8_t port,uint8_t timeout)1677963169b4SHans Petter Selasky usbd_req_set_hub_u1_timeout(struct usb_device *udev, struct mtx *mtx,
1678963169b4SHans Petter Selasky uint8_t port, uint8_t timeout)
1679963169b4SHans Petter Selasky {
1680963169b4SHans Petter Selasky struct usb_device_request req;
1681963169b4SHans Petter Selasky
1682963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER;
1683963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE;
1684963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U1_TIMEOUT);
1685963169b4SHans Petter Selasky req.wIndex[0] = port;
1686963169b4SHans Petter Selasky req.wIndex[1] = timeout;
1687963169b4SHans Petter Selasky USETW(req.wLength, 0);
1688963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0));
1689963169b4SHans Petter Selasky }
1690963169b4SHans Petter Selasky
1691963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
1692963169b4SHans Petter Selasky * usbd_req_set_hub_u2_timeout
1693963169b4SHans Petter Selasky *
1694963169b4SHans Petter Selasky * Returns:
1695963169b4SHans Petter Selasky * 0: Success
1696963169b4SHans Petter Selasky * Else: Failure
1697963169b4SHans Petter Selasky *------------------------------------------------------------------------*/
1698963169b4SHans Petter Selasky usb_error_t
usbd_req_set_hub_u2_timeout(struct usb_device * udev,struct mtx * mtx,uint8_t port,uint8_t timeout)1699963169b4SHans Petter Selasky usbd_req_set_hub_u2_timeout(struct usb_device *udev, struct mtx *mtx,
1700963169b4SHans Petter Selasky uint8_t port, uint8_t timeout)
1701963169b4SHans Petter Selasky {
1702963169b4SHans Petter Selasky struct usb_device_request req;
1703963169b4SHans Petter Selasky
1704963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER;
1705963169b4SHans Petter Selasky req.bRequest = UR_SET_FEATURE;
1706963169b4SHans Petter Selasky USETW(req.wValue, UHF_PORT_U2_TIMEOUT);
1707963169b4SHans Petter Selasky req.wIndex[0] = port;
1708963169b4SHans Petter Selasky req.wIndex[1] = timeout;
1709963169b4SHans Petter Selasky USETW(req.wLength, 0);
1710963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0));
1711963169b4SHans Petter Selasky }
1712963169b4SHans Petter Selasky
1713963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
1714963169b4SHans Petter Selasky * usbd_req_set_hub_depth
1715963169b4SHans Petter Selasky *
1716963169b4SHans Petter Selasky * Returns:
1717963169b4SHans Petter Selasky * 0: Success
1718963169b4SHans Petter Selasky * Else: Failure
1719963169b4SHans Petter Selasky *------------------------------------------------------------------------*/
1720963169b4SHans Petter Selasky usb_error_t
usbd_req_set_hub_depth(struct usb_device * udev,struct mtx * mtx,uint16_t depth)1721963169b4SHans Petter Selasky usbd_req_set_hub_depth(struct usb_device *udev, struct mtx *mtx,
1722963169b4SHans Petter Selasky uint16_t depth)
1723963169b4SHans Petter Selasky {
1724963169b4SHans Petter Selasky struct usb_device_request req;
1725963169b4SHans Petter Selasky
1726963169b4SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_DEVICE;
1727963169b4SHans Petter Selasky req.bRequest = UR_SET_HUB_DEPTH;
1728963169b4SHans Petter Selasky USETW(req.wValue, depth);
1729963169b4SHans Petter Selasky USETW(req.wIndex, 0);
1730963169b4SHans Petter Selasky USETW(req.wLength, 0);
1731963169b4SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0));
1732963169b4SHans Petter Selasky }
1733963169b4SHans Petter Selasky
1734963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
1735a593f6b8SAndrew Thompson * usbd_req_clear_port_feature
173602ac6454SAndrew Thompson *
173702ac6454SAndrew Thompson * Returns:
173802ac6454SAndrew Thompson * 0: Success
173902ac6454SAndrew Thompson * Else: Failure
174002ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1741e0a69b51SAndrew Thompson usb_error_t
usbd_req_clear_port_feature(struct usb_device * udev,struct mtx * mtx,uint8_t port,uint16_t sel)1742a593f6b8SAndrew Thompson usbd_req_clear_port_feature(struct usb_device *udev, struct mtx *mtx,
174302ac6454SAndrew Thompson uint8_t port, uint16_t sel)
174402ac6454SAndrew Thompson {
1745760bc48eSAndrew Thompson struct usb_device_request req;
174602ac6454SAndrew Thompson
174702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER;
174802ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE;
174902ac6454SAndrew Thompson USETW(req.wValue, sel);
175002ac6454SAndrew Thompson req.wIndex[0] = port;
175102ac6454SAndrew Thompson req.wIndex[1] = 0;
175202ac6454SAndrew Thompson USETW(req.wLength, 0);
1753a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0));
175402ac6454SAndrew Thompson }
175502ac6454SAndrew Thompson
175602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1757a593f6b8SAndrew Thompson * usbd_req_set_port_feature
175802ac6454SAndrew Thompson *
175902ac6454SAndrew Thompson * Returns:
176002ac6454SAndrew Thompson * 0: Success
176102ac6454SAndrew Thompson * Else: Failure
176202ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1763e0a69b51SAndrew Thompson usb_error_t
usbd_req_set_port_feature(struct usb_device * udev,struct mtx * mtx,uint8_t port,uint16_t sel)1764a593f6b8SAndrew Thompson usbd_req_set_port_feature(struct usb_device *udev, struct mtx *mtx,
176502ac6454SAndrew Thompson uint8_t port, uint16_t sel)
176602ac6454SAndrew Thompson {
1767760bc48eSAndrew Thompson struct usb_device_request req;
176802ac6454SAndrew Thompson
176902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_OTHER;
177002ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE;
177102ac6454SAndrew Thompson USETW(req.wValue, sel);
177202ac6454SAndrew Thompson req.wIndex[0] = port;
177302ac6454SAndrew Thompson req.wIndex[1] = 0;
177402ac6454SAndrew Thompson USETW(req.wLength, 0);
1775a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0));
177602ac6454SAndrew Thompson }
177702ac6454SAndrew Thompson
177802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1779a593f6b8SAndrew Thompson * usbd_req_set_protocol
178002ac6454SAndrew Thompson *
178102ac6454SAndrew Thompson * Returns:
178202ac6454SAndrew Thompson * 0: Success
178302ac6454SAndrew Thompson * Else: Failure
178402ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1785e0a69b51SAndrew Thompson usb_error_t
usbd_req_set_protocol(struct usb_device * udev,struct mtx * mtx,uint8_t iface_index,uint16_t report)1786a593f6b8SAndrew Thompson usbd_req_set_protocol(struct usb_device *udev, struct mtx *mtx,
178702ac6454SAndrew Thompson uint8_t iface_index, uint16_t report)
178802ac6454SAndrew Thompson {
1789a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1790760bc48eSAndrew Thompson struct usb_device_request req;
179102ac6454SAndrew Thompson
179202ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) {
179302ac6454SAndrew Thompson return (USB_ERR_INVAL);
179402ac6454SAndrew Thompson }
179502ac6454SAndrew Thompson DPRINTFN(5, "iface=%p, report=%d, endpt=%d\n",
179602ac6454SAndrew Thompson iface, report, iface->idesc->bInterfaceNumber);
179702ac6454SAndrew Thompson
179802ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
179902ac6454SAndrew Thompson req.bRequest = UR_SET_PROTOCOL;
180002ac6454SAndrew Thompson USETW(req.wValue, report);
180102ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber;
180202ac6454SAndrew Thompson req.wIndex[1] = 0;
180302ac6454SAndrew Thompson USETW(req.wLength, 0);
1804a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0));
180502ac6454SAndrew Thompson }
180602ac6454SAndrew Thompson
180702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1808a593f6b8SAndrew Thompson * usbd_req_set_report
180902ac6454SAndrew Thompson *
181002ac6454SAndrew Thompson * Returns:
181102ac6454SAndrew Thompson * 0: Success
181202ac6454SAndrew Thompson * Else: Failure
181302ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1814e0a69b51SAndrew Thompson usb_error_t
usbd_req_set_report(struct usb_device * udev,struct mtx * mtx,void * data,uint16_t len,uint8_t iface_index,uint8_t type,uint8_t id)1815a593f6b8SAndrew Thompson usbd_req_set_report(struct usb_device *udev, struct mtx *mtx, void *data, uint16_t len,
181602ac6454SAndrew Thompson uint8_t iface_index, uint8_t type, uint8_t id)
181702ac6454SAndrew Thompson {
1818a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1819760bc48eSAndrew Thompson struct usb_device_request req;
182002ac6454SAndrew Thompson
182102ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) {
182202ac6454SAndrew Thompson return (USB_ERR_INVAL);
182302ac6454SAndrew Thompson }
182402ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len);
182502ac6454SAndrew Thompson
182602ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
182702ac6454SAndrew Thompson req.bRequest = UR_SET_REPORT;
182802ac6454SAndrew Thompson USETW2(req.wValue, type, id);
182902ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber;
183002ac6454SAndrew Thompson req.wIndex[1] = 0;
183102ac6454SAndrew Thompson USETW(req.wLength, len);
1832a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data));
183302ac6454SAndrew Thompson }
183402ac6454SAndrew Thompson
183502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1836a593f6b8SAndrew Thompson * usbd_req_get_report
183702ac6454SAndrew Thompson *
183802ac6454SAndrew Thompson * Returns:
183902ac6454SAndrew Thompson * 0: Success
184002ac6454SAndrew Thompson * Else: Failure
184102ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1842e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_report(struct usb_device * udev,struct mtx * mtx,void * data,uint16_t len,uint8_t iface_index,uint8_t type,uint8_t id)1843a593f6b8SAndrew Thompson usbd_req_get_report(struct usb_device *udev, struct mtx *mtx, void *data,
184402ac6454SAndrew Thompson uint16_t len, uint8_t iface_index, uint8_t type, uint8_t id)
184502ac6454SAndrew Thompson {
1846a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1847760bc48eSAndrew Thompson struct usb_device_request req;
184802ac6454SAndrew Thompson
1849477a63a8SAlexander Motin if ((iface == NULL) || (iface->idesc == NULL)) {
185002ac6454SAndrew Thompson return (USB_ERR_INVAL);
185102ac6454SAndrew Thompson }
185202ac6454SAndrew Thompson DPRINTFN(5, "len=%d\n", len);
185302ac6454SAndrew Thompson
185402ac6454SAndrew Thompson req.bmRequestType = UT_READ_CLASS_INTERFACE;
185502ac6454SAndrew Thompson req.bRequest = UR_GET_REPORT;
185602ac6454SAndrew Thompson USETW2(req.wValue, type, id);
185702ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber;
185802ac6454SAndrew Thompson req.wIndex[1] = 0;
185902ac6454SAndrew Thompson USETW(req.wLength, len);
1860a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, data));
186102ac6454SAndrew Thompson }
186202ac6454SAndrew Thompson
186302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1864a593f6b8SAndrew Thompson * usbd_req_set_idle
186502ac6454SAndrew Thompson *
186602ac6454SAndrew Thompson * Returns:
186702ac6454SAndrew Thompson * 0: Success
186802ac6454SAndrew Thompson * Else: Failure
186902ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1870e0a69b51SAndrew Thompson usb_error_t
usbd_req_set_idle(struct usb_device * udev,struct mtx * mtx,uint8_t iface_index,uint8_t duration,uint8_t id)1871a593f6b8SAndrew Thompson usbd_req_set_idle(struct usb_device *udev, struct mtx *mtx,
187202ac6454SAndrew Thompson uint8_t iface_index, uint8_t duration, uint8_t id)
187302ac6454SAndrew Thompson {
1874a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1875760bc48eSAndrew Thompson struct usb_device_request req;
187602ac6454SAndrew Thompson
187702ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) {
187802ac6454SAndrew Thompson return (USB_ERR_INVAL);
187902ac6454SAndrew Thompson }
188002ac6454SAndrew Thompson DPRINTFN(5, "%d %d\n", duration, id);
188102ac6454SAndrew Thompson
188202ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
188302ac6454SAndrew Thompson req.bRequest = UR_SET_IDLE;
188402ac6454SAndrew Thompson USETW2(req.wValue, duration, id);
188502ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber;
188602ac6454SAndrew Thompson req.wIndex[1] = 0;
188702ac6454SAndrew Thompson USETW(req.wLength, 0);
1888a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0));
188902ac6454SAndrew Thompson }
189002ac6454SAndrew Thompson
189102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1892a593f6b8SAndrew Thompson * usbd_req_get_report_descriptor
189302ac6454SAndrew Thompson *
189402ac6454SAndrew Thompson * Returns:
189502ac6454SAndrew Thompson * 0: Success
189602ac6454SAndrew Thompson * Else: Failure
189702ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1898e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_report_descriptor(struct usb_device * udev,struct mtx * mtx,void * d,uint16_t size,uint8_t iface_index)1899a593f6b8SAndrew Thompson usbd_req_get_report_descriptor(struct usb_device *udev, struct mtx *mtx,
190002ac6454SAndrew Thompson void *d, uint16_t size, uint8_t iface_index)
190102ac6454SAndrew Thompson {
1902a593f6b8SAndrew Thompson struct usb_interface *iface = usbd_get_iface(udev, iface_index);
1903760bc48eSAndrew Thompson struct usb_device_request req;
190402ac6454SAndrew Thompson
190502ac6454SAndrew Thompson if ((iface == NULL) || (iface->idesc == NULL)) {
190602ac6454SAndrew Thompson return (USB_ERR_INVAL);
190702ac6454SAndrew Thompson }
190802ac6454SAndrew Thompson req.bmRequestType = UT_READ_INTERFACE;
190902ac6454SAndrew Thompson req.bRequest = UR_GET_DESCRIPTOR;
191002ac6454SAndrew Thompson USETW2(req.wValue, UDESC_REPORT, 0); /* report id should be 0 */
191102ac6454SAndrew Thompson req.wIndex[0] = iface->idesc->bInterfaceNumber;
191202ac6454SAndrew Thompson req.wIndex[1] = 0;
191302ac6454SAndrew Thompson USETW(req.wLength, size);
1914a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, d));
191502ac6454SAndrew Thompson }
191602ac6454SAndrew Thompson
191702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1918a593f6b8SAndrew Thompson * usbd_req_set_config
191902ac6454SAndrew Thompson *
192002ac6454SAndrew Thompson * This function is used to select the current configuration number in
192102ac6454SAndrew Thompson * both USB device side mode and USB host side mode. When setting the
192202ac6454SAndrew Thompson * configuration the function of the interfaces can change.
192302ac6454SAndrew Thompson *
192402ac6454SAndrew Thompson * Returns:
192502ac6454SAndrew Thompson * 0: Success
192602ac6454SAndrew Thompson * Else: Failure
192702ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1928e0a69b51SAndrew Thompson usb_error_t
usbd_req_set_config(struct usb_device * udev,struct mtx * mtx,uint8_t conf)1929a593f6b8SAndrew Thompson usbd_req_set_config(struct usb_device *udev, struct mtx *mtx, uint8_t conf)
193002ac6454SAndrew Thompson {
1931760bc48eSAndrew Thompson struct usb_device_request req;
193202ac6454SAndrew Thompson
193302ac6454SAndrew Thompson DPRINTF("setting config %d\n", conf);
193402ac6454SAndrew Thompson
193502ac6454SAndrew Thompson /* do "set configuration" request */
193602ac6454SAndrew Thompson
193702ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE;
193802ac6454SAndrew Thompson req.bRequest = UR_SET_CONFIG;
193902ac6454SAndrew Thompson req.wValue[0] = conf;
194002ac6454SAndrew Thompson req.wValue[1] = 0;
194102ac6454SAndrew Thompson USETW(req.wIndex, 0);
194202ac6454SAndrew Thompson USETW(req.wLength, 0);
1943a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0));
194402ac6454SAndrew Thompson }
194502ac6454SAndrew Thompson
194602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1947a593f6b8SAndrew Thompson * usbd_req_get_config
194802ac6454SAndrew Thompson *
194902ac6454SAndrew Thompson * Returns:
195002ac6454SAndrew Thompson * 0: Success
195102ac6454SAndrew Thompson * Else: Failure
195202ac6454SAndrew Thompson *------------------------------------------------------------------------*/
1953e0a69b51SAndrew Thompson usb_error_t
usbd_req_get_config(struct usb_device * udev,struct mtx * mtx,uint8_t * pconf)1954a593f6b8SAndrew Thompson usbd_req_get_config(struct usb_device *udev, struct mtx *mtx, uint8_t *pconf)
195502ac6454SAndrew Thompson {
1956760bc48eSAndrew Thompson struct usb_device_request req;
195702ac6454SAndrew Thompson
195802ac6454SAndrew Thompson req.bmRequestType = UT_READ_DEVICE;
195902ac6454SAndrew Thompson req.bRequest = UR_GET_CONFIG;
196002ac6454SAndrew Thompson USETW(req.wValue, 0);
196102ac6454SAndrew Thompson USETW(req.wIndex, 0);
196202ac6454SAndrew Thompson USETW(req.wLength, 1);
1963a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, pconf));
196402ac6454SAndrew Thompson }
196502ac6454SAndrew Thompson
196602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
1967963169b4SHans Petter Selasky * usbd_setup_device_desc
1968963169b4SHans Petter Selasky *------------------------------------------------------------------------*/
1969963169b4SHans Petter Selasky usb_error_t
usbd_setup_device_desc(struct usb_device * udev,struct mtx * mtx)1970963169b4SHans Petter Selasky usbd_setup_device_desc(struct usb_device *udev, struct mtx *mtx)
1971963169b4SHans Petter Selasky {
1972963169b4SHans Petter Selasky usb_error_t err;
1973963169b4SHans Petter Selasky
1974963169b4SHans Petter Selasky /*
1975963169b4SHans Petter Selasky * Get the first 8 bytes of the device descriptor !
1976963169b4SHans Petter Selasky *
1977963169b4SHans Petter Selasky * NOTE: "usbd_do_request()" will check the device descriptor
1978963169b4SHans Petter Selasky * next time we do a request to see if the maximum packet size
1979963169b4SHans Petter Selasky * changed! The 8 first bytes of the device descriptor
1980963169b4SHans Petter Selasky * contains the maximum packet size to use on control endpoint
1981963169b4SHans Petter Selasky * 0. If this value is different from "USB_MAX_IPACKET" a new
1982963169b4SHans Petter Selasky * USB control request will be setup!
1983963169b4SHans Petter Selasky */
1984963169b4SHans Petter Selasky switch (udev->speed) {
1985963169b4SHans Petter Selasky case USB_SPEED_FULL:
198623de050bSHans Petter Selasky if (usb_full_ddesc != 0) {
198723de050bSHans Petter Selasky /* get full device descriptor */
198823de050bSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
198923de050bSHans Petter Selasky if (err == 0)
199023de050bSHans Petter Selasky break;
199123de050bSHans Petter Selasky }
199223de050bSHans Petter Selasky
199323de050bSHans Petter Selasky /* get partial device descriptor, some devices crash on this */
1994963169b4SHans Petter Selasky err = usbd_req_get_desc(udev, mtx, NULL, &udev->ddesc,
1995963169b4SHans Petter Selasky USB_MAX_IPACKET, USB_MAX_IPACKET, 0, UDESC_DEVICE, 0, 0);
1996c3a38d6cSHans Petter Selasky if (err != 0) {
1997c3a38d6cSHans Petter Selasky DPRINTF("Trying fallback for getting the USB device descriptor\n");
1998c3a38d6cSHans Petter Selasky /* try 8 bytes bMaxPacketSize */
1999c3a38d6cSHans Petter Selasky udev->ddesc.bMaxPacketSize = 8;
2000c3a38d6cSHans Petter Selasky /* get full device descriptor */
2001c3a38d6cSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
2002c3a38d6cSHans Petter Selasky if (err == 0)
2003963169b4SHans Petter Selasky break;
2004c3a38d6cSHans Petter Selasky /* try 16 bytes bMaxPacketSize */
2005c3a38d6cSHans Petter Selasky udev->ddesc.bMaxPacketSize = 16;
2006c3a38d6cSHans Petter Selasky /* get full device descriptor */
2007c3a38d6cSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
2008c3a38d6cSHans Petter Selasky if (err == 0)
2009c3a38d6cSHans Petter Selasky break;
2010c3a38d6cSHans Petter Selasky /* try 32/64 bytes bMaxPacketSize */
2011c3a38d6cSHans Petter Selasky udev->ddesc.bMaxPacketSize = 32;
2012c3a38d6cSHans Petter Selasky }
201323de050bSHans Petter Selasky /* get the full device descriptor */
201423de050bSHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
201523de050bSHans Petter Selasky break;
201623de050bSHans Petter Selasky
2017963169b4SHans Petter Selasky default:
2018a1a10f53SHans Petter Selasky DPRINTF("Minimum bMaxPacketSize is large enough "
201923de050bSHans Petter Selasky "to hold the complete device descriptor or "
2020a1a10f53SHans Petter Selasky "only one bMaxPacketSize choice\n");
2021963169b4SHans Petter Selasky
2022963169b4SHans Petter Selasky /* get the full device descriptor */
2023963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
2024963169b4SHans Petter Selasky
2025963169b4SHans Petter Selasky /* try one more time, if error */
202623de050bSHans Petter Selasky if (err != 0)
2027963169b4SHans Petter Selasky err = usbd_req_get_device_desc(udev, mtx, &udev->ddesc);
202823de050bSHans Petter Selasky break;
202923de050bSHans Petter Selasky }
2030963169b4SHans Petter Selasky
203123de050bSHans Petter Selasky if (err != 0) {
203223de050bSHans Petter Selasky DPRINTFN(0, "getting device descriptor "
203323de050bSHans Petter Selasky "at addr %d failed, %s\n", udev->address,
203423de050bSHans Petter Selasky usbd_errstr(err));
2035963169b4SHans Petter Selasky return (err);
2036963169b4SHans Petter Selasky }
2037963169b4SHans Petter Selasky
2038963169b4SHans Petter Selasky DPRINTF("adding unit addr=%d, rev=%02x, class=%d, "
2039963169b4SHans Petter Selasky "subclass=%d, protocol=%d, maxpacket=%d, len=%d, speed=%d\n",
2040963169b4SHans Petter Selasky udev->address, UGETW(udev->ddesc.bcdUSB),
2041963169b4SHans Petter Selasky udev->ddesc.bDeviceClass,
2042963169b4SHans Petter Selasky udev->ddesc.bDeviceSubClass,
2043963169b4SHans Petter Selasky udev->ddesc.bDeviceProtocol,
2044963169b4SHans Petter Selasky udev->ddesc.bMaxPacketSize,
2045963169b4SHans Petter Selasky udev->ddesc.bLength,
2046963169b4SHans Petter Selasky udev->speed);
2047963169b4SHans Petter Selasky
2048963169b4SHans Petter Selasky return (err);
2049963169b4SHans Petter Selasky }
2050963169b4SHans Petter Selasky
2051963169b4SHans Petter Selasky /*------------------------------------------------------------------------*
2052a593f6b8SAndrew Thompson * usbd_req_re_enumerate
205302ac6454SAndrew Thompson *
205402ac6454SAndrew Thompson * NOTE: After this function returns the hardware is in the
205502ac6454SAndrew Thompson * unconfigured state! The application is responsible for setting a
205602ac6454SAndrew Thompson * new configuration.
205702ac6454SAndrew Thompson *
205802ac6454SAndrew Thompson * Returns:
205902ac6454SAndrew Thompson * 0: Success
206002ac6454SAndrew Thompson * Else: Failure
206102ac6454SAndrew Thompson *------------------------------------------------------------------------*/
2062e0a69b51SAndrew Thompson usb_error_t
usbd_req_re_enumerate(struct usb_device * udev,struct mtx * mtx)2063a593f6b8SAndrew Thompson usbd_req_re_enumerate(struct usb_device *udev, struct mtx *mtx)
206402ac6454SAndrew Thompson {
2065760bc48eSAndrew Thompson struct usb_device *parent_hub;
2066e0a69b51SAndrew Thompson usb_error_t err;
206702ac6454SAndrew Thompson uint8_t old_addr;
206802ac6454SAndrew Thompson uint8_t do_retry = 1;
206902ac6454SAndrew Thompson
2070f29a0724SAndrew Thompson if (udev->flags.usb_mode != USB_MODE_HOST) {
207102ac6454SAndrew Thompson return (USB_ERR_INVAL);
207202ac6454SAndrew Thompson }
207302ac6454SAndrew Thompson old_addr = udev->address;
207402ac6454SAndrew Thompson parent_hub = udev->parent_hub;
207502ac6454SAndrew Thompson if (parent_hub == NULL) {
207602ac6454SAndrew Thompson return (USB_ERR_INVAL);
207702ac6454SAndrew Thompson }
207802ac6454SAndrew Thompson retry:
2079a0d53e0bSHans Petter Selasky #if USB_HAVE_TT_SUPPORT
20809eb0d702SHans Petter Selasky /*
20819eb0d702SHans Petter Selasky * Try to reset the High Speed parent HUB of a LOW- or FULL-
20829eb0d702SHans Petter Selasky * speed device, if any.
20839eb0d702SHans Petter Selasky */
20849eb0d702SHans Petter Selasky if (udev->parent_hs_hub != NULL &&
20859eb0d702SHans Petter Selasky udev->speed != USB_SPEED_HIGH) {
20869eb0d702SHans Petter Selasky DPRINTF("Trying to reset parent High Speed TT.\n");
2087a0d53e0bSHans Petter Selasky if (udev->parent_hs_hub == parent_hub &&
2088a0d53e0bSHans Petter Selasky (uhub_count_active_host_ports(parent_hub, USB_SPEED_LOW) +
2089a0d53e0bSHans Petter Selasky uhub_count_active_host_ports(parent_hub, USB_SPEED_FULL)) == 1) {
2090a0d53e0bSHans Petter Selasky /* we can reset the whole TT */
2091a0d53e0bSHans Petter Selasky err = usbd_req_reset_tt(parent_hub, NULL,
20929eb0d702SHans Petter Selasky udev->hs_port_no);
2093a0d53e0bSHans Petter Selasky } else {
2094a0d53e0bSHans Petter Selasky /* only reset a particular device and endpoint */
2095a0d53e0bSHans Petter Selasky err = usbd_req_clear_tt_buffer(udev->parent_hs_hub, NULL,
2096a0d53e0bSHans Petter Selasky udev->hs_port_no, old_addr, UE_CONTROL, 0);
2097a0d53e0bSHans Petter Selasky }
20989eb0d702SHans Petter Selasky if (err) {
20999eb0d702SHans Petter Selasky DPRINTF("Resetting parent High "
21009eb0d702SHans Petter Selasky "Speed TT failed (%s).\n",
21019eb0d702SHans Petter Selasky usbd_errstr(err));
21029eb0d702SHans Petter Selasky }
21039eb0d702SHans Petter Selasky }
2104a0d53e0bSHans Petter Selasky #endif
210593ee6e85SHans Petter Selasky /* Try to warm reset first */
210693ee6e85SHans Petter Selasky if (parent_hub->speed == USB_SPEED_SUPER)
210793ee6e85SHans Petter Selasky usbd_req_warm_reset_port(parent_hub, mtx, udev->port_no);
210893ee6e85SHans Petter Selasky
21099eb0d702SHans Petter Selasky /* Try to reset the parent HUB port. */
2110a593f6b8SAndrew Thompson err = usbd_req_reset_port(parent_hub, mtx, udev->port_no);
211102ac6454SAndrew Thompson if (err) {
211203797f33SAndrew Thompson DPRINTFN(0, "addr=%d, port reset failed, %s\n",
2113a593f6b8SAndrew Thompson old_addr, usbd_errstr(err));
211402ac6454SAndrew Thompson goto done;
211502ac6454SAndrew Thompson }
2116963169b4SHans Petter Selasky
211702ac6454SAndrew Thompson /*
211802ac6454SAndrew Thompson * After that the port has been reset our device should be at
211902ac6454SAndrew Thompson * address zero:
212002ac6454SAndrew Thompson */
212102ac6454SAndrew Thompson udev->address = USB_START_ADDR;
212202ac6454SAndrew Thompson
212302ac6454SAndrew Thompson /* reset "bMaxPacketSize" */
212402ac6454SAndrew Thompson udev->ddesc.bMaxPacketSize = USB_MAX_IPACKET;
212502ac6454SAndrew Thompson
2126963169b4SHans Petter Selasky /* reset USB state */
2127963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_POWERED);
2128963169b4SHans Petter Selasky
212902ac6454SAndrew Thompson /*
213002ac6454SAndrew Thompson * Restore device address:
213102ac6454SAndrew Thompson */
2132a593f6b8SAndrew Thompson err = usbd_req_set_address(udev, mtx, old_addr);
213302ac6454SAndrew Thompson if (err) {
213402ac6454SAndrew Thompson /* XXX ignore any errors! */
213503797f33SAndrew Thompson DPRINTFN(0, "addr=%d, set address failed! (%s, ignored)\n",
2136a593f6b8SAndrew Thompson old_addr, usbd_errstr(err));
213702ac6454SAndrew Thompson }
2138963169b4SHans Petter Selasky /*
2139963169b4SHans Petter Selasky * Restore device address, if the controller driver did not
2140963169b4SHans Petter Selasky * set a new one:
2141963169b4SHans Petter Selasky */
2142963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR)
214302ac6454SAndrew Thompson udev->address = old_addr;
214402ac6454SAndrew Thompson
2145963169b4SHans Petter Selasky /* setup the device descriptor and the initial "wMaxPacketSize" */
2146963169b4SHans Petter Selasky err = usbd_setup_device_desc(udev, mtx);
214702ac6454SAndrew Thompson
214802ac6454SAndrew Thompson done:
214902ac6454SAndrew Thompson if (err && do_retry) {
215002ac6454SAndrew Thompson /* give the USB firmware some time to load */
2151a593f6b8SAndrew Thompson usb_pause_mtx(mtx, hz / 2);
215202ac6454SAndrew Thompson /* no more retries after this retry */
215302ac6454SAndrew Thompson do_retry = 0;
215402ac6454SAndrew Thompson /* try again */
215502ac6454SAndrew Thompson goto retry;
215602ac6454SAndrew Thompson }
215702ac6454SAndrew Thompson /* restore address */
2158963169b4SHans Petter Selasky if (udev->address == USB_START_ADDR)
215902ac6454SAndrew Thompson udev->address = old_addr;
2160963169b4SHans Petter Selasky /* update state, if successful */
2161963169b4SHans Petter Selasky if (err == 0)
2162963169b4SHans Petter Selasky usb_set_device_state(udev, USB_STATE_ADDRESSED);
216302ac6454SAndrew Thompson return (err);
216402ac6454SAndrew Thompson }
216502ac6454SAndrew Thompson
216602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2167a593f6b8SAndrew Thompson * usbd_req_clear_device_feature
216802ac6454SAndrew Thompson *
216902ac6454SAndrew Thompson * Returns:
217002ac6454SAndrew Thompson * 0: Success
217102ac6454SAndrew Thompson * Else: Failure
217202ac6454SAndrew Thompson *------------------------------------------------------------------------*/
2173e0a69b51SAndrew Thompson usb_error_t
usbd_req_clear_device_feature(struct usb_device * udev,struct mtx * mtx,uint16_t sel)2174a593f6b8SAndrew Thompson usbd_req_clear_device_feature(struct usb_device *udev, struct mtx *mtx,
217502ac6454SAndrew Thompson uint16_t sel)
217602ac6454SAndrew Thompson {
2177760bc48eSAndrew Thompson struct usb_device_request req;
217802ac6454SAndrew Thompson
217902ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE;
218002ac6454SAndrew Thompson req.bRequest = UR_CLEAR_FEATURE;
218102ac6454SAndrew Thompson USETW(req.wValue, sel);
218202ac6454SAndrew Thompson USETW(req.wIndex, 0);
218302ac6454SAndrew Thompson USETW(req.wLength, 0);
2184a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0));
218502ac6454SAndrew Thompson }
218602ac6454SAndrew Thompson
218702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
2188a593f6b8SAndrew Thompson * usbd_req_set_device_feature
218902ac6454SAndrew Thompson *
219002ac6454SAndrew Thompson * Returns:
219102ac6454SAndrew Thompson * 0: Success
219202ac6454SAndrew Thompson * Else: Failure
219302ac6454SAndrew Thompson *------------------------------------------------------------------------*/
2194e0a69b51SAndrew Thompson usb_error_t
usbd_req_set_device_feature(struct usb_device * udev,struct mtx * mtx,uint16_t sel)2195a593f6b8SAndrew Thompson usbd_req_set_device_feature(struct usb_device *udev, struct mtx *mtx,
219602ac6454SAndrew Thompson uint16_t sel)
219702ac6454SAndrew Thompson {
2198760bc48eSAndrew Thompson struct usb_device_request req;
219902ac6454SAndrew Thompson
220002ac6454SAndrew Thompson req.bmRequestType = UT_WRITE_DEVICE;
220102ac6454SAndrew Thompson req.bRequest = UR_SET_FEATURE;
220202ac6454SAndrew Thompson USETW(req.wValue, sel);
220302ac6454SAndrew Thompson USETW(req.wIndex, 0);
220402ac6454SAndrew Thompson USETW(req.wLength, 0);
2205a593f6b8SAndrew Thompson return (usbd_do_request(udev, mtx, &req, 0));
220602ac6454SAndrew Thompson }
22079eb0d702SHans Petter Selasky
22089eb0d702SHans Petter Selasky /*------------------------------------------------------------------------*
22099eb0d702SHans Petter Selasky * usbd_req_reset_tt
22109eb0d702SHans Petter Selasky *
22119eb0d702SHans Petter Selasky * Returns:
22129eb0d702SHans Petter Selasky * 0: Success
22139eb0d702SHans Petter Selasky * Else: Failure
22149eb0d702SHans Petter Selasky *------------------------------------------------------------------------*/
22159eb0d702SHans Petter Selasky usb_error_t
usbd_req_reset_tt(struct usb_device * udev,struct mtx * mtx,uint8_t port)22169eb0d702SHans Petter Selasky usbd_req_reset_tt(struct usb_device *udev, struct mtx *mtx,
22179eb0d702SHans Petter Selasky uint8_t port)
22189eb0d702SHans Petter Selasky {
22199eb0d702SHans Petter Selasky struct usb_device_request req;
22209eb0d702SHans Petter Selasky
22219eb0d702SHans Petter Selasky /* For single TT HUBs the port should be 1 */
22229eb0d702SHans Petter Selasky
22239eb0d702SHans Petter Selasky if (udev->ddesc.bDeviceClass == UDCLASS_HUB &&
22249eb0d702SHans Petter Selasky udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT)
22259eb0d702SHans Petter Selasky port = 1;
22269eb0d702SHans Petter Selasky
22279eb0d702SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER;
22289eb0d702SHans Petter Selasky req.bRequest = UR_RESET_TT;
22299eb0d702SHans Petter Selasky USETW(req.wValue, 0);
22309eb0d702SHans Petter Selasky req.wIndex[0] = port;
22319eb0d702SHans Petter Selasky req.wIndex[1] = 0;
22329eb0d702SHans Petter Selasky USETW(req.wLength, 0);
22339eb0d702SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0));
22349eb0d702SHans Petter Selasky }
22359eb0d702SHans Petter Selasky
22369eb0d702SHans Petter Selasky /*------------------------------------------------------------------------*
22379eb0d702SHans Petter Selasky * usbd_req_clear_tt_buffer
22389eb0d702SHans Petter Selasky *
22399eb0d702SHans Petter Selasky * For single TT HUBs the port should be 1.
22409eb0d702SHans Petter Selasky *
22419eb0d702SHans Petter Selasky * Returns:
22429eb0d702SHans Petter Selasky * 0: Success
22439eb0d702SHans Petter Selasky * Else: Failure
22449eb0d702SHans Petter Selasky *------------------------------------------------------------------------*/
22459eb0d702SHans Petter Selasky usb_error_t
usbd_req_clear_tt_buffer(struct usb_device * udev,struct mtx * mtx,uint8_t port,uint8_t addr,uint8_t type,uint8_t endpoint)22469eb0d702SHans Petter Selasky usbd_req_clear_tt_buffer(struct usb_device *udev, struct mtx *mtx,
22479eb0d702SHans Petter Selasky uint8_t port, uint8_t addr, uint8_t type, uint8_t endpoint)
22489eb0d702SHans Petter Selasky {
22499eb0d702SHans Petter Selasky struct usb_device_request req;
22509eb0d702SHans Petter Selasky uint16_t wValue;
22519eb0d702SHans Petter Selasky
22529eb0d702SHans Petter Selasky /* For single TT HUBs the port should be 1 */
22539eb0d702SHans Petter Selasky
22549eb0d702SHans Petter Selasky if (udev->ddesc.bDeviceClass == UDCLASS_HUB &&
22559eb0d702SHans Petter Selasky udev->ddesc.bDeviceProtocol == UDPROTO_HSHUBSTT)
22569eb0d702SHans Petter Selasky port = 1;
22579eb0d702SHans Petter Selasky
22589eb0d702SHans Petter Selasky wValue = (endpoint & 0xF) | ((addr & 0x7F) << 4) |
22599eb0d702SHans Petter Selasky ((endpoint & 0x80) << 8) | ((type & 3) << 12);
22609eb0d702SHans Petter Selasky
22619eb0d702SHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER;
22629eb0d702SHans Petter Selasky req.bRequest = UR_CLEAR_TT_BUFFER;
22639eb0d702SHans Petter Selasky USETW(req.wValue, wValue);
22649eb0d702SHans Petter Selasky req.wIndex[0] = port;
22659eb0d702SHans Petter Selasky req.wIndex[1] = 0;
22669eb0d702SHans Petter Selasky USETW(req.wLength, 0);
22679eb0d702SHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0));
22689eb0d702SHans Petter Selasky }
22694131f6fbSHans Petter Selasky
22704131f6fbSHans Petter Selasky /*------------------------------------------------------------------------*
22714131f6fbSHans Petter Selasky * usbd_req_set_port_link_state
22724131f6fbSHans Petter Selasky *
22734131f6fbSHans Petter Selasky * USB 3.0 specific request
22744131f6fbSHans Petter Selasky *
22754131f6fbSHans Petter Selasky * Returns:
22764131f6fbSHans Petter Selasky * 0: Success
22774131f6fbSHans Petter Selasky * Else: Failure
22784131f6fbSHans Petter Selasky *------------------------------------------------------------------------*/
22794131f6fbSHans Petter Selasky usb_error_t
usbd_req_set_port_link_state(struct usb_device * udev,struct mtx * mtx,uint8_t port,uint8_t link_state)22804131f6fbSHans Petter Selasky usbd_req_set_port_link_state(struct usb_device *udev, struct mtx *mtx,
22814131f6fbSHans Petter Selasky uint8_t port, uint8_t link_state)
22824131f6fbSHans Petter Selasky {
22834131f6fbSHans Petter Selasky struct usb_device_request req;
22844131f6fbSHans Petter Selasky
22854131f6fbSHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER;
22864131f6fbSHans Petter Selasky req.bRequest = UR_SET_FEATURE;
22874131f6fbSHans Petter Selasky USETW(req.wValue, UHF_PORT_LINK_STATE);
22884131f6fbSHans Petter Selasky req.wIndex[0] = port;
22894131f6fbSHans Petter Selasky req.wIndex[1] = link_state;
22904131f6fbSHans Petter Selasky USETW(req.wLength, 0);
22914131f6fbSHans Petter Selasky return (usbd_do_request(udev, mtx, &req, 0));
22924131f6fbSHans Petter Selasky }
22934563ba7aSHans Petter Selasky
22944563ba7aSHans Petter Selasky /*------------------------------------------------------------------------*
22954563ba7aSHans Petter Selasky * usbd_req_set_lpm_info
22964563ba7aSHans Petter Selasky *
22974563ba7aSHans Petter Selasky * USB 2.0 specific request for Link Power Management.
22984563ba7aSHans Petter Selasky *
22994563ba7aSHans Petter Selasky * Returns:
23004563ba7aSHans Petter Selasky * 0: Success
23014563ba7aSHans Petter Selasky * USB_ERR_PENDING_REQUESTS: NYET
23024563ba7aSHans Petter Selasky * USB_ERR_TIMEOUT: TIMEOUT
23034563ba7aSHans Petter Selasky * USB_ERR_STALL: STALL
23044563ba7aSHans Petter Selasky * Else: Failure
23054563ba7aSHans Petter Selasky *------------------------------------------------------------------------*/
23064563ba7aSHans Petter Selasky usb_error_t
usbd_req_set_lpm_info(struct usb_device * udev,struct mtx * mtx,uint8_t port,uint8_t besl,uint8_t addr,uint8_t rwe)23074563ba7aSHans Petter Selasky usbd_req_set_lpm_info(struct usb_device *udev, struct mtx *mtx,
23084563ba7aSHans Petter Selasky uint8_t port, uint8_t besl, uint8_t addr, uint8_t rwe)
23094563ba7aSHans Petter Selasky {
23104563ba7aSHans Petter Selasky struct usb_device_request req;
23114563ba7aSHans Petter Selasky usb_error_t err;
23124563ba7aSHans Petter Selasky uint8_t buf[1];
23134563ba7aSHans Petter Selasky
23144563ba7aSHans Petter Selasky req.bmRequestType = UT_WRITE_CLASS_OTHER;
23154563ba7aSHans Petter Selasky req.bRequest = UR_SET_AND_TEST;
23164563ba7aSHans Petter Selasky USETW(req.wValue, UHF_PORT_L1);
23174563ba7aSHans Petter Selasky req.wIndex[0] = (port & 0xF) | ((besl & 0xF) << 4);
23184563ba7aSHans Petter Selasky req.wIndex[1] = (addr & 0x7F) | (rwe ? 0x80 : 0x00);
23194563ba7aSHans Petter Selasky USETW(req.wLength, sizeof(buf));
23204563ba7aSHans Petter Selasky
23214563ba7aSHans Petter Selasky /* set default value in case of short transfer */
23224563ba7aSHans Petter Selasky buf[0] = 0x00;
23234563ba7aSHans Petter Selasky
23244563ba7aSHans Petter Selasky err = usbd_do_request(udev, mtx, &req, buf);
23254563ba7aSHans Petter Selasky if (err)
23264563ba7aSHans Petter Selasky return (err);
23274563ba7aSHans Petter Selasky
23284563ba7aSHans Petter Selasky switch (buf[0]) {
23294563ba7aSHans Petter Selasky case 0x00: /* SUCCESS */
23304563ba7aSHans Petter Selasky break;
23314563ba7aSHans Petter Selasky case 0x10: /* NYET */
23324563ba7aSHans Petter Selasky err = USB_ERR_PENDING_REQUESTS;
23334563ba7aSHans Petter Selasky break;
23344563ba7aSHans Petter Selasky case 0x11: /* TIMEOUT */
23354563ba7aSHans Petter Selasky err = USB_ERR_TIMEOUT;
23364563ba7aSHans Petter Selasky break;
23374563ba7aSHans Petter Selasky case 0x30: /* STALL */
23384563ba7aSHans Petter Selasky err = USB_ERR_STALLED;
23394563ba7aSHans Petter Selasky break;
23404563ba7aSHans Petter Selasky default: /* reserved */
23414563ba7aSHans Petter Selasky err = USB_ERR_IOERROR;
23424563ba7aSHans Petter Selasky break;
23434563ba7aSHans Petter Selasky }
23444563ba7aSHans Petter Selasky return (err);
23454563ba7aSHans Petter Selasky }
2346