bmake-ify mega_sas
[unleashed.git] / usr / src / uts / common / io / tty_common.c
blobc967af2c5e8162e56797c78300f64b0293fb0229
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,
56 CERASE2
61 static int termioval(char **, uint_t *, char *);
63 void
64 ttycommon_close(tty_common_t *tc)
66 mutex_enter(&tc->t_excl);
67 tc->t_flags &= ~TS_XCLUDE;
68 tc->t_readq = NULL;
69 tc->t_writeq = NULL;
70 if (tc->t_iocpending != NULL) {
71 mblk_t *mp;
73 mp = tc->t_iocpending;
74 tc->t_iocpending = NULL;
75 mutex_exit(&tc->t_excl);
77 * We were holding an "ioctl" response pending the
78 * availability of an "mblk" to hold data to be passed up;
79 * another "ioctl" came through, which means that "ioctl"
80 * must have timed out or been aborted.
82 freemsg(mp);
83 } else
84 mutex_exit(&tc->t_excl);
88 * A "line discipline" module's queue is full.
89 * Check whether IMAXBEL is set; if so, output a ^G, otherwise send an M_FLUSH
90 * upstream flushing all the read queues.
92 void
93 ttycommon_qfull(tty_common_t *tc, queue_t *q)
95 mblk_t *mp;
97 if (tc->t_iflag & IMAXBEL) {
98 if (canput(WR(q))) {
99 if ((mp = allocb(1, BPRI_HI)) != NULL) {
100 *mp->b_wptr++ = CTRL('g');
101 (void) putq(WR(q), mp);
104 } else {
105 flushq(q, FLUSHDATA);
106 (void) putnextctl1(q, M_FLUSH, FLUSHR);
111 * Process an "ioctl" message sent down to us, and return a reply message,
112 * even if we don't understand the "ioctl". Our client may want to use
113 * that reply message for its own purposes if we don't understand it but
114 * they do, and may want to modify it if we both understand it but they
115 * understand it better than we do.
116 * If the "ioctl" reply requires additional data to be passed up to the
117 * caller, and we cannot allocate an mblk to hold the data, we return the
118 * amount of data to be sent, so that our caller can do a "bufcall" and try
119 * again later; otherwise, we return 0.
121 size_t
122 ttycommon_ioctl(tty_common_t *tc, queue_t *q, mblk_t *mp, int *errorp)
124 struct iocblk *iocp;
125 size_t ioctlrespsize;
126 mblk_t *tmp;
128 *errorp = 0; /* no error detected yet */
130 iocp = (struct iocblk *)mp->b_rptr;
132 if (iocp->ioc_count == TRANSPARENT) {
133 *errorp = -1; /* we don't understand it, maybe they do */
134 return (0);
137 switch (iocp->ioc_cmd) {
139 case TCSETSF:
141 * Flush the driver's queue, and send an M_FLUSH upstream
142 * to flush everybody above us.
144 flushq(RD(q), FLUSHDATA);
145 (void) putnextctl1(RD(q), M_FLUSH, FLUSHR);
146 /* FALLTHROUGH */
148 case TCSETSW:
149 case TCSETS: {
150 struct termios *cb;
152 if (miocpullup(mp, sizeof (struct termios)) != 0) {
153 *errorp = -1;
154 break;
158 * The only information we look at are the iflag word,
159 * the cflag word, and the start and stop characters.
161 cb = (struct termios *)mp->b_cont->b_rptr;
162 mutex_enter(&tc->t_excl);
163 tc->t_iflag = cb->c_iflag;
164 tc->t_cflag = cb->c_cflag;
165 tc->t_stopc = cb->c_cc[VSTOP];
166 tc->t_startc = cb->c_cc[VSTART];
167 mutex_exit(&tc->t_excl);
168 break;
171 case TCSETAF:
173 * Flush the driver's queue, and send an M_FLUSH upstream
174 * to flush everybody above us.
176 flushq(RD(q), FLUSHDATA);
177 (void) putnextctl1(RD(q), M_FLUSH, FLUSHR);
178 /* FALLTHROUGH */
180 case TCSETAW:
181 case TCSETA: {
182 struct termio *cb;
184 if (miocpullup(mp, sizeof (struct termio)) != 0) {
185 *errorp = -1;
186 break;
190 * The only information we look at are the iflag word
191 * and the cflag word. Don't touch the unset portions.
193 cb = (struct termio *)mp->b_cont->b_rptr;
194 mutex_enter(&tc->t_excl);
195 tc->t_iflag = (tc->t_iflag & 0xffff0000 | cb->c_iflag);
196 tc->t_cflag = (tc->t_cflag & 0xffff0000 | cb->c_cflag);
197 mutex_exit(&tc->t_excl);
198 break;
201 case TIOCSWINSZ: {
202 struct winsize *ws;
204 if (miocpullup(mp, sizeof (struct winsize)) != 0) {
205 *errorp = -1;
206 break;
210 * If the window size changed, send a SIGWINCH.
212 ws = (struct winsize *)mp->b_cont->b_rptr;
213 mutex_enter(&tc->t_excl);
214 if (bcmp(&tc->t_size, ws, sizeof (struct winsize)) != 0) {
215 tc->t_size = *ws;
216 mutex_exit(&tc->t_excl);
217 (void) putnextctl1(RD(q), M_PCSIG, SIGWINCH);
218 } else
219 mutex_exit(&tc->t_excl);
220 break;
224 * Prevent more opens.
226 case TIOCEXCL:
227 mutex_enter(&tc->t_excl);
228 tc->t_flags |= TS_XCLUDE;
229 mutex_exit(&tc->t_excl);
230 break;
233 * Permit more opens.
235 case TIOCNXCL:
236 mutex_enter(&tc->t_excl);
237 tc->t_flags &= ~TS_XCLUDE;
238 mutex_exit(&tc->t_excl);
239 break;
242 * Set or clear the "soft carrier" flag.
244 case TIOCSSOFTCAR:
245 if (miocpullup(mp, sizeof (int)) != 0) {
246 *errorp = -1;
247 break;
250 mutex_enter(&tc->t_excl);
251 if (*(int *)mp->b_cont->b_rptr)
252 tc->t_flags |= TS_SOFTCAR;
253 else
254 tc->t_flags &= ~TS_SOFTCAR;
255 mutex_exit(&tc->t_excl);
256 break;
259 * The permission checking has already been done at the stream
260 * head, since it has to be done in the context of the process
261 * doing the call.
263 case TIOCSTI: {
264 mblk_t *bp;
266 if (miocpullup(mp, sizeof (char)) != 0) {
267 *errorp = -1;
268 break;
272 * Simulate typing of a character at the terminal.
274 if ((bp = allocb(1, BPRI_MED)) != NULL) {
275 if (!canput(tc->t_readq->q_next))
276 freemsg(bp);
277 else {
278 *bp->b_wptr++ = *mp->b_cont->b_rptr;
279 putnext(tc->t_readq, bp);
282 break;
287 * Turn the ioctl message into an ioctl ACK message.
289 iocp->ioc_count = 0; /* no data returned unless we say so */
290 mp->b_datap->db_type = M_IOCACK;
292 switch (iocp->ioc_cmd) {
294 case TCSETSF:
295 case TCSETSW:
296 case TCSETS:
297 case TCSETAF:
298 case TCSETAW:
299 case TCSETA:
300 case TIOCSWINSZ:
301 case TIOCEXCL:
302 case TIOCNXCL:
303 case TIOCSSOFTCAR:
304 case TIOCSTI:
306 * We've done all the important work on these already;
307 * just reply with an ACK.
309 break;
311 case TCGETS: {
312 struct termios *cb;
313 mblk_t *datap;
315 if ((datap = allocb(sizeof (struct termios),
316 BPRI_HI)) == NULL) {
317 ioctlrespsize = sizeof (struct termios);
318 goto allocfailure;
320 cb = (struct termios *)datap->b_wptr;
322 * The only information we supply is the cflag word.
323 * Our copy of the iflag word is just that, a copy.
325 bzero(cb, sizeof (struct termios));
326 cb->c_cflag = tc->t_cflag;
327 datap->b_wptr += sizeof (struct termios);
328 iocp->ioc_count = sizeof (struct termios);
329 if (mp->b_cont != NULL)
330 freemsg(mp->b_cont);
331 mp->b_cont = datap;
332 break;
335 case TCGETA: {
336 struct termio *cb;
337 mblk_t *datap;
339 if ((datap = allocb(sizeof (struct termio), BPRI_HI)) == NULL) {
340 ioctlrespsize = sizeof (struct termio);
341 goto allocfailure;
344 cb = (struct termio *)datap->b_wptr;
346 * The only information we supply is the cflag word.
347 * Our copy of the iflag word is just that, a copy.
349 bzero(cb, sizeof (struct termio));
350 cb->c_cflag = tc->t_cflag;
351 datap->b_wptr += sizeof (struct termio);
352 iocp->ioc_count = sizeof (struct termio);
353 if (mp->b_cont != NULL)
354 freemsg(mp->b_cont);
355 mp->b_cont = datap;
356 break;
360 * Get the "soft carrier" flag.
362 case TIOCGSOFTCAR: {
363 mblk_t *datap;
365 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
366 ioctlrespsize = sizeof (int);
367 goto allocfailure;
369 if (tc->t_flags & TS_SOFTCAR)
370 *(int *)datap->b_wptr = 1;
371 else
372 *(int *)datap->b_wptr = 0;
373 datap->b_wptr += sizeof (int);
374 iocp->ioc_count = sizeof (int);
375 if (mp->b_cont != NULL)
376 freemsg(mp->b_cont);
377 mp->b_cont = datap;
378 break;
381 case TIOCGWINSZ: {
382 mblk_t *datap;
384 if ((datap = allocb(sizeof (struct winsize),
385 BPRI_HI)) == NULL) {
386 ioctlrespsize = sizeof (struct winsize);
387 goto allocfailure;
390 * Return the current size.
392 *(struct winsize *)datap->b_wptr = tc->t_size;
393 datap->b_wptr += sizeof (struct winsize);
394 iocp->ioc_count = sizeof (struct winsize);
395 if (mp->b_cont != NULL)
396 freemsg(mp->b_cont);
397 mp->b_cont = datap;
398 break;
401 default:
402 *errorp = -1; /* we don't understand it, maybe they do */
403 break;
405 return (0);
407 allocfailure:
409 mutex_enter(&tc->t_excl);
410 tmp = tc->t_iocpending;
411 tc->t_iocpending = mp; /* hold this ioctl */
412 mutex_exit(&tc->t_excl);
414 * We needed to allocate something to handle this "ioctl", but
415 * couldn't; save this "ioctl" and arrange to get called back when
416 * it's more likely that we can get what we need.
417 * If there's already one being saved, throw it out, since it
418 * must have timed out.
420 if (tmp != NULL)
421 freemsg(tmp);
422 return (ioctlrespsize);
425 #define NFIELDS 22 /* 18 control characters + 4 sets of modes */
428 * Init routine run from main at boot time.
429 * Creates a property in the "options" node that is
430 * the default set of termios modes upon driver open.
431 * If the property already existed, then it was
432 * defined in the options.conf file. In this case we
433 * need to convert this string (stty -g style) to an
434 * actual termios structure and store the new property
435 * value.
438 void
439 ttyinit()
441 dev_info_t *dip;
442 struct termios new_termios;
443 struct termios *tp;
444 char *property = "ttymodes";
445 char **modesp, *cp;
446 int i;
447 uint_t val;
448 uint_t len;
452 * If the termios defaults were NOT set up by the
453 * user via the options.conf file, create it using the
454 * "sane" set of termios modes.
455 * Note that if the property had been created via the
456 * options.conf file, it would have been created as
457 * a string property. Since we would like to store
458 * a structure (termios) in this property, we need
459 * to change the property type to byte array.
461 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, ddi_root_node(), 0,
462 property, (char ***)&modesp, &len) != DDI_PROP_SUCCESS) {
464 if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL) {
465 cmn_err(CE_PANIC,
466 "ttyinit: Can't find options node!\n");
469 * Create the property.
471 if (ddi_prop_update_byte_array(DDI_DEV_T_NONE, dip,
472 property, (uchar_t *)&default_termios,
473 sizeof (struct termios)) != DDI_PROP_SUCCESS) {
474 cmn_err(CE_PANIC, "ttyinit: can't create %s property\n",
475 property);
477 return;
481 * This property was already set in the options.conf
482 * file. We must convert it from a "stty -g" string
483 * to an actual termios structure.
485 bzero(&new_termios, sizeof (struct termios));
486 tp = &new_termios;
487 cp = *modesp;
488 for (i = 0; i < NFIELDS; i++) {
490 * Check for bad field/string.
492 if (termioval(&cp, &val, *modesp+strlen(*modesp)) == -1) {
493 cmn_err(CE_WARN,
494 "ttyinit: property '%s' %s\n", property,
495 "set incorrectly, using sane value");
496 tp = &default_termios;
497 break;
499 switch (i) {
500 case 0:
501 new_termios.c_iflag = (tcflag_t)val;
502 break;
503 case 1:
504 new_termios.c_oflag = (tcflag_t)val;
505 break;
506 case 2:
507 new_termios.c_cflag = (tcflag_t)val;
508 break;
509 case 3:
510 new_termios.c_lflag = (tcflag_t)val;
511 break;
512 default:
513 new_termios.c_cc[i - 4] = (cc_t)val;
516 if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL) {
517 cmn_err(CE_PANIC, "ttyinit: Can't find options node!\n");
521 * We need to create ttymode property as a byte array
522 * since it will be interpreted as a termios struct.
523 * The property was created as a string by default.
524 * So remove the old property and add the new one -
525 * otherwise we end up with two ttymodes properties.
527 if (e_ddi_prop_remove(DDI_DEV_T_NONE, dip, property)
528 != DDI_PROP_SUCCESS) {
529 cmn_err(CE_WARN, "ttyinit: cannot remove '%s' property\n",
530 property);
533 * Store the new defaults. Since, this property was
534 * autoconfig'ed, we must use e_ddi_prop_update_byte_array().
536 if (e_ddi_prop_update_byte_array(DDI_DEV_T_NONE, dip, property,
537 (uchar_t *)tp, sizeof (struct termios)) != DDI_PROP_SUCCESS) {
538 cmn_err(CE_PANIC, "ttyinit: cannot modify '%s' property\n",
539 property);
541 ddi_prop_free(modesp);
545 * Convert hex string representation of termios field
546 * to a uint_t. Increments string pointer to the next
547 * field, and assigns value. Returns -1 if no more fields
548 * or an error.
551 static int
552 termioval(char **sp, uint_t *valp, char *ep)
554 char *s = *sp;
555 uint_t digit;
557 if (s == 0)
558 return (-1);
559 *valp = 0;
560 while (s < ep) {
561 if (*s >= '0' && *s <= '9')
562 digit = *s++ - '0';
563 else if (*s >= 'a' && *s <= 'f')
564 digit = *s++ - 'a' + 10;
565 else if (*s >= 'A' && *s <= 'F')
566 digit = *s++ - 'A' + 10;
567 else if (*s == ':' || *s == '\0')
568 break;
569 else
570 return (-1);
571 *valp = (*valp * 16) + digit;
574 * Null string or empty field.
576 if (s == *sp)
577 return (-1);
579 if (s < ep && *s == ':')
580 s++;
582 *sp = s;
583 return (0);