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