freebsd-nq/sys/gnu/isdn/isdn.c
1995-12-17 21:17:48 +00:00

661 lines
12 KiB
C

static char _isdnid[] = "@(#)$Id: isdn.c,v 1.11 1995/12/10 15:54:17 bde Exp $";
/*******************************************************************************
* II - Version 0.1 $Revision: 1.11 $ $State: Exp $
*
* Copyright 1994 Dietmar Friede
*******************************************************************************
* Bug reports, patches, comments, suggestions should be sent to:
*
* jkr@saarlink.de or jkrause@guug.de
*
*******************************************************************************
*/
/*
* 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>
#ifdef DEVFS
#include <sys/devfsext.h>
#endif /*DEVFS*/
#include "gnu/isdn/isdn_ioctl.h"
isdn_appl_t isdn_appl[N_ISDN_APPL];
isdn_ctrl_t isdn_ctrl[N_ISDN_CTRL];
static int Isdn_Appl, Isdn_Ctrl, Isdn_Typ;
static void isdn_attach __P((void));
static timeout_t isdn_check;
static char *isdn_get_prot __P((int ap, int dir));
static int isdn_get_prot_size __P((int ap));
static int isdn_set_prot __P((int ap, int dir, char *p));
static int isdn_stat __P((int cn));
static void passout __P((int unit, int l, char *buf));
static d_open_t isdnopen;
static d_close_t isdnclose;
static d_read_t isdnread;
static d_write_t isdnwrite;
static d_ioctl_t isdnioctl;
#define CDEV_MAJOR 55
static struct cdevsw isdn_cdevsw =
{ isdnopen, isdnclose, isdnread, nowrite, /*55*/
isdnioctl, nostop, nullreset, nodevtotty,/* isdn */
seltrue, nommap, NULL, "isdn", NULL, -1 };
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;
static int
isdn_get_prot_size(int ap)
{
return (prot_size[isdn_appl[ap].prot]);
}
static char *
isdn_get_prot(int ap, int dir)
{
if(dir)
return(activ[isdn_appl[ap].prot]);
return(passiv[isdn_appl[ap].prot]);
}
static 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);
}
static 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;
#ifdef DEVFS
/*SOMETHING GOES IN HERE I THINK*/
#endif
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
*/
static 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);
}
static int
isdnclose(dev_t dev, int flags, int fmt, struct proc *p)
{
o_flags &= ~(1 << minor(dev));
return (0);
}
static 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;
}
static 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);
}
static 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);
}
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;
}
}
SYSINIT(isdndev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,isdn_drvinit,NULL)
#endif /* NISDN > 0 */