#!/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;
$basic_info_flag = 1;
$help_flag = 0;
$verbose_info_flag = 0;
$pciid_info_flag = 0;
$model_info_flag = 0;
$lun_info_flag = 0;
$remote_port_info_flag = 0;
$version_info_flag = 0;
$device_info=-1;
$lspcicmd = "lspci -D";
$qlogic_lspci_str = "QLogic";
$emulex_lspci_str = "Emulex";
$brocade_lspci_str = "Brocade";
$serverengines_lspci_str = "ServerEngines";
$sysfs_pci_dir = "/sys/bus/pci/devices/";
$sysfs_rport_dir = "";
$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: SetRportDir
# Description: Sets the rport directory for this adapter
# In: None
# Out: sysfs_rport_dir is set
# Returns: None
sub SetRportDir {
	my $scsihostno = $_[0];

	$sysfs_rport_dir = "/sys/class/scsi_host/host".$scsihostno."/device";
}

# Name: GetRportDir
# Description: Gets the rport directory for this adapter
# In: None
# Out: None
# Returns: sysfs rport directory for this scsi host adapter
sub GetRportDir {
	return $sysfs_rport_dir;
}

# Name: GetScsiHostNo
# Description: Retrieves the Scsi host number of an adapter
# In: @adapterid - PCI bus id 
# Out: None
# Returns: scsi host number
sub GetScsiHostNo {
    my $adapterid = $_[0];
    my $scsihostno = `ls $sysfs_pci_dir$adapterid | grep host |  sed \'s/host//g\'`;
	
	chomp ($scsihostno); 
	Debug ("GetScsiHostNo", "scsihostno is ".$scsihostno);

	# Set directories based on SCSI host number
	SetRportDir ($scsihostno);
    
    return $scsihostno;
}

# 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: IsQLogic
# Description: Returns whether an HBA is an QLogic HBA
# In: adapter_id - PCI address of HBA
# Out: None
# Returns: TRUE is the HBA is an QLogic HBA, FALSE otherwise
sub IsQLogic {
	my $adapter_id = $_[0];
	
	if (GetVendorId($adapter_id) eq "0x1077") {
		Debug ("IsQLogic", "Returning TRUE");
		return ($TRUE);
	}
	else {
		Debug ("IsQLogic", "Returning FALSE");
		return ($FALSE);
	}
}

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

# Name: IsBrocade
# 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 IsBrocade {
	my $adapter_id = $_[0];
	
	if (GetVendorId($adapter_id) eq "0x1657") {
		Debug ("IsBrocade", "Returning TRUE");
		return ($TRUE);
	}
	else {
		Debug ("IsBrocade", "Returning FALSE");
		return ($FALSE);
	}
}

# Name: IsServerengines
# Description: Returns whether an adapter is a Emulex/SE based CNA
# In: adapter_id - PCI address of CNA
# Out: None
# Returns: TRUE if the adapter is a Emulex/SE CNA, FALSE otherwise
sub IsServerengines {
	my $adapter_id = $_[0];
	
	if (GetVendorId($adapter_id) eq "0x19a2") {
		Debug ("IsServerengines", "Returning TRUE");
		return ($TRUE);
	}
	else {
		Debug ("IsServerengines", "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 (GetSubsystemVendorId($adapter_id) eq "0x1077") {
		$rv = "QLogic";
	}
	elsif (GetSubsystemVendorId($adapter_id) eq "0x10df") {
		$rv = "Emulex";
	}
	elsif (GetSubsystemVendorId($adapter_id) eq "0x1657" ) {
		$rv = "Brocade";
	}
	elsif (GetSubsystemVendorId($adapter_id) eq "0x103c" || GetSubsystemVendorId($adapter_id) eq "0x0e11") {
		$rv = "HP";
	}
	else {
		$rv = "Unknown";
	}
	
	Debug ("GetHbaVendorName", "Returning \"".$rv."\"\n");
	return ($rv);
}

# Name: GetQLogicHbas
# 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 GetQLogicHbas {
	Debug ("GetQLogicHbas", "Command is $lspcicmd | grep $qlogic_lspci_str | awk '{print \$1}'");

	$pci_addrs = `$lspcicmd | grep $qlogic_lspci_str | grep Fibre | awk '{print \$1}'`;
	Debug ("GetQLogicHbas", "pci_addrs = $pci_addrs");

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

# Name: GetEmulexHbas
# Description: Get a list of the PCI addresses of all Emulex adapters in the system
# In: None
# Out: None
# Returns: List of PCI addresses of Emulex HBAs
sub GetEmulexHbas {
	Debug ("GetEmulexHbas", "Command is $lspcicmd | grep $emulex_lspci_str | awk '{print \$1}'");

	$pci_addrs = `$lspcicmd | grep $emulex_lspci_str | grep -v $serverengines_lspci_str | grep Fibre | awk '{print \$1}'`;
	Debug ("GetEmulexHbas", "pci_addrs = $pci_addrs");

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

# Name: GetBrocadeHbas
# Description: Get a list of the PCI addresses of all Brocade adapters in the system
# In: None
# Out: None
# Returns: List of PCI addresses of Brocade HBAs
sub GetBrocadeHbas {
	Debug ("GetBrocadeHbas", "Command is $lspcicmd | grep $brocade_lspci_str | awk '{print \$1}'");

	$pci_addrs = `$lspcicmd | grep $brocade_lspci_str | awk '{print \$1}'`;
	Debug ("GetBrocadeHbas", "pci_addrs = $pci_addrs");

# For single-port devices, skip the phantom port (test for SCSI hosts)
        my @pci_addrs_1;
        my @pci_addrs_2;
        @pci_addrs_1 = split ('\n', $pci_addrs);

        foreach $pci_addrs (@pci_addrs_1) {
                if ((`ls $sysfs_pci_dir$pci_addrs | grep host`) ne "") {
                        push @pci_addrs_2, $pci_addrs;
                }
        }

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

# Name: GetEmulexCnas
# Description: Get a list of the PCI addresses of all Emulex CNA adapters in the system
# In: None
# Out: None
# Returns: List of PCI addresses of EmulexCNAs
sub GetEmulexCnas {
	Debug ("GetEmulexCnas", "Command is $lspcicmd | grep $serverengines_lspci_str | grep \"Fibre Channel\" | awk '{print \$1}'");

	$pci_addrs = `$lspcicmd | grep $serverengines_lspci_str | grep \"Fibre Channel\" | awk '{print \$1}'`;
	Debug ("GetBrocadeHbas", "pci_addrs = $pci_addrs");

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

# 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, GetQLogicHbas());
	push (@HBAs, GetEmulexHbas());
	push (@HBAs, GetEmulexCnas());
	push (@HBAs, GetBrocadeHbas());
	return (@HBAs); 
}

# Name: GetQLogicModelName
# Description: Gets the HBA model name from QLogic HBAs
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: Model string
sub GetQLogicModelName {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/model_name");	
}

# Name: GetEmulexModelName
# Description: Gets the HBA model name from Emulex HBAs
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: Model string
sub GetEmulexModelName {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/modelname");	
}

# Name: GetBrocadeModelName
# Description: Gets the HBA model name from Brocade HBAs
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: Model string
sub GetBrocadeModelName {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/model");	
}

# Name: GetHbaModelName
# Description: Gets the model name of the HBA
# In: adatper_id - PCI address of HBA
# Out: None
# Returns: Model name string of HBA
sub GetHbaModelName {
	my $adapter_id = $_[0];
	my $scsihostno = GetScsiHostNo($adapter_id);
	
	if (IsEmulex($adapter_id) || IsServerengines($adapter_id)) {
		return (GetEmulexModelName($scsihostno));
	}
	elsif (IsQLogic($adapter_id)) {
		return (GetQLogicModelName($scsihostno));
	}
	elsif (IsBrocade($adapter_id)) {
		return (GetBrocadeModelName($scsihostno));
	}	
	else {
		return ("");
	}
}  

# Name: GetQLogicHbaState
# Description: Gets the link state of the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: State string
sub GetQLogicHbaState {
	my $scsihostno = $_[0];
	
	if (-e "/sys/class/scsi_host/host".$scsihostno."/link_state") {
		return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/link_state");
	}
	else {
		 if (-e "/sys/class/scsi_host/host".$scsihostno."/state") {

			return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/state");
	}
	}
}

# Name: GetEmulexHbaState
# Description: Gets the link state of the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: State string
sub GetEmulexHbaState {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/fc_host/host".$scsihostno."/port_state");
}

# Name: GetBrocadeHbaState
# Description: Gets the link state of the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: State string
sub GetBrocadeHbaState {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/fc_host/host".$scsihostno."/port_state");
}

# Name: GetHbaState
# Description: Gets the model name of the HBA
# In: adatper_id - PCI address of HBA
# Out: None
# Returns: State of HBA
sub GetHbaState {
	my $adapter_id = $_[0];
	my $scsihostno = GetScsiHostNo($adapter_id);

	if (IsEmulex($adapter_id) || IsServerengines($adapter_id)) {
		return (GetEmulexHbaState($scsihostno));
	}
	elsif (IsQLogic($adapter_id)) {
		return (GetQLogicHbaState($scsihostno));
	}
	elsif (IsBrocade($adapter_id)) {
		return (GetBrocadeHbaState($scsihostno));
	}
	else {
		return ("");
	}
}  

# Name: GetQLogicDriverVersion
# Description: Gets the driver version that is running the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: Driver version string
sub GetQLogicDriverVersion {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/driver_version")
}

# Name: GetEmulexDriverVersion
# Description: Gets the driver version that is running the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: Driver version string
sub GetEmulexDriverVersion {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/lpfc_drvr_version")
}

# Name: GetBrocadeDriverVersion
# Description: Gets the driver version that is running the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: Driver version string
sub GetBrocadeDriverVersion {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/driver_version")
}

# Name: GetHbaDriverVersion
# Description: Gets the driver version of the HBA
# In: adatper_id - PCI address of HBA
# Out: None
# Returns: Driver version string of HBA
sub GetHbaDriverVersion {
	my $adapter_id = $_[0];
	my $scsihostno = GetScsiHostNo($adapter_id);
	
	if (IsEmulex($adapter_id) || IsServerengines($adapter_id)) {
		return (GetEmulexDriverVersion($scsihostno));
	}
	elsif (IsQLogic($adapter_id)) {
		return (GetQLogicDriverVersion($scsihostno));
	}
	elsif (IsBrocade($adapter_id)) {
		return (GetBrocadeDriverVersion($scsihostno));
	}
	else {
		return ("");
	}
}  

# Name: GetQLogicFirmwareVersion
# Description: Gets the firmware version that is running the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: Firmware version string
sub GetQLogicFirmwareVersion {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/fw_version")
}

# Name: GetEmulexFirmwareVersion
# Description: Gets the firmware version that is running the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: Firmware version string
sub GetEmulexFirmwareVersion {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/fwrev")
}

# Name: GetBrocadeFirmwareVersion
# Description: Gets the firmware version that is running the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: Firmware version string
sub GetBrocadeFirmwareVersion {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/scsi_host/host".$scsihostno."/firmware_version")
}

# Name: GetHbaFirmwareVersion
# Description: Gets the firmware versionof the HBA
# In: adatper_id - PCI address of HBA
# Out: None
# Returns: Firmware version string of HBA
sub GetHbaFirmwareVersion {
	my $adapter_id = $_[0];
	my $scsihostno = GetScsiHostNo($adapter_id);
	
	if (IsEmulex($adapter_id) || IsServerengines($adapter_id)) {
		return (GetEmulexFirmwareVersion($scsihostno));
	}
	elsif (IsQLogic($adapter_id)) {
		return (GetQLogicFirmwareVersion($scsihostno));
	}
	elsif (IsBrocade($adapter_id)) {
		return (GetBrocadeFirmwareVersion($scsihostno));
	}
	else {
		return ("");
	}
}  

# Name: GetHbaNodeName
# Description: Gets the world-wide node name for the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: World-wide node name string
sub GetHbaNodeName {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/fc_host/host".$scsihostno."/node_name");
}

# Name: GetHbaPortName
# Description: Gets the world-wide port name for the HBA
# In: scsihostno - SCSI host number of the HBA we're examining
# Out: None
# Returns: World-wide port name string
sub GetHbaPortName {
	my $scsihostno = $_[0];
	return GetSysfsAttr ("/sys/class/fc_host/host".$scsihostno."/port_name");
}

# Name: GetRports
# Description: Gets the rports for this HBA
# In: None
# Out: None
# Returns: Array of rport names
sub GetRports {
	my $cmdstr = "ls ".GetRportDir()." | grep rport"; 
	Debug ("GetRports", "cmdstr is $cmdstr");

	my $rport_str = `$cmdstr`;
	Debug ("GetRports", "rport_str is $rport_str");

	my @rports = split (/\n/, $rport_str);

	foreach $rport (@rports) {
		Debug ("HBA::GetRports",  "rport = $rport");
	}

	return @rports;
}

# Name: GetRportNodeData
# Description: Gets the information on a requested rport from sysfs
# In: rport - name of the rport on this HBA to look for
#     node - name of the node to get information from
# Out: None
# Returns: 
sub GetRportNodeData {
	my $rport_name = $_[0];
	my $rport_node = $_[1];
	my $rport_info_dir = GetRportDir()."/".$rport_name."/"."fc_remote_ports:".$rport_name;
	
	Debug ("GetRportNodeData", "rport_name = ".$rport_name);
	Debug ("GetRportNodeData", "rport_node = ".$rport_node);
	Debug ("GetRportNodeData", "rport_info_dir = ".$rport_info_dir);

	return (GetSysfsAttr ($rport_info_dir."/".$rport_node));
}

# Name: GetDevices
# Description: Returns a list of devices for a given rport
# In: rport - The rport to examine
# Out: None
# Returns: an array of devices where the devices are in the form of 
# "target_dir/device_address"
sub GetDevices {
	my $rport = $_[0];
	my $rport_dir = GetRportDir()."/".$rport;

	Debug ("GetDevices", "rport_dir=".$rport_dir);
		
	# Get a list of targets for this rport
	my $target_str = `ls $rport_dir | grep target`;
	Debug ("GetDevices", "target_str=".$target_str);
	
	my @targets = split (/\n/, $target_str);
	
	# Set up the array to contain all the devices for this rport
	my @total_devices = ();
	
	# Loop through each target
	
	foreach $target (@targets) {
        
        # Produce a list of devices
        
        Debug ("GetDevices", "target = ".$target);
        Debug ("GetDevices", "directory to look for devices is ".$rport_dir."/".$target);
        my $device_str = `ls $rport_dir/$target | grep [0-9]*:[0-9]*:[0-9]*:[0-9]*`;
        Debug ("GetDevices", "device_str=\"".$device_str."\"");
        my @devices = split (/\n/, $device_str);
		
		# Prepend the target string
		
		for ($i = 0; $i < @devices; $i++) {
			$devices[$i] = $target."/".$devices[$i];
		}	
			
		# Append the new devices to the total list
		push (@total_devices, @devices);
		
		if ($debug) {
        	foreach $device (@devices) {
                Debug ("GetDevices", "device = ".$device);
        	}
		}
	}        

	return @total_devices;  
}

# Name: GetDiskVendor
# Description: Returns the SCSI vendor id for a given device
# In: device - Directory in sysfs representing the device
# Out: None
# Returns: SCSI vendor id string
sub GetDiskVendor {
	my $device = $_[0];
	return GetSysfsAttr ($device."/vendor"); 
}

# Name: GetDiskModel
# Description: Returns the SCSI model id for a given device
# In: device - Directory in sysfs representing the device
# Out: None
# Returns: SCSI model id string
sub GetDiskModel {
	my $device = $_[0];
	return GetSysfsAttr ($device."/model"); 
}

# 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 "-i, --versioninfo - Prints driver version information for all HBAs\n";
	print "-l, --luns        - Prints the device information for all HBAs\n";
	print "-m, --model       - Prints the HBA model for all HBAs\n";
	print "-p, --pciids      - Prints the PCI IDs for all HBAs\n";
	print "-r, --remoteports - Prints the attached remote ports for all HBAs\n";
	print "-v, --verbose     - Prints all information except device and LUN information \n";
	exit ($GOOD);
}

# Name: PrintBasicInformation
# Description: Prints the sysfs location, wwnn and wwpn for a specific SCSI host
# In: adapter_id - PCI address of HBA
# Out: None
# Returns: None
sub PrintBasicInformation {
	my $adapter_id = $_[0];
	my $scsihostno = GetScsiHostNo ($adapter_id);
	
	print $sysfs_scsi_host_dir."/host".$scsihostno.": ";
	print "wwnn=".GetHbaNodeName($scsihostno)." ";
	print "wwpn=".GetHbaPortName($scsihostno)." ";
	print "state=".GetHbaState($adapter_id)."\n";
}

# 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: PrintModel
# Description: Prints the model name of the current HBA to stdout
# In: adapter_id - The PCI address of the HBA
# Out: None
# Returns: None
sub PrintModel {
	my $adapter_id = $_[0];
	my $scsihostno = GetScsiHostNo ($adapter_id);
	
	print "     vendor:            ".GetHbaVendorName($adapter_id)."\n";
	print "     model:             ".GetHbaModelName($adapter_id),"\n";		
}

# Name: PrintVersionInfo
# Description: Prints the driver and firmware version information of the current HBA to stdout
# In: adapter_id - The PCI address of the HBA
# Out: None
# Returns: None
sub PrintVersionInfo {
	my $adapter_id = $_[0];
	my $scsihostno = GetScsiHostNo ($adapter_id);

	print "     driver version:    ".GetHbaDriverVersion($adapter_id)."\n";
	print "     firmware version:  ".GetHbaFirmwareVersion($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);
	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: PrintVerboseInformation
# Description: Prints the following information
#	sysfs location
#	vendor
#	model
#   HBA state
#	world-wide node name
#	world-wide port name
#   driver version
#	firmware version
#	boot bios version
#   efi version
# In: adapter_id - PCI address of HBA
# Out: None
# Returns: None
sub PrintVerboseInformation {
	my $adapter_id = $_[0];
	my $scsihostno = GetScsiHostNo ($adapter_id);
	
	print $sysfs_scsi_host_dir."/host".$scsihostno.":\n";
	PrintModel ($adapter_id);
	print "     state:             ".GetHbaState($adapter_id)."\n";
	PrintPciIds($adapter_id);
	print "     wwnn:              ".GetHbaNodeName($scsihostno)."\n";
	print "     wwpn:              ".GetHbaPortName($scsihostno)."\n";
	PrintVersionInfo ($adapter_id);
	
#	if (IsServerengines($adapter_id)) {
#		PrintNetInformation ($adapter_id);
#	}
}

# Name: PrintRemotePorts
# Description: Prints the remote ports that are attached to a HBA
# In: adapter_id - PCI address of HBA
# Out: None
# Returns: None
sub PrintRemotePorts {
	my $adapter_id = $_[0];
	my $print_header = 0;
	
	# Sets the right Rport directory
	GetScsiHostNo ($adapter_id);
	
	# Loop through each rport found and print information
	
	foreach $rport (GetRports()) {
		Debug ("PrintRemotePorts", "rport = ".$rport);
		
		# Print the header if it has not been printed before.  Once it has been
		# printed, increment the state variable so it is not incremented again
		
		if ($print_header < 1) {
				print "\nRemote Ports\n";
				print "------------\n";
				$print_header++;				
		}
		
		my $node_name = GetRportNodeData ($rport, node_name);
		my $port_name = GetRportNodeData ($rport, port_name);
		my $roles = GetRportNodeData ($rport, roles);
		
		print "     $rport: wwnn=".$node_name." wwpn=".$port_name." role=".$roles."\n";	
	}
}

# Name: PrintDevices
# Description: Prints the devices that are attached to a HBA
# In: adapter_id - PCI address of HBA
# Out: None
# Returns: None
sub PrintDevices {
	my $adapter_id = $_[0];
	my $print_header = 0;
	
	# Sets the right Rport directory
	GetScsiHostNo ($adapter_id);
	
	# Loop through each rport, get the devices attached to that rport and then
	# print the SCSI model and vendor

	foreach $rport (GetRports()) {
		Debug ("main", "rport = ".$rport);
	
		# Print the header if it has not been printed before.  Once it has been
		# printed, increment the state variable so it is not incremented again
		
		if ($print_header < 1) {
				print "\nDevices\n";
				print "-------\n";
				$print_header++;				
		}
		
		foreach $device (GetDevices($rport)) {
			my $disk_vendor = GetDiskVendor (GetRportDir()."/".$rport."/".$device);
			my $disk_model = GetDiskModel (GetRportDir()."/".$rport."/".$device);
			$device =~ s/target[0-9]+:[0-9]+:[0-9]+\///;
			print "     ".$device."     ".$disk_vendor." ".$disk_model."\n";
		}
		
	}
}

#
# Script Main
#

ParseArguments();
Debug ("main", "device_info=".$device_info);

foreach $adapter (GetHbas()) {
	Debug ("main", "Hba 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 ($device_info > -1) {
		if ($scsihostno != $device_info) {
			next;
		}
	}
		
	if ($verbose_info_flag) {
		PrintVerboseInformation ($adapter);
	}
	else
	{
		PrintBasicInformation ($adapter);
		
		if ($pciid_info_flag) {
			PrintPciIds($adapter);
		}		
		
		if ($model_info_flag) {
			PrintModel ($adapter);
		}
		
		if ($version_info_flag) {
			PrintVersionInfo ($adapter);
		}
	}

	if ($lun_info_flag) {
		PrintDevices($adapter);
	}

	if ($remote_port_info_flag) {
		PrintRemotePorts ($adapter);
	}	
}


