ng_tty.c revision 186056
1/* 2 * ng_tty.c 3 */ 4 5/*- 6 * Copyright (c) 1996-1999 Whistle Communications, Inc. 7 * All rights reserved. 8 * 9 * Subject to the following obligations and disclaimer of warranty, use and 10 * redistribution of this software, in source or object code forms, with or 11 * without modifications are expressly permitted by Whistle Communications; 12 * provided, however, that: 13 * 1. Any and all reproductions of the source or object code must include the 14 * copyright notice above and the following disclaimer of warranties; and 15 * 2. No rights are granted, in any manner or form, to use Whistle 16 * Communications, Inc. trademarks, including the mark "WHISTLE 17 * COMMUNICATIONS" on advertising, endorsements, or otherwise except as 18 * such appears in the above copyright notice or in the software. 19 * 20 * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND 21 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO 22 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE, 23 * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF 24 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT. 25 * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY 26 * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS 27 * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE. 28 * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES 29 * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING 30 * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 31 * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR 32 * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY 36 * OF SUCH DAMAGE. 37 * 38 * Author: Archie Cobbs <archie@freebsd.org> 39 * 40 * Updated by Andrew Thompson <thompsa@FreeBSD.org> for MPSAFE TTY. 41 * 42 * $FreeBSD: head/sys/netgraph/ng_tty.c 186056 2008-12-13 21:17:46Z mav $ 43 * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $ 44 */ 45 46/* 47 * This file implements TTY hooks to link in to the netgraph system. The node 48 * is created and then passed the callers opened TTY file descriptor number to 49 * NGM_TTY_SET_TTY, this will hook the tty via ttyhook_register(). 50 * 51 * Incoming data is delivered directly to ng_tty via the TTY bypass hook as a 52 * buffer pointer and length, this is converted to a mbuf and passed to the 53 * peer. 54 * 55 * If the TTY device does not support bypass then incoming characters are 56 * delivered to the hook one at a time, each in its own mbuf. You may 57 * optionally define a ``hotchar,'' which causes incoming characters to be 58 * buffered up until either the hotchar is seen or the mbuf is full (MHLEN 59 * bytes). Then all buffered characters are immediately delivered. 60 */ 61 62#include <sys/param.h> 63#include <sys/systm.h> 64#include <sys/conf.h> 65#include <sys/errno.h> 66#include <sys/fcntl.h> 67#include <sys/ioccom.h> 68#include <sys/kernel.h> 69#include <sys/malloc.h> 70#include <sys/mbuf.h> 71#include <sys/priv.h> 72#include <sys/socket.h> 73#include <sys/syslog.h> 74#include <sys/tty.h> 75#include <sys/ttycom.h> 76#include <sys/proc.h> 77 78#include <net/if.h> 79#include <net/if_var.h> 80 81#include <netgraph/ng_message.h> 82#include <netgraph/netgraph.h> 83#include <netgraph/ng_tty.h> 84 85/* Per-node private info */ 86struct ngt_softc { 87 struct tty *tp; /* Terminal device */ 88 node_p node; /* Netgraph node */ 89 hook_p hook; /* Netgraph hook */ 90 struct ifqueue outq; /* Queue of outgoing data */ 91 size_t outqlen; /* Number of bytes in outq */ 92 struct mbuf *m; /* Incoming non-bypass data buffer */ 93 short hotchar; /* Hotchar, or -1 if none */ 94 u_int flags; /* Flags */ 95}; 96typedef struct ngt_softc *sc_p; 97 98/* Flags */ 99#define FLG_DEBUG 0x0002 100 101/* Netgraph methods */ 102static ng_constructor_t ngt_constructor; 103static ng_rcvmsg_t ngt_rcvmsg; 104static ng_shutdown_t ngt_shutdown; 105static ng_newhook_t ngt_newhook; 106static ng_connect_t ngt_connect; 107static ng_rcvdata_t ngt_rcvdata; 108static ng_disconnect_t ngt_disconnect; 109 110#define ERROUT(x) do { error = (x); goto done; } while (0) 111 112static th_getc_inject_t ngt_getc_inject; 113static th_getc_poll_t ngt_getc_poll; 114static th_rint_t ngt_rint; 115static th_rint_bypass_t ngt_rint_bypass; 116static th_rint_poll_t ngt_rint_poll; 117static th_close_t ngt_close; 118 119static struct ttyhook ngt_hook = { 120 .th_getc_inject = ngt_getc_inject, 121 .th_getc_poll = ngt_getc_poll, 122 .th_rint = ngt_rint, 123 .th_rint_bypass = ngt_rint_bypass, 124 .th_rint_poll = ngt_rint_poll, 125 .th_close = ngt_close, 126}; 127 128/* Netgraph node type descriptor */ 129static struct ng_type typestruct = { 130 .version = NG_ABI_VERSION, 131 .name = NG_TTY_NODE_TYPE, 132 .constructor = ngt_constructor, 133 .rcvmsg = ngt_rcvmsg, 134 .shutdown = ngt_shutdown, 135 .newhook = ngt_newhook, 136 .connect = ngt_connect, 137 .rcvdata = ngt_rcvdata, 138 .disconnect = ngt_disconnect, 139}; 140NETGRAPH_INIT(tty, &typestruct); 141 142#define NGTLOCK(sc) IF_LOCK(&sc->outq) 143#define NGTUNLOCK(sc) IF_UNLOCK(&sc->outq) 144 145/****************************************************************** 146 NETGRAPH NODE METHODS 147******************************************************************/ 148 149/* 150 * Initialize a new node of this type. 151 * 152 * We only allow nodes to be created as a result of setting 153 * the line discipline on a tty, so always return an error if not. 154 */ 155static int 156ngt_constructor(node_p node) 157{ 158 sc_p sc; 159 160 /* Allocate private structure */ 161 sc = malloc(sizeof(*sc), M_NETGRAPH, M_NOWAIT | M_ZERO); 162 if (sc == NULL) 163 return (ENOMEM); 164 165 NG_NODE_SET_PRIVATE(node, sc); 166 sc->node = node; 167 168 mtx_init(&sc->outq.ifq_mtx, "ng_tty node+queue", NULL, MTX_DEF); 169 IFQ_SET_MAXLEN(&sc->outq, IFQ_MAXLEN); 170 171 return (0); 172} 173 174/* 175 * Add a new hook. There can only be one. 176 */ 177static int 178ngt_newhook(node_p node, hook_p hook, const char *name) 179{ 180 const sc_p sc = NG_NODE_PRIVATE(node); 181 182 if (strcmp(name, NG_TTY_HOOK)) 183 return (EINVAL); 184 185 if (sc->hook) 186 return (EISCONN); 187 188 NGTLOCK(sc); 189 sc->hook = hook; 190 NGTUNLOCK(sc); 191 192 return (0); 193} 194 195/* 196 * Set the hook into queueing mode (for outgoing packets), 197 * so that we wont deliver mbuf thru the whole graph holding 198 * tty locks. 199 */ 200static int 201ngt_connect(hook_p hook) 202{ 203 NG_HOOK_FORCE_QUEUE(hook); 204 return (0); 205} 206 207/* 208 * Disconnect the hook 209 */ 210static int 211ngt_disconnect(hook_p hook) 212{ 213 const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 214 215 if (hook != sc->hook) 216 panic(__func__); 217 218 NGTLOCK(sc); 219 sc->hook = NULL; 220 NGTUNLOCK(sc); 221 222 return (0); 223} 224 225/* 226 * Remove this node. The does the netgraph portion of the shutdown. 227 */ 228static int 229ngt_shutdown(node_p node) 230{ 231 const sc_p sc = NG_NODE_PRIVATE(node); 232 struct tty *tp; 233 234 tp = sc->tp; 235 if (tp != NULL) { 236 tty_lock(tp); 237 ttyhook_unregister(tp); 238 } 239 /* Free resources */ 240 IF_DRAIN(&sc->outq); 241 mtx_destroy(&(sc)->outq.ifq_mtx); 242 NG_NODE_UNREF(sc->node); 243 free(sc, M_NETGRAPH); 244 245 return (0); 246} 247 248/* 249 * Receive control message 250 */ 251static int 252ngt_rcvmsg(node_p node, item_p item, hook_p lasthook) 253{ 254 struct proc *p; 255 const sc_p sc = NG_NODE_PRIVATE(node); 256 struct ng_mesg *msg, *resp = NULL; 257 int error = 0; 258 259 NGI_GET_MSG(item, msg); 260 switch (msg->header.typecookie) { 261 case NGM_TTY_COOKIE: 262 switch (msg->header.cmd) { 263 case NGM_TTY_SET_TTY: 264 if (sc->tp != NULL) 265 return (EBUSY); 266 267 p = pfind(((int *)msg->data)[0]); 268 if (p == NULL || (p->p_flag & P_WEXIT)) 269 return (ESRCH); 270 _PHOLD(p); 271 PROC_UNLOCK(p); 272 error = ttyhook_register(&sc->tp, p, ((int *)msg->data)[1], 273 &ngt_hook, sc); 274 PRELE(p); 275 if (error != 0) 276 return (error); 277 break; 278 case NGM_TTY_SET_HOTCHAR: 279 { 280 int hotchar; 281 282 if (msg->header.arglen != sizeof(int)) 283 ERROUT(EINVAL); 284 hotchar = *((int *) msg->data); 285 if (hotchar != (u_char) hotchar && hotchar != -1) 286 ERROUT(EINVAL); 287 sc->hotchar = hotchar; /* race condition is OK */ 288 break; 289 } 290 case NGM_TTY_GET_HOTCHAR: 291 NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT); 292 if (!resp) 293 ERROUT(ENOMEM); 294 /* Race condition here is OK */ 295 *((int *) resp->data) = sc->hotchar; 296 break; 297 default: 298 ERROUT(EINVAL); 299 } 300 break; 301 default: 302 ERROUT(EINVAL); 303 } 304done: 305 NG_RESPOND_MSG(error, node, item, resp); 306 NG_FREE_MSG(msg); 307 return (error); 308} 309 310/* 311 * Receive incoming data from netgraph system. Put it on our 312 * output queue and start output if necessary. 313 */ 314static int 315ngt_rcvdata(hook_p hook, item_p item) 316{ 317 const sc_p sc = NG_NODE_PRIVATE(NG_HOOK_NODE(hook)); 318 struct tty *tp = sc->tp; 319 struct mbuf *m; 320 321 if (hook != sc->hook) 322 panic(__func__); 323 324 NGI_GET_M(item, m); 325 NG_FREE_ITEM(item); 326 327 if (tp == NULL) { 328 NG_FREE_M(m); 329 return (ENXIO); 330 } 331 332 IF_LOCK(&sc->outq); 333 if (_IF_QFULL(&sc->outq)) { 334 _IF_DROP(&sc->outq); 335 IF_UNLOCK(&sc->outq); 336 NG_FREE_M(m); 337 return (ENOBUFS); 338 } 339 340 _IF_ENQUEUE(&sc->outq, m); 341 sc->outqlen += m->m_pkthdr.len; 342 IF_UNLOCK(&sc->outq); 343 344 /* notify the TTY that data is ready */ 345 tty_lock(tp); 346 if (!tty_gone(tp)) 347 ttydevsw_outwakeup(tp); 348 tty_unlock(tp); 349 350 return (0); 351} 352 353static size_t 354ngt_getc_inject(struct tty *tp, void *buf, size_t len) 355{ 356 sc_p sc = ttyhook_softc(tp); 357 size_t total = 0; 358 int length; 359 360 while (len) { 361 struct mbuf *m; 362 363 /* Remove first mbuf from queue */ 364 IF_DEQUEUE(&sc->outq, m); 365 if (m == NULL) 366 break; 367 368 /* Send as much of it as possible */ 369 while (m != NULL) { 370 length = min(m->m_len, len); 371 memcpy((char *)buf + total, mtod(m, char *), length); 372 373 m->m_data += length; 374 m->m_len -= length; 375 total += length; 376 len -= length; 377 378 if (m->m_len > 0) 379 break; /* device can't take any more */ 380 m = m_free(m); 381 } 382 383 /* Put remainder of mbuf chain (if any) back on queue */ 384 if (m != NULL) { 385 IF_PREPEND(&sc->outq, m); 386 break; 387 } 388 } 389 IF_LOCK(&sc->outq); 390 sc->outqlen -= total; 391 IF_UNLOCK(&sc->outq); 392 MPASS(sc->outqlen >= 0); 393 394 return (total); 395} 396 397static size_t 398ngt_getc_poll(struct tty *tp) 399{ 400 sc_p sc = ttyhook_softc(tp); 401 402 return (sc->outqlen); 403} 404 405/* 406 * Optimised TTY input. 407 * 408 * We get a buffer pointer to hopefully a complete data frame. Do not check for 409 * the hotchar, just pass it on. 410 */ 411static size_t 412ngt_rint_bypass(struct tty *tp, const void *buf, size_t len) 413{ 414 sc_p sc = ttyhook_softc(tp); 415 node_p node = sc->node; 416 struct mbuf *m, *mb; 417 size_t total = 0; 418 int error = 0, length; 419 420 tty_lock_assert(tp, MA_OWNED); 421 422 if (sc->hook == NULL) 423 return (0); 424 425 m = m_getm2(NULL, len, M_DONTWAIT, MT_DATA, M_PKTHDR); 426 if (m == NULL) { 427 if (sc->flags & FLG_DEBUG) 428 log(LOG_ERR, 429 "%s: can't get mbuf\n", NG_NODE_NAME(node)); 430 return (0); 431 } 432 m->m_pkthdr.rcvif = NULL; 433 434 for (mb = m; mb != NULL; mb = mb->m_next) { 435 length = min(M_TRAILINGSPACE(mb), len - total); 436 437 memcpy(mtod(m, char *), (const char *)buf + total, length); 438 mb->m_len = length; 439 total += length; 440 m->m_pkthdr.len += length; 441 } 442 if (sc->m != NULL) { 443 /* 444 * Odd, we have changed from non-bypass to bypass. It is 445 * unlikely but not impossible, flush the data first. 446 */ 447 sc->m->m_data = sc->m->m_pktdat; 448 NG_SEND_DATA_ONLY(error, sc->hook, sc->m); 449 sc->m = NULL; 450 } 451 NG_SEND_DATA_ONLY(error, sc->hook, m); 452 453 return (total); 454} 455 456/* 457 * Receive data coming from the device one char at a time, when it is not in 458 * bypass mode. 459 */ 460static int 461ngt_rint(struct tty *tp, char c, int flags) 462{ 463 sc_p sc = ttyhook_softc(tp); 464 node_p node = sc->node; 465 struct mbuf *m; 466 int error = 0; 467 468 tty_lock_assert(tp, MA_OWNED); 469 470 if (sc->hook == NULL) 471 return (0); 472 473 if (flags != 0) { 474 /* framing error or overrun on this char */ 475 if (sc->flags & FLG_DEBUG) 476 log(LOG_DEBUG, "%s: line error %x\n", 477 NG_NODE_NAME(node), flags); 478 return (0); 479 } 480 481 /* Get a new header mbuf if we need one */ 482 if (!(m = sc->m)) { 483 MGETHDR(m, M_DONTWAIT, MT_DATA); 484 if (!m) { 485 if (sc->flags & FLG_DEBUG) 486 log(LOG_ERR, 487 "%s: can't get mbuf\n", NG_NODE_NAME(node)); 488 return (ENOBUFS); 489 } 490 m->m_len = m->m_pkthdr.len = 0; 491 m->m_pkthdr.rcvif = NULL; 492 sc->m = m; 493 } 494 495 /* Add char to mbuf */ 496 *mtod(m, u_char *) = c; 497 m->m_data++; 498 m->m_len++; 499 m->m_pkthdr.len++; 500 501 /* Ship off mbuf if it's time */ 502 if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) { 503 m->m_data = m->m_pktdat; 504 sc->m = NULL; 505 NG_SEND_DATA_ONLY(error, sc->hook, m); /* Will queue */ 506 } 507 508 return (error); 509} 510 511static size_t 512ngt_rint_poll(struct tty *tp) 513{ 514 /* We can always accept input */ 515 return (1); 516} 517 518static void 519ngt_close(struct tty *tp) 520{ 521 sc_p sc = ttyhook_softc(tp); 522 523 /* Must be queued to drop the tty lock */ 524 ng_rmnode_flags(sc->node, NG_QUEUE); 525} 526 527