1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 23 /* All Rights Reserved */ 24 25 /* 26 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 /* 33 * Serial I/O driver for 82510/8250/16450/16550AF/16C554D chips. 34 * Modified as sparc keyboard/mouse driver. 35 */ 36 #define SU_REGISTER_FILE_NO 0 37 #define SU_REGOFFSET 0 38 #define SU_REGISTER_LEN 8 39 40 #include <sys/param.h> 41 #include <sys/types.h> 42 #include <sys/signal.h> 43 #include <sys/stream.h> 44 #include <sys/termio.h> 45 #include <sys/errno.h> 46 #include <sys/file.h> 47 #include <sys/cmn_err.h> 48 #include <sys/stropts.h> 49 #include <sys/strsubr.h> 50 #include <sys/strsun.h> 51 #include <sys/strtty.h> 52 #include <sys/debug.h> 53 #include <sys/kbio.h> 54 #include <sys/cred.h> 55 #include <sys/modctl.h> 56 #include <sys/stat.h> 57 #include <sys/consdev.h> 58 #include <sys/mkdev.h> 59 #include <sys/kmem.h> 60 #include <sys/cred.h> 61 #ifdef DEBUG 62 #include <sys/promif.h> 63 #endif 64 #include <sys/ddi.h> 65 #include <sys/sunddi.h> 66 #include <sys/sudev.h> 67 #include <sys/note.h> 68 #include <sys/timex.h> 69 #include <sys/policy.h> 70 71 #define async_stopc async_ttycommon.t_stopc 72 #define async_startc async_ttycommon.t_startc 73 74 #define ASY_INIT 1 75 #define ASY_NOINIT 0 76 77 #ifdef DEBUG 78 #define ASY_DEBUG_INIT 0x001 79 #define ASY_DEBUG_INPUT 0x002 80 #define ASY_DEBUG_EOT 0x004 81 #define ASY_DEBUG_CLOSE 0x008 82 #define ASY_DEBUG_HFLOW 0x010 83 #define ASY_DEBUG_PROCS 0x020 84 #define ASY_DEBUG_STATE 0x040 85 #define ASY_DEBUG_INTR 0x080 86 static int asydebug = 0; 87 #endif 88 static int su_log = 0; 89 90 int su_drain_check = 15000000; /* tunable: exit drain check time */ 91 92 static struct ppsclockev asy_ppsev; 93 94 static int max_asy_instance = -1; 95 static void *su_asycom; /* soft state asycom pointer */ 96 static void *su_asyncline; /* soft state asyncline pointer */ 97 static boolean_t abort_charseq_recognize(uchar_t ch); 98 99 static uint_t asysoftintr(caddr_t intarg); 100 static uint_t asyintr(caddr_t argasy); 101 102 /* The async interrupt entry points */ 103 static void async_txint(struct asycom *asy, uchar_t lsr); 104 static void async_rxint(struct asycom *asy, uchar_t lsr); 105 static void async_msint(struct asycom *asy); 106 static int async_softint(struct asycom *asy); 107 108 static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp, 109 boolean_t iswput); 110 static void async_reioctl(void *); 111 static void async_iocdata(queue_t *q, mblk_t *mp); 112 static void async_restart(void *); 113 static void async_start(struct asyncline *async); 114 static void async_nstart(struct asyncline *async, int mode); 115 static void async_resume(struct asyncline *async); 116 static int asy_program(struct asycom *asy, int mode); 117 118 static int asymctl(struct asycom *, int, int); 119 static int asytodm(int, int); 120 static int dmtoasy(int); 121 static void asycheckflowcontrol_hw(struct asycom *asy); 122 static boolean_t asycheckflowcontrol_sw(struct asycom *asy); 123 static void asy_ppsevent(struct asycom *asy, int msr); 124 125 extern kcondvar_t lbolt_cv; 126 extern int ddi_create_internal_pathname(dev_info_t *dip, char *name, 127 int spec_type, minor_t minor_num); 128 129 130 /* 131 * Baud rate table. Indexed by #defines found in sys/termios.h 132 */ 133 ushort_t asyspdtab[] = { 134 0, /* 0 baud rate */ 135 0x900, /* 50 baud rate */ 136 0x600, /* 75 baud rate */ 137 0x417, /* 110 baud rate (%0.026) */ 138 0x359, /* 134 baud rate (%0.058) */ 139 0x300, /* 150 baud rate */ 140 0x240, /* 200 baud rate */ 141 0x180, /* 300 baud rate */ 142 0x0c0, /* 600 baud rate */ 143 0x060, /* 1200 baud rate */ 144 0x040, /* 1800 baud rate */ 145 0x030, /* 2400 baud rate */ 146 0x018, /* 4800 baud rate */ 147 0x00c, /* 9600 baud rate */ 148 0x006, /* 19200 baud rate */ 149 0x003, /* 38400 baud rate */ 150 0x002, /* 57600 baud rate */ 151 0, /* 76800 baud rate - not supported */ 152 0x001, /* 115200 baud rate */ 153 0, /* 153600 baud rate - not supported */ 154 0x8002, /* 230400 baud rate - supported on specific platforms */ 155 0, /* 307200 baud rate - not supported */ 156 0x8001 /* 460800 baud rate - supported on specific platforms */ 157 }; 158 159 /* 160 * Number of speeds supported is the number of entries in 161 * the above table. 162 */ 163 #define N_SU_SPEEDS (sizeof (asyspdtab)/sizeof (ushort_t)) 164 165 /* 166 * Human-readable baud rate table. 167 * Indexed by #defines found in sys/termios.h 168 */ 169 int baudtable[] = { 170 0, /* 0 baud rate */ 171 50, /* 50 baud rate */ 172 75, /* 75 baud rate */ 173 110, /* 110 baud rate */ 174 134, /* 134 baud rate */ 175 150, /* 150 baud rate */ 176 200, /* 200 baud rate */ 177 300, /* 300 baud rate */ 178 600, /* 600 baud rate */ 179 1200, /* 1200 baud rate */ 180 1800, /* 1800 baud rate */ 181 2400, /* 2400 baud rate */ 182 4800, /* 4800 baud rate */ 183 9600, /* 9600 baud rate */ 184 19200, /* 19200 baud rate */ 185 38400, /* 38400 baud rate */ 186 57600, /* 57600 baud rate */ 187 76800, /* 76800 baud rate */ 188 115200, /* 115200 baud rate */ 189 153600, /* 153600 baud rate */ 190 230400, /* 230400 baud rate */ 191 307200, /* 307200 baud rate */ 192 460800 /* 460800 baud rate */ 193 }; 194 195 static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); 196 static int asyclose(queue_t *q, int flag); 197 static void asywput(queue_t *q, mblk_t *mp); 198 static void asyrsrv(queue_t *q); 199 200 struct module_info asy_info = { 201 0, 202 "su", 203 0, 204 INFPSZ, 205 32*4096, 206 4096 207 }; 208 209 static struct qinit asy_rint = { 210 putq, 211 (int (*)())asyrsrv, 212 asyopen, 213 asyclose, 214 NULL, 215 &asy_info, 216 NULL 217 }; 218 219 static struct qinit asy_wint = { 220 (int (*)())asywput, 221 NULL, 222 NULL, 223 NULL, 224 NULL, 225 &asy_info, 226 NULL 227 }; 228 229 struct streamtab asy_str_info = { 230 &asy_rint, 231 &asy_wint, 232 NULL, 233 NULL 234 }; 235 236 static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 237 void **result); 238 static int asyprobe(dev_info_t *); 239 static int asyattach(dev_info_t *, ddi_attach_cmd_t); 240 static int asydetach(dev_info_t *, ddi_detach_cmd_t); 241 242 static struct cb_ops cb_asy_ops = { 243 nodev, /* cb_open */ 244 nodev, /* cb_close */ 245 nodev, /* cb_strategy */ 246 nodev, /* cb_print */ 247 nodev, /* cb_dump */ 248 nodev, /* cb_read */ 249 nodev, /* cb_write */ 250 nodev, /* cb_ioctl */ 251 nodev, /* cb_devmap */ 252 nodev, /* cb_mmap */ 253 nodev, /* cb_segmap */ 254 nochpoll, /* cb_chpoll */ 255 ddi_prop_op, /* cb_prop_op */ 256 &asy_str_info, /* cb_stream */ 257 D_MP /* cb_flag */ 258 }; 259 260 struct dev_ops asy_ops = { 261 DEVO_REV, /* devo_rev */ 262 0, /* devo_refcnt */ 263 asyinfo, /* devo_getinfo */ 264 nulldev, /* devo_identify */ 265 asyprobe, /* devo_probe */ 266 asyattach, /* devo_attach */ 267 asydetach, /* devo_detach */ 268 nodev, /* devo_reset */ 269 &cb_asy_ops, /* devo_cb_ops */ 270 }; 271 272 /* 273 * Module linkage information for the kernel. 274 */ 275 276 static struct modldrv modldrv = { 277 &mod_driverops, /* Type of module. This one is a driver */ 278 "su driver %I%", 279 &asy_ops, /* driver ops */ 280 }; 281 282 static struct modlinkage modlinkage = { 283 MODREV_1, 284 &modldrv, 285 NULL 286 }; 287 288 int 289 _init(void) 290 { 291 int status; 292 293 status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom), 294 SU_INITIAL_SOFT_ITEMS); 295 if (status != 0) 296 return (status); 297 status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline), 298 SU_INITIAL_SOFT_ITEMS); 299 if (status != 0) { 300 ddi_soft_state_fini(&su_asycom); 301 return (status); 302 } 303 304 if ((status = mod_install(&modlinkage)) != 0) { 305 ddi_soft_state_fini(&su_asycom); 306 ddi_soft_state_fini(&su_asyncline); 307 } 308 309 return (status); 310 } 311 312 int 313 _fini(void) 314 { 315 int i; 316 317 i = mod_remove(&modlinkage); 318 if (i == 0) { 319 ddi_soft_state_fini(&su_asycom); 320 ddi_soft_state_fini(&su_asyncline); 321 } 322 323 return (i); 324 } 325 326 int 327 _info(struct modinfo *modinfop) 328 { 329 return (mod_info(&modlinkage, modinfop)); 330 } 331 332 static int 333 asyprobe(dev_info_t *devi) 334 { 335 int instance; 336 ddi_acc_handle_t handle; 337 uchar_t *addr; 338 ddi_device_acc_attr_t attr; 339 340 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 341 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 342 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 343 344 if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr, 345 SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) { 346 cmn_err(CE_WARN, "asyprobe regs map setup failed"); 347 return (DDI_PROBE_FAILURE); 348 } 349 #ifdef DEBUG 350 if (asydebug) 351 printf("Probe address mapped %p\n", (void *)addr); 352 #endif 353 354 /* 355 * Probe for the device: 356 * Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low. 357 * If bit 4 or 5 appears on inb() ISR, board is not there. 358 */ 359 if (ddi_get8(handle, addr+ISR) & 0x30) 360 return (DDI_PROBE_FAILURE); 361 instance = ddi_get_instance(devi); 362 if (max_asy_instance < instance) 363 max_asy_instance = instance; 364 ddi_regs_map_free(&handle); 365 366 return (DDI_PROBE_SUCCESS); /* hw is present */ 367 } 368 369 static int 370 asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) 371 { 372 register int instance; 373 struct asycom *asy; 374 struct asyncline *async; 375 char name[16]; 376 377 instance = ddi_get_instance(devi); /* find out which unit */ 378 379 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); 380 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); 381 382 switch (cmd) { 383 case DDI_DETACH: 384 break; 385 case DDI_SUSPEND: 386 /* grab both mutex locks */ 387 mutex_enter(asy->asy_excl); 388 mutex_enter(asy->asy_excl_hi); 389 if (asy->suspended) { 390 mutex_exit(asy->asy_excl_hi); 391 mutex_exit(asy->asy_excl); 392 return (DDI_SUCCESS); 393 } 394 asy->suspended = B_TRUE; 395 396 /* 397 * The quad UART ST16C554D, version D2 (made by EXAR) has an 398 * anomaly of generating spurious interrups when the ICR is 399 * loaded with zero. The workaround would be to read/write 400 * any register with DATA1 bit set to 0 before such write. 401 */ 402 if (asy->asy_hwtype == ASY16C554D) 403 OUTB(SPR, 0); 404 405 /* Disable further interrupts */ 406 OUTB(ICR, 0); 407 mutex_exit(asy->asy_excl_hi); 408 mutex_exit(asy->asy_excl); 409 return (DDI_SUCCESS); 410 411 default: 412 return (DDI_FAILURE); 413 } 414 415 #ifdef DEBUG 416 if (asydebug & ASY_DEBUG_INIT) 417 cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance, 418 asy->asy_hwtype == ASY82510 ? "82510" : 419 asy->asy_hwtype == ASY16550AF ? "16550AF" : 420 asy->asy_hwtype == ASY16C554D ? "16C554D" : 421 "8250"); 422 #endif 423 /* 424 * Before removing interrupts it is always better to disable 425 * interrupts if the chip gives a provision to disable the 426 * serial port interrupts. 427 */ 428 mutex_enter(asy->asy_excl); 429 mutex_enter(asy->asy_excl_hi); 430 /* disable interrupts, see EXAR bug */ 431 if (asy->asy_hwtype == ASY16C554D) 432 OUTB(SPR, 0); 433 OUTB(ICR, 0); 434 mutex_exit(asy->asy_excl_hi); 435 mutex_exit(asy->asy_excl); 436 437 /* remove minor device node(s) for this device */ 438 (void) sprintf(name, "%c", (instance+'a')); /* serial-port */ 439 ddi_remove_minor_node(devi, name); 440 (void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */ 441 ddi_remove_minor_node(devi, name); 442 443 mutex_destroy(asy->asy_excl); 444 mutex_destroy(asy->asy_excl_hi); 445 kmem_free(asy->asy_excl, sizeof (kmutex_t)); 446 kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); 447 cv_destroy(&async->async_flags_cv); 448 kstat_delete(asy->sukstat); 449 ddi_remove_intr(devi, 0, asy->asy_iblock); 450 ddi_regs_map_free(&asy->asy_handle); 451 ddi_remove_softintr(asy->asy_softintr_id); 452 mutex_destroy(asy->asy_soft_lock); 453 kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); 454 ddi_soft_state_free(su_asycom, instance); 455 ddi_soft_state_free(su_asyncline, instance); 456 return (DDI_SUCCESS); 457 } 458 459 static int 460 asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 461 { 462 register int instance; 463 struct asycom *asy; 464 struct asyncline *async; 465 char name[40]; 466 ddi_device_acc_attr_t attr; 467 enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR, 468 SOFTINTR, ASYINIT, KSTAT, MINORNODE }; 469 enum states state = EMPTY; 470 char *hwtype; 471 472 instance = ddi_get_instance(devi); /* find out which unit */ 473 474 /* cannot attach a device that has not been probed first */ 475 if (instance > max_asy_instance) 476 return (DDI_FAILURE); 477 478 if (cmd != DDI_RESUME) { 479 /* Allocate soft state space */ 480 if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) { 481 cmn_err(CE_WARN, "su%d: cannot allocate soft state", 482 instance); 483 goto error; 484 } 485 } 486 state = SOFTSTATE; 487 488 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); 489 490 if (asy == NULL) { 491 cmn_err(CE_WARN, "su%d: cannot get soft state", instance); 492 goto error; 493 } 494 495 switch (cmd) { 496 case DDI_ATTACH: 497 break; 498 case DDI_RESUME: { 499 struct asyncline *async; 500 501 /* grab both mutex locks */ 502 mutex_enter(asy->asy_excl); 503 mutex_enter(asy->asy_excl_hi); 504 if (!asy->suspended) { 505 mutex_exit(asy->asy_excl_hi); 506 mutex_exit(asy->asy_excl); 507 return (DDI_SUCCESS); 508 } 509 /* re-setup all the registers and enable interrupts if needed */ 510 async = (struct asyncline *)asy->asy_priv; 511 if ((async) && (async->async_flags & ASYNC_ISOPEN)) 512 (void) asy_program(asy, ASY_INIT); 513 asy->suspended = B_FALSE; 514 mutex_exit(asy->asy_excl_hi); 515 mutex_exit(asy->asy_excl); 516 return (DDI_SUCCESS); 517 } 518 default: 519 goto error; 520 } 521 522 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 523 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 524 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 525 526 if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, 527 (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN, 528 &attr, &asy->asy_handle) != DDI_SUCCESS) { 529 cmn_err(CE_WARN, "asyprobe regs map setup failed"); 530 goto error; 531 } 532 state = REGSMAP; 533 534 #ifdef DEBUG 535 if (asydebug) 536 printf("su attach mapped %p\n", (void *)asy->asy_ioaddr); 537 #endif 538 539 /* 540 * Initialize the port with default settings. 541 */ 542 asy->asy_fifo_buf = 1; 543 asy->asy_use_fifo = FIFO_OFF; 544 545 /* 546 * Check for baudrate generator's "baud-divisor-factor" property setup 547 * by OBP, since different UART chips might have different baudrate 548 * generator divisor. e.g., in case of NSPG's Sputnik platform, the 549 * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip 550 * instead of SuperIO. Since the baud-divisor-factor must be a positive 551 * integer, the divisors will always be at least as large as the values 552 * in asyspdtab[]. Make the default factor 1. 553 */ 554 asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 555 DDI_PROP_DONTPASS, "baud-divisor-factor", 1); 556 557 /* set speed cap */ 558 asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 559 DDI_PROP_DONTPASS, "serial-speed-cap", 115200); 560 561 /* check for ASY82510 chip */ 562 OUTB(ISR, 0x20); 563 if (INB(ISR) & 0x20) { /* 82510 chip is present */ 564 /* 565 * Since most of the general operation of the 82510 chip 566 * can be done from BANK 0 (8250A/16450 compatable mode) 567 * we will default to BANK 0. 568 */ 569 asy->asy_hwtype = ASY82510; 570 OUTB(DAT+7, 0x04); /* clear status */ 571 OUTB(ISR, 0x40); /* set to bank 2 */ 572 OUTB(MCR, 0x08); /* IMD */ 573 OUTB(DAT, 0x21); /* FMD */ 574 OUTB(ISR, 0x00); /* set to bank 0 */ 575 asy->asy_trig_level = 0; 576 } else { /* Set the UART in FIFO mode if it has FIFO buffers */ 577 asy->asy_hwtype = ASY16550AF; 578 OUTB(FIFOR, 0x00); /* clear fifo register */ 579 asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */ 580 581 /* set/Enable FIFO */ 582 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH | 583 (asy->asy_trig_level & 0xff)); 584 585 if ((INB(ISR) & 0xc0) == 0xc0) 586 asy->asy_use_fifo = FIFO_ON; 587 else { 588 asy->asy_hwtype = ASY8250; 589 OUTB(FIFOR, 0x00); /* NO FIFOs */ 590 asy->asy_trig_level = 0; 591 } 592 } 593 594 /* check for ST16C554D chip */ 595 if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, DDI_PROP_NOTPROM | 596 DDI_PROP_DONTPASS, "hwtype", &hwtype)) == DDI_PROP_SUCCESS) { 597 if (strcmp(hwtype, "ST16C554D") == 0) 598 asy->asy_hwtype = ASY16C554D; 599 ddi_prop_free(hwtype); 600 } 601 602 /* disable interrupts, see EXAR bug */ 603 if (asy->asy_hwtype == ASY16C554D) 604 OUTB(SPR, 0); 605 OUTB(ICR, 0); 606 OUTB(LCR, DLAB); /* select baud rate generator */ 607 /* Set the baud rate to 9600 */ 608 OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff); 609 OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff); 610 OUTB(LCR, STOP1|BITS8); 611 OUTB(MCR, (DTR | RTS| OUT2)); 612 613 /* 614 * Set up the other components of the asycom structure for this port. 615 */ 616 asy->asy_excl = (kmutex_t *) 617 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); 618 asy->asy_excl_hi = (kmutex_t *) 619 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); 620 asy->asy_soft_lock = (kmutex_t *) 621 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); 622 asy->asy_unit = instance; 623 asy->asy_dip = devi; 624 625 if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) { 626 cmn_err(CE_NOTE, 627 "Get iblock_cookie failed-Device interrupt%x\n", instance); 628 goto error; 629 } 630 631 if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH, 632 &asy->asy_soft_iblock) != DDI_SUCCESS) { 633 cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n", 634 instance); 635 goto error; 636 } 637 638 mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER, 639 (void *)asy->asy_soft_iblock); 640 mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL); 641 mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER, 642 (void *)asy->asy_iblock); 643 state = MUTEXES; 644 645 /* 646 * Install interrupt handlers for this device. 647 */ 648 if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr, 649 (caddr_t)asy) != DDI_SUCCESS) { 650 cmn_err(CE_CONT, 651 "Cannot set device interrupt for su driver\n"); 652 goto error; 653 } 654 state = ADDINTR; 655 656 if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id), 657 &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy) 658 != DDI_SUCCESS) { 659 cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n"); 660 goto error; 661 } 662 state = SOFTINTR; 663 664 /* initialize the asyncline structure */ 665 if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) { 666 cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance); 667 goto error; 668 } 669 state = ASYINIT; 670 671 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); 672 673 mutex_enter(asy->asy_excl); 674 async->async_common = asy; 675 cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL); 676 mutex_exit(asy->asy_excl); 677 678 if ((asy->sukstat = kstat_create("su", instance, "serialstat", 679 "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) { 680 asy->sukstat->ks_data = &asy->kstats; 681 kstat_named_init(&asy->kstats.ringover, "ring buffer overflow", 682 KSTAT_DATA_UINT64); 683 kstat_named_init(&asy->kstats.siloover, "silo overflow", 684 KSTAT_DATA_UINT64); 685 kstat_install(asy->sukstat); 686 } 687 state = KSTAT; 688 689 if (strcmp(ddi_node_name(devi), "rsc-console") == 0) { 690 /* 691 * If the device is configured as the 'rsc-console' 692 * create the minor device for this node. 693 */ 694 if (ddi_create_minor_node(devi, "ssp", S_IFCHR, 695 asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) 696 == DDI_FAILURE) { 697 cmn_err(CE_WARN, 698 "%s%d: Failed to create node rsc-console", 699 ddi_get_name(devi), ddi_get_instance(devi)); 700 goto error; 701 } 702 703 asy->asy_lom_console = 0; 704 asy->asy_rsc_console = 1; 705 asy->asy_rsc_control = 0; 706 asy->asy_device_type = ASY_SERIAL; 707 asy->asy_flags |= ASY_IGNORE_CD; 708 709 } else if (strcmp(ddi_node_name(devi), "lom-console") == 0) { 710 /* 711 * If the device is configured as the 'lom-console' 712 * create the minor device for this node. 713 * Do not create a dialout device. 714 * Use the same minor numbers as would be used for standard 715 * serial instances. 716 */ 717 if (ddi_create_minor_node(devi, "lom-console", S_IFCHR, 718 instance, DDI_NT_SERIAL_LOMCON, NULL) == DDI_FAILURE) { 719 cmn_err(CE_WARN, 720 "%s%d: Failed to create node lom-console", 721 ddi_get_name(devi), ddi_get_instance(devi)); 722 goto error; 723 } 724 asy->asy_lom_console = 1; 725 asy->asy_rsc_console = 0; 726 asy->asy_rsc_control = 0; 727 asy->asy_device_type = ASY_SERIAL; 728 asy->asy_flags |= ASY_IGNORE_CD; 729 730 } else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) { 731 /* 732 * If the device is configured as the 'rsc-control' 733 * create the minor device for this node. 734 */ 735 if (ddi_create_minor_node(devi, "sspctl", S_IFCHR, 736 asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) 737 == DDI_FAILURE) { 738 cmn_err(CE_WARN, "%s%d: Failed to create rsc-control", 739 ddi_get_name(devi), ddi_get_instance(devi)); 740 goto error; 741 } 742 743 asy->asy_lom_console = 0; 744 asy->asy_rsc_console = 0; 745 asy->asy_rsc_control = 1; 746 asy->asy_device_type = ASY_SERIAL; 747 asy->asy_flags |= ASY_IGNORE_CD; 748 749 } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 750 "keyboard", 0)) { 751 /* 752 * If the device is a keyboard, then create an internal 753 * pathname so that the dacf code will link the node into 754 * the keyboard console stream. See dacf.conf. 755 */ 756 if (ddi_create_internal_pathname(devi, "keyboard", 757 S_IFCHR, instance) == DDI_FAILURE) { 758 goto error; 759 } 760 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 761 asy->asy_device_type = ASY_KEYBOARD; /* Device type */ 762 } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 763 "mouse", 0)) { 764 /* 765 * If the device is a mouse, then create an internal 766 * pathname so that the dacf code will link the node into 767 * the mouse stream. See dacf.conf. 768 */ 769 if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR, 770 instance) == DDI_FAILURE) { 771 goto error; 772 } 773 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 774 asy->asy_device_type = ASY_MOUSE; 775 } else { 776 /* 777 * If not used for keyboard/mouse, create minor devices nodes 778 * for this device 779 */ 780 /* serial-port */ 781 (void) sprintf(name, "%c", (instance+'a')); 782 if (ddi_create_minor_node(devi, name, S_IFCHR, instance, 783 DDI_NT_SERIAL_MB, NULL) == DDI_FAILURE) { 784 goto error; 785 } 786 state = MINORNODE; 787 /* serial-port:dailout */ 788 (void) sprintf(name, "%c,cu", (instance+'a')); 789 if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE, 790 DDI_NT_SERIAL_MB_DO, NULL) == DDI_FAILURE) { 791 goto error; 792 } 793 /* Property for ignoring DCD */ 794 if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 795 "ignore-cd", 0)) { 796 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 797 } else { 798 asy->asy_flags &= ~ASY_IGNORE_CD; 799 /* 800 * if ignore-cd is not available it could be 801 * some old legacy platform, try to see 802 * whether the old legacy property exists 803 */ 804 (void) sprintf(name, 805 "port-%c-ignore-cd", (instance+ 'a')); 806 if (ddi_getprop(DDI_DEV_T_ANY, devi, 807 DDI_PROP_DONTPASS, name, 0)) 808 asy->asy_flags |= ASY_IGNORE_CD; 809 } 810 asy->asy_device_type = ASY_SERIAL; 811 } 812 ddi_report_dev(devi); 813 return (DDI_SUCCESS); 814 815 error: 816 if (state == MINORNODE) { 817 (void) sprintf(name, "%c", (instance+'a')); 818 ddi_remove_minor_node(devi, name); 819 } 820 if (state >= KSTAT) 821 kstat_delete(asy->sukstat); 822 if (state >= ASYINIT) { 823 cv_destroy(&async->async_flags_cv); 824 ddi_soft_state_free(su_asyncline, instance); 825 } 826 if (state >= SOFTINTR) 827 ddi_remove_softintr(asy->asy_softintr_id); 828 if (state >= ADDINTR) 829 ddi_remove_intr(devi, 0, asy->asy_iblock); 830 if (state >= MUTEXES) { 831 mutex_destroy(asy->asy_excl_hi); 832 mutex_destroy(asy->asy_excl); 833 mutex_destroy(asy->asy_soft_lock); 834 kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); 835 kmem_free(asy->asy_excl, sizeof (kmutex_t)); 836 kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); 837 } 838 if (state >= REGSMAP) 839 ddi_regs_map_free(&asy->asy_handle); 840 if (state >= SOFTSTATE) 841 ddi_soft_state_free(su_asycom, instance); 842 /* no action for EMPTY state */ 843 return (DDI_FAILURE); 844 } 845 846 static int 847 asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 848 void **result) 849 { 850 _NOTE(ARGUNUSED(dip)) 851 register dev_t dev = (dev_t)arg; 852 register int instance, error; 853 struct asycom *asy; 854 855 if ((instance = UNIT(dev)) > max_asy_instance) 856 return (DDI_FAILURE); 857 858 switch (infocmd) { 859 case DDI_INFO_DEVT2DEVINFO: 860 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); 861 if (asy->asy_dip == NULL) 862 error = DDI_FAILURE; 863 else { 864 *result = (void *) asy->asy_dip; 865 error = DDI_SUCCESS; 866 } 867 break; 868 case DDI_INFO_DEVT2INSTANCE: 869 *result = (void *)(uintptr_t)instance; 870 error = DDI_SUCCESS; 871 break; 872 default: 873 error = DDI_FAILURE; 874 } 875 return (error); 876 } 877 878 static int 879 asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) 880 { 881 _NOTE(ARGUNUSED(sflag)) 882 struct asycom *asy; 883 struct asyncline *async; 884 int mcr; 885 int unit; 886 int len; 887 struct termios *termiosp; 888 889 #ifdef DEBUG 890 if (asydebug & ASY_DEBUG_CLOSE) 891 printf("open\n"); 892 #endif 893 unit = UNIT(*dev); 894 if (unit > max_asy_instance) 895 return (ENXIO); /* unit not configured */ 896 897 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit); 898 if (async == NULL) 899 return (ENXIO); 900 901 asy = async->async_common; 902 if (asy == NULL) 903 return (ENXIO); /* device not found by autoconfig */ 904 905 mutex_enter(asy->asy_excl); 906 asy->asy_priv = (caddr_t)async; 907 908 again: 909 mutex_enter(asy->asy_excl_hi); 910 /* 911 * Block waiting for carrier to come up, unless this is a no-delay open. 912 */ 913 if (!(async->async_flags & ASYNC_ISOPEN)) { 914 /* 915 * If this port is for a RSC console or control 916 * use the following termio info 917 */ 918 if (asy->asy_rsc_console || asy->asy_rsc_control) { 919 async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT | 920 (B115200 & CBAUD); 921 async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT) 922 & CIBAUD); 923 async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; 924 } else if (asy->asy_lom_console) { 925 async->async_ttycommon.t_cflag = B9600 & CBAUD; 926 async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT) 927 & CIBAUD); 928 async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; 929 } else { 930 931 /* 932 * Set the default termios settings (cflag). 933 * Others are set in ldterm. Release the spin 934 * mutex as we can block here, reaquire before 935 * calling asy_program. 936 */ 937 mutex_exit(asy->asy_excl_hi); 938 if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 939 0, "ttymodes", (caddr_t)&termiosp, &len) 940 == DDI_PROP_SUCCESS && 941 len == sizeof (struct termios)) { 942 async->async_ttycommon.t_cflag = 943 termiosp->c_cflag; 944 kmem_free(termiosp, len); 945 } else { 946 cmn_err(CE_WARN, 947 "su: couldn't get ttymodes property!"); 948 } 949 mutex_enter(asy->asy_excl_hi); 950 } 951 async->async_ttycommon.t_iflag = 0; 952 async->async_ttycommon.t_iocpending = NULL; 953 async->async_ttycommon.t_size.ws_row = 0; 954 async->async_ttycommon.t_size.ws_col = 0; 955 async->async_ttycommon.t_size.ws_xpixel = 0; 956 async->async_ttycommon.t_size.ws_ypixel = 0; 957 async->async_dev = *dev; 958 async->async_wbufcid = 0; 959 960 async->async_startc = CSTART; 961 async->async_stopc = CSTOP; 962 (void) asy_program(asy, ASY_INIT); 963 } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) && 964 secpolicy_excl_open(cr) != 0) { 965 mutex_exit(asy->asy_excl_hi); 966 mutex_exit(asy->asy_excl); 967 return (EBUSY); 968 } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) { 969 mutex_exit(asy->asy_excl_hi); 970 mutex_exit(asy->asy_excl); 971 return (EBUSY); 972 } 973 974 if (*dev & OUTLINE) 975 async->async_flags |= ASYNC_OUT; 976 977 /* Raise DTR on every open */ 978 mcr = INB(MCR); 979 OUTB(MCR, mcr|DTR); 980 981 /* 982 * Check carrier. 983 */ 984 if (asy->asy_flags & ASY_IGNORE_CD) 985 async->async_ttycommon.t_flags |= TS_SOFTCAR; 986 if ((async->async_ttycommon.t_flags & TS_SOFTCAR) || 987 (INB(MSR) & DCD)) 988 async->async_flags |= ASYNC_CARR_ON; 989 else 990 async->async_flags &= ~ASYNC_CARR_ON; 991 mutex_exit(asy->asy_excl_hi); 992 993 /* 994 * If FNDELAY and FNONBLOCK are clear, block until carrier up. 995 * Quit on interrupt. 996 */ 997 if (!(flag & (FNDELAY|FNONBLOCK)) && 998 !(async->async_ttycommon.t_cflag & CLOCAL)) { 999 if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) || 1000 ((async->async_flags & ASYNC_OUT) && 1001 !(*dev & OUTLINE))) { 1002 async->async_flags |= ASYNC_WOPEN; 1003 if (cv_wait_sig(&async->async_flags_cv, 1004 asy->asy_excl) == 0) { 1005 async->async_flags &= ~ASYNC_WOPEN; 1006 mutex_exit(asy->asy_excl); 1007 return (EINTR); 1008 } 1009 async->async_flags &= ~ASYNC_WOPEN; 1010 goto again; 1011 } 1012 } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) { 1013 mutex_exit(asy->asy_excl); 1014 return (EBUSY); 1015 } 1016 1017 if (asy->suspended) { 1018 mutex_exit(asy->asy_excl); 1019 (void) ddi_dev_is_needed(asy->asy_dip, 0, 1); 1020 mutex_enter(asy->asy_excl); 1021 } 1022 1023 async->async_ttycommon.t_readq = rq; 1024 async->async_ttycommon.t_writeq = WR(rq); 1025 rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async; 1026 mutex_exit(asy->asy_excl); 1027 qprocson(rq); 1028 async->async_flags |= ASYNC_ISOPEN; 1029 async->async_polltid = 0; 1030 return (0); 1031 } 1032 1033 static void 1034 async_progress_check(void *arg) 1035 { 1036 struct asyncline *async = arg; 1037 struct asycom *asy = async->async_common; 1038 mblk_t *bp; 1039 1040 /* 1041 * We define "progress" as either waiting on a timed break or delay, or 1042 * having had at least one transmitter interrupt. If none of these are 1043 * true, then just terminate the output and wake up that close thread. 1044 */ 1045 mutex_enter(asy->asy_excl); 1046 mutex_enter(asy->asy_excl_hi); 1047 if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) { 1048 async->async_ocnt = 0; 1049 async->async_flags &= ~ASYNC_BUSY; 1050 async->async_timer = 0; 1051 bp = async->async_xmitblk; 1052 async->async_xmitblk = NULL; 1053 mutex_exit(asy->asy_excl_hi); 1054 if (bp != NULL) 1055 freeb(bp); 1056 /* 1057 * Since this timer is running, we know that we're in exit(2). 1058 * That means that the user can't possibly be waiting on any 1059 * valid ioctl(2) completion anymore, and we should just flush 1060 * everything. 1061 */ 1062 flushq(async->async_ttycommon.t_writeq, FLUSHALL); 1063 cv_broadcast(&async->async_flags_cv); 1064 } else { 1065 async->async_flags &= ~ASYNC_PROGRESS; 1066 async->async_timer = timeout(async_progress_check, async, 1067 drv_usectohz(su_drain_check)); 1068 mutex_exit(asy->asy_excl_hi); 1069 } 1070 mutex_exit(asy->asy_excl); 1071 } 1072 1073 /* 1074 * Close routine. 1075 */ 1076 static int 1077 asyclose(queue_t *q, int flag) 1078 { 1079 struct asyncline *async; 1080 struct asycom *asy; 1081 int icr, lcr; 1082 int nohupcl; 1083 1084 1085 #ifdef DEBUG 1086 if (asydebug & ASY_DEBUG_CLOSE) 1087 printf("close\n"); 1088 #endif 1089 async = q->q_ptr; 1090 ASSERT(async != NULL); 1091 asy = async->async_common; 1092 1093 /* get the nohupcl OBP property of this device */ 1094 nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS, 1095 "nohupcl", 0); 1096 1097 mutex_enter(asy->asy_excl); 1098 async->async_flags |= ASYNC_CLOSING; 1099 1100 /* 1101 * Turn off PPS handling early to avoid events occuring during 1102 * close. Also reset the DCD edge monitoring bit. 1103 */ 1104 mutex_enter(asy->asy_excl_hi); 1105 asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE); 1106 mutex_exit(asy->asy_excl_hi); 1107 1108 /* 1109 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and 1110 * untimed (TIOCSBRK). For the timed case, these are enqueued on our 1111 * write queue and there's a timer running, so we don't have to worry 1112 * about them. For the untimed case, though, the user obviously made a 1113 * mistake, because these are handled immediately. We'll terminate the 1114 * break now and honor his implicit request by discarding the rest of 1115 * the data. 1116 */ 1117 if (!(async->async_flags & ASYNC_BREAK)) { 1118 mutex_enter(asy->asy_excl_hi); 1119 lcr = INB(LCR); 1120 if (lcr & SETBREAK) { 1121 OUTB(LCR, (lcr & ~SETBREAK)); 1122 } 1123 mutex_exit(asy->asy_excl_hi); 1124 if (lcr & SETBREAK) 1125 goto nodrain; 1126 } 1127 1128 /* 1129 * If the user told us not to delay the close ("non-blocking"), then 1130 * don't bother trying to drain. 1131 * 1132 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever 1133 * getting an M_START (since these messages aren't enqueued), and the 1134 * only other way to clear the stop condition is by loss of DCD, which 1135 * would discard the queue data. Thus, we drop the output data if 1136 * ASYNC_STOPPED is set. 1137 */ 1138 if ((flag & (FNDELAY|FNONBLOCK)) || 1139 (async->async_flags & ASYNC_STOPPED)) { 1140 goto nodrain; 1141 } 1142 1143 /* 1144 * If there's any pending output, then we have to try to drain it. 1145 * There are two main cases to be handled: 1146 * - called by close(2): need to drain until done or until 1147 * a signal is received. No timeout. 1148 * - called by exit(2): need to drain while making progress 1149 * or until a timeout occurs. No signals. 1150 * 1151 * If we can't rely on receiving a signal to get us out of a hung 1152 * session, then we have to use a timer. In this case, we set a timer 1153 * to check for progress in sending the output data -- all that we ask 1154 * (at each interval) is that there's been some progress made. Since 1155 * the interrupt routine grabs buffers from the write queue, we can't 1156 * trust async_ocnt. Instead, we use a flag. 1157 * 1158 * Note that loss of carrier will cause the output queue to be flushed, 1159 * and we'll wake up again and finish normally. 1160 */ 1161 if (!ddi_can_receive_sig() && su_drain_check != 0) { 1162 async->async_flags &= ~ASYNC_PROGRESS; 1163 async->async_timer = timeout(async_progress_check, async, 1164 drv_usectohz(su_drain_check)); 1165 } 1166 1167 while (async->async_ocnt > 0 || 1168 async->async_ttycommon.t_writeq->q_first != NULL || 1169 (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) { 1170 if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0) 1171 break; 1172 } 1173 if (async->async_timer != 0) { 1174 (void) untimeout(async->async_timer); 1175 async->async_timer = 0; 1176 } 1177 1178 nodrain: 1179 mutex_enter(asy->asy_excl_hi); 1180 1181 /* turn off the loopback mode */ 1182 if ((async->async_dev != rconsdev) && 1183 (async->async_dev != kbddev) && 1184 (async->async_dev != stdindev)) { 1185 OUTB(MCR, INB(MCR) & ~ ASY_LOOP); 1186 } 1187 1188 async->async_ocnt = 0; 1189 if (async->async_xmitblk != NULL) 1190 freeb(async->async_xmitblk); 1191 async->async_xmitblk = NULL; 1192 1193 /* 1194 * If the "nohupcl" OBP property is set for this device, do 1195 * not turn off DTR and RTS no matter what. Otherwise, if the 1196 * line has HUPCL set or is incompletely opened, turn off DTR 1197 * and RTS to fix the modem line. 1198 */ 1199 if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) || 1200 (async->async_flags & ASYNC_WOPEN))) { 1201 /* turn off DTR, RTS but NOT interrupt to 386 */ 1202 OUTB(MCR, OUT2); 1203 mutex_exit(asy->asy_excl_hi); 1204 /* 1205 * Don't let an interrupt in the middle of close 1206 * bounce us back to the top; just continue closing 1207 * as if nothing had happened. 1208 */ 1209 if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0) 1210 goto out; 1211 mutex_enter(asy->asy_excl_hi); 1212 } 1213 1214 /* 1215 * If nobody's using it now, turn off receiver interrupts. 1216 */ 1217 if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) { 1218 icr = INB(ICR); 1219 OUTB(ICR, (icr & ~RIEN)); 1220 } 1221 mutex_exit(asy->asy_excl_hi); 1222 out: 1223 /* 1224 * Clear out device state. 1225 */ 1226 async->async_flags = 0; 1227 ttycommon_close(&async->async_ttycommon); 1228 cv_broadcast(&async->async_flags_cv); 1229 1230 /* 1231 * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in 1232 * async_softint or an interrupt was pending when the process 1233 * using the port exited. 1234 */ 1235 asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT; 1236 1237 /* 1238 * Cancel outstanding "bufcall" request. 1239 */ 1240 if (async->async_wbufcid) { 1241 unbufcall(async->async_wbufcid); 1242 async->async_wbufcid = 0; 1243 } 1244 1245 /* 1246 * If inperim is true, it means the port is closing while there's 1247 * a pending software interrupt. async_flags has been zeroed out, 1248 * so this instance of leaveq() needs to be called before we call 1249 * qprocsoff() to disable services on the q. If inperim is false, 1250 * leaveq() has already been called or we're not in a perimeter. 1251 */ 1252 if (asy->inperim == B_TRUE) { 1253 asy->inperim = B_FALSE; 1254 mutex_exit(asy->asy_excl); 1255 leaveq(q); 1256 } else { 1257 mutex_exit(asy->asy_excl); 1258 } 1259 1260 /* Note that qprocsoff can't be done until after interrupts are off */ 1261 qprocsoff(q); 1262 q->q_ptr = WR(q)->q_ptr = NULL; 1263 async->async_ttycommon.t_readq = NULL; 1264 async->async_ttycommon.t_writeq = NULL; 1265 1266 return (0); 1267 } 1268 1269 /* 1270 * Checks to see if the serial port is still transmitting 1271 * characters. It returns true when there are characters 1272 * queued to transmit, when the holding register contains 1273 * a byte, or when the shifting register still contains 1274 * data to send. 1275 * 1276 */ 1277 static boolean_t 1278 asy_isbusy(struct asycom *asy) 1279 { 1280 struct asyncline *async; 1281 1282 #ifdef DEBUG 1283 if (asydebug & ASY_DEBUG_EOT) 1284 printf("isbusy\n"); 1285 #endif 1286 async = (struct asyncline *)asy->asy_priv; 1287 ASSERT(mutex_owned(asy->asy_excl)); 1288 ASSERT(mutex_owned(asy->asy_excl_hi)); 1289 return ((async->async_ocnt > 0) || 1290 ((INB(LSR) & XSRE) == 0)); 1291 } 1292 1293 /* 1294 * Program the ASY port. Most of the async operation is based on the values 1295 * of 'c_iflag' and 'c_cflag'. 1296 */ 1297 static int 1298 asy_program(struct asycom *asy, int mode) 1299 { 1300 struct asyncline *async; 1301 int baudrate, c_flag; 1302 int icr, lcr; 1303 int ocflags; 1304 int error = 0; 1305 1306 ASSERT(mutex_owned(asy->asy_excl)); 1307 ASSERT(mutex_owned(asy->asy_excl_hi)); 1308 1309 #ifdef DEBUG 1310 if (asydebug & ASY_DEBUG_PROCS) 1311 printf("program\n"); 1312 #endif 1313 async = (struct asyncline *)asy->asy_priv; 1314 1315 baudrate = async->async_ttycommon.t_cflag & CBAUD; 1316 if (async->async_ttycommon.t_cflag & CBAUDEXT) 1317 baudrate += 16; 1318 1319 /* Limit baudrate so it can't index out of baudtable */ 1320 if (baudrate >= N_SU_SPEEDS) baudrate = B9600; 1321 1322 /* 1323 * If baud rate requested is greater than the speed cap 1324 * or is an unsupported baud rate then reset t_cflag baud 1325 * to the last valid baud rate. If this is the initial 1326 * pass through asy_program then set it to 9600. 1327 */ 1328 if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) || 1329 (baudtable[baudrate] > asy->asy_speed_cap)) { 1330 async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT & 1331 ~CIBAUD & ~CIBAUDEXT; 1332 if (mode == ASY_INIT) { 1333 async->async_ttycommon.t_cflag |= B9600; 1334 async->async_ttycommon.t_cflag |= B9600 << IBSHIFT; 1335 baudrate = B9600; 1336 } else { 1337 async->async_ttycommon.t_cflag |= 1338 (asy->asy_ocflags & (CBAUD | CBAUDEXT | 1339 CIBAUD | CIBAUDEXT)); 1340 error = EINVAL; 1341 goto end; 1342 } 1343 } 1344 1345 /* 1346 * If CIBAUD and CIBAUDEXT are zero then we should set them to 1347 * the equivelant output baud bits. Else, if CIBAUD and CIBAUDEXT 1348 * don't match CBAUD and CBAUDEXT respectively then we should 1349 * notify the requestor that we do not support split speeds. 1350 */ 1351 if ((async->async_ttycommon.t_cflag & (CIBAUD|CIBAUDEXT)) == 0) { 1352 async->async_ttycommon.t_cflag |= 1353 (async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT; 1354 if (async->async_ttycommon.t_cflag & CBAUDEXT) 1355 async->async_ttycommon.t_cflag |= CIBAUDEXT; 1356 } else { 1357 if ((((async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT) != 1358 (async->async_ttycommon.t_cflag & CIBAUD)) || 1359 !(((async->async_ttycommon.t_cflag & (CBAUDEXT | 1360 CIBAUDEXT)) == (CBAUDEXT | CIBAUDEXT)) || 1361 ((async->async_ttycommon.t_cflag & (CBAUDEXT | 1362 CIBAUDEXT)) == 0))) { 1363 async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT & 1364 ~CIBAUD & ~CIBAUDEXT; 1365 async->async_ttycommon.t_cflag |= 1366 (asy->asy_ocflags & (CBAUD | CBAUDEXT | 1367 CIBAUD | CIBAUDEXT)); 1368 error = EINVAL; 1369 goto end; 1370 } 1371 } 1372 1373 c_flag = async->async_ttycommon.t_cflag & 1374 (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD | 1375 CBAUDEXT | CIBAUD | CIBAUDEXT); 1376 1377 /* disable interrupts, see EXAR bug */ 1378 if (asy->asy_hwtype == ASY16C554D) 1379 OUTB(SPR, 0); 1380 OUTB(ICR, 0); 1381 1382 ocflags = asy->asy_ocflags; 1383 1384 /* flush/reset the status registers */ 1385 if (mode == ASY_INIT) { 1386 (void) INB(DAT); 1387 (void) INB(ISR); 1388 (void) INB(LSR); 1389 (void) INB(MSR); 1390 } 1391 1392 if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) { 1393 /* Set line control */ 1394 lcr = INB(LCR); 1395 lcr &= ~(WLS0|WLS1|STB|PEN|EPS); 1396 1397 if (c_flag & CSTOPB) 1398 lcr |= STB; /* 2 stop bits */ 1399 1400 if (c_flag & PARENB) 1401 lcr |= PEN; 1402 1403 if ((c_flag & PARODD) == 0) 1404 lcr |= EPS; 1405 1406 switch (c_flag & CSIZE) { 1407 case CS5: 1408 lcr |= BITS5; 1409 break; 1410 case CS6: 1411 lcr |= BITS6; 1412 break; 1413 case CS7: 1414 lcr |= BITS7; 1415 break; 1416 case CS8: 1417 lcr |= BITS8; 1418 break; 1419 } 1420 1421 /* set the baud rate when the rate is NOT B0 */ 1422 if (baudrate != 0) { 1423 OUTB(LCR, DLAB); 1424 OUTB(DAT, (asyspdtab[baudrate] * 1425 asy->asy_baud_divisor_factor) & 0xff); 1426 OUTB(ICR, ((asyspdtab[baudrate] * 1427 asy->asy_baud_divisor_factor) >> 8) & 0xff); 1428 } 1429 /* set the line control modes */ 1430 OUTB(LCR, lcr); 1431 1432 /* 1433 * if transitioning from CREAD off to CREAD on, 1434 * flush the FIFO buffer if we have one. 1435 */ 1436 if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) { 1437 if (asy->asy_use_fifo == FIFO_ON) { 1438 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | 1439 (asy->asy_trig_level & 0xff)); 1440 } 1441 } 1442 1443 /* remember the new cflags */ 1444 asy->asy_ocflags = c_flag & ~CLOCAL; 1445 } 1446 1447 /* whether or not CLOCAL is set, modify the modem control lines */ 1448 if (baudrate == 0) 1449 /* B0 has been issued, lower DTR */ 1450 OUTB(MCR, RTS|OUT2); 1451 else 1452 /* raise DTR */ 1453 OUTB(MCR, DTR|RTS|OUT2); 1454 1455 /* 1456 * Call the modem status interrupt handler to check for the carrier 1457 * in case CLOCAL was turned off after the carrier came on. 1458 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.) 1459 */ 1460 async_msint(asy); 1461 1462 /* Set interrupt control */ 1463 if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS)) 1464 /* 1465 * direct-wired line ignores DCD, so we don't enable modem 1466 * status interrupts. 1467 */ 1468 icr = (TIEN | SIEN); 1469 else 1470 icr = (TIEN | SIEN | MIEN); 1471 1472 if (c_flag & CREAD) 1473 icr |= RIEN; 1474 1475 OUTB(ICR, icr); 1476 end: 1477 return (error); 1478 } 1479 1480 /* 1481 * asyintr() is the High Level Interrupt Handler. 1482 * 1483 * There are four different interrupt types indexed by ISR register values: 1484 * 0: modem 1485 * 1: Tx holding register is empty, ready for next char 1486 * 2: Rx register now holds a char to be picked up 1487 * 3: error or break on line 1488 * This routine checks the Bit 0 (interrupt-not-pending) to determine if 1489 * the interrupt is from this port. 1490 */ 1491 uint_t 1492 asyintr(caddr_t argasy) 1493 { 1494 struct asycom *asy = (struct asycom *)argasy; 1495 struct asyncline *async; 1496 int ret_status = DDI_INTR_UNCLAIMED; 1497 uchar_t interrupt_id, lsr; 1498 1499 interrupt_id = INB(ISR) & 0x0F; 1500 async = (struct asyncline *)asy->asy_priv; 1501 if ((async == NULL) || 1502 !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { 1503 if (interrupt_id & NOINTERRUPT) { 1504 return (DDI_INTR_UNCLAIMED); 1505 } else { 1506 lsr = INB(LSR); 1507 if ((lsr & BRKDET) && 1508 ((abort_enable == KIOCABORTENABLE) && 1509 (async->async_dev == rconsdev))) 1510 abort_sequence_enter((char *)NULL); 1511 else { 1512 /* reset line status */ 1513 (void) INB(LSR); 1514 /* discard any data */ 1515 (void) INB(DAT); 1516 /* reset modem status */ 1517 (void) INB(MSR); 1518 return (DDI_INTR_CLAIMED); 1519 } 1520 } 1521 } 1522 /* 1523 * Spurious interrupts happen in this driver 1524 * because of the transmission on serial port not handled 1525 * properly. 1526 * 1527 * The reasons for Spurious interrupts are: 1528 * 1. There is a path in async_nstart which transmits 1529 * characters without going through interrupt services routine 1530 * which causes spurious interrupts to happen. 1531 * 2. In the async_txint more than one character is sent 1532 * in one interrupt service. 1533 * 3. In async_rxint more than one characters are received in 1534 * in one interrupt service. 1535 * 1536 * Hence we have flags to indicate that such scenerio has happened. 1537 * and claim only such interrupts and others we donot claim it 1538 * as it could be a indicator of some hardware problem. 1539 * 1540 */ 1541 if (interrupt_id & NOINTERRUPT) { 1542 mutex_enter(asy->asy_excl_hi); 1543 if ((asy->asy_xmit_count > 1) || 1544 (asy->asy_out_of_band_xmit > 0) || 1545 (asy->asy_rx_count > 1)) { 1546 asy->asy_xmit_count = 0; 1547 asy->asy_out_of_band_xmit = 0; 1548 asy->asy_rx_count = 0; 1549 mutex_exit(asy->asy_excl_hi); 1550 return (DDI_INTR_CLAIMED); 1551 } else { 1552 mutex_exit(asy->asy_excl_hi); 1553 return (DDI_INTR_UNCLAIMED); 1554 } 1555 } 1556 ret_status = DDI_INTR_CLAIMED; 1557 mutex_enter(asy->asy_excl_hi); 1558 if (asy->asy_hwtype == ASY82510) 1559 OUTB(ISR, 0x00); /* set bank 0 */ 1560 1561 #ifdef DEBUG 1562 if (asydebug & ASY_DEBUG_INTR) 1563 prom_printf("l"); 1564 #endif 1565 lsr = INB(LSR); 1566 switch (interrupt_id) { 1567 case RxRDY: 1568 case RSTATUS: 1569 case FFTMOUT: 1570 /* receiver interrupt or receiver errors */ 1571 async_rxint(asy, lsr); 1572 break; 1573 case TxRDY: 1574 /* transmit interrupt */ 1575 async_txint(asy, lsr); 1576 break; 1577 case MSTATUS: 1578 /* modem status interrupt */ 1579 async_msint(asy); 1580 break; 1581 } 1582 mutex_exit(asy->asy_excl_hi); 1583 return (ret_status); 1584 } 1585 1586 /* 1587 * Transmitter interrupt service routine. 1588 * If there is more data to transmit in the current pseudo-DMA block, 1589 * send the next character if output is not stopped or draining. 1590 * Otherwise, queue up a soft interrupt. 1591 * 1592 * XXX - Needs review for HW FIFOs. 1593 */ 1594 static void 1595 async_txint(struct asycom *asy, uchar_t lsr) 1596 { 1597 struct asyncline *async = (struct asyncline *)asy->asy_priv; 1598 int fifo_len; 1599 int xmit_progress; 1600 1601 asycheckflowcontrol_hw(asy); 1602 1603 /* 1604 * If ASYNC_BREAK has been set, return to asyintr()'s context to 1605 * claim the interrupt without performing any action. 1606 */ 1607 if (async->async_flags & ASYNC_BREAK) 1608 return; 1609 1610 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 1611 1612 /* 1613 * Check for flow control and do the needed action. 1614 */ 1615 if (asycheckflowcontrol_sw(asy)) { 1616 return; 1617 } 1618 1619 if (async->async_ocnt > 0 && 1620 !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) { 1621 xmit_progress = 0; 1622 while (fifo_len > 0 && async->async_ocnt > 0) { 1623 if (lsr & XHRE) { 1624 OUTB(DAT, *async->async_optr++); 1625 fifo_len--; 1626 async->async_ocnt--; 1627 xmit_progress++; 1628 } 1629 /* 1630 * Reading the lsr, (moved reading at the end of 1631 * while loop) as already we have read once at 1632 * the beginning of interrupt service 1633 */ 1634 lsr = INB(LSR); 1635 } 1636 asy->asy_xmit_count = xmit_progress; 1637 if (xmit_progress > 0) 1638 async->async_flags |= ASYNC_PROGRESS; 1639 } 1640 1641 if (fifo_len == 0) { 1642 return; 1643 } 1644 1645 1646 ASYSETSOFT(asy); 1647 } 1648 1649 /* 1650 * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive 1651 * error interrupt. 1652 * Try to put the character into the circular buffer for this line; if it 1653 * overflows, indicate a circular buffer overrun. If this port is always 1654 * to be serviced immediately, or the character is a STOP character, or 1655 * more than 15 characters have arrived, queue up a soft interrupt to 1656 * drain the circular buffer. 1657 * XXX - needs review for hw FIFOs support. 1658 */ 1659 1660 static void 1661 async_rxint(struct asycom *asy, uchar_t lsr) 1662 { 1663 struct asyncline *async = (struct asyncline *)asy->asy_priv; 1664 uchar_t c = 0; 1665 uint_t s = 0, needsoft = 0; 1666 register tty_common_t *tp; 1667 1668 tp = &async->async_ttycommon; 1669 if (!(tp->t_cflag & CREAD)) { 1670 if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 1671 (void) (INB(DAT) & 0xff); 1672 } 1673 return; /* line is not open for read? */ 1674 } 1675 asy->asy_rx_count = 0; 1676 while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 1677 c = 0; 1678 s = 0; 1679 asy->asy_rx_count++; 1680 if (lsr & RCA) { 1681 c = INB(DAT) & 0xff; 1682 /* 1683 * Even a single character is received 1684 * we need Soft interrupt to pass it to 1685 * higher layers. 1686 */ 1687 needsoft = 1; 1688 } 1689 1690 /* Check for character break sequence */ 1691 if ((abort_enable == KIOCABORTALTERNATE) && 1692 (async->async_dev == rconsdev)) { 1693 if (abort_charseq_recognize(c)) 1694 abort_sequence_enter((char *)NULL); 1695 } 1696 1697 /* Handle framing errors */ 1698 if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) { 1699 if (lsr & PARERR) { 1700 if (tp->t_iflag & INPCK) /* parity enabled */ 1701 s |= PERROR; 1702 } 1703 if (lsr & (FRMERR|BRKDET)) 1704 s |= FRERROR; 1705 if (lsr & OVRRUN) { 1706 async->async_hw_overrun = 1; 1707 s |= OVERRUN; 1708 } 1709 } 1710 1711 if (s == 0) 1712 if ((tp->t_iflag & PARMRK) && 1713 !(tp->t_iflag & (IGNPAR|ISTRIP)) && 1714 (c == 0377)) 1715 if (RING_POK(async, 2)) { 1716 RING_PUT(async, 0377); 1717 RING_PUT(async, c); 1718 } else 1719 async->async_sw_overrun = 1; 1720 else 1721 if (RING_POK(async, 1)) 1722 RING_PUT(async, c); 1723 else 1724 async->async_sw_overrun = 1; 1725 else 1726 if (s & FRERROR) { /* Handle framing errors */ 1727 if (c == 0) { 1728 /* Look for break on kbd, stdin, or rconsdev */ 1729 if ((async->async_dev == kbddev) || 1730 ((async->async_dev == rconsdev) || 1731 (async->async_dev == stdindev)) && 1732 (abort_enable != 1733 KIOCABORTALTERNATE)) 1734 abort_sequence_enter((char *)0); 1735 else 1736 async->async_break++; 1737 } else { 1738 if (RING_POK(async, 1)) 1739 RING_MARK(async, c, s); 1740 else 1741 async->async_sw_overrun = 1; 1742 } 1743 } else { /* Parity errors handled by ldterm */ 1744 if (RING_POK(async, 1)) 1745 RING_MARK(async, c, s); 1746 else 1747 async->async_sw_overrun = 1; 1748 } 1749 lsr = INB(LSR); 1750 if (asy->asy_rx_count > 16) break; 1751 } 1752 /* Check whether there is a request for hw/sw inbound/input flow ctrl */ 1753 if ((async->async_ttycommon.t_cflag & CRTSXOFF) || 1754 (async->async_ttycommon.t_iflag & IXOFF)) 1755 if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) { 1756 #ifdef DEBUG 1757 if (asydebug & ASY_DEBUG_HFLOW) 1758 printf("asy%d: hardware flow stop input.\n", 1759 UNIT(async->async_dev)); 1760 #endif 1761 async->async_flags |= ASYNC_HW_IN_FLOW; 1762 async->async_flowc = async->async_stopc; 1763 async->async_ringbuf_overflow = 1; 1764 } 1765 1766 if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || 1767 (RING_FRAC(async)) || (async->async_polltid == 0)) 1768 ASYSETSOFT(asy); /* need a soft interrupt */ 1769 } 1770 1771 /* 1772 * Interrupt on port: handle PPS event. This function is only called 1773 * for a port on which PPS event handling has been enabled. 1774 */ 1775 static void 1776 asy_ppsevent(struct asycom *asy, int msr) 1777 { 1778 if (asy->asy_flags & ASY_PPS_EDGE) { 1779 /* Have seen leading edge, now look for and record drop */ 1780 if ((msr & DCD) == 0) 1781 asy->asy_flags &= ~ASY_PPS_EDGE; 1782 /* 1783 * Waiting for leading edge, look for rise; stamp event and 1784 * calibrate kernel clock. 1785 */ 1786 } else if (msr & DCD) { 1787 /* 1788 * This code captures a timestamp at the designated 1789 * transition of the PPS signal (DCD asserted). The 1790 * code provides a pointer to the timestamp, as well 1791 * as the hardware counter value at the capture. 1792 * 1793 * Note: the kernel has nano based time values while 1794 * NTP requires micro based, an in-line fast algorithm 1795 * to convert nsec to usec is used here -- see hrt2ts() 1796 * in common/os/timers.c for a full description. 1797 */ 1798 struct timeval *tvp = &asy_ppsev.tv; 1799 timestruc_t ts; 1800 long nsec, usec; 1801 1802 asy->asy_flags |= ASY_PPS_EDGE; 1803 gethrestime(&ts); 1804 nsec = ts.tv_nsec; 1805 usec = nsec + (nsec >> 2); 1806 usec = nsec + (usec >> 1); 1807 usec = nsec + (usec >> 2); 1808 usec = nsec + (usec >> 4); 1809 usec = nsec - (usec >> 3); 1810 usec = nsec + (usec >> 2); 1811 usec = nsec + (usec >> 3); 1812 usec = nsec + (usec >> 4); 1813 usec = nsec + (usec >> 1); 1814 usec = nsec + (usec >> 6); 1815 tvp->tv_usec = usec >> 10; 1816 tvp->tv_sec = ts.tv_sec; 1817 1818 ++asy_ppsev.serial; 1819 1820 /* 1821 * Because the kernel keeps a high-resolution time, 1822 * pass the current highres timestamp in tvp and zero 1823 * in usec. 1824 */ 1825 ddi_hardpps(tvp, 0); 1826 } 1827 } 1828 1829 /* 1830 * Modem status interrupt. 1831 * 1832 * (Note: It is assumed that the MSR hasn't been read by asyintr().) 1833 */ 1834 1835 static void 1836 async_msint(struct asycom *asy) 1837 { 1838 struct asyncline *async = (struct asyncline *)asy->asy_priv; 1839 int msr; 1840 1841 msr = INB(MSR); /* this resets the interrupt */ 1842 asy->asy_cached_msr = msr; 1843 #ifdef DEBUG 1844 if (asydebug & ASY_DEBUG_STATE) { 1845 printf(" transition: %3s %3s %3s %3s\n" 1846 "current state: %3s %3s %3s %3s\n", 1847 (msr & DCTS) ? "CTS" : " ", 1848 (msr & DDSR) ? "DSR" : " ", 1849 (msr & DRI) ? "RI " : " ", 1850 (msr & DDCD) ? "DCD" : " ", 1851 (msr & CTS) ? "CTS" : " ", 1852 (msr & DSR) ? "DSR" : " ", 1853 (msr & RI) ? "RI " : " ", 1854 (msr & DCD) ? "DCD" : " "); 1855 } 1856 #endif 1857 if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) { 1858 #ifdef DEBUG 1859 if (asydebug & ASY_DEBUG_HFLOW) 1860 printf("asy%d: hflow start\n", 1861 UNIT(async->async_dev)); 1862 #endif 1863 async->async_flags |= ASYNC_HW_OUT_FLW; 1864 } 1865 if (asy->asy_hwtype == ASY82510) 1866 OUTB(MSR, (msr & 0xF0)); 1867 1868 /* Handle PPS event */ 1869 if (asy->asy_flags & ASY_PPS) 1870 asy_ppsevent(asy, msr); 1871 1872 async->async_ext++; 1873 ASYSETSOFT(asy); 1874 } 1875 1876 /* 1877 * Handle a second-stage interrupt. 1878 */ 1879 uint_t 1880 asysoftintr(caddr_t intarg) 1881 { 1882 struct asycom *asy = (struct asycom *)intarg; 1883 struct asyncline *async; 1884 int rv; 1885 int cc; 1886 /* 1887 * Test and clear soft interrupt. 1888 */ 1889 mutex_enter(asy->asy_soft_lock); 1890 #ifdef DEBUG 1891 if (asydebug & ASY_DEBUG_PROCS) 1892 printf("softintr\n"); 1893 #endif 1894 rv = asy->asysoftpend; 1895 if (rv != 0) 1896 asy->asysoftpend = 0; 1897 mutex_exit(asy->asy_soft_lock); 1898 1899 if (rv) { 1900 if (asy->asy_priv == NULL) 1901 return (rv); 1902 async = (struct asyncline *)asy->asy_priv; 1903 mutex_enter(asy->asy_excl_hi); 1904 if (asy->asy_flags & ASY_NEEDSOFT) { 1905 asy->asy_flags &= ~ASY_NEEDSOFT; 1906 mutex_exit(asy->asy_excl_hi); 1907 (void) async_softint(asy); 1908 mutex_enter(asy->asy_excl_hi); 1909 } 1910 /* 1911 * There are some instances where the softintr is not 1912 * scheduled and hence not called. It so happened that makes 1913 * the last few characters to be stuck in ringbuffer. 1914 * Hence, call once again the handler so that the last few 1915 * characters are cleared. 1916 */ 1917 cc = RING_CNT(async); 1918 mutex_exit(asy->asy_excl_hi); 1919 if (cc > 0) { 1920 (void) async_softint(asy); 1921 } 1922 } 1923 return (rv); 1924 } 1925 1926 /* 1927 * Handle a software interrupt. 1928 */ 1929 static int 1930 async_softint(struct asycom *asy) 1931 { 1932 struct asyncline *async = (struct asyncline *)asy->asy_priv; 1933 uint_t cc; 1934 mblk_t *bp; 1935 queue_t *q; 1936 uchar_t val; 1937 uchar_t c; 1938 tty_common_t *tp; 1939 1940 #ifdef DEBUG 1941 if (asydebug & ASY_DEBUG_PROCS) 1942 printf("process\n"); 1943 #endif 1944 mutex_enter(asy->asy_excl); 1945 if (asy->asy_flags & ASY_DOINGSOFT) { 1946 mutex_exit(asy->asy_excl); 1947 return (0); 1948 } 1949 tp = &async->async_ttycommon; 1950 q = tp->t_readq; 1951 if (q != NULL) { 1952 mutex_exit(asy->asy_excl); 1953 enterq(q); 1954 mutex_enter(asy->asy_excl); 1955 } 1956 mutex_enter(asy->asy_excl_hi); 1957 asy->asy_flags |= ASY_DOINGSOFT; 1958 1959 if (INB(ICR) & MIEN) 1960 val = asy->asy_cached_msr & 0xFF; 1961 else 1962 val = INB(MSR) & 0xFF; 1963 1964 if (async->async_ttycommon.t_cflag & CRTSCTS) { 1965 if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) { 1966 #ifdef DEBUG 1967 if (asydebug & ASY_DEBUG_HFLOW) 1968 printf("asy%d: hflow start\n", 1969 UNIT(async->async_dev)); 1970 #endif 1971 async->async_flags &= ~ASYNC_HW_OUT_FLW; 1972 mutex_exit(asy->asy_excl_hi); 1973 if (async->async_ocnt > 0) { 1974 mutex_enter(asy->asy_excl_hi); 1975 async_resume(async); 1976 mutex_exit(asy->asy_excl_hi); 1977 } else { 1978 async_start(async); 1979 } 1980 mutex_enter(asy->asy_excl_hi); 1981 } 1982 } 1983 if (async->async_ext) { 1984 async->async_ext = 0; 1985 /* check for carrier up */ 1986 if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) { 1987 /* carrier present */ 1988 if ((async->async_flags & ASYNC_CARR_ON) == 0) { 1989 async->async_flags |= ASYNC_CARR_ON; 1990 mutex_exit(asy->asy_excl_hi); 1991 mutex_exit(asy->asy_excl); 1992 if (async->async_flags & ASYNC_ISOPEN) 1993 (void) putctl(q, M_UNHANGUP); 1994 cv_broadcast(&async->async_flags_cv); 1995 mutex_enter(asy->asy_excl); 1996 mutex_enter(asy->asy_excl_hi); 1997 } 1998 } else { 1999 if ((async->async_flags & ASYNC_CARR_ON) && 2000 !(tp->t_cflag & CLOCAL)) { 2001 int flushflag; 2002 2003 /* 2004 * Carrier went away. 2005 * Drop DTR, abort any output in 2006 * progress, indicate that output is 2007 * not stopped, and send a hangup 2008 * notification upstream. 2009 * 2010 * If we're in the midst of close, then flush 2011 * everything. Don't leave stale ioctls lying 2012 * about. 2013 */ 2014 val = INB(MCR); 2015 OUTB(MCR, (val & ~DTR)); 2016 flushflag = (async->async_flags & 2017 ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA; 2018 flushq(tp->t_writeq, flushflag); 2019 if (async->async_xmitblk != NULL) { 2020 freeb(async->async_xmitblk); 2021 async->async_xmitblk = NULL; 2022 } 2023 if (async->async_flags & ASYNC_BUSY) { 2024 async->async_ocnt = 0; 2025 async->async_flags &= ~ASYNC_BUSY; 2026 } 2027 async->async_flags &= ~ASYNC_STOPPED; 2028 if (async->async_flags & ASYNC_ISOPEN) { 2029 mutex_exit(asy->asy_excl_hi); 2030 mutex_exit(asy->asy_excl); 2031 (void) putctl(q, M_HANGUP); 2032 mutex_enter(asy->asy_excl); 2033 mutex_enter(asy->asy_excl_hi); 2034 } 2035 async->async_flags &= ~ASYNC_CARR_ON; 2036 mutex_exit(asy->asy_excl_hi); 2037 cv_broadcast(&async->async_flags_cv); 2038 mutex_enter(asy->asy_excl_hi); 2039 } 2040 } 2041 } 2042 2043 /* 2044 * If data has been added to the circular buffer, remove 2045 * it from the buffer, and send it up the stream if there's 2046 * somebody listening. Try to do it 16 bytes at a time. If we 2047 * have more than 16 bytes to move, move 16 byte chunks and 2048 * leave the rest for next time around (maybe it will grow). 2049 */ 2050 if (!(async->async_flags & ASYNC_ISOPEN)) { 2051 RING_INIT(async); 2052 goto rv; 2053 } 2054 if ((cc = RING_CNT(async)) == 0) { 2055 goto rv; 2056 } 2057 mutex_exit(asy->asy_excl_hi); 2058 2059 if (!canput(q)) { 2060 if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { 2061 #ifdef DEBUG 2062 if (!(asydebug & ASY_DEBUG_HFLOW)) { 2063 printf("asy%d: hflow stop input.\n", 2064 UNIT(async->async_dev)); 2065 if (canputnext(q)) 2066 printf("asy%d: next queue is " 2067 "ready\n", 2068 UNIT(async->async_dev)); 2069 } 2070 #endif 2071 mutex_enter(asy->asy_excl_hi); 2072 async->async_flags |= ASYNC_HW_IN_FLOW; 2073 async->async_flowc = async->async_stopc; 2074 } else mutex_enter(asy->asy_excl_hi); 2075 goto rv; 2076 } 2077 2078 if (async->async_ringbuf_overflow) { 2079 if ((async->async_flags & ASYNC_HW_IN_FLOW) && 2080 ((int)(RING_CNT(async)) < (RINGSIZE/4))) { 2081 #ifdef DEBUG 2082 if (asydebug & ASY_DEBUG_HFLOW) 2083 printf("asy%d: hflow start input.\n", 2084 UNIT(async->async_dev)); 2085 #endif 2086 mutex_enter(asy->asy_excl_hi); 2087 async->async_flags &= ~ASYNC_HW_IN_FLOW; 2088 async->async_flowc = async->async_startc; 2089 async->async_ringbuf_overflow = 0; 2090 goto rv; 2091 } 2092 } 2093 #ifdef DEBUG 2094 if (asydebug & ASY_DEBUG_INPUT) 2095 printf("asy%d: %d char(s) in queue.\n", 2096 UNIT(async->async_dev), cc); 2097 #endif 2098 /* 2099 * Before you pull the characters from the RING BUF 2100 * Check whether you can put into the queue again 2101 */ 2102 if ((!canputnext(q)) || (!canput(q))) { 2103 mutex_enter(asy->asy_excl_hi); 2104 if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { 2105 async->async_flags |= ASYNC_HW_IN_FLOW; 2106 async->async_flowc = async->async_stopc; 2107 async->async_queue_full = 1; 2108 } 2109 goto rv; 2110 } 2111 mutex_enter(asy->asy_excl_hi); 2112 if (async->async_queue_full) { 2113 /* 2114 * Last time the Stream queue didnot allow 2115 * now it allows so, relax, the flow control 2116 */ 2117 if (async->async_flags & ASYNC_HW_IN_FLOW) { 2118 async->async_flags &= ~ASYNC_HW_IN_FLOW; 2119 async->async_queue_full = 0; 2120 async->async_flowc = async->async_startc; 2121 goto rv; 2122 } else 2123 async->async_queue_full = 0; 2124 } 2125 mutex_exit(asy->asy_excl_hi); 2126 if (!(bp = allocb(cc, BPRI_MED))) { 2127 ttycommon_qfull(&async->async_ttycommon, q); 2128 mutex_enter(asy->asy_excl_hi); 2129 goto rv; 2130 } 2131 mutex_enter(asy->asy_excl_hi); 2132 do { 2133 if (RING_ERR(async, S_ERRORS)) { 2134 RING_UNMARK(async); 2135 c = RING_GET(async); 2136 break; 2137 } else { 2138 *bp->b_wptr++ = RING_GET(async); 2139 } 2140 } while (--cc); 2141 2142 mutex_exit(asy->asy_excl_hi); 2143 mutex_exit(asy->asy_excl); 2144 if (bp->b_wptr > bp->b_rptr) { 2145 if (!canputnext(q)) { 2146 if (!canput(q)) { 2147 /* 2148 * Even after taking all precautions that 2149 * Still we are unable to queue, then we 2150 * cannot do anything, just drop the block 2151 */ 2152 cmn_err(CE_NOTE, 2153 "su%d: local queue full\n", 2154 UNIT(async->async_dev)); 2155 freemsg(bp); 2156 mutex_enter(asy->asy_excl_hi); 2157 if ((async->async_flags & 2158 ASYNC_HW_IN_FLOW) == 0) { 2159 async->async_flags |= 2160 ASYNC_HW_IN_FLOW; 2161 async->async_flowc = 2162 async->async_stopc; 2163 async->async_queue_full = 1; 2164 } 2165 mutex_exit(asy->asy_excl_hi); 2166 } else { 2167 (void) putq(q, bp); 2168 } 2169 } else { 2170 putnext(q, bp); 2171 } 2172 } else { 2173 freemsg(bp); 2174 } 2175 /* 2176 * If we have a parity error, then send 2177 * up an M_BREAK with the "bad" 2178 * character as an argument. Let ldterm 2179 * figure out what to do with the error. 2180 */ 2181 if (cc) 2182 (void) putctl1(q, M_BREAK, c); 2183 mutex_enter(asy->asy_excl); 2184 mutex_enter(asy->asy_excl_hi); 2185 rv: 2186 /* 2187 * If a transmission has finished, indicate that it's finished, 2188 * and start that line up again. 2189 */ 2190 if (async->async_break) { 2191 async->async_break = 0; 2192 if (async->async_flags & ASYNC_ISOPEN) { 2193 mutex_exit(asy->asy_excl_hi); 2194 mutex_exit(asy->asy_excl); 2195 (void) putctl(q, M_BREAK); 2196 mutex_enter(asy->asy_excl); 2197 mutex_enter(asy->asy_excl_hi); 2198 } 2199 } 2200 if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) || 2201 (async->async_flowc != '\0')) { 2202 async->async_flags &= ~ASYNC_BUSY; 2203 mutex_exit(asy->asy_excl_hi); 2204 if (async->async_xmitblk) 2205 freeb(async->async_xmitblk); 2206 async->async_xmitblk = NULL; 2207 if (async->async_flags & ASYNC_ISOPEN) { 2208 asy->inperim = B_TRUE; 2209 mutex_exit(asy->asy_excl); 2210 enterq(async->async_ttycommon.t_writeq); 2211 mutex_enter(asy->asy_excl); 2212 } 2213 async_start(async); 2214 /* 2215 * We need to check for inperim and ISOPEN due to 2216 * multi-threading implications; it's possible to close the 2217 * port and nullify async_flags while completing the software 2218 * interrupt. If the port is closed, leaveq() will have already 2219 * been called. We don't want to call it twice. 2220 */ 2221 if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) { 2222 mutex_exit(asy->asy_excl); 2223 leaveq(async->async_ttycommon.t_writeq); 2224 mutex_enter(asy->asy_excl); 2225 asy->inperim = B_FALSE; 2226 } 2227 if (!(async->async_flags & ASYNC_BUSY)) 2228 cv_broadcast(&async->async_flags_cv); 2229 mutex_enter(asy->asy_excl_hi); 2230 } 2231 /* 2232 * A note about these overrun bits: all they do is *tell* someone 2233 * about an error- They do not track multiple errors. In fact, 2234 * you could consider them latched register bits if you like. 2235 * We are only interested in printing the error message once for 2236 * any cluster of overrun errrors. 2237 */ 2238 if (async->async_hw_overrun) { 2239 if (async->async_flags & ASYNC_ISOPEN) { 2240 if (su_log > 0) { 2241 mutex_exit(asy->asy_excl_hi); 2242 mutex_exit(asy->asy_excl); 2243 cmn_err(CE_NOTE, "su%d: silo overflow\n", 2244 UNIT(async->async_dev)); 2245 mutex_enter(asy->asy_excl); 2246 mutex_enter(asy->asy_excl_hi); 2247 } 2248 INC64_KSTAT(asy, siloover); 2249 } 2250 async->async_hw_overrun = 0; 2251 } 2252 if (async->async_sw_overrun) { 2253 if (async->async_flags & ASYNC_ISOPEN) { 2254 if (su_log > 0) { 2255 mutex_exit(asy->asy_excl_hi); 2256 mutex_exit(asy->asy_excl); 2257 cmn_err(CE_NOTE, "su%d: ring buffer overflow\n", 2258 UNIT(async->async_dev)); 2259 mutex_enter(asy->asy_excl); 2260 mutex_enter(asy->asy_excl_hi); 2261 } 2262 INC64_KSTAT(asy, ringover); 2263 } 2264 async->async_sw_overrun = 0; 2265 } 2266 asy->asy_flags &= ~ASY_DOINGSOFT; 2267 mutex_exit(asy->asy_excl_hi); 2268 mutex_exit(asy->asy_excl); 2269 if (q != NULL) 2270 leaveq(q); 2271 return (0); 2272 } 2273 2274 /* 2275 * Restart output on a line after a delay or break timer expired. 2276 */ 2277 static void 2278 async_restart(void *arg) 2279 { 2280 struct asyncline *async = arg; 2281 struct asycom *asy = async->async_common; 2282 queue_t *q; 2283 uchar_t lcr; 2284 2285 /* 2286 * If break timer expired, turn off the break bit. 2287 */ 2288 #ifdef DEBUG 2289 if (asydebug & ASY_DEBUG_PROCS) 2290 printf("restart\n"); 2291 #endif 2292 mutex_enter(asy->asy_excl); 2293 if (async->async_flags & ASYNC_BREAK) { 2294 unsigned int rate; 2295 2296 mutex_enter(asy->asy_excl_hi); 2297 lcr = INB(LCR); 2298 OUTB(LCR, (lcr & ~SETBREAK)); 2299 2300 /* 2301 * Go to sleep for the time it takes for at least one 2302 * stop bit to be received by the device at the other 2303 * end of the line as stated in the RS-232 specification. 2304 * The wait period is equal to: 2305 * 2 clock cycles * (1 MICROSEC / baud rate) 2306 */ 2307 rate = async->async_ttycommon.t_cflag & CBAUD; 2308 if (async->async_ttycommon.t_cflag & CBAUDEXT) 2309 rate += 16; 2310 if (rate >= N_SU_SPEEDS || rate == B0) { 2311 rate = B9600; 2312 } 2313 2314 mutex_exit(asy->asy_excl_hi); 2315 mutex_exit(asy->asy_excl); 2316 drv_usecwait(2 * MICROSEC / baudtable[rate]); 2317 mutex_enter(asy->asy_excl); 2318 } 2319 async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING); 2320 if ((q = async->async_ttycommon.t_writeq) != NULL) { 2321 mutex_exit(asy->asy_excl); 2322 enterq(q); 2323 mutex_enter(asy->asy_excl); 2324 } 2325 async_start(async); 2326 mutex_exit(asy->asy_excl); 2327 if (q != NULL) 2328 leaveq(q); 2329 2330 /* cleared break or delay flag; may have made some output progress */ 2331 cv_broadcast(&async->async_flags_cv); 2332 } 2333 2334 static void 2335 async_start(struct asyncline *async) 2336 { 2337 async_nstart(async, 0); 2338 } 2339 2340 /* 2341 * Start output on a line, unless it's busy, frozen, or otherwise. 2342 */ 2343 static void 2344 async_nstart(struct asyncline *async, int mode) 2345 { 2346 register struct asycom *asy = async->async_common; 2347 register int cc; 2348 register queue_t *q; 2349 mblk_t *bp, *nbp; 2350 uchar_t *xmit_addr; 2351 uchar_t val; 2352 int fifo_len = 1; 2353 int xmit_progress; 2354 2355 #ifdef DEBUG 2356 if (asydebug & ASY_DEBUG_PROCS) 2357 printf("start\n"); 2358 #endif 2359 if (asy->asy_use_fifo == FIFO_ON) 2360 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 2361 2362 ASSERT(mutex_owned(asy->asy_excl)); 2363 mutex_enter(asy->asy_excl_hi); 2364 asycheckflowcontrol_hw(asy); 2365 2366 /* 2367 * If the chip is busy (i.e., we're waiting for a break timeout 2368 * to expire, or for the current transmission to finish, or for 2369 * output to finish draining from chip), don't grab anything new. 2370 */ 2371 if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) { 2372 mutex_exit(asy->asy_excl_hi); 2373 #ifdef DEBUG 2374 if (mode && asydebug & ASY_DEBUG_CLOSE) 2375 printf("asy%d: start %s.\n", 2376 UNIT(async->async_dev), 2377 async->async_flags & ASYNC_BREAK 2378 ? "break" : "busy"); 2379 #endif 2380 return; 2381 } 2382 2383 /* 2384 * If we have a flow-control character to transmit, do it now. 2385 */ 2386 if (asycheckflowcontrol_sw(asy)) { 2387 mutex_exit(asy->asy_excl_hi); 2388 return; 2389 } 2390 mutex_exit(asy->asy_excl_hi); 2391 /* 2392 * If we're waiting for a delay timeout to expire, don't grab 2393 * anything new. 2394 */ 2395 if (async->async_flags & ASYNC_DELAY) { 2396 #ifdef DEBUG 2397 if (mode && asydebug & ASY_DEBUG_CLOSE) 2398 printf("asy%d: start ASYNC_DELAY.\n", 2399 UNIT(async->async_dev)); 2400 #endif 2401 return; 2402 } 2403 2404 if ((q = async->async_ttycommon.t_writeq) == NULL) { 2405 #ifdef DEBUG 2406 if (mode && asydebug & ASY_DEBUG_CLOSE) 2407 printf("asy%d: start writeq is null.\n", 2408 UNIT(async->async_dev)); 2409 #endif 2410 return; /* not attached to a stream */ 2411 } 2412 2413 for (;;) { 2414 if ((bp = getq(q)) == NULL) 2415 return; /* no data to transmit */ 2416 2417 /* 2418 * We have a message block to work on. 2419 * Check whether it's a break, a delay, or an ioctl (the latter 2420 * occurs if the ioctl in question was waiting for the output 2421 * to drain). If it's one of those, process it immediately. 2422 */ 2423 switch (bp->b_datap->db_type) { 2424 2425 case M_BREAK: 2426 /* 2427 * Set the break bit, and arrange for "async_restart" 2428 * to be called in 1/4 second; it will turn the 2429 * break bit off, and call "async_start" to grab 2430 * the next message. 2431 */ 2432 mutex_enter(asy->asy_excl_hi); 2433 val = INB(LCR); 2434 OUTB(LCR, (val | SETBREAK)); 2435 mutex_exit(asy->asy_excl_hi); 2436 async->async_flags |= ASYNC_BREAK; 2437 (void) timeout(async_restart, async, hz / 4); 2438 freemsg(bp); 2439 return; /* wait for this to finish */ 2440 2441 case M_DELAY: 2442 /* 2443 * Arrange for "async_restart" to be called when the 2444 * delay expires; it will turn ASYNC_DELAY off, 2445 * and call "async_start" to grab the next message. 2446 */ 2447 (void) timeout(async_restart, async, 2448 (clock_t)(*(unsigned char *)bp->b_rptr + 6)); 2449 async->async_flags |= ASYNC_DELAY; 2450 freemsg(bp); 2451 return; /* wait for this to finish */ 2452 2453 case M_IOCTL: 2454 /* 2455 * This ioctl needs to wait for the output ahead of 2456 * it to drain. Try to do it, and then either 2457 * redo the ioctl at a later time or grab the next 2458 * message after it. 2459 */ 2460 2461 mutex_enter(asy->asy_excl_hi); 2462 if (asy_isbusy(asy)) { 2463 /* 2464 * Get the divisor by calculating the rate 2465 */ 2466 unsigned int rate; 2467 2468 mutex_exit(asy->asy_excl_hi); 2469 rate = async->async_ttycommon.t_cflag & CBAUD; 2470 if (async->async_ttycommon.t_cflag & CBAUDEXT) 2471 rate += 16; 2472 if (rate >= N_SU_SPEEDS || rate == B0) { 2473 rate = B9600; 2474 } 2475 2476 /* 2477 * We need to do a callback as the port will 2478 * be set to drain 2479 */ 2480 async->async_flags |= ASYNC_DRAINING; 2481 2482 /* 2483 * Put the message we just processed back onto 2484 * the end of the queue 2485 */ 2486 if (putq(q, bp) == 0) 2487 freemsg(bp); 2488 2489 /* 2490 * We need to delay until the TSR and THR 2491 * have been exhausted. We base the delay on 2492 * the amount of time it takes to transmit 2493 * 2 chars at the current baud rate in 2494 * microseconds. 2495 * 2496 * Therefore, the wait period is: 2497 * 2498 * (#TSR bits + #THR bits) * 2499 * 1 MICROSEC / baud rate 2500 */ 2501 (void) timeout(async_restart, async, 2502 drv_usectohz(16 * MICROSEC / 2503 baudtable[rate])); 2504 return; 2505 } 2506 mutex_exit(asy->asy_excl_hi); 2507 mutex_exit(asy->asy_excl); 2508 async_ioctl(async, q, bp, B_FALSE); 2509 mutex_enter(asy->asy_excl); 2510 continue; 2511 } 2512 2513 while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) { 2514 nbp = bp->b_cont; 2515 freeb(bp); 2516 bp = nbp; 2517 } 2518 if (bp != NULL) 2519 break; 2520 } 2521 2522 /* 2523 * We have data to transmit. If output is stopped, put 2524 * it back and try again later. 2525 */ 2526 if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) { 2527 #ifdef DEBUG 2528 if (asydebug & ASY_DEBUG_HFLOW && 2529 async->async_flags & ASYNC_HW_OUT_FLW) 2530 printf("asy%d: output hflow in effect.\n", 2531 UNIT(async->async_dev)); 2532 #endif 2533 mutex_exit(asy->asy_excl); 2534 (void) putbq(q, bp); 2535 /* 2536 * We entered the routine owning the lock, we need to 2537 * exit the routine owning the lock. 2538 */ 2539 mutex_enter(asy->asy_excl); 2540 return; 2541 } 2542 2543 async->async_xmitblk = bp; 2544 xmit_addr = bp->b_rptr; 2545 bp = bp->b_cont; 2546 if (bp != NULL) { 2547 mutex_exit(asy->asy_excl); 2548 (void) putbq(q, bp); /* not done with this message yet */ 2549 mutex_enter(asy->asy_excl); 2550 } 2551 2552 /* 2553 * In 5-bit mode, the high order bits are used 2554 * to indicate character sizes less than five, 2555 * so we need to explicitly mask before transmitting 2556 */ 2557 if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { 2558 register unsigned char *p = xmit_addr; 2559 register int cnt = cc; 2560 2561 while (cnt--) 2562 *p++ &= (unsigned char) 0x1f; 2563 } 2564 2565 /* 2566 * Set up this block for pseudo-DMA. 2567 */ 2568 mutex_enter(asy->asy_excl_hi); 2569 async->async_optr = xmit_addr; 2570 async->async_ocnt = cc; 2571 /* 2572 * If the transmitter is ready, shove some 2573 * characters out. 2574 */ 2575 xmit_progress = 0; 2576 while (fifo_len-- && async->async_ocnt) { 2577 if (INB(LSR) & XHRE) { 2578 OUTB(DAT, *async->async_optr++); 2579 async->async_ocnt--; 2580 xmit_progress++; 2581 } 2582 } 2583 asy->asy_out_of_band_xmit = xmit_progress; 2584 if (xmit_progress > 0) 2585 async->async_flags |= ASYNC_PROGRESS; 2586 async->async_flags |= ASYNC_BUSY; 2587 mutex_exit(asy->asy_excl_hi); 2588 } 2589 2590 /* 2591 * Resume output by poking the transmitter. 2592 */ 2593 static void 2594 async_resume(struct asyncline *async) 2595 { 2596 register struct asycom *asy = async->async_common; 2597 2598 ASSERT(mutex_owned(asy->asy_excl_hi)); 2599 #ifdef DEBUG 2600 if (asydebug & ASY_DEBUG_PROCS) 2601 printf("resume\n"); 2602 #endif 2603 2604 asycheckflowcontrol_hw(asy); 2605 2606 if (INB(LSR) & XHRE) { 2607 if (asycheckflowcontrol_sw(asy)) { 2608 return; 2609 } else if (async->async_ocnt > 0) { 2610 OUTB(DAT, *async->async_optr++); 2611 async->async_ocnt--; 2612 async->async_flags |= ASYNC_PROGRESS; 2613 } 2614 } 2615 } 2616 2617 /* 2618 * Process an "ioctl" message sent down to us. 2619 * Note that we don't need to get any locks until we are ready to access 2620 * the hardware. Nothing we access until then is going to be altered 2621 * outside of the STREAMS framework, so we should be safe. 2622 */ 2623 static void 2624 async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput) 2625 { 2626 register struct asycom *asy = async->async_common; 2627 register tty_common_t *tp = &async->async_ttycommon; 2628 register struct iocblk *iocp; 2629 register unsigned datasize; 2630 size_t ioc_count; 2631 mblk_t *datamp; 2632 int error = 0; 2633 uchar_t val, icr; 2634 #ifdef DEBUG 2635 if (asydebug & ASY_DEBUG_PROCS) 2636 printf("ioctl\n"); 2637 #endif 2638 2639 if (tp->t_iocpending != NULL) { 2640 /* 2641 * We were holding an "ioctl" response pending the 2642 * availability of an "mblk" to hold data to be passed up; 2643 * another "ioctl" came through, which means that "ioctl" 2644 * must have timed out or been aborted. 2645 */ 2646 freemsg(async->async_ttycommon.t_iocpending); 2647 async->async_ttycommon.t_iocpending = NULL; 2648 } 2649 2650 iocp = (struct iocblk *)mp->b_rptr; 2651 2652 /* 2653 * Save off the ioc count in case we need to restore it 2654 * because we are queuing a message block. 2655 */ 2656 ioc_count = iocp->ioc_count; 2657 2658 /* 2659 * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call 2660 * ttycommon_ioctl() because this function frees up the message block 2661 * (mp->b_cont) that contains the address of the user variable where 2662 * we need to pass back the bit array. 2663 */ 2664 if (iocp->ioc_cmd == TIOCMGET || 2665 iocp->ioc_cmd == TIOCMBIC || 2666 iocp->ioc_cmd == TIOCMBIS || 2667 iocp->ioc_cmd == TIOCMSET || 2668 iocp->ioc_cmd == TIOCGPPS || 2669 iocp->ioc_cmd == TIOCSPPS || 2670 iocp->ioc_cmd == TIOCGPPSEV) 2671 error = -1; /* Do Nothing */ 2672 else 2673 2674 /* 2675 * The only way in which "ttycommon_ioctl" can fail is if the "ioctl" 2676 * requires a response containing data to be returned to the user, 2677 * and no mblk could be allocated for the data. 2678 * No such "ioctl" alters our state. Thus, we always go ahead and 2679 * do any state-changes the "ioctl" calls for. If we couldn't allocate 2680 * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so 2681 * we just call "bufcall" to request that we be called back when we 2682 * stand a better chance of allocating the data. 2683 */ 2684 if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { 2685 if (async->async_wbufcid) 2686 unbufcall(async->async_wbufcid); 2687 async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl, 2688 async); 2689 return; 2690 } 2691 2692 mutex_enter(asy->asy_excl); 2693 2694 if (error == 0) { 2695 /* 2696 * "ttycommon_ioctl" did most of the work; we just use the 2697 * data it set up. 2698 */ 2699 switch (iocp->ioc_cmd) { 2700 2701 case TCSETS: 2702 if (!(asy->asy_rsc_console || asy->asy_rsc_control || 2703 asy->asy_lom_console)) { 2704 mutex_enter(asy->asy_excl_hi); 2705 error = asy_program(asy, ASY_NOINIT); 2706 mutex_exit(asy->asy_excl_hi); 2707 } 2708 break; 2709 case TCSETSF: 2710 case TCSETSW: 2711 case TCSETA: 2712 case TCSETAW: 2713 case TCSETAF: 2714 if (!(asy->asy_rsc_console || asy->asy_rsc_control || 2715 asy->asy_lom_console)) { 2716 mutex_enter(asy->asy_excl_hi); 2717 if (iswput && asy_isbusy(asy)) { 2718 /* 2719 * ttycommon_ioctl sets the db_type to 2720 * M_IOCACK and ioc_count to zero 2721 * we need to undo this when we 2722 * queue a control message. This will 2723 * allow the control messages to be 2724 * processed again when the chip 2725 * becomes available. 2726 */ 2727 mp->b_datap->db_type = M_IOCTL; 2728 iocp->ioc_count = ioc_count; 2729 2730 if (putq(wq, mp) == 0) 2731 freemsg(mp); 2732 mutex_exit(asy->asy_excl_hi); 2733 mutex_exit(asy->asy_excl); 2734 return; 2735 } 2736 error = asy_program(asy, ASY_NOINIT); 2737 mutex_exit(asy->asy_excl_hi); 2738 } 2739 break; 2740 case TIOCSSOFTCAR: 2741 /* Set the driver state appropriately */ 2742 mutex_enter(asy->asy_excl_hi); 2743 if (tp->t_flags & TS_SOFTCAR) 2744 asy->asy_flags |= ASY_IGNORE_CD; 2745 else 2746 asy->asy_flags &= ~ASY_IGNORE_CD; 2747 mutex_exit(asy->asy_excl_hi); 2748 break; 2749 } 2750 } else if (error < 0) { 2751 /* 2752 * "ttycommon_ioctl" didn't do anything; we process it here. 2753 */ 2754 error = 0; 2755 switch (iocp->ioc_cmd) { 2756 2757 case TIOCGPPS: 2758 /* 2759 * Get PPS on/off. 2760 */ 2761 if (mp->b_cont != NULL) 2762 freemsg(mp->b_cont); 2763 2764 mp->b_cont = allocb(sizeof (int), BPRI_HI); 2765 if (mp->b_cont == NULL) { 2766 error = ENOMEM; 2767 break; 2768 } 2769 if (asy->asy_flags & ASY_PPS) 2770 *(int *)mp->b_cont->b_wptr = 1; 2771 else 2772 *(int *)mp->b_cont->b_wptr = 0; 2773 mp->b_cont->b_wptr += sizeof (int); 2774 mp->b_datap->db_type = M_IOCACK; 2775 iocp->ioc_count = sizeof (int); 2776 break; 2777 2778 case TIOCSPPS: 2779 /* 2780 * Set PPS on/off. 2781 */ 2782 error = miocpullup(mp, sizeof (int)); 2783 if (error != 0) 2784 break; 2785 2786 mutex_enter(asy->asy_excl_hi); 2787 if (*(int *)mp->b_cont->b_rptr) 2788 asy->asy_flags |= ASY_PPS; 2789 else 2790 asy->asy_flags &= ~ASY_PPS; 2791 /* Reset edge sense */ 2792 asy->asy_flags &= ~ASY_PPS_EDGE; 2793 mutex_exit(asy->asy_excl_hi); 2794 mp->b_datap->db_type = M_IOCACK; 2795 break; 2796 2797 case TIOCGPPSEV: { 2798 /* 2799 * Get PPS event data. 2800 */ 2801 mblk_t *bp; 2802 void *buf; 2803 #ifdef _SYSCALL32_IMPL 2804 struct ppsclockev32 p32; 2805 #endif 2806 struct ppsclockev ppsclockev; 2807 2808 if (mp->b_cont != NULL) { 2809 freemsg(mp->b_cont); 2810 mp->b_cont = NULL; 2811 } 2812 2813 if ((asy->asy_flags & ASY_PPS) == 0) { 2814 error = ENXIO; 2815 break; 2816 } 2817 2818 /* Protect from incomplete asy_ppsev */ 2819 mutex_enter(asy->asy_excl_hi); 2820 ppsclockev = asy_ppsev; 2821 mutex_exit(asy->asy_excl_hi); 2822 2823 #ifdef _SYSCALL32_IMPL 2824 if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { 2825 TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); 2826 p32.serial = ppsclockev.serial; 2827 buf = &p32; 2828 iocp->ioc_count = sizeof (struct ppsclockev32); 2829 } else 2830 #endif 2831 { 2832 buf = &ppsclockev; 2833 iocp->ioc_count = sizeof (struct ppsclockev); 2834 } 2835 2836 if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { 2837 error = ENOMEM; 2838 break; 2839 } 2840 mp->b_cont = bp; 2841 2842 bcopy(buf, bp->b_wptr, iocp->ioc_count); 2843 bp->b_wptr += iocp->ioc_count; 2844 mp->b_datap->db_type = M_IOCACK; 2845 break; 2846 } 2847 2848 case TCSBRK: 2849 error = miocpullup(mp, sizeof (int)); 2850 if (error != 0) 2851 break; 2852 2853 mutex_enter(asy->asy_excl_hi); 2854 if (*(int *)mp->b_cont->b_rptr == 0) { 2855 /* 2856 * Get the divisor by calculating the rate 2857 */ 2858 unsigned int rate, divisor; 2859 rate = async->async_ttycommon.t_cflag & CBAUD; 2860 if (async->async_ttycommon.t_cflag & CBAUDEXT) 2861 rate += 16; 2862 if (rate >= N_SU_SPEEDS) rate = B9600; 2863 divisor = asyspdtab[rate] & 0xfff; 2864 2865 /* 2866 * To ensure that erroneous characters are 2867 * not sent out when the break is set, SB 2868 * recommends three steps: 2869 * 2870 * 1) pad the TSR with 0 bits 2871 * 2) When the TSR is full, set break 2872 * 3) When the TSR has been flushed, unset 2873 * the break when transmission must be 2874 * restored. 2875 * 2876 * We loop until the TSR is empty and then 2877 * set the break. ASYNC_BREAK has been set 2878 * to ensure that no characters are 2879 * transmitted while the TSR is being 2880 * flushed and SOUT is being used for the 2881 * break signal. 2882 * 2883 * The wait period is equal to 2884 * clock / (baud * 16) * 16 * 2. 2885 */ 2886 async->async_flags |= ASYNC_BREAK; 2887 while ((INB(LSR) & XSRE) == 0) { 2888 mutex_exit(asy->asy_excl_hi); 2889 mutex_exit(asy->asy_excl); 2890 drv_usecwait(32*divisor); 2891 mutex_enter(asy->asy_excl); 2892 mutex_enter(asy->asy_excl_hi); 2893 } 2894 2895 /* 2896 * Set the break bit, and arrange for 2897 * "async_restart" to be called in 1/4 second; 2898 * it will turn the break bit off, and call 2899 * "async_start" to grab the next message. 2900 */ 2901 val = INB(LCR); 2902 OUTB(LCR, (val | SETBREAK)); 2903 mutex_exit(asy->asy_excl_hi); 2904 (void) timeout(async_restart, async, hz / 4); 2905 } else { 2906 #ifdef DEBUG 2907 if (asydebug & ASY_DEBUG_CLOSE) 2908 printf("asy%d: wait for flush.\n", 2909 UNIT(async->async_dev)); 2910 #endif 2911 if (iswput && asy_isbusy(asy)) { 2912 if (putq(wq, mp) == 0) 2913 freemsg(mp); 2914 mutex_exit(asy->asy_excl_hi); 2915 mutex_exit(asy->asy_excl); 2916 return; 2917 } 2918 mutex_exit(asy->asy_excl_hi); 2919 #ifdef DEBUG 2920 if (asydebug & ASY_DEBUG_CLOSE) 2921 printf("asy%d: ldterm satisfied.\n", 2922 UNIT(async->async_dev)); 2923 #endif 2924 } 2925 break; 2926 2927 case TIOCSBRK: 2928 mutex_enter(asy->asy_excl_hi); 2929 val = INB(LCR); 2930 OUTB(LCR, (val | SETBREAK)); 2931 mutex_exit(asy->asy_excl_hi); 2932 mutex_exit(asy->asy_excl); 2933 miocack(wq, mp, 0, 0); 2934 return; 2935 2936 case TIOCCBRK: 2937 mutex_enter(asy->asy_excl_hi); 2938 val = INB(LCR); 2939 OUTB(LCR, (val & ~SETBREAK)); 2940 mutex_exit(asy->asy_excl_hi); 2941 mutex_exit(asy->asy_excl); 2942 miocack(wq, mp, 0, 0); 2943 return; 2944 2945 case TIOCMSET: 2946 case TIOCMBIS: 2947 case TIOCMBIC: 2948 if (iocp->ioc_count == TRANSPARENT) 2949 mcopyin(mp, NULL, sizeof (int), NULL); 2950 else { 2951 error = miocpullup(mp, sizeof (int)); 2952 if (error != 0) 2953 break; 2954 2955 mutex_enter(asy->asy_excl_hi); 2956 2957 (void) asymctl(asy, 2958 dmtoasy(*(int *)mp->b_cont->b_rptr), 2959 iocp->ioc_cmd); 2960 2961 mutex_exit(asy->asy_excl_hi); 2962 iocp->ioc_error = 0; 2963 mp->b_datap->db_type = M_IOCACK; 2964 } 2965 break; 2966 2967 case TIOCSILOOP: 2968 mutex_enter(asy->asy_excl_hi); 2969 /* 2970 * If somebody misues this Ioctl when used for 2971 * driving keyboard and mouse indicate not supported 2972 */ 2973 if ((asy->asy_device_type == ASY_KEYBOARD) || 2974 (asy->asy_device_type == ASY_MOUSE)) { 2975 mutex_exit(asy->asy_excl_hi); 2976 error = ENOTTY; 2977 break; 2978 } 2979 2980 /* should not use when we're the console */ 2981 if ((async->async_dev == kbddev) || 2982 (async->async_dev == rconsdev) || 2983 (async->async_dev == stdindev)) { 2984 mutex_exit(asy->asy_excl_hi); 2985 error = EINVAL; 2986 break; 2987 } 2988 2989 val = INB(MCR); 2990 icr = INB(ICR); 2991 /* 2992 * Disable the Modem Status Interrupt 2993 * The reason for disabling is the status of 2994 * modem signal are in the higher 4 bits instead of 2995 * lower four bits when in loopback mode, 2996 * so, donot worry about Modem interrupt when 2997 * you are planning to set 2998 * this in loopback mode until it is cleared by 2999 * another ioctl to get out of the loopback mode 3000 */ 3001 OUTB(ICR, icr & ~ MIEN); 3002 OUTB(MCR, val | ASY_LOOP); 3003 mutex_exit(asy->asy_excl_hi); 3004 iocp->ioc_error = 0; 3005 mp->b_datap->db_type = M_IOCACK; 3006 break; 3007 3008 case TIOCMGET: 3009 datamp = allocb(sizeof (int), BPRI_MED); 3010 if (datamp == NULL) { 3011 error = EAGAIN; 3012 break; 3013 } 3014 3015 mutex_enter(asy->asy_excl_hi); 3016 *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); 3017 mutex_exit(asy->asy_excl_hi); 3018 3019 if (iocp->ioc_count == TRANSPARENT) { 3020 mcopyout(mp, NULL, sizeof (int), NULL, datamp); 3021 } else { 3022 if (mp->b_cont != NULL) 3023 freemsg(mp->b_cont); 3024 mp->b_cont = datamp; 3025 mp->b_cont->b_wptr += sizeof (int); 3026 mp->b_datap->db_type = M_IOCACK; 3027 iocp->ioc_count = sizeof (int); 3028 } 3029 break; 3030 3031 default: /* unexpected ioctl type */ 3032 /* 3033 * If we don't understand it, it's an error. NAK it. 3034 */ 3035 error = EINVAL; 3036 break; 3037 } 3038 } 3039 if (error != 0) { 3040 iocp->ioc_error = error; 3041 mp->b_datap->db_type = M_IOCNAK; 3042 } 3043 mutex_exit(asy->asy_excl); 3044 qreply(wq, mp); 3045 } 3046 3047 static void 3048 asyrsrv(queue_t *q) 3049 { 3050 mblk_t *bp; 3051 struct asyncline *async; 3052 3053 async = (struct asyncline *)q->q_ptr; 3054 3055 while (canputnext(q) && (bp = getq(q))) 3056 putnext(q, bp); 3057 ASYSETSOFT(async->async_common); 3058 async->async_polltid = 0; 3059 } 3060 3061 /* 3062 * Put procedure for write queue. 3063 * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; 3064 * set the flow control character for M_STOPI and M_STARTI messages; 3065 * queue up M_BREAK, M_DELAY, and M_DATA messages for processing 3066 * by the start routine, and then call the start routine; discard 3067 * everything else. Note that this driver does not incorporate any 3068 * mechanism to negotiate to handle the canonicalization process. 3069 * It expects that these functions are handled in upper module(s), 3070 * as we do in ldterm. 3071 */ 3072 static void 3073 asywput(queue_t *q, mblk_t *mp) 3074 { 3075 register struct asyncline *async; 3076 register struct asycom *asy; 3077 int error; 3078 3079 async = (struct asyncline *)q->q_ptr; 3080 asy = async->async_common; 3081 3082 switch (mp->b_datap->db_type) { 3083 3084 case M_STOP: 3085 /* 3086 * Since we don't do real DMA, we can just let the 3087 * chip coast to a stop after applying the brakes. 3088 */ 3089 mutex_enter(asy->asy_excl); 3090 async->async_flags |= ASYNC_STOPPED; 3091 mutex_exit(asy->asy_excl); 3092 freemsg(mp); 3093 break; 3094 3095 case M_START: 3096 mutex_enter(asy->asy_excl); 3097 if (async->async_flags & ASYNC_STOPPED) { 3098 async->async_flags &= ~ASYNC_STOPPED; 3099 /* 3100 * If an output operation is in progress, 3101 * resume it. Otherwise, prod the start 3102 * routine. 3103 */ 3104 if (async->async_ocnt > 0) { 3105 mutex_enter(asy->asy_excl_hi); 3106 async_resume(async); 3107 mutex_exit(asy->asy_excl_hi); 3108 } else { 3109 async_start(async); 3110 } 3111 } 3112 mutex_exit(asy->asy_excl); 3113 freemsg(mp); 3114 break; 3115 3116 case M_IOCTL: 3117 switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 3118 3119 case TCSBRK: 3120 error = miocpullup(mp, sizeof (int)); 3121 if (error != 0) { 3122 miocnak(q, mp, 0, error); 3123 return; 3124 } 3125 3126 if (*(int *)mp->b_cont->b_rptr != 0) { 3127 #ifdef DEBUG 3128 if (asydebug & ASY_DEBUG_CLOSE) 3129 printf("asy%d: flush request.\n", 3130 UNIT(async->async_dev)); 3131 #endif 3132 (void) putq(q, mp); 3133 mutex_enter(asy->asy_excl); 3134 async_nstart(async, 1); 3135 mutex_exit(asy->asy_excl); 3136 break; 3137 } 3138 /*FALLTHROUGH*/ 3139 case TCSETSW: 3140 case TCSETSF: 3141 case TCSETAW: 3142 case TCSETAF: 3143 /* 3144 * The changes do not take effect until all 3145 * output queued before them is drained. 3146 * Put this message on the queue, so that 3147 * "async_start" will see it when it's done 3148 * with the output before it. Poke the 3149 * start routine, just in case. 3150 */ 3151 (void) putq(q, mp); 3152 mutex_enter(asy->asy_excl); 3153 async_start(async); 3154 mutex_exit(asy->asy_excl); 3155 break; 3156 3157 default: 3158 /* 3159 * Do it now. 3160 */ 3161 async_ioctl(async, q, mp, B_TRUE); 3162 break; 3163 } 3164 break; 3165 3166 case M_FLUSH: 3167 if (*mp->b_rptr & FLUSHW) { 3168 mutex_enter(asy->asy_excl); 3169 3170 /* 3171 * Abort any output in progress. 3172 */ 3173 mutex_enter(asy->asy_excl_hi); 3174 if (async->async_flags & ASYNC_BUSY) { 3175 async->async_ocnt = 0; 3176 async->async_flags &= ~ASYNC_BUSY; 3177 } 3178 mutex_exit(asy->asy_excl_hi); 3179 3180 /* Flush FIFO buffers */ 3181 if (asy->asy_use_fifo == FIFO_ON) { 3182 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | 3183 (asy->asy_trig_level & 0xff)); 3184 } 3185 3186 /* 3187 * Flush our write queue. 3188 */ 3189 flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ 3190 if (async->async_xmitblk != NULL) { 3191 freeb(async->async_xmitblk); 3192 async->async_xmitblk = NULL; 3193 } 3194 3195 mutex_exit(asy->asy_excl); 3196 *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ 3197 } 3198 if (*mp->b_rptr & FLUSHR) { 3199 /* Flush FIFO buffers */ 3200 if (asy->asy_use_fifo == FIFO_ON) { 3201 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | 3202 (asy->asy_trig_level & 0xff)); 3203 } 3204 flushq(RD(q), FLUSHDATA); 3205 qreply(q, mp); /* give the read queues a crack at it */ 3206 } else { 3207 freemsg(mp); 3208 } 3209 3210 /* 3211 * We must make sure we process messages that survive the 3212 * write-side flush. Without this call, the close protocol 3213 * with ldterm can hang forever. (ldterm will have sent us a 3214 * TCSBRK ioctl that it expects a response to.) 3215 */ 3216 mutex_enter(asy->asy_excl); 3217 async_start(async); 3218 mutex_exit(asy->asy_excl); 3219 break; 3220 case M_BREAK: 3221 case M_DELAY: 3222 case M_DATA: 3223 /* 3224 * Queue the message up to be transmitted, 3225 * and poke the start routine. 3226 */ 3227 (void) putq(q, mp); 3228 mutex_enter(asy->asy_excl); 3229 async_start(async); 3230 mutex_exit(asy->asy_excl); 3231 break; 3232 3233 case M_STOPI: 3234 mutex_enter(asy->asy_excl); 3235 async->async_flowc = async->async_stopc; 3236 async_start(async); /* poke the start routine */ 3237 mutex_exit(asy->asy_excl); 3238 freemsg(mp); 3239 break; 3240 3241 case M_STARTI: 3242 mutex_enter(asy->asy_excl); 3243 async->async_flowc = async->async_startc; 3244 async_start(async); /* poke the start routine */ 3245 mutex_exit(asy->asy_excl); 3246 freemsg(mp); 3247 break; 3248 3249 case M_CTL: 3250 if (MBLKL(mp) >= sizeof (struct iocblk) && 3251 ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { 3252 ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX; 3253 qreply(q, mp); 3254 } else { 3255 /* 3256 * These MC_SERVICE type messages are used by upper 3257 * modules to tell this driver to send input up 3258 * immediately, or that it can wait for normal 3259 * processing that may or may not be done. Sun 3260 * requires these for the mouse module. 3261 * (XXX - for x86?) 3262 */ 3263 mutex_enter(asy->asy_excl); 3264 switch (*mp->b_rptr) { 3265 3266 case MC_SERVICEIMM: 3267 async->async_flags |= ASYNC_SERVICEIMM; 3268 break; 3269 3270 case MC_SERVICEDEF: 3271 async->async_flags &= ~ASYNC_SERVICEIMM; 3272 break; 3273 } 3274 mutex_exit(asy->asy_excl); 3275 freemsg(mp); 3276 } 3277 break; 3278 3279 case M_IOCDATA: 3280 async_iocdata(q, mp); 3281 break; 3282 3283 default: 3284 freemsg(mp); 3285 break; 3286 } 3287 } 3288 3289 /* 3290 * Retry an "ioctl", now that "bufcall" claims we may be able to allocate 3291 * the buffer we need. 3292 */ 3293 static void 3294 async_reioctl(void *arg) 3295 { 3296 struct asyncline *async = arg; 3297 struct asycom *asy = async->async_common; 3298 queue_t *q; 3299 mblk_t *mp; 3300 3301 /* 3302 * The bufcall is no longer pending. 3303 */ 3304 mutex_enter(asy->asy_excl); 3305 async->async_wbufcid = 0; 3306 if ((q = async->async_ttycommon.t_writeq) == NULL) { 3307 mutex_exit(asy->asy_excl); 3308 return; 3309 } 3310 if ((mp = async->async_ttycommon.t_iocpending) != NULL) { 3311 /* not pending any more */ 3312 async->async_ttycommon.t_iocpending = NULL; 3313 mutex_exit(asy->asy_excl); 3314 /* not in STREAMS queue; we no longer know if we're in wput */ 3315 async_ioctl(async, q, mp, B_TRUE); 3316 } else 3317 mutex_exit(asy->asy_excl); 3318 } 3319 3320 static void 3321 async_iocdata(queue_t *q, mblk_t *mp) 3322 { 3323 struct asyncline *async = (struct asyncline *)q->q_ptr; 3324 struct asycom *asy; 3325 struct copyresp *csp; 3326 3327 asy = async->async_common; 3328 csp = (struct copyresp *)mp->b_rptr; 3329 3330 if (csp->cp_rval != 0) { 3331 freemsg(mp); 3332 return; 3333 } 3334 3335 mutex_enter(asy->asy_excl); 3336 3337 switch (csp->cp_cmd) { 3338 case TIOCMSET: 3339 case TIOCMBIS: 3340 case TIOCMBIC: 3341 if (mp->b_cont == NULL) { 3342 mutex_exit(asy->asy_excl); 3343 miocnak(q, mp, 0, EINVAL); 3344 break; 3345 } 3346 3347 mutex_enter(asy->asy_excl_hi); 3348 (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr), 3349 csp->cp_cmd); 3350 mutex_exit(asy->asy_excl_hi); 3351 3352 freemsg(mp->b_cont); 3353 mp->b_cont = NULL; 3354 mutex_exit(asy->asy_excl); 3355 miocack(q, mp, 0, 0); 3356 break; 3357 3358 case TIOCMGET: 3359 if (mp->b_cont != NULL) { 3360 freemsg(mp->b_cont); 3361 mp->b_cont = NULL; 3362 } 3363 mutex_exit(asy->asy_excl); 3364 miocack(q, mp, 0, 0); 3365 break; 3366 3367 default: 3368 mutex_exit(asy->asy_excl); 3369 miocnak(q, mp, 0, EINVAL); 3370 break; 3371 } 3372 } 3373 3374 3375 /* 3376 * Set or get the modem control status. 3377 */ 3378 static int 3379 asymctl(struct asycom *asy, int bits, int how) 3380 { 3381 register int mcr_r, msr_r; 3382 3383 ASSERT(mutex_owned(asy->asy_excl_hi)); 3384 ASSERT(mutex_owned(asy->asy_excl)); 3385 3386 /* Read Modem Control Registers */ 3387 mcr_r = INB(MCR); 3388 3389 switch (how) { 3390 3391 case TIOCMSET: 3392 mcr_r = bits; 3393 break; 3394 3395 case TIOCMBIS: 3396 mcr_r |= bits; /* Set bits from input */ 3397 break; 3398 3399 case TIOCMBIC: 3400 mcr_r &= ~bits; /* Set ~bits from input */ 3401 break; 3402 3403 case TIOCMGET: 3404 /* Read Modem Status Registers */ 3405 if (INB(ICR) & MIEN) 3406 msr_r = asy->asy_cached_msr; 3407 else 3408 msr_r = INB(MSR); 3409 return (asytodm(mcr_r, msr_r)); 3410 } 3411 3412 OUTB(MCR, mcr_r); 3413 3414 return (mcr_r); 3415 } 3416 3417 static int 3418 asytodm(int mcr_r, int msr_r) 3419 { 3420 register int b = 0; 3421 3422 3423 /* MCR registers */ 3424 if (mcr_r & RTS) 3425 b |= TIOCM_RTS; 3426 3427 if (mcr_r & DTR) 3428 b |= TIOCM_DTR; 3429 3430 /* MSR registers */ 3431 if (msr_r & DCD) 3432 b |= TIOCM_CAR; 3433 3434 if (msr_r & CTS) 3435 b |= TIOCM_CTS; 3436 3437 if (msr_r & DSR) 3438 b |= TIOCM_DSR; 3439 3440 if (msr_r & RI) 3441 b |= TIOCM_RNG; 3442 3443 return (b); 3444 } 3445 3446 static int 3447 dmtoasy(int bits) 3448 { 3449 register int b = 0; 3450 3451 #ifdef CAN_NOT_SET /* only DTR and RTS can be set */ 3452 if (bits & TIOCM_CAR) 3453 b |= DCD; 3454 if (bits & TIOCM_CTS) 3455 b |= CTS; 3456 if (bits & TIOCM_DSR) 3457 b |= DSR; 3458 if (bits & TIOCM_RNG) 3459 b |= RI; 3460 #endif 3461 3462 if (bits & TIOCM_RTS) 3463 b |= RTS; 3464 if (bits & TIOCM_DTR) 3465 b |= DTR; 3466 3467 return (b); 3468 } 3469 3470 static void 3471 asycheckflowcontrol_hw(struct asycom *asy) 3472 { 3473 struct asyncline *async; 3474 uchar_t mcr, flag; 3475 3476 ASSERT(mutex_owned(asy->asy_excl_hi)); 3477 3478 async = (struct asyncline *)asy->asy_priv; 3479 ASSERT(async != NULL); 3480 3481 if (async->async_ttycommon.t_cflag & CRTSXOFF) { 3482 mcr = INB(MCR); 3483 flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS; 3484 if (((mcr ^ flag) & RTS) != 0) { 3485 OUTB(MCR, (mcr ^ RTS)); 3486 } 3487 } 3488 } 3489 3490 static boolean_t 3491 asycheckflowcontrol_sw(struct asycom *asy) 3492 { 3493 uchar_t ss; 3494 struct asyncline *async; 3495 int rval = B_FALSE; 3496 3497 ASSERT(mutex_owned(asy->asy_excl_hi)); 3498 3499 async = (struct asyncline *)asy->asy_priv; 3500 ASSERT(async != NULL); 3501 3502 if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) { 3503 /* 3504 * If we get this far, then we know that flowc is non-zero and 3505 * that there's transmit room available. We've "handled" the 3506 * request now, so clear it. If the user didn't ask for IXOFF, 3507 * then don't actually send anything, but wait for the next 3508 * opportunity. 3509 */ 3510 async->async_flowc = '\0'; 3511 if (async->async_ttycommon.t_iflag & IXOFF) { 3512 async->async_flags |= ASYNC_BUSY; 3513 OUTB(DAT, ss); 3514 rval = B_TRUE; 3515 } 3516 } 3517 3518 return (rval); 3519 } 3520 3521 /* 3522 * Check for abort character sequence 3523 */ 3524 static boolean_t 3525 abort_charseq_recognize(uchar_t ch) 3526 { 3527 static int state = 0; 3528 #define CNTRL(c) ((c)&037) 3529 static char sequence[] = { '\r', '~', CNTRL('b') }; 3530 3531 if (ch == sequence[state]) { 3532 if (++state >= sizeof (sequence)) { 3533 state = 0; 3534 return (B_TRUE); 3535 } 3536 } else { 3537 state = (ch == sequence[0]) ? 1 : 0; 3538 } 3539 return (B_FALSE); 3540 } 3541