diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/kern/tty.c | 239 | ||||
-rw-r--r-- | sys/sys/tty.h | 7 |
2 files changed, 240 insertions, 6 deletions
diff --git a/sys/kern/tty.c b/sys/kern/tty.c index dfa9490..191d45a 100644 --- a/sys/kern/tty.c +++ b/sys/kern/tty.c @@ -106,6 +106,8 @@ __FBSDID("$FreeBSD$"); #include <sys/sysctl.h> #include <sys/timepps.h> +#include <machine/stdarg.h> + #include <vm/vm.h> #include <vm/pmap.h> #include <vm/vm_map.h> @@ -117,6 +119,35 @@ long tk_nin; long tk_nout; long tk_rawcc; +static d_open_t ttysopen; +static d_close_t ttysclose; +static d_read_t ttysrdwr; +static d_ioctl_t ttysioctl; +static d_purge_t ttypurge; + +/* Default cdevsw for common tty devices */ +static struct cdevsw tty_cdevsw = { + .d_version = D_VERSION, + .d_open = ttyopen, + .d_close = ttyclose, + .d_ioctl = ttyioctl, + .d_purge = ttypurge, + .d_name = "ttydrv", + .d_flags = D_TTY | D_NEEDGIANT, +}; + +/* Cdevsw for slave tty devices */ +static struct cdevsw ttys_cdevsw = { + .d_version = D_VERSION, + .d_open = ttysopen, + .d_close = ttysclose, + .d_read = ttysrdwr, + .d_write = ttysrdwr, + .d_ioctl = ttysioctl, + .d_name = "TTYS", + .d_flags = D_TTY | D_NEEDGIANT, +}; + static int proc_compare(struct proc *p1, struct proc *p2); static int ttnread(struct tty *tp); static void ttyecho(int c, struct tty *tp); @@ -2723,6 +2754,8 @@ ttysleep(struct tty *tp, void *chan, int pri, char *wmesg, int timo) gen = tp->t_gen; error = tsleep(chan, pri, wmesg, timo); + if (tp->t_state & TS_GONE) + return (ENXIO); if (error) return (error); return (tp->t_gen == gen ? 0 : ERESTART); @@ -2832,6 +2865,129 @@ ttyalloc() return (ttymalloc(NULL)); } +static void +ttypurge(struct cdev *dev) +{ + + if (dev->si_tty == NULL) + return; + ttygone(dev->si_tty); +} + +/* + * ttycreate() + * + * Create the device entries for this tty thereby opening it for business. + * + * The flags argument controls if "cua" units are created. + * + * The t_sc filed is copied to si_drv1 in the created cdevs. This + * is particularly important for ->t_cioctl() users. + * + * XXX: implement the init and lock devices by cloning. + */ + +int +ttycreate(struct tty *tp, struct cdevsw *csw, int unit, int flags, const char *fmt, ...) +{ + char namebuf[SPECNAMELEN - 3]; /* XXX space for "tty" */ + va_list ap; + struct cdev *cp; + int i, minor; + + if (csw == NULL) + csw = &tty_cdevsw; + KASSERT(csw->d_purge == NULL || csw->d_purge == ttypurge, + ("tty should not have d_purge")); + + csw->d_purge = ttypurge; + + minor = unit2minor(unit); + va_start(ap, fmt); + i = vsnrprintf(namebuf, sizeof namebuf, 32, fmt, ap); + va_end(ap); + KASSERT(i < sizeof namebuf, ("Too long tty name (%s)", namebuf)); + + cp = make_dev(csw, minor, + UID_ROOT, GID_WHEEL, 0600, "tty%s", namebuf); + tp->t_dev = cp; + tp->t_mdev = cp; + cp->si_tty = tp; + cp->si_drv1 = tp->t_sc; + + cp = make_dev(&ttys_cdevsw, minor | MINOR_INIT, + UID_ROOT, GID_WHEEL, 0600, "tty%s.init", namebuf); + dev_depends(tp->t_dev, cp); + cp->si_drv1 = tp->t_sc; + cp->si_drv2 = &tp->t_init_in; + cp->si_tty = tp; + + cp = make_dev(&ttys_cdevsw, minor | MINOR_LOCK, + UID_ROOT, GID_WHEEL, 0600, "tty%s.lock", namebuf); + dev_depends(tp->t_dev, cp); + cp->si_drv1 = tp->t_sc; + cp->si_drv2 = &tp->t_lock_in; + cp->si_tty = tp; + + if (flags & MINOR_CALLOUT) { + cp = make_dev(csw, minor | MINOR_CALLOUT, + UID_UUCP, GID_DIALER, 0660, "cua%s", namebuf); + dev_depends(tp->t_dev, cp); + cp->si_drv1 = tp->t_sc; + cp->si_tty = tp; + + cp = make_dev(&ttys_cdevsw, minor | MINOR_CALLOUT | MINOR_INIT, + UID_UUCP, GID_DIALER, 0660, "cua%s.init", namebuf); + dev_depends(tp->t_dev, cp); + cp->si_drv1 = tp->t_sc; + cp->si_drv2 = &tp->t_init_out; + cp->si_tty = tp; + + cp = make_dev(&ttys_cdevsw, minor | MINOR_CALLOUT | MINOR_LOCK, + UID_UUCP, GID_DIALER, 0660, "cua%s.lock", namebuf); + dev_depends(tp->t_dev, cp); + cp->si_drv1 = tp->t_sc; + cp->si_drv2 = &tp->t_lock_out; + cp->si_tty = tp; + } + + return (0); +} + +/* + * This function is called when the hardware disappears. We set a flag + * and wake up stuff so all sleeping threads will notice. + */ +void +ttygone(struct tty *tp) +{ + + tp->t_state |= TS_GONE; + wakeup(&tp->t_dtr_wait); + wakeup(TSA_CARR_ON(tp)); + wakeup(TSA_HUP_OR_INPUT(tp)); + wakeup(TSA_OCOMPLETE(tp)); + wakeup(TSA_OLOWAT(tp)); + if (tp->t_purge != NULL) + tp->t_purge(tp); +} + +/* + * ttyfree() + * + * Called when the driver is ready to free the tty structure. + * + * XXX: This shall sleep until all threads have left the driver. + */ + +void +ttyfree(struct tty *tp) +{ + + ttygone(tp); + destroy_dev(tp->t_mdev); +} + static int sysctl_kern_ttys(SYSCTL_HANDLER_ARGS) { @@ -3098,16 +3254,87 @@ ttydtrwaitsleep(struct tty *tp) return (error); } +static int +ttysopen(struct cdev *dev, int flag, int mode, struct thread *td) +{ + struct tty *tp; + + tp = dev->si_tty; + KASSERT(tp != NULL, + ("ttysopen(): no tty pointer on device (%s)", devtoname(dev))); + if (tp->t_state & TS_GONE) + return (ENODEV); + return (0); +} + +static int +ttysclose(struct cdev *dev, int flag, int mode, struct thread *td) +{ + + return (0); +} + +static int +ttysrdwr(struct cdev *dev, struct uio *uio, int flag) +{ + + return (ENODEV); +} + +static int +ttysioctl(struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) +{ + struct tty *tp; + int error; + struct termios *ct; + + tp = dev->si_tty; + KASSERT(tp != NULL, + ("ttysopen(): no tty pointer on device (%s)", devtoname(dev))); + if (tp->t_state & TS_GONE) + return (ENODEV); + ct = dev->si_drv2; + switch (cmd) { + case TIOCSETA: + error = suser(td); + if (error != 0) + return (error); + *ct = *(struct termios *)data; + return (0); + case TIOCGETA: + *(struct termios *)data = *ct; + return (0); + case TIOCGETD: + *(int *)data = TTYDISC; + return (0); + case TIOCGWINSZ: + bzero(data, sizeof(struct winsize)); + return (0); + default: + if (tp->t_cioctl != NULL) + return(tp->t_cioctl(dev, cmd, data, flag, td)); + return (ENOTTY); + } +} + /* - * This function is called when the hardware disappears. We set a flag - * and wake up stuff so all sleeping threads will notice. + * Use more "normal" termios paramters for consoles. */ -void -ttygone(struct tty *tp) +void +ttyconsolemode(struct tty *tp, int speed) { - tp->t_state |= TS_GONE; - wakeup(&tp->t_dtr_wait); + tp->t_init_in.c_iflag = TTYDEF_IFLAG; + tp->t_init_in.c_oflag = TTYDEF_OFLAG; + tp->t_init_in.c_cflag = TTYDEF_CFLAG | CLOCAL; + tp->t_init_in.c_lflag = TTYDEF_LFLAG | ECHO | ECHOE | ECHOKE | ECHOCTL; + tp->t_lock_out.c_cflag = tp->t_lock_in.c_cflag = CLOCAL; + if (speed == 0) + speed = TTYDEF_SPEED; + tp->t_lock_out.c_ispeed = tp->t_lock_out.c_ospeed = + tp->t_lock_in.c_ispeed = tp->t_lock_in.c_ospeed = + tp->t_init_in.c_ispeed = tp->t_init_in.c_ospeed = speed; + tp->t_init_out = tp->t_init_in; } /* diff --git a/sys/sys/tty.h b/sys/sys/tty.h index fdbdc72..0054a42 100644 --- a/sys/sys/tty.h +++ b/sys/sys/tty.h @@ -68,10 +68,12 @@ struct clist { struct tty; struct pps_state; struct cdev; +struct cdevsw; typedef int t_open_t(struct tty *, struct cdev *); typedef void t_close_t(struct tty *); typedef void t_oproc_t(struct tty *); +typedef void t_purge_t(struct tty *); typedef void t_stop_t(struct tty *, int); typedef int t_param_t(struct tty *, struct termios *); typedef int t_modem_t(struct tty *, int, int); @@ -99,6 +101,7 @@ struct tty { long t_outcc; /* Output queue statistics. */ int t_line; /* Interface to device drivers. */ struct cdev *t_dev; /* Device. */ + struct cdev *t_mdev; /* Device. */ int t_state; /* Device and driver (TS*) state. */ int t_flags; /* Tty flags. */ int t_timeout; /* Timeout for ttywait() */ @@ -145,6 +148,7 @@ struct tty { t_break_t *t_break; /* Set break state (optional). */ t_ioctl_t *t_ioctl; /* Set ioctl handling (optional). */ t_open_t *t_open; /* First open */ + t_purge_t *t_purge; /* Purge threads */ t_close_t *t_close; /* Last close */ __d_ioctl_t *t_cioctl; /* Ioctl on control devices */ }; @@ -343,10 +347,13 @@ struct tty *ttyalloc(void); void ttyblock(struct tty *tp); void ttychars(struct tty *tp); int ttycheckoutq(struct tty *tp, int wait); +void ttyconsolemode(struct tty *tp, int speed); int tty_close(struct tty *tp); +int ttycreate(struct tty *tp, struct cdevsw *, int unit, int flags, const char *fmt, ...) __printflike(5, 6); int ttydtrwaitsleep(struct tty *tp); void ttydtrwaitstart(struct tty *tp); void ttyflush(struct tty *tp, int rw); +void ttyfree(struct tty *tp); void ttygone(struct tty *tp); void ttyinfo(struct tty *tp); int ttyinput(int c, struct tty *tp); |