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 * 4. Neither the name of the University nor the names of its contributors 14 * may be used to endorse or promote products derived from this software 15 * without specific prior written permission. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 * 29 */ 30 31 #include <sys/cdefs.h> 32 __FBSDID("$FreeBSD$"); 33 34 /* 35 * Pseudo-nulmodem driver 36 * Mighty handy for use with serial console in Vmware 37 */ 38 39 #include "opt_compat.h" 40 #include "opt_tty.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/proc.h> 45 #include <sys/tty.h> 46 #include <sys/conf.h> 47 #include <sys/fcntl.h> 48 #include <sys/poll.h> 49 #include <sys/kernel.h> 50 #include <sys/module.h> 51 #include <sys/serial.h> 52 #include <sys/signalvar.h> 53 #include <sys/malloc.h> 54 #include <sys/taskqueue.h> 55 56 MALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures"); 57 58 static d_close_t nmdmclose; 59 static t_modem_t nmdmmodem; 60 static d_open_t nmdmopen; 61 static t_oproc_t nmdmoproc; 62 static t_param_t nmdmparam; 63 static t_stop_t nmdmstop; 64 65 static struct cdevsw nmdm_cdevsw = { 66 .d_version = D_VERSION, 67 .d_open = nmdmopen, 68 .d_close = nmdmclose, 69 .d_name = "nmdn", 70 .d_flags = D_TTY | D_PSEUDO | D_NEEDGIANT, 71 }; 72 73 #define BUFSIZ 100 /* Chunk size iomoved to/from user */ 74 #define NMDM_MAX_NUM 128 /* Artificially limit # devices. */ 75 #define PF_STOPPED 0x10 /* user told stopped */ 76 #define BFLAG CLONE_FLAG0 77 78 struct softpart { 79 struct tty *nm_tty; 80 struct cdev *dev; 81 int nm_dcd; 82 struct task pt_task; 83 struct softpart *other; 84 struct callout co; 85 u_long quota; 86 u_long accumulator; 87 int rate; 88 int credits; 89 90 #define QS 8 /* Quota shift */ 91 }; 92 93 struct nm_softc { 94 TAILQ_ENTRY(nm_softc) pt_list; 95 int pt_flags; 96 struct softpart part1, part2; 97 struct prison *pt_prison; 98 }; 99 100 static struct clonedevs *nmdmclones; 101 static TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); 102 103 static void 104 nmdm_clone(void *arg, struct ucred *cred, char *name, int nameen, 105 struct cdev **dev) 106 { 107 int i, unit; 108 char *p; 109 struct cdev *d1, *d2; 110 111 if (*dev != NULL) 112 return; 113 if (strcmp(name, "nmdm") == 0) { 114 p = NULL; 115 unit = -1; 116 } else { 117 i = dev_stdclone(name, &p, "nmdm", &unit); 118 if (i == 0) 119 return; 120 if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') 121 return; 122 else if (p[0] != '\0' && p[1] != '\0') 123 return; 124 } 125 i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); 126 if (i) { 127 d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), 128 0, 0, 0666, "nmdm%dA", unit); 129 if (d1 == NULL) 130 return; 131 d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, 132 0, 0, 0666, "nmdm%dB", unit); 133 if (d2 == NULL) { 134 destroy_dev(d1); 135 return; 136 } 137 d2->si_drv2 = d1; 138 d1->si_drv2 = d2; 139 dev_depends(d1, d2); 140 dev_depends(d2, d1); 141 d1->si_flags |= SI_CHEAPCLONE; 142 d2->si_flags |= SI_CHEAPCLONE; 143 } 144 if (p != NULL && p[0] == 'B') 145 *dev = d1->si_drv2; 146 else 147 *dev = d1; 148 dev_ref(*dev); 149 } 150 151 static void 152 nmdm_timeout(void *arg) 153 { 154 struct softpart *sp; 155 156 sp = arg; 157 158 if (sp->rate == 0) 159 return; 160 161 /* 162 * Do a simple Floyd-Steinberg dither here to avoid FP math. 163 * Wipe out unused quota from last tick. 164 */ 165 sp->accumulator += sp->credits; 166 sp->quota = sp->accumulator >> QS; 167 sp->accumulator &= ((1 << QS) - 1); 168 169 taskqueue_enqueue(taskqueue_swi_giant, &sp->pt_task); 170 callout_reset(&sp->co, sp->rate, nmdm_timeout, arg); 171 } 172 173 static void 174 nmdm_task_tty(void *arg, int pending __unused) 175 { 176 struct tty *tp, *otp; 177 struct softpart *sp; 178 int c; 179 180 tp = arg; 181 sp = tp->t_sc; 182 otp = sp->other->nm_tty; 183 KASSERT(otp != NULL, ("NULL otp in nmdmstart")); 184 KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); 185 if (sp->other->nm_dcd) { 186 if (!(tp->t_state & TS_ISOPEN)) { 187 sp->other->nm_dcd = 0; 188 (void)ttyld_modem(otp, 0); 189 } 190 } else { 191 if (tp->t_state & TS_ISOPEN) { 192 sp->other->nm_dcd = 1; 193 (void)ttyld_modem(otp, 1); 194 } 195 } 196 if (tp->t_state & TS_TTSTOP) 197 return; 198 while (tp->t_outq.c_cc != 0) { 199 if (sp->rate && !sp->quota) 200 return; 201 if (otp->t_state & TS_TBLOCK) 202 return; 203 sp->quota--; 204 c = getc(&tp->t_outq); 205 if (otp->t_state & TS_ISOPEN) 206 ttyld_rint(otp, c); 207 } 208 if (tp->t_outq.c_cc == 0) 209 ttwwakeup(tp); 210 211 } 212 213 /* 214 * This function creates and initializes a pair of ttys. 215 */ 216 static void 217 nmdminit(struct cdev *dev1) 218 { 219 struct cdev *dev2; 220 struct nm_softc *pt; 221 222 dev2 = dev1->si_drv2; 223 224 dev1->si_flags &= ~SI_CHEAPCLONE; 225 dev2->si_flags &= ~SI_CHEAPCLONE; 226 227 pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); 228 TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); 229 230 dev1->si_drv1 = dev2->si_drv1 = pt; 231 232 pt->part1.dev = dev1; 233 pt->part2.dev = dev2; 234 235 pt->part1.nm_tty = ttyalloc(); 236 pt->part1.nm_tty->t_oproc = nmdmoproc; 237 pt->part1.nm_tty->t_stop = nmdmstop; 238 pt->part1.nm_tty->t_modem = nmdmmodem; 239 pt->part1.nm_tty->t_param = nmdmparam; 240 pt->part1.nm_tty->t_dev = dev1; 241 pt->part1.nm_tty->t_sc = &pt->part1; 242 TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty); 243 callout_init(&pt->part1.co, 0); 244 245 pt->part2.nm_tty = ttyalloc(); 246 pt->part2.nm_tty->t_oproc = nmdmoproc; 247 pt->part2.nm_tty->t_stop = nmdmstop; 248 pt->part2.nm_tty->t_modem = nmdmmodem; 249 pt->part2.nm_tty->t_param = nmdmparam; 250 pt->part2.nm_tty->t_dev = dev2; 251 pt->part2.nm_tty->t_sc = &pt->part2; 252 TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty); 253 callout_init(&pt->part2.co, 0); 254 255 pt->part1.other = &pt->part2; 256 pt->part2.other = &pt->part1; 257 258 dev1->si_tty = pt->part1.nm_tty; 259 dev1->si_drv1 = pt; 260 261 dev2->si_tty = pt->part2.nm_tty; 262 dev2->si_drv1 = pt; 263 } 264 265 /* 266 * Device opened from userland 267 */ 268 static int 269 nmdmopen(struct cdev *dev, int flag, int devtype, struct thread *td) 270 { 271 struct tty *tp, *tp2; 272 int error; 273 struct nm_softc *pti; 274 struct softpart *sp; 275 276 if (dev->si_drv1 == NULL) 277 nmdminit(dev); 278 pti = dev->si_drv1; 279 if (pti->pt_prison != td->td_ucred->cr_prison) 280 return (EBUSY); 281 282 tp = dev->si_tty; 283 sp = tp->t_sc; 284 tp2 = sp->other->nm_tty; 285 286 if ((tp->t_state & TS_ISOPEN) == 0) { 287 ttyinitmode(tp, 0, 0); 288 ttsetwater(tp); /* XXX ? */ 289 } else if (tp->t_state & TS_XCLUDE && suser(td)) { 290 return (EBUSY); 291 } 292 293 error = ttyld_open(tp, dev); 294 return (error); 295 } 296 297 static int 298 bits_per_char(struct termios *t) 299 { 300 int bits; 301 302 bits = 1; /* start bit */ 303 switch (t->c_cflag & CSIZE) { 304 case CS5: bits += 5; break; 305 case CS6: bits += 6; break; 306 case CS7: bits += 7; break; 307 case CS8: bits += 8; break; 308 } 309 bits++; /* stop bit */ 310 if (t->c_cflag & PARENB) 311 bits++; 312 if (t->c_cflag & CSTOPB) 313 bits++; 314 return (bits); 315 } 316 317 static int 318 nmdmparam(struct tty *tp, struct termios *t) 319 { 320 struct softpart *sp; 321 struct tty *tp2; 322 int bpc, rate, speed, i; 323 324 sp = tp->t_sc; 325 tp2 = sp->other->nm_tty; 326 327 if (!((t->c_cflag | tp2->t_cflag) & CDSR_OFLOW)) { 328 sp->rate = 0; 329 sp->other->rate = 0; 330 return (0); 331 } 332 333 /* 334 * DSRFLOW one either side enables rate-simulation for both 335 * directions. 336 * NB: the two directions may run at different rates. 337 */ 338 339 /* Find the larger of the number of bits transmitted */ 340 bpc = imax(bits_per_char(t), bits_per_char(&tp2->t_termios)); 341 342 for (i = 0; i < 2; i++) { 343 /* Use the slower of our receive and their transmit rate */ 344 speed = imin(tp2->t_ospeed, t->c_ispeed); 345 if (speed == 0) { 346 sp->rate = 0; 347 sp->other->rate = 0; 348 return (0); 349 } 350 351 speed <<= QS; /* [bit/sec, scaled] */ 352 speed /= bpc; /* [char/sec, scaled] */ 353 rate = (hz << QS) / speed; /* [hz per callout] */ 354 if (rate == 0) 355 rate = 1; 356 357 speed *= rate; 358 speed /= hz; /* [(char/sec)/tick, scaled */ 359 360 sp->credits = speed; 361 sp->rate = rate; 362 callout_reset(&sp->co, rate, nmdm_timeout, sp); 363 364 /* 365 * swap pointers for second pass so the other end gets 366 * updated as well. 367 */ 368 sp = sp->other; 369 t = &tp2->t_termios; 370 tp2 = tp; 371 } 372 return (0); 373 } 374 375 static int 376 nmdmmodem(struct tty *tp, int sigon, int sigoff) 377 { 378 struct softpart *sp; 379 int i; 380 381 sp = tp->t_sc; 382 if (sigon || sigoff) { 383 if (sigon & SER_DTR) 384 sp->other->nm_dcd = 1; 385 if (sigoff & SER_DTR) 386 sp->other->nm_dcd = 0; 387 ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd); 388 return (0); 389 } else { 390 i = 0; 391 if (sp->nm_dcd) 392 i |= SER_DCD; 393 if (sp->other->nm_dcd) 394 i |= SER_DTR; 395 return (i); 396 } 397 } 398 399 static int 400 nmdmclose(struct cdev *dev, int flag, int mode, struct thread *td) 401 { 402 403 return (tty_close(dev->si_tty)); 404 } 405 406 static void 407 nmdmoproc(struct tty *tp) 408 { 409 struct softpart *pt; 410 411 pt = tp->t_sc; 412 taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 413 } 414 415 static void 416 nmdmstop(struct tty *tp, int flush) 417 { 418 struct softpart *pt; 419 420 pt = tp->t_sc; 421 taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 422 } 423 424 /* 425 * Module handling 426 */ 427 static int 428 nmdm_modevent(module_t mod, int type, void *data) 429 { 430 static eventhandler_tag tag; 431 struct nm_softc *pt, *tpt; 432 int error = 0; 433 434 switch(type) { 435 case MOD_LOAD: 436 clone_setup(&nmdmclones); 437 tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 438 if (tag == NULL) 439 return (ENOMEM); 440 break; 441 442 case MOD_SHUTDOWN: 443 /* FALLTHROUGH */ 444 case MOD_UNLOAD: 445 EVENTHANDLER_DEREGISTER(dev_clone, tag); 446 TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { 447 destroy_dev(pt->part1.dev); 448 TAILQ_REMOVE(&nmdmhead, pt, pt_list); 449 free(pt, M_NLMDM); 450 } 451 clone_cleanup(&nmdmclones); 452 break; 453 default: 454 error = EOPNOTSUPP; 455 } 456 return (error); 457 } 458 459 DEV_MODULE(nmdm, nmdm_modevent, NULL); 460