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