Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / net / i4b / layer1 / iwic / i4b_iwic_bchan.c
1 /*
2  * Copyright (c) 1999, 2000 Dave Boyce. All rights reserved.
3  *
4  * Copyright (c) 2000, 2001 Hellmuth Michaelis. All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  *---------------------------------------------------------------------------
28  *
29  *      i4b_iwic - isdn4bsd Winbond W6692 driver
30  *      ----------------------------------------
31  *
32  * $FreeBSD: src/sys/i4b/layer1/iwic/i4b_iwic_bchan.c,v 1.7.2.1 2001/08/10 14:08:40 obrien Exp $
33  *
34  *      last edit-date: [Tue Jan 16 13:21:24 2001]
35  *
36  *---------------------------------------------------------------------------*/
37
38 #include "iwic.h"
39 #include "opt_i4b.h"
40 #include "pci.h"
41
42 #if (NIWIC > 0) && (NPCI > 0)
43
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/mbuf.h>
47 #include <sys/socket.h>
48
49 #include <net/if.h>
50
51
52 #include <machine/i4b_debug.h>
53 #include <machine/i4b_ioctl.h>
54 #include <machine/i4b_trace.h>
55
56 #include <i4b/layer1/i4b_l1.h>
57
58 #include <i4b/layer1/iwic/i4b_iwic.h>
59 #include <i4b/layer1/iwic/i4b_w6692.h>
60
61 #include <i4b/include/i4b_global.h>
62 #include <i4b/include/i4b_mbuf.h>
63
64 static void iwic_bchan_init(struct iwic_softc *sc, int chan_no, int activate);
65
66 /*---------------------------------------------------------------------------*
67  *      B-channel interrupt handler
68  *---------------------------------------------------------------------------*/
69 void
70 iwic_bchan_xirq(struct iwic_softc *sc, int chan_no)
71 {
72         int irq_stat;
73         struct iwic_bchan *chan;
74         int cmd = 0;
75         int activity = 0;
76
77         chan = &sc->sc_bchan[chan_no];
78
79         irq_stat = IWIC_READ(sc, chan->offset + B_EXIR);
80
81         NDBGL1(L1_H_IRQ, "irq_stat = 0x%x", irq_stat);
82         
83         if((irq_stat & (B_EXIR_RMR | B_EXIR_RME | B_EXIR_RDOV | B_EXIR_XFR | B_EXIR_XDUN)) == 0)
84         {
85                 NDBGL1(L1_H_XFRERR, "spurious IRQ!");
86                 return;
87         }
88
89         if (irq_stat & B_EXIR_RDOV)
90         {
91                 NDBGL1(L1_H_XFRERR, "iwic%d: EXIR B-channel Receive Data Overflow", sc->sc_unit);
92         }
93
94         if (irq_stat & B_EXIR_XDUN)
95         {
96                 NDBGL1(L1_H_XFRERR, "iwic%d: EXIR B-channel Transmit Data Underrun", sc->sc_unit);
97                 cmd |= (B_CMDR_XRST);   /*XXX must retransmit frame ! */
98         }
99
100 /* RX message end interrupt */
101         
102         if(irq_stat & B_EXIR_RME)
103         {
104                 int error;
105
106                 NDBGL1(L1_H_IRQ, "B_EXIR_RME");
107
108                 error = (IWIC_READ(sc,chan->offset+B_STAR) &
109                          (B_STAR_RDOV | B_STAR_CRCE | B_STAR_RMB));
110
111                 if(error)
112                 {
113                         if(error & B_STAR_RDOV)
114                                 NDBGL1(L1_H_XFRERR, "iwic%d: B-channel Receive Data Overflow", sc->sc_unit);
115                         if(error & B_STAR_CRCE)
116                                 NDBGL1(L1_H_XFRERR, "iwic%d: B-channel CRC Error", sc->sc_unit);
117                         if(error & B_STAR_RMB)
118                                 NDBGL1(L1_H_XFRERR, "iwic%d: B-channel Receive Message Aborted", sc->sc_unit);
119                 }
120
121                 /* all error conditions checked, now decide and take action */
122                 
123                 if(error == 0)
124                 {
125                         register int fifo_data_len;
126                         fifo_data_len = ((IWIC_READ(sc,chan->offset+B_RBCL)) &
127                                         ((IWIC_BCHAN_FIFO_LEN)-1));
128                 
129                         if(fifo_data_len == 0)
130                                 fifo_data_len = IWIC_BCHAN_FIFO_LEN;
131
132
133                         if(chan->in_mbuf == NULL)
134                         {
135                                 if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL)
136                                         panic("L1 iwic_bchan_irq: RME, cannot allocate mbuf!\n");
137                                 chan->in_cbptr = chan->in_mbuf->m_data;
138                                 chan->in_len = 0;
139                         }
140
141                         if((chan->in_len + fifo_data_len) <= BCH_MAX_DATALEN)
142                         {
143                                 /* read data from fifo */
144         
145                                 NDBGL1(L1_H_IRQ, "B_EXIR_RME, rd fifo, len = %d", fifo_data_len);
146
147                                 IWIC_RDBFIFO(sc, chan, chan->in_cbptr, fifo_data_len);
148
149                                 cmd |= (B_CMDR_RACK | B_CMDR_RACT);
150                                 IWIC_WRITE(sc, chan->offset + B_CMDR, cmd);
151                                 cmd = 0;
152                                 
153                                 chan->in_len += fifo_data_len;
154                                 chan->rxcount += fifo_data_len;
155
156                                 /* setup mbuf data length */
157                                         
158                                 chan->in_mbuf->m_len = chan->in_len;
159                                 chan->in_mbuf->m_pkthdr.len = chan->in_len;
160
161                                 if(sc->sc_trace & TRACE_B_RX)
162                                 {
163                                         i4b_trace_hdr_t hdr;
164                                         hdr.unit = L0IWICUNIT(sc->sc_unit);
165                                         hdr.type = (chan_no == IWIC_BCH_A ? TRC_CH_B1 : TRC_CH_B2);
166                                         hdr.dir = FROM_NT;
167                                         hdr.count = ++sc->sc_bchan[chan_no].sc_trace_bcount;
168                                         MICROTIME(hdr.time);
169                                         i4b_l1_trace_ind(&hdr, chan->in_mbuf->m_len, chan->in_mbuf->m_data);
170                                 }
171
172                                 (*chan->iwic_drvr_linktab->bch_rx_data_ready)(chan->iwic_drvr_linktab->unit);
173
174                                 activity = ACT_RX;
175                                 
176                                 /* mark buffer ptr as unused */
177                                         
178                                 chan->in_mbuf = NULL;
179                                 chan->in_cbptr = NULL;
180                                 chan->in_len = 0;
181                         }
182                         else
183                         {
184                                 NDBGL1(L1_H_XFRERR, "RAWHDLC rx buffer overflow in RME, in_len=%d, fifolen=%d", chan->in_len, fifo_data_len);
185                                 chan->in_cbptr = chan->in_mbuf->m_data;
186                                 chan->in_len = 0;
187                                 cmd |= (B_CMDR_RRST | B_CMDR_RACK);
188                         }
189                 }
190                 else
191                 {
192                         if (chan->in_mbuf != NULL)
193                         {
194                                 i4b_Bfreembuf(chan->in_mbuf);
195                                 chan->in_mbuf = NULL;
196                                 chan->in_cbptr = NULL;
197                                 chan->in_len = 0;
198                         }
199                         cmd |= (B_CMDR_RRST | B_CMDR_RACK);
200                 }
201         }
202
203 /* RX fifo full interrupt */
204
205         if(irq_stat & B_EXIR_RMR)
206         {
207                 NDBGL1(L1_H_IRQ, "B_EXIR_RMR");
208
209                 if(chan->in_mbuf == NULL)
210                 {
211                         if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL)
212                                 panic("L1 iwic_bchan_irq: RMR, cannot allocate mbuf!\n");
213                         chan->in_cbptr = chan->in_mbuf->m_data;
214                         chan->in_len = 0;
215                 }
216
217                 chan->rxcount += IWIC_BCHAN_FIFO_LEN;
218                 
219                 if((chan->in_len + IWIC_BCHAN_FIFO_LEN) <= BCH_MAX_DATALEN)
220                 {
221                         /* read data from fifo */
222
223                         NDBGL1(L1_H_IRQ, "B_EXIR_RMR, rd fifo, len = max (64)");
224                         
225                         IWIC_RDBFIFO(sc, chan, chan->in_cbptr, IWIC_BCHAN_FIFO_LEN);
226
227                         chan->in_cbptr += IWIC_BCHAN_FIFO_LEN;
228                         chan->in_len += IWIC_BCHAN_FIFO_LEN;
229                 }
230                 else
231                 {
232                         if(chan->bprot == BPROT_NONE)
233                         {
234                                 /* setup mbuf data length */
235                                 
236                                 chan->in_mbuf->m_len = chan->in_len;
237                                 chan->in_mbuf->m_pkthdr.len = chan->in_len;
238
239                                 if(sc->sc_trace & TRACE_B_RX)
240                                 {
241                                         i4b_trace_hdr_t hdr;
242                                         hdr.unit = L0IWICUNIT(sc->sc_unit);
243                                         hdr.type = (chan_no == IWIC_BCH_A ? TRC_CH_B1 : TRC_CH_B2);
244                                         hdr.dir = FROM_NT;
245                                         hdr.count = ++sc->sc_bchan[chan_no].sc_trace_bcount;
246                                         MICROTIME(hdr.time);
247                                         i4b_l1_trace_ind(&hdr, chan->in_mbuf->m_len, chan->in_mbuf->m_data);
248                                 }
249
250                                 /* silence detection */
251                                 
252                                 if(!(i4b_l1_bchan_tel_silence(chan->in_mbuf->m_data, chan->in_mbuf->m_len)))
253                                         activity = ACT_RX;
254
255 #if defined (__FreeBSD__) && __FreeBSD__ > 4
256                                 (void) IF_HANDOFF(&chan->rx_queue, chan->in_mbuf, NULL);
257 #else
258                                 if(!(IF_QFULL(&chan->rx_queue)))
259                                 {
260                                         IF_ENQUEUE(&chan->rx_queue, chan->in_mbuf);
261                                 }
262                                 else
263                                 {
264                                         i4b_Bfreembuf(chan->in_mbuf);
265                                 }
266 #endif
267                                 /* signal upper driver that data is available */
268
269                                 (*chan->iwic_drvr_linktab->bch_rx_data_ready)(chan->iwic_drvr_linktab->unit);
270                                 
271                                 /* alloc new buffer */
272                                 
273                                 if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL)
274                                         panic("L1 iwic_bchan_irq: RMR, cannot allocate new mbuf!\n");
275         
276                                 /* setup new data ptr */
277                                 
278                                 chan->in_cbptr = chan->in_mbuf->m_data;
279         
280                                 /* read data from fifo */
281         
282                                 NDBGL1(L1_H_IRQ, "B_EXIR_RMR, rd fifo1, len = max (64)");
283                                 
284                                 IWIC_RDBFIFO(sc, chan, chan->in_cbptr, IWIC_BCHAN_FIFO_LEN);
285
286                                 chan->in_cbptr += IWIC_BCHAN_FIFO_LEN;
287                                 chan->in_len = IWIC_BCHAN_FIFO_LEN;
288
289                                 chan->rxcount += IWIC_BCHAN_FIFO_LEN;
290                         }
291                         else
292                         {
293                                 NDBGL1(L1_H_XFRERR, "RAWHDLC rx buffer overflow in RPF, in_len=%d", chan->in_len);
294                                 chan->in_cbptr = chan->in_mbuf->m_data;
295                                 chan->in_len = 0;
296                                 cmd |= (B_CMDR_RRST | B_CMDR_RACK);
297                         }
298                 }
299                 
300                 /* command to release fifo space */
301                 
302                 cmd |= B_CMDR_RACK;
303         }
304
305 /* TX interrupt */
306         
307         if (irq_stat & B_EXIR_XFR)
308         {                       
309                 /* transmit fifo empty, new data can be written to fifo */
310
311                 int activity = -1;
312                 int len;
313                 int nextlen;
314
315                 NDBGL1(L1_H_IRQ, "B_EXIR_XFR");
316                 
317                 if(chan->out_mbuf_cur == NULL)  /* last frame is transmitted */
318                 {
319                         IF_DEQUEUE(&chan->tx_queue, chan->out_mbuf_head);
320
321                         if(chan->out_mbuf_head == NULL)
322                         {
323                                 chan->state &= ~ST_TX_ACTIVE;
324                                 (*chan->iwic_drvr_linktab->bch_tx_queue_empty)(chan->iwic_drvr_linktab->unit);
325                         }
326                         else
327                         {
328                                 chan->state |= ST_TX_ACTIVE;
329                                 chan->out_mbuf_cur = chan->out_mbuf_head;
330                                 chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data;
331                                 chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len;
332
333                                 if(sc->sc_trace & TRACE_B_TX)
334                                 {
335                                         i4b_trace_hdr_t hdr;
336                                         hdr.unit = L0IWICUNIT(sc->sc_unit);
337                                         hdr.type = (chan_no == IWIC_BCH_A ? TRC_CH_B1 : TRC_CH_B2);
338                                         hdr.dir = FROM_TE;
339                                         hdr.count = ++sc->sc_bchan[chan_no].sc_trace_bcount;
340                                         MICROTIME(hdr.time);
341                                         i4b_l1_trace_ind(&hdr, chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data);
342                                 }
343
344                                 if(chan->bprot == BPROT_NONE)
345                                 {
346                                         if(!(i4b_l1_bchan_tel_silence(chan->out_mbuf_cur->m_data, chan->out_mbuf_cur->m_len)))
347                                                 activity = ACT_TX;
348                                 }
349                                 else
350                                 {
351                                         activity = ACT_TX;
352                                 }
353                         }
354                 }
355                         
356                 len = 0;
357
358                 while(chan->out_mbuf_cur && len != IWIC_BCHAN_FIFO_LEN)
359                 {
360                         nextlen = min(chan->out_mbuf_cur_len, IWIC_BCHAN_FIFO_LEN - len);
361
362                         NDBGL1(L1_H_IRQ, "B_EXIR_XFR, wr fifo, len = %d", nextlen);
363                         
364                         IWIC_WRBFIFO(sc, chan, chan->out_mbuf_cur_ptr, nextlen);
365
366                         cmd |= B_CMDR_XMS;
367         
368                         len += nextlen;
369                         chan->txcount += nextlen;
370         
371                         chan->out_mbuf_cur_ptr += nextlen;
372                         chan->out_mbuf_cur_len -= nextlen;
373                         
374                         if(chan->out_mbuf_cur_len == 0) 
375                         {
376                                 if((chan->out_mbuf_cur = chan->out_mbuf_cur->m_next) != NULL)
377                                 {
378                                         chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data;
379                                         chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len;
380
381                                         if(sc->sc_trace & TRACE_B_TX)
382                                         {
383                                                 i4b_trace_hdr_t hdr;
384                                                 hdr.unit = L0IWICUNIT(sc->sc_unit);
385                                                 hdr.type = (chan_no == IWIC_BCH_A ? TRC_CH_B1 : TRC_CH_B2);
386                                                 hdr.dir = FROM_TE;
387                                                 hdr.count = ++sc->sc_bchan[chan_no].sc_trace_bcount;
388                                                 MICROTIME(hdr.time);
389                                                 i4b_l1_trace_ind(&hdr, chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data);
390                                         }
391                                 }
392                                 else
393                                 {
394                                         if (chan->bprot != BPROT_NONE)
395                                                 cmd |= B_CMDR_XME;
396                                         i4b_Bfreembuf(chan->out_mbuf_head);
397                                         chan->out_mbuf_head = NULL;
398                                 }
399                         }
400                 }
401         }
402         if(cmd)
403         {
404                 cmd |= B_CMDR_RACT;
405                 IWIC_WRITE(sc, chan->offset + B_CMDR, cmd);
406         }
407 }
408
409 /*---------------------------------------------------------------------------*
410  *      initialize one B channels rx/tx data structures
411  *---------------------------------------------------------------------------*/
412 void
413 iwic_bchannel_setup(int unit, int chan_no, int bprot, int activate)
414 {
415         struct iwic_softc *sc = &iwic_sc[unit];
416         struct iwic_bchan *chan = &sc->sc_bchan[chan_no];
417
418         int s = SPLI4B();
419         
420         NDBGL1(L1_BCHAN, "unit %d, chan %d, bprot %d, activate %d", unit, chan_no, bprot, activate);
421
422         /* general part */
423
424         chan->bprot = bprot;            /* B channel protocol */
425         chan->state = ST_IDLE;          /* B channel state */
426
427         if(activate == 0)
428         {
429                 /* deactivation */
430                 iwic_bchan_init(sc, chan_no, activate);
431         }
432                 
433         /* receiver part */
434
435         chan->rx_queue.ifq_maxlen = IFQ_MAXLEN;
436
437 #if defined (__FreeBSD__) && __FreeBSD__ > 4
438         mtx_init(&chan->rx_queue.ifq_mtx, "i4b_iwic_rx", MTX_DEF);
439 #endif
440
441         i4b_Bcleanifq(&chan->rx_queue); /* clean rx queue */
442
443         chan->rxcount = 0;              /* reset rx counter */
444         
445         i4b_Bfreembuf(chan->in_mbuf);   /* clean rx mbuf */
446
447         chan->in_mbuf = NULL;           /* reset mbuf ptr */
448         chan->in_cbptr = NULL;          /* reset mbuf curr ptr */
449         chan->in_len = 0;               /* reset mbuf data len */
450         
451         /* transmitter part */
452
453         chan->tx_queue.ifq_maxlen = IFQ_MAXLEN;
454
455 #if defined (__FreeBSD__) && __FreeBSD__ > 4    
456         mtx_init(&chan->tx_queue.ifq_mtx, "i4b_iwic_tx", MTX_DEF);
457 #endif
458
459         i4b_Bcleanifq(&chan->tx_queue); /* clean tx queue */
460         
461         chan->txcount = 0;              /* reset tx counter */
462         
463         i4b_Bfreembuf(chan->out_mbuf_head);     /* clean tx mbuf */
464
465         chan->out_mbuf_head = NULL;     /* reset head mbuf ptr */
466         chan->out_mbuf_cur = NULL;      /* reset current mbuf ptr */    
467         chan->out_mbuf_cur_ptr = NULL;  /* reset current mbuf data ptr */
468         chan->out_mbuf_cur_len = 0;     /* reset current mbuf data cnt */
469         
470         if(activate != 0)
471         {
472                 /* activation */
473                 iwic_bchan_init(sc, chan_no, activate);
474         }
475
476         splx(s);
477 }
478
479 /*---------------------------------------------------------------------------*
480  *      initalize / deinitialize B-channel hardware
481  *---------------------------------------------------------------------------*/
482 static void
483 iwic_bchan_init(struct iwic_softc *sc, int chan_no, int activate)
484 {
485         struct iwic_bchan *bchan = &sc->sc_bchan[chan_no];
486
487         NDBGL1(L1_BCHAN, "chan %d, activate %d", chan_no, activate);
488
489         if(activate)
490         {
491                 if(bchan->bprot == BPROT_NONE)
492                 {
493                         /* Extended transparent mode */
494                         IWIC_WRITE(sc, bchan->offset + B_MODE, B_MODE_MMS);
495                 }
496                 else
497                 {
498                         /* Transparent mode */
499                         IWIC_WRITE(sc, bchan->offset + B_MODE, 0);
500                         /* disable address comparation */
501                         IWIC_WRITE (sc, bchan->offset+B_ADM1, 0xff);
502                         IWIC_WRITE (sc, bchan->offset+B_ADM2, 0xff);
503                 }
504
505                 /* reset & start receiver */
506                 IWIC_WRITE(sc, bchan->offset + B_CMDR, B_CMDR_RRST|B_CMDR_RACT);
507
508                 /* clear irq mask */
509                 IWIC_WRITE(sc, bchan->offset + B_EXIM, 0);
510         }
511         else
512         {
513                 /* mask all irqs */             
514                 IWIC_WRITE(sc, bchan->offset + B_EXIM, 0xff);
515
516                 /* reset mode */
517                 IWIC_WRITE(sc, bchan->offset + B_MODE, 0);
518                 
519                 /* Bring interface down */
520                 IWIC_WRITE(sc, bchan->offset + B_CMDR, B_CMDR_RRST | B_CMDR_XRST);
521
522                 /* Flush pending interrupts */
523                 IWIC_READ(sc, bchan->offset + B_EXIR);
524         }
525 }
526
527 /*---------------------------------------------------------------------------*
528  *      start transmission on a b channel
529  *---------------------------------------------------------------------------*/
530 static void
531 iwic_bchannel_start(int unit, int chan_no)
532 {
533         struct iwic_softc *sc = &iwic_sc[unit];
534         register struct iwic_bchan *chan = &sc->sc_bchan[chan_no];
535         register int next_len;
536         register int len;
537
538         int s;
539         int activity = -1;
540         int cmd = 0;
541
542         s = SPLI4B();                           /* enter critical section */
543
544         NDBGL1(L1_BCHAN, "unit %d, channel %d", unit, chan_no);
545
546         if(chan->state & ST_TX_ACTIVE)          /* already running ? */
547         {
548                 splx(s);
549                 return;                         /* yes, leave */
550         }
551
552         /* get next mbuf from queue */
553         
554         IF_DEQUEUE(&chan->tx_queue, chan->out_mbuf_head);
555         
556         if(chan->out_mbuf_head == NULL)         /* queue empty ? */
557         {
558                 splx(s);                        /* leave critical section */
559                 return;                         /* yes, exit */
560         }
561
562         /* init current mbuf values */
563         
564         chan->out_mbuf_cur = chan->out_mbuf_head;
565         chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len;
566         chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data;    
567         
568         /* activity indicator for timeout handling */
569
570         if(chan->bprot == BPROT_NONE)
571         {
572                 if(!(i4b_l1_bchan_tel_silence(chan->out_mbuf_cur->m_data, chan->out_mbuf_cur->m_len)))
573                         activity = ACT_TX;
574         }
575         else
576         {
577                 activity = ACT_TX;
578         }
579
580         chan->state |= ST_TX_ACTIVE;            /* we start transmitting */
581
582         if(sc->sc_trace & TRACE_B_TX)   /* if trace, send mbuf to trace dev */
583         {
584                 i4b_trace_hdr_t hdr;
585                 hdr.unit = L0IWICUNIT(unit);
586                 hdr.type = (chan_no == IWIC_BCH_A ? TRC_CH_B1 : TRC_CH_B2);
587                 hdr.dir = FROM_TE;
588                 hdr.count = ++sc->sc_bchan[chan_no].sc_trace_bcount;
589                 MICROTIME(hdr.time);
590                 i4b_l1_trace_ind(&hdr, chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data);
591         }                       
592
593         len = 0;        /* # of chars put into tx fifo this time */
594
595         /*
596          * fill the tx fifo with data from the current mbuf. if
597          * current mbuf holds less data than fifo length, try to
598          * get the next mbuf from (a possible) mbuf chain. if there is
599          * not enough data in a single mbuf or in a chain, then this
600          * is the last mbuf and we tell the chip that it has to send
601          * CRC and closing flag
602          */
603          
604         while((len < IWIC_BCHAN_FIFO_LEN) && chan->out_mbuf_cur)
605         {
606                 /*
607                  * put as much data into the fifo as is
608                  * available from the current mbuf
609                  */
610                  
611                 if((len + chan->out_mbuf_cur_len) >= IWIC_BCHAN_FIFO_LEN)
612                         next_len = IWIC_BCHAN_FIFO_LEN - len;
613                 else
614                         next_len = chan->out_mbuf_cur_len;
615
616                 /* write what we have from current mbuf to fifo */
617
618                 IWIC_WRBFIFO(sc, chan, chan->out_mbuf_cur_ptr, next_len);
619                 
620                 len += next_len;                /* update # of bytes written */
621                 chan->txcount += next_len;      /* statistics */
622                 chan->out_mbuf_cur_ptr += next_len;     /* data ptr */
623                 chan->out_mbuf_cur_len -= next_len;     /* data len */
624
625                 /*
626                  * in case the current mbuf (of a possible chain) data
627                  * has been put into the fifo, check if there is a next
628                  * mbuf in the chain. If there is one, get ptr to it
629                  * and update the data ptr and the length
630                  */
631                  
632                 if((chan->out_mbuf_cur_len <= 0)        &&
633                   ((chan->out_mbuf_cur = chan->out_mbuf_cur->m_next) != NULL))
634                 {
635                         chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data;
636                         chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len;
637
638                         if(sc->sc_trace & TRACE_B_TX)
639                         {
640                                 i4b_trace_hdr_t hdr;
641                                 hdr.unit = L0IWICUNIT(unit);
642                                 hdr.type = (chan_no == IWIC_BCH_A ? TRC_CH_B1 : TRC_CH_B2);
643                                 hdr.dir = FROM_TE;
644                                 hdr.count = ++sc->sc_bchan[chan_no].sc_trace_bcount;
645                                 MICROTIME(hdr.time);
646                                 i4b_l1_trace_ind(&hdr, chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data);
647                         }
648                 }
649         }
650
651         /*
652          * if there is either still data in the current mbuf and/or
653          * there is a successor on the chain available issue just
654          * a XTF (transmit) command to the chip. if there is no more
655          * data available from the current mbuf (-chain), issue
656          * an XTF and an XME (message end) command which will then
657          * send the CRC and the closing HDLC flag sequence
658          */
659          
660         if(chan->out_mbuf_cur && (chan->out_mbuf_cur_len > 0))
661         {
662                 /*
663                  * more data available, send current fifo out.
664                  * next xfer to tx fifo is done in the
665                  * interrupt routine.
666                  */
667                  
668                 cmd |= B_CMDR_XMS;
669         }
670         else
671         {
672                 /* end of mbuf chain */
673         
674                 if(chan->bprot == BPROT_NONE)
675                         cmd |= B_CMDR_XMS;
676                 else
677                         cmd |= (B_CMDR_XMS | B_CMDR_XME);
678                 
679                 i4b_Bfreembuf(chan->out_mbuf_head);     /* free mbuf chain */
680                 
681                 chan->out_mbuf_head = NULL;
682                 chan->out_mbuf_cur = NULL;                      
683                 chan->out_mbuf_cur_ptr = NULL;
684                 chan->out_mbuf_cur_len = 0;
685         }
686
687         /* call timeout handling routine */
688         
689         if(activity == ACT_RX || activity == ACT_TX)
690                 (*chan->iwic_drvr_linktab->bch_activity)(chan->iwic_drvr_linktab->unit, activity);
691
692         if(cmd)
693         {
694                 cmd |= B_CMDR_RACT;
695                 IWIC_WRITE(sc, chan->offset + B_CMDR, cmd);
696         }
697                 
698         splx(s);        
699 }
700
701 /*---------------------------------------------------------------------------*
702  *      return B-channel statistics
703  *---------------------------------------------------------------------------*/
704 static void
705 iwic_bchannel_stat(int unit, int chan_no, bchan_statistics_t *bsp)
706 {
707         struct iwic_softc *sc = iwic_find_sc(unit);
708         struct iwic_bchan *bchan = &sc->sc_bchan[chan_no];
709
710         int s = SPLI4B();
711
712         bsp->outbytes = bchan->txcount;
713         bsp->inbytes = bchan->rxcount;
714
715         bchan->txcount = 0;
716         bchan->rxcount = 0;
717
718         splx(s);
719 }
720
721 /*---------------------------------------------------------------------------*
722  *      initialize our local linktab
723  *---------------------------------------------------------------------------*/
724 void
725 iwic_init_linktab(struct iwic_softc *sc)
726 {
727         struct iwic_bchan *chan;
728         isdn_link_t *lt;
729
730         /* make sure the hardware driver is known to layer 4 */
731         ctrl_types[CTRL_PASSIVE].set_linktab = i4b_l1_set_linktab;
732         ctrl_types[CTRL_PASSIVE].get_linktab = i4b_l1_ret_linktab;
733
734         /* channel A */
735         
736         chan = &sc->sc_bchan[IWIC_BCH_A];
737         lt = &chan->iwic_isdn_linktab;
738         
739         lt->unit = sc->sc_unit;
740         lt->channel = IWIC_BCH_A;
741         lt->bch_config = iwic_bchannel_setup;
742         lt->bch_tx_start = iwic_bchannel_start;
743         lt->bch_stat = iwic_bchannel_stat;
744         lt->tx_queue = &chan->tx_queue;
745
746         /* used by non-HDLC data transfers, i.e. telephony drivers */
747         lt->rx_queue = &chan->rx_queue;
748
749         /* used by HDLC data transfers, i.e. ipr and isp drivers */     
750         lt->rx_mbuf = &chan->in_mbuf;   
751                                                 
752         /* channel B */
753         
754         chan = &sc->sc_bchan[IWIC_BCH_B];
755         lt = &chan->iwic_isdn_linktab;
756         
757         lt->unit = sc->sc_unit;
758         lt->channel = IWIC_BCH_B;
759         lt->bch_config = iwic_bchannel_setup;
760         lt->bch_tx_start = iwic_bchannel_start;
761         lt->bch_stat = iwic_bchannel_stat;
762         lt->tx_queue = &chan->tx_queue;
763
764         /* used by non-HDLC data transfers, i.e. telephony drivers */
765         lt->rx_queue = &chan->rx_queue;
766
767         /* used by HDLC data transfers, i.e. ipr and isp drivers */     
768         lt->rx_mbuf = &chan->in_mbuf;   
769 }
770  
771 #endif /* NIWIC > 0 */