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 _______________________________________________ linux-usb-devel@lists.sourceforge.net To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel