diff options
Diffstat (limited to 'xpp/card_fxs.c')
-rw-r--r-- | xpp/card_fxs.c | 183 |
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, ®_type, ®_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(®cmd, 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(®cmd, data_low) = data_low; REG_FIELD(®cmd, data_high) = data_high; REG_FIELD(®cmd, 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", ®cmd, 1); @@ -1389,7 +1475,8 @@ static int handle_register_command(xpd_t *xpd, char *cmdline) REG_FIELD(®cmd, subreg), REG_FIELD(®cmd, data_low), REG_FIELD(®cmd, data_high)); - up_read(&xbus->in_use); +out: + XBUS_PUT(xbus); return ret; } |