summaryrefslogtreecommitdiff
path: root/kernel/xpp/xbus-pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/xpp/xbus-pcm.c')
-rw-r--r--kernel/xpp/xbus-pcm.c1168
1 files changed, 1168 insertions, 0 deletions
diff --git a/kernel/xpp/xbus-pcm.c b/kernel/xpp/xbus-pcm.c
new file mode 100644
index 0000000..0b1e5d1
--- /dev/null
+++ b/kernel/xpp/xbus-pcm.c
@@ -0,0 +1,1168 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * Copyright (C) 2004-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.
+ *
+ */
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
+# warning "This module is tested only with 2.6 kernels"
+#endif
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include "xbus-pcm.h"
+#include "xbus-core.h"
+#include "xpp_zap.h"
+#include "zap_debug.h"
+#include "parport_debug.h"
+
+static const char rcsid[] = "$Id$";
+
+extern int print_dbg;
+#ifdef XPP_EC_CHUNK
+#include "supress/ec_xpp.h"
+DEF_PARM_BOOL(xpp_ec, 0, 0444, "Do we use our own (1) or Zaptel's (0) echo canceller");
+#endif
+#ifdef OPTIMIZE_CHANMUTE
+DEF_PARM_BOOL(optimize_chanmute, 1, 0644, "Optimize by muting inactive channels");
+#endif
+
+DEF_PARM(int, disable_pcm, 0, 0644, "Disable all PCM transmissions");
+#ifdef DEBUG_PCMTX
+DEF_PARM(int, pcmtx, -1, 0644, "Forced PCM value to transmit (negative to disable)");
+DEF_PARM(int, pcmtx_chan, 0, 0644, "channel to force PCM value");
+#endif
+DEF_PARM_BOOL(pcm_tasklet, 0, 0644, "Handle PCM in a tasklet (lower interrupt load)");
+#define PCM_TASKLET_DEPRECATION "\n" \
+ "====================================================================\n" \
+ "CONFIGURATION ERROR: 'pcm_tasklet' module parameter is deprecated!!!\n" \
+ "====================================================================\n"
+
+static xbus_t *syncer; /* current syncer */
+static struct xpp_timing ref_sync;
+static atomic_t xpp_tick_counter;
+static const struct xpp_timing *global_ticker; /* increment xpp_tick_counter */
+static bool zaptel_syncer = 0;
+
+#define PROC_SYNC "sync"
+#define BIG_TICK_INTERVAL 1000
+#define SYNC_ADJ_MIN (-30) /* minimal firmware drift unit */
+#define SYNC_ADJ_MAX 30 /* maximal firmware drift unit */
+#define SYNC_ADJ_FACTOR(x) ((x) / 30) /* average usec/drift_unit */
+
+#ifdef ZAPTEL_SYNC_TICK
+static unsigned int zaptel_tick_count = 0;
+#endif
+
+/*------------------------- SYNC Handling --------------------------*/
+
+
+const char *sync_mode_name(enum sync_mode mode)
+{
+ static const char *sync_mode_names[] = {
+ [SYNC_MODE_AB] "SYNC_MODE_AB",
+ [SYNC_MODE_NONE] "SYNC_MODE_NONE",
+ [SYNC_MODE_PLL] "SYNC_MODE_PLL",
+ [SYNC_MODE_QUERY] "SYNC_MODE_QUERY",
+ };
+ if(mode >= ARRAY_SIZE(sync_mode_names))
+ return NULL;
+ return sync_mode_names[mode];
+}
+
+static void xpp_set_syncer(xbus_t *xbus, bool on)
+{
+ if(syncer != xbus && on) {
+ XBUS_DBG(SYNC, xbus, "New syncer\n");
+ syncer = xbus;
+ } else if(syncer == xbus && !on) {
+ XBUS_DBG(SYNC, xbus, "Lost syncer\n");
+ syncer = NULL;
+ } else
+ XBUS_DBG(SYNC, xbus, "ignore %s (current syncer: %s)\n",
+ (on)?"ON":"OFF",
+ (syncer) ? syncer->busname : "NO-SYNC");
+}
+
+void xpp_timing_init(struct xpp_timing *timing, const char *name)
+{
+ memset(timing, 0, sizeof(*timing));
+ do_gettimeofday(&timing->timing_val);
+ spin_lock_init(&timing->lock);
+ timing->name = name;
+}
+
+#define XPP_TIMING_SAMPLES 50
+#define XPP_TIMING_TICKS 100
+#define XPP_TIMING_MAX_STDDEV 500
+
+static void xpp_timing_tick(struct xpp_timing *timing, const struct timeval *val)
+{
+ long usec;
+ int diff_sec;
+ int diff_usec;
+ unsigned long flags;
+
+ spin_lock_irqsave(&timing->lock, flags);
+ if((timing->timing_count % XPP_TIMING_TICKS) != 0)
+ goto out;
+ diff_sec = val->tv_sec - timing->timing_val.tv_sec;
+ diff_usec = val->tv_usec - timing->timing_val.tv_usec;
+ timing->timing_val = *val;
+ /* ignore first batch of samples */
+ if(timing->timing_count < (XPP_TIMING_TICKS * XPP_TIMING_SAMPLES))
+ goto out;
+ if(abs(diff_sec) > 2) {
+ static int rate_limit;
+
+ if((rate_limit++ % 1003) == 0)
+ NOTICE("TIMING(%s): bad timing: diff_sec=%d\n",
+ timing->name, diff_sec);
+ goto out;
+ }
+ usec = diff_sec * 1000000 + diff_usec;
+ if(usec)
+ timing->tick_rate = XPP_TIMING_TICKS * 1000000 / usec;
+ usec -= 1000 * XPP_TIMING_TICKS; /* normalize */
+
+ timing->accumulated_usec += usec;
+ timing->accumulated_usec_sqr += usec * usec;
+ if((timing->timing_count % (XPP_TIMING_TICKS * XPP_TIMING_SAMPLES)) == 0) {
+ int avg;
+ int stddev;
+
+ avg = timing->accumulated_usec / XPP_TIMING_SAMPLES;
+ stddev = (timing->accumulated_usec_sqr / XPP_TIMING_SAMPLES);
+ stddev = int_sqrt(stddev);
+ timing->accumulated_usec = 0;
+ timing->accumulated_usec_sqr = 0;
+ if(stddev > XPP_TIMING_MAX_STDDEV) {
+ static int rate_limit;
+
+ if((rate_limit++ % 1003) == 0)
+ NOTICE("TIMING(%s): bad timing: stddev=%d avg=%d\n",
+ timing->name, stddev, avg);
+ goto out;
+ }
+ timing->tick_avg = avg;
+ timing->tick_stddev = stddev;
+ }
+out:
+ timing->timing_count++;
+ if(timing == global_ticker)
+ atomic_inc(&xpp_tick_counter);
+ spin_unlock_irqrestore(&timing->lock, flags);
+}
+
+void xbus_command_timer(unsigned long param)
+{
+ xbus_t *xbus = (xbus_t *)param;
+ struct timeval now;
+
+ BUG_ON(!xbus);
+ do_gettimeofday(&now);
+ xbus_command_queue_tick(xbus);
+ if(!xbus->self_ticking)
+ mod_timer(&xbus->command_timer, jiffies + 1); /* Must be 1KHz rate */
+}
+
+void xbus_set_command_timer(xbus_t *xbus, bool on)
+{
+ XBUS_DBG(SYNC, xbus, "%s\n", (on)?"ON":"OFF");
+ if(on) {
+ if(!timer_pending(&xbus->command_timer)) {
+ XBUS_DBG(SYNC, xbus, "add_timer\n");
+ xbus->command_timer.function = xbus_command_timer;
+ xbus->command_timer.data = (unsigned long)xbus;
+ xbus->command_timer.expires = jiffies + 1;
+ add_timer(&xbus->command_timer);
+ xbus->self_ticking = 0;
+ }
+ } else if(timer_pending(&xbus->command_timer)) {
+ xbus->self_ticking = 1;
+ XBUS_DBG(SYNC, xbus, "del_timer\n");
+ del_timer(&xbus->command_timer);
+ }
+}
+
+/*
+ * Called when the Astribank replies to a sync change request
+ */
+void got_new_syncer(xbus_t *xbus, enum sync_mode mode, int drift)
+{
+ unsigned long flags;
+
+ XBUS_DBG(SYNC, xbus, "%s (%d), drift=%d (pcm_rx_counter=%d)\n",
+ sync_mode_name(mode), mode, drift, atomic_read(&xbus->pcm_rx_counter));
+ spin_lock_irqsave(&xbus->lock, flags);
+ xbus->sync_adjustment = (signed char)drift;
+ if(xbus->sync_mode == mode) {
+ XBUS_DBG(SYNC, xbus, "Already in %s. Ignored\n", sync_mode_name(mode));
+ goto out;
+ }
+ switch(mode) {
+ case SYNC_MODE_AB:
+ xbus->sync_mode = mode;
+ xbus_set_command_timer(xbus, 0);
+ xpp_set_syncer(xbus, 1);
+ break;
+ case SYNC_MODE_PLL:
+ xbus->sync_mode = mode;
+ xbus_set_command_timer(xbus, 0);
+ xpp_set_syncer(xbus, 0);
+ break;
+ case SYNC_MODE_NONE: /* lost sync source */
+ xbus->sync_mode = mode;
+ xbus_set_command_timer(xbus, 1);
+ xpp_set_syncer(xbus, 0);
+ break;
+ case SYNC_MODE_QUERY: /* ignore */
+ break;
+ default:
+ XBUS_ERR(xbus, "%s: unknown mode=0x%X\n", __FUNCTION__, mode);
+ }
+out:
+ spin_unlock_irqrestore(&xbus->lock, flags);
+}
+
+void xbus_request_sync(xbus_t *xbus, enum sync_mode mode)
+{
+ BUG_ON(!xbus);
+ XBUS_DBG(SYNC, xbus, "sent request (mode=%d)\n", mode);
+ CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, mode, 0);
+}
+
+static void reset_sync_counters(void)
+{
+ int i;
+
+ //DBG(SYNC, "%d\n", atomic_read(&xpp_tick_counter));
+ for(i = 0; i < MAX_BUSES; i++) {
+ xbus_t *xbus = get_xbus(i);
+
+ if(!xbus)
+ continue;
+ if (TRANSPORT_RUNNING(xbus)) {
+ if(XBUS_GET(xbus)) {
+ /* Reset sync LEDs once in a while */
+ CALL_PROTO(GLOBAL, RESET_SYNC_COUNTERS, xbus, NULL);
+ XBUS_PUT(xbus);
+ } else {
+ static int rate_limit;
+
+ if((rate_limit++ % 1003) == 0)
+ XBUS_DBG(GENERAL, xbus,
+ "Dropped packet. Is shutting down. (%d)\n", rate_limit);
+ }
+ }
+ put_xbus(xbus);
+ }
+}
+
+static void send_drift(xbus_t *xbus, int drift)
+{
+ struct timeval now;
+ const char *msg;
+
+ BUG_ON(drift < SYNC_ADJ_MIN || drift > SYNC_ADJ_MAX);
+ do_gettimeofday(&now);
+ if(drift > xbus->sync_adjustment)
+ msg = "up";
+ else
+ msg = "down";
+ XBUS_DBG(SYNC, xbus, "DRIFT adjust %s (%d) (last update %ld seconds ago)\n",
+ msg, drift, now.tv_sec - xbus->pll_updated_at);
+ CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, SYNC_MODE_PLL, drift);
+ xbus->pll_updated_at = now.tv_sec;
+}
+
+#ifdef ZAPTEL_SYNC_TICK
+int zaptel_sync_tick(struct zt_span *span, int is_master)
+{
+ xpd_t *xpd = span->pvt;
+ struct timeval now;
+ static int redundant_ticks; /* for extra spans */
+
+ if(!zaptel_syncer)
+ goto noop;
+ BUG_ON(!xpd);
+ /*
+ * Detect if any of our spans is zaptel sync master
+ */
+ if(is_master) {
+ static int rate_limit;
+
+ if(xpd->xbus != syncer && ((rate_limit % 1003) == 0)) {
+ XPD_ERR(xpd,
+ "Zaptel master, but syncer=%s\n",
+ xpd->xbus->busname);
+ }
+ if((rate_limit % 5003) == 0)
+ XPD_NOTICE(xpd, "Zaptel master: ignore ZAPTEL sync\n");
+ rate_limit++;
+ goto noop;
+ }
+ /* Now we know for sure someone else is zaptel sync master */
+ if(syncer) {
+ static int rate_limit;
+
+ if((rate_limit++ % 5003) == 0)
+ XBUS_DBG(SYNC, syncer,
+ "Already a syncer, ignore ZAPTEL sync\n");
+ goto noop;
+ }
+ /* ignore duplicate calls from all our registered spans */
+ if((redundant_ticks++ % total_registered_spans()) != 0) {
+#if 0
+ static int rate_limit;
+
+ if((rate_limit++ % 1003) < 16)
+ XPD_NOTICE(xpd, "boop (%d)\n", zaptel_tick_count);
+#endif
+ goto noop;
+ }
+ do_gettimeofday(&now);
+ xpp_timing_tick(&ref_sync, &now);
+ zaptel_tick_count++;
+ flip_parport_bit(1);
+ return 0;
+noop:
+ return 0; /* No auto sync from zaptel */
+}
+#endif
+
+
+static void sync_rate_adjust(xbus_t *xbus)
+{
+ int offset;
+
+ xbus->sync_offset_usec = xbus->timing.tick_avg - ref_sync.tick_avg;
+ /* Calculate required PLL fix */
+ offset = SYNC_ADJ_FACTOR(xbus->sync_offset_usec);
+ if(offset < SYNC_ADJ_MIN)
+ offset = SYNC_ADJ_MIN;
+ if(offset > SYNC_ADJ_MAX)
+ offset = SYNC_ADJ_MAX;
+ xbus->sync_adjustment_offset = offset;
+ if(xbus != syncer && xbus->sync_adjustment != offset)
+ send_drift(xbus, offset);
+}
+
+/*
+ * called from elect_syncer()
+ * if new_syncer is NULL, than we move all to SYNC_MODE_PLL
+ * for ZAPTEL sync.
+ */
+static void update_sync_master(xbus_t *new_syncer)
+{
+ const char *msg = (zaptel_syncer) ? "ZAPTEL" : "NO-SYNC";
+ int i;
+
+ DBG(SYNC, "%s => %s\n",
+ (syncer) ? syncer->busname : msg,
+ (new_syncer) ? new_syncer->busname : msg);
+ if(new_syncer) {
+ XBUS_DBG(SYNC, new_syncer, "pcm_rx_counter=%d\n",
+ atomic_read(&new_syncer->pcm_rx_counter));
+ zaptel_syncer = 0;
+ global_ticker = &new_syncer->timing;
+ xbus_request_sync(new_syncer, SYNC_MODE_AB);
+ } else
+ global_ticker = &ref_sync;
+ DBG(SYNC, "stop unwanted syncers\n");
+ /* Shut all down except the wanted sync master */
+ for(i = 0; i < MAX_BUSES; i++) {
+ xbus_t *xbus = get_xbus(i);
+ if(!xbus)
+ continue;
+ if(TRANSPORT_RUNNING(xbus) && xbus != new_syncer) {
+ if(xbus->self_ticking)
+ xbus_request_sync(xbus, SYNC_MODE_PLL);
+ else
+ XBUS_DBG(SYNC, xbus, "Not self_ticking yet. Ignore\n");
+ }
+ put_xbus(xbus);
+ }
+}
+
+void elect_syncer(const char *msg)
+{
+ int i;
+ int j;
+ uint timing_priority = 0;
+ xpd_t *best_xpd = NULL;
+ xbus_t *the_xbus = NULL;
+
+ for(i = 0; i < MAX_BUSES; i++) {
+ xbus_t *xbus = get_xbus(i);
+ if(!xbus)
+ continue;
+ if(!the_xbus)
+ the_xbus = xbus;
+ if (TRANSPORT_RUNNING(xbus)) {
+ for(j = 0; j < MAX_XPDS; j++) {
+ xpd_t *xpd = xpd_of(xbus, j);
+
+ if(!xpd)
+ continue;
+ if(xpd->timing_priority > timing_priority) {
+ timing_priority = xpd->timing_priority;
+ best_xpd = xpd;
+ }
+ }
+ }
+ put_xbus(xbus);
+ }
+ if(best_xpd) {
+ the_xbus = best_xpd->xbus;
+ XPD_DBG(SYNC, best_xpd, "%s: elected with priority %d\n", msg, timing_priority);
+ } else if(the_xbus) {
+ XBUS_DBG(SYNC, the_xbus, "%s: elected\n", msg);
+ } else
+ DBG(SYNC, "%s: No more syncers\n", msg);
+ if(the_xbus != syncer)
+ update_sync_master(the_xbus);
+}
+
+/*
+ * This function is used by FXS/FXO. The pcm_mask argument signifies
+ * channels which should be *added* to the automatic calculation.
+ * Normally, this argument is 0.
+ *
+ * The caller should spinlock the XPD before calling it.
+ */
+void __pcm_recompute(xpd_t *xpd, xpp_line_t pcm_mask)
+{
+ int i;
+ int line_count = 0;
+
+ /* Add/remove all the trivial cases */
+ pcm_mask |= xpd->offhook;
+ pcm_mask |= xpd->cid_on;
+ pcm_mask &= ~xpd->digital_signalling; /* No PCM in D-Channels */
+ pcm_mask &= ~xpd->digital_inputs;
+ pcm_mask &= ~xpd->digital_outputs;
+ for_each_line(xpd, i)
+ if(IS_SET(pcm_mask, i))
+ line_count++;
+ /*
+ * FIXME: Workaround a bug in sync code of the Astribank.
+ * Send dummy PCM for sync.
+ */
+ if(xpd->addr.unit == 0 && pcm_mask == 0) {
+ pcm_mask = BIT(0);
+ line_count = 1;
+ }
+ xpd->pcm_len = (line_count)
+ ? RPACKET_HEADERSIZE + sizeof(xpp_line_t) + line_count * ZT_CHUNKSIZE
+ : 0L;
+ xpd->wanted_pcm_mask = pcm_mask;
+}
+
+/*
+ * A spinlocked version of __pcm_recompute()
+ */
+void pcm_recompute(xpd_t *xpd, xpp_line_t pcm_mask)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&xpd->lock, flags);
+ __pcm_recompute(xpd, pcm_mask);
+ spin_unlock_irqrestore(&xpd->lock, flags);
+}
+
+void fill_beep(u_char *buf, int num, int duration)
+{
+ bool alternate = (duration) ? (jiffies/(duration*1000)) & 0x1 : 0;
+ int which;
+ u_char *snd;
+
+ /*
+ * debug tones
+ */
+ static u_char beep[] = {
+ 0x7F, 0xBE, 0xD8, 0xBE, 0x80, 0x41, 0x24, 0x41, /* Dima */
+ 0x67, 0x90, 0x89, 0x90, 0xFF, 0x10, 0x09, 0x10, /* Izzy */
+ };
+ static u_char beep_alt[] = {
+ 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, /* silence */
+ };
+ if(alternate) {
+ which = num % ARRAY_SIZE(beep_alt);
+ snd = &beep_alt[which];
+ } else {
+ which = num % ARRAY_SIZE(beep);
+ snd = &beep[which];
+ }
+ memcpy(buf, snd, ZT_CHUNKSIZE);
+}
+
+#ifdef XPP_EC_CHUNK
+/*
+ * Taken from zaptel.c
+ */
+static inline void xpp_ec_chunk(struct zt_chan *chan, unsigned char *rxchunk, const unsigned char *txchunk)
+{
+ int16_t rxlin;
+ int x;
+ unsigned long flags;
+
+ /* Perform echo cancellation on a chunk if necessary */
+ if (!chan->ec)
+ return;
+ spin_lock_irqsave(&chan->lock, flags);
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ rxlin = ZT_XLAW(rxchunk[x], chan);
+ rxlin = xpp_echo_can_update(chan->ec, ZT_XLAW(txchunk[x], chan), rxlin);
+ rxchunk[x] = ZT_LIN2X((int)rxlin, chan);
+ }
+ spin_unlock_irqrestore(&chan->lock, flags);
+}
+#endif
+
+static void do_ec(xpd_t *xpd)
+{
+ struct zt_chan *chans = xpd->span.chans;
+ int i;
+
+#ifdef WITH_ECHO_SUPPRESSION
+ /* FIXME: need to Echo cancel double buffered data */
+ for (i = 0;i < xpd->span.channels; i++) {
+ if(unlikely(IS_SET(xpd->digital_signalling, i))) /* Don't echo cancel BRI D-chans */
+ continue;
+#ifdef XPP_EC_CHUNK
+ /* even if defined, parameterr xpp_ec can override at run-time */
+ if (xpp_ec)
+ xpp_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]);
+ else
+#endif
+ zt_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]);
+ memcpy(xpd->ec_chunk2[i], xpd->ec_chunk1[i], ZT_CHUNKSIZE);
+ memcpy(xpd->ec_chunk1[i], chans[i].writechunk, ZT_CHUNKSIZE);
+ }
+#endif
+}
+
+#if 0
+/* Okay, now we get to the signalling. You have several options: */
+
+/* Option 1: If you're a T1 like interface, you can just provide a
+ rbsbits function and we'll assert robbed bits for you. Be sure to
+ set the ZT_FLAG_RBS in this case. */
+
+/* Opt: If the span uses A/B bits, set them here */
+int (*rbsbits)(struct zt_chan *chan, int bits);
+
+/* Option 2: If you don't know about sig bits, but do have their
+ equivalents (i.e. you can disconnect battery, detect off hook,
+ generate ring, etc directly) then you can just specify a
+ sethook function, and we'll call you with appropriate hook states
+ to set. Still set the ZT_FLAG_RBS in this case as well */
+int (*hooksig)(struct zt_chan *chan, zt_txsig_t hookstate);
+
+/* Option 3: If you can't use sig bits, you can write a function
+ which handles the individual hook states */
+int (*sethook)(struct zt_chan *chan, int hookstate);
+#endif
+
+int xpp_echocan(struct zt_chan *chan, int len)
+{
+#ifdef XPP_EC_CHUNK
+ if(len == 0) { /* shut down */
+ /* zaptel calls this also during channel initialization */
+ if(chan->ec) {
+ xpp_echo_can_free(chan->ec);
+ }
+ return 0;
+ }
+ if(chan->ec) {
+ ERR("%s: Trying to override an existing EC (%p)\n", __FUNCTION__, chan->ec);
+ return -EINVAL;
+ }
+ chan->ec = xpp_echo_can_create(len, 0);
+ if(!chan->ec) {
+ ERR("%s: Failed creating xpp EC (len=%d)\n", __FUNCTION__, len);
+ return -EINVAL;
+ }
+#endif
+ return 0;
+}
+
+static bool pcm_valid(xpd_t *xpd, xpacket_t *pack)
+{
+ xpp_line_t lines = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines);
+ int i;
+ int count = 0;
+ uint16_t good_len;
+
+ BUG_ON(!pack);
+ BUG_ON(XPACKET_OP(pack) != XPROTO_NAME(GLOBAL, PCM_READ));
+ /*
+ * Don't use for_each_line(xpd, i) here because for BRI it will
+ * ignore the channels of the other xpd's in the same unit.
+ */
+ for (i = 0; i < CHANNELS_PERXPD; i++)
+ if(IS_SET(lines, i))
+ count++;
+ /* FRAMES: include opcode in calculation */
+ good_len = RPACKET_HEADERSIZE + sizeof(xpp_line_t) + count * 8;
+ if(XPACKET_LEN(pack) != good_len) {
+ static int rate_limit = 0;
+
+ XPD_COUNTER(xpd, RECV_ERRORS)++;
+ if((rate_limit++ % 1000) <= 10) {
+ XPD_ERR(xpd, "BAD PCM REPLY: packet_len=%d (should be %d), count=%d\n",
+ XPACKET_LEN(pack), good_len, count);
+ dump_packet("BAD PCM REPLY", pack, 1);
+ }
+ return 0;
+ }
+ return 1;
+}
+
+
+
+static inline void pcm_frame_out(xbus_t *xbus, xframe_t *xframe)
+{
+ unsigned long flags;
+ struct timeval now;
+ unsigned long usec;
+
+ spin_lock_irqsave(&xbus->lock, flags);
+ do_gettimeofday(&now);
+ if(unlikely(disable_pcm || !TRANSPORT_RUNNING(xbus)))
+ goto dropit;
+ if(XPACKET_ADDR_SYNC((xpacket_t *)xframe->packets)) {
+ usec = usec_diff(&now, &xbus->last_tx_sync);
+ xbus->last_tx_sync = now;
+ /* ignore startup statistics */
+ if(likely(atomic_read(&xbus->pcm_rx_counter) > BIG_TICK_INTERVAL)) {
+ if(abs(usec - 1000) > TICK_TOLERANCE) {
+ static int rate_limit;
+
+ if((rate_limit++ % 5003) == 0)
+ XBUS_DBG(SYNC, xbus, "Bad PCM TX timing(%d): usec=%ld.\n",
+ rate_limit, usec);
+ }
+ if(usec > xbus->max_tx_sync)
+ xbus->max_tx_sync = usec;
+ if(usec < xbus->min_tx_sync)
+ xbus->min_tx_sync = usec;
+ }
+ }
+ spin_unlock_irqrestore(&xbus->lock, flags);
+ /* OK, really send it */
+ if(print_dbg & DBG_PCM )
+ dump_xframe("TX_XFRAME_PCM", xbus, xframe);
+ send_pcm_frame(xbus, xframe);
+ XBUS_COUNTER(xbus, TX_XFRAME_PCM)++;
+ return;
+dropit:
+ spin_unlock_irqrestore(&xbus->lock, flags);
+ FREE_SEND_XFRAME(xbus, xframe);
+}
+
+/*
+ * Generic implementations of card_pcmfromspan()/card_pcmtospan()
+ * For FXS/FXO
+ */
+void generic_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, xpacket_t *pack)
+{
+ byte *pcm;
+ struct zt_chan *chans;
+ unsigned long flags;
+ int i;
+
+ BUG_ON(!xbus);
+ BUG_ON(!xpd);
+ BUG_ON(!pack);
+ RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = lines;
+ pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm);
+ spin_lock_irqsave(&xpd->lock, flags);
+ chans = xpd->span.chans;
+ for (i = 0; i < xpd->channels; i++) {
+ if(IS_SET(lines, i)) {
+ if(SPAN_REGISTERED(xpd)) {
+#ifdef DEBUG_PCMTX
+ if(pcmtx >= 0 && pcmtx_chan == i)
+ memset((u_char *)pcm, pcmtx, ZT_CHUNKSIZE);
+ else
+#endif
+ memcpy((u_char *)pcm, chans[i].writechunk, ZT_CHUNKSIZE);
+ // fill_beep((u_char *)pcm, xpd->addr.subunit, 2);
+ } else
+ memset((u_char *)pcm, 0x7F, ZT_CHUNKSIZE);
+ pcm += ZT_CHUNKSIZE;
+ }
+ }
+ XPD_COUNTER(xpd, PCM_WRITE)++;
+ spin_unlock_irqrestore(&xpd->lock, flags);
+}
+
+void generic_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
+{
+ byte *pcm;
+ xpp_line_t pcm_mask;
+ unsigned long flags;
+ int i;
+
+ pcm = RPACKET_FIELD(pack, GLOBAL, PCM_READ, pcm);
+ pcm_mask = RPACKET_FIELD(pack, GLOBAL, PCM_READ, lines);
+ spin_lock_irqsave(&xpd->lock, flags);
+ if(!SPAN_REGISTERED(xpd))
+ goto out;
+ for (i = 0; i < xpd->channels; i++) {
+ volatile u_char *r = xpd->span.chans[i].readchunk;
+
+ if(!IS_SET(xpd->wanted_pcm_mask, i)) {
+ if(IS_SET(xpd->silence_pcm, i))
+ memset((u_char *)r, 0x7F, ZT_CHUNKSIZE); // SILENCE
+ continue;
+ }
+ pcm_mask &= ~xpd->mute_dtmf;
+ if(IS_SET(pcm_mask, i)) {
+ // memset((u_char *)r, 0x5A, ZT_CHUNKSIZE); // DEBUG
+ // fill_beep((u_char *)r, 1, 1); // DEBUG: BEEP
+ memcpy((u_char *)r, pcm, ZT_CHUNKSIZE);
+ pcm += ZT_CHUNKSIZE;
+ } else {
+ memset((u_char *)r, 0x7F, ZT_CHUNKSIZE); // SILENCE
+ }
+ }
+out:
+ XPD_COUNTER(xpd, PCM_READ)++;
+ spin_unlock_irqrestore(&xpd->lock, flags);
+}
+
+static int copy_pcm_tospan(xbus_t *xbus, xframe_t *xframe)
+{
+ byte *xframe_end;
+ xpacket_t *pack;
+ byte *p;
+ int ret = -EPROTO; /* Assume error */
+
+ if(print_dbg & DBG_PCM)
+ dump_xframe("RX_XFRAME_PCM", xbus, xframe);
+ /* handle content */
+
+ p = xframe->packets;
+ xframe_end = p + XFRAME_LEN(xframe);
+ do {
+ int len;
+ xpd_t *xpd;
+
+ pack = (xpacket_t *)p;
+ len = XPACKET_LEN(pack);
+ /* Sanity checks */
+ if(unlikely(XPACKET_OP(pack) != XPROTO_NAME(GLOBAL,PCM_READ))) {
+ static int rate_limit;
+
+ if((rate_limit++ % 1003) == 0) {
+ XBUS_NOTICE(xbus,
+ "%s: Non-PCM packet within a PCM xframe. (%d)\n",
+ __FUNCTION__, rate_limit);
+ dump_xframe("In PCM xframe", xbus, xframe);
+ }
+ goto out;
+ }
+ p += len;
+ if(p > xframe_end || len < RPACKET_HEADERSIZE) {
+ static int rate_limit;
+
+ if((rate_limit++ % 1003) == 0) {
+ XBUS_NOTICE(xbus,
+ "%s: Invalid packet length %d. (%d)\n",
+ __FUNCTION__, len, rate_limit);
+ dump_xframe("BAD LENGTH", xbus, xframe);
+ }
+ goto out;
+ }
+ xpd = xpd_byaddr(xbus, XPACKET_ADDR_UNIT(pack), XPACKET_ADDR_SUBUNIT(pack));
+ if(unlikely(!xpd)) {
+ static int rate_limit;
+
+ if((rate_limit++ % 1003) == 0) {
+ notify_bad_xpd(__FUNCTION__, xbus, XPACKET_ADDR(pack), "RECEIVE PCM");
+ dump_xframe("Unknown XPD addr", xbus, xframe);
+ }
+ goto out;
+ }
+ if(!pcm_valid(xpd, pack))
+ goto out;
+ if(SPAN_REGISTERED(xpd)) {
+ XBUS_COUNTER(xbus, RX_PACK_PCM)++;
+ CALL_XMETHOD(card_pcm_tospan, xbus, xpd, pack);
+ }
+ } while(p < xframe_end);
+ ret = 0; /* all good */
+ XBUS_COUNTER(xbus, RX_XFRAME_PCM)++;
+out:
+ FREE_RECV_XFRAME(xbus, xframe);
+ return ret;
+}
+
+static void xbus_tick(xbus_t *xbus)
+{
+ int i;
+ xpd_t *xpd;
+ xframe_t *xframe = NULL;
+ xpacket_t *pack = NULL;
+ size_t pcm_len;
+ bool sent_sync_bit = 0;
+
+ /*
+ * Update zaptel
+ */
+ for(i = 0; i < MAX_XPDS; i++) {
+ xpd = xpd_of(xbus, i);
+ if(xpd && SPAN_REGISTERED(xpd)) {
+#ifdef OPTIMIZE_CHANMUTE
+ int j;
+ xpp_line_t xmit_mask = xpd->wanted_pcm_mask;
+
+ xmit_mask |= xpd->silence_pcm;
+ xmit_mask |= xpd->digital_signalling;
+ for_each_line(xpd, j) {
+ xpd->chans[j].chanmute = (optimize_chanmute)
+ ? !IS_SET(xmit_mask, j)
+ : 0;
+ }
+#endif
+ /*
+ * calls to zt_transmit should be out of spinlocks, as it may call back
+ * our hook setting methods.
+ */
+ zt_transmit(&xpd->span);
+ }
+ }
+ /*
+ * Fill xframes
+ */
+ for(i = 0; i < MAX_XPDS; i++) {
+ if((xpd = xpd_of(xbus, i)) == NULL)
+ continue;
+ pcm_len = xpd->pcm_len;
+ if(SPAN_REGISTERED(xpd)) {
+ if(pcm_len && xpd->card_present) {
+ do {
+ // pack = NULL; /* FORCE single packet frames */
+ if(xframe && !pack) { /* FULL frame */
+ pcm_frame_out(xbus, xframe);
+ xframe = NULL;
+ XBUS_COUNTER(xbus, TX_PCM_FRAG)++;
+ }
+ if(!xframe) { /* Alloc frame */
+ xframe = ALLOC_SEND_XFRAME(xbus);
+ if (!xframe) {
+ static int rate_limit;
+
+ if((rate_limit++ % 3001) == 0)
+ XBUS_ERR(xbus,
+ "%s: failed to allocate new xframe\n",
+ __FUNCTION__);
+ return;
+ }
+ }
+ pack = xframe_next_packet(xframe, pcm_len);
+ } while(!pack);
+ XPACKET_INIT(pack, GLOBAL, PCM_WRITE, xpd->xbus_idx, 1, 0);
+ XPACKET_LEN(pack) = pcm_len;
+ if(!sent_sync_bit) {
+ XPACKET_ADDR_SYNC(pack) = 1;
+ sent_sync_bit = 1;
+ }
+ CALL_XMETHOD(card_pcm_fromspan, xbus, xpd, xpd->wanted_pcm_mask, pack);
+ XBUS_COUNTER(xbus, TX_PACK_PCM)++;
+ }
+ }
+ }
+ if(xframe) /* clean any leftovers */
+ pcm_frame_out(xbus, xframe);
+ /*
+ * Receive PCM
+ */
+ i = atomic_read(&xbus->pcm_rx_counter) & 1;
+ while((xframe = xframe_dequeue(&xbus->pcm_tospan[i])) != NULL) {
+ copy_pcm_tospan(xbus, xframe);
+ if(XPACKET_ADDR_SYNC((xpacket_t *)xframe->packets)) {
+ struct timeval now;
+ unsigned long usec;
+
+ do_gettimeofday(&now);
+ usec = usec_diff(&now, &xbus->last_rx_sync);
+ xbus->last_rx_sync = now;
+ /* ignore startup statistics */
+ if(likely(atomic_read(&xbus->pcm_rx_counter) > BIG_TICK_INTERVAL)) {
+ if(abs(usec - 1000) > TICK_TOLERANCE) {
+ static int rate_limit;
+
+ if((rate_limit++ % 5003) == 0)
+ XBUS_DBG(SYNC, xbus, "Bad PCM RX timing(%d): usec=%ld.\n",
+ rate_limit, usec);
+ }
+ if(usec > xbus->max_rx_sync)
+ xbus->max_rx_sync = usec;
+ if(usec < xbus->min_rx_sync)
+ xbus->min_rx_sync = usec;
+ }
+ }
+ }
+ for(i = 0; i < MAX_XPDS; i++) {
+ xpd = xpd_of(xbus, i);
+ if(!xpd || !xpd->card_present)
+ continue;
+ if(SPAN_REGISTERED(xpd)) {
+ do_ec(xpd);
+ zt_receive(&xpd->span);
+ }
+ xpd->silence_pcm = 0; /* silence was injected */
+ xpd->timer_count = xbus->global_counter;
+ /*
+ * Must be called *after* tx/rx so
+ * D-Chan counters may be cleared
+ */
+ CALL_XMETHOD(card_tick, xbus, xpd);
+ }
+}
+
+void do_tick(xbus_t *xbus, struct timeval tv_received)
+{
+ int counter = atomic_read(&xpp_tick_counter);
+
+ xbus_command_queue_tick(xbus);
+ xpp_timing_tick(&xbus->timing, &tv_received);
+ if(syncer == xbus) {
+ xpp_timing_tick(&ref_sync, &tv_received);
+ if((counter % BIG_TICK_INTERVAL) == 0)
+ reset_sync_counters();
+ }
+ if((atomic_read(&xbus->pcm_rx_counter) % BIG_TICK_INTERVAL) == 0) {
+ if(xbus->sync_mode == SYNC_MODE_PLL)
+ sync_rate_adjust(xbus);
+ }
+ if(likely(xbus->self_ticking))
+ xbus_tick(xbus);
+ xbus->global_counter = counter;
+}
+
+void xframe_receive_pcm(xbus_t *xbus, xframe_t *xframe)
+{
+ int which = atomic_read(&xbus->pcm_rx_counter) & 1;
+
+ if(!xframe_enqueue(&xbus->pcm_tospan[which], xframe)) {
+ static int rate_limit;
+
+ if((rate_limit++ % 1003) == 0)
+ XBUS_DBG(SYNC, xbus,
+ "Failed to enqueue received pcm frame. (%d)\n",
+ rate_limit);
+ FREE_RECV_XFRAME(xbus, xframe);
+ }
+ /*
+ * The sync_master bit is marked at the first packet
+ * of the frame, regardless of the XPD that is sync master.
+ * FIXME: what about PRI split?
+ */
+ if(XPACKET_ADDR_SYNC((xpacket_t *)xframe->packets)) {
+ do_tick(xbus, xframe->tv_received);
+ atomic_inc(&xbus->pcm_rx_counter);
+ } else
+ xbus->xbus_frag_count++;
+}
+
+#ifdef CONFIG_PROC_FS
+int proc_sync_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ int len = 0;
+ struct timeval now;
+ unsigned int counter = atomic_read(&xpp_tick_counter);
+ unsigned long usec;
+
+ do_gettimeofday(&now);
+ len += sprintf(page + len, "# To modify sync source write into this file:\n");
+ len += sprintf(page + len, "# ZAPTEL - Another zaptel device provide sync\n");
+ len += sprintf(page + len, "# SYNC=nn - XBUS-nn provide sync\n");
+ len += sprintf(page + len, "# QUERY=nn - Query XBUS-nn for sync information (DEBUG)\n");
+ if(!syncer) {
+ if(zaptel_syncer)
+ len += sprintf(page + len, "ZAPTEL\n");
+ else
+ len += sprintf(page + len, "NO-SYNC\n");
+ } else
+ len += sprintf(page + len, "SYNC=%02d\n", syncer->num);
+#ifdef ZAPTEL_SYNC_TICK
+ if(zaptel_syncer) {
+ len += sprintf(page + len,
+ "Zaptel Reference Sync (%d registered spans):\n",
+ total_registered_spans());
+ len += sprintf(page + len, "\tzaptel_tick: #%d\n", zaptel_tick_count);
+ len += sprintf(page + len, "\ttick - zaptel_tick = %d\n",
+ counter - zaptel_tick_count);
+ } else {
+ len += sprintf(page + len,
+ "Zaptel Reference Sync Not activated\n");
+ }
+#endif
+ usec = usec_diff(&now, &ref_sync.timing_val);
+ len += sprintf(page + len, "\ntick: #%d\n", counter);
+ len += sprintf(page + len,
+ "tick rate: %4d/second (measured %ld.%ld msec ago)\n",
+ ref_sync.tick_rate,
+ usec / 1000, usec % 1000);
+ if(pcm_tasklet)
+ len += sprintf(page + len, PCM_TASKLET_DEPRECATION);
+ if (len <= off+count)
+ *eof = 1;
+ *start = page + off;
+ len -= off;
+ if (len > count)
+ len = count;
+ if (len < 0)
+ len = 0;
+ return len;
+}
+
+static int proc_sync_write(struct file *file, const char __user *buffer, unsigned long count, void *data)
+{
+ char buf[MAX_PROC_WRITE];
+ int xbus_num;
+ int xpd_num;
+ xbus_t *xbus;
+ xpd_t *xpd;
+
+ // DBG(SYNC, "%s: count=%ld\n", __FUNCTION__, count);
+ if(count >= MAX_PROC_WRITE)
+ return -EINVAL;
+ if(copy_from_user(buf, buffer, count))
+ return -EFAULT;
+ buf[count] = '\0';
+ if(strncmp("ZAPTEL", buf, 6) == 0) {
+ DBG(SYNC, "ZAPTEL\n");
+ zaptel_syncer=1;
+ update_sync_master(NULL);
+ } else if(sscanf(buf, "SYNC=%d", &xbus_num) == 1) {
+ DBG(SYNC, "SYNC=%d\n", xbus_num);
+ if((xbus = get_xbus(xbus_num)) == NULL) {
+ ERR("No bus %d exists\n", xbus_num);
+ return -ENXIO;
+ }
+ update_sync_master(xbus);
+ put_xbus(xbus);
+ } else if(sscanf(buf, "QUERY=%d", &xbus_num) == 1) {
+ DBG(SYNC, "QUERY=%d\n", xbus_num);
+ if((xbus = get_xbus(xbus_num)) == NULL) {
+ ERR("No bus %d exists\n", xbus_num);
+ return -ENXIO;
+ }
+ CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, SYNC_MODE_QUERY, 0);
+ put_xbus(xbus);
+ } else if(sscanf(buf, "%d %d", &xbus_num, &xpd_num) == 2) {
+ NOTICE("Using deprecated syntax to update %s file\n",
+ PROC_SYNC);
+ if(xpd_num != 0) {
+ ERR("Currently can only set sync for XPD #0\n");
+ return -EINVAL;
+ }
+ if((xbus = get_xbus(xbus_num)) == NULL) {
+ ERR("No bus %d exists\n", xbus_num);
+ return -ENXIO;
+ }
+ if((xpd = xpd_of(xbus, xpd_num)) == NULL) {
+ XBUS_ERR(xbus, "No xpd %d exists\n", xpd_num);
+ put_xbus(xbus);
+ return -ENXIO;
+ }
+ update_sync_master(xbus);
+ put_xbus(xbus);
+ } else {
+ ERR("%s: cannot parse '%s'\n", __FUNCTION__, buf);
+ count = -EINVAL;
+ }
+ return count;
+}
+
+static struct proc_dir_entry *top;
+
+#endif
+
+int xbus_pcm_init(struct proc_dir_entry *toplevel)
+{
+ int ret = 0;
+#ifdef CONFIG_PROC_FS
+ struct proc_dir_entry *ent;
+#endif
+
+#ifdef OPTIMIZE_CHANMUTE
+ INFO("FEATURE: with CHANMUTE optimization (%sactivated)\n",
+ (optimize_chanmute)?"":"de");
+#endif
+#ifdef WITH_ECHO_SUPPRESSION
+ INFO("FEATURE: with ECHO_SUPPRESSION\n");
+#else
+ INFO("FEATURE: without ECHO_SUPPRESSION\n");
+#endif
+ if(xpp_ec)
+ INFO("FEATURE: with XPP_EC_CHUNK\n");
+ else
+ INFO("FEATURE: without XPP_EC_CHUNK\n");
+#ifdef ZAPTEL_SYNC_TICK
+ INFO("FEATURE: with sync_tick() from ZAPTEL\n");
+#else
+ INFO("FEATURE: without sync_tick() from ZAPTEL\n");
+#endif
+#ifdef CONFIG_PROC_FS
+ top = toplevel;
+ ent = create_proc_entry(PROC_SYNC, 0644, top);
+ if(!ent) {
+ ret = -EFAULT;
+ goto err;
+ }
+ ent->read_proc = proc_sync_read;
+ ent->write_proc = proc_sync_write;
+ ent->data = NULL;
+#endif
+ if(pcm_tasklet)
+ ERR(PCM_TASKLET_DEPRECATION);
+ xpp_timing_init(&ref_sync, "REF-SYNC");
+err:
+ return ret;
+}
+
+void xbus_pcm_shutdown(void)
+{
+#ifdef CONFIG_PROC_FS
+ DBG(GENERAL, "Removing '%s' from proc\n", PROC_SYNC);
+ remove_proc_entry(PROC_SYNC, top);
+#endif
+}
+
+
+EXPORT_SYMBOL(xbus_request_sync);
+EXPORT_SYMBOL(got_new_syncer);
+EXPORT_SYMBOL(elect_syncer);
+EXPORT_SYMBOL(xpp_echocan);
+#ifdef ZAPTEL_SYNC_TICK
+EXPORT_SYMBOL(zaptel_sync_tick);
+#endif
+EXPORT_SYMBOL(__pcm_recompute);
+EXPORT_SYMBOL(pcm_recompute);
+EXPORT_SYMBOL(generic_card_pcm_tospan);
+EXPORT_SYMBOL(generic_card_pcm_fromspan);
+