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 #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>
28
29 /*
30 * The default (sane) set of termios values, unless
31 * otherwise set by the user.
32 */
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 */
38 {
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 }
57 };
58
59
60 static int termioval(char **, uint_t *, char *);
61
62 void
ttycommon_close(tty_common_t * tc)63 ttycommon_close(tty_common_t *tc)
64 {
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;
71
72 mp = tc->t_iocpending;
73 tc->t_iocpending = NULL;
74 mutex_exit(&tc->t_excl);
75 /*
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.
80 */
81 freemsg(mp);
82 } else
83 mutex_exit(&tc->t_excl);
84 }
85
86 /*
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.
90 */
91 void
ttycommon_qfull(tty_common_t * tc,queue_t * q)92 ttycommon_qfull(tty_common_t *tc, queue_t *q)
93 {
94 mblk_t *mp;
95
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);
101 }
102 }
103 } else {
104 flushq(q, FLUSHDATA);
105 (void) putnextctl1(q, M_FLUSH, FLUSHR);
106 }
107 }
108
109 /*
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.
119 */
120 size_t
ttycommon_ioctl(tty_common_t * tc,queue_t * q,mblk_t * mp,int * errorp)121 ttycommon_ioctl(tty_common_t *tc, queue_t *q, mblk_t *mp, int *errorp)
122 {
123 struct iocblk *iocp;
124 size_t ioctlrespsize;
125 mblk_t *tmp;
126
127 *errorp = 0; /* no error detected yet */
128
129 iocp = (struct iocblk *)mp->b_rptr;
130
131 if (iocp->ioc_count == TRANSPARENT) {
132 *errorp = -1; /* we don't understand it, maybe they do */
133 return (0);
134 }
135
136 switch (iocp->ioc_cmd) {
137
138 case TCSETSF:
139 /*
140 * Flush the driver's queue, and send an M_FLUSH upstream
141 * to flush everybody above us.
142 */
143 flushq(RD(q), FLUSHDATA);
144 (void) putnextctl1(RD(q), M_FLUSH, FLUSHR);
145 /* FALLTHROUGH */
146
147 case TCSETSW:
148 case TCSETS: {
149 struct termios *cb;
150
151 if (miocpullup(mp, sizeof (struct termios)) != 0) {
152 *errorp = -1;
153 break;
154 }
155
156 /*
157 * The only information we look at are the iflag word,
158 * the cflag word, and the start and stop characters.
159 */
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;
168 }
169
170 case TCSETAF:
171 /*
172 * Flush the driver's queue, and send an M_FLUSH upstream
173 * to flush everybody above us.
174 */
175 flushq(RD(q), FLUSHDATA);
176 (void) putnextctl1(RD(q), M_FLUSH, FLUSHR);
177 /* FALLTHROUGH */
178
179 case TCSETAW:
180 case TCSETA: {
181 struct termio *cb;
182
183 if (miocpullup(mp, sizeof (struct termio)) != 0) {
184 *errorp = -1;
185 break;
186 }
187
188 /*
189 * The only information we look at are the iflag word
190 * and the cflag word. Don't touch the unset portions.
191 */
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;
198 }
199
200 case TIOCSWINSZ: {
201 struct winsize *ws;
202
203 if (miocpullup(mp, sizeof (struct winsize)) != 0) {
204 *errorp = -1;
205 break;
206 }
207
208 /*
209 * If the window size changed, send a SIGWINCH.
210 */
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;
220 }
221
222 /*
223 * Prevent more opens.
224 */
225 case TIOCEXCL:
226 mutex_enter(&tc->t_excl);
227 tc->t_flags |= TS_XCLUDE;
228 mutex_exit(&tc->t_excl);
229 break;
230
231 /*
232 * Permit more opens.
233 */
234 case TIOCNXCL:
235 mutex_enter(&tc->t_excl);
236 tc->t_flags &= ~TS_XCLUDE;
237 mutex_exit(&tc->t_excl);
238 break;
239
240 /*
241 * Set or clear the "soft carrier" flag.
242 */
243 case TIOCSSOFTCAR:
244 if (miocpullup(mp, sizeof (int)) != 0) {
245 *errorp = -1;
246 break;
247 }
248
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;
256
257 /*
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.
261 */
262 case TIOCSTI: {
263 mblk_t *bp;
264
265 if (miocpullup(mp, sizeof (char)) != 0) {
266 *errorp = -1;
267 break;
268 }
269
270 /*
271 * Simulate typing of a character at the terminal.
272 */
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);
279 }
280 }
281 break;
282 }
283 }
284
285 /*
286 * Turn the ioctl message into an ioctl ACK message.
287 */
288 iocp->ioc_count = 0; /* no data returned unless we say so */
289 mp->b_datap->db_type = M_IOCACK;
290
291 switch (iocp->ioc_cmd) {
292
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:
304 /*
305 * We've done all the important work on these already;
306 * just reply with an ACK.
307 */
308 break;
309
310 case TCGETS: {
311 struct termios *cb;
312 mblk_t *datap;
313
314 if ((datap = allocb(sizeof (struct termios),
315 BPRI_HI)) == NULL) {
316 ioctlrespsize = sizeof (struct termios);
317 goto allocfailure;
318 }
319 cb = (struct termios *)datap->b_wptr;
320 /*
321 * The only information we supply is the cflag word.
322 * Our copy of the iflag word is just that, a copy.
323 */
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;
332 }
333
334 case TCGETA: {
335 struct termio *cb;
336 mblk_t *datap;
337
338 if ((datap = allocb(sizeof (struct termio), BPRI_HI)) == NULL) {
339 ioctlrespsize = sizeof (struct termio);
340 goto allocfailure;
341 }
342
343 cb = (struct termio *)datap->b_wptr;
344 /*
345 * The only information we supply is the cflag word.
346 * Our copy of the iflag word is just that, a copy.
347 */
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;
356 }
357
358 /*
359 * Get the "soft carrier" flag.
360 */
361 case TIOCGSOFTCAR: {
362 mblk_t *datap;
363
364 if ((datap = allocb(sizeof (int), BPRI_HI)) == NULL) {
365 ioctlrespsize = sizeof (int);
366 goto allocfailure;
367 }
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;
378 }
379
380 case TIOCGWINSZ: {
381 mblk_t *datap;
382
383 if ((datap = allocb(sizeof (struct winsize),
384 BPRI_HI)) == NULL) {
385 ioctlrespsize = sizeof (struct winsize);
386 goto allocfailure;
387 }
388 /*
389 * Return the current size.
390 */
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;
398 }
399
400 default:
401 *errorp = -1; /* we don't understand it, maybe they do */
402 break;
403 }
404 return (0);
405
406 allocfailure:
407
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);
412 /*
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.
418 */
419 if (tmp != NULL)
420 freemsg(tmp);
421 return (ioctlrespsize);
422 }
423
424 #define NFIELDS 21 /* 16 control characters + 4 sets of modes */
425
426 /*
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.
435 */
436
437 void
ttyinit()438 ttyinit()
439 {
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;
448
449
450 /*
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.
459 */
460 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, ddi_root_node(), 0,
461 property, (char ***)&modesp, &len) != DDI_PROP_SUCCESS) {
462
463 if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL) {
464 cmn_err(CE_PANIC,
465 "ttyinit: Can't find options node!\n");
466 }
467 /*
468 * Create the property.
469 */
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);
475 }
476 return;
477 }
478
479 /*
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.
483 */
484 bzero(&new_termios, sizeof (struct termios));
485 tp = &new_termios;
486 cp = *modesp;
487 for (i = 0; i < NFIELDS; i++) {
488 /*
489 * Check for bad field/string.
490 */
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;
497 }
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;
513 }
514 }
515 if ((dip = ddi_find_devinfo("options", -1, 0)) == NULL) {
516 cmn_err(CE_PANIC, "ttyinit: Can't find options node!\n");
517 }
518
519 /*
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.
525 */
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);
530 }
531 /*
532 * Store the new defaults. Since, this property was
533 * autoconfig'ed, we must use e_ddi_prop_update_byte_array().
534 */
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);
539 }
540 ddi_prop_free(modesp);
541 }
542
543 /*
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.
548 */
549
550 static int
termioval(char ** sp,uint_t * valp,char * ep)551 termioval(char **sp, uint_t *valp, char *ep)
552 {
553 char *s = *sp;
554 uint_t digit;
555
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;
571 }
572 /*
573 * Null string or empty field.
574 */
575 if (s == *sp)
576 return (-1);
577
578 if (s < ep && *s == ':')
579 s++;
580
581 *sp = s;
582 return (0);
583 }
584