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 
  22 /*      Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
  23 /*      Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T       */
  24 /*        All Rights Reserved                                   */
  25 
  26 /*
  27  * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
  28  * Copyright 2012 Milan Jurik. All rights reserved.
  29  */
  30 
  31 
  32 /*
  33  * Serial I/O driver for 8250/16450/16550A/16650/16750 chips.
  34  */
  35 
  36 #include <sys/param.h>
  37 #include <sys/types.h>
  38 #include <sys/signal.h>
  39 #include <sys/stream.h>
  40 #include <sys/termio.h>
  41 #include <sys/errno.h>
  42 #include <sys/file.h>
  43 #include <sys/cmn_err.h>
  44 #include <sys/stropts.h>
  45 #include <sys/strsubr.h>
  46 #include <sys/strtty.h>
  47 #include <sys/debug.h>
  48 #include <sys/kbio.h>
  49 #include <sys/cred.h>
  50 #include <sys/stat.h>
  51 #include <sys/consdev.h>
  52 #include <sys/mkdev.h>
  53 #include <sys/kmem.h>
  54 #include <sys/cred.h>
  55 #include <sys/strsun.h>
  56 #ifdef DEBUG
  57 #include <sys/promif.h>
  58 #endif
  59 #include <sys/modctl.h>
  60 #include <sys/ddi.h>
  61 #include <sys/sunddi.h>
  62 #include <sys/pci.h>
  63 #include <sys/asy.h>
  64 #include <sys/policy.h>
  65 
  66 /*
  67  * set the RX FIFO trigger_level to half the RX FIFO size for now
  68  * we may want to make this configurable later.
  69  */
  70 static  int asy_trig_level = FIFO_TRIG_8;
  71 
  72 int asy_drain_check = 15000000;         /* tunable: exit drain check time */
  73 int asy_min_dtr_low = 500000;           /* tunable: minimum DTR down time */
  74 int asy_min_utbrk = 100000;             /* tunable: minumum untimed brk time */
  75 
  76 int asymaxchip = ASY16750;      /* tunable: limit chip support we look for */
  77 
  78 /*
  79  * Just in case someone has a chip with broken loopback mode, we provide a
  80  * means to disable the loopback test. By default, we only loopback test
  81  * UARTs which look like they have FIFOs bigger than 16 bytes.
  82  * Set to 0 to suppress test, or to 2 to enable test on any size FIFO.
  83  */
  84 int asy_fifo_test = 1;          /* tunable: set to 0, 1, or 2 */
  85 
  86 /*
  87  * Allow ability to switch off testing of the scratch register.
  88  * Some UART emulators might not have it. This will also disable the test
  89  * for Exar/Startech ST16C650, as that requires use of the SCR register.
  90  */
  91 int asy_scr_test = 1;           /* tunable: set to 0 to disable SCR reg test */
  92 
  93 /*
  94  * As we don't yet support on-chip flow control, it's a bad idea to put a
  95  * large number of characters in the TX FIFO, since if other end tells us
  96  * to stop transmitting, we can only stop filling the TX FIFO, but it will
  97  * still carry on draining by itself, so remote end still gets what's left
  98  * in the FIFO.
  99  */
 100 int asy_max_tx_fifo = 16;       /* tunable: max fill of TX FIFO */
 101 
 102 #define async_stopc     async_ttycommon.t_stopc
 103 #define async_startc    async_ttycommon.t_startc
 104 
 105 #define ASY_INIT        1
 106 #define ASY_NOINIT      0
 107 
 108 /* enum value for sw and hw flow control action */
 109 typedef enum {
 110         FLOW_CHECK,
 111         FLOW_STOP,
 112         FLOW_START
 113 } async_flowc_action;
 114 
 115 #ifdef DEBUG
 116 #define ASY_DEBUG_INIT  0x0001  /* Output msgs during driver initialization. */
 117 #define ASY_DEBUG_INPUT 0x0002  /* Report characters received during int. */
 118 #define ASY_DEBUG_EOT   0x0004  /* Output msgs when wait for xmit to finish. */
 119 #define ASY_DEBUG_CLOSE 0x0008  /* Output msgs when driver open/close called */
 120 #define ASY_DEBUG_HFLOW 0x0010  /* Output msgs when H/W flowcontrol is active */
 121 #define ASY_DEBUG_PROCS 0x0020  /* Output each proc name as it is entered. */
 122 #define ASY_DEBUG_STATE 0x0040  /* Output value of Interrupt Service Reg. */
 123 #define ASY_DEBUG_INTR  0x0080  /* Output value of Interrupt Service Reg. */
 124 #define ASY_DEBUG_OUT   0x0100  /* Output msgs about output events. */
 125 #define ASY_DEBUG_BUSY  0x0200  /* Output msgs when xmit is enabled/disabled */
 126 #define ASY_DEBUG_MODEM 0x0400  /* Output msgs about modem status & control. */
 127 #define ASY_DEBUG_MODM2 0x0800  /* Output msgs about modem status & control. */
 128 #define ASY_DEBUG_IOCTL 0x1000  /* Output msgs about ioctl messages. */
 129 #define ASY_DEBUG_CHIP  0x2000  /* Output msgs about chip identification. */
 130 #define ASY_DEBUG_SFLOW 0x4000  /* Output msgs when S/W flowcontrol is active */
 131 #define ASY_DEBUG(x) (debug & (x))
 132 static  int debug  = 0;
 133 #else
 134 #define ASY_DEBUG(x) B_FALSE
 135 #endif
 136 
 137 /* pnpISA compressed device ids */
 138 #define pnpMTS0219 0xb6930219   /* Multitech MT5634ZTX modem */
 139 
 140 /*
 141  * PPS (Pulse Per Second) support.
 142  */
 143 void ddi_hardpps(struct timeval *, int);
 144 /*
 145  * This is protected by the asy_excl_hi of the port on which PPS event
 146  * handling is enabled.  Note that only one port should have this enabled at
 147  * any one time.  Enabling PPS handling on multiple ports will result in
 148  * unpredictable (but benign) results.
 149  */
 150 static struct ppsclockev asy_ppsev;
 151 
 152 #ifdef PPSCLOCKLED
 153 /* XXX Use these to observe PPS latencies and jitter on a scope */
 154 #define LED_ON
 155 #define LED_OFF
 156 #else
 157 #define LED_ON
 158 #define LED_OFF
 159 #endif
 160 
 161 static  int max_asy_instance = -1;
 162 
 163 static  uint_t  asysoftintr(caddr_t intarg);
 164 static  uint_t  asyintr(caddr_t argasy);
 165 
 166 static boolean_t abort_charseq_recognize(uchar_t ch);
 167 
 168 /* The async interrupt entry points */
 169 static void     async_txint(struct asycom *asy);
 170 static void     async_rxint(struct asycom *asy, uchar_t lsr);
 171 static void     async_msint(struct asycom *asy);
 172 static void     async_softint(struct asycom *asy);
 173 
 174 static void     async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp);
 175 static void     async_reioctl(void *unit);
 176 static void     async_iocdata(queue_t *q, mblk_t *mp);
 177 static void     async_restart(void *arg);
 178 static void     async_start(struct asyncline *async);
 179 static void     async_nstart(struct asyncline *async, int mode);
 180 static void     async_resume(struct asyncline *async);
 181 static void     asy_program(struct asycom *asy, int mode);
 182 static void     asyinit(struct asycom *asy);
 183 static void     asy_waiteot(struct asycom *asy);
 184 static void     asyputchar(cons_polledio_arg_t, uchar_t c);
 185 static int      asygetchar(cons_polledio_arg_t);
 186 static boolean_t        asyischar(cons_polledio_arg_t);
 187 
 188 static int      asymctl(struct asycom *, int, int);
 189 static int      asytodm(int, int);
 190 static int      dmtoasy(int);
 191 /*PRINTFLIKE2*/
 192 static void     asyerror(int level, const char *fmt, ...) __KPRINTFLIKE(2);
 193 static void     asy_parse_mode(dev_info_t *devi, struct asycom *asy);
 194 static void     asy_soft_state_free(struct asycom *);
 195 static char     *asy_hw_name(struct asycom *asy);
 196 static void     async_hold_utbrk(void *arg);
 197 static void     async_resume_utbrk(struct asyncline *async);
 198 static void     async_dtr_free(struct asyncline *async);
 199 static int      asy_identify_chip(dev_info_t *devi, struct asycom *asy);
 200 static void     asy_reset_fifo(struct asycom *asy, uchar_t flags);
 201 static int      asy_getproperty(dev_info_t *devi, struct asycom *asy,
 202                     const char *property);
 203 static boolean_t        async_flowcontrol_sw_input(struct asycom *asy,
 204                             async_flowc_action onoff, int type);
 205 static void     async_flowcontrol_sw_output(struct asycom *asy,
 206                     async_flowc_action onoff);
 207 static void     async_flowcontrol_hw_input(struct asycom *asy,
 208                     async_flowc_action onoff, int type);
 209 static void     async_flowcontrol_hw_output(struct asycom *asy,
 210                     async_flowc_action onoff);
 211 
 212 #define GET_PROP(devi, pname, pflag, pval, plen) \
 213                 (ddi_prop_op(DDI_DEV_T_ANY, (devi), PROP_LEN_AND_VAL_BUF, \
 214                 (pflag), (pname), (caddr_t)(pval), (plen)))
 215 
 216 kmutex_t asy_glob_lock; /* lock protecting global data manipulation */
 217 void *asy_soft_state;
 218 
 219 /* Standard COM port I/O addresses */
 220 static const int standard_com_ports[] = {
 221         COM1_IOADDR, COM2_IOADDR, COM3_IOADDR, COM4_IOADDR
 222 };
 223 
 224 static int *com_ports;
 225 static uint_t num_com_ports;
 226 
 227 #ifdef  DEBUG
 228 /*
 229  * Set this to true to make the driver pretend to do a suspend.  Useful
 230  * for debugging suspend/resume code with a serial debugger.
 231  */
 232 boolean_t       asy_nosuspend = B_FALSE;
 233 #endif
 234 
 235 
 236 /*
 237  * Baud rate table. Indexed by #defines found in sys/termios.h
 238  */
 239 ushort_t asyspdtab[] = {
 240         0,      /* 0 baud rate */
 241         0x900,  /* 50 baud rate */
 242         0x600,  /* 75 baud rate */
 243         0x417,  /* 110 baud rate (%0.026) */
 244         0x359,  /* 134 baud rate (%0.058) */
 245         0x300,  /* 150 baud rate */
 246         0x240,  /* 200 baud rate */
 247         0x180,  /* 300 baud rate */
 248         0x0c0,  /* 600 baud rate */
 249         0x060,  /* 1200 baud rate */
 250         0x040,  /* 1800 baud rate */
 251         0x030,  /* 2400 baud rate */
 252         0x018,  /* 4800 baud rate */
 253         0x00c,  /* 9600 baud rate */
 254         0x006,  /* 19200 baud rate */
 255         0x003,  /* 38400 baud rate */
 256 
 257         0x002,  /* 57600 baud rate */
 258         0x0,    /* 76800 baud rate not supported */
 259         0x001,  /* 115200 baud rate */
 260         0x0,    /* 153600 baud rate not supported */
 261         0x0,    /* 0x8002 (SMC chip) 230400 baud rate not supported */
 262         0x0,    /* 307200 baud rate not supported */
 263         0x0,    /* 0x8001 (SMC chip) 460800 baud rate not supported */
 264         0x0,    /* unused */
 265         0x0,    /* unused */
 266         0x0,    /* unused */
 267         0x0,    /* unused */
 268         0x0,    /* unused */
 269         0x0,    /* unused */
 270         0x0,    /* unused */
 271         0x0,    /* unused */
 272         0x0,    /* unused */
 273 };
 274 
 275 static int asyrsrv(queue_t *q);
 276 static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
 277 static int asyclose(queue_t *q, int flag, cred_t *credp);
 278 static int asywputdo(queue_t *q, mblk_t *mp, boolean_t);
 279 static int asywput(queue_t *q, mblk_t *mp);
 280 
 281 struct module_info asy_info = {
 282         0,
 283         "asy",
 284         0,
 285         INFPSZ,
 286         4096,
 287         128
 288 };
 289 
 290 static struct qinit asy_rint = {
 291         putq,
 292         asyrsrv,
 293         asyopen,
 294         asyclose,
 295         NULL,
 296         &asy_info,
 297         NULL
 298 };
 299 
 300 static struct qinit asy_wint = {
 301         asywput,
 302         NULL,
 303         NULL,
 304         NULL,
 305         NULL,
 306         &asy_info,
 307         NULL
 308 };
 309 
 310 struct streamtab asy_str_info = {
 311         &asy_rint,
 312         &asy_wint,
 313         NULL,
 314         NULL
 315 };
 316 
 317 static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
 318                 void **result);
 319 static int asyprobe(dev_info_t *);
 320 static int asyattach(dev_info_t *, ddi_attach_cmd_t);
 321 static int asydetach(dev_info_t *, ddi_detach_cmd_t);
 322 static int asyquiesce(dev_info_t *);
 323 
 324 static  struct cb_ops cb_asy_ops = {
 325         nodev,                  /* cb_open */
 326         nodev,                  /* cb_close */
 327         nodev,                  /* cb_strategy */
 328         nodev,                  /* cb_print */
 329         nodev,                  /* cb_dump */
 330         nodev,                  /* cb_read */
 331         nodev,                  /* cb_write */
 332         nodev,                  /* cb_ioctl */
 333         nodev,                  /* cb_devmap */
 334         nodev,                  /* cb_mmap */
 335         nodev,                  /* cb_segmap */
 336         nochpoll,               /* cb_chpoll */
 337         ddi_prop_op,            /* cb_prop_op */
 338         &asy_str_info,              /* cb_stream */
 339         D_MP                    /* cb_flag */
 340 };
 341 
 342 struct dev_ops asy_ops = {
 343         DEVO_REV,               /* devo_rev */
 344         0,                      /* devo_refcnt */
 345         asyinfo,                /* devo_getinfo */
 346         nulldev,                /* devo_identify */
 347         asyprobe,               /* devo_probe */
 348         asyattach,              /* devo_attach */
 349         asydetach,              /* devo_detach */
 350         nodev,                  /* devo_reset */
 351         &cb_asy_ops,                /* devo_cb_ops */
 352         NULL,                   /* devo_bus_ops */
 353         NULL,                   /* power */
 354         asyquiesce,             /* quiesce */
 355 };
 356 
 357 static struct modldrv modldrv = {
 358         &mod_driverops, /* Type of module.  This one is a driver */
 359         "ASY driver",
 360         &asy_ops,   /* driver ops */
 361 };
 362 
 363 static struct modlinkage modlinkage = {
 364         MODREV_1,
 365         (void *)&modldrv,
 366         NULL
 367 };
 368 
 369 int
 370 _init(void)
 371 {
 372         int i;
 373 
 374         i = ddi_soft_state_init(&asy_soft_state, sizeof (struct asycom), 2);
 375         if (i == 0) {
 376                 mutex_init(&asy_glob_lock, NULL, MUTEX_DRIVER, NULL);
 377                 if ((i = mod_install(&modlinkage)) != 0) {
 378                         mutex_destroy(&asy_glob_lock);
 379                         ddi_soft_state_fini(&asy_soft_state);
 380                 } else {
 381                         DEBUGCONT2(ASY_DEBUG_INIT, "%s, debug = %x\n",
 382                             modldrv.drv_linkinfo, debug);
 383                 }
 384         }
 385         return (i);
 386 }
 387 
 388 int
 389 _fini(void)
 390 {
 391         int i;
 392 
 393         if ((i = mod_remove(&modlinkage)) == 0) {
 394                 DEBUGCONT1(ASY_DEBUG_INIT, "%s unloading\n",
 395                     modldrv.drv_linkinfo);
 396                 ASSERT(max_asy_instance == -1);
 397                 mutex_destroy(&asy_glob_lock);
 398                 /* free "motherboard-serial-ports" property if allocated */
 399                 if (com_ports != NULL && com_ports != (int *)standard_com_ports)
 400                         ddi_prop_free(com_ports);
 401                 com_ports = NULL;
 402                 ddi_soft_state_fini(&asy_soft_state);
 403         }
 404         return (i);
 405 }
 406 
 407 int
 408 _info(struct modinfo *modinfop)
 409 {
 410         return (mod_info(&modlinkage, modinfop));
 411 }
 412 
 413 void
 414 async_put_suspq(struct asycom *asy, mblk_t *mp)
 415 {
 416         struct asyncline *async = asy->asy_priv;
 417 
 418         ASSERT(mutex_owned(&asy->asy_excl));
 419 
 420         if (async->async_suspqf == NULL)
 421                 async->async_suspqf = mp;
 422         else
 423                 async->async_suspqb->b_next = mp;
 424 
 425         async->async_suspqb = mp;
 426 }
 427 
 428 static mblk_t *
 429 async_get_suspq(struct asycom *asy)
 430 {
 431         struct asyncline *async = asy->asy_priv;
 432         mblk_t *mp;
 433 
 434         ASSERT(mutex_owned(&asy->asy_excl));
 435 
 436         if ((mp = async->async_suspqf) != NULL) {
 437                 async->async_suspqf = mp->b_next;
 438                 mp->b_next = NULL;
 439         } else {
 440                 async->async_suspqb = NULL;
 441         }
 442         return (mp);
 443 }
 444 
 445 static void
 446 async_process_suspq(struct asycom *asy)
 447 {
 448         struct asyncline *async = asy->asy_priv;
 449         mblk_t *mp;
 450 
 451         ASSERT(mutex_owned(&asy->asy_excl));
 452 
 453         while ((mp = async_get_suspq(asy)) != NULL) {
 454                 queue_t *q;
 455 
 456                 q = async->async_ttycommon.t_writeq;
 457                 ASSERT(q != NULL);
 458                 mutex_exit(&asy->asy_excl);
 459                 (void) asywputdo(q, mp, B_FALSE);
 460                 mutex_enter(&asy->asy_excl);
 461         }
 462         async->async_flags &= ~ASYNC_DDI_SUSPENDED;
 463         cv_broadcast(&async->async_flags_cv);
 464 }
 465 
 466 static int
 467 asy_get_bus_type(dev_info_t *devinfo)
 468 {
 469         char    parent_type[16];
 470         int     parentlen;
 471 
 472         parentlen = sizeof (parent_type);
 473 
 474         if (ddi_prop_op(DDI_DEV_T_ANY, devinfo, PROP_LEN_AND_VAL_BUF, 0,
 475             "device_type", (caddr_t)parent_type, &parentlen)
 476             != DDI_PROP_SUCCESS && ddi_prop_op(DDI_DEV_T_ANY, devinfo,
 477             PROP_LEN_AND_VAL_BUF, 0, "bus-type", (caddr_t)parent_type,
 478             &parentlen) != DDI_PROP_SUCCESS) {
 479                         cmn_err(CE_WARN,
 480                             "asy: can't figure out device type for"
 481                             " parent \"%s\"",
 482                             ddi_get_name(ddi_get_parent(devinfo)));
 483                         return (ASY_BUS_UNKNOWN);
 484         }
 485         if (strcmp(parent_type, "isa") == 0)
 486                 return (ASY_BUS_ISA);
 487         else if (strcmp(parent_type, "pci") == 0)
 488                 return (ASY_BUS_PCI);
 489         else
 490                 return (ASY_BUS_UNKNOWN);
 491 }
 492 
 493 static int
 494 asy_get_io_regnum_pci(dev_info_t *devi, struct asycom *asy)
 495 {
 496         int reglen, nregs;
 497         int regnum, i;
 498         uint64_t size;
 499         struct pci_phys_spec *reglist;
 500 
 501         if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
 502             "reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
 503                 cmn_err(CE_WARN, "asy_get_io_regnum_pci: reg property"
 504                     " not found in devices property list");
 505                 return (-1);
 506         }
 507 
 508         /*
 509          * PCI devices are assumed to not have broken FIFOs;
 510          * Agere/Lucent Venus PCI modem chipsets are an example
 511          */
 512         if (asy)
 513                 asy->asy_flags2 |= ASY2_NO_LOOPBACK;
 514 
 515         regnum = -1;
 516         nregs = reglen / sizeof (*reglist);
 517         for (i = 0; i < nregs; i++) {
 518                 switch (reglist[i].pci_phys_hi & PCI_ADDR_MASK) {
 519                 case PCI_ADDR_IO:               /* I/O bus reg property */
 520                         if (regnum == -1) /* use only the first one */
 521                                 regnum = i;
 522                         break;
 523 
 524                 default:
 525                         break;
 526                 }
 527         }
 528 
 529         /* check for valid count of registers */
 530         if (regnum >= 0) {
 531                 size = ((uint64_t)reglist[regnum].pci_size_low) |
 532                     ((uint64_t)reglist[regnum].pci_size_hi) << 32;
 533                 if (size < 8)
 534                         regnum = -1;
 535         }
 536         kmem_free(reglist, reglen);
 537         return (regnum);
 538 }
 539 
 540 static int
 541 asy_get_io_regnum_isa(dev_info_t *devi, struct asycom *asy)
 542 {
 543         int reglen, nregs;
 544         int regnum, i;
 545         struct {
 546                 uint_t bustype;
 547                 int base;
 548                 int size;
 549         } *reglist;
 550 
 551         if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
 552             "reg", (caddr_t)&reglist, &reglen) != DDI_PROP_SUCCESS) {
 553                 cmn_err(CE_WARN, "asy_get_io_regnum: reg property not found "
 554                     "in devices property list");
 555                 return (-1);
 556         }
 557 
 558         regnum = -1;
 559         nregs = reglen / sizeof (*reglist);
 560         for (i = 0; i < nregs; i++) {
 561                 switch (reglist[i].bustype) {
 562                 case 1:                 /* I/O bus reg property */
 563                         if (regnum == -1) /* only use the first one */
 564                                 regnum = i;
 565                         break;
 566 
 567                 case pnpMTS0219:        /* Multitech MT5634ZTX modem */
 568                         /* Venus chipset can't do loopback test */
 569                         if (asy)
 570                                 asy->asy_flags2 |= ASY2_NO_LOOPBACK;
 571                         break;
 572 
 573                 default:
 574                         break;
 575                 }
 576         }
 577 
 578         /* check for valid count of registers */
 579         if ((regnum < 0) || (reglist[regnum].size < 8))
 580                 regnum = -1;
 581         kmem_free(reglist, reglen);
 582         return (regnum);
 583 }
 584 
 585 static int
 586 asy_get_io_regnum(dev_info_t *devinfo, struct asycom *asy)
 587 {
 588         switch (asy_get_bus_type(devinfo)) {
 589         case ASY_BUS_ISA:
 590                 return (asy_get_io_regnum_isa(devinfo, asy));
 591         case ASY_BUS_PCI:
 592                 return (asy_get_io_regnum_pci(devinfo, asy));
 593         default:
 594                 return (-1);
 595         }
 596 }
 597 
 598 static int
 599 asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
 600 {
 601         int instance;
 602         struct asycom *asy;
 603         struct asyncline *async;
 604 
 605         instance = ddi_get_instance(devi);      /* find out which unit */
 606 
 607         asy = ddi_get_soft_state(asy_soft_state, instance);
 608         if (asy == NULL)
 609                 return (DDI_FAILURE);
 610         async = asy->asy_priv;
 611 
 612         switch (cmd) {
 613         case DDI_DETACH:
 614                 DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.",
 615                     instance, asy_hw_name(asy));
 616 
 617                 /* cancel DTR hold timeout */
 618                 if (async->async_dtrtid != 0) {
 619                         (void) untimeout(async->async_dtrtid);
 620                         async->async_dtrtid = 0;
 621                 }
 622 
 623                 /* remove all minor device node(s) for this device */
 624                 ddi_remove_minor_node(devi, NULL);
 625 
 626                 mutex_destroy(&asy->asy_excl);
 627                 mutex_destroy(&asy->asy_excl_hi);
 628                 cv_destroy(&async->async_flags_cv);
 629                 ddi_remove_intr(devi, 0, asy->asy_iblock);
 630                 ddi_regs_map_free(&asy->asy_iohandle);
 631                 ddi_remove_softintr(asy->asy_softintr_id);
 632                 mutex_destroy(&asy->asy_soft_lock);
 633                 asy_soft_state_free(asy);
 634                 DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete",
 635                     instance);
 636                 break;
 637         case DDI_SUSPEND:
 638                 {
 639                 unsigned i;
 640                 uchar_t lsr;
 641 
 642 #ifdef  DEBUG
 643                 if (asy_nosuspend)
 644                         return (DDI_SUCCESS);
 645 #endif
 646                 mutex_enter(&asy->asy_excl);
 647 
 648                 ASSERT(async->async_ops >= 0);
 649                 while (async->async_ops > 0)
 650                         cv_wait(&async->async_ops_cv, &asy->asy_excl);
 651 
 652                 async->async_flags |= ASYNC_DDI_SUSPENDED;
 653 
 654                 /* Wait for timed break and delay to complete */
 655                 while ((async->async_flags & (ASYNC_BREAK|ASYNC_DELAY))) {
 656                         if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl)
 657                             == 0) {
 658                                 async_process_suspq(asy);
 659                                 mutex_exit(&asy->asy_excl);
 660                                 return (DDI_FAILURE);
 661                         }
 662                 }
 663 
 664                 /* Clear untimed break */
 665                 if (async->async_flags & ASYNC_OUT_SUSPEND)
 666                         async_resume_utbrk(async);
 667 
 668                 mutex_exit(&asy->asy_excl);
 669 
 670                 mutex_enter(&asy->asy_soft_sr);
 671                 mutex_enter(&asy->asy_excl);
 672                 if (async->async_wbufcid != 0) {
 673                         bufcall_id_t bcid = async->async_wbufcid;
 674                         async->async_wbufcid = 0;
 675                         async->async_flags |= ASYNC_RESUME_BUFCALL;
 676                         mutex_exit(&asy->asy_excl);
 677                         unbufcall(bcid);
 678                         mutex_enter(&asy->asy_excl);
 679                 }
 680                 mutex_enter(&asy->asy_excl_hi);
 681 
 682                 /* Disable interrupts from chip */
 683                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
 684                 asy->asy_flags |= ASY_DDI_SUSPENDED;
 685 
 686                 /*
 687                  * Hardware interrupts are disabled we can drop our high level
 688                  * lock and proceed.
 689                  */
 690                 mutex_exit(&asy->asy_excl_hi);
 691 
 692                 /* Process remaining RX characters and RX errors, if any */
 693                 lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
 694                 async_rxint(asy, lsr);
 695 
 696                 /* Wait for TX to drain */
 697                 for (i = 1000; i > 0; i--) {
 698                         lsr = ddi_get8(asy->asy_iohandle,
 699                             asy->asy_ioaddr + LSR);
 700                         if ((lsr & (XSRE | XHRE)) == (XSRE | XHRE))
 701                                 break;
 702                         delay(drv_usectohz(10000));
 703                 }
 704                 if (i == 0)
 705                         cmn_err(CE_WARN,
 706                             "asy: transmitter wasn't drained before "
 707                             "driver was suspended");
 708 
 709                 mutex_exit(&asy->asy_excl);
 710                 mutex_exit(&asy->asy_soft_sr);
 711                 break;
 712         }
 713         default:
 714                 return (DDI_FAILURE);
 715         }
 716 
 717         return (DDI_SUCCESS);
 718 }
 719 
 720 /*
 721  * asyprobe
 722  * We don't bother probing for the hardware, as since Solaris 2.6, device
 723  * nodes are only created for auto-detected hardware or nodes explicitly
 724  * created by the user, e.g. via the DCA. However, we should check the
 725  * device node is at least vaguely usable, i.e. we have a block of 8 i/o
 726  * ports. This prevents attempting to attach to bogus serial ports which
 727  * some BIOSs still partially report when they are disabled in the BIOS.
 728  */
 729 static int
 730 asyprobe(dev_info_t *devi)
 731 {
 732         return ((asy_get_io_regnum(devi, NULL) < 0) ?
 733             DDI_PROBE_FAILURE : DDI_PROBE_DONTCARE);
 734 }
 735 
 736 static int
 737 asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
 738 {
 739         int instance;
 740         int mcr;
 741         int ret;
 742         int regnum = 0;
 743         int i;
 744         struct asycom *asy;
 745         char name[ASY_MINOR_LEN];
 746         int status;
 747         static ddi_device_acc_attr_t ioattr = {
 748                 DDI_DEVICE_ATTR_V0,
 749                 DDI_NEVERSWAP_ACC,
 750                 DDI_STRICTORDER_ACC,
 751         };
 752 
 753         instance = ddi_get_instance(devi);      /* find out which unit */
 754 
 755         switch (cmd) {
 756         case DDI_ATTACH:
 757                 break;
 758         case DDI_RESUME:
 759         {
 760                 struct asyncline *async;
 761 
 762 #ifdef  DEBUG
 763                 if (asy_nosuspend)
 764                         return (DDI_SUCCESS);
 765 #endif
 766                 asy = ddi_get_soft_state(asy_soft_state, instance);
 767                 if (asy == NULL)
 768                         return (DDI_FAILURE);
 769 
 770                 mutex_enter(&asy->asy_soft_sr);
 771                 mutex_enter(&asy->asy_excl);
 772                 mutex_enter(&asy->asy_excl_hi);
 773 
 774                 async = asy->asy_priv;
 775                 /* Disable interrupts */
 776                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
 777                 if (asy_identify_chip(devi, asy) != DDI_SUCCESS) {
 778                         mutex_exit(&asy->asy_excl_hi);
 779                         mutex_exit(&asy->asy_excl);
 780                         mutex_exit(&asy->asy_soft_sr);
 781                         cmn_err(CE_WARN, "!Cannot identify UART chip at %p\n",
 782                             (void *)asy->asy_ioaddr);
 783                         return (DDI_FAILURE);
 784                 }
 785                 asy->asy_flags &= ~ASY_DDI_SUSPENDED;
 786                 if (async->async_flags & ASYNC_ISOPEN) {
 787                         asy_program(asy, ASY_INIT);
 788                         /* Kick off output */
 789                         if (async->async_ocnt > 0) {
 790                                 async_resume(async);
 791                         } else {
 792                                 mutex_exit(&asy->asy_excl_hi);
 793                                 if (async->async_xmitblk)
 794                                         freeb(async->async_xmitblk);
 795                                 async->async_xmitblk = NULL;
 796                                 async_start(async);
 797                                 mutex_enter(&asy->asy_excl_hi);
 798                         }
 799                         ASYSETSOFT(asy);
 800                 }
 801                 mutex_exit(&asy->asy_excl_hi);
 802                 mutex_exit(&asy->asy_excl);
 803                 mutex_exit(&asy->asy_soft_sr);
 804 
 805                 mutex_enter(&asy->asy_excl);
 806                 if (async->async_flags & ASYNC_RESUME_BUFCALL) {
 807                         async->async_wbufcid = bufcall(async->async_wbufcds,
 808                             BPRI_HI, (void (*)(void *)) async_reioctl,
 809                             (void *)(intptr_t)async->async_common->asy_unit);
 810                         async->async_flags &= ~ASYNC_RESUME_BUFCALL;
 811                 }
 812                 async_process_suspq(asy);
 813                 mutex_exit(&asy->asy_excl);
 814                 return (DDI_SUCCESS);
 815         }
 816         default:
 817                 return (DDI_FAILURE);
 818         }
 819 
 820         ret = ddi_soft_state_zalloc(asy_soft_state, instance);
 821         if (ret != DDI_SUCCESS)
 822                 return (DDI_FAILURE);
 823         asy = ddi_get_soft_state(asy_soft_state, instance);
 824         ASSERT(asy != NULL);    /* can't fail - we only just allocated it */
 825         asy->asy_unit = instance;
 826         mutex_enter(&asy_glob_lock);
 827         if (instance > max_asy_instance)
 828                 max_asy_instance = instance;
 829         mutex_exit(&asy_glob_lock);
 830 
 831         regnum = asy_get_io_regnum(devi, asy);
 832 
 833         if (regnum < 0 ||
 834             ddi_regs_map_setup(devi, regnum, (caddr_t *)&asy->asy_ioaddr,
 835             (offset_t)0, (offset_t)0, &ioattr, &asy->asy_iohandle)
 836             != DDI_SUCCESS) {
 837                 cmn_err(CE_WARN, "asy%d: could not map UART registers @ %p",
 838                     instance, (void *)asy->asy_ioaddr);
 839 
 840                 asy_soft_state_free(asy);
 841                 return (DDI_FAILURE);
 842         }
 843 
 844         DEBUGCONT2(ASY_DEBUG_INIT, "asy%dattach: UART @ %p\n",
 845             instance, (void *)asy->asy_ioaddr);
 846 
 847         mutex_enter(&asy_glob_lock);
 848         if (com_ports == NULL) {        /* need to initialize com_ports */
 849                 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi, 0,
 850                     "motherboard-serial-ports", &com_ports, &num_com_ports) !=
 851                     DDI_PROP_SUCCESS) {
 852                         /* Use our built-in COM[1234] values */
 853                         com_ports = (int *)standard_com_ports;
 854                         num_com_ports = sizeof (standard_com_ports) /
 855                             sizeof (standard_com_ports[0]);
 856                 }
 857                 if (num_com_ports > 10) {
 858                         /* We run out of single digits for device properties */
 859                         num_com_ports = 10;
 860                         cmn_err(CE_WARN,
 861                             "More than %d motherboard-serial-ports",
 862                             num_com_ports);
 863                 }
 864         }
 865         mutex_exit(&asy_glob_lock);
 866 
 867         /*
 868          * Lookup the i/o address to see if this is a standard COM port
 869          * in which case we assign it the correct tty[a-d] to match the
 870          * COM port number, or some other i/o address in which case it
 871          * will be assigned /dev/term/[0123...] in some rather arbitrary
 872          * fashion.
 873          */
 874 
 875         for (i = 0; i < num_com_ports; i++) {
 876                 if (asy->asy_ioaddr == (uint8_t *)(uintptr_t)com_ports[i]) {
 877                         asy->asy_com_port = i + 1;
 878                         break;
 879                 }
 880         }
 881 
 882         /*
 883          * It appears that there was async hardware that on reset
 884          * did not clear ICR.  Hence when we get to
 885          * ddi_get_iblock_cookie below, this hardware would cause
 886          * the system to hang if there was input available.
 887          */
 888 
 889         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0x00);
 890 
 891         /* establish default usage */
 892         asy->asy_mcr |= RTS|DTR;             /* do use RTS/DTR after open */
 893         asy->asy_lcr = STOP1|BITS8;          /* default to 1 stop 8 bits */
 894         asy->asy_bidx = B9600;                       /* default to 9600  */
 895 #ifdef DEBUG
 896         asy->asy_msint_cnt = 0;                      /* # of times in async_msint */
 897 #endif
 898         mcr = 0;                                /* don't enable until open */
 899 
 900         if (asy->asy_com_port != 0) {
 901                 /*
 902                  * For motherboard ports, emulate tty eeprom properties.
 903                  * Actually, we can't tell if a port is motherboard or not,
 904                  * so for "motherboard ports", read standard DOS COM ports.
 905                  */
 906                 switch (asy_getproperty(devi, asy, "ignore-cd")) {
 907                 case 0:                         /* *-ignore-cd=False */
 908                         DEBUGCONT1(ASY_DEBUG_MODEM,
 909                             "asy%dattach: clear ASY_IGNORE_CD\n", instance);
 910                         asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */
 911                         break;
 912                 case 1:                         /* *-ignore-cd=True */
 913                         /*FALLTHRU*/
 914                 default:                        /* *-ignore-cd not defined */
 915                         /*
 916                          * We set rather silly defaults of soft carrier on
 917                          * and DTR/RTS raised here because it might be that
 918                          * one of the motherboard ports is the system console.
 919                          */
 920                         DEBUGCONT1(ASY_DEBUG_MODEM,
 921                             "asy%dattach: set ASY_IGNORE_CD, set RTS & DTR\n",
 922                             instance);
 923                         mcr = asy->asy_mcr;          /* rts/dtr on */
 924                         asy->asy_flags |= ASY_IGNORE_CD;     /* ignore cd */
 925                         break;
 926                 }
 927 
 928                 /* Property for not raising DTR/RTS */
 929                 switch (asy_getproperty(devi, asy, "rts-dtr-off")) {
 930                 case 0:                         /* *-rts-dtr-off=False */
 931                         asy->asy_flags |= ASY_RTS_DTR_OFF;   /* OFF */
 932                         mcr = asy->asy_mcr;          /* rts/dtr on */
 933                         DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dattach: "
 934                             "ASY_RTS_DTR_OFF set and DTR & RTS set\n",
 935                             instance);
 936                         break;
 937                 case 1:                         /* *-rts-dtr-off=True */
 938                         /*FALLTHRU*/
 939                 default:                        /* *-rts-dtr-off undefined */
 940                         break;
 941                 }
 942 
 943                 /* Parse property for tty modes */
 944                 asy_parse_mode(devi, asy);
 945         } else {
 946                 DEBUGCONT1(ASY_DEBUG_MODEM,
 947                     "asy%dattach: clear ASY_IGNORE_CD, clear RTS & DTR\n",
 948                     instance);
 949                 asy->asy_flags &= ~ASY_IGNORE_CD;        /* wait for cd */
 950         }
 951 
 952         /*
 953          * Initialize the port with default settings.
 954          */
 955 
 956         asy->asy_fifo_buf = 1;
 957         asy->asy_use_fifo = FIFO_OFF;
 958 
 959         /*
 960          * Get icookie for mutexes initialization
 961          */
 962         if ((ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) !=
 963             DDI_SUCCESS) ||
 964             (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_MED,
 965             &asy->asy_soft_iblock) != DDI_SUCCESS)) {
 966                 ddi_regs_map_free(&asy->asy_iohandle);
 967                 cmn_err(CE_CONT,
 968                     "asy%d: could not hook interrupt for UART @ %p\n",
 969                     instance, (void *)asy->asy_ioaddr);
 970                 asy_soft_state_free(asy);
 971                 return (DDI_FAILURE);
 972         }
 973 
 974         /*
 975          * Initialize mutexes before accessing the hardware
 976          */
 977         mutex_init(&asy->asy_soft_lock, NULL, MUTEX_DRIVER,
 978             (void *)asy->asy_soft_iblock);
 979         mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, NULL);
 980         mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER,
 981             (void *)asy->asy_iblock);
 982         mutex_init(&asy->asy_soft_sr, NULL, MUTEX_DRIVER,
 983             (void *)asy->asy_soft_iblock);
 984         mutex_enter(&asy->asy_excl);
 985         mutex_enter(&asy->asy_excl_hi);
 986 
 987         if (asy_identify_chip(devi, asy) != DDI_SUCCESS) {
 988                 mutex_exit(&asy->asy_excl_hi);
 989                 mutex_exit(&asy->asy_excl);
 990                 mutex_destroy(&asy->asy_soft_lock);
 991                 mutex_destroy(&asy->asy_excl);
 992                 mutex_destroy(&asy->asy_excl_hi);
 993                 mutex_destroy(&asy->asy_soft_sr);
 994                 ddi_regs_map_free(&asy->asy_iohandle);
 995                 cmn_err(CE_CONT, "!Cannot identify UART chip at %p\n",
 996                     (void *)asy->asy_ioaddr);
 997                 asy_soft_state_free(asy);
 998                 return (DDI_FAILURE);
 999         }
1000 
1001         /* disable all interrupts */
1002         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
1003         /* select baud rate generator */
1004         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB);
1005         /* Set the baud rate to 9600 */
1006         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLL),
1007             asyspdtab[asy->asy_bidx] & 0xff);
1008         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLH),
1009             (asyspdtab[asy->asy_bidx] >> 8) & 0xff);
1010         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, asy->asy_lcr);
1011         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr);
1012 
1013         mutex_exit(&asy->asy_excl_hi);
1014         mutex_exit(&asy->asy_excl);
1015 
1016         /*
1017          * Set up the other components of the asycom structure for this port.
1018          */
1019         asy->asy_dip = devi;
1020 
1021         /*
1022          * Install per instance software interrupt handler.
1023          */
1024         if (ddi_add_softintr(devi, DDI_SOFTINT_MED,
1025             &(asy->asy_softintr_id), NULL, 0, asysoftintr,
1026             (caddr_t)asy) != DDI_SUCCESS) {
1027                 mutex_destroy(&asy->asy_soft_lock);
1028                 mutex_destroy(&asy->asy_excl);
1029                 mutex_destroy(&asy->asy_excl_hi);
1030                 ddi_regs_map_free(&asy->asy_iohandle);
1031                 cmn_err(CE_CONT,
1032                     "Can not set soft interrupt for ASY driver\n");
1033                 asy_soft_state_free(asy);
1034                 return (DDI_FAILURE);
1035         }
1036 
1037         mutex_enter(&asy->asy_excl);
1038         mutex_enter(&asy->asy_excl_hi);
1039 
1040         /*
1041          * Install interrupt handler for this device.
1042          */
1043         if (ddi_add_intr(devi, 0, NULL, 0, asyintr,
1044             (caddr_t)asy) != DDI_SUCCESS) {
1045                 mutex_exit(&asy->asy_excl_hi);
1046                 mutex_exit(&asy->asy_excl);
1047                 ddi_remove_softintr(asy->asy_softintr_id);
1048                 mutex_destroy(&asy->asy_soft_lock);
1049                 mutex_destroy(&asy->asy_excl);
1050                 mutex_destroy(&asy->asy_excl_hi);
1051                 ddi_regs_map_free(&asy->asy_iohandle);
1052                 cmn_err(CE_CONT,
1053                     "Can not set device interrupt for ASY driver\n");
1054                 asy_soft_state_free(asy);
1055                 return (DDI_FAILURE);
1056         }
1057 
1058         mutex_exit(&asy->asy_excl_hi);
1059         mutex_exit(&asy->asy_excl);
1060 
1061         asyinit(asy);   /* initialize the asyncline structure */
1062 
1063         /* create minor device nodes for this device */
1064         if (asy->asy_com_port != 0) {
1065                 /*
1066                  * For DOS COM ports, add letter suffix so
1067                  * devfsadm can create correct link names.
1068                  */
1069                 name[0] = asy->asy_com_port + 'a' - 1;
1070                 name[1] = '\0';
1071         } else {
1072                 /*
1073                  * asy port which isn't a standard DOS COM
1074                  * port gets a numeric name based on instance
1075                  */
1076                 (void) snprintf(name, ASY_MINOR_LEN, "%d", instance);
1077         }
1078         status = ddi_create_minor_node(devi, name, S_IFCHR, instance,
1079             asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB : DDI_NT_SERIAL, NULL);
1080         if (status == DDI_SUCCESS) {
1081                 (void) strcat(name, ",cu");
1082                 status = ddi_create_minor_node(devi, name, S_IFCHR,
1083                     OUTLINE | instance,
1084                     asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB_DO :
1085                     DDI_NT_SERIAL_DO, NULL);
1086         }
1087 
1088         if (status != DDI_SUCCESS) {
1089                 struct asyncline *async = asy->asy_priv;
1090 
1091                 ddi_remove_minor_node(devi, NULL);
1092                 ddi_remove_intr(devi, 0, asy->asy_iblock);
1093                 ddi_remove_softintr(asy->asy_softintr_id);
1094                 mutex_destroy(&asy->asy_soft_lock);
1095                 mutex_destroy(&asy->asy_excl);
1096                 mutex_destroy(&asy->asy_excl_hi);
1097                 cv_destroy(&async->async_flags_cv);
1098                 ddi_regs_map_free(&asy->asy_iohandle);
1099                 asy_soft_state_free(asy);
1100                 return (DDI_FAILURE);
1101         }
1102 
1103         /*
1104          * Fill in the polled I/O structure.
1105          */
1106         asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0;
1107         asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy;
1108         asy->polledio.cons_polledio_putchar = asyputchar;
1109         asy->polledio.cons_polledio_getchar = asygetchar;
1110         asy->polledio.cons_polledio_ischar = asyischar;
1111         asy->polledio.cons_polledio_enter = NULL;
1112         asy->polledio.cons_polledio_exit = NULL;
1113 
1114         ddi_report_dev(devi);
1115         DEBUGCONT1(ASY_DEBUG_INIT, "asy%dattach: done\n", instance);
1116         return (DDI_SUCCESS);
1117 }
1118 
1119 /*ARGSUSED*/
1120 static int
1121 asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
1122         void **result)
1123 {
1124         dev_t dev = (dev_t)arg;
1125         int instance, error;
1126         struct asycom *asy;
1127 
1128         instance = UNIT(dev);
1129 
1130         switch (infocmd) {
1131         case DDI_INFO_DEVT2DEVINFO:
1132                 asy = ddi_get_soft_state(asy_soft_state, instance);
1133                 if ((asy == NULL) || (asy->asy_dip == NULL))
1134                         error = DDI_FAILURE;
1135                 else {
1136                         *result = (void *) asy->asy_dip;
1137                         error = DDI_SUCCESS;
1138                 }
1139                 break;
1140         case DDI_INFO_DEVT2INSTANCE:
1141                 *result = (void *)(intptr_t)instance;
1142                 error = DDI_SUCCESS;
1143                 break;
1144         default:
1145                 error = DDI_FAILURE;
1146         }
1147         return (error);
1148 }
1149 
1150 /* asy_getproperty -- walk through all name variants until we find a match */
1151 
1152 static int
1153 asy_getproperty(dev_info_t *devi, struct asycom *asy, const char *property)
1154 {
1155         int len;
1156         int ret;
1157         char letter = asy->asy_com_port + 'a' - 1;   /* for ttya */
1158         char number = asy->asy_com_port + '0';               /* for COM1 */
1159         char val[40];
1160         char name[40];
1161 
1162         /* Property for ignoring DCD */
1163         (void) sprintf(name, "tty%c-%s", letter, property);
1164         len = sizeof (val);
1165         ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
1166         if (ret != DDI_PROP_SUCCESS) {
1167                 (void) sprintf(name, "com%c-%s", number, property);
1168                 len = sizeof (val);
1169                 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
1170         }
1171         if (ret != DDI_PROP_SUCCESS) {
1172                 (void) sprintf(name, "tty0%c-%s", number, property);
1173                 len = sizeof (val);
1174                 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
1175         }
1176         if (ret != DDI_PROP_SUCCESS) {
1177                 (void) sprintf(name, "port-%c-%s", letter, property);
1178                 len = sizeof (val);
1179                 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
1180         }
1181         if (ret != DDI_PROP_SUCCESS)
1182                 return (-1);            /* property non-existant */
1183         if (val[0] == 'f' || val[0] == 'F' || val[0] == '0')
1184                 return (0);             /* property false/0 */
1185         return (1);                     /* property true/!0 */
1186 }
1187 
1188 /* asy_soft_state_free - local wrapper for ddi_soft_state_free(9F) */
1189 
1190 static void
1191 asy_soft_state_free(struct asycom *asy)
1192 {
1193         mutex_enter(&asy_glob_lock);
1194         /* If we were the max_asy_instance, work out new value */
1195         if (asy->asy_unit == max_asy_instance) {
1196                 while (--max_asy_instance >= 0) {
1197                         if (ddi_get_soft_state(asy_soft_state,
1198                             max_asy_instance) != NULL)
1199                                 break;
1200                 }
1201         }
1202         mutex_exit(&asy_glob_lock);
1203 
1204         if (asy->asy_priv != NULL) {
1205                 kmem_free(asy->asy_priv, sizeof (struct asyncline));
1206                 asy->asy_priv = NULL;
1207         }
1208         ddi_soft_state_free(asy_soft_state, asy->asy_unit);
1209 }
1210 
1211 static char *
1212 asy_hw_name(struct asycom *asy)
1213 {
1214         switch (asy->asy_hwtype) {
1215         case ASY8250A:
1216                 return ("8250A/16450");
1217         case ASY16550:
1218                 return ("16550");
1219         case ASY16550A:
1220                 return ("16550A");
1221         case ASY16650:
1222                 return ("16650");
1223         case ASY16750:
1224                 return ("16750");
1225         default:
1226                 DEBUGNOTE2(ASY_DEBUG_INIT,
1227                     "asy%d: asy_hw_name: unknown asy_hwtype: %d",
1228                     asy->asy_unit, asy->asy_hwtype);
1229                 return ("?");
1230         }
1231 }
1232 
1233 static int
1234 asy_identify_chip(dev_info_t *devi, struct asycom *asy)
1235 {
1236         int ret;
1237         int mcr;
1238         dev_t dev;
1239         uint_t hwtype;
1240 
1241         if (asy_scr_test) {
1242                 /* Check scratch register works. */
1243 
1244                 /* write to scratch register */
1245                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, SCRTEST);
1246                 /* make sure that pattern doesn't just linger on the bus */
1247                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR, 0x00);
1248                 /* read data back from scratch register */
1249                 ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR);
1250                 if (ret != SCRTEST) {
1251                         /*
1252                          * Scratch register not working.
1253                          * Probably not an async chip.
1254                          * 8250 and 8250B don't have scratch registers,
1255                          * but only worked in ancient PC XT's anyway.
1256                          */
1257                         cmn_err(CE_CONT, "!asy%d: UART @ %p "
1258                             "scratch register: expected 0x5a, got 0x%02x\n",
1259                             asy->asy_unit, (void *)asy->asy_ioaddr, ret);
1260                         return (DDI_FAILURE);
1261                 }
1262         }
1263         /*
1264          * Use 16550 fifo reset sequence specified in NS application
1265          * note. Disable fifos until chip is initialized.
1266          */
1267         ddi_put8(asy->asy_iohandle,
1268             asy->asy_ioaddr + FIFOR, 0x00);  /* clear */
1269         ddi_put8(asy->asy_iohandle,
1270             asy->asy_ioaddr + FIFOR, FIFO_ON);       /* enable */
1271         ddi_put8(asy->asy_iohandle,
1272             asy->asy_ioaddr + FIFOR, FIFO_ON | FIFORXFLSH);
1273                                                 /* reset */
1274         if (asymaxchip >= ASY16650 && asy_scr_test) {
1275                 /*
1276                  * Reset 16650 enhanced regs also, in case we have one of these
1277                  */
1278                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1279                     EFRACCESS);
1280                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR,
1281                     0);
1282                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1283                     STOP1|BITS8);
1284         }
1285 
1286         /*
1287          * See what sort of FIFO we have.
1288          * Try enabling it and see what chip makes of this.
1289          */
1290 
1291         asy->asy_fifor = 0;
1292         asy->asy_hwtype = asymaxchip; /* just for asy_reset_fifo() */
1293         if (asymaxchip >= ASY16550A)
1294                 asy->asy_fifor |=
1295                     FIFO_ON | FIFODMA | (asy_trig_level & 0xff);
1296         if (asymaxchip >= ASY16650)
1297                 asy->asy_fifor |= FIFOEXTRA1 | FIFOEXTRA2;
1298 
1299         asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH);
1300 
1301         mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
1302         ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR);
1303         DEBUGCONT4(ASY_DEBUG_CHIP,
1304             "asy%d: probe fifo FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n",
1305             asy->asy_unit, asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH,
1306             ret, mcr);
1307         switch (ret & 0xf0) {
1308         case 0x40:
1309                 hwtype = ASY16550; /* 16550 with broken FIFO */
1310                 asy->asy_fifor = 0;
1311                 break;
1312         case 0xc0:
1313                 hwtype = ASY16550A;
1314                 asy->asy_fifo_buf = 16;
1315                 asy->asy_use_fifo = FIFO_ON;
1316                 asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2);
1317                 break;
1318         case 0xe0:
1319                 hwtype = ASY16650;
1320                 asy->asy_fifo_buf = 32;
1321                 asy->asy_use_fifo = FIFO_ON;
1322                 asy->asy_fifor &= ~(FIFOEXTRA1);
1323                 break;
1324         case 0xf0:
1325                 /*
1326                  * Note we get 0xff if chip didn't return us anything,
1327                  * e.g. if there's no chip there.
1328                  */
1329                 if (ret == 0xff) {
1330                         cmn_err(CE_CONT, "asy%d: UART @ %p "
1331                             "interrupt register: got 0xff\n",
1332                             asy->asy_unit, (void *)asy->asy_ioaddr);
1333                         return (DDI_FAILURE);
1334                 }
1335                 /*FALLTHRU*/
1336         case 0xd0:
1337                 hwtype = ASY16750;
1338                 asy->asy_fifo_buf = 64;
1339                 asy->asy_use_fifo = FIFO_ON;
1340                 break;
1341         default:
1342                 hwtype = ASY8250A; /* No FIFO */
1343                 asy->asy_fifor = 0;
1344         }
1345 
1346         if (hwtype > asymaxchip) {
1347                 cmn_err(CE_CONT, "asy%d: UART @ %p "
1348                     "unexpected probe result: "
1349                     "FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n",
1350                     asy->asy_unit, (void *)asy->asy_ioaddr,
1351                     asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH, ret, mcr);
1352                 return (DDI_FAILURE);
1353         }
1354 
1355         /*
1356          * Now reset the FIFO operation appropriate for the chip type.
1357          * Note we must call asy_reset_fifo() before any possible
1358          * downgrade of the asy->asy_hwtype, or it may not disable
1359          * the more advanced features we specifically want downgraded.
1360          */
1361         asy_reset_fifo(asy, 0);
1362         asy->asy_hwtype = hwtype;
1363 
1364         /*
1365          * Check for Exar/Startech ST16C650, which will still look like a
1366          * 16550A until we enable its enhanced mode.
1367          */
1368         if (asy->asy_hwtype == ASY16550A && asymaxchip >= ASY16650 &&
1369             asy_scr_test) {
1370                 /* Enable enhanced mode register access */
1371                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1372                     EFRACCESS);
1373                 /* zero scratch register (not scratch register if enhanced) */
1374                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, 0);
1375                 /* Disable enhanced mode register access */
1376                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1377                     STOP1|BITS8);
1378                 /* read back scratch register */
1379                 ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR);
1380                 if (ret == SCRTEST) {
1381                         /* looks like we have an ST16650 -- enable it */
1382                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1383                             EFRACCESS);
1384                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR,
1385                             ENHENABLE);
1386                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1387                             STOP1|BITS8);
1388                         asy->asy_hwtype = ASY16650;
1389                         asy->asy_fifo_buf = 32;
1390                         asy->asy_fifor |= 0x10; /* 24 byte txfifo trigger */
1391                         asy_reset_fifo(asy, 0);
1392                 }
1393         }
1394 
1395         /*
1396          * If we think we might have a FIFO larger than 16 characters,
1397          * measure FIFO size and check it against expected.
1398          */
1399         if (asy_fifo_test > 0 &&
1400             !(asy->asy_flags2 & ASY2_NO_LOOPBACK) &&
1401             (asy->asy_fifo_buf > 16 ||
1402             (asy_fifo_test > 1 && asy->asy_use_fifo == FIFO_ON) ||
1403             ASY_DEBUG(ASY_DEBUG_CHIP))) {
1404                 int i;
1405 
1406                 /* Set baud rate to 57600 (fairly arbitrary choice) */
1407                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1408                     DLAB);
1409                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
1410                     asyspdtab[B57600] & 0xff);
1411                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
1412                     (asyspdtab[B57600] >> 8) & 0xff);
1413                 /* Set 8 bits, 1 stop bit */
1414                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1415                     STOP1|BITS8);
1416                 /* Set loopback mode */
1417                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
1418                     DTR | RTS | ASY_LOOP | OUT1 | OUT2);
1419 
1420                 /* Overfill fifo */
1421                 for (i = 0; i < asy->asy_fifo_buf * 2; i++) {
1422                         ddi_put8(asy->asy_iohandle,
1423                             asy->asy_ioaddr + DAT, i);
1424                 }
1425                 /*
1426                  * Now there's an interesting question here about which
1427                  * FIFO we're testing the size of, RX or TX. We just
1428                  * filled the TX FIFO much faster than it can empty,
1429                  * although it is possible one or two characters may
1430                  * have gone from it to the TX shift register.
1431                  * We wait for enough time for all the characters to
1432                  * move into the RX FIFO and any excess characters to
1433                  * have been lost, and then read all the RX FIFO. So
1434                  * the answer we finally get will be the size which is
1435                  * the MIN(RX FIFO,(TX FIFO + 1 or 2)). The critical
1436                  * one is actually the TX FIFO, because if we overfill
1437                  * it in normal operation, the excess characters are
1438                  * lost with no warning.
1439                  */
1440                 /*
1441                  * Wait for characters to move into RX FIFO.
1442                  * In theory, 200 * asy->asy_fifo_buf * 2 should be
1443                  * enough. However, in practice it isn't always, so we
1444                  * increase to 400 so some slow 16550A's finish, and we
1445                  * increase to 3 so we spot more characters coming back
1446                  * than we sent, in case that should ever happen.
1447                  */
1448                 delay(drv_usectohz(400 * asy->asy_fifo_buf * 3));
1449 
1450                 /* Now see how many characters we can read back */
1451                 for (i = 0; i < asy->asy_fifo_buf * 3; i++) {
1452                         ret = ddi_get8(asy->asy_iohandle,
1453                             asy->asy_ioaddr + LSR);
1454                         if (!(ret & RCA))
1455                                 break;  /* FIFO emptied */
1456                         (void) ddi_get8(asy->asy_iohandle,
1457                             asy->asy_ioaddr + DAT); /* lose another */
1458                 }
1459 
1460                 DEBUGCONT3(ASY_DEBUG_CHIP,
1461                     "asy%d FIFO size: expected=%d, measured=%d\n",
1462                     asy->asy_unit, asy->asy_fifo_buf, i);
1463 
1464                 hwtype = asy->asy_hwtype;
1465                 if (i < asy->asy_fifo_buf) {
1466                         /*
1467                          * FIFO is somewhat smaller than we anticipated.
1468                          * If we have 16 characters usable, then this
1469                          * UART will probably work well enough in
1470                          * 16550A mode. If less than 16 characters,
1471                          * then we'd better not use it at all.
1472                          * UARTs with busted FIFOs do crop up.
1473                          */
1474                         if (i >= 16 && asy->asy_fifo_buf >= 16) {
1475                                 /* fall back to a 16550A */
1476                                 hwtype = ASY16550A;
1477                                 asy->asy_fifo_buf = 16;
1478                                 asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2);
1479                         } else {
1480                                 /* fall back to no FIFO at all */
1481                                 hwtype = ASY16550;
1482                                 asy->asy_fifo_buf = 1;
1483                                 asy->asy_use_fifo = FIFO_OFF;
1484                                 asy->asy_fifor &=
1485                                     ~(FIFO_ON | FIFOEXTRA1 | FIFOEXTRA2);
1486                         }
1487                 }
1488                 /*
1489                  * We will need to reprogram the FIFO if we changed
1490                  * our mind about how to drive it above, and in any
1491                  * case, it would be a good idea to flush any garbage
1492                  * out incase the loopback test left anything behind.
1493                  * Again as earlier above, we must call asy_reset_fifo()
1494                  * before any possible downgrade of asy->asy_hwtype.
1495                  */
1496                 if (asy->asy_hwtype >= ASY16650 && hwtype < ASY16650) {
1497                         /* Disable 16650 enhanced mode */
1498                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1499                             EFRACCESS);
1500                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR,
1501                             0);
1502                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1503                             STOP1|BITS8);
1504                 }
1505                 asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH);
1506                 asy->asy_hwtype = hwtype;
1507 
1508                 /* Clear loopback mode and restore DTR/RTS */
1509                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr);
1510         }
1511 
1512         DEBUGNOTE3(ASY_DEBUG_CHIP, "asy%d %s @ %p",
1513             asy->asy_unit, asy_hw_name(asy), (void *)asy->asy_ioaddr);
1514 
1515         /* Make UART type visible in device tree for prtconf, etc */
1516         dev = makedevice(DDI_MAJOR_T_UNKNOWN, asy->asy_unit);
1517         (void) ddi_prop_update_string(dev, devi, "uart", asy_hw_name(asy));
1518 
1519         if (asy->asy_hwtype == ASY16550)     /* for broken 16550's, */
1520                 asy->asy_hwtype = ASY8250A;  /* drive them as 8250A */
1521 
1522         return (DDI_SUCCESS);
1523 }
1524 
1525 /*
1526  * asyinit() initializes the TTY protocol-private data for this channel
1527  * before enabling the interrupts.
1528  */
1529 static void
1530 asyinit(struct asycom *asy)
1531 {
1532         struct asyncline *async;
1533 
1534         asy->asy_priv = kmem_zalloc(sizeof (struct asyncline), KM_SLEEP);
1535         async = asy->asy_priv;
1536         mutex_enter(&asy->asy_excl);
1537         async->async_common = asy;
1538         cv_init(&async->async_flags_cv, NULL, CV_DRIVER, NULL);
1539         mutex_exit(&asy->asy_excl);
1540 }
1541 
1542 /*ARGSUSED3*/
1543 static int
1544 asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
1545 {
1546         struct asycom   *asy;
1547         struct asyncline *async;
1548         int             mcr;
1549         int             unit;
1550         int             len;
1551         struct termios  *termiosp;
1552 
1553         unit = UNIT(*dev);
1554         DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dopen\n", unit);
1555         asy = ddi_get_soft_state(asy_soft_state, unit);
1556         if (asy == NULL)
1557                 return (ENXIO);         /* unit not configured */
1558         async = asy->asy_priv;
1559         mutex_enter(&asy->asy_excl);
1560 
1561 again:
1562         mutex_enter(&asy->asy_excl_hi);
1563 
1564         /*
1565          * Block waiting for carrier to come up, unless this is a no-delay open.
1566          */
1567         if (!(async->async_flags & ASYNC_ISOPEN)) {
1568                 /*
1569                  * Set the default termios settings (cflag).
1570                  * Others are set in ldterm.
1571                  */
1572                 mutex_exit(&asy->asy_excl_hi);
1573 
1574                 if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(),
1575                     0, "ttymodes",
1576                     (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
1577                     len == sizeof (struct termios)) {
1578                         async->async_ttycommon.t_cflag = termiosp->c_cflag;
1579                         kmem_free(termiosp, len);
1580                 } else
1581                         cmn_err(CE_WARN,
1582                             "asy: couldn't get ttymodes property!");
1583                 mutex_enter(&asy->asy_excl_hi);
1584 
1585                 /* eeprom mode support - respect properties */
1586                 if (asy->asy_cflag)
1587                         async->async_ttycommon.t_cflag = asy->asy_cflag;
1588 
1589                 async->async_ttycommon.t_iflag = 0;
1590                 async->async_ttycommon.t_iocpending = NULL;
1591                 async->async_ttycommon.t_size.ws_row = 0;
1592                 async->async_ttycommon.t_size.ws_col = 0;
1593                 async->async_ttycommon.t_size.ws_xpixel = 0;
1594                 async->async_ttycommon.t_size.ws_ypixel = 0;
1595                 async->async_dev = *dev;
1596                 async->async_wbufcid = 0;
1597 
1598                 async->async_startc = CSTART;
1599                 async->async_stopc = CSTOP;
1600                 asy_program(asy, ASY_INIT);
1601         } else
1602                 if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
1603                     secpolicy_excl_open(cr) != 0) {
1604                 mutex_exit(&asy->asy_excl_hi);
1605                 mutex_exit(&asy->asy_excl);
1606                 return (EBUSY);
1607         } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) {
1608                 mutex_exit(&asy->asy_excl_hi);
1609                 mutex_exit(&asy->asy_excl);
1610                 return (EBUSY);
1611         }
1612 
1613         if (*dev & OUTLINE)
1614                 async->async_flags |= ASYNC_OUT;
1615 
1616         /* Raise DTR on every open, but delay if it was just lowered. */
1617         while (async->async_flags & ASYNC_DTR_DELAY) {
1618                 DEBUGCONT1(ASY_DEBUG_MODEM,
1619                     "asy%dopen: waiting for the ASYNC_DTR_DELAY to be clear\n",
1620                     unit);
1621                 mutex_exit(&asy->asy_excl_hi);
1622                 if (cv_wait_sig(&async->async_flags_cv,
1623                     &asy->asy_excl) == 0) {
1624                         DEBUGCONT1(ASY_DEBUG_MODEM,
1625                             "asy%dopen: interrupted by signal, exiting\n",
1626                             unit);
1627                         mutex_exit(&asy->asy_excl);
1628                         return (EINTR);
1629                 }
1630                 mutex_enter(&asy->asy_excl_hi);
1631         }
1632 
1633         mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
1634         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
1635             mcr|(asy->asy_mcr&DTR));
1636 
1637         DEBUGCONT3(ASY_DEBUG_INIT,
1638             "asy%dopen: \"Raise DTR on every open\": make mcr = %x, "
1639             "make TS_SOFTCAR = %s\n",
1640             unit, mcr|(asy->asy_mcr&DTR),
1641             (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF");
1642 
1643         if (asy->asy_flags & ASY_IGNORE_CD) {
1644                 DEBUGCONT1(ASY_DEBUG_MODEM,
1645                     "asy%dopen: ASY_IGNORE_CD set, set TS_SOFTCAR\n",
1646                     unit);
1647                 async->async_ttycommon.t_flags |= TS_SOFTCAR;
1648         }
1649         else
1650                 async->async_ttycommon.t_flags &= ~TS_SOFTCAR;
1651 
1652         /*
1653          * Check carrier.
1654          */
1655         asy->asy_msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
1656         DEBUGCONT3(ASY_DEBUG_INIT, "asy%dopen: TS_SOFTCAR is %s, "
1657             "MSR & DCD is %s\n",
1658             unit,
1659             (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear",
1660             (asy->asy_msr & DCD) ? "set" : "clear");
1661 
1662         if (asy->asy_msr & DCD)
1663                 async->async_flags |= ASYNC_CARR_ON;
1664         else
1665                 async->async_flags &= ~ASYNC_CARR_ON;
1666         mutex_exit(&asy->asy_excl_hi);
1667 
1668         /*
1669          * If FNDELAY and FNONBLOCK are clear, block until carrier up.
1670          * Quit on interrupt.
1671          */
1672         if (!(flag & (FNDELAY|FNONBLOCK)) &&
1673             !(async->async_ttycommon.t_cflag & CLOCAL)) {
1674                 if ((!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) &&
1675                     !(async->async_ttycommon.t_flags & TS_SOFTCAR)) ||
1676                     ((async->async_flags & ASYNC_OUT) &&
1677                     !(*dev & OUTLINE))) {
1678                         async->async_flags |= ASYNC_WOPEN;
1679                         if (cv_wait_sig(&async->async_flags_cv,
1680                             &asy->asy_excl) == B_FALSE) {
1681                                 async->async_flags &= ~ASYNC_WOPEN;
1682                                 mutex_exit(&asy->asy_excl);
1683                                 return (EINTR);
1684                         }
1685                         async->async_flags &= ~ASYNC_WOPEN;
1686                         goto again;
1687                 }
1688         } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) {
1689                 mutex_exit(&asy->asy_excl);
1690                 return (EBUSY);
1691         }
1692 
1693         async->async_ttycommon.t_readq = rq;
1694         async->async_ttycommon.t_writeq = WR(rq);
1695         rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async;
1696         mutex_exit(&asy->asy_excl);
1697         /*
1698          * Caution here -- qprocson sets the pointers that are used by canput
1699          * called by async_softint.  ASYNC_ISOPEN must *not* be set until those
1700          * pointers are valid.
1701          */
1702         qprocson(rq);
1703         async->async_flags |= ASYNC_ISOPEN;
1704         async->async_polltid = 0;
1705         DEBUGCONT1(ASY_DEBUG_INIT, "asy%dopen: done\n", unit);
1706         return (0);
1707 }
1708 
1709 static void
1710 async_progress_check(void *arg)
1711 {
1712         struct asyncline *async = arg;
1713         struct asycom    *asy = async->async_common;
1714         mblk_t *bp;
1715 
1716         /*
1717          * We define "progress" as either waiting on a timed break or delay, or
1718          * having had at least one transmitter interrupt.  If none of these are
1719          * true, then just terminate the output and wake up that close thread.
1720          */
1721         mutex_enter(&asy->asy_excl);
1722         mutex_enter(&asy->asy_excl_hi);
1723         if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) {
1724                 async->async_ocnt = 0;
1725                 async->async_flags &= ~ASYNC_BUSY;
1726                 async->async_timer = 0;
1727                 bp = async->async_xmitblk;
1728                 async->async_xmitblk = NULL;
1729                 mutex_exit(&asy->asy_excl_hi);
1730                 if (bp != NULL)
1731                         freeb(bp);
1732                 /*
1733                  * Since this timer is running, we know that we're in exit(2).
1734                  * That means that the user can't possibly be waiting on any
1735                  * valid ioctl(2) completion anymore, and we should just flush
1736                  * everything.
1737                  */
1738                 flushq(async->async_ttycommon.t_writeq, FLUSHALL);
1739                 cv_broadcast(&async->async_flags_cv);
1740         } else {
1741                 async->async_flags &= ~ASYNC_PROGRESS;
1742                 async->async_timer = timeout(async_progress_check, async,
1743                     drv_usectohz(asy_drain_check));
1744                 mutex_exit(&asy->asy_excl_hi);
1745         }
1746         mutex_exit(&asy->asy_excl);
1747 }
1748 
1749 /*
1750  * Release DTR so that asyopen() can raise it.
1751  */
1752 static void
1753 async_dtr_free(struct asyncline *async)
1754 {
1755         struct asycom *asy = async->async_common;
1756 
1757         DEBUGCONT0(ASY_DEBUG_MODEM,
1758             "async_dtr_free, clearing ASYNC_DTR_DELAY\n");
1759         mutex_enter(&asy->asy_excl);
1760         async->async_flags &= ~ASYNC_DTR_DELAY;
1761         async->async_dtrtid = 0;
1762         cv_broadcast(&async->async_flags_cv);
1763         mutex_exit(&asy->asy_excl);
1764 }
1765 
1766 /*
1767  * Close routine.
1768  */
1769 /*ARGSUSED2*/
1770 static int
1771 asyclose(queue_t *q, int flag, cred_t *credp)
1772 {
1773         struct asyncline *async;
1774         struct asycom    *asy;
1775         int icr, lcr;
1776 #ifdef DEBUG
1777         int instance;
1778 #endif
1779 
1780         async = (struct asyncline *)q->q_ptr;
1781         ASSERT(async != NULL);
1782 #ifdef DEBUG
1783         instance = UNIT(async->async_dev);
1784         DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dclose\n", instance);
1785 #endif
1786         asy = async->async_common;
1787 
1788         mutex_enter(&asy->asy_excl);
1789         async->async_flags |= ASYNC_CLOSING;
1790 
1791         /*
1792          * Turn off PPS handling early to avoid events occuring during
1793          * close.  Also reset the DCD edge monitoring bit.
1794          */
1795         mutex_enter(&asy->asy_excl_hi);
1796         asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE);
1797         mutex_exit(&asy->asy_excl_hi);
1798 
1799         /*
1800          * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
1801          * untimed (TIOCSBRK).  For the timed case, these are enqueued on our
1802          * write queue and there's a timer running, so we don't have to worry
1803          * about them.  For the untimed case, though, the user obviously made a
1804          * mistake, because these are handled immediately.  We'll terminate the
1805          * break now and honor his implicit request by discarding the rest of
1806          * the data.
1807          */
1808         if (async->async_flags & ASYNC_OUT_SUSPEND) {
1809                 if (async->async_utbrktid != 0) {
1810                         (void) untimeout(async->async_utbrktid);
1811                         async->async_utbrktid = 0;
1812                 }
1813                 mutex_enter(&asy->asy_excl_hi);
1814                 lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
1815                 ddi_put8(asy->asy_iohandle,
1816                     asy->asy_ioaddr + LCR, (lcr & ~SETBREAK));
1817                 mutex_exit(&asy->asy_excl_hi);
1818                 async->async_flags &= ~ASYNC_OUT_SUSPEND;
1819                 goto nodrain;
1820         }
1821 
1822         /*
1823          * If the user told us not to delay the close ("non-blocking"), then
1824          * don't bother trying to drain.
1825          *
1826          * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
1827          * getting an M_START (since these messages aren't enqueued), and the
1828          * only other way to clear the stop condition is by loss of DCD, which
1829          * would discard the queue data.  Thus, we drop the output data if
1830          * ASYNC_STOPPED is set.
1831          */
1832         if ((flag & (FNDELAY|FNONBLOCK)) ||
1833             (async->async_flags & ASYNC_STOPPED)) {
1834                 goto nodrain;
1835         }
1836 
1837         /*
1838          * If there's any pending output, then we have to try to drain it.
1839          * There are two main cases to be handled:
1840          *      - called by close(2): need to drain until done or until
1841          *        a signal is received.  No timeout.
1842          *      - called by exit(2): need to drain while making progress
1843          *        or until a timeout occurs.  No signals.
1844          *
1845          * If we can't rely on receiving a signal to get us out of a hung
1846          * session, then we have to use a timer.  In this case, we set a timer
1847          * to check for progress in sending the output data -- all that we ask
1848          * (at each interval) is that there's been some progress made.  Since
1849          * the interrupt routine grabs buffers from the write queue, we can't
1850          * trust changes in async_ocnt.  Instead, we use a progress flag.
1851          *
1852          * Note that loss of carrier will cause the output queue to be flushed,
1853          * and we'll wake up again and finish normally.
1854          */
1855         if (!ddi_can_receive_sig() && asy_drain_check != 0) {
1856                 async->async_flags &= ~ASYNC_PROGRESS;
1857                 async->async_timer = timeout(async_progress_check, async,
1858                     drv_usectohz(asy_drain_check));
1859         }
1860         while (async->async_ocnt > 0 ||
1861             async->async_ttycommon.t_writeq->q_first != NULL ||
1862             (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) {
1863                 if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) == 0)
1864                         break;
1865         }
1866         if (async->async_timer != 0) {
1867                 (void) untimeout(async->async_timer);
1868                 async->async_timer = 0;
1869         }
1870 
1871 nodrain:
1872         async->async_ocnt = 0;
1873         if (async->async_xmitblk != NULL)
1874                 freeb(async->async_xmitblk);
1875         async->async_xmitblk = NULL;
1876 
1877         /*
1878          * If line has HUPCL set or is incompletely opened fix up the modem
1879          * lines.
1880          */
1881         DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dclose: next check HUPCL flag\n",
1882             instance);
1883         mutex_enter(&asy->asy_excl_hi);
1884         if ((async->async_ttycommon.t_cflag & HUPCL) ||
1885             (async->async_flags & ASYNC_WOPEN)) {
1886                 DEBUGCONT3(ASY_DEBUG_MODEM,
1887                     "asy%dclose: HUPCL flag = %x, ASYNC_WOPEN flag = %x\n",
1888                     instance,
1889                     async->async_ttycommon.t_cflag & HUPCL,
1890                     async->async_ttycommon.t_cflag & ASYNC_WOPEN);
1891                 async->async_flags |= ASYNC_DTR_DELAY;
1892 
1893                 /* turn off DTR, RTS but NOT interrupt to 386 */
1894                 if (asy->asy_flags & (ASY_IGNORE_CD|ASY_RTS_DTR_OFF)) {
1895                         DEBUGCONT3(ASY_DEBUG_MODEM,
1896                             "asy%dclose: ASY_IGNORE_CD flag = %x, "
1897                             "ASY_RTS_DTR_OFF flag = %x\n",
1898                             instance,
1899                             asy->asy_flags & ASY_IGNORE_CD,
1900                             asy->asy_flags & ASY_RTS_DTR_OFF);
1901 
1902                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
1903                             asy->asy_mcr|OUT2);
1904                 } else {
1905                         DEBUGCONT1(ASY_DEBUG_MODEM,
1906                             "asy%dclose: Dropping DTR and RTS\n", instance);
1907                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
1908                             OUT2);
1909                 }
1910                 async->async_dtrtid =
1911                     timeout((void (*)())async_dtr_free,
1912                     (caddr_t)async, drv_usectohz(asy_min_dtr_low));
1913         }
1914         /*
1915          * If nobody's using it now, turn off receiver interrupts.
1916          */
1917         if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) {
1918                 icr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ICR);
1919                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
1920                     (icr & ~RIEN));
1921         }
1922         mutex_exit(&asy->asy_excl_hi);
1923 out:
1924         ttycommon_close(&async->async_ttycommon);
1925 
1926         /*
1927          * Cancel outstanding "bufcall" request.
1928          */
1929         if (async->async_wbufcid != 0) {
1930                 unbufcall(async->async_wbufcid);
1931                 async->async_wbufcid = 0;
1932         }
1933 
1934         /* Note that qprocsoff can't be done until after interrupts are off */
1935         qprocsoff(q);
1936         q->q_ptr = WR(q)->q_ptr = NULL;
1937         async->async_ttycommon.t_readq = NULL;
1938         async->async_ttycommon.t_writeq = NULL;
1939 
1940         /*
1941          * Clear out device state, except persistant device property flags.
1942          */
1943         async->async_flags &= (ASYNC_DTR_DELAY|ASY_RTS_DTR_OFF);
1944         cv_broadcast(&async->async_flags_cv);
1945         mutex_exit(&asy->asy_excl);
1946 
1947         DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dclose: done\n", instance);
1948         return (0);
1949 }
1950 
1951 static boolean_t
1952 asy_isbusy(struct asycom *asy)
1953 {
1954         struct asyncline *async;
1955 
1956         DEBUGCONT0(ASY_DEBUG_EOT, "asy_isbusy\n");
1957         async = asy->asy_priv;
1958         ASSERT(mutex_owned(&asy->asy_excl));
1959         ASSERT(mutex_owned(&asy->asy_excl_hi));
1960 /*
1961  * XXXX this should be recoded
1962  */
1963         return ((async->async_ocnt > 0) ||
1964             ((ddi_get8(asy->asy_iohandle,
1965             asy->asy_ioaddr + LSR) & (XSRE|XHRE)) == 0));
1966 }
1967 
1968 static void
1969 asy_waiteot(struct asycom *asy)
1970 {
1971         /*
1972          * Wait for the current transmission block and the
1973          * current fifo data to transmit. Once this is done
1974          * we may go on.
1975          */
1976         DEBUGCONT0(ASY_DEBUG_EOT, "asy_waiteot\n");
1977         ASSERT(mutex_owned(&asy->asy_excl));
1978         ASSERT(mutex_owned(&asy->asy_excl_hi));
1979         while (asy_isbusy(asy)) {
1980                 mutex_exit(&asy->asy_excl_hi);
1981                 mutex_exit(&asy->asy_excl);
1982                 drv_usecwait(10000);            /* wait .01 */
1983                 mutex_enter(&asy->asy_excl);
1984                 mutex_enter(&asy->asy_excl_hi);
1985         }
1986 }
1987 
1988 /* asy_reset_fifo -- flush fifos and [re]program fifo control register */
1989 static void
1990 asy_reset_fifo(struct asycom *asy, uchar_t flush)
1991 {
1992         uchar_t lcr;
1993 
1994         /* On a 16750, we have to set DLAB in order to set FIFOEXTRA. */
1995 
1996         if (asy->asy_hwtype >= ASY16750) {
1997                 lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
1998                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
1999                     lcr | DLAB);
2000         }
2001 
2002         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR,
2003             asy->asy_fifor | flush);
2004 
2005         /* Clear DLAB */
2006 
2007         if (asy->asy_hwtype >= ASY16750) {
2008                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr);
2009         }
2010 }
2011 
2012 /*
2013  * Program the ASY port. Most of the async operation is based on the values
2014  * of 'c_iflag' and 'c_cflag'.
2015  */
2016 
2017 #define BAUDINDEX(cflg) (((cflg) & CBAUDEXT) ? \
2018                         (((cflg) & CBAUD) + CBAUD + 1) : ((cflg) & CBAUD))
2019 
2020 static void
2021 asy_program(struct asycom *asy, int mode)
2022 {
2023         struct asyncline *async;
2024         int baudrate, c_flag;
2025         int icr, lcr;
2026         int flush_reg;
2027         int ocflags;
2028 #ifdef DEBUG
2029         int instance;
2030 #endif
2031 
2032         ASSERT(mutex_owned(&asy->asy_excl));
2033         ASSERT(mutex_owned(&asy->asy_excl_hi));
2034 
2035         async = asy->asy_priv;
2036 #ifdef DEBUG
2037         instance = UNIT(async->async_dev);
2038         DEBUGCONT2(ASY_DEBUG_PROCS,
2039             "asy%d_program: mode = 0x%08X, enter\n", instance, mode);
2040 #endif
2041 
2042         baudrate = BAUDINDEX(async->async_ttycommon.t_cflag);
2043 
2044         async->async_ttycommon.t_cflag &= ~(CIBAUD);
2045 
2046         if (baudrate > CBAUD) {
2047                 async->async_ttycommon.t_cflag |= CIBAUDEXT;
2048                 async->async_ttycommon.t_cflag |=
2049                     (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
2050         } else {
2051                 async->async_ttycommon.t_cflag &= ~CIBAUDEXT;
2052                 async->async_ttycommon.t_cflag |=
2053                     ((baudrate << IBSHIFT) & CIBAUD);
2054         }
2055 
2056         c_flag = async->async_ttycommon.t_cflag &
2057             (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT);
2058 
2059         /* disable interrupts */
2060         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
2061 
2062         ocflags = asy->asy_ocflag;
2063 
2064         /* flush/reset the status registers */
2065         (void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR);
2066         (void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
2067         asy->asy_msr = flush_reg = ddi_get8(asy->asy_iohandle,
2068             asy->asy_ioaddr + MSR);
2069         /*
2070          * The device is programmed in the open sequence, if we
2071          * have to hardware handshake, then this is a good time
2072          * to check if the device can receive any data.
2073          */
2074 
2075         if ((CRTSCTS & async->async_ttycommon.t_cflag) && !(flush_reg & CTS)) {
2076                 async_flowcontrol_hw_output(asy, FLOW_STOP);
2077         } else {
2078                 /*
2079                  * We can not use async_flowcontrol_hw_output(asy, FLOW_START)
2080                  * here, because if CRTSCTS is clear, we need clear
2081                  * ASYNC_HW_OUT_FLW bit.
2082                  */
2083                 async->async_flags &= ~ASYNC_HW_OUT_FLW;
2084         }
2085 
2086         /*
2087          * If IXON is not set, clear ASYNC_SW_OUT_FLW;
2088          * If IXON is set, no matter what IXON flag is before this
2089          * function call to asy_program,
2090          * we will use the old ASYNC_SW_OUT_FLW status.
2091          * Because of handling IXON in the driver, we also should re-calculate
2092          * the value of ASYNC_OUT_FLW_RESUME bit, but in fact,
2093          * the TCSET* commands which call asy_program
2094          * are put into the write queue, so there is no output needed to
2095          * be resumed at this point.
2096          */
2097         if (!(IXON & async->async_ttycommon.t_iflag))
2098                 async->async_flags &= ~ASYNC_SW_OUT_FLW;
2099 
2100         /* manually flush receive buffer or fifo (workaround for buggy fifos) */
2101         if (mode == ASY_INIT)
2102                 if (asy->asy_use_fifo == FIFO_ON) {
2103                         for (flush_reg = asy->asy_fifo_buf; flush_reg-- > 0; ) {
2104                                 (void) ddi_get8(asy->asy_iohandle,
2105                                     asy->asy_ioaddr + DAT);
2106                         }
2107                 } else {
2108                         flush_reg = ddi_get8(asy->asy_iohandle,
2109                             asy->asy_ioaddr + DAT);
2110                 }
2111 
2112         if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) {
2113                 /* Set line control */
2114                 lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
2115                 lcr &= ~(WLS0|WLS1|STB|PEN|EPS);
2116 
2117                 if (c_flag & CSTOPB)
2118                         lcr |= STB;     /* 2 stop bits */
2119 
2120                 if (c_flag & PARENB)
2121                         lcr |= PEN;
2122 
2123                 if ((c_flag & PARODD) == 0)
2124                         lcr |= EPS;
2125 
2126                 switch (c_flag & CSIZE) {
2127                 case CS5:
2128                         lcr |= BITS5;
2129                         break;
2130                 case CS6:
2131                         lcr |= BITS6;
2132                         break;
2133                 case CS7:
2134                         lcr |= BITS7;
2135                         break;
2136                 case CS8:
2137                         lcr |= BITS8;
2138                         break;
2139                 }
2140 
2141                 /* set the baud rate, unless it is "0" */
2142                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB);
2143 
2144                 if (baudrate != 0) {
2145                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
2146                             asyspdtab[baudrate] & 0xff);
2147                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
2148                             (asyspdtab[baudrate] >> 8) & 0xff);
2149                 }
2150                 /* set the line control modes */
2151                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr);
2152 
2153                 /*
2154                  * If we have a FIFO buffer, enable/flush
2155                  * at intialize time, flush if transitioning from
2156                  * CREAD off to CREAD on.
2157                  */
2158                 if ((ocflags & CREAD) == 0 && (c_flag & CREAD) ||
2159                     mode == ASY_INIT)
2160                         if (asy->asy_use_fifo == FIFO_ON)
2161                                 asy_reset_fifo(asy, FIFORXFLSH);
2162 
2163                 /* remember the new cflags */
2164                 asy->asy_ocflag = c_flag & ~CLOCAL;
2165         }
2166 
2167         if (baudrate == 0)
2168                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
2169                     (asy->asy_mcr & RTS) | OUT2);
2170         else
2171                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
2172                     asy->asy_mcr | OUT2);
2173 
2174         /*
2175          * Call the modem status interrupt handler to check for the carrier
2176          * in case CLOCAL was turned off after the carrier came on.
2177          * (Note: Modem status interrupt is not enabled if CLOCAL is ON.)
2178          */
2179         async_msint(asy);
2180 
2181         /* Set interrupt control */
2182         DEBUGCONT3(ASY_DEBUG_MODM2,
2183             "asy%d_program: c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x\n",
2184             instance, c_flag & CLOCAL,
2185             async->async_ttycommon.t_cflag & CRTSCTS);
2186 
2187         if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS))
2188                 /*
2189                  * direct-wired line ignores DCD, so we don't enable modem
2190                  * status interrupts.
2191                  */
2192                 icr = (TIEN | SIEN);
2193         else
2194                 icr = (TIEN | SIEN | MIEN);
2195 
2196         if (c_flag & CREAD)
2197                 icr |= RIEN;
2198 
2199         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, icr);
2200         DEBUGCONT1(ASY_DEBUG_PROCS, "asy%d_program: done\n", instance);
2201 }
2202 
2203 static boolean_t
2204 asy_baudok(struct asycom *asy)
2205 {
2206         struct asyncline *async = asy->asy_priv;
2207         int baudrate;
2208 
2209 
2210         baudrate = BAUDINDEX(async->async_ttycommon.t_cflag);
2211 
2212         if (baudrate >= sizeof (asyspdtab)/sizeof (*asyspdtab))
2213                 return (0);
2214 
2215         return (baudrate == 0 || asyspdtab[baudrate]);
2216 }
2217 
2218 /*
2219  * asyintr() is the High Level Interrupt Handler.
2220  *
2221  * There are four different interrupt types indexed by ISR register values:
2222  *              0: modem
2223  *              1: Tx holding register is empty, ready for next char
2224  *              2: Rx register now holds a char to be picked up
2225  *              3: error or break on line
2226  * This routine checks the Bit 0 (interrupt-not-pending) to determine if
2227  * the interrupt is from this port.
2228  */
2229 uint_t
2230 asyintr(caddr_t argasy)
2231 {
2232         struct asycom           *asy = (struct asycom *)argasy;
2233         struct asyncline        *async;
2234         int                     ret_status = DDI_INTR_UNCLAIMED;
2235         uchar_t                 interrupt_id, lsr;
2236 
2237         interrupt_id = ddi_get8(asy->asy_iohandle,
2238             asy->asy_ioaddr + ISR) & 0x0F;
2239         async = asy->asy_priv;
2240 
2241         if ((async == NULL) ||
2242             !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
2243                 if (interrupt_id & NOINTERRUPT)
2244                         return (DDI_INTR_UNCLAIMED);
2245                 else {
2246                         /*
2247                          * reset the device by:
2248                          *      reading line status
2249                          *      reading any data from data status register
2250                          *      reading modem status
2251                          */
2252                         (void) ddi_get8(asy->asy_iohandle,
2253                             asy->asy_ioaddr + LSR);
2254                         (void) ddi_get8(asy->asy_iohandle,
2255                             asy->asy_ioaddr + DAT);
2256                         asy->asy_msr = ddi_get8(asy->asy_iohandle,
2257                             asy->asy_ioaddr + MSR);
2258                         return (DDI_INTR_CLAIMED);
2259                 }
2260         }
2261 
2262         mutex_enter(&asy->asy_excl_hi);
2263 
2264         if (asy->asy_flags & ASY_DDI_SUSPENDED) {
2265                 mutex_exit(&asy->asy_excl_hi);
2266                 return (DDI_INTR_CLAIMED);
2267         }
2268 
2269         /*
2270          * We will loop until the interrupt line is pulled low. asy
2271          * interrupt is edge triggered.
2272          */
2273         /* CSTYLED */
2274         for (;; interrupt_id =
2275             (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR) & 0x0F)) {
2276 
2277                 if (interrupt_id & NOINTERRUPT)
2278                         break;
2279                 ret_status = DDI_INTR_CLAIMED;
2280 
2281                 DEBUGCONT1(ASY_DEBUG_INTR, "asyintr: interrupt_id = 0x%d\n",
2282                     interrupt_id);
2283                 lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
2284                 switch (interrupt_id) {
2285                 case RxRDY:
2286                 case RSTATUS:
2287                 case FFTMOUT:
2288                         /* receiver interrupt or receiver errors */
2289                         async_rxint(asy, lsr);
2290                         break;
2291                 case TxRDY:
2292                         /* transmit interrupt */
2293                         async_txint(asy);
2294                         continue;
2295                 case MSTATUS:
2296                         /* modem status interrupt */
2297                         async_msint(asy);
2298                         break;
2299                 }
2300                 if ((lsr & XHRE) && (async->async_flags & ASYNC_BUSY) &&
2301                     (async->async_ocnt > 0))
2302                         async_txint(asy);
2303         }
2304         mutex_exit(&asy->asy_excl_hi);
2305         return (ret_status);
2306 }
2307 
2308 /*
2309  * Transmitter interrupt service routine.
2310  * If there is more data to transmit in the current pseudo-DMA block,
2311  * send the next character if output is not stopped or draining.
2312  * Otherwise, queue up a soft interrupt.
2313  *
2314  * XXX -  Needs review for HW FIFOs.
2315  */
2316 static void
2317 async_txint(struct asycom *asy)
2318 {
2319         struct asyncline *async = asy->asy_priv;
2320         int             fifo_len;
2321 
2322         /*
2323          * If ASYNC_BREAK or ASYNC_OUT_SUSPEND has been set, return to
2324          * asyintr()'s context to claim the interrupt without performing
2325          * any action. No character will be loaded into FIFO/THR until
2326          * timed or untimed break is removed
2327          */
2328         if (async->async_flags & (ASYNC_BREAK|ASYNC_OUT_SUSPEND))
2329                 return;
2330 
2331         fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
2332         if (fifo_len > asy_max_tx_fifo)
2333                 fifo_len = asy_max_tx_fifo;
2334 
2335         if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL))
2336                 fifo_len--;
2337 
2338         if (async->async_ocnt > 0 && fifo_len > 0 &&
2339             !(async->async_flags &
2340             (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_STOPPED))) {
2341                 while (fifo_len-- > 0 && async->async_ocnt-- > 0) {
2342                         ddi_put8(asy->asy_iohandle,
2343                             asy->asy_ioaddr + DAT, *async->async_optr++);
2344                 }
2345                 async->async_flags |= ASYNC_PROGRESS;
2346         }
2347 
2348         if (fifo_len <= 0)
2349                 return;
2350 
2351         ASYSETSOFT(asy);
2352 }
2353 
2354 /*
2355  * Interrupt on port: handle PPS event.  This function is only called
2356  * for a port on which PPS event handling has been enabled.
2357  */
2358 static void
2359 asy_ppsevent(struct asycom *asy, int msr)
2360 {
2361         if (asy->asy_flags & ASY_PPS_EDGE) {
2362                 /* Have seen leading edge, now look for and record drop */
2363                 if ((msr & DCD) == 0)
2364                         asy->asy_flags &= ~ASY_PPS_EDGE;
2365                 /*
2366                  * Waiting for leading edge, look for rise; stamp event and
2367                  * calibrate kernel clock.
2368                  */
2369         } else if (msr & DCD) {
2370                         /*
2371                          * This code captures a timestamp at the designated
2372                          * transition of the PPS signal (DCD asserted).  The
2373                          * code provides a pointer to the timestamp, as well
2374                          * as the hardware counter value at the capture.
2375                          *
2376                          * Note: the kernel has nano based time values while
2377                          * NTP requires micro based, an in-line fast algorithm
2378                          * to convert nsec to usec is used here -- see hrt2ts()
2379                          * in common/os/timers.c for a full description.
2380                          */
2381                         struct timeval *tvp = &asy_ppsev.tv;
2382                         timestruc_t ts;
2383                         long nsec, usec;
2384 
2385                         asy->asy_flags |= ASY_PPS_EDGE;
2386                         LED_OFF;
2387                         gethrestime(&ts);
2388                         LED_ON;
2389                         nsec = ts.tv_nsec;
2390                         usec = nsec + (nsec >> 2);
2391                         usec = nsec + (usec >> 1);
2392                         usec = nsec + (usec >> 2);
2393                         usec = nsec + (usec >> 4);
2394                         usec = nsec - (usec >> 3);
2395                         usec = nsec + (usec >> 2);
2396                         usec = nsec + (usec >> 3);
2397                         usec = nsec + (usec >> 4);
2398                         usec = nsec + (usec >> 1);
2399                         usec = nsec + (usec >> 6);
2400                         tvp->tv_usec = usec >> 10;
2401                         tvp->tv_sec = ts.tv_sec;
2402 
2403                         ++asy_ppsev.serial;
2404 
2405                         /*
2406                          * Because the kernel keeps a high-resolution time,
2407                          * pass the current highres timestamp in tvp and zero
2408                          * in usec.
2409                          */
2410                         ddi_hardpps(tvp, 0);
2411         }
2412 }
2413 
2414 /*
2415  * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive
2416  * error interrupt.
2417  * Try to put the character into the circular buffer for this line; if it
2418  * overflows, indicate a circular buffer overrun. If this port is always
2419  * to be serviced immediately, or the character is a STOP character, or
2420  * more than 15 characters have arrived, queue up a soft interrupt to
2421  * drain the circular buffer.
2422  * XXX - needs review for hw FIFOs support.
2423  */
2424 
2425 static void
2426 async_rxint(struct asycom *asy, uchar_t lsr)
2427 {
2428         struct asyncline *async = asy->asy_priv;
2429         uchar_t c;
2430         uint_t s, needsoft = 0;
2431         tty_common_t *tp;
2432         int looplim = asy->asy_fifo_buf * 2;
2433 
2434         tp = &async->async_ttycommon;
2435         if (!(tp->t_cflag & CREAD)) {
2436                 while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
2437                         (void) (ddi_get8(asy->asy_iohandle,
2438                             asy->asy_ioaddr + DAT) & 0xff);
2439                         lsr = ddi_get8(asy->asy_iohandle,
2440                             asy->asy_ioaddr + LSR);
2441                         if (looplim-- < 0)           /* limit loop */
2442                                 break;
2443                 }
2444                 return; /* line is not open for read? */
2445         }
2446 
2447         while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
2448                 c = 0;
2449                 s = 0;                          /* reset error status */
2450                 if (lsr & RCA) {
2451                         c = ddi_get8(asy->asy_iohandle,
2452                             asy->asy_ioaddr + DAT) & 0xff;
2453 
2454                         /*
2455                          * We handle XON/XOFF char if IXON is set,
2456                          * but if received char is _POSIX_VDISABLE,
2457                          * we left it to the up level module.
2458                          */
2459                         if (tp->t_iflag & IXON) {
2460                                 if ((c == async->async_stopc) &&
2461                                     (c != _POSIX_VDISABLE)) {
2462                                         async_flowcontrol_sw_output(asy,
2463                                             FLOW_STOP);
2464                                         goto check_looplim;
2465                                 } else if ((c == async->async_startc) &&
2466                                     (c != _POSIX_VDISABLE)) {
2467                                         async_flowcontrol_sw_output(asy,
2468                                             FLOW_START);
2469                                         needsoft = 1;
2470                                         goto check_looplim;
2471                                 }
2472                                 if ((tp->t_iflag & IXANY) &&
2473                                     (async->async_flags & ASYNC_SW_OUT_FLW)) {
2474                                         async_flowcontrol_sw_output(asy,
2475                                             FLOW_START);
2476                                         needsoft = 1;
2477                                 }
2478                         }
2479                 }
2480 
2481                 /*
2482                  * Check for character break sequence
2483                  */
2484                 if ((abort_enable == KIOCABORTALTERNATE) &&
2485                     (asy->asy_flags & ASY_CONSOLE)) {
2486                         if (abort_charseq_recognize(c))
2487                                 abort_sequence_enter((char *)NULL);
2488                 }
2489 
2490                 /* Handle framing errors */
2491                 if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) {
2492                         if (lsr & PARERR) {
2493                                 if (tp->t_iflag & INPCK) /* parity enabled */
2494                                         s |= PERROR;
2495                         }
2496 
2497                         if (lsr & (FRMERR|BRKDET))
2498                                 s |= FRERROR;
2499                         if (lsr & OVRRUN) {
2500                                 async->async_hw_overrun = 1;
2501                                 s |= OVERRUN;
2502                         }
2503                 }
2504 
2505                 if (s == 0)
2506                         if ((tp->t_iflag & PARMRK) &&
2507                             !(tp->t_iflag & (IGNPAR|ISTRIP)) &&
2508                             (c == 0377))
2509                                 if (RING_POK(async, 2)) {
2510                                         RING_PUT(async, 0377);
2511                                         RING_PUT(async, c);
2512                                 } else
2513                                         async->async_sw_overrun = 1;
2514                         else
2515                                 if (RING_POK(async, 1))
2516                                         RING_PUT(async, c);
2517                                 else
2518                                         async->async_sw_overrun = 1;
2519                 else
2520                         if (s & FRERROR) /* Handle framing errors */
2521                                 if (c == 0)
2522                                         if ((asy->asy_flags & ASY_CONSOLE) &&
2523                                             (abort_enable !=
2524                                             KIOCABORTALTERNATE))
2525                                                 abort_sequence_enter((char *)0);
2526                                         else
2527                                                 async->async_break++;
2528                                 else
2529                                         if (RING_POK(async, 1))
2530                                                 RING_MARK(async, c, s);
2531                                         else
2532                                                 async->async_sw_overrun = 1;
2533                         else /* Parity errors are handled by ldterm */
2534                                 if (RING_POK(async, 1))
2535                                         RING_MARK(async, c, s);
2536                                 else
2537                                         async->async_sw_overrun = 1;
2538 check_looplim:
2539                 lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
2540                 if (looplim-- < 0)           /* limit loop */
2541                         break;
2542         }
2543         if ((RING_CNT(async) > (RINGSIZE * 3)/4) &&
2544             !(async->async_inflow_source & IN_FLOW_RINGBUFF)) {
2545                 async_flowcontrol_hw_input(asy, FLOW_STOP, IN_FLOW_RINGBUFF);
2546                 (void) async_flowcontrol_sw_input(asy, FLOW_STOP,
2547                     IN_FLOW_RINGBUFF);
2548         }
2549 
2550         if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft ||
2551             (RING_FRAC(async)) || (async->async_polltid == 0))
2552                 ASYSETSOFT(asy);        /* need a soft interrupt */
2553 }
2554 
2555 /*
2556  * Modem status interrupt.
2557  *
2558  * (Note: It is assumed that the MSR hasn't been read by asyintr().)
2559  */
2560 
2561 static void
2562 async_msint(struct asycom *asy)
2563 {
2564         struct asyncline *async = asy->asy_priv;
2565         int msr, t_cflag = async->async_ttycommon.t_cflag;
2566 #ifdef DEBUG
2567         int instance = UNIT(async->async_dev);
2568 #endif
2569 
2570 async_msint_retry:
2571         /* this resets the interrupt */
2572         msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
2573         DEBUGCONT10(ASY_DEBUG_STATE,
2574             "async%d_msint call #%d:\n"
2575             "   transition: %3s %3s %3s %3s\n"
2576             "current state: %3s %3s %3s %3s\n",
2577             instance,
2578             ++(asy->asy_msint_cnt),
2579             (msr & DCTS) ? "DCTS" : "    ",
2580             (msr & DDSR) ? "DDSR" : "    ",
2581             (msr & DRI)  ? "DRI " : "    ",
2582             (msr & DDCD) ? "DDCD" : "    ",
2583             (msr & CTS)  ? "CTS " : "    ",
2584             (msr & DSR)  ? "DSR " : "    ",
2585             (msr & RI)   ? "RI  " : "    ",
2586             (msr & DCD)  ? "DCD " : "    ");
2587 
2588         /* If CTS status is changed, do H/W output flow control */
2589         if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & CTS) != 0))
2590                 async_flowcontrol_hw_output(asy,
2591                     msr & CTS ? FLOW_START : FLOW_STOP);
2592         /*
2593          * Reading MSR resets the interrupt, we save the
2594          * value of msr so that other functions could examine MSR by
2595          * looking at asy_msr.
2596          */
2597         asy->asy_msr = (uchar_t)msr;
2598 
2599         /* Handle PPS event */
2600         if (asy->asy_flags & ASY_PPS)
2601                 asy_ppsevent(asy, msr);
2602 
2603         async->async_ext++;
2604         ASYSETSOFT(asy);
2605         /*
2606          * We will make sure that the modem status presented to us
2607          * during the previous read has not changed. If the chip samples
2608          * the modem status on the falling edge of the interrupt line,
2609          * and uses this state as the base for detecting change of modem
2610          * status, we would miss a change of modem status event that occured
2611          * after we initiated a read MSR operation.
2612          */
2613         msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
2614         if (STATES(msr) != STATES(asy->asy_msr))
2615                 goto    async_msint_retry;
2616 }
2617 
2618 /*
2619  * Handle a second-stage interrupt.
2620  */
2621 /*ARGSUSED*/
2622 uint_t
2623 asysoftintr(caddr_t intarg)
2624 {
2625         struct asycom *asy = (struct asycom *)intarg;
2626         struct asyncline *async;
2627         int rv;
2628         uint_t cc;
2629 
2630         /*
2631          * Test and clear soft interrupt.
2632          */
2633         mutex_enter(&asy->asy_soft_lock);
2634         DEBUGCONT0(ASY_DEBUG_PROCS, "asysoftintr: enter\n");
2635         rv = asy->asysoftpend;
2636         if (rv != 0)
2637                 asy->asysoftpend = 0;
2638         mutex_exit(&asy->asy_soft_lock);
2639 
2640         if (rv) {
2641                 if (asy->asy_priv == NULL)
2642                         return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
2643                 async = (struct asyncline *)asy->asy_priv;
2644                 mutex_enter(&asy->asy_excl_hi);
2645                 if (asy->asy_flags & ASY_NEEDSOFT) {
2646                         asy->asy_flags &= ~ASY_NEEDSOFT;
2647                         mutex_exit(&asy->asy_excl_hi);
2648                         async_softint(asy);
2649                         mutex_enter(&asy->asy_excl_hi);
2650                 }
2651 
2652                 /*
2653                  * There are some instances where the softintr is not
2654                  * scheduled and hence not called. It so happens that
2655                  * causes the last few characters to be stuck in the
2656                  * ringbuffer. Hence, call the handler once again so
2657                  * the last few characters are cleared.
2658                  */
2659                 cc = RING_CNT(async);
2660                 mutex_exit(&asy->asy_excl_hi);
2661                 if (cc > 0)
2662                         (void) async_softint(asy);
2663         }
2664         return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
2665 }
2666 
2667 /*
2668  * Handle a software interrupt.
2669  */
2670 static void
2671 async_softint(struct asycom *asy)
2672 {
2673         struct asyncline *async = asy->asy_priv;
2674         uint_t  cc;
2675         mblk_t  *bp;
2676         queue_t *q;
2677         uchar_t val;
2678         uchar_t c;
2679         tty_common_t    *tp;
2680         int nb;
2681         int instance = UNIT(async->async_dev);
2682 
2683         DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint\n", instance);
2684         mutex_enter(&asy->asy_excl_hi);
2685         if (asy->asy_flags & ASY_DOINGSOFT) {
2686                 asy->asy_flags |= ASY_DOINGSOFT_RETRY;
2687                 mutex_exit(&asy->asy_excl_hi);
2688                 return;
2689         }
2690         asy->asy_flags |= ASY_DOINGSOFT;
2691 begin:
2692         asy->asy_flags &= ~ASY_DOINGSOFT_RETRY;
2693         mutex_exit(&asy->asy_excl_hi);
2694         mutex_enter(&asy->asy_excl);
2695         tp = &async->async_ttycommon;
2696         q = tp->t_readq;
2697         if (async->async_flags & ASYNC_OUT_FLW_RESUME) {
2698                 if (async->async_ocnt > 0) {
2699                         mutex_enter(&asy->asy_excl_hi);
2700                         async_resume(async);
2701                         mutex_exit(&asy->asy_excl_hi);
2702                 } else {
2703                         if (async->async_xmitblk)
2704                                 freeb(async->async_xmitblk);
2705                         async->async_xmitblk = NULL;
2706                         async_start(async);
2707                 }
2708                 async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
2709         }
2710         mutex_enter(&asy->asy_excl_hi);
2711         if (async->async_ext) {
2712                 async->async_ext = 0;
2713                 /* check for carrier up */
2714                 DEBUGCONT3(ASY_DEBUG_MODM2,
2715                     "async%d_softint: asy_msr & DCD = %x, "
2716                     "tp->t_flags & TS_SOFTCAR = %x\n",
2717                     instance, asy->asy_msr & DCD, tp->t_flags & TS_SOFTCAR);
2718 
2719                 if (asy->asy_msr & DCD) {
2720                         /* carrier present */
2721                         if ((async->async_flags & ASYNC_CARR_ON) == 0) {
2722                                 DEBUGCONT1(ASY_DEBUG_MODM2,
2723                                     "async%d_softint: set ASYNC_CARR_ON\n",
2724                                     instance);
2725                                 async->async_flags |= ASYNC_CARR_ON;
2726                                 if (async->async_flags & ASYNC_ISOPEN) {
2727                                         mutex_exit(&asy->asy_excl_hi);
2728                                         mutex_exit(&asy->asy_excl);
2729                                         (void) putctl(q, M_UNHANGUP);
2730                                         mutex_enter(&asy->asy_excl);
2731                                         mutex_enter(&asy->asy_excl_hi);
2732                                 }
2733                                 cv_broadcast(&async->async_flags_cv);
2734                         }
2735                 } else {
2736                         if ((async->async_flags & ASYNC_CARR_ON) &&
2737                             !(tp->t_cflag & CLOCAL) &&
2738                             !(tp->t_flags & TS_SOFTCAR)) {
2739                                 int flushflag;
2740 
2741                                 DEBUGCONT1(ASY_DEBUG_MODEM,
2742                                     "async%d_softint: carrier dropped, "
2743                                     "so drop DTR\n",
2744                                     instance);
2745                                 /*
2746                                  * Carrier went away.
2747                                  * Drop DTR, abort any output in
2748                                  * progress, indicate that output is
2749                                  * not stopped, and send a hangup
2750                                  * notification upstream.
2751                                  */
2752                                 val = ddi_get8(asy->asy_iohandle,
2753                                     asy->asy_ioaddr + MCR);
2754                                 ddi_put8(asy->asy_iohandle,
2755                                     asy->asy_ioaddr + MCR, (val & ~DTR));
2756 
2757                                 if (async->async_flags & ASYNC_BUSY) {
2758                                         DEBUGCONT0(ASY_DEBUG_BUSY,
2759                                             "async_softint: "
2760                                             "Carrier dropped.  "
2761                                             "Clearing async_ocnt\n");
2762                                         async->async_ocnt = 0;
2763                                 }       /* if */
2764 
2765                                 async->async_flags &= ~ASYNC_STOPPED;
2766                                 if (async->async_flags & ASYNC_ISOPEN) {
2767                                         mutex_exit(&asy->asy_excl_hi);
2768                                         mutex_exit(&asy->asy_excl);
2769                                         (void) putctl(q, M_HANGUP);
2770                                         mutex_enter(&asy->asy_excl);
2771                                         DEBUGCONT1(ASY_DEBUG_MODEM,
2772                                             "async%d_softint: "
2773                                             "putctl(q, M_HANGUP)\n",
2774                                             instance);
2775                                         /*
2776                                          * Flush FIFO buffers
2777                                          * Any data left in there is invalid now
2778                                          */
2779                                         if (asy->asy_use_fifo == FIFO_ON)
2780                                                 asy_reset_fifo(asy, FIFOTXFLSH);
2781                                         /*
2782                                          * Flush our write queue if we have one.
2783                                          * If we're in the midst of close, then
2784                                          * flush everything. Don't leave stale
2785                                          * ioctls lying about.
2786                                          */
2787                                         flushflag = (async->async_flags &
2788                                             ASYNC_CLOSING) ? FLUSHALL :
2789                                             FLUSHDATA;
2790                                         flushq(tp->t_writeq, flushflag);
2791 
2792                                         /* active msg */
2793                                         bp = async->async_xmitblk;
2794                                         if (bp != NULL) {
2795                                                 freeb(bp);
2796                                                 async->async_xmitblk = NULL;
2797                                         }
2798 
2799                                         mutex_enter(&asy->asy_excl_hi);
2800                                         async->async_flags &= ~ASYNC_BUSY;
2801                                         /*
2802                                          * This message warns of Carrier loss
2803                                          * with data left to transmit can hang
2804                                          * the system.
2805                                          */
2806                                         DEBUGCONT0(ASY_DEBUG_MODEM,
2807                                             "async_softint: Flushing to "
2808                                             "prevent HUPCL hanging\n");
2809                                 }       /* if (ASYNC_ISOPEN) */
2810                         }       /* if (ASYNC_CARR_ON && CLOCAL) */
2811                         async->async_flags &= ~ASYNC_CARR_ON;
2812                         cv_broadcast(&async->async_flags_cv);
2813                 }       /* else */
2814         }       /* if (async->async_ext) */
2815 
2816         mutex_exit(&asy->asy_excl_hi);
2817 
2818         /*
2819          * If data has been added to the circular buffer, remove
2820          * it from the buffer, and send it up the stream if there's
2821          * somebody listening. Try to do it 16 bytes at a time. If we
2822          * have more than 16 bytes to move, move 16 byte chunks and
2823          * leave the rest for next time around (maybe it will grow).
2824          */
2825         mutex_enter(&asy->asy_excl_hi);
2826         if (!(async->async_flags & ASYNC_ISOPEN)) {
2827                 RING_INIT(async);
2828                 goto rv;
2829         }
2830         if ((cc = RING_CNT(async)) == 0)
2831                 goto rv;
2832         mutex_exit(&asy->asy_excl_hi);
2833 
2834         if (!canput(q)) {
2835                 mutex_enter(&asy->asy_excl_hi);
2836                 if (!(async->async_inflow_source & IN_FLOW_STREAMS)) {
2837                         async_flowcontrol_hw_input(asy, FLOW_STOP,
2838                             IN_FLOW_STREAMS);
2839                         (void) async_flowcontrol_sw_input(asy, FLOW_STOP,
2840                             IN_FLOW_STREAMS);
2841                 }
2842                 goto rv;
2843         }
2844         if (async->async_inflow_source & IN_FLOW_STREAMS) {
2845                 mutex_enter(&asy->asy_excl_hi);
2846                 async_flowcontrol_hw_input(asy, FLOW_START,
2847                     IN_FLOW_STREAMS);
2848                 (void) async_flowcontrol_sw_input(asy, FLOW_START,
2849                     IN_FLOW_STREAMS);
2850                 mutex_exit(&asy->asy_excl_hi);
2851         }
2852 
2853         DEBUGCONT2(ASY_DEBUG_INPUT, "async%d_softint: %d char(s) in queue.\n",
2854             instance, cc);
2855 
2856         if (!(bp = allocb(cc, BPRI_MED))) {
2857                 mutex_exit(&asy->asy_excl);
2858                 ttycommon_qfull(&async->async_ttycommon, q);
2859                 mutex_enter(&asy->asy_excl);
2860                 mutex_enter(&asy->asy_excl_hi);
2861                 goto rv;
2862         }
2863         mutex_enter(&asy->asy_excl_hi);
2864         do {
2865                 if (RING_ERR(async, S_ERRORS)) {
2866                         RING_UNMARK(async);
2867                         c = RING_GET(async);
2868                         break;
2869                 } else
2870                         *bp->b_wptr++ = RING_GET(async);
2871         } while (--cc);
2872         mutex_exit(&asy->asy_excl_hi);
2873         mutex_exit(&asy->asy_excl);
2874         if (bp->b_wptr > bp->b_rptr) {
2875                         if (!canput(q)) {
2876                                 asyerror(CE_NOTE, "asy%d: local queue full",
2877                                     instance);
2878                                 freemsg(bp);
2879                         } else
2880                                 (void) putq(q, bp);
2881         } else
2882                 freemsg(bp);
2883         /*
2884          * If we have a parity error, then send
2885          * up an M_BREAK with the "bad"
2886          * character as an argument. Let ldterm
2887          * figure out what to do with the error.
2888          */
2889         if (cc) {
2890                 (void) putctl1(q, M_BREAK, c);
2891                 ASYSETSOFT(async->async_common);     /* finish cc chars */
2892         }
2893         mutex_enter(&asy->asy_excl);
2894         mutex_enter(&asy->asy_excl_hi);
2895 rv:
2896         if ((RING_CNT(async) < (RINGSIZE/4)) &&
2897             (async->async_inflow_source & IN_FLOW_RINGBUFF)) {
2898                 async_flowcontrol_hw_input(asy, FLOW_START, IN_FLOW_RINGBUFF);
2899                 (void) async_flowcontrol_sw_input(asy, FLOW_START,
2900                     IN_FLOW_RINGBUFF);
2901         }
2902 
2903         /*
2904          * If a transmission has finished, indicate that it's finished,
2905          * and start that line up again.
2906          */
2907         if (async->async_break > 0) {
2908                 nb = async->async_break;
2909                 async->async_break = 0;
2910                 if (async->async_flags & ASYNC_ISOPEN) {
2911                         mutex_exit(&asy->asy_excl_hi);
2912                         mutex_exit(&asy->asy_excl);
2913                         for (; nb > 0; nb--)
2914                                 (void) putctl(q, M_BREAK);
2915                         mutex_enter(&asy->asy_excl);
2916                         mutex_enter(&asy->asy_excl_hi);
2917                 }
2918         }
2919         if (async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) {
2920                 DEBUGCONT2(ASY_DEBUG_BUSY,
2921                     "async%d_softint: Clearing ASYNC_BUSY.  async_ocnt=%d\n",
2922                     instance,
2923                     async->async_ocnt);
2924                 async->async_flags &= ~ASYNC_BUSY;
2925                 mutex_exit(&asy->asy_excl_hi);
2926                 if (async->async_xmitblk)
2927                         freeb(async->async_xmitblk);
2928                 async->async_xmitblk = NULL;
2929                 async_start(async);
2930                 /*
2931                  * If the flag isn't set after doing the async_start above, we
2932                  * may have finished all the queued output.  Signal any thread
2933                  * stuck in close.
2934                  */
2935                 if (!(async->async_flags & ASYNC_BUSY))
2936                         cv_broadcast(&async->async_flags_cv);
2937                 mutex_enter(&asy->asy_excl_hi);
2938         }
2939         /*
2940          * A note about these overrun bits: all they do is *tell* someone
2941          * about an error- They do not track multiple errors. In fact,
2942          * you could consider them latched register bits if you like.
2943          * We are only interested in printing the error message once for
2944          * any cluster of overrun errrors.
2945          */
2946         if (async->async_hw_overrun) {
2947                 if (async->async_flags & ASYNC_ISOPEN) {
2948                         mutex_exit(&asy->asy_excl_hi);
2949                         mutex_exit(&asy->asy_excl);
2950                         asyerror(CE_NOTE, "asy%d: silo overflow", instance);
2951                         mutex_enter(&asy->asy_excl);
2952                         mutex_enter(&asy->asy_excl_hi);
2953                 }
2954                 async->async_hw_overrun = 0;
2955         }
2956         if (async->async_sw_overrun) {
2957                 if (async->async_flags & ASYNC_ISOPEN) {
2958                         mutex_exit(&asy->asy_excl_hi);
2959                         mutex_exit(&asy->asy_excl);
2960                         asyerror(CE_NOTE, "asy%d: ring buffer overflow",
2961                             instance);
2962                         mutex_enter(&asy->asy_excl);
2963                         mutex_enter(&asy->asy_excl_hi);
2964                 }
2965                 async->async_sw_overrun = 0;
2966         }
2967         if (asy->asy_flags & ASY_DOINGSOFT_RETRY) {
2968                 mutex_exit(&asy->asy_excl);
2969                 goto begin;
2970         }
2971         asy->asy_flags &= ~ASY_DOINGSOFT;
2972         mutex_exit(&asy->asy_excl_hi);
2973         mutex_exit(&asy->asy_excl);
2974         DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint: done\n", instance);
2975 }
2976 
2977 /*
2978  * Restart output on a line after a delay or break timer expired.
2979  */
2980 static void
2981 async_restart(void *arg)
2982 {
2983         struct asyncline *async = (struct asyncline *)arg;
2984         struct asycom *asy = async->async_common;
2985         uchar_t lcr;
2986 
2987         /*
2988          * If break timer expired, turn off the break bit.
2989          */
2990 #ifdef DEBUG
2991         int instance = UNIT(async->async_dev);
2992 
2993         DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_restart\n", instance);
2994 #endif
2995         mutex_enter(&asy->asy_excl);
2996         /*
2997          * If ASYNC_OUT_SUSPEND is also set, we don't really
2998          * clean the HW break, TIOCCBRK is responsible for this.
2999          */
3000         if ((async->async_flags & ASYNC_BREAK) &&
3001             !(async->async_flags & ASYNC_OUT_SUSPEND)) {
3002                 mutex_enter(&asy->asy_excl_hi);
3003                 lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
3004                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
3005                     (lcr & ~SETBREAK));
3006                 mutex_exit(&asy->asy_excl_hi);
3007         }
3008         async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK);
3009         cv_broadcast(&async->async_flags_cv);
3010         async_start(async);
3011 
3012         mutex_exit(&asy->asy_excl);
3013 }
3014 
3015 static void
3016 async_start(struct asyncline *async)
3017 {
3018         async_nstart(async, 0);
3019 }
3020 
3021 /*
3022  * Start output on a line, unless it's busy, frozen, or otherwise.
3023  */
3024 /*ARGSUSED*/
3025 static void
3026 async_nstart(struct asyncline *async, int mode)
3027 {
3028         struct asycom *asy = async->async_common;
3029         int cc;
3030         queue_t *q;
3031         mblk_t *bp;
3032         uchar_t *xmit_addr;
3033         uchar_t val;
3034         int     fifo_len = 1;
3035         boolean_t didsome;
3036         mblk_t *nbp;
3037 
3038 #ifdef DEBUG
3039         int instance = UNIT(async->async_dev);
3040 
3041         DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_nstart\n", instance);
3042 #endif
3043         if (asy->asy_use_fifo == FIFO_ON) {
3044                 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
3045                 if (fifo_len > asy_max_tx_fifo)
3046                         fifo_len = asy_max_tx_fifo;
3047         }
3048 
3049         ASSERT(mutex_owned(&asy->asy_excl));
3050 
3051         /*
3052          * If the chip is busy (i.e., we're waiting for a break timeout
3053          * to expire, or for the current transmission to finish, or for
3054          * output to finish draining from chip), don't grab anything new.
3055          */
3056         if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) {
3057                 DEBUGCONT2((mode? ASY_DEBUG_OUT : 0),
3058                     "async%d_nstart: start %s.\n",
3059                     instance,
3060                     async->async_flags & ASYNC_BREAK ? "break" : "busy");
3061                 return;
3062         }
3063 
3064         /*
3065          * Check only pended sw input flow control.
3066          */
3067         mutex_enter(&asy->asy_excl_hi);
3068         if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL))
3069                 fifo_len--;
3070         mutex_exit(&asy->asy_excl_hi);
3071 
3072         /*
3073          * If we're waiting for a delay timeout to expire, don't grab
3074          * anything new.
3075          */
3076         if (async->async_flags & ASYNC_DELAY) {
3077                 DEBUGCONT1((mode? ASY_DEBUG_OUT : 0),
3078                     "async%d_nstart: start ASYNC_DELAY.\n", instance);
3079                 return;
3080         }
3081 
3082         if ((q = async->async_ttycommon.t_writeq) == NULL) {
3083                 DEBUGCONT1((mode? ASY_DEBUG_OUT : 0),
3084                     "async%d_nstart: start writeq is null.\n", instance);
3085                 return; /* not attached to a stream */
3086         }
3087 
3088         for (;;) {
3089                 if ((bp = getq(q)) == NULL)
3090                         return; /* no data to transmit */
3091 
3092                 /*
3093                  * We have a message block to work on.
3094                  * Check whether it's a break, a delay, or an ioctl (the latter
3095                  * occurs if the ioctl in question was waiting for the output
3096                  * to drain).  If it's one of those, process it immediately.
3097                  */
3098                 switch (bp->b_datap->db_type) {
3099 
3100                 case M_BREAK:
3101                         /*
3102                          * Set the break bit, and arrange for "async_restart"
3103                          * to be called in 1/4 second; it will turn the
3104                          * break bit off, and call "async_start" to grab
3105                          * the next message.
3106                          */
3107                         mutex_enter(&asy->asy_excl_hi);
3108                         val = ddi_get8(asy->asy_iohandle,
3109                             asy->asy_ioaddr + LCR);
3110                         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
3111                             (val | SETBREAK));
3112                         mutex_exit(&asy->asy_excl_hi);
3113                         async->async_flags |= ASYNC_BREAK;
3114                         (void) timeout(async_restart, (caddr_t)async,
3115                             drv_usectohz(1000000)/4);
3116                         freemsg(bp);
3117                         return; /* wait for this to finish */
3118 
3119                 case M_DELAY:
3120                         /*
3121                          * Arrange for "async_restart" to be called when the
3122                          * delay expires; it will turn ASYNC_DELAY off,
3123                          * and call "async_start" to grab the next message.
3124                          */
3125                         (void) timeout(async_restart, (caddr_t)async,
3126                             (int)(*(unsigned char *)bp->b_rptr + 6));
3127                         async->async_flags |= ASYNC_DELAY;
3128                         freemsg(bp);
3129                         return; /* wait for this to finish */
3130 
3131                 case M_IOCTL:
3132                         /*
3133                          * This ioctl was waiting for the output ahead of
3134                          * it to drain; obviously, it has.  Do it, and
3135                          * then grab the next message after it.
3136                          */
3137                         mutex_exit(&asy->asy_excl);
3138                         async_ioctl(async, q, bp);
3139                         mutex_enter(&asy->asy_excl);
3140                         continue;
3141                 }
3142 
3143                 while (bp != NULL && ((cc = MBLKL(bp)) == 0)) {
3144                         nbp = bp->b_cont;
3145                         freeb(bp);
3146                         bp = nbp;
3147                 }
3148                 if (bp != NULL)
3149                         break;
3150         }
3151 
3152         /*
3153          * We have data to transmit.  If output is stopped, put
3154          * it back and try again later.
3155          */
3156         if (async->async_flags & (ASYNC_HW_OUT_FLW | ASYNC_SW_OUT_FLW |
3157             ASYNC_STOPPED | ASYNC_OUT_SUSPEND)) {
3158                 (void) putbq(q, bp);
3159                 return;
3160         }
3161 
3162         async->async_xmitblk = bp;
3163         xmit_addr = bp->b_rptr;
3164         bp = bp->b_cont;
3165         if (bp != NULL)
3166                 (void) putbq(q, bp);    /* not done with this message yet */
3167 
3168         /*
3169          * In 5-bit mode, the high order bits are used
3170          * to indicate character sizes less than five,
3171          * so we need to explicitly mask before transmitting
3172          */
3173         if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) {
3174                 unsigned char *p = xmit_addr;
3175                 int cnt = cc;
3176 
3177                 while (cnt--)
3178                         *p++ &= (unsigned char) 0x1f;
3179         }
3180 
3181         /*
3182          * Set up this block for pseudo-DMA.
3183          */
3184         mutex_enter(&asy->asy_excl_hi);
3185         /*
3186          * If the transmitter is ready, shove the first
3187          * character out.
3188          */
3189         didsome = B_FALSE;
3190         while (--fifo_len >= 0 && cc > 0) {
3191                 if (!(ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) &
3192                     XHRE))
3193                         break;
3194                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
3195                     *xmit_addr++);
3196                 cc--;
3197                 didsome = B_TRUE;
3198         }
3199         async->async_optr = xmit_addr;
3200         async->async_ocnt = cc;
3201         if (didsome)
3202                 async->async_flags |= ASYNC_PROGRESS;
3203         DEBUGCONT2(ASY_DEBUG_BUSY,
3204             "async%d_nstart: Set ASYNC_BUSY.  async_ocnt=%d\n",
3205             instance, async->async_ocnt);
3206         async->async_flags |= ASYNC_BUSY;
3207         mutex_exit(&asy->asy_excl_hi);
3208 }
3209 
3210 /*
3211  * Resume output by poking the transmitter.
3212  */
3213 static void
3214 async_resume(struct asyncline *async)
3215 {
3216         struct asycom *asy = async->async_common;
3217 #ifdef DEBUG
3218         int instance;
3219 #endif
3220 
3221         ASSERT(mutex_owned(&asy->asy_excl_hi));
3222 #ifdef DEBUG
3223         instance = UNIT(async->async_dev);
3224         DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_resume\n", instance);
3225 #endif
3226 
3227         if (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE) {
3228                 if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL))
3229                         return;
3230                 if (async->async_ocnt > 0 &&
3231                     !(async->async_flags &
3232                     (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_OUT_SUSPEND))) {
3233                         ddi_put8(asy->asy_iohandle,
3234                             asy->asy_ioaddr + DAT, *async->async_optr++);
3235                         async->async_ocnt--;
3236                         async->async_flags |= ASYNC_PROGRESS;
3237                 }
3238         }
3239 }
3240 
3241 /*
3242  * Hold the untimed break to last the minimum time.
3243  */
3244 static void
3245 async_hold_utbrk(void *arg)
3246 {
3247         struct asyncline *async = arg;
3248         struct asycom *asy = async->async_common;
3249 
3250         mutex_enter(&asy->asy_excl);
3251         async->async_flags &= ~ASYNC_HOLD_UTBRK;
3252         cv_broadcast(&async->async_flags_cv);
3253         async->async_utbrktid = 0;
3254         mutex_exit(&asy->asy_excl);
3255 }
3256 
3257 /*
3258  * Resume the untimed break.
3259  */
3260 static void
3261 async_resume_utbrk(struct asyncline *async)
3262 {
3263         uchar_t val;
3264         struct asycom *asy = async->async_common;
3265         ASSERT(mutex_owned(&asy->asy_excl));
3266 
3267         /*
3268          * Because the wait time is very short,
3269          * so we use uninterruptably wait.
3270          */
3271         while (async->async_flags & ASYNC_HOLD_UTBRK) {
3272                 cv_wait(&async->async_flags_cv, &asy->asy_excl);
3273         }
3274         mutex_enter(&asy->asy_excl_hi);
3275         /*
3276          * Timed break and untimed break can exist simultaneously,
3277          * if ASYNC_BREAK is also set at here, we don't
3278          * really clean the HW break.
3279          */
3280         if (!(async->async_flags & ASYNC_BREAK)) {
3281                 val = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
3282                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
3283                     (val & ~SETBREAK));
3284         }
3285         async->async_flags &= ~ASYNC_OUT_SUSPEND;
3286         cv_broadcast(&async->async_flags_cv);
3287         if (async->async_ocnt > 0) {
3288                 async_resume(async);
3289                 mutex_exit(&asy->asy_excl_hi);
3290         } else {
3291                 async->async_flags &= ~ASYNC_BUSY;
3292                 mutex_exit(&asy->asy_excl_hi);
3293                 if (async->async_xmitblk != NULL) {
3294                         freeb(async->async_xmitblk);
3295                         async->async_xmitblk = NULL;
3296                 }
3297                 async_start(async);
3298         }
3299 }
3300 
3301 /*
3302  * Process an "ioctl" message sent down to us.
3303  * Note that we don't need to get any locks until we are ready to access
3304  * the hardware.  Nothing we access until then is going to be altered
3305  * outside of the STREAMS framework, so we should be safe.
3306  */
3307 int asydelay = 10000;
3308 static void
3309 async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
3310 {
3311         struct asycom *asy = async->async_common;
3312         tty_common_t  *tp = &async->async_ttycommon;
3313         struct iocblk *iocp;
3314         unsigned datasize;
3315         int error = 0;
3316         uchar_t val;
3317         mblk_t *datamp;
3318         unsigned int index;
3319 
3320 #ifdef DEBUG
3321         int instance = UNIT(async->async_dev);
3322 
3323         DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl\n", instance);
3324 #endif
3325 
3326         if (tp->t_iocpending != NULL) {
3327                 /*
3328                  * We were holding an "ioctl" response pending the
3329                  * availability of an "mblk" to hold data to be passed up;
3330                  * another "ioctl" came through, which means that "ioctl"
3331                  * must have timed out or been aborted.
3332                  */
3333                 freemsg(async->async_ttycommon.t_iocpending);
3334                 async->async_ttycommon.t_iocpending = NULL;
3335         }
3336 
3337         iocp = (struct iocblk *)mp->b_rptr;
3338 
3339         /*
3340          * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl()
3341          * because this function frees up the message block (mp->b_cont) that
3342          * contains the user location where we pass back the results.
3343          *
3344          * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl
3345          * zaps.  We know that ttycommon_ioctl doesn't know any CONS*
3346          * ioctls, so keep the others safe too.
3347          */
3348         DEBUGCONT2(ASY_DEBUG_IOCTL, "async%d_ioctl: %s\n",
3349             instance,
3350             iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" :
3351             iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" :
3352             iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" :
3353             iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" :
3354             "other");
3355 
3356         switch (iocp->ioc_cmd) {
3357         case TIOCMGET:
3358         case TIOCGPPS:
3359         case TIOCSPPS:
3360         case TIOCGPPSEV:
3361         case CONSOPENPOLLEDIO:
3362         case CONSCLOSEPOLLEDIO:
3363         case CONSSETABORTENABLE:
3364         case CONSGETABORTENABLE:
3365                 error = -1; /* Do Nothing */
3366                 break;
3367         default:
3368 
3369                 /*
3370                  * The only way in which "ttycommon_ioctl" can fail is if the
3371                  * "ioctl" requires a response containing data to be returned
3372                  * to the user, and no mblk could be allocated for the data.
3373                  * No such "ioctl" alters our state.  Thus, we always go ahead
3374                  * and do any state-changes the "ioctl" calls for.  If we
3375                  * couldn't allocate the data, "ttycommon_ioctl" has stashed
3376                  * the "ioctl" away safely, so we just call "bufcall" to
3377                  * request that we be called back when we stand a better
3378                  * chance of allocating the data.
3379                  */
3380                 if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) {
3381                         if (async->async_wbufcid)
3382                                 unbufcall(async->async_wbufcid);
3383                         async->async_wbufcid = bufcall(datasize, BPRI_HI,
3384                             (void (*)(void *)) async_reioctl,
3385                             (void *)(intptr_t)async->async_common->asy_unit);
3386                         return;
3387                 }
3388         }
3389 
3390         mutex_enter(&asy->asy_excl);
3391 
3392         if (error == 0) {
3393                 /*
3394                  * "ttycommon_ioctl" did most of the work; we just use the
3395                  * data it set up.
3396                  */
3397                 switch (iocp->ioc_cmd) {
3398 
3399                 case TCSETS:
3400                         mutex_enter(&asy->asy_excl_hi);
3401                         if (asy_baudok(asy))
3402                                 asy_program(asy, ASY_NOINIT);
3403                         else
3404                                 error = EINVAL;
3405                         mutex_exit(&asy->asy_excl_hi);
3406                         break;
3407                 case TCSETSF:
3408                 case TCSETSW:
3409                 case TCSETA:
3410                 case TCSETAW:
3411                 case TCSETAF:
3412                         mutex_enter(&asy->asy_excl_hi);
3413                         if (!asy_baudok(asy))
3414                                 error = EINVAL;
3415                         else {
3416                                 if (asy_isbusy(asy))
3417                                         asy_waiteot(asy);
3418                                 asy_program(asy, ASY_NOINIT);
3419                         }
3420                         mutex_exit(&asy->asy_excl_hi);
3421                         break;
3422                 }
3423         } else if (error < 0) {
3424                 /*
3425                  * "ttycommon_ioctl" didn't do anything; we process it here.
3426                  */
3427                 error = 0;
3428                 switch (iocp->ioc_cmd) {
3429 
3430                 case TIOCGPPS:
3431                         /*
3432                          * Get PPS on/off.
3433                          */
3434                         if (mp->b_cont != NULL)
3435                                 freemsg(mp->b_cont);
3436 
3437                         mp->b_cont = allocb(sizeof (int), BPRI_HI);
3438                         if (mp->b_cont == NULL) {
3439                                 error = ENOMEM;
3440                                 break;
3441                         }
3442                         if (asy->asy_flags & ASY_PPS)
3443                                 *(int *)mp->b_cont->b_wptr = 1;
3444                         else
3445                                 *(int *)mp->b_cont->b_wptr = 0;
3446                         mp->b_cont->b_wptr += sizeof (int);
3447                         mp->b_datap->db_type = M_IOCACK;
3448                         iocp->ioc_count = sizeof (int);
3449                         break;
3450 
3451                 case TIOCSPPS:
3452                         /*
3453                          * Set PPS on/off.
3454                          */
3455                         error = miocpullup(mp, sizeof (int));
3456                         if (error != 0)
3457                                 break;
3458 
3459                         mutex_enter(&asy->asy_excl_hi);
3460                         if (*(int *)mp->b_cont->b_rptr)
3461                                 asy->asy_flags |= ASY_PPS;
3462                         else
3463                                 asy->asy_flags &= ~ASY_PPS;
3464                         /* Reset edge sense */
3465                         asy->asy_flags &= ~ASY_PPS_EDGE;
3466                         mutex_exit(&asy->asy_excl_hi);
3467                         mp->b_datap->db_type = M_IOCACK;
3468                         break;
3469 
3470                 case TIOCGPPSEV:
3471                 {
3472                         /*
3473                          * Get PPS event data.
3474                          */
3475                         mblk_t *bp;
3476                         void *buf;
3477 #ifdef _SYSCALL32_IMPL
3478                         struct ppsclockev32 p32;
3479 #endif
3480                         struct ppsclockev ppsclockev;
3481 
3482                         if (mp->b_cont != NULL) {
3483                                 freemsg(mp->b_cont);
3484                                 mp->b_cont = NULL;
3485                         }
3486 
3487                         if ((asy->asy_flags & ASY_PPS) == 0) {
3488                                 error = ENXIO;
3489                                 break;
3490                         }
3491 
3492                         /* Protect from incomplete asy_ppsev */
3493                         mutex_enter(&asy->asy_excl_hi);
3494                         ppsclockev = asy_ppsev;
3495                         mutex_exit(&asy->asy_excl_hi);
3496 
3497 #ifdef _SYSCALL32_IMPL
3498                         if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
3499                                 TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv);
3500                                 p32.serial = ppsclockev.serial;
3501                                 buf = &p32;
3502                                 iocp->ioc_count = sizeof (struct ppsclockev32);
3503                         } else
3504 #endif
3505                         {
3506                                 buf = &ppsclockev;
3507                                 iocp->ioc_count = sizeof (struct ppsclockev);
3508                         }
3509 
3510                         if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) {
3511                                 error = ENOMEM;
3512                                 break;
3513                         }
3514                         mp->b_cont = bp;
3515 
3516                         bcopy(buf, bp->b_wptr, iocp->ioc_count);
3517                         bp->b_wptr += iocp->ioc_count;
3518                         mp->b_datap->db_type = M_IOCACK;
3519                         break;
3520                 }
3521 
3522                 case TCSBRK:
3523                         error = miocpullup(mp, sizeof (int));
3524                         if (error != 0)
3525                                 break;
3526 
3527                         if (*(int *)mp->b_cont->b_rptr == 0) {
3528 
3529                                 /*
3530                                  * XXX Arrangements to ensure that a break
3531                                  * isn't in progress should be sufficient.
3532                                  * This ugly delay() is the only thing
3533                                  * that seems to work on the NCR Worldmark.
3534                                  * It should be replaced. Note that an
3535                                  * asy_waiteot() also does not work.
3536                                  */
3537                                 if (asydelay)
3538                                         delay(drv_usectohz(asydelay));
3539 
3540                                 while (async->async_flags & ASYNC_BREAK) {
3541                                         cv_wait(&async->async_flags_cv,
3542                                             &asy->asy_excl);
3543                                 }
3544                                 mutex_enter(&asy->asy_excl_hi);
3545                                 /*
3546                                  * We loop until the TSR is empty and then
3547                                  * set the break.  ASYNC_BREAK has been set
3548                                  * to ensure that no characters are
3549                                  * transmitted while the TSR is being
3550                                  * flushed and SOUT is being used for the
3551                                  * break signal.
3552                                  *
3553                                  * The wait period is equal to
3554                                  * clock / (baud * 16) * 16 * 2.
3555                                  */
3556                                 index = BAUDINDEX(
3557                                     async->async_ttycommon.t_cflag);
3558                                 async->async_flags |= ASYNC_BREAK;
3559 
3560                                 while ((ddi_get8(asy->asy_iohandle,
3561                                     asy->asy_ioaddr + LSR) & XSRE) == 0) {
3562                                         mutex_exit(&asy->asy_excl_hi);
3563                                         mutex_exit(&asy->asy_excl);
3564                                         drv_usecwait(
3565                                             32*asyspdtab[index] & 0xfff);
3566                                         mutex_enter(&asy->asy_excl);
3567                                         mutex_enter(&asy->asy_excl_hi);
3568                                 }
3569                                 /*
3570                                  * Arrange for "async_restart"
3571                                  * to be called in 1/4 second;
3572                                  * it will turn the break bit off, and call
3573                                  * "async_start" to grab the next message.
3574                                  */
3575                                 val = ddi_get8(asy->asy_iohandle,
3576                                     asy->asy_ioaddr + LCR);
3577                                 ddi_put8(asy->asy_iohandle,
3578                                     asy->asy_ioaddr + LCR,
3579                                     (val | SETBREAK));
3580                                 mutex_exit(&asy->asy_excl_hi);
3581                                 (void) timeout(async_restart, (caddr_t)async,
3582                                     drv_usectohz(1000000)/4);
3583                         } else {
3584                                 DEBUGCONT1(ASY_DEBUG_OUT,
3585                                     "async%d_ioctl: wait for flush.\n",
3586                                     instance);
3587                                 mutex_enter(&asy->asy_excl_hi);
3588                                 asy_waiteot(asy);
3589                                 mutex_exit(&asy->asy_excl_hi);
3590                                 DEBUGCONT1(ASY_DEBUG_OUT,
3591                                     "async%d_ioctl: ldterm satisfied.\n",
3592                                     instance);
3593                         }
3594                         break;
3595 
3596                 case TIOCSBRK:
3597                         if (!(async->async_flags & ASYNC_OUT_SUSPEND)) {
3598                                 mutex_enter(&asy->asy_excl_hi);
3599                                 async->async_flags |= ASYNC_OUT_SUSPEND;
3600                                 async->async_flags |= ASYNC_HOLD_UTBRK;
3601                                 index = BAUDINDEX(
3602                                     async->async_ttycommon.t_cflag);
3603                                 while ((ddi_get8(asy->asy_iohandle,
3604                                     asy->asy_ioaddr + LSR) & XSRE) == 0) {
3605                                         mutex_exit(&asy->asy_excl_hi);
3606                                         mutex_exit(&asy->asy_excl);
3607                                         drv_usecwait(
3608                                             32*asyspdtab[index] & 0xfff);
3609                                         mutex_enter(&asy->asy_excl);
3610                                         mutex_enter(&asy->asy_excl_hi);
3611                                 }
3612                                 val = ddi_get8(asy->asy_iohandle,
3613                                     asy->asy_ioaddr + LCR);
3614                                 ddi_put8(asy->asy_iohandle,
3615                                     asy->asy_ioaddr + LCR, (val | SETBREAK));
3616                                 mutex_exit(&asy->asy_excl_hi);
3617                                 /* wait for 100ms to hold BREAK */
3618                                 async->async_utbrktid =
3619                                     timeout((void (*)())async_hold_utbrk,
3620                                     (caddr_t)async,
3621                                     drv_usectohz(asy_min_utbrk));
3622                         }
3623                         mioc2ack(mp, NULL, 0, 0);
3624                         break;
3625 
3626                 case TIOCCBRK:
3627                         if (async->async_flags & ASYNC_OUT_SUSPEND)
3628                                 async_resume_utbrk(async);
3629                         mioc2ack(mp, NULL, 0, 0);
3630                         break;
3631 
3632                 case TIOCMSET:
3633                 case TIOCMBIS:
3634                 case TIOCMBIC:
3635                         if (iocp->ioc_count != TRANSPARENT) {
3636                                 DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
3637                                     "non-transparent\n", instance);
3638 
3639                                 error = miocpullup(mp, sizeof (int));
3640                                 if (error != 0)
3641                                         break;
3642 
3643                                 mutex_enter(&asy->asy_excl_hi);
3644                                 (void) asymctl(asy,
3645                                     dmtoasy(*(int *)mp->b_cont->b_rptr),
3646                                     iocp->ioc_cmd);
3647                                 mutex_exit(&asy->asy_excl_hi);
3648                                 iocp->ioc_error = 0;
3649                                 mp->b_datap->db_type = M_IOCACK;
3650                         } else {
3651                                 DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
3652                                     "transparent\n", instance);
3653                                 mcopyin(mp, NULL, sizeof (int), NULL);
3654                         }
3655                         break;
3656 
3657                 case TIOCMGET:
3658                         datamp = allocb(sizeof (int), BPRI_MED);
3659                         if (datamp == NULL) {
3660                                 error = EAGAIN;
3661                                 break;
3662                         }
3663 
3664                         mutex_enter(&asy->asy_excl_hi);
3665                         *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET);
3666                         mutex_exit(&asy->asy_excl_hi);
3667 
3668                         if (iocp->ioc_count == TRANSPARENT) {
3669                                 DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
3670                                     "transparent\n", instance);
3671                                 mcopyout(mp, NULL, sizeof (int), NULL, datamp);
3672                         } else {
3673                                 DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
3674                                     "non-transparent\n", instance);
3675                                 mioc2ack(mp, datamp, sizeof (int), 0);
3676                         }
3677                         break;
3678 
3679                 case CONSOPENPOLLEDIO:
3680                         error = miocpullup(mp, sizeof (struct cons_polledio *));
3681                         if (error != 0)
3682                                 break;
3683 
3684                         *(struct cons_polledio **)mp->b_cont->b_rptr =
3685                             &asy->polledio;
3686 
3687                         mp->b_datap->db_type = M_IOCACK;
3688                         break;
3689 
3690                 case CONSCLOSEPOLLEDIO:
3691                         mp->b_datap->db_type = M_IOCACK;
3692                         iocp->ioc_error = 0;
3693                         iocp->ioc_rval = 0;
3694                         break;
3695 
3696                 case CONSSETABORTENABLE:
3697                         error = secpolicy_console(iocp->ioc_cr);
3698                         if (error != 0)
3699                                 break;
3700 
3701                         if (iocp->ioc_count != TRANSPARENT) {
3702                                 error = EINVAL;
3703                                 break;
3704                         }
3705 
3706                         if (*(intptr_t *)mp->b_cont->b_rptr)
3707                                 asy->asy_flags |= ASY_CONSOLE;
3708                         else
3709                                 asy->asy_flags &= ~ASY_CONSOLE;
3710 
3711                         mp->b_datap->db_type = M_IOCACK;
3712                         iocp->ioc_error = 0;
3713                         iocp->ioc_rval = 0;
3714                         break;
3715 
3716                 case CONSGETABORTENABLE:
3717                         /*CONSTANTCONDITION*/
3718                         ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *));
3719                         /*
3720                          * Store the return value right in the payload
3721                          * we were passed.  Crude.
3722                          */
3723                         mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL);
3724                         *(boolean_t *)mp->b_cont->b_rptr =
3725                             (asy->asy_flags & ASY_CONSOLE) != 0;
3726                         break;
3727 
3728                 default:
3729                         /*
3730                          * If we don't understand it, it's an error.  NAK it.
3731                          */
3732                         error = EINVAL;
3733                         break;
3734                 }
3735         }
3736         if (error != 0) {
3737                 iocp->ioc_error = error;
3738                 mp->b_datap->db_type = M_IOCNAK;
3739         }
3740         mutex_exit(&asy->asy_excl);
3741         qreply(wq, mp);
3742         DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl: done\n", instance);
3743 }
3744 
3745 static int
3746 asyrsrv(queue_t *q)
3747 {
3748         mblk_t *bp;
3749         struct asyncline *async;
3750 
3751         async = (struct asyncline *)q->q_ptr;
3752 
3753         while (canputnext(q) && (bp = getq(q)))
3754                 putnext(q, bp);
3755         ASYSETSOFT(async->async_common);
3756         async->async_polltid = 0;
3757         return (0);
3758 }
3759 
3760 /*
3761  * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should
3762  * handle messages as though the driver is operating normally or is
3763  * suspended.  In the suspended case, some or all of the processing may have
3764  * to be delayed until the driver is resumed.
3765  */
3766 #define ASYWPUTDO_NOT_SUSP(async, wput) \
3767         !((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED))
3768 
3769 /*
3770  * Processing for write queue put procedure.
3771  * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
3772  * set the flow control character for M_STOPI and M_STARTI messages;
3773  * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
3774  * by the start routine, and then call the start routine; discard
3775  * everything else.  Note that this driver does not incorporate any
3776  * mechanism to negotiate to handle the canonicalization process.
3777  * It expects that these functions are handled in upper module(s),
3778  * as we do in ldterm.
3779  */
3780 static int
3781 asywputdo(queue_t *q, mblk_t *mp, boolean_t wput)
3782 {
3783         struct asyncline *async;
3784         struct asycom *asy;
3785 #ifdef DEBUG
3786         int instance;
3787 #endif
3788         int error;
3789 
3790         async = (struct asyncline *)q->q_ptr;
3791 
3792 #ifdef DEBUG
3793         instance = UNIT(async->async_dev);
3794 #endif
3795         asy = async->async_common;
3796 
3797         switch (mp->b_datap->db_type) {
3798 
3799         case M_STOP:
3800                 /*
3801                  * Since we don't do real DMA, we can just let the
3802                  * chip coast to a stop after applying the brakes.
3803                  */
3804                 mutex_enter(&asy->asy_excl);
3805                 async->async_flags |= ASYNC_STOPPED;
3806                 mutex_exit(&asy->asy_excl);
3807                 freemsg(mp);
3808                 break;
3809 
3810         case M_START:
3811                 mutex_enter(&asy->asy_excl);
3812                 if (async->async_flags & ASYNC_STOPPED) {
3813                         async->async_flags &= ~ASYNC_STOPPED;
3814                         if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3815                                 /*
3816                                  * If an output operation is in progress,
3817                                  * resume it.  Otherwise, prod the start
3818                                  * routine.
3819                                  */
3820                                 if (async->async_ocnt > 0) {
3821                                         mutex_enter(&asy->asy_excl_hi);
3822                                         async_resume(async);
3823                                         mutex_exit(&asy->asy_excl_hi);
3824                                 } else {
3825                                         async_start(async);
3826                                 }
3827                         }
3828                 }
3829                 mutex_exit(&asy->asy_excl);
3830                 freemsg(mp);
3831                 break;
3832 
3833         case M_IOCTL:
3834                 switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
3835 
3836                 case TCSBRK:
3837                         error = miocpullup(mp, sizeof (int));
3838                         if (error != 0) {
3839                                 miocnak(q, mp, 0, error);
3840                                 return (0);
3841                         }
3842 
3843                         if (*(int *)mp->b_cont->b_rptr != 0) {
3844                                 DEBUGCONT1(ASY_DEBUG_OUT,
3845                                     "async%d_ioctl: flush request.\n",
3846                                     instance);
3847                                 (void) putq(q, mp);
3848 
3849                                 mutex_enter(&asy->asy_excl);
3850                                 if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3851                                         /*
3852                                          * If an TIOCSBRK is in progress,
3853                                          * clean it as TIOCCBRK does,
3854                                          * then kick off output.
3855                                          * If TIOCSBRK is not in progress,
3856                                          * just kick off output.
3857                                          */
3858                                         async_resume_utbrk(async);
3859                                 }
3860                                 mutex_exit(&asy->asy_excl);
3861                                 break;
3862                         }
3863                         /*FALLTHROUGH*/
3864                 case TCSETSW:
3865                 case TCSETSF:
3866                 case TCSETAW:
3867                 case TCSETAF:
3868                         /*
3869                          * The changes do not take effect until all
3870                          * output queued before them is drained.
3871                          * Put this message on the queue, so that
3872                          * "async_start" will see it when it's done
3873                          * with the output before it.  Poke the
3874                          * start routine, just in case.
3875                          */
3876                         (void) putq(q, mp);
3877 
3878                         mutex_enter(&asy->asy_excl);
3879                         if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3880                                 /*
3881                                  * If an TIOCSBRK is in progress,
3882                                  * clean it as TIOCCBRK does.
3883                                  * then kick off output.
3884                                  * If TIOCSBRK is not in progress,
3885                                  * just kick off output.
3886                                  */
3887                                 async_resume_utbrk(async);
3888                         }
3889                         mutex_exit(&asy->asy_excl);
3890                         break;
3891 
3892                 default:
3893                         /*
3894                          * Do it now.
3895                          */
3896                         mutex_enter(&asy->asy_excl);
3897                         if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3898                                 mutex_exit(&asy->asy_excl);
3899                                 async_ioctl(async, q, mp);
3900                                 break;
3901                         }
3902                         async_put_suspq(asy, mp);
3903                         mutex_exit(&asy->asy_excl);
3904                         break;
3905                 }
3906                 break;
3907 
3908         case M_FLUSH:
3909                 if (*mp->b_rptr & FLUSHW) {
3910                         mutex_enter(&asy->asy_excl);
3911 
3912                         /*
3913                          * Abort any output in progress.
3914                          */
3915                         mutex_enter(&asy->asy_excl_hi);
3916                         if (async->async_flags & ASYNC_BUSY) {
3917                                 DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: "
3918                                     "Clearing async_ocnt, "
3919                                     "leaving ASYNC_BUSY set\n",
3920                                     instance);
3921                                 async->async_ocnt = 0;
3922                                 async->async_flags &= ~ASYNC_BUSY;
3923                         } /* if */
3924 
3925                         if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3926                                 /* Flush FIFO buffers */
3927                                 if (asy->asy_use_fifo == FIFO_ON) {
3928                                         asy_reset_fifo(asy, FIFOTXFLSH);
3929                                 }
3930                         }
3931                         mutex_exit(&asy->asy_excl_hi);
3932 
3933                         /* Flush FIFO buffers */
3934                         if (asy->asy_use_fifo == FIFO_ON) {
3935                                 asy_reset_fifo(asy, FIFOTXFLSH);
3936                         }
3937 
3938                         /*
3939                          * Flush our write queue.
3940                          */
3941                         flushq(q, FLUSHDATA);   /* XXX doesn't flush M_DELAY */
3942                         if (async->async_xmitblk != NULL) {
3943                                 freeb(async->async_xmitblk);
3944                                 async->async_xmitblk = NULL;
3945                         }
3946                         mutex_exit(&asy->asy_excl);
3947                         *mp->b_rptr &= ~FLUSHW;  /* it has been flushed */
3948                 }
3949                 if (*mp->b_rptr & FLUSHR) {
3950                         if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3951                                 /* Flush FIFO buffers */
3952                                 if (asy->asy_use_fifo == FIFO_ON) {
3953                                         asy_reset_fifo(asy, FIFORXFLSH);
3954                                 }
3955                         }
3956                         flushq(RD(q), FLUSHDATA);
3957                         qreply(q, mp);  /* give the read queues a crack at it */
3958                 } else {
3959                         freemsg(mp);
3960                 }
3961 
3962                 /*
3963                  * We must make sure we process messages that survive the
3964                  * write-side flush.
3965                  */
3966                 if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3967                         mutex_enter(&asy->asy_excl);
3968                         async_start(async);
3969                         mutex_exit(&asy->asy_excl);
3970                 }
3971                 break;
3972 
3973         case M_BREAK:
3974         case M_DELAY:
3975         case M_DATA:
3976                 /*
3977                  * Queue the message up to be transmitted,
3978                  * and poke the start routine.
3979                  */
3980                 (void) putq(q, mp);
3981                 if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3982                         mutex_enter(&asy->asy_excl);
3983                         async_start(async);
3984                         mutex_exit(&asy->asy_excl);
3985                 }
3986                 break;
3987 
3988         case M_STOPI:
3989                 mutex_enter(&asy->asy_excl);
3990                 if (ASYWPUTDO_NOT_SUSP(async, wput)) {
3991                         mutex_enter(&asy->asy_excl_hi);
3992                         if (!(async->async_inflow_source & IN_FLOW_USER)) {
3993                                 async_flowcontrol_hw_input(asy, FLOW_STOP,
3994                                     IN_FLOW_USER);
3995                                 (void) async_flowcontrol_sw_input(asy,
3996                                     FLOW_STOP, IN_FLOW_USER);
3997                         }
3998                         mutex_exit(&asy->asy_excl_hi);
3999                         mutex_exit(&asy->asy_excl);
4000                         freemsg(mp);
4001                         break;
4002                 }
4003                 async_put_suspq(asy, mp);
4004                 mutex_exit(&asy->asy_excl);
4005                 break;
4006 
4007         case M_STARTI:
4008                 mutex_enter(&asy->asy_excl);
4009                 if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4010                         mutex_enter(&asy->asy_excl_hi);
4011                         if (async->async_inflow_source & IN_FLOW_USER) {
4012                                 async_flowcontrol_hw_input(asy, FLOW_START,
4013                                     IN_FLOW_USER);
4014                                 (void) async_flowcontrol_sw_input(asy,
4015                                     FLOW_START, IN_FLOW_USER);
4016                         }
4017                         mutex_exit(&asy->asy_excl_hi);
4018                         mutex_exit(&asy->asy_excl);
4019                         freemsg(mp);
4020                         break;
4021                 }
4022                 async_put_suspq(asy, mp);
4023                 mutex_exit(&asy->asy_excl);
4024                 break;
4025 
4026         case M_CTL:
4027                 if (MBLKL(mp) >= sizeof (struct iocblk) &&
4028                     ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
4029                         mutex_enter(&asy->asy_excl);
4030                         if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4031                                 ((struct iocblk *)mp->b_rptr)->ioc_cmd =
4032                                     MC_HAS_POSIX;
4033                                 mutex_exit(&asy->asy_excl);
4034                                 qreply(q, mp);
4035                                 break;
4036                         } else {
4037                                 async_put_suspq(asy, mp);
4038                         }
4039                 } else {
4040                         /*
4041                          * These MC_SERVICE type messages are used by upper
4042                          * modules to tell this driver to send input up
4043                          * immediately, or that it can wait for normal
4044                          * processing that may or may not be done.  Sun
4045                          * requires these for the mouse module.
4046                          * (XXX - for x86?)
4047                          */
4048                         mutex_enter(&asy->asy_excl);
4049                         switch (*mp->b_rptr) {
4050 
4051                         case MC_SERVICEIMM:
4052                                 async->async_flags |= ASYNC_SERVICEIMM;
4053                                 break;
4054 
4055                         case MC_SERVICEDEF:
4056                                 async->async_flags &= ~ASYNC_SERVICEIMM;
4057                                 break;
4058                         }
4059                         mutex_exit(&asy->asy_excl);
4060                         freemsg(mp);
4061                 }
4062                 break;
4063 
4064         case M_IOCDATA:
4065                 mutex_enter(&asy->asy_excl);
4066                 if (ASYWPUTDO_NOT_SUSP(async, wput)) {
4067                         mutex_exit(&asy->asy_excl);
4068                         async_iocdata(q, mp);
4069                         break;
4070                 }
4071                 async_put_suspq(asy, mp);
4072                 mutex_exit(&asy->asy_excl);
4073                 break;
4074 
4075         default:
4076                 freemsg(mp);
4077                 break;
4078         }
4079         return (0);
4080 }
4081 
4082 static int
4083 asywput(queue_t *q, mblk_t *mp)
4084 {
4085         return (asywputdo(q, mp, B_TRUE));
4086 }
4087 
4088 /*
4089  * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
4090  * the buffer we need.
4091  */
4092 static void
4093 async_reioctl(void *unit)
4094 {
4095         int instance = (uintptr_t)unit;
4096         struct asyncline *async;
4097         struct asycom *asy;
4098         queue_t *q;
4099         mblk_t  *mp;
4100 
4101         asy = ddi_get_soft_state(asy_soft_state, instance);
4102         ASSERT(asy != NULL);
4103         async = asy->asy_priv;
4104 
4105         /*
4106          * The bufcall is no longer pending.
4107          */
4108         mutex_enter(&asy->asy_excl);
4109         async->async_wbufcid = 0;
4110         if ((q = async->async_ttycommon.t_writeq) == NULL) {
4111                 mutex_exit(&asy->asy_excl);
4112                 return;
4113         }
4114         if ((mp = async->async_ttycommon.t_iocpending) != NULL) {
4115                 /* not pending any more */
4116                 async->async_ttycommon.t_iocpending = NULL;
4117                 mutex_exit(&asy->asy_excl);
4118                 async_ioctl(async, q, mp);
4119         } else
4120                 mutex_exit(&asy->asy_excl);
4121 }
4122 
4123 static void
4124 async_iocdata(queue_t *q, mblk_t *mp)
4125 {
4126         struct asyncline        *async = (struct asyncline *)q->q_ptr;
4127         struct asycom           *asy;
4128         struct iocblk *ip;
4129         struct copyresp *csp;
4130 #ifdef DEBUG
4131         int instance = UNIT(async->async_dev);
4132 #endif
4133 
4134         asy = async->async_common;
4135         ip = (struct iocblk *)mp->b_rptr;
4136         csp = (struct copyresp *)mp->b_rptr;
4137 
4138         if (csp->cp_rval != 0) {
4139                 if (csp->cp_private)
4140                         freemsg(csp->cp_private);
4141                 freemsg(mp);
4142                 return;
4143         }
4144 
4145         mutex_enter(&asy->asy_excl);
4146         DEBUGCONT2(ASY_DEBUG_MODEM, "async%d_iocdata: case %s\n",
4147             instance,
4148             csp->cp_cmd == TIOCMGET ? "TIOCMGET" :
4149             csp->cp_cmd == TIOCMSET ? "TIOCMSET" :
4150             csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" :
4151             "TIOCMBIC");
4152         switch (csp->cp_cmd) {
4153 
4154         case TIOCMGET:
4155                 if (mp->b_cont) {
4156                         freemsg(mp->b_cont);
4157                         mp->b_cont = NULL;
4158                 }
4159                 mp->b_datap->db_type = M_IOCACK;
4160                 ip->ioc_error = 0;
4161                 ip->ioc_count = 0;
4162                 ip->ioc_rval = 0;
4163                 mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
4164                 break;
4165 
4166         case TIOCMSET:
4167         case TIOCMBIS:
4168         case TIOCMBIC:
4169                 mutex_enter(&asy->asy_excl_hi);
4170                 (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr),
4171                     csp->cp_cmd);
4172                 mutex_exit(&asy->asy_excl_hi);
4173                 mioc2ack(mp, NULL, 0, 0);
4174                 break;
4175 
4176         default:
4177                 mp->b_datap->db_type = M_IOCNAK;
4178                 ip->ioc_error = EINVAL;
4179                 break;
4180         }
4181         qreply(q, mp);
4182         mutex_exit(&asy->asy_excl);
4183 }
4184 
4185 /*
4186  * debugger/console support routines.
4187  */
4188 
4189 /*
4190  * put a character out
4191  * Do not use interrupts.  If char is LF, put out CR, LF.
4192  */
4193 static void
4194 asyputchar(cons_polledio_arg_t arg, uchar_t c)
4195 {
4196         struct asycom *asy = (struct asycom *)arg;
4197 
4198         if (c == '\n')
4199                 asyputchar(arg, '\r');
4200 
4201         while ((ddi_get8(asy->asy_iohandle,
4202             asy->asy_ioaddr + LSR) & XHRE) == 0) {
4203                 /* wait for xmit to finish */
4204                 drv_usecwait(10);
4205         }
4206 
4207         /* put the character out */
4208         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, c);
4209 }
4210 
4211 /*
4212  * See if there's a character available. If no character is
4213  * available, return 0. Run in polled mode, no interrupts.
4214  */
4215 static boolean_t
4216 asyischar(cons_polledio_arg_t arg)
4217 {
4218         struct asycom *asy = (struct asycom *)arg;
4219 
4220         return ((ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & RCA)
4221             != 0);
4222 }
4223 
4224 /*
4225  * Get a character. Run in polled mode, no interrupts.
4226  */
4227 static int
4228 asygetchar(cons_polledio_arg_t arg)
4229 {
4230         struct asycom *asy = (struct asycom *)arg;
4231 
4232         while (!asyischar(arg))
4233                 drv_usecwait(10);
4234         return (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + DAT));
4235 }
4236 
4237 /*
4238  * Set or get the modem control status.
4239  */
4240 static int
4241 asymctl(struct asycom *asy, int bits, int how)
4242 {
4243         int mcr_r, msr_r;
4244         int instance = asy->asy_unit;
4245 
4246         ASSERT(mutex_owned(&asy->asy_excl_hi));
4247         ASSERT(mutex_owned(&asy->asy_excl));
4248 
4249         /* Read Modem Control Registers */
4250         mcr_r = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
4251 
4252         switch (how) {
4253 
4254         case TIOCMSET:
4255                 DEBUGCONT2(ASY_DEBUG_MODEM,
4256                     "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits);
4257                 mcr_r = bits;           /* Set bits     */
4258                 break;
4259 
4260         case TIOCMBIS:
4261                 DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIS, bits = %x\n",
4262                     instance, bits);
4263                 mcr_r |= bits;          /* Mask in bits */
4264                 break;
4265 
4266         case TIOCMBIC:
4267                 DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIC, bits = %x\n",
4268                     instance, bits);
4269                 mcr_r &= ~bits;             /* Mask out bits */
4270                 break;
4271 
4272         case TIOCMGET:
4273                 /* Read Modem Status Registers */
4274                 /*
4275                  * If modem interrupts are enabled, we return the
4276                  * saved value of msr. We read MSR only in async_msint()
4277                  */
4278                 if (ddi_get8(asy->asy_iohandle,
4279                     asy->asy_ioaddr + ICR) & MIEN) {
4280                         msr_r = asy->asy_msr;
4281                         DEBUGCONT2(ASY_DEBUG_MODEM,
4282                             "asy%dmctl: TIOCMGET, read msr_r = %x\n",
4283                             instance, msr_r);
4284                 } else {
4285                         msr_r = ddi_get8(asy->asy_iohandle,
4286                             asy->asy_ioaddr + MSR);
4287                         DEBUGCONT2(ASY_DEBUG_MODEM,
4288                             "asy%dmctl: TIOCMGET, read MSR = %x\n",
4289                             instance, msr_r);
4290                 }
4291                 DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dtodm: modem_lines = %x\n",
4292                     instance, asytodm(mcr_r, msr_r));
4293                 return (asytodm(mcr_r, msr_r));
4294         }
4295 
4296         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr_r);
4297 
4298         return (mcr_r);
4299 }
4300 
4301 static int
4302 asytodm(int mcr_r, int msr_r)
4303 {
4304         int b = 0;
4305 
4306         /* MCR registers */
4307         if (mcr_r & RTS)
4308                 b |= TIOCM_RTS;
4309 
4310         if (mcr_r & DTR)
4311                 b |= TIOCM_DTR;
4312 
4313         /* MSR registers */
4314         if (msr_r & DCD)
4315                 b |= TIOCM_CAR;
4316 
4317         if (msr_r & CTS)
4318                 b |= TIOCM_CTS;
4319 
4320         if (msr_r & DSR)
4321                 b |= TIOCM_DSR;
4322 
4323         if (msr_r & RI)
4324                 b |= TIOCM_RNG;
4325         return (b);
4326 }
4327 
4328 static int
4329 dmtoasy(int bits)
4330 {
4331         int b = 0;
4332 
4333         DEBUGCONT1(ASY_DEBUG_MODEM, "dmtoasy: bits = %x\n", bits);
4334 #ifdef  CAN_NOT_SET     /* only DTR and RTS can be set */
4335         if (bits & TIOCM_CAR)
4336                 b |= DCD;
4337         if (bits & TIOCM_CTS)
4338                 b |= CTS;
4339         if (bits & TIOCM_DSR)
4340                 b |= DSR;
4341         if (bits & TIOCM_RNG)
4342                 b |= RI;
4343 #endif
4344 
4345         if (bits & TIOCM_RTS) {
4346                 DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & RTS\n");
4347                 b |= RTS;
4348         }
4349         if (bits & TIOCM_DTR) {
4350                 DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & DTR\n");
4351                 b |= DTR;
4352         }
4353 
4354         return (b);
4355 }
4356 
4357 static void
4358 asyerror(int level, const char *fmt, ...)
4359 {
4360         va_list adx;
4361         static  time_t  last;
4362         static  const char *lastfmt;
4363         time_t  now;
4364 
4365         /*
4366          * Don't print the same error message too often.
4367          * Print the message only if we have not printed the
4368          * message within the last second.
4369          * Note: that fmt cannot be a pointer to a string
4370          * stored on the stack. The fmt pointer
4371          * must be in the data segment otherwise lastfmt would point
4372          * to non-sense.
4373          */
4374         now = gethrestime_sec();
4375         if (last == now && lastfmt == fmt)
4376                 return;
4377 
4378         last = now;
4379         lastfmt = fmt;
4380 
4381         va_start(adx, fmt);
4382         vcmn_err(level, fmt, adx);
4383         va_end(adx);
4384 }
4385 
4386 /*
4387  * asy_parse_mode(dev_info_t *devi, struct asycom *asy)
4388  * The value of this property is in the form of "9600,8,n,1,-"
4389  * 1) speed: 9600, 4800, ...
4390  * 2) data bits
4391  * 3) parity: n(none), e(even), o(odd)
4392  * 4) stop bits
4393  * 5) handshake: -(none), h(hardware: rts/cts), s(software: xon/off)
4394  *
4395  * This parsing came from a SPARCstation eeprom.
4396  */
4397 static void
4398 asy_parse_mode(dev_info_t *devi, struct asycom *asy)
4399 {
4400         char            name[40];
4401         char            val[40];
4402         int             len;
4403         int             ret;
4404         char            *p;
4405         char            *p1;
4406 
4407         ASSERT(asy->asy_com_port != 0);
4408 
4409         /*
4410          * Parse the ttyx-mode property
4411          */
4412         (void) sprintf(name, "tty%c-mode", asy->asy_com_port + 'a' - 1);
4413         len = sizeof (val);
4414         ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
4415         if (ret != DDI_PROP_SUCCESS) {
4416                 (void) sprintf(name, "com%c-mode", asy->asy_com_port + '0');
4417                 len = sizeof (val);
4418                 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
4419         }
4420 
4421         /* no property to parse */
4422         asy->asy_cflag = 0;
4423         if (ret != DDI_PROP_SUCCESS)
4424                 return;
4425 
4426         p = val;
4427         /* ---- baud rate ---- */
4428         asy->asy_cflag = CREAD|B9600;                /* initial default */
4429         if (p && (p1 = strchr(p, ',')) != 0) {
4430                 *p1++ = '\0';
4431         } else {
4432                 asy->asy_cflag |= BITS8;     /* add default bits */
4433                 return;
4434         }
4435 
4436         if (strcmp(p, "110") == 0)
4437                 asy->asy_bidx = B110;
4438         else if (strcmp(p, "150") == 0)
4439                 asy->asy_bidx = B150;
4440         else if (strcmp(p, "300") == 0)
4441                 asy->asy_bidx = B300;
4442         else if (strcmp(p, "600") == 0)
4443                 asy->asy_bidx = B600;
4444         else if (strcmp(p, "1200") == 0)
4445                 asy->asy_bidx = B1200;
4446         else if (strcmp(p, "2400") == 0)
4447                 asy->asy_bidx = B2400;
4448         else if (strcmp(p, "4800") == 0)
4449                 asy->asy_bidx = B4800;
4450         else if (strcmp(p, "9600") == 0)
4451                 asy->asy_bidx = B9600;
4452         else if (strcmp(p, "19200") == 0)
4453                 asy->asy_bidx = B19200;
4454         else if (strcmp(p, "38400") == 0)
4455                 asy->asy_bidx = B38400;
4456         else if (strcmp(p, "57600") == 0)
4457                 asy->asy_bidx = B57600;
4458         else if (strcmp(p, "115200") == 0)
4459                 asy->asy_bidx = B115200;
4460         else
4461                 asy->asy_bidx = B9600;
4462 
4463         asy->asy_cflag &= ~CBAUD;
4464         if (asy->asy_bidx > CBAUD) {      /* > 38400 uses the CBAUDEXT bit */
4465                 asy->asy_cflag |= CBAUDEXT;
4466                 asy->asy_cflag |= asy->asy_bidx - CBAUD - 1;
4467         } else {
4468                 asy->asy_cflag |= asy->asy_bidx;
4469         }
4470 
4471         ASSERT(asy->asy_bidx == BAUDINDEX(asy->asy_cflag));
4472 
4473         /* ---- Next item is data bits ---- */
4474         p = p1;
4475         if (p && (p1 = strchr(p, ',')) != 0)  {
4476                 *p1++ = '\0';
4477         } else {
4478                 asy->asy_cflag |= BITS8;     /* add default bits */
4479                 return;
4480         }
4481         switch (*p) {
4482                 default:
4483                 case '8':
4484                         asy->asy_cflag |= CS8;
4485                         asy->asy_lcr = BITS8;
4486                         break;
4487                 case '7':
4488                         asy->asy_cflag |= CS7;
4489                         asy->asy_lcr = BITS7;
4490                         break;
4491                 case '6':
4492                         asy->asy_cflag |= CS6;
4493                         asy->asy_lcr = BITS6;
4494                         break;
4495                 case '5':
4496                         /* LINTED: CS5 is currently zero (but might change) */
4497                         asy->asy_cflag |= CS5;
4498                         asy->asy_lcr = BITS5;
4499                         break;
4500         }
4501 
4502         /* ---- Parity info ---- */
4503         p = p1;
4504         if (p && (p1 = strchr(p, ',')) != 0)  {
4505                 *p1++ = '\0';
4506         } else {
4507                 return;
4508         }
4509         switch (*p)  {
4510                 default:
4511                 case 'n':
4512                         break;
4513                 case 'e':
4514                         asy->asy_cflag |= PARENB;
4515                         asy->asy_lcr |= PEN; break;
4516                 case 'o':
4517                         asy->asy_cflag |= PARENB|PARODD;
4518                         asy->asy_lcr |= PEN|EPS;
4519                         break;
4520         }
4521 
4522         /* ---- Find stop bits ---- */
4523         p = p1;
4524         if (p && (p1 = strchr(p, ',')) != 0)  {
4525                 *p1++ = '\0';
4526         } else {
4527                 return;
4528         }
4529         if (*p == '2') {
4530                 asy->asy_cflag |= CSTOPB;
4531                 asy->asy_lcr |= STB;
4532         }
4533 
4534         /* ---- handshake is next ---- */
4535         p = p1;
4536         if (p) {
4537                 if ((p1 = strchr(p, ',')) != 0)
4538                         *p1++ = '\0';
4539 
4540                 if (*p == 'h')
4541                         asy->asy_cflag |= CRTSCTS;
4542                 else if (*p == 's')
4543                         asy->asy_cflag |= CRTSXOFF;
4544         }
4545 }
4546 
4547 /*
4548  * Check for abort character sequence
4549  */
4550 static boolean_t
4551 abort_charseq_recognize(uchar_t ch)
4552 {
4553         static int state = 0;
4554 #define CNTRL(c) ((c)&037)
4555         static char sequence[] = { '\r', '~', CNTRL('b') };
4556 
4557         if (ch == sequence[state]) {
4558                 if (++state >= sizeof (sequence)) {
4559                         state = 0;
4560                         return (B_TRUE);
4561                 }
4562         } else {
4563                 state = (ch == sequence[0]) ? 1 : 0;
4564         }
4565         return (B_FALSE);
4566 }
4567 
4568 /*
4569  * Flow control functions
4570  */
4571 /*
4572  * Software input flow control
4573  * This function can execute software input flow control sucessfully
4574  * at most of situations except that the line is in BREAK status
4575  * (timed and untimed break).
4576  * INPUT VALUE of onoff:
4577  *               FLOW_START means to send out a XON char
4578  *                          and clear SW input flow control flag.
4579  *               FLOW_STOP means to send out a XOFF char
4580  *                          and set SW input flow control flag.
4581  *               FLOW_CHECK means to check whether there is pending XON/XOFF
4582  *                          if it is true, send it out.
4583  * INPUT VALUE of type:
4584  *               IN_FLOW_RINGBUFF means flow control is due to RING BUFFER
4585  *               IN_FLOW_STREAMS means flow control is due to STREAMS
4586  *               IN_FLOW_USER means flow control is due to user's commands
4587  * RETURN VALUE: B_FALSE means no flow control char is sent
4588  *               B_TRUE means one flow control char is sent
4589  */
4590 static boolean_t
4591 async_flowcontrol_sw_input(struct asycom *asy, async_flowc_action onoff,
4592     int type)
4593 {
4594         struct asyncline *async = asy->asy_priv;
4595         int instance = UNIT(async->async_dev);
4596         int rval = B_FALSE;
4597 
4598         ASSERT(mutex_owned(&asy->asy_excl_hi));
4599 
4600         if (!(async->async_ttycommon.t_iflag & IXOFF))
4601                 return (rval);
4602 
4603         /*
4604          * If we get this far, then we know IXOFF is set.
4605          */
4606         switch (onoff) {
4607         case FLOW_STOP:
4608                 async->async_inflow_source |= type;
4609 
4610                 /*
4611                  * We'll send an XOFF character for each of up to
4612                  * three different input flow control attempts to stop input.
4613                  * If we already send out one XOFF, but FLOW_STOP comes again,
4614                  * it seems that input flow control becomes more serious,
4615                  * then send XOFF again.
4616                  */
4617                 if (async->async_inflow_source & (IN_FLOW_RINGBUFF |
4618                     IN_FLOW_STREAMS | IN_FLOW_USER))
4619                         async->async_flags |= ASYNC_SW_IN_FLOW |
4620                             ASYNC_SW_IN_NEEDED;
4621                 DEBUGCONT2(ASY_DEBUG_SFLOW, "async%d: input sflow stop, "
4622                     "type = %x\n", instance, async->async_inflow_source);
4623                 break;
4624         case FLOW_START:
4625                 async->async_inflow_source &= ~type;
4626                 if (async->async_inflow_source == 0) {
4627                         async->async_flags = (async->async_flags &
4628                             ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED;
4629                         DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: "
4630                             "input sflow start\n", instance);
4631                 }
4632                 break;
4633         default:
4634                 break;
4635         }
4636 
4637         if (((async->async_flags & (ASYNC_SW_IN_NEEDED | ASYNC_BREAK |
4638             ASYNC_OUT_SUSPEND)) == ASYNC_SW_IN_NEEDED) &&
4639             (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE)) {
4640                 /*
4641                  * If we get this far, then we know we need to send out
4642                  * XON or XOFF char.
4643                  */
4644                 async->async_flags = (async->async_flags &
4645                     ~ASYNC_SW_IN_NEEDED) | ASYNC_BUSY;
4646                 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
4647                     async->async_flags & ASYNC_SW_IN_FLOW ?
4648                     async->async_stopc : async->async_startc);
4649                 rval = B_TRUE;
4650         }
4651         return (rval);
4652 }
4653 
4654 /*
4655  * Software output flow control
4656  * This function can be executed sucessfully at any situation.
4657  * It does not handle HW, and just change the SW output flow control flag.
4658  * INPUT VALUE of onoff:
4659  *                 FLOW_START means to clear SW output flow control flag,
4660  *                      also combine with HW output flow control status to
4661  *                      determine if we need to set ASYNC_OUT_FLW_RESUME.
4662  *                 FLOW_STOP means to set SW output flow control flag,
4663  *                      also clear ASYNC_OUT_FLW_RESUME.
4664  */
4665 static void
4666 async_flowcontrol_sw_output(struct asycom *asy, async_flowc_action onoff)
4667 {
4668         struct asyncline *async = asy->asy_priv;
4669         int instance = UNIT(async->async_dev);
4670 
4671         ASSERT(mutex_owned(&asy->asy_excl_hi));
4672 
4673         if (!(async->async_ttycommon.t_iflag & IXON))
4674                 return;
4675 
4676         switch (onoff) {
4677         case FLOW_STOP:
4678                 async->async_flags |= ASYNC_SW_OUT_FLW;
4679                 async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
4680                 DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow stop\n",
4681                     instance);
4682                 break;
4683         case FLOW_START:
4684                 async->async_flags &= ~ASYNC_SW_OUT_FLW;
4685                 if (!(async->async_flags & ASYNC_HW_OUT_FLW))
4686                         async->async_flags |= ASYNC_OUT_FLW_RESUME;
4687                 DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow start\n",
4688                     instance);
4689                 break;
4690         default:
4691                 break;
4692         }
4693 }
4694 
4695 /*
4696  * Hardware input flow control
4697  * This function can be executed sucessfully at any situation.
4698  * It directly changes RTS depending on input parameter onoff.
4699  * INPUT VALUE of onoff:
4700  *       FLOW_START means to clear HW input flow control flag,
4701  *                  and pull up RTS if it is low.
4702  *       FLOW_STOP means to set HW input flow control flag,
4703  *                  and low RTS if it is high.
4704  * INPUT VALUE of type:
4705  *               IN_FLOW_RINGBUFF means flow control is due to RING BUFFER
4706  *               IN_FLOW_STREAMS means flow control is due to STREAMS
4707  *               IN_FLOW_USER means flow control is due to user's commands
4708  */
4709 static void
4710 async_flowcontrol_hw_input(struct asycom *asy, async_flowc_action onoff,
4711     int type)
4712 {
4713         uchar_t mcr;
4714         uchar_t flag;
4715         struct asyncline *async = asy->asy_priv;
4716         int instance = UNIT(async->async_dev);
4717 
4718         ASSERT(mutex_owned(&asy->asy_excl_hi));
4719 
4720         if (!(async->async_ttycommon.t_cflag & CRTSXOFF))
4721                 return;
4722 
4723         switch (onoff) {
4724         case FLOW_STOP:
4725                 async->async_inflow_source |= type;
4726                 if (async->async_inflow_source & (IN_FLOW_RINGBUFF |
4727                     IN_FLOW_STREAMS | IN_FLOW_USER))
4728                         async->async_flags |= ASYNC_HW_IN_FLOW;
4729                 DEBUGCONT2(ASY_DEBUG_HFLOW, "async%d: input hflow stop, "
4730                     "type = %x\n", instance, async->async_inflow_source);
4731                 break;
4732         case FLOW_START:
4733                 async->async_inflow_source &= ~type;
4734                 if (async->async_inflow_source == 0) {
4735                         async->async_flags &= ~ASYNC_HW_IN_FLOW;
4736                         DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: "
4737                             "input hflow start\n", instance);
4738                 }
4739                 break;
4740         default:
4741                 break;
4742         }
4743         mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
4744         flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS;
4745 
4746         if (((mcr ^ flag) & RTS) != 0) {
4747                 ddi_put8(asy->asy_iohandle,
4748                     asy->asy_ioaddr + MCR, (mcr ^ RTS));
4749         }
4750 }
4751 
4752 /*
4753  * Hardware output flow control
4754  * This function can execute HW output flow control sucessfully
4755  * at any situation.
4756  * It doesn't really change RTS, and just change
4757  * HW output flow control flag depending on CTS status.
4758  * INPUT VALUE of onoff:
4759  *                FLOW_START means to clear HW output flow control flag.
4760  *                      also combine with SW output flow control status to
4761  *                      determine if we need to set ASYNC_OUT_FLW_RESUME.
4762  *                FLOW_STOP means to set HW output flow control flag.
4763  *                      also clear ASYNC_OUT_FLW_RESUME.
4764  */
4765 static void
4766 async_flowcontrol_hw_output(struct asycom *asy, async_flowc_action onoff)
4767 {
4768         struct asyncline *async = asy->asy_priv;
4769         int instance = UNIT(async->async_dev);
4770 
4771         ASSERT(mutex_owned(&asy->asy_excl_hi));
4772 
4773         if (!(async->async_ttycommon.t_cflag & CRTSCTS))
4774                 return;
4775 
4776         switch (onoff) {
4777         case FLOW_STOP:
4778                 async->async_flags |= ASYNC_HW_OUT_FLW;
4779                 async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
4780                 DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow stop\n",
4781                     instance);
4782                 break;
4783         case FLOW_START:
4784                 async->async_flags &= ~ASYNC_HW_OUT_FLW;
4785                 if (!(async->async_flags & ASYNC_SW_OUT_FLW))
4786                         async->async_flags |= ASYNC_OUT_FLW_RESUME;
4787                 DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow start\n",
4788                     instance);
4789                 break;
4790         default:
4791                 break;
4792         }
4793 }
4794 
4795 
4796 /*
4797  * quiesce(9E) entry point.
4798  *
4799  * This function is called when the system is single-threaded at high
4800  * PIL with preemption disabled. Therefore, this function must not be
4801  * blocked.
4802  *
4803  * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
4804  * DDI_FAILURE indicates an error condition and should almost never happen.
4805  */
4806 static int
4807 asyquiesce(dev_info_t *devi)
4808 {
4809         int instance;
4810         struct asycom *asy;
4811 
4812         instance = ddi_get_instance(devi);      /* find out which unit */
4813 
4814         asy = ddi_get_soft_state(asy_soft_state, instance);
4815         if (asy == NULL)
4816                 return (DDI_FAILURE);
4817 
4818         /* disable all interrupts */
4819         ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
4820 
4821         /* reset the FIFO */
4822         asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH);
4823 
4824         return (DDI_SUCCESS);
4825 }