diff options
Diffstat (limited to 'kernel/xpp/utils')
45 files changed, 4787 insertions, 240 deletions
diff --git a/kernel/xpp/utils/Makefile b/kernel/xpp/utils/Makefile index b88bb75..a2c2e71 100644 --- a/kernel/xpp/utils/Makefile +++ b/kernel/xpp/utils/Makefile @@ -65,13 +65,20 @@ PERL_SCRIPTS = \ PERL_MANS = $(PERL_SCRIPTS:%=%.8) -TARGETS = init_fxo_modes print_modes perlcheck +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 = init_fxo_modes print_modes .perlcheck PROG_INSTALL = genzaptelconf MAN_INSTALL = $(PROG_INSTALL:%=%.8) ifeq (1,$(PBX_LIBUSB)) TARGETS += fpga_load \ + astribank_tool \ + astribank_hexload \ + astribank_allow \ test_parse -PROG_INSTALL += fpga_load +PROG_INSTALL += fpga_load astribank_tool astribank_hexload endif ifneq (,$(PERLLIBDIR)) PROG_INSTALL += $(PERL_SCRIPTS) @@ -111,6 +118,15 @@ endif fpga_load: fpga_load.o hexfile.o $(CC) -L. -o $@ $^ $(EXTRA_LIBS) -lusb +astribank_hexload: $(ABHEXLOAD_OBJS) + $(CC) -L. -o $@ $(ABHEXLOAD_OBJS) $(EXTRA_LIBS) -lusb + +astribank_tool: $(ABTOOL_OBJS) + $(CC) -L. -o $@ $(ABTOOL_OBJS) $(EXTRA_LIBS) -lusb + +astribank_allow: $(ABALLOW_OBJS) + $(CC) -L. -o $@ $(ABALLOW_OBJS) $(EXTRA_LIBS) -lusb + fpga_load.o: CFLAGS+=-D_GNU_SOURCE # We use memrchr() test_parse: test_parse.o hexfile.o @@ -127,12 +143,12 @@ wctdm_fxomodes.h: $(WCTDM) init_fxo_modes: print_modes ./$< >$@ -perlcheck: $(PERL_SCRIPTS) +.perlcheck: $(PERL_SCRIPTS) for i in $^; do perl -I./zconf -c $$i || exit 1; done touch $@ clean: - $(RM) .depend *.o $(TARGETS) wctdm_fxomodes.h + $(RM) .depend *.o $(TARGETS) .PHONY: depend depend: .depend diff --git a/kernel/xpp/utils/astribank_allow.c b/kernel/xpp/utils/astribank_allow.c new file mode 100644 index 0000000..ec60d4a --- /dev/null +++ b/kernel/xpp/utils/astribank_allow.c @@ -0,0 +1,378 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> and + * Alex Landau <alex.landau@xorcom.com> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <getopt.h> +#include <time.h> +#include <arpa/inet.h> +#include <ctype.h> +#include "mpp.h" +#include "mpp_funcs.h" +#include "debug.h" + +static const char rcsid[] = "$Id$"; + +#define DBG_MASK 0x80 + +static char *progname; + +static void usage() +{ + fprintf(stderr, "Usage: %s [options...] -D {/proc/bus/usb|/dev/bus/usb}/<bus>/<dev> options\n", progname); + fprintf(stderr, "\tOptions:\n"); + fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); + fprintf(stderr, "\t\t[-d mask] # Debug mask (0xFF for everything)\n"); + fprintf(stderr, "\t\t[-w] # Write capabilities to EEPROM, otherwise read capabilities\n"); + fprintf(stderr, "\t\t[-f filename] # License filename (stdin/stdout if not specified)\n\n"); + exit(1); +} + +static int capabilities_burn( + struct astribank_device *astribank, + struct eeprom_table *eeprom_table, + struct capabilities *capabilities, + struct capkey *key) +{ + int ret; + + INFO("Burning capabilities\n"); + ret = mpp_caps_set(astribank, eeprom_table, capabilities, key); + if(ret < 0) { + ERR("Capabilities burning failed: %d\n", ret); + return ret; + } + INFO("Done\n"); + return 0; +} + +static int bin_to_file(void *buf, int len, FILE *f) +{ + static int bytes_on_line; + unsigned char *p = buf; + if (buf == NULL) { + if (bytes_on_line != 0) { + if (fprintf(f, "\n") != 1) + return -1; + bytes_on_line = 0; + } + return 0; + } + int i; + for (i = 0; i < len; i++) { + if (fprintf(f, "%02x", *p++) != 2) + return -1; + bytes_on_line++; + if (bytes_on_line >= 16) { + if (fprintf(f, "\n") != 1) + return -1; + bytes_on_line = 0; + } + } + return 0; +} + +static int write_to_file(struct eeprom_table *eeprom_table, struct capabilities *caps, struct capkey *key, FILE *f) +{ + fprintf(f, "-----BEGIN XORCOM LICENSE BLOCK-----\n"); + fprintf(f, "Version: 1.0\n"); + fprintf(f, "Timestamp: %u\n", caps->timestamp); + fprintf(f, "Serial: %.*s\n", LABEL_SIZE, eeprom_table->label); + fprintf(f, "Capabilities.Port.FXS: %d\n", caps->ports_fxs); + fprintf(f, "Capabilities.Port.FXO: %d\n", caps->ports_fxo); + fprintf(f, "Capabilities.Port.BRI: %d\n", caps->ports_bri); + fprintf(f, "Capabilities.Port.PRI: %d\n", caps->ports_pri); + fprintf(f, "Capabilities.Twinstar: %d\n", CAP_EXTRA_TWINSTAR(caps)); + fprintf(f, "Data:\n"); + bin_to_file(eeprom_table, sizeof(*eeprom_table), f); + bin_to_file(caps, sizeof(*caps), f); + bin_to_file(key, sizeof(*key), f); + bin_to_file(NULL, 0, f); + fprintf(f, "-----END XORCOM LICENSE BLOCK-----\n"); + return 0; +} + +/* + * Removes whitespace on both sizes of the string. + * Returns a pointer to the first non-space char. The string + * is modified in place to trim trailing whitespace. + * If the whole string is whitespace, returns NULL. + */ +char *trim(char *s) +{ + int len = strlen(s); + while (len > 0 && isspace(s[len-1])) { + len--; + } + if (len == 0) + return NULL; + s[len] = '\0'; + while (isspace(*s)) + s++; + /* *s is not a space, since in this case we'd return NULL above */ + return s; +} + +int get_key_value(char *line, char **key, char **value) +{ + char *p = strchr(line, ':'); + if (p == NULL) + return -1; + *p = '\0'; + *key = trim(line); + *value = trim(p + 1); + return 0; +} + +static int hex_digit_to_int(char c) +{ + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else + return -1; +} + +static int str_to_bin(char *line, void *buf, int maxlen) +{ + static int offset; + unsigned char *p = buf; + if (strlen(line) % 2 != 0) + return -1; + while (offset < maxlen && *line) { + uint8_t value; + char c = hex_digit_to_int(*line++); + if (c < 0 || *line == '\0') + return -1; + value = c << 4; + c = hex_digit_to_int(*line++); + if (c < 0) + return -1; + value |= c; + p[offset++] = value; + } + if (offset == maxlen && *line) + return -1; + return offset; +} + +static int read_from_file(struct eeprom_table *eeprom_table, struct capabilities *caps, struct capkey *capkey, FILE *f) +{ + char buf[256]; + char *line, *key, *value; + int state = 0; + int lineno = 0; + struct table { + struct eeprom_table eeprom_table; + struct capabilities capabilities; + struct capkey capkey; + } PACKED table; + + memset(&table, 0, sizeof(struct table)); + /* + * states: + * 0: start - before BEGIN_LICENSE_BLOCK line. on BEGIN_LICENSE_BLOCK line goto 1. + * 1: read Version, goto 2. if not version line then error. + * 2: after BEGIN line. split line into key:value. if line is Data:, goto 3. + * 3: read binary data. if line is END_LICENSE_BLOCK goto 4. + * 4: END_LICENSE_BLOCK - if not EOF - error. otherwise exit loop and success. + */ + while (fgets(buf, 256, f) != NULL) { + lineno++; + int len = strlen(buf); + if (len > 0 && buf[len-1] != '\n') { + ERR("Line %d: Line too long\n", lineno); + return -1; + } + line = trim(buf); + if (line == NULL) { + ERR("Line %d: Empty line\n", lineno); + } + switch (state) { + case 0: + if (strcmp(line, "-----BEGIN XORCOM LICENSE BLOCK-----") == 0) + state = 1; + else { + ERR("Line %d: Invalid license begin block\n", lineno); + return -1; + } + break; + case 1: + if (get_key_value(line, &key, &value) < 0) { + ERR("Line %d: Can't parse line\n", lineno); + return -1; + } + if (strcmp(key, "Version") == 0) { + if (strcmp(value, "1.0") == 0) { + state = 2; + } else { + ERR("Line %d: Unknown license file version '%s', need version '1.0'\n", lineno, value); + return -1; + } + } else { + ERR("Line %d: No license file version\n", lineno); + return -1; + } + break; + case 2: + if (get_key_value(line, &key, &value) < 0) { + ERR("Line %d: Can't parse line\n", lineno); + return -1; + } + if (strcmp(key, "Data") == 0) { + state = 3; + break; + } + break; + case 3: + if (strcmp(line, "-----END XORCOM LICENSE BLOCK-----") == 0) { + state = 4; + break; + } + if (str_to_bin(line, &table, sizeof(table)) < 0) { + ERR("Line %d: Error in data block\n", lineno); + return -1; + } + break; + case 4: + ERR("Extra data after license end block\n"); + return -1; + } + } + if (state != 4) { + ERR("Invalid license file\n"); + return -1; + } + memcpy(eeprom_table, &table.eeprom_table, sizeof(*eeprom_table)); + memcpy(caps, &table.capabilities, sizeof(*caps)); + memcpy(capkey, &table.capkey, sizeof(*capkey)); + return 0; +} + +int main(int argc, char *argv[]) +{ + char *devpath = NULL; + struct astribank_device *astribank; + struct eeprom_table eeprom_table; + struct capabilities caps; + struct capkey key; + const char options[] = "vd:D:wf:"; + int do_write = 0; + FILE *file; + char *filename = NULL; + int ret; + + progname = argv[0]; + while (1) { + int c; + + c = getopt (argc, argv, options); + if (c == -1) + break; + + switch (c) { + case 'D': + devpath = optarg; + break; + case 'v': + verbose++; + break; + case 'd': + debug_mask = strtoul(optarg, NULL, 0); + break; + case 'w': + do_write = 1; + break; + case 'f': + filename = optarg; + break; + case 'h': + default: + ERR("Unknown option '%c'\n", c); + usage(); + } + } + if(!devpath) { + ERR("Missing device path\n"); + usage(); + } + DBG("Startup %s\n", devpath); + if((astribank = mpp_init(devpath)) == NULL) { + ERR("Failed initializing MPP\n"); + return 1; + } + if(astribank->eeprom_type != EEPROM_TYPE_LARGE) { + ERR("Cannot use this program with astribank EEPROM type %d (need %d)\n", + astribank->eeprom_type, EEPROM_TYPE_LARGE); + return 1; + } + ret = mpp_caps_get(astribank, &eeprom_table, &caps, &key); + if(ret < 0) { + ERR("Failed to get original capabilities: %d\n", ret); + return 1; + } + if (do_write) { + /* update capabilities based on input file */ + file = stdin; + if (filename) { + file = fopen(filename, "r"); + if (file == NULL) { + ERR("Can't open file '%s'\n", filename); + return 1; + } + } + ret = read_from_file(&eeprom_table, &caps, &key, file); + if (ret < 0) { + ERR("Failed to read capabilities from file: %d\n", ret); + return 1; + } + show_capabilities(&caps, stderr); + if (capabilities_burn(astribank, &eeprom_table, &caps, &key) < 0) + return 1; + if (file != stdin) + fclose(file); + } else { + /* print capabilities to stdout */ + file = stdout; + if (filename) { + file = fopen(filename, "w"); + if (file == NULL) { + ERR("Can't create file '%s'\n", filename); + return 1; + } + } + ret = write_to_file(&eeprom_table, &caps, &key, file); + if (ret < 0) { + ERR("Failed to write capabilities to file: %d\n", ret); + return 1; + } + if (file != stdout) + fclose(file); + } + mpp_exit(astribank); + return 0; +} diff --git a/kernel/xpp/utils/astribank_hexload.8 b/kernel/xpp/utils/astribank_hexload.8 new file mode 100644 index 0000000..4017f78 --- /dev/null +++ b/kernel/xpp/utils/astribank_hexload.8 @@ -0,0 +1,66 @@ +.TH "ASTRIBANK_HEXLOAD" "8" "29 March 2009" "" "" + +.SH NAME +astribank_tool \- Xorcom Astribank (xpp) firmware loader +.SH SYNOPSIS +.B astribank_tool -D \fIdevice-path\fR <\fB-F|-p\fR> [\fIoptions\fR] \fIhexfile\fR + +.B astribank_tool [-h] + +.SH DESCRIPTION +.B astribank_hexload +is a second-stage firmware loader for Xorcom Astribanks. Note that some +older models use fpga_load(8) instead. + +It is used to load a file in the Intel HEX format into a Xorcom +Astribank. It can be used to load either an FPGA firmware or a PIC +firmware. It is normally run by the script xpp_fxloader. + +.SH OPTIONS +.B -D +.I device-path +.RS +Required. The device to read from/write to. On modern UDEV-based system +this is usually /dev/bus/usb/\fIbus_num\fR/\fIdevice_num\fR, +where \fIbus_num\fR and \fIdevice_num\fR are the first two numbers in the +output of lsusb(8). +On older systems that use usbfs, it is usually +/proc/bus/usb/\fIbus_num\fR/\fIdevice_num\fR. +.RE + +.B -F +.RS +The firmware to load is a FPGA firmware. +.RE + +.B -p +.RS +The firmware to load is a PIC firmware. +.RE + +.B -v +.RS +Increase verbosity. May be used multiple times. +.RE + +.B -d \fImask\fR +.RS +Set debug mask to \fImask\fR. Default is 0, 0xFF is "everything". +.RE + +.B -h +.RS +Displays usage message. +.RE + +.SH SEE ALSO +fxload(8), lsusb(8), astribank_tool(8), fpga_load(8) + +.SH AUTHOR +This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> . +Permission is granted to copy, distribute and/or modify this document under +the terms of the GNU General Public License, Version 2 any +later version published by the Free Software Foundation. + +On Debian systems, the complete text of the GNU General Public +License can be found in /usr/share/common-licenses/GPL. diff --git a/kernel/xpp/utils/astribank_hexload.c b/kernel/xpp/utils/astribank_hexload.c new file mode 100644 index 0000000..85cfd23 --- /dev/null +++ b/kernel/xpp/utils/astribank_hexload.c @@ -0,0 +1,201 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <arpa/inet.h> +#include "debug.h" +#include "hexfile.h" +#include "mpp_funcs.h" +#include "pic_loader.h" +#include "astribank_usb.h" + +#define DBG_MASK 0x80 +#define MAX_HEX_LINES 10000 + +static char *progname; + +static void usage() +{ + fprintf(stderr, "Usage: %s [options...] -D {/proc/bus/usb|/dev/bus/usb}/<bus>/<dev> hexfile...\n", progname); + fprintf(stderr, "\tOptions: {-F|-p}\n"); + fprintf(stderr, "\t\t[-F] # Load FPGA firmware\n"); + fprintf(stderr, "\t\t[-p] # Load PIC firmware\n"); + fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); + fprintf(stderr, "\t\t[-d mask] # Debug mask (0xFF for everything)\n"); + exit(1); +} + +int handle_hexline(struct astribank_device *astribank, struct hexline *hexline) +{ + uint16_t len; + uint16_t offset_dummy; + uint8_t *data; + int ret; + + assert(hexline); + assert(astribank); + if(hexline->d.content.header.tt != TT_DATA) { + DBG("Non data record type = %d\n", hexline->d.content.header.tt); + return 0; + } + len = hexline->d.content.header.ll; + offset_dummy = hexline->d.content.header.offset; + data = hexline->d.content.tt_data.data; + if((ret = mpp_send_seg(astribank, data, offset_dummy, len)) < 0) { + ERR("Failed FPGA send line: %d\n", ret); + return -EINVAL; + } + return 0; +} + +static int load_fpga(struct astribank_device *astribank, const char *hexfile) +{ + struct hexdata *hexdata = NULL; + int finished = 0; + int ret; + int i; + + if((hexdata = parse_hexfile(hexfile, MAX_HEX_LINES)) == NULL) { + perror(hexfile); + return -errno; + } + INFO("Loading FPGA firmware version %s\n", hexdata->version_info); +#if 0 + FILE *fp; + if((fp = fopen("fpga_dump_new.txt", "w")) == NULL) { + perror("dump"); + exit(1); + } +#endif + if((ret = mpp_send_start(astribank, DEST_FPGA)) < 0) { + ERR("Failed FPGA send start: %d\n", ret); + return ret; + } + for(i = 0; i < hexdata->maxlines; i++) { + struct hexline *hexline = hexdata->lines[i]; + + if(!hexline) + break; + if(finished) { + ERR("Extra data after End Of Data Record (line %d)\n", i); + return 0; + } + if(hexline->d.content.header.tt == TT_EOF) { + DBG("End of data\n"); + finished = 1; + continue; + } + if((ret = handle_hexline(astribank, hexline)) < 0) { + ERR("Failed FPGA sending in lineno %d (ret=%d)\n", i, ret);; + return ret; + } + } + if((ret = mpp_send_end(astribank)) < 0) { + ERR("Failed FPGA send end: %d\n", ret); + return ret; + } +#if 0 + fclose(fp); +#endif + free_hexdata(hexdata); + INFO("FPGA firmware loaded successfully\n"); + return 0; +} + +int main(int argc, char *argv[]) +{ + char *devpath = NULL; + struct astribank_device *astribank; + int opt_fpga = 0; + int opt_pic = 0; + const char options[] = "vd:D:Fp"; + int iface_num; + int ret; + + progname = argv[0]; + while (1) { + int c; + + c = getopt (argc, argv, options); + if (c == -1) + break; + + switch (c) { + case 'D': + devpath = optarg; + break; + case 'F': + opt_fpga = 1; + break; + case 'p': + opt_pic = 1; + break; + case 'v': + verbose++; + break; + case 'd': + debug_mask = strtoul(optarg, NULL, 0); + break; + case 'h': + default: + ERR("Unknown option '%c'\n", c); + return 1; + } + } + if((opt_fpga ^ opt_pic) == 0) { + ERR("The -F and -p options are mutually exclusive.\n"); + usage(); + } + iface_num = (opt_fpga) ? 1 : 0; + if(opt_fpga) { + if(optind != argc - 1) { + ERR("The -F option requires exacly one hexfile argument\n"); + usage(); + } + } + if(!devpath) { + ERR("Missing device path.\n"); + usage(); + } + if((astribank = astribank_open(devpath, iface_num)) == NULL) { + ERR("Opening astribank failed\n"); + return 1; + } + show_astribank_info(astribank); + if(opt_fpga) { + if(load_fpga(astribank, argv[optind]) < 0) { + ERR("Loading FPGA firmware failed\n"); + return 1; + } + } else if(opt_pic) { + if((ret = load_pic(astribank, argc - optind, argv + optind)) < 0) { + ERR("Loading PIC's failed\n"); + return 1; + } + } + astribank_close(astribank, 0); + return 0; +} diff --git a/kernel/xpp/utils/astribank_tool.8 b/kernel/xpp/utils/astribank_tool.8 new file mode 100644 index 0000000..0507ad5 --- /dev/null +++ b/kernel/xpp/utils/astribank_tool.8 @@ -0,0 +1,79 @@ +.TH "ASTRIBANK_TOOL" "8" "29 March 2009" "" "" + +.SH NAME +astribank_tool \- Xorcom Astribank (xpp) control tool +.SH SYNOPSIS +.B astribank_tool -D \fIdevice-path\fR [ options ] + +.B astribank_tool [-h] + +.SH DESCRIPTION +.B astribank_tool +is a tool to control the USB-level functionality of an Astribank. + +.SH OPTIONS +.B -D +.I device-path +.RS +Required. The device to read from/write to. On modern UDEV-based system +this is usually /dev/bus/usb/\fIbus_num\fR/\fIdevice_num\fR, +where \fIbus_num\fR and \fIdevice_num\fR are the first two numbers in the +output of lsusb(8). +On older systems that use usbfs, it is usually +/proc/bus/usb/\fIbus_num\fR/\fIdevice_num\fR. +.RE + +.B -p \fInum\fR +.RS +Set the TwinStar port number. Either 0 or 1. + +(TODO: explain). +.RE + +.B -r \fItype\fR +.RS +Reset the Astribank and renumerate its USB connection to power on product ID. + +Tyep can be: \fBhalf\fR or \fBfull\fR. + +(TODO: explain those). +.RE + +.B -w 0|1 +.RS +Enable (1) or disable (0) the TwinStar watchdog. When enabled, the +Astribank will jump to the second port if this system is "not working" +and the system on the second port is available. +.RE + +.B -n +.RS +Renumerate the Astribank product number (e.g: from 1161 to 1162). +.RE + +.B -v +.RS +Increase verbosity. May be used multiple times. +.RE + +.B -d \fImask\fR +.RS +Set debug mask to \fImask\fR. Default is 0, 0xFF is "everything". +.RE + +.B -h +.RS +Displays usage message. +.RE + +.SH SEE ALSO +fxload(8), lsusb(8), astribank_hextool(8) + +.SH AUTHOR +This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> . +Permission is granted to copy, distribute and/or modify this document under +the terms of the GNU General Public License, Version 2 any +later version published by the Free Software Foundation. + +On Debian systems, the complete text of the GNU General Public +License can be found in /usr/share/common-licenses/GPL. diff --git a/kernel/xpp/utils/astribank_tool.c b/kernel/xpp/utils/astribank_tool.c new file mode 100644 index 0000000..d5ba667 --- /dev/null +++ b/kernel/xpp/utils/astribank_tool.c @@ -0,0 +1,202 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <getopt.h> +#include <arpa/inet.h> +#include "mpp_funcs.h" +#include "debug.h" + +#define DBG_MASK 0x80 + +static char *progname; + +static void usage() +{ + fprintf(stderr, "Usage: %s [options...] -D {/proc/bus/usb|/dev/bus/usb}/<bus>/<dev>\n", progname); + fprintf(stderr, "\tOptions: {-n|-r kind}\n"); + fprintf(stderr, "\t\t[-n] # Renumerate device\n"); + fprintf(stderr, "\t\t[-r kind] # Reset: kind = {half|full}\n"); + fprintf(stderr, "\t\t[-p port] # TwinStar: USB port number [0, 1]\n"); + fprintf(stderr, "\t\t[-w (0|1)] # TwinStar: Watchdog off or on guard\n"); + fprintf(stderr, "\t\t[-v] # Increase verbosity\n"); + fprintf(stderr, "\t\t[-d mask] # Debug mask (0xFF for everything)\n"); + exit(1); +} + +static int reset_kind(const char *arg) +{ + static const struct { + const char *name; + int type_code; + } reset_kinds[] = { + { "half", 0 }, + { "full", 1 }, + }; + int i; + + for(i = 0; i < sizeof(reset_kinds)/sizeof(reset_kinds[0]); i++) { + if(strcasecmp(reset_kinds[i].name, arg) == 0) + return reset_kinds[i].type_code; + } + ERR("Uknown reset kind '%s'\n", arg); + return -1; +} + + +static int show_hardware(struct astribank_device *astribank) +{ + uint8_t unit; + uint8_t card_status; + uint8_t card_type; + int ret; + struct eeprom_table eeprom_table; + struct capabilities capabilities; + struct extrainfo extrainfo; + + show_astribank_info(astribank); + ret = mpp_caps_get(astribank, &eeprom_table, &capabilities, NULL); + if(ret < 0) + return ret; + show_eeprom(&eeprom_table, stdout); + show_astribank_status(astribank, stdout); + if(astribank->eeprom_type == EEPROM_TYPE_LARGE) { + show_capabilities(&capabilities, stdout); + if(STATUS_FPGA_LOADED(astribank->status)) { + for(unit = 0; unit < 4; unit++) { + ret = mpps_card_info(astribank, unit, &card_type, &card_status); + if(ret < 0) + return ret; + printf("CARD %d: type=%x.%x %s\n", unit, + ((card_type >> 4) & 0xF), (card_type & 0xF), + ((card_status & 0x1) ? "PIC" : "NOPIC")); + } + } + ret = mpp_extrainfo_get(astribank, &extrainfo); + if(ret < 0) + return ret; + show_extrainfo(&extrainfo, stdout); + if(CAP_EXTRA_TWINSTAR(&capabilities)) { + twinstar_show(astribank, stdout); + } + } + return 0; +} + +int main(int argc, char *argv[]) +{ + char *devpath = NULL; + struct astribank_device *astribank; + const char options[] = "vd:D:nr:p:w:"; + int opt_renumerate = 0; + char *opt_port = NULL; + char *opt_watchdog = NULL; + char *opt_reset = NULL; + int tws_portnum; + int full_reset; + int ret; + + progname = argv[0]; + while (1) { + int c; + + c = getopt (argc, argv, options); + if (c == -1) + break; + + switch (c) { + case 'D': + devpath = optarg; + break; + case 'n': + opt_renumerate++; + break; + case 'p': + opt_port = optarg; + break; + case 'w': + opt_watchdog = optarg; + break; + case 'r': + opt_reset = optarg; + if((full_reset = reset_kind(opt_reset)) < 0) + usage(); + break; + case 'v': + verbose++; + break; + case 'd': + debug_mask = strtoul(optarg, NULL, 0); + break; + case 'h': + default: + ERR("Unknown option '%c'\n", c); + usage(); + } + } + if(!devpath) { + ERR("Missing device path\n"); + usage(); + } + DBG("Startup %s\n", devpath); + if((astribank = mpp_init(devpath)) == NULL) { + ERR("Failed initializing MPP\n"); + return 1; + } + show_hardware(astribank); + if(opt_reset) { + if((ret = mpp_reset(astribank, full_reset)) < 0) { + ERR("%s Reseting astribank failed: %d\n", + (full_reset) ? "Full" : "Half", ret); + } + } else if(opt_renumerate) { + if((ret = mpp_renumerate(astribank)) < 0) { + ERR("Renumerating astribank failed: %d\n", ret); + } + } else if(opt_watchdog) { + int watchdogstate = strtoul(opt_watchdog, NULL, 0); + + INFO("TWINSTAR: Setting watchdog %s-guard\n", + (watchdogstate) ? "on" : "off"); + if((ret = mpp_tws_setwatchdog(astribank, watchdogstate)) < 0) { + ERR("Failed to set watchdog to %d\n", watchdogstate); + return 1; + } + } else if(opt_port) { + int new_portnum = strtoul(opt_port, NULL, 0); + char *msg = (new_portnum == tws_portnum) + ? " Same same, never mind..." + : ""; + + INFO("TWINSTAR: Setting portnum to %d.%s\n", new_portnum, msg); + if((ret = mpp_tws_setportnum(astribank, new_portnum)) < 0) { + ERR("Failed to set USB portnum to %d\n", new_portnum); + return 1; + } + } + mpp_exit(astribank); + return 0; +} diff --git a/kernel/xpp/utils/astribank_usb.c b/kernel/xpp/utils/astribank_usb.c new file mode 100644 index 0000000..088d360 --- /dev/null +++ b/kernel/xpp/utils/astribank_usb.c @@ -0,0 +1,536 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#define _GNU_SOURCE /* for memrchr() */ +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <stdarg.h> +#include <syslog.h> +#include <arpa/inet.h> +#include "astribank_usb.h" +#include "debug.h" + +static const char rcsid[] = "$Id$"; + +#define DBG_MASK 0x01 +#define TIMEOUT 500 + +#define TYPE_ENTRY(t,ni,n,ne,out,in,...) \ + [t] = { \ + .type_code = (t), \ + .num_interfaces = (ni), \ + .my_interface_num = (n), \ + .num_endpoints = (ne), \ + .my_ep_in = (in), \ + .my_ep_out = (out), \ + .name = #t, \ + .endpoints = { __VA_ARGS__ }, \ + } + +static const struct interface_type interface_types[] = { + TYPE_ENTRY(USB_11xx, 1, 0, 4, MP_EP_OUT, MP_EP_IN, + XPP_EP_OUT, + MP_EP_OUT, + XPP_EP_IN, + MP_EP_IN), + TYPE_ENTRY(USB_FIRMWARE_II, 2, 1, 2, MP_EP_OUT, MP_EP_IN, + MP_EP_OUT, + MP_EP_IN), + TYPE_ENTRY(USB_PIC, 2, 0, 2, XPP_EP_OUT, XPP_EP_IN, + XPP_EP_OUT, + XPP_EP_IN), + +}; +#undef TYPE_ENTRY + +//static int verbose = LOG_DEBUG; + +/* + * USB handling + */ + +/* return 1 if: + * - str has a number + * - It is larger than 0 + * - It equals num + */ +static int num_matches(int num, const char* str) { + int str_val = atoi(str); + if (str_val <= 0) + return 0; + return (str_val == num); +} + +struct usb_device *dev_of_path(const char *path) +{ + struct usb_bus *bus; + struct usb_device *dev; + char dirname[PATH_MAX]; + char filename[PATH_MAX]; + const char *p; + int bnum; + int dnum; + int ret; + + assert(path != NULL); + if(access(path, F_OK) < 0) { + perror(path); + return NULL; + } + /* Find last '/' */ + if((p = memrchr(path, '/', strlen(path))) == NULL) { + ERR("Missing a '/' in %s\n", path); + return NULL; + } + /* Get the device number */ + ret = sscanf(p + 1, "%d", &dnum); + if(ret != 1) { + ERR("Path tail is not a device number: '%s'\n", p); + return NULL; + } + /* Search for a '/' before that */ + p = memrchr(path, '/', p - path); + if(p == NULL) + p = path; /* Relative path */ + else + p++; /* skip '/' */ + /* Get the bus number */ + ret = sscanf(p, "%d", &bnum); + if(ret != 1) { + ERR("Path tail is not a bus number: '%s'\n", p); + return NULL; + } + sprintf(dirname, "%03d", bnum); + sprintf(filename, "%03d", dnum); + for (bus = usb_busses; bus; bus = bus->next) { + if (! num_matches(bnum, bus->dirname)) + //if(strcmp(bus->dirname, dirname) != 0) + continue; + for (dev = bus->devices; dev; dev = dev->next) { + //if(strcmp(dev->filename, filename) == 0) + if (num_matches(dnum, dev->filename)) + return dev; + } + } + ERR("no usb device match '%s'\n", path); + return NULL; +} + +int get_usb_string(struct astribank_device *astribank, uint8_t item, char *buf, unsigned int len) +{ + char tmp[BUFSIZ]; + int ret; + + assert(astribank->handle); + if (!item) + return 0; + ret = usb_get_string_simple(astribank->handle, item, tmp, BUFSIZ); + if (ret <= 0) + return ret; + return snprintf(buf, len, "%s", tmp); +} + +static int match_interface(const struct astribank_device *astribank, + const struct interface_type *itype) +{ + struct usb_interface *interface; + struct usb_interface_descriptor *iface_desc; + struct usb_config_descriptor *config_desc; + int i = itype - interface_types; + int inum; + int num_altsetting; + + DBG("Checking[%d]: interfaces=%d interface num=%d endpoints=%d: \"%s\"\n", + i, + itype->num_interfaces, + itype->my_interface_num, + itype->num_endpoints, + itype->name); + config_desc = astribank->dev->config; + if (!config_desc) { + ERR("No configuration descriptor: strange USB1 controller?\n"); + return 0; + } + if(config_desc->bNumInterfaces <= itype->my_interface_num) { + DBG("Too little interfaces: have %d need %d\n", + config_desc->bNumInterfaces, itype->my_interface_num + 1); + return 0; + } + if(astribank->my_interface_num != itype->my_interface_num) { + DBG("Wrong match -- not my interface num (wanted %d)\n", astribank->my_interface_num); + return 0; + } + inum = itype->my_interface_num; + interface = &config_desc->interface[inum]; + assert(interface != NULL); + iface_desc = interface->altsetting; + num_altsetting = interface->num_altsetting; + assert(num_altsetting != 0); + assert(iface_desc != NULL); + if(iface_desc->bInterfaceClass != 0xFF) { + DBG("Bad interface class 0x%X\n", iface_desc->bInterfaceClass); + return 0; + } + if(iface_desc->bInterfaceNumber != itype->my_interface_num) { + DBG("Bad interface number %d\n", iface_desc->bInterfaceNumber); + return 0; + } + if(iface_desc->bNumEndpoints != itype->num_endpoints) { + DBG("Different number of endpoints %d\n", iface_desc->bNumEndpoints); + return 0; + } + return 1; +} + +static int astribank_init(struct astribank_device *astribank) +{ + struct usb_device_descriptor *dev_desc; + struct usb_config_descriptor *config_desc; + struct usb_interface *interface; + struct usb_interface_descriptor *iface_desc; + struct usb_endpoint_descriptor *endpoint; + const struct interface_type *fwtype; + int i; + + assert(astribank); + astribank->handle = usb_open(astribank->dev); + if(!astribank->handle) { + ERR("Failed to open usb device '%s/%s': %s\n", + astribank->dev->bus->dirname, astribank->dev->filename, usb_strerror()); + return 0; + } + fwtype = astribank->fwtype; + if(usb_claim_interface(astribank->handle, fwtype->my_interface_num) != 0) { + ERR("usb_claim_interface: %s\n", usb_strerror()); + return 0; + } + dev_desc = &astribank->dev->descriptor; + config_desc = astribank->dev->config; + if (!config_desc) { + ERR("usb interface without a configuration\n"); + return 0; + } + DBG("Got config_desc. Looking for interface %d\n", fwtype->my_interface_num); + interface = &config_desc->interface[fwtype->my_interface_num]; + iface_desc = interface->altsetting; + endpoint = iface_desc->endpoint; + astribank->is_usb2 = (endpoint->wMaxPacketSize == 512); + for(i = 0; i < iface_desc->bNumEndpoints; i++, endpoint++) { + DBG("Validating endpoint @ %d (interface %d)\n", i, fwtype->my_interface_num); + if(endpoint->bEndpointAddress != fwtype->endpoints[i]) { + ERR("Wrong endpoint 0x%X != 0x%X (at index %d)\n", + endpoint->bEndpointAddress, + fwtype->endpoints[i], + i); + return 0; + } + if(endpoint->bEndpointAddress == MP_EP_OUT || endpoint->bEndpointAddress == MP_EP_IN) { + if(endpoint->wMaxPacketSize > PACKET_SIZE) { + ERR("Endpoint #%d wMaxPacketSize too large (%d)\n", i, endpoint->wMaxPacketSize); + return 0; + } + } + } + astribank->my_ep_in = fwtype->my_ep_in; + astribank->my_ep_out = fwtype->my_ep_out; + if(get_usb_string(astribank, dev_desc->iManufacturer, astribank->iManufacturer, BUFSIZ) < 0) + return 0; + if(get_usb_string(astribank, dev_desc->iProduct, astribank->iProduct, BUFSIZ) < 0) + return 0; + if(get_usb_string(astribank, dev_desc->iSerialNumber, astribank->iSerialNumber, BUFSIZ) < 0) + return 0; + if(get_usb_string(astribank, iface_desc->iInterface, astribank->iInterface, BUFSIZ) < 0) + return 0; + INFO("ID=%04X:%04X Manufacturer=[%s] Product=[%s] SerialNumber=[%s] Interface=[%s]\n", + dev_desc->idVendor, + dev_desc->idProduct, + astribank->iManufacturer, + astribank->iProduct, + astribank->iSerialNumber, + astribank->iInterface); + if(usb_clear_halt(astribank->handle, astribank->my_ep_out) != 0) { + ERR("Clearing output endpoint: %s\n", usb_strerror()); + return 0; + } + if(usb_clear_halt(astribank->handle, astribank->my_ep_in) != 0) { + ERR("Clearing input endpoint: %s\n", usb_strerror()); + return 0; + } + if((i = flush_read(astribank)) < 0) { + ERR("flush_read failed: %d\n", i); + return 0; + } + return 1; +} + +struct astribank_device *astribank_open(const char devpath[], int iface_num) +{ + struct astribank_device *astribank; + int i; + + DBG("devpath='%s' iface_num=%d\n", devpath, iface_num); + if((astribank = malloc(sizeof(*astribank))) == NULL) { + ERR("Out of memory"); + return NULL; + } + memset(astribank, 0, sizeof(*astribank)); + astribank->my_interface_num = iface_num; + usb_init(); + usb_find_busses(); + usb_find_devices(); + astribank->dev = dev_of_path(devpath); + if(!astribank->dev) { + ERR("Bailing out\n"); + goto fail; + } + DBG("Scan interface types (astribank has %d interfaces)\n", astribank->dev->config->bNumInterfaces); + for(i = 0; i < sizeof(interface_types)/sizeof(interface_types[0]); i++) { + if(match_interface(astribank, &interface_types[i])) { + DBG("Identified[%d]: interfaces=%d endpoints=%d: \"%s\"\n", + i, + interface_types[i].num_interfaces, + interface_types[i].num_endpoints, + interface_types[i].name); + astribank->fwtype = &interface_types[i]; + goto found; + } + } + ERR("Didn't find suitable device\n"); +fail: + free(astribank); + return NULL; +found: + if(!astribank_init(astribank)) + goto fail; + astribank->tx_sequenceno = 1; + return astribank; +} + +/* + * MP device handling + */ +void show_astribank_info(const struct astribank_device *astribank) +{ + assert(astribank != NULL); + printf("USB Firmware Type: [%s]\n", astribank->fwtype->name); + printf("USB iManufacturer: [%s]\n", astribank->iManufacturer); + printf("USB iProduct: [%s]\n", astribank->iProduct); + printf("USB iSerialNumber: [%s]\n", astribank->iSerialNumber); +} + +void astribank_close(struct astribank_device *astribank, int disconnected) +{ + assert(astribank != NULL); + if(!astribank->handle) + return; /* Nothing to do */ + if(!disconnected) { + if(usb_release_interface(astribank->handle, astribank->fwtype->my_interface_num) != 0) { + ERR("Releasing interface: usb: %s\n", usb_strerror()); + } + } + if(usb_close(astribank->handle) != 0) { + ERR("Closing device: usb: %s\n", usb_strerror()); + } + astribank->tx_sequenceno = 0; + astribank->handle = NULL; +} + +int send_usb(struct astribank_device *astribank, char *buf, int len, int timeout) +{ + int ret; + + dump_packet(LOG_DEBUG, __FUNCTION__, buf, len); + if(astribank->my_ep_out & USB_ENDPOINT_IN) { + ERR("send_usb called with an input endpoint 0x%x\n", astribank->my_ep_out); + return -EINVAL; + } + ret = usb_bulk_write(astribank->handle, astribank->my_ep_out, buf, len, timeout); + if(ret < 0) { + /* + * If the device was gone, it may be the + * result of renumeration. Ignore it. + */ + if(ret != -ENODEV) { + ERR("bulk_write to endpoint 0x%x failed: (%d) %s\n", + astribank->my_ep_out, ret, usb_strerror()); + dump_packet(LOG_ERR, "send_usb[ERR]", buf, len); + exit(2); + } else { + DBG("bulk_write to endpoint 0x%x got ENODEV\n", astribank->my_ep_out); + astribank_close(astribank, 1); + } + return ret; + } else if(ret != len) { + ERR("bulk_write to endpoint 0x%x short write: (%d) %s\n", + astribank->my_ep_out, ret, usb_strerror()); + dump_packet(LOG_ERR, "send_usb[ERR]", buf, len); + return -EFAULT; + } + return ret; +} + +int recv_usb(struct astribank_device *astribank, char *buf, size_t len, int timeout) +{ + int ret; + + if(astribank->my_ep_in & USB_ENDPOINT_OUT) { + ERR("recv_usb called with an output endpoint 0x%x\n", astribank->my_ep_in); + return -EINVAL; + } + ret = usb_bulk_read(astribank->handle, astribank->my_ep_in, buf, len, timeout); + if(ret < 0) { + DBG("bulk_read from endpoint 0x%x failed: (%d) %s\n", + astribank->my_ep_in, ret, usb_strerror()); + memset(buf, 0, len); + return ret; + } + dump_packet(LOG_DEBUG, __FUNCTION__, buf, ret); + return ret; +} + +int flush_read(struct astribank_device *astribank) +{ + char tmpbuf[BUFSIZ]; + int ret; + + DBG("starting...\n"); + memset(tmpbuf, 0, BUFSIZ); + ret = recv_usb(astribank, tmpbuf, BUFSIZ, 1); + if(ret < 0 && ret != -ETIMEDOUT) { + ERR("ret=%d\n", ret); + return ret; + } else if(ret > 0) { + DBG("Got %d bytes:\n", ret); + dump_packet(LOG_DEBUG, __FUNCTION__, tmpbuf, ret); + } + return 0; +} + + +int release_isvalid(uint16_t release) +{ + uint8_t rmajor = (release >> 8) & 0xFF; + uint8_t rminor = release & 0xFF; + + return (rmajor > 0) && + (rmajor < 10) && + (rminor > 0) && + (rminor < 10); +} + +int label_isvalid(const char *label) +{ + int len; + int goodlen; + const char GOOD_CHARS[] = + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "-_."; + + len = strlen(label); + goodlen = strspn(label, GOOD_CHARS); + if(len > LABEL_SIZE) { + ERR("Label too long (%d > %d)\n", len, LABEL_SIZE); + return 0; + } + if(goodlen != len) { + ERR("Bad character in label (pos=%d)\n", goodlen); + return 0; + } + return 1; +} + +int eeprom_fill(struct eeprom_table *eprm, + const char *vendor, + const char *product, + const char *release, + const char *label) +{ + uint16_t val; + + eprm->source = 0xC0; + eprm->config_byte = 0; + if(vendor) { + val = strtoul(vendor, NULL, 0); + if(!val) { + ERR("Invalid vendor '%s'\n", + vendor); + return -EINVAL; + } + eprm->vendor = val; + } + if(product) { + val = strtoul(product, NULL, 0); + if(!val) { + ERR("Invalid product '%s'\n", + product); + return -EINVAL; + } + eprm->product = val; + } + if(release) { + int release_major = 0; + int release_minor = 0; + uint16_t value; + + if(sscanf(release, "%d.%d", &release_major, &release_minor) != 2) { + ERR("Failed to parse release number '%s'\n", release); + return -EINVAL; + } + value = (release_major << 8) | release_minor; + DBG("Parsed release(%d): major=%d, minor=%d\n", + value, release_major, release_minor); + if(!release_isvalid(value)) { + ERR("Invalid release number 0x%X\n", value); + return -EINVAL; + } + eprm->release = value; + } + if(label) { + /* padding */ + if(!label_isvalid(label)) { + ERR("Invalid label '%s'\n", label); + return -EINVAL; + } + memset(eprm->label, 0, LABEL_SIZE); + memcpy(eprm->label, label, strlen(label)); + } + return 0; +} + +int astribank_has_twinstar(struct astribank_device *astribank) +{ + struct usb_device_descriptor *dev_desc; + uint16_t product_series; + + assert(astribank != NULL); + dev_desc = &astribank->dev->descriptor; + product_series = dev_desc->idProduct; + product_series &= 0xFFF0; + if(product_series == 0x1160) /* New boards */ + return 1; + return 0; +} + diff --git a/kernel/xpp/utils/astribank_usb.h b/kernel/xpp/utils/astribank_usb.h new file mode 100644 index 0000000..6f9a429 --- /dev/null +++ b/kernel/xpp/utils/astribank_usb.h @@ -0,0 +1,102 @@ +#ifndef ASTRIBANK_USB_H +#define ASTRIBANK_USB_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <usb.h> +#include "mpp.h" + +/* + * Astribank handling + */ + +#define PACKET_SIZE 512 + +/* USB Endpoints */ +#define MP_EP_OUT 0x04 /* Managment processor */ +#define MP_EP_IN 0x88 /* Managment processor */ + +#define XPP_EP_OUT 0x02 /* XPP */ +#define XPP_EP_IN 0x86 /* XPP */ + +/* USB firmware types */ +#define USB_11xx 0 +#define USB_FIRMWARE_II 1 +#define USB_PIC 2 + +struct interface_type { + int type_code; + int num_interfaces; + int my_interface_num; + int num_endpoints; + int my_ep_out; + int my_ep_in; + char *name; + int endpoints[4]; /* for matching */ +}; + +enum eeprom_burn_state { + BURN_STATE_NONE = 0, + BURN_STATE_STARTED = 1, + BURN_STATE_ENDED = 2, + BURN_STATE_FAILED = 3, +}; + +struct astribank_device { + struct usb_device *dev; + usb_dev_handle *handle; + int my_interface_num; + int my_ep_out; + int my_ep_in; + char iManufacturer[BUFSIZ]; + char iProduct[BUFSIZ]; + char iSerialNumber[BUFSIZ]; + char iInterface[BUFSIZ]; + int is_usb2; + enum eeprom_type eeprom_type; + enum eeprom_burn_state burn_state; + uint8_t status; + uint8_t mpp_proto_version; + struct eeprom_table *eeprom; + const struct interface_type *fwtype; + uint16_t tx_sequenceno; +}; + +/* + * Prototypes + */ +struct astribank_device *astribank_open(const char devpath[], int iface_num); +void astribank_close(struct astribank_device *astribank, int disconnected); +void show_astribank_info(const struct astribank_device *astribank); +int send_usb(struct astribank_device *astribank, char *buf, int len, int timeout); +int recv_usb(struct astribank_device *astribank, char *buf, size_t len, int timeout); +int flush_read(struct astribank_device *astribank); +int eeprom_fill(struct eeprom_table *eprm, + const char *vendor, + const char *product, + const char *release, + const char *label); +int astribank_has_twinstar(struct astribank_device *astribank); +int label_isvalid(const char *label); + +#endif /* ASTRIBANK_USB_H */ diff --git a/kernel/xpp/utils/debug.c b/kernel/xpp/utils/debug.c new file mode 100644 index 0000000..f188a2d --- /dev/null +++ b/kernel/xpp/utils/debug.c @@ -0,0 +1,53 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <stdint.h> +#include <stdarg.h> +#include <syslog.h> +#include "debug.h" + +int verbose = LOG_DEBUG; +int debug_mask = 0; + +void log_function(int level, int mask, const char *msg, ...) +{ + va_list ap; + + va_start(ap, msg); + if(verbose >= level) { + if(level < LOG_DEBUG || (mask & debug_mask)) + vfprintf(stderr, msg, ap); + } + va_end(ap); +} + +void dump_packet(int loglevel, const char *msg, const char *buf, int len) +{ + int i; + + log_function(loglevel, ~0, "%-15s:", msg); + for(i = 0; i < len; i++) + log_function(loglevel, ~0, " %02X", (uint8_t)buf[i]); + log_function(loglevel, ~0, "\n"); +} + diff --git a/kernel/xpp/utils/debug.h b/kernel/xpp/utils/debug.h new file mode 100644 index 0000000..185848a --- /dev/null +++ b/kernel/xpp/utils/debug.h @@ -0,0 +1,46 @@ +#ifndef DEBUG_H +#define DEBUG_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <syslog.h> + +/* + * Each module should define a unique DBG_MASK + */ + +extern int verbose; +extern int debug_mask; + +/* + * Logging + */ +void log_function(int level, int mask, const char *msg, ...) __attribute__(( format(printf, 3, 4) )); + +#define ERR(fmt, arg...) log_function(LOG_ERR, 0, "%s:%d: ERROR(%s): " fmt, __FILE__, __LINE__, __FUNCTION__, ## arg) +#define INFO(fmt, arg...) log_function(LOG_INFO, 0, "INFO: " fmt, ## arg) +#define DBG(fmt, arg...) log_function(LOG_DEBUG, DBG_MASK, \ + "%s:%d: DBG(%s): " fmt, __FILE__, __LINE__, __FUNCTION__, ## arg) + +void dump_packet(int loglevel, const char *msg, const char *buf, int len); + +#endif /* DEBUG_H */ diff --git a/kernel/xpp/utils/genconf_parameters b/kernel/xpp/utils/genconf_parameters index d1b8f62..66adab9 100644 --- a/kernel/xpp/utils/genconf_parameters +++ b/kernel/xpp/utils/genconf_parameters @@ -1,19 +1,19 @@ # -# /etc/dahdi/genconf_parameters +# /etc/genconf_parameters # # This file contains parameters that affect the -# dahdi_genconf configurator generator. +# zapconf configuration generator. # # Syntax: # * A comment from '#' to end of line # * Blank lines ignored # * Whitespace at end of line trimmed # * Single valued items: -# key <whitespaces...> value +# key <whitespace...> value # * List valued items: # key -# <whitespaces...>value1 -# <whitespaces...>value2 +# <whitespace...>value1 +# <whitespace...>value2 # ... # @@ -21,27 +21,27 @@ # extension number will be channel_number+base_exten . The default is: #base_exten 4000 # -# Make FXS (analog phones) extentions answer immediately (sets +# Make FXS (analog phones) extensions answer immediately (sets # 'immediate = yes' for them in zapata.conf). Don't enable this before # you're read documentation about this option. #fxs_immediate yes # # For FXS (analog phones) - use KS or LS? ks is the only method for # Asterisk to provide disconnect supervision and thus it would normally -# be preffered and is the default. +# be preferred and is the default. #fxs_default_start ls # # For FXO (analog lines) - use KS or LS? KS is the default and is -# normally the better choice as it allows detecting handups on many +# normally the better choice as it allows detecting hang-ups on many # lines. #fxo_default_start ls -# Set tone zone values. This is used for playing tones (busy, dialtone +# Set tone zone values. This is used for playing tones (busy, dial-tone # and such). The default is 'us'. This sets the value for both loadzone # and defaultzone in zaptel.conf . #lc_country il -# The dialplan contenxt into which to send trunks in zapata.conf or +# The dialplan context into which to send trunks in zapata.conf or # users.conf. The default value is: #context_lines from-pstn # @@ -49,7 +49,7 @@ # users.conf. The default value is: #context_phones from-internal # -# Two extea contexts for the input ports and output ports of an +# Two extra contexts for the input ports and output ports of an # Astribank. Default values are: #context_input astbank-input #context_output astbank-output @@ -78,16 +78,23 @@ # phones. #brint_overlap -# bri_hardhdlc: If this parameter is set to 'yes', in the entries for -# BRI cards 'hardhdlc' will be used instead of 'dchan' (an alias for -# 'fcshdlc'). -#bri_hardhdlc yes +# bri_hardhdlc: +# 'yes' - forces BRI cards to use 'hardhdlc' signalling. +# 'no' - forces BRI cards to use 'dchan' (an alias for 'fcshdlc'). +# It is usefull only for zaptel with the bristuff patch. +# +# If it is left out or set to 'auto': +# * Information supplied by the driver is used to decide: +# - Currently implemented for Astribanks. +# - Taken from /sys/bus/xpds/drivers/bri/dchan_hardhdlc. +# * Without this info, falls back to 'dchan'. +#bri_hardhdlc auto # For MFC/R2 Support: 'R2' will make E1 spans CAS and with the # 'r2_idle_bits' bit in zaptel.conf . It will also make zapconf default # to generating the channels of this card in unicall.conf rather than in # zapata.conf . The meaning of this may be extended somehow to support -# R2 through openr2/chan_dahdi later on. +# R2 through openr2/chan_zap later on. #pri_connection_type R2 #r2_idle_bits 1101 diff --git a/kernel/xpp/utils/hexfile.c b/kernel/xpp/utils/hexfile.c index 2a01b3f..1227b26 100644 --- a/kernel/xpp/utils/hexfile.c +++ b/kernel/xpp/utils/hexfile.c @@ -1,6 +1,6 @@ /* * Written by Oron Peled <oron@actcom.co.il> - * Copyright (C) 2006, Xorcom + * Copyright (C) 2006, 2007, 2008, Xorcom * * All rights reserved. * @@ -426,6 +426,7 @@ struct hexdata *parse_hexfile(const char *fname, unsigned int maxlines) report_func(LOG_ERR, "Failed to open hexfile '%s'\n", fname); goto err; } + snprintf(hexdata->fname, PATH_MAX, "%s", fname); for(line = 1; fgets(buf, BUFSIZ, fp); line++) { if(dos_eof) { if(report_func) diff --git a/kernel/xpp/utils/hexfile.h b/kernel/xpp/utils/hexfile.h index f8bf6a9..27c71e7 100644 --- a/kernel/xpp/utils/hexfile.h +++ b/kernel/xpp/utils/hexfile.h @@ -1,6 +1,6 @@ /* * Written by Oron Peled <oron@actcom.co.il> - * Copyright (C) 2006, Xorcom + * Copyright (C) 2006, 2007, 2008, Xorcom * * All rights reserved. * @@ -24,49 +24,12 @@ #define PARSE_HEXFILE_H #include <stdarg.h> - -/* - * Some portability workarounds - */ -#ifdef _WINDOWS - -#include <windows.h> /* for UCHAR USHORT */ -typedef UCHAR uint8_t; -typedef USHORT uint16_t; -#define PACKED -#define sscanf sscanf_s -#define ZERO_SIZE 1 - -/* From /usr/include/syslog.h */ -#define LOG_EMERG 0 /* system is unusable */ -#define LOG_ALERT 1 /* action must be taken immediately */ -#define LOG_CRIT 2 /* critical conditions */ -#define LOG_ERR 3 /* error conditions */ -#define LOG_WARNING 4 /* warning conditions */ -#define LOG_NOTICE 5 /* normal but significant condition */ -#define LOG_INFO 6 /* informational */ -#define LOG_DEBUG 7 /* debug-level messages */ - -#ifdef __cplusplus -# define __BEGIN_DECLS extern "C" { -# define __END_DECLS } -#else -# define __BEGIN_DECLS -# define __END_DECLS -#endif - -#elif __GNUC__ - +#include <stdio.h> #include <stdint.h> +#include <sys/param.h> #include <syslog.h> #define PACKED __attribute__((packed)) -#define ZERO_SIZE 1 - -#else - -#error "Cannot compile on this platform" - -#endif +#define ZERO_SIZE 0 /* Record types in hexfile */ enum { @@ -101,6 +64,7 @@ struct hexdata { unsigned int maxlines; unsigned int last_line; int got_eof; + char fname[PATH_MAX]; char version_info[BUFSIZ]; struct hexline *lines[ZERO_SIZE]; }; diff --git a/kernel/xpp/utils/lszaptel b/kernel/xpp/utils/lszaptel index d365ca1..71a95d1 100755 --- a/kernel/xpp/utils/lszaptel +++ b/kernel/xpp/utils/lszaptel @@ -17,7 +17,7 @@ use Zaptel::Xpp; use Zaptel::Xpp::Xbus; use Zaptel::Xpp::Xpd; -my @xbuses = Zaptel::Xpp::xbuses("SORT_CONNECTOR"); +my @xbuses = Zaptel::Xpp::xbuses; my @xpds = map { $_->xpds } @xbuses; foreach my $span (Zaptel::spans()) { diff --git a/kernel/xpp/utils/mpp.h b/kernel/xpp/utils/mpp.h new file mode 100644 index 0000000..b71eb30 --- /dev/null +++ b/kernel/xpp/utils/mpp.h @@ -0,0 +1,324 @@ +#ifndef MPP_H +#define MPP_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * MPP - Managment Processor Protocol definitions + */ + +#ifdef __GNUC__ +#define PACKED __attribute__((packed)) +#else +#error "We do not know how your compiler packs structures" +#endif + +#define MK_PROTO_VERSION(major, minor) (((major) << 4) | (0x0F & (minor))) + +#define MPP_PROTOCOL_VERSION MK_PROTO_VERSION(1,3) + +/* + * The eeprom_table is common to all eeprom types. + */ +#define LABEL_SIZE 8 +struct eeprom_table { + uint8_t source; /* C0 - small eeprom, C2 - large eeprom */ + uint16_t vendor; + uint16_t product; + uint16_t release; /* BCD encoded release */ + uint8_t config_byte; /* Must be 0 */ + uint8_t label[LABEL_SIZE]; +} PACKED; + +struct capabilities { + uint8_t ports_fxs; + uint8_t ports_fxo; + uint8_t ports_bri; + uint8_t ports_pri; + uint8_t extra_features; /* BIT(0) - TwinStar */ + uint8_t reserved[3]; + uint32_t timestamp; +} PACKED; + +#define CAP_EXTRA_TWINSTAR(c) ((c)->extra_features & 0x01) +#define CAP_EXTRA_TWINSTAR_SET(c) do {(c)->extra_features |= 0x01;} while (0) +#define CAP_EXTRA_TWINSTAR_CLR(c) do {(c)->extra_features &= ~0x01;} while (0) + +#define KEYSIZE 16 + +struct capkey { + uint8_t k[KEYSIZE]; +} PACKED; + +struct extrainfo { + char text[24]; +} PACKED; + +enum mpp_command_ops { + /* MSB of op signifies a reply from device */ + MPP_ACK = 0x80, + + MPP_PROTO_QUERY = 0x01, + MPP_PROTO_REPLY = 0x81, + + MPP_RENUM = 0x0B, /* Trigger USB renumeration */ + + MPP_EEPROM_SET = 0x0D, + + MPP_CAPS_GET = 0x0E, + MPP_CAPS_GET_REPLY = 0x8E, + MPP_CAPS_SET = 0x0F, /* Set AB capabilities */ + + MPP_DEV_SEND_START = 0x05, + MPP_DEV_SEND_SEG = 0x07, + MPP_DEV_SEND_END = 0x09, + + MPP_STATUS_GET = 0x11, /* Get Astribank Status */ + MPP_STATUS_GET_REPLY = 0x91, + + MPP_EXTRAINFO_GET = 0x13, /* Get extra vendor information */ + MPP_EXTRAINFO_GET_REPLY = 0x93, + MPP_EXTRAINFO_SET = 0x15, /* Set extra vendor information */ + + MPP_EEPROM_BLK_RD = 0x27, + MPP_EEPROM_BLK_RD_REPLY = 0xA7, + + MPP_SER_SEND = 0x37, + MPP_SER_RECV = 0xB7, + + MPP_RESET = 0x45, /* Reset both FPGA and USB firmwares */ + MPP_HALF_RESET = 0x47, /* Reset only FPGA firmware */ + + /* Twinstar */ + MPP_TWS_WD_MODE_SET = 0x31, /* Set watchdog off/on guard */ + MPP_TWS_WD_MODE_GET = 0x32, /* Current watchdog mode */ + MPP_TWS_WD_MODE_GET_REPLY = 0xB2, /* Current watchdog mode */ + MPP_TWS_PORT_SET = 0x34, /* USB-[0/1] */ + MPP_TWS_PORT_GET = 0x35, /* USB-[0/1] */ + MPP_TWS_PORT_GET_REPLY = 0xB5, /* USB-[0/1] */ + MPP_TWS_PWR_GET = 0x36, /* Power: bits -> USB ports */ + MPP_TWS_PWR_GET_REPLY = 0xB6, /* Power: bits -> USB ports */ +}; + +struct mpp_header { + uint16_t len; + uint16_t seq; + uint8_t op; /* MSB: 0 - to device, 1 - from device */ +} PACKED; + +enum mpp_ser_op { + SER_CARD_INFO_GET = 0x1, + SER_STAT_GET = 0x3, +}; + +/* Individual commands structure */ + +#define CMD_DEF(name, ...) struct d_ ## name { __VA_ARGS__ } PACKED d_ ## name + +CMD_DEF(ACK, + uint8_t stat; + ); + +CMD_DEF(PROTO_QUERY, + uint8_t proto_version; + uint8_t reserved; + ); + +CMD_DEF(PROTO_REPLY, + uint8_t proto_version; + uint8_t reserved; + ); + +CMD_DEF(STATUS_GET); + +CMD_DEF(STATUS_GET_REPLY, + uint8_t i2cs_data; + +#define STATUS_FPGA_LOADED(x) ((x) & 0x01) + uint8_t status; /* BIT(0) - FPGA is loaded */ + ); + +CMD_DEF(EEPROM_SET, + struct eeprom_table data; + ); + +CMD_DEF(CAPS_GET); + +CMD_DEF(CAPS_GET_REPLY, + struct eeprom_table data; + struct capabilities capabilities; + struct capkey key; + ); + +CMD_DEF(CAPS_SET, + struct eeprom_table data; + struct capabilities capabilities; + struct capkey key; + ); + +CMD_DEF(EXTRAINFO_GET); + +CMD_DEF(EXTRAINFO_GET_REPLY, + struct extrainfo info; + ); + +CMD_DEF(EXTRAINFO_SET, + struct extrainfo info; + ); + +CMD_DEF(RENUM); + +CMD_DEF(EEPROM_BLK_RD, + uint16_t offset; + uint16_t len; + ); + +CMD_DEF(EEPROM_BLK_RD_REPLY, + uint16_t offset; + uint8_t data[0]; + ); + +CMD_DEF(DEV_SEND_START, + uint8_t dest; + ); + +CMD_DEF(DEV_SEND_END); + +CMD_DEF(DEV_SEND_SEG, + uint16_t offset; + uint8_t data[0]; + ); + +CMD_DEF(RESET); +CMD_DEF(HALF_RESET); + +CMD_DEF(SER_SEND, + uint8_t data[0]; + ); + +CMD_DEF(SER_RECV, + uint8_t data[0]; + ); + +CMD_DEF(TWS_WD_MODE_SET, + uint8_t wd_active; + ); + +CMD_DEF(TWS_WD_MODE_GET); +CMD_DEF(TWS_WD_MODE_GET_REPLY, + uint8_t wd_active; + ); + +CMD_DEF(TWS_PORT_SET, + uint8_t portnum; + ); + +CMD_DEF(TWS_PORT_GET); +CMD_DEF(TWS_PORT_GET_REPLY, + uint8_t portnum; + ); + +CMD_DEF(TWS_PWR_GET); +CMD_DEF(TWS_PWR_GET_REPLY, + uint8_t power; + ); + +#undef CMD_DEF + +#define MEMBER(n) struct d_ ## n d_ ## n + +struct mpp_command { + struct mpp_header header; + union { + MEMBER(ACK); + MEMBER(PROTO_QUERY); + MEMBER(PROTO_REPLY); + MEMBER(STATUS_GET); + MEMBER(STATUS_GET_REPLY); + MEMBER(EEPROM_SET); + MEMBER(CAPS_GET); + MEMBER(CAPS_GET_REPLY); + MEMBER(CAPS_SET); + MEMBER(EXTRAINFO_GET); + MEMBER(EXTRAINFO_GET_REPLY); + MEMBER(EXTRAINFO_SET); + MEMBER(RENUM); + MEMBER(EEPROM_BLK_RD); + MEMBER(EEPROM_BLK_RD_REPLY); + MEMBER(DEV_SEND_START); + MEMBER(DEV_SEND_SEG); + MEMBER(DEV_SEND_END); + MEMBER(RESET); + MEMBER(HALF_RESET); + MEMBER(SER_SEND); + MEMBER(SER_RECV); + /* Twinstar */ + MEMBER(TWS_WD_MODE_SET); + MEMBER(TWS_WD_MODE_GET); + MEMBER(TWS_WD_MODE_GET_REPLY); + MEMBER(TWS_PORT_SET); + MEMBER(TWS_PORT_GET); + MEMBER(TWS_PORT_GET_REPLY); + MEMBER(TWS_PWR_GET); + MEMBER(TWS_PWR_GET_REPLY); + uint8_t raw_data[0]; + } PACKED alt; +} PACKED; +#undef MEMBER + +#define CMD_FIELD(cmd, name, field) ((cmd)->alt.d_ ## name.field) + +enum mpp_ack_stat { + STAT_OK = 0x00, /* acknowledges previous command */ + STAT_FAIL = 0x01, /* Last command failed */ + STAT_RESET_FAIL = 0x02, /* reset failed */ + STAT_NODEST = 0x03, /* No destination is selected */ + STAT_MISMATCH = 0x04, /* Data mismatch */ + STAT_NOACCESS = 0x05, /* No access */ + STAT_BAD_CMD = 0x06, /* Bad command */ + STAT_TOO_SHORT = 0x07, /* Packet is too short */ + STAT_ERROFFS = 0x08, /* Offset error */ + STAT_NOCODE = 0x09, /* Source was not burned before */ + STAT_NO_LEEPROM = 0x0A, /* Large EEPROM was not found */ + STAT_NO_EEPROM = 0x0B, /* No EEPROM was found */ + STAT_WRITE_FAIL = 0x0C, /* Writing to device failed */ + STAT_FPGA_ERR = 0x0D, /* FPGA error */ + STAT_KEY_ERR = 0x0E, /* Bad Capabilities Key */ + STAT_NOCAPS_ERR = 0x0F, /* No matching capability */ + STAT_NOPWR_ERR = 0x10, /* No power on USB connector */ + STAT_CAPS_FPGA_ERR = 0x11, /* Setting of the capabilities while FPGA is loaded */ +}; + +enum eeprom_type { /* EEPROM_QUERY: i2cs(ID1, ID0) */ + EEPROM_TYPE_NONE = 0, + EEPROM_TYPE_SMALL = 1, + EEPROM_TYPE_LARGE = 2, + EEPROM_TYPE_UNUSED = 3, +}; + +enum dev_dest { + DEST_NONE = 0x00, + DEST_FPGA = 0x01, + DEST_EEPROM = 0x02, +}; + +#endif /* MPP_H */ diff --git a/kernel/xpp/utils/mpp_funcs.c b/kernel/xpp/utils/mpp_funcs.c new file mode 100644 index 0000000..c52397f --- /dev/null +++ b/kernel/xpp/utils/mpp_funcs.c @@ -0,0 +1,1028 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdio.h> +#include <string.h> +#include <assert.h> +#include <errno.h> +#include <arpa/inet.h> +#include "hexfile.h" +#include "astribank_usb.h" +#include "mpp_funcs.h" +#include "debug.h" + +static const char rcsid[] = "$Id$"; + +#define DBG_MASK 0x02 + +const char *ack_status_msg(uint8_t status) +{ + const static char *msgs[] = { + [STAT_OK] = "Acknowledges previous command", + [STAT_FAIL] = "Last command failed", + [STAT_RESET_FAIL] = "Reset failed", + [STAT_NODEST] = "No destination is selected", + [STAT_MISMATCH] = "Data mismatch", + [STAT_NOACCESS] = "No access", + [STAT_BAD_CMD] = "Bad command", + [STAT_TOO_SHORT] = "Packet is too short", + [STAT_ERROFFS] = "Offset error", + [STAT_NOCODE] = "Source was not burned before", + [STAT_NO_LEEPROM] = "Large EEPROM was not found", + [STAT_NO_EEPROM] = "No EEPROM was found", + [STAT_WRITE_FAIL] = "Writing to device failed", + [STAT_FPGA_ERR] = "FPGA error", + [STAT_KEY_ERR] = "Bad Capabilities Key", + [STAT_NOCAPS_ERR] = "No matching capability", + [STAT_NOPWR_ERR] = "No power on USB connector", + [STAT_CAPS_FPGA_ERR] = "Setting of the capabilities while FPGA is loaded", + }; + if(status > sizeof(msgs)/sizeof(msgs[0])) + return "ERROR CODE TOO LARGE"; + if(!msgs[status]) + return "MISSING ERROR CODE"; + return msgs[status]; +} + +const char *eeprom_type2str(enum eeprom_type et) +{ + const static char *msgs[] = { + [EEPROM_TYPE_NONE] = "NONE", + [EEPROM_TYPE_SMALL] = "SMALL", + [EEPROM_TYPE_LARGE] = "LARGE", + [EEPROM_TYPE_UNUSED] = "UNUSED", + }; + if(et > sizeof(msgs)/sizeof(msgs[0])) + return NULL; + return msgs[et]; +}; + +struct command_desc { + uint8_t op; + const char *name; + uint16_t len; +}; + +#define CMD_RECV(o) [MPP_ ## o] { \ + .op = MPP_ ## o, \ + .name = #o, \ + .len = sizeof(struct mpp_header) + sizeof(struct d_ ## o), \ + } + +#define CMD_SEND(o) [MPP_ ## o] { \ + .op = MPP_ ## o, \ + .name = #o, \ + .len = sizeof(struct mpp_header) + sizeof(struct d_ ## o), \ + } + +static const struct command_desc command_table[] = { + CMD_RECV(ACK), + CMD_SEND(PROTO_QUERY), + CMD_SEND(STATUS_GET), + CMD_SEND(STATUS_GET_REPLY), + CMD_SEND(EEPROM_SET), + CMD_SEND(CAPS_GET), + CMD_RECV(CAPS_GET_REPLY), + CMD_SEND(CAPS_SET), + CMD_SEND(EXTRAINFO_GET), + CMD_SEND(EXTRAINFO_GET_REPLY), + CMD_SEND(EXTRAINFO_SET), + CMD_RECV(PROTO_REPLY), + CMD_SEND(RENUM), + CMD_SEND(EEPROM_BLK_RD), + CMD_RECV(EEPROM_BLK_RD_REPLY), + CMD_SEND(DEV_SEND_SEG), + CMD_SEND(DEV_SEND_START), + CMD_SEND(DEV_SEND_END), + CMD_SEND(RESET), + CMD_SEND(HALF_RESET), + CMD_SEND(SER_SEND), + CMD_SEND(SER_RECV), + /* Twinstar */ + CMD_SEND(TWS_WD_MODE_SET), + CMD_SEND(TWS_WD_MODE_GET), + CMD_RECV(TWS_WD_MODE_GET_REPLY), + CMD_SEND(TWS_PORT_SET), + CMD_SEND(TWS_PORT_GET), + CMD_RECV(TWS_PORT_GET_REPLY), + CMD_SEND(TWS_PWR_GET), + CMD_RECV(TWS_PWR_GET_REPLY), +}; +#undef CMD_SEND +#undef CMD_RECV + +struct cmd_queue { + struct cmd_queue *next; + struct cmd_queue *prev; + struct mpp_command *cmd; +}; + +static struct cmd_queue output_queue = { + .next = &output_queue, + .prev = &output_queue, + .cmd = NULL + }; + +void free_command(struct mpp_command *cmd) +{ + memset(cmd, 0, cmd->header.len); + free(cmd); +} + +struct mpp_command *new_command(uint8_t op, uint16_t extra_data) +{ + struct mpp_command *cmd; + const struct command_desc *desc; + uint16_t len; + + DBG("OP=0x%X (extra_data %d)\n", op, extra_data); + if(op > sizeof(command_table)/sizeof(command_table[0])) { + ERR("Invalid op=0x%X. Bigger than max valid op\n", op); + return NULL; + } + desc = &command_table[op]; + if(!desc->name) { + ERR("Unknown op=0x%X.\n", op); + return NULL; + } + len = desc->len + extra_data; + if((cmd = malloc(len)) == NULL) { + ERR("Out of memory\n"); + return NULL; + } + cmd->header.op = op; + cmd->header.len = len; + cmd->header.seq = 0; /* Overwritten in send_usb() */ + return cmd; +} + +const struct command_desc *get_command_desc(uint8_t op) +{ + const struct command_desc *desc; + + if(op > sizeof(command_table)/sizeof(command_table[0])) { + //ERR("Invalid op=0x%X. Bigger than max valid op\n", op); + return NULL; + } + desc = &command_table[op]; + if(!desc->name) + return NULL; + return desc; +} + +const char *get_command_name(uint8_t op) +{ + const struct command_desc *desc; + + if((desc = get_command_desc(op)) == NULL) + return NULL; + return desc->name; +} + +void dump_command(struct mpp_command *cmd) +{ + uint16_t len; + int i; + + len = cmd->header.len; + if(len < sizeof(struct mpp_header)) { + ERR("Command too short (%d)\n", len); + return; + } + INFO("DUMP: OP=0x%X len=%d seq=%d\n", + cmd->header.op, cmd->header.len, cmd->header.seq); + for(i = 0; i < len - sizeof(struct mpp_header); i++) { + INFO(" %2d. 0x%X\n", i, cmd->alt.raw_data[i]); + } +} + +int send_command(struct astribank_device *astribank, struct mpp_command *cmd, int timeout) +{ + int ret; + int len; + char *buf; + + len = cmd->header.len; + cmd->header.seq = astribank->tx_sequenceno; + + buf = (char *)cmd; + //printf("%s: len=%d\n", __FUNCTION__, len); +#if 0 + extern FILE *fp; + if(fp) { + int i; + + fprintf(fp, "%05d:", cmd->header.seq); + for(i = 0; i < len; i++) + fprintf(fp, " %02X", (uint8_t)buf[i]); + fprintf(fp, "\n"); + } +#endif + ret = send_usb(astribank, (char *)cmd, len, timeout); + if(ret < 0) { + ERR("send_usb failed ret=%d\n", ret); + } + astribank->tx_sequenceno++; + return ret; +} + +struct mpp_command *recv_command(struct astribank_device *astribank, int timeout) +{ + struct mpp_command *reply; + int ret; + + if((reply = malloc(PACKET_SIZE)) == NULL) { + ERR("Out of memory\n"); + goto err; + } + reply->header.len = 0; + 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%s\n", + ret, reply->header.len, + (ret == 1)? ". Old USB firmware?": ""); + 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); + goto err; + } + ret = mpp_status_query(astribank); + if(ret < 0) { + ERR("Status query failed: %d\n", ret); + goto err; + } + return astribank; + +err: + if (astribank) + astribank_close(astribank, 0); + return NULL; +} + +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/kernel/xpp/utils/mpp_funcs.h b/kernel/xpp/utils/mpp_funcs.h new file mode 100644 index 0000000..8ab4b3a --- /dev/null +++ b/kernel/xpp/utils/mpp_funcs.h @@ -0,0 +1,78 @@ +#ifndef MPP_FUNCS_H +#define MPP_FUNCS_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "mpp.h" +#include "astribank_usb.h" + +#define TIMEOUT 2000 + +/* high-level */ +struct astribank_device *mpp_init(const char devpath[]); +void mpp_exit(struct astribank_device *astribank); +int mpp_proto_query(struct astribank_device *astribank); +int mpp_status_query(struct astribank_device *astribank); +int mpp_eeprom_set(struct astribank_device *astribank, const struct eeprom_table *et); +int mpp_renumerate(struct astribank_device *astribank); +int mpp_caps_get(struct astribank_device *astribank, + struct eeprom_table *et, + struct capabilities *cap, + struct capkey *key); +int mpp_caps_set(struct astribank_device *astribank, + const struct eeprom_table *eeprom_table, + const struct capabilities *capabilities, + const struct capkey *key); +int mpp_extrainfo_get(struct astribank_device *astribank, struct extrainfo *info); +int mpp_extrainfo_set(struct astribank_device *astribank, const struct extrainfo *info); +int mpp_eeprom_blk_rd(struct astribank_device *astribank, uint8_t *buf, uint16_t offset, uint16_t len); +int mpp_send_start(struct astribank_device *astribank, enum dev_dest dest); +int mpp_send_end(struct astribank_device *astribank); +int mpp_send_seg(struct astribank_device *astribank, const uint8_t *data, uint16_t offset, uint16_t len); +int mpp_reset(struct astribank_device *astribank, int full_reset); +int mpp_serial_cmd(struct astribank_device *astribank, const uint8_t *in, uint8_t *out, uint16_t len); +void show_eeprom(const struct eeprom_table *eprm, FILE *fp); +void show_capabilities(const struct capabilities *capabilities, FILE *fp); +void show_astribank_status(struct astribank_device *astribank, FILE *fp); +void show_extrainfo(const struct extrainfo *extrainfo, FILE *fp); +int twinstar_show(struct astribank_device *astribank, FILE *fp); + +/* + * Serial commands to FPGA + */ +int mpps_card_info(struct astribank_device *astribank, int unit, uint8_t *card_type, uint8_t *card_status); + +/* + * Twinstar + */ +int mpp_tws_watchdog(struct astribank_device *astribank); +int mpp_tws_setwatchdog(struct astribank_device *astribank, int yes); +int mpp_tws_powerstate(struct astribank_device *astribank); +int mpp_tws_portnum(struct astribank_device *astribank); +int mpp_tws_setportnum(struct astribank_device *astribank, uint8_t portnum); + +/* low-level */ +int process_command(struct astribank_device *astribank, struct mpp_command *cmd, struct mpp_command **reply_ref); +struct mpp_command *new_command(uint8_t op, uint16_t extra_data); +void free_command(struct mpp_command *cmd); + +#endif /* MPP_FUNCS_H */ diff --git a/kernel/xpp/utils/pic_loader.c b/kernel/xpp/utils/pic_loader.c new file mode 100644 index 0000000..c74718e --- /dev/null +++ b/kernel/xpp/utils/pic_loader.c @@ -0,0 +1,275 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <assert.h> +#include <string.h> +#include <errno.h> +#include <regex.h> +#include "hexfile.h" +#include "pic_loader.h" +#include "debug.h" + +#define DBG_MASK 0x03 +#define MAX_HEX_LINES 10000 +#define TIMEOUT 500 + +enum xpp_packet_types { + PIC_REQ_XOP = 0x09, + PIC_REP_XOP = 0x0A +}; + +struct xpp_packet_header { + struct { + uint16_t len; + uint8_t op; + uint8_t unit; + } PACKED header; + union { + struct { + struct { + uint8_t flags; + uint8_t card_type; + uint16_t offs; + } pic_header; + uint8_t data[3]; + } PACKED pic_packet; + } d; +} PACKED; + +int send_picline(struct astribank_device *astribank, uint8_t card_type, enum pic_command pcmd, int offs, uint8_t *data, int data_len) +{ + int recv_answer = 0; + char buf[PACKET_SIZE]; + struct xpp_packet_header *phead = (struct xpp_packet_header *)buf; + int pack_len; + int ret; + + assert(astribank != NULL); + pack_len = data_len + sizeof(phead->header) + sizeof(phead->d.pic_packet.pic_header); + phead->header.len = pack_len; + phead->header.op = PIC_REQ_XOP; + phead->header.unit = 0x00; + phead->d.pic_packet.pic_header.flags = pcmd; + phead->d.pic_packet.pic_header.card_type = card_type; + phead->d.pic_packet.pic_header.offs = offs; + if(data) + memcpy(phead->d.pic_packet.data, data, data_len); + switch (pcmd) { + case PIC_START_FLAG: + break; + case PIC_DATA_FLAG: + break; + case PIC_END_FLAG: + recv_answer = 1; + break; + case PIC_ENDS_FLAG: + break; + } + + DBG("PICLINE: pack_len=%d pcmd=%d\n", pack_len, pcmd); + dump_packet(LOG_DEBUG, "dump:picline[W]", (char *)phead, pack_len); + + ret = send_usb(astribank, buf, pack_len, TIMEOUT); + if(ret < 0) { + ERR("send_usb failed: %d\n", ret); + return ret; + } + DBG("send_usb: Written %d bytes\n", ret); + if (recv_answer) { + ret = recv_usb(astribank, buf, sizeof(buf), TIMEOUT); + if(ret <= 0) { + ERR("No USB packs to read\n"); + return ret; + } else { + phead = (struct xpp_packet_header *)buf; + if(phead->header.op != PIC_REP_XOP) { + ERR("Got unexpected reply OP=0x%02X\n", phead->header.op); + dump_packet(LOG_ERR, "hexline[ERR]", buf, ret); + return -EINVAL; + } + DBG("received OP=0x%02X, checksum=%02X\n", phead->header.op, phead->d.pic_packet.data[0]); + if(phead->d.pic_packet.data[0] != 0) { + ERR("PIC burning, bad checksum\n"); + return -EINVAL; + } + } + } + return 0; +} + +static const char *pic_basename(const char *fname, uint8_t *card_type) +{ + const char *basename; + regex_t regex; + char ebuf[BUFSIZ]; + const char re[] = "PIC_TYPE_([0-9]+)\\.hex"; + regmatch_t pmatch[2]; /* One for the whole match, one for the number */ + int nmatch = (sizeof(pmatch)/sizeof(pmatch[0])); + int len; + int ret; + + basename = strrchr(fname, '/'); + if(!basename) + basename = fname; + if((ret = regcomp(®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/kernel/xpp/utils/pic_loader.h b/kernel/xpp/utils/pic_loader.h new file mode 100644 index 0000000..f871bca --- /dev/null +++ b/kernel/xpp/utils/pic_loader.h @@ -0,0 +1,46 @@ +#ifndef PIC_LOADER_H +#define PIC_LOADER_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2008, Xorcom + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include <stdint.h> +#include "astribank_usb.h" + +/* + * Astribank PIC loading + */ + +enum pic_command { + PIC_DATA_FLAG = 0x00, + PIC_START_FLAG = 0x01, + PIC_END_FLAG = 0x02, + PIC_ENDS_FLAG = 0x04, +}; + +#define PIC_PACK_LEN 0x0B +#define PIC_LINE_LEN 0x03 + +int send_picline(struct astribank_device *astribank, uint8_t card_type, + enum pic_command pcmd, int offs, uint8_t *data, int data_len); +int load_pic(struct astribank_device *astribank, int numfiles, char *filelist[]); + +#endif /* PIC_LOADER_H */ diff --git a/kernel/xpp/utils/twinstar b/kernel/xpp/utils/twinstar new file mode 100755 index 0000000..1c4fe78 --- /dev/null +++ b/kernel/xpp/utils/twinstar @@ -0,0 +1,160 @@ +#! /usr/bin/perl -w +# +# Written by Oron Peled <oron@actcom.co.il> +# Copyright (C) 2007, Xorcom +# This program is free software; you can redistribute and/or +# modify it under the same terms as Perl itself. +# +# $Id$ +# +use strict; +use File::Basename; +use Getopt::Std; +BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); } + +use Zaptel; +use Zaptel::Hardware; +use Zaptel::Span; +use Zaptel::Xpp; +use Zaptel::Xpp::Xbus; +use Zaptel::Xpp::Mpp; + +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 (Zaptel::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 = @_; + my $format = "%-15s %-10s %-15s %-10s %-10s\n"; + + printf $format, 'DEVICE', 'PORT', 'WATCHDOG', 'POWER0', 'POWER1'; + foreach my $dev (@usb_devs) { + my $mppinfo = $dev->mppinfo; + if(!defined $mppinfo) { + printf STDERR "%s: no MPP information\n", $dev->hardware_name; + next; + } + if(!defined $mppinfo->{TWINSTAR_PORT}) { + printf STDERR "%s: no TWINSTAR_PORT information\n", $dev->hardware_name; + next; + } + my $power = $mppinfo->twinstar_power; + printf $format, + $dev->hardware_name, + $mppinfo->twinstar_port, + ($mppinfo->twinstar_watchdog) ? "on" : "off", + ($power->[0]) ? "yes" : "no", + ($power->[1]) ? "yes" : "no"; + } +} + +sub tws_portnum($) { + my $dev = shift || die "Missing dev"; + my $mppinfo = $dev->mppinfo; + if(!defined $mppinfo) { + printf STDERR "%s: no MPP information\n", $dev->hardware_name; + return undef; + } + return $mppinfo->twinstar_port; +} + +sub tws_showports(@) { + my @usb_devs = @_; + foreach my $dev (@usb_devs) { + my $mppinfo = $dev->mppinfo; + if(!defined $mppinfo) { + printf STDERR "%s: no MPP information\n", $dev->hardware_name; + next; + } + if(!defined $mppinfo->{TWINSTAR_PORT}) { + printf STDERR "%s: no TWINSTAR_PORT information\n", $dev->hardware_name; + next; + } + printf "%s\n", $mppinfo->{TWINSTAR_PORT}; + } +} + +sub tws_watchdog($@) { + my $on = shift; + die "tws_watchdog() on/off?" unless defined $on; + my @usb_devs = @_; + + foreach my $dev (@usb_devs) { + my $mppinfo = $dev->mppinfo; + if(!defined $mppinfo) { + printf STDERR "%s: no MPP information\n", $dev->hardware_name; + next; + } + $mppinfo->mpp_setwatchdog($on); + } +} + +sub tws_jump(@) { + my @usb_devs = @_; + + foreach my $dev (@usb_devs) { + my $mppinfo = $dev->mppinfo; + if(!defined $mppinfo) { + printf STDERR "%s: no MPP information\n", $dev->hardware_name; + next; + } + $mppinfo->mpp_jump; + } +} + +sub dev_list() { + my @devs; + foreach my $dev (Zaptel::Hardware->device_list) { + next unless $dev->is_astribank; + next unless $dev->product =~ /116./; + Zaptel::Xpp::Mpp->mpp_addinfo($dev); + push(@devs, $dev); + } + return @devs; +} + +my @usb_devices = dev_list(); + +if($ARGV[0] eq 'status') { + tws_show(@usb_devices); +} elsif($ARGV[0] eq 'jump') { + tws_jump(@usb_devices); +} elsif($ARGV[0] eq 'disable-wd') { + tws_watchdog(0, @usb_devices); +} elsif($ARGV[0] eq 'enable-wd') { + tws_watchdog(1, @usb_devices); +} elsif($ARGV[0] eq 'ports') { + tws_showports(@usb_devices); +} + +__END__ diff --git a/kernel/xpp/utils/twinstar_hook b/kernel/xpp/utils/twinstar_hook new file mode 100755 index 0000000..c934e7e --- /dev/null +++ b/kernel/xpp/utils/twinstar_hook @@ -0,0 +1,132 @@ +#! /bin/sh +# +# Written by Oron Peled <oron@actcom.co.il> +# Copyright (C) 2009, Xorcom +# +# All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +# +# This is an experimental script to activate an Astribank TwinStar +# during failover. +# +# The script assume that there is an /etc/xpp_order file +# specifying the Astribanks' labels according to the required +# registration order. +# +# This file can be easily generated by running: +# zapconf xpporder +# after the system is configured and working. +# + +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 + +# Our directory in the beginning, so we can use local lab setup +PATH="$dir:/usr/sbin:/sbin:/usr/bin:/bin" + +set -e + +# For lab testing +liveconf="$dir/liveconf/zaptel" + +if [ -d "$liveconf" ]; then + zaptel_conf="$liveconf" +else + zaptel_conf="/etc" +fi + +export XPPORDER_CONF="$zaptel_conf/xpp_order" +if [ ! -r "$XPPORDER_CONF" ]; then + echo "Skip($ACTION): No '$XPPORDER_CONF'" | $LOGGER + exit 0 +fi +ZAPTELCONF="$zaptel_conf/zaptel.conf" + +TWS_DIR="/var/tmp" +TWS_PREFIX="$TWS_DIR/twinstar-" +LABELFILE="/sys/$DEVPATH/label" +export XBUS_SORT='SORT_LABEL' + +echo "starting($ACTION): setup from $XPPORDER_CONF" | $LOGGER +STAMP="${TWS_PREFIX}${XBUS_NAME}" +TWS_TOTAL=`sed -e 's/#.*//' -e '/^[ ]*$/d' -e 'y/\t/ /' "$XPPORDER_CONF" | 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/" $XPPORDER_CONF` + if [ "$TWS_LINE" = "" ]; then + echo "online($XBUS_NAME): '$LABEL' is not in '$XPPORDER_CONF'" | $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 + zt_registration on + zt_registration 2>&1 | tr '\t' ' ' | $LOGGER + echo "Using '$ZAPTELCONF'" 2>&1 | $LOGGER + ztcfg -v -c "$ZAPTELCONF" 2>&1 | $LOGGER + sleep 1 # Just for visual effect + asterisk -rx 'module load chan_zap.so' 2>&1 | $LOGGER + 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/" $XPPORDER_CONF` + rm -f "$STAMP" + if [ "$TWS_LINE" = "" ]; then + echo "offline($XBUS_NAME): $LABEL is not in $XPPORDER_CONF" | $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_zap.so' + ;; +*) + echo "$0: Unknown ACTION='$ACTION'" | $LOGGER + echo "$0: ARGS='$*'" | $LOGGER + echo "$0: ENV:" | $LOGGER + env | $LOGGER + exit 1 +esac + diff --git a/kernel/xpp/utils/twinstar_setup b/kernel/xpp/utils/twinstar_setup new file mode 100644 index 0000000..d2686b6 --- /dev/null +++ b/kernel/xpp/utils/twinstar_setup @@ -0,0 +1,155 @@ +#! /usr/bin/perl -w +# +# Written by Oron Peled <oron@actcom.co.il> +# Copyright (C) 2009, Xorcom +# This program is free software; you can redistribute and/or +# modify it under the same terms as Perl itself. +# +# $Id$ +# +use strict; +use File::Basename; +BEGIN { + my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); + $ENV{PATH} = "$dir:$ENV{PATH}"; +} + +use Zaptel::Config::Gen qw(is_true); +use Zaptel::Hardware; +use Zaptel::Xpp::Mpp; +use Zaptel::Xpp::Xbus; + +my $xpporder_file = $ENV{XPPORDER_CONF} || "/etc/xpp_order"; + +my @devices = Zaptel::Hardware->device_list; +my @xbuses = Zaptel::Xpp::xbuses; + +my $format = "%-20s %-10s # %s\n"; + +sub bad_xpds($) { + my $xbus = shift || die; + my $bad_xpds = 0; + + foreach my $xpd ($xbus->xpds) { + if(! $xpd->spanno) { + my $fqn = $xpd->fqn; + warn "\t$fqn -- Not registered with ZAPTEL\n"; + $bad_xpds++; + } + } + return $bad_xpds; +} + +sub twinstar_checks() { + my @twinstar_good; + my $first_port; + if(! -d "/sys/bus/astribanks") { + die "CANNOT generate TwinStar setup -- xpp drivers are not loaded\n"; + } + foreach my $dev (@devices) { + my $hwname = $dev->hardware_name; + my $xbus; + my $loaded; + my $tws_port; + my $tws_power; + my $tws_watchdog; + my $mppinfo; + if(! $dev->is_astribank) { + warn "SKIP $hwname -- Only Astribanks can be used for TwinStar\n"; + next; + } + Zaptel::Xpp::Mpp->mpp_addinfo($dev); + $mppinfo = $dev->mppinfo; + if(! defined $mppinfo) { + warn "SKIP $hwname -- is not TwinStar ready\n"; + next; + } + if(! defined $mppinfo->{MPP_TALK}) { + warn "SKIP $hwname -- USB firmware is not loaded\n"; + next; + } + if(! $mppinfo->{TWINSTAR_CAPABLE}) { + warn "SKIP $hwname -- is not TwinStar capable\n"; + next; + } + $xbus = $dev->xbus; + if(! defined $xbus) { + warn "SKIP $hwname -- No XBUS for this device (FPGA firmware? Initialization?)\n"; + next; + } + my $dev = $xbus->transport; + my $connector = $xbus->connector; + my $label = $xbus->label; + my $xbusstr = sprintf "%s (%s) [%s]", $xbus->name, $connector, $label; + if(bad_xpds($xbus)) { + warn "SKIP $xbusstr -- Not registered with ZAPTEL\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 "WARNING: Only one cable: $xbusstr\n"; + } + $first_port = $port unless defined $first_port; + printf "GOOD: %-15s port=%d %s\n", $label, $port, $connector; + push(@twinstar_good, $xbus); + if($first_port != $port) { + die + "$0: ", + "XBUS($connector, $label) ", + "connected to PORT $port ", + "(others to $first_port)\n"; + } + } + return @twinstar_good; +} + +my @twinstar_good = twinstar_checks; +if(!@twinstar_good) { + print STDERR "Abort. No Twinstar capable Astribanks found\n"; + exit 1; +} +print "Generating Configuration\n"; +system("zapconf -v xpporder"); +die "Failed: $?\n" if $?; + +1; + +__END__ + +=head1 NAME + +twinstar_setup - Prepares a server for Astribank TwinStar operation. + +=head1 DESCRIPTION + +This script prepares a server for Astribank TwinStar operation. +The stages are: + +=over + +=item Preliminary checks + +Check that we have only TwinStar capable Astribanks, that the drivers are already loaded. + +=item Configuration Generation + +Indirectly generate the F</etc/xpp_order> file that describes the current configuration. +This is done by running C<zapconf xpporder> + +This configuration file is used by twinstar_hook(8) to know when all Astribanks has reconnected +to the backup server. + +=item Deployment to Backup Server + +Not implemented yet. Should be done manualy. + +=back diff --git a/kernel/xpp/utils/xpp.rules b/kernel/xpp/utils/xpp.rules index d3cc226..4a26484 100644 --- a/kernel/xpp/utils/xpp.rules +++ b/kernel/xpp/utils/xpp.rules @@ -3,7 +3,7 @@ KERNEL=="*_ep*", GOTO="xpp_usb_add_end" KERNEL=="[0-9]*", GOTO="xpp_usb_add_end" # Load firmware into the Xorcom Astribank device: -SYSFS{idVendor}=="e4e4", SYSFS{idProduct}=="11[345][01]", \ +SYSFS{idVendor}=="e4e4", SYSFS{idProduct}=="11[3456][01]", \ RUN+="/usr/share/zaptel/xpp_fxloader udev $sysfs{idVendor}/$sysfs{idProduct}/$sysfs{bcdDevice}" LABEL="xpp_usb_add_end" diff --git a/kernel/xpp/utils/xpp_fxloader b/kernel/xpp/utils/xpp_fxloader index 925ee97..feaf712 100755 --- a/kernel/xpp/utils/xpp_fxloader +++ b/kernel/xpp/utils/xpp_fxloader @@ -2,13 +2,34 @@ # xpp_fxloader: load Xorcom Astribank (XPP) firmware # +# Written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> +# Copyright (C) 2006, 2007, 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. +# + +# # This script can be run manually or from hotplug/udev. # # Firmware files should be located in $FIRMWARE_DIR which defaults: # 1. /usr/share/zaptel # 2. Can be overidden by setting $FIRMWARE_DIR in the environment -# 3. Can be overidden by setting $FIRMWARE_DIR in /etc/default/zaptel -# +# 3. Can be overidden by setting $FIRMWARE_DIR in /etc/sysconfig/zaptel (Red Hat derivatives) +# or /etc/default/zaptel (Debian derivatives) # Manual Run # ########## # @@ -76,10 +97,9 @@ DEVUSB_PREFIX=/dev/bus/usb USB_PREFIX= FIRMWARE_DIR="${FIRMWARE_DIR:-/usr/share/zaptel}" +ASTRIBANK_HEXLOAD=${ASTRIBANK_HEXLOAD:-/usr/sbin/astribank_hexload} +ASTRIBANK_TOOL=${ASTRIBANK_TOOL:-/usr/sbin/astribank_tool} -FIRM_FXS=$FIRMWARE_DIR/FPGA_FXS.hex - -FPGA_LOAD=${FPGA_LOAD:-/usr/sbin/fpga_load} USB_FW="${USB_FW:-USB_FW.hex}" if [ -r "$DEFAULTS" ]; then @@ -112,12 +132,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 } -load_fw() { +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_usb_fw() { v_id=$1 p_id=$2 fw=$3 @@ -125,34 +170,48 @@ load_fw() { devices=`find_dev $v_id $p_id` for dev in $devices do - $LOGGER "USB Firmware $FIRMWARE_DIR/$fw into $dev" - do_fxload -D $dev -I $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 "$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() { @@ -188,23 +247,26 @@ 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' fi } +usage() { + echo "$0: Astribank firmware loading script." + echo "Usage: " + echo "$0 load : manual firmware loading." + echo "$0 usb : manual firmware loading: USB firmware only." + echo "$0 help : this text." +} + ######################### ## ## Manual run @@ -226,36 +288,38 @@ 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) - echo "$0: Astribank firmware loading script." - echo "Usage: " - echo "$0 load : manual firmware loading." - echo "$0 usb : manual firmware loading: USB firmware only." - echo "$0 help : this text." - echo "" - echo "('xppdetect' is an alias of 'load')" + usage exit 0 ;; +*) + if [ "$ACTION" = '' ]; then # not called from hotplug + echo "$0: Error: unknown command \"$1\"" + echo '' + usage + exit 1 + fi + ;; esac ######################### @@ -274,19 +338,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/*) + 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="$FIRMWARE_DIR/FPGA_FXS.hex" # Legacy + FIRM_FPGA="FPGA_FXS.hex" # Legacy else - FIRM_FPGA="$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/kernel/xpp/utils/xpp_sync b/kernel/xpp/utils/xpp_sync index 2840271..8167eb1 100755 --- a/kernel/xpp/utils/xpp_sync +++ b/kernel/xpp/utils/xpp_sync @@ -39,7 +39,7 @@ if(@ARGV == 1) { sub get_sorted_xpds() { my @good_xpds; - foreach my $xbus (Zaptel::Xpp::xbuses('SORT_CONNECTOR')) { + foreach my $xbus (Zaptel::Xpp::xbuses) { next unless $xbus->status eq 'CONNECTED'; foreach my $xpd ($xbus->xpds()) { my $isreg = $xpd->zt_registration(); @@ -198,7 +198,7 @@ from the '+' beside it. xpp_sync is normally called from the zaptel init.d script. The parameter it is called with defaults to I<auto>, but it is possible to override that parameter (e.g: set it to -I<zaptel>) through the value of XPP_SYNC in either /etc/defualt/zaptel +I<zaptel>) through the value of XPP_SYNC in either /etc/default/zaptel or /etc/sysconfig/zaptel . =head1 FILES @@ -207,8 +207,13 @@ or /etc/sysconfig/zaptel . =item /proc/xpp/sync +(Deprecated: no longer supported) xpp_sync is essentially a nicer interface to /proc/xpp/sync . That file shows the current xpp sync master (and in what format you need to write to it to set the master). +=head1 SEE ALSO + +zt_registration(1), ztcfg(1), README.Astribank + =back diff --git a/kernel/xpp/utils/zapconf b/kernel/xpp/utils/zapconf index ac1af0b..9991611 100755 --- a/kernel/xpp/utils/zapconf +++ b/kernel/xpp/utils/zapconf @@ -128,7 +128,7 @@ It uses two information sources: The actual zaptel hardware is automatically detected on the host. -=item /etc/genconf_params +=item /etc/genconf_parameters A configuration file that supplements the hardware information. Its location may be overriden via the C<GENCONF_PARAMETERS> environment diff --git a/kernel/xpp/utils/zaptel_drivers b/kernel/xpp/utils/zaptel_drivers index 5ace08b..e53d56f 100755 --- a/kernel/xpp/utils/zaptel_drivers +++ b/kernel/xpp/utils/zaptel_drivers @@ -24,7 +24,7 @@ if(-f $zaptel_redhat) { die "$0: Could not find '$zaptel_redhat' nor '$zaptel_debian'\n"; } -my $hardware = Zaptel::Hardware->scan; +my @drivers = Zaptel::Hardware->drivers; sub update_zaptel_distro(@) { my @driver_list = @_; @@ -110,14 +110,14 @@ sub update_etc_modules(@) { } if($opts{'d'}) { - update_etc_modules($hardware->drivers); + update_etc_modules(@drivers); } if($opts{'M'}) { - update_zaptel_distro($hardware->drivers); + update_zaptel_distro(@drivers); } if(!$opts{'d'} && !$opts{'M'}) { - print join("\n", $hardware->drivers),"\n"; + print join("\n", @drivers),"\n"; } __END__ diff --git a/kernel/xpp/utils/zaptel_hardware b/kernel/xpp/utils/zaptel_hardware index 004a44b..81823f1 100755 --- a/kernel/xpp/utils/zaptel_hardware +++ b/kernel/xpp/utils/zaptel_hardware @@ -17,16 +17,16 @@ use Zaptel::Span; use Zaptel::Xpp; use Zaptel::Xpp::Xbus; use Zaptel::Hardware; +use Zaptel::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 = Zaptel::Hardware->scan; my @spans = Zaptel::spans; sub show_xbus($) { @@ -37,6 +37,7 @@ sub show_xbus($) { printf " LABEL=%-20s CONNECTOR=%-20s\n", $label, $connector; foreach my $xpd (@xpds) { my $reg = $xpd->zt_registration; + my $channels = '(' . $xpd->channels . ')'; my $span; my $spanstr; if($reg && @spans) { @@ -48,7 +49,7 @@ sub show_xbus($) { my $master = ''; #$master = "XPP-SYNC" if $xpd->is_sync_master; $master .= " ZAPTEL-SYNC" if defined($span) && $span->is_zaptel_sync_master; - printf "\t%-10s: %-8s %s %s\n", $xpd->fqn, $xpd->type, $spanstr, $master; + printf "\t%-10s: %-8s %-5s %s %s\n", $xpd->fqn, $xpd->type, $channels, $spanstr, $master; } } @@ -59,37 +60,68 @@ sub show_disconnected(%) { my %seen = @_; my $notified_lost = 0; - foreach my $xbus (Zaptel::Xpp::xbuses('SORT_CONNECTOR')) { + foreach my $xbus (Zaptel::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: +Zaptel::Xpp::xbuses if ($opts{'v'}); + +my @devices = Zaptel::Hardware->device_list; +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'}) { + Zaptel::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 && exists $mppinfo->{MPP_TALK}) { + printf " MPP: TWINSTAR_PORT=$tws_port\n" if defined $tws_port; + printf " MPP: TWINSTAR_WATCHDOG=$tws_watchdog\n" if defined $tws_watchdog; + 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/kernel/xpp/utils/zconf/Zaptel.pm b/kernel/xpp/utils/zconf/Zaptel.pm index 1b73d5b..a7784f9 100644 --- a/kernel/xpp/utils/zconf/Zaptel.pm +++ b/kernel/xpp/utils/zconf/Zaptel.pm @@ -46,6 +46,7 @@ sub spans() { -d $proc_base or return (); foreach my $zfile (glob "$proc_base/*") { $zfile =~ s:$proc_base/::; + next unless ($zfile =~ /^\d+$/); my $span = Zaptel::Span->new($zfile); push(@spans, $span); } diff --git a/kernel/xpp/utils/zconf/Zaptel/Chans.pm b/kernel/xpp/utils/zconf/Zaptel/Chans.pm index aa58d1d..3090b8a 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Chans.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Chans.pm @@ -139,21 +139,25 @@ sub new($$$$$$) { $type = "FXS"; # likely Rhino } elsif ($fqn =~ m{\bFXO/.*}) { $type = "FXO"; # likely Rhino - } elsif ($fqn =~ m{\b---/.*}) { + } elsif ($fqn =~ m{---/.*}) { $type = "EMPTY"; # likely Rhino, empty slot. - } elsif ($fqn =~ m{\b(TE[24]|WCT1|Tor2|TorISA|WP[TE]1|cwain[12])/.*}) { + } elsif ($fqn =~ m{\b(TE[24]|WCT1|Tor2|TorISA|WP[TE]1|cwain[12]|R[124]T1)/.*}) { # TE[24]: Digium wct4xxp # WCT1: Digium single span card drivers? # Tor2: Tor PCI cards # TorISA: ISA ones (still used?) # WP[TE]1: Sangoma. TODO: this one tells us if it is TE or NT. # cwain: Junghanns E1 card. + # R[124]: Rhino r1t1/rxt1 cards $type = "PRI"; } elsif ($fqn =~ m{\b(B4|ZTHFC\d*|ztqoz\d*)/.*}) { # B4: The Digium wcb4xxp ZAPTEL driver # ZTHFC: HFC-s single-port card (zaphfc/vzaphfc) # ztqoz: qozap (Junghanns) multi-port HFC card $type = "BRI"; + } elsif ($fqn =~ m{\bZTD/.*}) { + # DYN : Dynamic span (TDMOE) + $type = "ZTD" } elsif ($fqn =~ m{\bztgsm/.*}) { # Junghanns GSM card $type = "GSM"; @@ -189,7 +193,7 @@ sub probe_type($) { my $num = $self->num; my $type; - if($fqn =~ m:WCTDM/| WRTDM/|OPVXA1200/:) { + if($fqn =~ m:WCTDM/|WRTDM/|OPVXA1200/:) { my %maybe; undef %maybe; diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Unicall.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Unicall.pm index 3e1048d..6d9552f 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Unicall.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Unicall.pm @@ -32,7 +32,9 @@ sub generate($) { print "Generating $file\n" if $genopts->{verbose}; open(F, ">$file") || die "$0: Failed to open $file: $!\n"; my $old = select F; - printf "; Autogenerated by %s on %s -- do not hand edit\n", $0, scalar(localtime); + printf "; Autogenerated by $0 on %s\n", scalar(localtime); + print "; If you edit this file and execute $0 again,\n"; + print "; your manual changes will be LOST.\n"; print "; This file should be #included in unicall.conf\n\n"; foreach my $span (@spans) { next unless $span->is_digital(); diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Users.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Users.pm index 7c9e937..dbbf9d2 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Users.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Users.pm @@ -88,6 +88,8 @@ sub generate($) { ;! Filename: @{[basename($file)]} ($file) ;! Generator: $0 ;! Creation Date: @{[scalar(localtime)]} +;! If you edit this file and execute $0 again,\n"; +;! your manual changes will be LOST.\n"; ;! [general] ; diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Xpporder.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Xpporder.pm new file mode 100644 index 0000000..0d34d93 --- /dev/null +++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Xpporder.pm @@ -0,0 +1,142 @@ +package Zaptel::Config::Gen::Xpporder; +use strict; + +use Zaptel::Config::Gen qw(is_true); +use Zaptel::Xpp; + +sub new($$$) { + my $pack = shift || die; + my $gconfig = shift || die; + my $genopts = shift || die; + my $file = $ENV{XPPORDER_CONF} || "/etc/xpp_order"; + my $self = { + FILE => $file, + GCONFIG => $gconfig, + GENOPTS => $genopts, + }; + bless $self, $pack; + return $self; +} + +# +# Returns list of xbuses sorted by the span numbers assigned +# to their XPD's. Also checks that each XBUS span numbers are sequential. +sub get_sorted_xbuses(@) { + my @spans = @_; # Verify our spans + my @xbuses = Zaptel::Xpp::xbuses; + my %xbus_of_span; + my %xbus_beginning; + my %seen_spans; + my @sorted_xbuses; + foreach my $xbus (@xbuses) { + my $last_spanno; + foreach my $xpd ($xbus->xpds) { + my $spanno = $xpd->spanno; + if(!$spanno) { + printf STDERR "%s: Is not registered. Skipping.\n", $xpd->fqn; + next; + } + $seen_spans{$spanno}++; + if($xbus_of_span{$spanno}) { + printf STDERR "%s: Span %d already seen on %s\n", + $xpd->fqn, $spanno, $xbus_of_span{$spanno}->name; + die; + } + $xbus_of_span{$spanno} = $xbus; + # Check XPD's sequential numbering + if(defined $last_spanno) { + if($last_spanno + 1 != $spanno) { + printf STDERR "%s: Bad span numbers (%d, %d)\n", + $xpd->fqn, $last_spanno, $spanno; + die; + } + } else { + $xbus_beginning{$xbus} = $spanno; + } + $last_spanno = $spanno; + } + } + foreach my $span (@spans) { + my $spanno = $span->num; + if(!defined($seen_spans{$spanno})) { + warn "Span $spanno: Ignored: Does not belong to any XPD\n"; + } + } + @sorted_xbuses = sort { $xbus_beginning{$a} <=> $xbus_beginning{$b} } @xbuses; + return @sorted_xbuses; +} + +sub generate($$$) { + my $self = shift || die; + my $file = $self->{FILE}; + my $gconfig = $self->{GCONFIG}; + my $genopts = $self->{GENOPTS}; + my @spans = @_; # Verify it's all our spans + my @xbuses = get_sorted_xbuses(@spans); + warn "Empty configuration -- no xbuses\n" unless @xbuses; + rename "$file", "$file.bak" + or $! == 2 # ENOENT (No dependency on Errno.pm) + or die "Failed to backup old config: $!\n"; + #$gconfig->dump; + print "Generating $file\n" if $genopts->{verbose}; + open(F, ">$file") || die "$0: Failed to open $file: $!\n"; + my $old = select F; + printf "# Autogenerated by $0 on %s\n", scalar(localtime); + print "# If you edit this file and execute $0 again,\n"; + print "# your manual changes will be LOST.\n"; + print <<'HEAD'; +# +# This is an optional configuration file for ordering +# Zaptel registration. +# +# It is read from /etc/xpp_order. This location +# may be overridden 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 99 (last). +# +# Astribanks with same ordering number are sorted by their +# connectors (to preserve legacy behavior). +# +# Examples: +#usb:1234 +#@usb-0000:06:02.2-2 +HEAD + foreach my $xbus (@xbuses) { + my $label = $xbus->label; + my $connector = $xbus->connector; + my $name = $xbus->name; + printf "%s\t# %s (%s)\n", $label, $connector, $name; + } + close F; + select $old; +} + +1; + +__END__ + +=head1 NAME + +Xpporder - Generate Astribank ordering information for zt_registration. + +=head1 SYNOPSIS + + use Zaptel::Config::Gen::Xpporder; + + my $cfg = new Zaptel::Config::Gen::Xpporder(\%global_config, \%genopts); + $cfg->generate; + +=head1 DESCRIPTION + +Generate the F</etc/xpp_order>. +This is the configuration for zt_registration(1). +The order is determined according to current Zaptel registration +order. + +Its location may be overriden via the environment variable F<XPPORDER_CONF>. diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zapata.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zapata.pm index e8fd733..8e0ebce 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zapata.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zapata.pm @@ -140,7 +140,9 @@ sub generate($) { print "Generating $file\n" if $genopts->{verbose}; open(F, ">$file") || die "$0: Failed to open $file: $!\n"; my $old = select F; - printf "; Autogenerated by %s on %s -- do not hand edit\n", $0, scalar(localtime); + printf "; Autogenerated by $0 on %s\n", scalar(localtime); + print "; If you edit this file and execute $0 again,\n"; + print "; your manual changes will be LOST.\n"; print <<"HEAD"; ; Zaptel Channels Configurations (zapata.conf) ; diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zaptel.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zaptel.pm index 2c30c33..892442f 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zaptel.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zaptel.pm @@ -39,11 +39,6 @@ sub gen_digital($$) { $span_crc4 = ''; $framing = 'cas'; } - my $dchan_type = 'dchan'; - if ($span->is_bri() && is_true($gconfig->{'bri_hardhdlc'})) { - $dchan_type = 'hardhdlc'; - } - $timing = ($termtype eq 'NT') ? 0 : $bri_te_last_timing++; printf "span=%d,%d,%d,%s,%s%s%s\n", $num, @@ -54,14 +49,42 @@ sub gen_digital($$) { $span_crc4, $span_yellow; printf "# termtype: %s\n", lc($termtype); - if ($gconfig->{'pri_connection_type'} eq 'PRI') { + my $dchan_type; + if ($span->is_bri()) { + my $use_bristuff = 0; + my $cfg_hardhdlc = $gconfig->{'bri_hardhdlc'}; + my $xpd = $span->xpd(); + if(!defined($cfg_hardhdlc) || $cfg_hardhdlc =~ /AUTO/i) { + # Autodetect + if(defined($xpd)) { + # Bristuff? + if(defined($xpd->dchan_hardhdlc) && !is_true($xpd->dchan_hardhdlc)) { + $use_bristuff = 1; + } + } + } elsif(!is_true($cfg_hardhdlc)) { + $use_bristuff = 1; + } + if($use_bristuff) { + $dchan_type = 'dchan'; + } else { + $dchan_type = 'hardhdlc'; + } printf "bchan=%s\n", Zaptel::Config::Gen::bchan_range($span); my $dchan = $span->dchan(); printf "$dchan_type=%d\n", $dchan->num(); - } elsif ($gconfig->{'pri_connection_type'} eq 'R2' ) { - my $idle_bits = $gconfig->{'r2_idle_bits'}; - printf "cas=%s:$idle_bits\n", Zaptel::Config::Gen::bchan_range($span); - printf "dchan=%d\n", $span->dchan()->num(); + } elsif($span->is_pri()) { + if ($gconfig->{'pri_connection_type'} eq 'PRI') { + printf "bchan=%s\n", Zaptel::Config::Gen::bchan_range($span); + my $dchan = $span->dchan(); + printf "dchan=%d\n", $dchan->num(); + } elsif ($gconfig->{'pri_connection_type'} eq 'R2' ) { + my $idle_bits = $gconfig->{'r2_idle_bits'}; + printf "cas=%s:$idle_bits\n", Zaptel::Config::Gen::bchan_range($span); + printf "dchan=%d\n", $span->dchan()->num(); + } + } else { + die "Digital span $num is not BRI, nor PRI?"; } } @@ -100,7 +123,9 @@ sub generate($$$) { print "Generating $file\n" if $genopts->{verbose}; open(F, ">$file") || die "$0: Failed to open $file: $!\n"; my $old = select F; - printf "# Autogenerated by %s on %s -- do not hand edit\n", $0, scalar(localtime); + printf "# Autogenerated by $0 on %s\n", scalar(localtime); + print "# If you edit this file and execute $0 again,\n"; + print "# your manual changes will be LOST.\n"; print <<"HEAD"; # Zaptel Configuration File # diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Params.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Params.pm index 7f6ae80..35ded3b 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Config/Params.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Config/Params.pm @@ -106,7 +106,7 @@ sub item($$) { brint_overlap => 'no', bri_sig_style => 'bri_ptmp', echo_can => 'mg2', - bri_hardhdlc => 'no', + bri_hardhdlc => 'auto', pri_connection_type => 'PRI', r2_idle_bits => '1101', 'pri_termtype' => [ 'SPAN/* TE' ], diff --git a/kernel/xpp/utils/zconf/Zaptel/Hardware.pm b/kernel/xpp/utils/zconf/Zaptel/Hardware.pm index ff7aeea..b12368b 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Hardware.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Hardware.pm @@ -8,8 +8,6 @@ package Zaptel::Hardware; # $Id$ # use strict; -use Zaptel::Hardware::USB; -use Zaptel::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 zaptel 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} = [ Zaptel::Hardware::USB->devices ]; - $self->{PCI} = [ Zaptel::Hardware::PCI->scan_devices ]; - return $self; + return if $hardware_scanned++; + foreach my $type (qw(PCI USB)) { + eval "use Zaptel::Hardware::$type"; + die $@ if $@; + $hardware_list{$type} = [ "Zaptel::Hardware::$type"->scan_devices ]; + } +} + +sub import { + Zaptel::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 Zaptel::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/kernel/xpp/utils/zconf/Zaptel/Hardware/PCI.pm b/kernel/xpp/utils/zconf/Zaptel/Hardware/PCI.pm index e9166c7..73e3e71 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Hardware/PCI.pm +++ b/kernel/xpp/utils/zconf/Zaptel/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 = Zaptel::Hardware->new($name, 'PCI'); + %{$self} = (%{$self}, %attr); bless $self, $pack; - Zaptel::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 = Zaptel::Hardware::PCI->new( - BUS_TYPE => 'PCI', PRIV_DEVICE_NAME => $dev->{PRIV_DEVICE_NAME}, VENDOR => $dev->{VENDOR}, PRODUCT => $dev->{PRODUCT}, diff --git a/kernel/xpp/utils/zconf/Zaptel/Hardware/USB.pm b/kernel/xpp/utils/zconf/Zaptel/Hardware/USB.pm index a2dc08f..4ac0c17 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Hardware/USB.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Hardware/USB.pm @@ -10,8 +10,7 @@ package Zaptel::Hardware::USB; use strict; use Zaptel::Utils; use Zaptel::Hardware; -use Zaptel::Xpp; -use Zaptel::Xpp::Xbus; +use Zaptel::Xpp::Mpp; our @ISA = qw(Zaptel::Hardware); @@ -34,46 +33,83 @@ my %usb_ids = ( 'e4e4:1160' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-modular no-firmware' }, 'e4e4:1161' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-modular USB-firmware' }, 'e4e4:1162' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-modular FPGA-firmware' }, + + # Sangoma USB FXO: + '10c4:8461' => { DRIVER => 'wanpipe', DESCRIPTION => 'Sangoma WANPIPE USB-FXO Device' }, ); $ENV{PATH} .= ":/usr/sbin:/sbin:/usr/bin:/bin"; -my @xbuses = Zaptel::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 = Zaptel::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 = Zaptel::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; - } - Zaptel::Hardware::device_detected($self, - sprintf("usb:%s", $self->{PRIV_DEVICE_NAME})); return $self; } -sub devices($) { +sub readval($) { + my $fname = shift || warn; + open(F, $fname) || warn "Failed opening '$fname': $!"; + my $val = <F>; + close F; + chomp $val; + warn "$fname is empty" unless defined $val and $val; + return $val; +} + +sub set_transport($$) { + my $pack = shift || die; + my $xbus = shift || die; + my $xbus_dir = shift; + my $transportdir = "$xbus_dir/transport"; + my $hwdev; + if(! -e "$transportdir/ep_00") { + warn "A trasnport in '$transportdir' is not USB"; + return undef; + } + my ($usbdev) = glob("$transportdir/usb_device:*"); + my $busnum; + my $devnum; + # Different kernels... + if(defined $usbdev) { # It's USB + if($usbdev =~ /.*usb_device:usbdev(\d+)\.(\d+)/) { + $busnum = $1; + $devnum = $2; + } else { + warn "Bad USB transportdir='$transportdir' usbdev='$usbdev'\n"; + } + } elsif(-d "$transportdir/usb_endpoint") { + $busnum = readval("$transportdir/busnum"); + $devnum = readval("$transportdir/devnum"); + } + my $usbname = sprintf("%03d/%03d", $busnum, $devnum); + #printf STDERR "DEBUG: %03d/%03d\n", $busnum, $devnum; + $xbus->{USB_DEVNAME} = $usbname; + $hwdev = Zaptel::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); @@ -99,7 +135,6 @@ sub devices($) { next unless defined $model; my $d = Zaptel::Hardware::USB->new( IS_ASTRIBANK => ($model->{DRIVER} eq 'xpp_usb')?1:0, - BUS_TYPE => 'USB', PRIV_DEVICE_NAME => $devname, VENDOR => $vendor, PRODUCT => $product, @@ -111,6 +146,7 @@ sub devices($) { } close F; @devices = sort usb_sorter @devices; + return @devices; } 1; diff --git a/kernel/xpp/utils/zconf/Zaptel/Span.pm b/kernel/xpp/utils/zconf/Zaptel/Span.pm index c771a26..23e02b6 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Span.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Span.pm @@ -95,7 +95,7 @@ Suggested sane framing type (e.g.: "ccs", "esf") for this type of span. =head2 yellow(), crc4() Likewise, suggestions ofr the respective fields in the span= line in -zaptel.conf for this span. +/etc/zaptel.conf for this span. =head2 signalling() @@ -147,6 +147,9 @@ my @pri_strings = ( 'Wildcard TE121', # wcte12xp 'Wildcard TE122', # wcte12xp 'T[24]XXP \(PCI\) Card ', # wct4xxp + 'R[24]T1 \(PCI\) Card', # rxt1 + 'Rhino R1T1 (E1)/PRA Card', # r1t1 + 'Rhino R1T1 (T1)/PRI Card', # r1t1 ); our $ZAPBRI_NET = 'bri_net'; diff --git a/kernel/xpp/utils/zconf/Zaptel/Xpp.pm b/kernel/xpp/utils/zconf/Zaptel/Xpp.pm index b626bfa..8473b79 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Xpp.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Xpp.pm @@ -8,6 +8,7 @@ package Zaptel::Xpp; # $Id$ # use strict; +use Zaptel::Hardware; use Zaptel::Xpp::Xbus; =head1 NAME @@ -28,12 +29,29 @@ Zaptel::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 = Zaptel::Xpp::Xbus->new($1); + push(@xbuses, $xbus); + } + closedir D; + return @xbuses; +} + # Nominal sorters for xbuses sub by_name { return $a->name cmp $b->name; @@ -72,6 +90,11 @@ sub by_type { return $res; } +sub by_xpporder { + my $cmp = $a->xpporder cmp $b->xpporder; + return $cmp if $cmp != 0; + return $a->connector cmp $b->connector; +} =head1 xbuses([sort_order]) @@ -91,6 +114,19 @@ The built in sorters are: =over +=item SORT_XPPORDER + +Sort by ordering defined in F</etc/xpp_order> file. +Astribanks can be listed in this file by their label or by +their connector string (prefixed with <@>). + +Astribanks not listed in the F<xpp_order> file are sorted +via ordering number 999 -- So they come after the Astribanks +that are listed. + +Astribanks with same ordering number (e.g: 999) are sorted +by their connector string (to preserve legacy behaviour). + =item SORT_CONNECTOR Sort by the connector string. For USB this defines the "path" to get to @@ -129,38 +165,73 @@ sub sorters { SORT_NAME => \&by_name, SORT_LABEL => \&by_label, SORT_TYPE => \&by_type, + SORT_XPPORDER => \&by_xpporder, # Aliases connector => \&by_connector, name => \&by_name, label => \&by_label, type => \&by_type, + xpporder => \&by_xpporder, ); my $which_sorter = shift || return sort keys %sorter_table; return $which_sorter if ref($which_sorter) eq 'CODE'; return $sorter_table{$which_sorter}; } +sub add_xpporder(@) { + my @xbuses = @_; + my $cfg = $ENV{XPPORDER_CONF} || '/etc/xpp_order'; + my %order; + + # Set defaults + foreach my $xbus (@xbuses) { + $xbus->{XPPORDER} = 99; + } + # Read from optional config file + if(!open(F, $cfg)) { + warn "$0: Failed opening '$cfg': $!" + unless $! == 2; # ENOENT + return; + } + my $count = 1; + while(<F>) { + chomp; + s/#.*//; + s/^\s*//; + s/\s*$//; + next unless /\S/; + $order{$_} = $count++; + } + close F; + # Overrides from config file + foreach my $xbus (@xbuses) { + my $label = $xbus->label; + my $connector = '@' . $xbus->connector; + my $val; + $val = $order{$label}; + $val = $order{$connector} unless defined $val; + $xbus->{XPPORDER} = $val if defined $val; + } +} + sub xbuses { - my $optsort = shift || 'SORT_CONNECTOR'; - my @xbuses; + my $optsort = shift || 'SORT_XPPORDER'; + my @sorted_xbuses; - opendir(D, $sysfs_astribanks) || return(); - while(my $entry = readdir D) { - next unless $entry =~ /xbus-(\d+)/; - my $xbus = Zaptel::Xpp::Xbus->new($1); - push(@xbuses, $xbus); + if(! @xbuses) { + @xbuses = Zaptel::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 (Zaptel::Xpp::xbuses('SORT_CONNECTOR')) { + foreach my $xbus (Zaptel::Xpp::xbuses) { foreach my $xpd ($xbus->xpds()) { return $xpd if $xpd->fqn eq $span->name; } diff --git a/kernel/xpp/utils/zconf/Zaptel/Xpp/Mpp.pm b/kernel/xpp/utils/zconf/Zaptel/Xpp/Mpp.pm new file mode 100644 index 0000000..44807bd --- /dev/null +++ b/kernel/xpp/utils/zconf/Zaptel/Xpp/Mpp.pm @@ -0,0 +1,221 @@ +package Zaptel::Xpp::Mpp; +# +# Written by Oron Peled <oron@actcom.co.il> +# Copyright (C) 2009, Xorcom +# This program is free software; you can redistribute and/or +# modify it under the same terms as Perl itself. +# +# $Id$ +# +use strict; +use File::Basename; +use Getopt::Std; +BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); } + +use Zaptel::Utils; + +=head1 NAME + +Zaptel::Xpp::Mpp - Perl interface to C<astribank_tool(8)> + +=head1 DESCRIPTION + +This package uses C<astribank_tool(8)> to collect information +about Astribanks via MPP (Management Processor Protocol). + +The binary default location is F</usr/sbin/astribank_tool>. It may be +overridden via module parameter C<astribank_tool=> and the +C<ASTRIBANK_TOOL> environment variable (higher priority). + +It may also be set/unset from code via the set_astribank_tool() method. + +=head1 METHODS + +=head2 mpp_addinfo() + +Called with a list of C<Zaptel::Hardware> objects and augment their +data with C<Zaptel::Xpp::Mpp> objects. + +This method is the normal external interface of this class. + +=head2 new() + +Constructor. Receive as parameter an instance of C<Zaptel::Hardware> class +and return a C<Zaptel::Xpp:Mpp> object. + +Normally, used indirectly via the mpp_addinfo() method. + +=head2 set_astribank_tool() + +Override default location of astribank_tool(8). It is legal +to set it to C<undef>. + +=head2 showinfo() + +Dump an C<Zaptel::Xpp::Mpp> object for debugging. + +=cut + +my $astribank_tool = '/usr/sbin/astribank_tool'; + +sub set_astribank_tool($$) { + my $pack = shift || die; + $pack eq 'Zaptel::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 astribank_tool_cmd($) { + my $dev = shift || die; + my $usb_top; + + # 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"; + return ($astribank_tool, '-D', "$path"); +} + +sub new($$$) { + my $pack = shift || die; + my $dev = shift || die; + my $product = $dev->product; + + return undef unless $dev->is_astribank; + return undef unless $dev->bus_type eq 'USB'; + return undef unless $product =~ /116./; + 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 @cmd = astribank_tool_cmd($dev); + my $name = $dev->priv_device_name(); + my $dbg_file = "$name"; + $dbg_file =~ s/\W/_/g; + #$dbg_file = "/tmp/twinstar-debug-$dbg_file"; + $dbg_file = "/dev/null"; + unless(open(F, "@cmd 2> '$dbg_file' |")) { + warn "Failed running '$astribank_tool': $!"; + return undef; + } + local $/ = "\n"; + local $_; + while(<F>) { + chomp; + #printf STDERR "'%s'\n", $_; + 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/^Capabilities:\s*TwinStar\s*:\s*(.+?)$//) { + my $cap = $1; + $mppinfo->{'TWINSTAR_CAPABLE'} = ($cap =~ /yes/i) ? 1 : 0; + } 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_setwatchdog($$) { + my $mppinfo = shift || die; + my $on = shift; + die "$0: Bad value '$on'" unless defined($on) && $on =~ /^[0-1]$/; + my $dev = $mppinfo->dev || die; + return undef unless defined $mppinfo->mpp_talk; + my $old = $mppinfo->tws_watchdog; + my @cmd = astribank_tool_cmd($dev); + print STDERR "DEBUG($on): '@cmd'\n"; + system(@cmd, '-w', $on); + die "Running $astribank_tool failed: $?" if $?; +} + +sub mpp_jump($) { + my $mppinfo = shift || die; + my $dev = $mppinfo->dev || die; + return undef unless defined $mppinfo->mpp_talk; + my $port = $mppinfo->twinstar_port; + $port = ($port == 1) ? 0 : 1; + die "Unknown TwinStar port" unless defined $port; + my @cmd = astribank_tool_cmd($dev); + system(@cmd, '-p', $port); + die "Running $astribank_tool failed: $?" if $?; +} + +sub mpp_addinfo($@) { + my $pack = shift || die; + my @devlist = @_; + + foreach my $dev (@devlist) { + $dev->{MPPINFO} = $pack->new($dev); + } +} + +1; diff --git a/kernel/xpp/utils/zconf/Zaptel/Xpp/Xbus.pm b/kernel/xpp/utils/zconf/Zaptel/Xpp/Xbus.pm index f87e2aa..9c73404 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Xpp/Xbus.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Xpp/Xbus.pm @@ -9,6 +9,7 @@ package Zaptel::Xpp::Xbus; # use strict; use Zaptel::Utils; +use Zaptel::Hardware; use Zaptel::Xpp::Xpd; my $proc_base = "/proc/xpp"; @@ -87,6 +88,8 @@ sub read_attrs() { if($attr eq 'STATUS') { # Some values are in all caps as well $val = uc($val); + } elsif($attr eq 'CONNECTOR') { + $val =~ s/^/@/; # Add prefix } elsif($attr eq 'LABEL') { # Fix badly burned labels. $val =~ s/[[:^print:]]/_/g; @@ -95,6 +98,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 +158,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 = "Zaptel::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/kernel/xpp/utils/zconf/Zaptel/Xpp/Xpd.pm b/kernel/xpp/utils/zconf/Zaptel/Xpp/Xpd.pm index 30a0bad..80478af 100644 --- a/kernel/xpp/utils/zconf/Zaptel/Xpp/Xpd.pm +++ b/kernel/xpp/utils/zconf/Zaptel/Xpp/Xpd.pm @@ -85,6 +85,25 @@ sub xpd_old_getoffhook($) { my %attr_missing_warned; # Prevent duplicate warnings +sub xpd_driver_getattr($$) { + my $xpd = shift || die; + my $attr = shift || die; + $attr = lc($attr); + my ($busnum, $unitnum, $subunitnum) = ($xpd->xbus->num, $xpd->unit, $xpd->subunit); + my $file = sprintf "$Zaptel::Xpp::sysfs_xpds/%02d:%1d:%1d/driver/$attr", + $busnum, $unitnum, $subunitnum; + if(!defined($file)) { + warn "$0: xpd_driver_getattr($attr) -- Missing attribute.\n" if + $attr_missing_warned{$attr}; + return undef; + } + open(F, $file) || return undef; + my $val = <F>; + close F; + chomp $val; + return $val; +} + sub xpd_getattr($$) { my $xpd = shift || die; my $attr = shift || die; @@ -162,7 +181,7 @@ sub zt_registration($$) { } sub xpds_by_spanno() { - my @xbuses = Zaptel::Xpp::xbuses("SORT_CONNECTOR"); + my @xbuses = Zaptel::Xpp::xbuses(); my @xpds = map { $_->xpds } @xbuses; @xpds = grep { $_->spanno } @xpds; @xpds = sort { $a->spanno <=> $b->spanno } @xpds; @@ -181,7 +200,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, @@ -198,6 +217,7 @@ sub new($$$$$) { if($type =~ /BRI_(NT|TE)/) { $self->{IS_BRI} = 1; $self->{TERMTYPE} = $1; + $self->{DCHAN_HARDHDLC} = $self->xpd_driver_getattr('dchan_hardhdlc'); } $self->{IS_PRI} = ($type =~ /[ETJ]1/); $self->{IS_DIGITAL} = ( $self->{IS_BRI} || $self->{IS_PRI} ); diff --git a/kernel/xpp/utils/zt_registration b/kernel/xpp/utils/zt_registration index 4ae5a66..90596b9 100755 --- a/kernel/xpp/utils/zt_registration +++ b/kernel/xpp/utils/zt_registration @@ -18,21 +18,24 @@ use Zaptel::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 = Zaptel::Xpp::sorters($sort_order); - -if(!defined $sorter) { - my @sorter_names = Zaptel::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 = Zaptel::Xpp::sorters($sort_order); + + if(!defined $sorter) { + my @sorter_names = Zaptel::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 = Zaptel::spans; foreach my $xbus (Zaptel::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->zt_registration($on); @@ -76,7 +80,7 @@ foreach my $xbus (Zaptel::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 Zaptel::Xpp manual. |