nmdm.c revision 139749
1139749Simp/*- 273161Sjulian * Copyright (c) 1982, 1986, 1989, 1993 373161Sjulian * The Regents of the University of California. All rights reserved. 473161Sjulian * 573161Sjulian * Redistribution and use in source and binary forms, with or without 673161Sjulian * modification, are permitted provided that the following conditions 773161Sjulian * are met: 873161Sjulian * 1. Redistributions of source code must retain the above copyright 973161Sjulian * notice, this list of conditions and the following disclaimer. 1073161Sjulian * 2. Redistributions in binary form must reproduce the above copyright 1173161Sjulian * notice, this list of conditions and the following disclaimer in the 1273161Sjulian * documentation and/or other materials provided with the distribution. 1373161Sjulian * 4. Neither the name of the University nor the names of its contributors 1473161Sjulian * may be used to endorse or promote products derived from this software 1573161Sjulian * without specific prior written permission. 1673161Sjulian * 1773161Sjulian * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 1873161Sjulian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1973161Sjulian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2073161Sjulian * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2173161Sjulian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2273161Sjulian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2373161Sjulian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2473161Sjulian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2573161Sjulian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2673161Sjulian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2773161Sjulian * SUCH DAMAGE. 2873161Sjulian * 2973161Sjulian */ 3073161Sjulian 31119418Sobrien#include <sys/cdefs.h> 32119418Sobrien__FBSDID("$FreeBSD: head/sys/dev/nmdm/nmdm.c 139749 2005-01-06 01:43:34Z imp $"); 33119418Sobrien 3473161Sjulian/* 3590995Sjulian * Pseudo-nulmodem driver 3690995Sjulian * Mighty handy for use with serial console in Vmware 3773161Sjulian */ 3890995Sjulian 3973161Sjulian#include "opt_compat.h" 40111899Sdas#include "opt_tty.h" 41111899Sdas 4273161Sjulian#include <sys/param.h> 4373161Sjulian#include <sys/systm.h> 4473161Sjulian#include <sys/proc.h> 4573161Sjulian#include <sys/tty.h> 4673161Sjulian#include <sys/conf.h> 4773161Sjulian#include <sys/fcntl.h> 4873161Sjulian#include <sys/poll.h> 4973161Sjulian#include <sys/kernel.h> 50129879Sphk#include <sys/module.h> 51131579Sphk#include <sys/serial.h> 5273161Sjulian#include <sys/signalvar.h> 5373161Sjulian#include <sys/malloc.h> 54129968Sphk#include <sys/taskqueue.h> 5573161Sjulian 5673161SjulianMALLOC_DEFINE(M_NLMDM, "nullmodem", "nullmodem data structures"); 5773161Sjulian 5890995Sjulianstatic void nmdmstart(struct tty *tp); 5990995Sjulianstatic void nmdmstop(struct tty *tp, int rw); 60130585Sphkstatic void nmdminit(struct cdev *dev); 61131579Sphkstatic t_modem_t nmdmmodem; 6273161Sjulian 6390995Sjulianstatic d_open_t nmdmopen; 6490995Sjulianstatic d_close_t nmdmclose; 6573161Sjulian 6673161Sjulianstatic struct cdevsw nmdm_cdevsw = { 67126080Sphk .d_version = D_VERSION, 68111815Sphk .d_open = nmdmopen, 69111815Sphk .d_close = nmdmclose, 70126080Sphk .d_name = "nmdn", 71126080Sphk .d_flags = D_TTY | D_PSEUDO | D_NEEDGIANT, 7273161Sjulian}; 7373161Sjulian 7490995Sjulian#define BUFSIZ 100 /* Chunk size iomoved to/from user */ 7590995Sjulian#define NMDM_MAX_NUM 128 /* Artificially limit # devices. */ 7690995Sjulian#define PF_STOPPED 0x10 /* user told stopped */ 77126077Sphk#define BFLAG CLONE_FLAG0 7873161Sjulian 7973161Sjulianstruct softpart { 80131579Sphk struct tty *nm_tty; 81131579Sphk struct cdev *dev; 82131579Sphk int nm_dcd; 83129968Sphk struct task pt_task; 84129968Sphk struct softpart *other; 8573161Sjulian}; 8673161Sjulian 8773161Sjulianstruct nm_softc { 88126077Sphk TAILQ_ENTRY(nm_softc) pt_list; 89126077Sphk int pt_flags; 90126077Sphk struct softpart part1, part2; 91126077Sphk struct prison *pt_prison; 9273161Sjulian}; 9373161Sjulian 94126077Sphkstatic struct clonedevs *nmdmclones; 95126077Sphkstatic TAILQ_HEAD(,nm_softc) nmdmhead = TAILQ_HEAD_INITIALIZER(nmdmhead); 96126077Sphk 9773161Sjulianstatic void 98130585Sphknmdm_clone(void *arg, char *name, int nameen, struct cdev **dev) 99126077Sphk{ 100126077Sphk int i, unit; 101126077Sphk char *p; 102130585Sphk struct cdev *d1, *d2; 103126077Sphk 104130640Sphk if (*dev != NULL) 105126077Sphk return; 106126077Sphk if (strcmp(name, "nmdm") == 0) { 107126077Sphk p = NULL; 108126077Sphk unit = -1; 109126077Sphk } else { 110126077Sphk i = dev_stdclone(name, &p, "nmdm", &unit); 111126077Sphk if (i == 0) 112126077Sphk return; 113126077Sphk if (p[0] != '\0' && p[0] != 'A' && p[0] != 'B') 114126077Sphk return; 115126077Sphk else if (p[0] != '\0' && p[1] != '\0') 116126077Sphk return; 117126077Sphk } 118126077Sphk i = clone_create(&nmdmclones, &nmdm_cdevsw, &unit, &d1, 0); 119126077Sphk if (i) { 120126077Sphk d1 = make_dev(&nmdm_cdevsw, unit2minor(unit), 121126077Sphk 0, 0, 0666, "nmdm%dA", unit); 122126077Sphk if (d1 == NULL) 123126077Sphk return; 124126077Sphk d2 = make_dev(&nmdm_cdevsw, unit2minor(unit) | BFLAG, 125126077Sphk 0, 0, 0666, "nmdm%dB", unit); 126126077Sphk if (d2 == NULL) { 127126077Sphk destroy_dev(d1); 128126077Sphk return; 129126077Sphk } 130126077Sphk d2->si_drv2 = d1; 131126077Sphk d1->si_drv2 = d2; 132126077Sphk dev_depends(d1, d2); 133126077Sphk dev_depends(d2, d1); 134126077Sphk d1->si_flags |= SI_CHEAPCLONE; 135126077Sphk d2->si_flags |= SI_CHEAPCLONE; 136126077Sphk } 137126077Sphk if (p != NULL && p[0] == 'B') 138126077Sphk *dev = d1->si_drv2; 139126077Sphk else 140126077Sphk *dev = d1; 141126077Sphk} 142126077Sphk 143126077Sphkstatic void 144129968Sphknmdm_task_tty(void *arg, int pending __unused) 145129968Sphk{ 146129968Sphk struct tty *tp, *otp; 147129968Sphk struct softpart *sp; 148129968Sphk int c; 14973161Sjulian 150129968Sphk tp = arg; 151129968Sphk sp = tp->t_sc; 152129968Sphk otp = sp->other->nm_tty; 153129968Sphk KASSERT(otp != NULL, ("NULL otp in nmdmstart")); 154129968Sphk KASSERT(otp != tp, ("NULL otp == tp nmdmstart")); 155131579Sphk if (sp->other->nm_dcd) { 156129968Sphk if (!(tp->t_state & TS_ISOPEN)) { 157131579Sphk sp->other->nm_dcd = 0; 158130077Sphk (void)ttyld_modem(otp, 0); 159129968Sphk } 160129968Sphk } else { 161129968Sphk if (tp->t_state & TS_ISOPEN) { 162131579Sphk sp->other->nm_dcd = 1; 163130077Sphk (void)ttyld_modem(otp, 1); 164129968Sphk } 165129968Sphk } 166129968Sphk if (tp->t_state & TS_TTSTOP) 167129968Sphk return; 168129968Sphk while (tp->t_outq.c_cc != 0) { 169129968Sphk if (otp->t_state & TS_TBLOCK) 170129968Sphk return; 171129968Sphk c = getc(&tp->t_outq); 172129968Sphk if (otp->t_state & TS_ISOPEN) 173130077Sphk ttyld_rint(otp, c); 174129968Sphk } 175129968Sphk if (tp->t_outq.c_cc == 0) 176129968Sphk ttwwakeup(tp); 177129968Sphk} 17873161Sjulian 17973161Sjulian/* 18073161Sjulian * This function creates and initializes a pair of ttys. 18173161Sjulian */ 18273161Sjulianstatic void 183130585Sphknmdminit(struct cdev *dev1) 18473161Sjulian{ 185130585Sphk struct cdev *dev2; 18673161Sjulian struct nm_softc *pt; 18773161Sjulian 188126077Sphk dev2 = dev1->si_drv2; 18973161Sjulian 190126077Sphk dev1->si_flags &= ~SI_CHEAPCLONE; 191126077Sphk dev2->si_flags &= ~SI_CHEAPCLONE; 19273161Sjulian 193126077Sphk pt = malloc(sizeof(*pt), M_NLMDM, M_WAITOK | M_ZERO); 194126077Sphk TAILQ_INSERT_TAIL(&nmdmhead, pt, pt_list); 195129968Sphk 19673161Sjulian dev1->si_drv1 = dev2->si_drv1 = pt; 197126077Sphk 198126077Sphk pt->part1.dev = dev1; 199126077Sphk pt->part2.dev = dev2; 200129968Sphk 201129968Sphk pt->part1.nm_tty = ttymalloc(pt->part1.nm_tty); 202129968Sphk pt->part1.nm_tty->t_oproc = nmdmstart; 203129968Sphk pt->part1.nm_tty->t_stop = nmdmstop; 204131579Sphk pt->part1.nm_tty->t_modem = nmdmmodem; 205129968Sphk pt->part1.nm_tty->t_dev = dev1; 206129968Sphk pt->part1.nm_tty->t_sc = &pt->part1; 207129968Sphk TASK_INIT(&pt->part1.pt_task, 0, nmdm_task_tty, pt->part1.nm_tty); 208129968Sphk 209129968Sphk pt->part2.nm_tty = ttymalloc(pt->part2.nm_tty); 210129968Sphk pt->part2.nm_tty->t_oproc = nmdmstart; 211129968Sphk pt->part2.nm_tty->t_stop = nmdmstop; 212131579Sphk pt->part2.nm_tty->t_modem = nmdmmodem; 213129968Sphk pt->part2.nm_tty->t_dev = dev2; 214129968Sphk pt->part2.nm_tty->t_sc = &pt->part2; 215129968Sphk TASK_INIT(&pt->part2.pt_task, 0, nmdm_task_tty, pt->part2.nm_tty); 216129968Sphk 217129968Sphk pt->part1.other = &pt->part2; 218129968Sphk pt->part2.other = &pt->part1; 219129968Sphk 220129968Sphk dev1->si_tty = pt->part1.nm_tty; 221129968Sphk dev1->si_drv1 = pt; 222129968Sphk 223129968Sphk dev2->si_tty = pt->part2.nm_tty; 224129968Sphk dev2->si_drv1 = pt; 22573161Sjulian} 22673161Sjulian 22790995Sjulian/* 22890995Sjulian * Device opened from userland 22990995Sjulian */ 23073161Sjulianstatic int 231130585Sphknmdmopen(struct cdev *dev, int flag, int devtype, struct thread *td) 23273161Sjulian{ 233129968Sphk struct tty *tp, *tp2; 23473161Sjulian int error; 23573161Sjulian struct nm_softc *pti; 236129968Sphk struct softpart *sp; 23773161Sjulian 238126077Sphk if (dev->si_drv1 == NULL) 239126077Sphk nmdminit(dev); 240126077Sphk pti = dev->si_drv1; 241129968Sphk if (pti->pt_prison != td->td_ucred->cr_prison) 242129968Sphk return (EBUSY); 24373161Sjulian 244129968Sphk tp = dev->si_tty; 245129968Sphk sp = tp->t_sc; 246129968Sphk tp2 = sp->other->nm_tty; 24790995Sjulian 24873161Sjulian if ((tp->t_state & TS_ISOPEN) == 0) { 249136680Sphk ttyinitmode(tp, 0, 0); 250129968Sphk ttsetwater(tp); /* XXX ? */ 25193593Sjhb } else if (tp->t_state & TS_XCLUDE && suser(td)) { 25273161Sjulian return (EBUSY); 25373161Sjulian } 25473161Sjulian 255130077Sphk error = ttyld_open(tp, dev); 25673161Sjulian return (error); 25773161Sjulian} 25873161Sjulian 259129968Sphkstatic int 260131579Sphknmdmmodem(struct tty *tp, int sigon, int sigoff) 261131579Sphk{ 262131579Sphk struct softpart *sp; 263131579Sphk int i; 264131579Sphk 265131579Sphk sp = tp->t_sc; 266131579Sphk if (sigon || sigoff) { 267131579Sphk if (sigon & SER_DTR) { 268131579Sphk sp->other->nm_dcd = 1; 269131579Sphk ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd); 270131579Sphk } 271131579Sphk if (sigoff & SER_DTR) { 272131579Sphk sp->other->nm_dcd = 0; 273131579Sphk ttyld_modem(sp->other->nm_tty, sp->other->nm_dcd); 274131579Sphk } 275131579Sphk return (0); 276131579Sphk } else { 277131579Sphk i = 0; 278131579Sphk if (sp->nm_dcd) 279131579Sphk i |= SER_DCD; 280131579Sphk if (sp->other->nm_dcd) 281131579Sphk i |= SER_DTR; 282131579Sphk return (i); 283131579Sphk } 284131579Sphk} 285131579Sphk 286131579Sphkstatic int 287130585Sphknmdmclose(struct cdev *dev, int flag, int mode, struct thread *td) 28873161Sjulian{ 28973161Sjulian 290132226Sphk return (tty_close(dev->si_tty)); 29173161Sjulian} 29273161Sjulian 29373161Sjulianstatic void 29490995Sjuliannmdmstart(struct tty *tp) 29573161Sjulian{ 296129968Sphk struct softpart *pt; 29773161Sjulian 298129968Sphk pt = tp->t_sc; 299129968Sphk taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 30073161Sjulian} 30173161Sjulian 30273161Sjulianstatic void 303129968Sphknmdmstop(struct tty *tp, int flush) 30473161Sjulian{ 305129968Sphk struct softpart *pt; 30673161Sjulian 307129968Sphk pt = tp->t_sc; 308129968Sphk taskqueue_enqueue(taskqueue_swi_giant, &pt->pt_task); 30973161Sjulian} 31073161Sjulian 31190995Sjulian/* 31290995Sjulian * Module handling 31390995Sjulian */ 31490995Sjulianstatic int 31590995Sjuliannmdm_modevent(module_t mod, int type, void *data) 31690995Sjulian{ 317126077Sphk static eventhandler_tag tag; 318126077Sphk struct nm_softc *pt, *tpt; 31990995Sjulian int error = 0; 32073161Sjulian 32190995Sjulian switch(type) { 322126077Sphk case MOD_LOAD: 323126845Sphk clone_setup(&nmdmclones); 324126077Sphk tag = EVENTHANDLER_REGISTER(dev_clone, nmdm_clone, 0, 1000); 325126077Sphk if (tag == NULL) 326126077Sphk return (ENOMEM); 32790995Sjulian break; 32873161Sjulian 32990995Sjulian case MOD_SHUTDOWN: 33090995Sjulian /* FALLTHROUGH */ 33190995Sjulian case MOD_UNLOAD: 332126077Sphk EVENTHANDLER_DEREGISTER(dev_clone, tag); 333126077Sphk TAILQ_FOREACH_SAFE(pt, &nmdmhead, pt_list, tpt) { 334126077Sphk destroy_dev(pt->part1.dev); 335126077Sphk TAILQ_REMOVE(&nmdmhead, pt, pt_list); 336126077Sphk free(pt, M_NLMDM); 337126077Sphk } 338126077Sphk clone_cleanup(&nmdmclones); 33990995Sjulian break; 34090995Sjulian default: 34190995Sjulian error = EOPNOTSUPP; 34290995Sjulian } 34390995Sjulian return (error); 34490995Sjulian} 34573161Sjulian 34690995SjulianDEV_MODULE(nmdm, nmdm_modevent, NULL); 347