From cb01267f42eba7bc5a572235e5d2571cc9b60303 Mon Sep 17 00:00:00 2001 From: Tzafrir Cohen Date: Thu, 2 Apr 2009 20:56:42 +0000 Subject: 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 --- xpp/Makefile | 52 +- xpp/astribank_allow.c | 378 ++++++++++++ xpp/astribank_hexload.8 | 66 +++ xpp/astribank_hexload.c | 201 +++++++ xpp/astribank_tool.8 | 79 +++ xpp/astribank_tool.c | 202 +++++++ xpp/astribank_upgrade | 150 +++++ xpp/astribank_usb.c | 536 +++++++++++++++++ xpp/astribank_usb.h | 102 ++++ xpp/dahdi_drivers | 4 +- xpp/dahdi_genconf | 4 +- xpp/dahdi_hardware | 50 +- xpp/dahdi_registration | 34 +- xpp/debug.c | 53 ++ xpp/debug.h | 46 ++ xpp/hexfile.h | 46 +- xpp/lsdahdi | 2 +- xpp/mpp.h | 324 ++++++++++ xpp/mpp_funcs.c | 1021 ++++++++++++++++++++++++++++++++ xpp/mpp_funcs.h | 78 +++ xpp/perl_modules/Dahdi/Hardware.pm | 95 ++- xpp/perl_modules/Dahdi/Hardware/PCI.pm | 12 +- xpp/perl_modules/Dahdi/Hardware/USB.pm | 85 ++- xpp/perl_modules/Dahdi/Xpp.pm | 93 ++- xpp/perl_modules/Dahdi/Xpp/Mpp.pm | 184 ++++++ xpp/perl_modules/Dahdi/Xpp/Xbus.pm | 29 +- xpp/perl_modules/Dahdi/Xpp/Xpd.pm | 4 +- xpp/pic_loader.c | 275 +++++++++ xpp/pic_loader.h | 46 ++ xpp/twinstar | 149 +++++ xpp/twinstar_hook | 93 +++ xpp/twinstar_setup | 144 +++++ xpp/xpp_fxloader | 128 ++-- xpp/xpp_order | 21 + xpp/xpp_sync | 10 +- 35 files changed, 4575 insertions(+), 221 deletions(-) create mode 100644 xpp/astribank_allow.c create mode 100644 xpp/astribank_hexload.8 create mode 100644 xpp/astribank_hexload.c create mode 100644 xpp/astribank_tool.8 create mode 100644 xpp/astribank_tool.c create mode 100755 xpp/astribank_upgrade create mode 100644 xpp/astribank_usb.c create mode 100644 xpp/astribank_usb.h create mode 100644 xpp/debug.c create mode 100644 xpp/debug.h create mode 100644 xpp/mpp.h create mode 100644 xpp/mpp_funcs.c create mode 100644 xpp/mpp_funcs.h create mode 100644 xpp/perl_modules/Dahdi/Xpp/Mpp.pm create mode 100644 xpp/pic_loader.c create mode 100644 xpp/pic_loader.h create mode 100755 xpp/twinstar create mode 100755 xpp/twinstar_hook create mode 100755 xpp/twinstar_setup create mode 100644 xpp/xpp_order 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 and + * Alex Landau + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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}// 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 . +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 + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include "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}// 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 . +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 + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include "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}//\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 +# 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 " + 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 + * 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 +#include +#include +#include +#include +#include +#include +#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 + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include "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 + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include "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 + * 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 + +/* + * 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 - * Copyright (C) 2006, Xorcom + * Copyright (C) 2006, 2007, 2008, Xorcom * * All rights reserved. * @@ -24,49 +24,12 @@ #define PARSE_HEXFILE_H #include - -/* - * Some portability workarounds - */ -#ifdef _WINDOWS - -#include /* 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 #include +#include #include #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 + * 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 + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include "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 *)∈ + 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 + * 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 = ; + 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 file. +Astribanks can be listed in this file by their label or by +their connector string (prefixed with <@>). + +Astribanks not listed in the F 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() { + 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 +# 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 + +=head1 DESCRIPTION + +This package uses C to collect information +about Astribanks via MPP (Managment Processor Protocol). + +The binary default location is F. It may be +overridden via module parameter C and the +C 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 objects and augment their +data with C objects. + +This method is the normal external interface of this class. + +=head2 new() + +Constructor. Receive as parameter an instance of C class +and return a C 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. + +=head2 showinfo() + +Dump an C 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() { + 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 + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include "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(®ex, re, REG_ICASE | REG_EXTENDED)) != 0) { + regerror(ret, ®ex, ebuf, sizeof(ebuf)); + ERR("regcomp: %s\n", ebuf); + return NULL; + } + if((ret = regexec(®ex, basename, nmatch, pmatch, 0)) != 0) { + regerror(ret, ®ex, ebuf, sizeof(ebuf)); + ERR("regexec: %s\n", ebuf); + regfree(®ex); + 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(®ex); + 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(®ex); + return NULL; + } + *card_type = ret; + regfree(®ex); + 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 + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include "astribank_usb.h" + +/* + * 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 +# 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() { + 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() { + 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 +# 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. +This is the configuration for twinstar_hook(8). + +Its location may be overriden via the environment variable F. 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 -# 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, but it is possible to override that parameter (e.g: set it to I) 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) -- cgit v1.2.3