usb_handle_request.c revision 233774
1184610Salfred/* $FreeBSD: head/sys/dev/usb/usb_handle_request.c 233774 2012-04-02 10:50:42Z hselasky $ */
2184610Salfred/*-
3184610Salfred * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4184610Salfred *
5184610Salfred * Redistribution and use in source and binary forms, with or without
6184610Salfred * modification, are permitted provided that the following conditions
7184610Salfred * are met:
8184610Salfred * 1. Redistributions of source code must retain the above copyright
9184610Salfred *    notice, this list of conditions and the following disclaimer.
10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright
11184610Salfred *    notice, this list of conditions and the following disclaimer in the
12184610Salfred *    documentation and/or other materials provided with the distribution.
13184610Salfred *
14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17184610Salfred * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24184610Salfred * SUCH DAMAGE.
25184610Salfred */
26184610Salfred
27194677Sthompsa#include <sys/stdint.h>
28194677Sthompsa#include <sys/stddef.h>
29194677Sthompsa#include <sys/param.h>
30194677Sthompsa#include <sys/queue.h>
31194677Sthompsa#include <sys/types.h>
32194677Sthompsa#include <sys/systm.h>
33194677Sthompsa#include <sys/kernel.h>
34194677Sthompsa#include <sys/bus.h>
35194677Sthompsa#include <sys/module.h>
36194677Sthompsa#include <sys/lock.h>
37194677Sthompsa#include <sys/mutex.h>
38194677Sthompsa#include <sys/condvar.h>
39194677Sthompsa#include <sys/sysctl.h>
40194677Sthompsa#include <sys/sx.h>
41194677Sthompsa#include <sys/unistd.h>
42194677Sthompsa#include <sys/callout.h>
43194677Sthompsa#include <sys/malloc.h>
44194677Sthompsa#include <sys/priv.h>
45194677Sthompsa
46188942Sthompsa#include <dev/usb/usb.h>
47194677Sthompsa#include <dev/usb/usbdi.h>
48195963Salfred#include <dev/usb/usbdi_util.h>
49194677Sthompsa#include "usb_if.h"
50184610Salfred
51194228Sthompsa#define	USB_DEBUG_VAR usb_debug
52184610Salfred
53188942Sthompsa#include <dev/usb/usb_core.h>
54188942Sthompsa#include <dev/usb/usb_process.h>
55188942Sthompsa#include <dev/usb/usb_busdma.h>
56188942Sthompsa#include <dev/usb/usb_transfer.h>
57188942Sthompsa#include <dev/usb/usb_device.h>
58188942Sthompsa#include <dev/usb/usb_debug.h>
59188942Sthompsa#include <dev/usb/usb_dynamic.h>
60188942Sthompsa#include <dev/usb/usb_hub.h>
61184610Salfred
62188942Sthompsa#include <dev/usb/usb_controller.h>
63188942Sthompsa#include <dev/usb/usb_bus.h>
64184610Salfred
65184610Salfred/* function prototypes */
66184610Salfred
67194228Sthompsastatic uint8_t usb_handle_get_stall(struct usb_device *, uint8_t);
68194228Sthompsastatic usb_error_t	 usb_handle_remote_wakeup(struct usb_xfer *, uint8_t);
69194228Sthompsastatic usb_error_t	 usb_handle_request(struct usb_xfer *);
70194228Sthompsastatic usb_error_t	 usb_handle_set_config(struct usb_xfer *, uint8_t);
71194228Sthompsastatic usb_error_t	 usb_handle_set_stall(struct usb_xfer *, uint8_t,
72185948Sthompsa			    uint8_t);
73194228Sthompsastatic usb_error_t	 usb_handle_iface_request(struct usb_xfer *, void **,
74192984Sthompsa			    uint16_t *, struct usb_device_request, uint16_t,
75185948Sthompsa			    uint8_t);
76184610Salfred
77184610Salfred/*------------------------------------------------------------------------*
78194228Sthompsa *	usb_handle_request_callback
79184610Salfred *
80184610Salfred * This function is the USB callback for generic USB Device control
81184610Salfred * transfers.
82184610Salfred *------------------------------------------------------------------------*/
83184610Salfredvoid
84194677Sthompsausb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error)
85184610Salfred{
86193045Sthompsa	usb_error_t err;
87184610Salfred
88184610Salfred	/* check the current transfer state */
89184610Salfred
90184610Salfred	switch (USB_GET_STATE(xfer)) {
91184610Salfred	case USB_ST_SETUP:
92184610Salfred	case USB_ST_TRANSFERRED:
93184610Salfred
94184610Salfred		/* handle the request */
95194228Sthompsa		err = usb_handle_request(xfer);
96184610Salfred
97184610Salfred		if (err) {
98184610Salfred
99184610Salfred			if (err == USB_ERR_BAD_CONTEXT) {
100184610Salfred				/* we need to re-setup the control transfer */
101194228Sthompsa				usb_needs_explore(xfer->xroot->bus, 0);
102184610Salfred				break;
103184610Salfred			}
104184610Salfred			goto tr_restart;
105184610Salfred		}
106194228Sthompsa		usbd_transfer_submit(xfer);
107184610Salfred		break;
108184610Salfred
109184610Salfred	default:
110194064Sthompsa		/* check if a control transfer is active */
111194064Sthompsa		if (xfer->flags_int.control_rem != 0xFFFF) {
112194064Sthompsa			/* handle the request */
113194228Sthompsa			err = usb_handle_request(xfer);
114194064Sthompsa		}
115184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
116184610Salfred			/* should not happen - try stalling */
117184610Salfred			goto tr_restart;
118184610Salfred		}
119184610Salfred		break;
120184610Salfred	}
121184610Salfred	return;
122184610Salfred
123184610Salfredtr_restart:
124194064Sthompsa	/*
125194064Sthompsa	 * If a control transfer is active, stall it, and wait for the
126194064Sthompsa	 * next control transfer.
127194064Sthompsa	 */
128194677Sthompsa	usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request));
129184610Salfred	xfer->nframes = 1;
130184610Salfred	xfer->flags.manual_status = 1;
131184610Salfred	xfer->flags.force_short_xfer = 0;
132194677Sthompsa	usbd_xfer_set_stall(xfer);	/* cancel previous transfer, if any */
133194228Sthompsa	usbd_transfer_submit(xfer);
134184610Salfred}
135184610Salfred
136184610Salfred/*------------------------------------------------------------------------*
137194228Sthompsa *	usb_handle_set_config
138184610Salfred *
139184610Salfred * Returns:
140184610Salfred *    0: Success
141184610Salfred * Else: Failure
142184610Salfred *------------------------------------------------------------------------*/
143193045Sthompsastatic usb_error_t
144194228Sthompsausb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no)
145184610Salfred{
146192984Sthompsa	struct usb_device *udev = xfer->xroot->udev;
147193045Sthompsa	usb_error_t err = 0;
148184610Salfred
149184610Salfred	/*
150184610Salfred	 * We need to protect against other threads doing probe and
151184610Salfred	 * attach:
152184610Salfred	 */
153184824Sthompsa	USB_XFER_UNLOCK(xfer);
154184610Salfred
155196498Salfred	usbd_enum_lock(udev);
156196498Salfred
157184610Salfred	if (conf_no == USB_UNCONFIG_NO) {
158184610Salfred		conf_no = USB_UNCONFIG_INDEX;
159184610Salfred	} else {
160184610Salfred		/*
161184610Salfred		 * The relationship between config number and config index
162184610Salfred		 * is very simple in our case:
163184610Salfred		 */
164184610Salfred		conf_no--;
165184610Salfred	}
166184610Salfred
167194228Sthompsa	if (usbd_set_config_index(udev, conf_no)) {
168184610Salfred		DPRINTF("set config %d failed\n", conf_no);
169184610Salfred		err = USB_ERR_STALLED;
170184610Salfred		goto done;
171184610Salfred	}
172194228Sthompsa	if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
173184610Salfred		DPRINTF("probe and attach failed\n");
174184610Salfred		err = USB_ERR_STALLED;
175184610Salfred		goto done;
176184610Salfred	}
177184610Salfreddone:
178196498Salfred	usbd_enum_unlock(udev);
179184824Sthompsa	USB_XFER_LOCK(xfer);
180184610Salfred	return (err);
181184610Salfred}
182184610Salfred
183195963Salfredstatic usb_error_t
184195963Salfredusb_check_alt_setting(struct usb_device *udev,
185195963Salfred     struct usb_interface *iface, uint8_t alt_index)
186195963Salfred{
187195963Salfred	uint8_t do_unlock;
188195963Salfred	usb_error_t err = 0;
189195963Salfred
190195963Salfred	/* automatic locking */
191196498Salfred	if (usbd_enum_is_locked(udev)) {
192195963Salfred		do_unlock = 0;
193195963Salfred	} else {
194195963Salfred		do_unlock = 1;
195196498Salfred		usbd_enum_lock(udev);
196195963Salfred	}
197195963Salfred
198195963Salfred	if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc))
199195963Salfred		err = USB_ERR_INVAL;
200195963Salfred
201196498Salfred	if (do_unlock)
202196498Salfred		usbd_enum_unlock(udev);
203196498Salfred
204195963Salfred	return (err);
205195963Salfred}
206195963Salfred
207184610Salfred/*------------------------------------------------------------------------*
208194228Sthompsa *	usb_handle_iface_request
209184610Salfred *
210184610Salfred * Returns:
211184610Salfred *    0: Success
212184610Salfred * Else: Failure
213184610Salfred *------------------------------------------------------------------------*/
214193045Sthompsastatic usb_error_t
215194228Sthompsausb_handle_iface_request(struct usb_xfer *xfer,
216184610Salfred    void **ppdata, uint16_t *plen,
217192984Sthompsa    struct usb_device_request req, uint16_t off, uint8_t state)
218184610Salfred{
219192984Sthompsa	struct usb_interface *iface;
220192984Sthompsa	struct usb_interface *iface_parent;	/* parent interface */
221192984Sthompsa	struct usb_device *udev = xfer->xroot->udev;
222184610Salfred	int error;
223184610Salfred	uint8_t iface_index;
224195121Sthompsa	uint8_t temp_state;
225184610Salfred
226184610Salfred	if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
227184610Salfred		iface_index = req.wIndex[0];	/* unicast */
228184610Salfred	} else {
229184610Salfred		iface_index = 0;	/* broadcast */
230184610Salfred	}
231184610Salfred
232184610Salfred	/*
233184610Salfred	 * We need to protect against other threads doing probe and
234184610Salfred	 * attach:
235184610Salfred	 */
236184824Sthompsa	USB_XFER_UNLOCK(xfer);
237184610Salfred
238196498Salfred	usbd_enum_lock(udev);
239196498Salfred
240184610Salfred	error = ENXIO;
241184610Salfred
242184610Salfredtr_repeat:
243194228Sthompsa	iface = usbd_get_iface(udev, iface_index);
244184610Salfred	if ((iface == NULL) ||
245184610Salfred	    (iface->idesc == NULL)) {
246184610Salfred		/* end of interfaces non-existing interface */
247184610Salfred		goto tr_stalled;
248184610Salfred	}
249195121Sthompsa	/* set initial state */
250195121Sthompsa
251195121Sthompsa	temp_state = state;
252195121Sthompsa
253184610Salfred	/* forward request to interface, if any */
254184610Salfred
255184610Salfred	if ((error != 0) &&
256184610Salfred	    (error != ENOTTY) &&
257184610Salfred	    (iface->subdev != NULL) &&
258184610Salfred	    device_is_attached(iface->subdev)) {
259184610Salfred#if 0
260190186Sthompsa		DEVMETHOD(usb_handle_request, NULL);	/* dummy */
261184610Salfred#endif
262188942Sthompsa		error = USB_HANDLE_REQUEST(iface->subdev,
263184610Salfred		    &req, ppdata, plen,
264195121Sthompsa		    off, &temp_state);
265184610Salfred	}
266194228Sthompsa	iface_parent = usbd_get_iface(udev, iface->parent_iface_index);
267184610Salfred
268184610Salfred	if ((iface_parent == NULL) ||
269184610Salfred	    (iface_parent->idesc == NULL)) {
270184610Salfred		/* non-existing interface */
271184610Salfred		iface_parent = NULL;
272184610Salfred	}
273184610Salfred	/* forward request to parent interface, if any */
274184610Salfred
275184610Salfred	if ((error != 0) &&
276184610Salfred	    (error != ENOTTY) &&
277184610Salfred	    (iface_parent != NULL) &&
278184610Salfred	    (iface_parent->subdev != NULL) &&
279184610Salfred	    ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
280184610Salfred	    (iface_parent->subdev != iface->subdev) &&
281184610Salfred	    device_is_attached(iface_parent->subdev)) {
282188942Sthompsa		error = USB_HANDLE_REQUEST(iface_parent->subdev,
283195121Sthompsa		    &req, ppdata, plen, off, &temp_state);
284184610Salfred	}
285184610Salfred	if (error == 0) {
286184610Salfred		/* negativly adjust pointer and length */
287184610Salfred		*ppdata = ((uint8_t *)(*ppdata)) - off;
288184610Salfred		*plen += off;
289195121Sthompsa
290195121Sthompsa		if ((state == USB_HR_NOT_COMPLETE) &&
291195121Sthompsa		    (temp_state == USB_HR_COMPLETE_OK))
292195121Sthompsa			goto tr_short;
293195121Sthompsa		else
294195121Sthompsa			goto tr_valid;
295184610Salfred	} else if (error == ENOTTY) {
296184610Salfred		goto tr_stalled;
297184610Salfred	}
298184610Salfred	if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
299184610Salfred		iface_index++;		/* iterate */
300184610Salfred		goto tr_repeat;
301184610Salfred	}
302194064Sthompsa	if (state != USB_HR_NOT_COMPLETE) {
303184610Salfred		/* we are complete */
304184610Salfred		goto tr_valid;
305184610Salfred	}
306184610Salfred	switch (req.bmRequestType) {
307184610Salfred	case UT_WRITE_INTERFACE:
308184610Salfred		switch (req.bRequest) {
309184610Salfred		case UR_SET_INTERFACE:
310184610Salfred			/*
311195963Salfred			 * We assume that the endpoints are the same
312195963Salfred			 * accross the alternate settings.
313195963Salfred			 *
314195963Salfred			 * Reset the endpoints, because re-attaching
315195963Salfred			 * only a part of the device is not possible.
316184610Salfred			 */
317195963Salfred			error = usb_check_alt_setting(udev,
318195963Salfred			    iface, req.wValue[0]);
319184610Salfred			if (error) {
320195963Salfred				DPRINTF("alt setting does not exist %s\n",
321194228Sthompsa				    usbd_errstr(error));
322184610Salfred				goto tr_stalled;
323184610Salfred			}
324195963Salfred			error = usb_reset_iface_endpoints(udev, iface_index);
325184610Salfred			if (error) {
326195963Salfred				DPRINTF("alt setting failed %s\n",
327195963Salfred				    usbd_errstr(error));
328184610Salfred				goto tr_stalled;
329184610Salfred			}
330195963Salfred			/* update the current alternate setting */
331195963Salfred			iface->alt_index = req.wValue[0];
332184610Salfred			break;
333195963Salfred
334184610Salfred		default:
335184610Salfred			goto tr_stalled;
336184610Salfred		}
337184610Salfred		break;
338184610Salfred
339184610Salfred	case UT_READ_INTERFACE:
340184610Salfred		switch (req.bRequest) {
341184610Salfred		case UR_GET_INTERFACE:
342184610Salfred			*ppdata = &iface->alt_index;
343184610Salfred			*plen = 1;
344184610Salfred			break;
345184610Salfred
346184610Salfred		default:
347184610Salfred			goto tr_stalled;
348184610Salfred		}
349184610Salfred		break;
350184610Salfred	default:
351184610Salfred		goto tr_stalled;
352184610Salfred	}
353184610Salfredtr_valid:
354196498Salfred	usbd_enum_unlock(udev);
355184824Sthompsa	USB_XFER_LOCK(xfer);
356184610Salfred	return (0);
357184610Salfred
358195121Sthompsatr_short:
359196498Salfred	usbd_enum_unlock(udev);
360195121Sthompsa	USB_XFER_LOCK(xfer);
361195121Sthompsa	return (USB_ERR_SHORT_XFER);
362195121Sthompsa
363184610Salfredtr_stalled:
364196498Salfred	usbd_enum_unlock(udev);
365184824Sthompsa	USB_XFER_LOCK(xfer);
366184610Salfred	return (USB_ERR_STALLED);
367184610Salfred}
368184610Salfred
369184610Salfred/*------------------------------------------------------------------------*
370194228Sthompsa *	usb_handle_stall
371184610Salfred *
372184610Salfred * Returns:
373184610Salfred *    0: Success
374184610Salfred * Else: Failure
375184610Salfred *------------------------------------------------------------------------*/
376193045Sthompsastatic usb_error_t
377194228Sthompsausb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall)
378184610Salfred{
379192984Sthompsa	struct usb_device *udev = xfer->xroot->udev;
380193045Sthompsa	usb_error_t err;
381184610Salfred
382184824Sthompsa	USB_XFER_UNLOCK(xfer);
383194228Sthompsa	err = usbd_set_endpoint_stall(udev,
384194228Sthompsa	    usbd_get_ep_by_addr(udev, ep), do_stall);
385184824Sthompsa	USB_XFER_LOCK(xfer);
386184610Salfred	return (err);
387184610Salfred}
388184610Salfred
389184610Salfred/*------------------------------------------------------------------------*
390194228Sthompsa *	usb_handle_get_stall
391184610Salfred *
392184610Salfred * Returns:
393184610Salfred *    0: Success
394184610Salfred * Else: Failure
395184610Salfred *------------------------------------------------------------------------*/
396184610Salfredstatic uint8_t
397194228Sthompsausb_handle_get_stall(struct usb_device *udev, uint8_t ea_val)
398184610Salfred{
399193644Sthompsa	struct usb_endpoint *ep;
400184610Salfred	uint8_t halted;
401184610Salfred
402194228Sthompsa	ep = usbd_get_ep_by_addr(udev, ea_val);
403193644Sthompsa	if (ep == NULL) {
404184610Salfred		/* nothing to do */
405184610Salfred		return (0);
406184610Salfred	}
407184824Sthompsa	USB_BUS_LOCK(udev->bus);
408193644Sthompsa	halted = ep->is_stalled;
409184824Sthompsa	USB_BUS_UNLOCK(udev->bus);
410184610Salfred
411184610Salfred	return (halted);
412184610Salfred}
413184610Salfred
414184610Salfred/*------------------------------------------------------------------------*
415194228Sthompsa *	usb_handle_remote_wakeup
416184610Salfred *
417184610Salfred * Returns:
418184610Salfred *    0: Success
419184610Salfred * Else: Failure
420184610Salfred *------------------------------------------------------------------------*/
421193045Sthompsastatic usb_error_t
422194228Sthompsausb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on)
423184610Salfred{
424192984Sthompsa	struct usb_device *udev;
425192984Sthompsa	struct usb_bus *bus;
426184610Salfred
427187173Sthompsa	udev = xfer->xroot->udev;
428184610Salfred	bus = udev->bus;
429184610Salfred
430184824Sthompsa	USB_BUS_LOCK(bus);
431184610Salfred
432184610Salfred	if (is_on) {
433184610Salfred		udev->flags.remote_wakeup = 1;
434184610Salfred	} else {
435184610Salfred		udev->flags.remote_wakeup = 0;
436184610Salfred	}
437184610Salfred
438184824Sthompsa	USB_BUS_UNLOCK(bus);
439184610Salfred
440213434Shselasky#if USB_HAVE_POWERD
441186730Salfred	/* In case we are out of sync, update the power state. */
442194228Sthompsa	usb_bus_power_update(udev->bus);
443213434Shselasky#endif
444184610Salfred	return (0);			/* success */
445184610Salfred}
446184610Salfred
447184610Salfred/*------------------------------------------------------------------------*
448194228Sthompsa *	usb_handle_request
449184610Salfred *
450184610Salfred * Internal state sequence:
451184610Salfred *
452194064Sthompsa * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR
453184610Salfred *
454184610Salfred * Returns:
455184610Salfred * 0: Ready to start hardware
456184610Salfred * Else: Stall current transfer, if any
457184610Salfred *------------------------------------------------------------------------*/
458193045Sthompsastatic usb_error_t
459194228Sthompsausb_handle_request(struct usb_xfer *xfer)
460184610Salfred{
461192984Sthompsa	struct usb_device_request req;
462192984Sthompsa	struct usb_device *udev;
463184610Salfred	const void *src_zcopy;		/* zero-copy source pointer */
464184610Salfred	const void *src_mcopy;		/* non zero-copy source pointer */
465184610Salfred	uint16_t off;			/* data offset */
466184610Salfred	uint16_t rem;			/* data remainder */
467184610Salfred	uint16_t max_len;		/* max fragment length */
468184610Salfred	uint16_t wValue;
469184610Salfred	uint8_t state;
470195121Sthompsa	uint8_t is_complete = 1;
471193045Sthompsa	usb_error_t err;
472184610Salfred	union {
473184610Salfred		uWord	wStatus;
474184610Salfred		uint8_t	buf[2];
475184610Salfred	}     temp;
476184610Salfred
477184610Salfred	/*
478184610Salfred	 * Filter the USB transfer state into
479184610Salfred	 * something which we understand:
480184610Salfred	 */
481184610Salfred
482184610Salfred	switch (USB_GET_STATE(xfer)) {
483184610Salfred	case USB_ST_SETUP:
484194064Sthompsa		state = USB_HR_NOT_COMPLETE;
485184610Salfred
486184610Salfred		if (!xfer->flags_int.control_act) {
487184610Salfred			/* nothing to do */
488184610Salfred			goto tr_stalled;
489184610Salfred		}
490184610Salfred		break;
491194064Sthompsa	case USB_ST_TRANSFERRED:
492184610Salfred		if (!xfer->flags_int.control_act) {
493194064Sthompsa			state = USB_HR_COMPLETE_OK;
494184610Salfred		} else {
495194064Sthompsa			state = USB_HR_NOT_COMPLETE;
496184610Salfred		}
497184610Salfred		break;
498194064Sthompsa	default:
499194064Sthompsa		state = USB_HR_COMPLETE_ERR;
500194064Sthompsa		break;
501184610Salfred	}
502184610Salfred
503184610Salfred	/* reset frame stuff */
504184610Salfred
505194677Sthompsa	usbd_xfer_set_frame_len(xfer, 0, 0);
506184610Salfred
507194677Sthompsa	usbd_xfer_set_frame_offset(xfer, 0, 0);
508194677Sthompsa	usbd_xfer_set_frame_offset(xfer, sizeof(req), 1);
509184610Salfred
510184610Salfred	/* get the current request, if any */
511184610Salfred
512194228Sthompsa	usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
513184610Salfred
514184610Salfred	if (xfer->flags_int.control_rem == 0xFFFF) {
515184610Salfred		/* first time - not initialised */
516184610Salfred		rem = UGETW(req.wLength);
517184610Salfred		off = 0;
518184610Salfred	} else {
519184610Salfred		/* not first time - initialised */
520184610Salfred		rem = xfer->flags_int.control_rem;
521184610Salfred		off = UGETW(req.wLength) - rem;
522184610Salfred	}
523184610Salfred
524184610Salfred	/* set some defaults */
525184610Salfred
526184610Salfred	max_len = 0;
527184610Salfred	src_zcopy = NULL;
528184610Salfred	src_mcopy = NULL;
529187173Sthompsa	udev = xfer->xroot->udev;
530184610Salfred
531184610Salfred	/* get some request fields decoded */
532184610Salfred
533184610Salfred	wValue = UGETW(req.wValue);
534184610Salfred
535184610Salfred	DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
536184610Salfred	    "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
537233774Shselasky	    req.bRequest, wValue, UGETW(req.wIndex), off, rem, state);
538184610Salfred
539184610Salfred	/* demultiplex the control request */
540184610Salfred
541184610Salfred	switch (req.bmRequestType) {
542184610Salfred	case UT_READ_DEVICE:
543194064Sthompsa		if (state != USB_HR_NOT_COMPLETE) {
544184610Salfred			break;
545184610Salfred		}
546184610Salfred		switch (req.bRequest) {
547184610Salfred		case UR_GET_DESCRIPTOR:
548184610Salfred			goto tr_handle_get_descriptor;
549184610Salfred		case UR_GET_CONFIG:
550184610Salfred			goto tr_handle_get_config;
551184610Salfred		case UR_GET_STATUS:
552184610Salfred			goto tr_handle_get_status;
553184610Salfred		default:
554184610Salfred			goto tr_stalled;
555184610Salfred		}
556184610Salfred		break;
557184610Salfred
558184610Salfred	case UT_WRITE_DEVICE:
559184610Salfred		switch (req.bRequest) {
560184610Salfred		case UR_SET_ADDRESS:
561184610Salfred			goto tr_handle_set_address;
562184610Salfred		case UR_SET_CONFIG:
563184610Salfred			goto tr_handle_set_config;
564184610Salfred		case UR_CLEAR_FEATURE:
565184610Salfred			switch (wValue) {
566184610Salfred			case UF_DEVICE_REMOTE_WAKEUP:
567184610Salfred				goto tr_handle_clear_wakeup;
568184610Salfred			default:
569184610Salfred				goto tr_stalled;
570184610Salfred			}
571184610Salfred			break;
572184610Salfred		case UR_SET_FEATURE:
573184610Salfred			switch (wValue) {
574184610Salfred			case UF_DEVICE_REMOTE_WAKEUP:
575184610Salfred				goto tr_handle_set_wakeup;
576184610Salfred			default:
577184610Salfred				goto tr_stalled;
578184610Salfred			}
579184610Salfred			break;
580184610Salfred		default:
581184610Salfred			goto tr_stalled;
582184610Salfred		}
583184610Salfred		break;
584184610Salfred
585184610Salfred	case UT_WRITE_ENDPOINT:
586184610Salfred		switch (req.bRequest) {
587184610Salfred		case UR_CLEAR_FEATURE:
588184610Salfred			switch (wValue) {
589184610Salfred			case UF_ENDPOINT_HALT:
590184610Salfred				goto tr_handle_clear_halt;
591184610Salfred			default:
592184610Salfred				goto tr_stalled;
593184610Salfred			}
594184610Salfred			break;
595184610Salfred		case UR_SET_FEATURE:
596184610Salfred			switch (wValue) {
597184610Salfred			case UF_ENDPOINT_HALT:
598184610Salfred				goto tr_handle_set_halt;
599184610Salfred			default:
600184610Salfred				goto tr_stalled;
601184610Salfred			}
602184610Salfred			break;
603184610Salfred		default:
604184610Salfred			goto tr_stalled;
605184610Salfred		}
606184610Salfred		break;
607184610Salfred
608184610Salfred	case UT_READ_ENDPOINT:
609184610Salfred		switch (req.bRequest) {
610184610Salfred		case UR_GET_STATUS:
611184610Salfred			goto tr_handle_get_ep_status;
612184610Salfred		default:
613184610Salfred			goto tr_stalled;
614184610Salfred		}
615184610Salfred		break;
616184610Salfred	default:
617184610Salfred		/* we use "USB_ADD_BYTES" to de-const the src_zcopy */
618194228Sthompsa		err = usb_handle_iface_request(xfer,
619184610Salfred		    USB_ADD_BYTES(&src_zcopy, 0),
620184610Salfred		    &max_len, req, off, state);
621184610Salfred		if (err == 0) {
622195121Sthompsa			is_complete = 0;
623184610Salfred			goto tr_valid;
624195121Sthompsa		} else if (err == USB_ERR_SHORT_XFER) {
625195121Sthompsa			goto tr_valid;
626184610Salfred		}
627184610Salfred		/*
628184610Salfred		 * Reset zero-copy pointer and max length
629184610Salfred		 * variable in case they were unintentionally
630184610Salfred		 * set:
631184610Salfred		 */
632184610Salfred		src_zcopy = NULL;
633184610Salfred		max_len = 0;
634184610Salfred
635184610Salfred		/*
636184610Salfred		 * Check if we have a vendor specific
637184610Salfred		 * descriptor:
638184610Salfred		 */
639184610Salfred		goto tr_handle_get_descriptor;
640184610Salfred	}
641184610Salfred	goto tr_valid;
642184610Salfred
643184610Salfredtr_handle_get_descriptor:
644194228Sthompsa	err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
645191402Sthompsa	if (err)
646184610Salfred		goto tr_stalled;
647191402Sthompsa	if (src_zcopy == NULL)
648191402Sthompsa		goto tr_stalled;
649184610Salfred	goto tr_valid;
650184610Salfred
651184610Salfredtr_handle_get_config:
652184610Salfred	temp.buf[0] = udev->curr_config_no;
653184610Salfred	src_mcopy = temp.buf;
654184610Salfred	max_len = 1;
655184610Salfred	goto tr_valid;
656184610Salfred
657184610Salfredtr_handle_get_status:
658184610Salfred
659184610Salfred	wValue = 0;
660184610Salfred
661184824Sthompsa	USB_BUS_LOCK(udev->bus);
662184610Salfred	if (udev->flags.remote_wakeup) {
663184610Salfred		wValue |= UDS_REMOTE_WAKEUP;
664184610Salfred	}
665184610Salfred	if (udev->flags.self_powered) {
666184610Salfred		wValue |= UDS_SELF_POWERED;
667184610Salfred	}
668184824Sthompsa	USB_BUS_UNLOCK(udev->bus);
669184610Salfred
670184610Salfred	USETW(temp.wStatus, wValue);
671184610Salfred	src_mcopy = temp.wStatus;
672184610Salfred	max_len = sizeof(temp.wStatus);
673184610Salfred	goto tr_valid;
674184610Salfred
675184610Salfredtr_handle_set_address:
676194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
677184610Salfred		if (wValue >= 0x80) {
678184610Salfred			/* invalid value */
679184610Salfred			goto tr_stalled;
680184610Salfred		} else if (udev->curr_config_no != 0) {
681184610Salfred			/* we are configured ! */
682184610Salfred			goto tr_stalled;
683184610Salfred		}
684194064Sthompsa	} else if (state != USB_HR_NOT_COMPLETE) {
685184610Salfred		udev->address = (wValue & 0x7F);
686184610Salfred		goto tr_bad_context;
687184610Salfred	}
688184610Salfred	goto tr_valid;
689184610Salfred
690184610Salfredtr_handle_set_config:
691194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
692194228Sthompsa		if (usb_handle_set_config(xfer, req.wValue[0])) {
693184610Salfred			goto tr_stalled;
694184610Salfred		}
695184610Salfred	}
696184610Salfred	goto tr_valid;
697184610Salfred
698184610Salfredtr_handle_clear_halt:
699194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
700194228Sthompsa		if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) {
701184610Salfred			goto tr_stalled;
702184610Salfred		}
703184610Salfred	}
704184610Salfred	goto tr_valid;
705184610Salfred
706184610Salfredtr_handle_clear_wakeup:
707194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
708194228Sthompsa		if (usb_handle_remote_wakeup(xfer, 0)) {
709184610Salfred			goto tr_stalled;
710184610Salfred		}
711184610Salfred	}
712184610Salfred	goto tr_valid;
713184610Salfred
714184610Salfredtr_handle_set_halt:
715194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
716194228Sthompsa		if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) {
717184610Salfred			goto tr_stalled;
718184610Salfred		}
719184610Salfred	}
720184610Salfred	goto tr_valid;
721184610Salfred
722184610Salfredtr_handle_set_wakeup:
723194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
724194228Sthompsa		if (usb_handle_remote_wakeup(xfer, 1)) {
725184610Salfred			goto tr_stalled;
726184610Salfred		}
727184610Salfred	}
728184610Salfred	goto tr_valid;
729184610Salfred
730184610Salfredtr_handle_get_ep_status:
731194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
732184610Salfred		temp.wStatus[0] =
733194228Sthompsa		    usb_handle_get_stall(udev, req.wIndex[0]);
734184610Salfred		temp.wStatus[1] = 0;
735184610Salfred		src_mcopy = temp.wStatus;
736184610Salfred		max_len = sizeof(temp.wStatus);
737184610Salfred	}
738184610Salfred	goto tr_valid;
739184610Salfred
740184610Salfredtr_valid:
741194064Sthompsa	if (state != USB_HR_NOT_COMPLETE) {
742184610Salfred		goto tr_stalled;
743184610Salfred	}
744184610Salfred	/* subtract offset from length */
745184610Salfred
746184610Salfred	max_len -= off;
747184610Salfred
748184610Salfred	/* Compute the real maximum data length */
749184610Salfred
750184610Salfred	if (max_len > xfer->max_data_length) {
751194677Sthompsa		max_len = usbd_xfer_max_len(xfer);
752184610Salfred	}
753184610Salfred	if (max_len > rem) {
754184610Salfred		max_len = rem;
755184610Salfred	}
756184610Salfred	/*
757184610Salfred	 * If the remainder is greater than the maximum data length,
758184610Salfred	 * we need to truncate the value for the sake of the
759184610Salfred	 * comparison below:
760184610Salfred	 */
761184610Salfred	if (rem > xfer->max_data_length) {
762194677Sthompsa		rem = usbd_xfer_max_len(xfer);
763184610Salfred	}
764195121Sthompsa	if ((rem != max_len) && (is_complete != 0)) {
765184610Salfred		/*
766184610Salfred	         * If we don't transfer the data we can transfer, then
767184610Salfred	         * the transfer is short !
768184610Salfred	         */
769184610Salfred		xfer->flags.force_short_xfer = 1;
770184610Salfred		xfer->nframes = 2;
771184610Salfred	} else {
772184610Salfred		/*
773184610Salfred		 * Default case
774184610Salfred		 */
775184610Salfred		xfer->flags.force_short_xfer = 0;
776184610Salfred		xfer->nframes = max_len ? 2 : 1;
777184610Salfred	}
778184610Salfred	if (max_len > 0) {
779184610Salfred		if (src_mcopy) {
780184610Salfred			src_mcopy = USB_ADD_BYTES(src_mcopy, off);
781194228Sthompsa			usbd_copy_in(xfer->frbuffers + 1, 0,
782184610Salfred			    src_mcopy, max_len);
783194677Sthompsa			usbd_xfer_set_frame_len(xfer, 1, max_len);
784184610Salfred		} else {
785194677Sthompsa			usbd_xfer_set_frame_data(xfer, 1,
786194677Sthompsa			    USB_ADD_BYTES(src_zcopy, off), max_len);
787184610Salfred		}
788184610Salfred	} else {
789184610Salfred		/* the end is reached, send status */
790184610Salfred		xfer->flags.manual_status = 0;
791194677Sthompsa		usbd_xfer_set_frame_len(xfer, 1, 0);
792184610Salfred	}
793184610Salfred	DPRINTF("success\n");
794184610Salfred	return (0);			/* success */
795184610Salfred
796184610Salfredtr_stalled:
797194064Sthompsa	DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ?
798184610Salfred	    "complete" : "stalled");
799184610Salfred	return (USB_ERR_STALLED);
800184610Salfred
801184610Salfredtr_bad_context:
802184610Salfred	DPRINTF("bad context\n");
803184610Salfred	return (USB_ERR_BAD_CONTEXT);
804184610Salfred}
805