#! /usr/bin/perl
# usbmondump script -- make usbmon dumps a bit more readable
# usage: usbmondump usbmon_file [ >usbdump_file or |$PAGER ]
# Copyright 2005 Randy Dunlap <rdunlap@xenotime.net>
# License: GPLv2

# Raw text data format
#
# Here is the list of words, from left to right:
# - URB Tag: normally a kernel mode address of the URB structure in hexadecimal;
# - Timestamp in microseconds, a decimal number;
# - Event Type. This type refers to the format of the event, not URB type.
#   Available types are: S - submission, C - callback, E - submission error.
# - Message (a composite word), it consists of three fields, separated
#   by colons: URB type and direction, Device address, Endpoint number.
#   Type and direction are encoded with two bytes in the following manner:
#     Ci Co   Control input and output
#     Zi Zo   Isochronous input and output
#     Ii Io   Interrupt input and output
#     Bi Bo   Bulk input and output
#   Device address and Endpoint number are decimal numbers with leading zeroes
#   or 3 and 2 positions, correspondingly.
# - URB Status. This field makes no sense for submissions, but is present
#   to help scripts with parsing. In error case, it contains the error code.
#   In case of a setup packet, it contains a Setup Tag ('s').
#   If scripts read a number in this field, they proceed to read Data Length.
#   Otherwise, they read the setup packet before reading the Data Length.
# - Setup packet, if present, consists of 5 words: 1 of each for bmRequestType,
#   bRequest, wValue, wIndex, wLength.
#   These words are safe to decode if Setup Tag was 's'. Otherwise, the setup
#   packet was present, but not captured, and the fields contain filler.
# - Data Length. This is the actual length in the URB.
# - Data tag. The usbmon may not always capture data, even if length is nonzero.
#   Only if tag is '=', the data words are present.
# - Data words follow, in big endian hexadecimal format.
#   The last word may contain from one to four bytes.
#   The length of collected data is limited and can be less than the data length
#   report in Data Length word.
#
# Examples:
#
# An input control transfer to get a port status:
# d5ea89a0 3575914555 S Ci:001:00 s a3 00 0000 0003 0004 4 <
# d5ea89a0 3575914560 C Ci:001:00 0 4 = 01050000
#
# An output bulk transfer to send a SCSI command 0x5E in a 31-byte Bulk wrapper
# to a storage device at address 5:
#
# dd65f0e8 4128379752 S Bo:005:02 -115 31 = 55534243 5e000000 00000000 00000600 00000000 00000000 00000000 000000
# dd65f0e8 4128379808 C Bo:005:02 0 31 >


# get input file name:
$INPUTNAME = $ARGV[0];

if (length ($INPUTNAME) == 0) {
        print "usage: usbmondump.pl usbmon_file\n\n";
        exit 1;
}

if (! open (INPUTNUM, "<$INPUTNAME")) {
        print "cannot open '$INPUTNAME'\n\n";
        exit 2;
}

$linecount = 0;
$heading1 = "URB..... time.....<tm_diff...>Ev Tp:Dev:Ep Sts Len = data";
$prev_tmstamp = 0;

LINE: while ($line = <INPUTNUM>)
{
	if (($linecount % 10) == 10) {
		print "$heading1\n";
	}

	chomp $line;
	$linecount++;
	##print "$line\n";

	# split into words
	# @words = split /\s+/, $line;
	($urb, $tmstamp, $event, $type_dev_ep, $status, $len, $data_tag, 
	 $rest) = split /\s+/, $line, 8;
	##print "rest: {$rest}\n";

	if ($prev_tmstamp == 0) {
		$prev_tmstamp = $tmstamp;
	}
	$tmsecs = $tmstamp / 1000000;
	$tmdiff = $tmstamp - $prev_tmstamp;
	$tmdiff_secs = $tmdiff / 1000000;

	if ($event eq "S") { # Submit
	    if ($status ne "s") { # not Setup
		print "$urb $tmsecs +$tmdiff_secs $event $type_dev_ep L=$len $data_tag $rest\n";
	    }
	    else { # Setup
		$bmReqType = $len;
		$bmRequest = $data_tag;
		($wValue, $wIndex, $wLength, $len) = split /\s+/, $rest;
		print "$urb $tmsecs +$tmdiff_secs $event $type_dev_ep [ReqType:$bmReqType Req:$bmRequest Val:$wValue Indx:$wIndex Len:$wLength] L=$len\n";

	    }
	}
	elsif ($event eq "C") { # Callback
		print "$urb $tmsecs +$tmdiff_secs $event $type_dev_ep St=$status L=$len $data_tag $rest\n";
	}
	else { # Error on Submit
		print "$urb $tmsecs +$tmdiff_secs $event $type_dev_ep St=$status L=$len $data_tag $rest\n";
	}

	$prev_tmstamp = $tmstamp;
} # end LINE

close (INPUTNUM);
