14cf49a43SJulian Elischer /* 24cf49a43SJulian Elischer * ng_tty.c 3c398230bSWarner Losh */ 4c398230bSWarner Losh 5c398230bSWarner Losh /*- 64cf49a43SJulian Elischer * Copyright (c) 1996-1999 Whistle Communications, Inc. 74cf49a43SJulian Elischer * All rights reserved. 84cf49a43SJulian Elischer * 94cf49a43SJulian Elischer * Subject to the following obligations and disclaimer of warranty, use and 104cf49a43SJulian Elischer * redistribution of this software, in source or object code forms, with or 114cf49a43SJulian Elischer * without modifications are expressly permitted by Whistle Communications; 124cf49a43SJulian Elischer * provided, however, that: 134cf49a43SJulian Elischer * 1. Any and all reproductions of the source or object code must include the 144cf49a43SJulian Elischer * copyright notice above and the following disclaimer of warranties; and 154cf49a43SJulian Elischer * 2. No rights are granted, in any manner or form, to use Whistle 164cf49a43SJulian Elischer * Communications, Inc. trademarks, including the mark "WHISTLE 174cf49a43SJulian Elischer * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 184cf49a43SJulian Elischer * such appears in the above copyright notice or in the software. 194cf49a43SJulian Elischer * 204cf49a43SJulian Elischer * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 214cf49a43SJulian Elischer * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 224cf49a43SJulian Elischer * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 234cf49a43SJulian Elischer * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 244cf49a43SJulian Elischer * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 254cf49a43SJulian Elischer * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 264cf49a43SJulian Elischer * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 274cf49a43SJulian Elischer * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 284cf49a43SJulian Elischer * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 294cf49a43SJulian Elischer * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 304cf49a43SJulian Elischer * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 314cf49a43SJulian Elischer * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 324cf49a43SJulian Elischer * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 334cf49a43SJulian Elischer * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 344cf49a43SJulian Elischer * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 354cf49a43SJulian Elischer * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 364cf49a43SJulian Elischer * OF SUCH DAMAGE. 374cf49a43SJulian Elischer * 38cc3bbd68SJulian Elischer * Author: Archie Cobbs <archie@freebsd.org> 394cf49a43SJulian Elischer * 404cf49a43SJulian Elischer * $FreeBSD$ 4174f5c6aaSJulian Elischer * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $ 424cf49a43SJulian Elischer */ 434cf49a43SJulian Elischer 444cf49a43SJulian Elischer /* 454cf49a43SJulian Elischer * This file implements a terminal line discipline that is also a 464cf49a43SJulian Elischer * netgraph node. Installing this line discipline on a terminal device 474cf49a43SJulian Elischer * instantiates a new netgraph node of this type, which allows access 484cf49a43SJulian Elischer * to the device via the "hook" hook of the node. 494cf49a43SJulian Elischer * 504cf49a43SJulian Elischer * Once the line discipline is installed, you can find out the name 514cf49a43SJulian Elischer * of the corresponding netgraph node via a NGIOCGINFO ioctl(). 524cf49a43SJulian Elischer * 534cf49a43SJulian Elischer * Incoming characters are delievered to the hook one at a time, each 544cf49a43SJulian Elischer * in its own mbuf. You may optionally define a ``hotchar,'' which causes 554cf49a43SJulian Elischer * incoming characters to be buffered up until either the hotchar is 564cf49a43SJulian Elischer * seen or the mbuf is full (MHLEN bytes). Then all buffered characters 574cf49a43SJulian Elischer * are immediately delivered. 584cf49a43SJulian Elischer */ 594cf49a43SJulian Elischer 604cf49a43SJulian Elischer #include <sys/param.h> 614cf49a43SJulian Elischer #include <sys/systm.h> 624cf49a43SJulian Elischer #include <sys/conf.h> 63d1426d7fSGleb Smirnoff #include <sys/errno.h> 644cf49a43SJulian Elischer #include <sys/fcntl.h> 65d1426d7fSGleb Smirnoff #include <sys/ioccom.h> 66d1426d7fSGleb Smirnoff #include <sys/kernel.h> 67d1426d7fSGleb Smirnoff #include <sys/malloc.h> 68d1426d7fSGleb Smirnoff #include <sys/mbuf.h> 69acd3428bSRobert Watson #include <sys/priv.h> 70d1426d7fSGleb Smirnoff #include <sys/socket.h> 71d1426d7fSGleb Smirnoff #include <sys/syslog.h> 724cf49a43SJulian Elischer #include <sys/tty.h> 73b58a8a3bSJulian Elischer #include <sys/ttycom.h> 74d1426d7fSGleb Smirnoff 75d1426d7fSGleb Smirnoff #include <net/if.h> 76d1426d7fSGleb Smirnoff #include <net/if_var.h> 774cf49a43SJulian Elischer 784cf49a43SJulian Elischer #include <netgraph/ng_message.h> 794cf49a43SJulian Elischer #include <netgraph/netgraph.h> 804cf49a43SJulian Elischer #include <netgraph/ng_tty.h> 814cf49a43SJulian Elischer 824cf49a43SJulian Elischer /* Misc defs */ 834cf49a43SJulian Elischer #define MAX_MBUFQ 3 /* Max number of queued mbufs */ 844cf49a43SJulian Elischer #define NGT_HIWATER 400 /* High water mark on output */ 854cf49a43SJulian Elischer 864cf49a43SJulian Elischer /* Per-node private info */ 874cf49a43SJulian Elischer struct ngt_sc { 884cf49a43SJulian Elischer struct tty *tp; /* Terminal device */ 894cf49a43SJulian Elischer node_p node; /* Netgraph node */ 904cf49a43SJulian Elischer hook_p hook; /* Netgraph hook */ 91d1426d7fSGleb Smirnoff struct ifqueue outq; /* Queue of outgoing data */ 924cf49a43SJulian Elischer struct mbuf *m; /* Incoming data buffer */ 934cf49a43SJulian Elischer short hotchar; /* Hotchar, or -1 if none */ 944cf49a43SJulian Elischer u_int flags; /* Flags */ 959f3a2adcSGleb Smirnoff struct callout chand; /* See man timeout(9) */ 964cf49a43SJulian Elischer }; 974cf49a43SJulian Elischer typedef struct ngt_sc *sc_p; 984cf49a43SJulian Elischer 994cf49a43SJulian Elischer /* Flags */ 1004cf49a43SJulian Elischer #define FLG_DEBUG 0x0002 101d1426d7fSGleb Smirnoff #define FLG_DIE 0x0004 1024cf49a43SJulian Elischer 1034cf49a43SJulian Elischer /* Line discipline methods */ 10489c9c53dSPoul-Henning Kamp static int ngt_open(struct cdev *dev, struct tty *tp); 1054cf49a43SJulian Elischer static int ngt_close(struct tty *tp, int flag); 1064cf49a43SJulian Elischer static int ngt_read(struct tty *tp, struct uio *uio, int flag); 1074cf49a43SJulian Elischer static int ngt_write(struct tty *tp, struct uio *uio, int flag); 1084cf49a43SJulian Elischer static int ngt_tioctl(struct tty *tp, 109b40ce416SJulian Elischer u_long cmd, caddr_t data, int flag, struct thread *); 1104cf49a43SJulian Elischer static int ngt_input(int c, struct tty *tp); 1114cf49a43SJulian Elischer static int ngt_start(struct tty *tp); 1124cf49a43SJulian Elischer 1134cf49a43SJulian Elischer /* Netgraph methods */ 11474f5c6aaSJulian Elischer static ng_constructor_t ngt_constructor; 11574f5c6aaSJulian Elischer static ng_rcvmsg_t ngt_rcvmsg; 11674f5c6aaSJulian Elischer static ng_shutdown_t ngt_shutdown; 11774f5c6aaSJulian Elischer static ng_newhook_t ngt_newhook; 118859a4d16SJulian Elischer static ng_connect_t ngt_connect; 11974f5c6aaSJulian Elischer static ng_rcvdata_t ngt_rcvdata; 12074f5c6aaSJulian Elischer static ng_disconnect_t ngt_disconnect; 1214cf49a43SJulian Elischer static int ngt_mod_event(module_t mod, int event, void *data); 1224cf49a43SJulian Elischer 1234cf49a43SJulian Elischer /* Other stuff */ 1249f3a2adcSGleb Smirnoff static void ngt_timeout(node_p node, hook_p hook, void *arg1, int arg2); 1254cf49a43SJulian Elischer 1264cf49a43SJulian Elischer #define ERROUT(x) do { error = (x); goto done; } while (0) 1274cf49a43SJulian Elischer 1284cf49a43SJulian Elischer /* Line discipline descriptor */ 1294cf49a43SJulian Elischer static struct linesw ngt_disc = { 130d1426d7fSGleb Smirnoff .l_open = ngt_open, 131d1426d7fSGleb Smirnoff .l_close = ngt_close, 132d1426d7fSGleb Smirnoff .l_read = ngt_read, 133d1426d7fSGleb Smirnoff .l_write = ngt_write, 134d1426d7fSGleb Smirnoff .l_ioctl = ngt_tioctl, 135d1426d7fSGleb Smirnoff .l_rint = ngt_input, 136d1426d7fSGleb Smirnoff .l_start = ngt_start, 137d1426d7fSGleb Smirnoff .l_modem = ttymodem, 1384cf49a43SJulian Elischer }; 1394cf49a43SJulian Elischer 1404cf49a43SJulian Elischer /* Netgraph node type descriptor */ 1414cf49a43SJulian Elischer static struct ng_type typestruct = { 142f8aae777SJulian Elischer .version = NG_ABI_VERSION, 143f8aae777SJulian Elischer .name = NG_TTY_NODE_TYPE, 144f8aae777SJulian Elischer .mod_event = ngt_mod_event, 145f8aae777SJulian Elischer .constructor = ngt_constructor, 146f8aae777SJulian Elischer .rcvmsg = ngt_rcvmsg, 147f8aae777SJulian Elischer .shutdown = ngt_shutdown, 148f8aae777SJulian Elischer .newhook = ngt_newhook, 149f8aae777SJulian Elischer .connect = ngt_connect, 150f8aae777SJulian Elischer .rcvdata = ngt_rcvdata, 151f8aae777SJulian Elischer .disconnect = ngt_disconnect, 1524cf49a43SJulian Elischer }; 1534cf49a43SJulian Elischer NETGRAPH_INIT(tty, &typestruct); 1544cf49a43SJulian Elischer 155c61340f3SRobert Watson /* 156d1426d7fSGleb Smirnoff * Locking: 157d1426d7fSGleb Smirnoff * 158d1426d7fSGleb Smirnoff * - node private data and tp->t_lsc is protected by mutex in struct 159d1426d7fSGleb Smirnoff * ifqueue, locking is done using IF_XXX() macros. 160d1426d7fSGleb Smirnoff * - in all tty methods we should acquire node ifqueue mutex, when accessing 161d1426d7fSGleb Smirnoff * private data. 162d1426d7fSGleb Smirnoff * - in _rcvdata() we should use locked versions of IF_{EN,DE}QUEUE() since 163d1426d7fSGleb Smirnoff * we may have multiple _rcvdata() threads. 164d1426d7fSGleb Smirnoff * - when calling any of tty methods from netgraph methods, we should 165d1426d7fSGleb Smirnoff * acquire tty locking (now Giant). 166d1426d7fSGleb Smirnoff * 167d1426d7fSGleb Smirnoff * - ngt_unit is incremented atomically. 168c61340f3SRobert Watson */ 1694cf49a43SJulian Elischer 170d1426d7fSGleb Smirnoff #define NGTLOCK(sc) IF_LOCK(&sc->outq) 171d1426d7fSGleb Smirnoff #define NGTUNLOCK(sc) IF_UNLOCK(&sc->outq) 172d1426d7fSGleb Smirnoff 173d1426d7fSGleb Smirnoff static int ngt_unit; 174d1426d7fSGleb Smirnoff static int ngt_ldisc; 175c61340f3SRobert Watson 1764cf49a43SJulian Elischer /****************************************************************** 1774cf49a43SJulian Elischer LINE DISCIPLINE METHODS 1784cf49a43SJulian Elischer ******************************************************************/ 1794cf49a43SJulian Elischer 1804cf49a43SJulian Elischer /* 1814cf49a43SJulian Elischer * Set our line discipline on the tty. 182d1426d7fSGleb Smirnoff * Called from device open routine or ttioctl() 1834cf49a43SJulian Elischer */ 1844cf49a43SJulian Elischer static int 18589c9c53dSPoul-Henning Kamp ngt_open(struct cdev *dev, struct tty *tp) 1864cf49a43SJulian Elischer { 187b40ce416SJulian Elischer struct thread *const td = curthread; /* XXX */ 1884cf49a43SJulian Elischer char name[sizeof(NG_TTY_NODE_TYPE) + 8]; 1894cf49a43SJulian Elischer sc_p sc; 190d1426d7fSGleb Smirnoff int error; 1914cf49a43SJulian Elischer 1924cf49a43SJulian Elischer /* Super-user only */ 193acd3428bSRobert Watson error = priv_check(td, PRIV_NETGRAPH_TTY); 194acd3428bSRobert Watson if (error) 1954cf49a43SJulian Elischer return (error); 1964cf49a43SJulian Elischer 1974cf49a43SJulian Elischer /* Initialize private struct */ 198a163d034SWarner Losh MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO); 199d1426d7fSGleb Smirnoff if (sc == NULL) 200d1426d7fSGleb Smirnoff return (ENOMEM); 201d1426d7fSGleb Smirnoff 2024cf49a43SJulian Elischer sc->tp = tp; 203d1426d7fSGleb Smirnoff sc->hotchar = tp->t_hotchar = NG_TTY_DFL_HOTCHAR; 204d1426d7fSGleb Smirnoff mtx_init(&sc->outq.ifq_mtx, "ng_tty node+queue", NULL, MTX_DEF); 205d1426d7fSGleb Smirnoff IFQ_SET_MAXLEN(&sc->outq, MAX_MBUFQ); 206d1426d7fSGleb Smirnoff 207d1426d7fSGleb Smirnoff NGTLOCK(sc); 2084cf49a43SJulian Elischer 2094cf49a43SJulian Elischer /* Setup netgraph node */ 2104cf49a43SJulian Elischer error = ng_make_node_common(&typestruct, &sc->node); 2114cf49a43SJulian Elischer if (error) { 212d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 2134cf49a43SJulian Elischer FREE(sc, M_NETGRAPH); 214d1426d7fSGleb Smirnoff return (error); 2154cf49a43SJulian Elischer } 216d1426d7fSGleb Smirnoff 217d1426d7fSGleb Smirnoff atomic_add_int(&ngt_unit, 1); 218d1426d7fSGleb Smirnoff snprintf(name, sizeof(name), "%s%d", typestruct.name, ngt_unit); 2194cf49a43SJulian Elischer 2204cf49a43SJulian Elischer /* Assign node its name */ 2214cf49a43SJulian Elischer if ((error = ng_name_node(sc->node, name))) { 222d1426d7fSGleb Smirnoff sc->flags |= FLG_DIE; 223d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 22430400f03SJulian Elischer NG_NODE_UNREF(sc->node); 225d1426d7fSGleb Smirnoff log(LOG_ERR, "%s: node name exists?\n", name); 226d1426d7fSGleb Smirnoff return (error); 2274cf49a43SJulian Elischer } 2284cf49a43SJulian Elischer 229069154d5SJulian Elischer /* Set back pointers */ 23030400f03SJulian Elischer NG_NODE_SET_PRIVATE(sc->node, sc); 23108d79b63SPoul-Henning Kamp tp->t_lsc = sc; 232069154d5SJulian Elischer 233d1426d7fSGleb Smirnoff ng_callout_init(&sc->chand); 234d1426d7fSGleb Smirnoff 2354cf49a43SJulian Elischer /* 2364cf49a43SJulian Elischer * Pre-allocate cblocks to the an appropriate amount. 2374cf49a43SJulian Elischer * I'm not sure what is appropriate. 2384cf49a43SJulian Elischer */ 2394cf49a43SJulian Elischer ttyflush(tp, FREAD | FWRITE); 2404cf49a43SJulian Elischer clist_alloc_cblocks(&tp->t_canq, 0, 0); 2414cf49a43SJulian Elischer clist_alloc_cblocks(&tp->t_rawq, 0, 0); 2424cf49a43SJulian Elischer clist_alloc_cblocks(&tp->t_outq, 2434cf49a43SJulian Elischer MLEN + NGT_HIWATER, MLEN + NGT_HIWATER); 2444cf49a43SJulian Elischer 245d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 246d1426d7fSGleb Smirnoff 247d1426d7fSGleb Smirnoff return (0); 2484cf49a43SJulian Elischer } 2494cf49a43SJulian Elischer 2504cf49a43SJulian Elischer /* 2514cf49a43SJulian Elischer * Line specific close routine, called from device close routine 252d1426d7fSGleb Smirnoff * and from ttioctl. This causes the node to be destroyed as well. 2534cf49a43SJulian Elischer */ 2544cf49a43SJulian Elischer static int 2554cf49a43SJulian Elischer ngt_close(struct tty *tp, int flag) 2564cf49a43SJulian Elischer { 25708d79b63SPoul-Henning Kamp const sc_p sc = (sc_p) tp->t_lsc; 2584cf49a43SJulian Elischer 2594cf49a43SJulian Elischer ttyflush(tp, FREAD | FWRITE); 2604cf49a43SJulian Elischer clist_free_cblocks(&tp->t_outq); 2614cf49a43SJulian Elischer if (sc != NULL) { 262d1426d7fSGleb Smirnoff NGTLOCK(sc); 263d1426d7fSGleb Smirnoff if (callout_pending(&sc->chand)) 2649f3a2adcSGleb Smirnoff ng_uncallout(&sc->chand, sc->node); 26508d79b63SPoul-Henning Kamp tp->t_lsc = NULL; 266d1426d7fSGleb Smirnoff sc->flags |= FLG_DIE; 267d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 268d1426d7fSGleb Smirnoff ng_rmnode_self(sc->node); 2694cf49a43SJulian Elischer } 2704cf49a43SJulian Elischer return (0); 2714cf49a43SJulian Elischer } 2724cf49a43SJulian Elischer 2734cf49a43SJulian Elischer /* 2744cf49a43SJulian Elischer * Once the device has been turned into a node, we don't allow reading. 2754cf49a43SJulian Elischer */ 2764cf49a43SJulian Elischer static int 2774cf49a43SJulian Elischer ngt_read(struct tty *tp, struct uio *uio, int flag) 2784cf49a43SJulian Elischer { 2794cf49a43SJulian Elischer return (EIO); 2804cf49a43SJulian Elischer } 2814cf49a43SJulian Elischer 2824cf49a43SJulian Elischer /* 2834cf49a43SJulian Elischer * Once the device has been turned into a node, we don't allow writing. 2844cf49a43SJulian Elischer */ 2854cf49a43SJulian Elischer static int 2864cf49a43SJulian Elischer ngt_write(struct tty *tp, struct uio *uio, int flag) 2874cf49a43SJulian Elischer { 2884cf49a43SJulian Elischer return (EIO); 2894cf49a43SJulian Elischer } 2904cf49a43SJulian Elischer 2914cf49a43SJulian Elischer /* 2924cf49a43SJulian Elischer * We implement the NGIOCGINFO ioctl() defined in ng_message.h. 2934cf49a43SJulian Elischer */ 2944cf49a43SJulian Elischer static int 295b40ce416SJulian Elischer ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct thread *td) 2964cf49a43SJulian Elischer { 29708d79b63SPoul-Henning Kamp const sc_p sc = (sc_p) tp->t_lsc; 2984cf49a43SJulian Elischer 299d1426d7fSGleb Smirnoff if (sc == NULL) 300d1426d7fSGleb Smirnoff /* No node attached */ 301d1426d7fSGleb Smirnoff return (0); 302d1426d7fSGleb Smirnoff 3034cf49a43SJulian Elischer switch (cmd) { 3044cf49a43SJulian Elischer case NGIOCGINFO: 3054cf49a43SJulian Elischer { 3064cf49a43SJulian Elischer struct nodeinfo *const ni = (struct nodeinfo *) data; 3074cf49a43SJulian Elischer const node_p node = sc->node; 3084cf49a43SJulian Elischer 3094cf49a43SJulian Elischer bzero(ni, sizeof(*ni)); 310d1426d7fSGleb Smirnoff NGTLOCK(sc); 31130400f03SJulian Elischer if (NG_NODE_HAS_NAME(node)) 31230400f03SJulian Elischer strncpy(ni->name, NG_NODE_NAME(node), sizeof(ni->name) - 1); 31330400f03SJulian Elischer strncpy(ni->type, node->nd_type->name, sizeof(ni->type) - 1); 31430400f03SJulian Elischer ni->id = (u_int32_t) ng_node2ID(node); 31530400f03SJulian Elischer ni->hooks = NG_NODE_NUMHOOKS(node); 316d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 3174cf49a43SJulian Elischer break; 3184cf49a43SJulian Elischer } 3194cf49a43SJulian Elischer default: 320d1426d7fSGleb Smirnoff return (ENOIOCTL); 3214cf49a43SJulian Elischer } 322d1426d7fSGleb Smirnoff 323d1426d7fSGleb Smirnoff return (0); 3244cf49a43SJulian Elischer } 3254cf49a43SJulian Elischer 3264cf49a43SJulian Elischer /* 3274cf49a43SJulian Elischer * Receive data coming from the device. We get one character at 3284cf49a43SJulian Elischer * a time, which is kindof silly. 329d1426d7fSGleb Smirnoff * 330d1426d7fSGleb Smirnoff * Full locking of softc is not required, since we are the only 331d1426d7fSGleb Smirnoff * user of sc->m. 3324cf49a43SJulian Elischer */ 3334cf49a43SJulian Elischer static int 3344cf49a43SJulian Elischer ngt_input(int c, struct tty *tp) 3354cf49a43SJulian Elischer { 33631437823SSeigo Tanimura sc_p sc; 33731437823SSeigo Tanimura node_p node; 3384cf49a43SJulian Elischer struct mbuf *m; 339d1426d7fSGleb Smirnoff int error = 0; 3404cf49a43SJulian Elischer 34131437823SSeigo Tanimura sc = (sc_p) tp->t_lsc; 342d1426d7fSGleb Smirnoff if (sc == NULL) 343d1426d7fSGleb Smirnoff /* No node attached */ 3444cf49a43SJulian Elischer return (0); 345d1426d7fSGleb Smirnoff 34631437823SSeigo Tanimura node = sc->node; 34731437823SSeigo Tanimura 348d1426d7fSGleb Smirnoff if (tp != sc->tp) 349d1426d7fSGleb Smirnoff panic("ngt_input"); 3504cf49a43SJulian Elischer 3514cf49a43SJulian Elischer /* Check for error conditions */ 3524cf49a43SJulian Elischer if ((tp->t_state & TS_CONNECTED) == 0) { 3534cf49a43SJulian Elischer if (sc->flags & FLG_DEBUG) 35430400f03SJulian Elischer log(LOG_DEBUG, "%s: no carrier\n", NG_NODE_NAME(node)); 355d1426d7fSGleb Smirnoff return (0); 3564cf49a43SJulian Elischer } 3574cf49a43SJulian Elischer if (c & TTY_ERRORMASK) { 3584cf49a43SJulian Elischer /* framing error or overrun on this char */ 3594cf49a43SJulian Elischer if (sc->flags & FLG_DEBUG) 3604cf49a43SJulian Elischer log(LOG_DEBUG, "%s: line error %x\n", 36130400f03SJulian Elischer NG_NODE_NAME(node), c & TTY_ERRORMASK); 362d1426d7fSGleb Smirnoff return (0); 3634cf49a43SJulian Elischer } 3644cf49a43SJulian Elischer c &= TTY_CHARMASK; 3654cf49a43SJulian Elischer 3664cf49a43SJulian Elischer /* Get a new header mbuf if we need one */ 3674cf49a43SJulian Elischer if (!(m = sc->m)) { 368a163d034SWarner Losh MGETHDR(m, M_DONTWAIT, MT_DATA); 3694cf49a43SJulian Elischer if (!m) { 3704cf49a43SJulian Elischer if (sc->flags & FLG_DEBUG) 3714cf49a43SJulian Elischer log(LOG_ERR, 37230400f03SJulian Elischer "%s: can't get mbuf\n", NG_NODE_NAME(node)); 373d1426d7fSGleb Smirnoff return (ENOBUFS); 3744cf49a43SJulian Elischer } 3754e256e6eSArchie Cobbs m->m_len = m->m_pkthdr.len = 0; 3764e256e6eSArchie Cobbs m->m_pkthdr.rcvif = NULL; 3774cf49a43SJulian Elischer sc->m = m; 3784cf49a43SJulian Elischer } 3794cf49a43SJulian Elischer 3804cf49a43SJulian Elischer /* Add char to mbuf */ 3814cf49a43SJulian Elischer *mtod(m, u_char *) = c; 3824cf49a43SJulian Elischer m->m_data++; 3834cf49a43SJulian Elischer m->m_len++; 3844cf49a43SJulian Elischer m->m_pkthdr.len++; 3854cf49a43SJulian Elischer 3864cf49a43SJulian Elischer /* Ship off mbuf if it's time */ 3874cf49a43SJulian Elischer if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) { 3884cf49a43SJulian Elischer m->m_data = m->m_pktdat; 3894cf49a43SJulian Elischer sc->m = NULL; 390d1426d7fSGleb Smirnoff 391d1426d7fSGleb Smirnoff /* 392d1426d7fSGleb Smirnoff * We have built our mbuf without checking that we actually 393d1426d7fSGleb Smirnoff * have a hook to send it. This was done to avoid 394d1426d7fSGleb Smirnoff * acquiring mutex on each character. Check now. 395d1426d7fSGleb Smirnoff * 396d1426d7fSGleb Smirnoff */ 397d1426d7fSGleb Smirnoff 398d1426d7fSGleb Smirnoff NGTLOCK(sc); 399d1426d7fSGleb Smirnoff if (sc->hook == NULL) { 400d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 401d1426d7fSGleb Smirnoff m_freem(m); 402d1426d7fSGleb Smirnoff return (0); /* XXX: original behavior */ 4034cf49a43SJulian Elischer } 404d1426d7fSGleb Smirnoff NG_SEND_DATA_ONLY(error, sc->hook, m); /* Will queue */ 405d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 406d1426d7fSGleb Smirnoff } 407d1426d7fSGleb Smirnoff 4084cf49a43SJulian Elischer return (error); 4094cf49a43SJulian Elischer } 4104cf49a43SJulian Elischer 4114cf49a43SJulian Elischer /* 4124cf49a43SJulian Elischer * This is called when the device driver is ready for more output. 413d1426d7fSGleb Smirnoff * Also called from ngt_rcv_data() when a new mbuf is available for output. 4144cf49a43SJulian Elischer */ 4154cf49a43SJulian Elischer static int 4164cf49a43SJulian Elischer ngt_start(struct tty *tp) 4174cf49a43SJulian Elischer { 41808d79b63SPoul-Henning Kamp const sc_p sc = (sc_p) tp->t_lsc; 4194cf49a43SJulian Elischer 4204cf49a43SJulian Elischer while (tp->t_outq.c_cc < NGT_HIWATER) { /* XXX 2.2 specific ? */ 421d1426d7fSGleb Smirnoff struct mbuf *m; 4224cf49a43SJulian Elischer 4234cf49a43SJulian Elischer /* Remove first mbuf from queue */ 424d1426d7fSGleb Smirnoff IF_DEQUEUE(&sc->outq, m); 425d1426d7fSGleb Smirnoff if (m == NULL) 4264cf49a43SJulian Elischer break; 4274cf49a43SJulian Elischer 4284cf49a43SJulian Elischer /* Send as much of it as possible */ 429d1426d7fSGleb Smirnoff while (m != NULL) { 4304cf49a43SJulian Elischer int sent; 4314cf49a43SJulian Elischer 4324cf49a43SJulian Elischer sent = m->m_len 4334cf49a43SJulian Elischer - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq); 4344cf49a43SJulian Elischer m->m_data += sent; 4354cf49a43SJulian Elischer m->m_len -= sent; 4364cf49a43SJulian Elischer if (m->m_len > 0) 4374cf49a43SJulian Elischer break; /* device can't take no more */ 438ecde8f7cSMatthew Dillon m = m_free(m); 4394cf49a43SJulian Elischer } 4404cf49a43SJulian Elischer 4414cf49a43SJulian Elischer /* Put remainder of mbuf chain (if any) back on queue */ 442d1426d7fSGleb Smirnoff if (m != NULL) { 443d1426d7fSGleb Smirnoff IF_PREPEND(&sc->outq, m); 4444cf49a43SJulian Elischer break; 4454cf49a43SJulian Elischer } 4464cf49a43SJulian Elischer } 4474cf49a43SJulian Elischer 4484cf49a43SJulian Elischer /* Call output process whether or not there is any output. We are 4494cf49a43SJulian Elischer * being called in lieu of ttstart and must do what it would. */ 4502cccccddSPoul-Henning Kamp tt_oproc(tp); 4514cf49a43SJulian Elischer 4524cf49a43SJulian Elischer /* This timeout is needed for operation on a pseudo-tty, because the 4534cf49a43SJulian Elischer * pty code doesn't call pppstart after it has drained the t_outq. */ 454d1426d7fSGleb Smirnoff /* XXX: outq not locked */ 455d1426d7fSGleb Smirnoff if (!IFQ_IS_EMPTY(&sc->outq) && !callout_pending(&sc->chand)) 4569f3a2adcSGleb Smirnoff ng_callout(&sc->chand, sc->node, NULL, 1, ngt_timeout, NULL, 0); 457d1426d7fSGleb Smirnoff 4584cf49a43SJulian Elischer return (0); 4594cf49a43SJulian Elischer } 4604cf49a43SJulian Elischer 4614cf49a43SJulian Elischer /* 4624cf49a43SJulian Elischer * We still have data to output to the device, so try sending more. 4634cf49a43SJulian Elischer */ 4644cf49a43SJulian Elischer static void 4659f3a2adcSGleb Smirnoff ngt_timeout(node_p node, hook_p hook, void *arg1, int arg2) 4664cf49a43SJulian Elischer { 4679f3a2adcSGleb Smirnoff const sc_p sc = NG_NODE_PRIVATE(node); 4684cf49a43SJulian Elischer 469d1426d7fSGleb Smirnoff mtx_lock(&Giant); 4704cf49a43SJulian Elischer ngt_start(sc->tp); 471d1426d7fSGleb Smirnoff mtx_unlock(&Giant); 4724cf49a43SJulian Elischer } 4734cf49a43SJulian Elischer 4744cf49a43SJulian Elischer /****************************************************************** 4754cf49a43SJulian Elischer NETGRAPH NODE METHODS 4764cf49a43SJulian Elischer ******************************************************************/ 4774cf49a43SJulian Elischer 4784cf49a43SJulian Elischer /* 4794cf49a43SJulian Elischer * Initialize a new node of this type. 4804cf49a43SJulian Elischer * 4814cf49a43SJulian Elischer * We only allow nodes to be created as a result of setting 4824cf49a43SJulian Elischer * the line discipline on a tty, so always return an error if not. 4834cf49a43SJulian Elischer */ 4844cf49a43SJulian Elischer static int 485069154d5SJulian Elischer ngt_constructor(node_p node) 4864cf49a43SJulian Elischer { 4874cf49a43SJulian Elischer return (EOPNOTSUPP); 4884cf49a43SJulian Elischer } 4894cf49a43SJulian Elischer 4904cf49a43SJulian Elischer /* 4914cf49a43SJulian Elischer * Add a new hook. There can only be one. 4924cf49a43SJulian Elischer */ 4934cf49a43SJulian Elischer static int 4944cf49a43SJulian Elischer ngt_newhook(node_p node, hook_p hook, const char *name) 4954cf49a43SJulian Elischer { 49630400f03SJulian Elischer const sc_p sc = NG_NODE_PRIVATE(node); 4974cf49a43SJulian Elischer 4984cf49a43SJulian Elischer if (strcmp(name, NG_TTY_HOOK)) 4994cf49a43SJulian Elischer return (EINVAL); 500d1426d7fSGleb Smirnoff 5014cf49a43SJulian Elischer if (sc->hook) 502d1426d7fSGleb Smirnoff return (EISCONN); 503d1426d7fSGleb Smirnoff 504d1426d7fSGleb Smirnoff NGTLOCK(sc); 5054cf49a43SJulian Elischer sc->hook = hook; 506d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 507d1426d7fSGleb Smirnoff 508d1426d7fSGleb Smirnoff return (0); 5094cf49a43SJulian Elischer } 5104cf49a43SJulian Elischer 5114cf49a43SJulian Elischer /* 512d1426d7fSGleb Smirnoff * Set the hook into queueing mode (for outgoing packets), 513d1426d7fSGleb Smirnoff * so that we wont deliver mbuf thru the whole graph holding 514d1426d7fSGleb Smirnoff * tty locks. 515859a4d16SJulian Elischer */ 516859a4d16SJulian Elischer static int 517859a4d16SJulian Elischer ngt_connect(hook_p hook) 518859a4d16SJulian Elischer { 519d1426d7fSGleb Smirnoff NG_HOOK_FORCE_QUEUE(NG_HOOK_PEER(hook)); 520d1426d7fSGleb Smirnoff /* 521d1426d7fSGleb Smirnoff * XXX: While ngt_start() is Giant-locked, queue incoming 522d1426d7fSGleb Smirnoff * packets, too. Otherwise we acquire Giant holding some 523d1426d7fSGleb Smirnoff * IP stack locks, e.g. divinp, and this makes WITNESS scream. 524d1426d7fSGleb Smirnoff */ 525d1426d7fSGleb Smirnoff NG_HOOK_FORCE_QUEUE(hook); 526859a4d16SJulian Elischer return (0); 527859a4d16SJulian Elischer } 528859a4d16SJulian Elischer 529859a4d16SJulian Elischer /* 5304cf49a43SJulian Elischer * Disconnect the hook 5314cf49a43SJulian Elischer */ 5324cf49a43SJulian Elischer static int 5334cf49a43SJulian Elischer ngt_disconnect(hook_p hook) 5344cf49a43SJulian Elischer { 53530400f03SJulian Elischer const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 5364cf49a43SJulian Elischer 5374cf49a43SJulian Elischer if (hook != sc->hook) 5386e551fb6SDavid E. O'Brien panic(__func__); 539d1426d7fSGleb Smirnoff 540d1426d7fSGleb Smirnoff NGTLOCK(sc); 5414cf49a43SJulian Elischer sc->hook = NULL; 542d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 543d1426d7fSGleb Smirnoff 5444cf49a43SJulian Elischer return (0); 5454cf49a43SJulian Elischer } 5464cf49a43SJulian Elischer 5474cf49a43SJulian Elischer /* 5484cf49a43SJulian Elischer * Remove this node. The does the netgraph portion of the shutdown. 5494cf49a43SJulian Elischer * This should only be called indirectly from ngt_close(). 550d1426d7fSGleb Smirnoff * 551d1426d7fSGleb Smirnoff * tp->t_lsc is already NULL, so we should be protected from 552d1426d7fSGleb Smirnoff * tty calls now. 5534cf49a43SJulian Elischer */ 5544cf49a43SJulian Elischer static int 5554cf49a43SJulian Elischer ngt_shutdown(node_p node) 5564cf49a43SJulian Elischer { 55730400f03SJulian Elischer const sc_p sc = NG_NODE_PRIVATE(node); 5584cf49a43SJulian Elischer 559d1426d7fSGleb Smirnoff NGTLOCK(sc); 560d1426d7fSGleb Smirnoff if (!(sc->flags & FLG_DIE)) { 561d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 5624cf49a43SJulian Elischer return (EOPNOTSUPP); 563d1426d7fSGleb Smirnoff } 564d1426d7fSGleb Smirnoff NGTUNLOCK(sc); 565d1426d7fSGleb Smirnoff 566d1426d7fSGleb Smirnoff /* Free resources */ 567d1426d7fSGleb Smirnoff _IF_DRAIN(&sc->outq); 568d1426d7fSGleb Smirnoff mtx_destroy(&(sc)->outq.ifq_mtx); 5694cf49a43SJulian Elischer m_freem(sc->m); 570d1426d7fSGleb Smirnoff NG_NODE_UNREF(sc->node); 5714cf49a43SJulian Elischer FREE(sc, M_NETGRAPH); 572d1426d7fSGleb Smirnoff 5734cf49a43SJulian Elischer return (0); 5744cf49a43SJulian Elischer } 5754cf49a43SJulian Elischer 5764cf49a43SJulian Elischer /* 5774cf49a43SJulian Elischer * Receive incoming data from netgraph system. Put it on our 5784cf49a43SJulian Elischer * output queue and start output if necessary. 5794cf49a43SJulian Elischer */ 5804cf49a43SJulian Elischer static int 581069154d5SJulian Elischer ngt_rcvdata(hook_p hook, item_p item) 5824cf49a43SJulian Elischer { 58330400f03SJulian Elischer const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 584069154d5SJulian Elischer struct mbuf *m; 585d1426d7fSGleb Smirnoff int qlen; 5864cf49a43SJulian Elischer 5874cf49a43SJulian Elischer if (hook != sc->hook) 5886e551fb6SDavid E. O'Brien panic(__func__); 589069154d5SJulian Elischer 590069154d5SJulian Elischer NGI_GET_M(item, m); 591069154d5SJulian Elischer NG_FREE_ITEM(item); 592d1426d7fSGleb Smirnoff 593d1426d7fSGleb Smirnoff IF_LOCK(&sc->outq); 594d1426d7fSGleb Smirnoff if (_IF_QFULL(&sc->outq)) { 595d1426d7fSGleb Smirnoff _IF_DROP(&sc->outq); 596d1426d7fSGleb Smirnoff IF_UNLOCK(&sc->outq); 597d1426d7fSGleb Smirnoff NG_FREE_M(m); 598d1426d7fSGleb Smirnoff return (ENOBUFS); 599d1426d7fSGleb Smirnoff } 600d1426d7fSGleb Smirnoff 601d1426d7fSGleb Smirnoff _IF_ENQUEUE(&sc->outq, m); 602d1426d7fSGleb Smirnoff qlen = sc->outq.ifq_len; 603d1426d7fSGleb Smirnoff IF_UNLOCK(&sc->outq); 604d1426d7fSGleb Smirnoff 605d1426d7fSGleb Smirnoff /* 606d1426d7fSGleb Smirnoff * If qlen > 1, then we should already have a scheduled callout. 607d1426d7fSGleb Smirnoff */ 608d1426d7fSGleb Smirnoff if (qlen == 1) { 609d1426d7fSGleb Smirnoff mtx_lock(&Giant); 6104cf49a43SJulian Elischer ngt_start(sc->tp); 611d1426d7fSGleb Smirnoff mtx_unlock(&Giant); 612d1426d7fSGleb Smirnoff } 613d1426d7fSGleb Smirnoff 614d1426d7fSGleb Smirnoff return (0); 6154cf49a43SJulian Elischer } 6164cf49a43SJulian Elischer 6174cf49a43SJulian Elischer /* 6184cf49a43SJulian Elischer * Receive control message 6194cf49a43SJulian Elischer */ 6204cf49a43SJulian Elischer static int 621069154d5SJulian Elischer ngt_rcvmsg(node_p node, item_p item, hook_p lasthook) 6224cf49a43SJulian Elischer { 62330400f03SJulian Elischer const sc_p sc = NG_NODE_PRIVATE(node); 624d1426d7fSGleb Smirnoff struct ng_mesg *msg, *resp = NULL; 6254cf49a43SJulian Elischer int error = 0; 6264cf49a43SJulian Elischer 627069154d5SJulian Elischer NGI_GET_MSG(item, msg); 6284cf49a43SJulian Elischer switch (msg->header.typecookie) { 6294cf49a43SJulian Elischer case NGM_TTY_COOKIE: 6304cf49a43SJulian Elischer switch (msg->header.cmd) { 6314cf49a43SJulian Elischer case NGM_TTY_SET_HOTCHAR: 6324cf49a43SJulian Elischer { 6334cf49a43SJulian Elischer int hotchar; 6344cf49a43SJulian Elischer 6354cf49a43SJulian Elischer if (msg->header.arglen != sizeof(int)) 6364cf49a43SJulian Elischer ERROUT(EINVAL); 6374cf49a43SJulian Elischer hotchar = *((int *) msg->data); 6384cf49a43SJulian Elischer if (hotchar != (u_char) hotchar && hotchar != -1) 6394cf49a43SJulian Elischer ERROUT(EINVAL); 6404cf49a43SJulian Elischer sc->hotchar = hotchar; /* race condition is OK */ 6414cf49a43SJulian Elischer break; 6424cf49a43SJulian Elischer } 6434cf49a43SJulian Elischer case NGM_TTY_GET_HOTCHAR: 6444cf49a43SJulian Elischer NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT); 6454cf49a43SJulian Elischer if (!resp) 6464cf49a43SJulian Elischer ERROUT(ENOMEM); 6474cf49a43SJulian Elischer /* Race condition here is OK */ 6484cf49a43SJulian Elischer *((int *) resp->data) = sc->hotchar; 6494cf49a43SJulian Elischer break; 6504cf49a43SJulian Elischer default: 6514cf49a43SJulian Elischer ERROUT(EINVAL); 6524cf49a43SJulian Elischer } 6534cf49a43SJulian Elischer break; 6544cf49a43SJulian Elischer default: 6554cf49a43SJulian Elischer ERROUT(EINVAL); 6564cf49a43SJulian Elischer } 6574cf49a43SJulian Elischer done: 658069154d5SJulian Elischer NG_RESPOND_MSG(error, node, item, resp); 659069154d5SJulian Elischer NG_FREE_MSG(msg); 6604cf49a43SJulian Elischer return (error); 6614cf49a43SJulian Elischer } 6624cf49a43SJulian Elischer 6634cf49a43SJulian Elischer /****************************************************************** 6644cf49a43SJulian Elischer INITIALIZATION 6654cf49a43SJulian Elischer ******************************************************************/ 6664cf49a43SJulian Elischer 6674cf49a43SJulian Elischer /* 6684cf49a43SJulian Elischer * Handle loading and unloading for this node type 6694cf49a43SJulian Elischer */ 6704cf49a43SJulian Elischer static int 6714cf49a43SJulian Elischer ngt_mod_event(module_t mod, int event, void *data) 6724cf49a43SJulian Elischer { 673d1426d7fSGleb Smirnoff int error = 0; 6744cf49a43SJulian Elischer 6754cf49a43SJulian Elischer switch (event) { 6764cf49a43SJulian Elischer case MOD_LOAD: 6774cf49a43SJulian Elischer 6784cf49a43SJulian Elischer /* Register line discipline */ 679d1426d7fSGleb Smirnoff mtx_lock(&Giant); 680b58a8a3bSJulian Elischer if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) { 681d1426d7fSGleb Smirnoff mtx_unlock(&Giant); 6824cf49a43SJulian Elischer log(LOG_ERR, "%s: can't register line discipline", 6836e551fb6SDavid E. O'Brien __func__); 6844cf49a43SJulian Elischer return (EIO); 6854cf49a43SJulian Elischer } 686d1426d7fSGleb Smirnoff mtx_unlock(&Giant); 6874cf49a43SJulian Elischer break; 6884cf49a43SJulian Elischer 6894cf49a43SJulian Elischer case MOD_UNLOAD: 6904cf49a43SJulian Elischer 6914cf49a43SJulian Elischer /* Unregister line discipline */ 692d1426d7fSGleb Smirnoff mtx_lock(&Giant); 6934cf49a43SJulian Elischer ldisc_deregister(ngt_ldisc); 694d1426d7fSGleb Smirnoff mtx_unlock(&Giant); 6954cf49a43SJulian Elischer break; 6964cf49a43SJulian Elischer 6974cf49a43SJulian Elischer default: 6984cf49a43SJulian Elischer error = EOPNOTSUPP; 6994cf49a43SJulian Elischer break; 7004cf49a43SJulian Elischer } 7014cf49a43SJulian Elischer return (error); 7024cf49a43SJulian Elischer } 703