1 /* ISDN4BSD code */
2 /*
3  * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  *---------------------------------------------------------------------------
27  *
28  *	i4b_bchan.c - B channel handling L1 procedures
29  *
30  *---------------------------------------------------------------------------*/
31 
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$MirOS: src/sys/dev/ic/isic_bchan.c,v 1.1.7.1 2005/03/06 16:33:43 tg Exp $");
34 
35 #include <sys/param.h>
36 #if defined(__FreeBSD__) && __FreeBSD__ >= 3
37 #include <sys/ioccom.h>
38 #else
39 #include <sys/ioctl.h>
40 #endif
41 #include <sys/kernel.h>
42 #include <sys/systm.h>
43 #include <sys/mbuf.h>
44 #include <sys/stdarg.h>
45 
46 #ifdef __FreeBSD__
47 #include <machine/clock.h>
48 #include <i386/isa/isa_device.h>
49 #else
50 #ifndef __bsdi__
51 #include <machine/bus.h>
52 #endif
53 #include <sys/device.h>
54 #endif
55 
56 #include <sys/socket.h>
57 #include <net/if.h>
58 
59 #include <sys/timeout.h>
60 
61 #include <netisdn/i4b_debug.h>
62 #include <netisdn/i4b_ioctl.h>
63 #include <netisdn/i4b_trace.h>
64 
65 #include <netisdn/i4b_l2.h>
66 #include <netisdn/i4b_l1l2.h>
67 #include <netisdn/i4b_mbuf.h>
68 #include <netisdn/i4b_global.h>
69 
70 #include <dev/ic/isic_l1.h>
71 #include <dev/ic/isac.h>
72 #include <dev/ic/hscx.h>
73 
74 static void isic_bchannel_start(isdn_layer1token, int h_chan);
75 static void isic_bchannel_stat(isdn_layer1token, int h_chan, bchan_statistics_t *bsp);
76 
77 void isic_set_link(void*, int channel, const struct isdn_l4_driver_functions *l4_driver, void *l4_driver_softc);
78 isdn_link_t *isic_ret_linktab(void*, int channel);
79 
80 /*---------------------------------------------------------------------------*
81  *	initialize one B channels rx/tx data structures and init/deinit HSCX
82  *---------------------------------------------------------------------------*/
83 void
isic_bchannel_setup(isdn_layer1token t,int h_chan,int bprot,int activate)84 isic_bchannel_setup(isdn_layer1token t, int h_chan, int bprot, int activate)
85 {
86 	struct isic_softc *sc = (struct isic_softc*)t;
87 	l1_bchan_state_t *chan = &sc->sc_chan[h_chan];
88 
89 	int s = splnet();
90 
91 	if(activate == 0)
92 	{
93 		/* deactivation */
94 		isic_hscx_init(sc, h_chan, activate);
95 	}
96 
97 	NDBGL1(L1_BCHAN, "%s, channel=%d, %s",
98 		sc->sc_dev.dv_xname, h_chan, activate ? "activate" : "deactivate");
99 
100 	/* general part */
101 
102 	chan->channel = h_chan;		/* B channel */
103 	chan->bprot = bprot;		/* B channel protocol */
104 	chan->state = HSCX_IDLE;	/* B channel state */
105 
106 	/* receiver part */
107 
108 	i4b_Bcleanifq(&chan->rx_queue);	/* clean rx queue */
109 
110 	chan->rx_queue.ifq_maxlen = IFQ_MAXLEN;
111 
112 	chan->rxcount = 0;		/* reset rx counter */
113 
114 	i4b_Bfreembuf(chan->in_mbuf);	/* clean rx mbuf */
115 
116 	chan->in_mbuf = NULL;		/* reset mbuf ptr */
117 	chan->in_cbptr = NULL;		/* reset mbuf curr ptr */
118 	chan->in_len = 0;		/* reset mbuf data len */
119 
120 	/* transmitter part */
121 
122 	i4b_Bcleanifq(&chan->tx_queue);	/* clean tx queue */
123 
124 	chan->tx_queue.ifq_maxlen = IFQ_MAXLEN;
125 
126 	chan->txcount = 0;		/* reset tx counter */
127 
128 	i4b_Bfreembuf(chan->out_mbuf_head);	/* clean tx mbuf */
129 
130 	chan->out_mbuf_head = NULL;	/* reset head mbuf ptr */
131 	chan->out_mbuf_cur = NULL;	/* reset current mbuf ptr */
132 	chan->out_mbuf_cur_ptr = NULL;	/* reset current mbuf data ptr */
133 	chan->out_mbuf_cur_len = 0;	/* reset current mbuf data cnt */
134 
135 	if(activate != 0)
136 	{
137 		/* activation */
138 		isic_hscx_init(sc, h_chan, activate);
139 	}
140 
141 	splx(s);
142 }
143 
144 /*---------------------------------------------------------------------------*
145  *	start transmission on a b channel
146  *---------------------------------------------------------------------------*/
147 static void
isic_bchannel_start(isdn_layer1token t,int h_chan)148 isic_bchannel_start(isdn_layer1token t, int h_chan)
149 {
150 	struct isic_softc *sc = (struct isic_softc*)t;
151 
152 	register l1_bchan_state_t *chan = &sc->sc_chan[h_chan];
153 	register int next_len;
154 	register int len;
155 
156 	int s;
157 	int activity = -1;
158 	int cmd = 0;
159 
160 	s = splnet();				/* enter critical section */
161 	if(chan->state & HSCX_TX_ACTIVE)	/* already running ? */
162 	{
163 		splx(s);
164 		return;				/* yes, leave */
165 	}
166 
167 	/* get next mbuf from queue */
168 
169 	IF_DEQUEUE(&chan->tx_queue, chan->out_mbuf_head);
170 
171 	if(chan->out_mbuf_head == NULL)		/* queue empty ? */
172 	{
173 		splx(s);			/* leave critical section */
174 		return;				/* yes, exit */
175 	}
176 
177 	/* init current mbuf values */
178 
179 	chan->out_mbuf_cur = chan->out_mbuf_head;
180 	chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len;
181 	chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data;
182 
183 	/* activity indicator for timeout handling */
184 
185 	if(chan->bprot == BPROT_NONE)
186 	{
187 		if(!(isdn_bchan_silence(chan->out_mbuf_cur->m_data,
188 		    chan->out_mbuf_cur->m_len)))
189 			activity = ACT_TX;
190 	}
191 	else
192 	{
193 		activity = ACT_TX;
194 	}
195 
196 	chan->state |= HSCX_TX_ACTIVE;	/* we start transmitting */
197 
198 	if(sc->sc_trace & TRACE_B_TX)	/* if trace, send mbuf to trace dev */
199 	{
200 		i4b_trace_hdr hdr;
201 		hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2);
202 		hdr.dir = FROM_TE;
203 		hdr.count = ++sc->sc_trace_bcount;
204 		isdn_layer2_trace_ind(&sc->sc_l2, sc->sc_l3token, &hdr,
205 			chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data);
206 	}
207 
208 	len = 0;	/* # of chars put into HSCX tx fifo this time */
209 
210 	/*
211 	 * fill the HSCX tx fifo with data from the current mbuf. if
212 	 * current mbuf holds less data than HSCX fifo length, try to
213 	 * get the next mbuf from (a possible) mbuf chain. if there is
214 	 * not enough data in a single mbuf or in a chain, then this
215 	 * is the last mbuf and we tell the HSCX that it has to send
216 	 * CRC and closing flag
217 	 */
218 
219 	while((len < sc->sc_bfifolen) && chan->out_mbuf_cur)
220 	{
221 		/*
222 		 * put as much data into the HSCX fifo as is
223 		 * available from the current mbuf
224 		 */
225 
226 		if((len + chan->out_mbuf_cur_len) >= sc->sc_bfifolen)
227 			next_len = sc->sc_bfifolen - len;
228 		else
229 			next_len = chan->out_mbuf_cur_len;
230 
231 #ifdef NOTDEF
232 		printf("b:mh=%x, mc=%x, mcp=%x, mcl=%d l=%d nl=%d # ",
233 			chan->out_mbuf_head,
234 			chan->out_mbuf_cur,
235 			chan->out_mbuf_cur_ptr,
236 			chan->out_mbuf_cur_len,
237 			len,
238 			next_len);
239 #endif
240 
241 		/* wait for tx fifo write enabled */
242 
243 		isic_hscx_waitxfw(sc, h_chan);
244 
245 		/* write what we have from current mbuf to HSCX fifo */
246 
247 		HSCX_WRFIFO(h_chan, chan->out_mbuf_cur_ptr, next_len);
248 
249 		len += next_len;		/* update # of bytes written */
250 		chan->txcount += next_len;	/* statistics */
251 		chan->out_mbuf_cur_ptr += next_len;	/* data ptr */
252 		chan->out_mbuf_cur_len -= next_len;	/* data len */
253 
254 		/*
255 		 * in case the current mbuf (of a possible chain) data
256 		 * has been put into the fifo, check if there is a next
257 		 * mbuf in the chain. If there is one, get ptr to it
258 		 * and update the data ptr and the length
259 		 */
260 
261 		if((chan->out_mbuf_cur_len <= 0)	&&
262 		  ((chan->out_mbuf_cur = chan->out_mbuf_cur->m_next) != NULL))
263 		{
264 			chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data;
265 			chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len;
266 
267 			if(sc->sc_trace & TRACE_B_TX)
268 			{
269 				i4b_trace_hdr hdr;
270 				hdr.type = (h_chan == HSCX_CH_A ?
271 					TRC_CH_B1 : TRC_CH_B2);
272 				hdr.dir = FROM_TE;
273 				hdr.count = ++sc->sc_trace_bcount;
274 				isdn_layer2_trace_ind(&sc->sc_l2, sc->sc_l3token,
275 					&hdr,
276 					chan->out_mbuf_cur->m_len,
277 					chan->out_mbuf_cur->m_data);
278 			}
279 		}
280 	}
281 
282 	/*
283 	 * if there is either still data in the current mbuf and/or
284 	 * there is a successor on the chain available issue just
285 	 * a XTF (transmit) command to HSCX. if ther is no more
286 	 * data available from the current mbuf (-chain), issue
287 	 * an XTF and an XME (message end) command which will then
288 	 * send the CRC and the closing HDLC flag sequence
289 	 */
290 
291 	if(chan->out_mbuf_cur && (chan->out_mbuf_cur_len > 0))
292 	{
293 		/*
294 		 * more data available, send current fifo out.
295 		 * next xfer to HSCX tx fifo is done in the
296 		 * HSCX interrupt routine.
297 		 */
298 
299 		cmd |= HSCX_CMDR_XTF;
300 	}
301 	else
302 	{
303 		/* end of mbuf chain */
304 
305 		if(chan->bprot == BPROT_NONE)
306 			cmd |= HSCX_CMDR_XTF;
307 		else
308 			cmd |= HSCX_CMDR_XTF | HSCX_CMDR_XME;
309 
310 		i4b_Bfreembuf(chan->out_mbuf_head);	/* free mbuf chain */
311 
312 		chan->out_mbuf_head = NULL;
313 		chan->out_mbuf_cur = NULL;
314 		chan->out_mbuf_cur_ptr = NULL;
315 		chan->out_mbuf_cur_len = 0;
316 	}
317 
318 	/* call timeout handling routine */
319 
320 	if(activity == ACT_RX || activity == ACT_TX)
321 		(*chan->l4_driver->bch_activity)(
322 		    chan->l4_driver_softc, activity);
323 
324 	if(cmd)
325 		isic_hscx_cmd(sc, h_chan, cmd);
326 
327 	splx(s);
328 }
329 
330 /*---------------------------------------------------------------------------*
331  *	fill statistics struct
332  *---------------------------------------------------------------------------*/
333 static void
isic_bchannel_stat(isdn_layer1token t,int h_chan,bchan_statistics_t * bsp)334 isic_bchannel_stat(isdn_layer1token t, int h_chan, bchan_statistics_t *bsp)
335 {
336 	struct isic_softc *sc = (struct isic_softc*)t;
337 	l1_bchan_state_t *chan = &sc->sc_chan[h_chan];
338 	int s;
339 
340 	s = splnet();
341 
342 	bsp->outbytes = chan->txcount;
343 	bsp->inbytes = chan->rxcount;
344 
345 	chan->txcount = 0;
346 	chan->rxcount = 0;
347 
348 	splx(s);
349 }
350 
351 /*---------------------------------------------------------------------------*
352  *	return the address of isic drivers linktab
353  *---------------------------------------------------------------------------*/
354 isdn_link_t *
isic_ret_linktab(void * token,int channel)355 isic_ret_linktab(void *token, int channel)
356 {
357 	struct l2_softc *l2sc = token;
358 	struct isic_softc *sc = l2sc->l1_token;
359 	l1_bchan_state_t *chan = &sc->sc_chan[channel];
360 
361 	return(&chan->isdn_linktab);
362 }
363 
364 /*---------------------------------------------------------------------------*
365  *	set the driver linktab in the b channel softc
366  *---------------------------------------------------------------------------*/
367 void
isic_set_link(void * token,int channel,const struct isdn_l4_driver_functions * l4_driver,void * l4_driver_softc)368 isic_set_link(void *token, int channel, const struct isdn_l4_driver_functions *l4_driver, void *l4_driver_softc)
369 {
370 	struct l2_softc *l2sc = token;
371 	struct isic_softc *sc = l2sc->l1_token;
372 	l1_bchan_state_t *chan = &sc->sc_chan[channel];
373 
374 	chan->l4_driver = l4_driver;
375 	chan->l4_driver_softc = l4_driver_softc;
376 }
377 
378 static const struct isdn_l4_bchannel_functions
379 isic_l4_bchannel_functions = {
380 	isic_bchannel_setup,
381 	isic_bchannel_start,
382 	isic_bchannel_stat
383 };
384 
385 /*---------------------------------------------------------------------------*
386  *	initialize our local linktab
387  *---------------------------------------------------------------------------*/
388 void
isic_init_linktab(struct isic_softc * sc)389 isic_init_linktab(struct isic_softc *sc)
390 {
391 	l1_bchan_state_t *chan = &sc->sc_chan[HSCX_CH_A];
392 	isdn_link_t *lt = &chan->isdn_linktab;
393 
394 	/* local setup */
395 	lt->l1token = sc;
396 	lt->channel = HSCX_CH_A;
397 	lt->bchannel_driver = &isic_l4_bchannel_functions;
398 	lt->tx_queue = &chan->tx_queue;
399 
400 	/* used by non-HDLC data transfers, i.e. telephony drivers */
401 	lt->rx_queue = &chan->rx_queue;
402 
403 	/* used by HDLC data transfers, i.e. ipr and isp drivers */
404 	lt->rx_mbuf = &chan->in_mbuf;
405 
406 	chan = &sc->sc_chan[HSCX_CH_B];
407 	lt = &chan->isdn_linktab;
408 
409 	lt->l1token = sc;
410 	lt->channel = HSCX_CH_B;
411 	lt->bchannel_driver = &isic_l4_bchannel_functions;
412 	lt->tx_queue = &chan->tx_queue;
413 
414 	/* used by non-HDLC data transfers, i.e. telephony drivers */
415 	lt->rx_queue = &chan->rx_queue;
416 
417 	/* used by HDLC data transfers, i.e. ipr and isp drivers */
418 	lt->rx_mbuf = &chan->in_mbuf;
419 }
420