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). Confirm that 2291 * before attempting to transmit more data. 2292 */ 2293 VERIFY((lsr & XHRE) != 0); 2294 async_txint(asy); 2295 /* 2296 * Unlike the other interrupts which fall through to 2297 * attempting to fill the output register/FIFO, TxRDY 2298 * has no need having just done so. 2299 */ 2300 continue; 2301 2302 case RxRDY: 2303 case RSTATUS: 2304 case FFTMOUT: 2305 /* receiver interrupt or receiver errors */ 2306 async_rxint(asy, lsr); 2307 break; 2308 case MSTATUS: 2309 /* modem status interrupt */ 2310 async_msint(asy); 2311 break; 2312 } 2313 /* Refill the output FIFO if it has gone empty */ 2314 if ((lsr & XHRE) && (async->async_flags & ASYNC_BUSY) && 2315 (async->async_ocnt > 0)) 2316 async_txint(asy); 2317 } 2318 mutex_exit(&asy->asy_excl_hi); 2319 return (ret_status); 2320 } 2321 2322 /* 2323 * Transmitter interrupt service routine. 2324 * If there is more data to transmit in the current pseudo-DMA block, 2325 * send the next character if output is not stopped or draining. 2326 * Otherwise, queue up a soft interrupt. 2327 * 2328 * XXX - Needs review for HW FIFOs. 2329 */ 2330 static void 2331 async_txint(struct asycom *asy) 2332 { 2333 struct asyncline *async = asy->asy_priv; 2334 int fifo_len; 2335 2336 ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 2337 2338 /* 2339 * If ASYNC_BREAK or ASYNC_OUT_SUSPEND has been set, return to 2340 * asyintr()'s context to claim the interrupt without performing 2341 * any action. No character will be loaded into FIFO/THR until 2342 * timed or untimed break is removed 2343 */ 2344 if (async->async_flags & (ASYNC_BREAK|ASYNC_OUT_SUSPEND)) 2345 return; 2346 2347 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 2348 if (fifo_len > asy_max_tx_fifo) 2349 fifo_len = asy_max_tx_fifo; 2350 2351 if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 2352 fifo_len--; 2353 2354 if (async->async_ocnt > 0 && fifo_len > 0 && 2355 !(async->async_flags & 2356 (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_STOPPED))) { 2357 while (fifo_len-- > 0 && async->async_ocnt-- > 0) { 2358 ddi_put8(asy->asy_iohandle, 2359 asy->asy_ioaddr + DAT, *async->async_optr++); 2360 } 2361 async->async_flags |= ASYNC_PROGRESS; 2362 } 2363 2364 if (fifo_len <= 0) 2365 return; 2366 2367 ASYSETSOFT(asy); 2368 } 2369 2370 /* 2371 * Interrupt on port: handle PPS event. This function is only called 2372 * for a port on which PPS event handling has been enabled. 2373 */ 2374 static void 2375 asy_ppsevent(struct asycom *asy, int msr) 2376 { 2377 ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 2378 2379 if (asy->asy_flags & ASY_PPS_EDGE) { 2380 /* Have seen leading edge, now look for and record drop */ 2381 if ((msr & DCD) == 0) 2382 asy->asy_flags &= ~ASY_PPS_EDGE; 2383 /* 2384 * Waiting for leading edge, look for rise; stamp event and 2385 * calibrate kernel clock. 2386 */ 2387 } else if (msr & DCD) { 2388 /* 2389 * This code captures a timestamp at the designated 2390 * transition of the PPS signal (DCD asserted). The 2391 * code provides a pointer to the timestamp, as well 2392 * as the hardware counter value at the capture. 2393 * 2394 * Note: the kernel has nano based time values while 2395 * NTP requires micro based, an in-line fast algorithm 2396 * to convert nsec to usec is used here -- see hrt2ts() 2397 * in common/os/timers.c for a full description. 2398 */ 2399 struct timeval *tvp = &asy_ppsev.tv; 2400 timestruc_t ts; 2401 long nsec, usec; 2402 2403 asy->asy_flags |= ASY_PPS_EDGE; 2404 LED_OFF; 2405 gethrestime(&ts); 2406 LED_ON; 2407 nsec = ts.tv_nsec; 2408 usec = nsec + (nsec >> 2); 2409 usec = nsec + (usec >> 1); 2410 usec = nsec + (usec >> 2); 2411 usec = nsec + (usec >> 4); 2412 usec = nsec - (usec >> 3); 2413 usec = nsec + (usec >> 2); 2414 usec = nsec + (usec >> 3); 2415 usec = nsec + (usec >> 4); 2416 usec = nsec + (usec >> 1); 2417 usec = nsec + (usec >> 6); 2418 tvp->tv_usec = usec >> 10; 2419 tvp->tv_sec = ts.tv_sec; 2420 2421 ++asy_ppsev.serial; 2422 2423 /* 2424 * Because the kernel keeps a high-resolution time, 2425 * pass the current highres timestamp in tvp and zero 2426 * in usec. 2427 */ 2428 ddi_hardpps(tvp, 0); 2429 } 2430 } 2431 2432 /* 2433 * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive 2434 * error interrupt. 2435 * Try to put the character into the circular buffer for this line; if it 2436 * overflows, indicate a circular buffer overrun. If this port is always 2437 * to be serviced immediately, or the character is a STOP character, or 2438 * more than 15 characters have arrived, queue up a soft interrupt to 2439 * drain the circular buffer. 2440 * XXX - needs review for hw FIFOs support. 2441 */ 2442 2443 static void 2444 async_rxint(struct asycom *asy, uchar_t lsr) 2445 { 2446 struct asyncline *async = asy->asy_priv; 2447 uchar_t c; 2448 uint_t s, needsoft = 0; 2449 tty_common_t *tp; 2450 int looplim = asy->asy_fifo_buf * 2; 2451 2452 ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 2453 2454 tp = &async->async_ttycommon; 2455 if (!(tp->t_cflag & CREAD)) { 2456 while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 2457 (void) (ddi_get8(asy->asy_iohandle, 2458 asy->asy_ioaddr + DAT) & 0xff); 2459 lsr = ddi_get8(asy->asy_iohandle, 2460 asy->asy_ioaddr + LSR); 2461 if (looplim-- < 0) /* limit loop */ 2462 break; 2463 } 2464 return; /* line is not open for read? */ 2465 } 2466 2467 while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 2468 c = 0; 2469 s = 0; /* reset error status */ 2470 if (lsr & RCA) { 2471 c = ddi_get8(asy->asy_iohandle, 2472 asy->asy_ioaddr + DAT) & 0xff; 2473 2474 /* 2475 * We handle XON/XOFF char if IXON is set, 2476 * but if received char is _POSIX_VDISABLE, 2477 * we left it to the up level module. 2478 */ 2479 if (tp->t_iflag & IXON) { 2480 if ((c == async->async_stopc) && 2481 (c != _POSIX_VDISABLE)) { 2482 async_flowcontrol_sw_output(asy, 2483 FLOW_STOP); 2484 goto check_looplim; 2485 } else if ((c == async->async_startc) && 2486 (c != _POSIX_VDISABLE)) { 2487 async_flowcontrol_sw_output(asy, 2488 FLOW_START); 2489 needsoft = 1; 2490 goto check_looplim; 2491 } 2492 if ((tp->t_iflag & IXANY) && 2493 (async->async_flags & ASYNC_SW_OUT_FLW)) { 2494 async_flowcontrol_sw_output(asy, 2495 FLOW_START); 2496 needsoft = 1; 2497 } 2498 } 2499 } 2500 2501 /* 2502 * Check for character break sequence 2503 */ 2504 if ((abort_enable == KIOCABORTALTERNATE) && 2505 (asy->asy_flags & ASY_CONSOLE)) { 2506 if (abort_charseq_recognize(c)) 2507 abort_sequence_enter((char *)NULL); 2508 } 2509 2510 /* Handle framing errors */ 2511 if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) { 2512 if (lsr & PARERR) { 2513 if (tp->t_iflag & INPCK) /* parity enabled */ 2514 s |= PERROR; 2515 } 2516 2517 if (lsr & (FRMERR|BRKDET)) 2518 s |= FRERROR; 2519 if (lsr & OVRRUN) { 2520 async->async_hw_overrun = 1; 2521 s |= OVERRUN; 2522 } 2523 } 2524 2525 if (s == 0) 2526 if ((tp->t_iflag & PARMRK) && 2527 !(tp->t_iflag & (IGNPAR|ISTRIP)) && 2528 (c == 0377)) 2529 if (RING_POK(async, 2)) { 2530 RING_PUT(async, 0377); 2531 RING_PUT(async, c); 2532 } else 2533 async->async_sw_overrun = 1; 2534 else 2535 if (RING_POK(async, 1)) 2536 RING_PUT(async, c); 2537 else 2538 async->async_sw_overrun = 1; 2539 else 2540 if (s & FRERROR) /* Handle framing errors */ 2541 if (c == 0) 2542 if ((asy->asy_flags & ASY_CONSOLE) && 2543 (abort_enable != 2544 KIOCABORTALTERNATE)) 2545 abort_sequence_enter((char *)0); 2546 else 2547 async->async_break++; 2548 else 2549 if (RING_POK(async, 1)) 2550 RING_MARK(async, c, s); 2551 else 2552 async->async_sw_overrun = 1; 2553 else /* Parity errors are handled by ldterm */ 2554 if (RING_POK(async, 1)) 2555 RING_MARK(async, c, s); 2556 else 2557 async->async_sw_overrun = 1; 2558 check_looplim: 2559 lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); 2560 if (looplim-- < 0) /* limit loop */ 2561 break; 2562 } 2563 if ((RING_CNT(async) > (RINGSIZE * 3)/4) && 2564 !(async->async_inflow_source & IN_FLOW_RINGBUFF)) { 2565 async_flowcontrol_hw_input(asy, FLOW_STOP, IN_FLOW_RINGBUFF); 2566 (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 2567 IN_FLOW_RINGBUFF); 2568 } 2569 2570 if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || 2571 (RING_FRAC(async)) || (async->async_polltid == 0)) { 2572 ASYSETSOFT(asy); /* need a soft interrupt */ 2573 } 2574 } 2575 2576 /* 2577 * Modem status interrupt. 2578 * 2579 * (Note: It is assumed that the MSR hasn't been read by asyintr().) 2580 */ 2581 2582 static void 2583 async_msint(struct asycom *asy) 2584 { 2585 struct asyncline *async = asy->asy_priv; 2586 int msr, t_cflag = async->async_ttycommon.t_cflag; 2587 #ifdef DEBUG 2588 int instance = UNIT(async->async_dev); 2589 #endif 2590 2591 ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 2592 2593 async_msint_retry: 2594 /* this resets the interrupt */ 2595 msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 2596 DEBUGCONT10(ASY_DEBUG_STATE, 2597 "async%d_msint call #%d:\n" 2598 " transition: %3s %3s %3s %3s\n" 2599 "current state: %3s %3s %3s %3s\n", 2600 instance, 2601 ++(asy->asy_msint_cnt), 2602 (msr & DCTS) ? "DCTS" : " ", 2603 (msr & DDSR) ? "DDSR" : " ", 2604 (msr & DRI) ? "DRI " : " ", 2605 (msr & DDCD) ? "DDCD" : " ", 2606 (msr & CTS) ? "CTS " : " ", 2607 (msr & DSR) ? "DSR " : " ", 2608 (msr & RI) ? "RI " : " ", 2609 (msr & DCD) ? "DCD " : " "); 2610 2611 /* If CTS status is changed, do H/W output flow control */ 2612 if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & CTS) != 0)) 2613 async_flowcontrol_hw_output(asy, 2614 msr & CTS ? FLOW_START : FLOW_STOP); 2615 /* 2616 * Reading MSR resets the interrupt, we save the 2617 * value of msr so that other functions could examine MSR by 2618 * looking at asy_msr. 2619 */ 2620 asy->asy_msr = (uchar_t)msr; 2621 2622 /* Handle PPS event */ 2623 if (asy->asy_flags & ASY_PPS) 2624 asy_ppsevent(asy, msr); 2625 2626 async->async_ext++; 2627 ASYSETSOFT(asy); 2628 /* 2629 * We will make sure that the modem status presented to us 2630 * during the previous read has not changed. If the chip samples 2631 * the modem status on the falling edge of the interrupt line, 2632 * and uses this state as the base for detecting change of modem 2633 * status, we would miss a change of modem status event that occured 2634 * after we initiated a read MSR operation. 2635 */ 2636 msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 2637 if (STATES(msr) != STATES(asy->asy_msr)) 2638 goto async_msint_retry; 2639 } 2640 2641 /* 2642 * Handle a second-stage interrupt. 2643 */ 2644 /*ARGSUSED*/ 2645 uint_t 2646 asysoftintr(caddr_t intarg) 2647 { 2648 struct asycom *asy = (struct asycom *)intarg; 2649 struct asyncline *async; 2650 int rv; 2651 uint_t cc; 2652 2653 /* 2654 * Test and clear soft interrupt. 2655 */ 2656 mutex_enter(&asy->asy_soft_lock); 2657 DEBUGCONT0(ASY_DEBUG_PROCS, "asysoftintr: enter\n"); 2658 rv = asy->asysoftpend; 2659 if (rv != 0) 2660 asy->asysoftpend = 0; 2661 mutex_exit(&asy->asy_soft_lock); 2662 2663 if (rv) { 2664 if (asy->asy_priv == NULL) 2665 return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 2666 async = (struct asyncline *)asy->asy_priv; 2667 mutex_enter(&asy->asy_excl_hi); 2668 if (asy->asy_flags & ASY_NEEDSOFT) { 2669 asy->asy_flags &= ~ASY_NEEDSOFT; 2670 mutex_exit(&asy->asy_excl_hi); 2671 async_softint(asy); 2672 mutex_enter(&asy->asy_excl_hi); 2673 } 2674 2675 /* 2676 * There are some instances where the softintr is not 2677 * scheduled and hence not called. It so happens that 2678 * causes the last few characters to be stuck in the 2679 * ringbuffer. Hence, call the handler once again so 2680 * the last few characters are cleared. 2681 */ 2682 cc = RING_CNT(async); 2683 mutex_exit(&asy->asy_excl_hi); 2684 if (cc > 0) 2685 (void) async_softint(asy); 2686 } 2687 return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 2688 } 2689 2690 /* 2691 * Handle a software interrupt. 2692 */ 2693 static void 2694 async_softint(struct asycom *asy) 2695 { 2696 struct asyncline *async = asy->asy_priv; 2697 uint_t cc; 2698 mblk_t *bp; 2699 queue_t *q; 2700 uchar_t val; 2701 uchar_t c; 2702 tty_common_t *tp; 2703 int nb; 2704 int instance = UNIT(async->async_dev); 2705 2706 DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint\n", instance); 2707 mutex_enter(&asy->asy_excl_hi); 2708 if (asy->asy_flags & ASY_DOINGSOFT) { 2709 asy->asy_flags |= ASY_DOINGSOFT_RETRY; 2710 mutex_exit(&asy->asy_excl_hi); 2711 return; 2712 } 2713 asy->asy_flags |= ASY_DOINGSOFT; 2714 begin: 2715 asy->asy_flags &= ~ASY_DOINGSOFT_RETRY; 2716 mutex_exit(&asy->asy_excl_hi); 2717 mutex_enter(&asy->asy_excl); 2718 tp = &async->async_ttycommon; 2719 q = tp->t_readq; 2720 if (async->async_flags & ASYNC_OUT_FLW_RESUME) { 2721 if (async->async_ocnt > 0) { 2722 mutex_enter(&asy->asy_excl_hi); 2723 async_resume(async); 2724 mutex_exit(&asy->asy_excl_hi); 2725 } else { 2726 if (async->async_xmitblk) 2727 freeb(async->async_xmitblk); 2728 async->async_xmitblk = NULL; 2729 async_start(async); 2730 } 2731 async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 2732 } 2733 mutex_enter(&asy->asy_excl_hi); 2734 if (async->async_ext) { 2735 async->async_ext = 0; 2736 /* check for carrier up */ 2737 DEBUGCONT3(ASY_DEBUG_MODM2, 2738 "async%d_softint: asy_msr & DCD = %x, " 2739 "tp->t_flags & TS_SOFTCAR = %x\n", 2740 instance, asy->asy_msr & DCD, tp->t_flags & TS_SOFTCAR); 2741 2742 if (asy->asy_msr & DCD) { 2743 /* carrier present */ 2744 if ((async->async_flags & ASYNC_CARR_ON) == 0) { 2745 DEBUGCONT1(ASY_DEBUG_MODM2, 2746 "async%d_softint: set ASYNC_CARR_ON\n", 2747 instance); 2748 async->async_flags |= ASYNC_CARR_ON; 2749 if (async->async_flags & ASYNC_ISOPEN) { 2750 mutex_exit(&asy->asy_excl_hi); 2751 mutex_exit(&asy->asy_excl); 2752 (void) putctl(q, M_UNHANGUP); 2753 mutex_enter(&asy->asy_excl); 2754 mutex_enter(&asy->asy_excl_hi); 2755 } 2756 cv_broadcast(&async->async_flags_cv); 2757 } 2758 } else { 2759 if ((async->async_flags & ASYNC_CARR_ON) && 2760 !(tp->t_cflag & CLOCAL) && 2761 !(tp->t_flags & TS_SOFTCAR)) { 2762 int flushflag; 2763 2764 DEBUGCONT1(ASY_DEBUG_MODEM, 2765 "async%d_softint: carrier dropped, " 2766 "so drop DTR\n", 2767 instance); 2768 /* 2769 * Carrier went away. 2770 * Drop DTR, abort any output in 2771 * progress, indicate that output is 2772 * not stopped, and send a hangup 2773 * notification upstream. 2774 */ 2775 val = ddi_get8(asy->asy_iohandle, 2776 asy->asy_ioaddr + MCR); 2777 ddi_put8(asy->asy_iohandle, 2778 asy->asy_ioaddr + MCR, (val & ~DTR)); 2779 2780 if (async->async_flags & ASYNC_BUSY) { 2781 DEBUGCONT0(ASY_DEBUG_BUSY, 2782 "async_softint: " 2783 "Carrier dropped. " 2784 "Clearing async_ocnt\n"); 2785 async->async_ocnt = 0; 2786 } /* if */ 2787 2788 async->async_flags &= ~ASYNC_STOPPED; 2789 if (async->async_flags & ASYNC_ISOPEN) { 2790 mutex_exit(&asy->asy_excl_hi); 2791 mutex_exit(&asy->asy_excl); 2792 (void) putctl(q, M_HANGUP); 2793 mutex_enter(&asy->asy_excl); 2794 DEBUGCONT1(ASY_DEBUG_MODEM, 2795 "async%d_softint: " 2796 "putctl(q, M_HANGUP)\n", 2797 instance); 2798 /* 2799 * Flush FIFO buffers 2800 * Any data left in there is invalid now 2801 */ 2802 if (asy->asy_use_fifo == FIFO_ON) 2803 asy_reset_fifo(asy, FIFOTXFLSH); 2804 /* 2805 * Flush our write queue if we have one. 2806 * If we're in the midst of close, then 2807 * flush everything. Don't leave stale 2808 * ioctls lying about. 2809 */ 2810 flushflag = (async->async_flags & 2811 ASYNC_CLOSING) ? FLUSHALL : 2812 FLUSHDATA; 2813 flushq(tp->t_writeq, flushflag); 2814 2815 /* active msg */ 2816 bp = async->async_xmitblk; 2817 if (bp != NULL) { 2818 freeb(bp); 2819 async->async_xmitblk = NULL; 2820 } 2821 2822 mutex_enter(&asy->asy_excl_hi); 2823 async->async_flags &= ~ASYNC_BUSY; 2824 /* 2825 * This message warns of Carrier loss 2826 * with data left to transmit can hang 2827 * the system. 2828 */ 2829 DEBUGCONT0(ASY_DEBUG_MODEM, 2830 "async_softint: Flushing to " 2831 "prevent HUPCL hanging\n"); 2832 } /* if (ASYNC_ISOPEN) */ 2833 } /* if (ASYNC_CARR_ON && CLOCAL) */ 2834 async->async_flags &= ~ASYNC_CARR_ON; 2835 cv_broadcast(&async->async_flags_cv); 2836 } /* else */ 2837 } /* if (async->async_ext) */ 2838 2839 mutex_exit(&asy->asy_excl_hi); 2840 2841 /* 2842 * If data has been added to the circular buffer, remove 2843 * it from the buffer, and send it up the stream if there's 2844 * somebody listening. Try to do it 16 bytes at a time. If we 2845 * have more than 16 bytes to move, move 16 byte chunks and 2846 * leave the rest for next time around (maybe it will grow). 2847 */ 2848 mutex_enter(&asy->asy_excl_hi); 2849 if (!(async->async_flags & ASYNC_ISOPEN)) { 2850 RING_INIT(async); 2851 goto rv; 2852 } 2853 if ((cc = RING_CNT(async)) == 0) 2854 goto rv; 2855 mutex_exit(&asy->asy_excl_hi); 2856 2857 if (!canput(q)) { 2858 mutex_enter(&asy->asy_excl_hi); 2859 if (!(async->async_inflow_source & IN_FLOW_STREAMS)) { 2860 async_flowcontrol_hw_input(asy, FLOW_STOP, 2861 IN_FLOW_STREAMS); 2862 (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 2863 IN_FLOW_STREAMS); 2864 } 2865 goto rv; 2866 } 2867 if (async->async_inflow_source & IN_FLOW_STREAMS) { 2868 mutex_enter(&asy->asy_excl_hi); 2869 async_flowcontrol_hw_input(asy, FLOW_START, 2870 IN_FLOW_STREAMS); 2871 (void) async_flowcontrol_sw_input(asy, FLOW_START, 2872 IN_FLOW_STREAMS); 2873 mutex_exit(&asy->asy_excl_hi); 2874 } 2875 2876 DEBUGCONT2(ASY_DEBUG_INPUT, "async%d_softint: %d char(s) in queue.\n", 2877 instance, cc); 2878 2879 if (!(bp = allocb(cc, BPRI_MED))) { 2880 mutex_exit(&asy->asy_excl); 2881 ttycommon_qfull(&async->async_ttycommon, q); 2882 mutex_enter(&asy->asy_excl); 2883 mutex_enter(&asy->asy_excl_hi); 2884 goto rv; 2885 } 2886 mutex_enter(&asy->asy_excl_hi); 2887 do { 2888 if (RING_ERR(async, S_ERRORS)) { 2889 RING_UNMARK(async); 2890 c = RING_GET(async); 2891 break; 2892 } else 2893 *bp->b_wptr++ = RING_GET(async); 2894 } while (--cc); 2895 mutex_exit(&asy->asy_excl_hi); 2896 mutex_exit(&asy->asy_excl); 2897 if (bp->b_wptr > bp->b_rptr) { 2898 if (!canput(q)) { 2899 asyerror(CE_NOTE, "asy%d: local queue full", 2900 instance); 2901 freemsg(bp); 2902 } else 2903 (void) putq(q, bp); 2904 } else 2905 freemsg(bp); 2906 /* 2907 * If we have a parity error, then send 2908 * up an M_BREAK with the "bad" 2909 * character as an argument. Let ldterm 2910 * figure out what to do with the error. 2911 */ 2912 if (cc) 2913 (void) putctl1(q, M_BREAK, c); 2914 mutex_enter(&asy->asy_excl); 2915 mutex_enter(&asy->asy_excl_hi); 2916 if (cc) { 2917 ASYSETSOFT(asy); /* finish cc chars */ 2918 } 2919 rv: 2920 if ((RING_CNT(async) < (RINGSIZE/4)) && 2921 (async->async_inflow_source & IN_FLOW_RINGBUFF)) { 2922 async_flowcontrol_hw_input(asy, FLOW_START, IN_FLOW_RINGBUFF); 2923 (void) async_flowcontrol_sw_input(asy, FLOW_START, 2924 IN_FLOW_RINGBUFF); 2925 } 2926 2927 /* 2928 * If a transmission has finished, indicate that it's finished, 2929 * and start that line up again. 2930 */ 2931 if (async->async_break > 0) { 2932 nb = async->async_break; 2933 async->async_break = 0; 2934 if (async->async_flags & ASYNC_ISOPEN) { 2935 mutex_exit(&asy->asy_excl_hi); 2936 mutex_exit(&asy->asy_excl); 2937 for (; nb > 0; nb--) 2938 (void) putctl(q, M_BREAK); 2939 mutex_enter(&asy->asy_excl); 2940 mutex_enter(&asy->asy_excl_hi); 2941 } 2942 } 2943 if (async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) { 2944 DEBUGCONT2(ASY_DEBUG_BUSY, 2945 "async%d_softint: Clearing ASYNC_BUSY. async_ocnt=%d\n", 2946 instance, 2947 async->async_ocnt); 2948 async->async_flags &= ~ASYNC_BUSY; 2949 mutex_exit(&asy->asy_excl_hi); 2950 if (async->async_xmitblk) 2951 freeb(async->async_xmitblk); 2952 async->async_xmitblk = NULL; 2953 async_start(async); 2954 /* 2955 * If the flag isn't set after doing the async_start above, we 2956 * may have finished all the queued output. Signal any thread 2957 * stuck in close. 2958 */ 2959 if (!(async->async_flags & ASYNC_BUSY)) 2960 cv_broadcast(&async->async_flags_cv); 2961 mutex_enter(&asy->asy_excl_hi); 2962 } 2963 /* 2964 * A note about these overrun bits: all they do is *tell* someone 2965 * about an error- They do not track multiple errors. In fact, 2966 * you could consider them latched register bits if you like. 2967 * We are only interested in printing the error message once for 2968 * any cluster of overrun errors. 2969 */ 2970 if (async->async_hw_overrun) { 2971 if (async->async_flags & ASYNC_ISOPEN) { 2972 mutex_exit(&asy->asy_excl_hi); 2973 mutex_exit(&asy->asy_excl); 2974 asyerror(CE_NOTE, "asy%d: silo overflow", instance); 2975 mutex_enter(&asy->asy_excl); 2976 mutex_enter(&asy->asy_excl_hi); 2977 } 2978 async->async_hw_overrun = 0; 2979 } 2980 if (async->async_sw_overrun) { 2981 if (async->async_flags & ASYNC_ISOPEN) { 2982 mutex_exit(&asy->asy_excl_hi); 2983 mutex_exit(&asy->asy_excl); 2984 asyerror(CE_NOTE, "asy%d: ring buffer overflow", 2985 instance); 2986 mutex_enter(&asy->asy_excl); 2987 mutex_enter(&asy->asy_excl_hi); 2988 } 2989 async->async_sw_overrun = 0; 2990 } 2991 if (asy->asy_flags & ASY_DOINGSOFT_RETRY) { 2992 mutex_exit(&asy->asy_excl); 2993 goto begin; 2994 } 2995 asy->asy_flags &= ~ASY_DOINGSOFT; 2996 mutex_exit(&asy->asy_excl_hi); 2997 mutex_exit(&asy->asy_excl); 2998 DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint: done\n", instance); 2999 } 3000 3001 /* 3002 * Restart output on a line after a delay or break timer expired. 3003 */ 3004 static void 3005 async_restart(void *arg) 3006 { 3007 struct asyncline *async = (struct asyncline *)arg; 3008 struct asycom *asy = async->async_common; 3009 uchar_t lcr; 3010 3011 /* 3012 * If break timer expired, turn off the break bit. 3013 */ 3014 #ifdef DEBUG 3015 int instance = UNIT(async->async_dev); 3016 3017 DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_restart\n", instance); 3018 #endif 3019 mutex_enter(&asy->asy_excl); 3020 /* 3021 * If ASYNC_OUT_SUSPEND is also set, we don't really 3022 * clean the HW break, TIOCCBRK is responsible for this. 3023 */ 3024 if ((async->async_flags & ASYNC_BREAK) && 3025 !(async->async_flags & ASYNC_OUT_SUSPEND)) { 3026 mutex_enter(&asy->asy_excl_hi); 3027 lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 3028 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 3029 (lcr & ~SETBREAK)); 3030 mutex_exit(&asy->asy_excl_hi); 3031 } 3032 async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK); 3033 cv_broadcast(&async->async_flags_cv); 3034 async_start(async); 3035 3036 mutex_exit(&asy->asy_excl); 3037 } 3038 3039 static void 3040 async_start(struct asyncline *async) 3041 { 3042 async_nstart(async, 0); 3043 } 3044 3045 /* 3046 * Start output on a line, unless it's busy, frozen, or otherwise. 3047 */ 3048 /*ARGSUSED*/ 3049 static void 3050 async_nstart(struct asyncline *async, int mode) 3051 { 3052 struct asycom *asy = async->async_common; 3053 int cc; 3054 queue_t *q; 3055 mblk_t *bp; 3056 uchar_t *xmit_addr; 3057 uchar_t val; 3058 int fifo_len = 1; 3059 boolean_t didsome; 3060 mblk_t *nbp; 3061 3062 #ifdef DEBUG 3063 int instance = UNIT(async->async_dev); 3064 3065 DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_nstart\n", instance); 3066 #endif 3067 if (asy->asy_use_fifo == FIFO_ON) { 3068 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 3069 if (fifo_len > asy_max_tx_fifo) 3070 fifo_len = asy_max_tx_fifo; 3071 } 3072 3073 ASSERT(mutex_owned(&asy->asy_excl)); 3074 3075 /* 3076 * If the chip is busy (i.e., we're waiting for a break timeout 3077 * to expire, or for the current transmission to finish, or for 3078 * output to finish draining from chip), don't grab anything new. 3079 */ 3080 if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) { 3081 DEBUGCONT2((mode? ASY_DEBUG_OUT : 0), 3082 "async%d_nstart: start %s.\n", 3083 instance, 3084 async->async_flags & ASYNC_BREAK ? "break" : "busy"); 3085 return; 3086 } 3087 3088 /* 3089 * Check only pended sw input flow control. 3090 */ 3091 mutex_enter(&asy->asy_excl_hi); 3092 if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 3093 fifo_len--; 3094 mutex_exit(&asy->asy_excl_hi); 3095 3096 /* 3097 * If we're waiting for a delay timeout to expire, don't grab 3098 * anything new. 3099 */ 3100 if (async->async_flags & ASYNC_DELAY) { 3101 DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), 3102 "async%d_nstart: start ASYNC_DELAY.\n", instance); 3103 return; 3104 } 3105 3106 if ((q = async->async_ttycommon.t_writeq) == NULL) { 3107 DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), 3108 "async%d_nstart: start writeq is null.\n", instance); 3109 return; /* not attached to a stream */ 3110 } 3111 3112 for (;;) { 3113 if ((bp = getq(q)) == NULL) 3114 return; /* no data to transmit */ 3115 3116 /* 3117 * We have a message block to work on. 3118 * Check whether it's a break, a delay, or an ioctl (the latter 3119 * occurs if the ioctl in question was waiting for the output 3120 * to drain). If it's one of those, process it immediately. 3121 */ 3122 switch (bp->b_datap->db_type) { 3123 3124 case M_BREAK: 3125 /* 3126 * Set the break bit, and arrange for "async_restart" 3127 * to be called in 1/4 second; it will turn the 3128 * break bit off, and call "async_start" to grab 3129 * the next message. 3130 */ 3131 mutex_enter(&asy->asy_excl_hi); 3132 val = ddi_get8(asy->asy_iohandle, 3133 asy->asy_ioaddr + LCR); 3134 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 3135 (val | SETBREAK)); 3136 mutex_exit(&asy->asy_excl_hi); 3137 async->async_flags |= ASYNC_BREAK; 3138 (void) timeout(async_restart, (caddr_t)async, 3139 drv_usectohz(1000000)/4); 3140 freemsg(bp); 3141 return; /* wait for this to finish */ 3142 3143 case M_DELAY: 3144 /* 3145 * Arrange for "async_restart" to be called when the 3146 * delay expires; it will turn ASYNC_DELAY off, 3147 * and call "async_start" to grab the next message. 3148 */ 3149 (void) timeout(async_restart, (caddr_t)async, 3150 (int)(*(unsigned char *)bp->b_rptr + 6)); 3151 async->async_flags |= ASYNC_DELAY; 3152 freemsg(bp); 3153 return; /* wait for this to finish */ 3154 3155 case M_IOCTL: 3156 /* 3157 * This ioctl was waiting for the output ahead of 3158 * it to drain; obviously, it has. Do it, and 3159 * then grab the next message after it. 3160 */ 3161 mutex_exit(&asy->asy_excl); 3162 async_ioctl(async, q, bp); 3163 mutex_enter(&asy->asy_excl); 3164 continue; 3165 } 3166 3167 while (bp != NULL && ((cc = MBLKL(bp)) == 0)) { 3168 nbp = bp->b_cont; 3169 freeb(bp); 3170 bp = nbp; 3171 } 3172 if (bp != NULL) 3173 break; 3174 } 3175 3176 /* 3177 * We have data to transmit. If output is stopped, put 3178 * it back and try again later. 3179 */ 3180 if (async->async_flags & (ASYNC_HW_OUT_FLW | ASYNC_SW_OUT_FLW | 3181 ASYNC_STOPPED | ASYNC_OUT_SUSPEND)) { 3182 (void) putbq(q, bp); 3183 return; 3184 } 3185 3186 async->async_xmitblk = bp; 3187 xmit_addr = bp->b_rptr; 3188 bp = bp->b_cont; 3189 if (bp != NULL) 3190 (void) putbq(q, bp); /* not done with this message yet */ 3191 3192 /* 3193 * In 5-bit mode, the high order bits are used 3194 * to indicate character sizes less than five, 3195 * so we need to explicitly mask before transmitting 3196 */ 3197 if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { 3198 unsigned char *p = xmit_addr; 3199 int cnt = cc; 3200 3201 while (cnt--) 3202 *p++ &= (unsigned char) 0x1f; 3203 } 3204 3205 /* 3206 * Set up this block for pseudo-DMA. 3207 */ 3208 mutex_enter(&asy->asy_excl_hi); 3209 /* 3210 * If the transmitter is ready, shove the first 3211 * character out. 3212 */ 3213 didsome = B_FALSE; 3214 while (--fifo_len >= 0 && cc > 0) { 3215 if (!(ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & 3216 XHRE)) 3217 break; 3218 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 3219 *xmit_addr++); 3220 cc--; 3221 didsome = B_TRUE; 3222 } 3223 async->async_optr = xmit_addr; 3224 async->async_ocnt = cc; 3225 if (didsome) 3226 async->async_flags |= ASYNC_PROGRESS; 3227 DEBUGCONT2(ASY_DEBUG_BUSY, 3228 "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n", 3229 instance, async->async_ocnt); 3230 async->async_flags |= ASYNC_BUSY; 3231 mutex_exit(&asy->asy_excl_hi); 3232 } 3233 3234 /* 3235 * Resume output by poking the transmitter. 3236 */ 3237 static void 3238 async_resume(struct asyncline *async) 3239 { 3240 struct asycom *asy = async->async_common; 3241 #ifdef DEBUG 3242 int instance; 3243 #endif 3244 3245 ASSERT(mutex_owned(&asy->asy_excl_hi)); 3246 #ifdef DEBUG 3247 instance = UNIT(async->async_dev); 3248 DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_resume\n", instance); 3249 #endif 3250 3251 if (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE) { 3252 if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 3253 return; 3254 if (async->async_ocnt > 0 && 3255 !(async->async_flags & 3256 (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_OUT_SUSPEND))) { 3257 ddi_put8(asy->asy_iohandle, 3258 asy->asy_ioaddr + DAT, *async->async_optr++); 3259 async->async_ocnt--; 3260 async->async_flags |= ASYNC_PROGRESS; 3261 } 3262 } 3263 } 3264 3265 /* 3266 * Hold the untimed break to last the minimum time. 3267 */ 3268 static void 3269 async_hold_utbrk(void *arg) 3270 { 3271 struct asyncline *async = arg; 3272 struct asycom *asy = async->async_common; 3273 3274 mutex_enter(&asy->asy_excl); 3275 async->async_flags &= ~ASYNC_HOLD_UTBRK; 3276 cv_broadcast(&async->async_flags_cv); 3277 async->async_utbrktid = 0; 3278 mutex_exit(&asy->asy_excl); 3279 } 3280 3281 /* 3282 * Resume the untimed break. 3283 */ 3284 static void 3285 async_resume_utbrk(struct asyncline *async) 3286 { 3287 uchar_t val; 3288 struct asycom *asy = async->async_common; 3289 ASSERT(mutex_owned(&asy->asy_excl)); 3290 3291 /* 3292 * Because the wait time is very short, 3293 * so we use uninterruptably wait. 3294 */ 3295 while (async->async_flags & ASYNC_HOLD_UTBRK) { 3296 cv_wait(&async->async_flags_cv, &asy->asy_excl); 3297 } 3298 mutex_enter(&asy->asy_excl_hi); 3299 /* 3300 * Timed break and untimed break can exist simultaneously, 3301 * if ASYNC_BREAK is also set at here, we don't 3302 * really clean the HW break. 3303 */ 3304 if (!(async->async_flags & ASYNC_BREAK)) { 3305 val = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 3306 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 3307 (val & ~SETBREAK)); 3308 } 3309 async->async_flags &= ~ASYNC_OUT_SUSPEND; 3310 cv_broadcast(&async->async_flags_cv); 3311 if (async->async_ocnt > 0) { 3312 async_resume(async); 3313 mutex_exit(&asy->asy_excl_hi); 3314 } else { 3315 async->async_flags &= ~ASYNC_BUSY; 3316 mutex_exit(&asy->asy_excl_hi); 3317 if (async->async_xmitblk != NULL) { 3318 freeb(async->async_xmitblk); 3319 async->async_xmitblk = NULL; 3320 } 3321 async_start(async); 3322 } 3323 } 3324 3325 /* 3326 * Process an "ioctl" message sent down to us. 3327 * Note that we don't need to get any locks until we are ready to access 3328 * the hardware. Nothing we access until then is going to be altered 3329 * outside of the STREAMS framework, so we should be safe. 3330 */ 3331 int asydelay = 10000; 3332 static void 3333 async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) 3334 { 3335 struct asycom *asy = async->async_common; 3336 tty_common_t *tp = &async->async_ttycommon; 3337 struct iocblk *iocp; 3338 unsigned datasize; 3339 int error = 0; 3340 uchar_t val; 3341 mblk_t *datamp; 3342 unsigned int index; 3343 3344 #ifdef DEBUG 3345 int instance = UNIT(async->async_dev); 3346 3347 DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl\n", instance); 3348 #endif 3349 3350 if (tp->t_iocpending != NULL) { 3351 /* 3352 * We were holding an "ioctl" response pending the 3353 * availability of an "mblk" to hold data to be passed up; 3354 * another "ioctl" came through, which means that "ioctl" 3355 * must have timed out or been aborted. 3356 */ 3357 freemsg(async->async_ttycommon.t_iocpending); 3358 async->async_ttycommon.t_iocpending = NULL; 3359 } 3360 3361 iocp = (struct iocblk *)mp->b_rptr; 3362 3363 /* 3364 * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl() 3365 * because this function frees up the message block (mp->b_cont) that 3366 * contains the user location where we pass back the results. 3367 * 3368 * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl 3369 * zaps. We know that ttycommon_ioctl doesn't know any CONS* 3370 * ioctls, so keep the others safe too. 3371 */ 3372 DEBUGCONT2(ASY_DEBUG_IOCTL, "async%d_ioctl: %s\n", 3373 instance, 3374 iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" : 3375 iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" : 3376 iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" : 3377 iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" : 3378 "other"); 3379 3380 switch (iocp->ioc_cmd) { 3381 case TIOCMGET: 3382 case TIOCGPPS: 3383 case TIOCSPPS: 3384 case TIOCGPPSEV: 3385 case CONSOPENPOLLEDIO: 3386 case CONSCLOSEPOLLEDIO: 3387 case CONSSETABORTENABLE: 3388 case CONSGETABORTENABLE: 3389 error = -1; /* Do Nothing */ 3390 break; 3391 default: 3392 3393 /* 3394 * The only way in which "ttycommon_ioctl" can fail is if the 3395 * "ioctl" requires a response containing data to be returned 3396 * to the user, and no mblk could be allocated for the data. 3397 * No such "ioctl" alters our state. Thus, we always go ahead 3398 * and do any state-changes the "ioctl" calls for. If we 3399 * couldn't allocate the data, "ttycommon_ioctl" has stashed 3400 * the "ioctl" away safely, so we just call "bufcall" to 3401 * request that we be called back when we stand a better 3402 * chance of allocating the data. 3403 */ 3404 if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { 3405 if (async->async_wbufcid) 3406 unbufcall(async->async_wbufcid); 3407 async->async_wbufcid = bufcall(datasize, BPRI_HI, 3408 (void (*)(void *)) async_reioctl, 3409 (void *)(intptr_t)async->async_common->asy_unit); 3410 return; 3411 } 3412 } 3413 3414 mutex_enter(&asy->asy_excl); 3415 3416 if (error == 0) { 3417 /* 3418 * "ttycommon_ioctl" did most of the work; we just use the 3419 * data it set up. 3420 */ 3421 switch (iocp->ioc_cmd) { 3422 3423 case TCSETS: 3424 mutex_enter(&asy->asy_excl_hi); 3425 if (asy_baudok(asy)) 3426 asy_program(asy, ASY_NOINIT); 3427 else 3428 error = EINVAL; 3429 mutex_exit(&asy->asy_excl_hi); 3430 break; 3431 case TCSETSF: 3432 case TCSETSW: 3433 case TCSETA: 3434 case TCSETAW: 3435 case TCSETAF: 3436 mutex_enter(&asy->asy_excl_hi); 3437 if (!asy_baudok(asy)) 3438 error = EINVAL; 3439 else { 3440 if (asy_isbusy(asy)) 3441 asy_waiteot(asy); 3442 asy_program(asy, ASY_NOINIT); 3443 } 3444 mutex_exit(&asy->asy_excl_hi); 3445 break; 3446 } 3447 } else if (error < 0) { 3448 /* 3449 * "ttycommon_ioctl" didn't do anything; we process it here. 3450 */ 3451 error = 0; 3452 switch (iocp->ioc_cmd) { 3453 3454 case TIOCGPPS: 3455 /* 3456 * Get PPS on/off. 3457 */ 3458 if (mp->b_cont != NULL) 3459 freemsg(mp->b_cont); 3460 3461 mp->b_cont = allocb(sizeof (int), BPRI_HI); 3462 if (mp->b_cont == NULL) { 3463 error = ENOMEM; 3464 break; 3465 } 3466 if (asy->asy_flags & ASY_PPS) 3467 *(int *)mp->b_cont->b_wptr = 1; 3468 else 3469 *(int *)mp->b_cont->b_wptr = 0; 3470 mp->b_cont->b_wptr += sizeof (int); 3471 mp->b_datap->db_type = M_IOCACK; 3472 iocp->ioc_count = sizeof (int); 3473 break; 3474 3475 case TIOCSPPS: 3476 /* 3477 * Set PPS on/off. 3478 */ 3479 error = miocpullup(mp, sizeof (int)); 3480 if (error != 0) 3481 break; 3482 3483 mutex_enter(&asy->asy_excl_hi); 3484 if (*(int *)mp->b_cont->b_rptr) 3485 asy->asy_flags |= ASY_PPS; 3486 else 3487 asy->asy_flags &= ~ASY_PPS; 3488 /* Reset edge sense */ 3489 asy->asy_flags &= ~ASY_PPS_EDGE; 3490 mutex_exit(&asy->asy_excl_hi); 3491 mp->b_datap->db_type = M_IOCACK; 3492 break; 3493 3494 case TIOCGPPSEV: 3495 { 3496 /* 3497 * Get PPS event data. 3498 */ 3499 mblk_t *bp; 3500 void *buf; 3501 #ifdef _SYSCALL32_IMPL 3502 struct ppsclockev32 p32; 3503 #endif 3504 struct ppsclockev ppsclockev; 3505 3506 if (mp->b_cont != NULL) { 3507 freemsg(mp->b_cont); 3508 mp->b_cont = NULL; 3509 } 3510 3511 if ((asy->asy_flags & ASY_PPS) == 0) { 3512 error = ENXIO; 3513 break; 3514 } 3515 3516 /* Protect from incomplete asy_ppsev */ 3517 mutex_enter(&asy->asy_excl_hi); 3518 ppsclockev = asy_ppsev; 3519 mutex_exit(&asy->asy_excl_hi); 3520 3521 #ifdef _SYSCALL32_IMPL 3522 if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { 3523 TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); 3524 p32.serial = ppsclockev.serial; 3525 buf = &p32; 3526 iocp->ioc_count = sizeof (struct ppsclockev32); 3527 } else 3528 #endif 3529 { 3530 buf = &ppsclockev; 3531 iocp->ioc_count = sizeof (struct ppsclockev); 3532 } 3533 3534 if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { 3535 error = ENOMEM; 3536 break; 3537 } 3538 mp->b_cont = bp; 3539 3540 bcopy(buf, bp->b_wptr, iocp->ioc_count); 3541 bp->b_wptr += iocp->ioc_count; 3542 mp->b_datap->db_type = M_IOCACK; 3543 break; 3544 } 3545 3546 case TCSBRK: 3547 error = miocpullup(mp, sizeof (int)); 3548 if (error != 0) 3549 break; 3550 3551 if (*(int *)mp->b_cont->b_rptr == 0) { 3552 3553 /* 3554 * XXX Arrangements to ensure that a break 3555 * isn't in progress should be sufficient. 3556 * This ugly delay() is the only thing 3557 * that seems to work on the NCR Worldmark. 3558 * It should be replaced. Note that an 3559 * asy_waiteot() also does not work. 3560 */ 3561 if (asydelay) 3562 delay(drv_usectohz(asydelay)); 3563 3564 while (async->async_flags & ASYNC_BREAK) { 3565 cv_wait(&async->async_flags_cv, 3566 &asy->asy_excl); 3567 } 3568 mutex_enter(&asy->asy_excl_hi); 3569 /* 3570 * We loop until the TSR is empty and then 3571 * set the break. ASYNC_BREAK has been set 3572 * to ensure that no characters are 3573 * transmitted while the TSR is being 3574 * flushed and SOUT is being used for the 3575 * break signal. 3576 * 3577 * The wait period is equal to 3578 * clock / (baud * 16) * 16 * 2. 3579 */ 3580 index = BAUDINDEX( 3581 async->async_ttycommon.t_cflag); 3582 async->async_flags |= ASYNC_BREAK; 3583 3584 while ((ddi_get8(asy->asy_iohandle, 3585 asy->asy_ioaddr + LSR) & XSRE) == 0) { 3586 mutex_exit(&asy->asy_excl_hi); 3587 mutex_exit(&asy->asy_excl); 3588 drv_usecwait( 3589 32*asyspdtab[index] & 0xfff); 3590 mutex_enter(&asy->asy_excl); 3591 mutex_enter(&asy->asy_excl_hi); 3592 } 3593 /* 3594 * Arrange for "async_restart" 3595 * to be called in 1/4 second; 3596 * it will turn the break bit off, and call 3597 * "async_start" to grab the next message. 3598 */ 3599 val = ddi_get8(asy->asy_iohandle, 3600 asy->asy_ioaddr + LCR); 3601 ddi_put8(asy->asy_iohandle, 3602 asy->asy_ioaddr + LCR, 3603 (val | SETBREAK)); 3604 mutex_exit(&asy->asy_excl_hi); 3605 (void) timeout(async_restart, (caddr_t)async, 3606 drv_usectohz(1000000)/4); 3607 } else { 3608 DEBUGCONT1(ASY_DEBUG_OUT, 3609 "async%d_ioctl: wait for flush.\n", 3610 instance); 3611 mutex_enter(&asy->asy_excl_hi); 3612 asy_waiteot(asy); 3613 mutex_exit(&asy->asy_excl_hi); 3614 DEBUGCONT1(ASY_DEBUG_OUT, 3615 "async%d_ioctl: ldterm satisfied.\n", 3616 instance); 3617 } 3618 break; 3619 3620 case TIOCSBRK: 3621 if (!(async->async_flags & ASYNC_OUT_SUSPEND)) { 3622 mutex_enter(&asy->asy_excl_hi); 3623 async->async_flags |= ASYNC_OUT_SUSPEND; 3624 async->async_flags |= ASYNC_HOLD_UTBRK; 3625 index = BAUDINDEX( 3626 async->async_ttycommon.t_cflag); 3627 while ((ddi_get8(asy->asy_iohandle, 3628 asy->asy_ioaddr + LSR) & XSRE) == 0) { 3629 mutex_exit(&asy->asy_excl_hi); 3630 mutex_exit(&asy->asy_excl); 3631 drv_usecwait( 3632 32*asyspdtab[index] & 0xfff); 3633 mutex_enter(&asy->asy_excl); 3634 mutex_enter(&asy->asy_excl_hi); 3635 } 3636 val = ddi_get8(asy->asy_iohandle, 3637 asy->asy_ioaddr + LCR); 3638 ddi_put8(asy->asy_iohandle, 3639 asy->asy_ioaddr + LCR, (val | SETBREAK)); 3640 mutex_exit(&asy->asy_excl_hi); 3641 /* wait for 100ms to hold BREAK */ 3642 async->async_utbrktid = 3643 timeout((void (*)())async_hold_utbrk, 3644 (caddr_t)async, 3645 drv_usectohz(asy_min_utbrk)); 3646 } 3647 mioc2ack(mp, NULL, 0, 0); 3648 break; 3649 3650 case TIOCCBRK: 3651 if (async->async_flags & ASYNC_OUT_SUSPEND) 3652 async_resume_utbrk(async); 3653 mioc2ack(mp, NULL, 0, 0); 3654 break; 3655 3656 case TIOCMSET: 3657 case TIOCMBIS: 3658 case TIOCMBIC: 3659 if (iocp->ioc_count != TRANSPARENT) { 3660 DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 3661 "non-transparent\n", instance); 3662 3663 error = miocpullup(mp, sizeof (int)); 3664 if (error != 0) 3665 break; 3666 3667 mutex_enter(&asy->asy_excl_hi); 3668 (void) asymctl(asy, 3669 dmtoasy(*(int *)mp->b_cont->b_rptr), 3670 iocp->ioc_cmd); 3671 mutex_exit(&asy->asy_excl_hi); 3672 iocp->ioc_error = 0; 3673 mp->b_datap->db_type = M_IOCACK; 3674 } else { 3675 DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 3676 "transparent\n", instance); 3677 mcopyin(mp, NULL, sizeof (int), NULL); 3678 } 3679 break; 3680 3681 case TIOCMGET: 3682 datamp = allocb(sizeof (int), BPRI_MED); 3683 if (datamp == NULL) { 3684 error = EAGAIN; 3685 break; 3686 } 3687 3688 mutex_enter(&asy->asy_excl_hi); 3689 *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); 3690 mutex_exit(&asy->asy_excl_hi); 3691 3692 if (iocp->ioc_count == TRANSPARENT) { 3693 DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 3694 "transparent\n", instance); 3695 mcopyout(mp, NULL, sizeof (int), NULL, datamp); 3696 } else { 3697 DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 3698 "non-transparent\n", instance); 3699 mioc2ack(mp, datamp, sizeof (int), 0); 3700 } 3701 break; 3702 3703 case CONSOPENPOLLEDIO: 3704 error = miocpullup(mp, sizeof (struct cons_polledio *)); 3705 if (error != 0) 3706 break; 3707 3708 *(struct cons_polledio **)mp->b_cont->b_rptr = 3709 &asy->polledio; 3710 3711 mp->b_datap->db_type = M_IOCACK; 3712 break; 3713 3714 case CONSCLOSEPOLLEDIO: 3715 mp->b_datap->db_type = M_IOCACK; 3716 iocp->ioc_error = 0; 3717 iocp->ioc_rval = 0; 3718 break; 3719 3720 case CONSSETABORTENABLE: 3721 error = secpolicy_console(iocp->ioc_cr); 3722 if (error != 0) 3723 break; 3724 3725 if (iocp->ioc_count != TRANSPARENT) { 3726 error = EINVAL; 3727 break; 3728 } 3729 3730 mutex_enter(&asy->asy_excl_hi); 3731 if (*(intptr_t *)mp->b_cont->b_rptr) 3732 asy->asy_flags |= ASY_CONSOLE; 3733 else 3734 asy->asy_flags &= ~ASY_CONSOLE; 3735 mutex_exit(&asy->asy_excl_hi); 3736 3737 mp->b_datap->db_type = M_IOCACK; 3738 iocp->ioc_error = 0; 3739 iocp->ioc_rval = 0; 3740 break; 3741 3742 case CONSGETABORTENABLE: 3743 /*CONSTANTCONDITION*/ 3744 ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *)); 3745 /* 3746 * Store the return value right in the payload 3747 * we were passed. Crude. 3748 */ 3749 mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL); 3750 *(boolean_t *)mp->b_cont->b_rptr = 3751 (asy->asy_flags & ASY_CONSOLE) != 0; 3752 break; 3753 3754 default: 3755 /* 3756 * If we don't understand it, it's an error. NAK it. 3757 */ 3758 error = EINVAL; 3759 break; 3760 } 3761 } 3762 if (error != 0) { 3763 iocp->ioc_error = error; 3764 mp->b_datap->db_type = M_IOCNAK; 3765 } 3766 mutex_exit(&asy->asy_excl); 3767 qreply(wq, mp); 3768 DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl: done\n", instance); 3769 } 3770 3771 static int 3772 asyrsrv(queue_t *q) 3773 { 3774 mblk_t *bp; 3775 struct asyncline *async; 3776 struct asycom *asy; 3777 3778 async = (struct asyncline *)q->q_ptr; 3779 asy = (struct asycom *)async->async_common; 3780 3781 while (canputnext(q) && (bp = getq(q))) 3782 putnext(q, bp); 3783 mutex_enter(&asy->asy_excl_hi); 3784 ASYSETSOFT(asy); 3785 mutex_exit(&asy->asy_excl_hi); 3786 async->async_polltid = 0; 3787 return (0); 3788 } 3789 3790 /* 3791 * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should 3792 * handle messages as though the driver is operating normally or is 3793 * suspended. In the suspended case, some or all of the processing may have 3794 * to be delayed until the driver is resumed. 3795 */ 3796 #define ASYWPUTDO_NOT_SUSP(async, wput) \ 3797 !((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED)) 3798 3799 /* 3800 * Processing for write queue put procedure. 3801 * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; 3802 * set the flow control character for M_STOPI and M_STARTI messages; 3803 * queue up M_BREAK, M_DELAY, and M_DATA messages for processing 3804 * by the start routine, and then call the start routine; discard 3805 * everything else. Note that this driver does not incorporate any 3806 * mechanism to negotiate to handle the canonicalization process. 3807 * It expects that these functions are handled in upper module(s), 3808 * as we do in ldterm. 3809 */ 3810 static int 3811 asywputdo(queue_t *q, mblk_t *mp, boolean_t wput) 3812 { 3813 struct asyncline *async; 3814 struct asycom *asy; 3815 #ifdef DEBUG 3816 int instance; 3817 #endif 3818 int error; 3819 3820 async = (struct asyncline *)q->q_ptr; 3821 3822 #ifdef DEBUG 3823 instance = UNIT(async->async_dev); 3824 #endif 3825 asy = async->async_common; 3826 3827 switch (mp->b_datap->db_type) { 3828 3829 case M_STOP: 3830 /* 3831 * Since we don't do real DMA, we can just let the 3832 * chip coast to a stop after applying the brakes. 3833 */ 3834 mutex_enter(&asy->asy_excl); 3835 async->async_flags |= ASYNC_STOPPED; 3836 mutex_exit(&asy->asy_excl); 3837 freemsg(mp); 3838 break; 3839 3840 case M_START: 3841 mutex_enter(&asy->asy_excl); 3842 if (async->async_flags & ASYNC_STOPPED) { 3843 async->async_flags &= ~ASYNC_STOPPED; 3844 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 3845 /* 3846 * If an output operation is in progress, 3847 * resume it. Otherwise, prod the start 3848 * routine. 3849 */ 3850 if (async->async_ocnt > 0) { 3851 mutex_enter(&asy->asy_excl_hi); 3852 async_resume(async); 3853 mutex_exit(&asy->asy_excl_hi); 3854 } else { 3855 async_start(async); 3856 } 3857 } 3858 } 3859 mutex_exit(&asy->asy_excl); 3860 freemsg(mp); 3861 break; 3862 3863 case M_IOCTL: 3864 switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 3865 3866 case TCSBRK: 3867 error = miocpullup(mp, sizeof (int)); 3868 if (error != 0) { 3869 miocnak(q, mp, 0, error); 3870 return (0); 3871 } 3872 3873 if (*(int *)mp->b_cont->b_rptr != 0) { 3874 DEBUGCONT1(ASY_DEBUG_OUT, 3875 "async%d_ioctl: flush request.\n", 3876 instance); 3877 (void) putq(q, mp); 3878 3879 mutex_enter(&asy->asy_excl); 3880 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 3881 /* 3882 * If an TIOCSBRK is in progress, 3883 * clean it as TIOCCBRK does, 3884 * then kick off output. 3885 * If TIOCSBRK is not in progress, 3886 * just kick off output. 3887 */ 3888 async_resume_utbrk(async); 3889 } 3890 mutex_exit(&asy->asy_excl); 3891 break; 3892 } 3893 /*FALLTHROUGH*/ 3894 case TCSETSW: 3895 case TCSETSF: 3896 case TCSETAW: 3897 case TCSETAF: 3898 /* 3899 * The changes do not take effect until all 3900 * output queued before them is drained. 3901 * Put this message on the queue, so that 3902 * "async_start" will see it when it's done 3903 * with the output before it. Poke the 3904 * start routine, just in case. 3905 */ 3906 (void) putq(q, mp); 3907 3908 mutex_enter(&asy->asy_excl); 3909 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 3910 /* 3911 * If an TIOCSBRK is in progress, 3912 * clean it as TIOCCBRK does. 3913 * then kick off output. 3914 * If TIOCSBRK is not in progress, 3915 * just kick off output. 3916 */ 3917 async_resume_utbrk(async); 3918 } 3919 mutex_exit(&asy->asy_excl); 3920 break; 3921 3922 default: 3923 /* 3924 * Do it now. 3925 */ 3926 mutex_enter(&asy->asy_excl); 3927 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 3928 mutex_exit(&asy->asy_excl); 3929 async_ioctl(async, q, mp); 3930 break; 3931 } 3932 async_put_suspq(asy, mp); 3933 mutex_exit(&asy->asy_excl); 3934 break; 3935 } 3936 break; 3937 3938 case M_FLUSH: 3939 if (*mp->b_rptr & FLUSHW) { 3940 mutex_enter(&asy->asy_excl); 3941 3942 /* 3943 * Abort any output in progress. 3944 */ 3945 mutex_enter(&asy->asy_excl_hi); 3946 if (async->async_flags & ASYNC_BUSY) { 3947 DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: " 3948 "Clearing async_ocnt, " 3949 "leaving ASYNC_BUSY set\n", 3950 instance); 3951 async->async_ocnt = 0; 3952 async->async_flags &= ~ASYNC_BUSY; 3953 } /* if */ 3954 3955 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 3956 /* Flush FIFO buffers */ 3957 if (asy->asy_use_fifo == FIFO_ON) { 3958 asy_reset_fifo(asy, FIFOTXFLSH); 3959 } 3960 } 3961 mutex_exit(&asy->asy_excl_hi); 3962 3963 /* Flush FIFO buffers */ 3964 if (asy->asy_use_fifo == FIFO_ON) { 3965 asy_reset_fifo(asy, FIFOTXFLSH); 3966 } 3967 3968 /* 3969 * Flush our write queue. 3970 */ 3971 flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ 3972 if (async->async_xmitblk != NULL) { 3973 freeb(async->async_xmitblk); 3974 async->async_xmitblk = NULL; 3975 } 3976 mutex_exit(&asy->asy_excl); 3977 *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ 3978 } 3979 if (*mp->b_rptr & FLUSHR) { 3980 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 3981 /* Flush FIFO buffers */ 3982 if (asy->asy_use_fifo == FIFO_ON) { 3983 asy_reset_fifo(asy, FIFORXFLSH); 3984 } 3985 } 3986 flushq(RD(q), FLUSHDATA); 3987 qreply(q, mp); /* give the read queues a crack at it */ 3988 } else { 3989 freemsg(mp); 3990 } 3991 3992 /* 3993 * We must make sure we process messages that survive the 3994 * write-side flush. 3995 */ 3996 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 3997 mutex_enter(&asy->asy_excl); 3998 async_start(async); 3999 mutex_exit(&asy->asy_excl); 4000 } 4001 break; 4002 4003 case M_BREAK: 4004 case M_DELAY: 4005 case M_DATA: 4006 /* 4007 * Queue the message up to be transmitted, 4008 * and poke the start routine. 4009 */ 4010 (void) putq(q, mp); 4011 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4012 mutex_enter(&asy->asy_excl); 4013 async_start(async); 4014 mutex_exit(&asy->asy_excl); 4015 } 4016 break; 4017 4018 case M_STOPI: 4019 mutex_enter(&asy->asy_excl); 4020 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4021 mutex_enter(&asy->asy_excl_hi); 4022 if (!(async->async_inflow_source & IN_FLOW_USER)) { 4023 async_flowcontrol_hw_input(asy, FLOW_STOP, 4024 IN_FLOW_USER); 4025 (void) async_flowcontrol_sw_input(asy, 4026 FLOW_STOP, IN_FLOW_USER); 4027 } 4028 mutex_exit(&asy->asy_excl_hi); 4029 mutex_exit(&asy->asy_excl); 4030 freemsg(mp); 4031 break; 4032 } 4033 async_put_suspq(asy, mp); 4034 mutex_exit(&asy->asy_excl); 4035 break; 4036 4037 case M_STARTI: 4038 mutex_enter(&asy->asy_excl); 4039 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4040 mutex_enter(&asy->asy_excl_hi); 4041 if (async->async_inflow_source & IN_FLOW_USER) { 4042 async_flowcontrol_hw_input(asy, FLOW_START, 4043 IN_FLOW_USER); 4044 (void) async_flowcontrol_sw_input(asy, 4045 FLOW_START, IN_FLOW_USER); 4046 } 4047 mutex_exit(&asy->asy_excl_hi); 4048 mutex_exit(&asy->asy_excl); 4049 freemsg(mp); 4050 break; 4051 } 4052 async_put_suspq(asy, mp); 4053 mutex_exit(&asy->asy_excl); 4054 break; 4055 4056 case M_CTL: 4057 if (MBLKL(mp) >= sizeof (struct iocblk) && 4058 ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { 4059 mutex_enter(&asy->asy_excl); 4060 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4061 ((struct iocblk *)mp->b_rptr)->ioc_cmd = 4062 MC_HAS_POSIX; 4063 mutex_exit(&asy->asy_excl); 4064 qreply(q, mp); 4065 break; 4066 } else { 4067 async_put_suspq(asy, mp); 4068 } 4069 } else { 4070 /* 4071 * These MC_SERVICE type messages are used by upper 4072 * modules to tell this driver to send input up 4073 * immediately, or that it can wait for normal 4074 * processing that may or may not be done. Sun 4075 * requires these for the mouse module. 4076 * (XXX - for x86?) 4077 */ 4078 mutex_enter(&asy->asy_excl); 4079 switch (*mp->b_rptr) { 4080 4081 case MC_SERVICEIMM: 4082 async->async_flags |= ASYNC_SERVICEIMM; 4083 break; 4084 4085 case MC_SERVICEDEF: 4086 async->async_flags &= ~ASYNC_SERVICEIMM; 4087 break; 4088 } 4089 mutex_exit(&asy->asy_excl); 4090 freemsg(mp); 4091 } 4092 break; 4093 4094 case M_IOCDATA: 4095 mutex_enter(&asy->asy_excl); 4096 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4097 mutex_exit(&asy->asy_excl); 4098 async_iocdata(q, mp); 4099 break; 4100 } 4101 async_put_suspq(asy, mp); 4102 mutex_exit(&asy->asy_excl); 4103 break; 4104 4105 default: 4106 freemsg(mp); 4107 break; 4108 } 4109 return (0); 4110 } 4111 4112 static int 4113 asywput(queue_t *q, mblk_t *mp) 4114 { 4115 return (asywputdo(q, mp, B_TRUE)); 4116 } 4117 4118 /* 4119 * Retry an "ioctl", now that "bufcall" claims we may be able to allocate 4120 * the buffer we need. 4121 */ 4122 static void 4123 async_reioctl(void *unit) 4124 { 4125 int instance = (uintptr_t)unit; 4126 struct asyncline *async; 4127 struct asycom *asy; 4128 queue_t *q; 4129 mblk_t *mp; 4130 4131 asy = ddi_get_soft_state(asy_soft_state, instance); 4132 ASSERT(asy != NULL); 4133 async = asy->asy_priv; 4134 4135 /* 4136 * The bufcall is no longer pending. 4137 */ 4138 mutex_enter(&asy->asy_excl); 4139 async->async_wbufcid = 0; 4140 if ((q = async->async_ttycommon.t_writeq) == NULL) { 4141 mutex_exit(&asy->asy_excl); 4142 return; 4143 } 4144 if ((mp = async->async_ttycommon.t_iocpending) != NULL) { 4145 /* not pending any more */ 4146 async->async_ttycommon.t_iocpending = NULL; 4147 mutex_exit(&asy->asy_excl); 4148 async_ioctl(async, q, mp); 4149 } else 4150 mutex_exit(&asy->asy_excl); 4151 } 4152 4153 static void 4154 async_iocdata(queue_t *q, mblk_t *mp) 4155 { 4156 struct asyncline *async = (struct asyncline *)q->q_ptr; 4157 struct asycom *asy; 4158 struct iocblk *ip; 4159 struct copyresp *csp; 4160 #ifdef DEBUG 4161 int instance = UNIT(async->async_dev); 4162 #endif 4163 4164 asy = async->async_common; 4165 ip = (struct iocblk *)mp->b_rptr; 4166 csp = (struct copyresp *)mp->b_rptr; 4167 4168 if (csp->cp_rval != 0) { 4169 if (csp->cp_private) 4170 freemsg(csp->cp_private); 4171 freemsg(mp); 4172 return; 4173 } 4174 4175 mutex_enter(&asy->asy_excl); 4176 DEBUGCONT2(ASY_DEBUG_MODEM, "async%d_iocdata: case %s\n", 4177 instance, 4178 csp->cp_cmd == TIOCMGET ? "TIOCMGET" : 4179 csp->cp_cmd == TIOCMSET ? "TIOCMSET" : 4180 csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" : 4181 "TIOCMBIC"); 4182 switch (csp->cp_cmd) { 4183 4184 case TIOCMGET: 4185 if (mp->b_cont) { 4186 freemsg(mp->b_cont); 4187 mp->b_cont = NULL; 4188 } 4189 mp->b_datap->db_type = M_IOCACK; 4190 ip->ioc_error = 0; 4191 ip->ioc_count = 0; 4192 ip->ioc_rval = 0; 4193 mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); 4194 break; 4195 4196 case TIOCMSET: 4197 case TIOCMBIS: 4198 case TIOCMBIC: 4199 mutex_enter(&asy->asy_excl_hi); 4200 (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr), 4201 csp->cp_cmd); 4202 mutex_exit(&asy->asy_excl_hi); 4203 mioc2ack(mp, NULL, 0, 0); 4204 break; 4205 4206 default: 4207 mp->b_datap->db_type = M_IOCNAK; 4208 ip->ioc_error = EINVAL; 4209 break; 4210 } 4211 qreply(q, mp); 4212 mutex_exit(&asy->asy_excl); 4213 } 4214 4215 /* 4216 * debugger/console support routines. 4217 */ 4218 4219 /* 4220 * put a character out 4221 * Do not use interrupts. If char is LF, put out CR, LF. 4222 */ 4223 static void 4224 asyputchar(cons_polledio_arg_t arg, uchar_t c) 4225 { 4226 struct asycom *asy = (struct asycom *)arg; 4227 4228 if (c == '\n') 4229 asyputchar(arg, '\r'); 4230 4231 while ((ddi_get8(asy->asy_iohandle, 4232 asy->asy_ioaddr + LSR) & XHRE) == 0) { 4233 /* wait for xmit to finish */ 4234 drv_usecwait(10); 4235 } 4236 4237 /* put the character out */ 4238 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, c); 4239 } 4240 4241 /* 4242 * See if there's a character available. If no character is 4243 * available, return 0. Run in polled mode, no interrupts. 4244 */ 4245 static boolean_t 4246 asyischar(cons_polledio_arg_t arg) 4247 { 4248 struct asycom *asy = (struct asycom *)arg; 4249 4250 return ((ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & RCA) 4251 != 0); 4252 } 4253 4254 /* 4255 * Get a character. Run in polled mode, no interrupts. 4256 */ 4257 static int 4258 asygetchar(cons_polledio_arg_t arg) 4259 { 4260 struct asycom *asy = (struct asycom *)arg; 4261 4262 while (!asyischar(arg)) 4263 drv_usecwait(10); 4264 return (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + DAT)); 4265 } 4266 4267 /* 4268 * Set or get the modem control status. 4269 */ 4270 static int 4271 asymctl(struct asycom *asy, int bits, int how) 4272 { 4273 int mcr_r, msr_r; 4274 int instance = asy->asy_unit; 4275 4276 ASSERT(mutex_owned(&asy->asy_excl_hi)); 4277 ASSERT(mutex_owned(&asy->asy_excl)); 4278 4279 /* Read Modem Control Registers */ 4280 mcr_r = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 4281 4282 switch (how) { 4283 4284 case TIOCMSET: 4285 DEBUGCONT2(ASY_DEBUG_MODEM, 4286 "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits); 4287 mcr_r = bits; /* Set bits */ 4288 break; 4289 4290 case TIOCMBIS: 4291 DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIS, bits = %x\n", 4292 instance, bits); 4293 mcr_r |= bits; /* Mask in bits */ 4294 break; 4295 4296 case TIOCMBIC: 4297 DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIC, bits = %x\n", 4298 instance, bits); 4299 mcr_r &= ~bits; /* Mask out bits */ 4300 break; 4301 4302 case TIOCMGET: 4303 /* Read Modem Status Registers */ 4304 /* 4305 * If modem interrupts are enabled, we return the 4306 * saved value of msr. We read MSR only in async_msint() 4307 */ 4308 if (ddi_get8(asy->asy_iohandle, 4309 asy->asy_ioaddr + ICR) & MIEN) { 4310 msr_r = asy->asy_msr; 4311 DEBUGCONT2(ASY_DEBUG_MODEM, 4312 "asy%dmctl: TIOCMGET, read msr_r = %x\n", 4313 instance, msr_r); 4314 } else { 4315 msr_r = ddi_get8(asy->asy_iohandle, 4316 asy->asy_ioaddr + MSR); 4317 DEBUGCONT2(ASY_DEBUG_MODEM, 4318 "asy%dmctl: TIOCMGET, read MSR = %x\n", 4319 instance, msr_r); 4320 } 4321 DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dtodm: modem_lines = %x\n", 4322 instance, asytodm(mcr_r, msr_r)); 4323 return (asytodm(mcr_r, msr_r)); 4324 } 4325 4326 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr_r); 4327 4328 return (mcr_r); 4329 } 4330 4331 static int 4332 asytodm(int mcr_r, int msr_r) 4333 { 4334 int b = 0; 4335 4336 /* MCR registers */ 4337 if (mcr_r & RTS) 4338 b |= TIOCM_RTS; 4339 4340 if (mcr_r & DTR) 4341 b |= TIOCM_DTR; 4342 4343 /* MSR registers */ 4344 if (msr_r & DCD) 4345 b |= TIOCM_CAR; 4346 4347 if (msr_r & CTS) 4348 b |= TIOCM_CTS; 4349 4350 if (msr_r & DSR) 4351 b |= TIOCM_DSR; 4352 4353 if (msr_r & RI) 4354 b |= TIOCM_RNG; 4355 return (b); 4356 } 4357 4358 static int 4359 dmtoasy(int bits) 4360 { 4361 int b = 0; 4362 4363 DEBUGCONT1(ASY_DEBUG_MODEM, "dmtoasy: bits = %x\n", bits); 4364 #ifdef CAN_NOT_SET /* only DTR and RTS can be set */ 4365 if (bits & TIOCM_CAR) 4366 b |= DCD; 4367 if (bits & TIOCM_CTS) 4368 b |= CTS; 4369 if (bits & TIOCM_DSR) 4370 b |= DSR; 4371 if (bits & TIOCM_RNG) 4372 b |= RI; 4373 #endif 4374 4375 if (bits & TIOCM_RTS) { 4376 DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & RTS\n"); 4377 b |= RTS; 4378 } 4379 if (bits & TIOCM_DTR) { 4380 DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & DTR\n"); 4381 b |= DTR; 4382 } 4383 4384 return (b); 4385 } 4386 4387 static void 4388 asyerror(int level, const char *fmt, ...) 4389 { 4390 va_list adx; 4391 static time_t last; 4392 static const char *lastfmt; 4393 time_t now; 4394 4395 /* 4396 * Don't print the same error message too often. 4397 * Print the message only if we have not printed the 4398 * message within the last second. 4399 * Note: that fmt cannot be a pointer to a string 4400 * stored on the stack. The fmt pointer 4401 * must be in the data segment otherwise lastfmt would point 4402 * to non-sense. 4403 */ 4404 now = gethrestime_sec(); 4405 if (last == now && lastfmt == fmt) 4406 return; 4407 4408 last = now; 4409 lastfmt = fmt; 4410 4411 va_start(adx, fmt); 4412 vcmn_err(level, fmt, adx); 4413 va_end(adx); 4414 } 4415 4416 /* 4417 * asy_parse_mode(dev_info_t *devi, struct asycom *asy) 4418 * The value of this property is in the form of "9600,8,n,1,-" 4419 * 1) speed: 9600, 4800, ... 4420 * 2) data bits 4421 * 3) parity: n(none), e(even), o(odd) 4422 * 4) stop bits 4423 * 5) handshake: -(none), h(hardware: rts/cts), s(software: xon/off) 4424 * 4425 * This parsing came from a SPARCstation eeprom. 4426 */ 4427 static void 4428 asy_parse_mode(dev_info_t *devi, struct asycom *asy) 4429 { 4430 char name[40]; 4431 char val[40]; 4432 int len; 4433 int ret; 4434 char *p; 4435 char *p1; 4436 4437 ASSERT(asy->asy_com_port != 0); 4438 4439 /* 4440 * Parse the ttyx-mode property 4441 */ 4442 (void) sprintf(name, "tty%c-mode", asy->asy_com_port + 'a' - 1); 4443 len = sizeof (val); 4444 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 4445 if (ret != DDI_PROP_SUCCESS) { 4446 (void) sprintf(name, "com%c-mode", asy->asy_com_port + '0'); 4447 len = sizeof (val); 4448 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 4449 } 4450 4451 /* no property to parse */ 4452 asy->asy_cflag = 0; 4453 if (ret != DDI_PROP_SUCCESS) 4454 return; 4455 4456 p = val; 4457 /* ---- baud rate ---- */ 4458 asy->asy_cflag = CREAD|B9600; /* initial default */ 4459 if (p && (p1 = strchr(p, ',')) != 0) { 4460 *p1++ = '\0'; 4461 } else { 4462 asy->asy_cflag |= BITS8; /* add default bits */ 4463 return; 4464 } 4465 4466 if (strcmp(p, "110") == 0) 4467 asy->asy_bidx = B110; 4468 else if (strcmp(p, "150") == 0) 4469 asy->asy_bidx = B150; 4470 else if (strcmp(p, "300") == 0) 4471 asy->asy_bidx = B300; 4472 else if (strcmp(p, "600") == 0) 4473 asy->asy_bidx = B600; 4474 else if (strcmp(p, "1200") == 0) 4475 asy->asy_bidx = B1200; 4476 else if (strcmp(p, "2400") == 0) 4477 asy->asy_bidx = B2400; 4478 else if (strcmp(p, "4800") == 0) 4479 asy->asy_bidx = B4800; 4480 else if (strcmp(p, "9600") == 0) 4481 asy->asy_bidx = B9600; 4482 else if (strcmp(p, "19200") == 0) 4483 asy->asy_bidx = B19200; 4484 else if (strcmp(p, "38400") == 0) 4485 asy->asy_bidx = B38400; 4486 else if (strcmp(p, "57600") == 0) 4487 asy->asy_bidx = B57600; 4488 else if (strcmp(p, "115200") == 0) 4489 asy->asy_bidx = B115200; 4490 else 4491 asy->asy_bidx = B9600; 4492 4493 asy->asy_cflag &= ~CBAUD; 4494 if (asy->asy_bidx > CBAUD) { /* > 38400 uses the CBAUDEXT bit */ 4495 asy->asy_cflag |= CBAUDEXT; 4496 asy->asy_cflag |= asy->asy_bidx - CBAUD - 1; 4497 } else { 4498 asy->asy_cflag |= asy->asy_bidx; 4499 } 4500 4501 ASSERT(asy->asy_bidx == BAUDINDEX(asy->asy_cflag)); 4502 4503 /* ---- Next item is data bits ---- */ 4504 p = p1; 4505 if (p && (p1 = strchr(p, ',')) != 0) { 4506 *p1++ = '\0'; 4507 } else { 4508 asy->asy_cflag |= BITS8; /* add default bits */ 4509 return; 4510 } 4511 switch (*p) { 4512 default: 4513 case '8': 4514 asy->asy_cflag |= CS8; 4515 asy->asy_lcr = BITS8; 4516 break; 4517 case '7': 4518 asy->asy_cflag |= CS7; 4519 asy->asy_lcr = BITS7; 4520 break; 4521 case '6': 4522 asy->asy_cflag |= CS6; 4523 asy->asy_lcr = BITS6; 4524 break; 4525 case '5': 4526 /* LINTED: CS5 is currently zero (but might change) */ 4527 asy->asy_cflag |= CS5; 4528 asy->asy_lcr = BITS5; 4529 break; 4530 } 4531 4532 /* ---- Parity info ---- */ 4533 p = p1; 4534 if (p && (p1 = strchr(p, ',')) != 0) { 4535 *p1++ = '\0'; 4536 } else { 4537 return; 4538 } 4539 switch (*p) { 4540 default: 4541 case 'n': 4542 break; 4543 case 'e': 4544 asy->asy_cflag |= PARENB; 4545 asy->asy_lcr |= PEN; break; 4546 case 'o': 4547 asy->asy_cflag |= PARENB|PARODD; 4548 asy->asy_lcr |= PEN|EPS; 4549 break; 4550 } 4551 4552 /* ---- Find stop bits ---- */ 4553 p = p1; 4554 if (p && (p1 = strchr(p, ',')) != 0) { 4555 *p1++ = '\0'; 4556 } else { 4557 return; 4558 } 4559 if (*p == '2') { 4560 asy->asy_cflag |= CSTOPB; 4561 asy->asy_lcr |= STB; 4562 } 4563 4564 /* ---- handshake is next ---- */ 4565 p = p1; 4566 if (p) { 4567 if ((p1 = strchr(p, ',')) != 0) 4568 *p1++ = '\0'; 4569 4570 if (*p == 'h') 4571 asy->asy_cflag |= CRTSCTS; 4572 else if (*p == 's') 4573 asy->asy_cflag |= CRTSXOFF; 4574 } 4575 } 4576 4577 /* 4578 * Check for abort character sequence 4579 */ 4580 static boolean_t 4581 abort_charseq_recognize(uchar_t ch) 4582 { 4583 static int state = 0; 4584 #define CNTRL(c) ((c)&037) 4585 static char sequence[] = { '\r', '~', CNTRL('b') }; 4586 4587 if (ch == sequence[state]) { 4588 if (++state >= sizeof (sequence)) { 4589 state = 0; 4590 return (B_TRUE); 4591 } 4592 } else { 4593 state = (ch == sequence[0]) ? 1 : 0; 4594 } 4595 return (B_FALSE); 4596 } 4597 4598 /* 4599 * Flow control functions 4600 */ 4601 /* 4602 * Software input flow control 4603 * This function can execute software input flow control sucessfully 4604 * at most of situations except that the line is in BREAK status 4605 * (timed and untimed break). 4606 * INPUT VALUE of onoff: 4607 * FLOW_START means to send out a XON char 4608 * and clear SW input flow control flag. 4609 * FLOW_STOP means to send out a XOFF char 4610 * and set SW input flow control flag. 4611 * FLOW_CHECK means to check whether there is pending XON/XOFF 4612 * if it is true, send it out. 4613 * INPUT VALUE of type: 4614 * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 4615 * IN_FLOW_STREAMS means flow control is due to STREAMS 4616 * IN_FLOW_USER means flow control is due to user's commands 4617 * RETURN VALUE: B_FALSE means no flow control char is sent 4618 * B_TRUE means one flow control char is sent 4619 */ 4620 static boolean_t 4621 async_flowcontrol_sw_input(struct asycom *asy, async_flowc_action onoff, 4622 int type) 4623 { 4624 struct asyncline *async = asy->asy_priv; 4625 int instance = UNIT(async->async_dev); 4626 int rval = B_FALSE; 4627 4628 ASSERT(mutex_owned(&asy->asy_excl_hi)); 4629 4630 if (!(async->async_ttycommon.t_iflag & IXOFF)) 4631 return (rval); 4632 4633 /* 4634 * If we get this far, then we know IXOFF is set. 4635 */ 4636 switch (onoff) { 4637 case FLOW_STOP: 4638 async->async_inflow_source |= type; 4639 4640 /* 4641 * We'll send an XOFF character for each of up to 4642 * three different input flow control attempts to stop input. 4643 * If we already send out one XOFF, but FLOW_STOP comes again, 4644 * it seems that input flow control becomes more serious, 4645 * then send XOFF again. 4646 */ 4647 if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 4648 IN_FLOW_STREAMS | IN_FLOW_USER)) 4649 async->async_flags |= ASYNC_SW_IN_FLOW | 4650 ASYNC_SW_IN_NEEDED; 4651 DEBUGCONT2(ASY_DEBUG_SFLOW, "async%d: input sflow stop, " 4652 "type = %x\n", instance, async->async_inflow_source); 4653 break; 4654 case FLOW_START: 4655 async->async_inflow_source &= ~type; 4656 if (async->async_inflow_source == 0) { 4657 async->async_flags = (async->async_flags & 4658 ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED; 4659 DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: " 4660 "input sflow start\n", instance); 4661 } 4662 break; 4663 default: 4664 break; 4665 } 4666 4667 if (((async->async_flags & (ASYNC_SW_IN_NEEDED | ASYNC_BREAK | 4668 ASYNC_OUT_SUSPEND)) == ASYNC_SW_IN_NEEDED) && 4669 (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE)) { 4670 /* 4671 * If we get this far, then we know we need to send out 4672 * XON or XOFF char. 4673 */ 4674 async->async_flags = (async->async_flags & 4675 ~ASYNC_SW_IN_NEEDED) | ASYNC_BUSY; 4676 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 4677 async->async_flags & ASYNC_SW_IN_FLOW ? 4678 async->async_stopc : async->async_startc); 4679 rval = B_TRUE; 4680 } 4681 return (rval); 4682 } 4683 4684 /* 4685 * Software output flow control 4686 * This function can be executed sucessfully at any situation. 4687 * It does not handle HW, and just change the SW output flow control flag. 4688 * INPUT VALUE of onoff: 4689 * FLOW_START means to clear SW output flow control flag, 4690 * also combine with HW output flow control status to 4691 * determine if we need to set ASYNC_OUT_FLW_RESUME. 4692 * FLOW_STOP means to set SW output flow control flag, 4693 * also clear ASYNC_OUT_FLW_RESUME. 4694 */ 4695 static void 4696 async_flowcontrol_sw_output(struct asycom *asy, async_flowc_action onoff) 4697 { 4698 struct asyncline *async = asy->asy_priv; 4699 int instance = UNIT(async->async_dev); 4700 4701 ASSERT(mutex_owned(&asy->asy_excl_hi)); 4702 4703 if (!(async->async_ttycommon.t_iflag & IXON)) 4704 return; 4705 4706 switch (onoff) { 4707 case FLOW_STOP: 4708 async->async_flags |= ASYNC_SW_OUT_FLW; 4709 async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 4710 DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow stop\n", 4711 instance); 4712 break; 4713 case FLOW_START: 4714 async->async_flags &= ~ASYNC_SW_OUT_FLW; 4715 if (!(async->async_flags & ASYNC_HW_OUT_FLW)) 4716 async->async_flags |= ASYNC_OUT_FLW_RESUME; 4717 DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow start\n", 4718 instance); 4719 break; 4720 default: 4721 break; 4722 } 4723 } 4724 4725 /* 4726 * Hardware input flow control 4727 * This function can be executed sucessfully at any situation. 4728 * It directly changes RTS depending on input parameter onoff. 4729 * INPUT VALUE of onoff: 4730 * FLOW_START means to clear HW input flow control flag, 4731 * and pull up RTS if it is low. 4732 * FLOW_STOP means to set HW input flow control flag, 4733 * and low RTS if it is high. 4734 * INPUT VALUE of type: 4735 * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 4736 * IN_FLOW_STREAMS means flow control is due to STREAMS 4737 * IN_FLOW_USER means flow control is due to user's commands 4738 */ 4739 static void 4740 async_flowcontrol_hw_input(struct asycom *asy, async_flowc_action onoff, 4741 int type) 4742 { 4743 uchar_t mcr; 4744 uchar_t flag; 4745 struct asyncline *async = asy->asy_priv; 4746 int instance = UNIT(async->async_dev); 4747 4748 ASSERT(mutex_owned(&asy->asy_excl_hi)); 4749 4750 if (!(async->async_ttycommon.t_cflag & CRTSXOFF)) 4751 return; 4752 4753 switch (onoff) { 4754 case FLOW_STOP: 4755 async->async_inflow_source |= type; 4756 if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 4757 IN_FLOW_STREAMS | IN_FLOW_USER)) 4758 async->async_flags |= ASYNC_HW_IN_FLOW; 4759 DEBUGCONT2(ASY_DEBUG_HFLOW, "async%d: input hflow stop, " 4760 "type = %x\n", instance, async->async_inflow_source); 4761 break; 4762 case FLOW_START: 4763 async->async_inflow_source &= ~type; 4764 if (async->async_inflow_source == 0) { 4765 async->async_flags &= ~ASYNC_HW_IN_FLOW; 4766 DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: " 4767 "input hflow start\n", instance); 4768 } 4769 break; 4770 default: 4771 break; 4772 } 4773 mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 4774 flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS; 4775 4776 if (((mcr ^ flag) & RTS) != 0) { 4777 ddi_put8(asy->asy_iohandle, 4778 asy->asy_ioaddr + MCR, (mcr ^ RTS)); 4779 } 4780 } 4781 4782 /* 4783 * Hardware output flow control 4784 * This function can execute HW output flow control sucessfully 4785 * at any situation. 4786 * It doesn't really change RTS, and just change 4787 * HW output flow control flag depending on CTS status. 4788 * INPUT VALUE of onoff: 4789 * FLOW_START means to clear HW output flow control flag. 4790 * also combine with SW output flow control status to 4791 * determine if we need to set ASYNC_OUT_FLW_RESUME. 4792 * FLOW_STOP means to set HW output flow control flag. 4793 * also clear ASYNC_OUT_FLW_RESUME. 4794 */ 4795 static void 4796 async_flowcontrol_hw_output(struct asycom *asy, async_flowc_action onoff) 4797 { 4798 struct asyncline *async = asy->asy_priv; 4799 int instance = UNIT(async->async_dev); 4800 4801 ASSERT(mutex_owned(&asy->asy_excl_hi)); 4802 4803 if (!(async->async_ttycommon.t_cflag & CRTSCTS)) 4804 return; 4805 4806 switch (onoff) { 4807 case FLOW_STOP: 4808 async->async_flags |= ASYNC_HW_OUT_FLW; 4809 async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 4810 DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow stop\n", 4811 instance); 4812 break; 4813 case FLOW_START: 4814 async->async_flags &= ~ASYNC_HW_OUT_FLW; 4815 if (!(async->async_flags & ASYNC_SW_OUT_FLW)) 4816 async->async_flags |= ASYNC_OUT_FLW_RESUME; 4817 DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow start\n", 4818 instance); 4819 break; 4820 default: 4821 break; 4822 } 4823 } 4824 4825 4826 /* 4827 * quiesce(9E) entry point. 4828 * 4829 * This function is called when the system is single-threaded at high 4830 * PIL with preemption disabled. Therefore, this function must not be 4831 * blocked. 4832 * 4833 * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 4834 * DDI_FAILURE indicates an error condition and should almost never happen. 4835 */ 4836 static int 4837 asyquiesce(dev_info_t *devi) 4838 { 4839 int instance; 4840 struct asycom *asy; 4841 4842 instance = ddi_get_instance(devi); /* find out which unit */ 4843 4844 asy = ddi_get_soft_state(asy_soft_state, instance); 4845 if (asy == NULL) 4846 return (DDI_FAILURE); 4847 4848 /* disable all interrupts */ 4849 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 4850 4851 /* reset the FIFO */ 4852 asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH); 4853 4854 return (DDI_SUCCESS); 4855 } 4856