diff options
author | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-02-15 02:26:14 +0000 |
---|---|---|
committer | kpfleming <kpfleming@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2006-02-15 02:26:14 +0000 |
commit | a9ababa28c7ccedaf4be1501d009f99d9a7ba580 (patch) | |
tree | b9259934f16b50693c73f9300287a7f92936837c /xpp/card_fxs.c | |
parent | 590e1c07e60d8564388533bb85deee9531df4893 (diff) |
Merged revisions 949 via svnmerge from
https://origsvn.digium.com/svn/zaptel/branches/1.2
........
r949 | kpfleming | 2006-02-14 20:24:18 -0600 (Tue, 14 Feb 2006) | 2 lines
initial import of Xorcom Astribank driver (issue #6452, with minor mods)
........
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@950 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'xpp/card_fxs.c')
-rw-r--r-- | xpp/card_fxs.c | 703 |
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, ®_type, ®_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); |