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/vnode.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 void nmdmstart(struct tty *tp); 59 static void nmdmstop(struct tty *tp, int rw); 60 static void nmdminit(struct cdev *dev); 61 62 static d_open_t nmdmopen; 63 static d_close_t nmdmclose; 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 dcd; 82 struct task pt_task; 83 struct softpart *other; 84 }; 85 86 struct nm_softc { 87 TAILQ_ENTRY(nm_softc) pt_list; 88 int pt_flags; 89 struct softpart part1, part2; 90 struct prison *pt_prison; 91 }; 92 93 static struct clonedevs *nmdmclones; 94 static TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); 95 96 static void 97 nmdm_clone(void *arg, char *name, int nameen, struct cdev **dev) 98 { 99 int i, unit; 100 char *p; 101 struct cdev *d1, *d2; 102 103 if (*dev != NULL) 104 return; 105 if (strcmp(name, "nmdm") == 0) { 106 p = NULL; 107 unit = -1; 108 } else { 109 i = dev_stdclone(name, &p, "nmdm", &unit); 110 if (i == 0) 111 return; 112 if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') 113 return; 114 else if (p[0] != '\0' && p[1] != '\0') 115 return; 116 } 117 i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); 118 if (i) { 119 d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), 120 0, 0, 0666, "nmdm%dA", unit); 121 if (d1 == NULL) 122 return; 123 d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, 124 0, 0, 0666, "nmdm%dB", unit); 125 if (d2 == NULL) { 126 destroy_dev(d1); 127 return; 128 } 129 d2->si_drv2 = d1; 130 d1->si_drv2 = d2; 131 dev_depends(d1, d2); 132 dev_depends(d2, d1); 133 d1->si_flags |= SI_CHEAPCLONE; 134 d2->si_flags |= SI_CHEAPCLONE; 135 } 136 if (p != NULL && p[0] == 'B') 137 *dev = d1->si_drv2; 138 else 139 *dev = d1; 140 } 141 142 static void 143 nmdm_task_tty(void *arg, int pending __unused) 144 { 145 struct tty *tp, *otp; 146 struct softpart *sp; 147 int c; 148 149 tp = arg; 150 sp = tp->t_sc; 151 otp = sp->other->nm_tty; 152 KASSERT(otp != NULL, ("NULL otp in nmdmstart")); 153 KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); 154 if (sp->other->dcd) { 155 if (!(tp->t_state & TS_ISOPEN)) { 156 sp->other->dcd = 0; 157 (void)ttyld_modem(otp, 0); 158 } 159 } else { 160 if (tp->t_state & TS_ISOPEN) { 161 sp->other->dcd = 1; 162 (void)ttyld_modem(otp, 1); 163 } 164 } 165 if (tp->t_state & TS_TTSTOP) 166 return; 167 while (tp->t_outq.c_cc != 0) { 168 if (otp->t_state & TS_TBLOCK) 169 return; 170 c = getc(&tp->t_outq); 171 if (otp->t_state & TS_ISOPEN) 172 ttyld_rint(otp, c); 173 } 174 if (tp->t_outq.c_cc == 0) 175 ttwwakeup(tp); 176 } 177 178 /* 179 * This function creates and initializes a pair of ttys. 180 */ 181 static void 182 nmdminit(struct cdev *dev1) 183 { 184 struct cdev *dev2; 185 struct nm_softc *pt; 186 187 dev2 = dev1->si_drv2; 188 189 dev1->si_flags &= ~SI_CHEAPCLONE; 190 dev2->si_flags &= ~SI_CHEAPCLONE; 191 192 pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); 193 TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); 194 195 dev1->si_drv1 = dev2->si_drv1 = pt; 196 197 pt->part1.dev = dev1; 198 pt->part2.dev = dev2; 199 200 pt->part1.nm_tty = ttymalloc(pt->part1.nm_tty); 201 pt->part1.nm_tty->t_oproc = nmdmstart; 202 pt->part1.nm_tty->t_stop = nmdmstop; 203 pt->part1.nm_tty->t_dev = dev1; 204 pt->part1.nm_tty->t_sc = &pt->part1; 205 TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty); 206 207 pt->part2.nm_tty = ttymalloc(pt->part2.nm_tty); 208 pt->part2.nm_tty->t_oproc = nmdmstart; 209 pt->part2.nm_tty->t_stop = nmdmstop; 210 pt->part2.nm_tty->t_dev = dev2; 211 pt->part2.nm_tty->t_sc = &pt->part2; 212 TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty); 213 214 pt->part1.other = &pt->part2; 215 pt->part2.other = &pt->part1; 216 217 dev1->si_tty = pt->part1.nm_tty; 218 dev1->si_drv1 = pt; 219 220 dev2->si_tty = pt->part2.nm_tty; 221 dev2->si_drv1 = pt; 222 } 223 224 /* 225 * Device opened from userland 226 */ 227 static int 228 nmdmopen(struct cdev *dev, int flag, int devtype, struct thread *td) 229 { 230 struct tty *tp, *tp2; 231 int error; 232 struct nm_softc *pti; 233 struct softpart *sp; 234 235 if (dev->si_drv1 == NULL) 236 nmdminit(dev); 237 pti = dev->si_drv1; 238 if (pti->pt_prison != td->td_ucred->cr_prison) 239 return (EBUSY); 240 241 tp = dev->si_tty; 242 sp = tp->t_sc; 243 tp2 = sp->other->nm_tty; 244 245 if ((tp->t_state & TS_ISOPEN) == 0) { 246 ttychars(tp); /* Set up default chars */ 247 tp->t_iflag = TTYDEF_IFLAG; 248 tp->t_oflag = TTYDEF_OFLAG; 249 tp->t_lflag = TTYDEF_LFLAG; 250 tp->t_lflag = 0; 251 tp->t_cflag = TTYDEF_CFLAG; 252 tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED; 253 ttsetwater(tp); /* XXX ? */ 254 } else if (tp->t_state & TS_XCLUDE && suser(td)) { 255 return (EBUSY); 256 } 257 258 error = ttyld_open(tp, dev); 259 return (error); 260 } 261 262 static int 263 nmdmclose(struct cdev *dev, int flag, int mode, struct thread *td) 264 { 265 266 return (ttyclose(dev->si_tty)); 267 } 268 269 static void 270 nmdmstart(struct tty *tp) 271 { 272 struct softpart *pt; 273 274 pt = tp->t_sc; 275 taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 276 } 277 278 static void 279 nmdmstop(struct tty *tp, int flush) 280 { 281 struct softpart *pt; 282 283 pt = tp->t_sc; 284 taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 285 } 286 287 /* 288 * Module handling 289 */ 290 static int 291 nmdm_modevent(module_t mod, int type, void *data) 292 { 293 static eventhandler_tag tag; 294 struct nm_softc *pt, *tpt; 295 int error = 0; 296 297 switch(type) { 298 case MOD_LOAD: 299 clone_setup(&nmdmclones); 300 tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 301 if (tag == NULL) 302 return (ENOMEM); 303 break; 304 305 case MOD_SHUTDOWN: 306 /* FALLTHROUGH */ 307 case MOD_UNLOAD: 308 EVENTHANDLER_DEREGISTER(dev_clone, tag); 309 TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { 310 destroy_dev(pt->part1.dev); 311 TAILQ_REMOVE(&nmdmhead, pt, pt_list); 312 free(pt, M_NLMDM); 313 } 314 clone_cleanup(&nmdmclones); 315 break; 316 default: 317 error = EOPNOTSUPP; 318 } 319 return (error); 320 } 321 322 DEV_MODULE(nmdm, nmdm_modevent, NULL); 323