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