nmdm.c revision 119418
15992SN/A/*
25992SN/A * Copyright (c) 1982, 1986, 1989, 1993
35992SN/A *	The Regents of the University of California.  All rights reserved.
45992SN/A *
55992SN/A * Redistribution and use in source and binary forms, with or without
65992SN/A * modification, are permitted provided that the following conditions
75992SN/A * are met:
85992SN/A * 1. Redistributions of source code must retain the above copyright
95992SN/A *    notice, this list of conditions and the following disclaimer.
105992SN/A * 2. Redistributions in binary form must reproduce the above copyright
115992SN/A *    notice, this list of conditions and the following disclaimer in the
125992SN/A *    documentation and/or other materials provided with the distribution.
135992SN/A * 3. All advertising materials mentioning features or use of this software
145992SN/A *    must display the following acknowledgement:
155992SN/A *	This product includes software developed by the University of
165992SN/A *	California, Berkeley and its contributors.
175992SN/A * 4. Neither the name of the University nor the names of its contributors
185992SN/A *    may be used to endorse or promote products derived from this software
195992SN/A *    without specific prior written permission.
205992SN/A *
215992SN/A * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
225992SN/A * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
235992SN/A * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
245992SN/A * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
255992SN/A * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
265992SN/A * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
275992SN/A * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
285992SN/A * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
295992SN/A * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
305992SN/A * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
315992SN/A * SUCH DAMAGE.
325992SN/A *
335992SN/A */
345992SN/A
355992SN/A#include <sys/cdefs.h>
365992SN/A__FBSDID("$FreeBSD: head/sys/dev/nmdm/nmdm.c 119418 2003-08-24 17:55:58Z obrien $");
375992SN/A
385992SN/A/*
395992SN/A * Pseudo-nulmodem driver
405992SN/A * Mighty handy for use with serial console in Vmware
415992SN/A */
425992SN/A
435992SN/A#include "opt_compat.h"
445992SN/A#include "opt_tty.h"
455992SN/A
465992SN/A#include <sys/param.h>
475992SN/A#include <sys/systm.h>
485992SN/A#if defined(COMPAT_43) || defined(COMPAT_SUNOS)
495992SN/A#include <sys/ioctl_compat.h>
505992SN/A#endif
515992SN/A#include <sys/proc.h>
525992SN/A#include <sys/tty.h>
535992SN/A#include <sys/conf.h>
545992SN/A#include <sys/fcntl.h>
555992SN/A#include <sys/poll.h>
565992SN/A#include <sys/kernel.h>
575992SN/A#include <sys/vnode.h>
585992SN/A#include <sys/signalvar.h>
595992SN/A#include <sys/malloc.h>
605992SN/A
615992SN/AMALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures");
625992SN/A
635992SN/Astatic void 	nmdmstart(struct tty *tp);
645992SN/Astatic void 	nmdmstop(struct tty *tp, int rw);
655992SN/Astatic void 	wakeup_other(struct tty *tp, int flag);
665992SN/Astatic void 	nmdminit(int);
675992SN/Astatic int 	nmdmshutdown(void);
685992SN/A
695992SN/Astatic d_open_t		nmdmopen;
705992SN/Astatic d_close_t	nmdmclose;
715992SN/Astatic d_read_t		nmdmread;
725992SN/Astatic d_write_t	nmdmwrite;
735992SN/Astatic d_ioctl_t	nmdmioctl;
745992SN/A
757084SN/A#define	CDEV_MAJOR	18
767084SN/Astatic struct cdevsw nmdm_cdevsw = {
777084SN/A	.d_open =	nmdmopen,
787084SN/A	.d_close =	nmdmclose,
795992SN/A	.d_read =	nmdmread,
805992SN/A	.d_write =	nmdmwrite,
815992SN/A	.d_ioctl =	nmdmioctl,
825992SN/A	.d_poll =	ttypoll,
835992SN/A	.d_name =	"pts",
845992SN/A	.d_maj =	CDEV_MAJOR,
855992SN/A	.d_flags =	D_TTY,
865992SN/A};
87
88#define BUFSIZ 		100		/* Chunk size iomoved to/from user */
89#define NMDM_MAX_NUM	128		/* Artificially limit # devices. */
90#define	PF_STOPPED	0x10		/* user told stopped */
91
92struct softpart {
93	struct tty	nm_tty;
94	dev_t	dev;
95	int	modemsignals;	/* bits defined in sys/ttycom.h */
96	int	gotbreak;
97};
98
99struct	nm_softc {
100	int	pt_flags;
101	struct softpart part1, part2;
102	struct	prison *pt_prison;
103};
104
105static void
106nmdm_crossover(struct nm_softc *pti,
107		struct softpart *ourpart,
108		struct softpart *otherpart);
109
110#define GETPARTS(tp, ourpart, otherpart) \
111do {	\
112	struct nm_softc *pti = tp->t_dev->si_drv1; \
113	if (tp == &pti->part1.nm_tty) { \
114		ourpart = &pti->part1; \
115		otherpart = &pti->part2; \
116	} else { \
117		ourpart = &pti->part2; \
118		otherpart = &pti->part1; \
119	}  \
120} while (0)
121
122/*
123 * This function creates and initializes a pair of ttys.
124 */
125static void
126nmdminit(n)
127	int n;
128{
129	dev_t dev1, dev2;
130	struct nm_softc *pt;
131
132	/* For now we only map the lower 8 bits of the minor */
133	if (n & ~0xff)
134		return;
135
136	pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK);
137	bzero(pt, sizeof(*pt));
138	pt->part1.dev = dev1 = make_dev(&nmdm_cdevsw, n+n,
139	    0, 0, 0666, "nmdm%dA", n);
140	pt->part2.dev = dev2 = make_dev(&nmdm_cdevsw, n+n+1,
141	    0, 0, 0666, "nmdm%dB", n);
142
143	dev1->si_drv1 = dev2->si_drv1 = pt;
144	dev1->si_tty = &pt->part1.nm_tty;
145	dev2->si_tty = &pt->part2.nm_tty;
146	ttyregister(&pt->part1.nm_tty);
147	ttyregister(&pt->part2.nm_tty);
148	pt->part1.nm_tty.t_oproc = nmdmstart;
149	pt->part2.nm_tty.t_oproc = nmdmstart;
150	pt->part1.nm_tty.t_stop = nmdmstop;
151	pt->part2.nm_tty.t_dev = dev1;
152	pt->part1.nm_tty.t_dev = dev2;
153	pt->part2.nm_tty.t_stop = nmdmstop;
154}
155
156/*
157 * Device opened from userland
158 */
159static	int
160nmdmopen(dev_t dev, int flag, int devtype, struct thread *td)
161{
162	register struct tty *tp, *tp2;
163	int error;
164	int minr;
165	dev_t nextdev;
166	struct nm_softc *pti;
167	int is_b;
168	int	pair;
169	struct	softpart *ourpart, *otherpart;
170
171	/*
172	 * XXX: Gross hack for DEVFS:
173	 * If we openned this device, ensure we have the
174	 * next one too, so people can open it.
175	 */
176	minr = dev2unit(dev);
177	pair = minr >> 1;
178	is_b = minr & 1;
179
180	if (pair < (NMDM_MAX_NUM - 1)) {
181		nextdev = makedev(major(dev), minr + 2);
182		if (!nextdev->si_drv1) {
183			nmdminit(pair + 1);
184		}
185	} else { /* Limit ourselves to 128 of them for now */
186		if (pair > (NMDM_MAX_NUM - 1))
187			return (ENXIO);
188	}
189	if (!dev->si_drv1)
190		nmdminit(pair);
191
192	if (!dev->si_drv1)
193		return(ENXIO);
194
195	pti = dev->si_drv1;
196	if (is_b)
197		tp = &pti->part2.nm_tty;
198	else
199		tp = &pti->part1.nm_tty;
200	GETPARTS(tp, ourpart, otherpart);
201
202	tp2 = &otherpart->nm_tty;
203	ourpart->modemsignals |= TIOCM_LE;
204
205	if ((tp->t_state & TS_ISOPEN) == 0) {
206		ttychars(tp);		/* Set up default chars */
207		tp->t_iflag = TTYDEF_IFLAG;
208		tp->t_oflag = TTYDEF_OFLAG;
209		tp->t_lflag = TTYDEF_LFLAG;
210		tp->t_cflag = TTYDEF_CFLAG;
211		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
212	} else if (tp->t_state & TS_XCLUDE && suser(td)) {
213		return (EBUSY);
214	} else if (pti->pt_prison != td->td_ucred->cr_prison) {
215		return (EBUSY);
216	}
217
218	/*
219	 * If the other side is open we have carrier
220	 */
221	if (tp2->t_state & TS_ISOPEN) {
222		(void)(*linesw[tp->t_line].l_modem)(tp, 1);
223	}
224
225	/*
226	 * And the other side gets carrier as we are now open.
227	 */
228	(void)(*linesw[tp2->t_line].l_modem)(tp2, 1);
229
230	/* External processing makes no sense here */
231	tp->t_lflag &= ~EXTPROC;
232
233	/*
234	 * Wait here if we don't have carrier.
235	 */
236#if 0
237	while ((tp->t_state & TS_CARR_ON) == 0) {
238		if (flag & FNONBLOCK)
239			break;
240		error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
241				 "nmdopn", 0);
242		if (error)
243			return (error);
244	}
245#endif
246
247	/*
248	 * Give the line disciplin a chance to set this end up.
249	 */
250	error = (*linesw[tp->t_line].l_open)(dev, tp);
251
252	/*
253	 * Wake up the other side.
254	 * Theoretically not needed.
255	 */
256	ourpart->modemsignals |= TIOCM_DTR;
257	nmdm_crossover(pti, ourpart, otherpart);
258	if (error == 0)
259		wakeup_other(tp, FREAD|FWRITE); /* XXX */
260	return (error);
261}
262
263/*
264 * Device closed again
265 */
266static	int
267nmdmclose(dev_t dev, int flag, int mode, struct thread *td)
268{
269	register struct tty *tp, *tp2;
270	int err;
271	struct softpart *ourpart, *otherpart;
272
273	/*
274	 * let the other end know that the game is up
275	 */
276	tp = dev->si_tty;
277	GETPARTS(tp, ourpart, otherpart);
278	tp2 = &otherpart->nm_tty;
279	(void)(*linesw[tp2->t_line].l_modem)(tp2, 0);
280
281	/*
282	 * XXX MDMBUF makes no sense for nmdms but would inhibit the above
283	 * l_modem().  CLOCAL makes sense but isn't supported.   Special
284	 * l_modem()s that ignore carrier drop make no sense for nmdms but
285	 * may be in use because other parts of the line discipline make
286	 * sense for nmdms.  Recover by doing everything that a normal
287	 * ttymodem() would have done except for sending a SIGHUP.
288	 */
289	if (tp2->t_state & TS_ISOPEN) {
290		tp2->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
291		tp2->t_state |= TS_ZOMBIE;
292		ttyflush(tp2, FREAD | FWRITE);
293	}
294
295	err = (*linesw[tp->t_line].l_close)(tp, flag);
296	ourpart->modemsignals &= ~TIOCM_DTR;
297	nmdm_crossover(dev->si_drv1, ourpart, otherpart);
298	nmdmstop(tp, FREAD|FWRITE);
299	(void) ttyclose(tp);
300	return (err);
301}
302
303/*
304 * handle read(2) request from userland
305 */
306static	int
307nmdmread(dev_t dev, struct uio *uio, int flag)
308{
309	int error = 0;
310	struct tty *tp, *tp2;
311	struct softpart *ourpart, *otherpart;
312
313	tp = dev->si_tty;
314	GETPARTS(tp, ourpart, otherpart);
315	tp2 = &otherpart->nm_tty;
316
317#if 0
318	if (tp2->t_state & TS_ISOPEN) {
319		error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
320		wakeup_other(tp, FWRITE);
321	} else {
322		if (flag & IO_NDELAY) {
323			return (EWOULDBLOCK);
324		}
325		error = tsleep(TSA_PTC_READ(tp),
326				TTIPRI | PCATCH, "nmdout", 0);
327		}
328	}
329#else
330	if ((error = (*linesw[tp->t_line].l_read)(tp, uio, flag)) == 0)
331		wakeup_other(tp, FWRITE);
332#endif
333	return (error);
334}
335
336/*
337 * Write to pseudo-tty.
338 * Wakeups of controlling tty will happen
339 * indirectly, when tty driver calls nmdmstart.
340 */
341static	int
342nmdmwrite(dev_t dev, struct uio *uio, int flag)
343{
344	register u_char *cp = 0;
345	register int cc = 0;
346	u_char locbuf[BUFSIZ];
347	int cnt = 0;
348	int error = 0;
349	struct tty *tp1, *tp;
350	struct softpart *ourpart, *otherpart;
351
352	tp1 = dev->si_tty;
353	/*
354	 * Get the other tty struct.
355	 * basically we are writing into the INPUT side of the other device.
356	 */
357	GETPARTS(tp1, ourpart, otherpart);
358	tp = &otherpart->nm_tty;
359
360again:
361	if ((tp->t_state & TS_ISOPEN) == 0)
362		return (EIO);
363	while (uio->uio_resid > 0 || cc > 0) {
364		/*
365		 * Fill up the buffer if it's empty
366		 */
367		if (cc == 0) {
368			cc = min(uio->uio_resid, BUFSIZ);
369			cp = locbuf;
370			error = uiomove((caddr_t)cp, cc, uio);
371			if (error)
372				return (error);
373			/* check again for safety */
374			if ((tp->t_state & TS_ISOPEN) == 0) {
375				/* adjust for data copied in but not written */
376				uio->uio_resid += cc;
377				return (EIO);
378			}
379		}
380		while (cc > 0) {
381			if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2))
382			&& ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) {
383				/*
384	 			 * Come here to wait for space in outq,
385				 * or space in rawq, or an empty canq.
386	 			 */
387				wakeup(TSA_HUP_OR_INPUT(tp));
388				if ((tp->t_state & TS_CONNECTED) == 0) {
389					/*
390					 * Data piled up because not connected.
391					 * Adjust for data copied in but
392					 * not written.
393					 */
394					uio->uio_resid += cc;
395					return (EIO);
396				}
397				if (flag & IO_NDELAY) {
398					/*
399				         * Don't wait if asked not to.
400					 * Adjust for data copied in but
401					 * not written.
402					 */
403					uio->uio_resid += cc;
404					if (cnt == 0)
405						return (EWOULDBLOCK);
406					return (0);
407				}
408				error = tsleep(TSA_PTC_WRITE(tp),
409						TTOPRI | PCATCH, "nmdout", 0);
410				if (error) {
411					/*
412					 * Tsleep returned (signal?).
413					 * Go find out what the user wants.
414					 * adjust for data copied in but
415					 * not written
416					 */
417					uio->uio_resid += cc;
418					return (error);
419				}
420				goto again;
421			}
422			(*linesw[tp->t_line].l_rint)(*cp++, tp);
423			cnt++;
424			cc--;
425		}
426		cc = 0;
427	}
428	return (0);
429}
430
431/*
432 * Start output on pseudo-tty.
433 * Wake up process selecting or sleeping for input from controlling tty.
434 */
435static void
436nmdmstart(struct tty *tp)
437{
438	register struct nm_softc *pti = tp->t_dev->si_drv1;
439
440	if (tp->t_state & TS_TTSTOP)
441		return;
442	pti->pt_flags &= ~PF_STOPPED;
443	wakeup_other(tp, FREAD);
444}
445
446/* Wakes up the OTHER tty;*/
447static void
448wakeup_other(struct tty *tp, int flag)
449{
450	struct softpart *ourpart, *otherpart;
451
452	GETPARTS(tp, ourpart, otherpart);
453	if (flag & FREAD) {
454		selwakeup(&otherpart->nm_tty.t_rsel);
455		wakeup(TSA_PTC_READ((&otherpart->nm_tty)));
456	}
457	if (flag & FWRITE) {
458		selwakeup(&otherpart->nm_tty.t_wsel);
459		wakeup(TSA_PTC_WRITE((&otherpart->nm_tty)));
460	}
461}
462
463/*
464 * stopped output on tty, called when device is closed
465 */
466static	void
467nmdmstop(register struct tty *tp, int flush)
468{
469	struct nm_softc *pti = tp->t_dev->si_drv1;
470	int flag;
471
472	/* note: FLUSHREAD and FLUSHWRITE already ok */
473	if (flush == 0) {
474		flush = TIOCPKT_STOP;
475		pti->pt_flags |= PF_STOPPED;
476	} else
477		pti->pt_flags &= ~PF_STOPPED;
478	/* change of perspective */
479	flag = 0;
480	if (flush & FREAD)
481		flag |= FWRITE;
482	if (flush & FWRITE)
483		flag |= FREAD;
484	wakeup_other(tp, flag);
485}
486
487/*
488 * handle ioctl(2) request from userland
489 */
490static	int
491nmdmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td)
492{
493	register struct tty *tp = dev->si_tty;
494	struct nm_softc *pti = dev->si_drv1;
495	int error, s;
496	register struct tty *tp2;
497	struct softpart *ourpart, *otherpart;
498
499	s = spltty();
500	GETPARTS(tp, ourpart, otherpart);
501	tp2 = &otherpart->nm_tty;
502
503	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
504	if (error == ENOIOCTL)
505		 error = ttioctl(tp, cmd, data, flag);
506	if (error == ENOIOCTL) {
507		switch (cmd) {
508		case TIOCSBRK:
509			otherpart->gotbreak = 1;
510			break;
511		case TIOCCBRK:
512			break;
513		case TIOCSDTR:
514			ourpart->modemsignals |= TIOCM_DTR;
515			break;
516		case TIOCCDTR:
517			ourpart->modemsignals &= TIOCM_DTR;
518			break;
519		case TIOCMSET:
520			ourpart->modemsignals = *(int *)data;
521			otherpart->modemsignals = *(int *)data;
522			break;
523		case TIOCMBIS:
524			ourpart->modemsignals |= *(int *)data;
525			break;
526		case TIOCMBIC:
527			ourpart->modemsignals &= ~(*(int *)data);
528			otherpart->modemsignals &= ~(*(int *)data);
529			break;
530		case TIOCMGET:
531			*(int *)data = ourpart->modemsignals;
532			break;
533		case TIOCMSDTRWAIT:
534			break;
535		case TIOCMGDTRWAIT:
536			*(int *)data = 0;
537			break;
538		case TIOCTIMESTAMP:
539			/* FALLTHROUGH */
540		case TIOCDCDTIMESTAMP:
541		default:
542			splx(s);
543			error = ENOTTY;
544			return (error);
545		}
546		error = 0;
547		nmdm_crossover(pti, ourpart, otherpart);
548	}
549	splx(s);
550	return (error);
551}
552
553static void
554nmdm_crossover(struct nm_softc *pti, struct softpart *ourpart,
555    struct softpart *otherpart)
556{
557	otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR);
558	if (ourpart->modemsignals & TIOCM_RTS)
559		otherpart->modemsignals |= TIOCM_CTS;
560	if (ourpart->modemsignals & TIOCM_DTR)
561		otherpart->modemsignals |= TIOCM_CAR;
562}
563
564/*
565 * Module handling
566 */
567static int
568nmdm_modevent(module_t mod, int type, void *data)
569{
570        int error = 0;
571
572        switch(type) {
573        case MOD_LOAD: /* start with 4 of them */
574		nmdminit(0);
575		nmdminit(1);
576		nmdminit(2);
577		nmdminit(3);
578		break;
579
580	case MOD_SHUTDOWN:
581		/* FALLTHROUGH */
582	case MOD_UNLOAD:
583		nmdmshutdown();
584		break;
585	default:
586		error = EOPNOTSUPP;
587	}
588	return (error);
589}
590
591/*
592 * Handle teardown of device
593 */
594static int
595nmdmshutdown(void)
596{
597	int i;
598	dev_t	nextdev1;
599	dev_t	nextdev2;
600	void * ptr1;
601
602	for(i = 0;( i < NMDM_MAX_NUM) ;i++) {
603		nextdev1 = makedev(CDEV_MAJOR, (i+i) );
604		nextdev2 = makedev(CDEV_MAJOR, (i+i) + 1);
605		ptr1 = nextdev1->si_drv1;
606		if (ptr1) {
607			revoke_and_destroy_dev(nextdev1);
608			revoke_and_destroy_dev(nextdev2);
609			free(ptr1, M_NLMDM);
610		} else {
611			freedev(nextdev1);
612			freedev(nextdev2);
613		}
614	}
615	return(0);
616}
617
618DEV_MODULE(nmdm, nmdm_modevent, NULL);
619