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