# -*- shell-script -*-

# 00minimal - Directory-based VPD creation functions.

# This file is part of the Linux lsvpd package.

# (C) Copyright IBM Corp. 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: 00vpd,v 1.6 2004/12/02 06:01:57 martins Exp $

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

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

vpd_create_hook ()
{
    # Uses: ds, ax, mf, tm, sn, cd, rl, rm, yl

    local node="$1"

    local fc
    do_fc_hook "$node"

    local vpd_dir
    vpd_dir_set_hook "$node"

    _vpd_field_append "value" "$vpd_dir" "FC" "$fc"
    _vpd_field_append "value" "$vpd_dir" "DS" "$ds"
    _vpd_field_append "value" "$vpd_dir" "MF" "$mf"
    _vpd_field_append "value" "$vpd_dir" "TM" "$tm"
    _vpd_field_append "value" "$vpd_dir" "SN" "$sn"
    _vpd_field_append "value" "$vpd_dir" "CD" "$cd"
    _vpd_field_append "value" "$vpd_dir" "RL" "$rl"
    _vpd_field_append "value" "$vpd_dir" "RM" "$rm"
    _vpd_field_append "value" "$vpd_dir" "YL" "$yl"
}

vpd_field_add_value ()
{
    local dir="$1"
    local field="$2"
    local value="$3"

    ensure_directory "$dir"
    local f="${dir}/${field}"
    vpd_field_remove_dir "$f"
    echo -n "$value"  >"$f"
}

vpd_field_remove_dir ()
{
    local d="$1"

    if [ -d "$d" ] ; then
	local f
	for f in "link" "last" ; do
	    local t="${d}/${f}"
	    [ -f "$t" ] && rm -f "$t"
	done
	debug_cmd rmdir "$d"
    fi
}

vpd_field_add_link ()
{
    local dir="$1"
    local field="$2"
    local link="$3"

    ensure_directory "$dir"

    # Remove any old value.
    local f="${dir}/${field}"
    vpd_field_remove_dir "$f"
    rm -f "$f"

    # Create directory and link.
    ensure_directory "$f"
    ln -s "$link" "${f}/link"

    # Populate cache.
    local vpd_value
    vpd_field_get "$dir" "$field"
}

_vpd_field_add ()
{
    local flag="$1"
    shift

    case "$flag" in
	(value) vpd_field_add_value "$@" ;;
	(link)  vpd_field_add_link  "$@" ;;
    esac
}

_vpd_field_append ()
{
    local flag="$1"
    local dir="$2"
    local field="$3"
    local value="$4"

    if [ -n "$value" ] ; then 
	_vpd_field_add "$flag" "$dir" "$field" "$value"
	echo -n "${field} " >>"${dir}/.lsvpd"
    fi
}

_vpd_field_insert ()
{
    local flag="$1"
    local dir="$2"
    local field="$3"
    local value="$4"

    local f="${dir}/.lsvpd"
    if [ ! -f "$f" ] ; then
	_vpd_field_append "$flag" "$dir" "$field" "$value"
    else
	local all
	read all <"$f"
	# Important trailing space lost above!
	all="${all} "

	case "$field" in
	    AX)
	        # Insert after DS, if possible.
		all="${all/DS /DS AX }"
		case "$all" in
		    (*DS\ AX*) : ;;
		    (*) all="${all}AX " ;;
		esac
		;;
	    ?*)
	        # Insert before YL, if possible.
		all="${all/YL /${field} YL }"
		case "$all" in
		    (*${field}\ YL*) : ;;
		    (*) all="${all}${field} " ;;
		esac
		;;
	esac

	echo "${all}" >"$f"
	_vpd_field_add "$flag" "$dir" "$field" "$value"
    fi
}

_vpd_field_ensure ()
{
    local flag="$1"
    local dir="$2"
    local field="$3"
    local value="$4"

    if [ ! -e "${dir}/${field}" ] ; then
	_vpd_field_insert "$flag" "$dir" "$field" "$value"
    else
	return 1
    fi
}

vpd_field_ensure ()
{
    _vpd_field_ensure "value" "$@"
}

vpd_field_ensure_link ()
{
    _vpd_field_ensure "link" "$@"
}

_vpd_field_override ()
{
    local flag="$1"
    local dir="$2"
    local field="$3"
    local value="$4"

    local f="${dir}/${field}"
    if [ -e "$f" ] ; then
	_vpd_field_add    "$flag" "$dir" "$field" "$value"
    else
	_vpd_field_insert "$flag" "$dir" "$field" "$value"
    fi
}

vpd_field_override ()
{
    _vpd_field_override "value" "$@"
}

vpd_field_override_link ()
{
    _vpd_field_override "link" "$@"
}

adapter_extra_vpd_hooks=""

adapter_extra_vpd_hook ()
{
    local type="$1"
    local bus_info="$2"
    local adapter_bus_dir="$3"

    local vpd_dir
    vpd_dir_set_hook "$adapter_bus_dir"

    local i
    for i in $adapter_extra_vpd_hooks ; do
	$i "$type" "$bus_info" "$adapter_bus_dir" "$vpd_dir"
    done
}

vpd_dir_set_hook ()
{
    local node="$1"
    local subnode="$2" ; [ -n "$subnode" ] || subnode="0"

    if [ $subnode -eq 0 ] ; then
	vpd_dir="${node}/linux,vpd/000"
    else
	vpd_dir=$(printf "${node}/linux,vpd/%03d" $subnode)
    fi
}

vpd_subdirs_list_hook ()
{
    local node="$1"

    local d="${node}/linux,vpd"

    if [ ! -d "${d}/001" ] ; then
	if [ -d "${d}/000" ] ; then
	    vpd_subdirs="${d}/000"
	else
	    vpd_subdirs=""
	fi
    else
        # shopt -s nullglob
	vpd_subdirs=$(echo "${d}/"[0-9][0-9][0-9])
    fi
}

vpd_fields_list_hook ()
{
    local vpd_dir="$1"

    local f="${vpd_dir}/.lsvpd"
    [ -r "$f" ] && read vpd_fields <"$f"
}

# For non-unique field names, this interface can be used in 2 ways.
# The 3 digit sequence number for fields (after the first, numbered 0,
# but omitted) can be appended to the field name (as in ZZ.002), or
# the sequence number can be provided as an optional 3rd argument.
vpd_field_get ()
{
    # Sets: vpd_value
    vpd_value=""

    local vpd_dir="$1"
    local field="$2"
    local seq="$3"

    local f="${vpd_dir}/${field}"
    if [ -n "$seq" ] ; then
	if [ "$seq" -gt 0 ] ; then
	    f=$(printf "%s.%03d" "$f" "$seq")
	fi
    fi

    local last="${f}/last"
    if [ -d "$f" ] ; then
	local link="${f}/link"
	if on_live_system && [ -r "$link" ] ; then
	    read vpd_value <"$link"
	    can_cache_vpd && \
		echo -n "$vpd_value" >"$last"
	elif should_show_cached_vpd && [ -r "$last" ] ; then
	    read vpd_value <"$last"
	fi
    elif [ -L "$f" ] && ! on_live_system ; then
	: # Backward compatibility.
    elif [ -r "$f" ] ; then
	read vpd_value <"$f"
    fi
}

