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