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 27ed6d949aSAndrew Thompson #include <sys/stdint.h> 28ed6d949aSAndrew Thompson #include <sys/stddef.h> 29ed6d949aSAndrew Thompson #include <sys/param.h> 30ed6d949aSAndrew Thompson #include <sys/queue.h> 31ed6d949aSAndrew Thompson #include <sys/types.h> 32ed6d949aSAndrew Thompson #include <sys/systm.h> 33ed6d949aSAndrew Thompson #include <sys/kernel.h> 34ed6d949aSAndrew Thompson #include <sys/bus.h> 35ed6d949aSAndrew Thompson #include <sys/linker_set.h> 36ed6d949aSAndrew Thompson #include <sys/module.h> 37ed6d949aSAndrew Thompson #include <sys/lock.h> 38ed6d949aSAndrew Thompson #include <sys/mutex.h> 39ed6d949aSAndrew Thompson #include <sys/condvar.h> 40ed6d949aSAndrew Thompson #include <sys/sysctl.h> 41ed6d949aSAndrew Thompson #include <sys/sx.h> 42ed6d949aSAndrew Thompson #include <sys/unistd.h> 43ed6d949aSAndrew Thompson #include <sys/callout.h> 44ed6d949aSAndrew Thompson #include <sys/malloc.h> 45ed6d949aSAndrew Thompson #include <sys/priv.h> 46ed6d949aSAndrew Thompson 4702ac6454SAndrew Thompson #include <dev/usb/usb.h> 48ed6d949aSAndrew Thompson #include <dev/usb/usbdi.h> 49bd73b187SAlfred Perlstein #include <dev/usb/usbdi_util.h> 50ed6d949aSAndrew Thompson #include "usb_if.h" 5102ac6454SAndrew Thompson 52a593f6b8SAndrew Thompson #define USB_DEBUG_VAR usb_debug 5302ac6454SAndrew Thompson 5402ac6454SAndrew Thompson #include <dev/usb/usb_core.h> 5502ac6454SAndrew Thompson #include <dev/usb/usb_process.h> 5602ac6454SAndrew Thompson #include <dev/usb/usb_busdma.h> 5702ac6454SAndrew Thompson #include <dev/usb/usb_transfer.h> 5802ac6454SAndrew Thompson #include <dev/usb/usb_device.h> 5902ac6454SAndrew Thompson #include <dev/usb/usb_debug.h> 6002ac6454SAndrew Thompson #include <dev/usb/usb_dynamic.h> 6102ac6454SAndrew Thompson #include <dev/usb/usb_hub.h> 6202ac6454SAndrew Thompson 6302ac6454SAndrew Thompson #include <dev/usb/usb_controller.h> 6402ac6454SAndrew Thompson #include <dev/usb/usb_bus.h> 6502ac6454SAndrew Thompson 6602ac6454SAndrew Thompson /* function prototypes */ 6702ac6454SAndrew Thompson 68a593f6b8SAndrew Thompson static uint8_t usb_handle_get_stall(struct usb_device *, uint8_t); 69a593f6b8SAndrew Thompson static usb_error_t usb_handle_remote_wakeup(struct usb_xfer *, uint8_t); 70a593f6b8SAndrew Thompson static usb_error_t usb_handle_request(struct usb_xfer *); 71a593f6b8SAndrew Thompson static usb_error_t usb_handle_set_config(struct usb_xfer *, uint8_t); 72a593f6b8SAndrew Thompson static usb_error_t usb_handle_set_stall(struct usb_xfer *, uint8_t, 7302ac6454SAndrew Thompson uint8_t); 74a593f6b8SAndrew Thompson static usb_error_t usb_handle_iface_request(struct usb_xfer *, void **, 75760bc48eSAndrew Thompson uint16_t *, struct usb_device_request, uint16_t, 7602ac6454SAndrew Thompson uint8_t); 7702ac6454SAndrew Thompson 7802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 79a593f6b8SAndrew Thompson * usb_handle_request_callback 8002ac6454SAndrew Thompson * 8102ac6454SAndrew Thompson * This function is the USB callback for generic USB Device control 8202ac6454SAndrew Thompson * transfers. 8302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 8402ac6454SAndrew Thompson void 85ed6d949aSAndrew Thompson usb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error) 8602ac6454SAndrew Thompson { 87e0a69b51SAndrew Thompson usb_error_t err; 8802ac6454SAndrew Thompson 8902ac6454SAndrew Thompson /* check the current transfer state */ 9002ac6454SAndrew Thompson 9102ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 9202ac6454SAndrew Thompson case USB_ST_SETUP: 9302ac6454SAndrew Thompson case USB_ST_TRANSFERRED: 9402ac6454SAndrew Thompson 9502ac6454SAndrew Thompson /* handle the request */ 96a593f6b8SAndrew Thompson err = usb_handle_request(xfer); 9702ac6454SAndrew Thompson 9802ac6454SAndrew Thompson if (err) { 9902ac6454SAndrew Thompson 10002ac6454SAndrew Thompson if (err == USB_ERR_BAD_CONTEXT) { 10102ac6454SAndrew Thompson /* we need to re-setup the control transfer */ 102a593f6b8SAndrew Thompson usb_needs_explore(xfer->xroot->bus, 0); 10302ac6454SAndrew Thompson break; 10402ac6454SAndrew Thompson } 10502ac6454SAndrew Thompson goto tr_restart; 10602ac6454SAndrew Thompson } 107a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 10802ac6454SAndrew Thompson break; 10902ac6454SAndrew Thompson 11002ac6454SAndrew Thompson default: 111a755d548SAndrew Thompson /* check if a control transfer is active */ 112a755d548SAndrew Thompson if (xfer->flags_int.control_rem != 0xFFFF) { 113a755d548SAndrew Thompson /* handle the request */ 114a593f6b8SAndrew Thompson err = usb_handle_request(xfer); 115a755d548SAndrew Thompson } 11602ac6454SAndrew Thompson if (xfer->error != USB_ERR_CANCELLED) { 11702ac6454SAndrew Thompson /* should not happen - try stalling */ 11802ac6454SAndrew Thompson goto tr_restart; 11902ac6454SAndrew Thompson } 12002ac6454SAndrew Thompson break; 12102ac6454SAndrew Thompson } 12202ac6454SAndrew Thompson return; 12302ac6454SAndrew Thompson 12402ac6454SAndrew Thompson tr_restart: 125a755d548SAndrew Thompson /* 126a755d548SAndrew Thompson * If a control transfer is active, stall it, and wait for the 127a755d548SAndrew Thompson * next control transfer. 128a755d548SAndrew Thompson */ 129ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request)); 13002ac6454SAndrew Thompson xfer->nframes = 1; 13102ac6454SAndrew Thompson xfer->flags.manual_status = 1; 13202ac6454SAndrew Thompson xfer->flags.force_short_xfer = 0; 133ed6d949aSAndrew Thompson usbd_xfer_set_stall(xfer); /* cancel previous transfer, if any */ 134a593f6b8SAndrew Thompson usbd_transfer_submit(xfer); 13502ac6454SAndrew Thompson } 13602ac6454SAndrew Thompson 13702ac6454SAndrew Thompson /*------------------------------------------------------------------------* 138a593f6b8SAndrew Thompson * usb_handle_set_config 13902ac6454SAndrew Thompson * 14002ac6454SAndrew Thompson * Returns: 14102ac6454SAndrew Thompson * 0: Success 14202ac6454SAndrew Thompson * Else: Failure 14302ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 144e0a69b51SAndrew Thompson static usb_error_t 145a593f6b8SAndrew Thompson usb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) 14602ac6454SAndrew Thompson { 147760bc48eSAndrew Thompson struct usb_device *udev = xfer->xroot->udev; 148e0a69b51SAndrew Thompson usb_error_t err = 0; 14902ac6454SAndrew Thompson 15002ac6454SAndrew Thompson /* 15102ac6454SAndrew Thompson * We need to protect against other threads doing probe and 15202ac6454SAndrew Thompson * attach: 15302ac6454SAndrew Thompson */ 15402ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 155cb18f7d1SAlfred Perlstein 156cb18f7d1SAlfred Perlstein usbd_enum_lock(udev); 15702ac6454SAndrew Thompson 15802ac6454SAndrew Thompson if (conf_no == USB_UNCONFIG_NO) { 15902ac6454SAndrew Thompson conf_no = USB_UNCONFIG_INDEX; 16002ac6454SAndrew Thompson } else { 16102ac6454SAndrew Thompson /* 16202ac6454SAndrew Thompson * The relationship between config number and config index 16302ac6454SAndrew Thompson * is very simple in our case: 16402ac6454SAndrew Thompson */ 16502ac6454SAndrew Thompson conf_no--; 16602ac6454SAndrew Thompson } 16702ac6454SAndrew Thompson 168a593f6b8SAndrew Thompson if (usbd_set_config_index(udev, conf_no)) { 16902ac6454SAndrew Thompson DPRINTF("set config %d failed\n", conf_no); 17002ac6454SAndrew Thompson err = USB_ERR_STALLED; 17102ac6454SAndrew Thompson goto done; 17202ac6454SAndrew Thompson } 173a593f6b8SAndrew Thompson if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { 17402ac6454SAndrew Thompson DPRINTF("probe and attach failed\n"); 17502ac6454SAndrew Thompson err = USB_ERR_STALLED; 17602ac6454SAndrew Thompson goto done; 17702ac6454SAndrew Thompson } 17802ac6454SAndrew Thompson done: 179cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 18002ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 18102ac6454SAndrew Thompson return (err); 18202ac6454SAndrew Thompson } 18302ac6454SAndrew Thompson 184bd73b187SAlfred Perlstein static usb_error_t 185bd73b187SAlfred Perlstein usb_check_alt_setting(struct usb_device *udev, 186bd73b187SAlfred Perlstein struct usb_interface *iface, uint8_t alt_index) 187bd73b187SAlfred Perlstein { 188bd73b187SAlfred Perlstein uint8_t do_unlock; 189bd73b187SAlfred Perlstein usb_error_t err = 0; 190bd73b187SAlfred Perlstein 191bd73b187SAlfred Perlstein /* automatic locking */ 192cb18f7d1SAlfred Perlstein if (usbd_enum_is_locked(udev)) { 193bd73b187SAlfred Perlstein do_unlock = 0; 194bd73b187SAlfred Perlstein } else { 195bd73b187SAlfred Perlstein do_unlock = 1; 196cb18f7d1SAlfred Perlstein usbd_enum_lock(udev); 197bd73b187SAlfred Perlstein } 198bd73b187SAlfred Perlstein 199bd73b187SAlfred Perlstein if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc)) 200bd73b187SAlfred Perlstein err = USB_ERR_INVAL; 201bd73b187SAlfred Perlstein 202cb18f7d1SAlfred Perlstein if (do_unlock) 203cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 204cb18f7d1SAlfred Perlstein 205bd73b187SAlfred Perlstein return (err); 206bd73b187SAlfred Perlstein } 207bd73b187SAlfred Perlstein 20802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 209a593f6b8SAndrew Thompson * usb_handle_iface_request 21002ac6454SAndrew Thompson * 21102ac6454SAndrew Thompson * Returns: 21202ac6454SAndrew Thompson * 0: Success 21302ac6454SAndrew Thompson * Else: Failure 21402ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 215e0a69b51SAndrew Thompson static usb_error_t 216a593f6b8SAndrew Thompson usb_handle_iface_request(struct usb_xfer *xfer, 21702ac6454SAndrew Thompson void **ppdata, uint16_t *plen, 218760bc48eSAndrew Thompson struct usb_device_request req, uint16_t off, uint8_t state) 21902ac6454SAndrew Thompson { 220760bc48eSAndrew Thompson struct usb_interface *iface; 221760bc48eSAndrew Thompson struct usb_interface *iface_parent; /* parent interface */ 222760bc48eSAndrew Thompson struct usb_device *udev = xfer->xroot->udev; 22302ac6454SAndrew Thompson int error; 22402ac6454SAndrew Thompson uint8_t iface_index; 22529bd7d7eSAndrew Thompson uint8_t temp_state; 22602ac6454SAndrew Thompson 22702ac6454SAndrew Thompson if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { 22802ac6454SAndrew Thompson iface_index = req.wIndex[0]; /* unicast */ 22902ac6454SAndrew Thompson } else { 23002ac6454SAndrew Thompson iface_index = 0; /* broadcast */ 23102ac6454SAndrew Thompson } 23202ac6454SAndrew Thompson 23302ac6454SAndrew Thompson /* 23402ac6454SAndrew Thompson * We need to protect against other threads doing probe and 23502ac6454SAndrew Thompson * attach: 23602ac6454SAndrew Thompson */ 23702ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 238cb18f7d1SAlfred Perlstein 239cb18f7d1SAlfred Perlstein usbd_enum_lock(udev); 24002ac6454SAndrew Thompson 24102ac6454SAndrew Thompson error = ENXIO; 24202ac6454SAndrew Thompson 24302ac6454SAndrew Thompson tr_repeat: 244a593f6b8SAndrew Thompson iface = usbd_get_iface(udev, iface_index); 24502ac6454SAndrew Thompson if ((iface == NULL) || 24602ac6454SAndrew Thompson (iface->idesc == NULL)) { 24702ac6454SAndrew Thompson /* end of interfaces non-existing interface */ 24802ac6454SAndrew Thompson goto tr_stalled; 24902ac6454SAndrew Thompson } 25029bd7d7eSAndrew Thompson /* set initial state */ 25129bd7d7eSAndrew Thompson 25229bd7d7eSAndrew Thompson temp_state = state; 25329bd7d7eSAndrew Thompson 25402ac6454SAndrew Thompson /* forward request to interface, if any */ 25502ac6454SAndrew Thompson 25602ac6454SAndrew Thompson if ((error != 0) && 25702ac6454SAndrew Thompson (error != ENOTTY) && 25802ac6454SAndrew Thompson (iface->subdev != NULL) && 25902ac6454SAndrew Thompson device_is_attached(iface->subdev)) { 26002ac6454SAndrew Thompson #if 0 261090f65a6SAndrew Thompson DEVMETHOD(usb_handle_request, NULL); /* dummy */ 26202ac6454SAndrew Thompson #endif 26302ac6454SAndrew Thompson error = USB_HANDLE_REQUEST(iface->subdev, 26402ac6454SAndrew Thompson &req, ppdata, plen, 26529bd7d7eSAndrew Thompson off, &temp_state); 26602ac6454SAndrew Thompson } 267a593f6b8SAndrew Thompson iface_parent = usbd_get_iface(udev, iface->parent_iface_index); 26802ac6454SAndrew Thompson 26902ac6454SAndrew Thompson if ((iface_parent == NULL) || 27002ac6454SAndrew Thompson (iface_parent->idesc == NULL)) { 27102ac6454SAndrew Thompson /* non-existing interface */ 27202ac6454SAndrew Thompson iface_parent = NULL; 27302ac6454SAndrew Thompson } 27402ac6454SAndrew Thompson /* forward request to parent interface, if any */ 27502ac6454SAndrew Thompson 27602ac6454SAndrew Thompson if ((error != 0) && 27702ac6454SAndrew Thompson (error != ENOTTY) && 27802ac6454SAndrew Thompson (iface_parent != NULL) && 27902ac6454SAndrew Thompson (iface_parent->subdev != NULL) && 28002ac6454SAndrew Thompson ((req.bmRequestType & 0x1F) == UT_INTERFACE) && 28102ac6454SAndrew Thompson (iface_parent->subdev != iface->subdev) && 28202ac6454SAndrew Thompson device_is_attached(iface_parent->subdev)) { 28302ac6454SAndrew Thompson error = USB_HANDLE_REQUEST(iface_parent->subdev, 28429bd7d7eSAndrew Thompson &req, ppdata, plen, off, &temp_state); 28502ac6454SAndrew Thompson } 28602ac6454SAndrew Thompson if (error == 0) { 28702ac6454SAndrew Thompson /* negativly adjust pointer and length */ 28802ac6454SAndrew Thompson *ppdata = ((uint8_t *)(*ppdata)) - off; 28902ac6454SAndrew Thompson *plen += off; 29029bd7d7eSAndrew Thompson 29129bd7d7eSAndrew Thompson if ((state == USB_HR_NOT_COMPLETE) && 29229bd7d7eSAndrew Thompson (temp_state == USB_HR_COMPLETE_OK)) 29329bd7d7eSAndrew Thompson goto tr_short; 29429bd7d7eSAndrew Thompson else 29502ac6454SAndrew Thompson goto tr_valid; 29602ac6454SAndrew Thompson } else if (error == ENOTTY) { 29702ac6454SAndrew Thompson goto tr_stalled; 29802ac6454SAndrew Thompson } 29902ac6454SAndrew Thompson if ((req.bmRequestType & 0x1F) != UT_INTERFACE) { 30002ac6454SAndrew Thompson iface_index++; /* iterate */ 30102ac6454SAndrew Thompson goto tr_repeat; 30202ac6454SAndrew Thompson } 303a755d548SAndrew Thompson if (state != USB_HR_NOT_COMPLETE) { 30402ac6454SAndrew Thompson /* we are complete */ 30502ac6454SAndrew Thompson goto tr_valid; 30602ac6454SAndrew Thompson } 30702ac6454SAndrew Thompson switch (req.bmRequestType) { 30802ac6454SAndrew Thompson case UT_WRITE_INTERFACE: 30902ac6454SAndrew Thompson switch (req.bRequest) { 31002ac6454SAndrew Thompson case UR_SET_INTERFACE: 31102ac6454SAndrew Thompson /* 312bd73b187SAlfred Perlstein * We assume that the endpoints are the same 313bd73b187SAlfred Perlstein * accross the alternate settings. 314bd73b187SAlfred Perlstein * 315bd73b187SAlfred Perlstein * Reset the endpoints, because re-attaching 316bd73b187SAlfred Perlstein * only a part of the device is not possible. 31702ac6454SAndrew Thompson */ 318bd73b187SAlfred Perlstein error = usb_check_alt_setting(udev, 319bd73b187SAlfred Perlstein iface, req.wValue[0]); 320bd73b187SAlfred Perlstein if (error) { 321bd73b187SAlfred Perlstein DPRINTF("alt setting does not exist %s\n", 322bd73b187SAlfred Perlstein usbd_errstr(error)); 323bd73b187SAlfred Perlstein goto tr_stalled; 324bd73b187SAlfred Perlstein } 325bd73b187SAlfred Perlstein error = usb_reset_iface_endpoints(udev, iface_index); 32602ac6454SAndrew Thompson if (error) { 32702ac6454SAndrew Thompson DPRINTF("alt setting failed %s\n", 328a593f6b8SAndrew Thompson usbd_errstr(error)); 32902ac6454SAndrew Thompson goto tr_stalled; 33002ac6454SAndrew Thompson } 331bd73b187SAlfred Perlstein /* update the current alternate setting */ 332bd73b187SAlfred Perlstein iface->alt_index = req.wValue[0]; 33302ac6454SAndrew Thompson break; 334bd73b187SAlfred Perlstein 33502ac6454SAndrew Thompson default: 33602ac6454SAndrew Thompson goto tr_stalled; 33702ac6454SAndrew Thompson } 33802ac6454SAndrew Thompson break; 33902ac6454SAndrew Thompson 34002ac6454SAndrew Thompson case UT_READ_INTERFACE: 34102ac6454SAndrew Thompson switch (req.bRequest) { 34202ac6454SAndrew Thompson case UR_GET_INTERFACE: 34302ac6454SAndrew Thompson *ppdata = &iface->alt_index; 34402ac6454SAndrew Thompson *plen = 1; 34502ac6454SAndrew Thompson break; 34602ac6454SAndrew Thompson 34702ac6454SAndrew Thompson default: 34802ac6454SAndrew Thompson goto tr_stalled; 34902ac6454SAndrew Thompson } 35002ac6454SAndrew Thompson break; 35102ac6454SAndrew Thompson default: 35202ac6454SAndrew Thompson goto tr_stalled; 35302ac6454SAndrew Thompson } 35402ac6454SAndrew Thompson tr_valid: 355cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 35602ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 35702ac6454SAndrew Thompson return (0); 35802ac6454SAndrew Thompson 35929bd7d7eSAndrew Thompson tr_short: 360cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 36129bd7d7eSAndrew Thompson USB_XFER_LOCK(xfer); 36229bd7d7eSAndrew Thompson return (USB_ERR_SHORT_XFER); 36329bd7d7eSAndrew Thompson 36402ac6454SAndrew Thompson tr_stalled: 365cb18f7d1SAlfred Perlstein usbd_enum_unlock(udev); 36602ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 36702ac6454SAndrew Thompson return (USB_ERR_STALLED); 36802ac6454SAndrew Thompson } 36902ac6454SAndrew Thompson 37002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 371a593f6b8SAndrew Thompson * usb_handle_stall 37202ac6454SAndrew Thompson * 37302ac6454SAndrew Thompson * Returns: 37402ac6454SAndrew Thompson * 0: Success 37502ac6454SAndrew Thompson * Else: Failure 37602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 377e0a69b51SAndrew Thompson static usb_error_t 378a593f6b8SAndrew Thompson usb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall) 37902ac6454SAndrew Thompson { 380760bc48eSAndrew Thompson struct usb_device *udev = xfer->xroot->udev; 381e0a69b51SAndrew Thompson usb_error_t err; 38202ac6454SAndrew Thompson 38302ac6454SAndrew Thompson USB_XFER_UNLOCK(xfer); 384a593f6b8SAndrew Thompson err = usbd_set_endpoint_stall(udev, 385a593f6b8SAndrew Thompson usbd_get_ep_by_addr(udev, ep), do_stall); 38602ac6454SAndrew Thompson USB_XFER_LOCK(xfer); 38702ac6454SAndrew Thompson return (err); 38802ac6454SAndrew Thompson } 38902ac6454SAndrew Thompson 39002ac6454SAndrew Thompson /*------------------------------------------------------------------------* 391a593f6b8SAndrew Thompson * usb_handle_get_stall 39202ac6454SAndrew Thompson * 39302ac6454SAndrew Thompson * Returns: 39402ac6454SAndrew Thompson * 0: Success 39502ac6454SAndrew Thompson * Else: Failure 39602ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 39702ac6454SAndrew Thompson static uint8_t 398a593f6b8SAndrew Thompson usb_handle_get_stall(struct usb_device *udev, uint8_t ea_val) 39902ac6454SAndrew Thompson { 400ae60fdfbSAndrew Thompson struct usb_endpoint *ep; 40102ac6454SAndrew Thompson uint8_t halted; 40202ac6454SAndrew Thompson 403a593f6b8SAndrew Thompson ep = usbd_get_ep_by_addr(udev, ea_val); 404ae60fdfbSAndrew Thompson if (ep == NULL) { 40502ac6454SAndrew Thompson /* nothing to do */ 40602ac6454SAndrew Thompson return (0); 40702ac6454SAndrew Thompson } 40802ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 409ae60fdfbSAndrew Thompson halted = ep->is_stalled; 41002ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 41102ac6454SAndrew Thompson 41202ac6454SAndrew Thompson return (halted); 41302ac6454SAndrew Thompson } 41402ac6454SAndrew Thompson 41502ac6454SAndrew Thompson /*------------------------------------------------------------------------* 416a593f6b8SAndrew Thompson * usb_handle_remote_wakeup 41702ac6454SAndrew Thompson * 41802ac6454SAndrew Thompson * Returns: 41902ac6454SAndrew Thompson * 0: Success 42002ac6454SAndrew Thompson * Else: Failure 42102ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 422e0a69b51SAndrew Thompson static usb_error_t 423a593f6b8SAndrew Thompson usb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on) 42402ac6454SAndrew Thompson { 425760bc48eSAndrew Thompson struct usb_device *udev; 426760bc48eSAndrew Thompson struct usb_bus *bus; 42702ac6454SAndrew Thompson 42802ac6454SAndrew Thompson udev = xfer->xroot->udev; 42902ac6454SAndrew Thompson bus = udev->bus; 43002ac6454SAndrew Thompson 43102ac6454SAndrew Thompson USB_BUS_LOCK(bus); 43202ac6454SAndrew Thompson 43302ac6454SAndrew Thompson if (is_on) { 43402ac6454SAndrew Thompson udev->flags.remote_wakeup = 1; 43502ac6454SAndrew Thompson } else { 43602ac6454SAndrew Thompson udev->flags.remote_wakeup = 0; 43702ac6454SAndrew Thompson } 43802ac6454SAndrew Thompson 43902ac6454SAndrew Thompson USB_BUS_UNLOCK(bus); 44002ac6454SAndrew Thompson 441*2ecb4e91SHans Petter Selasky #if USB_HAVE_POWERD 44202ac6454SAndrew Thompson /* In case we are out of sync, update the power state. */ 443a593f6b8SAndrew Thompson usb_bus_power_update(udev->bus); 444*2ecb4e91SHans Petter Selasky #endif 44502ac6454SAndrew Thompson return (0); /* success */ 44602ac6454SAndrew Thompson } 44702ac6454SAndrew Thompson 44802ac6454SAndrew Thompson /*------------------------------------------------------------------------* 449a593f6b8SAndrew Thompson * usb_handle_request 45002ac6454SAndrew Thompson * 45102ac6454SAndrew Thompson * Internal state sequence: 45202ac6454SAndrew Thompson * 453a755d548SAndrew Thompson * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR 45402ac6454SAndrew Thompson * 45502ac6454SAndrew Thompson * Returns: 45602ac6454SAndrew Thompson * 0: Ready to start hardware 45702ac6454SAndrew Thompson * Else: Stall current transfer, if any 45802ac6454SAndrew Thompson *------------------------------------------------------------------------*/ 459e0a69b51SAndrew Thompson static usb_error_t 460a593f6b8SAndrew Thompson usb_handle_request(struct usb_xfer *xfer) 46102ac6454SAndrew Thompson { 462760bc48eSAndrew Thompson struct usb_device_request req; 463760bc48eSAndrew Thompson struct usb_device *udev; 46402ac6454SAndrew Thompson const void *src_zcopy; /* zero-copy source pointer */ 46502ac6454SAndrew Thompson const void *src_mcopy; /* non zero-copy source pointer */ 46602ac6454SAndrew Thompson uint16_t off; /* data offset */ 46702ac6454SAndrew Thompson uint16_t rem; /* data remainder */ 46802ac6454SAndrew Thompson uint16_t max_len; /* max fragment length */ 46902ac6454SAndrew Thompson uint16_t wValue; 47002ac6454SAndrew Thompson uint16_t wIndex; 47102ac6454SAndrew Thompson uint8_t state; 47229bd7d7eSAndrew Thompson uint8_t is_complete = 1; 473e0a69b51SAndrew Thompson usb_error_t err; 47402ac6454SAndrew Thompson union { 47502ac6454SAndrew Thompson uWord wStatus; 47602ac6454SAndrew Thompson uint8_t buf[2]; 47702ac6454SAndrew Thompson } temp; 47802ac6454SAndrew Thompson 47902ac6454SAndrew Thompson /* 48002ac6454SAndrew Thompson * Filter the USB transfer state into 48102ac6454SAndrew Thompson * something which we understand: 48202ac6454SAndrew Thompson */ 48302ac6454SAndrew Thompson 48402ac6454SAndrew Thompson switch (USB_GET_STATE(xfer)) { 48502ac6454SAndrew Thompson case USB_ST_SETUP: 486a755d548SAndrew Thompson state = USB_HR_NOT_COMPLETE; 48702ac6454SAndrew Thompson 48802ac6454SAndrew Thompson if (!xfer->flags_int.control_act) { 48902ac6454SAndrew Thompson /* nothing to do */ 49002ac6454SAndrew Thompson goto tr_stalled; 49102ac6454SAndrew Thompson } 49202ac6454SAndrew Thompson break; 493a755d548SAndrew Thompson case USB_ST_TRANSFERRED: 49402ac6454SAndrew Thompson if (!xfer->flags_int.control_act) { 495a755d548SAndrew Thompson state = USB_HR_COMPLETE_OK; 49602ac6454SAndrew Thompson } else { 497a755d548SAndrew Thompson state = USB_HR_NOT_COMPLETE; 49802ac6454SAndrew Thompson } 49902ac6454SAndrew Thompson break; 500a755d548SAndrew Thompson default: 501a755d548SAndrew Thompson state = USB_HR_COMPLETE_ERR; 502a755d548SAndrew Thompson break; 50302ac6454SAndrew Thompson } 50402ac6454SAndrew Thompson 50502ac6454SAndrew Thompson /* reset frame stuff */ 50602ac6454SAndrew Thompson 507ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 0, 0); 50802ac6454SAndrew Thompson 509ed6d949aSAndrew Thompson usbd_xfer_set_frame_offset(xfer, 0, 0); 510ed6d949aSAndrew Thompson usbd_xfer_set_frame_offset(xfer, sizeof(req), 1); 51102ac6454SAndrew Thompson 51202ac6454SAndrew Thompson /* get the current request, if any */ 51302ac6454SAndrew Thompson 514a593f6b8SAndrew Thompson usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); 51502ac6454SAndrew Thompson 51602ac6454SAndrew Thompson if (xfer->flags_int.control_rem == 0xFFFF) { 51702ac6454SAndrew Thompson /* first time - not initialised */ 51802ac6454SAndrew Thompson rem = UGETW(req.wLength); 51902ac6454SAndrew Thompson off = 0; 52002ac6454SAndrew Thompson } else { 52102ac6454SAndrew Thompson /* not first time - initialised */ 52202ac6454SAndrew Thompson rem = xfer->flags_int.control_rem; 52302ac6454SAndrew Thompson off = UGETW(req.wLength) - rem; 52402ac6454SAndrew Thompson } 52502ac6454SAndrew Thompson 52602ac6454SAndrew Thompson /* set some defaults */ 52702ac6454SAndrew Thompson 52802ac6454SAndrew Thompson max_len = 0; 52902ac6454SAndrew Thompson src_zcopy = NULL; 53002ac6454SAndrew Thompson src_mcopy = NULL; 53102ac6454SAndrew Thompson udev = xfer->xroot->udev; 53202ac6454SAndrew Thompson 53302ac6454SAndrew Thompson /* get some request fields decoded */ 53402ac6454SAndrew Thompson 53502ac6454SAndrew Thompson wValue = UGETW(req.wValue); 53602ac6454SAndrew Thompson wIndex = UGETW(req.wIndex); 53702ac6454SAndrew Thompson 53802ac6454SAndrew Thompson DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " 53902ac6454SAndrew Thompson "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, 54002ac6454SAndrew Thompson req.bRequest, wValue, wIndex, off, rem, state); 54102ac6454SAndrew Thompson 54202ac6454SAndrew Thompson /* demultiplex the control request */ 54302ac6454SAndrew Thompson 54402ac6454SAndrew Thompson switch (req.bmRequestType) { 54502ac6454SAndrew Thompson case UT_READ_DEVICE: 546a755d548SAndrew Thompson if (state != USB_HR_NOT_COMPLETE) { 54702ac6454SAndrew Thompson break; 54802ac6454SAndrew Thompson } 54902ac6454SAndrew Thompson switch (req.bRequest) { 55002ac6454SAndrew Thompson case UR_GET_DESCRIPTOR: 55102ac6454SAndrew Thompson goto tr_handle_get_descriptor; 55202ac6454SAndrew Thompson case UR_GET_CONFIG: 55302ac6454SAndrew Thompson goto tr_handle_get_config; 55402ac6454SAndrew Thompson case UR_GET_STATUS: 55502ac6454SAndrew Thompson goto tr_handle_get_status; 55602ac6454SAndrew Thompson default: 55702ac6454SAndrew Thompson goto tr_stalled; 55802ac6454SAndrew Thompson } 55902ac6454SAndrew Thompson break; 56002ac6454SAndrew Thompson 56102ac6454SAndrew Thompson case UT_WRITE_DEVICE: 56202ac6454SAndrew Thompson switch (req.bRequest) { 56302ac6454SAndrew Thompson case UR_SET_ADDRESS: 56402ac6454SAndrew Thompson goto tr_handle_set_address; 56502ac6454SAndrew Thompson case UR_SET_CONFIG: 56602ac6454SAndrew Thompson goto tr_handle_set_config; 56702ac6454SAndrew Thompson case UR_CLEAR_FEATURE: 56802ac6454SAndrew Thompson switch (wValue) { 56902ac6454SAndrew Thompson case UF_DEVICE_REMOTE_WAKEUP: 57002ac6454SAndrew Thompson goto tr_handle_clear_wakeup; 57102ac6454SAndrew Thompson default: 57202ac6454SAndrew Thompson goto tr_stalled; 57302ac6454SAndrew Thompson } 57402ac6454SAndrew Thompson break; 57502ac6454SAndrew Thompson case UR_SET_FEATURE: 57602ac6454SAndrew Thompson switch (wValue) { 57702ac6454SAndrew Thompson case UF_DEVICE_REMOTE_WAKEUP: 57802ac6454SAndrew Thompson goto tr_handle_set_wakeup; 57902ac6454SAndrew Thompson default: 58002ac6454SAndrew Thompson goto tr_stalled; 58102ac6454SAndrew Thompson } 58202ac6454SAndrew Thompson break; 58302ac6454SAndrew Thompson default: 58402ac6454SAndrew Thompson goto tr_stalled; 58502ac6454SAndrew Thompson } 58602ac6454SAndrew Thompson break; 58702ac6454SAndrew Thompson 58802ac6454SAndrew Thompson case UT_WRITE_ENDPOINT: 58902ac6454SAndrew Thompson switch (req.bRequest) { 59002ac6454SAndrew Thompson case UR_CLEAR_FEATURE: 59102ac6454SAndrew Thompson switch (wValue) { 59202ac6454SAndrew Thompson case UF_ENDPOINT_HALT: 59302ac6454SAndrew Thompson goto tr_handle_clear_halt; 59402ac6454SAndrew Thompson default: 59502ac6454SAndrew Thompson goto tr_stalled; 59602ac6454SAndrew Thompson } 59702ac6454SAndrew Thompson break; 59802ac6454SAndrew Thompson case UR_SET_FEATURE: 59902ac6454SAndrew Thompson switch (wValue) { 60002ac6454SAndrew Thompson case UF_ENDPOINT_HALT: 60102ac6454SAndrew Thompson goto tr_handle_set_halt; 60202ac6454SAndrew Thompson default: 60302ac6454SAndrew Thompson goto tr_stalled; 60402ac6454SAndrew Thompson } 60502ac6454SAndrew Thompson break; 60602ac6454SAndrew Thompson default: 60702ac6454SAndrew Thompson goto tr_stalled; 60802ac6454SAndrew Thompson } 60902ac6454SAndrew Thompson break; 61002ac6454SAndrew Thompson 61102ac6454SAndrew Thompson case UT_READ_ENDPOINT: 61202ac6454SAndrew Thompson switch (req.bRequest) { 61302ac6454SAndrew Thompson case UR_GET_STATUS: 61402ac6454SAndrew Thompson goto tr_handle_get_ep_status; 61502ac6454SAndrew Thompson default: 61602ac6454SAndrew Thompson goto tr_stalled; 61702ac6454SAndrew Thompson } 61802ac6454SAndrew Thompson break; 61902ac6454SAndrew Thompson default: 62002ac6454SAndrew Thompson /* we use "USB_ADD_BYTES" to de-const the src_zcopy */ 621a593f6b8SAndrew Thompson err = usb_handle_iface_request(xfer, 62202ac6454SAndrew Thompson USB_ADD_BYTES(&src_zcopy, 0), 62302ac6454SAndrew Thompson &max_len, req, off, state); 62402ac6454SAndrew Thompson if (err == 0) { 62529bd7d7eSAndrew Thompson is_complete = 0; 62629bd7d7eSAndrew Thompson goto tr_valid; 62729bd7d7eSAndrew Thompson } else if (err == USB_ERR_SHORT_XFER) { 62802ac6454SAndrew Thompson goto tr_valid; 62902ac6454SAndrew Thompson } 63002ac6454SAndrew Thompson /* 63102ac6454SAndrew Thompson * Reset zero-copy pointer and max length 63202ac6454SAndrew Thompson * variable in case they were unintentionally 63302ac6454SAndrew Thompson * set: 63402ac6454SAndrew Thompson */ 63502ac6454SAndrew Thompson src_zcopy = NULL; 63602ac6454SAndrew Thompson max_len = 0; 63702ac6454SAndrew Thompson 63802ac6454SAndrew Thompson /* 63902ac6454SAndrew Thompson * Check if we have a vendor specific 64002ac6454SAndrew Thompson * descriptor: 64102ac6454SAndrew Thompson */ 64202ac6454SAndrew Thompson goto tr_handle_get_descriptor; 64302ac6454SAndrew Thompson } 64402ac6454SAndrew Thompson goto tr_valid; 64502ac6454SAndrew Thompson 64602ac6454SAndrew Thompson tr_handle_get_descriptor: 647a593f6b8SAndrew Thompson err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len); 648459d369eSAndrew Thompson if (err) 64902ac6454SAndrew Thompson goto tr_stalled; 650459d369eSAndrew Thompson if (src_zcopy == NULL) 651459d369eSAndrew Thompson goto tr_stalled; 65202ac6454SAndrew Thompson goto tr_valid; 65302ac6454SAndrew Thompson 65402ac6454SAndrew Thompson tr_handle_get_config: 65502ac6454SAndrew Thompson temp.buf[0] = udev->curr_config_no; 65602ac6454SAndrew Thompson src_mcopy = temp.buf; 65702ac6454SAndrew Thompson max_len = 1; 65802ac6454SAndrew Thompson goto tr_valid; 65902ac6454SAndrew Thompson 66002ac6454SAndrew Thompson tr_handle_get_status: 66102ac6454SAndrew Thompson 66202ac6454SAndrew Thompson wValue = 0; 66302ac6454SAndrew Thompson 66402ac6454SAndrew Thompson USB_BUS_LOCK(udev->bus); 66502ac6454SAndrew Thompson if (udev->flags.remote_wakeup) { 66602ac6454SAndrew Thompson wValue |= UDS_REMOTE_WAKEUP; 66702ac6454SAndrew Thompson } 66802ac6454SAndrew Thompson if (udev->flags.self_powered) { 66902ac6454SAndrew Thompson wValue |= UDS_SELF_POWERED; 67002ac6454SAndrew Thompson } 67102ac6454SAndrew Thompson USB_BUS_UNLOCK(udev->bus); 67202ac6454SAndrew Thompson 67302ac6454SAndrew Thompson USETW(temp.wStatus, wValue); 67402ac6454SAndrew Thompson src_mcopy = temp.wStatus; 67502ac6454SAndrew Thompson max_len = sizeof(temp.wStatus); 67602ac6454SAndrew Thompson goto tr_valid; 67702ac6454SAndrew Thompson 67802ac6454SAndrew Thompson tr_handle_set_address: 679a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 68002ac6454SAndrew Thompson if (wValue >= 0x80) { 68102ac6454SAndrew Thompson /* invalid value */ 68202ac6454SAndrew Thompson goto tr_stalled; 68302ac6454SAndrew Thompson } else if (udev->curr_config_no != 0) { 68402ac6454SAndrew Thompson /* we are configured ! */ 68502ac6454SAndrew Thompson goto tr_stalled; 68602ac6454SAndrew Thompson } 687a755d548SAndrew Thompson } else if (state != USB_HR_NOT_COMPLETE) { 68802ac6454SAndrew Thompson udev->address = (wValue & 0x7F); 68902ac6454SAndrew Thompson goto tr_bad_context; 69002ac6454SAndrew Thompson } 69102ac6454SAndrew Thompson goto tr_valid; 69202ac6454SAndrew Thompson 69302ac6454SAndrew Thompson tr_handle_set_config: 694a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 695a593f6b8SAndrew Thompson if (usb_handle_set_config(xfer, req.wValue[0])) { 69602ac6454SAndrew Thompson goto tr_stalled; 69702ac6454SAndrew Thompson } 69802ac6454SAndrew Thompson } 69902ac6454SAndrew Thompson goto tr_valid; 70002ac6454SAndrew Thompson 70102ac6454SAndrew Thompson tr_handle_clear_halt: 702a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 703a593f6b8SAndrew Thompson if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) { 70402ac6454SAndrew Thompson goto tr_stalled; 70502ac6454SAndrew Thompson } 70602ac6454SAndrew Thompson } 70702ac6454SAndrew Thompson goto tr_valid; 70802ac6454SAndrew Thompson 70902ac6454SAndrew Thompson tr_handle_clear_wakeup: 710a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 711a593f6b8SAndrew Thompson if (usb_handle_remote_wakeup(xfer, 0)) { 71202ac6454SAndrew Thompson goto tr_stalled; 71302ac6454SAndrew Thompson } 71402ac6454SAndrew Thompson } 71502ac6454SAndrew Thompson goto tr_valid; 71602ac6454SAndrew Thompson 71702ac6454SAndrew Thompson tr_handle_set_halt: 718a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 719a593f6b8SAndrew Thompson if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) { 72002ac6454SAndrew Thompson goto tr_stalled; 72102ac6454SAndrew Thompson } 72202ac6454SAndrew Thompson } 72302ac6454SAndrew Thompson goto tr_valid; 72402ac6454SAndrew Thompson 72502ac6454SAndrew Thompson tr_handle_set_wakeup: 726a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 727a593f6b8SAndrew Thompson if (usb_handle_remote_wakeup(xfer, 1)) { 72802ac6454SAndrew Thompson goto tr_stalled; 72902ac6454SAndrew Thompson } 73002ac6454SAndrew Thompson } 73102ac6454SAndrew Thompson goto tr_valid; 73202ac6454SAndrew Thompson 73302ac6454SAndrew Thompson tr_handle_get_ep_status: 734a755d548SAndrew Thompson if (state == USB_HR_NOT_COMPLETE) { 73502ac6454SAndrew Thompson temp.wStatus[0] = 736a593f6b8SAndrew Thompson usb_handle_get_stall(udev, req.wIndex[0]); 73702ac6454SAndrew Thompson temp.wStatus[1] = 0; 73802ac6454SAndrew Thompson src_mcopy = temp.wStatus; 73902ac6454SAndrew Thompson max_len = sizeof(temp.wStatus); 74002ac6454SAndrew Thompson } 74102ac6454SAndrew Thompson goto tr_valid; 74202ac6454SAndrew Thompson 74302ac6454SAndrew Thompson tr_valid: 744a755d548SAndrew Thompson if (state != USB_HR_NOT_COMPLETE) { 74502ac6454SAndrew Thompson goto tr_stalled; 74602ac6454SAndrew Thompson } 74702ac6454SAndrew Thompson /* subtract offset from length */ 74802ac6454SAndrew Thompson 74902ac6454SAndrew Thompson max_len -= off; 75002ac6454SAndrew Thompson 75102ac6454SAndrew Thompson /* Compute the real maximum data length */ 75202ac6454SAndrew Thompson 75302ac6454SAndrew Thompson if (max_len > xfer->max_data_length) { 754ed6d949aSAndrew Thompson max_len = usbd_xfer_max_len(xfer); 75502ac6454SAndrew Thompson } 75602ac6454SAndrew Thompson if (max_len > rem) { 75702ac6454SAndrew Thompson max_len = rem; 75802ac6454SAndrew Thompson } 75902ac6454SAndrew Thompson /* 76002ac6454SAndrew Thompson * If the remainder is greater than the maximum data length, 76102ac6454SAndrew Thompson * we need to truncate the value for the sake of the 76202ac6454SAndrew Thompson * comparison below: 76302ac6454SAndrew Thompson */ 76402ac6454SAndrew Thompson if (rem > xfer->max_data_length) { 765ed6d949aSAndrew Thompson rem = usbd_xfer_max_len(xfer); 76602ac6454SAndrew Thompson } 76729bd7d7eSAndrew Thompson if ((rem != max_len) && (is_complete != 0)) { 76802ac6454SAndrew Thompson /* 76902ac6454SAndrew Thompson * If we don't transfer the data we can transfer, then 77002ac6454SAndrew Thompson * the transfer is short ! 77102ac6454SAndrew Thompson */ 77202ac6454SAndrew Thompson xfer->flags.force_short_xfer = 1; 77302ac6454SAndrew Thompson xfer->nframes = 2; 77402ac6454SAndrew Thompson } else { 77502ac6454SAndrew Thompson /* 77602ac6454SAndrew Thompson * Default case 77702ac6454SAndrew Thompson */ 77802ac6454SAndrew Thompson xfer->flags.force_short_xfer = 0; 77902ac6454SAndrew Thompson xfer->nframes = max_len ? 2 : 1; 78002ac6454SAndrew Thompson } 78102ac6454SAndrew Thompson if (max_len > 0) { 78202ac6454SAndrew Thompson if (src_mcopy) { 78302ac6454SAndrew Thompson src_mcopy = USB_ADD_BYTES(src_mcopy, off); 784a593f6b8SAndrew Thompson usbd_copy_in(xfer->frbuffers + 1, 0, 78502ac6454SAndrew Thompson src_mcopy, max_len); 786ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, max_len); 78702ac6454SAndrew Thompson } else { 788ed6d949aSAndrew Thompson usbd_xfer_set_frame_data(xfer, 1, 789ed6d949aSAndrew Thompson USB_ADD_BYTES(src_zcopy, off), max_len); 79002ac6454SAndrew Thompson } 79102ac6454SAndrew Thompson } else { 79202ac6454SAndrew Thompson /* the end is reached, send status */ 79302ac6454SAndrew Thompson xfer->flags.manual_status = 0; 794ed6d949aSAndrew Thompson usbd_xfer_set_frame_len(xfer, 1, 0); 79502ac6454SAndrew Thompson } 79602ac6454SAndrew Thompson DPRINTF("success\n"); 79702ac6454SAndrew Thompson return (0); /* success */ 79802ac6454SAndrew Thompson 79902ac6454SAndrew Thompson tr_stalled: 800a755d548SAndrew Thompson DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ? 80102ac6454SAndrew Thompson "complete" : "stalled"); 80202ac6454SAndrew Thompson return (USB_ERR_STALLED); 80302ac6454SAndrew Thompson 80402ac6454SAndrew Thompson tr_bad_context: 80502ac6454SAndrew Thompson DPRINTF("bad context\n"); 80602ac6454SAndrew Thompson return (USB_ERR_BAD_CONTEXT); 80702ac6454SAndrew Thompson } 808