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