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 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 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 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 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 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