# -*- shell-script -*-

# 07device_scsi_sg_inq - SCSI device inquiry support using sg3_utils.

# 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: 07device_scsi_sg_inq,v 1.12 2004/06/10 02:39:48 martins Exp $

sg_inq=$(type -p sg_inq)
[ -n "$sg_inq" ] || return 0

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

do_device_scsi ()
{
    local hbtl="$1" # host:bus:target:lun

    adapter="host${hbtl%%:*}"

    # This is unfortunate: do_device_hook can depend upon
    # $device_type, so the inquiry output has to go to a temporary
    # place, so we can extract the type and let the hook use it to
    # help construct $device_bus_dir.  Then we can move the output to
    # the correct place.
    local d="${db_bus_dir}/tmp/${hbtl}"
    ensure_directory "$d"

    if scsi_vpd_inquire "$hbtl" "$d" ; then

	local outdir="${d}/vpd"
	ensure_directory "$outdir"
	scsi_vpd_render "$d" "$outdir"

	# Used by hook.
	local device_type
	if [ -f "${d}/type" ] ; then
	    read device_type < "${d}/type"
	fi

	local bus_info="scsi/${hbtl}"
	local yl

	# Render.
	if do_device_hook "scsi" ; then
	    local vpd_dir
	    vpd_dir_set_hook "$device_bus_dir"
	    ensure_directory "${vpd_dir%/*}" # dirname 

	    mv "$outdir" "$vpd_dir"
	    local fc
	    do_fc_hook "$device_bus_dir"
	    vpd_field_add_value "$vpd_dir" "FC" "$fc"
	    vpd_field_add_value "$vpd_dir" "YL" "$yl"
	    scsi_vpd_enumerate "$vpd_dir"

	    # Keep for debugging.
	    local t="${device_bus_dir}/linux,tmp"
	    mv "$d" "$t"
	else
	    # This is a little safer than an "rm -rf ..."
	    rm -f "${outdir}/"*
	    rmdir "$outdir"
	    rm -f "${d}/"*
	    rmdir "$d"
	fi
    else
	rmdir "$d"
    fi
}

get_device_name_scsi ()
{
    # $1 is colon-separated: host:bus:target:lun
    set -- ${1//:/ }

    # Use non-sg name (at $) if available, otherwise use sg name (at ^).
    sed -n -e "/ $1 $2 $3 $4  *[0-9][0-9]*  *\/dev\//s/.* \([^ ]*\)$/\1/p" \
	   -e "/^\/dev\/[^ ]*  *$1 $2 $3 $4  */s/ .*//p" \
	   "${db_misc_dir}/sg_map-x"
}

get_major_minor_scsi ()
{
    # $1 is colon-separated: host:bus:target:lun
    set -- ${1//:/ }

    local minor=$(sed -n -e "s@^\/dev\/sg\([^ ]*\)  *$1 $2 $3 $4  *.*\$@\1@p" \
	"${db_misc_dir}/sg_map-x")
    [ -n "$minor" ] && echo "21:${minor}"
}

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

scsi_vpd_inquire ()
{
    local hbtl="$1"
    local outd="$2"

    local dev="${outd}/node"
    local mm=$(get_major_minor_scsi "$hbtl")

    if [ -n "$mm" ] ; then
	debug_cmd mknod "$dev" c ${mm/:/ }
	
        # Retrieve VPD data via SG interface.
	local t="$outd/inquiry"
	if debug_cmd "$sg_inq" -r "$dev" >"$t"  ; then
	    t="$outd/0x00"
	    if debug_cmd "$sg_inq" -r -e -o=0x00 "$dev" >"$t" ; then
		for i in $(tdump "$t" | (read x x x x rest ; echo $rest)) ; do
		    j="0x${i}"
		    if [ "$j" != "0x00" ] ; then
			t="$outd/$j"
			if ! debug_cmd "$sg_inq" -r -e -o=$j "$dev" >"$t" ; then
			    rm -f "$t"
			fi
		    fi
		done
	    else
		rm -f "$t"
	    fi
	else
	    rm -f "$t"
	    rm -f "$dev"
	    debug "scsi_vpd_inquire: Standard inquiry on ${dev} failed"
	    return 1
	fi
    fi
	
    rm -f "$dev"

    return 0
}

scsi_vpd_enumerate ()
{
    local vpd_dir="$1"

    local sort_order="FC DS AX PL MF TM CD LL YL FN RL SN EC PN"

    local ks=""

    local oldpwd=$(pwd)
    cd "$vpd_dir"

    local i
    for i in $sort_order ; do
	[ -e "$i" ] && ks="${ks}${i} "
    done
    
    for i in * ; do
	case "$ks" in
	    (*${i}\ *) : ;;  # Ignore, already added in previous pass.
	    (*) ks="${ks}${i} " ;;
	esac
    done

    echo "$ks" > "${vpd_dir}/.lsvpd"

    cd "$oldpwd"
}
