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