usb_handle_request.c revision 196498
1252190Srpaulo/* $FreeBSD: head/sys/dev/usb/usb_handle_request.c 196498 2009-08-24 05:05:38Z alfred $ */ 2252190Srpaulo/*- 3252190Srpaulo * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 4252190Srpaulo * 5252190Srpaulo * Redistribution and use in source and binary forms, with or without 6252190Srpaulo * modification, are permitted provided that the following conditions 7252190Srpaulo * are met: 8252190Srpaulo * 1. Redistributions of source code must retain the above copyright 9252190Srpaulo * notice, this list of conditions and the following disclaimer. 10252190Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11252190Srpaulo * notice, this list of conditions and the following disclaimer in the 12252190Srpaulo * documentation and/or other materials provided with the distribution. 13252190Srpaulo * 14252190Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15252190Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16252190Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17252190Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18252190Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19252190Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20252190Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21252190Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22252190Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23252190Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24252190Srpaulo * SUCH DAMAGE. 25252190Srpaulo */ 26252190Srpaulo 27252190Srpaulo#include <sys/stdint.h> 28252190Srpaulo#include <sys/stddef.h> 29252190Srpaulo#include <sys/param.h> 30252190Srpaulo#include <sys/queue.h> 31252190Srpaulo#include <sys/types.h> 32252190Srpaulo#include <sys/systm.h> 33252190Srpaulo#include <sys/kernel.h> 34252190Srpaulo#include <sys/bus.h> 35252190Srpaulo#include <sys/linker_set.h> 36252190Srpaulo#include <sys/module.h> 37252190Srpaulo#include <sys/lock.h> 38252190Srpaulo#include <sys/mutex.h> 39252190Srpaulo#include <sys/condvar.h> 40252190Srpaulo#include <sys/sysctl.h> 41252190Srpaulo#include <sys/sx.h> 42252190Srpaulo#include <sys/unistd.h> 43252190Srpaulo#include <sys/callout.h> 44252190Srpaulo#include <sys/malloc.h> 45252190Srpaulo#include <sys/priv.h> 46252190Srpaulo 47252190Srpaulo#include <dev/usb/usb.h> 48252190Srpaulo#include <dev/usb/usbdi.h> 49252190Srpaulo#include <dev/usb/usbdi_util.h> 50252190Srpaulo#include "usb_if.h" 51252190Srpaulo 52252190Srpaulo#define USB_DEBUG_VAR usb_debug 53252190Srpaulo 54252190Srpaulo#include <dev/usb/usb_core.h> 55252190Srpaulo#include <dev/usb/usb_process.h> 56252190Srpaulo#include <dev/usb/usb_busdma.h> 57252190Srpaulo#include <dev/usb/usb_transfer.h> 58252190Srpaulo#include <dev/usb/usb_device.h> 59252190Srpaulo#include <dev/usb/usb_debug.h> 60252190Srpaulo#include <dev/usb/usb_dynamic.h> 61252190Srpaulo#include <dev/usb/usb_hub.h> 62252190Srpaulo 63252190Srpaulo#include <dev/usb/usb_controller.h> 64252190Srpaulo#include <dev/usb/usb_bus.h> 65252190Srpaulo 66252190Srpaulo/* function prototypes */ 67252190Srpaulo 68252190Srpaulostatic uint8_t usb_handle_get_stall(struct usb_device *, uint8_t); 69252190Srpaulostatic usb_error_t usb_handle_remote_wakeup(struct usb_xfer *, uint8_t); 70252190Srpaulostatic usb_error_t usb_handle_request(struct usb_xfer *); 71252190Srpaulostatic usb_error_t usb_handle_set_config(struct usb_xfer *, uint8_t); 72252190Srpaulostatic usb_error_t usb_handle_set_stall(struct usb_xfer *, uint8_t, 73252190Srpaulo uint8_t); 74252190Srpaulostatic usb_error_t usb_handle_iface_request(struct usb_xfer *, void **, 75252190Srpaulo uint16_t *, struct usb_device_request, uint16_t, 76252190Srpaulo uint8_t); 77252190Srpaulo 78252190Srpaulo/*------------------------------------------------------------------------* 79252190Srpaulo * usb_handle_request_callback 80252190Srpaulo * 81252190Srpaulo * This function is the USB callback for generic USB Device control 82252190Srpaulo * transfers. 83252190Srpaulo *------------------------------------------------------------------------*/ 84252190Srpaulovoid 85252190Srpaulousb_handle_request_callback(struct usb_xfer *xfer, usb_error_t error) 86252190Srpaulo{ 87252190Srpaulo usb_error_t err; 88252190Srpaulo 89252190Srpaulo /* check the current transfer state */ 90252190Srpaulo 91252190Srpaulo switch (USB_GET_STATE(xfer)) { 92252190Srpaulo case USB_ST_SETUP: 93252190Srpaulo case USB_ST_TRANSFERRED: 94252190Srpaulo 95252190Srpaulo /* handle the request */ 96252190Srpaulo err = usb_handle_request(xfer); 97252190Srpaulo 98252190Srpaulo if (err) { 99252190Srpaulo 100252190Srpaulo if (err == USB_ERR_BAD_CONTEXT) { 101252190Srpaulo /* we need to re-setup the control transfer */ 102252190Srpaulo usb_needs_explore(xfer->xroot->bus, 0); 103252190Srpaulo break; 104252190Srpaulo } 105252190Srpaulo goto tr_restart; 106252190Srpaulo } 107252190Srpaulo usbd_transfer_submit(xfer); 108252190Srpaulo break; 109252190Srpaulo 110252190Srpaulo default: 111252190Srpaulo /* check if a control transfer is active */ 112252190Srpaulo if (xfer->flags_int.control_rem != 0xFFFF) { 113252190Srpaulo /* handle the request */ 114252190Srpaulo err = usb_handle_request(xfer); 115252190Srpaulo } 116252190Srpaulo if (xfer->error != USB_ERR_CANCELLED) { 117252190Srpaulo /* should not happen - try stalling */ 118252190Srpaulo goto tr_restart; 119252190Srpaulo } 120252190Srpaulo break; 121252190Srpaulo } 122252190Srpaulo return; 123252190Srpaulo 124252190Srpaulotr_restart: 125252190Srpaulo /* 126252190Srpaulo * If a control transfer is active, stall it, and wait for the 127252190Srpaulo * next control transfer. 128252190Srpaulo */ 129252190Srpaulo usbd_xfer_set_frame_len(xfer, 0, sizeof(struct usb_device_request)); 130252190Srpaulo xfer->nframes = 1; 131252190Srpaulo xfer->flags.manual_status = 1; 132252190Srpaulo xfer->flags.force_short_xfer = 0; 133252190Srpaulo usbd_xfer_set_stall(xfer); /* cancel previous transfer, if any */ 134252190Srpaulo usbd_transfer_submit(xfer); 135252190Srpaulo} 136252190Srpaulo 137252190Srpaulo/*------------------------------------------------------------------------* 138252190Srpaulo * usb_handle_set_config 139252190Srpaulo * 140252190Srpaulo * Returns: 141252190Srpaulo * 0: Success 142252190Srpaulo * Else: Failure 143252190Srpaulo *------------------------------------------------------------------------*/ 144252190Srpaulostatic usb_error_t 145252190Srpaulousb_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) 146252190Srpaulo{ 147252190Srpaulo struct usb_device *udev = xfer->xroot->udev; 148252190Srpaulo usb_error_t err = 0; 149252190Srpaulo 150252190Srpaulo /* 151252190Srpaulo * We need to protect against other threads doing probe and 152252190Srpaulo * attach: 153252190Srpaulo */ 154252190Srpaulo USB_XFER_UNLOCK(xfer); 155252190Srpaulo 156252190Srpaulo usbd_enum_lock(udev); 157252190Srpaulo 158252190Srpaulo if (conf_no == USB_UNCONFIG_NO) { 159252190Srpaulo conf_no = USB_UNCONFIG_INDEX; 160252190Srpaulo } else { 161252190Srpaulo /* 162252190Srpaulo * The relationship between config number and config index 163252190Srpaulo * is very simple in our case: 164252190Srpaulo */ 165252190Srpaulo conf_no--; 166252190Srpaulo } 167252190Srpaulo 168252190Srpaulo if (usbd_set_config_index(udev, conf_no)) { 169252190Srpaulo DPRINTF("set config %d failed\n", conf_no); 170252190Srpaulo err = USB_ERR_STALLED; 171252190Srpaulo goto done; 172252190Srpaulo } 173252190Srpaulo if (usb_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { 174252190Srpaulo DPRINTF("probe and attach failed\n"); 175252190Srpaulo err = USB_ERR_STALLED; 176252190Srpaulo goto done; 177252190Srpaulo } 178252190Srpaulodone: 179252190Srpaulo usbd_enum_unlock(udev); 180252190Srpaulo USB_XFER_LOCK(xfer); 181252190Srpaulo return (err); 182252190Srpaulo} 183252190Srpaulo 184252190Srpaulostatic usb_error_t 185252190Srpaulousb_check_alt_setting(struct usb_device *udev, 186252190Srpaulo struct usb_interface *iface, uint8_t alt_index) 187252190Srpaulo{ 188252190Srpaulo uint8_t do_unlock; 189252190Srpaulo usb_error_t err = 0; 190252190Srpaulo 191252190Srpaulo /* automatic locking */ 192252190Srpaulo if (usbd_enum_is_locked(udev)) { 193252190Srpaulo do_unlock = 0; 194252190Srpaulo } else { 195252190Srpaulo do_unlock = 1; 196252190Srpaulo usbd_enum_lock(udev); 197252190Srpaulo } 198252190Srpaulo 199252190Srpaulo if (alt_index >= usbd_get_no_alts(udev->cdesc, iface->idesc)) 200252190Srpaulo err = USB_ERR_INVAL; 201252190Srpaulo 202252190Srpaulo if (do_unlock) 203252190Srpaulo usbd_enum_unlock(udev); 204252190Srpaulo 205252190Srpaulo return (err); 206252190Srpaulo} 207252190Srpaulo 208252190Srpaulo/*------------------------------------------------------------------------* 209252190Srpaulo * usb_handle_iface_request 210252190Srpaulo * 211252190Srpaulo * Returns: 212252190Srpaulo * 0: Success 213252190Srpaulo * Else: Failure 214252190Srpaulo *------------------------------------------------------------------------*/ 215252190Srpaulostatic usb_error_t 216252190Srpaulousb_handle_iface_request(struct usb_xfer *xfer, 217252190Srpaulo void **ppdata, uint16_t *plen, 218252190Srpaulo struct usb_device_request req, uint16_t off, uint8_t state) 219252190Srpaulo{ 220252190Srpaulo struct usb_interface *iface; 221252190Srpaulo struct usb_interface *iface_parent; /* parent interface */ 222252190Srpaulo struct usb_device *udev = xfer->xroot->udev; 223252190Srpaulo int error; 224252190Srpaulo uint8_t iface_index; 225252190Srpaulo uint8_t temp_state; 226252190Srpaulo 227252190Srpaulo if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { 228252190Srpaulo iface_index = req.wIndex[0]; /* unicast */ 229252190Srpaulo } else { 230252190Srpaulo iface_index = 0; /* broadcast */ 231252190Srpaulo } 232252190Srpaulo 233252190Srpaulo /* 234252190Srpaulo * We need to protect against other threads doing probe and 235252190Srpaulo * attach: 236252190Srpaulo */ 237252190Srpaulo USB_XFER_UNLOCK(xfer); 238252190Srpaulo 239252190Srpaulo usbd_enum_lock(udev); 240252190Srpaulo 241252190Srpaulo error = ENXIO; 242252190Srpaulo 243252190Srpaulotr_repeat: 244252190Srpaulo iface = usbd_get_iface(udev, iface_index); 245252190Srpaulo if ((iface == NULL) || 246252190Srpaulo (iface->idesc == NULL)) { 247252190Srpaulo /* end of interfaces non-existing interface */ 248252190Srpaulo goto tr_stalled; 249252190Srpaulo } 250252190Srpaulo /* set initial state */ 251252190Srpaulo 252252190Srpaulo temp_state = state; 253252190Srpaulo 254252190Srpaulo /* forward request to interface, if any */ 255252190Srpaulo 256252190Srpaulo if ((error != 0) && 257252190Srpaulo (error != ENOTTY) && 258252190Srpaulo (iface->subdev != NULL) && 259252190Srpaulo device_is_attached(iface->subdev)) { 260252190Srpaulo#if 0 261252190Srpaulo DEVMETHOD(usb_handle_request, NULL); /* dummy */ 262252190Srpaulo#endif 263252190Srpaulo error = USB_HANDLE_REQUEST(iface->subdev, 264252190Srpaulo &req, ppdata, plen, 265252190Srpaulo off, &temp_state); 266252190Srpaulo } 267252190Srpaulo iface_parent = usbd_get_iface(udev, iface->parent_iface_index); 268252190Srpaulo 269252190Srpaulo if ((iface_parent == NULL) || 270252190Srpaulo (iface_parent->idesc == NULL)) { 271252190Srpaulo /* non-existing interface */ 272252190Srpaulo iface_parent = NULL; 273252190Srpaulo } 274252190Srpaulo /* forward request to parent interface, if any */ 275252190Srpaulo 276252190Srpaulo if ((error != 0) && 277252190Srpaulo (error != ENOTTY) && 278252190Srpaulo (iface_parent != NULL) && 279252190Srpaulo (iface_parent->subdev != NULL) && 280252190Srpaulo ((req.bmRequestType & 0x1F) == UT_INTERFACE) && 281252190Srpaulo (iface_parent->subdev != iface->subdev) && 282252190Srpaulo device_is_attached(iface_parent->subdev)) { 283252190Srpaulo error = USB_HANDLE_REQUEST(iface_parent->subdev, 284252190Srpaulo &req, ppdata, plen, off, &temp_state); 285252190Srpaulo } 286252190Srpaulo if (error == 0) { 287252190Srpaulo /* negativly adjust pointer and length */ 288252190Srpaulo *ppdata = ((uint8_t *)(*ppdata)) - off; 289252190Srpaulo *plen += off; 290252190Srpaulo 291252190Srpaulo if ((state == USB_HR_NOT_COMPLETE) && 292252190Srpaulo (temp_state == USB_HR_COMPLETE_OK)) 293252190Srpaulo goto tr_short; 294252190Srpaulo else 295252190Srpaulo goto tr_valid; 296252190Srpaulo } else if (error == ENOTTY) { 297252190Srpaulo goto tr_stalled; 298252190Srpaulo } 299252190Srpaulo if ((req.bmRequestType & 0x1F) != UT_INTERFACE) { 300252190Srpaulo iface_index++; /* iterate */ 301252190Srpaulo goto tr_repeat; 302252190Srpaulo } 303252190Srpaulo if (state != USB_HR_NOT_COMPLETE) { 304252190Srpaulo /* we are complete */ 305252190Srpaulo goto tr_valid; 306252190Srpaulo } 307252190Srpaulo switch (req.bmRequestType) { 308252190Srpaulo case UT_WRITE_INTERFACE: 309252190Srpaulo switch (req.bRequest) { 310252190Srpaulo case UR_SET_INTERFACE: 311252190Srpaulo /* 312252190Srpaulo * We assume that the endpoints are the same 313252190Srpaulo * accross the alternate settings. 314252190Srpaulo * 315252190Srpaulo * Reset the endpoints, because re-attaching 316252190Srpaulo * only a part of the device is not possible. 317252190Srpaulo */ 318252190Srpaulo error = usb_check_alt_setting(udev, 319252190Srpaulo iface, req.wValue[0]); 320252190Srpaulo if (error) { 321252190Srpaulo DPRINTF("alt setting does not exist %s\n", 322252190Srpaulo usbd_errstr(error)); 323252190Srpaulo goto tr_stalled; 324252190Srpaulo } 325252190Srpaulo error = usb_reset_iface_endpoints(udev, iface_index); 326252190Srpaulo if (error) { 327252190Srpaulo DPRINTF("alt setting failed %s\n", 328252190Srpaulo usbd_errstr(error)); 329252190Srpaulo goto tr_stalled; 330252190Srpaulo } 331252190Srpaulo /* update the current alternate setting */ 332252190Srpaulo iface->alt_index = req.wValue[0]; 333252190Srpaulo break; 334252190Srpaulo 335252190Srpaulo default: 336252190Srpaulo goto tr_stalled; 337252190Srpaulo } 338252190Srpaulo break; 339252190Srpaulo 340252190Srpaulo case UT_READ_INTERFACE: 341252190Srpaulo switch (req.bRequest) { 342252190Srpaulo case UR_GET_INTERFACE: 343252190Srpaulo *ppdata = &iface->alt_index; 344252190Srpaulo *plen = 1; 345252190Srpaulo break; 346252190Srpaulo 347252190Srpaulo default: 348252190Srpaulo goto tr_stalled; 349252190Srpaulo } 350252190Srpaulo break; 351252190Srpaulo default: 352252190Srpaulo goto tr_stalled; 353252190Srpaulo } 354252190Srpaulotr_valid: 355252190Srpaulo usbd_enum_unlock(udev); 356252190Srpaulo USB_XFER_LOCK(xfer); 357252190Srpaulo return (0); 358252190Srpaulo 359252190Srpaulotr_short: 360252190Srpaulo usbd_enum_unlock(udev); 361252190Srpaulo USB_XFER_LOCK(xfer); 362252190Srpaulo return (USB_ERR_SHORT_XFER); 363252190Srpaulo 364252190Srpaulotr_stalled: 365252190Srpaulo usbd_enum_unlock(udev); 366252190Srpaulo USB_XFER_LOCK(xfer); 367252190Srpaulo return (USB_ERR_STALLED); 368252190Srpaulo} 369252190Srpaulo 370252190Srpaulo/*------------------------------------------------------------------------* 371252190Srpaulo * usb_handle_stall 372252190Srpaulo * 373252190Srpaulo * Returns: 374252190Srpaulo * 0: Success 375252190Srpaulo * Else: Failure 376252190Srpaulo *------------------------------------------------------------------------*/ 377252190Srpaulostatic usb_error_t 378252190Srpaulousb_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall) 379252190Srpaulo{ 380252190Srpaulo struct usb_device *udev = xfer->xroot->udev; 381252190Srpaulo usb_error_t err; 382252190Srpaulo 383252190Srpaulo USB_XFER_UNLOCK(xfer); 384252190Srpaulo err = usbd_set_endpoint_stall(udev, 385252190Srpaulo usbd_get_ep_by_addr(udev, ep), do_stall); 386252190Srpaulo USB_XFER_LOCK(xfer); 387252190Srpaulo return (err); 388252190Srpaulo} 389252190Srpaulo 390252190Srpaulo/*------------------------------------------------------------------------* 391252190Srpaulo * usb_handle_get_stall 392252190Srpaulo * 393252190Srpaulo * Returns: 394252190Srpaulo * 0: Success 395252190Srpaulo * Else: Failure 396252190Srpaulo *------------------------------------------------------------------------*/ 397252190Srpaulostatic uint8_t 398252190Srpaulousb_handle_get_stall(struct usb_device *udev, uint8_t ea_val) 399252190Srpaulo{ 400252190Srpaulo struct usb_endpoint *ep; 401252190Srpaulo uint8_t halted; 402252190Srpaulo 403252190Srpaulo ep = usbd_get_ep_by_addr(udev, ea_val); 404252190Srpaulo if (ep == NULL) { 405252190Srpaulo /* nothing to do */ 406252190Srpaulo return (0); 407252190Srpaulo } 408252190Srpaulo USB_BUS_LOCK(udev->bus); 409252190Srpaulo halted = ep->is_stalled; 410252190Srpaulo USB_BUS_UNLOCK(udev->bus); 411252190Srpaulo 412252190Srpaulo return (halted); 413252190Srpaulo} 414252190Srpaulo 415252190Srpaulo/*------------------------------------------------------------------------* 416252190Srpaulo * usb_handle_remote_wakeup 417252190Srpaulo * 418252190Srpaulo * Returns: 419252190Srpaulo * 0: Success 420252190Srpaulo * Else: Failure 421252190Srpaulo *------------------------------------------------------------------------*/ 422252190Srpaulostatic usb_error_t 423252190Srpaulousb_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on) 424252190Srpaulo{ 425252190Srpaulo struct usb_device *udev; 426252190Srpaulo struct usb_bus *bus; 427252190Srpaulo 428252190Srpaulo udev = xfer->xroot->udev; 429252190Srpaulo bus = udev->bus; 430252190Srpaulo 431252190Srpaulo USB_BUS_LOCK(bus); 432252190Srpaulo 433252190Srpaulo if (is_on) { 434252190Srpaulo udev->flags.remote_wakeup = 1; 435252190Srpaulo } else { 436252190Srpaulo udev->flags.remote_wakeup = 0; 437252190Srpaulo } 438252190Srpaulo 439252190Srpaulo USB_BUS_UNLOCK(bus); 440252190Srpaulo 441252190Srpaulo /* In case we are out of sync, update the power state. */ 442252190Srpaulo usb_bus_power_update(udev->bus); 443252190Srpaulo return (0); /* success */ 444252190Srpaulo} 445252190Srpaulo 446252190Srpaulo/*------------------------------------------------------------------------* 447252190Srpaulo * usb_handle_request 448252190Srpaulo * 449252190Srpaulo * Internal state sequence: 450252190Srpaulo * 451252190Srpaulo * USB_HR_NOT_COMPLETE -> USB_HR_COMPLETE_OK v USB_HR_COMPLETE_ERR 452252190Srpaulo * 453252190Srpaulo * Returns: 454252190Srpaulo * 0: Ready to start hardware 455252190Srpaulo * Else: Stall current transfer, if any 456252190Srpaulo *------------------------------------------------------------------------*/ 457252190Srpaulostatic usb_error_t 458252190Srpaulousb_handle_request(struct usb_xfer *xfer) 459252190Srpaulo{ 460252190Srpaulo struct usb_device_request req; 461252190Srpaulo struct usb_device *udev; 462252190Srpaulo const void *src_zcopy; /* zero-copy source pointer */ 463252190Srpaulo const void *src_mcopy; /* non zero-copy source pointer */ 464252190Srpaulo uint16_t off; /* data offset */ 465252190Srpaulo uint16_t rem; /* data remainder */ 466252190Srpaulo uint16_t max_len; /* max fragment length */ 467252190Srpaulo uint16_t wValue; 468252190Srpaulo uint16_t wIndex; 469252190Srpaulo uint8_t state; 470252190Srpaulo uint8_t is_complete = 1; 471252190Srpaulo usb_error_t err; 472252190Srpaulo union { 473252190Srpaulo uWord wStatus; 474252190Srpaulo uint8_t buf[2]; 475252190Srpaulo } temp; 476252190Srpaulo 477252190Srpaulo /* 478252190Srpaulo * Filter the USB transfer state into 479252190Srpaulo * something which we understand: 480252190Srpaulo */ 481252190Srpaulo 482252190Srpaulo switch (USB_GET_STATE(xfer)) { 483252190Srpaulo case USB_ST_SETUP: 484252190Srpaulo state = USB_HR_NOT_COMPLETE; 485252190Srpaulo 486252190Srpaulo if (!xfer->flags_int.control_act) { 487252190Srpaulo /* nothing to do */ 488252190Srpaulo goto tr_stalled; 489252190Srpaulo } 490252190Srpaulo break; 491252190Srpaulo case USB_ST_TRANSFERRED: 492252190Srpaulo if (!xfer->flags_int.control_act) { 493252190Srpaulo state = USB_HR_COMPLETE_OK; 494252190Srpaulo } else { 495252190Srpaulo state = USB_HR_NOT_COMPLETE; 496252190Srpaulo } 497252190Srpaulo break; 498252190Srpaulo default: 499252190Srpaulo state = USB_HR_COMPLETE_ERR; 500252190Srpaulo break; 501252190Srpaulo } 502252190Srpaulo 503252190Srpaulo /* reset frame stuff */ 504252190Srpaulo 505252190Srpaulo usbd_xfer_set_frame_len(xfer, 0, 0); 506252190Srpaulo 507252190Srpaulo usbd_xfer_set_frame_offset(xfer, 0, 0); 508252190Srpaulo usbd_xfer_set_frame_offset(xfer, sizeof(req), 1); 509252190Srpaulo 510252190Srpaulo /* get the current request, if any */ 511252190Srpaulo 512252190Srpaulo usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); 513252190Srpaulo 514252190Srpaulo if (xfer->flags_int.control_rem == 0xFFFF) { 515252190Srpaulo /* first time - not initialised */ 516252190Srpaulo rem = UGETW(req.wLength); 517252190Srpaulo off = 0; 518252190Srpaulo } else { 519252190Srpaulo /* not first time - initialised */ 520252190Srpaulo rem = xfer->flags_int.control_rem; 521252190Srpaulo off = UGETW(req.wLength) - rem; 522252190Srpaulo } 523252190Srpaulo 524252190Srpaulo /* set some defaults */ 525252190Srpaulo 526252190Srpaulo max_len = 0; 527252190Srpaulo src_zcopy = NULL; 528252190Srpaulo src_mcopy = NULL; 529252190Srpaulo udev = xfer->xroot->udev; 530252190Srpaulo 531252190Srpaulo /* get some request fields decoded */ 532252190Srpaulo 533252190Srpaulo wValue = UGETW(req.wValue); 534252190Srpaulo wIndex = UGETW(req.wIndex); 535252190Srpaulo 536252190Srpaulo DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " 537252190Srpaulo "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, 538252190Srpaulo req.bRequest, wValue, wIndex, off, rem, state); 539252190Srpaulo 540252190Srpaulo /* demultiplex the control request */ 541252190Srpaulo 542252190Srpaulo switch (req.bmRequestType) { 543252190Srpaulo case UT_READ_DEVICE: 544252190Srpaulo if (state != USB_HR_NOT_COMPLETE) { 545252190Srpaulo break; 546252190Srpaulo } 547252190Srpaulo switch (req.bRequest) { 548252190Srpaulo case UR_GET_DESCRIPTOR: 549252190Srpaulo goto tr_handle_get_descriptor; 550252190Srpaulo case UR_GET_CONFIG: 551252190Srpaulo goto tr_handle_get_config; 552252190Srpaulo case UR_GET_STATUS: 553252190Srpaulo goto tr_handle_get_status; 554252190Srpaulo default: 555252190Srpaulo goto tr_stalled; 556252190Srpaulo } 557252190Srpaulo break; 558252190Srpaulo 559252190Srpaulo case UT_WRITE_DEVICE: 560252190Srpaulo switch (req.bRequest) { 561252190Srpaulo case UR_SET_ADDRESS: 562252190Srpaulo goto tr_handle_set_address; 563252190Srpaulo case UR_SET_CONFIG: 564252190Srpaulo goto tr_handle_set_config; 565252190Srpaulo case UR_CLEAR_FEATURE: 566252190Srpaulo switch (wValue) { 567252190Srpaulo case UF_DEVICE_REMOTE_WAKEUP: 568252190Srpaulo goto tr_handle_clear_wakeup; 569252190Srpaulo default: 570252190Srpaulo goto tr_stalled; 571252190Srpaulo } 572252190Srpaulo break; 573252190Srpaulo case UR_SET_FEATURE: 574252190Srpaulo switch (wValue) { 575252190Srpaulo case UF_DEVICE_REMOTE_WAKEUP: 576252190Srpaulo goto tr_handle_set_wakeup; 577252190Srpaulo default: 578252190Srpaulo goto tr_stalled; 579252190Srpaulo } 580252190Srpaulo break; 581252190Srpaulo default: 582252190Srpaulo goto tr_stalled; 583252190Srpaulo } 584252190Srpaulo break; 585252190Srpaulo 586252190Srpaulo case UT_WRITE_ENDPOINT: 587252190Srpaulo switch (req.bRequest) { 588252190Srpaulo case UR_CLEAR_FEATURE: 589252190Srpaulo switch (wValue) { 590252190Srpaulo case UF_ENDPOINT_HALT: 591252190Srpaulo goto tr_handle_clear_halt; 592252190Srpaulo default: 593252190Srpaulo goto tr_stalled; 594252190Srpaulo } 595252190Srpaulo break; 596252190Srpaulo case UR_SET_FEATURE: 597252190Srpaulo switch (wValue) { 598252190Srpaulo case UF_ENDPOINT_HALT: 599252190Srpaulo goto tr_handle_set_halt; 600252190Srpaulo default: 601252190Srpaulo goto tr_stalled; 602252190Srpaulo } 603252190Srpaulo break; 604252190Srpaulo default: 605252190Srpaulo goto tr_stalled; 606252190Srpaulo } 607252190Srpaulo break; 608252190Srpaulo 609252190Srpaulo case UT_READ_ENDPOINT: 610252190Srpaulo switch (req.bRequest) { 611252190Srpaulo case UR_GET_STATUS: 612252190Srpaulo goto tr_handle_get_ep_status; 613252190Srpaulo default: 614252190Srpaulo goto tr_stalled; 615252190Srpaulo } 616252190Srpaulo break; 617252190Srpaulo default: 618252190Srpaulo /* we use "USB_ADD_BYTES" to de-const the src_zcopy */ 619252190Srpaulo err = usb_handle_iface_request(xfer, 620252190Srpaulo USB_ADD_BYTES(&src_zcopy, 0), 621252190Srpaulo &max_len, req, off, state); 622252190Srpaulo if (err == 0) { 623252190Srpaulo is_complete = 0; 624252190Srpaulo goto tr_valid; 625252190Srpaulo } else if (err == USB_ERR_SHORT_XFER) { 626252190Srpaulo goto tr_valid; 627252190Srpaulo } 628252190Srpaulo /* 629252190Srpaulo * Reset zero-copy pointer and max length 630252190Srpaulo * variable in case they were unintentionally 631252190Srpaulo * set: 632252190Srpaulo */ 633252190Srpaulo src_zcopy = NULL; 634252190Srpaulo max_len = 0; 635252190Srpaulo 636252190Srpaulo /* 637252190Srpaulo * Check if we have a vendor specific 638252190Srpaulo * descriptor: 639252190Srpaulo */ 640252190Srpaulo goto tr_handle_get_descriptor; 641252190Srpaulo } 642252190Srpaulo goto tr_valid; 643252190Srpaulo 644252190Srpaulotr_handle_get_descriptor: 645252190Srpaulo err = (usb_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len); 646252190Srpaulo if (err) 647252190Srpaulo goto tr_stalled; 648252190Srpaulo if (src_zcopy == NULL) 649252190Srpaulo goto tr_stalled; 650252190Srpaulo goto tr_valid; 651252190Srpaulo 652252190Srpaulotr_handle_get_config: 653252190Srpaulo temp.buf[0] = udev->curr_config_no; 654252190Srpaulo src_mcopy = temp.buf; 655252190Srpaulo max_len = 1; 656252190Srpaulo goto tr_valid; 657252190Srpaulo 658252190Srpaulotr_handle_get_status: 659252190Srpaulo 660252190Srpaulo wValue = 0; 661252190Srpaulo 662252190Srpaulo USB_BUS_LOCK(udev->bus); 663252190Srpaulo if (udev->flags.remote_wakeup) { 664252190Srpaulo wValue |= UDS_REMOTE_WAKEUP; 665252190Srpaulo } 666252190Srpaulo if (udev->flags.self_powered) { 667252190Srpaulo wValue |= UDS_SELF_POWERED; 668252190Srpaulo } 669252190Srpaulo USB_BUS_UNLOCK(udev->bus); 670252190Srpaulo 671252190Srpaulo USETW(temp.wStatus, wValue); 672252190Srpaulo src_mcopy = temp.wStatus; 673252190Srpaulo max_len = sizeof(temp.wStatus); 674252190Srpaulo goto tr_valid; 675252190Srpaulo 676252190Srpaulotr_handle_set_address: 677252190Srpaulo if (state == USB_HR_NOT_COMPLETE) { 678252190Srpaulo if (wValue >= 0x80) { 679252190Srpaulo /* invalid value */ 680252190Srpaulo goto tr_stalled; 681252190Srpaulo } else if (udev->curr_config_no != 0) { 682252190Srpaulo /* we are configured ! */ 683252190Srpaulo goto tr_stalled; 684252190Srpaulo } 685252190Srpaulo } else if (state != USB_HR_NOT_COMPLETE) { 686252190Srpaulo udev->address = (wValue & 0x7F); 687252190Srpaulo goto tr_bad_context; 688252190Srpaulo } 689252190Srpaulo goto tr_valid; 690252190Srpaulo 691252190Srpaulotr_handle_set_config: 692252190Srpaulo if (state == USB_HR_NOT_COMPLETE) { 693252190Srpaulo if (usb_handle_set_config(xfer, req.wValue[0])) { 694252190Srpaulo goto tr_stalled; 695252190Srpaulo } 696252190Srpaulo } 697252190Srpaulo goto tr_valid; 698252190Srpaulo 699252190Srpaulotr_handle_clear_halt: 700252190Srpaulo if (state == USB_HR_NOT_COMPLETE) { 701252190Srpaulo if (usb_handle_set_stall(xfer, req.wIndex[0], 0)) { 702252190Srpaulo goto tr_stalled; 703252190Srpaulo } 704252190Srpaulo } 705252190Srpaulo goto tr_valid; 706252190Srpaulo 707252190Srpaulotr_handle_clear_wakeup: 708252190Srpaulo if (state == USB_HR_NOT_COMPLETE) { 709252190Srpaulo if (usb_handle_remote_wakeup(xfer, 0)) { 710252190Srpaulo goto tr_stalled; 711252190Srpaulo } 712252190Srpaulo } 713252190Srpaulo goto tr_valid; 714252190Srpaulo 715252190Srpaulotr_handle_set_halt: 716252190Srpaulo if (state == USB_HR_NOT_COMPLETE) { 717252190Srpaulo if (usb_handle_set_stall(xfer, req.wIndex[0], 1)) { 718252190Srpaulo goto tr_stalled; 719252190Srpaulo } 720252190Srpaulo } 721252190Srpaulo goto tr_valid; 722252190Srpaulo 723252190Srpaulotr_handle_set_wakeup: 724252190Srpaulo if (state == USB_HR_NOT_COMPLETE) { 725252190Srpaulo if (usb_handle_remote_wakeup(xfer, 1)) { 726252190Srpaulo goto tr_stalled; 727252190Srpaulo } 728252190Srpaulo } 729252190Srpaulo goto tr_valid; 730252190Srpaulo 731252190Srpaulotr_handle_get_ep_status: 732252190Srpaulo if (state == USB_HR_NOT_COMPLETE) { 733252190Srpaulo temp.wStatus[0] = 734252190Srpaulo usb_handle_get_stall(udev, req.wIndex[0]); 735252190Srpaulo temp.wStatus[1] = 0; 736252190Srpaulo src_mcopy = temp.wStatus; 737252190Srpaulo max_len = sizeof(temp.wStatus); 738252190Srpaulo } 739252190Srpaulo goto tr_valid; 740252190Srpaulo 741252190Srpaulotr_valid: 742252190Srpaulo if (state != USB_HR_NOT_COMPLETE) { 743252190Srpaulo goto tr_stalled; 744252190Srpaulo } 745252190Srpaulo /* subtract offset from length */ 746252190Srpaulo 747252190Srpaulo max_len -= off; 748252190Srpaulo 749252190Srpaulo /* Compute the real maximum data length */ 750252190Srpaulo 751252190Srpaulo if (max_len > xfer->max_data_length) { 752252190Srpaulo max_len = usbd_xfer_max_len(xfer); 753252190Srpaulo } 754252190Srpaulo if (max_len > rem) { 755252190Srpaulo max_len = rem; 756252190Srpaulo } 757252190Srpaulo /* 758252190Srpaulo * If the remainder is greater than the maximum data length, 759252190Srpaulo * we need to truncate the value for the sake of the 760252190Srpaulo * comparison below: 761252190Srpaulo */ 762252190Srpaulo if (rem > xfer->max_data_length) { 763252190Srpaulo rem = usbd_xfer_max_len(xfer); 764252190Srpaulo } 765252190Srpaulo if ((rem != max_len) && (is_complete != 0)) { 766252190Srpaulo /* 767252190Srpaulo * If we don't transfer the data we can transfer, then 768252190Srpaulo * the transfer is short ! 769252190Srpaulo */ 770252190Srpaulo xfer->flags.force_short_xfer = 1; 771252190Srpaulo xfer->nframes = 2; 772252190Srpaulo } else { 773252190Srpaulo /* 774252190Srpaulo * Default case 775252190Srpaulo */ 776252190Srpaulo xfer->flags.force_short_xfer = 0; 777252190Srpaulo xfer->nframes = max_len ? 2 : 1; 778252190Srpaulo } 779252190Srpaulo if (max_len > 0) { 780252190Srpaulo if (src_mcopy) { 781252190Srpaulo src_mcopy = USB_ADD_BYTES(src_mcopy, off); 782252190Srpaulo usbd_copy_in(xfer->frbuffers + 1, 0, 783252190Srpaulo src_mcopy, max_len); 784252190Srpaulo usbd_xfer_set_frame_len(xfer, 1, max_len); 785252190Srpaulo } else { 786252190Srpaulo usbd_xfer_set_frame_data(xfer, 1, 787252190Srpaulo USB_ADD_BYTES(src_zcopy, off), max_len); 788252190Srpaulo } 789252190Srpaulo } else { 790252190Srpaulo /* the end is reached, send status */ 791252190Srpaulo xfer->flags.manual_status = 0; 792252190Srpaulo usbd_xfer_set_frame_len(xfer, 1, 0); 793252190Srpaulo } 794252190Srpaulo DPRINTF("success\n"); 795252190Srpaulo return (0); /* success */ 796252190Srpaulo 797252190Srpaulotr_stalled: 798252190Srpaulo DPRINTF("%s\n", (state != USB_HR_NOT_COMPLETE) ? 799252190Srpaulo "complete" : "stalled"); 800252190Srpaulo return (USB_ERR_STALLED); 801252190Srpaulo 802252190Srpaulotr_bad_context: 803252190Srpaulo DPRINTF("bad context\n"); 804252190Srpaulo return (USB_ERR_BAD_CONTEXT); 805252190Srpaulo} 806252190Srpaulo