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