usb_handle_request.c revision 193045
1214501Srpaulo/* $FreeBSD: head/sys/dev/usb/usb_handle_request.c 193045 2009-05-29 18:46:57Z thompsa $ */
2214501Srpaulo/*-
3214501Srpaulo * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
4214501Srpaulo *
5252190Srpaulo * Redistribution and use in source and binary forms, with or without
6252190Srpaulo * modification, are permitted provided that the following conditions
7214501Srpaulo * are met:
8214501Srpaulo * 1. Redistributions of source code must retain the above copyright
9214501Srpaulo *    notice, this list of conditions and the following disclaimer.
10214501Srpaulo * 2. Redistributions in binary form must reproduce the above copyright
11214501Srpaulo *    notice, this list of conditions and the following disclaimer in the
12214501Srpaulo *    documentation and/or other materials provided with the distribution.
13214501Srpaulo *
14214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15214501Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16214501Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17214501Srpaulo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18214501Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19214501Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20214501Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21214501Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22214501Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23214501Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24214501Srpaulo * SUCH DAMAGE.
25214501Srpaulo */
26214501Srpaulo
27214501Srpaulo#include <dev/usb/usb_mfunc.h>
28214501Srpaulo#include <dev/usb/usb_error.h>
29214501Srpaulo#include <dev/usb/usb.h>
30214501Srpaulo
31214501Srpaulo#define	USB_DEBUG_VAR usb2_debug
32214501Srpaulo
33214501Srpaulo#include <dev/usb/usb_core.h>
34214501Srpaulo#include <dev/usb/usb_process.h>
35214501Srpaulo#include <dev/usb/usb_busdma.h>
36214501Srpaulo#include <dev/usb/usb_transfer.h>
37214501Srpaulo#include <dev/usb/usb_device.h>
38214501Srpaulo#include <dev/usb/usb_debug.h>
39214501Srpaulo#include <dev/usb/usb_dynamic.h>
40214501Srpaulo#include <dev/usb/usb_hub.h>
41214501Srpaulo
42214501Srpaulo#include <dev/usb/usb_controller.h>
43214501Srpaulo#include <dev/usb/usb_bus.h>
44214501Srpaulo
45214501Srpaulo/* enum */
46214501Srpaulo
47214501Srpauloenum {
48214501Srpaulo	ST_DATA,
49214501Srpaulo	ST_POST_STATUS,
50214501Srpaulo};
51214501Srpaulo
52214501Srpaulo/* function prototypes */
53214501Srpaulo
54214501Srpaulostatic uint8_t usb2_handle_get_stall(struct usb_device *, uint8_t);
55214501Srpaulostatic usb_error_t	 usb2_handle_remote_wakeup(struct usb_xfer *, uint8_t);
56214501Srpaulostatic usb_error_t	 usb2_handle_request(struct usb_xfer *);
57214501Srpaulostatic usb_error_t	 usb2_handle_set_config(struct usb_xfer *, uint8_t);
58214501Srpaulostatic usb_error_t	 usb2_handle_set_stall(struct usb_xfer *, uint8_t,
59214501Srpaulo			    uint8_t);
60214501Srpaulostatic usb_error_t	 usb2_handle_iface_request(struct usb_xfer *, void **,
61214501Srpaulo			    uint16_t *, struct usb_device_request, uint16_t,
62214501Srpaulo			    uint8_t);
63214501Srpaulo
64214501Srpaulo/*------------------------------------------------------------------------*
65214501Srpaulo *	usb2_handle_request_callback
66214501Srpaulo *
67214501Srpaulo * This function is the USB callback for generic USB Device control
68214501Srpaulo * transfers.
69214501Srpaulo *------------------------------------------------------------------------*/
70214501Srpaulovoid
71214501Srpaulousb2_handle_request_callback(struct usb_xfer *xfer)
72214501Srpaulo{
73214501Srpaulo	usb_error_t err;
74214501Srpaulo
75214501Srpaulo	/* check the current transfer state */
76214501Srpaulo
77214501Srpaulo	switch (USB_GET_STATE(xfer)) {
78214501Srpaulo	case USB_ST_SETUP:
79214501Srpaulo	case USB_ST_TRANSFERRED:
80214501Srpaulo
81214501Srpaulo		/* handle the request */
82214501Srpaulo		err = usb2_handle_request(xfer);
83214501Srpaulo
84214501Srpaulo		if (err) {
85214501Srpaulo
86214501Srpaulo			if (err == USB_ERR_BAD_CONTEXT) {
87214501Srpaulo				/* we need to re-setup the control transfer */
88214501Srpaulo				usb2_needs_explore(xfer->xroot->bus, 0);
89214501Srpaulo				break;
90214501Srpaulo			}
91214501Srpaulo			/*
92214501Srpaulo		         * If no control transfer is active,
93214501Srpaulo		         * receive the next SETUP message:
94214501Srpaulo		         */
95214501Srpaulo			goto tr_restart;
96214501Srpaulo		}
97214501Srpaulo		usb2_start_hardware(xfer);
98214501Srpaulo		break;
99214501Srpaulo
100214501Srpaulo	default:
101214501Srpaulo		if (xfer->error != USB_ERR_CANCELLED) {
102214501Srpaulo			/* should not happen - try stalling */
103214501Srpaulo			goto tr_restart;
104214501Srpaulo		}
105214501Srpaulo		break;
106214501Srpaulo	}
107214501Srpaulo	return;
108214501Srpaulo
109214501Srpaulotr_restart:
110214501Srpaulo	xfer->frlengths[0] = sizeof(struct usb_device_request);
111214501Srpaulo	xfer->nframes = 1;
112214501Srpaulo	xfer->flags.manual_status = 1;
113252190Srpaulo	xfer->flags.force_short_xfer = 0;
114214501Srpaulo	xfer->flags.stall_pipe = 1;	/* cancel previous transfer, if any */
115214501Srpaulo	usb2_start_hardware(xfer);
116214501Srpaulo}
117214501Srpaulo
118214501Srpaulo/*------------------------------------------------------------------------*
119214501Srpaulo *	usb2_handle_set_config
120214501Srpaulo *
121214501Srpaulo * Returns:
122214501Srpaulo *    0: Success
123214501Srpaulo * Else: Failure
124214501Srpaulo *------------------------------------------------------------------------*/
125214501Srpaulostatic usb_error_t
126214501Srpaulousb2_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no)
127214501Srpaulo{
128214501Srpaulo	struct usb_device *udev = xfer->xroot->udev;
129214501Srpaulo	usb_error_t err = 0;
130214501Srpaulo
131214501Srpaulo	/*
132214501Srpaulo	 * We need to protect against other threads doing probe and
133214501Srpaulo	 * attach:
134214501Srpaulo	 */
135214501Srpaulo	USB_XFER_UNLOCK(xfer);
136214501Srpaulo	mtx_lock(&Giant);		/* XXX */
137214501Srpaulo	sx_xlock(udev->default_sx + 1);
138214501Srpaulo
139214501Srpaulo	if (conf_no == USB_UNCONFIG_NO) {
140214501Srpaulo		conf_no = USB_UNCONFIG_INDEX;
141214501Srpaulo	} else {
142214501Srpaulo		/*
143214501Srpaulo		 * The relationship between config number and config index
144214501Srpaulo		 * is very simple in our case:
145214501Srpaulo		 */
146214501Srpaulo		conf_no--;
147214501Srpaulo	}
148214501Srpaulo
149214501Srpaulo	if (usb2_set_config_index(udev, conf_no)) {
150214501Srpaulo		DPRINTF("set config %d failed\n", conf_no);
151214501Srpaulo		err = USB_ERR_STALLED;
152214501Srpaulo		goto done;
153214501Srpaulo	}
154214501Srpaulo	if (usb2_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
155214501Srpaulo		DPRINTF("probe and attach failed\n");
156214501Srpaulo		err = USB_ERR_STALLED;
157214501Srpaulo		goto done;
158214501Srpaulo	}
159214501Srpaulodone:
160214501Srpaulo	mtx_unlock(&Giant);		/* XXX */
161214501Srpaulo	sx_unlock(udev->default_sx + 1);
162214501Srpaulo	USB_XFER_LOCK(xfer);
163214501Srpaulo	return (err);
164214501Srpaulo}
165214501Srpaulo
166214501Srpaulo/*------------------------------------------------------------------------*
167214501Srpaulo *	usb2_handle_iface_request
168214501Srpaulo *
169214501Srpaulo * Returns:
170214501Srpaulo *    0: Success
171214501Srpaulo * Else: Failure
172214501Srpaulo *------------------------------------------------------------------------*/
173214501Srpaulostatic usb_error_t
174214501Srpaulousb2_handle_iface_request(struct usb_xfer *xfer,
175214501Srpaulo    void **ppdata, uint16_t *plen,
176214501Srpaulo    struct usb_device_request req, uint16_t off, uint8_t state)
177214501Srpaulo{
178214501Srpaulo	struct usb_interface *iface;
179214501Srpaulo	struct usb_interface *iface_parent;	/* parent interface */
180214501Srpaulo	struct usb_device *udev = xfer->xroot->udev;
181214501Srpaulo	int error;
182214501Srpaulo	uint8_t iface_index;
183214501Srpaulo
184214501Srpaulo	if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
185214501Srpaulo		iface_index = req.wIndex[0];	/* unicast */
186214501Srpaulo	} else {
187214501Srpaulo		iface_index = 0;	/* broadcast */
188214501Srpaulo	}
189214501Srpaulo
190214501Srpaulo	/*
191214501Srpaulo	 * We need to protect against other threads doing probe and
192214501Srpaulo	 * attach:
193214501Srpaulo	 */
194214501Srpaulo	USB_XFER_UNLOCK(xfer);
195214501Srpaulo	mtx_lock(&Giant);		/* XXX */
196214501Srpaulo	sx_xlock(udev->default_sx + 1);
197214501Srpaulo
198214501Srpaulo	error = ENXIO;
199214501Srpaulo
200214501Srpaulotr_repeat:
201214501Srpaulo	iface = usb2_get_iface(udev, iface_index);
202214501Srpaulo	if ((iface == NULL) ||
203214501Srpaulo	    (iface->idesc == NULL)) {
204214501Srpaulo		/* end of interfaces non-existing interface */
205214501Srpaulo		goto tr_stalled;
206214501Srpaulo	}
207214501Srpaulo	/* forward request to interface, if any */
208214501Srpaulo
209214501Srpaulo	if ((error != 0) &&
210214501Srpaulo	    (error != ENOTTY) &&
211214501Srpaulo	    (iface->subdev != NULL) &&
212214501Srpaulo	    device_is_attached(iface->subdev)) {
213214501Srpaulo#if 0
214214501Srpaulo		DEVMETHOD(usb_handle_request, NULL);	/* dummy */
215214501Srpaulo#endif
216214501Srpaulo		error = USB_HANDLE_REQUEST(iface->subdev,
217214501Srpaulo		    &req, ppdata, plen,
218214501Srpaulo		    off, (state == ST_POST_STATUS));
219214501Srpaulo	}
220214501Srpaulo	iface_parent = usb2_get_iface(udev, iface->parent_iface_index);
221214501Srpaulo
222214501Srpaulo	if ((iface_parent == NULL) ||
223214501Srpaulo	    (iface_parent->idesc == NULL)) {
224214501Srpaulo		/* non-existing interface */
225214501Srpaulo		iface_parent = NULL;
226252190Srpaulo	}
227214501Srpaulo	/* forward request to parent interface, if any */
228214501Srpaulo
229214501Srpaulo	if ((error != 0) &&
230214501Srpaulo	    (error != ENOTTY) &&
231214501Srpaulo	    (iface_parent != NULL) &&
232214501Srpaulo	    (iface_parent->subdev != NULL) &&
233214501Srpaulo	    ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
234214501Srpaulo	    (iface_parent->subdev != iface->subdev) &&
235214501Srpaulo	    device_is_attached(iface_parent->subdev)) {
236214501Srpaulo		error = USB_HANDLE_REQUEST(iface_parent->subdev,
237214501Srpaulo		    &req, ppdata, plen, off,
238214501Srpaulo		    (state == ST_POST_STATUS));
239214501Srpaulo	}
240214501Srpaulo	if (error == 0) {
241214501Srpaulo		/* negativly adjust pointer and length */
242214501Srpaulo		*ppdata = ((uint8_t *)(*ppdata)) - off;
243214501Srpaulo		*plen += off;
244214501Srpaulo		goto tr_valid;
245214501Srpaulo	} else if (error == ENOTTY) {
246214501Srpaulo		goto tr_stalled;
247214501Srpaulo	}
248214501Srpaulo	if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
249214501Srpaulo		iface_index++;		/* iterate */
250214501Srpaulo		goto tr_repeat;
251214501Srpaulo	}
252214501Srpaulo	if (state == ST_POST_STATUS) {
253214501Srpaulo		/* we are complete */
254214501Srpaulo		goto tr_valid;
255214501Srpaulo	}
256214501Srpaulo	switch (req.bmRequestType) {
257214501Srpaulo	case UT_WRITE_INTERFACE:
258214501Srpaulo		switch (req.bRequest) {
259214501Srpaulo		case UR_SET_INTERFACE:
260214501Srpaulo			/*
261214501Srpaulo			 * Handle special case. If we have parent interface
262214501Srpaulo			 * we just reset the endpoints, because this is a
263214501Srpaulo			 * multi interface device and re-attaching only a
264214501Srpaulo			 * part of the device is not possible. Also if the
265214501Srpaulo			 * alternate setting is the same like before we just
266214501Srpaulo			 * reset the interface endoints.
267214501Srpaulo			 */
268214501Srpaulo			if ((iface_parent != NULL) ||
269214501Srpaulo			    (iface->alt_index == req.wValue[0])) {
270214501Srpaulo				error = usb2_reset_iface_endpoints(udev,
271214501Srpaulo				    iface_index);
272214501Srpaulo				if (error) {
273214501Srpaulo					DPRINTF("alt setting failed %s\n",
274214501Srpaulo					    usb2_errstr(error));
275214501Srpaulo					goto tr_stalled;
276214501Srpaulo				}
277214501Srpaulo				break;
278214501Srpaulo			}
279214501Srpaulo			/*
280214501Srpaulo			 * Doing the alternate setting will detach the
281214501Srpaulo			 * interface aswell:
282214501Srpaulo			 */
283214501Srpaulo			error = usb2_set_alt_interface_index(udev,
284214501Srpaulo			    iface_index, req.wValue[0]);
285214501Srpaulo			if (error) {
286214501Srpaulo				DPRINTF("alt setting failed %s\n",
287214501Srpaulo				    usb2_errstr(error));
288214501Srpaulo				goto tr_stalled;
289214501Srpaulo			}
290214501Srpaulo			error = usb2_probe_and_attach(udev,
291214501Srpaulo			    iface_index);
292214501Srpaulo			if (error) {
293214501Srpaulo				DPRINTF("alt setting probe failed\n");
294214501Srpaulo				goto tr_stalled;
295214501Srpaulo			}
296214501Srpaulo			break;
297214501Srpaulo		default:
298214501Srpaulo			goto tr_stalled;
299214501Srpaulo		}
300214501Srpaulo		break;
301214501Srpaulo
302214501Srpaulo	case UT_READ_INTERFACE:
303214501Srpaulo		switch (req.bRequest) {
304214501Srpaulo		case UR_GET_INTERFACE:
305214501Srpaulo			*ppdata = &iface->alt_index;
306214501Srpaulo			*plen = 1;
307214501Srpaulo			break;
308214501Srpaulo
309214501Srpaulo		default:
310214501Srpaulo			goto tr_stalled;
311214501Srpaulo		}
312214501Srpaulo		break;
313214501Srpaulo	default:
314214501Srpaulo		goto tr_stalled;
315214501Srpaulo	}
316214501Srpaulotr_valid:
317214501Srpaulo	mtx_unlock(&Giant);
318214501Srpaulo	sx_unlock(udev->default_sx + 1);
319214501Srpaulo	USB_XFER_LOCK(xfer);
320214501Srpaulo	return (0);
321214501Srpaulo
322214501Srpaulotr_stalled:
323214501Srpaulo	mtx_unlock(&Giant);
324214501Srpaulo	sx_unlock(udev->default_sx + 1);
325214501Srpaulo	USB_XFER_LOCK(xfer);
326214501Srpaulo	return (USB_ERR_STALLED);
327214501Srpaulo}
328214501Srpaulo
329214501Srpaulo/*------------------------------------------------------------------------*
330214501Srpaulo *	usb2_handle_stall
331214501Srpaulo *
332214501Srpaulo * Returns:
333214501Srpaulo *    0: Success
334252190Srpaulo * Else: Failure
335214501Srpaulo *------------------------------------------------------------------------*/
336214501Srpaulostatic usb_error_t
337214501Srpaulousb2_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall)
338214501Srpaulo{
339214501Srpaulo	struct usb_device *udev = xfer->xroot->udev;
340214501Srpaulo	usb_error_t err;
341214501Srpaulo
342214501Srpaulo	USB_XFER_UNLOCK(xfer);
343214501Srpaulo	err = usb2_set_endpoint_stall(udev,
344214501Srpaulo	    usb2_get_pipe_by_addr(udev, ep), do_stall);
345214501Srpaulo	USB_XFER_LOCK(xfer);
346214501Srpaulo	return (err);
347214501Srpaulo}
348214501Srpaulo
349214501Srpaulo/*------------------------------------------------------------------------*
350214501Srpaulo *	usb2_handle_get_stall
351214501Srpaulo *
352214501Srpaulo * Returns:
353214501Srpaulo *    0: Success
354214501Srpaulo * Else: Failure
355214501Srpaulo *------------------------------------------------------------------------*/
356214501Srpaulostatic uint8_t
357214501Srpaulousb2_handle_get_stall(struct usb_device *udev, uint8_t ea_val)
358214501Srpaulo{
359214501Srpaulo	struct usb_pipe *pipe;
360214501Srpaulo	uint8_t halted;
361214501Srpaulo
362214501Srpaulo	pipe = usb2_get_pipe_by_addr(udev, ea_val);
363214501Srpaulo	if (pipe == NULL) {
364214501Srpaulo		/* nothing to do */
365214501Srpaulo		return (0);
366214501Srpaulo	}
367214501Srpaulo	USB_BUS_LOCK(udev->bus);
368214501Srpaulo	halted = pipe->is_stalled;
369214501Srpaulo	USB_BUS_UNLOCK(udev->bus);
370214501Srpaulo
371214501Srpaulo	return (halted);
372214501Srpaulo}
373214501Srpaulo
374214501Srpaulo/*------------------------------------------------------------------------*
375214501Srpaulo *	usb2_handle_remote_wakeup
376214501Srpaulo *
377214501Srpaulo * Returns:
378214501Srpaulo *    0: Success
379214501Srpaulo * Else: Failure
380214501Srpaulo *------------------------------------------------------------------------*/
381214501Srpaulostatic usb_error_t
382214501Srpaulousb2_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on)
383214501Srpaulo{
384214501Srpaulo	struct usb_device *udev;
385214501Srpaulo	struct usb_bus *bus;
386214501Srpaulo
387214501Srpaulo	udev = xfer->xroot->udev;
388214501Srpaulo	bus = udev->bus;
389214501Srpaulo
390214501Srpaulo	USB_BUS_LOCK(bus);
391214501Srpaulo
392214501Srpaulo	if (is_on) {
393214501Srpaulo		udev->flags.remote_wakeup = 1;
394214501Srpaulo	} else {
395214501Srpaulo		udev->flags.remote_wakeup = 0;
396214501Srpaulo	}
397214501Srpaulo
398214501Srpaulo	USB_BUS_UNLOCK(bus);
399214501Srpaulo
400214501Srpaulo	/* In case we are out of sync, update the power state. */
401214501Srpaulo	usb2_bus_power_update(udev->bus);
402214501Srpaulo	return (0);			/* success */
403214501Srpaulo}
404214501Srpaulo
405214501Srpaulo/*------------------------------------------------------------------------*
406214501Srpaulo *	usb2_handle_request
407214501Srpaulo *
408214501Srpaulo * Internal state sequence:
409214501Srpaulo *
410214501Srpaulo * ST_DATA -> ST_POST_STATUS
411214501Srpaulo *
412214501Srpaulo * Returns:
413214501Srpaulo * 0: Ready to start hardware
414214501Srpaulo * Else: Stall current transfer, if any
415214501Srpaulo *------------------------------------------------------------------------*/
416214501Srpaulostatic usb_error_t
417214501Srpaulousb2_handle_request(struct usb_xfer *xfer)
418214501Srpaulo{
419214501Srpaulo	struct usb_device_request req;
420214501Srpaulo	struct usb_device *udev;
421214501Srpaulo	const void *src_zcopy;		/* zero-copy source pointer */
422214501Srpaulo	const void *src_mcopy;		/* non zero-copy source pointer */
423214501Srpaulo	uint16_t off;			/* data offset */
424214501Srpaulo	uint16_t rem;			/* data remainder */
425214501Srpaulo	uint16_t max_len;		/* max fragment length */
426214501Srpaulo	uint16_t wValue;
427214501Srpaulo	uint16_t wIndex;
428214501Srpaulo	uint8_t state;
429214501Srpaulo	usb_error_t err;
430214501Srpaulo	union {
431214501Srpaulo		uWord	wStatus;
432214501Srpaulo		uint8_t	buf[2];
433214501Srpaulo	}     temp;
434214501Srpaulo
435214501Srpaulo	/*
436214501Srpaulo	 * Filter the USB transfer state into
437214501Srpaulo	 * something which we understand:
438214501Srpaulo	 */
439214501Srpaulo
440214501Srpaulo	switch (USB_GET_STATE(xfer)) {
441214501Srpaulo	case USB_ST_SETUP:
442214501Srpaulo		state = ST_DATA;
443214501Srpaulo
444214501Srpaulo		if (!xfer->flags_int.control_act) {
445214501Srpaulo			/* nothing to do */
446214501Srpaulo			goto tr_stalled;
447214501Srpaulo		}
448214501Srpaulo		break;
449214501Srpaulo
450214501Srpaulo	default:			/* USB_ST_TRANSFERRED */
451214501Srpaulo		if (!xfer->flags_int.control_act) {
452214501Srpaulo			state = ST_POST_STATUS;
453214501Srpaulo		} else {
454214501Srpaulo			state = ST_DATA;
455214501Srpaulo		}
456214501Srpaulo		break;
457214501Srpaulo	}
458214501Srpaulo
459214501Srpaulo	/* reset frame stuff */
460214501Srpaulo
461214501Srpaulo	xfer->frlengths[0] = 0;
462214501Srpaulo
463214501Srpaulo	usb2_set_frame_offset(xfer, 0, 0);
464214501Srpaulo	usb2_set_frame_offset(xfer, sizeof(req), 1);
465214501Srpaulo
466214501Srpaulo	/* get the current request, if any */
467214501Srpaulo
468214501Srpaulo	usb2_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
469214501Srpaulo
470214501Srpaulo	if (xfer->flags_int.control_rem == 0xFFFF) {
471214501Srpaulo		/* first time - not initialised */
472214501Srpaulo		rem = UGETW(req.wLength);
473214501Srpaulo		off = 0;
474214501Srpaulo	} else {
475214501Srpaulo		/* not first time - initialised */
476214501Srpaulo		rem = xfer->flags_int.control_rem;
477214501Srpaulo		off = UGETW(req.wLength) - rem;
478214501Srpaulo	}
479214501Srpaulo
480214501Srpaulo	/* set some defaults */
481214501Srpaulo
482214501Srpaulo	max_len = 0;
483214501Srpaulo	src_zcopy = NULL;
484214501Srpaulo	src_mcopy = NULL;
485214501Srpaulo	udev = xfer->xroot->udev;
486214501Srpaulo
487214501Srpaulo	/* get some request fields decoded */
488214501Srpaulo
489214501Srpaulo	wValue = UGETW(req.wValue);
490214501Srpaulo	wIndex = UGETW(req.wIndex);
491214501Srpaulo
492214501Srpaulo	DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
493214501Srpaulo	    "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
494214501Srpaulo	    req.bRequest, wValue, wIndex, off, rem, state);
495214501Srpaulo
496214501Srpaulo	/* demultiplex the control request */
497214501Srpaulo
498214501Srpaulo	switch (req.bmRequestType) {
499214501Srpaulo	case UT_READ_DEVICE:
500214501Srpaulo		if (state != ST_DATA) {
501214501Srpaulo			break;
502214501Srpaulo		}
503214501Srpaulo		switch (req.bRequest) {
504214501Srpaulo		case UR_GET_DESCRIPTOR:
505214501Srpaulo			goto tr_handle_get_descriptor;
506214501Srpaulo		case UR_GET_CONFIG:
507214501Srpaulo			goto tr_handle_get_config;
508214501Srpaulo		case UR_GET_STATUS:
509214501Srpaulo			goto tr_handle_get_status;
510214501Srpaulo		default:
511214501Srpaulo			goto tr_stalled;
512214501Srpaulo		}
513214501Srpaulo		break;
514214501Srpaulo
515214501Srpaulo	case UT_WRITE_DEVICE:
516214501Srpaulo		switch (req.bRequest) {
517214501Srpaulo		case UR_SET_ADDRESS:
518214501Srpaulo			goto tr_handle_set_address;
519214501Srpaulo		case UR_SET_CONFIG:
520214501Srpaulo			goto tr_handle_set_config;
521214501Srpaulo		case UR_CLEAR_FEATURE:
522214501Srpaulo			switch (wValue) {
523214501Srpaulo			case UF_DEVICE_REMOTE_WAKEUP:
524214501Srpaulo				goto tr_handle_clear_wakeup;
525214501Srpaulo			default:
526214501Srpaulo				goto tr_stalled;
527214501Srpaulo			}
528214501Srpaulo			break;
529214501Srpaulo		case UR_SET_FEATURE:
530214501Srpaulo			switch (wValue) {
531214501Srpaulo			case UF_DEVICE_REMOTE_WAKEUP:
532214501Srpaulo				goto tr_handle_set_wakeup;
533214501Srpaulo			default:
534214501Srpaulo				goto tr_stalled;
535214501Srpaulo			}
536214501Srpaulo			break;
537214501Srpaulo		default:
538214501Srpaulo			goto tr_stalled;
539214501Srpaulo		}
540214501Srpaulo		break;
541214501Srpaulo
542214501Srpaulo	case UT_WRITE_ENDPOINT:
543214501Srpaulo		switch (req.bRequest) {
544214501Srpaulo		case UR_CLEAR_FEATURE:
545214501Srpaulo			switch (wValue) {
546214501Srpaulo			case UF_ENDPOINT_HALT:
547214501Srpaulo				goto tr_handle_clear_halt;
548214501Srpaulo			default:
549214501Srpaulo				goto tr_stalled;
550214501Srpaulo			}
551214501Srpaulo			break;
552214501Srpaulo		case UR_SET_FEATURE:
553214501Srpaulo			switch (wValue) {
554214501Srpaulo			case UF_ENDPOINT_HALT:
555214501Srpaulo				goto tr_handle_set_halt;
556214501Srpaulo			default:
557214501Srpaulo				goto tr_stalled;
558214501Srpaulo			}
559214501Srpaulo			break;
560214501Srpaulo		default:
561214501Srpaulo			goto tr_stalled;
562214501Srpaulo		}
563214501Srpaulo		break;
564214501Srpaulo
565214501Srpaulo	case UT_READ_ENDPOINT:
566214501Srpaulo		switch (req.bRequest) {
567214501Srpaulo		case UR_GET_STATUS:
568214501Srpaulo			goto tr_handle_get_ep_status;
569214501Srpaulo		default:
570214501Srpaulo			goto tr_stalled;
571214501Srpaulo		}
572214501Srpaulo		break;
573214501Srpaulo	default:
574214501Srpaulo		/* we use "USB_ADD_BYTES" to de-const the src_zcopy */
575214501Srpaulo		err = usb2_handle_iface_request(xfer,
576214501Srpaulo		    USB_ADD_BYTES(&src_zcopy, 0),
577214501Srpaulo		    &max_len, req, off, state);
578214501Srpaulo		if (err == 0) {
579214501Srpaulo			goto tr_valid;
580214501Srpaulo		}
581214501Srpaulo		/*
582214501Srpaulo		 * Reset zero-copy pointer and max length
583214501Srpaulo		 * variable in case they were unintentionally
584214501Srpaulo		 * set:
585214501Srpaulo		 */
586214501Srpaulo		src_zcopy = NULL;
587214501Srpaulo		max_len = 0;
588214501Srpaulo
589214501Srpaulo		/*
590214501Srpaulo		 * Check if we have a vendor specific
591214501Srpaulo		 * descriptor:
592214501Srpaulo		 */
593214501Srpaulo		goto tr_handle_get_descriptor;
594214501Srpaulo	}
595214501Srpaulo	goto tr_valid;
596214501Srpaulo
597214501Srpaulotr_handle_get_descriptor:
598214501Srpaulo	err = (usb2_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
599214501Srpaulo	if (err)
600214501Srpaulo		goto tr_stalled;
601214501Srpaulo	if (src_zcopy == NULL)
602214501Srpaulo		goto tr_stalled;
603214501Srpaulo	goto tr_valid;
604214501Srpaulo
605214501Srpaulotr_handle_get_config:
606214501Srpaulo	temp.buf[0] = udev->curr_config_no;
607214501Srpaulo	src_mcopy = temp.buf;
608214501Srpaulo	max_len = 1;
609214501Srpaulo	goto tr_valid;
610214501Srpaulo
611214501Srpaulotr_handle_get_status:
612214501Srpaulo
613214501Srpaulo	wValue = 0;
614214501Srpaulo
615214501Srpaulo	USB_BUS_LOCK(udev->bus);
616214501Srpaulo	if (udev->flags.remote_wakeup) {
617214501Srpaulo		wValue |= UDS_REMOTE_WAKEUP;
618214501Srpaulo	}
619214501Srpaulo	if (udev->flags.self_powered) {
620214501Srpaulo		wValue |= UDS_SELF_POWERED;
621214501Srpaulo	}
622214501Srpaulo	USB_BUS_UNLOCK(udev->bus);
623214501Srpaulo
624214501Srpaulo	USETW(temp.wStatus, wValue);
625214501Srpaulo	src_mcopy = temp.wStatus;
626214501Srpaulo	max_len = sizeof(temp.wStatus);
627214501Srpaulo	goto tr_valid;
628214501Srpaulo
629214501Srpaulotr_handle_set_address:
630214501Srpaulo	if (state == ST_DATA) {
631214501Srpaulo		if (wValue >= 0x80) {
632214501Srpaulo			/* invalid value */
633214501Srpaulo			goto tr_stalled;
634214501Srpaulo		} else if (udev->curr_config_no != 0) {
635214501Srpaulo			/* we are configured ! */
636214501Srpaulo			goto tr_stalled;
637214501Srpaulo		}
638214501Srpaulo	} else if (state == ST_POST_STATUS) {
639214501Srpaulo		udev->address = (wValue & 0x7F);
640214501Srpaulo		goto tr_bad_context;
641214501Srpaulo	}
642214501Srpaulo	goto tr_valid;
643214501Srpaulo
644214501Srpaulotr_handle_set_config:
645214501Srpaulo	if (state == ST_DATA) {
646214501Srpaulo		if (usb2_handle_set_config(xfer, req.wValue[0])) {
647214501Srpaulo			goto tr_stalled;
648214501Srpaulo		}
649214501Srpaulo	}
650214501Srpaulo	goto tr_valid;
651214501Srpaulo
652214501Srpaulotr_handle_clear_halt:
653214501Srpaulo	if (state == ST_DATA) {
654214501Srpaulo		if (usb2_handle_set_stall(xfer, req.wIndex[0], 0)) {
655214501Srpaulo			goto tr_stalled;
656214501Srpaulo		}
657214501Srpaulo	}
658214501Srpaulo	goto tr_valid;
659214501Srpaulo
660214501Srpaulotr_handle_clear_wakeup:
661214501Srpaulo	if (state == ST_DATA) {
662214501Srpaulo		if (usb2_handle_remote_wakeup(xfer, 0)) {
663214501Srpaulo			goto tr_stalled;
664214501Srpaulo		}
665214501Srpaulo	}
666214501Srpaulo	goto tr_valid;
667214501Srpaulo
668214501Srpaulotr_handle_set_halt:
669214501Srpaulo	if (state == ST_DATA) {
670214501Srpaulo		if (usb2_handle_set_stall(xfer, req.wIndex[0], 1)) {
671214501Srpaulo			goto tr_stalled;
672214501Srpaulo		}
673214501Srpaulo	}
674214501Srpaulo	goto tr_valid;
675214501Srpaulo
676214501Srpaulotr_handle_set_wakeup:
677214501Srpaulo	if (state == ST_DATA) {
678214501Srpaulo		if (usb2_handle_remote_wakeup(xfer, 1)) {
679214501Srpaulo			goto tr_stalled;
680214501Srpaulo		}
681214501Srpaulo	}
682214501Srpaulo	goto tr_valid;
683214501Srpaulo
684214501Srpaulotr_handle_get_ep_status:
685214501Srpaulo	if (state == ST_DATA) {
686214501Srpaulo		temp.wStatus[0] =
687214501Srpaulo		    usb2_handle_get_stall(udev, req.wIndex[0]);
688214501Srpaulo		temp.wStatus[1] = 0;
689214501Srpaulo		src_mcopy = temp.wStatus;
690214501Srpaulo		max_len = sizeof(temp.wStatus);
691214501Srpaulo	}
692214501Srpaulo	goto tr_valid;
693214501Srpaulo
694214501Srpaulotr_valid:
695214501Srpaulo	if (state == ST_POST_STATUS) {
696214501Srpaulo		goto tr_stalled;
697214501Srpaulo	}
698214501Srpaulo	/* subtract offset from length */
699214501Srpaulo
700214501Srpaulo	max_len -= off;
701214501Srpaulo
702214501Srpaulo	/* Compute the real maximum data length */
703214501Srpaulo
704214501Srpaulo	if (max_len > xfer->max_data_length) {
705214501Srpaulo		max_len = xfer->max_data_length;
706214501Srpaulo	}
707214501Srpaulo	if (max_len > rem) {
708214501Srpaulo		max_len = rem;
709214501Srpaulo	}
710214501Srpaulo	/*
711214501Srpaulo	 * If the remainder is greater than the maximum data length,
712214501Srpaulo	 * we need to truncate the value for the sake of the
713214501Srpaulo	 * comparison below:
714214501Srpaulo	 */
715214501Srpaulo	if (rem > xfer->max_data_length) {
716214501Srpaulo		rem = xfer->max_data_length;
717214501Srpaulo	}
718214501Srpaulo	if (rem != max_len) {
719214501Srpaulo		/*
720214501Srpaulo	         * If we don't transfer the data we can transfer, then
721214501Srpaulo	         * the transfer is short !
722214501Srpaulo	         */
723214501Srpaulo		xfer->flags.force_short_xfer = 1;
724214501Srpaulo		xfer->nframes = 2;
725214501Srpaulo	} else {
726214501Srpaulo		/*
727214501Srpaulo		 * Default case
728214501Srpaulo		 */
729214501Srpaulo		xfer->flags.force_short_xfer = 0;
730214501Srpaulo		xfer->nframes = max_len ? 2 : 1;
731214501Srpaulo	}
732214501Srpaulo	if (max_len > 0) {
733214501Srpaulo		if (src_mcopy) {
734214501Srpaulo			src_mcopy = USB_ADD_BYTES(src_mcopy, off);
735214501Srpaulo			usb2_copy_in(xfer->frbuffers + 1, 0,
736214501Srpaulo			    src_mcopy, max_len);
737214501Srpaulo		} else {
738214501Srpaulo			usb2_set_frame_data(xfer,
739214501Srpaulo			    USB_ADD_BYTES(src_zcopy, off), 1);
740214501Srpaulo		}
741214501Srpaulo		xfer->frlengths[1] = max_len;
742214501Srpaulo	} else {
743214501Srpaulo		/* the end is reached, send status */
744214501Srpaulo		xfer->flags.manual_status = 0;
745214501Srpaulo		xfer->frlengths[1] = 0;
746214501Srpaulo	}
747214501Srpaulo	DPRINTF("success\n");
748214501Srpaulo	return (0);			/* success */
749214501Srpaulo
750214501Srpaulotr_stalled:
751214501Srpaulo	DPRINTF("%s\n", (state == ST_POST_STATUS) ?
752214501Srpaulo	    "complete" : "stalled");
753214501Srpaulo	return (USB_ERR_STALLED);
754214501Srpaulo
755214501Srpaulotr_bad_context:
756214501Srpaulo	DPRINTF("bad context\n");
757214501Srpaulo	return (USB_ERR_BAD_CONTEXT);
758214501Srpaulo}
759214501Srpaulo