nmdm.c revision 139749
1139749Simp/*-
273161Sjulian * Copyright (c) 1982, 1986, 1989, 1993
373161Sjulian *	The Regents of the University of California.  All rights reserved.
473161Sjulian *
573161Sjulian * Redistribution and use in source and binary forms, with or without
673161Sjulian * modification, are permitted provided that the following conditions
773161Sjulian * are met:
873161Sjulian * 1. Redistributions of source code must retain the above copyright
973161Sjulian *    notice, this list of conditions and the following disclaimer.
1073161Sjulian * 2. Redistributions in binary form must reproduce the above copyright
1173161Sjulian *    notice, this list of conditions and the following disclaimer in the
1273161Sjulian *    documentation and/or other materials provided with the distribution.
1373161Sjulian * 4. Neither the name of the University nor the names of its contributors
1473161Sjulian *    may be used to endorse or promote products derived from this software
1573161Sjulian *    without specific prior written permission.
1673161Sjulian *
1773161Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1873161Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1973161Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2073161Sjulian * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2173161Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2273161Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2373161Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2473161Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2573161Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2673161Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2773161Sjulian * SUCH DAMAGE.
2873161Sjulian *
2973161Sjulian */
3073161Sjulian
31119418Sobrien#include <sys/cdefs.h>
32119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/nmdm/nmdm.c 139749 2005-01-06 01:43:34Z imp $");
33119418Sobrien
3473161Sjulian/*
3590995Sjulian * Pseudo-nulmodem driver
3690995Sjulian * Mighty handy for use with serial console in Vmware
3773161Sjulian */
3890995Sjulian
3973161Sjulian#include "opt_compat.h"
40111899Sdas#include "opt_tty.h"
41111899Sdas
4273161Sjulian#include <sys/param.h>
4373161Sjulian#include <sys/systm.h>
4473161Sjulian#include <sys/proc.h>
4573161Sjulian#include <sys/tty.h>
4673161Sjulian#include <sys/conf.h>
4773161Sjulian#include <sys/fcntl.h>
4873161Sjulian#include <sys/poll.h>
4973161Sjulian#include <sys/kernel.h>
50129879Sphk#include <sys/module.h>
51131579Sphk#include <sys/serial.h>
5273161Sjulian#include <sys/signalvar.h>
5373161Sjulian#include <sys/malloc.h>
54129968Sphk#include <sys/taskqueue.h>
5573161Sjulian
5673161SjulianMALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures");
5773161Sjulian
5890995Sjulianstatic void 	nmdmstart(struct tty *tp);
5990995Sjulianstatic void 	nmdmstop(struct tty *tp, int rw);
60130585Sphkstatic void 	nmdminit(struct cdev *dev);
61131579Sphkstatic t_modem_t	nmdmmodem;
6273161Sjulian
6390995Sjulianstatic d_open_t		nmdmopen;
6490995Sjulianstatic d_close_t	nmdmclose;
6573161Sjulian
6673161Sjulianstatic struct cdevsw nmdm_cdevsw = {
67126080Sphk	.d_version =	D_VERSION,
68111815Sphk	.d_open =	nmdmopen,
69111815Sphk	.d_close =	nmdmclose,
70126080Sphk	.d_name =	"nmdn",
71126080Sphk	.d_flags =	D_TTY | D_PSEUDO | D_NEEDGIANT,
7273161Sjulian};
7373161Sjulian
7490995Sjulian#define BUFSIZ 		100		/* Chunk size iomoved to/from user */
7590995Sjulian#define NMDM_MAX_NUM	128		/* Artificially limit # devices. */
7690995Sjulian#define	PF_STOPPED	0x10		/* user told stopped */
77126077Sphk#define BFLAG		CLONE_FLAG0
7873161Sjulian
7973161Sjulianstruct softpart {
80131579Sphk	struct tty		*nm_tty;
81131579Sphk	struct cdev 		*dev;
82131579Sphk	int			nm_dcd;
83129968Sphk	struct task		pt_task;
84129968Sphk	struct softpart		*other;
8573161Sjulian};
8673161Sjulian
8773161Sjulianstruct	nm_softc {
88126077Sphk	TAILQ_ENTRY(nm_softc)	pt_list;
89126077Sphk	int			pt_flags;
90126077Sphk	struct softpart 	part1, part2;
91126077Sphk	struct	prison 		*pt_prison;
9273161Sjulian};
9373161Sjulian
94126077Sphkstatic struct clonedevs *nmdmclones;
95126077Sphkstatic TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead);
96126077Sphk
9773161Sjulianstatic void
98130585Sphknmdm_clone(void *arg, char *name, int nameen, struct cdev **dev)
99126077Sphk{
100126077Sphk	int i, unit;
101126077Sphk	char *p;
102130585Sphk	struct cdev *d1, *d2;
103126077Sphk
104130640Sphk	if (*dev != NULL)
105126077Sphk		return;
106126077Sphk	if (strcmp(name, "nmdm") == 0) {
107126077Sphk		p = NULL;
108126077Sphk		unit = -1;
109126077Sphk	} else {
110126077Sphk		i = dev_stdclone(name, &p, "nmdm", &unit);
111126077Sphk		if (i == 0)
112126077Sphk			return;
113126077Sphk		if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B')
114126077Sphk			return;
115126077Sphk		else if (p[0] != '\0' && p[1] != '\0')
116126077Sphk			return;
117126077Sphk	}
118126077Sphk	i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0);
119126077Sphk	if (i) {
120126077Sphk		d1 = make_dev(&nmdm_cdevsw, unit2minor(unit),
121126077Sphk		     0, 0, 0666, "nmdm%dA", unit);
122126077Sphk		if (d1 == NULL)
123126077Sphk			return;
124126077Sphk		d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG,
125126077Sphk		     0, 0, 0666, "nmdm%dB", unit);
126126077Sphk		if (d2 == NULL) {
127126077Sphk			destroy_dev(d1);
128126077Sphk			return;
129126077Sphk		}
130126077Sphk		d2->si_drv2 = d1;
131126077Sphk		d1->si_drv2 = d2;
132126077Sphk		dev_depends(d1, d2);
133126077Sphk		dev_depends(d2, d1);
134126077Sphk		d1->si_flags |= SI_CHEAPCLONE;
135126077Sphk		d2->si_flags |= SI_CHEAPCLONE;
136126077Sphk	}
137126077Sphk	if (p != NULL && p[0] == 'B')
138126077Sphk		*dev = d1->si_drv2;
139126077Sphk	else
140126077Sphk		*dev = d1;
141126077Sphk}
142126077Sphk
143126077Sphkstatic void
144129968Sphknmdm_task_tty(void *arg, int pending __unused)
145129968Sphk{
146129968Sphk	struct tty *tp, *otp;
147129968Sphk	struct softpart *sp;
148129968Sphk	int c;
14973161Sjulian
150129968Sphk	tp = arg;
151129968Sphk	sp = tp->t_sc;
152129968Sphk	otp = sp->other->nm_tty;
153129968Sphk	KASSERT(otp != NULL, ("NULL otp in nmdmstart"));
154129968Sphk	KASSERT(otp != tp, ("NULL otp == tp nmdmstart"));
155131579Sphk	if (sp->other->nm_dcd) {
156129968Sphk		if (!(tp->t_state & TS_ISOPEN)) {
157131579Sphk			sp->other->nm_dcd = 0;
158130077Sphk			(void)ttyld_modem(otp, 0);
159129968Sphk		}
160129968Sphk	} else {
161129968Sphk		if (tp->t_state & TS_ISOPEN) {
162131579Sphk			sp->other->nm_dcd = 1;
163130077Sphk			(void)ttyld_modem(otp, 1);
164129968Sphk		}
165129968Sphk	}
166129968Sphk	if (tp->t_state & TS_TTSTOP)
167129968Sphk		return;
168129968Sphk	while (tp->t_outq.c_cc != 0) {
169129968Sphk		if (otp->t_state & TS_TBLOCK)
170129968Sphk			return;
171129968Sphk		c = getc(&tp->t_outq);
172129968Sphk		if (otp->t_state & TS_ISOPEN)
173130077Sphk			ttyld_rint(otp, c);
174129968Sphk	}
175129968Sphk	if (tp->t_outq.c_cc == 0)
176129968Sphk		ttwwakeup(tp);
177129968Sphk}
17873161Sjulian
17973161Sjulian/*
18073161Sjulian * This function creates and initializes a pair of ttys.
18173161Sjulian */
18273161Sjulianstatic void
183130585Sphknmdminit(struct cdev *dev1)
18473161Sjulian{
185130585Sphk	struct cdev *dev2;
18673161Sjulian	struct nm_softc *pt;
18773161Sjulian
188126077Sphk	dev2 = dev1->si_drv2;
18973161Sjulian
190126077Sphk	dev1->si_flags &= ~SI_CHEAPCLONE;
191126077Sphk	dev2->si_flags &= ~SI_CHEAPCLONE;
19273161Sjulian
193126077Sphk	pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO);
194126077Sphk	TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list);
195129968Sphk
19673161Sjulian	dev1->si_drv1 = dev2->si_drv1 = pt;
197126077Sphk
198126077Sphk	pt->part1.dev = dev1;
199126077Sphk	pt->part2.dev = dev2;
200129968Sphk
201129968Sphk	pt->part1.nm_tty = ttymalloc(pt->part1.nm_tty);
202129968Sphk	pt->part1.nm_tty->t_oproc = nmdmstart;
203129968Sphk	pt->part1.nm_tty->t_stop = nmdmstop;
204131579Sphk	pt->part1.nm_tty->t_modem = nmdmmodem;
205129968Sphk	pt->part1.nm_tty->t_dev = dev1;
206129968Sphk	pt->part1.nm_tty->t_sc = &pt->part1;
207129968Sphk	TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty);
208129968Sphk
209129968Sphk	pt->part2.nm_tty = ttymalloc(pt->part2.nm_tty);
210129968Sphk	pt->part2.nm_tty->t_oproc = nmdmstart;
211129968Sphk	pt->part2.nm_tty->t_stop = nmdmstop;
212131579Sphk	pt->part2.nm_tty->t_modem = nmdmmodem;
213129968Sphk	pt->part2.nm_tty->t_dev = dev2;
214129968Sphk	pt->part2.nm_tty->t_sc = &pt->part2;
215129968Sphk	TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty);
216129968Sphk
217129968Sphk	pt->part1.other = &pt->part2;
218129968Sphk	pt->part2.other = &pt->part1;
219129968Sphk
220129968Sphk	dev1->si_tty = pt->part1.nm_tty;
221129968Sphk	dev1->si_drv1 = pt;
222129968Sphk
223129968Sphk	dev2->si_tty = pt->part2.nm_tty;
224129968Sphk	dev2->si_drv1 = pt;
22573161Sjulian}
22673161Sjulian
22790995Sjulian/*
22890995Sjulian * Device opened from userland
22990995Sjulian */
23073161Sjulianstatic	int
231130585Sphknmdmopen(struct cdev *dev, int flag, int devtype, struct thread *td)
23273161Sjulian{
233129968Sphk	struct tty *tp, *tp2;
23473161Sjulian	int error;
23573161Sjulian	struct nm_softc *pti;
236129968Sphk	struct softpart *sp;
23773161Sjulian
238126077Sphk	if (dev->si_drv1 == NULL)
239126077Sphk		nmdminit(dev);
240126077Sphk	pti = dev->si_drv1;
241129968Sphk	if (pti->pt_prison != td->td_ucred->cr_prison)
242129968Sphk		return (EBUSY);
24373161Sjulian
244129968Sphk	tp = dev->si_tty;
245129968Sphk	sp = tp->t_sc;
246129968Sphk	tp2 = sp->other->nm_tty;
24790995Sjulian
24873161Sjulian	if ((tp->t_state & TS_ISOPEN) == 0) {
249136680Sphk		ttyinitmode(tp, 0, 0);
250129968Sphk		ttsetwater(tp); /* XXX ? */
25193593Sjhb	} else if (tp->t_state & TS_XCLUDE && suser(td)) {
25273161Sjulian		return (EBUSY);
25373161Sjulian	}
25473161Sjulian
255130077Sphk	error = ttyld_open(tp, dev);
25673161Sjulian	return (error);
25773161Sjulian}
25873161Sjulian
259129968Sphkstatic int
260131579Sphknmdmmodem(struct tty *tp, int sigon, int sigoff)
261131579Sphk{
262131579Sphk	struct softpart *sp;
263131579Sphk	int i;
264131579Sphk
265131579Sphk	sp = tp->t_sc;
266131579Sphk	if (sigon || sigoff) {
267131579Sphk		if (sigon & SER_DTR) {
268131579Sphk			sp->other->nm_dcd = 1;
269131579Sphk			ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd);
270131579Sphk		}
271131579Sphk		if (sigoff & SER_DTR) {
272131579Sphk			sp->other->nm_dcd = 0;
273131579Sphk			ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd);
274131579Sphk		}
275131579Sphk		return (0);
276131579Sphk	} else {
277131579Sphk		i = 0;
278131579Sphk		if (sp->nm_dcd)
279131579Sphk			i |= SER_DCD;
280131579Sphk		if (sp->other->nm_dcd)
281131579Sphk			i |= SER_DTR;
282131579Sphk		return (i);
283131579Sphk	}
284131579Sphk}
285131579Sphk
286131579Sphkstatic int
287130585Sphknmdmclose(struct cdev *dev, int flag, int mode, struct thread *td)
28873161Sjulian{
28973161Sjulian
290132226Sphk	return (tty_close(dev->si_tty));
29173161Sjulian}
29273161Sjulian
29373161Sjulianstatic void
29490995Sjuliannmdmstart(struct tty *tp)
29573161Sjulian{
296129968Sphk	struct softpart *pt;
29773161Sjulian
298129968Sphk	pt = tp->t_sc;
299129968Sphk	taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task);
30073161Sjulian}
30173161Sjulian
30273161Sjulianstatic void
303129968Sphknmdmstop(struct tty *tp, int flush)
30473161Sjulian{
305129968Sphk	struct softpart *pt;
30673161Sjulian
307129968Sphk	pt = tp->t_sc;
308129968Sphk	taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task);
30973161Sjulian}
31073161Sjulian
31190995Sjulian/*
31290995Sjulian * Module handling
31390995Sjulian */
31490995Sjulianstatic int
31590995Sjuliannmdm_modevent(module_t mod, int type, void *data)
31690995Sjulian{
317126077Sphk	static eventhandler_tag tag;
318126077Sphk	struct nm_softc *pt, *tpt;
31990995Sjulian        int error = 0;
32073161Sjulian
32190995Sjulian        switch(type) {
322126077Sphk        case MOD_LOAD:
323126845Sphk		clone_setup(&nmdmclones);
324126077Sphk		tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000);
325126077Sphk		if (tag == NULL)
326126077Sphk			return (ENOMEM);
32790995Sjulian		break;
32873161Sjulian
32990995Sjulian	case MOD_SHUTDOWN:
33090995Sjulian		/* FALLTHROUGH */
33190995Sjulian	case MOD_UNLOAD:
332126077Sphk		EVENTHANDLER_DEREGISTER(dev_clone, tag);
333126077Sphk		TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) {
334126077Sphk			destroy_dev(pt->part1.dev);
335126077Sphk			TAILQ_REMOVE(&nmdmhead, pt, pt_list);
336126077Sphk			free(pt, M_NLMDM);
337126077Sphk		}
338126077Sphk		clone_cleanup(&nmdmclones);
33990995Sjulian		break;
34090995Sjulian	default:
34190995Sjulian		error = EOPNOTSUPP;
34290995Sjulian	}
34390995Sjulian	return (error);
34490995Sjulian}
34573161Sjulian
34690995SjulianDEV_MODULE(nmdm, nmdm_modevent, NULL);
347