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