102ac6454SAndrew Thompson /* $FreeBSD$ */ 202ac6454SAndrew Thompson /*- 302ac6454SAndrew Thompson * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 402ac6454SAndrew Thompson * 502ac6454SAndrew Thompson * Redistribution and use in source and binary forms, with or without 602ac6454SAndrew Thompson * modification, are permitted provided that the following conditions 702ac6454SAndrew Thompson * are met: 802ac6454SAndrew Thompson * 1. Redistributions of source code must retain the above copyright 902ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer. 1002ac6454SAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 1102ac6454SAndrew Thompson * notice, this list of conditions and the following disclaimer in the 1202ac6454SAndrew Thompson * documentation and/or other materials provided with the distribution. 1302ac6454SAndrew Thompson * 1402ac6454SAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1502ac6454SAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1602ac6454SAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1702ac6454SAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1802ac6454SAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1902ac6454SAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2002ac6454SAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2102ac6454SAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2202ac6454SAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2302ac6454SAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2402ac6454SAndrew Thompson * SUCH DAMAGE. 2502ac6454SAndrew Thompson */ 2602ac6454SAndrew Thompson 27d2b99310SHans Petter Selasky #ifdef USB_GLOBAL_INCLUDE_FILE 28d2b99310SHans Petter Selasky #include USB_GLOBAL_INCLUDE_FILE 29d2b99310SHans Petter Selasky #else 30ed6d949aSAndrew Thompson #include <sys/stdint.h> 31ed6d949aSAndrew Thompson #include <sys/stddef.h> 32ed6d949aSAndrew Thompson #include <sys/param.h> 33ed6d949aSAndrew Thompson #include <sys/queue.h> 34ed6d949aSAndrew Thompson #include <sys/types.h> 35ed6d949aSAndrew Thompson #include <sys/systm.h> 36ed6d949aSAndrew Thompson #include <sys/kernel.h> 37ed6d949aSAndrew Thompson #include <sys/bus.h> 38ed6d949aSAndrew Thompson #include <sys/module.h> 39ed6d949aSAndrew Thompson #include <sys/lock.h> 40ed6d949aSAndrew Thompson #include <sys/mutex.h> 41ed6d949aSAndrew Thompson #include <sys/condvar.h> 42ed6d949aSAndrew Thompson #include <sys/sysctl.h> 43ed6d949aSAndrew Thompson #include <sys/sx.h> 44ed6d949aSAndrew Thompson #include <sys/unistd.h> 45ed6d949aSAndrew Thompson #include <sys/callout.h> 46ed6d949aSAndrew Thompson #include <sys/malloc.h> 47ed6d949aSAndrew Thompson #include <sys/priv.h> 48ed6d949aSAndrew Thompson 4902ac6454SAndrew Thompson #include <dev/usb/usb.h> 50ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 51bd73b187SAlfred Perlstein #include <dev/usb/usbdi_util.h> 52ed6d949aSAndrew Thompson #include "usb_if.h" 5302ac6454SAndrew Thompson 54a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 5502ac6454SAndrew Thompson 5602ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5702ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6202ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 6302ac6454SAndrew Thompson #include <dev/usb/usb_hub.h> 6402ac6454SAndrew Thompson 6502ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6602ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 67d2b99310SHans Petter Selasky #endif /* USB_GLOBAL_INCLUDE_FILE */ 6802ac6454SAndrew Thompson 6902ac6454SAndrew Thompson /* function prototypes */ 7002ac6454SAndrew Thompson 71a593f6b8SAndrew Thompson static uint8_t usb_handle_get_stall(struct usb_device *, uint8_t); 72a593f6b8SAndrew Thompson static usb_error_t usb_handle_remote_wakeup(struct usb_xfer *, uint8_t); 73a593f6b8SAndrew Thompson static usb_error_t usb_handle_request(struct usb_xfer *); 74a593f6b8SAndrew Thompson static usb_error_t usb_handle_set_config(struct usb_xfer *, uint8_t); 75a593f6b8SAndrew Thompson static usb_error_t usb_handle_set_stall(struct usb_xfer *, uint8_t, 7602ac6454SAndrew Thompson uint8_t); 77a593f6b8SAndrew Thompson static usb_error_t usb_handle_iface_request(struct usb_xfer *, void **, 78760bc48eSAndrew Thompson uint16_t *, struct usb_device_request, uint16_t, 7902ac6454SAndrew Thompson uint8_t); 8002ac6454SAndrew Thompson 8102ac6454SAndrew Thompson /*------------------------------------------------------------------------* 82a593f6b8SAndrew Thompson * usb_handle_request_callback 8302ac6454SAndrew Thompson * 8402ac6454SAndrew Thompson * This function is the USB callback for generic USB Device control 8502ac6454SAndrew Thompson * transfers. 8602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 8702ac6454SAndrew Thompson void 88ed6d949aSAndrew Thompson usb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error) 8902ac6454SAndrew Thompson { 90e0a69b51SAndrew Thompson usb_error_t err; 9102ac6454SAndrew Thompson 9202ac6454SAndrew Thompson /* check the current transfer state */ 9302ac6454SAndrew Thompson 9402ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 9502ac6454SAndrew Thompson case USB_ST_SETUP: 9602ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 9702ac6454SAndrew Thompson 9802ac6454SAndrew Thompson /* handle the request */ 99a593f6b8SAndrew Thompson err = usb_handle_request(xfer); 10002ac6454SAndrew Thompson 10102ac6454SAndrew Thompson if (err) { 10202ac6454SAndrew Thompson 10302ac6454SAndrew Thompson if (err == USB_ERR_BAD_CONTEXT) { 10402ac6454SAndrew Thompson /* we need to re-setup the control transfer */ 105a593f6b8SAndrew Thompson usb_needs_explore(xfer->xroot->bus, 0); 10602ac6454SAndrew Thompson break; 10702ac6454SAndrew Thompson } 10802ac6454SAndrew Thompson goto tr_restart; 10902ac6454SAndrew Thompson } 110a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 11102ac6454SAndrew Thompson break; 11202ac6454SAndrew Thompson 11302ac6454SAndrew Thompson default: 114a755d548SAndrew Thompson /* check if a control transfer is active */ 115a755d548SAndrew Thompson if (xfer->flags_int.control_rem != 0xFFFF) { 116a755d548SAndrew Thompson /* handle the request */ 117a593f6b8SAndrew Thompson err = usb_handle_request(xfer); 118a755d548SAndrew Thompson } 11902ac6454SAndrew Thompson if (xfer->error != USB_ERR_CANCELLED) { 12002ac6454SAndrew Thompson /* should not happen - try stalling */ 12102ac6454SAndrew Thompson goto tr_restart; 12202ac6454SAndrew Thompson } 12302ac6454SAndrew Thompson break; 12402ac6454SAndrew Thompson } 12502ac6454SAndrew Thompson return; 12602ac6454SAndrew Thompson 12702ac6454SAndrew Thompson tr_restart: 128a755d548SAndrew Thompson /* 129a755d548SAndrew Thompson * If a control transfer is active, stall it, and wait for the 130a755d548SAndrew Thompson * next control transfer. 131a755d548SAndrew Thompson */ 132ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request)); 13302ac6454SAndrew Thompson xfer->nframes = 1; 13402ac6454SAndrew Thompson xfer->flags.manual_status = 1; 13502ac6454SAndrew Thompson xfer->flags.force_short_xfer = 0; 136ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); /* cancel previous transfer, if any */ 137a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 13802ac6454SAndrew Thompson } 13902ac6454SAndrew Thompson 14002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 141a593f6b8SAndrew Thompson * usb_handle_set_config 14202ac6454SAndrew Thompson * 14302ac6454SAndrew Thompson * Returns: 14402ac6454SAndrew Thompson * 0: Success 14502ac6454SAndrew Thompson * Else: Failure 14602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 147e0a69b51SAndrew Thompson static usb_error_t 148a593f6b8SAndrew Thompson usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) 14902ac6454SAndrew Thompson { 150760bc48eSAndrew Thompson struct usb_device *udev = xfer->xroot->udev; 151e0a69b51SAndrew Thompson usb_error_t err = 0; 15202ac6454SAndrew Thompson 15302ac6454SAndrew Thompson /* 15402ac6454SAndrew Thompson * We need to protect against other threads doing probe and 15502ac6454SAndrew Thompson * attach: 15602ac6454SAndrew Thompson */ 15702ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 158cb18f7d1SAlfred Perlstein 159cb18f7d1SAlfred Perlstein usbd_enum_lock(udev); 16002ac6454SAndrew Thompson 16102ac6454SAndrew Thompson if (conf_no == USB_UNCONFIG_NO) { 16202ac6454SAndrew Thompson conf_no = USB_UNCONFIG_INDEX; 16302ac6454SAndrew Thompson } else { 16402ac6454SAndrew Thompson /* 16502ac6454SAndrew Thompson * The relationship between config number and config index 16602ac6454SAndrew Thompson * is very simple in our case: 16702ac6454SAndrew Thompson */ 16802ac6454SAndrew Thompson conf_no--; 16902ac6454SAndrew Thompson } 17002ac6454SAndrew Thompson 171a593f6b8SAndrew Thompson if (usbd_set_config_index(udev, conf_no)) { 17202ac6454SAndrew Thompson DPRINTF("set config %d failed\n", conf_no); 17302ac6454SAndrew Thompson err = USB_ERR_STALLED; 17402ac6454SAndrew Thompson goto done; 17502ac6454SAndrew Thompson } 176a593f6b8SAndrew Thompson if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { 17702ac6454SAndrew Thompson DPRINTF("probe and attach failed\n"); 17802ac6454SAndrew Thompson err = USB_ERR_STALLED; 17902ac6454SAndrew Thompson goto done; 18002ac6454SAndrew Thompson } 18102ac6454SAndrew Thompson done: 182cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 18302ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 18402ac6454SAndrew Thompson return (err); 18502ac6454SAndrew Thompson } 18602ac6454SAndrew Thompson 187bd73b187SAlfred Perlstein static usb_error_t 188bd73b187SAlfred Perlstein usb_check_alt_setting(struct usb_device *udev, 189bd73b187SAlfred Perlstein struct usb_interface *iface, uint8_t alt_index) 190bd73b187SAlfred Perlstein { 191bd73b187SAlfred Perlstein uint8_t do_unlock; 192bd73b187SAlfred Perlstein usb_error_t err = 0; 193bd73b187SAlfred Perlstein 194*6950c75fSHans Petter Selasky /* Prevent re-enumeration */ 195*6950c75fSHans Petter Selasky do_unlock = usbd_enum_lock(udev); 196bd73b187SAlfred Perlstein 197bd73b187SAlfred Perlstein if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc)) 198bd73b187SAlfred Perlstein err = USB_ERR_INVAL; 199bd73b187SAlfred Perlstein 200cb18f7d1SAlfred Perlstein if (do_unlock) 201cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 202cb18f7d1SAlfred Perlstein 203bd73b187SAlfred Perlstein return (err); 204bd73b187SAlfred Perlstein } 205bd73b187SAlfred Perlstein 20602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 207a593f6b8SAndrew Thompson * usb_handle_iface_request 20802ac6454SAndrew Thompson * 20902ac6454SAndrew Thompson * Returns: 21002ac6454SAndrew Thompson * 0: Success 21102ac6454SAndrew Thompson * Else: Failure 21202ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 213e0a69b51SAndrew Thompson static usb_error_t 214a593f6b8SAndrew Thompson usb_handle_iface_request(struct usb_xfer *xfer, 21502ac6454SAndrew Thompson void **ppdata, uint16_t *plen, 216760bc48eSAndrew Thompson struct usb_device_request req, uint16_t off, uint8_t state) 21702ac6454SAndrew Thompson { 218760bc48eSAndrew Thompson struct usb_interface *iface; 219760bc48eSAndrew Thompson struct usb_interface *iface_parent; /* parent interface */ 220760bc48eSAndrew Thompson struct usb_device *udev = xfer->xroot->udev; 22102ac6454SAndrew Thompson int error; 22202ac6454SAndrew Thompson uint8_t iface_index; 22329bd7d7eSAndrew Thompson uint8_t temp_state; 22402ac6454SAndrew Thompson 22502ac6454SAndrew Thompson if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { 22602ac6454SAndrew Thompson iface_index = req.wIndex[0]; /* unicast */ 22702ac6454SAndrew Thompson } else { 22802ac6454SAndrew Thompson iface_index = 0; /* broadcast */ 22902ac6454SAndrew Thompson } 23002ac6454SAndrew Thompson 23102ac6454SAndrew Thompson /* 23202ac6454SAndrew Thompson * We need to protect against other threads doing probe and 23302ac6454SAndrew Thompson * attach: 23402ac6454SAndrew Thompson */ 23502ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 236cb18f7d1SAlfred Perlstein 237cb18f7d1SAlfred Perlstein usbd_enum_lock(udev); 23802ac6454SAndrew Thompson 23902ac6454SAndrew Thompson error = ENXIO; 24002ac6454SAndrew Thompson 24102ac6454SAndrew Thompson tr_repeat: 242a593f6b8SAndrew Thompson iface = usbd_get_iface(udev, iface_index); 24302ac6454SAndrew Thompson if ((iface == NULL) || 24402ac6454SAndrew Thompson (iface->idesc == NULL)) { 24502ac6454SAndrew Thompson /* end of interfaces non-existing interface */ 24602ac6454SAndrew Thompson goto tr_stalled; 24702ac6454SAndrew Thompson } 24829bd7d7eSAndrew Thompson /* set initial state */ 24929bd7d7eSAndrew Thompson 25029bd7d7eSAndrew Thompson temp_state = state; 25129bd7d7eSAndrew Thompson 25202ac6454SAndrew Thompson /* forward request to interface, if any */ 25302ac6454SAndrew Thompson 25402ac6454SAndrew Thompson if ((error != 0) && 25502ac6454SAndrew Thompson (error != ENOTTY) && 25602ac6454SAndrew Thompson (iface->subdev != NULL) && 25702ac6454SAndrew Thompson device_is_attached(iface->subdev)) { 25802ac6454SAndrew Thompson #if 0 259090f65a6SAndrew Thompson DEVMETHOD(usb_handle_request, NULL); /* dummy */ 26002ac6454SAndrew Thompson #endif 26102ac6454SAndrew Thompson error = USB_HANDLE_REQUEST(iface->subdev, 26202ac6454SAndrew Thompson &req, ppdata, plen, 26329bd7d7eSAndrew Thompson off, &temp_state); 26402ac6454SAndrew Thompson } 265a593f6b8SAndrew Thompson iface_parent = usbd_get_iface(udev, iface->parent_iface_index); 26602ac6454SAndrew Thompson 26702ac6454SAndrew Thompson if ((iface_parent == NULL) || 26802ac6454SAndrew Thompson (iface_parent->idesc == NULL)) { 26902ac6454SAndrew Thompson /* non-existing interface */ 27002ac6454SAndrew Thompson iface_parent = NULL; 27102ac6454SAndrew Thompson } 27202ac6454SAndrew Thompson /* forward request to parent interface, if any */ 27302ac6454SAndrew Thompson 27402ac6454SAndrew Thompson if ((error != 0) && 27502ac6454SAndrew Thompson (error != ENOTTY) && 27602ac6454SAndrew Thompson (iface_parent != NULL) && 27702ac6454SAndrew Thompson (iface_parent->subdev != NULL) && 27802ac6454SAndrew Thompson ((req.bmRequestType & 0x1F) == UT_INTERFACE) && 27902ac6454SAndrew Thompson (iface_parent->subdev != iface->subdev) && 28002ac6454SAndrew Thompson device_is_attached(iface_parent->subdev)) { 28102ac6454SAndrew Thompson error = USB_HANDLE_REQUEST(iface_parent->subdev, 28229bd7d7eSAndrew Thompson &req, ppdata, plen, off, &temp_state); 28302ac6454SAndrew Thompson } 28402ac6454SAndrew Thompson if (error == 0) { 28502ac6454SAndrew Thompson /* negativly adjust pointer and length */ 28602ac6454SAndrew Thompson *ppdata = ((uint8_t *)(*ppdata)) - off; 28702ac6454SAndrew Thompson *plen += off; 28829bd7d7eSAndrew Thompson 28929bd7d7eSAndrew Thompson if ((state == USB_HR_NOT_COMPLETE) && 29029bd7d7eSAndrew Thompson (temp_state == USB_HR_COMPLETE_OK)) 29129bd7d7eSAndrew Thompson goto tr_short; 29229bd7d7eSAndrew Thompson else 29302ac6454SAndrew Thompson goto tr_valid; 29402ac6454SAndrew Thompson } else if (error == ENOTTY) { 29502ac6454SAndrew Thompson goto tr_stalled; 29602ac6454SAndrew Thompson } 29702ac6454SAndrew Thompson if ((req.bmRequestType & 0x1F) != UT_INTERFACE) { 29802ac6454SAndrew Thompson iface_index++; /* iterate */ 29902ac6454SAndrew Thompson goto tr_repeat; 30002ac6454SAndrew Thompson } 301a755d548SAndrew Thompson if (state != USB_HR_NOT_COMPLETE) { 30202ac6454SAndrew Thompson /* we are complete */ 30302ac6454SAndrew Thompson goto tr_valid; 30402ac6454SAndrew Thompson } 30502ac6454SAndrew Thompson switch (req.bmRequestType) { 30602ac6454SAndrew Thompson case UT_WRITE_INTERFACE: 30702ac6454SAndrew Thompson switch (req.bRequest) { 30802ac6454SAndrew Thompson case UR_SET_INTERFACE: 30902ac6454SAndrew Thompson /* 310bd73b187SAlfred Perlstein * We assume that the endpoints are the same 311bd73b187SAlfred Perlstein * accross the alternate settings. 312bd73b187SAlfred Perlstein * 313bd73b187SAlfred Perlstein * Reset the endpoints, because re-attaching 314bd73b187SAlfred Perlstein * only a part of the device is not possible. 31502ac6454SAndrew Thompson */ 316bd73b187SAlfred Perlstein error = usb_check_alt_setting(udev, 317bd73b187SAlfred Perlstein iface, req.wValue[0]); 318bd73b187SAlfred Perlstein if (error) { 319bd73b187SAlfred Perlstein DPRINTF("alt setting does not exist %s\n", 320bd73b187SAlfred Perlstein usbd_errstr(error)); 321bd73b187SAlfred Perlstein goto tr_stalled; 322bd73b187SAlfred Perlstein } 323bd73b187SAlfred Perlstein error = usb_reset_iface_endpoints(udev, iface_index); 32402ac6454SAndrew Thompson if (error) { 32502ac6454SAndrew Thompson DPRINTF("alt setting failed %s\n", 326a593f6b8SAndrew Thompson usbd_errstr(error)); 32702ac6454SAndrew Thompson goto tr_stalled; 32802ac6454SAndrew Thompson } 329bd73b187SAlfred Perlstein /* update the current alternate setting */ 330bd73b187SAlfred Perlstein iface->alt_index = req.wValue[0]; 33102ac6454SAndrew Thompson break; 332bd73b187SAlfred Perlstein 33302ac6454SAndrew Thompson default: 33402ac6454SAndrew Thompson goto tr_stalled; 33502ac6454SAndrew Thompson } 33602ac6454SAndrew Thompson break; 33702ac6454SAndrew Thompson 33802ac6454SAndrew Thompson case UT_READ_INTERFACE: 33902ac6454SAndrew Thompson switch (req.bRequest) { 34002ac6454SAndrew Thompson case UR_GET_INTERFACE: 34102ac6454SAndrew Thompson *ppdata = &iface->alt_index; 34202ac6454SAndrew Thompson *plen = 1; 34302ac6454SAndrew Thompson break; 34402ac6454SAndrew Thompson 34502ac6454SAndrew Thompson default: 34602ac6454SAndrew Thompson goto tr_stalled; 34702ac6454SAndrew Thompson } 34802ac6454SAndrew Thompson break; 34902ac6454SAndrew Thompson default: 35002ac6454SAndrew Thompson goto tr_stalled; 35102ac6454SAndrew Thompson } 35202ac6454SAndrew Thompson tr_valid: 353cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 35402ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 35502ac6454SAndrew Thompson return (0); 35602ac6454SAndrew Thompson 35729bd7d7eSAndrew Thompson tr_short: 358cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 35929bd7d7eSAndrew Thompson USB_XFER_LOCK(xfer); 36029bd7d7eSAndrew Thompson return (USB_ERR_SHORT_XFER); 36129bd7d7eSAndrew Thompson 36202ac6454SAndrew Thompson tr_stalled: 363cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 36402ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 36502ac6454SAndrew Thompson return (USB_ERR_STALLED); 36602ac6454SAndrew Thompson } 36702ac6454SAndrew Thompson 36802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 369a593f6b8SAndrew Thompson * usb_handle_stall 37002ac6454SAndrew Thompson * 37102ac6454SAndrew Thompson * Returns: 37202ac6454SAndrew Thompson * 0: Success 37302ac6454SAndrew Thompson * Else: Failure 37402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 375e0a69b51SAndrew Thompson static usb_error_t 376a593f6b8SAndrew Thompson usb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall) 37702ac6454SAndrew Thompson { 378760bc48eSAndrew Thompson struct usb_device *udev = xfer->xroot->udev; 379e0a69b51SAndrew Thompson usb_error_t err; 38002ac6454SAndrew Thompson 38102ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 382a593f6b8SAndrew Thompson err = usbd_set_endpoint_stall(udev, 383a593f6b8SAndrew Thompson usbd_get_ep_by_addr(udev, ep), do_stall); 38402ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 38502ac6454SAndrew Thompson return (err); 38602ac6454SAndrew Thompson } 38702ac6454SAndrew Thompson 38802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 389a593f6b8SAndrew Thompson * usb_handle_get_stall 39002ac6454SAndrew Thompson * 39102ac6454SAndrew Thompson * Returns: 39202ac6454SAndrew Thompson * 0: Success 39302ac6454SAndrew Thompson * Else: Failure 39402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 39502ac6454SAndrew Thompson static uint8_t 396a593f6b8SAndrew Thompson usb_handle_get_stall(struct usb_device *udev, uint8_t ea_val) 39702ac6454SAndrew Thompson { 398ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 39902ac6454SAndrew Thompson uint8_t halted; 40002ac6454SAndrew Thompson 401a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(udev, ea_val); 402ae60fdfbSAndrew Thompson if (ep == NULL) { 40302ac6454SAndrew Thompson /* nothing to do */ 40402ac6454SAndrew Thompson return (0); 40502ac6454SAndrew Thompson } 40602ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 407ae60fdfbSAndrew Thompson halted = ep->is_stalled; 40802ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 40902ac6454SAndrew Thompson 41002ac6454SAndrew Thompson return (halted); 41102ac6454SAndrew Thompson } 41202ac6454SAndrew Thompson 41302ac6454SAndrew Thompson /*------------------------------------------------------------------------* 414a593f6b8SAndrew Thompson * usb_handle_remote_wakeup 41502ac6454SAndrew Thompson * 41602ac6454SAndrew Thompson * Returns: 41702ac6454SAndrew Thompson * 0: Success 41802ac6454SAndrew Thompson * Else: Failure 41902ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 420e0a69b51SAndrew Thompson static usb_error_t 421a593f6b8SAndrew Thompson usb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on) 42202ac6454SAndrew Thompson { 423760bc48eSAndrew Thompson struct usb_device *udev; 424760bc48eSAndrew Thompson struct usb_bus *bus; 42502ac6454SAndrew Thompson 42602ac6454SAndrew Thompson udev = xfer->xroot->udev; 42702ac6454SAndrew Thompson bus = udev->bus; 42802ac6454SAndrew Thompson 42902ac6454SAndrew Thompson USB_BUS_LOCK(bus); 43002ac6454SAndrew Thompson 43102ac6454SAndrew Thompson if (is_on) { 43202ac6454SAndrew Thompson udev->flags.remote_wakeup = 1; 43302ac6454SAndrew Thompson } else { 43402ac6454SAndrew Thompson udev->flags.remote_wakeup = 0; 43502ac6454SAndrew Thompson } 43602ac6454SAndrew Thompson 43702ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 43802ac6454SAndrew Thompson 4392ecb4e91SHans Petter Selasky #if USB_HAVE_POWERD 44002ac6454SAndrew Thompson /* In case we are out of sync, update the power state. */ 441a593f6b8SAndrew Thompson usb_bus_power_update(udev->bus); 4422ecb4e91SHans Petter Selasky #endif 44302ac6454SAndrew Thompson return (0); /* success */ 44402ac6454SAndrew Thompson } 44502ac6454SAndrew Thompson 44602ac6454SAndrew Thompson /*------------------------------------------------------------------------* 447a593f6b8SAndrew Thompson * usb_handle_request 44802ac6454SAndrew Thompson * 44902ac6454SAndrew Thompson * Internal state sequence: 45002ac6454SAndrew Thompson * 451a755d548SAndrew Thompson * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR 45202ac6454SAndrew Thompson * 45302ac6454SAndrew Thompson * Returns: 45402ac6454SAndrew Thompson * 0: Ready to start hardware 45502ac6454SAndrew Thompson * Else: Stall current transfer, if any 45602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 457e0a69b51SAndrew Thompson static usb_error_t 458a593f6b8SAndrew Thompson usb_handle_request(struct usb_xfer *xfer) 45902ac6454SAndrew Thompson { 460760bc48eSAndrew Thompson struct usb_device_request req; 461760bc48eSAndrew Thompson struct usb_device *udev; 46202ac6454SAndrew Thompson const void *src_zcopy; /* zero-copy source pointer */ 46302ac6454SAndrew Thompson const void *src_mcopy; /* non zero-copy source pointer */ 46402ac6454SAndrew Thompson uint16_t off; /* data offset */ 46502ac6454SAndrew Thompson uint16_t rem; /* data remainder */ 46602ac6454SAndrew Thompson uint16_t max_len; /* max fragment length */ 46702ac6454SAndrew Thompson uint16_t wValue; 46802ac6454SAndrew Thompson uint8_t state; 46929bd7d7eSAndrew Thompson uint8_t is_complete = 1; 470e0a69b51SAndrew Thompson usb_error_t err; 47102ac6454SAndrew Thompson union { 47202ac6454SAndrew Thompson uWord wStatus; 47302ac6454SAndrew Thompson uint8_t buf[2]; 47402ac6454SAndrew Thompson } temp; 47502ac6454SAndrew Thompson 47602ac6454SAndrew Thompson /* 47702ac6454SAndrew Thompson * Filter the USB transfer state into 47802ac6454SAndrew Thompson * something which we understand: 47902ac6454SAndrew Thompson */ 48002ac6454SAndrew Thompson 48102ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 48202ac6454SAndrew Thompson case USB_ST_SETUP: 483a755d548SAndrew Thompson state = USB_HR_NOT_COMPLETE; 48402ac6454SAndrew Thompson 48502ac6454SAndrew Thompson if (!xfer->flags_int.control_act) { 48602ac6454SAndrew Thompson /* nothing to do */ 48702ac6454SAndrew Thompson goto tr_stalled; 48802ac6454SAndrew Thompson } 48902ac6454SAndrew Thompson break; 490a755d548SAndrew Thompson case USB_ST_TRANSFERRED: 49102ac6454SAndrew Thompson if (!xfer->flags_int.control_act) { 492a755d548SAndrew Thompson state = USB_HR_COMPLETE_OK; 49302ac6454SAndrew Thompson } else { 494a755d548SAndrew Thompson state = USB_HR_NOT_COMPLETE; 49502ac6454SAndrew Thompson } 49602ac6454SAndrew Thompson break; 497a755d548SAndrew Thompson default: 498a755d548SAndrew Thompson state = USB_HR_COMPLETE_ERR; 499a755d548SAndrew Thompson break; 50002ac6454SAndrew Thompson } 50102ac6454SAndrew Thompson 50202ac6454SAndrew Thompson /* reset frame stuff */ 50302ac6454SAndrew Thompson 504ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, 0); 50502ac6454SAndrew Thompson 506ed6d949aSAndrew Thompson usbd_xfer_set_frame_offset(xfer, 0, 0); 507ed6d949aSAndrew Thompson usbd_xfer_set_frame_offset(xfer, sizeof(req), 1); 50802ac6454SAndrew Thompson 50902ac6454SAndrew Thompson /* get the current request, if any */ 51002ac6454SAndrew Thompson 511a593f6b8SAndrew Thompson usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); 51202ac6454SAndrew Thompson 51302ac6454SAndrew Thompson if (xfer->flags_int.control_rem == 0xFFFF) { 51402ac6454SAndrew Thompson /* first time - not initialised */ 51502ac6454SAndrew Thompson rem = UGETW(req.wLength); 51602ac6454SAndrew Thompson off = 0; 51702ac6454SAndrew Thompson } else { 51802ac6454SAndrew Thompson /* not first time - initialised */ 51902ac6454SAndrew Thompson rem = xfer->flags_int.control_rem; 52002ac6454SAndrew Thompson off = UGETW(req.wLength) - rem; 52102ac6454SAndrew Thompson } 52202ac6454SAndrew Thompson 52302ac6454SAndrew Thompson /* set some defaults */ 52402ac6454SAndrew Thompson 52502ac6454SAndrew Thompson max_len = 0; 52602ac6454SAndrew Thompson src_zcopy = NULL; 52702ac6454SAndrew Thompson src_mcopy = NULL; 52802ac6454SAndrew Thompson udev = xfer->xroot->udev; 52902ac6454SAndrew Thompson 53002ac6454SAndrew Thompson /* get some request fields decoded */ 53102ac6454SAndrew Thompson 53202ac6454SAndrew Thompson wValue = UGETW(req.wValue); 53302ac6454SAndrew Thompson 53402ac6454SAndrew Thompson DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " 53502ac6454SAndrew Thompson "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, 5366d917491SHans Petter Selasky req.bRequest, wValue, UGETW(req.wIndex), off, rem, state); 53702ac6454SAndrew Thompson 53802ac6454SAndrew Thompson /* demultiplex the control request */ 53902ac6454SAndrew Thompson 54002ac6454SAndrew Thompson switch (req.bmRequestType) { 54102ac6454SAndrew Thompson case UT_READ_DEVICE: 542a755d548SAndrew Thompson if (state != USB_HR_NOT_COMPLETE) { 54302ac6454SAndrew Thompson break; 54402ac6454SAndrew Thompson } 54502ac6454SAndrew Thompson switch (req.bRequest) { 54602ac6454SAndrew Thompson case UR_GET_DESCRIPTOR: 54702ac6454SAndrew Thompson goto tr_handle_get_descriptor; 54802ac6454SAndrew Thompson case UR_GET_CONFIG: 54902ac6454SAndrew Thompson goto tr_handle_get_config; 55002ac6454SAndrew Thompson case UR_GET_STATUS: 55102ac6454SAndrew Thompson goto tr_handle_get_status; 55202ac6454SAndrew Thompson default: 55302ac6454SAndrew Thompson goto tr_stalled; 55402ac6454SAndrew Thompson } 55502ac6454SAndrew Thompson break; 55602ac6454SAndrew Thompson 55702ac6454SAndrew Thompson case UT_WRITE_DEVICE: 55802ac6454SAndrew Thompson switch (req.bRequest) { 55902ac6454SAndrew Thompson case UR_SET_ADDRESS: 56002ac6454SAndrew Thompson goto tr_handle_set_address; 56102ac6454SAndrew Thompson case UR_SET_CONFIG: 56202ac6454SAndrew Thompson goto tr_handle_set_config; 56302ac6454SAndrew Thompson case UR_CLEAR_FEATURE: 56402ac6454SAndrew Thompson switch (wValue) { 56502ac6454SAndrew Thompson case UF_DEVICE_REMOTE_WAKEUP: 56602ac6454SAndrew Thompson goto tr_handle_clear_wakeup; 56702ac6454SAndrew Thompson default: 56802ac6454SAndrew Thompson goto tr_stalled; 56902ac6454SAndrew Thompson } 57002ac6454SAndrew Thompson break; 57102ac6454SAndrew Thompson case UR_SET_FEATURE: 57202ac6454SAndrew Thompson switch (wValue) { 57302ac6454SAndrew Thompson case UF_DEVICE_REMOTE_WAKEUP: 57402ac6454SAndrew Thompson goto tr_handle_set_wakeup; 57502ac6454SAndrew Thompson default: 57602ac6454SAndrew Thompson goto tr_stalled; 57702ac6454SAndrew Thompson } 57802ac6454SAndrew Thompson break; 57902ac6454SAndrew Thompson default: 58002ac6454SAndrew Thompson goto tr_stalled; 58102ac6454SAndrew Thompson } 58202ac6454SAndrew Thompson break; 58302ac6454SAndrew Thompson 58402ac6454SAndrew Thompson case UT_WRITE_ENDPOINT: 58502ac6454SAndrew Thompson switch (req.bRequest) { 58602ac6454SAndrew Thompson case UR_CLEAR_FEATURE: 58702ac6454SAndrew Thompson switch (wValue) { 58802ac6454SAndrew Thompson case UF_ENDPOINT_HALT: 58902ac6454SAndrew Thompson goto tr_handle_clear_halt; 59002ac6454SAndrew Thompson default: 59102ac6454SAndrew Thompson goto tr_stalled; 59202ac6454SAndrew Thompson } 59302ac6454SAndrew Thompson break; 59402ac6454SAndrew Thompson case UR_SET_FEATURE: 59502ac6454SAndrew Thompson switch (wValue) { 59602ac6454SAndrew Thompson case UF_ENDPOINT_HALT: 59702ac6454SAndrew Thompson goto tr_handle_set_halt; 59802ac6454SAndrew Thompson default: 59902ac6454SAndrew Thompson goto tr_stalled; 60002ac6454SAndrew Thompson } 60102ac6454SAndrew Thompson break; 60202ac6454SAndrew Thompson default: 60302ac6454SAndrew Thompson goto tr_stalled; 60402ac6454SAndrew Thompson } 60502ac6454SAndrew Thompson break; 60602ac6454SAndrew Thompson 60702ac6454SAndrew Thompson case UT_READ_ENDPOINT: 60802ac6454SAndrew Thompson switch (req.bRequest) { 60902ac6454SAndrew Thompson case UR_GET_STATUS: 61002ac6454SAndrew Thompson goto tr_handle_get_ep_status; 61102ac6454SAndrew Thompson default: 61202ac6454SAndrew Thompson goto tr_stalled; 61302ac6454SAndrew Thompson } 61402ac6454SAndrew Thompson break; 61502ac6454SAndrew Thompson default: 61602ac6454SAndrew Thompson /* we use "USB_ADD_BYTES" to de-const the src_zcopy */ 617a593f6b8SAndrew Thompson err = usb_handle_iface_request(xfer, 61802ac6454SAndrew Thompson USB_ADD_BYTES(&src_zcopy, 0), 61902ac6454SAndrew Thompson &max_len, req, off, state); 62002ac6454SAndrew Thompson if (err == 0) { 62129bd7d7eSAndrew Thompson is_complete = 0; 62229bd7d7eSAndrew Thompson goto tr_valid; 62329bd7d7eSAndrew Thompson } else if (err == USB_ERR_SHORT_XFER) { 62402ac6454SAndrew Thompson goto tr_valid; 62502ac6454SAndrew Thompson } 62602ac6454SAndrew Thompson /* 62702ac6454SAndrew Thompson * Reset zero-copy pointer and max length 62802ac6454SAndrew Thompson * variable in case they were unintentionally 62902ac6454SAndrew Thompson * set: 63002ac6454SAndrew Thompson */ 63102ac6454SAndrew Thompson src_zcopy = NULL; 63202ac6454SAndrew Thompson max_len = 0; 63302ac6454SAndrew Thompson 63402ac6454SAndrew Thompson /* 63502ac6454SAndrew Thompson * Check if we have a vendor specific 63602ac6454SAndrew Thompson * descriptor: 63702ac6454SAndrew Thompson */ 63802ac6454SAndrew Thompson goto tr_handle_get_descriptor; 63902ac6454SAndrew Thompson } 64002ac6454SAndrew Thompson goto tr_valid; 64102ac6454SAndrew Thompson 64202ac6454SAndrew Thompson tr_handle_get_descriptor: 643a593f6b8SAndrew Thompson err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len); 644459d369eSAndrew Thompson if (err) 64502ac6454SAndrew Thompson goto tr_stalled; 646459d369eSAndrew Thompson if (src_zcopy == NULL) 647459d369eSAndrew Thompson goto tr_stalled; 64802ac6454SAndrew Thompson goto tr_valid; 64902ac6454SAndrew Thompson 65002ac6454SAndrew Thompson tr_handle_get_config: 65102ac6454SAndrew Thompson temp.buf[0] = udev->curr_config_no; 65202ac6454SAndrew Thompson src_mcopy = temp.buf; 65302ac6454SAndrew Thompson max_len = 1; 65402ac6454SAndrew Thompson goto tr_valid; 65502ac6454SAndrew Thompson 65602ac6454SAndrew Thompson tr_handle_get_status: 65702ac6454SAndrew Thompson 65802ac6454SAndrew Thompson wValue = 0; 65902ac6454SAndrew Thompson 66002ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 66102ac6454SAndrew Thompson if (udev->flags.remote_wakeup) { 66202ac6454SAndrew Thompson wValue |= UDS_REMOTE_WAKEUP; 66302ac6454SAndrew Thompson } 66402ac6454SAndrew Thompson if (udev->flags.self_powered) { 66502ac6454SAndrew Thompson wValue |= UDS_SELF_POWERED; 66602ac6454SAndrew Thompson } 66702ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 66802ac6454SAndrew Thompson 66902ac6454SAndrew Thompson USETW(temp.wStatus, wValue); 67002ac6454SAndrew Thompson src_mcopy = temp.wStatus; 67102ac6454SAndrew Thompson max_len = sizeof(temp.wStatus); 67202ac6454SAndrew Thompson goto tr_valid; 67302ac6454SAndrew Thompson 67402ac6454SAndrew Thompson tr_handle_set_address: 675a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 67602ac6454SAndrew Thompson if (wValue >= 0x80) { 67702ac6454SAndrew Thompson /* invalid value */ 67802ac6454SAndrew Thompson goto tr_stalled; 67902ac6454SAndrew Thompson } else if (udev->curr_config_no != 0) { 68002ac6454SAndrew Thompson /* we are configured ! */ 68102ac6454SAndrew Thompson goto tr_stalled; 68202ac6454SAndrew Thompson } 683a755d548SAndrew Thompson } else if (state != USB_HR_NOT_COMPLETE) { 68402ac6454SAndrew Thompson udev->address = (wValue & 0x7F); 68502ac6454SAndrew Thompson goto tr_bad_context; 68602ac6454SAndrew Thompson } 68702ac6454SAndrew Thompson goto tr_valid; 68802ac6454SAndrew Thompson 68902ac6454SAndrew Thompson tr_handle_set_config: 690a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 691a593f6b8SAndrew Thompson if (usb_handle_set_config(xfer, req.wValue[0])) { 69202ac6454SAndrew Thompson goto tr_stalled; 69302ac6454SAndrew Thompson } 69402ac6454SAndrew Thompson } 69502ac6454SAndrew Thompson goto tr_valid; 69602ac6454SAndrew Thompson 69702ac6454SAndrew Thompson tr_handle_clear_halt: 698a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 699a593f6b8SAndrew Thompson if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) { 70002ac6454SAndrew Thompson goto tr_stalled; 70102ac6454SAndrew Thompson } 70202ac6454SAndrew Thompson } 70302ac6454SAndrew Thompson goto tr_valid; 70402ac6454SAndrew Thompson 70502ac6454SAndrew Thompson tr_handle_clear_wakeup: 706a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 707a593f6b8SAndrew Thompson if (usb_handle_remote_wakeup(xfer, 0)) { 70802ac6454SAndrew Thompson goto tr_stalled; 70902ac6454SAndrew Thompson } 71002ac6454SAndrew Thompson } 71102ac6454SAndrew Thompson goto tr_valid; 71202ac6454SAndrew Thompson 71302ac6454SAndrew Thompson tr_handle_set_halt: 714a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 715a593f6b8SAndrew Thompson if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) { 71602ac6454SAndrew Thompson goto tr_stalled; 71702ac6454SAndrew Thompson } 71802ac6454SAndrew Thompson } 71902ac6454SAndrew Thompson goto tr_valid; 72002ac6454SAndrew Thompson 72102ac6454SAndrew Thompson tr_handle_set_wakeup: 722a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 723a593f6b8SAndrew Thompson if (usb_handle_remote_wakeup(xfer, 1)) { 72402ac6454SAndrew Thompson goto tr_stalled; 72502ac6454SAndrew Thompson } 72602ac6454SAndrew Thompson } 72702ac6454SAndrew Thompson goto tr_valid; 72802ac6454SAndrew Thompson 72902ac6454SAndrew Thompson tr_handle_get_ep_status: 730a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 73102ac6454SAndrew Thompson temp.wStatus[0] = 732a593f6b8SAndrew Thompson usb_handle_get_stall(udev, req.wIndex[0]); 73302ac6454SAndrew Thompson temp.wStatus[1] = 0; 73402ac6454SAndrew Thompson src_mcopy = temp.wStatus; 73502ac6454SAndrew Thompson max_len = sizeof(temp.wStatus); 73602ac6454SAndrew Thompson } 73702ac6454SAndrew Thompson goto tr_valid; 73802ac6454SAndrew Thompson 73902ac6454SAndrew Thompson tr_valid: 740a755d548SAndrew Thompson if (state != USB_HR_NOT_COMPLETE) { 74102ac6454SAndrew Thompson goto tr_stalled; 74202ac6454SAndrew Thompson } 74302ac6454SAndrew Thompson /* subtract offset from length */ 74402ac6454SAndrew Thompson 74502ac6454SAndrew Thompson max_len -= off; 74602ac6454SAndrew Thompson 74702ac6454SAndrew Thompson /* Compute the real maximum data length */ 74802ac6454SAndrew Thompson 74902ac6454SAndrew Thompson if (max_len > xfer->max_data_length) { 750ed6d949aSAndrew Thompson max_len = usbd_xfer_max_len(xfer); 75102ac6454SAndrew Thompson } 75202ac6454SAndrew Thompson if (max_len > rem) { 75302ac6454SAndrew Thompson max_len = rem; 75402ac6454SAndrew Thompson } 75502ac6454SAndrew Thompson /* 75602ac6454SAndrew Thompson * If the remainder is greater than the maximum data length, 75702ac6454SAndrew Thompson * we need to truncate the value for the sake of the 75802ac6454SAndrew Thompson * comparison below: 75902ac6454SAndrew Thompson */ 76002ac6454SAndrew Thompson if (rem > xfer->max_data_length) { 761ed6d949aSAndrew Thompson rem = usbd_xfer_max_len(xfer); 76202ac6454SAndrew Thompson } 76329bd7d7eSAndrew Thompson if ((rem != max_len) && (is_complete != 0)) { 76402ac6454SAndrew Thompson /* 76502ac6454SAndrew Thompson * If we don't transfer the data we can transfer, then 76602ac6454SAndrew Thompson * the transfer is short ! 76702ac6454SAndrew Thompson */ 76802ac6454SAndrew Thompson xfer->flags.force_short_xfer = 1; 76902ac6454SAndrew Thompson xfer->nframes = 2; 77002ac6454SAndrew Thompson } else { 77102ac6454SAndrew Thompson /* 77202ac6454SAndrew Thompson * Default case 77302ac6454SAndrew Thompson */ 77402ac6454SAndrew Thompson xfer->flags.force_short_xfer = 0; 77502ac6454SAndrew Thompson xfer->nframes = max_len ? 2 : 1; 77602ac6454SAndrew Thompson } 77702ac6454SAndrew Thompson if (max_len > 0) { 77802ac6454SAndrew Thompson if (src_mcopy) { 77902ac6454SAndrew Thompson src_mcopy = USB_ADD_BYTES(src_mcopy, off); 780a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers + 1, 0, 78102ac6454SAndrew Thompson src_mcopy, max_len); 782ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, max_len); 78302ac6454SAndrew Thompson } else { 784ed6d949aSAndrew Thompson usbd_xfer_set_frame_data(xfer, 1, 785ed6d949aSAndrew Thompson USB_ADD_BYTES(src_zcopy, off), max_len); 78602ac6454SAndrew Thompson } 78702ac6454SAndrew Thompson } else { 78802ac6454SAndrew Thompson /* the end is reached, send status */ 78902ac6454SAndrew Thompson xfer->flags.manual_status = 0; 790ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, 0); 79102ac6454SAndrew Thompson } 79202ac6454SAndrew Thompson DPRINTF("success\n"); 79302ac6454SAndrew Thompson return (0); /* success */ 79402ac6454SAndrew Thompson 79502ac6454SAndrew Thompson tr_stalled: 796a755d548SAndrew Thompson DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ? 79702ac6454SAndrew Thompson "complete" : "stalled"); 79802ac6454SAndrew Thompson return (USB_ERR_STALLED); 79902ac6454SAndrew Thompson 80002ac6454SAndrew Thompson tr_bad_context: 80102ac6454SAndrew Thompson DPRINTF("bad context\n"); 80202ac6454SAndrew Thompson return (USB_ERR_BAD_CONTEXT); 80302ac6454SAndrew Thompson } 804