freebsd-dev/sys/gnu/isdn/iitty.c

363 lines
7.1 KiB
C
Raw Normal View History

/* @(#)$Id: iitty.c,v 1.21 1996/04/02 22:06:23 gpalmer Exp $
*******************************************************************************
* II - Version 0.1 $Revision: 1.21 $ $State: Exp $
*
* Copyright 1994 Dietmar Friede
*******************************************************************************
* Bug reports, patches, comments, suggestions should be sent to:
*
* jkr@saarlink.de or jkrause@guug.de
*
*******************************************************************************
1995-12-17 21:17:48 +00:00
*/
#include "ity.h"
#if NITY > 0
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/conf.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/tty.h>
#include <sys/proc.h>
#include <sys/file.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/syslog.h>
#include <sys/types.h>
#ifdef DEVFS
#include <sys/devfsext.h>
#endif /*DEVFS*/
#include "gnu/isdn/isdn_ioctl.h"
static d_open_t ityopen;
static d_close_t ityclose;
static d_read_t ityread;
static d_write_t itywrite;
static d_ioctl_t ityioctl;
static d_stop_t itystop;
static d_devtotty_t itydevtotty;
#define CDEV_MAJOR 56
static struct cdevsw ity_cdevsw =
{ ityopen, ityclose, ityread, itywrite, /*56*/
ityioctl, itystop, noreset, itydevtotty,/* ity */
ttselect, nommap, NULL, "ity", NULL, -1 };
1995-12-17 21:17:48 +00:00
static int ityparam __P((struct tty *tp, struct termios *t));
static void itystart __P((struct tty *tp));
1995-12-17 21:17:48 +00:00
static int itydefaultrate = 64000;
static short ity_addr[NITY];
static struct tty ity_tty[NITY];
static int applnr[NITY];
static int next_if= 0;
#ifdef DEVFS
void *devfs_token[NITY];
1995-12-17 21:17:48 +00:00
static void *devfs_token_out[NITY];
#endif
#define UNIT(x) (minor(x)&0x3f)
#define OUTBOUND(x) ((minor(x)&0x80)==0x80)
int
ityattach(int ap)
{
if(next_if >= NITY)
return(-1);
applnr[next_if]= ap;
#ifdef DEVFS
devfs_token[next_if] =
devfs_add_devswf(&ity_cdevsw, next_if, DV_CHR, 0, 0,
0600, "isdn/ity%d", next_if);
devfs_token_out[next_if] =
devfs_add_devswf(&ity_cdevsw,(next_if | 0x80), DV_CHR, 0, 0,
0600, "isdn/Oity%d", next_if);
#endif
return(next_if++);
}
/* ARGSUSED */
static int
ityopen(dev_t dev, int flag, int mode, struct proc * p)
{
register struct tty *tp;
register int unit;
int error = 0;
unit = UNIT(dev);
if (unit >= next_if)
return (ENXIO);
tp = &ity_tty[unit];
tp->t_oproc = itystart;
tp->t_param = ityparam;
tp->t_dev = dev;
if ((tp->t_state & TS_ISOPEN) == 0)
{
ttychars(tp);
if (tp->t_ispeed == 0)
{
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_cflag = TTYDEF_CFLAG;
tp->t_lflag = TTYDEF_LFLAG;
tp->t_ispeed = tp->t_ospeed = itydefaultrate;
}
ityparam(tp, &tp->t_termios);
ttsetwater(tp);
} else if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0)
return (EBUSY);
(void) spltty();
Obtained from: partly from ancient patches of mine via 1.1.5 Introduce TS_CONNECTED and TS_ZOMBIE states. TS_CONNECTED is set while a connection is established. It is set while (TS_CARR_ON or CLOCAL is set) and TS_ZOMBIE is clear. TS_ZOMBIE is set for on to off transitions of TS_CARR_ON that occur when CLOCAL is clear and is cleared for off to on transitions of CLOCAL. I/o can only occur while TS_CONNECTED is set. TS_ZOMBIE prevents further i/o. Split the input-event sleep address TSA_CARR_ON(tp) into TSA_CARR_ON(tp) and TSA_HUP_OR_INPUT(tp). The former address is now used only for off to on carrier transitions and equivalent CLOCAL transitions. The latter is used for all input events, all carrier transitions and certain CLOCAL transitions. There are some harmless extra wakeups for rare connection- related events. Previously there were too many extra wakeups for non-rare input events. Drivers now call l_modem() instead of setting TS_CARR_ON directly to handle even the initial off to on transition of carrier. They should always have done this. l_modem() now handles TS_CONNECTED and TS_ZOMBIE as well as TS_CARR_ON. gnu/isdn/iitty.c: Set TS_CONNECTED for first open ourself to go with bogusly setting CLOCAL. i386/isa/syscons.c, i386/isa/pcvt/pcvt_drv.c: We fake carrier, so don't also fake CLOCAL. kern/tty.c: Testing TS_CONNECTED instead of TS_CARR_ON fixes TIOCCONS forgetting to test CLOCAL. TS_ISOPEN was tested instead, but that broke when we disabled the clearing of TS_ISOPEN for certain transitions of CLOCAL. Testing TS_CONNECTED fixes ttyselect() returning false success for output to devices in state !TS_CARR_ON && !CLOCAL. Optimize the other selwakeup() call (this is not related to the other changes). kern/tty_pty.c: ptcopen() can be declared in traditional C now that dev_t isn't short.
1995-07-31 21:02:00 +00:00
if (OUTBOUND(dev)) {
/*
* XXX should call l_modem() here and not meddle with CLOCAL,
* but itystart() wants TS_CARR_ON to give the true carrier.
*/
tp->t_cflag |= CLOCAL;
tp->t_state |= TS_CONNECTED;
}
while ((flag & O_NONBLOCK) == 0 && (tp->t_cflag & CLOCAL) == 0 &&
(tp->t_state & TS_CARR_ON) == 0)
{
error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "iidcd", 0);
if (error)
break;
}
(void) spl0();
if (error == 0)
error = (*linesw[tp->t_line].l_open) (dev, tp);
return (error);
}
/* ARGSUSED */
static int
ityclose(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
register struct tty *tp;
register ity;
register int unit;
unit = UNIT(dev);
ity = ity_addr[unit];
if(tp = &ity_tty[unit])
(*linesw[tp->t_line].l_close) (tp, flag);
ttyclose(tp);
isdn_disconnect(applnr[unit],0);
return (0);
}
static int
ityread(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
register struct tty *tp = &ity_tty[UNIT(dev)];
return ((*linesw[tp->t_line].l_read) (tp, uio, flag));
}
static int
itywrite(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
int unit = UNIT(dev);
register struct tty *tp = &ity_tty[unit];
return ((*linesw[tp->t_line].l_write) (tp, uio, flag));
}
int
ity_input(int no, int len, char *buf, int dir)
{
register struct tty *tp = &ity_tty[no];
int i;
if (tp->t_state & TS_ISOPEN)
for(i= 0; i<len; i++)
(*linesw[tp->t_line].l_rint)(buf[i], tp);
else len= 0;
return(len);
}
1995-12-17 21:17:48 +00:00
static void
itystart(struct tty *tp)
{
int s, unit;
unit = UNIT(tp->t_dev);
s = splhigh();
if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP))
{
splx(s);
return;
}
ttwwakeup(tp);
if (tp->t_outq.c_cc)
{
if(OUTBOUND(tp->t_dev) && (tp->t_cflag & CLOCAL) &&
((tp->t_state & TS_CARR_ON) == 0))
isdn_msg(applnr[unit]);
else isdn_output(applnr[unit]);
tp->t_state |= TS_BUSY;
}
splx(s);
}
int
ity_out(int no, char *buf, int len)
{
struct tty *tp = &ity_tty[no];
int i;
if(tp == NULL)
return(0);
if(tp->t_outq.c_cc)
{
for (i = 0; i < len && tp->t_outq.c_cc; ++i)
buf[i]= getc(&tp->t_outq);
return(i);
}
tp->t_state &=~ (TS_BUSY|TS_FLUSH);
if (tp->t_line)
(*linesw[tp->t_line].l_start)(tp);
else
itystart(tp);
return(0);
}
void
ity_connect(int no)
{
struct tty *tp = &ity_tty[no];
if(tp == NULL)
return;
if(OUTBOUND(tp->t_dev)) tp->t_cflag &= ~CLOCAL;
(*linesw[tp->t_line].l_modem) (tp, 1);
tp->t_state &=~ (TS_BUSY|TS_FLUSH);
if (tp->t_line)
(*linesw[tp->t_line].l_start)(tp);
else
itystart(tp);
}
void
ity_disconnect(int no)
{
struct tty *tp = &ity_tty[no];
if(tp) (*linesw[tp->t_line].l_modem) (tp, 0);
}
static int
ityioctl(dev, cmd, data, flag,p)
dev_t dev;
int cmd;
caddr_t data;
int flag;
struct proc *p;
{
register struct tty *tp;
register int unit = UNIT(dev);
register int error;
tp = &ity_tty[unit];
error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, data, flag,p);
if (error >= 0)
return (error);
error = ttioctl(tp, cmd, data, flag);
if (error >= 0)
return (error);
switch (cmd)
{
default:
return (ENOTTY);
}
return (0);
}
1995-12-17 21:17:48 +00:00
static int
ityparam(tp, t)
register struct tty *tp;
register struct termios *t;
{
register int cflag = t->c_cflag;
int unit = UNIT(tp->t_dev);
int ospeed = t->c_ospeed;
/* check requested parameters */
if (ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed))
return (EINVAL);
/* and copy to tty */
tp->t_ispeed = t->c_ispeed;
tp->t_ospeed = t->c_ospeed;
tp->t_cflag = cflag;
if (ospeed == 0)
{
isdn_disconnect(applnr[unit],0);
return (0);
}
return (0);
}
/*
* Stop output on a line.
*/
/* ARGSUSED */
static void
itystop(struct tty *tp, int flag)
{
register int s;
s = splhigh();
if (tp->t_state & TS_BUSY)
{
if ((tp->t_state & TS_TTSTOP) == 0)
tp->t_state |= TS_FLUSH;
}
splx(s);
}
static struct tty *
itydevtotty(dev_t dev)
{
register int unit = UNIT(dev);
if (unit >= next_if)
return (NULL);
return (&ity_tty[unit]);
}
static ity_devsw_installed = 0;
static void
ity_drvinit(void *unused)
{
dev_t dev;
if( ! ity_devsw_installed ) {
dev = makedev(CDEV_MAJOR, 0);
cdevsw_add(&dev,&ity_cdevsw, NULL);
ity_devsw_installed = 1;
}
}
SYSINIT(itydev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,ity_drvinit,NULL)
#endif