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