xref: /freebsd/sys/dev/usb/usb_handle_request.c (revision 2ecb4e919f3df1edf00be35c9a85bd067495be2e)
102ac6454SAndrew Thompson /* $FreeBSD$ */
202ac6454SAndrew Thompson /*-
302ac6454SAndrew Thompson  * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
402ac6454SAndrew Thompson  *
502ac6454SAndrew Thompson  * Redistribution and use in source and binary forms, with or without
602ac6454SAndrew Thompson  * modification, are permitted provided that the following conditions
702ac6454SAndrew Thompson  * are met:
802ac6454SAndrew Thompson  * 1. Redistributions of source code must retain the above copyright
902ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer.
1002ac6454SAndrew Thompson  * 2. Redistributions in binary form must reproduce the above copyright
1102ac6454SAndrew Thompson  *    notice, this list of conditions and the following disclaimer in the
1202ac6454SAndrew Thompson  *    documentation and/or other materials provided with the distribution.
1302ac6454SAndrew Thompson  *
1402ac6454SAndrew Thompson  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1502ac6454SAndrew Thompson  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1602ac6454SAndrew Thompson  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1702ac6454SAndrew Thompson  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1802ac6454SAndrew Thompson  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1902ac6454SAndrew Thompson  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2002ac6454SAndrew Thompson  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2102ac6454SAndrew Thompson  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2202ac6454SAndrew Thompson  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2302ac6454SAndrew Thompson  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2402ac6454SAndrew Thompson  * SUCH DAMAGE.
2502ac6454SAndrew Thompson  */
2602ac6454SAndrew Thompson 
27ed6d949aSAndrew Thompson #include <sys/stdint.h>
28ed6d949aSAndrew Thompson #include <sys/stddef.h>
29ed6d949aSAndrew Thompson #include <sys/param.h>
30ed6d949aSAndrew Thompson #include <sys/queue.h>
31ed6d949aSAndrew Thompson #include <sys/types.h>
32ed6d949aSAndrew Thompson #include <sys/systm.h>
33ed6d949aSAndrew Thompson #include <sys/kernel.h>
34ed6d949aSAndrew Thompson #include <sys/bus.h>
35ed6d949aSAndrew Thompson #include <sys/linker_set.h>
36ed6d949aSAndrew Thompson #include <sys/module.h>
37ed6d949aSAndrew Thompson #include <sys/lock.h>
38ed6d949aSAndrew Thompson #include <sys/mutex.h>
39ed6d949aSAndrew Thompson #include <sys/condvar.h>
40ed6d949aSAndrew Thompson #include <sys/sysctl.h>
41ed6d949aSAndrew Thompson #include <sys/sx.h>
42ed6d949aSAndrew Thompson #include <sys/unistd.h>
43ed6d949aSAndrew Thompson #include <sys/callout.h>
44ed6d949aSAndrew Thompson #include <sys/malloc.h>
45ed6d949aSAndrew Thompson #include <sys/priv.h>
46ed6d949aSAndrew Thompson 
4702ac6454SAndrew Thompson #include <dev/usb/usb.h>
48ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
49bd73b187SAlfred Perlstein #include <dev/usb/usbdi_util.h>
50ed6d949aSAndrew Thompson #include "usb_if.h"
5102ac6454SAndrew Thompson 
52a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_debug
5302ac6454SAndrew Thompson 
5402ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
5502ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
5602ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
5702ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h>
5802ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
5902ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
6002ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
6102ac6454SAndrew Thompson #include <dev/usb/usb_hub.h>
6202ac6454SAndrew Thompson 
6302ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
6402ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
6502ac6454SAndrew Thompson 
6602ac6454SAndrew Thompson /* function prototypes */
6702ac6454SAndrew Thompson 
68a593f6b8SAndrew Thompson static uint8_t usb_handle_get_stall(struct usb_device *, uint8_t);
69a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_remote_wakeup(struct usb_xfer *, uint8_t);
70a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_request(struct usb_xfer *);
71a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_set_config(struct usb_xfer *, uint8_t);
72a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_set_stall(struct usb_xfer *, uint8_t,
7302ac6454SAndrew Thompson 			    uint8_t);
74a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_iface_request(struct usb_xfer *, void **,
75760bc48eSAndrew Thompson 			    uint16_t *, struct usb_device_request, uint16_t,
7602ac6454SAndrew Thompson 			    uint8_t);
7702ac6454SAndrew Thompson 
7802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
79a593f6b8SAndrew Thompson  *	usb_handle_request_callback
8002ac6454SAndrew Thompson  *
8102ac6454SAndrew Thompson  * This function is the USB callback for generic USB Device control
8202ac6454SAndrew Thompson  * transfers.
8302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
8402ac6454SAndrew Thompson void
85ed6d949aSAndrew Thompson usb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error)
8602ac6454SAndrew Thompson {
87e0a69b51SAndrew Thompson 	usb_error_t err;
8802ac6454SAndrew Thompson 
8902ac6454SAndrew Thompson 	/* check the current transfer state */
9002ac6454SAndrew Thompson 
9102ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9202ac6454SAndrew Thompson 	case USB_ST_SETUP:
9302ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
9402ac6454SAndrew Thompson 
9502ac6454SAndrew Thompson 		/* handle the request */
96a593f6b8SAndrew Thompson 		err = usb_handle_request(xfer);
9702ac6454SAndrew Thompson 
9802ac6454SAndrew Thompson 		if (err) {
9902ac6454SAndrew Thompson 
10002ac6454SAndrew Thompson 			if (err == USB_ERR_BAD_CONTEXT) {
10102ac6454SAndrew Thompson 				/* we need to re-setup the control transfer */
102a593f6b8SAndrew Thompson 				usb_needs_explore(xfer->xroot->bus, 0);
10302ac6454SAndrew Thompson 				break;
10402ac6454SAndrew Thompson 			}
10502ac6454SAndrew Thompson 			goto tr_restart;
10602ac6454SAndrew Thompson 		}
107a593f6b8SAndrew Thompson 		usbd_transfer_submit(xfer);
10802ac6454SAndrew Thompson 		break;
10902ac6454SAndrew Thompson 
11002ac6454SAndrew Thompson 	default:
111a755d548SAndrew Thompson 		/* check if a control transfer is active */
112a755d548SAndrew Thompson 		if (xfer->flags_int.control_rem != 0xFFFF) {
113a755d548SAndrew Thompson 			/* handle the request */
114a593f6b8SAndrew Thompson 			err = usb_handle_request(xfer);
115a755d548SAndrew Thompson 		}
11602ac6454SAndrew Thompson 		if (xfer->error != USB_ERR_CANCELLED) {
11702ac6454SAndrew Thompson 			/* should not happen - try stalling */
11802ac6454SAndrew Thompson 			goto tr_restart;
11902ac6454SAndrew Thompson 		}
12002ac6454SAndrew Thompson 		break;
12102ac6454SAndrew Thompson 	}
12202ac6454SAndrew Thompson 	return;
12302ac6454SAndrew Thompson 
12402ac6454SAndrew Thompson tr_restart:
125a755d548SAndrew Thompson 	/*
126a755d548SAndrew Thompson 	 * If a control transfer is active, stall it, and wait for the
127a755d548SAndrew Thompson 	 * next control transfer.
128a755d548SAndrew Thompson 	 */
129ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request));
13002ac6454SAndrew Thompson 	xfer->nframes = 1;
13102ac6454SAndrew Thompson 	xfer->flags.manual_status = 1;
13202ac6454SAndrew Thompson 	xfer->flags.force_short_xfer = 0;
133ed6d949aSAndrew Thompson 	usbd_xfer_set_stall(xfer);	/* cancel previous transfer, if any */
134a593f6b8SAndrew Thompson 	usbd_transfer_submit(xfer);
13502ac6454SAndrew Thompson }
13602ac6454SAndrew Thompson 
13702ac6454SAndrew Thompson /*------------------------------------------------------------------------*
138a593f6b8SAndrew Thompson  *	usb_handle_set_config
13902ac6454SAndrew Thompson  *
14002ac6454SAndrew Thompson  * Returns:
14102ac6454SAndrew Thompson  *    0: Success
14202ac6454SAndrew Thompson  * Else: Failure
14302ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
144e0a69b51SAndrew Thompson static usb_error_t
145a593f6b8SAndrew Thompson usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no)
14602ac6454SAndrew Thompson {
147760bc48eSAndrew Thompson 	struct usb_device *udev = xfer->xroot->udev;
148e0a69b51SAndrew Thompson 	usb_error_t err = 0;
14902ac6454SAndrew Thompson 
15002ac6454SAndrew Thompson 	/*
15102ac6454SAndrew Thompson 	 * We need to protect against other threads doing probe and
15202ac6454SAndrew Thompson 	 * attach:
15302ac6454SAndrew Thompson 	 */
15402ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
155cb18f7d1SAlfred Perlstein 
156cb18f7d1SAlfred Perlstein 	usbd_enum_lock(udev);
15702ac6454SAndrew Thompson 
15802ac6454SAndrew Thompson 	if (conf_no == USB_UNCONFIG_NO) {
15902ac6454SAndrew Thompson 		conf_no = USB_UNCONFIG_INDEX;
16002ac6454SAndrew Thompson 	} else {
16102ac6454SAndrew Thompson 		/*
16202ac6454SAndrew Thompson 		 * The relationship between config number and config index
16302ac6454SAndrew Thompson 		 * is very simple in our case:
16402ac6454SAndrew Thompson 		 */
16502ac6454SAndrew Thompson 		conf_no--;
16602ac6454SAndrew Thompson 	}
16702ac6454SAndrew Thompson 
168a593f6b8SAndrew Thompson 	if (usbd_set_config_index(udev, conf_no)) {
16902ac6454SAndrew Thompson 		DPRINTF("set config %d failed\n", conf_no);
17002ac6454SAndrew Thompson 		err = USB_ERR_STALLED;
17102ac6454SAndrew Thompson 		goto done;
17202ac6454SAndrew Thompson 	}
173a593f6b8SAndrew Thompson 	if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
17402ac6454SAndrew Thompson 		DPRINTF("probe and attach failed\n");
17502ac6454SAndrew Thompson 		err = USB_ERR_STALLED;
17602ac6454SAndrew Thompson 		goto done;
17702ac6454SAndrew Thompson 	}
17802ac6454SAndrew Thompson done:
179cb18f7d1SAlfred Perlstein 	usbd_enum_unlock(udev);
18002ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
18102ac6454SAndrew Thompson 	return (err);
18202ac6454SAndrew Thompson }
18302ac6454SAndrew Thompson 
184bd73b187SAlfred Perlstein static usb_error_t
185bd73b187SAlfred Perlstein usb_check_alt_setting(struct usb_device *udev,
186bd73b187SAlfred Perlstein      struct usb_interface *iface, uint8_t alt_index)
187bd73b187SAlfred Perlstein {
188bd73b187SAlfred Perlstein 	uint8_t do_unlock;
189bd73b187SAlfred Perlstein 	usb_error_t err = 0;
190bd73b187SAlfred Perlstein 
191bd73b187SAlfred Perlstein 	/* automatic locking */
192cb18f7d1SAlfred Perlstein 	if (usbd_enum_is_locked(udev)) {
193bd73b187SAlfred Perlstein 		do_unlock = 0;
194bd73b187SAlfred Perlstein 	} else {
195bd73b187SAlfred Perlstein 		do_unlock = 1;
196cb18f7d1SAlfred Perlstein 		usbd_enum_lock(udev);
197bd73b187SAlfred Perlstein 	}
198bd73b187SAlfred Perlstein 
199bd73b187SAlfred Perlstein 	if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc))
200bd73b187SAlfred Perlstein 		err = USB_ERR_INVAL;
201bd73b187SAlfred Perlstein 
202cb18f7d1SAlfred Perlstein 	if (do_unlock)
203cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(udev);
204cb18f7d1SAlfred Perlstein 
205bd73b187SAlfred Perlstein 	return (err);
206bd73b187SAlfred Perlstein }
207bd73b187SAlfred Perlstein 
20802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
209a593f6b8SAndrew Thompson  *	usb_handle_iface_request
21002ac6454SAndrew Thompson  *
21102ac6454SAndrew Thompson  * Returns:
21202ac6454SAndrew Thompson  *    0: Success
21302ac6454SAndrew Thompson  * Else: Failure
21402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
215e0a69b51SAndrew Thompson static usb_error_t
216a593f6b8SAndrew Thompson usb_handle_iface_request(struct usb_xfer *xfer,
21702ac6454SAndrew Thompson     void **ppdata, uint16_t *plen,
218760bc48eSAndrew Thompson     struct usb_device_request req, uint16_t off, uint8_t state)
21902ac6454SAndrew Thompson {
220760bc48eSAndrew Thompson 	struct usb_interface *iface;
221760bc48eSAndrew Thompson 	struct usb_interface *iface_parent;	/* parent interface */
222760bc48eSAndrew Thompson 	struct usb_device *udev = xfer->xroot->udev;
22302ac6454SAndrew Thompson 	int error;
22402ac6454SAndrew Thompson 	uint8_t iface_index;
22529bd7d7eSAndrew Thompson 	uint8_t temp_state;
22602ac6454SAndrew Thompson 
22702ac6454SAndrew Thompson 	if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
22802ac6454SAndrew Thompson 		iface_index = req.wIndex[0];	/* unicast */
22902ac6454SAndrew Thompson 	} else {
23002ac6454SAndrew Thompson 		iface_index = 0;	/* broadcast */
23102ac6454SAndrew Thompson 	}
23202ac6454SAndrew Thompson 
23302ac6454SAndrew Thompson 	/*
23402ac6454SAndrew Thompson 	 * We need to protect against other threads doing probe and
23502ac6454SAndrew Thompson 	 * attach:
23602ac6454SAndrew Thompson 	 */
23702ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
238cb18f7d1SAlfred Perlstein 
239cb18f7d1SAlfred Perlstein 	usbd_enum_lock(udev);
24002ac6454SAndrew Thompson 
24102ac6454SAndrew Thompson 	error = ENXIO;
24202ac6454SAndrew Thompson 
24302ac6454SAndrew Thompson tr_repeat:
244a593f6b8SAndrew Thompson 	iface = usbd_get_iface(udev, iface_index);
24502ac6454SAndrew Thompson 	if ((iface == NULL) ||
24602ac6454SAndrew Thompson 	    (iface->idesc == NULL)) {
24702ac6454SAndrew Thompson 		/* end of interfaces non-existing interface */
24802ac6454SAndrew Thompson 		goto tr_stalled;
24902ac6454SAndrew Thompson 	}
25029bd7d7eSAndrew Thompson 	/* set initial state */
25129bd7d7eSAndrew Thompson 
25229bd7d7eSAndrew Thompson 	temp_state = state;
25329bd7d7eSAndrew Thompson 
25402ac6454SAndrew Thompson 	/* forward request to interface, if any */
25502ac6454SAndrew Thompson 
25602ac6454SAndrew Thompson 	if ((error != 0) &&
25702ac6454SAndrew Thompson 	    (error != ENOTTY) &&
25802ac6454SAndrew Thompson 	    (iface->subdev != NULL) &&
25902ac6454SAndrew Thompson 	    device_is_attached(iface->subdev)) {
26002ac6454SAndrew Thompson #if 0
261090f65a6SAndrew Thompson 		DEVMETHOD(usb_handle_request, NULL);	/* dummy */
26202ac6454SAndrew Thompson #endif
26302ac6454SAndrew Thompson 		error = USB_HANDLE_REQUEST(iface->subdev,
26402ac6454SAndrew Thompson 		    &req, ppdata, plen,
26529bd7d7eSAndrew Thompson 		    off, &temp_state);
26602ac6454SAndrew Thompson 	}
267a593f6b8SAndrew Thompson 	iface_parent = usbd_get_iface(udev, iface->parent_iface_index);
26802ac6454SAndrew Thompson 
26902ac6454SAndrew Thompson 	if ((iface_parent == NULL) ||
27002ac6454SAndrew Thompson 	    (iface_parent->idesc == NULL)) {
27102ac6454SAndrew Thompson 		/* non-existing interface */
27202ac6454SAndrew Thompson 		iface_parent = NULL;
27302ac6454SAndrew Thompson 	}
27402ac6454SAndrew Thompson 	/* forward request to parent interface, if any */
27502ac6454SAndrew Thompson 
27602ac6454SAndrew Thompson 	if ((error != 0) &&
27702ac6454SAndrew Thompson 	    (error != ENOTTY) &&
27802ac6454SAndrew Thompson 	    (iface_parent != NULL) &&
27902ac6454SAndrew Thompson 	    (iface_parent->subdev != NULL) &&
28002ac6454SAndrew Thompson 	    ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
28102ac6454SAndrew Thompson 	    (iface_parent->subdev != iface->subdev) &&
28202ac6454SAndrew Thompson 	    device_is_attached(iface_parent->subdev)) {
28302ac6454SAndrew Thompson 		error = USB_HANDLE_REQUEST(iface_parent->subdev,
28429bd7d7eSAndrew Thompson 		    &req, ppdata, plen, off, &temp_state);
28502ac6454SAndrew Thompson 	}
28602ac6454SAndrew Thompson 	if (error == 0) {
28702ac6454SAndrew Thompson 		/* negativly adjust pointer and length */
28802ac6454SAndrew Thompson 		*ppdata = ((uint8_t *)(*ppdata)) - off;
28902ac6454SAndrew Thompson 		*plen += off;
29029bd7d7eSAndrew Thompson 
29129bd7d7eSAndrew Thompson 		if ((state == USB_HR_NOT_COMPLETE) &&
29229bd7d7eSAndrew Thompson 		    (temp_state == USB_HR_COMPLETE_OK))
29329bd7d7eSAndrew Thompson 			goto tr_short;
29429bd7d7eSAndrew Thompson 		else
29502ac6454SAndrew Thompson 			goto tr_valid;
29602ac6454SAndrew Thompson 	} else if (error == ENOTTY) {
29702ac6454SAndrew Thompson 		goto tr_stalled;
29802ac6454SAndrew Thompson 	}
29902ac6454SAndrew Thompson 	if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
30002ac6454SAndrew Thompson 		iface_index++;		/* iterate */
30102ac6454SAndrew Thompson 		goto tr_repeat;
30202ac6454SAndrew Thompson 	}
303a755d548SAndrew Thompson 	if (state != USB_HR_NOT_COMPLETE) {
30402ac6454SAndrew Thompson 		/* we are complete */
30502ac6454SAndrew Thompson 		goto tr_valid;
30602ac6454SAndrew Thompson 	}
30702ac6454SAndrew Thompson 	switch (req.bmRequestType) {
30802ac6454SAndrew Thompson 	case UT_WRITE_INTERFACE:
30902ac6454SAndrew Thompson 		switch (req.bRequest) {
31002ac6454SAndrew Thompson 		case UR_SET_INTERFACE:
31102ac6454SAndrew Thompson 			/*
312bd73b187SAlfred Perlstein 			 * We assume that the endpoints are the same
313bd73b187SAlfred Perlstein 			 * accross the alternate settings.
314bd73b187SAlfred Perlstein 			 *
315bd73b187SAlfred Perlstein 			 * Reset the endpoints, because re-attaching
316bd73b187SAlfred Perlstein 			 * only a part of the device is not possible.
31702ac6454SAndrew Thompson 			 */
318bd73b187SAlfred Perlstein 			error = usb_check_alt_setting(udev,
319bd73b187SAlfred Perlstein 			    iface, req.wValue[0]);
320bd73b187SAlfred Perlstein 			if (error) {
321bd73b187SAlfred Perlstein 				DPRINTF("alt setting does not exist %s\n",
322bd73b187SAlfred Perlstein 				    usbd_errstr(error));
323bd73b187SAlfred Perlstein 				goto tr_stalled;
324bd73b187SAlfred Perlstein 			}
325bd73b187SAlfred Perlstein 			error = usb_reset_iface_endpoints(udev, iface_index);
32602ac6454SAndrew Thompson 			if (error) {
32702ac6454SAndrew Thompson 				DPRINTF("alt setting failed %s\n",
328a593f6b8SAndrew Thompson 				    usbd_errstr(error));
32902ac6454SAndrew Thompson 				goto tr_stalled;
33002ac6454SAndrew Thompson 			}
331bd73b187SAlfred Perlstein 			/* update the current alternate setting */
332bd73b187SAlfred Perlstein 			iface->alt_index = req.wValue[0];
33302ac6454SAndrew Thompson 			break;
334bd73b187SAlfred Perlstein 
33502ac6454SAndrew Thompson 		default:
33602ac6454SAndrew Thompson 			goto tr_stalled;
33702ac6454SAndrew Thompson 		}
33802ac6454SAndrew Thompson 		break;
33902ac6454SAndrew Thompson 
34002ac6454SAndrew Thompson 	case UT_READ_INTERFACE:
34102ac6454SAndrew Thompson 		switch (req.bRequest) {
34202ac6454SAndrew Thompson 		case UR_GET_INTERFACE:
34302ac6454SAndrew Thompson 			*ppdata = &iface->alt_index;
34402ac6454SAndrew Thompson 			*plen = 1;
34502ac6454SAndrew Thompson 			break;
34602ac6454SAndrew Thompson 
34702ac6454SAndrew Thompson 		default:
34802ac6454SAndrew Thompson 			goto tr_stalled;
34902ac6454SAndrew Thompson 		}
35002ac6454SAndrew Thompson 		break;
35102ac6454SAndrew Thompson 	default:
35202ac6454SAndrew Thompson 		goto tr_stalled;
35302ac6454SAndrew Thompson 	}
35402ac6454SAndrew Thompson tr_valid:
355cb18f7d1SAlfred Perlstein 	usbd_enum_unlock(udev);
35602ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
35702ac6454SAndrew Thompson 	return (0);
35802ac6454SAndrew Thompson 
35929bd7d7eSAndrew Thompson tr_short:
360cb18f7d1SAlfred Perlstein 	usbd_enum_unlock(udev);
36129bd7d7eSAndrew Thompson 	USB_XFER_LOCK(xfer);
36229bd7d7eSAndrew Thompson 	return (USB_ERR_SHORT_XFER);
36329bd7d7eSAndrew Thompson 
36402ac6454SAndrew Thompson tr_stalled:
365cb18f7d1SAlfred Perlstein 	usbd_enum_unlock(udev);
36602ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
36702ac6454SAndrew Thompson 	return (USB_ERR_STALLED);
36802ac6454SAndrew Thompson }
36902ac6454SAndrew Thompson 
37002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
371a593f6b8SAndrew Thompson  *	usb_handle_stall
37202ac6454SAndrew Thompson  *
37302ac6454SAndrew Thompson  * Returns:
37402ac6454SAndrew Thompson  *    0: Success
37502ac6454SAndrew Thompson  * Else: Failure
37602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
377e0a69b51SAndrew Thompson static usb_error_t
378a593f6b8SAndrew Thompson usb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall)
37902ac6454SAndrew Thompson {
380760bc48eSAndrew Thompson 	struct usb_device *udev = xfer->xroot->udev;
381e0a69b51SAndrew Thompson 	usb_error_t err;
38202ac6454SAndrew Thompson 
38302ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
384a593f6b8SAndrew Thompson 	err = usbd_set_endpoint_stall(udev,
385a593f6b8SAndrew Thompson 	    usbd_get_ep_by_addr(udev, ep), do_stall);
38602ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
38702ac6454SAndrew Thompson 	return (err);
38802ac6454SAndrew Thompson }
38902ac6454SAndrew Thompson 
39002ac6454SAndrew Thompson /*------------------------------------------------------------------------*
391a593f6b8SAndrew Thompson  *	usb_handle_get_stall
39202ac6454SAndrew Thompson  *
39302ac6454SAndrew Thompson  * Returns:
39402ac6454SAndrew Thompson  *    0: Success
39502ac6454SAndrew Thompson  * Else: Failure
39602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
39702ac6454SAndrew Thompson static uint8_t
398a593f6b8SAndrew Thompson usb_handle_get_stall(struct usb_device *udev, uint8_t ea_val)
39902ac6454SAndrew Thompson {
400ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
40102ac6454SAndrew Thompson 	uint8_t halted;
40202ac6454SAndrew Thompson 
403a593f6b8SAndrew Thompson 	ep = usbd_get_ep_by_addr(udev, ea_val);
404ae60fdfbSAndrew Thompson 	if (ep == NULL) {
40502ac6454SAndrew Thompson 		/* nothing to do */
40602ac6454SAndrew Thompson 		return (0);
40702ac6454SAndrew Thompson 	}
40802ac6454SAndrew Thompson 	USB_BUS_LOCK(udev->bus);
409ae60fdfbSAndrew Thompson 	halted = ep->is_stalled;
41002ac6454SAndrew Thompson 	USB_BUS_UNLOCK(udev->bus);
41102ac6454SAndrew Thompson 
41202ac6454SAndrew Thompson 	return (halted);
41302ac6454SAndrew Thompson }
41402ac6454SAndrew Thompson 
41502ac6454SAndrew Thompson /*------------------------------------------------------------------------*
416a593f6b8SAndrew Thompson  *	usb_handle_remote_wakeup
41702ac6454SAndrew Thompson  *
41802ac6454SAndrew Thompson  * Returns:
41902ac6454SAndrew Thompson  *    0: Success
42002ac6454SAndrew Thompson  * Else: Failure
42102ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
422e0a69b51SAndrew Thompson static usb_error_t
423a593f6b8SAndrew Thompson usb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on)
42402ac6454SAndrew Thompson {
425760bc48eSAndrew Thompson 	struct usb_device *udev;
426760bc48eSAndrew Thompson 	struct usb_bus *bus;
42702ac6454SAndrew Thompson 
42802ac6454SAndrew Thompson 	udev = xfer->xroot->udev;
42902ac6454SAndrew Thompson 	bus = udev->bus;
43002ac6454SAndrew Thompson 
43102ac6454SAndrew Thompson 	USB_BUS_LOCK(bus);
43202ac6454SAndrew Thompson 
43302ac6454SAndrew Thompson 	if (is_on) {
43402ac6454SAndrew Thompson 		udev->flags.remote_wakeup = 1;
43502ac6454SAndrew Thompson 	} else {
43602ac6454SAndrew Thompson 		udev->flags.remote_wakeup = 0;
43702ac6454SAndrew Thompson 	}
43802ac6454SAndrew Thompson 
43902ac6454SAndrew Thompson 	USB_BUS_UNLOCK(bus);
44002ac6454SAndrew Thompson 
441*2ecb4e91SHans Petter Selasky #if USB_HAVE_POWERD
44202ac6454SAndrew Thompson 	/* In case we are out of sync, update the power state. */
443a593f6b8SAndrew Thompson 	usb_bus_power_update(udev->bus);
444*2ecb4e91SHans Petter Selasky #endif
44502ac6454SAndrew Thompson 	return (0);			/* success */
44602ac6454SAndrew Thompson }
44702ac6454SAndrew Thompson 
44802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
449a593f6b8SAndrew Thompson  *	usb_handle_request
45002ac6454SAndrew Thompson  *
45102ac6454SAndrew Thompson  * Internal state sequence:
45202ac6454SAndrew Thompson  *
453a755d548SAndrew Thompson  * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR
45402ac6454SAndrew Thompson  *
45502ac6454SAndrew Thompson  * Returns:
45602ac6454SAndrew Thompson  * 0: Ready to start hardware
45702ac6454SAndrew Thompson  * Else: Stall current transfer, if any
45802ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
459e0a69b51SAndrew Thompson static usb_error_t
460a593f6b8SAndrew Thompson usb_handle_request(struct usb_xfer *xfer)
46102ac6454SAndrew Thompson {
462760bc48eSAndrew Thompson 	struct usb_device_request req;
463760bc48eSAndrew Thompson 	struct usb_device *udev;
46402ac6454SAndrew Thompson 	const void *src_zcopy;		/* zero-copy source pointer */
46502ac6454SAndrew Thompson 	const void *src_mcopy;		/* non zero-copy source pointer */
46602ac6454SAndrew Thompson 	uint16_t off;			/* data offset */
46702ac6454SAndrew Thompson 	uint16_t rem;			/* data remainder */
46802ac6454SAndrew Thompson 	uint16_t max_len;		/* max fragment length */
46902ac6454SAndrew Thompson 	uint16_t wValue;
47002ac6454SAndrew Thompson 	uint16_t wIndex;
47102ac6454SAndrew Thompson 	uint8_t state;
47229bd7d7eSAndrew Thompson 	uint8_t is_complete = 1;
473e0a69b51SAndrew Thompson 	usb_error_t err;
47402ac6454SAndrew Thompson 	union {
47502ac6454SAndrew Thompson 		uWord	wStatus;
47602ac6454SAndrew Thompson 		uint8_t	buf[2];
47702ac6454SAndrew Thompson 	}     temp;
47802ac6454SAndrew Thompson 
47902ac6454SAndrew Thompson 	/*
48002ac6454SAndrew Thompson 	 * Filter the USB transfer state into
48102ac6454SAndrew Thompson 	 * something which we understand:
48202ac6454SAndrew Thompson 	 */
48302ac6454SAndrew Thompson 
48402ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
48502ac6454SAndrew Thompson 	case USB_ST_SETUP:
486a755d548SAndrew Thompson 		state = USB_HR_NOT_COMPLETE;
48702ac6454SAndrew Thompson 
48802ac6454SAndrew Thompson 		if (!xfer->flags_int.control_act) {
48902ac6454SAndrew Thompson 			/* nothing to do */
49002ac6454SAndrew Thompson 			goto tr_stalled;
49102ac6454SAndrew Thompson 		}
49202ac6454SAndrew Thompson 		break;
493a755d548SAndrew Thompson 	case USB_ST_TRANSFERRED:
49402ac6454SAndrew Thompson 		if (!xfer->flags_int.control_act) {
495a755d548SAndrew Thompson 			state = USB_HR_COMPLETE_OK;
49602ac6454SAndrew Thompson 		} else {
497a755d548SAndrew Thompson 			state = USB_HR_NOT_COMPLETE;
49802ac6454SAndrew Thompson 		}
49902ac6454SAndrew Thompson 		break;
500a755d548SAndrew Thompson 	default:
501a755d548SAndrew Thompson 		state = USB_HR_COMPLETE_ERR;
502a755d548SAndrew Thompson 		break;
50302ac6454SAndrew Thompson 	}
50402ac6454SAndrew Thompson 
50502ac6454SAndrew Thompson 	/* reset frame stuff */
50602ac6454SAndrew Thompson 
507ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_len(xfer, 0, 0);
50802ac6454SAndrew Thompson 
509ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_offset(xfer, 0, 0);
510ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_offset(xfer, sizeof(req), 1);
51102ac6454SAndrew Thompson 
51202ac6454SAndrew Thompson 	/* get the current request, if any */
51302ac6454SAndrew Thompson 
514a593f6b8SAndrew Thompson 	usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
51502ac6454SAndrew Thompson 
51602ac6454SAndrew Thompson 	if (xfer->flags_int.control_rem == 0xFFFF) {
51702ac6454SAndrew Thompson 		/* first time - not initialised */
51802ac6454SAndrew Thompson 		rem = UGETW(req.wLength);
51902ac6454SAndrew Thompson 		off = 0;
52002ac6454SAndrew Thompson 	} else {
52102ac6454SAndrew Thompson 		/* not first time - initialised */
52202ac6454SAndrew Thompson 		rem = xfer->flags_int.control_rem;
52302ac6454SAndrew Thompson 		off = UGETW(req.wLength) - rem;
52402ac6454SAndrew Thompson 	}
52502ac6454SAndrew Thompson 
52602ac6454SAndrew Thompson 	/* set some defaults */
52702ac6454SAndrew Thompson 
52802ac6454SAndrew Thompson 	max_len = 0;
52902ac6454SAndrew Thompson 	src_zcopy = NULL;
53002ac6454SAndrew Thompson 	src_mcopy = NULL;
53102ac6454SAndrew Thompson 	udev = xfer->xroot->udev;
53202ac6454SAndrew Thompson 
53302ac6454SAndrew Thompson 	/* get some request fields decoded */
53402ac6454SAndrew Thompson 
53502ac6454SAndrew Thompson 	wValue = UGETW(req.wValue);
53602ac6454SAndrew Thompson 	wIndex = UGETW(req.wIndex);
53702ac6454SAndrew Thompson 
53802ac6454SAndrew Thompson 	DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
53902ac6454SAndrew Thompson 	    "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
54002ac6454SAndrew Thompson 	    req.bRequest, wValue, wIndex, off, rem, state);
54102ac6454SAndrew Thompson 
54202ac6454SAndrew Thompson 	/* demultiplex the control request */
54302ac6454SAndrew Thompson 
54402ac6454SAndrew Thompson 	switch (req.bmRequestType) {
54502ac6454SAndrew Thompson 	case UT_READ_DEVICE:
546a755d548SAndrew Thompson 		if (state != USB_HR_NOT_COMPLETE) {
54702ac6454SAndrew Thompson 			break;
54802ac6454SAndrew Thompson 		}
54902ac6454SAndrew Thompson 		switch (req.bRequest) {
55002ac6454SAndrew Thompson 		case UR_GET_DESCRIPTOR:
55102ac6454SAndrew Thompson 			goto tr_handle_get_descriptor;
55202ac6454SAndrew Thompson 		case UR_GET_CONFIG:
55302ac6454SAndrew Thompson 			goto tr_handle_get_config;
55402ac6454SAndrew Thompson 		case UR_GET_STATUS:
55502ac6454SAndrew Thompson 			goto tr_handle_get_status;
55602ac6454SAndrew Thompson 		default:
55702ac6454SAndrew Thompson 			goto tr_stalled;
55802ac6454SAndrew Thompson 		}
55902ac6454SAndrew Thompson 		break;
56002ac6454SAndrew Thompson 
56102ac6454SAndrew Thompson 	case UT_WRITE_DEVICE:
56202ac6454SAndrew Thompson 		switch (req.bRequest) {
56302ac6454SAndrew Thompson 		case UR_SET_ADDRESS:
56402ac6454SAndrew Thompson 			goto tr_handle_set_address;
56502ac6454SAndrew Thompson 		case UR_SET_CONFIG:
56602ac6454SAndrew Thompson 			goto tr_handle_set_config;
56702ac6454SAndrew Thompson 		case UR_CLEAR_FEATURE:
56802ac6454SAndrew Thompson 			switch (wValue) {
56902ac6454SAndrew Thompson 			case UF_DEVICE_REMOTE_WAKEUP:
57002ac6454SAndrew Thompson 				goto tr_handle_clear_wakeup;
57102ac6454SAndrew Thompson 			default:
57202ac6454SAndrew Thompson 				goto tr_stalled;
57302ac6454SAndrew Thompson 			}
57402ac6454SAndrew Thompson 			break;
57502ac6454SAndrew Thompson 		case UR_SET_FEATURE:
57602ac6454SAndrew Thompson 			switch (wValue) {
57702ac6454SAndrew Thompson 			case UF_DEVICE_REMOTE_WAKEUP:
57802ac6454SAndrew Thompson 				goto tr_handle_set_wakeup;
57902ac6454SAndrew Thompson 			default:
58002ac6454SAndrew Thompson 				goto tr_stalled;
58102ac6454SAndrew Thompson 			}
58202ac6454SAndrew Thompson 			break;
58302ac6454SAndrew Thompson 		default:
58402ac6454SAndrew Thompson 			goto tr_stalled;
58502ac6454SAndrew Thompson 		}
58602ac6454SAndrew Thompson 		break;
58702ac6454SAndrew Thompson 
58802ac6454SAndrew Thompson 	case UT_WRITE_ENDPOINT:
58902ac6454SAndrew Thompson 		switch (req.bRequest) {
59002ac6454SAndrew Thompson 		case UR_CLEAR_FEATURE:
59102ac6454SAndrew Thompson 			switch (wValue) {
59202ac6454SAndrew Thompson 			case UF_ENDPOINT_HALT:
59302ac6454SAndrew Thompson 				goto tr_handle_clear_halt;
59402ac6454SAndrew Thompson 			default:
59502ac6454SAndrew Thompson 				goto tr_stalled;
59602ac6454SAndrew Thompson 			}
59702ac6454SAndrew Thompson 			break;
59802ac6454SAndrew Thompson 		case UR_SET_FEATURE:
59902ac6454SAndrew Thompson 			switch (wValue) {
60002ac6454SAndrew Thompson 			case UF_ENDPOINT_HALT:
60102ac6454SAndrew Thompson 				goto tr_handle_set_halt;
60202ac6454SAndrew Thompson 			default:
60302ac6454SAndrew Thompson 				goto tr_stalled;
60402ac6454SAndrew Thompson 			}
60502ac6454SAndrew Thompson 			break;
60602ac6454SAndrew Thompson 		default:
60702ac6454SAndrew Thompson 			goto tr_stalled;
60802ac6454SAndrew Thompson 		}
60902ac6454SAndrew Thompson 		break;
61002ac6454SAndrew Thompson 
61102ac6454SAndrew Thompson 	case UT_READ_ENDPOINT:
61202ac6454SAndrew Thompson 		switch (req.bRequest) {
61302ac6454SAndrew Thompson 		case UR_GET_STATUS:
61402ac6454SAndrew Thompson 			goto tr_handle_get_ep_status;
61502ac6454SAndrew Thompson 		default:
61602ac6454SAndrew Thompson 			goto tr_stalled;
61702ac6454SAndrew Thompson 		}
61802ac6454SAndrew Thompson 		break;
61902ac6454SAndrew Thompson 	default:
62002ac6454SAndrew Thompson 		/* we use "USB_ADD_BYTES" to de-const the src_zcopy */
621a593f6b8SAndrew Thompson 		err = usb_handle_iface_request(xfer,
62202ac6454SAndrew Thompson 		    USB_ADD_BYTES(&src_zcopy, 0),
62302ac6454SAndrew Thompson 		    &max_len, req, off, state);
62402ac6454SAndrew Thompson 		if (err == 0) {
62529bd7d7eSAndrew Thompson 			is_complete = 0;
62629bd7d7eSAndrew Thompson 			goto tr_valid;
62729bd7d7eSAndrew Thompson 		} else if (err == USB_ERR_SHORT_XFER) {
62802ac6454SAndrew Thompson 			goto tr_valid;
62902ac6454SAndrew Thompson 		}
63002ac6454SAndrew Thompson 		/*
63102ac6454SAndrew Thompson 		 * Reset zero-copy pointer and max length
63202ac6454SAndrew Thompson 		 * variable in case they were unintentionally
63302ac6454SAndrew Thompson 		 * set:
63402ac6454SAndrew Thompson 		 */
63502ac6454SAndrew Thompson 		src_zcopy = NULL;
63602ac6454SAndrew Thompson 		max_len = 0;
63702ac6454SAndrew Thompson 
63802ac6454SAndrew Thompson 		/*
63902ac6454SAndrew Thompson 		 * Check if we have a vendor specific
64002ac6454SAndrew Thompson 		 * descriptor:
64102ac6454SAndrew Thompson 		 */
64202ac6454SAndrew Thompson 		goto tr_handle_get_descriptor;
64302ac6454SAndrew Thompson 	}
64402ac6454SAndrew Thompson 	goto tr_valid;
64502ac6454SAndrew Thompson 
64602ac6454SAndrew Thompson tr_handle_get_descriptor:
647a593f6b8SAndrew Thompson 	err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
648459d369eSAndrew Thompson 	if (err)
64902ac6454SAndrew Thompson 		goto tr_stalled;
650459d369eSAndrew Thompson 	if (src_zcopy == NULL)
651459d369eSAndrew Thompson 		goto tr_stalled;
65202ac6454SAndrew Thompson 	goto tr_valid;
65302ac6454SAndrew Thompson 
65402ac6454SAndrew Thompson tr_handle_get_config:
65502ac6454SAndrew Thompson 	temp.buf[0] = udev->curr_config_no;
65602ac6454SAndrew Thompson 	src_mcopy = temp.buf;
65702ac6454SAndrew Thompson 	max_len = 1;
65802ac6454SAndrew Thompson 	goto tr_valid;
65902ac6454SAndrew Thompson 
66002ac6454SAndrew Thompson tr_handle_get_status:
66102ac6454SAndrew Thompson 
66202ac6454SAndrew Thompson 	wValue = 0;
66302ac6454SAndrew Thompson 
66402ac6454SAndrew Thompson 	USB_BUS_LOCK(udev->bus);
66502ac6454SAndrew Thompson 	if (udev->flags.remote_wakeup) {
66602ac6454SAndrew Thompson 		wValue |= UDS_REMOTE_WAKEUP;
66702ac6454SAndrew Thompson 	}
66802ac6454SAndrew Thompson 	if (udev->flags.self_powered) {
66902ac6454SAndrew Thompson 		wValue |= UDS_SELF_POWERED;
67002ac6454SAndrew Thompson 	}
67102ac6454SAndrew Thompson 	USB_BUS_UNLOCK(udev->bus);
67202ac6454SAndrew Thompson 
67302ac6454SAndrew Thompson 	USETW(temp.wStatus, wValue);
67402ac6454SAndrew Thompson 	src_mcopy = temp.wStatus;
67502ac6454SAndrew Thompson 	max_len = sizeof(temp.wStatus);
67602ac6454SAndrew Thompson 	goto tr_valid;
67702ac6454SAndrew Thompson 
67802ac6454SAndrew Thompson tr_handle_set_address:
679a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
68002ac6454SAndrew Thompson 		if (wValue >= 0x80) {
68102ac6454SAndrew Thompson 			/* invalid value */
68202ac6454SAndrew Thompson 			goto tr_stalled;
68302ac6454SAndrew Thompson 		} else if (udev->curr_config_no != 0) {
68402ac6454SAndrew Thompson 			/* we are configured ! */
68502ac6454SAndrew Thompson 			goto tr_stalled;
68602ac6454SAndrew Thompson 		}
687a755d548SAndrew Thompson 	} else if (state != USB_HR_NOT_COMPLETE) {
68802ac6454SAndrew Thompson 		udev->address = (wValue & 0x7F);
68902ac6454SAndrew Thompson 		goto tr_bad_context;
69002ac6454SAndrew Thompson 	}
69102ac6454SAndrew Thompson 	goto tr_valid;
69202ac6454SAndrew Thompson 
69302ac6454SAndrew Thompson tr_handle_set_config:
694a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
695a593f6b8SAndrew Thompson 		if (usb_handle_set_config(xfer, req.wValue[0])) {
69602ac6454SAndrew Thompson 			goto tr_stalled;
69702ac6454SAndrew Thompson 		}
69802ac6454SAndrew Thompson 	}
69902ac6454SAndrew Thompson 	goto tr_valid;
70002ac6454SAndrew Thompson 
70102ac6454SAndrew Thompson tr_handle_clear_halt:
702a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
703a593f6b8SAndrew Thompson 		if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) {
70402ac6454SAndrew Thompson 			goto tr_stalled;
70502ac6454SAndrew Thompson 		}
70602ac6454SAndrew Thompson 	}
70702ac6454SAndrew Thompson 	goto tr_valid;
70802ac6454SAndrew Thompson 
70902ac6454SAndrew Thompson tr_handle_clear_wakeup:
710a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
711a593f6b8SAndrew Thompson 		if (usb_handle_remote_wakeup(xfer, 0)) {
71202ac6454SAndrew Thompson 			goto tr_stalled;
71302ac6454SAndrew Thompson 		}
71402ac6454SAndrew Thompson 	}
71502ac6454SAndrew Thompson 	goto tr_valid;
71602ac6454SAndrew Thompson 
71702ac6454SAndrew Thompson tr_handle_set_halt:
718a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
719a593f6b8SAndrew Thompson 		if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) {
72002ac6454SAndrew Thompson 			goto tr_stalled;
72102ac6454SAndrew Thompson 		}
72202ac6454SAndrew Thompson 	}
72302ac6454SAndrew Thompson 	goto tr_valid;
72402ac6454SAndrew Thompson 
72502ac6454SAndrew Thompson tr_handle_set_wakeup:
726a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
727a593f6b8SAndrew Thompson 		if (usb_handle_remote_wakeup(xfer, 1)) {
72802ac6454SAndrew Thompson 			goto tr_stalled;
72902ac6454SAndrew Thompson 		}
73002ac6454SAndrew Thompson 	}
73102ac6454SAndrew Thompson 	goto tr_valid;
73202ac6454SAndrew Thompson 
73302ac6454SAndrew Thompson tr_handle_get_ep_status:
734a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
73502ac6454SAndrew Thompson 		temp.wStatus[0] =
736a593f6b8SAndrew Thompson 		    usb_handle_get_stall(udev, req.wIndex[0]);
73702ac6454SAndrew Thompson 		temp.wStatus[1] = 0;
73802ac6454SAndrew Thompson 		src_mcopy = temp.wStatus;
73902ac6454SAndrew Thompson 		max_len = sizeof(temp.wStatus);
74002ac6454SAndrew Thompson 	}
74102ac6454SAndrew Thompson 	goto tr_valid;
74202ac6454SAndrew Thompson 
74302ac6454SAndrew Thompson tr_valid:
744a755d548SAndrew Thompson 	if (state != USB_HR_NOT_COMPLETE) {
74502ac6454SAndrew Thompson 		goto tr_stalled;
74602ac6454SAndrew Thompson 	}
74702ac6454SAndrew Thompson 	/* subtract offset from length */
74802ac6454SAndrew Thompson 
74902ac6454SAndrew Thompson 	max_len -= off;
75002ac6454SAndrew Thompson 
75102ac6454SAndrew Thompson 	/* Compute the real maximum data length */
75202ac6454SAndrew Thompson 
75302ac6454SAndrew Thompson 	if (max_len > xfer->max_data_length) {
754ed6d949aSAndrew Thompson 		max_len = usbd_xfer_max_len(xfer);
75502ac6454SAndrew Thompson 	}
75602ac6454SAndrew Thompson 	if (max_len > rem) {
75702ac6454SAndrew Thompson 		max_len = rem;
75802ac6454SAndrew Thompson 	}
75902ac6454SAndrew Thompson 	/*
76002ac6454SAndrew Thompson 	 * If the remainder is greater than the maximum data length,
76102ac6454SAndrew Thompson 	 * we need to truncate the value for the sake of the
76202ac6454SAndrew Thompson 	 * comparison below:
76302ac6454SAndrew Thompson 	 */
76402ac6454SAndrew Thompson 	if (rem > xfer->max_data_length) {
765ed6d949aSAndrew Thompson 		rem = usbd_xfer_max_len(xfer);
76602ac6454SAndrew Thompson 	}
76729bd7d7eSAndrew Thompson 	if ((rem != max_len) && (is_complete != 0)) {
76802ac6454SAndrew Thompson 		/*
76902ac6454SAndrew Thompson 	         * If we don't transfer the data we can transfer, then
77002ac6454SAndrew Thompson 	         * the transfer is short !
77102ac6454SAndrew Thompson 	         */
77202ac6454SAndrew Thompson 		xfer->flags.force_short_xfer = 1;
77302ac6454SAndrew Thompson 		xfer->nframes = 2;
77402ac6454SAndrew Thompson 	} else {
77502ac6454SAndrew Thompson 		/*
77602ac6454SAndrew Thompson 		 * Default case
77702ac6454SAndrew Thompson 		 */
77802ac6454SAndrew Thompson 		xfer->flags.force_short_xfer = 0;
77902ac6454SAndrew Thompson 		xfer->nframes = max_len ? 2 : 1;
78002ac6454SAndrew Thompson 	}
78102ac6454SAndrew Thompson 	if (max_len > 0) {
78202ac6454SAndrew Thompson 		if (src_mcopy) {
78302ac6454SAndrew Thompson 			src_mcopy = USB_ADD_BYTES(src_mcopy, off);
784a593f6b8SAndrew Thompson 			usbd_copy_in(xfer->frbuffers + 1, 0,
78502ac6454SAndrew Thompson 			    src_mcopy, max_len);
786ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 1, max_len);
78702ac6454SAndrew Thompson 		} else {
788ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_data(xfer, 1,
789ed6d949aSAndrew Thompson 			    USB_ADD_BYTES(src_zcopy, off), max_len);
79002ac6454SAndrew Thompson 		}
79102ac6454SAndrew Thompson 	} else {
79202ac6454SAndrew Thompson 		/* the end is reached, send status */
79302ac6454SAndrew Thompson 		xfer->flags.manual_status = 0;
794ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 1, 0);
79502ac6454SAndrew Thompson 	}
79602ac6454SAndrew Thompson 	DPRINTF("success\n");
79702ac6454SAndrew Thompson 	return (0);			/* success */
79802ac6454SAndrew Thompson 
79902ac6454SAndrew Thompson tr_stalled:
800a755d548SAndrew Thompson 	DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ?
80102ac6454SAndrew Thompson 	    "complete" : "stalled");
80202ac6454SAndrew Thompson 	return (USB_ERR_STALLED);
80302ac6454SAndrew Thompson 
80402ac6454SAndrew Thompson tr_bad_context:
80502ac6454SAndrew Thompson 	DPRINTF("bad context\n");
80602ac6454SAndrew Thompson 	return (USB_ERR_BAD_CONTEXT);
80702ac6454SAndrew Thompson }
808