diff options
Diffstat (limited to 'sys/dev/nmdm/nmdm.c')
-rw-r--r-- | sys/dev/nmdm/nmdm.c | 412 |
1 files changed, 159 insertions, 253 deletions
diff --git a/sys/dev/nmdm/nmdm.c b/sys/dev/nmdm/nmdm.c index 8536c18..706d416 100644 --- a/sys/dev/nmdm/nmdm.c +++ b/sys/dev/nmdm/nmdm.c @@ -36,9 +36,6 @@ __FBSDID("$FreeBSD$"); * Mighty handy for use with serial console in Vmware */ -#include "opt_compat.h" -#include "opt_tty.h" - #include <sys/param.h> #include <sys/systm.h> #include <sys/priv.h> @@ -48,252 +45,176 @@ __FBSDID("$FreeBSD$"); #include <sys/fcntl.h> #include <sys/poll.h> #include <sys/kernel.h> +#include <sys/limits.h> #include <sys/module.h> #include <sys/serial.h> #include <sys/signalvar.h> #include <sys/malloc.h> #include <sys/taskqueue.h> -MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures"); - -static d_close_t nmdmclose; -static t_modem_t nmdmmodem; -static d_open_t nmdmopen; -static t_oproc_t nmdmoproc; -static t_param_t nmdmparam; -static t_stop_t nmdmstop; - -static struct cdevsw nmdm_cdevsw = { - .d_version = D_VERSION, - .d_open = nmdmopen, - .d_close = nmdmclose, - .d_name = "nmdn", - .d_flags = D_TTY | D_PSEUDO | D_NEEDGIANT | D_NEEDMINOR, +MALLOC_DEFINE(M_NMDM, "nullmodem", "nullmodem data structures"); + +static tsw_inwakeup_t nmdm_outwakeup; +static tsw_outwakeup_t nmdm_inwakeup; +static tsw_param_t nmdm_param; +static tsw_modem_t nmdm_modem; + +static struct ttydevsw nmdm_class = { + .tsw_flags = TF_NOPREFIX, + .tsw_inwakeup = nmdm_inwakeup, + .tsw_outwakeup = nmdm_outwakeup, + .tsw_param = nmdm_param, + .tsw_modem = nmdm_modem, }; -#define BUFSIZ 100 /* Chunk size iomoved to/from user */ -#define NMDM_MAX_NUM 128 /* Artificially limit # devices. */ -#define PF_STOPPED 0x10 /* user told stopped */ -#define BFLAG CLONE_FLAG0 - -struct softpart { - struct tty *nm_tty; - struct cdev *dev; - int nm_dcd; - struct task pt_task; - struct softpart *other; - struct callout co; - u_long quota; - u_long accumulator; - int rate; - int credits; +static void nmdm_task_tty(void *, int); + +struct nmdmsoftc; + +struct nmdmpart { + struct tty *np_tty; + int np_dcd; + struct task np_task; + struct nmdmpart *np_other; + struct nmdmsoftc *np_pair; + struct callout np_callout; + u_long np_quota; + u_long np_accumulator; + int np_rate; + int np_credits; #define QS 8 /* Quota shift */ }; -struct nm_softc { - TAILQ_ENTRY(nm_softc) pt_list; - int pt_flags; - struct softpart part1, part2; - struct prison *pt_prison; +struct nmdmsoftc { + struct nmdmpart ns_part1; + struct nmdmpart ns_part2; + struct mtx ns_mtx; }; -static struct clonedevs *nmdmclones; -static TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); +static int nmdm_count = 0; + +static struct nmdmsoftc * +nmdm_alloc(unsigned long unit) +{ + struct nmdmsoftc *ns; + struct tty *tp; + + atomic_add_acq_int(&nmdm_count, 1); + + ns = malloc(sizeof(*ns), M_NMDM, M_WAITOK|M_ZERO); + mtx_init(&ns->ns_mtx, "nmdm", NULL, MTX_DEF); + + /* Hook the pairs together. */ + ns->ns_part1.np_pair = ns; + ns->ns_part1.np_other = &ns->ns_part2; + TASK_INIT(&ns->ns_part1.np_task, 0, nmdm_task_tty, &ns->ns_part1); + callout_init(&ns->ns_part1.np_callout, 0); + + ns->ns_part2.np_pair = ns; + ns->ns_part2.np_other = &ns->ns_part1; + TASK_INIT(&ns->ns_part2.np_task, 0, nmdm_task_tty, &ns->ns_part2); + callout_init(&ns->ns_part2.np_callout, 0); + + /* Create device nodes. */ + tp = ns->ns_part1.np_tty = tty_alloc(&nmdm_class, &ns->ns_part1, + &ns->ns_mtx); + tty_makedev(tp, NULL, "nmdm%luA", unit); + + tp = ns->ns_part2.np_tty = tty_alloc(&nmdm_class, &ns->ns_part2, + &ns->ns_mtx); + tty_makedev(tp, NULL, "nmdm%luB", unit); + + return (ns); +} static void nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen, struct cdev **dev) { - int i, unit; - char *p; - struct cdev *d1, *d2; + unsigned long unit; + char *end; + struct nmdmsoftc *ns; if (*dev != NULL) return; - if (strcmp(name, "nmdm") == 0) { - p = NULL; - unit = -1; - } else { - i = dev_stdclone(name, &p, "nmdm", &unit); - if (i == 0) - return; - if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') - return; - else if (p[0] != '\0' && p[1] != '\0') - return; - } - i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); - if (i) { - d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), - 0, 0, 0666, "nmdm%dA", unit); - if (d1 == NULL) - return; - d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, - 0, 0, 0666, "nmdm%dB", unit); - if (d2 == NULL) { - destroy_dev(d1); - return; - } - d2->si_drv2 = d1; - d1->si_drv2 = d2; - dev_depends(d1, d2); - dev_depends(d2, d1); - d1->si_flags |= SI_CHEAPCLONE; - d2->si_flags |= SI_CHEAPCLONE; - } - if (p != NULL && p[0] == 'B') - *dev = d1->si_drv2; + if (strncmp(name, "nmdm", 4) != 0) + return; + + /* Device name must be "nmdm%lu%c", where %c is 'A' or 'B'. */ + name += 4; + unit = strtoul(name, &end, 10); + if (unit == ULONG_MAX || name == end) + return; + if ((end[0] != 'A' && end[0] != 'B') || end[1] != '\0') + return; + + /* XXX: pass privileges? */ + ns = nmdm_alloc(unit); + + if (end[0] == 'A') + *dev = ns->ns_part1.np_tty->t_dev; else - *dev = d1; - dev_ref(*dev); + *dev = ns->ns_part2.np_tty->t_dev; } static void nmdm_timeout(void *arg) { - struct softpart *sp; - - sp = arg; + struct nmdmpart *np = arg; - if (sp->rate == 0) + if (np->np_rate == 0) return; /* * Do a simple Floyd-Steinberg dither here to avoid FP math. * Wipe out unused quota from last tick. */ - sp->accumulator += sp->credits; - sp->quota = sp->accumulator >> QS; - sp->accumulator &= ((1 << QS) - 1); + np->np_accumulator += np->np_credits; + np->np_quota = np->np_accumulator >> QS; + np->np_accumulator &= ((1 << QS) - 1); - taskqueue_enqueue(taskqueue_swi_giant, &sp->pt_task); - callout_reset(&sp->co, sp->rate, nmdm_timeout, arg); + taskqueue_enqueue(taskqueue_swi, &np->np_task); + callout_reset(&np->np_callout, np->np_rate, nmdm_timeout, np); } static void nmdm_task_tty(void *arg, int pending __unused) { struct tty *tp, *otp; - struct softpart *sp; - int c; + struct nmdmpart *np = arg; + char c; - tp = arg; - sp = tp->t_sc; - otp = sp->other->nm_tty; + tp = np->np_tty; + tty_lock(tp); + + otp = np->np_other->np_tty; KASSERT(otp != NULL, ("NULL otp in nmdmstart")); KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); - if (sp->other->nm_dcd) { - if (!(tp->t_state & TS_ISOPEN)) { - sp->other->nm_dcd = 0; - (void)ttyld_modem(otp, 0); + if (np->np_other->np_dcd) { + if (!tty_opened(tp)) { + np->np_other->np_dcd = 0; + ttydisc_modem(otp, 0); } } else { - if (tp->t_state & TS_ISOPEN) { - sp->other->nm_dcd = 1; - (void)ttyld_modem(otp, 1); + if (tty_opened(tp)) { + np->np_other->np_dcd = 1; + ttydisc_modem(otp, 1); } } - if (tp->t_state & TS_TTSTOP) - return; - while (tp->t_outq.c_cc != 0) { - if (sp->rate && !sp->quota) - return; - if (otp->t_state & TS_TBLOCK) - return; - sp->quota--; - c = getc(&tp->t_outq); - if (otp->t_state & TS_ISOPEN) - ttyld_rint(otp, c); - } - if (tp->t_outq.c_cc == 0) - ttwwakeup(tp); -} - -/* - * This function creates and initializes a pair of ttys. - */ -static void -nmdminit(struct cdev *dev1) -{ - struct cdev *dev2; - struct nm_softc *pt; - - dev2 = dev1->si_drv2; - - dev1->si_flags &= ~SI_CHEAPCLONE; - dev2->si_flags &= ~SI_CHEAPCLONE; - - pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); - TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); - - dev1->si_drv1 = dev2->si_drv1 = pt; - - pt->part1.dev = dev1; - pt->part2.dev = dev2; - - pt->part1.nm_tty = ttyalloc(); - pt->part1.nm_tty->t_oproc = nmdmoproc; - pt->part1.nm_tty->t_stop = nmdmstop; - pt->part1.nm_tty->t_modem = nmdmmodem; - pt->part1.nm_tty->t_param = nmdmparam; - pt->part1.nm_tty->t_dev = dev1; - pt->part1.nm_tty->t_sc = &pt->part1; - TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty); - callout_init(&pt->part1.co, 0); - - pt->part2.nm_tty = ttyalloc(); - pt->part2.nm_tty->t_oproc = nmdmoproc; - pt->part2.nm_tty->t_stop = nmdmstop; - pt->part2.nm_tty->t_modem = nmdmmodem; - pt->part2.nm_tty->t_param = nmdmparam; - pt->part2.nm_tty->t_dev = dev2; - pt->part2.nm_tty->t_sc = &pt->part2; - TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty); - callout_init(&pt->part2.co, 0); - - pt->part1.other = &pt->part2; - pt->part2.other = &pt->part1; - - dev1->si_tty = pt->part1.nm_tty; - dev1->si_drv1 = pt; - - dev2->si_tty = pt->part2.nm_tty; - dev2->si_drv1 = pt; -} - -/* - * Device opened from userland - */ -static int -nmdmopen(struct cdev *dev, int flag, int devtype, struct thread *td) -{ - struct tty *tp, *tp2; - int error; - struct nm_softc *pti; - struct softpart *sp; - - if (dev->si_drv1 == NULL) - nmdminit(dev); - pti = dev->si_drv1; - if (pti->pt_prison != td->td_ucred->cr_prison) - return (EBUSY); - - tp = dev->si_tty; - sp = tp->t_sc; - tp2 = sp->other->nm_tty; - - if ((tp->t_state & TS_ISOPEN) == 0) { - ttyinitmode(tp, 0, 0); - ttsetwater(tp); /* XXX ? */ - } else if (tp->t_state & TS_XCLUDE && - priv_check(td, PRIV_TTY_EXCLUSIVE)) { - return (EBUSY); + while (ttydisc_rint_poll(otp) > 0) { + if (np->np_rate && !np->np_quota) + break; + if (ttydisc_getc(tp, &c, 1) != 1) + break; + np->np_quota--; + ttydisc_rint(otp, c, 0); } - error = ttyld_open(tp, dev); - return (error); + ttydisc_rint_done(otp); + + tty_unlock(tp); } static int @@ -317,18 +238,17 @@ bits_per_char(struct termios *t) } static int -nmdmparam(struct tty *tp, struct termios *t) +nmdm_param(struct tty *tp, struct termios *t) { - struct softpart *sp; + struct nmdmpart *np = tty_softc(tp); struct tty *tp2; int bpc, rate, speed, i; - sp = tp->t_sc; - tp2 = sp->other->nm_tty; + tp2 = np->np_other->np_tty; - if (!((t->c_cflag | tp2->t_cflag) & CDSR_OFLOW)) { - sp->rate = 0; - sp->other->rate = 0; + if (!((t->c_cflag | tp2->t_termios.c_cflag) & CDSR_OFLOW)) { + np->np_rate = 0; + np->np_other->np_rate = 0; return (0); } @@ -343,10 +263,10 @@ nmdmparam(struct tty *tp, struct termios *t) for (i = 0; i < 2; i++) { /* Use the slower of our receive and their transmit rate */ - speed = imin(tp2->t_ospeed, t->c_ispeed); + speed = imin(tp2->t_termios.c_ospeed, t->c_ispeed); if (speed == 0) { - sp->rate = 0; - sp->other->rate = 0; + np->np_rate = 0; + np->np_other->np_rate = 0; return (0); } @@ -359,73 +279,63 @@ nmdmparam(struct tty *tp, struct termios *t) speed *= rate; speed /= hz; /* [(char/sec)/tick, scaled */ - sp->credits = speed; - sp->rate = rate; - callout_reset(&sp->co, rate, nmdm_timeout, sp); + np->np_credits = speed; + np->np_rate = rate; + callout_reset(&np->np_callout, rate, nmdm_timeout, np); /* * swap pointers for second pass so the other end gets * updated as well. */ - sp = sp->other; + np = np->np_other; t = &tp2->t_termios; tp2 = tp; } + return (0); } static int -nmdmmodem(struct tty *tp, int sigon, int sigoff) +nmdm_modem(struct tty *tp, int sigon, int sigoff) { - struct softpart *sp; - int i; + struct nmdmpart *np = tty_softc(tp); + int i = 0; - sp = tp->t_sc; if (sigon || sigoff) { if (sigon & SER_DTR) - sp->other->nm_dcd = 1; + np->np_other->np_dcd = 1; if (sigoff & SER_DTR) - sp->other->nm_dcd = 0; - ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd); + np->np_other->np_dcd = 0; + + ttydisc_modem(np->np_other->np_tty, np->np_other->np_dcd); + return (0); } else { - i = 0; - if (sp->nm_dcd) + if (np->np_dcd) i |= SER_DCD; - if (sp->other->nm_dcd) + if (np->np_other->np_dcd) i |= SER_DTR; + return (i); } } -static int -nmdmclose(struct cdev *dev, int flag, int mode, struct thread *td) -{ - struct tty *tp = dev->si_tty; - int error; - - error = ttyld_close(tp, flag); - (void) tty_close(dev->si_tty); - - return (error); -} - static void -nmdmoproc(struct tty *tp) +nmdm_inwakeup(struct tty *tp) { - struct softpart *pt; + struct nmdmpart *np = tty_softc(tp); - pt = tp->t_sc; - taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); + /* We can receive again, so wake up the other side. */ + taskqueue_enqueue(taskqueue_swi, &np->np_other->np_task); } static void -nmdmstop(struct tty *tp, int flush) +nmdm_outwakeup(struct tty *tp) { - struct softpart *pt; + struct nmdmpart *np = tty_softc(tp); - pt = tp->t_sc; - taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); + /* We can transmit again, so wake up our side. */ + taskqueue_enqueue(taskqueue_swi, &np->np_task); } /* @@ -435,32 +345,28 @@ static int nmdm_modevent(module_t mod, int type, void *data) { static eventhandler_tag tag; - struct nm_softc *pt, *tpt; - int error = 0; switch(type) { case MOD_LOAD: - clone_setup(&nmdmclones); tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); if (tag == NULL) return (ENOMEM); break; case MOD_SHUTDOWN: - /* FALLTHROUGH */ + break; + case MOD_UNLOAD: + if (nmdm_count != 0) + return (EBUSY); EVENTHANDLER_DEREGISTER(dev_clone, tag); - TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { - destroy_dev(pt->part1.dev); - TAILQ_REMOVE(&nmdmhead, pt, pt_list); - free(pt, M_NLMDM); - } - clone_cleanup(&nmdmclones); break; + default: - error = EOPNOTSUPP; + return (EOPNOTSUPP); } - return (error); + + return (0); } DEV_MODULE(nmdm, nmdm_modevent, NULL); |