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