xref: /freebsd/sys/dev/usb/usb_handle_request.c (revision 6950c75f401152bfb9b7a0603241434b51cba482)
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 
27d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE
28d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE
29d2b99310SHans Petter Selasky #else
30ed6d949aSAndrew Thompson #include <sys/stdint.h>
31ed6d949aSAndrew Thompson #include <sys/stddef.h>
32ed6d949aSAndrew Thompson #include <sys/param.h>
33ed6d949aSAndrew Thompson #include <sys/queue.h>
34ed6d949aSAndrew Thompson #include <sys/types.h>
35ed6d949aSAndrew Thompson #include <sys/systm.h>
36ed6d949aSAndrew Thompson #include <sys/kernel.h>
37ed6d949aSAndrew Thompson #include <sys/bus.h>
38ed6d949aSAndrew Thompson #include <sys/module.h>
39ed6d949aSAndrew Thompson #include <sys/lock.h>
40ed6d949aSAndrew Thompson #include <sys/mutex.h>
41ed6d949aSAndrew Thompson #include <sys/condvar.h>
42ed6d949aSAndrew Thompson #include <sys/sysctl.h>
43ed6d949aSAndrew Thompson #include <sys/sx.h>
44ed6d949aSAndrew Thompson #include <sys/unistd.h>
45ed6d949aSAndrew Thompson #include <sys/callout.h>
46ed6d949aSAndrew Thompson #include <sys/malloc.h>
47ed6d949aSAndrew Thompson #include <sys/priv.h>
48ed6d949aSAndrew Thompson 
4902ac6454SAndrew Thompson #include <dev/usb/usb.h>
50ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h>
51bd73b187SAlfred Perlstein #include <dev/usb/usbdi_util.h>
52ed6d949aSAndrew Thompson #include "usb_if.h"
5302ac6454SAndrew Thompson 
54a593f6b8SAndrew Thompson #define	USB_DEBUG_VAR usb_debug
5502ac6454SAndrew Thompson 
5602ac6454SAndrew Thompson #include <dev/usb/usb_core.h>
5702ac6454SAndrew Thompson #include <dev/usb/usb_process.h>
5802ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h>
5902ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h>
6002ac6454SAndrew Thompson #include <dev/usb/usb_device.h>
6102ac6454SAndrew Thompson #include <dev/usb/usb_debug.h>
6202ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h>
6302ac6454SAndrew Thompson #include <dev/usb/usb_hub.h>
6402ac6454SAndrew Thompson 
6502ac6454SAndrew Thompson #include <dev/usb/usb_controller.h>
6602ac6454SAndrew Thompson #include <dev/usb/usb_bus.h>
67d2b99310SHans Petter Selasky #endif			/* USB_GLOBAL_INCLUDE_FILE */
6802ac6454SAndrew Thompson 
6902ac6454SAndrew Thompson /* function prototypes */
7002ac6454SAndrew Thompson 
71a593f6b8SAndrew Thompson static uint8_t usb_handle_get_stall(struct usb_device *, uint8_t);
72a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_remote_wakeup(struct usb_xfer *, uint8_t);
73a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_request(struct usb_xfer *);
74a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_set_config(struct usb_xfer *, uint8_t);
75a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_set_stall(struct usb_xfer *, uint8_t,
7602ac6454SAndrew Thompson 			    uint8_t);
77a593f6b8SAndrew Thompson static usb_error_t	 usb_handle_iface_request(struct usb_xfer *, void **,
78760bc48eSAndrew Thompson 			    uint16_t *, struct usb_device_request, uint16_t,
7902ac6454SAndrew Thompson 			    uint8_t);
8002ac6454SAndrew Thompson 
8102ac6454SAndrew Thompson /*------------------------------------------------------------------------*
82a593f6b8SAndrew Thompson  *	usb_handle_request_callback
8302ac6454SAndrew Thompson  *
8402ac6454SAndrew Thompson  * This function is the USB callback for generic USB Device control
8502ac6454SAndrew Thompson  * transfers.
8602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
8702ac6454SAndrew Thompson void
88ed6d949aSAndrew Thompson usb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error)
8902ac6454SAndrew Thompson {
90e0a69b51SAndrew Thompson 	usb_error_t err;
9102ac6454SAndrew Thompson 
9202ac6454SAndrew Thompson 	/* check the current transfer state */
9302ac6454SAndrew Thompson 
9402ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
9502ac6454SAndrew Thompson 	case USB_ST_SETUP:
9602ac6454SAndrew Thompson 	case USB_ST_TRANSFERRED:
9702ac6454SAndrew Thompson 
9802ac6454SAndrew Thompson 		/* handle the request */
99a593f6b8SAndrew Thompson 		err = usb_handle_request(xfer);
10002ac6454SAndrew Thompson 
10102ac6454SAndrew Thompson 		if (err) {
10202ac6454SAndrew Thompson 
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
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;
15202ac6454SAndrew Thompson 
15302ac6454SAndrew Thompson 	/*
15402ac6454SAndrew Thompson 	 * We need to protect against other threads doing probe and
15502ac6454SAndrew Thompson 	 * attach:
15602ac6454SAndrew Thompson 	 */
15702ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
158cb18f7d1SAlfred Perlstein 
159cb18f7d1SAlfred Perlstein 	usbd_enum_lock(udev);
16002ac6454SAndrew Thompson 
16102ac6454SAndrew Thompson 	if (conf_no == USB_UNCONFIG_NO) {
16202ac6454SAndrew Thompson 		conf_no = USB_UNCONFIG_INDEX;
16302ac6454SAndrew Thompson 	} else {
16402ac6454SAndrew Thompson 		/*
16502ac6454SAndrew Thompson 		 * The relationship between config number and config index
16602ac6454SAndrew Thompson 		 * is very simple in our case:
16702ac6454SAndrew Thompson 		 */
16802ac6454SAndrew Thompson 		conf_no--;
16902ac6454SAndrew Thompson 	}
17002ac6454SAndrew Thompson 
171a593f6b8SAndrew Thompson 	if (usbd_set_config_index(udev, conf_no)) {
17202ac6454SAndrew Thompson 		DPRINTF("set config %d failed\n", conf_no);
17302ac6454SAndrew Thompson 		err = USB_ERR_STALLED;
17402ac6454SAndrew Thompson 		goto done;
17502ac6454SAndrew Thompson 	}
176a593f6b8SAndrew Thompson 	if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
17702ac6454SAndrew Thompson 		DPRINTF("probe and attach failed\n");
17802ac6454SAndrew Thompson 		err = USB_ERR_STALLED;
17902ac6454SAndrew Thompson 		goto done;
18002ac6454SAndrew Thompson 	}
18102ac6454SAndrew Thompson done:
182cb18f7d1SAlfred Perlstein 	usbd_enum_unlock(udev);
18302ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
18402ac6454SAndrew Thompson 	return (err);
18502ac6454SAndrew Thompson }
18602ac6454SAndrew Thompson 
187bd73b187SAlfred Perlstein static usb_error_t
188bd73b187SAlfred Perlstein usb_check_alt_setting(struct usb_device *udev,
189bd73b187SAlfred Perlstein      struct usb_interface *iface, uint8_t alt_index)
190bd73b187SAlfred Perlstein {
191bd73b187SAlfred Perlstein 	uint8_t do_unlock;
192bd73b187SAlfred Perlstein 	usb_error_t err = 0;
193bd73b187SAlfred Perlstein 
194*6950c75fSHans Petter Selasky 	/* Prevent re-enumeration */
195*6950c75fSHans Petter Selasky 	do_unlock = usbd_enum_lock(udev);
196bd73b187SAlfred Perlstein 
197bd73b187SAlfred Perlstein 	if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc))
198bd73b187SAlfred Perlstein 		err = USB_ERR_INVAL;
199bd73b187SAlfred Perlstein 
200cb18f7d1SAlfred Perlstein 	if (do_unlock)
201cb18f7d1SAlfred Perlstein 		usbd_enum_unlock(udev);
202cb18f7d1SAlfred Perlstein 
203bd73b187SAlfred Perlstein 	return (err);
204bd73b187SAlfred Perlstein }
205bd73b187SAlfred Perlstein 
20602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
207a593f6b8SAndrew Thompson  *	usb_handle_iface_request
20802ac6454SAndrew Thompson  *
20902ac6454SAndrew Thompson  * Returns:
21002ac6454SAndrew Thompson  *    0: Success
21102ac6454SAndrew Thompson  * Else: Failure
21202ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
213e0a69b51SAndrew Thompson static usb_error_t
214a593f6b8SAndrew Thompson usb_handle_iface_request(struct usb_xfer *xfer,
21502ac6454SAndrew Thompson     void **ppdata, uint16_t *plen,
216760bc48eSAndrew Thompson     struct usb_device_request req, uint16_t off, uint8_t state)
21702ac6454SAndrew Thompson {
218760bc48eSAndrew Thompson 	struct usb_interface *iface;
219760bc48eSAndrew Thompson 	struct usb_interface *iface_parent;	/* parent interface */
220760bc48eSAndrew Thompson 	struct usb_device *udev = xfer->xroot->udev;
22102ac6454SAndrew Thompson 	int error;
22202ac6454SAndrew Thompson 	uint8_t iface_index;
22329bd7d7eSAndrew Thompson 	uint8_t temp_state;
22402ac6454SAndrew Thompson 
22502ac6454SAndrew Thompson 	if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
22602ac6454SAndrew Thompson 		iface_index = req.wIndex[0];	/* unicast */
22702ac6454SAndrew Thompson 	} else {
22802ac6454SAndrew Thompson 		iface_index = 0;	/* broadcast */
22902ac6454SAndrew Thompson 	}
23002ac6454SAndrew Thompson 
23102ac6454SAndrew Thompson 	/*
23202ac6454SAndrew Thompson 	 * We need to protect against other threads doing probe and
23302ac6454SAndrew Thompson 	 * attach:
23402ac6454SAndrew Thompson 	 */
23502ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
236cb18f7d1SAlfred Perlstein 
237cb18f7d1SAlfred Perlstein 	usbd_enum_lock(udev);
23802ac6454SAndrew Thompson 
23902ac6454SAndrew Thompson 	error = ENXIO;
24002ac6454SAndrew Thompson 
24102ac6454SAndrew Thompson tr_repeat:
242a593f6b8SAndrew Thompson 	iface = usbd_get_iface(udev, iface_index);
24302ac6454SAndrew Thompson 	if ((iface == NULL) ||
24402ac6454SAndrew Thompson 	    (iface->idesc == NULL)) {
24502ac6454SAndrew Thompson 		/* end of interfaces non-existing interface */
24602ac6454SAndrew Thompson 		goto tr_stalled;
24702ac6454SAndrew Thompson 	}
24829bd7d7eSAndrew Thompson 	/* set initial state */
24929bd7d7eSAndrew Thompson 
25029bd7d7eSAndrew Thompson 	temp_state = state;
25129bd7d7eSAndrew Thompson 
25202ac6454SAndrew Thompson 	/* forward request to interface, if any */
25302ac6454SAndrew Thompson 
25402ac6454SAndrew Thompson 	if ((error != 0) &&
25502ac6454SAndrew Thompson 	    (error != ENOTTY) &&
25602ac6454SAndrew Thompson 	    (iface->subdev != NULL) &&
25702ac6454SAndrew Thompson 	    device_is_attached(iface->subdev)) {
25802ac6454SAndrew Thompson #if 0
259090f65a6SAndrew Thompson 		DEVMETHOD(usb_handle_request, NULL);	/* dummy */
26002ac6454SAndrew Thompson #endif
26102ac6454SAndrew Thompson 		error = USB_HANDLE_REQUEST(iface->subdev,
26202ac6454SAndrew Thompson 		    &req, ppdata, plen,
26329bd7d7eSAndrew Thompson 		    off, &temp_state);
26402ac6454SAndrew Thompson 	}
265a593f6b8SAndrew Thompson 	iface_parent = usbd_get_iface(udev, iface->parent_iface_index);
26602ac6454SAndrew Thompson 
26702ac6454SAndrew Thompson 	if ((iface_parent == NULL) ||
26802ac6454SAndrew Thompson 	    (iface_parent->idesc == NULL)) {
26902ac6454SAndrew Thompson 		/* non-existing interface */
27002ac6454SAndrew Thompson 		iface_parent = NULL;
27102ac6454SAndrew Thompson 	}
27202ac6454SAndrew Thompson 	/* forward request to parent interface, if any */
27302ac6454SAndrew Thompson 
27402ac6454SAndrew Thompson 	if ((error != 0) &&
27502ac6454SAndrew Thompson 	    (error != ENOTTY) &&
27602ac6454SAndrew Thompson 	    (iface_parent != NULL) &&
27702ac6454SAndrew Thompson 	    (iface_parent->subdev != NULL) &&
27802ac6454SAndrew Thompson 	    ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
27902ac6454SAndrew Thompson 	    (iface_parent->subdev != iface->subdev) &&
28002ac6454SAndrew Thompson 	    device_is_attached(iface_parent->subdev)) {
28102ac6454SAndrew Thompson 		error = USB_HANDLE_REQUEST(iface_parent->subdev,
28229bd7d7eSAndrew Thompson 		    &req, ppdata, plen, off, &temp_state);
28302ac6454SAndrew Thompson 	}
28402ac6454SAndrew Thompson 	if (error == 0) {
28502ac6454SAndrew Thompson 		/* negativly adjust pointer and length */
28602ac6454SAndrew Thompson 		*ppdata = ((uint8_t *)(*ppdata)) - off;
28702ac6454SAndrew Thompson 		*plen += off;
28829bd7d7eSAndrew Thompson 
28929bd7d7eSAndrew Thompson 		if ((state == USB_HR_NOT_COMPLETE) &&
29029bd7d7eSAndrew Thompson 		    (temp_state == USB_HR_COMPLETE_OK))
29129bd7d7eSAndrew Thompson 			goto tr_short;
29229bd7d7eSAndrew Thompson 		else
29302ac6454SAndrew Thompson 			goto tr_valid;
29402ac6454SAndrew Thompson 	} else if (error == ENOTTY) {
29502ac6454SAndrew Thompson 		goto tr_stalled;
29602ac6454SAndrew Thompson 	}
29702ac6454SAndrew Thompson 	if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
29802ac6454SAndrew Thompson 		iface_index++;		/* iterate */
29902ac6454SAndrew Thompson 		goto tr_repeat;
30002ac6454SAndrew Thompson 	}
301a755d548SAndrew Thompson 	if (state != USB_HR_NOT_COMPLETE) {
30202ac6454SAndrew Thompson 		/* we are complete */
30302ac6454SAndrew Thompson 		goto tr_valid;
30402ac6454SAndrew Thompson 	}
30502ac6454SAndrew Thompson 	switch (req.bmRequestType) {
30602ac6454SAndrew Thompson 	case UT_WRITE_INTERFACE:
30702ac6454SAndrew Thompson 		switch (req.bRequest) {
30802ac6454SAndrew Thompson 		case UR_SET_INTERFACE:
30902ac6454SAndrew Thompson 			/*
310bd73b187SAlfred Perlstein 			 * We assume that the endpoints are the same
311bd73b187SAlfred Perlstein 			 * accross the alternate settings.
312bd73b187SAlfred Perlstein 			 *
313bd73b187SAlfred Perlstein 			 * Reset the endpoints, because re-attaching
314bd73b187SAlfred Perlstein 			 * only a part of the device is not possible.
31502ac6454SAndrew Thompson 			 */
316bd73b187SAlfred Perlstein 			error = usb_check_alt_setting(udev,
317bd73b187SAlfred Perlstein 			    iface, req.wValue[0]);
318bd73b187SAlfred Perlstein 			if (error) {
319bd73b187SAlfred Perlstein 				DPRINTF("alt setting does not exist %s\n",
320bd73b187SAlfred Perlstein 				    usbd_errstr(error));
321bd73b187SAlfred Perlstein 				goto tr_stalled;
322bd73b187SAlfred Perlstein 			}
323bd73b187SAlfred Perlstein 			error = usb_reset_iface_endpoints(udev, iface_index);
32402ac6454SAndrew Thompson 			if (error) {
32502ac6454SAndrew Thompson 				DPRINTF("alt setting failed %s\n",
326a593f6b8SAndrew Thompson 				    usbd_errstr(error));
32702ac6454SAndrew Thompson 				goto tr_stalled;
32802ac6454SAndrew Thompson 			}
329bd73b187SAlfred Perlstein 			/* update the current alternate setting */
330bd73b187SAlfred Perlstein 			iface->alt_index = req.wValue[0];
33102ac6454SAndrew Thompson 			break;
332bd73b187SAlfred Perlstein 
33302ac6454SAndrew Thompson 		default:
33402ac6454SAndrew Thompson 			goto tr_stalled;
33502ac6454SAndrew Thompson 		}
33602ac6454SAndrew Thompson 		break;
33702ac6454SAndrew Thompson 
33802ac6454SAndrew Thompson 	case UT_READ_INTERFACE:
33902ac6454SAndrew Thompson 		switch (req.bRequest) {
34002ac6454SAndrew Thompson 		case UR_GET_INTERFACE:
34102ac6454SAndrew Thompson 			*ppdata = &iface->alt_index;
34202ac6454SAndrew Thompson 			*plen = 1;
34302ac6454SAndrew Thompson 			break;
34402ac6454SAndrew Thompson 
34502ac6454SAndrew Thompson 		default:
34602ac6454SAndrew Thompson 			goto tr_stalled;
34702ac6454SAndrew Thompson 		}
34802ac6454SAndrew Thompson 		break;
34902ac6454SAndrew Thompson 	default:
35002ac6454SAndrew Thompson 		goto tr_stalled;
35102ac6454SAndrew Thompson 	}
35202ac6454SAndrew Thompson tr_valid:
353cb18f7d1SAlfred Perlstein 	usbd_enum_unlock(udev);
35402ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
35502ac6454SAndrew Thompson 	return (0);
35602ac6454SAndrew Thompson 
35729bd7d7eSAndrew Thompson tr_short:
358cb18f7d1SAlfred Perlstein 	usbd_enum_unlock(udev);
35929bd7d7eSAndrew Thompson 	USB_XFER_LOCK(xfer);
36029bd7d7eSAndrew Thompson 	return (USB_ERR_SHORT_XFER);
36129bd7d7eSAndrew Thompson 
36202ac6454SAndrew Thompson tr_stalled:
363cb18f7d1SAlfred Perlstein 	usbd_enum_unlock(udev);
36402ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
36502ac6454SAndrew Thompson 	return (USB_ERR_STALLED);
36602ac6454SAndrew Thompson }
36702ac6454SAndrew Thompson 
36802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
369a593f6b8SAndrew Thompson  *	usb_handle_stall
37002ac6454SAndrew Thompson  *
37102ac6454SAndrew Thompson  * Returns:
37202ac6454SAndrew Thompson  *    0: Success
37302ac6454SAndrew Thompson  * Else: Failure
37402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
375e0a69b51SAndrew Thompson static usb_error_t
376a593f6b8SAndrew Thompson usb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall)
37702ac6454SAndrew Thompson {
378760bc48eSAndrew Thompson 	struct usb_device *udev = xfer->xroot->udev;
379e0a69b51SAndrew Thompson 	usb_error_t err;
38002ac6454SAndrew Thompson 
38102ac6454SAndrew Thompson 	USB_XFER_UNLOCK(xfer);
382a593f6b8SAndrew Thompson 	err = usbd_set_endpoint_stall(udev,
383a593f6b8SAndrew Thompson 	    usbd_get_ep_by_addr(udev, ep), do_stall);
38402ac6454SAndrew Thompson 	USB_XFER_LOCK(xfer);
38502ac6454SAndrew Thompson 	return (err);
38602ac6454SAndrew Thompson }
38702ac6454SAndrew Thompson 
38802ac6454SAndrew Thompson /*------------------------------------------------------------------------*
389a593f6b8SAndrew Thompson  *	usb_handle_get_stall
39002ac6454SAndrew Thompson  *
39102ac6454SAndrew Thompson  * Returns:
39202ac6454SAndrew Thompson  *    0: Success
39302ac6454SAndrew Thompson  * Else: Failure
39402ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
39502ac6454SAndrew Thompson static uint8_t
396a593f6b8SAndrew Thompson usb_handle_get_stall(struct usb_device *udev, uint8_t ea_val)
39702ac6454SAndrew Thompson {
398ae60fdfbSAndrew Thompson 	struct usb_endpoint *ep;
39902ac6454SAndrew Thompson 	uint8_t halted;
40002ac6454SAndrew Thompson 
401a593f6b8SAndrew Thompson 	ep = usbd_get_ep_by_addr(udev, ea_val);
402ae60fdfbSAndrew Thompson 	if (ep == NULL) {
40302ac6454SAndrew Thompson 		/* nothing to do */
40402ac6454SAndrew Thompson 		return (0);
40502ac6454SAndrew Thompson 	}
40602ac6454SAndrew Thompson 	USB_BUS_LOCK(udev->bus);
407ae60fdfbSAndrew Thompson 	halted = ep->is_stalled;
40802ac6454SAndrew Thompson 	USB_BUS_UNLOCK(udev->bus);
40902ac6454SAndrew Thompson 
41002ac6454SAndrew Thompson 	return (halted);
41102ac6454SAndrew Thompson }
41202ac6454SAndrew Thompson 
41302ac6454SAndrew Thompson /*------------------------------------------------------------------------*
414a593f6b8SAndrew Thompson  *	usb_handle_remote_wakeup
41502ac6454SAndrew Thompson  *
41602ac6454SAndrew Thompson  * Returns:
41702ac6454SAndrew Thompson  *    0: Success
41802ac6454SAndrew Thompson  * Else: Failure
41902ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
420e0a69b51SAndrew Thompson static usb_error_t
421a593f6b8SAndrew Thompson usb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on)
42202ac6454SAndrew Thompson {
423760bc48eSAndrew Thompson 	struct usb_device *udev;
424760bc48eSAndrew Thompson 	struct usb_bus *bus;
42502ac6454SAndrew Thompson 
42602ac6454SAndrew Thompson 	udev = xfer->xroot->udev;
42702ac6454SAndrew Thompson 	bus = udev->bus;
42802ac6454SAndrew Thompson 
42902ac6454SAndrew Thompson 	USB_BUS_LOCK(bus);
43002ac6454SAndrew Thompson 
43102ac6454SAndrew Thompson 	if (is_on) {
43202ac6454SAndrew Thompson 		udev->flags.remote_wakeup = 1;
43302ac6454SAndrew Thompson 	} else {
43402ac6454SAndrew Thompson 		udev->flags.remote_wakeup = 0;
43502ac6454SAndrew Thompson 	}
43602ac6454SAndrew Thompson 
43702ac6454SAndrew Thompson 	USB_BUS_UNLOCK(bus);
43802ac6454SAndrew Thompson 
4392ecb4e91SHans Petter Selasky #if USB_HAVE_POWERD
44002ac6454SAndrew Thompson 	/* In case we are out of sync, update the power state. */
441a593f6b8SAndrew Thompson 	usb_bus_power_update(udev->bus);
4422ecb4e91SHans Petter Selasky #endif
44302ac6454SAndrew Thompson 	return (0);			/* success */
44402ac6454SAndrew Thompson }
44502ac6454SAndrew Thompson 
44602ac6454SAndrew Thompson /*------------------------------------------------------------------------*
447a593f6b8SAndrew Thompson  *	usb_handle_request
44802ac6454SAndrew Thompson  *
44902ac6454SAndrew Thompson  * Internal state sequence:
45002ac6454SAndrew Thompson  *
451a755d548SAndrew Thompson  * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR
45202ac6454SAndrew Thompson  *
45302ac6454SAndrew Thompson  * Returns:
45402ac6454SAndrew Thompson  * 0: Ready to start hardware
45502ac6454SAndrew Thompson  * Else: Stall current transfer, if any
45602ac6454SAndrew Thompson  *------------------------------------------------------------------------*/
457e0a69b51SAndrew Thompson static usb_error_t
458a593f6b8SAndrew Thompson usb_handle_request(struct usb_xfer *xfer)
45902ac6454SAndrew Thompson {
460760bc48eSAndrew Thompson 	struct usb_device_request req;
461760bc48eSAndrew Thompson 	struct usb_device *udev;
46202ac6454SAndrew Thompson 	const void *src_zcopy;		/* zero-copy source pointer */
46302ac6454SAndrew Thompson 	const void *src_mcopy;		/* non zero-copy source pointer */
46402ac6454SAndrew Thompson 	uint16_t off;			/* data offset */
46502ac6454SAndrew Thompson 	uint16_t rem;			/* data remainder */
46602ac6454SAndrew Thompson 	uint16_t max_len;		/* max fragment length */
46702ac6454SAndrew Thompson 	uint16_t wValue;
46802ac6454SAndrew Thompson 	uint8_t state;
46929bd7d7eSAndrew Thompson 	uint8_t is_complete = 1;
470e0a69b51SAndrew Thompson 	usb_error_t err;
47102ac6454SAndrew Thompson 	union {
47202ac6454SAndrew Thompson 		uWord	wStatus;
47302ac6454SAndrew Thompson 		uint8_t	buf[2];
47402ac6454SAndrew Thompson 	}     temp;
47502ac6454SAndrew Thompson 
47602ac6454SAndrew Thompson 	/*
47702ac6454SAndrew Thompson 	 * Filter the USB transfer state into
47802ac6454SAndrew Thompson 	 * something which we understand:
47902ac6454SAndrew Thompson 	 */
48002ac6454SAndrew Thompson 
48102ac6454SAndrew Thompson 	switch (USB_GET_STATE(xfer)) {
48202ac6454SAndrew Thompson 	case USB_ST_SETUP:
483a755d548SAndrew Thompson 		state = USB_HR_NOT_COMPLETE;
48402ac6454SAndrew Thompson 
48502ac6454SAndrew Thompson 		if (!xfer->flags_int.control_act) {
48602ac6454SAndrew Thompson 			/* nothing to do */
48702ac6454SAndrew Thompson 			goto tr_stalled;
48802ac6454SAndrew Thompson 		}
48902ac6454SAndrew Thompson 		break;
490a755d548SAndrew Thompson 	case USB_ST_TRANSFERRED:
49102ac6454SAndrew Thompson 		if (!xfer->flags_int.control_act) {
492a755d548SAndrew Thompson 			state = USB_HR_COMPLETE_OK;
49302ac6454SAndrew Thompson 		} else {
494a755d548SAndrew Thompson 			state = USB_HR_NOT_COMPLETE;
49502ac6454SAndrew Thompson 		}
49602ac6454SAndrew Thompson 		break;
497a755d548SAndrew Thompson 	default:
498a755d548SAndrew Thompson 		state = USB_HR_COMPLETE_ERR;
499a755d548SAndrew Thompson 		break;
50002ac6454SAndrew Thompson 	}
50102ac6454SAndrew Thompson 
50202ac6454SAndrew Thompson 	/* reset frame stuff */
50302ac6454SAndrew Thompson 
504ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_len(xfer, 0, 0);
50502ac6454SAndrew Thompson 
506ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_offset(xfer, 0, 0);
507ed6d949aSAndrew Thompson 	usbd_xfer_set_frame_offset(xfer, sizeof(req), 1);
50802ac6454SAndrew Thompson 
50902ac6454SAndrew Thompson 	/* get the current request, if any */
51002ac6454SAndrew Thompson 
511a593f6b8SAndrew Thompson 	usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
51202ac6454SAndrew Thompson 
51302ac6454SAndrew Thompson 	if (xfer->flags_int.control_rem == 0xFFFF) {
51402ac6454SAndrew Thompson 		/* first time - not initialised */
51502ac6454SAndrew Thompson 		rem = UGETW(req.wLength);
51602ac6454SAndrew Thompson 		off = 0;
51702ac6454SAndrew Thompson 	} else {
51802ac6454SAndrew Thompson 		/* not first time - initialised */
51902ac6454SAndrew Thompson 		rem = xfer->flags_int.control_rem;
52002ac6454SAndrew Thompson 		off = UGETW(req.wLength) - rem;
52102ac6454SAndrew Thompson 	}
52202ac6454SAndrew Thompson 
52302ac6454SAndrew Thompson 	/* set some defaults */
52402ac6454SAndrew Thompson 
52502ac6454SAndrew Thompson 	max_len = 0;
52602ac6454SAndrew Thompson 	src_zcopy = NULL;
52702ac6454SAndrew Thompson 	src_mcopy = NULL;
52802ac6454SAndrew Thompson 	udev = xfer->xroot->udev;
52902ac6454SAndrew Thompson 
53002ac6454SAndrew Thompson 	/* get some request fields decoded */
53102ac6454SAndrew Thompson 
53202ac6454SAndrew Thompson 	wValue = UGETW(req.wValue);
53302ac6454SAndrew Thompson 
53402ac6454SAndrew Thompson 	DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
53502ac6454SAndrew Thompson 	    "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
5366d917491SHans Petter Selasky 	    req.bRequest, wValue, UGETW(req.wIndex), off, rem, state);
53702ac6454SAndrew Thompson 
53802ac6454SAndrew Thompson 	/* demultiplex the control request */
53902ac6454SAndrew Thompson 
54002ac6454SAndrew Thompson 	switch (req.bmRequestType) {
54102ac6454SAndrew Thompson 	case UT_READ_DEVICE:
542a755d548SAndrew Thompson 		if (state != USB_HR_NOT_COMPLETE) {
54302ac6454SAndrew Thompson 			break;
54402ac6454SAndrew Thompson 		}
54502ac6454SAndrew Thompson 		switch (req.bRequest) {
54602ac6454SAndrew Thompson 		case UR_GET_DESCRIPTOR:
54702ac6454SAndrew Thompson 			goto tr_handle_get_descriptor;
54802ac6454SAndrew Thompson 		case UR_GET_CONFIG:
54902ac6454SAndrew Thompson 			goto tr_handle_get_config;
55002ac6454SAndrew Thompson 		case UR_GET_STATUS:
55102ac6454SAndrew Thompson 			goto tr_handle_get_status;
55202ac6454SAndrew Thompson 		default:
55302ac6454SAndrew Thompson 			goto tr_stalled;
55402ac6454SAndrew Thompson 		}
55502ac6454SAndrew Thompson 		break;
55602ac6454SAndrew Thompson 
55702ac6454SAndrew Thompson 	case UT_WRITE_DEVICE:
55802ac6454SAndrew Thompson 		switch (req.bRequest) {
55902ac6454SAndrew Thompson 		case UR_SET_ADDRESS:
56002ac6454SAndrew Thompson 			goto tr_handle_set_address;
56102ac6454SAndrew Thompson 		case UR_SET_CONFIG:
56202ac6454SAndrew Thompson 			goto tr_handle_set_config;
56302ac6454SAndrew Thompson 		case UR_CLEAR_FEATURE:
56402ac6454SAndrew Thompson 			switch (wValue) {
56502ac6454SAndrew Thompson 			case UF_DEVICE_REMOTE_WAKEUP:
56602ac6454SAndrew Thompson 				goto tr_handle_clear_wakeup;
56702ac6454SAndrew Thompson 			default:
56802ac6454SAndrew Thompson 				goto tr_stalled;
56902ac6454SAndrew Thompson 			}
57002ac6454SAndrew Thompson 			break;
57102ac6454SAndrew Thompson 		case UR_SET_FEATURE:
57202ac6454SAndrew Thompson 			switch (wValue) {
57302ac6454SAndrew Thompson 			case UF_DEVICE_REMOTE_WAKEUP:
57402ac6454SAndrew Thompson 				goto tr_handle_set_wakeup;
57502ac6454SAndrew Thompson 			default:
57602ac6454SAndrew Thompson 				goto tr_stalled;
57702ac6454SAndrew Thompson 			}
57802ac6454SAndrew Thompson 			break;
57902ac6454SAndrew Thompson 		default:
58002ac6454SAndrew Thompson 			goto tr_stalled;
58102ac6454SAndrew Thompson 		}
58202ac6454SAndrew Thompson 		break;
58302ac6454SAndrew Thompson 
58402ac6454SAndrew Thompson 	case UT_WRITE_ENDPOINT:
58502ac6454SAndrew Thompson 		switch (req.bRequest) {
58602ac6454SAndrew Thompson 		case UR_CLEAR_FEATURE:
58702ac6454SAndrew Thompson 			switch (wValue) {
58802ac6454SAndrew Thompson 			case UF_ENDPOINT_HALT:
58902ac6454SAndrew Thompson 				goto tr_handle_clear_halt;
59002ac6454SAndrew Thompson 			default:
59102ac6454SAndrew Thompson 				goto tr_stalled;
59202ac6454SAndrew Thompson 			}
59302ac6454SAndrew Thompson 			break;
59402ac6454SAndrew Thompson 		case UR_SET_FEATURE:
59502ac6454SAndrew Thompson 			switch (wValue) {
59602ac6454SAndrew Thompson 			case UF_ENDPOINT_HALT:
59702ac6454SAndrew Thompson 				goto tr_handle_set_halt;
59802ac6454SAndrew Thompson 			default:
59902ac6454SAndrew Thompson 				goto tr_stalled;
60002ac6454SAndrew Thompson 			}
60102ac6454SAndrew Thompson 			break;
60202ac6454SAndrew Thompson 		default:
60302ac6454SAndrew Thompson 			goto tr_stalled;
60402ac6454SAndrew Thompson 		}
60502ac6454SAndrew Thompson 		break;
60602ac6454SAndrew Thompson 
60702ac6454SAndrew Thompson 	case UT_READ_ENDPOINT:
60802ac6454SAndrew Thompson 		switch (req.bRequest) {
60902ac6454SAndrew Thompson 		case UR_GET_STATUS:
61002ac6454SAndrew Thompson 			goto tr_handle_get_ep_status;
61102ac6454SAndrew Thompson 		default:
61202ac6454SAndrew Thompson 			goto tr_stalled;
61302ac6454SAndrew Thompson 		}
61402ac6454SAndrew Thompson 		break;
61502ac6454SAndrew Thompson 	default:
61602ac6454SAndrew Thompson 		/* we use "USB_ADD_BYTES" to de-const the src_zcopy */
617a593f6b8SAndrew Thompson 		err = usb_handle_iface_request(xfer,
61802ac6454SAndrew Thompson 		    USB_ADD_BYTES(&src_zcopy, 0),
61902ac6454SAndrew Thompson 		    &max_len, req, off, state);
62002ac6454SAndrew Thompson 		if (err == 0) {
62129bd7d7eSAndrew Thompson 			is_complete = 0;
62229bd7d7eSAndrew Thompson 			goto tr_valid;
62329bd7d7eSAndrew Thompson 		} else if (err == USB_ERR_SHORT_XFER) {
62402ac6454SAndrew Thompson 			goto tr_valid;
62502ac6454SAndrew Thompson 		}
62602ac6454SAndrew Thompson 		/*
62702ac6454SAndrew Thompson 		 * Reset zero-copy pointer and max length
62802ac6454SAndrew Thompson 		 * variable in case they were unintentionally
62902ac6454SAndrew Thompson 		 * set:
63002ac6454SAndrew Thompson 		 */
63102ac6454SAndrew Thompson 		src_zcopy = NULL;
63202ac6454SAndrew Thompson 		max_len = 0;
63302ac6454SAndrew Thompson 
63402ac6454SAndrew Thompson 		/*
63502ac6454SAndrew Thompson 		 * Check if we have a vendor specific
63602ac6454SAndrew Thompson 		 * descriptor:
63702ac6454SAndrew Thompson 		 */
63802ac6454SAndrew Thompson 		goto tr_handle_get_descriptor;
63902ac6454SAndrew Thompson 	}
64002ac6454SAndrew Thompson 	goto tr_valid;
64102ac6454SAndrew Thompson 
64202ac6454SAndrew Thompson tr_handle_get_descriptor:
643a593f6b8SAndrew Thompson 	err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
644459d369eSAndrew Thompson 	if (err)
64502ac6454SAndrew Thompson 		goto tr_stalled;
646459d369eSAndrew Thompson 	if (src_zcopy == NULL)
647459d369eSAndrew Thompson 		goto tr_stalled;
64802ac6454SAndrew Thompson 	goto tr_valid;
64902ac6454SAndrew Thompson 
65002ac6454SAndrew Thompson tr_handle_get_config:
65102ac6454SAndrew Thompson 	temp.buf[0] = udev->curr_config_no;
65202ac6454SAndrew Thompson 	src_mcopy = temp.buf;
65302ac6454SAndrew Thompson 	max_len = 1;
65402ac6454SAndrew Thompson 	goto tr_valid;
65502ac6454SAndrew Thompson 
65602ac6454SAndrew Thompson tr_handle_get_status:
65702ac6454SAndrew Thompson 
65802ac6454SAndrew Thompson 	wValue = 0;
65902ac6454SAndrew Thompson 
66002ac6454SAndrew Thompson 	USB_BUS_LOCK(udev->bus);
66102ac6454SAndrew Thompson 	if (udev->flags.remote_wakeup) {
66202ac6454SAndrew Thompson 		wValue |= UDS_REMOTE_WAKEUP;
66302ac6454SAndrew Thompson 	}
66402ac6454SAndrew Thompson 	if (udev->flags.self_powered) {
66502ac6454SAndrew Thompson 		wValue |= UDS_SELF_POWERED;
66602ac6454SAndrew Thompson 	}
66702ac6454SAndrew Thompson 	USB_BUS_UNLOCK(udev->bus);
66802ac6454SAndrew Thompson 
66902ac6454SAndrew Thompson 	USETW(temp.wStatus, wValue);
67002ac6454SAndrew Thompson 	src_mcopy = temp.wStatus;
67102ac6454SAndrew Thompson 	max_len = sizeof(temp.wStatus);
67202ac6454SAndrew Thompson 	goto tr_valid;
67302ac6454SAndrew Thompson 
67402ac6454SAndrew Thompson tr_handle_set_address:
675a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
67602ac6454SAndrew Thompson 		if (wValue >= 0x80) {
67702ac6454SAndrew Thompson 			/* invalid value */
67802ac6454SAndrew Thompson 			goto tr_stalled;
67902ac6454SAndrew Thompson 		} else if (udev->curr_config_no != 0) {
68002ac6454SAndrew Thompson 			/* we are configured ! */
68102ac6454SAndrew Thompson 			goto tr_stalled;
68202ac6454SAndrew Thompson 		}
683a755d548SAndrew Thompson 	} else if (state != USB_HR_NOT_COMPLETE) {
68402ac6454SAndrew Thompson 		udev->address = (wValue & 0x7F);
68502ac6454SAndrew Thompson 		goto tr_bad_context;
68602ac6454SAndrew Thompson 	}
68702ac6454SAndrew Thompson 	goto tr_valid;
68802ac6454SAndrew Thompson 
68902ac6454SAndrew Thompson tr_handle_set_config:
690a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
691a593f6b8SAndrew Thompson 		if (usb_handle_set_config(xfer, req.wValue[0])) {
69202ac6454SAndrew Thompson 			goto tr_stalled;
69302ac6454SAndrew Thompson 		}
69402ac6454SAndrew Thompson 	}
69502ac6454SAndrew Thompson 	goto tr_valid;
69602ac6454SAndrew Thompson 
69702ac6454SAndrew Thompson tr_handle_clear_halt:
698a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
699a593f6b8SAndrew Thompson 		if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) {
70002ac6454SAndrew Thompson 			goto tr_stalled;
70102ac6454SAndrew Thompson 		}
70202ac6454SAndrew Thompson 	}
70302ac6454SAndrew Thompson 	goto tr_valid;
70402ac6454SAndrew Thompson 
70502ac6454SAndrew Thompson tr_handle_clear_wakeup:
706a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
707a593f6b8SAndrew Thompson 		if (usb_handle_remote_wakeup(xfer, 0)) {
70802ac6454SAndrew Thompson 			goto tr_stalled;
70902ac6454SAndrew Thompson 		}
71002ac6454SAndrew Thompson 	}
71102ac6454SAndrew Thompson 	goto tr_valid;
71202ac6454SAndrew Thompson 
71302ac6454SAndrew Thompson tr_handle_set_halt:
714a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
715a593f6b8SAndrew Thompson 		if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) {
71602ac6454SAndrew Thompson 			goto tr_stalled;
71702ac6454SAndrew Thompson 		}
71802ac6454SAndrew Thompson 	}
71902ac6454SAndrew Thompson 	goto tr_valid;
72002ac6454SAndrew Thompson 
72102ac6454SAndrew Thompson tr_handle_set_wakeup:
722a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
723a593f6b8SAndrew Thompson 		if (usb_handle_remote_wakeup(xfer, 1)) {
72402ac6454SAndrew Thompson 			goto tr_stalled;
72502ac6454SAndrew Thompson 		}
72602ac6454SAndrew Thompson 	}
72702ac6454SAndrew Thompson 	goto tr_valid;
72802ac6454SAndrew Thompson 
72902ac6454SAndrew Thompson tr_handle_get_ep_status:
730a755d548SAndrew Thompson 	if (state == USB_HR_NOT_COMPLETE) {
73102ac6454SAndrew Thompson 		temp.wStatus[0] =
732a593f6b8SAndrew Thompson 		    usb_handle_get_stall(udev, req.wIndex[0]);
73302ac6454SAndrew Thompson 		temp.wStatus[1] = 0;
73402ac6454SAndrew Thompson 		src_mcopy = temp.wStatus;
73502ac6454SAndrew Thompson 		max_len = sizeof(temp.wStatus);
73602ac6454SAndrew Thompson 	}
73702ac6454SAndrew Thompson 	goto tr_valid;
73802ac6454SAndrew Thompson 
73902ac6454SAndrew Thompson tr_valid:
740a755d548SAndrew Thompson 	if (state != USB_HR_NOT_COMPLETE) {
74102ac6454SAndrew Thompson 		goto tr_stalled;
74202ac6454SAndrew Thompson 	}
74302ac6454SAndrew Thompson 	/* subtract offset from length */
74402ac6454SAndrew Thompson 
74502ac6454SAndrew Thompson 	max_len -= off;
74602ac6454SAndrew Thompson 
74702ac6454SAndrew Thompson 	/* Compute the real maximum data length */
74802ac6454SAndrew Thompson 
74902ac6454SAndrew Thompson 	if (max_len > xfer->max_data_length) {
750ed6d949aSAndrew Thompson 		max_len = usbd_xfer_max_len(xfer);
75102ac6454SAndrew Thompson 	}
75202ac6454SAndrew Thompson 	if (max_len > rem) {
75302ac6454SAndrew Thompson 		max_len = rem;
75402ac6454SAndrew Thompson 	}
75502ac6454SAndrew Thompson 	/*
75602ac6454SAndrew Thompson 	 * If the remainder is greater than the maximum data length,
75702ac6454SAndrew Thompson 	 * we need to truncate the value for the sake of the
75802ac6454SAndrew Thompson 	 * comparison below:
75902ac6454SAndrew Thompson 	 */
76002ac6454SAndrew Thompson 	if (rem > xfer->max_data_length) {
761ed6d949aSAndrew Thompson 		rem = usbd_xfer_max_len(xfer);
76202ac6454SAndrew Thompson 	}
76329bd7d7eSAndrew Thompson 	if ((rem != max_len) && (is_complete != 0)) {
76402ac6454SAndrew Thompson 		/*
76502ac6454SAndrew Thompson 	         * If we don't transfer the data we can transfer, then
76602ac6454SAndrew Thompson 	         * the transfer is short !
76702ac6454SAndrew Thompson 	         */
76802ac6454SAndrew Thompson 		xfer->flags.force_short_xfer = 1;
76902ac6454SAndrew Thompson 		xfer->nframes = 2;
77002ac6454SAndrew Thompson 	} else {
77102ac6454SAndrew Thompson 		/*
77202ac6454SAndrew Thompson 		 * Default case
77302ac6454SAndrew Thompson 		 */
77402ac6454SAndrew Thompson 		xfer->flags.force_short_xfer = 0;
77502ac6454SAndrew Thompson 		xfer->nframes = max_len ? 2 : 1;
77602ac6454SAndrew Thompson 	}
77702ac6454SAndrew Thompson 	if (max_len > 0) {
77802ac6454SAndrew Thompson 		if (src_mcopy) {
77902ac6454SAndrew Thompson 			src_mcopy = USB_ADD_BYTES(src_mcopy, off);
780a593f6b8SAndrew Thompson 			usbd_copy_in(xfer->frbuffers + 1, 0,
78102ac6454SAndrew Thompson 			    src_mcopy, max_len);
782ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_len(xfer, 1, max_len);
78302ac6454SAndrew Thompson 		} else {
784ed6d949aSAndrew Thompson 			usbd_xfer_set_frame_data(xfer, 1,
785ed6d949aSAndrew Thompson 			    USB_ADD_BYTES(src_zcopy, off), max_len);
78602ac6454SAndrew Thompson 		}
78702ac6454SAndrew Thompson 	} else {
78802ac6454SAndrew Thompson 		/* the end is reached, send status */
78902ac6454SAndrew Thompson 		xfer->flags.manual_status = 0;
790ed6d949aSAndrew Thompson 		usbd_xfer_set_frame_len(xfer, 1, 0);
79102ac6454SAndrew Thompson 	}
79202ac6454SAndrew Thompson 	DPRINTF("success\n");
79302ac6454SAndrew Thompson 	return (0);			/* success */
79402ac6454SAndrew Thompson 
79502ac6454SAndrew Thompson tr_stalled:
796a755d548SAndrew Thompson 	DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ?
79702ac6454SAndrew Thompson 	    "complete" : "stalled");
79802ac6454SAndrew Thompson 	return (USB_ERR_STALLED);
79902ac6454SAndrew Thompson 
80002ac6454SAndrew Thompson tr_bad_context:
80102ac6454SAndrew Thompson 	DPRINTF("bad context\n");
80202ac6454SAndrew Thompson 	return (USB_ERR_BAD_CONTEXT);
80302ac6454SAndrew Thompson }
804