usb_handle_request.c revision 194228
1184610Salfred/* $FreeBSD: head/sys/dev/usb/usb_handle_request.c 194228 2009-06-15 01:02:43Z thompsa $ */
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
27188942Sthompsa#include <dev/usb/usb_mfunc.h>
28188942Sthompsa#include <dev/usb/usb_error.h>
29188942Sthompsa#include <dev/usb/usb.h>
30184610Salfred
31194228Sthompsa#define	USB_DEBUG_VAR usb_debug
32184610Salfred
33188942Sthompsa#include <dev/usb/usb_core.h>
34188942Sthompsa#include <dev/usb/usb_process.h>
35188942Sthompsa#include <dev/usb/usb_busdma.h>
36188942Sthompsa#include <dev/usb/usb_transfer.h>
37188942Sthompsa#include <dev/usb/usb_device.h>
38188942Sthompsa#include <dev/usb/usb_debug.h>
39188942Sthompsa#include <dev/usb/usb_dynamic.h>
40188942Sthompsa#include <dev/usb/usb_hub.h>
41184610Salfred
42188942Sthompsa#include <dev/usb/usb_controller.h>
43188942Sthompsa#include <dev/usb/usb_bus.h>
44184610Salfred
45184610Salfred/* function prototypes */
46184610Salfred
47194228Sthompsastatic uint8_t usb_handle_get_stall(struct usb_device *, uint8_t);
48194228Sthompsastatic usb_error_t	 usb_handle_remote_wakeup(struct usb_xfer *, uint8_t);
49194228Sthompsastatic usb_error_t	 usb_handle_request(struct usb_xfer *);
50194228Sthompsastatic usb_error_t	 usb_handle_set_config(struct usb_xfer *, uint8_t);
51194228Sthompsastatic usb_error_t	 usb_handle_set_stall(struct usb_xfer *, uint8_t,
52185948Sthompsa			    uint8_t);
53194228Sthompsastatic usb_error_t	 usb_handle_iface_request(struct usb_xfer *, void **,
54192984Sthompsa			    uint16_t *, struct usb_device_request, uint16_t,
55185948Sthompsa			    uint8_t);
56184610Salfred
57184610Salfred/*------------------------------------------------------------------------*
58194228Sthompsa *	usb_handle_request_callback
59184610Salfred *
60184610Salfred * This function is the USB callback for generic USB Device control
61184610Salfred * transfers.
62184610Salfred *------------------------------------------------------------------------*/
63184610Salfredvoid
64194228Sthompsausb_handle_request_callback(struct usb_xfer *xfer)
65184610Salfred{
66193045Sthompsa	usb_error_t err;
67184610Salfred
68184610Salfred	/* check the current transfer state */
69184610Salfred
70184610Salfred	switch (USB_GET_STATE(xfer)) {
71184610Salfred	case USB_ST_SETUP:
72184610Salfred	case USB_ST_TRANSFERRED:
73184610Salfred
74184610Salfred		/* handle the request */
75194228Sthompsa		err = usb_handle_request(xfer);
76184610Salfred
77184610Salfred		if (err) {
78184610Salfred
79184610Salfred			if (err == USB_ERR_BAD_CONTEXT) {
80184610Salfred				/* we need to re-setup the control transfer */
81194228Sthompsa				usb_needs_explore(xfer->xroot->bus, 0);
82184610Salfred				break;
83184610Salfred			}
84184610Salfred			goto tr_restart;
85184610Salfred		}
86194228Sthompsa		usbd_transfer_submit(xfer);
87184610Salfred		break;
88184610Salfred
89184610Salfred	default:
90194064Sthompsa		/* check if a control transfer is active */
91194064Sthompsa		if (xfer->flags_int.control_rem != 0xFFFF) {
92194064Sthompsa			/* handle the request */
93194228Sthompsa			err = usb_handle_request(xfer);
94194064Sthompsa		}
95184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
96184610Salfred			/* should not happen - try stalling */
97184610Salfred			goto tr_restart;
98184610Salfred		}
99184610Salfred		break;
100184610Salfred	}
101184610Salfred	return;
102184610Salfred
103184610Salfredtr_restart:
104194064Sthompsa	/*
105194064Sthompsa	 * If a control transfer is active, stall it, and wait for the
106194064Sthompsa	 * next control transfer.
107194064Sthompsa	 */
108192984Sthompsa	xfer->frlengths[0] = sizeof(struct usb_device_request);
109184610Salfred	xfer->nframes = 1;
110184610Salfred	xfer->flags.manual_status = 1;
111184610Salfred	xfer->flags.force_short_xfer = 0;
112184610Salfred	xfer->flags.stall_pipe = 1;	/* cancel previous transfer, if any */
113194228Sthompsa	usbd_transfer_submit(xfer);
114184610Salfred}
115184610Salfred
116184610Salfred/*------------------------------------------------------------------------*
117194228Sthompsa *	usb_handle_set_config
118184610Salfred *
119184610Salfred * Returns:
120184610Salfred *    0: Success
121184610Salfred * Else: Failure
122184610Salfred *------------------------------------------------------------------------*/
123193045Sthompsastatic usb_error_t
124194228Sthompsausb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no)
125184610Salfred{
126192984Sthompsa	struct usb_device *udev = xfer->xroot->udev;
127193045Sthompsa	usb_error_t err = 0;
128184610Salfred
129184610Salfred	/*
130184610Salfred	 * We need to protect against other threads doing probe and
131184610Salfred	 * attach:
132184610Salfred	 */
133184824Sthompsa	USB_XFER_UNLOCK(xfer);
134184610Salfred	mtx_lock(&Giant);		/* XXX */
135187173Sthompsa	sx_xlock(udev->default_sx + 1);
136184610Salfred
137184610Salfred	if (conf_no == USB_UNCONFIG_NO) {
138184610Salfred		conf_no = USB_UNCONFIG_INDEX;
139184610Salfred	} else {
140184610Salfred		/*
141184610Salfred		 * The relationship between config number and config index
142184610Salfred		 * is very simple in our case:
143184610Salfred		 */
144184610Salfred		conf_no--;
145184610Salfred	}
146184610Salfred
147194228Sthompsa	if (usbd_set_config_index(udev, conf_no)) {
148184610Salfred		DPRINTF("set config %d failed\n", conf_no);
149184610Salfred		err = USB_ERR_STALLED;
150184610Salfred		goto done;
151184610Salfred	}
152194228Sthompsa	if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
153184610Salfred		DPRINTF("probe and attach failed\n");
154184610Salfred		err = USB_ERR_STALLED;
155184610Salfred		goto done;
156184610Salfred	}
157184610Salfreddone:
158184610Salfred	mtx_unlock(&Giant);		/* XXX */
159187173Sthompsa	sx_unlock(udev->default_sx + 1);
160184824Sthompsa	USB_XFER_LOCK(xfer);
161184610Salfred	return (err);
162184610Salfred}
163184610Salfred
164184610Salfred/*------------------------------------------------------------------------*
165194228Sthompsa *	usb_handle_iface_request
166184610Salfred *
167184610Salfred * Returns:
168184610Salfred *    0: Success
169184610Salfred * Else: Failure
170184610Salfred *------------------------------------------------------------------------*/
171193045Sthompsastatic usb_error_t
172194228Sthompsausb_handle_iface_request(struct usb_xfer *xfer,
173184610Salfred    void **ppdata, uint16_t *plen,
174192984Sthompsa    struct usb_device_request req, uint16_t off, uint8_t state)
175184610Salfred{
176192984Sthompsa	struct usb_interface *iface;
177192984Sthompsa	struct usb_interface *iface_parent;	/* parent interface */
178192984Sthompsa	struct usb_device *udev = xfer->xroot->udev;
179184610Salfred	int error;
180184610Salfred	uint8_t iface_index;
181184610Salfred
182184610Salfred	if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
183184610Salfred		iface_index = req.wIndex[0];	/* unicast */
184184610Salfred	} else {
185184610Salfred		iface_index = 0;	/* broadcast */
186184610Salfred	}
187184610Salfred
188184610Salfred	/*
189184610Salfred	 * We need to protect against other threads doing probe and
190184610Salfred	 * attach:
191184610Salfred	 */
192184824Sthompsa	USB_XFER_UNLOCK(xfer);
193184610Salfred	mtx_lock(&Giant);		/* XXX */
194184610Salfred	sx_xlock(udev->default_sx + 1);
195184610Salfred
196184610Salfred	error = ENXIO;
197184610Salfred
198184610Salfredtr_repeat:
199194228Sthompsa	iface = usbd_get_iface(udev, iface_index);
200184610Salfred	if ((iface == NULL) ||
201184610Salfred	    (iface->idesc == NULL)) {
202184610Salfred		/* end of interfaces non-existing interface */
203184610Salfred		goto tr_stalled;
204184610Salfred	}
205184610Salfred	/* forward request to interface, if any */
206184610Salfred
207184610Salfred	if ((error != 0) &&
208184610Salfred	    (error != ENOTTY) &&
209184610Salfred	    (iface->subdev != NULL) &&
210184610Salfred	    device_is_attached(iface->subdev)) {
211184610Salfred#if 0
212190186Sthompsa		DEVMETHOD(usb_handle_request, NULL);	/* dummy */
213184610Salfred#endif
214188942Sthompsa		error = USB_HANDLE_REQUEST(iface->subdev,
215184610Salfred		    &req, ppdata, plen,
216194064Sthompsa		    off, state);
217184610Salfred	}
218194228Sthompsa	iface_parent = usbd_get_iface(udev, iface->parent_iface_index);
219184610Salfred
220184610Salfred	if ((iface_parent == NULL) ||
221184610Salfred	    (iface_parent->idesc == NULL)) {
222184610Salfred		/* non-existing interface */
223184610Salfred		iface_parent = NULL;
224184610Salfred	}
225184610Salfred	/* forward request to parent interface, if any */
226184610Salfred
227184610Salfred	if ((error != 0) &&
228184610Salfred	    (error != ENOTTY) &&
229184610Salfred	    (iface_parent != NULL) &&
230184610Salfred	    (iface_parent->subdev != NULL) &&
231184610Salfred	    ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
232184610Salfred	    (iface_parent->subdev != iface->subdev) &&
233184610Salfred	    device_is_attached(iface_parent->subdev)) {
234188942Sthompsa		error = USB_HANDLE_REQUEST(iface_parent->subdev,
235184610Salfred		    &req, ppdata, plen, off,
236194064Sthompsa		    state);
237184610Salfred	}
238184610Salfred	if (error == 0) {
239184610Salfred		/* negativly adjust pointer and length */
240184610Salfred		*ppdata = ((uint8_t *)(*ppdata)) - off;
241184610Salfred		*plen += off;
242184610Salfred		goto tr_valid;
243184610Salfred	} else if (error == ENOTTY) {
244184610Salfred		goto tr_stalled;
245184610Salfred	}
246184610Salfred	if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
247184610Salfred		iface_index++;		/* iterate */
248184610Salfred		goto tr_repeat;
249184610Salfred	}
250194064Sthompsa	if (state != USB_HR_NOT_COMPLETE) {
251184610Salfred		/* we are complete */
252184610Salfred		goto tr_valid;
253184610Salfred	}
254184610Salfred	switch (req.bmRequestType) {
255184610Salfred	case UT_WRITE_INTERFACE:
256184610Salfred		switch (req.bRequest) {
257184610Salfred		case UR_SET_INTERFACE:
258184610Salfred			/*
259184610Salfred			 * Handle special case. If we have parent interface
260184610Salfred			 * we just reset the endpoints, because this is a
261184610Salfred			 * multi interface device and re-attaching only a
262184610Salfred			 * part of the device is not possible. Also if the
263184610Salfred			 * alternate setting is the same like before we just
264184610Salfred			 * reset the interface endoints.
265184610Salfred			 */
266184610Salfred			if ((iface_parent != NULL) ||
267184610Salfred			    (iface->alt_index == req.wValue[0])) {
268194228Sthompsa				error = usb_reset_iface_endpoints(udev,
269184610Salfred				    iface_index);
270184610Salfred				if (error) {
271184610Salfred					DPRINTF("alt setting failed %s\n",
272194228Sthompsa					    usbd_errstr(error));
273184610Salfred					goto tr_stalled;
274184610Salfred				}
275184610Salfred				break;
276184610Salfred			}
277188986Sthompsa			/*
278188986Sthompsa			 * Doing the alternate setting will detach the
279188986Sthompsa			 * interface aswell:
280188986Sthompsa			 */
281194228Sthompsa			error = usbd_set_alt_interface_index(udev,
282184610Salfred			    iface_index, req.wValue[0]);
283184610Salfred			if (error) {
284184610Salfred				DPRINTF("alt setting failed %s\n",
285194228Sthompsa				    usbd_errstr(error));
286184610Salfred				goto tr_stalled;
287184610Salfred			}
288194228Sthompsa			error = usb_probe_and_attach(udev,
289184610Salfred			    iface_index);
290184610Salfred			if (error) {
291184610Salfred				DPRINTF("alt setting probe failed\n");
292184610Salfred				goto tr_stalled;
293184610Salfred			}
294184610Salfred			break;
295184610Salfred		default:
296184610Salfred			goto tr_stalled;
297184610Salfred		}
298184610Salfred		break;
299184610Salfred
300184610Salfred	case UT_READ_INTERFACE:
301184610Salfred		switch (req.bRequest) {
302184610Salfred		case UR_GET_INTERFACE:
303184610Salfred			*ppdata = &iface->alt_index;
304184610Salfred			*plen = 1;
305184610Salfred			break;
306184610Salfred
307184610Salfred		default:
308184610Salfred			goto tr_stalled;
309184610Salfred		}
310184610Salfred		break;
311184610Salfred	default:
312184610Salfred		goto tr_stalled;
313184610Salfred	}
314184610Salfredtr_valid:
315184610Salfred	mtx_unlock(&Giant);
316184610Salfred	sx_unlock(udev->default_sx + 1);
317184824Sthompsa	USB_XFER_LOCK(xfer);
318184610Salfred	return (0);
319184610Salfred
320184610Salfredtr_stalled:
321184610Salfred	mtx_unlock(&Giant);
322184610Salfred	sx_unlock(udev->default_sx + 1);
323184824Sthompsa	USB_XFER_LOCK(xfer);
324184610Salfred	return (USB_ERR_STALLED);
325184610Salfred}
326184610Salfred
327184610Salfred/*------------------------------------------------------------------------*
328194228Sthompsa *	usb_handle_stall
329184610Salfred *
330184610Salfred * Returns:
331184610Salfred *    0: Success
332184610Salfred * Else: Failure
333184610Salfred *------------------------------------------------------------------------*/
334193045Sthompsastatic usb_error_t
335194228Sthompsausb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall)
336184610Salfred{
337192984Sthompsa	struct usb_device *udev = xfer->xroot->udev;
338193045Sthompsa	usb_error_t err;
339184610Salfred
340184824Sthompsa	USB_XFER_UNLOCK(xfer);
341194228Sthompsa	err = usbd_set_endpoint_stall(udev,
342194228Sthompsa	    usbd_get_ep_by_addr(udev, ep), do_stall);
343184824Sthompsa	USB_XFER_LOCK(xfer);
344184610Salfred	return (err);
345184610Salfred}
346184610Salfred
347184610Salfred/*------------------------------------------------------------------------*
348194228Sthompsa *	usb_handle_get_stall
349184610Salfred *
350184610Salfred * Returns:
351184610Salfred *    0: Success
352184610Salfred * Else: Failure
353184610Salfred *------------------------------------------------------------------------*/
354184610Salfredstatic uint8_t
355194228Sthompsausb_handle_get_stall(struct usb_device *udev, uint8_t ea_val)
356184610Salfred{
357193644Sthompsa	struct usb_endpoint *ep;
358184610Salfred	uint8_t halted;
359184610Salfred
360194228Sthompsa	ep = usbd_get_ep_by_addr(udev, ea_val);
361193644Sthompsa	if (ep == NULL) {
362184610Salfred		/* nothing to do */
363184610Salfred		return (0);
364184610Salfred	}
365184824Sthompsa	USB_BUS_LOCK(udev->bus);
366193644Sthompsa	halted = ep->is_stalled;
367184824Sthompsa	USB_BUS_UNLOCK(udev->bus);
368184610Salfred
369184610Salfred	return (halted);
370184610Salfred}
371184610Salfred
372184610Salfred/*------------------------------------------------------------------------*
373194228Sthompsa *	usb_handle_remote_wakeup
374184610Salfred *
375184610Salfred * Returns:
376184610Salfred *    0: Success
377184610Salfred * Else: Failure
378184610Salfred *------------------------------------------------------------------------*/
379193045Sthompsastatic usb_error_t
380194228Sthompsausb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on)
381184610Salfred{
382192984Sthompsa	struct usb_device *udev;
383192984Sthompsa	struct usb_bus *bus;
384184610Salfred
385187173Sthompsa	udev = xfer->xroot->udev;
386184610Salfred	bus = udev->bus;
387184610Salfred
388184824Sthompsa	USB_BUS_LOCK(bus);
389184610Salfred
390184610Salfred	if (is_on) {
391184610Salfred		udev->flags.remote_wakeup = 1;
392184610Salfred	} else {
393184610Salfred		udev->flags.remote_wakeup = 0;
394184610Salfred	}
395184610Salfred
396184824Sthompsa	USB_BUS_UNLOCK(bus);
397184610Salfred
398186730Salfred	/* In case we are out of sync, update the power state. */
399194228Sthompsa	usb_bus_power_update(udev->bus);
400184610Salfred	return (0);			/* success */
401184610Salfred}
402184610Salfred
403184610Salfred/*------------------------------------------------------------------------*
404194228Sthompsa *	usb_handle_request
405184610Salfred *
406184610Salfred * Internal state sequence:
407184610Salfred *
408194064Sthompsa * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR
409184610Salfred *
410184610Salfred * Returns:
411184610Salfred * 0: Ready to start hardware
412184610Salfred * Else: Stall current transfer, if any
413184610Salfred *------------------------------------------------------------------------*/
414193045Sthompsastatic usb_error_t
415194228Sthompsausb_handle_request(struct usb_xfer *xfer)
416184610Salfred{
417192984Sthompsa	struct usb_device_request req;
418192984Sthompsa	struct usb_device *udev;
419184610Salfred	const void *src_zcopy;		/* zero-copy source pointer */
420184610Salfred	const void *src_mcopy;		/* non zero-copy source pointer */
421184610Salfred	uint16_t off;			/* data offset */
422184610Salfred	uint16_t rem;			/* data remainder */
423184610Salfred	uint16_t max_len;		/* max fragment length */
424184610Salfred	uint16_t wValue;
425184610Salfred	uint16_t wIndex;
426184610Salfred	uint8_t state;
427193045Sthompsa	usb_error_t err;
428184610Salfred	union {
429184610Salfred		uWord	wStatus;
430184610Salfred		uint8_t	buf[2];
431184610Salfred	}     temp;
432184610Salfred
433184610Salfred	/*
434184610Salfred	 * Filter the USB transfer state into
435184610Salfred	 * something which we understand:
436184610Salfred	 */
437184610Salfred
438184610Salfred	switch (USB_GET_STATE(xfer)) {
439184610Salfred	case USB_ST_SETUP:
440194064Sthompsa		state = USB_HR_NOT_COMPLETE;
441184610Salfred
442184610Salfred		if (!xfer->flags_int.control_act) {
443184610Salfred			/* nothing to do */
444184610Salfred			goto tr_stalled;
445184610Salfred		}
446184610Salfred		break;
447194064Sthompsa	case USB_ST_TRANSFERRED:
448184610Salfred		if (!xfer->flags_int.control_act) {
449194064Sthompsa			state = USB_HR_COMPLETE_OK;
450184610Salfred		} else {
451194064Sthompsa			state = USB_HR_NOT_COMPLETE;
452184610Salfred		}
453184610Salfred		break;
454194064Sthompsa	default:
455194064Sthompsa		state = USB_HR_COMPLETE_ERR;
456194064Sthompsa		break;
457184610Salfred	}
458184610Salfred
459184610Salfred	/* reset frame stuff */
460184610Salfred
461184610Salfred	xfer->frlengths[0] = 0;
462184610Salfred
463194228Sthompsa	usbd_set_frame_offset(xfer, 0, 0);
464194228Sthompsa	usbd_set_frame_offset(xfer, sizeof(req), 1);
465184610Salfred
466184610Salfred	/* get the current request, if any */
467184610Salfred
468194228Sthompsa	usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
469184610Salfred
470184610Salfred	if (xfer->flags_int.control_rem == 0xFFFF) {
471184610Salfred		/* first time - not initialised */
472184610Salfred		rem = UGETW(req.wLength);
473184610Salfred		off = 0;
474184610Salfred	} else {
475184610Salfred		/* not first time - initialised */
476184610Salfred		rem = xfer->flags_int.control_rem;
477184610Salfred		off = UGETW(req.wLength) - rem;
478184610Salfred	}
479184610Salfred
480184610Salfred	/* set some defaults */
481184610Salfred
482184610Salfred	max_len = 0;
483184610Salfred	src_zcopy = NULL;
484184610Salfred	src_mcopy = NULL;
485187173Sthompsa	udev = xfer->xroot->udev;
486184610Salfred
487184610Salfred	/* get some request fields decoded */
488184610Salfred
489184610Salfred	wValue = UGETW(req.wValue);
490184610Salfred	wIndex = UGETW(req.wIndex);
491184610Salfred
492184610Salfred	DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
493184610Salfred	    "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
494184610Salfred	    req.bRequest, wValue, wIndex, off, rem, state);
495184610Salfred
496184610Salfred	/* demultiplex the control request */
497184610Salfred
498184610Salfred	switch (req.bmRequestType) {
499184610Salfred	case UT_READ_DEVICE:
500194064Sthompsa		if (state != USB_HR_NOT_COMPLETE) {
501184610Salfred			break;
502184610Salfred		}
503184610Salfred		switch (req.bRequest) {
504184610Salfred		case UR_GET_DESCRIPTOR:
505184610Salfred			goto tr_handle_get_descriptor;
506184610Salfred		case UR_GET_CONFIG:
507184610Salfred			goto tr_handle_get_config;
508184610Salfred		case UR_GET_STATUS:
509184610Salfred			goto tr_handle_get_status;
510184610Salfred		default:
511184610Salfred			goto tr_stalled;
512184610Salfred		}
513184610Salfred		break;
514184610Salfred
515184610Salfred	case UT_WRITE_DEVICE:
516184610Salfred		switch (req.bRequest) {
517184610Salfred		case UR_SET_ADDRESS:
518184610Salfred			goto tr_handle_set_address;
519184610Salfred		case UR_SET_CONFIG:
520184610Salfred			goto tr_handle_set_config;
521184610Salfred		case UR_CLEAR_FEATURE:
522184610Salfred			switch (wValue) {
523184610Salfred			case UF_DEVICE_REMOTE_WAKEUP:
524184610Salfred				goto tr_handle_clear_wakeup;
525184610Salfred			default:
526184610Salfred				goto tr_stalled;
527184610Salfred			}
528184610Salfred			break;
529184610Salfred		case UR_SET_FEATURE:
530184610Salfred			switch (wValue) {
531184610Salfred			case UF_DEVICE_REMOTE_WAKEUP:
532184610Salfred				goto tr_handle_set_wakeup;
533184610Salfred			default:
534184610Salfred				goto tr_stalled;
535184610Salfred			}
536184610Salfred			break;
537184610Salfred		default:
538184610Salfred			goto tr_stalled;
539184610Salfred		}
540184610Salfred		break;
541184610Salfred
542184610Salfred	case UT_WRITE_ENDPOINT:
543184610Salfred		switch (req.bRequest) {
544184610Salfred		case UR_CLEAR_FEATURE:
545184610Salfred			switch (wValue) {
546184610Salfred			case UF_ENDPOINT_HALT:
547184610Salfred				goto tr_handle_clear_halt;
548184610Salfred			default:
549184610Salfred				goto tr_stalled;
550184610Salfred			}
551184610Salfred			break;
552184610Salfred		case UR_SET_FEATURE:
553184610Salfred			switch (wValue) {
554184610Salfred			case UF_ENDPOINT_HALT:
555184610Salfred				goto tr_handle_set_halt;
556184610Salfred			default:
557184610Salfred				goto tr_stalled;
558184610Salfred			}
559184610Salfred			break;
560184610Salfred		default:
561184610Salfred			goto tr_stalled;
562184610Salfred		}
563184610Salfred		break;
564184610Salfred
565184610Salfred	case UT_READ_ENDPOINT:
566184610Salfred		switch (req.bRequest) {
567184610Salfred		case UR_GET_STATUS:
568184610Salfred			goto tr_handle_get_ep_status;
569184610Salfred		default:
570184610Salfred			goto tr_stalled;
571184610Salfred		}
572184610Salfred		break;
573184610Salfred	default:
574184610Salfred		/* we use "USB_ADD_BYTES" to de-const the src_zcopy */
575194228Sthompsa		err = usb_handle_iface_request(xfer,
576184610Salfred		    USB_ADD_BYTES(&src_zcopy, 0),
577184610Salfred		    &max_len, req, off, state);
578184610Salfred		if (err == 0) {
579184610Salfred			goto tr_valid;
580184610Salfred		}
581184610Salfred		/*
582184610Salfred		 * Reset zero-copy pointer and max length
583184610Salfred		 * variable in case they were unintentionally
584184610Salfred		 * set:
585184610Salfred		 */
586184610Salfred		src_zcopy = NULL;
587184610Salfred		max_len = 0;
588184610Salfred
589184610Salfred		/*
590184610Salfred		 * Check if we have a vendor specific
591184610Salfred		 * descriptor:
592184610Salfred		 */
593184610Salfred		goto tr_handle_get_descriptor;
594184610Salfred	}
595184610Salfred	goto tr_valid;
596184610Salfred
597184610Salfredtr_handle_get_descriptor:
598194228Sthompsa	err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
599191402Sthompsa	if (err)
600184610Salfred		goto tr_stalled;
601191402Sthompsa	if (src_zcopy == NULL)
602191402Sthompsa		goto tr_stalled;
603184610Salfred	goto tr_valid;
604184610Salfred
605184610Salfredtr_handle_get_config:
606184610Salfred	temp.buf[0] = udev->curr_config_no;
607184610Salfred	src_mcopy = temp.buf;
608184610Salfred	max_len = 1;
609184610Salfred	goto tr_valid;
610184610Salfred
611184610Salfredtr_handle_get_status:
612184610Salfred
613184610Salfred	wValue = 0;
614184610Salfred
615184824Sthompsa	USB_BUS_LOCK(udev->bus);
616184610Salfred	if (udev->flags.remote_wakeup) {
617184610Salfred		wValue |= UDS_REMOTE_WAKEUP;
618184610Salfred	}
619184610Salfred	if (udev->flags.self_powered) {
620184610Salfred		wValue |= UDS_SELF_POWERED;
621184610Salfred	}
622184824Sthompsa	USB_BUS_UNLOCK(udev->bus);
623184610Salfred
624184610Salfred	USETW(temp.wStatus, wValue);
625184610Salfred	src_mcopy = temp.wStatus;
626184610Salfred	max_len = sizeof(temp.wStatus);
627184610Salfred	goto tr_valid;
628184610Salfred
629184610Salfredtr_handle_set_address:
630194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
631184610Salfred		if (wValue >= 0x80) {
632184610Salfred			/* invalid value */
633184610Salfred			goto tr_stalled;
634184610Salfred		} else if (udev->curr_config_no != 0) {
635184610Salfred			/* we are configured ! */
636184610Salfred			goto tr_stalled;
637184610Salfred		}
638194064Sthompsa	} else if (state != USB_HR_NOT_COMPLETE) {
639184610Salfred		udev->address = (wValue & 0x7F);
640184610Salfred		goto tr_bad_context;
641184610Salfred	}
642184610Salfred	goto tr_valid;
643184610Salfred
644184610Salfredtr_handle_set_config:
645194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
646194228Sthompsa		if (usb_handle_set_config(xfer, req.wValue[0])) {
647184610Salfred			goto tr_stalled;
648184610Salfred		}
649184610Salfred	}
650184610Salfred	goto tr_valid;
651184610Salfred
652184610Salfredtr_handle_clear_halt:
653194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
654194228Sthompsa		if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) {
655184610Salfred			goto tr_stalled;
656184610Salfred		}
657184610Salfred	}
658184610Salfred	goto tr_valid;
659184610Salfred
660184610Salfredtr_handle_clear_wakeup:
661194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
662194228Sthompsa		if (usb_handle_remote_wakeup(xfer, 0)) {
663184610Salfred			goto tr_stalled;
664184610Salfred		}
665184610Salfred	}
666184610Salfred	goto tr_valid;
667184610Salfred
668184610Salfredtr_handle_set_halt:
669194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
670194228Sthompsa		if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) {
671184610Salfred			goto tr_stalled;
672184610Salfred		}
673184610Salfred	}
674184610Salfred	goto tr_valid;
675184610Salfred
676184610Salfredtr_handle_set_wakeup:
677194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
678194228Sthompsa		if (usb_handle_remote_wakeup(xfer, 1)) {
679184610Salfred			goto tr_stalled;
680184610Salfred		}
681184610Salfred	}
682184610Salfred	goto tr_valid;
683184610Salfred
684184610Salfredtr_handle_get_ep_status:
685194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
686184610Salfred		temp.wStatus[0] =
687194228Sthompsa		    usb_handle_get_stall(udev, req.wIndex[0]);
688184610Salfred		temp.wStatus[1] = 0;
689184610Salfred		src_mcopy = temp.wStatus;
690184610Salfred		max_len = sizeof(temp.wStatus);
691184610Salfred	}
692184610Salfred	goto tr_valid;
693184610Salfred
694184610Salfredtr_valid:
695194064Sthompsa	if (state != USB_HR_NOT_COMPLETE) {
696184610Salfred		goto tr_stalled;
697184610Salfred	}
698184610Salfred	/* subtract offset from length */
699184610Salfred
700184610Salfred	max_len -= off;
701184610Salfred
702184610Salfred	/* Compute the real maximum data length */
703184610Salfred
704184610Salfred	if (max_len > xfer->max_data_length) {
705184610Salfred		max_len = xfer->max_data_length;
706184610Salfred	}
707184610Salfred	if (max_len > rem) {
708184610Salfred		max_len = rem;
709184610Salfred	}
710184610Salfred	/*
711184610Salfred	 * If the remainder is greater than the maximum data length,
712184610Salfred	 * we need to truncate the value for the sake of the
713184610Salfred	 * comparison below:
714184610Salfred	 */
715184610Salfred	if (rem > xfer->max_data_length) {
716184610Salfred		rem = xfer->max_data_length;
717184610Salfred	}
718184610Salfred	if (rem != max_len) {
719184610Salfred		/*
720184610Salfred	         * If we don't transfer the data we can transfer, then
721184610Salfred	         * the transfer is short !
722184610Salfred	         */
723184610Salfred		xfer->flags.force_short_xfer = 1;
724184610Salfred		xfer->nframes = 2;
725184610Salfred	} else {
726184610Salfred		/*
727184610Salfred		 * Default case
728184610Salfred		 */
729184610Salfred		xfer->flags.force_short_xfer = 0;
730184610Salfred		xfer->nframes = max_len ? 2 : 1;
731184610Salfred	}
732184610Salfred	if (max_len > 0) {
733184610Salfred		if (src_mcopy) {
734184610Salfred			src_mcopy = USB_ADD_BYTES(src_mcopy, off);
735194228Sthompsa			usbd_copy_in(xfer->frbuffers + 1, 0,
736184610Salfred			    src_mcopy, max_len);
737184610Salfred		} else {
738194228Sthompsa			usbd_set_frame_data(xfer,
739184610Salfred			    USB_ADD_BYTES(src_zcopy, off), 1);
740184610Salfred		}
741184610Salfred		xfer->frlengths[1] = max_len;
742184610Salfred	} else {
743184610Salfred		/* the end is reached, send status */
744184610Salfred		xfer->flags.manual_status = 0;
745184610Salfred		xfer->frlengths[1] = 0;
746184610Salfred	}
747184610Salfred	DPRINTF("success\n");
748184610Salfred	return (0);			/* success */
749184610Salfred
750184610Salfredtr_stalled:
751194064Sthompsa	DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ?
752184610Salfred	    "complete" : "stalled");
753184610Salfred	return (USB_ERR_STALLED);
754184610Salfred
755184610Salfredtr_bad_context:
756184610Salfred	DPRINTF("bad context\n");
757184610Salfred	return (USB_ERR_BAD_CONTEXT);
758184610Salfred}
759