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