# -*- shell-script -*-

# 00pci - Generic PCI-related functions.

# This file is part of the Linux lsvpd package.

# (C) Copyright IBM Corp. 2002, 2003, 2004

# Maintained by  Martin Schwenke <martins@au.ibm.com>

# 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; either version 2, or (at your option)
# any later version.
 
# 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.
 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
# $Id: 00pci,v 1.5 2004/05/20 08:05:25 martins Exp $

# This module is always loaded.
true || return 0

######################################################################

make_multiplexed pci_check_class

######################################################################

pci_check_class_DEFAULT ()
{
    return 1
}

pci_get_config_filename ()
{
    local pci_addr="$1"

    local d="/proc/bus/pci"

    local t="${pci_addr//://}" # Replace colons with slashes.
    case "$t" in
	(*/*/*) t="${t/\//:}" ;; # Put back first colon too many slashes.
    esac
    
    local conf="${d}/${t}"
    # Try removing domain if file doesn't exist.
    [ -f "$conf" ] || conf="${d}/${t#0000:}"

    [ -f "$conf" ] && echo "$conf"
}

pci_render_vpd ()
{
    local dir="$1"

    local i="${dir}/ibm,vpd"

    local vpd_subdirs
    vpd_subdirs_list_hook "$dir"

    if [ -f "$i" -a -z "$vpd_subdirs" ] ; then
	debug_cmd ibm_vpd_render "$i" "$dir"
	local fc
	do_fc_hook "$dir"
	vpd_subdirs_list_hook "$dir"
	local vpd_dir
	for vpd_dir in $vpd_subdirs ; do
	    vpd_field_override "$vpd_dir" "FC" "$fc"
	done
    fi
}

pci_do_pci22_vpd ()
{
    local pci_addr="$1"
    local dir="$2"

    local i="${dir}/ibm,vpd"
    local l="${dir}/linux,vpd"

    if [ ! -f "$i" -a ! -e "$l" ] ; then
	local conf=$(pci_get_config_filename "$pci_addr")
	if [ -f "$conf" ]  ; then
	    if debug_cmd pci_vpd_cap_grab "$conf" >"$i" ; then
		pci_render_vpd "$dir"
	    else
		rm -f "$i"
	    fi
	fi
    fi
}

######################################################################

# This group of functions is horrible.  However, under Linux 2.4, on
# big-bus-num machines (such as larger ppc64), PCI bus numbers are
# handled inconsistently and sometimes you only see the bottom 8 bits.
# This code does extra matching on the type and the IRQ.
pci_get_bus_dev_fun_irq_type_list ()
{
    local busdevfun class type t irq

    /sbin/lspci -nv |
    while read line ; do
	case "$line" in
	    *Class\ [0-9a-f][0-9a-f][0-9a-f][0-9a-f]:*)
		busdevfun="${line%% *}"
		class="${line#*Class }"
		class="${class%%:*}"
		for t in "scsi" "ethernet" "ide" ; do
		    if pci_check_class "$t" "$class" ; then
			type="$t"
			break
		    fi
		done
		;;
	    *IRQ\ [0-9]*)
		if [ -n "$busdevfun" ] ; then
		    irq="${line#*IRQ }"
		    irq="${irq%%,*}"
		    echo "${busdevfun}-${irq}-${type}"
		fi
		;;
	esac
    done
}

pci_hack_dom_bus_hook ()
{
    local type="$1"
    local unreal="$2"
    local ret

    # Sets: dom, bus
    dom=""
    bus=""

    unreal="${unreal}-${type}"

    # This is a cache variable, so should not be local!
    if [ -z "$pci_bus_dev_fun_irq_type_list" ] ; then
	pci_bus_dev_fun_irq_type_list=$(pci_get_bus_dev_fun_irq_type_list)
    fi

    local i n
    n=0
    for i in $pci_bus_dev_fun_irq_type_list ; do
	case "$i" in
	    (*${unreal}) bus="${bus} ${i%%:*}" ; n=$(($n + 1)) ;;
	esac
    done

    if [ $n -ne 1 ] ; then
	debug "pci_get_real_bus: bus number for \"${unreal}\" not found/unique"
	bus=""
	return 1
    fi

    bus=$(hex2dec $bus)
    dom=$(( ($bus >> 8) & 0xffffff ))
    bus=$(($bus & 0xff))
}

######################################################################

pci_set_mf_tm_hook ()
{
    # Uses: vendor, device, subvendor, subdevice
    # Sets: mf, tm

    # This is to workaround a broken adapter.  We're much more likely
    # to see the Ethernet adapter from AMD (1022) than the display
    # adapter from Trident (1023).
    if [ "$vendor" = "1023" -a "$device" = "2000" ] ; then
	vendor="1022"
    fi

    if [ -n "$vendor" -a -n "$device" ] ; then
	if [ -n "$subvendor" -a -n "$subdevice" ] ; then
	    tm=$(pci_lookup "${libdir}/pci.ids" \
		"$vendor" "$device" "$subvendor $subdevice")
	    if [ -n "$tm" ] ; then
		mf=$(pci_lookup "${libdir}/pci.ids" "$subvendor")
		[ -z "$mf" ] && mf="Unknown (0x${subvendor})"
	    fi
	fi

	if [ -z "$mf" -a -z "$tm" ] ; then
	    tm=$(pci_lookup "${libdir}/pci.ids" "$vendor" "$device")
	    mf=$(pci_lookup "${libdir}/pci.ids" "$vendor")
	fi

	[ -z "$mf" ] && mf="Unknown (0x${vendor})"
	[ -z "$tm" ] && tm="Unknown (0x${device})"
    fi
}

pci_parse_addr_hook ()
{
    local pci_addr="$1" # dom:bus:dev.fun

    # Sets: dom, bus, dev, fun

    local busdev="${pci_addr#*:}"
    busdev="${busdev%.*}"

    dom="0x${pci_addr%%:*}"
    bus="0x${busdev%:*}"
    dev="0x${busdev#*:}"
    fun="0x${pci_addr#*.}"
}
