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