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