1184610Salfred/* $FreeBSD$ */
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
27246122Shselasky#ifdef USB_GLOBAL_INCLUDE_FILE
28246122Shselasky#include USB_GLOBAL_INCLUDE_FILE
29246122Shselasky#else
30194677Sthompsa#include <sys/stdint.h>
31194677Sthompsa#include <sys/stddef.h>
32194677Sthompsa#include <sys/param.h>
33194677Sthompsa#include <sys/queue.h>
34194677Sthompsa#include <sys/types.h>
35194677Sthompsa#include <sys/systm.h>
36194677Sthompsa#include <sys/kernel.h>
37194677Sthompsa#include <sys/bus.h>
38194677Sthompsa#include <sys/module.h>
39194677Sthompsa#include <sys/lock.h>
40194677Sthompsa#include <sys/mutex.h>
41194677Sthompsa#include <sys/condvar.h>
42194677Sthompsa#include <sys/sysctl.h>
43194677Sthompsa#include <sys/sx.h>
44194677Sthompsa#include <sys/unistd.h>
45194677Sthompsa#include <sys/callout.h>
46194677Sthompsa#include <sys/malloc.h>
47194677Sthompsa#include <sys/priv.h>
48194677Sthompsa
49188942Sthompsa#include <dev/usb/usb.h>
50194677Sthompsa#include <dev/usb/usbdi.h>
51195963Salfred#include <dev/usb/usbdi_util.h>
52194677Sthompsa#include "usb_if.h"
53184610Salfred
54194228Sthompsa#define	USB_DEBUG_VAR usb_debug
55184610Salfred
56188942Sthompsa#include <dev/usb/usb_core.h>
57188942Sthompsa#include <dev/usb/usb_process.h>
58188942Sthompsa#include <dev/usb/usb_busdma.h>
59188942Sthompsa#include <dev/usb/usb_transfer.h>
60188942Sthompsa#include <dev/usb/usb_device.h>
61188942Sthompsa#include <dev/usb/usb_debug.h>
62188942Sthompsa#include <dev/usb/usb_dynamic.h>
63188942Sthompsa#include <dev/usb/usb_hub.h>
64184610Salfred
65188942Sthompsa#include <dev/usb/usb_controller.h>
66188942Sthompsa#include <dev/usb/usb_bus.h>
67246122Shselasky#endif			/* USB_GLOBAL_INCLUDE_FILE */
68184610Salfred
69184610Salfred/* function prototypes */
70184610Salfred
71194228Sthompsastatic uint8_t usb_handle_get_stall(struct usb_device *, uint8_t);
72194228Sthompsastatic usb_error_t	 usb_handle_remote_wakeup(struct usb_xfer *, uint8_t);
73194228Sthompsastatic usb_error_t	 usb_handle_request(struct usb_xfer *);
74194228Sthompsastatic usb_error_t	 usb_handle_set_config(struct usb_xfer *, uint8_t);
75194228Sthompsastatic usb_error_t	 usb_handle_set_stall(struct usb_xfer *, uint8_t,
76185948Sthompsa			    uint8_t);
77194228Sthompsastatic usb_error_t	 usb_handle_iface_request(struct usb_xfer *, void **,
78192984Sthompsa			    uint16_t *, struct usb_device_request, uint16_t,
79185948Sthompsa			    uint8_t);
80184610Salfred
81184610Salfred/*------------------------------------------------------------------------*
82194228Sthompsa *	usb_handle_request_callback
83184610Salfred *
84184610Salfred * This function is the USB callback for generic USB Device control
85184610Salfred * transfers.
86184610Salfred *------------------------------------------------------------------------*/
87184610Salfredvoid
88194677Sthompsausb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error)
89184610Salfred{
90193045Sthompsa	usb_error_t err;
91184610Salfred
92184610Salfred	/* check the current transfer state */
93184610Salfred
94184610Salfred	switch (USB_GET_STATE(xfer)) {
95184610Salfred	case USB_ST_SETUP:
96184610Salfred	case USB_ST_TRANSFERRED:
97184610Salfred
98184610Salfred		/* handle the request */
99194228Sthompsa		err = usb_handle_request(xfer);
100184610Salfred
101184610Salfred		if (err) {
102184610Salfred
103184610Salfred			if (err == USB_ERR_BAD_CONTEXT) {
104184610Salfred				/* we need to re-setup the control transfer */
105194228Sthompsa				usb_needs_explore(xfer->xroot->bus, 0);
106184610Salfred				break;
107184610Salfred			}
108184610Salfred			goto tr_restart;
109184610Salfred		}
110194228Sthompsa		usbd_transfer_submit(xfer);
111184610Salfred		break;
112184610Salfred
113184610Salfred	default:
114194064Sthompsa		/* check if a control transfer is active */
115194064Sthompsa		if (xfer->flags_int.control_rem != 0xFFFF) {
116194064Sthompsa			/* handle the request */
117194228Sthompsa			err = usb_handle_request(xfer);
118194064Sthompsa		}
119184610Salfred		if (xfer->error != USB_ERR_CANCELLED) {
120184610Salfred			/* should not happen - try stalling */
121184610Salfred			goto tr_restart;
122184610Salfred		}
123184610Salfred		break;
124184610Salfred	}
125184610Salfred	return;
126184610Salfred
127184610Salfredtr_restart:
128194064Sthompsa	/*
129194064Sthompsa	 * If a control transfer is active, stall it, and wait for the
130194064Sthompsa	 * next control transfer.
131194064Sthompsa	 */
132194677Sthompsa	usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request));
133184610Salfred	xfer->nframes = 1;
134184610Salfred	xfer->flags.manual_status = 1;
135184610Salfred	xfer->flags.force_short_xfer = 0;
136194677Sthompsa	usbd_xfer_set_stall(xfer);	/* cancel previous transfer, if any */
137194228Sthompsa	usbd_transfer_submit(xfer);
138184610Salfred}
139184610Salfred
140184610Salfred/*------------------------------------------------------------------------*
141194228Sthompsa *	usb_handle_set_config
142184610Salfred *
143184610Salfred * Returns:
144184610Salfred *    0: Success
145184610Salfred * Else: Failure
146184610Salfred *------------------------------------------------------------------------*/
147193045Sthompsastatic usb_error_t
148194228Sthompsausb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no)
149184610Salfred{
150192984Sthompsa	struct usb_device *udev = xfer->xroot->udev;
151193045Sthompsa	usb_error_t err = 0;
152246759Shselasky	uint8_t do_unlock;
153184610Salfred
154184610Salfred	/*
155184610Salfred	 * We need to protect against other threads doing probe and
156184610Salfred	 * attach:
157184610Salfred	 */
158184824Sthompsa	USB_XFER_UNLOCK(xfer);
159184610Salfred
160246759Shselasky	/* Prevent re-enumeration */
161246759Shselasky	do_unlock = usbd_enum_lock(udev);
162196498Salfred
163184610Salfred	if (conf_no == USB_UNCONFIG_NO) {
164184610Salfred		conf_no = USB_UNCONFIG_INDEX;
165184610Salfred	} else {
166184610Salfred		/*
167184610Salfred		 * The relationship between config number and config index
168184610Salfred		 * is very simple in our case:
169184610Salfred		 */
170184610Salfred		conf_no--;
171184610Salfred	}
172184610Salfred
173194228Sthompsa	if (usbd_set_config_index(udev, conf_no)) {
174184610Salfred		DPRINTF("set config %d failed\n", conf_no);
175184610Salfred		err = USB_ERR_STALLED;
176184610Salfred		goto done;
177184610Salfred	}
178194228Sthompsa	if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
179184610Salfred		DPRINTF("probe and attach failed\n");
180184610Salfred		err = USB_ERR_STALLED;
181184610Salfred		goto done;
182184610Salfred	}
183184610Salfreddone:
184246759Shselasky	if (do_unlock)
185246759Shselasky		usbd_enum_unlock(udev);
186184824Sthompsa	USB_XFER_LOCK(xfer);
187184610Salfred	return (err);
188184610Salfred}
189184610Salfred
190195963Salfredstatic usb_error_t
191195963Salfredusb_check_alt_setting(struct usb_device *udev,
192195963Salfred     struct usb_interface *iface, uint8_t alt_index)
193195963Salfred{
194195963Salfred	uint8_t do_unlock;
195195963Salfred	usb_error_t err = 0;
196195963Salfred
197246616Shselasky	/* Prevent re-enumeration */
198246616Shselasky	do_unlock = usbd_enum_lock(udev);
199195963Salfred
200195963Salfred	if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc))
201195963Salfred		err = USB_ERR_INVAL;
202195963Salfred
203196498Salfred	if (do_unlock)
204196498Salfred		usbd_enum_unlock(udev);
205196498Salfred
206195963Salfred	return (err);
207195963Salfred}
208195963Salfred
209184610Salfred/*------------------------------------------------------------------------*
210194228Sthompsa *	usb_handle_iface_request
211184610Salfred *
212184610Salfred * Returns:
213184610Salfred *    0: Success
214184610Salfred * Else: Failure
215184610Salfred *------------------------------------------------------------------------*/
216193045Sthompsastatic usb_error_t
217194228Sthompsausb_handle_iface_request(struct usb_xfer *xfer,
218184610Salfred    void **ppdata, uint16_t *plen,
219192984Sthompsa    struct usb_device_request req, uint16_t off, uint8_t state)
220184610Salfred{
221192984Sthompsa	struct usb_interface *iface;
222192984Sthompsa	struct usb_interface *iface_parent;	/* parent interface */
223192984Sthompsa	struct usb_device *udev = xfer->xroot->udev;
224184610Salfred	int error;
225184610Salfred	uint8_t iface_index;
226195121Sthompsa	uint8_t temp_state;
227246759Shselasky	uint8_t do_unlock;
228184610Salfred
229184610Salfred	if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
230184610Salfred		iface_index = req.wIndex[0];	/* unicast */
231184610Salfred	} else {
232184610Salfred		iface_index = 0;	/* broadcast */
233184610Salfred	}
234184610Salfred
235184610Salfred	/*
236184610Salfred	 * We need to protect against other threads doing probe and
237184610Salfred	 * attach:
238184610Salfred	 */
239184824Sthompsa	USB_XFER_UNLOCK(xfer);
240184610Salfred
241246759Shselasky	/* Prevent re-enumeration */
242246759Shselasky	do_unlock = usbd_enum_lock(udev);
243196498Salfred
244184610Salfred	error = ENXIO;
245184610Salfred
246184610Salfredtr_repeat:
247194228Sthompsa	iface = usbd_get_iface(udev, iface_index);
248184610Salfred	if ((iface == NULL) ||
249184610Salfred	    (iface->idesc == NULL)) {
250184610Salfred		/* end of interfaces non-existing interface */
251184610Salfred		goto tr_stalled;
252184610Salfred	}
253195121Sthompsa	/* set initial state */
254195121Sthompsa
255195121Sthompsa	temp_state = state;
256195121Sthompsa
257184610Salfred	/* forward request to interface, if any */
258184610Salfred
259184610Salfred	if ((error != 0) &&
260184610Salfred	    (error != ENOTTY) &&
261184610Salfred	    (iface->subdev != NULL) &&
262184610Salfred	    device_is_attached(iface->subdev)) {
263184610Salfred#if 0
264190186Sthompsa		DEVMETHOD(usb_handle_request, NULL);	/* dummy */
265184610Salfred#endif
266188942Sthompsa		error = USB_HANDLE_REQUEST(iface->subdev,
267184610Salfred		    &req, ppdata, plen,
268195121Sthompsa		    off, &temp_state);
269184610Salfred	}
270194228Sthompsa	iface_parent = usbd_get_iface(udev, iface->parent_iface_index);
271184610Salfred
272184610Salfred	if ((iface_parent == NULL) ||
273184610Salfred	    (iface_parent->idesc == NULL)) {
274184610Salfred		/* non-existing interface */
275184610Salfred		iface_parent = NULL;
276184610Salfred	}
277184610Salfred	/* forward request to parent interface, if any */
278184610Salfred
279184610Salfred	if ((error != 0) &&
280184610Salfred	    (error != ENOTTY) &&
281184610Salfred	    (iface_parent != NULL) &&
282184610Salfred	    (iface_parent->subdev != NULL) &&
283184610Salfred	    ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
284184610Salfred	    (iface_parent->subdev != iface->subdev) &&
285184610Salfred	    device_is_attached(iface_parent->subdev)) {
286188942Sthompsa		error = USB_HANDLE_REQUEST(iface_parent->subdev,
287195121Sthompsa		    &req, ppdata, plen, off, &temp_state);
288184610Salfred	}
289184610Salfred	if (error == 0) {
290184610Salfred		/* negativly adjust pointer and length */
291184610Salfred		*ppdata = ((uint8_t *)(*ppdata)) - off;
292184610Salfred		*plen += off;
293195121Sthompsa
294195121Sthompsa		if ((state == USB_HR_NOT_COMPLETE) &&
295195121Sthompsa		    (temp_state == USB_HR_COMPLETE_OK))
296195121Sthompsa			goto tr_short;
297195121Sthompsa		else
298195121Sthompsa			goto tr_valid;
299184610Salfred	} else if (error == ENOTTY) {
300184610Salfred		goto tr_stalled;
301184610Salfred	}
302184610Salfred	if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
303184610Salfred		iface_index++;		/* iterate */
304184610Salfred		goto tr_repeat;
305184610Salfred	}
306194064Sthompsa	if (state != USB_HR_NOT_COMPLETE) {
307184610Salfred		/* we are complete */
308184610Salfred		goto tr_valid;
309184610Salfred	}
310184610Salfred	switch (req.bmRequestType) {
311184610Salfred	case UT_WRITE_INTERFACE:
312184610Salfred		switch (req.bRequest) {
313184610Salfred		case UR_SET_INTERFACE:
314184610Salfred			/*
315195963Salfred			 * We assume that the endpoints are the same
316195963Salfred			 * accross the alternate settings.
317195963Salfred			 *
318195963Salfred			 * Reset the endpoints, because re-attaching
319195963Salfred			 * only a part of the device is not possible.
320184610Salfred			 */
321195963Salfred			error = usb_check_alt_setting(udev,
322195963Salfred			    iface, req.wValue[0]);
323184610Salfred			if (error) {
324195963Salfred				DPRINTF("alt setting does not exist %s\n",
325194228Sthompsa				    usbd_errstr(error));
326184610Salfred				goto tr_stalled;
327184610Salfred			}
328195963Salfred			error = usb_reset_iface_endpoints(udev, iface_index);
329184610Salfred			if (error) {
330195963Salfred				DPRINTF("alt setting failed %s\n",
331195963Salfred				    usbd_errstr(error));
332184610Salfred				goto tr_stalled;
333184610Salfred			}
334195963Salfred			/* update the current alternate setting */
335195963Salfred			iface->alt_index = req.wValue[0];
336184610Salfred			break;
337195963Salfred
338184610Salfred		default:
339184610Salfred			goto tr_stalled;
340184610Salfred		}
341184610Salfred		break;
342184610Salfred
343184610Salfred	case UT_READ_INTERFACE:
344184610Salfred		switch (req.bRequest) {
345184610Salfred		case UR_GET_INTERFACE:
346184610Salfred			*ppdata = &iface->alt_index;
347184610Salfred			*plen = 1;
348184610Salfred			break;
349184610Salfred
350184610Salfred		default:
351184610Salfred			goto tr_stalled;
352184610Salfred		}
353184610Salfred		break;
354184610Salfred	default:
355184610Salfred		goto tr_stalled;
356184610Salfred	}
357184610Salfredtr_valid:
358246759Shselasky	if (do_unlock)
359246759Shselasky		usbd_enum_unlock(udev);
360184824Sthompsa	USB_XFER_LOCK(xfer);
361184610Salfred	return (0);
362184610Salfred
363195121Sthompsatr_short:
364246759Shselasky	if (do_unlock)
365246759Shselasky		usbd_enum_unlock(udev);
366195121Sthompsa	USB_XFER_LOCK(xfer);
367195121Sthompsa	return (USB_ERR_SHORT_XFER);
368195121Sthompsa
369184610Salfredtr_stalled:
370246759Shselasky	if (do_unlock)
371246759Shselasky		usbd_enum_unlock(udev);
372184824Sthompsa	USB_XFER_LOCK(xfer);
373184610Salfred	return (USB_ERR_STALLED);
374184610Salfred}
375184610Salfred
376184610Salfred/*------------------------------------------------------------------------*
377194228Sthompsa *	usb_handle_stall
378184610Salfred *
379184610Salfred * Returns:
380184610Salfred *    0: Success
381184610Salfred * Else: Failure
382184610Salfred *------------------------------------------------------------------------*/
383193045Sthompsastatic usb_error_t
384194228Sthompsausb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall)
385184610Salfred{
386192984Sthompsa	struct usb_device *udev = xfer->xroot->udev;
387193045Sthompsa	usb_error_t err;
388184610Salfred
389184824Sthompsa	USB_XFER_UNLOCK(xfer);
390194228Sthompsa	err = usbd_set_endpoint_stall(udev,
391194228Sthompsa	    usbd_get_ep_by_addr(udev, ep), do_stall);
392184824Sthompsa	USB_XFER_LOCK(xfer);
393184610Salfred	return (err);
394184610Salfred}
395184610Salfred
396184610Salfred/*------------------------------------------------------------------------*
397194228Sthompsa *	usb_handle_get_stall
398184610Salfred *
399184610Salfred * Returns:
400184610Salfred *    0: Success
401184610Salfred * Else: Failure
402184610Salfred *------------------------------------------------------------------------*/
403184610Salfredstatic uint8_t
404194228Sthompsausb_handle_get_stall(struct usb_device *udev, uint8_t ea_val)
405184610Salfred{
406193644Sthompsa	struct usb_endpoint *ep;
407184610Salfred	uint8_t halted;
408184610Salfred
409194228Sthompsa	ep = usbd_get_ep_by_addr(udev, ea_val);
410193644Sthompsa	if (ep == NULL) {
411184610Salfred		/* nothing to do */
412184610Salfred		return (0);
413184610Salfred	}
414184824Sthompsa	USB_BUS_LOCK(udev->bus);
415193644Sthompsa	halted = ep->is_stalled;
416184824Sthompsa	USB_BUS_UNLOCK(udev->bus);
417184610Salfred
418184610Salfred	return (halted);
419184610Salfred}
420184610Salfred
421184610Salfred/*------------------------------------------------------------------------*
422194228Sthompsa *	usb_handle_remote_wakeup
423184610Salfred *
424184610Salfred * Returns:
425184610Salfred *    0: Success
426184610Salfred * Else: Failure
427184610Salfred *------------------------------------------------------------------------*/
428193045Sthompsastatic usb_error_t
429194228Sthompsausb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on)
430184610Salfred{
431192984Sthompsa	struct usb_device *udev;
432192984Sthompsa	struct usb_bus *bus;
433184610Salfred
434187173Sthompsa	udev = xfer->xroot->udev;
435184610Salfred	bus = udev->bus;
436184610Salfred
437184824Sthompsa	USB_BUS_LOCK(bus);
438184610Salfred
439184610Salfred	if (is_on) {
440184610Salfred		udev->flags.remote_wakeup = 1;
441184610Salfred	} else {
442184610Salfred		udev->flags.remote_wakeup = 0;
443184610Salfred	}
444184610Salfred
445184824Sthompsa	USB_BUS_UNLOCK(bus);
446184610Salfred
447213434Shselasky#if USB_HAVE_POWERD
448186730Salfred	/* In case we are out of sync, update the power state. */
449194228Sthompsa	usb_bus_power_update(udev->bus);
450213434Shselasky#endif
451184610Salfred	return (0);			/* success */
452184610Salfred}
453184610Salfred
454184610Salfred/*------------------------------------------------------------------------*
455194228Sthompsa *	usb_handle_request
456184610Salfred *
457184610Salfred * Internal state sequence:
458184610Salfred *
459194064Sthompsa * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR
460184610Salfred *
461184610Salfred * Returns:
462184610Salfred * 0: Ready to start hardware
463184610Salfred * Else: Stall current transfer, if any
464184610Salfred *------------------------------------------------------------------------*/
465193045Sthompsastatic usb_error_t
466194228Sthompsausb_handle_request(struct usb_xfer *xfer)
467184610Salfred{
468192984Sthompsa	struct usb_device_request req;
469192984Sthompsa	struct usb_device *udev;
470184610Salfred	const void *src_zcopy;		/* zero-copy source pointer */
471184610Salfred	const void *src_mcopy;		/* non zero-copy source pointer */
472184610Salfred	uint16_t off;			/* data offset */
473184610Salfred	uint16_t rem;			/* data remainder */
474184610Salfred	uint16_t max_len;		/* max fragment length */
475184610Salfred	uint16_t wValue;
476184610Salfred	uint8_t state;
477195121Sthompsa	uint8_t is_complete = 1;
478193045Sthompsa	usb_error_t err;
479184610Salfred	union {
480184610Salfred		uWord	wStatus;
481184610Salfred		uint8_t	buf[2];
482184610Salfred	}     temp;
483184610Salfred
484184610Salfred	/*
485184610Salfred	 * Filter the USB transfer state into
486184610Salfred	 * something which we understand:
487184610Salfred	 */
488184610Salfred
489184610Salfred	switch (USB_GET_STATE(xfer)) {
490184610Salfred	case USB_ST_SETUP:
491194064Sthompsa		state = USB_HR_NOT_COMPLETE;
492184610Salfred
493184610Salfred		if (!xfer->flags_int.control_act) {
494184610Salfred			/* nothing to do */
495184610Salfred			goto tr_stalled;
496184610Salfred		}
497184610Salfred		break;
498194064Sthompsa	case USB_ST_TRANSFERRED:
499184610Salfred		if (!xfer->flags_int.control_act) {
500194064Sthompsa			state = USB_HR_COMPLETE_OK;
501184610Salfred		} else {
502194064Sthompsa			state = USB_HR_NOT_COMPLETE;
503184610Salfred		}
504184610Salfred		break;
505194064Sthompsa	default:
506194064Sthompsa		state = USB_HR_COMPLETE_ERR;
507194064Sthompsa		break;
508184610Salfred	}
509184610Salfred
510184610Salfred	/* reset frame stuff */
511184610Salfred
512194677Sthompsa	usbd_xfer_set_frame_len(xfer, 0, 0);
513184610Salfred
514194677Sthompsa	usbd_xfer_set_frame_offset(xfer, 0, 0);
515194677Sthompsa	usbd_xfer_set_frame_offset(xfer, sizeof(req), 1);
516184610Salfred
517184610Salfred	/* get the current request, if any */
518184610Salfred
519194228Sthompsa	usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
520184610Salfred
521184610Salfred	if (xfer->flags_int.control_rem == 0xFFFF) {
522184610Salfred		/* first time - not initialised */
523184610Salfred		rem = UGETW(req.wLength);
524184610Salfred		off = 0;
525184610Salfred	} else {
526184610Salfred		/* not first time - initialised */
527184610Salfred		rem = xfer->flags_int.control_rem;
528184610Salfred		off = UGETW(req.wLength) - rem;
529184610Salfred	}
530184610Salfred
531184610Salfred	/* set some defaults */
532184610Salfred
533184610Salfred	max_len = 0;
534184610Salfred	src_zcopy = NULL;
535184610Salfred	src_mcopy = NULL;
536187173Sthompsa	udev = xfer->xroot->udev;
537184610Salfred
538184610Salfred	/* get some request fields decoded */
539184610Salfred
540184610Salfred	wValue = UGETW(req.wValue);
541184610Salfred
542184610Salfred	DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
543184610Salfred	    "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
544233774Shselasky	    req.bRequest, wValue, UGETW(req.wIndex), off, rem, state);
545184610Salfred
546184610Salfred	/* demultiplex the control request */
547184610Salfred
548184610Salfred	switch (req.bmRequestType) {
549184610Salfred	case UT_READ_DEVICE:
550194064Sthompsa		if (state != USB_HR_NOT_COMPLETE) {
551184610Salfred			break;
552184610Salfred		}
553184610Salfred		switch (req.bRequest) {
554184610Salfred		case UR_GET_DESCRIPTOR:
555184610Salfred			goto tr_handle_get_descriptor;
556184610Salfred		case UR_GET_CONFIG:
557184610Salfred			goto tr_handle_get_config;
558184610Salfred		case UR_GET_STATUS:
559184610Salfred			goto tr_handle_get_status;
560184610Salfred		default:
561184610Salfred			goto tr_stalled;
562184610Salfred		}
563184610Salfred		break;
564184610Salfred
565184610Salfred	case UT_WRITE_DEVICE:
566184610Salfred		switch (req.bRequest) {
567184610Salfred		case UR_SET_ADDRESS:
568184610Salfred			goto tr_handle_set_address;
569184610Salfred		case UR_SET_CONFIG:
570184610Salfred			goto tr_handle_set_config;
571184610Salfred		case UR_CLEAR_FEATURE:
572184610Salfred			switch (wValue) {
573184610Salfred			case UF_DEVICE_REMOTE_WAKEUP:
574184610Salfred				goto tr_handle_clear_wakeup;
575184610Salfred			default:
576184610Salfred				goto tr_stalled;
577184610Salfred			}
578184610Salfred			break;
579184610Salfred		case UR_SET_FEATURE:
580184610Salfred			switch (wValue) {
581184610Salfred			case UF_DEVICE_REMOTE_WAKEUP:
582184610Salfred				goto tr_handle_set_wakeup;
583184610Salfred			default:
584184610Salfred				goto tr_stalled;
585184610Salfred			}
586184610Salfred			break;
587184610Salfred		default:
588184610Salfred			goto tr_stalled;
589184610Salfred		}
590184610Salfred		break;
591184610Salfred
592184610Salfred	case UT_WRITE_ENDPOINT:
593184610Salfred		switch (req.bRequest) {
594184610Salfred		case UR_CLEAR_FEATURE:
595184610Salfred			switch (wValue) {
596184610Salfred			case UF_ENDPOINT_HALT:
597184610Salfred				goto tr_handle_clear_halt;
598184610Salfred			default:
599184610Salfred				goto tr_stalled;
600184610Salfred			}
601184610Salfred			break;
602184610Salfred		case UR_SET_FEATURE:
603184610Salfred			switch (wValue) {
604184610Salfred			case UF_ENDPOINT_HALT:
605184610Salfred				goto tr_handle_set_halt;
606184610Salfred			default:
607184610Salfred				goto tr_stalled;
608184610Salfred			}
609184610Salfred			break;
610184610Salfred		default:
611184610Salfred			goto tr_stalled;
612184610Salfred		}
613184610Salfred		break;
614184610Salfred
615184610Salfred	case UT_READ_ENDPOINT:
616184610Salfred		switch (req.bRequest) {
617184610Salfred		case UR_GET_STATUS:
618184610Salfred			goto tr_handle_get_ep_status;
619184610Salfred		default:
620184610Salfred			goto tr_stalled;
621184610Salfred		}
622184610Salfred		break;
623184610Salfred	default:
624184610Salfred		/* we use "USB_ADD_BYTES" to de-const the src_zcopy */
625194228Sthompsa		err = usb_handle_iface_request(xfer,
626184610Salfred		    USB_ADD_BYTES(&src_zcopy, 0),
627184610Salfred		    &max_len, req, off, state);
628184610Salfred		if (err == 0) {
629195121Sthompsa			is_complete = 0;
630184610Salfred			goto tr_valid;
631195121Sthompsa		} else if (err == USB_ERR_SHORT_XFER) {
632195121Sthompsa			goto tr_valid;
633184610Salfred		}
634184610Salfred		/*
635184610Salfred		 * Reset zero-copy pointer and max length
636184610Salfred		 * variable in case they were unintentionally
637184610Salfred		 * set:
638184610Salfred		 */
639184610Salfred		src_zcopy = NULL;
640184610Salfred		max_len = 0;
641184610Salfred
642184610Salfred		/*
643184610Salfred		 * Check if we have a vendor specific
644184610Salfred		 * descriptor:
645184610Salfred		 */
646184610Salfred		goto tr_handle_get_descriptor;
647184610Salfred	}
648184610Salfred	goto tr_valid;
649184610Salfred
650184610Salfredtr_handle_get_descriptor:
651194228Sthompsa	err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
652191402Sthompsa	if (err)
653184610Salfred		goto tr_stalled;
654191402Sthompsa	if (src_zcopy == NULL)
655191402Sthompsa		goto tr_stalled;
656184610Salfred	goto tr_valid;
657184610Salfred
658184610Salfredtr_handle_get_config:
659184610Salfred	temp.buf[0] = udev->curr_config_no;
660184610Salfred	src_mcopy = temp.buf;
661184610Salfred	max_len = 1;
662184610Salfred	goto tr_valid;
663184610Salfred
664184610Salfredtr_handle_get_status:
665184610Salfred
666184610Salfred	wValue = 0;
667184610Salfred
668184824Sthompsa	USB_BUS_LOCK(udev->bus);
669184610Salfred	if (udev->flags.remote_wakeup) {
670184610Salfred		wValue |= UDS_REMOTE_WAKEUP;
671184610Salfred	}
672184610Salfred	if (udev->flags.self_powered) {
673184610Salfred		wValue |= UDS_SELF_POWERED;
674184610Salfred	}
675184824Sthompsa	USB_BUS_UNLOCK(udev->bus);
676184610Salfred
677184610Salfred	USETW(temp.wStatus, wValue);
678184610Salfred	src_mcopy = temp.wStatus;
679184610Salfred	max_len = sizeof(temp.wStatus);
680184610Salfred	goto tr_valid;
681184610Salfred
682184610Salfredtr_handle_set_address:
683194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
684184610Salfred		if (wValue >= 0x80) {
685184610Salfred			/* invalid value */
686184610Salfred			goto tr_stalled;
687184610Salfred		} else if (udev->curr_config_no != 0) {
688184610Salfred			/* we are configured ! */
689184610Salfred			goto tr_stalled;
690184610Salfred		}
691194064Sthompsa	} else if (state != USB_HR_NOT_COMPLETE) {
692184610Salfred		udev->address = (wValue & 0x7F);
693184610Salfred		goto tr_bad_context;
694184610Salfred	}
695184610Salfred	goto tr_valid;
696184610Salfred
697184610Salfredtr_handle_set_config:
698194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
699194228Sthompsa		if (usb_handle_set_config(xfer, req.wValue[0])) {
700184610Salfred			goto tr_stalled;
701184610Salfred		}
702184610Salfred	}
703184610Salfred	goto tr_valid;
704184610Salfred
705184610Salfredtr_handle_clear_halt:
706194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
707194228Sthompsa		if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) {
708184610Salfred			goto tr_stalled;
709184610Salfred		}
710184610Salfred	}
711184610Salfred	goto tr_valid;
712184610Salfred
713184610Salfredtr_handle_clear_wakeup:
714194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
715194228Sthompsa		if (usb_handle_remote_wakeup(xfer, 0)) {
716184610Salfred			goto tr_stalled;
717184610Salfred		}
718184610Salfred	}
719184610Salfred	goto tr_valid;
720184610Salfred
721184610Salfredtr_handle_set_halt:
722194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
723194228Sthompsa		if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) {
724184610Salfred			goto tr_stalled;
725184610Salfred		}
726184610Salfred	}
727184610Salfred	goto tr_valid;
728184610Salfred
729184610Salfredtr_handle_set_wakeup:
730194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
731194228Sthompsa		if (usb_handle_remote_wakeup(xfer, 1)) {
732184610Salfred			goto tr_stalled;
733184610Salfred		}
734184610Salfred	}
735184610Salfred	goto tr_valid;
736184610Salfred
737184610Salfredtr_handle_get_ep_status:
738194064Sthompsa	if (state == USB_HR_NOT_COMPLETE) {
739184610Salfred		temp.wStatus[0] =
740194228Sthompsa		    usb_handle_get_stall(udev, req.wIndex[0]);
741184610Salfred		temp.wStatus[1] = 0;
742184610Salfred		src_mcopy = temp.wStatus;
743184610Salfred		max_len = sizeof(temp.wStatus);
744184610Salfred	}
745184610Salfred	goto tr_valid;
746184610Salfred
747184610Salfredtr_valid:
748194064Sthompsa	if (state != USB_HR_NOT_COMPLETE) {
749184610Salfred		goto tr_stalled;
750184610Salfred	}
751184610Salfred	/* subtract offset from length */
752184610Salfred
753184610Salfred	max_len -= off;
754184610Salfred
755184610Salfred	/* Compute the real maximum data length */
756184610Salfred
757184610Salfred	if (max_len > xfer->max_data_length) {
758194677Sthompsa		max_len = usbd_xfer_max_len(xfer);
759184610Salfred	}
760184610Salfred	if (max_len > rem) {
761184610Salfred		max_len = rem;
762184610Salfred	}
763184610Salfred	/*
764184610Salfred	 * If the remainder is greater than the maximum data length,
765184610Salfred	 * we need to truncate the value for the sake of the
766184610Salfred	 * comparison below:
767184610Salfred	 */
768184610Salfred	if (rem > xfer->max_data_length) {
769194677Sthompsa		rem = usbd_xfer_max_len(xfer);
770184610Salfred	}
771195121Sthompsa	if ((rem != max_len) && (is_complete != 0)) {
772184610Salfred		/*
773184610Salfred	         * If we don't transfer the data we can transfer, then
774184610Salfred	         * the transfer is short !
775184610Salfred	         */
776184610Salfred		xfer->flags.force_short_xfer = 1;
777184610Salfred		xfer->nframes = 2;
778184610Salfred	} else {
779184610Salfred		/*
780184610Salfred		 * Default case
781184610Salfred		 */
782184610Salfred		xfer->flags.force_short_xfer = 0;
783184610Salfred		xfer->nframes = max_len ? 2 : 1;
784184610Salfred	}
785184610Salfred	if (max_len > 0) {
786184610Salfred		if (src_mcopy) {
787184610Salfred			src_mcopy = USB_ADD_BYTES(src_mcopy, off);
788194228Sthompsa			usbd_copy_in(xfer->frbuffers + 1, 0,
789184610Salfred			    src_mcopy, max_len);
790194677Sthompsa			usbd_xfer_set_frame_len(xfer, 1, max_len);
791184610Salfred		} else {
792194677Sthompsa			usbd_xfer_set_frame_data(xfer, 1,
793194677Sthompsa			    USB_ADD_BYTES(src_zcopy, off), max_len);
794184610Salfred		}
795184610Salfred	} else {
796184610Salfred		/* the end is reached, send status */
797184610Salfred		xfer->flags.manual_status = 0;
798194677Sthompsa		usbd_xfer_set_frame_len(xfer, 1, 0);
799184610Salfred	}
800184610Salfred	DPRINTF("success\n");
801184610Salfred	return (0);			/* success */
802184610Salfred
803184610Salfredtr_stalled:
804194064Sthompsa	DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ?
805184610Salfred	    "complete" : "stalled");
806184610Salfred	return (USB_ERR_STALLED);
807184610Salfred
808184610Salfredtr_bad_context:
809184610Salfred	DPRINTF("bad context\n");
810184610Salfred	return (USB_ERR_BAD_CONTEXT);
811184610Salfred}
812