#ifdef USB_GLOBAL_INCLUDE_FILE
#include USB_GLOBAL_INCLUDE_FILE
#else
#include <sys/stdint.h>
#include <sys/stddef.h>
#include <sys/param.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/bus.h>
#include <sys/module.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/condvar.h>
#include <sys/sysctl.h>
#include <sys/sx.h>
#include <sys/unistd.h>
#include <sys/callout.h>
#include <sys/malloc.h>
#include <sys/priv.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <dev/usb/usbdi_util.h>
#define USB_DEBUG_VAR usb_debug
#include <dev/usb/usb_core.h>
#include <dev/usb/usb_busdma.h>
#include <dev/usb/usb_process.h>
#include <dev/usb/usb_transfer.h>
#include <dev/usb/usb_device.h>
#include <dev/usb/usb_debug.h>
#include <dev/usb/usb_util.h>
#include <dev/usb/usb_controller.h>
#include <dev/usb/usb_bus.h>
#include <dev/usb/usb_pf.h>
#endif
struct usb_std_packet_size {
struct {
uint16_t min;
uint16_t max;
} range;
uint16_t fixed[4];
};
static usb_callback_t usb_request_callback;
static const struct usb_config usb_control_ep_cfg[USB_CTRL_XFER_MAX] = {
[0] = {
.type = UE_CONTROL,
.endpoint = 0x00,
.direction = UE_DIR_ANY,
.bufsize = USB_EP0_BUFSIZE,
.flags = {.proxy_buffer = 1,},
.callback = &usb_request_callback,
.usb_mode = USB_MODE_DUAL,
},
[1] = {
.type = UE_CONTROL,
.endpoint = 0x00,
.direction = UE_DIR_ANY,
.bufsize = sizeof(struct usb_device_request),
.callback = &usb_do_clear_stall_callback,
.timeout = 1000,
.interval = 50,
.usb_mode = USB_MODE_HOST,
},
};
static const struct usb_config usb_control_ep_quirk_cfg[USB_CTRL_XFER_MAX] = {
[0] = {
.type = UE_CONTROL,
.endpoint = 0x00,
.direction = UE_DIR_ANY,
.bufsize = 65535,
.callback = &usb_request_callback,
.usb_mode = USB_MODE_DUAL,
},
[1] = {
.type = UE_CONTROL,
.endpoint = 0x00,
.direction = UE_DIR_ANY,
.bufsize = sizeof(struct usb_device_request),
.callback = &usb_do_clear_stall_callback,
.timeout = 1000,
.interval = 50,
.usb_mode = USB_MODE_HOST,
},
};
static void usbd_update_max_frame_size(struct usb_xfer *);
static void usbd_transfer_unsetup_sub(struct usb_xfer_root *, uint8_t);
static void usbd_control_transfer_init(struct usb_xfer *);
static int usbd_setup_ctrl_transfer(struct usb_xfer *);
static void usb_callback_proc(struct usb_proc_msg *);
static void usbd_callback_ss_done_defer(struct usb_xfer *);
static void usbd_callback_wrapper(struct usb_xfer_queue *);
static void usbd_transfer_start_cb(void *);
static uint8_t usbd_callback_wrapper_sub(struct usb_xfer *);
static void usbd_get_std_packet_size(struct usb_std_packet_size *ptr,
uint8_t type, enum usb_dev_speed speed);
static void
usb_request_callback(struct usb_xfer *xfer, usb_error_t error)
{
if (xfer->flags_int.usb_mode == USB_MODE_DEVICE)
usb_handle_request_callback(xfer, error);
else
usbd_do_request_callback(xfer, error);
}
static void
usbd_update_max_frame_size(struct usb_xfer *xfer)
{
xfer->max_frame_size = xfer->max_packet_size * xfer->max_packet_count;
}
usb_timeout_t
usbd_get_dma_delay(struct usb_device *udev)
{
const struct usb_bus_methods *mtod;
uint32_t temp;
mtod = udev->bus->methods;
temp = 0;
if (mtod->get_dma_delay) {
(mtod->get_dma_delay) (udev, &temp);
temp += 0x3FF;
temp /= 0x400;
}
return (temp);
}
#if USB_HAVE_BUSDMA
uint8_t
usbd_transfer_setup_sub_malloc(struct usb_setup_params *parm,
struct usb_page_cache **ppc, usb_size_t size, usb_size_t align,
usb_size_t count)
{
struct usb_page_cache *pc;
struct usb_page *pg;
void *buf;
usb_size_t n_dma_pc;
usb_size_t n_dma_pg;
usb_size_t n_obj;
usb_size_t x;
usb_size_t y;
usb_size_t r;
usb_size_t z;
USB_ASSERT(align > 0, ("Invalid alignment, 0x%08x\n",
align));
USB_ASSERT(size > 0, ("Invalid size = 0\n"));
if (count == 0) {
return (0);
}
size = -((-size) & (-align));
if (align == 1) {
n_dma_pc = count;
n_dma_pg = (2 + (size / USB_PAGE_SIZE));
n_obj = 1;
} else if (size >= USB_PAGE_SIZE) {
n_dma_pc = count;
n_dma_pg = 1;
n_obj = 1;
} else {
#ifdef USB_DMA_SINGLE_ALLOC
n_obj = 1;
#else
n_obj = (USB_PAGE_SIZE / size);
#endif
n_dma_pc = howmany(count, n_obj);
n_dma_pg = 1;
}
if (parm->buf == NULL) {
parm->dma_page_ptr += n_dma_pc * n_dma_pg;
parm->dma_page_cache_ptr += n_dma_pc;
parm->dma_page_ptr += count * n_dma_pg;
parm->xfer_page_cache_ptr += count;
return (0);
}
for (x = 0; x != n_dma_pc; x++) {
parm->dma_page_cache_ptr[x].tag_parent =
&parm->curr_xfer->xroot->dma_parent_tag;
}
for (x = 0; x != count; x++) {
parm->xfer_page_cache_ptr[x].tag_parent =
&parm->curr_xfer->xroot->dma_parent_tag;
}
if (ppc != NULL) {
if (n_obj != 1)
*ppc = parm->xfer_page_cache_ptr;
else
*ppc = parm->dma_page_cache_ptr;
}
r = count;
z = n_obj * size;
pc = parm->xfer_page_cache_ptr;
pg = parm->dma_page_ptr;
if (n_obj == 1) {
for (x = 0; x != n_dma_pc; x++) {
if (usb_pc_alloc_mem(parm->dma_page_cache_ptr,
pg, z, align)) {
return (1);
}
parm->dma_page_cache_ptr++;
pg += n_dma_pg;
}
} else {
for (x = 0; x != n_dma_pc; x++) {
if (r < n_obj) {
z = r * size;
n_obj = r;
}
if (usb_pc_alloc_mem(parm->dma_page_cache_ptr,
pg, z, align)) {
return (1);
}
buf = parm->dma_page_cache_ptr->buffer;
parm->dma_page_cache_ptr++;
pg += n_dma_pg;
for (y = 0; (y != n_obj); y++, r--, pc++, pg += n_dma_pg) {
if (usb_pc_dmamap_create(pc, size)) {
return (1);
}
pc->buffer = USB_ADD_BYTES(buf, y * size);
pc->page_start = pg;
USB_MTX_LOCK(pc->tag_parent->mtx);
if (usb_pc_load_mem(pc, size, 1 )) {
USB_MTX_UNLOCK(pc->tag_parent->mtx);
return (1);
}
USB_MTX_UNLOCK(pc->tag_parent->mtx);
}
}
}
parm->xfer_page_cache_ptr = pc;
parm->dma_page_ptr = pg;
return (0);
}
#endif
uint32_t
usbd_get_max_frame_length(const struct usb_endpoint_descriptor *edesc,
const struct usb_endpoint_ss_comp_descriptor *ecomp,
enum usb_dev_speed speed)
{
uint32_t max_packet_size;
uint32_t max_packet_count;
uint8_t type;
max_packet_size = UGETW(edesc->wMaxPacketSize);
max_packet_count = 1;
type = (edesc->bmAttributes & UE_XFERTYPE);
switch (speed) {
case USB_SPEED_HIGH:
switch (type) {
case UE_ISOCHRONOUS:
case UE_INTERRUPT:
max_packet_count +=
(max_packet_size >> 11) & 3;
if (max_packet_count > 3)
max_packet_count = 3;
break;
default:
break;
}
max_packet_size &= 0x7FF;
break;
case USB_SPEED_SUPER:
max_packet_count += (max_packet_size >> 11) & 3;
if (ecomp != NULL)
max_packet_count += ecomp->bMaxBurst;
if ((max_packet_count == 0) ||
(max_packet_count > 16))
max_packet_count = 16;
switch (type) {
case UE_CONTROL:
max_packet_count = 1;
break;
case UE_ISOCHRONOUS:
if (ecomp != NULL) {
uint8_t mult;
mult = UE_GET_SS_ISO_MULT(
ecomp->bmAttributes) + 1;
if (mult > 3)
mult = 3;
max_packet_count *= mult;
}
break;
default:
break;
}
max_packet_size &= 0x7FF;
break;
default:
break;
}
return (max_packet_size * max_packet_count);
}
void
usbd_transfer_setup_sub(struct usb_setup_params *parm)
{
enum {
REQ_SIZE = 8,
MIN_PKT = 8,
};
struct usb_xfer *xfer = parm->curr_xfer;
const struct usb_config *setup = parm->curr_setup;
struct usb_endpoint_ss_comp_descriptor *ecomp;
struct usb_endpoint_descriptor *edesc;
struct usb_std_packet_size std_size;
usb_frcount_t n_frlengths;
usb_frcount_t n_frbuffers;
usb_frcount_t x;
uint16_t maxp_old;
uint8_t type;
uint8_t zmps;
if ((parm->hc_max_packet_size == 0) ||
(parm->hc_max_packet_count == 0) ||
(parm->hc_max_frame_size == 0)) {
parm->err = USB_ERR_INVAL;
goto done;
}
edesc = xfer->endpoint->edesc;
ecomp = xfer->endpoint->ecomp;
type = (edesc->bmAttributes & UE_XFERTYPE);
xfer->flags = setup->flags;
xfer->nframes = setup->frames;
xfer->timeout = setup->timeout;
xfer->callback = setup->callback;
xfer->interval = setup->interval;
xfer->endpointno = edesc->bEndpointAddress;
xfer->max_packet_size = UGETW(edesc->wMaxPacketSize);
xfer->max_packet_count = 1;
xfer->flags_int.usb_mode = parm->udev->flags.usb_mode;
parm->bufsize = setup->bufsize;
switch (parm->speed) {
case USB_SPEED_HIGH:
switch (type) {
case UE_ISOCHRONOUS:
case UE_INTERRUPT:
xfer->max_packet_count +=
(xfer->max_packet_size >> 11) & 3;
if (xfer->max_packet_count > 3)
xfer->max_packet_count = 3;
break;
default:
break;
}
xfer->max_packet_size &= 0x7FF;
break;
case USB_SPEED_SUPER:
xfer->max_packet_count += (xfer->max_packet_size >> 11) & 3;
if (ecomp != NULL)
xfer->max_packet_count += ecomp->bMaxBurst;
if ((xfer->max_packet_count == 0) ||
(xfer->max_packet_count > 16))
xfer->max_packet_count = 16;
switch (type) {
case UE_CONTROL:
xfer->max_packet_count = 1;
break;
case UE_ISOCHRONOUS:
if (ecomp != NULL) {
uint8_t mult;
mult = UE_GET_SS_ISO_MULT(
ecomp->bmAttributes) + 1;
if (mult > 3)
mult = 3;
xfer->max_packet_count *= mult;
}
break;
default:
break;
}
xfer->max_packet_size &= 0x7FF;
break;
default:
break;
}
if (xfer->max_packet_count > parm->hc_max_packet_count) {
xfer->max_packet_count = parm->hc_max_packet_count;
}
maxp_old = xfer->max_packet_size;
if ((xfer->max_packet_size > parm->hc_max_packet_size) ||
(xfer->max_packet_size == 0)) {
xfer->max_packet_size = parm->hc_max_packet_size;
}
usbd_get_std_packet_size(&std_size, type, parm->speed);
if (std_size.range.min || std_size.range.max) {
if (xfer->max_packet_size < std_size.range.min) {
xfer->max_packet_size = std_size.range.min;
}
if (xfer->max_packet_size > std_size.range.max) {
xfer->max_packet_size = std_size.range.max;
}
} else {
if (xfer->max_packet_size >= std_size.fixed[3]) {
xfer->max_packet_size = std_size.fixed[3];
} else if (xfer->max_packet_size >= std_size.fixed[2]) {
xfer->max_packet_size = std_size.fixed[2];
} else if (xfer->max_packet_size >= std_size.fixed[1]) {
xfer->max_packet_size = std_size.fixed[1];
} else {
xfer->max_packet_size = std_size.fixed[0];
}
}
if (maxp_old != xfer->max_packet_size)
xfer->flags_int.maxp_was_clamped = 1;
usbd_update_max_frame_size(xfer);
if (type == UE_ISOCHRONOUS) {
uint16_t frame_limit;
xfer->interval = 0;
xfer->flags_int.isochronous_xfr = 1;
if (xfer->timeout == 0) {
xfer->timeout = 1000 / 4;
}
switch (parm->speed) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
frame_limit = USB_MAX_FS_ISOC_FRAMES_PER_XFER;
xfer->fps_shift = 0;
break;
default:
frame_limit = USB_MAX_HS_ISOC_FRAMES_PER_XFER;
xfer->fps_shift = edesc->bInterval;
if (xfer->fps_shift > 0)
xfer->fps_shift--;
if (xfer->fps_shift > 3)
xfer->fps_shift = 3;
if (xfer->flags.pre_scale_frames != 0)
xfer->nframes <<= (3 - xfer->fps_shift);
break;
}
if (xfer->nframes > frame_limit) {
parm->err = USB_ERR_INVAL;
goto done;
}
if (xfer->nframes == 0) {
parm->err = USB_ERR_ZERO_NFRAMES;
goto done;
}
} else {
if (type == UE_INTERRUPT) {
uint32_t temp;
if (xfer->interval == 0) {
xfer->interval = edesc->bInterval;
switch (parm->speed) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
break;
default:
if (xfer->interval < 4)
xfer->interval = 1;
else if (xfer->interval > 16)
xfer->interval = (1 << (16 - 4));
else
xfer->interval =
(1 << (xfer->interval - 4));
break;
}
}
if (xfer->interval == 0) {
xfer->interval = 1;
}
xfer->fps_shift = 0;
temp = 1;
while ((temp != 0) && (temp < xfer->interval)) {
xfer->fps_shift++;
temp *= 2;
}
switch (parm->speed) {
case USB_SPEED_LOW:
case USB_SPEED_FULL:
break;
default:
xfer->fps_shift += 3;
break;
}
}
}
if ((xfer->max_frame_size == 0) ||
(xfer->max_packet_size == 0)) {
zmps = 1;
if ((parm->bufsize <= MIN_PKT) &&
(type != UE_CONTROL) &&
(type != UE_BULK)) {
xfer->max_packet_size = MIN_PKT;
xfer->max_packet_count = 1;
parm->bufsize = 0;
usbd_update_max_frame_size(xfer);
} else {
parm->err = USB_ERR_ZERO_MAXP;
goto done;
}
} else {
zmps = 0;
}
if (parm->bufsize == 0) {
parm->bufsize = xfer->max_frame_size;
if (type == UE_ISOCHRONOUS) {
parm->bufsize *= xfer->nframes;
}
}
if (xfer->flags.proxy_buffer) {
parm->bufsize += (xfer->max_frame_size - 1);
if (parm->bufsize < xfer->max_frame_size) {
parm->err = USB_ERR_INVAL;
goto done;
}
parm->bufsize -= (parm->bufsize % xfer->max_frame_size);
if (type == UE_CONTROL) {
parm->bufsize += REQ_SIZE;
}
}
xfer->max_data_length = parm->bufsize;
if (type == UE_ISOCHRONOUS) {
n_frlengths = xfer->nframes;
n_frbuffers = 1;
} else {
if (type == UE_CONTROL) {
xfer->flags_int.control_xfr = 1;
if (xfer->nframes == 0) {
if (parm->bufsize <= REQ_SIZE) {
xfer->nframes = 1;
} else {
xfer->nframes = 2;
}
}
} else {
if (xfer->nframes == 0) {
xfer->nframes = 1;
}
}
n_frlengths = xfer->nframes;
n_frbuffers = xfer->nframes;
}
if (type == UE_CONTROL) {
if (xfer->max_data_length < REQ_SIZE) {
parm->err = USB_ERR_INVAL;
goto done;
}
xfer->max_data_length -= REQ_SIZE;
}
xfer->frlengths = parm->xfer_length_ptr;
parm->xfer_length_ptr += 2 * n_frlengths;
xfer->frbuffers = parm->xfer_page_cache_ptr;
parm->xfer_page_cache_ptr += n_frbuffers;
xfer->max_frame_count = xfer->nframes;
if (!xfer->flags.ext_buffer) {
#if USB_HAVE_BUSDMA
struct usb_page_search page_info;
struct usb_page_cache *pc;
if (usbd_transfer_setup_sub_malloc(parm,
&pc, parm->bufsize, 1, 1)) {
parm->err = USB_ERR_NOMEM;
} else if (parm->buf != NULL) {
usbd_get_page(pc, 0, &page_info);
xfer->local_buffer = page_info.buffer;
usbd_xfer_set_frame_offset(xfer, 0, 0);
if ((type == UE_CONTROL) && (n_frbuffers > 1)) {
usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1);
}
}
#else
parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
if (parm->buf != NULL) {
xfer->local_buffer =
USB_ADD_BYTES(parm->buf, parm->size[0]);
usbd_xfer_set_frame_offset(xfer, 0, 0);
if ((type == UE_CONTROL) && (n_frbuffers > 1)) {
usbd_xfer_set_frame_offset(xfer, REQ_SIZE, 1);
}
}
parm->size[0] += parm->bufsize;
parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
#endif
}
if (parm->bufsize_max < parm->bufsize) {
parm->bufsize_max = parm->bufsize;
}
#if USB_HAVE_BUSDMA
if (xfer->flags_int.bdma_enable) {
xfer->dma_page_ptr = parm->dma_page_ptr;
parm->dma_page_ptr += (2 * n_frbuffers);
parm->dma_page_ptr += (parm->bufsize / USB_PAGE_SIZE);
}
#endif
if (zmps) {
xfer->max_data_length = 0;
}
xfer->max_hc_frame_size =
(parm->hc_max_frame_size -
(parm->hc_max_frame_size % xfer->max_frame_size));
if (xfer->max_hc_frame_size == 0) {
parm->err = USB_ERR_INVAL;
goto done;
}
if (parm->buf) {
for (x = 0; x != n_frbuffers; x++) {
xfer->frbuffers[x].tag_parent =
&xfer->xroot->dma_parent_tag;
#if USB_HAVE_BUSDMA
if (xfer->flags_int.bdma_enable &&
(parm->bufsize_max > 0)) {
if (usb_pc_dmamap_create(
xfer->frbuffers + x,
parm->bufsize_max)) {
parm->err = USB_ERR_NOMEM;
goto done;
}
}
#endif
}
}
done:
if (parm->err) {
xfer->max_hc_frame_size = 1;
xfer->max_frame_size = 1;
xfer->max_packet_size = 1;
xfer->max_data_length = 0;
xfer->nframes = 0;
xfer->max_frame_count = 0;
}
}
static uint8_t
usbd_transfer_setup_has_bulk(const struct usb_config *setup_start,
uint16_t n_setup)
{
while (n_setup--) {
uint8_t type = setup_start[n_setup].type;
if (type == UE_BULK || type == UE_BULK_INTR ||
type == UE_TYPE_ANY)
return (1);
}
return (0);
}
usb_error_t
usbd_transfer_setup(struct usb_device *udev,
const uint8_t *ifaces, struct usb_xfer **ppxfer,
const struct usb_config *setup_start, uint16_t n_setup,
void *priv_sc, struct mtx *xfer_mtx)
{
const struct usb_config *setup_end = setup_start + n_setup;
const struct usb_config *setup;
struct usb_setup_params *parm;
struct usb_endpoint *ep;
struct usb_xfer_root *info;
struct usb_xfer *xfer;
void *buf = NULL;
usb_error_t error = 0;
uint16_t n;
uint16_t refcount;
uint8_t do_unlock;
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"usbd_transfer_setup can sleep!");
if (n_setup == 0) {
DPRINTFN(6, "setup array has zero length!\n");
return (USB_ERR_INVAL);
}
if (ifaces == NULL) {
DPRINTFN(6, "ifaces array is NULL!\n");
return (USB_ERR_INVAL);
}
if (xfer_mtx == NULL) {
DPRINTFN(6, "using global lock\n");
xfer_mtx = &Giant;
}
for (setup = setup_start, n = 0;
setup != setup_end; setup++, n++) {
if (setup->bufsize == (usb_frlength_t)-1) {
error = USB_ERR_BAD_BUFSIZE;
DPRINTF("invalid bufsize\n");
}
if (setup->callback == NULL) {
error = USB_ERR_NO_CALLBACK;
DPRINTF("no callback\n");
}
ppxfer[n] = NULL;
}
if (error)
return (error);
do_unlock = usbd_ctrl_lock(udev);
refcount = 0;
info = NULL;
parm = &udev->scratch.xfer_setup[0].parm;
memset(parm, 0, sizeof(*parm));
parm->udev = udev;
parm->speed = usbd_get_speed(udev);
parm->hc_max_packet_count = 1;
if (parm->speed >= USB_SPEED_MAX) {
parm->err = USB_ERR_INVAL;
goto done;
}
while (1) {
if (buf) {
info = USB_ADD_BYTES(buf, 0);
info->memory_base = buf;
info->memory_size = parm->size[0];
#if USB_HAVE_BUSDMA
info->dma_page_cache_start = USB_ADD_BYTES(buf, parm->size[4]);
info->dma_page_cache_end = USB_ADD_BYTES(buf, parm->size[5]);
#endif
info->xfer_page_cache_start = USB_ADD_BYTES(buf, parm->size[5]);
info->xfer_page_cache_end = USB_ADD_BYTES(buf, parm->size[2]);
cv_init(&info->cv_drain, "WDRAIN");
info->xfer_mtx = xfer_mtx;
#if USB_HAVE_BUSDMA
usb_dma_tag_setup(&info->dma_parent_tag,
parm->dma_tag_p, udev->bus->dma_parent_tag[0].tag,
xfer_mtx, &usb_bdma_done_event, udev->bus->dma_bits,
parm->dma_tag_max);
#endif
info->bus = udev->bus;
info->udev = udev;
TAILQ_INIT(&info->done_q.head);
info->done_q.command = &usbd_callback_wrapper;
#if USB_HAVE_BUSDMA
TAILQ_INIT(&info->dma_q.head);
info->dma_q.command = &usb_bdma_work_loop;
#endif
info->done_m[0].hdr.pm_callback = &usb_callback_proc;
info->done_m[0].xroot = info;
info->done_m[1].hdr.pm_callback = &usb_callback_proc;
info->done_m[1].xroot = info;
if (setup_start == usb_control_ep_cfg ||
setup_start == usb_control_ep_quirk_cfg)
info->done_p =
USB_BUS_CONTROL_XFER_PROC(udev->bus);
else if (xfer_mtx == &Giant)
info->done_p =
USB_BUS_GIANT_PROC(udev->bus);
else if (usbd_transfer_setup_has_bulk(setup_start, n_setup))
info->done_p =
USB_BUS_NON_GIANT_BULK_PROC(udev->bus);
else
info->done_p =
USB_BUS_NON_GIANT_ISOC_PROC(udev->bus);
}
parm->size[0] = 0;
parm->buf = buf;
parm->size[0] += sizeof(info[0]);
for (setup = setup_start, n = 0;
setup != setup_end; setup++, n++) {
if (setup->callback == NULL) {
continue;
}
ep = usbd_get_endpoint(udev,
ifaces[setup->if_index], setup);
if ((ep == NULL) || (ep->methods == NULL) ||
((ep->ep_mode != USB_EP_MODE_STREAMS) &&
(ep->ep_mode != USB_EP_MODE_DEFAULT)) ||
(setup->stream_id != 0 &&
(setup->stream_id >= USB_MAX_EP_STREAMS ||
(ep->ep_mode != USB_EP_MODE_STREAMS)))) {
if (setup->flags.no_pipe_ok)
continue;
if ((setup->usb_mode != USB_MODE_DUAL) &&
(setup->usb_mode != udev->flags.usb_mode))
continue;
parm->err = USB_ERR_NO_PIPE;
goto done;
}
parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
parm->curr_setup = setup;
if (buf) {
xfer = USB_ADD_BYTES(buf, parm->size[0]);
xfer->address = udev->address;
xfer->priv_sc = priv_sc;
xfer->xroot = info;
usb_callout_init_mtx(&xfer->timeout_handle,
&udev->bus->bus_mtx, 0);
} else {
xfer = &udev->scratch.xfer_setup[0].dummy;
memset(xfer, 0, sizeof(*xfer));
refcount++;
}
xfer->endpoint = ep;
xfer->stream_id = setup->stream_id;
parm->size[0] += sizeof(xfer[0]);
parm->methods = xfer->endpoint->methods;
parm->curr_xfer = xfer;
(udev->bus->methods->xfer_setup) (parm);
if (parm->err)
goto done;
if (buf) {
USB_BUS_LOCK(info->bus);
if (xfer->endpoint->refcount_alloc >= USB_EP_REF_MAX)
parm->err = USB_ERR_INVAL;
xfer->endpoint->refcount_alloc++;
if (xfer->endpoint->refcount_alloc == 0)
panic("usbd_transfer_setup(): Refcount wrapped to zero\n");
USB_BUS_UNLOCK(info->bus);
info->setup_refcount++;
ppxfer[n] = xfer;
}
if (parm->err)
goto done;
}
if (buf != NULL || parm->err != 0)
goto done;
if (refcount == 0)
goto done;
parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
parm->size[1] = parm->size[0];
parm->dma_tag_max += 3 * MIN(n_setup, USB_EP_MAX);
parm->dma_tag_max += 8;
parm->dma_tag_p += parm->dma_tag_max;
parm->size[0] += ((uint8_t *)parm->dma_tag_p) -
((uint8_t *)0);
parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
parm->size[3] = parm->size[0];
parm->size[0] += ((uint8_t *)parm->dma_page_ptr) -
((uint8_t *)0);
parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
parm->size[4] = parm->size[0];
parm->size[0] += ((uint8_t *)parm->dma_page_cache_ptr) -
((uint8_t *)0);
parm->size[5] = parm->size[0];
parm->size[0] += ((uint8_t *)parm->xfer_page_cache_ptr) -
((uint8_t *)0);
parm->size[2] = parm->size[0];
parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
parm->size[6] = parm->size[0];
parm->size[0] += ((uint8_t *)parm->xfer_length_ptr) -
((uint8_t *)0);
parm->size[0] += ((-parm->size[0]) & (USB_HOST_ALIGN - 1));
buf = malloc(parm->size[0], M_USB, M_WAITOK | M_ZERO);
#if (USB_HAVE_MALLOC_WAITOK == 0)
if (buf == NULL) {
parm->err = USB_ERR_NOMEM;
DPRINTFN(0, "cannot allocate memory block for "
"configuration (%d bytes)\n",
parm->size[0]);
goto done;
}
#endif
parm->dma_tag_p = USB_ADD_BYTES(buf, parm->size[1]);
parm->dma_page_ptr = USB_ADD_BYTES(buf, parm->size[3]);
parm->dma_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[4]);
parm->xfer_page_cache_ptr = USB_ADD_BYTES(buf, parm->size[5]);
parm->xfer_length_ptr = USB_ADD_BYTES(buf, parm->size[6]);
}
done:
if (buf) {
if (info->setup_refcount == 0) {
USB_BUS_LOCK(info->bus);
usbd_transfer_unsetup_sub(info, 0);
}
}
if (parm->err)
usbd_transfer_unsetup(ppxfer, n_setup);
error = parm->err;
if (do_unlock)
usbd_ctrl_unlock(udev);
return (error);
}
static void
usbd_transfer_unsetup_sub(struct usb_xfer_root *info, uint8_t needs_delay)
{
#if USB_HAVE_BUSDMA
struct usb_page_cache *pc;
#endif
USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED);
if (needs_delay) {
usb_timeout_t temp;
temp = usbd_get_dma_delay(info->udev);
if (temp != 0) {
usb_pause_mtx(&info->bus->bus_mtx,
USB_MS_TO_TICKS(temp));
}
}
usb_proc_mwait(info->done_p, &info->done_m[0], &info->done_m[1]);
USB_BUS_UNLOCK(info->bus);
#if USB_HAVE_BUSDMA
pc = info->dma_page_cache_start;
while (pc != info->dma_page_cache_end) {
usb_pc_free_mem(pc);
pc++;
}
pc = info->xfer_page_cache_start;
while (pc != info->xfer_page_cache_end) {
usb_pc_dmamap_destroy(pc);
pc++;
}
usb_dma_tag_unsetup(&info->dma_parent_tag);
#endif
cv_destroy(&info->cv_drain);
free(info->memory_base, M_USB);
}
void
usbd_transfer_unsetup(struct usb_xfer **pxfer, uint16_t n_setup)
{
struct usb_xfer *xfer;
struct usb_xfer_root *info;
uint8_t needs_delay = 0;
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"usbd_transfer_unsetup can sleep!");
while (n_setup--) {
xfer = pxfer[n_setup];
if (xfer == NULL)
continue;
info = xfer->xroot;
USB_XFER_LOCK(xfer);
USB_BUS_LOCK(info->bus);
pxfer[n_setup] = NULL;
USB_BUS_UNLOCK(info->bus);
USB_XFER_UNLOCK(xfer);
usbd_transfer_drain(xfer);
#if USB_HAVE_BUSDMA
if (xfer->flags_int.bdma_enable)
needs_delay = 1;
#endif
USB_BUS_LOCK(info->bus);
xfer->endpoint->refcount_alloc--;
USB_BUS_UNLOCK(info->bus);
usb_callout_drain(&xfer->timeout_handle);
USB_BUS_LOCK(info->bus);
USB_ASSERT(info->setup_refcount != 0, ("Invalid setup "
"reference count\n"));
info->setup_refcount--;
if (info->setup_refcount == 0) {
usbd_transfer_unsetup_sub(info,
needs_delay);
} else {
USB_BUS_UNLOCK(info->bus);
}
}
}
static void
usbd_control_transfer_init(struct usb_xfer *xfer)
{
struct usb_device_request req;
usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
xfer->flags_int.control_rem = UGETW(req.wLength);
xfer->endpointno &= ~(UE_DIR_IN | UE_DIR_OUT);
xfer->endpointno |=
(req.bmRequestType & UT_READ) ? UE_DIR_IN : UE_DIR_OUT;
}
static uint8_t
usbd_control_transfer_did_data(struct usb_xfer *xfer)
{
struct usb_device_request req;
if (xfer->flags_int.control_hdr != 0)
return (0);
usbd_copy_out(xfer->frbuffers, 0, &req, sizeof(req));
return (xfer->flags_int.control_rem != UGETW(req.wLength));
}
static int
usbd_setup_ctrl_transfer(struct usb_xfer *xfer)
{
usb_frlength_t len;
if (xfer->flags.stall_pipe && xfer->flags_int.control_act) {
xfer->flags_int.control_stall = 1;
xfer->flags_int.control_act = 0;
} else {
xfer->flags_int.control_stall = 0;
}
if (xfer->nframes > 2) {
DPRINTFN(0, "Too many frames: %u\n",
(unsigned)xfer->nframes);
goto error;
}
if (xfer->flags_int.control_act) {
if (xfer->flags_int.control_hdr) {
xfer->flags_int.control_hdr = 0;
if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
usbd_control_transfer_init(xfer);
}
}
len = xfer->sumlen;
} else {
if (xfer->frlengths[0] != sizeof(struct usb_device_request)) {
DPRINTFN(0, "Wrong framelength %u != %zu\n",
xfer->frlengths[0], sizeof(struct
usb_device_request));
goto error;
}
if (xfer->flags_int.usb_mode == USB_MODE_DEVICE) {
if (xfer->nframes != 1) {
DPRINTF("Misconfigured transfer\n");
goto error;
}
xfer->flags_int.control_rem = 0xFFFF;
} else {
usbd_control_transfer_init(xfer);
}
xfer->flags_int.control_hdr = 1;
len = (xfer->sumlen - sizeof(struct usb_device_request));
}
xfer->flags_int.control_did_data =
usbd_control_transfer_did_data(xfer);
if (len > xfer->flags_int.control_rem) {
DPRINTFN(0, "Length (%d) greater than "
"remaining length (%d)\n", len,
xfer->flags_int.control_rem);
goto error;
}
if (xfer->flags.force_short_xfer) {
xfer->flags_int.control_rem = 0;
} else {
if ((len != xfer->max_data_length) &&
(len != xfer->flags_int.control_rem) &&
(xfer->nframes != 1)) {
DPRINTFN(0, "Short control transfer without "
"force_short_xfer set\n");
goto error;
}
xfer->flags_int.control_rem -= len;
}
if ((xfer->flags_int.control_rem > 0) ||
(xfer->flags.manual_status)) {
xfer->flags_int.control_act = 1;
if ((!xfer->flags_int.control_hdr) &&
(xfer->nframes == 1)) {
DPRINTFN(0, "Invalid parameter "
"combination\n");
goto error;
}
} else {
xfer->flags_int.control_act = 0;
}
return (0);
error:
return (1);
}
void
usbd_transfer_submit(struct usb_xfer *xfer)
{
struct usb_xfer_root *info;
struct usb_bus *bus;
usb_frcount_t x;
info = xfer->xroot;
bus = info->bus;
DPRINTF("xfer=%p, endpoint=%p, nframes=%d, dir=%s\n",
xfer, xfer->endpoint, xfer->nframes, USB_GET_DATA_ISREAD(xfer) ?
"read" : "write");
#ifdef USB_DEBUG
if (USB_DEBUG_VAR > 0) {
USB_BUS_LOCK(bus);
usb_dump_endpoint(xfer->endpoint);
USB_BUS_UNLOCK(bus);
}
#endif
USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
USB_BUS_LOCK_ASSERT(bus, MA_NOTOWNED);
if (!xfer->flags_int.open) {
xfer->flags_int.open = 1;
DPRINTF("open\n");
USB_BUS_LOCK(bus);
(xfer->endpoint->methods->open) (xfer);
USB_BUS_UNLOCK(bus);
}
xfer->flags_int.transferring = 1;
#if USB_HAVE_POWERD
usbd_transfer_power_ref(xfer, 1);
#endif
if (xfer->wait_queue) {
USB_BUS_LOCK(bus);
usbd_transfer_dequeue(xfer);
USB_BUS_UNLOCK(bus);
}
xfer->flags_int.did_dma_delay = 0;
xfer->flags_int.did_close = 0;
#if USB_HAVE_BUSDMA
xfer->flags_int.bdma_setup = 0;
#endif
xfer->flags_int.can_cancel_immed = 0;
xfer->sumlen = 0;
xfer->actlen = 0;
xfer->aframes = 0;
xfer->error = 0;
if (info->udev->state < USB_STATE_POWERED) {
USB_BUS_LOCK(bus);
usbd_transfer_done(xfer, USB_ERR_CANCELLED);
USB_BUS_UNLOCK(bus);
return;
}
if (xfer->nframes == 0) {
if (xfer->flags.stall_pipe) {
DPRINTF("xfer=%p nframes=0: stall "
"or clear stall!\n", xfer);
USB_BUS_LOCK(bus);
xfer->flags_int.can_cancel_immed = 1;
usb_command_wrapper(&xfer->endpoint->
endpoint_q[xfer->stream_id], xfer);
USB_BUS_UNLOCK(bus);
return;
}
USB_BUS_LOCK(bus);
usbd_transfer_done(xfer, USB_ERR_INVAL);
USB_BUS_UNLOCK(bus);
return;
}
for (x = 0; x != xfer->nframes; x++) {
xfer->frlengths[x + xfer->max_frame_count] = xfer->frlengths[x];
xfer->sumlen += xfer->frlengths[x];
if (xfer->sumlen < xfer->frlengths[x]) {
USB_BUS_LOCK(bus);
usbd_transfer_done(xfer, USB_ERR_INVAL);
USB_BUS_UNLOCK(bus);
return;
}
}
xfer->flags_int.short_xfer_ok = 0;
xfer->flags_int.short_frames_ok = 0;
if (xfer->flags_int.control_xfr) {
if (usbd_setup_ctrl_transfer(xfer)) {
USB_BUS_LOCK(bus);
usbd_transfer_done(xfer, USB_ERR_STALLED);
USB_BUS_UNLOCK(bus);
return;
}
}
if (USB_GET_DATA_ISREAD(xfer)) {
if (xfer->flags.short_frames_ok) {
xfer->flags_int.short_xfer_ok = 1;
xfer->flags_int.short_frames_ok = 1;
} else if (xfer->flags.short_xfer_ok) {
xfer->flags_int.short_xfer_ok = 1;
if (xfer->flags_int.control_xfr) {
xfer->flags_int.short_frames_ok = 1;
}
}
}
#if USB_HAVE_BUSDMA
if (xfer->flags_int.bdma_enable) {
usb_command_wrapper(&xfer->xroot->dma_q, xfer);
return;
}
#endif
usbd_pipe_enter(xfer);
}
void
usbd_pipe_enter(struct usb_xfer *xfer)
{
struct usb_endpoint *ep;
USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
USB_BUS_LOCK(xfer->xroot->bus);
ep = xfer->endpoint;
DPRINTF("enter\n");
xfer->flags_int.can_cancel_immed = 1;
(ep->methods->enter) (xfer);
if (xfer->error) {
usbd_transfer_done(xfer, 0);
USB_BUS_UNLOCK(xfer->xroot->bus);
return;
}
usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], xfer);
USB_BUS_UNLOCK(xfer->xroot->bus);
}
void
usbd_transfer_start(struct usb_xfer *xfer)
{
if (xfer == NULL) {
return;
}
USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
if (!xfer->flags_int.started) {
USB_BUS_LOCK(xfer->xroot->bus);
xfer->flags_int.started = 1;
USB_BUS_UNLOCK(xfer->xroot->bus);
}
if (xfer->flags_int.transferring) {
return;
}
USB_BUS_LOCK(xfer->xroot->bus);
usbd_callback_ss_done_defer(xfer);
USB_BUS_UNLOCK(xfer->xroot->bus);
}
void
usbd_transfer_stop(struct usb_xfer *xfer)
{
struct usb_endpoint *ep;
if (xfer == NULL) {
return;
}
USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
if (!xfer->flags_int.open) {
if (xfer->flags_int.started) {
USB_BUS_LOCK(xfer->xroot->bus);
xfer->flags_int.started = 0;
USB_BUS_UNLOCK(xfer->xroot->bus);
}
return;
}
USB_BUS_LOCK(xfer->xroot->bus);
xfer->error = USB_ERR_CANCELLED;
xfer->flags_int.open = 0;
xfer->flags_int.started = 0;
if (xfer->flags_int.transferring) {
if (xfer->flags_int.can_cancel_immed &&
(!xfer->flags_int.did_close)) {
DPRINTF("close\n");
(xfer->endpoint->methods->close) (xfer);
xfer->flags_int.did_close = 1;
} else {
}
} else {
DPRINTF("close\n");
(xfer->endpoint->methods->close) (xfer);
ep = xfer->endpoint;
if (ep->endpoint_q[xfer->stream_id].curr == xfer) {
usb_command_wrapper(
&ep->endpoint_q[xfer->stream_id], NULL);
}
}
USB_BUS_UNLOCK(xfer->xroot->bus);
}
uint8_t
usbd_transfer_pending(struct usb_xfer *xfer)
{
struct usb_xfer_root *info;
struct usb_xfer_queue *pq;
if (xfer == NULL) {
return (0);
}
USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
if (xfer->flags_int.transferring) {
return (1);
}
USB_BUS_LOCK(xfer->xroot->bus);
if (xfer->wait_queue) {
USB_BUS_UNLOCK(xfer->xroot->bus);
return (1);
}
info = xfer->xroot;
pq = &info->done_q;
if (pq->curr == xfer) {
USB_BUS_UNLOCK(xfer->xroot->bus);
return (1);
}
USB_BUS_UNLOCK(xfer->xroot->bus);
return (0);
}
void
usbd_transfer_drain(struct usb_xfer *xfer)
{
WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL,
"usbd_transfer_drain can sleep!");
if (xfer == NULL) {
return;
}
if (xfer->xroot->xfer_mtx != &Giant) {
USB_XFER_LOCK_ASSERT(xfer, MA_NOTOWNED);
}
USB_XFER_LOCK(xfer);
usbd_transfer_stop(xfer);
while (usbd_transfer_pending(xfer) ||
xfer->flags_int.doing_callback) {
xfer->flags_int.draining = 1;
cv_wait(&xfer->xroot->cv_drain, xfer->xroot->xfer_mtx);
}
USB_XFER_UNLOCK(xfer);
}
struct usb_page_cache *
usbd_xfer_get_frame(struct usb_xfer *xfer, usb_frcount_t frindex)
{
KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
return (&xfer->frbuffers[frindex]);
}
void *
usbd_xfer_get_frame_buffer(struct usb_xfer *xfer, usb_frcount_t frindex)
{
struct usb_page_search page_info;
KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
usbd_get_page(&xfer->frbuffers[frindex], 0, &page_info);
return (page_info.buffer);
}
uint8_t
usbd_xfer_get_fps_shift(struct usb_xfer *xfer)
{
return (xfer->fps_shift);
}
usb_frlength_t
usbd_xfer_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex)
{
KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
return (xfer->frlengths[frindex]);
}
void
usbd_xfer_set_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
void *ptr, usb_frlength_t len)
{
KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
xfer->frbuffers[frindex].buffer = ptr;
usbd_xfer_set_frame_len(xfer, frindex, len);
}
void
usbd_xfer_frame_data(struct usb_xfer *xfer, usb_frcount_t frindex,
void **ptr, int *len)
{
KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
if (ptr != NULL)
*ptr = xfer->frbuffers[frindex].buffer;
if (len != NULL)
*len = xfer->frlengths[frindex];
}
usb_frlength_t
usbd_xfer_old_frame_length(struct usb_xfer *xfer, usb_frcount_t frindex)
{
KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
return (xfer->frlengths[frindex + xfer->max_frame_count]);
}
void
usbd_xfer_status(struct usb_xfer *xfer, int *actlen, int *sumlen, int *aframes,
int *nframes)
{
if (actlen != NULL)
*actlen = xfer->actlen;
if (sumlen != NULL)
*sumlen = xfer->sumlen;
if (aframes != NULL)
*aframes = xfer->aframes;
if (nframes != NULL)
*nframes = xfer->nframes;
}
void
usbd_xfer_set_frame_offset(struct usb_xfer *xfer, usb_frlength_t offset,
usb_frcount_t frindex)
{
KASSERT(!xfer->flags.ext_buffer, ("Cannot offset data frame "
"when the USB buffer is external\n"));
KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
xfer->frbuffers[frindex].buffer =
USB_ADD_BYTES(xfer->local_buffer, offset);
}
void
usbd_xfer_set_interval(struct usb_xfer *xfer, int i)
{
xfer->interval = i;
}
void
usbd_xfer_set_timeout(struct usb_xfer *xfer, int t)
{
xfer->timeout = t;
}
void
usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n)
{
xfer->nframes = n;
}
usb_frcount_t
usbd_xfer_max_frames(struct usb_xfer *xfer)
{
return (xfer->max_frame_count);
}
usb_frlength_t
usbd_xfer_max_len(struct usb_xfer *xfer)
{
return (xfer->max_data_length);
}
usb_frlength_t
usbd_xfer_max_framelen(struct usb_xfer *xfer)
{
return (xfer->max_frame_size);
}
void
usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex,
usb_frlength_t len)
{
KASSERT(frindex < xfer->max_frame_count, ("frame index overflow"));
xfer->frlengths[frindex] = len;
}
static void
usb_callback_proc(struct usb_proc_msg *_pm)
{
struct usb_done_msg *pm = (void *)_pm;
struct usb_xfer_root *info = pm->xroot;
USB_BUS_UNLOCK(info->bus);
USB_MTX_LOCK(info->xfer_mtx);
USB_BUS_LOCK(info->bus);
usb_command_wrapper(&info->done_q,
info->done_q.curr);
USB_MTX_UNLOCK(info->xfer_mtx);
}
static void
usbd_callback_ss_done_defer(struct usb_xfer *xfer)
{
struct usb_xfer_root *info = xfer->xroot;
struct usb_xfer_queue *pq = &info->done_q;
USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
if (pq->curr != xfer) {
usbd_transfer_enqueue(pq, xfer);
}
if (!pq->recurse_1) {
(void) usb_proc_msignal(info->done_p,
&info->done_m[0], &info->done_m[1]);
} else {
pq->recurse_2 = 0;
}
return;
}
static void
usbd_callback_wrapper(struct usb_xfer_queue *pq)
{
struct usb_xfer *xfer = pq->curr;
struct usb_xfer_root *info = xfer->xroot;
USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED);
if ((pq->recurse_3 != 0 || mtx_owned(info->xfer_mtx) == 0) &&
USB_IN_POLLING_MODE_FUNC() == 0) {
DPRINTFN(3, "case 5 and 6\n");
(void) usb_proc_msignal(info->done_p,
&info->done_m[0], &info->done_m[1]);
return;
}
DPRINTFN(3, "case 1-4\n");
info->done_q.curr = NULL;
xfer->flags_int.doing_callback = 1;
USB_BUS_UNLOCK(info->bus);
USB_BUS_LOCK_ASSERT(info->bus, MA_NOTOWNED);
if (!xfer->flags_int.transferring) {
xfer->usb_state = USB_ST_SETUP;
if (!xfer->flags_int.started) {
USB_BUS_LOCK(info->bus);
goto done;
}
} else {
if (usbd_callback_wrapper_sub(xfer)) {
USB_BUS_LOCK(info->bus);
goto done;
}
#if USB_HAVE_POWERD
usbd_transfer_power_ref(xfer, -1);
#endif
xfer->flags_int.transferring = 0;
if (xfer->error) {
xfer->usb_state = USB_ST_ERROR;
} else {
xfer->usb_state = USB_ST_TRANSFERRED;
#if USB_HAVE_BUSDMA
if (xfer->flags_int.bdma_enable &&
(!xfer->flags_int.bdma_no_post_sync)) {
usb_bdma_post_sync(xfer);
}
#endif
}
}
#if USB_HAVE_PF
if (xfer->usb_state != USB_ST_SETUP) {
USB_BUS_LOCK(info->bus);
usbpf_xfertap(xfer, USBPF_XFERTAP_DONE);
USB_BUS_UNLOCK(info->bus);
}
#endif
(xfer->callback) (xfer, xfer->error);
USB_BUS_LOCK(info->bus);
if ((!xfer->flags_int.open) &&
(xfer->flags_int.started) &&
(xfer->usb_state == USB_ST_ERROR)) {
xfer->flags_int.doing_callback = 0;
usb_command_wrapper(&info->done_q, xfer);
return;
}
done:
xfer->flags_int.doing_callback = 0;
if (xfer->flags_int.draining &&
(!xfer->flags_int.transferring)) {
xfer->flags_int.draining = 0;
cv_broadcast(&info->cv_drain);
}
usb_command_wrapper(&info->done_q,
info->done_q.curr);
}
void
usb_dma_delay_done_cb(struct usb_xfer *xfer)
{
USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
DPRINTFN(3, "Completed %p\n", xfer);
usbd_transfer_done(xfer, 0);
}
void
usbd_transfer_dequeue(struct usb_xfer *xfer)
{
struct usb_xfer_queue *pq;
pq = xfer->wait_queue;
if (pq) {
TAILQ_REMOVE(&pq->head, xfer, wait_entry);
xfer->wait_queue = NULL;
}
}
void
usbd_transfer_enqueue(struct usb_xfer_queue *pq, struct usb_xfer *xfer)
{
if (xfer->wait_queue == NULL) {
xfer->wait_queue = pq;
TAILQ_INSERT_TAIL(&pq->head, xfer, wait_entry);
}
}
void
usbd_transfer_done(struct usb_xfer *xfer, usb_error_t error)
{
struct usb_xfer_root *info = xfer->xroot;
USB_BUS_LOCK_ASSERT(info->bus, MA_OWNED);
DPRINTF("err=%s\n", usbd_errstr(error));
if (!xfer->flags_int.transferring) {
DPRINTF("not transferring\n");
xfer->flags_int.control_act = 0;
return;
}
if (xfer->error == USB_ERR_NORMAL_COMPLETION)
xfer->error = error;
usb_callout_stop(&xfer->timeout_handle);
usbd_transfer_dequeue(xfer);
#if USB_HAVE_BUSDMA
if (mtx_owned(info->xfer_mtx)) {
struct usb_xfer_queue *pq;
pq = &info->dma_q;
if (pq->curr == xfer) {
usb_command_wrapper(pq, NULL);
}
}
#endif
if (xfer->error == USB_ERR_CANCELLED) {
info->udev->stats_cancelled.uds_requests
[xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++;
} else if (xfer->error != USB_ERR_NORMAL_COMPLETION) {
info->udev->stats_err.uds_requests
[xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++;
} else {
info->udev->stats_ok.uds_requests
[xfer->endpoint->edesc->bmAttributes & UE_XFERTYPE]++;
}
usbd_callback_ss_done_defer(xfer);
}
static void
usbd_transfer_start_cb(void *arg)
{
struct usb_xfer *xfer = arg;
struct usb_endpoint *ep = xfer->endpoint;
USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
DPRINTF("start\n");
#if USB_HAVE_PF
usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT);
#endif
xfer->flags_int.can_cancel_immed = 1;
if (xfer->error == 0)
(ep->methods->start) (xfer);
if (xfer->error) {
usbd_transfer_done(xfer, 0);
}
}
void
usbd_xfer_set_zlp(struct usb_xfer *xfer)
{
if (xfer == NULL) {
return;
}
USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
USB_BUS_LOCK(xfer->xroot->bus);
xfer->flags.send_zlp = 1;
USB_BUS_UNLOCK(xfer->xroot->bus);
}
uint8_t
usbd_xfer_get_and_clr_zlp(struct usb_xfer *xfer)
{
uint8_t retval;
if (xfer == NULL) {
return (0);
}
USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
retval = xfer->flags.send_zlp;
if (retval != 0) {
DPRINTFN(1, "Sending zero-length packet.\n");
USB_BUS_LOCK(xfer->xroot->bus);
xfer->flags.send_zlp = 0;
USB_BUS_UNLOCK(xfer->xroot->bus);
usbd_xfer_set_frame_len(xfer, 0, 0);
usbd_xfer_set_frames(xfer, 1);
usbd_transfer_submit(xfer);
}
return (retval);
}
void
usbd_xfer_set_stall(struct usb_xfer *xfer)
{
if (xfer == NULL) {
return;
}
USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
USB_BUS_LOCK(xfer->xroot->bus);
xfer->flags.stall_pipe = 1;
USB_BUS_UNLOCK(xfer->xroot->bus);
}
int
usbd_xfer_is_stalled(struct usb_xfer *xfer)
{
return (xfer->endpoint->is_stalled);
}
void
usbd_transfer_clear_stall(struct usb_xfer *xfer)
{
if (xfer == NULL) {
return;
}
USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
USB_BUS_LOCK(xfer->xroot->bus);
xfer->flags.stall_pipe = 0;
USB_BUS_UNLOCK(xfer->xroot->bus);
}
void
usbd_pipe_start(struct usb_xfer_queue *pq)
{
struct usb_endpoint *ep;
struct usb_xfer *xfer;
uint8_t type;
xfer = pq->curr;
ep = xfer->endpoint;
USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
if (ep->is_stalled) {
return;
}
if (xfer->flags.stall_pipe) {
struct usb_device *udev;
struct usb_xfer_root *info;
xfer->flags.stall_pipe = 0;
info = xfer->xroot;
udev = info->udev;
type = (ep->edesc->bmAttributes & UE_XFERTYPE);
if ((type == UE_BULK) ||
(type == UE_INTERRUPT)) {
uint8_t did_stall;
did_stall = 1;
if (udev->flags.usb_mode == USB_MODE_DEVICE) {
(udev->bus->methods->set_stall) (
udev, ep, &did_stall);
} else if (udev->ctrl_xfer[1]) {
info = udev->ctrl_xfer[1]->xroot;
usb_proc_msignal(
USB_BUS_CS_PROC(info->bus),
&udev->cs_msg[0], &udev->cs_msg[1]);
} else {
DPRINTFN(0, "No stall handler\n");
}
if (did_stall) {
ep->is_stalled = 1;
return;
}
} else if (type == UE_ISOCHRONOUS) {
if (udev->flags.usb_mode == USB_MODE_DEVICE) {
(udev->bus->methods->clear_stall) (udev, ep);
}
}
}
if (xfer->nframes == 0) {
xfer->aframes = 0;
usbd_transfer_done(xfer, 0);
return;
}
if (xfer->interval > 0) {
type = (ep->edesc->bmAttributes & UE_XFERTYPE);
if ((type == UE_BULK) ||
(type == UE_CONTROL)) {
usbd_transfer_timeout_ms(xfer,
&usbd_transfer_start_cb,
xfer->interval);
return;
}
}
DPRINTF("start\n");
#if USB_HAVE_PF
usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT);
#endif
xfer->flags_int.can_cancel_immed = 1;
if (xfer->error == 0)
(ep->methods->start) (xfer);
if (xfer->error) {
usbd_transfer_done(xfer, 0);
}
}
void
usbd_transfer_timeout_ms(struct usb_xfer *xfer,
void (*cb) (void *arg), usb_timeout_t ms)
{
USB_BUS_LOCK_ASSERT(xfer->xroot->bus, MA_OWNED);
usb_callout_reset(&xfer->timeout_handle,
USB_MS_TO_TICKS(ms) + USB_CALLOUT_ZERO_TICKS, cb, xfer);
}
static uint8_t
usbd_callback_wrapper_sub(struct usb_xfer *xfer)
{
struct usb_endpoint *ep;
struct usb_bus *bus;
usb_frcount_t x;
bus = xfer->xroot->bus;
if ((!xfer->flags_int.open) &&
(!xfer->flags_int.did_close)) {
DPRINTF("close\n");
USB_BUS_LOCK(bus);
(xfer->endpoint->methods->close) (xfer);
USB_BUS_UNLOCK(bus);
xfer->flags_int.did_close = 1;
return (1);
}
if (xfer->error != 0 && !xfer->flags_int.did_dma_delay &&
(xfer->error == USB_ERR_CANCELLED ||
xfer->error == USB_ERR_TIMEOUT ||
bus->methods->start_dma_delay != NULL)) {
usb_timeout_t temp;
xfer->flags_int.did_dma_delay = 1;
xfer->flags_int.can_cancel_immed = 0;
temp = usbd_get_dma_delay(xfer->xroot->udev);
DPRINTFN(3, "DMA delay, %u ms, "
"on %p\n", temp, xfer);
if (temp != 0) {
USB_BUS_LOCK(bus);
if (bus->methods->start_dma_delay != NULL) {
(bus->methods->start_dma_delay) (xfer);
} else {
usbd_transfer_timeout_ms(xfer,
(void (*)(void *))&usb_dma_delay_done_cb,
temp);
}
USB_BUS_UNLOCK(bus);
return (1);
}
}
if (xfer->aframes > xfer->nframes) {
if (xfer->error == 0) {
panic("%s: actual number of frames, %d, is "
"greater than initial number of frames, %d\n",
__FUNCTION__, xfer->aframes, xfer->nframes);
} else {
xfer->aframes = xfer->nframes;
}
}
xfer->actlen = 0;
for (x = 0; x != xfer->aframes; x++) {
xfer->actlen += xfer->frlengths[x];
}
for (; x < xfer->nframes; x++) {
usbd_xfer_set_frame_len(xfer, x, 0);
}
if (xfer->actlen > xfer->sumlen) {
if (xfer->error == 0) {
panic("%s: actual length, %d, is greater than "
"initial length, %d\n",
__FUNCTION__, xfer->actlen, xfer->sumlen);
} else {
xfer->actlen = xfer->sumlen;
}
}
DPRINTFN(1, "xfer=%p endpoint=%p sts=%d alen=%d, slen=%d, afrm=%d, nfrm=%d\n",
xfer, xfer->endpoint, xfer->error, xfer->actlen, xfer->sumlen,
xfer->aframes, xfer->nframes);
if (xfer->error) {
xfer->flags_int.control_act = 0;
#if USB_HAVE_TT_SUPPORT
switch (xfer->error) {
case USB_ERR_NORMAL_COMPLETION:
case USB_ERR_SHORT_XFER:
case USB_ERR_STALLED:
case USB_ERR_CANCELLED:
break;
default:
USB_BUS_LOCK(bus);
uhub_tt_buffer_reset_async_locked(xfer->xroot->udev, xfer->endpoint);
USB_BUS_UNLOCK(bus);
break;
}
#endif
if ((xfer->error != USB_ERR_CANCELLED) &&
(xfer->flags.pipe_bof)) {
DPRINTFN(2, "xfer=%p: Block On Failure "
"on endpoint=%p\n", xfer, xfer->endpoint);
goto done;
}
} else {
if (xfer->actlen < xfer->sumlen) {
xfer->flags_int.control_act = 0;
if (!xfer->flags_int.short_xfer_ok) {
xfer->error = USB_ERR_SHORT_XFER;
if (xfer->flags.pipe_bof) {
DPRINTFN(2, "xfer=%p: Block On Failure on "
"Short Transfer on endpoint %p.\n",
xfer, xfer->endpoint);
goto done;
}
}
} else {
if (xfer->flags_int.control_act) {
DPRINTFN(5, "xfer=%p: Control transfer "
"active on endpoint=%p\n", xfer, xfer->endpoint);
goto done;
}
}
}
ep = xfer->endpoint;
USB_BUS_LOCK(bus);
if (ep->endpoint_q[xfer->stream_id].curr == xfer) {
usb_command_wrapper(&ep->endpoint_q[xfer->stream_id], NULL);
if (ep->endpoint_q[xfer->stream_id].curr != NULL ||
TAILQ_FIRST(&ep->endpoint_q[xfer->stream_id].head) != NULL) {
} else {
xfer->endpoint->is_synced = 0;
}
}
USB_BUS_UNLOCK(bus);
done:
return (0);
}
void
usb_command_wrapper(struct usb_xfer_queue *pq, struct usb_xfer *xfer)
{
if (xfer) {
if (pq->curr != xfer) {
usbd_transfer_enqueue(pq, xfer);
if (pq->curr != NULL) {
DPRINTFN(6, "busy %p\n", pq->curr);
return;
}
}
} else {
pq->curr = NULL;
}
if (!pq->recurse_1) {
pq->recurse_3 = 0;
do {
pq->recurse_1 = 1;
pq->recurse_2 = 1;
if (pq->curr == NULL) {
xfer = TAILQ_FIRST(&pq->head);
if (xfer) {
TAILQ_REMOVE(&pq->head, xfer,
wait_entry);
xfer->wait_queue = NULL;
pq->curr = xfer;
} else {
break;
}
}
DPRINTFN(6, "cb %p (enter)\n", pq->curr);
(pq->command) (pq);
DPRINTFN(6, "cb %p (leave)\n", pq->curr);
pq->recurse_3 = 1;
} while (!pq->recurse_2);
pq->recurse_1 = 0;
} else {
pq->recurse_2 = 0;
}
}
void
usbd_ctrl_transfer_setup(struct usb_device *udev)
{
struct usb_xfer *xfer;
uint8_t no_resetup;
uint8_t iface_index;
if (udev->parent_hub == NULL)
return;
repeat:
xfer = udev->ctrl_xfer[0];
if (xfer) {
USB_XFER_LOCK(xfer);
no_resetup =
((xfer->address == udev->address) &&
(udev->ctrl_ep_desc.wMaxPacketSize[0] ==
udev->ddesc.bMaxPacketSize));
if (udev->flags.usb_mode == USB_MODE_DEVICE) {
if (no_resetup) {
usbd_transfer_start(xfer);
}
}
USB_XFER_UNLOCK(xfer);
} else {
no_resetup = 0;
}
if (no_resetup) {
return;
}
udev->ctrl_ep_desc.wMaxPacketSize[0] =
udev->ddesc.bMaxPacketSize;
usbd_transfer_unsetup(udev->ctrl_xfer, USB_CTRL_XFER_MAX);
udev->clear_stall_errors = 0;
iface_index = 0;
if (usbd_transfer_setup(udev, &iface_index,
udev->ctrl_xfer, udev->bus->control_ep_quirk ?
usb_control_ep_quirk_cfg : usb_control_ep_cfg, USB_CTRL_XFER_MAX, NULL,
&udev->device_mtx)) {
DPRINTFN(0, "could not setup default "
"USB transfer\n");
} else {
goto repeat;
}
}
void
usbd_clear_stall_locked(struct usb_device *udev, struct usb_endpoint *ep)
{
USB_BUS_LOCK_ASSERT(udev->bus, MA_OWNED);
if (udev->flags.usb_mode == USB_MODE_HOST &&
udev->parent_hub != NULL &&
udev->bus->methods->clear_stall != NULL &&
ep->methods != NULL) {
(udev->bus->methods->clear_stall) (udev, ep);
}
}
void
usbd_clear_data_toggle(struct usb_device *udev, struct usb_endpoint *ep)
{
DPRINTFN(5, "udev=%p endpoint=%p\n", udev, ep);
USB_BUS_LOCK(udev->bus);
ep->toggle_next = 0;
usbd_clear_stall_locked(udev, ep);
USB_BUS_UNLOCK(udev->bus);
}
uint8_t
usbd_clear_stall_callback(struct usb_xfer *xfer1,
struct usb_xfer *xfer2)
{
struct usb_device_request req;
if (xfer2 == NULL) {
DPRINTF("NULL input parameter\n");
return (0);
}
USB_XFER_LOCK_ASSERT(xfer1, MA_OWNED);
USB_XFER_LOCK_ASSERT(xfer2, MA_OWNED);
switch (USB_GET_STATE(xfer1)) {
case USB_ST_SETUP:
usbd_clear_data_toggle(xfer2->xroot->udev, xfer2->endpoint);
req.bmRequestType = UT_WRITE_ENDPOINT;
req.bRequest = UR_CLEAR_FEATURE;
USETW(req.wValue, UF_ENDPOINT_HALT);
req.wIndex[0] = xfer2->endpoint->edesc->bEndpointAddress;
req.wIndex[1] = 0;
USETW(req.wLength, 0);
usbd_copy_in(xfer1->frbuffers, 0, &req, sizeof(req));
xfer1->frlengths[0] = sizeof(req);
xfer1->nframes = 1;
usbd_transfer_submit(xfer1);
return (0);
case USB_ST_TRANSFERRED:
break;
default:
if (xfer1->error == USB_ERR_CANCELLED) {
return (0);
}
break;
}
return (1);
}
void
usbd_transfer_poll(struct usb_xfer **ppxfer, uint16_t max)
{
struct usb_xfer *xfer;
struct usb_xfer_root *xroot;
struct usb_device *udev;
struct usb_proc_msg *pm;
struct usb_bus *bus;
uint16_t n;
uint16_t drop_bus_spin;
uint16_t drop_bus;
uint16_t drop_xfer;
for (n = 0; n != max; n++) {
xfer = ppxfer[n];
if (xfer == NULL)
continue;
xroot = xfer->xroot;
if (xroot == NULL)
continue;
udev = xroot->udev;
if (udev == NULL)
continue;
bus = udev->bus;
if (bus == NULL)
continue;
if (bus->methods == NULL)
continue;
if (bus->methods->xfer_poll == NULL)
continue;
drop_bus_spin = 0;
drop_bus = 0;
drop_xfer = 0;
if (USB_IN_POLLING_MODE_FUNC() == 0) {
while (mtx_owned(&bus->bus_spin_lock)) {
mtx_unlock_spin(&bus->bus_spin_lock);
drop_bus_spin++;
}
while (mtx_owned(&bus->bus_mtx)) {
mtx_unlock(&bus->bus_mtx);
drop_bus++;
}
while (mtx_owned(xroot->xfer_mtx)) {
mtx_unlock(xroot->xfer_mtx);
drop_xfer++;
}
}
USB_BUS_CONTROL_XFER_PROC(bus)->up_msleep = 0;
USB_BUS_EXPLORE_PROC(bus)->up_msleep = 0;
USB_BUS_GIANT_PROC(bus)->up_msleep = 0;
USB_BUS_NON_GIANT_ISOC_PROC(bus)->up_msleep = 0;
USB_BUS_NON_GIANT_BULK_PROC(bus)->up_msleep = 0;
(bus->methods->xfer_poll) (bus);
USB_BUS_LOCK(xroot->bus);
if (udev->ctrl_xfer[1] != NULL) {
pm = &udev->cs_msg[0].hdr;
(pm->pm_callback) (pm);
pm = &udev->ctrl_xfer[1]->
xroot->done_m[0].hdr;
(pm->pm_callback) (pm);
}
pm = &xroot->done_m[0].hdr;
(pm->pm_callback) (pm);
USB_BUS_UNLOCK(xroot->bus);
while (drop_xfer--)
mtx_lock(xroot->xfer_mtx);
while (drop_bus--)
mtx_lock(&bus->bus_mtx);
while (drop_bus_spin--)
mtx_lock_spin(&bus->bus_spin_lock);
}
}
static void
usbd_get_std_packet_size(struct usb_std_packet_size *ptr,
uint8_t type, enum usb_dev_speed speed)
{
static const uint16_t intr_range_max[USB_SPEED_MAX] = {
[USB_SPEED_LOW] = 8,
[USB_SPEED_FULL] = 64,
[USB_SPEED_HIGH] = 1024,
[USB_SPEED_VARIABLE] = 1024,
[USB_SPEED_SUPER] = 1024,
};
static const uint16_t isoc_range_max[USB_SPEED_MAX] = {
[USB_SPEED_LOW] = 0,
[USB_SPEED_FULL] = 1023,
[USB_SPEED_HIGH] = 1024,
[USB_SPEED_VARIABLE] = 3584,
[USB_SPEED_SUPER] = 1024,
};
static const uint16_t control_min[USB_SPEED_MAX] = {
[USB_SPEED_LOW] = 8,
[USB_SPEED_FULL] = 8,
[USB_SPEED_HIGH] = 64,
[USB_SPEED_VARIABLE] = 512,
[USB_SPEED_SUPER] = 512,
};
static const uint16_t bulk_min[USB_SPEED_MAX] = {
[USB_SPEED_LOW] = 8,
[USB_SPEED_FULL] = 8,
[USB_SPEED_HIGH] = 512,
[USB_SPEED_VARIABLE] = 512,
[USB_SPEED_SUPER] = 1024,
};
uint16_t temp;
memset(ptr, 0, sizeof(*ptr));
switch (type) {
case UE_INTERRUPT:
ptr->range.max = intr_range_max[speed];
break;
case UE_ISOCHRONOUS:
ptr->range.max = isoc_range_max[speed];
break;
default:
if (type == UE_BULK)
temp = bulk_min[speed];
else
temp = control_min[speed];
ptr->fixed[0] = temp;
ptr->fixed[1] = temp;
ptr->fixed[2] = temp;
ptr->fixed[3] = temp;
if (speed == USB_SPEED_FULL) {
ptr->fixed[1] = 16;
ptr->fixed[2] = 32;
ptr->fixed[3] = 64;
}
if ((speed == USB_SPEED_VARIABLE) &&
(type == UE_BULK)) {
ptr->fixed[2] = 1024;
ptr->fixed[3] = 1536;
}
break;
}
}
void *
usbd_xfer_softc(struct usb_xfer *xfer)
{
return (xfer->priv_sc);
}
void *
usbd_xfer_get_priv(struct usb_xfer *xfer)
{
return (xfer->priv_fifo);
}
void
usbd_xfer_set_priv(struct usb_xfer *xfer, void *ptr)
{
xfer->priv_fifo = ptr;
}
uint8_t
usbd_xfer_state(struct usb_xfer *xfer)
{
return (xfer->usb_state);
}
void
usbd_xfer_set_flag(struct usb_xfer *xfer, int flag)
{
switch (flag) {
case USB_FORCE_SHORT_XFER:
xfer->flags.force_short_xfer = 1;
break;
case USB_SHORT_XFER_OK:
xfer->flags.short_xfer_ok = 1;
break;
case USB_MULTI_SHORT_OK:
xfer->flags.short_frames_ok = 1;
break;
case USB_MANUAL_STATUS:
xfer->flags.manual_status = 1;
break;
}
}
void
usbd_xfer_clr_flag(struct usb_xfer *xfer, int flag)
{
switch (flag) {
case USB_FORCE_SHORT_XFER:
xfer->flags.force_short_xfer = 0;
break;
case USB_SHORT_XFER_OK:
xfer->flags.short_xfer_ok = 0;
break;
case USB_MULTI_SHORT_OK:
xfer->flags.short_frames_ok = 0;
break;
case USB_MANUAL_STATUS:
xfer->flags.manual_status = 0;
break;
}
}
uint16_t
usbd_xfer_get_timestamp(struct usb_xfer *xfer)
{
return (xfer->isoc_time_complete);
}
uint8_t
usbd_xfer_maxp_was_clamped(struct usb_xfer *xfer)
{
return (xfer->flags_int.maxp_was_clamped);
}
uint8_t
usbd_xfer_get_isochronous_start_frame(struct usb_xfer *xfer, uint32_t frame_curr,
uint32_t frame_min, uint32_t frame_ms, uint32_t frame_mask, uint32_t *p_frame_start)
{
uint32_t duration;
uint32_t delta;
uint8_t retval;
uint8_t shift;
delta = (xfer->endpoint->isoc_next - frame_curr) & frame_mask;
if (xfer->endpoint->is_synced == 0 ||
delta < (frame_ms + frame_min) ||
delta > (frame_mask / 2)) {
xfer->endpoint->isoc_next = (frame_curr + 2 * frame_ms + frame_min) & frame_mask;
xfer->endpoint->is_synced = 1;
retval = 1;
} else {
retval = 0;
}
if (p_frame_start != NULL)
*p_frame_start = xfer->endpoint->isoc_next & frame_mask;
delta = xfer->endpoint->isoc_next - frame_curr + (frame_curr % frame_ms);
delta &= frame_mask;
delta /= frame_ms;
switch (usbd_get_speed(xfer->xroot->udev)) {
case USB_SPEED_FULL:
shift = 3;
break;
default:
shift = usbd_xfer_get_fps_shift(xfer);
break;
}
duration = ((xfer->nframes << shift) + 7) / 8;
xfer->isoc_time_complete =
usb_isoc_time_expand(xfer->xroot->bus, frame_curr / frame_ms) +
delta + duration;
xfer->endpoint->isoc_next += duration * frame_ms;
xfer->endpoint->isoc_next &= frame_mask;
return (retval);
}