#! /bin/sh
#
# dasd_configure
# $Id: dasd_configure,v 1.10 2004/11/26 15:50:48 hare Exp $
#
# Copyright (c) 2001-2014 SUSE LINUX Products GmbH, Nuernberg, Germany.
#
# Configures or deconfigures a DASD device
#
# Usage:
#   dasd_configure [-f -t <dasd_type> ] <ccwid> <online> [use_diag]
#
#	-f force creation of udev rules, do not check values in /sys.
#	-t DASD type. Must be provided if -f is used.
#	ccwid = x.y.ssss where
#		x is always 0 until IBM creates something that uses that number
#		y is the logical channel subsystem (lcss) number. Most often this is 0, but it could be non-zero
#		ssss is the four digit subchannel address of the device, in hexidecimal, with leading zeros.
#	online = 0 to take the device offline
#		 1 to bring the device online
#	use_diag = 0 to _not_ use z/VM DIAG250 I/O, which is the default
#		   1 to use z/VM DIAG250 I/O
#
# Return values:
#   1  If the "Usage:" line is displayed, not enough parameters specified.
#   1  sysfs not mounted (if the "Usage:" line is not displayed).
#   2  Invalid status for <online>
#   3  No device found for <ccwid>
#   4  Could not change state of the device
#   5  Device is not a DASD
#   6  Could not load module
#   7  Failed to activate DASD
#   8  DASD not formatted
#   9  Only dasd-fba or dasd-eckd is supported.
#

if [ "${DEBUG}" != "yes" ]; then
  DEBUG="no"
fi

exitcode=0
DASD_FORCE=0
DASD_TYPE="unknown"

DATUM=$(date)

add_channel_for_cio() {
	echo "$* # $DATUM" >> /boot/zipl/active_devices.txt
}

remove_channel_for_cio() {
	[ -w /boot/zipl/active_devices.txt ] && sed -i -e "/^$1/d" /boot/zipl/active_devices.txt
}

mesg () {
    echo "$@"
}

debug_mesg () {
    case "$DEBUG" in
	yes) mesg "$@" ;;
	*) ;;
    esac
}

if [ $# -lt 2 ] ; then
    echo "Usage: $0 [options] <ccwid> <online> [use_diag]"
    echo
    echo "	-f force creation of udev rules, do not check values in /sys."
    echo "	-t DASD type. Must be provided if -f is used."
    echo "	ccwid = x.y.ssss where"
    echo "		x is always 0 until IBM creates something that uses that number"
    echo "		y is the logical channel subsystem (lcss) number. Most often this is 0, but it could be non-zero"
    echo "		ssss is the four digit subchannel address of the device, in hexidecimal, with leading zeros."
    echo "	online = 0 to take the device offline"
    echo "		 1 to bring the device online"
    echo "	use_diag = 0 to _not_ use z/VM DIAG250 I/O, which is the default"
    echo "		   1 to use z/VM DIAG250 I/O"
    exit 1
fi

while [ $# -gt 0 ] ; do
    case "$1" in
	-f) # force creation of udev rules, do not check values in /sys
	    DASD_FORCE=1
	    ;;
	-t*) # drive type. Must be provided if -f  is used.
	    if [ "$1" = "-t" ] ; then
		if [ "$2" = "dasd-eckd" -o "$2" = "dasd-fba" ]; then
		    DASD_TYPE=$2
		    shift
		else
		    debug_mesg "Only dasd-eckd or dasd-fba are supported."
		    exit 9
		fi
	    fi
	    ;;
	*)
	    break;
	    ;;
    esac
    shift
done

if [ $DASD_FORCE -eq 0 ]; then
    # Get the mount point for sysfs
    while read MNTPT MNTDIR MNTSYS MNTTYPE; do
	if test "$MNTSYS" = "sysfs"; then
	    SYSFS="$MNTDIR"
	    break;
	fi 
    done </proc/mounts

    if [ -z "$SYSFS" ]; then
	mesg "/sysfs not present"
	exit 1
    fi
else
    CCW_CHAN_NAME=$DASD_TYPE
    case $DASD_TYPE in
	*eckd)
	    DISCIPLINE=ECKD
	    ;;
	*fba)
	    DISCIPLINE=FBA
	    ;;
	*)
	    mesg "Only dasd-eckd or dasd-fba are supported."
	    exit 9
	    ;;
    esac
fi # First instance of if [ $DASD_FORCE -eq 0 ]

CCW_CHAN_ID=$1
ONLINE=$2
USE_DIAG=$3

[ -z "$USE_DIAG" ] && USE_DIAG=0

if [ -z "$ONLINE" ] || [ "$ONLINE" -ne "1" -a "$ONLINE" -ne "0" ]; then
    mesg "Invalid device status $ONLINE"
    mesg "It must be a zero or a one."
    exit 2
fi

if [ $DASD_FORCE -eq 0 ]; then
    _ccw_dir=${SYSFS}/bus/ccw/devices

    debug_mesg "Configuring device ${CCW_CHAN_ID}"
    _ccw_status_dir="$_ccw_dir/$CCW_CHAN_ID"

    if test ! -d "$_ccw_status_dir" ; then
	if test "$ONLINE" -eq 1 ; then
	    mesg "No device ${CCW_CHAN_ID}"
	    exit 3
	fi
	_ccw_dev_status=0
    else
	read _cu_type < $_ccw_status_dir/cutype
	read _dev_type < $_ccw_status_dir/devtype

	case "$_cu_type" in
	    3990/*|2105/*|2107/*|1750/*|9343/*)
		CCW_CHAN_NAME="dasd-eckd"
		MODULE=dasd_eckd_mod
		DISCIPLINE=ECKD
		;;
	    6310/*)
		CCW_CHAN_NAME="dasd-fba"
		MODULE=dasd_fba_mod
		DISCIPLINE=FBA
		;;
	    3880/*)
		case "$_dev_type" in
		    3390/*)
			CCW_CHAN_NAME="dasd-eckd"
			MODULE=dasd_eckd_mod
			DISCIPLINE=ECKD
			;;
		    3370/*)
			CCW_CHAN_NAME="dasd-fba"
			MODULE=dasd_fba_mod
			DISCIPLINE=FBA
			;;
		    *)
			CCW_CHAN_NAME=
			;;
		esac
		;;
	    *)
		CCW_CHAN_NAME=
		;;
	esac

	if [ -z "$CCW_CHAN_NAME" ]; then
	    mesg "Not a DASD device (cu $_cutype, dev $_devtype)"
	    exit 5
	fi

	# Check for modules
	if test ! -d "${SYSFS}/bus/ccw/drivers/${CCW_CHAN_NAME}" ; then
	    /sbin/modprobe $MODULE

	    # Re-check whether module loading has succeeded
	    if test ! -d "${SYSFS}/bus/ccw/drivers/${CCW_CHAN_NAME}"; then
		mesg "Could not load module ${MODULE}"
		exit 6
	    fi
	fi

	read _ccw_dev_status < $_ccw_status_dir/online
    fi # if test ! -d "$_ccw_status_dir"

    #
    # We check whether we are running under z/VM by looking for the string
    # "Control Program: z/VM" in /proc/sysinfo
    #
    /bin/grep "Control Program: z/VM" /proc/sysinfo 2>&1 > /dev/null
    if [ -x /sbin/vmcp -a $? -eq 0 ]; then
	# Unconditionally load the vmcp module, loader might be broken
	[ -x /sbin/modprobe ] && /sbin/modprobe -q vmcp
	# Wait until udev is settled
	[ -x /sbin/udevadm ] && /sbin/udevadm settle --timeout=30

	# Check read-only status of virtual DASDs from z/VM
	if /sbin/vmcp q v dasd > /dasd_attr.lst 2> /dev/null; then
	    while read x dev type label attr1 attr2 rest; do
		dev=`echo $dev|tr A-F a-f`
		if test "$type" = "ON"; then
		    attr="$attr2"
		else
		    attr="$attr1"
		fi
		if [ "$CCW_CHAN_ID" = "0.0.$dev" ]; then
		    if test "$attr" = "R/O"; then
			_ccw_use_readonly="1"
		    fi
		fi
	    done < /dasd_attr.lst
	fi
	rm -f /dasd_attr.lst
    fi # if [ -x /sbin/vmcp -a $? -eq 0 ]

    if [ "$ONLINE" -eq 1 ]; then
	# Check whether we need to do something
	read _ccw_use_diag < $_ccw_status_dir/use_diag

	if [ "$_ccw_use_diag" -ne "$USE_DIAG" ] && 
	    [ "$_ccw_dev_status" -eq 1 ] ; then
	    # We need to change the DIAG access mode
	    # so we have to set the device offline first
	    debug_mesg "Setting device offline for DIAG access"
	    echo "0" > $_ccw_status_dir/online
	    # Re-read device status
	    read _ccw_dev_status < $_ccw_status_dir/online
	    if [ "$_ccw_dev_status" -ne 0 ]; then
		mesg "Could not set the device offline for DIAG access"
		exit 4
	    fi
	fi

	if [ "$_ccw_dev_status" -eq 0 ]; then
	    # Set readonly access if detected
	    if [ "$_ccw_use_readonly" ]; then
		debug_mesg "Setting device read-only"
		echo 1 > $_ccw_status_dir/readonly
	    fi

	    if [ "$USE_DIAG" -eq 1 ]; then
		# Load the diag module if possible
		[ -x /sbin/modprobe ] && /sbin/modprobe -q dasd_diag_mod
		_has_dasd_diag=$(/bin/sed -n '/^dasd_diag_mod/p' /proc/modules)
		if [ "$_has_dasd_diag" ]; then
		    # DIAG mode is special:
		    # We can only be sure if DIAG is available
		    # _after_ we have activated the device
		    debug_mesg "Activating DIAG access mode"
		    echo "$USE_DIAG" > $_ccw_status_dir/use_diag
		    read _ccw_use_diag < $_ccw_status_dir/use_diag
		    # Set the device online
		    debug_mesg "Setting device online"
		    echo "1" > $_ccw_status_dir/online 2>/dev/null
		    # Re-read device status
		    read _ccw_dev_status < $_ccw_status_dir/online
		    if [ "$_ccw_dev_status" -eq 0 ]; then
			mesg "Could not activate DIAG access mode for device ${CCW_CHAN_ID}"
			USE_DIAG=0
			echo "$USE_DIAG" > $_ccw_status_dir/use_diag
			# Set the device online
			debug_mesg "Setting device online"
			echo "1" > $_ccw_status_dir/online
		    else
			MODULE=dasd_diag_mod
		    fi
		else
		    debug_mesg "DIAG mode not available"
		    USE_DIAG=0
		    # Set the device online
		    debug_mesg "Setting device online"
		    echo "1" > $_ccw_status_dir/online
		fi # if [ "$_has_dasd_diag" ];
	    else
		if [ "$_ccw_use_diag" -eq 1 ] ; then
		    debug_mesg "Deactivating DIAG access mode"
		    echo "0" > $_ccw_status_dir/use_diag
		    read _ccw_use_diag < $_ccw_status_dir/use_diag
		fi
		# Set the device online
		debug_mesg "Setting device online"
		echo "1" > $_ccw_status_dir/online
	    fi # if [ "$USE_DIAG" -eq 1 ]

	    # Re-read device status
	    read _ccw_dev_status < $_ccw_status_dir/online
	    if [ "$_ccw_dev_status" -eq 0 ]; then
		mesg "Could not set device ${CCW_CHAN_ID} online"
		exit 4
	    fi

	    # Wait for the device to come online
	    read _dasd_state < $_ccw_status_dir/status
	    i=0
	    while [ "$_dasd_state" != "online" ] ; do
		if [ "$_dasd_state" = "unformatted" ] ; then
		    mesg "Device ${CCW_CHAN_ID} is unformatted"
		    exitcode=8
		    break
		fi
		[ $i -gt 30 ] && break
		i=$(expr $i + 1)
		sleep .1
		read _dasd_state < $_ccw_status_dir/status
	    done
	else
	    debug_mesg "Device ${CCW_CHAN_ID} is already online"
	fi # if [ "$_ccw_dev_status" -eq 0 ]

	read _dasd_state < $_ccw_status_dir/status
	if [ "$_dasd_state" != "online" ] && [ "$_dasd_state" != "unformatted" ] ; then
	    mesg "Failed to activate device ${CCW_CHAN_ID}, device in state $_dasd_state"
	    exit 7
	fi
    else
	if [ "$_ccw_dev_status" -eq 1 ]; then
	    # Set the device offline
	    debug_mesg "Setting device offline"
	    echo "$ONLINE" > $_ccw_status_dir/online

	    # Re-read to check whether we have succeeded
	    _ccw_dev_status=$(cat $_ccw_status_dir/online)
	    if [ "$?" -ne  0 -o "$_ccw_dev_status" -ne "$ONLINE" ]; then
		mesg "Could not set device ${CCW_CHAN_ID} offline"
		exit 4
	    fi
	else
	    debug_mesg "Device ${CCW_CHAN_ID} is already offline"
	fi

	if [ -d "$_ccw_status_dir" ] ; then
	    # Always disabling DIAG access
	    echo "0" > $_ccw_status_dir/use_diag
	fi

	# Set readonly access if detected
	if [ "$_ccw_use_readonly" ]; then
	    debug_mesg "Setting device read-only"
	    echo 1 > $_ccw_status_dir/readonly
	fi
    fi # if [ "$ONLINE" -eq 1 ]

    # Wait until udev is settled
    [ -x /sbin/udevadm ] && /sbin/udevadm settle --timeout=30

fi # Second instance of if [ $DASD_FORCE -eq 0 ]

if [ $DEBUG = "no" ]; then
    RULES_DIR=/etc/udev/rules.d
else
    RULES_DIR=.
fi

RULES_FILE=51-dasd-${CCW_CHAN_ID}.rules

if [ -d "$RULES_DIR" ]; then
    if [ -f ${RULES_DIR}/${RULES_FILE} ]; then
	rm -f ${RULES_DIR}/${RULES_FILE}
    fi
    remove_channel_for_cio "${CCW_CHAN_ID}"

    if [ "$ONLINE" -eq "1" ]; then
	add_channel_for_cio "${CCW_CHAN_ID}"
	# Write a new hwcfg file
	cat > ${RULES_DIR}/${RULES_FILE} <<EOF
# Configure DASD device at ${CCW_CHAN_ID} (${DISCIPLINE} mode)
ACTION=="add", SUBSYSTEM=="ccw", KERNEL=="${CCW_CHAN_ID}", IMPORT{program}="collect ${CCW_CHAN_ID} %k ${CCW_CHAN_ID} ${CCW_CHAN_NAME}"
ACTION=="add", SUBSYSTEM=="drivers", KERNEL=="${CCW_CHAN_NAME}", IMPORT{program}="collect ${CCW_CHAN_ID} %k ${CCW_CHAN_ID} ${CCW_CHAN_NAME}"
EOF
	if [ "$USE_DIAG" -eq 1 ]; then
	    cat >> ${RULES_DIR}/${RULES_FILE} <<EOF
ACTION=="add", ENV{COLLECT_${CCW_CHAN_ID}}=="0", ATTR{[ccw/$CCW_CHAN_ID]use_diag}="1"
EOF
	fi
	cat >> ${RULES_DIR}/${RULES_FILE} <<EOF
ACTION=="add", ENV{COLLECT_${CCW_CHAN_ID}}=="0", ATTR{[ccw/$CCW_CHAN_ID]online}="1"
EOF
    fi
fi

exit $exitcode
