summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-11-30 12:10:03 +0000
committertzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2008-11-30 12:10:03 +0000
commit4ba510314a8dcd24bbcd32af137439c75f4c652f (patch)
tree651cb0114d4e6a220d37ef21e16113bff69e8392
parentd007090e421daa4bd616bba8844b3fa17b00e0e8 (diff)
xpp: PCM changes and related bugfixes.
* Power-denial signalling is now sent to Zaptel to decide if we're LS or KS (and not hang up ourselves always). * Fix card_fxo's caller_id_style=1 (FSK). * Macro XPD_CHAN: s/xpd->chans[i]/XPD_CHAN(xpd, i)/ to reduce diff from DAHDI. git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.4@4590 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rw-r--r--kernel/xpp/.version2
-rw-r--r--kernel/xpp/card_bri.c141
-rw-r--r--kernel/xpp/card_fxo.c116
-rw-r--r--kernel/xpp/card_fxs.c134
-rw-r--r--kernel/xpp/card_pri.c44
-rw-r--r--kernel/xpp/xbus-pcm.c65
-rw-r--r--kernel/xpp/xbus-pcm.h4
-rw-r--r--kernel/xpp/xpd.h13
-rw-r--r--kernel/xpp/xpp_zap.c132
-rw-r--r--kernel/xpp/xpp_zap.h6
-rw-r--r--kernel/xpp/xproto.c1
-rw-r--r--kernel/xpp/xproto.h3
12 files changed, 386 insertions, 275 deletions
diff --git a/kernel/xpp/.version b/kernel/xpp/.version
index e7cb22c..1e3ee87 100644
--- a/kernel/xpp/.version
+++ b/kernel/xpp/.version
@@ -1 +1 @@
-trunk-r6058
+branch-rel-6171-r6408
diff --git a/kernel/xpp/card_bri.c b/kernel/xpp/card_bri.c
index ea78ea6..fb6be03 100644
--- a/kernel/xpp/card_bri.c
+++ b/kernel/xpp/card_bri.c
@@ -208,7 +208,6 @@ struct BRI_priv_data {
bool reg30_good;
uint reg30_ticks;
bool layer1_up;
- xpp_line_t card_pcm_mask;
/*
* D-Chan: buffers + extra state info.
@@ -460,8 +459,8 @@ static int rx_dchan(xpd_t *xpd, reg_cmd_t *regcmd)
#ifdef XPP_DEBUGFS
xbus_log(xbus, xpd, 0, regcmd, sizeof(reg_cmd_t)); /* 0 = RX */
#endif
- dchan = &xpd->span.chans[2];
- if(!IS_SET(xpd->offhook, 2)) { /* D-chan is used? */
+ dchan = XPD_CHAN(xpd, 2);
+ if(!IS_OFFHOOK(xpd, 2)) { /* D-chan is used? */
static int rate_limit;
if((rate_limit++ % 1000) == 0)
@@ -534,7 +533,7 @@ static int tx_dchan(xpd_t *xpd)
BUG_ON(!priv);
if(!SPAN_REGISTERED(xpd) || !(xpd->span.flags & ZT_FLAG_RUNNING))
return 0;
- dchan = &xpd->chans[2];
+ dchan = XPD_CHAN(xpd, 2);
len = dchan->bytes2transmit; /* dchan's hdlc package len */
eoframe = dchan->eoftx; /* dchan's end of frame */
dchan->bytes2transmit = 0;
@@ -659,9 +658,6 @@ static int BRI_card_zaptel_preregistration(xpd_t *xpd, bool on)
{
xbus_t *xbus;
struct BRI_priv_data *priv;
- xpp_line_t tmp_pcm_mask;
- int tmp_pcm_len;
- unsigned long flags;
int i;
BUG_ON(!xpd);
@@ -680,7 +676,7 @@ static int BRI_card_zaptel_preregistration(xpd_t *xpd, bool on)
xpd->span.deflaw = ZT_LAW_ALAW;
BIT_SET(xpd->digital_signalling, 2); /* D-Channel */
for_each_line(xpd, i) {
- struct zt_chan *cur_chan = &xpd->chans[i];
+ struct zt_chan *cur_chan = XPD_CHAN(xpd, i);
XPD_DBG(GENERAL, xpd, "setting BRI channel %d\n", i);
snprintf(cur_chan->name, MAX_CHANNAME, "XPP_%s/%02d/%1d%1d/%d",
@@ -705,35 +701,11 @@ static int BRI_card_zaptel_preregistration(xpd_t *xpd, bool on)
} else
cur_chan->sigcap = BRI_BCHAN_SIGCAP;
}
- xpd->offhook = BIT(0) | BIT(1); /* 2*bchan */
-
- /*
- * Compute PCM lentgh and mask
- * We know all cards have been initialized until now
- */
- tmp_pcm_mask = 0;
- if(xpd->addr.subunit == 0) {
- int line_count = 0;
-
- for(i = 0; i < MAX_SUBUNIT; i++) {
- xpd_t *sub_xpd = xpd_byaddr(xbus, xpd->addr.unit, i);
- if(sub_xpd) {
- tmp_pcm_mask |= PCM_SHIFT(sub_xpd->wanted_pcm_mask, i);
- line_count += 2;
- }
- }
- tmp_pcm_len = RPACKET_HEADERSIZE + sizeof(xpp_line_t) + line_count * ZT_CHUNKSIZE;
- } else
- tmp_pcm_len = 0;
- spin_lock_irqsave(&xpd->lock, flags);
- xpd->pcm_len = tmp_pcm_len;
- xpd->wanted_pcm_mask = xpd->offhook;
- priv->card_pcm_mask = tmp_pcm_mask;
+ CALL_XMETHOD(card_pcm_recompute, xbus, xpd, 0);
xpd->span.spanconfig = bri_spanconfig;
xpd->span.chanconfig = bri_chanconfig;
xpd->span.startup = bri_startup;
xpd->span.shutdown = bri_shutdown;
- spin_unlock_irqrestore(&xpd->lock, flags);
return 0;
}
@@ -943,15 +915,38 @@ static int BRI_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
return 0;
}
+static int BRI_card_open(xpd_t *xpd, lineno_t pos)
+{
+ struct BRI_priv_data *priv;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+ if(pos == 2) {
+ LINE_DBG(SIGNAL, xpd, pos, "OFFHOOK the whole span\n");
+ BIT_SET(xpd->offhook_state, 0);
+ BIT_SET(xpd->offhook_state, 1);
+ BIT_SET(xpd->offhook_state, 2);
+ CALL_XMETHOD(card_pcm_recompute, xpd->xbus, xpd, 0);
+ }
+ return 0;
+}
+
static int BRI_card_close(xpd_t *xpd, lineno_t pos)
{
- struct zt_chan *chan = &xpd->span.chans[pos];
+ struct zt_chan *chan = XPD_CHAN(xpd, pos);
/* Clear D-Channel pending data */
chan->bytes2receive = 0;
chan->eofrx = 0;
chan->bytes2transmit = 0;
chan->eoftx = 0;
+ if(pos == 2) {
+ LINE_DBG(SIGNAL, xpd, pos, "ONHOOK the whole span\n");
+ BIT_CLR(xpd->offhook_state, 0);
+ BIT_CLR(xpd->offhook_state, 1);
+ BIT_CLR(xpd->offhook_state, 2);
+ CALL_XMETHOD(card_pcm_recompute, xpd->xbus, xpd, 0);
+ }
return 0;
}
@@ -1031,7 +1026,7 @@ static int bri_startup(struct zt_span *span)
// Turn on all channels
CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 1);
if(SPAN_REGISTERED(xpd)) {
- dchan = &span->chans[2];
+ dchan = XPD_CHAN(xpd, 2);
span->flags |= ZT_FLAG_RUNNING;
/*
* Zaptel (wrongly) assume that D-Channel need HDLC decoding
@@ -1066,14 +1061,74 @@ static int bri_shutdown(struct zt_span *span)
return 0;
}
-static void BRI_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpp_line_t wanted_lines, xpacket_t *pack)
+void BRI_card_pcm_recompute(xbus_t *xbus, xpd_t *xpd, xpp_line_t dont_care)
+{
+ int i;
+ int line_count;
+ xpp_line_t pcm_mask;
+ xpd_t *main_xpd;
+ unsigned long flags;
+
+ BUG_ON(!xpd);
+ main_xpd = xpd_byaddr(xbus, xpd->addr.unit, 0);
+ if(!main_xpd) {
+ XPD_DBG(DEVICES, xpd, "Unit 0 is already gone. Ignore request\n");
+ return;
+ }
+ /*
+ * We calculate all subunits, so use the main lock
+ * as a mutex for the whole operation.
+ */
+ spin_lock_irqsave(&main_xpd->lock_recompute_pcm, flags);
+ line_count = 0;
+ pcm_mask = 0;
+ for(i = 0; i < MAX_SUBUNIT; i++) {
+ xpd_t *sub_xpd = xpd_byaddr(xbus, main_xpd->addr.unit, i);
+
+ if(sub_xpd) {
+ xpp_line_t lines =
+ sub_xpd->offhook_state & ~sub_xpd->digital_signalling;
+
+ if(lines) {
+ pcm_mask |= PCM_SHIFT(lines, i);
+ line_count += 2;
+ }
+ /* subunits have fake pcm_len and wanted_pcm_mask */
+ if(i > 0) {
+ sub_xpd->pcm_len = 0;
+ sub_xpd->wanted_pcm_mask = lines;
+ }
+ }
+ }
+ /*
+ * FIXME: Workaround a bug in sync code of the Astribank.
+ * Send dummy PCM for sync.
+ */
+ if(main_xpd->addr.unit == 0 && line_count == 0) {
+ pcm_mask = BIT(0);
+ line_count = 1;
+ }
+ /*
+ * The main unit account for all subunits (pcm_len and wanted_pcm_mask).
+ */
+ main_xpd->pcm_len = (line_count)
+ ? RPACKET_HEADERSIZE + sizeof(xpp_line_t) + line_count * ZT_CHUNKSIZE
+ : 0L;
+ main_xpd->wanted_pcm_mask = pcm_mask;
+ XPD_DBG(SIGNAL, main_xpd, "pcm_len=%d wanted_pcm_mask=0x%X (%s)\n",
+ main_xpd->pcm_len, main_xpd->wanted_pcm_mask,
+ xpd->xpdname);
+ spin_unlock_irqrestore(&main_xpd->lock_recompute_pcm, flags);
+}
+
+static void BRI_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
{
byte *pcm;
- struct zt_chan *chans;
unsigned long flags;
int i;
int subunit;
xpp_line_t pcm_mask = 0;
+ xpp_line_t wanted_lines;
BUG_ON(!xbus);
@@ -1087,18 +1142,20 @@ static void BRI_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpp_line_t wanted_li
if(!tmp_xpd || !tmp_xpd->card_present)
continue;
spin_lock_irqsave(&tmp_xpd->lock, flags);
- chans = tmp_xpd->span.chans;
+ wanted_lines = tmp_xpd->wanted_pcm_mask;
for_each_line(tmp_xpd, i) {
+ struct zt_chan *chan = XPD_CHAN(tmp_xpd, i);
+
if(IS_SET(wanted_lines, i)) {
if(SPAN_REGISTERED(tmp_xpd)) {
#ifdef DEBUG_PCMTX
- int channo = tmp_xpd->span.chans[i].channo;
+ int channo = chan->channo;
if(pcmtx >= 0 && pcmtx_chan == channo)
memset((u_char *)pcm, pcmtx, ZT_CHUNKSIZE);
else
#endif
- memcpy((u_char *)pcm, chans[i].writechunk, ZT_CHUNKSIZE);
+ memcpy((u_char *)pcm, chan->writechunk, ZT_CHUNKSIZE);
} else
memset((u_char *)pcm, 0x7F, ZT_CHUNKSIZE);
pcm += ZT_CHUNKSIZE;
@@ -1142,7 +1199,7 @@ static void BRI_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
volatile u_char *r;
if(IS_SET(tmp_mask, i)) {
- r = tmp_xpd->span.chans[i].readchunk;
+ r = XPD_CHAN(tmp_xpd, i)->readchunk;
// memset((u_char *)r, 0x5A, ZT_CHUNKSIZE); // DEBUG
memcpy((u_char *)r, pcm, ZT_CHUNKSIZE);
pcm += ZT_CHUNKSIZE;
@@ -1395,9 +1452,11 @@ static xproto_table_t PROTO_TABLE(BRI) = {
.card_zaptel_postregistration = BRI_card_zaptel_postregistration,
.card_hooksig = BRI_card_hooksig,
.card_tick = BRI_card_tick,
+ .card_pcm_recompute = BRI_card_pcm_recompute,
.card_pcm_fromspan = BRI_card_pcm_fromspan,
.card_pcm_tospan = BRI_card_pcm_tospan,
.card_ioctl = BRI_card_ioctl,
+ .card_open = BRI_card_open,
.card_close = BRI_card_close,
.card_register_reply = BRI_card_register_reply,
diff --git a/kernel/xpp/card_fxo.c b/kernel/xpp/card_fxo.c
index 30c9de8..2090773 100644
--- a/kernel/xpp/card_fxo.c
+++ b/kernel/xpp/card_fxo.c
@@ -175,6 +175,27 @@ struct FXO_priv_data {
/*---------------- FXO: Static functions ----------------------------------*/
+static const char *power2str(enum power_state pw)
+{
+ switch(pw) {
+ case POWER_UNKNOWN: return "UNKOWN";
+ case POWER_OFF: return "OFF";
+ case POWER_ON: return "ON";
+ }
+ return NULL;
+}
+
+static void power_change(xpd_t *xpd, int portno, enum power_state pw)
+{
+ struct FXO_priv_data *priv;
+
+ priv = xpd->priv;
+ LINE_DBG(SIGNAL, xpd, portno, "power: %s -> %s\n",
+ power2str(priv->power[portno]),
+ power2str(pw));
+ priv->power[portno] = pw;
+}
+
static void reset_battery_readings(xpd_t *xpd, lineno_t pos)
{
struct FXO_priv_data *priv = xpd->priv;
@@ -265,30 +286,15 @@ static void handle_fxo_leds(xpd_t *xpd)
static void update_zap_ring(xpd_t *xpd, int pos, bool on)
{
- zt_rxsig_t rxsig;
-
BUG_ON(!xpd);
- if(on) {
- if(caller_id_style == CID_STYLE_BELL) {
- LINE_DBG(SIGNAL, xpd, pos, "Caller-ID PCM: off\n");
- BIT_CLR(xpd->cid_on, pos);
- }
- rxsig = ZT_RXSIG_RING;
- } else {
- if(caller_id_style == CID_STYLE_BELL) {
- LINE_DBG(SIGNAL, xpd, pos, "Caller-ID PCM: on\n");
- BIT_SET(xpd->cid_on, pos);
- }
- rxsig = ZT_RXSIG_OFFHOOK;
- }
- pcm_recompute(xpd, 0);
+ if(caller_id_style == CID_STYLE_BELL)
+ oht_pcm(xpd, pos, !on);
/*
* 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);
+ notify_rxsig(xpd, pos, (on) ? ZT_RXSIG_RING : ZT_RXSIG_OFFHOOK);
}
static void mark_ring(xpd_t *xpd, lineno_t pos, bool on, bool update_zap)
@@ -349,15 +355,9 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
else
MARK_OFF(priv, pos, LED_GREEN);
ret = DAA_DIRECT_REQUEST(xbus, xpd, pos, DAA_WRITE, REG_DAA_CONTROL1, value);
- if(to_offhook) {
- BIT_SET(xpd->offhook, pos);
- } else {
- BIT_CLR(xpd->offhook, pos);
- }
- if(caller_id_style != CID_STYLE_ETSI_DTMF) {
- LINE_DBG(SIGNAL, xpd, pos, "Caller-ID PCM: off\n");
- BIT_CLR(xpd->cid_on, pos);
- }
+ mark_offhook(xpd, pos, to_offhook);
+ if(caller_id_style != CID_STYLE_ETSI_DTMF)
+ oht_pcm(xpd, pos, 0);
#ifdef WITH_METERING
priv->metering_count[pos] = 0;
priv->metering_tone_state = 0L;
@@ -366,7 +366,7 @@ static int do_sethook(xpd_t *xpd, int pos, bool to_offhook)
reset_battery_readings(xpd, pos); /* unstable during hook changes */
priv->power_denial_safezone[pos] = (to_offhook) ? POWER_DENIAL_SAFEZONE : 0;
if(!to_offhook)
- priv->power[pos] = POWER_UNKNOWN;
+ power_change(xpd, pos, POWER_UNKNOWN);
priv->cidtimer[pos] = xpd->timer_count;
spin_unlock_irqrestore(&xpd->lock, flags);
return ret;
@@ -469,10 +469,11 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
for_each_line(xpd, i) {
do_sethook(xpd, i, 0);
priv->polarity[i] = POL_UNKNOWN; /* will be updated on next battery sample */
+ priv->polarity_debounce[i] = 0;
priv->battery[i] = BATTERY_UNKNOWN; /* will be updated on next battery sample */
priv->power[i] = POWER_UNKNOWN; /* will be updated on next battery sample */
if(caller_id_style == CID_STYLE_ETSI_DTMF)
- BIT_SET(xpd->cid_on, i);
+ oht_pcm(xpd, i, 1);
}
XPD_DBG(GENERAL, xpd, "done\n");
for_each_line(xpd, i) {
@@ -486,7 +487,7 @@ static int FXO_card_init(xbus_t *xbus, xpd_t *xpd)
do_led(xpd, i, LED_GREEN, 0);
msleep(50);
}
- pcm_recompute(xpd, 0);
+ CALL_XMETHOD(card_pcm_recompute, xbus, xpd, 0);
return 0;
}
@@ -519,7 +520,7 @@ static int FXO_card_zaptel_preregistration(xpd_t *xpd, bool on)
xpd->span.spantype = "FXO";
#endif
for_each_line(xpd, i) {
- struct zt_chan *cur_chan = &xpd->chans[i];
+ struct zt_chan *cur_chan = XPD_CHAN(xpd, i);
XPD_DBG(GENERAL, xpd, "setting FXO channel %d\n", i);
snprintf(cur_chan->name, MAX_CHANNAME, "XPP_FXO/%02d/%1d%1d/%d",
@@ -585,7 +586,6 @@ static int FXO_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
txsig2str(txsig), txsig);
return -EINVAL;
}
- pcm_recompute(xpd, 0);
return ret;
}
@@ -602,11 +602,11 @@ static void zap_report_battery(xpd_t *xpd, lineno_t chan)
break;
case BATTERY_OFF:
LINE_DBG(SIGNAL, xpd, chan, "Send ZT_ALARM_RED\n");
- zt_alarm_channel(&xpd->chans[chan], ZT_ALARM_RED);
+ zt_alarm_channel(XPD_CHAN(xpd, chan), ZT_ALARM_RED);
break;
case BATTERY_ON:
LINE_DBG(SIGNAL, xpd, chan, "Send ZT_ALARM_NONE\n");
- zt_alarm_channel(&xpd->chans[chan], ZT_ALARM_NONE);
+ zt_alarm_channel(XPD_CHAN(xpd, chan), ZT_ALARM_NONE);
break;
}
}
@@ -636,7 +636,7 @@ static void poll_metering(xbus_t *xbus, xpd_t *xpd)
int i;
for_each_line(xpd, i) {
- if (IS_SET(xpd->offhook, i))
+ if (IS_OFFHOOK(xpd, i))
DAA_DIRECT_REQUEST(xbus, xpd, i, DAA_READ, DAA_REG_METERING, 0);
}
}
@@ -689,7 +689,7 @@ static void handle_fxo_power_denial(xpd_t *xpd)
DAA_DIRECT_REQUEST(xpd->xbus, xpd, i, DAA_READ, DAA_REG_CURRENT, 0);
}
}
- if(IS_SET(priv->maybe_power_denial, i) && !xpd->ringing[i] && IS_SET(xpd->offhook, i)) {
+ if(IS_SET(priv->maybe_power_denial, i) && !xpd->ringing[i] && IS_OFFHOOK(xpd, i)) {
/*
* Ring detection by the firmware takes some time.
* Therefore we delay our decision until we are
@@ -700,9 +700,10 @@ static void handle_fxo_power_denial(xpd_t *xpd)
LINE_DBG(SIGNAL, xpd, i, "Power Denial Hangup\n");
priv->power_denial_delay[i] = 0;
BIT_CLR(priv->maybe_power_denial, i);
- do_sethook(xpd, i, 0);
- update_line_status(xpd, i, 0);
- pcm_recompute(xpd, 0);
+ /*
+ * Let Asterisk decide what to do
+ */
+ notify_rxsig(xpd, i, ZT_RXSIG_ONHOOK);
}
} else {
priv->power_denial_delay[i] = 0;
@@ -731,7 +732,7 @@ static void check_etsi_dtmf(xpd_t *xpd)
timer_count = xpd->timer_count;
for_each_line(xpd, portno) {
/* Skip offhook and ringing ports */
- if(IS_SET(xpd->offhook, portno) || xpd->ringing[portno])
+ if(IS_OFFHOOK(xpd, portno) || xpd->ringing[portno])
continue;
if(IS_SET(priv->cidfound, portno)) {
if(timer_count > priv->cidtimer[portno] + 4000) {
@@ -743,7 +744,7 @@ static void check_etsi_dtmf(xpd_t *xpd)
continue;
}
if(timer_count > priv->cidtimer[portno] + 400) {
- struct zt_chan *chan = &xpd->span.chans[portno];
+ struct zt_chan *chan = XPD_CHAN(xpd, portno);
int sample;
int i;
@@ -922,8 +923,11 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, xportno_t portno)
priv->battery[portno] = BATTERY_OFF;
if(SPAN_REGISTERED(xpd))
zap_report_battery(xpd, portno);
- priv->polarity[portno] = POL_UNKNOWN; /* What's the polarity ? */
- priv->power[portno] = POWER_UNKNOWN; /* What's the current ? */
+ /* What's the polarity ? */
+ priv->polarity[portno] = POL_UNKNOWN;
+ priv->polarity_debounce[portno] = 0;
+ /* What's the current ? */
+ power_change(xpd, portno, POWER_UNKNOWN);
/*
* Stop further processing for now
*/
@@ -976,7 +980,7 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, xportno_t portno)
msec = priv->polarity_debounce[portno]++ * poll_battery_interval;
if (msec >= POLREV_THRESHOLD) {
priv->polarity_debounce[portno] = 0;
- if(pol != POL_UNKNOWN) {
+ if(pol != POL_UNKNOWN && priv->polarity[portno] != POL_UNKNOWN) {
char *polname = NULL;
if(pol == POL_POSITIVE)
@@ -993,14 +997,12 @@ static void update_battery_voltage(xpd_t *xpd, byte data_low, xportno_t portno)
* 2. In some countries used to report caller-id during onhook
* but before first ring.
*/
- if(caller_id_style == CID_STYLE_ETSI_FSK) {
- LINE_DBG(SIGNAL, xpd, portno, "Caller-ID PCM: on\n");
- BIT_SET(xpd->cid_on, portno); /* will be cleared on ring/offhook */
- }
+ if(caller_id_style == CID_STYLE_ETSI_FSK)
+ oht_pcm(xpd, portno, 1); /* will be cleared on ring/offhook */
if(SPAN_REGISTERED(xpd)) {
LINE_DBG(SIGNAL, xpd, portno,
"Send ZT_EVENT_POLARITY: %s\n", polname);
- zt_qevent_lock(&xpd->chans[portno], ZT_EVENT_POLARITY);
+ zt_qevent_lock(XPD_CHAN(xpd, portno), ZT_EVENT_POLARITY);
}
}
priv->polarity[portno] = pol;
@@ -1024,7 +1026,7 @@ static void update_battery_current(xpd_t *xpd, byte data_low, xportno_t portno)
* During ringing, current is not stable.
* During onhook there should not be current anyway.
*/
- if(xpd->ringing[portno] || !IS_SET(xpd->offhook, portno))
+ if(xpd->ringing[portno] || !IS_OFFHOOK(xpd, portno))
goto ignore_it;
/*
* Power denial with no battery voltage is meaningless
@@ -1036,15 +1038,16 @@ static void update_battery_current(xpd_t *xpd, byte data_low, xportno_t portno)
goto ignore_it;
if(data_low < POWER_DENIAL_CURRENT) {
if(priv->power[portno] == POWER_ON) {
- LINE_DBG(SIGNAL, xpd, portno, "power: ON -> OFF\n");
- priv->power[portno] = POWER_OFF;
+ power_change(xpd, portno, POWER_OFF);
priv->power_denial_minimum[portno] = POWER_DENIAL_TIME;
}
} else {
- LINE_DBG(SIGNAL, xpd, portno, "power: ON\n");
- priv->power[portno] = POWER_ON;
- priv->power_denial_minimum[portno] = 0;
- update_line_status(xpd, portno, 1);
+ if(priv->power[portno] != POWER_ON) {
+ power_change(xpd, portno, POWER_ON);
+ priv->power_denial_minimum[portno] = 0;
+ /* We are now OFFHOOK */
+ hookstate_changed(xpd, portno, 1);
+ }
}
return;
ignore_it:
@@ -1135,6 +1138,7 @@ static xproto_table_t PROTO_TABLE(FXO) = {
.card_zaptel_postregistration = FXO_card_zaptel_postregistration,
.card_hooksig = FXO_card_hooksig,
.card_tick = FXO_card_tick,
+ .card_pcm_recompute = generic_card_pcm_recompute,
.card_pcm_fromspan = generic_card_pcm_fromspan,
.card_pcm_tospan = generic_card_pcm_tospan,
.card_ioctl = FXO_card_ioctl,
diff --git a/kernel/xpp/card_fxs.c b/kernel/xpp/card_fxs.c
index 89293ea..7068bf7 100644
--- a/kernel/xpp/card_fxs.c
+++ b/kernel/xpp/card_fxs.c
@@ -174,6 +174,21 @@ static int do_chan_power(xbus_t *xbus, xpd_t *xpd, lineno_t chan, bool on)
return SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, REG_BATTERY, value);
}
+static void vmwi_search(xpd_t *xpd, lineno_t pos, bool on)
+{
+ struct FXS_priv_data *priv;
+
+ priv = xpd->priv;
+ BUG_ON(!xpd);
+ if(vmwineon && on) {
+ LINE_DBG(SIGNAL, xpd, pos, "START\n");
+ BIT_SET(priv->search_fsk_pattern, pos);
+ } else {
+ LINE_DBG(SIGNAL, xpd, pos, "STOP\n");
+ BIT_CLR(priv->search_fsk_pattern, pos);
+ }
+}
+
/*
* LED and RELAY control is done via SLIC register 0x06:
* 7 6 5 4 3 2 1 0
@@ -286,7 +301,7 @@ static void restore_leds(xpd_t *xpd)
priv = xpd->priv;
for_each_line(xpd, i) {
- if(IS_SET(xpd->offhook, i))
+ if(IS_OFFHOOK(xpd, i))
MARK_ON(priv, i, LED_GREEN);
else
MARK_OFF(priv, i, LED_GREEN);
@@ -438,7 +453,7 @@ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd)
msleep(50);
}
restore_leds(xpd);
- pcm_recompute(xpd, 0);
+ CALL_XMETHOD(card_pcm_recompute, xbus, xpd, 0);
/*
* We should query our offhook state long enough time after we
* set the linefeed_control()
@@ -483,7 +498,7 @@ static int FXS_card_zaptel_preregistration(xpd_t *xpd, bool on)
xpd->span.spantype = "FXS";
#endif
for_each_line(xpd, i) {
- struct zt_chan *cur_chan = &xpd->chans[i];
+ struct zt_chan *cur_chan = XPD_CHAN(xpd, i);
XPD_DBG(GENERAL, xpd, "setting FXS channel %d\n", i);
if(IS_SET(xpd->digital_outputs, i)) {
@@ -540,19 +555,18 @@ static void __do_mute_dtmf(xpd_t *xpd, int pos, bool muteit)
BIT_SET(xpd->mute_dtmf, pos);
else
BIT_CLR(xpd->mute_dtmf, pos);
+ CALL_XMETHOD(card_pcm_recompute, xpd->xbus, xpd, 0); /* already spinlocked */
}
-static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos, int on)
+static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos, bool msg_waiting)
{
int ret = 0;
BUG_ON(!xbus);
BUG_ON(!xpd);
- LINE_DBG(SIGNAL, xpd, pos, "%s%s\n", (on)?"ON":"OFF", (vmwineon)?"":" (Ignored)");
- if (!vmwineon)
- return 0;
- if (on) {
+ if (vmwineon && msg_waiting) {
/* A write to register 0x40 will now turn on/off the VM led */
+ LINE_DBG(SIGNAL, xpd, pos, "NEON\n");
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x16, 0xE8, 0x03);
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x15, 0xEF, 0x7B);
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x14, 0x9F, 0x00);
@@ -565,6 +579,7 @@ static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos, int on)
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x1D, 0x00, 0x46);
} else {
/* A write to register 0x40 will now turn on/off the ringer */
+ LINE_DBG(SIGNAL, xpd, pos, "RINGER\n");
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x16, 0x00, 0x00);
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x15, 0x60, 0x01);
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x14, 0xF0, 0x7E);
@@ -576,7 +591,6 @@ static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos, int on)
ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x33, 0x00);
ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x1D, 0x00, 0x36);
}
-
return (ret ? -EPROTO : 0);
}
@@ -586,7 +600,7 @@ static void start_stop_vm_led(xbus_t *xbus, xpd_t *xpd, lineno_t pos)
bool on;
BUG_ON(!xpd);
- if (!vmwineon || IS_SET(xpd->digital_outputs | xpd->digital_inputs, pos))
+ if (IS_SET(xpd->digital_outputs | xpd->digital_inputs, pos))
return;
priv = xpd->priv;
on = IS_SET(xpd->msg_waiting, pos);
@@ -655,17 +669,16 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
return 0;
}
if(SPAN_REGISTERED(xpd))
- chan = &xpd->span.chans[pos];
+ chan = XPD_CHAN(xpd, pos);
switch(txsig) {
case ZT_TXSIG_ONHOOK:
spin_lock_irqsave(&xpd->lock, flags);
xpd->ringing[pos] = 0;
- BIT_CLR(xpd->cid_on, pos);
- BIT_CLR(priv->search_fsk_pattern, pos);
+ oht_pcm(xpd, pos, 0);
+ vmwi_search(xpd, pos, 0);
BIT_CLR(priv->want_dtmf_events, pos);
BIT_CLR(priv->want_dtmf_mute, pos);
__do_mute_dtmf(xpd, pos, 0);
- __pcm_recompute(xpd, 0); /* already spinlocked */
spin_unlock_irqrestore(&xpd->lock, flags);
if(IS_SET(xpd->digital_outputs, pos)) {
LINE_DBG(SIGNAL, xpd, pos, "%s -> digital output OFF\n", txsig2str(txsig));
@@ -678,11 +691,11 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
*/
LINE_DBG(SIGNAL, xpd, pos, "KEWL STOP\n");
linefeed_control(xbus, xpd, pos, FXS_LINE_POL_ACTIVE);
- if(IS_SET(xpd->offhook, pos))
+ if(IS_OFFHOOK(xpd, pos))
MARK_ON(priv, pos, LED_GREEN);
}
ret = send_ring(xpd, pos, 0); // RING off
- if (!IS_SET(xpd->offhook, pos))
+ if (!IS_OFFHOOK(xpd, pos))
start_stop_vm_led(xbus, xpd, pos);
txhook = priv->lasttxhook[pos];
if(chan) {
@@ -706,8 +719,7 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
}
txhook = priv->lasttxhook[pos];
if(xpd->ringing[pos]) {
- BIT_SET(xpd->cid_on, pos);
- pcm_recompute(xpd, 0);
+ oht_pcm(xpd, pos, 1);
txhook = FXS_LINE_OHTRANS;
}
xpd->ringing[pos] = 0;
@@ -725,9 +737,8 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
break;
case ZT_TXSIG_START:
xpd->ringing[pos] = 1;
- BIT_CLR(xpd->cid_on, pos);
- BIT_CLR(priv->search_fsk_pattern, pos);
- pcm_recompute(xpd, 0);
+ oht_pcm(xpd, pos, 0);
+ vmwi_search(xpd, pos, 0);
if(IS_SET(xpd->digital_outputs, pos)) {
LINE_DBG(SIGNAL, xpd, pos, "%s -> digital output ON\n", txsig2str(txsig));
ret = relay_out(xpd, pos, 1);
@@ -782,14 +793,15 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
LINE_DBG(SIGNAL, xpd, pos, "ZT_ONHOOKTRANSFER (%d millis)\n", val);
if (IS_SET(xpd->digital_inputs | xpd->digital_outputs, pos))
return 0; /* Nothing to do */
- BIT_CLR(xpd->cid_on, pos);
+ oht_pcm(xpd, pos, 1); /* Get ready of VMWI FSK tones */
if(priv->lasttxhook[pos] == FXS_LINE_POL_ACTIVE) {
- priv->ohttimer[pos] = OHT_TIMER;
+ priv->ohttimer[pos] = val;
priv->idletxhookstate[pos] = FXS_LINE_POL_OHTRANS;
- BIT_SET(priv->search_fsk_pattern, pos);
- pcm_recompute(xpd, priv->search_fsk_pattern);
+ vmwi_search(xpd, pos, 1);
+ CALL_XMETHOD(card_pcm_recompute, xbus, xpd, priv->search_fsk_pattern);
+ LINE_DBG(SIGNAL, xpd, pos, "Start OHT_TIMER. wanted_pcm_mask=0x%X\n", xpd->wanted_pcm_mask);
}
- if(!IS_SET(xpd->offhook, pos))
+ if(vmwineon && !IS_OFFHOOK(xpd, pos))
start_stop_vm_led(xbus, xpd, pos);
return 0;
case ZT_TONEDETECT:
@@ -808,7 +820,6 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
BIT_CLR(priv->want_dtmf_events, pos);
BIT_CLR(priv->want_dtmf_mute, pos);
__do_mute_dtmf(xpd, pos, 0);
- __pcm_recompute(xpd, 0); /* already spinlocked */
spin_unlock_irqrestore(&xpd->lock, flags);
return -ENOTTY;
}
@@ -840,7 +851,6 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
} else {
BIT_CLR(priv->want_dtmf_mute, pos);
__do_mute_dtmf(xpd, pos, 0);
- __pcm_recompute(xpd, 0);
}
spin_unlock_irqrestore(&xpd->lock, flags);
return 0;
@@ -886,12 +896,10 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
static int FXS_card_open(xpd_t *xpd, lineno_t chan)
{
struct FXS_priv_data *priv;
- bool is_offhook;
BUG_ON(!xpd);
priv = xpd->priv;
- is_offhook = IS_SET(xpd->offhook, chan);
- if(is_offhook)
+ if(IS_OFFHOOK(xpd, chan))
LINE_NOTICE(xpd, chan, "Already offhook during open. OK.\n");
else
LINE_DBG(SIGNAL, xpd, chan, "is onhook\n");
@@ -957,10 +965,10 @@ static void handle_linefeed(xpd_t *xpd)
if (priv->ohttimer[i]) {
priv->ohttimer[i]--;
if (!priv->ohttimer[i]) {
+ LINE_DBG(SIGNAL, xpd, i, "ohttimer expired\n");
priv->idletxhookstate[i] = FXS_LINE_POL_ACTIVE;
- BIT_CLR(xpd->cid_on, i);
- BIT_CLR(priv->search_fsk_pattern, i);
- pcm_recompute(xpd, 0);
+ oht_pcm(xpd, i, 0);
+ vmwi_search(xpd, i, 0);
if (priv->lasttxhook[i] == FXS_LINE_POL_OHTRANS) {
/* Apply the change if appropriate */
linefeed_control(xpd->xbus, xpd, i, FXS_LINE_POL_ACTIVE);
@@ -996,16 +1004,23 @@ static void detect_vmwi(xpd_t *xpd)
static const byte FSK_ON_PATTERN[] = { 0xA2, 0x2C, 0x1F, 0x2C, 0xBB, 0xA1, 0xA5, 0xFF };
static const byte FSK_OFF_PATTERN[] = { 0xA2, 0x2C, 0x28, 0xA5, 0xB1, 0x21, 0x49, 0x9F };
int i;
+ xpp_line_t ignore_mask;
BUG_ON(!xpd);
xbus = xpd->xbus;
priv = xpd->priv;
BUG_ON(!priv);
+ ignore_mask =
+ xpd->offhook_state |
+ ~xpd->oht_pcm_pass |
+ ~priv->search_fsk_pattern |
+ xpd->digital_inputs |
+ xpd->digital_outputs;
for_each_line(xpd, i) {
- struct zt_chan *chan = &xpd->span.chans[i];
+ struct zt_chan *chan = XPD_CHAN(xpd, i);
byte *writechunk = chan->writechunk;
- if(IS_SET(xpd->offhook | xpd->cid_on | xpd->digital_inputs | xpd->digital_outputs, i))
+ if(IS_SET(ignore_mask, i))
continue;
#if 0
if(writechunk[0] != 0x7F && writechunk[0] != 0) {
@@ -1020,10 +1035,12 @@ static void detect_vmwi(xpd_t *xpd)
printk("\n");
}
#endif
- if(unlikely(mem_equal(writechunk, FSK_COMMON_PATTERN, ZT_CHUNKSIZE)))
+ if(unlikely(mem_equal(writechunk, FSK_COMMON_PATTERN, ZT_CHUNKSIZE))) {
+ LINE_DBG(SIGNAL, xpd, i, "Found common FSK pattern. Start looking for ON/OFF patterns.\n");
BIT_SET(priv->found_fsk_pattern, i);
- else if(unlikely(IS_SET(priv->found_fsk_pattern, i))) {
+ } else if(unlikely(IS_SET(priv->found_fsk_pattern, i))) {
BIT_CLR(priv->found_fsk_pattern, i);
+ oht_pcm(xpd, i, 0);
if(unlikely(mem_equal(writechunk, FSK_ON_PATTERN, ZT_CHUNKSIZE))) {
LINE_DBG(SIGNAL, xpd, i, "MSG WAITING ON\n");
BIT_SET(xpd->msg_waiting, i);
@@ -1060,25 +1077,27 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd)
#endif
handle_fxs_leds(xpd);
handle_linefeed(xpd);
- if(priv->update_offhook_state) { /* set in FXS_card_open() */
- int i;
+ /*
+ * Hack alert (FIXME):
+ * Asterisk did FXS_card_open() and we wanted to report
+ * offhook state. However, the channel is spinlocked by zaptel
+ * so we marked it in the priv->update_offhook_state mask and
+ * now we take care of notification to zaptel and Asterisk
+ */
+ if(priv->update_offhook_state) {
+ zt_rxsig_t rxsig;
+ int i;
for_each_line(xpd, i) {
if(!IS_SET(priv->update_offhook_state, i))
continue;
- /*
- * Update zaptel with current state of line.
- */
- if(IS_SET(xpd->offhook, i)) {
- update_line_status(xpd, i, 1);
- } else {
- update_line_status(xpd, i, 0);
- }
+ rxsig = IS_OFFHOOK(xpd, i) ? ZT_RXSIG_OFFHOOK : ZT_RXSIG_ONHOOK;
+ notify_rxsig(xpd, i, rxsig); /* Notify after open() */
BIT_CLR(priv->update_offhook_state, i);
}
}
if(SPAN_REGISTERED(xpd)) {
- if(vmwineon && !vmwi_ioctl)
+ if(vmwineon && !vmwi_ioctl && priv->search_fsk_pattern)
detect_vmwi(xpd); /* Detect via FSK modulation */
}
return 0;
@@ -1127,11 +1146,11 @@ static void process_hookstate(xpd_t *xpd, xpp_line_t offhook, xpp_line_t change_
if(IS_SET(offhook, i)) {
LINE_DBG(SIGNAL, xpd, i, "OFFHOOK\n");
MARK_ON(priv, i, LED_GREEN);
- update_line_status(xpd, i, 1);
+ hookstate_changed(xpd, i, 1);
} else {
LINE_DBG(SIGNAL, xpd, i, "ONHOOK\n");
MARK_OFF(priv, i, LED_GREEN);
- update_line_status(xpd, i, 0);
+ hookstate_changed(xpd, i, 0);
}
/*
* Must switch to low power. In high power, an ONHOOK
@@ -1140,7 +1159,6 @@ static void process_hookstate(xpd_t *xpd, xpp_line_t offhook, xpp_line_t change_
do_chan_power(xbus, xpd, i, 0);
}
}
- __pcm_recompute(xpd, 0); /* in a spinlock */
}
HANDLER_DEF(FXS, SIG_CHANGED)
@@ -1182,12 +1200,12 @@ static void process_digital_inputs(xpd_t *xpd, const reg_cmd_t *info)
BIT_CLR(lines, channo);
BIT_SET(lines, newchanno);
xpd->ringing[newchanno] = 0; // Stop ringing. No leds for digital inputs.
- if(offhook && !IS_SET(xpd->offhook, newchanno)) { // OFFHOOK
+ if(offhook && !IS_OFFHOOK(xpd, newchanno)) { // OFFHOOK
LINE_DBG(SIGNAL, xpd, newchanno, "OFFHOOK\n");
- update_line_status(xpd, newchanno, 1);
- } else if(!offhook && IS_SET(xpd->offhook, newchanno)) { // ONHOOK
+ hookstate_changed(xpd, newchanno, 1);
+ } else if(!offhook && IS_OFFHOOK(xpd, newchanno)) { // ONHOOK
LINE_DBG(SIGNAL, xpd, newchanno, "ONHOOK\n");
- update_line_status(xpd, newchanno, 0);
+ hookstate_changed(xpd, newchanno, 0);
}
}
}
@@ -1253,11 +1271,10 @@ static void process_dtmf(xpd_t *xpd, uint portnum, byte val)
__do_mute_dtmf(xpd, portnum, 1);
else
__do_mute_dtmf(xpd, portnum, 0);
- __pcm_recompute(xpd, 0); /* XPD is locked */
if(want_event) {
int event = (key_down) ? ZT_EVENT_DTMFDOWN : ZT_EVENT_DTMFUP;
- zt_qevent_lock(&xpd->chans[portnum], event | digit);
+ zt_qevent_lock(XPD_CHAN(xpd, portnum), event | digit);
}
}
@@ -1334,6 +1351,7 @@ static xproto_table_t PROTO_TABLE(FXS) = {
.card_zaptel_postregistration = FXS_card_zaptel_postregistration,
.card_hooksig = FXS_card_hooksig,
.card_tick = FXS_card_tick,
+ .card_pcm_recompute = generic_card_pcm_recompute,
.card_pcm_fromspan = generic_card_pcm_fromspan,
.card_pcm_tospan = generic_card_pcm_tospan,
.card_open = FXS_card_open,
diff --git a/kernel/xpp/card_pri.c b/kernel/xpp/card_pri.c
index 970a2a5..78d2025 100644
--- a/kernel/xpp/card_pri.c
+++ b/kernel/xpp/card_pri.c
@@ -1037,7 +1037,7 @@ static int PRI_card_zaptel_preregistration(xpd_t *xpd, bool on)
xpd->span.linecompat = pri_linecompat(priv->pri_protocol);
xpd->span.deflaw = priv->deflaw;
for_each_line(xpd, i) {
- struct zt_chan *cur_chan = &xpd->chans[i];
+ struct zt_chan *cur_chan = XPD_CHAN(xpd, i);
bool is_dchan = i == PRI_DCHAN_IDX(priv);
XPD_DBG(GENERAL, xpd, "setting PRI channel %d (%s)\n", i,
@@ -1053,7 +1053,7 @@ static int PRI_card_zaptel_preregistration(xpd_t *xpd, bool on)
} else
cur_chan->sigcap = PRI_BCHAN_SIGCAP;
}
- xpd->offhook = xpd->wanted_pcm_mask;
+ xpd->offhook_state = xpd->wanted_pcm_mask;
xpd->span.spanconfig = pri_spanconfig;
xpd->span.chanconfig = pri_chanconfig;
xpd->span.startup = pri_startup;
@@ -1102,9 +1102,9 @@ static void dchan_state(xpd_t *xpd, bool up)
if(SPAN_REGISTERED(xpd) && d >= 0 && d < xpd->channels) {
byte *pcm;
- pcm = (byte *)xpd->span.chans[d].readchunk;
+ pcm = (byte *)XPD_CHAN(xpd, d)->readchunk;
pcm[0] = 0x00;
- pcm = (byte *)xpd->span.chans[d].writechunk;
+ pcm = (byte *)XPD_CHAN(xpd, d)->writechunk;
pcm[0] = 0x00;
}
XPD_DBG(SIGNAL, xpd, "STATE CHANGE: D-Channel STOPPED\n");
@@ -1233,7 +1233,7 @@ static int PRI_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
static int PRI_card_close(xpd_t *xpd, lineno_t pos)
{
- //struct zt_chan *chan = &xpd->span.chans[pos];
+ //struct zt_chan *chan = XPD_CHAN(xpd, pos);
dchan_state(xpd, 0);
return 0;
}
@@ -1342,13 +1342,13 @@ static int pri_rbsbits(struct zt_chan *chan, int bits)
* send 31 channels to the device, but they should be called 1-31 rather
* than 0-30 .
*/
-static void PRI_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, xpacket_t *pack)
+static void PRI_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
{
struct PRI_priv_data *priv;
byte *pcm;
- struct zt_chan *chans;
unsigned long flags;
int i;
+ xpp_line_t wanted_lines;
int physical_chan;
int physical_mask = 0;
@@ -1359,9 +1359,11 @@ static void PRI_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, xp
BUG_ON(!priv);
pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm);
spin_lock_irqsave(&xpd->lock, flags);
- chans = xpd->span.chans;
+ wanted_lines = xpd->wanted_pcm_mask;
physical_chan = 0;
for_each_line(xpd, i) {
+ struct zt_chan *chan = XPD_CHAN(xpd, i);
+
if(priv->pri_protocol == PRI_PROTO_E1) {
/* In E1 - Only 0'th channel is unused */
if(i == 0) {
@@ -1373,28 +1375,28 @@ static void PRI_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, xp
physical_chan++;
}
}
- if(IS_SET(lines, i)) {
+ if(IS_SET(wanted_lines, i)) {
physical_mask |= BIT(physical_chan);
if(SPAN_REGISTERED(xpd)) {
#ifdef DEBUG_PCMTX
- int channo = xpd->span.chans[i].channo;
+ int channo = XPD_CHAN(xpd, i)->channo;
if(pcmtx >= 0 && pcmtx_chan == channo)
memset((u_char *)pcm, pcmtx, ZT_CHUNKSIZE);
else
#endif
- memcpy((u_char *)pcm, chans[i].writechunk, ZT_CHUNKSIZE);
+ memcpy((u_char *)pcm, chan->writechunk, ZT_CHUNKSIZE);
if(i == PRI_DCHAN_IDX(priv)) {
- if(priv->dchan_tx_sample != chans[i].writechunk[0]) {
- priv->dchan_tx_sample = chans[i].writechunk[0];
+ if(priv->dchan_tx_sample != chan->writechunk[0]) {
+ priv->dchan_tx_sample = chan->writechunk[0];
priv->dchan_tx_counter++;
- } else if(chans[i].writechunk[0] == 0xFF)
+ } else if(chan->writechunk[0] == 0xFF)
dchan_state(xpd, 0);
else
- chans[i].writechunk[0] = 0xFF; /* Clobber for next tick */
+ chan->writechunk[0] = 0xFF; /* Clobber for next tick */
}
} else
- memset((u_char *)pcm, ZT_XLAW(0, (&chans[i])), ZT_CHUNKSIZE);
+ memset((u_char *)pcm, ZT_XLAW(0, chan), ZT_CHUNKSIZE);
pcm += ZT_CHUNKSIZE;
}
physical_chan++;
@@ -1418,7 +1420,6 @@ static void PRI_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
{
struct PRI_priv_data *priv;
byte *pcm;
- struct zt_chan *chans;
xpp_line_t physical_mask;
unsigned long flags;
int i;
@@ -1431,7 +1432,6 @@ static void PRI_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
pcm = RPACKET_FIELD(pack, GLOBAL, PCM_READ, pcm);
physical_mask = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines);
spin_lock_irqsave(&xpd->lock, flags);
- chans = xpd->span.chans;
logical_chan = 0;
for (i = 0; i < CHANNELS_PERXPD; i++) {
volatile u_char *r;
@@ -1458,9 +1458,8 @@ static void PRI_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
dchan_state(xpd, 0);
}
if(IS_SET(physical_mask, i)) {
- r = chans[logical_chan].readchunk;
+ r = XPD_CHAN(xpd, logical_chan)->readchunk;
// 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;
}
@@ -1576,9 +1575,9 @@ static void process_cas_dchan(xpd_t *xpd, byte regnum, byte data_low)
rsnum, chan1+1, chan2+1, priv->cas_rs_e[pos], data_low);
if(SPAN_REGISTERED(xpd)) {
if(old1 != new1)
- zt_rbsbits(&xpd->span.chans[chan1], new1);
+ zt_rbsbits(XPD_CHAN(xpd, chan1), new1);
if(old2 != new2)
- zt_rbsbits(&xpd->span.chans[chan2], new2);
+ zt_rbsbits(XPD_CHAN(xpd, chan2), new2);
}
priv->dchan_rx_counter++;
priv->cas_rs_e[pos] = data_low;
@@ -1657,6 +1656,7 @@ static xproto_table_t PROTO_TABLE(PRI) = {
.card_zaptel_postregistration = PRI_card_zaptel_postregistration,
.card_hooksig = PRI_card_hooksig,
.card_tick = PRI_card_tick,
+ .card_pcm_recompute = generic_card_pcm_recompute,
.card_pcm_fromspan = PRI_card_pcm_fromspan,
.card_pcm_tospan = PRI_card_pcm_tospan,
.card_ioctl = PRI_card_ioctl,
diff --git a/kernel/xpp/xbus-pcm.c b/kernel/xpp/xbus-pcm.c
index a5c186c..ef92a88 100644
--- a/kernel/xpp/xbus-pcm.c
+++ b/kernel/xpp/xbus-pcm.c
@@ -580,19 +580,18 @@ void elect_syncer(const char *msg)
* 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)
+void generic_card_pcm_recompute(xbus_t *xbus, xpd_t *xpd, xpp_line_t pcm_mask)
{
int i;
int line_count = 0;
+ unsigned long flags;
- XPD_DBG(SIGNAL, xpd, "pcm_mask=0x%X\n", pcm_mask);
+ spin_lock_irqsave(&xpd->lock_recompute_pcm, flags);
+ //XPD_DBG(SIGNAL, xpd, "pcm_mask=0x%X\n", pcm_mask);
/* 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->offhook_state;
+ pcm_mask |= xpd->oht_pcm_pass;
pcm_mask &= ~xpd->digital_inputs;
pcm_mask &= ~xpd->digital_outputs;
for_each_line(xpd, i)
@@ -610,18 +609,9 @@ void __pcm_recompute(xpd_t *xpd, xpp_line_t pcm_mask)
? 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);
+ XPD_DBG(SIGNAL, xpd, "pcm_len=%d wanted_pcm_mask=0x%X\n",
+ xpd->pcm_len, xpd->wanted_pcm_mask);
+ spin_unlock_irqrestore(&xpd->lock_recompute_pcm, flags);
}
void fill_beep(u_char *buf, int num, int duration)
@@ -676,11 +666,12 @@ static inline void xpp_ec_chunk(struct zt_chan *chan, unsigned char *rxchunk, co
static void do_ec(xpd_t *xpd)
{
#ifdef WITH_ECHO_SUPPRESSION
- struct zt_chan *chans = xpd->span.chans;
int i;
/* FIXME: need to Echo cancel double buffered data */
for (i = 0;i < xpd->span.channels; i++) {
+ struct zt_chan *chan = XPD_CHAN(xpd, i);
+
if(unlikely(IS_SET(xpd->digital_signalling, i))) /* Don't echo cancel BRI D-chans */
continue;
if(!IS_SET(xpd->wanted_pcm_mask, i)) /* No ec for unwanted PCM */
@@ -688,12 +679,12 @@ static void do_ec(xpd_t *xpd)
#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]);
+ xpp_ec_chunk(chan, chan->readchunk, xpd->ec_chunk2[i]);
else
#endif
- zt_ec_chunk(&chans[i], chans[i].readchunk, xpd->ec_chunk2[i]);
+ zt_ec_chunk(chan, chan->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);
+ memcpy(xpd->ec_chunk1[i], chan->writechunk, ZT_CHUNKSIZE);
}
#endif
}
@@ -821,31 +812,33 @@ dropit:
* 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)
+void generic_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
{
byte *pcm;
- struct zt_chan *chans;
unsigned long flags;
+ xpp_line_t wanted_lines;
int i;
BUG_ON(!xbus);
BUG_ON(!xpd);
BUG_ON(!pack);
- RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = lines;
+ wanted_lines = xpd->wanted_pcm_mask;
+ RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = wanted_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)) {
+ struct zt_chan *chan = XPD_CHAN(xpd, i);
+
+ if(IS_SET(wanted_lines, i)) {
if(SPAN_REGISTERED(xpd)) {
#ifdef DEBUG_PCMTX
- int channo = xpd->span.chans[i].channo;
+ int channo = chan->channo;
if(pcmtx >= 0 && pcmtx_chan == channo)
memset((u_char *)pcm, pcmtx, ZT_CHUNKSIZE);
else
#endif
- memcpy((u_char *)pcm, chans[i].writechunk, ZT_CHUNKSIZE);
+ memcpy((u_char *)pcm, chan->writechunk, ZT_CHUNKSIZE);
} else
memset((u_char *)pcm, 0x7F, ZT_CHUNKSIZE);
pcm += ZT_CHUNKSIZE;
@@ -874,7 +867,7 @@ void generic_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
if(!SPAN_REGISTERED(xpd))
goto out;
for (i = 0; i < xpd->channels; i++) {
- volatile u_char *r = xpd->span.chans[i].readchunk;
+ volatile u_char *r = XPD_CHAN(xpd, i)->readchunk;
bool got_data = IS_SET(pcm_mask, i);
if(got_data && !IS_SET(pcm_mute, i)) {
@@ -974,7 +967,6 @@ static void xbus_tick(xbus_t *xbus)
xpd_t *xpd;
xframe_t *xframe = NULL;
xpacket_t *pack = NULL;
- size_t pcm_len;
bool sent_sync_bit = 0;
/*
@@ -990,7 +982,7 @@ static void xbus_tick(xbus_t *xbus)
xmit_mask |= xpd->silence_pcm;
xmit_mask |= xpd->digital_signalling;
for_each_line(xpd, j) {
- xpd->chans[j].chanmute = (optimize_chanmute)
+ XPD_CHAN(xpd, j)->chanmute = (optimize_chanmute)
? !IS_SET(xmit_mask, j)
: 0;
}
@@ -1006,6 +998,8 @@ static void xbus_tick(xbus_t *xbus)
* Fill xframes
*/
for(i = 0; i < MAX_XPDS; i++) {
+ size_t pcm_len;
+
if((xpd = xpd_of(xbus, i)) == NULL)
continue;
pcm_len = xpd->pcm_len;
@@ -1038,7 +1032,7 @@ static void xbus_tick(xbus_t *xbus)
XPACKET_ADDR_SYNC(pack) = 1;
sent_sync_bit = 1;
}
- CALL_XMETHOD(card_pcm_fromspan, xbus, xpd, xpd->wanted_pcm_mask, pack);
+ CALL_XMETHOD(card_pcm_fromspan, xbus, xpd, pack);
XBUS_COUNTER(xbus, TX_PACK_PCM)++;
}
}
@@ -1301,8 +1295,7 @@ 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_recompute);
EXPORT_SYMBOL(generic_card_pcm_tospan);
EXPORT_SYMBOL(generic_card_pcm_fromspan);
#ifdef DEBUG_PCMTX
diff --git a/kernel/xpp/xbus-pcm.h b/kernel/xpp/xbus-pcm.h
index 3265f68..b4c0cf6 100644
--- a/kernel/xpp/xbus-pcm.h
+++ b/kernel/xpp/xbus-pcm.h
@@ -100,9 +100,9 @@ int xbus_pcm_init(struct proc_dir_entry *top);
void xbus_pcm_shutdown(void);
int send_pcm_frame(xbus_t *xbus, xframe_t *xframe);
void pcm_recompute(xpd_t *xpd, xpp_line_t tmp_pcm_mask);
-void __pcm_recompute(xpd_t *xpd, xpp_line_t tmp_pcm_mask); /* non locking */
void xframe_receive_pcm(xbus_t *xbus, xframe_t *xframe);
-void generic_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, xpacket_t *pack);
+void generic_card_pcm_recompute(xbus_t *xbus, xpd_t *xpd, xpp_line_t pcm_mask);
+void generic_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack);
void generic_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack);
void fill_beep(u_char *buf, int num, int duration);
const char *sync_mode_name(enum sync_mode mode);
diff --git a/kernel/xpp/xpd.h b/kernel/xpp/xpd.h
index 35688d6..c0bfcc0 100644
--- a/kernel/xpp/xpd.h
+++ b/kernel/xpp/xpd.h
@@ -29,13 +29,8 @@
#ifdef __KERNEL__
#include <linux/kernel.h>
#include <linux/device.h>
-#include <linux/version.h>
#include <asm/atomic.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,26)
-#include <linux/semaphore.h>
-#else
#include <asm/semaphore.h>
-#endif
#include <linux/moduleparam.h>
#ifdef XPP_DEBUGFS
#ifndef CONFIG_DEBUG_FS
@@ -146,6 +141,8 @@ struct xpd {
char xpdname[XPD_NAMELEN];
struct zt_span span;
struct zt_chan *chans;
+#define XPD_CHAN(xpd,chan) (&((xpd)->chans[(chan)]))
+
int channels;
xpd_type_t type;
const char *type_name;
@@ -153,14 +150,16 @@ struct xpd {
xpd_direction_t direction; /* TO_PHONE, TO_PSTN */
int subunits; /* all siblings */
xpp_line_t no_pcm; /* Temporary: disable PCM (for USB-1) */
- xpp_line_t offhook; /* Actual chip state: 0 - ONHOOK, 1 - OFHOOK */
- xpp_line_t cid_on;
+ xpp_line_t offhook_state; /* Actual chip state: 0 - ONHOOK, 1 - OFHOOK */
+ xpp_line_t oht_pcm_pass; /* Transfer on-hook PCM */
xpp_line_t msg_waiting; /* Voice Mail Waiting Indication */
xpp_line_t digital_outputs; /* 0 - no, 1 - yes */
xpp_line_t digital_inputs; /* 0 - no, 1 - yes */
xpp_line_t digital_signalling; /* BRI signalling channels */
uint timing_priority; /* from 'span' directives in zapata.conf */
+ /* Assure atomicity of changes to pcm_len and wanted_pcm_mask */
+ spinlock_t lock_recompute_pcm;
/* maintained by card drivers */
uint pcm_len; /* allocation length of PCM packet (dynamic) */
xpp_line_t wanted_pcm_mask;
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);
diff --git a/kernel/xpp/xpp_zap.h b/kernel/xpp/xpp_zap.h
index dd56657..8a4ffad 100644
--- a/kernel/xpp/xpp_zap.h
+++ b/kernel/xpp/xpp_zap.h
@@ -34,13 +34,17 @@ xpd_t *xpd_alloc(size_t privsize, const xproto_table_t *proto_table, int channel
void xpd_free(xpd_t *xpd);
void xpd_remove(xpd_t *xpd);
void update_xpd_status(xpd_t *xpd, int alarm_flag);
-void update_line_status(xpd_t *xpd, int pos, bool good);
+void hookstate_changed(xpd_t *xpd, int pos, bool good);
int xpp_open(struct zt_chan *chan);
int xpp_close(struct zt_chan *chan);
int xpp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long arg);
int xpp_maint(struct zt_span *span, int cmd);
void report_bad_ioctl(const char *msg, xpd_t *xpd, int pos, unsigned int cmd);
int total_registered_spans(void);
+void oht_pcm(xpd_t *xpd, int pos, bool pass);
+void mark_offhook(xpd_t *xpd, int pos, bool to_offhook);
+#define IS_OFFHOOK(xpd,pos) IS_SET((xpd)->offhook_state, (pos))
+void notify_rxsig(xpd_t *xpd, int pos, zt_rxsig_t rxsig);
#ifdef CONFIG_PROC_FS
#include <linux/proc_fs.h>
diff --git a/kernel/xpp/xproto.c b/kernel/xpp/xproto.c
index c3aa34e..ae6a9b2 100644
--- a/kernel/xpp/xproto.c
+++ b/kernel/xpp/xproto.c
@@ -435,6 +435,7 @@ int xproto_register(const xproto_table_t *proto_table)
CHECK_XOP(card_init);
CHECK_XOP(card_remove);
CHECK_XOP(card_tick);
+ CHECK_XOP(card_pcm_recompute);
CHECK_XOP(card_pcm_fromspan);
CHECK_XOP(card_pcm_tospan);
CHECK_XOP(card_zaptel_preregistration);
diff --git a/kernel/xpp/xproto.h b/kernel/xpp/xproto.h
index 4691094..cbf0726 100644
--- a/kernel/xpp/xproto.h
+++ b/kernel/xpp/xproto.h
@@ -221,7 +221,8 @@ struct xops {
int (*card_init)(xbus_t *xbus, xpd_t *xpd);
int (*card_remove)(xbus_t *xbus, xpd_t *xpd);
int (*card_tick)(xbus_t *xbus, xpd_t *xpd);
- void (*card_pcm_fromspan)(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, xpacket_t *pack);
+ void (*card_pcm_recompute)(xbus_t *xbus, xpd_t *xpd, xpp_line_t pcm_mask);
+ void (*card_pcm_fromspan)(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack);
void (*card_pcm_tospan)(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack);
int (*card_zaptel_preregistration)(xpd_t *xpd, bool on);
int (*card_zaptel_postregistration)(xpd_t *xpd, bool on);