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