Merge branch 'vendor/AWK'
[dragonfly.git] / sys / netgraph / tty / ng_tty.c
1 /*
2  * (MPSAFE)
3  *
4  * ng_tty.c
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  * $FreeBSD: src/sys/netgraph/ng_tty.c,v 1.7.2.3 2002/02/13 00:43:12 dillon Exp $
41  * $DragonFly: src/sys/netgraph/tty/ng_tty.c,v 1.19 2008/01/06 16:55:52 swildner Exp $
42  * $Whistle: ng_tty.c,v 1.21 1999/11/01 09:24:52 julian Exp $
43  */
44
45 /*
46  * This file implements a terminal line discipline that is also a
47  * netgraph node. Installing this line discipline on a terminal device
48  * instantiates a new netgraph node of this type, which allows access
49  * to the device via the "hook" hook of the node.
50  *
51  * Once the line discipline is installed, you can find out the name
52  * of the corresponding netgraph node via a NGIOCGINFO ioctl().
53  *
54  * Incoming characters are delievered to the hook one at a time, each
55  * in its own mbuf. You may optionally define a ``hotchar,'' which causes
56  * incoming characters to be buffered up until either the hotchar is
57  * seen or the mbuf is full (MHLEN bytes). Then all buffered characters
58  * are immediately delivered.
59  *
60  * NOTE: This node operates at spltty().
61  */
62
63 #include <sys/param.h>
64 #include <sys/systm.h>
65 #include <sys/kernel.h>
66 #include <sys/conf.h>
67 #include <sys/proc.h>
68 #include <sys/priv.h>
69 #include <sys/mbuf.h>
70 #include <sys/malloc.h>
71 #include <sys/fcntl.h>
72 #include <sys/tty.h>
73 #include <sys/ttycom.h>
74 #include <sys/syslog.h>
75 #include <sys/errno.h>
76 #include <sys/thread2.h>
77
78 #include <netgraph/ng_message.h>
79 #include <netgraph/netgraph.h>
80 #include "ng_tty.h"
81
82 /* Misc defs */
83 #define MAX_MBUFQ               3       /* Max number of queued mbufs */
84 #define NGT_HIWATER             400     /* High water mark on output */
85
86 /* Per-node private info */
87 struct ngt_sc {
88         struct  tty *tp;                /* Terminal device */
89         node_p  node;                   /* Netgraph node */
90         hook_p  hook;                   /* Netgraph hook */
91         struct  mbuf *m;                /* Incoming data buffer */
92         struct  mbuf *qhead, **qtail;   /* Queue of outgoing mbuf's */
93         short   qlen;                   /* Length of queue */
94         short   hotchar;                /* Hotchar, or -1 if none */
95         u_int   flags;                  /* Flags */
96         struct  callout ctimeout;       /* See man timeout(9) */
97 };
98 typedef struct ngt_sc *sc_p;
99
100 /* Flags */
101 #define FLG_TIMEOUT             0x0001  /* A timeout is pending */
102 #define FLG_DEBUG               0x0002
103
104 /* Debugging */
105 #ifdef INVARIANTS
106 #define QUEUECHECK(sc)                                                  \
107     do {                                                                \
108       struct mbuf       **mp;                                           \
109       int               k;                                              \
110                                                                         \
111       for (k = 0, mp = &sc->qhead;                                      \
112         k <= MAX_MBUFQ && *mp;                                          \
113         k++, mp = &(*mp)->m_nextpkt);                                   \
114       if (k != sc->qlen || k > MAX_MBUFQ || *mp || mp != sc->qtail)     \
115         panic("%s: queue", __func__);                           \
116     } while (0)
117 #else
118 #define QUEUECHECK(sc)  do {} while (0)
119 #endif
120
121 /* Line discipline methods */
122 static int      ngt_open(cdev_t dev, struct tty *tp);
123 static int      ngt_close(struct tty *tp, int flag);
124 static int      ngt_read(struct tty *tp, struct uio *uio, int flag);
125 static int      ngt_write(struct tty *tp, struct uio *uio, int flag);
126 static int      ngt_tioctl(struct tty *tp,
127                     u_long cmd, caddr_t data, int flag, struct ucred *cred);
128 static int      ngt_input(int c, struct tty *tp);
129 static int      ngt_start(struct tty *tp);
130
131 /* Netgraph methods */
132 static ng_constructor_t ngt_constructor;
133 static ng_rcvmsg_t      ngt_rcvmsg;
134 static ng_shutdown_t    ngt_shutdown;
135 static ng_newhook_t     ngt_newhook;
136 static ng_rcvdata_t     ngt_rcvdata;
137 static ng_disconnect_t  ngt_disconnect;
138 static int      ngt_mod_event(module_t mod, int event, void *data);
139
140 /* Other stuff */
141 static void     ngt_timeout(void *arg);
142
143 #define ERROUT(x)               do { error = (x); goto done; } while (0)
144
145 /* Line discipline descriptor */
146 static struct linesw ngt_disc = {
147         ngt_open,
148         ngt_close,
149         ngt_read,
150         ngt_write,
151         ngt_tioctl,
152         ngt_input,
153         ngt_start,
154         ttymodem,
155         NG_TTY_DFL_HOTCHAR      /* XXX can't change this in serial driver */
156 };
157
158 /* Netgraph node type descriptor */
159 static struct ng_type typestruct = {
160         NG_VERSION,
161         NG_TTY_NODE_TYPE,
162         ngt_mod_event,
163         ngt_constructor,
164         ngt_rcvmsg,
165         ngt_shutdown,
166         ngt_newhook,
167         NULL,
168         NULL,
169         ngt_rcvdata,
170         ngt_rcvdata,
171         ngt_disconnect,
172         NULL
173 };
174 NETGRAPH_INIT(tty, &typestruct);
175
176 static int ngt_unit;
177 static int ngt_nodeop_ok;       /* OK to create/remove node */
178 static int ngt_ldisc;
179
180 /******************************************************************
181                     LINE DISCIPLINE METHODS
182 ******************************************************************/
183
184 /*
185  * Set our line discipline on the tty.
186  * Called from device open routine or ttioctl() at >= splsofttty()
187  */
188 static int
189 ngt_open(cdev_t dev, struct tty *tp)
190 {
191         struct thread *td = curthread;  /* XXX */
192         char name[sizeof(NG_TTY_NODE_TYPE) + 8];
193         sc_p sc;
194         int error;
195
196         /* Super-user only */
197         if ((error = priv_check(td, PRIV_ROOT)))
198                 return (error);
199         crit_enter();
200         lwkt_gettoken(&tty_token);
201
202         /* Already installed? */
203         if (tp->t_line == NETGRAPHDISC) {
204                 sc = (sc_p) tp->t_sc;
205                 if (sc != NULL && sc->tp == tp)
206                         goto done;
207         }
208
209         /* Initialize private struct */
210         MALLOC(sc, sc_p, sizeof(*sc), M_NETGRAPH, M_WAITOK | M_ZERO);
211         sc->tp = tp;
212         sc->hotchar = NG_TTY_DFL_HOTCHAR;
213         sc->qtail = &sc->qhead;
214         QUEUECHECK(sc);
215         callout_init_mp(&sc->ctimeout);
216
217         /* Setup netgraph node */
218         ngt_nodeop_ok = 1;
219         error = ng_make_node_common(&typestruct, &sc->node);
220         ngt_nodeop_ok = 0;
221         if (error) {
222                 FREE(sc, M_NETGRAPH);
223                 goto done;
224         }
225         ksnprintf(name, sizeof(name), "%s%d", typestruct.name, ngt_unit++);
226
227         /* Set back pointers */
228         sc->node->private = sc;
229         tp->t_sc = (caddr_t) sc;
230
231         /* Assign node its name */
232         if ((error = ng_name_node(sc->node, name))) {
233                 log(LOG_ERR, "%s: node name exists?\n", name);
234                 ngt_nodeop_ok = 1;
235                 ng_rmnode(sc->node);
236                 ngt_nodeop_ok = 0;
237                 goto done;
238         }
239
240         /*
241          * Pre-allocate cblocks to the an appropriate amount.
242          * I'm not sure what is appropriate.
243          */
244         ttyflush(tp, FREAD | FWRITE);
245         clist_alloc_cblocks(&tp->t_canq, 0, 0);
246         clist_alloc_cblocks(&tp->t_rawq, 0, 0);
247         clist_alloc_cblocks(&tp->t_outq,
248             MLEN + NGT_HIWATER, MLEN + NGT_HIWATER);
249
250 done:
251         /* Done */
252         lwkt_reltoken(&tty_token);
253         crit_exit();
254         return (error);
255 }
256
257 /*
258  * Line specific close routine, called from device close routine
259  * and from ttioctl at >= splsofttty(). This causes the node to
260  * be destroyed as well.
261  */
262 static int
263 ngt_close(struct tty *tp, int flag)
264 {
265         const sc_p sc = (sc_p) tp->t_sc;
266
267         crit_enter();
268         lwkt_gettoken(&tty_token);
269         ttyflush(tp, FREAD | FWRITE);
270         clist_free_cblocks(&tp->t_outq);
271         tp->t_line = 0;
272         if (sc != NULL) {
273                 if (sc->flags & FLG_TIMEOUT) {
274                         callout_stop(&sc->ctimeout);
275                         sc->flags &= ~FLG_TIMEOUT;
276                 }
277                 ngt_nodeop_ok = 1;
278                 ng_rmnode(sc->node);
279                 ngt_nodeop_ok = 0;
280                 tp->t_sc = NULL;
281         }
282         lwkt_reltoken(&tty_token);
283         crit_exit();
284         return (0);
285 }
286
287 /*
288  * Once the device has been turned into a node, we don't allow reading.
289  */
290 static int
291 ngt_read(struct tty *tp, struct uio *uio, int flag)
292 {
293         return (EIO);
294 }
295
296 /*
297  * Once the device has been turned into a node, we don't allow writing.
298  */
299 static int
300 ngt_write(struct tty *tp, struct uio *uio, int flag)
301 {
302         return (EIO);
303 }
304
305 /*
306  * We implement the NGIOCGINFO ioctl() defined in ng_message.h.
307  */
308 static int
309 ngt_tioctl(struct tty *tp, u_long cmd, caddr_t data, int flag, struct ucred *cred)
310 {
311         const sc_p sc = (sc_p) tp->t_sc;
312         int error = 0;
313
314         crit_enter();
315         lwkt_gettoken(&tty_token);
316         switch (cmd) {
317         case NGIOCGINFO:
318             {
319                 struct nodeinfo *const ni = (struct nodeinfo *) data;
320                 const node_p node = sc->node;
321
322                 bzero(ni, sizeof(*ni));
323                 if (node->name)
324                         strncpy(ni->name, node->name, sizeof(ni->name) - 1);
325                 strncpy(ni->type, node->type->name, sizeof(ni->type) - 1);
326                 ni->id = (u_int32_t)(uintptr_t)node;
327                 ni->hooks = node->numhooks;
328                 break;
329             }
330         default:
331                 ERROUT(ENOIOCTL);
332         }
333 done:
334         lwkt_reltoken(&tty_token);
335         crit_exit();
336         return (error);
337 }
338
339 /*
340  * Receive data coming from the device. We get one character at
341  * a time, which is kindof silly.
342  * Only guaranteed to be at splsofttty() or spltty().
343  */
344 static int
345 ngt_input(int c, struct tty *tp)
346 {
347         const sc_p sc = (sc_p) tp->t_sc;
348         const node_p node = sc->node;
349         struct mbuf *m;
350         int error = 0;
351
352         lwkt_gettoken(&tty_token);
353         if (!sc || tp != sc->tp) {
354                 lwkt_reltoken(&tty_token);
355                 return (0);
356         }
357         crit_enter();
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, MB_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         crit_exit();
404         lwkt_reltoken(&tty_token);
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
418         crit_enter();
419         lwkt_gettoken(&tty_token);
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                 callout_reset(&sc->ctimeout, 1, ngt_timeout, sc);
465                 sc->flags |= FLG_TIMEOUT;
466         }
467         lwkt_reltoken(&tty_token);
468         crit_exit();
469         return (0);
470 }
471
472 /*
473  * We still have data to output to the device, so try sending more.
474  */
475 static void
476 ngt_timeout(void *arg)
477 {
478         const sc_p sc = (sc_p) arg;
479
480         crit_enter();
481         lwkt_gettoken(&tty_token);
482         sc->flags &= ~FLG_TIMEOUT;
483         ngt_start(sc->tp);
484         lwkt_reltoken(&tty_token);
485         crit_exit();
486 }
487
488 /******************************************************************
489                     NETGRAPH NODE METHODS
490 ******************************************************************/
491
492 /*
493  * Initialize a new node of this type.
494  *
495  * We only allow nodes to be created as a result of setting
496  * the line discipline on a tty, so always return an error if not.
497  */
498 static int
499 ngt_constructor(node_p *nodep)
500 {
501         if (!ngt_nodeop_ok)
502                 return (EOPNOTSUPP);
503         return (ng_make_node_common(&typestruct, nodep));
504 }
505
506 /*
507  * Add a new hook. There can only be one.
508  */
509 static int
510 ngt_newhook(node_p node, hook_p hook, const char *name)
511 {
512         const sc_p sc = node->private;
513         int error = 0;
514
515         if (strcmp(name, NG_TTY_HOOK))
516                 return (EINVAL);
517         crit_enter();
518         lwkt_gettoken(&tty_token);
519         if (sc->hook)
520                 ERROUT(EISCONN);
521         sc->hook = hook;
522 done:
523         lwkt_reltoken(&tty_token);
524         crit_exit();
525         return (error);
526 }
527
528 /*
529  * Disconnect the hook
530  */
531 static int
532 ngt_disconnect(hook_p hook)
533 {
534         const sc_p sc = hook->node->private;
535
536         crit_enter();
537         lwkt_gettoken(&tty_token);
538         if (hook != sc->hook)
539                 panic(__func__);
540         sc->hook = NULL;
541         m_freem(sc->m);
542         sc->m = NULL;
543         lwkt_reltoken(&tty_token);
544         crit_exit();
545         return (0);
546 }
547
548 /*
549  * Remove this node. The does the netgraph portion of the shutdown.
550  * This should only be called indirectly from ngt_close().
551  */
552 static int
553 ngt_shutdown(node_p node)
554 {
555         const sc_p sc = node->private;
556
557         if (!ngt_nodeop_ok)
558                 return (EOPNOTSUPP);
559         lwkt_gettoken(&tty_token);
560         ng_unname(node);
561         ng_cutlinks(node);
562         node->private = NULL;
563         ng_unref(sc->node);
564         m_freem(sc->qhead);
565         m_freem(sc->m);
566         bzero(sc, sizeof(*sc));
567         FREE(sc, M_NETGRAPH);
568         lwkt_reltoken(&tty_token);
569         return (0);
570 }
571
572 /*
573  * Receive incoming data from netgraph system. Put it on our
574  * output queue and start output if necessary.
575  */
576 static int
577 ngt_rcvdata(hook_p hook, struct mbuf *m, meta_p meta)
578 {
579         const sc_p sc = hook->node->private;
580         int error = 0;
581
582         if (hook != sc->hook)
583                 panic(__func__);
584         NG_FREE_META(meta);
585         crit_enter();
586         lwkt_gettoken(&tty_token);
587         if (sc->qlen >= MAX_MBUFQ)
588                 ERROUT(ENOBUFS);
589         m->m_nextpkt = NULL;
590         *sc->qtail = m;
591         sc->qtail = &m->m_nextpkt;
592         sc->qlen++;
593         QUEUECHECK(sc);
594         m = NULL;
595         if (sc->qlen == 1)
596                 ngt_start(sc->tp);
597 done:
598         lwkt_reltoken(&tty_token);
599         crit_exit();
600         if (m)
601                 m_freem(m);
602         return (error);
603 }
604
605 /*
606  * Receive control message
607  */
608 static int
609 ngt_rcvmsg(node_p node, struct ng_mesg *msg, const char *retaddr,
610            struct ng_mesg **rptr)
611 {
612         const sc_p sc = (sc_p) node->private;
613         struct ng_mesg *resp = NULL;
614         int error = 0;
615
616         lwkt_gettoken(&tty_token);
617         switch (msg->header.typecookie) {
618         case NGM_TTY_COOKIE:
619                 switch (msg->header.cmd) {
620                 case NGM_TTY_SET_HOTCHAR:
621                     {
622                         int     hotchar;
623
624                         if (msg->header.arglen != sizeof(int))
625                                 ERROUT(EINVAL);
626                         hotchar = *((int *) msg->data);
627                         if (hotchar != (u_char) hotchar && hotchar != -1)
628                                 ERROUT(EINVAL);
629                         sc->hotchar = hotchar;  /* race condition is OK */
630                         break;
631                     }
632                 case NGM_TTY_GET_HOTCHAR:
633                         NG_MKRESPONSE(resp, msg, sizeof(int), M_NOWAIT);
634                         if (!resp)
635                                 ERROUT(ENOMEM);
636                         /* Race condition here is OK */
637                         *((int *) resp->data) = sc->hotchar;
638                         break;
639                 default:
640                         ERROUT(EINVAL);
641                 }
642                 break;
643         default:
644                 ERROUT(EINVAL);
645         }
646         if (rptr)
647                 *rptr = resp;
648         else if (resp)
649                 FREE(resp, M_NETGRAPH);
650
651 done:
652         FREE(msg, M_NETGRAPH);
653         lwkt_reltoken(&tty_token);
654         return (error);
655 }
656
657 /******************************************************************
658                         INITIALIZATION
659 ******************************************************************/
660
661 /*
662  * Handle loading and unloading for this node type
663  */
664 static int
665 ngt_mod_event(module_t mod, int event, void *data)
666 {
667         /* struct ng_type *const type = data;*/
668         int error = 0;
669
670         lwkt_gettoken(&tty_token);
671         switch (event) {
672         case MOD_LOAD:
673                 /* Register line discipline */
674                 crit_enter();
675                 if ((ngt_ldisc = ldisc_register(NETGRAPHDISC, &ngt_disc)) < 0) {
676                         crit_exit();
677                         log(LOG_ERR, "%s: can't register line discipline",
678                             __func__);
679                         return (EIO);
680                 }
681                 crit_exit();
682                 break;
683
684         case MOD_UNLOAD:
685
686                 /* Unregister line discipline */
687                 crit_enter();
688                 ldisc_deregister(ngt_ldisc);
689                 crit_exit();
690                 break;
691
692         default:
693                 error = EOPNOTSUPP;
694                 break;
695         }
696         lwkt_reltoken(&tty_token);
697         return (error);
698 }