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