#include "ohci.h"
#include "uhci.h"
#include "ehci.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <sys/kthread.h>
#include <sys/conf.h>
#include <sys/fcntl.h>
#include <sys/time.h>
#include <sys/rwlock.h>
#include <dev/usb/usb.h>
#include <dev/usb/usbdi.h>
#include <machine/bus.h>
#include <dev/usb/usbdivar.h>
#include <dev/usb/usb_mem.h>
#include <dev/usb/usbpcap.h>
#ifdef USB_DEBUG
#define DPRINTF(x) do { if (usbdebug) printf x; } while (0)
#define DPRINTFN(n,x) do { if (usbdebug>(n)) printf x; } while (0)
int usbdebug = 0;
#if defined(UHCI_DEBUG) && NUHCI > 0
extern int uhcidebug;
#endif
#if defined(OHCI_DEBUG) && NOHCI > 0
extern int ohcidebug;
#endif
#if defined(EHCI_DEBUG) && NEHCI > 0
extern int ehcidebug;
#endif
int usb_noexplore = 0;
#else
#define DPRINTF(x)
#define DPRINTFN(n,x)
#endif
struct usb_softc {
struct device sc_dev;
struct usbd_bus *sc_bus;
struct usbd_port sc_port;
int sc_speed;
struct usb_task sc_explore_task;
struct timeval sc_ptime;
};
struct rwlock usbpalock;
TAILQ_HEAD(, usb_task) usb_abort_tasks;
TAILQ_HEAD(, usb_task) usb_explore_tasks;
TAILQ_HEAD(, usb_task) usb_generic_tasks;
static int usb_nbuses = 0;
static int usb_run_tasks, usb_run_abort_tasks;
int explore_pending;
const char *usbrev_str[] = USBREV_STR;
void usb_explore(void *);
void usb_create_task_threads(void *);
void usb_task_thread(void *);
struct proc *usb_task_thread_proc = NULL;
void usb_abort_task_thread(void *);
struct proc *usb_abort_task_thread_proc = NULL;
void usb_fill_udc_task(void *);
void usb_fill_udf_task(void *);
int usb_match(struct device *, void *, void *);
void usb_attach(struct device *, struct device *, void *);
int usb_detach(struct device *, int);
int usb_activate(struct device *, int);
int usb_attach_roothub(struct usb_softc *);
void usb_detach_roothub(struct usb_softc *);
struct cfdriver usb_cd = {
NULL, "usb", DV_DULL
};
const struct cfattach usb_ca = {
sizeof(struct usb_softc), usb_match, usb_attach, usb_detach,
usb_activate,
};
int
usb_match(struct device *parent, void *match, void *aux)
{
return (1);
}
void
usb_attach(struct device *parent, struct device *self, void *aux)
{
struct usb_softc *sc = (struct usb_softc *)self;
int usbrev;
if (usb_nbuses == 0) {
rw_init(&usbpalock, "usbpalock");
TAILQ_INIT(&usb_abort_tasks);
TAILQ_INIT(&usb_explore_tasks);
TAILQ_INIT(&usb_generic_tasks);
usb_run_tasks = usb_run_abort_tasks = 1;
kthread_create_deferred(usb_create_task_threads, NULL);
}
usb_nbuses++;
sc->sc_bus = aux;
sc->sc_bus->usbctl = self;
sc->sc_port.power = USB_MAX_POWER;
usbrev = sc->sc_bus->usbrev;
printf(": USB revision %s", usbrev_str[usbrev]);
switch (usbrev) {
case USBREV_1_0:
case USBREV_1_1:
sc->sc_speed = USB_SPEED_FULL;
break;
case USBREV_2_0:
sc->sc_speed = USB_SPEED_HIGH;
break;
case USBREV_3_0:
sc->sc_speed = USB_SPEED_SUPER;
break;
default:
printf(", not supported\n");
sc->sc_bus->dying = 1;
return;
}
printf("\n");
#if NBPFILTER > 0
sc->sc_bus->bpfif = bpfsattach(&sc->sc_bus->bpf, sc->sc_dev.dv_xname,
DLT_USBPCAP, sizeof(struct usbpcap_pkt_hdr));
#endif
if (cold)
sc->sc_bus->use_polling++;
sc->sc_bus->flags |= USB_BUS_CONFIG_PENDING;
usb_init_task(&sc->sc_explore_task, usb_explore, sc,
USB_TASK_TYPE_EXPLORE);
sc->sc_bus->soft = softintr_establish(IPL_SOFTUSB,
sc->sc_bus->methods->soft_intr, sc->sc_bus);
if (sc->sc_bus->soft == NULL) {
printf("%s: can't register softintr\n", sc->sc_dev.dv_xname);
sc->sc_bus->dying = 1;
return;
}
if (!usb_attach_roothub(sc)) {
struct usbd_device *dev = sc->sc_bus->root_hub;
#if 1
if (cold && (sc->sc_dev.dv_cfdata->cf_flags & 1))
dev->hub->explore(sc->sc_bus->root_hub);
#endif
}
if (cold)
sc->sc_bus->use_polling--;
if (!sc->sc_bus->dying) {
getmicrouptime(&sc->sc_ptime);
if (sc->sc_bus->usbrev == USBREV_2_0)
explore_pending++;
config_pending_incr();
usb_needs_explore(sc->sc_bus->root_hub, 1);
}
}
int
usb_attach_roothub(struct usb_softc *sc)
{
struct usbd_device *dev;
if (usbd_new_device(&sc->sc_dev, sc->sc_bus, 0, sc->sc_speed, 0,
&sc->sc_port)) {
printf("%s: root hub problem\n", sc->sc_dev.dv_xname);
sc->sc_bus->dying = 1;
return (1);
}
dev = sc->sc_port.device;
if (dev->hub == NULL) {
printf("%s: root device is not a hub\n", sc->sc_dev.dv_xname);
sc->sc_bus->dying = 1;
return (1);
}
sc->sc_bus->root_hub = dev;
return (0);
}
void
usb_detach_roothub(struct usb_softc *sc)
{
sc->sc_bus->flags |= USB_BUS_DISCONNECTING;
sc->sc_bus->dying = 0;
usb_needs_explore(sc->sc_bus->root_hub, 0);
usb_wait_task(sc->sc_bus->root_hub, &sc->sc_explore_task);
sc->sc_bus->root_hub = NULL;
}
void
usb_create_task_threads(void *arg)
{
if (kthread_create(usb_abort_task_thread, NULL,
&usb_abort_task_thread_proc, "usbatsk"))
panic("unable to create usb abort task thread");
if (kthread_create(usb_task_thread, NULL,
&usb_task_thread_proc, "usbtask"))
panic("unable to create usb task thread");
}
void
usb_add_task(struct usbd_device *dev, struct usb_task *task)
{
int s;
if (usbd_is_dying(dev) && (task->type != USB_TASK_TYPE_ABORT))
return;
DPRINTFN(2,("%s: task=%p state=%d type=%d\n", __func__, task,
task->state, task->type));
s = splusb();
if (!(task->state & USB_TASK_STATE_ONQ)) {
switch (task->type) {
case USB_TASK_TYPE_ABORT:
TAILQ_INSERT_TAIL(&usb_abort_tasks, task, next);
break;
case USB_TASK_TYPE_EXPLORE:
TAILQ_INSERT_TAIL(&usb_explore_tasks, task, next);
break;
case USB_TASK_TYPE_GENERIC:
TAILQ_INSERT_TAIL(&usb_generic_tasks, task, next);
break;
}
task->state |= USB_TASK_STATE_ONQ;
task->dev = dev;
}
if (task->type == USB_TASK_TYPE_ABORT)
wakeup(&usb_run_abort_tasks);
else
wakeup(&usb_run_tasks);
splx(s);
}
void
usb_rem_task(struct usbd_device *dev, struct usb_task *task)
{
int s;
if (!(task->state & USB_TASK_STATE_ONQ))
return;
DPRINTFN(2,("%s: task=%p state=%d type=%d\n", __func__, task,
task->state, task->type));
s = splusb();
switch (task->type) {
case USB_TASK_TYPE_ABORT:
TAILQ_REMOVE(&usb_abort_tasks, task, next);
break;
case USB_TASK_TYPE_EXPLORE:
TAILQ_REMOVE(&usb_explore_tasks, task, next);
break;
case USB_TASK_TYPE_GENERIC:
TAILQ_REMOVE(&usb_generic_tasks, task, next);
break;
}
task->state &= ~USB_TASK_STATE_ONQ;
if (task->state == USB_TASK_STATE_NONE)
wakeup(task);
splx(s);
}
void
usb_wait_task(struct usbd_device *dev, struct usb_task *task)
{
int s;
DPRINTFN(2,("%s: task=%p state=%d type=%d\n", __func__, task,
task->state, task->type));
if (task->state == USB_TASK_STATE_NONE)
return;
s = splusb();
while (task->state != USB_TASK_STATE_NONE) {
DPRINTF(("%s: waiting for task to complete\n", __func__));
tsleep_nsec(task, PWAIT, "endtask", INFSLP);
}
splx(s);
}
void
usb_rem_wait_task(struct usbd_device *dev, struct usb_task *task)
{
usb_rem_task(dev, task);
usb_wait_task(dev, task);
}
void
usb_task_thread(void *arg)
{
struct usb_task *task;
int s;
DPRINTF(("usb_task_thread: start\n"));
s = splusb();
while (usb_run_tasks) {
if ((task = TAILQ_FIRST(&usb_explore_tasks)) != NULL)
TAILQ_REMOVE(&usb_explore_tasks, task, next);
else if ((task = TAILQ_FIRST(&usb_generic_tasks)) != NULL)
TAILQ_REMOVE(&usb_generic_tasks, task, next);
else {
tsleep_nsec(&usb_run_tasks, PWAIT, "usbtsk", INFSLP);
continue;
}
task->state |= USB_TASK_STATE_RUN;
task->state &= ~USB_TASK_STATE_ONQ;
if (!usbd_is_dying(task->dev)) {
splx(s);
task->fun(task->arg);
s = splusb();
}
task->state &= ~USB_TASK_STATE_RUN;
if (task->state == USB_TASK_STATE_NONE)
wakeup(task);
}
splx(s);
kthread_exit(0);
}
void
usb_abort_task_thread(void *arg)
{
struct usb_task *task;
int s;
DPRINTF(("usb_xfer_abort_thread: start\n"));
s = splusb();
while (usb_run_abort_tasks) {
if ((task = TAILQ_FIRST(&usb_abort_tasks)) != NULL)
TAILQ_REMOVE(&usb_abort_tasks, task, next);
else {
tsleep_nsec(&usb_run_abort_tasks, PWAIT, "usbatsk",
INFSLP);
continue;
}
task->state |= USB_TASK_STATE_RUN;
task->state &= ~USB_TASK_STATE_ONQ;
splx(s);
task->fun(task->arg);
s = splusb();
task->state &= ~USB_TASK_STATE_RUN;
if (task->state == USB_TASK_STATE_NONE)
wakeup(task);
}
splx(s);
kthread_exit(0);
}
int
usbctlprint(void *aux, const char *pnp)
{
if (pnp)
printf("usb at %s", pnp);
return (UNCONF);
}
int
usbopen(dev_t dev, int flag, int mode, struct proc *p)
{
int unit = minor(dev);
struct usb_softc *sc;
if (unit >= usb_cd.cd_ndevs)
return (ENXIO);
sc = usb_cd.cd_devs[unit];
if (sc == NULL)
return (ENXIO);
if (sc->sc_bus->dying)
return (EIO);
return (0);
}
int
usbclose(dev_t dev, int flag, int mode, struct proc *p)
{
return (0);
}
void
usb_fill_udc_task(void *arg)
{
struct usb_device_cdesc *udc = (struct usb_device_cdesc *)arg;
struct usb_softc *sc;
struct usbd_device *dev;
int addr = udc->udc_addr, cdesc_len;
usb_config_descriptor_t *cdesc;
if (udc->udc_bus >= usb_cd.cd_ndevs)
return;
sc = usb_cd.cd_devs[udc->udc_bus];
if (sc == NULL)
return;
dev = sc->sc_bus->devices[udc->udc_addr];
if (dev == NULL)
return;
cdesc = usbd_get_cdesc(sc->sc_bus->devices[addr],
udc->udc_config_index, &cdesc_len);
if (cdesc == NULL)
return;
udc->udc_desc = *cdesc;
free(cdesc, M_TEMP, cdesc_len);
}
void
usb_fill_udf_task(void *arg)
{
struct usb_device_fdesc *udf = (struct usb_device_fdesc *)arg;
struct usb_softc *sc;
struct usbd_device *dev;
int addr = udf->udf_addr;
usb_config_descriptor_t *cdesc;
if (udf->udf_bus >= usb_cd.cd_ndevs)
return;
sc = usb_cd.cd_devs[udf->udf_bus];
if (sc == NULL)
return;
dev = sc->sc_bus->devices[udf->udf_addr];
if (dev == NULL)
return;
cdesc = usbd_get_cdesc(sc->sc_bus->devices[addr],
udf->udf_config_index, &udf->udf_size);
udf->udf_data = (char *)cdesc;
}
int
usbioctl(dev_t devt, u_long cmd, caddr_t data, int flag, struct proc *p)
{
struct usb_softc *sc;
int unit = minor(devt);
int error;
sc = usb_cd.cd_devs[unit];
if (sc->sc_bus->dying)
return (EIO);
error = 0;
switch (cmd) {
#ifdef USB_DEBUG
case USB_SETDEBUG:
if ((error = suser(curproc)) != 0)
return (error);
if (!(flag & FWRITE))
return (EBADF);
usbdebug = ((*(unsigned int *)data) & 0x000000ff);
#if defined(UHCI_DEBUG) && NUHCI > 0
uhcidebug = ((*(unsigned int *)data) & 0x0000ff00) >> 8;
#endif
#if defined(OHCI_DEBUG) && NOHCI > 0
ohcidebug = ((*(unsigned int *)data) & 0x00ff0000) >> 16;
#endif
#if defined(EHCI_DEBUG) && NEHCI > 0
ehcidebug = ((*(unsigned int *)data) & 0xff000000) >> 24;
#endif
break;
#endif
case USB_REQUEST:
{
struct usb_ctl_request *ur = (void *)data;
size_t len = UGETW(ur->ucr_request.wLength), mlen;
struct iovec iov;
struct uio uio;
void *ptr = NULL;
int addr = ur->ucr_addr;
usbd_status err;
if (!(flag & FWRITE))
return (EBADF);
DPRINTF(("%s: USB_REQUEST addr=%d len=%zu\n", __func__, addr, len));
if ((ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
ur->ucr_request.bRequest == UR_SET_ADDRESS) ||
(ur->ucr_request.bmRequestType == UT_WRITE_DEVICE &&
ur->ucr_request.bRequest == UR_SET_CONFIG) ||
(ur->ucr_request.bmRequestType == UT_WRITE_INTERFACE &&
ur->ucr_request.bRequest == UR_SET_INTERFACE))
return (EINVAL);
if (len > 32767)
return (EINVAL);
if (addr < 0 || addr >= USB_MAX_DEVICES)
return (EINVAL);
if (sc->sc_bus->devices[addr] == NULL)
return (ENXIO);
if (len != 0) {
iov.iov_base = (caddr_t)ur->ucr_data;
iov.iov_len = len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_resid = len;
uio.uio_offset = 0;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_rw =
ur->ucr_request.bmRequestType & UT_READ ?
UIO_READ : UIO_WRITE;
uio.uio_procp = p;
if ((ptr = malloc(len, M_TEMP, M_NOWAIT)) == NULL) {
error = ENOMEM;
goto ret;
}
if (uio.uio_rw == UIO_WRITE) {
error = uiomove(ptr, len, &uio);
if (error)
goto ret;
}
}
err = usbd_do_request_flags(sc->sc_bus->devices[addr],
&ur->ucr_request, ptr, ur->ucr_flags,
&ur->ucr_actlen, USBD_DEFAULT_TIMEOUT);
if (err) {
error = EIO;
goto ret;
}
mlen = len;
if (mlen > ur->ucr_actlen)
mlen = ur->ucr_actlen;
if (mlen != 0) {
if (uio.uio_rw == UIO_READ) {
error = uiomove(ptr, mlen, &uio);
if (error)
goto ret;
}
}
ret:
free(ptr, M_TEMP, len);
return (error);
}
case USB_DEVICEINFO:
{
struct usb_device_info *di = (void *)data;
int addr = di->udi_addr;
struct usbd_device *dev;
if (addr < 1 || addr >= USB_MAX_DEVICES)
return (EINVAL);
dev = sc->sc_bus->devices[addr];
if (dev == NULL)
return (ENXIO);
usbd_fill_deviceinfo(dev, di);
break;
}
case USB_DEVICESTATS:
*(struct usb_device_stats *)data = sc->sc_bus->stats;
break;
case USB_DEVICE_GET_DDESC:
{
struct usb_device_ddesc *udd = (struct usb_device_ddesc *)data;
int addr = udd->udd_addr;
struct usbd_device *dev;
if (addr < 1 || addr >= USB_MAX_DEVICES)
return (EINVAL);
dev = sc->sc_bus->devices[addr];
if (dev == NULL)
return (ENXIO);
udd->udd_bus = unit;
udd->udd_desc = *usbd_get_device_descriptor(dev);
break;
}
case USB_DEVICE_GET_CDESC:
{
struct usb_device_cdesc *udc = (struct usb_device_cdesc *)data;
int addr = udc->udc_addr;
struct usb_task udc_task;
if (addr < 1 || addr >= USB_MAX_DEVICES)
return (EINVAL);
if (sc->sc_bus->devices[addr] == NULL)
return (ENXIO);
udc->udc_bus = unit;
udc->udc_desc.bLength = 0;
usb_init_task(&udc_task, usb_fill_udc_task, udc,
USB_TASK_TYPE_GENERIC);
usb_add_task(sc->sc_bus->root_hub, &udc_task);
usb_wait_task(sc->sc_bus->root_hub, &udc_task);
if (udc->udc_desc.bLength == 0)
return (EINVAL);
break;
}
case USB_DEVICE_GET_FDESC:
{
struct usb_device_fdesc *udf = (struct usb_device_fdesc *)data;
int addr = udf->udf_addr;
struct usb_task udf_task;
struct usb_device_fdesc save_udf;
usb_config_descriptor_t *cdesc;
struct iovec iov;
struct uio uio;
size_t len, cdesc_len;
if (addr < 1 || addr >= USB_MAX_DEVICES)
return (EINVAL);
if (sc->sc_bus->devices[addr] == NULL)
return (ENXIO);
udf->udf_bus = unit;
save_udf = *udf;
udf->udf_data = NULL;
usb_init_task(&udf_task, usb_fill_udf_task, udf,
USB_TASK_TYPE_GENERIC);
usb_add_task(sc->sc_bus->root_hub, &udf_task);
usb_wait_task(sc->sc_bus->root_hub, &udf_task);
len = cdesc_len = udf->udf_size;
cdesc = (usb_config_descriptor_t *)udf->udf_data;
*udf = save_udf;
if (cdesc == NULL)
return (EINVAL);
if (len > udf->udf_size)
len = udf->udf_size;
iov.iov_base = (caddr_t)udf->udf_data;
iov.iov_len = len;
uio.uio_iov = &iov;
uio.uio_iovcnt = 1;
uio.uio_resid = len;
uio.uio_offset = 0;
uio.uio_segflg = UIO_USERSPACE;
uio.uio_rw = UIO_READ;
uio.uio_procp = p;
error = uiomove((void *)cdesc, len, &uio);
free(cdesc, M_TEMP, cdesc_len);
return (error);
}
default:
return (EINVAL);
}
return (0);
}
void
usb_explore(void *v)
{
struct usb_softc *sc = v;
struct timeval now, waited;
int pwrdly, waited_ms;
DPRINTFN(2,("%s: %s\n", __func__, sc->sc_dev.dv_xname));
#ifdef USB_DEBUG
if (usb_noexplore)
return;
#endif
if (sc->sc_bus->dying)
return;
if (sc->sc_bus->flags & USB_BUS_CONFIG_PENDING) {
if (sc->sc_bus->usbrev < USBREV_2_0 && explore_pending > 0) {
usb_add_task(sc->sc_bus->root_hub,
&sc->sc_explore_task);
return;
}
getmicrouptime(&now);
timersub(&now, &sc->sc_ptime, &waited);
waited_ms = waited.tv_sec * 1000 + waited.tv_usec / 1000;
pwrdly = sc->sc_bus->root_hub->hub->powerdelay +
USB_EXTRA_POWER_UP_TIME;
if (pwrdly > waited_ms)
usb_delay_ms(sc->sc_bus, pwrdly - waited_ms);
}
if (sc->sc_bus->flags & USB_BUS_DISCONNECTING) {
sc->sc_bus->dying = 1;
if (sc->sc_port.device != NULL) {
usbd_detach(sc->sc_port.device, (struct device *)sc);
sc->sc_port.device = NULL;
}
sc->sc_bus->flags &= ~USB_BUS_DISCONNECTING;
} else {
sc->sc_bus->root_hub->hub->explore(sc->sc_bus->root_hub);
}
if (sc->sc_bus->flags & USB_BUS_CONFIG_PENDING) {
DPRINTF(("%s: %s: first explore done\n", __func__,
sc->sc_dev.dv_xname));
if (sc->sc_bus->usbrev == USBREV_2_0 && explore_pending)
explore_pending--;
config_pending_decr();
sc->sc_bus->flags &= ~(USB_BUS_CONFIG_PENDING);
}
}
void
usb_needs_explore(struct usbd_device *dev, int first_explore)
{
struct usb_softc *usbctl = (struct usb_softc *)dev->bus->usbctl;
DPRINTFN(3,("%s: %s\n", usbctl->sc_dev.dv_xname, __func__));
if (!first_explore && (dev->bus->flags & USB_BUS_CONFIG_PENDING)) {
DPRINTF(("%s: %s: not exploring before first explore\n",
__func__, usbctl->sc_dev.dv_xname));
return;
}
usb_add_task(dev, &usbctl->sc_explore_task);
}
void
usb_needs_reattach(struct usbd_device *dev)
{
DPRINTFN(2,("usb_needs_reattach\n"));
dev->powersrc->reattach = 1;
usb_needs_explore(dev, 0);
}
void
usb_schedsoftintr(struct usbd_bus *bus)
{
DPRINTFN(10,("%s: polling=%d\n", __func__, bus->use_polling));
if (bus->soft == NULL)
return;
if (bus->use_polling) {
bus->methods->soft_intr(bus);
} else {
softintr_schedule(bus->soft);
}
}
int
usb_activate(struct device *self, int act)
{
struct usb_softc *sc = (struct usb_softc *)self;
int rv = 0;
switch (act) {
case DVACT_QUIESCE:
if (sc->sc_bus->root_hub != NULL)
usb_detach_roothub(sc);
break;
case DVACT_RESUME:
sc->sc_bus->dying = 0;
break;
case DVACT_WAKEUP:
sc->sc_bus->use_polling++;
if (!usb_attach_roothub(sc))
usb_needs_explore(sc->sc_bus->root_hub, 0);
sc->sc_bus->use_polling--;
break;
default:
rv = config_activate_children(self, act);
break;
}
return (rv);
}
int
usb_detach(struct device *self, int flags)
{
struct usb_softc *sc = (struct usb_softc *)self;
if (sc->sc_bus->root_hub != NULL) {
usb_detach_roothub(sc);
if (--usb_nbuses == 0) {
usb_run_tasks = usb_run_abort_tasks = 0;
wakeup(&usb_run_abort_tasks);
wakeup(&usb_run_tasks);
}
}
if (sc->sc_bus->soft != NULL) {
softintr_disestablish(sc->sc_bus->soft);
sc->sc_bus->soft = NULL;
}
#if NBPFILTER > 0
bpfsdetach(sc->sc_bus->bpfif);
#endif
return (0);
}
void
usb_tap(struct usbd_bus *bus, struct usbd_xfer *xfer, uint8_t dir)
{
#if NBPFILTER > 0
struct usb_softc *sc = (struct usb_softc *)bus->usbctl;
usb_endpoint_descriptor_t *ed = xfer->pipe->endpoint->edesc;
union {
struct usbpcap_ctl_hdr uch;
struct usbpcap_iso_hdr_full uih;
} h;
struct usbpcap_pkt_hdr *uph = &h.uch.uch_hdr;
uint32_t nframes, psize;
unsigned int bpfdir;
void *data = NULL;
size_t flen;
caddr_t bpf;
int i;
bpf = bus->bpf;
if (bpf == NULL)
return;
switch (UE_GET_XFERTYPE(ed->bmAttributes)) {
case UE_CONTROL:
uph->uph_hlen = htole16(sizeof(struct usbpcap_ctl_hdr));
uph->uph_xfertype = USBPCAP_TRANSFER_CONTROL;
break;
case UE_ISOCHRONOUS:
nframes = xfer->nframes;
psize = xfer->length / nframes;
#ifdef DIAGNOSTIC
if (nframes > _USBPCAP_MAX_ISOFRAMES) {
printf("%s: too many frames: %d > %d\n", __func__,
xfer->nframes, _USBPCAP_MAX_ISOFRAMES);
nframes = _USBPCAP_MAX_ISOFRAMES;
}
#endif
flen = (nframes - 1) * sizeof(struct usbpcap_iso_pkt);
uph->uph_hlen = htole16(sizeof(struct usbpcap_iso_hdr) + flen);
uph->uph_xfertype = USBPCAP_TRANSFER_ISOCHRONOUS;
h.uih.uih_startframe = 0;
h.uih.uih_nframes = nframes;
h.uih.uih_errors = 0;
for (i = 0; i < nframes; i++) {
h.uih.uih_frames[i].uip_offset = i * psize;
h.uih.uih_frames[i].uip_length = xfer->frlengths[i];
h.uih.uih_frames[i].uip_status = 0;
}
break;
case UE_BULK:
uph->uph_hlen = htole16(sizeof(*uph));
uph->uph_xfertype = USBPCAP_TRANSFER_BULK;
break;
case UE_INTERRUPT:
uph->uph_hlen = htole16(sizeof(*uph));
uph->uph_xfertype = USBPCAP_TRANSFER_INTERRUPT;
break;
default:
return;
}
uph->uph_id = 0;
uph->uph_status = htole32(xfer->status);
uph->uph_function = 0;
uph->uph_bus = htole32(sc->sc_dev.dv_unit);
uph->uph_devaddr = htole16(xfer->device->address);
uph->uph_epaddr = ed->bEndpointAddress;
uph->uph_info = 0;
if ((xfer->rqflags & URQ_REQUEST) && (dir == USBTAP_DIR_OUT)) {
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_SETUP;
uph->uph_dlen = sizeof(usb_device_request_t);
bpf_tap_hdr(bpf, uph, uph->uph_hlen, &xfer->request,
uph->uph_dlen, BPF_DIRECTION_OUT);
}
if (dir == USBTAP_DIR_OUT) {
bpfdir = BPF_DIRECTION_OUT;
if (!usbd_xfer_isread(xfer)) {
data = KERNADDR(&xfer->dmabuf, 0);
uph->uph_dlen = xfer->length;
if (xfer->rqflags & URQ_REQUEST)
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA;
} else {
data = NULL;
uph->uph_dlen = 0;
if (xfer->rqflags & URQ_REQUEST)
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS;
}
} else {
bpfdir = BPF_DIRECTION_IN;
uph->uph_info = USBPCAP_INFO_DIRECTION_IN;
if (usbd_xfer_isread(xfer)) {
data = KERNADDR(&xfer->dmabuf, 0);
uph->uph_dlen = xfer->actlen;
if (xfer->rqflags & URQ_REQUEST)
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_DATA;
} else {
data = NULL;
uph->uph_dlen = 0;
if (xfer->rqflags & URQ_REQUEST)
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS;
}
}
if (bpfdir == BPF_DIRECTION_IN && uph->uph_dlen > 0 &&
uph->uph_xfertype == USBPCAP_TRANSFER_ISOCHRONOUS) {
uph->uph_dlen = xfer->length;
}
bpf_tap_hdr(bpf, uph, uph->uph_hlen, data, uph->uph_dlen, bpfdir);
if ((xfer->rqflags & URQ_REQUEST) && (dir == USBTAP_DIR_IN) &&
(h.uch.uch_stage == USBPCAP_CONTROL_STAGE_DATA)) {
h.uch.uch_stage = USBPCAP_CONTROL_STAGE_STATUS;
uph->uph_dlen = 0;
bpf_tap_hdr(bpf, uph, uph->uph_hlen, NULL, 0, BPF_DIRECTION_IN);
}
#endif
}