# -*- 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.19 2005/11/15 01:08:13 martins Exp $

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

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

pci_get_field_value ()
{
    # Sets: value
    value=""

    local node="$1"
    local field="$2"

    :
}

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

pci_retrieve_vpd_pre_functions=""
pci_retrieve_vpd_std_functions="pci_retrieve_pci22_vpd pci_manufacture_vpd"

pci_retrieve_vpd ()
{
    local bus_addr="$1"
    local node="$2"

    local subtype
    adapter_get_subtype "pci" "$bus_addr"
    [ -n "$subtype" ] || return 1 ### !!!
    echo "$subtype" >"${node}/lsvpd,subtype"

    local vpd_dir
    vpd_dir_set_hook "$node"

    local f
    for f in \
	$pci_retrieve_vpd_pre_functions \
	$pci_retrieve_vpd_std_functions ; do
    
	"$f" "$bus_addr" "$subtype" "$node" && return 0 ### !!!
    done

    return 1
}

pci_manufacture_vpd ()
{
    local bus_addr="$1"
    local subtype="$2"
    local node="$3"

    local ds mf tm cd rk rn sn
	
    adapter_set_ds "pci" "$bus_addr" "$subtype"

    local source_node=""
    adapter_get_source_node "pci" "$bus_addr" "$subtype"

    [ -n "$source_node" ] || return 1 ### !!!

    local value vendor device subsystem_vendor subsystem_device dscd

    # Retrieve desired fields.
    pci_get_field_value "$source_node" "vendor"
    vendor="$value"
    pci_get_field_value "$source_node" "device"
    device="$value"
    pci_get_field_value "$source_node" "subsystem_vendor"
    subsystem_vendor="$value"
    pci_get_field_value "$source_node" "subsystem_device"
    subsystem_device="$value"

    # 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

    [ -n "$vendor" -a -n "$device" ] || return 1 ### !!!

    # Now set $mf, $tm.

    # Process subsystem data if available.
    if [ -n "$subsystem_vendor" -a -n "$subsystem_device" ] ; then
	cd="${subsystem_vendor}${subsystem_device}"
	local dscd="${cd:2:2}${cd:0:2}${cd:6:2}${cd:4:2}"
	ds="${ds} (${dscd})"
    fi

    local f="${db_tmp_dir}/pci-${bus_add}.lookup"
    debug_cmd pci_lookup "${LSVPD_LIBDIR}/pci.ids" \
	"$vendor" "$device" "$subsystem_vendor" "$subsystem_device" >"$f"

    local submf subtm
    { read mf ; read tm ; read submf ; read subtm ; } <"$f"
    rm -f "$f"

    # If subdevice got us something, then use it...
    if [ -n "$subtm" ] ; then
	tm="$subtm"
	mf="$submf"
    fi

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

    vpd_create_hook "$node"

    return 0
}

pci_get_config_filename ()
{
    # Sets: pci_config
    pci_config=""

    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" ] && pci_config="$conf"
}

pci_render_vpd ()
{
    local ibm_vpd_chunk="$1"
    local node="$2"

    local vpd_subdirs
    vpd_subdirs_list_hook "$node"

    if [ -f "$ibm_vpd_chunk" -a -z "$vpd_subdirs" ] ; then
	debug_cmd ibm_vpd_render "$ibm_vpd_chunk" "$node"
    else
	return 1
    fi
}

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

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

    local value
    pci_get_field_value "$node" "vendor"

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

    return 0
}

pci_retrieve_pci22_vpd ()
{
    local pci_addr="$1"
    local subtype="$2"
    local node="$3"

    local ret=1

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

    local tmp_dir="${node}/lsvpd,tmp"
    ensure_directory "$tmp_dir"
    local i="${tmp_dir}/pci-2.2,vpd"

    local vpd_subdirs
    vpd_subdirs_list_hook "$node"

    if [ ! -f "$i" -a -z "$vpd_subdirs" ] ; then
	local pci_config
	pci_get_config_filename "$pci_addr"
	if [ -f "$pci_config" ] ; 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
		debug_cmd pci_vpd_cap_grab "$pci_config" >"${i}.$$" && \
		    mv -f "${i}.$$" "$i" && \
		    debug_cmd pci_render_vpd "$i" "$node" && \
		    ret=0
		rm -f "${i}.$$"
		unlock_file "$lock"
	    fi
	fi
    fi

    return $ret
}
