nmdm.c revision 126077
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 */ 34 35#include <sys/cdefs.h> 36__FBSDID("$FreeBSD: head/sys/dev/nmdm/nmdm.c 126077 2004-02-21 20:29:52Z phk $"); 37 38/* 39 * Pseudo-nulmodem driver 40 * Mighty handy for use with serial console in Vmware 41 */ 42 43#include "opt_compat.h" 44#include "opt_tty.h" 45 46#include <sys/param.h> 47#include <sys/systm.h> 48#if defined(COMPAT_43) || defined(COMPAT_SUNOS) 49#include <sys/ioctl_compat.h> 50#endif 51#include <sys/proc.h> 52#include <sys/tty.h> 53#include <sys/conf.h> 54#include <sys/fcntl.h> 55#include <sys/poll.h> 56#include <sys/kernel.h> 57#include <sys/vnode.h> 58#include <sys/signalvar.h> 59#include <sys/malloc.h> 60 61MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures"); 62 63static void nmdmstart(struct tty *tp); 64static void nmdmstop(struct tty *tp, int rw); 65static void wakeup_other(struct tty *tp, int flag); 66static void nmdminit(dev_t dev); 67 68static d_open_t nmdmopen; 69static d_close_t nmdmclose; 70static d_read_t nmdmread; 71static d_write_t nmdmwrite; 72static d_ioctl_t nmdmioctl; 73 74static struct cdevsw nmdm_cdevsw = { 75 .d_open = nmdmopen, 76 .d_close = nmdmclose, 77 .d_read = nmdmread, 78 .d_write = nmdmwrite, 79 .d_ioctl = nmdmioctl, 80 .d_poll = ttypoll, 81 .d_name = "nmdm", 82 .d_flags = D_TTY | D_PSEUDO, 83}; 84 85#define BUFSIZ 100 /* Chunk size iomoved to/from user */ 86#define NMDM_MAX_NUM 128 /* Artificially limit # devices. */ 87#define PF_STOPPED 0x10 /* user told stopped */ 88#define BFLAG CLONE_FLAG0 89 90struct softpart { 91 struct tty nm_tty; 92 dev_t dev; 93 int modemsignals; /* bits defined in sys/ttycom.h */ 94 int gotbreak; 95}; 96 97struct nm_softc { 98 TAILQ_ENTRY(nm_softc) pt_list; 99 int pt_flags; 100 struct softpart part1, part2; 101 struct prison *pt_prison; 102}; 103 104static struct clonedevs *nmdmclones; 105static TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); 106 107static void 108nmdm_clone(void *arg, char *name, int nameen, dev_t *dev) 109{ 110 int i, unit; 111 char *p; 112 dev_t d1, d2; 113 114 if (*dev != NODEV) 115 return; 116 if (strcmp(name, "nmdm") == 0) { 117 p = NULL; 118 unit = -1; 119 } else { 120 i = dev_stdclone(name, &p, "nmdm", &unit); 121 if (i == 0) 122 return; 123 if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') 124 return; 125 else if (p[0] != '\0' && p[1] != '\0') 126 return; 127 } 128 i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); 129 if (i) { 130 d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), 131 0, 0, 0666, "nmdm%dA", unit); 132 if (d1 == NULL) 133 return; 134 d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, 135 0, 0, 0666, "nmdm%dB", unit); 136 if (d2 == NULL) { 137 destroy_dev(d1); 138 return; 139 } 140 d2->si_drv2 = d1; 141 d1->si_drv2 = d2; 142 dev_depends(d1, d2); 143 dev_depends(d2, d1); 144 d1->si_flags |= SI_CHEAPCLONE; 145 d2->si_flags |= SI_CHEAPCLONE; 146 } 147 if (p != NULL && p[0] == 'B') 148 *dev = d1->si_drv2; 149 else 150 *dev = d1; 151} 152 153static void 154nmdm_crossover(struct nm_softc *pti, 155 struct softpart *ourpart, 156 struct softpart *otherpart); 157 158#define GETPARTS(tp, ourpart, otherpart) \ 159do { \ 160 struct nm_softc *pti = tp->t_dev->si_drv1; \ 161 if (tp == &pti->part1.nm_tty) { \ 162 ourpart = &pti->part1; \ 163 otherpart = &pti->part2; \ 164 } else { \ 165 ourpart = &pti->part2; \ 166 otherpart = &pti->part1; \ 167 } \ 168} while (0) 169 170/* 171 * This function creates and initializes a pair of ttys. 172 */ 173static void 174nmdminit(dev_t dev1) 175{ 176 dev_t dev2; 177 struct nm_softc *pt; 178 179 dev2 = dev1->si_drv2; 180 181 dev1->si_flags &= ~SI_CHEAPCLONE; 182 dev2->si_flags &= ~SI_CHEAPCLONE; 183 184 pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); 185 TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); 186 dev1->si_drv1 = dev2->si_drv1 = pt; 187 188 pt->part1.dev = dev1; 189 pt->part2.dev = dev2; 190 dev1->si_tty = &pt->part1.nm_tty; 191 dev2->si_tty = &pt->part2.nm_tty; 192 ttyregister(&pt->part1.nm_tty); 193 ttyregister(&pt->part2.nm_tty); 194 pt->part1.nm_tty.t_oproc = nmdmstart; 195 pt->part2.nm_tty.t_oproc = nmdmstart; 196 pt->part1.nm_tty.t_stop = nmdmstop; 197 pt->part2.nm_tty.t_stop = nmdmstop; 198 pt->part2.nm_tty.t_dev = dev1; 199 pt->part1.nm_tty.t_dev = dev2; 200} 201 202/* 203 * Device opened from userland 204 */ 205static int 206nmdmopen(dev_t dev, int flag, int devtype, struct thread *td) 207{ 208 register struct tty *tp, *tp2; 209 int error; 210 struct nm_softc *pti; 211 struct softpart *ourpart, *otherpart; 212 213 if (dev->si_drv1 == NULL) 214 nmdminit(dev); 215 pti = dev->si_drv1; 216 217 if (minor(dev) & BFLAG) 218 tp = &pti->part2.nm_tty; 219 else 220 tp = &pti->part1.nm_tty; 221 GETPARTS(tp, ourpart, otherpart); 222 223 tp2 = &otherpart->nm_tty; 224 ourpart->modemsignals |= TIOCM_LE; 225 226 if ((tp->t_state & TS_ISOPEN) == 0) { 227 ttychars(tp); /* Set up default chars */ 228 tp->t_iflag = TTYDEF_IFLAG; 229 tp->t_oflag = TTYDEF_OFLAG; 230 tp->t_lflag = TTYDEF_LFLAG; 231 tp->t_cflag = TTYDEF_CFLAG; 232 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 233 } else if (tp->t_state & TS_XCLUDE && suser(td)) { 234 return (EBUSY); 235 } else if (pti->pt_prison != td->td_ucred->cr_prison) { 236 return (EBUSY); 237 } 238 239 /* 240 * If the other side is open we have carrier 241 */ 242 if (tp2->t_state & TS_ISOPEN) { 243 (void)(*linesw[tp->t_line].l_modem)(tp, 1); 244 } 245 246 /* 247 * And the other side gets carrier as we are now open. 248 */ 249 (void)(*linesw[tp2->t_line].l_modem)(tp2, 1); 250 251 /* External processing makes no sense here */ 252 tp->t_lflag &= ~EXTPROC; 253 254 /* 255 * Wait here if we don't have carrier. 256 */ 257#if 0 258 while ((tp->t_state & TS_CARR_ON) == 0) { 259 if (flag & FNONBLOCK) 260 break; 261 error = ttysleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, 262 "nmdopn", 0); 263 if (error) 264 return (error); 265 } 266#endif 267 268 /* 269 * Give the line disciplin a chance to set this end up. 270 */ 271 error = (*linesw[tp->t_line].l_open)(dev, tp); 272 273 /* 274 * Wake up the other side. 275 * Theoretically not needed. 276 */ 277 ourpart->modemsignals |= TIOCM_DTR; 278 nmdm_crossover(pti, ourpart, otherpart); 279 if (error == 0) 280 wakeup_other(tp, FREAD|FWRITE); /* XXX */ 281 return (error); 282} 283 284/* 285 * Device closed again 286 */ 287static int 288nmdmclose(dev_t dev, int flag, int mode, struct thread *td) 289{ 290 register struct tty *tp, *tp2; 291 int err; 292 struct softpart *ourpart, *otherpart; 293 294 /* 295 * let the other end know that the game is up 296 */ 297 tp = dev->si_tty; 298 GETPARTS(tp, ourpart, otherpart); 299 tp2 = &otherpart->nm_tty; 300 (void)(*linesw[tp2->t_line].l_modem)(tp2, 0); 301 302 /* 303 * XXX MDMBUF makes no sense for nmdms but would inhibit the above 304 * l_modem(). CLOCAL makes sense but isn't supported. Special 305 * l_modem()s that ignore carrier drop make no sense for nmdms but 306 * may be in use because other parts of the line discipline make 307 * sense for nmdms. Recover by doing everything that a normal 308 * ttymodem() would have done except for sending a SIGHUP. 309 */ 310 if (tp2->t_state & TS_ISOPEN) { 311 tp2->t_state &= ~(TS_CARR_ON | TS_CONNECTED); 312 tp2->t_state |= TS_ZOMBIE; 313 ttyflush(tp2, FREAD | FWRITE); 314 } 315 316 err = (*linesw[tp->t_line].l_close)(tp, flag); 317 ourpart->modemsignals &= ~TIOCM_DTR; 318 nmdm_crossover(dev->si_drv1, ourpart, otherpart); 319 nmdmstop(tp, FREAD|FWRITE); 320 (void) ttyclose(tp); 321 return (err); 322} 323 324/* 325 * handle read(2) request from userland 326 */ 327static int 328nmdmread(dev_t dev, struct uio *uio, int flag) 329{ 330 int error = 0; 331 struct tty *tp, *tp2; 332 struct softpart *ourpart, *otherpart; 333 334 tp = dev->si_tty; 335 GETPARTS(tp, ourpart, otherpart); 336 tp2 = &otherpart->nm_tty; 337 338#if 0 339 if (tp2->t_state & TS_ISOPEN) { 340 error = (*linesw[tp->t_line].l_read)(tp, uio, flag); 341 wakeup_other(tp, FWRITE); 342 } else { 343 if (flag & IO_NDELAY) { 344 return (EWOULDBLOCK); 345 } 346 error = tsleep(TSA_PTC_READ(tp), 347 TTIPRI | PCATCH, "nmdout", 0); 348 } 349 } 350#else 351 if ((error = (*linesw[tp->t_line].l_read)(tp, uio, flag)) == 0) 352 wakeup_other(tp, FWRITE); 353#endif 354 return (error); 355} 356 357/* 358 * Write to pseudo-tty. 359 * Wakeups of controlling tty will happen 360 * indirectly, when tty driver calls nmdmstart. 361 */ 362static int 363nmdmwrite(dev_t dev, struct uio *uio, int flag) 364{ 365 register u_char *cp = 0; 366 register int cc = 0; 367 u_char locbuf[BUFSIZ]; 368 int cnt = 0; 369 int error = 0; 370 struct tty *tp1, *tp; 371 struct softpart *ourpart, *otherpart; 372 373 tp1 = dev->si_tty; 374 /* 375 * Get the other tty struct. 376 * basically we are writing into the INPUT side of the other device. 377 */ 378 GETPARTS(tp1, ourpart, otherpart); 379 tp = &otherpart->nm_tty; 380 381again: 382 if ((tp->t_state & TS_ISOPEN) == 0) 383 return (EIO); 384 while (uio->uio_resid > 0 || cc > 0) { 385 /* 386 * Fill up the buffer if it's empty 387 */ 388 if (cc == 0) { 389 cc = min(uio->uio_resid, BUFSIZ); 390 cp = locbuf; 391 error = uiomove((caddr_t)cp, cc, uio); 392 if (error) 393 return (error); 394 /* check again for safety */ 395 if ((tp->t_state & TS_ISOPEN) == 0) { 396 /* adjust for data copied in but not written */ 397 uio->uio_resid += cc; 398 return (EIO); 399 } 400 } 401 while (cc > 0) { 402 if (((tp->t_rawq.c_cc + tp->t_canq.c_cc) >= (TTYHOG-2)) 403 && ((tp->t_canq.c_cc > 0) || !(tp->t_iflag&ICANON))) { 404 /* 405 * Come here to wait for space in outq, 406 * or space in rawq, or an empty canq. 407 */ 408 wakeup(TSA_HUP_OR_INPUT(tp)); 409 if ((tp->t_state & TS_CONNECTED) == 0) { 410 /* 411 * Data piled up because not connected. 412 * Adjust for data copied in but 413 * not written. 414 */ 415 uio->uio_resid += cc; 416 return (EIO); 417 } 418 if (flag & IO_NDELAY) { 419 /* 420 * Don't wait if asked not to. 421 * Adjust for data copied in but 422 * not written. 423 */ 424 uio->uio_resid += cc; 425 if (cnt == 0) 426 return (EWOULDBLOCK); 427 return (0); 428 } 429 error = tsleep(TSA_PTC_WRITE(tp), 430 TTOPRI | PCATCH, "nmdout", 0); 431 if (error) { 432 /* 433 * Tsleep returned (signal?). 434 * Go find out what the user wants. 435 * adjust for data copied in but 436 * not written 437 */ 438 uio->uio_resid += cc; 439 return (error); 440 } 441 goto again; 442 } 443 (*linesw[tp->t_line].l_rint)(*cp++, tp); 444 cnt++; 445 cc--; 446 } 447 cc = 0; 448 } 449 return (0); 450} 451 452/* 453 * Start output on pseudo-tty. 454 * Wake up process selecting or sleeping for input from controlling tty. 455 */ 456static void 457nmdmstart(struct tty *tp) 458{ 459 register struct nm_softc *pti = tp->t_dev->si_drv1; 460 461 if (tp->t_state & TS_TTSTOP) 462 return; 463 pti->pt_flags &= ~PF_STOPPED; 464 wakeup_other(tp, FREAD); 465} 466 467/* Wakes up the OTHER tty;*/ 468static void 469wakeup_other(struct tty *tp, int flag) 470{ 471 struct softpart *ourpart, *otherpart; 472 473 GETPARTS(tp, ourpart, otherpart); 474 if (flag & FREAD) { 475 selwakeuppri(&otherpart->nm_tty.t_rsel, TTIPRI); 476 wakeup(TSA_PTC_READ((&otherpart->nm_tty))); 477 } 478 if (flag & FWRITE) { 479 selwakeuppri(&otherpart->nm_tty.t_wsel, TTOPRI); 480 wakeup(TSA_PTC_WRITE((&otherpart->nm_tty))); 481 } 482} 483 484/* 485 * stopped output on tty, called when device is closed 486 */ 487static void 488nmdmstop(register struct tty *tp, int flush) 489{ 490 struct nm_softc *pti = tp->t_dev->si_drv1; 491 int flag; 492 493 /* note: FLUSHREAD and FLUSHWRITE already ok */ 494 if (flush == 0) { 495 flush = TIOCPKT_STOP; 496 pti->pt_flags |= PF_STOPPED; 497 } else 498 pti->pt_flags &= ~PF_STOPPED; 499 /* change of perspective */ 500 flag = 0; 501 if (flush & FREAD) 502 flag |= FWRITE; 503 if (flush & FWRITE) 504 flag |= FREAD; 505 wakeup_other(tp, flag); 506} 507 508/* 509 * handle ioctl(2) request from userland 510 */ 511static int 512nmdmioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct thread *td) 513{ 514 register struct tty *tp = dev->si_tty; 515 struct nm_softc *pti = dev->si_drv1; 516 int error, s; 517 register struct tty *tp2; 518 struct softpart *ourpart, *otherpart; 519 520 s = spltty(); 521 GETPARTS(tp, ourpart, otherpart); 522 tp2 = &otherpart->nm_tty; 523 524 error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td); 525 if (error == ENOIOCTL) 526 error = ttioctl(tp, cmd, data, flag); 527 if (error == ENOIOCTL) { 528 switch (cmd) { 529 case TIOCSBRK: 530 otherpart->gotbreak = 1; 531 break; 532 case TIOCCBRK: 533 break; 534 case TIOCSDTR: 535 ourpart->modemsignals |= TIOCM_DTR; 536 break; 537 case TIOCCDTR: 538 ourpart->modemsignals &= TIOCM_DTR; 539 break; 540 case TIOCMSET: 541 ourpart->modemsignals = *(int *)data; 542 otherpart->modemsignals = *(int *)data; 543 break; 544 case TIOCMBIS: 545 ourpart->modemsignals |= *(int *)data; 546 break; 547 case TIOCMBIC: 548 ourpart->modemsignals &= ~(*(int *)data); 549 otherpart->modemsignals &= ~(*(int *)data); 550 break; 551 case TIOCMGET: 552 *(int *)data = ourpart->modemsignals; 553 break; 554 case TIOCMSDTRWAIT: 555 break; 556 case TIOCMGDTRWAIT: 557 *(int *)data = 0; 558 break; 559 case TIOCTIMESTAMP: 560 /* FALLTHROUGH */ 561 case TIOCDCDTIMESTAMP: 562 default: 563 splx(s); 564 error = ENOTTY; 565 return (error); 566 } 567 error = 0; 568 nmdm_crossover(pti, ourpart, otherpart); 569 } 570 splx(s); 571 return (error); 572} 573 574static void 575nmdm_crossover(struct nm_softc *pti, struct softpart *ourpart, 576 struct softpart *otherpart) 577{ 578 otherpart->modemsignals &= ~(TIOCM_CTS|TIOCM_CAR); 579 if (ourpart->modemsignals & TIOCM_RTS) 580 otherpart->modemsignals |= TIOCM_CTS; 581 if (ourpart->modemsignals & TIOCM_DTR) 582 otherpart->modemsignals |= TIOCM_CAR; 583} 584 585/* 586 * Module handling 587 */ 588static int 589nmdm_modevent(module_t mod, int type, void *data) 590{ 591 static eventhandler_tag tag; 592 struct nm_softc *pt, *tpt; 593 int error = 0; 594 595 switch(type) { 596 case MOD_LOAD: 597 tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 598 if (tag == NULL) 599 return (ENOMEM); 600 break; 601 602 case MOD_SHUTDOWN: 603 /* FALLTHROUGH */ 604 case MOD_UNLOAD: 605 EVENTHANDLER_DEREGISTER(dev_clone, tag); 606 TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { 607 destroy_dev(pt->part1.dev); 608 TAILQ_REMOVE(&nmdmhead, pt, pt_list); 609 free(pt, M_NLMDM); 610 } 611 clone_cleanup(&nmdmclones); 612 break; 613 default: 614 error = EOPNOTSUPP; 615 } 616 return (error); 617} 618 619DEV_MODULE(nmdm, nmdm_modevent, NULL); 620