diff options
Diffstat (limited to 'xpp')
-rw-r--r-- | xpp/.version | 2 | ||||
-rw-r--r-- | xpp/Makefile | 2 | ||||
-rw-r--r-- | xpp/card_bri.c | 1360 | ||||
-rw-r--r-- | xpp/card_bri.h | 40 | ||||
-rw-r--r-- | xpp/card_fxo.c | 3 |
5 files changed, 1402 insertions, 5 deletions
diff --git a/xpp/.version b/xpp/.version index 0a25737..bf031d4 100644 --- a/xpp/.version +++ b/xpp/.version @@ -1 +1 @@ -trunk-r3489 +trunk-r3495 diff --git a/xpp/Makefile b/xpp/Makefile index 205fd76..2162ab3 100644 --- a/xpp/Makefile +++ b/xpp/Makefile @@ -1,4 +1,5 @@ ZAPTEL_DIR = $(SUBDIRS) + EXTRA_CFLAGS = $(XPP_LOCAL_CFLAGS) \ -g3 -I$(ZAPTEL_DIR) \ -DDEBUG \ @@ -13,7 +14,6 @@ ifneq (,$(shell grep -w echo_can_state_t $(ZAPTEL_DIR)/zaptel.h)) EXTRA_CFLAGS += -DZAPTEL_EC_TYPEDEF endif - obj-m += xpp.o xpd_fxs.o xpd_fxo.o HAS_BRISTUFF := $(shell cpp $(CPPFLAGS) -dM $(ZAPTEL_DIR)/zconfig.h | sed -n 's/^.*CONFIG_ZAPATA_BRI_DCHANS/y/p') diff --git a/xpp/card_bri.c b/xpp/card_bri.c new file mode 100644 index 0000000..fecaee9 --- /dev/null +++ b/xpp/card_bri.c @@ -0,0 +1,1360 @@ +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2006, Xorcom + * + * Parts derived from Cologne demo driver for the chip. + * + * 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 <linux/delay.h> +#include "xpd.h" +#include "xproto.h" +#include "xpp_zap.h" +#include "card_bri.h" +#include "zap_debug.h" +#include "xpd.h" +#include "xbus-core.h" + +static const char rcsid[] = "$Id$"; + +#ifndef CONFIG_ZAPATA_BRI_DCHANS +#error CONFIG_ZAPATA_BRI_DCHANS is not defined +#endif + +DEF_PARM(int, print_dbg, 0, 0600, "Print DBG statements"); /* must be before zap_debug.h */ +DEF_PARM(uint, poll_interval, 100, 0600, "Poll channel state interval in milliseconds (0 - disable)"); + +enum xhfc_states { + ST_RESET = 0, /* G/F0 */ + /* TE */ + ST_TE_SENSING = 2, /* F2 */ + ST_TE_DEACTIVATED = 3, /* F3 */ + ST_TE_SIGWAIT = 4, /* F4 */ + ST_TE_IDENT = 5, /* F5 */ + ST_TE_SYNCED = 6, /* F6 */ + ST_TE_ACTIVATED = 7, /* F7 */ + ST_TE_LOST_FRAMING = 8, /* F8 */ + /* NT */ + ST_NT_DEACTIVATED = 1, /* G1 */ + ST_NT_ACTIVATING = 2, /* G2 */ + ST_NT_ACTIVATED = 3, /* G3 */ + ST_NT_DEACTIVTING = 4, /* G4 */ +}; + +static const char *xhfc_state_name(xpd_type_t xpd_type, enum xhfc_states state) +{ + const char *p; + +#define _E(x) [ST_ ## x] = #x + static const char *te_names[] = { + _E(RESET), + _E(TE_SENSING), + _E(TE_DEACTIVATED), + _E(TE_SIGWAIT), + _E(TE_IDENT), + _E(TE_SYNCED), + _E(TE_ACTIVATED), + _E(TE_LOST_FRAMING), + }; + static const char *nt_names[] = { + _E(RESET), + _E(NT_DEACTIVATED), + _E(NT_ACTIVATING), + _E(NT_ACTIVATED), + _E(NT_DEACTIVTING), + }; +#undef _E + if(xpd_type == XPD_TYPE_BRI_TE) { + if ((state < ST_RESET) || (state > ST_TE_LOST_FRAMING)) + p = "TE ???"; + else + p = te_names[state]; + } else { + if ((state < ST_RESET) || (state > ST_NT_DEACTIVTING)) + p = "NT ???"; + else + p = nt_names[state]; + } + return p; +} + +/* xhfc Layer1 physical commands */ +#define HFC_L1_ACTIVATE_TE 0x01 +#define HFC_L1_FORCE_DEACTIVATE_TE 0x02 +#define HFC_L1_ACTIVATE_NT 0x03 +#define HFC_L1_DEACTIVATE_NT 0x04 + +#define HFC_L1_ACTIVATING 1 +#define HFC_L1_ACTIVATED 2 +#define NT_T1_COUNT 25 /* number of 4ms interrupts for G2 timeout */ +#define HFC_TIMER_T3 8000 /* 8s activation timer T3 */ +#define HFC_TIMER_T4 500 /* 500ms deactivation timer T4 */ +#define HFC_TIMER_OFF -1 /* timer disabled */ + +#define A_SU_WR_STA 0x30 /* ST/Up state machine register */ +#define V_SU_LD_STA 0x10 +#define V_SU_ACT 0x60 /* start activation/deactivation */ +#define STA_DEACTIVATE 0x40 /* start deactivation in A_SU_WR_STA */ +#define STA_ACTIVATE 0x60 /* start activation in A_SU_WR_STA */ +#define V_SU_SET_G2_G3 0x80 + +#define A_SU_RD_STA 0x30 +typedef union { + struct { + byte v_su_sta:4; + byte v_su_fr_sync:1; + byte v_su_t2_exp:1; + byte v_su_info0:1; + byte v_g2_g3:1; + } bits; + byte reg; +} su_rd_sta_t; + +#define BRI_DCHAN_SIGCAP ( \ + ZT_SIG_EM | \ + ZT_SIG_CLEAR | \ + ZT_SIG_FXSLS | \ + ZT_SIG_FXSGS | \ + ZT_SIG_FXSKS | \ + ZT_SIG_FXOLS | \ + ZT_SIG_FXOGS | \ + ZT_SIG_FXOKS | \ + ZT_SIG_CAS | \ + ZT_SIG_SF \ + ) +#define BRI_BCHAN_SIGCAP ZT_SIG_CLEAR + + +/*---------------- BRI Protocol Commands ----------------------------------*/ + +static int write_state_register(xpd_t *xpd, byte value); +static bool bri_packet_is_valid(xpacket_t *pack); +static void bri_packet_dump(const char *msg, xpacket_t *pack); +static int proc_bri_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); + +#define PROC_REGISTER_FNAME "slics" +#define PROC_BRI_INFO_FNAME "bri_info" + +#define VALID_CHIPSEL(x) ((x) == 0) + +enum led_state { + BRI_LED_OFF = 0x0, + BRI_LED_ON = 0x1, + BRI_LED_BLINK_SLOW = 0x2, /* 1/2 a second blink cycle */ + BRI_LED_BLINK_FAST = 0x3 /* 1/4 a second blink cycle */ +}; + +enum bri_led_names { + GREEN_LED = 0, + RED_LED = 1 +}; + +#define NUM_LEDS 2 +#define LED_TICKS 100 + +struct bri_leds { + byte state:2; + byte led_sel:1; /* 0 - GREEN, 1 - RED */ + byte reserved:5; +}; + +#ifndef MAX_DFRAME_LEN_L1 +#define MAX_DFRAME_LEN_L1 300 +#endif + +#define DCHAN_BUFSIZE MAX_DFRAME_LEN_L1 + +struct BRI_priv_data { + struct proc_dir_entry *regfile; + struct proc_dir_entry *bri_info; + su_rd_sta_t state_register; + bool initialized; + int t3; /* timer 3 for activation */ + int t4; /* timer 4 for deactivation */ + ulong l1_flags; + bool reg30_good; + ulong last_reg30_reply; + + /* + * D-Chan: buffers + extra state info. + */ + int dchan_r_idx; + byte dchan_rbuf[DCHAN_BUFSIZE]; + byte dchan_tbuf[DCHAN_BUFSIZE]; + bool txframe_begin; + + reg_cmd_t requested_reply; + reg_cmd_t last_reply; + uint tick_counter; + uint poll_counter; + uint drop_counter; + enum led_state ledstate[NUM_LEDS]; + enum led_state ledcontrol[NUM_LEDS]; +}; + +xproto_table_t PROTO_TABLE(BRI_NT); +xproto_table_t PROTO_TABLE(BRI_TE); + + +DEF_RPACKET_DATA(BRI, SET_LED, /* Set one of the LED's */ + struct bri_leds bri_leds; + ); + +static /* 0x33 */ DECLARE_CMD(BRI, SET_LED, bool red_led, enum led_state to_led_state); +static /* 0x0F */ DECLARE_CMD(BRI, REGISTER_REQUEST, byte chipsel, bool writing, bool do_subreg, byte regnum, byte subreg, byte data_low, byte data_high); + +#define DEBUG_BUF_SIZE (100) +static void dump_hex_buf(xpd_t *xpd, char *msg, byte *buf, int len) +{ + char debug_buf[DEBUG_BUF_SIZE + 1]; + int i; + char *p; + xbus_t *xbus = xpd->xbus; + + if (len >= DEBUG_BUF_SIZE) { + ERR("%s: dumped buffer too long. Length: %d, Maximum allowed: %d.\n", + __FUNCTION__, len, DEBUG_BUF_SIZE); + return; + } + p = debug_buf; + *p = '\0'; + for(i=0; i<len; i++) + p += snprintf(p, DEBUG_BUF_SIZE - (p - debug_buf), "%02X ", buf[i]); + printk(KERN_DEBUG "%s/%s: %s[0..%d]: %s\n", xbus->busname, xpd->xpdname, msg, len-1, debug_buf); +} + +static void dump_dchan_packet(xpd_t *xpd, bool transmit, byte *buf, int len) +{ + struct BRI_priv_data *priv; + char msgbuf[MAX_PROC_WRITE]; + char ftype = '?'; + char *direction; + int frame_begin; + + priv = xpd->priv; + BUG_ON(!priv); + if(transmit) { + direction = "TX"; + frame_begin = priv->txframe_begin; + } else { + direction = "RX"; + frame_begin = 1; + } + if(frame_begin) { /* Packet start */ + if(!IS_SET(buf[0], 7)) + ftype = 'I'; /* Information */ + else if(IS_SET(buf[0], 7) && !IS_SET(buf[0], 6)) + ftype = 'S'; /* Supervisory */ + else if(IS_SET(buf[0], 7) && IS_SET(buf[0], 6)) + ftype = 'U'; /* Unnumbered */ + else + NOTICE("Unknown frame type 0x%X\n", buf[0]); + + snprintf(msgbuf, MAX_PROC_WRITE, "D-Chan %s = (%c) ", direction, ftype); + } else { + snprintf(msgbuf, MAX_PROC_WRITE, "D-Chan %s = ", direction); + } + dump_hex_buf(xpd, msgbuf, buf, len); +} + +/* + * D-Chan receive + */ +static int rx_dchan(xpd_t *xpd, reg_cmd_t *regcmd) +{ + xbus_t *xbus; + struct BRI_priv_data *priv; + byte *src; + byte *dst; + byte *dchan_buf; + struct zt_chan *dchan; + uint len; + bool eoframe; + int idx; + int ret = 0; + + src = REG_XDATA(regcmd); + len = regcmd->bytes; + eoframe = regcmd->eoframe; + if(len <= 0) + return 0; + if(!SPAN_REGISTERED(xpd)) /* Nowhere to copy data */ + return 0; + BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); + xbus = xpd->xbus; +#ifdef XPP_DEBUGFS + xbus_log(xbus, xpd, 0, regcmd, sizeof(reg_cmd_t)); /* 0 = RX */ +#endif + dchan = &xpd->span.chans[2]; + if(!IS_SET(xpd->offhook, 2)) { /* D-chan is used? */ + static int rate_limit; + + if((rate_limit++ % 1000) == 0) + DBG("%s/%s: D-Chan unused\n", xbus->busname, xpd->xpdname); + dchan->bytes2receive = 0; + dchan->bytes2transmit = 0; + goto out; + } + dchan_buf = dchan->readchunk; + idx = priv->dchan_r_idx; + if(idx + len >= DCHAN_BUFSIZE) { + ERR("%s/%s: D-Chan RX overflow: %d\n", xbus->busname, xpd->xpdname, idx); + dump_hex_buf(xpd, " current packet", src, len); + dump_hex_buf(xpd, " dchan_buf", dchan_buf, idx); + ret = -ENOSPC; + if(eoframe) + goto drop; + goto out; + } + dst = dchan_buf + idx; + idx += len; + priv->dchan_r_idx = idx; + memcpy(dst, src, len); + if(!eoframe) + goto out; + if(idx < 4) { + NOTICE("%s/%s: D-Chan RX short frame (idx=%d)\n", xbus->busname, xpd->xpdname, idx); + dump_hex_buf(xpd, "D-Chan RX: current packet", src, len); + dump_hex_buf(xpd, "D-Chan RX: chan_buf", dchan_buf, idx); + ret = -EPROTO; + goto drop; + } + if(dchan_buf[idx-1]) { + NOTICE("%s/%s: D-Chan RX Bad checksum: [%02X:%02X=%02X] (%d)\n", + xbus->busname, xpd->xpdname, + dchan_buf[idx-3], dchan_buf[idx-2], dchan_buf[idx-1], dchan_buf[idx-1]); + dump_hex_buf(xpd, "D-Chan RX: current packet", src, len); + dump_hex_buf(xpd, "D-Chan RX: chan_buf", dchan_buf, idx); + ret = -EPROTO; + goto drop; + } + if(print_dbg) + dump_dchan_packet(xpd, 0, dchan_buf, idx /* - 3 */); /* Print checksum? */ + /* + * Tell Zaptel that we received idx-1 bytes. They include the data and a 2-byte checksum. + * The last byte (that we don't pass on) is 0 if the checksum is correct. If it were wrong, + * we would drop the packet in the "if(dchan_buf[idx-1])" above. + */ + dchan->bytes2receive = idx - 1; + dchan->eofrx = 1; +drop: + priv->dchan_r_idx = 0; +out: + return ret; +} + +static int send_bri_multibyte(xpd_t *xpd, byte *buf, int len, bool eoftx) +{ + xbus_t *xbus = xpd->xbus; + xframe_t *xframe; + xpacket_t *pack; + reg_cmd_t *reg_cmd; + + BUG_ON(len < 0); + /* + * Zero length multibyte is legal and has special meaning for the + * firmware: + * eoftx==1: Start sending us D-channel packets. + * eoftx==0: Stop sending us D-channel packets. + */ + if(len > MULTIBYTE_MAX_LEN) { + ERR("%s: len=%d is too long. dropping.\n", __FUNCTION__, len); + return -EINVAL; + } + XFRAME_NEW(xframe, pack, xbus, BRI, REGISTER_REQUEST, xpd->id); + reg_cmd = &RPACKET_FIELD(pack, BRI, REGISTER_REQUEST, reg_cmd); + reg_cmd->bytes = len; + reg_cmd->eoframe = eoftx; + reg_cmd->multibyte = 1; + if(len > 0) { + memcpy(REG_XDATA(reg_cmd), (byte *)buf, len); + } else { + DBG("Magic Packet (eoftx=%d)\n", eoftx); + } +#ifdef XPP_DEBUGFS + xbus_log(xbus, xpd, 1, reg_cmd, sizeof(reg_cmd_t)); /* 1 = TX */ +#else + if(print_dbg) + dump_xframe("SEND_BRI_MULTI", xbus, xframe); +#endif + return xframe_send(xbus, xframe); +} + +/* + * D-Chan transmit + */ +static int tx_dchan(xpd_t *xpd) +{ + struct BRI_priv_data *priv; + struct zt_chan *dchan; + int len; + int eoframe; + + priv = xpd->priv; + BUG_ON(!priv); + if(xpd->type == XPD_TYPE_BRI_TE) { + static int rate_limit; + + if (priv->t3 > HFC_TIMER_OFF) { + /* timer expired ? */ + if (--priv->t3 == 0) { + if ((rate_limit % 1003) >= 5) + DBG("%s/%s: T3 expired\n", xpd->xbus->busname, xpd->xpdname); + priv->t3 = HFC_TIMER_OFF; + clear_bit(HFC_L1_ACTIVATING, &priv->l1_flags); + CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 0); /* Deactivate TE */ + } + } + if (priv->t4 > HFC_TIMER_OFF) { + /* timer expired ? */ + if (--priv->t4 == 0) { + if ((rate_limit % 1003) >= 5) + DBG("%s/%s: T4 expired\n", xpd->xbus->busname, xpd->xpdname); + priv->t4 = HFC_TIMER_OFF; + } + } + rate_limit++; + } + if(!SPAN_REGISTERED(xpd) || !(xpd->span.flags & ZT_FLAG_RUNNING)) + return 0; + dchan = &xpd->chans[2]; + len = dchan->bytes2transmit; /* dchan's hdlc package len */ + eoframe = dchan->eoftx; /* dchan's end of frame */ + dchan->bytes2transmit = 0; + dchan->eoftx = 0; + dchan->bytes2receive = 0; + dchan->eofrx = 0; + if(len <= 0) + return 0; /* Nothing to transmit on D channel */ + if(len > MULTIBYTE_MAX_LEN) { + ERR("%s: len=%d. need to split. Unimplemented.\n", __FUNCTION__, len); + return -EINVAL; + } + if(!test_bit(HFC_L1_ACTIVATED, &priv->l1_flags) && !test_bit(HFC_L1_ACTIVATING, &priv->l1_flags)) { + CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 1); /* Kick transmitter */ + return 0; + } + if(print_dbg) + dump_dchan_packet(xpd, 1, priv->dchan_tbuf, len); + if(eoframe) + priv->txframe_begin = 1; + else + priv->txframe_begin = 0; + return send_bri_multibyte(xpd, priv->dchan_tbuf, len, eoframe); +} + +/*---------------- BRI: Methods -------------------------------------------*/ + +static xpd_t *BRI_card_new(xbus_t *xbus, int xpd_num, const xproto_table_t *proto_table, byte revision) +{ + xpd_t *xpd = NULL; + int channels = min(3, CHANNELS_PERXPD); + + DBG("\n"); + + xpd = xpd_alloc(sizeof(struct BRI_priv_data), xbus, xpd_num, proto_table, channels, revision); + if(!xpd) + return NULL; + xpd->direction = (proto_table == &PROTO_TABLE(BRI_NT)) ? TO_PHONE : TO_PSTN; + xpd->revision = revision; + return xpd; +} + +static void clean_proc(xbus_t *xbus, xpd_t *xpd) +{ + struct BRI_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; + DBG("%s/%s\n", xbus->busname, xpd->xpdname); +#ifdef CONFIG_PROC_FS + if(priv->regfile) { + DBG("Removing registers file for %s/%s\n", xbus->busname, xpd->xpdname); + priv->regfile->data = NULL; + remove_proc_entry(PROC_REGISTER_FNAME, xpd->proc_xpd_dir); + } + if(priv->bri_info) { + DBG("Removing xpd BRI_INFO file %s/%s\n", xbus->busname, xpd->xpdname); + remove_proc_entry(PROC_BRI_INFO_FNAME, xpd->proc_xpd_dir); + } +#endif +} + +static int BRI_card_init(xbus_t *xbus, xpd_t *xpd) +{ + struct BRI_priv_data *priv; + int ret = 0; + + DBG("\n"); + + BUG_ON(!xpd); + priv = xpd->priv; +#ifdef CONFIG_PROC_FS + DBG("Creating BRI_INFO file for %s/%s\n", xbus->busname, xpd->xpdname); + priv->bri_info = create_proc_read_entry(PROC_BRI_INFO_FNAME, 0444, xpd->proc_xpd_dir, proc_bri_info_read, xpd); + if(!priv->bri_info) { + ERR("Failed to create proc '%s' for %s/%s\n", PROC_BRI_INFO_FNAME, xbus->busname, xpd->xpdname); + ret = -ENOENT; + goto err; + } + priv->bri_info->owner = THIS_MODULE; + DBG("Creating registers file for %s/%s\n", xbus->busname, xpd->xpdname); + priv->regfile = create_proc_entry(PROC_REGISTER_FNAME, 0644, xpd->proc_xpd_dir); + if(!priv->regfile) { + ERR("Failed to create proc file for registers of %s/%s\n", xbus->busname, xpd->xpdname); + 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 + ret = run_initialize_registers(xpd); + if(ret < 0) + goto err; + /* + * FPGA firmware limitation: + * Force HOST sync *before* sending PCM + */ + CALL_PROTO(GLOBAL, SYNC_SOURCE, xbus, NULL, 1, 0); + DBG("done: %s/%s\n", xbus->busname, xpd->xpdname); + priv->initialized = 1; + CALL_PROTO(BRI, SET_LED, xbus, xpd, GREEN_LED, BRI_LED_ON); + CALL_PROTO(BRI, SET_LED, xbus, xpd, RED_LED, BRI_LED_ON); + msleep(10); + return 0; +err: + clean_proc(xbus, xpd); + ERR("%s/%s: Failed initializing registers (%d)\n", xbus->busname, xpd->xpdname, ret); + return ret; +} + +static int BRI_card_remove(xbus_t *xbus, xpd_t *xpd) +{ + struct BRI_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; + DBG("%s/%s\n", xbus->busname, xpd->xpdname); + clean_proc(xbus, xpd); + return 0; +} + +static int BRI_card_zaptel_preregistration(xpd_t *xpd, bool on) +{ + xbus_t *xbus; + struct BRI_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) { + /* Nothing to do yet */ + return 0; + } + snprintf(xpd->span.desc, MAX_SPANDESC, "Xorcom XPD #%d/%d: %s", + xbus->num, xpd->id, + xpd->xproto->name + ); + xpd->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_CCS; + xpd->span.deflaw = ZT_LAW_ALAW; + BIT_SET(xpd->digital_signalling, 2); /* D-Channel */ + for_each_line(xpd, i) { + struct zt_chan *cur_chan = &xpd->chans[i]; + + DBG("setting BRI channel %d\n", i); + snprintf(cur_chan->name, MAX_CHANNAME, "XPP_%s/%d/%d/%d", + xpd->xproto->name, xbus->num, xpd->id, i); + cur_chan->chanpos = i + 1; + cur_chan->pvt = xpd; + if(i == 2) { /* D-CHAN */ + cur_chan->sigcap = BRI_DCHAN_SIGCAP; + cur_chan->flags |= ZT_FLAG_BRIDCHAN; + cur_chan->flags &= ~ZT_FLAG_HDLC; + + /* Setup big buffers for D-Channel rx/tx */ + cur_chan->readchunk = priv->dchan_rbuf; + cur_chan->writechunk = priv->dchan_tbuf; + priv->dchan_r_idx = 0; + priv->txframe_begin = 1; + + cur_chan->maxbytes2transmit = MULTIBYTE_MAX_LEN; + cur_chan->bytes2transmit = 0; + cur_chan->bytes2receive = 0; + } else + cur_chan->sigcap = BRI_BCHAN_SIGCAP; + } + xpd->offhook = BIT(0) | BIT(1); /* 2*bchan */ + return 0; +} + +static int BRI_card_zaptel_postregistration(xpd_t *xpd, bool on) +{ + xbus_t *xbus; + struct BRI_priv_data *priv; + + BUG_ON(!xpd); + xbus = xpd->xbus; + priv = xpd->priv; + BUG_ON(!xbus); + DBG("%s/%s: %s\n", xbus->busname, xpd->xpdname, (on)?"on":"off"); + msleep(10); + CALL_PROTO(BRI, SET_LED, xbus, xpd, GREEN_LED, BRI_LED_OFF); + CALL_PROTO(BRI, SET_LED, xbus, xpd, RED_LED, BRI_LED_OFF); + return(0); +} + +int BRI_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, zt_txsig_t txsig) +{ + DBG("%s/%s/%d: %s\n", xbus->busname, xpd->xpdname, pos, txsig2str(txsig)); + return 0; +} + +/* Poll the register ST/Up-State-machine Register, to see if the cable + * if a cable is connected to the port. + */ +static int BRI_card_tick(xbus_t *xbus, xpd_t *xpd) +{ + struct BRI_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); + if(!priv->initialized) + return 0; + if(poll_interval != 0 && (priv->tick_counter % poll_interval) == 0) { + // DBG("%d\n", priv->tick_counter); + priv->poll_counter++; + CALL_PROTO(BRI, REGISTER_REQUEST, xbus, xpd, 0, 0, 0, A_SU_RD_STA, 0, 0, 0); + } + if((priv->tick_counter % LED_TICKS) == 0) { + int i; + + if(priv->reg30_good && time_before(priv->last_reg30_reply + HZ/2, jiffies)) { + /* No reply for 1/2 a second */ + ERR("%s/%s: Lost state tracking\n", xbus->busname, xpd->xpdname); + priv->reg30_good = 0; + } + if(!priv->reg30_good) { + priv->ledcontrol[RED_LED] = BRI_LED_OFF; + priv->ledcontrol[GREEN_LED] = BRI_LED_OFF; + } + for(i = 0; i < NUM_LEDS; i++) { + if(xpd->blink_mode) { + CALL_PROTO(BRI, SET_LED, xbus, xpd, i, BRI_LED_BLINK_FAST); + } else if(priv->ledstate[i] != priv->ledcontrol[i]) { + CALL_PROTO(BRI, SET_LED, xbus, xpd, i, + priv->ledcontrol[i]); + } + } + } + tx_dchan(xpd); + priv->tick_counter++; + return 0; +} + +static int BRI_card_close(xpd_t *xpd, lineno_t pos) +{ + struct zt_chan *chan = &xpd->span.chans[pos]; + + /* Clear D-Channel pending data */ + chan->bytes2receive = 0; + chan->eofrx = 0; + chan->bytes2transmit = 0; + chan->eoftx = 0; + return 0; +} + +static int BRI_span_startup(xpd_t *xpd) +{ + struct BRI_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); + DBG("%s/%s: STARTUP\n", xpd->xbus->busname, xpd->xpdname); + write_state_register(xpd, 0); /* Enable L1 state machine */ + CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 1); + if(SPAN_REGISTERED(xpd)) + xpd->span.flags |= ZT_FLAG_RUNNING; + return 0; +} + +static int BRI_span_shutdown(xpd_t *xpd) +{ + struct BRI_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); + DBG("%s/%s: SHUTDOWN\n", xpd->xbus->busname, xpd->xpdname); + if(xpd->type == XPD_TYPE_BRI_NT) + CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 0); + return 0; +} + +/*---------------- BRI: HOST COMMANDS -------------------------------------*/ + +/* 0x0F */ HOSTCMD(BRI, 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; + + if(!xbus) { + DBG("NO XBUS\n"); + return -EINVAL; + } + XFRAME_NEW(xframe, pack, xbus, BRI, REGISTER_REQUEST, xpd->id); +#if 0 + DBG("%s/%s/%d: %c%c R%02X S%02X %02X %02X\n", + xbus->busname, xpd->xpdname, chipsel, + (writing)?'W':'R', + (do_subreg)?'S':'D', + regnum, subreg, data_low, data_high); +#endif + reg_cmd = &RPACKET_FIELD(pack, BRI, REGISTER_REQUEST, 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; + REG_FIELD(reg_cmd, do_subreg) = do_subreg; + REG_FIELD(reg_cmd, regnum) = regnum; + REG_FIELD(reg_cmd, subreg) = subreg; + REG_FIELD(reg_cmd, data_low) = data_low; + REG_FIELD(reg_cmd, data_high) = data_high; + ret = xframe_send(xbus, xframe); + return ret; +} + +/* 0x0F */ HOSTCMD(BRI, XPD_STATE, bool on) +{ + struct BRI_priv_data *priv; + + BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); + DBG("%s/%s: %s\n", xbus->busname, xpd->xpdname, (on)?"ON":"OFF"); + switch(xpd->type) { + case XPD_TYPE_BRI_TE: + if(on) { + DBG("%s/%s: HFC_L1_ACTIVATE_TE\n", xbus->busname, xpd->xpdname); + set_bit(HFC_L1_ACTIVATING, &priv->l1_flags); + write_state_register(xpd, STA_ACTIVATE); + priv->t3 = HFC_TIMER_T3; + } else { + DBG("%s/%s: HFC_L1_FORCE_DEACTIVATE_TE\n", xbus->busname, xpd->xpdname); + write_state_register(xpd, STA_DEACTIVATE); + } + break; + case XPD_TYPE_BRI_NT: + if(on) { + DBG("%s/%s: HFC_L1_ACTIVATE_NT\n", xbus->busname, xpd->xpdname); + set_bit(HFC_L1_ACTIVATING, &priv->l1_flags); + write_state_register(xpd, STA_ACTIVATE | V_SU_SET_G2_G3); + } else { + DBG("%s/%s: HFC_L1_DEACTIVATE_NT\n", xbus->busname, xpd->xpdname); + write_state_register(xpd, STA_DEACTIVATE); + } + break; + default: + ERR("%s: %s/%s: Bad xpd type %d\n", __FUNCTION__, + xbus->busname, xpd->xpdname, xpd->type); + return -EINVAL; + } + return 0; +} + +/* 0x0F */ HOSTCMD(BRI, RING, lineno_t chan, bool on) +{ + ERR("%s: Unsupported\n", __FUNCTION__); + return -ENOSYS; +} + +/* 0x0F */ HOSTCMD(BRI, RELAY_OUT, byte which, bool on) +{ + ERR("%s: Unsupported\n", __FUNCTION__); + return -ENOSYS; +} + +/* 0x33 */ HOSTCMD(BRI, SET_LED, bool red_led, enum led_state to_led_state) +{ + int ret = 0; + xframe_t *xframe; + xpacket_t *pack; + struct bri_leds *bri_leds; + struct BRI_priv_data *priv; + int which_led = (red_led) ? RED_LED : GREEN_LED; + + BUG_ON(!xbus); + priv = xpd->priv; + BUG_ON(!priv); + XFRAME_NEW(xframe, pack, xbus, BRI, SET_LED, xpd->id); +#if 1 + DBG("%s/%s: %s %d\n", xbus->busname, xpd->xpdname, + (red_led)?"RED":"GREEN", to_led_state); +#endif + bri_leds = &RPACKET_FIELD(pack, BRI, SET_LED, bri_leds); + bri_leds->state = to_led_state; + bri_leds->led_sel = which_led; + pack->datalen = RPACKET_SIZE(BRI, SET_LED); + ret = xframe_send(xbus, xframe); + priv->ledstate[which_led] = to_led_state; + return ret; +} + +static int write_state_register(xpd_t *xpd, byte value) +{ + int ret; + + DBG("%s/%s: value = 0x%02X\n", xpd->xbus->busname, xpd->xpdname, value); + ret = CALL_PROTO(BRI, REGISTER_REQUEST, xpd->xbus, xpd, + 0, /* chipsel */ + 1, /* writing */ + 0, /* do_subreg */ + A_SU_WR_STA, /* regnum */ + 0, /* subreg */ + value, /* data_low */ + 0 /* data_high */ + ); + return ret; +} + + +/*---------------- BRI: Astribank Reply Handlers --------------------------*/ +static void su_new_state(xpd_t *xpd, byte reg_x30) +{ + xbus_t *xbus; + struct BRI_priv_data *priv; + su_rd_sta_t new_state; + int which_led = (xpd->type == XPD_TYPE_BRI_TE) ? GREEN_LED : RED_LED; + + BUG_ON(!xpd); + priv = xpd->priv; + BUG_ON(!priv); + xbus = xpd->xbus; + new_state.reg = reg_x30; + priv->last_reg30_reply = jiffies; + priv->reg30_good = 1; + if((xpd->type == XPD_TYPE_BRI_TE && new_state.bits.v_su_sta == ST_TE_ACTIVATED) || + (xpd->type == XPD_TYPE_BRI_NT && new_state.bits.v_su_sta == ST_NT_ACTIVATED)) { + priv->ledcontrol[which_led] = (new_state.bits.v_su_fr_sync) ? BRI_LED_BLINK_SLOW : BRI_LED_BLINK_FAST; + update_xpd_status(xpd, ZT_ALARM_NONE); + } else { + priv->ledcontrol[which_led] = BRI_LED_OFF; + update_xpd_status(xpd, ZT_ALARM_RED); + } + if (priv->state_register.bits.v_su_sta == new_state.bits.v_su_sta) + return; /* same same */ + DBG("%02X ---> %02X\n", priv->state_register.reg, reg_x30); + DBG("%s/%s: %s%i\n", xpd->xbus->busname, xpd->xpdname, + (xpd->type == XPD_TYPE_BRI_NT)?"G":"F", + new_state.bits.v_su_sta); + + if(xpd->type == XPD_TYPE_BRI_TE) { + /* disable T3 ? */ + if ((new_state.bits.v_su_sta <= ST_TE_DEACTIVATED) || (new_state.bits.v_su_sta >= ST_TE_ACTIVATED)) { + DBG("%s/%s: Disable T3 ?\n", xbus->busname, xpd->xpdname); + priv->t3 = HFC_TIMER_OFF; + } + switch (new_state.bits.v_su_sta) { + case ST_TE_DEACTIVATED: /* F3 */ + DBG("%s/%s: State ST_TE_DEACTIVATED (F3)\n", xbus->busname, xpd->xpdname); + if (test_and_clear_bit(HFC_L1_ACTIVATED, &priv->l1_flags)) + priv->t4 = HFC_TIMER_T4; + break; + case ST_TE_SIGWAIT: /* F4 */ + DBG("%s/%s: State ST_TE_SIGWAIT (F4)\n", xbus->busname, xpd->xpdname); + break; + case ST_TE_IDENT: /* F5 */ + DBG("%s/%s: State ST_TE_IDENT (F5)\n", xbus->busname, xpd->xpdname); + break; + case ST_TE_SYNCED: /* F6 */ + DBG("%s/%s: State ST_TE_SYNCED (F6)\n", xbus->busname, xpd->xpdname); + break; + case ST_TE_ACTIVATED: /* F7 */ + DBG("%s/%s: State ST_TE_ACTIVATED (F7)\n", xbus->busname, xpd->xpdname); + if (priv->t4 > HFC_TIMER_OFF) + priv->t4 = HFC_TIMER_OFF; + clear_bit(HFC_L1_ACTIVATING, &priv->l1_flags); + set_bit(HFC_L1_ACTIVATED, &priv->l1_flags); + update_xpd_status(xpd, ZT_ALARM_NONE); + break; + + case ST_TE_LOST_FRAMING: /* F8 */ + DBG("%s/%s: State ST_TE_LOST_FRAMING (F8)\n", xbus->busname, xpd->xpdname); + priv->t4 = HFC_TIMER_OFF; + break; + default: + NOTICE("%s/%s: Bad TE state: %d\n", xbus->busname, xpd->xpdname, new_state.bits.v_su_sta); + break; + } + + } else if(xpd->type == XPD_TYPE_BRI_NT) { + switch (new_state.bits.v_su_sta) { + case ST_NT_DEACTIVATED: /* G1 */ + DBG("%s/%s: State ST_NT_DEACTIVATED (G1)\n", xbus->busname, xpd->xpdname); + clear_bit(HFC_L1_ACTIVATED, &priv->l1_flags); + break; + case ST_NT_ACTIVATING: /* G2 */ + DBG("%s/%s: State ST_NT_ACTIVATING (G2)\n", xbus->busname, xpd->xpdname); + write_state_register(xpd, V_SU_SET_G2_G3); /* Enable transition to G3 */ + break; + case ST_NT_ACTIVATED: /* G3 */ + DBG("%s/%s: State ST_NT_ACTIVATED (G3)\n", xbus->busname, xpd->xpdname); + clear_bit(HFC_L1_ACTIVATING, &priv->l1_flags); + set_bit(HFC_L1_ACTIVATED, &priv->l1_flags); + break; + case ST_NT_DEACTIVTING: /* G4 */ + DBG("%s/%s: State ST_NT_DEACTIVTING (G4)\n", xbus->busname, xpd->xpdname); + break; + default: + NOTICE("%s/%s: Bad NT state: %d\n", xbus->busname, xpd->xpdname, new_state.bits.v_su_sta); + break; + } + } else + ERR("%s: %s/%s: Unknown xpd type %d\n", __FUNCTION__, + xpd->xbus->busname, xpd->xpdname, xpd->type); + priv->state_register.reg = new_state.reg; +} + +HANDLER_DEF(BRI, REGISTER_REPLY) +{ + reg_cmd_t *info = &RPACKET_FIELD(pack, BRI, REGISTER_REPLY, regcmd); + unsigned long flags; + struct BRI_priv_data *priv; + int ret; + + if(!xpd) { + NOTICE("%s: received %s for non-existing xpd: %d\n", + __FUNCTION__, cmd->name, XPD_NUM(pack->addr)); + return -EPROTO; + } + spin_lock_irqsave(&xpd->lock, flags); + priv = xpd->priv; + BUG_ON(!priv); +#if 0 + if(REG_FIELD(info, do_subreg)) { + DBG("REGISTER_REPLY: %s/%s: RS %02X %02X %02X\n", + xbus->busname, xpd->xpdname, REG_FIELD(info, regnum), REG_FIELD(info, subreg), REG_FIELD(info, data_low)); + } else { + if (REG_FIELD(info, regnum) != A_SU_RD_STA) + DBG("REGISTER_REPLY: %s/%s: RD %02X %02X\n", + xbus->busname, xpd->xpdname, REG_FIELD(info, regnum), REG_FIELD(info, data_low)); + } +#endif + if(info->multibyte) { +#if 0 + DBG("%s/%s: Got Multibyte: %d bytes, eoframe: %d\n", + xbus->busname, xpd->xpdname, + info->bytes, info->eoframe); +#endif + ret = rx_dchan(xpd, info); + if (ret < 0) { + priv->drop_counter++; + if(atomic_read(&xpd->open_counter) > 0) + NOTICE("%s/%s: Multibyte Drop: errno=%d\n", xbus->busname, xpd->xpdname, ret); + } + goto end; + } + if(REG_FIELD(info, regnum) == A_SU_RD_STA) { + su_new_state(xpd, REG_FIELD(info, data_low)); + } + + /* Update /proc info only if reply relate to the last slic read request */ + if( + REG_FIELD(&priv->requested_reply, regnum) == REG_FIELD(info, regnum) && + REG_FIELD(&priv->requested_reply, do_subreg) == REG_FIELD(info, do_subreg) && + REG_FIELD(&priv->requested_reply, subreg) == REG_FIELD(info, subreg)) { + priv->last_reply = *info; + } + +end: + spin_unlock_irqrestore(&xpd->lock, flags); + return 0; +} + +xproto_table_t PROTO_TABLE(BRI_NT) = { + .owner = THIS_MODULE, + .entries = { + /* Table Card Opcode */ + XENTRY( BRI_NT, BRI, REGISTER_REPLY ), + }, + .name = "BRI_NT", + .type = XPD_TYPE_BRI_NT, + .xops = { + .card_new = BRI_card_new, + .card_init = BRI_card_init, + .card_remove = BRI_card_remove, + .card_zaptel_preregistration = BRI_card_zaptel_preregistration, + .card_zaptel_postregistration = BRI_card_zaptel_postregistration, + .card_hooksig = BRI_card_hooksig, + .card_tick = BRI_card_tick, + .card_close = BRI_card_close, + .span_startup = BRI_span_startup, + .span_shutdown = BRI_span_shutdown, + + .RING = XPROTO_CALLER(BRI, RING), + .RELAY_OUT = XPROTO_CALLER(BRI, RELAY_OUT), + .XPD_STATE = XPROTO_CALLER(BRI, XPD_STATE), + }, + .packet_is_valid = bri_packet_is_valid, + .packet_dump = bri_packet_dump, +}; + +xproto_table_t PROTO_TABLE(BRI_TE) = { + .owner = THIS_MODULE, + .entries = { + /* Table Card Opcode */ + XENTRY( BRI_TE, BRI, REGISTER_REPLY ), + }, + .name = "BRI_TE", + .type = XPD_TYPE_BRI_TE, + .xops = { + .card_new = BRI_card_new, + .card_init = BRI_card_init, + .card_remove = BRI_card_remove, + .card_zaptel_preregistration = BRI_card_zaptel_preregistration, + .card_zaptel_postregistration = BRI_card_zaptel_postregistration, + .card_hooksig = BRI_card_hooksig, + .card_tick = BRI_card_tick, + .span_startup = BRI_span_startup, + .span_shutdown = BRI_span_shutdown, + + .RING = XPROTO_CALLER(BRI, RING), + .RELAY_OUT = XPROTO_CALLER(BRI, RELAY_OUT), + .XPD_STATE = XPROTO_CALLER(BRI, XPD_STATE), + }, + .packet_is_valid = bri_packet_is_valid, + .packet_dump = bri_packet_dump, +}; + +static bool bri_packet_is_valid(xpacket_t *pack) +{ + const xproto_entry_t *xe_nt = NULL; + const xproto_entry_t *xe_te = NULL; + // DBG("\n"); + xe_nt = xproto_card_entry(&PROTO_TABLE(BRI_NT), pack->opcode); + xe_te = xproto_card_entry(&PROTO_TABLE(BRI_TE), pack->opcode); + return xe_nt != NULL || xe_te != NULL; +} + +static void bri_packet_dump(const char *msg, xpacket_t *pack) +{ + DBG("%s\n", msg); +} +/*------------------------- REGISTER Handling --------------------------*/ + +static int proc_bri_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 BRI_priv_data *priv; + int led; + + DBG("\n"); + if(!xpd) + return -ENODEV; + spin_lock_irqsave(&xpd->lock, flags); + priv = xpd->priv; + BUG_ON(!priv); + len += sprintf(page + len, "%05d Layer 1 State: ", priv->poll_counter); + if(priv->reg30_good) { + len += sprintf(page + len, "%c%d %-15s -- fr_sync=%d t2_exp=%d info0=%d g2_g3=%d\n", + (xpd->type == XPD_TYPE_BRI_NT)?'G':'F', + priv->state_register.bits.v_su_sta, + xhfc_state_name(xpd->type, priv->state_register.bits.v_su_sta), + priv->state_register.bits.v_su_fr_sync, + priv->state_register.bits.v_su_t2_exp, + priv->state_register.bits.v_su_info0, + priv->state_register.bits.v_g2_g3); + } else + len += sprintf(page + len, "Unkown\n"); + len += sprintf(page + len, "Tick Counter: %d\n", priv->tick_counter); + len += sprintf(page + len, "Last Poll Reply at: %ld\n", priv->last_reg30_reply); + len += sprintf(page + len, "reg30_good=%d\n", priv->reg30_good); + len += sprintf(page + len, "Drop Counter: %d\n", priv->drop_counter); + for(led = 0; led < NUM_LEDS; led++) { + len += sprintf(page + len, "LED #%d\n", led); + len += sprintf(page + len, "\tledstate : %d\n", priv->ledstate[led]); + len += sprintf(page + len, "\tledcontrol: %d\n", priv->ledcontrol[led]); + } + len += sprintf(page + len, "\nDCHAN:\n"); + 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; +} + +/* + * + * 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 = 0; + unsigned xdata1 = 0; + unsigned xdata2 = 0; + char op; /* [W]rite, [R]ead */ + char reg_type; /* [D]irect, [S]ubregister */ + int reg_num; + int subreg; + int elements; + bool writing; + char *p; + reg_cmd_t regcmd; + xbus_t *xbus; + int ret; + struct BRI_priv_data *priv; + byte buf[MAX_PROC_WRITE]; + + 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; + + memset(buf, 0, MAX_PROC_WRITE); + elements = sscanf(cmdline, "%d %c%c %x %x %x %x %x", + &chipsel, + &op, ®_type, ®_num, + &subreg, + &data, &xdata1, &xdata2); + // DBG("'%s': %d %c%c %02X %02X %02X\n", cmdline, chipsel, op, reg_type, reg_num, subreg, data); + if(elements < 3) { // At least: chipsel, op, reg_type, reg_num + ERR("Not enough arguments: (%d args) '%s'\n", elements, cmdline); + return -EINVAL; + } + if(!VALID_CHIPSEL(chipsel)) { + ERR("Bad chip select number: %d\n", chipsel); + return -EINVAL; + } + 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); + return -EINVAL; + } + if( + (op == 'W' && reg_type == 'D' && elements != 5) || + (op == 'W' && reg_type == 'S' && elements != 6) || + (op == 'R' && reg_type == 'D' && elements != 4) || + (op == 'R' && reg_type == 'S' && elements != 4) + ) { + ERR("Bad number of elements: '%s' (%d elements): %d %c%c %02X %02X %02X\n", + cmdline, elements, + chipsel, op, reg_type, reg_num, subreg, data); + return -EINVAL; + } + switch(reg_type) { + case 'S': + REG_FIELD(®cmd, do_subreg) = 1; + REG_FIELD(®cmd, regnum) = reg_num; + REG_FIELD(®cmd, subreg) = subreg; + REG_FIELD(®cmd, data_low) = data; + break; + case 'D': + REG_FIELD(®cmd, do_subreg) = 0; + REG_FIELD(®cmd, regnum) = reg_num; + REG_FIELD(®cmd, subreg) = 0; + REG_FIELD(®cmd, data_low) = subreg; + break; + case 'M': /* Multi without eoftx */ + case 'm': /* Multi with eoftx */ + if(!writing) { + ERR("Read multibyte is not implemented\n"); + return -EINVAL; + } + elements -= 3; + REG_XDATA(®cmd)[0] = reg_num; + REG_XDATA(®cmd)[1] = subreg; + REG_XDATA(®cmd)[2] = data; + REG_XDATA(®cmd)[3] = xdata1; + REG_XDATA(®cmd)[4] = xdata2; + return send_bri_multibyte(xpd, REG_XDATA(®cmd), elements, (reg_type == 'm')); + default: + ERR("Unkown register type '%c'\n", reg_type); + return -EINVAL; + } + regcmd.bytes = sizeof(regcmd) - 1; + REG_FIELD(®cmd, read_request) = writing; + REG_FIELD(®cmd, data_high) = 0; + BUG_ON(!xpd); + xbus = xpd->xbus; + if(!down_read_trylock(&xbus->in_use)) { + DBG("Dropped packet. %s is in_use\n", xbus->busname); + return -EBUSY; + } + priv = xpd->priv; + BUG_ON(!priv); + priv->requested_reply = regcmd; + if(print_dbg) + dump_reg_cmd("BRI", ®cmd); + ret = CALL_PROTO(BRI, 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)); + up_read(&xbus->in_use); + 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; + struct BRI_priv_data *priv; + + if(!xpd) + return -ENODEV; + priv = xpd->priv; + BUG_ON(!priv); + spin_lock_irqsave(&xpd->lock, flags); + 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, "#\n"); + if(REG_FIELD(info, do_subreg)) { + len += sprintf(page + len, "#CH\tOP\tReg.\tSub\tDL\n"); + len += sprintf(page + len, "%2d\tRS\t%02X\t%02X\t%02X\n", + REG_FIELD(info, chipsel), + REG_FIELD(info, regnum), REG_FIELD(info, subreg), REG_FIELD(info, data_low)); + } else { + len += sprintf(page + len, "#CH\tOP\tReg.\tDL\n"); + len += sprintf(page + len, "%2d\tRD\t%02X\t%02X\n", + REG_FIELD(info, chipsel), + REG_FIELD(info, regnum), REG_FIELD(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; +} + +int __init card_bri_startup(void) +{ + DBG("\n"); + + INFO("%s revision %s\n", THIS_MODULE->name, XPP_VERSION); + xproto_register(&PROTO_TABLE(BRI_NT)); + xproto_register(&PROTO_TABLE(BRI_TE)); + return 0; +} + +void __exit card_bri_cleanup(void) +{ + DBG("\n"); + xproto_unregister(&PROTO_TABLE(BRI_NT)); + xproto_unregister(&PROTO_TABLE(BRI_TE)); +} + +MODULE_DESCRIPTION("XPP BRI Card Driver"); +MODULE_AUTHOR("Oron Peled <oron@actcom.co.il>"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(XPP_VERSION); +MODULE_ALIAS_XPD(XPD_TYPE_BRI_NT); +MODULE_ALIAS_XPD(XPD_TYPE_BRI_TE); + +module_init(card_bri_startup); +module_exit(card_bri_cleanup); diff --git a/xpp/card_bri.h b/xpp/card_bri.h new file mode 100644 index 0000000..1111d2a --- /dev/null +++ b/xpp/card_bri.h @@ -0,0 +1,40 @@ +#ifndef CARD_BRI_H +#define CARD_BRI_H +/* + * Written by Oron Peled <oron@actcom.co.il> + * Copyright (C) 2004-2006, 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 "xpd.h" + +enum bri_opcodes { + XPROTO_NAME(BRI, REGISTER_REQUEST) = 0x0F, + XPROTO_NAME(BRI, REGISTER_REPLY) = 0x10, + XPROTO_NAME(BRI, SET_LED) = 0x33, +}; + +DEF_RPACKET_DATA(BRI, REGISTER_REPLY, /* Get status of a single register (for debugging) */ + reg_cmd_t regcmd; + ); +DEF_RPACKET_DATA(BRI, REGISTER_REQUEST, + reg_cmd_t reg_cmd; + ); + +#endif /* CARD_BRI_H */ diff --git a/xpp/card_fxo.c b/xpp/card_fxo.c index 6348955..ff87120 100644 --- a/xpp/card_fxo.c +++ b/xpp/card_fxo.c @@ -721,7 +721,6 @@ static void update_power_denial(xpd_t *xpd, byte data_low, lineno_t chipsel) HANDLER_DEF(FXO, DAA_REPLY) { reg_cmd_t *info = &RPACKET_FIELD(pack, FXO, DAA_REPLY, regcmd); - unsigned long flags; struct FXO_priv_data *priv; lineno_t chipsel; @@ -730,7 +729,6 @@ HANDLER_DEF(FXO, DAA_REPLY) __FUNCTION__, cmd->name, XPD_NUM(pack->addr)); return -EPROTO; } - spin_lock_irqsave(&xpd->lock, flags); priv = xpd->priv; BUG_ON(!priv); chipsel = REG_FIELD(info, chipsel); @@ -755,7 +753,6 @@ HANDLER_DEF(FXO, DAA_REPLY) REG_FIELD(&xpd->requested_reply, subreg) == REG_FIELD(info, subreg)) { xpd->last_reply = *info; } - spin_unlock_irqrestore(&xpd->lock, flags); return 0; } |