freebsd-skq/sys/kern/tty_snoop.c
Julian Elischer 87f6c6625d Pass 3 of the great devsw changes
most devsw referenced functions are now static, as they are
in the same file as their devsw structure. I've also added DEVFS
support for nearly every device in the system, however
many of the devices have 'incorrect' names under DEVFS
because I couldn't quickly work out the correct naming conventions.
(but devfs won't be coming on line for a month or so anyhow so that doesn't
matter)

If you "OWN" a device which would normally have an entry in /dev
then search for the devfs_add_devsw() entries and munge to make them right..
check out similar devices to see what I might have done in them in you
can't see what's going on..
for a laugh compare conf.c conf.h defore and after... :)
I have not doen DEVFS entries for any DISKSLICE devices yet as that will be
a much more complicated job.. (pass 5 :)

pass 4 will be to make the devsw tables of type (cdevsw * )
rather than (cdevsw)
seems to work here..
complaints to the usual places.. :)
1995-12-08 11:19:42 +00:00

542 lines
11 KiB
C

/*
* Copyright (c) 1995 Ugen J.S.Antsilevich
*
* Redistribution and use in source forms, with and without modification,
* are permitted provided that this entire comment appears intact.
*
* Redistribution in binary form may occur without any restrictions.
* Obviously, it would be nice if you gave credit where credit is due
* but requiring it would be too onerous.
*
* This software is provided ``AS IS'' without any warranties of any kind.
*
* Snoop stuff.
*/
#include "snp.h"
#if NSNP > 0
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/ioctl.h>
#include <sys/ioctl_compat.h> /* Oooh..We need O/NTTYDISC */
#include <sys/proc.h>
#define TTYDEFCHARS
#include <sys/tty.h>
#undef TTYDEFCHARS
#include <sys/file.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/malloc.h>
#ifdef DEVFS
#include <sys/devfsext.h>
#endif /*DEVFS*/
#include <sys/snoop.h>
static d_open_t snpopen;
static d_close_t snpclose;
static d_read_t snpread;
static d_write_t snpwrite;
static d_ioctl_t snpioctl;
static d_select_t snpselect;
#define CDEV_MAJOR 53
struct cdevsw snp_cdevsw =
{ snpopen, snpclose, snpread, snpwrite, /*53*/
snpioctl, nostop, nullreset, nodevtotty,/* snoop */
snpselect, nommap, NULL, "snp", NULL, -1 };
#ifndef MIN
#define MIN(a,b) (((a)<(b))?(a):(b))
#endif
static struct snoop snoopsw[NSNP];
static struct tty *devtotty __P((dev_t dev));
static struct tty *
devtotty (dev)
dev_t dev;
{
if (major(dev) > nchrdev)
return (NULL); /* no such device available */
return (*cdevsw[major(dev)].d_devtotty)(dev);
}
#define SNP_INPUT_BUF 5 /* This is even too much,the maximal
* interactive mode write is 3 bytes
* length for function keys...
*/
static int
snpwrite(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
int unit = minor(dev), len, i, error;
struct snoop *snp = &snoopsw[unit];
struct tty *tp;
char c[SNP_INPUT_BUF];
if (snp->snp_tty == NULL)
return (EIO);
tp = snp->snp_tty;
if ((tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
(tp->t_line == OTTYDISC || tp->t_line == NTTYDISC))
goto tty_input;
printf("Snoop: attempt to write to bad tty.\n");
return (EIO);
tty_input:
if (!(tp->t_state & TS_ISOPEN))
return (EIO);
while (uio->uio_resid > 0) {
len = MIN(uio->uio_resid,SNP_INPUT_BUF);
if ((error = uiomove(c, len, uio)) != 0)
return (error);
for (i=0;i<len;i++) {
if (ttyinput(c[i] , tp))
return (EIO);
}
}
return 0;
}
static int
snpread(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
int unit = minor(dev), s;
struct snoop *snp = &snoopsw[unit];
int len, n, nblen, error = 0;
caddr_t from;
char *nbuf;
#ifdef DIAGNOSTIC
if ((snp->snp_len + snp->snp_base) > snp->snp_blen)
panic("snoop buffer error");
#endif
if (snp->snp_tty == NULL)
return (EIO);
snp->snp_flags &= ~SNOOP_RWAIT;
do {
if (snp->snp_len == 0) {
if (snp->snp_flags & SNOOP_NBIO) {
return EWOULDBLOCK;
}
snp->snp_flags |= SNOOP_RWAIT;
tsleep((caddr_t) snp, (PZERO + 1) | PCATCH, "snoopread", 0);
}
} while (snp->snp_len == 0);
n = snp->snp_len;
while (snp->snp_len > 0 && uio->uio_resid > 0 && error == 0) {
len = MIN(uio->uio_resid, snp->snp_len);
from = (caddr_t) (snp->snp_buf + snp->snp_base);
if (len == 0)
break;
error = uiomove(from, len, uio);
snp->snp_base += len;
snp->snp_len -= len;
}
if ((snp->snp_flags & SNOOP_OFLOW) && (n < snp->snp_len)) {
snp->snp_flags &= ~SNOOP_OFLOW;
}
s = spltty();
nblen = snp->snp_blen;
if (((nblen / 2) >= SNOOP_MINLEN) && (nblen / 2) >= snp->snp_len) {
while (((nblen / 2) >= snp->snp_len) && ((nblen / 2) >= SNOOP_MINLEN))
nblen = nblen / 2;
if (nbuf = malloc(nblen, M_TTYS, M_NOWAIT)) {
bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
free(snp->snp_buf, M_TTYS);
snp->snp_buf = nbuf;
snp->snp_blen = nblen;
snp->snp_base = 0;
}
}
splx(s);
return error;
}
int
snpinc(snp, c)
struct snoop *snp;
char c;
{
char buf[1];
buf[0]=c;
return (snpin(snp,buf,1));
}
int
snpin(snp, buf, n)
struct snoop *snp;
char *buf;
int n;
{
int s_free, s_tail;
int s, len, nblen;
caddr_t from, to;
char *nbuf;
if (n == 0)
return 0;
#ifdef DIAGNOSTIC
if (n < 0)
panic("bad snoop char count");
if (!(snp->snp_flags & SNOOP_OPEN)) {
printf("Snoop: data coming to closed device.\n");
return 0;
}
#endif
if (snp->snp_flags & SNOOP_DOWN) {
printf("Snoop: more data to down interface.\n");
return 0;
}
if (snp->snp_flags & SNOOP_OFLOW) {
printf("Snoop: buffer overflow.\n");
/*
* On overflow we just repeat the standart close
* procedure...yes , this is waste of space but.. Then next
* read from device will fail if one would recall he is
* snooping and retry...
*/
return (snpdown(snp));
}
s_tail = snp->snp_blen - (snp->snp_len + snp->snp_base);
s_free = snp->snp_blen - snp->snp_len;
if (n > s_free) {
s = spltty();
nblen = snp->snp_blen;
while ((n > s_free) && ((nblen * 2) <= SNOOP_MAXLEN)) {
nblen = snp->snp_blen * 2;
s_free = nblen - (snp->snp_len + snp->snp_base);
}
if ((n <= s_free) && (nbuf = malloc(nblen, M_TTYS, M_NOWAIT))) {
bcopy(snp->snp_buf + snp->snp_base, nbuf, snp->snp_len);
free(snp->snp_buf, M_TTYS);
snp->snp_buf = nbuf;
snp->snp_blen = nblen;
snp->snp_base = 0;
} else {
snp->snp_flags |= SNOOP_OFLOW;
if (snp->snp_flags & SNOOP_RWAIT) {
snp->snp_flags &= ~SNOOP_RWAIT;
wakeup((caddr_t) snp);
}
splx(s);
return 0;
}
splx(s);
}
if (n > s_tail) {
from = (caddr_t) (snp->snp_buf + snp->snp_base);
to = (caddr_t) (snp->snp_buf);
len = snp->snp_len;
bcopy(from, to, len);
snp->snp_base = 0;
}
to = (caddr_t) (snp->snp_buf + snp->snp_base + snp->snp_len);
bcopy(buf, to, n);
snp->snp_len += n;
if (snp->snp_flags & SNOOP_RWAIT) {
snp->snp_flags &= ~SNOOP_RWAIT;
wakeup((caddr_t) snp);
}
selwakeup(&snp->snp_sel);
snp->snp_sel.si_pid = 0;
return n;
}
static int
snpopen(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
struct snoop *snp;
register int unit, error;
if (error = suser(p->p_ucred, &p->p_acflag))
return (error);
if ((unit = minor(dev)) >= NSNP)
return (ENXIO);
snp = &snoopsw[unit];
if (snp->snp_flags & SNOOP_OPEN)
return (ENXIO);
/*
* We intentionally do not OR flags with SNOOP_OPEN,but set them so
* all previous settings (especially SNOOP_OFLOW) will be cleared.
*/
snp->snp_flags = SNOOP_OPEN;
snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK);
snp->snp_blen = SNOOP_MINLEN;
snp->snp_base = 0;
snp->snp_len = 0;
/*
* snp_tty == NULL is for inactive snoop devices.
*/
snp->snp_tty = NULL;
snp->snp_target = -1;
return (0);
}
int
snp_detach(snp)
struct snoop *snp;
{
struct tty *tp;
snp->snp_base = 0;
snp->snp_len = 0;
/*
* If line disc. changed we do not touch this pointer,SLIP/PPP will
* change it anyway.
*/
if (snp->snp_tty == NULL)
goto detach_notty;
tp = snp->snp_tty;
if (tp && (tp->t_sc == snp) && (tp->t_state & TS_SNOOP) &&
(tp->t_line == OTTYDISC || tp->t_line == NTTYDISC)) {
tp->t_sc = NULL;
tp->t_state &= ~TS_SNOOP;
} else
printf("Snoop: bad attached tty data.\n");
snp->snp_tty = NULL;
snp->snp_target = -1;
detach_notty:
selwakeup(&snp->snp_sel);
snp->snp_sel.si_pid = 0;
return (0);
}
static int
snpclose(dev, flags, fmt, p)
dev_t dev;
int flags;
int fmt;
struct proc *p;
{
register int unit = minor(dev);
struct snoop *snp = &snoopsw[unit];
snp->snp_blen = 0;
free(snp->snp_buf, M_TTYS);
snp->snp_flags &= ~SNOOP_OPEN;
return (snp_detach(snp));
}
int
snpdown(snp)
struct snoop *snp;
{
snp->snp_blen = SNOOP_MINLEN;
free(snp->snp_buf, M_TTYS);
snp->snp_buf = malloc(SNOOP_MINLEN, M_TTYS, M_WAITOK);
snp->snp_flags |= SNOOP_DOWN;
return (snp_detach(snp));
}
static int
snpioctl(dev, cmd, data, flags, p)
dev_t dev;
int cmd;
caddr_t data;
int flags;
struct proc *p;
{
int unit = minor(dev), s;
dev_t tdev;
struct snoop *snp = &snoopsw[unit];
struct tty *tp, *tpo;
switch (cmd) {
case SNPSTTY:
tdev = *((dev_t *) data);
if (tdev == -1)
return (snpdown(snp));
tp = devtotty(tdev);
if (!tp)
return (EINVAL);
if ((tp->t_sc != (caddr_t) snp) && (tp->t_state & TS_SNOOP))
return (EBUSY);
if ((tp->t_line != OTTYDISC) && (tp->t_line != NTTYDISC))
return (EBUSY);
s = spltty();
if (snp->snp_target == -1) {
tpo = snp->snp_tty;
if (tpo)
tpo->t_state &= ~TS_SNOOP;
}
tp->t_sc = (caddr_t) snp;
tp->t_state |= TS_SNOOP;
snp->snp_tty = tp;
snp->snp_target = tdev;
/*
* Clean overflow and down flags -
* we'll have a chance to get them in the future :)))
*/
snp->snp_flags &= ~SNOOP_OFLOW;
snp->snp_flags &= ~SNOOP_DOWN;
splx(s);
break;
case SNPGTTY:
/*
* We keep snp_target field specially to make
* SNPGTTY happy,else we can't know what is device
* major/minor for tty.
*/
*((dev_t *) data) = snp->snp_target;
break;
case FIONBIO:
if (*(int *) data)
snp->snp_flags |= SNOOP_NBIO;
else
snp->snp_flags &= ~SNOOP_NBIO;
break;
case FIOASYNC:
if (*(int *) data)
snp->snp_flags |= SNOOP_ASYNC;
else
snp->snp_flags &= ~SNOOP_ASYNC;
break;
case FIONREAD:
s = spltty();
if (snp->snp_tty != NULL)
*(int *) data = snp->snp_len;
else
if (snp->snp_flags & SNOOP_DOWN) {
if (snp->snp_flags & SNOOP_OFLOW)
*(int *) data = SNP_OFLOW;
else
*(int *) data = SNP_TTYCLOSE;
} else {
*(int *) data = SNP_DETACH;
}
splx(s);
break;
default:
return (ENOTTY);
}
return (0);
}
static int
snpselect(dev, rw, p)
dev_t dev;
int rw;
struct proc *p;
{
int unit = minor(dev);
struct snoop *snp = &snoopsw[unit];
if (rw != FREAD)
return 1;
if (snp->snp_len > 0)
return 1;
/*
* If snoop is down,we don't want to select() forever so we return 1.
* Caller should see if we down via FIONREAD ioctl().The last should
* return -1 to indicate down state.
*/
if (snp->snp_flags & SNOOP_DOWN)
return 1;
selrecord(p, &snp->snp_sel);
return 0;
}
static void *snp_devfs_token[NSNP];
static snp_devsw_installed = 0;
static void
snp_drvinit(void *unused)
{
dev_t dev;
char name[32];
int i;
if( ! snp_devsw_installed ) {
dev = makedev(CDEV_MAJOR, 0);
cdevsw_add(&dev,&snp_cdevsw, NULL);
snp_devsw_installed = 1;
#ifdef DEVFS
for ( i = 0 ; i < NSNP ; i++) {
sprintf(name,"snp%d",i);
snp_devfs_token[i] =
devfs_add_devsw( "/", name, &snp_cdevsw, i,
DV_CHR, 0, 0, 0600);
}
#endif
}
}
SYSINIT(snpdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,snp_drvinit,NULL)
#endif