LWN.net Logo

usbmon, next round

From:  Pete Zaitcev <zaitcev@redhat.com>
To:  paolo.abeni@email.it
Subject:  usbmon, next round
Date:  Tue, 21 Nov 2006 22:56:20 -0800
Cc:  zaitcev@redhat.com, linux-usb-devel@lists.sourceforge.net
Archive-link:  Article, Thread

Hi, Paolo:

Here's what I have right now. It pretty much takes shape, only mmap is
remaining to be done. I'm basically going to copy your code there, only
change it from 4-space to 1-tab formatting.

This reminds me, I need your copyright statement for the banner comment.
Please send me one.

-- Pete

diff -uprN -X dontdiff linux-2.6.18/Documentation/usb/usbmon.txt
linux-2.6.18-ub/Documentation/usb/usbmon.txt
--- linux-2.6.18/Documentation/usb/usbmon.txt	2006-09-29 21:26:46.000000000 -0700
+++ linux-2.6.18-ub/Documentation/usb/usbmon.txt	2006-11-20 21:00:55.000000000 -0800
@@ -170,4 +170,37 @@ dd65f0e8 4128379808 C Bo:005:02 0 31 >
 
 * Raw binary format and API
 
-TBD
+struct usbmon_packet {
+	u64 id;			/*  0: URB ID - from submission to callback */
+	unsigned char type;	/*  8: Same as text; extensible. */
+	unsigned char xfer_type; /*    ISO, Intr, Control, Bulk; 0x80 IN */
+	unsigned char epnum;	/*     Endpoint number */
+	unsigned char devnum;	/*     Device address */
+	u16 busnum;		/* 12: Bus number */
+	char flag_setup;	/* 14: Same as text */
+	char flag_data;		/* 15: Same as text; Binary zero is OK. */
+	s64 ts_sec;		/* 16: gettimeofday */
+	s32 ts_usec;		/* 24: gettimeofday */
+	int status;		/* 28: */
+	unsigned int length;	/* 32: Length of data (submitted or actual) */
+	unsigned int len_cap;	/* 36: Delivered length */
+	unsigned char setup[8];	/* 40: Only for Control 'S' */
+};				/* 48 bytes total */
+
+Minor structure: 0 is "everything", the rest is bus number.
+
+-- ID leaks a kernel pointer (same as text API)
+   Since callbacks terminate, is likely to change to a real random or seq. ID
+-- Packets are aligned to 64 bytes.
+
+ioctl:
+ fetch (blocking)
+ FNBIO - bytes available (includes headers)
+ stats, loss
+ set buffer size
+ mfetch (for mmap)
+
+select/poll ok
+lseek: not working
+mmap:
+ ioctl mfetch returns array of ptrs, frees old buffers
diff -uprN -X dontdiff linux-2.6.18/drivers/usb/mon/Makefile
linux-2.6.18-ub/drivers/usb/mon/Makefile
--- linux-2.6.18/drivers/usb/mon/Makefile	2006-03-17 16:53:54.000000000 -0800
+++ linux-2.6.18-ub/drivers/usb/mon/Makefile	2006-11-09 19:35:05.000000000 -0800
@@ -2,7 +2,7 @@
 # Makefile for USB Core files and filesystem
 #
 
-usbmon-objs	:= mon_main.o mon_stat.o mon_text.o mon_dma.o
+usbmon-objs	:= mon_main.o mon_stat.o mon_text.o mon_bin.o mon_dma.o
 
 # This does not use CONFIG_USB_MON because we want this to use a tristate.
 obj-$(CONFIG_USB)	+= usbmon.o
diff -uprN -X dontdiff linux-2.6.18/drivers/usb/mon/mon_bin.c
linux-2.6.18-ub/drivers/usb/mon/mon_bin.c
--- linux-2.6.18/drivers/usb/mon/mon_bin.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.6.18-ub/drivers/usb/mon/mon_bin.c	2006-11-21 21:41:37.000000000 -0800
@@ -0,0 +1,953 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is a binary format reader.
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/cdev.h>
+#include <linux/usb.h>
+#include <linux/poll.h>
+
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+/*
+ * Defined by USB 2.0 clause 9.3, table 9.2.
+ */
+#define SETUP_LEN  8
+
+/* ioctl macros */
+#define MON_IOC_MAGIC 0x92
+
+#define MON_IOCQ_URB_LEN _IO(MON_IOC_MAGIC, 1)
+#define MON_IOCX_URB  _IOWR(MON_IOC_MAGIC, 2, struct mon_bin_hdr)
+#define MON_IOCG_STATS _IOR(MON_IOC_MAGIC, 3, struct mon_bin_stats)
+#define MON_IOCT_RING_SIZE _IO(MON_IOC_MAGIC, 4)
+#define MON_IOCQ_RING_SIZE _IO(MON_IOC_MAGIC, 5)
+#define MON_IOCX_GET   _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get)
+#ifdef CONFIG_COMPAT
+#define MON_IOCX_GET32 _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get32)
+#endif
+
+/*
+ * Some architectures have enormous basic pages (16KB for ia64, 64KB for ppc).
+ * But it's all right. Just use a simple way to make sure the chunk is never
+ * smaller than a page.
+ *
+ * N.B. An application does not know our chunk size.
+ *
+ * Woops, get_zeroed_page() returns a single page. I guess we're stuck with
+ * page-sized chunks for the time being.
+ */
+#define CHUNK_SIZE   PAGE_SIZE
+#define CHUNK_ALIGN(x)   (((x)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1))
+
+/*
+ * The magic limit was calculated so that it allows the monitoring
+ * application to pick data once in two ticks. This way, another application,
+ * which presumably drives the bus, gets to hog CPU, yet we collect our data.
+ * If HZ is 100, a 480 mbit/s bus drives 614 KB every jiffy. USB has an
+ * enormous overhead built into the bus protocol, so we need about 1000 KB.
+ *
+ * This is still too much for most cases, where we just snoop a few
+ * descriptor fetches for enumeration. So, the default is a "reasonable"
+ * amount for systems with HZ=250 and incomplete bus saturation.
+ *
+ * XXX What about multi-megabyte URBs which take minutes to transfer?
+ */
+#define BUFF_MAX  CHUNK_ALIGN(1200*1024)
+#define BUFF_DFL   CHUNK_ALIGN(300*1024)
+#define BUFF_MIN     CHUNK_ALIGN(8*1024)
+
+/*
+ * The per-event API header (2 per URB).
+ *
+ * This structure is seen in userland as defined by the documentation.
+ */
+struct mon_bin_hdr {
+	u64 id;			/* URB ID - from submission to callback */
+	unsigned char type;	/* Same as in text API; extensible. */
+	unsigned char xfer_type;	/* ISO, Intr, Control, Bulk; 0x80 IN */
+	unsigned char epnum;	/* Endpoint number */
+	unsigned char devnum;	/* Device address */
+	unsigned short busnum;	/* Bus number */
+	char flag_setup;
+	char flag_data;
+	s64 ts_sec;		/* gettimeofday */
+	s32 ts_usec;		/* gettimeofday */
+	int status;
+	unsigned int len_urb;	/* Length of data (submitted or actual) */
+	unsigned int len_cap;	/* Delivered length */
+	unsigned char setup[SETUP_LEN];	/* Only for Control S-type */
+};
+
+struct mon_bin_urb_info {
+	struct mon_bin_hdr hdr;
+	char * data;
+};
+
+/* per file statistic */
+struct mon_bin_stats {
+	u32 queued;
+	u32 dropped;
+};
+
+struct mon_bin_get {
+	struct mon_bin_hdr __user *hdr;	/* Only 48 bytes, not 64. */
+	void __user *data;
+	size_t alloc;		/* Length of data (can be zero) */
+};
+
+#ifdef CONFIG_COMPAT
+struct mon_bin_get32 {
+	u32 hdr32;
+	u32 data32;
+	u32 alloc32;
+};
+#endif
+
+/* Having these two values same prevents wrapping of the mon_bin_hdr */
+#define PKT_ALIGN   64
+#define PKT_SIZE    64
+
+/* max number of USB bus supported */
+#define MON_BIN_MAX_MINOR 128
+
+/*
+ * The buffer: map of used pages.
+ */
+struct mon_pgmap {
+	struct page *pg;
+	unsigned char *ptr;	/* XXX just use page_to_virt everywhere? */
+};
+
+/*
+ * This gets associated with an open file struct.
+ */
+struct mon_reader_bin {
+	/* The buffer: one per open. */
+	spinlock_t b_lock;		/* Protect b_cnt, b_in */
+	unsigned int b_size;		/* Current size of the buffer - bytes */
+	unsigned int b_cnt;		/* Bytes used */
+	unsigned int b_in, b_out;	/* Offsets into buffer - bytes */
+	unsigned int b_read;		/* Amount of read data in curr. pkt. */
+	struct mon_pgmap *b_vec;	/* The map array */
+	wait_queue_head_t b_wait;	/* Wait for data here */
+
+	struct mutex fetch_lock;	/* Protect b_read, b_out */
+	int mmap_active;
+
+	/* A list of these is needed for "bus 0". Some time later. */
+	struct mon_reader r;
+
+	/* Stats */
+	unsigned int nevents;		/* Fill level, not counting fillers */
+	unsigned int cnt_lost;
+};
+
+static inline struct mon_bin_hdr *MON_OFF2HDR(const struct mon_reader_bin *rp,
+    unsigned int offset)
+{
+	return (struct mon_bin_hdr *)
+	    (rp->b_vec[offset / CHUNK_SIZE].ptr + offset % CHUNK_SIZE);
+}
+
+#define MON_RING_EMPTY(rp)	((rp)->b_cnt == 0)
+
+static dev_t mon_bin_dev0;
+static struct cdev mon_bin_cdev;
+
+static void mon_buff_area_fill(const struct mon_reader_bin *rp,
+    unsigned int offset, unsigned int size);
+static int mon_alloc_buff(struct mon_pgmap *map, int npages);
+static void mon_free_buff(struct mon_pgmap *map, int npages);
+
+/*
+ * This is a "chunked memcpy". It does not manipulate any counters.
+ * But it returns the new offset for repeated application.
+ */
+unsigned int mon_copy_to_buff(const struct mon_reader_bin *this,
+    unsigned int off, const unsigned char *from, unsigned int length)
+{
+	unsigned int step_len;
+	unsigned char *buf;
+	unsigned int in_page;
+
+	while (length) {
+		/*
+		 * Determine step_len.
+		 */
+		step_len = length;
+		in_page = CHUNK_SIZE - (off & (CHUNK_SIZE-1));
+		if (in_page < step_len)
+			step_len = in_page;
+
+		/*
+		 * Copy data and advance pointers.
+		 */
+		buf = this->b_vec[off / CHUNK_SIZE].ptr + off % CHUNK_SIZE;
+		memcpy(buf, from, step_len);
+		if ((off += step_len) >= this->b_size) off = 0;
+		from += step_len;
+		length -= step_len;
+	}
+	return off;
+}
+
+/*
+ * This is a little worse than the above because it's "chunked copy_to_user".
+ * The return value is an error code, not an offset.
+ */
+static int copy_from_buf(const struct mon_reader_bin *this, unsigned int off,
+    char __user *to, int length)
+{
+	unsigned int step_len;
+	unsigned char *buf;
+	unsigned int in_page;
+
+	while (length) {
+		/*
+		 * Determine step_len.
+		 */
+		step_len = length;
+		in_page = CHUNK_SIZE - (off & (CHUNK_SIZE-1));
+		if (in_page < step_len)
+			step_len = in_page;
+
+		/*
+		 * Copy data and advance pointers.
+		 */
+		buf = this->b_vec[off / CHUNK_SIZE].ptr + off % CHUNK_SIZE;
+		if (copy_to_user(to, buf, step_len))
+			return -EINVAL;
+		if ((off += step_len) >= this->b_size) off = 0;
+		to += step_len;
+		length -= step_len;
+	}
+	return off;
+}
+
+/*
+ * Allocate an (aligned) area in the buffer.
+ * This is called under b_lock.
+ * Returns ~0 on failure.
+ */
+static unsigned int mon_buff_area_alloc(struct mon_reader_bin *rp,
+    unsigned int size)
+{
+	unsigned int offset;
+
+	size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1);
+	if (rp->b_cnt + size > rp->b_size)
+		return ~0;
+	offset = rp->b_in;
+	rp->b_cnt += size;
+	if ((rp->b_in += size) >= rp->b_size)
+		rp->b_in -= rp->b_size;
+	return offset;
+}
+
+/*
+ * This is the same thing as mon_buff_area_alloc, only it does not allow
+ * buffers to wrap. This is needed by applications which pass references
+ * into mmap-ed buffers up their stacks (libpcap can do that).
+ *
+ * Currently, we always have the header stuck with the data, although
+ * it is not strictly speaking necessary.
+ *
+ * When a buffer would wrap, we place a filler packet to mark the space.
+ */
+static unsigned int mon_buff_area_alloc_contiguous(struct mon_reader_bin *rp,
+    unsigned int size)
+{
+	unsigned int offset;
+	unsigned int fill_size;
+
+	size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1);
+	if (rp->b_cnt + size > rp->b_size)
+		return ~0;
+	if (rp->b_in + size > rp->b_size) {
+		/*
+		 * This would wrap. Find if we still have space after
+		 * skipping to the end of the buffer. If we do, place
+		 * a filler packet and allocate a new packet.
+		 */
+		fill_size = rp->b_size - rp->b_in;
+		if (rp->b_cnt + size + fill_size > rp->b_size)
+			return ~0;
+		mon_buff_area_fill(rp, rp->b_in, fill_size);
+
+		offset = 0;
+		rp->b_in = size;
+		rp->b_cnt += size + fill_size;
+	} else if (rp->b_in + size == rp->b_size) {
+		offset = rp->b_in;
+		rp->b_in = 0;
+		rp->b_cnt += size;
+	} else {
+		offset = rp->b_in;
+		rp->b_in += size;
+		rp->b_cnt += size;
+	}
+	return offset;
+}
+
+/*
+ * Return a few (kilo-)bytes to the head of the buffer.
+ * This is used if a DMA fetch fails.
+ */
+static void mon_buff_area_shrink(struct mon_reader_bin *rp, unsigned int size)
+{
+
+	size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1);
+	rp->b_cnt -= size;
+	if (rp->b_in < size)
+		rp->b_in += rp->b_size;
+	rp->b_in -= size;
+}
+
+/*
+ * This has to be called under both b_lock and fetch_lock, because
+ * it accesses both b_cnt and b_out.
+ */
+static void mon_buff_area_free(struct mon_reader_bin *rp, unsigned int size)
+{
+
+	size = (size + PKT_ALIGN-1) & ~(PKT_ALIGN-1);
+	rp->b_cnt -= size;
+	if ((rp->b_out += size) >= rp->b_size)
+		rp->b_out -= rp->b_size;
+}
+
+static void mon_buff_area_fill(const struct mon_reader_bin *rp,
+    unsigned int offset, unsigned int size)
+{
+	struct mon_bin_hdr *ep;
+
+	ep = MON_OFF2HDR(rp, offset);
+	memset(ep, 0, PKT_SIZE);
+	ep->type = '@';
+	ep->len_cap = size - PKT_SIZE;
+}
+
+static inline char mon_bin_get_setup(unsigned char *setupb,
+    const struct urb *urb, char ev_type)
+{
+
+	if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
+		return '-';
+
+	if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
+		return mon_dmapeek(setupb, urb->setup_dma, SETUP_LEN);
+	if (urb->setup_packet == NULL)
+		return 'Z';
+
+	memcpy(setupb, urb->setup_packet, SETUP_LEN);
+	return 0;
+}
+
+static char mon_bin_get_data(const struct mon_reader_bin *rp,
+    unsigned int offset, struct urb *urb, unsigned int length)
+{
+
+	if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
+		mon_dmapeek_vec(rp, offset, urb->transfer_dma, length);
+		return 0;
+	}
+
+	if (urb->transfer_buffer == NULL)
+		return 'Z';
+
+	mon_copy_to_buff(rp, offset, urb->transfer_buffer, length);
+	return 0;
+}
+
+static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
+    char ev_type)
+{
+	unsigned long flags;
+	struct timeval ts;
+	unsigned int urb_length;
+	unsigned int offset;
+	unsigned int length;
+	struct mon_bin_hdr *ep;
+	char data_tag = 0;
+
+	do_gettimeofday(&ts);
+
+	spin_lock_irqsave(&rp->b_lock, flags);
+
+	/*
+	 * Find the maximum allowable length, then allocate space.
+	 */
+	urb_length = (ev_type == 'S') ?
+	    urb->transfer_buffer_length : urb->actual_length;
+	length = urb_length;
+
+	if (length >= rp->b_size/5)
+		length = rp->b_size/5;
+
+	if (usb_pipein(urb->pipe)) {
+		if (ev_type == 'S') {
+			length = 0;
+			data_tag = '<';
+		}
+	} else {
+		if (ev_type == 'C') {
+			length = 0;
+			data_tag = '>';
+		}
+	}
+
+	if (rp->mmap_active)
+		offset = mon_buff_area_alloc_contiguous(rp, length + PKT_SIZE);
+	else
+		offset = mon_buff_area_alloc(rp, length + PKT_SIZE);
+	if (offset == ~0) {
+		spin_unlock_irqrestore(&rp->b_lock, flags);
+		return;
+	}
+
+	ep = MON_OFF2HDR(rp, offset);
+	if ((offset += PKT_SIZE) >= rp->b_size) offset = 0;
+
+	/*
+	 * Fill the allocated area.
+	 */
+	memset(ep, 0, PKT_SIZE);
+	ep->type = ev_type;
+	/* We use the fact that usb_pipein() returns 0x80 */
+	ep->xfer_type = usb_pipetype(urb->pipe) | usb_pipein(urb->pipe);
+	ep->epnum = usb_pipeendpoint(urb->pipe);
+	ep->devnum = usb_pipedevice(urb->pipe);
+	ep->busnum = rp->r.m_bus->u_bus->busnum;
+	ep->id = (unsigned long) urb;
+	ep->ts_sec = ts.tv_sec;
+	ep->ts_usec = ts.tv_usec;
+	ep->status = urb->status;
+	ep->len_urb = urb_length;
+	ep->len_cap = length;
+
+	ep->flag_setup = mon_bin_get_setup(ep->setup, urb, ev_type);
+	if (length != 0) {
+		ep->flag_data = mon_bin_get_data(rp, offset, urb, length);
+		if (ep->flag_data != 0) {	/* Yes, it's 0x00, not '0' */
+			ep->len_cap = 0;
+			mon_buff_area_shrink(rp, length);
+		}
+	} else {
+		ep->flag_data = data_tag;
+	}
+
+	rp->nevents++;
+	spin_unlock_irqrestore(&rp->b_lock, flags);
+
+	wake_up(&rp->b_wait);
+}
+
+static void mon_bin_submit(void *data, struct urb *urb)
+{
+	struct mon_reader_bin *rp = data;
+	mon_bin_event(rp, urb, 'S');
+}
+
+static void mon_bin_complete(void *data, struct urb *urb)
+{
+	struct mon_reader_bin *rp = data;
+	mon_bin_event(rp, urb, 'C');
+}
+
+static void mon_bin_error(void *data, struct urb *urb, int error)
+{
+	;
+}
+
+static int mon_bin_open(struct inode *inode, struct file *file)
+{
+	struct mon_bus *mbus;
+	struct usb_bus *ubus;
+	struct mon_reader_bin *rp;
+	size_t size;
+	int rc;
+
+	mutex_lock(&mon_lock);
+	if ((mbus = mon_bus_lookup(iminor(inode))) == NULL) {
+		mutex_unlock(&mon_lock);
+		return -ENODEV;
+	}
+	if ((ubus = mbus->u_bus) == NULL) {
+		printk(KERN_ERR TAG ": consistency error on open\n");
+		mutex_unlock(&mon_lock);
+		return -ENODEV;
+	}
+
+	rp = kzalloc(sizeof(struct mon_reader_bin), GFP_KERNEL);
+	if (rp == NULL) {
+		rc = -ENOMEM;
+		goto err_alloc;
+	}
+	spin_lock_init(&rp->b_lock);
+	init_waitqueue_head(&rp->b_wait);
+	mutex_init(&rp->fetch_lock);
+
+	rp->b_size = BUFF_DFL;
+
+	size = sizeof(struct mon_pgmap) * (rp->b_size/CHUNK_SIZE);
+	if ((rp->b_vec = kzalloc(size, GFP_KERNEL)) == NULL) {
+		rc = -ENOMEM;
+		goto err_allocvec;
+	}
+
+	if ((rc = mon_alloc_buff(rp->b_vec, rp->b_size/CHUNK_SIZE)) < 0)
+		goto err_allocbuff;
+
+	rp->r.m_bus = mbus;
+	rp->r.r_data = rp;
+	rp->r.rnf_submit = mon_bin_submit;
+	rp->r.rnf_error = mon_bin_error;
+	rp->r.rnf_complete = mon_bin_complete;
+
+	mon_reader_add(mbus, &rp->r);
+
+	file->private_data = rp;
+	mutex_unlock(&mon_lock);
+	return 0;
+
+err_allocbuff:
+	kfree(rp->b_vec);
+err_allocvec:
+	kfree(rp);
+err_alloc:
+	mutex_unlock(&mon_lock);
+	return rc;
+}
+
+/*
+ * Extract an event from buffer and copy it to user space.
+ * Wait if there is no event ready.
+ */
+static int mon_bin_get_event(struct file *file, struct mon_reader_bin *rp, 
+    struct mon_bin_hdr __user *hdr, void __user *data, unsigned int nbytes)
+{
+	DECLARE_WAITQUEUE(waita, current);
+	unsigned long flags;
+	struct mon_bin_hdr *ep;
+	size_t step_len;
+	unsigned int offset;
+
+	mutex_lock(&rp->fetch_lock);
+
+	add_wait_queue(&rp->b_wait, &waita);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	spin_lock_irqsave(&rp->b_lock, flags);
+	while (!MON_RING_EMPTY(rp)) {
+		spin_unlock_irqrestore(&rp->b_lock, flags);
+
+		if (file->f_flags & O_NONBLOCK) {
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&rp->b_wait, &waita);
+			return -EWOULDBLOCK; /* Same as EAGAIN in Linux */
+		}
+		schedule();
+		if (signal_pending(current)) {
+			remove_wait_queue(&rp->b_wait, &waita);
+			return -EINTR;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+
+		spin_lock_irqsave(&rp->b_lock, flags);
+	}
+	spin_unlock_irqrestore(&rp->b_lock, flags);
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rp->b_wait, &waita);
+
+	ep = MON_OFF2HDR(rp, rp->b_out);
+
+	if (copy_to_user(hdr, ep, sizeof(struct mon_bin_hdr))) {
+		mutex_unlock(&rp->fetch_lock);
+		return -EFAULT;
+	}
+
+	step_len = min(ep->len_cap, nbytes);
+	if ((offset = rp->b_out + PKT_SIZE) >= rp->b_size) offset = 0;
+
+	if (copy_from_buf(rp, offset, data, step_len)) {
+		mutex_unlock(&rp->fetch_lock);
+		return -EFAULT;
+	}
+
+	spin_lock_irqsave(&rp->b_lock, flags);
+	mon_buff_area_free(rp, PKT_SIZE + ep->len_cap);
+	spin_unlock_irqrestore(&rp->b_lock, flags);
+	rp->b_read = 0;
+
+	mutex_unlock(&rp->fetch_lock);
+	return 0;
+}
+
+static int mon_bin_release(struct inode *inode, struct file *file)
+{
+	struct mon_reader_bin *rp = file->private_data;
+	struct mon_bus* mbus = rp->r.m_bus;
+
+	mutex_lock(&mon_lock);
+
+	if (mbus->nreaders <= 0) {
+		printk(KERN_ERR TAG ": consistency error on close\n");
+		mutex_unlock(&mon_lock);
+		return 0;
+	}
+	mon_reader_del(mbus, &rp->r);
+
+	mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE);
+	kfree(rp->b_vec);
+	kfree(rp);
+
+	mutex_unlock(&mon_lock);
+	return 0;
+}
+
+static ssize_t mon_bin_read(struct file *file, char __user *buf,
+    size_t nbytes, loff_t *ppos)
+{
+	struct mon_reader_bin *rp = file->private_data;
+	DECLARE_WAITQUEUE(waita, current);
+	unsigned long flags;
+	struct mon_bin_hdr *ep;
+	unsigned int offset;
+	size_t step_len;
+	char *ptr;
+	ssize_t done = 0;
+
+	mutex_lock(&rp->fetch_lock);
+
+	/* XXX Factor this out with mon_bin_get_event */
+	add_wait_queue(&rp->b_wait, &waita);
+	set_current_state(TASK_INTERRUPTIBLE);
+	spin_lock_irqsave(&rp->b_lock, flags);
+	while (!MON_RING_EMPTY(rp)) {
+		spin_unlock_irqrestore(&rp->b_lock, flags);
+		if (file->f_flags & O_NONBLOCK) {
+			set_current_state(TASK_RUNNING);
+			remove_wait_queue(&rp->b_wait, &waita);
+			return -EWOULDBLOCK; /* Same as EAGAIN in Linux */
+		}
+		schedule();
+		if (signal_pending(current)) {
+			remove_wait_queue(&rp->b_wait, &waita);
+			return -EINTR;
+		}
+		set_current_state(TASK_INTERRUPTIBLE);
+		spin_lock_irqsave(&rp->b_lock, flags);
+	}
+	spin_unlock_irqrestore(&rp->b_lock, flags);
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&rp->b_wait, &waita);
+
+	ep = MON_OFF2HDR(rp, rp->b_out);
+
+	if (rp->b_read < sizeof(struct mon_bin_hdr)) {
+		step_len = min(nbytes, sizeof(struct mon_bin_hdr) - rp->b_read);
+		ptr = ((char *)ep) + rp->b_read;
+		if (step_len && copy_to_user(buf, ptr, step_len)) {
+			mutex_unlock(&rp->fetch_lock);
+			return -EFAULT;
+		}
+		nbytes -= step_len;
+		buf += step_len;
+		rp->b_read += step_len;
+		done += step_len;
+	}
+
+	if (rp->b_read >= sizeof(struct mon_bin_hdr)) {
+		step_len = min(nbytes, (size_t)ep->len_cap);
+		offset = rp->b_out + PKT_SIZE;
+		offset += rp->b_read - sizeof(struct mon_bin_hdr);
+		if (offset >= rp->b_size)
+			offset -= rp->b_size;
+		if (copy_from_buf(rp, offset, buf, step_len)) {
+			mutex_unlock(&rp->fetch_lock);
+			return -EFAULT;
+		}
+		nbytes -= step_len;
+		buf += step_len;
+		rp->b_read += step_len;
+		done += step_len;
+	}
+
+	/*
+	 * Check if whole packet was read, and if so, jump to the next one.
+	 */
+	if (rp->b_read >= sizeof(struct mon_bin_hdr) + ep->len_cap) {
+		spin_lock_irqsave(&rp->b_lock, flags);
+		mon_buff_area_free(rp, PKT_SIZE + ep->len_cap);
+		spin_unlock_irqrestore(&rp->b_lock, flags);
+		rp->b_read = 0;
+	}
+
+	mutex_unlock(&rp->fetch_lock);
+	return done;
+}
+
+static int mon_bin_ioctl(struct inode *inode, struct file *file, 
+    unsigned int cmd, unsigned long arg)
+{
+	struct mon_reader_bin *rp = file->private_data;
+	// struct mon_bus* mbus = rp->r.m_bus;
+	int ret = 0;
+	struct mon_bin_hdr *ep;
+	unsigned long flags;
+
+	switch (cmd) {
+
+	case MON_IOCQ_URB_LEN:
+		/*
+		 * N.B. This only returns the size of data, without the header.
+		 */
+		spin_lock_irqsave(&rp->b_lock, flags);
+		if (!MON_RING_EMPTY(rp)) {
+			ep = MON_OFF2HDR(rp, rp->b_out);
+			ret = ep->len_cap;
+		}
+		spin_unlock_irqrestore(&rp->b_lock, flags);
+		break;
+
+	case MON_IOCQ_RING_SIZE:
+		ret = rp->b_size;
+		break;
+
+#if 0
++    case MON_IOCT_RING_SIZE:
++        spin_lock_irqsave(&mbus->lock, flags);
++        ret = mon_bin_create_ring(&ring, arg);
++        if (!ret) {
++            mon_bin_destroy_ring(&rp->ring);
++            rp->ring = ring;
++        }
++        spin_unlock_irqrestore(&mbus->lock, flags);
++        break;
+#endif
+
+	case MON_IOCX_URB:
+		{
+		/*
+		 * This is the original ABI which Paolo's libpcap uses.
+		 * Don't use this. Use MON_IOCX_GET instead.
+		 * N.B. This ioctl is misnamed: it does not retrieve URBs,
+		 * it retrieves events (2 events per one URB).
+		 */
+		struct mon_bin_urb_info info;
+
+		if (copy_from_user(&info, (struct mon_bin_urb_info __user *)arg,
+                                 sizeof(struct mon_bin_urb_info)))
+			return -EFAULT;
+
+		ret = mon_bin_get_event(file, rp,
+		    (struct mon_bin_hdr __user *)arg, info.data,
+		    info.hdr.len_cap);
+		}
+		break;
+
+	case MON_IOCX_GET:
+		{
+		struct mon_bin_get getb;
+
+		if (copy_from_user(&getb, (void __user *)arg,
+					    sizeof(struct mon_bin_get)))
+			return -EFAULT;
+
+		if (getb.alloc > 0x10000000)	/* Want to cast to u32 */
+			return -EINVAL;
+		ret = mon_bin_get_event(file, rp,
+			  getb.hdr, getb.data, (unsigned int)getb.alloc);
+		}
+		break;
+
+#ifdef CONFIG_COMPAT
+	case MON_IOCX_GET32: {
+		struct mon_bin_get32 getb;
+
+		if (copy_from_user(&getb, (void __user *)arg,
+					    sizeof(struct mon_bin_get32)))
+			return -EFAULT;
+
+		ret = mon_bin_get_event(file, rp,
+		    compat_ptr(getb.hdr32), compat_ptr(getb.data32),
+		    getb.alloc32);
+		}
+		break;
+#endif
+
+	case MON_IOCG_STATS: {
+		struct mon_bin_stats __user *sp;
+		sp = (struct mon_bin_stats __user *)arg;
+		if (put_user(rp->cnt_lost, &sp->dropped))
+			return -EFAULT;
+		if (put_user(rp->nevents, &sp->queued))
+			return -EFAULT;
+		}
+		break;
+
+	default:
+		return -ENOTTY;
+	}
+
+	return ret;
+}
+
+static unsigned int
+mon_bin_poll(struct file *file, struct poll_table_struct *wait)
+{
+	struct mon_reader_bin *rp = file->private_data;
+	unsigned int mask = 0;
+	unsigned long flags;
+
+	if (file->f_mode & FMODE_READ)
+		poll_wait(file, &rp->b_wait, wait);
+
+	spin_lock_irqsave(&rp->b_lock, flags);
+	if (!MON_RING_EMPTY(rp))
+		mask |= POLLIN | POLLRDNORM;    /* readable */
+	spin_unlock_irqrestore(&rp->b_lock, flags);
+	return mask;
+}
+
+#if 0
++
++/*
++ * open and close: just keep track of how many times the device is
++ * mapped, to avoid releasing it.
++ */
++
++static void mon_bin_vma_open(struct vm_area_struct *vma)
++{
++    struct mon_reader_bin *rp = vma->vm_private_data;
++
++    rp->vmas++;
++}
++
++static void mon_bin_vma_close(struct vm_area_struct *vma)
++{
++    struct mon_reader_bin *rp = vma->vm_private_data;
++
++    rp->vmas--;
++}
++
++/*
++ * map ring pages to user space
++ */
++struct page *mon_bin_vma_nopage(struct vm_area_struct *vma,
++                                unsigned long address, int *type)
++{
++    unsigned long offset, chunk_idx, chunk_offset;
++    struct mon_reader_bin *rp = vma->vm_private_data;
++    struct page *pageptr = NOPAGE_SIGBUS;
++
++    offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
++    if (offset < PAGE_SIZE) {
++    	pageptr = virt_to_page(rp->ring.rd);
++    	goto found;
++    }
++    chunk_idx = (offset - PAGE_SIZE) >> MON_CHUNK_SHIFT;
++    if (chunk_idx > rp->ring.chunk_num)
++        goto out;
++    
++    chunk_offset = ((offset - PAGE_SIZE) & MON_CHUNK_MASK) & (~PAGE_MASK);
++    pageptr = virt_to_page(rp->ring.chunk_vec[chunk_idx] + chunk_offset);
++found:    
++    get_page(pageptr);
++    if (type)
++        *type = VM_FAULT_MINOR;
++out:
++    return pageptr;
++}
++
++struct vm_operations_struct mon_bin_vm_ops = {
++    .open =     mon_bin_vma_open,
++    .close =    mon_bin_vma_close,
++    .nopage =   mon_bin_vma_nopage,
++};
++
++int mon_bin_mmap(struct file *filp, struct vm_area_struct *vma)
++{
++    /* don't do anything here: "nopage" will set up page table entries */
++    vma->vm_ops = &mon_bin_vm_ops;
++    vma->vm_flags |= VM_RESERVED;
++    vma->vm_private_data = filp->private_data;
++    mon_bin_vma_open(vma);
++    return 0;
++
++}
+#endif
+
+struct file_operations mon_fops_binary = {
+	.owner =	THIS_MODULE,
+	.open =		mon_bin_open,
+	.llseek =	no_llseek,
+	.read =		mon_bin_read,
+	/* .write =	mon_text_write, */
+	.poll =		mon_bin_poll,
+	.ioctl =	mon_bin_ioctl,
+	.release =	mon_bin_release,
+};
+
+static int mon_alloc_buff(struct mon_pgmap *map, int npages)
+{
+	int n;
+	unsigned long vaddr;
+
+	for (n = 0; n < npages; n++) {
+		vaddr = get_zeroed_page(GFP_KERNEL);
+		if (vaddr == 0) {
+			while (n-- != 0)
+				free_page((unsigned long) map[n].ptr);
+			return -ENOMEM;
+		}
+		map[n].ptr = (unsigned char *) vaddr;
+		map[n].pg = virt_to_page(vaddr);
+	}
+	return 0;
+}
+
+static void mon_free_buff(struct mon_pgmap *map, int npages)
+{
+	int n;
+
+	for (n = 0; n < npages; n++)
+		free_page((unsigned long) map[n].ptr);
+}
+
+int __init mon_bin_init(void)
+{
+	int rc;
+
+	rc = alloc_chrdev_region(&mon_bin_dev0, 0, MON_BIN_MAX_MINOR, "usbmon");
+	if (rc < 0)
+		goto err_dev;
+
+	cdev_init(&mon_bin_cdev, &mon_fops_binary);
+	mon_bin_cdev.owner = THIS_MODULE;
+
+	rc = cdev_add(&mon_bin_cdev, mon_bin_dev0, MON_BIN_MAX_MINOR);
+	if (rc < 0)
+		goto err_add;
+
+	return 0;
+
+err_add:
+	unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
+err_dev:
+	return rc;
+}
+
+void __exit mon_bin_exit(void)
+{
+	cdev_del(&mon_bin_cdev);
+	unregister_chrdev_region(mon_bin_dev0, MON_BIN_MAX_MINOR);
+}
diff -uprN -X dontdiff linux-2.6.18/drivers/usb/mon/mon_dma.c
linux-2.6.18-ub/drivers/usb/mon/mon_dma.c
--- linux-2.6.18/drivers/usb/mon/mon_dma.c	2006-09-29 21:27:14.000000000 -0700
+++ linux-2.6.18-ub/drivers/usb/mon/mon_dma.c	2006-11-17 00:17:36.000000000 -0800
@@ -48,6 +48,36 @@ char mon_dmapeek(unsigned char *dst, dma
 	local_irq_restore(flags);
 	return 0;
 }
+
+void mon_dmapeek_vec(const struct mon_reader_bin *rp,
+    unsigned int offset, dma_addr_t dma_addr, unsigned int length)
+{
+	unsigned long flags;
+	unsigned int step_len;
+	struct page *pg;
+	unsigned char *map;
+	unsigned long page_off, page_len;
+
+	local_irq_save(flags);
+	while (length) {
+		/* compute number of bytes we are going to copy in this page */
+		step_len = length;
+		page_off = dma_addr & (PAGE_SIZE-1);
+		page_len = PAGE_SIZE - page_off;
+		if (page_len < step_len)
+			step_len = page_len;
+
+		/* copy data and advance pointers */
+		pg = phys_to_page(dma_addr);
+		map = kmap_atomic(pg, KM_IRQ0);
+		offset = mon_copy_to_buff(rp, offset, map + page_off, step_len);
+		kunmap_atomic(map, KM_IRQ0);
+		dma_addr += step_len;
+		length -= step_len;
+	}
+	local_irq_restore(flags);
+}
+
 #endif /* __i386__ */
 
 #ifndef MON_HAS_UNMAP
@@ -55,4 +85,11 @@ char mon_dmapeek(unsigned char *dst, dma
 {
 	return 'D';
 }
-#endif
+
+void mon_dmapeek_vec(const struct mon_reader_bin *rp,
+    unsigned int offset, dma_addr_t dma_addr, unsigned int length)
+{
+	;
+}
+
+#endif /* MON_HAS_UNMAP */
diff -uprN -X dontdiff linux-2.6.18/drivers/usb/mon/mon_main.c
linux-2.6.18-ub/drivers/usb/mon/mon_main.c
--- linux-2.6.18/drivers/usb/mon/mon_main.c	2006-09-29 21:27:14.000000000 -0700
+++ linux-2.6.18-ub/drivers/usb/mon/mon_main.c	2006-11-21 21:40:48.000000000 -0800
@@ -9,7 +9,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/usb.h>
-#include <linux/debugfs.h>
 #include <linux/smp_lock.h>
 #include <linux/notifier.h>
 #include <linux/mutex.h>
@@ -22,11 +21,10 @@ static void mon_complete(struct usb_bus 
 static void mon_stop(struct mon_bus *mbus);
 static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus);
 static void mon_bus_drop(struct kref *r);
-static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus);
+static void mon_bus_init(struct usb_bus *ubus);
 
 DEFINE_MUTEX(mon_lock);
 
-static struct dentry *mon_dir;		/* /dbg/usbmon */
 static LIST_HEAD(mon_buses);		/* All buses we know: struct mon_bus */
 
 /*
@@ -200,7 +198,7 @@ static void mon_stop(struct mon_bus *mbu
  */
 static void mon_bus_add(struct usb_bus *ubus)
 {
-	mon_bus_init(mon_dir, ubus);
+	mon_bus_init(ubus);
 }
 
 /*
@@ -212,8 +210,8 @@ static void mon_bus_remove(struct usb_bu
 
 	mutex_lock(&mon_lock);
 	list_del(&mbus->bus_link);
-	debugfs_remove(mbus->dent_t);
-	debugfs_remove(mbus->dent_s);
+	if (mbus->text_inited)
+		mon_text_del(mbus);
 
 	mon_dissolve(mbus, ubus);
 	kref_put(&mbus->ref, mon_bus_drop);
@@ -282,13 +280,9 @@ static void mon_bus_drop(struct kref *r)
  *  - refcount USB bus struct
  *  - link
  */
-static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
+static void mon_bus_init(struct usb_bus *ubus)
 {
-	struct dentry *d;
 	struct mon_bus *mbus;
-	enum { NAMESZ = 10 };
-	char name[NAMESZ];
-	int rc;
 
 	if ((mbus = kzalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
 		goto err_alloc;
@@ -304,57 +298,54 @@ static void mon_bus_init(struct dentry *
 	mbus->u_bus = ubus;
 	ubus->mon_bus = mbus;
 
-	rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
-	if (rc <= 0 || rc >= NAMESZ)
-		goto err_print_t;
-	d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_text);
-	if (d == NULL)
-		goto err_create_t;
-	mbus->dent_t = d;
-
-	rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
-	if (rc <= 0 || rc >= NAMESZ)
-		goto err_print_s;
-	d = debugfs_create_file(name, 0600, mondir, mbus, &mon_fops_stat);
-	if (d == NULL)
-		goto err_create_s;
-	mbus->dent_s = d;
+	mbus->text_inited = mon_text_add(mbus, ubus);
+	// mon_bin_add(...)
 
 	mutex_lock(&mon_lock);
 	list_add_tail(&mbus->bus_link, &mon_buses);
 	mutex_unlock(&mon_lock);
 	return;
 
-err_create_s:
-err_print_s:
-	debugfs_remove(mbus->dent_t);
-err_create_t:
-err_print_t:
-	kfree(mbus);
 err_alloc:
 	return;
 }
 
+/*
+ * Search a USB bus by number. Notice that USB bus numbers start from one,
+ * which we may later use to identify "all" with zero.
+ *
+ * This function must be called with mon_lock held.
+ *
+ * This is obviously inefficient and may be revised in the future.
+ */
+struct mon_bus *mon_bus_lookup(unsigned int num)
+{
+	struct list_head *p;
+	struct mon_bus *mbus;
+
+	list_for_each (p, &mon_buses) {
+		mbus = list_entry(p, struct mon_bus, bus_link);
+		if (mbus->u_bus->busnum == num) {
+			return mbus;
+		}
+	}
+	return NULL;
+}
+
 static int __init mon_init(void)
 {
 	struct usb_bus *ubus;
-	struct dentry *mondir;
+	int rc;
 
-	mondir = debugfs_create_dir("usbmon", NULL);
-	if (IS_ERR(mondir)) {
-		printk(KERN_NOTICE TAG ": debugfs is not available\n");
-		return -ENODEV;
-	}
-	if (mondir == NULL) {
-		printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
-		return -ENODEV;
-	}
-	mon_dir = mondir;
+	if ((rc = mon_text_init()) != 0)
+		goto err_text;
+	if ((rc = mon_bin_init()) != 0)
+		goto err_bin;
 
 	if (usb_mon_register(&mon_ops_0) != 0) {
 		printk(KERN_NOTICE TAG ": unable to register with the core\n");
-		debugfs_remove(mondir);
-		return -ENODEV;
+		rc = -ENODEV;
+		goto err_reg;
 	}
 	// MOD_INC_USE_COUNT(which_module?);
 
@@ -362,10 +353,17 @@ static int __init mon_init(void)
 
 	mutex_lock(&usb_bus_list_lock);
 	list_for_each_entry (ubus, &usb_bus_list, bus_list) {
-		mon_bus_init(mondir, ubus);
+		mon_bus_init(ubus);
 	}
 	mutex_unlock(&usb_bus_list_lock);
 	return 0;
+
+err_reg:
+	mon_bin_exit();
+err_bin:
+	mon_text_exit();
+err_text:
+	return rc;
 }
 
 static void __exit mon_exit(void)
@@ -382,8 +380,8 @@ static void __exit mon_exit(void)
 		mbus = list_entry(p, struct mon_bus, bus_link);
 		list_del(p);
 
-		debugfs_remove(mbus->dent_t);
-		debugfs_remove(mbus->dent_s);
+		if (mbus->text_inited)
+			mon_text_del(mbus);
 
 		/*
 		 * This never happens, because the open/close paths in
@@ -402,7 +400,8 @@ static void __exit mon_exit(void)
 	}
 	mutex_unlock(&mon_lock);
 
-	debugfs_remove(mon_dir);
+	mon_text_exit();
+	mon_bin_exit();
 }
 
 module_init(mon_init);
diff -uprN -X dontdiff linux-2.6.18/drivers/usb/mon/mon_text.c
linux-2.6.18-ub/drivers/usb/mon/mon_text.c
--- linux-2.6.18/drivers/usb/mon/mon_text.c	2006-09-29 21:27:14.000000000 -0700
+++ linux-2.6.18-ub/drivers/usb/mon/mon_text.c	2006-11-10 20:44:37.000000000 -0800
@@ -9,6 +9,7 @@
 #include <linux/usb.h>
 #include <linux/time.h>
 #include <linux/mutex.h>
+#include <linux/debugfs.h>
 #include <asm/uaccess.h>
 
 #include "usb_mon.h"
@@ -63,6 +64,8 @@ struct mon_reader_text {
 	char slab_name[SLAB_NAME_SZ];
 };
 
+static struct dentry *mon_dir;		/* Usually /sys/kernel/debug/usbmon */
+
 static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
 
 /*
@@ -446,6 +449,47 @@ struct file_operations mon_fops_text = {
 	.release =	mon_text_release,
 };
 
+int mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus)
+{
+	struct dentry *d;
+	enum { NAMESZ = 10 };
+	char name[NAMESZ];
+	int rc;
+
+	rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
+	if (rc <= 0 || rc >= NAMESZ)
+		goto err_print_t;
+	d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_text);
+	if (d == NULL)
+		goto err_create_t;
+	mbus->dent_t = d;
+
+	/* XXX The stats do not belong to here (text API), but oh well... */
+	rc = snprintf(name, NAMESZ, "%ds", ubus->busnum);
+	if (rc <= 0 || rc >= NAMESZ)
+		goto err_print_s;
+	d = debugfs_create_file(name, 0600, mon_dir, mbus, &mon_fops_stat);
+	if (d == NULL)
+		goto err_create_s;
+	mbus->dent_s = d;
+
+	return 1;
+
+err_create_s:
+err_print_s:
+	debugfs_remove(mbus->dent_t);
+	mbus->dent_t = NULL;
+err_create_t:
+err_print_t:
+	return 0;
+}
+
+void mon_text_del(struct mon_bus *mbus)
+{
+	debugfs_remove(mbus->dent_t);
+	debugfs_remove(mbus->dent_s);
+}
+
 /*
  * Slab interface: constructor.
  */
@@ -458,3 +502,24 @@ static void mon_text_ctor(void *mem, kme
 	memset(mem, 0xe5, sizeof(struct mon_event_text));
 }
 
+int __init mon_text_init(void)
+{
+	struct dentry *mondir;
+
+	mondir = debugfs_create_dir("usbmon", NULL);
+	if (IS_ERR(mondir)) {
+		printk(KERN_NOTICE TAG ": debugfs is not available\n");
+		return -ENODEV;
+	}
+	if (mondir == NULL) {
+		printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
+		return -ENODEV;
+	}
+	mon_dir = mondir;
+	return 0;
+}
+
+void __exit mon_text_exit(void)
+{
+	debugfs_remove(mon_dir);
+}
diff -uprN -X dontdiff linux-2.6.18/drivers/usb/mon/usb_mon.h
linux-2.6.18-ub/drivers/usb/mon/usb_mon.h
--- linux-2.6.18/drivers/usb/mon/usb_mon.h	2006-09-29 21:27:14.000000000 -0700
+++ linux-2.6.18-ub/drivers/usb/mon/usb_mon.h	2006-11-14 17:18:13.000000000 -0800
@@ -17,9 +17,11 @@
 struct mon_bus {
 	struct list_head bus_link;
 	spinlock_t lock;
+	struct usb_bus *u_bus;
+
+	int text_inited;
 	struct dentry *dent_s;		/* Debugging file */
 	struct dentry *dent_t;		/* Text interface file */
-	struct usb_bus *u_bus;
 
 	/* Ref */
 	int nreaders;			/* Under mon_lock AND mbus->lock */
@@ -47,13 +49,33 @@ struct mon_reader {
 void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
 void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
 
+struct mon_bus *mon_bus_lookup(unsigned int num);
+
+int /*bool*/ mon_text_add(struct mon_bus *mbus, const struct usb_bus *ubus);
+void mon_text_del(struct mon_bus *mbus);
+// void mon_bin_add(struct mon_bus *);
+
+int __init mon_text_init(void);
+void __exit mon_text_exit(void);
+
 /*
+ * DMA interface.
+ *
+ * XXX The vectored side needs a serious re-thinking. Abstracting vectors,
+ * like in Paolo's original patch, produces a double pkmap. We need an idea.
  */
 extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len);
 
+struct mon_reader_bin;
+extern void mon_dmapeek_vec(const struct mon_reader_bin *rp,
+    unsigned int offset, dma_addr_t dma_addr, unsigned int len);
+extern unsigned int mon_copy_to_buff(const struct mon_reader_bin *rp,
+    unsigned int offset, const unsigned char *from, unsigned int len);
+
+/*
+ */
 extern struct mutex mon_lock;
 
-extern struct file_operations mon_fops_text;
 extern struct file_operations mon_fops_stat;
 
 #endif /* __USB_MON_H */

-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=so...
_______________________________________________
linux-usb-devel@lists.sourceforge.net
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel


Copyright © 2006, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds