xref: /dragonfly/sys/dev/misc/nmdm/nmdm.c (revision 6a3cbbc2)
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  * Pseudo-nulmodem Driver
36  */
37 #include <sys/param.h>
38 #include <sys/systm.h>
39 #include <sys/uio.h>
40 #include <sys/proc.h>
41 #include <sys/priv.h>
42 #include <sys/tty.h>
43 #include <sys/ttydefaults.h>	/* for TTYDEF_* */
44 #include <sys/conf.h>
45 #include <sys/fcntl.h>
46 #include <sys/kernel.h>
47 #include <sys/vnode.h>
48 #include <sys/signalvar.h>
49 #include <sys/malloc.h>
50 
51 MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures");
52 
53 static void nmdmstart (struct tty *tp);
54 static void nmdmstop (struct tty *tp, int rw);
55 static void wakeup_other (struct tty *tp, int flag);
56 static void nmdminit (int n);
57 
58 static	d_open_t	nmdmopen;
59 static	d_close_t	nmdmclose;
60 static	d_read_t	nmdmread;
61 static	d_write_t	nmdmwrite;
62 static	d_ioctl_t	nmdmioctl;
63 
64 #define	CDEV_MAJOR	18
65 static struct dev_ops nmdm_ops = {
66 	{ "pts", 0, D_TTY },
67 	.d_open =	nmdmopen,
68 	.d_close =	nmdmclose,
69 	.d_read =	nmdmread,
70 	.d_write =	nmdmwrite,
71 	.d_ioctl =	nmdmioctl,
72 	.d_kqfilter = 	ttykqfilter,
73 	.d_revoke =	ttyrevoke
74 };
75 
76 #define BUFSIZ 100		/* Chunk size iomoved to/from user */
77 
78 struct softpart {
79 	struct tty nm_tty;
80 	cdev_t	dev;
81 	int	modemsignals;	/* bits defined in sys/ttycom.h */
82 	int	gotbreak;
83 };
84 
85 struct	nm_softc {
86 	int	pt_flags;
87 	struct softpart part1, part2;
88 	struct	prison *pt_prison;
89 };
90 
91 #define	PF_STOPPED	0x10		/* user told stopped */
92 
93 static void
94 nmdm_crossover(struct nm_softc *pti,
95 		struct softpart *ourpart,
96 		struct softpart *otherpart);
97 
98 #define GETPARTS(tp, ourpart, otherpart) \
99 do {	\
100 	struct nm_softc *pti = tp->t_dev->si_drv1; \
101 	if (tp == &pti->part1.nm_tty) { \
102 		ourpart = &pti->part1; \
103 		otherpart = &pti->part2; \
104 	} else { \
105 		ourpart = &pti->part2; \
106 		otherpart = &pti->part1; \
107 	}  \
108 } while (0)
109 
110 /*
111  * This function creates and initializes a pair of ttys.
112  */
113 static void
114 nmdminit(int n)
115 {
116 	cdev_t dev1, dev2;
117 	struct nm_softc *pt;
118 
119 	/*
120 	 * Simplified unit number, use low 8 bits of minor number
121 	 * (remember, the minor number mask is 0xffff00ff).
122 	 */
123 	if (n & ~0x7f)
124 		return;
125 
126 	pt = kmalloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO);
127 	pt->part1.dev = dev1 = make_dev(&nmdm_ops, n << 1,
128 					0, 0, 0666, "nmdm%dA", n);
129 	pt->part2.dev = dev2 = make_dev(&nmdm_ops, (n << 1) + 1,
130 					0, 0, 0666, "nmdm%dB", n);
131 
132 	dev1->si_drv1 = dev2->si_drv1 = pt;
133 	dev1->si_tty = &pt->part1.nm_tty;
134 	dev2->si_tty = &pt->part2.nm_tty;
135 	ttyinit(&pt->part1.nm_tty);
136 	ttyinit(&pt->part2.nm_tty);
137 	ttyregister(&pt->part1.nm_tty);
138 	ttyregister(&pt->part2.nm_tty);
139 	pt->part1.nm_tty.t_oproc = nmdmstart;
140 	pt->part2.nm_tty.t_oproc = nmdmstart;
141 	pt->part1.nm_tty.t_stop = nmdmstop;
142 	pt->part2.nm_tty.t_dev = dev1;
143 	pt->part1.nm_tty.t_dev = dev2;
144 	pt->part2.nm_tty.t_stop = nmdmstop;
145 }
146 
147 /*ARGSUSED*/
148 static	int
149 nmdmopen(struct dev_open_args *ap)
150 {
151 	cdev_t dev = ap->a_head.a_dev;
152 	struct tty *tp, *tp2;
153 	int error;
154 	int minr;
155 #if 0
156 	cdev_t nextdev;
157 #endif
158 	struct nm_softc *pti;
159 	int is_b;
160 	int	pair;
161 	struct	softpart *ourpart, *otherpart;
162 
163 	minr = lminor(dev);
164 	pair = minr >> 1;
165 	is_b = minr & 1;
166 
167 #if 0
168 	/*
169 	 * XXX: Gross hack for DEVFS:
170 	 * If we openned this device, ensure we have the
171 	 * next one too, so people can open it.
172 	 */
173 	if (pair < 127) {
174 		nextdev = makedev(major(dev), (pair+pair) + 1);
175 		if (!nextdev->si_drv1) {
176 			nmdminit(pair + 1);
177 		}
178 	}
179 #endif
180 	if (!dev->si_drv1)
181 		nmdminit(pair);
182 
183 	if (!dev->si_drv1)
184 		return(ENXIO);
185 
186 	pti = dev->si_drv1;
187 	if (is_b)
188 		tp = &pti->part2.nm_tty;
189 	else
190 		tp = &pti->part1.nm_tty;
191 	lwkt_gettoken(&tp->t_token);
192 	GETPARTS(tp, ourpart, otherpart);
193 	tp2 = &otherpart->nm_tty;
194 	lwkt_gettoken(&tp2->t_token);
195 	ourpart->modemsignals |= TIOCM_LE;
196 
197 	if ((tp->t_state & TS_ISOPEN) == 0) {
198 		ttychars(tp);		/* Set up default chars */
199 		tp->t_iflag = TTYDEF_IFLAG;
200 		tp->t_oflag = TTYDEF_OFLAG;
201 		tp->t_lflag = TTYDEF_LFLAG;
202 		tp->t_cflag = TTYDEF_CFLAG;
203 		tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
204 	} else if (tp->t_state & TS_XCLUDE && priv_check_cred(ap->a_cred, PRIV_ROOT, 0)) {
205 		lwkt_reltoken(&tp2->t_token);
206 		lwkt_reltoken(&tp->t_token);
207 
208 		return (EBUSY);
209 	} else if (pti->pt_prison != ap->a_cred->cr_prison) {
210 		lwkt_reltoken(&tp2->t_token);
211 		lwkt_reltoken(&tp->t_token);
212 
213 		return (EBUSY);
214 	}
215 
216 	/*
217 	 * If the other side is open we have carrier
218 	 */
219 	if (tp2->t_state & TS_ISOPEN) {
220 		(void)(*linesw[tp->t_line].l_modem)(tp, 1);
221 	}
222 
223 	/*
224 	 * And the other side gets carrier as we are now open.
225 	 */
226 	(void)(*linesw[tp2->t_line].l_modem)(tp2, 1);
227 
228 	/* External processing makes no sense here */
229 	tp->t_lflag &= ~EXTPROC;
230 
231 	/*
232 	 * Wait here if we don't have carrier.
233 	 */
234 #if 0
235 	while ((tp->t_state & TS_CARR_ON) == 0) {
236 		if (flag & FNONBLOCK)
237 			break;
238 		error = ttysleep(tp, TSA_CARR_ON(tp), PCATCH, "nmdopn", 0);
239 		if (error) {
240 			lwkt_reltoken(&tp2->t_token);
241 			lwkt_reltoken(&tp->t_token);
242 
243 			return (error);
244 		}
245 	}
246 #endif
247 
248 	/*
249 	 * Give the line disciplin a chance to set this end up.
250 	 */
251 	error = (*linesw[tp->t_line].l_open)(dev, tp);
252 
253 	/*
254 	 * Wake up the other side.
255 	 * Theoretically not needed.
256 	 */
257 	ourpart->modemsignals |= TIOCM_DTR;
258 	nmdm_crossover(pti, ourpart, otherpart);
259 	if (error == 0)
260 		wakeup_other(tp, FREAD|FWRITE); /* XXX */
261 	lwkt_reltoken(&tp2->t_token);
262 	lwkt_reltoken(&tp->t_token);
263 
264 	return (error);
265 }
266 
267 static int
268 nmdmclose(struct dev_close_args *ap)
269 {
270 	cdev_t dev = ap->a_head.a_dev;
271 	struct tty *tp, *tp2;
272 	int err;
273 	struct softpart *ourpart, *otherpart;
274 
275 	/*
276 	 * let the other end know that the game is up
277 	 */
278 	tp = dev->si_tty;
279 	lwkt_gettoken(&tp->t_token);
280 	GETPARTS(tp, ourpart, otherpart);
281 	tp2 = &otherpart->nm_tty;
282 	lwkt_gettoken(&tp2->t_token);
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 	ttyclose(tp);
304 	lwkt_reltoken(&tp2->t_token);
305 	lwkt_reltoken(&tp->t_token);
306 
307 	return (err);
308 }
309 
310 static int
311 nmdmread(struct dev_read_args *ap)
312 {
313 	cdev_t dev = ap->a_head.a_dev;
314 	int error = 0;
315 	struct tty *tp;
316 #if 0
317 	struct tty *tp2;
318 	struct softpart *ourpart, *otherpart;
319 #endif
320 
321 	tp = dev->si_tty;
322 	lwkt_gettoken(&tp->t_token);
323 #if 0
324 	GETPARTS(tp, ourpart, otherpart);
325 	tp2 = &otherpart->nm_tty;
326 	lwkt_gettoken(&tp2->t_token);
327 
328 	if (tp2->t_state & TS_ISOPEN) {
329 		error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, flag);
330 		wakeup_other(tp, FWRITE);
331 	} else {
332 		if (flag & IO_NDELAY) {
333 			lwkt_reltoken(&tp2->t_token);
334 			lwkt_reltoken(&tp->t_token);
335 			return (EWOULDBLOCK);
336 		}
337 		error = tsleep(TSA_PTC_READ(tp), PCATCH, "nmdout", 0);
338 		}
339 	}
340 	lwkt_reltoken(&tp2->t_token);
341 #else
342 	if ((error = (*linesw[tp->t_line].l_read)(tp, ap->a_uio, ap->a_ioflag)) == 0)
343 		wakeup_other(tp, FWRITE);
344 #endif
345 	lwkt_reltoken(&tp->t_token);
346 
347 	return (error);
348 }
349 
350 /*
351  * Write to pseudo-tty.
352  * Wakeups of controlling tty will happen
353  * indirectly, when tty driver calls nmdmstart.
354  */
355 static	int
356 nmdmwrite(struct dev_write_args *ap)
357 {
358 	cdev_t dev = ap->a_head.a_dev;
359 	struct uio *uio = ap->a_uio;
360 	u_char *cp = NULL;
361 	size_t cc = 0;
362 	u_char locbuf[BUFSIZ];
363 	int cnt = 0;
364 	int error = 0;
365 	struct tty *tp1, *tp;
366 	struct softpart *ourpart, *otherpart;
367 
368 	tp1 = dev->si_tty;
369 	lwkt_gettoken(&tp1->t_token);
370 	/*
371 	 * Get the other tty struct.
372 	 * basically we are writing into the INPUT side of the other device.
373 	 */
374 	GETPARTS(tp1, ourpart, otherpart);
375 	tp = &otherpart->nm_tty;
376 	lwkt_gettoken(&tp->t_token);
377 
378 again:
379 	if ((tp->t_state & TS_ISOPEN) == 0) {
380 		lwkt_reltoken(&tp->t_token);
381 		lwkt_reltoken(&tp1->t_token);
382 		return (EIO);
383 	}
384 	while (uio->uio_resid > 0 || cc > 0) {
385 		/*
386 		 * Fill up the buffer if it's empty
387 		 */
388 		if (cc == 0) {
389 			cc = szmin(uio->uio_resid, BUFSIZ);
390 			cp = locbuf;
391 			error = uiomove((caddr_t)cp, cc, uio);
392 			if (error) {
393 				lwkt_reltoken(&tp->t_token);
394 				lwkt_reltoken(&tp1->t_token);
395 				return (error);
396 			}
397 			/* check again for safety */
398 			if ((tp->t_state & TS_ISOPEN) == 0) {
399 				/* adjust for data copied in but not written */
400 				uio->uio_resid += cc;
401 				lwkt_reltoken(&tp->t_token);
402 				lwkt_reltoken(&tp1->t_token);
403 				return (EIO);
404 			}
405 		}
406 		while (cc > 0) {
407 			if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2))
408 			&& ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) {
409 				/*
410 	 			 * Come here to wait for space in outq,
411 				 * or space in rawq, or an empty canq.
412 	 			 */
413 				wakeup(TSA_HUP_OR_INPUT(tp));
414 				if ((tp->t_state & TS_CONNECTED) == 0) {
415 					/*
416 					 * Data piled up because not connected.
417 					 * Adjust for data copied in but
418 					 * not written.
419 					 */
420 					uio->uio_resid += cc;
421 					lwkt_reltoken(&tp->t_token);
422 					lwkt_reltoken(&tp1->t_token);
423 					return (EIO);
424 				}
425 				if (ap->a_ioflag & IO_NDELAY) {
426 					/*
427 				         * Don't wait if asked not to.
428 					 * Adjust for data copied in but
429 					 * not written.
430 					 */
431 					uio->uio_resid += cc;
432 					if (cnt == 0) {
433 						lwkt_reltoken(&tp->t_token);
434 						lwkt_reltoken(&tp1->t_token);
435 						return (EWOULDBLOCK);
436 					}
437 					lwkt_reltoken(&tp->t_token);
438 					lwkt_reltoken(&tp1->t_token);
439 					return (0);
440 				}
441 				error = tsleep(TSA_PTC_WRITE(tp),
442 						PCATCH, "nmdout", 0);
443 				if (error) {
444 					/*
445 					 * Tsleep returned (signal?).
446 					 * Go find out what the user wants.
447 					 * adjust for data copied in but
448 					 * not written
449 					 */
450 					uio->uio_resid += cc;
451 					lwkt_reltoken(&tp->t_token);
452 					lwkt_reltoken(&tp1->t_token);
453 					return (error);
454 				}
455 				goto again;
456 			}
457 			(*linesw[tp->t_line].l_rint)(*cp++, tp);
458 			cnt++;
459 			cc--;
460 		}
461 		cc = 0;
462 	}
463 	lwkt_reltoken(&tp->t_token);
464 	lwkt_reltoken(&tp1->t_token);
465 
466 	return (0);
467 }
468 
469 /*
470  * Start output on pseudo-tty.
471  * Wake up process selecting or sleeping for input from controlling tty.
472  */
473 static void
474 nmdmstart(struct tty *tp)
475 {
476 	struct nm_softc *pti = tp->t_dev->si_drv1;
477 
478 	lwkt_gettoken(&tp->t_token);
479 	if (tp->t_state & TS_TTSTOP) {
480 		lwkt_reltoken(&tp->t_token);
481 		return;
482 	}
483 	pti->pt_flags &= ~PF_STOPPED;
484 	wakeup_other(tp, FREAD);
485 	lwkt_reltoken(&tp->t_token);
486 }
487 
488 /*
489  * Wakes up the OTHER tty.  Caller must hold at least tp->t_token.
490  */
491 static void
492 wakeup_other(struct tty *tp, int flag)
493 {
494 	struct softpart *ourpart, *otherpart;
495 
496 	GETPARTS(tp, ourpart, otherpart);
497 	lwkt_gettoken(&otherpart->nm_tty.t_token);
498 	if (flag & FREAD) {
499 		wakeup(TSA_PTC_READ((&otherpart->nm_tty)));
500 		KNOTE(&otherpart->nm_tty.t_rkq.ki_note, 0);
501 	}
502 	if (flag & FWRITE) {
503 		wakeup(TSA_PTC_WRITE((&otherpart->nm_tty)));
504 		KNOTE(&otherpart->nm_tty.t_wkq.ki_note, 0);
505 	}
506 	lwkt_reltoken(&otherpart->nm_tty.t_token);
507 }
508 
509 static	void
510 nmdmstop(struct tty *tp, int flush)
511 {
512 	struct nm_softc *pti = tp->t_dev->si_drv1;
513 	int flag;
514 
515 	lwkt_gettoken(&tp->t_token);
516 	/* note: FLUSHREAD and FLUSHWRITE already ok */
517 	if (flush == 0) {
518 		flush = TIOCPKT_STOP;
519 		pti->pt_flags |= PF_STOPPED;
520 	} else
521 		pti->pt_flags &= ~PF_STOPPED;
522 	/* change of perspective */
523 	flag = 0;
524 	if (flush & FREAD)
525 		flag |= FWRITE;
526 	if (flush & FWRITE)
527 		flag |= FREAD;
528 	wakeup_other(tp, flag);
529 	lwkt_reltoken(&tp->t_token);
530 }
531 
532 /*ARGSUSED*/
533 static	int
534 nmdmioctl(struct dev_ioctl_args *ap)
535 {
536 	cdev_t dev = ap->a_head.a_dev;
537 	struct tty *tp = dev->si_tty;
538 	struct tty *tp2;
539 	struct nm_softc *pti = dev->si_drv1;
540 	int error;
541 	struct softpart *ourpart, *otherpart;
542 
543 	lwkt_gettoken(&tp->t_token);
544 	GETPARTS(tp, ourpart, otherpart);
545 	tp2 = &otherpart->nm_tty;
546 	lwkt_gettoken(&tp2->t_token);
547 
548 	error = (*linesw[tp->t_line].l_ioctl)(tp, ap->a_cmd, ap->a_data,
549 					      ap->a_fflag, ap->a_cred);
550 	if (error == ENOIOCTL)
551 		 error = ttioctl(tp, ap->a_cmd, ap->a_data, ap->a_fflag);
552 	if (error == ENOIOCTL) {
553 		switch (ap->a_cmd) {
554 		case TIOCSBRK:
555 			otherpart->gotbreak = 1;
556 			break;
557 		case TIOCCBRK:
558 			break;
559 		case TIOCSDTR:
560 			ourpart->modemsignals |= TIOCM_DTR;
561 			break;
562 		case TIOCCDTR:
563 			ourpart->modemsignals &= TIOCM_DTR;
564 			break;
565 		case TIOCMSET:
566 			ourpart->modemsignals = *(int *)ap->a_data;
567 			otherpart->modemsignals = *(int *)ap->a_data;
568 			break;
569 		case TIOCMBIS:
570 			ourpart->modemsignals |= *(int *)ap->a_data;
571 			break;
572 		case TIOCMBIC:
573 			ourpart->modemsignals &= ~(*(int *)ap->a_data);
574 			otherpart->modemsignals &= ~(*(int *)ap->a_data);
575 			break;
576 		case TIOCMGET:
577 			*(int *)ap->a_data = ourpart->modemsignals;
578 			break;
579 		case TIOCMSDTRWAIT:
580 			break;
581 		case TIOCMGDTRWAIT:
582 			*(int *)ap->a_data = 0;
583 			break;
584 		case TIOCTIMESTAMP:
585 		case TIOCDCDTIMESTAMP:
586 		default:
587 			lwkt_reltoken(&tp2->t_token);
588 			lwkt_reltoken(&tp->t_token);
589 			error = ENOTTY;
590 			return (error);
591 		}
592 		error = 0;
593 		nmdm_crossover(pti, ourpart, otherpart);
594 	}
595 	lwkt_reltoken(&tp2->t_token);
596 	lwkt_reltoken(&tp->t_token);
597 
598 	return (error);
599 }
600 
601 /*
602  * Caller must hold both tty tokens
603  */
604 static void
605 nmdm_crossover(struct nm_softc *pti,
606 		struct softpart *ourpart,
607 		struct softpart *otherpart)
608 {
609 	otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR);
610 	if (ourpart->modemsignals & TIOCM_RTS)
611 		otherpart->modemsignals |= TIOCM_CTS;
612 	if (ourpart->modemsignals & TIOCM_DTR)
613 		otherpart->modemsignals |= TIOCM_CAR;
614 }
615 
616 
617 
618 static void nmdm_drvinit (void *unused);
619 
620 static void
621 nmdm_drvinit(void *unused)
622 {
623 	nmdminit(0);
624 }
625 
626 SYSINIT(nmdmdev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE + CDEV_MAJOR, nmdm_drvinit,
627     NULL);
628