Hi, Guys:

This is usbmon which I cooked up because I got tired from adding dbg()'s
and polluting my dmesg. I use it to hunt bugs in USB storage devices so
far, and it's useful, although limited at this stage.

I looked at the Harding's USBmon patch, and I think he got a few things right.
The main of them is that I underestimated the benefits of placing the special
files into the filesystem namespace. When we discussed it with Greg in the
airport, we decided that having some sort of Netlink-style socket would be
the best option. I decided to make a u-turn and attach those sockets into
the namespace (currently under /dbg, but it can change). What this buys us is:

 1. cat(1): never bet against it. It's too handy. And netcat is just
    not the same.
 2. USBmon userland in Java. Just try to hack in JNI a little as I have
    and you'll see.

He also got some parts wrong. They are small things, but unfortunately,
pervasive. For example, he relies on urb->dev, which is not a good idea
in case of HCD which zero it far away from the completion call site, such
as usb-ohci in 2.4. And it's error-prone and a maintenance problem to
audit all HCDs and add usbmon calls. Races by design, too. Small things
like that, but many. Eventually, I wrote everything from scratch. It's
rather embarrassing that I could not save USBmon and gave in to NIH.

Since it's a big NIH, usbmon is not compatible with USBmon's userland.
It can be made compatible, but it needs a small adaptation layer,
because Harding aggregated at a device, and I do it on a bus
(I can explain why, but it's rather long; it has to do with hotplug
and races).

The architecture to support various output formats is present. Obvious
candidates are Old USBmon format and a Binary format. But it's not done.

Please ask if something is not obvious in the code.

Bye,
-- Pete

diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/Kconfig 
linux-2.6.10-rc3-usb/drivers/usb/Kconfig
--- linux-2.6.10-rc3/drivers/usb/Kconfig        2004-12-15 02:01:05.000000000 
-0800
+++ linux-2.6.10-rc3-usb/drivers/usb/Kconfig    2004-12-15 13:36:33.000000000 
-0800
@@ -56,6 +56,8 @@ source "drivers/usb/media/Kconfig"
 
 source "drivers/usb/net/Kconfig"
 
+source "drivers/usb/mon/Kconfig"
+
 comment "USB port drivers"
        depends on USB
 
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/Makefile 
linux-2.6.10-rc3-usb/drivers/usb/Makefile
--- linux-2.6.10-rc3/drivers/usb/Makefile       2004-12-15 02:01:05.000000000 
-0800
+++ linux-2.6.10-rc3-usb/drivers/usb/Makefile   2004-12-15 13:49:26.000000000 
-0800
@@ -6,6 +6,8 @@
 
 obj-$(CONFIG_USB)              += core/
 
+obj-$(CONFIG_USB_MON)          += mon/
+
 obj-$(CONFIG_USB_EHCI_HCD)     += host/
 obj-$(CONFIG_USB_OHCI_HCD)     += host/
 obj-$(CONFIG_USB_UHCI_HCD)     += host/
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/Kconfig 
linux-2.6.10-rc3-usb/drivers/usb/mon/Kconfig
--- linux-2.6.10-rc3/drivers/usb/mon/Kconfig    1969-12-31 16:00:00.000000000 
-0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/Kconfig        2004-12-15 
19:56:54.000000000 -0800
@@ -0,0 +1,21 @@
+#
+# USB Monitor configuration
+#
+
+# In normal life, it makes little sense to have usbmon as a module, and in fact
+# it is harmful, because there is no way to autoload the module.
+# The 'm' option is allowed for hackers who debug the usbmon itself,
+# and for those who have usbcore as a module.
+config USB_MON
+       tristate "USB Monitor"
+       depends on USB && SYSFS
+       default y
+       help
+         If you say Y here, a component which captures the USB traffic
+         between peripheral-specific drivers and HC drivers will be built.
+         The USB_MON is similar in spirit and may be compatible with Dave
+         Harding's USBMon.
+
+         This is somewhat experimental at this time, but it should be safe.
+
+         If unsure, say Y.
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/Makefile 
linux-2.6.10-rc3-usb/drivers/usb/mon/Makefile
--- linux-2.6.10-rc3/drivers/usb/mon/Makefile   1969-12-31 16:00:00.000000000 
-0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/Makefile       2004-12-17 
18:33:05.000000000 -0800
@@ -0,0 +1,7 @@
+#
+# Makefile for USB Core files and filesystem
+#
+
+usbmon-objs    := mon_main.o mon_stat.o mon_text.o
+
+obj-$(CONFIG_USB_MON)  += usbmon.o
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/mon_main.c 
linux-2.6.10-rc3-usb/drivers/usb/mon/mon_main.c
--- linux-2.6.10-rc3/drivers/usb/mon/mon_main.c 1969-12-31 16:00:00.000000000 
-0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/mon_main.c     2004-12-19 
21:04:13.852433525 -0800
@@ -0,0 +1,306 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * mon_main.c: Main file, module initiation and exit, registrations, etc.
+ *
+ * - If an URB was submitted before a reader opened a pipe, it's not tracked.
+ *   The only solution is to reject intercept-on-open and do intercept-always.
+ * - Hotplugging HCs will cause usbmon to crash on unload
+ * - Type files as fifos, sockets, or character specials? /proc/kmsg is 
regular.
+ * - Awaken rmmod when outstanding URBs and opens are gone.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/debugfs.h>
+#include <linux/smp_lock.h>
+
+#include "usb_mon.h"
+
+/* XXX Currently in drivers/usb/core/hcd.h */
+extern struct list_head usb_bus_list;
+extern struct semaphore usb_bus_list_lock;
+
+static int mon_submit(struct urb *urb, int mem_flags);
+static void mon_complete(struct urb *urb, struct pt_regs *regs);
+static void mon_cleanup(struct mon_bus *mbus);
+
+DECLARE_MUTEX(mon_lock);
+
+static struct dentry *mon_dir;         /* /dbg/usbmon */
+static LIST_HEAD(mon_buses);           /* All buses we know: struct mon_bus */
+
+/*
+ * Link a reader into the bus.
+ */
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r)
+{
+       unsigned long flags;
+       struct usb_bus *ubus;
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       if (mbus->nreaders == 0 && mbus->nurbs == 0) {
+               ubus = mbus->u_bus;
+               if (ubus->monitored) {
+                       /*
+                        * Something is really broken, refuse to go on and
+                        * possibly corrupt ops pointers or worse.
+                        */
+                       printk(KERN_ERR TAG ": bus %d is already monitored\n",
+                           ubus->busnum);
+                       spin_unlock_irqrestore(&mbus->lock, flags);
+                       return;
+               }
+               memcpy(&mbus->shim_ops, ubus->op, sizeof(struct 
usb_operations));
+               mbus->shim_ops.submit_urb = mon_submit;
+               mbus->saved_op = ubus->op;
+               ubus->op = &mbus->shim_ops;
+               ubus->monitored = 1;
+       }
+       mbus->nreaders++;
+
+       list_add_tail(&r->r_link, &mbus->r_list);
+       spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+/*
+ * Unlink reader from the bus.
+ */
+void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       list_del(&r->r_link);
+       --mbus->nreaders;
+       if (mbus->nreaders == 0 && mbus->nurbs == 0)
+               mon_cleanup(mbus);
+       spin_unlock_irqrestore(&mbus->lock, flags);
+}
+
+/*
+ */
+static int mon_submit(struct urb *urb, int mem_flags)
+{
+       struct mon_bus *mbus;
+       unsigned long flags;
+       struct list_head *pos;
+       struct mon_reader *r;
+
+       mbus = urb->dev->bus->mon_bus;
+       if (mbus == NULL)
+               goto out_unlocked;
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       if (mbus->nreaders == 0)
+               goto out_locked;
+
+       urb->mon_bus = mbus;
+       urb->mon_saved_complete = urb->complete;
+       urb->complete = mon_complete;
+       mbus->nurbs++;
+
+       list_for_each (pos, &mbus->r_list) {
+               r = list_entry(pos, struct mon_reader, r_link);
+               r->rnf_submit(r->r_data, urb);
+       }
+out_locked:
+       spin_unlock_irqrestore(&mbus->lock, flags);
+out_unlocked:
+       return mbus->saved_op->submit_urb(urb, mem_flags);
+       /* Woops, we forgot to deliver success or failure to the reader */
+}
+
+/*
+ * This is called by an HC driver directly.
+ */
+static void mon_complete(struct urb *urb, struct pt_regs *regs)
+{
+       struct mon_bus *mbus;
+       unsigned long flags;
+       struct list_head *pos;
+       struct mon_reader *r;
+
+       mbus = urb->mon_bus;
+       if (mbus == NULL) {
+               /*
+                * This should not happen.
+                * At this point we do not even know the bus number...
+                */
+               printk(KERN_ERR TAG ": Null mon bus in URB, pipe 0x%x\n",
+                   urb->pipe);
+               return;
+       }
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       list_for_each (pos, &mbus->r_list) {
+               r = list_entry(pos, struct mon_reader, r_link);
+               r->rnf_complete(r->r_data, urb);
+       }
+
+       /*
+        * Drop the URB from tracking.
+        * XXX ISOs and Interrupt get lost after the first complete this way.
+        */
+       urb->complete = urb->mon_saved_complete;
+       urb->mon_bus = NULL;
+       --mbus->nurbs;
+       if (mbus->nurbs == 0 && mbus->nreaders == 0)
+               mon_cleanup(mbus);
+       spin_unlock_irqrestore(&mbus->lock, flags);
+
+       (*urb->complete)(urb, regs);
+}
+
+/* int (*unlink_urb) (struct urb *urb, int status); */
+
+/*
+ * Stop monitoring.
+ * Obviously this must be well locked, so no need to play with mb's.
+ */
+static void mon_cleanup(struct mon_bus *mbus)
+{
+       struct usb_bus *ubus = mbus->u_bus;
+
+       ubus->op = mbus->saved_op;
+       ubus->monitored = 0;
+       mbus->saved_op = NULL;
+}
+
+/*
+ * Initialize a bus for us:
+ *  - allocate mon_bus
+ *  - refcount USB bus struct
+ *  - link
+ */
+static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
+{
+       struct dentry *d;
+       struct mon_bus *mbus;
+       enum { NAMESZ = 10 };
+       char name[NAMESZ];
+       int rc;
+
+       if ((mbus = kmalloc(sizeof(struct mon_bus), GFP_KERNEL)) == NULL)
+               goto err_alloc;
+       memset(mbus, 0, sizeof(struct mon_bus));
+       spin_lock_init(&mbus->lock);
+       INIT_LIST_HEAD(&mbus->r_list);
+
+       /* XXX Is this how I pin struct bus? Ask linux-usb-devel */
+       kobject_get(&ubus->class_dev.kobj);
+       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;
+
+       down(&mon_lock);
+       list_add_tail(&mbus->bus_link, &mon_buses);
+       up(&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;
+}
+
+static int __init mon_init(void)
+{
+       struct usb_bus *ubus;
+       struct dentry *mondir;
+
+       mondir = debugfs_create_dir("usbmon", NULL);
+       if (IS_ERR(mondir)) {
+               printk(KERN_NOTICE TAG ": debugs is not available\n");
+               return -ENODEV;
+       }
+       if (mondir == NULL) {
+               printk(KERN_NOTICE TAG ": unable to create usbmon directory\n");
+               return -ENODEV;
+       }
+       mon_dir = mondir;
+
+       down(&usb_bus_list_lock);
+       list_for_each_entry (ubus, &usb_bus_list, bus_list) {
+               mon_bus_init(mondir, ubus);
+       }
+       up(&usb_bus_list_lock);
+       return 0;
+}
+
+static void __exit mon_exit(void)
+{
+       struct mon_bus *mbus;
+       struct usb_bus *ubus;
+       struct list_head *p;
+
+       down(&usb_bus_list_lock);       /* Needed for kobject_put's ? */
+       down(&mon_lock);
+       while (!list_empty(&mon_buses)) {
+               p = mon_buses.next;
+               mbus = list_entry(p, struct mon_bus, bus_link);
+               list_del(p);
+
+               if (mbus->nurbs) {
+                       printk(KERN_ERR TAG
+                           ": Stuck URBs (%d) on usb%d, hanging...\n",
+                           mbus->nurbs, mbus->u_bus->busnum);
+                       up(&mon_lock);
+                       up(&usb_bus_list_lock);
+                       for (;;) {
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               schedule();
+                       }
+               }
+               if (mbus->nreaders) {
+                       printk(KERN_ERR TAG
+                           ": Outstanding opens (%d) on usb%d, hanging...\n",
+                           mbus->nreaders, mbus->u_bus->busnum);
+                       up(&mon_lock);
+                       up(&usb_bus_list_lock);
+                       for (;;) {
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               schedule();
+                       }
+               }
+
+               ubus = mbus->u_bus;
+               ubus->mon_bus = NULL;
+               mb();
+
+               /* XXX This also should be done in hot-remove path */
+               debugfs_remove(mbus->dent_t);
+               debugfs_remove(mbus->dent_s);
+               kobject_put(&ubus->class_dev.kobj);
+               kfree(mbus);
+       }
+       up(&mon_lock);
+       up(&usb_bus_list_lock);
+
+       debugfs_remove(mon_dir);
+}
+
+module_init(mon_init);
+module_exit(mon_exit);
+
+MODULE_LICENSE("GPL");
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/mon_stat.c 
linux-2.6.10-rc3-usb/drivers/usb/mon/mon_stat.c
--- linux-2.6.10-rc3/drivers/usb/mon/mon_stat.c 1969-12-31 16:00:00.000000000 
-0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/mon_stat.c     2004-12-19 
14:06:03.306311947 -0800
@@ -0,0 +1,75 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is the 's' or 'stat' reader which debugs usbmon itself.
+ * Note that this code blows through locks, so make sure that
+ * /dbg/usbmon/0s is well protected from non-root users.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+#define STAT_BUF_SIZE  80
+
+struct snap {
+       int slen;
+       char str[STAT_BUF_SIZE];
+};
+
+static int mon_stat_open(struct inode *inode, struct file *file)
+{
+       struct mon_bus *mbus;
+       struct snap *sp;
+
+       if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL)
+               return -ENOMEM;
+
+       mbus = inode->u.generic_ip;
+
+       sp->slen = snprintf(sp->str, STAT_BUF_SIZE,
+           "nreaders %d saved_op 0x%lx nurbs %d text_lost %u\n",
+           mbus->nreaders, (long) mbus->saved_op, mbus->nurbs,
+           mbus->cnt_text_lost);
+
+       file->private_data = sp;
+       return 0;
+}
+
+static ssize_t mon_stat_read(struct file *file, char __user *buf,
+                               size_t nbytes, loff_t *ppos)
+{
+       struct snap *sp = file->private_data;
+       loff_t pos = *ppos;
+       int cnt;
+
+       if (pos < 0 || pos >= sp->slen)
+               return 0;
+       if (nbytes == 0)
+               return 0;
+       if ((cnt = sp->slen - pos) > nbytes)
+               cnt = nbytes;
+       if (copy_to_user(buf, sp->str + pos, cnt))
+               return -EFAULT;
+       *ppos = pos + cnt;
+       return cnt;
+}
+
+static int mon_stat_release(struct inode *inode, struct file *file)
+{
+       return 0;
+}
+
+struct file_operations mon_fops_stat = {
+       .open =         mon_stat_open,
+       .llseek =       no_llseek,
+       .read =         mon_stat_read,
+       /* .write =     mon_stat_write, */
+       /* .poll =              mon_stat_poll, */
+       /* .ioctl =     mon_stat_ioctl, */
+       .release =      mon_stat_release,
+};
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/mon_text.c 
linux-2.6.10-rc3-usb/drivers/usb/mon/mon_text.c
--- linux-2.6.10-rc3/drivers/usb/mon/mon_text.c 1969-12-31 16:00:00.000000000 
-0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/mon_text.c     2004-12-19 
21:14:55.726702896 -0800
@@ -0,0 +1,295 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ *
+ * This is a text format reader.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#include "usb_mon.h"
+
+/*
+ * The limit exists to prevent OOMs when the user process stops reading.
+ */
+#define EVENT_MAX  100
+
+#define PRINTF_DFL  100
+
+struct mon_event_text {
+       struct list_head e_link;
+       int type;               /* submit, complete, etc. */
+       unsigned int pipe;      /* Pipe */
+       unsigned long id;       /* From pointer, most of the time */
+       /* To add: timestamp */
+       int length;             /* Depends on type: xfer length or act length */
+       int status;
+};
+
+#define SLAB_NAME_SZ  30
+struct mon_reader_text {
+       kmem_cache_t *e_slab;
+       int nevents;
+       struct list_head e_list;
+       struct mon_reader r;    /* In C, parent class can be placed anywhere */
+
+       wait_queue_head_t wait;
+       int printf_size;
+       char *printf_buf;
+       struct semaphore printf_lock;
+
+       char slab_name[SLAB_NAME_SZ];
+};
+
+static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
+static void mon_text_dtor(void *, kmem_cache_t *, unsigned long);
+
+/*
+ * May be called from an interrupt.
+ *
+ * This is called with the whole mon_bus locked, so no additional lock.
+ */
+static void mon_text_submit(void *data, struct urb *urb)
+{
+       struct mon_reader_text *rp = data;
+       struct mon_event_text *ep;
+
+       if (rp->nevents >= EVENT_MAX ||
+           (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) {
+               rp->r.m_bus->cnt_text_lost++;
+               return;
+       }
+
+       ep->type = 'S';
+       ep->pipe = urb->pipe;
+       ep->id = (unsigned long) urb;
+       ep->length = urb->transfer_buffer_length;
+       ep->status = urb->status;       /* Let's watch what dirt ULPs supply */
+
+       rp->nevents++;
+       list_add_tail(&ep->e_link, &rp->e_list);
+       wake_up(&rp->wait);
+}
+
+/*
+ * May be called from an interrupt.
+ *
+ * This is called with the whole mon_bus locked, so no additional lock.
+ */
+static void mon_text_complete(void *data, struct urb *urb)
+{
+       struct mon_reader_text *rp = data;
+       struct mon_event_text *ep;
+
+       if (rp->nevents >= EVENT_MAX ||
+           (ep = kmem_cache_alloc(rp->e_slab, SLAB_ATOMIC)) == NULL) {
+               rp->r.m_bus->cnt_text_lost++;
+               return;
+       }
+
+       ep->type = 'C';
+       ep->pipe = urb->pipe;
+       ep->id = (unsigned long) urb;
+       ep->length = urb->actual_length;
+       ep->status = urb->status;
+
+       rp->nevents++;
+       list_add_tail(&ep->e_link, &rp->e_list);
+       wake_up(&rp->wait);
+}
+
+/*
+ * Fetch next event from the circular buffer.
+ */
+static struct mon_event_text *mon_text_fetch(struct mon_reader_text *rp,
+    struct mon_bus *mbus)
+{
+       struct list_head *p;
+       unsigned long flags;
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       if (list_empty(&rp->e_list)) {
+               spin_unlock_irqrestore(&mbus->lock, flags);
+               return NULL;
+       }
+       p = rp->e_list.next;
+       list_del(p);
+       --rp->nevents;
+       spin_unlock_irqrestore(&mbus->lock, flags);
+       return list_entry(p, struct mon_event_text, e_link);
+}
+
+/*
+ */
+static int mon_text_open(struct inode *inode, struct file *file)
+{
+       struct mon_bus *mbus;
+       struct usb_bus *ubus;
+       struct mon_reader_text *rp;
+       int rc;
+
+       down(&mon_lock);
+       mbus = inode->u.generic_ip;
+       ubus = mbus->u_bus;
+
+       rp = kmalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
+       if (rp == NULL) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+       memset(rp, 0, sizeof(struct mon_reader_text));
+       INIT_LIST_HEAD(&rp->e_list);
+       init_waitqueue_head(&rp->wait);
+       init_MUTEX(&rp->printf_lock);
+
+       rp->printf_size = PRINTF_DFL;
+       rp->printf_buf = kmalloc(rp->printf_size, GFP_KERNEL);
+       if (rp->printf_buf == NULL) {
+               rc = -ENOMEM;
+               goto err_alloc_pr;
+       }
+
+       rp->r.m_bus = mbus;
+       rp->r.r_data = rp;
+       rp->r.rnf_submit = mon_text_submit;
+       rp->r.rnf_complete = mon_text_complete;
+
+       snprintf(rp->slab_name, SLAB_NAME_SZ, "mon%dt_%lx", ubus->busnum,
+           (long)rp);
+       rp->e_slab = kmem_cache_create(rp->slab_name,
+           sizeof(struct mon_event_text), sizeof(long), 0,
+           mon_text_ctor, mon_text_dtor);
+       if (rp->e_slab == NULL) {
+               rc = -ENOMEM;
+               goto err_slab;
+       }
+
+       mon_reader_add(mbus, &rp->r);
+
+       file->private_data = rp;
+       up(&mon_lock);
+       return 0;
+
+// err_busy:
+//     kmem_cache_destroy(rp->e_slab);
+err_slab:
+       kfree(rp->printf_buf);
+err_alloc_pr:
+       kfree(rp);
+err_alloc:
+       up(&mon_lock);
+       return rc;
+}
+
+/*
+ * For simplicity, we read one record in one system call and throw out
+ * what does not fit. This means that the following does not work:
+ *   dd if=/dbg/usbmon/0t bs=10
+ * Also, we do not allow seeks and do not bother advancing the offset.
+ */
+static ssize_t mon_text_read(struct file *file, char __user *buf,
+                               size_t nbytes, loff_t *ppos)
+{
+       struct mon_reader_text *rp = file->private_data;
+       struct mon_bus *mbus = rp->r.m_bus;
+       DECLARE_WAITQUEUE(waita, current);
+       struct mon_event_text *ep;
+       int cnt;
+
+       add_wait_queue(&rp->wait, &waita);
+       while ((ep = mon_text_fetch(rp, mbus)) == NULL) {
+               if (file->f_flags & O_NONBLOCK) {
+                       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.
+                */
+               set_current_state(TASK_INTERRUPTIBLE);
+               schedule();
+               if (signal_pending(current)) {
+                       remove_wait_queue(&rp->wait, &waita);
+                       return -EINTR;
+               }
+       }
+       remove_wait_queue(&rp->wait, &waita);
+
+       down(&rp->printf_lock);
+       cnt = snprintf(rp->printf_buf, rp->printf_size,
+           "%lx 0 %c %08x %d %d\n",
+           ep->id, ep->type, ep->pipe, ep->status, ep->length);
+       if (copy_to_user(buf, rp->printf_buf, cnt))
+               cnt = -EFAULT;
+       up(&rp->printf_lock);
+       kmem_cache_free(rp->e_slab, ep);
+       return cnt;
+}
+
+static int mon_text_release(struct inode *inode, struct file *file)
+{
+       struct mon_reader_text *rp = file->private_data;
+       struct mon_bus *mbus;
+       struct usb_bus *ubus;
+       unsigned long flags;
+       struct list_head *p;
+       struct mon_event_text *ep;
+
+       down(&mon_lock);
+       mbus = inode->u.generic_ip;
+       ubus = mbus->u_bus;
+
+       if (mbus->nreaders <= 0) {
+               printk(KERN_ERR TAG ": consistency error on close\n");
+               up(&mon_lock);
+               return 0;
+       }
+       mon_reader_del(mbus, &rp->r);
+
+       spin_lock_irqsave(&mbus->lock, flags);
+       while (!list_empty(&rp->e_list)) {
+               p = rp->e_list.next;
+               ep = list_entry(p, struct mon_event_text, e_link);
+               list_del(p);
+               --rp->nevents;
+               kmem_cache_free(rp->e_slab, ep);
+       }
+       spin_unlock_irqrestore(&mbus->lock, flags);
+
+       kmem_cache_destroy(rp->e_slab);
+       kfree(rp->printf_buf);
+       kfree(rp);
+
+       up(&mon_lock);
+       return 0;
+}
+
+struct file_operations mon_fops_text = {
+       .open =         mon_text_open,
+       .llseek =       no_llseek,
+       .read =         mon_text_read,
+       /* .write =     mon_text_write, */
+       /* .poll =              mon_text_poll, */
+       /* .ioctl =     mon_text_ioctl, */
+       .release =      mon_text_release,
+};
+
+/*
+ * Slab interface: constructor.
+ */
+static void mon_text_ctor(void *mem, kmem_cache_t *slab, unsigned long sflags)
+{
+       /*
+        * Nothing to initialize. No, really!
+        * So, we fill it with garbage to emulate a reused object.
+        */
+       memset(mem, 0xe5, sizeof(struct mon_event_text));
+}
+
+static void mon_text_dtor(void *mem, kmem_cache_t *slab, unsigned long sflags)
+{
+       ;
+}
diff -urpN -X dontdiff linux-2.6.10-rc3/drivers/usb/mon/usb_mon.h 
linux-2.6.10-rc3-usb/drivers/usb/mon/usb_mon.h
--- linux-2.6.10-rc3/drivers/usb/mon/usb_mon.h  1969-12-31 16:00:00.000000000 
-0800
+++ linux-2.6.10-rc3-usb/drivers/usb/mon/usb_mon.h      2004-12-19 
14:07:51.351399563 -0800
@@ -0,0 +1,59 @@
+/*
+ * The USB Monitor, inspired by Dave Harding's USBMon.
+ */
+
+#ifndef __USB_MON_H
+#define __USB_MON_H
+
+#include <linux/list.h>
+#include <linux/slab.h>
+/* #include <linux/usb.h> */
+#include "../core/hcd.h"   /* XXX Move mon to core or move this to include/ */
+
+#define TAG "usbmon"
+
+struct mon_urb {
+       struct list_head link;
+       struct urb *urb;
+       // struct usb_dev *dev;
+};
+
+struct mon_bus {
+       struct list_head bus_link;
+       spinlock_t lock;
+       int nurbs;                      /* Number of tracked URBs */
+       struct dentry *dent_s;          /* Debugging file */
+       struct dentry *dent_t;          /* Text interface file */
+       struct usb_bus *u_bus;
+
+       /* Ref */
+       int nreaders;
+       struct list_head r_list;        /* Chain of readers (usually one) */
+       struct usb_operations *saved_op;
+       struct usb_operations shim_ops;
+
+       /* Stats */
+       unsigned int cnt_text_lost;
+};
+
+/*
+ * An instance of a process which opened a file (but can fork later)
+ */
+struct mon_reader {
+       struct list_head r_link;
+       struct mon_bus *m_bus;
+       void *r_data;           /* Use container_of instead? */
+
+       void (*rnf_submit)(void *data, struct urb *urb);
+       void (*rnf_complete)(void *data, struct urb *urb);
+};
+
+void mon_reader_add(struct mon_bus *mbus, struct mon_reader *r);
+void mon_reader_del(struct mon_bus *mbus, struct mon_reader *r);
+
+extern struct semaphore mon_lock;
+
+extern struct file_operations mon_fops_text;
+extern struct file_operations mon_fops_stat;
+
+#endif /* __USB_MON_H */
diff -urpN -X dontdiff linux-2.6.10-rc3/include/linux/usb.h 
linux-2.6.10-rc3-usb/include/linux/usb.h
--- linux-2.6.10-rc3/include/linux/usb.h        2004-12-15 02:01:18.000000000 
-0800
+++ linux-2.6.10-rc3-usb/include/linux/usb.h    2004-12-19 15:13:03.289200450 
-0800
@@ -254,6 +254,7 @@ struct usb_bus {
        u8 otg_port;                    /* 0, or number of OTG/HNP port */
        unsigned is_b_host:1;           /* true during some HNP roleswitches */
        unsigned b_hnp_enable:1;        /* OTG: did A-Host enable HNP? */
+       unsigned monitored:1;           /* usbmon is active on this bus */
 
        int devnum_next;                /* Next open device number in 
round-robin allocation */
 
@@ -277,6 +278,9 @@ struct usb_bus {
 
        struct class_device class_dev;  /* class device for this bus */
        void (*release)(struct usb_bus *bus);   /* function to destroy this 
bus's memory */
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+       struct mon_bus *mon_bus;
+#endif
 };
 #define        to_usb_bus(d) container_of(d, struct usb_bus, class_dev)
 
@@ -804,6 +808,10 @@ struct urb
        int error_count;                /* (return) number of ISO errors */
        void *context;                  /* (in) context for completion */
        usb_complete_t complete;        /* (in) completion routine */
+#if defined(CONFIG_USB_MON) || defined(CONFIG_USB_MON_MODULE)
+       usb_complete_t mon_saved_complete;      /* (core private) */
+       struct mon_bus *mon_bus;                /* (core private) */
+#endif
        struct usb_iso_packet_descriptor iso_frame_desc[0];     /* (in) ISO 
ONLY */
 };
 


-------------------------------------------------------
SF email is sponsored by - The IT Product Guide
Read honest & candid reviews on hundreds of IT Products from real users.
Discover which products truly live up to the hype. Start reading now. 
http://productguide.itmanagersjournal.com/
_______________________________________________
[EMAIL PROTECTED]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel

Reply via email to