summaryrefslogtreecommitdiff
path: root/kernel/xpp/xpp_zap.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/xpp/xpp_zap.c')
-rw-r--r--kernel/xpp/xpp_zap.c132
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);