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 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 23 /* All Rights Reserved */ 24 25 /* 26 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 27 * Use is subject to license terms. 28 * Copyright (c) 2016 by Delphix. All rights reserved. 29 * Copyright (c) 2019 Peter Tribble. 30 */ 31 32 33 /* 34 * Serial I/O driver for 82510/8250/16450/16550AF/16C554D chips. 35 * Modified as sparc keyboard/mouse driver. 36 */ 37 #define SU_REGISTER_FILE_NO 0 38 #define SU_REGOFFSET 0 39 #define SU_REGISTER_LEN 8 40 41 #include <sys/param.h> 42 #include <sys/types.h> 43 #include <sys/signal.h> 44 #include <sys/stream.h> 45 #include <sys/termio.h> 46 #include <sys/errno.h> 47 #include <sys/file.h> 48 #include <sys/cmn_err.h> 49 #include <sys/stropts.h> 50 #include <sys/strsubr.h> 51 #include <sys/strsun.h> 52 #include <sys/strtty.h> 53 #include <sys/debug.h> 54 #include <sys/kbio.h> 55 #include <sys/cred.h> 56 #include <sys/modctl.h> 57 #include <sys/stat.h> 58 #include <sys/consdev.h> 59 #include <sys/mkdev.h> 60 #include <sys/kmem.h> 61 #include <sys/cred.h> 62 #ifdef DEBUG 63 #include <sys/promif.h> 64 #endif 65 #include <sys/ddi.h> 66 #include <sys/sunddi.h> 67 #include <sys/sudev.h> 68 #include <sys/note.h> 69 #include <sys/timex.h> 70 #include <sys/policy.h> 71 72 #define async_stopc async_ttycommon.t_stopc 73 #define async_startc async_ttycommon.t_startc 74 75 #define ASY_INIT 1 76 #define ASY_NOINIT 0 77 78 #ifdef DEBUG 79 #define ASY_DEBUG_INIT 0x001 80 #define ASY_DEBUG_INPUT 0x002 81 #define ASY_DEBUG_EOT 0x004 82 #define ASY_DEBUG_CLOSE 0x008 83 #define ASY_DEBUG_HFLOW 0x010 84 #define ASY_DEBUG_PROCS 0x020 85 #define ASY_DEBUG_STATE 0x040 86 #define ASY_DEBUG_INTR 0x080 87 static int asydebug = 0; 88 #endif 89 static int su_log = 0; 90 91 int su_drain_check = 15000000; /* tunable: exit drain check time */ 92 93 static struct ppsclockev asy_ppsev; 94 95 static int max_asy_instance = -1; 96 static void *su_asycom; /* soft state asycom pointer */ 97 static void *su_asyncline; /* soft state asyncline pointer */ 98 static boolean_t abort_charseq_recognize(uchar_t ch); 99 100 static uint_t asysoftintr(caddr_t intarg); 101 static uint_t asyintr(caddr_t argasy); 102 103 /* The async interrupt entry points */ 104 static void async_txint(struct asycom *asy, uchar_t lsr); 105 static void async_rxint(struct asycom *asy, uchar_t lsr); 106 static void async_msint(struct asycom *asy); 107 static int async_softint(struct asycom *asy); 108 109 static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp, 110 boolean_t iswput); 111 static void async_reioctl(void *); 112 static void async_iocdata(queue_t *q, mblk_t *mp); 113 static void async_restart(void *); 114 static void async_start(struct asyncline *async); 115 static void async_nstart(struct asyncline *async, int mode); 116 static void async_resume(struct asyncline *async); 117 static int asy_program(struct asycom *asy, int mode); 118 119 /* Polled mode functions */ 120 static void asyputchar(cons_polledio_arg_t, uchar_t c); 121 static int asygetchar(cons_polledio_arg_t); 122 static boolean_t asyischar(cons_polledio_arg_t); 123 static void asy_polled_enter(cons_polledio_arg_t); 124 static void asy_polled_exit(cons_polledio_arg_t); 125 126 static int asymctl(struct asycom *, int, int); 127 static int asytodm(int, int); 128 static int dmtoasy(int); 129 static void asycheckflowcontrol_hw(struct asycom *asy); 130 static boolean_t asycheckflowcontrol_sw(struct asycom *asy); 131 static void asy_ppsevent(struct asycom *asy, int msr); 132 133 extern kcondvar_t lbolt_cv; 134 extern int ddi_create_internal_pathname(dev_info_t *dip, char *name, 135 int spec_type, minor_t minor_num); 136 137 138 /* 139 * Baud rate table. Indexed by #defines found in sys/termios.h 140 */ 141 ushort_t asyspdtab[] = { 142 0, /* 0 baud rate */ 143 0x900, /* 50 baud rate */ 144 0x600, /* 75 baud rate */ 145 0x417, /* 110 baud rate (%0.026) */ 146 0x359, /* 134 baud rate (%0.058) */ 147 0x300, /* 150 baud rate */ 148 0x240, /* 200 baud rate */ 149 0x180, /* 300 baud rate */ 150 0x0c0, /* 600 baud rate */ 151 0x060, /* 1200 baud rate */ 152 0x040, /* 1800 baud rate */ 153 0x030, /* 2400 baud rate */ 154 0x018, /* 4800 baud rate */ 155 0x00c, /* 9600 baud rate */ 156 0x006, /* 19200 baud rate */ 157 0x003, /* 38400 baud rate */ 158 0x002, /* 57600 baud rate */ 159 0, /* 76800 baud rate - not supported */ 160 0x001, /* 115200 baud rate */ 161 0, /* 153600 baud rate - not supported */ 162 0x8002, /* 230400 baud rate - supported on specific platforms */ 163 0, /* 307200 baud rate - not supported */ 164 0x8001 /* 460800 baud rate - supported on specific platforms */ 165 }; 166 167 /* 168 * Number of speeds supported is the number of entries in 169 * the above table. 170 */ 171 #define N_SU_SPEEDS (sizeof (asyspdtab)/sizeof (ushort_t)) 172 173 /* 174 * Human-readable baud rate table. 175 * Indexed by #defines found in sys/termios.h 176 */ 177 int baudtable[] = { 178 0, /* 0 baud rate */ 179 50, /* 50 baud rate */ 180 75, /* 75 baud rate */ 181 110, /* 110 baud rate */ 182 134, /* 134 baud rate */ 183 150, /* 150 baud rate */ 184 200, /* 200 baud rate */ 185 300, /* 300 baud rate */ 186 600, /* 600 baud rate */ 187 1200, /* 1200 baud rate */ 188 1800, /* 1800 baud rate */ 189 2400, /* 2400 baud rate */ 190 4800, /* 4800 baud rate */ 191 9600, /* 9600 baud rate */ 192 19200, /* 19200 baud rate */ 193 38400, /* 38400 baud rate */ 194 57600, /* 57600 baud rate */ 195 76800, /* 76800 baud rate */ 196 115200, /* 115200 baud rate */ 197 153600, /* 153600 baud rate */ 198 230400, /* 230400 baud rate */ 199 307200, /* 307200 baud rate */ 200 460800 /* 460800 baud rate */ 201 }; 202 203 static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); 204 static int asyclose(queue_t *q, int flag, cred_t *cr); 205 static void asywput(queue_t *q, mblk_t *mp); 206 static void asyrsrv(queue_t *q); 207 208 struct module_info asy_info = { 209 0, 210 "su", 211 0, 212 INFPSZ, 213 32*4096, 214 4096 215 }; 216 217 static struct qinit asy_rint = { 218 putq, 219 (int (*)())asyrsrv, 220 asyopen, 221 asyclose, 222 NULL, 223 &asy_info, 224 NULL 225 }; 226 227 static struct qinit asy_wint = { 228 (int (*)())asywput, 229 NULL, 230 NULL, 231 NULL, 232 NULL, 233 &asy_info, 234 NULL 235 }; 236 237 struct streamtab asy_str_info = { 238 &asy_rint, 239 &asy_wint, 240 NULL, 241 NULL 242 }; 243 244 static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 245 void **result); 246 static int asyprobe(dev_info_t *); 247 static int asyattach(dev_info_t *, ddi_attach_cmd_t); 248 static int asydetach(dev_info_t *, ddi_detach_cmd_t); 249 250 static struct cb_ops cb_asy_ops = { 251 nodev, /* cb_open */ 252 nodev, /* cb_close */ 253 nodev, /* cb_strategy */ 254 nodev, /* cb_print */ 255 nodev, /* cb_dump */ 256 nodev, /* cb_read */ 257 nodev, /* cb_write */ 258 nodev, /* cb_ioctl */ 259 nodev, /* cb_devmap */ 260 nodev, /* cb_mmap */ 261 nodev, /* cb_segmap */ 262 nochpoll, /* cb_chpoll */ 263 ddi_prop_op, /* cb_prop_op */ 264 &asy_str_info, /* cb_stream */ 265 D_MP /* cb_flag */ 266 }; 267 268 struct dev_ops asy_ops = { 269 DEVO_REV, /* devo_rev */ 270 0, /* devo_refcnt */ 271 asyinfo, /* devo_getinfo */ 272 nulldev, /* devo_identify */ 273 asyprobe, /* devo_probe */ 274 asyattach, /* devo_attach */ 275 asydetach, /* devo_detach */ 276 nodev, /* devo_reset */ 277 &cb_asy_ops, /* devo_cb_ops */ 278 NULL, /* devo_bus_ops */ 279 NULL, /* devo_power */ 280 ddi_quiesce_not_supported, /* devo_quiesce */ 281 }; 282 283 /* 284 * Module linkage information for the kernel. 285 */ 286 287 static struct modldrv modldrv = { 288 &mod_driverops, /* Type of module. This one is a driver */ 289 "su driver", 290 &asy_ops, /* driver ops */ 291 }; 292 293 static struct modlinkage modlinkage = { 294 MODREV_1, 295 &modldrv, 296 NULL 297 }; 298 299 int 300 _init(void) 301 { 302 int status; 303 304 status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom), 305 SU_INITIAL_SOFT_ITEMS); 306 if (status != 0) 307 return (status); 308 status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline), 309 SU_INITIAL_SOFT_ITEMS); 310 if (status != 0) { 311 ddi_soft_state_fini(&su_asycom); 312 return (status); 313 } 314 315 if ((status = mod_install(&modlinkage)) != 0) { 316 ddi_soft_state_fini(&su_asycom); 317 ddi_soft_state_fini(&su_asyncline); 318 } 319 320 return (status); 321 } 322 323 int 324 _fini(void) 325 { 326 int i; 327 328 i = mod_remove(&modlinkage); 329 if (i == 0) { 330 ddi_soft_state_fini(&su_asycom); 331 ddi_soft_state_fini(&su_asyncline); 332 } 333 334 return (i); 335 } 336 337 int 338 _info(struct modinfo *modinfop) 339 { 340 return (mod_info(&modlinkage, modinfop)); 341 } 342 343 static int 344 asyprobe(dev_info_t *devi) 345 { 346 int instance; 347 ddi_acc_handle_t handle; 348 uchar_t *addr; 349 ddi_device_acc_attr_t attr; 350 351 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 352 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 353 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 354 if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr, 355 SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) { 356 cmn_err(CE_WARN, "asyprobe regs map setup failed"); 357 return (DDI_PROBE_FAILURE); 358 } 359 #ifdef DEBUG 360 if (asydebug) 361 printf("Probe address mapped %p\n", (void *)addr); 362 #endif 363 364 /* 365 * Probe for the device: 366 * Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low. 367 * If bit 4 or 5 appears on inb() ISR, board is not there. 368 */ 369 if (ddi_get8(handle, addr+ISR) & 0x30) { 370 ddi_regs_map_free(&handle); 371 return (DDI_PROBE_FAILURE); 372 } 373 374 instance = ddi_get_instance(devi); 375 if (max_asy_instance < instance) 376 max_asy_instance = instance; 377 ddi_regs_map_free(&handle); 378 379 return (DDI_PROBE_SUCCESS); /* hw is present */ 380 } 381 382 static int 383 asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) 384 { 385 register int instance; 386 struct asycom *asy; 387 struct asyncline *async; 388 char name[16]; 389 390 instance = ddi_get_instance(devi); /* find out which unit */ 391 392 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); 393 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); 394 395 switch (cmd) { 396 case DDI_DETACH: 397 break; 398 case DDI_SUSPEND: 399 /* grab both mutex locks */ 400 mutex_enter(asy->asy_excl); 401 mutex_enter(asy->asy_excl_hi); 402 if (asy->suspended) { 403 mutex_exit(asy->asy_excl_hi); 404 mutex_exit(asy->asy_excl); 405 return (DDI_SUCCESS); 406 } 407 asy->suspended = B_TRUE; 408 409 /* 410 * The quad UART ST16C554D, version D2 (made by EXAR) 411 * has an anomaly of generating spurious interrupts 412 * when the ICR is loaded with zero. The workaround 413 * would be to read/write any register with DATA1 bit 414 * set to 0 before such write. 415 */ 416 if (asy->asy_hwtype == ASY16C554D) 417 OUTB(SPR, 0); 418 419 /* Disable further interrupts */ 420 OUTB(ICR, 0); 421 mutex_exit(asy->asy_excl_hi); 422 mutex_exit(asy->asy_excl); 423 return (DDI_SUCCESS); 424 425 default: 426 return (DDI_FAILURE); 427 } 428 429 #ifdef DEBUG 430 if (asydebug & ASY_DEBUG_INIT) 431 cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance, 432 asy->asy_hwtype == ASY82510 ? "82510" : 433 asy->asy_hwtype == ASY16550AF ? "16550AF" : 434 asy->asy_hwtype == ASY16C554D ? "16C554D" : 435 "8250"); 436 #endif 437 /* 438 * Before removing interrupts it is always better to disable 439 * interrupts if the chip gives a provision to disable the 440 * serial port interrupts. 441 */ 442 mutex_enter(asy->asy_excl); 443 mutex_enter(asy->asy_excl_hi); 444 /* disable interrupts, see EXAR bug */ 445 if (asy->asy_hwtype == ASY16C554D) 446 OUTB(SPR, 0); 447 OUTB(ICR, 0); 448 mutex_exit(asy->asy_excl_hi); 449 mutex_exit(asy->asy_excl); 450 451 /* remove minor device node(s) for this device */ 452 (void) sprintf(name, "%c", (instance+'a')); /* serial-port */ 453 ddi_remove_minor_node(devi, name); 454 (void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */ 455 ddi_remove_minor_node(devi, name); 456 457 mutex_destroy(asy->asy_excl); 458 mutex_destroy(asy->asy_excl_hi); 459 kmem_free(asy->asy_excl, sizeof (kmutex_t)); 460 kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); 461 cv_destroy(&async->async_flags_cv); 462 kstat_delete(asy->sukstat); 463 ddi_remove_intr(devi, 0, asy->asy_iblock); 464 ddi_regs_map_free(&asy->asy_handle); 465 ddi_remove_softintr(asy->asy_softintr_id); 466 mutex_destroy(asy->asy_soft_lock); 467 kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); 468 ddi_soft_state_free(su_asycom, instance); 469 ddi_soft_state_free(su_asyncline, instance); 470 return (DDI_SUCCESS); 471 } 472 473 static int 474 asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 475 { 476 register int instance; 477 struct asycom *asy; 478 struct asyncline *async; 479 char name[40]; 480 ddi_device_acc_attr_t attr; 481 enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR, 482 SOFTINTR, ASYINIT, KSTAT, MINORNODE }; 483 enum states state = EMPTY; 484 char *hwtype; 485 486 instance = ddi_get_instance(devi); /* find out which unit */ 487 488 /* cannot attach a device that has not been probed first */ 489 if (instance > max_asy_instance) 490 return (DDI_FAILURE); 491 492 if (cmd != DDI_RESUME) { 493 /* Allocate soft state space */ 494 if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) { 495 cmn_err(CE_WARN, "su%d: cannot allocate soft state", 496 instance); 497 goto error; 498 } 499 } 500 state = SOFTSTATE; 501 502 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); 503 504 if (asy == NULL) { 505 cmn_err(CE_WARN, "su%d: cannot get soft state", instance); 506 goto error; 507 } 508 509 switch (cmd) { 510 case DDI_ATTACH: 511 break; 512 case DDI_RESUME: { 513 struct asyncline *async; 514 515 /* grab both mutex locks */ 516 mutex_enter(asy->asy_excl); 517 mutex_enter(asy->asy_excl_hi); 518 if (!asy->suspended) { 519 mutex_exit(asy->asy_excl_hi); 520 mutex_exit(asy->asy_excl); 521 return (DDI_SUCCESS); 522 } 523 /* 524 * re-setup all the registers and enable interrupts if 525 * needed 526 */ 527 async = (struct asyncline *)asy->asy_priv; 528 if ((async) && (async->async_flags & ASYNC_ISOPEN)) 529 (void) asy_program(asy, ASY_INIT); 530 asy->suspended = B_FALSE; 531 mutex_exit(asy->asy_excl_hi); 532 mutex_exit(asy->asy_excl); 533 return (DDI_SUCCESS); 534 } 535 default: 536 goto error; 537 } 538 539 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 540 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 541 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 542 543 if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, 544 (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN, 545 &attr, &asy->asy_handle) != DDI_SUCCESS) { 546 cmn_err(CE_WARN, "asyprobe regs map setup failed"); 547 goto error; 548 } 549 state = REGSMAP; 550 551 #ifdef DEBUG 552 if (asydebug) 553 printf("su attach mapped %p\n", (void *)asy->asy_ioaddr); 554 #endif 555 556 /* 557 * Initialize the port with default settings. 558 */ 559 asy->asy_fifo_buf = 1; 560 asy->asy_use_fifo = FIFO_OFF; 561 562 /* 563 * Check for baudrate generator's "baud-divisor-factor" property setup 564 * by OBP, since different UART chips might have different baudrate 565 * generator divisor. e.g., in case of NSPG's Sputnik platform, the 566 * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip 567 * instead of SuperIO. Since the baud-divisor-factor must be a positive 568 * integer, the divisors will always be at least as large as the values 569 * in asyspdtab[]. Make the default factor 1. 570 */ 571 asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 572 DDI_PROP_DONTPASS, "baud-divisor-factor", 1); 573 574 /* set speed cap */ 575 asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 576 DDI_PROP_DONTPASS, "serial-speed-cap", 115200); 577 578 /* check for ASY82510 chip */ 579 OUTB(ISR, 0x20); 580 if (INB(ISR) & 0x20) { /* 82510 chip is present */ 581 /* 582 * Since most of the general operation of the 82510 chip 583 * can be done from BANK 0 (8250A/16450 compatable mode) 584 * we will default to BANK 0. 585 */ 586 asy->asy_hwtype = ASY82510; 587 OUTB(DAT+7, 0x04); /* clear status */ 588 OUTB(ISR, 0x40); /* set to bank 2 */ 589 OUTB(MCR, 0x08); /* IMD */ 590 OUTB(DAT, 0x21); /* FMD */ 591 OUTB(ISR, 0x00); /* set to bank 0 */ 592 asy->asy_trig_level = 0; 593 } else { /* Set the UART in FIFO mode if it has FIFO buffers */ 594 asy->asy_hwtype = ASY16550AF; 595 OUTB(FIFOR, 0x00); /* clear fifo register */ 596 asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */ 597 598 /* set/Enable FIFO */ 599 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH | 600 (asy->asy_trig_level & 0xff)); 601 602 if ((INB(ISR) & 0xc0) == 0xc0) 603 asy->asy_use_fifo = FIFO_ON; 604 else { 605 asy->asy_hwtype = ASY8250; 606 OUTB(FIFOR, 0x00); /* NO FIFOs */ 607 asy->asy_trig_level = 0; 608 } 609 } 610 611 /* check for ST16C554D chip */ 612 if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, DDI_PROP_NOTPROM | 613 DDI_PROP_DONTPASS, "hwtype", &hwtype)) == DDI_PROP_SUCCESS) { 614 if (strcmp(hwtype, "ST16C554D") == 0) 615 asy->asy_hwtype = ASY16C554D; 616 ddi_prop_free(hwtype); 617 } 618 619 /* disable interrupts, see EXAR bug */ 620 if (asy->asy_hwtype == ASY16C554D) 621 OUTB(SPR, 0); 622 OUTB(ICR, 0); 623 OUTB(LCR, DLAB); /* select baud rate generator */ 624 /* Set the baud rate to 9600 */ 625 OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff); 626 OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff); 627 OUTB(LCR, STOP1|BITS8); 628 OUTB(MCR, (DTR | RTS| OUT2)); 629 630 /* 631 * Set up the other components of the asycom structure for this port. 632 */ 633 asy->asy_excl = (kmutex_t *) 634 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); 635 asy->asy_excl_hi = (kmutex_t *) 636 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); 637 asy->asy_soft_lock = (kmutex_t *) 638 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); 639 asy->asy_unit = instance; 640 asy->asy_dip = devi; 641 642 if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) { 643 cmn_err(CE_NOTE, 644 "Get iblock_cookie failed-Device interrupt%x\n", instance); 645 goto error; 646 } 647 648 if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH, 649 &asy->asy_soft_iblock) != DDI_SUCCESS) { 650 cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n", 651 instance); 652 goto error; 653 } 654 655 mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER, 656 (void *)asy->asy_soft_iblock); 657 mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL); 658 mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER, 659 (void *)asy->asy_iblock); 660 state = MUTEXES; 661 662 /* 663 * Install interrupt handlers for this device. 664 */ 665 if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr, 666 (caddr_t)asy) != DDI_SUCCESS) { 667 cmn_err(CE_CONT, 668 "Cannot set device interrupt for su driver\n"); 669 goto error; 670 } 671 state = ADDINTR; 672 673 if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id), 674 &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy) 675 != DDI_SUCCESS) { 676 cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n"); 677 goto error; 678 } 679 state = SOFTINTR; 680 681 /* initialize the asyncline structure */ 682 if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) { 683 cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance); 684 goto error; 685 } 686 state = ASYINIT; 687 688 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); 689 690 mutex_enter(asy->asy_excl); 691 async->async_common = asy; 692 cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL); 693 mutex_exit(asy->asy_excl); 694 695 if ((asy->sukstat = kstat_create("su", instance, "serialstat", 696 "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) { 697 asy->sukstat->ks_data = &asy->kstats; 698 kstat_named_init(&asy->kstats.ringover, "ring buffer overflow", 699 KSTAT_DATA_UINT64); 700 kstat_named_init(&asy->kstats.siloover, "silo overflow", 701 KSTAT_DATA_UINT64); 702 kstat_install(asy->sukstat); 703 } 704 state = KSTAT; 705 706 if (strcmp(ddi_node_name(devi), "rsc-console") == 0) { 707 /* 708 * If the device is configured as the 'rsc-console' 709 * create the minor device for this node. 710 */ 711 if (ddi_create_minor_node(devi, "ssp", S_IFCHR, 712 asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) 713 == DDI_FAILURE) { 714 cmn_err(CE_WARN, 715 "%s%d: Failed to create node rsc-console", 716 ddi_get_name(devi), ddi_get_instance(devi)); 717 goto error; 718 } 719 720 asy->asy_lom_console = 0; 721 asy->asy_rsc_console = 1; 722 asy->asy_rsc_control = 0; 723 asy->asy_device_type = ASY_SERIAL; 724 asy->asy_flags |= ASY_IGNORE_CD; 725 726 } else if (strcmp(ddi_node_name(devi), "lom-console") == 0) { 727 /* 728 * If the device is configured as the 'lom-console' 729 * create the minor device for this node. 730 * Do not create a dialout device. 731 * Use the same minor numbers as would be used for standard 732 * serial instances. 733 */ 734 if (ddi_create_minor_node(devi, "lom-console", S_IFCHR, 735 instance, DDI_NT_SERIAL_LOMCON, NULL) == DDI_FAILURE) { 736 cmn_err(CE_WARN, 737 "%s%d: Failed to create node lom-console", 738 ddi_get_name(devi), ddi_get_instance(devi)); 739 goto error; 740 } 741 asy->asy_lom_console = 1; 742 asy->asy_rsc_console = 0; 743 asy->asy_rsc_control = 0; 744 asy->asy_device_type = ASY_SERIAL; 745 asy->asy_flags |= ASY_IGNORE_CD; 746 747 } else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) { 748 /* 749 * If the device is configured as the 'rsc-control' 750 * create the minor device for this node. 751 */ 752 if (ddi_create_minor_node(devi, "sspctl", S_IFCHR, 753 asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) 754 == DDI_FAILURE) { 755 cmn_err(CE_WARN, "%s%d: Failed to create rsc-control", 756 ddi_get_name(devi), ddi_get_instance(devi)); 757 goto error; 758 } 759 760 asy->asy_lom_console = 0; 761 asy->asy_rsc_console = 0; 762 asy->asy_rsc_control = 1; 763 asy->asy_device_type = ASY_SERIAL; 764 asy->asy_flags |= ASY_IGNORE_CD; 765 766 } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 767 "keyboard", 0)) { 768 /* 769 * If the device is a keyboard, then create an internal 770 * pathname so that the dacf code will link the node into 771 * the keyboard console stream. See dacf.conf. 772 */ 773 if (ddi_create_internal_pathname(devi, "keyboard", 774 S_IFCHR, instance) == DDI_FAILURE) { 775 goto error; 776 } 777 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 778 asy->asy_device_type = ASY_KEYBOARD; /* Device type */ 779 } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 780 "mouse", 0)) { 781 /* 782 * If the device is a mouse, then create an internal 783 * pathname so that the dacf code will link the node into 784 * the mouse stream. See dacf.conf. 785 */ 786 if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR, 787 instance) == DDI_FAILURE) { 788 goto error; 789 } 790 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 791 asy->asy_device_type = ASY_MOUSE; 792 } else { 793 /* 794 * If not used for keyboard/mouse, create minor devices nodes 795 * for this device 796 */ 797 /* serial-port */ 798 (void) sprintf(name, "%c", (instance+'a')); 799 if (ddi_create_minor_node(devi, name, S_IFCHR, instance, 800 DDI_NT_SERIAL_MB, NULL) == DDI_FAILURE) { 801 goto error; 802 } 803 state = MINORNODE; 804 /* serial-port:dailout */ 805 (void) sprintf(name, "%c,cu", (instance+'a')); 806 if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE, 807 DDI_NT_SERIAL_MB_DO, NULL) == DDI_FAILURE) { 808 goto error; 809 } 810 /* Property for ignoring DCD */ 811 if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 812 "ignore-cd", 0)) { 813 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 814 } else { 815 asy->asy_flags &= ~ASY_IGNORE_CD; 816 /* 817 * if ignore-cd is not available it could be 818 * some old legacy platform, try to see 819 * whether the old legacy property exists 820 */ 821 (void) sprintf(name, 822 "port-%c-ignore-cd", (instance+ 'a')); 823 if (ddi_getprop(DDI_DEV_T_ANY, devi, 824 DDI_PROP_DONTPASS, name, 0)) 825 asy->asy_flags |= ASY_IGNORE_CD; 826 } 827 asy->asy_device_type = ASY_SERIAL; 828 } 829 830 /* 831 * Fill in the polled I/O structure 832 */ 833 asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0; 834 asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy; 835 asy->polledio.cons_polledio_putchar = asyputchar; 836 asy->polledio.cons_polledio_getchar = asygetchar; 837 asy->polledio.cons_polledio_ischar = asyischar; 838 asy->polledio.cons_polledio_enter = asy_polled_enter; 839 asy->polledio.cons_polledio_exit = asy_polled_exit; 840 841 /* Initialize saved ICR and polled_enter */ 842 asy->polled_icr = 0; 843 asy->polled_enter = B_FALSE; 844 845 ddi_report_dev(devi); 846 return (DDI_SUCCESS); 847 848 error: 849 if (state == MINORNODE) { 850 (void) sprintf(name, "%c", (instance+'a')); 851 ddi_remove_minor_node(devi, name); 852 } 853 if (state >= KSTAT) 854 kstat_delete(asy->sukstat); 855 if (state >= ASYINIT) { 856 cv_destroy(&async->async_flags_cv); 857 ddi_soft_state_free(su_asyncline, instance); 858 } 859 if (state >= SOFTINTR) 860 ddi_remove_softintr(asy->asy_softintr_id); 861 if (state >= ADDINTR) 862 ddi_remove_intr(devi, 0, asy->asy_iblock); 863 if (state >= MUTEXES) { 864 mutex_destroy(asy->asy_excl_hi); 865 mutex_destroy(asy->asy_excl); 866 mutex_destroy(asy->asy_soft_lock); 867 kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); 868 kmem_free(asy->asy_excl, sizeof (kmutex_t)); 869 kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); 870 } 871 if (state >= REGSMAP) 872 ddi_regs_map_free(&asy->asy_handle); 873 if (state >= SOFTSTATE) 874 ddi_soft_state_free(su_asycom, instance); 875 /* no action for EMPTY state */ 876 return (DDI_FAILURE); 877 } 878 879 static int 880 asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 881 void **result) 882 { 883 _NOTE(ARGUNUSED(dip)) 884 register dev_t dev = (dev_t)arg; 885 register int instance, error; 886 struct asycom *asy; 887 888 if ((instance = UNIT(dev)) > max_asy_instance) 889 return (DDI_FAILURE); 890 891 switch (infocmd) { 892 case DDI_INFO_DEVT2DEVINFO: 893 asy = (struct asycom *)ddi_get_soft_state(su_asycom, 894 instance); 895 if (asy->asy_dip == NULL) 896 error = DDI_FAILURE; 897 else { 898 *result = (void *) asy->asy_dip; 899 error = DDI_SUCCESS; 900 } 901 break; 902 case DDI_INFO_DEVT2INSTANCE: 903 *result = (void *)(uintptr_t)instance; 904 error = DDI_SUCCESS; 905 break; 906 default: 907 error = DDI_FAILURE; 908 } 909 return (error); 910 } 911 912 static int 913 asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) 914 { 915 _NOTE(ARGUNUSED(sflag)) 916 struct asycom *asy; 917 struct asyncline *async; 918 int mcr; 919 int unit; 920 int len; 921 struct termios *termiosp; 922 923 #ifdef DEBUG 924 if (asydebug & ASY_DEBUG_CLOSE) 925 printf("open\n"); 926 #endif 927 unit = UNIT(*dev); 928 if (unit > max_asy_instance) 929 return (ENXIO); /* unit not configured */ 930 931 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit); 932 if (async == NULL) 933 return (ENXIO); 934 935 asy = async->async_common; 936 if (asy == NULL) 937 return (ENXIO); /* device not found by autoconfig */ 938 939 mutex_enter(asy->asy_excl); 940 asy->asy_priv = (caddr_t)async; 941 942 again: 943 mutex_enter(asy->asy_excl_hi); 944 /* 945 * Block waiting for carrier to come up, unless this is a no-delay open. 946 */ 947 if (!(async->async_flags & ASYNC_ISOPEN)) { 948 /* 949 * If this port is for a RSC console or control 950 * use the following termio info 951 */ 952 if (asy->asy_rsc_console || asy->asy_rsc_control) { 953 async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT | 954 (B115200 & CBAUD); 955 async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT) 956 & CIBAUD); 957 async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; 958 } else if (asy->asy_lom_console) { 959 async->async_ttycommon.t_cflag = B9600 & CBAUD; 960 async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT) 961 & CIBAUD); 962 async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; 963 } else { 964 965 /* 966 * Set the default termios settings (cflag). 967 * Others are set in ldterm. Release the spin 968 * mutex as we can block here, reaquire before 969 * calling asy_program. 970 */ 971 mutex_exit(asy->asy_excl_hi); 972 if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 973 0, "ttymodes", (caddr_t)&termiosp, &len) 974 == DDI_PROP_SUCCESS && 975 len == sizeof (struct termios)) { 976 async->async_ttycommon.t_cflag = 977 termiosp->c_cflag; 978 kmem_free(termiosp, len); 979 } else { 980 cmn_err(CE_WARN, 981 "su: couldn't get ttymodes property!"); 982 } 983 mutex_enter(asy->asy_excl_hi); 984 } 985 async->async_ttycommon.t_iflag = 0; 986 async->async_ttycommon.t_iocpending = NULL; 987 async->async_ttycommon.t_size.ws_row = 0; 988 async->async_ttycommon.t_size.ws_col = 0; 989 async->async_ttycommon.t_size.ws_xpixel = 0; 990 async->async_ttycommon.t_size.ws_ypixel = 0; 991 async->async_dev = *dev; 992 async->async_wbufcid = 0; 993 994 async->async_startc = CSTART; 995 async->async_stopc = CSTOP; 996 (void) asy_program(asy, ASY_INIT); 997 } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) && 998 secpolicy_excl_open(cr) != 0) { 999 mutex_exit(asy->asy_excl_hi); 1000 mutex_exit(asy->asy_excl); 1001 return (EBUSY); 1002 } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) { 1003 mutex_exit(asy->asy_excl_hi); 1004 mutex_exit(asy->asy_excl); 1005 return (EBUSY); 1006 } 1007 1008 if (*dev & OUTLINE) 1009 async->async_flags |= ASYNC_OUT; 1010 1011 /* Raise DTR on every open */ 1012 mcr = INB(MCR); 1013 OUTB(MCR, mcr|DTR); 1014 1015 /* 1016 * Check carrier. 1017 */ 1018 if (asy->asy_flags & ASY_IGNORE_CD) 1019 async->async_ttycommon.t_flags |= TS_SOFTCAR; 1020 if ((async->async_ttycommon.t_flags & TS_SOFTCAR) || 1021 (INB(MSR) & DCD)) 1022 async->async_flags |= ASYNC_CARR_ON; 1023 else 1024 async->async_flags &= ~ASYNC_CARR_ON; 1025 mutex_exit(asy->asy_excl_hi); 1026 1027 /* 1028 * If FNDELAY and FNONBLOCK are clear, block until carrier up. 1029 * Quit on interrupt. 1030 */ 1031 if (!(flag & (FNDELAY|FNONBLOCK)) && 1032 !(async->async_ttycommon.t_cflag & CLOCAL)) { 1033 if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) || 1034 ((async->async_flags & ASYNC_OUT) && 1035 !(*dev & OUTLINE))) { 1036 async->async_flags |= ASYNC_WOPEN; 1037 if (cv_wait_sig(&async->async_flags_cv, 1038 asy->asy_excl) == 0) { 1039 async->async_flags &= ~ASYNC_WOPEN; 1040 mutex_exit(asy->asy_excl); 1041 return (EINTR); 1042 } 1043 async->async_flags &= ~ASYNC_WOPEN; 1044 goto again; 1045 } 1046 } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) { 1047 mutex_exit(asy->asy_excl); 1048 return (EBUSY); 1049 } 1050 1051 if (asy->suspended) { 1052 mutex_exit(asy->asy_excl); 1053 (void) ddi_dev_is_needed(asy->asy_dip, 0, 1); 1054 mutex_enter(asy->asy_excl); 1055 } 1056 1057 async->async_ttycommon.t_readq = rq; 1058 async->async_ttycommon.t_writeq = WR(rq); 1059 rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async; 1060 mutex_exit(asy->asy_excl); 1061 qprocson(rq); 1062 async->async_flags |= ASYNC_ISOPEN; 1063 async->async_polltid = 0; 1064 return (0); 1065 } 1066 1067 static void 1068 async_progress_check(void *arg) 1069 { 1070 struct asyncline *async = arg; 1071 struct asycom *asy = async->async_common; 1072 mblk_t *bp; 1073 1074 /* 1075 * We define "progress" as either waiting on a timed break or delay, or 1076 * having had at least one transmitter interrupt. If none of these are 1077 * true, then just terminate the output and wake up that close thread. 1078 */ 1079 mutex_enter(asy->asy_excl); 1080 mutex_enter(asy->asy_excl_hi); 1081 if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) { 1082 async->async_ocnt = 0; 1083 async->async_flags &= ~ASYNC_BUSY; 1084 async->async_timer = 0; 1085 bp = async->async_xmitblk; 1086 async->async_xmitblk = NULL; 1087 mutex_exit(asy->asy_excl_hi); 1088 if (bp != NULL) 1089 freeb(bp); 1090 /* 1091 * Since this timer is running, we know that we're in exit(2). 1092 * That means that the user can't possibly be waiting on any 1093 * valid ioctl(2) completion anymore, and we should just flush 1094 * everything. 1095 */ 1096 flushq(async->async_ttycommon.t_writeq, FLUSHALL); 1097 cv_broadcast(&async->async_flags_cv); 1098 } else { 1099 async->async_flags &= ~ASYNC_PROGRESS; 1100 async->async_timer = timeout(async_progress_check, async, 1101 drv_usectohz(su_drain_check)); 1102 mutex_exit(asy->asy_excl_hi); 1103 } 1104 mutex_exit(asy->asy_excl); 1105 } 1106 1107 /* 1108 * Close routine. 1109 */ 1110 static int 1111 asyclose(queue_t *q, int flag, cred_t *cr __unused) 1112 { 1113 struct asyncline *async; 1114 struct asycom *asy; 1115 int icr, lcr; 1116 int nohupcl; 1117 1118 1119 #ifdef DEBUG 1120 if (asydebug & ASY_DEBUG_CLOSE) 1121 printf("close\n"); 1122 #endif 1123 async = q->q_ptr; 1124 ASSERT(async != NULL); 1125 asy = async->async_common; 1126 1127 /* get the nohupcl OBP property of this device */ 1128 nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS, 1129 "nohupcl", 0); 1130 1131 mutex_enter(asy->asy_excl); 1132 async->async_flags |= ASYNC_CLOSING; 1133 1134 /* 1135 * Turn off PPS handling early to avoid events occuring during 1136 * close. Also reset the DCD edge monitoring bit. 1137 */ 1138 mutex_enter(asy->asy_excl_hi); 1139 asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE); 1140 mutex_exit(asy->asy_excl_hi); 1141 1142 /* 1143 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and 1144 * untimed (TIOCSBRK). For the timed case, these are enqueued on our 1145 * write queue and there's a timer running, so we don't have to worry 1146 * about them. For the untimed case, though, the user obviously made a 1147 * mistake, because these are handled immediately. We'll terminate the 1148 * break now and honor their implicit request by discarding the rest of 1149 * the data. 1150 */ 1151 if (!(async->async_flags & ASYNC_BREAK)) { 1152 mutex_enter(asy->asy_excl_hi); 1153 lcr = INB(LCR); 1154 if (lcr & SETBREAK) { 1155 OUTB(LCR, (lcr & ~SETBREAK)); 1156 } 1157 mutex_exit(asy->asy_excl_hi); 1158 if (lcr & SETBREAK) 1159 goto nodrain; 1160 } 1161 1162 /* 1163 * If the user told us not to delay the close ("non-blocking"), then 1164 * don't bother trying to drain. 1165 * 1166 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever 1167 * getting an M_START (since these messages aren't enqueued), and the 1168 * only other way to clear the stop condition is by loss of DCD, which 1169 * would discard the queue data. Thus, we drop the output data if 1170 * ASYNC_STOPPED is set. 1171 */ 1172 if ((flag & (FNDELAY|FNONBLOCK)) || 1173 (async->async_flags & ASYNC_STOPPED)) { 1174 goto nodrain; 1175 } 1176 1177 /* 1178 * If there's any pending output, then we have to try to drain it. 1179 * There are two main cases to be handled: 1180 * - called by close(2): need to drain until done or until 1181 * a signal is received. No timeout. 1182 * - called by exit(2): need to drain while making progress 1183 * or until a timeout occurs. No signals. 1184 * 1185 * If we can't rely on receiving a signal to get us out of a hung 1186 * session, then we have to use a timer. In this case, we set a timer 1187 * to check for progress in sending the output data -- all that we ask 1188 * (at each interval) is that there's been some progress made. Since 1189 * the interrupt routine grabs buffers from the write queue, we can't 1190 * trust async_ocnt. Instead, we use a flag. 1191 * 1192 * Note that loss of carrier will cause the output queue to be flushed, 1193 * and we'll wake up again and finish normally. 1194 */ 1195 if (!ddi_can_receive_sig() && su_drain_check != 0) { 1196 async->async_flags &= ~ASYNC_PROGRESS; 1197 async->async_timer = timeout(async_progress_check, async, 1198 drv_usectohz(su_drain_check)); 1199 } 1200 1201 while (async->async_ocnt > 0 || 1202 async->async_ttycommon.t_writeq->q_first != NULL || 1203 (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) { 1204 if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0) 1205 break; 1206 } 1207 if (async->async_timer != 0) { 1208 (void) untimeout(async->async_timer); 1209 async->async_timer = 0; 1210 } 1211 1212 nodrain: 1213 mutex_enter(asy->asy_excl_hi); 1214 1215 /* turn off the loopback mode */ 1216 if ((async->async_dev != rconsdev) && 1217 (async->async_dev != kbddev) && 1218 (async->async_dev != stdindev)) { 1219 OUTB(MCR, INB(MCR) & ~ ASY_LOOP); 1220 } 1221 1222 async->async_ocnt = 0; 1223 if (async->async_xmitblk != NULL) 1224 freeb(async->async_xmitblk); 1225 async->async_xmitblk = NULL; 1226 1227 /* 1228 * If the "nohupcl" OBP property is set for this device, do 1229 * not turn off DTR and RTS no matter what. Otherwise, if the 1230 * line has HUPCL set or is incompletely opened, turn off DTR 1231 * and RTS to fix the modem line. 1232 */ 1233 if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) || 1234 (async->async_flags & ASYNC_WOPEN))) { 1235 /* turn off DTR, RTS but NOT interrupt to 386 */ 1236 OUTB(MCR, OUT2); 1237 mutex_exit(asy->asy_excl_hi); 1238 /* 1239 * Don't let an interrupt in the middle of close 1240 * bounce us back to the top; just continue closing 1241 * as if nothing had happened. 1242 */ 1243 if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0) 1244 goto out; 1245 mutex_enter(asy->asy_excl_hi); 1246 } 1247 1248 /* 1249 * If nobody's using it now, turn off receiver interrupts. 1250 */ 1251 if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) { 1252 icr = INB(ICR); 1253 OUTB(ICR, (icr & ~RIEN)); 1254 } 1255 mutex_exit(asy->asy_excl_hi); 1256 out: 1257 /* 1258 * Clear out device state. 1259 */ 1260 async->async_flags = 0; 1261 ttycommon_close(&async->async_ttycommon); 1262 cv_broadcast(&async->async_flags_cv); 1263 1264 /* 1265 * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in 1266 * async_softint or an interrupt was pending when the process 1267 * using the port exited. 1268 */ 1269 asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT; 1270 1271 /* 1272 * Cancel outstanding "bufcall" request. 1273 */ 1274 if (async->async_wbufcid) { 1275 unbufcall(async->async_wbufcid); 1276 async->async_wbufcid = 0; 1277 } 1278 1279 /* 1280 * If inperim is true, it means the port is closing while there's 1281 * a pending software interrupt. async_flags has been zeroed out, 1282 * so this instance of leaveq() needs to be called before we call 1283 * qprocsoff() to disable services on the q. If inperim is false, 1284 * leaveq() has already been called or we're not in a perimeter. 1285 */ 1286 if (asy->inperim == B_TRUE) { 1287 asy->inperim = B_FALSE; 1288 mutex_exit(asy->asy_excl); 1289 leaveq(q); 1290 } else { 1291 mutex_exit(asy->asy_excl); 1292 } 1293 1294 /* Note that qprocsoff can't be done until after interrupts are off */ 1295 qprocsoff(q); 1296 q->q_ptr = WR(q)->q_ptr = NULL; 1297 async->async_ttycommon.t_readq = NULL; 1298 async->async_ttycommon.t_writeq = NULL; 1299 1300 return (0); 1301 } 1302 1303 /* 1304 * Checks to see if the serial port is still transmitting 1305 * characters. It returns true when there are characters 1306 * queued to transmit, when the holding register contains 1307 * a byte, or when the shifting register still contains 1308 * data to send. 1309 * 1310 */ 1311 static boolean_t 1312 asy_isbusy(struct asycom *asy) 1313 { 1314 struct asyncline *async; 1315 1316 #ifdef DEBUG 1317 if (asydebug & ASY_DEBUG_EOT) 1318 printf("isbusy\n"); 1319 #endif 1320 async = (struct asyncline *)asy->asy_priv; 1321 ASSERT(mutex_owned(asy->asy_excl)); 1322 ASSERT(mutex_owned(asy->asy_excl_hi)); 1323 return ((async->async_ocnt > 0) || 1324 ((INB(LSR) & XSRE) == 0)); 1325 } 1326 1327 /* 1328 * Program the ASY port. Most of the async operation is based on the values 1329 * of 'c_iflag' and 'c_cflag'. 1330 */ 1331 static int 1332 asy_program(struct asycom *asy, int mode) 1333 { 1334 struct asyncline *async; 1335 int baudrate, c_flag; 1336 int icr, lcr; 1337 int ocflags; 1338 int error = 0; 1339 1340 ASSERT(mutex_owned(asy->asy_excl)); 1341 ASSERT(mutex_owned(asy->asy_excl_hi)); 1342 1343 #ifdef DEBUG 1344 if (asydebug & ASY_DEBUG_PROCS) 1345 printf("program\n"); 1346 #endif 1347 async = (struct asyncline *)asy->asy_priv; 1348 1349 baudrate = async->async_ttycommon.t_cflag & CBAUD; 1350 if (async->async_ttycommon.t_cflag & CBAUDEXT) 1351 baudrate += 16; 1352 1353 /* Limit baudrate so it can't index out of baudtable */ 1354 if (baudrate >= N_SU_SPEEDS) baudrate = B9600; 1355 1356 /* 1357 * If baud rate requested is greater than the speed cap 1358 * or is an unsupported baud rate then reset t_cflag baud 1359 * to the last valid baud rate. If this is the initial 1360 * pass through asy_program then set it to 9600. 1361 */ 1362 if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) || 1363 (baudtable[baudrate] > asy->asy_speed_cap)) { 1364 async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT & 1365 ~CIBAUD & ~CIBAUDEXT; 1366 if (mode == ASY_INIT) { 1367 async->async_ttycommon.t_cflag |= B9600; 1368 async->async_ttycommon.t_cflag |= B9600 << IBSHIFT; 1369 baudrate = B9600; 1370 } else { 1371 async->async_ttycommon.t_cflag |= 1372 (asy->asy_ocflags & (CBAUD | CBAUDEXT | 1373 CIBAUD | CIBAUDEXT)); 1374 error = EINVAL; 1375 goto end; 1376 } 1377 } 1378 1379 /* 1380 * If CIBAUD and CIBAUDEXT are zero then we should set them to 1381 * the equivelant output baud bits. Else, if CIBAUD and CIBAUDEXT 1382 * don't match CBAUD and CBAUDEXT respectively then we should 1383 * notify the requestor that we do not support split speeds. 1384 */ 1385 if ((async->async_ttycommon.t_cflag & (CIBAUD|CIBAUDEXT)) == 0) { 1386 async->async_ttycommon.t_cflag |= 1387 (async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT; 1388 if (async->async_ttycommon.t_cflag & CBAUDEXT) 1389 async->async_ttycommon.t_cflag |= CIBAUDEXT; 1390 } else { 1391 if ((((async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT) != 1392 (async->async_ttycommon.t_cflag & CIBAUD)) || 1393 !(((async->async_ttycommon.t_cflag & (CBAUDEXT | 1394 CIBAUDEXT)) == (CBAUDEXT | CIBAUDEXT)) || 1395 ((async->async_ttycommon.t_cflag & (CBAUDEXT | 1396 CIBAUDEXT)) == 0))) { 1397 async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT & 1398 ~CIBAUD & ~CIBAUDEXT; 1399 async->async_ttycommon.t_cflag |= 1400 (asy->asy_ocflags & (CBAUD | CBAUDEXT | 1401 CIBAUD | CIBAUDEXT)); 1402 error = EINVAL; 1403 goto end; 1404 } 1405 } 1406 1407 c_flag = async->async_ttycommon.t_cflag & 1408 (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD | 1409 CBAUDEXT | CIBAUD | CIBAUDEXT); 1410 1411 /* disable interrupts, see EXAR bug */ 1412 if (asy->asy_hwtype == ASY16C554D) 1413 OUTB(SPR, 0); 1414 OUTB(ICR, 0); 1415 1416 ocflags = asy->asy_ocflags; 1417 1418 /* flush/reset the status registers */ 1419 if (mode == ASY_INIT) { 1420 (void) INB(DAT); 1421 (void) INB(ISR); 1422 (void) INB(LSR); 1423 (void) INB(MSR); 1424 } 1425 1426 if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) { 1427 /* Set line control */ 1428 lcr = INB(LCR); 1429 lcr &= ~(WLS0|WLS1|STB|PEN|EPS); 1430 1431 if (c_flag & CSTOPB) 1432 lcr |= STB; /* 2 stop bits */ 1433 1434 if (c_flag & PARENB) 1435 lcr |= PEN; 1436 1437 if ((c_flag & PARODD) == 0) 1438 lcr |= EPS; 1439 1440 switch (c_flag & CSIZE) { 1441 case CS5: 1442 lcr |= BITS5; 1443 break; 1444 case CS6: 1445 lcr |= BITS6; 1446 break; 1447 case CS7: 1448 lcr |= BITS7; 1449 break; 1450 case CS8: 1451 lcr |= BITS8; 1452 break; 1453 } 1454 1455 /* set the baud rate when the rate is NOT B0 */ 1456 if (baudrate != 0) { 1457 OUTB(LCR, DLAB); 1458 OUTB(DAT, (asyspdtab[baudrate] * 1459 asy->asy_baud_divisor_factor) & 0xff); 1460 OUTB(ICR, ((asyspdtab[baudrate] * 1461 asy->asy_baud_divisor_factor) >> 8) & 0xff); 1462 } 1463 /* set the line control modes */ 1464 OUTB(LCR, lcr); 1465 1466 /* 1467 * if transitioning from CREAD off to CREAD on, 1468 * flush the FIFO buffer if we have one. 1469 */ 1470 if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) { 1471 if (asy->asy_use_fifo == FIFO_ON) { 1472 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | 1473 (asy->asy_trig_level & 0xff)); 1474 } 1475 } 1476 1477 /* remember the new cflags */ 1478 asy->asy_ocflags = c_flag & ~CLOCAL; 1479 } 1480 1481 /* whether or not CLOCAL is set, modify the modem control lines */ 1482 if (baudrate == 0) 1483 /* B0 has been issued, lower DTR */ 1484 OUTB(MCR, RTS|OUT2); 1485 else 1486 /* raise DTR */ 1487 OUTB(MCR, DTR|RTS|OUT2); 1488 1489 /* 1490 * Call the modem status interrupt handler to check for the carrier 1491 * in case CLOCAL was turned off after the carrier came on. 1492 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.) 1493 */ 1494 async_msint(asy); 1495 1496 /* Set interrupt control */ 1497 if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS)) 1498 /* 1499 * direct-wired line ignores DCD, so we don't enable modem 1500 * status interrupts. 1501 */ 1502 icr = (TIEN | SIEN); 1503 else 1504 icr = (TIEN | SIEN | MIEN); 1505 1506 if (c_flag & CREAD) 1507 icr |= RIEN; 1508 1509 OUTB(ICR, icr); 1510 end: 1511 return (error); 1512 } 1513 1514 /* 1515 * Polled mode support -- all functions called with interrupts 1516 * disabled. 1517 */ 1518 1519 static void 1520 asyputchar(cons_polledio_arg_t arg, uchar_t c) 1521 { 1522 struct asycom *asy = (struct asycom *)arg; 1523 1524 /* 1525 * If we see a line feed make sure to also 1526 * put out a carriage return. 1527 */ 1528 if (c == '\n') 1529 asyputchar(arg, '\r'); 1530 1531 while ((INB(LSR) & XHRE) == 0) { 1532 /* wait for the transmission to complete */ 1533 drv_usecwait(10); 1534 } 1535 1536 /* ouput the character */ 1537 OUTB(DAT, c); 1538 } 1539 1540 /* 1541 * Determines if there is a character avaialable for 1542 * reading. 1543 */ 1544 static boolean_t 1545 asyischar(cons_polledio_arg_t arg) 1546 { 1547 struct asycom *asy = (struct asycom *)arg; 1548 return ((INB(LSR) & RCA) != 0); 1549 } 1550 1551 static int 1552 asygetchar(cons_polledio_arg_t arg) 1553 { 1554 struct asycom *asy = (struct asycom *)arg; 1555 1556 /* 1557 * Spin waiting for a character to be 1558 * available to read. 1559 */ 1560 while (!asyischar(arg)) 1561 drv_usecwait(10); 1562 1563 return (INB(DAT)); 1564 } 1565 1566 /* 1567 * Called when machine is transitioning to polled mode 1568 */ 1569 static void 1570 asy_polled_enter(cons_polledio_arg_t arg) 1571 { 1572 struct asycom *asy = (struct asycom *)arg; 1573 1574 mutex_enter(asy->asy_excl); 1575 mutex_enter(asy->asy_excl_hi); 1576 1577 /* 1578 * If this is the first time that asy_polled_enter() 1579 * has been called, during this transition request, 1580 * save the ICR. Clear the software interrupt 1581 * flags since we won't be able to handle these when 1582 * we are in polled mode. 1583 */ 1584 if (!asy->polled_enter) { 1585 asy->polled_enter = B_TRUE; 1586 asy->polled_icr = INB(ICR); 1587 1588 /* Disable HW interrupts */ 1589 if (asy->asy_hwtype == ASY16C554D) 1590 OUTB(SPR, 0); 1591 OUTB(ICR, 0); 1592 1593 asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT; 1594 } 1595 mutex_exit(asy->asy_excl_hi); 1596 mutex_exit(asy->asy_excl); 1597 } 1598 1599 /* 1600 * Called when machine is transitioning from polled mode. 1601 */ 1602 static void 1603 asy_polled_exit(cons_polledio_arg_t arg) 1604 { 1605 struct asycom *asy = (struct asycom *)arg; 1606 1607 mutex_enter(asy->asy_excl); 1608 mutex_enter(asy->asy_excl_hi); 1609 1610 /* Restore the ICR */ 1611 OUTB(ICR, asy->polled_icr); 1612 1613 /* 1614 * We have finished this polled IO transition. 1615 * Set polled_enter to B_FALSE to note this. 1616 */ 1617 asy->polled_enter = B_FALSE; 1618 mutex_exit(asy->asy_excl_hi); 1619 mutex_exit(asy->asy_excl); 1620 } 1621 1622 /* 1623 * asyintr() is the High Level Interrupt Handler. 1624 * 1625 * There are four different interrupt types indexed by ISR register values: 1626 * 0: modem 1627 * 1: Tx holding register is empty, ready for next char 1628 * 2: Rx register now holds a char to be picked up 1629 * 3: error or break on line 1630 * This routine checks the Bit 0 (interrupt-not-pending) to determine if 1631 * the interrupt is from this port. 1632 */ 1633 uint_t 1634 asyintr(caddr_t argasy) 1635 { 1636 struct asycom *asy = (struct asycom *)argasy; 1637 struct asyncline *async; 1638 int ret_status = DDI_INTR_UNCLAIMED; 1639 uchar_t interrupt_id, lsr; 1640 1641 interrupt_id = INB(ISR) & 0x0F; 1642 async = (struct asyncline *)asy->asy_priv; 1643 if ((async == NULL) || 1644 !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { 1645 if (interrupt_id & NOINTERRUPT) { 1646 return (DDI_INTR_UNCLAIMED); 1647 } else { 1648 lsr = INB(LSR); 1649 if ((lsr & BRKDET) && 1650 ((abort_enable == KIOCABORTENABLE) && 1651 (async->async_dev == rconsdev))) 1652 abort_sequence_enter((char *)NULL); 1653 else { 1654 /* reset line status */ 1655 (void) INB(LSR); 1656 /* discard any data */ 1657 (void) INB(DAT); 1658 /* reset modem status */ 1659 (void) INB(MSR); 1660 return (DDI_INTR_CLAIMED); 1661 } 1662 } 1663 } 1664 /* 1665 * Spurious interrupts happen in this driver 1666 * because of the transmission on serial port not handled 1667 * properly. 1668 * 1669 * The reasons for Spurious interrupts are: 1670 * 1. There is a path in async_nstart which transmits 1671 * characters without going through interrupt services routine 1672 * which causes spurious interrupts to happen. 1673 * 2. In the async_txint more than one character is sent 1674 * in one interrupt service. 1675 * 3. In async_rxint more than one characters are received in 1676 * in one interrupt service. 1677 * 1678 * Hence we have flags to indicate that such scenerio has happened. 1679 * and claim only such interrupts and others we donot claim it 1680 * as it could be a indicator of some hardware problem. 1681 * 1682 */ 1683 if (interrupt_id & NOINTERRUPT) { 1684 mutex_enter(asy->asy_excl_hi); 1685 if ((asy->asy_xmit_count > 1) || 1686 (asy->asy_out_of_band_xmit > 0) || 1687 (asy->asy_rx_count > 1)) { 1688 asy->asy_xmit_count = 0; 1689 asy->asy_out_of_band_xmit = 0; 1690 asy->asy_rx_count = 0; 1691 mutex_exit(asy->asy_excl_hi); 1692 return (DDI_INTR_CLAIMED); 1693 } else { 1694 mutex_exit(asy->asy_excl_hi); 1695 return (DDI_INTR_UNCLAIMED); 1696 } 1697 } 1698 ret_status = DDI_INTR_CLAIMED; 1699 mutex_enter(asy->asy_excl_hi); 1700 if (asy->asy_hwtype == ASY82510) 1701 OUTB(ISR, 0x00); /* set bank 0 */ 1702 1703 #ifdef DEBUG 1704 if (asydebug & ASY_DEBUG_INTR) 1705 prom_printf("l"); 1706 #endif 1707 lsr = INB(LSR); 1708 switch (interrupt_id) { 1709 case RxRDY: 1710 case RSTATUS: 1711 case FFTMOUT: 1712 /* receiver interrupt or receiver errors */ 1713 async_rxint(asy, lsr); 1714 break; 1715 case TxRDY: 1716 /* transmit interrupt */ 1717 async_txint(asy, lsr); 1718 break; 1719 case MSTATUS: 1720 /* modem status interrupt */ 1721 async_msint(asy); 1722 break; 1723 } 1724 mutex_exit(asy->asy_excl_hi); 1725 return (ret_status); 1726 } 1727 1728 /* 1729 * Transmitter interrupt service routine. 1730 * If there is more data to transmit in the current pseudo-DMA block, 1731 * send the next character if output is not stopped or draining. 1732 * Otherwise, queue up a soft interrupt. 1733 * 1734 * XXX - Needs review for HW FIFOs. 1735 */ 1736 static void 1737 async_txint(struct asycom *asy, uchar_t lsr) 1738 { 1739 struct asyncline *async = (struct asyncline *)asy->asy_priv; 1740 int fifo_len; 1741 int xmit_progress; 1742 1743 asycheckflowcontrol_hw(asy); 1744 1745 /* 1746 * If ASYNC_BREAK has been set, return to asyintr()'s context to 1747 * claim the interrupt without performing any action. 1748 */ 1749 if (async->async_flags & ASYNC_BREAK) 1750 return; 1751 1752 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 1753 1754 /* 1755 * Check for flow control and do the needed action. 1756 */ 1757 if (asycheckflowcontrol_sw(asy)) { 1758 return; 1759 } 1760 1761 if (async->async_ocnt > 0 && 1762 !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) { 1763 xmit_progress = 0; 1764 while (fifo_len > 0 && async->async_ocnt > 0) { 1765 if (lsr & XHRE) { 1766 OUTB(DAT, *async->async_optr++); 1767 fifo_len--; 1768 async->async_ocnt--; 1769 xmit_progress++; 1770 } 1771 /* 1772 * Reading the lsr, (moved reading at the end of 1773 * while loop) as already we have read once at 1774 * the beginning of interrupt service 1775 */ 1776 lsr = INB(LSR); 1777 } 1778 asy->asy_xmit_count = xmit_progress; 1779 if (xmit_progress > 0) 1780 async->async_flags |= ASYNC_PROGRESS; 1781 } 1782 1783 if (fifo_len == 0) { 1784 return; 1785 } 1786 1787 1788 ASYSETSOFT(asy); 1789 } 1790 1791 /* 1792 * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive 1793 * error interrupt. 1794 * Try to put the character into the circular buffer for this line; if it 1795 * overflows, indicate a circular buffer overrun. If this port is always 1796 * to be serviced immediately, or the character is a STOP character, or 1797 * more than 15 characters have arrived, queue up a soft interrupt to 1798 * drain the circular buffer. 1799 * XXX - needs review for hw FIFOs support. 1800 */ 1801 1802 static void 1803 async_rxint(struct asycom *asy, uchar_t lsr) 1804 { 1805 struct asyncline *async = (struct asyncline *)asy->asy_priv; 1806 uchar_t c = 0; 1807 uint_t s = 0, needsoft = 0; 1808 register tty_common_t *tp; 1809 1810 tp = &async->async_ttycommon; 1811 if (!(tp->t_cflag & CREAD)) { 1812 if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 1813 (void) (INB(DAT) & 0xff); 1814 } 1815 return; /* line is not open for read? */ 1816 } 1817 asy->asy_rx_count = 0; 1818 while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 1819 c = 0; 1820 s = 0; 1821 asy->asy_rx_count++; 1822 if (lsr & RCA) { 1823 c = INB(DAT) & 0xff; 1824 /* 1825 * Even a single character is received 1826 * we need Soft interrupt to pass it to 1827 * higher layers. 1828 */ 1829 needsoft = 1; 1830 } 1831 1832 /* Check for character break sequence */ 1833 if ((abort_enable == KIOCABORTALTERNATE) && 1834 (async->async_dev == rconsdev)) { 1835 if (abort_charseq_recognize(c)) 1836 abort_sequence_enter((char *)NULL); 1837 } 1838 1839 /* Handle framing errors */ 1840 if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) { 1841 if (lsr & PARERR) { 1842 if (tp->t_iflag & INPCK) /* parity enabled */ 1843 s |= PERROR; 1844 } 1845 if (lsr & (FRMERR|BRKDET)) 1846 s |= FRERROR; 1847 if (lsr & OVRRUN) { 1848 async->async_hw_overrun = 1; 1849 s |= OVERRUN; 1850 } 1851 } 1852 1853 if (s == 0) 1854 if ((tp->t_iflag & PARMRK) && 1855 !(tp->t_iflag & (IGNPAR|ISTRIP)) && 1856 (c == 0377)) 1857 if (RING_POK(async, 2)) { 1858 RING_PUT(async, 0377); 1859 RING_PUT(async, c); 1860 } else 1861 async->async_sw_overrun = 1; 1862 else 1863 if (RING_POK(async, 1)) 1864 RING_PUT(async, c); 1865 else 1866 async->async_sw_overrun = 1; 1867 else 1868 if (s & FRERROR) { /* Handle framing errors */ 1869 if (c == 0) { 1870 /* Look for break on kbd, stdin, or rconsdev */ 1871 if ((async->async_dev == kbddev) || 1872 ((async->async_dev == rconsdev) || 1873 (async->async_dev == stdindev)) && 1874 (abort_enable != 1875 KIOCABORTALTERNATE)) 1876 abort_sequence_enter((char *)0); 1877 else 1878 async->async_break++; 1879 } else { 1880 if (RING_POK(async, 1)) 1881 RING_MARK(async, c, s); 1882 else 1883 async->async_sw_overrun = 1; 1884 } 1885 } else { /* Parity errors handled by ldterm */ 1886 if (RING_POK(async, 1)) 1887 RING_MARK(async, c, s); 1888 else 1889 async->async_sw_overrun = 1; 1890 } 1891 lsr = INB(LSR); 1892 if (asy->asy_rx_count > 16) break; 1893 } 1894 /* Check whether there is a request for hw/sw inbound/input flow ctrl */ 1895 if ((async->async_ttycommon.t_cflag & CRTSXOFF) || 1896 (async->async_ttycommon.t_iflag & IXOFF)) 1897 if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) { 1898 #ifdef DEBUG 1899 if (asydebug & ASY_DEBUG_HFLOW) 1900 printf("asy%d: hardware flow stop input.\n", 1901 UNIT(async->async_dev)); 1902 #endif 1903 async->async_flags |= ASYNC_HW_IN_FLOW; 1904 async->async_flowc = async->async_stopc; 1905 async->async_ringbuf_overflow = 1; 1906 } 1907 1908 if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || 1909 (RING_FRAC(async)) || (async->async_polltid == 0)) 1910 ASYSETSOFT(asy); /* need a soft interrupt */ 1911 } 1912 1913 /* 1914 * Interrupt on port: handle PPS event. This function is only called 1915 * for a port on which PPS event handling has been enabled. 1916 */ 1917 static void 1918 asy_ppsevent(struct asycom *asy, int msr) 1919 { 1920 if (asy->asy_flags & ASY_PPS_EDGE) { 1921 /* Have seen leading edge, now look for and record drop */ 1922 if ((msr & DCD) == 0) 1923 asy->asy_flags &= ~ASY_PPS_EDGE; 1924 /* 1925 * Waiting for leading edge, look for rise; stamp event and 1926 * calibrate kernel clock. 1927 */ 1928 } else if (msr & DCD) { 1929 /* 1930 * This code captures a timestamp at the designated 1931 * transition of the PPS signal (DCD asserted). The 1932 * code provides a pointer to the timestamp, as well 1933 * as the hardware counter value at the capture. 1934 * 1935 * Note: the kernel has nano based time values while 1936 * NTP requires micro based, an in-line fast algorithm 1937 * to convert nsec to usec is used here -- see hrt2ts() 1938 * in common/os/timers.c for a full description. 1939 */ 1940 struct timeval *tvp = &asy_ppsev.tv; 1941 timestruc_t ts; 1942 long nsec, usec; 1943 1944 asy->asy_flags |= ASY_PPS_EDGE; 1945 gethrestime(&ts); 1946 nsec = ts.tv_nsec; 1947 usec = nsec + (nsec >> 2); 1948 usec = nsec + (usec >> 1); 1949 usec = nsec + (usec >> 2); 1950 usec = nsec + (usec >> 4); 1951 usec = nsec - (usec >> 3); 1952 usec = nsec + (usec >> 2); 1953 usec = nsec + (usec >> 3); 1954 usec = nsec + (usec >> 4); 1955 usec = nsec + (usec >> 1); 1956 usec = nsec + (usec >> 6); 1957 tvp->tv_usec = usec >> 10; 1958 tvp->tv_sec = ts.tv_sec; 1959 1960 ++asy_ppsev.serial; 1961 1962 /* 1963 * Because the kernel keeps a high-resolution time, 1964 * pass the current highres timestamp in tvp and zero 1965 * in usec. 1966 */ 1967 ddi_hardpps(tvp, 0); 1968 } 1969 } 1970 1971 /* 1972 * Modem status interrupt. 1973 * 1974 * (Note: It is assumed that the MSR hasn't been read by asyintr().) 1975 */ 1976 1977 static void 1978 async_msint(struct asycom *asy) 1979 { 1980 struct asyncline *async = (struct asyncline *)asy->asy_priv; 1981 int msr; 1982 1983 msr = INB(MSR); /* this resets the interrupt */ 1984 asy->asy_cached_msr = msr; 1985 #ifdef DEBUG 1986 if (asydebug & ASY_DEBUG_STATE) { 1987 printf(" transition: %3s %3s %3s %3s\n" 1988 "current state: %3s %3s %3s %3s\n", 1989 (msr & DCTS) ? "CTS" : " ", 1990 (msr & DDSR) ? "DSR" : " ", 1991 (msr & DRI) ? "RI " : " ", 1992 (msr & DDCD) ? "DCD" : " ", 1993 (msr & CTS) ? "CTS" : " ", 1994 (msr & DSR) ? "DSR" : " ", 1995 (msr & RI) ? "RI " : " ", 1996 (msr & DCD) ? "DCD" : " "); 1997 } 1998 #endif 1999 if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) { 2000 #ifdef DEBUG 2001 if (asydebug & ASY_DEBUG_HFLOW) 2002 printf("asy%d: hflow start\n", 2003 UNIT(async->async_dev)); 2004 #endif 2005 async->async_flags |= ASYNC_HW_OUT_FLW; 2006 } 2007 if (asy->asy_hwtype == ASY82510) 2008 OUTB(MSR, (msr & 0xF0)); 2009 2010 /* Handle PPS event */ 2011 if (asy->asy_flags & ASY_PPS) 2012 asy_ppsevent(asy, msr); 2013 2014 async->async_ext++; 2015 ASYSETSOFT(asy); 2016 } 2017 2018 /* 2019 * Handle a second-stage interrupt. 2020 */ 2021 uint_t 2022 asysoftintr(caddr_t intarg) 2023 { 2024 struct asycom *asy = (struct asycom *)intarg; 2025 struct asyncline *async; 2026 int rv; 2027 int cc; 2028 /* 2029 * Test and clear soft interrupt. 2030 */ 2031 mutex_enter(asy->asy_soft_lock); 2032 #ifdef DEBUG 2033 if (asydebug & ASY_DEBUG_PROCS) 2034 printf("softintr\n"); 2035 #endif 2036 rv = asy->asysoftpend; 2037 if (rv != 0) 2038 asy->asysoftpend = 0; 2039 mutex_exit(asy->asy_soft_lock); 2040 2041 if (rv) { 2042 if (asy->asy_priv == NULL) 2043 return (rv); 2044 async = (struct asyncline *)asy->asy_priv; 2045 mutex_enter(asy->asy_excl_hi); 2046 if (asy->asy_flags & ASY_NEEDSOFT) { 2047 asy->asy_flags &= ~ASY_NEEDSOFT; 2048 mutex_exit(asy->asy_excl_hi); 2049 (void) async_softint(asy); 2050 mutex_enter(asy->asy_excl_hi); 2051 } 2052 /* 2053 * There are some instances where the softintr is not 2054 * scheduled and hence not called. It so happened that makes 2055 * the last few characters to be stuck in ringbuffer. 2056 * Hence, call once again the handler so that the last few 2057 * characters are cleared. 2058 */ 2059 cc = RING_CNT(async); 2060 mutex_exit(asy->asy_excl_hi); 2061 if (cc > 0) { 2062 (void) async_softint(asy); 2063 } 2064 } 2065 return (rv); 2066 } 2067 2068 /* 2069 * Handle a software interrupt. 2070 */ 2071 static int 2072 async_softint(struct asycom *asy) 2073 { 2074 struct asyncline *async = (struct asyncline *)asy->asy_priv; 2075 uint_t cc; 2076 mblk_t *bp; 2077 queue_t *q; 2078 uchar_t val; 2079 uchar_t c; 2080 tty_common_t *tp; 2081 2082 #ifdef DEBUG 2083 if (asydebug & ASY_DEBUG_PROCS) 2084 printf("process\n"); 2085 #endif 2086 mutex_enter(asy->asy_excl); 2087 if (asy->asy_flags & ASY_DOINGSOFT) { 2088 mutex_exit(asy->asy_excl); 2089 return (0); 2090 } 2091 tp = &async->async_ttycommon; 2092 q = tp->t_readq; 2093 if (q != NULL) { 2094 mutex_exit(asy->asy_excl); 2095 enterq(q); 2096 mutex_enter(asy->asy_excl); 2097 } 2098 mutex_enter(asy->asy_excl_hi); 2099 asy->asy_flags |= ASY_DOINGSOFT; 2100 2101 if (INB(ICR) & MIEN) 2102 val = asy->asy_cached_msr & 0xFF; 2103 else 2104 val = INB(MSR) & 0xFF; 2105 2106 if (async->async_ttycommon.t_cflag & CRTSCTS) { 2107 if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) { 2108 #ifdef DEBUG 2109 if (asydebug & ASY_DEBUG_HFLOW) 2110 printf("asy%d: hflow start\n", 2111 UNIT(async->async_dev)); 2112 #endif 2113 async->async_flags &= ~ASYNC_HW_OUT_FLW; 2114 mutex_exit(asy->asy_excl_hi); 2115 if (async->async_ocnt > 0) { 2116 mutex_enter(asy->asy_excl_hi); 2117 async_resume(async); 2118 mutex_exit(asy->asy_excl_hi); 2119 } else { 2120 async_start(async); 2121 } 2122 mutex_enter(asy->asy_excl_hi); 2123 } 2124 } 2125 if (async->async_ext) { 2126 async->async_ext = 0; 2127 /* check for carrier up */ 2128 if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) { 2129 /* carrier present */ 2130 if ((async->async_flags & ASYNC_CARR_ON) == 0) { 2131 async->async_flags |= ASYNC_CARR_ON; 2132 mutex_exit(asy->asy_excl_hi); 2133 mutex_exit(asy->asy_excl); 2134 if (async->async_flags & ASYNC_ISOPEN) 2135 (void) putctl(q, M_UNHANGUP); 2136 cv_broadcast(&async->async_flags_cv); 2137 mutex_enter(asy->asy_excl); 2138 mutex_enter(asy->asy_excl_hi); 2139 } 2140 } else { 2141 if ((async->async_flags & ASYNC_CARR_ON) && 2142 !(tp->t_cflag & CLOCAL)) { 2143 int flushflag; 2144 2145 /* 2146 * Carrier went away. 2147 * Drop DTR, abort any output in 2148 * progress, indicate that output is 2149 * not stopped, and send a hangup 2150 * notification upstream. 2151 * 2152 * If we're in the midst of close, then flush 2153 * everything. Don't leave stale ioctls lying 2154 * about. 2155 */ 2156 val = INB(MCR); 2157 OUTB(MCR, (val & ~DTR)); 2158 flushflag = (async->async_flags & 2159 ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA; 2160 if (tp->t_writeq != NULL) { 2161 flushq(tp->t_writeq, flushflag); 2162 } 2163 if (async->async_xmitblk != NULL) { 2164 freeb(async->async_xmitblk); 2165 async->async_xmitblk = NULL; 2166 } 2167 if (async->async_flags & ASYNC_BUSY) { 2168 async->async_ocnt = 0; 2169 async->async_flags &= ~ASYNC_BUSY; 2170 } 2171 async->async_flags &= ~ASYNC_STOPPED; 2172 if (async->async_flags & ASYNC_ISOPEN) { 2173 mutex_exit(asy->asy_excl_hi); 2174 mutex_exit(asy->asy_excl); 2175 (void) putctl(q, M_HANGUP); 2176 mutex_enter(asy->asy_excl); 2177 mutex_enter(asy->asy_excl_hi); 2178 } 2179 async->async_flags &= ~ASYNC_CARR_ON; 2180 mutex_exit(asy->asy_excl_hi); 2181 cv_broadcast(&async->async_flags_cv); 2182 mutex_enter(asy->asy_excl_hi); 2183 } 2184 } 2185 } 2186 2187 /* 2188 * If data has been added to the circular buffer, remove 2189 * it from the buffer, and send it up the stream if there's 2190 * somebody listening. Try to do it 16 bytes at a time. If we 2191 * have more than 16 bytes to move, move 16 byte chunks and 2192 * leave the rest for next time around (maybe it will grow). 2193 */ 2194 if (!(async->async_flags & ASYNC_ISOPEN)) { 2195 RING_INIT(async); 2196 goto rv; 2197 } 2198 if ((cc = RING_CNT(async)) == 0) { 2199 goto rv; 2200 } 2201 mutex_exit(asy->asy_excl_hi); 2202 2203 if (!canput(q)) { 2204 if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { 2205 #ifdef DEBUG 2206 if (!(asydebug & ASY_DEBUG_HFLOW)) { 2207 printf("asy%d: hflow stop input.\n", 2208 UNIT(async->async_dev)); 2209 if (canputnext(q)) 2210 printf("asy%d: next queue is " 2211 "ready\n", 2212 UNIT(async->async_dev)); 2213 } 2214 #endif 2215 mutex_enter(asy->asy_excl_hi); 2216 async->async_flags |= ASYNC_HW_IN_FLOW; 2217 async->async_flowc = async->async_stopc; 2218 } else mutex_enter(asy->asy_excl_hi); 2219 goto rv; 2220 } 2221 2222 if (async->async_ringbuf_overflow) { 2223 if ((async->async_flags & ASYNC_HW_IN_FLOW) && 2224 ((int)(RING_CNT(async)) < (RINGSIZE/4))) { 2225 #ifdef DEBUG 2226 if (asydebug & ASY_DEBUG_HFLOW) 2227 printf("asy%d: hflow start input.\n", 2228 UNIT(async->async_dev)); 2229 #endif 2230 mutex_enter(asy->asy_excl_hi); 2231 async->async_flags &= ~ASYNC_HW_IN_FLOW; 2232 async->async_flowc = async->async_startc; 2233 async->async_ringbuf_overflow = 0; 2234 goto rv; 2235 } 2236 } 2237 #ifdef DEBUG 2238 if (asydebug & ASY_DEBUG_INPUT) 2239 printf("asy%d: %d char(s) in queue.\n", 2240 UNIT(async->async_dev), cc); 2241 #endif 2242 /* 2243 * Before you pull the characters from the RING BUF 2244 * Check whether you can put into the queue again 2245 */ 2246 if ((!canputnext(q)) || (!canput(q))) { 2247 mutex_enter(asy->asy_excl_hi); 2248 if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { 2249 async->async_flags |= ASYNC_HW_IN_FLOW; 2250 async->async_flowc = async->async_stopc; 2251 async->async_queue_full = 1; 2252 } 2253 goto rv; 2254 } 2255 mutex_enter(asy->asy_excl_hi); 2256 if (async->async_queue_full) { 2257 /* 2258 * Last time the Stream queue didnot allow 2259 * now it allows so, relax, the flow control 2260 */ 2261 if (async->async_flags & ASYNC_HW_IN_FLOW) { 2262 async->async_flags &= ~ASYNC_HW_IN_FLOW; 2263 async->async_queue_full = 0; 2264 async->async_flowc = async->async_startc; 2265 goto rv; 2266 } else 2267 async->async_queue_full = 0; 2268 } 2269 mutex_exit(asy->asy_excl_hi); 2270 if (!(bp = allocb(cc, BPRI_MED))) { 2271 ttycommon_qfull(&async->async_ttycommon, q); 2272 mutex_enter(asy->asy_excl_hi); 2273 goto rv; 2274 } 2275 mutex_enter(asy->asy_excl_hi); 2276 do { 2277 if (RING_ERR(async, S_ERRORS)) { 2278 RING_UNMARK(async); 2279 c = RING_GET(async); 2280 break; 2281 } else { 2282 *bp->b_wptr++ = RING_GET(async); 2283 } 2284 } while (--cc); 2285 2286 mutex_exit(asy->asy_excl_hi); 2287 mutex_exit(asy->asy_excl); 2288 if (bp->b_wptr > bp->b_rptr) { 2289 if (!canputnext(q)) { 2290 if (!canput(q)) { 2291 /* 2292 * Even after taking all precautions that 2293 * Still we are unable to queue, then we 2294 * cannot do anything, just drop the block 2295 */ 2296 cmn_err(CE_NOTE, 2297 "su%d: local queue full\n", 2298 UNIT(async->async_dev)); 2299 freemsg(bp); 2300 mutex_enter(asy->asy_excl_hi); 2301 if ((async->async_flags & 2302 ASYNC_HW_IN_FLOW) == 0) { 2303 async->async_flags |= 2304 ASYNC_HW_IN_FLOW; 2305 async->async_flowc = 2306 async->async_stopc; 2307 async->async_queue_full = 1; 2308 } 2309 mutex_exit(asy->asy_excl_hi); 2310 } else { 2311 (void) putq(q, bp); 2312 } 2313 } else { 2314 putnext(q, bp); 2315 } 2316 } else { 2317 freemsg(bp); 2318 } 2319 /* 2320 * If we have a parity error, then send 2321 * up an M_BREAK with the "bad" 2322 * character as an argument. Let ldterm 2323 * figure out what to do with the error. 2324 */ 2325 if (cc) 2326 (void) putctl1(q, M_BREAK, c); 2327 mutex_enter(asy->asy_excl); 2328 mutex_enter(asy->asy_excl_hi); 2329 rv: 2330 /* 2331 * If a transmission has finished, indicate that it's finished, 2332 * and start that line up again. 2333 */ 2334 if (async->async_break) { 2335 async->async_break = 0; 2336 if (async->async_flags & ASYNC_ISOPEN) { 2337 mutex_exit(asy->asy_excl_hi); 2338 mutex_exit(asy->asy_excl); 2339 (void) putctl(q, M_BREAK); 2340 mutex_enter(asy->asy_excl); 2341 mutex_enter(asy->asy_excl_hi); 2342 } 2343 } 2344 if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) || 2345 (async->async_flowc != '\0')) { 2346 async->async_flags &= ~ASYNC_BUSY; 2347 mutex_exit(asy->asy_excl_hi); 2348 if (async->async_xmitblk) 2349 freeb(async->async_xmitblk); 2350 async->async_xmitblk = NULL; 2351 if (async->async_flags & ASYNC_ISOPEN) { 2352 asy->inperim = B_TRUE; 2353 mutex_exit(asy->asy_excl); 2354 enterq(async->async_ttycommon.t_writeq); 2355 mutex_enter(asy->asy_excl); 2356 } 2357 async_start(async); 2358 /* 2359 * We need to check for inperim and ISOPEN due to 2360 * multi-threading implications; it's possible to close the 2361 * port and nullify async_flags while completing the software 2362 * interrupt. If the port is closed, leaveq() will have already 2363 * been called. We don't want to call it twice. 2364 */ 2365 if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) { 2366 mutex_exit(asy->asy_excl); 2367 leaveq(async->async_ttycommon.t_writeq); 2368 mutex_enter(asy->asy_excl); 2369 asy->inperim = B_FALSE; 2370 } 2371 if (!(async->async_flags & ASYNC_BUSY)) 2372 cv_broadcast(&async->async_flags_cv); 2373 mutex_enter(asy->asy_excl_hi); 2374 } 2375 /* 2376 * A note about these overrun bits: all they do is *tell* someone 2377 * about an error- They do not track multiple errors. In fact, 2378 * you could consider them latched register bits if you like. 2379 * We are only interested in printing the error message once for 2380 * any cluster of overrun errors. 2381 */ 2382 if (async->async_hw_overrun) { 2383 if (async->async_flags & ASYNC_ISOPEN) { 2384 if (su_log > 0) { 2385 mutex_exit(asy->asy_excl_hi); 2386 mutex_exit(asy->asy_excl); 2387 cmn_err(CE_NOTE, "su%d: silo overflow\n", 2388 UNIT(async->async_dev)); 2389 mutex_enter(asy->asy_excl); 2390 mutex_enter(asy->asy_excl_hi); 2391 } 2392 INC64_KSTAT(asy, siloover); 2393 } 2394 async->async_hw_overrun = 0; 2395 } 2396 if (async->async_sw_overrun) { 2397 if (async->async_flags & ASYNC_ISOPEN) { 2398 if (su_log > 0) { 2399 mutex_exit(asy->asy_excl_hi); 2400 mutex_exit(asy->asy_excl); 2401 cmn_err(CE_NOTE, "su%d: ring buffer overflow\n", 2402 UNIT(async->async_dev)); 2403 mutex_enter(asy->asy_excl); 2404 mutex_enter(asy->asy_excl_hi); 2405 } 2406 INC64_KSTAT(asy, ringover); 2407 } 2408 async->async_sw_overrun = 0; 2409 } 2410 asy->asy_flags &= ~ASY_DOINGSOFT; 2411 mutex_exit(asy->asy_excl_hi); 2412 mutex_exit(asy->asy_excl); 2413 if (q != NULL) 2414 leaveq(q); 2415 return (0); 2416 } 2417 2418 /* 2419 * Restart output on a line after a delay or break timer expired. 2420 */ 2421 static void 2422 async_restart(void *arg) 2423 { 2424 struct asyncline *async = arg; 2425 struct asycom *asy = async->async_common; 2426 queue_t *q; 2427 uchar_t lcr; 2428 2429 /* 2430 * If break timer expired, turn off the break bit. 2431 */ 2432 #ifdef DEBUG 2433 if (asydebug & ASY_DEBUG_PROCS) 2434 printf("restart\n"); 2435 #endif 2436 mutex_enter(asy->asy_excl); 2437 if (async->async_flags & ASYNC_BREAK) { 2438 unsigned int rate; 2439 2440 mutex_enter(asy->asy_excl_hi); 2441 lcr = INB(LCR); 2442 OUTB(LCR, (lcr & ~SETBREAK)); 2443 2444 /* 2445 * Go to sleep for the time it takes for at least one 2446 * stop bit to be received by the device at the other 2447 * end of the line as stated in the RS-232 specification. 2448 * The wait period is equal to: 2449 * 2 clock cycles * (1 MICROSEC / baud rate) 2450 */ 2451 rate = async->async_ttycommon.t_cflag & CBAUD; 2452 if (async->async_ttycommon.t_cflag & CBAUDEXT) 2453 rate += 16; 2454 if (rate >= N_SU_SPEEDS || rate == B0) { 2455 rate = B9600; 2456 } 2457 2458 mutex_exit(asy->asy_excl_hi); 2459 mutex_exit(asy->asy_excl); 2460 drv_usecwait(2 * MICROSEC / baudtable[rate]); 2461 mutex_enter(asy->asy_excl); 2462 } 2463 async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING); 2464 if ((q = async->async_ttycommon.t_writeq) != NULL) { 2465 mutex_exit(asy->asy_excl); 2466 enterq(q); 2467 mutex_enter(asy->asy_excl); 2468 } 2469 async_start(async); 2470 mutex_exit(asy->asy_excl); 2471 if (q != NULL) 2472 leaveq(q); 2473 2474 /* cleared break or delay flag; may have made some output progress */ 2475 cv_broadcast(&async->async_flags_cv); 2476 } 2477 2478 static void 2479 async_start(struct asyncline *async) 2480 { 2481 async_nstart(async, 0); 2482 } 2483 2484 /* 2485 * Start output on a line, unless it's busy, frozen, or otherwise. 2486 */ 2487 static void 2488 async_nstart(struct asyncline *async, int mode) 2489 { 2490 register struct asycom *asy = async->async_common; 2491 register int cc; 2492 register queue_t *q; 2493 mblk_t *bp, *nbp; 2494 uchar_t *xmit_addr; 2495 uchar_t val; 2496 int fifo_len = 1; 2497 int xmit_progress; 2498 2499 #ifdef DEBUG 2500 if (asydebug & ASY_DEBUG_PROCS) 2501 printf("start\n"); 2502 #endif 2503 if (asy->asy_use_fifo == FIFO_ON) 2504 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 2505 2506 ASSERT(mutex_owned(asy->asy_excl)); 2507 mutex_enter(asy->asy_excl_hi); 2508 asycheckflowcontrol_hw(asy); 2509 2510 /* 2511 * If the chip is busy (i.e., we're waiting for a break timeout 2512 * to expire, or for the current transmission to finish, or for 2513 * output to finish draining from chip), don't grab anything new. 2514 */ 2515 if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) { 2516 mutex_exit(asy->asy_excl_hi); 2517 #ifdef DEBUG 2518 if (mode && asydebug & ASY_DEBUG_CLOSE) 2519 printf("asy%d: start %s.\n", 2520 UNIT(async->async_dev), 2521 async->async_flags & ASYNC_BREAK 2522 ? "break" : "busy"); 2523 #endif 2524 return; 2525 } 2526 2527 /* 2528 * If we have a flow-control character to transmit, do it now. 2529 */ 2530 if (asycheckflowcontrol_sw(asy)) { 2531 mutex_exit(asy->asy_excl_hi); 2532 return; 2533 } 2534 mutex_exit(asy->asy_excl_hi); 2535 /* 2536 * If we're waiting for a delay timeout to expire, don't grab 2537 * anything new. 2538 */ 2539 if (async->async_flags & ASYNC_DELAY) { 2540 #ifdef DEBUG 2541 if (mode && asydebug & ASY_DEBUG_CLOSE) 2542 printf("asy%d: start ASYNC_DELAY.\n", 2543 UNIT(async->async_dev)); 2544 #endif 2545 return; 2546 } 2547 2548 if ((q = async->async_ttycommon.t_writeq) == NULL) { 2549 #ifdef DEBUG 2550 if (mode && asydebug & ASY_DEBUG_CLOSE) 2551 printf("asy%d: start writeq is null.\n", 2552 UNIT(async->async_dev)); 2553 #endif 2554 return; /* not attached to a stream */ 2555 } 2556 2557 for (;;) { 2558 if ((bp = getq(q)) == NULL) 2559 return; /* no data to transmit */ 2560 2561 /* 2562 * We have a message block to work on. 2563 * Check whether it's a break, a delay, or an ioctl (the latter 2564 * occurs if the ioctl in question was waiting for the output 2565 * to drain). If it's one of those, process it immediately. 2566 */ 2567 switch (bp->b_datap->db_type) { 2568 2569 case M_BREAK: 2570 /* 2571 * Set the break bit, and arrange for "async_restart" 2572 * to be called in 1/4 second; it will turn the 2573 * break bit off, and call "async_start" to grab 2574 * the next message. 2575 */ 2576 mutex_enter(asy->asy_excl_hi); 2577 val = INB(LCR); 2578 OUTB(LCR, (val | SETBREAK)); 2579 mutex_exit(asy->asy_excl_hi); 2580 async->async_flags |= ASYNC_BREAK; 2581 (void) timeout(async_restart, async, hz / 4); 2582 freemsg(bp); 2583 return; /* wait for this to finish */ 2584 2585 case M_DELAY: 2586 /* 2587 * Arrange for "async_restart" to be called when the 2588 * delay expires; it will turn ASYNC_DELAY off, 2589 * and call "async_start" to grab the next message. 2590 */ 2591 (void) timeout(async_restart, async, 2592 (clock_t)(*(unsigned char *)bp->b_rptr + 6)); 2593 async->async_flags |= ASYNC_DELAY; 2594 freemsg(bp); 2595 return; /* wait for this to finish */ 2596 2597 case M_IOCTL: 2598 /* 2599 * This ioctl needs to wait for the output ahead of 2600 * it to drain. Try to do it, and then either 2601 * redo the ioctl at a later time or grab the next 2602 * message after it. 2603 */ 2604 2605 mutex_enter(asy->asy_excl_hi); 2606 if (asy_isbusy(asy)) { 2607 /* 2608 * Get the divisor by calculating the rate 2609 */ 2610 unsigned int rate; 2611 2612 mutex_exit(asy->asy_excl_hi); 2613 rate = async->async_ttycommon.t_cflag & CBAUD; 2614 if (async->async_ttycommon.t_cflag & CBAUDEXT) 2615 rate += 16; 2616 if (rate >= N_SU_SPEEDS || rate == B0) { 2617 rate = B9600; 2618 } 2619 2620 /* 2621 * We need to do a callback as the port will 2622 * be set to drain 2623 */ 2624 async->async_flags |= ASYNC_DRAINING; 2625 2626 /* 2627 * Put the message we just processed back onto 2628 * the end of the queue 2629 */ 2630 if (putq(q, bp) == 0) 2631 freemsg(bp); 2632 2633 /* 2634 * We need to delay until the TSR and THR 2635 * have been exhausted. We base the delay on 2636 * the amount of time it takes to transmit 2637 * 2 chars at the current baud rate in 2638 * microseconds. 2639 * 2640 * Therefore, the wait period is: 2641 * 2642 * (#TSR bits + #THR bits) * 2643 * 1 MICROSEC / baud rate 2644 */ 2645 (void) timeout(async_restart, async, 2646 drv_usectohz(16 * MICROSEC / 2647 baudtable[rate])); 2648 return; 2649 } 2650 mutex_exit(asy->asy_excl_hi); 2651 mutex_exit(asy->asy_excl); 2652 async_ioctl(async, q, bp, B_FALSE); 2653 mutex_enter(asy->asy_excl); 2654 continue; 2655 } 2656 2657 while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) { 2658 nbp = bp->b_cont; 2659 freeb(bp); 2660 bp = nbp; 2661 } 2662 if (bp != NULL) 2663 break; 2664 } 2665 2666 /* 2667 * We have data to transmit. If output is stopped, put 2668 * it back and try again later. 2669 */ 2670 if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) { 2671 #ifdef DEBUG 2672 if (asydebug & ASY_DEBUG_HFLOW && 2673 async->async_flags & ASYNC_HW_OUT_FLW) 2674 printf("asy%d: output hflow in effect.\n", 2675 UNIT(async->async_dev)); 2676 #endif 2677 mutex_exit(asy->asy_excl); 2678 (void) putbq(q, bp); 2679 /* 2680 * We entered the routine owning the lock, we need to 2681 * exit the routine owning the lock. 2682 */ 2683 mutex_enter(asy->asy_excl); 2684 return; 2685 } 2686 2687 async->async_xmitblk = bp; 2688 xmit_addr = bp->b_rptr; 2689 bp = bp->b_cont; 2690 if (bp != NULL) { 2691 mutex_exit(asy->asy_excl); 2692 (void) putbq(q, bp); /* not done with this message yet */ 2693 mutex_enter(asy->asy_excl); 2694 } 2695 2696 /* 2697 * In 5-bit mode, the high order bits are used 2698 * to indicate character sizes less than five, 2699 * so we need to explicitly mask before transmitting 2700 */ 2701 if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { 2702 register unsigned char *p = xmit_addr; 2703 register int cnt = cc; 2704 2705 while (cnt--) 2706 *p++ &= (unsigned char) 0x1f; 2707 } 2708 2709 /* 2710 * Set up this block for pseudo-DMA. 2711 */ 2712 mutex_enter(asy->asy_excl_hi); 2713 async->async_optr = xmit_addr; 2714 async->async_ocnt = cc; 2715 /* 2716 * If the transmitter is ready, shove some 2717 * characters out. 2718 */ 2719 xmit_progress = 0; 2720 while (fifo_len-- && async->async_ocnt) { 2721 if (INB(LSR) & XHRE) { 2722 OUTB(DAT, *async->async_optr++); 2723 async->async_ocnt--; 2724 xmit_progress++; 2725 } 2726 } 2727 asy->asy_out_of_band_xmit = xmit_progress; 2728 if (xmit_progress > 0) 2729 async->async_flags |= ASYNC_PROGRESS; 2730 async->async_flags |= ASYNC_BUSY; 2731 mutex_exit(asy->asy_excl_hi); 2732 } 2733 2734 /* 2735 * Resume output by poking the transmitter. 2736 */ 2737 static void 2738 async_resume(struct asyncline *async) 2739 { 2740 register struct asycom *asy = async->async_common; 2741 2742 ASSERT(mutex_owned(asy->asy_excl_hi)); 2743 #ifdef DEBUG 2744 if (asydebug & ASY_DEBUG_PROCS) 2745 printf("resume\n"); 2746 #endif 2747 2748 asycheckflowcontrol_hw(asy); 2749 2750 if (INB(LSR) & XHRE) { 2751 if (asycheckflowcontrol_sw(asy)) { 2752 return; 2753 } else if (async->async_ocnt > 0) { 2754 OUTB(DAT, *async->async_optr++); 2755 async->async_ocnt--; 2756 async->async_flags |= ASYNC_PROGRESS; 2757 } 2758 } 2759 } 2760 2761 /* 2762 * Process an "ioctl" message sent down to us. 2763 * Note that we don't need to get any locks until we are ready to access 2764 * the hardware. Nothing we access until then is going to be altered 2765 * outside of the STREAMS framework, so we should be safe. 2766 */ 2767 static void 2768 async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput) 2769 { 2770 register struct asycom *asy = async->async_common; 2771 register tty_common_t *tp = &async->async_ttycommon; 2772 register struct iocblk *iocp; 2773 register unsigned datasize; 2774 size_t ioc_count; 2775 mblk_t *datamp; 2776 int error = 0; 2777 uchar_t val, icr; 2778 #ifdef DEBUG 2779 if (asydebug & ASY_DEBUG_PROCS) 2780 printf("ioctl\n"); 2781 #endif 2782 2783 if (tp->t_iocpending != NULL) { 2784 /* 2785 * We were holding an "ioctl" response pending the 2786 * availability of an "mblk" to hold data to be passed up; 2787 * another "ioctl" came through, which means that "ioctl" 2788 * must have timed out or been aborted. 2789 */ 2790 freemsg(async->async_ttycommon.t_iocpending); 2791 async->async_ttycommon.t_iocpending = NULL; 2792 } 2793 2794 iocp = (struct iocblk *)mp->b_rptr; 2795 2796 /* 2797 * Save off the ioc count in case we need to restore it 2798 * because we are queuing a message block. 2799 */ 2800 ioc_count = iocp->ioc_count; 2801 2802 /* 2803 * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call 2804 * ttycommon_ioctl() because this function frees up the message block 2805 * (mp->b_cont) that contains the address of the user variable where 2806 * we need to pass back the bit array. 2807 * 2808 * Similarly, ttycommon_ioctl() does not know about CONSOPENPOLLEDIO 2809 * and CONSCLOSEPOLLEDIO, so don't let ttycommon_ioctl() touch them. 2810 */ 2811 if (iocp->ioc_cmd == TIOCMGET || 2812 iocp->ioc_cmd == TIOCMBIC || 2813 iocp->ioc_cmd == TIOCMBIS || 2814 iocp->ioc_cmd == TIOCMSET || 2815 iocp->ioc_cmd == TIOCGPPS || 2816 iocp->ioc_cmd == TIOCSPPS || 2817 iocp->ioc_cmd == TIOCGPPSEV || 2818 iocp->ioc_cmd == CONSOPENPOLLEDIO || 2819 iocp->ioc_cmd == CONSCLOSEPOLLEDIO) 2820 error = -1; /* Do Nothing */ 2821 else 2822 2823 /* 2824 * The only way in which "ttycommon_ioctl" can fail is if the "ioctl" 2825 * requires a response containing data to be returned to the user, 2826 * and no mblk could be allocated for the data. 2827 * No such "ioctl" alters our state. Thus, we always go ahead and 2828 * do any state-changes the "ioctl" calls for. If we couldn't allocate 2829 * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so 2830 * we just call "bufcall" to request that we be called back when we 2831 * stand a better chance of allocating the data. 2832 */ 2833 if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { 2834 if (async->async_wbufcid) 2835 unbufcall(async->async_wbufcid); 2836 async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl, 2837 async); 2838 return; 2839 } 2840 2841 mutex_enter(asy->asy_excl); 2842 2843 if (error == 0) { 2844 /* 2845 * "ttycommon_ioctl" did most of the work; we just use the 2846 * data it set up. 2847 */ 2848 switch (iocp->ioc_cmd) { 2849 2850 case TCSETS: 2851 if (!(asy->asy_rsc_console || asy->asy_rsc_control || 2852 asy->asy_lom_console)) { 2853 mutex_enter(asy->asy_excl_hi); 2854 error = asy_program(asy, ASY_NOINIT); 2855 mutex_exit(asy->asy_excl_hi); 2856 } 2857 break; 2858 case TCSETSF: 2859 case TCSETSW: 2860 case TCSETA: 2861 case TCSETAW: 2862 case TCSETAF: 2863 if (!(asy->asy_rsc_console || asy->asy_rsc_control || 2864 asy->asy_lom_console)) { 2865 mutex_enter(asy->asy_excl_hi); 2866 if (iswput && asy_isbusy(asy)) { 2867 /* 2868 * ttycommon_ioctl sets the db_type to 2869 * M_IOCACK and ioc_count to zero 2870 * we need to undo this when we 2871 * queue a control message. This will 2872 * allow the control messages to be 2873 * processed again when the chip 2874 * becomes available. 2875 */ 2876 mp->b_datap->db_type = M_IOCTL; 2877 iocp->ioc_count = ioc_count; 2878 2879 if (putq(wq, mp) == 0) 2880 freemsg(mp); 2881 mutex_exit(asy->asy_excl_hi); 2882 mutex_exit(asy->asy_excl); 2883 return; 2884 } 2885 2886 /* 2887 * TCSETA, TCSETAW, and TCSETAF make use of 2888 * the termio structure and therefore have 2889 * no concept of any speed except what can 2890 * be represented by CBAUD. This is because 2891 * of legacy SVR4 code. Therefore, if we see 2892 * one of the aforementioned IOCTL commands 2893 * we should zero out CBAUDEXT, CIBAUD, and 2894 * CIBAUDEXT as to not break legacy 2895 * functionality. This is because CBAUDEXT, 2896 * CIBAUD, and CIBAUDEXT can't be stored in 2897 * an unsigned short. By zeroing out CBAUDEXT, 2898 * CIBAUD, and CIBAUDEXT in the t_cflag of the 2899 * termios structure asy_program() will set the 2900 * input baud rate to the output baud rate. 2901 */ 2902 if (iocp->ioc_cmd == TCSETA || 2903 iocp->ioc_cmd == TCSETAW || 2904 iocp->ioc_cmd == TCSETAF) 2905 tp->t_cflag &= ~(CIBAUD | 2906 CIBAUDEXT | CBAUDEXT); 2907 2908 error = asy_program(asy, ASY_NOINIT); 2909 mutex_exit(asy->asy_excl_hi); 2910 } 2911 break; 2912 case TIOCSSOFTCAR: 2913 /* Set the driver state appropriately */ 2914 mutex_enter(asy->asy_excl_hi); 2915 if (tp->t_flags & TS_SOFTCAR) 2916 asy->asy_flags |= ASY_IGNORE_CD; 2917 else 2918 asy->asy_flags &= ~ASY_IGNORE_CD; 2919 mutex_exit(asy->asy_excl_hi); 2920 break; 2921 } 2922 } else if (error < 0) { 2923 /* 2924 * "ttycommon_ioctl" didn't do anything; we process it here. 2925 */ 2926 error = 0; 2927 switch (iocp->ioc_cmd) { 2928 2929 case TIOCGPPS: 2930 /* 2931 * Get PPS on/off. 2932 */ 2933 if (mp->b_cont != NULL) 2934 freemsg(mp->b_cont); 2935 2936 mp->b_cont = allocb(sizeof (int), BPRI_HI); 2937 if (mp->b_cont == NULL) { 2938 error = ENOMEM; 2939 break; 2940 } 2941 if (asy->asy_flags & ASY_PPS) 2942 *(int *)mp->b_cont->b_wptr = 1; 2943 else 2944 *(int *)mp->b_cont->b_wptr = 0; 2945 mp->b_cont->b_wptr += sizeof (int); 2946 mp->b_datap->db_type = M_IOCACK; 2947 iocp->ioc_count = sizeof (int); 2948 break; 2949 2950 case TIOCSPPS: 2951 /* 2952 * Set PPS on/off. 2953 */ 2954 error = miocpullup(mp, sizeof (int)); 2955 if (error != 0) 2956 break; 2957 2958 mutex_enter(asy->asy_excl_hi); 2959 if (*(int *)mp->b_cont->b_rptr) 2960 asy->asy_flags |= ASY_PPS; 2961 else 2962 asy->asy_flags &= ~ASY_PPS; 2963 /* Reset edge sense */ 2964 asy->asy_flags &= ~ASY_PPS_EDGE; 2965 mutex_exit(asy->asy_excl_hi); 2966 mp->b_datap->db_type = M_IOCACK; 2967 break; 2968 2969 case TIOCGPPSEV: { 2970 /* 2971 * Get PPS event data. 2972 */ 2973 mblk_t *bp; 2974 void *buf; 2975 #ifdef _SYSCALL32_IMPL 2976 struct ppsclockev32 p32; 2977 #endif 2978 struct ppsclockev ppsclockev; 2979 2980 if (mp->b_cont != NULL) { 2981 freemsg(mp->b_cont); 2982 mp->b_cont = NULL; 2983 } 2984 2985 if ((asy->asy_flags & ASY_PPS) == 0) { 2986 error = ENXIO; 2987 break; 2988 } 2989 2990 /* Protect from incomplete asy_ppsev */ 2991 mutex_enter(asy->asy_excl_hi); 2992 ppsclockev = asy_ppsev; 2993 mutex_exit(asy->asy_excl_hi); 2994 2995 #ifdef _SYSCALL32_IMPL 2996 if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { 2997 TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); 2998 p32.serial = ppsclockev.serial; 2999 buf = &p32; 3000 iocp->ioc_count = sizeof (struct ppsclockev32); 3001 } else 3002 #endif 3003 { 3004 buf = &ppsclockev; 3005 iocp->ioc_count = sizeof (struct ppsclockev); 3006 } 3007 3008 if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { 3009 error = ENOMEM; 3010 break; 3011 } 3012 mp->b_cont = bp; 3013 3014 bcopy(buf, bp->b_wptr, iocp->ioc_count); 3015 bp->b_wptr += iocp->ioc_count; 3016 mp->b_datap->db_type = M_IOCACK; 3017 break; 3018 } 3019 3020 case TCSBRK: 3021 error = miocpullup(mp, sizeof (int)); 3022 if (error != 0) 3023 break; 3024 3025 mutex_enter(asy->asy_excl_hi); 3026 if (*(int *)mp->b_cont->b_rptr == 0) { 3027 /* 3028 * Get the divisor by calculating the rate 3029 */ 3030 unsigned int rate, divisor; 3031 rate = async->async_ttycommon.t_cflag & CBAUD; 3032 if (async->async_ttycommon.t_cflag & CBAUDEXT) 3033 rate += 16; 3034 if (rate >= N_SU_SPEEDS) rate = B9600; 3035 divisor = asyspdtab[rate] & 0xfff; 3036 3037 /* 3038 * To ensure that erroneous characters are 3039 * not sent out when the break is set, SB 3040 * recommends three steps: 3041 * 3042 * 1) pad the TSR with 0 bits 3043 * 2) When the TSR is full, set break 3044 * 3) When the TSR has been flushed, unset 3045 * the break when transmission must be 3046 * restored. 3047 * 3048 * We loop until the TSR is empty and then 3049 * set the break. ASYNC_BREAK has been set 3050 * to ensure that no characters are 3051 * transmitted while the TSR is being 3052 * flushed and SOUT is being used for the 3053 * break signal. 3054 * 3055 * The wait period is equal to 3056 * clock / (baud * 16) * 16 * 2. 3057 */ 3058 async->async_flags |= ASYNC_BREAK; 3059 while ((INB(LSR) & XSRE) == 0) { 3060 mutex_exit(asy->asy_excl_hi); 3061 mutex_exit(asy->asy_excl); 3062 drv_usecwait(32*divisor); 3063 mutex_enter(asy->asy_excl); 3064 mutex_enter(asy->asy_excl_hi); 3065 } 3066 3067 /* 3068 * Set the break bit, and arrange for 3069 * "async_restart" to be called in 1/4 second; 3070 * it will turn the break bit off, and call 3071 * "async_start" to grab the next message. 3072 */ 3073 val = INB(LCR); 3074 OUTB(LCR, (val | SETBREAK)); 3075 mutex_exit(asy->asy_excl_hi); 3076 (void) timeout(async_restart, async, hz / 4); 3077 } else { 3078 #ifdef DEBUG 3079 if (asydebug & ASY_DEBUG_CLOSE) 3080 printf("asy%d: wait for flush.\n", 3081 UNIT(async->async_dev)); 3082 #endif 3083 if (iswput && asy_isbusy(asy)) { 3084 if (putq(wq, mp) == 0) 3085 freemsg(mp); 3086 mutex_exit(asy->asy_excl_hi); 3087 mutex_exit(asy->asy_excl); 3088 return; 3089 } 3090 mutex_exit(asy->asy_excl_hi); 3091 #ifdef DEBUG 3092 if (asydebug & ASY_DEBUG_CLOSE) 3093 printf("asy%d: ldterm satisfied.\n", 3094 UNIT(async->async_dev)); 3095 #endif 3096 } 3097 break; 3098 3099 case TIOCSBRK: 3100 mutex_enter(asy->asy_excl_hi); 3101 val = INB(LCR); 3102 OUTB(LCR, (val | SETBREAK)); 3103 mutex_exit(asy->asy_excl_hi); 3104 mutex_exit(asy->asy_excl); 3105 miocack(wq, mp, 0, 0); 3106 return; 3107 3108 case TIOCCBRK: 3109 mutex_enter(asy->asy_excl_hi); 3110 val = INB(LCR); 3111 OUTB(LCR, (val & ~SETBREAK)); 3112 mutex_exit(asy->asy_excl_hi); 3113 mutex_exit(asy->asy_excl); 3114 miocack(wq, mp, 0, 0); 3115 return; 3116 3117 case TIOCMSET: 3118 case TIOCMBIS: 3119 case TIOCMBIC: 3120 if (iocp->ioc_count == TRANSPARENT) 3121 mcopyin(mp, NULL, sizeof (int), NULL); 3122 else { 3123 error = miocpullup(mp, sizeof (int)); 3124 if (error != 0) 3125 break; 3126 3127 mutex_enter(asy->asy_excl_hi); 3128 3129 (void) asymctl(asy, 3130 dmtoasy(*(int *)mp->b_cont->b_rptr), 3131 iocp->ioc_cmd); 3132 3133 mutex_exit(asy->asy_excl_hi); 3134 iocp->ioc_error = 0; 3135 mp->b_datap->db_type = M_IOCACK; 3136 } 3137 break; 3138 3139 case TIOCSILOOP: 3140 mutex_enter(asy->asy_excl_hi); 3141 /* 3142 * If somebody misues this Ioctl when used for 3143 * driving keyboard and mouse indicate not supported 3144 */ 3145 if ((asy->asy_device_type == ASY_KEYBOARD) || 3146 (asy->asy_device_type == ASY_MOUSE)) { 3147 mutex_exit(asy->asy_excl_hi); 3148 error = ENOTTY; 3149 break; 3150 } 3151 3152 /* should not use when we're the console */ 3153 if ((async->async_dev == kbddev) || 3154 (async->async_dev == rconsdev) || 3155 (async->async_dev == stdindev)) { 3156 mutex_exit(asy->asy_excl_hi); 3157 error = EINVAL; 3158 break; 3159 } 3160 3161 val = INB(MCR); 3162 icr = INB(ICR); 3163 /* 3164 * Disable the Modem Status Interrupt 3165 * The reason for disabling is the status of 3166 * modem signal are in the higher 4 bits instead of 3167 * lower four bits when in loopback mode, 3168 * so, donot worry about Modem interrupt when 3169 * you are planning to set 3170 * this in loopback mode until it is cleared by 3171 * another ioctl to get out of the loopback mode 3172 */ 3173 OUTB(ICR, icr & ~ MIEN); 3174 OUTB(MCR, val | ASY_LOOP); 3175 mutex_exit(asy->asy_excl_hi); 3176 iocp->ioc_error = 0; 3177 mp->b_datap->db_type = M_IOCACK; 3178 break; 3179 3180 case TIOCMGET: 3181 datamp = allocb(sizeof (int), BPRI_MED); 3182 if (datamp == NULL) { 3183 error = EAGAIN; 3184 break; 3185 } 3186 3187 mutex_enter(asy->asy_excl_hi); 3188 *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); 3189 mutex_exit(asy->asy_excl_hi); 3190 3191 if (iocp->ioc_count == TRANSPARENT) { 3192 mcopyout(mp, NULL, sizeof (int), NULL, datamp); 3193 } else { 3194 if (mp->b_cont != NULL) 3195 freemsg(mp->b_cont); 3196 mp->b_cont = datamp; 3197 mp->b_cont->b_wptr += sizeof (int); 3198 mp->b_datap->db_type = M_IOCACK; 3199 iocp->ioc_count = sizeof (int); 3200 } 3201 break; 3202 3203 case CONSOPENPOLLEDIO: 3204 /* 3205 * If we are driving a keyboard there is nothing 3206 * upstream to translate the scan codes. Therefore, 3207 * set the error code to ENOTSUP and NAK the request 3208 */ 3209 if (asy->asy_device_type == ASY_KEYBOARD) { 3210 error = ENOTSUP; 3211 break; 3212 } 3213 3214 error = miocpullup(mp, sizeof (struct cons_polledio *)); 3215 if (error != 0) 3216 break; 3217 3218 /* 3219 * send up a message block containing the 3220 * cons_polledio structure. This provides 3221 * handles to the putchar, getchar, ischar, 3222 * polledio_enter and polledio_exit functions. 3223 */ 3224 *(struct cons_polledio **)mp->b_cont->b_rptr = 3225 &asy->polledio; 3226 3227 mp->b_datap->db_type = M_IOCACK; 3228 break; 3229 3230 case CONSCLOSEPOLLEDIO: 3231 /* 3232 * If we are driving a keyboard we never successfully 3233 * called CONSOPENPOLLEDIO so set the error to 3234 * ENOTSUP and NAK the request. 3235 */ 3236 if (asy->asy_device_type == ASY_KEYBOARD) { 3237 error = ENOTSUP; 3238 break; 3239 } 3240 3241 mp->b_datap->db_type = M_IOCACK; 3242 iocp->ioc_error = 0; 3243 iocp->ioc_rval = 0; 3244 break; 3245 3246 default: /* unexpected ioctl type */ 3247 /* 3248 * If we don't understand it, it's an error. NAK it. 3249 */ 3250 error = EINVAL; 3251 break; 3252 } 3253 } 3254 if (error != 0) { 3255 iocp->ioc_error = error; 3256 mp->b_datap->db_type = M_IOCNAK; 3257 } 3258 mutex_exit(asy->asy_excl); 3259 qreply(wq, mp); 3260 } 3261 3262 static void 3263 asyrsrv(queue_t *q) 3264 { 3265 mblk_t *bp; 3266 struct asyncline *async; 3267 3268 async = (struct asyncline *)q->q_ptr; 3269 3270 while (canputnext(q) && (bp = getq(q))) 3271 putnext(q, bp); 3272 ASYSETSOFT(async->async_common); 3273 async->async_polltid = 0; 3274 } 3275 3276 /* 3277 * Put procedure for write queue. 3278 * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; 3279 * set the flow control character for M_STOPI and M_STARTI messages; 3280 * queue up M_BREAK, M_DELAY, and M_DATA messages for processing 3281 * by the start routine, and then call the start routine; discard 3282 * everything else. Note that this driver does not incorporate any 3283 * mechanism to negotiate to handle the canonicalization process. 3284 * It expects that these functions are handled in upper module(s), 3285 * as we do in ldterm. 3286 */ 3287 static void 3288 asywput(queue_t *q, mblk_t *mp) 3289 { 3290 register struct asyncline *async; 3291 register struct asycom *asy; 3292 int error; 3293 3294 async = (struct asyncline *)q->q_ptr; 3295 asy = async->async_common; 3296 3297 switch (mp->b_datap->db_type) { 3298 3299 case M_STOP: 3300 /* 3301 * Since we don't do real DMA, we can just let the 3302 * chip coast to a stop after applying the brakes. 3303 */ 3304 mutex_enter(asy->asy_excl); 3305 async->async_flags |= ASYNC_STOPPED; 3306 mutex_exit(asy->asy_excl); 3307 freemsg(mp); 3308 break; 3309 3310 case M_START: 3311 mutex_enter(asy->asy_excl); 3312 if (async->async_flags & ASYNC_STOPPED) { 3313 async->async_flags &= ~ASYNC_STOPPED; 3314 /* 3315 * If an output operation is in progress, 3316 * resume it. Otherwise, prod the start 3317 * routine. 3318 */ 3319 if (async->async_ocnt > 0) { 3320 mutex_enter(asy->asy_excl_hi); 3321 async_resume(async); 3322 mutex_exit(asy->asy_excl_hi); 3323 } else { 3324 async_start(async); 3325 } 3326 } 3327 mutex_exit(asy->asy_excl); 3328 freemsg(mp); 3329 break; 3330 3331 case M_IOCTL: 3332 switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 3333 3334 case TCSBRK: 3335 error = miocpullup(mp, sizeof (int)); 3336 if (error != 0) { 3337 miocnak(q, mp, 0, error); 3338 return; 3339 } 3340 3341 if (*(int *)mp->b_cont->b_rptr != 0) { 3342 #ifdef DEBUG 3343 if (asydebug & ASY_DEBUG_CLOSE) 3344 printf("asy%d: flush request.\n", 3345 UNIT(async->async_dev)); 3346 #endif 3347 (void) putq(q, mp); 3348 mutex_enter(asy->asy_excl); 3349 async_nstart(async, 1); 3350 mutex_exit(asy->asy_excl); 3351 break; 3352 } 3353 /*FALLTHROUGH*/ 3354 case TCSETSW: 3355 case TCSETSF: 3356 case TCSETAW: 3357 case TCSETAF: 3358 /* 3359 * The changes do not take effect until all 3360 * output queued before them is drained. 3361 * Put this message on the queue, so that 3362 * "async_start" will see it when it's done 3363 * with the output before it. Poke the 3364 * start routine, just in case. 3365 */ 3366 (void) putq(q, mp); 3367 mutex_enter(asy->asy_excl); 3368 async_start(async); 3369 mutex_exit(asy->asy_excl); 3370 break; 3371 3372 default: 3373 /* 3374 * Do it now. 3375 */ 3376 async_ioctl(async, q, mp, B_TRUE); 3377 break; 3378 } 3379 break; 3380 3381 case M_FLUSH: 3382 if (*mp->b_rptr & FLUSHW) { 3383 mutex_enter(asy->asy_excl); 3384 3385 /* 3386 * Abort any output in progress. 3387 */ 3388 mutex_enter(asy->asy_excl_hi); 3389 if (async->async_flags & ASYNC_BUSY) { 3390 async->async_ocnt = 0; 3391 async->async_flags &= ~ASYNC_BUSY; 3392 } 3393 mutex_exit(asy->asy_excl_hi); 3394 3395 /* Flush FIFO buffers */ 3396 if (asy->asy_use_fifo == FIFO_ON) { 3397 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | 3398 (asy->asy_trig_level & 0xff)); 3399 } 3400 3401 /* 3402 * Flush our write queue. 3403 */ 3404 flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ 3405 if (async->async_xmitblk != NULL) { 3406 freeb(async->async_xmitblk); 3407 async->async_xmitblk = NULL; 3408 } 3409 3410 mutex_exit(asy->asy_excl); 3411 *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ 3412 } 3413 if (*mp->b_rptr & FLUSHR) { 3414 /* Flush FIFO buffers */ 3415 if (asy->asy_use_fifo == FIFO_ON) { 3416 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | 3417 (asy->asy_trig_level & 0xff)); 3418 } 3419 flushq(RD(q), FLUSHDATA); 3420 qreply(q, mp); /* give the read queues a crack at it */ 3421 } else { 3422 freemsg(mp); 3423 } 3424 3425 /* 3426 * We must make sure we process messages that survive the 3427 * write-side flush. Without this call, the close protocol 3428 * with ldterm can hang forever. (ldterm will have sent us a 3429 * TCSBRK ioctl that it expects a response to.) 3430 */ 3431 mutex_enter(asy->asy_excl); 3432 async_start(async); 3433 mutex_exit(asy->asy_excl); 3434 break; 3435 case M_BREAK: 3436 case M_DELAY: 3437 case M_DATA: 3438 /* 3439 * Queue the message up to be transmitted, 3440 * and poke the start routine. 3441 */ 3442 (void) putq(q, mp); 3443 mutex_enter(asy->asy_excl); 3444 async_start(async); 3445 mutex_exit(asy->asy_excl); 3446 break; 3447 3448 case M_STOPI: 3449 mutex_enter(asy->asy_excl); 3450 async->async_flowc = async->async_stopc; 3451 async_start(async); /* poke the start routine */ 3452 mutex_exit(asy->asy_excl); 3453 freemsg(mp); 3454 break; 3455 3456 case M_STARTI: 3457 mutex_enter(asy->asy_excl); 3458 async->async_flowc = async->async_startc; 3459 async_start(async); /* poke the start routine */ 3460 mutex_exit(asy->asy_excl); 3461 freemsg(mp); 3462 break; 3463 3464 case M_CTL: 3465 if (MBLKL(mp) >= sizeof (struct iocblk) && 3466 ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { 3467 ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX; 3468 qreply(q, mp); 3469 } else { 3470 /* 3471 * These MC_SERVICE type messages are used by upper 3472 * modules to tell this driver to send input up 3473 * immediately, or that it can wait for normal 3474 * processing that may or may not be done. Sun 3475 * requires these for the mouse module. 3476 * (XXX - for x86?) 3477 */ 3478 mutex_enter(asy->asy_excl); 3479 switch (*mp->b_rptr) { 3480 3481 case MC_SERVICEIMM: 3482 async->async_flags |= ASYNC_SERVICEIMM; 3483 break; 3484 3485 case MC_SERVICEDEF: 3486 async->async_flags &= ~ASYNC_SERVICEIMM; 3487 break; 3488 } 3489 mutex_exit(asy->asy_excl); 3490 freemsg(mp); 3491 } 3492 break; 3493 3494 case M_IOCDATA: 3495 async_iocdata(q, mp); 3496 break; 3497 3498 default: 3499 freemsg(mp); 3500 break; 3501 } 3502 } 3503 3504 /* 3505 * Retry an "ioctl", now that "bufcall" claims we may be able to allocate 3506 * the buffer we need. 3507 */ 3508 static void 3509 async_reioctl(void *arg) 3510 { 3511 struct asyncline *async = arg; 3512 struct asycom *asy = async->async_common; 3513 queue_t *q; 3514 mblk_t *mp; 3515 3516 /* 3517 * The bufcall is no longer pending. 3518 */ 3519 mutex_enter(asy->asy_excl); 3520 async->async_wbufcid = 0; 3521 if ((q = async->async_ttycommon.t_writeq) == NULL) { 3522 mutex_exit(asy->asy_excl); 3523 return; 3524 } 3525 if ((mp = async->async_ttycommon.t_iocpending) != NULL) { 3526 /* not pending any more */ 3527 async->async_ttycommon.t_iocpending = NULL; 3528 mutex_exit(asy->asy_excl); 3529 /* not in STREAMS queue; we no longer know if we're in wput */ 3530 async_ioctl(async, q, mp, B_TRUE); 3531 } else 3532 mutex_exit(asy->asy_excl); 3533 } 3534 3535 static void 3536 async_iocdata(queue_t *q, mblk_t *mp) 3537 { 3538 struct asyncline *async = (struct asyncline *)q->q_ptr; 3539 struct asycom *asy; 3540 struct copyresp *csp; 3541 3542 asy = async->async_common; 3543 csp = (struct copyresp *)mp->b_rptr; 3544 3545 if (csp->cp_rval != 0) { 3546 freemsg(mp); 3547 return; 3548 } 3549 3550 mutex_enter(asy->asy_excl); 3551 3552 switch (csp->cp_cmd) { 3553 case TIOCMSET: 3554 case TIOCMBIS: 3555 case TIOCMBIC: 3556 if (mp->b_cont == NULL) { 3557 mutex_exit(asy->asy_excl); 3558 miocnak(q, mp, 0, EINVAL); 3559 break; 3560 } 3561 3562 mutex_enter(asy->asy_excl_hi); 3563 (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr), 3564 csp->cp_cmd); 3565 mutex_exit(asy->asy_excl_hi); 3566 3567 freemsg(mp->b_cont); 3568 mp->b_cont = NULL; 3569 mutex_exit(asy->asy_excl); 3570 miocack(q, mp, 0, 0); 3571 break; 3572 3573 case TIOCMGET: 3574 if (mp->b_cont != NULL) { 3575 freemsg(mp->b_cont); 3576 mp->b_cont = NULL; 3577 } 3578 mutex_exit(asy->asy_excl); 3579 miocack(q, mp, 0, 0); 3580 break; 3581 3582 default: 3583 mutex_exit(asy->asy_excl); 3584 miocnak(q, mp, 0, EINVAL); 3585 break; 3586 } 3587 } 3588 3589 3590 /* 3591 * Set or get the modem control status. 3592 */ 3593 static int 3594 asymctl(struct asycom *asy, int bits, int how) 3595 { 3596 register int mcr_r, msr_r; 3597 3598 ASSERT(mutex_owned(asy->asy_excl_hi)); 3599 ASSERT(mutex_owned(asy->asy_excl)); 3600 3601 /* Read Modem Control Registers */ 3602 mcr_r = INB(MCR); 3603 3604 switch (how) { 3605 3606 case TIOCMSET: 3607 mcr_r = bits; 3608 break; 3609 3610 case TIOCMBIS: 3611 mcr_r |= bits; /* Set bits from input */ 3612 break; 3613 3614 case TIOCMBIC: 3615 mcr_r &= ~bits; /* Set ~bits from input */ 3616 break; 3617 3618 case TIOCMGET: 3619 /* Read Modem Status Registers */ 3620 if (INB(ICR) & MIEN) 3621 msr_r = asy->asy_cached_msr; 3622 else 3623 msr_r = INB(MSR); 3624 return (asytodm(mcr_r, msr_r)); 3625 } 3626 3627 OUTB(MCR, mcr_r); 3628 3629 return (mcr_r); 3630 } 3631 3632 static int 3633 asytodm(int mcr_r, int msr_r) 3634 { 3635 register int b = 0; 3636 3637 3638 /* MCR registers */ 3639 if (mcr_r & RTS) 3640 b |= TIOCM_RTS; 3641 3642 if (mcr_r & DTR) 3643 b |= TIOCM_DTR; 3644 3645 /* MSR registers */ 3646 if (msr_r & DCD) 3647 b |= TIOCM_CAR; 3648 3649 if (msr_r & CTS) 3650 b |= TIOCM_CTS; 3651 3652 if (msr_r & DSR) 3653 b |= TIOCM_DSR; 3654 3655 if (msr_r & RI) 3656 b |= TIOCM_RNG; 3657 3658 return (b); 3659 } 3660 3661 static int 3662 dmtoasy(int bits) 3663 { 3664 register int b = 0; 3665 3666 #ifdef CAN_NOT_SET /* only DTR and RTS can be set */ 3667 if (bits & TIOCM_CAR) 3668 b |= DCD; 3669 if (bits & TIOCM_CTS) 3670 b |= CTS; 3671 if (bits & TIOCM_DSR) 3672 b |= DSR; 3673 if (bits & TIOCM_RNG) 3674 b |= RI; 3675 #endif 3676 3677 if (bits & TIOCM_RTS) 3678 b |= RTS; 3679 if (bits & TIOCM_DTR) 3680 b |= DTR; 3681 3682 return (b); 3683 } 3684 3685 static void 3686 asycheckflowcontrol_hw(struct asycom *asy) 3687 { 3688 struct asyncline *async; 3689 uchar_t mcr, flag; 3690 3691 ASSERT(mutex_owned(asy->asy_excl_hi)); 3692 3693 async = (struct asyncline *)asy->asy_priv; 3694 ASSERT(async != NULL); 3695 3696 if (async->async_ttycommon.t_cflag & CRTSXOFF) { 3697 mcr = INB(MCR); 3698 flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS; 3699 if (((mcr ^ flag) & RTS) != 0) { 3700 OUTB(MCR, (mcr ^ RTS)); 3701 } 3702 } 3703 } 3704 3705 static boolean_t 3706 asycheckflowcontrol_sw(struct asycom *asy) 3707 { 3708 uchar_t ss; 3709 struct asyncline *async; 3710 int rval = B_FALSE; 3711 3712 ASSERT(mutex_owned(asy->asy_excl_hi)); 3713 3714 async = (struct asyncline *)asy->asy_priv; 3715 ASSERT(async != NULL); 3716 3717 if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) { 3718 /* 3719 * If we get this far, then we know that flowc is non-zero and 3720 * that there's transmit room available. We've "handled" the 3721 * request now, so clear it. If the user didn't ask for IXOFF, 3722 * then don't actually send anything, but wait for the next 3723 * opportunity. 3724 */ 3725 async->async_flowc = '\0'; 3726 if (async->async_ttycommon.t_iflag & IXOFF) { 3727 async->async_flags |= ASYNC_BUSY; 3728 OUTB(DAT, ss); 3729 rval = B_TRUE; 3730 } 3731 } 3732 3733 return (rval); 3734 } 3735 3736 /* 3737 * Check for abort character sequence 3738 */ 3739 static boolean_t 3740 abort_charseq_recognize(uchar_t ch) 3741 { 3742 static int state = 0; 3743 #define CNTRL(c) ((c)&037) 3744 static char sequence[] = { '\r', '~', CNTRL('b') }; 3745 3746 if (ch == sequence[state]) { 3747 if (++state >= sizeof (sequence)) { 3748 state = 0; 3749 return (B_TRUE); 3750 } 3751 } else { 3752 state = (ch == sequence[0]) ? 1 : 0; 3753 } 3754 return (B_FALSE); 3755 } 3756