summaryrefslogtreecommitdiff
path: root/xhfc/xhfc.c
diff options
context:
space:
mode:
authorNoé Rubinstein <nrubinstein@proformatique.com>2010-08-04 11:37:50 +0200
committerNoé Rubinstein <nrubinstein@proformatique.com>2010-08-04 11:37:50 +0200
commit8ed88c4d9d3840beaf4cc670e657a8d940c3ab93 (patch)
treefb99c9a78a5817d0f6e9cbe064d840abed432dd0 /xhfc/xhfc.c
parentfe526e59ca388d149ee52f3cef80325343d6b6d4 (diff)
some progress made on the XHFC driver module
Diffstat (limited to 'xhfc/xhfc.c')
-rw-r--r--xhfc/xhfc.c1083
1 files changed, 992 insertions, 91 deletions
diff --git a/xhfc/xhfc.c b/xhfc/xhfc.c
index afabeab..1ca102c 100644
--- a/xhfc/xhfc.c
+++ b/xhfc/xhfc.c
@@ -9,7 +9,6 @@
#include "xhfc24sucd.h"
#include "xhfc.h"
#include "xhfc_leb.h"
-#include "xhfc_test_hdlc.h"
static const char xhfc_rev[] = "$Revision: 1.22 $";
static int card_cnt = 0;
@@ -17,69 +16,57 @@ static int card_cnt = 0;
MODULE_LICENSE("GPL");
uint debug = 0;
-static uint port = 3;
-static int nt = 0;
-static int pcm = 0;
-static uint fifo = 7;
-static uint slot = 23;
+uint dbg_spanfilter = 0xFFFFFFFF;
+//static uint port = 3;
+//static int nt = 0;
+//static int pcm = 0;
+//static uint fifo = 7;
+//static uint slot = 23;
static uint reset_gpio = 33;
int softconf = 0;
-static uint lineterm_gpio = 27;
-static uint ntte_gpio = 25;
-static uint lineterm_conf = 1;
-static uint ntte_conf = 1;
+//static uint lineterm_gpio = 27;
+//static uint ntte_gpio = 25;
+
+//module_param(port, uint, S_IRUGO);
+//module_param(nt, bool, S_IRUGO);
+//module_param(pcm, bool, S_IRUGO);
+//module_param(fifo, uint, S_IRUGO);
+//module_param(slot, uint, S_IRUGO);
+//MODULE_PARM_DESC(port, "(Only for S/T) XHFC S/T port number");
+//MODULE_PARM_DESC(fifo, "(Only for PCM) XHFC's host FIFO number.");
+//MODULE_PARM_DESC(slot, "(Only for PCM) PCM slot.");
module_param(debug, uint, S_IRUGO | S_IWUSR);
-module_param(port, uint, S_IRUGO);
-module_param(nt, bool, S_IRUGO);
-module_param(pcm, bool, S_IRUGO);
-module_param(fifo, uint, S_IRUGO);
-module_param(slot, uint, S_IRUGO);
+module_param(dbg_spanfilter, uint, S_IRUGO | S_IWUSR);
module_param(softconf, bool, S_IRUGO);
-module_param(lineterm_gpio, uint, S_IRUGO);
-module_param(ntte_gpio, uint, S_IRUGO);
-module_param(lineterm_conf, uint, S_IRUGO);
-module_param(ntte_conf, uint, S_IRUGO);
+//module_param(lineterm_gpio, uint, S_IRUGO);
+//module_param(ntte_gpio, uint, S_IRUGO);
-MODULE_PARM_DESC(pcm, "Use the PCM bus instead of the S/T interface.");
+MODULE_PARM_DESC(debug, "Debug bitfield:\n"
+ "\t0: general\n"
+ "\t1: (not used, DTMF)\n"
+ "\t2: verbose register access, only if DEBUG is defined\n"
+ "\t3: file operations\n"
+ "\t4: (not used, echo canceller)\n"
+ "\t5: (not used, ST state)\n"
+ "\t6: HDLC\n"
+ "\t7: (not used, alarms)\n"
+ "\t8: (not used, timing)\n");
+MODULE_PARM_DESC(dbg_spanfilter, "bitfield, filter debug info by span.");
-MODULE_PARM_DESC(softconf, "Configure the S/T port line interface in software.");
+//MODULE_PARM_DESC(pcm, "Use the PCM bus instead of the S/T interface.");
+
+//MODULE_PARM_DESC(softconf, "Configure the S/T port line interface in software.");
MODULE_PARM_DESC(reset_gpio, "Reset the XHFC using this GPIO");
-MODULE_PARM_DESC(lineterm_gpio, "(Only if softconf) Configure the line termination on the S/T port using this GPIO");
-MODULE_PARM_DESC(ntte_gpio, "(Only if softconf) Configure the the S/T port as NT/TE using this GPIO");
-MODULE_PARM_DESC(lineterm_conf, "(Only if softconf) 1=line termination on.");
-MODULE_PARM_DESC(ntte_conf, "(Only if softconf) 0=TE mode, no power feeding; 1=NT mode, power feeding");
+//MODULE_PARM_DESC(ntte_gpio, "(Only if softconf) Configure the the S/T port as NT/TE using this GPIO");
MODULE_PARM_DESC(nt, "(Only for S/T) 1 if the interface is NT, 0 if it is TE");
-MODULE_PARM_DESC(port, "(Only for S/T) XHFC S/T port number");
-MODULE_PARM_DESC(fifo, "(Only for PCM) XHFC's host FIFO number.");
-MODULE_PARM_DESC(slot, "(Only for PCM) PCM slot.");
uint trigger = 0;
-/* common xhfc operations */
-int
-xhfc_chipid(struct xhfc * xhfc)
-{
- __u8 chip_id;
- int result = TEST_FAILED;
-
- chip_id = read_xhfc(xhfc, R_CHIP_ID);
-
- switch (chip_id) {
- case CHIP_ID_1SU :
- case CHIP_ID_2SU :
- case CHIP_ID_2S4U :
- case CHIP_ID_4SU :
- result = TEST_PASSED;
- break;
- }
- return(result);
-}
-
void
xhfc_waitbusy(struct xhfc * xhfc)
{
@@ -160,7 +147,7 @@ xhfc_config_st(struct xhfc* x, int port, int nt)
/* configures PCM master mode
*/
int
-xhfc_cfg_pcm(struct xhfc * xhfc, int master_or_slave)
+xhfc_config_pcm(struct xhfc * xhfc, int master_or_slave)
{
static char msg[1000];
@@ -185,7 +172,7 @@ xhfc_cfg_pcm(struct xhfc * xhfc, int master_or_slave)
// for F1_0 pin on time slot 0 for 2MBit PCM
write_xhfc(xhfc, R_SL_SEL0, 0x1f);
- if (pcm == XHFC_PCM_MASTER) {
+ if (master_or_slave == XHFC_PCM_MASTER) {
write_xhfc(xhfc, R_PCM_MD0, SET_V_PCM_IDX(xhfc->r_pcm_md0, 0xA));
// enable PCM bit clk for C2O pin
write_xhfc(xhfc, R_PCM_MD2, M_C2O_EN);
@@ -204,6 +191,649 @@ xhfc_cfg_pcm(struct xhfc * xhfc, int master_or_slave)
return(0);
}
+/* NOTE: assumes fifo lock is held */
+#define debug_fz(b4, fifo, prefix, buf) \
+do { \
+ sprintf(buf, "%s: (fifo %d): flen=%d, " \
+ "zlen=%d\n", prefix, fifo, flen, \
+ zlen); \
+} while (0)
+
+#define DEFINE_GET_LEN(F_or_Z) \
+static inline int \
+get_ ## F_or_Z ## len(struct xhfc* x) { \
+ u8 _1, _2; \
+ int len; \
+ \
+ _1 = read_xhfc(x, A_ ## F_or_Z ## 1); \
+ _2 = read_xhfc(x, A_ ## F_or_Z ## 2); \
+ len = _1 - _2; \
+ \
+ if(len < 0) \
+ len += \
+ (XHFC_ ## F_or_Z ## MAX - \
+ XHFC_ ## F_or_Z ## MIN) + 1; \
+ \
+ return len; \
+}
+
+DEFINE_GET_LEN(F)
+DEFINE_GET_LEN(Z)
+
+#define WRITE_TO_REGS() \
+ do { \
+ write_xhfc(x, R_FIFO, r_fifo); \
+ xhfc_waitbusy(x); \
+ write_xhfc(x, A_CON_HDLC, a_con_hdlc); \
+ write_xhfc(x, A_CHANNEL, a_channel); \
+ write_xhfc(x, A_SUBCH_CFG, a_subch_cfg); \
+ write_xhfc(x, A_INC_RES_FIFO, a_inc_res_fifo); \
+ xhfc_waitbusy(x); \
+ } while(0)
+void
+xhfc_config_d_chan_on_fifo(struct xhfc_span* xhfc_span)
+{
+ struct xhfc* x = xhfc_span->xhfc;
+ u8 r_fifo, a_con_hdlc, a_channel, a_subch_cfg, a_inc_res_fifo;
+ r_fifo = a_con_hdlc = a_channel = a_subch_cfg = a_inc_res_fifo = 0x00;
+
+ SET_V_FIFO_NUM(r_fifo, dchan_fifo(xhfc_span));
+
+ SET_V_IFF(a_con_hdlc, 1); /* inter frame fill with 1s */
+ SET_V_HDLC_TRP(a_con_hdlc, 0); /* HDLC mode */
+ SET_V_FIFO_IRQ(a_con_hdlc, 7); /* FIFO & IRQ enabled */
+ SET_V_CH_FNUM(a_channel, dchan_fifo(xhfc_span)); /* chan number */
+ SET_V_BIT_CNT(a_subch_cfg, 2); /* only two bytes read or written */
+ SET_V_RES_FIFO(a_inc_res_fifo, 1); /* reset FIFO */
+ SET_V_RES_LOST(a_inc_res_fifo, 1); /* reset FIFO */
+ SET_V_RES_FIFO_ERR(a_inc_res_fifo, 1); /* reset FIFO */
+
+ SET_V_DATA_FLOW(a_con_hdlc, 0); /* FIFO -> ST/Up */
+
+ SET_V_FIFO_DIR(r_fifo, 0); /* transmit */
+ SET_V_CH_FDIR(a_channel, 0); /* transmit */
+
+ WRITE_TO_REGS();
+
+ SET_V_FIFO_DIR(r_fifo, 1); /* receive */
+ SET_V_CH_FDIR(a_channel, 1); /* receive */
+
+ WRITE_TO_REGS();
+}
+#undef WRITE_TO_REGS
+
+#if 0
+#define WRITE_TO_REGS() \
+ do { \
+ write_xhfc(x, R_FIFO, r_fifo); \
+ xhfc_waitbusy(x); \
+ write_xhfc(x, A_CON_HDLC, a_con_hdlc); \
+ write_xhfc(x, A_INC_RES_FIFO, a_inc_res_fifo); \
+ xhfc_waitbusy(x); \
+ } while(0)
+void
+xhfc_config_b_chan_on_fifo(struct xhfc* x)
+{
+ u8 r_fifo, a_con_hdlc, a_inc_res_fifo;
+ r_fifo = a_con_hdlc = a_inc_res_fifo = 0x00;
+
+ SET_V_IFF(a_con_hdlc, 1); /* inter frame fill with 1s */
+ SET_V_HDLC_TRP(a_con_hdlc, 1); /* transparent mode */
+ SET_V_FIFO_IRQ(a_con_hdlc, 7); /* FIFO & IRQ enabled */
+ SET_V_DATA_FLOW(a_con_hdlc, 0); /* FIFO -> ST/Up */
+
+ SET_V_RES_FIFO(a_inc_res_fifo, 1); /* reset FIFO */
+ SET_V_RES_LOST(a_inc_res_fifo, 1); /* reset FIFO */
+ SET_V_RES_FIFO_ERR(a_inc_res_fifo, 1); /* reset FIFO */
+
+ SET_V_FIFO_NUM(r_fifo, x->testdata->b1);
+ SET_V_FIFO_DIR(r_fifo, 0); /* transmit */
+ WRITE_TO_REGS();
+ SET_V_FIFO_DIR(r_fifo, 1); /* receive */
+ WRITE_TO_REGS();
+
+ SET_V_FIFO_NUM(r_fifo, x->testdata->b2);
+ SET_V_FIFO_DIR(r_fifo, 0); /* transmit */
+ WRITE_TO_REGS();
+ SET_V_FIFO_DIR(r_fifo, 1); /* receive */
+ WRITE_TO_REGS();
+}
+#undef WRITE_TO_REGS
+#endif
+
+/**
+ * hdlc_signal_complete() - Signal dahdi that we have a complete frame.
+ *
+ * @xhfc_span: The span which received the frame.
+ * @stat: The frame status byte from the XHFC controller.
+ *
+ */
+static void hdlc_signal_complete(struct xhfc_span *xhfc_span, u8 stat)
+{
+ struct xhfc *xhfc = xhfc_span->xhfc;
+
+ /* if STAT != 0, indicates bad frame */
+ if (stat != 0x00) {
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(xhfc, "(span %d) STAT=0x%02x indicates " \
+ "frame problem: %s\n", xhfc_span->port + 1, stat,
+ (0xff == stat) ? "HDLC Abort" : "Bad FCS");
+ }
+
+ dahdi_hdlc_abort(xhfc_span->sigchan, (0xff == stat) ?
+ DAHDI_EVENT_ABORT : DAHDI_EVENT_BADFCS);
+ /* STAT == 0, means frame was OK */
+ } else {
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(xhfc, "(span %d) Frame "/*"%d"*/" is good!\n",
+ xhfc_span->port + 1/*, xhfc_span->frames_in*/);
+ }
+ dahdi_hdlc_finish(xhfc_span->sigchan);
+ }
+}
+
+
+/*
+ * Inner loop for D-channel receive function. Retrieves HDLC data from the
+ * hardware. If the hardware indicates that the frame is complete, we check
+ * the HDLC engine's STAT byte and update DAHDI as needed.
+ *
+ * Returns the number of HDLC frames left in the FIFO, or -1 if we couldn't
+ * get the lock.
+ */
+static int hdlc_rx_frame(struct xhfc_span *xhfc_span)
+{
+ int fifo, i, j, k, zleft;
+ int zlen, flen, new_flen;
+ unsigned char buf[HDLC_BUF_LEN];
+ char debugbuf[256];
+ struct xhfc *x = xhfc_span->xhfc;
+
+ fifo = dchan_fifo( xhfc_span );
+
+ if (DBG_HDLC && DBG_SPANFILTER)
+ xhfc_info(x, "hdlc_rx_frame fifo %d: start\n", fifo);
+
+ if (down_trylock(&x->fifosem) && DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(x, "rx_frame: fifo %d 1: couldn't get lock\n",
+ fifo);
+ return -1;
+ }
+
+ xhfc_selfifo(x, fifo, RECEIVE);
+
+ flen = get_Flen(x);
+ zlen = get_Zlen(x);
+ debug_fz(x, fifo, "hdlc_rx_frame", debugbuf);
+ up(&x->fifosem);
+
+ if (DBG_HDLC && DBG_SPANFILTER)
+ pr_info("%s", debugbuf);
+
+ /* if we have at least one complete frame, increment zleft to include
+ * status byte */
+ zleft = zlen;
+ if (flen)
+ zleft++;
+
+ do {
+ if (zleft > HDLC_BUF_LEN)
+ j = HDLC_BUF_LEN;
+ else
+ j = zleft;
+
+ if (down_trylock(&x->fifosem) && DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(x,
+ "rx_frame fifo %d 2: couldn't get lock\n",
+ fifo);
+ return -1;
+ }
+
+ xhfc_selfifo(x, fifo, RECEIVE);
+
+ for (i = 0; i < j; i++)
+ buf[i] = read_xhfc(x, A_FIFO_DATA);
+ up(&x->fifosem);
+
+ /* don't send STAT byte to DAHDI */
+ k = j;
+ if (xhfc_span->sigchan) {
+ if ((j != HDLC_BUF_LEN) && flen)
+ k--;
+ if (k)
+ dahdi_hdlc_putbuf(xhfc_span->sigchan, buf, k);
+ }
+
+ zleft -= j;
+
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(x, "transmitted %d bytes to dahdi, " \
+ "zleft=%d\n", k, zleft);
+ }
+
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(x, "hdlc_rx_frame(span %d): " \
+ "zlen=%d, zleft=%d\n",
+ xhfc_span->port + 1, zlen, zleft);
+ for (i = 0; i < j; i++) {
+ xhfc_info(x, "%02x%c", buf[i],
+ (i < (j - 1)) ? ' ' : '\n');
+ }
+ }
+ } while (zleft > 0);
+
+ /* Frame received, increment F2 and get an updated count of frames
+ * left */
+ if (down_trylock(&x->fifosem) && DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(x, "rx_frame fifo %d 3: couldn't get lock\n",
+ fifo);
+ return 0;
+ }
+
+ /* go get the F count again, just in case another frame snuck in while
+ * we weren't looking. */
+ if (flen) {
+ xhfc_resetfifo(x);
+ //++xhfc_span->frames_in; //LOLCACA
+ new_flen = get_Flen(x);
+ } else
+ new_flen = flen;
+
+ up(&x->fifosem);
+
+ /* If this channel is not configured with a signalling span we don't
+ * need to notify the rest of dahdi about this frame. */
+ if (!xhfc_span->sigchan) {
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(x, "hdlc_rx_frame fifo %d: " \
+ "new_flen %d, early end.\n", fifo, new_flen);
+ }
+ return new_flen;
+ }
+
+ if (flen) {
+ /* disable < 3 check for now */
+#if 0
+ if(zlen < 3) {
+ if (DBG_HDLC && DBG_SPANFILTER)
+ xhfc_info(x, "odd, zlen less then 3?\n");
+ dahdi_hdlc_abort(xhfc_span->sigchan, DAHDI_EVENT_ABORT);
+ } else
+#endif
+ hdlc_signal_complete(xhfc_span, buf[i - 1]);
+ }
+
+ if (DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(x, "hdlc_rx_frame fifo %d: new_flen=%d end.\n",
+ fifo, new_flen);
+ }
+
+ return new_flen;
+}
+
+static uint frames_out;
+/*
+ * Takes one blob of data from DAHDI and shoots it out to the hardware. The
+ * blob may or may not be a complete HDLC frame. If it isn't, the D-channel
+ * FIFO interrupt handler will take care of pulling the rest. Returns nonzero
+ * if there is still data to send in the current HDLC frame.
+ */
+static int hdlc_tx_frame(struct xhfc_span *xhfc_span)
+{
+ struct xhfc *x = xhfc_span->xhfc;
+ int res, i, fifo;
+ int zlen;
+ int flen = -1;
+ unsigned char buf[HDLC_BUF_LEN];
+ unsigned int size = ARRAY_SIZE(buf);
+ char debugbuf[256];
+
+ //XXX
+#if 0
+ /* if we're ignoring TE red alarms and we are in alarm, restart the
+ * S/T state machine */
+ if (!span->nt && (bspan->newalarm != 0)) {
+ hfc_start_st(bspan);
+ }
+#endif
+
+ fifo = dchan_fifo(xhfc_span);
+ res = dahdi_hdlc_getbuf(xhfc_span->sigchan, buf, &size);
+
+ //XXX LOLCACA
+ if (down_interruptible(&x->fifosem)) {
+ static int arg;
+ xhfc_info(x, "xhfc: arg (%d), grabbed data from DAHDI " \
+ "but couldn't grab the lock!\n", ++arg);
+ /* TODO: Inform DAHDI that we have grabbed data and can't use
+ * it */
+ dahdi_hdlc_abort(xhfc_span->sigchan, DAHDI_EVENT_OVERRUN);
+ return 1; /* return 1 so we keep trying */
+ }
+
+
+
+ xhfc_selfifo(x, fifo, TRANSMIT);
+
+ zlen = get_Zlen(x);
+ debug_fz(x, fifo, __func__, debugbuf);
+
+ /* TODO: check zlen, etc. */
+ if ((XHFC_ZMAX-zlen) < size) {
+ static int arg;
+ xhfc_info(x, "xhfc: arg (%d), zlen (%d) < what we " \
+ "grabbed from DAHDI (%d)!\n", ++arg, zlen, size);
+ size = zlen;
+ dahdi_hdlc_abort(xhfc_span->sigchan, DAHDI_EVENT_OVERRUN);
+ }
+
+ if (size > 0) {
+ xhfc_span->sigactive = 1;
+
+ for (i = 0; i < size; i++)
+ write_xhfc(x, A_FIFO_DATA, buf[i]);
+ /*
+ * If we got a full frame from DAHDI, increment F and
+ * decrement our HDLC pending counter. Otherwise, select the
+ * FIFO again (to start transmission) and make sure the TX IRQ
+ * is enabled so we will get called again to finish off the
+ * data
+ */
+ if (res != 0) {
+ frames_out++;
+ xhfc_span->sigactive = 0;
+ xhfc_inc_f(x);
+ atomic_dec(&xhfc_span->hdlc_pending);
+ } else {
+ xhfc_selfifo(x, fifo, TRANSMIT);
+ }
+ }
+
+ up(&x->fifosem);
+
+ if (0 && DBG_HDLC && DBG_SPANFILTER) {
+ xhfc_info(xhfc_span->xhfc, "%s", debugbuf);
+
+ xhfc_info(xhfc_span->xhfc, "hdlc_tx_frame(span %d): DAHDI gave %d "
+ "bytes for FIFO %d (res = %d)\n",
+ xhfc_span->port + 1, size, fifo, res);
+
+ for (i = 0; i < size; i++)
+ xhfc_info(xhfc_span->xhfc,
+ "%02x%c\n", buf[i],
+ (i < (size - 1)) ? ' ' : '\n');
+
+ if (size && res != 0) {
+ pr_info("Transmitted frame %d on span %d\n",
+ frames_out - 1, xhfc_span->port);
+ }
+ }
+
+ return (res == 0);
+}
+
+/* DAHDI calls this when it has data it wants to send to the HDLC controller */
+void xhfc_hdlc_hard_xmit(struct dahdi_chan *chan)
+{
+ struct xhfc_span *xhfc_span;
+ struct dahdi_span *dspan;
+ struct xhfc *xhfc;
+ int span;
+
+ dspan = chan->span;
+ xhfc_span = dspan->pvt;
+ xhfc =xhfc_span->xhfc;
+ span = xhfc_span->port;
+
+ if ((DBG_FOPS || DBG_HDLC) && DBG_SPANFILTER) {
+ xhfc_info(xhfc, "hdlc_hard_xmit on chan %s (%i/%i), " \
+ "span=%i (sigchan=%p, chan=%p)\n", chan->name,
+ chan->channo, chan->chanpos, span + 1,
+ xhfc_span->sigchan, chan);
+ }
+
+ /* Increment the hdlc_pending counter and trigger the bottom-half so
+ * it will be picked up and sent. */
+ if (xhfc_span->sigchan == chan)
+ atomic_inc(&xhfc_span->hdlc_pending);
+}
+
+#if 0
+int
+xhfc_hdlc_init(struct xhfc* x)
+{
+
+ xhfc_reset(x);
+ xhfc_cfg_pcm(x, XHFC_PCM_MASTER);
+
+ return 0;
+}
+#endif
+
+#if 0 // LOLCACA
+int
+xhfc_hdlc_init_test_st(struct xhfc* x, int port, int nt)
+{
+
+ x->testdata->pcm = 0;
+ x->testdata->port = port;
+
+ x->testdata->b1 = port * 4 + 0;
+ x->testdata->b2 = port * 4 + 1;
+ x->testdata->dchan = port * 4 + 2;
+ x->testdata->nt = nt;
+
+dbg( xhfc_config_d_chan_on_fifo(x); );
+dbg( xhfc_config_st(x, port, nt); );
+
+ return 0;
+}
+
+int
+xhfc_hdlc_init_test_pcm(struct xhfc* x, int fifo, int slot)
+{
+ x->testdata->pcm = 1;
+ x->testdata->dchan = fifo;
+ x->testdata->ts = slot;
+
+dbg( xhfc_config_d_chan_to_pcm(x, fifo, slot); );
+
+ return 0;
+}
+
+void
+xhfc_flush_fifo(struct xhfc* x, int fifo)
+{
+ int cnt = 0, len;
+ u8 rcv[64];
+
+ do {
+ len = ARRAY_SIZE(rcv);
+ xhfc_hdlc_read_buf(x, x->testdata->dchan, rcv, &len);
+ if(len == 0) {
+ printk("Read %d frames (or less) while flushing the"
+ " FIFO.\n", cnt);
+ return;
+ }
+ } while(cnt++ < 100);
+
+ printk("Got an infinite loop while trying to flush the FIFO.\n");
+}
+#endif
+
+int
+xhfc_st_ready(struct xhfc* x, int port, int nt)
+{
+ u8 r_su_sel = 0x00, a_su_rd_sta;
+ write_xhfc(x, R_SU_SEL, SET_V_SU_SEL(r_su_sel, port));
+ a_su_rd_sta = read_xhfc(x, A_SU_RD_STA);
+
+ return (nt && GET_V_SU_STA(a_su_rd_sta) == 3) ||
+ GET_V_SU_STA(a_su_rd_sta) == 7;
+}
+
+#if USE_FIFO_IRQ
+int
+xhfc_fifo_irq(struct xhfc* x, int fifo, int receive)
+{
+ u8 reg, mask;
+ int bit;
+
+ reg = ( fifo / 4 == 0 ? R_FIFO_BL0_IRQ :
+ fifo / 4 == 1 ? R_FIFO_BL1_IRQ :
+ fifo / 4 == 2 ? R_FIFO_BL2_IRQ :
+ R_FIFO_BL3_IRQ);
+ bit = (fifo % 4) * 2 + !!receive;
+ mask = 1u << bit;
+
+ return read_xhfc(x, reg) & mask;
+}
+#endif
+
+#if 0
+static u8 cnt = 0;
+/* write in the FIFO while it's not full, and continuously reads */
+int
+xhfc_hdlc_interrupt(struct xhfc* x)
+{
+ int zlen, flen, i, res, len, msglen, chan;
+ char msg[100];
+
+ u8 buf[] = TEST_BUFFER;
+ u8 rcv[2 * ARRAY_SIZE(buf)]; // longer than expected for testing
+
+// if(x->irq_cnt < 50) return 1; // 5Oms wait
+
+ if(!x->testdata->pcm)
+ if(!xhfc_st_ready(x, x->testdata->port, x->testdata->nt)) {
+ printk(KERN_INFO "Waiting for the S/T interface to "
+ "activate.\n");
+ return 1;
+ }
+
+ msglen = 0;
+ msg[0] = '\0';
+
+ //trigger=1;
+
+
+ chan = x->testdata->dchan;
+
+#if USE_FIFO_IRQ
+ if(xhfc_fifo_irq(x, chan, 0)) {
+#endif
+
+ xhfc_selfifo(x, chan, 0);
+
+ while ( zlen = get_Zlen(x),
+ flen = get_Flen(x),
+ XHFC_ZMAX - zlen >= ARRAY_SIZE(buf) && flen < XHFC_FMAX) {
+
+ buf[ARRAY_SIZE(buf) - 1] = cnt++;
+
+ xhfc_hdlc_write_buf(x, chan, buf, ARRAY_SIZE(buf));
+ xhfc_inc_f(x);
+ }
+#if USE_FIFO_IRQ
+ }
+
+ if(xhfc_fifo_irq(x, chan, 1)) {
+#endif
+
+ len = ARRAY_SIZE(rcv);
+ res = xhfc_hdlc_read_buf(x, chan, rcv, &len);
+
+ if(CORRUPTED_FRAME == res) {
+ x->testdata->crpt++;
+ }
+ if(len) {
+ /* The case in which the frame is too long for the
+ * buffer is ignored in this test.
+ */
+ x->testdata->rcv++;
+ if(len < 2) {
+ msglen = sprintf(msg, "Received (shorter than 2 bytes): ");
+ for(i = 0; i < len; i++)
+ msglen += sprintf(msg+msglen, "%02x", rcv[i]);
+ } else {
+ msglen = sprintf(msg, "Received values: ");
+ for(i = 0; i < len - 2; i++)
+ msglen += sprintf(msg+msglen, "%02x", rcv[i]);
+
+ msglen += sprintf(msg+msglen, ", CRC=");
+ for(i = len - 2; i < len; i++)
+ msglen += sprintf(msg+msglen, "%02x", rcv[i]);
+ }
+ printk(KERN_INFO "%s\n", msg);
+ }
+#if USE_FIFO_IRQ
+ }
+#endif
+
+ return 1; //XXX
+}
+
+#endif
+
+
+#if 0
+void
+xhfc_hdlc_write_buf(struct xhfc* x, int fifo, u8* buf, int len)
+{
+ int i;
+
+ xhfc_selfifo(x, fifo, 0);
+
+ for(i = 0; i < len; i++)
+ write_xhfc(x, A_FIFO_DATA, buf[i]);
+}
+
+
+/* Read a frame, or *buflen bytes if the frame is longer than *buflen bytes.
+ * Sets *buflen to the number of bytes read.
+ * Returns the number of bytes remaining to read in the current frame, or
+ * CORRUPTED_FRAME if the frame was corrupted.
+ */
+int
+xhfc_hdlc_read_buf(struct xhfc* x, int fifo, u8* buf, int *buflen)
+{
+ u8 stat;
+ int i, zlen, flen;
+
+ xhfc_selfifo(x, fifo, 1);
+
+ flen = get_Flen(x);
+ zlen = get_Zlen(x);
+
+ if(flen == 0 || zlen == 0) {
+ *buflen = 0;
+ return 0;
+ }
+
+ *buflen = zlen < *buflen ? zlen : *buflen;
+
+ for(i = 0; i < *buflen; i++)
+ buf[i] = read_xhfc(x, A_FIFO_DATA);
+
+ if (zlen <= *buflen) {
+ /* (the end of) A whole frame has been read */
+ stat = read_xhfc(x, A_FIFO_DATA);
+ xhfc_inc_f(x);
+
+ if(stat) {
+ printk(KERN_INFO "Dropped corrupted frame (STAT:"
+ " %02x) ", stat);
+ // dahdi_hdlc_abort will be called here.
+ return CORRUPTED_FRAME;
+ }
+
+ return 0;
+ }
+
+ return zlen - *buflen;
+}
+#endif
+
/*******************************************/
/* Interrupt handler, just handle TimerIRQ */
/******************************************/
@@ -213,8 +843,8 @@ xhfc_interrupt(int irq, void *dev_id, struct pt_regs* ptregs)
{
/* (void) ptregs; */
- struct xhfc_pi * pi = dev_id;
- struct xhfc * xhfc = NULL;
+ struct xhfc_pi *pi = dev_id;
+ struct xhfc *xhfc;
u8 misc_irq;
xhfc = &pi->xhfc;
@@ -236,7 +866,7 @@ xhfc_interrupt(int irq, void *dev_id, struct pt_regs* ptregs)
if (misc_irq & M_TI_IRQMSK)
xhfc->irq_cnt++;
- xhfc_hdlc_interrupt(xhfc);
+#warning xhfc_hdlc_interrupt(xhfc);
return IRQ_HANDLED;
}
@@ -249,6 +879,7 @@ static void
disable_interrupts(struct xhfc * xhfc)
{
printk(KERN_INFO "%s %s\n", xhfc->name, __FUNCTION__);
+ xhfc->running = 0;
write_xhfc(xhfc, R_IRQ_CTRL, 0);
}
@@ -261,6 +892,8 @@ enable_interrupts(struct xhfc * xhfc)
{
printk(KERN_INFO "%s %s\n", xhfc->name, __FUNCTION__);
+ xhfc->running = 1;
+
/* set PCM master mode */
write_xhfc(xhfc, R_PCM_MD0, M_PCM_MD | M_F0_LEN);
// Setting M_F0_LEN here so it's not overwritten
@@ -336,19 +969,275 @@ void xhfc_hard_reset(void)
gpio_set_level(reset_gpio, GPIO_HIGH);
}
-void configure_interface(void)
+#if 0
+void configure_port(int port, int nt, int lineterm)
{
gpio_set_direction(lineterm_gpio, GPIO_OUTPUT);
gpio_set_to_gpio(lineterm_gpio);
gpio_set_direction(ntte_gpio, GPIO_OUTPUT);
gpio_set_to_gpio(ntte_gpio);
- gpio_set_level(lineterm_gpio, lineterm_conf);
- gpio_set_level(ntte_gpio, ntte_conf);
+ gpio_set_level(lineterm_gpio, lineterm);
+ gpio_set_level(ntte_gpio, nt);
+}
+#endif
+
+void xhfc_init_and_configure(struct xhfc* x)
+{
+ int i;
+
+ /* In DAHDI's driver's this part is "stage 1" */
+ xhfc_reset(x);
+
+ /* Make sure interrupts disabled */
+ disable_interrupts(x);
+
+ /* XXX Other drivers do a lot of "let's make sure that this isn't
+ * activated" and "let's clear that and that and again that" that
+ * should prolly be done here too. For now, let's suppose it doesn't
+ * need to be done immediatly
+ */
+
+ /* "stage 2" */
+ xhfc_config_pcm(x, XHFC_PCM_MASTER);
+
+ for(i = 0; i < SPANS_PER_CHIP; i++)
+ xhfc_config_st(x, x->spans[i].port, x->spans[i].nt);
+
+ /* No we need to setup the flow controller and stuff */
+
+ for(i = 0; i < SPANS_PER_CHIP; i++) {
+ //XXX Only the D chan is supposed to work atm.
+ xhfc_config_d_chan_on_fifo(&x->spans[i]);
+ }
+ // NEVAR FORG3T 4 8 15 16 23 42
+}
+
+/* spanconfig for us means ...? */ /* XXX This comment for us means ...? */
+int xhfc_spanconfig(struct dahdi_span *span, struct dahdi_lineconfig *lc)
+{
+ struct xhfc_span *xhfc_span;
+ struct xhfc *xhfc;
+ int te_mode, term;
+ int pos;
+
+ xhfc_span = span->pvt;
+ xhfc = xhfc_span->xhfc;
+
+ // xhfc_disable_workqueues(xhfc->wc); //XXX
+
+ te_mode = (lc->lineconfig & DAHDI_CONFIG_NTTE) ? 0 : 1;
+
+ term = (lc->lineconfig & DAHDI_CONFIG_TERM) ? 1 : 0;
+
+ xhfc_info(xhfc, "xhfc: Configuring port %d span %d in %s " \
+ "mode with termination resistance %s\n", xhfc_span->port,
+ span->spanno, (te_mode) ? "TE" : "NT",
+ (term) ? "ENABLED" : "DISABLED");
+
+#warning xhfc_set_ntte(xhfc_span, te_mode, term);
+ xhfc_info(xhfc, "WARNING: This driver can't configure the ports yet, "
+ "expect problems if port %d isn't configured in %s "
+ "mode.\n", xhfc_span->port, te_mode ? "TE" : "NT");
+
+ if (lc->sync < 0) {
+ xhfc_info(xhfc, "Span %d has invalid sync priority (%d), " \
+ "removing from sync source list\n", span->spanno,
+ lc->sync);
+ lc->sync = 0;
+ }
+
+ // XXX ??
+#if 0
+ if (span->offset >= 4) {
+ pos = span->offset;
+ } else {
+ /* This is tricky. Have to figure out if we're slot 1 or slot
+ * 2 */
+ pos = span->offset + xhfc->position;
+ }
+#else
+ pos = span->offset; // I guess
+#endif
+
+ if (!te_mode && lc->sync) {
+ xhfc_info(xhfc, "NT Spans cannot be timing sources. " \
+ "Span %d requested to be timing source of " \
+ "priority %d. Changing priority to 0\n", pos,
+ lc->sync);
+ lc->sync = 0;
+ }
+
+ // wc->spans[pos]->timing_priority = lc->sync;
+
+#if 0
+ // xhfc_reset_span(xhfc_span);
+
+ /* call startup() manually here, because DAHDI won't call the startup
+ * function unless it receives an IOCTL to do so, and dahdi_cfg
+ * doesn't. */
+ // xhfc_startup(span);
+#endif
+
+ span->flags |= DAHDI_FLAG_RUNNING;
+
+ //xhfc_enable_workqueues(xhfc->wc);
+
+ return 0;
+}
+
+/* chanconfig for us means to configure the HDLC controller, if appropriate
+ *
+ * NOTE: apparently the DAHDI ioctl function calls us with a interrupts
+ * disabled. This means we cannot actually touch the hardware, because all
+ * register accesses are wrapped up in a mutex that can sleep.
+ *
+ * The solution to that is to simply increment the span's "restart" flag, and
+ * the driver's workqueue will do the dirty work on our behalf.
+ */
+int xhfc_chanconfig(struct dahdi_chan *chan, int sigtype)
+{
+ int alreadyrunning;
+ struct xhfc_span *bspan = chan->span->pvt;
+ struct xhfc *b4 = bspan->xhfc;
+ int res;
+
+ alreadyrunning = bspan->span.flags & DAHDI_FLAG_RUNNING;
+
+ if (DBG_FOPS) {
+ xhfc_info(b4, "%s channel %d (%s) sigtype %08x\n",
+ alreadyrunning ? "Reconfigured" : "Configured",
+ chan->channo, chan->name, sigtype);
+ }
+
+ switch (sigtype) {
+ case DAHDI_SIG_HARDHDLC:
+ if (DBG_FOPS) {
+ xhfc_info(b4, "%sonfiguring hardware HDLC on %s\n",
+ ((sigtype == DAHDI_SIG_HARDHDLC) ? "C" :
+ "Unc"), chan->name);
+ }
+ bspan->sigchan = chan;
+ bspan->sigactive = 0;
+ atomic_set(&bspan->hdlc_pending, 0);
+ res = 0;
+ break;
+ case DAHDI_SIG_HDLCFCS:
+ case DAHDI_SIG_HDLCNET:
+ case DAHDI_SIG_HDLCRAW:
+ /* Only HARDHDLC is supported for the signalling channel on BRI
+ * spans. */
+ res = -EINVAL;
+ break;
+ default:
+ res = 0;
+ break;
+ };
+
+ return res;
+}
+
+int xhfc_startup(struct dahdi_span* span) //XXX not sure if useful
+{
+ enable_interrupts(((struct xhfc_span*) span->pvt)->xhfc);
+ return 0;
+}
+
+#if 0
+int xhfc_shutdown(struct dahdi_span* span) /* XXX not sure if useful
+ Plus should shutting down one
+ span shut down all the spans? */
+{
+ disable_interrupts(span->pvt->xhfc);
+ return 0;
+}
+#endif
+
+static int xhfc_ioctl(struct dahdi_chan *chan, unsigned int cmd, unsigned long data)
+{
+ switch(cmd) {
+ default:
+ return -ENOTTY;
+ }
+
+ return 0;
+}
+
+void init_spans(struct xhfc* x)
+{
+ int i, j;
+ struct xhfc_span *xhfc_span;
+ struct dahdi_chan *chan;
+
+/* for each span on the card */
+ for (i=0; i < SPANS_PER_CHIP; i++) {
+ xhfc_span = &x->spans[i];
+
+ xhfc_span->span.irq = x->pi->pci_dev->irq;
+ xhfc_span->span.pvt = xhfc_span;
+ xhfc_span->span.spantype = xhfc_span->nt ? "NT" : "TE";
+ xhfc_span->span.offset = i;
+ xhfc_span->span.channels = CHANS_PER_SPAN;
+ xhfc_span->span.flags = 0;
+
+ xhfc_span->span.deflaw = DAHDI_LAW_ALAW;
+ /* For simplicty, we'll accept all line modes since BRI
+ * ignores this setting anyway.*/ // XXX XXX
+ xhfc_span->span.linecompat = DAHDI_CONFIG_AMI |
+ DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 |
+ DAHDI_CONFIG_ESF | DAHDI_CONFIG_HDB3 |
+ DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4;
+
+ sprintf(xhfc_span->span.name, "XHFC/%d", i+1);
+ sprintf(xhfc_span->span.desc, "XHFC Span %d", i+1);
+ xhfc_span->span.manufacturer = "Avencall";
+ dahdi_copy_string(xhfc_span->span.devicetype, "XHFC-4SU",
+ sizeof(xhfc_span->span.devicetype));
+ sprintf(xhfc_span->span.location, "PCI Bus %02d Slot %02d",
+ x->pi->pci_dev->bus->number,
+ PCI_SLOT(x->pi->pci_dev->devfn) + 1);
+
+ xhfc_span->span.owner = THIS_MODULE;
+ xhfc_span->span.spanconfig = xhfc_spanconfig;
+ xhfc_span->span.chanconfig = xhfc_chanconfig;
+ xhfc_span->span.startup = xhfc_startup; //XXX not sure this is useful
+ //xhfc_span->span.shutdown = xhfc_shutdown; // wai
+ //xhfc_span->span.open = b4xxp_open; // don't need this i think
+ //xhfc_span->span.close = b4xxp_close; // XXX nor this one
+ xhfc_span->span.ioctl = xhfc_ioctl;
+ xhfc_span->span.hdlc_hard_xmit = xhfc_hdlc_hard_xmit;
+ /* T'ES FOU C'EST TROP BIEN L'ECHO
+ if (vpmsupport && CARD_HAS_EC(b4)) //XXX LOLCACACACA
+ xhfc_span->span.echocan_create = echocan_create;
+ */
+
+/* HDLC stuff */
+ xhfc_span->sigchan = NULL;
+ xhfc_span->sigactive = 0; // XXX nanikore
+
+ xhfc_span->span.chans = xhfc_span->chans;
+
+ //init_waitqueue_head(&xhfc_span->span.maintq);
+
+/* now initialize each channel in the span */
+ for (j=0; j < CHANS_PER_SPAN; j++) {
+ xhfc_span->chans[j] = &xhfc_span->_chans[j];
+ chan = xhfc_span->chans[j];
+ chan->pvt = x;
+
+ sprintf(chan->name, "XHFC/%d/%d", i + 1, j + 1);
+ /* The last channel in the span is the D-channel */
+ if (j == CHANS_PER_SPAN - 1) {
+ chan->sigcap = DAHDI_SIG_HARDHDLC;
+ } else {
+ chan->sigcap = DAHDI_SIG_CLEAR | DAHDI_SIG_DACS;
+ }
+ chan->chanpos = j + 1;
+ chan->writechunk = (void *)(xhfc_span->writechunk + j * DAHDI_CHUNKSIZE);
+ chan->readchunk = (void *)(xhfc_span->readchunk + j * DAHDI_CHUNKSIZE);
+ }
+ }
}
-/************************/
-/* release single card */
-/************************/
void
release_card_irq(struct xhfc_pi * pi)
{
@@ -367,17 +1256,18 @@ static const struct pci_device_id tlp_leb_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, tlp_leb_pci_tbl);
static struct pci_driver xhfc_driver = {
- .name = DRV_NAME,
- .id_table = tlp_leb_pci_tbl,
- .probe = xhfc_init_one,
- .remove = __devexit_p(xhfc_remove_one),
- .shutdown = xhfc_shutdown,
+ .name = DRIVER_NAME,
+ .id_table = tlp_leb_pci_tbl,
+ .probe = xhfc_init_one,
+ .remove = __devexit_p(xhfc_remove_one),
+ .shutdown = xhfc_shutdown,
};
int __devinit xhfc_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
+ int span;
struct xhfc_pi * pi;
unsigned long base, size;
int rc = -ENOMEM;
@@ -389,7 +1279,7 @@ int __devinit xhfc_init_one(struct pci_dev *pdev,
* Memory alloc *
****************/
- pi = kmalloc(sizeof(struct xhfc_pi), GFP_KERNEL);
+ pi = kzalloc(sizeof(struct xhfc_pi), GFP_KERNEL);
if (pi == NULL)
goto err_alloc;
@@ -413,7 +1303,7 @@ int __devinit xhfc_init_one(struct pci_dev *pdev,
printk(KERN_DEBUG "pci_enable_device succeeded.\n");
- rc = pci_request_regions(pdev, DRV_NAME);
+ rc = pci_request_regions(pdev, DRIVER_NAME);
if (rc)
goto err_request_regions;
@@ -434,6 +1324,17 @@ int __devinit xhfc_init_one(struct pci_dev *pdev,
pci_set_drvdata(pdev, pi);
+ /*************
+ * Hard init *
+ *************/
+
+ leb_init(pi);
+
+ //if(softconf) configure_interface();
+
+ xhfc_hard_reset();
+
+
/********************
* XHFC struct init *
********************/
@@ -444,24 +1345,18 @@ int __devinit xhfc_init_one(struct pci_dev *pdev,
/* only adress one xhfc per PI */
pi->xhfc.chipidx = 0;
pi->xhfc.name = pi->name;
- /* init interrupt engine */
- rc = pi->irq = acpi_register_gsi(IRQ_TLP_GPIO_30, ACPI_LEVEL_SENSITIVE, ACPI_ACTIVE_LOW);
+ /* init interrupt engine */
+ rc = pi->irq = acpi_register_gsi(
+ IRQ_TLP_GPIO_30,
+ ACPI_LEVEL_SENSITIVE,
+ ACPI_ACTIVE_LOW);
if(rc < 0)
goto err_acpi_register;
pi->xhfc.r_pcm_md0 = 0;
-
- /*************
- * Hard init *
- *************/
-
- leb_init(pi);
-
- if(softconf) configure_interface();
-
- xhfc_hard_reset();
+ init_MUTEX(&pi->xhfc.fifosem);
/********
@@ -479,17 +1374,6 @@ int __devinit xhfc_init_one(struct pci_dev *pdev,
card_cnt++;
- // XXX no module argument checking done!
- rc = xhfc_hdlc_init(&pi->xhfc);
- if (rc < 0)
- goto err_init_test;
-
- if(pcm)
- xhfc_hdlc_init_test_pcm(&pi->xhfc, fifo, slot);
- else
- xhfc_hdlc_init_test_st(&pi->xhfc, port, nt);
-
-
if (request_irq(pi->irq, &xhfc_interrupt, IRQF_SHARED, "XHFC_HWTD", pi)) {
printk(KERN_WARNING "%s %s: couldn't get interrupt %d\n",
pi->name, __FUNCTION__, pi->irq);
@@ -497,12 +1381,25 @@ int __devinit xhfc_init_one(struct pci_dev *pdev,
goto err_request_irq;
}
+ xhfc_init_and_configure(&pi->xhfc);
+
+ init_spans(&pi->xhfc);
+
+ for(span = 0; span < SPANS_PER_CHIP; span++)
+ if(dahdi_register(&pi->xhfc.spans[span].span, /*prefmaster*/ 0)) {
+ rc = -EINVAL; // XXX doesn't make too much sense
+ goto err_dahdi_register;
+ }
+ // don't care/know about prefmaster I think?
+
enable_interrupts(&pi->xhfc);
return 0;
+err_dahdi_register:
+ for(; span >= 0; span--)
+ dahdi_unregister(&pi->xhfc.spans[span].span);
err_request_irq:
-err_init_test:
err_collect_chip_id:
//acpi_unregister_gsi(IRQ_TLP_GPIO_30); /* symbol doesn't exist??? */
err_acpi_register:
@@ -521,6 +1418,7 @@ err_alloc:
void __devexit xhfc_remove_one(struct pci_dev *pdev)
{
+ int i;
struct xhfc_pi *pi;
printk(KERN_DEBUG "entering tlp_leb_remove_one.\n");
@@ -530,9 +1428,12 @@ void __devexit xhfc_remove_one(struct pci_dev *pdev)
printk(KERN_INFO "%s %s: removing card\n", pi->name,
__FUNCTION__);
+ for(i = 0; i < SPANS_PER_CHIP; i++)
+ dahdi_unregister(&pi->xhfc.spans[i].span);
+
release_card_irq(pi);
- xhfc_hdlc_end_test(&pi->xhfc);
+ //xhfc_hdlc_end_test(&pi->xhfc);
card_cnt--;