Make all network interrupt service routines MPSAFE part 1/3.
[dragonfly.git] / sys / net / i4b / driver / i4b_isppp.c
1 /*
2  *   Copyright (c) 1997 Joerg Wunsch. All rights reserved.
3  *
4  *   Copyright (c) 1997, 2000 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  *
10  *   1. Redistributions of source code must retain the above copyright
11  *      notice, this list of conditions and the following disclaimer.
12  *   2. Redistributions in binary form must reproduce the above copyright
13  *      notice, this list of conditions and the following disclaimer in the
14  *      documentation and/or other materials provided with the distribution.
15  *   
16  *   THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  *   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  *   ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  *   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  *   SUCH DAMAGE.
27  *
28  *---------------------------------------------------------------------------
29  *
30  *      i4b_isppp.c - isdn4bsd kernel SyncPPP driver
31  *      --------------------------------------------
32  *
33  *      Uses Serge Vakulenko's sppp backend (originally contributed with
34  *      the "cx" driver for Cronyx's HDLC-in-hardware device).  This driver
35  *      is only the glue between sppp and i4b.
36  *
37  *      $Id: i4b_isppp.c,v 1.44 2000/08/31 07:07:26 hm Exp $
38  *
39  * $FreeBSD: src/sys/i4b/driver/i4b_isppp.c,v 1.7.2.3 2003/02/06 14:50:53 gj Exp $
40  * $DragonFly: src/sys/net/i4b/driver/i4b_isppp.c,v 1.15 2005/11/28 17:13:45 dillon Exp $
41  *
42  *      last edit-date: [Thu Aug 31 09:02:27 2000]
43  *
44  *---------------------------------------------------------------------------*/
45
46
47 #include "use_i4bisppp.h"
48
49 #if NI4BISPPP == 0
50 # error "You need to define `device sppp <N>' with options ISPPP"
51 #endif
52
53 #include <sys/param.h>
54 #include <sys/systm.h>
55 #include <sys/mbuf.h>
56 #include <sys/socket.h>
57 #include <sys/errno.h>
58 #include <sys/ioccom.h>
59 #include <sys/sockio.h>
60 #include <sys/kernel.h>
61 #include <sys/thread2.h>
62
63 #include <net/if.h>
64 #include <net/if_arp.h>
65 #include <net/if_types.h>
66 #include <net/sppp/if_sppp.h>
67
68
69 #include "use_bpf.h"     
70 #include <sys/time.h>
71 #include <net/bpf.h>
72
73 #include <net/i4b/include/machine/i4b_debug.h>
74 #include <net/i4b/include/machine/i4b_ioctl.h>
75
76 #include "../include/i4b_global.h"
77 #include "../include/i4b_l3l4.h"
78 #include "../layer4/i4b_l4.h"
79
80 #define PDEVSTATIC      static
81
82 #define ISPPP_FMT       "%s: "
83 #define ISPPP_ARG(sc)   ((sc)->sc_if.if_xname)
84 #define IFP2UNIT(ifp)   ((struct i4bisppp_softc *)ifp->if_softc)->sc_unit
85                 
86 #  define IOCTL_CMD_T u_long
87
88 PDEVSTATIC void i4bispppattach(void *);
89 PSEUDO_SET(i4bispppattach, i4b_isppp);
90
91 #define I4BISPPPACCT            1       /* enable accounting messages */
92 #define I4BISPPPACCTINTVL       2       /* accounting msg interval in secs */
93 #define I4BISPPPDISCDEBUG       1       
94
95 #define PPP_HDRLEN              4       /* 4 octetts PPP header length  */
96
97 struct i4bisppp_softc {
98         /*
99          * struct sppp starts with a struct ifnet, but we gotta allocate
100          * more space for it.  NB: do not relocate this union, it must
101          * be first in isppp_softc.  The tls and tlf hooks below want to
102          * convert a ``struct sppp *'' into a ``struct isppp_softc *''.
103          */
104         union {
105                 struct ifnet scu_if;
106                 struct sppp scu_sp;
107         } sc_if_un;
108 #define sc_if sc_if_un.scu_if
109
110         int     sc_state;       /* state of the interface       */
111         int     sc_unit;        /* unit number                  */
112
113         call_desc_t *sc_cdp;    /* ptr to call descriptor       */
114
115 #ifdef I4BISPPPACCT
116         int sc_iinb;            /* isdn driver # of inbytes     */
117         int sc_ioutb;           /* isdn driver # of outbytes    */
118         int sc_inb;             /* # of bytes rx'd              */
119         int sc_outb;            /* # of bytes tx'd              */
120         int sc_linb;            /* last # of bytes rx'd         */
121         int sc_loutb;           /* last # of bytes tx'd         */
122         int sc_fn;              /* flag, first null acct        */
123 #endif
124
125         struct callout  sc_timeout;
126
127 } i4bisppp_softc[NI4BISPPP];
128
129 static void     i4bisppp_init_linktab(int unit);
130 static int      i4bisppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, caddr_t data,
131                                struct ucred *cr);
132
133 #if 0
134 static void     i4bisppp_send(struct ifnet *ifp);
135 #endif
136
137 static void     i4bisppp_start(struct ifnet *ifp);
138
139 #if 0 /* never used ??? */
140 static void     i4bisppp_timeout(void *cookie);
141 #endif
142
143 static void     i4bisppp_tls(struct sppp *sp);
144 static void     i4bisppp_tlf(struct sppp *sp);
145 static void     i4bisppp_state_changed(struct sppp *sp, int new_state);
146 static void     i4bisppp_negotiation_complete(struct sppp *sp);
147 static void     i4bisppp_watchdog(struct ifnet *ifp);
148 time_t          i4bisppp_idletime(int unit);
149
150 /* initialized by L4 */
151
152 static drvr_link_t i4bisppp_drvr_linktab[NI4BISPPP];
153 static isdn_link_t *isdn_linktab[NI4BISPPP];
154
155 enum i4bisppp_states {
156         ST_IDLE,                        /* initialized, ready, idle     */
157         ST_DIALING,                     /* dialling out to remote       */
158         ST_CONNECTED,                   /* connected to remote          */
159 };
160
161 /*===========================================================================*
162  *                      DEVICE DRIVER ROUTINES
163  *===========================================================================*/
164
165 /*---------------------------------------------------------------------------*
166  *      interface attach routine at kernel boot time
167  *---------------------------------------------------------------------------*/
168 PDEVSTATIC void
169 i4bispppattach(void *dummy)
170 {
171         struct i4bisppp_softc *sc = i4bisppp_softc;
172         int i;
173
174 #ifndef HACK_NO_PSEUDO_ATTACH_MSG
175 #ifdef SPPP_VJ
176         printf("i4bisppp: %d ISDN SyncPPP device(s) attached (VJ header compression)\n", NI4BISPPP);
177 #else
178         printf("i4bisppp: %d ISDN SyncPPP device(s) attached\n", NI4BISPPP);
179 #endif
180 #endif
181
182         for(i = 0; i < NI4BISPPP; sc++, i++) {
183                 i4bisppp_init_linktab(i);
184                 
185                 sc->sc_if.if_softc = sc;
186
187                 if_initname(&(sc->sc_if), "isp", i);
188                 sc->sc_if.if_mtu = PP_MTU;
189
190                 sc->sc_if.if_flags = IFF_SIMPLEX | IFF_POINTOPOINT;
191
192                 sc->sc_if.if_type = IFT_ISDNBASIC;
193                 sc->sc_state = ST_IDLE;
194
195                 sc->sc_if.if_ioctl = i4bisppp_ioctl;
196
197                 /* actually initialized by sppp_attach() */
198                 /* sc->sc_if.if_output = sppp_output; */
199
200                 sc->sc_if.if_start = i4bisppp_start;
201
202                 sc->sc_if.if_hdrlen = 0;
203                 sc->sc_if.if_addrlen = 0;
204                 sc->sc_if.if_snd.ifq_maxlen = IFQ_MAXLEN;
205
206                 sc->sc_if.if_ipackets = 0;
207                 sc->sc_if.if_ierrors = 0;
208                 sc->sc_if.if_opackets = 0;
209                 sc->sc_if.if_oerrors = 0;
210                 sc->sc_if.if_collisions = 0;
211                 sc->sc_if.if_ibytes = 0;
212                 sc->sc_if.if_obytes = 0;
213                 sc->sc_if.if_imcasts = 0;
214                 sc->sc_if.if_omcasts = 0;
215                 sc->sc_if.if_iqdrops = 0;
216                 sc->sc_if.if_noproto = 0;
217
218 #if I4BISPPPACCT
219                 sc->sc_if.if_timer = 0; 
220                 sc->sc_if.if_watchdog = i4bisppp_watchdog;      
221                 sc->sc_iinb = 0;
222                 sc->sc_ioutb = 0;
223                 sc->sc_inb = 0;
224                 sc->sc_outb = 0;
225                 sc->sc_linb = 0;
226                 sc->sc_loutb = 0;
227                 sc->sc_fn = 1;
228 #endif
229
230                 sc->sc_if_un.scu_sp.pp_tls = i4bisppp_tls;
231                 sc->sc_if_un.scu_sp.pp_tlf = i4bisppp_tlf;
232                 sc->sc_if_un.scu_sp.pp_con = i4bisppp_negotiation_complete;
233                 sc->sc_if_un.scu_sp.pp_chg = i4bisppp_state_changed;
234
235                 sppp_attach(&sc->sc_if);
236 /* XXX: validate / add proper code */
237                 ether_ifattach_bpf(&sc->sc_if, ((struct arpcom*)sc)->ac_enaddr,
238                                    DLT_PPP, PPP_HDRLEN, NULL);
239                 callout_init(&sc->sc_timeout);
240         }
241 }
242
243 /*---------------------------------------------------------------------------*
244  *      process ioctl
245  *---------------------------------------------------------------------------*/
246 static int
247 i4bisppp_ioctl(struct ifnet *ifp, IOCTL_CMD_T cmd, caddr_t data,
248                struct ucred *cr)
249 {
250         struct i4bisppp_softc *sc = ifp->if_softc;
251 #if 0
252         struct sppp *sp = (struct sppp *)sc;
253         struct ifaddr *ifa = (struct ifaddr *) data;
254         struct ifreq *ifr = (struct ifreq *) data;
255 #endif
256
257         int error;
258
259         error = sppp_ioctl(&sc->sc_if, cmd, data);
260         if (error)
261                 return error;
262
263         switch(cmd) {
264         case SIOCSIFFLAGS:
265 #if 0 /* never used ??? */
266                 crit_enter();
267                 if ((ifp->if_flags & IFF_UP) == 0)
268                         callout_stop(&sc->sc_timeout);
269                 crit_exit();
270 #endif
271                 break;
272         }
273
274         return 0;
275 }
276
277 /*---------------------------------------------------------------------------*
278  *      start output to ISDN B-channel
279  *---------------------------------------------------------------------------*/
280 static void
281 i4bisppp_start(struct ifnet *ifp)
282 {
283         struct i4bisppp_softc *sc = ifp->if_softc;
284         struct mbuf *m;
285         /* int s; */
286         int unit = IFP2UNIT(ifp);
287
288         if (sppp_isempty(ifp))
289                 return;
290
291         if(sc->sc_state != ST_CONNECTED)
292                 return;
293
294         /*
295          * crit_enter();
296          * ifp->if_flags |= IFF_OACTIVE; // - need to clear this somewhere
297          * crit_exit();
298          */
299
300         while ((m = sppp_dequeue(&sc->sc_if)) != NULL)
301         {
302                 BPF_MTAP(ifp, m);
303
304                 microtime(&ifp->if_lastchange);
305
306                 if(IF_QFULL(isdn_linktab[unit]->tx_queue))
307                 {
308                         NDBGL4(L4_ISPDBG, "isp%d, tx queue full!", unit);
309                         m_freem(m);
310                 }
311                 else
312                 {
313 #if 0
314                         sc->sc_if.if_obytes += m->m_pkthdr.len;
315 #endif
316                         sc->sc_outb += m->m_pkthdr.len;
317                         sc->sc_if.if_opackets++;
318
319                         IF_ENQUEUE(isdn_linktab[unit]->tx_queue, m);
320                 }
321         }
322         isdn_linktab[unit]->bch_tx_start(isdn_linktab[unit]->unit,
323                                          isdn_linktab[unit]->channel);
324 }
325
326 #ifdef I4BISPPPACCT
327 /*---------------------------------------------------------------------------*
328  *      watchdog routine
329  *---------------------------------------------------------------------------*/
330 static void
331 i4bisppp_watchdog(struct ifnet *ifp)
332 {
333         struct i4bisppp_softc *sc = ifp->if_softc;
334         int unit = IFP2UNIT(ifp);
335         bchan_statistics_t bs;
336         
337         (*isdn_linktab[unit]->bch_stat)
338                 (isdn_linktab[unit]->unit, isdn_linktab[unit]->channel, &bs);
339
340         sc->sc_ioutb += bs.outbytes;
341         sc->sc_iinb += bs.inbytes;
342         
343         if((sc->sc_iinb != sc->sc_linb) || (sc->sc_ioutb != sc->sc_loutb) || sc->sc_fn)
344         {
345                 int ri = (sc->sc_iinb - sc->sc_linb)/I4BISPPPACCTINTVL;
346                 int ro = (sc->sc_ioutb - sc->sc_loutb)/I4BISPPPACCTINTVL;
347
348                 if((sc->sc_iinb == sc->sc_linb) && (sc->sc_ioutb == sc->sc_loutb))
349                         sc->sc_fn = 0;
350                 else
351                         sc->sc_fn = 1;
352                         
353                 sc->sc_linb = sc->sc_iinb;
354                 sc->sc_loutb = sc->sc_ioutb;
355
356                 i4b_l4_accounting(BDRV_ISPPP, unit, ACCT_DURING,
357                          sc->sc_ioutb, sc->sc_iinb, ro, ri, sc->sc_outb, sc->sc_inb);
358         }
359         sc->sc_if.if_timer = I4BISPPPACCTINTVL;         
360
361 #if 0 /* old stuff, keep it around */
362         printf(ISPPP_FMT "transmit timeout\n", ISPPP_ARG(sc));
363         i4bisppp_start(ifp);
364 #endif
365 }
366 #endif /* I4BISPPPACCT */
367
368 /*
369  *===========================================================================*
370  *                      SyncPPP layer interface routines
371  *===========================================================================*
372  */
373
374 #if 0 /* never used ??? */
375 /*---------------------------------------------------------------------------*
376  *      just an alias for i4bisppp_tls, but of type timeout_t
377  *---------------------------------------------------------------------------*/
378 static void
379 i4bisppp_timeout(void *cookie)
380 {
381         i4bisppp_tls((struct sppp *)cookie);
382 }
383 #endif
384
385 /*---------------------------------------------------------------------------*
386  *      PPP this-layer-started action
387  *---------------------------------------------------------------------------*
388  */
389 static void
390 i4bisppp_tls(struct sppp *sp)
391 {
392         struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
393         struct ifnet *ifp = (struct ifnet *)sp;
394
395         if(sc->sc_state == ST_CONNECTED)
396                 return;
397
398         i4b_l4_dialout(BDRV_ISPPP, IFP2UNIT(ifp));
399 }
400
401 /*---------------------------------------------------------------------------*
402  *      PPP this-layer-finished action
403  *---------------------------------------------------------------------------*
404  */
405 static void
406 i4bisppp_tlf(struct sppp *sp)
407 {
408         struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
409 /*      call_desc_t *cd = sc->sc_cdp;   */
410         struct ifnet *ifp = (struct ifnet *)sp; 
411         
412         if(sc->sc_state != ST_CONNECTED)
413                 return;
414
415 #if 0 /* never used ??? */
416         callout_stop(&sc->sc_timeout);
417 #endif
418
419         i4b_l4_drvrdisc(BDRV_ISPPP, IFP2UNIT(ifp));
420 }
421 /*---------------------------------------------------------------------------*
422  *      PPP interface phase change
423  *---------------------------------------------------------------------------*
424  */
425 static void
426 i4bisppp_state_changed(struct sppp *sp, int new_state)
427 {
428         struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
429         
430         i4b_l4_ifstate_changed(sc->sc_cdp, new_state);
431 }
432
433 /*---------------------------------------------------------------------------*
434  *      PPP control protocol negotiation complete (run ip-up script now)
435  *---------------------------------------------------------------------------*
436  */
437 static void
438 i4bisppp_negotiation_complete(struct sppp *sp)
439 {
440         struct i4bisppp_softc *sc = (struct i4bisppp_softc *)sp;
441         
442         i4b_l4_negcomplete(sc->sc_cdp);
443 }
444
445 /*===========================================================================*
446  *                      ISDN INTERFACE ROUTINES
447  *===========================================================================*/
448
449 /*---------------------------------------------------------------------------*
450  *      this routine is called from L4 handler at connect time
451  *---------------------------------------------------------------------------*/
452 static void
453 i4bisppp_connect(int unit, void *cdp)
454 {
455         struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
456         struct sppp *sp = &sc->sc_if_un.scu_sp;
457
458         crit_enter();
459
460         sc->sc_cdp = (call_desc_t *)cdp;
461         sc->sc_state = ST_CONNECTED;
462
463 #if I4BISPPPACCT
464         sc->sc_iinb = 0;
465         sc->sc_ioutb = 0;
466         sc->sc_inb = 0;
467         sc->sc_outb = 0;
468         sc->sc_linb = 0;
469         sc->sc_loutb = 0;
470         sc->sc_if.if_timer = I4BISPPPACCTINTVL;
471 #endif
472         
473 #if 0 /* never used ??? */
474         callout_stop(&sc->sc_timeout);
475 #endif
476
477         sp->pp_up(sp);          /* tell PPP we are ready */
478         sp->pp_last_sent = sp->pp_last_recv = SECOND;
479
480         crit_exit();
481 }
482
483 /*---------------------------------------------------------------------------*
484  *      this routine is called from L4 handler at disconnect time
485  *---------------------------------------------------------------------------*/
486 static void
487 i4bisppp_disconnect(int unit, void *cdp)
488 {
489         call_desc_t *cd = (call_desc_t *)cdp;
490         struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
491         struct sppp *sp = &sc->sc_if_un.scu_sp;
492
493         crit_enter();
494
495         /* new stuff to check that the active channel is being closed */
496         if (cd != sc->sc_cdp)
497         {
498                 NDBGL4(L4_ISPDBG, "isp%d, channel%d not active!", unit, cd->channelid);
499                 crit_exit();
500                 return;
501         }
502
503 #if I4BISPPPACCT
504         sc->sc_if.if_timer = 0;
505 #endif
506
507         i4b_l4_accounting(BDRV_ISPPP, unit, ACCT_FINAL,
508                  sc->sc_ioutb, sc->sc_iinb, 0, 0, sc->sc_outb, sc->sc_inb);
509         
510         if (sc->sc_state == ST_CONNECTED)
511         {
512 #if 0 /* never used ??? */
513                 callout_stop(&sc->sc_timeout);
514 #endif
515                 sc->sc_cdp = (call_desc_t *)0;  
516                 /* do this here because pp_down calls i4bisppp_tlf */
517                 sc->sc_state = ST_IDLE;
518                 sp->pp_down(sp);        /* tell PPP we have hung up */
519         }
520
521         crit_exit();
522 }
523
524 /*---------------------------------------------------------------------------*
525  *      this routine is used to give a feedback from userland demon
526  *      in case of dial problems
527  *---------------------------------------------------------------------------*/
528 static void
529 i4bisppp_dialresponse(int unit, int status, cause_t cause)
530 {
531         struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
532         struct sppp *sp = &sc->sc_if_un.scu_sp;
533
534         NDBGL4(L4_ISPDBG, "isp%d: status=%d, cause=%d", unit, status, cause);
535
536         if(status != DSTAT_NONE)
537         {
538                 struct mbuf *m;
539                 
540                 NDBGL4(L4_ISPDBG, "isp%d: clearing queues", unit);
541
542                 if(!(sppp_isempty(&sc->sc_if)))
543                 {
544                         while((m = sppp_dequeue(&sc->sc_if)) != NULL)
545                                 m_freem(m);
546                 }
547
548                 sc->sc_cdp = (call_desc_t *)0;
549                 /* do this here because pp_down calls i4bisppp_tlf */
550                 sc->sc_state = ST_IDLE;
551                 /*
552                  * Ahh, sppp doesn't like to get a down event when
553                  * dialing fails. So first tell it that we are up
554                  * (doesn't hurt us since sc_state != ST_CONNECTED)
555                  * and then go down.
556                  */
557                 sp->pp_up(sp);
558                 sp->pp_down(sp);
559         }
560 }
561         
562 /*---------------------------------------------------------------------------*
563  *      interface up/down
564  *---------------------------------------------------------------------------*/
565 static void
566 i4bisppp_updown(int unit, int updown)
567 {
568         /* could probably do something useful here */
569 }
570         
571 /*---------------------------------------------------------------------------*
572  *      this routine is called from the HSCX interrupt handler
573  *      when a new frame (mbuf) has been received and was put on
574  *      the rx queue.
575  *---------------------------------------------------------------------------*/
576 static void
577 i4bisppp_rx_data_rdy(int unit)
578 {
579         struct i4bisppp_softc *sc = &i4bisppp_softc[unit];
580         struct mbuf *m;
581         
582         if((m = *isdn_linktab[unit]->rx_mbuf) == NULL)
583                 return;
584
585         m->m_pkthdr.rcvif = &sc->sc_if;
586         m->m_pkthdr.len = m->m_len;
587
588         microtime(&sc->sc_if.if_lastchange);
589
590         sc->sc_if.if_ipackets++;
591 #if 0
592         sc->sc_if.if_ibytes += m->m_pkthdr.len;
593 #endif
594
595 #if I4BISPPPACCT
596         sc->sc_inb += m->m_pkthdr.len;
597 #endif
598         
599 #ifdef I4BISPPPDEBUG
600         printf("i4bisppp_rx_data_ready: received packet!\n");
601 #endif
602
603         BPF_MTAP(&sc->sc_if, m);
604
605         crit_enter();
606         sppp_input(&sc->sc_if, m);
607         crit_exit();
608 }
609
610 /*---------------------------------------------------------------------------*
611  *      this routine is called from the HSCX interrupt handler
612  *      when the last frame has been sent out and there is no
613  *      further frame (mbuf) in the tx queue.
614  *---------------------------------------------------------------------------*/
615 static void
616 i4bisppp_tx_queue_empty(int unit)
617 {
618         i4bisppp_start(&i4bisppp_softc[unit].sc_if);    
619 }
620
621 /*---------------------------------------------------------------------------*
622  *      THIS should be used instead of last_active_time to implement
623  *      an activity timeout mechanism.
624  *
625  *      Sending back the time difference unneccessarily complicates the
626  *      idletime checks in i4b_l4.c. Return the largest time instead.
627  *      That way the code in i4b_l4.c needs only minimal changes.
628  *---------------------------------------------------------------------------*/
629 time_t
630 i4bisppp_idletime(int unit)
631 {
632         struct sppp *sp;
633         sp = (struct sppp *) &i4bisppp_softc[unit];
634
635         return((sp->pp_last_recv < sp->pp_last_sent) ?
636                         sp->pp_last_sent : sp->pp_last_recv);
637 }
638
639 /*---------------------------------------------------------------------------*
640  *      this routine is called from the HSCX interrupt handler
641  *      each time a packet is received or transmitted. It should
642  *      be used to implement an activity timeout mechanism.
643  *---------------------------------------------------------------------------*/
644 static void
645 i4bisppp_activity(int unit, int rxtx)
646 {
647         i4bisppp_softc[unit].sc_cdp->last_active_time = SECOND;
648 }
649
650 /*---------------------------------------------------------------------------*
651  *      return this drivers linktab address
652  *---------------------------------------------------------------------------*/
653 drvr_link_t *
654 i4bisppp_ret_linktab(int unit)
655 {
656         return(&i4bisppp_drvr_linktab[unit]);
657 }
658
659 /*---------------------------------------------------------------------------*
660  *      setup the isdn_linktab for this driver
661  *---------------------------------------------------------------------------*/
662 void
663 i4bisppp_set_linktab(int unit, isdn_link_t *ilt)
664 {
665         isdn_linktab[unit] = ilt;
666 }
667
668 /*---------------------------------------------------------------------------*
669  *      initialize this drivers linktab
670  *---------------------------------------------------------------------------*/
671 static void
672 i4bisppp_init_linktab(int unit)
673 {
674         i4bisppp_drvr_linktab[unit].unit = unit;
675         i4bisppp_drvr_linktab[unit].bch_rx_data_ready = i4bisppp_rx_data_rdy;
676         i4bisppp_drvr_linktab[unit].bch_tx_queue_empty = i4bisppp_tx_queue_empty;
677         i4bisppp_drvr_linktab[unit].bch_activity = i4bisppp_activity;
678         i4bisppp_drvr_linktab[unit].line_connected = i4bisppp_connect;
679         i4bisppp_drvr_linktab[unit].line_disconnected = i4bisppp_disconnect;
680         i4bisppp_drvr_linktab[unit].dial_response = i4bisppp_dialresponse;      
681         i4bisppp_drvr_linktab[unit].updown_ind = i4bisppp_updown;       
682 }
683
684 /*===========================================================================*/