#!/usr/bin/perl
# tlp-usblist - list usb device info with autosuspend attributes
#
# Copyright (c) 2013 Thomas Koch <linrunner at gmx.net>
# This software is licensed under the GPL v2 or later.


package tlp_usblist;
use strict;
use warnings;

my %usbdevices;
my $udev;

# Get sysfs value, return empty string if nonexistent or not readable 
sub catsysf {
    my $sysval = "";
    if (open SYSF, "$_[0]") {
        chomp ($sysval = <SYSF>);
        close SYSF;
    }
    return $sysval;
}

# Get list of drivers for usb device
sub usbdriverlist {
    my $driverlist = "";
    my $subdev;
    
    # Iterate subdevices
    foreach $subdev (glob "$_[0]/*:*") {
        if (-l "$subdev/driver") { 
            ( my $driver = readlink ("$subdev/driver") ) =~ s{.*/}{};
            
            if (index ($driverlist, $driver) == -1) {
                if ($driverlist) { $driverlist = $driverlist . ", " . $driver; } 
                else { $driverlist = $driver; }
            } # if index
        } # if $subdev/driver
    } # foreach $subdev
    
    if (! $driverlist) { $driverlist = "no driver"; }
    return $driverlist 
}

# Read usb device tree attributes as arrays into %usbdevices hash, indexed by Bus_Device
foreach $udev (grep { ! /:/ } glob "/sys/bus/usb/devices/*") {
    my $asf = "autosuspend_delay_ms";
    my $asv = catsysf ("$udev/power/$asf");
    
    if (length ($asv) == 0) { 
        # autosuspend_delay_ms nonexistent or not readable, try autosuspend (deprecated)
        $asf = "autosuspend"; 
        $asv = catsysf ("$udev/power/$asf");
    }
    
    if (length ($asv) > 0) { 
        my $cnf = "control";
        my $cnv = catsysf ("$udev/power/$cnf");
        if (! $cnv) {
            $cnf = "level"; # level is deprecated
            $cnv = catsysf ("$udev/power/$cnf");
        }
        my $usbk = sprintf ("%03d_%03d", 
                            catsysf ("$udev/busnum"),
                            catsysf ("$udev/devnum") );
        my $usbv;
        if ($asf eq "autosuspend") {
            $usbv = sprintf ("%s = %-5s %s = %2d", $cnf, $cnv . ",", $asf, $asv);
        } else {
            $usbv = sprintf ("%s = %-5s %s = %5d", $cnf, $cnv . ",", $asf, $asv);
        }
        @{$usbdevices{$usbk}} = ($udev, $usbv);
    }
}

# Output device list with attributes and drivers
foreach (`lsusb`) {
    my ($bus, $dev, $usbid, $desc) = /Bus (\S+) Device (\S+): ID (\S+)[ ]+(.*)/;
    my $usbk = $bus . "_" . $dev;
    $desc ||= "<unknown>";
    print "Bus $bus Device $dev ID $usbid $usbdevices{$usbk}[1] -- $desc (" 
        . usbdriverlist ($usbdevices{$usbk}[0]) . ")\n";
}


