#!/usr/bin/perl use strict; # Copyright (C) 2014 Mauro Carvalho Chehab # Copyright (c) 2014 Samsung Electronics Co., Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, version 2 of the License. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # This small script parses USB dumps generated by several drivers, # decoding USB bits. # # To use it, do: # dmesg | ./parse_usb.pl # # Also, there are other utilities that produce similar outputs, and it # is not hard to parse some USB analyzers log into the expected format. # my %speed_map = ( 0x07 => "250KHz", 0x14 => "100KHz", 0x40 => "30KHz", 0x60 => "20KHz", ); sub type_req($) { my $reqtype = shift; my $s; if ($reqtype & 0x80) { $s = "RD "; } else { $s = "WR "; } if (($reqtype & 0x60) == 0x20) { $s .= "CLAS "; } elsif (($reqtype & 0x60) == 0x40) { $s .= "VEND "; } elsif (($reqtype & 0x60) == 0x60) { $s .= "RSVD "; } if (($reqtype & 0x1f) == 0x00) { $s .= "DEV "; } elsif (($reqtype & 0x1f) == 0x01) { $s .= "INT "; } elsif (($reqtype & 0x1f) == 0x02) { $s .= "EP "; } elsif (($reqtype & 0x1f) == 0x03) { $s .= "OTHER "; } elsif (($reqtype & 0x1f) == 0x04) { $s .= "PORT "; } elsif (($reqtype & 0x1f) == 0x05) { $s .= "RPIPE "; } else { $s .= sprintf "RECIP 0x%02x ", $reqtype & 0x1f; } $s =~ s/\s+$//; return $s; } my $i2c_speed = "unknown"; my $i2c_addr = "unknown"; my @i2c_sbuf; my @i2c_rbuf; my $i2c_hold; while (<>) { if (m/(.*)([0-9a-f].) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].) ([0-9a-f].)[\<\>\s]+(.*)/) { my $timestamp = $1; my $reqtype = hex($2); my $req = hex($3); my $val = hex("$5$4"); my $reg = hex("$7$6"); my $wlen = hex("$9$8"); my $payload = $10; $timestamp =~ s/^\s+//; $timestamp =~ s/\s+$//; if (($reqtype == 0x40) && ($reg == 0x02ff)) { # Start of an I2C tranfer @i2c_sbuf = (); @i2c_rbuf = (); next; } if (($reqtype == 0x40) && ($reg == 0x0202)) { # I2C speed $i2c_speed = sprintf "0x%02x", $val; $i2c_speed = $speed_map{$val} if defined($speed_map{$val}); next; } if (($reqtype == 0x40) && ($reg == 0x0203)) { # I2C addr $i2c_addr = $val >> 1; next; } if (($reqtype == 0x40) && ($reg == 0x0200)) { # I2C trigger $i2c_hold = ($val & 0x40) == 0x40; if (!$i2c_hold && ($val & 0x01)) { printf "$timestamp au0828 I2C write addr = 0x%02x (speed = %s) ", $i2c_addr, $i2c_speed; printf "0x%02x ", $_ foreach (@i2c_sbuf); print "\n"; @i2c_sbuf = (); } next; } if (($reqtype == 0xc0) && ($reg == 0x0201)) { # Wait to be ready next; } if (($reqtype == 0x40) && ($reg == 0x0205)) { # I2C write data push @i2c_sbuf, $val; next; } if (($reqtype == 0xc0) && ($reg == 0x0209)) { # I2C read data my @bytes = split(/ /, $payload); push @i2c_rbuf, hex($_) foreach(@bytes); if (!$i2c_hold) { printf "$timestamp au0828 I2C read addr = 0x%02x (speed = %s, len=%d) ", $i2c_addr, $i2c_speed, scalar(@i2c_rbuf); printf "0x%02x ", $_ foreach (@i2c_rbuf); print "\n"; @i2c_rbuf = (); } next; } printf("%s %s(0x%02x), Req 0x%02x, register 0x%04x, value 0x%04x, wlen %d: %s\n", $timestamp, type_req($reqtype), $reqtype, $req, $reg, $val, $wlen, $payload); } }