Пример #1
0
/*---------------------------------------------------------------------------*
 *	HSCX IRQ Handler
 *---------------------------------------------------------------------------*/
void
isic_hscx_irq(register struct isic_softc *sc, u_char ista, int h_chan, u_char ex_irq)
{
	register l1_bchan_state_t *chan = &sc->sc_chan[h_chan];
	u_char exir = 0;
	int activity = -1;
	u_char cmd = 0;

	NDBGL1(L1_H_IRQ, "%#x", ista);

	if(ex_irq)
	{
		/* get channel extended irq reg */

		exir = HSCX_READ(h_chan, H_EXIR);

		if(exir & HSCX_EXIR_RFO)
		{
			chan->stat_RFO++;
			NDBGL1(L1_H_XFRERR, "ex_irq: receive data overflow");
		}

		if((exir & HSCX_EXIR_XDU) && (chan->bprot != BPROT_NONE))/* xmit data underrun */
		{
			chan->stat_XDU++;
			NDBGL1(L1_H_XFRERR, "ex_irq: xmit data underrun");
			isic_hscx_cmd(sc, h_chan, HSCX_CMDR_XRES);

			if (chan->out_mbuf_head != NULL)  /* don't continue to transmit this buffer */
			{
				i4b_Bfreembuf(chan->out_mbuf_head);
				chan->out_mbuf_cur = chan->out_mbuf_head = NULL;
			}
		}

	}

	/* rx message end, end of frame */

	if(ista & HSCX_ISTA_RME)
	{
		register int fifo_data_len;
		u_char rsta;
		int error = 0;

		rsta = HSCX_READ(h_chan, H_RSTA);

		if((rsta & 0xf0) != 0xa0)
		{
			if((rsta & HSCX_RSTA_VFR) == 0)
			{
				chan->stat_VFR++;
				cmd |= (HSCX_CMDR_RHR);
				NDBGL1(L1_H_XFRERR, "received invalid Frame");
				error++;
			}

			if(rsta & HSCX_RSTA_RDO)
			{
				chan->stat_RDO++;
				NDBGL1(L1_H_XFRERR, "receive data overflow");
				error++;
			}

			if((rsta & HSCX_RSTA_CRC) == 0)
			{
				chan->stat_CRC++;
				cmd |= (HSCX_CMDR_RHR);
				NDBGL1(L1_H_XFRERR, "CRC check failed");
				error++;
			}

			if(rsta & HSCX_RSTA_RAB)
			{
				chan->stat_RAB++;
				NDBGL1(L1_H_XFRERR, "Receive message aborted");
				error++;
			}
		}

		fifo_data_len = ((HSCX_READ(h_chan, H_RBCL)) &
						((sc->sc_bfifolen)-1));

		if(fifo_data_len == 0)
			fifo_data_len = sc->sc_bfifolen;

		/* all error conditions checked, now decide and take action */

		if(error == 0)
		{
			if(chan->in_mbuf == NULL)
			{
				if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL)
					panic("L1 isic_hscx_irq: RME, cannot allocate mbuf!");
				chan->in_cbptr = chan->in_mbuf->m_data;
				chan->in_len = 0;
			}

			fifo_data_len -= 1; /* last byte in fifo is RSTA ! */

			if((chan->in_len + fifo_data_len) <= BCH_MAX_DATALEN)
			{
				/* read data from HSCX fifo */

				HSCX_RDFIFO(h_chan, chan->in_cbptr, fifo_data_len);

				cmd |= (HSCX_CMDR_RMC);
				isic_hscx_cmd(sc, h_chan, cmd);
				cmd = 0;

		                chan->in_len += fifo_data_len;
				chan->rxcount += fifo_data_len;

				/* setup mbuf data length */

				chan->in_mbuf->m_len = chan->in_len;
				chan->in_mbuf->m_pkthdr.len = chan->in_len;

				if(sc->sc_trace & TRACE_B_RX)
				{
					i4b_trace_hdr hdr;
					hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
					hdr.dir = FROM_NT;
					hdr.count = ++sc->sc_trace_bcount;
					isdn_layer2_trace_ind(&sc->sc_l2, sc->sc_l3token, &hdr, chan->in_mbuf->m_len, chan->in_mbuf->m_data);
				}

				(*chan->l4_driver->bch_rx_data_ready)(chan->l4_driver_softc);

				activity = ACT_RX;

				/* mark buffer ptr as unused */

				chan->in_mbuf = NULL;
				chan->in_cbptr = NULL;
				chan->in_len = 0;
			}
			else
			{
				NDBGL1(L1_H_XFRERR, "RAWHDLC rx buffer overflow in RME, in_len=%d, fifolen=%d", chan->in_len, fifo_data_len);
				chan->in_cbptr = chan->in_mbuf->m_data;
				chan->in_len = 0;
				cmd |= (HSCX_CMDR_RHR | HSCX_CMDR_RMC);
			}
		}
		else
		{
			if (chan->in_mbuf != NULL)
			{
				i4b_Bfreembuf(chan->in_mbuf);
				chan->in_mbuf = NULL;
				chan->in_cbptr = NULL;
				chan->in_len = 0;
			}
			cmd |= (HSCX_CMDR_RMC);
		}
	}

	/* rx fifo full */

	if(ista & HSCX_ISTA_RPF)
	{
		if(chan->in_mbuf == NULL)
		{
			if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL)
				panic("L1 isic_hscx_irq: RPF, cannot allocate mbuf!");
			chan->in_cbptr = chan->in_mbuf->m_data;
			chan->in_len = 0;
		}

		chan->rxcount += sc->sc_bfifolen;

		if((chan->in_len + sc->sc_bfifolen) <= BCH_MAX_DATALEN)
		{
			/* read data from HSCX fifo */

			HSCX_RDFIFO(h_chan, chan->in_cbptr, sc->sc_bfifolen);

			chan->in_cbptr += sc->sc_bfifolen;
	                chan->in_len += sc->sc_bfifolen;
		}
		else
		{
			if(chan->bprot == BPROT_NONE)
			{
				/* setup mbuf data length */

				chan->in_mbuf->m_len = chan->in_len;
				chan->in_mbuf->m_pkthdr.len = chan->in_len;

				if(sc->sc_trace & TRACE_B_RX)
				{
					i4b_trace_hdr hdr;
					hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
					hdr.dir = FROM_NT;
					hdr.count = ++sc->sc_trace_bcount;
					isdn_layer2_trace_ind(&sc->sc_l2, sc->sc_l3token, &hdr,chan->in_mbuf->m_len, chan->in_mbuf->m_data);
				}

				/* silence detection */

				if(!(isdn_bchan_silence(chan->in_mbuf->m_data, chan->in_mbuf->m_len)))
					activity = ACT_RX;

				if(!(IF_QFULL(&chan->rx_queue)))
				{
					IF_ENQUEUE(&chan->rx_queue, chan->in_mbuf);
				}
				else
				{
					i4b_Bfreembuf(chan->in_mbuf);
				}

				/* signal upper driver that data is available */

				(*chan->l4_driver->bch_rx_data_ready)(chan->l4_driver_softc);

				/* alloc new buffer */

				if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL)
					panic("L1 isic_hscx_irq: RPF, cannot allocate new mbuf!");

				/* setup new data ptr */

				chan->in_cbptr = chan->in_mbuf->m_data;

				/* read data from HSCX fifo */

				HSCX_RDFIFO(h_chan, chan->in_cbptr, sc->sc_bfifolen);

				chan->in_cbptr += sc->sc_bfifolen;
				chan->in_len = sc->sc_bfifolen;

				chan->rxcount += sc->sc_bfifolen;
			}
			else
			{
				NDBGL1(L1_H_XFRERR, "RAWHDLC rx buffer overflow in RPF, in_len=%d", chan->in_len);
				chan->in_cbptr = chan->in_mbuf->m_data;
				chan->in_len = 0;
				cmd |= (HSCX_CMDR_RHR);
			}
		}

		/* command to release fifo space */

		cmd |= HSCX_CMDR_RMC;
	}

	/* transmit fifo empty, new data can be written to fifo */

	if(ista & HSCX_ISTA_XPR)
	{
		/*
		 * for a description what is going on here, please have
		 * a look at isic_bchannel_start() in i4b_bchan.c !
		 */

		int len;
		int nextlen;

		NDBGL1(L1_H_IRQ, "%s, chan %d - XPR, Tx Fifo Empty!", device_xname(sc->sc_dev), h_chan);

		if(chan->out_mbuf_cur == NULL) 	/* last frame is transmitted */
		{
			IF_DEQUEUE(&chan->tx_queue, chan->out_mbuf_head);

			if(chan->out_mbuf_head == NULL)
			{
				chan->state &= ~HSCX_TX_ACTIVE;
				(*chan->l4_driver->bch_tx_queue_empty)(chan->l4_driver_softc);
			}
			else
			{
				chan->state |= HSCX_TX_ACTIVE;
				chan->out_mbuf_cur = chan->out_mbuf_head;
				chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data;
				chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len;

				if(sc->sc_trace & TRACE_B_TX)
				{
					i4b_trace_hdr hdr;
					hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
					hdr.dir = FROM_TE;
					hdr.count = ++sc->sc_trace_bcount;
					isdn_layer2_trace_ind(&sc->sc_l2, sc->sc_l3token, &hdr, chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data);
				}

				if(chan->bprot == BPROT_NONE)
				{
					if(!(isdn_bchan_silence(chan->out_mbuf_cur->m_data, chan->out_mbuf_cur->m_len)))
						activity = ACT_TX;
				}
				else
				{
					activity = ACT_TX;
				}
			}
		}

		len = 0;

		while(chan->out_mbuf_cur && len != sc->sc_bfifolen)
		{
			nextlen = min(chan->out_mbuf_cur_len, sc->sc_bfifolen - len);

#ifdef NOTDEF
			printf("i:mh=%x, mc=%x, mcp=%x, mcl=%d l=%d nl=%d # ",
				chan->out_mbuf_head,
				chan->out_mbuf_cur,
				chan->out_mbuf_cur_ptr,
				chan->out_mbuf_cur_len,
				len,
				next_len);
#endif

			isic_hscx_waitxfw(sc, h_chan);	/* necessary !!! */

			HSCX_WRFIFO(h_chan, chan->out_mbuf_cur_ptr, nextlen);
			cmd |= HSCX_CMDR_XTF;

			len += nextlen;
			chan->txcount += nextlen;

			chan->out_mbuf_cur_ptr += nextlen;
			chan->out_mbuf_cur_len -= nextlen;

			if(chan->out_mbuf_cur_len == 0)
			{
				if((chan->out_mbuf_cur = chan->out_mbuf_cur->m_next) != NULL)
				{
					chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data;
					chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len;

					if(sc->sc_trace & TRACE_B_TX)
					{
						i4b_trace_hdr hdr;
						hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
						hdr.dir = FROM_TE;
						hdr.count = ++sc->sc_trace_bcount;
						isdn_layer2_trace_ind(&sc->sc_l2, sc->sc_l3token, &hdr, chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data);
					}
				}
				else
				{
					if (chan->bprot != BPROT_NONE)
						cmd |= HSCX_CMDR_XME;
					i4b_Bfreembuf(chan->out_mbuf_head);
					chan->out_mbuf_head = NULL;
				}

			}
		}
	}

	if(cmd)		/* is there a command for the HSCX ? */
	{
		isic_hscx_cmd(sc, h_chan, cmd);	/* yes, to HSCX */
	}

	/* call timeout handling routine */

	if(activity == ACT_RX || activity == ACT_TX)
		(*chan->l4_driver->bch_activity)(chan->l4_driver_softc, activity);
}
Пример #2
0
/*
 * this is the real interrupt routine
 */
static void
avm_pnp_hscx_intr(int h_chan, int stat, int cnt, struct isic_softc *sc)
{
	register isic_Bchan_t *chan = &sc->sc_chan[h_chan];
	int activity = -1;
	
	DBGL1(L1_H_IRQ, "avm_pnp_hscx_intr", ("%#x\n", stat));

	if((stat & HSCX_INT_XDU) && (chan->bprot != BPROT_NONE))/* xmit data underrun */
	{
		chan->stat_XDU++;			
		DBGL1(L1_H_XFRERR, "avm_pnp_hscx_intr", ("xmit data underrun\n"));
		/* abort the transmission */
		sc->avma1pp_txl = 0;
		sc->avma1pp_cmd |= HSCX_CMD_XRS;
		hscx_write_reg(h_chan, HSCX_STAT, sc, 1);
		sc->avma1pp_cmd &= ~HSCX_CMD_XRS;
		hscx_write_reg(h_chan, HSCX_STAT, sc, 1);

		if (chan->out_mbuf_head != NULL)  /* don't continue to transmit this buffer */
		{
			i4b_Bfreembuf(chan->out_mbuf_head);
			chan->out_mbuf_cur = chan->out_mbuf_head = NULL;
		}
	}

	/*
	 * The following is based on examination of the Linux driver.
	 *
	 * The logic here is different than with a "real" HSCX; all kinds
	 * of information (interrupt/status bits) are in stat.
	 *		HSCX_INT_RPR indicates a receive interrupt
	 *			HSCX_STAT_RDO indicates an overrun condition, abort -
	 *			otherwise read the bytes ((stat & HSCX_STZT_RML_MASK) >> 8)
	 *			HSCX_STAT_RME indicates end-of-frame and apparently any
	 *			CRC/framing errors are only reported in this state.
	 *				if ((stat & HSCX_STAT_CRCVFRRAB) != HSCX_STAT_CRCVFR)
	 *					CRC/framing error
	 */
	
	if(stat & HSCX_INT_RPR)
	{
		register int fifo_data_len;
		int error = 0;
		/* always have to read the FIFO, so use a scratch buffer */
		u_char scrbuf[HSCX_FIFO_LEN];

		if(stat & HSCX_STAT_RDO)
		{
			chan->stat_RDO++;
			DBGL1(L1_H_XFRERR, "avm_pnp_hscx_intr", ("receive data overflow\n"));
			error++;				
		}
	
		fifo_data_len = cnt;
		
		if(fifo_data_len == 0)
			fifo_data_len = sc->sc_bfifolen;

		/* ALWAYS read data from HSCX fifo */
	
		HSCX_RDFIFO(h_chan, scrbuf, fifo_data_len);
		chan->rxcount += fifo_data_len;

		/* all error conditions checked, now decide and take action */
		
		if(error == 0)
		{
			if(chan->in_mbuf == NULL)
			{
				if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL)
					panic("L1 avm_pnp_hscx_intr: RME, cannot allocate mbuf!\n");
				chan->in_cbptr = chan->in_mbuf->m_data;
				chan->in_len = 0;
			}

			if((chan->in_len + fifo_data_len) <= BCH_MAX_DATALEN)
			{
			   	/* OK to copy the data */
				bcopy(scrbuf, chan->in_cbptr, fifo_data_len);
				chan->in_cbptr += fifo_data_len;
				chan->in_len += fifo_data_len;

				/* setup mbuf data length */
					
				chan->in_mbuf->m_len = chan->in_len;
				chan->in_mbuf->m_pkthdr.len = chan->in_len;


				if(sc->sc_trace & TRACE_B_RX)
				{
					i4b_trace_hdr_t hdr;
					hdr.unit = sc->sc_unit;
					hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
					hdr.dir = FROM_NT;
					hdr.count = ++sc->sc_trace_bcount;
					MICROTIME(hdr.time);
					MPH_Trace_Ind(&hdr, chan->in_mbuf->m_len, chan->in_mbuf->m_data);
				}

				if (stat & HSCX_STAT_RME)
				{
				  if((stat & HSCX_STAT_CRCVFRRAB) == HSCX_STAT_CRCVFR)
				  {
					 (*chan->drvr_linktab->bch_rx_data_ready)(chan->drvr_linktab->unit);
					 activity = ACT_RX;
				
					 /* mark buffer ptr as unused */
					
					 chan->in_mbuf = NULL;
					 chan->in_cbptr = NULL;
					 chan->in_len = 0;
				  }
				  else
				  {
						chan->stat_CRC++;
						DBGL1(L1_H_XFRERR, "avm_pnp_hscx_intr", ("CRC/RAB\n"));
					  if (chan->in_mbuf != NULL)
					  {
						  i4b_Bfreembuf(chan->in_mbuf);
						  chan->in_mbuf = NULL;
						  chan->in_cbptr = NULL;
						  chan->in_len = 0;
					  }
				  }
				}
			} /* END enough space in mbuf */
			else
			{
				 if(chan->bprot == BPROT_NONE)
				 {
					  /* setup mbuf data length */
				
					  chan->in_mbuf->m_len = chan->in_len;
					  chan->in_mbuf->m_pkthdr.len = chan->in_len;

					  if(sc->sc_trace & TRACE_B_RX)
					  {
							i4b_trace_hdr_t hdr;
							hdr.unit = sc->sc_unit;
							hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
							hdr.dir = FROM_NT;
							hdr.count = ++sc->sc_trace_bcount;
							MICROTIME(hdr.time);
							MPH_Trace_Ind(&hdr, chan->in_mbuf->m_len, chan->in_mbuf->m_data);
						}

					  /* move rx'd data to rx queue */

					  IF_ENQUEUE(&chan->rx_queue, chan->in_mbuf);
				
					  (*chan->drvr_linktab->bch_rx_data_ready)(chan->drvr_linktab->unit);

					  if(!(isic_hscx_silence(chan->in_mbuf->m_data, chan->in_mbuf->m_len)))
						 activity = ACT_RX;
				
					  /* alloc new buffer */
				
					  if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL)
						 panic("L1 avm_pnp_hscx_intr: RPF, cannot allocate new mbuf!\n");
	
					  /* setup new data ptr */
				
					  chan->in_cbptr = chan->in_mbuf->m_data;
	
					  /* OK to copy the data */
					  bcopy(scrbuf, chan->in_cbptr, fifo_data_len);

					  chan->in_cbptr += fifo_data_len;
					  chan->in_len = fifo_data_len;

					  chan->rxcount += fifo_data_len;
					}
				 else
					{
					  DBGL1(L1_H_XFRERR, "avm_pnp_hscx_intr", ("RAWHDLC rx buffer overflow in RPF, in_len=%d\n", chan->in_len));
					  chan->in_cbptr = chan->in_mbuf->m_data;
					  chan->in_len = 0;
					}
			  }
		} /* if(error == 0) */
		else
		{
		  	/* land here for RDO */
			if (chan->in_mbuf != NULL)
			{
				i4b_Bfreembuf(chan->in_mbuf);
				chan->in_mbuf = NULL;
				chan->in_cbptr = NULL;
				chan->in_len = 0;
			}
			sc->avma1pp_txl = 0;
			sc->avma1pp_cmd |= HSCX_CMD_RRS;
			hscx_write_reg(h_chan, HSCX_STAT, sc, 1);
			sc->avma1pp_cmd &= ~HSCX_CMD_RRS;
			hscx_write_reg(h_chan, HSCX_STAT, sc, 1);
		}
	}


	/* transmit fifo empty, new data can be written to fifo */
	
	if(stat & HSCX_INT_XPR)
	{
		/*
		 * for a description what is going on here, please have
		 * a look at isic_bchannel_start() in i4b_bchan.c !
		 */

		DBGL1(L1_H_IRQ, "avm_pnp_hscx_intr", ("unit %d, chan %d - XPR, Tx Fifo Empty!\n", sc->sc_unit, h_chan));

		if(chan->out_mbuf_cur == NULL || chan->out_mbuf_head == NULL) 	/* last frame is transmitted */
		{
			IF_DEQUEUE(&chan->tx_queue, chan->out_mbuf_head);

			if(chan->out_mbuf_head == NULL)
			{
				chan->state &= ~HSCX_TX_ACTIVE;
				(*chan->drvr_linktab->bch_tx_queue_empty)(chan->drvr_linktab->unit);
			}
			else
			{
				chan->state |= HSCX_TX_ACTIVE;
				chan->out_mbuf_cur = chan->out_mbuf_head;
				chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data;
				chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len;

				if(sc->sc_trace & TRACE_B_TX)
				{
					i4b_trace_hdr_t hdr;
					hdr.unit = sc->sc_unit;
					hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
					hdr.dir = FROM_TE;
					hdr.count = ++sc->sc_trace_bcount;
					MICROTIME(hdr.time);
					MPH_Trace_Ind(&hdr, chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data);
				}
				if(chan->bprot == BPROT_NONE)
				{
					if(!(isic_hscx_silence(chan->out_mbuf_cur->m_data, chan->out_mbuf_cur->m_len)))
						activity = ACT_TX;
				}
				else
				{
					activity = ACT_TX;
				}
			}
		}
			
		isic_hscx_fifo(chan, sc);
	}

	/* call timeout handling routine */
	
	if(activity == ACT_RX || activity == ACT_TX)
		(*chan->drvr_linktab->bch_activity)(chan->drvr_linktab->unit, activity);
}