1b9a64e43b
USB_RECOV.hex is an extra firmware that may be useful for recovering from certain Astribank hardware faults in some scenarios. This adds support for either loading it manually ('/usr/share/dahdi/xpp_fxloader recover-sb') or through udev, *if* the required udev rules were set on the system. Signed-off-by: Tzafrir Cohen <tzafrir.cohen@xorcom.com> git-svn-id: http://svn.astersk.org/svn/dahdi/tools/trunk@10349 17933a7a-c749-41c5-a318-cba88f637d49
553 lines
13 KiB
Bash
553 lines
13 KiB
Bash
#!/bin/bash
|
|
|
|
# xpp_fxloader: load Xorcom Astribank (XPP) firmware
|
|
# $Id$
|
|
#
|
|
# Written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
|
|
# Copyright (C) 2006-2009, Xorcom
|
|
#
|
|
# All rights reserved.
|
|
#
|
|
# 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 of the License, 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.
|
|
#
|
|
#
|
|
# This script can be run manually or from hotplug/udev.
|
|
#
|
|
# Firmware files should be located in $FIRMWARE_DIR which defaults:
|
|
# 1. /usr/share/dahdi
|
|
# 2. Can be overidden by setting $FIRMWARE_DIR in the environment
|
|
# 3. Can be overidden by setting $FIRMWARE_DIR in /etc/dahdi/init.conf
|
|
#
|
|
# Manual Run
|
|
# ##########
|
|
#
|
|
# path/to/xpp_fxloader load
|
|
#
|
|
# Make sure the firmware files are in $FIRMWARE_DIR
|
|
#
|
|
|
|
set -e
|
|
|
|
# Make sure fxload is in the path:
|
|
PATH="$PATH:/usr/local/sbin:/sbin:/usr/sbin"
|
|
export PATH
|
|
|
|
me=`basename $0`
|
|
dir=`dirname $0`
|
|
PATH="$dir:$PATH"
|
|
DEFAULTS="/etc/dahdi/init.conf"
|
|
|
|
if [ -t 2 ]; then
|
|
LOGGER="logger -i -t '$me' -s"
|
|
else
|
|
LOGGER="logger -i -t '$me'"
|
|
fi
|
|
|
|
debug() {
|
|
[ "$DEBUG" != "" ] && $LOGGER "$@"
|
|
return 0
|
|
}
|
|
|
|
USBFS_PREFIX=/proc/bus/usb
|
|
DEVUSB_PREFIX=/dev/bus/usb
|
|
USB_PREFIX=
|
|
|
|
FIRMWARE_DIR="${FIRMWARE_DIR:-/usr/share/dahdi}"
|
|
ASTRIBANK_HEXLOAD=${ASTRIBANK_HEXLOAD:-/usr/sbin/astribank_hexload}
|
|
ASTRIBANK_TOOL=${ASTRIBANK_TOOL:-/usr/sbin/astribank_tool}
|
|
XPP_CONFIG="${XPP_CONFIG:-/etc/dahdi/xpp.conf}"
|
|
XPP_UDEV_SLEEP_TIME="${XPP_UDEV_SLEEP_TIME:-15}"
|
|
|
|
USB_RECOV="${USB_RECOV:-USB_RECOV.hex}"
|
|
|
|
if [ -r "$DEFAULTS" ]; then
|
|
. "$DEFAULTS"
|
|
fi
|
|
|
|
if [ "$USB_PREFIX" = '' ]; then
|
|
if [ -d "$DEVUSB_PREFIX" ]; then
|
|
USB_PREFIX=$DEVUSB_PREFIX
|
|
elif [ -r "$USBFS_PREFIX/devices" ]; then
|
|
USB_PREFIX=$USBFS_PREFIX
|
|
fi
|
|
fi
|
|
|
|
# With Kernels older that 2.6.10 it seems to be possible
|
|
# to trigger a race condition by running fxload or fpga_load
|
|
# immediately after the detection of the device.
|
|
KERNEL_HAS_USB_RACE=0
|
|
case "`uname -r`" in 2.6.[89]*) KERNEL_HAS_USB_RACE=1;; esac
|
|
sleep_if_race() {
|
|
if [ "$KERNEL_HAS_USB_RACE" = '1' ]; then
|
|
sleep 2
|
|
fi
|
|
}
|
|
|
|
find_dev() {
|
|
v_id=$1
|
|
p_id=$2
|
|
|
|
lsusb | tr -d : | awk "/ ID $v_id$p_id/{printf \"$USB_PREFIX/%s/%s \",\$2,\$4}"
|
|
}
|
|
|
|
run_fxload() {
|
|
sleep_if_race
|
|
fxload -t fx2 $* 2>&1 1>/dev/null | $LOGGER
|
|
status=$PIPESTATUS
|
|
if [ $status != 0 ]; then
|
|
$LOGGER "fxload failed with status $status"
|
|
exit 55
|
|
fi
|
|
}
|
|
|
|
list_via_proc() {
|
|
cat /proc/bus/usb/devices | egrep '^P:|^T:' | sed \
|
|
-e '/^T:/s/ *Spd.*//' \
|
|
-e '/^T:/s, *Lev.*Dev#= *,\t,' \
|
|
-e '/^T:/s,Bus=,,' \
|
|
-e '/^P:/s,[A-Za-z]\+=,,g' \
|
|
-e '/^P:/s,\.,,g' | awk -vusb_prefix="$USB_PREFIX" '
|
|
/^T:/ {
|
|
bus=$2
|
|
dev=$3
|
|
}
|
|
/^P:/ {
|
|
vendor=$2
|
|
sub("0x", "", vendor);
|
|
prod=$3
|
|
sub("0x", "", product);
|
|
bcd=$4
|
|
printf("%4s/%4s/%d\t%s/%03d/%03d\n",
|
|
vendor, prod, bcd, usb_prefix, bus, dev);
|
|
}
|
|
'
|
|
}
|
|
|
|
list_via_sysfs() {
|
|
find /sys/bus/usb/devices -maxdepth 1 -mindepth 1 | \
|
|
egrep -v '/usb[0-9]|:' | while read dev; do
|
|
(
|
|
cat "$dev/idVendor"
|
|
cat "$dev/idProduct"
|
|
cat "$dev/bcdDevice"
|
|
echo "$dev" | sed \
|
|
-e 's,/sys/bus/usb/devices/,,' \
|
|
-e 's,-.*,,'
|
|
cat "$dev/devnum"
|
|
) | tr -s '\n' '\t'
|
|
echo ''
|
|
done | awk -vusb_prefix="$USB_PREFIX" '{
|
|
printf("%4s/%4s/%d\t%s/%03d/%03d\n",
|
|
$1, $2, $3, usb_prefix, $4, $5);
|
|
}'
|
|
}
|
|
|
|
list_via_lsusb() {
|
|
lsusb -v | awk -vusb_prefix="$USB_PREFIX" '
|
|
/^Bus/ {
|
|
sub(":", "", $4);
|
|
dev = sprintf("%s/%s/%s ", usb_prefix, $2, $4);
|
|
}
|
|
/idVendor/ {
|
|
id_vendor = $2
|
|
sub("0x", "", id_vendor);
|
|
}
|
|
/idProduct/ {
|
|
id_product = $2
|
|
sub("0x", "", id_product);
|
|
}
|
|
/bcdDevice/ {
|
|
bcd_device = $2
|
|
sub("^0*", "", bcd_device);
|
|
sub("[.]", "", bcd_device);
|
|
printf("%s/%s/%s\t%s\n",
|
|
id_vendor, id_product, bcd_device, dev);
|
|
}
|
|
'
|
|
}
|
|
|
|
list_devs() {
|
|
#echo >&2 "list_devs"
|
|
if [ "$#" -eq 0 ]; then
|
|
if [ -f /proc/bus/usb/devices ]; then
|
|
method='via_proc'
|
|
elif [ -d /sys/bus/usb/devices ]; then
|
|
method='via_sysfs'
|
|
else
|
|
method='via_lsusb'
|
|
fi
|
|
elif [ "$#" -eq 1 ]; then
|
|
method="$1"
|
|
else
|
|
echo >&2 "$0: unknown list_devs method='$method'"
|
|
exit 1
|
|
fi
|
|
|
|
case "$method" in
|
|
via_proc|via_sysfs|via_lsusb)
|
|
;;
|
|
*)
|
|
echo >&2 "$0: unknown list_devs method='$method'"
|
|
exit 1
|
|
;;
|
|
esac
|
|
list_$method | grep -v '^0000/0000/' | sort
|
|
}
|
|
|
|
filter_devs() {
|
|
id_str="$1"
|
|
|
|
#echo >&2 "filter_devs($id_str)"
|
|
list_devs | awk -vid_str="$id_str" '{ if ($1 ~ id_str) { print } }'
|
|
}
|
|
|
|
usb_firmware_device() {
|
|
id_str="$1"
|
|
devpath="$2"
|
|
|
|
case "$id_str" in
|
|
e4e4/11[3456]0/101|e4e4/1163/101)
|
|
fw="USB_FW.hex"
|
|
;;
|
|
e4e4/116[03]/201)
|
|
fw="USB_FW.201.hex"
|
|
;;
|
|
e4e4/*)
|
|
debug "No USB firmware for device $devpath ($id_str)"
|
|
return
|
|
;;
|
|
*)
|
|
return
|
|
;;
|
|
esac
|
|
fw_file="$FIRMWARE_DIR/$fw"
|
|
ver=$(awk '/\$Id:/ { print $4 }' $fw_file)
|
|
debug "USB Firmware $fw_file (Version=$ver) into $devpath"
|
|
run_fxload -D "$devpath" -I "$fw_file" || exit 1
|
|
}
|
|
|
|
run_astribank_hexload() {
|
|
debug "Running: $ASTRIBANK_HEXLOAD $*"
|
|
$ASTRIBANK_HEXLOAD "$@" | $LOGGER
|
|
status=$PIPESTATUS
|
|
if [ $status != 0 ]; then
|
|
$LOGGER "$ASTRIBANK_HEXLOAD failed with status $status"
|
|
exit 77
|
|
fi
|
|
}
|
|
|
|
run_astribank_tool() {
|
|
debug "Running: $ASTRIBANK_TOOL $*"
|
|
$ASTRIBANK_TOOL "$@" | $LOGGER
|
|
status=$PIPESTATUS
|
|
if [ $status != 0 ]; then
|
|
$LOGGER "$ASTRIBANK_TOOL failed with status $status"
|
|
exit 77
|
|
fi
|
|
}
|
|
|
|
usb_firmware_all_devices() {
|
|
devs=`list_devs`
|
|
echo "USB firmware"
|
|
echo "$devs" | while read id_str devpath
|
|
do
|
|
usb_firmware_device "$id_str" "$devpath"
|
|
done
|
|
wait_renumeration $numdevs 'e4e4/11[3456]1/*' "usb_firmware_all_devices"
|
|
}
|
|
|
|
load_fw_device() {
|
|
dev="$1"
|
|
fw="$2"
|
|
debug "FPGA loading $fw into $dev"
|
|
run_astribank_hexload -D "$dev" -F "$FIRMWARE_DIR/$fw"
|
|
case "$fw" in
|
|
FPGA_1161*.hex)
|
|
echo_file="$FIRMWARE_DIR/OCT6104E-256D.ima"
|
|
law=''
|
|
law_str='uLaw'
|
|
card_type=`run_astribank_tool -D "$dev" -Q 2>&1 | grep 'CARD 4' | sed -e 's/.*type=//' -e 's/\..*//'`
|
|
caps_num=`run_astribank_tool -D "$dev" -Q 2>&1 | grep 'ECHO ports' | sed -e 's/.*: *//'`
|
|
if [ $card_type -eq 5 ]; then
|
|
debug "ECHO burning into $dev: $echo_file"
|
|
card_type=`run_astribank_tool -D "$dev" -Q 2>&1 | grep 'CARD 0' | sed -e 's/.*type=//' -e 's/\..*//'`
|
|
case "$card_type" in
|
|
3) law="-A";;
|
|
4)
|
|
pri_protocol=''
|
|
if [ -r "$XPP_CONFIG" ]; then
|
|
pri_protocol=`awk '/^pri_protocol/ {print $2}' $XPP_CONFIG`
|
|
fi
|
|
# "E1" or empty (implied E1) means aLaw
|
|
if [ "$pri_protocol" != 'T1' ]; then
|
|
law='-A'
|
|
fi
|
|
;;
|
|
esac
|
|
if [ "$law" = '-A' ]; then
|
|
law_str="aLaw"
|
|
fi
|
|
caps_num=`echo "$abtool_output" | grep 'ECHO ports' | sed -e 's/.*: *//'`
|
|
debug "ECHO: 1st module is $law_str, $caps_num channels allowed."
|
|
if [ "$caps_num" != '0' ]; then
|
|
run_astribank_hexload -D "$dev" -O $law "$echo_file"
|
|
else
|
|
echo "WARNING: ECHO burning was skipped (no capabilities)"
|
|
fi
|
|
fi
|
|
pic_files=`echo "$FIRMWARE_DIR"/PIC_TYPE_[1-4].hex`
|
|
debug "PIC burning into $dev: begin $pic_files"
|
|
run_astribank_hexload -D "$dev" -p $pic_files
|
|
debug "PIC burning into $dev: end $pic_files"
|
|
;;
|
|
esac
|
|
# Do renumeration!
|
|
run_astribank_tool -D "$dev" -n > /dev/null 2>&1
|
|
debug "Renumeration of $dev done."
|
|
}
|
|
|
|
fpga_firmware_device() {
|
|
id_str="$1"
|
|
devpath="$2"
|
|
|
|
id_product=`echo "$id_str" | cut -d/ -f2`
|
|
bcd_device=`echo "$id_str" | cut -d/ -f3`
|
|
case "$id_str" in
|
|
e4e4/1131/101)
|
|
fw="FPGA_FXS.hex"
|
|
;;
|
|
e4e4/11[456]1/101)
|
|
fw="FPGA_${id_product}.hex"
|
|
;;
|
|
e4e4/1161/201)
|
|
fw="FPGA_${id_product}.${bcd_device}.hex"
|
|
;;
|
|
e4e4/*)
|
|
debug "No FPGA firmware for device $devpath ($id_str)"
|
|
return
|
|
;;
|
|
*)
|
|
return
|
|
;;
|
|
esac
|
|
debug "Loading $fw into $devpath"
|
|
load_fw_device "$devpath" "$fw"
|
|
sleep_if_race
|
|
}
|
|
|
|
numdevs() {
|
|
id_str="$1"
|
|
|
|
#echo >&2 "numdevs($id_str)"
|
|
filter_devs "$id_str" | wc -l
|
|
}
|
|
|
|
wait_renumeration() {
|
|
num="$1"
|
|
id_str="$2"
|
|
caller="$3"
|
|
iter=10
|
|
|
|
prev=0
|
|
echo "Waiting renumeration ($caller)"
|
|
while
|
|
n=`numdevs "$id_str"`
|
|
[ "$num" -gt "$n" ]
|
|
do
|
|
if [ "$prev" -lt "$n" ]; then
|
|
echo -n "+"
|
|
else
|
|
echo -n "."
|
|
fi
|
|
sleep 1
|
|
prev="$n"
|
|
debug "wait($iter) (found $n from $num devices) ($caller)"
|
|
if ! iter=`expr $iter - 1`; then
|
|
echo "Timeout (found $n from $num devices) ($caller)"
|
|
break;
|
|
fi
|
|
done
|
|
echo "Got all $num devices ($caller)"
|
|
sleep 1 # Let everything settle
|
|
}
|
|
|
|
fpga_firmware_all_devices() {
|
|
echo "Loading FPGA firmwares" | $LOGGER
|
|
devs=`filter_devs 'e4e4/11[3456]1/*'`
|
|
n=`echo "$devs" | wc -l`
|
|
echo "$devs" | (
|
|
while read id_str devpath; do
|
|
fpga_firmware_device "$id_str" "$devpath" &
|
|
done
|
|
sleep 1
|
|
echo "Wait for FPGA loading processes"
|
|
wait
|
|
) 2>&1 | $LOGGER
|
|
wait_renumeration $numdevs 'e4e4/11[3456]2/*' "fpga_firmware_device"
|
|
}
|
|
|
|
reset_fpga() {
|
|
devices=`filter_devs 'e4e4/11[3456][124]/*'`
|
|
totaldevs=`numdevs 'e4e4/11[3456][124]/*'`
|
|
$LOGGER -- "Reseting devices [$totaldevs devices]"
|
|
echo "$devices" | grep -v '^$' | while read id_str dev
|
|
do
|
|
(
|
|
debug "Resetting FPGA Firmware on $dev"
|
|
sleep_if_race
|
|
run_astribank_tool -D "$dev" -r full >/dev/null 2>&1
|
|
) &
|
|
done
|
|
wait
|
|
if [ "$1" = 'wait' ]; then
|
|
wait_renumeration $totaldevs 'e4e4/11[3456][03]/*' "reset_fpga"
|
|
fi
|
|
}
|
|
|
|
usage() {
|
|
echo "$0: Astribank firmware loading script."
|
|
echo "Usage: "
|
|
echo "$0 load : manual firmware loading."
|
|
echo "$0 usb : manual firmware loading: USB firmware only."
|
|
echo "$0 help : this text."
|
|
}
|
|
|
|
# We have a potential astribank
|
|
astribank_is_starting -a
|
|
|
|
#########################
|
|
##
|
|
## Manual run
|
|
##
|
|
|
|
# to run manually, pass the parameter 'xppdetect'
|
|
case "$1" in
|
|
udev)
|
|
# Various kernel versions use different sets of variables.
|
|
# Here we want to make sure we have 'DEVICE' and 'PRODUCT' set
|
|
# up. DEVICE is now deprecated in favour of DEVNAME. It will
|
|
# likely to contain an invalid name if /proc/bus/usb is not
|
|
# mounted. So it needs further cooking.
|
|
DEVICE="${DEVNAME:-$DEVICE}"
|
|
case "$DEVICE" in /proc/*) DEVICE="/dev${DEVICE#/proc}" ;; esac
|
|
# PRODUCT contains 'vendor_id'/'product_id'/'version' . We
|
|
# currently pass it as a parameter, but might as well get it
|
|
# from the envirnment.
|
|
PRODUCT="${PRODUCT:-$2}"
|
|
# skip on to the rest of the script. Don't exit.
|
|
;;
|
|
reset-wait)
|
|
reset_fpga wait
|
|
;;
|
|
reset)
|
|
reset_fpga
|
|
;;
|
|
list)
|
|
filter_devs 'e4e4/*/*'
|
|
exit 0
|
|
;;
|
|
xppdetect|load|usb)
|
|
numdevs=`numdevs 'e4e4/11[3456][0134]/*'`
|
|
$LOGGER -- "--------- FIRMWARE LOADING: ($1) [$numdevs devices]"
|
|
|
|
usb_firmware_all_devices 2>&1 | $LOGGER
|
|
if [ "$1" != 'usb' ]
|
|
then
|
|
fpga_firmware_all_devices
|
|
fi
|
|
|
|
$LOGGER -- "--------- FIRMWARE IS LOADED"
|
|
exit 0
|
|
;;
|
|
recover-sb)
|
|
# Load a firmware that fixes a but which makes the Source Byte in the
|
|
# EEPROM reset and make the device appear like a Cypress dev kit:
|
|
load_usb_fw 04b4 8613 $USB_RECOV
|
|
;;
|
|
help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
if [ "$ACTION" = '' ]; then # not called from hotplug
|
|
echo "$0: Error: unknown command \"$1\""
|
|
echo ''
|
|
usage
|
|
exit 1
|
|
fi
|
|
;;
|
|
esac
|
|
|
|
#########################
|
|
##
|
|
## Hotplug run
|
|
##
|
|
|
|
# allow disabling automatic hotplugging:
|
|
if [ "$XPP_HOTPLUG_DISABLED" != '' ]; then
|
|
$LOGGER -p kern.info "Exiting... XPP_HOTPLUG_DISABLED"
|
|
exit 0
|
|
fi
|
|
|
|
if [ "$ACTION" != add ]; then
|
|
exit 0;
|
|
fi
|
|
|
|
# This procedure is run in the background to do the actual work of loading the
|
|
# firmware. Running it in the background allows udev to continue doing other tasks
|
|
# and thus provide a faster startup.
|
|
#
|
|
# On some systems (e.g. CentOS 5) we get the relevant udev event before the device
|
|
# file is ready. Which is why we want the background process to wait a bit first.
|
|
udev_delayed_load() {
|
|
sleep 0.2
|
|
# Make sure the new device is writable:
|
|
usb_dev_writable=0
|
|
for i in `seq $XPP_UDEV_SLEEP_TIME`; do
|
|
if [ -w "$DEVICE" ]; then
|
|
usb_dev_writable=1;
|
|
break;
|
|
fi
|
|
sleep 1
|
|
done
|
|
if [ $usb_dev_writable != 1 ]; then
|
|
$LOGGER "Device $DEVICE not writable. Can't load firmware."
|
|
return;
|
|
fi
|
|
|
|
$LOGGER "Trying to find what to do for product $PRODUCT, device $DEVICE"
|
|
case "$PRODUCT" in
|
|
4b4/8613/*)
|
|
# This case is for a potentially-broken Astribank.
|
|
# In most systems you should not set udev rules for those to
|
|
# get here, as this is actually the ID of a Cypress dev-kit:
|
|
FIRM_USB="$FIRMWARE_DIR/$USB_RECOV"
|
|
$LOGGER "Loading recovery firmware '$FIRM_USB' into '$DEVICE'"
|
|
run_fxload -D "$DEVICE" -I "$FIRM_USB"
|
|
;;
|
|
e4e4/11[3456]0/*|e4e4/1163/*)
|
|
usb_firmware_device "$PRODUCT" "$DEVICE"
|
|
;;
|
|
e4e4/11[3456]1/*)
|
|
sleep_if_race
|
|
fpga_firmware_device "$PRODUCT" "$DEVICE" &
|
|
wait # parallel firmware loading
|
|
;;
|
|
esac
|
|
}
|
|
|
|
udev_delayed_load 2>&1 | $LOGGER &
|
|
|