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