From c88eaa22e13bd4c092b367a28e57064659660466 Mon Sep 17 00:00:00 2001 From: tzafrir Date: Thu, 2 Aug 2007 12:21:11 +0000 Subject: Merge xpp r4372: * Update to zaptel-1.2.18 and zaptel-1.4.3 (r4308 onward) * Fix a critical race with zaptel synchronization (r4362) * Added a /proc/xpp/cmds for statistics about command timing (r4360) * Fix a digit mapping bug with hardware dtmf detection (r4357) * In xpp/utils/Makefile add perl syntax checks to our scripts (r4337) * Better USB data error checking (r4336) * udev rules (xpp.rules) avoid false calls from wrong nodes (r4331) * Improve hardware detection and reporting in lszaptel, zaptel_hardware. zapconf is basically functional. * Leds are blinked synchronously on all Astribanks now (r4262) * Fix a BRI bug if OPTIMIZE_CHANMUTE was compiled into zaptel (r4258) (This feature was not yet accepted into official zaptel) * Removed compile warning about HZ != 1000 (r4218) * Firmware updates. * fpga_load now supports USB pathes without zeros (r4211) * XPD numbers have changed to '' (r4196) * Proper support for ZT_VMWI ioctl, if used in zaptel (r4092) * Fix FXO power denial detection (r4054) * FXO could accidentally go off-hook with some compilers (r4048) (From branches/1.2 r2732, r2735 - branches/1.4 2736) git-svn-id: http://svn.digium.com/svn/zaptel/trunk@2813 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- xpp/card_fxs.c | 364 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 283 insertions(+), 81 deletions(-) (limited to 'xpp/card_fxs.c') diff --git a/xpp/card_fxs.c b/xpp/card_fxs.c index a5d4654..8392e73 100644 --- a/xpp/card_fxs.c +++ b/xpp/card_fxs.c @@ -36,6 +36,14 @@ static const char rcsid[] = "$Id$"; DEF_PARM(int, print_dbg, 0, 0644, "Print DBG statements"); /* must be before zap_debug.h */ DEF_PARM(uint, poll_digital_inputs, 1000, 0644, "Poll Digital Inputs"); DEF_PARM_BOOL(reversepolarity, 0, 0644, "Reverse Line Polarity"); +DEF_PARM_BOOL(vmwineon, 0, 0644, "Indicate voicemail to a neon lamp"); +DEF_PARM_BOOL(dtmf_detection, 1, 0644, "Do DTMF detection in hardware"); + +#ifdef ZT_VMWI +DEF_PARM_BOOL(vmwi_ioctl, 0, 0644, "Asterisk support VMWI notification via ioctl"); +#else +#define vmwi_ioctl 0 /* not supported */ +#endif /* Signaling is opposite (fxo signalling for fxs card) */ #if 1 @@ -85,6 +93,9 @@ enum fxs_state { #define FXS_LINE_POL_ACTIVE ((reversepolarity) ? FXS_LINE_REV_ACTIVE : FXS_LINE_ACTIVE) #define FXS_LINE_POL_OHTRANS ((reversepolarity) ? FXS_LINE_REV_OHTRANS : FXS_LINE_OHTRANS) +#define DTMF_REGISTER 0x18 /* 24 */ +#define POLL_DTMF_INTERVAL 20 /* ticks */ + /*---------------- FXS Protocol Commands ----------------------------------*/ @@ -95,21 +106,31 @@ static /* 0x0F */ DECLARE_CMD(FXS, RELAY_OUT, byte which, bool on); static bool fxs_packet_is_valid(xpacket_t *pack); static void fxs_packet_dump(const char *msg, xpacket_t *pack); static int proc_fxs_info_read(char *page, char **start, off_t off, int count, int *eof, void *data); +#ifdef WITH_METERING +static int proc_xpd_metering_write(struct file *file, const char __user *buffer, unsigned long count, void *data); +#endif static int proc_xpd_register_read(char *page, char **start, off_t off, int count, int *eof, void *data); static int proc_xpd_register_write(struct file *file, const char __user *buffer, unsigned long count, void *data); static void start_stop_vm_led(xbus_t *xbus, xpd_t *xpd, lineno_t pos); #define PROC_REGISTER_FNAME "slics" #define PROC_FXS_INFO_FNAME "fxs_info" +#ifdef WITH_METERING +#define PROC_METERING_FNAME "metering_gen" +#endif struct FXS_priv_data { struct proc_dir_entry *regfile; +#ifdef WITH_METERING + struct proc_dir_entry *meteringfile; +#endif struct proc_dir_entry *fxs_info; xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */ xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */ xpp_line_t found_fsk_pattern; xpp_line_t msg_waiting; xpp_line_t update_offhook_state; + xpp_line_t dtmf_keypressed; int led_counter[NUM_LEDS][CHANNELS_PERXPD]; int ohttimer[CHANNELS_PERXPD]; #define OHT_TIMER 6000 /* How long after RING to retain OHT */ @@ -269,25 +290,38 @@ static void restore_leds(xpd_t *xpd) } } +#ifdef WITH_METERING +static int metering_gen(xpd_t *xpd, lineno_t chan, bool on) +{ + byte value = (on) ? 0x94 : 0x00; + + DBG("%s/%s/%d: METERING Generate: %s\n", + xpd->xbus->busname, xpd->xpdname, chan, + (on)?"ON":"OFF"); + return SLIC_DIRECT_REQUEST(xpd->xbus, xpd, chan, SLIC_WRITE, 0x23, value); +} +#endif + /*---------------- FXS: Methods -------------------------------------------*/ -static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision) +static xpd_t *FXS_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table, byte revision) { xpd_t *xpd = NULL; int channels = min(8, CHANNELS_PERXPD); - if(xpd_num == 0) + if(unit == 0) channels += 6; /* 2 DIGITAL OUTPUTS, 4 DIGITAL INPUTS */ - xpd = xpd_alloc(sizeof(struct FXS_priv_data), xbus, xpd_num, proto_table, channels, revision); + xpd = xpd_alloc(sizeof(struct FXS_priv_data), proto_table, channels); if(!xpd) return NULL; - if(xpd_num == 0) { + if(unit == 0) { DBG("First XPD on %s detected. Initialize digital outputs/inputs\n", xbus->busname); xpd->digital_outputs = MASK_DIGI_OUT; xpd->digital_inputs = MASK_DIGI_INP; } xpd->direction = TO_PHONE; xpd->revision = revision; + xpd->type_name = proto_table->name; return xpd; } @@ -304,6 +338,14 @@ static void clean_proc(xbus_t *xbus, xpd_t *xpd) remove_proc_entry(PROC_REGISTER_FNAME, xpd->proc_xpd_dir); priv->regfile = NULL; } +#ifdef WITH_METERING + if(priv->meteringfile) { + DBG("Removing xpd metering tone file %s/%s\n", xbus->busname, xpd->xpdname); + priv->meteringfile->data = NULL; + remove_proc_entry(PROC_METERING_FNAME, xpd->proc_xpd_dir); + priv->meteringfile = NULL; + } +#endif if(priv->fxs_info) { DBG("Removing xpd FXS_INFO file %s/%s\n", xbus->busname, xpd->xpdname); remove_proc_entry(PROC_FXS_INFO_FNAME, xpd->proc_xpd_dir); @@ -329,6 +371,19 @@ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) goto err; } priv->fxs_info->owner = THIS_MODULE; +#ifdef WITH_METERING + DBG("Creating Metering tone file for %s/%s\n", xbus->busname, xpd->xpdname); + priv->meteringfile = create_proc_entry(PROC_METERING_FNAME, 0200, xpd->proc_xpd_dir); + if(!priv->meteringfile) { + ERR("Failed to create proc file for SLICs of %s/%s\n", xbus->busname, xpd->xpdname); + ret = -ENOENT; + goto err; + } + priv->meteringfile->owner = THIS_MODULE; + priv->meteringfile->write_proc = proc_xpd_metering_write; + priv->meteringfile->read_proc = NULL; + priv->meteringfile->data = xpd; +#endif DBG("Creating SLICs file for %s/%s\n", xbus->busname, xpd->xpdname); priv->regfile = create_proc_entry(PROC_REGISTER_FNAME, 0644, xpd->proc_xpd_dir); if(!priv->regfile) { @@ -368,6 +423,7 @@ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) msleep(50); } restore_leds(xpd); + pcm_recompute(xpd); return 0; err: clean_proc(xbus, xpd); @@ -398,17 +454,19 @@ static int FXS_card_zaptel_preregistration(xpd_t *xpd, bool on) priv = xpd->priv; BUG_ON(!priv); DBG("%s/%s (%d)\n", xbus->busname, xpd->xpdname, on); - snprintf(xpd->span.desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: FXS", xbus->num, xpd->id); for_each_line(xpd, i) { struct zt_chan *cur_chan = &xpd->chans[i]; DBG("setting FXS channel %d\n", i); if(IS_SET(xpd->digital_outputs, i)) { - snprintf(cur_chan->name, MAX_CHANNAME, "XPP_OUT/%d/%d/%d", xbus->num, xpd->id, i); + snprintf(cur_chan->name, MAX_CHANNAME, "XPP_OUT/%02d/%1d%1d/%d", + xbus->num, xpd->addr.unit, xpd->addr.subunit, i); } else if(IS_SET(xpd->digital_inputs, i)) { - snprintf(cur_chan->name, MAX_CHANNAME, "XPP_IN/%d/%d/%d", xbus->num, xpd->id, i); + snprintf(cur_chan->name, MAX_CHANNAME, "XPP_IN/%02d/%1d%1d/%d", + xbus->num, xpd->addr.unit, xpd->addr.subunit, i); } else { - snprintf(cur_chan->name, MAX_CHANNAME, "XPP_FXS/%d/%d/%d", xbus->num, xpd->id, i); + snprintf(cur_chan->name, MAX_CHANNAME, "XPP_FXS/%02d/%1d%1d/%d", + xbus->num, xpd->addr.unit, xpd->addr.subunit, i); } cur_chan->chanpos = i + 1; cur_chan->pvt = xpd; @@ -463,6 +521,7 @@ int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) case ZT_TXSIG_ONHOOK: xpd->ringing[pos] = 0; BIT_CLR(xpd->cid_on, pos); + pcm_recompute(xpd); if(IS_SET(xpd->digital_outputs, pos)) { DBG("%s/%s/%d: digital output OFF\n", xbus->busname, xpd->xpdname, pos); ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 0); @@ -500,6 +559,7 @@ 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); txhook = FXS_LINE_OHTRANS; } xpd->ringing[pos] = 0; @@ -518,6 +578,7 @@ int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) case ZT_TXSIG_START: xpd->ringing[pos] = 1; BIT_CLR(xpd->cid_on, pos); + pcm_recompute(xpd); if(IS_SET(xpd->digital_outputs, pos)) { DBG("%s/%s/%d: %s digital output ON\n", xbus->busname, xpd->xpdname, pos, txsig2str(txsig)); ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1); @@ -537,16 +598,16 @@ int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) return ret; } -#ifdef VMWI_IOCTL /* * Private ioctl() * We don't need it now, since we detect vmwi via FSK patterns */ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long arg) { - struct FXS_priv_data *priv; - xbus_t *xbus; - + struct FXS_priv_data *priv; + xbus_t *xbus; + int val; + BUG_ON(!xpd); priv = xpd->priv; BUG_ON(!priv); @@ -559,19 +620,47 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a } switch (cmd) { - case _IOW(ZT_CODE, 60, int): /* message-waiting led control */ + case ZT_ONHOOKTRANSFER: + if (get_user(val, (int __user *)arg)) + return -EFAULT; + DBG("%s/%s/%d: ZT_ONHOOKTRANSFER (%d millis)\n", + xbus->busname, xpd->xpdname, pos, val); + BUG_ON(pos == ALL_CHANS); + if (IS_SET(xpd->digital_inputs | xpd->digital_outputs, pos)) + return 0; /* Nothing to do */ + if(!IS_SET(xpd->offhook, pos)) + start_stop_vm_led(xbus, xpd, pos); + return 0; + case ZT_TONEDETECT: + if (get_user(val, (int __user *)arg)) + return -EFAULT; + DBG("%s/%s/%d: ZT_TONEDETECT: %s %s (dtmf_detection=%s)\n", + xbus->busname, xpd->xpdname, pos, + (val & ZT_TONEDETECT_ON) ? "ON" : "OFF", + (val & ZT_TONEDETECT_MUTE) ? "MUTE" : "NO-MUTE", + (dtmf_detection ? "YES" : "NO")); + return (dtmf_detection) ? 0 : -ENOTTY; +#ifdef ZT_VMWI + case ZT_VMWI: /* message-waiting led control */ + if (get_user(val, (int __user *)arg)) + return -EFAULT; + if(!vmwi_ioctl) { + NOTICE("%s/%s/%d: Got ZT_VMWI notification but vmwi_ioctl parameter is off. Ignoring.\n", + xbus->busname, xpd->xpdname, pos); + return 0; + } /* Digital inputs/outputs don't have VM leds */ if (IS_SET(xpd->digital_inputs | xpd->digital_outputs, pos)) return 0; - if (arg) + if (val) BIT_SET(priv->msg_waiting, pos); else BIT_CLR(priv->msg_waiting, pos); return 0; +#endif } return -ENOTTY; } -#endif static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos, int on) { @@ -579,6 +668,9 @@ static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos, int on) BUG_ON(!xbus); BUG_ON(!xpd); + if (!vmwineon) + return 0; + if (on) { /* A write to register 0x40 will now turn on/off the VM led */ ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x16, 0xE8, 0x03); @@ -624,20 +716,6 @@ static void start_stop_vm_led(xbus_t *xbus, xpd_t *xpd, lineno_t pos) linefeed_control(xbus, xpd, pos, (on) ? FXS_LINE_RING : priv->idletxhookstate[pos]); } -static int FXS_chan_onhooktransfer(xbus_t *xbus, xpd_t *xpd, lineno_t chan, int millies) -{ - struct FXS_priv_data *priv; - int ret = 0; - - BUG_ON(!xpd); - priv = xpd->priv; - BUG_ON(chan == ALL_CHANS); - DBG("%s/%s/%d: (%d millies)\n", xbus->busname, xpd->xpdname, chan, millies); - if(!IS_SET(xpd->offhook, chan)) - start_stop_vm_led(xbus, xpd, chan); - return ret; -} - static int FXS_card_open(xpd_t *xpd, lineno_t chan) { struct FXS_priv_data *priv; @@ -679,15 +757,25 @@ static int FXS_card_close(xpd_t *xpd, lineno_t chan) */ static int input_channels[] = { 6, 7, 2, 3 }; // Slic numbers of input relays -static void poll_inputs(xbus_t *xbus, xpd_t *xpd) +static void poll_inputs(xpd_t *xpd) { int i; - BUG_ON(xpd->id != 0); // Only unit #0 has digital inputs + BUG_ON(xpd->xbus_idx != 0); // Only unit #0 has digital inputs for(i = 0; i < ARRAY_SIZE(input_channels); i++) { byte pos = input_channels[i]; - SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_READ, 0x06, 0); + SLIC_DIRECT_REQUEST(xpd->xbus, xpd, pos, SLIC_READ, 0x06, 0); + } +} + +static void poll_dtmf(xpd_t *xpd) +{ + int i; + + for_each_line(xpd, i) { + if(IS_SET(xpd->offhook, i)) + SLIC_DIRECT_REQUEST(xpd->xbus, xpd, i, SLIC_READ, DTMF_REGISTER, 0); } } @@ -713,6 +801,7 @@ void handle_linefeed(xpd_t *xpd) enum fxs_state txhook = FXS_LINE_POL_ACTIVE; /* Apply the change if appropriate */ BIT_CLR(xpd->cid_on, i); + pcm_recompute(xpd); linefeed_control(xpd->xbus, xpd, i, txhook); } } @@ -721,7 +810,20 @@ void handle_linefeed(xpd_t *xpd) } } -#ifndef VMWI_IOCTL +/* + * Optimized memcmp() like function. Only test for equality (true/false). + * This optimization reduced the detect_vmwi() runtime by a factor of 3. + */ +static inline bool mem_equal(const char a[], const char b[], size_t len) +{ + int i; + + for(i = 0; i < len; i++) + if(a[i] != b[i]) + return 0; + return 1; +} + /* * Detect Voice Mail Waiting Indication */ @@ -735,6 +837,8 @@ static void detect_vmwi(xpd_t *xpd) int i; BUG_ON(!xpd); + if (!vmwineon) + return; xbus = xpd->xbus; priv = xpd->priv; BUG_ON(!priv); @@ -754,15 +858,15 @@ static void detect_vmwi(xpd_t *xpd) printk("\n"); } #endif - if(unlikely(memcmp(writechunk, FSK_COMMON_PATTERN, ZT_CHUNKSIZE) == 0)) + if(unlikely(mem_equal(writechunk, FSK_COMMON_PATTERN, ZT_CHUNKSIZE))) BIT_SET(priv->found_fsk_pattern, i); else if(unlikely(IS_SET(priv->found_fsk_pattern, i))) { BIT_CLR(priv->found_fsk_pattern, i); - if(memcmp(writechunk, FSK_ON_PATTERN, ZT_CHUNKSIZE) == 0) { + if(unlikely(mem_equal(writechunk, FSK_ON_PATTERN, ZT_CHUNKSIZE))) { DBG("%s/%s/%d: MSG WAITING ON\n", xbus->busname, xpd->xpdname, i); BIT_SET(priv->msg_waiting, i); start_stop_vm_led(xbus, xpd, i); - } else if(memcmp(writechunk, FSK_OFF_PATTERN, ZT_CHUNKSIZE) == 0) { + } else if(unlikely(mem_equal(writechunk, FSK_OFF_PATTERN, ZT_CHUNKSIZE))) { DBG("%s/%s/%d: MSG WAITING OFF\n", xbus->busname, xpd->xpdname, i); BIT_CLR(priv->msg_waiting, i); start_stop_vm_led(xbus, xpd, i); @@ -778,7 +882,6 @@ static void detect_vmwi(xpd_t *xpd) } } } -#endif static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) { @@ -788,9 +891,9 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) priv = xpd->priv; BUG_ON(!priv); #if POLL_DIGITAL_INPUTS - if(poll_digital_inputs && xpd->id == 0) { + if(poll_digital_inputs && xpd->xbus_idx == 0) { if((xpd->timer_count % poll_digital_inputs) == 0) - poll_inputs(xbus, xpd); + poll_inputs(xpd); } #endif handle_fxs_leds(xpd); @@ -816,10 +919,13 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) BIT_CLR(priv->update_offhook_state, i); } } -#ifndef VMWI_IOCTL - if(SPAN_REGISTERED(xpd)) - detect_vmwi(xpd); -#endif + if(SPAN_REGISTERED(xpd)) { + if(!vmwi_ioctl) + detect_vmwi(xpd); + if(dtmf_detection && + (xpd->timer_count % POLL_DTMF_INTERVAL) == 0) + poll_dtmf(xpd); + } return 0; } @@ -836,7 +942,7 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) DBG("NO XBUS\n"); return -EINVAL; } - XFRAME_NEW(xframe, pack, xbus, GLOBAL, REGISTER_REQUEST, xpd->id); + XFRAME_NEW(xframe, pack, xbus, GLOBAL, REGISTER_REQUEST, xpd->xbus_idx); #if 0 DBG("%s/%s/%d: %c%c R%02X S%02X %02X %02X\n", xbus->busname, xpd->xpdname, chipsel, @@ -853,7 +959,7 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) REG_FIELD(reg_cmd, subreg) = subreg; REG_FIELD(reg_cmd, data_low) = data_low; REG_FIELD(reg_cmd, data_high) = data_high; - ret = xframe_send(xbus, xframe); + ret = send_cmd_frame(xbus, xframe); return ret; } @@ -943,8 +1049,12 @@ HANDLER_DEF(FXS, SIG_CHANGED) for_each_line(xpd, i) { if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i)) continue; + BIT_CLR(priv->dtmf_keypressed, i); /* no dtmf history */ if(IS_SET(sig_toggles, i)) { - xpd->ringing[i] = 0; // No more ringing... + xpd->ringing[i] = 0; /* No more ringing... */ +#ifdef WITH_METERING + metering_gen(xpd, i, 0); /* Stop metering... */ +#endif MARK_BLINK(priv, i, LED_GREEN, 0); if(IS_SET(sig_status, i)) { DBG("%s/%s/%d: OFFHOOK\n", xbus->busname, xpd->xpdname, i); @@ -958,9 +1068,80 @@ HANDLER_DEF(FXS, SIG_CHANGED) } } spin_unlock_irqrestore(&xpd->lock, flags); + pcm_recompute(xpd); /* it's spinlocked */ return 0; } +static void process_digital_inputs(xpd_t *xpd, const reg_cmd_t *info) +{ + int i; + bool offhook = (REG_FIELD(info, data_low) & 0x1) == 0; + xpp_line_t lines = BIT(REG_FIELD(info, chipsel)); + + /* Map SLIC number into line number */ + for(i = 0; i < ARRAY_SIZE(input_channels); i++) { + int channo = input_channels[i]; + int newchanno; + + if(IS_SET(lines, channo)) { + newchanno = LINES_REGULAR + LINES_DIGI_OUT + i; + 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 + DBG("%s/%s/%d: OFFHOOK\n", xpd->xbus->busname, xpd->xpdname, newchanno); + update_line_status(xpd, newchanno, 1); + } else if(!offhook && IS_SET(xpd->offhook, newchanno)) { // ONHOOK + DBG("%s/%s/%d: ONHOOK\n", xpd->xbus->busname, xpd->xpdname, newchanno); + update_line_status(xpd, newchanno, 0); + } + } + } +} + +static const char dtmf_digits[] = { + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '*', '#', 'A', 'B', 'C', 'D' +}; + +void process_dtmf(xpd_t *xpd, const reg_cmd_t *info) +{ + int i; + byte val = REG_FIELD(info, data_low); + xpp_line_t lines = BIT(REG_FIELD(info, chipsel)); + byte digit; + bool on = val & 0x10; + struct FXS_priv_data *priv; + + priv = xpd->priv; + val &= 0xF; + if(val <= 0) { + if(on) + NOTICE("%s/%s: Bad DTMF value %d. Ignored\n", + xpd->xbus->busname, xpd->xpdname, val); + return; + } + val--; + digit = dtmf_digits[val]; + for_each_line(xpd, i) { + if(IS_SET(lines, i)) { + if(on && !IS_SET(priv->dtmf_keypressed, i)) { + DBG("%s/%s/%d: DTMF digit %2d PRESSED (%d)\n", + xpd->xbus->busname, xpd->xpdname, i, digit, val); + BIT_SET(priv->dtmf_keypressed, i); + if(dtmf_detection) + zt_qevent_lock(&xpd->chans[i], ZT_EVENT_DTMFDOWN | digit); + } else if(!on && IS_SET(priv->dtmf_keypressed, i)) { + DBG("%s/%s/%d: DTMF digit %2d RELEASED\n", + xpd->xbus->busname, xpd->xpdname, i, digit); + BIT_CLR(priv->dtmf_keypressed, i); + if(dtmf_detection) + zt_qevent_lock(&xpd->chans[i], ZT_EVENT_DTMFUP | digit); + } + break; + } + } +} + HANDLER_DEF(FXS, REGISTER_REPLY) { reg_cmd_t *info = &RPACKET_FIELD(pack, FXS, REGISTER_REPLY, reg_cmd); @@ -970,8 +1151,7 @@ HANDLER_DEF(FXS, REGISTER_REPLY) bool indirect; if(!xpd) { - NOTICE("%s: received %s for non-existing xpd: %d\n", - __FUNCTION__, cmd->name, XPD_NUM(pack->addr)); + notify_bad_xpd(__FUNCTION__, xbus, pack->addr, cmd->name); return -EPROTO; } spin_lock_irqsave(&xpd->lock, flags); @@ -981,7 +1161,7 @@ HANDLER_DEF(FXS, REGISTER_REPLY) regnum = (indirect) ? REG_FIELD(info, subreg) : REG_FIELD(info, regnum); #if 0 DBG("REGISTER_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n", - xpd->id, (indirect)?"I":"D", + xpd->xbus_idx, (indirect)?"I":"D", regnum, REG_FIELD(info, data_low), REG_FIELD(info, data_high)); #endif if(!SPAN_REGISTERED(xpd)) @@ -989,31 +1169,10 @@ HANDLER_DEF(FXS, REGISTER_REPLY) /* * Process digital inputs polling results */ - if(xpd->id == 0 && regnum == 0x06) { - int i; - bool offhook = (REG_FIELD(info, data_low) & 0x1) == 0; - xpp_line_t lines = BIT(REG_FIELD(info, chipsel)); - - /* Map SLIC number into line number */ - for(i = 0; i < ARRAY_SIZE(input_channels); i++) { - int channo = input_channels[i]; - int newchanno; - - if(IS_SET(lines, channo)) { - newchanno = LINES_REGULAR + LINES_DIGI_OUT + i; - 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 - DBG("%s/%s/%d: OFFHOOK\n", xbus->busname, xpd->xpdname, newchanno); - update_line_status(xpd, newchanno, 1); - } else if(!offhook && IS_SET(xpd->offhook, newchanno)) { // ONHOOK - DBG("%s/%s/%d: ONHOOK\n", xbus->busname, xpd->xpdname, newchanno); - update_line_status(xpd, newchanno, 0); - } - } - } - } + if(xpd->xbus_idx == 0 && !indirect && regnum == 0x06) + process_digital_inputs(xpd, info); + if(!indirect && regnum == DTMF_REGISTER) + process_dtmf(xpd, info); out: /* Update /proc info only if reply relate to the last slic read request */ if( @@ -1043,12 +1202,11 @@ xproto_table_t PROTO_TABLE(FXS) = { .card_zaptel_postregistration = FXS_card_zaptel_postregistration, .card_hooksig = FXS_card_hooksig, .card_tick = FXS_card_tick, - .chan_onhooktransfer = FXS_chan_onhooktransfer, + .card_pcm_fromspan = generic_card_pcm_fromspan, + .card_pcm_tospan = generic_card_pcm_tospan, .card_open = FXS_card_open, .card_close = FXS_card_close, -#ifdef VMWI_IOCTL .card_ioctl = FXS_card_ioctl, -#endif .RING = XPROTO_CALLER(FXS, RING), .RELAY_OUT = XPROTO_CALLER(FXS, RELAY_OUT), @@ -1249,7 +1407,7 @@ static int handle_register_command(xpd_t *xpd, char *cmdline) } xpd->requested_reply = regcmd; if(print_dbg) - dump_reg_cmd("FXS", ®cmd); + dump_reg_cmd("FXS", ®cmd, 1); ret = CALL_PROTO(FXS, REGISTER_REQUEST, xpd->xbus, xpd, REG_FIELD(®cmd, chipsel), writing, @@ -1328,6 +1486,45 @@ static int proc_xpd_register_read(char *page, char **start, off_t off, int count return len; } +#ifdef WITH_METERING +static int proc_xpd_metering_write(struct file *file, const char __user *buffer, unsigned long count, void *data) +{ + xpd_t *xpd = data; + char buf[MAX_PROC_WRITE]; + lineno_t chan; + int num; + int ret; + + if(!xpd) + return -ENODEV; + if(count >= MAX_PROC_WRITE - 1) { + ERR("%s/%s: Metering string too long (%lu)\n", + xpd->xbus->busname, xpd->xpdname, count); + return -EINVAL; + } + if(copy_from_user(&buf, buffer, count)) + return -EFAULT; + buf[count] = '\0'; + ret = sscanf(buf, "%d", &num); + if(ret != 1) { + ERR("%s/%s: Metering value should be number. Got '%s'\n", + xpd->xbus->busname, xpd->xpdname, buf); + return -EINVAL; + } + chan = num; + if(chan != ALL_CHANS && chan > xpd->channels) { + ERR("%s/%s: Metering tone: bad channel number %d\n", + xpd->xbus->busname, xpd->xpdname, chan); + return -EINVAL; + } + if((ret = metering_gen(xpd, chan, 1)) < 0) { + ERR("%s/%s: Failed sending metering tone\n", + xpd->xbus->busname, xpd->xpdname); + return ret; + } + return count; +} +#endif int __init card_fxs_startup(void) { @@ -1338,10 +1535,15 @@ int __init card_fxs_startup(void) #else INFO("FEATURE: %s without DIGITAL INPUTS support\n", THIS_MODULE->name); #endif -#ifdef VMWI_IOCTL - INFO("FEATURE: %s VMWI_IOCTL\n", THIS_MODULE->name); +#ifdef ZT_VMWI + INFO("FEATURE: %s ZT_VMWI\n", THIS_MODULE->name); +#else + INFO("FEATURE: %s NO ZT_VMWI\n", THIS_MODULE->name); +#endif +#ifdef WITH_METERING + INFO("FEATURE: %s WITH METERING Generation\n", THIS_MODULE->name); #else - INFO("FEATURE: %s NO VMWI_IOCTL\n", THIS_MODULE->name); + INFO("FEATURE: %s NO METERING Generation\n", THIS_MODULE->name); #endif xproto_register(&PROTO_TABLE(FXS)); return 0; -- cgit v1.2.3