USB: usbmon: Add binary API v1
Pete Zaitcev [Fri, 20 Feb 2009 05:54:45 +0000 (22:54 -0700)]
This patch adds an extension to the binary API so it reaches parity with
existing text API (so-called "1u"). The extension delivers additional data,
such as ISO descriptors and the interrupt interval.

Signed-Off-By: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

Documentation/usb/usbmon.txt
drivers/usb/mon/mon_bin.c

index 2704819..6c3c625 100644 (file)
@@ -229,16 +229,26 @@ struct usbmon_packet {
        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 */
+       union {                 /* 40: */
+               unsigned char setup[SETUP_LEN]; /* Only for Control S-type */
+               struct iso_rec {                /* Only for ISO */
+                       int error_count;
+                       int numdesc;
+               } iso;
+       } s;
+       int interval;           /* 48: Only for Interrupt and ISO */
+       int start_frame;        /* 52: For ISO */
+       unsigned int xfer_flags; /* 56: copy of URB's transfer_flags */
+       unsigned int ndesc;     /* 60: Actual number of ISO descriptors */
+};                             /* 64 total length */
 
 These events can be received from a character device by reading with read(2),
-with an ioctl(2), or by accessing the buffer with mmap.
+with an ioctl(2), or by accessing the buffer with mmap. However, read(2)
+only returns first 48 bytes for compatibility reasons.
 
 The character device is usually called /dev/usbmonN, where N is the USB bus
 number. Number zero (/dev/usbmon0) is special and means "all buses".
-However, this feature is not implemented yet. Note that specific naming
-policy is set by your Linux distribution.
+Note that specific naming policy is set by your Linux distribution.
 
 If you create /dev/usbmon0 by hand, make sure that it is owned by root
 and has mode 0600. Otherwise, unpriviledged users will be able to snoop
@@ -279,9 +289,10 @@ size is out of [unspecified] bounds for this kernel, the call fails with
 This call returns the current size of the buffer in bytes.
 
  MON_IOCX_GET, defined as _IOW(MON_IOC_MAGIC, 6, struct mon_get_arg)
+ MON_IOCX_GETX, defined as _IOW(MON_IOC_MAGIC, 10, struct mon_get_arg)
 
-This call waits for events to arrive if none were in the kernel buffer,
-then returns the first event. Its argument is a pointer to the following
+These calls wait for events to arrive if none were in the kernel buffer,
+then return the first event. The argument is a pointer to the following
 structure:
 
 struct mon_get_arg {
@@ -294,6 +305,8 @@ Before the call, hdr, data, and alloc should be filled. Upon return, the area
 pointed by hdr contains the next event structure, and the data buffer contains
 the data, if any. The event is removed from the kernel buffer.
 
+The MON_IOCX_GET copies 48 bytes, MON_IOCX_GETX copies 64 bytes.
+
  MON_IOCX_MFETCH, defined as _IOWR(MON_IOC_MAGIC, 7, struct mon_mfetch_arg)
 
 This ioctl is primarily used when the application accesses the buffer
index 4cf27c7..f8d9045 100644 (file)
 #define MON_IOCX_GET   _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get)
 #define MON_IOCX_MFETCH _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch)
 #define MON_IOCH_MFLUSH _IO(MON_IOC_MAGIC, 8)
+/* #9 was MON_IOCT_SETAPI */
+#define MON_IOCX_GETX   _IOW(MON_IOC_MAGIC, 10, struct mon_bin_get)
 
 #ifdef CONFIG_COMPAT
 #define MON_IOCX_GET32 _IOW(MON_IOC_MAGIC, 6, struct mon_bin_get32)
 #define MON_IOCX_MFETCH32 _IOWR(MON_IOC_MAGIC, 7, struct mon_bin_mfetch32)
+#define MON_IOCX_GETX32   _IOW(MON_IOC_MAGIC, 10, struct mon_bin_get32)
 #endif
 
 /*
@@ -92,7 +95,29 @@ struct mon_bin_hdr {
        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 */
+       union {
+               unsigned char setup[SETUP_LEN]; /* Only for Control S-type */
+               struct iso_rec {
+                       int error_count;
+                       int numdesc;
+               } iso;
+       } s;
+       int interval;
+       int start_frame;
+       unsigned int xfer_flags;
+       unsigned int ndesc;     /* Actual number of ISO descriptors */
+};
+
+/*
+ * ISO vector, packed into the head of data stream.
+ * This has to take 16 bytes to make sure that the end of buffer
+ * wrap is not happening in the middle of a descriptor.
+ */
+struct mon_bin_isodesc {
+       int          iso_status;
+       unsigned int iso_off;
+       unsigned int iso_len;
+       u32 _pad;
 };
 
 /* per file statistic */
@@ -102,7 +127,7 @@ struct mon_bin_stats {
 };
 
 struct mon_bin_get {
-       struct mon_bin_hdr __user *hdr; /* Only 48 bytes, not 64. */
+       struct mon_bin_hdr __user *hdr; /* Can be 48 bytes or 64. */
        void __user *data;
        size_t alloc;           /* Length of data (can be zero) */
 };
@@ -131,6 +156,11 @@ struct mon_bin_mfetch32 {
 #define PKT_ALIGN   64
 #define PKT_SIZE    64
 
+#define PKT_SZ_API0 48 /* API 0 (2.6.20) size */
+#define PKT_SZ_API1 64 /* API 1 size: extra fields */
+
+#define ISODESC_MAX   128      /* Same number as usbfs allows, 2048 bytes. */
+
 /* max number of USB bus supported */
 #define MON_BIN_MAX_MINOR 128
 
@@ -360,12 +390,8 @@ static inline char mon_bin_get_setup(unsigned char *setupb,
     const struct urb *urb, char ev_type)
 {
 
-       if (!usb_endpoint_xfer_control(&urb->ep->desc) || ev_type != 'S')
-               return '-';
-
        if (urb->setup_packet == NULL)
                return 'Z';
-
        memcpy(setupb, urb->setup_packet, SETUP_LEN);
        return 0;
 }
@@ -387,6 +413,26 @@ static char mon_bin_get_data(const struct mon_reader_bin *rp,
        return 0;
 }
 
+static void mon_bin_get_isodesc(const struct mon_reader_bin *rp,
+    unsigned int offset, struct urb *urb, char ev_type, unsigned int ndesc)
+{
+       struct mon_bin_isodesc *dp;
+       struct usb_iso_packet_descriptor *fp;
+
+       fp = urb->iso_frame_desc;
+       while (ndesc-- != 0) {
+               dp = (struct mon_bin_isodesc *)
+                   (rp->b_vec[offset / CHUNK_SIZE].ptr + offset % CHUNK_SIZE);
+               dp->iso_status = fp->status;
+               dp->iso_off = fp->offset;
+               dp->iso_len = (ev_type == 'S') ? fp->length : fp->actual_length;
+               dp->_pad = 0;
+               if ((offset += sizeof(struct mon_bin_isodesc)) >= rp->b_size)
+                       offset = 0;
+               fp++;
+       }
+}
+
 static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
     char ev_type, int status)
 {
@@ -396,6 +442,7 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
        unsigned int urb_length;
        unsigned int offset;
        unsigned int length;
+       unsigned int ndesc, lendesc;
        unsigned char dir;
        struct mon_bin_hdr *ep;
        char data_tag = 0;
@@ -407,6 +454,19 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
        /*
         * Find the maximum allowable length, then allocate space.
         */
+       if (usb_endpoint_xfer_isoc(epd)) {
+               if (urb->number_of_packets < 0) {
+                       ndesc = 0;
+               } else if (urb->number_of_packets >= ISODESC_MAX) {
+                       ndesc = ISODESC_MAX;
+               } else {
+                       ndesc = urb->number_of_packets;
+               }
+       } else {
+               ndesc = 0;
+       }
+       lendesc = ndesc*sizeof(struct mon_bin_isodesc);
+
        urb_length = (ev_type == 'S') ?
            urb->transfer_buffer_length : urb->actual_length;
        length = urb_length;
@@ -429,10 +489,12 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
                dir = 0;
        }
 
-       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 (rp->mmap_active) {
+               offset = mon_buff_area_alloc_contiguous(rp,
+                                                length + PKT_SIZE + lendesc);
+       } else {
+               offset = mon_buff_area_alloc(rp, length + PKT_SIZE + lendesc);
+       }
        if (offset == ~0) {
                rp->cnt_lost++;
                spin_unlock_irqrestore(&rp->b_lock, flags);
@@ -456,9 +518,31 @@ static void mon_bin_event(struct mon_reader_bin *rp, struct urb *urb,
        ep->ts_usec = ts.tv_usec;
        ep->status = status;
        ep->len_urb = urb_length;
-       ep->len_cap = length;
+       ep->len_cap = length + lendesc;
+       ep->xfer_flags = urb->transfer_flags;
+
+       if (usb_endpoint_xfer_int(epd)) {
+               ep->interval = urb->interval;
+       } else if (usb_endpoint_xfer_isoc(epd)) {
+               ep->interval = urb->interval;
+               ep->start_frame = urb->start_frame;
+               ep->s.iso.error_count = urb->error_count;
+               ep->s.iso.numdesc = urb->number_of_packets;
+       }
+
+       if (usb_endpoint_xfer_control(epd) && ev_type == 'S') {
+               ep->flag_setup = mon_bin_get_setup(ep->s.setup, urb, ev_type);
+       } else {
+               ep->flag_setup = '-';
+       }
+
+       if (ndesc != 0) {
+               ep->ndesc = ndesc;
+               mon_bin_get_isodesc(rp, offset, urb, ev_type, ndesc);
+               if ((offset += lendesc) >= rp->b_size)
+                       offset -= rp->b_size;
+       }
 
-       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' */
@@ -592,7 +676,8 @@ err_alloc:
  * Returns zero or error.
  */
 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)
+    struct mon_bin_hdr __user *hdr, unsigned int hdrbytes,
+    void __user *data, unsigned int nbytes)
 {
        unsigned long flags;
        struct mon_bin_hdr *ep;
@@ -609,7 +694,7 @@ static int mon_bin_get_event(struct file *file, struct mon_reader_bin *rp,
 
        ep = MON_OFF2HDR(rp, rp->b_out);
 
-       if (copy_to_user(hdr, ep, sizeof(struct mon_bin_hdr))) {
+       if (copy_to_user(hdr, ep, hdrbytes)) {
                mutex_unlock(&rp->fetch_lock);
                return -EFAULT;
        }
@@ -657,6 +742,7 @@ 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;
+       unsigned int hdrbytes = PKT_SZ_API0;
        unsigned long flags;
        struct mon_bin_hdr *ep;
        unsigned int offset;
@@ -674,8 +760,8 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf,
 
        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);
+       if (rp->b_read < hdrbytes) {
+               step_len = min(nbytes, (size_t)(hdrbytes - rp->b_read));
                ptr = ((char *)ep) + rp->b_read;
                if (step_len && copy_to_user(buf, ptr, step_len)) {
                        mutex_unlock(&rp->fetch_lock);
@@ -687,13 +773,13 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf,
                done += step_len;
        }
 
-       if (rp->b_read >= sizeof(struct mon_bin_hdr)) {
+       if (rp->b_read >= hdrbytes) {
                step_len = ep->len_cap;
-               step_len -= rp->b_read - sizeof(struct mon_bin_hdr);
+               step_len -= rp->b_read - hdrbytes;
                if (step_len > nbytes)
                        step_len = nbytes;
                offset = rp->b_out + PKT_SIZE;
-               offset += rp->b_read - sizeof(struct mon_bin_hdr);
+               offset += rp->b_read - hdrbytes;
                if (offset >= rp->b_size)
                        offset -= rp->b_size;
                if (copy_from_buf(rp, offset, buf, step_len)) {
@@ -709,7 +795,7 @@ static ssize_t mon_bin_read(struct file *file, char __user *buf,
        /*
         * 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) {
+       if (rp->b_read >= hdrbytes + 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);
@@ -908,6 +994,7 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file,
                break;
 
        case MON_IOCX_GET:
+       case MON_IOCX_GETX:
                {
                struct mon_bin_get getb;
 
@@ -917,8 +1004,9 @@ static int mon_bin_ioctl(struct inode *inode, struct file *file,
 
                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);
+               ret = mon_bin_get_event(file, rp, getb.hdr,
+                   (cmd == MON_IOCX_GET)? PKT_SZ_API0: PKT_SZ_API1,
+                   getb.data, (unsigned int)getb.alloc);
                }
                break;
 
@@ -984,16 +1072,18 @@ static long mon_bin_compat_ioctl(struct file *file,
 
        switch (cmd) {
 
-       case MON_IOCX_GET32: {
+       case MON_IOCX_GET32:
+       case MON_IOCX_GETX32:
+               {
                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);
+               ret = mon_bin_get_event(file, rp, compat_ptr(getb.hdr32),
+                   (cmd == MON_IOCX_GET32)? PKT_SZ_API0: PKT_SZ_API1,
+                   compat_ptr(getb.data32), getb.alloc32);
                if (ret < 0)
                        return ret;
                }