xref: /freebsd/sys/dev/usb/usb_handle_request.c (revision 71625ec9ad2a9bc8c09784fbd23b759830e0ee5f)
102ac6454SAndrew Thompson /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
402ac6454SAndrew Thompson  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
502ac6454SAndrew Thompson  *
602ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
702ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
802ac6454SAndrew Thompson  * are met:
902ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
1002ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1102ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1202ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1302ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1402ac6454SAndrew Thompson  *
1502ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1602ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1702ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1802ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1902ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2002ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2102ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2202ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2302ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2402ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2502ac6454SAndrew Thompson  * SUCH DAMAGE.
2602ac6454SAndrew Thompson  */
2702ac6454SAndrew Thompson 
28d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE
29d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE
30d2b99310SHans Petter Selasky #else
31ed6d949aSAndrew Thompson #include <sys/stdint.h>
32ed6d949aSAndrew Thompson #include <sys/stddef.h>
33ed6d949aSAndrew Thompson #include <sys/param.h>
34ed6d949aSAndrew Thompson #include <sys/queue.h>
35ed6d949aSAndrew Thompson #include <sys/types.h>
36ed6d949aSAndrew Thompson #include <sys/systm.h>
37ed6d949aSAndrew Thompson #include <sys/kernel.h>
38ed6d949aSAndrew Thompson #include <sys/bus.h>
39ed6d949aSAndrew Thompson #include <sys/module.h>
40ed6d949aSAndrew Thompson #include <sys/lock.h>
41ed6d949aSAndrew Thompson #include <sys/mutex.h>
42ed6d949aSAndrew Thompson #include <sys/condvar.h>
43ed6d949aSAndrew Thompson #include <sys/sysctl.h>
44ed6d949aSAndrew Thompson #include <sys/sx.h>
45ed6d949aSAndrew Thompson #include <sys/unistd.h>
46ed6d949aSAndrew Thompson #include <sys/callout.h>
47ed6d949aSAndrew Thompson #include <sys/malloc.h>
48ed6d949aSAndrew Thompson #include <sys/priv.h>
49ed6d949aSAndrew Thompson 
5002ac6454SAndrew Thompson #include <dev/usb/usb.h>
51ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
52bd73b187SAlfred Perlstein #include <dev/usb/usbdi_util.h>
53ed6d949aSAndrew Thompson #include "usb_if.h"
5402ac6454SAndrew Thompson 
55a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_debug
5602ac6454SAndrew Thompson 
5702ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
5802ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
5902ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
6002ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h>
6102ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
6202ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
6302ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
6402ac6454SAndrew Thompson #include <dev/usb/usb_hub.h>
6502ac6454SAndrew Thompson 
6602ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
6702ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
68d2b99310SHans Petter Selasky #endif			/* USB_GLOBAL_INCLUDE_FILE */
6902ac6454SAndrew Thompson 
7002ac6454SAndrew Thompson /* function prototypes */
7102ac6454SAndrew Thompson 
72a593f6b8SAndrew Thompson static uint8_t usb_handle_get_stall(struct usb_device *, uint8_t);
73a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_remote_wakeup(struct usb_xfer *, uint8_t);
74a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_request(struct usb_xfer *);
75a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_set_config(struct usb_xfer *, uint8_t);
76a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_set_stall(struct usb_xfer *, uint8_t,
7702ac6454SAndrew Thompson 			    uint8_t);
78a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_iface_request(struct usb_xfer *, void **,
79760bc48eSAndrew Thompson 			    uint16_t *, struct usb_device_request, uint16_t,
8002ac6454SAndrew Thompson 			    uint8_t);
8102ac6454SAndrew Thompson 
8202ac6454SAndrew Thompson /*------------------------------------------------------------------------*
83a593f6b8SAndrew Thompson  *	usb_handle_request_callback
8402ac6454SAndrew Thompson  *
8502ac6454SAndrew Thompson  * This function is the USB callback for generic USB Device control
8602ac6454SAndrew Thompson  * transfers.
8702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
8802ac6454SAndrew Thompson void
usb_handle_request_callback(struct usb_xfer * xfer,usb_error_t error)89ed6d949aSAndrew Thompson usb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error)
9002ac6454SAndrew Thompson {
91e0a69b51SAndrew Thompson 	usb_error_t err;
9202ac6454SAndrew Thompson 
9302ac6454SAndrew Thompson 	/* check the current transfer state */
9402ac6454SAndrew Thompson 
9502ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9602ac6454SAndrew Thompson 	case USB_ST_SETUP:
9702ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
9802ac6454SAndrew Thompson 
9902ac6454SAndrew Thompson 		/* handle the request */
100a593f6b8SAndrew Thompson 		err = usb_handle_request(xfer);
10102ac6454SAndrew Thompson 
10202ac6454SAndrew Thompson 		if (err) {
10302ac6454SAndrew Thompson 			if (err == USB_ERR_BAD_CONTEXT) {
10402ac6454SAndrew Thompson 				/* we need to re-setup the control transfer */
105a593f6b8SAndrew Thompson 				usb_needs_explore(xfer->xroot->bus, 0);
10602ac6454SAndrew Thompson 				break;
10702ac6454SAndrew Thompson 			}
10802ac6454SAndrew Thompson 			goto tr_restart;
10902ac6454SAndrew Thompson 		}
110a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
11102ac6454SAndrew Thompson 		break;
11202ac6454SAndrew Thompson 
11302ac6454SAndrew Thompson 	default:
114a755d548SAndrew Thompson 		/* check if a control transfer is active */
115a755d548SAndrew Thompson 		if (xfer->flags_int.control_rem != 0xFFFF) {
116a755d548SAndrew Thompson 			/* handle the request */
117a593f6b8SAndrew Thompson 			err = usb_handle_request(xfer);
118a755d548SAndrew Thompson 		}
11902ac6454SAndrew Thompson 		if (xfer->error != USB_ERR_CANCELLED) {
12002ac6454SAndrew Thompson 			/* should not happen - try stalling */
12102ac6454SAndrew Thompson 			goto tr_restart;
12202ac6454SAndrew Thompson 		}
12302ac6454SAndrew Thompson 		break;
12402ac6454SAndrew Thompson 	}
12502ac6454SAndrew Thompson 	return;
12602ac6454SAndrew Thompson 
12702ac6454SAndrew Thompson tr_restart:
128a755d548SAndrew Thompson 	/*
129a755d548SAndrew Thompson 	 * If a control transfer is active, stall it, and wait for the
130a755d548SAndrew Thompson 	 * next control transfer.
131a755d548SAndrew Thompson 	 */
132ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request));
13302ac6454SAndrew Thompson 	xfer->nframes = 1;
13402ac6454SAndrew Thompson 	xfer->flags.manual_status = 1;
13502ac6454SAndrew Thompson 	xfer->flags.force_short_xfer = 0;
136ed6d949aSAndrew Thompson 	usbd_xfer_set_stall(xfer);	/* cancel previous transfer, if any */
137a593f6b8SAndrew Thompson 	usbd_transfer_submit(xfer);
13802ac6454SAndrew Thompson }
13902ac6454SAndrew Thompson 
14002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
141a593f6b8SAndrew Thompson  *	usb_handle_set_config
14202ac6454SAndrew Thompson  *
14302ac6454SAndrew Thompson  * Returns:
14402ac6454SAndrew Thompson  *    0: Success
14502ac6454SAndrew Thompson  * Else: Failure
14602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
147e0a69b51SAndrew Thompson static usb_error_t
usb_handle_set_config(struct usb_xfer * xfer,uint8_t conf_no)148a593f6b8SAndrew Thompson usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no)
14902ac6454SAndrew Thompson {
150760bc48eSAndrew Thompson 	struct usb_device *udev = xfer->xroot->udev;
151e0a69b51SAndrew Thompson 	usb_error_t err = 0;
152a18a7a41SHans Petter Selasky 	uint8_t do_unlock;
15302ac6454SAndrew Thompson 
15402ac6454SAndrew Thompson 	/*
15502ac6454SAndrew Thompson 	 * We need to protect against other threads doing probe and
15602ac6454SAndrew Thompson 	 * attach:
15702ac6454SAndrew Thompson 	 */
15802ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
159cb18f7d1SAlfred Perlstein 
160a18a7a41SHans Petter Selasky 	/* Prevent re-enumeration */
161a18a7a41SHans Petter Selasky 	do_unlock = usbd_enum_lock(udev);
16202ac6454SAndrew Thompson 
16302ac6454SAndrew Thompson 	if (conf_no == USB_UNCONFIG_NO) {
16402ac6454SAndrew Thompson 		conf_no = USB_UNCONFIG_INDEX;
16502ac6454SAndrew Thompson 	} else {
16602ac6454SAndrew Thompson 		/*
16702ac6454SAndrew Thompson 		 * The relationship between config number and config index
16802ac6454SAndrew Thompson 		 * is very simple in our case:
16902ac6454SAndrew Thompson 		 */
17002ac6454SAndrew Thompson 		conf_no--;
17102ac6454SAndrew Thompson 	}
17202ac6454SAndrew Thompson 
173a593f6b8SAndrew Thompson 	if (usbd_set_config_index(udev, conf_no)) {
17402ac6454SAndrew Thompson 		DPRINTF("set config %d failed\n", conf_no);
17502ac6454SAndrew Thompson 		err = USB_ERR_STALLED;
17602ac6454SAndrew Thompson 		goto done;
17702ac6454SAndrew Thompson 	}
178a593f6b8SAndrew Thompson 	if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
17902ac6454SAndrew Thompson 		DPRINTF("probe and attach failed\n");
18002ac6454SAndrew Thompson 		err = USB_ERR_STALLED;
18102ac6454SAndrew Thompson 		goto done;
18202ac6454SAndrew Thompson 	}
18302ac6454SAndrew Thompson done:
184a18a7a41SHans Petter Selasky 	if (do_unlock)
185cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(udev);
18602ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
18702ac6454SAndrew Thompson 	return (err);
18802ac6454SAndrew Thompson }
18902ac6454SAndrew Thompson 
190bd73b187SAlfred Perlstein static usb_error_t
usb_check_alt_setting(struct usb_device * udev,struct usb_interface * iface,uint8_t alt_index)191bd73b187SAlfred Perlstein usb_check_alt_setting(struct usb_device *udev,
192bd73b187SAlfred Perlstein      struct usb_interface *iface, uint8_t alt_index)
193bd73b187SAlfred Perlstein {
194bd73b187SAlfred Perlstein 	uint8_t do_unlock;
195bd73b187SAlfred Perlstein 	usb_error_t err = 0;
196bd73b187SAlfred Perlstein 
1976950c75fSHans Petter Selasky 	/* Prevent re-enumeration */
1986950c75fSHans Petter Selasky 	do_unlock = usbd_enum_lock(udev);
199bd73b187SAlfred Perlstein 
200bd73b187SAlfred Perlstein 	if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc))
201bd73b187SAlfred Perlstein 		err = USB_ERR_INVAL;
202bd73b187SAlfred Perlstein 
203cb18f7d1SAlfred Perlstein 	if (do_unlock)
204cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(udev);
205cb18f7d1SAlfred Perlstein 
206bd73b187SAlfred Perlstein 	return (err);
207bd73b187SAlfred Perlstein }
208bd73b187SAlfred Perlstein 
20902ac6454SAndrew Thompson /*------------------------------------------------------------------------*
210a593f6b8SAndrew Thompson  *	usb_handle_iface_request
21102ac6454SAndrew Thompson  *
21202ac6454SAndrew Thompson  * Returns:
21302ac6454SAndrew Thompson  *    0: Success
21402ac6454SAndrew Thompson  * Else: Failure
21502ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
216e0a69b51SAndrew Thompson static usb_error_t
usb_handle_iface_request(struct usb_xfer * xfer,void ** ppdata,uint16_t * plen,struct usb_device_request req,uint16_t off,uint8_t state)217a593f6b8SAndrew Thompson usb_handle_iface_request(struct usb_xfer *xfer,
21802ac6454SAndrew Thompson     void **ppdata, uint16_t *plen,
219760bc48eSAndrew Thompson     struct usb_device_request req, uint16_t off, uint8_t state)
22002ac6454SAndrew Thompson {
221760bc48eSAndrew Thompson 	struct usb_interface *iface;
222760bc48eSAndrew Thompson 	struct usb_interface *iface_parent;	/* parent interface */
223760bc48eSAndrew Thompson 	struct usb_device *udev = xfer->xroot->udev;
22402ac6454SAndrew Thompson 	int error;
22502ac6454SAndrew Thompson 	uint8_t iface_index;
22629bd7d7eSAndrew Thompson 	uint8_t temp_state;
227a18a7a41SHans Petter Selasky 	uint8_t do_unlock;
22802ac6454SAndrew Thompson 
22902ac6454SAndrew Thompson 	if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
23002ac6454SAndrew Thompson 		iface_index = req.wIndex[0];	/* unicast */
23102ac6454SAndrew Thompson 	} else {
23202ac6454SAndrew Thompson 		iface_index = 0;	/* broadcast */
23302ac6454SAndrew Thompson 	}
23402ac6454SAndrew Thompson 
23502ac6454SAndrew Thompson 	/*
23602ac6454SAndrew Thompson 	 * We need to protect against other threads doing probe and
23702ac6454SAndrew Thompson 	 * attach:
23802ac6454SAndrew Thompson 	 */
23902ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
240cb18f7d1SAlfred Perlstein 
241a18a7a41SHans Petter Selasky 	/* Prevent re-enumeration */
242a18a7a41SHans Petter Selasky 	do_unlock = usbd_enum_lock(udev);
24302ac6454SAndrew Thompson 
24402ac6454SAndrew Thompson 	error = ENXIO;
24502ac6454SAndrew Thompson 
24602ac6454SAndrew Thompson tr_repeat:
247a593f6b8SAndrew Thompson 	iface = usbd_get_iface(udev, iface_index);
24802ac6454SAndrew Thompson 	if ((iface == NULL) ||
24902ac6454SAndrew Thompson 	    (iface->idesc == NULL)) {
25002ac6454SAndrew Thompson 		/* end of interfaces non-existing interface */
25102ac6454SAndrew Thompson 		goto tr_stalled;
25202ac6454SAndrew Thompson 	}
25329bd7d7eSAndrew Thompson 	/* set initial state */
25429bd7d7eSAndrew Thompson 
25529bd7d7eSAndrew Thompson 	temp_state = state;
25629bd7d7eSAndrew Thompson 
25702ac6454SAndrew Thompson 	/* forward request to interface, if any */
25802ac6454SAndrew Thompson 
25902ac6454SAndrew Thompson 	if ((error != 0) &&
26002ac6454SAndrew Thompson 	    (error != ENOTTY) &&
26102ac6454SAndrew Thompson 	    (iface->subdev != NULL) &&
26202ac6454SAndrew Thompson 	    device_is_attached(iface->subdev)) {
26302ac6454SAndrew Thompson #if 0
264090f65a6SAndrew Thompson 		DEVMETHOD(usb_handle_request, NULL);	/* dummy */
26502ac6454SAndrew Thompson #endif
26602ac6454SAndrew Thompson 		error = USB_HANDLE_REQUEST(iface->subdev,
26702ac6454SAndrew Thompson 		    &req, ppdata, plen,
26829bd7d7eSAndrew Thompson 		    off, &temp_state);
26902ac6454SAndrew Thompson 	}
270a593f6b8SAndrew Thompson 	iface_parent = usbd_get_iface(udev, iface->parent_iface_index);
27102ac6454SAndrew Thompson 
27202ac6454SAndrew Thompson 	if ((iface_parent == NULL) ||
27302ac6454SAndrew Thompson 	    (iface_parent->idesc == NULL)) {
27402ac6454SAndrew Thompson 		/* non-existing interface */
27502ac6454SAndrew Thompson 		iface_parent = NULL;
27602ac6454SAndrew Thompson 	}
27702ac6454SAndrew Thompson 	/* forward request to parent interface, if any */
27802ac6454SAndrew Thompson 
27902ac6454SAndrew Thompson 	if ((error != 0) &&
28002ac6454SAndrew Thompson 	    (error != ENOTTY) &&
28102ac6454SAndrew Thompson 	    (iface_parent != NULL) &&
28202ac6454SAndrew Thompson 	    (iface_parent->subdev != NULL) &&
28302ac6454SAndrew Thompson 	    ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
28402ac6454SAndrew Thompson 	    (iface_parent->subdev != iface->subdev) &&
28502ac6454SAndrew Thompson 	    device_is_attached(iface_parent->subdev)) {
28602ac6454SAndrew Thompson 		error = USB_HANDLE_REQUEST(iface_parent->subdev,
28729bd7d7eSAndrew Thompson 		    &req, ppdata, plen, off, &temp_state);
28802ac6454SAndrew Thompson 	}
28902ac6454SAndrew Thompson 	if (error == 0) {
29002ac6454SAndrew Thompson 		/* negativly adjust pointer and length */
29102ac6454SAndrew Thompson 		*ppdata = ((uint8_t *)(*ppdata)) - off;
29202ac6454SAndrew Thompson 		*plen += off;
29329bd7d7eSAndrew Thompson 
29429bd7d7eSAndrew Thompson 		if ((state == USB_HR_NOT_COMPLETE) &&
29529bd7d7eSAndrew Thompson 		    (temp_state == USB_HR_COMPLETE_OK))
29629bd7d7eSAndrew Thompson 			goto tr_short;
29729bd7d7eSAndrew Thompson 		else
29802ac6454SAndrew Thompson 			goto tr_valid;
29902ac6454SAndrew Thompson 	} else if (error == ENOTTY) {
30002ac6454SAndrew Thompson 		goto tr_stalled;
30102ac6454SAndrew Thompson 	}
30202ac6454SAndrew Thompson 	if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
30302ac6454SAndrew Thompson 		iface_index++;		/* iterate */
30402ac6454SAndrew Thompson 		goto tr_repeat;
30502ac6454SAndrew Thompson 	}
306a755d548SAndrew Thompson 	if (state != USB_HR_NOT_COMPLETE) {
30702ac6454SAndrew Thompson 		/* we are complete */
30802ac6454SAndrew Thompson 		goto tr_valid;
30902ac6454SAndrew Thompson 	}
31002ac6454SAndrew Thompson 	switch (req.bmRequestType) {
31102ac6454SAndrew Thompson 	case UT_WRITE_INTERFACE:
31202ac6454SAndrew Thompson 		switch (req.bRequest) {
31302ac6454SAndrew Thompson 		case UR_SET_INTERFACE:
31402ac6454SAndrew Thompson 			/*
315bd73b187SAlfred Perlstein 			 * We assume that the endpoints are the same
31620733245SPedro F. Giffuni 			 * across the alternate settings.
317bd73b187SAlfred Perlstein 			 *
318bd73b187SAlfred Perlstein 			 * Reset the endpoints, because re-attaching
319bd73b187SAlfred Perlstein 			 * only a part of the device is not possible.
32002ac6454SAndrew Thompson 			 */
321bd73b187SAlfred Perlstein 			error = usb_check_alt_setting(udev,
322bd73b187SAlfred Perlstein 			    iface, req.wValue[0]);
323bd73b187SAlfred Perlstein 			if (error) {
324bd73b187SAlfred Perlstein 				DPRINTF("alt setting does not exist %s\n",
325bd73b187SAlfred Perlstein 				    usbd_errstr(error));
326bd73b187SAlfred Perlstein 				goto tr_stalled;
327bd73b187SAlfred Perlstein 			}
328bd73b187SAlfred Perlstein 			error = usb_reset_iface_endpoints(udev, iface_index);
32902ac6454SAndrew Thompson 			if (error) {
33002ac6454SAndrew Thompson 				DPRINTF("alt setting failed %s\n",
331a593f6b8SAndrew Thompson 				    usbd_errstr(error));
33202ac6454SAndrew Thompson 				goto tr_stalled;
33302ac6454SAndrew Thompson 			}
334bd73b187SAlfred Perlstein 			/* update the current alternate setting */
335bd73b187SAlfred Perlstein 			iface->alt_index = req.wValue[0];
33602ac6454SAndrew Thompson 			break;
337bd73b187SAlfred Perlstein 
33802ac6454SAndrew Thompson 		default:
33902ac6454SAndrew Thompson 			goto tr_stalled;
34002ac6454SAndrew Thompson 		}
34102ac6454SAndrew Thompson 		break;
34202ac6454SAndrew Thompson 
34302ac6454SAndrew Thompson 	case UT_READ_INTERFACE:
34402ac6454SAndrew Thompson 		switch (req.bRequest) {
34502ac6454SAndrew Thompson 		case UR_GET_INTERFACE:
34602ac6454SAndrew Thompson 			*ppdata = &iface->alt_index;
34702ac6454SAndrew Thompson 			*plen = 1;
34802ac6454SAndrew Thompson 			break;
34902ac6454SAndrew Thompson 
35002ac6454SAndrew Thompson 		default:
35102ac6454SAndrew Thompson 			goto tr_stalled;
35202ac6454SAndrew Thompson 		}
35302ac6454SAndrew Thompson 		break;
35402ac6454SAndrew Thompson 	default:
35502ac6454SAndrew Thompson 		goto tr_stalled;
35602ac6454SAndrew Thompson 	}
35702ac6454SAndrew Thompson tr_valid:
358a18a7a41SHans Petter Selasky 	if (do_unlock)
359cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(udev);
36002ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
36102ac6454SAndrew Thompson 	return (0);
36202ac6454SAndrew Thompson 
36329bd7d7eSAndrew Thompson tr_short:
364a18a7a41SHans Petter Selasky 	if (do_unlock)
365cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(udev);
36629bd7d7eSAndrew Thompson 	USB_XFER_LOCK(xfer);
36729bd7d7eSAndrew Thompson 	return (USB_ERR_SHORT_XFER);
36829bd7d7eSAndrew Thompson 
36902ac6454SAndrew Thompson tr_stalled:
370a18a7a41SHans Petter Selasky 	if (do_unlock)
371cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(udev);
37202ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
37302ac6454SAndrew Thompson 	return (USB_ERR_STALLED);
37402ac6454SAndrew Thompson }
37502ac6454SAndrew Thompson 
37602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
377a593f6b8SAndrew Thompson  *	usb_handle_stall
37802ac6454SAndrew Thompson  *
37902ac6454SAndrew Thompson  * Returns:
38002ac6454SAndrew Thompson  *    0: Success
38102ac6454SAndrew Thompson  * Else: Failure
38202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
383e0a69b51SAndrew Thompson static usb_error_t
usb_handle_set_stall(struct usb_xfer * xfer,uint8_t ep,uint8_t do_stall)384a593f6b8SAndrew Thompson usb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall)
38502ac6454SAndrew Thompson {
386760bc48eSAndrew Thompson 	struct usb_device *udev = xfer->xroot->udev;
387e0a69b51SAndrew Thompson 	usb_error_t err;
38802ac6454SAndrew Thompson 
38902ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
390a593f6b8SAndrew Thompson 	err = usbd_set_endpoint_stall(udev,
391a593f6b8SAndrew Thompson 	    usbd_get_ep_by_addr(udev, ep), do_stall);
39202ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
39302ac6454SAndrew Thompson 	return (err);
39402ac6454SAndrew Thompson }
39502ac6454SAndrew Thompson 
39602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
397a593f6b8SAndrew Thompson  *	usb_handle_get_stall
39802ac6454SAndrew Thompson  *
39902ac6454SAndrew Thompson  * Returns:
40002ac6454SAndrew Thompson  *    0: Success
40102ac6454SAndrew Thompson  * Else: Failure
40202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
40302ac6454SAndrew Thompson static uint8_t
usb_handle_get_stall(struct usb_device * udev,uint8_t ea_val)404a593f6b8SAndrew Thompson usb_handle_get_stall(struct usb_device *udev, uint8_t ea_val)
40502ac6454SAndrew Thompson {
406ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
40702ac6454SAndrew Thompson 	uint8_t halted;
40802ac6454SAndrew Thompson 
409a593f6b8SAndrew Thompson 	ep = usbd_get_ep_by_addr(udev, ea_val);
410ae60fdfbSAndrew Thompson 	if (ep == NULL) {
41102ac6454SAndrew Thompson 		/* nothing to do */
41202ac6454SAndrew Thompson 		return (0);
41302ac6454SAndrew Thompson 	}
41402ac6454SAndrew Thompson 	USB_BUS_LOCK(udev->bus);
415ae60fdfbSAndrew Thompson 	halted = ep->is_stalled;
41602ac6454SAndrew Thompson 	USB_BUS_UNLOCK(udev->bus);
41702ac6454SAndrew Thompson 
41802ac6454SAndrew Thompson 	return (halted);
41902ac6454SAndrew Thompson }
42002ac6454SAndrew Thompson 
42102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
422a593f6b8SAndrew Thompson  *	usb_handle_remote_wakeup
42302ac6454SAndrew Thompson  *
42402ac6454SAndrew Thompson  * Returns:
42502ac6454SAndrew Thompson  *    0: Success
42602ac6454SAndrew Thompson  * Else: Failure
42702ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
428e0a69b51SAndrew Thompson static usb_error_t
usb_handle_remote_wakeup(struct usb_xfer * xfer,uint8_t is_on)429a593f6b8SAndrew Thompson usb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on)
43002ac6454SAndrew Thompson {
431760bc48eSAndrew Thompson 	struct usb_device *udev;
432760bc48eSAndrew Thompson 	struct usb_bus *bus;
43302ac6454SAndrew Thompson 
43402ac6454SAndrew Thompson 	udev = xfer->xroot->udev;
43502ac6454SAndrew Thompson 	bus = udev->bus;
43602ac6454SAndrew Thompson 
43702ac6454SAndrew Thompson 	USB_BUS_LOCK(bus);
43802ac6454SAndrew Thompson 
43902ac6454SAndrew Thompson 	if (is_on) {
44002ac6454SAndrew Thompson 		udev->flags.remote_wakeup = 1;
44102ac6454SAndrew Thompson 	} else {
44202ac6454SAndrew Thompson 		udev->flags.remote_wakeup = 0;
44302ac6454SAndrew Thompson 	}
44402ac6454SAndrew Thompson 
44502ac6454SAndrew Thompson 	USB_BUS_UNLOCK(bus);
44602ac6454SAndrew Thompson 
4472ecb4e91SHans Petter Selasky #if USB_HAVE_POWERD
44802ac6454SAndrew Thompson 	/* In case we are out of sync, update the power state. */
449a593f6b8SAndrew Thompson 	usb_bus_power_update(udev->bus);
4502ecb4e91SHans Petter Selasky #endif
45102ac6454SAndrew Thompson 	return (0);			/* success */
45202ac6454SAndrew Thompson }
45302ac6454SAndrew Thompson 
45402ac6454SAndrew Thompson /*------------------------------------------------------------------------*
455a593f6b8SAndrew Thompson  *	usb_handle_request
45602ac6454SAndrew Thompson  *
45702ac6454SAndrew Thompson  * Internal state sequence:
45802ac6454SAndrew Thompson  *
459a755d548SAndrew Thompson  * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR
46002ac6454SAndrew Thompson  *
46102ac6454SAndrew Thompson  * Returns:
46202ac6454SAndrew Thompson  * 0: Ready to start hardware
46302ac6454SAndrew Thompson  * Else: Stall current transfer, if any
46402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
465e0a69b51SAndrew Thompson static usb_error_t
usb_handle_request(struct usb_xfer * xfer)466a593f6b8SAndrew Thompson usb_handle_request(struct usb_xfer *xfer)
46702ac6454SAndrew Thompson {
468760bc48eSAndrew Thompson 	struct usb_device_request req;
469760bc48eSAndrew Thompson 	struct usb_device *udev;
47002ac6454SAndrew Thompson 	const void *src_zcopy;		/* zero-copy source pointer */
47102ac6454SAndrew Thompson 	const void *src_mcopy;		/* non zero-copy source pointer */
47202ac6454SAndrew Thompson 	uint16_t off;			/* data offset */
47302ac6454SAndrew Thompson 	uint16_t rem;			/* data remainder */
47402ac6454SAndrew Thompson 	uint16_t max_len;		/* max fragment length */
47502ac6454SAndrew Thompson 	uint16_t wValue;
47602ac6454SAndrew Thompson 	uint8_t state;
47729bd7d7eSAndrew Thompson 	uint8_t is_complete = 1;
478e0a69b51SAndrew Thompson 	usb_error_t err;
47902ac6454SAndrew Thompson 	union {
48002ac6454SAndrew Thompson 		uWord	wStatus;
48102ac6454SAndrew Thompson 		uint8_t	buf[2];
48202ac6454SAndrew Thompson 	}     temp;
48302ac6454SAndrew Thompson 
48402ac6454SAndrew Thompson 	/*
48502ac6454SAndrew Thompson 	 * Filter the USB transfer state into
48602ac6454SAndrew Thompson 	 * something which we understand:
48702ac6454SAndrew Thompson 	 */
48802ac6454SAndrew Thompson 
48902ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
49002ac6454SAndrew Thompson 	case USB_ST_SETUP:
491a755d548SAndrew Thompson 		state = USB_HR_NOT_COMPLETE;
49202ac6454SAndrew Thompson 
49302ac6454SAndrew Thompson 		if (!xfer->flags_int.control_act) {
49402ac6454SAndrew Thompson 			/* nothing to do */
49502ac6454SAndrew Thompson 			goto tr_stalled;
49602ac6454SAndrew Thompson 		}
49702ac6454SAndrew Thompson 		break;
498a755d548SAndrew Thompson 	case USB_ST_TRANSFERRED:
49902ac6454SAndrew Thompson 		if (!xfer->flags_int.control_act) {
500a755d548SAndrew Thompson 			state = USB_HR_COMPLETE_OK;
50102ac6454SAndrew Thompson 		} else {
502a755d548SAndrew Thompson 			state = USB_HR_NOT_COMPLETE;
50302ac6454SAndrew Thompson 		}
50402ac6454SAndrew Thompson 		break;
505a755d548SAndrew Thompson 	default:
506a755d548SAndrew Thompson 		state = USB_HR_COMPLETE_ERR;
507a755d548SAndrew Thompson 		break;
50802ac6454SAndrew Thompson 	}
50902ac6454SAndrew Thompson 
51002ac6454SAndrew Thompson 	/* reset frame stuff */
51102ac6454SAndrew Thompson 
512ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_len(xfer, 0, 0);
51302ac6454SAndrew Thompson 
514ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_offset(xfer, 0, 0);
515ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_offset(xfer, sizeof(req), 1);
51602ac6454SAndrew Thompson 
51702ac6454SAndrew Thompson 	/* get the current request, if any */
51802ac6454SAndrew Thompson 
519a593f6b8SAndrew Thompson 	usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
52002ac6454SAndrew Thompson 
52102ac6454SAndrew Thompson 	if (xfer->flags_int.control_rem == 0xFFFF) {
52202ac6454SAndrew Thompson 		/* first time - not initialised */
52302ac6454SAndrew Thompson 		rem = UGETW(req.wLength);
52402ac6454SAndrew Thompson 		off = 0;
52502ac6454SAndrew Thompson 	} else {
52602ac6454SAndrew Thompson 		/* not first time - initialised */
52702ac6454SAndrew Thompson 		rem = xfer->flags_int.control_rem;
52802ac6454SAndrew Thompson 		off = UGETW(req.wLength) - rem;
52902ac6454SAndrew Thompson 	}
53002ac6454SAndrew Thompson 
53102ac6454SAndrew Thompson 	/* set some defaults */
53202ac6454SAndrew Thompson 
53302ac6454SAndrew Thompson 	max_len = 0;
53402ac6454SAndrew Thompson 	src_zcopy = NULL;
53502ac6454SAndrew Thompson 	src_mcopy = NULL;
53602ac6454SAndrew Thompson 	udev = xfer->xroot->udev;
53702ac6454SAndrew Thompson 
53802ac6454SAndrew Thompson 	/* get some request fields decoded */
53902ac6454SAndrew Thompson 
54002ac6454SAndrew Thompson 	wValue = UGETW(req.wValue);
54102ac6454SAndrew Thompson 
54202ac6454SAndrew Thompson 	DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
54302ac6454SAndrew Thompson 	    "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
5446d917491SHans Petter Selasky 	    req.bRequest, wValue, UGETW(req.wIndex), off, rem, state);
54502ac6454SAndrew Thompson 
54602ac6454SAndrew Thompson 	/* demultiplex the control request */
54702ac6454SAndrew Thompson 
54802ac6454SAndrew Thompson 	switch (req.bmRequestType) {
54902ac6454SAndrew Thompson 	case UT_READ_DEVICE:
550a755d548SAndrew Thompson 		if (state != USB_HR_NOT_COMPLETE) {
55102ac6454SAndrew Thompson 			break;
55202ac6454SAndrew Thompson 		}
55302ac6454SAndrew Thompson 		switch (req.bRequest) {
55402ac6454SAndrew Thompson 		case UR_GET_DESCRIPTOR:
55502ac6454SAndrew Thompson 			goto tr_handle_get_descriptor;
55602ac6454SAndrew Thompson 		case UR_GET_CONFIG:
55702ac6454SAndrew Thompson 			goto tr_handle_get_config;
55802ac6454SAndrew Thompson 		case UR_GET_STATUS:
55902ac6454SAndrew Thompson 			goto tr_handle_get_status;
56002ac6454SAndrew Thompson 		default:
56102ac6454SAndrew Thompson 			goto tr_stalled;
56202ac6454SAndrew Thompson 		}
56302ac6454SAndrew Thompson 		break;
56402ac6454SAndrew Thompson 
56502ac6454SAndrew Thompson 	case UT_WRITE_DEVICE:
56602ac6454SAndrew Thompson 		switch (req.bRequest) {
56702ac6454SAndrew Thompson 		case UR_SET_ADDRESS:
56802ac6454SAndrew Thompson 			goto tr_handle_set_address;
56902ac6454SAndrew Thompson 		case UR_SET_CONFIG:
57002ac6454SAndrew Thompson 			goto tr_handle_set_config;
57102ac6454SAndrew Thompson 		case UR_CLEAR_FEATURE:
57202ac6454SAndrew Thompson 			switch (wValue) {
57302ac6454SAndrew Thompson 			case UF_DEVICE_REMOTE_WAKEUP:
57402ac6454SAndrew Thompson 				goto tr_handle_clear_wakeup;
57502ac6454SAndrew Thompson 			default:
57602ac6454SAndrew Thompson 				goto tr_stalled;
57702ac6454SAndrew Thompson 			}
57802ac6454SAndrew Thompson 			break;
57902ac6454SAndrew Thompson 		case UR_SET_FEATURE:
58002ac6454SAndrew Thompson 			switch (wValue) {
58102ac6454SAndrew Thompson 			case UF_DEVICE_REMOTE_WAKEUP:
58202ac6454SAndrew Thompson 				goto tr_handle_set_wakeup;
58302ac6454SAndrew Thompson 			default:
58402ac6454SAndrew Thompson 				goto tr_stalled;
58502ac6454SAndrew Thompson 			}
58602ac6454SAndrew Thompson 			break;
58702ac6454SAndrew Thompson 		default:
58802ac6454SAndrew Thompson 			goto tr_stalled;
58902ac6454SAndrew Thompson 		}
59002ac6454SAndrew Thompson 		break;
59102ac6454SAndrew Thompson 
59202ac6454SAndrew Thompson 	case UT_WRITE_ENDPOINT:
59302ac6454SAndrew Thompson 		switch (req.bRequest) {
59402ac6454SAndrew Thompson 		case UR_CLEAR_FEATURE:
59502ac6454SAndrew Thompson 			switch (wValue) {
59602ac6454SAndrew Thompson 			case UF_ENDPOINT_HALT:
59702ac6454SAndrew Thompson 				goto tr_handle_clear_halt;
59802ac6454SAndrew Thompson 			default:
59902ac6454SAndrew Thompson 				goto tr_stalled;
60002ac6454SAndrew Thompson 			}
60102ac6454SAndrew Thompson 			break;
60202ac6454SAndrew Thompson 		case UR_SET_FEATURE:
60302ac6454SAndrew Thompson 			switch (wValue) {
60402ac6454SAndrew Thompson 			case UF_ENDPOINT_HALT:
60502ac6454SAndrew Thompson 				goto tr_handle_set_halt;
60602ac6454SAndrew Thompson 			default:
60702ac6454SAndrew Thompson 				goto tr_stalled;
60802ac6454SAndrew Thompson 			}
60902ac6454SAndrew Thompson 			break;
61002ac6454SAndrew Thompson 		default:
61102ac6454SAndrew Thompson 			goto tr_stalled;
61202ac6454SAndrew Thompson 		}
61302ac6454SAndrew Thompson 		break;
61402ac6454SAndrew Thompson 
61502ac6454SAndrew Thompson 	case UT_READ_ENDPOINT:
61602ac6454SAndrew Thompson 		switch (req.bRequest) {
61702ac6454SAndrew Thompson 		case UR_GET_STATUS:
61802ac6454SAndrew Thompson 			goto tr_handle_get_ep_status;
61902ac6454SAndrew Thompson 		default:
62002ac6454SAndrew Thompson 			goto tr_stalled;
62102ac6454SAndrew Thompson 		}
62202ac6454SAndrew Thompson 		break;
62302ac6454SAndrew Thompson 	default:
62402ac6454SAndrew Thompson 		/* we use "USB_ADD_BYTES" to de-const the src_zcopy */
625a593f6b8SAndrew Thompson 		err = usb_handle_iface_request(xfer,
62602ac6454SAndrew Thompson 		    USB_ADD_BYTES(&src_zcopy, 0),
62702ac6454SAndrew Thompson 		    &max_len, req, off, state);
62802ac6454SAndrew Thompson 		if (err == 0) {
62929bd7d7eSAndrew Thompson 			is_complete = 0;
63029bd7d7eSAndrew Thompson 			goto tr_valid;
63129bd7d7eSAndrew Thompson 		} else if (err == USB_ERR_SHORT_XFER) {
63202ac6454SAndrew Thompson 			goto tr_valid;
63302ac6454SAndrew Thompson 		}
63402ac6454SAndrew Thompson 		/*
63502ac6454SAndrew Thompson 		 * Reset zero-copy pointer and max length
63602ac6454SAndrew Thompson 		 * variable in case they were unintentionally
63702ac6454SAndrew Thompson 		 * set:
63802ac6454SAndrew Thompson 		 */
63902ac6454SAndrew Thompson 		src_zcopy = NULL;
64002ac6454SAndrew Thompson 		max_len = 0;
64102ac6454SAndrew Thompson 
64202ac6454SAndrew Thompson 		/*
64302ac6454SAndrew Thompson 		 * Check if we have a vendor specific
64402ac6454SAndrew Thompson 		 * descriptor:
64502ac6454SAndrew Thompson 		 */
64602ac6454SAndrew Thompson 		goto tr_handle_get_descriptor;
64702ac6454SAndrew Thompson 	}
64802ac6454SAndrew Thompson 	goto tr_valid;
64902ac6454SAndrew Thompson 
65002ac6454SAndrew Thompson tr_handle_get_descriptor:
651a593f6b8SAndrew Thompson 	err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
652459d369eSAndrew Thompson 	if (err)
65302ac6454SAndrew Thompson 		goto tr_stalled;
654459d369eSAndrew Thompson 	if (src_zcopy == NULL)
655459d369eSAndrew Thompson 		goto tr_stalled;
65602ac6454SAndrew Thompson 	goto tr_valid;
65702ac6454SAndrew Thompson 
65802ac6454SAndrew Thompson tr_handle_get_config:
65902ac6454SAndrew Thompson 	temp.buf[0] = udev->curr_config_no;
66002ac6454SAndrew Thompson 	src_mcopy = temp.buf;
66102ac6454SAndrew Thompson 	max_len = 1;
66202ac6454SAndrew Thompson 	goto tr_valid;
66302ac6454SAndrew Thompson 
66402ac6454SAndrew Thompson tr_handle_get_status:
66502ac6454SAndrew Thompson 
66602ac6454SAndrew Thompson 	wValue = 0;
66702ac6454SAndrew Thompson 
66802ac6454SAndrew Thompson 	USB_BUS_LOCK(udev->bus);
66902ac6454SAndrew Thompson 	if (udev->flags.remote_wakeup) {
67002ac6454SAndrew Thompson 		wValue |= UDS_REMOTE_WAKEUP;
67102ac6454SAndrew Thompson 	}
67202ac6454SAndrew Thompson 	if (udev->flags.self_powered) {
67302ac6454SAndrew Thompson 		wValue |= UDS_SELF_POWERED;
67402ac6454SAndrew Thompson 	}
67502ac6454SAndrew Thompson 	USB_BUS_UNLOCK(udev->bus);
67602ac6454SAndrew Thompson 
67702ac6454SAndrew Thompson 	USETW(temp.wStatus, wValue);
67802ac6454SAndrew Thompson 	src_mcopy = temp.wStatus;
67902ac6454SAndrew Thompson 	max_len = sizeof(temp.wStatus);
68002ac6454SAndrew Thompson 	goto tr_valid;
68102ac6454SAndrew Thompson 
68202ac6454SAndrew Thompson tr_handle_set_address:
683a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
68402ac6454SAndrew Thompson 		if (wValue >= 0x80) {
68502ac6454SAndrew Thompson 			/* invalid value */
68602ac6454SAndrew Thompson 			goto tr_stalled;
68702ac6454SAndrew Thompson 		} else if (udev->curr_config_no != 0) {
68802ac6454SAndrew Thompson 			/* we are configured ! */
68902ac6454SAndrew Thompson 			goto tr_stalled;
69002ac6454SAndrew Thompson 		}
691a755d548SAndrew Thompson 	} else if (state != USB_HR_NOT_COMPLETE) {
69202ac6454SAndrew Thompson 		udev->address = (wValue & 0x7F);
69302ac6454SAndrew Thompson 		goto tr_bad_context;
69402ac6454SAndrew Thompson 	}
69502ac6454SAndrew Thompson 	goto tr_valid;
69602ac6454SAndrew Thompson 
69702ac6454SAndrew Thompson tr_handle_set_config:
698a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
699a593f6b8SAndrew Thompson 		if (usb_handle_set_config(xfer, req.wValue[0])) {
70002ac6454SAndrew Thompson 			goto tr_stalled;
70102ac6454SAndrew Thompson 		}
70202ac6454SAndrew Thompson 	}
70302ac6454SAndrew Thompson 	goto tr_valid;
70402ac6454SAndrew Thompson 
70502ac6454SAndrew Thompson tr_handle_clear_halt:
706a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
707a593f6b8SAndrew Thompson 		if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) {
70802ac6454SAndrew Thompson 			goto tr_stalled;
70902ac6454SAndrew Thompson 		}
71002ac6454SAndrew Thompson 	}
71102ac6454SAndrew Thompson 	goto tr_valid;
71202ac6454SAndrew Thompson 
71302ac6454SAndrew Thompson tr_handle_clear_wakeup:
714a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
715a593f6b8SAndrew Thompson 		if (usb_handle_remote_wakeup(xfer, 0)) {
71602ac6454SAndrew Thompson 			goto tr_stalled;
71702ac6454SAndrew Thompson 		}
71802ac6454SAndrew Thompson 	}
71902ac6454SAndrew Thompson 	goto tr_valid;
72002ac6454SAndrew Thompson 
72102ac6454SAndrew Thompson tr_handle_set_halt:
722a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
723a593f6b8SAndrew Thompson 		if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) {
72402ac6454SAndrew Thompson 			goto tr_stalled;
72502ac6454SAndrew Thompson 		}
72602ac6454SAndrew Thompson 	}
72702ac6454SAndrew Thompson 	goto tr_valid;
72802ac6454SAndrew Thompson 
72902ac6454SAndrew Thompson tr_handle_set_wakeup:
730a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
731a593f6b8SAndrew Thompson 		if (usb_handle_remote_wakeup(xfer, 1)) {
73202ac6454SAndrew Thompson 			goto tr_stalled;
73302ac6454SAndrew Thompson 		}
73402ac6454SAndrew Thompson 	}
73502ac6454SAndrew Thompson 	goto tr_valid;
73602ac6454SAndrew Thompson 
73702ac6454SAndrew Thompson tr_handle_get_ep_status:
738a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
73902ac6454SAndrew Thompson 		temp.wStatus[0] =
740a593f6b8SAndrew Thompson 		    usb_handle_get_stall(udev, req.wIndex[0]);
74102ac6454SAndrew Thompson 		temp.wStatus[1] = 0;
74202ac6454SAndrew Thompson 		src_mcopy = temp.wStatus;
74302ac6454SAndrew Thompson 		max_len = sizeof(temp.wStatus);
74402ac6454SAndrew Thompson 	}
74502ac6454SAndrew Thompson 	goto tr_valid;
74602ac6454SAndrew Thompson 
74702ac6454SAndrew Thompson tr_valid:
748a755d548SAndrew Thompson 	if (state != USB_HR_NOT_COMPLETE) {
74902ac6454SAndrew Thompson 		goto tr_stalled;
75002ac6454SAndrew Thompson 	}
75102ac6454SAndrew Thompson 	/* subtract offset from length */
75202ac6454SAndrew Thompson 
75302ac6454SAndrew Thompson 	max_len -= off;
75402ac6454SAndrew Thompson 
75502ac6454SAndrew Thompson 	/* Compute the real maximum data length */
75602ac6454SAndrew Thompson 
75702ac6454SAndrew Thompson 	if (max_len > xfer->max_data_length) {
758ed6d949aSAndrew Thompson 		max_len = usbd_xfer_max_len(xfer);
75902ac6454SAndrew Thompson 	}
76002ac6454SAndrew Thompson 	if (max_len > rem) {
76102ac6454SAndrew Thompson 		max_len = rem;
76202ac6454SAndrew Thompson 	}
76302ac6454SAndrew Thompson 	/*
76402ac6454SAndrew Thompson 	 * If the remainder is greater than the maximum data length,
76502ac6454SAndrew Thompson 	 * we need to truncate the value for the sake of the
76602ac6454SAndrew Thompson 	 * comparison below:
76702ac6454SAndrew Thompson 	 */
76802ac6454SAndrew Thompson 	if (rem > xfer->max_data_length) {
769ed6d949aSAndrew Thompson 		rem = usbd_xfer_max_len(xfer);
77002ac6454SAndrew Thompson 	}
77129bd7d7eSAndrew Thompson 	if ((rem != max_len) && (is_complete != 0)) {
77202ac6454SAndrew Thompson 		/*
77302ac6454SAndrew Thompson 	         * If we don't transfer the data we can transfer, then
77402ac6454SAndrew Thompson 	         * the transfer is short !
77502ac6454SAndrew Thompson 	         */
77602ac6454SAndrew Thompson 		xfer->flags.force_short_xfer = 1;
77702ac6454SAndrew Thompson 		xfer->nframes = 2;
77802ac6454SAndrew Thompson 	} else {
77902ac6454SAndrew Thompson 		/*
78002ac6454SAndrew Thompson 		 * Default case
78102ac6454SAndrew Thompson 		 */
78202ac6454SAndrew Thompson 		xfer->flags.force_short_xfer = 0;
78302ac6454SAndrew Thompson 		xfer->nframes = max_len ? 2 : 1;
78402ac6454SAndrew Thompson 	}
78502ac6454SAndrew Thompson 	if (max_len > 0) {
78602ac6454SAndrew Thompson 		if (src_mcopy) {
78702ac6454SAndrew Thompson 			src_mcopy = USB_ADD_BYTES(src_mcopy, off);
788a593f6b8SAndrew Thompson 			usbd_copy_in(xfer->frbuffers + 1, 0,
78902ac6454SAndrew Thompson 			    src_mcopy, max_len);
790ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 1, max_len);
79102ac6454SAndrew Thompson 		} else {
792ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_data(xfer, 1,
793ed6d949aSAndrew Thompson 			    USB_ADD_BYTES(src_zcopy, off), max_len);
79402ac6454SAndrew Thompson 		}
79502ac6454SAndrew Thompson 	} else {
79602ac6454SAndrew Thompson 		/* the end is reached, send status */
79702ac6454SAndrew Thompson 		xfer->flags.manual_status = 0;
798ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 1, 0);
79902ac6454SAndrew Thompson 	}
80002ac6454SAndrew Thompson 	DPRINTF("success\n");
80102ac6454SAndrew Thompson 	return (0);			/* success */
80202ac6454SAndrew Thompson 
80302ac6454SAndrew Thompson tr_stalled:
804a755d548SAndrew Thompson 	DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ?
80502ac6454SAndrew Thompson 	    "complete" : "stalled");
80602ac6454SAndrew Thompson 	return (USB_ERR_STALLED);
80702ac6454SAndrew Thompson 
80802ac6454SAndrew Thompson tr_bad_context:
80902ac6454SAndrew Thompson 	DPRINTF("bad context\n");
81002ac6454SAndrew Thompson 	return (USB_ERR_BAD_CONTEXT);
81102ac6454SAndrew Thompson }
812