Add support for USB isochronous. From Marton Nemeth via bug #5370.
authorChris Maynard <Christopher.Maynard@GTECH.COM>
Wed, 24 Nov 2010 17:33:58 +0000 (17:33 -0000)
committerChris Maynard <Christopher.Maynard@GTECH.COM>
Wed, 24 Nov 2010 17:33:58 +0000 (17:33 -0000)
svn path=/trunk/; revision=35025

epan/dissectors/packet-usb.c
wiretap/pcap-common.c

index 3b299d234c9359ecd51f1dda3caf3a607497d12e..bf66911bdc7786fad2654186a9e1939ee631cd34 100644 (file)
@@ -72,6 +72,15 @@ static int hf_usb_wFeatureSelector = -1;
 static int hf_usb_wInterface = -1;
 static int hf_usb_wStatus = -1;
 static int hf_usb_wFrameNumber = -1;
+
+static int hf_usb_iso_error_count = -1;
+static int hf_usb_iso_numdesc = -1;
+static int hf_usb_iso_status = -1;
+static int hf_usb_iso_off = -1;
+static int hf_usb_iso_len = -1;
+static int hf_usb_iso_pad = -1;
+static int hf_usb_iso_data = -1;
+
 static int hf_usb_bmRequestType = -1;
 static int hf_usb_bmRequestType_direction = -1;
 static int hf_usb_bmRequestType_type = -1;
@@ -125,6 +134,7 @@ static int hf_usb_request_in = -1;
 
 static gint usb_hdr = -1;
 static gint usb_setup_hdr = -1;
+static gint usb_isodesc = -1;
 static gint ett_usb_setup_bmrequesttype = -1;
 static gint ett_descriptor_device = -1;
 static gint ett_configuration_bmAttributes = -1;
@@ -2154,6 +2164,121 @@ dissect_linux_usb_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent,
         }
         }
         break;
+    case URB_ISOCHRONOUS:
+        {
+        /* All fields which belong to Linux usbmon headers are in host-endian
+         * byte order. The fields coming from the USB communication are in little
+         * endian format (see usb_20.pdf, chapter 8.1 Byte/Bit ordering).
+         *
+         * When a capture file is transfered to a host with different endianness
+         * than packet was captured then the necessary swapping happens in
+         * wiretap/pcap-common.c, pcap_process_linux_usb_pseudoheader().
+         */
+        guint32 iso_numdesc = 0;
+
+        if (setup_flag == 0) {
+            proto_item *ti = NULL;
+            proto_tree *setup_tree = NULL;
+            int type_2;
+
+            /* Dissect the setup header - it's applicable */
+
+            ti = proto_tree_add_protocol_format(tree, proto_usb, tvb, offset, 8, "URB setup");
+            setup_tree = proto_item_add_subtree(ti, usb_setup_hdr);
+
+            offset = dissect_usb_bmrequesttype(setup_tree, tvb, offset, &type_2);
+            proto_tree_add_item(setup_tree, hf_usb_request, tvb, offset, 1, TRUE);
+            offset++;
+            proto_tree_add_item(tree, hf_usb_value, tvb, offset, 2, TRUE);
+            offset += 2;
+            proto_tree_add_item(tree, hf_usb_index, tvb, offset, 2, TRUE);
+            offset += 2;
+            proto_tree_add_item(tree, hf_usb_length, tvb, offset, 2, TRUE);
+            offset += 2;
+        } else {
+
+            /* Process ISO related fields (usbmon_packet.iso). The fields are
+             * in host endian byte order so use tvb_memcopy() and
+             * proto_tree_add_uint() pair.
+             */
+            guint32 val32;
+
+            tvb_memcpy(tvb, (guint8 *)&val32, offset, 4);
+            proto_tree_add_uint(tree, hf_usb_iso_error_count, tvb, offset, 4, val32);
+            offset += 4;
+
+            tvb_memcpy(tvb, (guint8 *)&iso_numdesc, offset, 4);
+            proto_tree_add_uint(tree, hf_usb_iso_numdesc, tvb, offset, 4, iso_numdesc);
+            offset += 4;
+        }
+
+        /*
+         * If this has a 64-byte header, process the extra 16 bytes of
+         * pseudo-header information.
+         */
+        if (header_len_64_bytes) {
+            guint32 ndesc;
+
+            offset += 4;       /* interval */
+            offset += 4;       /* start_frame */
+            offset += 4;       /* copy of URB's transfer flags */
+
+            tvb_memcpy(tvb, (guint8 *)&ndesc, offset, 4);
+            offset += 4;
+
+        }
+
+        if (setup_flag != 0) {
+            proto_tree *urb_tree = NULL;
+            guint32 i;
+            unsigned int data_base;
+            guint32 iso_status;
+            guint32 iso_off;
+            guint32 iso_len;
+            guint32 iso_pad;
+
+            data_base = offset + iso_numdesc * 16;
+            urb_tree = tree;
+            for (i = 0; i != iso_numdesc; i++) {
+                if (parent) {
+                    proto_item *ti = NULL;
+                    ti = proto_tree_add_protocol_format(urb_tree, proto_usb, tvb, offset,
+                         16, "USB isodesc %u", i);
+                    tree = proto_item_add_subtree(ti, usb_isodesc);
+                }
+
+                /* Add ISO descriptor fields which are stored in host
+                 * endian byte order so use tvb_memcopy() and
+                 * proto_tree_add_uint()/proto_tree_add_int() pair.
+                 */
+                tvb_memcpy(tvb, (guint8 *)&iso_status, offset, 4);
+                proto_tree_add_int(tree, hf_usb_iso_status, tvb, offset, 4, iso_status);
+                offset += 4;
+
+                tvb_memcpy(tvb, (guint8 *)&iso_off, offset, 4);
+                proto_tree_add_uint(tree, hf_usb_iso_off, tvb, offset, 4, iso_off);
+                offset += 4;
+
+                tvb_memcpy(tvb, (guint8 *)&iso_len, offset, 4);
+                proto_tree_add_uint(tree, hf_usb_iso_len, tvb, offset, 4, iso_len);
+                offset += 4;
+
+                /* When the ISO status is OK and there is ISO data and this ISO data is
+                 * fully captured then show this data.
+                 */
+                if (!iso_status && iso_len && data_base + iso_off + iso_len <= tvb_length(tvb))
+                    proto_tree_add_item(tree, hf_usb_iso_data, tvb, data_base + iso_off, iso_len, TRUE);
+
+                tvb_memcpy(tvb, (guint8 *)&iso_pad, offset, 4);
+                proto_tree_add_uint(tree, hf_usb_iso_pad, tvb, offset, 4, iso_pad);
+                offset += 4;
+            }
+            tree = urb_tree;
+        }
+
+        }
+        break;
+
     default:
         /* dont know */
         if (setup_flag == 0) {
@@ -2326,6 +2451,37 @@ proto_register_usb(void)
         { "wFrameNumber", "usb.setup.wFrameNumber", FT_UINT16, BASE_DEC,
                 NULL, 0x0, NULL, HFILL }},
 
+    /* --------------------------------- */
+        { &hf_usb_iso_error_count,                /* host endian byte order */
+        { "ISO error count", "usb.iso.error_count", FT_UINT16, BASE_DEC, NULL, 0x0,
+                NULL, HFILL }},
+
+        { &hf_usb_iso_numdesc,                    /* host endian byte order */
+        { "Number of ISO descriptors", "usb.iso.numdesc", FT_UINT16, BASE_DEC, NULL, 0x0,
+                NULL, HFILL }},
+
+    /* fields of struct mon_bin_isodesc from linux/drivers/usb/mon/mon_bin.c */
+        { &hf_usb_iso_status,                     /* host endian byte order */
+        { "Status", "usb.iso.iso_status", FT_INT32, BASE_DEC,
+                VALS(usb_urb_status_vals), 0x0,
+                "ISO descriptor status", HFILL }},
+
+        { &hf_usb_iso_off,                        /* host endian byte order */
+        { "Offset [bytes]", "usb.iso.iso_off", FT_UINT32, BASE_DEC, NULL, 0x0,
+                "ISO data offset in bytes starting from the end of the last ISO descriptor", HFILL }},
+
+        { &hf_usb_iso_len,                        /* host endian byte order */
+        { "Length [bytes]", "usb.iso.iso_len", FT_UINT32, BASE_DEC, NULL, 0x0,
+                "ISO data length in bytes", HFILL }},
+
+        { &hf_usb_iso_pad,                        /* host endian byte order */
+        { "Padding", "usb.iso._pad", FT_UINT32, BASE_HEX, NULL, 0x0,
+                "Padding field of ISO descriptor structure", HFILL }},
+
+        { &hf_usb_iso_data,
+        {"ISO Data", "usb.iso.data",
+            FT_BYTES, BASE_NONE, NULL, 0x0,
+            NULL, HFILL }},
     /* --------------------------------- */
         { &hf_usb_data,
         {"Application Data", "usb.data",
@@ -2537,6 +2693,7 @@ proto_register_usb(void)
     static gint *usb_subtrees[] = {
         &usb_hdr,
         &usb_setup_hdr,
+        &usb_isodesc,
         &ett_usb_setup_bmrequesttype,
         &ett_descriptor_device,
         &ett_configuration_bmAttributes,
index f6467d2014dae7cafeb7b3b3ab3cc60cd1e231ae..82b4776fa8dd84124fda81cbf1425625a4f69ac5 100644 (file)
@@ -961,6 +961,24 @@ pcap_read_sita_pseudoheader(FILE_T fh, union wtap_pseudo_header *pseudo_header,
  * a "struct linux_usb_phdr_ext".
  */
 
+/*
+ * URB transfer_type values
+ */
+#define URB_ISOCHRONOUS   0x0
+#define URB_INTERRUPT     0x1
+#define URB_CONTROL       0x2
+#define URB_BULK          0x3
+
+/*
+ * Information from the URB for Isochronous transfers.
+ *
+ * This structure is 8 bytes long.
+ */
+struct iso_rec {
+    gint32 error_count;
+    gint32 numdesc;
+};
+
 /*
  * Header prepended by Linux kernel to each USB event.
  *
@@ -988,7 +1006,10 @@ struct linux_usb_phdr {
      * USB setup information of setup_flag is true.
      * Otherwise, some isochronous transfer information.
      */
-    guint8 data[8];
+    union {
+        guint8 data[8];
+        struct iso_rec iso;
+    } s;
 
     /*
      * This data is provided by Linux 2.6.31 and later kernels.
@@ -1008,6 +1029,13 @@ struct linux_usb_phdr {
     guint32 ndesc;      /* actual number of isochronous descriptors */
 };
 
+struct linux_usb_isodesc {
+    gint32 iso_status;
+    guint32 iso_off;
+    guint32 iso_len;
+    guint32 _pad;
+};
+
 /*
  * USB setup header as defined in USB specification
  * See usb_20.pdf, Chapter 9.3 'USB Device Requests' for details.
@@ -1023,15 +1051,6 @@ struct usb_device_setup_hdr {
     guint16 wLength;
 };
 
-/*
- * Information from the URB for Isochronous transfers.
- *
- * This structure is 8 bytes long.
- */
-struct iso_rec {
-    gint32 error_count;
-    gint32 numdesc;
-};
 
 /*
  * Offset of the *end* of a field within a particular structure.
@@ -1045,6 +1064,8 @@ pcap_process_linux_usb_pseudoheader(guint packet_size, gboolean byte_swapped,
     gboolean header_len_64_bytes, guint8 *pd)
 {
        struct linux_usb_phdr *phdr;
+       struct linux_usb_isodesc *pisodesc;
+       gint32 iso_numdesc, i;
 
        if (byte_swapped) {
                phdr = (struct linux_usb_phdr *)pd;
@@ -1071,6 +1092,17 @@ pcap_process_linux_usb_pseudoheader(guint packet_size, gboolean byte_swapped,
                        return;
                PBSWAP32((guint8 *)&phdr->data_len);
 
+               if (phdr->transfer_type == URB_ISOCHRONOUS) {
+                       if (packet_size < END_OFFSETOF(phdr, &phdr->s.iso.error_count))
+                               return;
+                       PBSWAP32((guint8 *)&phdr->s.iso.error_count);
+
+                       if (packet_size < END_OFFSETOF(phdr, &phdr->s.iso.numdesc))
+                               return;
+                       PBSWAP32((guint8 *)&phdr->s.iso.numdesc);
+
+               }
+
                if (header_len_64_bytes) {
                        /*
                         * This is either the "version 1" header, with
@@ -1097,7 +1129,37 @@ pcap_process_linux_usb_pseudoheader(guint packet_size, gboolean byte_swapped,
                        if (packet_size < END_OFFSETOF(phdr, &phdr->ndesc))
                                return;
                        PBSWAP32((guint8 *)&phdr->ndesc);
-               }       
+               }
+
+               if (phdr->transfer_type == URB_ISOCHRONOUS) {
+                       /* swap the values in struct linux_usb_isodesc */
+
+                       if (header_len_64_bytes) {
+                               pisodesc = (struct linux_usb_isodesc*)(pd + 64);
+                       } else {
+                               pisodesc = (struct linux_usb_isodesc*)(pd + 48);
+                       }
+                       iso_numdesc = phdr->s.iso.numdesc;
+                       for (i = 0; i < iso_numdesc; i++) {
+                               /* always check if we have enough data from the
+                                * beginnig of the packet (phdr)
+                                */
+                               if (packet_size < END_OFFSETOF(phdr, &pisodesc->iso_status))
+                                       return;
+                               PBSWAP32((guint8 *)&pisodesc->iso_status);
+                               if (packet_size < END_OFFSETOF(phdr, &pisodesc->iso_off))
+                                       return;
+                               PBSWAP32((guint8 *)&pisodesc->iso_off);
+                               if (packet_size < END_OFFSETOF(phdr, &pisodesc->iso_len))
+                                       return;
+                               PBSWAP32((guint8 *)&pisodesc->iso_len);
+                               if (packet_size < END_OFFSETOF(phdr, &pisodesc->_pad))
+                                       return;
+                               PBSWAP32((guint8 *)&pisodesc->_pad);
+
+                               pisodesc++;
+                       }
+               }
        }
 }