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