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