diff options
Diffstat (limited to 'xpp/card_fxs.c')
-rw-r--r-- | xpp/card_fxs.c | 253 |
1 files changed, 222 insertions, 31 deletions
diff --git a/xpp/card_fxs.c b/xpp/card_fxs.c index 5e5674b..5177f81 100644 --- a/xpp/card_fxs.c +++ b/xpp/card_fxs.c @@ -29,6 +29,7 @@ #include "xpp_zap.h" #include "card_fxo.h" #include "zap_debug.h" +#include "xbus-core.h" static const char rcsid[] = "$Id$"; @@ -64,14 +65,13 @@ static /* 0x0F */ DECLARE_CMD(FXS, REGISTER_REQUEST, byte chipsel, bool writing, #define SLIC_DIRECT_REQUEST(xbus,xpd,chipsel,writing,reg,dL) \ CALL_PROTO(FXS, REGISTER_REQUEST, (xbus), (xpd), (chipsel), (writing), 0, (reg), 0, (dL), 0) #define SLIC_INDIRECT_REQUEST(xbus,xpd,chipsel,writing,reg,dL,dH) \ - PROTO(FXS, REGISTER_REQUEST, (xbus), (xpd), (chipsel), (writing), 1, (reg), 0, (dL), (dH)) + CALL_PROTO(FXS, REGISTER_REQUEST, (xbus), (xpd), (chipsel), (writing), 1, 0x1E, (reg), (dL), (dH)) #define VALID_CHIPSEL(x) (((chipsel) >= 0 && (chipsel) <= 7) || (chipsel) == ALL_CHANS) /*---------------- FXS Protocol Commands ----------------------------------*/ static /* 0x0F */ DECLARE_CMD(FXS, XPD_STATE, bool on); -static /* 0x0F */ DECLARE_CMD(FXS, CHAN_CID, lineno_t chan); static /* 0x0F */ DECLARE_CMD(FXS, RING, lineno_t chan, bool on); static /* 0x0F */ DECLARE_CMD(FXS, RELAY_OUT, byte which, bool on); @@ -80,6 +80,7 @@ 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); 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" @@ -89,6 +90,8 @@ struct FXS_priv_data { 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; int blinking[NUM_LEDS][CHANNELS_PERXPD]; }; @@ -137,7 +140,7 @@ static const int led_register_vals[] = { BIT(4), BIT(1), BIT(0) }; /* * pos can be: * - A line number - * - ALL_LINES + * - ALL_LINES. This is not valid anymore since 8-Jan-2007. */ static int do_led(xpd_t *xpd, lineno_t chan, byte which, bool on) { @@ -147,6 +150,7 @@ static int do_led(xpd_t *xpd, lineno_t chan, byte which, bool on) xbus_t *xbus; BUG_ON(!xpd); + BUG_ON(chan == ALL_LINES); xbus = xpd->xbus; priv = xpd->priv; which = which % NUM_LEDS; @@ -186,7 +190,7 @@ static void handle_fxs_leds(xpd_t *xpd) for_each_line(xpd, i) { if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i)) continue; - if(IS_BLINKING(priv, i, color)) { // Blinking + if(xpd->blink_mode || IS_BLINKING(priv, i, color)) { // Blinking // led state is toggled if((timer_count % LED_BLINK_PERIOD) == 0) { DBG("%s/%s/%d ledstate=%s\n", xpd->xbus->busname, xpd->xpdname, i, @@ -207,6 +211,21 @@ static void handle_fxs_leds(xpd_t *xpd) } } +static int do_callerid(xbus_t *xbus, xpd_t *xpd, lineno_t chan) +{ + int ret = 0; + int i; + + BUG_ON(!xbus); + BUG_ON(!xpd); + DBG("%s/%s/%d:\n", xbus->busname, xpd->xpdname, chan); + ret = SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, 0x40, FXS_LINE_CID); + for_each_line(xpd, i) + xpd->lasttxhook[i] = FXS_LINE_CID; + return ret; +} + + /*---------------- FXS: Methods -------------------------------------------*/ static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision) @@ -337,9 +356,11 @@ static int FXS_card_zaptel_preregistration(xpd_t *xpd, bool on) cur_chan->pvt = xpd; cur_chan->sigcap = FXS_DEFAULT_SIGCAP; } - spin_lock_irqsave(&xpd->lock, flags); - do_led(xpd, ALL_LINES, color, LED_OFF); - spin_unlock_irqrestore(&xpd->lock, flags); + for_each_line(xpd, i) { + spin_lock_irqsave(&xpd->lock, flags); + do_led(xpd, i, color, LED_OFF); + spin_unlock_irqrestore(&xpd->lock, flags); + } for_each_line(xpd, i) { MARK_LED(priv, i, color, LED_ON); msleep(50); @@ -387,6 +408,8 @@ int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) return ret; } ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0); // RING off + if (!IS_SET(xpd->offhook, pos)) + start_stop_vm_led(xbus, xpd, pos); #if 0 switch(chan->sig) { case ZT_SIG_EM: @@ -403,7 +426,7 @@ int FXS_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) case ZT_TXSIG_OFFHOOK: if(xpd->ringing[pos]) { BIT_SET(xpd->cid_on, pos); - ret = CALL_XMETHOD(CHAN_CID, xpd->xbus, xpd, pos); // CALLER ID + ret = do_callerid(xpd->xbus, xpd, pos); // CALLER ID } xpd->ringing[pos] = 0; #if 0 @@ -438,6 +461,107 @@ 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; + + BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); + xbus = xpd->xbus; + BUG_ON(!xbus); + + if (pos < 0 || pos >= xpd->channels) { + NOTICE("%s/%s: Bad channel number %d in %s(), cmd=%u\n", xbus->busname, xpd->xpdname, pos, __FUNCTION__, cmd); + return -EINVAL; + } + + switch (cmd) { + case _IOW(ZT_CODE, 60, int): /* message-waiting led control */ + /* Digital inputs/outputs don't have VM leds */ + if (IS_SET(xpd->digital_inputs | xpd->digital_outputs, pos)) + return 0; + if (arg) + BIT_SET(priv->msg_waiting, pos); + else + BIT_CLR(priv->msg_waiting, pos); + return 0; + } + return -ENOTTY; +} +#endif + +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); + + 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) +{ + bool on; + + if (IS_SET(xpd->digital_outputs | xpd->digital_inputs, pos)) + return; + on = IS_SET(((struct FXS_priv_data *)xpd->priv)->msg_waiting, pos); + DBG("%s/%s/%d %s\n", xbus->busname, xpd->xpdname, pos, (on)?"ON":"OFF"); + set_vm_led_mode(xbus, xpd, pos, on); + do_chan_power(xbus, xpd, pos, on); + SLIC_DIRECT_REQUEST(xbus, xpd, pos, SLIC_WRITE, 0x40, (on) ? 0x04 : 0x01); +} + +static int FXS_chan_onhooktransfer(xbus_t *xbus, xpd_t *xpd, lineno_t chan, int millies) +{ + int ret = 0; + + BUG_ON(!xpd); + BUG_ON(chan == ALL_CHANS); + DBG("%s/%s/%d: (%d millies)\n", xbus->busname, xpd->xpdname, chan, millies); + xpd->ohttimer[chan] = millies << 3; + xpd->idletxhookstate[chan] = FXS_LINE_CID; /* OHT mode when idle */ + if (xpd->lasttxhook[chan] == FXS_LINE_ENABLED) { + /* Apply the change if appropriate */ + ret = do_callerid(xpd->xbus, xpd, chan); // CALLER ID + } + start_stop_vm_led(xbus, xpd, chan); + return ret; +} + /* * INPUT polling is done via SLIC register 0x06 (same as LEDS): * 7 6 5 4 3 2 1 0 @@ -460,6 +584,65 @@ static void poll_inputs(xbus_t *xbus, xpd_t *xpd) } } +#ifndef VMWI_IOCTL +/* + * Detect Voice Mail Waiting Indication + */ +static void detect_vmwi(xpd_t *xpd) +{ + struct FXS_priv_data *priv; + xbus_t *xbus; + static const byte FSK_COMMON_PATTERN[] = { 0xA8, 0x49, 0x22, 0x3B, 0x9F, 0xFF, 0x1F, 0xBB }; + static const byte FSK_ON_PATTERN[] = { 0xA2, 0x2C, 0x1F, 0x2C, 0xBB, 0xA1, 0xA5, 0xFF }; + static const byte FSK_OFF_PATTERN[] = { 0xA2, 0x2C, 0x28, 0xA5, 0xB1, 0x21, 0x49, 0x9F }; + int i; + + BUG_ON(!xpd); + xbus = xpd->xbus; + priv = xpd->priv; + BUG_ON(!priv); + for_each_line(xpd, i) { + byte *writechunk = xpd->span.chans[i].writechunk; + + if(IS_SET(xpd->offhook | xpd->cid_on | xpd->digital_inputs | xpd->digital_outputs, i)) + continue; +#if 0 + if(i == 0 && writechunk[0] != 0x7F) { + int j; + + DBG("%s/%s/%d: MSG:", xbus->busname, xpd->xpdname, i); + for(j = 0; j < ZT_CHUNKSIZE; j++) { + printk(" %02X", writechunk[j]); + } + printk("\n"); + } +#endif + if(unlikely(memcmp(writechunk, FSK_COMMON_PATTERN, ZT_CHUNKSIZE) == 0)) + 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) { + 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) { + 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); + } else { + int j; + + NOTICE("%s/%s/%d: MSG WAITING Unexpected:", xbus->busname, xpd->xpdname, i); + for(j = 0; j < ZT_CHUNKSIZE; j++) { + printk(" %02X", writechunk[j]); + } + printk("\n"); + } + } + } +} +#endif + static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) { static int rate_limit = 0; @@ -476,6 +659,10 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) } #endif handle_fxs_leds(xpd); +#ifndef VMWI_IOCTL + if(SPAN_REGISTERED(xpd)) + detect_vmwi(xpd); +#endif return 0; } @@ -484,6 +671,7 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) /* 0x0F */ HOSTCMD(FXS, REGISTER_REQUEST, byte chipsel, bool writing, bool do_subreg, byte regnum, byte subreg, byte data_low, byte data_high) { int ret = 0; + xframe_t *xframe; xpacket_t *pack; reg_cmd_t *reg_cmd; @@ -491,7 +679,7 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) DBG("NO XBUS\n"); return -EINVAL; } - XPACKET_NEW(pack, xbus, GLOBAL, REGISTER_REQUEST, xpd->id); + XFRAME_NEW(xframe, pack, xbus, GLOBAL, REGISTER_REQUEST, xpd->id); #if 0 DBG("%s/%s/%d: %c%c R%02X S%02X %02X %02X\n", xbus->busname, xpd->xpdname, chipsel, @@ -500,7 +688,6 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) regnum, subreg, data_low, data_high); #endif reg_cmd = &RPACKET_FIELD(pack, GLOBAL, REGISTER_REQUEST, reg_cmd); - pack->datalen = sizeof(*reg_cmd); reg_cmd->bytes = sizeof(*reg_cmd) - 1; // do not count the 'bytes' field REG_FIELD(reg_cmd, chipsel) = chipsel; REG_FIELD(reg_cmd, read_request) = (writing) ? 0 : 1; @@ -509,7 +696,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 = packet_send(xbus, pack); + ret = xframe_send(xbus, xframe); return ret; } @@ -538,21 +725,6 @@ static /* 0x0F */ HOSTCMD(FXS, XPD_STATE, bool on) return ret; } -static /* 0x0F */ HOSTCMD(FXS, CHAN_CID, lineno_t chan) -{ - int ret = 0; - int i; - - BUG_ON(!xbus); - BUG_ON(!xpd); - DBG("%s/%s/%d:\n", xbus->busname, xpd->xpdname, chan); - ret = SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, 0x40, FXS_LINE_CID); - for_each_line(xpd, i) - xpd->lasttxhook[i] = FXS_LINE_CID; - return ret; -} - - static /* 0x0F */ HOSTCMD(FXS, RING, lineno_t chan, bool on) { int ret = 0; @@ -563,6 +735,7 @@ static /* 0x0F */ HOSTCMD(FXS, RING, lineno_t chan, bool on) BUG_ON(!xpd); DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, chan, (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 = SLIC_DIRECT_REQUEST(xbus, xpd, chan, SLIC_WRITE, 0x40, value); xpd->lasttxhook[chan] = value; @@ -605,10 +778,19 @@ HANDLER_DEF(FXS, SIG_CHANGED) BUG_ON(xpd->direction != TO_PHONE); priv = xpd->priv; DBG("%s/%s: (PHONE) sig_toggles=0x%04X sig_status=0x%04X\n", xbus->busname, xpd->xpdname, sig_toggles, sig_status); +#if 0 + /* + * Not needed anymore. update_line_status() returns immediately + * if !SPAN_REGISTERED(). Now we maintain good status even if + * we are not registered. + * + * FIXME: Need to notify zaptel later (when registering). + */ if(!SPAN_REGISTERED(xpd)) { NOTICE("%s: %s/%s is not registered. Skipping.\n", __FUNCTION__, xbus->busname, xpd->xpdname); return -ENODEV; } +#endif #if 0 Is this needed? for_each_line(xpd, i) { @@ -631,6 +813,7 @@ HANDLER_DEF(FXS, SIG_CHANGED) DBG("%s/%s/%d: ONHOOK\n", xbus->busname, xpd->xpdname, i); MARK_LED(priv,i,LED_GREEN,LED_OFF); update_line_status(xpd, i, 0); + start_stop_vm_led(xbus, xpd, i); } } } @@ -648,7 +831,7 @@ HANDLER_DEF(FXS, REGISTER_REPLY) if(!xpd) { NOTICE("%s: received %s for non-existing xpd: %d\n", - __FUNCTION__, cmd->name, XPD_NUM(pack->content.addr)); + __FUNCTION__, cmd->name, XPD_NUM(pack->addr)); return -EPROTO; } spin_lock_irqsave(&xpd->lock, flags); @@ -720,14 +903,16 @@ 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, +#ifdef VMWI_IOCTL + .card_ioctl = FXS_card_ioctl, +#endif .RING = XPROTO_CALLER(FXS, RING), .RELAY_OUT = XPROTO_CALLER(FXS, RELAY_OUT), .XPD_STATE = XPROTO_CALLER(FXS, XPD_STATE), - .CHAN_CID = XPROTO_CALLER(FXS, CHAN_CID), .SYNC_SOURCE = XPROTO_CALLER(GLOBAL, SYNC_SOURCE), - .PCM_WRITE = XPROTO_CALLER(GLOBAL, PCM_WRITE), }, .packet_is_valid = fxs_packet_is_valid, .packet_dump = fxs_packet_dump, @@ -738,7 +923,7 @@ static bool fxs_packet_is_valid(xpacket_t *pack) const xproto_entry_t *xe; // DBG("\n"); - xe = xproto_card_entry(&PROTO_TABLE(FXS), pack->content.opcode); + xe = xproto_card_entry(&PROTO_TABLE(FXS), pack->opcode); return xe != NULL; } @@ -988,13 +1173,18 @@ static int proc_xpd_register_read(char *page, char **start, off_t off, int count int __init card_fxs_startup(void) { - INFO("%s\n", THIS_MODULE->name); + INFO("%s revision %s\n", THIS_MODULE->name, XPP_VERSION); #ifdef POLL_DIGITAL_INPUTS INFO("FEATURE: %s with DIGITAL INPUTS support (%s activated)\n", THIS_MODULE->name, (poll_digital_inputs) ? "is" : "is not"); #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); +#else + INFO("FEATURE: %s NO VMWI_IOCTL\n", THIS_MODULE->name); +#endif xproto_register(&PROTO_TABLE(FXS)); return 0; } @@ -1007,6 +1197,7 @@ void __exit card_fxs_cleanup(void) MODULE_DESCRIPTION("XPP FXS Card Driver"); MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>"); MODULE_LICENSE("GPL"); +MODULE_VERSION(XPP_VERSION); MODULE_ALIAS_XPD(XPD_TYPE_FXS); module_init(card_fxs_startup); |