#!/bin/sh

# Ethernet Module Configuration Tool
# Copyright 2007 Meru Networks

# This script is responsible for building the eth_intf_cfg module parameter for the e1000.o
# Intel network interface driver.
#
# The eth_intf_cfg parameter is designed to specify exactly which PCI network devices will
# be assigned to which network interface names.  If the eth_intf_cfg parameter is passed,
# a device *not* on the list will *not* be initialized as a network interface.
#
# This script is required for platforms such as the AT8020 which implement a variable
# number of Intel devices depending on the hardware configuration.  Since we need to peg
# the AMC-based ports to specific network interfaces, this parameter is required for a
# consistent mapping between network interface name and physical port.
# 
# Different platforms have different configurations.  The first parameter to this script
# should specify the target platorm.  The case statement at the end of this file will break 
# the argument out into the proper sub-function.
#
# The format of the eth_intf_cfg parameter is:
#
# eth_intf_cfg=<dev0_pci_bus>.<dev0_pci_slot>.<dev0_pci_func>:<dev0_iface_name>;<dev1_pci_bus>...
#
# Delimiters:
#   .   Between the PCI bus/slot/func parameters
#   :   Between the PCI address and the desired network interface name
#   ;   Between successive devices
#
# Example: Dual SFP card on MC5000:
#
# 1. The script is called as `/opt/meru/bin/ethmodcfg MC5000`
# 2. The script diverts the call to the config_mc5000 function
# 3. The INTEL_DEVICES variable scans /proc/bus/pci/devices for any matching
#    e1000 devices (vendor=8086, device=1xxx).
# 4. Each device is sent through the breakout_pci_loc function to get individual
#    PCI parameters.
# 5. The highest PCI bus encountered is kept track of as HIGHEST_BUS
# 6. The slot value of the device with the highest bus is kept track of as HIGHEST_SLOT
# 7. Verify that the bus is greater-or-equal to the AMC bus slot index (to verify that
#    it is really an AMC card).
# 8. Set up loop variables.  In this case, we use the default of 1 bus and 2 interfaces
#    per bus (since we didn't detect the 4-port AMC card).
# 9. Enumerate through the bus/interface counters and call add_iface to append each device
#    to the eth_intf_cfg string.
# 10. Echo the eth_intf_cfg string to stdout
#

ETHMOD_PARAM=eth_intf_cfg

PCI_DEVICES=`cat /proc/bus/pci/devices`

#Any e1000 AMC card uses a bus >= than this
MC5000_AMC_BUS_START=7
#MC5000 base interface name
MC5000_BASE_NAME=eth
MC5000_BASE_INDEX=6 

MOD_STRING="${ETHMOD_PARAM}="

# --------------- GENERAL FUNCTIONS --------------------------- #

# This function converts the hex value output by /proc/bus/pci/devices into
# The variables PCIBUSNUM, PCISLOTNUM and PCIFUNCNUM.
breakout_pci_loc()
{
	#Extract PCI location info from the devices line
	ARG=$1
 	PCILOC=${ARG:0:4}
	let pcilochex=0x"$PCILOC"

	#Extract bus number
	PCIBUSNUM=`expr $pcilochex / 256`
	modulo=`expr $PCIBUSNUM \* 256`
	slot_func=`expr $pcilochex - $modulo`

	#Extract slot number
	PCISLOTNUM=`expr $slot_func / 8`
	modulo=`expr $PCISLOTNUM \* 8`

	#Extract function number
	PCIFUNCNUM=`expr $slot_func - $modulo`
}

#$1 = bus
#$2 = slot
#$3 = func
#$4 = iface name
add_iface()
{
	TMP="${MOD_STRING}${1}.${2}.${3}:${4};"
	MOD_STRING=$TMP
}

# --------------- CONFIGURATION FOR MC1500 -------------------- #

# Nothing fancy needed here, since the config is static. 
# We just want to reverse the ports. 

config_mc1500()
{
        echo "\"eth_intf_cfg=2.0.0:eth0;3.0.0:eth1;\""
}

# --------------- CONFIGURATION FOR MC5000 -------------------- #

# This function checks the presence of any AMC-based E1000 modules.
#
# The "AMC" aspect is checked based on the PCI bus.  The AMC slots begin
# at bus 7, and increment upwards.
#
# The "E1000" aspect is checked based on the device+vendor ID.  The vendor portion
# should equal 8086 and the device ID should begin with a 1 (ref: grep "80861" below)
#
# There is an additional check for the four-port AM4301 based on the presence of the
# PLX bridge, unique to that card.  If detected, the number of buses increases and
# we have to decrement the start bus based on the two-bus configuration of the AM4301.

config_mc5000()
{
	#--------------------------------------------------------------------#
	#This must be done for the MC5K to retain backwards compatibility with 3.4/3.5 licensing.
	#Eth0 must be pulled from the Ethernet port at PCI address 03:00.0
	add_iface 3 0 0 "${MC5000_BASE_NAME}0"
	#--------------------------------------------------------------------#

	INTEL_DEVICES=`echo "$PCI_DEVICES" | grep "80861"`

	HIGHEST_BUS=0
	HIGHEST_SLOT=0

	IFS=$'\012'
	#Get highest PCI bus that uses an e1000 device
	for dev in $INTEL_DEVICES; do
		breakout_pci_loc $dev
		if [ `expr $PCIBUSNUM \> $HIGHEST_BUS` -eq 1 ]; then
			HIGHEST_BUS=$PCIBUSNUM
			HIGHEST_SLOT=$PCISLOTNUM
		fi
	done

	IFS=$' \t\n'

	#If we haven't exceed the AMC bus number, we have no AMC card.. no special config!
	if [ $HIGHEST_BUS -lt $MC5000_AMC_BUS_START ]; then
		#Print mod string before exiting		
		echo "\"$MOD_STRING\""
		exit 0
	fi

	#Setup default enumeration
	NUM_IFACES_PER_BUS=2
	NUM_BUSES=1
	START_BUS=$HIGHEST_BUS
	#Check for PLX bridge -> 4 port AMC card
	echo "$PCI_DEVICES" | grep "10b5" 2>&1 > /dev/null && {
 		NUM_BUSES=2
		START_BUS=`expr $START_BUS - 1`
	}

	#Enumerate the interfaces
	CURINDEX=$MC5000_BASE_INDEX
	for (( bus = 0; bus < NUM_BUSES; bus++ ))
	do
        CURBUS=`expr $START_BUS + $bus`

		for (( func = 0; func < NUM_IFACES_PER_BUS; func++ ))
		do
			add_iface $CURBUS $HIGHEST_SLOT $func "${MC5000_BASE_NAME}${CURINDEX}"
			CURINDEX=`expr $CURINDEX + 1`
		done

	done

	echo "\"$MOD_STRING\""
}

# -------------- INTERPRET COMMAND LINE ARGUMENTS ------------- #

case "$1" in
    "MC1500" )
		config_mc1500
		;;
    "MC5000" )
		config_mc5000
		;;
    *)
		exit 0
		;;
	

esac
