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