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.c703
1 files changed, 703 insertions, 0 deletions
diff --git a/xpp/card_fxs.c b/xpp/card_fxs.c
new file mode 100644
index 0000000..d408647
--- /dev/null
+++ b/xpp/card_fxs.c
@@ -0,0 +1,703 @@
+/*
+ * Written by Oron Peled <oron@actcom.co.il>
+ * Copyright (C) 2004-2005, Xorcom
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include "xpd.h"
+#include "xproto.h"
+#include "xpp_zap.h"
+#include <linux/delay.h>
+
+static const char rcsid[] = "$Id$";
+static const char revision[] = "$Revision$";
+
+DEF_PARM(int, print_dbg, 1, "Print DBG statements"); /* must be before zap_debug.h */
+
+#define LINES_REGULAR 8
+#define LINES_DIGI_OUT 2
+#define LINES_DIGI_INP 4
+
+#define MASK_BITS(b) ((1U << (b)) - 1)
+
+#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))
+
+/*---------------- 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 bool fxs_packet_is_valid(xpacket_t *pack);
+static void fxs_packet_dump(xpacket_t *pack);
+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);
+
+#define S_(s,l,...) \
+ { \
+ .lines = s, \
+ { \
+ .len = l, \
+ .data = { __VA_ARGS__ }, \
+ } \
+ }
+struct slic_init_data {
+ xpp_line_t lines;
+ slic_data_t slic_data;
+} slic_init_data[] = {
+#include "slic_init.inc"
+};
+#undef S_
+
+#define PROC_SLIC_FNAME "slics"
+
+struct FXS_priv_data {
+ struct proc_dir_entry *xpd_slic;
+ slic_reply_t last_reply;
+};
+
+/*---------------- FXS: Methods -------------------------------------------*/
+
+static xpd_t *FXS_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision)
+{
+ xpd_t *xpd = NULL;
+ int channels = min(8, CHANNELS_PERXPD);
+
+ if(xpd_num == 0)
+ channels += 6; /* 2 DIGITAL OUTPUTS, 4 DIGITAL INPUTS */
+ xpd = xpd_alloc(sizeof(struct FXS_priv_data), xbus, xpd_num, proto_table, channels, revision);
+ if(!xpd)
+ return NULL;
+ if(xpd_num == 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;
+ return xpd;
+}
+
+static int FXS_card_init(xbus_t *xbus, xpd_t *xpd)
+{
+ struct FXS_priv_data *priv;
+
+ BUG_ON(!xpd);
+ priv = xpd->priv;
+ CALL_PROTO(FXS, SLIC_INIT, xbus, xpd);
+#ifdef CONFIG_PROC_FS
+ 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);
+ 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;
+}
+
+static int FXS_card_remove(xbus_t *xbus, xpd_t *xpd)
+{
+ struct FXS_priv_data *priv;
+
+ 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);
+ }
+#endif
+ return 0;
+}
+
+/*
+ * INPUT polling is done via SLIC register 0x06 (same as LEDS):
+ * 7 6 5 4 3 2 1 0
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ * | I1 | I3 | | | I2 | I4 | | |
+ * +-----+-----+-----+-----+-----+-----+-----+-----+
+ *
+ */
+static int input_channels[] = { 6, 7, 2, 3 }; // Slic numbers of input relays
+
+static void poll_inputs(xbus_t *xbus, xpd_t *xpd)
+{
+ int i;
+
+ for(i = 0; i < ARRAY_SIZE(input_channels); i++) {
+ int pos = input_channels[i];
+
+ CALL_PROTO(FXS, SLIC_QUERY, xbus, xpd, pos, 0x06);
+ }
+}
+
+static int FXS_card_tick(xbus_t *xbus, xpd_t *xpd)
+{
+ static int rate_limit = 0;
+
+ if((rate_limit++ % 1000) == 0) {
+ poll_inputs(xbus, xpd);
+ }
+ return 0;
+}
+
+/*---------------- FXS: HOST COMMANDS -------------------------------------*/
+
+/* 0x0F */ HOSTCMD(FXS, CHAN_ENABLE, 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 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);
+ pack->datalen = len;
+
+ 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);
+ } else {
+ // Power down
+ len = slic_cmd_direct_write(sc, lines, 0x42, 0x00);
+ }
+ pack->datalen = len;
+
+ packet_send(xbus, pack);
+ return ret;
+}
+
+/* 0x0F */ HOSTCMD(FXS, CHAN_CID, xpp_line_t lines)
+{
+ int ret = 0;
+ xpacket_t *pack;
+ slic_cmd_t *sc;
+
+ BUG_ON(!xbus);
+ BUG_ON(!xpd);
+ lines &= xpd->enabled_chans; // Ignore disabled channels
+ if(!lines) {
+ return 0;
+ }
+ 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);
+ packet_send(xbus, pack);
+ return ret;
+}
+
+
+/* 0x0F */ HOSTCMD(FXS, RING, int pos, bool on)
+{
+ int ret = 0;
+ xpacket_t *pack;
+ slic_cmd_t *sc;
+ xpp_line_t mask = (1 << pos);
+ int len;
+
+ BUG_ON(!xbus);
+ BUG_ON(!xpd);
+ mask &= xpd->enabled_chans; // Ignore disabled channels
+ if(!mask) {
+ return 0;
+ }
+ DBG("%s pos=%d %s\n", xpd->xpdname, pos, (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, mask, 0x40, (on)?0x04:0x01);
+ pack->datalen = len;
+
+ packet_send(xbus, pack);
+ return ret;
+}
+
+/* 0x0F */ HOSTCMD(FXS, SETHOOK, xpp_line_t hook_status)
+{
+ DBG("\n");
+ 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)
+{
+ int ret = 0;
+ xpacket_t *pack;
+ slic_cmd_t *sc;
+ int len;
+ int value;
+ xpp_line_t lines;
+ int relay_channels[] = { 0, 4 };
+
+ BUG_ON(!xbus);
+ BUG_ON(!xpd);
+
+ DBG("RELAY_OUT: which=%d -- %s\n", which, (on) ? "on" : "off");
+ 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]);
+ if(on)
+ value |= led_vals[LED_BLUE];
+ 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("RELAY_OUT pack: line=%d value=0x%04X\n", lines, value);
+ pack->datalen = len;
+ packet_send(xbus, pack);
+ return ret;
+}
+
+/* 0x0F */ HOSTCMD(FXS, SLIC_INIT)
+{
+ int ret = 0;
+ xpacket_t *pack;
+ slic_data_t *slic;
+ struct slic_init_data *source;
+ int i;
+
+ BUG_ON(!xbus);
+ BUG_ON(!xpd);
+ DBG("INITIALIZING SLIC\n");
+ for(i = 0; i < ARRAY_SIZE(slic_init_data); i++) {
+ source = &slic_init_data[i];
+ XPACKET_NEW(pack, xbus, FXS, SLIC_INIT, xpd->id);
+ RPACKET_FIELD(pack, FXS, SLIC_INIT, lines) = source->lines;
+
+ slic = &RPACKET_FIELD(pack, FXS, SLIC_INIT, slic_data);
+ slic->len = source->slic_data.len;
+ memcpy(slic->data, source->slic_data.data, source->slic_data.len);
+ 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
+ }
+ return ret;
+}
+
+/* 0x0F */ HOSTCMD(FXS, SLIC_QUERY, int pos, byte reg_num)
+{
+ int ret = 0;
+ xpacket_t *pack;
+ slic_cmd_t *sc;
+ int len;
+
+ BUG_ON(!xbus);
+ BUG_ON(!xpd);
+ 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);
+
+ pack->datalen = len;
+
+ packet_send(xbus, pack);
+ return ret;
+}
+
+/*---------------- FXS: Astribank Reply Handlers --------------------------*/
+
+HANDLER_DEF(FXS, SIG_CHANGED)
+{
+ xpp_line_t sig_status = RPACKET_FIELD(pack, FXS, SIG_CHANGED, sig_status);
+
+ if(!xpd) {
+ NOTICE("%s: received %s for non-existing xpd: %d\n",
+ __FUNCTION__, cmd->name, XPD_NUM(pack->content.addr));
+ return -EPROTO;
+ }
+ 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(IS_SET(sig_status, i)) {
+ xpd->ringing[i] = RINGS_NUM*2;
+ zt_hooksig(&xpd->chans[i], ZT_RXSIG_OFFHOOK);
+ } else {
+ zt_hooksig(&xpd->chans[i], ZT_RXSIG_ONHOOK);
+ xpd->ringing[i] = 0;
+ }
+ }
+ spin_unlock_irqrestore(&xpd->lock, flags);
+ }
+ return 0;
+}
+
+HANDLER_DEF(FXS, SLIC_REPLY)
+{
+ slic_reply_t *info = &RPACKET_FIELD(pack, FXS, SLIC_REPLY, info);
+ xpp_line_t lines = RPACKET_FIELD(pack, FXS, SLIC_REPLY, lines);
+ unsigned long flags;
+ struct FXS_priv_data *priv;
+
+ if(!xpd) {
+ NOTICE("%s: received %s for non-existing xpd: %d\n",
+ __FUNCTION__, cmd->name, XPD_NUM(pack->content.addr));
+ return -EPROTO;
+ }
+ spin_lock_irqsave(&xpd->lock, flags);
+ priv = xpd->priv;
+ BUG_ON(!priv);
+ 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;
+ 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;
+
+ 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);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&xpd->lock, flags);
+ return 0;
+}
+
+
+xproto_table_t PROTO_TABLE(FXS) = {
+ .entries = {
+ /* Card Opcode */
+ XENTRY( FXS, SIG_CHANGED ),
+ XENTRY( FXS, SLIC_REPLY ),
+ },
+ .name = "FXS",
+ .type = XPD_TYPE(FXS),
+ .xops = {
+ .card_new = FXS_card_new,
+ .card_init = FXS_card_init,
+ .card_remove = FXS_card_remove,
+ .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),
+ .PCM_WRITE = XPROTO_CALLER(GLOBAL, PCM_WRITE),
+ },
+ .packet_is_valid = fxs_packet_is_valid,
+ .packet_dump = fxs_packet_dump,
+};
+
+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);
+ return xe != NULL;
+}
+
+static void fxs_packet_dump(xpacket_t *pack)
+{
+ DBG("\n");
+}
+
+/*------------------------- SLIC Handling --------------------------*/
+
+static int proc_xpd_slic_read(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+ int len = 0;
+ unsigned long flags;
+ xpd_t *xpd = data;
+ slic_reply_t *info;
+ struct FXS_priv_data *priv;
+
+ BUG_ON(!xpd);
+ spin_lock_irqsave(&xpd->lock, flags);
+ priv = xpd->priv;
+ BUG_ON(!priv);
+ info = &priv->last_reply;
+ 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, "SLIC_REPLY: %s reg_num=0x%X, dataH=0x%X dataL=0x%X\n",
+ (info->indirect)?"I":"D",
+ info->reg_num, info->data_high, info->data_low);
+ 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;
+}
+
+/*
+ * Direct/Indirect
+ * v
+ * FF FF FF FF WD 06 1
+ * ^---------^ ^ Reg
+ * | Write/Read
+ * |
+ * SLIC #
+ */
+static int parse_slic_cmd(const char *buf, slic_cmd_t *sc)
+{
+ char op; /* [W]rite, [R]ead */
+ char reg_type; /* [D]irect, [I]ndirect */
+ int s1, s2, s3, s4;
+ int reg_num;
+ int data_low, data_high;
+ xpp_line_t lines;
+ int ret;
+
+ ret = sscanf(buf, "%x %x %x %x %c%c %x %x %x",
+ &s1, &s2, &s3, &s4, &op, &reg_type, &reg_num, &data_high, &data_low);
+ lines = (s4 << 24) | (s3 << 16) | (s2 << 8) | (s1);
+ switch(op) {
+ case 'R':
+ 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);
+ } 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);
+ } else {
+ NOTICE("%s: Bad read input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
+ goto err;
+ }
+ break;
+ case 'W':
+ if(reg_type == 'D' && ret == 8) {
+ // DBG("0x%X 0x%X 0x%X 0x%X %c %x %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high);
+ ret = slic_cmd_direct_write(sc, lines, reg_num, data_high);
+ } else if(reg_type == 'I' && ret == 9) {
+ // DBG("0x%X 0x%X 0x%X 0x%X %c %x %X %X\n", s1, s2, s3, s4, reg_type, reg_num, data_high, data_low);
+ ret = slic_cmd_indirect_write(sc, lines, reg_num, data_low, data_high);
+ } else {
+ NOTICE("%s: Bad write input: ret=%d buf='%s' reg_type=%c\n", __FUNCTION__, ret, buf, reg_type);
+ goto err;
+ }
+ break;
+ default:
+ NOTICE("%s: Bad input: ret=%d buf='%s' op=%c\n", __FUNCTION__, ret, buf, op);
+ goto err;
+ }
+ return ret;
+err:
+ return -EINVAL;
+}
+
+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);
+
+ 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;
+ len = parse_slic_cmd(p, &sc);
+ if(len < 0)
+ return len;
+ sc.lines &= xpd->enabled_chans; // Ignore disabled channels
+ if(!sc.lines) {
+ NOTICE("%s: no enabled channels are marked. Skip.\n", __FUNCTION__);
+ return 0;
+ }
+ dump_slic_cmd("WRITE_SLIC", &sc);
+ XPACKET_NEW(pack, xbus, FXS, SLIC_WRITE, xpd->id);
+ RPACKET_FIELD(pack, FXS, SLIC_WRITE, slic_cmd) = sc;
+ pack->datalen = len;
+ packet_send(xbus, pack);
+ return 0;
+}
+
+static int proc_xpd_slic_write(struct file *file, const char __user *buffer, unsigned long count, void *data)
+{
+ xpd_t *xpd = data;
+ const int LINE_LEN = 500;
+ char buf[LINE_LEN];
+ char *p;
+ int i;
+ int ret;
+
+ BUG_ON(!xpd);
+ for(i = 0; i < count; /* noop */) {
+ for(p = buf; p < buf + LINE_LEN; 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 + LINE_LEN)
+ return -E2BIG;
+ *p = '\0';
+ ret = process_slic_cmdline(xpd, buf);
+ if(ret < 0)
+ return ret;
+ }
+ return count;
+}
+
+
+int __init card_fxs_startup(void)
+{
+ INFO("%s revision %s\n", THIS_MODULE->name, revision);
+ xproto_register(&PROTO_TABLE(FXS));
+ return 0;
+}
+
+void __exit card_fxs_cleanup(void)
+{
+ xproto_unregister(&PROTO_TABLE(FXS));
+}
+
+MODULE_DESCRIPTION("XPP FXS Card Driver");
+MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("$Id$");
+
+module_init(card_fxs_startup);
+module_exit(card_fxs_cleanup);