f498dc2227
- make usb2_power_mask_t 16-bit - remove "usb2_config_sub" structure from "usb2_config". To compensate for this "usb2_config" has a new field called "usb_mode" which select for which mode the current xfer entry is active. Options are: a) Device mode only b) Host mode only (default-by-zero) c) Both modes. This change was scripted using the following sed script: "s/\.mh\././g". - the standard packet size table in "usb_transfer.c" is now a function, hence the code for the function uses less memory than the table itself. Submitted by: Hans Petter Selasky
758 lines
18 KiB
C
758 lines
18 KiB
C
/* $FreeBSD$ */
|
|
/*-
|
|
* Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#include <dev/usb/usb_mfunc.h>
|
|
#include <dev/usb/usb_error.h>
|
|
#include <dev/usb/usb.h>
|
|
|
|
#define USB_DEBUG_VAR usb2_debug
|
|
|
|
#include <dev/usb/usb_core.h>
|
|
#include <dev/usb/usb_process.h>
|
|
#include <dev/usb/usb_busdma.h>
|
|
#include <dev/usb/usb_transfer.h>
|
|
#include <dev/usb/usb_device.h>
|
|
#include <dev/usb/usb_debug.h>
|
|
#include <dev/usb/usb_dynamic.h>
|
|
#include <dev/usb/usb_hub.h>
|
|
|
|
#include <dev/usb/usb_controller.h>
|
|
#include <dev/usb/usb_bus.h>
|
|
|
|
/* enum */
|
|
|
|
enum {
|
|
ST_DATA,
|
|
ST_POST_STATUS,
|
|
};
|
|
|
|
/* function prototypes */
|
|
|
|
static uint8_t usb2_handle_get_stall(struct usb2_device *, uint8_t);
|
|
static usb2_error_t usb2_handle_remote_wakeup(struct usb2_xfer *, uint8_t);
|
|
static usb2_error_t usb2_handle_request(struct usb2_xfer *);
|
|
static usb2_error_t usb2_handle_set_config(struct usb2_xfer *, uint8_t);
|
|
static usb2_error_t usb2_handle_set_stall(struct usb2_xfer *, uint8_t,
|
|
uint8_t);
|
|
static usb2_error_t usb2_handle_iface_request(struct usb2_xfer *, void **,
|
|
uint16_t *, struct usb2_device_request, uint16_t,
|
|
uint8_t);
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb2_handle_request_callback
|
|
*
|
|
* This function is the USB callback for generic USB Device control
|
|
* transfers.
|
|
*------------------------------------------------------------------------*/
|
|
void
|
|
usb2_handle_request_callback(struct usb2_xfer *xfer)
|
|
{
|
|
usb2_error_t err;
|
|
|
|
/* check the current transfer state */
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_SETUP:
|
|
case USB_ST_TRANSFERRED:
|
|
|
|
/* handle the request */
|
|
err = usb2_handle_request(xfer);
|
|
|
|
if (err) {
|
|
|
|
if (err == USB_ERR_BAD_CONTEXT) {
|
|
/* we need to re-setup the control transfer */
|
|
usb2_needs_explore(xfer->xroot->bus, 0);
|
|
break;
|
|
}
|
|
/*
|
|
* If no control transfer is active,
|
|
* receive the next SETUP message:
|
|
*/
|
|
goto tr_restart;
|
|
}
|
|
usb2_start_hardware(xfer);
|
|
break;
|
|
|
|
default:
|
|
if (xfer->error != USB_ERR_CANCELLED) {
|
|
/* should not happen - try stalling */
|
|
goto tr_restart;
|
|
}
|
|
break;
|
|
}
|
|
return;
|
|
|
|
tr_restart:
|
|
xfer->frlengths[0] = sizeof(struct usb2_device_request);
|
|
xfer->nframes = 1;
|
|
xfer->flags.manual_status = 1;
|
|
xfer->flags.force_short_xfer = 0;
|
|
xfer->flags.stall_pipe = 1; /* cancel previous transfer, if any */
|
|
usb2_start_hardware(xfer);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb2_handle_set_config
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static usb2_error_t
|
|
usb2_handle_set_config(struct usb2_xfer *xfer, uint8_t conf_no)
|
|
{
|
|
struct usb2_device *udev = xfer->xroot->udev;
|
|
usb2_error_t err = 0;
|
|
|
|
/*
|
|
* We need to protect against other threads doing probe and
|
|
* attach:
|
|
*/
|
|
USB_XFER_UNLOCK(xfer);
|
|
mtx_lock(&Giant); /* XXX */
|
|
sx_xlock(udev->default_sx + 1);
|
|
|
|
if (conf_no == USB_UNCONFIG_NO) {
|
|
conf_no = USB_UNCONFIG_INDEX;
|
|
} else {
|
|
/*
|
|
* The relationship between config number and config index
|
|
* is very simple in our case:
|
|
*/
|
|
conf_no--;
|
|
}
|
|
|
|
if (usb2_set_config_index(udev, conf_no)) {
|
|
DPRINTF("set config %d failed\n", conf_no);
|
|
err = USB_ERR_STALLED;
|
|
goto done;
|
|
}
|
|
if (usb2_probe_and_attach(udev, USB_IFACE_INDEX_ANY)) {
|
|
DPRINTF("probe and attach failed\n");
|
|
err = USB_ERR_STALLED;
|
|
goto done;
|
|
}
|
|
done:
|
|
mtx_unlock(&Giant); /* XXX */
|
|
sx_unlock(udev->default_sx + 1);
|
|
USB_XFER_LOCK(xfer);
|
|
return (err);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb2_handle_iface_request
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static usb2_error_t
|
|
usb2_handle_iface_request(struct usb2_xfer *xfer,
|
|
void **ppdata, uint16_t *plen,
|
|
struct usb2_device_request req, uint16_t off, uint8_t state)
|
|
{
|
|
struct usb2_interface *iface;
|
|
struct usb2_interface *iface_parent; /* parent interface */
|
|
struct usb2_device *udev = xfer->xroot->udev;
|
|
int error;
|
|
uint8_t iface_index;
|
|
|
|
if ((req.bmRequestType & 0x1F) == UT_INTERFACE) {
|
|
iface_index = req.wIndex[0]; /* unicast */
|
|
} else {
|
|
iface_index = 0; /* broadcast */
|
|
}
|
|
|
|
/*
|
|
* We need to protect against other threads doing probe and
|
|
* attach:
|
|
*/
|
|
USB_XFER_UNLOCK(xfer);
|
|
mtx_lock(&Giant); /* XXX */
|
|
sx_xlock(udev->default_sx + 1);
|
|
|
|
error = ENXIO;
|
|
|
|
tr_repeat:
|
|
iface = usb2_get_iface(udev, iface_index);
|
|
if ((iface == NULL) ||
|
|
(iface->idesc == NULL)) {
|
|
/* end of interfaces non-existing interface */
|
|
goto tr_stalled;
|
|
}
|
|
/* forward request to interface, if any */
|
|
|
|
if ((error != 0) &&
|
|
(error != ENOTTY) &&
|
|
(iface->subdev != NULL) &&
|
|
device_is_attached(iface->subdev)) {
|
|
#if 0
|
|
DEVMETHOD(usb_handle_request, NULL); /* dummy */
|
|
#endif
|
|
error = USB_HANDLE_REQUEST(iface->subdev,
|
|
&req, ppdata, plen,
|
|
off, (state == ST_POST_STATUS));
|
|
}
|
|
iface_parent = usb2_get_iface(udev, iface->parent_iface_index);
|
|
|
|
if ((iface_parent == NULL) ||
|
|
(iface_parent->idesc == NULL)) {
|
|
/* non-existing interface */
|
|
iface_parent = NULL;
|
|
}
|
|
/* forward request to parent interface, if any */
|
|
|
|
if ((error != 0) &&
|
|
(error != ENOTTY) &&
|
|
(iface_parent != NULL) &&
|
|
(iface_parent->subdev != NULL) &&
|
|
((req.bmRequestType & 0x1F) == UT_INTERFACE) &&
|
|
(iface_parent->subdev != iface->subdev) &&
|
|
device_is_attached(iface_parent->subdev)) {
|
|
error = USB_HANDLE_REQUEST(iface_parent->subdev,
|
|
&req, ppdata, plen, off,
|
|
(state == ST_POST_STATUS));
|
|
}
|
|
if (error == 0) {
|
|
/* negativly adjust pointer and length */
|
|
*ppdata = ((uint8_t *)(*ppdata)) - off;
|
|
*plen += off;
|
|
goto tr_valid;
|
|
} else if (error == ENOTTY) {
|
|
goto tr_stalled;
|
|
}
|
|
if ((req.bmRequestType & 0x1F) != UT_INTERFACE) {
|
|
iface_index++; /* iterate */
|
|
goto tr_repeat;
|
|
}
|
|
if (state == ST_POST_STATUS) {
|
|
/* we are complete */
|
|
goto tr_valid;
|
|
}
|
|
switch (req.bmRequestType) {
|
|
case UT_WRITE_INTERFACE:
|
|
switch (req.bRequest) {
|
|
case UR_SET_INTERFACE:
|
|
/*
|
|
* Handle special case. If we have parent interface
|
|
* we just reset the endpoints, because this is a
|
|
* multi interface device and re-attaching only a
|
|
* part of the device is not possible. Also if the
|
|
* alternate setting is the same like before we just
|
|
* reset the interface endoints.
|
|
*/
|
|
if ((iface_parent != NULL) ||
|
|
(iface->alt_index == req.wValue[0])) {
|
|
error = usb2_reset_iface_endpoints(udev,
|
|
iface_index);
|
|
if (error) {
|
|
DPRINTF("alt setting failed %s\n",
|
|
usb2_errstr(error));
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
}
|
|
/*
|
|
* Doing the alternate setting will detach the
|
|
* interface aswell:
|
|
*/
|
|
error = usb2_set_alt_interface_index(udev,
|
|
iface_index, req.wValue[0]);
|
|
if (error) {
|
|
DPRINTF("alt setting failed %s\n",
|
|
usb2_errstr(error));
|
|
goto tr_stalled;
|
|
}
|
|
error = usb2_probe_and_attach(udev,
|
|
iface_index);
|
|
if (error) {
|
|
DPRINTF("alt setting probe failed\n");
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
|
|
case UT_READ_INTERFACE:
|
|
switch (req.bRequest) {
|
|
case UR_GET_INTERFACE:
|
|
*ppdata = &iface->alt_index;
|
|
*plen = 1;
|
|
break;
|
|
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
tr_valid:
|
|
mtx_unlock(&Giant);
|
|
sx_unlock(udev->default_sx + 1);
|
|
USB_XFER_LOCK(xfer);
|
|
return (0);
|
|
|
|
tr_stalled:
|
|
mtx_unlock(&Giant);
|
|
sx_unlock(udev->default_sx + 1);
|
|
USB_XFER_LOCK(xfer);
|
|
return (USB_ERR_STALLED);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb2_handle_stall
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static usb2_error_t
|
|
usb2_handle_set_stall(struct usb2_xfer *xfer, uint8_t ep, uint8_t do_stall)
|
|
{
|
|
struct usb2_device *udev = xfer->xroot->udev;
|
|
usb2_error_t err;
|
|
|
|
USB_XFER_UNLOCK(xfer);
|
|
err = usb2_set_endpoint_stall(udev,
|
|
usb2_get_pipe_by_addr(udev, ep), do_stall);
|
|
USB_XFER_LOCK(xfer);
|
|
return (err);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb2_handle_get_stall
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static uint8_t
|
|
usb2_handle_get_stall(struct usb2_device *udev, uint8_t ea_val)
|
|
{
|
|
struct usb2_pipe *pipe;
|
|
uint8_t halted;
|
|
|
|
pipe = usb2_get_pipe_by_addr(udev, ea_val);
|
|
if (pipe == NULL) {
|
|
/* nothing to do */
|
|
return (0);
|
|
}
|
|
USB_BUS_LOCK(udev->bus);
|
|
halted = pipe->is_stalled;
|
|
USB_BUS_UNLOCK(udev->bus);
|
|
|
|
return (halted);
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb2_handle_remote_wakeup
|
|
*
|
|
* Returns:
|
|
* 0: Success
|
|
* Else: Failure
|
|
*------------------------------------------------------------------------*/
|
|
static usb2_error_t
|
|
usb2_handle_remote_wakeup(struct usb2_xfer *xfer, uint8_t is_on)
|
|
{
|
|
struct usb2_device *udev;
|
|
struct usb2_bus *bus;
|
|
|
|
udev = xfer->xroot->udev;
|
|
bus = udev->bus;
|
|
|
|
USB_BUS_LOCK(bus);
|
|
|
|
if (is_on) {
|
|
udev->flags.remote_wakeup = 1;
|
|
} else {
|
|
udev->flags.remote_wakeup = 0;
|
|
}
|
|
|
|
USB_BUS_UNLOCK(bus);
|
|
|
|
/* In case we are out of sync, update the power state. */
|
|
usb2_bus_power_update(udev->bus);
|
|
return (0); /* success */
|
|
}
|
|
|
|
/*------------------------------------------------------------------------*
|
|
* usb2_handle_request
|
|
*
|
|
* Internal state sequence:
|
|
*
|
|
* ST_DATA -> ST_POST_STATUS
|
|
*
|
|
* Returns:
|
|
* 0: Ready to start hardware
|
|
* Else: Stall current transfer, if any
|
|
*------------------------------------------------------------------------*/
|
|
static usb2_error_t
|
|
usb2_handle_request(struct usb2_xfer *xfer)
|
|
{
|
|
struct usb2_device_request req;
|
|
struct usb2_device *udev;
|
|
const void *src_zcopy; /* zero-copy source pointer */
|
|
const void *src_mcopy; /* non zero-copy source pointer */
|
|
uint16_t off; /* data offset */
|
|
uint16_t rem; /* data remainder */
|
|
uint16_t max_len; /* max fragment length */
|
|
uint16_t wValue;
|
|
uint16_t wIndex;
|
|
uint8_t state;
|
|
usb2_error_t err;
|
|
union {
|
|
uWord wStatus;
|
|
uint8_t buf[2];
|
|
} temp;
|
|
|
|
/*
|
|
* Filter the USB transfer state into
|
|
* something which we understand:
|
|
*/
|
|
|
|
switch (USB_GET_STATE(xfer)) {
|
|
case USB_ST_SETUP:
|
|
state = ST_DATA;
|
|
|
|
if (!xfer->flags_int.control_act) {
|
|
/* nothing to do */
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
|
|
default: /* USB_ST_TRANSFERRED */
|
|
if (!xfer->flags_int.control_act) {
|
|
state = ST_POST_STATUS;
|
|
} else {
|
|
state = ST_DATA;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* reset frame stuff */
|
|
|
|
xfer->frlengths[0] = 0;
|
|
|
|
usb2_set_frame_offset(xfer, 0, 0);
|
|
usb2_set_frame_offset(xfer, sizeof(req), 1);
|
|
|
|
/* get the current request, if any */
|
|
|
|
usb2_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
|
|
|
|
if (xfer->flags_int.control_rem == 0xFFFF) {
|
|
/* first time - not initialised */
|
|
rem = UGETW(req.wLength);
|
|
off = 0;
|
|
} else {
|
|
/* not first time - initialised */
|
|
rem = xfer->flags_int.control_rem;
|
|
off = UGETW(req.wLength) - rem;
|
|
}
|
|
|
|
/* set some defaults */
|
|
|
|
max_len = 0;
|
|
src_zcopy = NULL;
|
|
src_mcopy = NULL;
|
|
udev = xfer->xroot->udev;
|
|
|
|
/* get some request fields decoded */
|
|
|
|
wValue = UGETW(req.wValue);
|
|
wIndex = UGETW(req.wIndex);
|
|
|
|
DPRINTF("req 0x%02x 0x%02x 0x%04x 0x%04x "
|
|
"off=0x%x rem=0x%x, state=%d\n", req.bmRequestType,
|
|
req.bRequest, wValue, wIndex, off, rem, state);
|
|
|
|
/* demultiplex the control request */
|
|
|
|
switch (req.bmRequestType) {
|
|
case UT_READ_DEVICE:
|
|
if (state != ST_DATA) {
|
|
break;
|
|
}
|
|
switch (req.bRequest) {
|
|
case UR_GET_DESCRIPTOR:
|
|
goto tr_handle_get_descriptor;
|
|
case UR_GET_CONFIG:
|
|
goto tr_handle_get_config;
|
|
case UR_GET_STATUS:
|
|
goto tr_handle_get_status;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
|
|
case UT_WRITE_DEVICE:
|
|
switch (req.bRequest) {
|
|
case UR_SET_ADDRESS:
|
|
goto tr_handle_set_address;
|
|
case UR_SET_CONFIG:
|
|
goto tr_handle_set_config;
|
|
case UR_CLEAR_FEATURE:
|
|
switch (wValue) {
|
|
case UF_DEVICE_REMOTE_WAKEUP:
|
|
goto tr_handle_clear_wakeup;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
case UR_SET_FEATURE:
|
|
switch (wValue) {
|
|
case UF_DEVICE_REMOTE_WAKEUP:
|
|
goto tr_handle_set_wakeup;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
|
|
case UT_WRITE_ENDPOINT:
|
|
switch (req.bRequest) {
|
|
case UR_CLEAR_FEATURE:
|
|
switch (wValue) {
|
|
case UF_ENDPOINT_HALT:
|
|
goto tr_handle_clear_halt;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
case UR_SET_FEATURE:
|
|
switch (wValue) {
|
|
case UF_ENDPOINT_HALT:
|
|
goto tr_handle_set_halt;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
|
|
case UT_READ_ENDPOINT:
|
|
switch (req.bRequest) {
|
|
case UR_GET_STATUS:
|
|
goto tr_handle_get_ep_status;
|
|
default:
|
|
goto tr_stalled;
|
|
}
|
|
break;
|
|
default:
|
|
/* we use "USB_ADD_BYTES" to de-const the src_zcopy */
|
|
err = usb2_handle_iface_request(xfer,
|
|
USB_ADD_BYTES(&src_zcopy, 0),
|
|
&max_len, req, off, state);
|
|
if (err == 0) {
|
|
goto tr_valid;
|
|
}
|
|
/*
|
|
* Reset zero-copy pointer and max length
|
|
* variable in case they were unintentionally
|
|
* set:
|
|
*/
|
|
src_zcopy = NULL;
|
|
max_len = 0;
|
|
|
|
/*
|
|
* Check if we have a vendor specific
|
|
* descriptor:
|
|
*/
|
|
goto tr_handle_get_descriptor;
|
|
}
|
|
goto tr_valid;
|
|
|
|
tr_handle_get_descriptor:
|
|
(usb2_temp_get_desc_p) (udev, &req, &src_zcopy, &max_len);
|
|
if (src_zcopy == NULL) {
|
|
goto tr_stalled;
|
|
}
|
|
goto tr_valid;
|
|
|
|
tr_handle_get_config:
|
|
temp.buf[0] = udev->curr_config_no;
|
|
src_mcopy = temp.buf;
|
|
max_len = 1;
|
|
goto tr_valid;
|
|
|
|
tr_handle_get_status:
|
|
|
|
wValue = 0;
|
|
|
|
USB_BUS_LOCK(udev->bus);
|
|
if (udev->flags.remote_wakeup) {
|
|
wValue |= UDS_REMOTE_WAKEUP;
|
|
}
|
|
if (udev->flags.self_powered) {
|
|
wValue |= UDS_SELF_POWERED;
|
|
}
|
|
USB_BUS_UNLOCK(udev->bus);
|
|
|
|
USETW(temp.wStatus, wValue);
|
|
src_mcopy = temp.wStatus;
|
|
max_len = sizeof(temp.wStatus);
|
|
goto tr_valid;
|
|
|
|
tr_handle_set_address:
|
|
if (state == ST_DATA) {
|
|
if (wValue >= 0x80) {
|
|
/* invalid value */
|
|
goto tr_stalled;
|
|
} else if (udev->curr_config_no != 0) {
|
|
/* we are configured ! */
|
|
goto tr_stalled;
|
|
}
|
|
} else if (state == ST_POST_STATUS) {
|
|
udev->address = (wValue & 0x7F);
|
|
goto tr_bad_context;
|
|
}
|
|
goto tr_valid;
|
|
|
|
tr_handle_set_config:
|
|
if (state == ST_DATA) {
|
|
if (usb2_handle_set_config(xfer, req.wValue[0])) {
|
|
goto tr_stalled;
|
|
}
|
|
}
|
|
goto tr_valid;
|
|
|
|
tr_handle_clear_halt:
|
|
if (state == ST_DATA) {
|
|
if (usb2_handle_set_stall(xfer, req.wIndex[0], 0)) {
|
|
goto tr_stalled;
|
|
}
|
|
}
|
|
goto tr_valid;
|
|
|
|
tr_handle_clear_wakeup:
|
|
if (state == ST_DATA) {
|
|
if (usb2_handle_remote_wakeup(xfer, 0)) {
|
|
goto tr_stalled;
|
|
}
|
|
}
|
|
goto tr_valid;
|
|
|
|
tr_handle_set_halt:
|
|
if (state == ST_DATA) {
|
|
if (usb2_handle_set_stall(xfer, req.wIndex[0], 1)) {
|
|
goto tr_stalled;
|
|
}
|
|
}
|
|
goto tr_valid;
|
|
|
|
tr_handle_set_wakeup:
|
|
if (state == ST_DATA) {
|
|
if (usb2_handle_remote_wakeup(xfer, 1)) {
|
|
goto tr_stalled;
|
|
}
|
|
}
|
|
goto tr_valid;
|
|
|
|
tr_handle_get_ep_status:
|
|
if (state == ST_DATA) {
|
|
temp.wStatus[0] =
|
|
usb2_handle_get_stall(udev, req.wIndex[0]);
|
|
temp.wStatus[1] = 0;
|
|
src_mcopy = temp.wStatus;
|
|
max_len = sizeof(temp.wStatus);
|
|
}
|
|
goto tr_valid;
|
|
|
|
tr_valid:
|
|
if (state == ST_POST_STATUS) {
|
|
goto tr_stalled;
|
|
}
|
|
/* subtract offset from length */
|
|
|
|
max_len -= off;
|
|
|
|
/* Compute the real maximum data length */
|
|
|
|
if (max_len > xfer->max_data_length) {
|
|
max_len = xfer->max_data_length;
|
|
}
|
|
if (max_len > rem) {
|
|
max_len = rem;
|
|
}
|
|
/*
|
|
* If the remainder is greater than the maximum data length,
|
|
* we need to truncate the value for the sake of the
|
|
* comparison below:
|
|
*/
|
|
if (rem > xfer->max_data_length) {
|
|
rem = xfer->max_data_length;
|
|
}
|
|
if (rem != max_len) {
|
|
/*
|
|
* If we don't transfer the data we can transfer, then
|
|
* the transfer is short !
|
|
*/
|
|
xfer->flags.force_short_xfer = 1;
|
|
xfer->nframes = 2;
|
|
} else {
|
|
/*
|
|
* Default case
|
|
*/
|
|
xfer->flags.force_short_xfer = 0;
|
|
xfer->nframes = max_len ? 2 : 1;
|
|
}
|
|
if (max_len > 0) {
|
|
if (src_mcopy) {
|
|
src_mcopy = USB_ADD_BYTES(src_mcopy, off);
|
|
usb2_copy_in(xfer->frbuffers + 1, 0,
|
|
src_mcopy, max_len);
|
|
} else {
|
|
usb2_set_frame_data(xfer,
|
|
USB_ADD_BYTES(src_zcopy, off), 1);
|
|
}
|
|
xfer->frlengths[1] = max_len;
|
|
} else {
|
|
/* the end is reached, send status */
|
|
xfer->flags.manual_status = 0;
|
|
xfer->frlengths[1] = 0;
|
|
}
|
|
DPRINTF("success\n");
|
|
return (0); /* success */
|
|
|
|
tr_stalled:
|
|
DPRINTF("%s\n", (state == ST_POST_STATUS) ?
|
|
"complete" : "stalled");
|
|
return (USB_ERR_STALLED);
|
|
|
|
tr_bad_context:
|
|
DPRINTF("bad context\n");
|
|
return (USB_ERR_BAD_CONTEXT);
|
|
}
|