Initial import from FreeBSD RELENG_4:
[games.git] / sys / netgraph / tty / ng_tty.c
1
2 /*
3  * ng_tty.c
4  *
5  * Copyright (c) 1996-1999 Whistle Communications, Inc.
6  * All rights reserved.
7  * 
8  * Subject to the following obligations and disclaimer of warranty, use and
9  * redistribution of this software, in source or object code forms, with or
10  * without modifications are expressly permitted by Whistle Communications;
11  * provided, however, that:
12  * 1. Any and all reproductions of the source or object code must include the
13  *    copyright notice above and the following disclaimer of warranties; and
14  * 2. No rights are granted, in any manner or form, to use Whistle
15  *    Communications, Inc. trademarks, including the mark "WHISTLE
16  *    COMMUNICATIONS" on advertising, endorsements, or otherwise except as
17  *    such appears in the above copyright notice or in the software.
18  * 
19  * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
20  * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
21  * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
22  * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
23  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
24  * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
25  * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
26  * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
27  * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
28  * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
29  * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
30  * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
32  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
34  * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
35  * OF SUCH DAMAGE.
36  *
37  * Author: Archie Cobbs <archie@freebsd.org>
38  *
39  * $FreeBSD: src/sys/netgraph/ng_tty.c,v 1.7.2.3 2002/02/13 00:43:12 dillon Exp $
40  * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $
41  */
42
43 /*
44  * This file implements a terminal line discipline that is also a
45  * netgraph node. Installing this line discipline on a terminal device
46  * instantiates a new netgraph node of this type, which allows access
47  * to the device via the "hook" hook of the node.
48  *
49  * Once the line discipline is installed, you can find out the name
50  * of the corresponding netgraph node via a NGIOCGINFO ioctl().
51  *
52  * Incoming characters are delievered to the hook one at a time, each
53  * in its own mbuf. You may optionally define a ``hotchar,'' which causes
54  * incoming characters to be buffered up until either the hotchar is
55  * seen or the mbuf is full (MHLEN bytes). Then all buffered characters
56  * are immediately delivered.
57  *
58  * NOTE: This node operates at spltty().
59  */
60
61 #include <sys/param.h>
62 #include <sys/systm.h>
63 #include <sys/kernel.h>
64 #include <sys/conf.h>
65 #include <sys/proc.h>
66 #include <sys/mbuf.h>
67 #include <sys/malloc.h>
68 #include <sys/fcntl.h>
69 #include <sys/tty.h>
70 #include <sys/ttycom.h>
71 #include <sys/syslog.h>
72 #include <sys/errno.h>
73 #include <sys/ioccom.h>
74
75 #include <netgraph/ng_message.h>
76 #include <netgraph/netgraph.h>
77 #include <netgraph/ng_tty.h>
78
79 #ifdef __i386__                 /* fiddle with the spl locking */
80 #include <machine/ipl.h>
81 #include <i386/isa/intr_machdep.h>
82 #endif
83
84 /* Misc defs */
85 #define MAX_MBUFQ               3       /* Max number of queued mbufs */
86 #define NGT_HIWATER             400     /* High water mark on output */
87
88 /* Per-node private info */
89 struct ngt_sc {
90         struct  tty *tp;                /* Terminal device */
91         node_p  node;                   /* Netgraph node */
92         hook_p  hook;                   /* Netgraph hook */
93         struct  mbuf *m;                /* Incoming data buffer */
94         struct  mbuf *qhead, **qtail;   /* Queue of outgoing mbuf's */
95         short   qlen;                   /* Length of queue */
96         short   hotchar;                /* Hotchar, or -1 if none */
97         u_int   flags;                  /* Flags */
98         struct  callout_handle chand;   /* See man timeout(9) */
99 };
100 typedef struct ngt_sc *sc_p;
101
102 /* Flags */
103 #define FLG_TIMEOUT             0x0001  /* A timeout is pending */
104 #define FLG_DEBUG               0x0002
105
106 /* Debugging */
107 #ifdef INVARIANTS
108 #define QUEUECHECK(sc)                                                  \
109     do {                                                                \
110       struct mbuf       **mp;                                           \
111       int               k;                                              \
112                                                                         \
113       for (k = 0, mp = &sc->qhead;                                      \
114         k <= MAX_MBUFQ && *mp;                                          \
115         k++, mp = &(*mp)->m_nextpkt);                                   \
116       if (k != sc->qlen || k > MAX_MBUFQ || *mp || mp != sc->qtail)     \
117         panic(__FUNCTION__ ": queue");                                  \
118     } while (0)
119 #else
120 #define QUEUECHECK(sc)  do {} while (0)
121 #endif
122
123 /* Line discipline methods */
124 static int      ngt_open(dev_t dev, struct tty *tp);
125 static int      ngt_close(struct tty *tp, int flag);
126 static int      ngt_read(struct tty *tp, struct uio *uio, int flag);
127 static int      ngt_write(struct tty *tp, struct uio *uio, int flag);
128 static int      ngt_tioctl(struct tty *tp,
129                     u_long cmd, caddr_t data, int flag, struct proc *);
130 static int      ngt_input(int c, struct tty *tp);
131 static int      ngt_start(struct tty *tp);
132
133 /* Netgraph methods */
134 static ng_constructor_t ngt_constructor;
135 static ng_rcvmsg_t      ngt_rcvmsg;
136 static ng_shutdown_t    ngt_shutdown;
137 static ng_newhook_t     ngt_newhook;
138 static ng_rcvdata_t     ngt_rcvdata;
139 static ng_disconnect_t  ngt_disconnect;
140 static int      ngt_mod_event(module_t mod, int event, void *data);
141
142 /* Other stuff */
143 static void     ngt_timeout(void *arg);
144
145 #define ERROUT(x)               do { error = (x); goto done; } while (0)
146
147 /* Line discipline descriptor */
148 static struct linesw ngt_disc = {
149         ngt_open,
150         ngt_close,
151         ngt_read,
152         ngt_write,
153         ngt_tioctl,
154         ngt_input,
155         ngt_start,
156         ttymodem,
157         NG_TTY_DFL_HOTCHAR      /* XXX can't change this in serial driver */
158 };
159
160 /* Netgraph node type descriptor */
161 static struct ng_type typestruct = {
162         NG_VERSION,
163         NG_TTY_NODE_TYPE,
164         ngt_mod_event,
165         ngt_constructor,
166         ngt_rcvmsg,
167         ngt_shutdown,
168         ngt_newhook,
169         NULL,
170         NULL,
171         ngt_rcvdata,
172         ngt_rcvdata,
173         ngt_disconnect,
174         NULL
175 };
176 NETGRAPH_INIT(tty, &typestruct);
177
178 static int ngt_unit;
179 static int ngt_nodeop_ok;       /* OK to create/remove node */
180 static int ngt_ldisc;
181
182 /******************************************************************
183                     LINE DISCIPLINE METHODS
184 ******************************************************************/
185
186 /*
187  * Set our line discipline on the tty.
188  * Called from device open routine or ttioctl() at >= splsofttty()
189  */
190 static int
191 ngt_open(dev_t dev, struct tty *tp)
192 {
193         struct proc *const p = curproc; /* XXX */
194         char name[sizeof(NG_TTY_NODE_TYPE) + 8];
195         sc_p sc;
196         int s, error;
197
198         /* Super-user only */
199         if ((error = suser(p)))
200                 return (error);
201         s = splnet();
202         (void) spltty();        /* XXX is this necessary? */
203
204         /* Already installed? */
205         if (tp->t_line == NETGRAPHDISC) {
206                 sc = (sc_p) tp->t_sc;
207                 if (sc != NULL && sc->tp == tp)
208                         goto done;
209         }
210
211         /* Initialize private struct */
212         MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK);
213         if (sc == NULL) {
214                 error = ENOMEM;
215                 goto done;
216         }
217         bzero(sc, sizeof(*sc));
218         sc->tp = tp;
219         sc->hotchar = NG_TTY_DFL_HOTCHAR;
220         sc->qtail = &sc->qhead;
221         QUEUECHECK(sc);
222         callout_handle_init(&sc->chand);
223
224         /* Setup netgraph node */
225         ngt_nodeop_ok = 1;
226         error = ng_make_node_common(&typestruct, &sc->node);
227         ngt_nodeop_ok = 0;
228         if (error) {
229                 FREE(sc, M_NETGRAPH);
230                 goto done;
231         }
232         snprintf(name, sizeof(name), "%s%d", typestruct.name, ngt_unit++);
233
234         /* Set back pointers */
235         sc->node->private = sc;
236         tp->t_sc = (caddr_t) sc;
237
238         /* Assign node its name */
239         if ((error = ng_name_node(sc->node, name))) {
240                 log(LOG_ERR, "%s: node name exists?\n", name);
241                 ngt_nodeop_ok = 1;
242                 ng_rmnode(sc->node);
243                 ngt_nodeop_ok = 0;
244                 goto done;
245         }
246
247         /*
248          * Pre-allocate cblocks to the an appropriate amount.
249          * I'm not sure what is appropriate.
250          */
251         ttyflush(tp, FREAD | FWRITE);
252         clist_alloc_cblocks(&tp->t_canq, 0, 0);
253         clist_alloc_cblocks(&tp->t_rawq, 0, 0);
254         clist_alloc_cblocks(&tp->t_outq,
255             MLEN + NGT_HIWATER, MLEN + NGT_HIWATER);
256
257 done:
258         /* Done */
259         splx(s);
260         return (error);
261 }
262
263 /*
264  * Line specific close routine, called from device close routine
265  * and from ttioctl at >= splsofttty(). This causes the node to
266  * be destroyed as well.
267  */
268 static int
269 ngt_close(struct tty *tp, int flag)
270 {
271         const sc_p sc = (sc_p) tp->t_sc;
272         int s;
273
274         s = spltty();
275         ttyflush(tp, FREAD | FWRITE);
276         clist_free_cblocks(&tp->t_outq);
277         tp->t_line = 0;
278         if (sc != NULL) {
279                 if (sc->flags & FLG_TIMEOUT) {
280                         untimeout(ngt_timeout, sc, sc->chand);
281                         sc->flags &= ~FLG_TIMEOUT;
282                 }
283                 ngt_nodeop_ok = 1;
284                 ng_rmnode(sc->node);
285                 ngt_nodeop_ok = 0;
286                 tp->t_sc = NULL;
287         }
288         splx(s);
289         return (0);
290 }
291
292 /*
293  * Once the device has been turned into a node, we don't allow reading.
294  */
295 static int
296 ngt_read(struct tty *tp, struct uio *uio, int flag)
297 {
298         return (EIO);
299 }
300
301 /*
302  * Once the device has been turned into a node, we don't allow writing.
303  */
304 static int
305 ngt_write(struct tty *tp, struct uio *uio, int flag)
306 {
307         return (EIO);
308 }
309
310 /*
311  * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
312  */
313 static int
314 ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct proc *p)
315 {
316         const sc_p sc = (sc_p) tp->t_sc;
317         int s, error = 0;
318
319         s = spltty();
320         switch (cmd) {
321         case NGIOCGINFO:
322             {
323                 struct nodeinfo *const ni = (struct nodeinfo *) data;
324                 const node_p node = sc->node;
325
326                 bzero(ni, sizeof(*ni));
327                 if (node->name)
328                         strncpy(ni->name, node->name, sizeof(ni->name) - 1);
329                 strncpy(ni->type, node->type->name, sizeof(ni->type) - 1);
330                 ni->id = (u_int32_t) node;
331                 ni->hooks = node->numhooks;
332                 break;
333             }
334         default:
335                 ERROUT(ENOIOCTL);
336         }
337 done:
338         splx(s);
339         return (error);
340 }
341
342 /*
343  * Receive data coming from the device. We get one character at
344  * a time, which is kindof silly.
345  * Only guaranteed to be at splsofttty() or spltty().
346  */
347 static int
348 ngt_input(int c, struct tty *tp)
349 {
350         const sc_p sc = (sc_p) tp->t_sc;
351         const node_p node = sc->node;
352         struct mbuf *m;
353         int s, error = 0;
354
355         if (!sc || tp != sc->tp)
356                 return (0);
357         s = spltty();
358         if (!sc->hook)
359                 ERROUT(0);
360
361         /* Check for error conditions */
362         if ((tp->t_state & TS_CONNECTED) == 0) {
363                 if (sc->flags & FLG_DEBUG)
364                         log(LOG_DEBUG, "%s: no carrier\n", node->name);
365                 ERROUT(0);
366         }
367         if (c & TTY_ERRORMASK) {
368                 /* framing error or overrun on this char */
369                 if (sc->flags & FLG_DEBUG)
370                         log(LOG_DEBUG, "%s: line error %x\n",
371                             node->name, c & TTY_ERRORMASK);
372                 ERROUT(0);
373         }
374         c &= TTY_CHARMASK;
375
376         /* Get a new header mbuf if we need one */
377         if (!(m = sc->m)) {
378                 MGETHDR(m, M_DONTWAIT, MT_DATA);
379                 if (!m) {
380                         if (sc->flags & FLG_DEBUG)
381                                 log(LOG_ERR,
382                                     "%s: can't get mbuf\n", node->name);
383                         ERROUT(ENOBUFS);
384                 }
385                 m->m_len = m->m_pkthdr.len = 0;
386                 m->m_pkthdr.rcvif = NULL;
387                 sc->m = m;
388         }
389
390         /* Add char to mbuf */
391         *mtod(m, u_char *) = c;
392         m->m_data++;
393         m->m_len++;
394         m->m_pkthdr.len++;
395
396         /* Ship off mbuf if it's time */
397         if (sc->hotchar == -1 || c == sc->hotchar || m->m_len >= MHLEN) {
398                 m->m_data = m->m_pktdat;
399                 error = ng_queue_data(sc->hook, m, NULL);
400                 sc->m = NULL;
401         }
402 done:
403         splx(s);
404         return (error);
405 }
406
407 /*
408  * This is called when the device driver is ready for more output.
409  * Called from tty system at splsofttty() or spltty().
410  * Also call from ngt_rcv_data() when a new mbuf is available for output.
411  */
412 static int
413 ngt_start(struct tty *tp)
414 {
415         const sc_p sc = (sc_p) tp->t_sc;
416         int s;
417
418         s = spltty();
419         while (tp->t_outq.c_cc < NGT_HIWATER) { /* XXX 2.2 specific ? */
420                 struct mbuf *m = sc->qhead;
421
422                 /* Remove first mbuf from queue */
423                 if (!m)
424                         break;
425                 if ((sc->qhead = m->m_nextpkt) == NULL)
426                         sc->qtail = &sc->qhead;
427                 sc->qlen--;
428                 QUEUECHECK(sc);
429
430                 /* Send as much of it as possible */
431                 while (m) {
432                         int     sent;
433
434                         sent = m->m_len
435                             - b_to_q(mtod(m, u_char *), m->m_len, &tp->t_outq);
436                         m->m_data += sent;
437                         m->m_len -= sent;
438                         if (m->m_len > 0)
439                                 break;  /* device can't take no more */
440                         m = m_free(m);
441                 }
442
443                 /* Put remainder of mbuf chain (if any) back on queue */
444                 if (m) {
445                         m->m_nextpkt = sc->qhead;
446                         sc->qhead = m;
447                         if (sc->qtail == &sc->qhead)
448                                 sc->qtail = &m->m_nextpkt;
449                         sc->qlen++;
450                         QUEUECHECK(sc);
451                         break;
452                 }
453         }
454
455         /* Call output process whether or not there is any output. We are
456          * being called in lieu of ttstart and must do what it would. */
457         if (tp->t_oproc != NULL)
458                 (*tp->t_oproc) (tp);
459
460         /* This timeout is needed for operation on a pseudo-tty, because the
461          * pty code doesn't call pppstart after it has drained the t_outq. */
462         if (sc->qhead && (sc->flags & FLG_TIMEOUT) == 0) {
463                 sc->chand = timeout(ngt_timeout, sc, 1);
464                 sc->flags |= FLG_TIMEOUT;
465         }
466         splx(s);
467         return (0);
468 }
469
470 /*
471  * We still have data to output to the device, so try sending more.
472  */
473 static void
474 ngt_timeout(void *arg)
475 {
476         const sc_p sc = (sc_p) arg;
477         int s;
478
479         s = spltty();
480         sc->flags &= ~FLG_TIMEOUT;
481         ngt_start(sc->tp);
482         splx(s);
483 }
484
485 /******************************************************************
486                     NETGRAPH NODE METHODS
487 ******************************************************************/
488
489 /*
490  * Initialize a new node of this type.
491  *
492  * We only allow nodes to be created as a result of setting
493  * the line discipline on a tty, so always return an error if not.
494  */
495 static int
496 ngt_constructor(node_p *nodep)
497 {
498         if (!ngt_nodeop_ok)
499                 return (EOPNOTSUPP);
500         return (ng_make_node_common(&typestruct, nodep));
501 }
502
503 /*
504  * Add a new hook. There can only be one.
505  */
506 static int
507 ngt_newhook(node_p node, hook_p hook, const char *name)
508 {
509         const sc_p sc = node->private;
510         int s, error = 0;
511
512         if (strcmp(name, NG_TTY_HOOK))
513                 return (EINVAL);
514         s = spltty();
515         if (sc->hook)
516                 ERROUT(EISCONN);
517         sc->hook = hook;
518 done:
519         splx(s);
520         return (error);
521 }
522
523 /*
524  * Disconnect the hook
525  */
526 static int
527 ngt_disconnect(hook_p hook)
528 {
529         const sc_p sc = hook->node->private;
530         int s;
531
532         s = spltty();
533         if (hook != sc->hook)
534                 panic(__FUNCTION__);
535         sc->hook = NULL;
536         m_freem(sc->m);
537         sc->m = NULL;
538         splx(s);
539         return (0);
540 }
541
542 /*
543  * Remove this node. The does the netgraph portion of the shutdown.
544  * This should only be called indirectly from ngt_close().
545  */
546 static int
547 ngt_shutdown(node_p node)
548 {
549         const sc_p sc = node->private;
550
551         if (!ngt_nodeop_ok)
552                 return (EOPNOTSUPP);
553         ng_unname(node);
554         ng_cutlinks(node);
555         node->private = NULL;
556         ng_unref(sc->node);
557         m_freem(sc->qhead);
558         m_freem(sc->m);
559         bzero(sc, sizeof(*sc));
560         FREE(sc, M_NETGRAPH);
561         return (0);
562 }
563
564 /*
565  * Receive incoming data from netgraph system. Put it on our
566  * output queue and start output if necessary.
567  */
568 static int
569 ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
570 {
571         const sc_p sc = hook->node->private;
572         int s, error = 0;
573
574         if (hook != sc->hook)
575                 panic(__FUNCTION__);
576         NG_FREE_META(meta);
577         s = spltty();
578         if (sc->qlen >= MAX_MBUFQ)
579                 ERROUT(ENOBUFS);
580         m->m_nextpkt = NULL;
581         *sc->qtail = m;
582         sc->qtail = &m->m_nextpkt;
583         sc->qlen++;
584         QUEUECHECK(sc);
585         m = NULL;
586         if (sc->qlen == 1)
587                 ngt_start(sc->tp);
588 done:
589         splx(s);
590         if (m)
591                 m_freem(m);
592         return (error);
593 }
594
595 /*
596  * Receive control message
597  */
598 static int
599 ngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
600            struct ng_mesg **rptr)
601 {
602         const sc_p sc = (sc_p) node->private;
603         struct ng_mesg *resp = NULL;
604         int error = 0;
605
606         switch (msg->header.typecookie) {
607         case NGM_TTY_COOKIE:
608                 switch (msg->header.cmd) {
609                 case NGM_TTY_SET_HOTCHAR:
610                     {
611                         int     hotchar;
612
613                         if (msg->header.arglen != sizeof(int))
614                                 ERROUT(EINVAL);
615                         hotchar = *((int *) msg->data);
616                         if (hotchar != (u_char) hotchar && hotchar != -1)
617                                 ERROUT(EINVAL);
618                         sc->hotchar = hotchar;  /* race condition is OK */
619                         break;
620                     }
621                 case NGM_TTY_GET_HOTCHAR:
622                         NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT);
623                         if (!resp)
624                                 ERROUT(ENOMEM);
625                         /* Race condition here is OK */
626                         *((int *) resp->data) = sc->hotchar;
627                         break;
628                 default:
629                         ERROUT(EINVAL);
630                 }
631                 break;
632         default:
633                 ERROUT(EINVAL);
634         }
635         if (rptr)
636                 *rptr = resp;
637         else if (resp)
638                 FREE(resp, M_NETGRAPH);
639
640 done:
641         FREE(msg, M_NETGRAPH);
642         return (error);
643 }
644
645 /******************************************************************
646                         INITIALIZATION
647 ******************************************************************/
648
649 /*
650  * Handle loading and unloading for this node type
651  */
652 static int
653 ngt_mod_event(module_t mod, int event, void *data)
654 {
655         /* struct ng_type *const type = data;*/
656         int s, error = 0;
657
658         switch (event) {
659         case MOD_LOAD:
660 #ifdef __i386__
661                 /* Insure the soft net "engine" can't run during spltty code */
662                 s = splhigh();
663                 tty_imask |= softnet_imask; /* spltty() block spl[soft]net() */
664                 net_imask |= softtty_imask; /* splimp() block splsofttty() */
665                 net_imask |= tty_imask;     /* splimp() block spltty() */
666                 update_intr_masks();
667                 splx(s);
668
669                 if (bootverbose)
670                         log(LOG_DEBUG, "new masks: bio %x, tty %x, net %x\n",
671                             bio_imask, tty_imask, net_imask);
672 #endif
673
674                 /* Register line discipline */
675                 s = spltty();
676                 if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) {
677                         splx(s);
678                         log(LOG_ERR, "%s: can't register line discipline",
679                             __FUNCTION__);
680                         return (EIO);
681                 }
682                 splx(s);
683                 break;
684
685         case MOD_UNLOAD:
686
687                 /* Unregister line discipline */
688                 s = spltty();
689                 ldisc_deregister(ngt_ldisc);
690                 splx(s);
691                 break;
692
693         default:
694                 error = EOPNOTSUPP;
695                 break;
696         }
697         return (error);
698 }
699