53ac6efbd8
That's EVERY SINGLE driver that has an entry in conf.c.. my next trick will be to define cdevsw[] and bdevsw[] as empty arrays and remove all those DAMNED defines as well.. Each of these drivers has a SYSINIT linker set entry that comes in very early.. and asks teh driver to add it's own entry to the two devsw[] tables. some slight reworking of the commits from yesterday (added the SYSINIT stuff and some usually wrong but token DEVFS entries to all these devices. BTW does anyone know where the 'ata' entries in conf.c actually reside? seems we don't actually have a 'ataopen() etc... If you want to add a new device in conf.c please make sure I know so I can keep it up to date too.. as before, this is all dependent on #if defined(JREMOD) (and #ifdef DEVFS in parts)
703 lines
14 KiB
C
703 lines
14 KiB
C
static char _isdnid[] = "@(#)$Id: isdn.c,v 1.6 1995/11/16 10:47:21 bde Exp $";
|
|
/*******************************************************************************
|
|
* II - Version 0.1 $Revision: 1.6 $ $State: Exp $
|
|
*
|
|
* Copyright 1994 Dietmar Friede
|
|
*******************************************************************************
|
|
* Bug reports, patches, comments, suggestions should be sent to:
|
|
*
|
|
* jkr@saarlink.de or jkrause@guug.de
|
|
*
|
|
*******************************************************************************
|
|
* $Log: isdn.c,v $
|
|
* Revision 1.6 1995/11/16 10:47:21 bde
|
|
* Fixed a call to the listen function. A trailing arg was missing.
|
|
*
|
|
* Fixed the type of isdn_check(). A trailing arg was missing.
|
|
*
|
|
* Included "conf.h" to get some prototypes.
|
|
*
|
|
* Completed function declarations.
|
|
*
|
|
* Added prototypes.
|
|
*
|
|
* Removed some useless includes.
|
|
*
|
|
* Revision 1.5 1995/09/08 11:06:58 bde
|
|
* Fix benign type mismatches in devsw functions. 82 out of 299 devsw
|
|
* functions were wrong.
|
|
*
|
|
* Revision 1.4 1995/05/30 07:58:02 rgrimes
|
|
* Remove trailing whitespace.
|
|
*
|
|
* Revision 1.3 1995/03/28 07:54:44 bde
|
|
* Add and move declarations to fix all of the warnings from `gcc -Wimplicit'
|
|
* (except in netccitt, netiso and netns) that I didn't notice when I fixed
|
|
* "all" such warnings before.
|
|
*
|
|
* Revision 1.2 1995/02/15 06:28:29 jkh
|
|
* Fix up include paths, nuke some warnings.
|
|
*
|
|
* Revision 1.1 1995/02/14 15:00:33 jkh
|
|
* An ISDN driver that supports the EDSS1 and the 1TR6 ISDN interfaces.
|
|
* EDSS1 is the "Euro-ISDN", 1TR6 is the soon obsolete german ISDN Interface.
|
|
* Obtained from: Dietmar Friede <dfriede@drnhh.neuhaus.de> and
|
|
* Juergen Krause <jkr@saarlink.de>
|
|
*
|
|
* This is only one part - the rest to follow in a couple of hours.
|
|
* This part is a benign import, since it doesn't affect anything else.
|
|
*
|
|
*
|
|
******************************************************************************/
|
|
|
|
/*
|
|
* Copyright (c) 1994 Dietmar Friede (dietmar@friede.de) All rights reserved.
|
|
* FSF/FSAG GNU Copyright applies
|
|
*
|
|
* An intermediate level for ISDN Drivers.
|
|
*
|
|
*/
|
|
|
|
#include "isdn.h"
|
|
#include "ii.h"
|
|
#include "ity.h"
|
|
#include "itel.h"
|
|
#include "ispy.h"
|
|
#if NISDN > 0
|
|
|
|
#define TYPNR 4
|
|
#define N_ISDN_APPL (NII + NITY + NITEL + NISPY)
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/ioctl.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/conf.h>
|
|
#include <sys/proc.h>
|
|
|
|
#include "gnu/isdn/isdn_ioctl.h"
|
|
|
|
#ifdef JREMOD
|
|
#ifdef DEVFS
|
|
#include <sys/devfsext.h>
|
|
#endif /*DEVFS*/
|
|
#define CDEV_MAJOR 55
|
|
#endif /*JREMOD*/
|
|
|
|
isdn_appl_t isdn_appl[N_ISDN_APPL];
|
|
isdn_ctrl_t isdn_ctrl[N_ISDN_CTRL];
|
|
int Isdn_Appl, Isdn_Ctrl, Isdn_Typ;
|
|
|
|
extern void isdn_attach __P((void));
|
|
static timeout_t isdn_check;
|
|
extern char *isdn_get_prot __P((int ap, int dir));
|
|
extern int isdn_get_prot_size __P((int ap));
|
|
extern int isdn_set_prot __P((int ap, int dir, char *p));
|
|
extern int isdn_stat __P((int cn));
|
|
static void passout __P((int unit, int l, char *buf));
|
|
|
|
static int o_flags, r_flags, bufind[TYPNR];
|
|
static char buffer[TYPNR][257];
|
|
static u_char appl_list[TYPNR];
|
|
|
|
typedef u_char prot[2];
|
|
static u_char prot_size[2] = {0, 2};
|
|
static prot passiv[6] = {{0}, {3, 3}};
|
|
static prot activ[6] = {{0}, {1, 3}};
|
|
|
|
u_short isdn_state= 0;
|
|
static isdn_timeout= 0;
|
|
|
|
int
|
|
isdn_get_prot_size(int ap)
|
|
{
|
|
return (prot_size[isdn_appl[ap].prot]);
|
|
}
|
|
|
|
char *
|
|
isdn_get_prot(int ap, int dir)
|
|
{
|
|
if(dir)
|
|
return(activ[isdn_appl[ap].prot]);
|
|
return(passiv[isdn_appl[ap].prot]);
|
|
}
|
|
|
|
int
|
|
isdn_set_prot(int ap, int dir, char *p)
|
|
{
|
|
char *pr;
|
|
int i, l;
|
|
if ((l = isdn_get_prot_size(ap)) == 0)
|
|
return (0);
|
|
if (dir)
|
|
pr = passiv[isdn_appl[ap].prot];
|
|
else
|
|
pr = activ[isdn_appl[ap].prot];
|
|
for (i = 0; i < l; i++, pr++, p++)
|
|
*p = *pr;
|
|
return (l);
|
|
}
|
|
|
|
void
|
|
isdn_attach()
|
|
{
|
|
isdn_appl_t *appl;
|
|
int i, an;
|
|
|
|
appl_list[0]= Isdn_Typ= an= 0;
|
|
|
|
for(i= 0 ; i<NII; i++,an++)
|
|
{
|
|
appl = &isdn_appl[an];
|
|
appl->ctrl = -1;
|
|
appl->state = 0;
|
|
appl->appl = an;
|
|
appl->typ = Isdn_Typ;
|
|
appl->drivno = iiattach(an);
|
|
appl->PassUp = ii_input;
|
|
appl->PassDown = ii_out;
|
|
appl->Connect = ii_connect;
|
|
appl->DisConn = ii_disconnect;
|
|
}
|
|
|
|
appl_list[1]= an;
|
|
Isdn_Typ= 1;
|
|
for(i= 0 ; i<NITY; i++,an++)
|
|
{
|
|
appl = &isdn_appl[an];
|
|
appl->ctrl = -1;
|
|
appl->state = 0;
|
|
appl->appl = an;
|
|
appl->typ = Isdn_Typ;
|
|
appl->drivno = ityattach(an);
|
|
appl->PassUp = ity_input;
|
|
appl->PassDown = ity_out;
|
|
appl->Connect = ity_connect;
|
|
appl->DisConn = ity_disconnect;
|
|
}
|
|
|
|
appl_list[2]= an;
|
|
Isdn_Typ= 2;
|
|
for(i= 0 ; i<NITEL; i++,an++)
|
|
{
|
|
appl = &isdn_appl[an];
|
|
appl->ctrl = -1;
|
|
appl->state = 0;
|
|
appl->appl = an;
|
|
appl->typ = Isdn_Typ;
|
|
appl->drivno = itelattach(an);
|
|
appl->PassUp = itel_input;
|
|
appl->PassDown = itel_out;
|
|
appl->Connect = itel_connect;
|
|
appl->DisConn = itel_disconnect;
|
|
}
|
|
|
|
appl_list[3]= an;
|
|
Isdn_Typ= 3;
|
|
for(i= 0 ; i<NISPY; i++,an++)
|
|
{
|
|
appl = &isdn_appl[an];
|
|
appl->ctrl = -1;
|
|
appl->state = 0;
|
|
appl->appl = an;
|
|
appl->typ = Isdn_Typ;
|
|
appl->drivno = ispyattach(an);
|
|
appl->PassUp = ispy_input;
|
|
}
|
|
Isdn_Appl= an;
|
|
}
|
|
|
|
int
|
|
isdn_ctrl_attach(int n)
|
|
{
|
|
int c = Isdn_Ctrl;
|
|
|
|
if(Isdn_Ctrl == 0) isdn_attach();
|
|
if ((Isdn_Ctrl += n) <= N_ISDN_CTRL)
|
|
return (c);
|
|
Isdn_Ctrl = c;
|
|
return (-1);
|
|
}
|
|
|
|
/*
|
|
* isdnopen() New open on device.
|
|
*
|
|
* I forbid all but one open per application. The only programs opening the
|
|
* isdn device are the ISDN-daemon
|
|
*/
|
|
int
|
|
isdnopen(dev_t dev, int flags, int fmt, struct proc *p)
|
|
{
|
|
int err;
|
|
|
|
if (minor(dev)>Isdn_Typ)
|
|
return (ENXIO);
|
|
|
|
/* Card busy ? */
|
|
if (o_flags & (1 << minor(dev)))
|
|
return (EBUSY);
|
|
|
|
o_flags |= (1 << minor(dev));
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
isdnclose(dev_t dev, int flags, int fmt, struct proc *p)
|
|
{
|
|
o_flags &= ~(1 << minor(dev));
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
isdnread(dev_t dev, struct uio * uio, int ioflag)
|
|
{
|
|
int x;
|
|
int error = 0;
|
|
int unit= minor(dev);
|
|
|
|
r_flags &= ~(1 << unit);
|
|
|
|
x = splhigh();
|
|
if(bufind[unit] == 0)
|
|
{
|
|
r_flags |= (1 << unit);
|
|
error= tsleep((caddr_t) buffer[unit], PZERO + 1, "isdnin", hz);
|
|
}
|
|
if(bufind[unit])
|
|
{
|
|
buffer[unit][bufind[unit]++]= 0;
|
|
error = uiomove(buffer[unit], bufind[unit], uio);
|
|
bufind[unit] = 0;
|
|
}
|
|
splx(x);
|
|
return error;
|
|
}
|
|
|
|
int
|
|
isdnioctl(dev_t dev, int cmd, caddr_t data, int flags, struct proc *p)
|
|
{
|
|
int err, x, i;
|
|
isdn_appl_t *appl;
|
|
isdn_ctrl_t *ctrl;
|
|
short *val = (short *) data;
|
|
unsigned ab, an, cn;
|
|
|
|
err = 0;
|
|
ab= appl_list[minor(dev)];
|
|
|
|
switch (cmd)
|
|
{
|
|
case ISDN_LISTEN:
|
|
{
|
|
listen_t *s= (listen_t *) data;
|
|
|
|
an= ab;
|
|
if (s->ctrl >= Isdn_Ctrl)
|
|
return (ENODEV);
|
|
cn= s->ctrl;
|
|
ctrl = &isdn_ctrl[cn];
|
|
|
|
x = splhigh();
|
|
while(isdn_state)
|
|
{
|
|
err = tsleep((caddr_t) ctrl, PZERO | PCATCH, "slisten", 2);
|
|
if (err != EWOULDBLOCK)
|
|
{
|
|
splx(x);
|
|
return (err);
|
|
}
|
|
}
|
|
|
|
isdn_state = 0xffff;
|
|
while((err = (*ctrl->listen) (s->ctrl, minor(dev) | 0x30
|
|
, s->inf_mask ,s->subadr_mask ,s->si_mask, /* XXX */ 0)) == EBUSY)
|
|
{
|
|
err = tsleep((caddr_t) ctrl, PZERO | PCATCH, "blisten", 2);
|
|
if (err != EWOULDBLOCK)
|
|
{
|
|
splx(x);
|
|
return (err);
|
|
}
|
|
}
|
|
|
|
if (err)
|
|
{
|
|
splx(x);
|
|
return (err);
|
|
}
|
|
while (isdn_state == 0xffff)
|
|
{
|
|
err = tsleep((caddr_t) ctrl, PZERO | PCATCH, "ilisten", 2);
|
|
if (err != EWOULDBLOCK)
|
|
{
|
|
splx(x);
|
|
return (err);
|
|
}
|
|
}
|
|
splx(x);
|
|
err= isdn_state;
|
|
isdn_state= 0;
|
|
return (err); /* tricky but it works */
|
|
}
|
|
break;
|
|
|
|
case ISDN_DIAL:
|
|
{
|
|
dial_t *d= (dial_t*)data;
|
|
telno_t *t= &d->telno;
|
|
|
|
an = d->appl + ab;
|
|
cn = d->ctrl;
|
|
|
|
if (an >= Isdn_Appl || cn >= Isdn_Ctrl)
|
|
return (ENODEV);
|
|
|
|
appl = &isdn_appl[an];
|
|
|
|
if (ISBUSY(appl->ctrl) || appl->state)
|
|
return (EBUSY);
|
|
|
|
appl->state= 1;
|
|
x = splhigh();
|
|
|
|
while((err = (*isdn_ctrl[cn].connect) (cn, an
|
|
,d->b_channel, d->inf_mask, d->out_serv
|
|
,d->out_serv_add, d->src_subadr, t->length
|
|
,t->no, d->spv)) == EBUSY)
|
|
{
|
|
err = tsleep((caddr_t) appl, PZERO | PCATCH, "idial", 2);
|
|
if (err != EWOULDBLOCK)
|
|
{
|
|
splx(x);
|
|
return (err);
|
|
}
|
|
}
|
|
if(err) appl->state= 0;
|
|
splx(x);
|
|
return(err);
|
|
}
|
|
break;
|
|
case ISDN_HANGUP:
|
|
cn = data[0];
|
|
if (cn >= Isdn_Ctrl)
|
|
return (ENODEV);
|
|
x = splhigh();
|
|
|
|
while((err = (*isdn_ctrl[cn].disconnect) (cn, data[1])) == EBUSY)
|
|
{
|
|
err = tsleep((caddr_t) data, PZERO | PCATCH, "ihang", 2);
|
|
if (err != EWOULDBLOCK)
|
|
{
|
|
splx(x);
|
|
return (err);
|
|
}
|
|
}
|
|
splx(x);
|
|
break;
|
|
case ISDN_ACCEPT:
|
|
cn = data[0];
|
|
an = data[1] + ab;
|
|
if (cn >= Isdn_Ctrl)
|
|
return (ENODEV);
|
|
x = splhigh();
|
|
while((err = (*isdn_ctrl[cn].accept) (cn, an, data[2])) == EBUSY)
|
|
{
|
|
err = tsleep((caddr_t) data, PZERO | PCATCH, "iaccept", 2);
|
|
if (err != EWOULDBLOCK)
|
|
{
|
|
splx(x);
|
|
return (err);
|
|
}
|
|
}
|
|
splx(x);
|
|
break;
|
|
case ISDN_SET_PARAM:
|
|
{
|
|
isdn_param *p = (isdn_param *) data;
|
|
|
|
an = p->appl + ab;
|
|
if (an >= Isdn_Appl)
|
|
return (ENODEV);
|
|
appl = &isdn_appl[an];
|
|
bcopy(p, appl, sizeof(isdn_param));
|
|
appl->appl+= ab;
|
|
}
|
|
break;
|
|
case ISDN_GET_PARAM:
|
|
{
|
|
isdn_param *p = (isdn_param *) data;
|
|
an = p->appl + ab;
|
|
if (an >= Isdn_Appl)
|
|
return (ENODEV);
|
|
appl = &isdn_appl[an];
|
|
bcopy(appl, p, sizeof(isdn_param));
|
|
}
|
|
break;
|
|
default:
|
|
err = ENODEV;
|
|
}
|
|
|
|
return (err);
|
|
}
|
|
|
|
void
|
|
isdn_start_out(int cn)
|
|
{
|
|
isdn_ctrl_t *ctrl = &isdn_ctrl[cn];
|
|
isdn_appl_t *appl = &isdn_appl[ctrl->appl];
|
|
int x;
|
|
|
|
x= splhigh();
|
|
if (ctrl->o_len == 0)
|
|
{
|
|
int l;
|
|
l = isdn_set_prot(ctrl->appl, ctrl->islisten, ctrl->o_buf);
|
|
ctrl->o_len = (*appl->PassDown) (appl->drivno, ctrl->o_buf+l,2048-l);
|
|
|
|
if (ctrl->o_len == 0)
|
|
{
|
|
splx(x);
|
|
return;
|
|
}
|
|
ctrl->o_len+= l;
|
|
(*ctrl->output) (cn);
|
|
}
|
|
|
|
splx(x);
|
|
}
|
|
|
|
int
|
|
isdn_stat(int cn)
|
|
{
|
|
isdn_ctrl_t *ctrl = &isdn_ctrl[cn];
|
|
return((*ctrl->state) (cn));
|
|
}
|
|
|
|
int
|
|
isdn_output(int an)
|
|
{
|
|
isdn_appl_t *appl = &isdn_appl[an];
|
|
|
|
if (ISFREE(appl->ctrl))
|
|
{
|
|
int l;
|
|
char buf[10];
|
|
|
|
if(appl->state)
|
|
return(0);
|
|
|
|
l = sprintf(buf,"d %d", an-appl_list[appl->typ]);
|
|
passout(appl->typ,l,buf);
|
|
return(0);
|
|
}
|
|
isdn_start_out(appl->ctrl);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
isdn_msg(int an)
|
|
{
|
|
isdn_appl_t *appl = &isdn_appl[an];
|
|
|
|
if (ISFREE(appl->ctrl))
|
|
{
|
|
int l;
|
|
char buf[256];
|
|
|
|
l = sprintf(buf,"M %d", an-appl_list[appl->typ]);
|
|
l += (*appl->PassDown) (appl->drivno, buf+l,256-l);
|
|
passout(appl->typ,l,buf);
|
|
return(0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
int
|
|
isdn_input(int an, int len, char *buf, int dir)
|
|
{
|
|
int l;
|
|
char *p;
|
|
isdn_appl_t *appl = &isdn_appl[an];
|
|
|
|
if (l = isdn_get_prot_size(an))
|
|
{
|
|
p= isdn_get_prot(an,dir);
|
|
if((p[0] != buf[0]) || (p[1] != buf[1]))
|
|
return(0);
|
|
len -= l;
|
|
buf += l;
|
|
}
|
|
return ((*appl->PassUp) (appl->drivno, len, buf, dir));
|
|
}
|
|
|
|
void
|
|
isdn_accept_con_ind(int an, int cn, char serv, char serv_add, char subadr, char nl, char *num)
|
|
{
|
|
int l;
|
|
char buf[32];
|
|
|
|
an&= 0xf;
|
|
l = sprintf(buf, "a %d %d %d %d %c %d %d %s", an, cn ,serv, serv_add
|
|
, subadr,(u_char) num[0], nl, num + 1);
|
|
passout(an,l,buf);
|
|
}
|
|
|
|
void
|
|
isdn_info(int an, int typ, int len, char *data)
|
|
{
|
|
int l;
|
|
char buf[64];
|
|
u_short no;
|
|
|
|
if(an < Isdn_Appl)
|
|
no= isdn_appl[an].typ;
|
|
else no= an&0xf;
|
|
|
|
if(no > Isdn_Typ) no= 3;
|
|
|
|
if(len>48) len= 48;
|
|
data[len]= 0;
|
|
l = sprintf(buf,"i %d %d %d %s", an, typ, len, data);
|
|
passout(no,l,buf);
|
|
}
|
|
|
|
static void
|
|
isdn_check(void *chan)
|
|
{
|
|
int i;
|
|
|
|
isdn_timeout= 0;
|
|
for(i= 0; i < Isdn_Ctrl; i++)
|
|
{
|
|
int an;
|
|
isdn_ctrl_t *ctrl = &isdn_ctrl[i];
|
|
|
|
if((an= ctrl->appl) < Isdn_Appl)
|
|
{
|
|
isdn_appl_t *appl = &isdn_appl[an];
|
|
|
|
if(appl->timeout)
|
|
{
|
|
isdn_timeout= 1;
|
|
if(time.tv_sec > (ctrl->lastact + (appl->timeout)))
|
|
{
|
|
isdn_disconnect(an,0);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if(isdn_timeout)
|
|
{
|
|
timeout(isdn_check,0,hz/2);
|
|
}
|
|
}
|
|
|
|
void
|
|
isdn_conn_ind(int an, int cn, int dial)
|
|
{
|
|
isdn_appl_t *appl = &isdn_appl[an];
|
|
int l;
|
|
char buf[10];
|
|
|
|
if (appl->Connect)
|
|
(*appl->Connect) (appl->drivno);
|
|
|
|
l = sprintf(buf,"C %d %d %d", an-appl_list[appl->typ], cn, dial);
|
|
passout(appl->typ,l,buf);
|
|
if((isdn_timeout == 0) && appl->timeout)
|
|
{
|
|
isdn_timeout= 1;
|
|
timeout(isdn_check,0,hz/2);
|
|
}
|
|
}
|
|
|
|
void
|
|
isdn_disconn_ind(int an)
|
|
{
|
|
isdn_appl_t *appl = &isdn_appl[an];
|
|
int l;
|
|
char buf[10];
|
|
|
|
if(( an < 0) || (an >= Isdn_Appl))
|
|
return;
|
|
|
|
appl->state= 0;
|
|
if (appl->DisConn)
|
|
(*appl->DisConn) (appl->drivno);
|
|
l = sprintf(buf,"D %d", an-appl_list[appl->typ]);
|
|
passout(appl->typ,l,buf);
|
|
}
|
|
|
|
void
|
|
isdn_disconnect(int an, int rea)
|
|
{
|
|
isdn_appl_t *appl = &isdn_appl[an];
|
|
|
|
if (ISBUSY(appl->ctrl))
|
|
{
|
|
int x;
|
|
x = splhigh();
|
|
(*isdn_ctrl[appl->ctrl].disconnect)(appl->ctrl,rea);
|
|
splx(x);
|
|
}
|
|
}
|
|
|
|
static void
|
|
passout(int unit, int l, char *buf)
|
|
{
|
|
int x;
|
|
|
|
x = splhigh();
|
|
if ((bufind[unit] + l) >= 256)
|
|
{
|
|
splx(x);
|
|
return;
|
|
}
|
|
bcopy(buf,&buffer[unit][bufind[unit]],l);
|
|
bufind[unit] += l;
|
|
buffer[unit][bufind[unit]++]= 0;
|
|
if (r_flags & (1<<unit))
|
|
{
|
|
r_flags &= ~(1 << unit);
|
|
wakeup((caddr_t) buffer[unit]);
|
|
}
|
|
splx(x);
|
|
}
|
|
|
|
#ifdef JREMOD
|
|
struct cdevsw isdn_cdevsw =
|
|
{ isdnopen, isdnclose, isdnread, nowrite, /*55*/
|
|
isdnioctl, nostop, nullreset, nodevtotty,/* isdn */
|
|
seltrue, nommap, NULL };
|
|
|
|
static isdn_devsw_installed = 0;
|
|
|
|
static void isdn_drvinit(void *unused)
|
|
{
|
|
dev_t dev;
|
|
|
|
if( ! isdn_devsw_installed ) {
|
|
dev = makedev(CDEV_MAJOR,0);
|
|
cdevsw_add(&dev,&isdn_cdevsw,NULL);
|
|
isdn_devsw_installed = 1;
|
|
#ifdef DEVFS
|
|
{
|
|
int x;
|
|
/* default for a simple device with no probe routine (usually delete this) */
|
|
x=devfs_add_devsw(
|
|
/* path name devsw minor type uid gid perm*/
|
|
"/", "isdn", major(dev), 0, DV_CHR, 0, 0, 0600);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
SYSINIT(isdndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,isdn_drvinit,NULL)
|
|
|
|
#endif /* JREMOD */
|
|
|
|
#endif /* NISDN > 0 */
|