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