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