# -*- shell-script -*-

# 00minimal - Default hardware database scanning routines and variables.

# 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: 00minimal,v 1.60 2005/05/13 03:29:43 martins Exp $

# Default and common functions.

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

######################################################################
#
# MULTIPLEXING:
#
# Doing:
#
#   make_multiplexed add_device
#
# will allow a calls like:
#
#   add_device ide /dev/hda
#
# which will try to call
#
#   add_device_ide /dev/hda
#
# if it exists.  If not, the next attempt will be
#
#   add_device_DEFAULT ide /dev/hda
#
# If the DEFAULT function doesn't exist, failure is silent.

make_multiplexed add_adapter

make_multiplexed bus_set_yl
make_multiplexed adapter_set_yl

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

db_initialise_functions="db_initialise_basic db_initialise_bus"

db_initialise ()
{
    for f in $db_initialise_functions ; do
	$f
    done
}

db_initialise_bus ()
{
    ensure_directory "$db_bus_dir"
}

db_initialise_basic ()
{
    ensure_directory "$db"

    db_set_subdir_vars_hook

    db_initialise_misc
}

db_initialise_misc ()
{
    ensure_directory "$db_misc_dir"

    db_initialise_misc_uname
    db_initialise_misc_proc
}

# It might be possible to reparse the output of "uname -a", but this
# is likely to be much more systematic.
db_initialise_misc_uname ()
{
    ensure_directory "$db_uname_dir"

    for i in "kernel-name" "kernel-release" "kernel-version" \
	"machine" "nodename" ; do
      # Hey, there're 2 completely different versions of uname!
      local flag
      case "$i" in
	  kernel-name)    flag="-s"     ;;
	  kernel-release) flag="-r"     ;;
	  kernel-version) flag="-v"     ;;
	  *)              flag="--${i}" ;;
      esac
      uname "$flag" > "${db_uname_dir}/${i}"
    done
}

db_initialise_misc_proc ()
{
    ensure_directory "$db_proc_dir"

    for i in "partitions" "cpuinfo" ; do
	[ -f "/proc/${i}" ] && cat "/proc/${i}" >"${db_proc_dir}/${i}"
    done
}

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

list_buses ()
{
    :
}

add_bus ()
{
    :
}

bus_set_yl_DEFAULT ()
{
    # Sets: yl

    local node="$1"

    yl=""
}

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

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

    :
}

check_if_adapter_is_a_channel ()
{
    # Sets: adapter_is_a_channel
    adapter_is_a_channel=false

    local type="$1"
    local bus_info="$2"
    local adapter_bus_dir="$3"

    # There are numerous ways of doing this.  Alternate ways might
    # include checking for a device-tree "class-code" or
    # "assigned-addresses" property.
    case "$bus_info" in
	(*/*/*) adapter_is_a_channel=true ;;
    esac
}

add_adapter_DEFAULT ()
{
    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 "add_adapter_DEFAULT: failed to get node for \"$bus_info\""
	return
    fi

    adapter_init_node "$type" "$bus_info"

    local adapter_is_a_channel
    check_if_adapter_is_a_channel "$type" "$bus_info" "$adapter_bus_dir"

    local ds yl
    add_adapter_hook \
	"$type" "$bus_info" "$adapter_bus_dir" $adapter_is_a_channel

    local vpd_dir
    vpd_dir_set_hook "$adapter_bus_dir"
    if [ -e "$vpd_dir" ] ; then
        # Already VPD: ensure YL exists, runs extra hooks - we're done.
	vpd_field_ensure "$vpd_dir" "YL" "$yl"
	! $adapter_is_a_channel && \
	    adapter_extra_vpd_hook "$type" "$bus_info" "$adapter_bus_dir"
	return 0
    fi

    local mf tm cd
    ! $adapter_is_a_channel && [ "${bus_info%%/*}" = "pci" ] && \
	add_adapter_pci_hook "$adapter_bus_dir"
    
    local rl rm sn

    vpd_create_hook "$adapter_bus_dir"

    ! $adapter_is_a_channel && \
	adapter_extra_vpd_hook "$type" "$bus_info" "$adapter_bus_dir"
}

add_adapter_hook ()
{
    # Sets: ds, yl

    add_adapter_hook_basic "$@"
}

add_adapter_hook_basic ()
{
    # Sets: ds, yl

    local type="$1"
    local bus_info="$2"
    local adapter_bus_dir="$3"
    local adapter_is_a_channel="$4"

    ensure_directory "$adapter_bus_dir"
    echo "$bus_info" >"${adapter_bus_dir}/lsvpd,bus-info"
    adapter_set_ds "$type" "$adapter_bus_dir" $adapter_is_a_channel
    adapter_set_yl "$type"
}

adapter_set_yl_DEFAULT ()
{
    # Sets: yl

    yl=""
}

adapter_set_ax_osname_hook ()
{
    # Sets: ax osname
    local type="$1"
    local bus_info="$2"

    adapter_set_ax_osname_hook_basic "$type" "$bus_info"
}

adapter_set_ax_osname_hook_basic ()
{
    # Sets: ax osname
    local type="$1"
    local bus_info="$2"

    local adapter_bus_dir
    set_adapter_bus_dir "$type" "$bus_info"

    if [ -L "${adapter_bus_dir}/lsvpd,os-node" ] ; then
	cd -P "${adapter_bus_dir}/lsvpd,os-node"
	osname="${PWD##*/}" # basename
	cd "$OLDPWD"
	adapter_set_ax_from_osname_hook
    else
	# Not known by OS, use sequence number.
	osname=""
	local output_prefix
	adapter_set_output_prefix "$type"
	local minimum_unknown sequence_number
	adapter_set_minimum_unknown "$type"
	set_sequence_number_hook "adapter_${output_prefix}" $minimum_unknown
	ax="${output_prefix}${sequence_number}"
    fi
}

adapter_set_ax_from_osname_hook ()
{
    # Uses: type osname
    # Sets: ax
    local os_prefix
    adapter_set_os_prefix "$type"

    local output_prefix
    adapter_set_output_prefix "$type"

    if [ -n "$osname" ] ; then
	ax="$osname"
	[ "$os_prefix" != "$output_prefix" ] && \
	    ax="${output_prefix}${ax#${os_prefix}}"
    fi
}

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

set_adapter_bus_dir ()
{
    # Sets: adapter_bus_dir

    local type="$1"
    local bus_info="$2"

    adapter_bus_dir="${db_bus_dir}/${bus_info}"
}

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

do_fc_hook ()
{
    local dir="$1"

    # By default, VPD is marked global.
    fc='********'
}

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

add_adapter_name_and_crosslink ()
{
    local type="$1"
    local bus_info="$2"
    local ax="$3"
    local osname="$4"

    local adapter_bus_dir
    set_adapter_bus_dir "$type" "$bus_info"

    local vpd_dir
    vpd_dir_set_hook "$adapter_bus_dir"
    [ -e "$vpd_dir" ] && \
	vpd_field_ensure "$vpd_dir" "AX" "$ax"

    [ -n "$osname" ] && \
	add_adapter_crosslink "$type" "$bus_info" "$osname"
}

add_adapter_crosslink ()
{
    local type="$1"
    local bus_info="$2"
    local name="$3"

    local adapter_bus_dir adapter_os_dir

    set_adapter_bus_dir "$type" "$bus_info"

    if [ -n "$adapter_bus_dir" ] ; then
	local adapter_subdir
	adapter_set_adapter_subdir "$type"
	adapter_os_dir="${db_os_dir}/${adapter_subdir}/${name}"
	cross_link "$adapter_bus_dir" "$adapter_os_dir"
    else
	debug "add_adapter_crosslink: bus_directory for \"${bus_info}\""
    fi
}

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

set_dot_dots_hook ()
{
    local d="$1"
    local t="$2" # Must be a prefix of $d

    dot_dots=""

    if [ "${d}" = "${d#${t}/}" ] ; then
	debug "set_dot_dots_hook: ${t} is not a prefix of ${d}"
	return 1
    fi

    while [ -n "$d" -a "$d" != "$t" ] ; do
	dot_dots="${dot_dots}/.."
	local d="${d%/*}" # dirname
    done

    dot_dots="${dot_dots#/}"
}

cross_link ()
{
    local bus_d="$1"
    local os_d="$2"

    local bus_l="${bus_d}/lsvpd,os-node"
    local os_l="${os_d}/bus-node"

    if [ ! -L "$bus_l" -o ! -L "$os_l" ] ; then
	local dot_dots

	if [ ! -L "$bus_l" ] ; then
	    ensure_directory "$bus_d"
	    local os_path="${os_d#${db_os_dir}/}"
	    set_dot_dots_hook "$bus_d" "$db" && \
		ln -s "${dot_dots}/${db_os_subdir}/${os_path}" "$bus_l"
	fi

	if [ ! -L "$os_l" ] ; then
	    ensure_directory "$os_d"
	    local bus_path="${bus_d#${db_bus_dir}/}"
	    set_dot_dots_hook "$os_d" "$db" && \
		ln -s "${dot_dots}/${db_bus_subdir}/${bus_path}" "$os_l"
	fi
    fi
}

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

bus_alias_set ()
{
    local bus_info="$1"
    local target="$2"
    local alias_type="${3:-bus-alias}" # optional

    local dir="${db_bus_dir}/${bus_info}"
    local key="${dir}/${alias_type}"

    if [ ! -L "$key" ] ; then
	ensure_directory "$dir"
	case "$target" in
	    /*)
		ln -s "$target" "$key"
		;;
	    *)
		local dot_dots
		set_dot_dots_hook "$dir" "$db_bus_dir" && \
		    ln -s "${dot_dots}/${target}" "$key"
		;;
	esac
	return 0
    else
	return 1
    fi
}

bus_alias_clear ()
{
    local bus_info="$1"
    local alias_type="${2:-bus-alias}" # optional
    
    local dir="${db_bus_dir}/${bus_info}"
    local key="${dir}/${alias_type}"

    rm -f "$key"
    rmdir "$dir" >/dev/null 2>&1
}

bus_alias_get ()
{
    # Sets: bus_alias
    bus_alias=""

    local bus_info="$1"
    local alias_type="${2:-bus-alias}" # optional
    
    local key="${db_bus_dir}/${bus_info}/${alias_type}"
    if [ -L "$key" -a -d "$key" ] ; then
	cd -P "$key"
	bus_alias="$PWD"
	cd "$OLDPWD"
    fi
}

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

set_sequence_number_hook ()
{
    local prefix="$1"
    local min="${2:-0}"

    ensure_directory "$db_state_dir"
    local f="${db_state_dir}/${prefix}"

    sequence_number=""

    if lock_file "$f" ; then
	if [ -f "$f" ] ; then
	    read sequence_number <"$f"
	else
	    sequence_number="$min"
	fi
	echo $(($sequence_number + 1)) >"$f"
	unlock_file "$f"
    fi
}
