Logo Search packages:      
Sourcecode: dahdi-linux version File versions  Download package

card_bri.c

/*
 * 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_dahdi.h"
#include "card_bri.h"
#include "dahdi_debug.h"
#include "xbus-core.h"

static const char rcsid[] = "$Id: card_bri.c 7485 2009-11-04 21:45:55Z tzafrir $";

static DEF_PARM(int, debug, 0, 0644, "Print DBG statements");     /* must be before dahdi_debug.h */
static DEF_PARM(uint, poll_interval, 500, 0644, "Poll channel state interval in milliseconds (0 - disable)");
static DEF_PARM_BOOL(nt_keepalive, 1, 0644, "Force BRI_NT to keep trying connection");

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(bool is_nt, 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(is_nt) {
            if ((state < ST_RESET) || (state > ST_NT_DEACTIVTING))
                  p = "NT ???";
            else
                  p = nt_names[state];
      } else {
            if ((state < ST_RESET) || (state > ST_TE_LOST_FRAMING))
                  p = "TE ???";
            else
                  p = te_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     HFC_TIMER_T1            2500
#define     HFC_TIMER_T3            8000  /* 8s activation timer T3 */
#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     REG30_LOST  3     /* in polls */
#define     DCHAN_LOST  15000 /* in ticks */

#ifdef      CONFIG_DAHDI_BRI_DCHANS
#define     BRI_DCHAN_SIGCAP  (                   \
                              DAHDI_SIG_EM      | \
                              DAHDI_SIG_CLEAR   | \
                              DAHDI_SIG_FXSLS   | \
                              DAHDI_SIG_FXSGS   | \
                              DAHDI_SIG_FXSKS   | \
                              DAHDI_SIG_FXOLS   | \
                              DAHDI_SIG_FXOGS   | \
                              DAHDI_SIG_FXOKS   | \
                              DAHDI_SIG_CAS     | \
                              DAHDI_SIG_SF        \
                        )
#else
#define     BRI_DCHAN_SIGCAP  DAHDI_SIG_HARDHDLC
#endif
#define     BRI_BCHAN_SIGCAP  (DAHDI_SIG_CLEAR | DAHDI_SIG_DACS)

#define     IS_NT(xpd)        ((xpd)->direction == TO_PHONE)
#define     BRI_PORT(xpd)           ((xpd)->addr.subunit)

/* shift in PCM highway */
#define     SUBUNIT_PCM_SHIFT 4
#define     PCM_SHIFT(mask, sunit)  ((mask) << (SUBUNIT_PCM_SHIFT * (sunit)))

/*---------------- 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 bri_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc);
static int bri_chanconfig(struct dahdi_chan *chan, int sigtype);
static int bri_startup(struct dahdi_span *span);
static int bri_shutdown(struct dahdi_span *span);

#define     PROC_REGISTER_FNAME     "slics"
#define     PROC_BRI_INFO_FNAME     "bri_info"

enum led_state {
      BRI_LED_OFF       = 0x0,
      BRI_LED_ON        = 0x1,
      /*
       * We blink by software from driver, so that
       * if the driver malfunction that blink would stop.
       */
      // 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         *bri_info;
      su_rd_sta_t             state_register;
      bool                    initialized;
      int                     t1;   /* timer 1 for NT deactivation */
      int                     t3;   /* timer 3 for TE activation */
      ulong                   l1_flags;
      bool                    reg30_good;
      uint                    reg30_ticks;
      bool                    layer1_up;

      /*
       * D-Chan: buffers + extra state info.
       */
#ifdef      CONFIG_DAHDI_BRI_DCHANS
      int                     dchan_r_idx;
      byte                    dchan_rbuf[DCHAN_BUFSIZE];
#else
      atomic_t                hdlc_pending;
#endif
      byte                    dchan_tbuf[DCHAN_BUFSIZE];
      bool                    txframe_begin;

      uint                    tick_counter;
      uint                    poll_counter;
      uint                    dchan_tx_counter;
      uint                    dchan_rx_counter;
      uint                    dchan_rx_drops;
      bool                    dchan_alive;
      uint                    dchan_alive_ticks;
      uint                    dchan_notx_ticks;
      uint                    dchan_norx_ticks;
      enum led_state                ledstate[NUM_LEDS];
};

static xproto_table_t   PROTO_TABLE(BRI);


DEF_RPACKET_DATA(BRI, SET_LED,      /* Set one of the LED's */
      struct bri_leds         bri_leds;
      );

static /* 0x33 */ DECLARE_CMD(BRI, SET_LED, enum bri_led_names which_led, enum led_state to_led_state);

#define     DO_LED(xpd, which, tostate)   \
                  CALL_PROTO(BRI, SET_LED, (xpd)->xbus, (xpd), (which), (tostate))

#define DEBUG_BUF_SIZE (100)
static void dump_hex_buf(xpd_t *xpd, char *msg, byte *buf, size_t len)
{
      char  debug_buf[DEBUG_BUF_SIZE + 1];
      int   i;
      int   n = 0;

      debug_buf[0] = '\0';
      for(i = 0; i < len && n < DEBUG_BUF_SIZE; i++)
            n += snprintf(&debug_buf[n], DEBUG_BUF_SIZE - n, "%02X ", buf[i]);
      XPD_NOTICE(xpd, "%s[0..%zd]: %s%s\n", msg, len-1, debug_buf,
                  (n >= DEBUG_BUF_SIZE)?"...":"");
}

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
                  XPD_NOTICE(xpd, "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);
}

static void set_bri_timer(xpd_t *xpd, const char *name, int *bri_timer, int value)
{
      if(value == HFC_TIMER_OFF)
            XPD_DBG(SIGNAL, xpd, "Timer %s DISABLE\n", name);
      else
            XPD_DBG(SIGNAL, xpd, "Timer %s: set to %d\n", name, value);
      *bri_timer = value;
}

static void dchan_state(xpd_t *xpd, bool up)
{
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      priv = xpd->priv;
      BUG_ON(!priv);
      if(priv->dchan_alive == up)
            return;
      if(up) {
            XPD_DBG(SIGNAL, xpd, "STATE CHANGE: D-Channel RUNNING\n");
            priv->dchan_alive = 1;
      } else {
            XPD_DBG(SIGNAL, xpd, "STATE CHANGE: D-Channel STOPPED\n");
            priv->dchan_rx_counter = priv->dchan_tx_counter = priv->dchan_rx_drops = 0;
            priv->dchan_alive = 0;
            priv->dchan_alive_ticks = 0;
      }
}

static void layer1_state(xpd_t *xpd, bool up)
{
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      priv = xpd->priv;
      BUG_ON(!priv);
      if(priv->layer1_up == up)
            return;
      priv->layer1_up = up;
      XPD_DBG(SIGNAL, xpd, "STATE CHANGE: Layer1 %s\n", (up)?"UP":"DOWN");
      if(!up)
            dchan_state(xpd, 0);
}

static void te_activation(xpd_t *xpd, bool on)
{
      struct BRI_priv_data    *priv;
      xbus_t                  *xbus;
      byte              curr_state;

      BUG_ON(!xpd);
      priv = xpd->priv;
      BUG_ON(!priv);
      curr_state = priv->state_register.bits.v_su_sta;
      xbus = xpd->xbus;
      XPD_DBG(SIGNAL, xpd, "%s\n", (on)?"ON":"OFF");
      if(on) {
            if(curr_state == ST_TE_DEACTIVATED) {
                  XPD_DBG(SIGNAL, xpd, "HFC_L1_ACTIVATE_TE\n");
                  set_bit(HFC_L1_ACTIVATING, &priv->l1_flags);
                  write_state_register(xpd, STA_ACTIVATE);
                  set_bri_timer(xpd, "T3", &priv->t3, HFC_TIMER_T3);
            } else {
                  XPD_DBG(SIGNAL, xpd,
                        "HFC_L1_ACTIVATE_TE (state %d, ignored)\n",
                        curr_state);
            }
      } else {    /* happen only because of T3 expiry */
            switch (curr_state) {
                  case ST_TE_DEACTIVATED:       /* F3 */
                  case ST_TE_SYNCED:            /* F6 */
                  case ST_TE_ACTIVATED:         /* F7 */
                        XPD_DBG(SIGNAL, xpd,
                              "HFC_L1_FORCE_DEACTIVATE_TE (state %d, ignored)\n",
                              curr_state);
                        break;
                  case ST_TE_SIGWAIT:           /* F4 */
                  case ST_TE_IDENT:       /* F5 */
                  case ST_TE_LOST_FRAMING:      /* F8 */
                        XPD_DBG(SIGNAL, xpd, "HFC_L1_FORCE_DEACTIVATE_TE\n");
                        write_state_register(xpd, STA_DEACTIVATE);
                        break;
                  default:
                        XPD_NOTICE(xpd, "Bad TE state: %d\n", curr_state);
                        break;
            }
      }
}

static void nt_activation(xpd_t *xpd, bool on)
{
      struct BRI_priv_data    *priv;
      xbus_t                  *xbus;
      byte              curr_state;

      BUG_ON(!xpd);
      priv = xpd->priv;
      BUG_ON(!priv);
      curr_state = priv->state_register.bits.v_su_sta;
      xbus = xpd->xbus;
      XPD_DBG(SIGNAL, xpd, "%s\n", (on)?"ON":"OFF");
      if(on) {
            switch(curr_state) {
                  case ST_RESET:                /* F/G 0 */
                  case ST_NT_DEACTIVATED:       /* G1 */
                  case ST_NT_DEACTIVTING:       /* G4 */
                        XPD_DBG(SIGNAL, xpd, "HFC_L1_ACTIVATE_NT\n");
                        set_bri_timer(xpd, "T1", &priv->t1, HFC_TIMER_T1);
                        set_bit(HFC_L1_ACTIVATING, &priv->l1_flags);
                        write_state_register(xpd, STA_ACTIVATE);
                        break;
                  case ST_NT_ACTIVATING:        /* G2 */
                  case ST_NT_ACTIVATED:         /* G3 */
                        XPD_DBG(SIGNAL, xpd,
                                    "HFC_L1_ACTIVATE_NT (in state %d, ignored)\n",
                                    curr_state);
                        break;
            }
      } else {
            switch(curr_state) {
                  case ST_RESET:                /* F/G 0 */
                  case ST_NT_DEACTIVATED:       /* G1 */
                  case ST_NT_DEACTIVTING:       /* G4 */
                        XPD_DBG(SIGNAL, xpd,
                                    "HFC_L1_DEACTIVATE_NT (in state %d, ignored)\n",
                                    curr_state);
                        break;
                  case ST_NT_ACTIVATING:        /* G2 */
                  case ST_NT_ACTIVATED:         /* G3 */
                        XPD_DBG(SIGNAL, xpd, "HFC_L1_DEACTIVATE_NT\n");
                        write_state_register(xpd, STA_DEACTIVATE);
                        break;
                  default:
                        XPD_NOTICE(xpd, "Bad NT state: %d\n", curr_state);
                        break;
            }
      }
}


/*
 * D-Chan receive
 */
static void bri_hdlc_abort(xpd_t *xpd, struct dahdi_chan *dchan, int event)
{
      struct BRI_priv_data    *priv;

      priv = xpd->priv;
      BUG_ON(!priv);
#ifdef      CONFIG_DAHDI_BRI_DCHANS
      if(debug & DBG_COMMANDS)
            dump_hex_buf(xpd, "D-Chan(abort) RX: dchan_rbuf",
                  priv->dchan_rbuf, priv->dchan_r_idx);
      priv->dchan_r_idx = 0;
#else
      dahdi_hdlc_abort(dchan, event);
#endif
}

static int bri_check_stat(xpd_t *xpd, struct dahdi_chan *dchan, byte *buf, int len)
{
      struct BRI_priv_data    *priv;
      byte              status;

      priv = xpd->priv;
      BUG_ON(!priv);
#ifdef      CONFIG_DAHDI_BRI_DCHANS
      if(priv->dchan_r_idx < 4) {
            XPD_NOTICE(xpd, "D-Chan RX short frame (dchan_r_idx=%d)\n",
                  priv->dchan_r_idx);
            dump_hex_buf(xpd, "D-Chan RX:    current packet", buf, len);
            bri_hdlc_abort(xpd, dchan, DAHDI_EVENT_ABORT);
            return -EPROTO;
      }
#else
      if(len <= 0) {
            XPD_NOTICE(xpd, "D-Chan RX DROP: short frame (len=%d)\n", len);
            bri_hdlc_abort(xpd, dchan, DAHDI_EVENT_ABORT);
            return -EPROTO;
      }
#endif
      status = buf[len-1];
      if(status) {
            int   event = DAHDI_EVENT_ABORT;

            if(status == 0xFF) {
                  XPD_NOTICE(xpd, "D-Chan RX DROP: ABORT: %d\n", status);
            } else {
                  XPD_NOTICE(xpd, "D-Chan RX DROP: BADFCS: %d\n", status);
                  event = DAHDI_EVENT_BADFCS;
            }
            dump_hex_buf(xpd, "D-Chan RX:    current packet", buf, len);
            bri_hdlc_abort(xpd, dchan, event);
            return -EPROTO;
      }
      return 0;
}

int bri_hdlc_putbuf(xpd_t *xpd, struct dahdi_chan *dchan, unsigned char *buf, int len)
{
#ifdef      CONFIG_DAHDI_BRI_DCHANS
      struct BRI_priv_data    *priv;
      byte              *dchan_buf;
      byte              *dst;
      int               idx;

      priv = xpd->priv;
      BUG_ON(!priv);
      dchan_buf = dchan->readchunk;
      idx = priv->dchan_r_idx;
      if(idx + len >= DCHAN_BUFSIZE) {
            XPD_ERR(xpd, "D-Chan RX overflow: %d\n", idx);
            dump_hex_buf(xpd, "    current packet", buf, len);
            dump_hex_buf(xpd, "    dchan_buf", dchan_buf, idx);
            return -ENOSPC;
      }
      dst = dchan_buf + idx;
      idx += len;
      priv->dchan_r_idx = idx;
      memcpy(dst, buf, len);
#else
      dahdi_hdlc_putbuf(dchan, buf, len);
#endif
      return 0;
}

void bri_hdlc_finish(xpd_t *xpd, struct dahdi_chan *dchan)
{
      struct BRI_priv_data    *priv;

      priv = xpd->priv;
      BUG_ON(!priv);
#ifdef      CONFIG_DAHDI_BRI_DCHANS
      dchan->bytes2receive = priv->dchan_r_idx - 1;
      dchan->eofrx = 1;
#else
      dahdi_hdlc_finish(dchan);
#endif
}

#ifdef      CONFIG_DAHDI_BRI_DCHANS
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 dahdi_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_CHAN(xpd, 2);
      if(!IS_OFFHOOK(xpd, 2)) {     /* D-chan is used? */
            static int rate_limit;

            if((rate_limit++ % 1000) == 0)
                  XPD_DBG(SIGNAL, xpd, "D-Chan unused\n");
            dchan->bytes2receive = 0;
            dchan->bytes2transmit = 0;
            goto out;
      }
      dchan_buf = dchan->readchunk;
      idx = priv->dchan_r_idx;
      if(idx + len >= DCHAN_BUFSIZE) {
            XPD_ERR(xpd, "D-Chan RX overflow: %d\n", 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) {
            XPD_NOTICE(xpd, "D-Chan RX short frame (idx=%d)\n", 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((ret = bri_check_stat(xpd, dchan, dchan_buf, idx)) < 0)
            goto drop;
      if(debug)
            dump_dchan_packet(xpd, 0, dchan_buf, idx /* - 3 */);  /* Print checksum? */
      /* 
       * Tell Dahdi 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;
      priv->dchan_rx_counter++;
      priv->dchan_norx_ticks = 0;
drop:
      priv->dchan_r_idx = 0;
out:
      return ret;
}
#else
static int rx_dchan(xpd_t *xpd, reg_cmd_t *regcmd)
{
      xbus_t                  *xbus;
      struct BRI_priv_data    *priv;
      byte              *src;
      struct dahdi_chan *dchan;
      uint              len;
      bool              eoframe;
      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_CHAN(xpd, 2);
      if(!IS_OFFHOOK(xpd, 2)) {     /* D-chan is used? */
            static int rate_limit;

            if((rate_limit++ % 1000) == 0)
                  XPD_DBG(SIGNAL, xpd, "D-Chan unused\n");
#ifdef      CONFIG_DAHDI_BRI_DCHANS
            dchan->bytes2receive = 0;
            dchan->bytes2transmit = 0;
#endif
            goto out;
      }
      XPD_DBG(GENERAL, xpd, "D-Chan RX: eoframe=%d len=%d\n", eoframe, len);
      ret = bri_hdlc_putbuf(xpd, dchan, src, (eoframe) ? len - 1 : len);
      if(ret < 0)
            goto out;
      if(!eoframe)
            goto out;
      if((ret = bri_check_stat(xpd, dchan, src, len)) < 0)
            goto out;
      /* 
       * Tell Dahdi that we received len-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(src[len-1])" above.
       */
      bri_hdlc_finish(xpd, dchan);
      priv->dchan_rx_counter++;
      priv->dchan_norx_ticks = 0;
out:
      return ret;
}
#endif

/*
 * D-Chan transmit
 */
#ifndef     CONFIG_DAHDI_BRI_DCHANS
/* DAHDI calls this when it has data it wants to send to the HDLC controller */
static void bri_hdlc_hard_xmit(struct dahdi_chan *chan)
{
      xpd_t             *xpd = chan->pvt;
      struct dahdi_chan *dchan;
      struct BRI_priv_data    *priv;

      priv = xpd->priv;
      BUG_ON(!priv);
      dchan = XPD_CHAN(xpd, 2);
      if (dchan == chan) {
            atomic_inc(&priv->hdlc_pending);
      }
}
#endif

int bri_hdlc_getbuf(struct dahdi_chan *dchan, unsigned char *buf, unsigned int *size)
{
      int               len = *size;
      int               eoframe;

#ifdef      CONFIG_DAHDI_BRI_DCHANS
      len = dchan->bytes2transmit;  /* dchan's hdlc package len */
      if(len > *size)
            len = *size;            /* Silent truncation */
      eoframe = dchan->eoftx;       /* dchan's end of frame */
      dchan->bytes2transmit = 0;
      dchan->eoftx = 0;
      dchan->bytes2receive = 0;
      dchan->eofrx = 0;
#else
      eoframe = dahdi_hdlc_getbuf(dchan, buf, &len);
#endif
      *size = len;
      return eoframe;
}

static int tx_dchan(xpd_t *xpd)
{
      struct BRI_priv_data    *priv;
      struct dahdi_chan *dchan;
      int               len;
      int               eoframe;
      int               ret;

      priv = xpd->priv;
      BUG_ON(!priv);
#ifndef     CONFIG_DAHDI_BRI_DCHANS
      if(atomic_read(&priv->hdlc_pending) == 0)
            return 0;
#endif
      if(!SPAN_REGISTERED(xpd) || !(xpd->span.flags & DAHDI_FLAG_RUNNING))
            return 0;
      dchan = XPD_CHAN(xpd, 2);
      len = ARRAY_SIZE(priv->dchan_tbuf);
      if(len > MULTIBYTE_MAX_LEN)
            len = MULTIBYTE_MAX_LEN;
      eoframe = bri_hdlc_getbuf(dchan, priv->dchan_tbuf, &len);
      if(len <= 0)
            return 0; /* Nothing to transmit on D channel */
      if(len > MULTIBYTE_MAX_LEN) {
            XPD_ERR(xpd, "%s: len=%d. need to split. Unimplemented.\n", __FUNCTION__, len);
            dump_hex_buf(xpd, "D-Chan TX:", priv->dchan_tbuf, len);
            return -EINVAL;
      }
      if(!test_bit(HFC_L1_ACTIVATED, &priv->l1_flags) && !test_bit(HFC_L1_ACTIVATING, &priv->l1_flags)) {
            XPD_DBG(SIGNAL, xpd, "Want to transmit: Kick D-Channel transmiter\n");
            if(xpd->direction == TO_PSTN)
                  te_activation(xpd, 1);
            else
                  nt_activation(xpd, 1);
      }
      if(debug)
            dump_dchan_packet(xpd, 1, priv->dchan_tbuf, len);
      if(eoframe)
            priv->txframe_begin = 1;
      else
            priv->txframe_begin = 0;
      XPD_DBG(COMMANDS, xpd, "eoframe=%d len=%d\n", eoframe, len);
      ret = send_multibyte_request(xpd->xbus, xpd->addr.unit, xpd->addr.subunit,
                  eoframe, priv->dchan_tbuf, len);
      if(ret < 0)
            XPD_NOTICE(xpd, "%s: failed sending xframe\n", __FUNCTION__);
      if(eoframe) {
#ifndef     CONFIG_DAHDI_BRI_DCHANS
            atomic_dec(&priv->hdlc_pending);
#endif
            priv->dchan_tx_counter++;
      }
      priv->dchan_notx_ticks = 0;
      return ret;
}

/*---------------- BRI: Methods -------------------------------------------*/

static void bri_proc_remove(xbus_t *xbus, xpd_t *xpd)
{
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      priv = xpd->priv;
      XPD_DBG(PROC, xpd, "\n");
#ifdef      CONFIG_PROC_FS
      if(priv->bri_info) {
            XPD_DBG(PROC, xpd, "Removing '%s'\n", PROC_BRI_INFO_FNAME);
            remove_proc_entry(PROC_BRI_INFO_FNAME, xpd->proc_xpd_dir);
      }
#endif
}

static int bri_proc_create(xbus_t *xbus, xpd_t *xpd)
{
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      priv = xpd->priv;
      XPD_DBG(PROC, xpd, "\n");
#ifdef      CONFIG_PROC_FS
      XPD_DBG(PROC, xpd, "Creating '%s'\n", PROC_BRI_INFO_FNAME);
      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) {
            XPD_ERR(xpd, "Failed to create proc file '%s'\n", PROC_BRI_INFO_FNAME);
            goto err;
      }
      SET_PROC_DIRENTRY_OWNER(priv->bri_info);
#endif
      return 0;
err:
      bri_proc_remove(xbus, xpd);
      return -EINVAL;
}

static xpd_t *BRI_card_new(xbus_t *xbus, int unit, int subunit, const xproto_table_t *proto_table,
      byte subtype, int subunits, int subunit_ports, bool to_phone)
{
      xpd_t       *xpd = NULL;
      int         channels = min(3, CHANNELS_PERXPD);

      if(subunit_ports != 1) {
            XBUS_ERR(xbus, "Bad subunit_ports=%d\n", subunit_ports);
            return NULL;
      }
      XBUS_DBG(GENERAL, xbus, "\n");
      xpd = xpd_alloc(xbus, unit, subunit, subtype, subunits, sizeof(struct BRI_priv_data), proto_table, channels);
      if(!xpd)
            return NULL;
      xpd->direction = (to_phone) ? TO_PHONE : TO_PSTN;
      xpd->type_name = (to_phone) ? "BRI_NT" : "BRI_TE";
      if(bri_proc_create(xbus, xpd) < 0)
            goto err;
      return xpd;
err:
      xpd_free(xpd);
      return NULL;
}

static int BRI_card_init(xbus_t *xbus, xpd_t *xpd)
{
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      XPD_DBG(GENERAL, xpd, "\n");
      priv = xpd->priv;
      DO_LED(xpd, GREEN_LED, BRI_LED_OFF);
      DO_LED(xpd, RED_LED, BRI_LED_OFF);
      set_bri_timer(xpd, "T1", &priv->t1, HFC_TIMER_OFF);
      write_state_register(xpd, 0); /* Enable L1 state machine */
      priv->initialized = 1;
      return 0;
}

static int BRI_card_remove(xbus_t *xbus, xpd_t *xpd)
{
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      priv = xpd->priv;
      XPD_DBG(GENERAL, xpd, "\n");
      bri_proc_remove(xbus, xpd);
      return 0;
}

static int BRI_card_dahdi_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);
      XPD_DBG(GENERAL, xpd, "%s\n", (on)?"on":"off");
      if(!on) {
            /* Nothing to do yet */
            return 0;
      }
      xpd->span.spantype = "BRI";
      xpd->span.linecompat = DAHDI_CONFIG_AMI | DAHDI_CONFIG_CCS;
      xpd->span.deflaw = DAHDI_LAW_ALAW;
      BIT_SET(xpd->digital_signalling, 2);      /* D-Channel */
      for_each_line(xpd, i) {
            struct dahdi_chan *cur_chan = XPD_CHAN(xpd, i);

            XPD_DBG(GENERAL, xpd, "setting BRI channel %d\n", i);
            snprintf(cur_chan->name, MAX_CHANNAME, "XPP_%s/%02d/%1d%1d/%d",
                        xpd->type_name, xbus->num,
                        xpd->addr.unit, xpd->addr.subunit, i);
            cur_chan->chanpos = i + 1;
            cur_chan->pvt = xpd;
            if(i == 2) {      /* D-CHAN */
                  cur_chan->sigcap = BRI_DCHAN_SIGCAP;
                  clear_bit(DAHDI_FLAGBIT_HDLC, &cur_chan->flags);
                  priv->txframe_begin = 1;
#ifdef      CONFIG_DAHDI_BRI_DCHANS
                  priv->dchan_r_idx = 0;
                  set_bit(DAHDI_FLAGBIT_BRIDCHAN, &cur_chan->flags);
                  /* Setup big buffers for D-Channel rx/tx */
                  cur_chan->readchunk = priv->dchan_rbuf;
                  cur_chan->writechunk = priv->dchan_tbuf;

                  cur_chan->maxbytes2transmit = MULTIBYTE_MAX_LEN;
                  cur_chan->bytes2transmit = 0;
                  cur_chan->bytes2receive = 0;
#else
                  atomic_set(&priv->hdlc_pending, 0);
#endif
            } else {
                  cur_chan->sigcap = BRI_BCHAN_SIGCAP;
            }
      }
      CALL_XMETHOD(card_pcm_recompute, xbus, xpd, 0);
      xpd->span.spanconfig = bri_spanconfig;
      xpd->span.chanconfig = bri_chanconfig;
      xpd->span.startup = bri_startup;
      xpd->span.shutdown = bri_shutdown;
#ifndef     CONFIG_DAHDI_BRI_DCHANS
      xpd->span.hdlc_hard_xmit = bri_hdlc_hard_xmit;
#endif
      return 0;
}

static int BRI_card_dahdi_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);
      XPD_DBG(GENERAL, xpd, "%s\n", (on)?"on":"off");
      return(0);
}

static int BRI_card_hooksig(xbus_t *xbus, xpd_t *xpd, int pos, enum dahdi_txsig txsig)
{
      LINE_DBG(SIGNAL, xpd, pos, "%s\n", txsig2str(txsig));
      return 0;
}

/*
 * LED managment is done by the driver now:
 *   - Turn constant ON RED/GREEN led to indicate NT/TE port
 *   - Very fast "Double Blink" to indicate Layer1 alive (without D-Channel)
 *   - Constant blink (1/2 sec cycle) to indicate D-Channel alive.
 */
static void handle_leds(xbus_t *xbus, xpd_t *xpd)
{
      struct BRI_priv_data    *priv;
      unsigned int            timer_count;
      int               which_led;
      int               other_led;
      int               mod;

      BUG_ON(!xpd);
      if(IS_NT(xpd)) {
            which_led = RED_LED;
            other_led = GREEN_LED;
      } else {
            which_led = GREEN_LED;
            other_led = RED_LED;
      }
      priv = xpd->priv;
      BUG_ON(!priv);
      timer_count = xpd->timer_count;
      if(xpd->blink_mode) {
            if((timer_count % DEFAULT_LED_PERIOD) == 0) {
                  // led state is toggled
                  if(priv->ledstate[which_led] == BRI_LED_OFF) {
                        DO_LED(xpd, which_led, BRI_LED_ON);
                        DO_LED(xpd, other_led, BRI_LED_ON);
                  } else {
                        DO_LED(xpd, which_led, BRI_LED_OFF);
                        DO_LED(xpd, other_led, BRI_LED_OFF);
                  }
            }
            return;
      }
      if(priv->ledstate[other_led] != BRI_LED_OFF)
            DO_LED(xpd, other_led, BRI_LED_OFF);
      if(priv->dchan_alive) {
            mod = timer_count % 1000;
            switch(mod) {
                  case 0:
                        DO_LED(xpd, which_led, BRI_LED_ON);
                        break;
                  case 500:
                        DO_LED(xpd, which_led, BRI_LED_OFF);
                        break;
            }
      } else if(priv->layer1_up) {
            mod = timer_count % 1000;
            switch(mod) {
                  case 0:
                  case 100:
                        DO_LED(xpd, which_led, BRI_LED_ON);
                        break;
                  case 50:
                  case 150:
                        DO_LED(xpd, which_led, BRI_LED_OFF);
                        break;
            }
      } else {
            if(priv->ledstate[which_led] != BRI_LED_ON)
                  DO_LED(xpd, which_led, BRI_LED_ON);
      }
}

static void handle_bri_timers(xpd_t *xpd)
{
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      priv = xpd->priv;
      BUG_ON(!priv);
      if(IS_NT(xpd)) {
            if (priv->t1 > HFC_TIMER_OFF) {
                  if (--priv->t1 == 0) {
                        set_bri_timer(xpd, "T1", &priv->t1, HFC_TIMER_OFF);
                        if(!nt_keepalive) {
                              if(priv->state_register.bits.v_su_sta == ST_NT_ACTIVATING) {      /* G2 */
                                    XPD_DBG(SIGNAL, xpd, "T1 Expired. Deactivate NT\n");
                                    clear_bit(HFC_L1_ACTIVATING, &priv->l1_flags);
                                    nt_activation(xpd, 0);  /* Deactivate NT */
                              } else
                                    XPD_DBG(SIGNAL, xpd,
                                          "T1 Expired. (state %d, ignored)\n",
                                          priv->state_register.bits.v_su_sta);
                        }
                  }
            }
      } else {
            if (priv->t3 > HFC_TIMER_OFF) {
                  /* timer expired ? */
                  if (--priv->t3 == 0) {
                        XPD_DBG(SIGNAL, xpd, "T3 expired. Deactivate TE\n");
                        set_bri_timer(xpd, "T3", &priv->t3, HFC_TIMER_OFF);
                        clear_bit(HFC_L1_ACTIVATING, &priv->l1_flags);
                        te_activation(xpd, 0);        /* Deactivate TE */
                  }
            }
      }
}

/* 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 || !xbus->self_ticking)
            return 0;
      if(poll_interval != 0 && (priv->tick_counter % poll_interval) == 0) {
            // XPD_DBG(GENERAL, xpd, "%d\n", priv->tick_counter);
            priv->poll_counter++;
            xpp_register_request(xbus, xpd,
                        BRI_PORT(xpd),    /* portno   */
                        0,          /* writing  */
                        A_SU_RD_STA,      /* regnum   */
                        0,          /* do_subreg      */
                        0,          /* subreg   */
                        0,          /* data_low */
                        0,          /* do_datah */
                        0,          /* data_high      */
                        0           /* should_reply   */
                        );

            if(IS_NT(xpd) && nt_keepalive &&
                  !test_bit(HFC_L1_ACTIVATED, &priv->l1_flags) &&
                  !test_bit(HFC_L1_ACTIVATING, &priv->l1_flags)) {
                  XPD_DBG(SIGNAL, xpd, "Kick NT D-Channel\n");
                  nt_activation(xpd, 1);
            }
      }
      /* Detect D-Channel disconnect heuristic */
      priv->dchan_notx_ticks++;
      priv->dchan_norx_ticks++;
      priv->dchan_alive_ticks++;
      if(priv->dchan_alive && (priv->dchan_notx_ticks > DCHAN_LOST || priv->dchan_norx_ticks > DCHAN_LOST)) {
            /*
             * No tx_dchan() or rx_dchan() for many ticks
             * This D-Channel is probabelly dead.
             */
            dchan_state(xpd, 0);
      } else if(priv->dchan_rx_counter > 1 &&  priv->dchan_tx_counter > 1) {
            if(!priv->dchan_alive)
                  dchan_state(xpd, 1);
      }
      /* Detect Layer1 disconnect */
      if(priv->reg30_good && priv->reg30_ticks > poll_interval * REG30_LOST) {
            /* No reply for 1/2 a second */
            XPD_ERR(xpd, "Lost state tracking for %d ticks\n", priv->reg30_ticks);
            priv->reg30_good = 0;
            layer1_state(xpd, 0);
      }
      handle_leds(xbus, xpd);
      handle_bri_timers(xpd);
      tx_dchan(xpd);
      priv->tick_counter++;
      priv->reg30_ticks++;
      return 0;
}

static int BRI_card_ioctl(xpd_t *xpd, int pos, unsigned int cmd, unsigned long arg)
{
      BUG_ON(!xpd);
      if(!XBUS_IS(xpd->xbus, READY))
            return -ENODEV;
      switch (cmd) {
            case DAHDI_TONEDETECT:
                  /*
                   * Asterisk call all span types with this (FXS specific)
                   * call. Silently ignore it.
                   */
                  LINE_DBG(SIGNAL, xpd, pos, "BRI: Starting a call\n");
                  return -ENOTTY;
            default:
                  report_bad_ioctl(THIS_MODULE->name, xpd, pos, cmd);
                  return -ENOTTY;
      }
      return 0;
}

static int BRI_card_open(xpd_t *xpd, lineno_t pos)
{
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      priv = xpd->priv;
      if(pos == 2) {
            LINE_DBG(SIGNAL, xpd, pos, "OFFHOOK the whole span\n");
            BIT_SET(xpd->offhook_state, 0);
            BIT_SET(xpd->offhook_state, 1);
            BIT_SET(xpd->offhook_state, 2);
            CALL_XMETHOD(card_pcm_recompute, xpd->xbus, xpd, 0);
      }
      return 0;
}

static int BRI_card_close(xpd_t *xpd, lineno_t pos)
{
      /* Clear D-Channel pending data */
#ifdef      CONFIG_DAHDI_BRI_DCHANS
      struct dahdi_chan *chan = XPD_CHAN(xpd, pos);

      /* Clear D-Channel pending data */
      chan->bytes2receive = 0;
      chan->eofrx = 0;
      chan->bytes2transmit = 0;
      chan->eoftx = 0;
#endif
      if(pos == 2) {
            LINE_DBG(SIGNAL, xpd, pos, "ONHOOK the whole span\n");
            BIT_CLR(xpd->offhook_state, 0);
            BIT_CLR(xpd->offhook_state, 1);
            BIT_CLR(xpd->offhook_state, 2);
            CALL_XMETHOD(card_pcm_recompute, xpd->xbus, xpd, 0);
      }
      return 0;
}

/*
 * Called only for 'span' keyword in /etc/dahdi/system.conf
 */
static int bri_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc)
{
      xpd_t       *xpd = span->pvt;
      const char  *framingstr = "";
      const char  *codingstr = "";
      const char  *crcstr = "";

      /* framing first */
      if (lc->lineconfig & DAHDI_CONFIG_B8ZS)
            framingstr = "B8ZS";
      else if (lc->lineconfig & DAHDI_CONFIG_AMI)
            framingstr = "AMI";
      else if (lc->lineconfig & DAHDI_CONFIG_HDB3)
            framingstr = "HDB3";
      /* then coding */
      if (lc->lineconfig & DAHDI_CONFIG_ESF)
            codingstr = "ESF";
      else if (lc->lineconfig & DAHDI_CONFIG_D4)
            codingstr = "D4";
      else if (lc->lineconfig & DAHDI_CONFIG_CCS)
            codingstr = "CCS";
      /* E1's can enable CRC checking */
      if (lc->lineconfig & DAHDI_CONFIG_CRC4)
            crcstr = "CRC4";
      XPD_DBG(GENERAL, xpd, "[%s]: span=%d (%s) lbo=%d lineconfig=%s/%s/%s (0x%X) sync=%d\n",
            IS_NT(xpd)?"NT":"TE",
            lc->span,
            lc->name,
            lc->lbo,
            framingstr, codingstr, crcstr,
            lc->lineconfig,
            lc->sync);
      /*
       * FIXME: validate
       */
      span->lineconfig = lc->lineconfig;
      return 0;
}

/*
 * Set signalling type (if appropriate)
 * Called from dahdi with spinlock held on chan. Must not call back
 * dahdi functions.
 */
static int bri_chanconfig(struct dahdi_chan *chan, int sigtype)
{
      DBG(GENERAL, "channel %d (%s) -> %s\n", chan->channo, chan->name, sig2str(sigtype));
      // FIXME: sanity checks:
      // - should be supported (within the sigcap)
      // - should not replace fxs <->fxo ??? (covered by previous?)
      return 0;
}

/*
 * Called only for 'span' keyword in /etc/dahdi/system.conf
 */
static int bri_startup(struct dahdi_span *span)
{
      xpd_t             *xpd = span->pvt;
      struct BRI_priv_data    *priv;
      struct dahdi_chan *dchan;

      BUG_ON(!xpd);
      priv = xpd->priv;
      BUG_ON(!priv);
      if(!XBUS_IS(xpd->xbus, READY)) {
            XPD_DBG(GENERAL, xpd, "Startup called by dahdi. No Hardware. Ignored\n");
            return -ENODEV;
      }
      XPD_DBG(GENERAL, xpd, "STARTUP\n");
      // Turn on all channels
      CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 1);
      if(SPAN_REGISTERED(xpd)) {
            dchan = XPD_CHAN(xpd, 2);
            span->flags |= DAHDI_FLAG_RUNNING;
            /*
             * Dahdi (wrongly) assume that D-Channel need HDLC decoding
             * and during dahdi registration override our flags.
             *
             * Don't Get Mad, Get Even:  Now we override dahdi :-)
             */
#ifdef      CONFIG_DAHDI_BRI_DCHANS
            set_bit(DAHDI_FLAGBIT_BRIDCHAN, &dchan->flags);
#endif
            clear_bit(DAHDI_FLAGBIT_HDLC, &dchan->flags);
      }
      return 0;
}

/*
 * Called only for 'span' keyword in /etc/dahdi/system.conf
 */
static int bri_shutdown(struct dahdi_span *span)
{
      xpd_t             *xpd = span->pvt;
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      priv = xpd->priv;
      BUG_ON(!priv);
      if(!XBUS_IS(xpd->xbus, READY)) {
            XPD_DBG(GENERAL, xpd, "Shutdown called by dahdi. No Hardware. Ignored\n");
            return -ENODEV;
      }
      XPD_DBG(GENERAL, xpd, "SHUTDOWN\n");
      // Turn off all channels
      CALL_XMETHOD(XPD_STATE, xpd->xbus, xpd, 0);
      return 0;
}

void BRI_card_pcm_recompute(xbus_t *xbus, xpd_t *xpd, xpp_line_t dont_care)
{
      int         i;
      int         line_count;
      xpp_line_t  pcm_mask;
      uint        pcm_len;
      xpd_t       *main_xpd;
      unsigned long     flags;

      BUG_ON(!xpd);
      main_xpd = xpd_byaddr(xbus, xpd->addr.unit, 0);
      if(!main_xpd) {
            XPD_DBG(DEVICES, xpd, "Unit 0 is already gone. Ignore request\n");
            return;
      }
      /*
       * We calculate all subunits, so use the main lock
       * as a mutex for the whole operation.
       */
      spin_lock_irqsave(&main_xpd->lock_recompute_pcm, flags);
      line_count = 0;
      pcm_mask = 0;
      for(i = 0; i < MAX_SUBUNIT; i++) {
            xpd_t       *sub_xpd = xpd_byaddr(xbus, main_xpd->addr.unit, i);

            if(sub_xpd) {
                  xpp_line_t  lines =
                        sub_xpd->offhook_state & ~sub_xpd->digital_signalling;

                  if(lines) {
                        pcm_mask |= PCM_SHIFT(lines, i);
                        line_count += 2;
                  }
                  /* subunits have fake pcm_len and wanted_pcm_mask */
                  if(i > 0) {
                        update_wanted_pcm_mask(sub_xpd, lines, 0);
                  }
            }
      }
      /*
       * FIXME: Workaround a bug in sync code of the Astribank.
       *        Send dummy PCM for sync.
       */
      if(main_xpd->addr.unit == 0 && line_count == 0) {
            pcm_mask = BIT(0);
            line_count = 1;
      }
      /*
       * The main unit account for all subunits (pcm_len and wanted_pcm_mask).
       */
      pcm_len = (line_count)
            ? RPACKET_HEADERSIZE + sizeof(xpp_line_t) + line_count * DAHDI_CHUNKSIZE
            : 0L;
      update_wanted_pcm_mask(main_xpd, pcm_mask, pcm_len);
      spin_unlock_irqrestore(&main_xpd->lock_recompute_pcm, flags);
}

static void BRI_card_pcm_fromspan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
{
      byte        *pcm;
      unsigned long     flags;
      int         i;
      int         subunit;
      xpp_line_t  pcm_mask = 0;
      xpp_line_t  wanted_lines;


      BUG_ON(!xbus);
      BUG_ON(!xpd);
      BUG_ON(!pack);
      pcm = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, pcm);
      for(subunit = 0; subunit < MAX_SUBUNIT; subunit++) {
            xpd_t       *tmp_xpd;

            tmp_xpd = xpd_byaddr(xbus, xpd->addr.unit, subunit);
            if(!tmp_xpd || !tmp_xpd->card_present)
                  continue;
            spin_lock_irqsave(&tmp_xpd->lock, flags);
            wanted_lines = tmp_xpd->wanted_pcm_mask;
            for_each_line(tmp_xpd, i) {
                  struct dahdi_chan *chan = XPD_CHAN(tmp_xpd, i);

                  if(IS_SET(wanted_lines, i)) {
                        if(SPAN_REGISTERED(tmp_xpd)) {
#ifdef      DEBUG_PCMTX
                              int   channo = chan->channo;

                              if(pcmtx >= 0 && pcmtx_chan == channo)
                                    memset((u_char *)pcm, pcmtx, DAHDI_CHUNKSIZE);
                              else
#endif
                                    memcpy((u_char *)pcm, chan->writechunk, DAHDI_CHUNKSIZE);
                        } else
                              memset((u_char *)pcm, 0x7F, DAHDI_CHUNKSIZE);
                        pcm += DAHDI_CHUNKSIZE;
                  }
            }
            pcm_mask |= PCM_SHIFT(wanted_lines, subunit);
            XPD_COUNTER(tmp_xpd, PCM_WRITE)++;
            spin_unlock_irqrestore(&tmp_xpd->lock, flags);
      }
      RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines) = pcm_mask;
}

static void BRI_card_pcm_tospan(xbus_t *xbus, xpd_t *xpd, xpacket_t *pack)
{
      byte        *pcm;
      xpp_line_t  pcm_mask;
      unsigned long     flags;
      int         subunit;
      int         i;

      /*
       * Subunit 0 handle all other subunits
       */
      if(xpd->addr.subunit != 0)
            return;
      if(!SPAN_REGISTERED(xpd))
            return;
      pcm = RPACKET_FIELD(pack, GLOBAL, PCM_READ, pcm);
      pcm_mask = RPACKET_FIELD(pack, GLOBAL, PCM_WRITE, lines);
      for(subunit = 0; subunit < MAX_SUBUNIT; subunit++, pcm_mask >>= SUBUNIT_PCM_SHIFT) {
            xpd_t       *tmp_xpd;

            if(!pcm_mask)
                  break;      /* optimize */
            tmp_xpd = xpd_byaddr(xbus, xpd->addr.unit, subunit);
            if(!tmp_xpd || !tmp_xpd->card_present || !SPAN_REGISTERED(tmp_xpd))
                  continue;
            spin_lock_irqsave(&tmp_xpd->lock, flags);
            for (i = 0; i < 2; i++) {
                  xpp_line_t  tmp_mask = pcm_mask & (BIT(0) | BIT(1));
                  volatile u_char   *r;

                  if(IS_SET(tmp_mask, i)) {
                        r = XPD_CHAN(tmp_xpd, i)->readchunk;
                        // memset((u_char *)r, 0x5A, DAHDI_CHUNKSIZE);  // DEBUG
                        memcpy((u_char *)r, pcm, DAHDI_CHUNKSIZE);
                        pcm += DAHDI_CHUNKSIZE;
                  }
            }
            XPD_COUNTER(tmp_xpd, PCM_READ)++;
            spin_unlock_irqrestore(&tmp_xpd->lock, flags);
      }
}

/*---------------- BRI: HOST COMMANDS -------------------------------------*/

static /* 0x0F */ HOSTCMD(BRI, XPD_STATE, bool on)
{
      struct BRI_priv_data    *priv;

      BUG_ON(!xpd);
      priv = xpd->priv;
      XPD_DBG(GENERAL, xpd, "%s\n", (on)?"ON":"OFF");
      if(on) {
            if(!test_bit(HFC_L1_ACTIVATED, &priv->l1_flags)) {
                  if(xpd->direction == TO_PSTN)
                        te_activation(xpd, 1);
                  else
                        nt_activation(xpd, 1);
            }
      } else if(IS_NT(xpd))
            nt_activation(xpd, 0);
      return 0;
}

static /* 0x33 */ HOSTCMD(BRI, SET_LED, enum bri_led_names which_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;

      BUG_ON(!xbus);
      priv = xpd->priv;
      BUG_ON(!priv);
      XPD_DBG(LEDS, xpd, "%s -> %d\n",
            (which_led)?"RED":"GREEN",
            to_led_state);
      XFRAME_NEW_CMD(xframe, pack, xbus, BRI, SET_LED, xpd->xbus_idx);
      bri_leds = &RPACKET_FIELD(pack, BRI, SET_LED, bri_leds);
      bri_leds->state = to_led_state;
      bri_leds->led_sel = which_led;
      XPACKET_LEN(pack) = RPACKET_SIZE(BRI, SET_LED);
      ret = send_cmd_frame(xbus, xframe);
      priv->ledstate[which_led] = to_led_state;
      return ret;
}

static int write_state_register(xpd_t *xpd, byte value)
{
      int   ret;

      XPD_DBG(REGS, xpd, "value = 0x%02X\n", value);
      ret = xpp_register_request(xpd->xbus, xpd,
                  BRI_PORT(xpd),    /* portno   */
                  1,          /* writing  */
                  A_SU_WR_STA,      /* regnum   */
                  0,          /* do_subreg      */
                  0,          /* subreg   */
                  value,            /* data_low */
                  0,          /* do_datah */
                  0,          /* data_high      */
                  0           /* should_reply   */
                  );
      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;

      BUG_ON(!xpd);
      priv = xpd->priv;
      BUG_ON(!priv);
      xbus = xpd->xbus;
      if(!priv->initialized) {
            XPD_ERR(xpd, "%s called on uninitialized AB\n", __FUNCTION__);
            return;
      }
      new_state.reg = reg_x30;
      if(new_state.bits.v_su_t2_exp) {
            XPD_NOTICE(xpd, "T2 Expired\n");
      }
      priv->reg30_ticks = 0;
      priv->reg30_good = 1;
      if (priv->state_register.bits.v_su_sta == new_state.bits.v_su_sta)
            return;     /* same same */
      XPD_DBG(SIGNAL, xpd, "%02X ---> %02X (info0=%d) (%s%i)\n",
            priv->state_register.reg,
            reg_x30,
            new_state.bits.v_su_info0,
            IS_NT(xpd)?"G":"F",
            new_state.bits.v_su_sta);
      if(!IS_NT(xpd)) {
            switch (new_state.bits.v_su_sta) {
                  case ST_TE_DEACTIVATED:       /* F3 */
                        XPD_DBG(SIGNAL, xpd, "State ST_TE_DEACTIVATED (F3)\n");
                        clear_bit(HFC_L1_ACTIVATED, &priv->l1_flags);
                        layer1_state(xpd, 0);
                        break;
                  case ST_TE_SIGWAIT:           /* F4 */
                        XPD_DBG(SIGNAL, xpd, "State ST_TE_SIGWAIT (F4)\n");
                        layer1_state(xpd, 0);
                        break;
                  case ST_TE_IDENT:       /* F5 */
                        XPD_DBG(SIGNAL, xpd, "State ST_TE_IDENT (F5)\n");
                        layer1_state(xpd, 0);
                        break;
                  case ST_TE_SYNCED:            /* F6 */
                        XPD_DBG(SIGNAL, xpd, "State ST_TE_SYNCED (F6)\n");
                        layer1_state(xpd, 0);
                        break;
                  case ST_TE_ACTIVATED:         /* F7 */
                        XPD_DBG(SIGNAL, xpd, "State ST_TE_ACTIVATED (F7)\n");
                        set_bri_timer(xpd, "T3", &priv->t3, HFC_TIMER_OFF);
                        clear_bit(HFC_L1_ACTIVATING, &priv->l1_flags);
                        set_bit(HFC_L1_ACTIVATED, &priv->l1_flags);
                        layer1_state(xpd, 1);
                        update_xpd_status(xpd, DAHDI_ALARM_NONE);
                        break;
                  case ST_TE_LOST_FRAMING:      /* F8 */
                        XPD_DBG(SIGNAL, xpd, "State ST_TE_LOST_FRAMING (F8)\n");
                        layer1_state(xpd, 0);
                        break;
                  default:
                        XPD_NOTICE(xpd, "Bad TE state: %d\n", new_state.bits.v_su_sta);
                        break;
            }

      } else {
            switch (new_state.bits.v_su_sta) {
                  case ST_NT_DEACTIVATED:       /* G1 */
                        XPD_DBG(SIGNAL, xpd, "State ST_NT_DEACTIVATED (G1)\n");
                        clear_bit(HFC_L1_ACTIVATED, &priv->l1_flags);
                        set_bri_timer(xpd, "T1", &priv->t1, HFC_TIMER_OFF);
                        layer1_state(xpd, 0);
                        break;
                  case ST_NT_ACTIVATING:        /* G2 */
                        XPD_DBG(SIGNAL, xpd, "State ST_NT_ACTIVATING (G2)\n");
                        layer1_state(xpd, 0);
                        if(!test_bit(HFC_L1_ACTIVATED, &priv->l1_flags))
                              nt_activation(xpd, 1);
                        break;
                  case ST_NT_ACTIVATED:         /* G3 */
                        XPD_DBG(SIGNAL, xpd, "State ST_NT_ACTIVATED (G3)\n");
                        clear_bit(HFC_L1_ACTIVATING, &priv->l1_flags);
                        set_bit(HFC_L1_ACTIVATED, &priv->l1_flags);
                        set_bri_timer(xpd, "T1", &priv->t1, HFC_TIMER_OFF);
                        layer1_state(xpd, 1);
                        update_xpd_status(xpd, DAHDI_ALARM_NONE);
                        break;
                  case ST_NT_DEACTIVTING:       /* G4 */
                        XPD_DBG(SIGNAL, xpd, "State ST_NT_DEACTIVTING (G4)\n");
                        set_bri_timer(xpd, "T1", &priv->t1, HFC_TIMER_OFF);
                        layer1_state(xpd, 0);
                        break;
                  default:
                        XPD_NOTICE(xpd, "Bad NT state: %d\n", new_state.bits.v_su_sta);
                        break;
            }
      }
      priv->state_register.reg = new_state.reg;
}

static int BRI_card_register_reply(xbus_t *xbus, xpd_t *xpd, reg_cmd_t *info)
{
      unsigned long           flags;
      struct BRI_priv_data    *priv;
      struct xpd_addr         addr;
      xpd_t             *orig_xpd;
      int               ret;

      /* Map UNIT + PORTNUM to XPD */
      orig_xpd = xpd;
      addr.unit = orig_xpd->addr.unit;
      addr.subunit = info->portnum;
      xpd = xpd_byaddr(xbus, addr.unit, addr.subunit);
      if(!xpd) {
            static int  rate_limit;

            if((rate_limit++ % 1003) < 5)
                  notify_bad_xpd(__FUNCTION__, xbus, addr , orig_xpd->xpdname);
            return -EPROTO;
      }
      spin_lock_irqsave(&xpd->lock, flags);
      priv = xpd->priv;
      BUG_ON(!priv);
      if(REG_FIELD(info, do_subreg)) {
            XPD_DBG(REGS, xpd, "RI %02X %02X %02X\n",
                        REG_FIELD(info, regnum), REG_FIELD(info, subreg), REG_FIELD(info, data_low));
      } else {
            if (REG_FIELD(info, regnum) != A_SU_RD_STA)
                  XPD_DBG(REGS, xpd, "RD %02X %02X\n",
                              REG_FIELD(info, regnum), REG_FIELD(info, data_low));
            else
                  XPD_DBG(REGS, xpd, "Got SU_RD_STA=%02X\n",
                              REG_FIELD(info, data_low));
      }
      if(info->is_multibyte) {
            XPD_DBG(REGS, xpd, "Got Multibyte: %d bytes, eoframe: %d\n",
                        info->bytes, info->eoframe);
            ret = rx_dchan(xpd, info);
            if (ret < 0) {
                  priv->dchan_rx_drops++;
                  if(atomic_read(&xpd->open_counter) > 0)
                        XPD_NOTICE(xpd, "Multibyte Drop: errno=%d\n", 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(&xpd->requested_reply, regnum) == REG_FIELD(info, regnum) &&
                  REG_FIELD(&xpd->requested_reply, do_subreg) == REG_FIELD(info, do_subreg) &&
                  REG_FIELD(&xpd->requested_reply, subreg) == REG_FIELD(info, subreg)) {
            xpd->last_reply = *info;
      }
      
end:
      spin_unlock_irqrestore(&xpd->lock, flags);
      return 0;
}

static xproto_table_t PROTO_TABLE(BRI) = {
      .owner = THIS_MODULE,
      .entries = {
            /*    Table Card  Opcode            */
      },
      .name = "BRI",    /* protocol name */
      .ports_per_subunit = 1,
      .type = XPD_TYPE_BRI,
      .xops = {
            .card_new   = BRI_card_new,
            .card_init  = BRI_card_init,
            .card_remove      = BRI_card_remove,
            .card_dahdi_preregistration   = BRI_card_dahdi_preregistration,
            .card_dahdi_postregistration  = BRI_card_dahdi_postregistration,
            .card_hooksig     = BRI_card_hooksig,
            .card_tick  = BRI_card_tick,
            .card_pcm_recompute     = BRI_card_pcm_recompute,
            .card_pcm_fromspan      = BRI_card_pcm_fromspan,
            .card_pcm_tospan  = BRI_card_pcm_tospan,
            .card_ioctl = BRI_card_ioctl,
            .card_open  = BRI_card_open,
            .card_close = BRI_card_close,
            .card_register_reply    = BRI_card_register_reply,

            .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 = NULL;
      // DBG(GENERAL, "\n");
      xe = xproto_card_entry(&PROTO_TABLE(BRI), XPACKET_OP(pack));
      return xe != NULL;
}

static void bri_packet_dump(const char *msg, xpacket_t *pack)
{
      DBG(GENERAL, "%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;

      DBG(PROC, "\n");
      if(!xpd)
            return -ENODEV;
      spin_lock_irqsave(&xpd->lock, flags);
      priv = xpd->priv;
      BUG_ON(!priv);
      len += sprintf(page + len, "%05d Layer 1: ", priv->poll_counter);
      if(priv->reg30_good) {
            len += sprintf(page + len, "%-5s ", (priv->layer1_up) ? "UP" : "DOWN");
            len += sprintf(page + len, "%c%d %-15s -- fr_sync=%d t2_exp=%d info0=%d g2_g3=%d\n",
                              IS_NT(xpd)?'G':'F',
                              priv->state_register.bits.v_su_sta,
                              xhfc_state_name(IS_NT(xpd), 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");
      if(IS_NT(xpd)) {
            len += sprintf(page + len, "T1 Timer: %d\n", priv->t1);
      } else {
            len += sprintf(page + len, "T3 Timer: %d\n", priv->t3);
      }
      len += sprintf(page + len, "Tick Counter: %d\n", priv->tick_counter);
      len += sprintf(page + len, "Last Poll Reply: %d ticks ago\n", priv->reg30_ticks);
      len += sprintf(page + len, "reg30_good=%d\n", priv->reg30_good);
      len += sprintf(page + len, "D-Channel: TX=[%5d]    RX=[%5d]    BAD=[%5d] ",
                  priv->dchan_tx_counter, priv->dchan_rx_counter, priv->dchan_rx_drops);
      if(priv->dchan_alive) {
            len += sprintf(page + len, "(alive %d K-ticks)\n",
                  priv->dchan_alive_ticks/1000);
      } else {
            len += sprintf(page + len, "(dead)\n");
      }
#ifndef     CONFIG_DAHDI_BRI_DCHANS
      len += sprintf(page + len, "hdlc_pending=%d\n", atomic_read(&priv->hdlc_pending));
#endif
      len += sprintf(page + len, "dchan_notx_ticks: %d\n", priv->dchan_notx_ticks);
      len += sprintf(page + len, "dchan_norx_ticks: %d\n", priv->dchan_norx_ticks);
      len += sprintf(page + len, "LED: %-10s = %d\n", "GREEN", priv->ledstate[GREEN_LED]);
      len += sprintf(page + len, "LED: %-10s = %d\n", "RED", priv->ledstate[RED_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;
}

static DRIVER_ATTR_READER(dchan_hardhdlc_show, drv,buf)
{
      int               len = 0;

#if   defined(CONFIG_DAHDI_BRI_DCHANS)
      len += sprintf(buf + len, "0\n");
#elif defined(DAHDI_SIG_HARDHDLC)
      len += sprintf(buf + len, "1\n");
#else
#error Cannot build BRI without BRISTUFF or HARDHDLC supprt
#endif
      return len;
}

static      DRIVER_ATTR(dchan_hardhdlc,S_IRUGO,dchan_hardhdlc_show,NULL);

static int bri_xpd_probe(struct device *dev)
{
      xpd_t *xpd;

      xpd = dev_to_xpd(dev);
      /* Is it our device? */
      if(xpd->type != XPD_TYPE_BRI) {
            XPD_ERR(xpd, "drop suggestion for %s (%d)\n",
                  dev_name(dev), xpd->type);
            return -EINVAL;
      }
      XPD_DBG(DEVICES, xpd, "SYSFS\n");
      return 0;
}

static int bri_xpd_remove(struct device *dev)
{
      xpd_t *xpd;

      xpd = dev_to_xpd(dev);
      XPD_DBG(DEVICES, xpd, "SYSFS\n");
      return 0;
}

static struct xpd_driver      bri_driver = {
      .type       = XPD_TYPE_BRI,
      .driver           = {
            .name = "bri",
#ifndef OLD_HOTPLUG_SUPPORT
            .owner = THIS_MODULE,
#endif
            .probe = bri_xpd_probe,
            .remove = bri_xpd_remove
      }
};

static int __init card_bri_startup(void)
{
      int   ret;

      if((ret = xpd_driver_register(&bri_driver.driver)) < 0)
            return ret;
      ret = driver_create_file(&bri_driver.driver, &driver_attr_dchan_hardhdlc);
      if(ret < 0)
            return ret;
      INFO("revision %s\n", XPP_VERSION);
#if   defined(CONFIG_DAHDI_BRI_DCHANS)
      INFO("FEATURE: WITH BRISTUFF\n");
#elif defined(DAHDI_SIG_HARDHDLC)
      INFO("FEATURE: WITH HARDHDLC\n");
#else
#error Cannot build BRI without BRISTUFF or HARDHDLC supprt
#endif

      xproto_register(&PROTO_TABLE(BRI));
      return 0;
}

static void __exit card_bri_cleanup(void)
{
      DBG(GENERAL, "\n");
      xproto_unregister(&PROTO_TABLE(BRI));
      driver_remove_file(&bri_driver.driver, &driver_attr_dchan_hardhdlc);
      xpd_driver_unregister(&bri_driver.driver);
}

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);

module_init(card_bri_startup);
module_exit(card_bri_cleanup);

Generated by  Doxygen 1.6.0   Back to index