# -*- shell-script -*-

# 00pci - Generic PCI-related functions.

# This file is part of the Linux lsvpd package.

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

# 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.14 2005/05/12 04:57:07 martins Exp $

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

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

pci_check_class ()
{
    local type="$1"
    local class="$2"

    case "${type}-${class}" in
	(scsi-010[04])  return 0 ;;
	(scsi-0[Cc]04)  return 0 ;;
	(ethernet-0200) return 0 ;;
	(ide-0101)      return 0 ;;
	(usb-0[Cc]03)   return 0 ;;
	(display-0300)  return 0 ;;
	(serial-0700)   return 0 ;;
	(sound-0401)    return 0 ;;
	(unknown-ff00)  return 0 ;;
    esac

    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_get_vendor_id ()
{
    # Sets: vendor_id

    local adapter_bus_dir="$1"

    vendor_id="ffff"
}

pci_not_on_vendor_blacklist ()
{
    local pci_vendor_blacklist="$1"
    local adapter_bus_dir="$2"

    [ -f "$pci_vendor_blacklist" ] || return 0

    local vendor_id
    pci_get_vendor_id "$adapter_bus_dir"

    local v x
    # v contains only the 1st word on the line.
    while read v x ; do
	v="${v%%#*}"
	[ -n "$v" ] || continue
	[ "$v" = "$vendor_id" ] && return 1
    done < "$pci_vendor_blacklist"

    return 0
}

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

    local bl="${LSVPD_LIBDIR}/pci.vendor.blacklist"
    pci_not_on_vendor_blacklist "$bl" "$dir" || return

    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
	    # We're more interested in stopping concurrent access to
	    # the VPD capability area in PCI configuration space than
	    # locking the output file.  The former can cause real
	    # problems.
	    local lock="${db_state_dir}/pci22_${pci_addr}"
	    ensure_directory "$db_state_dir"
	    if lock_file "$lock" ; then
		if debug_cmd pci_vpd_cap_grab "$conf" >"${i}.$$" ; then
		    if mv -f "${i}.$$" "$i" ; then
			pci_render_vpd "$dir"
		    else
			rm -f "${i}.$$"
		    fi
		else
		    rm -f "${i}.$$"
		fi
		unlock_file "$lock"
	    fi
	fi
    fi
}

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

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 "${LSVPD_LIBDIR}/pci.ids" \
		"$vendor" "$device" "$subvendor $subdevice")
	    if [ -n "$tm" ] ; then
		mf=$(pci_lookup "${LSVPD_LIBDIR}/pci.ids" "$subvendor")
		[ -z "$mf" ] && mf="Unknown (0x${subvendor})"
	    fi
	fi

	if [ -z "$mf" -a -z "$tm" ] ; then
	    tm=$(pci_lookup "${LSVPD_LIBDIR}/pci.ids" "$vendor" "$device")
	    mf=$(pci_lookup "${LSVPD_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#*.}"
}
