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