diff options
author | Noé Rubinstein <nrubinstein@proformatique.com> | 2010-08-04 11:37:50 +0200 |
---|---|---|
committer | Noé Rubinstein <nrubinstein@proformatique.com> | 2010-08-04 11:37:50 +0200 |
commit | 8ed88c4d9d3840beaf4cc670e657a8d940c3ab93 (patch) | |
tree | fb99c9a78a5817d0f6e9cbe064d840abed432dd0 /xhfc/xhfc.c | |
parent | fe526e59ca388d149ee52f3cef80325343d6b6d4 (diff) |
some progress made on the XHFC driver module
Diffstat (limited to 'xhfc/xhfc.c')
-rw-r--r-- | xhfc/xhfc.c | 1083 |
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--; |