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 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 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 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 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 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 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 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 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 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 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 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