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.c364
1 files changed, 283 insertions, 81 deletions
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", &regcmd);
+ dump_reg_cmd("FXS", &regcmd, 1);
ret = CALL_PROTO(FXS, REGISTER_REQUEST, xpd->xbus, xpd,
REG_FIELD(&regcmd, 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;