diff options
author | Tzafrir Cohen <tzafrir.cohen@xorcom.com> | 2008-06-17 17:49:05 +0000 |
---|---|---|
committer | Tzafrir Cohen <tzafrir.cohen@xorcom.com> | 2008-06-17 17:49:05 +0000 |
commit | 53894e5aa1e733a0321a69bd1b12bd794586b4ab (patch) | |
tree | 3e0d4543ad58256dd39cae60ba6de5d7a91f46a6 /xpp | |
parent | 91d0a6d645e95755c4ae0f0a13919eeb213f6606 (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
Diffstat (limited to 'xpp')
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 |