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