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