summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTzafrir Cohen <tzafrir.cohen@xorcom.com>2009-04-02 20:56:42 +0000
committerTzafrir Cohen <tzafrir.cohen@xorcom.com>2009-04-02 20:56:42 +0000
commitcb01267f42eba7bc5a572235e5d2571cc9b60303 (patch)
tree421d7509698c604a76499fdc95432642f323d9ad
parent8c29f87f0fe6465a171644f1d916481d0d2def4f (diff)
Support for Astribanks 116x: tools part
* New USB firmware loading mechanism. - Incompatible with previous one: upgrade using fxload or hard reset - astribank_hexload is the new low-level loading tool - fpga_load remains for backward compatibility. - xpp/astribank_upgrade: automate upgrading using fxload * Much enhanced control protocol ("MPP") - astribank_tool is the low-level tool for that. * Support for the TwinStar (dual USB port) - Managed through astribank_tool - Wrapper perl modules and scripts provided * Allow explicit ordering of Astribanks - /etc/dahdi/xpp_order - explicit order of Astribanks on the system - The default sorter is now to use those and fall back to connectors (previous default). - An option to dahdi_registration to change sorting. git-svn-id: http://svn.asterisk.org/svn/dahdi/tools/trunk@6313 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r--xpp/Makefile52
-rw-r--r--xpp/astribank_allow.c378
-rw-r--r--xpp/astribank_hexload.866
-rw-r--r--xpp/astribank_hexload.c201
-rw-r--r--xpp/astribank_tool.879
-rw-r--r--xpp/astribank_tool.c202
-rwxr-xr-xxpp/astribank_upgrade150
-rw-r--r--xpp/astribank_usb.c536
-rw-r--r--xpp/astribank_usb.h102
-rwxr-xr-xxpp/dahdi_drivers4
-rwxr-xr-xxpp/dahdi_genconf4
-rwxr-xr-xxpp/dahdi_hardware50
-rwxr-xr-xxpp/dahdi_registration34
-rw-r--r--xpp/debug.c53
-rw-r--r--xpp/debug.h46
-rw-r--r--xpp/hexfile.h46
-rwxr-xr-xxpp/lsdahdi2
-rw-r--r--xpp/mpp.h324
-rw-r--r--xpp/mpp_funcs.c1021
-rw-r--r--xpp/mpp_funcs.h78
-rw-r--r--xpp/perl_modules/Dahdi/Hardware.pm95
-rw-r--r--xpp/perl_modules/Dahdi/Hardware/PCI.pm12
-rw-r--r--xpp/perl_modules/Dahdi/Hardware/USB.pm85
-rw-r--r--xpp/perl_modules/Dahdi/Xpp.pm93
-rw-r--r--xpp/perl_modules/Dahdi/Xpp/Mpp.pm184
-rw-r--r--xpp/perl_modules/Dahdi/Xpp/Xbus.pm29
-rw-r--r--xpp/perl_modules/Dahdi/Xpp/Xpd.pm4
-rw-r--r--xpp/pic_loader.c275
-rw-r--r--xpp/pic_loader.h46
-rwxr-xr-xxpp/twinstar149
-rwxr-xr-xxpp/twinstar_hook93
-rwxr-xr-xxpp/twinstar_setup144
-rw-r--r--xpp/xpp_fxloader128
-rw-r--r--xpp/xpp_order21
-rwxr-xr-xxpp/xpp_sync10
35 files changed, 4575 insertions, 221 deletions
diff --git a/xpp/Makefile b/xpp/Makefile
index 087bc4b..c8084b8 100644
--- a/xpp/Makefile
+++ b/xpp/Makefile
@@ -1,6 +1,5 @@
PEDANTIC = -ansi -pedantic -std=c99
-RANLIB = ranlib
INSTALL = install
INSTALL_DATA = install -m 644
@@ -54,12 +53,20 @@ PERL_SCRIPTS = \
PERL_MANS = $(PERL_SCRIPTS:%=%.8)
-TARGETS = perlcheck
-PROG_INSTALL = #genzaptelconf
+ABHEXLOAD_OBJS = astribank_hexload.o hexfile.o pic_loader.o astribank_usb.o mpp_funcs.o debug.o
+ABTOOL_OBJS = astribank_tool.o astribank_usb.o mpp_funcs.o debug.o
+ABALLOW_OBJS = astribank_allow.o astribank_usb.o mpp_funcs.o debug.o
+
+TARGETS = .perlcheck
+PROG_INSTALL =
MAN_INSTALL = $(PROG_INSTALL:%=%.8)
ifeq (1,$(PBX_USB))
-TARGETS += libhexfile.a fpga_load test_parse
-PROG_INSTALL += fpga_load
+TARGETS += fpga_load \
+ astribank_tool \
+ astribank_hexload \
+ astribank_allow \
+ test_parse
+PROG_INSTALL += fpga_load astribank_tool astribank_hexload
endif
ifneq (,$(PERLLIBDIR))
PROG_INSTALL += $(PERL_SCRIPTS)
@@ -94,27 +101,34 @@ ifneq (,$(PERLLIBDIR))
done
endif
-libhexfile.a: hexfile.o
- $(AR) cru $@ $^
- $(RANLIB) $@
+fpga_load: fpga_load.o hexfile.o
+ $(CC) -L. -o $@ $^ $(EXTRA_LIBS) -lusb
-fpga_load: fpga_load.o libhexfile.a
- $(CC) -L. -o $@ $@.o $(EXTRA_LIBS) -lhexfile -lusb
+astribank_hexload: $(ABHEXLOAD_OBJS)
+ $(CC) -L. -o $@ $(ABHEXLOAD_OBJS) $(EXTRA_LIBS) -lusb
-fpga_load.o: CFLAGS+=-D_GNU_SOURCE # We use memrchr()
+astribank_tool: $(ABTOOL_OBJS)
+ $(CC) -L. -o $@ $(ABTOOL_OBJS) $(EXTRA_LIBS) -lusb
+
+astribank_allow: $(ABALLOW_OBJS)
+ $(CC) -L. -o $@ $(ABALLOW_OBJS) $(EXTRA_LIBS) -lusb
-hexfile.o: hexfile.c hexfile.h
- $(CC) $(CFLAGS) $(PEDANTIC) -c $<
+fpga_load.o: CFLAGS+=-D_GNU_SOURCE # We use memrchr()
-test_parse.o: test_parse.c hexfile.h
- $(CC) $(CFLAGS) $(PEDANTIC) -c $<
+test_parse: test_parse.o hexfile.o
+ $(CC) -L. -o $@ $^ $(EXTRA_LIBS) -lusb
-test_parse: test_parse.o libhexfile.a
- $(CC) -L. -o $@ $@.o $(EXTRA_LIBS) -lhexfile -lusb
-perlcheck: $(PERL_SCRIPTS)
+.perlcheck: $(PERL_SCRIPTS)
for i in $^; do perl -I./perl_modules -c $$i || exit 1; done
touch $@
clean:
- $(RM) *.o $(TARGETS)
+ $(RM) .depend *.o $(TARGETS)
+
+.PHONY: depend
+depend: .depend
+.depend: *.c *.h
+ @$(CC) -MM *.c > $@ || rm -f $@
+
+include .depend
diff --git a/xpp/astribank_allow.c b/xpp/astribank_allow.c
new file mode 100644
index 0000000..ec60d4a
--- /dev/null
+++ b/xpp/astribank_allow.c
@@ -0,0 +1,378 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il> and
+ * Alex Landau <alex.landau@xorcom.com>
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <getopt.h>
+#include <time.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include "mpp.h"
+#include "mpp_funcs.h"
+#include "debug.h"
+
+static const char rcsid[] = "$Id$";
+
+#define DBG_MASK 0x80
+
+static char *progname;
+
+static void usage()
+{
+ fprintf(stderr, "Usage: %s [options...] -D {/proc/bus/usb|/dev/bus/usb}/<bus>/<dev> options\n", progname);
+ fprintf(stderr, "\tOptions:\n");
+ fprintf(stderr, "\t\t[-v] # Increase verbosity\n");
+ fprintf(stderr, "\t\t[-d mask] # Debug mask (0xFF for everything)\n");
+ fprintf(stderr, "\t\t[-w] # Write capabilities to EEPROM, otherwise read capabilities\n");
+ fprintf(stderr, "\t\t[-f filename] # License filename (stdin/stdout if not specified)\n\n");
+ exit(1);
+}
+
+static int capabilities_burn(
+ struct astribank_device *astribank,
+ struct eeprom_table *eeprom_table,
+ struct capabilities *capabilities,
+ struct capkey *key)
+{
+ int ret;
+
+ INFO("Burning capabilities\n");
+ ret = mpp_caps_set(astribank, eeprom_table, capabilities, key);
+ if(ret < 0) {
+ ERR("Capabilities burning failed: %d\n", ret);
+ return ret;
+ }
+ INFO("Done\n");
+ return 0;
+}
+
+static int bin_to_file(void *buf, int len, FILE *f)
+{
+ static int bytes_on_line;
+ unsigned char *p = buf;
+ if (buf == NULL) {
+ if (bytes_on_line != 0) {
+ if (fprintf(f, "\n") != 1)
+ return -1;
+ bytes_on_line = 0;
+ }
+ return 0;
+ }
+ int i;
+ for (i = 0; i < len; i++) {
+ if (fprintf(f, "%02x", *p++) != 2)
+ return -1;
+ bytes_on_line++;
+ if (bytes_on_line >= 16) {
+ if (fprintf(f, "\n") != 1)
+ return -1;
+ bytes_on_line = 0;
+ }
+ }
+ return 0;
+}
+
+static int write_to_file(struct eeprom_table *eeprom_table, struct capabilities *caps, struct capkey *key, FILE *f)
+{
+ fprintf(f, "-----BEGIN XORCOM LICENSE BLOCK-----\n");
+ fprintf(f, "Version: 1.0\n");
+ fprintf(f, "Timestamp: %u\n", caps->timestamp);
+ fprintf(f, "Serial: %.*s\n", LABEL_SIZE, eeprom_table->label);
+ fprintf(f, "Capabilities.Port.FXS: %d\n", caps->ports_fxs);
+ 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.Twinstar: %d\n", CAP_EXTRA_TWINSTAR(caps));
+ fprintf(f, "Data:\n");
+ bin_to_file(eeprom_table, sizeof(*eeprom_table), f);
+ bin_to_file(caps, sizeof(*caps), f);
+ bin_to_file(key, sizeof(*key), f);
+ bin_to_file(NULL, 0, f);
+ fprintf(f, "-----END XORCOM LICENSE BLOCK-----\n");
+ return 0;
+}
+
+/*
+ * Removes whitespace on both sizes of the string.
+ * Returns a pointer to the first non-space char. The string
+ * is modified in place to trim trailing whitespace.
+ * If the whole string is whitespace, returns NULL.
+ */
+char *trim(char *s)
+{
+ int len = strlen(s);
+ while (len > 0 && isspace(s[len-1])) {
+ len--;
+ }
+ if (len == 0)
+ return NULL;
+ s[len] = '\0';
+ while (isspace(*s))
+ s++;
+ /* *s is not a space, since in this case we'd return NULL above */
+ return s;
+}
+
+int get_key_value(char *line, char **key, char **value)
+{
+ char *p = strchr(line, ':');
+ if (p == NULL)
+ return -1;
+ *p = '\0';
+ *key = trim(line);
+ *value = trim(p + 1);
+ return 0;
+}
+
+static int hex_digit_to_int(char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ else if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ else
+ return -1;
+}
+
+static int str_to_bin(char *line, void *buf, int maxlen)
+{
+ static int offset;
+ unsigned char *p = buf;
+ if (strlen(line) % 2 != 0)
+ return -1;
+ while (offset < maxlen && *line) {
+ uint8_t value;
+ char c = hex_digit_to_int(*line++);
+ if (c < 0 || *line == '\0')
+ return -1;
+ value = c << 4;
+ c = hex_digit_to_int(*line++);
+ if (c < 0)
+ return -1;
+ value |= c;
+ p[offset++] = value;
+ }
+ if (offset == maxlen && *line)
+ return -1;
+ return offset;
+}
+
+static int read_from_file(struct eeprom_table *eeprom_table, struct capabilities *caps, struct capkey *capkey, FILE *f)
+{
+ char buf[256];
+ char *line, *key, *value;
+ int state = 0;
+ int lineno = 0;
+ struct table {
+ struct eeprom_table eeprom_table;
+ struct capabilities capabilities;
+ struct capkey capkey;
+ } PACKED table;
+
+ memset(&table, 0, sizeof(struct table));
+ /*
+ * states:
+ * 0: start - before BEGIN_LICENSE_BLOCK line. on BEGIN_LICENSE_BLOCK line goto 1.
+ * 1: read Version, goto 2. if not version line then error.
+ * 2: after BEGIN line. split line into key:value. if line is Data:, goto 3.
+ * 3: read binary data. if line is END_LICENSE_BLOCK goto 4.
+ * 4: END_LICENSE_BLOCK - if not EOF - error. otherwise exit loop and success.
+ */
+ while (fgets(buf, 256, f) != NULL) {
+ lineno++;
+ int len = strlen(buf);
+ if (len > 0 && buf[len-1] != '\n') {
+ ERR("Line %d: Line too long\n", lineno);
+ return -1;
+ }
+ line = trim(buf);
+ if (line == NULL) {
+ ERR("Line %d: Empty line\n", lineno);
+ }
+ switch (state) {
+ case 0:
+ if (strcmp(line, "-----BEGIN XORCOM LICENSE BLOCK-----") == 0)
+ state = 1;
+ else {
+ ERR("Line %d: Invalid license begin block\n", lineno);
+ return -1;
+ }
+ break;
+ case 1:
+ if (get_key_value(line, &key, &value) < 0) {
+ ERR("Line %d: Can't parse line\n", lineno);
+ return -1;
+ }
+ if (strcmp(key, "Version") == 0) {
+ if (strcmp(value, "1.0") == 0) {
+ state = 2;
+ } else {
+ ERR("Line %d: Unknown license file version '%s', need version '1.0'\n", lineno, value);
+ return -1;
+ }
+ } else {
+ ERR("Line %d: No license file version\n", lineno);
+ return -1;
+ }
+ break;
+ case 2:
+ if (get_key_value(line, &key, &value) < 0) {
+ ERR("Line %d: Can't parse line\n", lineno);
+ return -1;
+ }
+ if (strcmp(key, "Data") == 0) {
+ state = 3;
+ break;
+ }
+ break;
+ case 3:
+ if (strcmp(line, "-----END XORCOM LICENSE BLOCK-----") == 0) {
+ state = 4;
+ break;
+ }
+ if (str_to_bin(line, &table, sizeof(table)) < 0) {
+ ERR("Line %d: Error in data block\n", lineno);
+ return -1;
+ }
+ break;
+ case 4:
+ ERR("Extra data after license end block\n");
+ return -1;
+ }
+ }
+ if (state != 4) {
+ ERR("Invalid license file\n");
+ return -1;
+ }
+ memcpy(eeprom_table, &table.eeprom_table, sizeof(*eeprom_table));
+ memcpy(caps, &table.capabilities, sizeof(*caps));
+ memcpy(capkey, &table.capkey, sizeof(*capkey));
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char *devpath = NULL;
+ struct astribank_device *astribank;
+ struct eeprom_table eeprom_table;
+ struct capabilities caps;
+ struct capkey key;
+ const char options[] = "vd:D:wf:";
+ int do_write = 0;
+ FILE *file;
+ char *filename = NULL;
+ int ret;
+
+ progname = argv[0];
+ while (1) {
+ int c;
+
+ c = getopt (argc, argv, options);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'D':
+ devpath = optarg;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'd':
+ debug_mask = strtoul(optarg, NULL, 0);
+ break;
+ case 'w':
+ do_write = 1;
+ break;
+ case 'f':
+ filename = optarg;
+ break;
+ case 'h':
+ default:
+ ERR("Unknown option '%c'\n", c);
+ usage();
+ }
+ }
+ if(!devpath) {
+ ERR("Missing device path\n");
+ usage();
+ }
+ DBG("Startup %s\n", devpath);
+ if((astribank = mpp_init(devpath)) == NULL) {
+ ERR("Failed initializing MPP\n");
+ return 1;
+ }
+ if(astribank->eeprom_type != EEPROM_TYPE_LARGE) {
+ ERR("Cannot use this program with astribank EEPROM type %d (need %d)\n",
+ astribank->eeprom_type, EEPROM_TYPE_LARGE);
+ return 1;
+ }
+ ret = mpp_caps_get(astribank, &eeprom_table, &caps, &key);
+ if(ret < 0) {
+ ERR("Failed to get original capabilities: %d\n", ret);
+ return 1;
+ }
+ if (do_write) {
+ /* update capabilities based on input file */
+ file = stdin;
+ if (filename) {
+ file = fopen(filename, "r");
+ if (file == NULL) {
+ ERR("Can't open file '%s'\n", filename);
+ return 1;
+ }
+ }
+ ret = read_from_file(&eeprom_table, &caps, &key, file);
+ if (ret < 0) {
+ ERR("Failed to read capabilities from file: %d\n", ret);
+ return 1;
+ }
+ show_capabilities(&caps, stderr);
+ if (capabilities_burn(astribank, &eeprom_table, &caps, &key) < 0)
+ return 1;
+ if (file != stdin)
+ fclose(file);
+ } else {
+ /* print capabilities to stdout */
+ file = stdout;
+ if (filename) {
+ file = fopen(filename, "w");
+ if (file == NULL) {
+ ERR("Can't create file '%s'\n", filename);
+ return 1;
+ }
+ }
+ ret = write_to_file(&eeprom_table, &caps, &key, file);
+ if (ret < 0) {
+ ERR("Failed to write capabilities to file: %d\n", ret);
+ return 1;
+ }
+ if (file != stdout)
+ fclose(file);
+ }
+ mpp_exit(astribank);
+ return 0;
+}
diff --git a/xpp/astribank_hexload.8 b/xpp/astribank_hexload.8
new file mode 100644
index 0000000..4017f78
--- /dev/null
+++ b/xpp/astribank_hexload.8
@@ -0,0 +1,66 @@
+.TH "ASTRIBANK_HEXLOAD" "8" "29 March 2009" "" ""
+
+.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 [-h]
+
+.SH DESCRIPTION
+.B astribank_hexload
+is a second-stage firmware loader for Xorcom Astribanks. Note that some
+older models use fpga_load(8) instead.
+
+It is used to load a file in the Intel HEX format into a Xorcom
+Astribank. It can be used to load either an FPGA firmware or a PIC
+firmware. It is normally run by the script xpp_fxloader.
+
+.SH OPTIONS
+.B -D
+.I device-path
+.RS
+Required. The device to read from/write to. On modern UDEV-based system
+this is usually /dev/bus/usb/\fIbus_num\fR/\fIdevice_num\fR,
+where \fIbus_num\fR and \fIdevice_num\fR are the first two numbers in the
+output of lsusb(8).
+On older systems that use usbfs, it is usually
+/proc/bus/usb/\fIbus_num\fR/\fIdevice_num\fR.
+.RE
+
+.B -F
+.RS
+The firmware to load is a FPGA firmware.
+.RE
+
+.B -p
+.RS
+The firmware to load is a PIC firmware.
+.RE
+
+.B -v
+.RS
+Increase verbosity. May be used multiple times.
+.RE
+
+.B -d \fImask\fR
+.RS
+Set debug mask to \fImask\fR. Default is 0, 0xFF is "everything".
+.RE
+
+.B -h
+.RS
+Displays usage message.
+.RE
+
+.SH SEE ALSO
+fxload(8), lsusb(8), astribank_tool(8), fpga_load(8)
+
+.SH AUTHOR
+This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> .
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common-licenses/GPL.
diff --git a/xpp/astribank_hexload.c b/xpp/astribank_hexload.c
new file mode 100644
index 0000000..85cfd23
--- /dev/null
+++ b/xpp/astribank_hexload.c
@@ -0,0 +1,201 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <arpa/inet.h>
+#include "debug.h"
+#include "hexfile.h"
+#include "mpp_funcs.h"
+#include "pic_loader.h"
+#include "astribank_usb.h"
+
+#define DBG_MASK 0x80
+#define MAX_HEX_LINES 10000
+
+static char *progname;
+
+static void usage()
+{
+ fprintf(stderr, "Usage: %s [options...] -D {/proc/bus/usb|/dev/bus/usb}/<bus>/<dev> hexfile...\n", progname);
+ fprintf(stderr, "\tOptions: {-F|-p}\n");
+ 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[-d mask] # Debug mask (0xFF for everything)\n");
+ exit(1);
+}
+
+int handle_hexline(struct astribank_device *astribank, struct hexline *hexline)
+{
+ uint16_t len;
+ uint16_t offset_dummy;
+ uint8_t *data;
+ int ret;
+
+ assert(hexline);
+ assert(astribank);
+ if(hexline->d.content.header.tt != TT_DATA) {
+ DBG("Non data record type = %d\n", hexline->d.content.header.tt);
+ return 0;
+ }
+ len = hexline->d.content.header.ll;
+ offset_dummy = hexline->d.content.header.offset;
+ data = hexline->d.content.tt_data.data;
+ if((ret = mpp_send_seg(astribank, data, offset_dummy, len)) < 0) {
+ ERR("Failed FPGA send line: %d\n", ret);
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int load_fpga(struct astribank_device *astribank, const char *hexfile)
+{
+ struct hexdata *hexdata = NULL;
+ int finished = 0;
+ int ret;
+ int i;
+
+ if((hexdata = parse_hexfile(hexfile, MAX_HEX_LINES)) == NULL) {
+ perror(hexfile);
+ return -errno;
+ }
+ INFO("Loading FPGA firmware version %s\n", hexdata->version_info);
+#if 0
+ FILE *fp;
+ if((fp = fopen("fpga_dump_new.txt", "w")) == NULL) {
+ perror("dump");
+ exit(1);
+ }
+#endif
+ if((ret = mpp_send_start(astribank, DEST_FPGA)) < 0) {
+ ERR("Failed FPGA send start: %d\n", ret);
+ return ret;
+ }
+ for(i = 0; i < hexdata->maxlines; i++) {
+ struct hexline *hexline = hexdata->lines[i];
+
+ if(!hexline)
+ break;
+ if(finished) {
+ ERR("Extra data after End Of Data Record (line %d)\n", i);
+ return 0;
+ }
+ if(hexline->d.content.header.tt == TT_EOF) {
+ DBG("End of data\n");
+ finished = 1;
+ continue;
+ }
+ if((ret = handle_hexline(astribank, hexline)) < 0) {
+ ERR("Failed FPGA sending in lineno %d (ret=%d)\n", i, ret);;
+ return ret;
+ }
+ }
+ if((ret = mpp_send_end(astribank)) < 0) {
+ ERR("Failed FPGA send end: %d\n", ret);
+ return ret;
+ }
+#if 0
+ fclose(fp);
+#endif
+ free_hexdata(hexdata);
+ INFO("FPGA firmware loaded successfully\n");
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char *devpath = NULL;
+ struct astribank_device *astribank;
+ int opt_fpga = 0;
+ int opt_pic = 0;
+ const char options[] = "vd:D:Fp";
+ int iface_num;
+ int ret;
+
+ progname = argv[0];
+ while (1) {
+ int c;
+
+ c = getopt (argc, argv, options);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'D':
+ devpath = optarg;
+ break;
+ case 'F':
+ opt_fpga = 1;
+ break;
+ case 'p':
+ opt_pic = 1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'd':
+ debug_mask = strtoul(optarg, NULL, 0);
+ break;
+ case 'h':
+ default:
+ ERR("Unknown option '%c'\n", c);
+ return 1;
+ }
+ }
+ if((opt_fpga ^ opt_pic) == 0) {
+ ERR("The -F and -p options are mutually exclusive.\n");
+ usage();
+ }
+ iface_num = (opt_fpga) ? 1 : 0;
+ if(opt_fpga) {
+ if(optind != argc - 1) {
+ ERR("The -F option requires exacly one hexfile argument\n");
+ usage();
+ }
+ }
+ if(!devpath) {
+ ERR("Missing device path.\n");
+ usage();
+ }
+ if((astribank = astribank_open(devpath, iface_num)) == NULL) {
+ ERR("Opening astribank failed\n");
+ return 1;
+ }
+ show_astribank_info(astribank);
+ if(opt_fpga) {
+ if(load_fpga(astribank, argv[optind]) < 0) {
+ ERR("Loading FPGA firmware failed\n");
+ return 1;
+ }
+ } else if(opt_pic) {
+ if((ret = load_pic(astribank, argc - optind, argv + optind)) < 0) {
+ ERR("Loading PIC's failed\n");
+ return 1;
+ }
+ }
+ astribank_close(astribank, 0);
+ return 0;
+}
diff --git a/xpp/astribank_tool.8 b/xpp/astribank_tool.8
new file mode 100644
index 0000000..0507ad5
--- /dev/null
+++ b/xpp/astribank_tool.8
@@ -0,0 +1,79 @@
+.TH "ASTRIBANK_TOOL" "8" "29 March 2009" "" ""
+
+.SH NAME
+astribank_tool \- Xorcom Astribank (xpp) control tool
+.SH SYNOPSIS
+.B astribank_tool -D \fIdevice-path\fR [ options ]
+
+.B astribank_tool [-h]
+
+.SH DESCRIPTION
+.B astribank_tool
+is a tool to control the USB-level functionality of an Astribank.
+
+.SH OPTIONS
+.B -D
+.I device-path
+.RS
+Required. The device to read from/write to. On modern UDEV-based system
+this is usually /dev/bus/usb/\fIbus_num\fR/\fIdevice_num\fR,
+where \fIbus_num\fR and \fIdevice_num\fR are the first two numbers in the
+output of lsusb(8).
+On older systems that use usbfs, it is usually
+/proc/bus/usb/\fIbus_num\fR/\fIdevice_num\fR.
+.RE
+
+.B -p \fInum\fR
+.RS
+Set the TwinStar port number. Either 0 or 1.
+
+(TODO: explain).
+.RE
+
+.B -r \fItype\fR
+.RS
+Reset the Astribank and renumerate its USB connection to power on product ID.
+
+Tyep can be: \fBhalf\fR or \fBfull\fR.
+
+(TODO: explain those).
+.RE
+
+.B -w 0|1
+.RS
+Enable (1) or disable (0) the TwinStar watchdog. When enabled, the
+Astribank will jump to the second port if this system is "not working"
+and the system on the second port is available.
+.RE
+
+.B -n
+.RS
+Renumerate the Astribank product number (e.g: from 1161 to 1162).
+.RE
+
+.B -v
+.RS
+Increase verbosity. May be used multiple times.
+.RE
+
+.B -d \fImask\fR
+.RS
+Set debug mask to \fImask\fR. Default is 0, 0xFF is "everything".
+.RE
+
+.B -h
+.RS
+Displays usage message.
+.RE
+
+.SH SEE ALSO
+fxload(8), lsusb(8), astribank_hextool(8)
+
+.SH AUTHOR
+This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> .
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common-licenses/GPL.
diff --git a/xpp/astribank_tool.c b/xpp/astribank_tool.c
new file mode 100644
index 0000000..d5ba667
--- /dev/null
+++ b/xpp/astribank_tool.c
@@ -0,0 +1,202 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <getopt.h>
+#include <arpa/inet.h>
+#include "mpp_funcs.h"
+#include "debug.h"
+
+#define DBG_MASK 0x80
+
+static char *progname;
+
+static void usage()
+{
+ fprintf(stderr, "Usage: %s [options...] -D {/proc/bus/usb|/dev/bus/usb}/<bus>/<dev>\n", progname);
+ fprintf(stderr, "\tOptions: {-n|-r kind}\n");
+ fprintf(stderr, "\t\t[-n] # Renumerate device\n");
+ fprintf(stderr, "\t\t[-r kind] # Reset: kind = {half|full}\n");
+ fprintf(stderr, "\t\t[-p port] # TwinStar: USB port number [0, 1]\n");
+ fprintf(stderr, "\t\t[-w (0|1)] # TwinStar: Watchdog off or on guard\n");
+ fprintf(stderr, "\t\t[-v] # Increase verbosity\n");
+ fprintf(stderr, "\t\t[-d mask] # Debug mask (0xFF for everything)\n");
+ exit(1);
+}
+
+static int reset_kind(const char *arg)
+{
+ static const struct {
+ const char *name;
+ int type_code;
+ } reset_kinds[] = {
+ { "half", 0 },
+ { "full", 1 },
+ };
+ int i;
+
+ for(i = 0; i < sizeof(reset_kinds)/sizeof(reset_kinds[0]); i++) {
+ if(strcasecmp(reset_kinds[i].name, arg) == 0)
+ return reset_kinds[i].type_code;
+ }
+ ERR("Uknown reset kind '%s'\n", arg);
+ return -1;
+}
+
+
+static int show_hardware(struct astribank_device *astribank)
+{
+ uint8_t unit;
+ uint8_t card_status;
+ uint8_t card_type;
+ int ret;
+ struct eeprom_table eeprom_table;
+ struct capabilities capabilities;
+ struct extrainfo extrainfo;
+
+ show_astribank_info(astribank);
+ ret = mpp_caps_get(astribank, &eeprom_table, &capabilities, NULL);
+ if(ret < 0)
+ return ret;
+ show_eeprom(&eeprom_table, stdout);
+ show_astribank_status(astribank, stdout);
+ if(astribank->eeprom_type == EEPROM_TYPE_LARGE) {
+ show_capabilities(&capabilities, stdout);
+ if(STATUS_FPGA_LOADED(astribank->status)) {
+ for(unit = 0; unit < 4; unit++) {
+ ret = mpps_card_info(astribank, unit, &card_type, &card_status);
+ if(ret < 0)
+ return ret;
+ printf("CARD %d: type=%x.%x %s\n", unit,
+ ((card_type >> 4) & 0xF), (card_type & 0xF),
+ ((card_status & 0x1) ? "PIC" : "NOPIC"));
+ }
+ }
+ ret = mpp_extrainfo_get(astribank, &extrainfo);
+ if(ret < 0)
+ return ret;
+ show_extrainfo(&extrainfo, stdout);
+ if(CAP_EXTRA_TWINSTAR(&capabilities)) {
+ twinstar_show(astribank, stdout);
+ }
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char *devpath = NULL;
+ struct astribank_device *astribank;
+ const char options[] = "vd:D:nr:p:w:";
+ int opt_renumerate = 0;
+ char *opt_port = NULL;
+ char *opt_watchdog = NULL;
+ char *opt_reset = NULL;
+ int tws_portnum;
+ int full_reset;
+ int ret;
+
+ progname = argv[0];
+ while (1) {
+ int c;
+
+ c = getopt (argc, argv, options);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'D':
+ devpath = optarg;
+ break;
+ case 'n':
+ opt_renumerate++;
+ break;
+ case 'p':
+ opt_port = optarg;
+ break;
+ case 'w':
+ opt_watchdog = optarg;
+ break;
+ case 'r':
+ opt_reset = optarg;
+ if((full_reset = reset_kind(opt_reset)) < 0)
+ usage();
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'd':
+ debug_mask = strtoul(optarg, NULL, 0);
+ break;
+ case 'h':
+ default:
+ ERR("Unknown option '%c'\n", c);
+ usage();
+ }
+ }
+ if(!devpath) {
+ ERR("Missing device path\n");
+ usage();
+ }
+ DBG("Startup %s\n", devpath);
+ if((astribank = mpp_init(devpath)) == NULL) {
+ ERR("Failed initializing MPP\n");
+ return 1;
+ }
+ show_hardware(astribank);
+ if(opt_reset) {
+ if((ret = mpp_reset(astribank, full_reset)) < 0) {
+ ERR("%s Reseting astribank failed: %d\n",
+ (full_reset) ? "Full" : "Half", ret);
+ }
+ } else if(opt_renumerate) {
+ if((ret = mpp_renumerate(astribank)) < 0) {
+ ERR("Renumerating astribank failed: %d\n", ret);
+ }
+ } else if(opt_watchdog) {
+ int watchdogstate = strtoul(opt_watchdog, NULL, 0);
+
+ INFO("TWINSTAR: Setting watchdog %s-guard\n",
+ (watchdogstate) ? "on" : "off");
+ if((ret = mpp_tws_setwatchdog(astribank, watchdogstate)) < 0) {
+ ERR("Failed to set watchdog to %d\n", watchdogstate);
+ return 1;
+ }
+ } else if(opt_port) {
+ int new_portnum = strtoul(opt_port, NULL, 0);
+ char *msg = (new_portnum == tws_portnum)
+ ? " Same same, never mind..."
+ : "";
+
+ INFO("TWINSTAR: Setting portnum to %d.%s\n", new_portnum, msg);
+ if((ret = mpp_tws_setportnum(astribank, new_portnum)) < 0) {
+ ERR("Failed to set USB portnum to %d\n", new_portnum);
+ return 1;
+ }
+ }
+ mpp_exit(astribank);
+ return 0;
+}
diff --git a/xpp/astribank_upgrade b/xpp/astribank_upgrade
new file mode 100755
index 0000000..71ae238
--- /dev/null
+++ b/xpp/astribank_upgrade
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+# astribank_upgrade: force load Xorcom Astribank (XPP) USB firmware
+# A reduced version of xpp_fxloader for manual upgrades.
+#
+# Written by Oron Peled <oron@actcom.co.il>
+# Copyright (C) 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.
+#
+
+set -e
+
+# Make sure fxload is in the path:
+PATH="$PATH:/usr/local/sbin:/sbin:/usr/sbin"
+export PATH
+
+me=`basename $0`
+
+if [ -t 2 ]; then
+ LOGGER="logger -i -t '$me' -s"
+else
+ LOGGER="logger -i -t '$me'"
+fi
+
+USBFS_PREFIX=/proc/bus/usb
+DEVUSB_PREFIX=/dev/bus/usb
+USB_PREFIX=
+
+USB_FW="${USB_FW:-USB_FW.hex}"
+
+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
+}
+
+load_usb_fw() {
+ v_id=$1
+ p_id=$2
+ fw=$3
+
+ devices=`find_dev $v_id $p_id`
+ for dev in $devices
+ do
+ ver=$(awk '/\$Id:/ { print $4 }' $FIRMWARE_DIR/$fw)
+ $LOGGER "USB Firmware $FIRMWARE_DIR/$fw (Version=$ver) into $dev"
+ run_fxload -D $dev -I $FIRMWARE_DIR/$fw || exit 1
+ done
+}
+
+numdevs() {
+ v_ids="$1"
+ p_ids="$2"
+
+ for v in $v_ids
+ do
+ (
+ for p in $p_ids
+ do
+ find_dev $v $p
+ done
+ )
+ done | wc -w
+}
+
+wait_renumeration() {
+ num="$1"
+ v_ids="$2"
+ p_ids="$3"
+
+ while
+ n=`numdevs "$v_ids" "$p_ids"`
+ [ "$num" -gt "$n" ]
+ do
+ echo -n "."
+ sleep 1
+ done
+ echo "Got all $num devices"
+}
+
+if [ "$#" -ne 1 ]; then
+ echo >&2 "Usage: $0 <firmware_directory>"
+ exit 1
+fi
+FIRMWARE_DIR="$1"
+[ -f "$FIRMWARE_DIR/$USB_FW" ] || {
+ echo >&2 "$0: Could not find '$FIRMWARE_DIR/$USB_FW'"
+ exit 1
+}
+numdevs=`numdevs e4e4 '11[3456][01]'`
+$LOGGER -- "--------- LOADING NEW USB FIRMWARE: ($1) [$numdevs devices]"
+load_usb_fw e4e4 1130 $USB_FW
+load_usb_fw e4e4 1140 $USB_FW
+load_usb_fw e4e4 1150 $USB_FW
+load_usb_fw e4e4 1160 $USB_FW
+load_usb_fw e4e4 1131 $USB_FW
+load_usb_fw e4e4 1141 $USB_FW
+load_usb_fw e4e4 1151 $USB_FW
+load_usb_fw e4e4 1161 $USB_FW
+load_usb_fw e4e4 1132 $USB_FW
+load_usb_fw e4e4 1142 $USB_FW
+load_usb_fw e4e4 1152 $USB_FW
+load_usb_fw e4e4 1162 $USB_FW
+wait_renumeration $numdevs e4e4 '11[3456]1'
+$LOGGER -- "--------- NEW USB FIRMWARE IS LOADED"
diff --git a/xpp/astribank_usb.c b/xpp/astribank_usb.c
new file mode 100644
index 0000000..088d360
--- /dev/null
+++ b/xpp/astribank_usb.c
@@ -0,0 +1,536 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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.
+ *
+ */
+
+#define _GNU_SOURCE /* for memrchr() */
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <arpa/inet.h>
+#include "astribank_usb.h"
+#include "debug.h"
+
+static const char rcsid[] = "$Id$";
+
+#define DBG_MASK 0x01
+#define TIMEOUT 500
+
+#define TYPE_ENTRY(t,ni,n,ne,out,in,...) \
+ [t] = { \
+ .type_code = (t), \
+ .num_interfaces = (ni), \
+ .my_interface_num = (n), \
+ .num_endpoints = (ne), \
+ .my_ep_in = (in), \
+ .my_ep_out = (out), \
+ .name = #t, \
+ .endpoints = { __VA_ARGS__ }, \
+ }
+
+static const struct interface_type interface_types[] = {
+ TYPE_ENTRY(USB_11xx, 1, 0, 4, MP_EP_OUT, MP_EP_IN,
+ XPP_EP_OUT,
+ MP_EP_OUT,
+ XPP_EP_IN,
+ MP_EP_IN),
+ TYPE_ENTRY(USB_FIRMWARE_II, 2, 1, 2, MP_EP_OUT, MP_EP_IN,
+ MP_EP_OUT,
+ MP_EP_IN),
+ TYPE_ENTRY(USB_PIC, 2, 0, 2, XPP_EP_OUT, XPP_EP_IN,
+ XPP_EP_OUT,
+ XPP_EP_IN),
+
+};
+#undef TYPE_ENTRY
+
+//static int verbose = LOG_DEBUG;
+
+/*
+ * USB handling
+ */
+
+/* return 1 if:
+ * - str has a number
+ * - It is larger than 0
+ * - It equals num
+ */
+static int num_matches(int num, const char* str) {
+ int str_val = atoi(str);
+ if (str_val <= 0)
+ return 0;
+ return (str_val == num);
+}
+
+struct usb_device *dev_of_path(const char *path)
+{
+ struct usb_bus *bus;
+ struct usb_device *dev;
+ char dirname[PATH_MAX];
+ char filename[PATH_MAX];
+ const char *p;
+ int bnum;
+ int dnum;
+ int ret;
+
+ assert(path != NULL);
+ if(access(path, F_OK) < 0) {
+ perror(path);
+ return NULL;
+ }
+ /* Find last '/' */
+ if((p = memrchr(path, '/', strlen(path))) == NULL) {
+ ERR("Missing a '/' in %s\n", path);
+ return NULL;
+ }
+ /* Get the device number */
+ ret = sscanf(p + 1, "%d", &dnum);
+ if(ret != 1) {
+ ERR("Path tail is not a device number: '%s'\n", p);
+ return NULL;
+ }
+ /* Search for a '/' before that */
+ p = memrchr(path, '/', p - path);
+ if(p == NULL)
+ p = path; /* Relative path */
+ else
+ p++; /* skip '/' */
+ /* Get the bus number */
+ ret = sscanf(p, "%d", &bnum);
+ if(ret != 1) {
+ ERR("Path tail is not a bus number: '%s'\n", p);
+ return NULL;
+ }
+ sprintf(dirname, "%03d", bnum);
+ sprintf(filename, "%03d", dnum);
+ for (bus = usb_busses; bus; bus = bus->next) {
+ if (! num_matches(bnum, bus->dirname))
+ //if(strcmp(bus->dirname, dirname) != 0)
+ continue;
+ for (dev = bus->devices; dev; dev = dev->next) {
+ //if(strcmp(dev->filename, filename) == 0)
+ if (num_matches(dnum, dev->filename))
+ return dev;
+ }
+ }
+ ERR("no usb device match '%s'\n", path);
+ return NULL;
+}
+
+int get_usb_string(struct astribank_device *astribank, uint8_t item, char *buf, unsigned int len)
+{
+ char tmp[BUFSIZ];
+ int ret;
+
+ assert(astribank->handle);
+ if (!item)
+ return 0;
+ ret = usb_get_string_simple(astribank->handle, item, tmp, BUFSIZ);
+ if (ret <= 0)
+ return ret;
+ return snprintf(buf, len, "%s", tmp);
+}
+
+static int match_interface(const struct astribank_device *astribank,
+ const struct interface_type *itype)
+{
+ struct usb_interface *interface;
+ struct usb_interface_descriptor *iface_desc;
+ struct usb_config_descriptor *config_desc;
+ int i = itype - interface_types;
+ int inum;
+ int num_altsetting;
+
+ DBG("Checking[%d]: interfaces=%d interface num=%d endpoints=%d: \"%s\"\n",
+ i,
+ itype->num_interfaces,
+ itype->my_interface_num,
+ itype->num_endpoints,
+ itype->name);
+ config_desc = astribank->dev->config;
+ if (!config_desc) {
+ ERR("No configuration descriptor: strange USB1 controller?\n");
+ return 0;
+ }
+ if(config_desc->bNumInterfaces <= itype->my_interface_num) {
+ DBG("Too little interfaces: have %d need %d\n",
+ config_desc->bNumInterfaces, itype->my_interface_num + 1);
+ return 0;
+ }
+ if(astribank->my_interface_num != itype->my_interface_num) {
+ DBG("Wrong match -- not my interface num (wanted %d)\n", astribank->my_interface_num);
+ return 0;
+ }
+ inum = itype->my_interface_num;
+ interface = &config_desc->interface[inum];
+ assert(interface != NULL);
+ iface_desc = interface->altsetting;
+ num_altsetting = interface->num_altsetting;
+ assert(num_altsetting != 0);
+ assert(iface_desc != NULL);
+ if(iface_desc->bInterfaceClass != 0xFF) {
+ DBG("Bad interface class 0x%X\n", iface_desc->bInterfaceClass);
+ return 0;
+ }
+ if(iface_desc->bInterfaceNumber != itype->my_interface_num) {
+ DBG("Bad interface number %d\n", iface_desc->bInterfaceNumber);
+ return 0;
+ }
+ if(iface_desc->bNumEndpoints != itype->num_endpoints) {
+ DBG("Different number of endpoints %d\n", iface_desc->bNumEndpoints);
+ return 0;
+ }
+ return 1;
+}
+
+static int astribank_init(struct astribank_device *astribank)
+{
+ struct usb_device_descriptor *dev_desc;
+ struct usb_config_descriptor *config_desc;
+ struct usb_interface *interface;
+ struct usb_interface_descriptor *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ const struct interface_type *fwtype;
+ int i;
+
+ assert(astribank);
+ astribank->handle = usb_open(astribank->dev);
+ if(!astribank->handle) {
+ ERR("Failed to open usb device '%s/%s': %s\n",
+ astribank->dev->bus->dirname, astribank->dev->filename, usb_strerror());
+ return 0;
+ }
+ fwtype = astribank->fwtype;
+ if(usb_claim_interface(astribank->handle, fwtype->my_interface_num) != 0) {
+ ERR("usb_claim_interface: %s\n", usb_strerror());
+ return 0;
+ }
+ dev_desc = &astribank->dev->descriptor;
+ config_desc = astribank->dev->config;
+ if (!config_desc) {
+ ERR("usb interface without a configuration\n");
+ return 0;
+ }
+ DBG("Got config_desc. Looking for interface %d\n", fwtype->my_interface_num);
+ interface = &config_desc->interface[fwtype->my_interface_num];
+ iface_desc = interface->altsetting;
+ endpoint = iface_desc->endpoint;
+ astribank->is_usb2 = (endpoint->wMaxPacketSize == 512);
+ for(i = 0; i < iface_desc->bNumEndpoints; i++, endpoint++) {
+ DBG("Validating endpoint @ %d (interface %d)\n", i, fwtype->my_interface_num);
+ if(endpoint->bEndpointAddress != fwtype->endpoints[i]) {
+ ERR("Wrong endpoint 0x%X != 0x%X (at index %d)\n",
+ endpoint->bEndpointAddress,
+ fwtype->endpoints[i],
+ i);
+ return 0;
+ }
+ if(endpoint->bEndpointAddress == MP_EP_OUT || endpoint->bEndpointAddress == MP_EP_IN) {
+ if(endpoint->wMaxPacketSize > PACKET_SIZE) {
+ ERR("Endpoint #%d wMaxPacketSize too large (%d)\n", i, endpoint->wMaxPacketSize);
+ return 0;
+ }
+ }
+ }
+ astribank->my_ep_in = fwtype->my_ep_in;
+ astribank->my_ep_out = fwtype->my_ep_out;
+ if(get_usb_string(astribank, dev_desc->iManufacturer, astribank->iManufacturer, BUFSIZ) < 0)
+ return 0;
+ if(get_usb_string(astribank, dev_desc->iProduct, astribank->iProduct, BUFSIZ) < 0)
+ return 0;
+ if(get_usb_string(astribank, dev_desc->iSerialNumber, astribank->iSerialNumber, BUFSIZ) < 0)
+ return 0;
+ if(get_usb_string(astribank, iface_desc->iInterface, astribank->iInterface, BUFSIZ) < 0)
+ return 0;
+ INFO("ID=%04X:%04X Manufacturer=[%s] Product=[%s] SerialNumber=[%s] Interface=[%s]\n",
+ dev_desc->idVendor,
+ dev_desc->idProduct,
+ astribank->iManufacturer,
+ astribank->iProduct,
+ astribank->iSerialNumber,
+ astribank->iInterface);
+ if(usb_clear_halt(astribank->handle, astribank->my_ep_out) != 0) {
+ ERR("Clearing output endpoint: %s\n", usb_strerror());
+ return 0;
+ }
+ if(usb_clear_halt(astribank->handle, astribank->my_ep_in) != 0) {
+ ERR("Clearing input endpoint: %s\n", usb_strerror());
+ return 0;
+ }
+ if((i = flush_read(astribank)) < 0) {
+ ERR("flush_read failed: %d\n", i);
+ return 0;
+ }
+ return 1;
+}
+
+struct astribank_device *astribank_open(const char devpath[], int iface_num)
+{
+ struct astribank_device *astribank;
+ int i;
+
+ DBG("devpath='%s' iface_num=%d\n", devpath, iface_num);
+ if((astribank = malloc(sizeof(*astribank))) == NULL) {
+ ERR("Out of memory");
+ return NULL;
+ }
+ memset(astribank, 0, sizeof(*astribank));
+ astribank->my_interface_num = iface_num;
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+ astribank->dev = dev_of_path(devpath);
+ if(!astribank->dev) {
+ ERR("Bailing out\n");
+ goto fail;
+ }
+ DBG("Scan interface types (astribank has %d interfaces)\n", astribank->dev->config->bNumInterfaces);
+ for(i = 0; i < sizeof(interface_types)/sizeof(interface_types[0]); i++) {
+ if(match_interface(astribank, &interface_types[i])) {
+ DBG("Identified[%d]: interfaces=%d endpoints=%d: \"%s\"\n",
+ i,
+ interface_types[i].num_interfaces,
+ interface_types[i].num_endpoints,
+ interface_types[i].name);
+ astribank->fwtype = &interface_types[i];
+ goto found;
+ }
+ }
+ ERR("Didn't find suitable device\n");
+fail:
+ free(astribank);
+ return NULL;
+found:
+ if(!astribank_init(astribank))
+ goto fail;
+ astribank->tx_sequenceno = 1;
+ return astribank;
+}
+
+/*
+ * MP device handling
+ */
+void show_astribank_info(const struct astribank_device *astribank)
+{
+ assert(astribank != NULL);
+ printf("USB Firmware Type: [%s]\n", astribank->fwtype->name);
+ printf("USB iManufacturer: [%s]\n", astribank->iManufacturer);
+ printf("USB iProduct: [%s]\n", astribank->iProduct);
+ printf("USB iSerialNumber: [%s]\n", astribank->iSerialNumber);
+}
+
+void astribank_close(struct astribank_device *astribank, int disconnected)
+{
+ assert(astribank != NULL);
+ if(!astribank->handle)
+ return; /* Nothing to do */
+ if(!disconnected) {
+ if(usb_release_interface(astribank->handle, astribank->fwtype->my_interface_num) != 0) {
+ ERR("Releasing interface: usb: %s\n", usb_strerror());
+ }
+ }
+ if(usb_close(astribank->handle) != 0) {
+ ERR("Closing device: usb: %s\n", usb_strerror());
+ }
+ astribank->tx_sequenceno = 0;
+ astribank->handle = NULL;
+}
+
+int send_usb(struct astribank_device *astribank, char *buf, int len, int timeout)
+{
+ int ret;
+
+ dump_packet(LOG_DEBUG, __FUNCTION__, buf, len);
+ if(astribank->my_ep_out & USB_ENDPOINT_IN) {
+ ERR("send_usb called with an input endpoint 0x%x\n", astribank->my_ep_out);
+ return -EINVAL;
+ }
+ ret = usb_bulk_write(astribank->handle, astribank->my_ep_out, buf, len, timeout);
+ if(ret < 0) {
+ /*
+ * If the device was gone, it may be the
+ * result of renumeration. Ignore it.
+ */
+ if(ret != -ENODEV) {
+ ERR("bulk_write to endpoint 0x%x failed: (%d) %s\n",
+ astribank->my_ep_out, ret, usb_strerror());
+ dump_packet(LOG_ERR, "send_usb[ERR]", buf, len);
+ exit(2);
+ } else {
+ DBG("bulk_write to endpoint 0x%x got ENODEV\n", astribank->my_ep_out);
+ astribank_close(astribank, 1);
+ }
+ return ret;
+ } else if(ret != len) {
+ ERR("bulk_write to endpoint 0x%x short write: (%d) %s\n",
+ astribank->my_ep_out, ret, usb_strerror());
+ dump_packet(LOG_ERR, "send_usb[ERR]", buf, len);
+ return -EFAULT;
+ }
+ return ret;
+}
+
+int recv_usb(struct astribank_device *astribank, char *buf, size_t len, int timeout)
+{
+ int ret;
+
+ if(astribank->my_ep_in & USB_ENDPOINT_OUT) {
+ ERR("recv_usb called with an output endpoint 0x%x\n", astribank->my_ep_in);
+ return -EINVAL;
+ }
+ ret = usb_bulk_read(astribank->handle, astribank->my_ep_in, buf, len, timeout);
+ if(ret < 0) {
+ DBG("bulk_read from endpoint 0x%x failed: (%d) %s\n",
+ astribank->my_ep_in, ret, usb_strerror());
+ memset(buf, 0, len);
+ return ret;
+ }
+ dump_packet(LOG_DEBUG, __FUNCTION__, buf, ret);
+ return ret;
+}
+
+int flush_read(struct astribank_device *astribank)
+{
+ char tmpbuf[BUFSIZ];
+ int ret;
+
+ DBG("starting...\n");
+ memset(tmpbuf, 0, BUFSIZ);
+ ret = recv_usb(astribank, tmpbuf, BUFSIZ, 1);
+ if(ret < 0 && ret != -ETIMEDOUT) {
+ ERR("ret=%d\n", ret);
+ return ret;
+ } else if(ret > 0) {
+ DBG("Got %d bytes:\n", ret);
+ dump_packet(LOG_DEBUG, __FUNCTION__, tmpbuf, ret);
+ }
+ return 0;
+}
+
+
+int release_isvalid(uint16_t release)
+{
+ uint8_t rmajor = (release >> 8) & 0xFF;
+ uint8_t rminor = release & 0xFF;
+
+ return (rmajor > 0) &&
+ (rmajor < 10) &&
+ (rminor > 0) &&
+ (rminor < 10);
+}
+
+int label_isvalid(const char *label)
+{
+ int len;
+ int goodlen;
+ const char GOOD_CHARS[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "-_.";
+
+ len = strlen(label);
+ goodlen = strspn(label, GOOD_CHARS);
+ if(len > LABEL_SIZE) {
+ ERR("Label too long (%d > %d)\n", len, LABEL_SIZE);
+ return 0;
+ }
+ if(goodlen != len) {
+ ERR("Bad character in label (pos=%d)\n", goodlen);
+ return 0;
+ }
+ return 1;
+}
+
+int eeprom_fill(struct eeprom_table *eprm,
+ const char *vendor,
+ const char *product,
+ const char *release,
+ const char *label)
+{
+ uint16_t val;
+
+ eprm->source = 0xC0;
+ eprm->config_byte = 0;
+ if(vendor) {
+ val = strtoul(vendor, NULL, 0);
+ if(!val) {
+ ERR("Invalid vendor '%s'\n",
+ vendor);
+ return -EINVAL;
+ }
+ eprm->vendor = val;
+ }
+ if(product) {
+ val = strtoul(product, NULL, 0);
+ if(!val) {
+ ERR("Invalid product '%s'\n",
+ product);
+ return -EINVAL;
+ }
+ eprm->product = val;
+ }
+ if(release) {
+ int release_major = 0;
+ int release_minor = 0;
+ uint16_t value;
+
+ if(sscanf(release, "%d.%d", &release_major, &release_minor) != 2) {
+ ERR("Failed to parse release number '%s'\n", release);
+ return -EINVAL;
+ }
+ value = (release_major << 8) | release_minor;
+ DBG("Parsed release(%d): major=%d, minor=%d\n",
+ value, release_major, release_minor);
+ if(!release_isvalid(value)) {
+ ERR("Invalid release number 0x%X\n", value);
+ return -EINVAL;
+ }
+ eprm->release = value;
+ }
+ if(label) {
+ /* padding */
+ if(!label_isvalid(label)) {
+ ERR("Invalid label '%s'\n", label);
+ return -EINVAL;
+ }
+ memset(eprm->label, 0, LABEL_SIZE);
+ memcpy(eprm->label, label, strlen(label));
+ }
+ return 0;
+}
+
+int astribank_has_twinstar(struct astribank_device *astribank)
+{
+ struct usb_device_descriptor *dev_desc;
+ uint16_t product_series;
+
+ assert(astribank != NULL);
+ dev_desc = &astribank->dev->descriptor;
+ product_series = dev_desc->idProduct;
+ product_series &= 0xFFF0;
+ if(product_series == 0x1160) /* New boards */
+ return 1;
+ return 0;
+}
+
diff --git a/xpp/astribank_usb.h b/xpp/astribank_usb.h
new file mode 100644
index 0000000..6f9a429
--- /dev/null
+++ b/xpp/astribank_usb.h
@@ -0,0 +1,102 @@
+#ifndef ASTRIBANK_USB_H
+#define ASTRIBANK_USB_H
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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 <stdio.h>
+#include <usb.h>
+#include "mpp.h"
+
+/*
+ * Astribank handling
+ */
+
+#define PACKET_SIZE 512
+
+/* USB Endpoints */
+#define MP_EP_OUT 0x04 /* Managment processor */
+#define MP_EP_IN 0x88 /* Managment processor */
+
+#define XPP_EP_OUT 0x02 /* XPP */
+#define XPP_EP_IN 0x86 /* XPP */
+
+/* USB firmware types */
+#define USB_11xx 0
+#define USB_FIRMWARE_II 1
+#define USB_PIC 2
+
+struct interface_type {
+ int type_code;
+ int num_interfaces;
+ int my_interface_num;
+ int num_endpoints;
+ int my_ep_out;
+ int my_ep_in;
+ char *name;
+ int endpoints[4]; /* for matching */
+};
+
+enum eeprom_burn_state {
+ BURN_STATE_NONE = 0,
+ BURN_STATE_STARTED = 1,
+ BURN_STATE_ENDED = 2,
+ BURN_STATE_FAILED = 3,
+};
+
+struct astribank_device {
+ struct usb_device *dev;
+ usb_dev_handle *handle;
+ int my_interface_num;
+ int my_ep_out;
+ int my_ep_in;
+ char iManufacturer[BUFSIZ];
+ char iProduct[BUFSIZ];
+ char iSerialNumber[BUFSIZ];
+ char iInterface[BUFSIZ];
+ int is_usb2;
+ enum eeprom_type eeprom_type;
+ enum eeprom_burn_state burn_state;
+ uint8_t status;
+ uint8_t mpp_proto_version;
+ struct eeprom_table *eeprom;
+ const struct interface_type *fwtype;
+ uint16_t tx_sequenceno;
+};
+
+/*
+ * Prototypes
+ */
+struct astribank_device *astribank_open(const char devpath[], int iface_num);
+void astribank_close(struct astribank_device *astribank, int disconnected);
+void show_astribank_info(const struct astribank_device *astribank);
+int send_usb(struct astribank_device *astribank, char *buf, int len, int timeout);
+int recv_usb(struct astribank_device *astribank, char *buf, size_t len, int timeout);
+int flush_read(struct astribank_device *astribank);
+int eeprom_fill(struct eeprom_table *eprm,
+ const char *vendor,
+ const char *product,
+ const char *release,
+ const char *label);
+int astribank_has_twinstar(struct astribank_device *astribank);
+int label_isvalid(const char *label);
+
+#endif /* ASTRIBANK_USB_H */
diff --git a/xpp/dahdi_drivers b/xpp/dahdi_drivers
index 857324e..863d7b1 100755
--- a/xpp/dahdi_drivers
+++ b/xpp/dahdi_drivers
@@ -5,8 +5,8 @@ BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/perl_modules"); }
use Dahdi::Hardware;
-my $hardware = Dahdi::Hardware->scan;
-print join("\n", $hardware->drivers),"\n";
+my @drivers = Dahdi::Hardware->drivers;
+print join("\n", @drivers),"\n";
__END__
=head1 NAME
diff --git a/xpp/dahdi_genconf b/xpp/dahdi_genconf
index 2988343..8167514 100755
--- a/xpp/dahdi_genconf
+++ b/xpp/dahdi_genconf
@@ -150,7 +150,7 @@ a comma separated list of options to the generator name. E.g:
dahdi_genconf system chandahdi=verbose unicall
-=head1 Global options:
+=head2 Global options:
=over 4
@@ -170,7 +170,7 @@ Currently, chandahdi is affected.
=back
-=head1 Implementation notes:
+=head2 Implementation notes:
=over 4
diff --git a/xpp/dahdi_hardware b/xpp/dahdi_hardware
index 68b2345..2528fb7 100755
--- a/xpp/dahdi_hardware
+++ b/xpp/dahdi_hardware
@@ -17,16 +17,16 @@ use Dahdi::Span;
use Dahdi::Xpp;
use Dahdi::Xpp::Xbus;
use Dahdi::Hardware;
+use Dahdi::Xpp::Mpp;
sub usage {
- die "Usage: $0 [-v][-x]\n";
+ die "Usage: $0 [-v][-x][-t]\n";
}
-our ($opt_v, $opt_x);
-getopts('vx') || usage;
+my %opts;
+getopts('vxt', \%opts) || usage;
@ARGV == 0 or usage;
-my $hardware = Dahdi::Hardware->scan;
my @spans = Dahdi::spans;
sub show_xbus($) {
@@ -59,37 +59,69 @@ sub show_disconnected(%) {
my %seen = @_;
my $notified_lost = 0;
- foreach my $xbus (Dahdi::Xpp::xbuses('SORT_CONNECTOR')) {
+ foreach my $xbus (Dahdi::Xpp::xbuses) {
if(!$seen{$xbus->name}) {
print "----------- XPP Spans with disconnected hardware -----------\n"
unless $notified_lost++;
printf($format, $xbus->name, '', '', '', "NO HARDWARE");
- show_xbus($xbus) if $opt_v;
+ show_xbus($xbus) if $opts{'v'};
}
}
}
-foreach my $dev ($hardware->device_list) {
+# FIXME: For verbose display we also need to see the XPP devices.
+# If no spans are registered, this won't happen. A brute-force
+# methood for making it happe:
+Dahdi::Xpp::xbuses if ($opts{'v'});
+
+my @devices = Dahdi::Hardware->device_list;
+Dahdi::Xpp::Mpp->set_astribank_tool('./astribank_tool');
+foreach my $dev (@devices) {
my $driver = $dev->driver || "";
my $xbus;
my $loaded;
+ my $tws_port;
+ my $tws_power;
+ my $tws_watchdog;
+ my $mppinfo;
if($dev->is_astribank) {
$xbus = $dev->xbus;
+ if($opts{'v'} || $opts{'t'}) {
+ Dahdi::Xpp::Mpp->mpp_addinfo($dev);
+ $mppinfo = $dev->mppinfo;
+ if(defined $mppinfo) {
+ $tws_port = $mppinfo->{TWINSTAR_PORT};
+ $tws_power = $mppinfo->{TWINSTAR_POWER};
+ $tws_watchdog = $mppinfo->{TWINSTAR_WATCHDOG};
+ }
+ }
}
$loaded = $dev->loaded;
warn "driver should be '$driver' but is actually '$loaded'\n"
if defined($loaded) && $driver ne $loaded;
$driver = "$driver" . (($loaded) ? "+" : "-");
+ if(defined $tws_power && defined $tws_watchdog) {
+ my $tws_active = $tws_watchdog && $tws_power->[0] && $tws_power->[1];
+ $driver .= "[T]" if $tws_active;
+ }
my $description = $dev->description || "";
printf $format, $dev->hardware_name, $driver, $dev->vendor, $dev->product, $description;
+ if($opts{'v'} && defined $mppinfo) {
+ printf " MPP: TWINSTAR_PORT=$tws_port\n" if defined $tws_port;
+ printf " MPP: TWINSTAR_WATCHDOG=$tws_watchdog\n";
+ for(my $i = 0; $i < 2; $i++) {
+ printf " MPP: TWINSTAR_POWER[%d]=%d\n",
+ $i, $tws_power->[$i] if defined $tws_power;
+ }
+ }
if(!defined $xbus || !$xbus) {
next;
}
$seen{$xbus->name} = 1;
- show_xbus($xbus) if $opt_v;
+ show_xbus($xbus) if $opts{'v'};
}
-show_disconnected(%seen) if $opt_x;
+show_disconnected(%seen) if $opts{'x'};
__END__
diff --git a/xpp/dahdi_registration b/xpp/dahdi_registration
index 826721d..87b89f1 100755
--- a/xpp/dahdi_registration
+++ b/xpp/dahdi_registration
@@ -18,21 +18,24 @@ use Dahdi::Xpp::Xbus;
use Getopt::Std;
sub usage {
- die "Usage: $0 [on|off|1|0]\n";
+ die "Usage: $0 [-s sort_order] [on|off|1|0]\n";
}
my %opts;
-getopts('s:', \%opts);
-
-my $sort_order = $opts{s} || $ENV{XBUS_SORT} || 'SORT_CONNECTOR';
-my $sorter = Dahdi::Xpp::sorters($sort_order);
-
-if(!defined $sorter) {
- my @sorter_names = Dahdi::Xpp::sorters;
- print STDERR "Unknown sort order $sort_order. Select from:\n\t";
- print STDERR join("\n\t", @sorter_names);
- print STDERR "\n";
- exit 1;
+getopts('s:', \%opts) || usage;
+
+my $sorter;
+my $sort_order = $opts{'s'};
+if(defined $sort_order) {
+ my $sorter = Dahdi::Xpp::sorters($sort_order);
+
+ if(!defined $sorter) {
+ my @sorter_names = Dahdi::Xpp::sorters;
+ print STDERR "Unknown sort order $sort_order. Select from:\n\t";
+ print STDERR join("\n\t", @sorter_names);
+ print STDERR "\n";
+ exit 1;
+ }
}
@ARGV == 0 or @ARGV == 1 or usage;
@@ -58,7 +61,8 @@ sub myprintf {
my @spans = Dahdi::spans;
foreach my $xbus (Dahdi::Xpp::xbuses($sorter)) {
- myprintf "%-10s\t%s\t%s\n", $xbus->name, $xbus->label, $xbus->connector;
+ 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()) {
my $prev = $xpd->dahdi_registration($on);
@@ -76,7 +80,7 @@ foreach my $xbus (Dahdi::Xpp::xbuses($sorter)) {
myprintf "%3s ==> %3s\n", state2str($prev), state2str($on);
}
}
-myprintf "# Sorted: $sort_order\n";
+myprintf "# Sorted: $sort_order\n" if defined $sort_order;
__END__
@@ -118,7 +122,7 @@ The sort order to use.
If the option is not used, the sort order is taken from the environment
variable XBUS_SORT and failing that: the hard-coded default of
-SORT_CONNECTOR.
+SORT_XPPORDER.
The available sorting orders are documented in Dahdi::Xpp manual.
diff --git a/xpp/debug.c b/xpp/debug.c
new file mode 100644
index 0000000..f188a2d
--- /dev/null
+++ b/xpp/debug.c
@@ -0,0 +1,53 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include "debug.h"
+
+int verbose = LOG_DEBUG;
+int debug_mask = 0;
+
+void log_function(int level, int mask, const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ if(verbose >= level) {
+ if(level < LOG_DEBUG || (mask & debug_mask))
+ vfprintf(stderr, msg, ap);
+ }
+ va_end(ap);
+}
+
+void dump_packet(int loglevel, const char *msg, const char *buf, int len)
+{
+ int i;
+
+ log_function(loglevel, ~0, "%-15s:", msg);
+ for(i = 0; i < len; i++)
+ log_function(loglevel, ~0, " %02X", (uint8_t)buf[i]);
+ log_function(loglevel, ~0, "\n");
+}
+
diff --git a/xpp/debug.h b/xpp/debug.h
new file mode 100644
index 0000000..185848a
--- /dev/null
+++ b/xpp/debug.h
@@ -0,0 +1,46 @@
+#ifndef DEBUG_H
+#define DEBUG_H
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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 <syslog.h>
+
+/*
+ * Each module should define a unique DBG_MASK
+ */
+
+extern int verbose;
+extern int debug_mask;
+
+/*
+ * Logging
+ */
+void log_function(int level, int mask, const char *msg, ...) __attribute__(( format(printf, 3, 4) ));
+
+#define ERR(fmt, arg...) log_function(LOG_ERR, 0, "%s:%d: ERROR(%s): " fmt, __FILE__, __LINE__, __FUNCTION__, ## arg)
+#define INFO(fmt, arg...) log_function(LOG_INFO, 0, "INFO: " fmt, ## arg)
+#define DBG(fmt, arg...) log_function(LOG_DEBUG, DBG_MASK, \
+ "%s:%d: DBG(%s): " fmt, __FILE__, __LINE__, __FUNCTION__, ## arg)
+
+void dump_packet(int loglevel, const char *msg, const char *buf, int len);
+
+#endif /* DEBUG_H */
diff --git a/xpp/hexfile.h b/xpp/hexfile.h
index f8bf6a9..27c71e7 100644
--- a/xpp/hexfile.h
+++ b/xpp/hexfile.h
@@ -1,6 +1,6 @@
/*
* Written by Oron Peled <oron@actcom.co.il>
- * Copyright (C) 2006, Xorcom
+ * Copyright (C) 2006, 2007, 2008, Xorcom
*
* All rights reserved.
*
@@ -24,49 +24,12 @@
#define PARSE_HEXFILE_H
#include <stdarg.h>
-
-/*
- * Some portability workarounds
- */
-#ifdef _WINDOWS
-
-#include <windows.h> /* for UCHAR USHORT */
-typedef UCHAR uint8_t;
-typedef USHORT uint16_t;
-#define PACKED
-#define sscanf sscanf_s
-#define ZERO_SIZE 1
-
-/* From /usr/include/syslog.h */
-#define LOG_EMERG 0 /* system is unusable */
-#define LOG_ALERT 1 /* action must be taken immediately */
-#define LOG_CRIT 2 /* critical conditions */
-#define LOG_ERR 3 /* error conditions */
-#define LOG_WARNING 4 /* warning conditions */
-#define LOG_NOTICE 5 /* normal but significant condition */
-#define LOG_INFO 6 /* informational */
-#define LOG_DEBUG 7 /* debug-level messages */
-
-#ifdef __cplusplus
-# define __BEGIN_DECLS extern "C" {
-# define __END_DECLS }
-#else
-# define __BEGIN_DECLS
-# define __END_DECLS
-#endif
-
-#elif __GNUC__
-
+#include <stdio.h>
#include <stdint.h>
+#include <sys/param.h>
#include <syslog.h>
#define PACKED __attribute__((packed))
-#define ZERO_SIZE 1
-
-#else
-
-#error "Cannot compile on this platform"
-
-#endif
+#define ZERO_SIZE 0
/* Record types in hexfile */
enum {
@@ -101,6 +64,7 @@ struct hexdata {
unsigned int maxlines;
unsigned int last_line;
int got_eof;
+ char fname[PATH_MAX];
char version_info[BUFSIZ];
struct hexline *lines[ZERO_SIZE];
};
diff --git a/xpp/lsdahdi b/xpp/lsdahdi
index 2b68e2c..1298c3e 100755
--- a/xpp/lsdahdi
+++ b/xpp/lsdahdi
@@ -17,7 +17,7 @@ use Dahdi::Xpp;
use Dahdi::Xpp::Xbus;
use Dahdi::Xpp::Xpd;
-my @xbuses = Dahdi::Xpp::xbuses("SORT_CONNECTOR");
+my @xbuses = Dahdi::Xpp::xbuses;
my @xpds = map { $_->xpds } @xbuses;
foreach my $span (Dahdi::spans()) {
diff --git a/xpp/mpp.h b/xpp/mpp.h
new file mode 100644
index 0000000..b71eb30
--- /dev/null
+++ b/xpp/mpp.h
@@ -0,0 +1,324 @@
+#ifndef MPP_H
+#define MPP_H
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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.
+ *
+ */
+
+/*
+ * MPP - Managment Processor Protocol definitions
+ */
+
+#ifdef __GNUC__
+#define PACKED __attribute__((packed))
+#else
+#error "We do not know how your compiler packs structures"
+#endif
+
+#define MK_PROTO_VERSION(major, minor) (((major) << 4) | (0x0F & (minor)))
+
+#define MPP_PROTOCOL_VERSION MK_PROTO_VERSION(1,3)
+
+/*
+ * The eeprom_table is common to all eeprom types.
+ */
+#define LABEL_SIZE 8
+struct eeprom_table {
+ uint8_t source; /* C0 - small eeprom, C2 - large eeprom */
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t release; /* BCD encoded release */
+ uint8_t config_byte; /* Must be 0 */
+ uint8_t label[LABEL_SIZE];
+} PACKED;
+
+struct capabilities {
+ uint8_t ports_fxs;
+ uint8_t ports_fxo;
+ uint8_t ports_bri;
+ uint8_t ports_pri;
+ uint8_t extra_features; /* BIT(0) - TwinStar */
+ uint8_t reserved[3];
+ uint32_t timestamp;
+} PACKED;
+
+#define CAP_EXTRA_TWINSTAR(c) ((c)->extra_features & 0x01)
+#define CAP_EXTRA_TWINSTAR_SET(c) do {(c)->extra_features |= 0x01;} while (0)
+#define CAP_EXTRA_TWINSTAR_CLR(c) do {(c)->extra_features &= ~0x01;} while (0)
+
+#define KEYSIZE 16
+
+struct capkey {
+ uint8_t k[KEYSIZE];
+} PACKED;
+
+struct extrainfo {
+ char text[24];
+} PACKED;
+
+enum mpp_command_ops {
+ /* MSB of op signifies a reply from device */
+ MPP_ACK = 0x80,
+
+ MPP_PROTO_QUERY = 0x01,
+ MPP_PROTO_REPLY = 0x81,
+
+ MPP_RENUM = 0x0B, /* Trigger USB renumeration */
+
+ MPP_EEPROM_SET = 0x0D,
+
+ MPP_CAPS_GET = 0x0E,
+ MPP_CAPS_GET_REPLY = 0x8E,
+ MPP_CAPS_SET = 0x0F, /* Set AB capabilities */
+
+ MPP_DEV_SEND_START = 0x05,
+ MPP_DEV_SEND_SEG = 0x07,
+ MPP_DEV_SEND_END = 0x09,
+
+ MPP_STATUS_GET = 0x11, /* Get Astribank Status */
+ MPP_STATUS_GET_REPLY = 0x91,
+
+ MPP_EXTRAINFO_GET = 0x13, /* Get extra vendor information */
+ MPP_EXTRAINFO_GET_REPLY = 0x93,
+ MPP_EXTRAINFO_SET = 0x15, /* Set extra vendor information */
+
+ MPP_EEPROM_BLK_RD = 0x27,
+ MPP_EEPROM_BLK_RD_REPLY = 0xA7,
+
+ MPP_SER_SEND = 0x37,
+ MPP_SER_RECV = 0xB7,
+
+ MPP_RESET = 0x45, /* Reset both FPGA and USB firmwares */
+ MPP_HALF_RESET = 0x47, /* Reset only FPGA firmware */
+
+ /* Twinstar */
+ MPP_TWS_WD_MODE_SET = 0x31, /* Set watchdog off/on guard */
+ MPP_TWS_WD_MODE_GET = 0x32, /* Current watchdog mode */
+ MPP_TWS_WD_MODE_GET_REPLY = 0xB2, /* Current watchdog mode */
+ MPP_TWS_PORT_SET = 0x34, /* USB-[0/1] */
+ MPP_TWS_PORT_GET = 0x35, /* USB-[0/1] */
+ MPP_TWS_PORT_GET_REPLY = 0xB5, /* USB-[0/1] */
+ MPP_TWS_PWR_GET = 0x36, /* Power: bits -> USB ports */
+ MPP_TWS_PWR_GET_REPLY = 0xB6, /* Power: bits -> USB ports */
+};
+
+struct mpp_header {
+ uint16_t len;
+ uint16_t seq;
+ uint8_t op; /* MSB: 0 - to device, 1 - from device */
+} PACKED;
+
+enum mpp_ser_op {
+ SER_CARD_INFO_GET = 0x1,
+ SER_STAT_GET = 0x3,
+};
+
+/* Individual commands structure */
+
+#define CMD_DEF(name, ...) struct d_ ## name { __VA_ARGS__ } PACKED d_ ## name
+
+CMD_DEF(ACK,
+ uint8_t stat;
+ );
+
+CMD_DEF(PROTO_QUERY,
+ uint8_t proto_version;
+ uint8_t reserved;
+ );
+
+CMD_DEF(PROTO_REPLY,
+ uint8_t proto_version;
+ uint8_t reserved;
+ );
+
+CMD_DEF(STATUS_GET);
+
+CMD_DEF(STATUS_GET_REPLY,
+ uint8_t i2cs_data;
+
+#define STATUS_FPGA_LOADED(x) ((x) & 0x01)
+ uint8_t status; /* BIT(0) - FPGA is loaded */
+ );
+
+CMD_DEF(EEPROM_SET,
+ struct eeprom_table data;
+ );
+
+CMD_DEF(CAPS_GET);
+
+CMD_DEF(CAPS_GET_REPLY,
+ struct eeprom_table data;
+ struct capabilities capabilities;
+ struct capkey key;
+ );
+
+CMD_DEF(CAPS_SET,
+ struct eeprom_table data;
+ struct capabilities capabilities;
+ struct capkey key;
+ );
+
+CMD_DEF(EXTRAINFO_GET);
+
+CMD_DEF(EXTRAINFO_GET_REPLY,
+ struct extrainfo info;
+ );
+
+CMD_DEF(EXTRAINFO_SET,
+ struct extrainfo info;
+ );
+
+CMD_DEF(RENUM);
+
+CMD_DEF(EEPROM_BLK_RD,
+ uint16_t offset;
+ uint16_t len;
+ );
+
+CMD_DEF(EEPROM_BLK_RD_REPLY,
+ uint16_t offset;
+ uint8_t data[0];
+ );
+
+CMD_DEF(DEV_SEND_START,
+ uint8_t dest;
+ );
+
+CMD_DEF(DEV_SEND_END);
+
+CMD_DEF(DEV_SEND_SEG,
+ uint16_t offset;
+ uint8_t data[0];
+ );
+
+CMD_DEF(RESET);
+CMD_DEF(HALF_RESET);
+
+CMD_DEF(SER_SEND,
+ uint8_t data[0];
+ );
+
+CMD_DEF(SER_RECV,
+ uint8_t data[0];
+ );
+
+CMD_DEF(TWS_WD_MODE_SET,
+ uint8_t wd_active;
+ );
+
+CMD_DEF(TWS_WD_MODE_GET);
+CMD_DEF(TWS_WD_MODE_GET_REPLY,
+ uint8_t wd_active;
+ );
+
+CMD_DEF(TWS_PORT_SET,
+ uint8_t portnum;
+ );
+
+CMD_DEF(TWS_PORT_GET);
+CMD_DEF(TWS_PORT_GET_REPLY,
+ uint8_t portnum;
+ );
+
+CMD_DEF(TWS_PWR_GET);
+CMD_DEF(TWS_PWR_GET_REPLY,
+ uint8_t power;
+ );
+
+#undef CMD_DEF
+
+#define MEMBER(n) struct d_ ## n d_ ## n
+
+struct mpp_command {
+ struct mpp_header header;
+ union {
+ MEMBER(ACK);
+ MEMBER(PROTO_QUERY);
+ MEMBER(PROTO_REPLY);
+ MEMBER(STATUS_GET);
+ MEMBER(STATUS_GET_REPLY);
+ MEMBER(EEPROM_SET);
+ MEMBER(CAPS_GET);
+ MEMBER(CAPS_GET_REPLY);
+ MEMBER(CAPS_SET);
+ MEMBER(EXTRAINFO_GET);
+ MEMBER(EXTRAINFO_GET_REPLY);
+ MEMBER(EXTRAINFO_SET);
+ MEMBER(RENUM);
+ MEMBER(EEPROM_BLK_RD);
+ MEMBER(EEPROM_BLK_RD_REPLY);
+ MEMBER(DEV_SEND_START);
+ MEMBER(DEV_SEND_SEG);
+ MEMBER(DEV_SEND_END);
+ MEMBER(RESET);
+ MEMBER(HALF_RESET);
+ MEMBER(SER_SEND);
+ MEMBER(SER_RECV);
+ /* Twinstar */
+ MEMBER(TWS_WD_MODE_SET);
+ MEMBER(TWS_WD_MODE_GET);
+ MEMBER(TWS_WD_MODE_GET_REPLY);
+ MEMBER(TWS_PORT_SET);
+ MEMBER(TWS_PORT_GET);
+ MEMBER(TWS_PORT_GET_REPLY);
+ MEMBER(TWS_PWR_GET);
+ MEMBER(TWS_PWR_GET_REPLY);
+ uint8_t raw_data[0];
+ } PACKED alt;
+} PACKED;
+#undef MEMBER
+
+#define CMD_FIELD(cmd, name, field) ((cmd)->alt.d_ ## name.field)
+
+enum mpp_ack_stat {
+ STAT_OK = 0x00, /* acknowledges previous command */
+ STAT_FAIL = 0x01, /* Last command failed */
+ STAT_RESET_FAIL = 0x02, /* reset failed */
+ STAT_NODEST = 0x03, /* No destination is selected */
+ STAT_MISMATCH = 0x04, /* Data mismatch */
+ STAT_NOACCESS = 0x05, /* No access */
+ STAT_BAD_CMD = 0x06, /* Bad command */
+ STAT_TOO_SHORT = 0x07, /* Packet is too short */
+ STAT_ERROFFS = 0x08, /* Offset error */
+ STAT_NOCODE = 0x09, /* Source was not burned before */
+ STAT_NO_LEEPROM = 0x0A, /* Large EEPROM was not found */
+ STAT_NO_EEPROM = 0x0B, /* No EEPROM was found */
+ STAT_WRITE_FAIL = 0x0C, /* Writing to device failed */
+ STAT_FPGA_ERR = 0x0D, /* FPGA error */
+ STAT_KEY_ERR = 0x0E, /* Bad Capabilities Key */
+ STAT_NOCAPS_ERR = 0x0F, /* No matching capability */
+ STAT_NOPWR_ERR = 0x10, /* No power on USB connector */
+ STAT_CAPS_FPGA_ERR = 0x11, /* Setting of the capabilities while FPGA is loaded */
+};
+
+enum eeprom_type { /* EEPROM_QUERY: i2cs(ID1, ID0) */
+ EEPROM_TYPE_NONE = 0,
+ EEPROM_TYPE_SMALL = 1,
+ EEPROM_TYPE_LARGE = 2,
+ EEPROM_TYPE_UNUSED = 3,
+};
+
+enum dev_dest {
+ DEST_NONE = 0x00,
+ DEST_FPGA = 0x01,
+ DEST_EEPROM = 0x02,
+};
+
+#endif /* MPP_H */
diff --git a/xpp/mpp_funcs.c b/xpp/mpp_funcs.c
new file mode 100644
index 0000000..da4eb46
--- /dev/null
+++ b/xpp/mpp_funcs.c
@@ -0,0 +1,1021 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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 <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include "hexfile.h"
+#include "astribank_usb.h"
+#include "mpp_funcs.h"
+#include "debug.h"
+
+static const char rcsid[] = "$Id$";
+
+#define DBG_MASK 0x02
+
+const char *ack_status_msg(uint8_t status)
+{
+ const static char *msgs[] = {
+ [STAT_OK] = "Acknowledges previous command",
+ [STAT_FAIL] = "Last command failed",
+ [STAT_RESET_FAIL] = "Reset failed",
+ [STAT_NODEST] = "No destination is selected",
+ [STAT_MISMATCH] = "Data mismatch",
+ [STAT_NOACCESS] = "No access",
+ [STAT_BAD_CMD] = "Bad command",
+ [STAT_TOO_SHORT] = "Packet is too short",
+ [STAT_ERROFFS] = "Offset error",
+ [STAT_NOCODE] = "Source was not burned before",
+ [STAT_NO_LEEPROM] = "Large EEPROM was not found",
+ [STAT_NO_EEPROM] = "No EEPROM was found",
+ [STAT_WRITE_FAIL] = "Writing to device failed",
+ [STAT_FPGA_ERR] = "FPGA error",
+ [STAT_KEY_ERR] = "Bad Capabilities Key",
+ [STAT_NOCAPS_ERR] = "No matching capability",
+ [STAT_NOPWR_ERR] = "No power on USB connector",
+ [STAT_CAPS_FPGA_ERR] = "Setting of the capabilities while FPGA is loaded",
+ };
+ if(status > sizeof(msgs)/sizeof(msgs[0]))
+ return "ERROR CODE TOO LARGE";
+ if(!msgs[status])
+ return "MISSING ERROR CODE";
+ return msgs[status];
+}
+
+const char *eeprom_type2str(enum eeprom_type et)
+{
+ const static char *msgs[] = {
+ [EEPROM_TYPE_NONE] = "NONE",
+ [EEPROM_TYPE_SMALL] = "SMALL",
+ [EEPROM_TYPE_LARGE] = "LARGE",
+ [EEPROM_TYPE_UNUSED] = "UNUSED",
+ };
+ if(et > sizeof(msgs)/sizeof(msgs[0]))
+ return NULL;
+ return msgs[et];
+};
+
+struct command_desc {
+ uint8_t op;
+ const char *name;
+ uint16_t len;
+};
+
+#define CMD_RECV(o) [MPP_ ## o] { \
+ .op = MPP_ ## o, \
+ .name = #o, \
+ .len = sizeof(struct mpp_header) + sizeof(struct d_ ## o), \
+ }
+
+#define CMD_SEND(o) [MPP_ ## o] { \
+ .op = MPP_ ## o, \
+ .name = #o, \
+ .len = sizeof(struct mpp_header) + sizeof(struct d_ ## o), \
+ }
+
+static const struct command_desc command_table[] = {
+ CMD_RECV(ACK),
+ CMD_SEND(PROTO_QUERY),
+ CMD_SEND(STATUS_GET),
+ CMD_SEND(STATUS_GET_REPLY),
+ CMD_SEND(EEPROM_SET),
+ CMD_SEND(CAPS_GET),
+ CMD_RECV(CAPS_GET_REPLY),
+ CMD_SEND(CAPS_SET),
+ CMD_SEND(EXTRAINFO_GET),
+ CMD_SEND(EXTRAINFO_GET_REPLY),
+ CMD_SEND(EXTRAINFO_SET),
+ CMD_RECV(PROTO_REPLY),
+ CMD_SEND(RENUM),
+ CMD_SEND(EEPROM_BLK_RD),
+ CMD_RECV(EEPROM_BLK_RD_REPLY),
+ CMD_SEND(DEV_SEND_SEG),
+ CMD_SEND(DEV_SEND_START),
+ CMD_SEND(DEV_SEND_END),
+ CMD_SEND(RESET),
+ CMD_SEND(HALF_RESET),
+ CMD_SEND(SER_SEND),
+ CMD_SEND(SER_RECV),
+ /* Twinstar */
+ CMD_SEND(TWS_WD_MODE_SET),
+ CMD_SEND(TWS_WD_MODE_GET),
+ CMD_RECV(TWS_WD_MODE_GET_REPLY),
+ CMD_SEND(TWS_PORT_SET),
+ CMD_SEND(TWS_PORT_GET),
+ CMD_RECV(TWS_PORT_GET_REPLY),
+ CMD_SEND(TWS_PWR_GET),
+ CMD_RECV(TWS_PWR_GET_REPLY),
+};
+#undef CMD_SEND
+#undef CMD_RECV
+
+struct cmd_queue {
+ struct cmd_queue *next;
+ struct cmd_queue *prev;
+ struct mpp_command *cmd;
+};
+
+static struct cmd_queue output_queue = {
+ .next = &output_queue,
+ .prev = &output_queue,
+ .cmd = NULL
+ };
+
+void free_command(struct mpp_command *cmd)
+{
+ memset(cmd, 0, cmd->header.len);
+ free(cmd);
+}
+
+struct mpp_command *new_command(uint8_t op, uint16_t extra_data)
+{
+ struct mpp_command *cmd;
+ const struct command_desc *desc;
+ uint16_t len;
+
+ DBG("OP=0x%X (extra_data %d)\n", op, extra_data);
+ if(op > sizeof(command_table)/sizeof(command_table[0])) {
+ ERR("Invalid op=0x%X. Bigger than max valid op\n", op);
+ return NULL;
+ }
+ desc = &command_table[op];
+ if(!desc->name) {
+ ERR("Unknown op=0x%X.\n", op);
+ return NULL;
+ }
+ len = desc->len + extra_data;
+ if((cmd = malloc(len)) == NULL) {
+ ERR("Out of memory\n");
+ return NULL;
+ }
+ cmd->header.op = op;
+ cmd->header.len = len;
+ cmd->header.seq = 0; /* Overwritten in send_usb() */
+ return cmd;
+}
+
+const struct command_desc *get_command_desc(uint8_t op)
+{
+ const struct command_desc *desc;
+
+ if(op > sizeof(command_table)/sizeof(command_table[0])) {
+ //ERR("Invalid op=0x%X. Bigger than max valid op\n", op);
+ return NULL;
+ }
+ desc = &command_table[op];
+ if(!desc->name)
+ return NULL;
+ return desc;
+}
+
+const char *get_command_name(uint8_t op)
+{
+ const struct command_desc *desc;
+
+ if((desc = get_command_desc(op)) == NULL)
+ return NULL;
+ return desc->name;
+}
+
+void dump_command(struct mpp_command *cmd)
+{
+ uint16_t len;
+ int i;
+
+ len = cmd->header.len;
+ if(len < sizeof(struct mpp_header)) {
+ ERR("Command too short (%d)\n", len);
+ return;
+ }
+ INFO("DUMP: OP=0x%X len=%d seq=%d\n",
+ cmd->header.op, cmd->header.len, cmd->header.seq);
+ for(i = 0; i < len - sizeof(struct mpp_header); i++) {
+ INFO(" %2d. 0x%X\n", i, cmd->alt.raw_data[i]);
+ }
+}
+
+int send_command(struct astribank_device *astribank, struct mpp_command *cmd, int timeout)
+{
+ int ret;
+ int len;
+ char *buf;
+
+ len = cmd->header.len;
+ cmd->header.seq = astribank->tx_sequenceno;
+
+ buf = (char *)cmd;
+ //printf("%s: len=%d\n", __FUNCTION__, len);
+#if 0
+ extern FILE *fp;
+ if(fp) {
+ int i;
+
+ fprintf(fp, "%05d:", cmd->header.seq);
+ for(i = 0; i < len; i++)
+ fprintf(fp, " %02X", (uint8_t)buf[i]);
+ fprintf(fp, "\n");
+ }
+#endif
+ ret = send_usb(astribank, (char *)cmd, len, timeout);
+ if(ret < 0) {
+ ERR("send_usb failed ret=%d\n", ret);
+ }
+ astribank->tx_sequenceno++;
+ return ret;
+}
+
+struct mpp_command *recv_command(struct astribank_device *astribank, int timeout)
+{
+ struct mpp_command *reply;
+ int ret;
+
+ if((reply = malloc(PACKET_SIZE)) == NULL) {
+ ERR("Out of memory\n");
+ goto err;
+ }
+ ret = recv_usb(astribank, (char *)reply, PACKET_SIZE, timeout);
+ if(ret < 0) {
+ ERR("Receive from usb failed.\n");
+ goto err;
+ }
+ if(ret != reply->header.len) {
+ ERR("Wrong length received: got %d bytes, but length field says %d bytes\n",
+ ret, reply->header.len);
+ goto err;
+ }
+ //dump_packet(LOG_DEBUG, __FUNCTION__, (char *)reply, ret);
+ return reply;
+err:
+ if(reply) {
+ memset(reply, 0, PACKET_SIZE);
+ free_command(reply);
+ }
+ return NULL;
+}
+
+
+int process_command(struct astribank_device *astribank, struct mpp_command *cmd, struct mpp_command **reply_ref)
+{
+ struct mpp_command *reply = NULL;
+ const struct command_desc *reply_desc;
+ const struct command_desc *expected;
+ uint8_t reply_op;
+ int ret;
+
+ if(reply_ref)
+ *reply_ref = NULL; /* So the caller knows if a reply was received */
+ reply_op = cmd->header.op | 0x80;
+ expected = get_command_desc(reply_op);
+ //printf("%s: len=%d\n", __FUNCTION__, cmd->header.len);
+ ret = send_command(astribank, cmd, TIMEOUT);
+ if(ret < 0) {
+ ERR("send_command failed: %d\n", ret);
+ goto out;
+ }
+ if(!reply_ref) {
+ DBG("No reply requested\n");
+ goto out;
+ }
+ reply = recv_command(astribank, TIMEOUT);
+ if(!reply) {
+ ERR("recv_command failed\n");
+ ret = -EPROTO;
+ goto out;
+ }
+ *reply_ref = reply;
+ if((reply->header.op & 0x80) != 0x80) {
+ ERR("Unexpected reply op=0x%02X, should have MSB set.\n", reply->header.op);
+ ret = -EPROTO;
+ goto out;
+ }
+ DBG("REPLY OP: 0x%X\n", reply->header.op);
+ reply_desc = get_command_desc(reply->header.op);
+ if(!reply_desc) {
+ ERR("Unknown reply op=0x%02X\n", reply->header.op);
+ ret = -EPROTO;
+ goto out;
+ }
+ DBG("REPLY NAME: %s\n", reply_desc->name);
+ if(reply->header.op == MPP_ACK) {
+ int status = CMD_FIELD(reply, ACK, stat);
+
+ if(expected) {
+ ERR("Expected OP=0x%02X: Got ACK(%d): %s\n",
+ reply_op, status, ack_status_msg(status));
+ ret = -EPROTO;
+ goto out;
+ } else if(status != STAT_OK) {
+
+ ERR("Got ACK (for OP=0x%X): %d - %s\n",
+ cmd->header.op,
+ status,
+ ack_status_msg(status));
+#if 0
+ extern FILE *fp;
+ if(fp) {
+ fprintf(fp, "Got ACK(%d)\n", status);
+ }
+#endif
+ ret = -EPROTO;
+ goto out;
+ }
+ /* Good expected ACK ... */
+ } else if(reply->header.op != reply_op) {
+ ERR("Expected OP=0x%02X: Got OP=0x%02X\n",
+ reply_op, reply->header.op);
+ ret = -EPROTO;
+ goto out;
+ }
+ if(expected && expected->op != MPP_SER_RECV && expected->len != reply->header.len) {
+ ERR("Expected len=%d: Got len=%d\n",
+ expected->len, reply->header.len);
+ ret = -EPROTO;
+ goto out;
+ }
+ if(cmd->header.seq != reply->header.seq) {
+ ERR("Expected seq=%d: Got seq=%d\n",
+ cmd->header.seq, reply->header.seq);
+ ret = -EPROTO;
+ goto out;
+ }
+ ret = reply->header.len; /* All good, return the length */
+ DBG("returning reply op 0x%X (%d bytes)\n", reply->header.op, ret);
+out:
+ free_command(cmd);
+ if(!reply_ref && reply)
+ free_command(reply);
+ return ret;
+}
+
+/*
+ * Protocol Commands
+ */
+
+int mpp_proto_query(struct astribank_device *astribank)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_PROTO_QUERY, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ CMD_FIELD(cmd, PROTO_QUERY, proto_version) = MPP_PROTOCOL_VERSION; /* Protocol Version */
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ astribank->mpp_proto_version = CMD_FIELD(reply, PROTO_REPLY, proto_version);
+ if(astribank->mpp_proto_version != MPP_PROTOCOL_VERSION) {
+ ERR("Got mpp protocol version: %02x (expected %02x)\n",
+ astribank->mpp_proto_version,
+ MPP_PROTOCOL_VERSION);
+ ret = -EPROTO;
+ goto out;
+ }
+ INFO("Protocol version: %02x\n", astribank->mpp_proto_version);
+ free_command(reply);
+out:
+ return ret;
+}
+
+int mpp_status_query(struct astribank_device *astribank)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_STATUS_GET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ astribank->eeprom_type = 0x3 & (CMD_FIELD(reply, STATUS_GET_REPLY, i2cs_data) >> 3);
+ astribank->status = CMD_FIELD(reply, STATUS_GET_REPLY, status);
+ DBG("EEPROM TYPE: %02x\n", astribank->eeprom_type);
+ DBG("FPGA Firmware: %s\n", (astribank->status & 0x1) ? "Loaded" : "Empty");
+ free_command(reply);
+ return ret;
+}
+
+int mpp_eeprom_set(struct astribank_device *astribank, const struct eeprom_table *et)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_EEPROM_SET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ memcpy(&CMD_FIELD(cmd, EEPROM_SET, data), et, sizeof(*et));
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ free_command(reply);
+ return 0;
+}
+
+int mpp_renumerate(struct astribank_device *astribank)
+{
+ struct mpp_command *cmd;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_RENUM, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ ret = process_command(astribank, cmd, NULL);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+int mpp_caps_get(struct astribank_device *astribank,
+ struct eeprom_table *eeprom_table,
+ struct capabilities *capabilities,
+ struct capkey *key)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_CAPS_GET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ assert(reply->header.op == MPP_CAPS_GET_REPLY);
+ if(eeprom_table) {
+ memcpy(eeprom_table, (void *)&CMD_FIELD(reply, CAPS_GET_REPLY, data), sizeof(*eeprom_table));
+ }
+ if(capabilities) {
+ const struct capabilities *cap = &CMD_FIELD(reply, CAPS_GET_REPLY, capabilities);
+
+ memcpy(capabilities, cap, sizeof(*capabilities));
+ }
+ if(key) {
+ const struct capkey *k = &CMD_FIELD(reply, CAPS_GET_REPLY, key);
+
+ memcpy(key, k, sizeof(*key));
+ }
+ free_command(reply);
+ return 0;
+}
+
+int mpp_caps_set(struct astribank_device *astribank,
+ const struct eeprom_table *eeprom_table,
+ const struct capabilities *capabilities,
+ const struct capkey *key)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_CAPS_SET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ memcpy(&CMD_FIELD(cmd, CAPS_SET, data), eeprom_table, sizeof(*eeprom_table));
+ memcpy(&CMD_FIELD(cmd, CAPS_SET, capabilities), capabilities, sizeof(*capabilities));
+ memcpy(&CMD_FIELD(cmd, CAPS_SET, key), key, sizeof(*key));
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ free_command(reply);
+ return 0;
+}
+
+int mpp_extrainfo_get(struct astribank_device *astribank, struct extrainfo *info)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_EXTRAINFO_GET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ assert(reply->header.op == MPP_EXTRAINFO_GET_REPLY);
+ if(info) {
+ memcpy(info, (void *)&CMD_FIELD(reply, EXTRAINFO_GET_REPLY, info), sizeof(*info));
+ }
+ free_command(reply);
+ return 0;
+}
+
+int mpp_extrainfo_set(struct astribank_device *astribank, const struct extrainfo *info)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_EXTRAINFO_SET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ memcpy(&CMD_FIELD(cmd, EXTRAINFO_SET, info), info, sizeof(*info));
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ free_command(reply);
+ return 0;
+}
+
+int mpp_eeprom_blk_rd(struct astribank_device *astribank, uint8_t *buf, uint16_t offset, uint16_t len)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+ int size;
+
+ DBG("len = %d, offset = %d\n", len, offset);
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_EEPROM_BLK_RD, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ CMD_FIELD(cmd, EEPROM_BLK_RD, len) = len;
+ CMD_FIELD(cmd, EEPROM_BLK_RD, offset) = offset;
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ size = ret;
+ goto out;
+ }
+ INFO("size=%d offset=0x%X\n", size, CMD_FIELD(reply, EEPROM_BLK_RD_REPLY, offset));
+ dump_packet(LOG_DEBUG, "BLK_RD", (char *)reply, ret);
+ size = reply->header.len - sizeof(struct mpp_header) - sizeof(struct d_EEPROM_BLK_RD_REPLY);
+ if(size > len) {
+ ERR("Truncating reply (was %d, now %d)\n", size, len);
+ size = len;
+ }
+ memcpy(buf, CMD_FIELD(reply, EEPROM_BLK_RD_REPLY, data), size);
+out:
+ free_command(reply);
+ return size;
+}
+
+int mpp_send_start(struct astribank_device *astribank, enum dev_dest dest)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply = NULL;
+ int ret = 0;
+
+ DBG("dest = %d\n", dest);
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_DEV_SEND_START, 0)) == NULL) {
+ ERR("new_command failed\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ CMD_FIELD(cmd, DEV_SEND_START, dest) = dest;
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ goto out;
+ }
+out:
+ if(reply)
+ free_command(reply);
+ astribank->burn_state = (ret == 0)
+ ? BURN_STATE_STARTED
+ : BURN_STATE_FAILED;
+ return ret;
+}
+
+int mpp_send_end(struct astribank_device *astribank)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply = NULL;
+ int ret = 0;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_DEV_SEND_END, 0)) == NULL) {
+ ERR("new_command failed\n");
+ ret = -ENOMEM;
+ goto out;
+ }
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ goto out;
+ }
+out:
+ if(reply)
+ free_command(reply);
+ astribank->burn_state = (ret == 0)
+ ? BURN_STATE_ENDED
+ : BURN_STATE_FAILED;
+ return ret;
+}
+
+int mpp_send_seg(struct astribank_device *astribank, const uint8_t *data, uint16_t offset, uint16_t len)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ if(!astribank->burn_state == BURN_STATE_STARTED) {
+ ERR("Tried to send a segment while burn_state=%d\n",
+ astribank->burn_state);
+ return -EINVAL;
+ }
+ DBG("len = %d, offset = %d (0x%02X, 0x%02X)\n", len, offset, *data, *(data + 1));
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_DEV_SEND_SEG, len)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ CMD_FIELD(cmd, DEV_SEND_SEG, offset) = offset;
+ memcpy(CMD_FIELD(cmd, DEV_SEND_SEG, data), data, len);
+#if 0
+ {
+ FILE *fp;
+ if((fp = fopen("seg_data.bin", "a")) == NULL) {
+ perror("seg_data.bin");
+ exit(1);
+ }
+ if(fwrite(CMD_FIELD(cmd, DEV_SEND_SEG, data), len, 1, fp) != 1) {
+ perror("fwrite");
+ exit(1);
+ }
+ fclose(fp);
+ }
+#endif
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ free_command(reply);
+ return 0;
+}
+
+int mpp_reset(struct astribank_device *astribank, int full_reset)
+{
+ struct mpp_command *cmd;
+ int ret;
+ int op = (full_reset) ? MPP_RESET: MPP_HALF_RESET;
+
+ DBG("full = %s\n", (full_reset) ? "YES" : "NO");
+ assert(astribank != NULL);
+ if((cmd = new_command(op, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ ret = process_command(astribank, cmd, NULL);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ return 0;
+}
+
+int mpp_serial_cmd(struct astribank_device *astribank, const uint8_t *in, uint8_t *out, uint16_t len)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+ uint8_t *data;
+
+ DBG("len=%d\n", len);
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_SER_SEND, len)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ data = CMD_FIELD(cmd, SER_SEND, data);
+ memcpy(data, in, len);
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ assert(reply->header.op == MPP_SER_RECV);
+ data = CMD_FIELD(reply, SER_RECV, data);
+ memcpy(out, data, len);
+ free_command(reply);
+ return 0;
+}
+
+int mpps_card_info(struct astribank_device *astribank, int unit, uint8_t *card_type, uint8_t *card_status)
+{
+ struct card_info_send {
+ uint8_t ser_op;
+ uint8_t addr;
+ } *card_info_send;
+ struct card_info_recv {
+ uint8_t ser_op_undef; /* invalid data */
+ uint8_t addr;
+ uint8_t card_full_type; /* (type << 4 | subtype) */
+ uint8_t card_status; /* BIT(0) - PIC burned */
+ } *card_info_recv;
+ uint8_t in[sizeof(struct card_info_recv)];
+ uint8_t out[sizeof(struct card_info_recv)];
+ int len;
+ int ret;
+
+ len = sizeof(struct card_info_recv);
+ memset(in, 0, len);
+ memset(out, 0, len);
+ card_info_send = (struct card_info_send *)&in;
+ card_info_recv = (struct card_info_recv *)&out;
+ card_info_send->ser_op = SER_CARD_INFO_GET;
+ card_info_send->addr = (unit << 4); /* low nibble is subunit */
+ ret = mpp_serial_cmd(astribank, in, out, len);
+ if(ret < 0)
+ return ret;
+ *card_type = card_info_recv->card_full_type;
+ *card_status = card_info_recv->card_status;
+ return 0;
+}
+
+int mpp_tws_watchdog(struct astribank_device *astribank)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_TWS_WD_MODE_GET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ ret = CMD_FIELD(reply, TWS_WD_MODE_GET_REPLY, wd_active);
+ DBG("wd_active=0x%X\n", ret);
+ free_command(reply);
+ return ret == 1;
+}
+
+int mpp_tws_setwatchdog(struct astribank_device *astribank, int yes)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("%s\n", (yes) ? "YES" : "NO");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_TWS_WD_MODE_SET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ CMD_FIELD(cmd, TWS_WD_MODE_SET, wd_active) = (yes) ? 1 : 0;
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ free_command(reply);
+ return 0;
+}
+
+int mpp_tws_powerstate(struct astribank_device *astribank)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_TWS_PWR_GET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ ret = CMD_FIELD(reply, TWS_PWR_GET_REPLY, power);
+ DBG("power=0x%X\n", ret);
+ free_command(reply);
+ return ret;
+}
+
+int mpp_tws_portnum(struct astribank_device *astribank)
+{
+ struct mpp_command *cmd;
+ struct mpp_command *reply;
+ int ret;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if((cmd = new_command(MPP_TWS_PORT_GET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ ret = CMD_FIELD(reply, TWS_PORT_GET_REPLY, portnum);
+ DBG("portnum=0x%X\n", ret);
+ free_command(reply);
+ return ret;
+}
+
+int mpp_tws_setportnum(struct astribank_device *astribank, uint8_t portnum)
+{
+ struct mpp_command *cmd;
+ int ret;
+ struct mpp_command *reply;
+
+ DBG("\n");
+ assert(astribank != NULL);
+ if(portnum >= 2) {
+ ERR("Invalid portnum (%d)\n", portnum);
+ return -EINVAL;
+ }
+ if((cmd = new_command(MPP_TWS_PORT_SET, 0)) == NULL) {
+ ERR("new_command failed\n");
+ return -ENOMEM;
+ }
+ CMD_FIELD(cmd, TWS_PORT_SET, portnum) = portnum;
+ ret = process_command(astribank, cmd, &reply);
+ if(ret < 0) {
+ ERR("process_command failed: %d\n", ret);
+ return ret;
+ }
+ free_command(reply);
+ return 0;
+}
+
+/*
+ * Wrappers
+ */
+
+struct astribank_device *mpp_init(const char devpath[])
+{
+ struct astribank_device *astribank;
+ int ret;
+
+ DBG("devpath='%s'\n", devpath);
+ if((astribank = astribank_open(devpath, 1)) == NULL) {
+ ERR("Opening astribank failed\n");
+ return NULL;
+ }
+ ret = mpp_proto_query(astribank);
+ if(ret < 0) {
+ ERR("Protocol handshake failed: %d\n", ret);
+ return NULL;
+ }
+ ret = mpp_status_query(astribank);
+ if(ret < 0) {
+ ERR("Status query failed: %d\n", ret);
+ return NULL;
+ }
+ return astribank;
+}
+
+void mpp_exit(struct astribank_device *astribank)
+{
+ DBG("\n");
+ astribank_close(astribank, 0);
+}
+
+/*
+ * data structures
+ */
+
+void show_eeprom(const struct eeprom_table *eprm, FILE *fp)
+{
+ int rmajor = (eprm->release >> 8) & 0xFF;
+ int rminor = eprm->release & 0xFF;;
+ char buf[BUFSIZ];
+
+ memset(buf, 0, LABEL_SIZE + 1);
+ memcpy(buf, eprm->label, LABEL_SIZE);
+ fprintf(fp, "EEPROM: %-15s: 0x%02X\n", "Source", eprm->source);
+ fprintf(fp, "EEPROM: %-15s: 0x%04X\n", "Vendor", eprm->vendor);
+ fprintf(fp, "EEPROM: %-15s: 0x%04X\n", "Product", eprm->product);
+ fprintf(fp, "EEPROM: %-15s: %d.%d\n", "Release", rmajor, rminor);
+ fprintf(fp, "EEPROM: %-15s: 0x%02X\n", "Config", eprm->config_byte);
+ fprintf(fp, "EEPROM: %-15s: '%s'\n", "Label", buf);
+}
+
+void show_capabilities(const struct capabilities *capabilities, FILE *fp)
+{
+ fprintf(fp, "Capabilities: FXS ports: %2d\n", capabilities->ports_fxs);
+ fprintf(fp, "Capabilities: FXO ports: %2d\n", capabilities->ports_fxo);
+ fprintf(fp, "Capabilities: BRI ports: %2d\n", capabilities->ports_bri);
+ fprintf(fp, "Capabilities: PRI ports: %2d\n", capabilities->ports_pri);
+ fprintf(fp, "Capabilities: TwinStar : %s\n",
+ (CAP_EXTRA_TWINSTAR(capabilities)) ? "Yes" : "No");
+}
+
+void show_astribank_status(struct astribank_device *astribank, FILE *fp)
+{
+ fprintf(fp, "Astribank: EEPROM : %s\n", eeprom_type2str(astribank->eeprom_type));
+ fprintf(fp, "Astribank: FPGA status : %s\n",
+ STATUS_FPGA_LOADED(astribank->status) ? "Loaded" : "Empty");
+}
+
+void show_extrainfo(const struct extrainfo *extrainfo, FILE *fp)
+{
+ fprintf(fp, "Extrainfo: : %s\n", (const char *)(extrainfo->text));
+}
+
+int twinstar_show(struct astribank_device *astribank, FILE *fp)
+{
+ int watchdog;
+ int powerstate;
+ int portnum;
+ int i;
+
+ if(!astribank_has_twinstar(astribank)) {
+ fprintf(fp, "TwinStar: NO\n");
+ return 0;
+ }
+ if((watchdog = mpp_tws_watchdog(astribank)) < 0) {
+ ERR("Failed getting TwinStar information\n");
+ return watchdog;
+ }
+ if((powerstate = mpp_tws_powerstate(astribank)) < 0) {
+ ERR("Failed getting TwinStar powerstate\n");
+ return powerstate;
+ }
+ if((portnum = mpp_tws_portnum(astribank)) < 0) {
+ ERR("Failed getting TwinStar portnum\n");
+ return portnum;
+ }
+ fprintf(fp, "TwinStar: Connected to : USB-%1d\n", portnum);
+ fprintf(fp, "TwinStar: Watchdog : %s\n",
+ (watchdog) ? "on-guard" : "off-guard");
+ for(i = 0; i < 2; i++) {
+ int pw = (1 << i) & powerstate;
+
+ fprintf(fp, "TwinStar: USB-%1d POWER : %s\n",
+ i, (pw) ? "ON" : "OFF");
+ }
+ return 0;
+}
+
diff --git a/xpp/mpp_funcs.h b/xpp/mpp_funcs.h
new file mode 100644
index 0000000..8ab4b3a
--- /dev/null
+++ b/xpp/mpp_funcs.h
@@ -0,0 +1,78 @@
+#ifndef MPP_FUNCS_H
+#define MPP_FUNCS_H
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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 "mpp.h"
+#include "astribank_usb.h"
+
+#define TIMEOUT 2000
+
+/* high-level */
+struct astribank_device *mpp_init(const char devpath[]);
+void mpp_exit(struct astribank_device *astribank);
+int mpp_proto_query(struct astribank_device *astribank);
+int mpp_status_query(struct astribank_device *astribank);
+int mpp_eeprom_set(struct astribank_device *astribank, const struct eeprom_table *et);
+int mpp_renumerate(struct astribank_device *astribank);
+int mpp_caps_get(struct astribank_device *astribank,
+ struct eeprom_table *et,
+ struct capabilities *cap,
+ struct capkey *key);
+int mpp_caps_set(struct astribank_device *astribank,
+ const struct eeprom_table *eeprom_table,
+ const struct capabilities *capabilities,
+ const struct capkey *key);
+int mpp_extrainfo_get(struct astribank_device *astribank, struct extrainfo *info);
+int mpp_extrainfo_set(struct astribank_device *astribank, const struct extrainfo *info);
+int mpp_eeprom_blk_rd(struct astribank_device *astribank, uint8_t *buf, uint16_t offset, uint16_t len);
+int mpp_send_start(struct astribank_device *astribank, enum dev_dest dest);
+int mpp_send_end(struct astribank_device *astribank);
+int mpp_send_seg(struct astribank_device *astribank, const uint8_t *data, uint16_t offset, uint16_t len);
+int mpp_reset(struct astribank_device *astribank, int full_reset);
+int mpp_serial_cmd(struct astribank_device *astribank, const uint8_t *in, uint8_t *out, uint16_t len);
+void show_eeprom(const struct eeprom_table *eprm, FILE *fp);
+void show_capabilities(const struct capabilities *capabilities, FILE *fp);
+void show_astribank_status(struct astribank_device *astribank, FILE *fp);
+void show_extrainfo(const struct extrainfo *extrainfo, FILE *fp);
+int twinstar_show(struct astribank_device *astribank, FILE *fp);
+
+/*
+ * Serial commands to FPGA
+ */
+int mpps_card_info(struct astribank_device *astribank, int unit, uint8_t *card_type, uint8_t *card_status);
+
+/*
+ * Twinstar
+ */
+int mpp_tws_watchdog(struct astribank_device *astribank);
+int mpp_tws_setwatchdog(struct astribank_device *astribank, int yes);
+int mpp_tws_powerstate(struct astribank_device *astribank);
+int mpp_tws_portnum(struct astribank_device *astribank);
+int mpp_tws_setportnum(struct astribank_device *astribank, uint8_t portnum);
+
+/* low-level */
+int process_command(struct astribank_device *astribank, struct mpp_command *cmd, struct mpp_command **reply_ref);
+struct mpp_command *new_command(uint8_t op, uint16_t extra_data);
+void free_command(struct mpp_command *cmd);
+
+#endif /* MPP_FUNCS_H */
diff --git a/xpp/perl_modules/Dahdi/Hardware.pm b/xpp/perl_modules/Dahdi/Hardware.pm
index 6407760..1509615 100644
--- a/xpp/perl_modules/Dahdi/Hardware.pm
+++ b/xpp/perl_modules/Dahdi/Hardware.pm
@@ -8,8 +8,6 @@ package Dahdi::Hardware;
# $Id$
#
use strict;
-use Dahdi::Hardware::USB;
-use Dahdi::Hardware::PCI;
=head1 NAME
@@ -44,6 +42,7 @@ system. It identifies devices by (USB/PCI) bus IDs.
=head1 Device Attributes
+
As usual, object attributes can be used in either upp-case or
lower-case, or lower-case functions.
@@ -92,22 +91,27 @@ attributes).
Astribank.
=cut
+#
+# A global hardware handle
+#
-sub device_detected($$) {
- my $dev = shift || die;
- my $name = shift || die;
- die unless defined $dev->{'BUS_TYPE'};
+my %hardware_list = (
+ 'PCI' => [],
+ 'USB' => [],
+ );
+
+
+sub new($$) {
+ my $pack = shift || die "Wasn't called as a class method\n";
+ my $name = shift || die "$0: Missing device name";
+ my $type = shift || die "$0: Missing device type";
+ my $dev = {};
+ $dev->{'BUS_TYPE'} = $type;
$dev->{IS_ASTRIBANK} = 0 unless defined $dev->{'IS_ASTRIBANK'};
$dev->{'HARDWARE_NAME'} = $name;
+ return $dev;
}
-sub device_removed($) {
- my $dev = shift || die;
- my $name = $dev->hardware_name;
- die "Missing dahdi device hardware name" unless $name;
-}
-
-
=head1 device_list()
Returns a list of the hardware devices on the system.
@@ -117,17 +121,28 @@ You must run scan() first for this function to run meaningful output.
=cut
sub device_list($) {
- my $self = shift || die;
+ my $pack = shift || die;
my @types = @_;
my @list;
@types = qw(USB PCI) unless @types;
foreach my $t (@types) {
- @list = ( @list, @{$self->{$t}} );
+ my $lst = $hardware_list{$t};
+ @list = ( @list, @{$lst} );
}
return @list;
}
+sub device_by_hwname($$) {
+ my $pack = shift || die;
+ my $name = shift || die;
+ my @list = device_list('localcall');
+
+ my @good = grep { $_->hardware_name eq $name } @list;
+ return undef unless @good;
+ @good > 1 && die "$pack: Multiple matches for '$name': @good";
+ return $good[0];
+}
=head1 drivers()
@@ -139,7 +154,7 @@ loaded.
sub drivers($) {
my $self = shift || die;
- my @devs = $self->device_list;
+ my @devs = device_list('localcall');
my @drvs = map { $_->{DRIVER} } @devs;
# Make unique
my %drivers;
@@ -155,14 +170,52 @@ must be run to initialize the module.
=cut
+my $hardware_scanned;
+
sub scan($) {
my $pack = shift || die;
- my $self = {};
- bless $self, $pack;
- $self->{USB} = [ Dahdi::Hardware::USB->devices ];
- $self->{PCI} = [ Dahdi::Hardware::PCI->scan_devices ];
- return $self;
+ return if $hardware_scanned++;
+ foreach my $type (qw(PCI USB)) {
+ eval "use Dahdi::Hardware::$type";
+ die $@ if $@;
+ $hardware_list{$type} = [ "Dahdi::Hardware::$type"->scan_devices ];
+ }
+}
+
+sub import {
+ Dahdi::Hardware->scan unless grep(/\bnoscan\b/i, @_);
+}
+
+sub showall {
+ my $pack = shift || die;
+ my @devs;
+
+ my $printer = sub {
+ my $title = shift;
+ my @devs = @_;
+
+ return unless @devs;
+ printf "%s:\n", $title;
+ foreach my $dev (@devs) {
+ printf "\t%s\n", $dev->hardware_name;
+ foreach my $k (sort keys %{$dev}) {
+ my $v = $dev->{$k};
+ if($k eq 'MPPINFO') {
+ printf "\t\tMPPINFO:\n";
+ eval "use Dahdi::Xpp::Mpp";
+ die $@ if $@;
+ $v->showinfo("\t\t ");
+ } else {
+ printf "\t\t%-20s %s\n", $k, $v;
+ }
+ }
+ }
+ };
+ foreach my $type (qw(USB PCI)) {
+ my $lst = $hardware_list{$type};
+ &$printer("$type devices", @{$lst});
+ }
}
1;
diff --git a/xpp/perl_modules/Dahdi/Hardware/PCI.pm b/xpp/perl_modules/Dahdi/Hardware/PCI.pm
index 4811c54..02bce74 100644
--- a/xpp/perl_modules/Dahdi/Hardware/PCI.pm
+++ b/xpp/perl_modules/Dahdi/Hardware/PCI.pm
@@ -134,12 +134,13 @@ sub pci_sorter {
return $a->priv_device_name() cmp $b->priv_device_name();
}
-sub new($$) {
- my $pack = shift or die "Wasn't called as a class method\n";
- my $self = { @_ };
+sub new($@) {
+ my $pack = shift || die "Wasn't called as a class method\n";
+ my %attr = @_;
+ my $name = sprintf("pci:%s", $attr{PRIV_DEVICE_NAME});
+ my $self = Dahdi::Hardware->new($name, 'PCI');
+ %{$self} = (%{$self}, %attr);
bless $self, $pack;
- Dahdi::Hardware::device_detected($self,
- sprintf("pci:%s", $self->{PRIV_DEVICE_NAME}));
return $self;
}
@@ -197,7 +198,6 @@ sub scan_devices($) {
next unless defined $pci_ids{$key};
my $d = Dahdi::Hardware::PCI->new(
- BUS_TYPE => 'PCI',
PRIV_DEVICE_NAME => $dev->{PRIV_DEVICE_NAME},
VENDOR => $dev->{VENDOR},
PRODUCT => $dev->{PRODUCT},
diff --git a/xpp/perl_modules/Dahdi/Hardware/USB.pm b/xpp/perl_modules/Dahdi/Hardware/USB.pm
index 8c4220a..af0274f 100644
--- a/xpp/perl_modules/Dahdi/Hardware/USB.pm
+++ b/xpp/perl_modules/Dahdi/Hardware/USB.pm
@@ -10,8 +10,7 @@ package Dahdi::Hardware::USB;
use strict;
use Dahdi::Utils;
use Dahdi::Hardware;
-use Dahdi::Xpp;
-use Dahdi::Xpp::Xbus;
+use Dahdi::Xpp::Mpp;
our @ISA = qw(Dahdi::Hardware);
@@ -42,41 +41,75 @@ my %usb_ids = (
$ENV{PATH} .= ":/usr/sbin:/sbin:/usr/bin:/bin";
-my @xbuses = Dahdi::Xpp::xbuses('SORT_CONNECTOR');
-
sub usb_sorter() {
return $a->hardware_name cmp $b->hardware_name;
}
-sub xbus_of_usb($) {
- my $priv_device_name = shift;
- my $dev = shift;
+sub mpp_addinfo($) {
+ my $self = shift || die;
- my ($wanted) = grep {
- defined($_->usb_devname) &&
- $priv_device_name eq $_->usb_devname
- } @xbuses;
- return $wanted;
+ my $mppinfo = Dahdi::Xpp::Mpp->new($self);
+ $self->{MPPINFO} = $mppinfo if defined $mppinfo;
}
-sub new($$) {
+sub new($@) {
my $pack = shift or die "Wasn't called as a class method\n";
- my $self = { @_ };
+ my %attr = @_;
+ my $name = sprintf("usb:%s", $attr{PRIV_DEVICE_NAME});
+ my $self = Dahdi::Hardware->new($name, 'USB');
+ %{$self} = (%{$self}, %attr);
bless $self, $pack;
- my $xbus = xbus_of_usb($self->priv_device_name);
- if(defined $xbus) {
- $self->{XBUS} = $xbus;
- $self->{LOADED} = 'xpp_usb';
- } else {
- $self->{XBUS} = undef;
- $self->{LOADED} = undef;
- }
- Dahdi::Hardware::device_detected($self,
- sprintf("usb:%s", $self->{PRIV_DEVICE_NAME}));
return $self;
}
-sub devices($) {
+sub readval($) {
+ my $fname = shift || warn;
+ open(F, $fname) || warn "Failed opening '$fname': $!";
+ my $val = <F>;
+ close F;
+ chomp $val;
+ warn "$fname is empty" unless defined $val and $val;
+ return $val;
+}
+
+sub set_transport($$) {
+ my $pack = shift || die;
+ my $xbus = shift || die;
+ my $xbus_dir = shift;
+ my $transportdir = "$xbus_dir/transport";
+ my $hwdev;
+ if(! -e "$transportdir/ep_00") {
+ warn "A trasnport in '$transportdir' is not USB";
+ return undef;
+ }
+ my ($usbdev) = glob("$transportdir/usb_device:*");
+ my $busnum;
+ my $devnum;
+ # Different kernels...
+ if(defined $usbdev) { # It's USB
+ if($usbdev =~ /.*usb_device:usbdev(\d+)\.(\d+)/) {
+ $busnum = $1;
+ $devnum = $2;
+ } else {
+ warn "Bad USB transportdir='$transportdir' usbdev='$usbdev'\n";
+ }
+ } elsif(-d "$transportdir/usb_endpoint") {
+ $busnum = readval("$transportdir/busnum");
+ $devnum = readval("$transportdir/devnum");
+ }
+ my $usbname = sprintf("%03d/%03d", $busnum, $devnum);
+ #printf STDERR "DEBUG: %03d/%03d\n", $busnum, $devnum;
+ $xbus->{USB_DEVNAME} = $usbname;
+ $hwdev = Dahdi::Hardware->device_by_hwname("usb:$usbname");
+ #print "set_transport: ", $hwdev, "\n";
+ $xbus->{TRANSPORT} = $hwdev;
+ $hwdev->{XBUS} = $xbus;
+ $hwdev->{LOADED} = 'xpp_usb';
+ $xbus->{IS_TWINSTAR} = $hwdev->is_twinstar;
+ return $hwdev;
+}
+
+sub scan_devices($) {
my $pack = shift || die;
my $usb_device_list = "/proc/bus/usb/devices";
return unless (-r $usb_device_list);
@@ -102,7 +135,6 @@ sub devices($) {
next unless defined $model;
my $d = Dahdi::Hardware::USB->new(
IS_ASTRIBANK => ($model->{DRIVER} eq 'xpp_usb')?1:0,
- BUS_TYPE => 'USB',
PRIV_DEVICE_NAME => $devname,
VENDOR => $vendor,
PRODUCT => $product,
@@ -114,6 +146,7 @@ sub devices($) {
}
close F;
@devices = sort usb_sorter @devices;
+ return @devices;
}
1;
diff --git a/xpp/perl_modules/Dahdi/Xpp.pm b/xpp/perl_modules/Dahdi/Xpp.pm
index 847dedc..f9e62a5 100644
--- a/xpp/perl_modules/Dahdi/Xpp.pm
+++ b/xpp/perl_modules/Dahdi/Xpp.pm
@@ -8,6 +8,7 @@ package Dahdi::Xpp;
# $Id$
#
use strict;
+use Dahdi::Hardware;
use Dahdi::Xpp::Xbus;
=head1 NAME
@@ -28,12 +29,29 @@ Dahdi::Xpp - Perl interface to the Xorcom Astribank drivers.
}
=cut
+#
+# A global handle for all xbuses
+#
+my @xbuses;
my $proc_base = "/proc/xpp";
our $sysfs_astribanks = "/sys/bus/astribanks/devices";
our $sysfs_xpds = "/sys/bus/xpds/devices";
our $sysfs_ab_driver = "/sys/bus/astribanks/drivers/xppdrv";
+sub scan($) {
+ my $pack = shift || die;
+
+ opendir(D, $sysfs_astribanks) || return();
+ while(my $entry = readdir D) {
+ next unless $entry =~ /xbus-(\d+)/;
+ my $xbus = Dahdi::Xpp::Xbus->new($1);
+ push(@xbuses, $xbus);
+ }
+ closedir D;
+ return @xbuses;
+}
+
# Nominal sorters for xbuses
sub by_name {
return $a->name cmp $b->name;
@@ -72,6 +90,11 @@ sub by_type {
return $res;
}
+sub by_xpporder {
+ my $cmp = $a->xpporder cmp $b->xpporder;
+ return $cmp if $cmp != 0;
+ return $a->connector cmp $b->connector;
+}
=head1 xbuses([sort_order])
@@ -91,6 +114,19 @@ The built in sorters are:
=over
+=item SORT_XPPORDER
+
+Sort by ordering defined in F</etc/dahdi/xpp_order> file.
+Astribanks can be listed in this file by their label or by
+their connector string (prefixed with <@>).
+
+Astribanks not listed in the F<xpp_order> file are sorted
+via ordering number 999 -- So they come after the Astribanks
+that are listed.
+
+Astribanks with same ordering number (e.g: 999) are sorted
+by their connector string (to preserve legacy behaviour).
+
=item SORT_CONNECTOR
Sort by the connector string. For USB this defines the "path" to get to
@@ -129,38 +165,73 @@ sub sorters {
SORT_NAME => \&by_name,
SORT_LABEL => \&by_label,
SORT_TYPE => \&by_type,
+ SORT_XPPORDER => \&by_xpporder,
# Aliases
connector => \&by_connector,
name => \&by_name,
label => \&by_label,
type => \&by_type,
+ xpporder => \&by_xpporder,
);
my $which_sorter = shift || return sort keys %sorter_table;
return $which_sorter if ref($which_sorter) eq 'CODE';
return $sorter_table{$which_sorter};
}
+sub add_xpporder(@) {
+ my @xbuses = @_;
+ my $cfg = $ENV{XPPORDER_CONF} || '/etc/dahdi/xpp_order';
+ my %order;
+
+ # Set defaults
+ foreach my $xbus (@xbuses) {
+ $xbus->{XPPORDER} = 99;
+ }
+ # Read from optional config file
+ if(!open(F, $cfg)) {
+ warn "$0: Failed opening '$cfg': $!"
+ unless $! == 2; # ENOENT
+ return;
+ }
+ my $count = 1;
+ while(<F>) {
+ chomp;
+ s/#.*//;
+ s/^\s*//;
+ s/\s*$//;
+ next unless /\S/;
+ $order{$_} = $count++;
+ }
+ close F;
+ # Overrides from config file
+ foreach my $xbus (@xbuses) {
+ my $label = $xbus->label;
+ my $connector = '@' . $xbus->connector;
+ my $val;
+ $val = $order{$label};
+ $val = $order{$connector} unless defined $val;
+ $xbus->{XPPORDER} = $val if defined $val;
+ }
+}
+
sub xbuses {
- my $optsort = shift || 'SORT_CONNECTOR';
- my @xbuses;
+ my $optsort = shift || 'SORT_XPPORDER';
+ my @sorted_xbuses;
- opendir(D, $sysfs_astribanks) || return();
- while(my $entry = readdir D) {
- next unless $entry =~ /xbus-(\d+)/;
- my $xbus = Dahdi::Xpp::Xbus->new($1);
- push(@xbuses, $xbus);
+ if(! @xbuses) {
+ @xbuses = Dahdi::Xpp->scan();
}
- closedir D;
+ add_xpporder(@xbuses);
my $sorter = sorters($optsort);
die "Unknown optional sorter '$optsort'" unless defined $sorter;
- @xbuses = sort $sorter @xbuses;
- return @xbuses;
+ @sorted_xbuses = sort $sorter @xbuses;
+ return @sorted_xbuses;
}
sub xpd_of_span($) {
my $span = shift or die "Missing span parameter";
return undef unless defined $span;
- foreach my $xbus (Dahdi::Xpp::xbuses('SORT_CONNECTOR')) {
+ foreach my $xbus (Dahdi::Xpp::xbuses) {
foreach my $xpd ($xbus->xpds()) {
return $xpd if $xpd->fqn eq $span->name;
}
diff --git a/xpp/perl_modules/Dahdi/Xpp/Mpp.pm b/xpp/perl_modules/Dahdi/Xpp/Mpp.pm
new file mode 100644
index 0000000..6e23260
--- /dev/null
+++ b/xpp/perl_modules/Dahdi/Xpp/Mpp.pm
@@ -0,0 +1,184 @@
+package Dahdi::Xpp::Mpp;
+#
+# Written by Oron Peled <oron@actcom.co.il>
+# Copyright (C) 2009, Xorcom
+# This program is free software; you can redistribute and/or
+# modify it under the same terms as Perl itself.
+#
+# $Id$
+#
+use strict;
+use File::Basename;
+use Getopt::Std;
+BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/perl_modules"); }
+
+use Dahdi::Utils;
+
+=head1 NAME
+
+Dahdi::Xpp::Mpp - Perl interface to C<astribank_tool(8)>
+
+=head1 DESCRIPTION
+
+This package uses C<astribank_tool(8)> to collect information
+about Astribanks via MPP (Managment Processor Protocol).
+
+The binary default location is F</usr/sbin/astribank_tool>. It may be
+overridden via module parameter C<astribank_tool=> and the
+C<ASTRIBANK_TOOL> environment variable (higher priority).
+
+It may also be set/unset from code via the set_astribank_tool() method.
+
+=head1 METHODS
+
+=head2 mpp_addinfo()
+
+Called with a list of C<Dahdi::Hardware> objects and augment their
+data with C<Dahdi::Xpp::Mpp> objects.
+
+This method is the normal external interface of this class.
+
+=head2 new()
+
+Constructor. Receive as parameter an instance of C<Dahdi::Hardware> class
+and return a C<Dahdi::Xpp:Mpp> object.
+
+Normally, used indirectly via the mpp_addinfo() method.
+
+=head2 set_astribank_tool()
+
+Override default location of astribank_tool(8). It is legal
+to set it to C<undef>.
+
+=head2 showinfo()
+
+Dump an C<Dahdi::Xpp::Mpp> object for debugging.
+
+=cut
+
+my $astribank_tool = '/usr/sbin/astribank_tool';
+
+sub set_astribank_tool($$) {
+ my $pack = shift || die;
+ $pack eq 'Dahdi::Xpp::Mpp' or die "$0: Called from wrong package? ($pack)";
+ my $arg = shift;
+ $astribank_tool = $arg;
+ #print STDERR "Setting astribank_tool='$astribank_tool'\n";
+}
+
+sub import {
+ my ($param) = grep(/^astribank_tool=/, @_);
+ if(defined $param) {
+ $param =~ s/^astribank_tool=//;
+ $astribank_tool = $param;
+ }
+ if(defined $ENV{ASTRIBANK_TOOL}) {
+ $astribank_tool = $ENV{ASTRIBANK_TOOL};
+ }
+}
+
+sub showinfo($$) {
+ my $self = shift || die;
+ my $prefix = shift || die;
+
+ return unless defined $self;
+ foreach my $k (sort keys %{$self}) {
+ my $v = $self->{$k};
+ if(ref($v) eq 'ARRAY') {
+ my @a = @{$v};
+ my $i;
+ my $ki;
+ for($i = 0; $i < @a; $i++) {
+ $ki = sprintf "%s[%d]", $k, $i;
+ printf "$prefix%-20s %s\n", $ki, $a[$i];
+ }
+ } else {
+ if($k eq 'DEV') {
+ printf "$prefix%-20s -> %s\n", $k, $v->hardware_name;
+ } else {
+ printf "$prefix%-20s %s\n", $k, $v;
+ }
+ }
+ }
+}
+
+sub new($$$) {
+ my $pack = shift || die;
+ my $dev = shift || die;
+ my $product = $dev->product;
+ my $usb_top;
+
+ return undef unless $dev->is_astribank;
+ return undef unless $dev->bus_type eq 'USB';
+ return undef unless $product =~ /116./;
+ # Find USB bus toplevel
+ $usb_top = '/dev/bus/usb';
+ $usb_top = '/proc/bus/usb' unless -d $usb_top;
+ die "No USB toplevel found\n" unless -d $usb_top;
+ my $name = $dev->priv_device_name();
+ die "$0: Unkown private device name" unless defined $name;
+ my $path = "$usb_top/$name";
+ my $mppinfo = {
+ DEV => $dev,
+ HAS_MPP => 1,
+ };
+ bless $mppinfo, $pack;
+ #print STDERR "$astribank_tool($path) -- '$product'\n";
+ if(! -x $astribank_tool) {
+ warn "Could not run '$astribank_tool'\n";
+ return $mppinfo;
+ }
+ return $mppinfo unless $product =~ /116[12]/;
+ $mppinfo->{'MPP_TALK'} = 1;
+ my $dbg_file = "$name";
+ $dbg_file =~ s/\W/_/g;
+ #$dbg_file = "/tmp/twinstar-debug-$dbg_file";
+ $dbg_file = "/dev/null";
+ unless(open(F, "$astribank_tool -D '$path' 2> '$dbg_file' |")) {
+ warn "Failed running '$astribank_tool': $!";
+ return undef;
+ }
+ local $/ = "\n";
+ local $_;
+ while(<F>) {
+ chomp;
+ if(s/^INFO:\s*//) {
+ $mppinfo->{'PROTOCOL'} = $1 if /^protocol\s+version:\s*(\d+)/i;
+ } elsif(s/^EEPROM:\s*//) {
+ $mppinfo->{'EEPROM_RELEASE'} = $1 if /^release\s*:\s*([\d\.]+)/i;
+ $mppinfo->{'EEPROM_LABEL'} = $1 if /^label\s*:\s*([\w._'-]+)/i;
+ } elsif(s/^Extrainfo:\s+:\s*(.+?)$//) {
+ $mppinfo->{'EEPROM_EXTRAINFO'} = $1;
+ } elsif(s/^TwinStar:\s*//) {
+ $mppinfo->{'TWINSTAR_PORT'} = $1 if /^connected\s+to\s*:\s*usb-(\d+)/i;
+ if(s/^USB-(\d+)\s*POWER\s*:\s*//) {
+ my $v = ($_ eq 'ON') ? 1 : 0;
+ $mppinfo->{'TWINSTAR_POWER'}->[$1] = $v;
+ }
+ if(s/^Watchdog[^:]+:\s*//) {
+ my $v = ($_ eq 'on-guard') ? 1 : 0;
+ $mppinfo->{'TWINSTAR_WATCHDOG'} = $v;
+ }
+ #printf STDERR "\t%s\n", $_;
+ } else {
+ #printf STDERR "\t%s\n", $_;
+ }
+ }
+ unless(close F) {
+ warn "Failed running '$astribank_tool': $!";
+ return undef;
+ }
+ #$mppinfo->showinfo;
+ return $mppinfo;
+}
+
+sub mpp_addinfo($@) {
+ my $pack = shift || die;
+ my @devlist = @_;
+
+ foreach my $dev (@devlist) {
+ $dev->{MPPINFO} = $pack->new($dev);
+ }
+}
+
+1;
diff --git a/xpp/perl_modules/Dahdi/Xpp/Xbus.pm b/xpp/perl_modules/Dahdi/Xpp/Xbus.pm
index b7994f8..b57148c 100644
--- a/xpp/perl_modules/Dahdi/Xpp/Xbus.pm
+++ b/xpp/perl_modules/Dahdi/Xpp/Xbus.pm
@@ -9,6 +9,7 @@ package Dahdi::Xpp::Xbus;
#
use strict;
use Dahdi::Utils;
+use Dahdi::Hardware;
use Dahdi::Xpp::Xpd;
my $proc_base = "/proc/xpp";
@@ -95,6 +96,19 @@ sub read_attrs() {
}
}
+sub transport_type($$) {
+ my $xbus = shift || die;
+ my $xbus_dir = shift;
+ my $transport = "$xbus_dir/transport";
+ if(-e "$transport/ep_00") { # It's USB
+ $xbus->{TRANSPORT_TYPE} = 'USB';
+ } else {
+ warn "Unkown transport in $xbus_dir\n";
+ undef $xbus->{TRANSPORT_TYPE};
+ }
+ return $xbus->{TRANSPORT_TYPE};
+}
+
sub read_xpdnames_old($) {
my $xbus_num = shift || die;
my $pat = sprintf "/proc/xpp/XBUS-%02d/XPD-[0-9][0-9]", $xbus_num;
@@ -142,16 +156,11 @@ sub new($$) {
$self->read_attrs;
# Get transport related info
my $transport = "$xbus_dir/transport";
- my ($usbdev) = glob("$transport/usb_device:*");
- if(defined $usbdev) { # It's USB
- if($usbdev =~ /.*usb_device:usbdev(\d+)\.(\d+)/) {
- my $busnum = $1;
- my $devnum = $2;
- #printf STDERR "DEBUG: %03d/%03d\n", $busnum, $devnum;
- $self->{USB_DEVNAME} = sprintf("%03d/%03d", $busnum, $devnum);
- } else {
- warn "Bad USB transport='$transport' usbdev='$usbdev'\n";
- }
+ my $transport_type = $self->transport_type($xbus_dir);
+ if(defined $transport_type) {
+ my $tt = "Dahdi::Hardware::$transport_type";
+ my $hw = $tt->set_transport($self, $xbus_dir);
+ #printf STDERR "Xbus::new transport($transport_type): %s\n", $hw->{HARDWARE_NAME};
}
my @xpdnames;
my @xpds;
diff --git a/xpp/perl_modules/Dahdi/Xpp/Xpd.pm b/xpp/perl_modules/Dahdi/Xpp/Xpd.pm
index e5aed5d..a1e7eb3 100644
--- a/xpp/perl_modules/Dahdi/Xpp/Xpd.pm
+++ b/xpp/perl_modules/Dahdi/Xpp/Xpd.pm
@@ -162,7 +162,7 @@ sub dahdi_registration($$) {
}
sub xpds_by_spanno() {
- my @xbuses = Dahdi::Xpp::xbuses("SORT_CONNECTOR");
+ my @xbuses = Dahdi::Xpp::xbuses();
my @xpds = map { $_->xpds } @xbuses;
@xpds = grep { $_->spanno } @xpds;
@xpds = sort { $a->spanno <=> $b->spanno } @xpds;
@@ -181,7 +181,7 @@ sub new($$$$$) {
my $sysfsdir = shift || die;
my $self = {
XBUS => $xbus,
- ID => "$unit$subunit",
+ ID => sprintf("%1d%1d", $unit, $subunit),
FQN => $xbus->name . "/" . "XPD-$unit$subunit",
UNIT => $unit,
SUBUNIT => $subunit,
diff --git a/xpp/pic_loader.c b/xpp/pic_loader.c
new file mode 100644
index 0000000..c74718e
--- /dev/null
+++ b/xpp/pic_loader.c
@@ -0,0 +1,275 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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 <assert.h>
+#include <string.h>
+#include <errno.h>
+#include <regex.h>
+#include "hexfile.h"
+#include "pic_loader.h"
+#include "debug.h"
+
+#define DBG_MASK 0x03
+#define MAX_HEX_LINES 10000
+#define TIMEOUT 500
+
+enum xpp_packet_types {
+ PIC_REQ_XOP = 0x09,
+ PIC_REP_XOP = 0x0A
+};
+
+struct xpp_packet_header {
+ struct {
+ uint16_t len;
+ uint8_t op;
+ uint8_t unit;
+ } PACKED header;
+ union {
+ struct {
+ struct {
+ uint8_t flags;
+ uint8_t card_type;
+ uint16_t offs;
+ } pic_header;
+ uint8_t data[3];
+ } PACKED pic_packet;
+ } d;
+} PACKED;
+
+int send_picline(struct astribank_device *astribank, uint8_t card_type, enum pic_command pcmd, int offs, uint8_t *data, int data_len)
+{
+ int recv_answer = 0;
+ char buf[PACKET_SIZE];
+ struct xpp_packet_header *phead = (struct xpp_packet_header *)buf;
+ int pack_len;
+ int ret;
+
+ assert(astribank != NULL);
+ pack_len = data_len + sizeof(phead->header) + sizeof(phead->d.pic_packet.pic_header);
+ phead->header.len = pack_len;
+ phead->header.op = PIC_REQ_XOP;
+ phead->header.unit = 0x00;
+ phead->d.pic_packet.pic_header.flags = pcmd;
+ phead->d.pic_packet.pic_header.card_type = card_type;
+ phead->d.pic_packet.pic_header.offs = offs;
+ if(data)
+ memcpy(phead->d.pic_packet.data, data, data_len);
+ switch (pcmd) {
+ case PIC_START_FLAG:
+ break;
+ case PIC_DATA_FLAG:
+ break;
+ case PIC_END_FLAG:
+ recv_answer = 1;
+ break;
+ case PIC_ENDS_FLAG:
+ break;
+ }
+
+ DBG("PICLINE: pack_len=%d pcmd=%d\n", pack_len, pcmd);
+ dump_packet(LOG_DEBUG, "dump:picline[W]", (char *)phead, pack_len);
+
+ ret = send_usb(astribank, buf, pack_len, TIMEOUT);
+ if(ret < 0) {
+ ERR("send_usb failed: %d\n", ret);
+ return ret;
+ }
+ DBG("send_usb: Written %d bytes\n", ret);
+ if (recv_answer) {
+ ret = recv_usb(astribank, buf, sizeof(buf), TIMEOUT);
+ if(ret <= 0) {
+ ERR("No USB packs to read\n");
+ return ret;
+ } else {
+ phead = (struct xpp_packet_header *)buf;
+ if(phead->header.op != PIC_REP_XOP) {
+ ERR("Got unexpected reply OP=0x%02X\n", phead->header.op);
+ dump_packet(LOG_ERR, "hexline[ERR]", buf, ret);
+ return -EINVAL;
+ }
+ DBG("received OP=0x%02X, checksum=%02X\n", phead->header.op, phead->d.pic_packet.data[0]);
+ if(phead->d.pic_packet.data[0] != 0) {
+ ERR("PIC burning, bad checksum\n");
+ return -EINVAL;
+ }
+ }
+ }
+ return 0;
+}
+
+static const char *pic_basename(const char *fname, uint8_t *card_type)
+{
+ const char *basename;
+ regex_t regex;
+ char ebuf[BUFSIZ];
+ const char re[] = "PIC_TYPE_([0-9]+)\\.hex";
+ regmatch_t pmatch[2]; /* One for the whole match, one for the number */
+ int nmatch = (sizeof(pmatch)/sizeof(pmatch[0]));
+ int len;
+ int ret;
+
+ basename = strrchr(fname, '/');
+ if(!basename)
+ basename = fname;
+ if((ret = regcomp(&regex, re, REG_ICASE | REG_EXTENDED)) != 0) {
+ regerror(ret, &regex, ebuf, sizeof(ebuf));
+ ERR("regcomp: %s\n", ebuf);
+ return NULL;
+ }
+ if((ret = regexec(&regex, basename, nmatch, pmatch, 0)) != 0) {
+ regerror(ret, &regex, ebuf, sizeof(ebuf));
+ ERR("regexec: %s\n", ebuf);
+ regfree(&regex);
+ return NULL;
+ }
+ /*
+ * Should have both complete match and a parentheses match
+ */
+ if(pmatch[0].rm_so == -1 || pmatch[1].rm_so == -1) {
+ ERR("pic_basename: Bad match: pmatch[0].rm_so=%d pmatch[1].rm_so=%d\n",
+ pmatch[0].rm_so, pmatch[1].rm_so == -1);
+ regfree(&regex);
+ return NULL;
+ }
+ len = pmatch[1].rm_eo - pmatch[1].rm_so;
+ if(len >= sizeof(ebuf) - 1)
+ len = sizeof(ebuf) - 1;
+ memcpy(ebuf, basename + pmatch[1].rm_so, len);
+ ebuf[len] = '\0';
+ DBG("match: %s\n", ebuf);
+ ret = atoi(ebuf);
+ if(ret <= 0 || ret > 9) {
+ ERR("pic_basename: Bad type number %d\n", ret);
+ regfree(&regex);
+ return NULL;
+ }
+ *card_type = ret;
+ regfree(&regex);
+ return basename;
+}
+
+/*
+ * Returns: true on success, false on failure
+ */
+static int pic_burn(struct astribank_device *astribank, const struct hexdata *hexdata)
+{
+ const char *v = hexdata->version_info;
+ const char *basename;
+ uint8_t *data;
+ unsigned char check_sum = 0;
+ uint8_t card_type;
+ int ret;
+ unsigned int i;
+
+ v = (v[0]) ? v : "Unknown";
+ assert(astribank != NULL);
+ assert(hexdata != NULL);
+ if(!astribank->is_usb2) {
+ ERR("Skip PIC burning (not USB2)\n");
+ return 0;
+ }
+ INFO("Load PIC: %s\n", hexdata->fname);
+ basename = pic_basename(hexdata->fname, &card_type);
+ if(!basename) {
+ ERR("Bad PIC filename '%s'. Abort.\n", hexdata->fname);
+ return 0;
+ }
+ DBG("basename=%s card_type=%d maxlines=%d\n",
+ basename, card_type, hexdata->maxlines);
+ /*
+ * Try to read extra left-overs from USB controller
+ */
+ for(i = 2; i; i--) {
+ char buf[PACKET_SIZE];
+
+ if(usb_bulk_read(astribank->handle, astribank->my_ep_in, buf, sizeof(buf), 1) <= 0)
+ break;
+ }
+ if((ret = send_picline(astribank, card_type, PIC_START_FLAG, 0, NULL, 0)) != 0) {
+ perror("Failed sending start hexline");
+ return 0;
+ }
+ for(i = 0; i < hexdata->maxlines; i++) {
+ struct hexline *hexline;
+ unsigned int len;
+
+ hexline = hexdata->lines[i];
+ if(!hexline) {
+ ERR("hexdata finished early (line %d)", i);
+ return 0;
+ }
+ if(hexline->d.content.header.tt == TT_DATA) {
+ len = hexline->d.content.header.ll; /* don't send checksum */
+ if(len != 3) {
+ ERR("Bad line len %d\n", len);
+ return 0;
+ }
+ data = hexline->d.content.tt_data.data;
+ check_sum ^= data[0] ^ data[1] ^ data[2];
+ ret = send_picline(astribank, card_type, PIC_DATA_FLAG,
+ hexline->d.content.header.offset, data, len);
+ if(ret) {
+ perror("Failed sending data hexline");
+ return 0;
+ }
+ } else if(hexline->d.content.header.tt == TT_EOF) {
+ break;
+ } else {
+ ERR("Unexpected TT = %d in line %d\n",
+ hexline->d.content.header.tt, i);
+ return 0;
+ }
+ }
+ if((ret = send_picline(astribank, card_type, PIC_END_FLAG, 0, &check_sum, 1)) != 0) {
+ perror("Failed sending end hexline");
+ return 0;
+ }
+ DBG("Finished...\n");
+ return 1;
+}
+
+int load_pic(struct astribank_device *astribank, int numfiles, char *filelist[])
+{
+ int i;
+
+ INFO("Loading %d PIC files...\n", numfiles);
+ for(i = 0; i < numfiles; i++) {
+ struct hexdata *picdata;
+ const char *curr = filelist[i];
+
+ DBG("%s\n", curr);
+ if((picdata = parse_hexfile(curr, MAX_HEX_LINES)) == NULL) {
+ perror(curr);
+ return -errno;
+ }
+ if(!pic_burn(astribank, picdata)) {
+ ERR("PIC %s burning failed\n", curr);
+ return -ENODEV;
+ }
+ free_hexdata(picdata);
+ }
+ if((i = send_picline(astribank, 0, PIC_ENDS_FLAG, 0, NULL, 0)) != 0) {
+ ERR("PIC end burning failed\n");
+ return -ENODEV;
+ }
+ return 0;
+}
diff --git a/xpp/pic_loader.h b/xpp/pic_loader.h
new file mode 100644
index 0000000..f871bca
--- /dev/null
+++ b/xpp/pic_loader.h
@@ -0,0 +1,46 @@
+#ifndef PIC_LOADER_H
+#define PIC_LOADER_H
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * 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 <stdint.h>
+#include "astribank_usb.h"
+
+/*
+ * Astribank PIC loading
+ */
+
+enum pic_command {
+ PIC_DATA_FLAG = 0x00,
+ PIC_START_FLAG = 0x01,
+ PIC_END_FLAG = 0x02,
+ PIC_ENDS_FLAG = 0x04,
+};
+
+#define PIC_PACK_LEN 0x0B
+#define PIC_LINE_LEN 0x03
+
+int send_picline(struct astribank_device *astribank, uint8_t card_type,
+ enum pic_command pcmd, int offs, uint8_t *data, int data_len);
+int load_pic(struct astribank_device *astribank, int numfiles, char *filelist[]);
+
+#endif /* PIC_LOADER_H */
diff --git a/xpp/twinstar b/xpp/twinstar
new file mode 100755
index 0000000..cbcd97f
--- /dev/null
+++ b/xpp/twinstar
@@ -0,0 +1,149 @@
+#! /usr/bin/perl -w
+#
+# Written by Oron Peled <oron@actcom.co.il>
+# Copyright (C) 2007, Xorcom
+# This program is free software; you can redistribute and/or
+# modify it under the same terms as Perl itself.
+#
+# $Id$
+#
+use strict;
+use File::Basename;
+use Getopt::Std;
+BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/perl_modules"); }
+
+use Dahdi;
+use Dahdi::Hardware;
+use Dahdi::Span;
+use Dahdi::Xpp;
+use Dahdi::Xpp::Xbus;
+
+my $astribank_tool = '/usr/sbin/astribank_tool';
+
+sub usage {
+ die "Usage: $0 {status|jump|enable-wd|disable-wd|ports}\n";
+}
+
+our ($opt_v, $opt_x);
+getopts('vx') || usage;
+@ARGV == 1 or usage;
+
+
+# Find USB bus toplevel
+my $usb_top;
+$usb_top = '/dev/bus/usb';
+$usb_top = '/proc/bus/usb' unless -d $usb_top;
+die "No USB toplevel found\n" unless -d $usb_top;
+
+sub tws_devs() {
+ my @devs;
+ foreach my $dev (Dahdi::Hardware->device_list) {
+ next unless $dev->is_astribank;
+ next unless $dev->product =~ /116./;
+ push(@devs, $dev->hardware_name);
+ }
+ return @devs;
+}
+
+sub tws_usb_devfile($) {
+ my $name = shift || die;
+ # Remove prefix
+ if($name !~ s/usb://) {
+ die "$name is not a USB name\n";
+ }
+ return "$usb_top/$name";
+}
+
+sub tws_show() {
+ my @usb_devs = grep { /usb:/ } tws_devs();
+
+ foreach my $dev (@usb_devs) {
+ my $path = tws_usb_devfile($dev);
+ print "$path:\n";
+ open(F, "$astribank_tool -D '$path' 2>&1 |") or
+ die "Failed running '$astribank_tool': $!";
+ while(<F>) {
+ chomp;
+ next unless /twinstar/i;
+ printf "\t%s\n", $_;
+ }
+ close F || die "Failed running '$astribank_tool': $!";
+ }
+}
+
+sub tws_portnum($) {
+ my $dev = shift || die "Missing dev";
+ my $path = tws_usb_devfile($dev);
+ my $port;
+ open(F, "$astribank_tool -D '$path' 2>&1 |") or
+ die "Failed running '$astribank_tool': $!";
+ while(<F>) {
+ chomp;
+ next unless /twinstar/i;
+ next unless /connected\s+to/i;
+ if(/USB-(\d+)/i) {
+ $port = $1;
+ }
+ }
+ close F || die "Failed running '$astribank_tool': $!";
+ return $port;
+}
+
+sub tws_jumpdev($$) {
+ my $dev = shift || die;
+ my $port = shift;
+ my $path = tws_usb_devfile($dev);
+ print "$path -> $port\n";
+ system($astribank_tool, "-D", $path, "-p", $port);
+}
+
+sub tws_jump() {
+ my @usb_devs = grep { /usb:/ } tws_devs();
+ my %ports;
+
+ foreach my $dev (@usb_devs) {
+ my $path = tws_usb_devfile($dev);
+ my $port = tws_portnum($dev);
+ # The "other" port
+ $port = ($port == 1) ? 0 : 1;
+ $ports{$dev} = $port;
+ }
+ foreach my $usbdev (sort keys %ports) {
+ tws_jumpdev($usbdev, $ports{$usbdev});
+ }
+}
+
+sub tws_showports() {
+ my @usb_devs = grep { /usb:/ } tws_devs();
+ foreach my $dev (@usb_devs) {
+ my $port = tws_portnum($dev);
+ print "$port\n";
+ }
+}
+
+sub tws_watchdog($) {
+ my $on = shift;
+ die "tws_watchdog() on/off?" unless defined $on;
+ my @usb_devs = grep { /usb:/ } tws_devs();
+
+ foreach my $dev (@usb_devs) {
+ my $path = tws_usb_devfile($dev);
+ print "$path:\n";
+ system($astribank_tool, "-D", $path, "-w", $on);
+ die "Running $astribank_tool failed: $?" if $?;
+ }
+}
+
+if($ARGV[0] eq 'status') {
+ tws_show();
+} elsif($ARGV[0] eq 'jump') {
+ tws_jump();
+} elsif($ARGV[0] eq 'disable-wd') {
+ tws_watchdog(0);
+} elsif($ARGV[0] eq 'enable-wd') {
+ tws_watchdog(1);
+} elsif($ARGV[0] eq 'ports') {
+ tws_showports();
+}
+
+__END__
diff --git a/xpp/twinstar_hook b/xpp/twinstar_hook
new file mode 100755
index 0000000..d014500
--- /dev/null
+++ b/xpp/twinstar_hook
@@ -0,0 +1,93 @@
+#! /bin/sh
+
+me=`basename $0`
+dir=`dirname $0`
+LOGGER="logger -i -t '$me'"
+
+# Always redirect stderr somewhere, otherwise the shell script will die
+# when it tries to do I/O related stuff on closed file descriptor.
+# Our default is to throw it down the bit-bucket.
+#exec 2> /dev/console
+## If you wish to trace this script:
+#exec 2> "/tmp/${me}_$XBUS_NAME" 1>&2
+
+PATH="$dir:/usr/sbin:/sbin:/usr/bin:/bin"
+
+set -e
+
+TWS_SETUP="/etc/dahdi/twinstar_setup.conf"
+if [ ! -r "$TWS_SETUP" ]; then
+ echo "Skip($ACTION): No '$TWS_SETUP'" | $LOGGER
+ exit 0
+fi
+SYSTEMCONF="/tmp/xortel/dahdi/system.conf"
+dahdi_registration="/usr/sbin/dahdi_registration"
+dahdi_cfg="/usr/sbin/dahdi_cfg"
+xpp_blink="/usr/sbin/xpp_blink"
+twinstar="/usr/sbin/twinstar"
+
+TWS_DIR="/var/tmp"
+TWS_PREFIX="$TWS_DIR/twinstar-"
+LABELFILE="/sys/$DEVPATH/label"
+export XBUS_SORT='SORT_LABEL'
+
+echo "starting($ACTION): '$*'" | $LOGGER
+STAMP="${TWS_PREFIX}${XBUS_NAME}"
+TWS_TOTAL=`sed -e 's/#.*//' -e '/^[ ]*$/d' "$TWS_SETUP" | wc -l`
+
+case "$ACTION" in
+online)
+ if [ ! -f "$LABELFILE" ]; then
+ echo "Missing labelfile '$LABELFILE'" | $LOGGER
+ exit 1
+ fi
+ LABEL=`cat $LABELFILE`
+ TWS_LINE=`awk "/^$LABEL/" $TWS_SETUP`
+ if [ "$TWS_LINE" = "" ]; then
+ echo "online($XBUS_NAME): '$LABEL' is not in '$TWS_SETUP'" | $LOGGER
+ exit 1
+ fi
+ echo "$LABEL" > "$STAMP"
+ HOW_MANY=`ls ${TWS_PREFIX}* | wc -l`
+ echo "online($XBUS_NAME): $LABEL (${HOW_MANY}/${TWS_TOTAL})" | $LOGGER
+ "$twinstar" enable-wd
+ if [ "$HOW_MANY" -eq "$TWS_TOTAL" ]; then
+ echo "online: GOT ALL $TWS_TOTAL" | $LOGGER
+ "$dahdi_registration" on
+ "$dahdi_registration" 2>&1 | $LOGGER
+ echo "Using '$SYSTEMCONF'" 2>&1 | $LOGGER
+ "$dahdi_cfg" -v -c "$SYSTEMCONF" 2>&1 | $LOGGER
+ asterisk -rx 'module load chan_dahdi.so' 2>&1 | $LOGGER
+ sleep 1 # Just for visual effect
+ "$xpp_blink" bzzt xpd "$XBUS_NUM"
+ ports=`"$twinstar" ports`
+ if [ "$ports" = 0 ]; then
+ play /usr/share/zaptel/primary-pbx-is-ready.wav || :
+ elif [ "$ports" = 1 ]; then
+ play /usr/share/zaptel/backup-pbx-is-ready.wav || :
+ fi
+ echo "online: READY" | $LOGGER
+ fi
+ ;;
+offline)
+ LABEL=`cat $STAMP`
+ TWS_LINE=`awk "/^$LABEL/" $TWS_SETUP`
+ rm -f "$STAMP"
+ if [ "$TWS_LINE" = "" ]; then
+ echo "offline($XBUS_NAME): $LABEL is not in $TWS_SETUP" | $LOGGER
+ exit 1
+ fi
+ echo "offline($XBUS_NAME): $LABEL" | $LOGGER
+ "$twinstar" disable-wd
+ # If we want to disconnect everybody
+ "$twinstar" jump
+ asterisk -rx 'module unload chan_dahdi.so'
+ ;;
+*)
+ echo "$0: Unknown ACTION='$ACTION'" | $LOGGER
+ echo "$0: ARGS='$*'" | $LOGGER
+ echo "$0: ENV:" | $LOGGER
+ env | $LOGGER
+ exit 1
+esac
+
diff --git a/xpp/twinstar_setup b/xpp/twinstar_setup
new file mode 100755
index 0000000..da76dca
--- /dev/null
+++ b/xpp/twinstar_setup
@@ -0,0 +1,144 @@
+#! /usr/bin/perl -w
+#
+# Written by Oron Peled <oron@actcom.co.il>
+# Copyright (C) 2009, Xorcom
+# This program is free software; you can redistribute and/or
+# modify it under the same terms as Perl itself.
+#
+# $Id$
+#
+use strict;
+use File::Basename;
+BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/perl_modules"); }
+
+use Dahdi::Config::Gen qw(is_true);
+use Dahdi::Hardware;
+use Dahdi::Xpp::Mpp;
+use Dahdi::Xpp::Xbus;
+
+my $tws_file = $ENV{DAHDI_TWINSTAR_SETUP} || "/etc/dahdi/twinstar_setup.conf";
+my $xpporder_file = $ENV{XPPORDER_CONF} || "/etc/dahdi/xpp_order";
+
+my @xbuses = Dahdi::Xpp::xbuses;
+
+my $format = "%-20s %-10s # %s\n";
+
+sub prep_lines() {
+ my @twinstar_out;
+ my $first_port;
+ if(! -d "/sys/bus/astribanks") {
+ die "CANNOT generate TwinStar setup -- xpp drivers are not loaded\n";
+ }
+ foreach my $xbus (@xbuses) {
+ my $dev = $xbus->transport;
+ my $connector = $xbus->connector;
+ my $label = $xbus->label;
+ my $xbusstr = sprintf "%s (%s) [%s]", $xbus->name, $connector, $label;
+ Dahdi::Xpp::Mpp->mpp_addinfo($dev);
+ my $mppinfo = $dev->mppinfo;
+ if(! defined $mppinfo) {
+ warn "SKIP $xbusstr -- is not Twinstar capable\n";
+ next;
+ }
+ if(! defined $mppinfo->{MPP_TALK}) {
+ warn "SKIP $xbusstr -- USB firmware is not loaded\n";
+ next;
+ }
+ my $port = $mppinfo->{TWINSTAR_PORT};
+ if(! defined $port) {
+ warn "SKIP $xbusstr -- Cannot read USB port info\n";
+ next;
+ }
+ my $power = $mppinfo->{TWINSTAR_POWER};
+ if(! defined $power) {
+ warn "SKIP $xbusstr -- Cannot read USB power info\n";
+ next;
+ }
+ if(!$power->[0] || !$power->[1]) {
+ warn "Only one cable: $xbusstr\n";
+ }
+ $first_port = $port unless defined $first_port;
+ my $line = sprintf $format,
+ $label, $port, $connector;
+ push(@twinstar_out, $line);
+ if($first_port != $port) {
+ die
+ "$0: ",
+ "XBUS($connector, $label) ",
+ "connected to PORT $port ",
+ "(others to $first_port)\n";
+ }
+ }
+ return @twinstar_out;
+}
+
+sub gen_twinstar_setup($) {
+ my $file = shift || die;
+ my @twinstar_out = prep_lines;
+ if(!@twinstar_out) {
+ print STDERR "No Twinstar capable Astribanks found\n";
+ return;
+ }
+ rename "$file", "$file.bak"
+ or $! == 2 # ENOENT (No dependency on Errno.pm)
+ or die "Failed to backup old config: $!\n";
+ print "Generating $file\n";
+ open(F, ">$file") || die "$0: Failed to open $file: $!\n";
+ my $old = select F;
+ printf "# Autogenerated by %s on %s -- Next run will overwrite contents.\n",
+ $0, scalar(localtime);
+ print <<"HEAD";
+#
+# This file is parsed by twinstar_hook
+#
+HEAD
+ printf $format, "# LABEL", "PORT", "CONNECTOR";
+ foreach (@twinstar_out) {
+ print;
+ }
+ close F;
+ select $old;
+}
+
+sub gen_xpporder($) {
+ my $file = shift || die;
+
+ rename "$file", "$file.bak"
+ or $! == 2 # ENOENT (No dependency on Errno.pm)
+ or die "Failed to backup old config: $!\n";
+ print "Generating $file\n";
+ open(F, ">$file") || die "$0: Failed to open $file: $!\n";
+ my $old = select F;
+ printf "# Autogenerated by %s on %s -- Next run will overwrite contents.\n",
+ $0, scalar(localtime);
+ print <<"HEAD";
+#
+# This file is parsed by Dahdi::Xpp
+#
+HEAD
+ foreach my $xbus (@xbuses) {
+ my $label = $xbus->label;
+ printf "%s\t# @%s (%s)\n",
+ $label, $xbus->connector, $xbus->name;
+ }
+ close F;
+ select $old;
+}
+
+gen_twinstar_setup($tws_file);
+gen_xpporder($xpporder_file);
+
+1;
+
+__END__
+
+=head1 NAME
+
+twinstar - Generate configuration for dahdi drivers.
+
+=head1 DESCRIPTION
+
+Generate the F</etc/dahdi/twinstar_setup.conf>.
+This is the configuration for twinstar_hook(8).
+
+Its location may be overriden via the environment variable F<DAHDI_TWINSTAR_SETUP>.
diff --git a/xpp/xpp_fxloader b/xpp/xpp_fxloader
index 7dff66e..247f129 100644
--- a/xpp/xpp_fxloader
+++ b/xpp/xpp_fxloader
@@ -3,7 +3,7 @@
# xpp_fxloader: load Xorcom Astribank (XPP) firmware
#
# Written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
-# Copyright (C) 2006-2008, Xorcom
+# Copyright (C) 2006, 2007, 2008, Xorcom
#
# All rights reserved.
#
@@ -25,17 +25,17 @@
#
# This script can be run manually or from hotplug/udev.
#
-# Firmware files should be located in $XPP_FIRMWARE_DIR which defaults:
+# Firmware files should be located in $FIRMWARE_DIR which defaults:
# 1. /usr/share/dahdi
-# 2. Can be overidden by setting $XPP_FIRMWARE_DIR in the environment
-# 3. Can be overidden by setting $XPP_FIRMWARE_DIR in /etc/dahdi/init.conf
+# 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 $XPP_FIRMWARE_DIR
+# Make sure the firmware files are in $FIRMWARE_DIR
#
# UDEV Installation
# #################
@@ -86,9 +86,10 @@ USBFS_PREFIX=/proc/bus/usb
DEVUSB_PREFIX=/dev/bus/usb
USB_PREFIX=
-XPP_FIRMWARE_DIR="${XPP_FIRMWARE_DIR:-/usr/share/dahdi}"
+FIRMWARE_DIR="${FIRMWARE_DIR:-/usr/share/dahdi}"
+ASTRIBANK_HEXLOAD=${ASTRIBANK_HEXLOAD:-/usr/sbin/astribank_hexload}
+ASTRIBANK_TOOL=${ASTRIBANK_TOOL:-/usr/sbin/astribank_tool}
-FPGA_LOAD=${FPGA_LOAD:-/usr/sbin/fpga_load}
USB_FW="${USB_FW:-USB_FW.hex}"
if [ -r "$DEFAULTS" ]; then
@@ -121,12 +122,37 @@ find_dev() {
lsusb | tr -d : | awk "/ ID $v_id$p_id/{printf \"$USB_PREFIX/%s/%s \",\$2,\$4}"
}
-do_fxload() {
+run_fxload() {
sleep_if_race
- ( fxload -t fx2 $* 2>&1 1>/dev/null || exit 1 ) | $LOGGER
+ fxload -t fx2 $* 2>&1 1>/dev/null | $LOGGER
+ status=$PIPESTATUS
+ if [ $status != 0 ]; then
+ $LOGGER "fxload failed with status $status"
+ exit 55
+ fi
+}
+
+run_astribank_hexload() {
+ $LOGGER "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() {
+ $LOGGER "Running: $ASTRIBANK_TOOL $*"
+ $ASTRIBANK_TOOL "$@" | $LOGGER
+ status=$PIPESTATUS
+ if [ $status != 0 ]; then
+ $LOGGER "$ASTRIBANK_TOOL failed with status $status"
+ exit 77
+ fi
}
-load_fw() {
+load_usb_fw() {
v_id=$1
p_id=$2
fw=$3
@@ -134,35 +160,48 @@ load_fw() {
devices=`find_dev $v_id $p_id`
for dev in $devices
do
- ver=$(awk '/\$Id:/ { print $4 }' $XPP_FIRMWARE_DIR/$fw)
- $LOGGER "USB Firmware $XPP_FIRMWARE_DIR/$fw (Version=$ver) into $dev"
- do_fxload -D $dev -I $XPP_FIRMWARE_DIR/$fw || exit 1
+ ver=$(awk '/\$Id:/ { print $4 }' $FIRMWARE_DIR/$fw)
+ $LOGGER "USB Firmware $FIRMWARE_DIR/$fw (Version=$ver) into $dev"
+ run_fxload -D $dev -I $FIRMWARE_DIR/$fw || exit 1
done
}
-load_fpga() {
+load_fw_device() {
+ dev=$1
+ fw=$2
+ $LOGGER "FPGA loading $fw into $dev"
+ run_astribank_hexload -D "$dev" -F "$FIRMWARE_DIR/$fw"
+ pic_files=`echo "$FIRMWARE_DIR"/PIC_TYPE_[1-4].hex`
+ $LOGGER "PIC burning into $dev: $pic_files"
+ run_astribank_hexload -D "$dev" -p $pic_files
+ run_astribank_tool -D "$dev" -n # Do renumeration!
+ $LOGGER "PIC burning finished $pic_files"
+}
+
+#
+# Use in manual loading. Parallelize loading
+# firmwares to all of our devices
+#
+firmware_by_id() {
v_id=$1
p_id=$2
fw=$3
devices=`find_dev $v_id $p_id`
+ childs=""
for dev in $devices
do
(
- card_ver=`$FPGA_LOAD -g -D $dev | sed -n 's/^.*Release: *//'`
+ set -e
- $LOGGER "FPGA Firmware into $dev"
+ load_fw_device "$dev" "$fw"
sleep_if_race
- $FPGA_LOAD -D "$dev" -I "$XPP_FIRMWARE_DIR/$fw" -i | $LOGGER
- status=$PIPESTATUS
- if [ "$status" != 0 ]; then
- echo "fpga_load failed with status $status" | $LOGGER
- exit 77
- fi
) &
+ childs="$childs $!"
sleep 0.4
done
- wait
+ # Wait for specific childs to get their exit status
+ wait $childs
}
numdevs() {
@@ -198,17 +237,12 @@ wait_renumeration() {
reset_fpga() {
totaldevs=`numdevs e4e4 '11[3456][012]'`
devices=`find_dev e4e4 '11[3456][12]'`
- echo "Reseting devices [$totaldevs devices]"
+ $LOGGER "Reseting devices [$totaldevs devices]"
for dev in $devices
do
$LOGGER "Resetting FPGA Firmware on $dev"
sleep_if_race
- $FPGA_LOAD -D "$dev" -r 2>&1 >/dev/null | $LOGGER
- status=$PIPESTATUS
- if [ "$status" != 0 ]; then
- echo "fpga_load failed removing with status $status" | $LOGGER
- exit 78
- fi
+ run_astribank_tool -D "$dev" -r full 2>&1 >/dev/null
done
if [ "$1" = 'wait' ]; then
wait_renumeration $totaldevs e4e4 '11[3456]0'
@@ -236,24 +270,24 @@ reset)
;;
xppdetect|load|usb)
numdevs=`numdevs e4e4 '11[3456][01]'`
- echo "--------- FIRMWARE LOADING: ($1) [$numdevs devices]"
+ $LOGGER -- "--------- FIRMWARE LOADING: ($1) [$numdevs devices]"
- load_fw e4e4 1130 $USB_FW
- load_fw e4e4 1140 $USB_FW
- load_fw e4e4 1150 $USB_FW
- load_fw e4e4 1160 $USB_FW
+ load_usb_fw e4e4 1130 $USB_FW
+ load_usb_fw e4e4 1140 $USB_FW
+ load_usb_fw e4e4 1150 $USB_FW
+ load_usb_fw e4e4 1160 $USB_FW
wait_renumeration $numdevs e4e4 '11[3456]1'
if [ "$1" != 'usb' ]
then
- load_fpga e4e4 1131 FPGA_FXS.hex
- load_fpga e4e4 1141 FPGA_1141.hex
- load_fpga e4e4 1151 FPGA_1151.hex
- load_fpga e4e4 1161 FPGA_1161.hex
+ firmware_by_id e4e4 1131 FPGA_FXS.hex
+ firmware_by_id e4e4 1141 FPGA_1141.hex
+ firmware_by_id e4e4 1151 FPGA_1151.hex
+ firmware_by_id e4e4 1161 FPGA_1161.hex
wait_renumeration $numdevs e4e4 '11[3456]2'
fi
sleep 3 # Let it stabilize
- echo "--------- FIRMWARE IS LOADED"
+ $LOGGER -- "--------- FIRMWARE IS LOADED"
exit 0
;;
help)
@@ -284,19 +318,19 @@ then
$LOGGER "Trying to find what to do for product $PRODUCT, device $DEVICE"
prod_id=`echo "$PRODUCT" | cut -d/ -f2`
case "$PRODUCT" in
- e4e4/11[345]0/*)
- FIRM_USB="$XPP_FIRMWARE_DIR/$USB_FW"
+ e4e4/11[3456]0/*)
+ FIRM_USB="$FIRMWARE_DIR/$USB_FW"
$LOGGER "Loading firmware '$FIRM_USB' into '$DEVICE'"
- do_fxload -D "$DEVICE" -I "$FIRM_USB"
+ run_fxload -D "$DEVICE" -I "$FIRM_USB"
;;
- e4e4/11[345]1/*)
+ e4e4/11[3456]1/*)
if [ "$prod_id" = 1131 ]; then
- FIRM_FPGA="$XPP_FIRMWARE_DIR/FPGA_FXS.hex" # Legacy
+ FIRM_FPGA="FPGA_FXS.hex" # Legacy
else
- FIRM_FPGA="$XPP_FIRMWARE_DIR/FPGA_$prod_id.hex"
+ FIRM_FPGA="FPGA_$prod_id.hex"
fi
sleep_if_race
- $FPGA_LOAD -D "$DEVICE" -I "$FIRM_FPGA" 2>&1 >/dev/null | $LOGGER
+ load_fw_device "$DEVICE" "$FIRM_FPGA"
;;
esac
fi
diff --git a/xpp/xpp_order b/xpp/xpp_order
new file mode 100644
index 0000000..0345350
--- /dev/null
+++ b/xpp/xpp_order
@@ -0,0 +1,21 @@
+#
+# This is an optional configuration file for ordering
+# Dahdi registration.
+#
+# It is read from /etc/dahdi/xpp_order. This location
+# may be overriden via the environment variable XPPORDER_CONF
+#
+# Lines may contain:
+# - The Astribank label (verbatim)
+# - The Astribank connector string (prefixed with @)
+# Ordering number of each listed Astribank is determined
+# by its position in this file.
+# Astribanks not listed in this file, get an ordering
+# number of 999 (last).
+#
+# Astribanks with same ordering number are sorted by their
+# connectors (to preserve legacy behaviour).
+#
+# Examples:
+#usb:TWS-08
+#@usb-0000:06:02.2-2
diff --git a/xpp/xpp_sync b/xpp/xpp_sync
index e62169a..44f8e39 100755
--- a/xpp/xpp_sync
+++ b/xpp/xpp_sync
@@ -39,7 +39,7 @@ if(@ARGV == 1) {
sub get_sorted_xpds() {
my @good_xpds;
- foreach my $xbus (Dahdi::Xpp::xbuses('SORT_CONNECTOR')) {
+ foreach my $xbus (Dahdi::Xpp::xbuses) {
next unless $xbus->status eq 'CONNECTED';
foreach my $xpd ($xbus->xpds()) {
my $isreg = $xpd->dahdi_registration();
@@ -200,18 +200,10 @@ The parameter it is called with defaults to
I<auto>, but it is possible to override that parameter (e.g: set it to
I<dahdi>) through the value of XPP_SYNC in /etc/dahdi/init.conf .
-It should be run after dahdi_cfg as the configuration of a PRI module
-is set from it. It must be run after dahdi_registration as it only
-handles XPDs that are registered spans.
-
=head1 FILES
=over
-=item /sys/bus/astribanks/drivers/xppdrv/sync
-
-The low-level interface used by xpp_sync to set / read registration.
-
=item /proc/xpp/sync
(Deprecated: no longer supported)