usb_handle_request.c revision 193045
1214501Srpaulo/* $FreeBSD: head/sys/dev/usb/usb_handle_request.c 193045 2009-05-29 18:46:57Z thompsa $ */ 2214501Srpaulo/*- 3214501Srpaulo * Copyright (c) 2008 Hans Petter Selasky. All rights reserved. 4214501Srpaulo * 5252190Srpaulo * Redistribution and use in source and binary forms, with or without 6252190Srpaulo * modification, are permitted provided that the following conditions 7214501Srpaulo * are met: 8214501Srpaulo * 1. Redistributions of source code must retain the above copyright 9214501Srpaulo * notice, this list of conditions and the following disclaimer. 10214501Srpaulo * 2. Redistributions in binary form must reproduce the above copyright 11214501Srpaulo * notice, this list of conditions and the following disclaimer in the 12214501Srpaulo * documentation and/or other materials provided with the distribution. 13214501Srpaulo * 14214501Srpaulo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15214501Srpaulo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16214501Srpaulo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17214501Srpaulo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18214501Srpaulo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19214501Srpaulo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20214501Srpaulo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21214501Srpaulo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22214501Srpaulo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23214501Srpaulo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24214501Srpaulo * SUCH DAMAGE. 25214501Srpaulo */ 26214501Srpaulo 27214501Srpaulo#include <dev/usb/usb_mfunc.h> 28214501Srpaulo#include <dev/usb/usb_error.h> 29214501Srpaulo#include <dev/usb/usb.h> 30214501Srpaulo 31214501Srpaulo#define USB_DEBUG_VAR usb2_debug 32214501Srpaulo 33214501Srpaulo#include <dev/usb/usb_core.h> 34214501Srpaulo#include <dev/usb/usb_process.h> 35214501Srpaulo#include <dev/usb/usb_busdma.h> 36214501Srpaulo#include <dev/usb/usb_transfer.h> 37214501Srpaulo#include <dev/usb/usb_device.h> 38214501Srpaulo#include <dev/usb/usb_debug.h> 39214501Srpaulo#include <dev/usb/usb_dynamic.h> 40214501Srpaulo#include <dev/usb/usb_hub.h> 41214501Srpaulo 42214501Srpaulo#include <dev/usb/usb_controller.h> 43214501Srpaulo#include <dev/usb/usb_bus.h> 44214501Srpaulo 45214501Srpaulo/* enum */ 46214501Srpaulo 47214501Srpauloenum { 48214501Srpaulo ST_DATA, 49214501Srpaulo ST_POST_STATUS, 50214501Srpaulo}; 51214501Srpaulo 52214501Srpaulo/* function prototypes */ 53214501Srpaulo 54214501Srpaulostatic uint8_t usb2_handle_get_stall(struct usb_device *, uint8_t); 55214501Srpaulostatic usb_error_t usb2_handle_remote_wakeup(struct usb_xfer *, uint8_t); 56214501Srpaulostatic usb_error_t usb2_handle_request(struct usb_xfer *); 57214501Srpaulostatic usb_error_t usb2_handle_set_config(struct usb_xfer *, uint8_t); 58214501Srpaulostatic usb_error_t usb2_handle_set_stall(struct usb_xfer *, uint8_t, 59214501Srpaulo uint8_t); 60214501Srpaulostatic usb_error_t usb2_handle_iface_request(struct usb_xfer *, void **, 61214501Srpaulo uint16_t *, struct usb_device_request, uint16_t, 62214501Srpaulo uint8_t); 63214501Srpaulo 64214501Srpaulo/*------------------------------------------------------------------------* 65214501Srpaulo * usb2_handle_request_callback 66214501Srpaulo * 67214501Srpaulo * This function is the USB callback for generic USB Device control 68214501Srpaulo * transfers. 69214501Srpaulo *------------------------------------------------------------------------*/ 70214501Srpaulovoid 71214501Srpaulousb2_handle_request_callback(struct usb_xfer *xfer) 72214501Srpaulo{ 73214501Srpaulo usb_error_t err; 74214501Srpaulo 75214501Srpaulo /* check the current transfer state */ 76214501Srpaulo 77214501Srpaulo switch (USB_GET_STATE(xfer)) { 78214501Srpaulo case USB_ST_SETUP: 79214501Srpaulo case USB_ST_TRANSFERRED: 80214501Srpaulo 81214501Srpaulo /* handle the request */ 82214501Srpaulo err = usb2_handle_request(xfer); 83214501Srpaulo 84214501Srpaulo if (err) { 85214501Srpaulo 86214501Srpaulo if (err == USB_ERR_BAD_CONTEXT) { 87214501Srpaulo /* we need to re-setup the control transfer */ 88214501Srpaulo usb2_needs_explore(xfer->xroot->bus, 0); 89214501Srpaulo break; 90214501Srpaulo } 91214501Srpaulo /* 92214501Srpaulo * If no control transfer is active, 93214501Srpaulo * receive the next SETUP message: 94214501Srpaulo */ 95214501Srpaulo goto tr_restart; 96214501Srpaulo } 97214501Srpaulo usb2_start_hardware(xfer); 98214501Srpaulo break; 99214501Srpaulo 100214501Srpaulo default: 101214501Srpaulo if (xfer->error != USB_ERR_CANCELLED) { 102214501Srpaulo /* should not happen - try stalling */ 103214501Srpaulo goto tr_restart; 104214501Srpaulo } 105214501Srpaulo break; 106214501Srpaulo } 107214501Srpaulo return; 108214501Srpaulo 109214501Srpaulotr_restart: 110214501Srpaulo xfer->frlengths[0] = sizeof(struct usb_device_request); 111214501Srpaulo xfer->nframes = 1; 112214501Srpaulo xfer->flags.manual_status = 1; 113252190Srpaulo xfer->flags.force_short_xfer = 0; 114214501Srpaulo xfer->flags.stall_pipe = 1; /* cancel previous transfer, if any */ 115214501Srpaulo usb2_start_hardware(xfer); 116214501Srpaulo} 117214501Srpaulo 118214501Srpaulo/*------------------------------------------------------------------------* 119214501Srpaulo * usb2_handle_set_config 120214501Srpaulo * 121214501Srpaulo * Returns: 122214501Srpaulo * 0: Success 123214501Srpaulo * Else: Failure 124214501Srpaulo *------------------------------------------------------------------------*/ 125214501Srpaulostatic usb_error_t 126214501Srpaulousb2_handle_set_config(struct usb_xfer *xfer, uint8_t conf_no) 127214501Srpaulo{ 128214501Srpaulo struct usb_device *udev = xfer->xroot->udev; 129214501Srpaulo usb_error_t err = 0; 130214501Srpaulo 131214501Srpaulo /* 132214501Srpaulo * We need to protect against other threads doing probe and 133214501Srpaulo * attach: 134214501Srpaulo */ 135214501Srpaulo USB_XFER_UNLOCK(xfer); 136214501Srpaulo mtx_lock(&Giant); /* XXX */ 137214501Srpaulo sx_xlock(udev->default_sx + 1); 138214501Srpaulo 139214501Srpaulo if (conf_no == USB_UNCONFIG_NO) { 140214501Srpaulo conf_no = USB_UNCONFIG_INDEX; 141214501Srpaulo } else { 142214501Srpaulo /* 143214501Srpaulo * The relationship between config number and config index 144214501Srpaulo * is very simple in our case: 145214501Srpaulo */ 146214501Srpaulo conf_no--; 147214501Srpaulo } 148214501Srpaulo 149214501Srpaulo if (usb2_set_config_index(udev, conf_no)) { 150214501Srpaulo DPRINTF("set config %d failed\n", conf_no); 151214501Srpaulo err = USB_ERR_STALLED; 152214501Srpaulo goto done; 153214501Srpaulo } 154214501Srpaulo if (usb2_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) { 155214501Srpaulo DPRINTF("probe and attach failed\n"); 156214501Srpaulo err = USB_ERR_STALLED; 157214501Srpaulo goto done; 158214501Srpaulo } 159214501Srpaulodone: 160214501Srpaulo mtx_unlock(&Giant); /* XXX */ 161214501Srpaulo sx_unlock(udev->default_sx + 1); 162214501Srpaulo USB_XFER_LOCK(xfer); 163214501Srpaulo return (err); 164214501Srpaulo} 165214501Srpaulo 166214501Srpaulo/*------------------------------------------------------------------------* 167214501Srpaulo * usb2_handle_iface_request 168214501Srpaulo * 169214501Srpaulo * Returns: 170214501Srpaulo * 0: Success 171214501Srpaulo * Else: Failure 172214501Srpaulo *------------------------------------------------------------------------*/ 173214501Srpaulostatic usb_error_t 174214501Srpaulousb2_handle_iface_request(struct usb_xfer *xfer, 175214501Srpaulo void **ppdata, uint16_t *plen, 176214501Srpaulo struct usb_device_request req, uint16_t off, uint8_t state) 177214501Srpaulo{ 178214501Srpaulo struct usb_interface *iface; 179214501Srpaulo struct usb_interface *iface_parent; /* parent interface */ 180214501Srpaulo struct usb_device *udev = xfer->xroot->udev; 181214501Srpaulo int error; 182214501Srpaulo uint8_t iface_index; 183214501Srpaulo 184214501Srpaulo if ((req.bmRequestType & 0x1F) == UT_INTERFACE) { 185214501Srpaulo iface_index = req.wIndex[0]; /* unicast */ 186214501Srpaulo } else { 187214501Srpaulo iface_index = 0; /* broadcast */ 188214501Srpaulo } 189214501Srpaulo 190214501Srpaulo /* 191214501Srpaulo * We need to protect against other threads doing probe and 192214501Srpaulo * attach: 193214501Srpaulo */ 194214501Srpaulo USB_XFER_UNLOCK(xfer); 195214501Srpaulo mtx_lock(&Giant); /* XXX */ 196214501Srpaulo sx_xlock(udev->default_sx + 1); 197214501Srpaulo 198214501Srpaulo error = ENXIO; 199214501Srpaulo 200214501Srpaulotr_repeat: 201214501Srpaulo iface = usb2_get_iface(udev, iface_index); 202214501Srpaulo if ((iface == NULL) || 203214501Srpaulo (iface->idesc == NULL)) { 204214501Srpaulo /* end of interfaces non-existing interface */ 205214501Srpaulo goto tr_stalled; 206214501Srpaulo } 207214501Srpaulo /* forward request to interface, if any */ 208214501Srpaulo 209214501Srpaulo if ((error != 0) && 210214501Srpaulo (error != ENOTTY) && 211214501Srpaulo (iface->subdev != NULL) && 212214501Srpaulo device_is_attached(iface->subdev)) { 213214501Srpaulo#if 0 214214501Srpaulo DEVMETHOD(usb_handle_request, NULL); /* dummy */ 215214501Srpaulo#endif 216214501Srpaulo error = USB_HANDLE_REQUEST(iface->subdev, 217214501Srpaulo &req, ppdata, plen, 218214501Srpaulo off, (state == ST_POST_STATUS)); 219214501Srpaulo } 220214501Srpaulo iface_parent = usb2_get_iface(udev, iface->parent_iface_index); 221214501Srpaulo 222214501Srpaulo if ((iface_parent == NULL) || 223214501Srpaulo (iface_parent->idesc == NULL)) { 224214501Srpaulo /* non-existing interface */ 225214501Srpaulo iface_parent = NULL; 226252190Srpaulo } 227214501Srpaulo /* forward request to parent interface, if any */ 228214501Srpaulo 229214501Srpaulo if ((error != 0) && 230214501Srpaulo (error != ENOTTY) && 231214501Srpaulo (iface_parent != NULL) && 232214501Srpaulo (iface_parent->subdev != NULL) && 233214501Srpaulo ((req.bmRequestType & 0x1F) == UT_INTERFACE) && 234214501Srpaulo (iface_parent->subdev != iface->subdev) && 235214501Srpaulo device_is_attached(iface_parent->subdev)) { 236214501Srpaulo error = USB_HANDLE_REQUEST(iface_parent->subdev, 237214501Srpaulo &req, ppdata, plen, off, 238214501Srpaulo (state == ST_POST_STATUS)); 239214501Srpaulo } 240214501Srpaulo if (error == 0) { 241214501Srpaulo /* negativly adjust pointer and length */ 242214501Srpaulo *ppdata = ((uint8_t *)(*ppdata)) - off; 243214501Srpaulo *plen += off; 244214501Srpaulo goto tr_valid; 245214501Srpaulo } else if (error == ENOTTY) { 246214501Srpaulo goto tr_stalled; 247214501Srpaulo } 248214501Srpaulo if ((req.bmRequestType & 0x1F) != UT_INTERFACE) { 249214501Srpaulo iface_index++; /* iterate */ 250214501Srpaulo goto tr_repeat; 251214501Srpaulo } 252214501Srpaulo if (state == ST_POST_STATUS) { 253214501Srpaulo /* we are complete */ 254214501Srpaulo goto tr_valid; 255214501Srpaulo } 256214501Srpaulo switch (req.bmRequestType) { 257214501Srpaulo case UT_WRITE_INTERFACE: 258214501Srpaulo switch (req.bRequest) { 259214501Srpaulo case UR_SET_INTERFACE: 260214501Srpaulo /* 261214501Srpaulo * Handle special case. If we have parent interface 262214501Srpaulo * we just reset the endpoints, because this is a 263214501Srpaulo * multi interface device and re-attaching only a 264214501Srpaulo * part of the device is not possible. Also if the 265214501Srpaulo * alternate setting is the same like before we just 266214501Srpaulo * reset the interface endoints. 267214501Srpaulo */ 268214501Srpaulo if ((iface_parent != NULL) || 269214501Srpaulo (iface->alt_index == req.wValue[0])) { 270214501Srpaulo error = usb2_reset_iface_endpoints(udev, 271214501Srpaulo iface_index); 272214501Srpaulo if (error) { 273214501Srpaulo DPRINTF("alt setting failed %s\n", 274214501Srpaulo usb2_errstr(error)); 275214501Srpaulo goto tr_stalled; 276214501Srpaulo } 277214501Srpaulo break; 278214501Srpaulo } 279214501Srpaulo /* 280214501Srpaulo * Doing the alternate setting will detach the 281214501Srpaulo * interface aswell: 282214501Srpaulo */ 283214501Srpaulo error = usb2_set_alt_interface_index(udev, 284214501Srpaulo iface_index, req.wValue[0]); 285214501Srpaulo if (error) { 286214501Srpaulo DPRINTF("alt setting failed %s\n", 287214501Srpaulo usb2_errstr(error)); 288214501Srpaulo goto tr_stalled; 289214501Srpaulo } 290214501Srpaulo error = usb2_probe_and_attach(udev, 291214501Srpaulo iface_index); 292214501Srpaulo if (error) { 293214501Srpaulo DPRINTF("alt setting probe failed\n"); 294214501Srpaulo goto tr_stalled; 295214501Srpaulo } 296214501Srpaulo break; 297214501Srpaulo default: 298214501Srpaulo goto tr_stalled; 299214501Srpaulo } 300214501Srpaulo break; 301214501Srpaulo 302214501Srpaulo case UT_READ_INTERFACE: 303214501Srpaulo switch (req.bRequest) { 304214501Srpaulo case UR_GET_INTERFACE: 305214501Srpaulo *ppdata = &iface->alt_index; 306214501Srpaulo *plen = 1; 307214501Srpaulo break; 308214501Srpaulo 309214501Srpaulo default: 310214501Srpaulo goto tr_stalled; 311214501Srpaulo } 312214501Srpaulo break; 313214501Srpaulo default: 314214501Srpaulo goto tr_stalled; 315214501Srpaulo } 316214501Srpaulotr_valid: 317214501Srpaulo mtx_unlock(&Giant); 318214501Srpaulo sx_unlock(udev->default_sx + 1); 319214501Srpaulo USB_XFER_LOCK(xfer); 320214501Srpaulo return (0); 321214501Srpaulo 322214501Srpaulotr_stalled: 323214501Srpaulo mtx_unlock(&Giant); 324214501Srpaulo sx_unlock(udev->default_sx + 1); 325214501Srpaulo USB_XFER_LOCK(xfer); 326214501Srpaulo return (USB_ERR_STALLED); 327214501Srpaulo} 328214501Srpaulo 329214501Srpaulo/*------------------------------------------------------------------------* 330214501Srpaulo * usb2_handle_stall 331214501Srpaulo * 332214501Srpaulo * Returns: 333214501Srpaulo * 0: Success 334252190Srpaulo * Else: Failure 335214501Srpaulo *------------------------------------------------------------------------*/ 336214501Srpaulostatic usb_error_t 337214501Srpaulousb2_handle_set_stall(struct usb_xfer *xfer, uint8_t ep, uint8_t do_stall) 338214501Srpaulo{ 339214501Srpaulo struct usb_device *udev = xfer->xroot->udev; 340214501Srpaulo usb_error_t err; 341214501Srpaulo 342214501Srpaulo USB_XFER_UNLOCK(xfer); 343214501Srpaulo err = usb2_set_endpoint_stall(udev, 344214501Srpaulo usb2_get_pipe_by_addr(udev, ep), do_stall); 345214501Srpaulo USB_XFER_LOCK(xfer); 346214501Srpaulo return (err); 347214501Srpaulo} 348214501Srpaulo 349214501Srpaulo/*------------------------------------------------------------------------* 350214501Srpaulo * usb2_handle_get_stall 351214501Srpaulo * 352214501Srpaulo * Returns: 353214501Srpaulo * 0: Success 354214501Srpaulo * Else: Failure 355214501Srpaulo *------------------------------------------------------------------------*/ 356214501Srpaulostatic uint8_t 357214501Srpaulousb2_handle_get_stall(struct usb_device *udev, uint8_t ea_val) 358214501Srpaulo{ 359214501Srpaulo struct usb_pipe *pipe; 360214501Srpaulo uint8_t halted; 361214501Srpaulo 362214501Srpaulo pipe = usb2_get_pipe_by_addr(udev, ea_val); 363214501Srpaulo if (pipe == NULL) { 364214501Srpaulo /* nothing to do */ 365214501Srpaulo return (0); 366214501Srpaulo } 367214501Srpaulo USB_BUS_LOCK(udev->bus); 368214501Srpaulo halted = pipe->is_stalled; 369214501Srpaulo USB_BUS_UNLOCK(udev->bus); 370214501Srpaulo 371214501Srpaulo return (halted); 372214501Srpaulo} 373214501Srpaulo 374214501Srpaulo/*------------------------------------------------------------------------* 375214501Srpaulo * usb2_handle_remote_wakeup 376214501Srpaulo * 377214501Srpaulo * Returns: 378214501Srpaulo * 0: Success 379214501Srpaulo * Else: Failure 380214501Srpaulo *------------------------------------------------------------------------*/ 381214501Srpaulostatic usb_error_t 382214501Srpaulousb2_handle_remote_wakeup(struct usb_xfer *xfer, uint8_t is_on) 383214501Srpaulo{ 384214501Srpaulo struct usb_device *udev; 385214501Srpaulo struct usb_bus *bus; 386214501Srpaulo 387214501Srpaulo udev = xfer->xroot->udev; 388214501Srpaulo bus = udev->bus; 389214501Srpaulo 390214501Srpaulo USB_BUS_LOCK(bus); 391214501Srpaulo 392214501Srpaulo if (is_on) { 393214501Srpaulo udev->flags.remote_wakeup = 1; 394214501Srpaulo } else { 395214501Srpaulo udev->flags.remote_wakeup = 0; 396214501Srpaulo } 397214501Srpaulo 398214501Srpaulo USB_BUS_UNLOCK(bus); 399214501Srpaulo 400214501Srpaulo /* In case we are out of sync, update the power state. */ 401214501Srpaulo usb2_bus_power_update(udev->bus); 402214501Srpaulo return (0); /* success */ 403214501Srpaulo} 404214501Srpaulo 405214501Srpaulo/*------------------------------------------------------------------------* 406214501Srpaulo * usb2_handle_request 407214501Srpaulo * 408214501Srpaulo * Internal state sequence: 409214501Srpaulo * 410214501Srpaulo * ST_DATA -> ST_POST_STATUS 411214501Srpaulo * 412214501Srpaulo * Returns: 413214501Srpaulo * 0: Ready to start hardware 414214501Srpaulo * Else: Stall current transfer, if any 415214501Srpaulo *------------------------------------------------------------------------*/ 416214501Srpaulostatic usb_error_t 417214501Srpaulousb2_handle_request(struct usb_xfer *xfer) 418214501Srpaulo{ 419214501Srpaulo struct usb_device_request req; 420214501Srpaulo struct usb_device *udev; 421214501Srpaulo const void *src_zcopy; /* zero-copy source pointer */ 422214501Srpaulo const void *src_mcopy; /* non zero-copy source pointer */ 423214501Srpaulo uint16_t off; /* data offset */ 424214501Srpaulo uint16_t rem; /* data remainder */ 425214501Srpaulo uint16_t max_len; /* max fragment length */ 426214501Srpaulo uint16_t wValue; 427214501Srpaulo uint16_t wIndex; 428214501Srpaulo uint8_t state; 429214501Srpaulo usb_error_t err; 430214501Srpaulo union { 431214501Srpaulo uWord wStatus; 432214501Srpaulo uint8_t buf[2]; 433214501Srpaulo } temp; 434214501Srpaulo 435214501Srpaulo /* 436214501Srpaulo * Filter the USB transfer state into 437214501Srpaulo * something which we understand: 438214501Srpaulo */ 439214501Srpaulo 440214501Srpaulo switch (USB_GET_STATE(xfer)) { 441214501Srpaulo case USB_ST_SETUP: 442214501Srpaulo state = ST_DATA; 443214501Srpaulo 444214501Srpaulo if (!xfer->flags_int.control_act) { 445214501Srpaulo /* nothing to do */ 446214501Srpaulo goto tr_stalled; 447214501Srpaulo } 448214501Srpaulo break; 449214501Srpaulo 450214501Srpaulo default: /* USB_ST_TRANSFERRED */ 451214501Srpaulo if (!xfer->flags_int.control_act) { 452214501Srpaulo state = ST_POST_STATUS; 453214501Srpaulo } else { 454214501Srpaulo state = ST_DATA; 455214501Srpaulo } 456214501Srpaulo break; 457214501Srpaulo } 458214501Srpaulo 459214501Srpaulo /* reset frame stuff */ 460214501Srpaulo 461214501Srpaulo xfer->frlengths[0] = 0; 462214501Srpaulo 463214501Srpaulo usb2_set_frame_offset(xfer, 0, 0); 464214501Srpaulo usb2_set_frame_offset(xfer, sizeof(req), 1); 465214501Srpaulo 466214501Srpaulo /* get the current request, if any */ 467214501Srpaulo 468214501Srpaulo usb2_copy_out(xfer->frbuffers, 0, &req, sizeof(req)); 469214501Srpaulo 470214501Srpaulo if (xfer->flags_int.control_rem == 0xFFFF) { 471214501Srpaulo /* first time - not initialised */ 472214501Srpaulo rem = UGETW(req.wLength); 473214501Srpaulo off = 0; 474214501Srpaulo } else { 475214501Srpaulo /* not first time - initialised */ 476214501Srpaulo rem = xfer->flags_int.control_rem; 477214501Srpaulo off = UGETW(req.wLength) - rem; 478214501Srpaulo } 479214501Srpaulo 480214501Srpaulo /* set some defaults */ 481214501Srpaulo 482214501Srpaulo max_len = 0; 483214501Srpaulo src_zcopy = NULL; 484214501Srpaulo src_mcopy = NULL; 485214501Srpaulo udev = xfer->xroot->udev; 486214501Srpaulo 487214501Srpaulo /* get some request fields decoded */ 488214501Srpaulo 489214501Srpaulo wValue = UGETW(req.wValue); 490214501Srpaulo wIndex = UGETW(req.wIndex); 491214501Srpaulo 492214501Srpaulo DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x " 493214501Srpaulo "off=0x%x rem=0x%x, state=%d\n", req.bmRequestType, 494214501Srpaulo req.bRequest, wValue, wIndex, off, rem, state); 495214501Srpaulo 496214501Srpaulo /* demultiplex the control request */ 497214501Srpaulo 498214501Srpaulo switch (req.bmRequestType) { 499214501Srpaulo case UT_READ_DEVICE: 500214501Srpaulo if (state != ST_DATA) { 501214501Srpaulo break; 502214501Srpaulo } 503214501Srpaulo switch (req.bRequest) { 504214501Srpaulo case UR_GET_DESCRIPTOR: 505214501Srpaulo goto tr_handle_get_descriptor; 506214501Srpaulo case UR_GET_CONFIG: 507214501Srpaulo goto tr_handle_get_config; 508214501Srpaulo case UR_GET_STATUS: 509214501Srpaulo goto tr_handle_get_status; 510214501Srpaulo default: 511214501Srpaulo goto tr_stalled; 512214501Srpaulo } 513214501Srpaulo break; 514214501Srpaulo 515214501Srpaulo case UT_WRITE_DEVICE: 516214501Srpaulo switch (req.bRequest) { 517214501Srpaulo case UR_SET_ADDRESS: 518214501Srpaulo goto tr_handle_set_address; 519214501Srpaulo case UR_SET_CONFIG: 520214501Srpaulo goto tr_handle_set_config; 521214501Srpaulo case UR_CLEAR_FEATURE: 522214501Srpaulo switch (wValue) { 523214501Srpaulo case UF_DEVICE_REMOTE_WAKEUP: 524214501Srpaulo goto tr_handle_clear_wakeup; 525214501Srpaulo default: 526214501Srpaulo goto tr_stalled; 527214501Srpaulo } 528214501Srpaulo break; 529214501Srpaulo case UR_SET_FEATURE: 530214501Srpaulo switch (wValue) { 531214501Srpaulo case UF_DEVICE_REMOTE_WAKEUP: 532214501Srpaulo goto tr_handle_set_wakeup; 533214501Srpaulo default: 534214501Srpaulo goto tr_stalled; 535214501Srpaulo } 536214501Srpaulo break; 537214501Srpaulo default: 538214501Srpaulo goto tr_stalled; 539214501Srpaulo } 540214501Srpaulo break; 541214501Srpaulo 542214501Srpaulo case UT_WRITE_ENDPOINT: 543214501Srpaulo switch (req.bRequest) { 544214501Srpaulo case UR_CLEAR_FEATURE: 545214501Srpaulo switch (wValue) { 546214501Srpaulo case UF_ENDPOINT_HALT: 547214501Srpaulo goto tr_handle_clear_halt; 548214501Srpaulo default: 549214501Srpaulo goto tr_stalled; 550214501Srpaulo } 551214501Srpaulo break; 552214501Srpaulo case UR_SET_FEATURE: 553214501Srpaulo switch (wValue) { 554214501Srpaulo case UF_ENDPOINT_HALT: 555214501Srpaulo goto tr_handle_set_halt; 556214501Srpaulo default: 557214501Srpaulo goto tr_stalled; 558214501Srpaulo } 559214501Srpaulo break; 560214501Srpaulo default: 561214501Srpaulo goto tr_stalled; 562214501Srpaulo } 563214501Srpaulo break; 564214501Srpaulo 565214501Srpaulo case UT_READ_ENDPOINT: 566214501Srpaulo switch (req.bRequest) { 567214501Srpaulo case UR_GET_STATUS: 568214501Srpaulo goto tr_handle_get_ep_status; 569214501Srpaulo default: 570214501Srpaulo goto tr_stalled; 571214501Srpaulo } 572214501Srpaulo break; 573214501Srpaulo default: 574214501Srpaulo /* we use "USB_ADD_BYTES" to de-const the src_zcopy */ 575214501Srpaulo err = usb2_handle_iface_request(xfer, 576214501Srpaulo USB_ADD_BYTES(&src_zcopy, 0), 577214501Srpaulo &max_len, req, off, state); 578214501Srpaulo if (err == 0) { 579214501Srpaulo goto tr_valid; 580214501Srpaulo } 581214501Srpaulo /* 582214501Srpaulo * Reset zero-copy pointer and max length 583214501Srpaulo * variable in case they were unintentionally 584214501Srpaulo * set: 585214501Srpaulo */ 586214501Srpaulo src_zcopy = NULL; 587214501Srpaulo max_len = 0; 588214501Srpaulo 589214501Srpaulo /* 590214501Srpaulo * Check if we have a vendor specific 591214501Srpaulo * descriptor: 592214501Srpaulo */ 593214501Srpaulo goto tr_handle_get_descriptor; 594214501Srpaulo } 595214501Srpaulo goto tr_valid; 596214501Srpaulo 597214501Srpaulotr_handle_get_descriptor: 598214501Srpaulo err = (usb2_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len); 599214501Srpaulo if (err) 600214501Srpaulo goto tr_stalled; 601214501Srpaulo if (src_zcopy == NULL) 602214501Srpaulo goto tr_stalled; 603214501Srpaulo goto tr_valid; 604214501Srpaulo 605214501Srpaulotr_handle_get_config: 606214501Srpaulo temp.buf[0] = udev->curr_config_no; 607214501Srpaulo src_mcopy = temp.buf; 608214501Srpaulo max_len = 1; 609214501Srpaulo goto tr_valid; 610214501Srpaulo 611214501Srpaulotr_handle_get_status: 612214501Srpaulo 613214501Srpaulo wValue = 0; 614214501Srpaulo 615214501Srpaulo USB_BUS_LOCK(udev->bus); 616214501Srpaulo if (udev->flags.remote_wakeup) { 617214501Srpaulo wValue |= UDS_REMOTE_WAKEUP; 618214501Srpaulo } 619214501Srpaulo if (udev->flags.self_powered) { 620214501Srpaulo wValue |= UDS_SELF_POWERED; 621214501Srpaulo } 622214501Srpaulo USB_BUS_UNLOCK(udev->bus); 623214501Srpaulo 624214501Srpaulo USETW(temp.wStatus, wValue); 625214501Srpaulo src_mcopy = temp.wStatus; 626214501Srpaulo max_len = sizeof(temp.wStatus); 627214501Srpaulo goto tr_valid; 628214501Srpaulo 629214501Srpaulotr_handle_set_address: 630214501Srpaulo if (state == ST_DATA) { 631214501Srpaulo if (wValue >= 0x80) { 632214501Srpaulo /* invalid value */ 633214501Srpaulo goto tr_stalled; 634214501Srpaulo } else if (udev->curr_config_no != 0) { 635214501Srpaulo /* we are configured ! */ 636214501Srpaulo goto tr_stalled; 637214501Srpaulo } 638214501Srpaulo } else if (state == ST_POST_STATUS) { 639214501Srpaulo udev->address = (wValue & 0x7F); 640214501Srpaulo goto tr_bad_context; 641214501Srpaulo } 642214501Srpaulo goto tr_valid; 643214501Srpaulo 644214501Srpaulotr_handle_set_config: 645214501Srpaulo if (state == ST_DATA) { 646214501Srpaulo if (usb2_handle_set_config(xfer, req.wValue[0])) { 647214501Srpaulo goto tr_stalled; 648214501Srpaulo } 649214501Srpaulo } 650214501Srpaulo goto tr_valid; 651214501Srpaulo 652214501Srpaulotr_handle_clear_halt: 653214501Srpaulo if (state == ST_DATA) { 654214501Srpaulo if (usb2_handle_set_stall(xfer, req.wIndex[0], 0)) { 655214501Srpaulo goto tr_stalled; 656214501Srpaulo } 657214501Srpaulo } 658214501Srpaulo goto tr_valid; 659214501Srpaulo 660214501Srpaulotr_handle_clear_wakeup: 661214501Srpaulo if (state == ST_DATA) { 662214501Srpaulo if (usb2_handle_remote_wakeup(xfer, 0)) { 663214501Srpaulo goto tr_stalled; 664214501Srpaulo } 665214501Srpaulo } 666214501Srpaulo goto tr_valid; 667214501Srpaulo 668214501Srpaulotr_handle_set_halt: 669214501Srpaulo if (state == ST_DATA) { 670214501Srpaulo if (usb2_handle_set_stall(xfer, req.wIndex[0], 1)) { 671214501Srpaulo goto tr_stalled; 672214501Srpaulo } 673214501Srpaulo } 674214501Srpaulo goto tr_valid; 675214501Srpaulo 676214501Srpaulotr_handle_set_wakeup: 677214501Srpaulo if (state == ST_DATA) { 678214501Srpaulo if (usb2_handle_remote_wakeup(xfer, 1)) { 679214501Srpaulo goto tr_stalled; 680214501Srpaulo } 681214501Srpaulo } 682214501Srpaulo goto tr_valid; 683214501Srpaulo 684214501Srpaulotr_handle_get_ep_status: 685214501Srpaulo if (state == ST_DATA) { 686214501Srpaulo temp.wStatus[0] = 687214501Srpaulo usb2_handle_get_stall(udev, req.wIndex[0]); 688214501Srpaulo temp.wStatus[1] = 0; 689214501Srpaulo src_mcopy = temp.wStatus; 690214501Srpaulo max_len = sizeof(temp.wStatus); 691214501Srpaulo } 692214501Srpaulo goto tr_valid; 693214501Srpaulo 694214501Srpaulotr_valid: 695214501Srpaulo if (state == ST_POST_STATUS) { 696214501Srpaulo goto tr_stalled; 697214501Srpaulo } 698214501Srpaulo /* subtract offset from length */ 699214501Srpaulo 700214501Srpaulo max_len -= off; 701214501Srpaulo 702214501Srpaulo /* Compute the real maximum data length */ 703214501Srpaulo 704214501Srpaulo if (max_len > xfer->max_data_length) { 705214501Srpaulo max_len = xfer->max_data_length; 706214501Srpaulo } 707214501Srpaulo if (max_len > rem) { 708214501Srpaulo max_len = rem; 709214501Srpaulo } 710214501Srpaulo /* 711214501Srpaulo * If the remainder is greater than the maximum data length, 712214501Srpaulo * we need to truncate the value for the sake of the 713214501Srpaulo * comparison below: 714214501Srpaulo */ 715214501Srpaulo if (rem > xfer->max_data_length) { 716214501Srpaulo rem = xfer->max_data_length; 717214501Srpaulo } 718214501Srpaulo if (rem != max_len) { 719214501Srpaulo /* 720214501Srpaulo * If we don't transfer the data we can transfer, then 721214501Srpaulo * the transfer is short ! 722214501Srpaulo */ 723214501Srpaulo xfer->flags.force_short_xfer = 1; 724214501Srpaulo xfer->nframes = 2; 725214501Srpaulo } else { 726214501Srpaulo /* 727214501Srpaulo * Default case 728214501Srpaulo */ 729214501Srpaulo xfer->flags.force_short_xfer = 0; 730214501Srpaulo xfer->nframes = max_len ? 2 : 1; 731214501Srpaulo } 732214501Srpaulo if (max_len > 0) { 733214501Srpaulo if (src_mcopy) { 734214501Srpaulo src_mcopy = USB_ADD_BYTES(src_mcopy, off); 735214501Srpaulo usb2_copy_in(xfer->frbuffers + 1, 0, 736214501Srpaulo src_mcopy, max_len); 737214501Srpaulo } else { 738214501Srpaulo usb2_set_frame_data(xfer, 739214501Srpaulo USB_ADD_BYTES(src_zcopy, off), 1); 740214501Srpaulo } 741214501Srpaulo xfer->frlengths[1] = max_len; 742214501Srpaulo } else { 743214501Srpaulo /* the end is reached, send status */ 744214501Srpaulo xfer->flags.manual_status = 0; 745214501Srpaulo xfer->frlengths[1] = 0; 746214501Srpaulo } 747214501Srpaulo DPRINTF("success\n"); 748214501Srpaulo return (0); /* success */ 749214501Srpaulo 750214501Srpaulotr_stalled: 751214501Srpaulo DPRINTF("%s\n", (state == ST_POST_STATUS) ? 752214501Srpaulo "complete" : "stalled"); 753214501Srpaulo return (USB_ERR_STALLED); 754214501Srpaulo 755214501Srpaulotr_bad_context: 756214501Srpaulo DPRINTF("bad context\n"); 757214501Srpaulo return (USB_ERR_BAD_CONTEXT); 758214501Srpaulo} 759214501Srpaulo