xref: /freebsd/sys/dev/nmdm/nmdm.c (revision 5521ff5a4d1929056e7ffc982fac3341ca54df7c)
1 /*
2  * Copyright (c) 1982, 1986, 1989, 1993
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. All advertising materials mentioning features or use of this software
14  *    must display the following acknowledgement:
15  *	This product includes software developed by the University of
16  *	California, Berkeley and its contributors.
17  * 4. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * $FreeBSD$
34  */
35 
36 /*
37  * Pseudo-nulmodem Driver
38  */
39 #include "opt_compat.h"
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #if defined(COMPAT_43) || defined(COMPAT_SUNOS)
43 #include <sys/ioctl_compat.h>
44 #endif
45 #include <sys/proc.h>
46 #include <sys/tty.h>
47 #include <sys/conf.h>
48 #include <sys/fcntl.h>
49 #include <sys/poll.h>
50 #include <sys/kernel.h>
51 #include <sys/vnode.h>
52 #include <sys/signalvar.h>
53 #include <sys/malloc.h>
54 
55 MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures");
56 
57 static void nmdmstart __P((struct tty *tp));
58 static void nmdmstop __P((struct tty *tp, int rw));
59 static void wakeup_other __P((struct tty *tp, int flag));
60 static void nmdminit __P((int n));
61 
62 static	d_open_t	nmdmopen;
63 static	d_close_t	nmdmclose;
64 static	d_read_t	nmdmread;
65 static	d_write_t	nmdmwrite;
66 static	d_ioctl_t	nmdmioctl;
67 
68 #define	CDEV_MAJOR	18
69 static struct cdevsw nmdm_cdevsw = {
70 	/* open */	nmdmopen,
71 	/* close */	nmdmclose,
72 	/* read */	nmdmread,
73 	/* write */	nmdmwrite,
74 	/* ioctl */	nmdmioctl,
75 	/* poll */	ttypoll,
76 	/* mmap */	nommap,
77 	/* strategy */	nostrategy,
78 	/* name */	"pts",
79 	/* maj */	CDEV_MAJOR,
80 	/* dump */	nodump,
81 	/* psize */	nopsize,
82 	/* flags */	D_TTY,
83 };
84 
85 #define BUFSIZ 100		/* Chunk size iomoved to/from user */
86 
87 struct softpart {
88 	struct tty nm_tty;
89 	dev_t	dev;
90 	int	modemsignals;	/* bits defined in sys/ttycom.h */
91 	int	gotbreak;
92 };
93 
94 struct	nm_softc {
95 	int	pt_flags;
96 	struct softpart part1, part2;
97 	struct	prison *pt_prison;
98 };
99 
100 #define	PF_STOPPED	0x10		/* user told stopped */
101 
102 static void
103 nmdm_crossover(struct nm_softc *pti,
104 		struct softpart *ourpart,
105 		struct softpart *otherpart);
106 
107 #define GETPARTS(tp, ourpart, otherpart) \
108 do {	\
109 	struct nm_softc *pti = tp->t_dev->si_drv1; \
110 	if (tp == &pti->part1.nm_tty) { \
111 		ourpart = &pti->part1; \
112 		otherpart = &pti->part2; \
113 	} else { \
114 		ourpart = &pti->part2; \
115 		otherpart = &pti->part1; \
116 	}  \
117 } while (0)
118 
119 /*
120  * This function creates and initializes a pair of ttys.
121  */
122 static void
123 nmdminit(n)
124 	int n;
125 {
126 	dev_t dev1, dev2;
127 	struct nm_softc *pt;
128 
129 	/* For now we only map the lower 8 bits of the minor */
130 	if (n & ~0xff)
131 		return;
132 
133 	pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK);
134 	bzero(pt, sizeof(*pt));
135 	pt->part1.dev = dev1 = make_dev(&nmdm_cdevsw, n+n,
136 	    0, 0, 0666, "nmdm%dA", n);
137 	pt->part2.dev = dev2 = make_dev(&nmdm_cdevsw, n+n+1,
138 	    0, 0, 0666, "nmdm%dB", n);
139 
140 	dev1->si_drv1 = dev2->si_drv1 = pt;
141 	dev1->si_tty = &pt->part1.nm_tty;
142 	dev2->si_tty = &pt->part2.nm_tty;
143 	ttyregister(&pt->part1.nm_tty);
144 	ttyregister(&pt->part2.nm_tty);
145 	pt->part1.nm_tty.t_oproc = nmdmstart;
146 	pt->part2.nm_tty.t_oproc = nmdmstart;
147 	pt->part1.nm_tty.t_stop = nmdmstop;
148 	pt->part2.nm_tty.t_dev = dev1;
149 	pt->part1.nm_tty.t_dev = dev2;
150 	pt->part2.nm_tty.t_stop = nmdmstop;
151 }
152 
153 /*ARGSUSED*/
154 static	int
155 nmdmopen(dev, flag, devtype, p)
156 	dev_t dev;
157 	int flag, devtype;
158 	struct proc *p;
159 {
160 	register struct tty *tp, *tp2;
161 	int error;
162 	int minr;
163 	dev_t nextdev;
164 	struct nm_softc *pti;
165 	int is_b;
166 	int	pair;
167 	struct	softpart *ourpart, *otherpart;
168 
169 	/*
170 	 * XXX: Gross hack for DEVFS:
171 	 * If we openned this device, ensure we have the
172 	 * next one too, so people can open it.
173 	 */
174 	minr = dev2unit(dev);
175 	pair = minr >> 1;
176 	is_b = minr & 1;
177 
178 	if (pair < 127) {
179 		nextdev = makedev(major(dev), (pair+pair) + 1);
180 		if (!nextdev->si_drv1) {
181 			nmdminit(pair + 1);
182 		}
183 	}
184 	if (!dev->si_drv1)
185 		nmdminit(pair);
186 
187 	if (!dev->si_drv1)
188 		return(ENXIO);
189 
190 	pti = dev->si_drv1;
191 	if (is_b)
192 		tp = &pti->part2.nm_tty;
193 	else
194 		tp = &pti->part1.nm_tty;
195 	GETPARTS(tp, ourpart, otherpart);
196 	tp2 = &otherpart->nm_tty;
197 	ourpart->modemsignals |= TIOCM_LE;
198 
199 	if ((tp->t_state & TS_ISOPEN) == 0) {
200 		ttychars(tp);		/* Set up default chars */
201 		tp->t_iflag = TTYDEF_IFLAG;
202 		tp->t_oflag = TTYDEF_OFLAG;
203 		tp->t_lflag = TTYDEF_LFLAG;
204 		tp->t_cflag = TTYDEF_CFLAG;
205 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
206 	} else if (tp->t_state & TS_XCLUDE && suser(p)) {
207 		return (EBUSY);
208 	} else if (pti->pt_prison != p->p_ucred->cr_prison) {
209 		return (EBUSY);
210 	}
211 
212 	/*
213 	 * If the other side is open we have carrier
214 	 */
215 	if (tp2->t_state & TS_ISOPEN) {
216 		(void)(*linesw[tp->t_line].l_modem)(tp, 1);
217 	}
218 
219 	/*
220 	 * And the other side gets carrier as we are now open.
221 	 */
222 	(void)(*linesw[tp2->t_line].l_modem)(tp2, 1);
223 
224 	/* External processing makes no sense here */
225 	tp->t_lflag &= ~EXTPROC;
226 
227 	/*
228 	 * Wait here if we don't have carrier.
229 	 */
230 #if 0
231 	while ((tp->t_state & TS_CARR_ON) == 0) {
232 		if (flag & FNONBLOCK)
233 			break;
234 		error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH,
235 				 "nmdopn", 0);
236 		if (error)
237 			return (error);
238 	}
239 #endif
240 
241 	/*
242 	 * Give the line disciplin a chance to set this end up.
243 	 */
244 	error = (*linesw[tp->t_line].l_open)(dev, tp);
245 
246 	/*
247 	 * Wake up the other side.
248 	 * Theoretically not needed.
249 	 */
250 	ourpart->modemsignals |= TIOCM_DTR;
251 	nmdm_crossover(pti, ourpart, otherpart);
252 	if (error == 0)
253 		wakeup_other(tp, FREAD|FWRITE); /* XXX */
254 	return (error);
255 }
256 
257 static	int
258 nmdmclose(dev, flag, mode, p)
259 	dev_t dev;
260 	int flag, mode;
261 	struct proc *p;
262 {
263 	register struct tty *tp, *tp2;
264 	int err;
265 	struct softpart *ourpart, *otherpart;
266 
267 	/*
268 	 * let the other end know that the game is up
269 	 */
270 	tp = dev->si_tty;
271 	GETPARTS(tp, ourpart, otherpart);
272 	tp2 = &otherpart->nm_tty;
273 	(void)(*linesw[tp2->t_line].l_modem)(tp2, 0);
274 
275 	/*
276 	 * XXX MDMBUF makes no sense for nmdms but would inhibit the above
277 	 * l_modem().  CLOCAL makes sense but isn't supported.   Special
278 	 * l_modem()s that ignore carrier drop make no sense for nmdms but
279 	 * may be in use because other parts of the line discipline make
280 	 * sense for nmdms.  Recover by doing everything that a normal
281 	 * ttymodem() would have done except for sending a SIGHUP.
282 	 */
283 	if (tp2->t_state & TS_ISOPEN) {
284 		tp2->t_state &= ~(TS_CARR_ON | TS_CONNECTED);
285 		tp2->t_state |= TS_ZOMBIE;
286 		ttyflush(tp2, FREAD | FWRITE);
287 	}
288 
289 	err = (*linesw[tp->t_line].l_close)(tp, flag);
290 	ourpart->modemsignals &= ~TIOCM_DTR;
291 	nmdm_crossover(dev->si_drv1, ourpart, otherpart);
292 	nmdmstop(tp, FREAD|FWRITE);
293 	(void) ttyclose(tp);
294 	return (err);
295 }
296 
297 static	int
298 nmdmread(dev, uio, flag)
299 	dev_t dev;
300 	struct uio *uio;
301 	int flag;
302 {
303 	int error = 0;
304 	struct tty *tp, *tp2;
305 	struct softpart *ourpart, *otherpart;
306 
307 	tp = dev->si_tty;
308 	GETPARTS(tp, ourpart, otherpart);
309 	tp2 = &otherpart->nm_tty;
310 
311 #if 0
312 	if (tp2->t_state & TS_ISOPEN) {
313 		error = (*linesw[tp->t_line].l_read)(tp, uio, flag);
314 		wakeup_other(tp, FWRITE);
315 	} else {
316 		if (flag & IO_NDELAY) {
317 			return (EWOULDBLOCK);
318 		}
319 		error = tsleep(TSA_PTC_READ(tp),
320 				TTIPRI | PCATCH, "nmdout", 0);
321 		}
322 	}
323 #else
324 	if ((error = (*linesw[tp->t_line].l_read)(tp, uio, flag)) == 0)
325 		wakeup_other(tp, FWRITE);
326 #endif
327 	return (error);
328 }
329 
330 /*
331  * Write to pseudo-tty.
332  * Wakeups of controlling tty will happen
333  * indirectly, when tty driver calls nmdmstart.
334  */
335 static	int
336 nmdmwrite(dev, uio, flag)
337 	dev_t dev;
338 	struct uio *uio;
339 	int flag;
340 {
341 	register u_char *cp = 0;
342 	register int cc = 0;
343 	u_char locbuf[BUFSIZ];
344 	int cnt = 0;
345 	int error = 0;
346 	struct tty *tp1, *tp;
347 	struct softpart *ourpart, *otherpart;
348 
349 	tp1 = dev->si_tty;
350 	/*
351 	 * Get the other tty struct.
352 	 * basically we are writing into the INPUT side of the other device.
353 	 */
354 	GETPARTS(tp1, ourpart, otherpart);
355 	tp = &otherpart->nm_tty;
356 
357 again:
358 	if ((tp->t_state & TS_ISOPEN) == 0)
359 		return (EIO);
360 	while (uio->uio_resid > 0 || cc > 0) {
361 		/*
362 		 * Fill up the buffer if it's empty
363 		 */
364 		if (cc == 0) {
365 			cc = min(uio->uio_resid, BUFSIZ);
366 			cp = locbuf;
367 			error = uiomove((caddr_t)cp, cc, uio);
368 			if (error)
369 				return (error);
370 			/* check again for safety */
371 			if ((tp->t_state & TS_ISOPEN) == 0) {
372 				/* adjust for data copied in but not written */
373 				uio->uio_resid += cc;
374 				return (EIO);
375 			}
376 		}
377 		while (cc > 0) {
378 			if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2))
379 			&& ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) {
380 				/*
381 	 			 * Come here to wait for space in outq,
382 				 * or space in rawq, or an empty canq.
383 	 			 */
384 				wakeup(TSA_HUP_OR_INPUT(tp));
385 				if ((tp->t_state & TS_CONNECTED) == 0) {
386 					/*
387 					 * Data piled up because not connected.
388 					 * Adjust for data copied in but
389 					 * not written.
390 					 */
391 					uio->uio_resid += cc;
392 					return (EIO);
393 				}
394 				if (flag & IO_NDELAY) {
395 					/*
396 				         * Don't wait if asked not to.
397 					 * Adjust for data copied in but
398 					 * not written.
399 					 */
400 					uio->uio_resid += cc;
401 					if (cnt == 0)
402 						return (EWOULDBLOCK);
403 					return (0);
404 				}
405 				error = tsleep(TSA_PTC_WRITE(tp),
406 						TTOPRI | PCATCH, "nmdout", 0);
407 				if (error) {
408 					/*
409 					 * Tsleep returned (signal?).
410 					 * Go find out what the user wants.
411 					 * adjust for data copied in but
412 					 * not written
413 					 */
414 					uio->uio_resid += cc;
415 					return (error);
416 				}
417 				goto again;
418 			}
419 			(*linesw[tp->t_line].l_rint)(*cp++, tp);
420 			cnt++;
421 			cc--;
422 		}
423 		cc = 0;
424 	}
425 	return (0);
426 }
427 
428 /*
429  * Start output on pseudo-tty.
430  * Wake up process selecting or sleeping for input from controlling tty.
431  */
432 static void
433 nmdmstart(tp)
434 	struct tty *tp;
435 {
436 	register struct nm_softc *pti = tp->t_dev->si_drv1;
437 
438 	if (tp->t_state & TS_TTSTOP)
439 		return;
440 	pti->pt_flags &= ~PF_STOPPED;
441 	wakeup_other(tp, FREAD);
442 }
443 
444 /* Wakes up the OTHER tty;*/
445 static void
446 wakeup_other(tp, flag)
447 	struct tty *tp;
448 	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 static	void
464 nmdmstop(tp, flush)
465 	register struct tty *tp;
466 	int flush;
467 {
468 	struct nm_softc *pti = tp->t_dev->si_drv1;
469 	int flag;
470 
471 	/* note: FLUSHREAD and FLUSHWRITE already ok */
472 	if (flush == 0) {
473 		flush = TIOCPKT_STOP;
474 		pti->pt_flags |= PF_STOPPED;
475 	} else
476 		pti->pt_flags &= ~PF_STOPPED;
477 	/* change of perspective */
478 	flag = 0;
479 	if (flush & FREAD)
480 		flag |= FWRITE;
481 	if (flush & FWRITE)
482 		flag |= FREAD;
483 	wakeup_other(tp, flag);
484 }
485 
486 #if 0
487 static int
488 nmdmpoll(dev_t dev, int events, struct proc *p)
489 {
490 	register struct tty *tp = dev->si_tty;
491 	register struct tty *tp2;
492 	int revents = 0;
493 	int s;
494 	struct softpart *ourpart, *otherpart;
495 
496 	GETPARTS(tp, ourpart, otherpart);
497 	tp2 = &otherpart->nm_tty;
498 
499 	if ((tp->t_state & TS_CONNECTED) == 0)
500 		return (seltrue(dev, events, p) | POLLHUP);
501 
502 	/*
503 	 * Need to block timeouts (ttrstart).
504 	 */
505 	s = spltty();
506 
507 	/*
508 	 * First check if there is something to report immediatly.
509 	 */
510 	if ((events & (POLLIN | POLLRDNORM))) {
511 		if (tp->t_iflag & ICANON)  {
512 			if (tp->t_canq.c_cc)
513 				revents |= events & (POLLIN | POLLRDNORM);
514 		} else {
515 			if (tp->t_rawq.c_cc + tp->t_canq.c_cc)
516 				revents |= events & (POLLIN | POLLRDNORM);
517 		}
518 	}
519 
520 	/*
521 	 * check if there is room in the other tty's input buffers.
522 	 */
523 	if ((events & (POLLOUT | POLLWRNORM))
524 	&& ((tp2->t_rawq.c_cc + tp2->t_canq.c_cc < TTYHOG - 2)
525 	   || (tp2->t_canq.c_cc == 0 && (tp2->t_iflag & ICANON)))) {
526 			revents |= events & (POLLOUT | POLLWRNORM);
527 	}
528 
529 	if (events & POLLHUP)
530 		if ((tp->t_state & TS_CARR_ON) == 0)
531 			revents |= POLLHUP;
532 
533 	/*
534 	 * If nothing immediate, set us to return when something IS found.
535 	 */
536 	if (revents == 0) {
537 		if (events & (POLLIN | POLLRDNORM))
538 			selrecord(p, &tp->t_rsel);
539 
540 		if (events & (POLLOUT | POLLWRNORM))
541 			selrecord(p, &tp->t_wsel);
542 	}
543 	splx(s);
544 
545 	return (revents);
546 }
547 #endif	/* 0 */
548 
549 /*ARGSUSED*/
550 static	int
551 nmdmioctl(dev, cmd, data, flag, p)
552 	dev_t dev;
553 	u_long cmd;
554 	caddr_t data;
555 	int flag;
556 	struct proc *p;
557 {
558 	register struct tty *tp = dev->si_tty;
559 	struct nm_softc *pti = dev->si_drv1;
560 	int error, s;
561 	register struct tty *tp2;
562 	struct softpart *ourpart, *otherpart;
563 
564 	s = spltty();
565 	GETPARTS(tp, ourpart, otherpart);
566 	tp2 = &otherpart->nm_tty;
567 
568 	error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
569 	if (error == ENOIOCTL)
570 		 error = ttioctl(tp, cmd, data, flag);
571 	if (error == ENOIOCTL) {
572 		switch (cmd) {
573 		case TIOCSBRK:
574 			otherpart->gotbreak = 1;
575 			break;
576 		case TIOCCBRK:
577 			break;
578 		case TIOCSDTR:
579 			ourpart->modemsignals |= TIOCM_DTR;
580 			break;
581 		case TIOCCDTR:
582 			ourpart->modemsignals &= TIOCM_DTR;
583 			break;
584 		case TIOCMSET:
585 			ourpart->modemsignals = *(int *)data;
586 			otherpart->modemsignals = *(int *)data;
587 			break;
588 		case TIOCMBIS:
589 			ourpart->modemsignals |= *(int *)data;
590 			break;
591 		case TIOCMBIC:
592 			ourpart->modemsignals &= ~(*(int *)data);
593 			otherpart->modemsignals &= ~(*(int *)data);
594 			break;
595 		case TIOCMGET:
596 			*(int *)data = ourpart->modemsignals;
597 			break;
598 		case TIOCMSDTRWAIT:
599 			break;
600 		case TIOCMGDTRWAIT:
601 			*(int *)data = 0;
602 			break;
603 		case TIOCTIMESTAMP:
604 		case TIOCDCDTIMESTAMP:
605 		default:
606 			splx(s);
607 			error = ENOTTY;
608 			return (error);
609 		}
610 		error = 0;
611 		nmdm_crossover(pti, ourpart, otherpart);
612 	}
613 	splx(s);
614 	return (error);
615 }
616 
617 static void
618 nmdm_crossover(struct nm_softc *pti,
619 		struct softpart *ourpart,
620 		struct softpart *otherpart)
621 {
622 	otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR);
623 	if (ourpart->modemsignals & TIOCM_RTS)
624 		otherpart->modemsignals |= TIOCM_CTS;
625 	if (ourpart->modemsignals & TIOCM_DTR)
626 		otherpart->modemsignals |= TIOCM_CAR;
627 }
628 
629 
630 
631 static void nmdm_drvinit __P((void *unused));
632 
633 static void
634 nmdm_drvinit(unused)
635 	void *unused;
636 {
637 	cdevsw_add(&nmdm_cdevsw);
638 	/* XXX: Gross hack for DEVFS */
639 	nmdminit(0);
640 }
641 
642 SYSINIT(nmdmdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,nmdm_drvinit,NULL)
643