1210284Sjmallett/* 2215990Sjmallett * ng_tty.c 3215990Sjmallett */ 4210284Sjmallett 5210284Sjmallett/*- 6215990Sjmallett * Copyright (c) 1996-1999 Whistle Communications, Inc. 7215990Sjmallett * All rights reserved. 8215990Sjmallett * 9210284Sjmallett * Subject to the following obligations and disclaimer of warranty, use and 10215990Sjmallett * redistribution of this software, in source or object code forms, with or 11215990Sjmallett * without modifications are expressly permitted by Whistle Communications; 12210284Sjmallett * provided, however, that: 13215990Sjmallett * 1. Any and all reproductions of the source or object code must include the 14215990Sjmallett * copyright notice above and the following disclaimer of warranties; and 15215990Sjmallett * 2. No rights are granted, in any manner or form, to use Whistle 16215990Sjmallett * Communications, Inc. trademarks, including the mark "WHISTLE 17215990Sjmallett * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 18215990Sjmallett * such appears in the above copyright notice or in the software. 19215990Sjmallett * 20215990Sjmallett * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 21215990Sjmallett * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 22215990Sjmallett * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 23215990Sjmallett * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 24215990Sjmallett * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 25215990Sjmallett * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 26215990Sjmallett * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 27215990Sjmallett * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 28215990Sjmallett * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 29215990Sjmallett * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 30215990Sjmallett * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 31215990Sjmallett * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 32215990Sjmallett * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 33215990Sjmallett * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34215990Sjmallett * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35215990Sjmallett * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 36215990Sjmallett * OF SUCH DAMAGE. 37215990Sjmallett * 38210284Sjmallett * Author: Archie Cobbs <archie@freebsd.org> 39210284Sjmallett * 40210284Sjmallett * Updated by Andrew Thompson <thompsa@FreeBSD.org> for MPSAFE TTY. 41210284Sjmallett * 42210284Sjmallett * $FreeBSD$ 43210284Sjmallett * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $ 44215990Sjmallett */ 45210284Sjmallett 46210284Sjmallett/* 47210284Sjmallett * This file implements TTY hooks to link in to the netgraph system. The node 48210284Sjmallett * is created and then passed the callers opened TTY file descriptor number to 49215990Sjmallett * NGM_TTY_SET_TTY, this will hook the tty via ttyhook_register(). 50210284Sjmallett * 51210284Sjmallett * Incoming data is delivered directly to ng_tty via the TTY bypass hook as a 52210284Sjmallett * buffer pointer and length, this is converted to a mbuf and passed to the 53210284Sjmallett * peer. 54210284Sjmallett * 55210284Sjmallett * If the TTY device does not support bypass then incoming characters are 56210284Sjmallett * delivered to the hook one at a time, each in its own mbuf. You may 57210284Sjmallett * optionally define a ``hotchar,'' which causes incoming characters to be 58210284Sjmallett * buffered up until either the hotchar is seen or the mbuf is full (MHLEN 59210284Sjmallett * bytes). Then all buffered characters are immediately delivered. 60210284Sjmallett */ 61210284Sjmallett 62210284Sjmallett#include <sys/param.h> 63210284Sjmallett#include <sys/systm.h> 64210284Sjmallett#include <sys/conf.h> 65210284Sjmallett#include <sys/errno.h> 66210284Sjmallett#include <sys/fcntl.h> 67210284Sjmallett#include <sys/ioccom.h> 68210284Sjmallett#include <sys/kernel.h> 69210284Sjmallett#include <sys/malloc.h> 70210284Sjmallett#include <sys/mbuf.h> 71210284Sjmallett#include <sys/priv.h> 72210284Sjmallett#include <sys/socket.h> 73210284Sjmallett#include <sys/syslog.h> 74210284Sjmallett#include <sys/tty.h> 75210284Sjmallett#include <sys/ttycom.h> 76210284Sjmallett#include <sys/proc.h> 77210284Sjmallett 78215990Sjmallett#include <net/if.h> 79210284Sjmallett#include <net/if_var.h> 80210284Sjmallett 81210284Sjmallett#include <netgraph/ng_message.h> 82210284Sjmallett#include <netgraph/netgraph.h> 83210284Sjmallett#include <netgraph/ng_tty.h> 84210284Sjmallett 85210284Sjmallett/* Per-node private info */ 86210284Sjmallettstruct ngt_softc { 87210284Sjmallett struct tty *tp; /* Terminal device */ 88210284Sjmallett node_p node; /* Netgraph node */ 89210284Sjmallett hook_p hook; /* Netgraph hook */ 90210284Sjmallett struct ifqueue outq; /* Queue of outgoing data */ 91210284Sjmallett size_t outqlen; /* Number of bytes in outq */ 92210284Sjmallett struct mbuf *m; /* Incoming non-bypass data buffer */ 93210284Sjmallett short hotchar; /* Hotchar, or -1 if none */ 94210284Sjmallett u_int flags; /* Flags */ 95210284Sjmallett}; 96210284Sjmalletttypedef struct ngt_softc *sc_p; 97210284Sjmallett 98210284Sjmallett/* Flags */ 99210284Sjmallett#define FLG_DEBUG 0x0002 100210284Sjmallett 101210284Sjmallett/* Netgraph methods */ 102210284Sjmallettstatic ng_constructor_t ngt_constructor; 103210284Sjmallettstatic ng_rcvmsg_t ngt_rcvmsg; 104210284Sjmallettstatic ng_shutdown_t ngt_shutdown; 105210284Sjmallettstatic ng_newhook_t ngt_newhook; 106210284Sjmallettstatic ng_connect_t ngt_connect; 107210284Sjmallettstatic ng_rcvdata_t ngt_rcvdata; 108210284Sjmallettstatic ng_disconnect_t ngt_disconnect; 109210284Sjmallett 110210284Sjmallett#define ERROUT(x) do { error = (x); goto done; } while (0) 111210284Sjmallett 112210284Sjmallettstatic th_getc_inject_t ngt_getc_inject; 113210284Sjmallettstatic th_getc_poll_t ngt_getc_poll; 114210284Sjmallettstatic th_rint_t ngt_rint; 115210284Sjmallettstatic th_rint_bypass_t ngt_rint_bypass; 116210284Sjmallettstatic th_rint_poll_t ngt_rint_poll; 117210284Sjmallett 118210284Sjmallettstatic struct ttyhook ngt_hook = { 119210284Sjmallett .th_getc_inject = ngt_getc_inject, 120210284Sjmallett .th_getc_poll = ngt_getc_poll, 121210284Sjmallett .th_rint = ngt_rint, 122210284Sjmallett .th_rint_bypass = ngt_rint_bypass, 123210284Sjmallett .th_rint_poll = ngt_rint_poll, 124210284Sjmallett}; 125215990Sjmallett 126210284Sjmallett/* Netgraph node type descriptor */ 127215990Sjmallettstatic struct ng_type typestruct = { 128215990Sjmallett .version = NG_ABI_VERSION, 129210284Sjmallett .name = NG_TTY_NODE_TYPE, 130210284Sjmallett .constructor = ngt_constructor, 131210284Sjmallett .rcvmsg = ngt_rcvmsg, 132210284Sjmallett .shutdown = ngt_shutdown, 133210284Sjmallett .newhook = ngt_newhook, 134210284Sjmallett .connect = ngt_connect, 135210284Sjmallett .rcvdata = ngt_rcvdata, 136210284Sjmallett .disconnect = ngt_disconnect, 137210284Sjmallett}; 138210284SjmallettNETGRAPH_INIT(tty, &typestruct); 139210284Sjmallett 140210284Sjmallett#define NGTLOCK(sc) IF_LOCK(&sc->outq) 141210284Sjmallett#define NGTUNLOCK(sc) IF_UNLOCK(&sc->outq) 142210284Sjmallett 143210284Sjmallett/****************************************************************** 144210284Sjmallett NETGRAPH NODE METHODS 145210284Sjmallett******************************************************************/ 146210284Sjmallett 147210284Sjmallett/* 148210284Sjmallett * Initialize a new node of this type. 149210284Sjmallett * 150210284Sjmallett * We only allow nodes to be created as a result of setting 151210284Sjmallett * the line discipline on a tty, so always return an error if not. 152210284Sjmallett */ 153210284Sjmallettstatic int 154210284Sjmallettngt_constructor(node_p node) 155210284Sjmallett{ 156210284Sjmallett sc_p sc; 157210284Sjmallett 158210284Sjmallett /* Allocate private structure */ 159210284Sjmallett sc = malloc(sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO); 160210284Sjmallett 161210284Sjmallett NG_NODE_SET_PRIVATE(node, sc); 162210284Sjmallett sc->node = node; 163210284Sjmallett 164210284Sjmallett mtx_init(&sc->outq.ifq_mtx, "ng_tty node+queue", NULL, MTX_DEF); 165210284Sjmallett IFQ_SET_MAXLEN(&sc->outq, ifqmaxlen); 166210284Sjmallett 167210284Sjmallett return (0); 168210284Sjmallett} 169210284Sjmallett 170210284Sjmallett/* 171210284Sjmallett * Add a new hook. There can only be one. 172215990Sjmallett */ 173215990Sjmallettstatic int 174215990Sjmallettngt_newhook(node_p node, hook_p hook, const char *name) 175215990Sjmallett{ 176215990Sjmallett const sc_p sc = NG_NODE_PRIVATE(node); 177215990Sjmallett 178215990Sjmallett if (strcmp(name, NG_TTY_HOOK)) 179215990Sjmallett return (EINVAL); 180215990Sjmallett 181210284Sjmallett if (sc->hook) 182210284Sjmallett return (EISCONN); 183210284Sjmallett 184210284Sjmallett NGTLOCK(sc); 185210284Sjmallett sc->hook = hook; 186210284Sjmallett NGTUNLOCK(sc); 187210284Sjmallett 188210284Sjmallett return (0); 189210284Sjmallett} 190210284Sjmallett 191210284Sjmallett/* 192210284Sjmallett * Set the hook into queueing mode (for outgoing packets), 193210284Sjmallett * so that we wont deliver mbuf thru the whole graph holding 194210284Sjmallett * tty locks. 195210284Sjmallett */ 196210284Sjmallettstatic int 197212844Sjmallettngt_connect(hook_p hook) 198210284Sjmallett{ 199212844Sjmallett NG_HOOK_FORCE_QUEUE(hook); 200212844Sjmallett return (0); 201212844Sjmallett} 202210284Sjmallett 203210284Sjmallett/* 204210284Sjmallett * Disconnect the hook 205210284Sjmallett */ 206210284Sjmallettstatic int 207210284Sjmallettngt_disconnect(hook_p hook) 208215990Sjmallett{ 209215990Sjmallett const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 210215990Sjmallett 211215990Sjmallett if (hook != sc->hook) 212215990Sjmallett panic("%s", __func__); 213215990Sjmallett 214215014Sjmallett NGTLOCK(sc); 215215990Sjmallett sc->hook = NULL; 216215990Sjmallett NGTUNLOCK(sc); 217215990Sjmallett 218215014Sjmallett return (0); 219210284Sjmallett} 220210284Sjmallett 221210284Sjmallett/* 222210284Sjmallett * Remove this node. The does the netgraph portion of the shutdown. 223210284Sjmallett */ 224210311Sjmallettstatic int 225210311Sjmallettngt_shutdown(node_p node) 226216476Sjmallett{ 227210311Sjmallett const sc_p sc = NG_NODE_PRIVATE(node); 228210284Sjmallett struct tty *tp; 229210284Sjmallett 230215990Sjmallett tp = sc->tp; 231215990Sjmallett if (tp != NULL) { 232215990Sjmallett tty_lock(tp); 233215990Sjmallett ttyhook_unregister(tp); 234215990Sjmallett } 235215990Sjmallett /* Free resources */ 236215990Sjmallett IF_DRAIN(&sc->outq); 237215990Sjmallett mtx_destroy(&(sc)->outq.ifq_mtx); 238215990Sjmallett NG_NODE_UNREF(sc->node); 239215990Sjmallett free(sc, M_NETGRAPH); 240215990Sjmallett 241215990Sjmallett return (0); 242215990Sjmallett} 243215990Sjmallett 244210284Sjmallett/* 245210284Sjmallett * Receive control message 246210284Sjmallett */ 247210284Sjmallettstatic int 248210284Sjmallettngt_rcvmsg(node_p node, item_p item, hook_p lasthook) 249210284Sjmallett{ 250210284Sjmallett struct proc *p; 251210284Sjmallett const sc_p sc = NG_NODE_PRIVATE(node); 252210284Sjmallett struct ng_mesg *msg, *resp = NULL; 253210284Sjmallett int error = 0; 254210284Sjmallett 255210284Sjmallett NGI_GET_MSG(item, msg); 256210284Sjmallett switch (msg->header.typecookie) { 257210284Sjmallett case NGM_TTY_COOKIE: 258210284Sjmallett switch (msg->header.cmd) { 259210284Sjmallett case NGM_TTY_SET_TTY: 260210284Sjmallett if (sc->tp != NULL) 261210284Sjmallett return (EBUSY); 262210284Sjmallett 263210284Sjmallett p = pfind(((int *)msg->data)[0]); 264210284Sjmallett if (p == NULL || (p->p_flag & P_WEXIT)) 265210284Sjmallett return (ESRCH); 266210284Sjmallett _PHOLD(p); 267210284Sjmallett PROC_UNLOCK(p); 268210284Sjmallett error = ttyhook_register(&sc->tp, p, ((int *)msg->data)[1], 269210284Sjmallett &ngt_hook, sc); 270210284Sjmallett PRELE(p); 271210284Sjmallett if (error != 0) 272210284Sjmallett return (error); 273210284Sjmallett break; 274210284Sjmallett case NGM_TTY_SET_HOTCHAR: 275210284Sjmallett { 276210284Sjmallett int hotchar; 277210284Sjmallett 278210284Sjmallett if (msg->header.arglen != sizeof(int)) 279210284Sjmallett ERROUT(EINVAL); 280210284Sjmallett hotchar = *((int *) msg->data); 281210284Sjmallett if (hotchar != (u_char) hotchar && hotchar != -1) 282210284Sjmallett ERROUT(EINVAL); 283210284Sjmallett sc->hotchar = hotchar; /* race condition is OK */ 284210284Sjmallett break; 285210284Sjmallett } 286210284Sjmallett case NGM_TTY_GET_HOTCHAR: 287210284Sjmallett NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT); 288210284Sjmallett if (!resp) 289210284Sjmallett ERROUT(ENOMEM); 290210284Sjmallett /* Race condition here is OK */ 291215990Sjmallett *((int *) resp->data) = sc->hotchar; 292215990Sjmallett break; 293215990Sjmallett default: 294215990Sjmallett ERROUT(EINVAL); 295215990Sjmallett } 296215990Sjmallett break; 297215990Sjmallett default: 298215990Sjmallett ERROUT(EINVAL); 299215990Sjmallett } 300210284Sjmallettdone: 301210284Sjmallett NG_RESPOND_MSG(error, node, item, resp); 302210284Sjmallett NG_FREE_MSG(msg); 303210284Sjmallett return (error); 304210284Sjmallett} 305210284Sjmallett 306210284Sjmallett/* 307210284Sjmallett * Receive incoming data from netgraph system. Put it on our 308210284Sjmallett * output queue and start output if necessary. 309210284Sjmallett */ 310210284Sjmallettstatic int 311212844Sjmallettngt_rcvdata(hook_p hook, item_p item) 312210284Sjmallett{ 313212844Sjmallett const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 314212844Sjmallett struct tty *tp = sc->tp; 315212844Sjmallett struct mbuf *m; 316210284Sjmallett 317210284Sjmallett if (hook != sc->hook) 318210284Sjmallett panic("%s", __func__); 319210284Sjmallett 320210284Sjmallett NGI_GET_M(item, m); 321210284Sjmallett NG_FREE_ITEM(item); 322215990Sjmallett 323215990Sjmallett if (tp == NULL) { 324215990Sjmallett NG_FREE_M(m); 325215990Sjmallett return (ENXIO); 326215990Sjmallett } 327215990Sjmallett 328215014Sjmallett IF_LOCK(&sc->outq); 329215014Sjmallett if (_IF_QFULL(&sc->outq)) { 330215990Sjmallett _IF_DROP(&sc->outq); 331215990Sjmallett IF_UNLOCK(&sc->outq); 332215014Sjmallett NG_FREE_M(m); 333215990Sjmallett return (ENOBUFS); 334210284Sjmallett } 335210284Sjmallett 336210284Sjmallett _IF_ENQUEUE(&sc->outq, m); 337210284Sjmallett sc->outqlen += m->m_pkthdr.len; 338210311Sjmallett IF_UNLOCK(&sc->outq); 339210311Sjmallett 340216476Sjmallett /* notify the TTY that data is ready */ 341210311Sjmallett tty_lock(tp); 342210284Sjmallett if (!tty_gone(tp)) 343215990Sjmallett ttydevsw_outwakeup(tp); 344215990Sjmallett tty_unlock(tp); 345215990Sjmallett 346215990Sjmallett return (0); 347215990Sjmallett} 348215990Sjmallett 349215990Sjmallettstatic size_t 350215990Sjmallettngt_getc_inject(struct tty *tp, void *buf, size_t len) 351215990Sjmallett{ 352215990Sjmallett sc_p sc = ttyhook_softc(tp); 353215990Sjmallett size_t total = 0; 354215990Sjmallett int length; 355215990Sjmallett 356210284Sjmallett while (len) { 357210284Sjmallett struct mbuf *m; 358210284Sjmallett 359210284Sjmallett /* Remove first mbuf from queue */ 360210284Sjmallett IF_DEQUEUE(&sc->outq, m); 361210284Sjmallett if (m == NULL) 362210284Sjmallett break; 363210284Sjmallett 364210284Sjmallett /* Send as much of it as possible */ 365210284Sjmallett while (m != NULL) { 366210284Sjmallett length = min(m->m_len, len); 367210284Sjmallett memcpy((char *)buf + total, mtod(m, char *), length); 368210284Sjmallett 369210284Sjmallett m->m_data += length; 370210284Sjmallett m->m_len -= length; 371210284Sjmallett total += length; 372210284Sjmallett len -= length; 373210284Sjmallett 374210284Sjmallett if (m->m_len > 0) 375210284Sjmallett break; /* device can't take any more */ 376210284Sjmallett m = m_free(m); 377210284Sjmallett } 378210284Sjmallett 379210284Sjmallett /* Put remainder of mbuf chain (if any) back on queue */ 380210284Sjmallett if (m != NULL) { 381210284Sjmallett IF_PREPEND(&sc->outq, m); 382210284Sjmallett break; 383 } 384 } 385 IF_LOCK(&sc->outq); 386 sc->outqlen -= total; 387 IF_UNLOCK(&sc->outq); 388 MPASS(sc->outqlen >= 0); 389 390 return (total); 391} 392 393static size_t 394ngt_getc_poll(struct tty *tp) 395{ 396 sc_p sc = ttyhook_softc(tp); 397 398 return (sc->outqlen); 399} 400 401/* 402 * Optimised TTY input. 403 * 404 * We get a buffer pointer to hopefully a complete data frame. Do not check for 405 * the hotchar, just pass it on. 406 */ 407static size_t 408ngt_rint_bypass(struct tty *tp, const void *buf, size_t len) 409{ 410 sc_p sc = ttyhook_softc(tp); 411 node_p node = sc->node; 412 struct mbuf *m, *mb; 413 size_t total = 0; 414 int error = 0, length; 415 416 tty_lock_assert(tp, MA_OWNED); 417 418 if (sc->hook == NULL) 419 return (0); 420 421 m = m_getm2(NULL, len, M_DONTWAIT, MT_DATA, M_PKTHDR); 422 if (m == NULL) { 423 if (sc->flags & FLG_DEBUG) 424 log(LOG_ERR, 425 "%s: can't get mbuf\n", NG_NODE_NAME(node)); 426 return (0); 427 } 428 m->m_pkthdr.rcvif = NULL; 429 430 for (mb = m; mb != NULL; mb = mb->m_next) { 431 length = min(M_TRAILINGSPACE(mb), len - total); 432 433 memcpy(mtod(m, char *), (const char *)buf + total, length); 434 mb->m_len = length; 435 total += length; 436 m->m_pkthdr.len += length; 437 } 438 if (sc->m != NULL) { 439 /* 440 * Odd, we have changed from non-bypass to bypass. It is 441 * unlikely but not impossible, flush the data first. 442 */ 443 sc->m->m_data = sc->m->m_pktdat; 444 NG_SEND_DATA_ONLY(error, sc->hook, sc->m); 445 sc->m = NULL; 446 } 447 NG_SEND_DATA_ONLY(error, sc->hook, m); 448 449 return (total); 450} 451 452/* 453 * Receive data coming from the device one char at a time, when it is not in 454 * bypass mode. 455 */ 456static int 457ngt_rint(struct tty *tp, char c, int flags) 458{ 459 sc_p sc = ttyhook_softc(tp); 460 node_p node = sc->node; 461 struct mbuf *m; 462 int error = 0; 463 464 tty_lock_assert(tp, MA_OWNED); 465 466 if (sc->hook == NULL) 467 return (0); 468 469 if (flags != 0) { 470 /* framing error or overrun on this char */ 471 if (sc->flags & FLG_DEBUG) 472 log(LOG_DEBUG, "%s: line error %x\n", 473 NG_NODE_NAME(node), flags); 474 return (0); 475 } 476 477 /* Get a new header mbuf if we need one */ 478 if (!(m = sc->m)) { 479 MGETHDR(m, M_DONTWAIT, MT_DATA); 480 if (!m) { 481 if (sc->flags & FLG_DEBUG) 482 log(LOG_ERR, 483 "%s: can't get mbuf\n", NG_NODE_NAME(node)); 484 return (ENOBUFS); 485 } 486 m->m_len = m->m_pkthdr.len = 0; 487 m->m_pkthdr.rcvif = NULL; 488 sc->m = m; 489 } 490 491 /* Add char to mbuf */ 492 *mtod(m, u_char *) = c; 493 m->m_data++; 494 m->m_len++; 495 m->m_pkthdr.len++; 496 497 /* Ship off mbuf if it's time */ 498 if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) { 499 m->m_data = m->m_pktdat; 500 sc->m = NULL; 501 NG_SEND_DATA_ONLY(error, sc->hook, m); /* Will queue */ 502 } 503 504 return (error); 505} 506 507static size_t 508ngt_rint_poll(struct tty *tp) 509{ 510 /* We can always accept input */ 511 return (1); 512} 513 514