#!/bin/bash
# $Id: pci.rc 303 2005-01-20 08:59:56Z hare $
# vim: syntax=sh
#
# pci.rc	mostly to recover lost boot-time pci hotplug events
#

export LOGPID=$$

cd /etc/hotplug
. hotplug.functions

pci_call_hotplug () {
	# There may be /etc/hotplug.d/default/eventrecorder.hotplug
	# that logs the environment of every hotplug events. Therefore
	# we keep environment clean.
#	echo " "\
	eval \
	$ENV \
	${ACTION:+ ACTION=$ACTION} \
	${DEVPATH:+ DEVPATH="$DEVPATH"} \
	${PCI_SLOT_NAME:+ PCI_SLOT_NAME="$PCI_SLOT_NAME"} \
	${PCI_CLASS:+ PCI_CLASS="$PCI_CLASS$PCI_PROGIF"} \
	${PCI_ID:+ PCI_ID="$PCI_ID"} \
	${PCI_SUBSYS_ID:+ PCI_SUBSYS_ID="$PCI_SUBSYS_ID"} \
	${HOTPLUG_TRACE:+ HOTPLUG_TRACE="$HOTPLUG_TRACE"} \
	$HOTPLUG_BIN pci \
	< /dev/null > /dev/null 2>&1 & 
}

pci_boot_events_nosysfs () {
	if [ ! -x /sbin/lspci -o ! -f /proc/bus/pci/devices ]; then
	    debug_mesg " ** can't synthesize pci hotplug events"
	    return
	fi
	/sbin/lspci -nm | while read pci_data ; do
		IFS="$IFS\""
		eval set -- $pci_data 
		PCI_SLOT_NAME=$1
		shift 2
		PCI_CLASS=$1
		shift
		D=""
		while read x I x x x x x x x x x x x x x x x D; do
			test "$I" != "$1$2" && continue
			if [ -n "$D" ] ; then
				echo -n . >&3
				debug_mesg -n ". `lspci -s ${PCI_SLOT_NAME/0000:/}` -> "
				debug_mesg "driven by $D"
				continue 2
			fi
		done < /proc/bus/pci/devices
		PCI_ID=$1:$2
		shift 2
		PCI_PROGIF="00"
		case "$1" in
			-r*)
				shift
				;;
			-p*)
				PCI_PROGIF=${1/-p/}
				shift
				;;
		esac
		if [ "$1" = "" ] ; then
			PCI_SUBSYS_ID="0:0"
		else
			PCI_SUBSYS_ID="$1:$2"
		fi
		debug_mesg "* `lspci -s ${PCI_SLOT_NAME/0000:/}` -> generating event"
		echo -n "*" >&3
		pci_call_hotplug
	done
}

cat_0x () {
	test -r "$1" || return
	# sed -e 's/^0x//' $1
	local x=`cat $1`
	echo ${x/0x/}
}

pci_boot_events_sysfs () {
	# $1 == class to load in hex
	local OK USE SKIP TAIL
	cd /sys/bus/pci/devices
	for i in * ; do
		PCI_SLOT_NAME=$i
		PCI_CLASS="`cat_0x $i/class`"
		
		# If wrong class then continue
		if [ -n "$1" -a "$a" != "$PCI_CLASS" ] ; then
			continue;
		fi

		# Handle only some pci classes
		OK=no
		for USE in $COLDPLUG_PCI_CLASSES_WHITELIST; do
			TAIL=${PCI_CLASS##$USE}
			if [ ${#TAIL} -le 4 ] ; then
				OK=yes
			fi
		done
		if [ "$OK" = no -a -n "$COLDPLUG_PCI_CLASSES_WHITELIST" ] ; then
			debug_mesg "W `lspci -s ${PCI_SLOT_NAME/0000:/}` -> skipped"
			echo -n W >&3
			continue;
		fi
		# Skip some classes
		for SKIP in $COLDPLUG_PCI_CLASSES_BLACKLIST; do
			# The numbers in COLDPLUG_PCI_CLASSES_BLACKLIST must not specify
			# prog-if. Therfore check if at least the first four digits match.
			TAIL=${PCI_CLASS##$SKIP}
			# debug_mesg $PCI_CLASS $SKIP $TAIL
			if [ ${#TAIL} -le 4 ] ; then
				debug_mesg "B `lspci -s ${PCI_SLOT_NAME/0000:/}` -> skipped"
				echo -n B >&3
				continue 2
			fi
		done

		# If device is already driven then continue
		if [ -d /sys/bus/pci/drivers/*/$i \
		     -a "$SKIP_DRIVEN_DEVICES" != no ] ; then
			echo -n . >&3
			i=${i%/*}; i=${i##*/}
			debug_mesg ". `lspci -s ${PCI_SLOT_NAME/0000:/}` -> driven by $i"
			continue
		fi
	
		PCI_ID="`cat_0x $i/vendor`:`cat_0x $i/device`"
		PCI_SUBSYS_ID="`cat_0x $i/subsystem_vendor`"
		PCI_SUBSYS_ID="$PCI_SUBSYS_ID:`cat_0x $i/subsystem_device`"

		DEVPATH=`cd $i/ ; /bin/pwd`
		DEVPATH=${DEVPATH/\/sys/}
		
		echo -n "*" >&3
		debug_mesg "* `lspci -s ${PCI_SLOT_NAME/0000:/}` -> generating event"
		pci_call_hotplug
	done
}

pci_boot_events ()
{
	local ENV
	if [ -x /usr/bin/env ] ; then
		ENV='env -i - '
	else
		ENV=
	fi

	if [ -d /sys/bus/pci/devices ] ; then
		# We need module acpiphp for pci hotplug. I don't know how to decide when
		# we need that module. Therefore i'll make configurable via
		# /etc/sysconfig/hotplug:HOTPLUG_DO_REAL_PCI_HOTPLUG
		if [ "$HOTPLUG_DO_REAL_PCI_HOTPLUG" = yes ] ; then
			$MODPROBE acpiphp
		fi
		# There are devices with ohci and ehci chip. The ehci chip
		# provides USB 2 and should be preferred normally. But
		# mostly the ohci chip comes first and would therefore be
		# initialized. Therefore we look for ehci chips first.
		pci_boot_events_sysfs 0c0320
		# echo -n "|" >&3
		# load remaining drivers
		pci_boot_events_sysfs ""
	else
		pci_boot_events_nosysfs
	fi
	# echo >&3
}

# See how we were called.
case "$1" in
  start)
	ACTION=add
	pci_boot_events;
	;;
  stop)
	# ACTION=remove
	# SKIP_DRIVEN_DEVICES=no;
	# pci_boot_events;
	;;
  status)
	if [ -f /proc/bus/pci/devices ]; then
	    BUS_COUNT="`ls -d /proc/bus/pci/* 2>/dev/null | grep -c .`"
	    DEV_COUNT="`ls -d /proc/bus/pci/*/* 2>/dev/null | grep -c .`"
	    if [ $BUS_COUNT -gt 1 ]; then
		BUS_COUNT=`expr $BUS_COUNT - 1`
		echo $"PCI up; bus count is $BUS_COUNT, device count is $DEV_COUNT"
	    else
		echo $"no PCI busses?"
	    fi
	else
	    echo $"no PCI /proc support?"
	fi
  	;;
  status-verbose)
	echo $"PCI Status for kernel: "  `uname -srm`
	echo ''

	if [ -f /proc/bus/pci/devices ]; then
	    BUS_COUNT="`ls -d /proc/bus/pci/* 2>/dev/null | grep -c .`"
	    DEV_COUNT="`ls -d /proc/bus/pci/*/* 2>/dev/null | grep -c .`"
	    if [ $BUS_COUNT -gt 1 ]; then
		BUS_COUNT=`expr $BUS_COUNT - 1`
		echo $"PCI up; bus count is $BUS_COUNT, device count is $DEV_COUNT"
		if [ -x /sbin/lspci ]; then
		    /sbin/lspci
		fi
	    else
		echo $"no PCI busses?"
	    fi
	    echo ''
	else
	    echo $"no PCI /proc support?"
	fi
	echo ''

	;;
  restart)
	# always invoke by absolute path, else PATH=$PATH:
	$0 stop && $0 start
	;;
  *)
        echo $"Usage: $0 {start|stop|status[-verbose]|restart}"
        exit 1
	;;
esac
