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