diff options
author | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-05-03 23:06:02 +0000 |
---|---|---|
committer | tzafrir <tzafrir@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-05-03 23:06:02 +0000 |
commit | 2dd60aaf18e98b0e9d3c06bd9dce5f1128fa55ad (patch) | |
tree | 1a1cd28888f191e6ce83bcbbe539124e2529c90b /xpp/card_fxs.c | |
parent | 8c4db4e3acd9a7626e709af0494055487b589719 (diff) |
xpp driver release 1.1.0 (first part of commit)
* FPGA firmware now loaded from PC (for newer models)
* Driver for the FXO module
* Moved most userspace files to the subdirectory utils (see also next commit)
* Explicit license for firmware files
* Optionally avoid auto-registration
* Initializations parameters to chips given from userspace
* And did I mention bugfixes?
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@1021 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'xpp/card_fxs.c')
-rw-r--r-- | xpp/card_fxs.c | 661 |
1 files changed, 502 insertions, 159 deletions
diff --git a/xpp/card_fxs.c b/xpp/card_fxs.c index 37c0229..e4480a3 100644 --- a/xpp/card_fxs.c +++ b/xpp/card_fxs.c @@ -1,6 +1,6 @@ /* * Written by Oron Peled <oron@actcom.co.il> - * Copyright (C) 2004-2005, Xorcom + * Copyright (C) 2004-2006, Xorcom * * All rights reserved. * @@ -23,13 +23,15 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/fs.h> +#include <linux/delay.h> +#include <version.h> /* For zaptel version */ #include "xpd.h" #include "xproto.h" #include "xpp_zap.h" -#include <linux/delay.h> +#include "card_fxo.h" +#include "zap_debug.h" static const char rcsid[] = "$Id$"; -static const char revision[] = "$Revision$"; DEF_PARM(int, print_dbg, 0, "Print DBG statements"); /* must be before zap_debug.h */ @@ -42,20 +44,27 @@ DEF_PARM(int, print_dbg, 0, "Print DBG statements"); /* must be before zap_debug #define MASK_DIGI_OUT (MASK_BITS(LINES_DIGI_OUT) << LINES_REGULAR) #define MASK_DIGI_INP (MASK_BITS(LINES_DIGI_INP) << (LINES_REGULAR + LINES_DIGI_OUT)) +enum fxs_leds { + LED_GREEN, + LED_RED, + OUTPUT_RELAY, +}; + +#define NUM_LEDS 2 + /*---------------- FXS Protocol Commands ----------------------------------*/ -/* 0x0F */ DECLARE_CMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on); -/* 0x0F */ DECLARE_CMD(FXS, CHAN_POWER, xpp_line_t lines, bool on); -/* 0x0F */ DECLARE_CMD(FXS, CHAN_CID, xpp_line_t lines); -/* 0x0F */ DECLARE_CMD(FXS, RING, int pos, bool on); -/* 0x0F */ DECLARE_CMD(FXS, SETHOOK, xpp_line_t hook_status); -/* 0x0F */ DECLARE_CMD(FXS, LED, xpp_line_t lines, byte which, bool on); -/* 0x0F */ DECLARE_CMD(FXS, RELAY_OUT, byte which, bool on); -/* 0x0F */ DECLARE_CMD(FXS, SLIC_INIT); -/* 0x0F */ DECLARE_CMD(FXS, SLIC_QUERY, int pos, byte reg_num); +static /* 0x0F */ DECLARE_CMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on); +static /* 0x0F */ DECLARE_CMD(FXS, CHAN_CID, xpp_line_t lines); +static /* 0x0F */ DECLARE_CMD(FXS, RING, int pos, bool on); +static /* 0x0F */ DECLARE_CMD(FXS, SETHOOK, int pos, bool offhook); +static /* 0x0F */ DECLARE_CMD(FXS, RELAY_OUT, byte which, bool on); +static /* 0x0F */ DECLARE_CMD(FXS, SLIC_INIT); +static /* 0x0F */ DECLARE_CMD(FXS, SLIC_QUERY, int pos, byte reg_num); static bool fxs_packet_is_valid(xpacket_t *pack); static void fxs_packet_dump(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_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data); static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data); @@ -71,17 +80,180 @@ struct slic_init_data { xpp_line_t lines; slic_data_t slic_data; } slic_init_data[] = { -#include "slic_init.inc" +#ifdef OLD_CARD +#include "init_data_3_19.inc" +#else +#include "init_data_3_20.inc" +#endif }; #undef S_ #define PROC_SLIC_FNAME "slics" +#define PROC_FXS_INFO_FNAME "fxs_info" struct FXS_priv_data { - struct proc_dir_entry *xpd_slic; - slic_reply_t last_reply; + struct proc_dir_entry *xpd_slic; + struct proc_dir_entry *fxs_info; + slic_reply_t requested_reply; + slic_reply_t last_reply; + xpp_line_t ledstate[NUM_LEDS]; /* 0 - OFF, 1 - ON */ + xpp_line_t ledcontrol[NUM_LEDS]; /* 0 - OFF, 1 - ON */ + int blinking[NUM_LEDS][CHANNELS_PERXPD]; }; +/*---------------- FXS: Static functions ----------------------------------*/ +static int do_chan_power(xbus_t *xbus, xpd_t *xpd, xpp_line_t lines, bool on) +{ + int ret = 0; + xpacket_t *pack; + slic_cmd_t *sc; + int len; + + BUG_ON(!xbus); + BUG_ON(!xpd); + lines &= xpd->enabled_chans; // Ignore disabled channels + if(!lines) { + return 0; + } + DBG("%s/%s: 0x%04X %s\n", xbus->busname, xpd->xpdname, lines, (on) ? "up" : "down"); + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); + if(on) { + // Power up + len = slic_cmd_direct_write(sc, lines, 0x42, 0x06); + } else { + // Power down + len = slic_cmd_direct_write(sc, lines, 0x42, 0x00); + } + pack->datalen = len; + + packet_send(xbus, pack); + return ret; +} + +#define IS_BLINKING(priv,pos,color) ((priv)->blinking[color][pos] != 0) +#define DO_BLINK(priv,pos,color,val) ((priv)->blinking[color][pos] = (val)) +#define DO_LED(priv,pos,color,val) ((val)?BIT_SET((priv)->ledcontrol[color],(pos)):BIT_CLR((priv)->ledcontrol[color],(pos))) + +/* + * LED and RELAY control is done via SLIC register 0x06: + * 7 6 5 4 3 2 1 0 + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * | M2 | M1 | M3 | C2 | O1 | O3 | C1 | C3 | + * +-----+-----+-----+-----+-----+-----+-----+-----+ + * + * Cn - Control bit (control one digital line) + * On - Output bit (program a digital line for output) + * Mn - Mask bit (only the matching output control bit is affected) + * + * C3 - OUTPUT RELAY (0 - OFF, 1 - ON) + * C1 - GREEN LED (0 - OFF, 1 - ON) + * O3 - Output RELAY (this line is output) + * O1 - Output GREEN (this line is output) + * C2 - RED LED (0 - OFF, 1 - ON) + * M3 - Mask RELAY. (1 - C3 effect the OUTPUT RELAY) + * M2 - Mask RED. (1 - C2 effect the RED LED) + * M1 - Mask GREEN. (1 - C1 effect the GREEN LED) + * + * The OUTPUT RELAY (actually a relay out) is connected to line 0 and 4 only. + */ + +// GREEN RED OUTPUT RELAY +static const int led_register_mask[] = { BIT(7), BIT(6), BIT(5) }; +static const int led_register_vals[] = { BIT(4), BIT(1), BIT(0) }; + +/* + * pos can be: + * - A line number + * - ALL_LINES + */ +static int do_led(xpd_t *xpd, lineno_t pos, byte which, bool on) +{ + unsigned long flags; + int ret = 0; + xpacket_t *pack; + slic_cmd_t *sc; + int len; + int value; + struct FXS_priv_data *priv; + xpp_line_t lines; + xbus_t *xbus; + + BUG_ON(!xpd); + spin_lock_irqsave(&xpd->lock, flags); + xbus = xpd->xbus; + priv = xpd->priv; + which = which % NUM_LEDS; + if(IS_SET(xpd->digital_outputs, pos) || IS_SET(xpd->digital_inputs, pos)) + goto out; + if(pos == ALL_LINES) { + lines = ~0; + priv->ledstate[which] = (on) ? ~0 : 0; + } else { + lines = BIT(pos); + if(on) { + BIT_SET(priv->ledstate[which], pos); + } else { + BIT_CLR(priv->ledstate[which], pos); + } + } + if(!(lines & xpd->enabled_chans)) // Ignore disabled channels + goto out; + DBG("%s/%s: LED: lines=0x%04X which=%d -- %s\n", xbus->busname, xpd->xpdname, lines, which, (on) ? "on" : "off"); + value = BIT(2) | BIT(3); + value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[which]); + if(on) + value |= led_register_vals[which]; + XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); + sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); + len = slic_cmd_direct_write(sc, lines, 0x06, value); + pack->datalen = len; + packet_send(xbus, pack); + +out: + spin_unlock_irqrestore(&xpd->lock, flags); + return ret; +} + +static void handle_fxs_leds(xpd_t *xpd) +{ + int i; + unsigned long flags; + const enum fxs_leds colors[] = { LED_GREEN, LED_RED }; + int color; + unsigned int timer_count; + struct FXS_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; + spin_lock_irqsave(&xpd->lock, flags); + timer_count = xpd->timer_count; + for(color = 0; color < ARRAY_SIZE(colors); color++) { + for_each_enabled_line(xpd, i) { + if(IS_SET(xpd->digital_outputs, i) || IS_SET(xpd->digital_inputs, i)) + continue; + if(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, + (IS_SET(priv->ledstate[color], i))?"ON":"OFF"); + if(!IS_SET(priv->ledstate[color], i)) { + do_led(xpd, i, color, 1); + } else { + do_led(xpd, i, color, 0); + } + } + } else if(IS_SET(priv->ledcontrol[color], i) && !IS_SET(priv->ledstate[color], i)) { + do_led(xpd, i, color, 1); + } else if(!IS_SET(priv->ledcontrol[color], i) && IS_SET(priv->ledstate[color], i)) { + do_led(xpd, i, color, 0); + } + + } + } + spin_unlock_irqrestore(&xpd->lock, flags); +} + /*---------------- FXS: Methods -------------------------------------------*/ static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision) @@ -100,29 +272,67 @@ static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *prot xpd->digital_inputs = MASK_DIGI_INP; } xpd->direction = TO_PHONE; + xpd->revision = revision; return xpd; } +static void clean_proc(xbus_t *xbus, xpd_t *xpd) +{ + struct FXS_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; +#ifdef CONFIG_PROC_FS + if(priv->xpd_slic) { + DBG("Removing xpd SLIC file %s/%s\n", xbus->busname, xpd->xpdname); + remove_proc_entry(PROC_SLIC_FNAME, xpd->proc_xpd_dir); + } + 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); + } +#endif +} + static int FXS_card_init(xbus_t *xbus, xpd_t *xpd) { struct FXS_priv_data *priv; + int ret = 0; BUG_ON(!xpd); priv = xpd->priv; - CALL_PROTO(FXS, SLIC_INIT, xbus, xpd); #ifdef CONFIG_PROC_FS + DBG("Creating FXS_INFO file for %s/%s\n", xbus->busname, xpd->xpdname); + 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) { + ERR("Failed to create proc '%s' for %s/%s\n", PROC_FXS_INFO_FNAME, xbus->busname, xpd->xpdname); + ret = -ENOENT; + goto out; + } DBG("Creating SLICs file for %s/%s\n", xbus->busname, xpd->xpdname); priv->xpd_slic = create_proc_entry(PROC_SLIC_FNAME, 0644, xpd->proc_xpd_dir); if(!priv->xpd_slic) { ERR("Failed to create proc file for SLICs of %s/%s\n", xbus->busname, xpd->xpdname); + ret = -ENOENT; goto out; } priv->xpd_slic->write_proc = proc_xpd_slic_write; priv->xpd_slic->read_proc = proc_xpd_slic_read; priv->xpd_slic->data = xpd; -out: #endif - return 0; +#ifdef HARD_CODED_INIT + CALL_PROTO(FXS, SLIC_INIT, xbus, xpd); +#else + ret = run_initialize_registers(xpd); +#endif +out: + if(ret < 0) { + clean_proc(xbus, xpd); + ERR("%s/%s: Failed initializing registers (%d)\n", xbus->busname, xpd->xpdname, ret); + } else { + DBG("done: %s/%s\n", xbus->busname, xpd->xpdname); + } + return ret; } static int FXS_card_remove(xbus_t *xbus, xpd_t *xpd) @@ -132,15 +342,111 @@ static int FXS_card_remove(xbus_t *xbus, xpd_t *xpd) BUG_ON(!xpd); priv = xpd->priv; DBG("%s/%s\n", xbus->busname, xpd->xpdname); -#ifdef CONFIG_PROC_FS - if(priv->xpd_slic) { - DBG("Removing xpd SLIC file %s/%s\n", xbus->busname, xpd->xpdname); - remove_proc_entry(PROC_SLIC_FNAME, xpd->proc_xpd_dir); + clean_proc(xbus, xpd); + return 0; +} + +static int FXS_card_zaptel_registration(xpd_t *xpd, bool on) +{ + xbus_t *xbus; + struct FXS_priv_data *priv; + int i; + + BUG_ON(!xpd); + xbus = xpd->xbus; + priv = xpd->priv; + BUG_ON(!xbus); + DBG("%s/%s: %s\n", xbus->busname, xpd->xpdname, (on)?"on":"off"); + if(on) { + do_led(xpd, ALL_LINES, LED_GREEN, LED_OFF); + for_each_enabled_line(xpd, i) { + DO_LED(priv,i,LED_GREEN,LED_ON); + mdelay(50); + } + for_each_enabled_line(xpd, i) { + DO_LED(priv,i,LED_GREEN,LED_OFF); + mdelay(50); + } + } else { + do_led(xpd, ALL_LINES, LED_RED, LED_OFF); + for_each_enabled_line(xpd, i) { + DO_LED(priv,i,LED_RED,LED_ON); + mdelay(50); + } + for_each_enabled_line(xpd, i) { + DO_LED(priv,i,LED_RED,LED_OFF); + mdelay(50); + } } -#endif return 0; } +int FXS_card_sethook(xbus_t *xbus, xpd_t *xpd, int pos, int hookstate) +{ + int ret = 0; + + DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, hookstate2str(hookstate)); + switch(hookstate) { + /* On-hook, off-hook: The PBX is playing a phone on an FXO line. + * Can be ignored for an FXS line + */ + case ZT_ONHOOK: + if(IS_SET(xpd->digital_inputs, pos)) { + NOTICE("%s: Trying to ONHOOK a digital input channel %d. Ignoring\n", __FUNCTION__, pos); + ret = -EINVAL; + break; + } + 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); + break; + } + xpd->ringing[pos] = 0; + DBG("%s/%s/%d: stop ringing\n", xbus->busname, xpd->xpdname, pos); +#if 1 // FIXME: Not needed -- verify + ret = CALL_XMETHOD(RING, xbus, xpd, pos, 0); // RING off +#endif + if(ret) { + DBG("ZT_ONHOOK(stop ring) Failed: ret=0x%02X\n", ret); + break; + } + break; + case ZT_START: + DBG("%s/%s/%d: fall through ZT_OFFHOOK\n", xbus->busname, xpd->xpdname, pos); + // Fall through + case ZT_OFFHOOK: + DBG("%s/%s/%d: ignoring (PHONE)\n", xbus->busname, xpd->xpdname, pos); + break; + case ZT_WINK: + WARN("No code yet\n"); + break; + case ZT_FLASH: + WARN("No code yet\n"); + break; + case ZT_RING: + DBG("%s/%s/%d: ringing[%d]=%d\n", xbus->busname, xpd->xpdname, pos, pos, xpd->ringing[pos]); + if(IS_SET(xpd->digital_inputs, pos)) { + NOTICE("%s: Trying to RING a digital input channel %d. Ignoring\n", __FUNCTION__, pos); + return -EINVAL; + } + if(IS_SET(xpd->digital_outputs, pos)) { + DBG("%s/%s/%d: digital output ON\n", xbus->busname, xpd->xpdname, pos); + ret = CALL_XMETHOD(RELAY_OUT, xpd->xbus, xpd, pos-8, 1); + return ret; + } + xpd->ringing[pos] = 1; + ret = CALL_XMETHOD(RING, xbus, xpd, pos, 1); // RING on + if(ret) { + DBG("ZT_RING Failed: ret=0x%02X\n", ret); + } + break; + case ZT_RINGOFF: + WARN("No code yet\n"); + break; + } + return ret; +} + /* * INPUT polling is done via SLIC register 0x06 (same as LEDS): * 7 6 5 4 3 2 1 0 @@ -164,22 +470,29 @@ static void poll_inputs(xbus_t *xbus, xpd_t *xpd) static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) { - static int rate_limit = 0; + static int rate_limit = 0; + struct FXS_priv_data *priv; + BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); if((rate_limit++ % 1000) == 0) { poll_inputs(xbus, xpd); } + handle_fxs_leds(xpd); return 0; } /*---------------- FXS: HOST COMMANDS -------------------------------------*/ -/* 0x0F */ HOSTCMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on) +static /* 0x0F */ HOSTCMD(FXS, CHAN_ENABLE, xpp_line_t lines, bool on) { int ret = 0; xpacket_t *pack; slic_cmd_t *sc; int len; + enum fxs_state value = (on) ? 0x01 : 0x00; + int i; BUG_ON(!xbus); BUG_ON(!xpd); @@ -190,47 +503,26 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) DBG("Channel Activation: 0x%4X %s\n", lines, (on) ? "on" : "off"); XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); - len = slic_cmd_direct_write(sc, lines, 0x40, (on)?0x01:0x00); + len = slic_cmd_direct_write(sc, lines, 0x40, value); pack->datalen = len; + for_each_line(xpd, i) + xpd->lasttxhook[i] = value; packet_send(xbus, pack); - return ret; -} - -/* 0x0F */ HOSTCMD(FXS, CHAN_POWER, xpp_line_t lines, bool on) -{ - int ret = 0; - xpacket_t *pack; - slic_cmd_t *sc; - int len; - - BUG_ON(!xbus); - BUG_ON(!xpd); - lines &= xpd->enabled_chans; // Ignore disabled channels - if(!lines) { - return 0; - } - DBG("Channel Power: 0x%04X %s\n", lines, (on) ? "up" : "down"); - XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); - sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); if(on) { - // Power up - len = slic_cmd_direct_write(sc, lines, 0x42, 0x06); + do_led(xpd, ALL_LINES, LED_GREEN, LED_ON); } else { - // Power down - len = slic_cmd_direct_write(sc, lines, 0x42, 0x00); + do_led(xpd, ALL_LINES, LED_GREEN, LED_OFF); } - pack->datalen = len; - - packet_send(xbus, pack); return ret; } -/* 0x0F */ HOSTCMD(FXS, CHAN_CID, xpp_line_t lines) +static /* 0x0F */ HOSTCMD(FXS, CHAN_CID, xpp_line_t lines) { int ret = 0; xpacket_t *pack; slic_cmd_t *sc; + int i; BUG_ON(!xbus); BUG_ON(!xpd); @@ -241,100 +533,56 @@ static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd) DBG("Channel CID: 0x%04X\n", lines); XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); - pack->datalen = slic_cmd_direct_write(sc, lines, 0x40, 0x02); + pack->datalen = slic_cmd_direct_write(sc, lines, 0x40, FXS_LINE_CID); packet_send(xbus, pack); + for_each_line(xpd, i) + xpd->lasttxhook[i] = FXS_LINE_CID; return ret; } -/* 0x0F */ HOSTCMD(FXS, RING, int pos, bool on) +static /* 0x0F */ HOSTCMD(FXS, RING, int pos, bool on) { int ret = 0; + struct FXS_priv_data *priv; xpacket_t *pack; slic_cmd_t *sc; xpp_line_t mask = (1 << pos); int len; + enum fxs_state value = (on) ? 0x04 : 0x01; BUG_ON(!xbus); BUG_ON(!xpd); + priv = xpd->priv; mask &= xpd->enabled_chans; // Ignore disabled channels if(!mask) { return 0; } - DBG("%s pos=%d %s\n", xpd->xpdname, pos, (on) ? "on" : "off"); + DBG("%s/%s/%d %s\n", xbus->busname, xpd->xpdname, pos, (on) ? "on" : "off"); + do_chan_power(xbus, xpd, BIT(pos), on); // Power up (for ring) XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); - len = slic_cmd_direct_write(sc, mask, 0x40, (on)?0x04:0x01); + len = slic_cmd_direct_write(sc, mask, 0x40, value); + xpd->lasttxhook[pos] = value; pack->datalen = len; packet_send(xbus, pack); + if(on) { + DO_BLINK(priv,pos,LED_GREEN,LED_BLINK); + } else { + if(IS_BLINKING(priv, pos, LED_GREEN)) + DO_BLINK(priv,pos,LED_GREEN,0); + } return ret; } -/* 0x0F */ HOSTCMD(FXS, SETHOOK, xpp_line_t hook_status) +static /* 0x0F */ HOSTCMD(FXS, SETHOOK, int pos, bool offhook) { - DBG("\n"); + BUG(); // Should never be called return 0; } -/* - * LED control is done via SLIC register 0x06: - * 7 6 5 4 3 2 1 0 - * +-----+-----+-----+-----+-----+-----+-----+-----+ - * | MR | MG | MB | R | OG | OB | G | B | - * +-----+-----+-----+-----+-----+-----+-----+-----+ - * - * B - BLUE LED (0 - OFF, 1 - ON) - * G - GREEN LED (0 - OFF, 1 - ON) - * OB - Output BLUE (this line is output) - * OG - Output GREEN (this line is output) - * R - RED LED (0 - OFF, 1 - ON) - * MB - Mask BLUE. (1 - B effect the BLUE LED) - * MR - Mask RED. (1 - R effect the RED LED) - * MG - Mask GREEN. (1 - G effect the GREEN LED) - * - * The BLUE LED (actually a relay out) is connected to line 0 and 4 only. - */ - -// GREEN RED BLUE -static const int led_mask[NUM_LEDS] = { BIT(7), BIT(6), BIT(5) }; -static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) }; - -/* 0x0F */ HOSTCMD(FXS, LED, xpp_line_t lines, byte which, bool on) -{ - int ret = 0; - xpacket_t *pack; - slic_cmd_t *sc; - int len; - int value; - int i; - - BUG_ON(!xbus); - BUG_ON(!xpd); - lines &= xpd->enabled_chans; // Ignore disabled channels - if(!lines) { - return 0; - } - DBG("LED: lines=0x%04X which=%d -- %s\n", lines, which, (on) ? "on" : "off"); - which = which % NUM_LEDS; - value = BIT(2) | BIT(3); - value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[which]); - if(on) - value |= led_vals[which]; - for(i = 0; i < CHANNELS_PERXPD; i++) { - if(!IS_SET(lines, i)) - continue; - XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); - sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); - len = slic_cmd_direct_write(sc, lines, 0x06, value); - DBG("LED pack: line=%d value=0x%04X\n", i, value); - pack->datalen = len; - packet_send(xbus, pack); - } - return ret; -} - -/* 0x0F */ HOSTCMD(FXS, RELAY_OUT, byte which, bool on) +static /* 0x0F */ HOSTCMD(FXS, RELAY_OUT, byte which, bool on) { int ret = 0; xpacket_t *pack; @@ -351,9 +599,9 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) }; which = which % ARRAY_SIZE(relay_channels); lines = BIT(relay_channels[which]); value = BIT(2) | BIT(3); - value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_mask[LED_BLUE]); + value |= ((BIT(5) | BIT(6) | BIT(7)) & ~led_register_mask[OUTPUT_RELAY]); if(on) - value |= led_vals[LED_BLUE]; + value |= led_register_vals[OUTPUT_RELAY]; XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); len = slic_cmd_direct_write(sc, lines, 0x06, value); @@ -364,7 +612,7 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) }; return ret; } -/* 0x0F */ HOSTCMD(FXS, SLIC_INIT) +static /* 0x0F */ HOSTCMD(FXS, SLIC_INIT) { int ret = 0; xpacket_t *pack; @@ -386,12 +634,12 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) }; pack->datalen = sizeof(xpp_line_t) + slic->len + 1; // dump_packet("SLIC", pack, print_dbg); packet_send(xbus, pack); - mdelay(10); // FIXME: check with Dima + mdelay(1); // FIXME: check with Dima } return ret; } -/* 0x0F */ HOSTCMD(FXS, SLIC_QUERY, int pos, byte reg_num) +static /* 0x0F */ HOSTCMD(FXS, SLIC_QUERY, int pos, byte reg_num) { int ret = 0; xpacket_t *pack; @@ -400,7 +648,7 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) }; BUG_ON(!xbus); BUG_ON(!xpd); - DBG("\n"); + // DBG("\n"); XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id); sc = &RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd); len = slic_cmd_direct_read(sc, BIT(pos), reg_num); @@ -416,31 +664,45 @@ static const int led_vals[NUM_LEDS] = { BIT(4), BIT(1), BIT(0) }; 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); + struct FXS_priv_data *priv; + int i; - if(!xpd) { - NOTICE("%s: received %s for non-existing xpd: %d\n", - __FUNCTION__, cmd->name, XPD_NUM(pack->content.addr)); - return -EPROTO; + BUG_ON(!xpd); + 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(!SPAN_REGISTERED(xpd)) { + NOTICE("%s: %s/%s is not registered. Skipping.\n", __FUNCTION__, xbus->busname, xpd->xpdname); + return -ENODEV; } - if(xpd->direction == TO_PHONE) { /* Hook state changes */ - DBG("%s (PHONE) sig_status=0x%04X\n", xpd->xpdname, sig_status); - xpp_check_hookstate(xpd, sig_status); - } else { /* TO_PSTN - line ring changes */ - unsigned long flags; - int i; - - DBG("%s (PSTN) sig_status=0x%04X\n", xpd->xpdname, sig_status); - spin_lock_irqsave(&xpd->lock, flags); - for(i = 0; i < xpd->channels; i++) { +#if 0 + Is this needed? + for_each_enabled_line(xpd, i) { + if(IS_SET(sig_toggles, i)) + do_chan_power(xpd->xbus, xpd, BIT(i), 0); // Power down (prevent overheating!!!) + } +#endif + 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)) { + struct zt_chan *chan = &xpd->span.chans[i]; + + xpd->ringing[i] = 0; // No more ringing... + DO_BLINK(priv,i,LED_GREEN,0); if(IS_SET(sig_status, i)) { - xpd->ringing[i] = RINGS_NUM*2; - zt_hooksig(&xpd->chans[i], ZT_RXSIG_OFFHOOK); + DBG("OFFHOOK: channo=%d\n", chan->channo); + DO_LED(priv,i,LED_GREEN,LED_ON); + BIT_SET(xpd->hookstate, i); + zt_hooksig(chan, ZT_RXSIG_OFFHOOK); } else { - zt_hooksig(&xpd->chans[i], ZT_RXSIG_ONHOOK); - xpd->ringing[i] = 0; + DBG("ONHOOK channo=%d\n", chan->channo); + DO_LED(priv,i,LED_GREEN,LED_OFF); + BIT_CLR(xpd->hookstate, i); + zt_hooksig(chan, ZT_RXSIG_ONHOOK); } } - spin_unlock_irqrestore(&xpd->lock, flags); } return 0; } @@ -460,52 +722,71 @@ HANDLER_DEF(FXS, SLIC_REPLY) spin_lock_irqsave(&xpd->lock, flags); priv = xpd->priv; BUG_ON(!priv); +#if 0 DBG("SLIC_REPLY: xpd #%d %s reg_num=0x%X, dataL=0x%X dataH=0x%X\n", xpd->id, (info->indirect)?"I":"D", info->reg_num, info->data_low, info->data_high); - priv->last_reply = *info; +#endif if(xpd->id == 0 && info->indirect == 0 && info->reg_num == 0x06) { /* Digital Inputs Poll Result */ int i; bool offhook = (info->data_low & 0x1) == 0; /* Map SLIC number into line number */ for(i = 0; i < ARRAY_SIZE(input_channels); i++) { - int channo = input_channels[i]; - int newchanno; + int channo = input_channels[i]; + int newchanno; + struct zt_chan *chan; if(IS_SET(lines, channo)) { newchanno = LINES_REGULAR + LINES_DIGI_OUT + i; BIT_CLR(lines, channo); BIT_SET(lines, newchanno); - phone_hook(xpd, newchanno, offhook); + chan = &xpd->span.chans[newchanno]; + xpd->ringing[newchanno] = 0; // Stop ringing. No leds for digital inputs. + if(offhook && !IS_SET(xpd->hookstate, newchanno)) { // OFFHOOK + DBG("OFFHOOK: channo=%d\n", chan->channo); + BIT_SET(xpd->hookstate, newchanno); + zt_hooksig(chan, ZT_RXSIG_OFFHOOK); + } else if(!offhook && IS_SET(xpd->hookstate, newchanno)) { // ONHOOK + DBG("ONHOOK channo=%d\n", chan->channo); + BIT_CLR(xpd->hookstate, newchanno); + zt_hooksig(chan, ZT_RXSIG_ONHOOK); + } } } } + + /* Update /proc info only if reply relate to the last slic read request */ + if(priv->requested_reply.indirect == info->indirect && + priv->requested_reply.reg_num == info->reg_num) { + priv->last_reply = *info; + } spin_unlock_irqrestore(&xpd->lock, flags); return 0; } xproto_table_t PROTO_TABLE(FXS) = { + .owner = THIS_MODULE, .entries = { /* Card Opcode */ XENTRY( FXS, SIG_CHANGED ), XENTRY( FXS, SLIC_REPLY ), }, .name = "FXS", - .type = XPD_TYPE(FXS), + .type = XPD_TYPE_FXS, .xops = { .card_new = FXS_card_new, .card_init = FXS_card_init, .card_remove = FXS_card_remove, + .card_zaptel_registration = FXS_card_zaptel_registration, + .card_sethook = FXS_card_sethook, .card_tick = FXS_card_tick, .RING = XPROTO_CALLER(FXS, RING), .SETHOOK = XPROTO_CALLER(FXS, SETHOOK), - .LED = XPROTO_CALLER(FXS, LED), .RELAY_OUT = XPROTO_CALLER(FXS, RELAY_OUT), .CHAN_ENABLE = XPROTO_CALLER(FXS, CHAN_ENABLE), - .CHAN_POWER = XPROTO_CALLER(FXS, CHAN_POWER), .CHAN_CID = XPROTO_CALLER(FXS, CHAN_CID), .SYNC_SOURCE = XPROTO_CALLER(GLOBAL, SYNC_SOURCE), @@ -519,7 +800,7 @@ static bool fxs_packet_is_valid(xpacket_t *pack) { const xproto_entry_t *xe; - DBG("\n"); + // DBG("\n"); xe = xproto_card_entry(&PROTO_TABLE(FXS), pack->content.opcode); return xe != NULL; } @@ -531,6 +812,56 @@ static void fxs_packet_dump(xpacket_t *pack) /*------------------------- SLIC Handling --------------------------*/ +static int proc_fxs_info_read(char *page, char **start, off_t off, int count, int *eof, void *data) +{ + int len = 0; + unsigned long flags; + xpd_t *xpd = data; + struct FXS_priv_data *priv; + int i; + int led; + + BUG_ON(!xpd); + spin_lock_irqsave(&xpd->lock, flags); + priv = xpd->priv; + BUG_ON(!priv); + len += sprintf(page + len, "\t%-17s: ", "Channel"); + for_each_line(xpd, i) { + if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i)) + len += sprintf(page + len, "%d ", i % 10); + } + len += sprintf(page + len, "\n"); + for(led = 0; led < NUM_LEDS; led++) { + len += sprintf(page + len, "LED #%d", led); + len += sprintf(page + len, "\n\t%-17s: ", "ledstate"); + for_each_line(xpd, i) { + if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i)) + len += sprintf(page + len, "%d ", IS_SET(priv->ledstate[led], i)); + } + len += sprintf(page + len, "\n\t%-17s: ", "ledcontrol"); + for_each_line(xpd, i) { + if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i)) + len += sprintf(page + len, "%d ", IS_SET(priv->ledcontrol[led], i)); + } + len += sprintf(page + len, "\n\t%-17s: ", "blinking"); + for_each_line(xpd, i) { + if(!IS_SET(xpd->digital_outputs, i) && !IS_SET(xpd->digital_inputs, i)) + len += sprintf(page + len, "%d ", IS_BLINKING(priv,i,led)); + } + len += sprintf(page + len, "\n"); + } + 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; +} + static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data) { int len = 0; @@ -570,7 +901,7 @@ static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, in * | * SLIC # */ -static int parse_slic_cmd(const char *buf, slic_cmd_t *sc) +static int parse_slic_cmd(const char *buf, slic_cmd_t *sc, slic_reply_t *requested_reply) { char op; /* [W]rite, [R]ead */ char reg_type; /* [D]irect, [I]ndirect */ @@ -588,9 +919,17 @@ static int parse_slic_cmd(const char *buf, slic_cmd_t *sc) if(reg_type == 'D' && ret == 7) { // DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num); ret = slic_cmd_direct_read(sc, lines, reg_num); + if(requested_reply) { + requested_reply->indirect = 0; + requested_reply->reg_num = reg_num; + } } else if(reg_type == 'I' && ret == 7) { // DBG("0x%X 0x%X 0x%X 0x%X %c %x\n", s1, s2, s3, s4, reg_type, reg_num); ret = slic_cmd_indirect_read(sc, lines, reg_num); + if(requested_reply) { + requested_reply->indirect = 1; + requested_reply->reg_num = reg_num; + } } else { NOTICE("%s: Bad read input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type); goto err; @@ -619,14 +958,16 @@ err: static int process_slic_cmdline(xpd_t *xpd, char *cmdline) { - xbus_t *xbus; - slic_cmd_t sc; - xpacket_t *pack; - char *p; - int len = strlen(cmdline); + xbus_t *xbus; + struct FXS_priv_data *priv; + slic_cmd_t sc; + xpacket_t *pack; + char *p; + int len = strlen(cmdline); BUG_ON(!xpd); xbus = xpd->xbus; + priv = xpd->priv; if((p = strchr(cmdline, '#')) != NULL) /* Truncate comments */ *p = '\0'; if((p = strchr(cmdline, ';')) != NULL) /* Truncate comments */ @@ -635,7 +976,7 @@ static int process_slic_cmdline(xpd_t *xpd, char *cmdline) ; if(*p == '\0') return 0; - len = parse_slic_cmd(p, &sc); + len = parse_slic_cmd(p, &sc, &priv->requested_reply); if(len < 0) return len; sc.lines &= xpd->enabled_chans; // Ignore disabled channels @@ -677,6 +1018,7 @@ static int proc_xpd_slic_write(struct file *file, const char __user *buffer, uns ret = process_slic_cmdline(xpd, buf); if(ret < 0) return ret; + mdelay(1); } return count; } @@ -684,7 +1026,7 @@ static int proc_xpd_slic_write(struct file *file, const char __user *buffer, uns int __init card_fxs_startup(void) { - INFO("%s revision %s\n", THIS_MODULE->name, revision); + INFO("%s revision %s\n", THIS_MODULE->name, ZAPTEL_VERSION); xproto_register(&PROTO_TABLE(FXS)); return 0; } @@ -697,7 +1039,8 @@ 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("$Id$"); +MODULE_VERSION(ZAPTEL_VERSION); +MODULE_ALIAS_XPD(XPD_TYPE_FXS); module_init(card_fxs_startup); module_exit(card_fxs_cleanup); |