diff --git a/xpp/Makefile b/xpp/Makefile index e4f460f..9b49e7e 100644 --- a/xpp/Makefile +++ b/xpp/Makefile @@ -37,8 +37,23 @@ PERL_MODS := $(shell cd perl_modules; echo $(PERL_MODS_PAT)) # FIXME: Are those values really sane? HOSTCC ?= $(CC) +USE_OCTASIC := yes +OCTASIC_DIR := oct612x -CFLAGS += -g -Wall $(USB_INCLUDE) +ifneq (no,$(USE_OCTASIC)) + +OCT_OBJS = $(shell $(OCTASIC_DIR)/octasic-helper objects $(OCTASIC_DIR)) +OCT_SRCS = $(shell echo $(OCT_OBJS) | tr -s ' ' '\n' | sed 's/\.o$$/.c/g') +OCT_HERE_OBJS = $(shell echo $(OCT_OBJS) | tr -s ' ' '\n' | sed 's,^.*/,,') +OCT_CFLAGS = $(shell $(OCTASIC_DIR)/octasic-helper cflags $(OCTASIC_DIR)) +OCT_DEFINES = \ + -DPTR_TYPE=uint32_t \ + -DcOCT6100_INTERNAL_SUPER_ARRAY_SIZE=1024 \ + -DcOCT6100_MAX_ECHO_CHANNELS=672 \ + -DcOCT6100_MAX_MIXER_EVENTS=1344 + +ECHO_LOADER = echo_loader.o +endif %.8: % pod2man --section 8 $^ > $@ || $(RM) $@ @@ -57,7 +72,7 @@ PERL_MANS = $(PERL_SCRIPTS:%=%.8) XTALK_OBJS = xtalk/xtalk.o xtalk/xusb.o xtalk/xlist.o xtalk/debug.o ASTRIBANK_OBJS = astribank_usb.o mpptalk.o $(XTALK_OBJS) -ABHEXLOAD_OBJS = astribank_hexload.o hexfile.o pic_loader.o $(ASTRIBANK_OBJS) $(OCT_OBJS) +ABHEXLOAD_OBJS = astribank_hexload.o hexfile.o pic_loader.o $(ECHO_LOADER) $(ASTRIBANK_OBJS) $(OCT_HERE_OBJS) ABTOOL_OBJS = astribank_tool.o $(ASTRIBANK_OBJS) ABALLOW_OBJS = astribank_allow.o $(ASTRIBANK_OBJS) @@ -112,6 +127,7 @@ fpga_load: LIBS+=$(EXTRA_LIBS) $(USB_LIB) astribank_hexload: $(ABHEXLOAD_OBJS) astribank_hexload: LIBS+=$(EXTRA_LIBS) $(USB_LIB) +astribank_hexload: CFLAGS+=$(OCT_CFLAGS) astribank_tool: $(ABTOOL_OBJS) astribank_tool: LIBS+=$(EXTRA_LIBS) $(USB_LIB) @@ -124,9 +140,26 @@ astribank_is_starting: LIBS+=$(EXTRA_LIBS) fpga_load.o: CFLAGS+=-D_GNU_SOURCE # We use memrchr() +hex2iic: hex2iic.o iic.o hexfile.o + test_parse: test_parse.o hexfile.o test_parse: LIBS+=$(EXTRA_LIBS) $(USB_LIB) +ifneq (no,$(USE_OCTASIC)) +.octasic.depend: $(OCTASIC_DIR)/octasic-helper Makefile ../config.status + $(CC) -MM $(OCT_CFLAGS) \ + `$(OCTASIC_DIR)/octasic-helper objects | \ + tr -s ' ' '\n' | \ + sed -e 's,.*,$(OCTASIC_DIR)/&,' -e 's/\.o$$/.c/'` > $@ + +-include .octasic.depend + +$(OCT_HERE_OBJS): Makefile + $(CC) -c $(CFLAGS) $(OCT_CFLAGS) $(OCT_DEFINES) $(OCT_SRCS) + +endif + + %: %.o $(CC) $(LDFLAGS) $^ $(LIBS) -o $@ @@ -135,7 +168,7 @@ test_parse: LIBS+=$(EXTRA_LIBS) $(USB_LIB) touch $@ clean: - $(RM) .depend *.o xtalk/*.o $(TARGETS) + $(RM) .depend .octasic.depend *.o xtalk/*.o $(OCT_HERE_OBJS) $(TARGETS) .PHONY: depend depend: .depend diff --git a/xpp/README.Astribank b/xpp/README.Astribank index cd46144..62e0d8a 100644 --- a/xpp/README.Astribank +++ b/xpp/README.Astribank @@ -955,6 +955,9 @@ following command in order to load the FPGA firmware manually: # pick the right name according to the device ID. FPGA_1161.hex is for # 116x Astribanks: astribank_hexload -D /dev/bus/usb/MMM/NNN -F /usr/share/dahdi/FPGA_1161.hex + # If the device has an echo canceller unit (If the unit is BRI/E1, you + # need to add an extra -A to the command-line after the -O) + #astribank_hexload -D /dev/bus/usb/MMM/NNN -O /usr/share/dahdi/OCT6104E-256D.ima # Note the shell expantion in this line: astribank_hexload -D /dev/bus/usb/MMM/NNN -p /usr/share/dahdi/PIC_TYPE_[1-4].hex # reenumerate (disconnect and reconnect) @@ -1072,6 +1075,9 @@ xpd_bri:: BRI ("ISDN") modules. Module type 3. xpd_pri:: The module for controlling E1/T1 modules. Module type 4. +xpd_echo:: + The module for controlling hardware echo canceller modules. Module type 5. + Does not generate a span. xpp_usb:: The functionality needed to connect to the USB bus. diff --git a/xpp/astribank_allow.c b/xpp/astribank_allow.c index b1e8c52..18c7573 100644 --- a/xpp/astribank_allow.c +++ b/xpp/astribank_allow.c @@ -106,6 +106,7 @@ static int write_to_file(struct eeprom_table *eeprom_table, struct capabilities fprintf(f, "Capabilities.Port.FXO: %d\n", caps->ports_fxo); fprintf(f, "Capabilities.Port.BRI: %d\n", caps->ports_bri); fprintf(f, "Capabilities.Port.PRI: %d\n", caps->ports_pri); + fprintf(f, "Capabilities.Port.ECHO: %d\n", caps->ports_echo); fprintf(f, "Capabilities.Twinstar: %d\n", CAP_EXTRA_TWINSTAR(caps)); fprintf(f, "Data:\n"); bin_to_file(eeprom_table, sizeof(*eeprom_table), f); diff --git a/xpp/astribank_hexload.8 b/xpp/astribank_hexload.8 index 08054f6..82573a8 100644 --- a/xpp/astribank_hexload.8 +++ b/xpp/astribank_hexload.8 @@ -1,11 +1,19 @@ -.TH "ASTRIBANK_HEXLOAD" "8" "29 March 2009" "" "" +.TH "ASTRIBANK_HEXLOAD" "8" "30 May 2011" "" "" .SH NAME astribank_tool \- Xorcom Astribank (xpp) firmware loader .SH SYNOPSIS -.B astribank_tool \-D \fIdevice-path\fR <\fB\-F|\-p\fR> [\fIoptions\fR] \fIhexfile\fR +.B astribank_tool \-D \fIdevice-path\fR \-F [\fIoptions\fR] \fIhexfile\fR -.B astribank_tool [\-h] +.B astribank_tool \-D \fIdevice-path\fR \-p [\fIoptions\fR] \fIhexfile1 .. hexfile4\fR + +.B astribank_tool \-D \fIdevice-path\fR \-O [-A] [\fIoptions\fR] \fIimagefile\fR + +.B astribank_tool \-D \fIdevice-path\fR \-o [\fIoptions\fR] + +.B astribank_tool \-D \fIdevice-path\fR \-E [\fIoptions\fR] \fIhexfile\fR + +.B astribank_tool \-h .SH DESCRIPTION .B astribank_hexload @@ -28,6 +36,8 @@ which would be /dev/bus/usb/\fIbus_num\fR/\fIdevice_num\fR, or /proc/bus/usb/\fIbus_num\fR/\fIdevice_num\fR. .RE +One of the following is required: + .B \-F .RS The firmware to load is a FPGA firmware. @@ -35,9 +45,29 @@ The firmware to load is a FPGA firmware. .B \-p .RS -The firmware to load is a PIC firmware. +The firmwares to load is are PIC firmwares. All (typically 4) should be +on the command-line. .RE +.B \-O +.RS +The firmware to load is an Octasic echo canceller firmware image file. +.RE + +.B \-o +.RS +Don't load firmware. Just print the version number of the currently-loaded +Octasic echo canceller firmware. +.RE + +.B \-E +.RS +The firmware to load is a special EEPROM burning one. +.RE + + +Other options: + .B \-v .RS Increase verbosity. May be used multiple times. @@ -53,6 +83,14 @@ Set debug mask to \fImask\fR. Default is 0, 0xFF is "everything". Displays usage message. .RE +.B \-A +.RS +When loading a Octasic echo canceller firmware, set the channels of the +first Astribank module to use aLaw (G.711a). This is what you'd normally +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. +.RE + .SH SEE ALSO fxload(8), lsusb(8), astribank_tool(8), fpga_load(8) diff --git a/xpp/astribank_hexload.c b/xpp/astribank_hexload.c index 71d7aae..f0aa167 100644 --- a/xpp/astribank_hexload.c +++ b/xpp/astribank_hexload.c @@ -31,7 +31,9 @@ #include "hexfile.h" #include "mpptalk.h" #include "pic_loader.h" +#include "echo_loader.h" #include "astribank_usb.h" +#include "../autoconfig.h" #define DBG_MASK 0x80 #define MAX_HEX_LINES 10000 @@ -43,9 +45,14 @@ static void usage() fprintf(stderr, "Usage: %s [options...] -D {/proc/bus/usb|/dev/bus/usb}// hexfile...\n", progname); fprintf(stderr, "\tOptions: {-F|-p}\n"); fprintf(stderr, "\t\t[-E] # Burn to EEPROM\n"); +#if HAVE_OCTASIC + fprintf(stderr, "\t\t[-O] # Load Octasic firmware\n"); + fprintf(stderr, "\t\t[-o] # Show Octasic version\n"); +#endif fprintf(stderr, "\t\t[-F] # Load FPGA firmware\n"); fprintf(stderr, "\t\t[-p] # Load PIC firmware\n"); fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); + fprintf(stderr, "\t\t[-A] # Set A-Law for 1st module\n"); fprintf(stderr, "\t\t[-d mask] # Debug mask (0xFF for everything)\n"); exit(1); } @@ -146,9 +153,15 @@ int main(int argc, char *argv[]) { char *devpath = NULL; int opt_pic = 0; + int opt_echo = 0; + int opt_ecver = 0; +#if HAVE_OCTASIC + int opt_alaw = 0; +#endif int opt_dest = 0; + int opt_sum = 0; enum dev_dest dest = DEST_NONE; - const char options[] = "vd:D:EFp"; + const char options[] = "vd:D:EFOopA"; int iface_num; int ret; @@ -169,7 +182,7 @@ int main(int argc, char *argv[]) ERR("The -F and -E options are mutually exclusive.\n"); usage(); } - opt_dest = 1; + opt_dest++; dest = DEST_EEPROM; break; case 'F': @@ -177,9 +190,20 @@ int main(int argc, char *argv[]) ERR("The -F and -E options are mutually exclusive.\n"); usage(); } - opt_dest = 1; + opt_dest++; dest = DEST_FPGA; break; +#if HAVE_OCTASIC + case 'O': + opt_echo = 1; + break; + case 'o': + opt_ecver = 1; + break; + case 'A': + opt_alaw = 1; + break; +#endif case 'p': opt_pic = 1; break; @@ -195,12 +219,17 @@ int main(int argc, char *argv[]) usage(); } } - if((opt_dest ^ opt_pic) == 0) { - ERR("The -F, -E and -p options are mutually exclusive.\n"); + opt_sum = opt_dest + opt_pic + opt_echo; + if(opt_sum > 1 || (opt_sum == 0 && opt_ecver == 0)) { + ERR("The -F, -E" +#if HAVE_OCTASIC + ", -O" +#endif + " and -p options are mutually exclusive, if neither is used then -o should present\n"); usage(); } iface_num = (opt_dest) ? 1 : 0; - if(!opt_pic) { + if(!opt_pic && !opt_ecver) { if(optind != argc - 1) { ERR("Got %d hexfile names (Need exactly one hexfile)\n", argc - 1 - optind); @@ -227,7 +256,7 @@ int main(int argc, char *argv[]) return 1; } astribank_close(astribank, 0); - } else if(opt_pic) { + } else if(opt_pic || opt_echo || opt_ecver) { /* * XPP Interface */ @@ -237,11 +266,28 @@ int main(int argc, char *argv[]) ERR("%s: Opening astribank failed\n", devpath); return 1; } + //show_astribank_info(astribank); +#if HAVE_OCTASIC + if (opt_ecver) { + if((ret = echo_ver(astribank)) < 0) { + ERR("%s: Get Octasic version failed (Is Echo canceller card connected?)\n", devpath); + return 1; + } else + INFO("Octasic version: 0x%0X\n", ret); + } +#endif if (opt_pic) { if ((ret = load_pic(astribank, argc - optind, argv + optind)) < 0) { ERR("%s: Loading PIC's failed\n", devpath); return 1; } +#if HAVE_OCTASIC + } else if (opt_echo) { + if((ret = load_echo(astribank, argv[optind], opt_alaw)) < 0) { + ERR("%s: Loading ECHO's failed\n", devpath); + return 1; + } +#endif } astribank_close(astribank, 0); } diff --git a/xpp/astribank_tool.c b/xpp/astribank_tool.c index 5cf0dc3..7a88334 100644 --- a/xpp/astribank_tool.c +++ b/xpp/astribank_tool.c @@ -95,7 +95,7 @@ static int show_hardware(struct astribank_device *astribank) if(astribank->eeprom_type == EEPROM_TYPE_LARGE) { show_capabilities(&capabilities, stdout); if(STATUS_FPGA_LOADED(astribank->status)) { - for(unit = 0; unit < 4; unit++) { + for(unit = 0; unit < 5; unit++) { ret = mpps_card_info(astribank, unit, &card_type, &card_status); if(ret < 0) return ret; diff --git a/xpp/dahdi_registration b/xpp/dahdi_registration index 87b89f1..8b73aea 100755 --- a/xpp/dahdi_registration +++ b/xpp/dahdi_registration @@ -15,6 +15,7 @@ use Dahdi; use Dahdi::Span; use Dahdi::Xpp; use Dahdi::Xpp::Xbus; +use Dahdi::Xpp::Xpd; use Getopt::Std; sub usage { @@ -64,7 +65,7 @@ foreach my $xbus (Dahdi::Xpp::xbuses($sorter)) { myprintf "%-10s\t%3s-%s\t%s\n", $xbus->name, $xbus->xpporder, $xbus->label, $xbus->connector; next unless $xbus->status eq 'CONNECTED'; - foreach my $xpd ($xbus->xpds()) { + foreach my $xpd (Dahdi::Xpp::Xpd::telephony_devs($xbus->xpds())) { my $prev = $xpd->dahdi_registration($on); if(!defined($prev)) { # Failure printf "%s: Failed %s\n", $xpd->fqn, $!; diff --git a/xpp/echo_loader.c b/xpp/echo_loader.c new file mode 100644 index 0000000..eaf183b --- /dev/null +++ b/xpp/echo_loader.c @@ -0,0 +1,784 @@ +/* + * Written by Oron Peled + * Copyright (C) 2008, 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 +#include +#include +#include +#include +#include +#include +#include "echo_loader.h" +#include "debug.h" +#include + +#define DBG_MASK 0x03 +#define TIMEOUT 1000 +#define ECHO_MAX_CHANS 128 +#define ECHO_RIN_STREAM 0 +#define ECHO_ROUT_STREAM 1 +#define ECHO_SIN_STREAM 2 +#define ECHO_SOUT_STREAM 3 + +#define ECHO_RIN_STREAM2 4 +#define ECHO_SIN_STREAM2 6 +#define ECHO_ROUT_STREAM2 5 +#define ECHO_SOUT_STREAM2 7 + +#define EC_VER_TEST 0xABCD +#define EC_VER_INVALID 0xFFFF +static float oct_fw_load_timeout = 2.0; + +struct echo_mod { + tPOCT6100_INSTANCE_API pApiInstance; + UINT32 ulEchoChanHndl[256]; + struct astribank_device *astribank; + int maxchans; +}; + +enum xpp_packet_types { + SPI_SND_XOP = 0x0F, + SPI_RCV_XOP = 0x10, + TST_SND_XOP = 0x35, + TST_RCV_XOP = 0x36, +}; + +struct xpp_packet_header { + struct { + uint16_t len; + uint8_t op; + uint8_t unit; + } PACKED header; + union { + struct { + uint8_t header; + uint8_t flags; + uint8_t addr_l; + uint8_t addr_h; + uint8_t data_l; + uint8_t data_h; + } PACKED spi_pack; + struct { + uint8_t tid; + uint8_t tsid; + } PACKED tst_pack; + } alt; +} PACKED; + +static struct usb_buffer { + char data[PACKET_SIZE]; + int max_len; + int curr; + /* statistics */ + int min_send; + int max_send; + int num_sends; + long total_bytes; + struct timeval start; + struct timeval end; +} usb_buffer; + + +static void usb_buffer_init(struct astribank_device *astribank, struct usb_buffer *ub) +{ + ub->max_len = xusb_packet_size(astribank->xusb); + ub->curr = 0; + ub->min_send = INT_MAX; + ub->max_send = 0; + ub->num_sends = 0; + ub->total_bytes = 0; + gettimeofday(&ub->start, NULL); +} + +static long usb_buffer_usec(struct usb_buffer *ub) +{ + struct timeval now; + + gettimeofday(&now, NULL); + return (now.tv_sec - ub->start.tv_sec) * 1000000 + + (now.tv_usec - ub->start.tv_usec); +} + +static void usb_buffer_showstatistics(struct astribank_device *astribank, struct usb_buffer *ub) +{ + long usec; + + usec = usb_buffer_usec(ub); + INFO("%s [%s]: Octasic statistics: packet_size=[%d, %ld, %d] packets=%d, bytes=%ld msec=%ld usec/packet=%d\n", + xusb_devpath(astribank->xusb), + xusb_serial(astribank->xusb), + ub->min_send, + ub->total_bytes / ub->num_sends, + ub->max_send, + ub->num_sends, ub->total_bytes, + usec / 1000, usec / ub->num_sends); +} + +static int usb_buffer_flush(struct astribank_device *astribank, struct usb_buffer *ub) +{ + int ret; + long t; + long sec; + static int last_sec; + + if (ub->curr == 0) + return 0; + ret = xusb_send(astribank->xusb, ub->data, ub->curr, TIMEOUT); + if(ret < 0) { + ERR("xusb_send failed: %d\n", ret); + return ret; + } + DBG("%s: Written %d bytes\n", __func__, ret); + if (ret > ub->max_send) + ub->max_send = ret; + if (ret < ub->min_send) + ub->min_send = ret; + ub->total_bytes += ret; + ub->num_sends++; + ub->curr = 0; + + sec = usb_buffer_usec(ub) / (1000 * 1000); + if (sec > last_sec) { + DBG("bytes/sec=%ld average len=%ld\n", + ub->total_bytes / sec, + ub->total_bytes / ub->num_sends); + last_sec = sec; + } + + /* + * Best result with high frequency firmware: 21 seconds + * Octasic statistics: packet_size=[10, 239, 510] packets=26806, bytes=6419640 usec=21127883 usec/packet=788 + * t = 0.3 * ret - 150; + */ + t = oct_fw_load_timeout * ret - 150; + if (t > 0) + usleep(t); + return ret; +} + +static int usb_buffer_append(struct astribank_device *astribank, struct usb_buffer *ub, + char *buf, int len) +{ + if (ub->curr + len >= ub->max_len) { + ERR("%s: buffer too small ub->curr=%d, len=%d, ub->max_len=%d\n", + __func__, ub->curr, len, ub->max_len); + return -ENOMEM; + } + memcpy(ub->data + ub->curr, buf, len); + ub->curr += len; + return len; +} + +static int usb_buffer_send(struct astribank_device *astribank, struct usb_buffer *ub, + char *buf, int len, int timeout, int recv_answer) +{ + int ret = 0; + + if (ub->curr + len >= ub->max_len) { + ret = usb_buffer_flush(astribank, ub); + if (ret < 0) + return ret; + } + + if ((ret = usb_buffer_append(astribank, ub, buf, len)) < 0) { + return ret; + } + DBG("%s: %d bytes %s\n", __func__, len, (recv_answer) ? "recv" : "send"); + if (recv_answer) { + struct xpp_packet_header *phead; + + ret = usb_buffer_flush(astribank, ub); + if (ret < 0) + return ret; + ret = xusb_recv(astribank->xusb, buf, PACKET_SIZE, TIMEOUT); + if(ret <= 0) { + ERR("No USB packs to read: %s\n", strerror(-ret)); + return -EINVAL; + } + DBG("%s: %d bytes recv\n", __func__, ret); + phead = (struct xpp_packet_header *)buf; + if(phead->header.op != SPI_RCV_XOP && phead->header.op != TST_RCV_XOP) { + ERR("Got unexpected reply OP=0x%02X\n", phead->header.op); + dump_packet(LOG_ERR, DBG_MASK, "hexline[ERR]", buf, ret); + return -EINVAL; + } + dump_packet(LOG_DEBUG, DBG_MASK, "dump:echoline[R]", (char *)phead, phead->header.len); + switch(phead->header.op) { + case SPI_RCV_XOP: + ret = (phead->alt.spi_pack.data_h << 8) | phead->alt.spi_pack.data_l; + break; + case TST_RCV_XOP: + ret = (phead->alt.tst_pack.tid << 8) | phead->alt.tst_pack.tsid; + break; + default: + ret = -EINVAL; + } + } + return ret; +} + +int spi_send(struct astribank_device *astribank, uint16_t addr, uint16_t data, int recv_answer, int ver) +{ + int ret; + char buf[PACKET_SIZE]; + struct xpp_packet_header *phead = (struct xpp_packet_header *)buf; + int pack_len; + + assert(astribank != NULL); + pack_len = sizeof(phead->header) + sizeof(phead->alt.spi_pack); + phead->header.len = pack_len; + phead->header.op = SPI_SND_XOP; + phead->header.unit = 0x40; /* EC has always this unit num */ + phead->alt.spi_pack.header = 0x05; + phead->alt.spi_pack.flags = 0x30 | (recv_answer ? 0x40: 0x00) | (ver ? 0x01: 0x00); + phead->alt.spi_pack.addr_l = (addr >> 0) & 0xFF; + phead->alt.spi_pack.addr_h = (addr >> 8) & 0xFF; + phead->alt.spi_pack.data_l = (data >> 0) & 0xFF; + phead->alt.spi_pack.data_h = (data >> 8) & 0xFF; + + dump_packet(LOG_DEBUG, DBG_MASK, "dump:echoline[W]", (char *)phead, pack_len); + + + ret = usb_buffer_send(astribank, &usb_buffer, buf, pack_len, TIMEOUT, recv_answer); + if(ret < 0) { + ERR("usb_buffer_send failed: %d\n", ret); + return ret; + } + DBG("%s: Written %d bytes\n", __func__, ret); + return ret; +} + +int test_send(struct astribank_device *astribank) +{ + int ret; + char buf[PACKET_SIZE]; + struct xpp_packet_header *phead = (struct xpp_packet_header *)buf; + int pack_len; + + assert(astribank != NULL); + pack_len = sizeof(phead->header) + sizeof(phead->alt.tst_pack); + phead->header.len = 6; + phead->header.op = 0x35; + phead->header.unit = 0x00; + phead->alt.tst_pack.tid = 0x28; // EC TestId + phead->alt.tst_pack.tsid = 0x00; // EC SubId + + dump_packet(LOG_DEBUG, DBG_MASK, "dump:echoline[W]", (char *)phead, pack_len); + + + ret = usb_buffer_send(astribank, &usb_buffer, buf, pack_len, TIMEOUT, 1); + if(ret < 0) { + ERR("usb_buffer_send failed: %d\n", ret); + return ret; + } + DBG("%s: Written %d bytes\n", __func__, ret); + return ret; +} + +void echo_send_data(struct astribank_device *astribank, const unsigned int addr, const unsigned int data) +{ +/* DBG("SEND: %04X -> [%04X]\n", data, addr); + DBG("\t\t[%04X] <- %04X\n", 0x0008, (addr >> 20)); + DBG("\t\t[%04X] <- %04X\n", 0x000A, (addr >> 4) & ((1 << 16) - 1)); + DBG("\t\t[%04X] <- %04X\n", 0x0004, data); + DBG("\t\t[%04X] <- %04X\n", 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (3 << 12) | 1); + */ + + DBG("SND:\n"); + spi_send(astribank, 0x0008, (addr >> 20) , 0, 0); + spi_send(astribank, 0x000A, (addr >> 4) & ((1 << 16) - 1) , 0, 0); + spi_send(astribank, 0x0004, data , 0, 0); + spi_send(astribank, 0x0000, (((addr >> 1) & 0x7) << 9) | + (1 << 8) | (3 << 12) | 1 , 0, 0); +} + +unsigned int echo_recv_data(struct astribank_device *astribank, const unsigned int addr) +{ + unsigned int data = 0x00; + unsigned int ret; + + DBG("RCV:\n"); + spi_send(astribank, 0x0008, (addr >> 20) , 0, 0); + spi_send(astribank, 0x000A, (addr >> 4) & ((1 << 16) - 1) , 0, 0); + spi_send(astribank, 0x0000, (((addr >> 1) & 0x7) << 9) | + (1 << 8) | 1 , 0, 0); + ret = spi_send(astribank, 0x0004, data , 1, 0); + return ret; +} + +int load_file(char *filename, unsigned char **ppBuf, UINT32 *pLen) +{ + unsigned char * pbyFileData = NULL; + FILE* pFile; + + DBG("Loading %s file...\n", filename); + pFile = fopen( filename, "rb" ); + if (pFile == NULL) { + ERR("fopen\n"); + return -ENODEV; + } + + fseek( pFile, 0L, SEEK_END ); + *pLen = ftell( pFile ); + fseek( pFile, 0L, SEEK_SET ); + + pbyFileData = (unsigned char *)malloc(*pLen); + if (pbyFileData == NULL) { + fclose( pFile ); + ERR("malloc\n" ); + return -ENODEV; + } else { + DBG("allocated mem for pbyFileData\n"); + } + fread(pbyFileData, 1, *pLen, pFile); + fclose(pFile); + DBG("Successful loading %s file into memory (size = %d, DUMP: first = %02X %02X, last = %02X %02X)\n", + filename, *pLen, + pbyFileData[0], pbyFileData[1], + pbyFileData[(*pLen)-2], pbyFileData[(*pLen)-1]); + *ppBuf = pbyFileData; + return 0; +} + +UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime) +{ + ///* Why couldn't they just take a timeval like everyone else? */ + struct timeval tv; + unsigned long long total_usecs; + unsigned int mask = ~0; + + gettimeofday(&tv, 0); + total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) + + (((unsigned long long)(tv.tv_usec))); + f_pTime->aulWallTimeUs[0] = (total_usecs & mask); + f_pTime->aulWallTimeUs[1] = (total_usecs >> 32); + //printf("Inside of Oct6100UserGetTime\n"); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength) +{ + memset(f_pAddress, f_ulPattern, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemCopy(PVOID f_pDestination, const void *f_pSource, UINT32 f_ulLength) +{ + memcpy(f_pDestination, f_pSource, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate) +{ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy) +{ +#ifdef OCTASIC_DEBUG + ERR("I should never be called! (destroy serialize object)\n"); +#endif + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams) +{ + const unsigned int addr = f_pWriteParams->ulWriteAddress; + const unsigned int data = f_pWriteParams->usWriteData; + const struct echo_mod *echo_mod = (struct echo_mod *)(f_pWriteParams->pProcessContext); + struct astribank_device *astribank = echo_mod->astribank; + + echo_send_data(astribank, addr, data); + + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams) +{ + unsigned int addr; + unsigned int data; + unsigned int len = f_pSmearParams->ulWriteLength; + const struct echo_mod *echo_mod = (struct echo_mod *)f_pSmearParams->pProcessContext; + struct astribank_device *astribank = echo_mod->astribank; + unsigned int i; + + for (i = 0; i < len; i++) { + addr = f_pSmearParams->ulWriteAddress + (i << 1); + data = f_pSmearParams->usWriteData; + echo_send_data(astribank, addr, data); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams) +{ + unsigned int addr; + unsigned int data; + unsigned int len = f_pBurstParams->ulWriteLength; + const struct echo_mod *echo_mod = (struct echo_mod *)f_pBurstParams->pProcessContext; + struct astribank_device *astribank = echo_mod->astribank; + unsigned int i; + + for (i = 0; i < len; i++) { + addr = f_pBurstParams->ulWriteAddress + (i << 1); + data = f_pBurstParams->pusWriteData[i]; + echo_send_data(astribank, addr, data); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams) +{ + const unsigned int addr = f_pReadParams->ulReadAddress; + const struct echo_mod *echo_mod = (struct echo_mod *)f_pReadParams->pProcessContext; + struct astribank_device *astribank = echo_mod->astribank; + + *f_pReadParams->pusReadData = echo_recv_data(astribank, addr); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams) +{ + unsigned int addr; + unsigned int len = f_pBurstParams->ulReadLength; + const struct echo_mod *echo_mod = (struct echo_mod *)f_pBurstParams->pProcessContext; + struct astribank_device *astribank = echo_mod->astribank; + unsigned int i; + + for (i = 0;i < len; i++) { + addr = f_pBurstParams->ulReadAddress + (i << 1); + f_pBurstParams->pusReadData[i] = echo_recv_data(astribank, addr); + } + return cOCT6100_ERR_OK; +} + +inline int get_ver(struct astribank_device *astribank) +{ + + return spi_send(astribank, 0, 0, 1, 1); +} + +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +UINT32 init_octasic(char *filename, struct astribank_device *astribank, int is_alaw) +{ + int cpld_ver; + struct echo_mod *echo_mod; + UINT32 nChan; + UINT32 nSlot; + UINT32 pcmLaw; + UINT32 ulResult; + + tOCT6100_GET_INSTANCE_SIZE InstanceSize; + tPOCT6100_INSTANCE_API pApiInstance; + tOCT6100_CHIP_OPEN OpenChip; + + UINT32 ulImageByteSize; + PUINT8 pbyImageData = NULL; + + /*=========================================================================*/ + /* Channel resources.*/ + tOCT6100_CHANNEL_OPEN ChannelOpen; + UINT32 ulChanHndl; + + test_send(astribank); + cpld_ver = get_ver(astribank); + INFO("%s [%s]: Check EC_CPLD version: %d\n", + xusb_devpath(astribank->xusb), + xusb_serial(astribank->xusb), + cpld_ver); + if (cpld_ver < 0) + return cpld_ver; + else if (cpld_ver == EC_VER_TEST) { + INFO("+---------------------------------------------------------+\n"); + INFO("| WARNING: TEST HARDWARE IS ON THE BOARD INSTEAD OF EC!!! |\n"); + INFO("+---------------------------------------------------------+\n"); + return cOCT6100_ERR_OK; + } + + + /**************************************************************************/ + /**************************************************************************/ + /* 1) Configure and Open the OCT6100. */ + /**************************************************************************/ + /**************************************************************************/ + + memset(&InstanceSize, 0, sizeof(tOCT6100_GET_INSTANCE_SIZE)); + memset(&OpenChip, 0, sizeof(tOCT6100_CHIP_OPEN)); + + if (!(echo_mod = malloc(sizeof(struct echo_mod)))) { + ERR("cannot allocate memory for echo_mod\n"); + return 1; + } + DBG("allocated mem for echo_mod\n"); + + memset(echo_mod, 0, sizeof(struct echo_mod)); + + /* Fill the OCT6100 Chip Open configuration structure with default values */ + + ulResult = Oct6100ChipOpenDef( &OpenChip ); + if (ulResult != cOCT6100_ERR_OK) { + ERR("Oct6100ChipOpenDef failed: result=%X\n", ulResult); + return ulResult; + } + + OpenChip.pProcessContext = echo_mod; + /* Configure clocks */ + + /* upclk oscillator is at 33.33 Mhz */ + OpenChip.ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ; + + /* mclk will be generated by internal PLL at 133 Mhz */ + OpenChip.fEnableMemClkOut = TRUE; + OpenChip.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; + + /* General parameters */ + OpenChip.fEnableChannelRecording = TRUE; + + /* Chip ID.*/ + OpenChip.ulUserChipId = 1; + + /* Set the max number of accesses to 1024 to speed things up */ + /* OpenChip.ulMaxRwAccesses = 1024; */ + + /* Set the maximums that the chip needs to support for this test */ + OpenChip.ulMaxChannels = 256; + OpenChip.ulMaxPlayoutBuffers = 2; + + OpenChip.ulMaxBiDirChannels = 0; + OpenChip.ulMaxConfBridges = 0; + OpenChip.ulMaxPhasingTssts = 0; + OpenChip.ulMaxTdmStreams = 8; + OpenChip.ulMaxTsiCncts = 0; + + /* External Memory Settings: Use DDR memory*/ + OpenChip.ulMemoryType = cOCT6100_MEM_TYPE_DDR; + + OpenChip.ulNumMemoryChips = 1; + OpenChip.ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB; + + + /* Load the image file */ + ulResult = load_file( filename, + &pbyImageData, + &ulImageByteSize ); + + if (pbyImageData == NULL || ulImageByteSize == 0){ + ERR("Bad pbyImageData or ulImageByteSize\n"); + return 1; + } + if ( ulResult != 0 ) { + ERR("Failed load_file %s (%08X)\n", filename, ulResult); + return ulResult; + } + + /* Assign the image file.*/ + OpenChip.pbyImageFile = pbyImageData; + OpenChip.ulImageSize = ulImageByteSize; + + /* Inserting default values into tOCT6100_GET_INSTANCE_SIZE structure parameters. */ + Oct6100GetInstanceSizeDef ( &InstanceSize ); + + /* Get the size of the OCT6100 instance structure. */ + ulResult = Oct6100GetInstanceSize(&OpenChip, &InstanceSize ); + if (ulResult != cOCT6100_ERR_OK) + { + ERR("Oct6100GetInstanceSize failed (%08X)\n", ulResult); + return ulResult; + } + + pApiInstance = malloc(InstanceSize.ulApiInstanceSize); + echo_mod->pApiInstance = pApiInstance; + echo_mod->astribank = astribank; + + if (!pApiInstance) { + ERR("Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize); + return 1; + } + + /* Perform actual open of chip */ + ulResult = Oct6100ChipOpen(pApiInstance, &OpenChip); + if (ulResult != cOCT6100_ERR_OK) { + ERR("Oct6100ChipOpen failed: result=%X\n", ulResult); + return ulResult; + } + DBG("%s: OCT6100 is open\n", __func__); + + /* Free the image file data */ + free( pbyImageData ); + + /**************************************************************************/ + /**************************************************************************/ + /* 2) Open channels in echo cancellation mode. */ + /**************************************************************************/ + /**************************************************************************/ + + for( nChan = 0; nChan < ECHO_MAX_CHANS; nChan++ ) { + nSlot = nChan; + /* open a channel.*/ + Oct6100ChannelOpenDef( &ChannelOpen ); + + /* Assign the handle memory.*/ + ChannelOpen.pulChannelHndl = &ulChanHndl; + + /* Set the channel to work at the echo cancellation mode.*/ + ChannelOpen.ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_NORMAL; + + pcmLaw = (is_alaw ? cOCT6100_PCM_A_LAW: cOCT6100_PCM_U_LAW); + + /* Configure the TDM interface.*/ + ChannelOpen.TdmConfig.ulRinPcmLaw = pcmLaw; + ChannelOpen.TdmConfig.ulRinStream = ECHO_RIN_STREAM; + ChannelOpen.TdmConfig.ulRinTimeslot = nSlot; + + ChannelOpen.TdmConfig.ulSinPcmLaw = pcmLaw; + ChannelOpen.TdmConfig.ulSinStream = ECHO_SIN_STREAM; + ChannelOpen.TdmConfig.ulSinTimeslot = nSlot; + + ChannelOpen.TdmConfig.ulRoutPcmLaw = pcmLaw; + ChannelOpen.TdmConfig.ulRoutStream = ECHO_ROUT_STREAM; + ChannelOpen.TdmConfig.ulRoutTimeslot = nSlot; + + ChannelOpen.TdmConfig.ulSoutPcmLaw = pcmLaw; + ChannelOpen.TdmConfig.ulSoutStream = ECHO_SOUT_STREAM; + ChannelOpen.TdmConfig.ulSoutTimeslot = nSlot; + + /* Set the desired VQE features.*/ + ChannelOpen.VqeConfig.fEnableNlp = TRUE; + ChannelOpen.VqeConfig.fRinDcOffsetRemoval = TRUE; + ChannelOpen.VqeConfig.fSinDcOffsetRemoval = TRUE; + + ChannelOpen.VqeConfig.ulComfortNoiseMode = cOCT6100_COMFORT_NOISE_NORMAL; + /* cOCT6100_COMFORT_NOISE_NORMAL + cOCT6100_COMFORT_NOISE_EXTENDED, + cOCT6100_COMFORT_NOISE_OFF, + cOCT6100_COMFORT_NOISE_FAST_LATCH + */ + ulResult = Oct6100ChannelOpen( pApiInstance, + &ChannelOpen ); + if (ulResult != cOCT6100_ERR_OK) { + ERR("Found error on chan %d\n", nChan); + return ulResult; + } + } + + /**************************************************************************/ + /**************************************************************************/ + /* *) Open channels in echo cancellation mode for second bus. */ + /**************************************************************************/ + /**************************************************************************/ + + for( nChan = 8; nChan < 32; nChan++ ) { + nSlot = (nChan >> 3) * 32 + (nChan & 0x07); + /* open a channel.*/ + Oct6100ChannelOpenDef( &ChannelOpen ); + + /* Assign the handle memory.*/ + ChannelOpen.pulChannelHndl = &ulChanHndl; + + /* Set the channel to work at the echo cancellation mode.*/ + ChannelOpen.ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_NORMAL; + + /* Configure the TDM interface.*/ + ChannelOpen.TdmConfig.ulRinStream = ECHO_RIN_STREAM2;; + ChannelOpen.TdmConfig.ulRinTimeslot = nSlot; + + ChannelOpen.TdmConfig.ulSinStream = ECHO_SIN_STREAM2; + ChannelOpen.TdmConfig.ulSinTimeslot = nSlot; + + ChannelOpen.TdmConfig.ulRoutStream = ECHO_ROUT_STREAM2; + ChannelOpen.TdmConfig.ulRoutTimeslot = nSlot; + + ChannelOpen.TdmConfig.ulSoutStream = ECHO_SOUT_STREAM2; + ChannelOpen.TdmConfig.ulSoutTimeslot = nSlot; + + /* Set the desired VQE features.*/ + ChannelOpen.VqeConfig.fEnableNlp = TRUE; + ChannelOpen.VqeConfig.fRinDcOffsetRemoval = TRUE; + ChannelOpen.VqeConfig.fSinDcOffsetRemoval = TRUE; + + ChannelOpen.VqeConfig.ulComfortNoiseMode = cOCT6100_COMFORT_NOISE_NORMAL; + /* cOCT6100_COMFORT_NOISE_NORMAL + cOCT6100_COMFORT_NOISE_EXTENDED, + cOCT6100_COMFORT_NOISE_OFF, + cOCT6100_COMFORT_NOISE_FAST_LATCH + */ + ulResult = Oct6100ChannelOpen( pApiInstance, + &ChannelOpen ); + if (ulResult != cOCT6100_ERR_OK) { + ERR("Found error on chan %d\n", nChan); + return ulResult; + } + } + + + DBG("%s: Finishing\n", __func__); + free(pApiInstance); + free(echo_mod); + return cOCT6100_ERR_OK; + +} +//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +int load_echo(struct astribank_device *astribank, char *filename, int is_alaw) +{ + int iLen; + int ret; + unsigned char *pbyFileData = NULL; + const char *devstr; + + devstr = xusb_devpath(astribank->xusb); + INFO("%s [%s]: Loading ECHOCAN Firmware: %s (%s)\n", + devstr, xusb_serial(astribank->xusb), filename, + (is_alaw) ? "alaw" : "ulaw"); + usb_buffer_init(astribank, &usb_buffer); + ret = init_octasic(filename, astribank, is_alaw); + if (ret) { + ERR("ECHO %s burning failed (%08X)\n", filename, ret); + return -ENODEV; + } + ret = usb_buffer_flush(astribank, &usb_buffer); + if (ret < 0) { + ERR("ECHO %s buffer flush failed (%d)\n", filename, ret); + return -ENODEV; + } + usb_buffer_showstatistics(astribank, &usb_buffer); + return 0; +} + +int echo_ver(struct astribank_device *astribank) +{ + usb_buffer_init(astribank, &usb_buffer); + return get_ver(astribank); +} + diff --git a/xpp/echo_loader.h b/xpp/echo_loader.h new file mode 100644 index 0000000..6e4e3a7 --- /dev/null +++ b/xpp/echo_loader.h @@ -0,0 +1,32 @@ +#ifndef ECHO_LOADER_H +#define ECHO_LOADER_H +/* + * Written by Oron Peled + * Copyright (C) 2008, 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 +#include "astribank_usb.h" + +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 echo_ver(struct astribank_device *astribank); + +#endif /* ECHO_LOADER_H */ diff --git a/xpp/perl_modules/Dahdi/Xpp/Xpd.pm b/xpp/perl_modules/Dahdi/Xpp/Xpd.pm index 6cd49a7..197d969 100644 --- a/xpp/perl_modules/Dahdi/Xpp/Xpd.pm +++ b/xpp/perl_modules/Dahdi/Xpp/Xpd.pm @@ -327,6 +327,14 @@ sub new($$$$$) { # static xpd related helper functions #------------------------------------ +# Returns only the telephony XPD's from a list +# of one or more XPD's. +# I.e: Filters-out ECHO cancelers +sub telephony_devs { + my @devs = grep { $_->channels } @_; + return @devs; +} + sub format_rank($$) { my ($rank, $prio) = @_; my $width = 2; diff --git a/xpp/waitfor_xpds b/xpp/waitfor_xpds index 30b3ac5..352a250 100755 --- a/xpp/waitfor_xpds +++ b/xpp/waitfor_xpds @@ -56,7 +56,7 @@ fi # Wait for driver and first device echo -n 1>&2 "Astribanks detection " -tries=10 +tries=40 while [ ! -e "/sys/bus/astribanks/devices/xbus-00" ] do if [ "$tries" -le 0 ]; then diff --git a/xpp/xpp_fxloader b/xpp/xpp_fxloader index e316d0e..5ed87e5 100644 --- a/xpp/xpp_fxloader +++ b/xpp/xpp_fxloader @@ -67,9 +67,11 @@ 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_FW="${USB_FW:-USB_FW.hex}" +USB_RECOV="${USB_RECOV:-USB_RECOV.hex}" if [ -r "$DEFAULTS" ]; then . "$DEFAULTS" @@ -151,6 +153,38 @@ load_fw_device() { debug "FPGA loading $fw into $dev" run_astribank_hexload -D "$dev" -F "$FIRMWARE_DIR/$fw" if [ "$fw" = "FPGA_1161.hex" ]; then + echo_file="$FIRMWARE_DIR/OCT6104E-256D.ima" + law='' + law_str='uLaw' + abtool_output=`$ASTRIBANK_TOOL -D "$dev" -Q 2>/dev/null` + card_type=`echo "$abtool_output" | grep '^CARD 4:' | cut -d= -f2 | cut -d. -f1` + if [ "$card_type" = '5' ]; then + debug "ECHO burning into $dev: $echo_file" + card_type_first=`echo "$abtool_output" | grep '^CARD 0:' | cut -d= -f2 | cut -d. -f1` + case "$card_type_first" 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 + $LOGGER "WARNING: ECHO burning was skipped (no capabilities)" + fi + fi pic_files=`echo "$FIRMWARE_DIR"/PIC_TYPE_[1-4].hex` debug "PIC burning into $dev: $pic_files" run_astribank_hexload -D "$dev" -p $pic_files