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