From 18ec6525529b6c68d5ce5986c2eb3fa4fbc769c1 Mon Sep 17 00:00:00 2001 From: Weongyo Jeong Date: Mon, 22 Nov 2010 01:11:28 +0000 Subject: [PATCH] Adds a USB packet filter feature to the stack that it could capture packets which go through each USB host controllers. Its implementations are almost based on BPF code and very similar with it except it's little bit customized for USB packet only. The userland program usbdump(8) would be committed soon. Discussed with: hps, thompsa, yongari --- sys/conf/files | 1 + sys/dev/usb/controller/usb_controller.c | 32 + sys/dev/usb/usb_bus.h | 2 + sys/dev/usb/usb_controller.h | 1 + sys/dev/usb/usb_pf.c | 1862 +++++++++++++++++++++++ sys/dev/usb/usb_pf.h | 319 ++++ sys/dev/usb/usb_transfer.c | 8 + sys/modules/usb/usb/Makefile | 4 +- 8 files changed, 2227 insertions(+), 2 deletions(-) create mode 100644 sys/dev/usb/usb_pf.c create mode 100644 sys/dev/usb/usb_pf.h diff --git a/sys/conf/files b/sys/conf/files index c859ec82b3a..db3e5383ed1 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1779,6 +1779,7 @@ dev/usb/usb_lookup.c optional usb dev/usb/usb_mbuf.c optional usb dev/usb/usb_msctest.c optional usb dev/usb/usb_parse.c optional usb +dev/usb/usb_pf.c optional usb dev/usb/usb_process.c optional usb dev/usb/usb_request.c optional usb dev/usb/usb_transfer.c optional usb diff --git a/sys/dev/usb/controller/usb_controller.c b/sys/dev/usb/controller/usb_controller.c index 71e7ef1da69..afc21182bc0 100644 --- a/sys/dev/usb/controller/usb_controller.c +++ b/sys/dev/usb/controller/usb_controller.c @@ -61,6 +61,7 @@ #include #include +#include /* function prototypes */ @@ -547,6 +548,8 @@ usb_bus_mem_alloc_all(struct usb_bus *bus, bus_dma_tag_t dmat, TAILQ_INIT(&bus->intr_q.head); + usbpf_attach(bus, &bus->uif); + #if USB_HAVE_BUSDMA usb_dma_tag_setup(bus->dma_parent_tag, bus->dma_tags, dmat, &bus->bus_mtx, NULL, 32, USB_BUS_DMA_TAG_MAX); @@ -594,5 +597,34 @@ usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb) usb_dma_tag_unsetup(bus->dma_parent_tag); #endif + usbpf_detach(bus); + mtx_destroy(&bus->bus_mtx); } + +struct usb_bus * +usb_bus_find(const char *name) +{ + struct usb_bus *ubus; + devclass_t dc; + device_t *devlist; + int devcount, error, i; + const char *nameunit; + + dc = devclass_find("usbus"); + if (dc == NULL) + return (NULL); + error = devclass_get_devices(dc, &devlist, &devcount); + if (error != 0) + return (NULL); + for (i = 0; i < devcount; i++) { + nameunit = device_get_nameunit(devlist[i]); + if (!strncmp(name, nameunit, strlen(nameunit))) { + ubus = device_get_ivars(devlist[i]); + free(devlist, M_TEMP); + return (ubus); + } + } + free(devlist, M_TEMP); + return (NULL); +} diff --git a/sys/dev/usb/usb_bus.h b/sys/dev/usb/usb_bus.h index 99e97770665..c5dfeff1731 100644 --- a/sys/dev/usb/usb_bus.h +++ b/sys/dev/usb/usb_bus.h @@ -86,6 +86,8 @@ struct usb_bus { struct usb_bus_methods *methods; /* filled by HC driver */ struct usb_device **devices; + struct usbpf_if *uif; /* USB Packet Filter */ + usb_power_mask_t hw_power_state; /* see USB_HW_POWER_XXX */ usb_size_t uframe_usage[USB_HS_MICRO_FRAMES_MAX]; diff --git a/sys/dev/usb/usb_controller.h b/sys/dev/usb/usb_controller.h index 6b15dab993d..fb5d091335e 100644 --- a/sys/dev/usb/usb_controller.h +++ b/sys/dev/usb/usb_controller.h @@ -221,5 +221,6 @@ void usb_bus_mem_free_all(struct usb_bus *bus, usb_bus_mem_cb_t *cb); uint16_t usb_isoc_time_expand(struct usb_bus *bus, uint16_t isoc_time_curr); uint16_t usbd_fs_isoc_schedule_isoc_time_expand(struct usb_device *udev, struct usb_fs_isoc_schedule **pp_start, struct usb_fs_isoc_schedule **pp_end, uint16_t isoc_time); uint8_t usbd_fs_isoc_schedule_alloc(struct usb_fs_isoc_schedule *fss, uint8_t *pstart, uint16_t len); +struct usb_bus *usb_bus_find(const char *name); #endif /* _USB_CONTROLLER_H_ */ diff --git a/sys/dev/usb/usb_pf.c b/sys/dev/usb/usb_pf.c new file mode 100644 index 00000000000..3f5f204d4d7 --- /dev/null +++ b/sys/dev/usb/usb_pf.c @@ -0,0 +1,1862 @@ +/*- + * Copyright (c) 1990, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 +__FBSDID("$FreeBSD$"); +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * All usbpf implementations are extracted from bpf(9) APIs and it's + * specialized for USB packet filtering between the driver and the host + * controller. + */ + +MALLOC_DEFINE(M_USBPF, "USBPktFilter", "USB Packet Filter"); + +/* + * Rotate the packet buffers in descriptor ud. Move the store buffer into the + * hold slot, and the free buffer ino the store slot. Zero the length of the + * new store buffer. Descriptor lock should be held. + */ +#define USBPF_ROTATE_BUFFERS(ud) do { \ + (ud)->ud_hbuf = (ud)->ud_sbuf; \ + (ud)->ud_hlen = (ud)->ud_slen; \ + (ud)->ud_sbuf = (ud)->ud_fbuf; \ + (ud)->ud_slen = 0; \ + (ud)->ud_fbuf = NULL; \ + usbpf_bufheld(ud); \ +} while (0) + +#ifndef __i386__ +#define USBPF_ALIGN +#endif + +#ifndef USBPF_ALIGN +#define USBPF_EXTRACT_SHORT(p) ((u_int16_t)ntohs(*(u_int16_t *)p)) +#define USBPF_EXTRACT_LONG(p) (ntohl(*(u_int32_t *)p)) +#else +#define USBPF_EXTRACT_SHORT(p) \ + ((u_int16_t) \ + ((u_int16_t)*((u_char *)p+0)<<8| \ + (u_int16_t)*((u_char *)p+1)<<0)) +#define USBPF_EXTRACT_LONG(p) \ + ((u_int32_t)*((u_char *)p+0)<<24| \ + (u_int32_t)*((u_char *)p+1)<<16| \ + (u_int32_t)*((u_char *)p+2)<<8| \ + (u_int32_t)*((u_char *)p+3)<<0) +#endif + +/* + * Number of scratch memory words (for USBPF_LD|USBPF_MEM and USBPF_ST). + */ +#define USBPF_MEMWORDS 16 + +/* Values for ud_state */ +#define USBPF_IDLE 0 /* no select in progress */ +#define USBPF_WAITING 1 /* waiting for read timeout in select */ +#define USBPF_TIMED_OUT 2 /* read timeout has expired in select */ + +#define PRIUSB 26 /* interruptible */ + +/* Frame directions */ +enum usbpf_direction { + USBPF_D_IN, /* See incoming frames */ + USBPF_D_INOUT, /* See incoming and outgoing frames */ + USBPF_D_OUT /* See outgoing frames */ +}; + +static void usbpf_append_bytes(struct usbpf_d *, caddr_t, u_int, void *, + u_int); +static void usbpf_attachd(struct usbpf_d *, struct usbpf_if *); +static void usbpf_detachd(struct usbpf_d *); +static int usbpf_canfreebuf(struct usbpf_d *); +static void usbpf_buf_reclaimed(struct usbpf_d *); +static int usbpf_canwritebuf(struct usbpf_d *); + +static d_open_t usbpf_open; +static d_read_t usbpf_read; +static d_write_t usbpf_write; +static d_ioctl_t usbpf_ioctl; +static d_poll_t usbpf_poll; +static d_kqfilter_t usbpf_kqfilter; + +static struct cdevsw usbpf_cdevsw = { + .d_version = D_VERSION, + .d_open = usbpf_open, + .d_read = usbpf_read, + .d_write = usbpf_write, + .d_ioctl = usbpf_ioctl, + .d_poll = usbpf_poll, + .d_name = "usbpf", + .d_kqfilter = usbpf_kqfilter, +}; + +static LIST_HEAD(, usbpf_if) usbpf_iflist; +static struct mtx usbpf_mtx; /* global lock */ +static int usbpf_uifd_cnt; + +static int usbpf_bufsize = 4096; +#define USBPF_MINBUFSIZE 32 +#define USBPF_MAXBUFSIZE 0x80000 +static int usbpf_maxbufsize = USBPF_MAXBUFSIZE; +#define USBPF_MAXINSNS 512 +static int usbpf_maxinsns = USBPF_MAXINSNS; + +static void +usbpf_buffer_init(struct usbpf_d *ud) +{ + + ud->ud_bufsize = usbpf_bufsize; +} + +/* + * Free USBPF kernel buffers on device close. + */ +static void +usbpf_buffer_free(struct usbpf_d *ud) +{ + + if (ud->ud_sbuf != NULL) + free(ud->ud_sbuf, M_USBPF); + if (ud->ud_hbuf != NULL) + free(ud->ud_hbuf, M_USBPF); + if (ud->ud_fbuf != NULL) + free(ud->ud_fbuf, M_USBPF); + +#ifdef INVARIANTS + ud->ud_sbuf = ud->ud_hbuf = ud->ud_fbuf = (caddr_t)~0; +#endif +} + +static void +usbpf_buffer_alloc(struct usbpf_d *ud) +{ + + KASSERT(ud->ud_fbuf == NULL, ("%s: ud_fbuf != NULL", __func__)); + KASSERT(ud->ud_sbuf == NULL, ("%s: ud_sbuf != NULL", __func__)); + KASSERT(ud->ud_hbuf == NULL, ("%s: ud_hbuf != NULL", __func__)); + + ud->ud_fbuf = (caddr_t)malloc(ud->ud_bufsize, M_USBPF, M_WAITOK); + ud->ud_sbuf = (caddr_t)malloc(ud->ud_bufsize, M_USBPF, M_WAITOK); + ud->ud_hbuf = NULL; + ud->ud_slen = 0; + ud->ud_hlen = 0; +} + +/* + * Copy buffer storage to user space in read(). + */ +static int +usbpf_buffer_uiomove(struct usbpf_d *ud, caddr_t buf, u_int len, + struct uio *uio) +{ + + return (uiomove(buf, len, uio)); +} + +/* + * Simple data copy to the current kernel buffer. + */ +static void +usbpf_buffer_append_bytes(struct usbpf_d *ud, caddr_t buf, u_int offset, + void *src, u_int len) +{ + u_char *src_bytes; + + src_bytes = (u_char *)src; + bcopy(src_bytes, buf + offset, len); +} + +/* + * Allocate or resize buffers. + */ +static int +usbpf_buffer_ioctl_sblen(struct usbpf_d *ud, u_int *i) +{ + u_int size; + + USBPFD_LOCK(ud); + if (ud->ud_bif != NULL) { + USBPFD_UNLOCK(ud); + return (EINVAL); + } + size = *i; + if (size > usbpf_maxbufsize) + *i = size = usbpf_maxbufsize; + else if (size < USBPF_MINBUFSIZE) + *i = size = USBPF_MINBUFSIZE; + ud->ud_bufsize = size; + USBPFD_UNLOCK(ud); + return (0); +} + +static const u_short usbpf_code_map[] = { + 0x10ff, /* 0x00-0x0f: 1111111100001000 */ + 0x3070, /* 0x10-0x1f: 0000111000001100 */ + 0x3131, /* 0x20-0x2f: 1000110010001100 */ + 0x3031, /* 0x30-0x3f: 1000110000001100 */ + 0x3131, /* 0x40-0x4f: 1000110010001100 */ + 0x1011, /* 0x50-0x5f: 1000100000001000 */ + 0x1013, /* 0x60-0x6f: 1100100000001000 */ + 0x1010, /* 0x70-0x7f: 0000100000001000 */ + 0x0093, /* 0x80-0x8f: 1100100100000000 */ + 0x0000, /* 0x90-0x9f: 0000000000000000 */ + 0x0000, /* 0xa0-0xaf: 0000000000000000 */ + 0x0002, /* 0xb0-0xbf: 0100000000000000 */ + 0x0000, /* 0xc0-0xcf: 0000000000000000 */ + 0x0000, /* 0xd0-0xdf: 0000000000000000 */ + 0x0000, /* 0xe0-0xef: 0000000000000000 */ + 0x0000 /* 0xf0-0xff: 0000000000000000 */ +}; + +#define USBPF_VALIDATE_CODE(c) \ + ((c) <= 0xff && (usbpf_code_map[(c) >> 4] & (1 << ((c) & 0xf))) != 0) + +/* + * Return true if the 'fcode' is a valid filter program. + * The constraints are that each jump be forward and to a valid + * code. The code must terminate with either an accept or reject. + * + * The kernel needs to be able to verify an application's filter code. + * Otherwise, a bogus program could easily crash the system. + */ +static int +usbpf_validate(const struct usbpf_insn *f, int len) +{ + register int i; + register const struct usbpf_insn *p; + + /* Do not accept negative length filter. */ + if (len < 0) + return (0); + + /* An empty filter means accept all. */ + if (len == 0) + return (1); + + for (i = 0; i < len; ++i) { + p = &f[i]; + /* + * Check that the code is valid. + */ + if (!USBPF_VALIDATE_CODE(p->code)) + return (0); + /* + * Check that that jumps are forward, and within + * the code block. + */ + if (USBPF_CLASS(p->code) == USBPF_JMP) { + register u_int offset; + + if (p->code == (USBPF_JMP|USBPF_JA)) + offset = p->k; + else + offset = p->jt > p->jf ? p->jt : p->jf; + if (offset >= (u_int)(len - i) - 1) + return (0); + continue; + } + /* + * Check that memory operations use valid addresses. + */ + if (p->code == USBPF_ST || p->code == USBPF_STX || + p->code == (USBPF_LD|USBPF_MEM) || + p->code == (USBPF_LDX|USBPF_MEM)) { + if (p->k >= USBPF_MEMWORDS) + return (0); + continue; + } + /* + * Check for constant division by 0. + */ + if (p->code == (USBPF_ALU|USBPF_DIV|USBPF_K) && p->k == 0) + return (0); + } + return (USBPF_CLASS(f[len - 1].code) == USBPF_RET); +} + +#ifdef _KERNEL +#define MINDEX(m, k) \ +{ \ + register int len = m->m_len; \ + \ + while (k >= len) { \ + k -= len; \ + m = m->m_next; \ + if (m == 0) \ + return (0); \ + len = m->m_len; \ + } \ +} + +static u_int16_t m_xhalf(struct mbuf *m, usbpf_u_int32 k, int *err); +static u_int32_t m_xword(struct mbuf *m, usbpf_u_int32 k, int *err); + +static u_int32_t +m_xword(struct mbuf *m, usbpf_u_int32 k, int *err) +{ + size_t len; + u_char *cp, *np; + struct mbuf *m0; + + len = m->m_len; + while (k >= len) { + k -= len; + m = m->m_next; + if (m == 0) + goto bad; + len = m->m_len; + } + cp = mtod(m, u_char *) + k; + if (len - k >= 4) { + *err = 0; + return (USBPF_EXTRACT_LONG(cp)); + } + m0 = m->m_next; + if (m0 == 0 || m0->m_len + len - k < 4) + goto bad; + *err = 0; + np = mtod(m0, u_char *); + switch (len - k) { + case 1: + return (((u_int32_t)cp[0] << 24) | + ((u_int32_t)np[0] << 16) | + ((u_int32_t)np[1] << 8) | + (u_int32_t)np[2]); + + case 2: + return (((u_int32_t)cp[0] << 24) | + ((u_int32_t)cp[1] << 16) | + ((u_int32_t)np[0] << 8) | + (u_int32_t)np[1]); + + default: + return (((u_int32_t)cp[0] << 24) | + ((u_int32_t)cp[1] << 16) | + ((u_int32_t)cp[2] << 8) | + (u_int32_t)np[0]); + } + bad: + *err = 1; + return (0); +} + +static u_int16_t +m_xhalf(struct mbuf *m, usbpf_u_int32 k, int *err) +{ + size_t len; + u_char *cp; + struct mbuf *m0; + + len = m->m_len; + while (k >= len) { + k -= len; + m = m->m_next; + if (m == 0) + goto bad; + len = m->m_len; + } + cp = mtod(m, u_char *) + k; + if (len - k >= 2) { + *err = 0; + return (USBPF_EXTRACT_SHORT(cp)); + } + m0 = m->m_next; + if (m0 == 0) + goto bad; + *err = 0; + return ((cp[0] << 8) | mtod(m0, u_char *)[0]); + bad: + *err = 1; + return (0); +} +#endif + +/* + * Execute the filter program starting at pc on the packet p + * wirelen is the length of the original packet + * buflen is the amount of data present + */ +static u_int +usbpf_filter(const struct usbpf_insn *pc, u_char *p, u_int wirelen, + u_int buflen) +{ + u_int32_t A = 0, X = 0; + usbpf_u_int32 k; + u_int32_t mem[USBPF_MEMWORDS]; + + /* + * XXX temporarily the filter system is disabled because currently it + * could not handle the some machine code properly that leads to + * kernel crash by invalid usage. + */ + return ((u_int)-1); + + if (pc == NULL) + /* + * No filter means accept all. + */ + return ((u_int)-1); + + --pc; + while (1) { + ++pc; + switch (pc->code) { + default: +#ifdef _KERNEL + return (0); +#else + abort(); +#endif + + case USBPF_RET|USBPF_K: + return ((u_int)pc->k); + + case USBPF_RET|USBPF_A: + return ((u_int)A); + + case USBPF_LD|USBPF_W|USBPF_ABS: + k = pc->k; + if (k > buflen || sizeof(int32_t) > buflen - k) { +#ifdef _KERNEL + int merr; + + if (buflen != 0) + return (0); + A = m_xword((struct mbuf *)p, k, &merr); + if (merr != 0) + return (0); + continue; +#else + return (0); +#endif + } +#ifdef USBPF_ALIGN + if (((intptr_t)(p + k) & 3) != 0) + A = USBPF_EXTRACT_LONG(&p[k]); + else +#endif + A = ntohl(*(int32_t *)(p + k)); + continue; + + case USBPF_LD|USBPF_H|USBPF_ABS: + k = pc->k; + if (k > buflen || sizeof(int16_t) > buflen - k) { +#ifdef _KERNEL + int merr; + + if (buflen != 0) + return (0); + A = m_xhalf((struct mbuf *)p, k, &merr); + continue; +#else + return (0); +#endif + } + A = USBPF_EXTRACT_SHORT(&p[k]); + continue; + + case USBPF_LD|USBPF_B|USBPF_ABS: + k = pc->k; + if (k >= buflen) { +#ifdef _KERNEL + struct mbuf *m; + + if (buflen != 0) + return (0); + m = (struct mbuf *)p; + MINDEX(m, k); + A = mtod(m, u_char *)[k]; + continue; +#else + return (0); +#endif + } + A = p[k]; + continue; + + case USBPF_LD|USBPF_W|USBPF_LEN: + A = wirelen; + continue; + + case USBPF_LDX|USBPF_W|USBPF_LEN: + X = wirelen; + continue; + + case USBPF_LD|USBPF_W|USBPF_IND: + k = X + pc->k; + if (pc->k > buflen || X > buflen - pc->k || + sizeof(int32_t) > buflen - k) { +#ifdef _KERNEL + int merr; + + if (buflen != 0) + return (0); + A = m_xword((struct mbuf *)p, k, &merr); + if (merr != 0) + return (0); + continue; +#else + return (0); +#endif + } +#ifdef USBPF_ALIGN + if (((intptr_t)(p + k) & 3) != 0) + A = USBPF_EXTRACT_LONG(&p[k]); + else +#endif + A = ntohl(*(int32_t *)(p + k)); + continue; + + case USBPF_LD|USBPF_H|USBPF_IND: + k = X + pc->k; + if (X > buflen || pc->k > buflen - X || + sizeof(int16_t) > buflen - k) { +#ifdef _KERNEL + int merr; + + if (buflen != 0) + return (0); + A = m_xhalf((struct mbuf *)p, k, &merr); + if (merr != 0) + return (0); + continue; +#else + return (0); +#endif + } + A = USBPF_EXTRACT_SHORT(&p[k]); + continue; + + case USBPF_LD|USBPF_B|USBPF_IND: + k = X + pc->k; + if (pc->k >= buflen || X >= buflen - pc->k) { +#ifdef _KERNEL + struct mbuf *m; + + if (buflen != 0) + return (0); + m = (struct mbuf *)p; + MINDEX(m, k); + A = mtod(m, u_char *)[k]; + continue; +#else + return (0); +#endif + } + A = p[k]; + continue; + + case USBPF_LDX|USBPF_MSH|USBPF_B: + k = pc->k; + if (k >= buflen) { +#ifdef _KERNEL + register struct mbuf *m; + + if (buflen != 0) + return (0); + m = (struct mbuf *)p; + MINDEX(m, k); + X = (mtod(m, u_char *)[k] & 0xf) << 2; + continue; +#else + return (0); +#endif + } + X = (p[pc->k] & 0xf) << 2; + continue; + + case USBPF_LD|USBPF_IMM: + A = pc->k; + continue; + + case USBPF_LDX|USBPF_IMM: + X = pc->k; + continue; + + case USBPF_LD|USBPF_MEM: + A = mem[pc->k]; + continue; + + case USBPF_LDX|USBPF_MEM: + X = mem[pc->k]; + continue; + + case USBPF_ST: + mem[pc->k] = A; + continue; + + case USBPF_STX: + mem[pc->k] = X; + continue; + + case USBPF_JMP|USBPF_JA: + pc += pc->k; + continue; + + case USBPF_JMP|USBPF_JGT|USBPF_K: + pc += (A > pc->k) ? pc->jt : pc->jf; + continue; + + case USBPF_JMP|USBPF_JGE|USBPF_K: + pc += (A >= pc->k) ? pc->jt : pc->jf; + continue; + + case USBPF_JMP|USBPF_JEQ|USBPF_K: + pc += (A == pc->k) ? pc->jt : pc->jf; + continue; + + case USBPF_JMP|USBPF_JSET|USBPF_K: + pc += (A & pc->k) ? pc->jt : pc->jf; + continue; + + case USBPF_JMP|USBPF_JGT|USBPF_X: + pc += (A > X) ? pc->jt : pc->jf; + continue; + + case USBPF_JMP|USBPF_JGE|USBPF_X: + pc += (A >= X) ? pc->jt : pc->jf; + continue; + + case USBPF_JMP|USBPF_JEQ|USBPF_X: + pc += (A == X) ? pc->jt : pc->jf; + continue; + + case USBPF_JMP|USBPF_JSET|USBPF_X: + pc += (A & X) ? pc->jt : pc->jf; + continue; + + case USBPF_ALU|USBPF_ADD|USBPF_X: + A += X; + continue; + + case USBPF_ALU|USBPF_SUB|USBPF_X: + A -= X; + continue; + + case USBPF_ALU|USBPF_MUL|USBPF_X: + A *= X; + continue; + + case USBPF_ALU|USBPF_DIV|USBPF_X: + if (X == 0) + return (0); + A /= X; + continue; + + case USBPF_ALU|USBPF_AND|USBPF_X: + A &= X; + continue; + + case USBPF_ALU|USBPF_OR|USBPF_X: + A |= X; + continue; + + case USBPF_ALU|USBPF_LSH|USBPF_X: + A <<= X; + continue; + + case USBPF_ALU|USBPF_RSH|USBPF_X: + A >>= X; + continue; + + case USBPF_ALU|USBPF_ADD|USBPF_K: + A += pc->k; + continue; + + case USBPF_ALU|USBPF_SUB|USBPF_K: + A -= pc->k; + continue; + + case USBPF_ALU|USBPF_MUL|USBPF_K: + A *= pc->k; + continue; + + case USBPF_ALU|USBPF_DIV|USBPF_K: + A /= pc->k; + continue; + + case USBPF_ALU|USBPF_AND|USBPF_K: + A &= pc->k; + continue; + + case USBPF_ALU|USBPF_OR|USBPF_K: + A |= pc->k; + continue; + + case USBPF_ALU|USBPF_LSH|USBPF_K: + A <<= pc->k; + continue; + + case USBPF_ALU|USBPF_RSH|USBPF_K: + A >>= pc->k; + continue; + + case USBPF_ALU|USBPF_NEG: + A = -A; + continue; + + case USBPF_MISC|USBPF_TAX: + X = A; + continue; + + case USBPF_MISC|USBPF_TXA: + A = X; + continue; + } + } +} + +static void +usbpf_free(struct usbpf_d *ud) +{ + + switch (ud->ud_bufmode) { + case USBPF_BUFMODE_BUFFER: + return (usbpf_buffer_free(ud)); + default: + panic("usbpf_buf_free"); + } +} + +/* + * Notify the buffer model that a buffer has moved into the hold position. + */ +static void +usbpf_bufheld(struct usbpf_d *ud) +{ + + USBPFD_LOCK_ASSERT(ud); +} + +/* + * Free buffers currently in use by a descriptor. + * Called on close. + */ +static void +usbpf_freed(struct usbpf_d *ud) +{ + + /* + * We don't need to lock out interrupts since this descriptor has + * been detached from its interface and it yet hasn't been marked + * free. + */ + usbpf_free(ud); + if (ud->ud_rfilter != NULL) + free((caddr_t)ud->ud_rfilter, M_USBPF); + if (ud->ud_wfilter != NULL) + free((caddr_t)ud->ud_wfilter, M_USBPF); + mtx_destroy(&ud->ud_mtx); +} + +/* + * Close the descriptor by detaching it from its interface, + * deallocating its buffers, and marking it free. + */ +static void +usbpf_dtor(void *data) +{ + struct usbpf_d *ud = data; + + USBPFD_LOCK(ud); + if (ud->ud_state == USBPF_WAITING) + callout_stop(&ud->ud_callout); + ud->ud_state = USBPF_IDLE; + USBPFD_UNLOCK(ud); + funsetown(&ud->ud_sigio); + mtx_lock(&usbpf_mtx); + if (ud->ud_bif) + usbpf_detachd(ud); + mtx_unlock(&usbpf_mtx); + selwakeuppri(&ud->ud_sel, PRIUSB); + knlist_destroy(&ud->ud_sel.si_note); + callout_drain(&ud->ud_callout); + usbpf_freed(ud); + free(ud, M_USBPF); +} + +/* + * Open device. Returns ENXIO for illegal minor device number, + * EBUSY if file is open by another process. + */ +/* ARGSUSED */ +static int +usbpf_open(struct cdev *dev, int flags, int fmt, struct thread *td) +{ + struct usbpf_d *ud; + int error; + + ud = malloc(sizeof(*ud), M_USBPF, M_WAITOK | M_ZERO); + error = devfs_set_cdevpriv(ud, usbpf_dtor); + if (error != 0) { + free(ud, M_USBPF); + return (error); + } + + usbpf_buffer_init(ud); + ud->ud_bufmode = USBPF_BUFMODE_BUFFER; + ud->ud_sig = SIGIO; + ud->ud_direction = USBPF_D_INOUT; + ud->ud_pid = td->td_proc->p_pid; + mtx_init(&ud->ud_mtx, devtoname(dev), "usbpf cdev lock", MTX_DEF); + callout_init_mtx(&ud->ud_callout, &ud->ud_mtx, 0); + knlist_init_mtx(&ud->ud_sel.si_note, &ud->ud_mtx); + + return (0); +} + +static int +usbpf_uiomove(struct usbpf_d *ud, caddr_t buf, u_int len, struct uio *uio) +{ + + if (ud->ud_bufmode != USBPF_BUFMODE_BUFFER) + return (EOPNOTSUPP); + return (usbpf_buffer_uiomove(ud, buf, len, uio)); +} + +/* + * usbpf_read - read next chunk of packets from buffers + */ +static int +usbpf_read(struct cdev *dev, struct uio *uio, int ioflag) +{ + struct usbpf_d *ud; + int error; + int non_block; + int timed_out; + + error = devfs_get_cdevpriv((void **)&ud); + if (error != 0) + return (error); + + /* + * Restrict application to use a buffer the same size as + * as kernel buffers. + */ + if (uio->uio_resid != ud->ud_bufsize) + return (EINVAL); + + non_block = ((ioflag & O_NONBLOCK) != 0); + + USBPFD_LOCK(ud); + ud->ud_pid = curthread->td_proc->p_pid; + if (ud->ud_bufmode != USBPF_BUFMODE_BUFFER) { + USBPFD_UNLOCK(ud); + return (EOPNOTSUPP); + } + if (ud->ud_state == USBPF_WAITING) + callout_stop(&ud->ud_callout); + timed_out = (ud->ud_state == USBPF_TIMED_OUT); + ud->ud_state = USBPF_IDLE; + /* + * If the hold buffer is empty, then do a timed sleep, which + * ends when the timeout expires or when enough packets + * have arrived to fill the store buffer. + */ + while (ud->ud_hbuf == NULL) { + if (ud->ud_slen != 0) { + /* + * A packet(s) either arrived since the previous + * read or arrived while we were asleep. + */ + if (ud->ud_immediate || non_block || timed_out) { + /* + * Rotate the buffers and return what's here + * if we are in immediate mode, non-blocking + * flag is set, or this descriptor timed out. + */ + USBPF_ROTATE_BUFFERS(ud); + break; + } + } + + /* + * No data is available, check to see if the usbpf device + * is still pointed at a real interface. If not, return + * ENXIO so that the userland process knows to rebind + * it before using it again. + */ + if (ud->ud_bif == NULL) { + USBPFD_UNLOCK(ud); + return (ENXIO); + } + + if (non_block) { + USBPFD_UNLOCK(ud); + return (EWOULDBLOCK); + } + error = msleep(ud, &ud->ud_mtx, PRIUSB|PCATCH, + "uff", ud->ud_rtout); + if (error == EINTR || error == ERESTART) { + USBPFD_UNLOCK(ud); + return (error); + } + if (error == EWOULDBLOCK) { + /* + * On a timeout, return what's in the buffer, + * which may be nothing. If there is something + * in the store buffer, we can rotate the buffers. + */ + if (ud->ud_hbuf) + /* + * We filled up the buffer in between + * getting the timeout and arriving + * here, so we don't need to rotate. + */ + break; + + if (ud->ud_slen == 0) { + USBPFD_UNLOCK(ud); + return (0); + } + USBPF_ROTATE_BUFFERS(ud); + break; + } + } + /* + * At this point, we know we have something in the hold slot. + */ + USBPFD_UNLOCK(ud); + + /* + * Move data from hold buffer into user space. + * We know the entire buffer is transferred since + * we checked above that the read buffer is usbpf_bufsize bytes. + * + * XXXRW: More synchronization needed here: what if a second thread + * issues a read on the same fd at the same time? Don't want this + * getting invalidated. + */ + error = usbpf_uiomove(ud, ud->ud_hbuf, ud->ud_hlen, uio); + + USBPFD_LOCK(ud); + ud->ud_fbuf = ud->ud_hbuf; + ud->ud_hbuf = NULL; + ud->ud_hlen = 0; + usbpf_buf_reclaimed(ud); + USBPFD_UNLOCK(ud); + + return (error); +} + +static int +usbpf_write(struct cdev *dev, struct uio *uio, int ioflag) +{ + + /* NOT IMPLEMENTED */ + return (ENOSYS); +} + +static int +usbpf_ioctl_sblen(struct usbpf_d *ud, u_int *i) +{ + + if (ud->ud_bufmode != USBPF_BUFMODE_BUFFER) + return (EOPNOTSUPP); + return (usbpf_buffer_ioctl_sblen(ud, i)); +} + +/* + * Reset a descriptor by flushing its packet buffer and clearing the receive + * and drop counts. This is doable for kernel-only buffers, but with + * zero-copy buffers, we can't write to (or rotate) buffers that are + * currently owned by userspace. It would be nice if we could encapsulate + * this logic in the buffer code rather than here. + */ +static void +usbpf_reset_d(struct usbpf_d *ud) +{ + + USBPFD_LOCK_ASSERT(ud); + + if ((ud->ud_hbuf != NULL) && + (ud->ud_bufmode != USBPF_BUFMODE_ZBUF || usbpf_canfreebuf(ud))) { + /* Free the hold buffer. */ + ud->ud_fbuf = ud->ud_hbuf; + ud->ud_hbuf = NULL; + ud->ud_hlen = 0; + usbpf_buf_reclaimed(ud); + } + if (usbpf_canwritebuf(ud)) + ud->ud_slen = 0; + ud->ud_rcount = 0; + ud->ud_dcount = 0; + ud->ud_fcount = 0; + ud->ud_wcount = 0; + ud->ud_wfcount = 0; + ud->ud_wdcount = 0; + ud->ud_zcopy = 0; +} + +static int +usbpf_setif(struct usbpf_d *ud, struct usbpf_ifreq *ufr) +{ + struct usbpf_if *uif; + struct usb_bus *theywant; + + theywant = usb_bus_find(ufr->ufr_name); + if (theywant == NULL || theywant->uif == NULL) + return (ENXIO); + + uif = theywant->uif; + + switch (ud->ud_bufmode) { + case USBPF_BUFMODE_BUFFER: + if (ud->ud_sbuf == NULL) + usbpf_buffer_alloc(ud); + KASSERT(ud->ud_sbuf != NULL, ("%s: ud_sbuf == NULL", __func__)); + break; + + default: + panic("usbpf_setif: bufmode %d", ud->ud_bufmode); + } + if (uif != ud->ud_bif) { + if (ud->ud_bif) + /* + * Detach if attached to something else. + */ + usbpf_detachd(ud); + + usbpf_attachd(ud, uif); + } + USBPFD_LOCK(ud); + usbpf_reset_d(ud); + USBPFD_UNLOCK(ud); + return (0); +} + +/* + * Set d's packet filter program to fp. If this file already has a filter, + * free it and replace it. Returns EINVAL for bogus requests. + */ +static int +usbpf_setf(struct usbpf_d *ud, struct usbpf_program *fp, u_long cmd) +{ + struct usbpf_insn *fcode, *old; + u_int wfilter, flen, size; + + if (cmd == UIOCSETWF) { + old = ud->ud_wfilter; + wfilter = 1; + } else { + wfilter = 0; + old = ud->ud_rfilter; + } + if (fp->uf_insns == NULL) { + if (fp->uf_len != 0) + return (EINVAL); + USBPFD_LOCK(ud); + if (wfilter) + ud->ud_wfilter = NULL; + else { + ud->ud_rfilter = NULL; + if (cmd == UIOCSETF) + usbpf_reset_d(ud); + } + USBPFD_UNLOCK(ud); + if (old != NULL) + free((caddr_t)old, M_USBPF); + return (0); + } + flen = fp->uf_len; + if (flen > usbpf_maxinsns) + return (EINVAL); + + size = flen * sizeof(*fp->uf_insns); + fcode = (struct usbpf_insn *)malloc(size, M_USBPF, M_WAITOK); + if (copyin((caddr_t)fp->uf_insns, (caddr_t)fcode, size) == 0 && + usbpf_validate(fcode, (int)flen)) { + USBPFD_LOCK(ud); + if (wfilter) + ud->ud_wfilter = fcode; + else { + ud->ud_rfilter = fcode; + if (cmd == UIOCSETF) + usbpf_reset_d(ud); + } + USBPFD_UNLOCK(ud); + if (old != NULL) + free((caddr_t)old, M_USBPF); + + return (0); + } + free((caddr_t)fcode, M_USBPF); + return (EINVAL); +} + +static int +usbpf_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, + struct thread *td) +{ + struct usbpf_d *ud; + int error; + + error = devfs_get_cdevpriv((void **)&ud); + if (error != 0) + return (error); + + /* + * Refresh PID associated with this descriptor. + */ + USBPFD_LOCK(ud); + ud->ud_pid = td->td_proc->p_pid; + if (ud->ud_state == USBPF_WAITING) + callout_stop(&ud->ud_callout); + ud->ud_state = USBPF_IDLE; + USBPFD_UNLOCK(ud); + + if (ud->ud_locked == 1) { + switch (cmd) { + case UIOCGBLEN: + case UIOCSBLEN: + case UIOCVERSION: + break; + default: + return (EPERM); + } + } + + switch (cmd) { + + default: + error = EINVAL; + break; + + /* + * Get buffer len [for read()]. + */ + case UIOCGBLEN: + *(u_int *)addr = ud->ud_bufsize; + break; + + /* + * Set buffer length. + */ + case UIOCSBLEN: + error = usbpf_ioctl_sblen(ud, (u_int *)addr); + break; + + /* + * Set read filter. + */ + case UIOCSETF: + error = usbpf_setf(ud, (struct usbpf_program *)addr, cmd); + break; + + /* + * Set read timeout. + */ + case UIOCSRTIMEOUT: + { + struct timeval *tv = (struct timeval *)addr; + + /* + * Subtract 1 tick from tvtohz() since this isn't + * a one-shot timer. + */ + if ((error = itimerfix(tv)) == 0) + ud->ud_rtout = tvtohz(tv) - 1; + break; + } + + /* + * Get read timeout. + */ + case UIOCGRTIMEOUT: + { + struct timeval *tv = (struct timeval *)addr; + + tv->tv_sec = ud->ud_rtout / hz; + tv->tv_usec = (ud->ud_rtout % hz) * tick; + break; + } + + /* + * Get packet stats. + */ + case UIOCGSTATS: + { + struct usbpf_stat *us = (struct usbpf_stat *)addr; + + /* XXXCSJP overflow */ + us->us_recv = ud->ud_rcount; + us->us_drop = ud->ud_dcount; + break; + } + + case UIOCVERSION: + { + struct usbpf_version *uv = (struct usbpf_version *)addr; + + uv->uv_major = USBPF_MAJOR_VERSION; + uv->uv_minor = USBPF_MINOR_VERSION; + break; + } + + /* + * Set interface. + */ + case UIOCSETIF: + error = usbpf_setif(ud, (struct usbpf_ifreq *)addr); + break; + + } + return (error); +} + +/* + * Support for select() and poll() system calls + * + * Return true iff the specific operation will not block indefinitely. + * Otherwise, return false but make a note that a selwakeup() must be done. + */ +static int +usbpf_poll(struct cdev *dev, int events, struct thread *td) +{ + + /* NOT IMPLEMENTED */ + return (ENOSYS); +} + +/* + * Support for kevent() system call. Register EVFILT_READ filters and + * reject all others. + */ +int +usbpf_kqfilter(struct cdev *dev, struct knote *kn) +{ + + /* NOT IMPLEMENTED */ + return (ENOSYS); +} + +/* + * Attach file to the usbpf interface, i.e. make d listen on bp. + */ +static void +usbpf_attachd(struct usbpf_d *ud, struct usbpf_if *uif) +{ + + USBPFIF_LOCK(uif); + ud->ud_bif = uif; + LIST_INSERT_HEAD(&uif->uif_dlist, ud, ud_next); + + usbpf_uifd_cnt++; + USBPFIF_UNLOCK(uif); +} + +/* + * Detach a file from its interface. + */ +static void +usbpf_detachd(struct usbpf_d *ud) +{ + struct usbpf_if *uif; + struct usb_bus *ubus; + + uif = ud->ud_bif; + USBPFIF_LOCK(uif); + USBPFD_LOCK(ud); + ubus = ud->ud_bif->uif_ubus; + + /* + * Remove d from the interface's descriptor list. + */ + LIST_REMOVE(ud, ud_next); + + usbpf_uifd_cnt--; + ud->ud_bif = NULL; + USBPFD_UNLOCK(ud); + USBPFIF_UNLOCK(uif); +} + +void +usbpf_attach(struct usb_bus *ubus, struct usbpf_if **driverp) +{ + struct usbpf_if *uif; + + uif = malloc(sizeof(*uif), M_USBPF, M_WAITOK | M_ZERO); + LIST_INIT(&uif->uif_dlist); + uif->uif_ubus = ubus; + mtx_init(&uif->uif_mtx, "usbpf interface lock", NULL, MTX_DEF); + KASSERT(*driverp == NULL, + ("usbpf_attach: driverp already initialized")); + *driverp = uif; + + mtx_lock(&usbpf_mtx); + LIST_INSERT_HEAD(&usbpf_iflist, uif, uif_next); + mtx_unlock(&usbpf_mtx); + + if (bootverbose) + device_printf(ubus->parent, "usbpf attached\n"); +} + +/* + * If there are processes sleeping on this descriptor, wake them up. + */ +static __inline void +usbpf_wakeup(struct usbpf_d *ud) +{ + + USBPFD_LOCK_ASSERT(ud); + if (ud->ud_state == USBPF_WAITING) { + callout_stop(&ud->ud_callout); + ud->ud_state = USBPF_IDLE; + } + wakeup(ud); + if (ud->ud_async && ud->ud_sig && ud->ud_sigio) + pgsigio(&ud->ud_sigio, ud->ud_sig, 0); + + selwakeuppri(&ud->ud_sel, PRIUSB); + KNOTE_LOCKED(&ud->ud_sel.si_note, 0); +} + +void +usbpf_detach(struct usb_bus *ubus) +{ + struct usbpf_if *uif; + struct usbpf_d *ud; + + /* Locate USBPF interface information */ + mtx_lock(&usbpf_mtx); + LIST_FOREACH(uif, &usbpf_iflist, uif_next) { + if (ubus == uif->uif_ubus) + break; + } + + /* Interface wasn't attached */ + if ((uif == NULL) || (uif->uif_ubus == NULL)) { + mtx_unlock(&usbpf_mtx); + printf("usbpf_detach: not attached\n"); /* XXX */ + return; + } + + LIST_REMOVE(uif, uif_next); + mtx_unlock(&usbpf_mtx); + + while ((ud = LIST_FIRST(&uif->uif_dlist)) != NULL) { + usbpf_detachd(ud); + USBPFD_LOCK(ud); + usbpf_wakeup(ud); + USBPFD_UNLOCK(ud); + } + + mtx_destroy(&uif->uif_mtx); + free(uif, M_USBPF); +} + +/* Time stamping functions */ +#define USBPF_T_MICROTIME 0x0000 +#define USBPF_T_NANOTIME 0x0001 +#define USBPF_T_BINTIME 0x0002 +#define USBPF_T_NONE 0x0003 +#define USBPF_T_FORMAT_MASK 0x0003 +#define USBPF_T_NORMAL 0x0000 +#define USBPF_T_FAST 0x0100 +#define USBPF_T_MONOTONIC 0x0200 +#define USBPF_T_FORMAT(t) ((t) & USBPF_T_FORMAT_MASK) + +#define USBPF_TSTAMP_NONE 0 +#define USBPF_TSTAMP_FAST 1 +#define USBPF_TSTAMP_NORMAL 2 + +static int +usbpf_ts_quality(int tstype) +{ + + if (tstype == USBPF_T_NONE) + return (USBPF_TSTAMP_NONE); + if ((tstype & USBPF_T_FAST) != 0) + return (USBPF_TSTAMP_FAST); + + return (USBPF_TSTAMP_NORMAL); +} + +static int +usbpf_gettime(struct bintime *bt, int tstype) +{ + int quality; + + quality = usbpf_ts_quality(tstype); + if (quality == USBPF_TSTAMP_NONE) + return (quality); + if (quality == USBPF_TSTAMP_NORMAL) + binuptime(bt); + else + getbinuptime(bt); + + return (quality); +} + +/* + * If the buffer mechanism has a way to decide that a held buffer can be made + * free, then it is exposed via the usbpf_canfreebuf() interface. (1) is + * returned if the buffer can be discarded, (0) is returned if it cannot. + */ +static int +usbpf_canfreebuf(struct usbpf_d *ud) +{ + + USBPFD_LOCK_ASSERT(ud); + + return (0); +} + +/* + * Allow the buffer model to indicate that the current store buffer is + * immutable, regardless of the appearance of space. Return (1) if the + * buffer is writable, and (0) if not. + */ +static int +usbpf_canwritebuf(struct usbpf_d *ud) +{ + + USBPFD_LOCK_ASSERT(ud); + return (1); +} + +/* + * Notify buffer model that an attempt to write to the store buffer has + * resulted in a dropped packet, in which case the buffer may be considered + * full. + */ +static void +usbpf_buffull(struct usbpf_d *ud) +{ + + USBPFD_LOCK_ASSERT(ud); +} + +/* + * This function gets called when the free buffer is re-assigned. + */ +static void +usbpf_buf_reclaimed(struct usbpf_d *ud) +{ + + USBPFD_LOCK_ASSERT(ud); + + switch (ud->ud_bufmode) { + case USBPF_BUFMODE_BUFFER: + return; + + default: + panic("usbpf_buf_reclaimed"); + } +} + +#define SIZEOF_USBPF_HDR(type) \ + (offsetof(type, uh_hdrlen) + sizeof(((type *)0)->uh_hdrlen)) + +static int +usbpf_hdrlen(struct usbpf_d *ud) +{ + int hdrlen; + + hdrlen = ud->ud_bif->uif_hdrlen; + hdrlen += SIZEOF_USBPF_HDR(struct usbpf_xhdr); + hdrlen = USBPF_WORDALIGN(hdrlen); + + return (hdrlen - ud->ud_bif->uif_hdrlen); +} + +static void +usbpf_bintime2ts(struct bintime *bt, struct usbpf_ts *ts, int tstype) +{ + struct bintime bt2; + struct timeval tsm; + struct timespec tsn; + + if ((tstype & USBPF_T_MONOTONIC) == 0) { + bt2 = *bt; + bintime_add(&bt2, &boottimebin); + bt = &bt2; + } + switch (USBPF_T_FORMAT(tstype)) { + case USBPF_T_MICROTIME: + bintime2timeval(bt, &tsm); + ts->ut_sec = tsm.tv_sec; + ts->ut_frac = tsm.tv_usec; + break; + case USBPF_T_NANOTIME: + bintime2timespec(bt, &tsn); + ts->ut_sec = tsn.tv_sec; + ts->ut_frac = tsn.tv_nsec; + break; + case USBPF_T_BINTIME: + ts->ut_sec = bt->sec; + ts->ut_frac = bt->frac; + break; + } +} + +/* + * Move the packet data from interface memory (pkt) into the + * store buffer. "cpfn" is the routine called to do the actual data + * transfer. bcopy is passed in to copy contiguous chunks, while + * usbpf_append_mbuf is passed in to copy mbuf chains. In the latter case, + * pkt is really an mbuf. + */ +static void +catchpacket(struct usbpf_d *ud, u_char *pkt, u_int pktlen, u_int snaplen, + void (*cpfn)(struct usbpf_d *, caddr_t, u_int, void *, u_int), + struct bintime *bt) +{ + struct usbpf_xhdr hdr; + int caplen, curlen, hdrlen, totlen; + int do_wakeup = 0; + int do_timestamp; + int tstype; + + USBPFD_LOCK_ASSERT(ud); + + /* + * Detect whether user space has released a buffer back to us, and if + * so, move it from being a hold buffer to a free buffer. This may + * not be the best place to do it (for example, we might only want to + * run this check if we need the space), but for now it's a reliable + * spot to do it. + */ + if (ud->ud_fbuf == NULL && usbpf_canfreebuf(ud)) { + ud->ud_fbuf = ud->ud_hbuf; + ud->ud_hbuf = NULL; + ud->ud_hlen = 0; + usbpf_buf_reclaimed(ud); + } + + /* + * Figure out how many bytes to move. If the packet is + * greater or equal to the snapshot length, transfer that + * much. Otherwise, transfer the whole packet (unless + * we hit the buffer size limit). + */ + hdrlen = usbpf_hdrlen(ud); + totlen = hdrlen + min(snaplen, pktlen); + if (totlen > ud->ud_bufsize) + totlen = ud->ud_bufsize; + + /* + * Round up the end of the previous packet to the next longword. + * + * Drop the packet if there's no room and no hope of room + * If the packet would overflow the storage buffer or the storage + * buffer is considered immutable by the buffer model, try to rotate + * the buffer and wakeup pending processes. + */ + curlen = USBPF_WORDALIGN(ud->ud_slen); + if (curlen + totlen > ud->ud_bufsize || !usbpf_canwritebuf(ud)) { + if (ud->ud_fbuf == NULL) { + /* + * There's no room in the store buffer, and no + * prospect of room, so drop the packet. Notify the + * buffer model. + */ + usbpf_buffull(ud); + ++ud->ud_dcount; + return; + } + USBPF_ROTATE_BUFFERS(ud); + do_wakeup = 1; + curlen = 0; + } else if (ud->ud_immediate || ud->ud_state == USBPF_TIMED_OUT) + /* + * Immediate mode is set, or the read timeout has already + * expired during a select call. A packet arrived, so the + * reader should be woken up. + */ + do_wakeup = 1; + caplen = totlen - hdrlen; + tstype = ud->ud_tstamp; + do_timestamp = tstype != USBPF_T_NONE; + + /* + * Append the usbpf header. Note we append the actual header size, but + * move forward the length of the header plus padding. + */ + bzero(&hdr, sizeof(hdr)); + if (do_timestamp) + usbpf_bintime2ts(bt, &hdr.uh_tstamp, tstype); + hdr.uh_datalen = pktlen; + hdr.uh_hdrlen = hdrlen; + hdr.uh_caplen = caplen; + usbpf_append_bytes(ud, ud->ud_sbuf, curlen, &hdr, sizeof(hdr)); + + /* + * Copy the packet data into the store buffer and update its length. + */ + (*cpfn)(ud, ud->ud_sbuf, curlen + hdrlen, pkt, caplen); + ud->ud_slen = curlen + totlen; + + if (do_wakeup) + usbpf_wakeup(ud); +} + +/* + * Incoming linkage from device drivers. Process the packet pkt, of length + * pktlen, which is stored in a contiguous buffer. The packet is parsed + * by each process' filter, and if accepted, stashed into the corresponding + * buffer. + */ +static void +usbpf_tap(struct usbpf_if *uif, u_char *pkt, u_int pktlen) +{ + struct bintime bt; + struct usbpf_d *ud; + u_int slen; + int gottime; + + gottime = USBPF_TSTAMP_NONE; + USBPFIF_LOCK(uif); + LIST_FOREACH(ud, &uif->uif_dlist, ud_next) { + USBPFD_LOCK(ud); + ++ud->ud_rcount; + slen = usbpf_filter(ud->ud_rfilter, pkt, pktlen, pktlen); + if (slen != 0) { + ud->ud_fcount++; + if (gottime < usbpf_ts_quality(ud->ud_tstamp)) + gottime = usbpf_gettime(&bt, ud->ud_tstamp); + catchpacket(ud, pkt, pktlen, slen, + usbpf_append_bytes, &bt); + } + USBPFD_UNLOCK(ud); + } + USBPFIF_UNLOCK(uif); +} + +static uint32_t +usbpf_aggregate_xferflags(struct usb_xfer_flags *flags) +{ + uint32_t val = 0; + + if (flags->force_short_xfer == 1) + val |= USBPF_FLAG_FORCE_SHORT_XFER; + if (flags->short_xfer_ok == 1) + val |= USBPF_FLAG_SHORT_XFER_OK; + if (flags->short_frames_ok == 1) + val |= USBPF_FLAG_SHORT_FRAMES_OK; + if (flags->pipe_bof == 1) + val |= USBPF_FLAG_PIPE_BOF; + if (flags->proxy_buffer == 1) + val |= USBPF_FLAG_PROXY_BUFFER; + if (flags->ext_buffer == 1) + val |= USBPF_FLAG_EXT_BUFFER; + if (flags->manual_status == 1) + val |= USBPF_FLAG_MANUAL_STATUS; + if (flags->no_pipe_ok == 1) + val |= USBPF_FLAG_NO_PIPE_OK; + if (flags->stall_pipe == 1) + val |= USBPF_FLAG_STALL_PIPE; + return (val); +} + +static uint32_t +usbpf_aggregate_status(struct usb_xfer_flags_int *flags) +{ + uint32_t val = 0; + + if (flags->open == 1) + val |= USBPF_STATUS_OPEN; + if (flags->transferring == 1) + val |= USBPF_STATUS_TRANSFERRING; + if (flags->did_dma_delay == 1) + val |= USBPF_STATUS_DID_DMA_DELAY; + if (flags->did_close == 1) + val |= USBPF_STATUS_DID_CLOSE; + if (flags->draining == 1) + val |= USBPF_STATUS_DRAINING; + if (flags->started == 1) + val |= USBPF_STATUS_STARTED; + if (flags->bandwidth_reclaimed == 1) + val |= USBPF_STATUS_BW_RECLAIMED; + if (flags->control_xfr == 1) + val |= USBPF_STATUS_CONTROL_XFR; + if (flags->control_hdr == 1) + val |= USBPF_STATUS_CONTROL_HDR; + if (flags->control_act == 1) + val |= USBPF_STATUS_CONTROL_ACT; + if (flags->control_stall == 1) + val |= USBPF_STATUS_CONTROL_STALL; + if (flags->short_frames_ok == 1) + val |= USBPF_STATUS_SHORT_FRAMES_OK; + if (flags->short_xfer_ok == 1) + val |= USBPF_STATUS_SHORT_XFER_OK; +#if USB_HAVE_BUSDMA + if (flags->bdma_enable == 1) + val |= USBPF_STATUS_BDMA_ENABLE; + if (flags->bdma_no_post_sync == 1) + val |= USBPF_STATUS_BDMA_NO_POST_SYNC; + if (flags->bdma_setup == 1) + val |= USBPF_STATUS_BDMA_SETUP; +#endif + if (flags->isochronous_xfr == 1) + val |= USBPF_STATUS_ISOCHRONOUS_XFR; + if (flags->curr_dma_set == 1) + val |= USBPF_STATUS_CURR_DMA_SET; + if (flags->can_cancel_immed == 1) + val |= USBPF_STATUS_CAN_CANCEL_IMMED; + if (flags->doing_callback == 1) + val |= USBPF_STATUS_DOING_CALLBACK; + + return (val); +} + +void +usbpf_xfertap(struct usb_xfer *xfer, int type) +{ + struct usb_endpoint *ep = xfer->endpoint; + struct usb_page_search res; + struct usb_xfer_root *info = xfer->xroot; + struct usb_bus *bus = info->bus; + struct usbpf_pkthdr *up; + usb_frlength_t isoc_offset = 0; + int i; + char *buf, *ptr, *end; + + /* + * NB: usbpf_uifd_cnt isn't protected by USBPFIF_LOCK() because it's + * not harmful. + */ + if (usbpf_uifd_cnt == 0) + return; + + /* + * XXX TODO + * Allocating the buffer here causes copy operations twice what's + * really inefficient. Copying usbpf_pkthdr and data is for USB packet + * read filter to pass a virtually linear buffer. + */ + buf = ptr = malloc(sizeof(struct usbpf_pkthdr) + (USB_PAGE_SIZE * 5), + M_USBPF, M_NOWAIT); + if (buf == NULL) { + printf("usbpf_xfertap: out of memory\n"); /* XXX */ + return; + } + end = buf + sizeof(struct usbpf_pkthdr) + (USB_PAGE_SIZE * 5); + + bzero(ptr, sizeof(struct usbpf_pkthdr)); + up = (struct usbpf_pkthdr *)ptr; + up->up_busunit = htole32(device_get_unit(bus->bdev)); + up->up_type = type; + up->up_xfertype = ep->edesc->bmAttributes & UE_XFERTYPE; + up->up_address = xfer->address; + up->up_endpoint = xfer->endpointno; + up->up_flags = htole32(usbpf_aggregate_xferflags(&xfer->flags)); + up->up_status = htole32(usbpf_aggregate_status(&xfer->flags_int)); + switch (type) { + case USBPF_XFERTAP_SUBMIT: + up->up_length = htole32(xfer->sumlen); + up->up_frames = htole32(xfer->nframes); + break; + case USBPF_XFERTAP_DONE: + up->up_length = htole32(xfer->actlen); + up->up_frames = htole32(xfer->aframes); + break; + default: + panic("wrong usbpf type (%d)", type); + } + + up->up_error = htole32(xfer->error); + up->up_interval = htole32(xfer->interval); + ptr += sizeof(struct usbpf_pkthdr); + + for (i = 0; i < up->up_frames; i++) { + if (ptr + sizeof(u_int32_t) >= end) + goto done; + *((u_int32_t *)ptr) = htole32(xfer->frlengths[i]); + ptr += sizeof(u_int32_t); + + if (ptr + xfer->frlengths[i] >= end) + goto done; + if (xfer->flags_int.isochronous_xfr == 1) { + usbd_get_page(&xfer->frbuffers[0], isoc_offset, &res); + isoc_offset += xfer->frlengths[i]; + } else + usbd_get_page(&xfer->frbuffers[i], 0, &res); + bcopy(res.buffer, ptr, xfer->frlengths[i]); + ptr += xfer->frlengths[i]; + } + + usbpf_tap(bus->uif, buf, ptr - buf); +done: + free(buf, M_USBPF); +} + +static void +usbpf_append_bytes(struct usbpf_d *ud, caddr_t buf, u_int offset, void *src, + u_int len) +{ + + USBPFD_LOCK_ASSERT(ud); + + switch (ud->ud_bufmode) { + case USBPF_BUFMODE_BUFFER: + return (usbpf_buffer_append_bytes(ud, buf, offset, src, len)); + default: + panic("usbpf_buf_append_bytes"); + } +} + +static void +usbpf_drvinit(void *unused) +{ + struct cdev *dev; + + mtx_init(&usbpf_mtx, "USB packet filter global lock", NULL, + MTX_DEF); + LIST_INIT(&usbpf_iflist); + + dev = make_dev(&usbpf_cdevsw, 0, UID_ROOT, GID_WHEEL, 0600, "usbpf"); +} + +SYSINIT(usbpf_dev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, usbpf_drvinit, NULL); diff --git a/sys/dev/usb/usb_pf.h b/sys/dev/usb/usb_pf.h new file mode 100644 index 00000000000..f5ed9a08096 --- /dev/null +++ b/sys/dev/usb/usb_pf.h @@ -0,0 +1,319 @@ +/*- + * Copyright (c) 1990, 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from the Stanford/CMU enet packet filter, + * (net/enet.c) distributed as part of 4.3BSD, and code contributed + * to Berkeley by Steven McCanne and Van Jacobson both of Lawrence + * Berkeley Laboratory. + * + * 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. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * $FreeBSD$ + */ + +#ifndef _DEV_USB_PF_H +#define _DEV_USB_PF_H + +#ifdef _KERNEL +#include +#include +#include +#include +#endif + +typedef int32_t usbpf_int32; +typedef u_int32_t usbpf_u_int32; +typedef int64_t usbpf_int64; +typedef u_int64_t usbpf_u_int64; + +struct usbpf_if; + +/* + * Alignment macros. USBPF_WORDALIGN rounds up to the next + * even multiple of USBPF_ALIGNMENT. + */ +#define USBPF_ALIGNMENT sizeof(long) +#define USBPF_WORDALIGN(x) (((x)+(USBPF_ALIGNMENT-1))&~(USBPF_ALIGNMENT-1)) + +/* + * The instruction encodings. + */ + +/* instruction classes */ +#define USBPF_CLASS(code) ((code) & 0x07) +#define USBPF_LD 0x00 +#define USBPF_LDX 0x01 +#define USBPF_ST 0x02 +#define USBPF_STX 0x03 +#define USBPF_ALU 0x04 +#define USBPF_JMP 0x05 +#define USBPF_RET 0x06 +#define USBPF_MISC 0x07 + +/* ld/ldx fields */ +#define USBPF_SIZE(code) ((code) & 0x18) +#define USBPF_W 0x00 +#define USBPF_H 0x08 +#define USBPF_B 0x10 +#define USBPF_MODE(code) ((code) & 0xe0) +#define USBPF_IMM 0x00 +#define USBPF_ABS 0x20 +#define USBPF_IND 0x40 +#define USBPF_MEM 0x60 +#define USBPF_LEN 0x80 +#define USBPF_MSH 0xa0 + +/* alu/jmp fields */ +#define USBPF_OP(code) ((code) & 0xf0) +#define USBPF_ADD 0x00 +#define USBPF_SUB 0x10 +#define USBPF_MUL 0x20 +#define USBPF_DIV 0x30 +#define USBPF_OR 0x40 +#define USBPF_AND 0x50 +#define USBPF_LSH 0x60 +#define USBPF_RSH 0x70 +#define USBPF_NEG 0x80 +#define USBPF_JA 0x00 +#define USBPF_JEQ 0x10 +#define USBPF_JGT 0x20 +#define USBPF_JGE 0x30 +#define USBPF_JSET 0x40 +#define USBPF_SRC(code) ((code) & 0x08) +#define USBPF_K 0x00 +#define USBPF_X 0x08 + +/* ret - USBPF_K and USBPF_X also apply */ +#define USBPF_RVAL(code) ((code) & 0x18) +#define USBPF_A 0x10 + +/* misc */ +#define USBPF_MISCOP(code) ((code) & 0xf8) +#define USBPF_TAX 0x00 +#define USBPF_TXA 0x80 + +/* + * The instruction data structure. + */ +struct usbpf_insn { + u_short code; + u_char jt; + u_char jf; + usbpf_u_int32 k; +}; + +#ifdef _KERNEL + +/* + * Descriptor associated with each open uff file. + */ + +struct usbpf_d { + LIST_ENTRY(usbpf_d) ud_next; /* Linked list of descriptors */ + /* + * Buffer slots: two memory buffers store the incoming packets. + * The model has three slots. Sbuf is always occupied. + * sbuf (store) - Receive interrupt puts packets here. + * hbuf (hold) - When sbuf is full, put buffer here and + * wakeup read (replace sbuf with fbuf). + * fbuf (free) - When read is done, put buffer here. + * On receiving, if sbuf is full and fbuf is 0, packet is dropped. + */ + caddr_t ud_sbuf; /* store slot */ + caddr_t ud_hbuf; /* hold slot */ + caddr_t ud_fbuf; /* free slot */ + int ud_slen; /* current length of store buffer */ + int ud_hlen; /* current length of hold buffer */ + + int ud_bufsize; /* absolute length of buffers */ + + struct usbpf_if *ud_bif; /* interface descriptor */ + u_long ud_rtout; /* Read timeout in 'ticks' */ + struct usbpf_insn *ud_rfilter; /* read filter code */ + struct usbpf_insn *ud_wfilter; /* write filter code */ + void *ud_bfilter; /* binary filter code */ + u_int64_t ud_rcount; /* number of packets received */ + u_int64_t ud_dcount; /* number of packets dropped */ + + u_char ud_promisc; /* true if listening promiscuously */ + u_char ud_state; /* idle, waiting, or timed out */ + u_char ud_immediate; /* true to return on packet arrival */ + int ud_hdrcmplt; /* false to fill in src lladdr automatically */ + int ud_direction; /* select packet direction */ + int ud_tstamp; /* select time stamping function */ + int ud_feedback; /* true to feed back sent packets */ + int ud_async; /* non-zero if packet reception should generate signal */ + int ud_sig; /* signal to send upon packet reception */ + struct sigio * ud_sigio; /* information for async I/O */ + struct selinfo ud_sel; /* bsd select info */ + struct mtx ud_mtx; /* mutex for this descriptor */ + struct callout ud_callout; /* for USBPF timeouts with select */ + struct label *ud_label; /* MAC label for descriptor */ + u_int64_t ud_fcount; /* number of packets which matched filter */ + pid_t ud_pid; /* PID which created descriptor */ + int ud_locked; /* true if descriptor is locked */ + u_int ud_bufmode; /* Current buffer mode. */ + u_int64_t ud_wcount; /* number of packets written */ + u_int64_t ud_wfcount; /* number of packets that matched write filter */ + u_int64_t ud_wdcount; /* number of packets dropped during a write */ + u_int64_t ud_zcopy; /* number of zero copy operations */ + u_char ud_compat32; /* 32-bit stream on LP64 system */ +}; + +#define USBPFD_LOCK(ud) mtx_lock(&(ud)->ud_mtx) +#define USBPFD_UNLOCK(ud) mtx_unlock(&(ud)->ud_mtx) +#define USBPFD_LOCK_ASSERT(ud) mtx_assert(&(ud)->ud_mtx, MA_OWNED) + +/* + * Descriptor associated with each attached hardware interface. + */ +struct usbpf_if { + LIST_ENTRY(usbpf_if) uif_next; /* list of all interfaces */ + LIST_HEAD(, usbpf_d) uif_dlist; /* descriptor list */ + u_int uif_hdrlen; /* length of link header */ + struct usb_bus *uif_ubus; /* corresponding interface */ + struct mtx uif_mtx; /* mutex for interface */ +}; + +#define USBPFIF_LOCK(uif) mtx_lock(&(uif)->uif_mtx) +#define USBPFIF_UNLOCK(uif) mtx_unlock(&(uif)->uif_mtx) + +#endif + +/* + * Structure prepended to each packet. + */ +struct usbpf_ts { + usbpf_int64 ut_sec; /* seconds */ + usbpf_u_int64 ut_frac; /* fraction */ +}; +struct usbpf_xhdr { + struct usbpf_ts uh_tstamp; /* time stamp */ + usbpf_u_int32 uh_caplen; /* length of captured portion */ + usbpf_u_int32 uh_datalen; /* original length of packet */ + u_short uh_hdrlen; /* length of uff header (this struct + plus alignment padding) */ +}; + +#define USBPF_BUFMODE_BUFFER 1 /* Kernel buffers with read(). */ +#define USBPF_BUFMODE_ZBUF 2 /* Zero-copy buffers. */ + +struct usbpf_pkthdr { + int up_busunit; /* Host controller unit number */ + u_char up_address; /* USB device address */ + u_char up_endpoint; /* USB endpoint */ + u_char up_type; /* points SUBMIT / DONE */ + u_char up_xfertype; /* Transfer type */ + u_int32_t up_flags; /* Transfer flags */ +#define USBPF_FLAG_FORCE_SHORT_XFER (1 << 0) +#define USBPF_FLAG_SHORT_XFER_OK (1 << 1) +#define USBPF_FLAG_SHORT_FRAMES_OK (1 << 2) +#define USBPF_FLAG_PIPE_BOF (1 << 3) +#define USBPF_FLAG_PROXY_BUFFER (1 << 4) +#define USBPF_FLAG_EXT_BUFFER (1 << 5) +#define USBPF_FLAG_MANUAL_STATUS (1 << 6) +#define USBPF_FLAG_NO_PIPE_OK (1 << 7) +#define USBPF_FLAG_STALL_PIPE (1 << 8) + u_int32_t up_status; /* Transfer status */ +#define USBPF_STATUS_OPEN (1 << 0) +#define USBPF_STATUS_TRANSFERRING (1 << 1) +#define USBPF_STATUS_DID_DMA_DELAY (1 << 2) +#define USBPF_STATUS_DID_CLOSE (1 << 3) +#define USBPF_STATUS_DRAINING (1 << 4) +#define USBPF_STATUS_STARTED (1 << 5) +#define USBPF_STATUS_BW_RECLAIMED (1 << 6) +#define USBPF_STATUS_CONTROL_XFR (1 << 7) +#define USBPF_STATUS_CONTROL_HDR (1 << 8) +#define USBPF_STATUS_CONTROL_ACT (1 << 9) +#define USBPF_STATUS_CONTROL_STALL (1 << 10) +#define USBPF_STATUS_SHORT_FRAMES_OK (1 << 11) +#define USBPF_STATUS_SHORT_XFER_OK (1 << 12) +#if USB_HAVE_BUSDMA +#define USBPF_STATUS_BDMA_ENABLE (1 << 13) +#define USBPF_STATUS_BDMA_NO_POST_SYNC (1 << 14) +#define USBPF_STATUS_BDMA_SETUP (1 << 15) +#endif +#define USBPF_STATUS_ISOCHRONOUS_XFR (1 << 16) +#define USBPF_STATUS_CURR_DMA_SET (1 << 17) +#define USBPF_STATUS_CAN_CANCEL_IMMED (1 << 18) +#define USBPF_STATUS_DOING_CALLBACK (1 << 19) + u_int32_t up_length; /* Total data length (submit/actual) */ + u_int32_t up_frames; /* USB frame number (submit/actual) */ + u_int32_t up_error; /* usb_error_t */ + u_int32_t up_interval; /* for interrupt and isoc */ + /* sizeof(struct usbpf_pkthdr) == 128 bytes */ + u_char up_reserved[96]; +}; + +struct usbpf_version { + u_short uv_major; + u_short uv_minor; +}; +#define USBPF_MAJOR_VERSION 1 +#define USBPF_MINOR_VERSION 1 + +#define USBPF_IFNAMSIZ 32 +struct usbpf_ifreq { + /* bus name, e.g. "usbus0" */ + char ufr_name[USBPF_IFNAMSIZ]; +}; + +/* + * Structure for UIOCSETF. + */ +struct usbpf_program { + u_int uf_len; + struct usbpf_insn *uf_insns; +}; + +/* + * Struct returned by UIOCGSTATS. + */ +struct usbpf_stat { + u_int us_recv; /* number of packets received */ + u_int us_drop; /* number of packets dropped */ +}; + +#define UIOCGBLEN _IOR('U', 102, u_int) +#define UIOCSBLEN _IOWR('U', 102, u_int) +#define UIOCSETF _IOW('U', 103, struct usbpf_program) +#define UIOCSETIF _IOW('U', 108, struct usbpf_ifreq) +#define UIOCSRTIMEOUT _IOW('U', 109, struct timeval) +#define UIOCGRTIMEOUT _IOR('U', 110, struct timeval) +#define UIOCGSTATS _IOR('U', 111, struct usbpf_stat) +#define UIOCVERSION _IOR('U', 113, struct usbpf_version) +#define UIOCSETWF _IOW('U', 123, struct usbpf_program) + +#define USBPF_XFERTAP_SUBMIT 0 +#define USBPF_XFERTAP_DONE 1 + +#ifdef _KERNEL +void usbpf_attach(struct usb_bus *, struct usbpf_if **); +void usbpf_detach(struct usb_bus *); +void usbpf_xfertap(struct usb_xfer *, int); +#endif + +#endif diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c index e0f5a3bc7cd..cdaa227c52a 100644 --- a/sys/dev/usb/usb_transfer.c +++ b/sys/dev/usb/usb_transfer.c @@ -60,6 +60,7 @@ #include #include +#include struct usb_std_packet_size { struct { @@ -2196,6 +2197,9 @@ usbd_callback_wrapper(struct usb_xfer_queue *pq) } } + if (xfer->usb_state != USB_ST_SETUP) + usbpf_xfertap(xfer, USBPF_XFERTAP_DONE); + /* call processing routine */ (xfer->callback) (xfer, xfer->error); @@ -2383,6 +2387,8 @@ usbd_transfer_start_cb(void *arg) DPRINTF("start\n"); + usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); + /* start the transfer */ (ep->methods->start) (xfer); @@ -2560,6 +2566,8 @@ usbd_pipe_start(struct usb_xfer_queue *pq) } DPRINTF("start\n"); + usbpf_xfertap(xfer, USBPF_XFERTAP_SUBMIT); + /* start USB transfer */ (ep->methods->start) (xfer); diff --git a/sys/modules/usb/usb/Makefile b/sys/modules/usb/usb/Makefile index 32a21075005..5bae119aaae 100644 --- a/sys/modules/usb/usb/Makefile +++ b/sys/modules/usb/usb/Makefile @@ -32,11 +32,11 @@ S= ${.CURDIR}/../../.. KMOD= usb SRCS= bus_if.h device_if.h usb_if.h usb_if.c vnode_if.h \ opt_usb.h opt_bus.h opt_ddb.h \ - usbdevs.h usbdevs_data.h \ + usb_pf.h usbdevs.h usbdevs_data.h \ usb_busdma.c usb_controller.c usb_compat_linux.c usb_core.c usb_debug.c \ usb_dev.c usb_device.c usb_dynamic.c usb_error.c usb_generic.c \ usb_handle_request.c usb_hid.c usb_hub.c usb_lookup.c usb_mbuf.c \ - usb_msctest.c usb_parse.c usb_process.c usb_request.c \ + usb_msctest.c usb_parse.c usb_pf.c usb_process.c usb_request.c \ usb_transfer.c usb_util.c .include