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