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