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