6324 Add an `ndp' tool for manipulating the neighbors table
[illumos-gate.git] / usr / src / uts / common / io / tty_common.c
blob0ad4a3b25aee66035b154ecc497264664135c5c6
1 /*
2 * Copyright 2005 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
6 /*
7 * Copyright (c) 1983 Regents of the University of California.
8 * All rights reserved. The Berkeley software License Agreement
9 * specifies the terms and conditions for redistribution.
12 #include <sys/types.h>
13 #include <sys/param.h>
14 #include <sys/signal.h>
15 #include <sys/systm.h>
16 #include <sys/termio.h>
17 #include <sys/ttold.h>
18 #include <sys/stropts.h>
19 #include <sys/stream.h>
20 #include <sys/strsubr.h>
21 #include <sys/strsun.h>
22 #include <sys/tty.h>
23 #include <sys/kmem.h>
24 #include <sys/errno.h>
25 #include <sys/ddi.h>
26 #include <sys/sunddi.h>
27 #include <sys/esunddi.h>
30 * The default (sane) set of termios values, unless
31 * otherwise set by the user.
33 static struct termios default_termios = {
34 BRKINT|ICRNL|IXON|IMAXBEL, /* c_iflag */
35 OPOST|ONLCR|TAB3, /* c_oflag */
36 B9600|CS8|CREAD, /* c_cflag */
37 ISIG|ICANON|IEXTEN|ECHO|ECHOK|ECHOE|ECHOKE|ECHOCTL, /* c_lflag */
39 CINTR,
40 CQUIT,
41 CERASE,
42 CKILL,
43 CEOF,
44 CEOL,
45 CEOL2,
46 CNSWTCH,
47 CSTART,
48 CSTOP,
49 CSUSP,
50 CDSUSP,
51 CRPRNT,
52 CFLUSH,
53 CWERASE,
54 CLNEXT,
55 CSTATUS
60 static int termioval(char **, uint_t *, char *);
62 void
63 ttycommon_close(tty_common_t *tc)
65 mutex_enter(&tc->t_excl);
66 tc->t_flags &= ~TS_XCLUDE;
67 tc->t_readq = NULL;
68 tc->t_writeq = NULL;
69 if (tc->t_iocpending != NULL) {
70 mblk_t *mp;
72 mp = tc->t_iocpending;
73 tc->t_iocpending = NULL;
74 mutex_exit(&tc->t_excl);
76 * We were holding an "ioctl" response pending the
77 * availability of an "mblk" to hold data to be passed up;
78 * another "ioctl" came through, which means that "ioctl"
79 * must have timed out or been aborted.
81 freemsg(mp);
82 } else
83 mutex_exit(&tc->t_excl);
87 * A "line discipline" module's queue is full.
88 * Check whether IMAXBEL is set; if so, output a ^G, otherwise send an M_FLUSH
89 * upstream flushing all the read queues.
91 void
92 ttycommon_qfull(tty_common_t *tc, queue_t *q)
94 mblk_t *mp;
96 if (tc->t_iflag & IMAXBEL) {
97 if (canput(WR(q))) {
98 if ((mp = allocb(1, BPRI_HI)) != NULL) {
99 *mp->b_wptr++ = CTRL('g');
100 (void) putq(WR(q), mp);
103 } else {
104 flushq(q, FLUSHDATA);
105 (void) putnextctl1(q, M_FLUSH, FLUSHR);
110 * Process an "ioctl" message sent down to us, and return a reply message,
111 * even if we don't understand the "ioctl". Our client may want to use
112 * that reply message for its own purposes if we don't understand it but
113 * they do, and may want to modify it if we both understand it but they
114 * understand it better than we do.
115 * If the "ioctl" reply requires additional data to be passed up to the
116 * caller, and we cannot allocate an mblk to hold the data, we return the
117 * amount of data to be sent, so that our caller can do a "bufcall" and try
118 * again later; otherwise, we return 0.
120 size_t
121 ttycommon_ioctl(tty_common_t *tc, queue_t *q, mblk_t *mp, int *errorp)
123 struct iocblk *iocp;
124 size_t ioctlrespsize;
125 mblk_t *tmp;
127 *errorp = 0; /* no error detected yet */
129 iocp = (struct iocblk *)mp->b_rptr;
131 if (iocp->ioc_count == TRANSPARENT) {
132 *errorp = -1; /* we don't understand it, maybe they do */
133 return (0);
136 switch (iocp->ioc_cmd) {
138 case TCSETSF:
140 * Flush the driver's queue, and send an M_FLUSH upstream
141 * to flush everybody above us.
143 flushq(RD(q), FLUSHDATA);
144 (void) putnextctl1(RD(q), M_FLUSH, FLUSHR);
145 /* FALLTHROUGH */
147 case TCSETSW:
148 case TCSETS: {
149 struct termios *cb;
151 if (miocpullup(mp, sizeof (struct termios)) != 0) {
152 *errorp = -1;
153 break;
157 * The only information we look at are the iflag word,
158 * the cflag word, and the start and stop characters.
160 cb = (struct termios *)mp->b_cont->b_rptr;
161 mutex_enter(&tc->t_excl);
162 tc->t_iflag = cb->c_iflag;
163 tc->t_cflag = cb->c_cflag;
164 tc->t_stopc = cb->c_cc[VSTOP];
165 tc->t_startc = cb->c_cc[VSTART];
166 mutex_exit(&tc->t_excl);
167 break;
170 case TCSETAF:
172 * Flush the driver's queue, and send an M_FLUSH upstream
173 * to flush everybody above us.
175 flushq(RD(q), FLUSHDATA);
176 (void) putnextctl1(RD(q), M_FLUSH, FLUSHR);
177 /* FALLTHROUGH */
179 case TCSETAW:
180 case TCSETA: {
181 struct termio *cb;
183 if (miocpullup(mp, sizeof (struct termio)) != 0) {
184 *errorp = -1;
185 break;
189 * The only information we look at are the iflag word
190 * and the cflag word. Don't touch the unset portions.
192 cb = (struct termio *)mp->b_cont->b_rptr;
193 mutex_enter(&tc->t_excl);
194 tc->t_iflag = (tc->t_iflag & 0xffff0000 | cb->c_iflag);
195 tc->t_cflag = (tc->t_cflag & 0xffff0000 | cb->c_cflag);
196 mutex_exit(&tc->t_excl);
197 break;
200 case TIOCSWINSZ: {
201 struct winsize *ws;
203 if (miocpullup(mp, sizeof (struct winsize)) != 0) {
204 *errorp = -1;
205 break;
209 * If the window size changed, send a SIGWINCH.
211 ws = (struct winsize *)mp->b_cont->b_rptr;
212 mutex_enter(&tc->t_excl);
213 if (bcmp(&tc->t_size, ws, sizeof (struct winsize)) != 0) {
214 tc->t_size = *ws;
215 mutex_exit(&tc->t_excl);
216 (void) putnextctl1(RD(q), M_PCSIG, SIGWINCH);
217 } else
218 mutex_exit(&tc->t_excl);
219 break;
223 * Prevent more opens.
225 case TIOCEXCL:
226 mutex_enter(&tc->t_excl);
227 tc->t_flags |= TS_XCLUDE;
228 mutex_exit(&tc->t_excl);
229 break;
232 * Permit more opens.
234 case TIOCNXCL:
235 mutex_enter(&tc->t_excl);
236 tc->t_flags &= ~TS_XCLUDE;
237 mutex_exit(&tc->t_excl);
238 break;
241 * Set or clear the "soft carrier" flag.
243 case TIOCSSOFTCAR:
244 if (miocpullup(mp, sizeof (int)) != 0) {
245 *errorp = -1;
246 break;
249 mutex_enter(&tc->t_excl);
250 if (*(int *)mp->b_cont->b_rptr)
251 tc->t_flags |= TS_SOFTCAR;
252 else
253 tc->t_flags &= ~TS_SOFTCAR;
254 mutex_exit(&tc->t_excl);
255 break;
258 * The permission checking has already been done at the stream
259 * head, since it has to be done in the context of the process
260 * doing the call.
262 case TIOCSTI: {
263 mblk_t *bp;
265 if (miocpullup(mp, sizeof (char)) != 0) {
266 *errorp = -1;
267 break;
271 * Simulate typing of a character at the terminal.
273 if ((bp = allocb(1, BPRI_MED)) != NULL) {
274 if (!canput(tc->t_readq->q_next))
275 freemsg(bp);
276 else {
277 *bp->b_wptr++ = *mp->b_cont->b_rptr;
278 putnext(tc->t_readq, bp);
281 break;
286 * Turn the ioctl message into an ioctl ACK message.
288 iocp->ioc_count = 0; /* no data returned unless we say so */
289 mp->b_datap->db_type = M_IOCACK;
291 switch (iocp->ioc_cmd) {
293 case TCSETSF:
294 case TCSETSW:
295 case TCSETS:
296 case TCSETAF:
297 case TCSETAW:
298 case TCSETA:
299 case TIOCSWINSZ:
300 case TIOCEXCL:
301 case TIOCNXCL:
302 case TIOCSSOFTCAR:
303 case TIOCSTI:
305 * We've done all the important work on these already;
306 * just reply with an ACK.
308 break;
310 case TCGETS: {
311 struct termios *cb;
312 mblk_t *datap;
314 if ((datap = allocb(sizeof (struct termios),
315 BPRI_HI)) == NULL) {
316 ioctlrespsize = sizeof (struct termios);
317 goto allocfailure;
319 cb = (struct termios *)datap->b_wptr;
321 * The only information we supply is the cflag word.
322 * Our copy of the iflag word is just that, a copy.
324 bzero(cb, sizeof (struct termios));
325 cb->c_cflag = tc->t_cflag;
326 datap->b_wptr += sizeof (struct termios);
327 iocp->ioc_count = sizeof (struct termios);
328 if (mp->b_cont != NULL)
329 freemsg(mp->b_cont);
330 mp->b_cont = datap;
331 break;
334 case TCGETA: {
335 struct termio *cb;
336 mblk_t *datap;
338 if ((datap = allocb(sizeof (struct termio), BPRI_HI)) == NULL) {
339 ioctlrespsize = sizeof (struct termio);
340 goto allocfailure;
343 cb = (struct termio *)datap->b_wptr;
345 * The only information we supply is the cflag word.
346 * Our copy of the iflag word is just that, a copy.
348 bzero(cb, sizeof (struct termio));
349 cb->c_cflag = tc->t_cflag;
350 datap->b_wptr += sizeof (struct termio);
351 iocp->ioc_count = sizeof (struct termio);
352 if (mp->b_cont != NULL)
353 freemsg(mp->b_cont);
354 mp->b_cont = datap;
355 break;
359 * Get the "soft carrier" flag.
361 case TIOCGSOFTCAR: {
362 mblk_t *datap;
364 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
365 ioctlrespsize = sizeof (int);
366 goto allocfailure;
368 if (tc->t_flags & TS_SOFTCAR)
369 *(int *)datap->b_wptr = 1;
370 else
371 *(int *)datap->b_wptr = 0;
372 datap->b_wptr += sizeof (int);
373 iocp->ioc_count = sizeof (int);
374 if (mp->b_cont != NULL)
375 freemsg(mp->b_cont);
376 mp->b_cont = datap;
377 break;
380 case TIOCGWINSZ: {
381 mblk_t *datap;
383 if ((datap = allocb(sizeof (struct winsize),
384 BPRI_HI)) == NULL) {
385 ioctlrespsize = sizeof (struct winsize);
386 goto allocfailure;
389 * Return the current size.
391 *(struct winsize *)datap->b_wptr = tc->t_size;
392 datap->b_wptr += sizeof (struct winsize);
393 iocp->ioc_count = sizeof (struct winsize);
394 if (mp->b_cont != NULL)
395 freemsg(mp->b_cont);
396 mp->b_cont = datap;
397 break;
400 default:
401 *errorp = -1; /* we don't understand it, maybe they do */
402 break;
404 return (0);
406 allocfailure:
408 mutex_enter(&tc->t_excl);
409 tmp = tc->t_iocpending;
410 tc->t_iocpending = mp; /* hold this ioctl */
411 mutex_exit(&tc->t_excl);
413 * We needed to allocate something to handle this "ioctl", but
414 * couldn't; save this "ioctl" and arrange to get called back when
415 * it's more likely that we can get what we need.
416 * If there's already one being saved, throw it out, since it
417 * must have timed out.
419 if (tmp != NULL)
420 freemsg(tmp);
421 return (ioctlrespsize);
424 #define NFIELDS 21 /* 16 control characters + 4 sets of modes */
427 * Init routine run from main at boot time.
428 * Creates a property in the "options" node that is
429 * the default set of termios modes upon driver open.
430 * If the property already existed, then it was
431 * defined in the options.conf file. In this case we
432 * need to convert this string (stty -g style) to an
433 * actual termios structure and store the new property
434 * value.
437 void
438 ttyinit()
440 dev_info_t *dip;
441 struct termios new_termios;
442 struct termios *tp;
443 char *property = "ttymodes";
444 char **modesp, *cp;
445 int i;
446 uint_t val;
447 uint_t len;
451 * If the termios defaults were NOT set up by the
452 * user via the options.conf file, create it using the
453 * "sane" set of termios modes.
454 * Note that if the property had been created via the
455 * options.conf file, it would have been created as
456 * a string property. Since we would like to store
457 * a structure (termios) in this property, we need
458 * to change the property type to byte array.
460 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, ddi_root_node(), 0,
461 property, (char ***)&modesp, &len) != DDI_PROP_SUCCESS) {
463 if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL) {
464 cmn_err(CE_PANIC,
465 "ttyinit: Can't find options node!\n");
468 * Create the property.
470 if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
471 property, (uchar_t *)&default_termios,
472 sizeof (struct termios)) != DDI_PROP_SUCCESS) {
473 cmn_err(CE_PANIC, "ttyinit: can't create %s property\n",
474 property);
476 return;
480 * This property was already set in the options.conf
481 * file. We must convert it from a "stty -g" string
482 * to an actual termios structure.
484 bzero(&new_termios, sizeof (struct termios));
485 tp = &new_termios;
486 cp = *modesp;
487 for (i = 0; i < NFIELDS; i++) {
489 * Check for bad field/string.
491 if (termioval(&cp, &val, *modesp+strlen(*modesp)) == -1) {
492 cmn_err(CE_WARN,
493 "ttyinit: property '%s' %s\n", property,
494 "set incorrectly, using sane value");
495 tp = &default_termios;
496 break;
498 switch (i) {
499 case 0:
500 new_termios.c_iflag = (tcflag_t)val;
501 break;
502 case 1:
503 new_termios.c_oflag = (tcflag_t)val;
504 break;
505 case 2:
506 new_termios.c_cflag = (tcflag_t)val;
507 break;
508 case 3:
509 new_termios.c_lflag = (tcflag_t)val;
510 break;
511 default:
512 new_termios.c_cc[i - 4] = (cc_t)val;
515 if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL) {
516 cmn_err(CE_PANIC, "ttyinit: Can't find options node!\n");
520 * We need to create ttymode property as a byte array
521 * since it will be interpreted as a termios struct.
522 * The property was created as a string by default.
523 * So remove the old property and add the new one -
524 * otherwise we end up with two ttymodes properties.
526 if (e_ddi_prop_remove(DDI_DEV_T_NONE, dip, property)
527 != DDI_PROP_SUCCESS) {
528 cmn_err(CE_WARN, "ttyinit: cannot remove '%s' property\n",
529 property);
532 * Store the new defaults. Since, this property was
533 * autoconfig'ed, we must use e_ddi_prop_update_byte_array().
535 if (e_ddi_prop_update_byte_array(DDI_DEV_T_NONE, dip, property,
536 (uchar_t *)tp, sizeof (struct termios)) != DDI_PROP_SUCCESS) {
537 cmn_err(CE_PANIC, "ttyinit: cannot modify '%s' property\n",
538 property);
540 ddi_prop_free(modesp);
544 * Convert hex string representation of termios field
545 * to a uint_t. Increments string pointer to the next
546 * field, and assigns value. Returns -1 if no more fields
547 * or an error.
550 static int
551 termioval(char **sp, uint_t *valp, char *ep)
553 char *s = *sp;
554 uint_t digit;
556 if (s == 0)
557 return (-1);
558 *valp = 0;
559 while (s < ep) {
560 if (*s >= '0' && *s <= '9')
561 digit = *s++ - '0';
562 else if (*s >= 'a' && *s <= 'f')
563 digit = *s++ - 'a' + 10;
564 else if (*s >= 'A' && *s <= 'F')
565 digit = *s++ - 'A' + 10;
566 else if (*s == ':' || *s == '\0')
567 break;
568 else
569 return (-1);
570 *valp = (*valp * 16) + digit;
573 * Null string or empty field.
575 if (s == *sp)
576 return (-1);
578 if (s < ep && *s == ':')
579 s++;
581 *sp = s;
582 return (0);