summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorphk <phk@FreeBSD.org>2004-09-28 19:33:49 +0000
committerphk <phk@FreeBSD.org>2004-09-28 19:33:49 +0000
commitd08ddc3f6b1f2a309026672172529e0e6b5c49ba (patch)
treeafd629dc9404f876695ee7e9634821d15b4fd059
parent0d1f936e786e73bd039b6c59438d854bdbe9be0c (diff)
downloadFreeBSD-src-d08ddc3f6b1f2a309026672172529e0e6b5c49ba.zip
FreeBSD-src-d08ddc3f6b1f2a309026672172529e0e6b5c49ba.tar.gz
Add functions to create and free the "tty-ness" of a serial port in a
generic way. This code will allow a similar amount of code to be removed from most if not all serial port drivers. Add generic cdevsw for tty devices. Add generic slave cdevsw for init/lock devices. Add ttypurge function which wakes up all know generic sleep points in the tty code, and calls into the hw-driver if it provides a method. Add ttycreate function which creates tty device and optionally cua device. In both cases .init/.lock devices are created as well. Change ttygone() slightly to also call the hw driver provided purge routine. Add ttyfree() which will purge and destroy the cdevs. Add ttyconsole mode for setting console friendly termios on a port.
-rw-r--r--sys/kern/tty.c239
-rw-r--r--sys/sys/tty.h7
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);
OpenPOWER on IntegriCloud