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