summaryrefslogtreecommitdiff
path: root/xpp/xpp_zap.c
diff options
context:
space:
mode:
Diffstat (limited to 'xpp/xpp_zap.c')
-rw-r--r--xpp/xpp_zap.c590
1 files changed, 398 insertions, 192 deletions
diff --git a/xpp/xpp_zap.c b/xpp/xpp_zap.c
index f7ce8d1..b3e7017 100644
--- a/xpp/xpp_zap.c
+++ b/xpp/xpp_zap.c
@@ -36,7 +36,7 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/delay.h> /* for udelay */
-#include <linux/workqueue.h>
+#include <linux/interrupt.h>
#include <linux/proc_fs.h>
#include <zaptel.h>
#include "xbus-core.h"
@@ -50,6 +50,7 @@ struct proc_dir_entry *xpp_proc_toplevel = NULL;
#define PROC_DIR "xpp"
#define PROC_SYNC "sync"
#define PROC_XPD_ZTREGISTER "zt_registration"
+#define PROC_XPD_BLINK "blink"
#define PROC_XPD_SUMMARY "summary"
#endif
@@ -62,11 +63,14 @@ static xpd_t *sync_master = NULL; // Start with host based sync
static unsigned int xpp_timer_count = 0;
static unsigned int xpp_last_jiffies = 0;
-DEF_PARM(int, print_dbg, 0, "Print DBG statements");
-DEF_PARM(bool, zap_autoreg, 1, "Register spans automatically (1) or not (0)");
-DEF_PARM(bool, prefmaster, 1, "Do we want to be zaptel preferred sync master");
+
+DEF_PARM_BOOL(pcm_tasklet, 0, 0600, "Handle PCM in a tasklet (lower interrupt load)");
+DEF_PARM(int, disable_pcm, 0, 0600, "Disable all PCM transmissions");
+DEF_PARM(int, print_dbg, 0, 0600, "Print DBG statements");
+DEF_PARM_BOOL(zap_autoreg, 1, 0600, "Register spans automatically (1) or not (0)");
+DEF_PARM_BOOL(prefmaster, 1, 0600, "Do we want to be zaptel preferred sync master");
#ifdef XPP_EC_CHUNK
-DEF_PARM_RO(bool, xpp_ec, 0, "Do we use our own (1) or Zaptel's (0) echo canceller");
+DEF_PARM_BOOL(xpp_ec, 1, 0400, "Do we use our own (1) or Zaptel's (0) echo canceller");
#else
static int xpp_ec = 0;
#endif
@@ -78,14 +82,19 @@ static int xpp_ec = 0;
#endif
+static void xpp_tick(unsigned long param);
static int zaptel_register_xpd(xpd_t *xpd);
static int zaptel_unregister_xpd(xpd_t *xpd);
-static void xpp_transmitprep(xpd_t *xpd);
+static void xpp_transmitprep(xpd_t *xpd, xpp_line_t lines, xpacket_t *pack);
static void xpp_receiveprep(xpd_t *xpd);
static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data);
static int proc_xpd_ztregister_read(char *page, char **start, off_t off, int count, int *eof, void *data);
static int proc_xpd_ztregister_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
+static int proc_xpd_blink_read(char *page, char **start, off_t off, int count, int *eof, void *data);
+static int proc_xpd_blink_write(struct file *file, const char __user *buffer, unsigned long count, void *data);
static void xpd_free(xpd_t *xpd);
+static DECLARE_TASKLET(tasklet_tick, xpp_tick, 0L);
+static atomic_t missed_ticks = ATOMIC_INIT(0); /* In pcm_tasklet mode */
static void external_sync(xpd_t *the_xpd)
{
@@ -110,15 +119,23 @@ static void external_sync(xpd_t *the_xpd)
CALL_XMETHOD(SYNC_SOURCE, the_xpd->xbus, the_xpd, 1, 1);
}
+/*
+ * Change sync_master. May block. Cannot be called from atomic context
+ */
void sync_master_is(xpd_t *xpd)
{
DBG("SYNC MASTER CHANGED: %s => %s\n",
(sync_master) ? sync_master->xpdname : "HOST",
(xpd) ? xpd->xpdname : "HOST");
- sync_master = xpd;
- if(xpd) { // XPD
+ /* First stop all generators */
+ if(sync_master) {
+ tasklet_kill(&tasklet_tick);
+ } else
del_timer_sync(&xpp_timer);
- xpp_tick((unsigned long)xpd);
+ /* Now set a new master */
+ sync_master = xpd;
+ if(sync_master) { // XPD
+ external_sync(xpd);
} else { // HOST
external_sync(NULL);
if(!timer_pending(&xpp_timer)) {
@@ -130,23 +147,158 @@ void sync_master_is(xpd_t *xpd)
}
}
+static inline void send_pcm_frame(xbus_t *xbus, xframe_t *xframe)
+{
+ static int rate_limit;
+
+ if(disable_pcm)
+ xbus->ops->xframe_free(xbus, xframe);
+ else {
+ if(print_dbg && ((rate_limit++ % 1003) == 0))
+ dump_xframe("SEND_PCM", xbus, xframe);
+ xframe_send(xbus, xframe);
+ XBUS_COUNTER(xbus, SEND_PCM)++;
+ }
+}
+
+/*
+ * Calculate PCM line_count and lines from a xbus+unit number:
+ * - Takes into account BRI subunits
+ * - Returns an XPD pointer if we should transmit, NULL otherwise
+ */
+static xpd_t *unit_pcm_calc(const xbus_t *xbus, int unit, size_t *pcm_len, xpp_line_t *plines)
+{
+ bool is_bri;
+ bool digital_telephony;
+ int line_count = 0;
+ xpp_line_t lines;
+ xpd_t *xpd;
+ unsigned long flags;
+ int i;
+
+ xpd = xpd_by_addr(xbus, unit, 0);
+ if(!xpd || !xpd->card_present)
+ return NULL;
+ spin_lock_irqsave(&xpd->lock, flags);
+ is_bri = (xpd->type == XPD_TYPE_BRI_NT) || (xpd->type == XPD_TYPE_BRI_TE);
+ digital_telephony = is_bri; /* or PRI */
+ if(digital_telephony) {
+ int subunit;
+
+ /* without D-Channel */
+ lines = xpd->offhook & ~xpd->digital_signalling;
+ for_each_line(xpd, i)
+ if(IS_SET(lines, i))
+ line_count++;
+ if(is_bri) {
+ /*
+ * BRI has a single PCM highway for all subunits, so
+ * we agregate the next subunits into the same
+ * transmition.
+ */
+ for(subunit = 1; subunit < MAX_SUBUNIT; subunit++) {
+ xpd_t *tmp_xpd = xpd_by_addr(xbus, unit, subunit);
+ xpp_line_t tmp_lines;
+
+ if(!tmp_xpd || !tmp_xpd->card_present)
+ continue;
+ tmp_lines = xpd->offhook & ~xpd->digital_signalling; /* Without D-channel */
+ for_each_line(tmp_xpd, i)
+ if(IS_SET(tmp_lines, i))
+ line_count++;
+ lines = (lines << SUBUNIT_PCM_SHIFT) | tmp_lines; /* B1, B2, D, E */
+ }
+ }
+ } else {
+ lines = (xpd->offhook | xpd->cid_on);
+ for_each_line(xpd, i)
+ if(IS_SET(lines, i))
+ line_count++;
+ }
+ /*
+ * FIXME: Workaround a bug in sync code of the Astribank.
+ * Send dummy PCM for sync.
+ */
+ if(unit == 0 && lines == 0) {
+ lines = BIT(0);
+ line_count = 1;
+ }
+ *pcm_len = RPACKET_HEADERSIZE + sizeof(xpp_line_t) + line_count * ZT_CHUNKSIZE;
+ *plines = lines;
+ spin_unlock_irqrestore(&xpd->lock, flags);
+ return xpd;
+}
+
+static void xbus_tick(xbus_t *xbus)
+{
+ int unit;
+ int i;
+ xpp_line_t lines;
+ xpd_t *xpd;
+ xframe_t *xframe = NULL;
+ xpacket_t *pack = NULL;
+ size_t pcm_len;
+
+ for(unit = 0; unit < MAX_UNIT; unit++) {
+ if((xpd = unit_pcm_calc(xbus, unit, &pcm_len, &lines)) == NULL)
+ continue;
+ if(lines && SPAN_REGISTERED(xpd)) {
+ do {
+ // pack = NULL; /* FORCE single packet frames */
+ if(xframe && !pack) { /* FULL frame */
+ send_pcm_frame(xbus, xframe);
+ xframe = NULL;
+ }
+ if(!xframe) { /* Alloc frame */
+ xframe = xbus->ops->xframe_new(xbus, GFP_ATOMIC);
+ if (!xframe) {
+ ERR("%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_set_addr(&pack->addr, xpd->id);
+ pack->datalen = pcm_len;
+ RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = lines;
+ xpp_transmitprep(xpd, lines, pack);
+ }
+
+ }
+ if(xframe) /* clean any leftovers */
+ send_pcm_frame(xbus, xframe);
+
+ for(i = 0; i < MAX_XPDS; i++) {
+ xpd = xpd_of(xbus, i);
+ if(!xpd || !xpd->card_present)
+ continue;
+
+ if(SPAN_REGISTERED(xpd))
+ xpp_receiveprep(xpd);
+ xpd->timer_count++;
+ /*
+ * Must be called *after* tx/rx so
+ * D-Chan counters may be cleared
+ */
+ CALL_XMETHOD(card_tick, xbus, xpd);
+ }
+}
+
void xpp_tick(unsigned long param)
{
- xbus_t *xbus;
- xpd_t *the_xpd = (xpd_t *)param;
- int i;
- int j;
+ xbus_t *xbus;
+ int i, j;
- if(!the_xpd) { /* Called from timer */
+ if(!sync_master) { /* Called from timer */
#if 0
static int rate_limit = 0;
if(rate_limit++ % 1000 == 0)
DBG("FROM_TIMER\n");
#endif
mod_timer(&xpp_timer, jiffies + 1); /* Must be 1KHz rate */
- }
- else if(the_xpd != sync_master)
- return;
+ } else
+ atomic_dec(&missed_ticks);
/* Statistics */
if((xpp_timer_count % SAMPLE_TICKS) == 0) {
xpp_last_jiffies = jiffies;
@@ -163,32 +315,34 @@ void xpp_tick(unsigned long param)
DBG("Dropped packet. %s is in_use\n", xbus->busname);
continue;
}
-#if 0
- if(xbus->open_counter == 0)
- continue; // optimize, but zttool loopback won't function
-#endif
+ /*
+ * calls to zt_transmit should be out of spinlocks, as it may call back
+ * our hook setting methods.
+ */
for(j = 0; j < MAX_XPDS; j++) {
xpd_t *xpd = xpd_of(xbus, j);
- if(!xpd)
- continue;
- if(!xpd->card_present)
- continue;
- xpd->timer_count++;
- if(SPAN_REGISTERED(xpd))
- xpp_transmitprep(xpd);
- if(SPAN_REGISTERED(xpd))
- xpp_receiveprep(xpd);
- /*
- * Must be called *after* tx/rx so
- * D-Chan counters may be cleared
- */
- CALL_XMETHOD(card_tick, xbus, xpd);
+ if(xpd && SPAN_REGISTERED(xpd)) {
+ zt_transmit(&xpd->span);
+ }
}
+ xbus_tick(xbus);
up_read(&xbus->in_use);
}
}
+void got_pcm_from(xpd_t *xpd)
+{
+ if(xpd != sync_master)
+ return;
+ atomic_inc(&missed_ticks);
+ if(!pcm_tasklet) {
+ xpp_tick(0L);
+ return;
+ }
+ tasklet_schedule(&tasklet_tick);
+}
+
#if HZ != 1000
#warning "xpp_timer must be sampled EXACTLY 1000/per second"
#endif
@@ -216,14 +370,19 @@ static void xpd_free(xpd_t *xpd)
remove_proc_entry(PROC_XPD_ZTREGISTER, xpd->proc_xpd_dir);
xpd->proc_xpd_ztregister = NULL;
}
+ if(xpd->proc_xpd_blink) {
+ DBG("Removing proc '%s' for %s/%s\n", PROC_XPD_BLINK, xbus->busname, xpd->xpdname);
+ remove_proc_entry(PROC_XPD_BLINK, xpd->proc_xpd_dir);
+ xpd->proc_xpd_blink = NULL;
+ }
DBG("Removing proc directory for %s/%s\n", xbus->busname, xpd->xpdname);
remove_proc_entry(xpd->xpdname, xbus->proc_xbus_dir);
xpd->proc_xpd_dir = NULL;
}
#endif
- if(xpd->writechunk)
- kfree((void *)xpd->writechunk);
- xpd->writechunk = NULL;
+ if(xpd->readchunk)
+ kfree((void *)xpd->readchunk);
+ xpd->readchunk = NULL;
if(xpd->xproto)
xproto_put(xpd->xproto);
xpd->xproto = NULL;
@@ -235,7 +394,7 @@ static void xpd_free(xpd_t *xpd)
#define REV(x,y) (10 * (x) + (y))
static byte good_revs[] = {
- REV(2,3),
+ REV(2,4),
};
#undef REV
@@ -268,7 +427,7 @@ void card_detected(struct card_desc_struct *card_desc)
BUG_ON(!card_desc);
BUG_ON(card_desc->magic != CARD_DESC_MAGIC);
xbus = card_desc->xbus;
- xpd_num = xpd_addr2num(&card_desc->xpd_addr);
+ xpd_num = XPD_NUM(card_desc->xpd_addr);
type = card_desc->type;
rev = card_desc->rev;
BUG_ON(!xbus);
@@ -341,6 +500,15 @@ void card_detected(struct card_desc_struct *card_desc)
xpd->proc_xpd_ztregister->data = xpd;
xpd->proc_xpd_ztregister->read_proc = proc_xpd_ztregister_read;
xpd->proc_xpd_ztregister->write_proc = proc_xpd_ztregister_write;
+ xpd->proc_xpd_blink = create_proc_entry(PROC_XPD_BLINK, 0644, xpd->proc_xpd_dir);
+ if (!xpd->proc_xpd_blink) {
+ ERR("Failed to create proc '%s' for %s/%s\n", PROC_XPD_BLINK, xbus->busname, xpd->xpdname);
+ goto err;
+ }
+ xpd->proc_xpd_blink->owner = THIS_MODULE;
+ xpd->proc_xpd_blink->data = xpd;
+ xpd->proc_xpd_blink->read_proc = proc_xpd_blink_read;
+ xpd->proc_xpd_blink->write_proc = proc_xpd_blink_write;
#endif
xbus_register_xpd(xbus, xpd);
if(CALL_XMETHOD(card_init, xbus, xpd) < 0)
@@ -430,12 +598,9 @@ static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eo
}
#if 1
if(SPAN_REGISTERED(xpd)) {
- len += sprintf(page + len, "\nzaptel state: %s RUNNING\n", (xpd->span.flags & ZT_FLAG_RUNNING)?"IS":"IS NOT");
len += sprintf(page + len, "\nPCM:\n | [readchunk] | [writechunk] | delay");
for_each_line(xpd, i) {
-#if 0
struct zt_chan *chans = xpd->span.chans;
-#endif
byte rchunk[ZT_CHUNKSIZE];
byte wchunk[ZT_CHUNKSIZE];
byte *rp;
@@ -450,11 +615,10 @@ static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eo
continue;
#if 0
rp = chans[i].readchunk;
- wp = chans[i].writechunk;
#else
rp = (byte *)xpd->readchunk + (ZT_CHUNKSIZE * i);
- wp = (byte *)xpd->writechunk + (ZT_CHUNKSIZE * i);
#endif
+ wp = chans[i].writechunk;
memcpy(rchunk, rp, ZT_CHUNKSIZE);
memcpy(wchunk, wp, ZT_CHUNKSIZE);
len += sprintf(page + len, "\n port %2d> | ", i);
@@ -465,7 +629,7 @@ static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eo
for(j = 0; j < ZT_CHUNKSIZE; j++) {
len += sprintf(page + len, "%02X ", wchunk[j]);
}
- len += sprintf(page + len, " | %d ", xpd->delay_until_dialtone[i]);
+ len += sprintf(page + len, " | ");
}
}
#endif
@@ -506,7 +670,6 @@ out:
xpd_t *xpd_alloc(size_t privsize, xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, int channels, byte revision)
{
xpd_t *xpd = NULL;
- size_t pcm_size;
size_t alloc_size = sizeof(xpd_t) + privsize;
int i;
@@ -533,7 +696,7 @@ xpd_t *xpd_alloc(size_t privsize, xbus_t *xbus, int xpd_num, const xproto_table_
xpd->channels = channels;
xpd->chans = NULL;
xpd->card_present = 0;
- snprintf(xpd->xpdname, XPD_NAMELEN, "XPD-%d", xpd_num);
+ snprintf(xpd->xpdname, XPD_NAMELEN, "XPD-%02x", xpd_num);
xpd->offhook = 0x0; /* ONHOOK */
xpd->type = proto_table->type;
xpd->xproto = proto_table;
@@ -553,23 +716,20 @@ xpd_t *xpd_alloc(size_t privsize, xbus_t *xbus, int xpd_num, const xproto_table_
ERR("%s: Unable to allocate channels\n", __FUNCTION__);
goto err;
}
- pcm_size = ZT_MAX_CHUNKSIZE * CHANNELS_PERXPD * 2; /* Double Buffer */
- alloc_size = pcm_size * 2; /* Read/Write */
- if((xpd->writechunk = kmalloc(alloc_size, GFP_KERNEL)) == NULL) {
- ERR("%s: Unable to allocate memory for writechunks\n", __FUNCTION__);
+ alloc_size = ZT_MAX_CHUNKSIZE * CHANNELS_PERXPD * 2; /* Double Buffer */
+ if((xpd->readchunk = kmalloc(alloc_size, GFP_KERNEL)) == NULL) {
+ ERR("%s: Unable to allocate memory for readchunk\n", __FUNCTION__);
goto err;
}
- /* Initialize Write/Buffers to all blank data */
- memset((void *)xpd->writechunk, 0x00, alloc_size);
- xpd->readchunk = xpd->writechunk + pcm_size;
-
+ /* Initialize read buffers to all blank data */
+ memset((void *)xpd->readchunk, 0, alloc_size);
return xpd;
err:
if(xpd) {
if(xpd->chans)
kfree((void *)xpd->chans);
- if(xpd->writechunk)
- kfree((void *)xpd->writechunk);
+ if(xpd->readchunk)
+ kfree((void *)xpd->readchunk);
kfree(xpd);
}
return NULL;
@@ -628,7 +788,7 @@ void update_xpd_status(xpd_t *xpd, int alarm_flag)
struct zt_span *span = &xpd->span;
if(!SPAN_REGISTERED(xpd)) {
- NOTICE("%s: %s is not registered. Skipping.\n", __FUNCTION__, xpd->xpdname);
+ // NOTICE("%s: %s is not registered. Skipping.\n", __FUNCTION__, xpd->xpdname);
return;
}
switch (alarm_flag) {
@@ -648,47 +808,45 @@ void update_xpd_status(xpd_t *xpd, int alarm_flag)
void update_line_status(xpd_t *xpd, int pos, bool to_offhook)
{
- struct zt_chan *chan;
+ zt_rxsig_t rxsig;
BUG_ON(!xpd);
- if(!SPAN_REGISTERED(xpd))
- return;
- chan = &xpd->chans[pos];
- /*
- * We should not spinlock before calling zt_hooksig() as
- * it may call back into our xpp_hooksig() and cause
- * a nested spinlock scenario
- */
if(to_offhook) {
BIT_SET(xpd->offhook, pos);
- zt_hooksig(chan, ZT_RXSIG_OFFHOOK);
+ rxsig = ZT_RXSIG_OFFHOOK;
} else {
BIT_CLR(xpd->offhook, pos);
BIT_CLR(xpd->cid_on, pos);
- zt_hooksig(chan, ZT_RXSIG_ONHOOK);
+ rxsig = ZT_RXSIG_ONHOOK;
}
+ /*
+ * We should not spinlock before calling zt_hooksig() as
+ * it may call back into our xpp_hooksig() and cause
+ * a nested spinlock scenario
+ */
+ if(SPAN_REGISTERED(xpd))
+ zt_hooksig(&xpd->chans[pos], rxsig);
}
void update_zap_ring(xpd_t *xpd, int pos, bool on)
{
- struct zt_chan *chan;
+ zt_rxsig_t rxsig;
BUG_ON(!xpd);
- if(!SPAN_REGISTERED(xpd))
- return;
- chan = &xpd->chans[pos];
- /*
- * We should not spinlock before calling zt_hooksig() as
- * it may call back into our xpp_hooksig() and cause
- * a nested spinlock scenario
- */
if(on) {
BIT_CLR(xpd->cid_on, pos);
- zt_hooksig(chan, ZT_RXSIG_RING);
+ rxsig = ZT_RXSIG_RING;
} else {
BIT_SET(xpd->cid_on, pos);
- zt_hooksig(chan, ZT_RXSIG_OFFHOOK);
+ rxsig = ZT_RXSIG_OFFHOOK;
}
+ /*
+ * We should not spinlock before calling zt_hooksig() as
+ * it may call back into our xpp_hooksig() and cause
+ * a nested spinlock scenario
+ */
+ if(SPAN_REGISTERED(xpd))
+ zt_hooksig(&xpd->chans[pos], rxsig);
}
#ifdef CONFIG_PROC_FS
@@ -715,6 +873,8 @@ int proc_sync_read(char *page, char **start, off_t off, int count, int *eof, voi
xpp_timer_rate = (xpp_timer_count % SAMPLE_TICKS) * HZ / delta;
len += sprintf(page + len, "tick rate: %4d/second (SAMPLE_TICKS=%d)\n", xpp_timer_rate, SAMPLE_TICKS);
}
+ if(pcm_tasklet)
+ len += sprintf(page + len, "TASKLETS: missed_ticks: %d\n", atomic_read(&missed_ticks));
if (len <= off+count)
*eof = 1;
*start = page + off;
@@ -734,7 +894,7 @@ static int proc_sync_write(struct file *file, const char __user *buffer, unsigne
xbus_t *xbus;
xpd_t *xpd;
int ret;
- bool setit;
+ int setit;
// DBG("%s: count=%ld\n", __FUNCTION__, count);
if(count >= MAX_PROC_WRITE)
@@ -773,8 +933,9 @@ static int proc_sync_write(struct file *file, const char __user *buffer, unsigne
return -ENXIO;
}
DBG("%s: %d/%d %s\n", __FUNCTION__, xbus_num, xpd_num, (setit)?"SET":"QUERY");
- if(setit)
- external_sync(xpd);
+ if(setit) {
+ sync_master_is(xpd);
+ }
out:
return count;
}
@@ -805,7 +966,7 @@ static int proc_xpd_ztregister_write(struct file *file, const char __user *buffe
{
xpd_t *xpd = data;
char buf[MAX_PROC_WRITE];
- bool zt_reg;
+ int zt_reg;
int ret;
BUG_ON(!xpd);
@@ -826,66 +987,87 @@ static int proc_xpd_ztregister_write(struct file *file, const char __user *buffe
return (ret < 0) ? ret : count;
}
+int proc_xpd_blink_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ int len = 0;
+ unsigned long flags;
+ xpd_t *xpd = data;
+
+ BUG_ON(!xpd);
+ spin_lock_irqsave(&xpd->lock, flags);
+
+ len += sprintf(page + len, "%d\n", xpd->blink_mode);
+ spin_unlock_irqrestore(&xpd->lock, flags);
+ 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_xpd_blink_write(struct file *file, const char __user *buffer, unsigned long count, void *data)
+{
+ xpd_t *xpd = data;
+ char buf[MAX_PROC_WRITE];
+ int blink;
+ int ret;
+
+ BUG_ON(!xpd);
+ if(count >= MAX_PROC_WRITE)
+ return -EINVAL;
+ if(copy_from_user(buf, buffer, count))
+ return -EFAULT;
+ buf[count] = '\0';
+ ret = sscanf(buf, "%d", &blink);
+ if(ret != 1)
+ return -EINVAL;
+ DBG("%s: %s/%s %s\n", __FUNCTION__,
+ xpd->xbus->busname, xpd->xpdname, (blink) ? "blink" : "unblink");
+ xpd->blink_mode = blink;
+ return count;
+}
+
#endif
/**
*
- * Packet is freed:
+ * Frame is freed:
* - In case of error, by this function.
* - Otherwise, by the underlying sending mechanism
*/
-int packet_send(xbus_t *xbus, xpacket_t *pack_tx)
+int xframe_send(xbus_t *xbus, xframe_t *xframe)
{
int ret = -ENODEV;
- int toxpd;
- if(!pack_tx) {
- DBG("null pack\n");
+ if(!xframe) {
+ DBG("null xframe\n");
return -EINVAL;
}
- toxpd = XPD_NUM(pack_tx->content.addr);
if(!xbus) {
DBG("null xbus\n");
ret = -EINVAL;
goto error;
}
if (!xbus->hardware_exists) {
- DBG("xbus %s Dropped a packet -- NO HARDWARE.", xbus->busname);
+ DBG("xbus %s Dropped a xframe -- NO HARDWARE.", xbus->busname);
ret = -ENODEV;
goto error;
}
- if(!VALID_XPD_NUM(toxpd)) {
- ERR("%s: toxpd=%d > MAX_XPDS\n", __FUNCTION__, toxpd);
- ret = -EINVAL;
- goto error;
- }
-#if 0
- // DEBUG: For Dima
- if(pack_tx->content.opcode == XPP_PCM_WRITE) {
- static int rate_limit;
- static int count;
-
- if(sync_master == NULL)
- count = 0;
- if(count++ > 5) {
- ret = 0;
- goto error;
- }
- if(rate_limit++ % 1000 == 0)
- INFO("DEBUG: TRANSMIT (PCM_WRITE)\n");
- }
-#endif
if(down_read_trylock(&xbus->in_use)) {
- ret = xbus->ops->packet_send(xbus, pack_tx);
- XBUS_COUNTER(xbus, TX_BYTES) += pack_tx->datalen;
+ ret = xbus->ops->xframe_send(xbus, xframe);
+ XBUS_COUNTER(xbus, TX_BYTES) += XFRAME_LEN(xframe);
up_read(&xbus->in_use);
} else {
- DBG("Dropped packet. %s is in_use\n", xbus->busname);
+ DBG("Dropped xframe. %s is in_use\n", xbus->busname);
}
return ret;
error:
- xbus->ops->packet_free(xbus, pack_tx);
+ xbus->ops->xframe_free(xbus, xframe);
return ret;
}
@@ -896,69 +1078,48 @@ error:
#define PREP_REPORT_RATE 1000
-static void xpp_transmitprep(xpd_t *xpd)
+static void xpp_transmitprep(xpd_t *xpd, xpp_line_t lines, xpacket_t *pack)
{
- volatile u_char *writechunk;
- volatile u_char *w;
- int ret;
- int i;
- int channels = xpd->channels;
- struct zt_chan *chans = xpd->span.chans;
+ byte *pcm;
+ int channels;
+ struct zt_chan *chans;
unsigned long flags;
- bool digital_telephony;
-
- spin_lock_irqsave(&xpd->lock, flags);
- digital_telephony = (xpd->type == XPD_TYPE_BRI_NT) || (xpd->type == XPD_TYPE_BRI_TE);
-// if((xpd->timer_count % PREP_REPORT_RATE) < 10)
-// DBG("%d\n", xpd->timer_count);
+ int i;
+ int subunit;
+ xpd_t *tmp_xpd;
- if (xpd->timer_count & 1) {
- /* First part */
- w = writechunk = xpd->writechunk /* + 1 */;
- } else {
- w = writechunk = xpd->writechunk + ZT_CHUNKSIZE * CHANNELS_PERXPD /* + 1 */;
- }
- spin_unlock_irqrestore(&xpd->lock, flags);
- /*
- * This should be out of spinlocks, as it may call back our hook setting
- * methods
- */
- zt_transmit(&xpd->span);
- spin_lock_irqsave(&xpd->lock, flags);
- for (i = 0; i < channels; i++, w += ZT_CHUNKSIZE) {
- /*
- * We don't copy signalling buffers (they may be
- * longer than ZT_CHUNKSIZE).
- */
- if(IS_SET(xpd->digital_signalling, i))
+ BUG_ON(!xpd);
+ BUG_ON(!pack);
+ pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm);
+ for(subunit = 0; subunit < MAX_SUBUNIT; subunit++) {
+ tmp_xpd = xpd_by_addr(xpd->xbus, xpd->addr.unit, subunit);
+ if(!tmp_xpd)
continue;
- if (xpd->delay_until_dialtone[i] > 0) {
- xpd->delay_until_dialtone[i]--;
- if (xpd->delay_until_dialtone[i] <= 0) {
- xpd->delay_until_dialtone[i] = 0;
- wake_up_interruptible(&xpd->txstateq[i]);
- }
- }
- if(IS_SET(xpd->offhook, i) || IS_SET(xpd->cid_on, i) || digital_telephony) {
+ spin_lock_irqsave(&tmp_xpd->lock, flags);
- memcpy((u_char *)w, chans[i].writechunk, ZT_CHUNKSIZE);
- // fill_beep((u_char *)w, xpd->addr.subunit, 2);
- // memset((u_char *)w, pcmtx[xpd->addr.subunit % 4], ZT_CHUNKSIZE);
+ channels = tmp_xpd->channels;
+ chans = tmp_xpd->span.chans;
+ for (i = 0; i < channels; i++) {
+ if(IS_SET(lines, i)) {
+ if(SPAN_REGISTERED(tmp_xpd)) {
+ memcpy((u_char *)pcm, chans[i].writechunk, ZT_CHUNKSIZE);
+ // fill_beep((u_char *)pcm, tmp_xpd->addr.subunit, 2);
+ // memset((u_char *)pcm, pcmtx[tmp_xpd->addr.subunit % 4], ZT_CHUNKSIZE);
+ } else
+ memset((u_char *)pcm, 0x7F, ZT_CHUNKSIZE);
+ pcm += ZT_CHUNKSIZE;
+ }
}
+ XPD_COUNTER(tmp_xpd, PCM_WRITE)++;
+ spin_unlock_irqrestore(&tmp_xpd->lock, flags);
+ lines >>= SUBUNIT_PCM_SHIFT;
}
-// if(xpd->offhook != 0 || sync_master != xpd) {
- ret = CALL_XMETHOD(PCM_WRITE, xpd->xbus, xpd, xpd->offhook | xpd->cid_on, writechunk);
- if(ret < 0) {
- DBG("failed to write PCM %d\n", ret);
- }
-// }
- spin_unlock_irqrestore(&xpd->lock, flags);
}
void fill_beep(u_char *buf, int num, int duration)
{
- bool alternate = (duration) ? (jiffies/(duration*HZ)) & 0x1 : 0;
+ bool alternate = (duration) ? (jiffies/(duration*1000)) & 0x1 : 0;
int which;
u_char *snd;
@@ -1067,10 +1228,21 @@ static void xpp_receiveprep(xpd_t *xpd)
zt_receive(&xpd->span);
}
+/*
+ * Called only for 'span' keyword in /etc/zaptel.conf
+ */
static int xpp_startup(struct zt_span *span)
{
- DBG("\n");
- return 0;
+ xpd_t *xpd = span->pvt;
+
+ BUG_ON(!xpd);
+ // Turn on all channels
+ CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 1);
+ if(!xpd->xops->span_startup) {
+ NOTICE("%s/%s: %s called\n", xpd->xbus->busname, xpd->xpdname, __FUNCTION__);
+ return 0;
+ }
+ return xpd->xops->span_startup(xpd);
}
/*
@@ -1089,12 +1261,22 @@ static int xpp_spanconfig(struct zt_span *span, struct zt_lineconfig *lc)
*/
static int xpp_shutdown(struct zt_span *span)
{
- xpd_t *xpd = span->pvt;
+ xpd_t *xpd = span->pvt;
- DBG("%s\n", xpd->xpdname);
- return 0;
+ BUG_ON(!xpd);
+ // Turn off all channels
+ CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 0);
+ if(!xpd->xops->span_shutdown) {
+ NOTICE("%s/%s: %s called\n", xpd->xbus->busname, xpd->xpdname, __FUNCTION__);
+ return 0;
+ }
+ return xpd->xops->span_shutdown(xpd);
}
+/*
+ * Called from zaptel with spinlock held on chan. Must not call back
+ * zaptel functions.
+ */
int xpp_open(struct zt_chan *chan)
{
xpd_t *xpd = chan->pvt;
@@ -1149,21 +1331,19 @@ int xpp_close(struct zt_chan *chan)
int xpp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg)
{
xpd_t *xpd = chan->pvt;
- int pos = chan->chanpos - 1;
- int x;
+ int pos = chan->chanpos - 1;
+ int x;
+ int ret = 0;
switch (cmd) {
case ZT_ONHOOKTRANSFER:
if (get_user(x, (int __user *)arg))
return -EFAULT;
- xpd->ohttimer[pos] = x << 3;
- xpd->idletxhookstate[pos] = FXS_LINE_CID; /* OHT mode when idle */
- if (xpd->lasttxhook[pos] == FXS_LINE_ENABLED) {
- /* Apply the change if appropriate */
- CALL_XMETHOD(CHAN_CID, xpd->xbus, xpd, pos); // CALLER ID
- }
- DBG("xpd=%d: ZT_ONHOOKTRANSFER (%d millis) chan=%d\n", xpd->id, x, pos);
- return -ENOTTY;
+ DBG("%s/%s/%d: ZT_ONHOOKTRANSFER (%d millis)\n",
+ xpd->xbus->busname, xpd->xpdname, pos, x);
+ if(xpd->xops->chan_onhooktransfer)
+ ret = CALL_XMETHOD(chan_onhooktransfer, xpd->xbus, xpd, pos, x);
+ return ret;
case ZT_TONEDETECT:
if (get_user(x, (int __user *)arg))
return -EFAULT;
@@ -1173,11 +1353,10 @@ int xpp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg)
default:
/* Some span-specific commands before we give up: */
if (xpd->xops->card_ioctl != NULL) {
- x = xpd->xops->card_ioctl(xpd, pos, cmd, arg);
- if (x != -ENOTTY)
- return x;
+ ret = xpd->xops->card_ioctl(xpd, pos, cmd, arg);
+ if (ret != -ENOTTY)
+ return ret;
}
-
DBG("ENOTTY: chan=%d cmd=0x%x\n", pos, cmd);
DBG(" IOC_TYPE=0x%02X\n", _IOC_TYPE(cmd));
DBG(" IOC_DIR=0x%02X\n", _IOC_DIR(cmd));
@@ -1246,7 +1425,11 @@ int xpp_maint(struct zt_span *span, int cmd)
return ret;
}
-/* Set signalling type (if appropriate) */
+/*
+ * Set signalling type (if appropriate)
+ * Called from zaptel with spinlock held on chan. Must not call back
+ * zaptel functions.
+ */
static int xpp_chanconfig(struct zt_chan *chan, int sigtype)
{
DBG("channel %d (%s) -> %s\n", chan->channo, chan->name, sig2str(sigtype));
@@ -1412,6 +1595,28 @@ static int zaptel_register_xpd(xpd_t *xpd)
}
atomic_inc(&xpd->zt_registered);
xpd->xops->card_zaptel_postregistration(xpd, 1);
+ /*
+ * Update zaptel about our state
+ */
+#if 0
+ /*
+ * FIXME: since asterisk didn't open the channel yet, the report
+ * is discarded anyway. OTOH, we cannot report in xpp_open or
+ * xpp_chanconfig since zaptel call them with a spinlock on the channel
+ * and zt_hooksig tries to acquire the same spinlock, resulting in
+ * double spinlock deadlock (we are lucky that RH/Fedora kernel are
+ * compiled with spinlock debugging).... tough.
+ */
+ for_each_line(xpd, cn) {
+ struct zt_chan *chans = xpd->span.chans;
+
+ if(IS_SET(xpd->offhook, cn)) {
+ NOTICE("%s/%s/%d: Report OFFHOOK to zaptel\n",
+ xbus->busname, xpd->xpdname, cn);
+ zt_hooksig(&chans[cn], ZT_RXSIG_OFFHOOK);
+ }
+ }
+#endif
return 0;
}
@@ -1433,6 +1638,7 @@ static void do_cleanup(void)
{
if(timer_pending(&xpp_timer))
del_timer_sync(&xpp_timer);
+ tasklet_kill(&tasklet_tick);
#ifdef CONFIG_PROC_FS
DBG("Removing '%s' from proc\n", PROC_SYNC);
remove_proc_entry(PROC_SYNC, xpp_proc_toplevel);
@@ -1449,9 +1655,9 @@ int __init xpp_zap_init(void)
int ret = 0;
struct proc_dir_entry *ent;
- INFO("%s MAX_XPDS=%d (%d*%d)\n", THIS_MODULE->name,
+ INFO("%s revision %s MAX_XPDS=%d (%d*%d)\n", THIS_MODULE->name, XPP_VERSION,
MAX_XPDS, MAX_UNIT, MAX_SUBUNIT);
-#if WITH_ECHO_SUPPRESSION
+#if WITH_ECHO_SUPPRESSION
INFO("FEATURE: %s (with ECHO_SUPPRESSION)\n", THIS_MODULE->name);
#else
INFO("FEATURE: %s (without ECHO_SUPPRESSION)\n", THIS_MODULE->name);
@@ -1507,12 +1713,11 @@ EXPORT_SYMBOL(print_dbg);
EXPORT_SYMBOL(card_detected);
EXPORT_SYMBOL(xpd_alloc);
EXPORT_SYMBOL(xpd_disconnect);
-EXPORT_SYMBOL(packet_send);
+EXPORT_SYMBOL(xframe_send);
EXPORT_SYMBOL(update_xpd_status);
EXPORT_SYMBOL(update_zap_ring);
EXPORT_SYMBOL(update_line_status);
EXPORT_SYMBOL(fill_beep);
-EXPORT_SYMBOL(xpp_tick);
EXPORT_SYMBOL(xpp_open);
EXPORT_SYMBOL(xpp_close);
EXPORT_SYMBOL(xpp_ioctl);
@@ -1521,6 +1726,7 @@ EXPORT_SYMBOL(xpp_maint);
MODULE_DESCRIPTION("XPP Zaptel Driver");
MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>");
MODULE_LICENSE("GPL");
+MODULE_VERSION(XPP_VERSION);
module_init(xpp_zap_init);
module_exit(xpp_zap_cleanup);