Hello,
I would like to share my last work to add a binary interface to usbmon.
This is still quite rough and does use the dreaded next_urb field into
the urb header structure, so it's not intended as a finished work.
--
diff -upN linux-2.6.18-vanilla/drivers/usb/mon/Makefile
linux-2.6.18-mon_mmap/drivers/usb/mon/Makefile
--- linux-2.6.18-vanilla/drivers/usb/mon/Makefile 2006-09-20
05:42:06.000000000 +0200
+++ linux-2.6.18-mon_mmap/drivers/usb/mon/Makefile 2006-10-26
11:30:35.000000000 +0200
@@ -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_dma.o mon_bindev.o
# This does not use CONFIG_USB_MON because we want this to use a tristate.
obj-$(CONFIG_USB) += usbmon.o
diff -upN linux-2.6.18-vanilla/drivers/usb/mon/mon_bindev.c
linux-2.6.18-mon_mmap/drivers/usb/mon/mon_bindev.c
--- linux-2.6.18-vanilla/drivers/usb/mon/mon_bindev.c 1970-01-01
01:00:00.000000000 +0100
+++ linux-2.6.18-mon_mmap/drivers/usb/mon/mon_bindev.c 2006-11-06
11:02:20.000000000 +0100
@@ -0,0 +1,775 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is a binary format reader using a character device with dyanamically
+ * allocated major number
+ */
+
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <linux/time.h>
+#include <linux/mutex.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/cdev.h>
+#include <linux/uio.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+/*
+ * Defined by USB 2.0 clause 9.3, table 9.2.
+ */
+#define SETUP_MAX 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_urb_info)
+#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_RING_MIN_SIZE (16*1024)
+#define MON_RING_DFL_SIZE (64*1024)
+#define MON_RING_MAX_SIZE (1024*1024)
+
+#define MON_CHUNK_SHIFT 12
+#define MON_CHUNK_SIZE (1<<MON_CHUNK_SHIFT)
+#define MON_CHUNK_MASK (MON_CHUNK_SIZE - 1)
+
+#define MON_RING_PTR(rp, of) ((rp)->chunk_vec[(of) >> MON_CHUNK_SHIFT] + ((of)
& MON_CHUNK_MASK))
+#define MON_RING_EMPTY(rp) ((rp)->write_pos == (rp)->rd->read_pos)
+
+struct mon_bin_hdr {
+ u64 id;
+ u8 type; /* submit, complete, etc. */
+ u8 xfer_type;
+ u8 epnum;
+ u8 devnum;
+ u16 busnum;
+ u16 flags; /* setup hdr presence */
+ s64 ts_sec;
+ s32 ts_usec;
+ s32 status;
+ u32 caplen; /* length of data delivered to the application */
+ u32 len; /* the actual length of URB data */
+ u32 next_urb;
+};
+
+struct mon_bin_urb_info {
+ struct mon_bin_hdr hdr;
+ char * data;
+};
+
+/* per file statistic */
+struct mon_bin_stats {
+ u32 queued;
+ u32 dropped;
+};
+
+/* this can be mmap from user space */
+struct mon_bin_ring_user {
+ u32 read_pos;
+};
+
+struct mon_bin_ring {
+ struct mon_bin_ring_user* rd; /* this can be memory mapped to user space*/
+ char** chunk_vec;
+ unsigned chunk_num;
+ unsigned size;
+ unsigned write_pos;
+
+ struct mon_vec urb_data; /* store used for moving around current urb data*/
+};
+
+/* max number of USB bus supported */
+#define MON_BIN_MAX_MINOR 128
+
+#define MON_BIN_SETUP 0x1 /* setup hdr is present*/
+#define MON_BIN_SETUP_ZERO 0x2 /* setup buffer is not available */
+#define MON_BIN_DATA_ZERO 0x4 /* data buffer is not available */
+#define MON_BIN_ERROR 0x8
+#define MON_BIN_NO_MAP 0x10
+
+
+struct mon_bin_dev {
+ struct mon_bus *mbus;
+ struct cdev cdev;
+};
+
+struct mon_reader_bin {
+ int nevents;
+ int cnt_lost;
+ struct mon_reader r; /* In C, parent class can be placed anywhere */
+ struct mon_bin_ring ring;
+
+ wait_queue_head_t wait;
+ int vmas;
+};
+
+static int mon_bin_major = -1;
+static struct mon_bin_dev* mon_bin_devices[MON_BIN_MAX_MINOR];
+
+/*
+ * Initialize data to handle 'vec_len' bytes of the ring buffer starting from
position 'cur_pos'
+ */
+static void mon_bin_vec_init(const struct mon_bin_ring* rp, int cur_pos, int
vec_len,
+ struct mon_vec* data)
+{
+ struct kvec* buf;
+ int i = 0;
+ data->vec = data->store;
+
+ for (buf = data->vec; (vec_len > 0); ++buf, ++i) {
+ unsigned cur_chunk = cur_pos >> MON_CHUNK_SHIFT;
+ unsigned cur_offset = cur_pos & MON_CHUNK_MASK;
+ unsigned cur_len = MON_CHUNK_SIZE - cur_offset;
+ cur_len = (cur_len > vec_len) ? vec_len : cur_len;
+ if (cur_chunk > rp->chunk_num) {
+ printk(KERN_ERR TAG ": consistency error on ring iovec: chunk %d
max %d \n",
+ cur_chunk, rp->chunk_num);
+ data->nsegs = 0;
+ return;
+ }
+
+ buf->iov_base = rp->chunk_vec[cur_chunk] + cur_offset;
+ buf->iov_len = cur_len;
+ cur_pos += cur_len;
+ vec_len -= cur_len;
+ }
+ data->nsegs = i;
+}
+
+/*
+ * Copy len bytes from src to iovec dst. Update vec free segment counter and
+ * free vec pointer
+ */
+static void memcpy_vec(struct mon_vec* dst, const char* src, int len)
+{
+ struct kvec* buf= dst->vec;
+ while ((len>0) && (dst->nsegs>0)) {
+ int clen = (len > buf->iov_len) ? buf->iov_len: len;
+
+ memcpy(buf->iov_base, src, clen);
+ src += clen;
+ buf->iov_base += clen;
+ buf->iov_len -= clen;
+ len -= clen;
+ if (buf->iov_len == 0) {
+ ++buf;
+ --dst->nsegs;
+ }
+ }
+ dst->vec = buf;
+}
+
+/*
+ * Try to allocate an mon_bin_hdr struct plus data_len bytes from circular
buffer
+ * The header can't be splitted on different chunks and the whole data can't
be
+ * wrapped around the circular buffer, so when we are writing near the
+ * end of the circular buffer, we can leave some bytes unused and 'skip' to
+ * the ring start, if more bytes are available there.
+ * The size of the allocated chunk is set into the header fields.
+ */
+static struct mon_bin_hdr * mon_bin_alloc(struct mon_reader_bin *rp, int
data_len)
+{
+ struct mon_bin_hdr * ep;
+ int avail_for_data, urb_end, avail_in_chunk;
+ int end = rp->ring.size;
+
+ /* check if enough free space is available for storing at least the
header*/
+ if (rp->ring.rd->read_pos > rp->ring.write_pos)
+ end = rp->ring.rd->read_pos;
+ avail_for_data = (end - rp->ring.write_pos) - 1 - sizeof(struct
mon_bin_hdr);
+ if (avail_for_data < 0)
+ return 0;
+
+ ep = (struct mon_bin_hdr *) MON_RING_PTR(&rp->ring,rp->ring.write_pos);
+ if (avail_for_data > data_len)
+ ep->caplen = data_len;
+ else
+ ep->caplen = avail_for_data;
+
+ mon_bin_vec_init(&rp->ring, rp->ring.write_pos + sizeof(struct
mon_bin_hdr),
+ ep->caplen, &rp->ring.urb_data);
+
+ /* check if next header will be stored on the same chunk */
+ urb_end = rp->ring.write_pos + ep->caplen + sizeof(struct mon_bin_hdr);
+ avail_in_chunk = MON_CHUNK_SIZE - (urb_end & MON_CHUNK_MASK) - 1;
+ if (avail_in_chunk > sizeof(struct mon_bin_hdr))
+ rp->ring.write_pos = urb_end;
+ else
+ rp->ring.write_pos = ((urb_end & ~MON_CHUNK_MASK) + MON_CHUNK_SIZE) %
rp->ring.size;
+ ep->next_urb = rp->ring.write_pos;
+ return ep;
+}
+
+static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
+ char ev_type)
+{
+ int pipein, data_len, total_len, data_avail, setup_bytes, data_bytes;
+ struct mon_bin_hdr *ep;
+ struct timeval ts;
+ int flags = 0;
+
+ data_len = (ev_type == 'S') ?
+ urb->transfer_buffer_length : urb->actual_length;
+ data_len = (data_len < 0) ? 0 : data_len;
+ total_len = data_len;
+
+ /* check for setup hdr availability */
+ setup_bytes = 0;
+ if (usb_pipecontrol(urb->pipe) && ev_type == 'S') {
+ total_len += SETUP_MAX;
+ if (urb->setup_packet == NULL) {
+ flags |= MON_BIN_SETUP_ZERO;
+ goto check_data;
+ }
+
+ setup_bytes = SETUP_MAX;
+ }
+
+check_data:
+ /* check for data availability */
+ data_bytes = 0;
+ pipein = usb_pipeint(urb->pipe);
+ if ((pipein && (ev_type == 'S')) || (!pipein && (ev_type == 'C')))
+ goto alloc;
+ if (urb->transfer_buffer == NULL) {
+ flags |= MON_BIN_DATA_ZERO;
+ goto alloc;
+ }
+ data_bytes = data_len;
+
+alloc:
+ if ((ep = mon_bin_alloc(rp, setup_bytes + data_bytes)) == NULL) {
+ rp->cnt_lost++;
+ return;
+ }
+
+ /* this is a single writer access, no lock shold be required */
+ ep->type = ev_type;
+ ep->xfer_type = usb_pipetype(urb->pipe);
+ ep->epnum = usb_pipeendpoint(urb->pipe);
+ ep->devnum = usb_pipedevice(urb->pipe);
+ do_gettimeofday(&ts);
+ ep->ts_sec = ts.tv_sec;
+ ep->ts_usec = ts.tv_usec;
+ ep->status = urb->status;
+ ep->len = total_len;
+ ep->status = urb->status;
+ data_avail = ep->caplen;
+
+ /* if dma copy fails we are going to whaste some pre allocate- bytes in the
+ * circular buffer, but this helps to keep things simple */
+ if (setup_bytes) {
+ setup_bytes = setup_bytes > data_avail ? data_avail : setup_bytes;
+ if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP) {
+ if (mon_dmapeek_vec(&rp->ring.urb_data, urb->setup_dma,
setup_bytes) != 0) {
+ flags |= MON_BIN_NO_MAP;
+ setup_bytes = 0;
+ goto copy_data;
+ }
+ }
+ else
+ memcpy_vec(&rp->ring.urb_data, urb->setup_packet, setup_bytes);
+
+ data_avail -= setup_bytes;
+ flags |= MON_BIN_SETUP;
+ }
+
+copy_data:
+ if (data_bytes) {
+ data_bytes = data_bytes > data_avail ? data_avail : data_bytes;
+ if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP) {
+ if (mon_dmapeek_vec(&rp->ring.urb_data, urb->transfer_dma,
data_bytes) != 0) {
+ data_bytes = 0;
+ goto out;
+ }
+ }
+ else
+ memcpy_vec(&rp->ring.urb_data, urb->transfer_buffer, data_bytes);
+ }
+
+out:
+ ep->caplen = data_bytes+setup_bytes;
+ ep->flags = flags;
+ rp->nevents++;
+ wake_up(&rp->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)
+{
+ struct timeval ts;
+ struct mon_bin_hdr* ep;
+ struct mon_reader_bin *rp = data;
+
+ /* we can be called from hardware IRQs */
+ if ((ep = mon_bin_alloc(rp, 0)) == NULL) {
+ rp->cnt_lost++;
+ return;
+ }
+
+ /* this is a single writer access, no lock shold be required */
+ ep->type = 'E';
+ ep->xfer_type = usb_pipetype(urb->pipe);
+ ep->epnum = usb_pipeendpoint(urb->pipe);
+ ep->devnum = usb_pipedevice(urb->pipe);
+ do_gettimeofday(&ts);
+ ep->ts_sec = ts.tv_sec;
+ ep->ts_usec = ts.tv_usec;
+ ep->len = 0;
+ ep->caplen = 0;
+ ep->status = error;
+ ep->flags = MON_BIN_ERROR;
+
+ rp->nevents++;
+ wake_up(&rp->wait);
+}
+
+/*
+ * Fetch next event from the circular buffer.
+ */
+struct mon_bin_hdr *mon_bin_fetch(struct mon_reader_bin *rp,
+ struct mon_bus *mbus)
+{
+ unsigned long flags;
+ struct mon_bin_hdr * ep = 0;
+
+ spin_lock_irqsave(&mbus->lock, flags);
+ if (MON_RING_EMPTY(&rp->ring))
+ goto out;
+
+ /* iovec must be initialized under the lock */
+ ep = (struct mon_bin_hdr *) MON_RING_PTR(&rp->ring,rp->ring.rd->read_pos);
+ mon_bin_vec_init(&rp->ring, rp->ring.rd->read_pos + sizeof(struct
mon_bin_hdr),
+ ep->caplen, &rp->ring.urb_data);
+ rp->ring.rd->read_pos = ep->next_urb;
+ --rp->nevents;
+
+out:
+ spin_unlock_irqrestore(&mbus->lock, flags);
+ return ep;
+}
+
+/*
+ * Destroy ring buffer reader. Required lock is done out of here
+ */
+static void mon_bin_destroy_ring(struct mon_bin_ring* rp)
+{
+ int i;
+ if (rp->rd)
+ free_page((unsigned long)rp->rd);
+ if (rp->chunk_vec) {
+ for (i=0; i<rp->chunk_num; ++i)
+ if (rp->chunk_vec[i])
+ kfree(rp->chunk_vec[i]);
+ kfree(rp->chunk_vec);
+ }
+ if (rp->urb_data.store)
+ kfree(rp->urb_data.store);
+}
+
+/*
+ * Allocate and initialize as empty ring of specified size
+ */
+static int mon_bin_create_ring(struct mon_bin_ring* rp, int size)
+{
+ int i;
+
+ memset(rp, 0, sizeof(*rp));
+ size = (size & (~MON_CHUNK_MASK)) + MON_CHUNK_SIZE;
+ if (size < MON_RING_MIN_SIZE || size > MON_RING_MAX_SIZE)
+ return -EINVAL;
+
+ rp->size = size;
+ rp->chunk_num = size >> MON_CHUNK_SHIFT;
+ if ((rp->chunk_vec = kzalloc(sizeof(char*)*rp->chunk_num, GFP_KERNEL)) ==
NULL)
+ return -ENOMEM;
+
+ if ((rp->urb_data.store = kmalloc(sizeof(struct kvec)*rp->chunk_num,
+ GFP_KERNEL)) == NULL) {
+ mon_bin_destroy_ring(rp);
+ return -ENOMEM;
+ }
+
+ if ((rp->rd = (struct mon_bin_ring_user *)__get_free_page(GFP_KERNEL)) ==
NULL) {
+ mon_bin_destroy_ring(rp);
+ return -ENOMEM;
+ }
+
+ for (i=0; i<rp->chunk_num; ++i)
+ if ((rp->chunk_vec[i] = kmalloc(MON_CHUNK_SIZE, GFP_KERNEL)) == NULL) {
+ mon_bin_destroy_ring(rp);
+ return -ENOMEM;
+ }
+ rp->rd->read_pos = 0;
+ rp->write_pos = 0;
+ return 0;
+}
+
+/*
+ * Create a new mon reader
+ */
+static int mon_bin_open(struct inode *inode, struct file *file)
+{
+ struct mon_bus *mbus;
+ struct mon_reader_bin *rp;
+ int rc;
+
+ struct usb_bus *ubus;
+ struct mon_bin_dev* dev = container_of(inode->i_cdev,
+ struct mon_bin_dev, cdev);
+
+ mutex_lock(&mon_lock);
+ mbus = dev->mbus;
+ ubus = mbus->u_bus;
+
+ rp = kzalloc(sizeof(struct mon_reader_bin), GFP_KERNEL);
+ if (rp == NULL) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+
+ init_waitqueue_head(&rp->wait);
+
+ 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;
+
+ if ((rc = mon_bin_create_ring(&rp->ring, MON_RING_DFL_SIZE)) != 0)
+ goto err_alloc_pr;
+
+ file->private_data = rp;
+ mon_reader_add(mbus, &rp->r);
+
+ mutex_unlock(&mon_lock);
+ return 0;
+
+err_alloc_pr:
+ kfree(rp);
+err_alloc:
+ mutex_unlock(&mon_lock);
+ return rc;
+}
+
+/*
+ * Extract an event from list and copy it to user space, may block
+ */
+static int mon_bin_get_event(struct file *file, struct mon_reader_bin *rp,
+ struct mon_bin_hdr __user *hdr, char __user *data, size_t nbytes)
+{
+ DECLARE_WAITQUEUE(waita, current);
+ struct mon_bin_hdr *ep;
+ int i;
+ int ret = 0;
+ struct mon_bus *mbus = rp->r.m_bus;
+
+ add_wait_queue(&rp->wait, &waita);
+ set_current_state(TASK_INTERRUPTIBLE);
+ while ((ep = mon_bin_fetch(rp, mbus)) == NULL) {
+ if (file->f_flags & O_NONBLOCK) {
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&rp->wait, &waita);
+ return -EWOULDBLOCK; /* Same as EAGAIN in Linux */
+ }
+ /*
+ * We do not count nwaiters, because ->release is supposed
+ * to be called when all openers are gone only.
+ */
+ schedule();
+ if (signal_pending(current)) {
+ remove_wait_queue(&rp->wait, &waita);
+ return -EINTR;
+ }
+ set_current_state(TASK_INTERRUPTIBLE);
+ }
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&rp->wait, &waita);
+
+ /* check out how much URB data is present in this buffer*/
+ ep->caplen = (nbytes > ep->caplen) ? ep->caplen : nbytes;
+ if (copy_to_user(hdr, ep, sizeof(struct mon_bin_hdr))) {
+ ret = -EFAULT;
+ goto out;
+ }
+
+ for (i=0; i < rp->ring.urb_data.nsegs; ++i) {
+ struct kvec* buf = &rp->ring.urb_data.vec[i];
+ if (copy_to_user(data, buf->iov_base, buf->iov_len)) {
+ ret = -EFAULT;
+ goto out;
+ }
+ data += buf->iov_len;
+ }
+
+out:
+ return ret;
+}
+
+static int mon_bin_release(struct inode *inode, struct file *file)
+{
+ struct mon_bus *mbus;
+ struct mon_reader_bin *rp = file->private_data;
+
+ mutex_lock(&mon_lock);
+ mbus = rp->r.m_bus;
+
+ 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_bin_destroy_ring(&rp->ring);
+ kfree(rp);
+
+ mutex_unlock(&mon_lock);
+ return 0;
+}
+
+static int mon_bin_read(struct file *file, char __user *buf,
+ size_t nbytes, loff_t *ppos)
+{
+ if (nbytes < sizeof(struct mon_bin_hdr))
+ return -EFAULT;
+ return mon_bin_get_event(file, (struct mon_reader_bin *)
file->private_data,
+ (struct mon_bin_hdr*) buf, buf+sizeof(struct mon_bin_hdr),
+ nbytes - sizeof(struct mon_bin_hdr));
+}
+
+static int mon_bin_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ unsigned long flags;
+ struct mon_bin_urb_info info;
+ struct mon_reader_bin * rp;
+ struct mon_bin_stats* sp;
+ struct mon_bus* mbus;
+ struct mon_bin_ring ring;
+ int ret = 0;
+
+ /* basic sanity check */
+ if (!(rp = file->private_data))
+ return -ENODEV;
+ if (_IOC_TYPE(cmd) != MON_IOC_MAGIC)
+ return -ENOTTY;
+
+ rp = file->private_data;
+ mbus = rp->r.m_bus;
+
+ switch (cmd) {
+ case MON_IOCQ_URB_LEN:
+ spin_lock_irqsave(&mbus->lock, flags);
+ if (!MON_RING_EMPTY(&rp->ring)) {
+ struct mon_bin_hdr *ep = (struct mon_bin_hdr*)
MON_RING_PTR(&rp->ring, rp->ring.rd->read_pos);
+ ret = ep->caplen;
+ }
+ spin_unlock_irqrestore(&mbus->lock, flags);
+ break;
+
+ case MON_IOCQ_RING_SIZE:
+ ret = rp->ring.size;
+ break;
+
+ 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;
+
+
+ case MON_IOCX_URB:
+ 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.caplen);
+ break;
+
+ case MON_IOCG_STATS:
+ 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:
+ ret = -ENOTTY;
+ break;
+ }
+
+ return ret;
+}
+
+static unsigned int mon_bin_poll(struct file *filp, poll_table *wait)
+{
+ unsigned long flags;
+ struct mon_reader_bin *rp = filp->private_data;
+ struct mon_bus* mbus = rp->r.m_bus;
+ unsigned int mask = 0;
+
+ spin_lock_irqsave(&mbus->lock, flags);
+ poll_wait(filp, &rp->wait, wait);
+ if (!MON_RING_EMPTY(&rp->ring))
+ mask |= POLLIN | POLLRDNORM; /* readable */
+ spin_unlock_irqrestore(&mbus->lock, flags);
+ return mask;
+}
+
+/*
+ * 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;
+
+}
+
+struct file_operations mon_fops_binary = {
+ .owner = THIS_MODULE,
+ .open = mon_bin_open,
+ .llseek = no_llseek,
+ .read = mon_bin_read,
+ .poll = mon_bin_poll,
+ .ioctl = mon_bin_ioctl,
+ .mmap = mon_bin_mmap,
+ .release = mon_bin_release,
+};
+
+#include "../core/hcd.h"
+
+int mon_bin_add(unsigned minor, struct mon_bus * mbus)
+{
+ struct mon_bin_dev* dev;
+ int devno = MKDEV(mon_bin_major, minor);
+ if ((minor >= MON_BIN_MAX_MINOR) || mon_bin_devices[minor])
+ return -EFAULT;
+
+ dev = kmalloc(sizeof(struct mon_bin_dev), GFP_KERNEL);
+ if (!dev)
+ return -ENOMEM;
+ cdev_init(&dev->cdev, &mon_fops_binary);
+ dev->cdev.owner = THIS_MODULE;
+ dev->cdev.ops = &mon_fops_binary;
+ dev->mbus = mbus;
+ return cdev_add (&dev->cdev, devno, 1);
+}
+
+void mon_bin_del(unsigned minor)
+{
+ if ((minor >= MON_BIN_MAX_MINOR) || !mon_bin_devices[minor])
+ return;
+
+ cdev_del(&mon_bin_devices[minor]->cdev);
+ kfree(mon_bin_devices[minor]);
+ mon_bin_devices[minor] = 0;
+}
+
+int mon_bin_init(void)
+{
+ dev_t dev;
+ int err;
+
+ if ((err = alloc_chrdev_region(&dev, 0, MON_BIN_MAX_MINOR,
+ "usb_mon_bin")) < 0)
+ return err;
+
+ memset(mon_bin_devices, 0, sizeof(mon_bin_devices));
+
+ mon_bin_major = MAJOR(dev);
+ return err;
+}
+
+void mon_bin_exit(void)
+{
+ unsigned i;
+ dev_t devno = MKDEV(mon_bin_major, 0);
+
+ for (i=0; i<MON_BIN_MAX_MINOR; ++i)
+ mon_bin_del(i);
+
+ /* free allocated main memory */
+ unregister_chrdev_region(devno, MON_BIN_MAX_MINOR);
+}
diff -upN linux-2.6.18-vanilla/drivers/usb/mon/mon_dma.c
linux-2.6.18-mon_mmap/drivers/usb/mon/mon_dma.c
--- linux-2.6.18-vanilla/drivers/usb/mon/mon_dma.c 2006-09-20
05:42:06.000000000 +0200
+++ linux-2.6.18-mon_mmap/drivers/usb/mon/mon_dma.c 2006-11-06
11:02:35.000000000 +0100
@@ -9,6 +9,8 @@
#include <linux/list.h>
#include <linux/highmem.h>
#include <asm/page.h>
+#include <asm/uaccess.h>
+#include <linux/uio.h>
#include <linux/usb.h> /* Only needed for declarations in usb_mon.h */
#include "usb_mon.h"
@@ -48,6 +50,56 @@ char mon_dmapeek(unsigned char *dst, dma
local_irq_restore(flags);
return 0;
}
+
+char mon_dmapeek_vec(struct mon_vec* dst, dma_addr_t dma_addr, int len)
+{
+ struct page *pg;
+ unsigned long flags;
+ unsigned char *map;
+ unsigned char *ptr;
+
+ struct kvec* buf = dst->vec;
+
+ /*
+ * We are called from hardware IRQs in case of callbacks.
+ * But we can be called from softirq or process context in case
+ * of submissions. In such case, we need to protect KM_IRQ0.
+ */
+ local_irq_save(flags);
+ while ((len > 0) && (dst->nsegs > 0)) {
+ /* compute number of bytes we are going to copy into this segment*/
+ int offset = dma_addr & (PAGE_SIZE-1);
+ int page_len = PAGE_SIZE - offset;
+ int copied = (len > page_len) ? page_len : len;
+ copied = (copied > buf->iov_len) ? buf->iov_len : copied;
+
+ /*
+ * On i386, a DMA handle is the "physical" address of a page.
+ * In other words, the bus address is equal to physical address.
+ * There is no IOMMU.
+ */
+ pg = phys_to_page(dma_addr);
+ map = kmap_atomic(pg, KM_IRQ0);
+ ptr = map + offset;
+ memcpy(buf->iov_base, ptr, copied);
+ kunmap_atomic(map, KM_IRQ0);
+
+ offset = 0;
+ dma_addr += copied;
+ len -= copied;
+ buf->iov_len -= copied;
+ buf->iov_base += copied;
+ if (buf->iov_len == 0) {
+ ++buf;
+ --dst->nsegs;
+ }
+ } while (len > 0);
+
+ dst->vec = buf;
+ local_irq_restore(flags);
+ return 0;
+}
+
#endif /* __i386__ */
#ifndef MON_HAS_UNMAP
@@ -55,4 +107,9 @@ char mon_dmapeek(unsigned char *dst, dma
{
return 'D';
}
+
+char mon_dmapeek_vec(struct mon_vec* dst, dma_addr_t dma_addr, int nsegs)
+{
+ return 'D';
+}
#endif
diff -upN linux-2.6.18-vanilla/drivers/usb/mon/mon_main.c
linux-2.6.18-mon_mmap/drivers/usb/mon/mon_main.c
--- linux-2.6.18-vanilla/drivers/usb/mon/mon_main.c 2006-09-20
05:42:06.000000000 +0200
+++ linux-2.6.18-mon_mmap/drivers/usb/mon/mon_main.c 2006-11-06
11:03:25.000000000 +0100
@@ -214,6 +214,7 @@ static void mon_bus_remove(struct usb_bu
list_del(&mbus->bus_link);
debugfs_remove(mbus->dent_t);
debugfs_remove(mbus->dent_s);
+ mon_bin_del(ubus->busnum);
mon_dissolve(mbus, ubus);
kref_put(&mbus->ref, mon_bus_drop);
@@ -319,12 +320,17 @@ static void mon_bus_init(struct dentry *
if (d == NULL)
goto err_create_s;
mbus->dent_s = d;
+
+ if (mon_bin_add(ubus->busnum, mbus) < 0)
+ goto err_create_b;
mutex_lock(&mon_lock);
list_add_tail(&mbus->bus_link, &mon_buses);
mutex_unlock(&mon_lock);
return;
+err_create_b:
+ debugfs_remove(mbus->dent_s);
err_create_s:
err_print_s:
debugfs_remove(mbus->dent_t);
@@ -358,6 +364,7 @@ static int __init mon_init(void)
}
// MOD_INC_USE_COUNT(which_module?);
+ mon_bin_init();
usb_register_notify(&mon_nb);
mutex_lock(&usb_bus_list_lock);
@@ -402,6 +409,7 @@ static void __exit mon_exit(void)
}
mutex_unlock(&mon_lock);
+ mon_bin_exit();
debugfs_remove(mon_dir);
}
diff -upN linux-2.6.18-vanilla/drivers/usb/mon/mon_text.c
linux-2.6.18-mon_mmap/drivers/usb/mon/mon_text.c
--- linux-2.6.18-vanilla/drivers/usb/mon/mon_text.c 2006-09-20
05:42:06.000000000 +0200
+++ linux-2.6.18-mon_mmap/drivers/usb/mon/mon_text.c 2006-10-26
11:30:35.000000000 +0200
@@ -457,4 +457,3 @@ static void mon_text_ctor(void *mem, kme
*/
memset(mem, 0xe5, sizeof(struct mon_event_text));
}
-
diff -upN linux-2.6.18-vanilla/drivers/usb/mon/usb_mon.h
linux-2.6.18-mon_mmap/drivers/usb/mon/usb_mon.h
--- linux-2.6.18-vanilla/drivers/usb/mon/usb_mon.h 2006-09-20
05:42:06.000000000 +0200
+++ linux-2.6.18-mon_mmap/drivers/usb/mon/usb_mon.h 2006-11-03
09:07:34.000000000 +0100
@@ -44,12 +44,24 @@ struct mon_reader {
void (*rnf_complete)(void *data, struct urb *urb);
};
+struct mon_vec {
+ struct kvec* store;
+ struct kvec* vec; /* ptr to first free item in store*/
+ unsigned nsegs; /* number of free items in store*/
+};
+
void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
+
+int mon_bin_init(void);
+void mon_bin_exit(void);
+int mon_bin_add(unsigned minor, struct mon_bus * mbus);
+void mon_bin_del(unsigned minor);
/*
*/
extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len);
+extern char mon_dmapeek_vec(struct mon_vec* dst, dma_addr_t dma_addr, int len);
extern struct mutex mon_lock;
-------------------------------------------------------------------------
Using Tomcat but need to do more? Need to support web services, security?
Get stuff done quickly with pre-integrated technology to make your job easier
Download IBM WebSphere Application Server v.1.0.1 based on Apache Geronimo
http://sel.as-us.falkag.net/sel?cmd=lnk&kid=120709&bid=263057&dat=121642
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel