#!/usr/bin/perl
#
# Name: adapter_info 
# Copyright: (c)Copyright 2008 Hewlett-Packard Development Company, L.P.
#
# Description: Print information for fibre channel HBAs
#  
# Modification History
#
# Chad Dupuis   	08/11/08 Initial Development
# Chad Dupuis		07/13/09 Added support for Brocade HBAs
# Chad dupuis		07/24/09 Fixed QLogic port state output on RHEL 5.3
# Stefan Stechemesser	11/19/09 lspci -D to support domains other than 0.
# Keith Wortman		02/25/11 Minor corrections for Emulex FCoE CNA's     	
# Keith Wortman         03/28/11 Corrected behavior with Brocade 1-port devices
# Keith Wortman		04/12/11 Corrected display of Emulex CNA info 
# Shreys Roy Chowdhury  12/17/12 Corrected link state for QLogic HBA

use Getopt::Long;

#
# Defines
#

$debug = 0;
$help_flag = 0;
$verbose_info_flag = 0;
$pciid_info_flag = 0;
$device_info=-1;
$lspcicmd = "lspci -D";
$broadcom_lspci_str= "FastLinQ";
$sysfs_pci_dir = "/sys/bus/pci/devices/";
$lspcicmd_br="lspci -vvns";
$sysfs_scsi_host_dir = "/sys/class/scsi_host";
$FALSE = 0;
$TRUE = 1;
$GOOD = 0;
$BAD = 1;

#
# Functions
#

# Name: Debug
# Description: Prints a debug statement
# In: func - Function name
#     message - Message to print
# Out: None
# Returns: None
sub Debug {
	my $func = $_[0];
	my $message = $_[1];

	if ($debug) {
		print "$func(): $message\n";
	}
}

# Name: ParseArguments
# Description: Parses the arguments passed at the command line
# In: None
# Out: basic_info_flag, verbose_info_flag, help_flag, pciid_flag, model_info_flag,
#      lun_info_flag and remote_port_info_flag filled in
# Returns: None
sub ParseArguments {
	# Use GetOptions to parse all the command line arguments.  If result is still set to
	# FALSE after the GetOptions call then an error occured to exit out of the script
	
	my $result = $FALSE;
	$result = GetOptions ('h' => \$help_flag, '--help' =>\$help_flag, 'v' => \$verbose_info_flag, 'verbose' =>\$verbose_info_flag, 'l' => \$lun_info_flag, 'luns' => \$lun_info_flag, 'r' => \$remote_port_info_flag, 'remoteports' => \$remote_port_info_flag, 'p' => \$pciid_info_flag, 'pciids' => \$pciid_info_flag, 'm' => \$model_info_flag, 'model' => \$model_info_flag, 'i' => \$version_info_flag, 'versioninfo' => \$version_info_flag, 'd=i' => \$device_info, 'device=i' => \$device_info);
	
	Debug ("ParseArguments", "result=\"".$result."\"\n");
	
	if ($result == $false) {
		exit ($BAD);
	}
	
	#
	# Argument post processing
	
	# If the help flag was specified simply print the help message and exist
	
	if ($help_flag) {
		PrintHelp();
	}
	
	# If the verbose flag was specified make sure that all other info flags are 0
	
	if ($verbose_info_flag) {
		$basic_info_flag = $FALSE;
		$pciid_info_flag = $FALSE;
		$model_info_flag = $FALSE;
	}
	
	Debug ("ParseArguments", "basic_info_flag=".$basic_info_flag.",verbose_info_flag=".$verbose_info_flag.",pciid_info_flag=".$pciid_info_flag.",model_info_flag=".$model_info_flag.",lun_info_flag=".$lun_info_flag.",remote_port_info_flag=".$remote_port_info_flag.",version_info_flag=".$version_info_flag);	
}

# Name: GetSysfsAttr
# Description: Gets a sysfs attribute from a sysfs attribute node
# In: attrfile - Attribute file to get
# Out: None
# Returns: Contents of sysfs node
sub GetSysfsAttr {
	my $attrfile = $_[0];
	my $contents = "";
	
	Debug ("GetsysfsAttr", "attribute to open is ".$attrfile);
	open (ATTRFILE, $attrfile) or return ("");
	
	$contents = <ATTRFILE>;
	Debug ("GetsysfsAttr", "attribute contents are ".$contents);

	close (ATTRFILE);
	
	chomp ($contents);
	return $contents;
}



# Name: GetVendorId
# Description: Returns the PCI vendor ID of the HBA
# In: @adapterid - PCI bus id
# Out: None
# Returns: VID
sub GetVendorId {
    my $adapterid = $_[0];
    my $vendor = GetSysfsAttr($sysfs_pci_dir.$adapterid."/vendor");
	Debug ("GetVendorId", "vendor is ".$vendor);
    return $vendor;
}

# Name: GetDeviceId
# Description: Returns the PCI device ID of the HBA
# In: @adapterid - PCI bus id
# Out: None
# Returns: DID
sub GetDeviceId {
	my $adapterid = $_[0];
	my $device = GetSysfsAttr($sysfs_pci_dir.$adapterid."/device");
    Debug ("GetDeviceId", "device is ".$device);
	return $device;
}

# Name: GetSubsystemVendorId
# Description: Returns the PCI subsystem vendor ID of the HBA
# In: @adapterid - PCI bus id
# Out: None
# Returns: SSVID
sub GetSubsystemVendorId {
    my $adapterid = $_[0];
    my $subsystem_vendor = GetSysfsAttr($sysfs_pci_dir.$adapterid."/subsystem_vendor");
	Debug ("GetsubsystemVendorId", "subsystem_vendor is ".$subsystem_vendor);
    return $subsystem_vendor;
}

# Name: GetSubsystemDeviceId
# Description: Returns the PCI subsystem device ID of the HBA
# In: @adapterid - PCI bus id fro lspci command
# Out: None
# Returns: SSDID
sub GetSubsystemDeviceId {
    my $adapterid = $_[0];
    my $subsystem_device = GetSysfsAttr($sysfs_pci_dir.$adapterid."/subsystem_device");
	Debug ("GetSubsystemDeviceId", "subsystem_device is ".$subsystem_device);
    return $subsystem_device;
}

# Name: IsBroadcom
# Description: Returns whether an HBA is a Brocade HBA
# In: adapter_id - PCI address of HBA
# Out: None
# Returns: TRUE is the HBA is a Brocade HBA, FALSE otherwise
sub IsBroadcom {
	my $adapter_id = $_[0];
	
	if (GetVendorId($adapter_id) eq "0x14E4") {
		Debug ("IsBrocade", "Returning TRUE");
		return ($TRUE);
	}
	else {
		Debug ("IsBrocade", "Returning FALSE");
		return ($FALSE);
	}
}

# Name: GetHbaVendorName
# Description: Returns the name of the current HBA vendor
# In: adapter_id - PCI address of the HBA
# Out: None
# Returns: HBA vendor name string
sub GetHbaVendorName {
	my $adapter_id = $_[0];
	my $rv = "";
	
	if (GetVendorId($adapter_id) eq "0x1077" || GetVendorId($adapter_id) eq "0x14E4" )  {
		$rv = "Broadcom";
	}
	elsif (GetVendorId($adapter_id) eq "0x10df") {
		$rv = "Emulex";
	}
	elsif (GetVendorId($adapter_id) eq "0x1657" ) {
		$rv = "Brocade";
	}

	elsif (GetVendorId($adapter_id) eq "0x103c" || GetVendorId($adapter_id) eq "0x0e11") {
		$rv = "HP";
	}
	else {
		$rv = "Unknown";
	}
	
	Debug ("GetHbaVendorName", "Returning \"".$rv."\"\n");
	return ($rv);
}

#Name: GetHbas
# Description: Get a list of the PCI addresses of HBAs in the system
# In: None
# Out: None
# Returns: List of PCI addresses of all HBAs in the system
sub GetHbas {
	my @HBAs;
	push (@HBAs, GetBroadcomHbas());
	return (@HBAs); 
}

# Name: GetBroadcomHbas
# Description: Get a list of the PCI addresses of all QLogic adapters in the system
# In: None
# Out: None
# Returns: List of PCI addresses of QLogic HBAs
sub GetBroadcomHbas {
	my $eh="Ethernet";
	my $broadcom ="FastLinQ";
	Debug ("GetBroadcomHbas", "Command is $lspcicmd | grep $broadcom_lspci_str | awk '{print \$1}'");

	$pci_addrs = `$lspcicmd | grep FastLinQ | grep  Ethernet | awk '{print \$1}'`;
	Debug ("GetBroadcomHbas", "pci_addrs = $pci_addrs");
	print "\n",$pci_addrs;

	return split ('\n', $pci_addrs);
}


# Name: GetNetPciAddr
# Description: Gets the PCI address of the comparable NIC device for a CNA
# In: FCoE device PCI address
# Out: None
# Returns: NIC device PCI addres
sub GetNetPciAddr {
	my $fcoe_pci_addr = $_[0];
	my @awk = split(/\./, $fcoe_pci_addr);
	my $base_addr = $awk[0];
	my $pci_func = $awk[1];
	
	Debug ("GetNetPciAddr", "fcoe_pci_addr=".$fcoe_pci_addr.",base_addr=".$base_addr.",pci_func=".$pci_func);

	# Subtract 2 from the pci function number to get the network function #
#	$pci_func = $pci_func - 2;

	Debug ("GetNetPciAddr", "Returning ".$base_addr.".".$pci_func);
	return ($base_addr.".".$pci_func);
}

# Name: GetEthNumber
# Description: Returns the ethX number based on the PCI address
# In: nic_pci_addr - The PCI address of the NIC function of a CNA
# Out: None
# Returns: The /sys/class/eth/ethX number of the CNA
  sub GetEthNumber {
	my $nic_pci_addr = $_[0];
	my $eth_number = `ls /sys/bus/pci/devices/$nic_pci_addr/net/ | sed 's/eth//g'`;
	return ($eth_number);
}

# Name: GetMacAddress
# Description: Returns the MAC address of a CNA
# In: eth_num - The ethernet adapter number from /sys/class/net
# Out: None
# Returns: MAC address of CNA
sub GetMacAddress {
	$eth_num = $_[0];
	chomp ($eth_num);
	return GetSysfsAttr ("/sys/class/net/eth".$eth_num."/address");
}

# Name: PrintHelp
# Description: Prints a help message
# In: None
# Out: None
# Returns: None
sub PrintHelp {
	print "NAME\n\n";
	print "adapter_info\n\n";
	print "DESCRIPTION\n\n";
	print "Prints information about Fibre Channel HBAs/CNAs.\n\n";
	print "OPTIONS\n\n";
	print "-d, --device      - Prints all information for a specific SCSI host adapter\n";
	print "-h, --help        - Prints this help message\n";
	print "-p, --pciids      - Prints the PCI IDs for all HBAs\n";
print "-v, --verbose     - Prints all information except device and LUN information \n";
	exit ($GOOD);
}

# Name: PrintPciIds
# Description: Prints the PCI IDs of the current HBA to stdout
# In: adapter_id - The PCI address of the HBA
# Out: None
# Returns: None
sub PrintPciIds {
	my $adapter_id = $_[0];
	#my $scsihostno = GetScsiHostNo ($adapter_id);
		
	print "     vid:               ".GetVendorId($adapter_id)."\n";
	print "     did:               ".GetDeviceId($adapter_id)."\n";
	print "     ssvid:             ".GetSubsystemVendorId($adapter_id)."\n";
	print "     ssdid:             ".GetSubsystemDeviceId($adapter_id)."\n";
}

# Name: PrintNetInformation
# Description: Prints the network information for a CNA
# In: adapter_id - PCI address of network adapter
# Out: None
# Returns: None
sub PrintNetInformation {
	my $adapter_id = $_[0];
	my $net_pci_addr = GetNetPciAddr($adapter);
	#print $net_pci_addr;
	Debug ("PrintNetInformation", "net_pci_addr=".$net_pci_addr);
		
	my $eth_num = GetEthNumber($net_pci_addr);
	Debug ("PrintNetInformation", "eth_num=".$eth_num);
	
	print "     ethernet device:   /sys/class/net/eth".$eth_num;
	print "     mac:               ".GetMacAddress($eth_num)."\n";
}



# Name: PrintAdapterInformation
# Description: Prints the adapter information for Broadcom adapter
# In: adapter_id - PCI address of network adapter
# Out: None
# Returns: None
sub PrintAdapterInformation {
	my $adapter_id = $_[0];
	my $start_id = "Vital Product Data";
	my $end_id = "End";
	#print $adapter_id;
	#my $adapter_details=`$lspcicmd_br$adapter_id|sed -n /Vital/,/^Read-only/p`;
	my $adapter_details= `$lspcicmd_br$adapter_id|awk '/Read-only/{e=0}/Vital Product Data/{gsub(".*Vital Product Data","");e=1}{if(e==1){print}}'`;
	print "     Adapter details:\n";
	print $adapter_details;	
	print "\n";
	PrintNetInformation($adapter_id);
	PrintOtherInformation($adapter_id);
}

# Name: PrintOtherInformation
# Description: Prints the adapter information for Broadcom adapter
# In: adapter_id - PCI address of network adapter
# Out: None
# Returns: None
sub PrintOtherInformation {
	my $adapter_id = $_[0];
	my $net_pci_addr = GetNetPciAddr($adapter);
        #print $net_pci_addr;
	Debug ("PrintNetInformation", "net_pci_addr=".$net_pci_addr);
		
	my $eth_num = GetEthNumber($net_pci_addr);
        #print $eth_num;
	chomp($eth_num);
	my $eth_dev ="eth".$eth_num;
	my $info = `ethtool -i $eth_dev`;
	print $info;	
	print "\n"
	#return ($info);
}




# Script Main
#

ParseArguments();
Debug ("main", "device_info=".$device_info);
foreach $adapter (GetHbas()) {
	Debug ("main", "Hba is ".$adapter);
	#print "adapter is",$adapter;
	#my $scsihostno = GetScsiHostNo($adapter);
	#Debug ("main", "scsihostnumber=".$scsihostno);

	# If a specific device was specified (i.e. device_info is greater than 0)
	# then check to see if the current device we are examining is the device
	# that we want to print information for.  If we do not have a match then
	# move on to the next adapter to examine.
	
	
	if ($verbose_info_flag) {
		PrintAdapterInformation($adapter);
	}
	else
	{
		     if ($pciid_info_flag) {
			PrintPciIds($adapter);
		}		
		
	}

	
}


