summaryrefslogtreecommitdiffstats
path: root/sys/dev/nmdm/nmdm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/nmdm/nmdm.c')
-rw-r--r--sys/dev/nmdm/nmdm.c412
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);
OpenPOWER on IntegriCloud