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