summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTzafrir Cohen <tzafrir.cohen@xorcom.com>2008-06-17 17:49:05 +0000
committerTzafrir Cohen <tzafrir.cohen@xorcom.com>2008-06-17 17:49:05 +0000
commit53894e5aa1e733a0321a69bd1b12bd794586b4ab (patch)
tree3e0d4543ad58256dd39cae60ba6de5d7a91f46a6
parent91d0a6d645e95755c4ae0f0a13919eeb213f6606 (diff)
Move the xpp utilities to the tools side.
git-svn-id: http://svn.asterisk.org/svn/dahdi/tools/trunk@4374 a0bf4364-ded3-4de4-8d8a-66a801d63aff
-rw-r--r--xpp/Makefile144
-rwxr-xr-xxpp/astribank_hook57
-rw-r--r--xpp/example_default_zaptel31
-rw-r--r--xpp/fpga_load.886
-rw-r--r--xpp/fpga_load.c1007
-rwxr-xr-xxpp/genzaptelconf1198
-rw-r--r--xpp/genzaptelconf.8326
-rw-r--r--xpp/hexfile.c567
-rw-r--r--xpp/hexfile.h123
-rwxr-xr-xxpp/lszaptel108
-rw-r--r--xpp/print_modes.c33
-rw-r--r--xpp/test_parse.c35
-rw-r--r--xpp/xpp.rules14
-rwxr-xr-xxpp/xpp_blink168
-rw-r--r--xpp/xpp_fxloader297
-rw-r--r--xpp/xpp_fxloader.usermap10
-rw-r--r--xpp/xpp_modprobe10
-rwxr-xr-xxpp/xpp_sync226
-rwxr-xr-xxpp/xpp_timing6
-rwxr-xr-xxpp/zapconf603
-rw-r--r--xpp/zaptel-helper401
-rwxr-xr-xxpp/zaptel_drivers9
-rwxr-xr-xxpp/zaptel_hardware164
-rw-r--r--xpp/zconf/Zaptel.pm68
-rw-r--r--xpp/zconf/Zaptel/Chans.pm202
-rw-r--r--xpp/zconf/Zaptel/Config/Defaults.pm56
-rw-r--r--xpp/zconf/Zaptel/Hardware.pm168
-rw-r--r--xpp/zconf/Zaptel/Hardware/PCI.pm208
-rw-r--r--xpp/zconf/Zaptel/Hardware/USB.pm116
-rw-r--r--xpp/zconf/Zaptel/Span.pm300
-rw-r--r--xpp/zconf/Zaptel/Utils.pm52
-rw-r--r--xpp/zconf/Zaptel/Xpp.pm199
-rw-r--r--xpp/zconf/Zaptel/Xpp/Line.pm95
-rw-r--r--xpp/zconf/Zaptel/Xpp/Xbus.pm118
-rw-r--r--xpp/zconf/Zaptel/Xpp/Xpd.pm123
-rwxr-xr-xxpp/zt_registration125
36 files changed, 7453 insertions, 0 deletions
diff --git a/xpp/Makefile b/xpp/Makefile
new file mode 100644
index 0000000..2e3624c
--- /dev/null
+++ b/xpp/Makefile
@@ -0,0 +1,144 @@
+PEDANTIC = -ansi -pedantic -std=c99
+
+RANLIB = ranlib
+INSTALL = install
+INSTALL_DATA = install -m 644
+
+#
+# Ugly hack to find kernel directories before/after the split
+# to kernel/user-space.
+#
+# These variables should be passed to us. But until then...
+#
+ZAPTEL_DIR ?= $(shell if [ -f "../../zaptel.h" ]; then echo "../.."; else echo "../../.."; fi)
+ZAP_KERNEL ?= $(shell if [ -d "$(ZAPTEL_DIR)/kernel" ]; then echo "$(ZAPTEL_DIR)/kernel"; else echo "$(ZAPTEL_DIR)"; fi)
+
+-include $(ZAPTEL_DIR)/makeopts
+
+INSTALL_DATA = $(INSTALL) -m 644
+
+# In 1.4 those are provided by autoconf through makeopts
+prefix ?= /usr
+datadir ?= $(prefix)/share
+mandir ?= $(datadir)/man
+INSTALL ?= install
+
+INSTALL_DATA = $(INSTALL) -m 644
+
+SBINDIR = $(prefix)/sbin
+DATADIR = $(datadir)/zaptel
+MANDIR = $(mandir)/man8
+HOTPLUG_USB_DIR = /etc/hotplug/usb
+UDEV_RULES_DIR = /etc/udev/rules.d
+PERLLIBDIR := $(shell eval `perl -V:sitelib`; echo "$$sitelib")
+PERL_DIRS := $(shell cd zconf; find * -name '[A-Z]*' -type d| xargs)
+PERL_MODS_PAT := *.pm $(PERL_DIRS:%=%/*.pm)
+PERL_MODS := $(shell cd zconf; echo $(PERL_MODS_PAT))
+
+XPD_FIRMWARE = $(wildcard ../firmwares/*.hex)
+XPD_INIT_DATA = $(XPD_FIRMWARE) init_fxo_modes
+XPD_INIT = $(wildcard ../init_card_?_*) xpp_fxloader
+
+# Variables that should be defined above, but need sane defaults:
+# FIXME: Are those values really sane?
+HOSTCC ?= $(CC)
+
+ifeq (,$(PBX_LIBUSB))
+# No PBX_LIBUSB? Maybe we compile against zaptel-1.2
+# Let's make a poor man detection of libusb
+PBX_LIBUSB = $(shell if [ -r /usr/include/usb.h ]; then echo 1; else echo 0; fi)
+endif
+
+WCTDM=$(shell for i in $(ZAP_KERNEL)/wctdm.c $(ZAP_KERNEL)/fxo_modes.h; do [ -f "$$i" ] && echo "$$i"; done)
+
+CFLAGS = -g -Wall $(EXTRA_CFLAGS)
+
+%.8: %
+ pod2man --section 8 $^ > $@ || $(RM) $@
+PERL_SCRIPTS = \
+ zt_registration \
+ xpp_sync \
+ lszaptel \
+ xpp_blink \
+ zapconf \
+ zaptel_hardware \
+ #
+
+PERL_MANS = $(PERL_SCRIPTS:%=%.8)
+
+TARGETS = init_fxo_modes print_modes perlcheck
+PROG_INSTALL = genzaptelconf
+MAN_INSTALL = $(PROG_INSTALL:%=%.8)
+ifeq (1,$(PBX_LIBUSB))
+TARGETS += libhexfile.a fpga_load test_parse
+PROG_INSTALL += fpga_load
+endif
+ifneq (,$(PERLLIBDIR))
+PROG_INSTALL += $(PERL_SCRIPTS)
+TARGETS += $(PERL_MANS)
+endif
+
+all: $(TARGETS)
+
+docs: $(PERL_MANS)
+
+install: all
+ $(INSTALL) -d $(DESTDIR)$(SBINDIR)
+ $(INSTALL) $(PROG_INSTALL) $(DESTDIR)$(SBINDIR)/
+ $(INSTALL) -d $(DESTDIR)$(DATADIR)
+ $(INSTALL_DATA) $(XPD_INIT_DATA) $(DESTDIR)$(DATADIR)/
+ $(INSTALL) $(XPD_INIT) $(DESTDIR)$(DATADIR)/
+ $(INSTALL) -d $(DESTDIR)$(MANDIR)
+ $(INSTALL_DATA) $(MAN_INSTALL) $(DESTDIR)$(MANDIR)/
+ $(INSTALL) -d $(DESTDIR)$(HOTPLUG_USB_DIR)
+ $(INSTALL_DATA) xpp_fxloader.usermap $(DESTDIR)$(HOTPLUG_USB_DIR)/
+ # for backward compatibility and for hotplug users:
+ ln -sf $(DATADIR)/xpp_fxloader $(DESTDIR)$(HOTPLUG_USB_DIR)/
+ $(INSTALL) -d $(DESTDIR)$(UDEV_RULES_DIR)
+ $(INSTALL_DATA) xpp.rules $(DESTDIR)$(UDEV_RULES_DIR)/
+ifneq (,$(PERLLIBDIR))
+ $(INSTALL) -d $(DESTDIR)$(PERLLIBDIR)
+ for i in $(PERL_DIRS); \
+ do \
+ $(INSTALL) -d "$(DESTDIR)$(PERLLIBDIR)/$$i"; \
+ done
+ for i in $(PERL_MODS); \
+ do \
+ $(INSTALL_DATA) "zconf/$$i" "$(DESTDIR)$(PERLLIBDIR)/$$i"; \
+ done
+endif
+
+libhexfile.a: hexfile.o
+ $(AR) cru $@ $^
+ $(RANLIB) $@
+
+fpga_load: fpga_load.o libhexfile.a
+ $(CC) -L. -o $@ $@.o $(EXTRA_LIBS) -lhexfile -lusb
+
+fpga_load.o: CFLAGS+=-D_GNU_SOURCE # We use memrchr()
+
+hexfile.o: hexfile.c hexfile.h
+ $(CC) $(CFLAGS) $(PEDANTIC) -c $<
+
+test_parse.o: test_parse.c hexfile.h
+ $(CC) $(CFLAGS) $(PEDANTIC) -c $<
+
+test_parse: test_parse.o libhexfile.a
+ $(CC) -L. -o $@ $@.o $(EXTRA_LIBS) -lhexfile -lusb
+
+print_modes: print_modes.c wctdm_fxomodes.h
+ $(HOSTCC) -o $@ $(CFLAGS) $<
+
+wctdm_fxomodes.h: $(WCTDM)
+ @echo Building FXO modes from: $(WCTDM)
+ perl -n -e 'print if (/^static struct fxo_mode {$$/ .. /};$$/)' $(WCTDM) >$@ || rm -f $@
+
+init_fxo_modes: print_modes
+ ./$< >$@
+
+perlcheck: $(PERL_SCRIPTS)
+ for i in $^; do perl -I./zconf -c $$i || exit 1; done
+ touch $@
+
+clean:
+ $(RM) *.o $(TARGETS)
diff --git a/xpp/astribank_hook b/xpp/astribank_hook
new file mode 100755
index 0000000..8dd13c9
--- /dev/null
+++ b/xpp/astribank_hook
@@ -0,0 +1,57 @@
+#! /bin/sh
+
+# This is an example of an Astribank device hook. The xpp.rules file
+# calls /usr/share/zaptel/astribank_hook after a new Astribank is ready
+# and after and old Astribank device has been destroyed.
+#
+
+set -e
+
+me=`basename $0`
+INIT_DIR=`dirname $0`
+XPP_BASE=/proc/xpp
+export XPP_BASE
+LOGGER="logger -s -t $me"
+
+ZAPTEL_BOOT_DEBIAN=${ZAPTEL_BOOT_DEBIAN:-/etc/default/zaptel}
+ZAPTEL_BOOT_FEDORA=${ZAPTEL_BOOT_FEDORA:-/etc/sysconfig/zaptel}
+
+# read default configuration from /etc/default/zaptel
+if [ -r $ZAPTEL_BOOT_DEBIAN ]; then . $ZAPTEL_BOOT_DEBIAN; fi
+if [ -r $ZAPTEL_BOOT_FEDORA ]; then . $ZAPTEL_BOOT_FEDORA; fi
+
+if [ "$ASTRIBANK_HOOK_DISABLED" != '' ]; then
+ $LOGGER -p kern.info "Exiting... ASTRIBANK_HOOK_DISABLED"
+ exit 0
+fi
+
+# 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/astribank_hook_$XBUS_NAME"
+
+# Maybe add some logging
+#$LOGGER -p kern.info "$0: $ACTION: $*."
+
+case "$ACTION" in
+add)
+ # An Astribank was added and is initialized and ready.
+ # Put your shell commands bellow
+ :
+ ;;
+remove)
+ # An Astribank was Removed.
+ # Put your shell commands bellow
+ # You should not access /proc/xpp/... or run related utilities
+ # like xpp_sync, since this is likely to cause very bad race
+ # conditions during driver removal.
+ :
+ ;;
+*)
+ ;;
+esac
+
+# Maybe add some logging
+#$LOGGER -p kern.info "$0: Done: $ACTION: $*."
diff --git a/xpp/example_default_zaptel b/xpp/example_default_zaptel
new file mode 100644
index 0000000..f60b651
--- /dev/null
+++ b/xpp/example_default_zaptel
@@ -0,0 +1,31 @@
+# Disables hotplug firmware loading
+#XPP_HOTPLUG_DISABLED=yes
+#
+# Disables udev hook called when an astribank is added and ready
+# or removed.
+#ASTRIBANK_HOOK_DISABLED=yes
+
+# Setup for XPP PRI. This allows to have fixed settings:
+# 1. The variable XPP_PRI_SETUP contains a whitespace separated list of
+# port specifications.
+# 2. Each port specification contains a match expression, a '=' and
+# a setting string.
+# 2. Match expressions may be:
+# - CONNECTOR/usb..../XPD-nn To identify by physical connector
+# - NUM/XBUS-mm/XPD-nn To identify by bus number
+# 4. Match expressions may contain "wildcards" (which are translated
+# internally to regular expressions):
+# * matches zero or more characters.
+# ? matches one charater
+# 5. The list of matches is scanned from beginning to end. First match wins.
+# 6. The list implicitly contains an 'NUM/*=TE,E1' catch all default, appended
+# to its end.
+# 7. The setting string is composed of comma separated settings. Valid
+# settings are:
+# - NT or TE
+# - E1 or T1 or J1
+#
+XPP_PRI_SETUP='
+ CONNECTOR/usb-0000:00:1d.7-1/XPD-01=NT,E1
+ NUM/*/XPD-03=NT,E1
+ '
diff --git a/xpp/fpga_load.8 b/xpp/fpga_load.8
new file mode 100644
index 0000000..dd21db0
--- /dev/null
+++ b/xpp/fpga_load.8
@@ -0,0 +1,86 @@
+.TH "FPGA_LOAD" "8" "16 April 2006" "" ""
+
+.SH NAME
+fpga_load \- Xorcom Astribank (xpp) firmware tool
+.SH SYNOPSIS
+
+.B fpga_load
+[\fB-g\fR] [\fB-r\fR] [\fB-v\fR] \fB-D \fR{/proc/bus/usb|/dev/bus/usb}/\fIBUS/DEV\fR
+
+.B fpga_load
+[\fB-g\fR] [\fB-v\fR] \fB-D \fR{/proc/bus/usb|/dev/bus/usb}/\fIBUS/DEV\fR \fB-I \fIfirmware.hex\fR [\fB-b \fIdump.bin\fR] [\fB-i\fR]
+
+.B fpga_load -h
+
+.SH DESCRIPTION
+.B fpga_load
+loads the FPGA firmware to the Xorcom Astribank device.
+The syntax resembles that of fxload(8).
+
+.SH OPTIONS
+.B -b
+.I dump.bin
+.RS
+Before writing firmware, bump the processed binary file to
+.I dump.bin\fR.
+.RE
+
+.B -D
+.I DEVICE
+.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 -r
+.RS
+Reset the Astribank and renumerate its USB connection to power on product ID.
+.RE
+
+.B -g
+.RS
+Dump all eeprom data to standard output.
+.RE
+
+.B -I
+.I fireware_file
+.RS
+The firmware file to write to the device.
+.RE
+
+.B -i
+.RS
+Show information about the firmware file (valid only with \fB-I\fR option).
+Example:
+.PP
+ ./FPGA_1151.hex: Version=3297 Checksum=58270
+
+In particular, the calculated checksum should match the output of \fIsum(1)\fR
+on the binary firmware file generated by the \fB-b\fR option.
+.RE
+
+.B -v
+.RS
+Increase verbosity. May be used multiple times.
+.RE
+
+.B -h
+.RS
+Displays usage message.
+.RE
+
+.SH SEE ALSO
+fxload(8), lsusb(8)
+
+.SH AUTHOR
+This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com> .
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common-licenses/GPL.
diff --git a/xpp/fpga_load.c b/xpp/fpga_load.c
new file mode 100644
index 0000000..7102501
--- /dev/null
+++ b/xpp/fpga_load.c
@@ -0,0 +1,1007 @@
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdint.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <usb.h>
+#include "hexfile.h"
+
+static const char rcsid[] = "$Id$";
+
+#define ERR(fmt, arg...) do { \
+ if(verbose >= LOG_ERR) \
+ fprintf(stderr, "%s: ERROR (%d): " fmt, \
+ progname, __LINE__, ## arg); \
+ } while(0);
+#define INFO(fmt, arg...) do { \
+ if(verbose >= LOG_INFO) \
+ fprintf(stderr, "%s: " fmt, \
+ progname, ## arg); \
+ } while(0);
+#define DBG(fmt, arg...) do { \
+ if(verbose >= LOG_DEBUG) \
+ fprintf(stderr, "%s: DBG: " fmt, \
+ progname, ## arg); \
+ } while(0);
+
+static int verbose = LOG_WARNING;
+static char *progname;
+static int disconnected = 0;
+
+#define MAX_HEX_LINES 10000
+#define PACKET_SIZE 512
+#define EEPROM_SIZE 16
+#define LABEL_SIZE 8
+#define TIMEOUT 5000
+
+
+/* My device parameters */
+#define MY_EP_OUT 0x04
+#define MY_EP_IN 0x88
+
+#define FPGA_EP_OUT 0x02
+#define FPGA_EP_IN 0x86
+
+/* USB firmware types */
+#define USB_11xx 0
+#define USB_FIRMWARE_II 1
+
+#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 astribank_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 */
+} astribank_types[] = {
+ TYPE_ENTRY(USB_11xx, 1, 0, 4, MY_EP_OUT, MY_EP_IN,
+ FPGA_EP_OUT,
+ MY_EP_OUT,
+ FPGA_EP_IN,
+ MY_EP_IN),
+ TYPE_ENTRY(USB_FIRMWARE_II, 2, 1, 2, MY_EP_OUT, MY_EP_IN,
+ MY_EP_OUT,
+ MY_EP_IN),
+};
+#undef TYPE_ENTRY
+
+enum fpga_load_packet_types {
+ PT_STATUS_REPLY = 0x01,
+ PT_DATA_PACKET = 0x01,
+#ifdef XORCOM_INTERNAL
+ PT_EEPROM_SET = 0x04,
+#endif
+ PT_EEPROM_GET = 0x08,
+ PT_RENUMERATE = 0x10,
+ PT_RESET = 0x20,
+ PT_BAD_COMMAND = 0xAA
+};
+
+struct myeeprom {
+ uint8_t source;
+ uint16_t vendor;
+ uint16_t product;
+ uint8_t release_major;
+ uint8_t release_minor;
+ uint8_t reserved;
+ uint8_t label[LABEL_SIZE];
+} PACKED;
+
+struct fpga_packet_header {
+ struct {
+ uint8_t op;
+ } PACKED header;
+ union {
+ struct {
+ uint16_t seq;
+ uint8_t status;
+ } PACKED status_reply;
+ struct {
+ uint16_t seq;
+ uint8_t reserved;
+ uint8_t data[ZERO_SIZE];
+ } PACKED data_packet;
+ struct {
+ struct myeeprom data;
+ } PACKED eeprom_set;
+ struct {
+ struct myeeprom data;
+ } PACKED eeprom_get;
+ } d;
+} PACKED;
+
+enum fpga_load_status {
+ FW_FAIL_RESET = 1,
+ FW_FAIL_TRANS = 2,
+ FW_TRANS_OK = 4,
+ FW_CONFIG_DONE = 8
+};
+
+struct my_usb_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;
+ struct myeeprom eeprom;
+ const struct astribank_type *abtype;
+};
+
+const char *load_status2str(enum fpga_load_status s)
+{
+ switch(s) {
+ case FW_FAIL_RESET: return "FW_FAIL_RESET";
+ case FW_FAIL_TRANS: return "FW_FAIL_TRANS";
+ case FW_TRANS_OK: return "FW_TRANS_OK";
+ case FW_CONFIG_DONE: return "FW_CONFIG_DONE";
+ default: return "UNKNOWN";
+ }
+}
+
+/* return 1 if:
+ * - str has a number
+ * - It is larger than 0
+ * - It equals num
+ */
+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(char *buf, unsigned int len, uint16_t item, usb_dev_handle *handle)
+{
+ char tmp[BUFSIZ];
+ int ret;
+
+ if (!item)
+ return 0;
+ ret = usb_get_string_simple(handle, item, tmp, BUFSIZ);
+ if (ret <= 0)
+ return ret;
+ return snprintf(buf, len, "%s", tmp);
+}
+
+void my_usb_device_cleanup(struct my_usb_device *mydev)
+{
+ assert(mydev != NULL);
+ if(!mydev->handle) {
+ return; /* Nothing to do */
+ }
+ if(!disconnected) {
+ if(usb_release_interface(mydev->handle, mydev->abtype->my_interface_num) != 0) {
+ ERR("Releasing interface: usb: %s\n", usb_strerror());
+ }
+ }
+ if(usb_close(mydev->handle) != 0) {
+ ERR("Closing device: usb: %s\n", usb_strerror());
+ }
+ disconnected = 1;
+ mydev->handle = NULL;
+}
+
+static void show_device_info(const struct my_usb_device *mydev)
+{
+ const struct myeeprom *eeprom;
+ uint8_t data[LABEL_SIZE + 1];
+
+ assert(mydev != NULL);
+ eeprom = &mydev->eeprom;
+ memset(data, 0, LABEL_SIZE + 1);
+ memcpy(data, eeprom->label, LABEL_SIZE);
+ printf("USB Firmware Type: [%s]\n", mydev->abtype->name);
+ printf("USB iManufacturer: [%s]\n", mydev->iManufacturer);
+ printf("USB iProduct: [%s]\n", mydev->iProduct);
+ printf("USB iSerialNumber: [%s]\n", mydev->iSerialNumber);
+ printf("EEPROM Source: 0x%02X\n", eeprom->source);
+ printf("EEPROM Vendor: 0x%04X\n", eeprom->vendor);
+ printf("EEPROM Product: 0x%04X\n", eeprom->product);
+ printf("EEPROM Release: %d.%03d\n", eeprom->release_major, eeprom->release_minor);
+ printf("EEPROM Label: HEX(%02X,%02X,%02X,%02X,%02X,%02X,%02X,%02X) [%s]\n",
+ data[0], data[1], data[2], data[3],
+ data[4], data[5], data[6], data[7], data);
+}
+
+void dump_packet(const char *msg, const char *buf, int len)
+{
+ int i;
+
+ for(i = 0; i < len; i++)
+ INFO("%s: %2d> 0x%02X\n", msg, i, (uint8_t)buf[i]);
+}
+
+int send_usb(const char *msg, struct my_usb_device *mydev, struct fpga_packet_header *phead, int len, int timeout)
+{
+ char *p = (char *)phead;
+ int ret;
+
+ if(verbose >= LOG_DEBUG)
+ dump_packet(msg, p, len);
+ if(mydev->my_ep_out & USB_ENDPOINT_IN) {
+ ERR("send_usb called with an input endpoint 0x%x\n", mydev->my_ep_out);
+ return -EINVAL;
+ }
+ ret = usb_bulk_write(mydev->handle, mydev->my_ep_out, p, 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: %s\n", mydev->my_ep_out, usb_strerror());
+ dump_packet("send_usb[ERR]", p, len);
+ } else {
+ disconnected = 1;
+ my_usb_device_cleanup(mydev);
+ }
+ return ret;
+ } else if(ret != len) {
+ ERR("bulk_write to endpoint 0x%x short write: %s\n", mydev->my_ep_out, usb_strerror());
+ dump_packet("send_usb[ERR]", p, len);
+ return -EFAULT;
+ }
+ return ret;
+}
+
+int recv_usb(const char *msg, struct my_usb_device *mydev, char *buf, size_t len, int timeout)
+{
+ int ret;
+
+ if(mydev->my_ep_in & USB_ENDPOINT_OUT) {
+ ERR("recv_usb called with an output endpoint 0x%x\n", mydev->my_ep_in);
+ return -EINVAL;
+ }
+ ret = usb_bulk_read(mydev->handle, mydev->my_ep_in, buf, len, timeout);
+ if(ret < 0) {
+ ERR("bulk_read from endpoint 0x%x failed: %s\n", mydev->my_ep_in, usb_strerror());
+ return ret;
+ }
+ if(verbose >= LOG_DEBUG)
+ dump_packet(msg, buf, ret);
+ return ret;
+}
+
+#ifdef XORCOM_INTERNAL
+int eeprom_set(struct my_usb_device *mydev, const struct myeeprom *eeprom)
+{
+ int ret;
+ int len;
+ char buf[PACKET_SIZE];
+ struct fpga_packet_header *phead = (struct fpga_packet_header *)buf;
+
+ DBG("%s Start...\n", __FUNCTION__);
+ assert(mydev != NULL);
+ phead->header.op = PT_EEPROM_SET;
+ memcpy(&phead->d.eeprom_set.data, eeprom, EEPROM_SIZE);
+ len = sizeof(phead->d.eeprom_set) + sizeof(phead->header.op);
+ ret = send_usb("eeprom_set[W]", mydev, phead, len, TIMEOUT);
+ if(ret < 0)
+ return ret;
+ ret = recv_usb("eeprom_set[R]", mydev, buf, sizeof(buf), TIMEOUT);
+ if(ret <= 0)
+ return ret;
+ phead = (struct fpga_packet_header *)buf;
+ if(phead->header.op == PT_BAD_COMMAND) {
+ ERR("Firmware rejected PT_EEPROM_SET command\n");
+ return -EINVAL;
+ } else if(phead->header.op != PT_EEPROM_SET) {
+ ERR("Got unexpected reply op=%d\n", phead->header.op);
+ return -EINVAL;
+ }
+ return 0;
+}
+#endif
+
+int eeprom_get(struct my_usb_device *mydev)
+{
+ int ret;
+ int len;
+ char buf[PACKET_SIZE];
+ struct fpga_packet_header *phead = (struct fpga_packet_header *)buf;
+ struct myeeprom *eeprom;
+
+ assert(mydev != NULL);
+ eeprom = &mydev->eeprom;
+ DBG("%s Start...\n", __FUNCTION__);
+ phead->header.op = PT_EEPROM_GET;
+ len = sizeof(phead->header.op); /* warning: sending small packet */
+ ret = send_usb("eeprom_get[W]", mydev, phead, len, TIMEOUT);
+ if(ret < 0)
+ return ret;
+ ret = recv_usb("eeprom_get[R]", mydev, buf, sizeof(buf), TIMEOUT);
+ if(ret <= 0)
+ return ret;
+ phead = (struct fpga_packet_header *)buf;
+ if(phead->header.op == PT_BAD_COMMAND) {
+ ERR("PT_BAD_COMMAND\n");
+ return -EINVAL;
+ } else if(phead->header.op != PT_EEPROM_GET) {
+ ERR("Got unexpected reply op=%d\n", phead->header.op);
+ return -EINVAL;
+ }
+ memcpy(eeprom, &phead->d.eeprom_get.data, EEPROM_SIZE);
+ return 0;
+}
+
+int send_hexline(struct my_usb_device *mydev, struct hexline *hexline, int seq)
+{
+ int ret;
+ int len;
+ uint8_t *data;
+ char buf[PACKET_SIZE];
+ struct fpga_packet_header *phead = (struct fpga_packet_header *)buf;
+ enum fpga_load_status status;
+
+ assert(mydev != NULL);
+ assert(hexline != NULL);
+ if(hexline->d.content.header.tt != TT_DATA) {
+ DBG("Non data record %d type = %d\n", seq, hexline->d.content.header.tt);
+ return 0;
+ }
+ len = hexline->d.content.header.ll; /* don't send checksum */
+ data = hexline->d.content.tt_data.data;
+ phead->header.op = PT_DATA_PACKET;
+ phead->d.data_packet.seq = seq;
+ phead->d.data_packet.reserved = 0x00;
+ memcpy(phead->d.data_packet.data, data, len);
+ len += sizeof(hexline->d.content.header);
+ DBG("%04d+\r", seq);
+ ret = send_usb("hexline[W]", mydev, phead, len, TIMEOUT);
+ if(ret < 0)
+ return ret;
+ ret = recv_usb("hexline[R]", mydev, buf, sizeof(buf), TIMEOUT);
+ if(ret <= 0)
+ return ret;
+ DBG("%04d-\r", seq);
+ phead = (struct fpga_packet_header *)buf;
+ if(phead->header.op != PT_STATUS_REPLY) {
+ ERR("Got unexpected reply op=%d\n", phead->header.op);
+ dump_packet("hexline[ERR]", buf, ret);
+ return -EINVAL;
+ }
+ status = (enum fpga_load_status)phead->d.status_reply.status;
+ switch(status) {
+ case FW_TRANS_OK:
+ case FW_CONFIG_DONE:
+ break;
+ case FW_FAIL_RESET:
+ case FW_FAIL_TRANS:
+ ERR("status reply %s (%d)\n", load_status2str(status), status);
+ dump_packet("hexline[ERR]", buf, ret);
+ return -EPROTO;
+ default:
+ ERR("Unknown status reply %d\n", status);
+ dump_packet("hexline[ERR]", buf, ret);
+ return -EPROTO;
+ }
+ return 0;
+}
+
+//. returns > 0 - ok, the number of lines sent
+//. returns < 0 - error number
+int send_splited_hexline(struct my_usb_device *mydev, struct hexline *hexline, int seq, uint8_t maxwidth)
+{
+ struct hexline *extraline;
+ int linessent = 0;
+ int allocsize;
+ int extra_offset = 0;
+ unsigned int this_line = 0;
+ uint8_t bytesleft = 0;
+
+ assert(mydev != NULL);
+ if(!hexline) {
+ ERR("Bad record %d type = %d\n", seq, hexline->d.content.header.tt);
+ return -EINVAL;
+ }
+ bytesleft = hexline->d.content.header.ll;
+ // split the line into several lines
+ while (bytesleft > 0) {
+ int status;
+ this_line = (bytesleft >= maxwidth) ? maxwidth : bytesleft;
+ allocsize = sizeof(struct hexline) + this_line + 1;
+ // generate the new line
+ if((extraline = (struct hexline *)malloc(allocsize)) == NULL) {
+ ERR("Not enough memory for spliting the lines\n" );
+ return -EINVAL;
+ }
+ memset(extraline, 0, allocsize);
+ extraline->d.content.header.ll = this_line;
+ extraline->d.content.header.offset = hexline->d.content.header.offset + extra_offset;
+ extraline->d.content.header.tt = hexline->d.content.header.tt;
+ memcpy( extraline->d.content.tt_data.data, hexline->d.content.tt_data.data+extra_offset, this_line);
+ status = send_hexline(mydev, extraline, seq+linessent );
+ // cleanups
+ free(extraline);
+ extra_offset += this_line;
+ bytesleft -= this_line;
+ if (status)
+ return status;
+ linessent++;
+ }
+ return linessent;
+}
+
+int match_usb_device_identity(const struct usb_config_descriptor *config_desc,
+ const struct astribank_type *ab)
+{
+ struct usb_interface *interface;
+ struct usb_interface_descriptor *iface_desc;
+
+ if(config_desc->bNumInterfaces <= ab->my_interface_num)
+ return 0;
+ interface = &config_desc->interface[ab->my_interface_num];
+ iface_desc = interface->altsetting;
+
+ return iface_desc->bInterfaceClass == 0xFF &&
+ iface_desc->bInterfaceNumber == ab->my_interface_num &&
+ iface_desc->bNumEndpoints == ab->num_endpoints;
+}
+
+const struct astribank_type *my_usb_device_identify(const char devpath[], struct my_usb_device *mydev)
+{
+ struct usb_device_descriptor *dev_desc;
+ struct usb_config_descriptor *config_desc;
+ int i;
+
+ assert(mydev != NULL);
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+ mydev->dev = dev_of_path(devpath);
+ if(!mydev->dev) {
+ ERR("Bailing out\n");
+ return 0;
+ }
+ dev_desc = &mydev->dev->descriptor;
+ config_desc = mydev->dev->config;
+ for(i = 0; i < sizeof(astribank_types)/sizeof(astribank_types[0]); i++) {
+ if(match_usb_device_identity(config_desc, &astribank_types[i])) {
+ DBG("Identified[%d]: interfaces=%d endpoints=%d: \"%s\"\n",
+ i,
+ astribank_types[i].num_interfaces,
+ astribank_types[i].num_endpoints,
+ astribank_types[i].name);
+ return &astribank_types[i];
+ }
+ }
+ return NULL;
+}
+
+int my_usb_device_init(const char devpath[], struct my_usb_device *mydev, const struct astribank_type *abtype)
+{
+ 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;
+ int ret;
+ int i;
+
+ assert(mydev != NULL);
+ usb_init();
+ usb_find_busses();
+ usb_find_devices();
+ mydev->dev = dev_of_path(devpath);
+ if(!mydev->dev) {
+ ERR("Bailing out\n");
+ return 0;
+ }
+ mydev->handle = usb_open(mydev->dev);
+ if(!mydev->handle) {
+ ERR("Failed to open usb device '%s/%s': %s\n", mydev->dev->bus->dirname, mydev->dev->filename, usb_strerror());
+ return 0;
+ }
+ if(usb_claim_interface(mydev->handle, abtype->my_interface_num) != 0) {
+ ERR("usb_claim_interface: %s\n", usb_strerror());
+ return 0;
+ }
+ dev_desc = &mydev->dev->descriptor;
+ config_desc = mydev->dev->config;
+ if (!config_desc) {
+ ERR("usb interface without a configuration\n");
+ return 0;
+ }
+ interface = &config_desc->interface[abtype->my_interface_num];
+ iface_desc = interface->altsetting;
+ endpoint = iface_desc->endpoint;
+ mydev->is_usb2 = (endpoint->wMaxPacketSize == 512);
+ for(i = 0; i < iface_desc->bNumEndpoints; i++, endpoint++) {
+ if(endpoint->bEndpointAddress != abtype->endpoints[i]) {
+ ERR("Wrong endpoint 0x%X (at index %d)\n", endpoint->bEndpointAddress, i);
+ return 0;
+ }
+ if(endpoint->bEndpointAddress == MY_EP_OUT || endpoint->bEndpointAddress == MY_EP_IN) {
+ if(endpoint->wMaxPacketSize > PACKET_SIZE) {
+ ERR("Endpoint #%d wMaxPacketSize too large (%d)\n", i, endpoint->wMaxPacketSize);
+ return 0;
+ }
+ }
+ }
+ mydev->abtype = abtype;
+ mydev->my_ep_in = abtype->my_ep_in;
+ mydev->my_ep_out = abtype->my_ep_out;
+ ret = get_usb_string(mydev->iManufacturer, BUFSIZ, dev_desc->iManufacturer, mydev->handle);
+ ret = get_usb_string(mydev->iProduct, BUFSIZ, dev_desc->iProduct, mydev->handle);
+ ret = get_usb_string(mydev->iSerialNumber, BUFSIZ, dev_desc->iSerialNumber, mydev->handle);
+ ret = get_usb_string(mydev->iInterface, BUFSIZ, iface_desc->iInterface, mydev->handle);
+ INFO("ID=%04X:%04X Manufacturer=[%s] Product=[%s] SerialNumber=[%s] Interface=[%s]\n",
+ dev_desc->idVendor,
+ dev_desc->idProduct,
+ mydev->iManufacturer,
+ mydev->iProduct,
+ mydev->iSerialNumber,
+ mydev->iInterface);
+ if(usb_clear_halt(mydev->handle, mydev->my_ep_out) != 0) {
+ ERR("Clearing output endpoint: %s\n", usb_strerror());
+ return 0;
+ }
+ if(usb_clear_halt(mydev->handle, mydev->my_ep_in) != 0) {
+ ERR("Clearing input endpoint: %s\n", usb_strerror());
+ return 0;
+ }
+ return 1;
+}
+
+int renumerate_device(struct my_usb_device *mydev, enum fpga_load_packet_types pt)
+{
+ char buf[PACKET_SIZE];
+ struct fpga_packet_header *phead = (struct fpga_packet_header *)buf;
+ int ret;
+
+ assert(mydev != NULL);
+ DBG("Renumerating with 0x%X\n", pt);
+ phead->header.op = pt;
+ ret = send_usb("renumerate[W]", mydev, phead, 1, TIMEOUT);
+ if(ret < 0 && ret != -ENODEV)
+ return ret;
+#if 0
+ /*
+ * FIXME: we count on our USB firmware to reset the device... should we?
+ */
+ ret = usb_reset(mydev->handle);
+ if(ret < 0) {
+ ERR("usb_reset: %s\n", usb_strerror());
+ return -ENODEV;
+ }
+#endif
+ return 0;
+}
+
+/*
+ * Returns: true on success, false on failure
+ */
+int fpga_load(struct my_usb_device *mydev, const struct hexdata *hexdata)
+{
+ unsigned int i;
+ unsigned int j = 0;
+ int ret;
+ int finished = 0;
+ const char *v = hexdata->version_info;
+
+ v = (v[0]) ? v : "Unknown";
+ assert(mydev != NULL);
+ INFO("FPGA_LOAD (version %s)\n", v);
+ /*
+ * i - is the line number
+ * j - is the sequence number, on USB 2, i=j, but on
+ * USB 1 send_splited_hexline may increase the sequence
+ * number, as it needs
+ */
+ 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(mydev->is_usb2) {
+ if((ret = send_hexline(mydev, hexline, i)) != 0) {
+ perror("Failed sending hexline");
+ return 0;
+ }
+ } else {
+ if((ret = send_splited_hexline(mydev, hexline, j, 60)) < 0) {
+ perror("Failed sending hexline (splitting did not help)");
+ return 0;
+ }
+ j += ret;
+ }
+ }
+ DBG("Finished...\n");
+ return 1;
+}
+
+#include <getopt.h>
+
+void usage()
+{
+ fprintf(stderr, "Usage: %s -D {/proc/bus/usb|/dev/bus/usb}/<bus>/<dev> [options...]\n", progname);
+ fprintf(stderr, "\tOptions:\n");
+ fprintf(stderr, "\t\t[-r] # Reset the device\n");
+ fprintf(stderr, "\t\t[-b <binfile>] # Output to <binfile>\n");
+ fprintf(stderr, "\t\t[-I <hexfile>] # Input from <hexfile>\n");
+ fprintf(stderr, "\t\t[-H <hexfile>] # Output to <hexfile> ('-' is stdout)\n");
+ fprintf(stderr, "\t\t[-i] # Show hexfile information\n");
+ fprintf(stderr, "\t\t[-g] # Get eeprom from device\n");
+ fprintf(stderr, "\t\t[-v] # Increase verbosity\n");
+#ifdef XORCOM_INTERNAL
+ fprintf(stderr, "\t\t[-C srC byte] # Set Address sourCe (default: C0)\n");
+ fprintf(stderr, "\t\t[-V vendorid] # Set Vendor id on device\n");
+ fprintf(stderr, "\t\t[-P productid] # Set Product id on device\n");
+ fprintf(stderr, "\t\t[-R release] # Set Release. 2 dot separated decimals\n");
+ fprintf(stderr, "\t\t[-L label] # Set label.\n");
+#endif
+ exit(1);
+}
+
+static void parse_report_func(int level, const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ if(level <= verbose)
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+}
+
+#ifdef XORCOM_INTERNAL
+static void eeprom_fill(struct myeeprom *myeeprom,
+ const char vendor[],
+ const char product[],
+ const char release[],
+ const char label[],
+ const char source[])
+{
+ // FF: address source is from device. C0: from eeprom
+ if (source)
+ myeeprom->source = strtoul(source, NULL, 0);
+ else
+ myeeprom->source = 0xC0;
+ if(vendor)
+ myeeprom->vendor = strtoul(vendor, NULL, 0);
+ if(product)
+ myeeprom->product = strtoul(product, NULL, 0);
+ if(release) {
+ int release_major = 0;
+ int release_minor = 0;
+
+ sscanf(release, "%d.%d", &release_major, &release_minor);
+ myeeprom->release_major = release_major;
+ myeeprom->release_minor = release_minor;
+ }
+ if(label) {
+ /* padding */
+ memset(myeeprom->label, 0, LABEL_SIZE);
+ memcpy(myeeprom->label, label, strlen(label));
+ }
+}
+#endif
+
+int main(int argc, char *argv[])
+{
+ const struct astribank_type *abtype;
+ struct my_usb_device mydev;
+ const char *devpath = NULL;
+ const char *binfile = NULL;
+ const char *inhexfile = NULL;
+ const char *outhexfile = NULL;
+ struct hexdata *hexdata = NULL;
+ int opt_reset = 0;
+ int opt_info = 0;
+ int opt_read_eeprom = 0;
+ int opt_output_width = 0;
+ int output_is_set = 0;
+#ifdef XORCOM_INTERNAL
+ int opt_write_eeprom = 0;
+ char *vendor = NULL;
+ char *source = NULL;
+ char *product = NULL;
+ char *release = NULL;
+ char *label = NULL;
+ const char options[] = "rib:D:ghH:I:vw:C:V:P:R:S:";
+#else
+ const char options[] = "rib:D:ghH:I:vw:";
+#endif
+ int ret = 0;
+
+ progname = argv[0];
+ assert(sizeof(struct fpga_packet_header) <= PACKET_SIZE);
+ assert(sizeof(struct myeeprom) == EEPROM_SIZE);
+ while (1) {
+ int c;
+
+ c = getopt (argc, argv, options);
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'D':
+ devpath = optarg;
+ if(output_is_set++) {
+ ERR("Cannot set -D. Another output option is already selected\n");
+ return 1;
+ }
+ break;
+ case 'r':
+ opt_reset = 1;
+ break;
+ case 'i':
+ opt_info = 1;
+ break;
+ case 'b':
+ binfile = optarg;
+ if(output_is_set++) {
+ ERR("Cannot set -b. Another output option is already selected\n");
+ return 1;
+ }
+ break;
+ case 'g':
+ opt_read_eeprom = 1;
+ break;
+ case 'H':
+ outhexfile = optarg;
+ if(output_is_set++) {
+ ERR("Cannot set -H. Another output option is already selected\n");
+ return 1;
+ }
+ break;
+ case 'I':
+ inhexfile = optarg;
+ break;
+#ifdef XORCOM_INTERNAL
+ case 'V':
+ vendor = optarg;
+ break;
+ case 'C':
+ source = optarg;
+ break;
+ case 'P':
+ product = optarg;
+ break;
+ case 'R':
+ release = optarg;
+ break;
+ case 'S':
+ label = optarg;
+ {
+ const char GOOD_CHARS[] =
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "-_.";
+ int len = strlen(label);
+ int goodlen = strspn(label, GOOD_CHARS);
+
+ if(len > LABEL_SIZE) {
+ ERR("Label too long (%d > %d)\n", len, LABEL_SIZE);
+ usage();
+ }
+ if(goodlen != len) {
+ ERR("Bad character in label number (pos=%d)\n", goodlen);
+ usage();
+ }
+ }
+ break;
+#endif
+ case 'w':
+ opt_output_width = strtoul(optarg, NULL, 0);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'h':
+ default:
+ ERR("Unknown option '%c'\n", c);
+ usage();
+ }
+ }
+
+ if (optind != argc) {
+ usage();
+ }
+ if(inhexfile) {
+#ifdef XORCOM_INTERNAL
+ if(vendor || product || release || label || source ) {
+ ERR("The -I option is exclusive of -[VPRSC]\n");
+ return 1;
+ }
+#endif
+ parse_hexfile_set_reporting(parse_report_func);
+ hexdata = parse_hexfile(inhexfile, MAX_HEX_LINES);
+ if(!hexdata) {
+ ERR("Bailing out\n");
+ exit(1);
+ }
+ if(opt_info) {
+ printf("%s: Version=%s Checksum=%d\n",
+ inhexfile, hexdata->version_info,
+ bsd_checksum(hexdata));
+ }
+ if(binfile) {
+ dump_binary(hexdata, binfile);
+ return 0;
+ }
+ if(outhexfile) {
+ if(opt_output_width)
+ dump_hexfile2(hexdata, outhexfile, opt_output_width);
+ else
+ dump_hexfile(hexdata, outhexfile);
+ return 0;
+ }
+ }
+#ifdef XORCOM_INTERNAL
+ else if(vendor || product || release || label || source ) {
+ if(outhexfile) {
+ FILE *fp;
+
+ if(strcmp(outhexfile, "-") == 0)
+ fp = stdout;
+ else if((fp = fopen(outhexfile, "w")) == NULL) {
+ perror(outhexfile);
+ return 1;
+ }
+ memset(&mydev.eeprom, 0, sizeof(struct myeeprom));
+ eeprom_fill(&mydev.eeprom, vendor, product, release, label, source);
+ gen_hexline((uint8_t *)&mydev.eeprom, 0, sizeof(mydev.eeprom), fp);
+ gen_hexline(NULL, 0, 0, fp); /* EOF */
+ return 0;
+ }
+ }
+#endif
+ if(!devpath) {
+ ERR("Missing device path\n");
+ usage();
+ }
+ DBG("Startup %s\n", devpath);
+ if((abtype = my_usb_device_identify(devpath, &mydev)) == NULL) {
+ ERR("Bad device. Does not match our types.\n");
+ usage();
+ }
+ INFO("FIRMWARE: %s (type=%d)\n", abtype->name, abtype->type_code);
+ if(!my_usb_device_init(devpath, &mydev, abtype)) {
+ ERR("Failed to initialize USB device '%s'\n", devpath);
+ ret = -ENODEV;
+ goto dev_err;
+ }
+ ret = eeprom_get(&mydev);
+ if(ret < 0) {
+ ERR("Failed reading eeprom\n");
+ goto dev_err;
+ }
+#ifdef XORCOM_INTERNAL
+ if(vendor || product || release || label || source ) {
+ eeprom_fill(&mydev.eeprom, vendor, product, release, label, source);
+ opt_write_eeprom = 1;
+ opt_read_eeprom = 1;
+ }
+#endif
+ if(opt_read_eeprom) {
+ show_device_info(&mydev);
+ }
+ if(hexdata) {
+ if (!mydev.is_usb2)
+ INFO("Warning: working on a low end USB1 backend\n");
+ if(!fpga_load(&mydev, hexdata)) {
+ ERR("FPGA loading failed\n");
+ ret = -ENODEV;
+ goto dev_err;
+ }
+ ret = renumerate_device(&mydev, PT_RENUMERATE);
+ if(ret < 0) {
+ ERR("Renumeration failed: errno=%d\n", ret);
+ goto dev_err;
+ }
+ }
+#ifdef XORCOM_INTERNAL
+ else if(opt_write_eeprom) {
+ if(abtype->type_code == USB_FIRMWARE_II) {
+ ERR("No EEPROM burning command in %s. Use fxload for that\n",
+ abtype->name);
+ goto dev_err;
+ }
+ ret = eeprom_set(&mydev, &mydev.eeprom);
+ if(ret < 0) {
+ ERR("Failed writing eeprom: %s\n", strerror(-ret));
+ goto dev_err;
+ }
+ printf("------- RESULTS -------\n");
+ show_device_info(&mydev);
+ }
+#endif
+ if(opt_reset) {
+ DBG("Reseting to default\n");
+ ret = renumerate_device(&mydev, PT_RESET);
+ if(ret < 0) {
+ ERR("Renumeration to default failed: errno=%d\n", ret);
+ goto dev_err;
+ }
+ }
+ DBG("Exiting\n");
+dev_err:
+ my_usb_device_cleanup(&mydev);
+ return ret;
+}
diff --git a/xpp/genzaptelconf b/xpp/genzaptelconf
new file mode 100755
index 0000000..163f595
--- /dev/null
+++ b/xpp/genzaptelconf
@@ -0,0 +1,1198 @@
+#! /bin/bash
+
+# genzaptelconf: generate as smartly as you can:
+# /etc/zaptel.conf
+# /etc/asterisk/zapata-channels.conf (to be #include-d into zapata.conf)
+# update:
+# With '-M' /etc/modules (list of modules to load)
+#
+# Copyright (C) 2005 by Xorcom <support@xorcom.com>
+#
+# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+#
+# If you have any technical questions, contact
+# Tzafrir Cohen <tzafrir.cohen@xorcom.com>
+#
+
+# The script uses a number of bash-specific features
+# TODO: either ditch them or convert to perl
+# Don't override variables here.
+# Override them in /etc/default/zaptel (debian) or /etc/sysconfig/zaptel
+# (redhat/centos)
+
+# /etc/default/zaptel may override the following variables
+VERSION=0.5.10
+rcsid='$Id$'
+lc_country=us
+# set to: ls, ks or gs for (Loopstart, Kewlstart and GroundStart)
+# on FXS channels (FXO signalling).
+fxs_default_start=ks
+base_exten=6000
+# If set: no context changes are made in zapata-channels.conf
+#context_manual=yes
+context_lines=from-pstn # context into which PSTN calls go
+context_phones=from-internal # context for internal phones calls.
+# The two below apply to input and output ports of the Xorcom Astribank:
+context_input=astbank-input
+context_output=astbank-output # useless, but helps marking the channels :-)
+# TODO: what about PRI/BRI?
+# If set: no group changes are made in zapata-channels.conf
+#group_manual=yes
+group_phones=5 # group for phones
+group_lines=0 # group for lines
+# Set fxs_immediate to 'yes' to make all FXS lines answer immediately.
+fxs_immediate=no
+
+ZAPCONF_FILE=${ZAPCONF_FILE:-/etc/zaptel.conf}
+ZAPCONF_FILE_SYSTEM=$ZAPCONF_FILE
+ZAPATA_FILE=${ZAPATA_FILE:-/etc/asterisk/zapata-channels.conf}
+ZAPSCAN_FILE=${ZAPSCAN_FILE:-/etc/asterisk/zapscan.conf}
+ZAPTEL_BOOT_DEBIAN=${ZAPTEL_BOOT_DEBIAN:-/etc/default/zaptel}
+ZAPTEL_BOOT_FEDORA=${ZAPTEL_BOOT_FEDORA:-/etc/sysconfig/zaptel}
+MODLIST_FILE=/etc/modules
+MODLIST_FILE_FEDORA=/etc/sysconfig/zaptel
+exten_base_dir=/etc/asterisk/extensions-phones.d
+exten_defs_file=/etc/asterisk/extensions-defs.conf
+# perl utilities:
+xpp_sync=/usr/sbin/xpp_sync
+zt_registration=/usr/sbin/zt_registration
+# how long to wait for /dev/zap/ctl to appear? (seconds)
+DEVZAP_TIMEOUT=${DEVZAP_TIMEOUT:-20}
+ZTCFG=${ZTCFG:-/sbin/ztcfg}
+# BRI/PRI spans will be in an additional per-span group whose number
+# is SPAN_GROUP_BASE + span-number
+SPAN_GROUP_BASE=10
+# set to "yes" to make BRI NT spans set overlapdial (handy for ISDN phones
+# and other devices).
+brint_overlap=no
+
+# a temporary directory to store whatever we need to remember.
+#
+# The main loop of genconf is run in a sub-process.
+tmp_dir=
+
+# A list of all modules:
+# - the list of modules which will be probed (in this order) if -d is used
+# - The module that will be deleted from /etc/modules , if -d -M is used
+ALL_MODULES="wct4xxp wcte12xp wcte11xp wct1xxp wanpipe tor2 torisa qozap vzaphfc zaphfc ztgsm wctdm24xxp wctdm opvxa1200 wcfxo pciradio wcusb xpp_usb"
+
+# The name of the variable in /etc/sysconfig/zaptel into which to set
+# the list of detected modules.
+modules_var=MODULES
+# On SuSE with the rpm package:
+#modules_var=ZAPTEL_MODULES
+
+# What signalling to give to ZapBRI channels?
+# bri: bri_net; bri_cpe (Bristuffed Asterisk. No multi- support)
+# bri_ptmpi: bri_net_ptmp; bri_cpe_ptmp (Bristuffed Asterisk, multi- support)
+# pri: pri_net; pri_cpe (Recent Asterisk. Experimental)
+#ZAPBRI_SIGNALLING=bri
+ZAPBRI_SIGNALLING=bri_ptmp
+#ZAPBRI_SIGNALLING=pri
+zapconf_def_termtype=te
+
+# A command to stop / start asterisk. Must support parameters "start"
+# and "stop" . This is the executable:
+ZAPCONF_ASTERISK_SCRIPT=/etc/init.d/asterisk
+#
+# Should you need to pass extra arguments:
+ZAPCONF_ASTERISK_CMD=$ZAPCONF_ASTERISK_SCRIPT
+
+# read default configuration from /etc/default/zaptel
+if [ -r $ZAPTEL_BOOT_DEBIAN ]; then . $ZAPTEL_BOOT_DEBIAN; fi
+if [ -r $ZAPTEL_BOOT_FEDORA ]; then . $ZAPTEL_BOOT_FEDORA; fi
+
+if [ ! -x "$ZTCFG" ]; then
+ # Work around a bug in the rpm package: ztcfg should be in
+ # /sbin as it may be required for starting a network interface
+ if [ -x /usr/sbin/ztcfg ]; then
+ ZTCFG=/usr/sbin/ztcfg
+ else
+ echo >&2 "ztcfg is not on found, do you have zaptel properly installed?"
+ exit_cleanup 1
+ fi
+fi
+
+XPP_SYNC=auto # sync mode. can be set to '0' or '1' or HOST explicitly.
+
+# it is safe to use -c twice: the last one will be used.
+ztcfg_cmd="$ZTCFG -c $ZAPCONF_FILE"
+
+# work around a bug (that was already fixed) in the installer:
+if [ "$lc_country" = '' ]; then lc_country=us; fi
+
+force_stop_ast=no
+do_detect=no
+do_unload=no
+do_module_list=no
+verbose=no
+do_restart=yes
+fxsdisable=no
+do_gen_zapscan=no
+
+span_te_timing_counter=1
+
+case "$ZAPBRI_SIGNALLING" in
+bri) ZAPBRI_NET=bri_net; ZAPBRI_CPE=bri_cpe ;;
+pri) ZAPBRI_NET=pri_net; ZAPBRI_CPE=pri_cpe ;;
+bri_ptmp) ZAPBRI_NET=bri_net_ptmp; ZAPBRI_CPE=bri_cpe_ptmp ;;
+*)
+ die "Incorrect value for ZAPBRI_SIGNALLING ($ZAPBRI_SIGNALLING). Abortring"
+ ;;
+esac
+
+die() {
+ echo "$@" >&2
+ exit_cleanup 1
+}
+
+say() {
+ if [ "$verbose" = no ]; then
+ return
+ fi
+ echo "$@" >&2
+}
+
+# exit (exit value is the optional $1), and clean up after us
+exit_cleanup() {
+ if [ -d "$tmp_dir" ]; then
+ # don't fail but don't hide error if directory is not
+ # empty
+ rmdir "$tmp_dir" || :
+ fi
+ exit $1
+}
+
+# Wait for udev to generate /dev/zap/ctl, if needed:
+wait_for_zapctl() {
+ # if device file already exists, or if zaptel has failed to load:
+ # no point waiting.
+ if [ -c /dev/zap/ctl ] || ! grep -q zaptel /proc/modules ; then
+ return
+ fi
+ say "Waiting for /dev/zap/ctl to be generated"
+ devzap_found=0
+ for i in `seq $DEVZAP_TIMEOUT`; do
+ sleep 1
+ if [ -c /dev/zap/ctl ]; then
+ devzap_found=1
+ break
+ fi
+ done
+ if [ "$devzap_found" != 1 ]; then
+ say "Still no /dev/zap/ctl after $devzap_timeout seconds."
+ echo >&2 "No /dev/zap/ctl: cannot run ztcfg. Aborting."
+ fi
+}
+
+run_ztcfg() {
+ # Run ztcfg itself
+ if [ "$verbose" = no ]; then
+ $ztcfg_cmd "$@"
+ else
+ say "Reconfiguring identified channels"
+ $ztcfg_cmd -vv "$@"
+ fi
+}
+
+update_module_list_debian() {
+ say "Updating Debian modules list $MODLIST_FILE."
+ del_args=`for i in $ALL_MODULES ztdummy
+ do
+ echo "$i" | sed s:.\*:-e\ '/^&/d':
+ done`
+ add_args=`for i in $*
+ do
+ echo "$i" | sed s:.\*:-e\ '\$a&':
+ done`
+
+ sed -i.bak $del_args "$MODLIST_FILE"
+ for i in $*
+ do
+ echo "$i"
+ done >> "$MODLIST_FILE"
+}
+
+update_module_list_fedora() {
+ say "Updating modules list in zaptel init config $MODLIST_FILE_FEDORA."
+ sed -i.bak -e "/^$modules_var=/d" "$MODLIST_FILE_FEDORA"
+ echo "$modules_var=\"$*\"" >> "$MODLIST_FILE_FEDORA"
+}
+
+update_module_list() {
+ if [ -f "$MODLIST_FILE" ]; then
+ update_module_list_debian "$@"
+ elif [ -f "$MODLIST_FILE_FEDORA" ]; then
+ update_module_list_fedora "$@"
+ else
+ die "Can't find a modules list to update. Tried: $MODLIST_FILE, $MODLIST_FILE_FEDORA. Aborting"
+ fi
+}
+
+
+
+zap_reg_xpp() {
+ if [ ! -d /proc/xpp ]; then return; fi
+
+ # Get a list of connected Astribank devices, sorted by the name of
+ # the USB connector. That order is rather arbitrary, but will not
+ # change without changes to the cabling.
+ xbusses=`sed -e '/STATUS=connected/!d' -e 's/ *STATUS=.*//' -e 's/ *CONNECTOR=//' /proc/xpp/xbuses | sort -t: -k 2 | cut -d: -f1`
+ say "Zaptel registration order:"
+ say "$xbusses"
+
+ # get a list of XPDs that were not yet registered as zaptel spans.
+ # this will be the case if you set the parameter zap_autoreg=0 to
+ # the module xpp
+ # Append /dev/null to provide a valid file name in case of an empty pattern.
+ xbusses_pattern=`echo $xbusses| sed -e 's|XBUS-[0-9]*|/proc/xpp/&/XPD-*/zt_registration|g'`' /dev/null'
+ xpds_to_register=`grep -l 0 $xbusses_pattern`
+ for file in $xpds_to_register; do
+ echo 1 >$file
+ done
+}
+
+# Initialize the Xorcom Astribank (xpp/)
+xpp_startup() {
+ # do nothing if the module xpp was not loaded, or if no
+ # Astribanks connected:
+ if [ ! -d /proc/xpp ]; then return 0; fi
+ if ! grep -q 'STATUS=connected' /proc/xpp/xbuses; then return 0; fi
+
+ echo "Waiting for Astribank devices to initialize:"
+ cat /proc/xpp/XBUS-[0-9]*/waitfor_xpds 2>/dev/null || true
+
+ # overriding locales for the above two, as perl can be noisy
+ # when locales are missing.
+ # No register all the devices if they didn't auto-register:
+ LC_ALL=C $zt_registration on
+
+ # this one could actually be run after ztcfg:
+ LC_ALL=C $xpp_sync "$XPP_SYNC"
+}
+
+
+
+usage() {
+ program=`basename $0`
+
+ echo >&2 "$program: generate zaptel.conf and zapata.conf"
+ echo >&2 "(version $VERSION, $rcsid)"
+ echo >&2 "usage:"
+ echo >&2 " $program [-sRdv] [-m k|l|g] [-c <country_code>] [-e <base_exten>] [-F]"
+ echo >&2 " $program [-sRdv] -l"
+ echo >&2 " $program -su"
+ echo >&2 " $program -h (this screen)"
+ echo >&2 ""
+ echo >&2 "Options:"
+ echo >&2 " -c CODE: set the country code (default: $lc_country)"
+ echo >&2 " -e NUM: set the base extension number (default: $base_exten)"
+ echo >&2 " -F: Don't print FXSs in zapata.conf"
+ echo >&2 " -l: output a list of detected channels instead of zaptel.conf"
+ echo >&2 " -d: Perform hardware detection"
+ echo >&2 " -u: Unload zaptel modules (will not restart Asterisk)."
+ echo >&2 " -v: verbose"
+ echo >&2 " -s: Stop Asterisk before running, and start it at the end."
+ echo >&2 " -R: Don't restart asterisk in the end."
+ echo >&2 " -z: also generate zapscan.conf for the asterisk GUI."
+}
+
+# print /etc/asterisk/zapscan.conf for the Asterisk-GUI
+# $1: port type. Currently only fxs/fxo . Note that this is the type, and
+# not the signalling (which would be the fxo fir an fxs port.
+# $2: port number. Probably a range of ports is also allowed.
+print_zapscan_port () {
+ if [ "$do_gen_zapscan" != 'yes' ]; then return 0; fi
+
+ echo "
+[$2]
+port=$1
+" >>$zapscan_file
+}
+
+# $1: channel number
+print_pattern() {
+ local astbank_type=''
+ local reset_values=""
+ OPTIND=1
+ while getopts 'a:' arg
+ do
+ case "$arg" in
+ a) case "$OPTARG" in input|output) astbank_type=$OPTARG;;esac ;;
+ esac
+ done
+ shift $(( $OPTIND-1 ))
+
+
+ local chan=$1
+ local sig=$2 #fxs/fxo
+ local mode=$3
+ local method
+
+ if [ "$sig" = 'fxs' ]; then
+ # Coutries in which we need to use busydetect:
+ # United Arab Emirats, Israel, Slovenia
+ case "$lc_country" in
+ ae|il|si)
+ method=ls
+ ;;
+ *)
+ method=ks
+ ;;
+ esac
+ else
+ method="$fxs_default_start"
+ fi
+ case "$sig" in
+ fxs) sig_name=FXO;;
+ fxo) sig_name=FXS;;
+ esac
+ case "$mode" in
+ list)
+ echo $chan $sig_name $astbank_type;;
+ files)
+ # sadly, both input ports and output ports go into the same span as
+ # the FXS ports. Thus we need to separate between them. See also
+ # the zapata.conf section:
+
+ echo ";;; line=\"$line\"" >> $zapata_file
+
+ if [ "$astbank_type" != '' ];
+ then echo "# astbanktype: $astbank_type" >>$zaptel_file;
+ fi
+ echo "${sig}$method=$chan" >>$zaptel_file
+ # zap2amp will rewrite those from zaptel.conf and hints there
+ if [ "$fxsdisable" = 'yes' ] && [ "$sig" = 'fxo' ]; then return; fi
+
+ echo "signalling=${sig}_$method" >>$zapata_file
+ if [ "$sig" = 'fxo' ]
+ then
+ # to preconfigure channel 1's extension to 550, set
+ # chan_1_exten=550
+ # in, e.g, /etc/default/zaptel
+ var_name=`echo chan_${chan}_exten`
+ cfg_exten=`echo ${!var_name} | tr -d -c 0-9`
+ var_name=`echo chan_${chan}_vmbox`
+ cfg_vmbox=`echo ${!var_name} | tr -d -c 0-9`
+ var_name=`echo chan_${chan}_cntxt`
+ cfg_cntxt=`echo ${!var_name} | tr -d -c 0-9`
+
+ if [ "$cfg_exten" = '' ]
+ then # No extension number set for this channel
+ exten=$(($chan+$base_exten))
+ else # use the pre-configured extension number
+ exten=$cfg_exten
+ fi
+ # is there any real need to set 'mailbox=' ?
+ if [ "x$cfg_vmbox" = x ]
+ then # No extension number set for this channel
+ vmbox=$exten
+ else # use the pre-configured extension number
+ vmbox=$cfg_vmbox
+ fi
+ echo "callerid=\"Channel $chan\" <$exten>" >> $zapata_file
+ reset_values="$reset_values callerid"
+ echo "mailbox=$exten" >> $zapata_file
+ reset_values="$reset_values mailbox"
+ if [ "$group_manual" != "yes" ]
+ then
+ echo "group=$group_phones" >> $zapata_file
+ reset_values="$reset_values group"
+ fi
+ if [ "$context_manual" != "yes" ]
+ then
+ if [ "$astbank_type" != '' ];
+ then
+ context_var_name=context_$astbank_type
+ echo context=${!context_var_name} >> $zapata_file
+ else
+ echo "context=$context_phones" >> $zapata_file
+ fi
+ reset_values="$reset_values context"
+ fi
+ else # this is an FXO (trunk/phone: FXO signalling)
+ # we have may have set it. So reset it:
+ echo "callerid=asreceived" >> $zapata_file
+ if [ "$group_manual" != "yes" ]
+ then
+ echo "group=$group_lines" >> $zapata_file
+ fi
+ if [ "$context_manual" != "yes" ]
+ then
+ echo "context=$context_lines" >> $zapata_file
+ reset_values="$reset_values context"
+ fi
+ if [ "$lc_country" = 'uk' ]
+ then
+ echo "cidsignalling=v23" >> $zapata_file
+ case $line in
+ *WCFXO*) echo "cidstart=history" >> $zapata_file;;
+ *) echo "cidstart=polarity" >> $zapata_file;;
+ esac
+ reset_values="$reset_values cidsignalling cidstart"
+ fi
+ case "$line" in
+ *XPP_FXO*)
+ if [ "$xpp_fxo_rxgain" != '' ]; then
+ echo "rxgain=$xpp_fxo_rxgain" >> $zapata_file
+ reset_values="$reset_values rxgain"
+ fi
+ ;;
+ esac
+ # if kewlstart is not used, busydetect has to be employed:
+ if [ "$method" = 'ls' ]
+ then
+ echo 'busydetect=yes' >> $zapata_file
+ reset_values="$reset_values busydetect"
+ fi
+ fi
+
+ if [ "$astbank_type" = 'input' ] || \
+ ( [ "$fxs_immediate" = 'yes' ] && [ "$sig" = "fxo" ] )
+ then
+ echo 'immediate=yes' >> $zapata_file
+ reset_values="$reset_values immediate"
+ fi
+ echo "channel => $chan" >> $zapata_file
+ reset_zapata_entry $zapata_file $reset_values
+ echo "" >> $zapata_file
+
+ print_zapscan_port "$sig" "$chan"
+ ;;
+ esac
+
+}
+
+# the number of channels from /proc/zaptel
+# must always print a number as its output.
+count_proc_zap_lines() {
+ # if zaptel is not loaded there are 0 channels:
+ if [ ! -d /proc/zaptel ]; then echo '0'; return; fi
+
+ (
+ for file in `echo /proc/zaptel/* |grep -v '\*'`
+ do sed -e 1,2d $file # remove the two header lines
+ done
+ ) | wc -l # the total number of lines
+}
+
+load_modules() {
+ say "Test Loading modules:"
+ for i in $ALL_MODULES
+ do
+ lines_before=`count_proc_zap_lines`
+ args="${i}_args"
+ eval "args=\$$args"
+ # a module is worth listing if it:
+ # a. loaded successfully, and
+ # b. added channels lines under /proc/zaptel/*
+ if /sbin/modprobe $i $args 2> /dev/null
+ then
+ check=0
+ case "$i" in
+ xpp_usb) check=`grep 'STATUS=connected' 2>/dev/null /proc/xpp/xbuses | wc -l` ;;
+ *) if [ $lines_before -lt `count_proc_zap_lines` ]; then check=1; fi ;;
+ esac
+ if [ "$check" != 0 ]
+ then
+ probed_modules="$probed_modules $i"
+ say " ok $i $args"
+ else
+ say " - $i $args"
+ rmmod $i
+ fi
+ else
+ say " - $i $args"
+ fi
+ done
+}
+
+# recursively unload a module and its dependencies, if possible.
+# where's modprobe -r when you need it?
+# inputs: module to unload.
+# returns: the result from
+unload_module() {
+ module="$1"
+ line=`lsmod 2>/dev/null | grep "^$1 "`
+ if [ "$line" = '' ]; then return; fi # module was not loaded
+
+ set -- $line
+ # $1: the original module, $2: size, $3: refcount, $4: deps list
+ mods=`echo $4 | tr , ' '`
+ # xpp_usb keeps the xpds below busy, and hence must be removed
+ # before them:
+ case "$module" in xpd_*) mods="xpp_usb $mods";; esac
+ for mod in $mods; do
+ # run in a subshell, so it won't step over our vars:
+ (unload_module $mod)
+ # TODO: the following is probably the error handling we want:
+ # if [ $? != 0 ]; then return 1; fi
+ done
+ rmmod $module
+}
+
+unload_modules() {
+ say "Unloading zaptel modules:"
+ unload_module zaptel
+ say ''
+}
+
+# sleep a while until the xpp modules fully register
+wait_for_xpp() {
+ if [ -d /proc/xpp ]
+ then
+ # wait for the XPDs to register:
+ # TODO: improve error reporting and produce a messagee here
+ cat /proc/xpp/XBUS-*/waitfor_xpds 2>/dev/null >/dev/null || true
+ fi
+}
+
+detect() {
+ unload_modules
+ temporary_zapconf save
+ load_modules
+ temporary_zapconf restore
+ modlist="$probed_modules"
+ modlist="$(echo $modlist)" # clean spaces
+ if [ "$do_module_list" = yes ]
+ then
+ update_module_list "$modlist"
+ fi
+ if echo $modlist | grep -q xpp_usb; then wait_for_xpp; fi
+}
+
+# The module configuration generated by zaptel includes a totally useless
+# automatic run of ztcfg after modules loading. As a workaround for that,
+# we provide an empty zaptel.conf temporarily.
+#
+# At hardware detection time we shouldn't really touch zaptel.conf . So we
+# must keep a copy of it somewhere.
+#
+# I use ZAPCONF_FILE_SYSTEM rather than ZAPCONF_FILE, as the bogus modprobe
+# entries will not be initiated by us.
+#
+# ZAPCONF_FILE_TMP is a "global", as it needs to be preserved between the
+# call to 'save' and to 'restore'
+ZAPCONF_FILE_TMP=
+temporary_zapconf() {
+ case "$1" in
+ save)
+ say "Temporarily moving zaptel.conf aside to work around broken modprobe.conf"
+ ZAPCONF_FILE_TMP=`mktemp /tmp/genzaptelconf-zaptel.conf-XXXXXX` || die "Error creating temporary zaptel.conf"
+ if [ -f $ZAPCONF_FILE_SYSTEM ]; then
+ cp -a $ZAPCONF_FILE_SYSTEM $ZAPCONF_FILE_TMP
+ fi
+ echo -n >$ZAPCONF_FILE_SYSTEM
+ ;;
+ restore)
+ # restore original zaptel.conf:
+ if [ "$ZAPCONF_FILE_TMP" = '' ]; then return; fi
+ mv $ZAPCONF_FILE_TMP $ZAPCONF_FILE_SYSTEM
+ ZAPCONF_FILE_TMP=''
+ ;;
+ esac
+}
+
+# run after the channel's entry. Used to reset all the values
+reset_zapata_entry() {
+ conf_file="$1"; shift
+
+ for arg in "$@"; do
+ case "$arg" in
+ busydetect) echo "$arg=no" >>$conf_file;;
+ context) echo "$arg=default" >>$conf_file;;
+ immediate) echo "$arg=no" >>$conf_file;;
+ overlapdial) echo "$arg=no" >>$conf_file;;
+ rxgain) echo "$arg=0" >>$conf_file;;
+ txgain) echo "$arg=0" >>$conf_file;;
+ *) echo "$arg=" >>$conf_file;;
+ esac
+ done
+}
+
+
+# we need to preserve the permissions of existing configurations files.
+# However we also don't want to be left with incomplete configurations in
+# case we are stopped in the middle. Thus we create a temporary file and
+# mv it to the original file only when done.
+#
+# This function gives the temporary file the permissions of the original,
+# or some sane defaults.
+#
+# The temporary file is generated here, and ths its name is passed through
+# a variable whose name is passed as a parameter (new_var).
+#
+# $1: orig_cfg_file
+# $2: new_var
+gen_tmp_conf() {
+ orig_cfg_file=$1
+ new_var=$2
+
+ # assign to new_var by reference:
+ eval $new_var=`mktemp /tmp/genzaptelconf-conf-XXXXXX` \
+ || die "Unable to create temporary config file for $orig_cfg_file"
+ if [ -r "$orig_cfg_file" ]; then
+ chown --reference="$orig_cfg_file" ${!new_var} 2>/dev/null
+ chmod --reference="$orig_cfg_file" ${!new_var} 2>/dev/null
+ else
+ chmod 644 ${!new_var}
+ fi
+}
+
+# Extract information from one digital channel (one line in a /proc/zaptel
+# file). Information is saved to $tmp_dir/span_foo variables.
+# FIXME: detection should move to when we know the number of channels
+# and hence can tell between an E1 and a T1.
+detect_digital_channel() {
+ line="$1"
+ chan_num="$2"
+ span_num="$3"
+
+ # PRI/BRI channel
+ # Rather than identifying cards by the header line, we identify
+ # them by the channel names. This is shorter.
+ # This also allows us to count the channel numbers and check if
+ # we have a E1 span, a T1 span or a BRI span.
+
+ # span_lastd is always the one before last
+ # channel. span_bchan is the last:
+ echo $chan_num >$tmp_dir/span_end
+
+ if [ "`cat $tmp_dir/span_begin`" != "-1" ]
+ then
+ return #we already configured this span.
+ fi
+
+ # Now we need to give initial configuration to the span
+ echo $chan_num >$tmp_dir/span_begin
+ echo $span_num >$tmp_dir/span_num
+
+ case "$line" in
+ *ZTHFC*/* | *ztqoz*/* | *XPP_BRI_*/*)
+ # BRI channel
+ echo 'ccs' >$tmp_dir/span_framing
+ echo 'euroisdn' >$tmp_dir/span_switchtype
+ if [ "`cat $tmp_dir/span_termtype`" = 'nt' 2>/dev/null ]
+ then
+ echo $ZAPBRI_NET >$tmp_dir/span_signalling
+ else
+ echo $ZAPBRI_CPE >$tmp_dir/span_signalling
+ fi
+ ;;
+ *ztgsm*/*)
+ # Junghanns's GSM cards.
+ echo 'ccs' >$tmp_dir/span_framing
+ #Does this mean anything?
+ echo 'gsm' >$tmp_dir/span_signalling
+ ;;
+ *TE[24]/* | *WCT1/* | *Tor2/* | *TorISA/* | *WP[TE]1/* | \
+ *R[124]T1/* | *XPP_[TEJ]1_*)
+ # FIXME: handle cwain around here.
+ # name: *cwain[12]/* . Always E1.
+
+ # PRI span (E1/T1)
+ echo 'esf' >$tmp_dir/span_framing
+ echo 'b8zs' >$tmp_dir/span_coding
+ echo 'national' >$tmp_dir/span_switchtype
+ if [ "`cat $tmp_dir/span_termtype`" = 'nt' 2>/dev/null ]
+ then
+ echo pri_net >$tmp_dir/span_signalling
+ else
+ echo pri_cpe >$tmp_dir/span_signalling
+ fi
+ # an example of country-specific setup. This is probably not accurate
+ # Contributions are welcome
+ case "$lc_country" in
+ nl)
+ # (Just an example for per-country info)
+ echo 'ccs' >$tmp_dir/span_framing
+ echo 'ami' >$tmp_dir/span_coding
+ #echo 'crc4' >$tmp_dir/span_yellow
+ #echo 'euroisdn' >$tmp_dir/span_switchtype
+ #echo 'pri_cpe' >$tmp_dir/span_signalling
+ ;;
+ il|de|au)
+ echo 'ccs' >$tmp_dir/span_framing
+ echo 'hdb3' >$tmp_dir/span_coding
+ echo 'crc4' >$tmp_dir/span_yellow
+ echo 'euroisdn' >$tmp_dir/span_switchtype
+ ;;
+ cl)
+ echo 'ccs' >$tmp_dir/span_framing
+ echo 'hdb3' >$tmp_dir/span_coding
+ #echo 'crc4' >$tmp_dir/span_yellow
+ echo 'national' >$tmp_dir/span_switchtype
+ ;;
+ esac
+ ;;
+ esac
+}
+
+# read information from the $tmp_dir/span_foo files and generate
+# configuration for the span and its channels.
+write_digital_config() {
+ # if the current file we checked was not of a digital span, we have
+ # nothing to do:
+ if [ "`cat $tmp_dir/span_begin`" = -1 ]; then return; fi
+
+ # read files to variables:
+ for suffix in num begin end timing lbo framing \
+ coding switchtype signalling yellow termtype
+ do
+ eval span_$suffix=`cat $tmp_dir/span_$suffix 2>/dev/null`
+ done
+
+ # exactly the same logic is used in asterisk's chan_zap.c.
+ # also not that $(( )) is bash-specific
+ case "$((1+ $span_end - $span_begin))" in
+ 2|3|24) #ztgsm, BRI or T1
+ dchan=$span_end
+ bchans="$span_begin-$(($span_end-1))"
+ ;;
+ 31) #E1
+ dchan="$(($span_begin+15))"
+ bchans="$span_begin-$(($span_begin+14)),$(($span_begin+16))-$span_end"
+ if [ "$span_framing" = 'esf' ]; then
+ # don't leave an E1 span with defective defaults:
+ span_framing=ccs
+ span_coding=hdb3
+ span_switchtype=euroisdn
+ span_yellow=crc4
+ fi
+ ;;
+ esac
+ if [ "$span_yellow" != '' ]; then span_yellow=",$span_yellow"; fi
+ # Let's assume that a TE span should get the clock from the remote unit,
+ # and NT spans should provide timing. Just as a sane default.
+ # If we have several TE spans, the first will have priority 1,
+ # second: 2, etc.
+ case "$span_signalling" in *_cpe*)
+ span_timing=$span_te_timing_counter
+ span_te_timing_counter=$(($span_te_timing_counter + 1))
+ ;;
+ esac
+ case "$mode" in
+ files)
+ echo span=$span_num,$span_timing,$span_lbo,$span_framing,$span_coding$span_yellow >> $zaptel_file
+ # leave a comment in zaptel.conf that allows to tell if
+ # this span is TE or NT:
+ if [ "$span_termtype" != '' ]
+ then echo "# termtype: $span_termtype" >>$zaptel_file
+ fi
+
+ echo bchan=$bchans >>$zaptel_file
+ echo dchan=$dchan >>$zaptel_file
+ if [ "$fxsdisable" = 'yes' ] && [ "$span_termtype" = 'nt' ]; then return; fi
+ # You should not send content to zapata.conf below this line
+
+ span_group=$(($SPAN_GROUP_BASE + $span_num))
+
+ if [ "$span_termtype" != '' ]
+ then
+ # an ISDN card's span that we know if it is in NT mode or TE mode.
+ # NT is the same as FXS for us and TE is the same as FXO
+ if [ "$span_termtype" = 'nt' ]
+ then
+ echo "callerid=\"Channels $span_begin - $span_end\" <$span_begin>" >> $zapata_file
+ reset_values="$reset_values callerid"
+ #echo "mailbox=$exten"
+ if [ "$brint_overlap" = 'yes' ]
+ then
+ echo "overlapdial=yes" >> $zapata_file
+ reset_values="$reset_values overlapdial"
+ fi
+ if [ "$group_manual" != "yes" ]
+ then
+ echo "group=$group_phones,$span_group" >> $zapata_file
+ reset_values="$reset_values group"
+ fi
+ if [ "$context_manual" != "yes" ]
+ then
+ echo "context=$context_phones" >> $zapata_file
+ fi
+ else
+ #echo "mailbox="
+ if [ "$group_manual" != "yes" ]
+ then
+ echo "group=$group_lines,$span_group" >> $zapata_file
+ reset_values="$reset_values group"
+ fi
+ if [ "$context_manual" != "yes" ]
+ then
+ echo "context=$context_lines" >> $zapata_file
+ reset_values="$reset_values context"
+ fi
+ fi
+ fi
+ echo "switchtype = $span_switchtype" >> $zapata_file
+ echo "signalling = $span_signalling" >> $zapata_file
+ echo "channel => $bchans" >> $zapata_file
+ reset_zapata_entry $zapata_file $reset_values
+ reset_values=
+ ;;
+ list)
+ echo "### BRI/PRI: $span_termtype"
+ echo "$bchans Data"
+ echo "$dchan Control"
+ #echo BRI/PRI: chans: $bchans, control: $dchan
+ ;;
+ esac
+}
+
+# This is where the actual detection configuration detection work happens.
+genconf() {
+ local mode=$1
+ local reset_values=""
+
+# spanlist=`echo /proc/zaptel/* | grep -v '\*'`
+# spanlist=$(for i in `for i in /proc/zaptel/*; do if [ -f $i ]; then echo $i | cut -f 4 -d / ; fi; done | sort -n`; do echo -n "/proc/zaptel/$i "; done)
+# spanlist=(cd /proc/zaptel; ls | sort -n | sed 's|^|/proc/zaptel/|')
+ spanlist=`ls /proc/zaptel/ 2>/dev/null | sort -n | sed 's|^|/proc/zaptel/|'`
+
+ #if [ "$spanlist" == "" ]; then
+ # die "No zapata interfaces in /proc/zaptel"
+ #fi
+
+
+ case "$mode" in
+ files)
+ if [ "$do_gen_zapscan" = 'yes' ]; then
+ gen_tmp_conf "$ZAPSCAN_FILE" zapscan_file
+ cat <<EOF >$zapscan_file
+; zapscan.conf: information about detected zaptel channels
+; (currently: analog only)
+;
+; Automatically generated by $0 -- Please do not edit.
+
+EOF
+ fi
+ gen_tmp_conf "$ZAPTEL_FILE" zaptel_file
+ gen_tmp_conf "$ZAPATA_FILE" zapata_file
+ cat <<EOF >$zaptel_file
+# Autogenerated by $0 -- do not hand edit
+# Zaptel Configuration File
+#
+# This file is parsed by the Zaptel Configurator, ztcfg
+#
+
+# It must be in the module loading order
+
+EOF
+ cat <<EOF >$zapata_file
+; Autogenerated by $0 -- do not hand edit
+; Zaptel Channels Configurations (zapata.conf)
+;
+; This is not intended to be a complete zapata.conf. Rather, it is intended
+; to be #include-d by /etc/zapata.conf that will include the global settings
+;
+EOF
+ ;;
+ esac
+
+ # For each line in the spanlist: see if it represents a channel.
+ # if it does, test that the channel is usable.
+ # we do that by configuring it (using ztcfg with a 1-line config file)
+ # and then trying to read 1 byte from the device file.
+ #
+ # The '<(command)' syntax creates a temporary file whose content is is the
+ # output of 'command'.
+ #
+ # Another problem with such an approach is how to include an existing
+ # configuration file. For instance: how to include some default settings.
+ #
+ # Maybe an 'include' directive should be added to zaptel.conf ?
+ #cat $spanlist |
+ for procfile in $spanlist
+ do
+ span_num=`basename $procfile`
+ # the first line is the title line. It states the model name
+ # the second line is empty
+ title=`head -n 1 $procfile`
+ # stuff that needs to be remembered accross lines (for PRI/BRI support)
+ case "$mode" in
+ list) echo "### $title";;
+ files)
+ echo "" >>$zaptel_file
+ echo "# $title" >>$zaptel_file
+ echo "" >>$zapata_file
+ echo "; $title" >>$zapata_file
+ ;;
+ esac
+ echo '-1' >$tmp_dir/span_begin
+ echo '-1' >$tmp_dir/span_end
+ echo '0' >$tmp_dir/span_timing
+ echo '0' >$tmp_dir/span_lbo
+ echo '' >$tmp_dir/span_framing
+ echo 'ami' >$tmp_dir/span_coding
+ echo '' >$tmp_dir/span_switchtype
+ echo '' >$tmp_dir/span_signalling
+ if [ "$zapconf_def_termtype" != '' ]
+ then
+ echo "$zapconf_def_termtype" >$tmp_dir/span_termtype
+ fi
+
+ # Check if ZapBRI cards are in TE or NT mode
+ if echo $title | egrep -q '((quad|octo)BRI PCI ISDN Card.* \[NT\]\ |octoBRI \[NT\] |HFC-S PCI A ISDN.* \[NT\] |Xorcom .* (BRI|T1|E1)_NT)'
+ then
+ echo 'nt' >$tmp_dir/span_termtype
+ elif echo $title | egrep -q '((quad|octo)BRI PCI ISDN Card.* \[TE\]\ |octoBRI \[TE\] |HFC-S PCI A ISDN.* \[TE\] |Xorcom .* (BRI|T1|E1)_TE)'
+ then
+ echo 'te' >$tmp_dir/span_termtype
+ fi
+
+ # The rest of the lines are per-channel lines
+ sed -e 1,2d $procfile | \
+ while read line
+ do
+ # in case this is a real channel.
+ chan_num=`echo $line |awk '{print $1}'`
+ case "$line" in
+ *WCTDM/* | *\ WRTDM/* | *OPVXA1200/*)
+ # TDM400P/2400P and similar cards (Sangoma A200, OpenVox A1200)
+ # this can be either FXS or FXO
+ maybe_fxs=0
+ maybe_fxo=0
+ $ztcfg_cmd -c <(echo fxoks=$chan_num) &>/dev/null && maybe_fxs=1
+ $ztcfg_cmd -c <(echo fxsks=$chan_num) &>/dev/null && maybe_fxo=1
+ if [ $maybe_fxs = 1 ] && [ $maybe_fxo = 1 ]
+ then
+ # An installed module won't accept both FXS and FXO signalling types:
+ # this is an empty slot.
+ # TODO: I believe that the Sangoma A20x will reject both and thus
+ # print nothing here.
+ case "$mode" in
+ list) echo "# channel $chan_num, WCTDM, no module.";;
+ files) echo "# channel $chan_num, WCTDM, no module." >> $zaptel_file;;
+ esac
+ continue
+ fi
+
+ if [ $maybe_fxs = 1 ]; then print_pattern $chan_num fxo $mode; fi
+ if [ $maybe_fxo = 1 ]; then print_pattern $chan_num fxs $mode; fi
+ ;;
+ *WCFXO/*)
+ # X100P
+ print_pattern $chan_num fxs $mode || \
+ echo "# channel $chan_num, WCFXO, inactive." >>$zaptel_file
+ ;;
+ *WCUSB/*)
+ print_pattern $chan_num fxo $mode
+ ;;
+ *XPP_FXO/*)
+ # Astribank FXO span
+ print_pattern $chan_num fxs $mode
+ ;;
+ *XPP_FXS/*)
+ # Astribank FXS span (regular port)
+ print_pattern $chan_num fxo $mode
+ ;;
+ *' FXO'/*)
+ # FXO module (probably Rhino)
+ print_pattern $chan_num fxs $mode
+ ;;
+ *' FXS'/*)
+ # FXS module (probably Rhino)
+ print_pattern $chan_num fxo $mode
+ ;;
+ *' ---'/*)
+ # no module (probably Rhino)
+ continue
+ ;;
+ *XPP_OUT/*)
+ # Astribank FXS span (output port)
+ print_pattern -a output $chan_num fxo $mode
+ ;;
+ *XPP_IN/*)
+ # Astribank FXS span (input port)
+ print_pattern -a input $chan_num fxo $mode
+ ;;
+ *ZTHFC*/* | *ztqoz*/* |*ztgsm/* |*TE[24]/* | \
+ *WCT1/*|*Tor2/* | *TorISA/* | \
+ *XPP_BRI_*/* | *WP[TE]1/* | *R[124]T1/* | \
+ *XPP_[TE]1*/* )
+ detect_digital_channel "$line" "$chan_num" "$span_num"
+ ;;
+ '') ;; # Empty line (after span header)
+ *)
+ case "$mode" in
+ list) echo "# ??: $line";;
+ files)
+ echo "# ??: $line" >>$zaptel_file
+ echo "; ??: $line" >>$zapata_file
+ esac
+ ;;
+ esac
+ done
+ # end of part in sub-process.
+
+ write_digital_config
+ done
+
+ if [ "$mode" = 'files' ]
+ then
+ cat <<EOF >> ${zaptel_file}
+
+# Global data
+
+loadzone = $loadzone
+defaultzone = $defaultzone
+EOF
+ fi
+
+ if [ "$mode" = 'files' ]; then
+ mv ${ZAPCONF_FILE} ${ZAPCONF_FILE}.bak 2>/dev/null
+ mv $zaptel_file ${ZAPCONF_FILE}
+ mv ${ZAPATA_FILE} ${ZAPATA_FILE}.bak 2>/dev/nullk
+ mv $zapata_file ${ZAPATA_FILE}
+ if [ "$do_gen_zapscan" = 'yes' ]; then
+ mv $ZAPSCAN_FILE $ZAPSCAN_FILE.bak 2>/dev/null
+ mv $zapscan_file $ZAPSCAN_FILE
+ fi
+
+ zapata_file_name=`basename $ZAPATA_FILE`
+ if ! grep -q "^#include.*$zapata_file_name" \
+ /etc/asterisk/zapata.conf
+ then
+ say "Note: generated $ZAPATA_FILE not included in zapata.conf"
+ say "To fix: echo '#include $zapata_file_name' >>/etc/asterisk/zapata.conf"
+ fi
+ fi
+}
+
+while getopts 'c:de:Fhlm:MRsuvz' arg
+do
+ case "$arg" in
+ e) # guarantee that it is a number:
+ new_base_exten=`echo $OPTARG | tr -d -c 0-9`
+ if [ "x$new_base_exten" != x ]; then base_exten=$new_base_exten; fi
+ ;;
+ c) lc_country=`echo $OPTARG | tr -d -c a-z` ;;
+ d) do_detect=yes ;;
+ F) fxsdisable=yes;;
+ u) do_unload=yes ; force_stop_ast=yes ;;
+ v) verbose=yes ;;
+ l) mode='list' ;;
+ M) do_module_list=yes; do_detect=yes ;;
+ s) force_stop_ast=yes ;;
+ R) do_restart=no ;;
+ z) do_gen_zapscan=yes ;;
+ h) usage; exit 0;;
+ *) echo >&2 "unknown parameter -$arg, Aborting"; usage; exit 1;;
+ esac
+done
+shift $(( $OPTIND-1 ))
+if [ $# != 0 ]; then
+ echo >&2 "$0: too many parameters"
+ usage
+ exit 1
+fi
+
+tmp_dir=`mktemp -d /tmp/genzaptelconf-dir-XXXXXX` || \
+ die "$0: failed to create temporary directory. Aborting"
+
+
+case "$lc_country" in
+ # the list was generated from the source of zaptel:
+ #grep '{.*[0-9]\+,.*"[a-z][a-z]"' zonedata.c | cut -d'"' -f 2 | xargs |tr ' ' '|'
+ us|au|fr|nl|uk|fi|es|jp|no|at|nz|it|gr|tw|cl|se|be|sg|il|br|hu|lt|pl|za|pt|ee|mx|in|de|ch|dk|cz|cn):;;
+ *)
+ lc_country=us
+ echo >&2 "unknown country-code $lc_country, defaulting to \"us\""
+ ;;
+esac
+# any reason for loadzone and defaultzone to be different? If so, this is
+# the place to make that difference
+loadzone=$lc_country
+defaultzone=$loadzone
+
+# make sure asterisk is not in our way
+if [ "$force_stop_ast" = 'yes' ]
+then
+ $ZAPCONF_ASTERISK_CMD stop 1>&2
+else
+ # if asterisk is running and we wanted to detect modules
+ # or simply to unload modules, asterisk needs to go away.
+ if ( [ "$do_unload" = yes ] || [ "$do_detect" = yes ] ) && \
+ pidof asterisk >/dev/null
+ then
+ echo >&2 "Asterisk is already running. Configuration left untouched"
+ echo >&2 "You can use the option -s to shut down Asterisk for the"
+ echo >&2 "duration of the detection."
+ exit_cleanup 1
+ fi
+fi
+
+if [ "$do_unload" = yes ]
+then
+ unload_modules
+ exit_cleanup $?
+fi
+
+if [ "$do_detect" = yes ]
+then
+ detect
+fi
+
+if [ "$zapconf_use_perl" = yes ]; then
+ #redefine genconf to use perl utilities:
+ genconf() {
+ case "$1" in
+ list) zaptel_hardware ;;
+ files) zapconf ;;
+ esac
+ }
+fi
+
+if [ "$mode" = list ]; then
+ genconf list
+else
+ #zap_reg_xpp
+ xpp_startup
+ wait_for_zapctl
+ say "Generating '${ZAPCONF_FILE} and ${ZAPATA_FILE}'"
+ genconf files
+ run_ztcfg
+fi
+
+if [ "$tmp_dir" != '' ]
+then
+ rm -rf "$tmp_dir"
+fi
+
+if [ "$force_stop_ast" != 'yes' ] || [ "$do_restart" != 'yes' ]
+then
+ exit_cleanup 0
+fi
+
+if [ -x "$ZAPCONF_ASTERISK_SCRIPT" ]
+then
+ $ZAPCONF_ASTERISK_CMD start 1>&2
+fi
+
+# if in verbose mode: verify that asterisk is running
+if [ "$verbose" != 'no' ]
+ then
+ say "Checking channels configured in Asterisk:"
+ sleep 1 # give it some time. This is enough on our simple test server
+ if [ -x ast-cmd ]
+ then
+ ast-cmd cmd "zap show channels"
+ else
+ asterisk -rx "zap show channels"
+ fi
+fi
+
+# vim:ts=8:
diff --git a/xpp/genzaptelconf.8 b/xpp/genzaptelconf.8
new file mode 100644
index 0000000..c3f6f73
--- /dev/null
+++ b/xpp/genzaptelconf.8
@@ -0,0 +1,326 @@
+.TH GENZAPTELCONF 8 "July 18th, 2005" "Xorcom Rapid Asterisk" "Linux Programmer's Manual"
+.SH "NAME"
+.B genzaptelconf
+-- generates zaptel configuration (TDM adaptors)
+.SH SYNOPSIS
+.PP
+.B genzaptelconf
+[-sRdvzF] [-c <country_code>] [-e <base_exten>]
+
+.B genzaptelconf
+[-sRdv] -l -- only list to standard output
+
+.B genzaptelconf
+-su -- only unload zaptel modules
+
+.B genzaptelconf
+-h -- Help screen
+
+.SH DESCRIPTION
+.B genzaptelconf
+is a script to detect zaptel devices (currently mostly TDM cards are
+supported). It generates both
+.I /etc/zaptel.conf
+and
+.I /etc/asterisk/zapata-channels.conf
+
+.I PRI
+and
+.I BRI
+(with ZapBRI) cards are basically identified as well. However the span
+configiration is a default that I only hope is sane. Looking for feedback
+
+.SH OPTIONS
+.B -c
+.I country_code
+.RS
+A two-letter country code. Sets the country-code for the zonezone
+entries in
+.I zaptel.conf
+, The default is the value of
+.I lc_country
+from
+.I /etc/default/zaptel
+and failing that, "us".
+.RE
+
+.B -d
+.RS
+Also try to detect modules. Unloads all zaptel modules and loads them
+one by one. Considers a module useful if it loaded successfully and if
+loading it has generated at least one zapata channel.
+
+The list of detected modules is written as the value of
+.I ZAPTEL_MODS
+in
+.I /etc/default/zaptel
+.RE
+
+.B -e
+.I base_exten_num
+.RS
+Configure channel
+.I i
+as extension
+.I exten_num
++
+.I i
+ . This is mostly for the caller-id values. Crude, but may be good enough.
+See also
+.I -r
+.RE
+
+.B -F
+.RS
+Disable writing FXS extensions in zapata.conf
+.RE
+
+.B -l
+.RS
+Only list deceted channels and their signalling. Don't write
+configuration files. Note, however that
+.I -ld
+will still rewrite the modules line in
+.I /etc/default/zaptel
+(see
+.I -d
+above).
+.RE
+
+.B -M
+.RS
+Update
+.I /etc/modules
+with a list of our modules, thereby
+triggers their loading via modprobe on the next boot.
+
+This triggers the
+.I -d
+option as well.
+.RE
+
+.B -R
+.RS
+Don't restart asterisk even if it was stopped using
+.I -s
+ .
+.RE
+
+.B -s
+.RS
+Stop asterisk for the duration of the test. The detection will only
+work if nobody uses the zaptel channels:
+
+* To allow unloading of modules
+
+* to allow reading configuration files.
+
+This option tells the script to stop asterisk (if it was running) and to
+try to start it after the end of the test.
+.RE
+
+.B -v
+.RS
+Be verbose. lists the detected modules if
+.I -d
+is used. Lists detected channls. In the end tries to connect to asterisk
+to get a list of configured zaptel channels.
+.RE
+
+.B -z
+.RS
+emulate the operation of zapscan.bin: generate
+.I /etc/asterisk/zapscan.conf
+with the results of the scan.
+.RE
+
+.SH CONFIGURATION
+Look at the beginning of the script for a number of variables that can
+be overriden through the configuraion file. Some variables can also be
+overriden through the environment. The configuration file is sourced by
+bash but for compatibility expected to have only 'var=VALUE' lines and
+comments or empty lines.
+
+The configuration will first be read from
+.I /etc/default/zaptel
+if it exists, and
+.I /etc/sysconfig/zaptel
+otherwise (But those file names may be overriden, see
+.I ZAPTEL_BOOT_DEBIAN
+and
+.I ZAPTEL_BOOT_FEDORA
+below). Variables set in those files will override the default settings
+and setting rom the environment.
+
+The following variables may be set from the environment:
+ZAPCONF_FILE, ZAPATA_FILE, ZAPTEL_BOOT_DEBIAN, ZAPTEL_BOOT_FEDORA,
+DEVZAP_TIMEOUT, ZTCFG
+
+.RS
+.I lc_country
+.RS
+The default country. Can be also overriden by the option -c
+.RE
+
+.I base_exten
+.RS
+The base number used for automatic numbering
+.RE
+
+.I context_manual
+.RS
+If set to 'yes', no context changes are made in zapata-channels.conf
+.RE
+
+.I context_lines
+.RS
+The context into which calls will go from zaptel trunks.
+.RE
+
+.I context_phones
+.RS
+The context into which calls will go from zaptel phones.
+.RE
+
+.I context_manual
+.RS
+If set to 'yes', no group settings are made in zapata-channels.conf
+.RE
+
+.I group_lines
+.RS
+The group number for zaptel trunks.
+.RE
+
+.I group_phones
+.RS
+The group number for zaptel phones.
+.RE
+
+.I ALL_MODULES
+.RS
+modules list. Used for unloading and modules detection. The order of modules
+is the same for both.
+.RE
+
+.I ZAPCONF_FILE
+.RS
+ztcfg's configuration file. The sane default is /etc/zaptel.conf .
+.RE
+
+.I ZAPATA_FILE
+.RS
+The generated partial zapata.conf snippet. Default:
+/etc/asterisk/zapata-channels.conf .
+.RE
+
+.I ZAPTEL_BOOT_DEBIAN
+.RS
+The Debian Zaptel defaults file. Normally
+.I /etc/default/zaptel
+.
+.RE
+
+
+.I ZAPTEL_BOOT_FEDORA
+.RS
+The Zaptel defaults file on various other distributions. Normally
+.I /etc/sysconfig/zaptel
+ .
+.RE
+
+.I DEVZAP_TIMEOUT
+.RS
+Maximal number of seconds to wait for /dev/zap to be initializaed by
+udev.
+.RE
+
+.I ZTCFG
+.RS
+The full path to the ztcfg tool. Default:
+.I /sbin/ztcfg
+genzaptelconf will also explicitly test for
+.I /usr/sbin/ztcfg
+as a last resort.
+.RE
+.RE
+
+.SH FILES
+.I /etc/zaptel.conf
+.RS
+The configuration file used by
+.I ztcfg
+to configure zaptel devices. re-written by
+.I genzaptelconf
+ . A backup copy is saved to
+.I /etc/zaptel.conf.bak
+ .
+.RE
+
+.I /etc/asterisk/zapata.conf
+.RS
+The configuration file of Asterisk's
+.I chan_zap.
+Not modified directly by
+.I genzaptelconf.
+If you want genzaptelconf's setting to take effect, add the following
+line at the end of
+.I zapata.conf:
+.RS
+#include "zapata-channels.conf"
+.RE
+.RE
+
+.I /etc/asterisk/zapata-channels.conf
+.RS
+This is the snippet of
+.I chan_zap
+configuration file that
+.I genzaptelconf generates.
+ . A backup copy is saved to
+.I /etc/asterisk/zapata-channels.conf.bak
+ .
+.RE
+
+.I /etc/default/zaptel
+.RS
+This file holds configuration for both
+.I genzaptelconf
+and
+.I /etc/init.d/zaptel .
+It is sourced by both scripts and can thus be used to override settings
+of variables from those scripts.
+.RE
+
+.I /etc/modules
+.RS
+A debian-specific list of kernel modules to be loaded by modprobe at
+boot time. When the option
+.I -d
+(detect) is used, genzaptelconf will write in this file zaptel modules
+to be loaded. If you want to use a different file, set
+.I MOD_FILELIST
+ . If it is rewritten, a backup copy is saved to
+.I /etc/modules.bak
+ .
+.RS
+The backup copy of
+.I /etc/modules
+.RE
+
+.SH "SEE ALSO"
+ztcfg(8) asterisk(8).
+
+.SH BUGS
+If you override a configuration variable both through the environment
+and through the configuration file, the value from the configuration
+file wins.
+
+.SH "AUTHOR"
+This manual page was written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
+Permission is granted to copy, distribute and/or modify this document under
+the terms of the GNU General Public License, Version 2 any
+later version published by the Free Software Foundation.
+
+On Debian systems, the complete text of the GNU General Public
+License can be found in /usr/share/common-licenses/GPL.
diff --git a/xpp/hexfile.c b/xpp/hexfile.c
new file mode 100644
index 0000000..2a01b3f
--- /dev/null
+++ b/xpp/hexfile.c
@@ -0,0 +1,567 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * Copyright (C) 2006, Xorcom
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include "hexfile.h"
+
+static const char rcsid[] = "$Id$";
+
+static parse_hexfile_report_func_t report_func = NULL;
+
+parse_hexfile_report_func_t parse_hexfile_set_reporting(parse_hexfile_report_func_t rf)
+{
+ parse_hexfile_report_func_t old_rf = report_func;
+ report_func = rf;
+ return old_rf;
+}
+
+static void chomp(char buf[])
+{
+ size_t last = strlen(buf) - 1;
+ while(last >= 0 && isspace(buf[last]))
+ buf[last--] = '\0';
+}
+
+static int hexline_checksum(struct hexline *hexline)
+{
+ unsigned int i;
+ unsigned int chksm = 0;
+ int ll = hexline->d.content.header.ll;
+
+ for(i = 0; i <= sizeof(hexline->d.content.header) + ll; i++) {
+ chksm += hexline->d.raw[i];
+ }
+ return chksm & 0xFF;
+}
+
+int dump_hexline(int recordno, struct hexline *line, FILE *fp)
+{
+ uint8_t ll;
+ uint16_t offset;
+ uint8_t tt;
+ uint8_t old_chksum;
+ uint8_t new_chksum;
+ uint8_t *data;
+ unsigned int i;
+
+ ll = line->d.content.header.ll;
+ offset = line->d.content.header.offset;
+ tt = line->d.content.header.tt;
+ fprintf(fp, ":%02X%04X%02X", ll, offset, tt);
+ data = line->d.content.tt_data.data;
+ for(i = 0; i < ll; i++) {
+ fprintf(fp, "%02X", data[i]);
+ }
+ old_chksum = data[ll];
+ data[ll] = 0;
+ new_chksum = 0xFF - hexline_checksum(line) + 1;
+ data[ll] = old_chksum;
+ fprintf(fp, "%02X\n", new_chksum);
+ if(new_chksum != old_chksum) {
+ if(report_func)
+ report_func(LOG_ERR, "record #%d: new_chksum(%02X) != old_chksum(%02X)\n",
+ recordno, new_chksum, old_chksum);
+ return 0;
+ }
+ return 1;
+}
+
+struct hexline *new_hexline(uint8_t datalen, uint16_t offset, uint8_t tt)
+{
+ struct hexline *hexline;
+ size_t allocsize;
+
+ allocsize = sizeof(struct hexline) + datalen + 1; /* checksum byte */
+ if((hexline = malloc(allocsize)) == NULL) {
+ if(report_func)
+ report_func(LOG_ERR, "No more memory\n");
+ return NULL;
+ }
+ memset(hexline, 0, allocsize);
+ hexline->d.content.header.ll = datalen;
+ hexline->d.content.header.offset = offset;
+ hexline->d.content.header.tt = tt;
+ return hexline;
+}
+
+static int append_hexline(struct hexdata *hexdata, char *buf)
+{
+ int ret;
+ unsigned int ll, offset, tt;
+ char *p;
+ struct hexline *hexline;
+ unsigned int i;
+
+ if(hexdata->got_eof) {
+ if(report_func)
+ report_func(LOG_ERR, "Extranous data after EOF record\n");
+ return -EINVAL;
+ }
+ if(hexdata->last_line >= hexdata->maxlines) {
+ if(report_func)
+ report_func(LOG_ERR, "Hexfile too large (maxline %d)\n", hexdata->maxlines);
+ return -ENOMEM;
+ }
+ ret = sscanf(buf, "%02X%04X%02X", &ll, &offset, &tt);
+ if(ret != 3) {
+ if(report_func)
+ report_func(LOG_ERR, "Bad line header (only %d items out of 3 parsed)\n", ret);
+ return -EINVAL;
+ }
+ switch(tt) {
+ case TT_DATA:
+ break;
+ case TT_EOF:
+ if(ll != 0) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(EOF): Bad len = %d\n",
+ hexdata->last_line, tt, ll);
+ return -EINVAL;
+ }
+ if(offset != 0) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(EOF): Bad offset = %d\n",
+ hexdata->last_line, tt, offset);
+ return -EINVAL;
+ }
+ hexdata->got_eof = 1;
+ break;
+ case TT_EXT_SEG:
+ if(ll != 2) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(EXT_SEG): Bad len = %d\n",
+ hexdata->last_line, tt, ll);
+ return -EINVAL;
+ }
+ if(offset != 0) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(EXT_SEG): Bad offset = %d\n",
+ hexdata->last_line, tt, offset);
+ return -EINVAL;
+ }
+ break;
+ case TT_START_SEG:
+ if(ll != 4) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(START_SEG): Bad len = %d\n",
+ hexdata->last_line, tt, ll);
+ return -EINVAL;
+ }
+ if(offset != 0) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(START_SEG): Bad offset = %d\n",
+ hexdata->last_line, tt, offset);
+ return -EINVAL;
+ }
+ break;
+ case TT_EXT_LIN:
+ if(ll != 2) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(EXT_LIN): Bad len = %d\n",
+ hexdata->last_line, tt, ll);
+ return -EINVAL;
+ }
+ if(offset != 0) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(EXT_LIN): Bad offset = %d\n",
+ hexdata->last_line, tt, ll);
+ return -EINVAL;
+ }
+ break;
+ case TT_START_LIN: /* Unimplemented */
+ if(ll != 4) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(EXT_LIN): Bad len = %d\n",
+ hexdata->last_line, tt, ll);
+ return -EINVAL;
+ }
+ if(offset != 0) {
+ if(report_func)
+ report_func(LOG_ERR,
+ "%d: Record %d(EXT_LIN): Bad offset = %d\n",
+ hexdata->last_line, tt, ll);
+ return -EINVAL;
+ }
+ break;
+ default:
+ if(report_func)
+ report_func(LOG_ERR, "%d: Unimplemented record type %d: %s\n",
+ hexdata->last_line, tt, buf);
+ return -EINVAL;
+ }
+ buf += 8; /* Skip header */
+ if((hexline = new_hexline(ll, offset, tt)) == NULL) {
+ if(report_func)
+ report_func(LOG_ERR, "No more memory for hexfile lines\n");
+ return -EINVAL;
+ }
+ p = buf;
+ for(i = 0; i < ll + 1; i++) { /* include checksum */
+ unsigned int val;
+
+ if((*p == '\0') || (*(p+1) == '\0')) {
+ if(report_func)
+ report_func(LOG_ERR, "Short data string '%s'\n", buf);
+ return -EINVAL;
+ }
+ ret = sscanf(p, "%02X", &val);
+ if(ret != 1) {
+ if(report_func)
+ report_func(LOG_ERR, "Bad data byte #%d\n", i);
+ return -EINVAL;
+ }
+ hexline->d.content.tt_data.data[i] = val;
+ p += 2;
+ }
+ if(hexline_checksum(hexline) != 0) {
+ if(report_func) {
+ report_func(LOG_ERR, "Bad checksum (%d instead of 0)\n",
+ hexline_checksum(hexline));
+ dump_hexline(hexdata->last_line, hexline, stderr);
+ }
+ return -EINVAL;
+ }
+ hexdata->lines[hexdata->last_line] = hexline;
+ if(hexdata->got_eof)
+ return 0;
+ hexdata->last_line++;
+ return 1;
+}
+
+void free_hexdata(struct hexdata *hexdata)
+{
+ if(hexdata) {
+ unsigned int i;
+
+ for(i = 0; i < hexdata->maxlines; i++)
+ if(hexdata->lines[i] != NULL)
+ free(hexdata->lines[i]);
+ free(hexdata);
+ }
+}
+
+int dump_hexfile(struct hexdata *hexdata, const char *outfile)
+{
+ FILE *fp;
+ unsigned int i;
+
+ if(report_func)
+ report_func(LOG_INFO, "Dumping hex data into '%s'\n", outfile);
+ if(!outfile || strcmp(outfile, "-") == 0)
+ fp = stdout;
+ else if((fp = fopen(outfile, "w")) == NULL) {
+ perror(outfile);
+ exit(1);
+ }
+ for(i = 0; i <= hexdata->last_line; i++) {
+ struct hexline *line = hexdata->lines[i];
+ if(!line) {
+ if(report_func)
+ report_func(LOG_ERR, "Missing line at #%d\n", i);
+ return -EINVAL;
+ }
+ if(!dump_hexline(i, line, fp))
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int dump_hexfile2(struct hexdata *hexdata, const char *outfile, uint8_t maxwidth)
+{
+ FILE *fp;
+ uint8_t tt;
+ unsigned int i;
+ struct hexline *line;
+
+ if(report_func)
+ report_func(LOG_INFO,
+ "Dumping hex data into '%s' (maxwidth=%d)\n",
+ outfile, maxwidth);
+ if(!outfile || strcmp(outfile, "-") == 0)
+ fp = stdout;
+ else if((fp = fopen(outfile, "w")) == NULL) {
+ perror(outfile);
+ exit(1);
+ }
+ if(maxwidth == 0)
+ maxwidth = UINT8_MAX;
+ for(i = 0; i <= hexdata->last_line; i++) {
+ int bytesleft = 0;
+ int extra_offset = 0;
+ int base_offset;
+ uint8_t *base_data;
+
+ line = hexdata->lines[i];
+ if(!line) {
+ if(report_func)
+ report_func(LOG_ERR, "Missing line at #%d\n", i);
+ return -EINVAL;
+ }
+ bytesleft = line->d.content.header.ll;
+ /* split the line into several lines */
+ tt = line->d.content.header.tt;
+ base_offset = line->d.content.header.offset;
+ base_data = line->d.content.tt_data.data;
+ while (bytesleft > 0) {
+ struct hexline *extraline;
+ uint8_t new_chksum;
+ unsigned int curr_bytes = (bytesleft >= maxwidth) ? maxwidth : bytesleft;
+
+ /* generate the new line */
+ if((extraline = new_hexline(curr_bytes, base_offset + extra_offset, tt)) == NULL) {
+ if(report_func)
+ report_func(LOG_ERR, "No more memory for hexfile lines\n");
+ return -EINVAL;
+ }
+ memcpy(extraline->d.content.tt_data.data, base_data + extra_offset, curr_bytes);
+ new_chksum = 0xFF - hexline_checksum(extraline) + 1;
+ extraline->d.content.tt_data.data[curr_bytes] = new_chksum;
+ /* print it */
+ dump_hexline(i, extraline, fp);
+ /* cleanups */
+ free(extraline);
+ extra_offset += curr_bytes;
+ bytesleft -= curr_bytes;
+ }
+ }
+ if(tt != TT_EOF) {
+ if(report_func)
+ report_func(LOG_ERR, "Missing EOF record\n");
+ return -EINVAL;
+ }
+ dump_hexline(i, line, fp);
+ return 0;
+}
+
+void process_comment(struct hexdata *hexdata, char buf[])
+{
+ char *dollar_start;
+ char *dollar_end;
+ const char id_prefix[] = "Id: ";
+ char tmp[BUFSIZ];
+ char *p;
+ int len;
+
+ if(report_func)
+ report_func(LOG_INFO, "Comment: %s\n", buf + 1);
+ /* Search for RCS keywords */
+ if((dollar_start = strchr(buf, '$')) == NULL)
+ return;
+ if((dollar_end = strchr(dollar_start + 1, '$')) == NULL)
+ return;
+ /* Crop the '$' signs */
+ len = dollar_end - dollar_start;
+ len -= 2;
+ memcpy(tmp, dollar_start + 1, len);
+ tmp[len] = '\0';
+ p = tmp;
+ if(strstr(tmp, id_prefix) == NULL)
+ return;
+ p += strlen(id_prefix);
+ if((p = strchr(p, ' ')) == NULL)
+ return;
+ p++;
+ snprintf(hexdata->version_info, BUFSIZ, "%s", p);
+ if((p = strchr(hexdata->version_info, ' ')) != NULL)
+ *p = '\0';
+}
+
+struct hexdata *parse_hexfile(const char *fname, unsigned int maxlines)
+{
+ FILE *fp;
+ struct hexdata *hexdata = NULL;
+ int datasize;
+ char buf[BUFSIZ];
+ int line;
+ int dos_eof = 0;
+ int ret;
+
+ assert(fname != NULL);
+ if(report_func)
+ report_func(LOG_INFO, "Parsing %s\n", fname);
+ datasize = sizeof(struct hexdata) + maxlines * sizeof(char *);
+ hexdata = (struct hexdata *)malloc(datasize);
+ if(!hexdata) {
+ if(report_func)
+ report_func(LOG_ERR, "Failed to allocate %d bytes for hexfile contents\n", datasize);
+ goto err;
+ }
+ memset(hexdata, 0, datasize);
+ hexdata->maxlines = maxlines;
+ if((fp = fopen(fname, "r")) == NULL) {
+ if(report_func)
+ report_func(LOG_ERR, "Failed to open hexfile '%s'\n", fname);
+ goto err;
+ }
+ for(line = 1; fgets(buf, BUFSIZ, fp); line++) {
+ if(dos_eof) {
+ if(report_func)
+ report_func(LOG_ERR, "%s:%d - Got DOS EOF character before true EOF\n", fname, line);
+ goto err;
+ }
+ if(buf[0] == 0x1A && buf[1] == '\0') { /* DOS EOF char */
+ dos_eof = 1;
+ continue;
+ }
+ chomp(buf);
+ if(buf[0] == '\0') {
+ if(report_func)
+ report_func(LOG_ERR, "%s:%d - Short line\n", fname, line);
+ goto err;
+ }
+ if(buf[0] == '#') {
+ process_comment(hexdata, buf);
+ continue;
+ }
+ if(buf[0] != ':') {
+ if(report_func)
+ report_func(LOG_ERR, "%s:%d - Line begins with 0x%X\n", fname, line, buf[0]);
+ goto err;
+ }
+ if((ret = append_hexline(hexdata, buf + 1)) < 0) {
+ if(report_func)
+ report_func(LOG_ERR, "%s:%d - Failed parsing.\n", fname, line);
+ goto err;
+ }
+ }
+ fclose(fp);
+ if(report_func)
+ report_func(LOG_INFO, "%s parsed OK\n", fname);
+ return hexdata;
+err:
+ free_hexdata(hexdata);
+ return NULL;
+}
+
+void dump_binary(struct hexdata *hexdata, const char *outfile)
+{
+ FILE *fp;
+ unsigned int i;
+ size_t len;
+
+ if(report_func)
+ report_func(LOG_INFO, "Dumping binary data into '%s'\n", outfile);
+ if((fp = fopen(outfile, "w")) == NULL) {
+ perror(outfile);
+ exit(1);
+ }
+ for(i = 0; i < hexdata->maxlines; i++) {
+ struct hexline *hexline = hexdata->lines[i];
+
+ if(!hexline)
+ break;
+ switch(hexline->d.content.header.tt) {
+ case TT_EOF:
+ if(report_func)
+ report_func(LOG_INFO, "\ndump: good EOF record");
+ break;
+ case TT_DATA:
+ if(report_func)
+ report_func(LOG_INFO, "dump: %6d\r", i);
+ len = hexline->d.content.header.ll;
+ if(fwrite(hexline->d.content.tt_data.data, 1, len, fp) != len) {
+ perror("write");
+ exit(1);
+ }
+ break;
+ case TT_EXT_SEG:
+ case TT_START_SEG:
+ case TT_EXT_LIN:
+ case TT_START_LIN:
+ if(report_func)
+ report_func(LOG_INFO,
+ "\ndump(%d): ignored record type %d",
+ i, hexline->d.content.header.tt);
+ break;
+ default:
+ if(report_func)
+ report_func(LOG_ERR, "dump: Unknown record type %d\n",
+ hexline->d.content.header.tt);
+ exit(1);
+ }
+ }
+ if(report_func)
+ report_func(LOG_INFO, "\nDump finished\n");
+ fclose(fp);
+}
+
+void gen_hexline(const uint8_t *data, uint16_t addr, size_t len, FILE *output)
+{
+ struct hexline *hexline;
+
+ if(!data) {
+ fprintf(output, ":%02X%04X%02XFF\n", 0, 0, TT_EOF);
+ return;
+ }
+ if((hexline = new_hexline(len, addr, (!data) ? TT_EOF : TT_DATA)) == NULL) {
+ if(report_func)
+ report_func(LOG_ERR, "No more memory\n");
+ return;
+ }
+ if(data)
+ memcpy(&hexline->d.content.tt_data, data, len);
+ dump_hexline(0, hexline, output);
+ free(hexline);
+}
+
+/*
+ * Algorithm lifted of sum(1) implementation from coreutils.
+ * We chose the default algorithm (BSD style).
+ */
+int bsd_checksum(struct hexdata *hexdata)
+{
+ unsigned int i;
+ size_t len;
+ int ck = 0;
+
+ for(i = 0; i < hexdata->maxlines; i++) {
+ struct hexline *hexline = hexdata->lines[i];
+ unsigned char *p;
+
+ if(!hexline)
+ break;
+ if(hexline->d.content.header.tt == TT_EOF)
+ continue;
+ len = hexline->d.content.header.ll;
+ p = hexline->d.content.tt_data.data;
+ for(; len; p++, len--) {
+ ck = (ck >> 1) + ((ck & 1) << 15);
+ ck += *p;
+ ck &= 0xffff; /* Keep it within bounds. */
+ }
+ }
+ return ck;
+}
diff --git a/xpp/hexfile.h b/xpp/hexfile.h
new file mode 100644
index 0000000..f8bf6a9
--- /dev/null
+++ b/xpp/hexfile.h
@@ -0,0 +1,123 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * Copyright (C) 2006, 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.
+ *
+ */
+
+#ifndef PARSE_HEXFILE_H
+#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 <stdint.h>
+#include <syslog.h>
+#define PACKED __attribute__((packed))
+#define ZERO_SIZE 1
+
+#else
+
+#error "Cannot compile on this platform"
+
+#endif
+
+/* Record types in hexfile */
+enum {
+ TT_DATA = 0,
+ TT_EOF = 1,
+ TT_EXT_SEG = 2,
+ TT_START_SEG = 3,
+ TT_EXT_LIN = 4,
+ TT_START_LIN = 5,
+ TT_NO_SUCH_TT
+};
+
+#pragma pack(1)
+struct hexline {
+ union {
+ uint8_t raw[ZERO_SIZE];
+ struct content {
+ struct header {
+ uint8_t ll; /* len */
+ uint16_t offset; /* offset */
+ uint8_t tt; /* type */
+ } PACKED header;
+ struct tt_data {
+ uint8_t data[ZERO_SIZE];
+ } tt_data;
+ } PACKED content;
+ } d;
+} PACKED;
+#pragma pack()
+
+struct hexdata {
+ unsigned int maxlines;
+ unsigned int last_line;
+ int got_eof;
+ char version_info[BUFSIZ];
+ struct hexline *lines[ZERO_SIZE];
+};
+
+
+__BEGIN_DECLS
+
+typedef void (*parse_hexfile_report_func_t)(int level, const char *msg, ...);
+
+parse_hexfile_report_func_t parse_hexfile_set_reporting(parse_hexfile_report_func_t rf);
+void free_hexdata(struct hexdata *hexdata);
+struct hexdata *parse_hexfile(const char *fname, unsigned int maxlines);
+int dump_hexfile(struct hexdata *hexdata, const char *outfile);
+int dump_hexfile2(struct hexdata *hexdata, const char *outfile, uint8_t maxwidth);
+void dump_binary(struct hexdata *hexdata, const char *outfile);
+void gen_hexline(const uint8_t *data, uint16_t addr, size_t len, FILE *output);
+int bsd_checksum(struct hexdata *hexdata);
+__END_DECLS
+
+#endif
diff --git a/xpp/lszaptel b/xpp/lszaptel
new file mode 100755
index 0000000..a836d98
--- /dev/null
+++ b/xpp/lszaptel
@@ -0,0 +1,108 @@
+#! /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;
+BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); }
+
+use Zaptel;
+use Zaptel::Span;
+use Zaptel::Xpp;
+use Zaptel::Xpp::Xbus;
+use Zaptel::Xpp::Xpd;
+
+my @xbuses = Zaptel::Xpp::xbuses("SORT_CONNECTOR");
+my @xpds = map { $_->xpds } @xbuses;
+
+foreach my $span (Zaptel::spans()) {
+ my $spanno = $span->num;
+ my $xpd = $span->xpd;
+ my @lines;
+ my $index = 0;
+
+ @lines = @{$xpd->lines} if defined $xpd;
+ printf "### Span %2d: %s %s\n", $span->num, $span->name, $span->description;
+ foreach my $chan ($span->chans()) {
+ my %type_map = (
+ OUT => 'Output',
+ IN => 'Input'
+ );
+ my ($type) = map { $type_map{$_} or $_ } $chan->type || ("unknown");
+ my $batt = "";
+ $batt = "(battery)" if $chan->battery;
+ printf "%3d %-10s %-10s %s %s\n",
+ $chan->num, $type, $chan->signalling, $chan->info, $batt;
+ $index++;
+ }
+}
+
+__END__
+
+=head1 NAME
+
+lszaptel - List all zaptel channels with their types and spans.
+
+=head1 SYNOPSIS
+
+lszaptel
+
+=head1 DESCRIPTION
+
+Example output:
+
+ ### Span 1: WCTDM/0 "Wildcard TDM400P REV E/F Board 1"
+ 1 FXO FXOLS (In use)
+ 2 FXS FXSKS
+ 3 FXS FXSKS
+ 4 FXS FXSKS
+ ### Span 2: XBUS-00/XPD-00 "Xorcom XPD #00/00: FXO"
+ 5 FXO FXSKS (In use)
+ 6 FXO FXSKS (In use) (no pcm)
+ 7 FXO FXSKS (In use) (no pcm)
+ 8 FXO FXSKS (In use) (no pcm)
+ 9 FXO FXSKS (In use) (no pcm)
+ 10 FXO FXSKS (In use) (no pcm)
+ 11 FXO FXSKS (In use) (no pcm)
+ 12 FXO FXSKS (In use) (no pcm)
+ ### Span 3: XBUS-00/XPD-10 "Xorcom XPD #00/10: FXO"
+ 13 FXO FXSKS (In use) (no pcm)
+ 14 FXO FXSKS (In use) (no pcm)
+ 15 FXO FXSKS (In use) (no pcm)
+ 16 FXO FXSKS (In use) (no pcm)
+ 17 FXO FXSKS (In use) (no pcm)
+ 18 FXO FXSKS (In use) (no pcm)
+ 19 FXO FXSKS (In use) (no pcm)
+ 20 FXO FXSKS (In use) (no pcm)
+
+ ...
+
+ ### Span 6: XBUS-01/XPD-00 "Xorcom XPD #01/00: FXS"
+ 37 FXS FXOLS (In use)
+ 38 FXS FXOLS (In use) (no pcm)
+ 39 FXS FXOLS (In use) (no pcm)
+ 40 FXS FXOLS (In use) (no pcm)
+ 41 FXS FXOLS (In use) (no pcm)
+ 42 FXS FXOLS (In use) (no pcm)
+ 43 FXS FXOLS (In use) (no pcm)
+ 44 FXS FXOLS (In use) (no pcm)
+ 45 Output FXOLS (In use) (no pcm)
+ 46 Output FXOLS (In use) (no pcm)
+ 47 Input FXOLS (In use) (no pcm)
+ 48 Input FXOLS (In use) (no pcm)
+ 49 Input FXOLS (In use) (no pcm)
+ 50 Input FXOLS (In use) (no pcm)
+
+The first column is the type of the channel (port, for an analog device)
+and the second one is the signalling (if set).
+
+=head1 FILES
+
+lszaptel is a somewhat glorified 'cat /proc/zaptel/*' . Unlike that
+command, it sorts the spans with the proper order. It also formats the
+output slightly differently.
diff --git a/xpp/print_modes.c b/xpp/print_modes.c
new file mode 100644
index 0000000..77e0e33
--- /dev/null
+++ b/xpp/print_modes.c
@@ -0,0 +1,33 @@
+#include <stdio.h>
+
+#include "wctdm_fxomodes.h"
+
+int main() {
+ size_t i;
+
+ for (i=0; i<(sizeof(fxo_modes)/sizeof(struct fxo_mode)); i++) {
+ if (fxo_modes[i].name == NULL) break;
+ int reg16=0, reg26=0, reg30=0, reg31=0x20;
+ char ring_osc[BUFSIZ]="", ring_x[BUFSIZ] = "";
+
+ reg16 |= (fxo_modes[i].ohs << 6);
+ reg16 |= (fxo_modes[i].rz << 1);
+ reg16 |= (fxo_modes[i].rt);
+
+ reg26 |= (fxo_modes[i].dcv << 6);
+ reg26 |= (fxo_modes[i].mini << 4);
+ reg26 |= (fxo_modes[i].ilim << 1);
+
+ reg30 = (fxo_modes[i].acim);
+
+ reg31 |= (fxo_modes[i].ohs2 << 3);
+
+ if (fxo_modes[i].ring_osc)
+ snprintf(ring_osc, BUFSIZ, "ring_osc=%04X", fxo_modes[i].ring_osc);
+ if (fxo_modes[i].ring_x)
+ snprintf(ring_x, BUFSIZ, "ring_x=%04X", fxo_modes[i].ring_x);
+ printf("%-15s\treg16=%02X\treg26=%02X\treg30=%02X\treg31=%02X\t%s\t%s\n",
+ fxo_modes[i].name, reg16, reg26, reg30, reg31, ring_osc, ring_x);
+ }
+ return 0;
+}
diff --git a/xpp/test_parse.c b/xpp/test_parse.c
new file mode 100644
index 0000000..8ac2023
--- /dev/null
+++ b/xpp/test_parse.c
@@ -0,0 +1,35 @@
+#include <stdio.h>
+#include <stdarg.h>
+#include "hexfile.h"
+
+static void default_report_func(int level, const char *msg, ...)
+{
+ va_list ap;
+
+ va_start(ap, msg);
+ vfprintf(stderr, msg, ap);
+ va_end(ap);
+}
+
+int main(int argc, char *argv[])
+{
+ struct hexdata *hd;
+ int i;
+
+ if(argc < 2) {
+ fprintf(stderr, "Usage: program hexfile...\n");
+ return 1;
+ }
+ parse_hexfile_set_reporting(default_report_func);
+ for(i = 1; i < argc; i++) {
+ hd = parse_hexfile(argv[i], 2000);
+ if(!hd) {
+ fprintf(stderr, "Parsing failed\n");
+ return 1;
+ }
+ fprintf(stderr, "=== %s === (version: %s)\n", argv[i], hd->version_info);
+ dump_hexfile2(hd, "-", 60 );
+ free_hexdata(hd);
+ }
+ return 0;
+}
diff --git a/xpp/xpp.rules b/xpp/xpp.rules
new file mode 100644
index 0000000..d3cc226
--- /dev/null
+++ b/xpp/xpp.rules
@@ -0,0 +1,14 @@
+BUS!="usb", ACTION!="add", GOTO="xpp_usb_add_end"
+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]", \
+ RUN+="/usr/share/zaptel/xpp_fxloader udev $sysfs{idVendor}/$sysfs{idProduct}/$sysfs{bcdDevice}"
+
+LABEL="xpp_usb_add_end"
+
+# Hotplug hook for Astribank up/down
+# By default XPP_INIT_DIR="/usr/share/zaptel"
+KERNEL=="xbus*" RUN+="%E{XPP_INIT_DIR}/astribank_hook udev $kernel $sysfs{status} $sysfs{connector}"
+
diff --git a/xpp/xpp_blink b/xpp/xpp_blink
new file mode 100755
index 0000000..7d0d845
--- /dev/null
+++ b/xpp/xpp_blink
@@ -0,0 +1,168 @@
+#! /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;
+BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); }
+
+use Zaptel;
+use Zaptel::Span;
+use Zaptel::Xpp;
+use Zaptel::Xpp::Xbus;
+
+sub usage {
+ die "Usage: $0 {on|off|bzzt} {span <number> | chan <number> | xpd <bus num> [<xpd num>] | label <label>}\n";
+}
+
+my $state = shift;
+my $selector = shift;
+usage unless defined($state) and $state =~ /^(on|off|bzzt)$/;
+usage unless defined($selector) and $selector =~ /^(span|chan|xpd|label)$/i;
+
+my $xpd;
+my @blinklist;
+my @channels;
+
+if($selector =~ /^span$/i) {
+ my $number = shift;
+ usage unless defined($number) and $number =~ /^\d+/;
+ my $span = Zaptel::Span::by_number($number);
+ die "Unkown Span $number\n" unless $span;
+ $xpd = Zaptel::Xpp::xpd_of_span($span);
+ die "Span $number is not an XPD\n" unless defined $xpd;
+ my $xpdname = $xpd->fqn;
+ my $connector = $xpd->xbus->connector;
+ die "$xpdname is not connected\n" unless defined $connector;
+ push(@blinklist, $xpd);
+ my @chans = $span->chans();
+ @channels = join(' ', map { $_->num } @chans);
+ printf "Using %s (connected via $connector): channels @channels\n", $xpd->fqn;
+} elsif($selector =~ /^chan$/i) {
+ my $channo = shift;
+ usage unless defined($channo) and $channo =~ /^\d+/;
+ my @spans = Zaptel::spans();
+ my @chans = map { $_->chans() } @spans;
+ my ($chan) = grep { $_->num eq $channo } @chans;
+ die "Channel $channo was not found\n" unless defined $chan;
+ die "Cannot blink Input ports\n" if $chan->type eq 'IN';
+ die "Cannot blink Output ports\n" if $chan->type eq 'OUT';
+ push(@blinklist, $chan);
+} elsif($selector =~ /^xpd$/i) {
+ my $busnum = shift;
+ my $xpdnum = shift;
+ my $linenum = shift;
+ usage unless defined($busnum) and $busnum =~ /^\d+/;
+ my $xbus = Zaptel::Xpp::Xbus::by_number($busnum);
+ die "Unkown XBUS number $busnum\n" unless defined $xbus;
+ if(defined $xpdnum) {
+ usage unless $xpdnum =~ /^\d+/;
+ $xpd = $xbus->get_xpd_by_number($xpdnum);
+ die "Unkown XPD number $xpdnum on XBUS number $busnum\n" unless defined $xpd;
+ if(defined $linenum) {
+ usage unless $linenum =~ /^\d+/;
+ my $lines = $xpd->lines;
+ my $l = @{$lines}[$linenum];
+ die "Bad line number $linenum on XPD $xpd->fqn\n" unless defined $l;
+ push(@blinklist, $l);
+ } else {
+ push(@blinklist, $xpd);
+ }
+ } else {
+ @blinklist = $xbus->xpds;
+ die "XBUS number $busnum has no XPDS!\n" unless @blinklist;
+ }
+} elsif($selector =~ /^label$/i) {
+ my $label = shift;
+ usage unless defined($label);
+ my $xbus = Zaptel::Xpp::Xbus::by_label($label);
+ die "Unkown XBUS label $label\n" unless defined $xbus;
+ @blinklist = $xbus->xpds;
+ die "XBUS label '$label' has no XPDS!\n" unless @blinklist;
+}
+
+if($state eq 'on') {
+ $_->blink(1) foreach (@blinklist);
+} elsif($state eq 'off') {
+ $_->blink(0) foreach (@blinklist);
+} elsif($state eq 'bzzt') {
+ $_->blink(1) foreach (@blinklist);
+ sleep 1;
+ $_->blink(0) foreach (@blinklist);
+}
+
+__END__
+
+=head1 NAME
+
+xpp_blink - Blink the leds of a specified XPD
+
+=head1 SYNOPSIS
+
+xpp_blink {on|off|bzzt} {span <number> | chan <number> | xpd <bus num> [<xpd num> [<lineno>]]}
+
+=head1 DESCRIPTION
+
+Blink all the leds of an XPD.
+
+=head2 Blink mode:
+
+=over
+
+=item on
+
+Turn on constant blink
+
+=item off
+
+Turn off blink
+
+=item bzzt
+
+Blink briefly for 1 second.
+
+=back
+
+=head2 Selector:
+
+=over
+
+=item span
+
+Select by span number. This only work for XPD registered to zaptel.
+
+will also print the zaptel channels of the span and the xbus/xpd this
+span represents.
+
+=item chan
+
+Select by channel number. This only work for XPD registered to zaptel.
+
+=item xpd
+
+Select by xbus + xpd numbers. If only xbus number is given, all the
+XPDs of the selected xbus (Astribank) are blinked.
+
+=item label
+
+Select by xbus label. Affect the whole Astribank.
+
+=back
+
+=head1 EXAMPLES
+
+ $ xpp_blink bzzt span 2
+ Using XBUS-04/XPD-10 (connected via usb-0000:00:1d.7-1): channels 15 16 17 18 19 20 21 22
+
+ $ xpp_blink bzzt chan 18
+
+ $ xpp_blink on xpd 0 1 5
+
+ $ xpp_blink off xpd 0
+
+ $ xpp_blink bzzt label 'usb:00000238'
diff --git a/xpp/xpp_fxloader b/xpp/xpp_fxloader
new file mode 100644
index 0000000..5a26560
--- /dev/null
+++ b/xpp/xpp_fxloader
@@ -0,0 +1,297 @@
+#!/bin/sh
+
+# xpp_fxloader: load Xorcom Astribank (XPP) firmware
+#
+# 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
+#
+# Manual Run
+# ##########
+#
+# path/to/xpp_fxloader load
+#
+# Make sure the firmware files are in $FIRMWARE_DIR
+#
+# UDEV Installation
+# #################
+#
+# Copy xpp.rules to /etc/udev/udev.d and xpp_fxloader to /etc/hotplug/usb/ .
+#
+# Hotplug Installation
+# ####################
+#
+# Copy this file and the file xpp_fxloader.usermap to /etc/hotplug/usb/ .
+#
+#
+# Written by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
+# Copyright (C) 2006, Xorcom
+#
+# All rights reserved.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+set -e
+
+# Make sure fxload is in the path:
+PATH="$PATH:/usr/local/sbin:/sbin:/usr/sbin"
+export PATH
+
+me=`basename $0`
+DEBIAN_DEFAULTS="/etc/default/zaptel"
+REDHAT_DEFAULTS="/etc/sysconfig/zaptel"
+
+status_fd=3
+
+if [ -r "$DEBIAN_DEFAULTS" -a -r "$REDHAT_DEFAULTS" ]; then
+ echo 1>&2 "$0: Both '$DEBIAN_DEFAULTS' and '$REDHAT_DEFAULTS' exist"
+ exit 1
+elif [ -r "$DEBIAN_DEFAULTS" ]; then
+ DEFAULTS="$DEBIAN_DEFAULTS"
+elif [ -r "$REDHAT_DEFAULTS" ]; then
+ DEFAULTS="$REDHAT_DEFAULTS"
+fi
+
+if [ -t 2 ]; then
+ LOGGER="logger -i -t '$me' -s"
+else
+ LOGGER="logger -i -t '$me'"
+fi
+
+USBFS_PREFIX=/proc/bus/usb
+DEVUSB_PREFIX=/dev/bus/usb
+USB_PREFIX=
+
+FIRMWARE_DIR="${FIRMWARE_DIR:-/usr/share/zaptel}"
+
+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
+ . "$DEFAULTS"
+fi
+
+if [ "$USB_PREFIX" = '' ]; then
+ if [ -d "$DEVUSB_PREFIX" ]; then
+ USB_PREFIX=$DEVUSB_PREFIX
+ elif [ -r "$USBFS_PREFIX/devices" ]; then
+ USB_PREFIX=$USBFS_PREFIX
+ fi
+fi
+
+# With Kernels older that 2.6.10 it seems to be possible
+# to trigger a race condition by running fxload or fpga_load
+# immediately after the detection of the device.
+KERNEL_HAS_USB_RACE=0
+case "`uname -r`" in 2.6.[89]*) KERNEL_HAS_USB_RACE=1;; esac
+sleep_if_race() {
+ if [ "$KERNEL_HAS_USB_RACE" = '1' ]; then
+ sleep 2
+ fi
+}
+
+find_dev() {
+ v_id=$1
+ p_id=$2
+
+ lsusb | tr -d : | awk "/ ID $v_id$p_id/{printf \"$USB_PREFIX/%s/%s \",\$2,\$4}"
+}
+
+do_fxload() {
+ sleep_if_race
+ ( fxload -t fx2 $* 2>&1 1>/dev/null || exit 1 ) | $LOGGER
+}
+
+load_fw() {
+ v_id=$1
+ p_id=$2
+ fw=$3
+
+ 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
+ done
+}
+
+load_fpga() {
+ v_id=$1
+ p_id=$2
+ fw=$3
+
+ devices=`find_dev $v_id $p_id`
+ for dev in $devices
+ do
+ (
+ card_ver=`$FPGA_LOAD -g -D $dev | sed -n 's/^.*Release: *//'`
+
+ $LOGGER "FPGA Firmware into $dev"
+ sleep_if_race
+ (
+ $FPGA_LOAD -D "$dev" -I "$FIRMWARE_DIR/$fw" -i
+ echo $? >$status_fd
+ )>| $LOGGER
+ status=`cat <$status_fd`
+ if [ "$status" != 0 ]; then
+ echo "fpga_load failed with status $status" | $LOGGER
+ exit 77
+ fi
+ ) &
+ sleep 0.4
+ done
+ wait
+}
+
+numdevs() {
+ v_ids="$1"
+ p_ids="$2"
+
+ for v in $v_ids
+ do
+ (
+ for p in $p_ids
+ do
+ find_dev $v $p
+ done
+ )
+ done | wc -w
+}
+
+wait_renumeration() {
+ num="$1"
+ v_ids="$2"
+ p_ids="$3"
+
+ while
+ n=`numdevs "$v_ids" "$p_ids"`
+ [ "$num" -gt "$n" ]
+ do
+ echo -n "."
+ sleep 1
+ done
+ echo "Got all $num devices"
+}
+
+reset_fpga() {
+ totaldevs=`numdevs e4e4 '11[3456][012]'`
+ devices=`find_dev e4e4 '11[3456][12]'`
+ echo "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
+ done
+ if [ "$1" = 'wait' ]; then
+ wait_renumeration $totaldevs e4e4 '11[3456]0'
+ fi
+}
+
+#########################
+##
+## Manual run
+##
+
+# to run manually, pass the parameter 'xppdetect'
+case "$1" in
+udev)
+ # the following emulate hotplug's environment from udev's environment:
+ DEVICE="$DEVNAME"
+ PRODUCT="$2"
+ # skip on to the rest of the script. Don't exit.
+ ;;
+reset-wait)
+ reset_fpga wait
+ ;;
+reset)
+ reset_fpga
+ ;;
+xppdetect|load|usb)
+ numdevs=`numdevs e4e4 '11[3456][01]'`
+ echo "--------- 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
+ 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
+ wait_renumeration $numdevs e4e4 '11[3456]2'
+ fi
+
+ sleep 3 # Let it stabilize
+ echo "--------- 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')"
+ exit 0
+ ;;
+esac
+
+#########################
+##
+## Hotplug run
+##
+
+# allow disabling automatic hotplugging:
+if [ "$XPP_HOTPLUG_DISABLED" != '' ]; then
+ $LOGGER -p kern.info "Exiting... XPP_HOTPLUG_DISABLED"
+ exit 0
+fi
+
+if [ "$ACTION" = "add" ] && [ -w "$DEVICE" ]
+then
+ $LOGGER "Trying to find what to do for product $PRODUCT, device $DEVICE"
+ prod_id=`echo "$PRODUCT" | cut -d/ -f2`
+ case "$PRODUCT" in
+ e4e4/11[345]0/*)
+ FIRM_USB="$FIRMWARE_DIR/$USB_FW"
+ $LOGGER "Loading firmware '$FIRM_USB' into '$DEVICE'"
+ do_fxload -D "$DEVICE" -I "$FIRM_USB"
+ ;;
+ e4e4/11[345]1/*)
+ if [ "$prod_id" = 1131 ]; then
+ FIRM_FPGA="$FIRMWARE_DIR/FPGA_FXS.hex" # Legacy
+ else
+ FIRM_FPGA="$FIRMWARE_DIR/FPGA_$prod_id.hex"
+ fi
+ sleep_if_race
+ $FPGA_LOAD -D "$DEVICE" -I "$FIRM_FPGA" 2>&1 >/dev/null | $LOGGER
+ ;;
+ esac
+fi
diff --git a/xpp/xpp_fxloader.usermap b/xpp/xpp_fxloader.usermap
new file mode 100644
index 0000000..8c14b72
--- /dev/null
+++ b/xpp/xpp_fxloader.usermap
@@ -0,0 +1,10 @@
+# module match_flags idVendor idProduct bcdDevice_lo bcdDevice_hi bDeviceClass bDeviceSubClass bDeviceProtocol bInterfaceClass bInterfaceSubClass bInterfaceProtocol driver_info
+xpp_fxloader 0x0003 0x04b4 0x8613 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
+xpp_fxloader 0x0003 0xe4e4 0x1130 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
+xpp_fxloader 0x0003 0xe4e4 0x1131 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
+xpp_fxloader 0x0003 0xe4e4 0x1140 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
+xpp_fxloader 0x0003 0xe4e4 0x1141 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
+xpp_fxloader 0x0003 0xe4e4 0x1150 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
+xpp_fxloader 0x0003 0xe4e4 0x1151 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
+xpp_fxloader 0x0003 0xe4e4 0x1160 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
+xpp_fxloader 0x0003 0xe4e4 0x1161 0x0000 0x0000 0x00 0x00 0x00 0x00 0x00 0x00 0x0
diff --git a/xpp/xpp_modprobe b/xpp/xpp_modprobe
new file mode 100644
index 0000000..e3bacc2
--- /dev/null
+++ b/xpp/xpp_modprobe
@@ -0,0 +1,10 @@
+# Some debugging options for the brave of heart:
+#options zaptel debug=1
+#options wcfxo debug=1
+#options xpp debug=1
+#options xpp_usb debug=1
+#options xpd_fxs debug=1
+#options xpd_fxo debug=1
+
+# For pre-loading of card modules (e.g: xpp_fxs)
+#install xpp_usb /sbin/modprobe xpd_fxs && /sbin/modprobe --ignore-install xpp_usb
diff --git a/xpp/xpp_sync b/xpp/xpp_sync
new file mode 100755
index 0000000..1438f50
--- /dev/null
+++ b/xpp/xpp_sync
@@ -0,0 +1,226 @@
+#! /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;
+BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); }
+
+use Zaptel::Xpp;
+use Zaptel::Xpp::Xbus;
+
+my $sync;
+my $autoselect;
+
+sub usage() {
+ print
+ "$0: show / set Astribank sync source\n".
+ "\n".
+ "Usage: $0 Show sync source.\n".
+ " $0 <auto|NN|zaptel> Set sync source.\n".
+ "";
+}
+
+if(@ARGV == 1) {
+ if ($ARGV[0] =~ /^(-h|--help|help)$/) {
+ usage;
+ exit(0);
+ }
+ $sync = shift;
+ $autoselect = 1 if $sync =~ /^auto$/i;
+}
+
+
+sub get_sorted_xpds() {
+ my @good_xpds;
+
+ foreach my $xbus (Zaptel::Xpp::xbuses('SORT_CONNECTOR')) {
+ next unless $xbus->status eq 'CONNECTED';
+ foreach my $xpd ($xbus->xpds()) {
+ my $isreg = $xpd->zt_registration();
+ if(!defined($isreg)) { # Failure
+ printf STDERR "%s: Failed %s\n", $xpd->fqn, $!;
+ next;
+ }
+ next unless $isreg; # Skip unregistered XPDs
+ push(@good_xpds, $xpd);
+ }
+ }
+ my @pri_nt_xpds = grep { $_->type =~ /(E1|T1|J1)_NT/; } @good_xpds;
+ my @pri_te_xpds = grep { $_->type =~ /(E1|T1|J1)_TE/; } @good_xpds;
+ my @bri_nt_xpds = grep { $_->type eq 'BRI_NT'; } @good_xpds;
+ my @bri_te_xpds = grep { $_->type eq 'BRI_TE'; } @good_xpds;
+ my @fxo_xpds = grep { $_->type eq 'FXO'; } @good_xpds;
+ my @fxs_xpds = grep { $_->type eq 'FXS'; } @good_xpds;
+
+ # Sync Priority
+ return
+ @pri_nt_xpds,
+ @bri_nt_xpds,
+ @fxo_xpds,
+ @pri_te_xpds,
+ @bri_te_xpds,
+ @fxs_xpds;
+}
+
+sub do_select(@) {
+ my $found;
+
+ foreach my $xpd (@_) {
+ my $xbus = $xpd->xbus;
+ my $busnum = $xbus->name;
+ die "Uknown bus name" unless $busnum;
+ $busnum =~ s/XBUS-//;
+ die "bad bus name" unless $busnum =~ /^\d+$/;
+ #printf "Setting sync: %-10s (%s)\n", $xpd->fqn, $xpd->type;
+ if(Zaptel::Xpp::sync($busnum)) {
+ #print "SET $busnum\n";
+ $found = 1;
+ last;
+ } else {
+ print STDERR "Failed to set $busnum: $!\n";
+ }
+ }
+}
+
+sub do_set($) {
+ my $sync = shift;
+ die "Failed to set sync to '$sync'" unless Zaptel::Xpp::sync($sync);
+}
+
+sub unique_xbus(@) {
+ my %seen;
+
+ grep { !$seen{$_->xbus}++; } @_;
+}
+
+my $curr_sync = Zaptel::Xpp::sync;
+my @sync_xpds = unique_xbus(get_sorted_xpds());
+
+sub show_sync() {
+ foreach my $xpd (@sync_xpds) {
+ my $xbus = $xpd->xbus;
+ my $xpdstr = '[ ' . $xbus->pretty_xpds . ' ]';
+ my $label = '[' . $xbus->label() . ']';
+ my $connector = '(' . $xbus->connector . ')';
+ my $mark = ($curr_sync =~ /\d+/ and $xbus->num == $curr_sync)?"+":"";
+ my $padding = ' ' x (40 - length $xpdstr);
+ printf " %1s %s %-25s %-14s %s\n", $mark, $xbus->name, $connector, $label, $xpdstr;
+ }
+}
+
+sub check_fxo_host_sync() {
+ my @host_synced_xpds = grep { $_->xbus->num() ne $curr_sync } @sync_xpds;
+ my @host_synced_fxos = grep($_->type eq 'FXO', @host_synced_xpds);
+ if(@host_synced_fxos) {
+ my @bad_xbus = map { $_->xbus } unique_xbus(@host_synced_fxos);
+ our $lines = join("\n\t", map { $_->name } @bad_xbus);
+ print STDERR <<"END";
+==================================================
+WARNING: FXO which is not the syncer cause bad PCM
+ Affected Astribanks are:
+--------------------------------------------------
+ $lines
+==================================================
+END
+ }
+}
+
+if($sync) {
+ if($autoselect) {
+ do_select(@sync_xpds);
+ } else {
+ $sync = uc($sync);
+ do_set($sync);
+ }
+ $curr_sync = Zaptel::Xpp::sync;
+ #print "New sync: ", Zaptel::Xpp::sync, "\n";
+} else {
+ print "Current sync: ", $curr_sync, "\n";
+ print "Best Available Syncers:\n";
+ show_sync;
+ check_fxo_host_sync;
+}
+
+__END__
+
+=head1 NAME
+
+xpp_sync - Handle sync selection of Xorcom Astribanks.
+
+=head1 SYNOPSIS
+
+xpp_sync [auto|zaptel|nn]
+
+=head1 DESCRIPTION
+
+On a normal operation one Astribank device provides timing for all the
+other Astribank devices.
+
+When run without parameters, xpp_sync will display a list of Astribanks
+(xbuses) that are connected and registered as Zaptel spans. The current
+xpp sync master will be marked.
+
+If you this an Astribank is connected and yet it does not appear on the
+output of xpp_sync, it may be unregistered. Try running zt_registration .
+
+=head2 Parameters
+
+=over
+
+=item auto
+
+Automatically selects the best Astribank for syncing.
+
+=item zaptel
+
+Gets synchronization from the Zaptel sync master.
+
+=item nn
+
+Sets XBUS-I<nn> as sync source.
+
+=back
+
+(Parameter name is case-insensitive)
+
+=head2 Example output:
+
+ Setting SYNC
+ Current sync: 01
+ Best Available Syncers:
+ + XBUS-01 (usb-0000:00:10.4-3) [usb:12345678] [ PRI_TE PRI_NT PRI_TE PRI_NT ]
+ XBUS-00 (usb-0000:00:10.4-2) [usb:QA-01] [ FXS FXO ]
+ ==================================================
+ WARNING: FXO which is not the syncer cause bad PCM
+ Affected Astribanks are:
+ --------------------------------------------------
+ XBUS-00
+ ==================================================
+
+In this example we see that the recommended xpp sync master is XBUS-02 -
+it is the first on the list. It is also the actual syncer, as we can see
+from the '+' beside it.
+
+xpp_sync is normally called from both the zaptel init.d script and the
+the Astribank udev 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
+or /etc/sysconfig/zaptel .
+
+=head1 FILES
+
+=over
+
+=item /proc/xpp/sync
+
+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).
+
+=back
diff --git a/xpp/xpp_timing b/xpp/xpp_timing
new file mode 100755
index 0000000..f87215c
--- /dev/null
+++ b/xpp/xpp_timing
@@ -0,0 +1,6 @@
+#! /bin/sh
+grep 'DRIFT:' /sys/bus/astribanks/devices/xbus-*/timing | sed \
+ -e 's,/sys/bus/astribanks/devices/,,' \
+ -e 's,/timing:,: ,' \
+ -e 's,DRIFT: ,,' \
+ -e 's/^[^:]*:/\U&/'
diff --git a/xpp/zapconf b/xpp/zapconf
new file mode 100755
index 0000000..7f94f6b
--- /dev/null
+++ b/xpp/zapconf
@@ -0,0 +1,603 @@
+#! /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;
+BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); }
+
+use Zaptel;
+use Zaptel::Xpp;
+use Zaptel::Config::Defaults;
+
+my %default_context = (
+ FXO => 'from-pstn',
+ FXS => 'from-internal',
+ IN => 'astbank-input',
+ OUT => 'astbank-output',
+ BRI_TE => 'from-pstn',
+ BRI_NT => 'from-internal',
+ E1_TE => 'from-pstn',
+ T1_TE => 'from-pstn',
+ J1_TE => 'from-pstn',
+ E1_NT => 'from-internal',
+ T1_NT => 'from-internal',
+ J1_NT => 'from-internal',
+ );
+
+my %default_group = (
+ FXO => 0,
+ FXS => "5",
+ IN => '',
+ OUT => '',
+ BRI_TE => 0,
+ BRI_NT => 6,
+ E1_TE => 0,
+ T1_TE => 0,
+ J1_TE => 0,
+ E1_NT => 6,
+ T1_NT => 6,
+ J1_NT => 6,
+ );
+
+my $fxs_default_start = 'ls';
+
+my %default_zaptel_signalling = (
+ FXO => 'fxsks',
+ FXS => "fxo{fxs_default_start}",
+ IN => "fxo{fxs_default_start}",
+ OUT => "fxo{fxs_default_start}",
+ );
+
+my %default_zapata_signalling = (
+ FXO => 'fxs_ks',
+ FXS => "fxo_{fxs_default_start}",
+ IN => "fxo_{fxs_default_start}",
+ OUT => "fxo_{fxs_default_start}",
+ );
+
+my $base_exten = 4000;
+my $fxs_immediate = 'no';
+my $lc_country = 'us';
+my $loadzone = $lc_country;
+my $defaultzone = $lc_country;
+my $bri_sig_style = 'bri_ptmp';
+my $brint_overlap = 'no';
+
+my %zaptel_default_vars = (
+ base_exten => \$base_exten,
+ fxs_immediate => \$fxs_immediate,
+ fxs_default_start => \$fxs_default_start,
+ lc_country => [
+ \$loadzone,
+ \$defaultzone,
+ ],
+ context_lines => \$default_context{FXO},
+ context_phones => \$default_context{FXS},
+ context_input => \$default_context{IN},
+ context_output => \$default_context{OUT},
+ group_phones => [
+ \$default_group{FXS},
+ \$default_group{IN},
+ \$default_group{OUT},
+ ],
+ group_lines => \$default_group{FXO},
+ ZAPBRI_SIGNALLING => \$bri_sig_style,
+ brint_overlap => \$brint_overlap,
+ );
+
+sub map_zaptel_defaults {
+ my %defaults = @_;
+ foreach my $name (keys %defaults) {
+ my $val = $defaults{$name};
+ my $ref = $zaptel_default_vars{$name};
+ my $type = ref $ref;
+ my @vars = ();
+ # Some broken shells (msh) export even variables
+ # That where not defined. Work around that.
+ next unless defined $val && $val ne '';
+ if($type eq 'SCALAR') {
+ @vars = ($ref);
+ } elsif($type eq 'ARRAY') {
+ @vars = @$ref;
+ } else {
+ die "$0: Don't know how to map '$name' (type=$type)\n";
+ }
+ foreach my $v (@vars) {
+ $$v = $val;
+ }
+ }
+}
+
+
+my $zapconf_file;
+my $zapatachannels_file;
+my $users_file;
+my $zapataconf_file;
+
+my %files = (
+ zaptel => { file => \$zapconf_file, func => \&gen_zaptelconf },
+ zapata => { file => \$zapatachannels_file, func => \&gen_zapatachannelsconf },
+ users => { file => \$users_file, func => \&gen_usersconf },
+ zapataconf => { file => \$zapataconf_file, func => \&gen_zapataconf },
+);
+
+my @default_files = ("zaptel", "zapata");
+
+my @spans = Zaptel::spans();
+
+sub bchan_range($) {
+ my $span = shift || die;
+ my $first_chan = ($span->chans())[0];
+ my $first_num = $first_chan->num();
+ my $range_start = $first_num;
+ my @range;
+ my $prev = undef;
+
+ die unless $span->is_digital();
+ foreach my $c (@{$span->bchan_list()}) {
+ my $curr = $c + $first_num;
+ if(!defined($prev)) {
+ $prev = $curr;
+ } elsif($curr != $prev + 1) {
+ push(@range, sprintf("%d-%d", $range_start, $prev));
+ $range_start = $curr;
+ }
+ $prev = $curr;
+ }
+ if($prev >= $first_num) {
+ push(@range, sprintf("%d-%d", $range_start, $prev));
+ }
+ return join(',', @range);
+}
+
+sub gen_zaptel_signalling($) {
+ my $chan = shift || die;
+ my $type = $chan->type;
+ my $num = $chan->num;
+
+ die "channel $num type $type is not an analog channel\n" if $chan->span->is_digital();
+ if($type eq 'EMPTY') {
+ printf "# channel %d, %s, no module.\n", $num, $chan->fqn;
+ return;
+ }
+ my $sig = $default_zaptel_signalling{$type} || die "unknown default zaptel signalling for chan $chan type $type";
+ if ($type eq 'IN') {
+ printf "# astbanktype: input\n";
+ } elsif ($type eq 'OUT') {
+ printf "# astbanktype: output\n";
+ }
+ printf "$sig=$num\n";
+}
+
+my $bri_te_last_timing = 1;
+
+sub gen_zaptel_digital($) {
+ my $span = shift || die;
+ my $num = $span->num() || die;
+ die "Span #$num is analog" unless $span->is_digital();
+ my $termtype = $span->termtype() || die "$0: Span #$num -- unkown termtype [NT/TE]\n";
+ my $timing;
+ my $lbo = 0;
+ my $framing = $span->framing() || die "$0: No framing information for span #$num\n";
+ my $coding = $span->coding() || die "$0: No coding information for span #$num\n";
+ my $span_crc4 = $span->crc4();
+ $span_crc4 = (defined $span_crc4) ? ",$span_crc4" : '';
+ my $span_yellow = $span->yellow();
+ $span_yellow = (defined $span_yellow) ? ",$span_yellow" : '';
+
+ $timing = ($termtype eq 'NT') ? 0 : $bri_te_last_timing++;
+ printf "span=%d,%d,%d,%s,%s%s%s\n",
+ $num,
+ $timing,
+ $lbo,
+ $framing,
+ $coding,
+ $span_crc4,
+ $span_yellow;
+ printf "# termtype: %s\n", lc($termtype);
+ printf "bchan=%s\n", bchan_range($span);
+ my $dchan = $span->dchan();
+ printf "dchan=%d\n", $dchan->num();
+}
+
+sub gen_zaptelconf($) {
+ my $file = shift || die;
+ rename "$file", "$file.bak"
+ or $! == 2 # ENOENT (No dependency on Errno.pm)
+ or die "Failed to backup old config: $!\n";
+ 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);
+ print <<"HEAD";
+# Zaptel Configuration File
+#
+# This file is parsed by the Zaptel Configurator, ztcfg
+#
+HEAD
+ foreach my $span (@spans) {
+ printf "# Span %d: %s %s\n", $span->num, $span->name, $span->description;
+ if($span->is_digital()) {
+ gen_zaptel_digital($span);
+ } else {
+ foreach my $chan ($span->chans()) {
+ if(1 || !defined $chan->type) {
+ my $type = $chan->probe_type;
+ my $num = $chan->num;
+ die "Failed probing type for channel $num"
+ unless defined $type;
+ $chan->type($type);
+ }
+ gen_zaptel_signalling($chan);
+ }
+ }
+ print "\n";
+ }
+ print <<"TAIL";
+# Global data
+
+loadzone = $loadzone
+defaultzone = $defaultzone
+TAIL
+ close F;
+ select $old;
+}
+
+my %DefaultConfigs = (
+ context => 'default',
+ group => '63', # FIXME: should not be needed.
+ overlapdial => 'no',
+ busydetect => 'no',
+ rxgain => 0,
+ txgain => 0,
+);
+
+sub reset_zapata_values {
+ foreach my $arg (@_) {
+ if (exists $DefaultConfigs{$arg}) {
+ print "$arg = $DefaultConfigs{$arg}\n";
+ } else {
+ print "$arg =\n";
+ }
+ }
+}
+
+sub gen_zapata_digital($) {
+ my $span = shift || die;
+ my $num = $span->num() || die;
+ die "Span #$num is analog" unless $span->is_digital();
+ my $type = $span->type() || die "$0: Span #$num -- unkown type\n";
+ my $termtype = $span->termtype() || die "$0: Span #$num -- unkown termtype [NT/TE]\n";
+ my $group = $default_group{"$type"};
+ my $context = $default_context{"$type"};
+ my @to_reset = qw/context group/;
+
+ die "$0: missing default group (termtype=$termtype)\n" unless defined($group);
+ die "$0: missing default context\n" unless $context;
+
+ my $sig = $span->signalling || die "missing signalling info for span #$num type $type";
+ grep($bri_sig_style eq $_, 'bri', 'bri_ptmp', 'pri') or die "unknown signalling style for BRI";
+ if($span->is_bri() and $bri_sig_style eq 'bri_ptmp') {
+ $sig .= '_ptmp';
+ }
+ if ($span->is_bri() && $termtype eq 'NT' && $brint_overlap eq 'yes') {
+ print "overlapdial = yes\n";
+ push(@to_reset, qw/overlapdial/);
+ }
+
+ $group .= "," . (10 + $num); # Invent unique group per span
+ printf "group=$group\n";
+ printf "context=$context\n";
+ printf "switchtype = %s\n", $span->switchtype;
+ printf "signalling = %s\n", $sig;
+ printf "channel => %s\n", bchan_range($span);
+ reset_zapata_values(@to_reset);
+}
+
+sub gen_zapata_channel($) {
+ my $chan = shift || die;
+ my $type = $chan->type;
+ my $num = $chan->num;
+ die "channel $num type $type is not an analog channel\n" if $chan->span->is_digital();
+ my $exten = $base_exten + $num;
+ my $sig = $default_zapata_signalling{$type};
+ my $context = $default_context{$type};
+ my $group = $default_group{$type};
+ my $callerid;
+ my $immediate;
+
+ return if $type eq 'EMPTY';
+ die "missing default_zapata_signalling for chan #$num type $type" unless $sig;
+ $callerid = ($type eq 'FXO')
+ ? 'asreceived'
+ : sprintf "\"Channel %d\" <%04d>", $num, $exten;
+ if($type eq 'IN') {
+ $immediate = 'yes';
+ }
+ # FIXME: $immediage should not be set for 'OUT' channels, but meanwhile
+ # it's better to be compatible with genzaptelconf
+ $immediate = 'yes' if $fxs_immediate eq 'yes' and $sig =~ /^fxo_/;
+ my $signalling = $chan->signalling;
+ $signalling = " " . $signalling if $signalling;
+ my $info = $chan->info;
+ $info = " " . $info if $info;
+ printf ";;; line=\"%d %s%s%s\"\n", $num, $chan->fqn, $signalling, $info;
+ printf "signalling=$sig\n";
+ printf "callerid=$callerid\n";
+ printf "mailbox=%04d\n", $exten unless $type eq 'FXO';
+ if(defined $group) {
+ printf "group=$group\n";
+ }
+ printf "context=$context\n";
+ printf "immediate=$immediate\n" if defined $immediate;
+ printf "channel => %d\n", $num;
+ # Reset following values to default
+ printf "callerid=\n";
+ printf "mailbox=\n" unless $type eq 'FXO';
+ if(defined $group) {
+ printf "group=\n";
+ }
+ printf "context=default\n";
+ printf "immediate=no\n" if defined $immediate;
+ print "\n";
+}
+
+sub gen_zapatachannelsconf($) {
+ my $file = shift || die;
+ rename "$file", "$file.bak"
+ or $! == 2 # ENOENT (No dependency on Errno.pm)
+ or die "Failed to backup old config: $!\n";
+ 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);
+ print <<"HEAD";
+; Zaptel Channels Configurations (zapata.conf)
+;
+; This is not intended to be a complete zapata.conf. Rather, it is intended
+; to be #include-d by /etc/zapata.conf that will include the global settings
+;
+
+HEAD
+ foreach my $span (@spans) {
+ printf "; Span %d: %s %s\n", $span->num, $span->name, $span->description;
+ if($span->is_digital()) {
+ gen_zapata_digital($span);
+ } else {
+ foreach my $chan ($span->chans()) {
+ gen_zapata_channel($chan);
+ }
+ }
+ print "\n";
+ }
+ close F;
+ select $old;
+}
+
+sub gen_users_channel($) {
+ my $chan = shift || die;
+ my $type = $chan->type;
+ my $num = $chan->num;
+ die "channel $num type $type is not an analog channel\n" if $chan->span->is_digital();
+ my $exten = $base_exten + $num;
+ my $sig = $default_zapata_signalling{$type};
+ my $full_name = "$type $num";
+
+ die "missing default_zapata_signalling for chan #$num type $type" unless $sig;
+ print << "EOF";
+[$exten]
+callwaiting = yes
+context = numberplan-custom-1
+fullname = $full_name
+cid_number = $exten
+hasagent = no
+hasdirectory = no
+hasiax = no
+hasmanager = no
+hassip = no
+hasvoicemail = yes
+host = dynamic
+mailbox = $exten
+threewaycalling = yes
+vmsecret = 1234
+secret = 1234
+signalling = $sig
+zapchan = $num
+registeriax = no
+registersip = no
+canreinvite = no
+nat = no
+dtmfmode = rfc2833
+disallow = all
+allow = all
+
+EOF
+}
+
+# generate users.conf . The specific users.conf is strictly oriented
+# towards using with the asterisk-gui .
+#
+# This code could have generated a much simpler and smaller
+# configuration file, had there been minimal level of support for
+# configuration templates in the asterisk configuration rewriting. Right
+# now Asterisk's configuration rewriting simply freaks out in the face
+# of templates: http://bugs.digium.com/11442 .
+sub gen_usersconf($) {
+ my $file = shift || die;
+ rename "$file", "$file.bak"
+ or $! == 2 # ENOENT (No dependency on Errno.pm)
+ or die "Failed to backup old config: $!\n";
+ open(F, ">$file") || die "$0: Failed to open $file: $!\n";
+ my $old = select F;
+ print <<"HEAD";
+;!
+;! Automatically generated configuration file
+;! Filename: @{[basename($file)]} ($file)
+;! Generator: $0
+;! Creation Date: @{[scalar(localtime)]}
+;!
+[general]
+;
+; Full name of a user
+;
+fullname = New User
+;
+; Starting point of allocation of extensions
+;
+userbase = @{[$base_exten+1]}
+;
+; Create voicemail mailbox and use use macro-stdexten
+;
+hasvoicemail = yes
+;
+; Set voicemail mailbox @{[$base_exten+1]} password to 1234
+;
+vmsecret = 1234
+;
+; Create SIP Peer
+;
+hassip = no
+;
+; Create IAX friend
+;
+hasiax = no
+;
+; Create Agent friend
+;
+hasagent = no
+;
+; Create H.323 friend
+;
+;hash323 = yes
+;
+; Create manager entry
+;
+hasmanager = no
+;
+; Remaining options are not specific to users.conf entries but are general.
+;
+callwaiting = yes
+threewaycalling = yes
+callwaitingcallerid = yes
+transfer = yes
+canpark = yes
+cancallforward = yes
+callreturn = yes
+callgroup = 1
+pickupgroup = 1
+localextenlength = @{[length($base_exten)]}
+
+
+HEAD
+ foreach my $span (@spans) {
+ next unless grep { $_ eq $span->type} ( 'FXS', 'IN', 'OUT' );
+ printf "; Span %d: %s %s\n", $span->num, $span->name, $span->description;
+ foreach my $chan ($span->chans()) {
+ gen_users_channel($chan);
+ }
+ print "\n";
+ }
+ close F;
+ select $old;
+}
+
+sub gen_zapataconf($) {
+ my $file = shift || die;
+ open(F, ">>$file") || die "$0: Failed to open $file: $!\n";
+ my $old = select F;
+ foreach my $span (@spans) {
+ next unless $span->type eq 'FXO';
+ my $current_sig = "";
+ for my $chan ($span->chans()) {
+ my $chan_num = $chan->num;
+ if ($default_zapata_signalling{$chan->type} ne $current_sig) {
+ $current_sig = $default_zapata_signalling{$chan->type};
+ print "\nsignalling = $current_sig";
+ print "\nchannel => $chan_num";
+ } else {
+ print ",$chan_num";
+ }
+ }
+ print "\n";
+ }
+ close F;
+ select $old;
+}
+
+sub set_defaults {
+ # Source default files
+ my ($default_file, %source_defaults) =
+ Zaptel::Config::Defaults::source_vars(keys(%zaptel_default_vars));
+ map_zaptel_defaults(%source_defaults);
+ # Fixups
+ foreach my $val (values %default_zaptel_signalling, values %default_zapata_signalling) {
+ $val =~ s/{fxs_default_start}/$fxs_default_start/g;
+ }
+ $zapconf_file = $ENV{ZAPCONF_FILE} || "/etc/zaptel.conf";
+ $zapatachannels_file = $ENV{ZAPATA_FILE} || "/etc/asterisk/zapata-channels.conf";
+ $users_file = $ENV{USERS_FILE} || "/etc/asterisk/users.conf";
+ $zapataconf_file = $ENV{ZAPATACONF_FILE} || "/etc/asterisk/zapata.conf";
+}
+
+sub parse_args {
+ return if @ARGV == 0;
+ @default_files = ();
+ for my $file (@ARGV) {
+ die "$0: Unknown file '$file'" unless defined $files{$file};
+ push @default_files, $file;
+ }
+}
+
+sub generate_files {
+ for my $file (@default_files) {
+ &{$files{$file}->{func}}(${$files{$file}->{file}});
+ }
+}
+set_defaults;
+parse_args;
+generate_files;
+
+__END__
+
+=head1 NAME
+
+zapconf - Generate configuration for zaptel channels.
+
+=head1 SYNOPSIS
+
+zapconf [FILES...]
+
+=head1 DESCRIPTION
+
+This script generate configuration files for Zaptel hardware.
+Currently it can generate three files: zaptel, zapata, users and zapataconf (see below).
+Without arguments, it generates only zaptel and zapata.
+
+=over 4
+
+=item zaptel - /etc/zaptel.conf
+
+Configuration for ztcfg(1). It's location may be overriden by the
+environment variable ZAPCONF_FILE.
+
+=item zapata - /etc/asterisk/zapata-channels.conf
+
+Configuration for asterisk(1). It should be included in the main /etc/asterisk/zapata.conf.
+It's location may be overriden by the environment variable ZAPATA_FILE.
+
+=item users - /etc/asterisk/users.conf
+
+Configuration for asterisk(1) and AsteriskGUI.
+It's location may be overriden by the environment variable USERS_FILE.
+
+=item zapataconf - /etc/asterisk/zapata.conf
+
+Configuration for asterisk(1) and AsteriskGUI.
+It's location may be overriden by the environment variable ZAPATACONF_FILE.
+
+
+=back
diff --git a/xpp/zaptel-helper b/xpp/zaptel-helper
new file mode 100644
index 0000000..1b2ca45
--- /dev/null
+++ b/xpp/zaptel-helper
@@ -0,0 +1,401 @@
+#!/bin/sh
+
+# zaptel-helper: helper script/functions for Zaptel
+
+# Wrriten by Tzafrir Cohen <tzafrir.cohen@xorcom.com>
+# Copyright (C) 2006-2007, 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.
+
+# Should be possible to run with -e set. This is also recommended.
+
+# Constants:
+# maximal time (in seconds) to wait for /dev/zap/dtl to appear after
+# loading zaptel
+DEVZAP_TIMEOUT=${DEVZAP_TIMEOUT:-20}
+
+# Zaptel modules we'll try when detecting zaptel hardware:
+ALL_MODULES="${ALL_MODULES:-zaphfc qozap ztgsm wctdm wctdm24xxp wcfxo wcfxs pciradio tor2 torisa wct1xxp wct4xxp wcte11xp wanpipe wcusb xpp_usb}"
+
+# Where do we write the list of modules we detected (if at all):
+MODLIST_FILE_DEBIAN=${MODLIST_FILE_DEBIAN:-/etc/modules}
+MODLIST_FILE_REDHAT=${MODLIST_FILE_REDHAT:-/etc/sysconfig/zaptel}
+
+# The location of of the fxotune binary
+FXOTUNE="${FXOTUNE:-/usr/sbin/fxotune}"
+FXOTUNE_CONF="${FXOTUNE_CONF:-/etc/fxotune.conf}"
+
+# this is the context FXO zaptel channels are in.
+# See run_fxotune.
+FXO_CONTEXT=${FXO_CONTEXT:-from-pstn}
+
+ZTCFG="${ZTCFG:-/sbin/ztcfg}"
+
+# TODO: this may not be appropriate for a general-purpose script.
+# However you should not use a direct 'echo' to write output to the user
+#, to make it simple to override.
+say() {
+ echo "$@"
+}
+
+error() {
+ echo >&2 "$@"
+}
+
+die() {
+ error "$@"
+ exit 1
+}
+
+
+#############################################################################
+#####
+##### Init helper functions
+#####
+
+
+# Wait for udev to generate /dev/zap/ctl, if needed:
+wait_for_zapctl() {
+ # if device file already exists, or if zaptel has failed to load:
+ # no point waiting.
+ if [ -c /dev/zap/ctl ] || ! grep -q zaptel /proc/modules ; then
+ return
+ fi
+
+ say "Waiting for /dev/zap/ctl to be generated"
+ devzap_found=0
+ for i in `seq $DEVZAP_TIMEOUT`; do
+ sleep 1
+ if [ -c /dev/zap/ctl ]; then
+ devzap_found=1
+ break
+ fi
+ done
+ if [ "$devzap_found" != 1 ]; then
+ say "Still no /dev/zap/ctl after $devzap_timeout seconds."
+ error "No /dev/zap/ctl: cannot run ztcfg. Aborting."
+ fi
+}
+
+# load the fxotune parameters
+# FIXME: /etc/fxotune.conf is a bad location for that file .
+# /etc/zaptel/fxotune.conf?
+fxotune_load() {
+ if [ -x "$FXOTUNE" ] && [ -r "FXOTUNE_CONF" ]; then
+ $FROTUNE -s
+ fi
+}
+
+# If there is no zaptel timing source, load
+# ztdummy. Other modules should have been loaded by
+# now.
+guarantee_timing_source() {
+ if ! head -c 0 /dev/zap/pseudo 2>/dev/null
+ then modprobe ztdummy || true # will fail if there is no module package
+ fi
+}
+
+kill_zaptel_users() {
+ fuser -k /dev/zap/*
+}
+
+# recursively unload a module and its dependencies, if possible.
+# where's modprobe -r when you need it?
+# inputs: module to unload.
+# returns: the result from
+unload_module() {
+ module="$1"
+ line=`lsmod 2>/dev/null | grep "^$1 "`
+ if [ "$line" = '' ]; then return; fi # module was not loaded
+
+ set -- $line
+ # $1: the original module, $2: size, $3: refcount, $4: deps list
+ mods=`echo $4 | tr , ' '`
+ for mod in $mods; do
+ # run in a subshell, so it won't step over our vars:
+ (unload_module $mod)
+ # TODO: the following is probably the error handling we want:
+ # if [ $? != 0 ]; then return 1; fi
+ done
+ rmmod $module
+}
+
+# sleep a while until the xpp modules fully register
+wait_for_xpp() {
+ if [ -d /proc/xpp ]
+ then
+ # wait for the XPDs to register:
+ # TODO: improve error reporting and produce a messagee here
+ cat /proc/xpp/XBUS-*/waitfor_xpds 2>/dev/null >/dev/null || true
+ fi
+}
+
+zap_reg_xpp() {
+ if [ ! -d /proc/xpp ]; then return; fi
+
+ # Get a list of connected Astribank devices, sorted by the name of
+ # the USB connector. That order is rather arbitrary, but will not
+ # change without changes to the cabling.
+ xbusses=`sort -k 2 /proc/xpp/xbuses | awk -F: '/STATUS=connected/ {print $1}'`
+
+ # get a list of XPDs that were not yet registered as zaptel spans.
+ # this will be the case if you set the parameter zap_autoreg=0 to
+ # the module xpp
+ # Append /dev/null to provide a valid file name in case of an empty pattern.
+ xbusses_pattern=`echo $xbusses| sed -e 's|XBUS-[0-9]*|/proc/xpp/&/XPD-*/zt_registration|g'`' /dev/null'
+ xpds_to_register=`grep -l 0 $xbusses_pattern 2>/dev/null` || true
+ for file in $xpds_to_register; do
+ echo 1 >$file
+ done
+}
+
+# Set the sync source of the Astribank to the right value
+fix_asterisbank_sync() {
+ # do nothing if module not present
+ if [ ! -d /proc/xpp ]; then return; fi
+
+ #if ! grep -q '^HOST' /proc/xpp/sync 2>/dev/null; then return; fi
+
+ case "$XPP_SYNC" in
+ n*|N*) return;;
+ host|HOST) sync_value="HOST";;
+ [0-9]*)sync_value="$XPP_SYNC";;
+ *)
+ # find the number of the first bus, and sync from it:
+ fxo_pat=`awk -F: '/STATUS=connected/{print $1}' /proc/xpp/xbuses | sed -e 's|.*|/proc/xpp/&/*/fxo_info|'`
+ # find the first FXO unit, and set it as the sync master
+ bus=`ls -1 $fxo_pat 2> /dev/null | head -n1 | cut -d- -f2 | cut -d/ -f1`
+
+ # do nothing if there is no bus:
+ case "$bus" in [0-9]*):;; *) return;; esac
+ sync_value="$bus 0"
+ ;;
+ esac
+ # the built-in echo of bash fails to print a proper error on failure
+ if ! /bin/echo "$sync_value" >/proc/xpp/sync
+ then
+ error "Updating XPP sync source failed (used XPP_SYNC='$XPP_SYNC')"
+ fi
+}
+
+run_adj_clock() {
+ if [ "$XPP_RUN_ADJ_CLOCK" = '' ]; then return; fi
+
+ # daemonize adj_clock:
+ (adj_clock </dev/null >/dev/null 2>&1 &)&
+}
+
+init_astribank() {
+ wait_for_xpp
+ zap_reg_xpp
+ fix_asterisbank_sync
+ run_adj_clock
+}
+
+xpp_do_blink() {
+ val="$1"
+ shift
+ for xbus in $*
+ do
+ for xpd in /proc/xpp/XBUS-"$xbus"/XPD-*
+ do
+ echo "$val" > "$xpd/blink"
+ done
+ done
+}
+
+xpp_blink() {
+ xbuses=`grep STATUS=connected /proc/xpp/xbuses | sed -e 's/^XBUS-//' -e 's/:.*$//'`
+ num=`echo $1 | tr -c -d 0-9`
+ case "$num" in
+ [0-9]*)
+ shift
+ xpp_do_blink 1 $xbuses
+ sleep 2
+ xpp_do_blink 0 $xbuses
+ ;;
+ *)
+ shift
+ echo 1>&2 Enumerating $xbuses
+ xpp_do_blink 0 $xbuses
+ for i in $xbuses
+ do
+ echo "BLINKING: $i"
+ xpp_do_blink 1 "$i"
+ sleep 2
+ xpp_do_blink 0 "$i"
+ done
+ ;;
+ esac
+}
+
+# The current Debian start function.
+# The function is not responsible for loading the zaptel modules:
+# they will be loaded beforehand.
+debian_start() {
+ wait_for_xpp
+ zap_reg_xpp
+ fix_asterisbank_sync
+ wait_for_zapctl
+
+ if [ -r /etc/fxotune.conf ] && [ -x $FXOTUNE ]; then
+ $FXOTUNE -s
+ fi
+
+ # configure existing modules:
+ $ZTCFG
+}
+
+
+# run_fxotune: destroy all FXO channels and run fxotune.
+# This allows running fxotune without completly shutting down Asterisk.
+#
+# A simplistic assumption: every zaptel channel in the context from-pstn
+# is a FXO ones.
+# or rather: all tunable FXO channels are in the context from-pstn are
+# not defined by zaptel.
+run_fxotune() {
+ zap_fxo_chans=`asterisk -rx "zap show channels" | awk "/$FXO_CONTEXT/{print \$1}"`
+ xpp_fxo_chans=`cat /proc/zaptel/* | awk '/XPP_FXO/{print $1}'`
+ for chan in $xpp_fxo_chans $zap_fxo_chans; do
+ asterisk -rx "zap destroy channel $chan"
+ done
+ $FXOTUNE -i
+ asterisk -rx "zap restart"
+}
+
+
+# recursively unload a module and its dependencies, if possible.
+# where's modprobe -r when you need it?
+# inputs: module to unload.
+unload_module() {
+ set +e
+ module="$1"
+ line=`lsmod 2>/dev/null | grep "^$module "`
+ if [ "$line" = '' ]; then return; fi # module was not loaded
+
+ set -- $line
+ # $1: the original module, $2: size, $3: refcount, $4: deps list
+ mods=`echo $4 | tr , ' '`
+ # xpd_fxs actually sort of depends on xpp:
+ case "$module" in xpd_*) mods="xpp_usb $mods";; esac
+ for mod in $mods; do
+ # run in a subshell, so it won't step over our vars:
+ (unload_module $mod)
+ done
+ rmmod $module || true
+ set -e
+}
+
+unload() {
+ unload_module zaptel
+}
+
+# sleep a while until the xpp modules fully register
+wait_for_xpp() {
+ if [ -d /proc/xpp ] && \
+ [ "`cat /sys/module/xpp/parameters/zap_autoreg`" = 'Y' ]
+ then
+ # wait for the XPDs to register:
+ # TODO: improve error reporting and produce a messagee here
+ cat /proc/xpp/XBUS-*/waitfor_xpds 2>/dev/null >/dev/null || true
+ fi
+}
+
+#############################################################################
+#####
+##### Hardware detection functions
+#####
+
+load_modules() {
+ say "Test Loading modules:"
+ for i in $ALL_MODULES
+ do
+ lines_before=`count_proc_zap_lines`
+ args="${i}_args"
+ eval "args=\$$args"
+ # a module is worth listing if it:
+ # a. loaded successfully, and
+ # b. added channels lines under /proc/zaptel/*
+ if /sbin/modprobe $i $args 2> /dev/null
+ then
+ check=0
+ case "$i" in
+ xpp_usb) check=`grep 'STATUS=connected' 2>/dev/null /proc/xpp/xbuses | wc -l` ;;
+ # FIXME: zttranscode will always load, and will never
+ # add a span. Maybe try to read from /dev/zap/transcode .
+ zttranscode) : ;;
+ *) if [ $lines_before -lt `count_proc_zap_lines` ]; then check=1; fi ;;
+ esac
+ if [ "$check" != 0 ]
+ then
+ probed_modules="$probed_modules $i"
+ say " ok $i $args"
+ else
+ say " - $i $args"
+ rmmod $i
+ fi
+ else
+ say " - $i $args"
+ fi
+ done
+}
+
+update_module_list_debian() {
+ say "Updating Debian modules list $MODLIST_FILE_DEBIAN."
+ del_args=`for i in $ALL_MODULES ztdummy
+ do
+ echo "$i" | sed s:.\*:-e\ '/^&/d':
+ done`
+ add_args=`for i in $*
+ do
+ echo "$i" | sed s:.\*:-e\ '\$a&':
+ done`
+
+ sed -i.bak $del_args "$MODLIST_FILE_DEBIAN"
+ for i in $*
+ do
+ echo "$i"
+ done >> "$MODLIST_FILE_DEBIAN"
+}
+
+update_module_list_redhat() {
+ say "Updating modules list in zaptel init config $MODLIST_FILE_REDHAT."
+ sed -i.bak -e '/^MODULES=/d' "$MODLIST_FILE_REDHAT"
+ echo "MODULES=\"$*\"" >> "$MODLIST_FILE_REDHAT"
+}
+
+update_module_list() {
+ if [ -f "$MODLIST_FILE_DEBIAN" ]; then
+ update_module_list_debian "$@"
+ elif [ -f "$MODLIST_FILE_REDHAT" ]; then
+ update_module_list_redhat "$@"
+ else
+ die "Can't find a modules list to update. Tried: $MODLIST_FILE_DEBIAN, $MODLIST_FILE_REDHAT. Aborting"
+ fi
+}
+
+
+
+
+
+
+# unless we wanted to use this as a set of functions, run
+# the given function with its parameters:
+if [ "$ZAPHELPER_ONLY_INCLUDE" = '' ]; then
+ "$@"
+fi
diff --git a/xpp/zaptel_drivers b/xpp/zaptel_drivers
new file mode 100755
index 0000000..d7904c0
--- /dev/null
+++ b/xpp/zaptel_drivers
@@ -0,0 +1,9 @@
+#! /usr/bin/perl -w
+use strict;
+use File::Basename;
+BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); }
+
+use Zaptel::Hardware;
+
+my $hardware = Zaptel::Hardware->scan;
+print join("\n", $hardware->drivers),"\n";
diff --git a/xpp/zaptel_hardware b/xpp/zaptel_hardware
new file mode 100755
index 0000000..004a44b
--- /dev/null
+++ b/xpp/zaptel_hardware
@@ -0,0 +1,164 @@
+#! /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::Span;
+use Zaptel::Xpp;
+use Zaptel::Xpp::Xbus;
+use Zaptel::Hardware;
+
+sub usage {
+ die "Usage: $0 [-v][-x]\n";
+}
+
+our ($opt_v, $opt_x);
+getopts('vx') || usage;
+@ARGV == 0 or usage;
+
+my $hardware = Zaptel::Hardware->scan;
+my @spans = Zaptel::spans;
+
+sub show_xbus($) {
+ my $xbus = shift or die;
+ my @xpds = $xbus->xpds;
+ my $label = '[' . $xbus->label() . ']';
+ my $connector = ($xbus->status eq 'CONNECTED') ? $xbus->connector : "MISSING";
+ printf " LABEL=%-20s CONNECTOR=%-20s\n", $label, $connector;
+ foreach my $xpd (@xpds) {
+ my $reg = $xpd->zt_registration;
+ my $span;
+ my $spanstr;
+ if($reg && @spans) {
+ ($span) = grep { $_->name eq $xpd->fqn } @spans;
+ $spanstr = ($span) ? ("Span " . $span->num) : "";
+ } else {
+ $spanstr = "Unregistered";
+ }
+ 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;
+ }
+}
+
+my %seen;
+my $format = "%-20s %-12s %4s:%4s %s\n";
+
+sub show_disconnected(%) {
+ my %seen = @_;
+
+ my $notified_lost = 0;
+ foreach my $xbus (Zaptel::Xpp::xbuses('SORT_CONNECTOR')) {
+ 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;
+ }
+ }
+}
+
+foreach my $dev ($hardware->device_list) {
+ my $driver = $dev->driver || "";
+ my $xbus;
+ my $loaded;
+ if($dev->is_astribank) {
+ $xbus = $dev->xbus;
+ }
+ $loaded = $dev->loaded;
+ warn "driver should be '$driver' but is actually '$loaded'\n"
+ if defined($loaded) && $driver ne $loaded;
+ $driver = "$driver" . (($loaded) ? "+" : "-");
+ my $description = $dev->description || "";
+ printf $format, $dev->hardware_name, $driver, $dev->vendor, $dev->product, $description;
+ if(!defined $xbus || !$xbus) {
+ next;
+ }
+ $seen{$xbus->name} = 1;
+ show_xbus($xbus) if $opt_v;
+}
+
+show_disconnected(%seen) if $opt_x;
+
+__END__
+
+=head1 NAME
+
+zaptel_hardware - Shows Zaptel hardware devices.
+
+=head1 SYNOPSIS
+
+zaptel_hardware [-v][-x]
+
+=head1 OPTIONS
+
+=over
+
+=item -v
+
+Verbose ouput - show spans used by each device etc. Currently only
+implemented for the Xorcom Astribank.
+
+=item -x
+
+Show disconnected Astribank unit, if any.
+
+=back
+
+=head1 DESCRIPTION
+
+Show all zaptel hardware devices. Devices are recognized according to
+lists of PCI and USB IDs in Zaptel::Hardware::PCI.pm and
+Zaptel::Hardware::USB.pm . For PCI it is possible to detect by
+sub-vendor and sub-product ID as well.
+
+The first output column is the connector: a bus specific field that
+shows where this device is.
+
+The second field shows which driver should handle the device. a "-" sign
+marks that the device is not yet handled by this driver. A "+" sign
+means that the device is handled by the driver.
+
+For the Xorcom Astribank (and in the future: for other Zaptel devices)
+some further information is provided from the driver. Those extra lines
+always begin with spaces.
+
+Example output:
+
+Without drivers loaded:
+
+ usb:001/002 xpp_usb- e4e4:1152 Astribank-multi FPGA-firmware
+ usb:001/003 xpp_usb- e4e4:1152 Astribank-multi FPGA-firmware
+ pci:0000:01:0b.0 wctdm- e159:0001 Wildcard TDM400P REV H
+
+With drivers loaded, without -v:
+ usb:001/002 xpp_usb+ e4e4:1152 Astribank-multi FPGA-firmware
+ usb:001/003 xpp_usb+ e4e4:1152 Astribank-multi FPGA-firmware
+ pci:0000:01:0b.0 wctdm+ e159:0001 Wildcard TDM400P REV E/F
+
+With drivers loaded, with -v:
+ usb:001/002 xpp_usb+ e4e4:1152 Astribank-multi FPGA-firmware
+ LABEL=[usb:123] CONNECTOR=usb-0000:00:1d.7-1
+ XBUS-00/XPD-00: FXS Span 2
+ XBUS-00/XPD-10: FXS Span 3
+ XBUS-00/XPD-20: FXS Span 4
+ XBUS-00/XPD-30: FXS Span 5
+ usb:001/003 xpp_usb+ e4e4:1152 Astribank-multi FPGA-firmware
+ LABEL=[usb:4567] CONNECTOR=usb-0000:00:1d.7-4
+ XBUS-01/XPD-00: FXS Span 6 XPP-SYNC
+ XBUS-01/XPD-10: FXO Span 7
+ XBUS-01/XPD-20: FXO Span 8
+ XBUS-01/XPD-30: FXO Span 9
+ pci:0000:01:0b.0 wctdm+ e159:0001 Wildcard TDM400P REV E/F
+
diff --git a/xpp/zconf/Zaptel.pm b/xpp/zconf/Zaptel.pm
new file mode 100644
index 0000000..a7e7d6c
--- /dev/null
+++ b/xpp/zconf/Zaptel.pm
@@ -0,0 +1,68 @@
+package Zaptel;
+#
+# 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 Zaptel::Span;
+
+=head1 NAME
+
+Zaptel - Perl interface to Zaptel information
+
+This package allows access from Perl to information about Zaptel
+hardware and loaded Zaptel devices.
+
+=head1 SYNOPSIS
+
+ # Listing channels in analog spans:
+ use Zaptel;
+ # scans system:
+ my @xbuses = Zaptel::spans();
+ for my $span (@spans) {
+ next if ($span->is_digital);
+ $span->num. " - [". $span->type ."] ". $span->name. "\n";
+ for my $chan ($span->chans) {
+ print " - ".$chan->num . " - [". $chan->type. "] ". $chan->fqn". \n";
+ }
+ }
+=cut
+
+my $proc_base = "/proc/zaptel";
+
+=head1 spans()
+
+Returns a list of span objects, ordered by span number.
+
+=cut
+
+sub spans() {
+ my @spans;
+
+ -d $proc_base or return ();
+ foreach my $zfile (glob "$proc_base/*") {
+ $zfile =~ s:$proc_base/::;
+ my $span = Zaptel::Span->new($zfile);
+ push(@spans, $span);
+ }
+ @spans = sort { $a->num <=> $b->num } @spans;
+ return @spans;
+}
+
+=head1 SEE ALSO
+
+Span objects: L<Zaptel::Span>.
+
+Zaptel channels objects: L<Zaptel::Chan>.
+
+Zaptel hardware devices information: L<Zaptel::Hardware>.
+
+Xorcom Astribank -specific information: L<Zaptel::Xpp>.
+
+=cut
+
+1;
diff --git a/xpp/zconf/Zaptel/Chans.pm b/xpp/zconf/Zaptel/Chans.pm
new file mode 100644
index 0000000..6f83f77
--- /dev/null
+++ b/xpp/zconf/Zaptel/Chans.pm
@@ -0,0 +1,202 @@
+package Zaptel::Chans;
+#
+# 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 Zaptel::Utils;
+
+=head1 NAME
+
+Zaptel::Chans - Perl interface to a Zaptel channel information
+
+This package allows access from perl to information about a Zaptel
+channel. It is part of the Zaptel Perl package.
+
+=head1 battery()
+
+Returns 1 if channel reports to have battery (A remote PBX connected to
+an FXO port), 0 if channel reports to not have battery and C<undef>
+otherwise.
+
+Currently only wcfxo and Astribank FXO modules report battery. For the
+rest of the channels
+
+=head1 fqn()
+
+(Fully Qualified Name) Returns the full "name" of the channel.
+
+=head1 index()
+
+Returns the number of this channel (in the span).
+
+=head1 num()
+
+Returns the number of this channel as a Zaptel channel.
+
+=head signalling()
+
+Returns the signalling set for this channel through /etc/zaptel.conf .
+This is always empty before ztcfg was run. And shows the "other" type
+for FXS and for FXO.
+
+=head1 span()
+
+Returns a reference to the span to which this channel belongs.
+
+=head1 type()
+
+Returns the type of the channel: 'FXS', 'FXO', 'EMPTY', etc.
+
+=cut
+
+sub new($$$$$$) {
+ my $pack = shift or die "Wasn't called as a class method\n";
+ my $span = shift or die "Missing a span parameter\n";
+ my $index = shift;
+ my $line = shift or die "Missing an input line\n";
+ defined $index or die "Missing an index parameter\n";
+ my $self = {
+ 'SPAN' => $span,
+ 'INDEX' => $index,
+ };
+ bless $self, $pack;
+ my ($num, $fqn, $rest) = split(/\s+/, $line, 3);
+ $num or die "Missing a channel number parameter\n";
+ $fqn or die "Missing a channel fqn parameter\n";
+ my $signalling = '';
+ my $info = '';
+ if(defined $rest) {
+ if($rest =~ s/^\s*(\w+)\s*//) {
+ $signalling = $1;
+ }
+ if($rest =~ s/(.*)//) {
+ $info = $1;
+ }
+ }
+ $self->{NUM} = $num;
+ $self->{FQN} = $fqn;
+ $self->{SIGNALLING} = $signalling;
+ $self->{INFO} = $info;
+ my $type;
+ if($fqn =~ m|\bXPP_(\w+)/.*$|) {
+ $type = $1; # An Astribank
+ } elsif ($fqn =~ m{\bWCFXO/.*}) {
+ $type = "FXO"; # wcfxo - x100p and relatives.
+ # A single port card. The driver issue RED alarm when
+ # There's no better
+ $self->{BATTERY} = !($span->description =~ /\bRED\b/);
+ } elsif ($fqn =~ m{\bFXS/.*}) {
+ $type = "FXS"; # likely Rhino
+ } elsif ($fqn =~ m{\bFXO/.*}) {
+ $type = "FXO"; # likely Rhino
+ } elsif ($fqn =~ m{\b---/.*}) {
+ $type = "EMPTY"; # likely Rhino, empty slot.
+ } elsif ($fqn =~ m{\b(TE[24]|WCT1|Tor2|TorISA|WP[TE]1|cwain[12])/.*}) {
+ # 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.
+ $type = "PRI";
+ } elsif ($fqn =~ m{\b(ZTHFC%d*|ztqoz\d*)/.*}) {
+ # ZTHFC: HFC-s single-port card (zaphfc/vzaphfc)
+ # ztqoz: qozap (Junghanns) multi-port HFC card
+ $type = "BRI";
+ } elsif ($fqn =~ m{\bztgsm/.*}) {
+ # Junghanns GSM card
+ $type = "GSM";
+ } elsif(defined $signalling) {
+ $type = 'FXS' if $signalling =~ /^FXS/;
+ $type = 'FXO' if $signalling =~ /^FXO/;
+ } else {
+ $type = undef;
+ }
+ $self->type($type);
+ $self->span()->type($type)
+ if ! defined($self->span()->type()) ||
+ $self->span()->type() eq 'UNKNOWN';
+ return $self;
+}
+
+=head1 probe_type()
+
+In the case of some cards, the information in /proc/zaptel is not good
+enough to tell the type of each channel. In this case an extra explicit
+probe is needed.
+
+Currently this is implemented by using some invocations of ztcfg(8).
+
+It may later be replaced by ztscan(8).
+
+=cut
+
+my $ztcfg = $ENV{ZTCFG} || '/sbin/ztcfg';
+sub probe_type($) {
+ my $self = shift;
+ my $fqn = $self->fqn;
+ my $num = $self->num;
+ my $type;
+
+ if($fqn =~ m:WCTDM/| WRTDM/|OPVXA1200/:) {
+ my %maybe;
+
+ undef %maybe;
+ foreach my $sig (qw(fxo fxs)) {
+ my $cmd = "echo ${sig}ks=$num | $ztcfg -c /dev/fd/0";
+
+ $maybe{$sig} = system("$cmd >/dev/null 2>&1") == 0;
+ }
+ if($maybe{fxo} and $maybe{fxs}) {
+ $type = 'EMPTY';
+ } elsif($maybe{fxo}) {
+ $type = 'FXS';
+ } elsif($maybe{fxs}) {
+ $type = 'FXO';
+ } else {
+ $type = 'EMPTY';
+ }
+ } else {
+ $type = $self->type;
+ }
+ return $type;
+}
+
+sub battery($) {
+ my $self = shift or die;
+ my $span = $self->span or die;
+
+ return undef unless $self->type eq 'FXO';
+ return $self->{BATTERY} if defined $self->{BATTERY};
+
+ my $xpd = $span->xpd;
+ my $index = $self->index;
+ return undef if !$xpd;
+
+ # It's an XPD (FXO)
+ my @lines = @{$xpd->lines};
+ my $line = $lines[$index];
+ return $line->battery;
+}
+
+sub blink($$) {
+ my $self = shift or die;
+ my $on = shift;
+ my $span = $self->span or die;
+
+ my $xpd = $span->xpd;
+ my $index = $self->index;
+ return undef if !$xpd;
+
+ my @lines = @{$xpd->lines};
+ my $line = $lines[$index];
+ return $line->blink($on);
+}
+
+
+1;
diff --git a/xpp/zconf/Zaptel/Config/Defaults.pm b/xpp/zconf/Zaptel/Config/Defaults.pm
new file mode 100644
index 0000000..360ca0a
--- /dev/null
+++ b/xpp/zconf/Zaptel/Config/Defaults.pm
@@ -0,0 +1,56 @@
+package Zaptel::Config::Defaults;
+#
+# 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 the shell to source a file and expand a given list
+# of variables.
+sub do_source($@) {
+ my $file = shift;
+ my @vars = @_;
+ my @output = `env -i sh -ec '. $file; export @vars; for i in @vars; do eval echo \$i=\\\$\$i; done'`;
+ die "$0: Sourcing '$file' exited with $?" if $?;
+ my %vars;
+
+ foreach my $line (@output) {
+ chomp $line;
+ my ($k, $v) = split(/=/, $line, 2);
+ $vars{$k} = $v if grep /^$k$/, @vars;
+ }
+ return %vars;
+}
+
+sub source_vars {
+ my @vars = @_;
+ my $default_file;
+ my %system_files = (
+ "/etc/default/zaptel" => 'Debian and friends',
+ "/etc/sysconfig/zaptel" => 'Red Hat and friends',
+ );
+
+ if(defined $ENV{ZAPTEL_DEFAULTS}) {
+ $default_file = $ENV{ZAPTEL_DEFAULTS};
+ } else {
+ foreach my $f (keys %system_files) {
+ if(-r $f) {
+ if(defined $default_file) {
+ die "An '$f' collides with '$default_file'";
+ }
+ $default_file = $f;
+ }
+ }
+ }
+ if (! $default_file) {
+ return ("", ());
+ }
+ my %vars = Zaptel::Config::Defaults::do_source($default_file, @vars);
+ return ($default_file, %vars);
+}
+
+1;
diff --git a/xpp/zconf/Zaptel/Hardware.pm b/xpp/zconf/Zaptel/Hardware.pm
new file mode 100644
index 0000000..ff7aeea
--- /dev/null
+++ b/xpp/zconf/Zaptel/Hardware.pm
@@ -0,0 +1,168 @@
+package Zaptel::Hardware;
+#
+# 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 Zaptel::Hardware::USB;
+use Zaptel::Hardware::PCI;
+
+=head1 NAME
+
+Zaptel::Hardware - Perl interface to a Zaptel devices listing
+
+
+ use Zaptel::Hardware;
+
+ my $hardware = Zaptel::Hardware->scan;
+
+ # mini zaptel_hardware:
+ foreach my $device ($hardware->device_list) {
+ print "Vendor: device->{VENDOR}, Product: $device->{PRODUCT}\n"
+ }
+
+ # let's see if there are devices without loaded drivers, and sugggest
+ # drivers to load:
+ my @to_load = ();
+ foreach my $device ($hardware->device_list) {
+ if (! $device->{LOADED} ) {
+ push @to_load, ($device->${DRIVER});
+ }
+ }
+ if (@to_load) {
+ print "To support the extra devices you probably need to run:\n"
+ print " modprobe ". (join ' ', @to_load). "\n";
+ }
+
+
+This module provides information about available Zaptel devices on the
+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.
+
+=head2 bus_type
+
+'PCI' or 'USB'.
+
+
+=head2 description
+
+A one-line description of the device.
+
+
+=head2 driver
+
+Name of a Zaptel device driver that should handle this device. This is
+based on a pre-made list.
+
+
+=head2 vendor, product, subvendor, subproduct
+
+The PCI and USB vendor ID, product ID, sub-vendor ID and sub-product ID.
+(The standard short lspci and lsusb listings show only vendor and
+product IDs).
+
+
+=head2 loaded
+
+If the device is handled by a module - the name of the module. Else -
+undef.
+
+
+=head2 priv_device_name
+
+A string that shows the "location" of that device on the bus.
+
+
+=head2 is_astribank
+
+True if the device is a Xorcom Astribank (which may provide some extra
+attributes).
+
+=head2 serial
+
+(Astribank-specific attrribute) - the serial number string of the
+Astribank.
+
+=cut
+
+sub device_detected($$) {
+ my $dev = shift || die;
+ my $name = shift || die;
+ die unless defined $dev->{'BUS_TYPE'};
+ $dev->{IS_ASTRIBANK} = 0 unless defined $dev->{'IS_ASTRIBANK'};
+ $dev->{'HARDWARE_NAME'} = $name;
+}
+
+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.
+
+You must run scan() first for this function to run meaningful output.
+
+=cut
+
+sub device_list($) {
+ my $self = shift || die;
+ my @types = @_;
+ my @list;
+
+ @types = qw(USB PCI) unless @types;
+ foreach my $t (@types) {
+ @list = ( @list, @{$self->{$t}} );
+ }
+ return @list;
+}
+
+
+=head1 drivers()
+
+Returns a list of drivers (currently sorted by name) that are used by
+the devices in the current system (regardless to whether or not they are
+loaded.
+
+=cut
+
+sub drivers($) {
+ my $self = shift || die;
+ my @devs = $self->device_list;
+ my @drvs = map { $_->{DRIVER} } @devs;
+ # Make unique
+ my %drivers;
+ @drivers{@drvs} = 1;
+ return sort keys %drivers;
+}
+
+
+=head1 scan()
+
+Scan the system for Zaptel devices (PCI and USB). Returns nothing but
+must be run to initialize the module.
+
+=cut
+
+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;
+}
+
+1;
diff --git a/xpp/zconf/Zaptel/Hardware/PCI.pm b/xpp/zconf/Zaptel/Hardware/PCI.pm
new file mode 100644
index 0000000..a63b09f
--- /dev/null
+++ b/xpp/zconf/Zaptel/Hardware/PCI.pm
@@ -0,0 +1,208 @@
+package Zaptel::Hardware::PCI;
+#
+# 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 Zaptel::Utils;
+use Zaptel::Hardware;
+
+our @ISA = qw(Zaptel::Hardware);
+
+# Lookup algorithm:
+# First match 'vendor:product/subvendor:subproduct' key
+# Else match 'vendor:product/subvendor' key
+# Else match 'vendor:product' key
+# Else not a zaptel hardware.
+my %pci_ids = (
+ # from wct4xxp
+ '10ee:0314' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE410P/TE405P (1st Gen)' },
+ 'd161:0420/0004' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE420 (4th Gen)' },
+ 'd161:0410/0004' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE410P (4th Gen)' },
+ 'd161:0405/0004' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE405P (4th Gen)' },
+ 'd161:0410/0003' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE410P (3rd Gen)' },
+ 'd161:0405/0003' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE405P (3rd Gen)' },
+ 'd161:0410' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE410P (2nd Gen)' },
+ 'd161:0405' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE405P (2nd Gen)' },
+ 'd161:0220/0004' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE220 (4th Gen)' },
+ 'd161:0205/0004' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE205P (4th Gen)' },
+ 'd161:0210/0004' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE210P (4th Gen)' },
+ 'd161:0205/0003' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE205P (3rd Gen)' },
+ 'd161:0210/0003' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE210P (3rd Gen)' },
+ 'd161:0205' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE205P ' },
+ 'd161:0210' => { DRIVER => 'wct4xxp', DESCRIPTION => 'Wildcard TE210P ' },
+
+ # from wctdm24xxp
+ 'd161:2400' => { DRIVER => 'wctdm24xxp', DESCRIPTION => 'Wildcard TDM2400P' },
+ 'd161:0800' => { DRIVER => 'wctdm24xxp', DESCRIPTION => 'Wildcard TDM800P' },
+ 'd161:8002' => { DRIVER => 'wctdm24xxp', DESCRIPTION => 'Wildcard AEX800' },
+ 'd161:8003' => { DRIVER => 'wctdm24xxp', DESCRIPTION => 'Wildcard AEX2400' },
+ 'd161:8005' => { DRIVER => 'wctdm24xxp', DESCRIPTION => 'Wildcard TDM410P' },
+ 'd161:8006' => { DRIVER => 'wctdm24xxp', DESCRIPTION => 'Wildcard AEX410P' },
+
+ # from pciradio
+ 'e159:0001/e16b' => { DRIVER => 'pciradio', DESCRIPTION => 'PCIRADIO' },
+
+ # from wcfxo
+ 'e159:0001/8084' => { DRIVER => 'wcfxo', DESCRIPTION => 'Wildcard X101P clone' },
+ 'e159:0001/8085' => { DRIVER => 'wcfxo', DESCRIPTION => 'Wildcard X101P' },
+ 'e159:0001/8086' => { DRIVER => 'wcfxo', DESCRIPTION => 'Wildcard X101P clone' },
+ 'e159:0001/8087' => { DRIVER => 'wcfxo', DESCRIPTION => 'Wildcard X101P clone' },
+ '1057:5608' => { DRIVER => 'wcfxo', DESCRIPTION => 'Wildcard X100P' },
+
+ # from wct1xxp
+ 'e159:0001/6159' => { DRIVER => 'wct1xxp', DESCRIPTION => 'Digium Wildcard T100P T1/PRI or E100P E1/PRA Board' },
+
+ # from wctdm
+ 'e159:0001/a159' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard S400P Prototype' },
+ 'e159:0001/e159' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard S400P Prototype' },
+ 'e159:0001/b100' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV E/F' },
+ 'e159:0001/b1d9' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV I' },
+ 'e159:0001/b118' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV I' },
+ 'e159:0001/b119' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV I' },
+ 'e159:0001/a9fd' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV H' },
+ 'e159:0001/a8fd' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV H' },
+ 'e159:0001/a800' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV H' },
+ 'e159:0001/a801' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV H' },
+ 'e159:0001/a908' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV H' },
+ 'e159:0001/a901' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV H' },
+ #'e159:0001' => { DRIVER => 'wctdm', DESCRIPTION => 'Wildcard TDM400P REV H' },
+
+ # from wcte11xp
+ 'e159:0001/71fe' => { DRIVER => 'wcte11xp', DESCRIPTION => 'Digium Wildcard TE110P T1/E1 Board' },
+ 'e159:0001/79fe' => { DRIVER => 'wcte11xp', DESCRIPTION => 'Digium Wildcard TE110P T1/E1 Board' },
+ 'e159:0001/795e' => { DRIVER => 'wcte11xp', DESCRIPTION => 'Digium Wildcard TE110P T1/E1 Board' },
+ 'e159:0001/79de' => { DRIVER => 'wcte11xp', DESCRIPTION => 'Digium Wildcard TE110P T1/E1 Board' },
+ 'e159:0001/797e' => { DRIVER => 'wcte11xp', DESCRIPTION => 'Digium Wildcard TE110P T1/E1 Board' },
+
+ # from wcte12xp
+ 'd161:0120' => { DRIVER => 'wcte12xp', DESCRIPTION => 'Wildcard TE12xP' },
+ 'd161:8000' => { DRIVER => 'wcte12xp', DESCRIPTION => 'Wildcard TE121' },
+ 'd161:8001' => { DRIVER => 'wcte12xp', DESCRIPTION => 'Wildcard TE122' },
+
+ # from tor2
+ '10b5:9030' => { DRIVER => 'tor2', DESCRIPTION => 'PLX 9030' },
+ '10b5:3001' => { DRIVER => 'tor2', DESCRIPTION => 'PLX Development Board' },
+ '10b5:D00D' => { DRIVER => 'tor2', DESCRIPTION => 'Tormenta 2 Quad T1/PRI or E1/PRA' },
+ '10b5:4000' => { DRIVER => 'tor2', DESCRIPTION => 'Tormenta 2 Quad T1/E1 (non-Digium clone)' },
+
+ # Cologne Chips:
+ # (Still a partial list)
+ '1397:08b4/b556' => { DRIVER => 'qozap', DESCRIPTION => 'Junghanns DuoBRI ISDN card' },
+ '1397:08b4' => { DRIVER => 'qozap', DESCRIPTION => 'Junghanns QuadBRI ISDN card' },
+ '1397:16b8' => { DRIVER => 'qozap', DESCRIPTION => 'Junghanns OctoBRI ISDN card' },
+ '1397:30b1' => { DRIVER => 'cwain', DESCRIPTION => 'HFC-E1 ISDN E1 card' },
+ '1397:2bd0' => { DRIVER => 'zaphfc', DESCRIPTION => 'HFC-S ISDN BRI card' },
+ '1397:f001' => { DRIVER => 'ztgsm', DESCRIPTION => 'HFC-GSM Cologne Chips GSM' },
+
+ # Rhino cards (based on pci.ids)
+ '0b0b:0105' => { DRIVER => 'r1t1', DESCRIPTION => 'Rhino R1T1' },
+ '0b0b:0205' => { DRIVER => 'r4fxo', DESCRIPTION => 'Rhino R14FXO' },
+ '0b0b:0206' => { DRIVER => 'rcbfx', DESCRIPTION => 'Rhino RCB4FXO 4-channel FXO analog telphony card' },
+ '0b0b:0305' => { DRIVER => 'r1t1', DESCRIPTION => 'Rhino R1T1' },
+ '0b0b:0405' => { DRIVER => 'rcbfx', DESCRIPTION => 'Rhino R8FXX' },
+ '0b0b:0406' => { DRIVER => 'rcbfx', DESCRIPTION => 'Rhino RCB8FXX 8-channel modular analog telphony card' },
+ '0b0b:0505' => { DRIVER => 'rcbfx', DESCRIPTION => 'Rhino R24FXX' },
+ '0b0b:0506' => { DRIVER => 'rcbfx', DESCRIPTION => 'Rhino RCB24FXS 24-Channel FXS analog telphony card' },
+ '0b0b:0605' => { DRIVER => 'rxt1', DESCRIPTION => 'Rhino R2T1' },
+ '0b0b:0705' => { DRIVER => 'rcbfx', DESCRIPTION => 'Rhino R24FXS' },
+ '0b0b:0706' => { DRIVER => 'rcbfx', DESCRIPTION => 'Rhino RCB24FXO 24-Channel FXO analog telphony card' },
+ '0b0b:0906' => { DRIVER => 'rcbfx', DESCRIPTION => 'Rhino RCB24FXX 24-channel modular analog telphony card' },
+
+ # Sangoma cards (based on pci.ids)
+ '1923:0040' => { DRIVER => 'wanpipe', DESCRIPTION => 'Sangoma Technologies Corp. A200/Remora FXO/FXS Analog AFT card' },
+ '1923:0100' => { DRIVER => 'wanpipe', DESCRIPTION => 'Sangoma Technologies Corp. A104d QUAD T1/E1 AFT card' },
+ '1923:0300' => { DRIVER => 'wanpipe', DESCRIPTION => 'Sangoma Technologies Corp. A101 single-port T1/E1' },
+ '1923:0400' => { DRIVER => 'wanpipe', DESCRIPTION => 'Sangoma Technologies Corp. A104u Quad T1/E1 AFT' },
+ );
+
+$ENV{PATH} .= ":/usr/sbin:/sbin:/usr/bin:/bin";
+
+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 = { @_ };
+ bless $self, $pack;
+ Zaptel::Hardware::device_detected($self,
+ sprintf("pci:%s", $self->{PRIV_DEVICE_NAME}));
+ return $self;
+}
+
+my %pci_devs;
+
+sub readfile($) {
+ my $name = shift || die;
+ open(F, $name) || die "Failed to open '$name': $!";
+ my $str = <F>;
+ close F;
+ chomp($str);
+ return $str;
+}
+
+sub scan_devices($) {
+ my @devices;
+
+ while(</sys/bus/pci/devices/*>) {
+ m,([^/]+)$,,;
+ my $name = $1;
+ my $l = readlink $_ || die;
+ $pci_devs{$name}{PRIV_DEVICE_NAME} = $name;
+ $pci_devs{$name}{DEVICE} = $l;
+ $pci_devs{$name}{VENDOR} = readfile "$_/vendor";
+ $pci_devs{$name}{PRODUCT} = readfile "$_/device";
+ $pci_devs{$name}{SUBVENDOR} = readfile "$_/subsystem_vendor";
+ $pci_devs{$name}{SUBPRODUCT} = readfile "$_/subsystem_device";
+ my $dev = $pci_devs{$name};
+ grep(s/0x//, $dev->{VENDOR}, $dev->{PRODUCT}, $dev->{SUBVENDOR}, $dev->{SUBPRODUCT});
+ $pci_devs{$name}{DRIVER} = '';
+ }
+
+ while(</sys/bus/pci/drivers/*/[0-9]*>) {
+ m,^(.*?)/([^/]+)/([^/]+)$,;
+ my $prefix = $1;
+ my $drvname = $2;
+ my $id = $3;
+ my $l = readlink "$prefix/$drvname/module";
+ # Find the real module name (if we can).
+ if(defined $l) {
+ my $moduledir = "$prefix/$drvname/$l";
+ my $modname = $moduledir;
+ $modname =~ s:^.*/::;
+ $drvname = $modname;
+ }
+ $pci_devs{$id}{LOADED} = $drvname;
+ }
+ foreach (sort keys %pci_devs) {
+ my $dev = $pci_devs{$_};
+ my $key;
+ # Try to match
+ $key = "$dev->{VENDOR}:$dev->{PRODUCT}/$dev->{SUBVENDOR}:$dev->{SUBPRODUCT}";
+ $key = "$dev->{VENDOR}:$dev->{PRODUCT}/$dev->{SUBVENDOR}" if !defined($pci_ids{$key});
+ $key = "$dev->{VENDOR}:$dev->{PRODUCT}" if !defined($pci_ids{$key});
+ 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},
+ SUBVENDOR => $dev->{SUBVENDOR},
+ SUBPRODUCT => $dev->{SUBPRODUCT},
+ LOADED => $dev->{LOADED},
+ DRIVER => $pci_ids{$key}{DRIVER},
+ DESCRIPTION => $pci_ids{$key}{DESCRIPTION},
+ );
+ push(@devices, $d);
+ }
+ @devices = sort pci_sorter @devices;
+ return @devices;
+}
+
+1;
diff --git a/xpp/zconf/Zaptel/Hardware/USB.pm b/xpp/zconf/Zaptel/Hardware/USB.pm
new file mode 100644
index 0000000..a2dc08f
--- /dev/null
+++ b/xpp/zconf/Zaptel/Hardware/USB.pm
@@ -0,0 +1,116 @@
+package Zaptel::Hardware::USB;
+#
+# 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 Zaptel::Utils;
+use Zaptel::Hardware;
+use Zaptel::Xpp;
+use Zaptel::Xpp::Xbus;
+
+our @ISA = qw(Zaptel::Hardware);
+
+my %usb_ids = (
+ # from wcusb
+ '06e6:831c' => { DRIVER => 'wcusb', DESCRIPTION => 'Wildcard S100U USB FXS Interface' },
+ '06e6:831e' => { DRIVER => 'wcusb2', DESCRIPTION => 'Wildcard S110U USB FXS Interface' },
+ '06e6:b210' => { DRIVER => 'wc_usb_phone', DESCRIPTION => 'Wildcard Phone Test driver' },
+
+ # from xpp_usb
+ 'e4e4:1130' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-8/16 no-firmware' },
+ 'e4e4:1131' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-8/16 USB-firmware' },
+ 'e4e4:1132' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-8/16 FPGA-firmware' },
+ 'e4e4:1140' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-BRI no-firmware' },
+ 'e4e4:1141' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-BRI USB-firmware' },
+ 'e4e4:1142' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-BRI FPGA-firmware' },
+ 'e4e4:1150' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-multi no-firmware' },
+ 'e4e4:1151' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-multi USB-firmware' },
+ 'e4e4:1152' => { DRIVER => 'xpp_usb', DESCRIPTION => 'Astribank-multi FPGA-firmware' },
+ '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' },
+ );
+
+
+$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;
+
+ my ($wanted) = grep {
+ defined($_->usb_devname) &&
+ $priv_device_name eq $_->usb_devname
+ } @xbuses;
+ return $wanted;
+}
+
+sub new($$) {
+ my $pack = shift or die "Wasn't called as a class method\n";
+ my $self = { @_ };
+ 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($) {
+ my $pack = shift || die;
+ my $usb_device_list = "/proc/bus/usb/devices";
+ return unless (-r $usb_device_list);
+
+ my @devices;
+ open(F, $usb_device_list) || die "Failed to open $usb_device_list: $!";
+ local $/ = '';
+ while(<F>) {
+ my @lines = split(/\n/);
+ my ($tline) = grep(/^T/, @lines);
+ my ($pline) = grep(/^P/, @lines);
+ my ($sline) = grep(/^S:.*SerialNumber=/, @lines);
+ my ($busnum,$devnum) = ($tline =~ /Bus=(\w+)\W.*Dev#=\s*(\w+)\W/);
+ my $devname = sprintf("%03d/%03d", $busnum, $devnum);
+ my ($vendor,$product) = ($pline =~ /Vendor=(\w+)\W.*ProdID=(\w+)\W/);
+ my $serial;
+ if(defined $sline) {
+ $sline =~ /SerialNumber=(.*)/;
+ $serial = $1;
+ #$serial =~ s/[[:^print:]]/_/g;
+ }
+ my $model = $usb_ids{"$vendor:$product"};
+ 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,
+ SERIAL => $serial,
+ DESCRIPTION => $model->{DESCRIPTION},
+ DRIVER => $model->{DRIVER},
+ );
+ push(@devices, $d);
+ }
+ close F;
+ @devices = sort usb_sorter @devices;
+}
+
+1;
diff --git a/xpp/zconf/Zaptel/Span.pm b/xpp/zconf/Zaptel/Span.pm
new file mode 100644
index 0000000..be49c28
--- /dev/null
+++ b/xpp/zconf/Zaptel/Span.pm
@@ -0,0 +1,300 @@
+package Zaptel::Span;
+#
+# 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 Zaptel::Utils;
+use Zaptel::Chans;
+use Zaptel::Xpp::Xpd;
+
+=head1 NAME
+
+Zaptel::Spans - Perl interface to a Zaptel span information
+
+This package allows access from perl to information about a Zaptel
+channel. It is part of the Zaptel Perl package.
+
+A span is a logical unit of Zaptel channels. Normally a port in a
+digital card or a whole analog card.
+
+See documentation of module L<Zaptel> for usage example. Specifically
+C<Zaptel::spans()> must be run initially.
+
+=head1 by_number()
+
+Get a span by its Zaptel span number.
+
+=head1 Span Properties
+
+=head2 num()
+
+The span number.
+
+=head2 name()
+
+The name field of a Zaptel span. E.g.:
+
+ TE2/0/1
+
+=head2 description()
+
+The description field of the span. e.g:
+
+ "T2XXP (PCI) Card 0 Span 1" HDB3/CCS/CRC4 RED
+
+=head2 chans()
+
+The list of the channels (L<Zaptel::Chan> objects) of this span.
+In a scalar context returns the number of channels this span has.
+
+=head2 bchans()
+
+Likewise a list of bchannels (or a count in a scalar context).
+
+=head2 is_sync_master()
+
+Is this span the source of timing for Zaptel?
+
+=head2 type()
+
+Type of span, or "UNKNOWN" if could not be detected. Current known
+types:
+
+BRI_TE, BRI_NT, E1_TE, E1_NT, J1_TE, J1_NT, T1_TE, T1_NT, FXS, FXO
+
+=head2 is_pri()
+
+Is this an E1/J1/T1 span?
+
+=head2 is_bri()
+
+Is this a BRI span?
+
+=head2 is_digital()
+
+Is this a digital (as opposed to analog) span?
+
+=head2 termtype()
+
+Set for digital spans. "TE" or "NT". Will probably be assumed to be "TE"
+if there's no information pointing either way.
+
+=head2 coding()
+
+Suggested sane coding type (e.g.: "hdb3", "b8zs") for this type of span.
+
+=head2 framing()
+
+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.
+
+=head2 signalling()
+
+Suggested zapata.conf signalling for channels of this span.
+
+=head2 switchtype()
+
+Suggested zapata.conf switchtype for channels of this span.
+
+=head1 Note
+
+Most of those properties are normally used as lower-case functions, but
+actually set in the module as capital-letter propeties. To look at e.g.
+"signalling" is set, look for "SIGNALLING".
+
+=cut
+
+my $proc_base = "/proc/zaptel";
+
+sub chans($) {
+ my $span = shift;
+ return @{$span->{CHANS}};
+}
+
+sub by_number($) {
+ my $span_number = shift;
+ die "Missing span number" unless defined $span_number;
+ my @spans = Zaptel::spans();
+
+ my ($span) = grep { $_->num == $span_number } @spans;
+ return $span;
+}
+
+my @bri_strings = (
+ 'BRI_(NT|TE)',
+ '(?:quad|octo)BRI PCI ISDN Card.* \[(NT|TE)\]\ ',
+ 'octoBRI \[(NT|TE)\] ',
+ 'HFC-S PCI A ISDN.* \[(NT|TE)\] '
+ );
+
+my @pri_strings = (
+ '(E1|T1|J1)_(NT|TE)',
+ 'Tormenta 2 .*Quad (E1|T1)', # tor2.
+ 'Digium Wildcard .100P (T1|E1)/', # wct1xxp
+ 'ISA Tormenta Span 1', # torisa
+ 'TE110P T1/E1', # wcte11xp
+ 'Wildcard TE120P', # wcte12xp
+ 'Wildcard TE121', # wcte12xp
+ 'Wildcard TE122', # wcte12xp
+ 'T[24]XXP \(PCI\) Card ', # wct4xxp
+ );
+
+our $ZAPBRI_NET = 'bri_net';
+our $ZAPBRI_CPE = 'bri_cpe';
+
+our $ZAPPRI_NET = 'pri_net';
+our $ZAPPRI_CPE = 'pri_cpe';
+
+sub init_proto($$) {
+ my $self = shift;
+ my $proto = shift;
+
+ $self->{PROTO} = $proto;
+ if($proto eq 'E1') {
+ $self->{DCHAN_IDX} = 15;
+ $self->{BCHAN_LIST} = [ 0 .. 14, 16 .. 30 ];
+ } elsif($proto eq 'T1') {
+ $self->{DCHAN_IDX} = 23;
+ $self->{BCHAN_LIST} = [ 0 .. 22 ];
+ }
+ $self->{TYPE} = "${proto}_$self->{TERMTYPE}";
+}
+
+sub new($$) {
+ my $pack = shift or die "Wasn't called as a class method\n";
+ my $num = shift or die "Missing a span number parameter\n";
+ my $self = { NUM => $num };
+ bless $self, $pack;
+ $self->{TYPE} = "UNKNOWN";
+ my @xpds = Zaptel::Xpp::Xpd::xpds_by_spanno;
+ my $xpd = $xpds[$num];
+ if(defined $xpd) {
+ die "Spanno mismatch: $xpd->spanno, $num" unless $xpd->spanno == $num;
+ $self->{XPD} = $xpd;
+ }
+ open(F, "$proc_base/$num") or die "Failed to open '$proc_base/$num\n";
+ my $head = <F>;
+ chomp $head;
+ $self->{IS_DIGITAL} = 0;
+ $self->{IS_BRI} = 0;
+ $self->{IS_PRI} = 0;
+ foreach my $cardtype (@bri_strings) {
+ if($head =~ m/$cardtype/) {
+ $self->{IS_DIGITAL} = 1;
+ $self->{IS_BRI} = 1;
+ $self->{TERMTYPE} = $1;
+ $self->{TYPE} = "BRI_$1";
+ $self->{DCHAN_IDX} = 2;
+ $self->{BCHAN_LIST} = [ 0, 1 ];
+ last;
+ }
+ }
+ foreach my $cardtype (@pri_strings) {
+ if($head =~ m/$cardtype/) {
+ my @info;
+
+ push(@info, $1) if defined $1;
+ push(@info, $2) if defined $2;
+ my ($proto) = grep(/(E1|T1|J1)/, @info);
+ $proto = 'UNKNOWN' unless defined $proto;
+ my ($termtype) = grep(/(NT|TE)/, @info);
+ $termtype = 'TE' unless defined $termtype;
+
+ $self->{IS_DIGITAL} = 1;
+ $self->{IS_PRI} = 1;
+ $self->{TERMTYPE} = $termtype;
+ $self->init_proto($proto);
+ last;
+ }
+ }
+ die "$0: Unkown TERMTYPE [NT/TE]\n"
+ if $self->is_digital and !defined $self->{TERMTYPE};
+ ($self->{NAME}, $self->{DESCRIPTION}) = (split(/\s+/, $head, 4))[2, 3];
+ $self->{IS_ZAPTEL_SYNC_MASTER} =
+ ($self->{DESCRIPTION} =~ /\(MASTER\)/) ? 1 : 0;
+ $self->{CHANS} = [];
+ my @channels;
+ my $index = 0;
+ while(<F>) {
+ chomp;
+ s/^\s*//;
+ s/\s*$//;
+ next unless /\S/;
+ next unless /^\s*\d+/; # must be a real channel string.
+ my $c = Zaptel::Chans->new($self, $index, $_);
+ push(@channels, $c);
+ $index++;
+ }
+ close F;
+ if($self->is_pri()) {
+ # Check for PRI with unknown type strings
+ if($index == 31) {
+ if($self->{PROTO} eq 'UNKNOWN') {
+ $self->init_proto('E1');
+ } elsif($self->{PROTO} ne 'E1') {
+ die "$index channels in a $self->{PROTO} span";
+ }
+ } elsif($index == 24) {
+ if($self->{PROTO} eq 'UNKNOWN') {
+ $self->init_proto('T1'); # FIXME: J1?
+ } elsif($self->{PROTO} ne 'T1') {
+ die "$index channels in a $self->{PROTO} span";
+ }
+ }
+ }
+ @channels = sort { $a->num <=> $b->num } @channels;
+ $self->{CHANS} = \@channels;
+ $self->{YELLOW} = undef;
+ $self->{CRC4} = undef;
+ if($self->is_bri()) {
+ $self->{CODING} = 'ami';
+ $self->{DCHAN} = ($self->chans())[$self->{DCHAN_IDX}];
+ $self->{BCHANS} = [ ($self->chans())[@{$self->{BCHAN_LIST}}] ];
+ # Infer some info from channel name:
+ my $first_chan = ($self->chans())[0] || die "$0: No channels in span #$num\n";
+ my $chan_fqn = $first_chan->fqn();
+ if($chan_fqn =~ m(ZTHFC.*/|ztqoz.*/|XPP_BRI_.*/)) { # BRI
+ $self->{FRAMING} = 'ccs';
+ $self->{SWITCHTYPE} = 'euroisdn';
+ $self->{SIGNALLING} = ($self->{TERMTYPE} eq 'NT') ? $ZAPBRI_NET : $ZAPBRI_CPE ;
+ } elsif($chan_fqn =~ m(ztgsm.*/)) { # Junghanns's GSM cards.
+ $self->{FRAMING} = 'ccs';
+ $self->{SIGNALLING} = 'gsm';
+ }
+ }
+ if($self->is_pri()) {
+ $self->{DCHAN} = ($self->chans())[$self->{DCHAN_IDX}];
+ $self->{BCHANS} = [ ($self->chans())[@{$self->{BCHAN_LIST}}] ];
+ if($self->{PROTO} eq 'E1') {
+ $self->{CODING} = 'hdb3';
+ $self->{FRAMING} = 'ccs';
+ $self->{SWITCHTYPE} = 'euroisdn';
+ $self->{CRC4} = 'crc4';
+ } elsif($self->{PROTO} eq 'T1') {
+ $self->{CODING} = 'b8zs';
+ $self->{FRAMING} = 'esf';
+ $self->{SWITCHTYPE} = 'national';
+ } else {
+ die "'$self->{PROTO}' unsupported yet";
+ }
+ $self->{SIGNALLING} = ($self->{TERMTYPE} eq 'NT') ? $ZAPPRI_NET : $ZAPPRI_CPE ;
+ }
+ return $self;
+}
+
+sub bchans($) {
+ my $self = shift || die;
+
+ return @{$self->{BCHANS}};
+}
+
+1;
diff --git a/xpp/zconf/Zaptel/Utils.pm b/xpp/zconf/Zaptel/Utils.pm
new file mode 100644
index 0000000..8d13ad7
--- /dev/null
+++ b/xpp/zconf/Zaptel/Utils.pm
@@ -0,0 +1,52 @@
+package Zaptel::Utils;
+
+# Accessors (miniperl does not have Class:Accessor)
+our $AUTOLOAD;
+sub AUTOLOAD {
+ my $self = shift;
+ my $name = $AUTOLOAD;
+ $name =~ s/.*://; # strip fully-qualified portion
+ return if $name =~ /^[A-Z_]+$/; # ignore special methods (DESTROY)
+ my $key = uc($name);
+ my $val = shift;
+ if (defined $val) {
+ #print STDERR "set: $key = $val\n";
+ return $self->{$key} = $val;
+ } else {
+ if(!exists $self->{$key}) {
+ #$self->xpp_dump;
+ #die "Trying to get uninitialized '$key'";
+ }
+ my $val = $self->{$key};
+ #print STDERR "get: $key ($val)\n";
+ return $val;
+ }
+}
+
+sub xpp_dump($) {
+ my $self = shift || die;
+ printf STDERR "Dump a %s\n", ref($self);
+ foreach my $k (sort keys %{$self}) {
+ my $val = $self->{$k};
+ $val = '**UNDEF**' if !defined $val;
+ printf STDERR " %-20s %s\n", $k, $val;
+ }
+}
+
+# Based on Autoloader
+
+sub import {
+ my $pkg = shift;
+ my $callpkg = caller;
+
+ #print STDERR "import: $pkg, $callpkg\n";
+ #
+ # Export symbols, but not by accident of inheritance.
+ #
+ die "Sombody inherited Zaptel::Utils" if $pkg ne 'Zaptel::Utils';
+ no strict 'refs';
+ *{ $callpkg . '::AUTOLOAD' } = \&AUTOLOAD;
+ *{ $callpkg . '::xpp_dump' } = \&xpp_dump;
+}
+
+1;
diff --git a/xpp/zconf/Zaptel/Xpp.pm b/xpp/zconf/Zaptel/Xpp.pm
new file mode 100644
index 0000000..8b7458f
--- /dev/null
+++ b/xpp/zconf/Zaptel/Xpp.pm
@@ -0,0 +1,199 @@
+package Zaptel::Xpp;
+#
+# 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 Zaptel::Xpp::Xbus;
+
+=head1 NAME
+
+Zaptel::Xpp - Perl interface to the Xorcom Astribank drivers.
+
+=head1 SYNOPSIS
+
+ # Listing all Astribanks:
+ use Zaptel::Xpp;
+ # scans hardware:
+ my @xbuses = Zaptel::Xpp::xbuses("SORT_CONNECTOR");
+ for my $xbus (@xbuses) {
+ print $xbus->name." (".$xbus->label .", ". $xbus->connector .")\n";
+ for my $xpd ($xbus->xpds) {
+ print " - ".$xpd->fqn,"\n";
+ }
+ }
+=cut
+
+
+my $proc_base = "/proc/xpp";
+
+# Nominal sorters for xbuses
+sub by_name {
+ return $a->name cmp $b->name;
+}
+
+sub by_connector {
+ return $a->connector cmp $b->connector;
+}
+
+sub by_label {
+ my $cmp = $a->label cmp $b->label;
+ return $cmp if $cmp != 0;
+ return $a->connector cmp $b->connector;
+}
+
+=head1 xbuses([sort_order])
+
+Scans system (/proc and /sys) and returns a list of Astribank (Xbus)
+objects. The optional parameter sort_order is the order in which
+the Astribanks will be returns:
+
+=over
+
+=item SORT_CONNECTOR
+
+Sort by the connector string. For USB this defines the "path" to get to
+the device through controllers, hubs etc.
+
+=item SORT_LABEL
+
+Sorts by the label of the Astribank. The label field is unique to the
+Astribank. It can also be viewed through 'lsusb -v' without the drivers
+loaded (the iSerial field in the Device Descriptor).
+
+=item SORT_NAME
+
+Sort by the "name". e.g: "XBUS-00". The order of Astribank names depends
+on the load order, and hence may change between different runs.
+
+=item custom function
+
+Instead of using a predefined sorter, you can pass your own sorting
+function. See the example sorters in the code of this module.
+
+=back
+
+=cut
+
+sub xbuses {
+ my $optsort = shift || 'SORT_CONNECTOR';
+ my @xbuses;
+
+ -d "$proc_base" or return ();
+ my @lines;
+ local $/ = "\n";
+ open(F, "$proc_base/xbuses") ||
+ die "$0: Failed to open $proc_base/xbuses: $!\n";
+ @lines = <F>;
+ close F;
+ foreach my $line (@lines) {
+ chomp $line;
+ my ($name, @attr) = split(/\s+/, $line);
+ $name =~ s/://;
+ $name =~ /XBUS-(\d\d)/ or die "Bad XBUS number: $name";
+ my $num = $1;
+ @attr = map { split(/=/); } @attr;
+ my $xbus = Zaptel::Xpp::Xbus->new(NAME => $name, NUM => $num, @attr);
+ push(@xbuses, $xbus);
+ }
+ my $sorter;
+ if($optsort eq "SORT_CONNECTOR") {
+ $sorter = \&by_connector;
+ } elsif($optsort eq "SORT_NAME") {
+ $sorter = \&by_name;
+ } elsif($optsort eq "SORT_LABEL") {
+ $sorter = \&by_label;
+ } elsif(ref($optsort) eq 'CODE') {
+ $sorter = $optsort;
+ } else {
+ die "Unknown optional sorter '$optsort'";
+ }
+ @xbuses = sort $sorter @xbuses;
+ return @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 $xpd ($xbus->xpds()) {
+ return $xpd if $xpd->fqn eq $span->name;
+ }
+ }
+ return undef;
+}
+
+=head1 sync([new_sync_source])
+
+Gets (and optionally sets) the internal Astribanks synchronization
+source. When used to set sync source, returns the original sync source.
+
+A synchronization source is a value valid writing into /proc/xpp/sync .
+For more information read that file and see README.Astribank .
+
+=cut
+
+sub sync {
+ my $newsync = shift;
+ my $result;
+ my $newapi = 0;
+
+ my $file = "$proc_base/sync";
+ return '' unless -f $file;
+ # First query
+ open(F, "$file") or die "Failed to open $file for reading: $!";
+ while(<F>) {
+ chomp;
+ /SYNC=/ and $newapi = 1;
+ s/#.*//;
+ if(/\S/) { # First non-comment line
+ s/^SYNC=\D*// if $newapi;
+ $result = $_;
+ last;
+ }
+ }
+ close F;
+ if(defined($newsync)) { # Now change
+ $newsync =~ s/.*/\U$&/;
+ if($newsync =~ /^(\d+)$/) {
+ $newsync = ($newapi)? "SYNC=$1" : "$1 0";
+ } elsif($newsync ne 'ZAPTEL') {
+ die "Bad sync parameter '$newsync'";
+ }
+ open(F, ">$file") or die "Failed to open $file for writing: $!";
+ print F $newsync;
+ close(F) or die "Failed in closing $file: $!";
+ }
+ return $result;
+}
+
+=head1 SEE ALSO
+
+=over
+
+=item L<Zaptel::Xpp::Xbus>
+
+Xbus (Astribank) object.
+
+=item L<Zaptel::Xpp::Xpd>
+
+XPD (the rough equivalent of a Zaptel span) object.
+
+=item L<Zaptel::Xpp::Line>
+
+Object for a line: an analog port or a time-slot in a adapter.
+Equivalent of a channel in Zaptel.
+
+=item L<Zaptel>
+
+General documentation in the master package.
+
+=back
+
+=cut
+
+1;
diff --git a/xpp/zconf/Zaptel/Xpp/Line.pm b/xpp/zconf/Zaptel/Xpp/Line.pm
new file mode 100644
index 0000000..2472c3b
--- /dev/null
+++ b/xpp/zconf/Zaptel/Xpp/Line.pm
@@ -0,0 +1,95 @@
+package Zaptel::Xpp::Line;
+#
+# Written by Oron Peled <oron@actcom.co.il>
+# Copyright (C) 2008, Xorcom
+# This program is free software; you can redistribute and/or
+# modify it under the same terms as Perl itself.
+#
+# $Id$
+#
+use strict;
+use Zaptel::Utils;
+
+my $proc_base = "/proc/xpp";
+
+sub new($$$) {
+ my $pack = shift or die "Wasn't called as a class method\n";
+ my $xpd = shift or die;
+ my $index = shift;
+ defined $index or die;
+ my $self = {};
+ bless $self, $pack;
+ $self->{XPD} = $xpd;
+ $self->{INDEX} = $index;
+ return $self;
+}
+
+sub blink($$) {
+ my $self = shift;
+ my $on = shift;
+ my $xpd = $self->xpd;
+ my $result;
+
+ my $file = "$proc_base/" . $xpd->fqn . "/blink";
+ die "$file is missing" unless -f $file;
+ # First query
+ open(F, "$file") or die "Failed to open $file for reading: $!";
+ $result = <F>;
+ chomp $result;
+ close F;
+ if(defined($on)) { # Now change
+ my $onbitmask = 1 << $self->index;
+ my $offbitmask = $result & ~$onbitmask;
+
+ $result = $offbitmask;
+ $result |= $onbitmask if $on;
+ open(F, ">$file") or die "Failed to open $file for writing: $!";
+ print F "$result";
+ if(!close(F)) {
+ if($! == 17) { # EEXISTS
+ # good
+ } else {
+ undef $result;
+ }
+ }
+ }
+ return $result;
+}
+
+sub create_all($$) {
+ my $pack = shift or die "Wasn't called as a class method\n";
+ my $xpd = shift || die;
+ my $procdir = shift || die;
+ local $/ = "\n";
+ my @lines;
+ for(my $i = 0; $i < $xpd->{CHANNELS}; $i++) {
+ my $line = Zaptel::Xpp::Line->new($xpd, $i);
+ push(@lines, $line);
+ }
+ $xpd->{LINES} = \@lines;
+ my ($infofile) = glob "$procdir/*_info";
+ die "Failed globbing '$procdir/*_info'" unless defined $infofile;
+ my $type = $xpd->type;
+ open(F, "$infofile") || die "Failed opening '$infofile': $!";
+ my $battery_info = 0;
+ while (<F>) {
+ chomp;
+ if($type eq 'FXO') {
+ $battery_info = 1 if /^Battery:/;
+ if($battery_info && s/^\s*on\s*:\s*//) {
+ my @batt = split;
+ foreach my $l (@lines) {
+ die unless @batt;
+ my $state = shift @batt;
+ $l->{BATTERY} = ($state eq '+') ? 1 : 0;
+ }
+ $battery_info = 0;
+ die if @batt;
+ }
+ }
+ }
+ close F;
+}
+
+
+1;
diff --git a/xpp/zconf/Zaptel/Xpp/Xbus.pm b/xpp/zconf/Zaptel/Xpp/Xbus.pm
new file mode 100644
index 0000000..e840f14
--- /dev/null
+++ b/xpp/zconf/Zaptel/Xpp/Xbus.pm
@@ -0,0 +1,118 @@
+package Zaptel::Xpp::Xbus;
+#
+# 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 Zaptel::Utils;
+use Zaptel::Xpp::Xpd;
+
+my $proc_base = "/proc/xpp";
+
+sub xpds($) {
+ my $xbus = shift;
+ return @{$xbus->{XPDS}};
+}
+
+sub by_number($) {
+ my $busnumber = shift;
+ die "Missing xbus number parameter" unless defined $busnumber;
+ my @xbuses = Zaptel::Xpp::xbuses();
+
+ my ($xbus) = grep { $_->num == $busnumber } @xbuses;
+ return $xbus;
+}
+
+sub by_label($) {
+ my $label = shift;
+ die "Missing xbus label parameter" unless defined $label;
+ my @xbuses = Zaptel::Xpp::xbuses();
+
+ my ($xbus) = grep { $_->label eq $label } @xbuses;
+ return $xbus;
+}
+
+sub get_xpd_by_number($$) {
+ my $xbus = shift;
+ my $xpdid = shift;
+ die "Missing XPD id parameter" unless defined $xpdid;
+ my @xpds = $xbus->xpds;
+ my ($wanted) = grep { $_->id eq $xpdid } @xpds;
+ return $wanted;
+}
+
+sub new($$) {
+ my $pack = shift or die "Wasn't called as a class method\n";
+ my $self = {};
+ bless $self, $pack;
+ while(@_) {
+ my ($k, $v) = @_;
+ shift; shift;
+ # Keys in all caps
+ $k = uc($k);
+ # Some values are in all caps as well
+ if($k =~ /^(STATUS)$/) {
+ $v = uc($v);
+ }
+ $self->{$k} = $v;
+ }
+ # backward compat for drivers without labels.
+ if(!defined $self->{LABEL}) {
+ $self->{LABEL} = '[]';
+ }
+ $self->{LABEL} =~ s/^\[(.*)\]$/$1/ or die "$self->{NAME}: Bad label";
+ # Fix badly burned labels.
+ $self->{LABEL} =~ s/[[:^print:]]/_/g;
+ $self->{NAME} or die "Missing xbus name";
+ my $prefix = "$proc_base/" . $self->{NAME};
+ my $usbfile = "$prefix/xpp_usb";
+ if(open(F, "$usbfile")) {
+ my $head = <F>;
+ chomp $head;
+ close F;
+ $head =~ s/^device: +([^, ]+)/$1/i or die;
+ $self->{USB_DEVNAME} = $head;
+ }
+ @{$self->{XPDS}} = ();
+ foreach my $dir (glob "$prefix/XPD-??") {
+ my $xpd = Zaptel::Xpp::Xpd->new($self, $dir);
+ push(@{$self->{XPDS}}, $xpd);
+ }
+ @{$self->{XPDS}} = sort { $a->id <=> $b->id } @{$self->{XPDS}};
+ return $self;
+}
+
+sub pretty_xpds($) {
+ my $xbus = shift;
+ my @xpds = sort { $a->id <=> $b->id } $xbus->xpds();
+ my @xpd_types = map { $_->type } @xpds;
+ my $last_type = '';
+ my $mult = 0;
+ my $xpdstr = '';
+ foreach my $curr (@xpd_types) {
+ if(!$last_type || ($curr eq $last_type)) {
+ $mult++;
+ } else {
+ if($mult == 1) {
+ $xpdstr .= "$last_type ";
+ } elsif($mult) {
+ $xpdstr .= "$last_type*$mult ";
+ }
+ $mult = 1;
+ }
+ $last_type = $curr;
+ }
+ if($mult == 1) {
+ $xpdstr .= "$last_type ";
+ } elsif($mult) {
+ $xpdstr .= "$last_type*$mult ";
+ }
+ $xpdstr =~ s/\s*$//; # trim trailing space
+ return $xpdstr;
+}
+
+1;
diff --git a/xpp/zconf/Zaptel/Xpp/Xpd.pm b/xpp/zconf/Zaptel/Xpp/Xpd.pm
new file mode 100644
index 0000000..1ddb5c8
--- /dev/null
+++ b/xpp/zconf/Zaptel/Xpp/Xpd.pm
@@ -0,0 +1,123 @@
+package Zaptel::Xpp::Xpd;
+#
+# 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 Zaptel::Utils;
+use Zaptel::Xpp;
+use Zaptel::Xpp::Line;
+
+my $proc_base = "/proc/xpp";
+
+sub blink($$) {
+ my $self = shift;
+ my $on = shift;
+ my $result;
+
+ my $file = "$proc_base/" . $self->fqn . "/blink";
+ die "$file is missing" unless -f $file;
+ # First query
+ open(F, "$file") or die "Failed to open $file for reading: $!";
+ $result = <F>;
+ chomp $result;
+ close F;
+ if(defined($on) and $on ne $result) { # Now change
+ open(F, ">$file") or die "Failed to open $file for writing: $!";
+ print F ($on)?"0xFFFF":"0";
+ if(!close(F)) {
+ if($! == 17) { # EEXISTS
+ # good
+ } else {
+ undef $result;
+ }
+ }
+ }
+ return $result;
+}
+
+sub zt_registration($$) {
+ my $self = shift;
+ my $on = shift;
+ my $result;
+
+ my $file = "$proc_base/" . $self->fqn . "/zt_registration";
+ die "$file is missing" unless -f $file;
+ # First query
+ open(F, "$file") or die "Failed to open $file for reading: $!";
+ $result = <F>;
+ chomp $result;
+ close F;
+ if(defined($on) and $on ne $result) { # Now change
+ open(F, ">$file") or die "Failed to open $file for writing: $!";
+ print F ($on)?"1":"0";
+ if(!close(F)) {
+ if($! == 17) { # EEXISTS
+ # good
+ } else {
+ undef $result;
+ }
+ }
+ }
+ return $result;
+}
+
+sub xpds_by_spanno() {
+ my @xbuses = Zaptel::Xpp::xbuses("SORT_CONNECTOR");
+ my @xpds = map { $_->xpds } @xbuses;
+ @xpds = grep { $_->spanno } @xpds;
+ @xpds = sort { $a->spanno <=> $b->spanno } @xpds;
+ my @spanno = map { $_->spanno } @xpds;
+ my @idx;
+ @idx[@spanno] = @xpds; # The spanno is the index now
+ return @idx;
+}
+
+sub new($$) {
+ my $pack = shift or die "Wasn't called as a class method\n";
+ my $xbus = shift || die;
+ my $procdir = shift || die;
+ my $self = {};
+ bless $self, $pack;
+ $self->{XBUS} = $xbus;
+ $self->{DIR} = $procdir;
+ local $/ = "\n";
+ open(F, "$procdir/summary") || die "Missing summary file in $procdir";
+ my $head = <F>;
+ chomp $head; # "XPD-00 (BRI_TE ,card present, span 3)"
+ # The driver does not export the number of channels...
+ # Let's find it indirectly
+ while(<F>) {
+ chomp;
+ if(s/^\s*offhook\s*:\s*//) {
+ my @offhook = split;
+ @offhook || die "No channels in '$procdir/summary'";
+ $self->{CHANNELS} = @offhook;
+ last;
+ }
+ }
+ close F;
+ $head =~ s/^(XPD-(\d\d))\s+// || die;
+ $self->{ID} = $2;
+ $self->{FQN} = $xbus->name . "/" . $1;
+ $head =~ s/^.*\(// || die;
+ $head =~ s/\) */, / || die;
+ $head =~ s/\s*,\s*/,/g || die;
+ my ($type,$present,$span,$rest) = split(/,/, $head);
+ #warn "Garbage in '$procdir/summary': rest='$rest'\n" if $rest;
+ if($span =~ s/span\s+(\d+)//) { # since changeset:5119
+ $self->{SPANNO} = $1;
+ }
+ $self->{TYPE} = $type;
+ $self->{IS_BRI} = ($type =~ /BRI_(NT|TE)/);
+ $self->{IS_PRI} = ($type =~ /[ETJ]1_(NT|TE)/);
+ $self->{IS_DIGITAL} = ( $self->{IS_BRI} || $self->{IS_PRI} );
+ Zaptel::Xpp::Line->create_all($self, $procdir);
+ return $self;
+}
+
+1;
diff --git a/xpp/zt_registration b/xpp/zt_registration
new file mode 100755
index 0000000..3bdc642
--- /dev/null
+++ b/xpp/zt_registration
@@ -0,0 +1,125 @@
+#! /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;
+BEGIN { my $dir = dirname($0); unshift(@INC, "$dir", "$dir/zconf"); }
+
+use Zaptel;
+use Zaptel::Span;
+use Zaptel::Xpp;
+use Zaptel::Xpp::Xbus;
+
+sub usage {
+ die "Usage: $0 [on|off|1|0]\n";
+}
+
+@ARGV == 0 or @ARGV == 1 or usage;
+my $on = shift;
+my $verbose = 0;
+my $should_output = 1;
+
+if(defined($on)) { # Translate to booleans
+ $on = uc($on);
+ $on =~ /^(ON|OFF|1|0)$/ or usage;
+ $on = ($on eq 'ON') ? 1 : 0;
+ $should_output = 0 unless $verbose;
+}
+
+sub state2str($) {
+ return (shift)?"on":"off";
+}
+
+sub myprintf {
+ printf @_ if $should_output;
+}
+
+my @spans = Zaptel::spans;
+
+foreach my $xbus (Zaptel::Xpp::xbuses('SORT_CONNECTOR')) {
+ myprintf "%-10s\t%s\t%s\n", $xbus->name, $xbus->label, $xbus->connector;
+ next unless $xbus->status eq 'CONNECTED';
+ foreach my $xpd ($xbus->xpds()) {
+ my $prev = $xpd->zt_registration($on);
+ if(!defined($prev)) { # Failure
+ printf "%s: Failed %s\n", $xpd->fqn, $!;
+ next;
+ }
+ myprintf "\t%-10s: ", $xpd->fqn;
+ if(!defined($on)) { # Query only
+ my ($span) = grep { $_->name eq $xpd->fqn } @spans;
+ my $spanstr = ($span) ? ("Span " . $span->num) : "";
+ myprintf "%s %s\n", state2str($prev), $spanstr ;
+ next;
+ }
+ myprintf "%3s ==> %3s\n", state2str($prev), state2str($on);
+ }
+}
+
+__END__
+
+=head1 NAME
+
+zt_registration - Handle registration of Xorcom XPD modules in zaptel.
+
+=head1 SYNOPSIS
+
+zt_registration [on|off]
+
+=head1 DESCRIPTION
+
+Without parameters, show all connected XPDs sorted by physical connector order.
+Each one is show to be unregistered (off), or registered to a specific zaptel
+span (the span number is shown).
+
+All registerations/deregisterations are sorted by physical connector string.
+
+Span registration should generally always succeed. Span unregistration may
+fail if channels from the span are in use by e.g. asterisk. In such a case
+you'll also see those channels as '(In use)' in the output of lszaptel(8).
+
+=head2 Parameters
+
+off -- deregisters all XPD's from zaptel.
+
+on -- registers all XPD's to zaptel.
+
+=head2 Sample Output
+
+An example of the output of zt_registration for some registered
+Astribanks:
+
+ $ zt_registration
+ XBUS-02 [] usb-0000:00:1d.7-4
+ XBUS-00/XPD-00: on Span 1
+ XBUS-00/XPD-10: on Span 2
+ XBUS-00 [usb:00000126] usb-0000:00:1d.7-2
+ XBUS-02/XPD-00: on Span 3
+ XBUS-02/XPD-10: on Span 4
+ XBUS-02/XPD-20: on Span 5
+ XBUS-02/XPD-30: on Span 6
+ XBUS-01 [usb:00000128] usb-0000:00:1d.7-1
+ XBUS-01/XPD-00: on Span 7
+ XBUS-01/XPD-10: on Span 8
+ XBUS-01/XPD-20: on Span 9
+ XBUS-01/XPD-30: on Span 10
+
+=head1 FILES
+
+=over
+
+=item /proc/xpp/XBUS-nn/XPD-mm/zt_registration
+
+Reading from this file shows if if the if the specific XPD is
+registered. Writing to it 0 or 1 registers / unregisters the device.
+
+This should allow you to register / unregister a specific XPD rather
+than all of them.
+
+=back