xpp: support per-port E1/T1 EC

* Added optional '-S <span-spec>' argument to astribank_hexload:
  - Allow passing PRI span specification to EC firmware loader.
  - The span specifications is whitespace/comma separate list
    of items.
  - Each item is: <span>:<type> (Example: 3:T1)
  - The <span> may use shell-like globbing (e.g: *:E1 or [12]:T1)
  - Any span not matched in the span specification will be set
    as without the new '-S' option (i.e: depends on the '-A' option).

* Adapted xpp_fxloader:
  - Read specification for both device label and wildcard from
    /etc/dahdi/span-types.conf
  - If the result is non-empty, pass it as '-S <span-spec>' to
    the EC firmware loader.
This commit is contained in:
Oron Peled 2014-05-28 08:58:54 -04:00 committed by Tzafrir Cohen
parent fc459c374c
commit 412c3f0fe3
8 changed files with 292 additions and 44 deletions

View File

@ -54,7 +54,7 @@ OCT_DEFINES = \
-DcOCT6100_MAX_ECHO_CHANNELS=672 \ -DcOCT6100_MAX_ECHO_CHANNELS=672 \
-DcOCT6100_MAX_MIXER_EVENTS=1344 -DcOCT6100_MAX_MIXER_EVENTS=1344
ECHO_LOADER_SRC = echo_loader.c ECHO_LOADER_SRC = echo_loader.c parse_span_specs.c
ECHO_LOADER = $(ECHO_LOADER_SRC:.c=.o) ECHO_LOADER = $(ECHO_LOADER_SRC:.c=.o)
endif endif

View File

@ -7,7 +7,7 @@ astribank_hexload \- Xorcom Astribank (xpp) firmware loader
.B astribank_hexload \-D \fIdevice-path\fR \-p [\fIoptions\fR] \fIhexfile1 .. hexfile4\fR .B astribank_hexload \-D \fIdevice-path\fR \-p [\fIoptions\fR] \fIhexfile1 .. hexfile4\fR
.B astribank_hexload \-D \fIdevice-path\fR \-O [-A] [\fIoptions\fR] \fIimagefile\fR .B astribank_hexload \-D \fIdevice-path\fR \-O [-A] [-S \fIspan-specs\fR] [\fIoptions\fR] \fIimagefile\fR
.B astribank_hexload \-D \fIdevice-path\fR \-o [\fIoptions\fR] .B astribank_hexload \-D \fIdevice-path\fR \-o [\fIoptions\fR]
@ -29,7 +29,7 @@ It can be used to load either an FPGA firmware or a PIC
firmware. It is normally run by the script xpp_fxloader. firmware. It is normally run by the script xpp_fxloader.
.SH OPTIONS .SH OPTIONS
.B \-D .B \-D
.I device-path .I device-path
.RS .RS
Required. The device to read from/write to. This is Required. The device to read from/write to. This is
@ -95,13 +95,38 @@ use for BRI and E1. If not set, the default mu-Law (G.711u), which is
what you'd normally use for FXS, FXO and T1. what you'd normally use for FXS, FXO and T1.
.RE .RE
.B \-S \fIspan-specs\fR
.RS
This option should only be used when loading Octasic echo canceller firmware
and only if the first Astribank module is PRI.
Its goal is to allow specifying different \fIline-mode\fR (E1/T1/J1) in different
ports of the PRI module. \fBastribank_hexload\fR use the \fIspan-specs\fR argument
to select aLaw/uLaw for each of the PRI ports in the module.
The \fIspan-specs\fR is a list of items separated by whitespace or commas.
Each item is composed of a port selector, colon and a \fIline-mode\fR specifier.
This syntax follows the syntax of specifiers in \fB/etc/dahdi/span-types.conf\fR.
Examples:
.RS
3:E1 \- The 3'rd port is E1.
*:T1 \- Any unspecified port is T1 (wildcard match).
1:T1,2:T1,*:E1 \- First and second ports are T1, the rest are E1.
.RE
If the \fB\-S\fR is not given, the PRI default is determined by the existance of the \fB\-A-fR option.
.RE
.SH SEE ALSO .SH SEE ALSO
fxload(8), lsusb(8), astribank_tool(8) fxload(8), lsusb(8), astribank_tool(8)
.SH AUTHOR .SH AUTHOR
This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> . This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> .
Permission is granted to copy, distribute and/or modify this document under Permission is granted to copy, distribute and/or modify this document under
the terms of the GNU General Public License, Version 2 any the terms of the GNU General Public License, Version 2 any
later version published by the Free Software Foundation. later version published by the Free Software Foundation.
On Debian systems, the complete text of the GNU General Public On Debian systems, the complete text of the GNU General Public

View File

@ -50,6 +50,7 @@ static void usage()
#if HAVE_OCTASIC #if HAVE_OCTASIC
fprintf(stderr, "\t\t[-O] # Load Octasic firmware\n"); fprintf(stderr, "\t\t[-O] # Load Octasic firmware\n");
fprintf(stderr, "\t\t[-o] # Show Octasic version\n"); fprintf(stderr, "\t\t[-o] # Show Octasic version\n");
fprintf(stderr, "\t\t[-S <pri-spec>] # Set PRI type specification string\n");
#endif #endif
fprintf(stderr, "\t\t[-F] # Load FPGA firmware\n"); fprintf(stderr, "\t\t[-F] # Load FPGA firmware\n");
fprintf(stderr, "\t\t[-p] # Load PIC firmware\n"); fprintf(stderr, "\t\t[-p] # Load PIC firmware\n");
@ -164,11 +165,12 @@ int main(int argc, char *argv[])
int opt_ecver = 0; int opt_ecver = 0;
#if HAVE_OCTASIC #if HAVE_OCTASIC
int opt_alaw = 0; int opt_alaw = 0;
const char *span_spec = NULL;
#endif #endif
int opt_dest = 0; int opt_dest = 0;
int opt_sum = 0; int opt_sum = 0;
enum dev_dest dest = DEST_NONE; enum dev_dest dest = DEST_NONE;
const char options[] = "vd:D:EFOopA"; const char options[] = "vd:D:EFOopAS:";
int iface_num; int iface_num;
int ret; int ret;
@ -210,6 +212,9 @@ int main(int argc, char *argv[])
case 'A': case 'A':
opt_alaw = 1; opt_alaw = 1;
break; break;
case 'S':
span_spec = optarg;
break;
#endif #endif
case 'p': case 'p':
opt_pic = 1; opt_pic = 1;
@ -290,7 +295,7 @@ int main(int argc, char *argv[])
} }
#if HAVE_OCTASIC #if HAVE_OCTASIC
} else if (opt_echo) { } else if (opt_echo) {
if((ret = load_echo(astribank, argv[optind], opt_alaw)) < 0) { if((ret = load_echo(astribank, argv[optind], opt_alaw, span_spec)) < 0) {
ERR("%s: Loading ECHO's failed\n", devpath); ERR("%s: Loading ECHO's failed\n", devpath);
return 1; return 1;
} }

View File

@ -31,6 +31,7 @@
#include "echo_loader.h" #include "echo_loader.h"
#include "debug.h" #include "debug.h"
#include <oct6100api/oct6100_api.h> #include <oct6100api/oct6100_api.h>
#include "parse_span_specs.h"
#define DBG_MASK 0x03 #define DBG_MASK 0x03
#define TIMEOUT 1000 #define TIMEOUT 1000
@ -560,7 +561,7 @@ inline int get_ver(struct astribank_device *astribank)
} }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
UINT32 init_octasic(char *filename, struct astribank_device *astribank, int is_alaw) UINT32 init_octasic(char *filename, struct astribank_device *astribank, struct span_specs *span_specs)
{ {
int cpld_ver; int cpld_ver;
struct echo_mod *echo_mod; struct echo_mod *echo_mod;
@ -580,6 +581,8 @@ UINT32 init_octasic(char *filename, struct astribank_device *astribank, int is_a
/* Channel resources.*/ /* Channel resources.*/
tOCT6100_CHANNEL_OPEN ChannelOpen; tOCT6100_CHANNEL_OPEN ChannelOpen;
UINT32 ulChanHndl; UINT32 ulChanHndl;
enum tdm_codec tdm_codec;
int spanno;
if (test_send(astribank) < 0) if (test_send(astribank) < 0)
return cOCT6100_ERR_FATAL; return cOCT6100_ERR_FATAL;
@ -729,7 +732,17 @@ UINT32 init_octasic(char *filename, struct astribank_device *astribank, int is_a
/* Set the channel to work at the echo cancellation mode.*/ /* Set the channel to work at the echo cancellation mode.*/
ChannelOpen.ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_NORMAL; ChannelOpen.ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_NORMAL;
pcmLaw = (is_alaw ? cOCT6100_PCM_A_LAW: cOCT6100_PCM_U_LAW); spanno = nChan % 4;
assert(spanno >= 0 && spanno < MAX_SPANNO);
tdm_codec = span_specs->span_is_alaw[spanno];
if (tdm_codec == TDM_CODEC_UNKNOWN) {
AB_ERR(astribank, "Calculated bad alaw/ulaw on channel %d\n", nChan);
return cOCT6100_ERR_FATAL;
}
if (nChan < 4)
AB_INFO(astribank, "ECHO PRI port %d = %s\n", spanno+1, (tdm_codec == TDM_CODEC_ALAW) ? "alaw" : "ulaw");
pcmLaw = ((tdm_codec == TDM_CODEC_ALAW) ? cOCT6100_PCM_A_LAW: cOCT6100_PCM_U_LAW);
/* Configure the TDM interface.*/ /* Configure the TDM interface.*/
ChannelOpen.TdmConfig.ulRinPcmLaw = pcmLaw; ChannelOpen.TdmConfig.ulRinPcmLaw = pcmLaw;
@ -825,15 +838,22 @@ UINT32 init_octasic(char *filename, struct astribank_device *astribank, int is_a
} }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
int load_echo(struct astribank_device *astribank, char *filename, int is_alaw) int load_echo(struct astribank_device *astribank, char *filename, int default_is_alaw, const char *span_spec)
{ {
int ret; int ret;
UINT32 octasic_status; UINT32 octasic_status;
struct span_specs *span_specs;
AB_INFO(astribank, "Loading ECHOCAN Firmware: %s (%s)\n", span_specs = parse_span_specifications(span_spec, default_is_alaw);
filename, (is_alaw) ? "alaw" : "ulaw"); if (!span_specs) {
AB_ERR(astribank, "ECHO parsing span specs failed\n");
return -EFAULT;
}
AB_INFO(astribank, "Loading ECHOCAN Firmware: %s (default %s)\n",
filename, (default_is_alaw) ? "alaw" : "ulaw");
usb_buffer_init(astribank, &usb_buffer); usb_buffer_init(astribank, &usb_buffer);
octasic_status = init_octasic(filename, astribank, is_alaw); octasic_status = init_octasic(filename, astribank, span_specs);
free_span_specifications(span_specs);
if (octasic_status != cOCT6100_ERR_OK) { if (octasic_status != cOCT6100_ERR_OK) {
AB_ERR(astribank, "ECHO %s burning failed (%08X)\n", AB_ERR(astribank, "ECHO %s burning failed (%08X)\n",
filename, octasic_status); filename, octasic_status);

View File

@ -26,7 +26,7 @@
#include "astribank_usb.h" #include "astribank_usb.h"
int spi_send(struct astribank_device *astribank, uint16_t addr, uint16_t data, int recv_answer, int ver); int spi_send(struct astribank_device *astribank, uint16_t addr, uint16_t data, int recv_answer, int ver);
int load_echo(struct astribank_device *astribank, char *filename, int is_alaw); int load_echo(struct astribank_device *astribank, char *filename, int is_alaw, const char *span_spec);
int echo_ver(struct astribank_device *astribank); int echo_ver(struct astribank_device *astribank);
#endif /* ECHO_LOADER_H */ #endif /* ECHO_LOADER_H */

152
xpp/parse_span_specs.c Normal file
View File

@ -0,0 +1,152 @@
/*
* Written by Oron Peled <oron@actcom.co.il>
* Copyright (C) 2014, 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.
*
*/
#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
#include <regex.h>
#include <fnmatch.h>
#include <sys/time.h>
#include "parse_span_specs.h"
void free_span_specifications(struct span_specs *span_specs)
{
if (span_specs) {
if (span_specs->buf)
free(span_specs->buf);
free(span_specs);
}
}
static enum tdm_codec is_alaw_span_type(const char *span_type)
{
assert(span_type);
if (strcmp(span_type, "E1") == 0)
return TDM_CODEC_ALAW;
else if (strcmp(span_type, "T1") == 0)
return TDM_CODEC_ULAW;
return TDM_CODEC_UNKNOWN;
}
struct span_specs *parse_span_specifications(const char *spec_string, int default_is_alaw)
{
struct span_specs *span_specs;
char *p;
int spanno;
int i;
if (!spec_string)
return NULL;
/* Allocate and Initialize */
span_specs = calloc(sizeof(char *), MAX_SPANNO);
if (!span_specs)
goto err;
for (spanno = 0; spanno < MAX_SPANNO; spanno++)
span_specs->span_is_alaw[spanno] = TDM_CODEC_UNKNOWN;
span_specs->buf = strdup(spec_string);
if (!span_specs->buf)
goto err;
for (i = 0;; i++) {
char *curr_item;
char *tokenize_key;
char *key;
char *value;
enum tdm_codec is_alaw;
int matched;
/* Split to items */
p = (i == 0) ? span_specs->buf : NULL;
p = strtok_r(p, " \t,", &curr_item);
if (!p)
break;
/* Split to <span>:<type> */
key = strtok_r(p, ":", &tokenize_key);
if (!key) {
fprintf(stderr,
"Missing ':' (item #%d inside '%s')\n",
i+1, spec_string);
goto err;
}
value = strtok_r(NULL, ":", &tokenize_key);
if (!value) {
fprintf(stderr,
"Missing value after ':' (item #%d inside '%s')\n",
i+1, spec_string);
goto err;
}
/* Match span specification and set alaw/ulaw */
is_alaw = is_alaw_span_type(value);
if (is_alaw == TDM_CODEC_UNKNOWN) {
fprintf(stderr,
"Illegal span type '%s' (item #%d inside '%s')\n",
value, i+1, spec_string);
goto err;
}
matched = 0;
for (spanno = 0; spanno < MAX_SPANNO; spanno++) {
char tmpbuf[BUFSIZ];
snprintf(tmpbuf, sizeof(tmpbuf), "%d", spanno + 1);
if (fnmatch(p, tmpbuf, 0) == 0) {
matched++;
span_specs->span_is_alaw[spanno] = is_alaw;
}
}
if (!matched) {
fprintf(stderr,
"Span specification '%s' does not match any span (item #%d inside '%s')\n",
key, i+1, spec_string);
goto err;
}
}
/* Set defaults */
for (spanno = 0; spanno < MAX_SPANNO; spanno++) {
if (span_specs->span_is_alaw[spanno] == TDM_CODEC_UNKNOWN) {
span_specs->span_is_alaw[spanno] = default_is_alaw;
}
}
return span_specs;
err:
free_span_specifications(span_specs);
return NULL;
}
void print_span_specifications(struct span_specs *span_specs, FILE *output)
{
int spanno;
if (!span_specs)
return;
for (spanno = 0; spanno < MAX_SPANNO; spanno++) {
enum tdm_codec is_alaw;
is_alaw = span_specs->span_is_alaw[spanno];
fprintf(output, "%d %s\n",
spanno+1, (is_alaw == TDM_CODEC_ALAW) ? "alaw" : "ulaw");
}
}

43
xpp/parse_span_specs.h Normal file
View File

@ -0,0 +1,43 @@
#ifndef PARSE_SPAN_SPECS_H
#define PARSE_SPAN_SPECS_H
/*
* Written by Oron Peled <oron@actcom.co.il>
* Copyright (C) 2014, 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.
*
*/
#define MAX_SPANNO 4 /* E1/T1 spans -- always in first unit. 1-based */
enum tdm_codec {
TDM_CODEC_UNKNOWN,
TDM_CODEC_ULAW,
TDM_CODEC_ALAW,
};
struct span_specs {
char *buf;
enum tdm_codec span_is_alaw[MAX_SPANNO];
};
struct span_specs *parse_span_specifications(const char *spec_string, int default_is_alaw);
void free_span_specifications(struct span_specs *span_specs);
void print_span_specifications(struct span_specs *span_specs, FILE *output);
#endif /* PARSE_SPAN_SPECS_H */

View File

@ -272,10 +272,12 @@ usb_firmware_all_devices() {
} }
filter_span_types() { filter_span_types() {
#sed -n -e 's/#.*//' -e 's/[ \t]*$//' -e 's/^[ \t]*//' -e 's/^\*[ \t]\+\*://p' /etc/dahdi/span-types.conf l="$1"
sed < "$SPAN_TYPES_CONFIG" 2>/dev/null \ sed < "$SPAN_TYPES_CONFIG" 2>/dev/null \
-e 's/#.*//' \ -e 's/#.*//' \
-e 's/[ \t]*$//' -e 's/[ \t]*$//' \
-e 's/^[ \t]*//' \
-e '/^$/d' | awk -vlabel="$l" '$1 == label { print $2 }' | tr -s ', \t\n' ','
} }
load_fw_device() { load_fw_device() {
@ -287,60 +289,61 @@ load_fw_device() {
FPGA_1161*.hex) FPGA_1161*.hex)
echo_file="$FIRMWARE_DIR/OCT6104E-256D.ima" echo_file="$FIRMWARE_DIR/OCT6104E-256D.ima"
law='' law=''
law_str='uLaw' dev_short=`echo "$dev" | sed -e 's,.*/usb/*,,'`
abtool_output=`$ASTRIBANK_TOOL -D "$dev" -Q 2>&1` abtool_output=`$ASTRIBANK_TOOL -D "$dev" -Q 2>&1`
ec_card_type=`echo "$abtool_output" | grep 'CARD 4' | sed -e 's/.*type=//' -e 's/\..*//'` ec_card_type=`echo "$abtool_output" | grep 'CARD 4' | sed -e 's/.*type=//' -e 's/\..*//'`
caps_num=`echo "$abtool_output" | grep 'ECHO ports' | sed -e 's/.*: *//'` caps_num=`echo "$abtool_output" | grep 'ECHO ports' | sed -e 's/.*: *//'`
if [ "$ec_card_type" = '5' ]; then if [ "$ec_card_type" = '5' ]; then
debug "ECHO burning into $dev: $echo_file" debug "ECHO($dev_short): Firmware $echo_file"
card_type=`echo "$abtool_output" | grep 'CARD 0' | sed -e 's/.*type=//' -e 's/\..*//'` card_type=`echo "$abtool_output" | grep 'CARD 0' | sed -e 's/.*type=//' -e 's/\..*//'`
case "$card_type" in case "$card_type" in
3) law="-A";; 3) law="-A";;
4) 4)
pri_protocol='' dev_lsusb=`echo "$dev_short" | tr '/' ':'`
dev_short=`echo "$dev" | sed -e 's,.*/usb/*,,' -e 's,/,:,'`
# Try modern configuration # Try modern configuration
if [ -r "$SPAN_TYPES_CONFIG" ]; then if [ -r "$SPAN_TYPES_CONFIG" ]; then
# Try exact match by label # Try exact match by label
label=`lsusb -s "$dev_short" -v 2>/dev/null | awk '$1 == "iSerial" && $2 == 3 { print $3 }'` label=`lsusb -s "$dev_lsusb" -v 2>/dev/null | awk '$1 == "iSerial" && $2 == 3 { print $3 }'`
if [ "$label" != '' ]; then if [ "$label" != '' ]; then
label="usb:$label" label="usb:$label"
debug "ECHO: checking device $dev_short [$label]" debug "ECHO($dev_short): Search span-types.conf for [$label]"
pri_protocol=`filter_span_types | sed -n "s/^${label}[ \t]\+[0-9*]://p"` pri_spec=`filter_span_types "${label}"`
if [ "$pri_protocol" != '' ]; then if [ "$pri_spec" != '' ]; then
debug "ECHO: device $dev_short [$label] will be set to $pri_protocol" debug "ECHO($dev_short): Found definitions for [$label] -- '$pri_spec'"
fi fi
else else
debug "ECHO: Device $dev_short without a label" debug "ECHO($dev_short): Device without a label"
fi fi
# Fallback to wildcard match # Check wildcard match
if [ "$pri_protocol" = '' ]; then pri_spec_wildcard=`filter_span_types '*'`
pri_protocol=`filter_span_types | sed -n 's/^\*[ \t]\+\*://p'` if [ "$pri_spec_wildcard" != '' ]; then
if [ "$pri_protocol" != '' ]; then debug "ECHO($dev_short): Found definitions for wildcard -- $pri_spec_wildcard"
debug "ECHO: device $dev_short will be set to $pri_protocol (wildcard match)" fi
fi pri_spec=`echo "$pri_spec_wildcard $pri_spec" | tr -s ' \t\n' ','`
if [ "$pri_spec" != '' ]; then
pri_spec_params="-S $pri_spec"
debug "ECHO($dev_short): pri_spec_params='$pri_spec_params'"
fi fi
fi fi
# Fallback to legacy xpp.conf # Fallback to legacy xpp.conf
if [ "$pri_protocol" = '' -a -r "$XPP_CONFIG" ]; then default_pri_protocol=''
pri_protocol=`awk '/^pri_protocol/ {print $2}' $XPP_CONFIG` default_law=''
if [ "$pri_protocol" != '' ]; then if [ -r "$XPP_CONFIG" ]; then
debug "ECHO: device $dev_short will be set to $pri_protocol (legacy xpp.conf setting)" default_pri_protocol=`awk '/^pri_protocol/ {print $2}' $XPP_CONFIG`
if [ "$default_pri_protocol" != '' ]; then
debug "ECHO($dev_short): Found legacy xpp.conf setting -- $default_pri_protocol"
# "E1" or empty (implied E1) means aLaw
if [ "$default_pri_protocol" != 'T1' ]; then
default_law='-A'
fi
fi fi
fi fi
# "E1" or empty (implied E1) means aLaw
if [ "$pri_protocol" != 'T1' ]; then
law='-A'
fi
;; ;;
esac esac
if [ "$law" = '-A' ]; then
law_str="aLaw"
fi
caps_num=`echo "$abtool_output" | grep 'ECHO ports' | sed -e 's/.*: *//'` caps_num=`echo "$abtool_output" | grep 'ECHO ports' | sed -e 's/.*: *//'`
debug "ECHO: 1st module is $law_str, $caps_num channels allowed." debug "ECHO($dev_short): $caps_num channels allowed."
if [ "$caps_num" != '0' ]; then if [ "$caps_num" != '0' ]; then
run_astribank_hexload -D "$dev" -O $law "$echo_file" run_astribank_hexload -D "$dev" -O $default_law $pri_spec_params "$echo_file"
else else
echo "WARNING: ECHO burning was skipped (no capabilities)" echo "WARNING: ECHO burning was skipped (no capabilities)"
fi fi