diff options
Diffstat (limited to 'xpp/card_fxs.c')
-rw-r--r-- | xpp/card_fxs.c | 707 |
1 files changed, 282 insertions, 425 deletions
diff --git a/xpp/card_fxs.c b/xpp/card_fxs.c index c6168c6..eeb02c9 100644 --- a/xpp/card_fxs.c +++ b/xpp/card_fxs.c @@ -33,16 +33,16 @@ static const char rcsid[] = "$Id$"; -DEF_PARM(int, print_dbg, 0, 0644, "Print DBG statements"); /* must be before zap_debug.h */ -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"); +static DEF_PARM(int, debug, 0, 0644, "Print DBG statements"); /* must be before zap_debug.h */ +static DEF_PARM_BOOL(reversepolarity, 0, 0644, "Reverse Line Polarity"); +static DEF_PARM_BOOL(vmwineon, 0, 0644, "Indicate voicemail to a neon lamp"); +static DEF_PARM_BOOL(dtmf_detection, 1, 0644, "Do DTMF detection in hardware"); #ifdef POLL_DIGITAL_INPUTS -DEF_PARM(uint, poll_digital_inputs, 1000, 0644, "Poll Digital Inputs"); +static DEF_PARM(uint, poll_digital_inputs, 1000, 0644, "Poll Digital Inputs"); #endif #ifdef ZT_VMWI -DEF_PARM_BOOL(vmwi_ioctl, 0, 0644, "Asterisk support VMWI notification via ioctl"); +static DEF_PARM_BOOL(vmwi_ioctl, 0, 0644, "Asterisk support VMWI notification via ioctl"); #else #define vmwi_ioctl 0 /* not supported */ #endif @@ -68,12 +68,14 @@ enum fxs_leds { /* Shortcuts */ #define SLIC_WRITE 1 #define SLIC_READ 0 -#define SLIC_DIRECT_REQUEST(xbus,xpd,chipsel,writing,reg,dL) \ - xpp_register_request((xbus), (xpd), (chipsel), (writing), 0, (reg), 0, (dL), 0) -#define SLIC_INDIRECT_REQUEST(xbus,xpd,chipsel,writing,reg,dL,dH) \ - xpp_register_request((xbus), (xpd), (chipsel), (writing), 1, 0x1E, (reg), (dL), (dH)) +#define SLIC_DIRECT_REQUEST(xbus,xpd,port,writing,reg,dL) \ + xpp_register_request((xbus), (xpd), (port), (writing), (reg), 0, 0, (dL), 0, 0, 0) +#define SLIC_INDIRECT_REQUEST(xbus,xpd,port,writing,reg,dL,dH) \ + xpp_register_request((xbus), (xpd), (port), (writing), 0x1E, 1, (reg), (dL), 1, (dH), 0) -#define VALID_CHIPSEL(x) (((chipsel) >= 0 && (chipsel) <= 7) || (chipsel) == ALL_CHANS) +#define VALID_PORT(port) (((port) >= 0 && (port) <= 7) || (port) == PORT_BROADCAST) + +#define REG_DIGITAL_IOCTRL 0x06 /* LED and RELAY control */ /* Values of SLIC linefeed control register (0x40) */ enum fxs_state { @@ -93,14 +95,16 @@ enum fxs_state { /* * DTMF detection */ -#define SLIC_REG_DTMF 0x18 /* 24 */ -#define SLIC_REG_VOLTAGE 0x42 /* 66 */ +#define REG_DTMF_DECODE 0x18 /* 24 - DTMF Decode Status */ +#define REG_BATTERY 0x42 /* 66 - Battery Feed Control */ +#define REG_BATTERY_BATSL BIT(1) /* Battery Feed Select */ + +#define REG_LOOPCLOSURE 0x44 /* 68 - Loop Closure/Ring Trip Detect Status */ +#define REG_LOOPCLOSURE_LCR BIT(0) /* Loop Closure Detect Indicator. */ /*---------------- FXS Protocol Commands ----------------------------------*/ static /* 0x0F */ DECLARE_CMD(FXS, XPD_STATE, bool on); -static /* 0x0F */ DECLARE_CMD(FXS, RING, lineno_t chan, bool on); -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); @@ -108,8 +112,6 @@ static int proc_fxs_info_read(char *page, char **start, off_t off, int count, in #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" @@ -119,7 +121,6 @@ static void start_stop_vm_led(xbus_t *xbus, xpd_t *xpd, lineno_t pos); #endif struct FXS_priv_data { - struct proc_dir_entry *regfile; #ifdef WITH_METERING struct proc_dir_entry *meteringfile; #endif @@ -163,12 +164,12 @@ static int linefeed_control(xbus_t *xbus, xpd_t *xpd, lineno_t chan, enum fxs_st static int do_chan_power(xbus_t *xbus, xpd_t *xpd, lineno_t chan, bool on) { - int value = (on) ? 0x06 : 0x00; + int value = (on) ? REG_BATTERY_BATSL : 0x00; 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, SLIC_REG_VOLTAGE, value); + return SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, REG_BATTERY, value); } /* @@ -217,7 +218,7 @@ static int do_led(xpd_t *xpd, lineno_t chan, byte which, bool on) which = which % NUM_LEDS; if(IS_SET(xpd->digital_outputs, chan) || IS_SET(xpd->digital_inputs, chan)) goto out; - if(chan == ALL_CHANS) { + if(chan == PORT_BROADCAST) { priv->ledstate[which] = (on) ? ~0 : 0; } else { if(on) { @@ -231,7 +232,8 @@ static int do_led(xpd_t *xpd, lineno_t chan, byte which, bool on) value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[which]); if(on) value |= led_register_vals[which]; - ret = SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, 0x06, value); + ret = SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, + REG_DIGITAL_IOCTRL, value); out: return ret; } @@ -240,7 +242,7 @@ static void handle_fxs_leds(xpd_t *xpd) { int i; const enum fxs_leds colors[] = { LED_GREEN, LED_RED }; - int color; + enum fxs_leds color; unsigned int timer_count; struct FXS_priv_data *priv; @@ -251,7 +253,7 @@ static void handle_fxs_leds(xpd_t *xpd) for_each_line(xpd, i) { if(IS_SET(xpd->digital_outputs | xpd->digital_inputs, i)) continue; - if(xpd->blink_mode || IS_BLINKING(priv, i, color)) { // Blinking + if((xpd->blink_mode & BIT(i)) || IS_BLINKING(priv, i, color)) { // Blinking int mod_value = LED_COUNTER(priv, i, color); if(!mod_value) @@ -301,46 +303,13 @@ static int metering_gen(xpd_t *xpd, lineno_t chan, bool on) /*---------------- FXS: Methods -------------------------------------------*/ -static xpd_t *FXS_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table, byte subtype, byte revision) -{ - xpd_t *xpd = NULL; - int channels; - int regular_channels; - - if(subtype == 2) - regular_channels = min(6, CHANNELS_PERXPD); - else - regular_channels = min(8, CHANNELS_PERXPD); - channels = regular_channels; - if(unit == 0) - channels += 6; /* 2 DIGITAL OUTPUTS, 4 DIGITAL INPUTS */ - xpd = xpd_alloc(sizeof(struct FXS_priv_data), proto_table, channels); - if(!xpd) - return NULL; - if(unit == 0) { - XBUS_DBG(GENERAL, xbus, "First XPD detected. Initialize digital outputs/inputs\n"); - xpd->digital_outputs = BITMASK(LINES_DIGI_OUT) << regular_channels; - xpd->digital_inputs = BITMASK(LINES_DIGI_INP) << (regular_channels + LINES_DIGI_OUT); - } - xpd->direction = TO_PHONE; - xpd->revision = revision; - xpd->type_name = proto_table->name; - return xpd; -} - -static void clean_proc(xbus_t *xbus, xpd_t *xpd) +static void fxs_proc_remove(xbus_t *xbus, xpd_t *xpd) { struct FXS_priv_data *priv; BUG_ON(!xpd); priv = xpd->priv; #ifdef CONFIG_PROC_FS - if(priv->regfile) { - XPD_DBG(PROC, xpd, "Removing xpd SLIC file\n"); - priv->regfile->data = NULL; - remove_proc_entry(PROC_REGISTER_FNAME, xpd->proc_xpd_dir); - priv->regfile = NULL; - } #ifdef WITH_METERING if(priv->meteringfile) { XPD_DBG(PROC, xpd, "Removing xpd metering tone file\n"); @@ -357,20 +326,18 @@ static void clean_proc(xbus_t *xbus, xpd_t *xpd) #endif } -static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) +static int fxs_proc_create(xbus_t *xbus, xpd_t *xpd) { struct FXS_priv_data *priv; - int ret = 0; - int i; BUG_ON(!xpd); priv = xpd->priv; + #ifdef CONFIG_PROC_FS XPD_DBG(PROC, xpd, "Creating FXS_INFO file\n"); priv->fxs_info = create_proc_read_entry(PROC_FXS_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_fxs_info_read, xpd); if(!priv->fxs_info) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_FXS_INFO_FNAME); - ret = -ENOENT; goto err; } priv->fxs_info->owner = THIS_MODULE; @@ -379,7 +346,6 @@ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) priv->meteringfile = create_proc_entry(PROC_METERING_FNAME, 0200, xpd->proc_xpd_dir); if(!priv->meteringfile) { XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_METERING_FNAME); - ret = -ENOENT; goto err; } priv->meteringfile->owner = THIS_MODULE; @@ -387,31 +353,75 @@ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) priv->meteringfile->read_proc = NULL; priv->meteringfile->data = xpd; #endif - XPD_DBG(PROC, xpd, "Creating SLICs file\n"); - priv->regfile = create_proc_entry(PROC_REGISTER_FNAME, 0644, xpd->proc_xpd_dir); - if(!priv->regfile) { - XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_REGISTER_FNAME); - ret = -ENOENT; - goto err; - } - priv->regfile->owner = THIS_MODULE; - priv->regfile->write_proc = proc_xpd_register_write; - priv->regfile->read_proc = proc_xpd_register_read; - priv->regfile->data = xpd; #endif + return 0; +err: + return -EINVAL; +} + +static xpd_t *FXS_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table, byte subtype, int subunits, bool to_phone) +{ + xpd_t *xpd = NULL; + int channels; + int regular_channels; + struct FXS_priv_data *priv; + int i; + + if(!to_phone) { + XBUS_NOTICE(xbus, + "XPD=%d%d: try to instanciate FXS with reverse direction\n", + unit, subunit); + return NULL; + } + if(subtype == 2) + regular_channels = min(6, CHANNELS_PERXPD); + else + regular_channels = min(8, CHANNELS_PERXPD); + channels = regular_channels; + if(unit == 0) + channels += 6; /* 2 DIGITAL OUTPUTS, 4 DIGITAL INPUTS */ + xpd = xpd_alloc(sizeof(struct FXS_priv_data), proto_table, channels); + if(!xpd) + return NULL; + if(unit == 0) { + XBUS_DBG(GENERAL, xbus, "First XPD detected. Initialize digital outputs/inputs\n"); + xpd->digital_outputs = BITMASK(LINES_DIGI_OUT) << regular_channels; + xpd->digital_inputs = BITMASK(LINES_DIGI_INP) << (regular_channels + LINES_DIGI_OUT); + } + xpd->direction = TO_PHONE; + xpd->type_name = "FXS"; + if(xpd_common_init(xbus, xpd, unit, subunit, subtype, subunits) < 0) + goto err; + if(fxs_proc_create(xbus, xpd) < 0) + goto err; + priv = xpd->priv; for_each_line(xpd, i) { priv->idletxhookstate[i] = FXS_LINE_POL_ACTIVE; } - ret = run_initialize_registers(xpd); - if(ret < 0) - goto err; + return xpd; +err: + xpd_free(xpd); + return NULL; +} + +static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) +{ + struct FXS_priv_data *priv; + int ret = 0; + int i; + + BUG_ON(!xpd); + priv = xpd->priv; /* * Setup ring timers */ /* Software controled ringing (for CID) */ - ret = SLIC_DIRECT_REQUEST(xbus, xpd, ALL_CHANS, SLIC_WRITE, 0x22, 0x00); /* Ringing Oscilator Control */ + ret = SLIC_DIRECT_REQUEST(xbus, xpd, PORT_BROADCAST, SLIC_WRITE, 0x22, 0x00); /* Ringing Oscilator Control */ if(ret < 0) goto err; + for_each_line(xpd, i) { + linefeed_control(xbus, xpd, i, FXS_LINE_POL_ACTIVE); + } XPD_DBG(GENERAL, xpd, "done\n"); for_each_line(xpd, i) { do_led(xpd, i, LED_GREEN, 0); @@ -427,9 +437,19 @@ static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) } restore_leds(xpd); pcm_recompute(xpd, 0); + /* + * We should query our offhook state long enough time after we + * set the linefeed_control() + * So we do this after the LEDs + */ + for_each_line(xpd, i) { + if(IS_SET(xpd->digital_outputs | xpd->digital_inputs, i)) + continue; + SLIC_DIRECT_REQUEST(xbus, xpd, i, SLIC_READ, REG_LOOPCLOSURE, 0); + } return 0; err: - clean_proc(xbus, xpd); + fxs_proc_remove(xbus, xpd); XPD_ERR(xpd, "Failed initializing registers (%d)\n", ret); return ret; } @@ -441,7 +461,7 @@ static int FXS_card_remove(xbus_t *xbus, xpd_t *xpd) BUG_ON(!xpd); priv = xpd->priv; XPD_DBG(GENERAL, xpd, "\n"); - clean_proc(xbus, xpd); + fxs_proc_remove(xbus, xpd); return 0; } @@ -481,6 +501,7 @@ static int FXS_card_zaptel_preregistration(xpd_t *xpd, bool on) for_each_line(xpd, i) { MARK_ON(priv, i, LED_GREEN); msleep(4); + MARK_ON(priv, i, LED_RED); } return 0; } @@ -519,6 +540,103 @@ static void __do_mute_dtmf(xpd_t *xpd, int pos, bool muteit) BIT_CLR(xpd->mute_dtmf, pos); } +static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos, int on) +{ + 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) { + /* 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); + ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x15, 0xEF, 0x7B); + ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x14, 0x9F, 0x00); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x22, 0x19); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x4A, 0x34); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x30, 0xE0); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x31, 0x01); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x32, 0xF0); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x33, 0x05); + 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 */ + 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); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x22, 0x00); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x4A, 0x34); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x30, 0x00); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x31, 0x00); + ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x32, 0x00); + 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); +} + +static void start_stop_vm_led(xbus_t *xbus, xpd_t *xpd, lineno_t pos) +{ + struct FXS_priv_data *priv; + bool on; + + BUG_ON(!xpd); + if (!vmwineon || IS_SET(xpd->digital_outputs | xpd->digital_inputs, pos)) + return; + priv = xpd->priv; + on = IS_SET(xpd->msg_waiting, pos); + LINE_DBG(SIGNAL, xpd, pos, "%s\n", (on)?"ON":"OFF"); + set_vm_led_mode(xbus, xpd, pos, on); + do_chan_power(xbus, xpd, pos, on); + linefeed_control(xbus, xpd, pos, (on) ? FXS_LINE_RING : priv->idletxhookstate[pos]); +} + +static int relay_out(xpd_t *xpd, int pos, bool on) +{ + int value; + int which = pos; + int relay_channels[] = { 0, 4 }; + + BUG_ON(!xpd); + /* map logical position to output port number (0/1) */ + which -= (xpd->subtype == 2) ? 6 : 8; + LINE_DBG(SIGNAL, xpd, pos, "which=%d -- %s\n", which, (on) ? "on" : "off"); + which = which % ARRAY_SIZE(relay_channels); + value = BIT(2) | BIT(3); + value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[OUTPUT_RELAY]); + if(on) + value |= led_register_vals[OUTPUT_RELAY]; + return SLIC_DIRECT_REQUEST(xpd->xbus, xpd, relay_channels[which], + SLIC_WRITE, REG_DIGITAL_IOCTRL, value); +} + +static int send_ring(xpd_t *xpd, lineno_t chan, bool on) +{ + int ret = 0; + xbus_t *xbus; + struct FXS_priv_data *priv; + enum fxs_state value = (on) ? FXS_LINE_RING : FXS_LINE_POL_ACTIVE; + + BUG_ON(!xpd); + xbus = xpd->xbus; + BUG_ON(!xbus); + LINE_DBG(SIGNAL, xpd, chan, "%s\n", (on)?"on":"off"); + priv = xpd->priv; + set_vm_led_mode(xbus, xpd, chan, 0); + do_chan_power(xbus, xpd, chan, on); // Power up (for ring) + ret = linefeed_control(xbus, xpd, chan, value); + if(on) { + MARK_BLINK(priv, chan, LED_GREEN, LED_BLINK_RING); + } else { + if(IS_BLINKING(priv, chan, LED_GREEN)) + MARK_BLINK(priv, chan, LED_GREEN, 0); + } + return ret; +} + static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) { struct FXS_priv_data *priv; @@ -548,8 +666,8 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) __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); + LINE_DBG(SIGNAL, xpd, pos, "%s -> digital output OFF\n", txsig2str(txsig)); + ret = relay_out(xpd, pos, 0); return ret; } if (priv->lasttxhook[pos] == FXS_LINE_OPEN) { @@ -561,7 +679,7 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) if(IS_SET(xpd->offhook, pos)) MARK_ON(priv, pos, LED_GREEN); } - ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0); // RING off + ret = send_ring(xpd, pos, 0); // RING off if (!IS_SET(xpd->offhook, pos)) start_stop_vm_led(xbus, xpd, pos); txhook = priv->lasttxhook[pos]; @@ -580,6 +698,10 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) ret = linefeed_control(xbus, xpd, pos, txhook); break; case ZT_TXSIG_OFFHOOK: + if(IS_SET(xpd->digital_outputs, pos)) { + LINE_NOTICE(xpd, pos, "%s -> Is digital output. Ignored\n", txsig2str(txsig)); + return -EINVAL; + } txhook = priv->lasttxhook[pos]; if(xpd->ringing[pos]) { BIT_SET(xpd->cid_on, pos); @@ -605,14 +727,17 @@ static int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) BIT_CLR(priv->search_fsk_pattern, pos); 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); + LINE_DBG(SIGNAL, xpd, pos, "%s -> digital output ON\n", txsig2str(txsig)); + ret = relay_out(xpd, pos, 1); return ret; } - ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1); // RING on + ret = send_ring(xpd, pos, 1); // RING on break; case ZT_TXSIG_KEWL: - LINE_DBG(SIGNAL, xpd, pos, "KEWL START\n"); + if(IS_SET(xpd->digital_outputs, pos)) { + LINE_DBG(SIGNAL, xpd, pos, "%s -> Is digital output. Ignored\n", txsig2str(txsig)); + return -EINVAL; + } linefeed_control(xbus, xpd, pos, FXS_LINE_OPEN); MARK_OFF(priv, pos, LED_GREEN); break; @@ -653,7 +778,6 @@ 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_ONHOOKTRANSFER (%d millis)\n", val); - BUG_ON(pos == ALL_CHANS); if (IS_SET(xpd->digital_inputs | xpd->digital_outputs, pos)) return 0; /* Nothing to do */ BIT_CLR(xpd->cid_on, pos); @@ -757,60 +881,6 @@ static int FXS_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long a return -ENOTTY; } -static int set_vm_led_mode(xbus_t *xbus, xpd_t *xpd, int pos, int on) -{ - 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) { - /* 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); - ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x15, 0xEF, 0x7B); - ret += SLIC_INDIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x14, 0x9F, 0x00); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x22, 0x19); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x4A, 0x34); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x30, 0xE0); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x31, 0x01); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x32, 0xF0); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x33, 0x05); - 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 */ - 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); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x22, 0x00); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x4A, 0x34); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x30, 0x00); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x31, 0x00); - ret += SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x32, 0x00); - 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); -} - -static void start_stop_vm_led(xbus_t *xbus, xpd_t *xpd, lineno_t pos) -{ - struct FXS_priv_data *priv; - bool on; - - BUG_ON(!xpd); - if (!vmwineon || IS_SET(xpd->digital_outputs | xpd->digital_inputs, pos)) - return; - priv = xpd->priv; - on = IS_SET(xpd->msg_waiting, pos); - LINE_DBG(SIGNAL, xpd, pos, "%s\n", (on)?"ON":"OFF"); - set_vm_led_mode(xbus, xpd, pos, on); - do_chan_power(xbus, xpd, pos, on); - linefeed_control(xbus, xpd, pos, (on) ? FXS_LINE_RING : priv->idletxhookstate[pos]); -} - static int FXS_card_open(xpd_t *xpd, lineno_t chan) { struct FXS_priv_data *priv; @@ -819,7 +889,10 @@ static int FXS_card_open(xpd_t *xpd, lineno_t chan) BUG_ON(!xpd); priv = xpd->priv; is_offhook = IS_SET(xpd->offhook, chan); - LINE_DBG(GENERAL, xpd, chan, "(is %shook)\n", (is_offhook)?"off":"on"); + if(is_offhook) + LINE_NOTICE(xpd, chan, "Already offhook during open. OK.\n"); + else + LINE_DBG(SIGNAL, xpd, chan, "is onhook\n"); /* * Delegate updating zaptel to FXS_card_tick(): * The problem is that zt_hooksig() is spinlocking the channel and @@ -883,13 +956,12 @@ static void handle_linefeed(xpd_t *xpd) priv->ohttimer[i]--; if (!priv->ohttimer[i]) { priv->idletxhookstate[i] = FXS_LINE_POL_ACTIVE; + BIT_CLR(xpd->cid_on, i); + BIT_CLR(priv->search_fsk_pattern, i); + pcm_recompute(xpd, 0); if (priv->lasttxhook[i] == FXS_LINE_POL_OHTRANS) { - enum fxs_state txhook = FXS_LINE_POL_ACTIVE; /* Apply the change if appropriate */ - BIT_CLR(xpd->cid_on, i); - BIT_CLR(priv->search_fsk_pattern, i); - pcm_recompute(xpd, 0); - linefeed_control(xpd->xbus, xpd, i, txhook); + linefeed_control(xpd->xbus, xpd, i, FXS_LINE_POL_ACTIVE); } } } @@ -939,10 +1011,10 @@ static void detect_vmwi(xpd_t *xpd) LINE_DBG(GENERAL, xpd, pos, "MSG:"); for(j = 0; j < ZT_CHUNKSIZE; j++) { - if(print_dbg) + if(debug) printk(" %02X", writechunk[j]); } - if(print_dbg) + if(debug) printk("\n"); } #endif @@ -993,14 +1065,11 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) if(!IS_SET(priv->update_offhook_state, i)) continue; /* - * Update LEDs and zaptel with current state of line. + * Update zaptel with current state of line. */ if(IS_SET(xpd->offhook, i)) { - LINE_NOTICE(xpd, i, "Already offhook during open. OK.\n"); - MARK_ON(priv, i, LED_GREEN); update_line_status(xpd, i, 1); } else { - MARK_OFF(priv, i, LED_GREEN); update_line_status(xpd, i, 0); } BIT_CLR(priv->update_offhook_state, i); @@ -1015,99 +1084,40 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) /*---------------- FXS: HOST COMMANDS -------------------------------------*/ -/* 0x0F */ HOSTCMD(FXS, XPD_STATE, bool on) +static /* 0x0F */ HOSTCMD(FXS, XPD_STATE, bool on) { - int i; - enum fxs_state value = (on) ? FXS_LINE_POL_ACTIVE : FXS_LINE_OPEN; - unsigned long flags; - struct FXS_priv_data *priv; - BUG_ON(!xbus); BUG_ON(!xpd); - priv = xpd->priv; - spin_lock_irqsave(&xpd->lock, flags); XPD_DBG(GENERAL, xpd, "%s\n", (on)?"on":"off"); - for_each_line(xpd, i) - linefeed_control(xbus, xpd, i, value); - if(on) { - MARK_ON(priv, ALL_CHANS, LED_GREEN); - } else { - MARK_OFF(priv, ALL_CHANS, LED_GREEN); - } - spin_unlock_irqrestore(&xpd->lock, flags); return 0; } -/* 0x0F */ HOSTCMD(FXS, RING, lineno_t chan, bool on) -{ - int ret = 0; - struct FXS_priv_data *priv; - enum fxs_state value = (on) ? FXS_LINE_RING : FXS_LINE_POL_ACTIVE; - - BUG_ON(!xbus); - BUG_ON(!xpd); - LINE_DBG(SIGNAL, xpd, chan, "%s\n", (on)?"on":"off"); - priv = xpd->priv; - set_vm_led_mode(xbus, xpd, chan, 0); - do_chan_power(xbus, xpd, chan, on); // Power up (for ring) - ret = linefeed_control(xbus, xpd, chan, value); - if(on) { - MARK_BLINK(priv, chan, LED_GREEN, LED_BLINK_RING); - } else { - if(IS_BLINKING(priv, chan, LED_GREEN)) - MARK_BLINK(priv, chan, LED_GREEN, 0); - } - return ret; -} - -/* 0x0F */ HOSTCMD(FXS, RELAY_OUT, byte which, bool on) -{ - int value; - int relay_channels[] = { 0, 4 }; - - BUG_ON(!xbus); - BUG_ON(!xpd); - XPD_DBG(SIGNAL, xpd, "RELAY_OUT: which=%d -- %s\n", which, (on) ? "on" : "off"); - which = which % ARRAY_SIZE(relay_channels); - value = BIT(2) | BIT(3); - value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[OUTPUT_RELAY]); - if(on) - value |= led_register_vals[OUTPUT_RELAY]; - return SLIC_DIRECT_REQUEST(xbus, xpd, relay_channels[which], SLIC_WRITE, 0x06, value); -} - /*---------------- FXS: Astribank Reply Handlers --------------------------*/ -HANDLER_DEF(FXS, SIG_CHANGED) +/* + * Should be called with spinlocked XPD + */ +static void process_hookstate(xpd_t *xpd, xpp_line_t offhook, xpp_line_t change_mask) { - xpp_line_t sig_status = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_status); - xpp_line_t sig_toggles = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_toggles); + xbus_t *xbus; struct FXS_priv_data *priv; int i; - unsigned long flags; BUG_ON(!xpd); BUG_ON(xpd->direction != TO_PHONE); + xbus = xpd->xbus; priv = xpd->priv; - XPD_DBG(SIGNAL, xpd, "(PHONE) sig_toggles=0x%04X sig_status=0x%04X\n", sig_toggles, sig_status); -#if 0 - Is this needed? - for_each_line(xpd, i) { - if(IS_SET(sig_toggles, i)) - do_chan_power(xpd->xbus, xpd, BIT(i), 0); // Power down (prevent overheating!!!) - } -#endif - spin_lock_irqsave(&xpd->lock, flags); + XPD_DBG(SIGNAL, xpd, "offhook=0x%X change_mask=0x%X\n", offhook, change_mask); for_each_line(xpd, i) { if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i)) continue; - if(IS_SET(sig_toggles, i)) { + if(IS_SET(change_mask, i)) { 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)) { + if(IS_SET(offhook, i)) { LINE_DBG(SIGNAL, xpd, i, "OFFHOOK\n"); MARK_ON(priv, i, LED_GREEN); update_line_status(xpd, i, 1); @@ -1119,6 +1129,26 @@ HANDLER_DEF(FXS, SIG_CHANGED) } } __pcm_recompute(xpd, 0); /* in a spinlock */ +} + +HANDLER_DEF(FXS, SIG_CHANGED) +{ + xpp_line_t sig_status = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_status); + xpp_line_t sig_toggles = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_toggles); + unsigned long flags; + + BUG_ON(!xpd); + BUG_ON(xpd->direction != TO_PHONE); + XPD_DBG(SIGNAL, xpd, "(PHONE) sig_toggles=0x%04X sig_status=0x%04X\n", sig_toggles, sig_status); +#if 0 + Is this needed? + for_each_line(xpd, i) { + if(IS_SET(sig_toggles, i)) + do_chan_power(xpd->xbus, xpd, BIT(i), 0); // Power down (prevent overheating!!!) + } +#endif + spin_lock_irqsave(&xpd->lock, flags); + process_hookstate(xpd, sig_status, sig_toggles); spin_unlock_irqrestore(&xpd->lock, flags); return 0; } @@ -1128,7 +1158,7 @@ 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)); + xpp_line_t lines = BIT(info->portnum); /* Map SLIC number into line number */ for(i = 0; i < ARRAY_SIZE(input_channels); i++) { @@ -1168,6 +1198,8 @@ static void process_dtmf(xpd_t *xpd, xpp_line_t lines, byte val) if(!dtmf_detection) return; + if(!SPAN_REGISTERED(xpd)) + return; priv = xpd->priv; val &= 0xF; if(val <= 0) { @@ -1225,26 +1257,37 @@ static int FXS_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info) XPD_DBG(REGS, xpd, "%s reg_num=0x%X, dataL=0x%X dataH=0x%X\n", (indirect)?"I":"D", regnum, REG_FIELD(info, data_low), REG_FIELD(info, data_high)); - if(!SPAN_REGISTERED(xpd)) - goto out; + if(!indirect && regnum == REG_DTMF_DECODE) { + byte val = REG_FIELD(info, data_low); + xpp_line_t lines = BIT(info->portnum); + + process_dtmf(xpd, lines, val); + } #ifdef POLL_DIGITAL_INPUTS /* * Process digital inputs polling results */ - if(xpd->xbus_idx == 0 && !indirect && regnum == 0x06) + else if(xpd->xbus_idx == 0 && !indirect && regnum == REG_DIGITAL_IOCTRL) { process_digital_inputs(xpd, info); + } #endif - if(!indirect && regnum == SLIC_REG_DTMF) { + else if(!indirect && regnum == REG_LOOPCLOSURE) { /* OFFHOOK ? */ byte val = REG_FIELD(info, data_low); - xpp_line_t lines = BIT(REG_FIELD(info, chipsel)); - + xpp_line_t mask = BIT(info->portnum); + xpp_line_t offhook; + + offhook = (val & REG_LOOPCLOSURE_LCR) ? mask : 0; + LINE_DBG(SIGNAL, xpd, info->portnum, + "REG_LOOPCLOSURE: dataL=0x%X (offhook=0x%X mask=0x%X\n", + val, offhook, mask); + process_hookstate(xpd, offhook, mask); + } else { #if 0 - XPD_DBG(SIGNAL, xpd, "DTMF result lines=0x%04X val=%d\n", - lines, val); + XPD_NOTICE(xpd, "Spurious register reply(ignored): %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n", + (indirect)?"I":"D", + regnum, REG_FIELD(info, data_low), REG_FIELD(info, data_high)); #endif - process_dtmf(xpd, lines, val); } -out: /* Update /proc info only if reply relate to the last slic read request */ if( REG_FIELD(&xpd->requested_reply, regnum) == REG_FIELD(info, regnum) && @@ -1262,7 +1305,8 @@ static xproto_table_t PROTO_TABLE(FXS) = { /* Prototable Card Opcode */ XENTRY( FXS, FXS, SIG_CHANGED ), }, - .name = "FXS", + .name = "FXS", /* protocol name */ + .ports_per_subunit = 8, .type = XPD_TYPE_FXS, .xops = { .card_new = FXS_card_new, @@ -1279,8 +1323,6 @@ static xproto_table_t PROTO_TABLE(FXS) = { .card_ioctl = FXS_card_ioctl, .card_register_reply = FXS_card_register_reply, - .RING = XPROTO_CALLER(FXS, RING), - .RELAY_OUT = XPROTO_CALLER(FXS, RELAY_OUT), .XPD_STATE = XPROTO_CALLER(FXS, XPD_STATE), }, .packet_is_valid = fxs_packet_is_valid, @@ -1372,191 +1414,6 @@ static int proc_fxs_info_read(char *page, char **start, off_t off, int count, in return len; } -/* - * - * Direct/Indirect - * | - * | Reg# - * | | - * | | Data (only in Write) - * | | | - * | | +-+-+ - * v v v v - * FF WD 06 01 05 - * ^ ^ - * | | - * | Write/Read - * | - * Chan# - * - */ -static int handle_register_command(xpd_t *xpd, char *cmdline) -{ - unsigned chipsel; - unsigned data_low = 0; - unsigned data_high = 0; - char op; /* [W]rite, [R]ead */ - char reg_type; /* [D]irect, [I]ndirect */ - int reg_num; - int elements; - bool writing; - char *p; - reg_cmd_t regcmd; - xbus_t *xbus; - int ret = -EINVAL; - - BUG_ON(!xpd); - xbus = xpd->xbus; - if((p = strchr(cmdline, '#')) != NULL) /* Truncate comments */ - *p = '\0'; - if((p = strchr(cmdline, ';')) != NULL) /* Truncate comments */ - *p = '\0'; - for(p = cmdline; *p && (*p == ' ' || *p == '\t'); p++) /* Trim leading whitespace */ - ; - 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, - &data_low, - &data_high); - 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); - goto out; - } - if(!VALID_CHIPSEL(chipsel)) { - ERR("Bad chipsel number: %d\n", chipsel); - goto out; - } - REG_FIELD(®cmd, chipsel) = chipsel; - switch(op) { - case 'W': - writing = 1; - break; - case 'R': - writing = 0; - break; - default: - ERR("Unkown operation type '%c'\n", op); - goto out; - } - switch(reg_type) { - case 'I': - REG_FIELD(®cmd, do_subreg) = 1; - REG_FIELD(®cmd, regnum) = 0x1E; // FIXME: card dependent... - REG_FIELD(®cmd, subreg) = reg_num; - break; - case 'D': - REG_FIELD(®cmd, do_subreg) = 0; - REG_FIELD(®cmd, regnum) = reg_num; - REG_FIELD(®cmd, subreg) = 0; - break; - default: - ERR("Unkown register type '%c'\n", reg_type); - goto out; - } - if( - (op == 'W' && reg_type == 'D' && elements != 5) || - (op == 'W' && reg_type == 'I' && elements != 6) || - (op == 'R' && reg_type == 'D' && elements != 4) || - (op == 'R' && reg_type == 'I' && elements != 4) - ) { - 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); - 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; - xpd->requested_reply = regcmd; - if(print_dbg) - dump_reg_cmd("FXS", ®cmd, 1); - ret = xpp_register_request(xpd->xbus, xpd, - REG_FIELD(®cmd, chipsel), - writing, - REG_FIELD(®cmd, do_subreg), - REG_FIELD(®cmd, regnum), - REG_FIELD(®cmd, subreg), - REG_FIELD(®cmd, data_low), - REG_FIELD(®cmd, data_high)); -out: - XBUS_PUT(xbus); - return ret; -} - -static int proc_xpd_register_write(struct file *file, const char __user *buffer, unsigned long count, void *data) -{ - xpd_t *xpd = data; - char buf[MAX_PROC_WRITE]; - char *p; - int i; - int ret; - - if(!xpd) - return -ENODEV; - for(i = 0; i < count; /* noop */) { - for(p = buf; p < buf + MAX_PROC_WRITE; p++) { /* read a line */ - if(i >= count) - break; - if(get_user(*p, buffer + i)) - return -EFAULT; - i++; - if(*p == '\n' || *p == '\r') /* whatever */ - break; - } - if(p >= buf + MAX_PROC_WRITE) - return -E2BIG; - *p = '\0'; - ret = handle_register_command(xpd, buf); - if(ret < 0) - return ret; - msleep(1); - } - return count; -} - -static int proc_xpd_register_read(char *page, char **start, off_t off, int count, int *eof, void *data) -{ - int len = 0; - unsigned long flags; - xpd_t *xpd = data; - reg_cmd_t *info; - byte regnum; - bool indirect; - - if(!xpd) - return -ENODEV; - spin_lock_irqsave(&xpd->lock, flags); - info = &xpd->last_reply; - indirect = (REG_FIELD(info, regnum) == 0x1E); - regnum = (indirect) ? REG_FIELD(info, subreg) : REG_FIELD(info, regnum); - len += sprintf(page + len, "# Writing bad data into this file may damage your hardware!\n"); - len += sprintf(page + len, "# Consult firmware docs first\n"); - len += sprintf(page + len, "#\n"); - len += sprintf(page + len, "#CH\tD/I\tReg.\tDL DH\n"); - len += sprintf(page + len, "%2d\tR%c\t%02X\t%02X %02X\n", - REG_FIELD(info, chipsel), - (indirect)?'I':'D', - regnum, REG_FIELD(info, data_low), REG_FIELD(info, data_high)); - spin_unlock_irqrestore(&xpd->lock, flags); - if (len <= off+count) - *eof = 1; - *start = page + off; - len -= off; - if (len > count) - len = count; - if (len < 0) - len = 0; - return len; -} - #ifdef WITH_METERING static int proc_xpd_metering_write(struct file *file, const char __user *buffer, unsigned long count, void *data) { @@ -1581,7 +1438,7 @@ static int proc_xpd_metering_write(struct file *file, const char __user *buffer, return -EINVAL; } chan = num; - if(chan != ALL_CHANS && chan > xpd->channels) { + if(chan != PORT_BROADCAST && chan > xpd->channels) { XPD_ERR(xpd, "Metering tone: bad channel number %d\n", chan); return -EINVAL; } @@ -1593,7 +1450,7 @@ static int proc_xpd_metering_write(struct file *file, const char __user *buffer, } #endif -int __init card_fxs_startup(void) +static int __init card_fxs_startup(void) { INFO("revision %s\n", XPP_VERSION); #ifdef POLL_DIGITAL_INPUTS @@ -1616,7 +1473,7 @@ int __init card_fxs_startup(void) return 0; } -void __exit card_fxs_cleanup(void) +static void __exit card_fxs_cleanup(void) { xproto_unregister(&PROTO_TABLE(FXS)); } |