Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / ttcompat.c
blob2cd754afd3681d2a2f2ab21e51477e5dfe0c9bc7
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 * Copyright 2018 OmniOS Community Edition (OmniOSce) Association.
27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
28 /* All Rights Reserved */
31 * University Copyright- Copyright (c) 1982, 1986, 1988
32 * The Regents of the University of California
33 * All Rights Reserved
35 * University Acknowledgment- Portions of this document are derived from
36 * software developed by the University of California, Berkeley, and its
37 * contributors.
41 * Module to intercept old V7 and 4BSD "ioctl" calls.
44 #include <sys/types.h>
45 #include <sys/param.h>
46 #include <sys/signal.h>
47 #include <sys/file.h>
48 #include <sys/termios.h>
49 #include <sys/ttold.h>
50 #include <sys/cmn_err.h>
51 #include <sys/stream.h>
52 #include <sys/stropts.h>
53 #include <sys/strsubr.h>
54 #include <sys/strsun.h>
55 #include <sys/errno.h>
56 #include <sys/debug.h>
57 #include <sys/ttcompat.h>
58 #include <sys/ddi.h>
59 #include <sys/sunddi.h>
60 #include <sys/kmem.h>
61 #include <sys/policy.h>
64 * This is the loadable module wrapper.
66 #include <sys/conf.h>
67 #include <sys/modctl.h>
69 /* See os/streamio.c */
70 extern int sgttyb_handling;
72 static struct streamtab ttcoinfo;
74 static struct fmodsw fsw = {
75 "ttcompat",
76 &ttcoinfo,
77 D_MTQPAIR | D_MP | _D_SINGLE_INSTANCE
81 * Module linkage information for the kernel.
84 static struct modlstrmod modlstrmod = {
85 &mod_strmodops,
86 "alt ioctl calls",
87 &fsw
90 static struct modlinkage modlinkage = {
91 MODREV_1, &modlstrmod, NULL
94 int
95 _init(void)
97 return (mod_install(&modlinkage));
101 _fini(void)
103 return (mod_remove(&modlinkage));
107 _info(struct modinfo *modinfop)
109 return (mod_info(&modlinkage, modinfop));
112 static int ttcompatopen(queue_t *, dev_t *, int, int, cred_t *);
113 static int ttcompatclose(queue_t *, int, cred_t *);
114 static void ttcompatrput(queue_t *, mblk_t *);
115 static void ttcompatwput(queue_t *, mblk_t *);
117 static struct module_info ttycompatmiinfo = {
119 "ttcompat",
121 INFPSZ,
122 2048,
126 static struct qinit ttycompatrinit = {
127 (int (*)())ttcompatrput,
128 NULL,
129 ttcompatopen,
130 ttcompatclose,
131 NULL,
132 &ttycompatmiinfo
135 static struct module_info ttycompatmoinfo = {
137 "ttcompat",
139 INFPSZ,
140 300,
144 static struct qinit ttycompatwinit = {
145 (int (*)())ttcompatwput,
146 NULL,
147 ttcompatopen,
148 ttcompatclose,
149 NULL,
150 &ttycompatmoinfo
153 static struct streamtab ttcoinfo = {
154 &ttycompatrinit,
155 &ttycompatwinit,
156 NULL,
157 NULL
160 static void ttcompat_do_ioctl(ttcompat_state_t *, queue_t *, mblk_t *);
161 static void ttcompat_ioctl_ack(queue_t *, mblk_t *);
162 static void ttcopyout(queue_t *, mblk_t *);
163 static void ttcompat_ioctl_nak(queue_t *, mblk_t *);
164 static void from_compat(compat_state_t *, struct termios *);
165 static void to_compat(struct termios *, compat_state_t *);
168 * Open - get the current modes and translate them to the V7/4BSD equivalent.
170 /*ARGSUSED*/
171 static int
172 ttcompatopen(queue_t *q, dev_t *devp, int oflag, int sflag, cred_t *crp)
174 ttcompat_state_t *tp;
176 if (q->q_ptr != NULL) {
177 tp = (ttcompat_state_t *)q->q_ptr;
178 /* fail open if TIOCEXCL was done and its not privileged */
179 if ((tp->t_new_lflags & XCLUDE) &&
180 secpolicy_excl_open(crp) != 0) {
181 return (EBUSY);
183 return (0); /* already attached */
185 tp = kmem_zalloc(sizeof (ttcompat_state_t), KM_SLEEP);
186 q->q_ptr = tp;
187 WR(q)->q_ptr = tp;
188 qprocson(q);
190 return (0);
193 /* ARGSUSED1 */
194 static int
195 ttcompatclose(queue_t *q, int flag, cred_t *crp)
197 ttcompat_state_t *tp = (ttcompat_state_t *)q->q_ptr;
198 mblk_t *mp;
200 /* Dump the state structure, then unlink it */
201 qprocsoff(q);
202 if (tp->t_bufcallid != 0) {
203 qunbufcall(q, tp->t_bufcallid);
204 tp->t_bufcallid = 0;
206 if ((mp = tp->t_iocpending) != NULL)
207 freemsg(mp);
208 kmem_free(tp, sizeof (ttcompat_state_t));
209 q->q_ptr = NULL;
211 return (0);
215 * Put procedure for input from driver end of stream (read queue).
216 * Most messages just get passed to the next guy up; we intercept
217 * "ioctl" replies, and if it's an "ioctl" whose reply we plan to do
218 * something with, we do it.
220 static void
221 ttcompatrput(queue_t *q, mblk_t *mp)
223 switch (mp->b_datap->db_type) {
225 case M_IOCACK:
226 ttcompat_ioctl_ack(q, mp);
227 break;
229 case M_IOCNAK:
230 ttcompat_ioctl_nak(q, mp);
231 break;
233 default:
234 putnext(q, mp);
235 break;
240 * Line discipline output queue put procedure: speeds M_IOCTL
241 * messages.
243 static void
244 ttcompatwput(queue_t *q, mblk_t *mp)
246 ttcompat_state_t *tp;
247 struct copyreq *cqp;
248 struct copyresp *csp;
249 struct iocblk *iocbp;
251 tp = (ttcompat_state_t *)q->q_ptr;
254 * Process some M_IOCTL messages here; pass everything else down.
256 switch (mp->b_datap->db_type) {
258 default:
259 putnext(q, mp);
260 return;
262 case M_IOCTL:
263 iocbp = (struct iocblk *)mp->b_rptr;
265 switch (iocbp->ioc_cmd) {
267 default:
268 /* these are ioctls with no arguments or are known to stream head */
269 /* process them right away */
270 ttcompat_do_ioctl(tp, q, mp);
271 return;
272 case TIOCSETN:
273 case TIOCSLTC:
274 case TIOCSETC:
275 case TIOCLBIS:
276 case TIOCLBIC:
277 case TIOCLSET:
278 case TIOCFLUSH:
279 if (iocbp->ioc_count != TRANSPARENT) {
280 putnext(q, mp);
281 return;
284 mp->b_datap->db_type = M_COPYIN;
285 cqp = (struct copyreq *)mp->b_rptr;
286 cqp->cq_addr = (caddr_t)*(intptr_t *)mp->b_cont->b_rptr;
287 switch (iocbp->ioc_cmd) {
288 case TIOCSETN:
289 cqp->cq_size = sizeof (struct sgttyb);
290 break;
291 case TIOCSLTC:
292 cqp->cq_size = sizeof (struct ltchars);
293 break;
294 case TIOCSETC:
295 cqp->cq_size = sizeof (struct tchars);
296 break;
297 case TIOCLBIS:
298 case TIOCLBIC:
299 case TIOCLSET:
300 case TIOCFLUSH:
301 cqp->cq_size = sizeof (int);
302 break;
303 default:
304 break;
306 cqp->cq_flag = 0;
307 cqp->cq_private = NULL;
308 freemsg(mp->b_cont);
309 mp->b_cont = NULL;
310 mp->b_wptr = mp->b_rptr + sizeof (struct copyreq);
311 tp->t_ioccmd = iocbp->ioc_cmd;
312 tp->t_state |= TS_W_IN;
313 qreply(q, mp);
314 return;
316 } /* switch ioc_cmd */
317 case M_IOCDATA:
318 csp = (struct copyresp *)mp->b_rptr;
320 switch (csp->cp_cmd) {
322 default:
323 putnext(q, mp);
324 return;
326 case TIOCSETN:
327 case TIOCSLTC:
328 case TIOCSETC:
329 case TIOCLBIS:
330 case TIOCLBIC:
331 case TIOCLSET:
332 case TIOCFLUSH:
333 tp->t_state &= ~TS_W_IN;
334 if (csp->cp_rval != 0) { /* failure */
335 freemsg(mp);
336 return;
339 /* make it look like an ioctl */
340 mp->b_datap->db_type = M_IOCTL;
341 mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
342 iocbp = (struct iocblk *)mp->b_rptr;
343 iocbp->ioc_count = MBLKL(mp->b_cont);
344 iocbp->ioc_error = 0;
345 iocbp->ioc_rval = 0;
346 ttcompat_do_ioctl(tp, q, mp);
347 return;
349 case TIOCGLTC:
350 case TIOCLGET:
351 case TIOCGETC:
352 tp->t_state &= ~TS_W_OUT;
353 if (csp->cp_rval != 0) { /* failure */
354 freemsg(mp);
355 return;
358 iocbp = (struct iocblk *)mp->b_rptr;
359 iocbp->ioc_count = 0;
360 iocbp->ioc_error = 0;
361 iocbp->ioc_rval = 0;
362 mp->b_datap->db_type = M_IOCACK;
363 qreply(q, mp);
364 return;
366 } /* switch cp_cmd */
367 } /* end message switch */
371 * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
372 * the buffer we need.
374 static void
375 ttcompat_reioctl(void *arg)
377 queue_t *q = arg;
378 ttcompat_state_t *tp;
379 mblk_t *mp;
381 tp = (ttcompat_state_t *)q->q_ptr;
382 tp->t_bufcallid = 0;
384 if ((mp = tp->t_iocpending) != NULL) {
385 tp->t_iocpending = NULL; /* not pending any more */
386 ttcompat_do_ioctl(tp, q, mp);
391 * Handle old-style "ioctl" messages; pass the rest down unmolested.
393 static void
394 ttcompat_do_ioctl(ttcompat_state_t *tp, queue_t *q, mblk_t *mp)
396 struct iocblk *iocp;
397 int error;
400 * Most of the miocpullup()'s below aren't needed because the
401 * ioctls in question are actually transparent M_IOCDATA messages
402 * dummied to look like M_IOCTL messages. However, for clarity and
403 * robustness against future changes, we've included them anyway.
406 iocp = (struct iocblk *)mp->b_rptr;
407 switch (iocp->ioc_cmd) {
410 * "get"-style calls that get translated data from the "termios"
411 * structure. Save the existing code and pass it down as a TCGETS.
413 case TIOCGETC:
414 case TIOCLGET:
415 case TIOCGLTC:
416 if (iocp->ioc_count != TRANSPARENT) {
417 miocnak(q, mp, 0, EINVAL);
418 return;
422 * We can get here with t_arg != 0, iff the stream head
423 * has for some reason given up on the ioctl in progress.
424 * The most likely cause is an interrupted ioctl syscall.
425 * We will behave robustly because (given our perimeter)
426 * the ttcompat_state_t will get set up for the new ioctl,
427 * and when the response we were waiting for appears it
428 * will be passed on to the stream head which will discard
429 * it as non-current.
431 ASSERT(mp->b_cont != NULL);
432 tp->t_arg = *(intptr_t *)mp->b_cont->b_rptr;
433 /* free the data buffer - it might not be sufficient */
434 /* driver will allocate one for termios size */
435 freemsg(mp->b_cont);
436 mp->b_cont = NULL;
437 iocp->ioc_count = 0;
438 /* FALLTHRU */
439 case TIOCGETP:
440 goto dogets;
443 * "set"-style calls that set translated data into a "termios"
444 * structure. Set our idea of the new state from the value
445 * given to us. We then have to get the current state, so we
446 * turn this guy into a TCGETS and pass it down. When the
447 * ACK comes back, we modify the state we got back and shove it
448 * back down as the appropriate type of TCSETS.
450 case TIOCSETP:
451 case TIOCSETN:
452 error = miocpullup(mp, sizeof (struct sgttyb));
453 if (error != 0) {
454 miocnak(q, mp, 0, error);
455 return;
457 tp->t_new_sgttyb = *((struct sgttyb *)mp->b_cont->b_rptr);
458 goto dogets;
460 case TIOCSETC:
461 error = miocpullup(mp, sizeof (struct tchars));
462 if (error != 0) {
463 miocnak(q, mp, 0, error);
464 return;
466 tp->t_new_tchars = *((struct tchars *)mp->b_cont->b_rptr);
467 goto dogets;
469 case TIOCSLTC:
470 error = miocpullup(mp, sizeof (struct ltchars));
471 if (error != 0) {
472 miocnak(q, mp, 0, error);
473 return;
475 tp->t_new_ltchars = *((struct ltchars *)mp->b_cont->b_rptr);
476 goto dogets;
478 case TIOCLBIS:
479 case TIOCLBIC:
480 case TIOCLSET:
481 error = miocpullup(mp, sizeof (int));
482 if (error != 0) {
483 miocnak(q, mp, 0, error);
484 return;
486 tp->t_new_lflags = *(int *)mp->b_cont->b_rptr;
487 goto dogets;
490 * "set"-style call that sets a particular bit in a "termios"
491 * structure. We then have to get the current state, so we
492 * turn this guy into a TCGETS and pass it down. When the
493 * ACK comes back, we modify the state we got back and shove it
494 * back down as the appropriate type of TCSETS.
496 case TIOCHPCL:
497 dogets:
498 tp->t_ioccmd = iocp->ioc_cmd;
499 tp->t_iocid = iocp->ioc_id;
500 tp->t_state |= TS_IOCWAIT;
501 iocp->ioc_cmd = TCGETS;
502 iocp->ioc_count = 0; /* no data returned unless we say so */
503 break;
506 * "set"-style call that sets DTR. Pretend that it was a TIOCMBIS
507 * with TIOCM_DTR set.
509 case TIOCSDTR: {
510 mblk_t *datap;
512 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
513 goto allocfailure;
514 *(int *)datap->b_wptr = TIOCM_DTR;
515 datap->b_wptr += sizeof (int);
516 iocp->ioc_cmd = TIOCMBIS; /* turn it into a TIOCMBIS */
517 if (mp->b_cont != NULL)
518 freemsg(mp->b_cont);
519 mp->b_cont = datap; /* attach the data */
520 iocp->ioc_count = sizeof (int); /* in case driver checks */
521 break;
525 * "set"-style call that clears DTR. Pretend that it was a TIOCMBIC
526 * with TIOCM_DTR set.
528 case TIOCCDTR: {
529 mblk_t *datap;
531 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
532 goto allocfailure;
533 *(int *)datap->b_wptr = TIOCM_DTR;
534 datap->b_wptr += sizeof (int);
535 iocp->ioc_cmd = TIOCMBIC; /* turn it into a TIOCMBIC */
536 if (mp->b_cont != NULL)
537 freemsg(mp->b_cont);
538 mp->b_cont = datap; /* attach the data */
539 iocp->ioc_count = sizeof (int); /* in case driver checks */
540 break;
544 * Translate into the S5 form of TCFLSH.
546 case TIOCFLUSH: {
547 int flags;
549 error = miocpullup(mp, sizeof (int));
550 if (error != 0) {
551 miocnak(q, mp, 0, error);
552 return;
554 flags = *(int *)mp->b_cont->b_rptr;
556 switch (flags&(FREAD|FWRITE)) {
558 case 0:
559 case FREAD|FWRITE:
560 flags = 2; /* flush 'em both */
561 break;
563 case FREAD:
564 flags = 0; /* flush read */
565 break;
567 case FWRITE:
568 flags = 1; /* flush write */
569 break;
571 iocp->ioc_cmd = TCFLSH; /* turn it into a TCFLSH */
572 *(int *)mp->b_cont->b_rptr = flags; /* fiddle the arg */
573 break;
577 * Turn into a TCXONC.
579 case TIOCSTOP: {
580 mblk_t *datap;
582 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
583 goto allocfailure;
584 *(int *)datap->b_wptr = 0; /* stop */
585 datap->b_wptr += sizeof (int);
586 iocp->ioc_cmd = TCXONC; /* turn it into a XONC */
587 iocp->ioc_count = sizeof (int);
588 if (mp->b_cont != NULL)
589 freemsg(mp->b_cont);
590 mp->b_cont = datap; /* attach the data */
591 break;
594 case TIOCSTART: {
595 mblk_t *datap;
597 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL)
598 goto allocfailure;
599 *(int *)datap->b_wptr = 1; /* start */
600 datap->b_wptr += sizeof (int);
601 iocp->ioc_cmd = TCXONC; /* turn it into a XONC */
602 iocp->ioc_count = sizeof (int);
603 if (mp->b_cont != NULL)
604 freemsg(mp->b_cont);
605 mp->b_cont = datap; /* attach the data */
606 break;
608 case TIOCSETD:
609 case TIOCGETD:
610 case DIOCSETP:
611 case DIOCGETP:
612 case LDOPEN:
613 case LDCLOSE:
614 case LDCHG:
615 case LDSETT:
616 case LDGETT:
618 * All of these ioctls are just ACK'd, except for
619 * TIOCSETD, which must be for line discipline zero.
621 mp->b_datap->db_type = M_IOCACK;
622 if (iocp->ioc_cmd == TIOCSETD) {
623 iocp->ioc_error = miocpullup(mp, sizeof (uchar_t));
624 if (iocp->ioc_error == 0 && (*mp->b_cont->b_rptr != 0))
625 mp->b_datap->db_type = M_IOCNAK;
628 iocp->ioc_error = 0;
629 iocp->ioc_count = 0;
630 iocp->ioc_rval = 0;
631 qreply(q, mp);
632 return;
633 case IOCTYPE:
634 mp->b_datap->db_type = M_IOCACK;
635 iocp->ioc_error = 0;
636 iocp->ioc_count = 0;
637 iocp->ioc_rval = TIOC;
638 qreply(q, mp);
639 return;
640 case TIOCEXCL:
641 /* check for binary value of XCLUDE flag ???? */
642 tp->t_new_lflags |= XCLUDE;
643 mp->b_datap->db_type = M_IOCACK;
644 iocp->ioc_error = 0;
645 iocp->ioc_count = 0;
646 iocp->ioc_rval = 0;
647 qreply(q, mp);
648 return;
649 case TIOCNXCL:
650 tp->t_new_lflags &= ~XCLUDE;
651 mp->b_datap->db_type = M_IOCACK;
652 iocp->ioc_error = 0;
653 iocp->ioc_count = 0;
654 iocp->ioc_rval = 0;
655 qreply(q, mp);
656 return;
660 * We don't reply to most calls, we just pass them down,
661 * possibly after modifying the arguments.
663 putnext(q, mp);
664 return;
666 allocfailure:
668 * We needed to allocate something to handle this "ioctl", but
669 * couldn't; save this "ioctl" and arrange to get called back when
670 * it's more likely that we can get what we need.
671 * If there's already one being saved, throw it out, since it
672 * must have timed out.
674 if (tp->t_iocpending != NULL)
675 freemsg(tp->t_iocpending);
676 tp->t_iocpending = mp; /* hold this ioctl */
677 if (tp->t_bufcallid != 0)
678 qunbufcall(q, tp->t_bufcallid);
680 tp->t_bufcallid = qbufcall(q, sizeof (struct iocblk), BPRI_HI,
681 ttcompat_reioctl, q);
685 * Called when an M_IOCACK message is seen on the read queue; if this
686 * is the response we were waiting for, we either:
687 * modify the data going up (if the "ioctl" read data); since in all
688 * cases, the old-style returned information is smaller than or the same
689 * size as the new-style returned information, we just overwrite the old
690 * stuff with the new stuff (beware of changing structure sizes, in case
691 * you invalidate this)
692 * or
693 * take this data, modify it appropriately, and send it back down (if
694 * the "ioctl" wrote data).
695 * In either case, we cancel the "wait"; the final response to a "write"
696 * ioctl goes back up to the user.
697 * If this wasn't the response we were waiting for, just pass it up.
699 static void
700 ttcompat_ioctl_ack(queue_t *q, mblk_t *mp)
702 ttcompat_state_t *tp;
703 struct iocblk *iocp;
704 mblk_t *datap;
706 tp = (ttcompat_state_t *)q->q_ptr;
707 iocp = (struct iocblk *)mp->b_rptr;
709 if (!(tp->t_state&TS_IOCWAIT) || iocp->ioc_id != tp->t_iocid) {
711 * This isn't the reply we're looking for. Move along.
713 putnext(q, mp);
714 return;
717 datap = mp->b_cont; /* mblk containing data going up */
719 switch (tp->t_ioccmd) {
721 case TIOCGETP: {
722 struct sgttyb *cb;
724 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
725 datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
726 /* recycle the reply's buffer */
727 cb = (struct sgttyb *)datap->b_wptr;
729 * This is used for TIOCGETP handling of sg_ispeed and
730 * sg_ospeed. If the current speed is over 38400 (the
731 * sgttyb limit), then we report 38400. Note that
732 * when "compatibility with old releases" is enabled
733 * (sgttyb_handling == 0), then t_[io]speed will have
734 * garbled nonsense, as in prior releases. (See
735 * to_compat() below).
737 cb->sg_ispeed = tp->t_curstate.t_ispeed > B38400 ? B38400 :
738 tp->t_curstate.t_ispeed;
739 cb->sg_ospeed = tp->t_curstate.t_ospeed > B38400 ? B38400 :
740 tp->t_curstate.t_ospeed;
741 cb->sg_erase = tp->t_curstate.t_erase;
742 cb->sg_kill = tp->t_curstate.t_kill;
743 cb->sg_flags = tp->t_curstate.t_flags;
744 datap->b_wptr += sizeof (struct sgttyb);
745 iocp->ioc_count = sizeof (struct sgttyb);
747 /* you are lucky - stream head knows how to copy you out */
749 tp->t_state &= ~TS_IOCWAIT; /* we got what we wanted */
750 iocp->ioc_rval = 0;
751 iocp->ioc_cmd = tp->t_ioccmd;
752 putnext(q, mp);
753 return;
756 case TIOCGETC:
757 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
758 datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
759 /* recycle the reply's buffer */
760 bcopy(&tp->t_curstate.t_intrc, datap->b_wptr,
761 sizeof (struct tchars));
762 datap->b_wptr += sizeof (struct tchars);
763 break;
765 case TIOCGLTC:
766 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
767 datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
768 /* recycle the reply's buffer */
769 bcopy(&tp->t_curstate.t_suspc, datap->b_wptr,
770 sizeof (struct ltchars));
771 datap->b_wptr += sizeof (struct ltchars);
772 break;
774 case TIOCLGET:
775 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
776 datap->b_rptr = datap->b_wptr = datap->b_datap->db_base;
777 /* recycle the reply's buffer */
778 *(int *)datap->b_wptr =
779 ((unsigned)tp->t_curstate.t_flags) >> 16;
780 datap->b_wptr += sizeof (int);
781 break;
783 case TIOCSETP:
784 case TIOCSETN:
786 * Get the current state from the GETS data, and
787 * update it.
789 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
790 tp->t_curstate.t_erase = tp->t_new_sgttyb.sg_erase;
791 tp->t_curstate.t_kill = tp->t_new_sgttyb.sg_kill;
793 * For new-style handling, we ignore requests to set
794 * B38400 when the current speed is over B38400. This
795 * means that we change the speed as requested if:
796 * old style (sgttyb_handling == 0) is requested
797 * the requested new speed isn't B38400
798 * the current speed is at or below B38400
799 * Note that when old style is requested, both speeds
800 * in t_curstate are set to <= B38400 by to_compat, so
801 * the first test isn't needed here.
802 * Also note that we silently allow the user to set
803 * speeds above B38400 through this interface,
804 * regardless of the style setting. This allows
805 * greater compatibility with current BSD releases.
807 if (tp->t_new_sgttyb.sg_ispeed != B38400 ||
808 tp->t_curstate.t_ispeed <= B38400)
809 tp->t_curstate.t_ispeed = tp->t_new_sgttyb.sg_ispeed;
810 if (tp->t_new_sgttyb.sg_ospeed != B38400 ||
811 tp->t_curstate.t_ospeed <= B38400)
812 tp->t_curstate.t_ospeed = tp->t_new_sgttyb.sg_ospeed;
813 tp->t_curstate.t_flags =
814 (tp->t_curstate.t_flags & 0xffff0000) |
815 (tp->t_new_sgttyb.sg_flags & 0xffff);
818 * Replace the data that came up with the updated data.
820 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
823 * Send it back down as a TCSETS or TCSETSF.
825 iocp->ioc_cmd = (tp->t_ioccmd == TIOCSETP) ? TCSETSF : TCSETS;
826 goto senddown;
828 case TIOCSETC:
830 * Get the current state from the GETS data, and
831 * update it.
833 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
834 bcopy(&tp->t_new_tchars,
835 &tp->t_curstate.t_intrc, sizeof (struct tchars));
838 * Replace the data that came up with the updated data.
840 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
843 * Send it back down as a TCSETS.
845 iocp->ioc_cmd = TCSETS;
846 goto senddown;
848 case TIOCSLTC:
850 * Get the current state from the GETS data, and
851 * update it.
853 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
854 bcopy(&tp->t_new_ltchars,
855 &tp->t_curstate.t_suspc, sizeof (struct ltchars));
858 * Replace the data that came up with the updated data.
860 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
863 * Send it back down as a TCSETS.
865 iocp->ioc_cmd = TCSETS;
866 goto senddown;
868 case TIOCLBIS:
870 * Get the current state from the GETS data, and
871 * update it.
873 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
874 tp->t_curstate.t_flags |= (tp->t_new_lflags << 16);
877 * Replace the data that came up with the updated data.
879 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
882 * Send it back down as a TCSETS.
884 iocp->ioc_cmd = TCSETS;
885 goto senddown;
887 case TIOCLBIC:
889 * Get the current state from the GETS data, and
890 * update it.
892 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
893 tp->t_curstate.t_flags &= ~(tp->t_new_lflags << 16);
896 * Replace the data that came up with the updated data.
898 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
901 * Send it back down as a TCSETS.
903 iocp->ioc_cmd = TCSETS;
904 goto senddown;
906 case TIOCLSET:
908 * Get the current state from the GETS data, and
909 * update it.
911 to_compat((struct termios *)datap->b_rptr, &tp->t_curstate);
912 tp->t_curstate.t_flags &= 0xffff;
913 tp->t_curstate.t_flags |= (tp->t_new_lflags << 16);
916 * Replace the data that came up with the updated data.
918 from_compat(&tp->t_curstate, (struct termios *)datap->b_rptr);
921 * Send it back down as a TCSETS.
923 iocp->ioc_cmd = TCSETS;
924 goto senddown;
926 case TIOCHPCL:
928 * Replace the data that came up with the updated data.
930 ((struct termios *)datap->b_rptr)->c_cflag |= HUPCL;
933 * Send it back down as a TCSETS.
935 iocp->ioc_cmd = TCSETS;
936 goto senddown;
938 case TCSETSF:
940 * We're acknowledging the terminal reset ioctl that we sent
941 * when the module was opened.
943 tp->t_state &= ~(TS_IOCWAIT | TS_TIOCNAK);
944 freemsg(mp);
945 return;
947 default:
948 cmn_err(CE_WARN, "ttcompat: Unexpected ioctl acknowledgment\n");
952 * All the calls that return something return 0.
954 tp->t_state &= ~TS_IOCWAIT; /* we got what we wanted */
955 iocp->ioc_rval = 0;
957 /* copy out the data - ioctl transparency */
958 iocp->ioc_cmd = tp->t_ioccmd;
959 ttcopyout(q, mp);
960 return;
962 senddown:
964 * Send a "get state" reply back down, with suitably-modified
965 * state, as a "set state" "ioctl".
967 tp->t_state &= ~TS_IOCWAIT;
968 mp->b_datap->db_type = M_IOCTL;
969 mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
970 putnext(WR(q), mp);
972 /* Called from ttcompatrput M_IOCACK processing. */
973 /* Copies out the data using M_COPYOUT messages */
975 static void
976 ttcopyout(queue_t *q, mblk_t *mp)
978 struct copyreq *cqp;
979 ttcompat_state_t *tp;
981 tp = (ttcompat_state_t *)q->q_ptr;
983 mp->b_datap->db_type = M_COPYOUT;
984 cqp = (struct copyreq *)mp->b_rptr;
985 cqp->cq_addr = (caddr_t)tp->t_arg; /* retrieve the 3rd argument */
986 tp->t_arg = 0; /* clear it since we don't need it anymore */
987 switch (tp->t_ioccmd) {
988 case TIOCGLTC:
989 cqp->cq_size = sizeof (struct ltchars);
990 break;
991 case TIOCGETC:
992 cqp->cq_size = sizeof (struct tchars);
993 break;
994 case TIOCLGET:
995 cqp->cq_size = sizeof (int);
996 break;
997 default:
998 cmn_err(CE_WARN,
999 "ttcompat: Unknown ioctl to copyout\n");
1000 break;
1002 cqp->cq_flag = 0;
1003 cqp->cq_private = NULL;
1004 tp->t_state |= TS_W_OUT;
1005 putnext(q, mp);
1010 * Called when an M_IOCNAK message is seen on the read queue; if this is
1011 * the response we were waiting for, cancel the wait. Pass the reply up;
1012 * if we were waiting for this response, we can't complete the "ioctl" and
1013 * the NAK will tell that to the guy above us.
1014 * If this wasn't the response we were waiting for, just pass it up.
1016 static void
1017 ttcompat_ioctl_nak(queue_t *q, mblk_t *mp)
1019 ttcompat_state_t *tp;
1020 struct iocblk *iocp;
1022 iocp = (struct iocblk *)mp->b_rptr;
1023 tp = (ttcompat_state_t *)q->q_ptr;
1025 if (tp->t_state&TS_IOCWAIT && iocp->ioc_id == tp->t_iocid) {
1026 tp->t_state &= ~TS_IOCWAIT; /* this call isn't going through */
1027 tp->t_arg = 0; /* we may have stashed the 3rd argument */
1029 putnext(q, mp);
1032 #define FROM_COMPAT_CHAR(to, from) { if ((to = from) == 0377) to = 0; }
1034 static void
1035 from_compat(compat_state_t *csp, struct termios *termiosp)
1037 termiosp->c_iflag = 0;
1038 termiosp->c_oflag &= (ONLRET|ONOCR);
1040 termiosp->c_cflag = (termiosp->c_cflag &
1041 (CRTSCTS|CRTSXOFF|PAREXT|LOBLK|HUPCL)) | CREAD;
1043 if (csp->t_ospeed > CBAUD) {
1044 termiosp->c_cflag |= ((csp->t_ospeed - CBAUD - 1) & CBAUD) |
1045 CBAUDEXT;
1046 } else {
1047 termiosp->c_cflag |= csp->t_ospeed & CBAUD;
1050 if (csp->t_ospeed != csp->t_ispeed) {
1051 if (csp->t_ispeed > (CIBAUD >> IBSHIFT)) {
1052 termiosp->c_cflag |= CIBAUDEXT |
1053 (((csp->t_ispeed - (CIBAUD >> IBSHIFT) - 1) <<
1054 IBSHIFT) & CIBAUD);
1055 } else {
1056 termiosp->c_cflag |= (csp->t_ispeed << IBSHIFT) &
1057 CIBAUD;
1059 /* hang up if ispeed=0 */
1060 if (csp->t_ispeed == 0)
1061 termiosp->c_cflag &= ~CBAUD & ~CBAUDEXT;
1063 if (csp->t_ispeed == B110 || csp->t_xflags & STOPB)
1064 termiosp->c_cflag |= CSTOPB;
1065 termiosp->c_lflag = ECHOK;
1066 FROM_COMPAT_CHAR(termiosp->c_cc[VERASE], csp->t_erase);
1067 FROM_COMPAT_CHAR(termiosp->c_cc[VKILL], csp->t_kill);
1068 FROM_COMPAT_CHAR(termiosp->c_cc[VINTR], csp->t_intrc);
1069 FROM_COMPAT_CHAR(termiosp->c_cc[VQUIT], csp->t_quitc);
1070 FROM_COMPAT_CHAR(termiosp->c_cc[VSTART], csp->t_startc);
1071 FROM_COMPAT_CHAR(termiosp->c_cc[VSTOP], csp->t_stopc);
1072 termiosp->c_cc[VEOL2] = 0;
1073 FROM_COMPAT_CHAR(termiosp->c_cc[VSUSP], csp->t_suspc);
1074 /* is this useful? */
1075 FROM_COMPAT_CHAR(termiosp->c_cc[VDSUSP], csp->t_dsuspc);
1076 FROM_COMPAT_CHAR(termiosp->c_cc[VREPRINT], csp->t_rprntc);
1077 FROM_COMPAT_CHAR(termiosp->c_cc[VDISCARD], csp->t_flushc);
1078 FROM_COMPAT_CHAR(termiosp->c_cc[VWERASE], csp->t_werasc);
1079 FROM_COMPAT_CHAR(termiosp->c_cc[VLNEXT], csp->t_lnextc);
1080 termiosp->c_cc[VSTATUS] = 0;
1081 if (csp->t_flags & O_TANDEM)
1082 termiosp->c_iflag |= IXOFF;
1083 if (csp->t_flags & O_LCASE) {
1084 termiosp->c_iflag |= IUCLC;
1085 termiosp->c_oflag |= OLCUC;
1086 termiosp->c_lflag |= XCASE;
1088 if (csp->t_flags & O_ECHO)
1089 termiosp->c_lflag |= ECHO;
1090 if (csp->t_flags & O_CRMOD) {
1091 termiosp->c_iflag |= ICRNL;
1092 termiosp->c_oflag |= ONLCR;
1093 switch (csp->t_flags & O_CRDELAY) {
1095 case O_CR1:
1096 termiosp->c_oflag |= CR2;
1097 break;
1099 case O_CR2:
1100 termiosp->c_oflag |= CR3;
1101 break;
1103 } else {
1104 if ((csp->t_flags & O_NLDELAY) == O_NL1)
1105 termiosp->c_oflag |= ONLRET|CR1; /* tty37 */
1107 if ((csp->t_flags & O_NLDELAY) == O_NL2)
1108 termiosp->c_oflag |= NL1;
1110 * When going into RAW mode, the special characters controlled by the
1111 * POSIX IEXTEN bit no longer apply; when leaving, they do.
1113 if (csp->t_flags & O_RAW) {
1114 termiosp->c_cflag |= CS8;
1115 termiosp->c_iflag &= ~(ICRNL|IUCLC);
1116 termiosp->c_lflag &= ~(XCASE|IEXTEN);
1117 } else {
1118 termiosp->c_iflag |= IMAXBEL|BRKINT|IGNPAR;
1119 if (termiosp->c_cc[VSTOP] != 0 && termiosp->c_cc[VSTART] != 0)
1120 termiosp->c_iflag |= IXON;
1121 if (csp->t_flags & O_LITOUT)
1122 termiosp->c_cflag |= CS8;
1123 else {
1124 if (csp->t_flags & O_PASS8)
1125 termiosp->c_cflag |= CS8;
1126 /* XXX - what about 8 bits plus parity? */
1127 else {
1128 switch (csp->t_flags & (O_EVENP|O_ODDP)) {
1130 case 0:
1131 termiosp->c_iflag |= ISTRIP;
1132 termiosp->c_cflag |= CS8;
1133 break;
1135 case O_EVENP:
1136 termiosp->c_iflag |= INPCK|ISTRIP;
1137 termiosp->c_cflag |= CS7|PARENB;
1138 break;
1140 case O_ODDP:
1141 termiosp->c_iflag |= INPCK|ISTRIP;
1142 termiosp->c_cflag |= CS7|PARENB|PARODD;
1143 break;
1145 case O_EVENP|O_ODDP:
1146 termiosp->c_iflag |= ISTRIP;
1147 termiosp->c_cflag |= CS7|PARENB;
1148 break;
1151 if (!(csp->t_xflags & NOPOST))
1152 termiosp->c_oflag |= OPOST;
1154 termiosp->c_lflag |= IEXTEN;
1155 if (!(csp->t_xflags & NOISIG))
1156 termiosp->c_lflag |= ISIG;
1157 if (!(csp->t_flags & O_CBREAK))
1158 termiosp->c_lflag |= ICANON;
1159 if (csp->t_flags & O_CTLECH)
1160 termiosp->c_lflag |= ECHOCTL;
1162 switch (csp->t_flags & O_TBDELAY) {
1164 case O_TAB1:
1165 termiosp->c_oflag |= TAB1;
1166 break;
1168 case O_TAB2:
1169 termiosp->c_oflag |= TAB2;
1170 break;
1172 case O_XTABS:
1173 termiosp->c_oflag |= TAB3;
1174 break;
1176 if (csp->t_flags & O_VTDELAY)
1177 termiosp->c_oflag |= FFDLY;
1178 if (csp->t_flags & O_BSDELAY)
1179 termiosp->c_oflag |= BSDLY;
1180 if (csp->t_flags & O_PRTERA)
1181 termiosp->c_lflag |= ECHOPRT;
1182 if (csp->t_flags & O_CRTERA)
1183 termiosp->c_lflag |= ECHOE;
1184 if (csp->t_flags & O_TOSTOP)
1185 termiosp->c_lflag |= TOSTOP;
1186 if (csp->t_flags & O_FLUSHO)
1187 termiosp->c_lflag |= FLUSHO;
1188 if (csp->t_flags & O_NOHANG)
1189 termiosp->c_cflag |= CLOCAL;
1190 if (csp->t_flags & O_CRTKIL)
1191 termiosp->c_lflag |= ECHOKE;
1192 if (csp->t_flags & O_PENDIN)
1193 termiosp->c_lflag |= PENDIN;
1194 if (!(csp->t_flags & O_DECCTQ))
1195 termiosp->c_iflag |= IXANY;
1196 if (csp->t_flags & O_NOFLSH)
1197 termiosp->c_lflag |= NOFLSH;
1198 if (termiosp->c_lflag & ICANON) {
1199 FROM_COMPAT_CHAR(termiosp->c_cc[VEOF], csp->t_eofc);
1200 FROM_COMPAT_CHAR(termiosp->c_cc[VEOL], csp->t_brkc);
1201 } else {
1202 termiosp->c_cc[VMIN] = 1;
1203 termiosp->c_cc[VTIME] = 0;
1207 #define TO_COMPAT_CHAR(to, from) { if ((to = from) == 0) to = (uchar_t)0377; }
1209 static void
1210 to_compat(struct termios *termiosp, compat_state_t *csp)
1212 csp->t_xflags &= (NOISIG|NOPOST);
1213 csp->t_ospeed = termiosp->c_cflag & CBAUD;
1214 csp->t_ispeed = (termiosp->c_cflag & CIBAUD) >> IBSHIFT;
1215 if (sgttyb_handling > 0) {
1216 if (termiosp->c_cflag & CBAUDEXT)
1217 csp->t_ospeed += CBAUD + 1;
1218 if (termiosp->c_cflag & CIBAUDEXT)
1219 csp->t_ispeed += (CIBAUD >> IBSHIFT) + 1;
1221 if (csp->t_ispeed == 0)
1222 csp->t_ispeed = csp->t_ospeed;
1223 if ((termiosp->c_cflag & CSTOPB) && csp->t_ispeed != B110)
1224 csp->t_xflags |= STOPB;
1225 TO_COMPAT_CHAR(csp->t_erase, termiosp->c_cc[VERASE]);
1226 TO_COMPAT_CHAR(csp->t_kill, termiosp->c_cc[VKILL]);
1227 TO_COMPAT_CHAR(csp->t_intrc, termiosp->c_cc[VINTR]);
1228 TO_COMPAT_CHAR(csp->t_quitc, termiosp->c_cc[VQUIT]);
1229 TO_COMPAT_CHAR(csp->t_startc, termiosp->c_cc[VSTART]);
1230 TO_COMPAT_CHAR(csp->t_stopc, termiosp->c_cc[VSTOP]);
1231 TO_COMPAT_CHAR(csp->t_suspc, termiosp->c_cc[VSUSP]);
1232 TO_COMPAT_CHAR(csp->t_dsuspc, termiosp->c_cc[VDSUSP]);
1233 TO_COMPAT_CHAR(csp->t_rprntc, termiosp->c_cc[VREPRINT]);
1234 TO_COMPAT_CHAR(csp->t_flushc, termiosp->c_cc[VDISCARD]);
1235 TO_COMPAT_CHAR(csp->t_werasc, termiosp->c_cc[VWERASE]);
1236 TO_COMPAT_CHAR(csp->t_lnextc, termiosp->c_cc[VLNEXT]);
1237 csp->t_flags &= (O_CTLECH|O_LITOUT|O_PASS8|O_ODDP|O_EVENP);
1238 if (termiosp->c_iflag & IXOFF)
1239 csp->t_flags |= O_TANDEM;
1240 if (!(termiosp->c_iflag &
1241 (IMAXBEL|BRKINT|IGNPAR|PARMRK|INPCK|ISTRIP|
1242 INLCR|IGNCR|ICRNL|IUCLC|IXON)) &&
1243 !(termiosp->c_oflag & OPOST) &&
1244 (termiosp->c_cflag & (CSIZE|PARENB)) == CS8 &&
1245 !(termiosp->c_lflag & (ISIG|ICANON|XCASE|IEXTEN)))
1246 csp->t_flags |= O_RAW;
1247 else {
1248 if (!(termiosp->c_iflag & IXON)) {
1249 csp->t_startc = (uchar_t)0377;
1250 csp->t_stopc = (uchar_t)0377;
1252 if ((termiosp->c_cflag & (CSIZE|PARENB)) == CS8 &&
1253 !(termiosp->c_oflag & OPOST))
1254 csp->t_flags |= O_LITOUT;
1255 else {
1256 csp->t_flags &= ~O_LITOUT;
1257 if ((termiosp->c_cflag & (CSIZE|PARENB)) == CS8) {
1258 if (!(termiosp->c_iflag & ISTRIP))
1259 csp->t_flags |= O_PASS8;
1260 } else {
1261 csp->t_flags &= ~(O_ODDP|O_EVENP|O_PASS8);
1262 if (termiosp->c_cflag & PARODD)
1263 csp->t_flags |= O_ODDP;
1264 else if (termiosp->c_iflag & INPCK)
1265 csp->t_flags |= O_EVENP;
1266 else
1267 csp->t_flags |= O_ODDP|O_EVENP;
1269 if (!(termiosp->c_oflag & OPOST))
1270 csp->t_xflags |= NOPOST;
1271 else
1272 csp->t_xflags &= ~NOPOST;
1274 if (!(termiosp->c_lflag & ISIG))
1275 csp->t_xflags |= NOISIG;
1276 else
1277 csp->t_xflags &= ~NOISIG;
1278 if (!(termiosp->c_lflag & ICANON))
1279 csp->t_flags |= O_CBREAK;
1280 if (termiosp->c_lflag & ECHOCTL)
1281 csp->t_flags |= O_CTLECH;
1282 else
1283 csp->t_flags &= ~O_CTLECH;
1285 if (termiosp->c_oflag & OLCUC)
1286 csp->t_flags |= O_LCASE;
1287 if (termiosp->c_lflag&ECHO)
1288 csp->t_flags |= O_ECHO;
1289 if (termiosp->c_oflag & ONLCR) {
1290 csp->t_flags |= O_CRMOD;
1291 switch (termiosp->c_oflag & CRDLY) {
1293 case CR2:
1294 csp->t_flags |= O_CR1;
1295 break;
1297 case CR3:
1298 csp->t_flags |= O_CR2;
1299 break;
1301 } else {
1302 if ((termiosp->c_oflag & CR1) &&
1303 (termiosp->c_oflag & ONLRET))
1304 csp->t_flags |= O_NL1; /* tty37 */
1306 if ((termiosp->c_oflag & ONLRET) && (termiosp->c_oflag & NL1))
1307 csp->t_flags |= O_NL2;
1308 switch (termiosp->c_oflag & TABDLY) {
1310 case TAB1:
1311 csp->t_flags |= O_TAB1;
1312 break;
1314 case TAB2:
1315 csp->t_flags |= O_TAB2;
1316 break;
1318 case XTABS:
1319 csp->t_flags |= O_XTABS;
1320 break;
1322 if (termiosp->c_oflag & FFDLY)
1323 csp->t_flags |= O_VTDELAY;
1324 if (termiosp->c_oflag & BSDLY)
1325 csp->t_flags |= O_BSDELAY;
1326 if (termiosp->c_lflag & ECHOPRT)
1327 csp->t_flags |= O_PRTERA;
1328 if (termiosp->c_lflag & ECHOE)
1329 csp->t_flags |= (O_CRTERA|O_CRTBS);
1330 if (termiosp->c_lflag & TOSTOP)
1331 csp->t_flags |= O_TOSTOP;
1332 if (termiosp->c_lflag & FLUSHO)
1333 csp->t_flags |= O_FLUSHO;
1334 if (termiosp->c_cflag & CLOCAL)
1335 csp->t_flags |= O_NOHANG;
1336 if (termiosp->c_lflag & ECHOKE)
1337 csp->t_flags |= O_CRTKIL;
1338 if (termiosp->c_lflag & PENDIN)
1339 csp->t_flags |= O_PENDIN;
1340 if (!(termiosp->c_iflag & IXANY))
1341 csp->t_flags |= O_DECCTQ;
1342 if (termiosp->c_lflag & NOFLSH)
1343 csp->t_flags |= O_NOFLSH;
1344 if (termiosp->c_lflag & ICANON) {
1345 TO_COMPAT_CHAR(csp->t_eofc, termiosp->c_cc[VEOF]);
1346 TO_COMPAT_CHAR(csp->t_brkc, termiosp->c_cc[VEOL]);
1347 } else {
1348 termiosp->c_cc[VMIN] = 1;
1349 termiosp->c_cc[VTIME] = 0;