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