# -*- 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

# 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.39 2004/06/17 05:31:56 martins Exp $

# Default and common functions.

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

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

# This will allow a calls like:
#
#   do_device ide /dev/hda
#
# which will try to call
#
#   do_device_ide /dev/hda
#
# if it exists.  If not, the next attempt will be
#
#   do_device_DEFAULT ide /dev/hda
#
# If the DEFAULT function doesn't exist, failure is silent.

make_multiplexed adapter_setup
make_multiplexed list_adapters
make_multiplexed do_adapter
make_multiplexed do_adapter_hook
make_multiplexed adapter_set_ax_osname_hook

make_multiplexed device_setup
make_multiplexed list_devices
make_multiplexed do_device
make_multiplexed do_device_hook
make_multiplexed get_device_name

make_multiplexed get_adapter_os_prefix
make_multiplexed get_adapter_output_prefix
make_multiplexed get_adapter_subdir
make_multiplexed get_adapter_ds

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

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
}

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

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

    :
}

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

# $num should be a "global" variable suitable for manufacturing
# adapter names in sequence, and will be incremented if used.
adapter_set_ax_osname_hook_DEFAULT ()
{
    # Uses: num
    # Sets: ax osname num
    local type="$1"
    local bus_info="$2"

    local adapter_bus_dir=$(get_adapter_bus_dir "$type" "$bus_info")

    if [ -L "${adapter_bus_dir}/linux,os-node" ] ; then
	local l=$(readlink "${adapter_bus_dir}/linux,os-node")
	osname="${l##*/}" # basename
	adapter_set_ax_from_osname_hook
    else
	# Not known by OS, use sequence number.
	osname=""
	local out_prefix=$(get_adapter_output_prefix "$type")
	[ -n "$out_prefix" ] || out_prefix="${type:0:4}"
	ax="${out_prefix}${num}"
	num=$(($num + 1))
    fi
}

adapter_set_ax_from_osname_hook ()
{
    # Uses: type osname
    # Sets: ax
    local os_prefix=$(get_adapter_os_prefix "$type")
    local out_prefix=$(get_adapter_output_prefix "$type")

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

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

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

    echo "${db_bus_dir}/${bus_info}"
}

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

do_fc_hook ()
{
    local dir="$1"

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

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

do_device_hook_common ()
{
    # Uses: bus_info, adapter
    # Sets: adapter_bus_dir, adapter_os_dir

    local subdir=$(get_adapter_subdir "${bus_info%%/*}")
    adapter_os_dir="${db_os_dir}/${subdir}/${adapter}"
    local f="${adapter_os_dir}/bus-node"
    adapter_bus_dir=""
    if [ -L "$f" ] ; then
	local l=$(readlink "$f")
	if [ -n "$l" ] ; then
	    adapter_bus_dir="${adapter_os_dir}/${l}"
	    return 0
	fi
    fi

    return 1
}

do_device_name_and_crosslink ()
{
    local adapter_os_dir="$1"
    local name="$2"
    local device_bus_dir="$3"

    local device_os_dir="${adapter_os_dir}/${name#/dev/}"

    ensure_directory "$device_os_dir"
    ensure_directory "$device_bus_dir"
    
    cross_link "$device_bus_dir" "$device_os_dir"

    local vpd_dir
    vpd_dir_set_hook "$device_bus_dir"
    vpd_field_ensure "$vpd_dir" "AX" "$name"
}

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

    local adapter_bus_dir=$(get_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"

    if [ -n "$osname" ] ; then
	do_adapter_crosslink "$type" "$bus_info" "$osname"

	case "${bus_info}-${osname}" in
	    */*/*-*:*)
		do_adapter_name_and_crosslink \
		    "$type" "${bus_info%/*}" "${ax%:*}" "${osname%:*}"
		;;
	esac
    fi
}

do_adapter_crosslink_basic ()
{
    # Sets adapter_bus_dir, adapter_os_dir

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

    adapter_bus_dir=$(get_adapter_bus_dir "$type" "$bus_info")

    if [ -n "$adapter_bus_dir" ] ; then
	local subdir=$(get_adapter_subdir "$type")
	adapter_os_dir="${db_os_dir}/${subdir}/${name}"
	ensure_directory "$adapter_bus_dir"
	ensure_directory "$adapter_os_dir"
	cross_link "$adapter_bus_dir" "$adapter_os_dir"
    else
	debug "do_adapter_crosslink: bus_directory for \"${bus_info}\""
    fi
}

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

    local adapter_bus_dir adapter_os_dir

    do_adapter_crosslink_basic "$type" "$bus_info" "$name"
}

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

set_dot_dots_hook ()
{
    local d="$1"
    local t="$2"

    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}/linux,os-node"
    local os_l="${os_d}/bus-node"

    if [ ! -L "$bus_l" -o ! -L "$os_l" ] ; then
	bus_d=$(pseudo_realpath "$bus_d")
	os_d=$(pseudo_realpath "$os_d")

	local dot_dots

	if [ ! -L "$bus_l" ] ; then
	    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
	    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
}

pseudo_realpath ()
{
    local f="$1"

    case "$f" in
	(/*) :                   ;;
	(*)  f="${PWD}/${f}"     ;;
    esac

    local out=""
    local IFS="/"
    for i in $f ; do
	case "$i" in
	    (.|"")  : ;;
	    (..) out="${out%/*}"   ;; # dirname
	    (*)  out="${out}/${i}" ;;
	esac
    done
    [ -z "$out" ] && out="/"
    echo "$out"
}

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

bus_alias_set ()
{
    local bus_info="$1"
    local relnode="$2"
    
    local dir="${db_bus_dir}/${bus_info}"
    local key="${dir}/bus-alias"

    if [ ! -L "$key" ] ; then
	ensure_directory "$dir"
	local dot_dots
	set_dot_dots_hook "$dir" "$db_bus_dir" && \
	    ln -s "${dot_dots}/${relnode}" "$key"
    fi
}

bus_alias_get ()
{
    local bus_info="$1"
    
    local key="${db_bus_dir}/${bus_info}/bus-alias"
    if [ -L "$key" ] ; then
	local dn="${key%/*}" # dirname
	local cv=$(readlink "$key")
	pseudo_realpath "${dn}/${cv}"
    fi
}

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

scsi_parse_addr_hook ()
{
    local scsi_addr="$1" # host:bus:target:lun

    # Sets: host, bus, target, lun

    local bustarget="${scsi_addr#*:}"
    bustarget="${bustarget%:*}"

    host="${scsi_addr%%:*}"
    bus="${bustarget%:*}"
    target="${bustarget#*:}"
    lun="${scsi_addr##*:}"
}
