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