diff options
Diffstat (limited to 'kernel/xpp/xpp_zap.c')
-rw-r--r-- | kernel/xpp/xpp_zap.c | 132 |
1 files changed, 82 insertions, 50 deletions
diff --git a/kernel/xpp/xpp_zap.c b/kernel/xpp/xpp_zap.c index 3ce9bff..b1a1bbe 100644 --- a/kernel/xpp/xpp_zap.c +++ b/kernel/xpp/xpp_zap.c @@ -216,7 +216,7 @@ __must_check int xpd_common_init(xbus_t *xbus, xpd_t *xpd, int unit, int subunit snprintf(xpd->xpdname, XPD_NAMELEN, "XPD-%1d%1d", unit, subunit); xpd->subtype = subtype; xpd->subunits = subunits; - xpd->offhook = 0; + xpd->offhook_state = 0; /* For USB-1 disable some channels */ if(MAX_SEND_SIZE(xbus) < RPACKET_SIZE(GLOBAL, PCM_WRITE)) { @@ -328,11 +328,11 @@ static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eo } len += sprintf(page + len, "\n\t%-17s: ", "offhook"); for_each_line(xpd, i) { - len += sprintf(page + len, "%d ", IS_SET(xpd->offhook, i)); + len += sprintf(page + len, "%d ", IS_OFFHOOK(xpd, i)); } - len += sprintf(page + len, "\n\t%-17s: ", "cid_on"); + len += sprintf(page + len, "\n\t%-17s: ", "oht_pcm_pass"); for_each_line(xpd, i) { - len += sprintf(page + len, "%d ", IS_SET(xpd->cid_on, i)); + len += sprintf(page + len, "%d ", IS_SET(xpd->oht_pcm_pass, i)); } len += sprintf(page + len, "\n\t%-17s: ", "msg_waiting"); for_each_line(xpd, i) { @@ -350,7 +350,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] | W D"); for_each_line(xpd, i) { - struct zt_chan *chans = xpd->span.chans; + struct zt_chan *chan = XPD_CHAN(xpd, i); byte rchunk[ZT_CHUNKSIZE]; byte wchunk[ZT_CHUNKSIZE]; byte *rp; @@ -363,8 +363,8 @@ static int xpd_read_proc(char *page, char **start, off_t off, int count, int *eo continue; if(IS_SET(xpd->digital_signalling, i)) continue; - rp = chans[i].readchunk; - wp = chans[i].writechunk; + rp = chan->readchunk; + wp = chan->writechunk; memcpy(rchunk, rp, ZT_CHUNKSIZE); memcpy(wchunk, wp, ZT_CHUNKSIZE); len += sprintf(page + len, "\n port %2d> | ", i); @@ -386,7 +386,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, "\nSignalling:\n"); for_each_line(xpd, i) { - struct zt_chan *chan = &xpd->span.chans[i]; + struct zt_chan *chan = XPD_CHAN(xpd, i); len += sprintf(page + len, "\t%2d> sigcap=0x%04X sig=0x%04X\n", i, chan->sigcap, chan->sig); } } @@ -438,12 +438,13 @@ xpd_t *xpd_alloc(size_t privsize, const xproto_table_t *proto_table, int channel } xpd->priv = (byte *)xpd + sizeof(xpd_t); spin_lock_init(&xpd->lock); + spin_lock_init(&xpd->lock_recompute_pcm); xpd->xbus = NULL; xpd->xbus_idx = -1; xpd->channels = channels; xpd->chans = NULL; xpd->card_present = 0; - xpd->offhook = 0x0; /* ONHOOK */ + xpd->offhook_state = 0x0; /* ONHOOK */ xpd->type = proto_table->type; xpd->xproto = proto_table; xpd->xops = &proto_table->xops; @@ -493,8 +494,9 @@ void xpd_disconnect(xpd_t *xpd) update_xpd_status(xpd, ZT_ALARM_NOTOPEN); /* TODO: Should this be done before releasing the spinlock? */ XPD_DBG(DEVICES, xpd, "Queuing ZT_EVENT_REMOVED on all channels to ask user to release them\n"); - for (i=0; i<xpd->span.channels; i++) - zt_qevent_lock(&xpd->chans[i],ZT_EVENT_REMOVED); + for (i=0; i<xpd->span.channels; i++) { + zt_qevent_lock(XPD_CHAN(xpd, i),ZT_EVENT_REMOVED); + } } out: spin_unlock_irqrestore(&xpd->lock, flags); @@ -535,18 +537,64 @@ void update_xpd_status(xpd_t *xpd, int alarm_flag) XPD_DBG(GENERAL, xpd, "Update XPD alarms: %s -> %02X\n", xpd->span.name, alarm_flag); } -void update_line_status(xpd_t *xpd, int pos, bool to_offhook) +/* + * Used to block/pass PCM during onhook-transfers. E.g: + * - Playing FSK after FXS ONHOOK for MWI (non-neon style) + * - Playing DTFM/FSK for FXO Caller-ID detection. + */ +void oht_pcm(xpd_t *xpd, int pos, bool pass) { - zt_rxsig_t rxsig; + if(pass) { + LINE_DBG(SIGNAL, xpd, pos, "OHT PCM: pass\n"); + BIT_SET(xpd->oht_pcm_pass, pos); + } else { + LINE_DBG(SIGNAL, xpd, pos, "OHT PCM: block\n"); + BIT_CLR(xpd->oht_pcm_pass, pos); + } + CALL_XMETHOD(card_pcm_recompute, xpd->xbus, xpd, 0); +} - BUG_ON(!xpd); +/* + * Update our hookstate -- for PCM block/pass + */ +void mark_offhook(xpd_t *xpd, int pos, bool to_offhook) +{ if(to_offhook) { - BIT_SET(xpd->offhook, pos); - rxsig = ZT_RXSIG_OFFHOOK; + LINE_DBG(SIGNAL, xpd, pos, "OFFHOOK\n"); + BIT_SET(xpd->offhook_state, pos); } else { - BIT_CLR(xpd->offhook, pos); - BIT_CLR(xpd->cid_on, pos); - rxsig = ZT_RXSIG_ONHOOK; + LINE_DBG(SIGNAL, xpd, pos, "ONHOOK\n"); + BIT_CLR(xpd->offhook_state, pos); + } + CALL_XMETHOD(card_pcm_recompute, xpd->xbus, xpd, 0); +} + +/* + * Send a signalling notification to Asterisk + */ +void notify_rxsig(xpd_t *xpd, int pos, zt_rxsig_t rxsig) +{ + /* + * We should not spinlock before calling zt_hooksig() as + * it may call back into our xpp_hooksig() and cause + * a nested spinlock scenario + */ + LINE_DBG(SIGNAL, xpd, pos, "rxsig=%s\n", rxsig2str(rxsig)); + if(SPAN_REGISTERED(xpd)) + zt_hooksig(XPD_CHAN(xpd, pos), rxsig); +} + +/* + * Called when hardware state changed: + * - FXS -- the phone was picked up or hanged-up. + * - FXO -- we answered the phone or handed-up. + */ +void hookstate_changed(xpd_t *xpd, int pos, bool to_offhook) +{ + BUG_ON(!xpd); + mark_offhook(xpd, pos, to_offhook); + if(!to_offhook) { + oht_pcm(xpd, pos, 0); /* * To prevent latest PCM to stay in buffers * indefinitely, mark this channel for a @@ -556,14 +604,7 @@ void update_line_status(xpd_t *xpd, int pos, bool to_offhook) */ BIT_SET(xpd->silence_pcm, pos); } - /* - * We should not spinlock before calling zt_hooksig() as - * it may call back into our xpp_hooksig() and cause - * a nested spinlock scenario - */ - LINE_DBG(SIGNAL, xpd, pos, "rxsig=%s\n", (rxsig == ZT_RXSIG_ONHOOK) ? "ONHOOK" : "OFFHOOK"); - if(SPAN_REGISTERED(xpd)) - zt_hooksig(&xpd->chans[pos], rxsig); + notify_rxsig(xpd, pos, (to_offhook) ? ZT_RXSIG_OFFHOOK : ZT_RXSIG_ONHOOK); } #ifdef CONFIG_PROC_FS @@ -704,8 +745,6 @@ int xpp_open(struct zt_chan *chan) spin_lock_irqsave(&xbus->lock, flags); atomic_inc(&xbus->xbus_ref_count); atomic_inc(&xpd->open_counter); - if(IS_SET(xpd->digital_signalling, pos)) /* D-chan offhook */ - BIT_SET(xpd->offhook, pos); DBG(DEVICES, "chan=%d (xbus_ref_count=%d)\n", pos, atomic_read(&xbus->xbus_ref_count)); spin_unlock_irqrestore(&xbus->lock, flags); @@ -723,8 +762,6 @@ int xpp_close(struct zt_chan *chan) spin_lock_irqsave(&xbus->lock, flags); atomic_dec(&xpd->open_counter); - if(IS_SET(xpd->digital_signalling, pos)) /* D-chan onhook */ - BIT_CLR(xpd->offhook, pos); spin_unlock_irqrestore(&xbus->lock, flags); if(xpd->xops->card_close) xpd->xops->card_close(xpd, pos); @@ -977,26 +1014,18 @@ 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. + * Update zaptel about our state: + * - Since asterisk didn't open the channel yet, + * the report is discarded anyway. + * - Our FXS driver have another notification mechanism that + * is triggered (indirectly) by the open() of the channe. + * - The real fix should be in Asterisk (to get the correct state + * after open). */ for_each_line(xpd, cn) { - struct zt_chan *chans = xpd->span.chans; - - if(IS_SET(xpd->offhook, cn)) { - LINE_NOTICE(xpd, cn, "Report OFFHOOK to zaptel\n"); - zt_hooksig(&chans[cn], ZT_RXSIG_OFFHOOK); - } - } -#endif + if(IS_OFFHOOK(xpd, cn)) + notify_rxsig(xpd, cn, ZT_RXSIG_OFFHOOK); + } return 0; } @@ -1075,7 +1104,10 @@ EXPORT_SYMBOL(xpd_alloc); EXPORT_SYMBOL(xpd_free); EXPORT_SYMBOL(xpd_disconnect); EXPORT_SYMBOL(update_xpd_status); -EXPORT_SYMBOL(update_line_status); +EXPORT_SYMBOL(oht_pcm); +EXPORT_SYMBOL(mark_offhook); +EXPORT_SYMBOL(notify_rxsig); +EXPORT_SYMBOL(hookstate_changed); EXPORT_SYMBOL(xpp_open); EXPORT_SYMBOL(xpp_close); EXPORT_SYMBOL(xpp_ioctl); |