diff options
author | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-01-25 10:48:33 +0000 |
---|---|---|
committer | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2007-01-25 10:48:33 +0000 |
commit | d717b83056e5076a64c9471dd85573de409582d7 (patch) | |
tree | f46ebf64df73d8f513989d29f54e5b22b8bce2a5 /xpp/xpp_zap.c | |
parent | a11cf4664ea4fd14478f88c9118acd0f87ad7008 (diff) |
* Xbus protocol version: 2.4 (Zaptel 1.2.12/1.4.0 had 2.3).
XPS Init scripts renamed accordingly.
* Performance improvements for multi-XPD (span) devices.
* Astribank BRI driver (in next commit).
* Changes under /proc:
- XBUS and XPD numbers have two digits.
- Every script wildcard should be replaced from XBUS-? to XBUS-[0-9]*
- Added /proc/xpp/XBUS-*/XPD-*/blink: echo 1 to start and 0 to stop.
* Several countries (South Africa, UAE, anybody else) require a shorter
ring delay. Adjust FXO reg 0x17 (23)'s bits 0:2 to 011.
* Use tasklets to move most of the interrupt PCM copying out of the interrupt.
* Debugfs-based code to dump data to userspace (used to debug BRI D channel).
* Pretend every 2.6.9 actually has later RHEL's typedefs.
* fpga_load supports /dev/bus/usb .
* Fixed physical order sorting in genzaptelconf.
git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.2@1966 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'xpp/xpp_zap.c')
-rw-r--r-- | xpp/xpp_zap.c | 496 |
1 files changed, 336 insertions, 160 deletions
diff --git a/xpp/xpp_zap.c b/xpp/xpp_zap.c index 2ca0500..cd10ff4 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(bool, pcm_tasklet, 0, "Handle PCM in a tasklet (lower interrupt load)"); +DEF_PARM(int, disable_pcm, 0, "Disable all PCM transmissions"); 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"); #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_RO(bool, xpp_ec, 1, "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,148 @@ 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, int *pline_count, xpp_line_t *plines) +{ + bool is_bri; + bool digital_telephony; + int line_count = 0; + xpp_line_t lines; + xpd_t *xpd; + + xpd = xpd_by_addr(xbus, unit, 0); + if(!xpd || !xpd->card_present) + return NULL; + is_bri = (xpd->type == XPD_TYPE_BRI_NT) || (xpd->type == XPD_TYPE_BRI_TE); + digital_telephony = is_bri; + if(digital_telephony) { + xpd_t *tmp_xpd; + int subunit; + + lines = xpd->offhook & ~xpd->digital_signalling; + line_count = xpd->channels - 1; /* without D-Channel */ + if(is_bri) { + for(subunit = 1; subunit < MAX_SUBUNIT; subunit++) { + tmp_xpd = xpd_by_addr(xbus, unit, subunit); + if(!tmp_xpd || !tmp_xpd->card_present) + continue; + lines <<= SUBUNIT_PCM_SHIFT; /* B1, B2, D, E */ + lines |= tmp_xpd->offhook; + lines &= ~(tmp_xpd->digital_signalling); + line_count += tmp_xpd->channels - 1; /* Without D-channel */ + } + } + } else { + int i; + + 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; + } + *pline_count = line_count; + *plines = lines; + return xpd; +} + +static void xbus_tick(xbus_t *xbus) +{ + int unit; + int i; + xpp_line_t lines; + int line_count; + xpd_t *xpd; + xframe_t *xframe = NULL; + xpacket_t *pack = NULL; + + for(unit = 0; unit < MAX_UNIT; unit++) { + if((xpd = unit_pcm_calc(xbus, unit, &line_count, &lines)) == NULL) + continue; + if(lines && SPAN_REGISTERED(xpd)) { + int pcm_len = RPACKET_HEADERSIZE + sizeof(xpp_line_t) + line_count * ZT_CHUNKSIZE; + 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 +305,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 +360,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 +384,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 +417,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 +490,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) @@ -432,9 +590,7 @@ static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eo if(SPAN_REGISTERED(xpd)) { 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; @@ -449,11 +605,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); @@ -505,7 +660,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; @@ -532,7 +686,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; @@ -552,23 +706,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; @@ -714,6 +865,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; @@ -772,8 +925,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; } @@ -825,66 +979,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]; + bool 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; } @@ -895,64 +1070,50 @@ 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; + int i; + int subunit; + xpd_t *tmp_xpd; - 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); - 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 (tmp_xpd->delay_until_dialtone[i] > 0) { + tmp_xpd->delay_until_dialtone[i]--; + if (tmp_xpd->delay_until_dialtone[i] <= 0) { + tmp_xpd->delay_until_dialtone[i] = 0; + wake_up_interruptible(&tmp_xpd->txstateq[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) @@ -1066,10 +1227,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); } /* @@ -1088,10 +1260,16 @@ 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); } int xpp_open(struct zt_chan *chan) @@ -1148,21 +1326,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; @@ -1172,11 +1348,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)); @@ -1432,6 +1607,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); @@ -1448,9 +1624,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); @@ -1506,12 +1682,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); @@ -1520,6 +1695,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); |