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