summaryrefslogtreecommitdiff
path: root/xpp/card_fxs.c
diff options
context:
space:
mode:
Diffstat (limited to 'xpp/card_fxs.c')
-rw-r--r--xpp/card_fxs.c183
1 files changed, 135 insertions, 48 deletions
diff --git a/xpp/card_fxs.c b/xpp/card_fxs.c
index e7a4474..f2251e0 100644
--- a/xpp/card_fxs.c
+++ b/xpp/card_fxs.c
@@ -91,7 +91,8 @@ enum fxs_state {
/*
* DTMF detection
*/
-#define DTMF_REGISTER 0x18 /* 24 */
+#define SLIC_REG_DTMF 0x18 /* 24 */
+#define SLIC_REG_VOLTAGE 0x42 /* 66 */
/*---------------- FXS Protocol Commands ----------------------------------*/
@@ -126,6 +127,8 @@ struct FXS_priv_data {
xpp_line_t search_fsk_pattern;
xpp_line_t found_fsk_pattern;
xpp_line_t update_offhook_state;
+ xpp_line_t want_dtmf_events; /* what zaptel want */
+ xpp_line_t want_dtmf_mute; /* what zaptel want */
int led_counter[NUM_LEDS][CHANNELS_PERXPD];
int ohttimer[CHANNELS_PERXPD];
#define OHT_TIMER 6000 /* How long after RING to retain OHT */
@@ -163,7 +166,7 @@ static int do_chan_power(xbus_t *xbus, xpd_t *xpd, lineno_t chan, bool on)
BUG_ON(!xbus);
BUG_ON(!xpd);
LINE_DBG(SIGNAL, xpd, chan, "%s\n", (on) ? "up" : "down");
- return SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, 0x42, value);
+ return SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, SLIC_REG_VOLTAGE, value);
}
/*
@@ -421,7 +424,7 @@ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd)
msleep(50);
}
restore_leds(xpd);
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on);
+ pcm_recompute(xpd, 0);
return 0;
err:
clean_proc(xbus, xpd);
@@ -499,12 +502,25 @@ static int FXS_card_zaptel_postregistration(xpd_t *xpd, bool on)
return 0;
}
+/*
+ * Called with XPD spinlocked
+ */
+static void __do_mute_dtmf(xpd_t *xpd, int pos, bool muteit)
+{
+ LINE_DBG(SIGNAL, xpd, pos, "%s\n", (muteit) ? "MUTE" : "UNMUTE");
+ if(muteit)
+ BIT_SET(xpd->mute_dtmf, pos);
+ else
+ BIT_CLR(xpd->mute_dtmf, pos);
+}
+
static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
{
struct FXS_priv_data *priv;
int ret = 0;
struct zt_chan *chan = NULL;
enum fxs_state txhook;
+ unsigned long flags;
LINE_DBG(SIGNAL, xpd, pos, "%s\n", txsig2str(txsig));
priv = xpd->priv;
@@ -517,10 +533,15 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
chan = &xpd->span.chans[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);
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on);
+ 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, "digital output OFF\n");
ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0);
@@ -557,7 +578,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, xpd->offhook | xpd->cid_on);
+ pcm_recompute(xpd, 0);
txhook = FXS_LINE_OHTRANS;
}
xpd->ringing[pos] = 0;
@@ -577,7 +598,7 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig)
xpd->ringing[pos] = 1;
BIT_CLR(xpd->cid_on, pos);
BIT_CLR(priv->search_fsk_pattern, pos);
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on);
+ pcm_recompute(xpd, 0);
if(IS_SET(xpd->digital_outputs, pos)) {
LINE_DBG(SIGNAL, xpd, pos, "%s digital output ON\n", txsig2str(txsig));
ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1);
@@ -607,13 +628,14 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
struct FXS_priv_data *priv;
xbus_t *xbus;
int val;
+ unsigned long flags;
BUG_ON(!xpd);
priv = xpd->priv;
BUG_ON(!priv);
xbus = xpd->xbus;
BUG_ON(!xbus);
- if(!xbus->hardware_exists)
+ if(!TRANSPORT_RUNNING(xbus))
return -ENODEV;
if (pos < 0 || pos >= xpd->channels) {
XPD_NOTICE(xpd, "Bad channel number %d in %s(), cmd=%u\n",
@@ -634,7 +656,7 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
priv->ohttimer[pos] = OHT_TIMER;
priv->idletxhookstate[pos] = FXS_LINE_POL_OHTRANS;
BIT_SET(priv->search_fsk_pattern, pos);
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on | priv->search_fsk_pattern);
+ pcm_recompute(xpd, priv->search_fsk_pattern);
}
if(!IS_SET(xpd->offhook, pos))
start_stop_vm_led(xbus, xpd, pos);
@@ -643,24 +665,61 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
if (get_user(val, (int __user *)arg))
return -EFAULT;
LINE_DBG(SIGNAL, xpd, pos, "ZT_TONEDETECT: %s %s (dtmf_detection=%s)\n",
- (val & ZT_TONEDETECT_ON) ? "ON" : "OFF",
- (val & ZT_TONEDETECT_MUTE) ? "MUTE" : "NO-MUTE",
- (dtmf_detection ? "YES" : "NO"));
+ (val & ZT_TONEDETECT_ON) ? "ON" : "OFF",
+ (val & ZT_TONEDETECT_MUTE) ? "MUTE" : "NO-MUTE",
+ (dtmf_detection ? "YES" : "NO"));
if(!dtmf_detection) {
- SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x17, 0);
+ spin_lock_irqsave(&xpd->lock, flags);
+ if(IS_SET(priv->want_dtmf_events, pos)) {
+ /* Detection mode changed: Disable DTMF interrupts */
+ SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x17, 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);
return -ENOTTY;
+ }
+ /*
+ * During natively bridged calls, Asterisk
+ * will request one of the sides to stop sending
+ * dtmf events. Check the requested state.
+ */
+ spin_lock_irqsave(&xpd->lock, flags);
+ if(val & ZT_TONEDETECT_ON) {
+ if(!IS_SET(priv->want_dtmf_events, pos)) {
+ /* Detection mode changed: Enable DTMF interrupts */
+ LINE_DBG(SIGNAL, xpd, pos,
+ "ZT_TONEDETECT: Enable Hardware DTMF\n");
+ SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x17, 1);
+ }
+ BIT_SET(priv->want_dtmf_events, pos);
} else {
- /* Enable DTMF interrupts (XPD will notify when DTMF will be detected) */
- SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x17, 1);
- return 0;
+ if(IS_SET(priv->want_dtmf_events, pos)) {
+ /* Detection mode changed: Disable DTMF interrupts */
+ LINE_DBG(SIGNAL, xpd, pos,
+ "ZT_TONEDETECT: Disable Hardware DTMF\n");
+ SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x17, 0);
+ }
+ BIT_CLR(priv->want_dtmf_events, pos);
}
+ if(val & ZT_TONEDETECT_MUTE) {
+ BIT_SET(priv->want_dtmf_mute, pos);
+ } 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;
case ZT_SETPOLARITY:
if (get_user(val, (int __user *)arg))
return -EFAULT;
/* Can't change polarity while ringing or when open */
if (priv->lasttxhook[pos] == FXS_LINE_RING || priv->lasttxhook[pos] == FXS_LINE_OPEN) {
LINE_ERR(xpd, pos, "ZT_SETPOLARITY: %s Cannot change when lasttxhook=0x%X\n",
- (val)?"ON":"OFF", priv->lasttxhook[pos]);
+ (val)?"ON":"OFF", priv->lasttxhook[pos]);
return -EINVAL;
}
LINE_DBG(SIGNAL, xpd, pos, "ZT_SETPOLARITY: %s\n", (val)?"ON":"OFF");
@@ -687,6 +746,8 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a
BIT_CLR(xpd->msg_waiting, pos);
return 0;
#endif
+ default:
+ report_bad_ioctl(THIS_MODULE->name, xpd, pos, cmd);
}
return -ENOTTY;
}
@@ -820,7 +881,7 @@ static void handle_linefeed(xpd_t *xpd)
/* Apply the change if appropriate */
BIT_CLR(xpd->cid_on, i);
BIT_CLR(priv->search_fsk_pattern, i);
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on);
+ pcm_recompute(xpd, 0);
linefeed_control(xpd->xbus, xpd, i, txhook);
}
}
@@ -1050,8 +1111,8 @@ HANDLER_DEF(FXS, SIG_CHANGED)
}
}
}
+ __pcm_recompute(xpd, 0); /* in a spinlock */
spin_unlock_irqrestore(&xpd->lock, flags);
- pcm_recompute(xpd, xpd->offhook | xpd->cid_on); /* it's spinlocked */
return 0;
}
@@ -1086,15 +1147,18 @@ static const char dtmf_digits[] = {
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '*', '#', 'A', 'B', 'C', 'D'
};
-static void process_dtmf(xpd_t *xpd, const reg_cmd_t *info)
+/*
+ * This function is called with spinlocked XPD
+ */
+static void process_dtmf(xpd_t *xpd, xpp_line_t lines, byte val)
{
- int i;
- byte val = REG_FIELD(info, data_low);
- xpp_line_t lines = BIT(REG_FIELD(info, chipsel));
- byte digit;
- bool is_down = val & 0x10;
+ int i;
+ byte digit;
+ bool is_down = val & 0x10;
struct FXS_priv_data *priv;
+ if(!dtmf_detection)
+ return;
priv = xpd->priv;
val &= 0xF;
if(val <= 0) {
@@ -1106,17 +1170,32 @@ static void process_dtmf(xpd_t *xpd, const reg_cmd_t *info)
digit = dtmf_digits[val];
for_each_line(xpd, i) {
if(IS_SET(lines, i)) {
- if(dtmf_detection) {
- LINE_DBG(SIGNAL, xpd, i, "DTMF digit %s (val=%d) '%c'\n",
- (is_down)?"DOWN":"UP", val, digit);
- if(is_down) {
- BIT_SET(xpd->mute_dtmf, i);
- zt_qevent_lock(&xpd->chans[i], ZT_EVENT_DTMFDOWN | digit);
- } else {
- zt_qevent_lock(&xpd->chans[i], ZT_EVENT_DTMFUP | digit);
- BIT_CLR(xpd->mute_dtmf, i);
- }
+ int event = (is_down) ? ZT_EVENT_DTMFDOWN : ZT_EVENT_DTMFUP;
+ bool want_mute = IS_SET(priv->want_dtmf_mute, i);
+ bool want_event = IS_SET(priv->want_dtmf_events, i);
+
+ if(want_event) {
+ LINE_DBG(SIGNAL, xpd, i,
+ "DTMF digit %s (val=%d) '%c' (want_mute=%s)\n",
+ (is_down)?"DOWN":"UP", val, digit,
+ (want_mute) ? "yes" : "no");
+ } else {
+ LINE_DBG(SIGNAL, xpd, i,
+ "Ignored DTMF digit %s '%c'\n",
+ (is_down)?"DOWN":"UP", digit);
}
+ /*
+ * FIXME: we currently don't use the want_dtmf_mute until
+ * we are sure about the logic in Asterisk native bridging.
+ * Meanwhile, simply mute it on button press.
+ */
+ if(is_down && want_mute)
+ __do_mute_dtmf(xpd, i, 1);
+ else
+ __do_mute_dtmf(xpd, i, 0);
+ __pcm_recompute(xpd, 0); /* XPD is locked */
+ if(want_event)
+ zt_qevent_lock(&xpd->chans[i], event | digit);
break;
}
}
@@ -1144,8 +1223,16 @@ static int FXS_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info)
*/
if(xpd->xbus_idx == 0 && !indirect && regnum == 0x06)
process_digital_inputs(xpd, info);
- if(!indirect && regnum == DTMF_REGISTER)
- process_dtmf(xpd, info);
+ if(!indirect && regnum == SLIC_REG_DTMF) {
+ byte val = REG_FIELD(info, data_low);
+ xpp_line_t lines = BIT(REG_FIELD(info, chipsel));
+
+#if 0
+ XPD_DBG(SIGNAL, xpd, "DTMF result lines=0x%04X val=%d\n",
+ lines, val);
+#endif
+ process_dtmf(xpd, lines, val);
+ }
out:
/* Update /proc info only if reply relate to the last slic read request */
if(
@@ -1305,7 +1392,7 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
char *p;
reg_cmd_t regcmd;
xbus_t *xbus;
- int ret;
+ int ret = -EINVAL;
BUG_ON(!xpd);
xbus = xpd->xbus;
@@ -1317,7 +1404,10 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
;
if(*p == '\0')
return 0;
-
+ if(!XBUS_GET(xbus)) {
+ XBUS_DBG(GENERAL, xbus, "Dropped packet. Is shutting down.\n");
+ return -EBUSY;
+ }
elements = sscanf(cmdline, "%d %c%c %x %x %x",
&chipsel,
&op, &reg_type, &reg_num,
@@ -1326,11 +1416,11 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
XPD_DBG(REGS, xpd, "'%s': %d %c%c %02X %02X %02X\n", cmdline, chipsel, op, reg_type, reg_num, data_low, data_high);
if(elements < 4) { // At least: chipsel, op, reg_type, reg_num
ERR("Not enough arguments: (%d args) '%s'\n", elements, cmdline);
- return -EINVAL;
+ goto out;
}
if(!VALID_CHIPSEL(chipsel)) {
ERR("Bad chipsel number: %d\n", chipsel);
- return -EINVAL;
+ goto out;
}
REG_FIELD(&regcmd, chipsel) = chipsel;
switch(op) {
@@ -1342,7 +1432,7 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
break;
default:
ERR("Unkown operation type '%c'\n", op);
- return -EINVAL;
+ goto out;
}
switch(reg_type) {
case 'I':
@@ -1357,7 +1447,7 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
break;
default:
ERR("Unkown register type '%c'\n", reg_type);
- return -EINVAL;
+ goto out;
}
if(
(op == 'W' && reg_type == 'D' && elements != 5) ||
@@ -1368,16 +1458,12 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
ERR("%s: '%s' (%d elements): %d %c%c %02X %02X %02X\n", __FUNCTION__,
cmdline, elements,
chipsel, op, reg_type, reg_num, data_low, data_high);
- return -EINVAL;
+ goto out;
}
regcmd.bytes = sizeof(regcmd) - 1;
REG_FIELD(&regcmd, data_low) = data_low;
REG_FIELD(&regcmd, data_high) = data_high;
REG_FIELD(&regcmd, read_request) = writing;
- if(!down_read_trylock(&xbus->in_use)) {
- XBUS_DBG(GENERAL, xbus, "Dropped packet. Is in_use\n");
- return -EBUSY;
- }
xpd->requested_reply = regcmd;
if(print_dbg)
dump_reg_cmd("FXS", &regcmd, 1);
@@ -1389,7 +1475,8 @@ static int handle_register_command(xpd_t *xpd, char *cmdline)
REG_FIELD(&regcmd, subreg),
REG_FIELD(&regcmd, data_low),
REG_FIELD(&regcmd, data_high));
- up_read(&xbus->in_use);
+out:
+ XBUS_PUT(xbus);
return ret;
}