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