usb_handle_request.c revision 233774
1184610Salfred/* $FreeBSD: head/sys/dev/usb/usb_handle_request.c 233774 2012-04-02 10:50:42Z hselasky $ */ 2184610Salfred/*- 3184610Salfred * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 4184610Salfred * 5184610Salfred * Redistribution and use in source and binary forms, with or without 6184610Salfred * modification, are permitted provided that the following conditions 7184610Salfred * are met: 8184610Salfred * 1. Redistributions of source code must retain the above copyright 9184610Salfred * notice, this list of conditions and the following disclaimer. 10184610Salfred * 2. Redistributions in binary form must reproduce the above copyright 11184610Salfred * notice, this list of conditions and the following disclaimer in the 12184610Salfred * documentation and/or other materials provided with the distribution. 13184610Salfred * 14184610Salfred * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15184610Salfred * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16184610Salfred * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17184610Salfred * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18184610Salfred * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19184610Salfred * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20184610Salfred * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21184610Salfred * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22184610Salfred * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23184610Salfred * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24184610Salfred * SUCH DAMAGE. 25184610Salfred */ 26184610Salfred 27194677Sthompsa#include <sys/stdint.h> 28194677Sthompsa#include <sys/stddef.h> 29194677Sthompsa#include <sys/param.h> 30194677Sthompsa#include <sys/queue.h> 31194677Sthompsa#include <sys/types.h> 32194677Sthompsa#include <sys/systm.h> 33194677Sthompsa#include <sys/kernel.h> 34194677Sthompsa#include <sys/bus.h> 35194677Sthompsa#include <sys/module.h> 36194677Sthompsa#include <sys/lock.h> 37194677Sthompsa#include <sys/mutex.h> 38194677Sthompsa#include <sys/condvar.h> 39194677Sthompsa#include <sys/sysctl.h> 40194677Sthompsa#include <sys/sx.h> 41194677Sthompsa#include <sys/unistd.h> 42194677Sthompsa#include <sys/callout.h> 43194677Sthompsa#include <sys/malloc.h> 44194677Sthompsa#include <sys/priv.h> 45194677Sthompsa 46188942Sthompsa#include <dev/usb/usb.h> 47194677Sthompsa#include <dev/usb/usbdi.h> 48195963Salfred#include <dev/usb/usbdi_util.h> 49194677Sthompsa#include "usb_if.h" 50184610Salfred 51194228Sthompsa#define USB_DEBUG_VAR usb_debug 52184610Salfred 53188942Sthompsa#include <dev/usb/usb_core.h> 54188942Sthompsa#include <dev/usb/usb_process.h> 55188942Sthompsa#include <dev/usb/usb_busdma.h> 56188942Sthompsa#include <dev/usb/usb_transfer.h> 57188942Sthompsa#include <dev/usb/usb_device.h> 58188942Sthompsa#include <dev/usb/usb_debug.h> 59188942Sthompsa#include <dev/usb/usb_dynamic.h> 60188942Sthompsa#include <dev/usb/usb_hub.h> 61184610Salfred 62188942Sthompsa#include <dev/usb/usb_controller.h> 63188942Sthompsa#include <dev/usb/usb_bus.h> 64184610Salfred 65184610Salfred/* function prototypes */ 66184610Salfred 67194228Sthompsastatic uint8_t usb_handle_get_stall(struct usb_device *, uint8_t); 68194228Sthompsastatic usb_error_t usb_handle_remote_wakeup(struct usb_xfer *, uint8_t); 69194228Sthompsastatic usb_error_t usb_handle_request(struct usb_xfer *); 70194228Sthompsastatic usb_error_t usb_handle_set_config(struct usb_xfer *, uint8_t); 71194228Sthompsastatic usb_error_t usb_handle_set_stall(struct usb_xfer *, uint8_t, 72185948Sthompsa uint8_t); 73194228Sthompsastatic usb_error_t usb_handle_iface_request(struct usb_xfer *, void **, 74192984Sthompsa uint16_t *, struct usb_device_request, uint16_t, 75185948Sthompsa uint8_t); 76184610Salfred 77184610Salfred/*------------------------------------------------------------------------* 78194228Sthompsa * usb_handle_request_callback 79184610Salfred * 80184610Salfred * This function is the USB callback for generic USB Device control 81184610Salfred * transfers. 82184610Salfred *------------------------------------------------------------------------*/ 83184610Salfredvoid 84194677Sthompsausb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error) 85184610Salfred{ 86193045Sthompsa usb_error_t err; 87184610Salfred 88184610Salfred /* check the current transfer state */ 89184610Salfred 90184610Salfred switch (USB_GET_STATE(xfer)) { 91184610Salfred case USB_ST_SETUP: 92184610Salfred case USB_ST_TRANSFERRED: 93184610Salfred 94184610Salfred /* handle the request */ 95194228Sthompsa err = usb_handle_request(xfer); 96184610Salfred 97184610Salfred if (err) { 98184610Salfred 99184610Salfred if (err == USB_ERR_BAD_CONTEXT) { 100184610Salfred /* we need to re-setup the control transfer */ 101194228Sthompsa usb_needs_explore(xfer->xroot->bus, 0); 102184610Salfred break; 103184610Salfred } 104184610Salfred goto tr_restart; 105184610Salfred } 106194228Sthompsa usbd_transfer_submit(xfer); 107184610Salfred break; 108184610Salfred 109184610Salfred default: 110194064Sthompsa /* check if a control transfer is active */ 111194064Sthompsa if (xfer->flags_int.control_rem != 0xFFFF) { 112194064Sthompsa /* handle the request */ 113194228Sthompsa err = usb_handle_request(xfer); 114194064Sthompsa } 115184610Salfred if (xfer->error != USB_ERR_CANCELLED) { 116184610Salfred /* should not happen - try stalling */ 117184610Salfred goto tr_restart; 118184610Salfred } 119184610Salfred break; 120184610Salfred } 121184610Salfred return; 122184610Salfred 123184610Salfredtr_restart: 124194064Sthompsa /* 125194064Sthompsa * If a control transfer is active, stall it, and wait for the 126194064Sthompsa * next control transfer. 127194064Sthompsa */ 128194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request)); 129184610Salfred xfer->nframes = 1; 130184610Salfred xfer->flags.manual_status = 1; 131184610Salfred xfer->flags.force_short_xfer = 0; 132194677Sthompsa usbd_xfer_set_stall(xfer); /* cancel previous transfer, if any */ 133194228Sthompsa usbd_transfer_submit(xfer); 134184610Salfred} 135184610Salfred 136184610Salfred/*------------------------------------------------------------------------* 137194228Sthompsa * usb_handle_set_config 138184610Salfred * 139184610Salfred * Returns: 140184610Salfred * 0: Success 141184610Salfred * Else: Failure 142184610Salfred *------------------------------------------------------------------------*/ 143193045Sthompsastatic usb_error_t 144194228Sthompsausb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) 145184610Salfred{ 146192984Sthompsa struct usb_device *udev = xfer->xroot->udev; 147193045Sthompsa usb_error_t err = 0; 148184610Salfred 149184610Salfred /* 150184610Salfred * We need to protect against other threads doing probe and 151184610Salfred * attach: 152184610Salfred */ 153184824Sthompsa USB_XFER_UNLOCK(xfer); 154184610Salfred 155196498Salfred usbd_enum_lock(udev); 156196498Salfred 157184610Salfred if (conf_no == USB_UNCONFIG_NO) { 158184610Salfred conf_no = USB_UNCONFIG_INDEX; 159184610Salfred } else { 160184610Salfred /* 161184610Salfred * The relationship between config number and config index 162184610Salfred * is very simple in our case: 163184610Salfred */ 164184610Salfred conf_no--; 165184610Salfred } 166184610Salfred 167194228Sthompsa if (usbd_set_config_index(udev, conf_no)) { 168184610Salfred DPRINTF("set config %d failed\n", conf_no); 169184610Salfred err = USB_ERR_STALLED; 170184610Salfred goto done; 171184610Salfred } 172194228Sthompsa if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { 173184610Salfred DPRINTF("probe and attach failed\n"); 174184610Salfred err = USB_ERR_STALLED; 175184610Salfred goto done; 176184610Salfred } 177184610Salfreddone: 178196498Salfred usbd_enum_unlock(udev); 179184824Sthompsa USB_XFER_LOCK(xfer); 180184610Salfred return (err); 181184610Salfred} 182184610Salfred 183195963Salfredstatic usb_error_t 184195963Salfredusb_check_alt_setting(struct usb_device *udev, 185195963Salfred struct usb_interface *iface, uint8_t alt_index) 186195963Salfred{ 187195963Salfred uint8_t do_unlock; 188195963Salfred usb_error_t err = 0; 189195963Salfred 190195963Salfred /* automatic locking */ 191196498Salfred if (usbd_enum_is_locked(udev)) { 192195963Salfred do_unlock = 0; 193195963Salfred } else { 194195963Salfred do_unlock = 1; 195196498Salfred usbd_enum_lock(udev); 196195963Salfred } 197195963Salfred 198195963Salfred if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc)) 199195963Salfred err = USB_ERR_INVAL; 200195963Salfred 201196498Salfred if (do_unlock) 202196498Salfred usbd_enum_unlock(udev); 203196498Salfred 204195963Salfred return (err); 205195963Salfred} 206195963Salfred 207184610Salfred/*------------------------------------------------------------------------* 208194228Sthompsa * usb_handle_iface_request 209184610Salfred * 210184610Salfred * Returns: 211184610Salfred * 0: Success 212184610Salfred * Else: Failure 213184610Salfred *------------------------------------------------------------------------*/ 214193045Sthompsastatic usb_error_t 215194228Sthompsausb_handle_iface_request(struct usb_xfer *xfer, 216184610Salfred void **ppdata, uint16_t *plen, 217192984Sthompsa struct usb_device_request req, uint16_t off, uint8_t state) 218184610Salfred{ 219192984Sthompsa struct usb_interface *iface; 220192984Sthompsa struct usb_interface *iface_parent; /* parent interface */ 221192984Sthompsa struct usb_device *udev = xfer->xroot->udev; 222184610Salfred int error; 223184610Salfred uint8_t iface_index; 224195121Sthompsa uint8_t temp_state; 225184610Salfred 226184610Salfred if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { 227184610Salfred iface_index = req.wIndex[0]; /* unicast */ 228184610Salfred } else { 229184610Salfred iface_index = 0; /* broadcast */ 230184610Salfred } 231184610Salfred 232184610Salfred /* 233184610Salfred * We need to protect against other threads doing probe and 234184610Salfred * attach: 235184610Salfred */ 236184824Sthompsa USB_XFER_UNLOCK(xfer); 237184610Salfred 238196498Salfred usbd_enum_lock(udev); 239196498Salfred 240184610Salfred error = ENXIO; 241184610Salfred 242184610Salfredtr_repeat: 243194228Sthompsa iface = usbd_get_iface(udev, iface_index); 244184610Salfred if ((iface == NULL) || 245184610Salfred (iface->idesc == NULL)) { 246184610Salfred /* end of interfaces non-existing interface */ 247184610Salfred goto tr_stalled; 248184610Salfred } 249195121Sthompsa /* set initial state */ 250195121Sthompsa 251195121Sthompsa temp_state = state; 252195121Sthompsa 253184610Salfred /* forward request to interface, if any */ 254184610Salfred 255184610Salfred if ((error != 0) && 256184610Salfred (error != ENOTTY) && 257184610Salfred (iface->subdev != NULL) && 258184610Salfred device_is_attached(iface->subdev)) { 259184610Salfred#if 0 260190186Sthompsa DEVMETHOD(usb_handle_request, NULL); /* dummy */ 261184610Salfred#endif 262188942Sthompsa error = USB_HANDLE_REQUEST(iface->subdev, 263184610Salfred &req, ppdata, plen, 264195121Sthompsa off, &temp_state); 265184610Salfred } 266194228Sthompsa iface_parent = usbd_get_iface(udev, iface->parent_iface_index); 267184610Salfred 268184610Salfred if ((iface_parent == NULL) || 269184610Salfred (iface_parent->idesc == NULL)) { 270184610Salfred /* non-existing interface */ 271184610Salfred iface_parent = NULL; 272184610Salfred } 273184610Salfred /* forward request to parent interface, if any */ 274184610Salfred 275184610Salfred if ((error != 0) && 276184610Salfred (error != ENOTTY) && 277184610Salfred (iface_parent != NULL) && 278184610Salfred (iface_parent->subdev != NULL) && 279184610Salfred ((req.bmRequestType & 0x1F) == UT_INTERFACE) && 280184610Salfred (iface_parent->subdev != iface->subdev) && 281184610Salfred device_is_attached(iface_parent->subdev)) { 282188942Sthompsa error = USB_HANDLE_REQUEST(iface_parent->subdev, 283195121Sthompsa &req, ppdata, plen, off, &temp_state); 284184610Salfred } 285184610Salfred if (error == 0) { 286184610Salfred /* negativly adjust pointer and length */ 287184610Salfred *ppdata = ((uint8_t *)(*ppdata)) - off; 288184610Salfred *plen += off; 289195121Sthompsa 290195121Sthompsa if ((state == USB_HR_NOT_COMPLETE) && 291195121Sthompsa (temp_state == USB_HR_COMPLETE_OK)) 292195121Sthompsa goto tr_short; 293195121Sthompsa else 294195121Sthompsa goto tr_valid; 295184610Salfred } else if (error == ENOTTY) { 296184610Salfred goto tr_stalled; 297184610Salfred } 298184610Salfred if ((req.bmRequestType & 0x1F) != UT_INTERFACE) { 299184610Salfred iface_index++; /* iterate */ 300184610Salfred goto tr_repeat; 301184610Salfred } 302194064Sthompsa if (state != USB_HR_NOT_COMPLETE) { 303184610Salfred /* we are complete */ 304184610Salfred goto tr_valid; 305184610Salfred } 306184610Salfred switch (req.bmRequestType) { 307184610Salfred case UT_WRITE_INTERFACE: 308184610Salfred switch (req.bRequest) { 309184610Salfred case UR_SET_INTERFACE: 310184610Salfred /* 311195963Salfred * We assume that the endpoints are the same 312195963Salfred * accross the alternate settings. 313195963Salfred * 314195963Salfred * Reset the endpoints, because re-attaching 315195963Salfred * only a part of the device is not possible. 316184610Salfred */ 317195963Salfred error = usb_check_alt_setting(udev, 318195963Salfred iface, req.wValue[0]); 319184610Salfred if (error) { 320195963Salfred DPRINTF("alt setting does not exist %s\n", 321194228Sthompsa usbd_errstr(error)); 322184610Salfred goto tr_stalled; 323184610Salfred } 324195963Salfred error = usb_reset_iface_endpoints(udev, iface_index); 325184610Salfred if (error) { 326195963Salfred DPRINTF("alt setting failed %s\n", 327195963Salfred usbd_errstr(error)); 328184610Salfred goto tr_stalled; 329184610Salfred } 330195963Salfred /* update the current alternate setting */ 331195963Salfred iface->alt_index = req.wValue[0]; 332184610Salfred break; 333195963Salfred 334184610Salfred default: 335184610Salfred goto tr_stalled; 336184610Salfred } 337184610Salfred break; 338184610Salfred 339184610Salfred case UT_READ_INTERFACE: 340184610Salfred switch (req.bRequest) { 341184610Salfred case UR_GET_INTERFACE: 342184610Salfred *ppdata = &iface->alt_index; 343184610Salfred *plen = 1; 344184610Salfred break; 345184610Salfred 346184610Salfred default: 347184610Salfred goto tr_stalled; 348184610Salfred } 349184610Salfred break; 350184610Salfred default: 351184610Salfred goto tr_stalled; 352184610Salfred } 353184610Salfredtr_valid: 354196498Salfred usbd_enum_unlock(udev); 355184824Sthompsa USB_XFER_LOCK(xfer); 356184610Salfred return (0); 357184610Salfred 358195121Sthompsatr_short: 359196498Salfred usbd_enum_unlock(udev); 360195121Sthompsa USB_XFER_LOCK(xfer); 361195121Sthompsa return (USB_ERR_SHORT_XFER); 362195121Sthompsa 363184610Salfredtr_stalled: 364196498Salfred usbd_enum_unlock(udev); 365184824Sthompsa USB_XFER_LOCK(xfer); 366184610Salfred return (USB_ERR_STALLED); 367184610Salfred} 368184610Salfred 369184610Salfred/*------------------------------------------------------------------------* 370194228Sthompsa * usb_handle_stall 371184610Salfred * 372184610Salfred * Returns: 373184610Salfred * 0: Success 374184610Salfred * Else: Failure 375184610Salfred *------------------------------------------------------------------------*/ 376193045Sthompsastatic usb_error_t 377194228Sthompsausb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall) 378184610Salfred{ 379192984Sthompsa struct usb_device *udev = xfer->xroot->udev; 380193045Sthompsa usb_error_t err; 381184610Salfred 382184824Sthompsa USB_XFER_UNLOCK(xfer); 383194228Sthompsa err = usbd_set_endpoint_stall(udev, 384194228Sthompsa usbd_get_ep_by_addr(udev, ep), do_stall); 385184824Sthompsa USB_XFER_LOCK(xfer); 386184610Salfred return (err); 387184610Salfred} 388184610Salfred 389184610Salfred/*------------------------------------------------------------------------* 390194228Sthompsa * usb_handle_get_stall 391184610Salfred * 392184610Salfred * Returns: 393184610Salfred * 0: Success 394184610Salfred * Else: Failure 395184610Salfred *------------------------------------------------------------------------*/ 396184610Salfredstatic uint8_t 397194228Sthompsausb_handle_get_stall(struct usb_device *udev, uint8_t ea_val) 398184610Salfred{ 399193644Sthompsa struct usb_endpoint *ep; 400184610Salfred uint8_t halted; 401184610Salfred 402194228Sthompsa ep = usbd_get_ep_by_addr(udev, ea_val); 403193644Sthompsa if (ep == NULL) { 404184610Salfred /* nothing to do */ 405184610Salfred return (0); 406184610Salfred } 407184824Sthompsa USB_BUS_LOCK(udev->bus); 408193644Sthompsa halted = ep->is_stalled; 409184824Sthompsa USB_BUS_UNLOCK(udev->bus); 410184610Salfred 411184610Salfred return (halted); 412184610Salfred} 413184610Salfred 414184610Salfred/*------------------------------------------------------------------------* 415194228Sthompsa * usb_handle_remote_wakeup 416184610Salfred * 417184610Salfred * Returns: 418184610Salfred * 0: Success 419184610Salfred * Else: Failure 420184610Salfred *------------------------------------------------------------------------*/ 421193045Sthompsastatic usb_error_t 422194228Sthompsausb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on) 423184610Salfred{ 424192984Sthompsa struct usb_device *udev; 425192984Sthompsa struct usb_bus *bus; 426184610Salfred 427187173Sthompsa udev = xfer->xroot->udev; 428184610Salfred bus = udev->bus; 429184610Salfred 430184824Sthompsa USB_BUS_LOCK(bus); 431184610Salfred 432184610Salfred if (is_on) { 433184610Salfred udev->flags.remote_wakeup = 1; 434184610Salfred } else { 435184610Salfred udev->flags.remote_wakeup = 0; 436184610Salfred } 437184610Salfred 438184824Sthompsa USB_BUS_UNLOCK(bus); 439184610Salfred 440213434Shselasky#if USB_HAVE_POWERD 441186730Salfred /* In case we are out of sync, update the power state. */ 442194228Sthompsa usb_bus_power_update(udev->bus); 443213434Shselasky#endif 444184610Salfred return (0); /* success */ 445184610Salfred} 446184610Salfred 447184610Salfred/*------------------------------------------------------------------------* 448194228Sthompsa * usb_handle_request 449184610Salfred * 450184610Salfred * Internal state sequence: 451184610Salfred * 452194064Sthompsa * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR 453184610Salfred * 454184610Salfred * Returns: 455184610Salfred * 0: Ready to start hardware 456184610Salfred * Else: Stall current transfer, if any 457184610Salfred *------------------------------------------------------------------------*/ 458193045Sthompsastatic usb_error_t 459194228Sthompsausb_handle_request(struct usb_xfer *xfer) 460184610Salfred{ 461192984Sthompsa struct usb_device_request req; 462192984Sthompsa struct usb_device *udev; 463184610Salfred const void *src_zcopy; /* zero-copy source pointer */ 464184610Salfred const void *src_mcopy; /* non zero-copy source pointer */ 465184610Salfred uint16_t off; /* data offset */ 466184610Salfred uint16_t rem; /* data remainder */ 467184610Salfred uint16_t max_len; /* max fragment length */ 468184610Salfred uint16_t wValue; 469184610Salfred uint8_t state; 470195121Sthompsa uint8_t is_complete = 1; 471193045Sthompsa usb_error_t err; 472184610Salfred union { 473184610Salfred uWord wStatus; 474184610Salfred uint8_t buf[2]; 475184610Salfred } temp; 476184610Salfred 477184610Salfred /* 478184610Salfred * Filter the USB transfer state into 479184610Salfred * something which we understand: 480184610Salfred */ 481184610Salfred 482184610Salfred switch (USB_GET_STATE(xfer)) { 483184610Salfred case USB_ST_SETUP: 484194064Sthompsa state = USB_HR_NOT_COMPLETE; 485184610Salfred 486184610Salfred if (!xfer->flags_int.control_act) { 487184610Salfred /* nothing to do */ 488184610Salfred goto tr_stalled; 489184610Salfred } 490184610Salfred break; 491194064Sthompsa case USB_ST_TRANSFERRED: 492184610Salfred if (!xfer->flags_int.control_act) { 493194064Sthompsa state = USB_HR_COMPLETE_OK; 494184610Salfred } else { 495194064Sthompsa state = USB_HR_NOT_COMPLETE; 496184610Salfred } 497184610Salfred break; 498194064Sthompsa default: 499194064Sthompsa state = USB_HR_COMPLETE_ERR; 500194064Sthompsa break; 501184610Salfred } 502184610Salfred 503184610Salfred /* reset frame stuff */ 504184610Salfred 505194677Sthompsa usbd_xfer_set_frame_len(xfer, 0, 0); 506184610Salfred 507194677Sthompsa usbd_xfer_set_frame_offset(xfer, 0, 0); 508194677Sthompsa usbd_xfer_set_frame_offset(xfer, sizeof(req), 1); 509184610Salfred 510184610Salfred /* get the current request, if any */ 511184610Salfred 512194228Sthompsa usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); 513184610Salfred 514184610Salfred if (xfer->flags_int.control_rem == 0xFFFF) { 515184610Salfred /* first time - not initialised */ 516184610Salfred rem = UGETW(req.wLength); 517184610Salfred off = 0; 518184610Salfred } else { 519184610Salfred /* not first time - initialised */ 520184610Salfred rem = xfer->flags_int.control_rem; 521184610Salfred off = UGETW(req.wLength) - rem; 522184610Salfred } 523184610Salfred 524184610Salfred /* set some defaults */ 525184610Salfred 526184610Salfred max_len = 0; 527184610Salfred src_zcopy = NULL; 528184610Salfred src_mcopy = NULL; 529187173Sthompsa udev = xfer->xroot->udev; 530184610Salfred 531184610Salfred /* get some request fields decoded */ 532184610Salfred 533184610Salfred wValue = UGETW(req.wValue); 534184610Salfred 535184610Salfred DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " 536184610Salfred "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, 537233774Shselasky req.bRequest, wValue, UGETW(req.wIndex), off, rem, state); 538184610Salfred 539184610Salfred /* demultiplex the control request */ 540184610Salfred 541184610Salfred switch (req.bmRequestType) { 542184610Salfred case UT_READ_DEVICE: 543194064Sthompsa if (state != USB_HR_NOT_COMPLETE) { 544184610Salfred break; 545184610Salfred } 546184610Salfred switch (req.bRequest) { 547184610Salfred case UR_GET_DESCRIPTOR: 548184610Salfred goto tr_handle_get_descriptor; 549184610Salfred case UR_GET_CONFIG: 550184610Salfred goto tr_handle_get_config; 551184610Salfred case UR_GET_STATUS: 552184610Salfred goto tr_handle_get_status; 553184610Salfred default: 554184610Salfred goto tr_stalled; 555184610Salfred } 556184610Salfred break; 557184610Salfred 558184610Salfred case UT_WRITE_DEVICE: 559184610Salfred switch (req.bRequest) { 560184610Salfred case UR_SET_ADDRESS: 561184610Salfred goto tr_handle_set_address; 562184610Salfred case UR_SET_CONFIG: 563184610Salfred goto tr_handle_set_config; 564184610Salfred case UR_CLEAR_FEATURE: 565184610Salfred switch (wValue) { 566184610Salfred case UF_DEVICE_REMOTE_WAKEUP: 567184610Salfred goto tr_handle_clear_wakeup; 568184610Salfred default: 569184610Salfred goto tr_stalled; 570184610Salfred } 571184610Salfred break; 572184610Salfred case UR_SET_FEATURE: 573184610Salfred switch (wValue) { 574184610Salfred case UF_DEVICE_REMOTE_WAKEUP: 575184610Salfred goto tr_handle_set_wakeup; 576184610Salfred default: 577184610Salfred goto tr_stalled; 578184610Salfred } 579184610Salfred break; 580184610Salfred default: 581184610Salfred goto tr_stalled; 582184610Salfred } 583184610Salfred break; 584184610Salfred 585184610Salfred case UT_WRITE_ENDPOINT: 586184610Salfred switch (req.bRequest) { 587184610Salfred case UR_CLEAR_FEATURE: 588184610Salfred switch (wValue) { 589184610Salfred case UF_ENDPOINT_HALT: 590184610Salfred goto tr_handle_clear_halt; 591184610Salfred default: 592184610Salfred goto tr_stalled; 593184610Salfred } 594184610Salfred break; 595184610Salfred case UR_SET_FEATURE: 596184610Salfred switch (wValue) { 597184610Salfred case UF_ENDPOINT_HALT: 598184610Salfred goto tr_handle_set_halt; 599184610Salfred default: 600184610Salfred goto tr_stalled; 601184610Salfred } 602184610Salfred break; 603184610Salfred default: 604184610Salfred goto tr_stalled; 605184610Salfred } 606184610Salfred break; 607184610Salfred 608184610Salfred case UT_READ_ENDPOINT: 609184610Salfred switch (req.bRequest) { 610184610Salfred case UR_GET_STATUS: 611184610Salfred goto tr_handle_get_ep_status; 612184610Salfred default: 613184610Salfred goto tr_stalled; 614184610Salfred } 615184610Salfred break; 616184610Salfred default: 617184610Salfred /* we use "USB_ADD_BYTES" to de-const the src_zcopy */ 618194228Sthompsa err = usb_handle_iface_request(xfer, 619184610Salfred USB_ADD_BYTES(&src_zcopy, 0), 620184610Salfred &max_len, req, off, state); 621184610Salfred if (err == 0) { 622195121Sthompsa is_complete = 0; 623184610Salfred goto tr_valid; 624195121Sthompsa } else if (err == USB_ERR_SHORT_XFER) { 625195121Sthompsa goto tr_valid; 626184610Salfred } 627184610Salfred /* 628184610Salfred * Reset zero-copy pointer and max length 629184610Salfred * variable in case they were unintentionally 630184610Salfred * set: 631184610Salfred */ 632184610Salfred src_zcopy = NULL; 633184610Salfred max_len = 0; 634184610Salfred 635184610Salfred /* 636184610Salfred * Check if we have a vendor specific 637184610Salfred * descriptor: 638184610Salfred */ 639184610Salfred goto tr_handle_get_descriptor; 640184610Salfred } 641184610Salfred goto tr_valid; 642184610Salfred 643184610Salfredtr_handle_get_descriptor: 644194228Sthompsa err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len); 645191402Sthompsa if (err) 646184610Salfred goto tr_stalled; 647191402Sthompsa if (src_zcopy == NULL) 648191402Sthompsa goto tr_stalled; 649184610Salfred goto tr_valid; 650184610Salfred 651184610Salfredtr_handle_get_config: 652184610Salfred temp.buf[0] = udev->curr_config_no; 653184610Salfred src_mcopy = temp.buf; 654184610Salfred max_len = 1; 655184610Salfred goto tr_valid; 656184610Salfred 657184610Salfredtr_handle_get_status: 658184610Salfred 659184610Salfred wValue = 0; 660184610Salfred 661184824Sthompsa USB_BUS_LOCK(udev->bus); 662184610Salfred if (udev->flags.remote_wakeup) { 663184610Salfred wValue |= UDS_REMOTE_WAKEUP; 664184610Salfred } 665184610Salfred if (udev->flags.self_powered) { 666184610Salfred wValue |= UDS_SELF_POWERED; 667184610Salfred } 668184824Sthompsa USB_BUS_UNLOCK(udev->bus); 669184610Salfred 670184610Salfred USETW(temp.wStatus, wValue); 671184610Salfred src_mcopy = temp.wStatus; 672184610Salfred max_len = sizeof(temp.wStatus); 673184610Salfred goto tr_valid; 674184610Salfred 675184610Salfredtr_handle_set_address: 676194064Sthompsa if (state == USB_HR_NOT_COMPLETE) { 677184610Salfred if (wValue >= 0x80) { 678184610Salfred /* invalid value */ 679184610Salfred goto tr_stalled; 680184610Salfred } else if (udev->curr_config_no != 0) { 681184610Salfred /* we are configured ! */ 682184610Salfred goto tr_stalled; 683184610Salfred } 684194064Sthompsa } else if (state != USB_HR_NOT_COMPLETE) { 685184610Salfred udev->address = (wValue & 0x7F); 686184610Salfred goto tr_bad_context; 687184610Salfred } 688184610Salfred goto tr_valid; 689184610Salfred 690184610Salfredtr_handle_set_config: 691194064Sthompsa if (state == USB_HR_NOT_COMPLETE) { 692194228Sthompsa if (usb_handle_set_config(xfer, req.wValue[0])) { 693184610Salfred goto tr_stalled; 694184610Salfred } 695184610Salfred } 696184610Salfred goto tr_valid; 697184610Salfred 698184610Salfredtr_handle_clear_halt: 699194064Sthompsa if (state == USB_HR_NOT_COMPLETE) { 700194228Sthompsa if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) { 701184610Salfred goto tr_stalled; 702184610Salfred } 703184610Salfred } 704184610Salfred goto tr_valid; 705184610Salfred 706184610Salfredtr_handle_clear_wakeup: 707194064Sthompsa if (state == USB_HR_NOT_COMPLETE) { 708194228Sthompsa if (usb_handle_remote_wakeup(xfer, 0)) { 709184610Salfred goto tr_stalled; 710184610Salfred } 711184610Salfred } 712184610Salfred goto tr_valid; 713184610Salfred 714184610Salfredtr_handle_set_halt: 715194064Sthompsa if (state == USB_HR_NOT_COMPLETE) { 716194228Sthompsa if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) { 717184610Salfred goto tr_stalled; 718184610Salfred } 719184610Salfred } 720184610Salfred goto tr_valid; 721184610Salfred 722184610Salfredtr_handle_set_wakeup: 723194064Sthompsa if (state == USB_HR_NOT_COMPLETE) { 724194228Sthompsa if (usb_handle_remote_wakeup(xfer, 1)) { 725184610Salfred goto tr_stalled; 726184610Salfred } 727184610Salfred } 728184610Salfred goto tr_valid; 729184610Salfred 730184610Salfredtr_handle_get_ep_status: 731194064Sthompsa if (state == USB_HR_NOT_COMPLETE) { 732184610Salfred temp.wStatus[0] = 733194228Sthompsa usb_handle_get_stall(udev, req.wIndex[0]); 734184610Salfred temp.wStatus[1] = 0; 735184610Salfred src_mcopy = temp.wStatus; 736184610Salfred max_len = sizeof(temp.wStatus); 737184610Salfred } 738184610Salfred goto tr_valid; 739184610Salfred 740184610Salfredtr_valid: 741194064Sthompsa if (state != USB_HR_NOT_COMPLETE) { 742184610Salfred goto tr_stalled; 743184610Salfred } 744184610Salfred /* subtract offset from length */ 745184610Salfred 746184610Salfred max_len -= off; 747184610Salfred 748184610Salfred /* Compute the real maximum data length */ 749184610Salfred 750184610Salfred if (max_len > xfer->max_data_length) { 751194677Sthompsa max_len = usbd_xfer_max_len(xfer); 752184610Salfred } 753184610Salfred if (max_len > rem) { 754184610Salfred max_len = rem; 755184610Salfred } 756184610Salfred /* 757184610Salfred * If the remainder is greater than the maximum data length, 758184610Salfred * we need to truncate the value for the sake of the 759184610Salfred * comparison below: 760184610Salfred */ 761184610Salfred if (rem > xfer->max_data_length) { 762194677Sthompsa rem = usbd_xfer_max_len(xfer); 763184610Salfred } 764195121Sthompsa if ((rem != max_len) && (is_complete != 0)) { 765184610Salfred /* 766184610Salfred * If we don't transfer the data we can transfer, then 767184610Salfred * the transfer is short ! 768184610Salfred */ 769184610Salfred xfer->flags.force_short_xfer = 1; 770184610Salfred xfer->nframes = 2; 771184610Salfred } else { 772184610Salfred /* 773184610Salfred * Default case 774184610Salfred */ 775184610Salfred xfer->flags.force_short_xfer = 0; 776184610Salfred xfer->nframes = max_len ? 2 : 1; 777184610Salfred } 778184610Salfred if (max_len > 0) { 779184610Salfred if (src_mcopy) { 780184610Salfred src_mcopy = USB_ADD_BYTES(src_mcopy, off); 781194228Sthompsa usbd_copy_in(xfer->frbuffers + 1, 0, 782184610Salfred src_mcopy, max_len); 783194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, max_len); 784184610Salfred } else { 785194677Sthompsa usbd_xfer_set_frame_data(xfer, 1, 786194677Sthompsa USB_ADD_BYTES(src_zcopy, off), max_len); 787184610Salfred } 788184610Salfred } else { 789184610Salfred /* the end is reached, send status */ 790184610Salfred xfer->flags.manual_status = 0; 791194677Sthompsa usbd_xfer_set_frame_len(xfer, 1, 0); 792184610Salfred } 793184610Salfred DPRINTF("success\n"); 794184610Salfred return (0); /* success */ 795184610Salfred 796184610Salfredtr_stalled: 797194064Sthompsa DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ? 798184610Salfred "complete" : "stalled"); 799184610Salfred return (USB_ERR_STALLED); 800184610Salfred 801184610Salfredtr_bad_context: 802184610Salfred DPRINTF("bad context\n"); 803184610Salfred return (USB_ERR_BAD_CONTEXT); 804184610Salfred} 805