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