Typos. Mostly by Lintian
[dahdi/tools.git] / xpp / xpp_fxloader
1 #!/bin/bash
2
3 # xpp_fxloader: load Xorcom Astribank (XPP) firmware
4 # $Id$
5 #
6 # Written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
7 # Copyright (C) 2006-2009, Xorcom
8 #
9 # All rights reserved.
10 #
11 # This program is free software; you can redistribute it and/or modify
12 # it under the terms of the GNU General Public License as published by
13 # the Free Software Foundation; either version 2 of the License, or
14 # (at your option) any later version.
15 #
16 # This program is distributed in the hope that it will be useful,
17 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 # GNU General Public License for more details.
20 #
21 # You should have received a copy of the GNU General Public License
22 # along with this program; if not, write to the Free Software
23 # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 #
25 #
26 # This script can be run manually or from hotplug/udev.
27 #
28 # Firmware files should be located in $FIRMWARE_DIR which defaults:
29 #       1. /usr/share/dahdi
30 #       2. Can be overidden by setting $FIRMWARE_DIR in the environment
31 #       3. Can be overidden by setting $FIRMWARE_DIR in /etc/dahdi/init.conf
32 #
33 # Manual Run
34 # ##########
35 #
36 #   path/to/xpp_fxloader load
37 #
38 # Make sure the firmware files are in $FIRMWARE_DIR
39 #
40
41 set -e
42
43 # Make sure fxload is in the path:
44 PATH="$PATH:/usr/local/sbin:/sbin:/usr/sbin"
45 export PATH
46
47 me=`basename $0`
48 dir=`dirname $0`
49 PATH="$dir:$PATH"
50 DEFAULTS="/etc/dahdi/init.conf"
51
52 if [ -t 2 ]; then
53         LOGGER="logger -i -t '$me' -s"
54 else
55         LOGGER="logger -i -t '$me'"
56 fi
57
58 debug() {
59         [ "$DEBUG" != "" ] && echo >&2 "$@"
60         return 0
61 }
62
63 USBFS_PREFIX=/proc/bus/usb
64 DEVUSB_PREFIX=/dev/bus/usb
65 USB_PREFIX=
66
67 FIRMWARE_DIR="${FIRMWARE_DIR:-/usr/share/dahdi}"
68 ASTRIBANK_HEXLOAD=${ASTRIBANK_HEXLOAD:-/usr/sbin/astribank_hexload}
69 ASTRIBANK_TOOL=${ASTRIBANK_TOOL:-/usr/sbin/astribank_tool}
70 XPP_CONFIG="${XPP_CONFIG:-/etc/dahdi/xpp.conf}"
71 SPAN_TYPES_CONFIG="${SPAN_TYPES_CONFIG:-/etc/dahdi/span-types.conf}"
72 XPP_UDEV_SLEEP_TIME="${XPP_UDEV_SLEEP_TIME:-15}"
73
74 USB_RECOV="${USB_RECOV:-USB_RECOV.hex}"
75
76 if [ -r "$DEFAULTS" ]; then
77         . "$DEFAULTS"
78 fi
79
80 if [ "$USB_PREFIX" = '' ]; then
81         if [ -d "$DEVUSB_PREFIX" ]; then
82                 USB_PREFIX=$DEVUSB_PREFIX
83         elif [ -r "$USBFS_PREFIX/devices" ]; then
84                 USB_PREFIX=$USBFS_PREFIX
85         fi
86 fi
87
88 # With Kernels older that 2.6.10 it seems to be possible
89 # to trigger a race condition by running fxload or fpga_load 
90 # immediately after the detection of the device.
91 KERNEL_HAS_USB_RACE=0
92 case "`uname -r`" in 2.6.[89]*) KERNEL_HAS_USB_RACE=1;; esac
93 sleep_if_race() {
94   if [ "$KERNEL_HAS_USB_RACE" = '1' ]; then
95     sleep 2
96   fi
97 }
98
99 find_dev() {
100   v_id=$1
101   p_id=$2
102   
103   lsusb | tr -d : | awk "/ ID $v_id$p_id/{printf \"$USB_PREFIX/%s/%s \",\$2,\$4}"
104 }
105
106 run_fxload() {
107   sleep_if_race
108   fxload -t fx2 $* 2>&1 1>/dev/null
109   status=$PIPESTATUS
110   if [ $status != 0 ]; then
111     echo >&2 "fxload failed with status $status"
112     exit 55
113   fi
114 }
115
116 list_via_proc() {
117         cat /proc/bus/usb/devices | egrep '^P:|^T:' | sed \
118                 -e '/^T:/s/ *Spd.*//' \
119                 -e '/^T:/s, *Lev.*Dev#= *,\t,' \
120                 -e '/^T:/s,Bus=,,' \
121                 -e '/^P:/s,[A-Za-z]\+=,,g' \
122                 -e '/^P:/s,\.,,g' | awk -vusb_prefix="$USB_PREFIX" '
123                         /^T:/   {
124                                 bus=$2
125                                 dev=$3
126                         }
127                         /^P:/   {
128                                 vendor=$2
129                                 sub("0x", "", vendor);
130                                 prod=$3
131                                 sub("0x", "", product);
132                                 bcd=$4
133                                 printf("%4s/%4s/%d\t%s/%03d/%03d\n",
134                                         vendor, prod, bcd, usb_prefix, bus, dev);
135                         }
136                         '
137 }
138
139 list_via_sysfs() {
140         find /sys/bus/usb/devices -maxdepth 1 -mindepth 1 | \
141                 egrep -v '/usb[0-9]|:' | while read dev; do
142                         (
143                                 cat "$dev/idVendor"
144                                 cat "$dev/idProduct"
145                                 cat "$dev/bcdDevice"
146                                 echo "$dev" | sed \
147                                         -e 's,/sys/bus/usb/devices/,,' \
148                                         -e 's,-.*,,'
149                                 cat "$dev/devnum"
150                         ) | tr -s '\n' '\t'
151                         echo ''
152                 done | awk -vusb_prefix="$USB_PREFIX" '{
153                         printf("%4s/%4s/%d\t%s/%03d/%03d\n",
154                                 $1, $2, $3, usb_prefix, $4, $5);
155                         }'
156 }
157
158 list_via_lsusb() {
159         lsusb -v | awk -vusb_prefix="$USB_PREFIX" '
160                 /^Bus/ {
161                         sub(":", "", $4);
162                         dev = sprintf("%s/%s/%s ", usb_prefix, $2, $4);
163                 }
164                 /idVendor/  {
165                         id_vendor = $2
166                         sub("0x", "", id_vendor);
167                 }
168                 /idProduct/ {
169                         id_product = $2
170                         sub("0x", "", id_product);
171                 }
172                 /bcdDevice/ {
173                         bcd_device = $2
174                         sub("^0*", "", bcd_device);
175                         sub("[.]", "", bcd_device);
176                         printf("%s/%s/%s\t%s\n",
177                                 id_vendor, id_product, bcd_device, dev);
178                 }
179                 '
180 }
181
182 list_devs() {
183         #echo >&2 "list_devs"
184         if [ "$#" -eq 0 ]; then
185                 if [ -f /proc/bus/usb/devices ]; then
186                         method='via_proc'
187                 elif [ -d /sys/bus/usb/devices ]; then
188                         method='via_sysfs'
189                 else
190                         method='via_lsusb'
191                 fi
192         elif [ "$#" -eq 1 ]; then
193                 method="$1"
194         else
195                 echo >&2 "$0: unknown list_devs method='$method'"
196                 exit 1
197         fi
198
199         case "$method" in
200         via_proc|via_sysfs|via_lsusb)
201                 ;;
202         *)
203                 echo >&2 "$0: unknown list_devs method='$method'"
204                 exit 1
205                 ;;
206         esac
207         list_$method | grep -v '^0000/0000/' | sort
208 }
209
210 filter_devs() {
211         id_str="$1"
212
213         #echo >&2 "filter_devs($id_str)"
214         list_devs | awk -vid_str="$id_str" '{ if ($1 ~ id_str) { print } }'
215 }
216
217 usb_firmware_device() {
218         id_str="$1"
219         devpath="$2"
220
221         bcd_device=`echo "$id_str" | cut -d/ -f3`
222
223         case "$id_str" in
224         e4e4/11[3456]0/101|e4e4/1163/101)
225                 fw="USB_FW.hex"
226                 ;;
227         e4e4/116[03]/20?)
228                 fw="USB_FW.${bcd_device}.hex"
229                 ;;
230         e4e4/*)
231                 debug "No USB firmware for device $devpath ($id_str)"
232                 return
233                 ;;
234         *)
235                 return
236                 ;;
237         esac
238         fw_file="$FIRMWARE_DIR/$fw"
239         ver=$(awk '/\$Id:/ { print $4 }' $fw_file)
240         debug "USB Firmware $fw_file (Version=$ver) into $devpath"
241         run_fxload -D "$devpath" -I "$fw_file" || exit 1
242 }
243
244 run_astribank_hexload() {
245         debug "Running: $ASTRIBANK_HEXLOAD $*"
246         $ASTRIBANK_HEXLOAD "$@"
247         status=$PIPESTATUS
248         if [ $status != 0 ]; then
249                 echo >&2 "$ASTRIBANK_HEXLOAD failed with status $status"
250                 exit 77
251         fi
252 }
253
254 run_astribank_tool() {
255         debug "Running: $ASTRIBANK_TOOL $*"
256         $ASTRIBANK_TOOL "$@"
257         status=$PIPESTATUS
258         if [ $status != 0 ]; then
259                 echo >&2 "$ASTRIBANK_TOOL failed with status $status"
260                 exit 77
261         fi
262 }
263
264 usb_firmware_all_devices() {
265         devs=`list_devs`
266         echo "USB firmware"
267         echo "$devs" | while read id_str devpath
268         do
269                 usb_firmware_device "$id_str" "$devpath"
270         done
271         wait_renumeration $numdevs 'e4e4/11[3456]1/*' "usb_firmware_all_devices"
272 }
273
274 filter_span_types() {
275         l="$1"
276         sed < "$SPAN_TYPES_CONFIG" 2>/dev/null \
277                 -e 's/#.*//'    \
278                 -e 's/[ \t]*$//' \
279                 -e 's/^[ \t]*//' \
280                 -e '/^$/d' | awk -vlabel="$l" '$1 == label { print $2 }' | tr -s ', \t\n' ','
281 }
282
283 load_fw_device() {
284         dev="$1"
285         fw="$2"
286         debug "FPGA loading $fw into $dev"
287         run_astribank_hexload -D "$dev" -F "$FIRMWARE_DIR/$fw"
288         case "$fw" in
289         FPGA_1161*.hex)
290                 echo_file="$FIRMWARE_DIR/OCT6104E-256D.ima"
291                 law=''
292                 dev_short=`echo "$dev" | sed -e 's,.*/usb/*,,'`
293                 abtool_output=`$ASTRIBANK_TOOL -D "$dev" -Q 2>&1`
294                 ec_card_type=`echo "$abtool_output" | grep 'CARD 4' | sed -e 's/.*type=//' -e 's/\..*//'`
295                 caps_num=`echo "$abtool_output" | grep 'ECHO ports' | sed -e 's/.*: *//'`
296                 if [ "$ec_card_type" = '5' ]; then
297                         debug "ECHO($dev_short): Firmware $echo_file"
298                         card_type=`echo "$abtool_output" | grep 'CARD 0' | sed -e 's/.*type=//' -e 's/\..*//'`
299                         case "$card_type" in
300                         3)      law="-A";;
301                         4)
302                                 dev_lsusb=`echo "$dev_short" | tr '/' ':'`
303                                 # Try modern configuration
304                                 if [ -r "$SPAN_TYPES_CONFIG" ]; then
305                                         # Try exact match by label
306                                         label=`lsusb -s "$dev_lsusb" -v 2>/dev/null | awk '$1 == "iSerial" && $2 == 3 { print $3 }'`
307                                         if [ "$label" != '' ]; then
308                                                 label="usb:$label"
309                                                 debug "ECHO($dev_short): Search span-types.conf for [$label]"
310                                                 pri_spec=`filter_span_types "${label}"`
311                                                 if [ "$pri_spec" != '' ]; then
312                                                         debug "ECHO($dev_short): Found definitions for [$label] -- '$pri_spec'"
313                                                 fi
314                                         else
315                                                 debug "ECHO($dev_short): Device without a label"
316                                         fi
317                                         # Check wildcard match
318                                         pri_spec_wildcard=`filter_span_types '*'`
319                                         if [ "$pri_spec_wildcard" != '' ]; then
320                                                 debug "ECHO($dev_short): Found definitions for wildcard -- $pri_spec_wildcard"
321                                         fi
322                                         pri_spec_params=""
323                                         if [ "$pri_spec$pri_spec_wildcard" != '' ]; then
324                                                 pri_spec=`echo "$pri_spec_wildcard $pri_spec" | tr -s ' \t\n' ','`
325                                                 pri_spec_params="-S $pri_spec"
326                                                 debug "ECHO($dev_short): pri_spec_params='$pri_spec_params'"
327                                         fi
328                                 fi
329                                 # Fallback to legacy xpp.conf
330                                 default_pri_protocol=''
331                                 law=''
332                                 if [ -r "$XPP_CONFIG" ]; then
333                                         default_pri_protocol=`awk '/^pri_protocol/ {print $2}' $XPP_CONFIG`
334                                         if [ "$default_pri_protocol" != '' ]; then
335                                                 debug "ECHO($dev_short): Found legacy xpp.conf setting -- $default_pri_protocol"
336                                                 # "E1" or empty (implied E1) means aLaw
337                                                 if [ "$default_pri_protocol" != 'T1' ]; then
338                                                         law='-A'
339                                                 fi
340                                         fi
341                                 fi
342                                 ;;
343                         esac
344                         caps_num=`echo "$abtool_output" | grep 'ECHO ports' | sed -e 's/.*: *//'`
345                         debug "ECHO($dev_short): $caps_num channels allowed."
346                         if [ "$caps_num" != '0' ]; then
347                                 run_astribank_hexload -D "$dev" -O $law $pri_spec_params "$echo_file"
348                         else
349                                 echo "WARNING: ECHO burning was skipped (no capabilities)"
350                         fi
351                 fi
352                 pic_files=`echo "$FIRMWARE_DIR"/PIC_TYPE_[1-4].hex`
353                 debug "PIC burning into $dev: begin $pic_files"
354                 run_astribank_hexload -D "$dev" -p $pic_files
355                 debug "PIC burning into $dev: end $pic_files"
356                 ;;
357         esac
358         # Do renumeration!
359         run_astribank_tool -D "$dev" -n > /dev/null 2>&1
360         debug "Renumeration of $dev done."
361 }
362
363 fpga_firmware_device() {
364         id_str="$1"
365         devpath="$2"
366
367         id_product=`echo "$id_str" | cut -d/ -f2`
368         bcd_device=`echo "$id_str" | cut -d/ -f3`
369         case "$id_str" in
370         e4e4/1131/101)
371                 fw="FPGA_FXS.hex"
372                 ;;
373         e4e4/11[456]1/101)
374                 fw="FPGA_${id_product}.hex"
375                 ;;
376         e4e4/1161/20?)
377                 fw="FPGA_${id_product}.${bcd_device}.hex"
378                 ;;
379         e4e4/*)
380                 debug "No FPGA firmware for device $devpath ($id_str)"
381                 return
382                 ;;
383         *)
384                 return
385                 ;;
386         esac
387         debug "Loading $fw into $devpath"
388         load_fw_device "$devpath" "$fw"
389         sleep_if_race
390 }
391
392 numdevs() {
393         id_str="$1"
394
395         #echo >&2 "numdevs($id_str)"
396         filter_devs "$id_str" | wc -l
397 }
398
399 wait_renumeration() {
400         num="$1"
401         id_str="$2"
402         caller="$3"
403         iter=10
404
405         prev=0
406         echo "Waiting renumeration ($caller)"
407         while
408                 n=`numdevs "$id_str"`
409                 [ "$num" -gt "$n" ]
410         do
411                 if [ "$prev" -lt "$n" ]; then
412                         echo -n "+"
413                 else
414                         echo -n "."
415                 fi
416                 sleep 1
417                 prev="$n"
418                 debug "wait($iter) (found $n from $num devices) ($caller)"
419                 if ! iter=`expr $iter - 1`; then
420                         echo "Timeout (found $n from $num devices) ($caller)"
421                         break;
422                 fi
423         done
424         echo "Got all $num devices ($caller)"
425         sleep 1 # Let everything settle
426 }
427
428 fpga_firmware_all_devices() {
429         echo "Loading FPGA firmwares"
430         devs=`filter_devs 'e4e4/11[3456]1/*'`
431         n=`echo "$devs" | wc -l`
432         echo "$devs" | (
433                 while read id_str devpath; do
434                         fpga_firmware_device "$id_str" "$devpath" &
435                 done
436                 sleep 1
437                 echo "Wait for FPGA loading processes"
438                 wait
439                 )
440         wait_renumeration $numdevs 'e4e4/11[3456]2/*' "fpga_firmware_device"
441 }
442
443 reset_fpga() {
444         devices=`filter_devs 'e4e4/11[3456][124]/*'`
445         totaldevs=`numdevs 'e4e4/11[3456][124]/*'`
446         echo >&2 -- "Resetting devices [$totaldevs devices]"
447         echo "$devices" | grep -v '^$' | while read id_str dev
448         do
449                 (
450                         debug "Resetting FPGA Firmware on $dev"
451                         sleep_if_race
452                         run_astribank_tool -D "$dev" -r full >/dev/null 2>&1
453                 ) &
454         done
455         wait
456         if [ "$1" = 'wait' ]; then
457                 wait_renumeration $totaldevs 'e4e4/11[3456][03]/*' "reset_fpga"
458         fi
459 }
460
461 usage() {
462         echo "$0: Astribank firmware loading script."
463         echo "Usage: "
464         echo "$0 load  : manual firmware loading."
465         echo "$0 usb   : manual firmware loading: USB firmware only."
466         echo "$0 help  : this text."
467 }
468
469 # We have a potential astribank
470 astribank_is_starting -a
471
472 #########################
473 ##
474 ## Manual run
475 ##
476
477 # to run manually, pass the parameter 'xppdetect'
478 case "$1" in
479 udev) 
480         # Various kernel versions use different sets of variables.
481         # Here we want to make sure we have 'DEVICE' and 'PRODUCT' set
482         # up. DEVICE is now deprecated in favour of DEVNAME. It will
483         # likely to contain an invalid name if /proc/bus/usb is not
484         # mounted. So it needs further cooking.
485         DEVICE="${DEVNAME:-$DEVICE}"
486         case "$DEVICE" in /proc/*) DEVICE="/dev${DEVICE#/proc}" ;; esac
487         # PRODUCT contains 'vendor_id'/'product_id'/'version' . We
488         # currently pass it as a parameter, but might as well get it
489         # from the envirnment.
490         PRODUCT="${PRODUCT:-$2}"
491         # skip on to the rest of the script. Don't exit.
492         ;;
493 reset-wait)
494         reset_fpga wait
495         ;;
496 reset)
497         reset_fpga
498         ;;
499 list)
500         filter_devs 'e4e4/*/*'
501         exit 0
502         ;;
503 xppdetect|load|usb)
504         numdevs=`numdevs 'e4e4/11[3456][0134]/*'`
505         echo >&2 -- "--------- FIRMWARE LOADING: ($1) [$numdevs devices]"
506
507         usb_firmware_all_devices
508         if [ "$1" != 'usb' ]
509         then
510                 fpga_firmware_all_devices
511         fi
512
513         echo >&2 -- "--------- FIRMWARE IS LOADED"
514         exit 0
515         ;;
516 recover-sb)
517         # Load a firmware that fixes a but which makes the Source Byte in the
518         # EEPROM reset and make the device appear like a Cypress dev kit:
519         load_usb_fw 04b4 8613 $USB_RECOV
520         ;;
521 help)
522         usage
523         exit 0
524         ;;
525 *)
526         if [ "$ACTION" = '' ]; then # not called from hotplug
527                 echo "$0: Error: unknown command \"$1\""
528                 echo ''
529                 usage
530                 exit 1
531         fi
532         ;;
533 esac
534
535 #########################
536 ##
537 ## Hotplug run
538 ##
539
540 # allow disabling automatic hotplugging:
541 if [ "$XPP_HOTPLUG_DISABLED" != '' ]; then
542         $LOGGER -p kern.info "Exiting... XPP_HOTPLUG_DISABLED"
543         exit 0
544 fi
545
546 if [ "$ACTION" != add ]; then
547         exit 0;
548 fi
549
550 # This procedure is run in the background to do the actual work of loading the
551 # firmware. Running it in the background allows udev to continue doing other tasks
552 # and thus provide a faster startup.
553 #
554 # On some systems (e.g. CentOS 5) we get the relevant udev event before the device
555 # file is ready. Which is why we want the background process to wait a bit first.
556 udev_delayed_load() {
557         sleep 0.2
558         # Make sure the new device is writable:
559         usb_dev_writable=0
560         for i in `seq $XPP_UDEV_SLEEP_TIME`; do
561                 if [ -w "$DEVICE" ]; then
562                         usb_dev_writable=1;
563                         break;
564                 fi
565                 sleep 1
566         done
567         if [ $usb_dev_writable != 1 ]; then
568                 echo >&2 "Device $DEVICE not writable. Can't load firmware."
569                 return;
570         fi
571
572         echo >&2 "Trying to find what to do for product $PRODUCT, device $DEVICE"
573         case "$PRODUCT" in
574         4b4/8613/*)
575                 # This case is for a potentially-broken Astribank.
576                 # In most systems you should not set udev rules for those to
577                 # get here, as this is actually the ID of a Cypress dev-kit:
578                 FIRM_USB="$FIRMWARE_DIR/$USB_RECOV"
579                 echo >&2 "Loading recovery firmware '$FIRM_USB' into '$DEVICE'"
580                 run_fxload -D "$DEVICE" -I "$FIRM_USB"
581                 ;;
582         e4e4/11[3456]0/*|e4e4/1163/*)
583                 usb_firmware_device "$PRODUCT" "$DEVICE"
584                 ;;
585         e4e4/11[3456]1/*)
586                 # There are potentially two separate udev events, for
587                 # each of the two endpoints. Ignore the first interface:
588                 case "$DEVPATH" in *.0) exit 0;; esac
589                 sleep_if_race
590                 fpga_firmware_device "$PRODUCT" "$DEVICE" &
591                 wait    # parallel firmware loading
592                 ;;
593         esac    
594 }
595
596 udev_delayed_load 2>&1 | $LOGGER &
597