1f9f848faSopenharmony_ci/*-
2f9f848faSopenharmony_ci * SPDX-License-Identifier: BSD-2-Clause
3f9f848faSopenharmony_ci *
4f9f848faSopenharmony_ci * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
5f9f848faSopenharmony_ci *
6f9f848faSopenharmony_ci * Redistribution and use in source and binary forms, with or without
7f9f848faSopenharmony_ci * modification, are permitted provided that the following conditions
8f9f848faSopenharmony_ci * are met:
9f9f848faSopenharmony_ci * 1. Redistributions of source code must retain the above copyright
10f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer.
11f9f848faSopenharmony_ci * 2. Redistributions in binary form must reproduce the above copyright
12f9f848faSopenharmony_ci *    notice, this list of conditions and the following disclaimer in the
13f9f848faSopenharmony_ci *    documentation and/or other materials provided with the distribution.
14f9f848faSopenharmony_ci *
15f9f848faSopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16f9f848faSopenharmony_ci * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17f9f848faSopenharmony_ci * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18f9f848faSopenharmony_ci * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19f9f848faSopenharmony_ci * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20f9f848faSopenharmony_ci * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21f9f848faSopenharmony_ci * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22f9f848faSopenharmony_ci * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23f9f848faSopenharmony_ci * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24f9f848faSopenharmony_ci * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25f9f848faSopenharmony_ci * SUCH DAMAGE.
26f9f848faSopenharmony_ci */
27f9f848faSopenharmony_ci
28f9f848faSopenharmony_ci#include "implementation/global_implementation.h"
29f9f848faSopenharmony_ci
30f9f848faSopenharmony_ci/* function prototypes */
31f9f848faSopenharmony_ci
32f9f848faSopenharmony_cistatic uint8_t usb_handle_get_stall(struct usb_device *, uint8_t);
33f9f848faSopenharmony_cistatic usb_error_t	 usb_handle_remote_wakeup(struct usb_xfer *, uint8_t);
34f9f848faSopenharmony_cistatic usb_error_t	 usb_handle_request(struct usb_xfer *);
35f9f848faSopenharmony_cistatic usb_error_t	 usb_handle_set_config(struct usb_xfer *, uint8_t);
36f9f848faSopenharmony_cistatic usb_error_t	 usb_handle_set_stall(struct usb_xfer *, uint8_t,
37f9f848faSopenharmony_ci			    uint8_t);
38f9f848faSopenharmony_cistatic usb_error_t	 usb_handle_iface_request(struct usb_xfer *, void **,
39f9f848faSopenharmony_ci			    uint16_t *, struct usb_device_request, uint16_t,
40f9f848faSopenharmony_ci			    uint8_t);
41f9f848faSopenharmony_ci
42f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
43f9f848faSopenharmony_ci *	usb_handle_request_callback
44f9f848faSopenharmony_ci *
45f9f848faSopenharmony_ci * This function is the USB callback for generic USB Device control
46f9f848faSopenharmony_ci * transfers.
47f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
48f9f848faSopenharmony_civoid
49f9f848faSopenharmony_ciusb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error)
50f9f848faSopenharmony_ci{
51f9f848faSopenharmony_ci	usb_error_t err;
52f9f848faSopenharmony_ci
53f9f848faSopenharmony_ci	/* check the current transfer state */
54f9f848faSopenharmony_ci
55f9f848faSopenharmony_ci	switch (USB_GET_STATE(xfer)) {
56f9f848faSopenharmony_ci	case USB_ST_SETUP:
57f9f848faSopenharmony_ci	case USB_ST_TRANSFERRED:
58f9f848faSopenharmony_ci
59f9f848faSopenharmony_ci		/* handle the request */
60f9f848faSopenharmony_ci		err = usb_handle_request(xfer);
61f9f848faSopenharmony_ci
62f9f848faSopenharmony_ci		if (err) {
63f9f848faSopenharmony_ci			if (err == USB_ERR_BAD_CONTEXT) {
64f9f848faSopenharmony_ci				/* we need to re-setup the control transfer */
65f9f848faSopenharmony_ci				usb_needs_explore(xfer->xroot->bus, 0);
66f9f848faSopenharmony_ci				break;
67f9f848faSopenharmony_ci			}
68f9f848faSopenharmony_ci			goto tr_restart;
69f9f848faSopenharmony_ci		}
70f9f848faSopenharmony_ci		usbd_transfer_submit(xfer);
71f9f848faSopenharmony_ci		break;
72f9f848faSopenharmony_ci
73f9f848faSopenharmony_ci	default:
74f9f848faSopenharmony_ci		/* check if a control transfer is active */
75f9f848faSopenharmony_ci		if (xfer->flags_int.control_rem != 0xFFFF) {
76f9f848faSopenharmony_ci			/* handle the request */
77f9f848faSopenharmony_ci			(void)usb_handle_request(xfer);
78f9f848faSopenharmony_ci		}
79f9f848faSopenharmony_ci		if (xfer->error != USB_ERR_CANCELLED) {
80f9f848faSopenharmony_ci			/* should not happen - try stalling */
81f9f848faSopenharmony_ci			goto tr_restart;
82f9f848faSopenharmony_ci		}
83f9f848faSopenharmony_ci		break;
84f9f848faSopenharmony_ci	}
85f9f848faSopenharmony_ci	return;
86f9f848faSopenharmony_ci
87f9f848faSopenharmony_citr_restart:
88f9f848faSopenharmony_ci	/*
89f9f848faSopenharmony_ci	 * If a control transfer is active, stall it, and wait for the
90f9f848faSopenharmony_ci	 * next control transfer.
91f9f848faSopenharmony_ci	 */
92f9f848faSopenharmony_ci	usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request));
93f9f848faSopenharmony_ci	xfer->nframes = 1;
94f9f848faSopenharmony_ci	xfer->flags.manual_status = 1;
95f9f848faSopenharmony_ci	xfer->flags.force_short_xfer = 0;
96f9f848faSopenharmony_ci	usbd_xfer_set_stall(xfer);	/* cancel previous transfer, if any */
97f9f848faSopenharmony_ci	usbd_transfer_submit(xfer);
98f9f848faSopenharmony_ci}
99f9f848faSopenharmony_ci
100f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
101f9f848faSopenharmony_ci *	usb_handle_set_config
102f9f848faSopenharmony_ci *
103f9f848faSopenharmony_ci * Returns:
104f9f848faSopenharmony_ci *    0: Success
105f9f848faSopenharmony_ci * Else: Failure
106f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
107f9f848faSopenharmony_cistatic usb_error_t
108f9f848faSopenharmony_ciusb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no)
109f9f848faSopenharmony_ci{
110f9f848faSopenharmony_ci	struct usb_device *udev = xfer->xroot->udev;
111f9f848faSopenharmony_ci	usb_error_t err = USB_ERR_NORMAL_COMPLETION;
112f9f848faSopenharmony_ci	uint8_t do_unlock;
113f9f848faSopenharmony_ci
114f9f848faSopenharmony_ci	/*
115f9f848faSopenharmony_ci	 * We need to protect against other threads doing probe and
116f9f848faSopenharmony_ci	 * attach:
117f9f848faSopenharmony_ci	 */
118f9f848faSopenharmony_ci	USB_XFER_UNLOCK(xfer);
119f9f848faSopenharmony_ci
120f9f848faSopenharmony_ci	/* Prevent re-enumeration */
121f9f848faSopenharmony_ci	do_unlock = usbd_enum_lock(udev);
122f9f848faSopenharmony_ci
123f9f848faSopenharmony_ci	if (conf_no == USB_UNCONFIG_NO) {
124f9f848faSopenharmony_ci		conf_no = USB_UNCONFIG_INDEX;
125f9f848faSopenharmony_ci	} else {
126f9f848faSopenharmony_ci		/*
127f9f848faSopenharmony_ci		 * The relationship between config number and config index
128f9f848faSopenharmony_ci		 * is very simple in our case:
129f9f848faSopenharmony_ci		 */
130f9f848faSopenharmony_ci		conf_no--;
131f9f848faSopenharmony_ci	}
132f9f848faSopenharmony_ci
133f9f848faSopenharmony_ci	if (usbd_set_config_index(udev, conf_no)) {
134f9f848faSopenharmony_ci		DPRINTF("set config %d failed\n", conf_no);
135f9f848faSopenharmony_ci		err = USB_ERR_STALLED;
136f9f848faSopenharmony_ci		goto done;
137f9f848faSopenharmony_ci	}
138f9f848faSopenharmony_ci	if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
139f9f848faSopenharmony_ci		DPRINTF("probe and attach failed\n");
140f9f848faSopenharmony_ci		err = USB_ERR_STALLED;
141f9f848faSopenharmony_ci		goto done;
142f9f848faSopenharmony_ci	}
143f9f848faSopenharmony_cidone:
144f9f848faSopenharmony_ci	if (do_unlock)
145f9f848faSopenharmony_ci		usbd_enum_unlock(udev);
146f9f848faSopenharmony_ci	USB_XFER_LOCK(xfer);
147f9f848faSopenharmony_ci	return (err);
148f9f848faSopenharmony_ci}
149f9f848faSopenharmony_ci
150f9f848faSopenharmony_cistatic usb_error_t
151f9f848faSopenharmony_ciusb_check_alt_setting(struct usb_device *udev,
152f9f848faSopenharmony_ci    struct usb_interface *iface, uint8_t alt_index)
153f9f848faSopenharmony_ci{
154f9f848faSopenharmony_ci	uint8_t do_unlock;
155f9f848faSopenharmony_ci	usb_error_t err = USB_ERR_NORMAL_COMPLETION;
156f9f848faSopenharmony_ci
157f9f848faSopenharmony_ci	/* Prevent re-enumeration */
158f9f848faSopenharmony_ci	do_unlock = usbd_enum_lock(udev);
159f9f848faSopenharmony_ci
160f9f848faSopenharmony_ci	if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc))
161f9f848faSopenharmony_ci		err = USB_ERR_INVAL;
162f9f848faSopenharmony_ci
163f9f848faSopenharmony_ci	if (do_unlock)
164f9f848faSopenharmony_ci		usbd_enum_unlock(udev);
165f9f848faSopenharmony_ci
166f9f848faSopenharmony_ci	return (err);
167f9f848faSopenharmony_ci}
168f9f848faSopenharmony_ci
169f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
170f9f848faSopenharmony_ci *	usb_handle_iface_request
171f9f848faSopenharmony_ci *
172f9f848faSopenharmony_ci * Returns:
173f9f848faSopenharmony_ci *    0: Success
174f9f848faSopenharmony_ci * Else: Failure
175f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
176f9f848faSopenharmony_cistatic usb_error_t
177f9f848faSopenharmony_ciusb_handle_iface_request(struct usb_xfer *xfer,
178f9f848faSopenharmony_ci    void **ppdata, uint16_t *plen,
179f9f848faSopenharmony_ci    struct usb_device_request req, uint16_t off, uint8_t state)
180f9f848faSopenharmony_ci{
181f9f848faSopenharmony_ci	struct usb_interface *iface;
182f9f848faSopenharmony_ci	struct usb_interface *iface_parent;	/* parent interface */
183f9f848faSopenharmony_ci	struct usb_device *udev = xfer->xroot->udev;
184f9f848faSopenharmony_ci	int error;
185f9f848faSopenharmony_ci	uint8_t iface_index;
186f9f848faSopenharmony_ci	uint8_t temp_state;
187f9f848faSopenharmony_ci	uint8_t do_unlock;
188f9f848faSopenharmony_ci
189f9f848faSopenharmony_ci	if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
190f9f848faSopenharmony_ci		iface_index = req.wIndex[0];	/* unicast */
191f9f848faSopenharmony_ci	} else {
192f9f848faSopenharmony_ci		iface_index = 0;	/* broadcast */
193f9f848faSopenharmony_ci	}
194f9f848faSopenharmony_ci
195f9f848faSopenharmony_ci	/*
196f9f848faSopenharmony_ci	 * We need to protect against other threads doing probe and
197f9f848faSopenharmony_ci	 * attach:
198f9f848faSopenharmony_ci	 */
199f9f848faSopenharmony_ci	USB_XFER_UNLOCK(xfer);
200f9f848faSopenharmony_ci
201f9f848faSopenharmony_ci	/* Prevent re-enumeration */
202f9f848faSopenharmony_ci	do_unlock = usbd_enum_lock(udev);
203f9f848faSopenharmony_ci
204f9f848faSopenharmony_ci	error = ENXIO;
205f9f848faSopenharmony_ci
206f9f848faSopenharmony_citr_repeat:
207f9f848faSopenharmony_ci	iface = usbd_get_iface(udev, iface_index);
208f9f848faSopenharmony_ci	if ((iface == NULL) ||
209f9f848faSopenharmony_ci	    (iface->idesc == NULL)) {
210f9f848faSopenharmony_ci		/* end of interfaces non-existing interface */
211f9f848faSopenharmony_ci		goto tr_stalled;
212f9f848faSopenharmony_ci	}
213f9f848faSopenharmony_ci	/* set initial state */
214f9f848faSopenharmony_ci
215f9f848faSopenharmony_ci	temp_state = state;
216f9f848faSopenharmony_ci
217f9f848faSopenharmony_ci	/* forward request to interface, if any */
218f9f848faSopenharmony_ci
219f9f848faSopenharmony_ci	if ((error != 0) &&
220f9f848faSopenharmony_ci	    (error != ENOTTY) &&
221f9f848faSopenharmony_ci	    (iface->subdev != NULL) &&
222f9f848faSopenharmony_ci	    device_is_attached(iface->subdev)) {
223f9f848faSopenharmony_ci		error = USB_HANDLE_REQUEST(iface->subdev,
224f9f848faSopenharmony_ci		    &req, ppdata, plen,
225f9f848faSopenharmony_ci		    off, &temp_state);
226f9f848faSopenharmony_ci	}
227f9f848faSopenharmony_ci	iface_parent = usbd_get_iface(udev, iface->parent_iface_index);
228f9f848faSopenharmony_ci
229f9f848faSopenharmony_ci	if ((iface_parent == NULL) ||
230f9f848faSopenharmony_ci	    (iface_parent->idesc == NULL)) {
231f9f848faSopenharmony_ci		/* non-existing interface */
232f9f848faSopenharmony_ci		iface_parent = NULL;
233f9f848faSopenharmony_ci	}
234f9f848faSopenharmony_ci	/* forward request to parent interface, if any */
235f9f848faSopenharmony_ci
236f9f848faSopenharmony_ci	if ((error != 0) &&
237f9f848faSopenharmony_ci	    (error != ENOTTY) &&
238f9f848faSopenharmony_ci	    (iface_parent != NULL) &&
239f9f848faSopenharmony_ci	    (iface_parent->subdev != NULL) &&
240f9f848faSopenharmony_ci	    ((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
241f9f848faSopenharmony_ci	    (iface_parent->subdev != iface->subdev) &&
242f9f848faSopenharmony_ci	    device_is_attached(iface_parent->subdev)) {
243f9f848faSopenharmony_ci		error = USB_HANDLE_REQUEST(iface_parent->subdev,
244f9f848faSopenharmony_ci		    &req, ppdata, plen, off, &temp_state);
245f9f848faSopenharmony_ci	}
246f9f848faSopenharmony_ci	if (error == 0) {
247f9f848faSopenharmony_ci		/* negativly adjust pointer and length */
248f9f848faSopenharmony_ci		*ppdata = ((uint8_t *)(*ppdata)) - off;
249f9f848faSopenharmony_ci		*plen += off;
250f9f848faSopenharmony_ci
251f9f848faSopenharmony_ci		if ((state == USB_HR_NOT_COMPLETE) &&
252f9f848faSopenharmony_ci		    (temp_state == USB_HR_COMPLETE_OK))
253f9f848faSopenharmony_ci			goto tr_short;
254f9f848faSopenharmony_ci		else
255f9f848faSopenharmony_ci			goto tr_valid;
256f9f848faSopenharmony_ci	} else if (error == ENOTTY) {
257f9f848faSopenharmony_ci		goto tr_stalled;
258f9f848faSopenharmony_ci	}
259f9f848faSopenharmony_ci	if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
260f9f848faSopenharmony_ci		iface_index++;		/* iterate */
261f9f848faSopenharmony_ci		goto tr_repeat;
262f9f848faSopenharmony_ci	}
263f9f848faSopenharmony_ci	if (state != USB_HR_NOT_COMPLETE) {
264f9f848faSopenharmony_ci		/* we are complete */
265f9f848faSopenharmony_ci		goto tr_valid;
266f9f848faSopenharmony_ci	}
267f9f848faSopenharmony_ci	switch (req.bmRequestType) {
268f9f848faSopenharmony_ci	case UT_WRITE_INTERFACE:
269f9f848faSopenharmony_ci		switch (req.bRequest) {
270f9f848faSopenharmony_ci		case UR_SET_INTERFACE:
271f9f848faSopenharmony_ci			/*
272f9f848faSopenharmony_ci			 * We assume that the endpoints are the same
273f9f848faSopenharmony_ci			 * across the alternate settings.
274f9f848faSopenharmony_ci			 *
275f9f848faSopenharmony_ci			 * Reset the endpoints, because re-attaching
276f9f848faSopenharmony_ci			 * only a part of the device is not possible.
277f9f848faSopenharmony_ci			 */
278f9f848faSopenharmony_ci			error = usb_check_alt_setting(udev,
279f9f848faSopenharmony_ci			    iface, req.wValue[0]);
280f9f848faSopenharmony_ci			if (error) {
281f9f848faSopenharmony_ci				DPRINTF("alt setting does not exist %s\n",
282f9f848faSopenharmony_ci				    usbd_errstr(error));
283f9f848faSopenharmony_ci				goto tr_stalled;
284f9f848faSopenharmony_ci			}
285f9f848faSopenharmony_ci			error = usb_reset_iface_endpoints(udev, iface_index);
286f9f848faSopenharmony_ci			if (error) {
287f9f848faSopenharmony_ci				DPRINTF("alt setting failed %s\n",
288f9f848faSopenharmony_ci				    usbd_errstr(error));
289f9f848faSopenharmony_ci				goto tr_stalled;
290f9f848faSopenharmony_ci			}
291f9f848faSopenharmony_ci			/* update the current alternate setting */
292f9f848faSopenharmony_ci			iface->alt_index = req.wValue[0];
293f9f848faSopenharmony_ci			break;
294f9f848faSopenharmony_ci
295f9f848faSopenharmony_ci		default:
296f9f848faSopenharmony_ci			goto tr_stalled;
297f9f848faSopenharmony_ci		}
298f9f848faSopenharmony_ci		break;
299f9f848faSopenharmony_ci
300f9f848faSopenharmony_ci	case UT_READ_INTERFACE:
301f9f848faSopenharmony_ci		switch (req.bRequest) {
302f9f848faSopenharmony_ci		case UR_GET_INTERFACE:
303f9f848faSopenharmony_ci			*ppdata = &iface->alt_index;
304f9f848faSopenharmony_ci			*plen = 1;
305f9f848faSopenharmony_ci			break;
306f9f848faSopenharmony_ci
307f9f848faSopenharmony_ci		default:
308f9f848faSopenharmony_ci			goto tr_stalled;
309f9f848faSopenharmony_ci		}
310f9f848faSopenharmony_ci		break;
311f9f848faSopenharmony_ci	default:
312f9f848faSopenharmony_ci		goto tr_stalled;
313f9f848faSopenharmony_ci	}
314f9f848faSopenharmony_citr_valid:
315f9f848faSopenharmony_ci	if (do_unlock)
316f9f848faSopenharmony_ci		usbd_enum_unlock(udev);
317f9f848faSopenharmony_ci	USB_XFER_LOCK(xfer);
318f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);
319f9f848faSopenharmony_ci
320f9f848faSopenharmony_citr_short:
321f9f848faSopenharmony_ci	if (do_unlock)
322f9f848faSopenharmony_ci		usbd_enum_unlock(udev);
323f9f848faSopenharmony_ci	USB_XFER_LOCK(xfer);
324f9f848faSopenharmony_ci	return (USB_ERR_SHORT_XFER);
325f9f848faSopenharmony_ci
326f9f848faSopenharmony_citr_stalled:
327f9f848faSopenharmony_ci	if (do_unlock)
328f9f848faSopenharmony_ci		usbd_enum_unlock(udev);
329f9f848faSopenharmony_ci	USB_XFER_LOCK(xfer);
330f9f848faSopenharmony_ci	return (USB_ERR_STALLED);
331f9f848faSopenharmony_ci}
332f9f848faSopenharmony_ci
333f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
334f9f848faSopenharmony_ci *	usb_handle_stall
335f9f848faSopenharmony_ci *
336f9f848faSopenharmony_ci * Returns:
337f9f848faSopenharmony_ci *    0: Success
338f9f848faSopenharmony_ci * Else: Failure
339f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
340f9f848faSopenharmony_cistatic usb_error_t
341f9f848faSopenharmony_ciusb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall)
342f9f848faSopenharmony_ci{
343f9f848faSopenharmony_ci	struct usb_device *udev = xfer->xroot->udev;
344f9f848faSopenharmony_ci	usb_error_t err;
345f9f848faSopenharmony_ci
346f9f848faSopenharmony_ci	USB_XFER_UNLOCK(xfer);
347f9f848faSopenharmony_ci	err = usbd_set_endpoint_stall(udev,
348f9f848faSopenharmony_ci	    usbd_get_ep_by_addr(udev, ep), do_stall);
349f9f848faSopenharmony_ci	USB_XFER_LOCK(xfer);
350f9f848faSopenharmony_ci	return (err);
351f9f848faSopenharmony_ci}
352f9f848faSopenharmony_ci
353f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
354f9f848faSopenharmony_ci *	usb_handle_get_stall
355f9f848faSopenharmony_ci *
356f9f848faSopenharmony_ci * Returns:
357f9f848faSopenharmony_ci *    0: Success
358f9f848faSopenharmony_ci * Else: Failure
359f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
360f9f848faSopenharmony_cistatic uint8_t
361f9f848faSopenharmony_ciusb_handle_get_stall(struct usb_device *udev, uint8_t ea_val)
362f9f848faSopenharmony_ci{
363f9f848faSopenharmony_ci	struct usb_endpoint *ep;
364f9f848faSopenharmony_ci	uint8_t halted;
365f9f848faSopenharmony_ci
366f9f848faSopenharmony_ci	ep = usbd_get_ep_by_addr(udev, ea_val);
367f9f848faSopenharmony_ci	if (ep == NULL) {
368f9f848faSopenharmony_ci		/* nothing to do */
369f9f848faSopenharmony_ci		return (0);
370f9f848faSopenharmony_ci	}
371f9f848faSopenharmony_ci	USB_BUS_LOCK(udev->bus);
372f9f848faSopenharmony_ci	halted = ep->is_stalled;
373f9f848faSopenharmony_ci	USB_BUS_UNLOCK(udev->bus);
374f9f848faSopenharmony_ci
375f9f848faSopenharmony_ci	return (halted);
376f9f848faSopenharmony_ci}
377f9f848faSopenharmony_ci
378f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
379f9f848faSopenharmony_ci *	usb_handle_remote_wakeup
380f9f848faSopenharmony_ci *
381f9f848faSopenharmony_ci * Returns:
382f9f848faSopenharmony_ci *    0: Success
383f9f848faSopenharmony_ci * Else: Failure
384f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
385f9f848faSopenharmony_cistatic usb_error_t
386f9f848faSopenharmony_ciusb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on)
387f9f848faSopenharmony_ci{
388f9f848faSopenharmony_ci	struct usb_device *udev;
389f9f848faSopenharmony_ci	struct usb_bus *bus;
390f9f848faSopenharmony_ci
391f9f848faSopenharmony_ci	udev = xfer->xroot->udev;
392f9f848faSopenharmony_ci	bus = udev->bus;
393f9f848faSopenharmony_ci
394f9f848faSopenharmony_ci	USB_BUS_LOCK(bus);
395f9f848faSopenharmony_ci
396f9f848faSopenharmony_ci	if (is_on) {
397f9f848faSopenharmony_ci		udev->flags.remote_wakeup = 1;
398f9f848faSopenharmony_ci	} else {
399f9f848faSopenharmony_ci		udev->flags.remote_wakeup = 0;
400f9f848faSopenharmony_ci	}
401f9f848faSopenharmony_ci
402f9f848faSopenharmony_ci	USB_BUS_UNLOCK(bus);
403f9f848faSopenharmony_ci
404f9f848faSopenharmony_ci#if USB_HAVE_POWERD
405f9f848faSopenharmony_ci	/* In case we are out of sync, update the power state. */
406f9f848faSopenharmony_ci	usb_bus_power_update(udev->bus);
407f9f848faSopenharmony_ci#endif
408f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);			/* success */
409f9f848faSopenharmony_ci}
410f9f848faSopenharmony_ci
411f9f848faSopenharmony_ci/*------------------------------------------------------------------------*
412f9f848faSopenharmony_ci *	usb_handle_request
413f9f848faSopenharmony_ci *
414f9f848faSopenharmony_ci * Internal state sequence:
415f9f848faSopenharmony_ci *
416f9f848faSopenharmony_ci * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR
417f9f848faSopenharmony_ci *
418f9f848faSopenharmony_ci * Returns:
419f9f848faSopenharmony_ci * 0: Ready to start hardware
420f9f848faSopenharmony_ci * Else: Stall current transfer, if any
421f9f848faSopenharmony_ci *------------------------------------------------------------------------*/
422f9f848faSopenharmony_cistatic usb_error_t
423f9f848faSopenharmony_ciusb_handle_request(struct usb_xfer *xfer)
424f9f848faSopenharmony_ci{
425f9f848faSopenharmony_ci	struct usb_device_request req;
426f9f848faSopenharmony_ci	struct usb_device *udev;
427f9f848faSopenharmony_ci	const void *src_zcopy;		/* zero-copy source pointer */
428f9f848faSopenharmony_ci	const void *src_mcopy;		/* non zero-copy source pointer */
429f9f848faSopenharmony_ci	uint16_t off;			/* data offset */
430f9f848faSopenharmony_ci	uint16_t rem;			/* data remainder */
431f9f848faSopenharmony_ci	uint16_t max_len;		/* max fragment length */
432f9f848faSopenharmony_ci	uint16_t wValue;
433f9f848faSopenharmony_ci	uint8_t state;
434f9f848faSopenharmony_ci	uint8_t is_complete = 1;
435f9f848faSopenharmony_ci	usb_error_t err;
436f9f848faSopenharmony_ci	union {
437f9f848faSopenharmony_ci		uWord	wStatus;
438f9f848faSopenharmony_ci		uint8_t	buf[2];
439f9f848faSopenharmony_ci	} temp;
440f9f848faSopenharmony_ci
441f9f848faSopenharmony_ci	/*
442f9f848faSopenharmony_ci	 * Filter the USB transfer state into
443f9f848faSopenharmony_ci	 * something which we understand:
444f9f848faSopenharmony_ci	 */
445f9f848faSopenharmony_ci
446f9f848faSopenharmony_ci	switch (USB_GET_STATE(xfer)) {
447f9f848faSopenharmony_ci	case USB_ST_SETUP:
448f9f848faSopenharmony_ci		state = USB_HR_NOT_COMPLETE;
449f9f848faSopenharmony_ci
450f9f848faSopenharmony_ci		if (!xfer->flags_int.control_act) {
451f9f848faSopenharmony_ci			/* nothing to do */
452f9f848faSopenharmony_ci			goto tr_stalled;
453f9f848faSopenharmony_ci		}
454f9f848faSopenharmony_ci		break;
455f9f848faSopenharmony_ci	case USB_ST_TRANSFERRED:
456f9f848faSopenharmony_ci		if (!xfer->flags_int.control_act) {
457f9f848faSopenharmony_ci			state = USB_HR_COMPLETE_OK;
458f9f848faSopenharmony_ci		} else {
459f9f848faSopenharmony_ci			state = USB_HR_NOT_COMPLETE;
460f9f848faSopenharmony_ci		}
461f9f848faSopenharmony_ci		break;
462f9f848faSopenharmony_ci	default:
463f9f848faSopenharmony_ci		state = USB_HR_COMPLETE_ERR;
464f9f848faSopenharmony_ci		break;
465f9f848faSopenharmony_ci	}
466f9f848faSopenharmony_ci
467f9f848faSopenharmony_ci	/* reset frame stuff */
468f9f848faSopenharmony_ci
469f9f848faSopenharmony_ci	usbd_xfer_set_frame_len(xfer, 0, 0);
470f9f848faSopenharmony_ci
471f9f848faSopenharmony_ci	usbd_xfer_set_frame_offset(xfer, 0, 0);
472f9f848faSopenharmony_ci	usbd_xfer_set_frame_offset(xfer, sizeof(req), 1);
473f9f848faSopenharmony_ci
474f9f848faSopenharmony_ci	/* get the current request, if any */
475f9f848faSopenharmony_ci
476f9f848faSopenharmony_ci	usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
477f9f848faSopenharmony_ci
478f9f848faSopenharmony_ci	if (xfer->flags_int.control_rem == 0xFFFF) {
479f9f848faSopenharmony_ci		/* first time - not initialised */
480f9f848faSopenharmony_ci		rem = UGETW(req.wLength);
481f9f848faSopenharmony_ci		off = 0;
482f9f848faSopenharmony_ci	} else {
483f9f848faSopenharmony_ci		/* not first time - initialised */
484f9f848faSopenharmony_ci		rem = xfer->flags_int.control_rem;
485f9f848faSopenharmony_ci		off = UGETW(req.wLength) - rem;
486f9f848faSopenharmony_ci	}
487f9f848faSopenharmony_ci
488f9f848faSopenharmony_ci	/* set some defaults */
489f9f848faSopenharmony_ci
490f9f848faSopenharmony_ci	max_len = 0;
491f9f848faSopenharmony_ci	src_zcopy = NULL;
492f9f848faSopenharmony_ci	src_mcopy = NULL;
493f9f848faSopenharmony_ci	udev = xfer->xroot->udev;
494f9f848faSopenharmony_ci
495f9f848faSopenharmony_ci	/* get some request fields decoded */
496f9f848faSopenharmony_ci
497f9f848faSopenharmony_ci	wValue = UGETW(req.wValue);
498f9f848faSopenharmony_ci
499f9f848faSopenharmony_ci	DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
500f9f848faSopenharmony_ci	    "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
501f9f848faSopenharmony_ci	    req.bRequest, wValue, UGETW(req.wIndex), off, rem, state);
502f9f848faSopenharmony_ci
503f9f848faSopenharmony_ci	/* demultiplex the control request */
504f9f848faSopenharmony_ci
505f9f848faSopenharmony_ci	switch (req.bmRequestType) {
506f9f848faSopenharmony_ci	case UT_READ_DEVICE:
507f9f848faSopenharmony_ci		if (state != USB_HR_NOT_COMPLETE) {
508f9f848faSopenharmony_ci			break;
509f9f848faSopenharmony_ci		}
510f9f848faSopenharmony_ci		switch (req.bRequest) {
511f9f848faSopenharmony_ci		case UR_GET_DESCRIPTOR:
512f9f848faSopenharmony_ci			goto tr_handle_get_descriptor;
513f9f848faSopenharmony_ci		case UR_GET_CONFIG:
514f9f848faSopenharmony_ci			goto tr_handle_get_config;
515f9f848faSopenharmony_ci		case UR_GET_STATUS:
516f9f848faSopenharmony_ci			goto tr_handle_get_status;
517f9f848faSopenharmony_ci		default:
518f9f848faSopenharmony_ci			goto tr_stalled;
519f9f848faSopenharmony_ci		}
520f9f848faSopenharmony_ci		break;
521f9f848faSopenharmony_ci
522f9f848faSopenharmony_ci	case UT_WRITE_DEVICE:
523f9f848faSopenharmony_ci		switch (req.bRequest) {
524f9f848faSopenharmony_ci		case UR_SET_ADDRESS:
525f9f848faSopenharmony_ci			goto tr_handle_set_address;
526f9f848faSopenharmony_ci		case UR_SET_CONFIG:
527f9f848faSopenharmony_ci			goto tr_handle_set_config;
528f9f848faSopenharmony_ci		case UR_CLEAR_FEATURE:
529f9f848faSopenharmony_ci			switch (wValue) {
530f9f848faSopenharmony_ci			case UF_DEVICE_REMOTE_WAKEUP:
531f9f848faSopenharmony_ci				goto tr_handle_clear_wakeup;
532f9f848faSopenharmony_ci			default:
533f9f848faSopenharmony_ci				goto tr_stalled;
534f9f848faSopenharmony_ci			}
535f9f848faSopenharmony_ci			break;
536f9f848faSopenharmony_ci		case UR_SET_FEATURE:
537f9f848faSopenharmony_ci			switch (wValue) {
538f9f848faSopenharmony_ci			case UF_DEVICE_REMOTE_WAKEUP:
539f9f848faSopenharmony_ci				goto tr_handle_set_wakeup;
540f9f848faSopenharmony_ci			default:
541f9f848faSopenharmony_ci				goto tr_stalled;
542f9f848faSopenharmony_ci			}
543f9f848faSopenharmony_ci			break;
544f9f848faSopenharmony_ci		default:
545f9f848faSopenharmony_ci			goto tr_stalled;
546f9f848faSopenharmony_ci		}
547f9f848faSopenharmony_ci		break;
548f9f848faSopenharmony_ci
549f9f848faSopenharmony_ci	case UT_WRITE_ENDPOINT:
550f9f848faSopenharmony_ci		switch (req.bRequest) {
551f9f848faSopenharmony_ci		case UR_CLEAR_FEATURE:
552f9f848faSopenharmony_ci			switch (wValue) {
553f9f848faSopenharmony_ci			case UF_ENDPOINT_HALT:
554f9f848faSopenharmony_ci				goto tr_handle_clear_halt;
555f9f848faSopenharmony_ci			default:
556f9f848faSopenharmony_ci				goto tr_stalled;
557f9f848faSopenharmony_ci			}
558f9f848faSopenharmony_ci			break;
559f9f848faSopenharmony_ci		case UR_SET_FEATURE:
560f9f848faSopenharmony_ci			switch (wValue) {
561f9f848faSopenharmony_ci			case UF_ENDPOINT_HALT:
562f9f848faSopenharmony_ci				goto tr_handle_set_halt;
563f9f848faSopenharmony_ci			default:
564f9f848faSopenharmony_ci				goto tr_stalled;
565f9f848faSopenharmony_ci			}
566f9f848faSopenharmony_ci			break;
567f9f848faSopenharmony_ci		default:
568f9f848faSopenharmony_ci			goto tr_stalled;
569f9f848faSopenharmony_ci		}
570f9f848faSopenharmony_ci		break;
571f9f848faSopenharmony_ci
572f9f848faSopenharmony_ci	case UT_READ_ENDPOINT:
573f9f848faSopenharmony_ci		switch (req.bRequest) {
574f9f848faSopenharmony_ci		case UR_GET_STATUS:
575f9f848faSopenharmony_ci			goto tr_handle_get_ep_status;
576f9f848faSopenharmony_ci		default:
577f9f848faSopenharmony_ci			goto tr_stalled;
578f9f848faSopenharmony_ci		}
579f9f848faSopenharmony_ci		break;
580f9f848faSopenharmony_ci	default:
581f9f848faSopenharmony_ci		/* we use "USB_ADD_BYTES" to de-const the src_zcopy */
582f9f848faSopenharmony_ci		err = usb_handle_iface_request(xfer,
583f9f848faSopenharmony_ci		    USB_ADD_BYTES(&src_zcopy, 0),
584f9f848faSopenharmony_ci		    &max_len, req, off, state);
585f9f848faSopenharmony_ci		if (err == 0) {
586f9f848faSopenharmony_ci			is_complete = 0;
587f9f848faSopenharmony_ci			goto tr_valid;
588f9f848faSopenharmony_ci		} else if (err == USB_ERR_SHORT_XFER) {
589f9f848faSopenharmony_ci			goto tr_valid;
590f9f848faSopenharmony_ci		}
591f9f848faSopenharmony_ci		/*
592f9f848faSopenharmony_ci		 * Reset zero-copy pointer and max length
593f9f848faSopenharmony_ci		 * variable in case they were unintentionally
594f9f848faSopenharmony_ci		 * set:
595f9f848faSopenharmony_ci		 */
596f9f848faSopenharmony_ci		src_zcopy = NULL;
597f9f848faSopenharmony_ci		max_len = 0;
598f9f848faSopenharmony_ci
599f9f848faSopenharmony_ci		/*
600f9f848faSopenharmony_ci		 * Check if we have a vendor specific
601f9f848faSopenharmony_ci		 * descriptor:
602f9f848faSopenharmony_ci		 */
603f9f848faSopenharmony_ci		goto tr_handle_get_descriptor;
604f9f848faSopenharmony_ci	}
605f9f848faSopenharmony_ci	goto tr_valid;
606f9f848faSopenharmony_ci
607f9f848faSopenharmony_citr_handle_get_descriptor:
608f9f848faSopenharmony_ci	err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
609f9f848faSopenharmony_ci	if (err)
610f9f848faSopenharmony_ci		goto tr_stalled;
611f9f848faSopenharmony_ci	if (src_zcopy == NULL)
612f9f848faSopenharmony_ci		goto tr_stalled;
613f9f848faSopenharmony_ci	goto tr_valid;
614f9f848faSopenharmony_ci
615f9f848faSopenharmony_citr_handle_get_config:
616f9f848faSopenharmony_ci	temp.buf[0] = udev->curr_config_no;
617f9f848faSopenharmony_ci	src_mcopy = temp.buf;
618f9f848faSopenharmony_ci	max_len = 1;
619f9f848faSopenharmony_ci	goto tr_valid;
620f9f848faSopenharmony_ci
621f9f848faSopenharmony_citr_handle_get_status:
622f9f848faSopenharmony_ci
623f9f848faSopenharmony_ci	wValue = 0;
624f9f848faSopenharmony_ci
625f9f848faSopenharmony_ci	USB_BUS_LOCK(udev->bus);
626f9f848faSopenharmony_ci	if (udev->flags.remote_wakeup) {
627f9f848faSopenharmony_ci		wValue |= UDS_REMOTE_WAKEUP;
628f9f848faSopenharmony_ci	}
629f9f848faSopenharmony_ci	if (udev->flags.self_powered) {
630f9f848faSopenharmony_ci		wValue |= UDS_SELF_POWERED;
631f9f848faSopenharmony_ci	}
632f9f848faSopenharmony_ci	USB_BUS_UNLOCK(udev->bus);
633f9f848faSopenharmony_ci
634f9f848faSopenharmony_ci	USETW(temp.wStatus, wValue);
635f9f848faSopenharmony_ci	src_mcopy = temp.wStatus;
636f9f848faSopenharmony_ci	max_len = sizeof(temp.wStatus);
637f9f848faSopenharmony_ci	goto tr_valid;
638f9f848faSopenharmony_ci
639f9f848faSopenharmony_citr_handle_set_address:
640f9f848faSopenharmony_ci	if (state == USB_HR_NOT_COMPLETE) {
641f9f848faSopenharmony_ci		if (wValue >= 0x80) {
642f9f848faSopenharmony_ci			/* invalid value */
643f9f848faSopenharmony_ci			goto tr_stalled;
644f9f848faSopenharmony_ci		} else if (udev->curr_config_no != 0) {
645f9f848faSopenharmony_ci			/* we are configured ! */
646f9f848faSopenharmony_ci			goto tr_stalled;
647f9f848faSopenharmony_ci		}
648f9f848faSopenharmony_ci	} else if (state != USB_HR_NOT_COMPLETE) {
649f9f848faSopenharmony_ci		udev->address = (wValue & 0x7F);
650f9f848faSopenharmony_ci		goto tr_bad_context;
651f9f848faSopenharmony_ci	}
652f9f848faSopenharmony_ci	goto tr_valid;
653f9f848faSopenharmony_ci
654f9f848faSopenharmony_citr_handle_set_config:
655f9f848faSopenharmony_ci	if (state == USB_HR_NOT_COMPLETE) {
656f9f848faSopenharmony_ci		if (usb_handle_set_config(xfer, req.wValue[0])) {
657f9f848faSopenharmony_ci			goto tr_stalled;
658f9f848faSopenharmony_ci		}
659f9f848faSopenharmony_ci	}
660f9f848faSopenharmony_ci	goto tr_valid;
661f9f848faSopenharmony_ci
662f9f848faSopenharmony_citr_handle_clear_halt:
663f9f848faSopenharmony_ci	if (state == USB_HR_NOT_COMPLETE) {
664f9f848faSopenharmony_ci		if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) {
665f9f848faSopenharmony_ci			goto tr_stalled;
666f9f848faSopenharmony_ci		}
667f9f848faSopenharmony_ci	}
668f9f848faSopenharmony_ci	goto tr_valid;
669f9f848faSopenharmony_ci
670f9f848faSopenharmony_citr_handle_clear_wakeup:
671f9f848faSopenharmony_ci	if (state == USB_HR_NOT_COMPLETE) {
672f9f848faSopenharmony_ci		if (usb_handle_remote_wakeup(xfer, 0)) {
673f9f848faSopenharmony_ci			goto tr_stalled;
674f9f848faSopenharmony_ci		}
675f9f848faSopenharmony_ci	}
676f9f848faSopenharmony_ci	goto tr_valid;
677f9f848faSopenharmony_ci
678f9f848faSopenharmony_citr_handle_set_halt:
679f9f848faSopenharmony_ci	if (state == USB_HR_NOT_COMPLETE) {
680f9f848faSopenharmony_ci		if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) {
681f9f848faSopenharmony_ci			goto tr_stalled;
682f9f848faSopenharmony_ci		}
683f9f848faSopenharmony_ci	}
684f9f848faSopenharmony_ci	goto tr_valid;
685f9f848faSopenharmony_ci
686f9f848faSopenharmony_citr_handle_set_wakeup:
687f9f848faSopenharmony_ci	if (state == USB_HR_NOT_COMPLETE) {
688f9f848faSopenharmony_ci		if (usb_handle_remote_wakeup(xfer, 1)) {
689f9f848faSopenharmony_ci			goto tr_stalled;
690f9f848faSopenharmony_ci		}
691f9f848faSopenharmony_ci	}
692f9f848faSopenharmony_ci	goto tr_valid;
693f9f848faSopenharmony_ci
694f9f848faSopenharmony_citr_handle_get_ep_status:
695f9f848faSopenharmony_ci	if (state == USB_HR_NOT_COMPLETE) {
696f9f848faSopenharmony_ci		temp.wStatus[0] =
697f9f848faSopenharmony_ci		    usb_handle_get_stall(udev, req.wIndex[0]);
698f9f848faSopenharmony_ci		temp.wStatus[1] = 0;
699f9f848faSopenharmony_ci		src_mcopy = temp.wStatus;
700f9f848faSopenharmony_ci		max_len = sizeof(temp.wStatus);
701f9f848faSopenharmony_ci	}
702f9f848faSopenharmony_ci	goto tr_valid;
703f9f848faSopenharmony_ci
704f9f848faSopenharmony_citr_valid:
705f9f848faSopenharmony_ci	if (state != USB_HR_NOT_COMPLETE) {
706f9f848faSopenharmony_ci		goto tr_stalled;
707f9f848faSopenharmony_ci	}
708f9f848faSopenharmony_ci	/* subtract offset from length */
709f9f848faSopenharmony_ci
710f9f848faSopenharmony_ci	max_len -= off;
711f9f848faSopenharmony_ci
712f9f848faSopenharmony_ci	/* Compute the real maximum data length */
713f9f848faSopenharmony_ci
714f9f848faSopenharmony_ci	if (max_len > xfer->max_data_length) {
715f9f848faSopenharmony_ci		max_len = usbd_xfer_max_len(xfer);
716f9f848faSopenharmony_ci	}
717f9f848faSopenharmony_ci	if (max_len > rem) {
718f9f848faSopenharmony_ci		max_len = rem;
719f9f848faSopenharmony_ci	}
720f9f848faSopenharmony_ci	/*
721f9f848faSopenharmony_ci	 * If the remainder is greater than the maximum data length,
722f9f848faSopenharmony_ci	 * we need to truncate the value for the sake of the
723f9f848faSopenharmony_ci	 * comparison below:
724f9f848faSopenharmony_ci	 */
725f9f848faSopenharmony_ci	if (rem > xfer->max_data_length) {
726f9f848faSopenharmony_ci		rem = usbd_xfer_max_len(xfer);
727f9f848faSopenharmony_ci	}
728f9f848faSopenharmony_ci	if ((rem != max_len) && (is_complete != 0)) {
729f9f848faSopenharmony_ci		/*
730f9f848faSopenharmony_ci	         * If we don't transfer the data we can transfer, then
731f9f848faSopenharmony_ci	         * the transfer is short !
732f9f848faSopenharmony_ci	         */
733f9f848faSopenharmony_ci		xfer->flags.force_short_xfer = 1;
734f9f848faSopenharmony_ci		xfer->nframes = 2;
735f9f848faSopenharmony_ci	} else {
736f9f848faSopenharmony_ci		/*
737f9f848faSopenharmony_ci		 * Default case
738f9f848faSopenharmony_ci		 */
739f9f848faSopenharmony_ci		xfer->flags.force_short_xfer = 0;
740f9f848faSopenharmony_ci		xfer->nframes = max_len ? 2 : 1;
741f9f848faSopenharmony_ci	}
742f9f848faSopenharmony_ci	if (max_len > 0) {
743f9f848faSopenharmony_ci		if (src_mcopy) {
744f9f848faSopenharmony_ci			src_mcopy = USB_ADD_BYTES(src_mcopy, off);
745f9f848faSopenharmony_ci			usbd_copy_in(xfer->frbuffers + 1, 0,
746f9f848faSopenharmony_ci			    src_mcopy, max_len);
747f9f848faSopenharmony_ci			usbd_xfer_set_frame_len(xfer, 1, max_len);
748f9f848faSopenharmony_ci		} else {
749f9f848faSopenharmony_ci			usbd_xfer_set_frame_data(xfer, 1,
750f9f848faSopenharmony_ci			    USB_ADD_BYTES(src_zcopy, off), max_len);
751f9f848faSopenharmony_ci		}
752f9f848faSopenharmony_ci	} else {
753f9f848faSopenharmony_ci		/* the end is reached, send status */
754f9f848faSopenharmony_ci		xfer->flags.manual_status = 0;
755f9f848faSopenharmony_ci		usbd_xfer_set_frame_len(xfer, 1, 0);
756f9f848faSopenharmony_ci	}
757f9f848faSopenharmony_ci	DPRINTF("success\n");
758f9f848faSopenharmony_ci	return (USB_ERR_NORMAL_COMPLETION);			/* success */
759f9f848faSopenharmony_ci
760f9f848faSopenharmony_citr_stalled:
761f9f848faSopenharmony_ci	DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ?
762f9f848faSopenharmony_ci	    "complete" : "stalled");
763f9f848faSopenharmony_ci	return (USB_ERR_STALLED);
764f9f848faSopenharmony_ci
765f9f848faSopenharmony_citr_bad_context:
766f9f848faSopenharmony_ci	DPRINTF("bad context\n");
767f9f848faSopenharmony_ci	return (USB_ERR_BAD_CONTEXT);
768f9f848faSopenharmony_ci}
769