# -*- shell-script -*-

# 30device-tree - Hardware database scanning routines and variables for
#                 systems with device-tree, with or without ibm,vpd properties.

# 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: 30device-tree,v 1.59 2005/05/13 03:25:50 martins Exp $

source_device_tree="/proc/device-tree"
[ -f "${source_device_tree}/system-id" ] || source_device_tree=""

[ -n "$source_device_tree" ] || return 0

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

if [ -f "${source_device_tree}/device_type" ] ; then
    read device_tree_device_type <"${source_device_tree}/device_type"
fi

db_subdir_vars_hooks="${db_subdir_vars_hooks} db_set_subdir_vars_dt"
db_set_subdir_vars_dt

db_initialise_bus ()
{
    # FIXME: Try testing with this disabled to test "hotplug" code.
    ensure_directory "$db_bus_dir"
    debug_cmd cp -a "$source_device_tree" "$db_bus_dt_dir"
}

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

# Adapters.

dt_adapter_init_hooks=""

adapter_init_node ()
{
    local type="$1"
    local bus_info="$2"

    local adapter_bus_dir
    set_adapter_bus_dir "$type" "$bus_info"

    if [ -z "$adapter_bus_dir" ] ; then
	debug "adapter_init_node: bus directory not found for \"${bus_info}\""
	return 1
    fi

    # Sanity check.
    if [ "$adapter_bus_dir" = "${adapter_bus_dir#${db_bus_dt_dir}}" ] ; then
	debug "adapter_init_node: bus node not under \"${db_bus_dir}\""
	return 1
    fi

    # Find how much of the device tree is missing?  Above tests
    # guarantee loop terminatation.
    local d="$adapter_bus_dir"
    while [ ! -d "$d" -a "$d" != "$db_bus_dt_dir" ] ; do
	d="${d%/*}" # dirname
    done

    if [ ! -d "$d" -a "$d" != "$db_bus_dt_dir" ] ; then
	# Copy over the missing part of the device tree.
	s="${source_device_tree}${d#${db_bus_dt_dir}}"
	if [ -d "$s" ] ; then
	    ensure_directory "$d"
	    cp -a "${s}/." "${d}/"
	else
	    debug "adapter_init_node: missing device-tree node \"$s\""
	fi
    fi

    local i
    for i in $dt_adapter_init_hooks ; do
	$i "$d"
    done

    local bus_addr="${bus_info#*/}"
    case "$bus_info" in
	(pci/*) pci_do_pci22_vpd "${bus_addr%/*}" "$d" ;;
    esac
}

pci_get_vendor_id ()
{
    # Sets: vendor_id

    local adapter_bus_dir="$1"

    vendor_id="ffff"

    local f="${adapter_bus_dir}/vendor-id"

    if [ ! -f "$f" -a ! -f "${adapter_bus_dir}/class-code" ] ; then
	adapter_bus_dir="${adapter_bus_dir%/*}" # dirname
	[ -f "${adapter_bus_dir}/class-code" ] && \
	    f="${adapter_bus_dir}/vendor-id"
    fi

    if [ -f "$f" ] ; then
	vendor_id=$(tdump -c "$f")
	vendor_id="${vendor_id#0000}"
    fi
}

#---------------------------------------------------------------------

add_adapter_hook ()
{
    local type="$1"
    local bus_info="$2"
    local adapter_bus_dir="$3"
    local adapter_is_a_channel="$4"

    $adapter_is_a_channel && \
	[ ! -f "${adapter_bus_dir}/built-in" ] && \
	[ -f "${adapter_bus_dir%/*}/built-in" ] && \
	>"${adapter_bus_dir}/built-in"

    add_adapter_hook_basic "$@"
}

#---------------------------------------------------------------------

unset -f adapter_set_ds_ide

adapter_set_ds_scsi ()
{
    # Sets: ds

    local adapter_bus_dir="$1"
    local adapter_is_a_channel="$2"

    case "$adapter_bus_dir" in
	*fibre-channel@*)
	    ds="Fibre Channel Adapter"
	    $adapter_is_a_channel && ds="${ds} Channel"
	    ;;
	*k2-sata*@*)
	    ds="Serial ATA Adapter"
	    $adapter_is_a_channel && ds="${ds} Channel"
	    ;;
	*)
	    adapter_set_ds_DEFAULT \
		"scsi" "$adapter_bus_dir" $adapter_is_a_channel
	    ;;
    esac

    if ! $adapter_is_a_channel && \
	[ -f "${adapter_bus_dir}/ibm,fw-adapter-name" ] ; then
	local t
	read t <"${adapter_bus_dir}/ibm,fw-adapter-name"
	ds="${t} ${ds}"
	if [ -f "${adapter_bus_dir}/wide" ] ; then
	    ds="Wide/${ds}"
	fi
    fi
}

adapter_set_ds_ethernet ()
{
    # Sets: ds

    local adapter_bus_dir="$1"
    local adapter_is_a_channel="$2"

    if ! $adapter_is_a_channel && \
	[ -f "${adapter_bus_dir}/ibm,fw-adapter-name" ] ; then
	read ds <"${adapter_bus_dir}/ibm,fw-adapter-name"
    else
	adapter_set_ds_DEFAULT \
	    "ethernet" "$adapter_bus_dir" $adapter_is_a_channel
    fi
}

#---------------------------------------------------------------------

adapter_extra_vpd_hooks="${adapter_extra_vpd_hooks} dt_mac_address_vpd_hook"

dt_mac_address_vpd_hook ()
{
    local type="$1"
    local bus_info="$2"
    local adapter_bus_dir="$3"
    local vpd_dir="$4"

    local props="mac-address local-mac-address"
    local p
    for p in $props ; do
	local f="${adapter_bus_dir}/${p}"
	if [ -f "$f" ] ; then
	    local mac_address=$(tdump -c "$f")
	    if [ -n "$mac_address" ] ; then
		vpd_field_ensure "$vpd_dir" "NA" "$mac_address"
		break
	    fi
	fi
    done
}

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

set_dt_pci_domain_prop_hook ()
{
    local d="${source_device_tree}/pci"
    local big_bus_num_flag="${source_device_tree}/rtas/ibm,read-pci-config"

    # Under 2.6, linux,pci-domain is always used.
    if [ -f "${d}/linux,pci-domain" ] ; then
	dt_pci_domain_prop="linux,pci-domain"
	return
    fi

    # Under 2.4, only take notice of these on big-bus-num machines.
    if [ -f "$big_bus_num_flag" ] ; then
	local i
	for i in "linux,phbnum" "linux,global-number" ; do
	    if [ -f "${d}/${i}" ] ; then
		dt_pci_domain_prop="$i"
		return
	    fi
	done
    fi
    
    # Sanity check.
    [ -f "$big_bus_num_flag" ] && \
	: "set_dt_pci_domain_prop_hook:" \
	"unable to determine PCI domain property name"
}

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

add_adapter_pci_hook ()
{
    # Sets: ds, mf, tm, cd

    local node="$1"

    local subvendor subdevice
    if [ -f "${node}/subsystem-vendor-id" -a -f "${node}/subsystem-id" ] ; then
	set -- $(tdump "${node}/subsystem-vendor-id")
	local b1="$3"
	local b2="$4"
	set -- $(tdump "${node}/subsystem-id")
	local b3="$3"
	local b4="$4"

	subvendor="${b1}${b2}"
	subdevice="${b3}${b4}"
	cd="${subvendor}${subdevice}"

	local dscd="${b2}${b1}${b4}${b3}"
	[ -n "$dscd" ] && ds="${ds} (${dscd})"
    fi

    local vendor device
    if [ -f "${node}/vendor-id" -a -f "${node}/device-id" ] ; then
	set -- $(tdump "${node}/vendor-id")
	local b1="$3"
	local b2="$4"
	set -- $(tdump "${node}/device-id")
	local b3="$3"
	local b4="$4"

	vendor="${b1}${b2}"
	device="${b3}${b4}"
    fi

    pci_set_mf_tm_hook
}

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

make_multiplexed get_dt_adapter_types

get_dt_adapter_types_DEFAULT ()
{
    # Sets: dt_adapter_types
    local type="$1"

    dt_adapter_types="$type"
}

get_dt_adapter_types_scsi ()
{
    # Sets: dt_adapter_types
    dt_adapter_types="scsi fibre-channel v-scsi k2-sata"
}

get_dt_adapter_types_ethernet ()
{
    # Sets: dt_adapter_types
    dt_adapter_types="ethernet l-lan"
}

get_dt_adapter_types_ide ()
{
    # Sets: dt_adapter_types
    dt_adapter_types="ide ata-6"
}
