• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/perl
2
3#   Copyright (C) 2011-2012 Mauro Carvalho Chehab
4#
5#   This program is free software; you can redistribute it and/or modify
6#   it under the terms of the GNU General Public License as published by
7#   the Free Software Foundation, version 2 of the License.
8#
9#   This program is distributed in the hope that it will be useful,
10#   but WITHOUT ANY WARRANTY; without even the implied warranty of
11#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12#   GNU General Public License for more details.
13#
14# tcpdump parser imported from TcpDumpLog.pm, in order to improve performance,
15# reduce memory footprint and allow doing realtime parsing.
16# The TcpDumpLog.pm is copyrighted by Brendan Gregg.
17#
18# Currently, the program is known to work with the frame formats as parsed by
19# libpcap 1.0.0 and 1.1.1, and provided that usbmon is using the mmaped
20# header: USB with padded Linux header (LINKTYPE_USB_LINUX_MMAPPED)
21
22# using cpan, you should install Net::Pcap, in order to allow direct capture
23#	On Fedora/RHEL6, it is called "perl-Net-Pcap"
24# FIXME: make this dependency optional
25use Net::Pcap;
26
27use strict;
28#use warnings;
29use Getopt::Long;
30use Pod::Usage;
31use File::Find;
32
33# Enable autoflush
34BEGIN { $| = 1 }
35
36# Debug levels:
37#	1 - frame request and frame response
38#	2 - parsed frames
39#	4 - raw data
40my $debug = 0;
41
42my $man = 0;
43my $help = 0;
44my $pcap = 0;
45my $list_devices = 0;
46my $device;
47my @usbdev = ();
48my $frame_processor;
49
50GetOptions('debug=i' => \$debug,
51	   'help|?' => \$help,
52	   'pcap' => \$pcap,
53	   'device=s' => \$device,
54	    man => \$man,
55	   'usbdev=i' => \@usbdev,
56	   'list-devices' => \$list_devices,
57	   'frame_processor=s' => \$frame_processor,
58	  ) or pod2usage(2);
59pod2usage(1) if $help;
60pod2usage(-exitstatus => 0, -verbose => 2) if $man;
61
62my %devs = map { $_ => 1 } @usbdev;
63
64my $filename = shift;
65
66$pcap = 1 if ($device);
67$device = "usbmon1" if ($pcap && !$device);
68die "Either use pcap or specify a filename" if ($pcap && $filename);
69
70#
71# tcpdump code imported from Tcpdumplog.pm
72# Copyright (c) 2003 Brendan Gregg. All rights reserved.  This
73#     library is free software; you can redistribute it and/or
74#     modify it under the same terms as Perl itself
75# Perl is dual-licensed between GPL and Artistic license, so we've opted
76# to make this utility as GPLv2.
77#
78# This is basically the code from TcpDumpLog.pm. The only change is that
79# instead of implementing a read() method, it was broken into two routines:
80# get_header() and get_packet(). Also, only the used sub-routines were
81# imported.
82#
83
84sub new {
85	my $proto = shift;
86	my $class = ref($proto) || $proto;
87	my $self = {};
88
89	my $bits = shift;
90	my $skip = shift;
91
92	$self->{major} = undef;
93	$self->{minor} = undef;
94	$self->{zoneoffset} = undef;
95	$self->{accuracy} = undef;
96	$self->{dumplength} = undef;
97	$self->{linktype} = undef;
98	$self->{bigendian} = undef;
99	$self->{data} = [];
100	$self->{length_orig} = [];
101	$self->{length_inc} = [];
102	$self->{drops} = [];
103	$self->{seconds} = [];
104	$self->{msecs} = [];
105	$self->{count} = 0;
106	$self->{sizeint} = length(pack("I",0));
107
108	if (defined $bits && $bits == 64) {
109		$self->{bits} = 64;
110	} elsif (defined $bits && $bits == 32) {
111		$self->{bits} = 32;
112	} else {
113		$self->{bits} = 0;	# Use native OS bits
114	}
115
116	if (defined $skip && $skip > 0) {
117		$self->{skip} = $skip;
118	}
119
120	bless($self,$class);
121	return $self;
122}
123
124sub get_header {
125	my $self = shift;
126	my $fh = shift;
127
128	my ($header, $length, $major, $minor, $zoneoffset, $accuracy);
129	my ($dumplength, $linktype, $version, $ident, $rest);
130
131	$length = read($fh, $header, 24);
132	die "ERROR: Can't read from tcpdump log\n" if $length < 24;
133
134	### Check file really is a tcpdump file
135	($ident, $rest) = unpack('a4a20', $header);
136
137	### Find out what type of tcpdump file it is
138	if ($ident =~ /^\241\262\303\324/) {
139		#
140		#  Standard format big endian, header "a1b2c3d4"
141		#  Seen from:
142		#	Solaris tcpdump
143		#	Solaris Ethereal "libpcap" format
144		#
145		$self->{style} = "standard1";
146		$self->{bigendian} = 1;
147		($ident,$major,$minor,$zoneoffset,$accuracy,$dumplength,
148		 $linktype) = unpack('a4nnNNNN',$header);
149	}
150	elsif ($ident =~ /^\324\303\262\241/) {
151		#
152		#  Standard format little endian, header "d4c3b2a1"
153		#  Seen from:
154		#	Windows Ethereal "libpcap" format
155		#
156		$self->{style} = "standard2";
157		$self->{bigendian} = 0;
158		($ident,$major,$minor,$zoneoffset,$accuracy,$dumplength,
159		 $linktype) = unpack('a4vvVVVV',$header);
160	}
161	elsif ($ident =~ /^\241\262\315\064/) {
162		#
163		#  Modified format big endian, header "a1b2cd34"
164		#  Seen from:
165		#	Solaris Ethereal "modified" format
166		#
167		$self->{style} = "modified1";
168		$self->{bigendian} = 1;
169		($ident,$major,$minor,$zoneoffset,$accuracy,$dumplength,
170		 $linktype) = unpack('a4nnNNNN',$header);
171	}
172	elsif ($ident =~ /^\064\315\262\241/) {
173		#
174		#  Modified format little endian, header "cd34a1b2"
175		#  Seen from:
176		#	Red Hat tcpdump
177		#	Windows Ethereal "modified" format
178		#
179		$self->{style} = "modified2";
180		$self->{bigendian} = 0;
181		($ident,$major,$minor,$zoneoffset,$accuracy,$dumplength,
182		 $linktype) = unpack('a4vvVVVV',$header);
183	}
184	else {
185		die "unknown magic in header, cannot parse this file, make sure it is pcap and not a pcapng (run file <filename> to find out) and then convert with wireshark.";
186	}
187	### Store values
188	$self->{version} = $version;
189	$self->{major} = $major;
190	$self->{minor} = $minor;
191	$self->{zoneoffset} = $zoneoffset;
192	$self->{accuracy} = $accuracy;
193	$self->{dumplength} = $dumplength;
194	$self->{linktype} = $linktype;
195}
196
197sub get_packet {
198	my $self = shift;
199	my $fh = shift;
200
201	my ($frame_data, $frame_drops, $frame_length_inc, $frame_length_orig);
202	my ($frame_msecs, $frame_seconds, $header_rec, $length, $more);
203
204	if ($self->{bits} == 64) {
205		#
206		#  64-bit timestamps, quads
207		#
208
209		### Fetch record header
210		$length = read($fh, $header_rec, 24);
211
212		### Quit loop if at end of file
213		return -1 if $length < 24;
214
215		### Unpack header
216		($frame_seconds, $frame_msecs, $frame_length_inc,
217			$frame_length_orig) = unpack('QQLL',$header_rec);
218	} elsif ($self->{bits} == 32) {
219		#
220		#  32-bit timestamps, big-endian
221		#
222
223		### Fetch record header
224		$length = read($fh, $header_rec, 16);
225
226		### Quit loop if at end of file
227		return -1 if $length < 16;
228
229		### Unpack header
230		if ($self->{bigendian}) {
231			($frame_seconds, $frame_msecs,
232				$frame_length_inc, $frame_length_orig)
233				= unpack('NNNN', $header_rec);
234		} else {
235			($frame_seconds, $frame_msecs,
236				$frame_length_inc, $frame_length_orig)
237				= unpack('VVVV', $header_rec);
238		}
239	} else {
240		#
241		#  Default to OS native timestamps
242		#
243
244		### Fetch record header
245		$length = read($fh, $header_rec,
246			($self->{sizeint} * 2 + 8) );
247
248		### Quit loop if at end of file
249		return -1 if $length < ($self->{sizeint} * 2 + 8);
250
251		### Unpack header
252		if ($self->{sizeint} == 4) {	# 32-bit
253			if ($self->{bigendian}) {
254				($frame_seconds, $frame_msecs,
255					$frame_length_inc, $frame_length_orig)
256					= unpack('NNNN', $header_rec);
257			} else {
258				($frame_seconds, $frame_msecs,
259					$frame_length_inc, $frame_length_orig)
260					= unpack('VVVV', $header_rec);
261			}
262		} else {			# 64-bit?
263			if ($self->{bigendian}) {
264				($frame_seconds, $frame_msecs,
265					$frame_length_inc, $frame_length_orig)
266					= unpack('IINN', $header_rec);
267			} else {
268				($frame_seconds,$frame_msecs,
269					$frame_length_inc, $frame_length_orig)
270					= unpack('IIVV', $header_rec);
271			}
272		}
273	}
274
275	### Fetch extra info if in modified format
276	if ($self->{style} =~ /^modified/) {
277		$length = read($fh, $more, 8);
278	}
279
280	### Check for skip bytes
281	if (defined $self->{skip}) {
282		$length = read($fh, $more, $self->{skip});
283	}
284
285	### Fetch the data
286	$length = read($fh, $frame_data, $frame_length_inc);
287
288	$frame_drops = $frame_length_orig - $frame_length_inc;
289
290	### Store values in memory
291	$self->{data} = $frame_data;
292	$self->{length_orig} = $frame_length_orig;
293	$self->{length_inc} = $frame_length_inc;
294	$self->{drops} = $frame_drops;
295	$self->{seconds} = $frame_seconds;
296	$self->{msecs} = $frame_msecs;
297	$self->{more} = $more;
298	$self->{count}++;
299
300	return 0;
301}
302
303sub packet {
304	my $self = shift;
305	return ($self->{length_orig},
306		$self->{length_inc},
307		$self->{drops},
308		$self->{seconds},
309		$self->{msecs},
310		$self->{more},
311		$self->{data});
312}
313
314sub linktype {
315	my $self = shift;
316	return sprintf("%u",$self->{linktype});
317}
318
319#
320# USBMON-specific code, written by Mauro Carvalho Chehab
321#
322
323my @pending;
324
325my $initial_time;
326my $last_time;
327
328sub print_frame($$)
329{
330	my %req = %{ $_[0] };
331	my %resp = %{ $_[1] };
332
333	if (!$initial_time) {
334		$initial_time = $req{"Time"};
335		$last_time = $initial_time;
336	}
337
338	# Print timestamps:
339	#	relative time from resp 1
340	#	relative time from last resp
341	#	time to complete
342	printf "%09d ms %06d ms (%06d us",
343		1000 * ($req{"Time"} - $initial_time) + 0.5,
344		1000 * ($req{"Time"} - $last_time) + 0.5,
345		($resp{"Time"} - $req{"Time"}) * 1000000 + 0.5;
346	$last_time = $req{"Time"};
347
348	printf " EP=%02x, Dev=%02x)", $resp{'Endpoint'}, $resp{'Device'};
349
350	my ($app_data, $type);
351
352	if ($req{"Endpoint"} == 0x80 || $req{"SetupFlag"} == 0) {
353		$app_data = substr($req{"Payload"}, 0, 8 * 2);
354		$type = hex(substr($app_data, 0, 2));
355		while ($app_data ne "") {
356			printf " %s", substr($app_data, 0, 2);
357			$app_data = substr($app_data, 2);
358		}
359	}
360
361	# Extra data
362	if ($resp{TransferType} == 2 || $resp{"Endpoint"} != 0x80) {
363		if ($type > 128) {
364			printf " <<<";
365		} else {
366			printf " >>>";
367		}
368	} else {
369		if ($resp{Endpoint} < 0x80) {
370			print " <<<";
371		} else {
372			print " >>>";
373		}
374	}
375
376	$app_data = substr($req{"Payload"}, 24 * 2);
377	while ($app_data ne "") {
378		printf " %s", substr($app_data, 0, 2);
379		$app_data = substr($app_data, 2);
380	}
381
382	$app_data = substr($resp{"Payload"}, 24 * 2);
383	while ($app_data ne "") {
384		printf " %s", substr($app_data, 0, 2);
385		$app_data = substr($app_data, 2);
386	}
387
388	printf " ERROR %d",$resp{"Status"} if ($resp{"Status"});
389
390	print "\n";
391
392	if ($debug & 1) {
393		my ($key, $value);
394		print "\tREQ:  $key => $value\n" while (($key, $value) = each(%req));
395		print "\tRESP: $key => $value\n" while (($key, $value) = each(%resp));
396		print "\n";
397	}
398
399	return;
400}
401
402my %frametype = (
403	0 => "ISOC",
404	1 => "Interrupt",
405	2 => "Control",
406	3 => "Bulk",
407);
408
409sub process_frame($) {
410	my %frame = %{ $_[0] };
411
412	if ($debug & 2) {
413		my ($key, $value);
414		print "PARSED data:\n";
415		print "\t\tRAW: $key => $value\n" while (($key, $value) = each(%frame));
416		print "\n";
417	}
418
419	if ($frame{"Status"} eq "-115") {
420		push @pending, \%frame;
421		return;
422	}
423
424	# Seek for operation origin
425	my $related = $frame{"ID"};
426	if (!$related) {
427		print "URB %s incomplete\n", $frame{"ID"};
428		return;
429	}
430	for (my $i = 0; $i < scalar(@pending); $i++) {
431		if ($related eq $pending[$i]{"ID"} && $frame{'Device'} eq $pending[$i]{'Device'}) {
432			my %req = %{$pending[$i]};
433
434# skip unwanted URBs
435			if (scalar @usbdev == 0 or exists($devs{$frame{'Device'}})) {
436				if ($frame_processor) {
437					require $frame_processor;
438					frame_processor(\%req, \%frame);
439				} else {
440					print_frame(\%req, \%frame);
441				}
442			}
443
444			# Remove from array, as it were already used
445			splice(@pending, $i, 1);
446			return;
447		}
448	}
449	printf "URB %s incomplete: Couldn't find related URB\n", $related;
450	return;
451}
452
453# Decode an USB header mapped frame. The frame is defined at libpcap as:
454#
455#typedef struct _usb_header_mmapped {
456#	u_int64_t id;
457#	u_int8_t event_type;
458#	u_int8_t transfer_type;
459#	u_int8_t endpoint_number;
460#	u_int8_t device_address;
461#	u_int16_t bus_id;
462#	char setup_flag;/*if !=0 the urb setup header is not present*/
463#	char data_flag; /*if !=0 no urb data is present*/
464#	int64_t ts_sec;
465#	int32_t ts_usec;
466#	int32_t status;
467#	u_int32_t urb_len;
468#	u_int32_t data_len; /* amount of urb data really present in this event*/
469#	union {
470#		pcap_usb_setup setup;
471#		iso_rec iso;
472#	} s;
473#	int32_t	interval;	/* for Interrupt and Isochronous events */
474#	int32_t start_frame;	/* for Isochronous events */
475#	u_int32_t xfer_flags;	/* copy of URB's transfer flags */
476#	u_int32_t ndesc;	/* number of isochronous descriptors */
477#} pcap_usb_header_mmapped;
478sub decode_frame($) {
479	my $strdata = shift;
480	my %frame;
481
482	if ($debug & 4) {
483		print "RAW DATA: ";
484		for (my $i = 0; $i < length($strdata); $i++) {
485			printf " %02x", ord(substr($strdata, $i, 1));
486		}
487		print "\n";
488	}
489
490	my ($frame_id, $tsSecHigh, $tsSecLow, $tsUsec);
491
492	($frame_id, $frame{"Type"}, $frame{"TransferType"},
493		$frame{"Endpoint"}, $frame{"Device"}, $frame{"BusID"},
494		$frame{"SetupFlag"}, $frame{"DataFlag"},
495		$tsSecHigh, $tsSecLow, $tsUsec,
496		$frame{"Status"}, $frame{"URBLength"},
497		$frame{"DataLength"}) = unpack("A8CCCCvCCVVVlVV", $strdata);
498	$frame{"ID"} = "0x";
499	for (my $i = 7; $i >= 0; $i--) {
500		$frame{"ID"} .= sprintf "%02x", ord(substr($frame_id, $i, 1));
501	}
502	$frame{"Type"} = chr($frame{"Type"});
503	$frame{"ArrivalTime"} = sprintf "%d.%06d", $tsSecHigh << 32 | $tsSecLow, $tsUsec;
504
505	my $payload;
506	my $payload_size;
507	for (my $i = 40; $i < length($strdata); $i++) {
508		$payload .= sprintf "%02x", ord(substr($strdata, $i, 1));
509		$payload_size++;
510	}
511	$frame{"Payload"} = $payload;
512	$frame{"PayloadSize"} = $payload_size;
513
514	return %frame;
515}
516
517sub parse_file($$)
518{
519	my $log = shift;
520	my $fh = shift;
521
522	while ($log->get_packet($fh) == 0) {
523		my ($length_orig,$length_incl,$drops,$secs,$msecs,$more,$strdata) = $log->packet();
524		my %frame = decode_frame($strdata);
525		$frame{"Time"} = sprintf "%d.%06d", $secs,$msecs;
526		my $s;
527		for (my $i = 0; $i < length($more); $i++) {
528			$s .= sprintf "%02x", ord(substr($more, $i, 1));
529		}
530		$frame{"More"} = $s;
531
532		process_frame(\%frame);
533	}
534}
535
536sub handle_pcap_packet($$$)
537{
538	my $user_data = $_[0];
539	my %hdr = %{ $_[1] };
540	my $strdata = $_[2];
541
542	my %frame = decode_frame($strdata);
543	$frame{"Time"} = sprintf "%d.%06d", $hdr{tv_sec}, $hdr{tv_usec};
544	process_frame(\%frame);
545}
546
547my $pcap_descr;
548sub sigint_handler {
549	# Close pcap gracefully after CTRL/C
550	if ($pcap_descr) {
551		Net::Pcap::close($pcap_descr);
552		print "End of capture.\n";
553		exit(0);
554	}
555}
556#
557# Ancillary routine to list what's connected to each USB port
558#
559
560if ($list_devices) {
561	my ($bus, $dev, $name, $usb, $lastname);
562
563	open IN, "/sys/kernel/debug/usb/devices";
564	while (<IN>) {
565		if (m/T:\s+Bus=(\d+).*Dev\#\=\s*(\d+)/) {
566			$bus = $1 + 0;
567			$dev = $2 + 0;
568		}
569		if (m/S:\s+Product=(.*)/) {
570			$name = $1;
571		}
572		if (m/P:\s+Vendor=(\S+)\s+ProdID=(\S+)\s+Rev=(\S+)/) {
573			$usb = "($1:$2 rev $3)";
574		}
575		if ($name && m/^$/) {
576			printf("For %-36s%-22s ==> $0 --device usbmon%s --usbdev %s\n", $name, $usb, $bus, $dev);
577			$lastname = $name;
578		}
579	}
580	printf("For %-36s%-22s ==> $0 --device usbmon%s --usbdev %s\n", $name, $usb, $bus, $dev) if ($lastname ne $name);
581
582	exit;
583}
584
585# Main program, reading from a file. A small change is needed to allow it to
586# accept a pipe
587
588if (!$pcap) {
589	my $fh;
590	if (!$filename) {
591		$fh = *STDIN;
592	} else {
593		open $fh, "<$filename" || die "ERROR: Can't read log $filename: $!\n";
594	}
595	binmode $fh;
596
597	my $log = new();
598	$log->get_header($fh);
599
600	# Check for LINKTYPE_USB_LINUX_MMAPPED (220)
601	if ($log->linktype() != 220) {
602		printf"Link type %d\n", $log->linktype();
603		die "ERROR: Link type is not USB";
604	}
605
606	parse_file $log, $fh;
607	close $fh;
608} else {
609	my $err;
610
611	$pcap_descr = Net::Pcap::open_live($device, 65535, 0, 1000, \$err);
612	die $err if ($err);
613
614	# Trap  signals to exit nicely
615	$SIG{HUP} = \&sigint_handler;
616	$SIG{INT} = \&sigint_handler;
617	$SIG{QUIT} = \&sigint_handler;
618	$SIG{TERM} = \&sigint_handler;
619
620	my $dl = Net::Pcap::datalink($pcap_descr);
621	if ($dl != 220) {
622		printf"Link type %d\n", $dl;
623		die "ERROR: Link type is not USB";
624	}
625
626	Net::Pcap::loop($pcap_descr, -1, \&handle_pcap_packet, '');
627	Net::Pcap::close($pcap_descr);
628	die $err;
629}
630
631__END__
632
633=head1 NAME
634
635parse_tcpdump_log.pl - Parses a tcpdump log captured via usbmon.
636
637=head1 SYNOPSIS
638
639parse_tcpdump_log.pl [options] [file ...]
640
641Options:
642
643	--help			brief help message
644
645	--man			full documentation
646
647	--debug [log level]	enables debug
648
649	--pcap			enables pcap capture
650
651	--device [usbmon dev]	allow changing the usbmon device (default: usbmon1)
652
653	--usbdev [usbdev id]    filter only traffic for a specific device
654
655	--list-devices          list the available USB devices for each usbmon port
656
657	--frame_processor [file] have this script call the function frame_processor of the script in file instead of printing.
658
659=head1 OPTIONS
660
661=over 8
662
663=item B<--help>
664
665Print a brief help message and exits.
666
667=item B<--man>
668
669Prints the manual page and exits.
670
671=item B<--debug> [log level]
672
673Changes the debug log level. The available levels are:
674
675	1 - frame request and frame response
676
677	2 - parsed frames
678
679	4 - raw data
680
681=item B<--pcap>
682
683Enables the capture from the usbmon directly, using Net::Pcap. For this
684to work, the kernel should be compiled with CONFIG_USB_MON, and the driver
685needs to be loaded, if compiled as a module.
686
687=item B<--device>
688
689Enables the capture from the usbmon directly, using Net::Pcap, using an
690interface different than usbmon1. It should be noticed, however, that the
691only datalink that this script can parse is the one provided by usbmon,
692e. g. datalink equal to 220 (LINKTYPE_USB_LINUX_MMAPPED).
693
694=item B<--list-devices>
695
696Lists all connected USB devices, and the associated usbmon device.
697
698=item B<--usbdev [id]>
699
700Filter traffic with given usbdev-id. By default no filtering is done
701and usbdev is -1.
702
703=item B<--frame_processor [perl-script]>
704
705Provide this option with a filename to a perl-script which contains a function
706frame_processor and instead of having the USB-frames printed to the screen
707you can process them programmatically. See print_frame for an example. This
708option exists to avoid the reparsing of the output generated by this script
709for analyzing.
710
711=back
712
713=head1 DESCRIPTION
714
715B<parse_tcpdump_log.pl> will parse a tcpdump log captured via usbmon.
716
717A typical usage is to do a real time capture and parser with:
718
719	# parse_tcpdump_log.pl --pcap
720
721Alternatively, it may work with an offline capture. In this case, the
722capture should be done with:
723
724	# tcpdump -i usbmon1 -w usb_device.tcpdump
725
726And the file will be parsed it with:
727
728	$ parse_tcpdump_log.pl usb_device.tcpdump
729
730It is also possible to parse a file via pipe, like:
731
732	$ cat usb_device.tcpdump | parse_tcpdump_log.pl
733
734=head1 DUMP OUTPUT FORMAT:
735
736The output of the script looks like:
737
738 000000000 ms 000000 ms (000127 us EP=80) 80 06 00 01 00 00 28 00 >>> 12 01 00 02 00 00 00 40 40 20 13 65 10 01 00 01 02 01
739 000000000 ms 000000 ms (000002 us EP=80) 80 06 00 01 00 00 28 00 >>> 12 01 00 02 09 00 00 40 6b 1d 02 00 06 02 03 02 01 01
740 000000006 ms 000005 ms (000239 us EP=80) c0 00 00 00 45 00 03 00 <<< 00 00 10
741 000001006 ms 001000 ms (000112 us EP=80) c0 00 00 00 45 00 03 00 <<< 00 00 10
742 000001106 ms 000100 ms (000150 us EP=80) c0 00 00 00 45 00 03 00 <<< 00 00 10
743
744The provided info are:
745
746    - Time from the script start;
747    - Time from the last transaction;
748    - Time between URB send request and URB response;
749    - Endpoint for the transfer;
750    - 8 bytes with the following URB fields:
751	- Type (1 byte);
752	- Request (1 byte);
753	- wValue (2 bytes);
754	- wIndex (2 bytes);
755	- wLength (2 bytes);
756    - URB direction:
757	>>> - To URB device
758	<<< - To host
759    - Optional data (length is given by wLength).
760
761=head1 BUGS
762
763Report bugs to Mauro Carvalho Chehab <mchehab@kernel.org>
764
765=head1 COPYRIGHT
766
767Copyright (c) 2011-2012 by Mauro Carvalho Chehab.
768
769License GPLv2: GNU GPL version 2 <http://gnu.org/licenses/gpl.html>.
770
771This is free software: you are free to change and redistribute it.
772There is NO WARRANTY, to the extent permitted by law.
773
774=cut
775