1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 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 (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved. 28 * Copyright 2012 Milan Jurik. All rights reserved. 29 * Copyright (c) 2016 by Delphix. All rights reserved. 30 * Copyright 2023 Oxide Computer Company 31 * Copyright 2024 Hans Rosenfeld 32 */ 33 34 35 /* 36 * Serial I/O driver for 8250/16450/16550A/16650/16750/16950 chips. 37 */ 38 39 #include <sys/param.h> 40 #include <sys/types.h> 41 #include <sys/signal.h> 42 #include <sys/stream.h> 43 #include <sys/termio.h> 44 #include <sys/errno.h> 45 #include <sys/file.h> 46 #include <sys/cmn_err.h> 47 #include <sys/stropts.h> 48 #include <sys/strsubr.h> 49 #include <sys/strtty.h> 50 #include <sys/debug.h> 51 #include <sys/kbio.h> 52 #include <sys/cred.h> 53 #include <sys/stat.h> 54 #include <sys/consdev.h> 55 #include <sys/mkdev.h> 56 #include <sys/kmem.h> 57 #include <sys/cred.h> 58 #include <sys/strsun.h> 59 #ifdef DEBUG 60 #include <sys/promif.h> 61 #endif 62 #include <sys/modctl.h> 63 #include <sys/ddi.h> 64 #include <sys/sunddi.h> 65 #include <sys/pci.h> 66 #include <sys/asy.h> 67 #include <sys/policy.h> 68 #include <sys/sysmacros.h> 69 70 /* 71 * set the RX FIFO trigger_level to half the RX FIFO size for now 72 * we may want to make this configurable later. 73 */ 74 static int asy_trig_level = ASY_FCR_RHR_TRIG_8; 75 76 int asy_drain_check = 15000000; /* tunable: exit drain check time */ 77 int asy_min_dtr_low = 500000; /* tunable: minimum DTR down time */ 78 int asy_min_utbrk = 100000; /* tunable: minumum untimed brk time */ 79 80 int asymaxchip = ASY_MAXCHIP; /* tunable: limit chip support we look for */ 81 82 /* 83 * Just in case someone has a chip with broken loopback mode, we provide a 84 * means to disable the loopback test. By default, we only loopback test 85 * UARTs which look like they have FIFOs bigger than 16 bytes. 86 * Set to 0 to suppress test, or to 2 to enable test on any size FIFO. 87 */ 88 int asy_fifo_test = 1; /* tunable: set to 0, 1, or 2 */ 89 90 /* 91 * Allow ability to switch off testing of the scratch register. 92 * Some UART emulators might not have it. This will also disable the test 93 * for Exar/Startech ST16C650, as that requires use of the SCR register. 94 */ 95 int asy_scr_test = 1; /* tunable: set to 0 to disable SCR reg test */ 96 97 /* 98 * As we don't yet support on-chip flow control, it's a bad idea to put a 99 * large number of characters in the TX FIFO, since if other end tells us 100 * to stop transmitting, we can only stop filling the TX FIFO, but it will 101 * still carry on draining by itself, so remote end still gets what's left 102 * in the FIFO. 103 */ 104 int asy_max_tx_fifo = 16; /* tunable: max fill of TX FIFO */ 105 106 #define async_stopc async_ttycommon.t_stopc 107 #define async_startc async_ttycommon.t_startc 108 109 #define ASY_INIT 1 110 #define ASY_NOINIT 0 111 112 /* enum value for sw and hw flow control action */ 113 typedef enum { 114 FLOW_CHECK, 115 FLOW_STOP, 116 FLOW_START 117 } async_flowc_action; 118 119 #ifdef DEBUG 120 #define ASY_DEBUG_INIT 0x0001 /* Output msgs during driver initialization. */ 121 #define ASY_DEBUG_INPUT 0x0002 /* Report characters received during int. */ 122 #define ASY_DEBUG_EOT 0x0004 /* Output msgs when wait for xmit to finish. */ 123 #define ASY_DEBUG_CLOSE 0x0008 /* Output msgs when driver open/close called */ 124 #define ASY_DEBUG_HFLOW 0x0010 /* Output msgs when H/W flowcontrol is active */ 125 #define ASY_DEBUG_PROCS 0x0020 /* Output each proc name as it is entered. */ 126 #define ASY_DEBUG_STATE 0x0040 /* Output value of Interrupt Service Reg. */ 127 #define ASY_DEBUG_INTR 0x0080 /* Output value of Interrupt Service Reg. */ 128 #define ASY_DEBUG_OUT 0x0100 /* Output msgs about output events. */ 129 #define ASY_DEBUG_BUSY 0x0200 /* Output msgs when xmit is enabled/disabled */ 130 #define ASY_DEBUG_MODEM 0x0400 /* Output msgs about modem status & control. */ 131 #define ASY_DEBUG_MODM2 0x0800 /* Output msgs about modem status & control. */ 132 #define ASY_DEBUG_IOCTL 0x1000 /* Output msgs about ioctl messages. */ 133 #define ASY_DEBUG_CHIP 0x2000 /* Output msgs about chip identification. */ 134 #define ASY_DEBUG_SFLOW 0x4000 /* Output msgs when S/W flowcontrol is active */ 135 136 static int debug = 0; 137 138 #define ASY_DEBUG(asy, x) (asy->asy_debug & (x)) 139 #define ASY_DPRINTF(asy, fac, format, ...) \ 140 if (ASY_DEBUG(asy, fac)) \ 141 asyerror(asy, CE_CONT, "!%s: " format, __func__, ##__VA_ARGS__) 142 #else 143 #define ASY_DEBUG(asy, x) B_FALSE 144 #define ASY_DPRINTF(asy, fac, format, ...) 145 #endif 146 147 /* 148 * PPS (Pulse Per Second) support. 149 */ 150 void ddi_hardpps(struct timeval *, int); 151 /* 152 * This is protected by the asy_excl_hi of the port on which PPS event 153 * handling is enabled. Note that only one port should have this enabled at 154 * any one time. Enabling PPS handling on multiple ports will result in 155 * unpredictable (but benign) results. 156 */ 157 static struct ppsclockev asy_ppsev; 158 159 #ifdef PPSCLOCKLED 160 /* XXX Use these to observe PPS latencies and jitter on a scope */ 161 #define LED_ON 162 #define LED_OFF 163 #else 164 #define LED_ON 165 #define LED_OFF 166 #endif 167 168 static void asy_put_idx(const struct asycom *, asy_reg_t, uint8_t); 169 static uint8_t asy_get_idx(const struct asycom *, asy_reg_t); 170 171 static void asy_put_add(const struct asycom *, asy_reg_t, uint8_t); 172 static uint8_t asy_get_add(const struct asycom *, asy_reg_t); 173 174 static void asy_put_ext(const struct asycom *, asy_reg_t, uint8_t); 175 static uint8_t asy_get_ext(const struct asycom *, asy_reg_t); 176 177 static void asy_put_reg(const struct asycom *, asy_reg_t, uint8_t); 178 static uint8_t asy_get_reg(const struct asycom *, asy_reg_t); 179 180 static void asy_put(const struct asycom *, asy_reg_t, uint8_t); 181 static uint8_t asy_get(const struct asycom *, asy_reg_t); 182 183 static void asy_set(const struct asycom *, asy_reg_t, uint8_t); 184 static void asy_clr(const struct asycom *, asy_reg_t, uint8_t); 185 186 static void asy_enable_interrupts(const struct asycom *, uint8_t); 187 static void asy_disable_interrupts(const struct asycom *, uint8_t); 188 static void asy_set_baudrate(const struct asycom *, int); 189 static void asy_wait_baudrate(struct asycom *); 190 191 #define BAUDINDEX(cflg) (((cflg) & CBAUDEXT) ? \ 192 (((cflg) & CBAUD) + CBAUD + 1) : ((cflg) & CBAUD)) 193 194 static void asysetsoft(struct asycom *); 195 static uint_t asysoftintr(caddr_t, caddr_t); 196 static uint_t asyintr(caddr_t, caddr_t); 197 198 static boolean_t abort_charseq_recognize(uchar_t ch); 199 200 /* The async interrupt entry points */ 201 static void async_txint(struct asycom *asy); 202 static void async_rxint(struct asycom *asy, uchar_t lsr); 203 static void async_msint(struct asycom *asy); 204 static void async_softint(struct asycom *asy); 205 206 static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp); 207 static void async_reioctl(void *unit); 208 static void async_iocdata(queue_t *q, mblk_t *mp); 209 static void async_restart(void *arg); 210 static void async_start(struct asyncline *async); 211 static void async_resume(struct asyncline *async); 212 static void asy_program(struct asycom *asy, int mode); 213 static void asyinit(struct asycom *asy); 214 static void asy_waiteot(struct asycom *asy); 215 static void asyputchar(cons_polledio_arg_t, uchar_t c); 216 static int asygetchar(cons_polledio_arg_t); 217 static boolean_t asyischar(cons_polledio_arg_t); 218 219 static int asymctl(struct asycom *, int, int); 220 static int asytodm(int, int); 221 static int dmtoasy(struct asycom *, int); 222 static void asyerror(const struct asycom *, int, const char *, ...) 223 __KPRINTFLIKE(3); 224 static void asy_parse_mode(dev_info_t *devi, struct asycom *asy); 225 static void asy_soft_state_free(struct asycom *); 226 static char *asy_hw_name(struct asycom *asy); 227 static void async_hold_utbrk(void *arg); 228 static void async_resume_utbrk(struct asyncline *async); 229 static void async_dtr_free(struct asyncline *async); 230 static int asy_identify_chip(dev_info_t *devi, struct asycom *asy); 231 static void asy_reset_fifo(struct asycom *asy, uchar_t flags); 232 static void asy_carrier_check(struct asycom *); 233 static int asy_getproperty(dev_info_t *devi, struct asycom *asy, 234 const char *property); 235 static boolean_t async_flowcontrol_sw_input(struct asycom *asy, 236 async_flowc_action onoff, int type); 237 static void async_flowcontrol_sw_output(struct asycom *asy, 238 async_flowc_action onoff); 239 static void async_flowcontrol_hw_input(struct asycom *asy, 240 async_flowc_action onoff, int type); 241 static void async_flowcontrol_hw_output(struct asycom *asy, 242 async_flowc_action onoff); 243 244 #define GET_PROP(devi, pname, pflag, pval, plen) \ 245 (ddi_prop_op(DDI_DEV_T_ANY, (devi), PROP_LEN_AND_VAL_BUF, \ 246 (pflag), (pname), (caddr_t)(pval), (plen))) 247 248 kmutex_t asy_glob_lock; /* lock protecting global data manipulation */ 249 void *asy_soft_state; 250 251 /* Standard COM port I/O addresses */ 252 static const int standard_com_ports[] = { 253 COM1_IOADDR, COM2_IOADDR, COM3_IOADDR, COM4_IOADDR 254 }; 255 256 static int *com_ports; 257 static uint_t num_com_ports; 258 259 #ifdef DEBUG 260 /* 261 * Set this to true to make the driver pretend to do a suspend. Useful 262 * for debugging suspend/resume code with a serial debugger. 263 */ 264 boolean_t asy_nosuspend = B_FALSE; 265 #endif 266 267 268 /* 269 * Baud rate table. Indexed by #defines found in sys/termios.h 270 * 271 * The default crystal frequency is 1.8432 MHz. The 8250A used a fixed /16 272 * prescaler and a 16bit divisor, split in two registers (DLH and DLL). 273 * 274 * The 16950 adds TCR and CKS registers. The TCR can be used to set the 275 * prescaler from /4 to /16. The CKS can be used, among other things, to 276 * select a isochronous 1x mode, effectively disabling the prescaler. 277 * This would theoretically allow a baud rate of 1843200 driven directly 278 * by the default crystal frequency, although the highest termios.h-defined 279 * baud rate we can support is half of that, 921600 baud. 280 */ 281 #define UNSUPPORTED 0x00, 0x00, 0x00 282 static struct { 283 uint8_t asy_dlh; 284 uint8_t asy_dll; 285 uint8_t asy_tcr; 286 } asy_baud_tab[] = { 287 [B0] = { UNSUPPORTED }, /* 0 baud */ 288 [B50] = { 0x09, 0x00, 0x00 }, /* 50 baud */ 289 [B75] = { 0x06, 0x00, 0x00 }, /* 75 baud */ 290 [B110] = { 0x04, 0x17, 0x00 }, /* 110 baud (0.026% error) */ 291 [B134] = { 0x03, 0x59, 0x00 }, /* 134 baud (0.058% error) */ 292 [B150] = { 0x03, 0x00, 0x00 }, /* 150 baud */ 293 [B200] = { 0x02, 0x40, 0x00 }, /* 200 baud */ 294 [B300] = { 0x01, 0x80, 0x00 }, /* 300 baud */ 295 [B600] = { 0x00, 0xc0, 0x00 }, /* 600 baud */ 296 [B1200] = { 0x00, 0x60, 0x00 }, /* 1200 baud */ 297 [B1800] = { 0x00, 0x40, 0x00 }, /* 1800 baud */ 298 [B2400] = { 0x00, 0x30, 0x00 }, /* 2400 baud */ 299 [B4800] = { 0x00, 0x18, 0x00 }, /* 4800 baud */ 300 [B9600] = { 0x00, 0x0c, 0x00 }, /* 9600 baud */ 301 [B19200] = { 0x00, 0x06, 0x00 }, /* 19200 baud */ 302 [B38400] = { 0x00, 0x03, 0x00 }, /* 38400 baud */ 303 [B57600] = { 0x00, 0x02, 0x00 }, /* 57600 baud */ 304 [B76800] = { 0x00, 0x06, 0x04 }, /* 76800 baud (16950) */ 305 [B115200] = { 0x00, 0x01, 0x00 }, /* 115200 baud */ 306 [B153600] = { 0x00, 0x03, 0x04 }, /* 153600 baud (16950) */ 307 [B230400] = { 0x00, 0x02, 0x04 }, /* 230400 baud (16950) */ 308 [B307200] = { 0x00, 0x01, 0x06 }, /* 307200 baud (16950) */ 309 [B460800] = { 0x00, 0x01, 0x04 }, /* 460800 baud (16950) */ 310 [B921600] = { 0x00, 0x02, 0x01 }, /* 921600 baud (16950) */ 311 [B1000000] = { UNSUPPORTED }, /* 1000000 baud */ 312 [B1152000] = { UNSUPPORTED }, /* 1152000 baud */ 313 [B1500000] = { UNSUPPORTED }, /* 1500000 baud */ 314 [B2000000] = { UNSUPPORTED }, /* 2000000 baud */ 315 [B2500000] = { UNSUPPORTED }, /* 2500000 baud */ 316 [B3000000] = { UNSUPPORTED }, /* 3000000 baud */ 317 [B3500000] = { UNSUPPORTED }, /* 3500000 baud */ 318 [B4000000] = { UNSUPPORTED }, /* 4000000 baud */ 319 }; 320 321 /* 322 * Register table. For each logical register, we define the minimum hwtype, the 323 * register offset, and function pointers for reading and writing the register. 324 * A NULL pointer indicates the register cannot be read from or written to, 325 * respectively. 326 */ 327 static struct { 328 int asy_min_hwtype; 329 int8_t asy_reg_off; 330 uint8_t (*asy_get_reg)(const struct asycom *, asy_reg_t); 331 void (*asy_put_reg)(const struct asycom *, asy_reg_t, uint8_t); 332 } asy_reg_table[] = { 333 [ASY_ILLEGAL] = { 0, -1, NULL, NULL }, 334 /* 8250 / 16450 / 16550 registers */ 335 [ASY_THR] = { ASY_8250A, 0, NULL, asy_put_reg }, 336 [ASY_RHR] = { ASY_8250A, 0, asy_get_reg, NULL }, 337 [ASY_IER] = { ASY_8250A, 1, asy_get_reg, asy_put_reg }, 338 [ASY_FCR] = { ASY_16550, 2, NULL, asy_put_reg }, 339 [ASY_ISR] = { ASY_8250A, 2, asy_get_reg, NULL }, 340 [ASY_LCR] = { ASY_8250A, 3, asy_get_reg, asy_put_reg }, 341 [ASY_MCR] = { ASY_8250A, 4, asy_get_reg, asy_put_reg }, 342 [ASY_LSR] = { ASY_8250A, 5, asy_get_reg, NULL }, 343 [ASY_MSR] = { ASY_8250A, 6, asy_get_reg, NULL }, 344 [ASY_SPR] = { ASY_8250A, 7, asy_get_reg, asy_put_reg }, 345 [ASY_DLL] = { ASY_8250A, 0, asy_get_reg, asy_put_reg }, 346 [ASY_DLH] = { ASY_8250A, 1, asy_get_reg, asy_put_reg }, 347 /* 16750 extended register */ 348 [ASY_EFR] = { ASY_16750, 2, asy_get_ext, asy_put_ext }, 349 /* 16650 extended registers */ 350 [ASY_XON1] = { ASY_16650, 4, asy_get_ext, asy_put_ext }, 351 [ASY_XON2] = { ASY_16650, 5, asy_get_ext, asy_put_ext }, 352 [ASY_XOFF1] = { ASY_16650, 6, asy_get_ext, asy_put_ext }, 353 [ASY_XOFF2] = { ASY_16650, 7, asy_get_ext, asy_put_ext }, 354 /* 16950 additional registers */ 355 [ASY_ASR] = { ASY_16950, 1, asy_get_add, asy_put_add }, 356 [ASY_RFL] = { ASY_16950, 3, asy_get_add, NULL }, 357 [ASY_TFL] = { ASY_16950, 4, asy_get_add, NULL }, 358 [ASY_ICR] = { ASY_16950, 5, asy_get_reg, asy_put_reg }, 359 /* 16950 indexed registers */ 360 [ASY_ACR] = { ASY_16950, 0, asy_get_idx, asy_put_idx }, 361 [ASY_CPR] = { ASY_16950, 1, asy_get_idx, asy_put_idx }, 362 [ASY_TCR] = { ASY_16950, 2, asy_get_idx, asy_put_idx }, 363 [ASY_CKS] = { ASY_16950, 3, asy_get_idx, asy_put_idx }, 364 [ASY_TTL] = { ASY_16950, 4, asy_get_idx, asy_put_idx }, 365 [ASY_RTL] = { ASY_16950, 5, asy_get_idx, asy_put_idx }, 366 [ASY_FCL] = { ASY_16950, 6, asy_get_idx, asy_put_idx }, 367 [ASY_FCH] = { ASY_16950, 7, asy_get_idx, asy_put_idx }, 368 [ASY_ID1] = { ASY_16950, 8, asy_get_idx, NULL }, 369 [ASY_ID2] = { ASY_16950, 9, asy_get_idx, NULL }, 370 [ASY_ID3] = { ASY_16950, 10, asy_get_idx, NULL }, 371 [ASY_REV] = { ASY_16950, 11, asy_get_idx, NULL }, 372 [ASY_CSR] = { ASY_16950, 12, NULL, asy_put_idx }, 373 [ASY_NMR] = { ASY_16950, 13, asy_get_idx, asy_put_idx }, 374 }; 375 376 377 static int asyrsrv(queue_t *q); 378 static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); 379 static int asyclose(queue_t *q, int flag, cred_t *credp); 380 static int asywputdo(queue_t *q, mblk_t *mp, boolean_t); 381 static int asywput(queue_t *q, mblk_t *mp); 382 383 struct module_info asy_info = { 384 0, 385 "asy", 386 0, 387 INFPSZ, 388 4096, 389 128 390 }; 391 392 static struct qinit asy_rint = { 393 putq, 394 asyrsrv, 395 asyopen, 396 asyclose, 397 NULL, 398 &asy_info, 399 NULL 400 }; 401 402 static struct qinit asy_wint = { 403 asywput, 404 NULL, 405 NULL, 406 NULL, 407 NULL, 408 &asy_info, 409 NULL 410 }; 411 412 struct streamtab asy_str_info = { 413 &asy_rint, 414 &asy_wint, 415 NULL, 416 NULL 417 }; 418 419 static void asy_intr_free(struct asycom *); 420 static int asy_intr_setup(struct asycom *, int); 421 422 static void asy_softintr_free(struct asycom *); 423 static int asy_softintr_setup(struct asycom *); 424 425 static int asy_suspend(struct asycom *); 426 static int asy_resume(dev_info_t *); 427 428 static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 429 void **result); 430 static int asyprobe(dev_info_t *); 431 static int asyattach(dev_info_t *, ddi_attach_cmd_t); 432 static int asydetach(dev_info_t *, ddi_detach_cmd_t); 433 static int asyquiesce(dev_info_t *); 434 435 static struct cb_ops cb_asy_ops = { 436 nodev, /* cb_open */ 437 nodev, /* cb_close */ 438 nodev, /* cb_strategy */ 439 nodev, /* cb_print */ 440 nodev, /* cb_dump */ 441 nodev, /* cb_read */ 442 nodev, /* cb_write */ 443 nodev, /* cb_ioctl */ 444 nodev, /* cb_devmap */ 445 nodev, /* cb_mmap */ 446 nodev, /* cb_segmap */ 447 nochpoll, /* cb_chpoll */ 448 ddi_prop_op, /* cb_prop_op */ 449 &asy_str_info, /* cb_stream */ 450 D_MP /* cb_flag */ 451 }; 452 453 struct dev_ops asy_ops = { 454 DEVO_REV, /* devo_rev */ 455 0, /* devo_refcnt */ 456 asyinfo, /* devo_getinfo */ 457 nulldev, /* devo_identify */ 458 asyprobe, /* devo_probe */ 459 asyattach, /* devo_attach */ 460 asydetach, /* devo_detach */ 461 nodev, /* devo_reset */ 462 &cb_asy_ops, /* devo_cb_ops */ 463 NULL, /* devo_bus_ops */ 464 NULL, /* power */ 465 asyquiesce, /* quiesce */ 466 }; 467 468 static struct modldrv modldrv = { 469 &mod_driverops, /* Type of module. This one is a driver */ 470 "ASY driver", 471 &asy_ops, /* driver ops */ 472 }; 473 474 static struct modlinkage modlinkage = { 475 MODREV_1, 476 (void *)&modldrv, 477 NULL 478 }; 479 480 int 481 _init(void) 482 { 483 int i; 484 485 i = ddi_soft_state_init(&asy_soft_state, sizeof (struct asycom), 2); 486 if (i == 0) { 487 mutex_init(&asy_glob_lock, NULL, MUTEX_DRIVER, NULL); 488 if ((i = mod_install(&modlinkage)) != 0) { 489 mutex_destroy(&asy_glob_lock); 490 ddi_soft_state_fini(&asy_soft_state); 491 #ifdef DEBUG 492 } else { 493 if (debug & ASY_DEBUG_INIT) 494 cmn_err(CE_NOTE, "!%s, debug = %x", 495 modldrv.drv_linkinfo, debug); 496 #endif 497 } 498 } 499 return (i); 500 } 501 502 int 503 _fini(void) 504 { 505 int i; 506 507 if ((i = mod_remove(&modlinkage)) == 0) { 508 #ifdef DEBUG 509 if (debug & ASY_DEBUG_INIT) 510 cmn_err(CE_NOTE, "!%s unloading", 511 modldrv.drv_linkinfo); 512 #endif 513 mutex_destroy(&asy_glob_lock); 514 /* free "motherboard-serial-ports" property if allocated */ 515 if (com_ports != NULL && com_ports != (int *)standard_com_ports) 516 ddi_prop_free(com_ports); 517 com_ports = NULL; 518 ddi_soft_state_fini(&asy_soft_state); 519 } 520 return (i); 521 } 522 523 int 524 _info(struct modinfo *modinfop) 525 { 526 return (mod_info(&modlinkage, modinfop)); 527 } 528 529 static void 530 asy_put_idx(const struct asycom *asy, asy_reg_t reg, uint8_t val) 531 { 532 ASSERT(asy->asy_hwtype >= ASY_16950); 533 534 ASSERT(reg >= ASY_ACR); 535 ASSERT(reg <= ASY_NREG); 536 537 /* 538 * The last value written to LCR must not have been the magic value for 539 * EFR access. Every time the driver writes that magic value to access 540 * EFR, XON1, XON2, XOFF1, and XOFF2, the driver restores the original 541 * value of LCR, so we should be good here. 542 * 543 * I'd prefer to ASSERT this, but I'm not sure it's worth the hassle. 544 */ 545 546 /* Write indexed register offset to SPR. */ 547 asy_put(asy, ASY_SPR, asy_reg_table[reg].asy_reg_off); 548 549 /* Write value to ICR. */ 550 asy_put(asy, ASY_ICR, val); 551 } 552 553 static uint8_t 554 asy_get_idx(const struct asycom *asy, asy_reg_t reg) 555 { 556 uint8_t val; 557 558 ASSERT(asy->asy_hwtype >= ASY_16950); 559 560 ASSERT(reg >= ASY_ACR); 561 ASSERT(reg <= ASY_NREG); 562 563 /* Enable access to ICR in ACR. */ 564 asy_put(asy, ASY_ACR, ASY_ACR_ICR | asy->asy_acr); 565 566 /* Write indexed register offset to SPR. */ 567 asy_put(asy, ASY_SPR, asy_reg_table[reg].asy_reg_off); 568 569 /* Read value from ICR. */ 570 val = asy_get(asy, ASY_ICR); 571 572 /* Restore ACR. */ 573 asy_put(asy, ASY_ACR, asy->asy_acr); 574 575 return (val); 576 } 577 578 static void 579 asy_put_add(const struct asycom *asy, asy_reg_t reg, uint8_t val) 580 { 581 ASSERT(asy->asy_hwtype >= ASY_16950); 582 583 /* Only ASR is writable, RFL and TFL are read-only. */ 584 ASSERT(reg == ASY_ASR); 585 586 /* 587 * Only ASR[0] (Transmitter Disabled) and ASR[1] (Remote Transmitter 588 * Disabled) are writable. 589 */ 590 ASSERT((val & ~(ASY_ASR_TD | ASY_ASR_RTD)) == 0); 591 592 /* Enable access to ASR in ACR. */ 593 asy_put(asy, ASY_ACR, ASY_ACR_ASR | asy->asy_acr); 594 595 /* Write value to ASR. */ 596 asy_put_reg(asy, reg, val); 597 598 /* Restore ACR. */ 599 asy_put(asy, ASY_ACR, asy->asy_acr); 600 } 601 602 static uint8_t 603 asy_get_add(const struct asycom *asy, asy_reg_t reg) 604 { 605 uint8_t val; 606 607 ASSERT(asy->asy_hwtype >= ASY_16950); 608 609 ASSERT(reg >= ASY_ASR); 610 ASSERT(reg <= ASY_TFL); 611 612 /* 613 * The last value written to LCR must not have been the magic value for 614 * EFR access. Every time the driver writes that magic value to access 615 * EFR, XON1, XON2, XOFF1, and XOFF2, the driver restores the original 616 * value of LCR, so we should be good here. 617 * 618 * I'd prefer to ASSERT this, but I'm not sure it's worth the hassle. 619 */ 620 621 /* Enable access to ASR in ACR. */ 622 asy_put(asy, ASY_ACR, ASY_ACR_ASR | asy->asy_acr); 623 624 /* Read value from register. */ 625 val = asy_get_reg(asy, reg); 626 627 /* Restore ACR. */ 628 asy_put(asy, ASY_ACR, 0 | asy->asy_acr); 629 630 return (val); 631 } 632 633 static void 634 asy_put_ext(const struct asycom *asy, asy_reg_t reg, uint8_t val) 635 { 636 uint8_t lcr; 637 638 /* 639 * On the 16750, EFR can be accessed when LCR[7]=1 (DLAB). 640 * Only two bits are assigned for auto RTS/CTS, which we don't support 641 * yet. 642 * 643 * So insist we have a 16650 or up. 644 */ 645 ASSERT(asy->asy_hwtype >= ASY_16650); 646 647 ASSERT(reg >= ASY_EFR); 648 ASSERT(reg <= ASY_XOFF2); 649 650 /* Save LCR contents. */ 651 lcr = asy_get(asy, ASY_LCR); 652 653 /* Enable extended register access. */ 654 asy_put(asy, ASY_LCR, ASY_LCR_EFRACCESS); 655 656 /* Write extended register */ 657 asy_put_reg(asy, reg, val); 658 659 /* Restore previous LCR contents, disabling extended register access. */ 660 asy_put(asy, ASY_LCR, lcr); 661 } 662 663 static uint8_t 664 asy_get_ext(const struct asycom *asy, asy_reg_t reg) 665 { 666 uint8_t lcr, val; 667 668 /* 669 * On the 16750, EFR can be accessed when LCR[7]=1 (DLAB). 670 * Only two bits are assigned for auto RTS/CTS, which we don't support 671 * yet. 672 * 673 * So insist we have a 16650 or up. 674 */ 675 ASSERT(asy->asy_hwtype >= ASY_16650); 676 677 ASSERT(reg >= ASY_EFR); 678 ASSERT(reg <= ASY_XOFF2); 679 680 /* Save LCR contents. */ 681 lcr = asy_get(asy, ASY_LCR); 682 683 /* Enable extended register access. */ 684 asy_put(asy, ASY_LCR, ASY_LCR_EFRACCESS); 685 686 /* Read extended register */ 687 val = asy_get_reg(asy, reg); 688 689 /* Restore previous LCR contents, disabling extended register access. */ 690 asy_put(asy, ASY_LCR, lcr); 691 692 return (val); 693 } 694 695 static void 696 asy_put_reg(const struct asycom *asy, asy_reg_t reg, uint8_t val) 697 { 698 ASSERT(asy->asy_hwtype >= asy_reg_table[reg].asy_min_hwtype); 699 700 ddi_put8(asy->asy_iohandle, 701 asy->asy_ioaddr + asy_reg_table[reg].asy_reg_off, val); 702 } 703 704 static uint8_t 705 asy_get_reg(const struct asycom *asy, asy_reg_t reg) 706 { 707 ASSERT(asy->asy_hwtype >= asy_reg_table[reg].asy_min_hwtype); 708 709 return (ddi_get8(asy->asy_iohandle, 710 asy->asy_ioaddr + asy_reg_table[reg].asy_reg_off)); 711 } 712 713 static void 714 asy_put(const struct asycom *asy, asy_reg_t reg, uint8_t val) 715 { 716 ASSERT(mutex_owned(&asy->asy_excl_hi)); 717 718 ASSERT(reg > ASY_ILLEGAL); 719 ASSERT(reg < ASY_NREG); 720 721 ASSERT(asy->asy_hwtype >= asy_reg_table[reg].asy_min_hwtype); 722 ASSERT(asy_reg_table[reg].asy_put_reg != NULL); 723 724 asy_reg_table[reg].asy_put_reg(asy, reg, val); 725 } 726 727 static uint8_t 728 asy_get(const struct asycom *asy, asy_reg_t reg) 729 { 730 uint8_t val; 731 732 ASSERT(mutex_owned(&asy->asy_excl_hi)); 733 734 ASSERT(reg > ASY_ILLEGAL); 735 ASSERT(reg < ASY_NREG); 736 737 ASSERT(asy->asy_hwtype >= asy_reg_table[reg].asy_min_hwtype); 738 ASSERT(asy_reg_table[reg].asy_get_reg != NULL); 739 740 val = asy_reg_table[reg].asy_get_reg(asy, reg); 741 742 return (val); 743 } 744 745 static void 746 asy_set(const struct asycom *asy, asy_reg_t reg, uint8_t bits) 747 { 748 uint8_t val = asy_get(asy, reg); 749 750 asy_put(asy, reg, val | bits); 751 } 752 753 static void 754 asy_clr(const struct asycom *asy, asy_reg_t reg, uint8_t bits) 755 { 756 uint8_t val = asy_get(asy, reg); 757 758 asy_put(asy, reg, val & ~bits); 759 } 760 761 static void 762 asy_enable_interrupts(const struct asycom *asy, uint8_t intr) 763 { 764 /* Don't touch any IER bits we don't support. */ 765 intr &= ASY_IER_ALL; 766 767 asy_set(asy, ASY_IER, intr); 768 } 769 770 static void 771 asy_disable_interrupts(const struct asycom *asy, uint8_t intr) 772 { 773 /* Don't touch any IER bits we don't support. */ 774 intr &= ASY_IER_ALL; 775 776 asy_clr(asy, ASY_IER, intr); 777 } 778 779 static void 780 asy_set_baudrate(const struct asycom *asy, int baudrate) 781 { 782 uint8_t tcr; 783 784 if (baudrate == 0) 785 return; 786 787 if (baudrate >= ARRAY_SIZE(asy_baud_tab)) 788 return; 789 790 tcr = asy_baud_tab[baudrate].asy_tcr; 791 792 if (tcr != 0 && asy->asy_hwtype < ASY_16950) 793 return; 794 795 if (asy->asy_hwtype >= ASY_16950) { 796 if (tcr == 0x01) { 797 /* Isochronous 1x mode is selected in CKS, not TCR. */ 798 asy_put(asy, ASY_CKS, 799 ASY_CKS_RCLK_1X | ASY_CKS_TCLK_1X); 800 asy_put(asy, ASY_TCR, 0); 801 } else { 802 /* Reset CKS in case it was set to 1x mode. */ 803 asy_put(asy, ASY_CKS, 0); 804 805 ASSERT(tcr == 0x00 || tcr >= 0x04 || tcr <= 0x0f); 806 asy_put(asy, ASY_TCR, tcr); 807 } 808 ASY_DPRINTF(asy, ASY_DEBUG_IOCTL, 809 "setting baudrate %d, CKS 0x%02x, TCR 0x%02x", 810 baudrate, asy_get(asy, ASY_CKS), asy_get(asy, ASY_TCR)); 811 } 812 813 ASY_DPRINTF(asy, ASY_DEBUG_IOCTL, 814 "setting baudrate %d, divisor 0x%02x%02x", 815 baudrate, asy_baud_tab[baudrate].asy_dlh, 816 asy_baud_tab[baudrate].asy_dll); 817 818 asy_set(asy, ASY_LCR, ASY_LCR_DLAB); 819 820 asy_put(asy, ASY_DLL, asy_baud_tab[baudrate].asy_dll); 821 asy_put(asy, ASY_DLH, asy_baud_tab[baudrate].asy_dlh); 822 823 asy_clr(asy, ASY_LCR, ASY_LCR_DLAB); 824 } 825 826 /* 827 * Loop until the TSR is empty. 828 * 829 * The wait period is clock / (baud * 16) * 16 * 2. 830 */ 831 static void 832 asy_wait_baudrate(struct asycom *asy) 833 { 834 struct asyncline *async = asy->asy_priv; 835 int rate = BAUDINDEX(async->async_ttycommon.t_cflag); 836 clock_t usec = 837 ((((clock_t)asy_baud_tab[rate].asy_dlh) << 8) | 838 ((clock_t)asy_baud_tab[rate].asy_dll)) * 16 * 2; 839 840 ASSERT(mutex_owned(&asy->asy_excl)); 841 ASSERT(mutex_owned(&asy->asy_excl_hi)); 842 843 while ((asy_get(asy, ASY_LSR) & ASY_LSR_TEMT) == 0) { 844 mutex_exit(&asy->asy_excl_hi); 845 mutex_exit(&asy->asy_excl); 846 drv_usecwait(usec); 847 mutex_enter(&asy->asy_excl); 848 mutex_enter(&asy->asy_excl_hi); 849 } 850 asy_set(asy, ASY_LCR, ASY_LCR_SETBRK); 851 } 852 853 void 854 async_put_suspq(struct asycom *asy, mblk_t *mp) 855 { 856 struct asyncline *async = asy->asy_priv; 857 858 ASSERT(mutex_owned(&asy->asy_excl)); 859 860 if (async->async_suspqf == NULL) 861 async->async_suspqf = mp; 862 else 863 async->async_suspqb->b_next = mp; 864 865 async->async_suspqb = mp; 866 } 867 868 static mblk_t * 869 async_get_suspq(struct asycom *asy) 870 { 871 struct asyncline *async = asy->asy_priv; 872 mblk_t *mp; 873 874 ASSERT(mutex_owned(&asy->asy_excl)); 875 876 if ((mp = async->async_suspqf) != NULL) { 877 async->async_suspqf = mp->b_next; 878 mp->b_next = NULL; 879 } else { 880 async->async_suspqb = NULL; 881 } 882 return (mp); 883 } 884 885 static void 886 async_process_suspq(struct asycom *asy) 887 { 888 struct asyncline *async = asy->asy_priv; 889 mblk_t *mp; 890 891 ASSERT(mutex_owned(&asy->asy_excl)); 892 893 while ((mp = async_get_suspq(asy)) != NULL) { 894 queue_t *q; 895 896 q = async->async_ttycommon.t_writeq; 897 ASSERT(q != NULL); 898 mutex_exit(&asy->asy_excl); 899 (void) asywputdo(q, mp, B_FALSE); 900 mutex_enter(&asy->asy_excl); 901 } 902 async->async_flags &= ~ASYNC_DDI_SUSPENDED; 903 cv_broadcast(&async->async_flags_cv); 904 } 905 906 static int 907 asy_get_bus_type(dev_info_t *devinfo) 908 { 909 char *prop; 910 int bustype; 911 912 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, devinfo, 0, "device_type", 913 &prop) != DDI_PROP_SUCCESS && 914 ddi_prop_lookup_string(DDI_DEV_T_ANY, devinfo, 0, "bus-type", 915 &prop) != DDI_PROP_SUCCESS) { 916 dev_err(devinfo, CE_WARN, 917 "!%s: can't figure out device type for parent \"%s\"", 918 __func__, ddi_get_name(ddi_get_parent(devinfo))); 919 return (ASY_BUS_UNKNOWN); 920 } 921 922 if (strcmp(prop, "isa") == 0) 923 bustype = ASY_BUS_ISA; 924 else if (strcmp(prop, "pci") == 0) 925 bustype = ASY_BUS_PCI; 926 else if (strcmp(prop, "pciex") == 0) 927 return (ASY_BUS_PCI); 928 else 929 bustype = ASY_BUS_UNKNOWN; 930 931 ddi_prop_free(prop); 932 return (bustype); 933 } 934 935 static int 936 asy_get_io_regnum_pci(dev_info_t *devi, struct asycom *asy) 937 { 938 int reglen, nregs; 939 int regnum, i; 940 uint64_t size; 941 struct pci_phys_spec *reglist; 942 943 if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 944 "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { 945 dev_err(devi, CE_WARN, "!%s: reg property" 946 " not found in devices property list", __func__); 947 return (-1); 948 } 949 950 regnum = -1; 951 nregs = reglen / sizeof (*reglist); 952 for (i = 0; i < nregs; i++) { 953 switch (reglist[i].pci_phys_hi & PCI_ADDR_MASK) { 954 case PCI_ADDR_IO: /* I/O bus reg property */ 955 if (regnum == -1) /* use only the first one */ 956 regnum = i; 957 break; 958 959 default: 960 break; 961 } 962 } 963 964 /* check for valid count of registers */ 965 if (regnum >= 0) { 966 size = ((uint64_t)reglist[regnum].pci_size_low) | 967 ((uint64_t)reglist[regnum].pci_size_hi) << 32; 968 if (size < 8) 969 regnum = -1; 970 } 971 kmem_free(reglist, reglen); 972 return (regnum); 973 } 974 975 static int 976 asy_get_io_regnum_isa(dev_info_t *devi, struct asycom *asy) 977 { 978 int regnum = -1; 979 int reglen, nregs; 980 struct { 981 uint_t bustype; 982 int base; 983 int size; 984 } *reglist; 985 986 if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 987 "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { 988 dev_err(devi, CE_WARN, "!%s: reg property not found " 989 "in devices property list", __func__); 990 return (-1); 991 } 992 993 nregs = reglen / sizeof (*reglist); 994 995 /* 996 * Find the first I/O bus in the "reg" property. 997 */ 998 for (int i = 0; i < nregs && regnum == -1; i++) { 999 if (reglist[i].bustype == 1) { 1000 regnum = i; 1001 break; 1002 } 1003 } 1004 1005 /* check for valid count of registers */ 1006 if ((regnum < 0) || (reglist[regnum].size < 8)) 1007 regnum = -1; 1008 1009 kmem_free(reglist, reglen); 1010 1011 return (regnum); 1012 } 1013 1014 static int 1015 asy_get_io_regnum(dev_info_t *devinfo, struct asycom *asy) 1016 { 1017 switch (asy_get_bus_type(devinfo)) { 1018 case ASY_BUS_ISA: 1019 return (asy_get_io_regnum_isa(devinfo, asy)); 1020 case ASY_BUS_PCI: 1021 return (asy_get_io_regnum_pci(devinfo, asy)); 1022 default: 1023 return (-1); 1024 } 1025 } 1026 1027 static void 1028 asy_intr_free(struct asycom *asy) 1029 { 1030 int i; 1031 1032 for (i = 0; i < asy->asy_intr_cnt; i++) { 1033 if (asy->asy_inth[i] == NULL) 1034 break; 1035 1036 if ((asy->asy_intr_cap & DDI_INTR_FLAG_BLOCK) != 0) 1037 (void) ddi_intr_block_disable(&asy->asy_inth[i], 1); 1038 else 1039 (void) ddi_intr_disable(asy->asy_inth[i]); 1040 1041 (void) ddi_intr_remove_handler(asy->asy_inth[i]); 1042 (void) ddi_intr_free(asy->asy_inth[i]); 1043 } 1044 1045 kmem_free(asy->asy_inth, asy->asy_inth_sz); 1046 asy->asy_inth = NULL; 1047 asy->asy_inth_sz = 0; 1048 } 1049 1050 static int 1051 asy_intr_setup(struct asycom *asy, int intr_type) 1052 { 1053 int nintrs, navail, count; 1054 int ret; 1055 int i; 1056 1057 if (asy->asy_intr_types == 0) { 1058 ret = ddi_intr_get_supported_types(asy->asy_dip, 1059 &asy->asy_intr_types); 1060 if (ret != DDI_SUCCESS) { 1061 asyerror(asy, CE_WARN, 1062 "ddi_intr_get_supported_types failed"); 1063 return (ret); 1064 } 1065 } 1066 1067 if ((asy->asy_intr_types & intr_type) == 0) 1068 return (DDI_FAILURE); 1069 1070 ret = ddi_intr_get_nintrs(asy->asy_dip, intr_type, &nintrs); 1071 if (ret != DDI_SUCCESS) { 1072 asyerror(asy, CE_WARN, "ddi_intr_get_nintrs failed, type %d", 1073 intr_type); 1074 return (ret); 1075 } 1076 1077 if (nintrs < 1) { 1078 asyerror(asy, CE_WARN, "no interrupts of type %d", intr_type); 1079 return (DDI_FAILURE); 1080 } 1081 1082 ret = ddi_intr_get_navail(asy->asy_dip, intr_type, &navail); 1083 if (ret != DDI_SUCCESS) { 1084 asyerror(asy, CE_WARN, "ddi_intr_get_navail failed, type %d", 1085 intr_type); 1086 return (ret); 1087 } 1088 1089 if (navail < 1) { 1090 asyerror(asy, CE_WARN, "no available interrupts, type %d", 1091 intr_type); 1092 return (DDI_FAILURE); 1093 } 1094 1095 /* 1096 * Some PCI(e) RS232 adapters seem to support more than one interrupt, 1097 * but the asy driver really doesn't. 1098 */ 1099 asy->asy_inth_sz = sizeof (ddi_intr_handle_t); 1100 asy->asy_inth = kmem_zalloc(asy->asy_inth_sz, KM_SLEEP); 1101 ret = ddi_intr_alloc(asy->asy_dip, asy->asy_inth, intr_type, 0, 1, 1102 &count, 0); 1103 if (ret != DDI_SUCCESS) { 1104 asyerror(asy, CE_WARN, "ddi_intr_alloc failed, count %d, " 1105 "type %d", navail, intr_type); 1106 goto fail; 1107 } 1108 1109 if (count != 1) { 1110 asyerror(asy, CE_WARN, "ddi_intr_alloc returned not 1 but %d " 1111 "interrupts of type %d", count, intr_type); 1112 goto fail; 1113 } 1114 1115 asy->asy_intr_cnt = count; 1116 1117 ret = ddi_intr_get_pri(asy->asy_inth[0], &asy->asy_intr_pri); 1118 if (ret != DDI_SUCCESS) { 1119 asyerror(asy, CE_WARN, "ddi_intr_get_pri failed, type %d", 1120 intr_type); 1121 goto fail; 1122 } 1123 1124 for (i = 0; i < count; i++) { 1125 ret = ddi_intr_add_handler(asy->asy_inth[i], asyintr, 1126 (void *)asy, (void *)(uintptr_t)i); 1127 if (ret != DDI_SUCCESS) { 1128 asyerror(asy, CE_WARN, "ddi_intr_add_handler failed, " 1129 "int %d, type %d", i, intr_type); 1130 goto fail; 1131 } 1132 } 1133 1134 (void) ddi_intr_get_cap(asy->asy_inth[0], &asy->asy_intr_cap); 1135 1136 for (i = 0; i < count; i++) { 1137 if (asy->asy_intr_cap & DDI_INTR_FLAG_BLOCK) 1138 ret = ddi_intr_block_enable(&asy->asy_inth[i], 1); 1139 else 1140 ret = ddi_intr_enable(asy->asy_inth[i]); 1141 1142 if (ret != DDI_SUCCESS) { 1143 asyerror(asy, CE_WARN, 1144 "enabling interrupt %d failed, type %d", 1145 i, intr_type); 1146 goto fail; 1147 } 1148 } 1149 1150 asy->asy_intr_type = intr_type; 1151 return (DDI_SUCCESS); 1152 1153 fail: 1154 asy_intr_free(asy); 1155 return (ret); 1156 } 1157 1158 static void 1159 asy_softintr_free(struct asycom *asy) 1160 { 1161 (void) ddi_intr_remove_softint(asy->asy_soft_inth); 1162 } 1163 1164 static int 1165 asy_softintr_setup(struct asycom *asy) 1166 { 1167 int ret; 1168 1169 ret = ddi_intr_add_softint(asy->asy_dip, &asy->asy_soft_inth, 1170 ASY_SOFT_INT_PRI, asysoftintr, asy); 1171 if (ret != DDI_SUCCESS) { 1172 asyerror(asy, CE_WARN, "ddi_intr_add_softint failed"); 1173 return (ret); 1174 } 1175 1176 /* 1177 * This may seem pointless since we specified ASY_SOFT_INT_PRI above, 1178 * but then it's probably a good idea to consider the soft interrupt 1179 * priority an opaque value and don't hardcode any assumptions about 1180 * its actual value here. 1181 */ 1182 ret = ddi_intr_get_softint_pri(asy->asy_soft_inth, 1183 &asy->asy_soft_intr_pri); 1184 if (ret != DDI_SUCCESS) { 1185 asyerror(asy, CE_WARN, "ddi_intr_get_softint_pri failed"); 1186 return (ret); 1187 } 1188 1189 return (DDI_SUCCESS); 1190 } 1191 1192 1193 static int 1194 asy_resume(dev_info_t *devi) 1195 { 1196 struct asyncline *async; 1197 struct asycom *asy; 1198 int instance = ddi_get_instance(devi); /* find out which unit */ 1199 1200 #ifdef DEBUG 1201 if (asy_nosuspend) 1202 return (DDI_SUCCESS); 1203 #endif 1204 asy = ddi_get_soft_state(asy_soft_state, instance); 1205 if (asy == NULL) 1206 return (DDI_FAILURE); 1207 1208 mutex_enter(&asy->asy_soft_sr); 1209 mutex_enter(&asy->asy_excl); 1210 mutex_enter(&asy->asy_excl_hi); 1211 1212 async = asy->asy_priv; 1213 asy_disable_interrupts(asy, ASY_IER_ALL); 1214 if (asy_identify_chip(devi, asy) != DDI_SUCCESS) { 1215 mutex_exit(&asy->asy_excl_hi); 1216 mutex_exit(&asy->asy_excl); 1217 mutex_exit(&asy->asy_soft_sr); 1218 asyerror(asy, CE_WARN, "Cannot identify UART chip at %p", 1219 (void *)asy->asy_ioaddr); 1220 return (DDI_FAILURE); 1221 } 1222 asy->asy_flags &= ~ASY_DDI_SUSPENDED; 1223 if (async->async_flags & ASYNC_ISOPEN) { 1224 asy_program(asy, ASY_INIT); 1225 /* Kick off output */ 1226 if (async->async_ocnt > 0) { 1227 async_resume(async); 1228 } else { 1229 mutex_exit(&asy->asy_excl_hi); 1230 if (async->async_xmitblk) 1231 freeb(async->async_xmitblk); 1232 async->async_xmitblk = NULL; 1233 async_start(async); 1234 mutex_enter(&asy->asy_excl_hi); 1235 } 1236 asysetsoft(asy); 1237 } 1238 mutex_exit(&asy->asy_excl_hi); 1239 mutex_exit(&asy->asy_excl); 1240 mutex_exit(&asy->asy_soft_sr); 1241 1242 mutex_enter(&asy->asy_excl); 1243 if (async->async_flags & ASYNC_RESUME_BUFCALL) { 1244 async->async_wbufcid = bufcall(async->async_wbufcds, 1245 BPRI_HI, (void (*)(void *)) async_reioctl, 1246 (void *)(intptr_t)async->async_common->asy_unit); 1247 async->async_flags &= ~ASYNC_RESUME_BUFCALL; 1248 } 1249 async_process_suspq(asy); 1250 mutex_exit(&asy->asy_excl); 1251 return (DDI_SUCCESS); 1252 } 1253 1254 static int 1255 asy_suspend(struct asycom *asy) 1256 { 1257 struct asyncline *async = asy->asy_priv; 1258 unsigned i; 1259 uchar_t lsr; 1260 1261 #ifdef DEBUG 1262 if (asy_nosuspend) 1263 return (DDI_SUCCESS); 1264 #endif 1265 mutex_enter(&asy->asy_excl); 1266 1267 ASSERT(async->async_ops >= 0); 1268 while (async->async_ops > 0) 1269 cv_wait(&async->async_ops_cv, &asy->asy_excl); 1270 1271 async->async_flags |= ASYNC_DDI_SUSPENDED; 1272 1273 /* Wait for timed break and delay to complete */ 1274 while ((async->async_flags & (ASYNC_BREAK|ASYNC_DELAY))) { 1275 if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) == 0) { 1276 async_process_suspq(asy); 1277 mutex_exit(&asy->asy_excl); 1278 return (DDI_FAILURE); 1279 } 1280 } 1281 1282 /* Clear untimed break */ 1283 if (async->async_flags & ASYNC_OUT_SUSPEND) 1284 async_resume_utbrk(async); 1285 1286 mutex_exit(&asy->asy_excl); 1287 1288 mutex_enter(&asy->asy_soft_sr); 1289 mutex_enter(&asy->asy_excl); 1290 if (async->async_wbufcid != 0) { 1291 bufcall_id_t bcid = async->async_wbufcid; 1292 async->async_wbufcid = 0; 1293 async->async_flags |= ASYNC_RESUME_BUFCALL; 1294 mutex_exit(&asy->asy_excl); 1295 unbufcall(bcid); 1296 mutex_enter(&asy->asy_excl); 1297 } 1298 mutex_enter(&asy->asy_excl_hi); 1299 1300 asy_disable_interrupts(asy, ASY_IER_ALL); 1301 asy->asy_flags |= ASY_DDI_SUSPENDED; 1302 1303 /* 1304 * Hardware interrupts are disabled we can drop our high level 1305 * lock and proceed. 1306 */ 1307 mutex_exit(&asy->asy_excl_hi); 1308 1309 /* Process remaining RX characters and RX errors, if any */ 1310 lsr = asy_get(asy, ASY_LSR); 1311 async_rxint(asy, lsr); 1312 1313 /* Wait for TX to drain */ 1314 for (i = 1000; i > 0; i--) { 1315 lsr = asy_get(asy, ASY_LSR); 1316 if ((lsr & (ASY_LSR_TEMT | ASY_LSR_THRE)) == 1317 (ASY_LSR_TEMT | ASY_LSR_THRE)) 1318 break; 1319 delay(drv_usectohz(10000)); 1320 } 1321 if (i == 0) 1322 asyerror(asy, CE_WARN, "transmitter wasn't drained before " 1323 "driver was suspended"); 1324 1325 mutex_exit(&asy->asy_excl); 1326 mutex_exit(&asy->asy_soft_sr); 1327 1328 return (DDI_SUCCESS); 1329 } 1330 1331 static int 1332 asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) 1333 { 1334 int instance; 1335 struct asycom *asy; 1336 1337 instance = ddi_get_instance(devi); /* find out which unit */ 1338 1339 asy = ddi_get_soft_state(asy_soft_state, instance); 1340 if (asy == NULL) 1341 return (DDI_FAILURE); 1342 1343 switch (cmd) { 1344 case DDI_DETACH: 1345 break; 1346 1347 case DDI_SUSPEND: 1348 return (asy_suspend(asy)); 1349 1350 default: 1351 return (DDI_FAILURE); 1352 } 1353 1354 ASY_DPRINTF(asy, ASY_DEBUG_INIT, "%s shutdown", asy_hw_name(asy)); 1355 1356 if ((asy->asy_progress & ASY_PROGRESS_ASYNC) != 0) { 1357 struct asyncline *async = asy->asy_priv; 1358 1359 /* cancel DTR hold timeout */ 1360 if (async->async_dtrtid != 0) { 1361 (void) untimeout(async->async_dtrtid); 1362 async->async_dtrtid = 0; 1363 } 1364 cv_destroy(&async->async_flags_cv); 1365 kmem_free(async, sizeof (struct asyncline)); 1366 asy->asy_priv = NULL; 1367 } 1368 1369 if ((asy->asy_progress & ASY_PROGRESS_MINOR) != 0) 1370 ddi_remove_minor_node(devi, NULL); 1371 1372 if ((asy->asy_progress & ASY_PROGRESS_MUTEX) != 0) { 1373 mutex_destroy(&asy->asy_excl); 1374 mutex_destroy(&asy->asy_excl_hi); 1375 mutex_destroy(&asy->asy_soft_lock); 1376 } 1377 1378 if ((asy->asy_progress & ASY_PROGRESS_INT) != 0) 1379 asy_intr_free(asy); 1380 1381 if ((asy->asy_progress & ASY_PROGRESS_SOFTINT) != 0) 1382 asy_softintr_free(asy); 1383 1384 if ((asy->asy_progress & ASY_PROGRESS_REGS) != 0) 1385 ddi_regs_map_free(&asy->asy_iohandle); 1386 1387 ASY_DPRINTF(asy, ASY_DEBUG_INIT, "shutdown complete"); 1388 asy_soft_state_free(asy); 1389 1390 return (DDI_SUCCESS); 1391 } 1392 1393 /* 1394 * asyprobe 1395 * We don't bother probing for the hardware, as since Solaris 2.6, device 1396 * nodes are only created for auto-detected hardware or nodes explicitly 1397 * created by the user, e.g. via the DCA. However, we should check the 1398 * device node is at least vaguely usable, i.e. we have a block of 8 i/o 1399 * ports. This prevents attempting to attach to bogus serial ports which 1400 * some BIOSs still partially report when they are disabled in the BIOS. 1401 */ 1402 static int 1403 asyprobe(dev_info_t *devi) 1404 { 1405 return ((asy_get_io_regnum(devi, NULL) < 0) ? 1406 DDI_PROBE_FAILURE : DDI_PROBE_DONTCARE); 1407 } 1408 1409 static int 1410 asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 1411 { 1412 int instance; 1413 int mcr; 1414 int ret; 1415 int regnum = 0; 1416 int i; 1417 struct asycom *asy; 1418 char name[ASY_MINOR_LEN]; 1419 int status; 1420 static ddi_device_acc_attr_t ioattr = { 1421 DDI_DEVICE_ATTR_V0, 1422 DDI_NEVERSWAP_ACC, 1423 DDI_STRICTORDER_ACC, 1424 }; 1425 1426 switch (cmd) { 1427 case DDI_ATTACH: 1428 break; 1429 1430 case DDI_RESUME: 1431 return (asy_resume(devi)); 1432 1433 default: 1434 return (DDI_FAILURE); 1435 } 1436 1437 mutex_enter(&asy_glob_lock); 1438 if (com_ports == NULL) { /* need to initialize com_ports */ 1439 if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi, 0, 1440 "motherboard-serial-ports", &com_ports, &num_com_ports) != 1441 DDI_PROP_SUCCESS) { 1442 /* Use our built-in COM[1234] values */ 1443 com_ports = (int *)standard_com_ports; 1444 num_com_ports = sizeof (standard_com_ports) / 1445 sizeof (standard_com_ports[0]); 1446 } 1447 if (num_com_ports > 10) { 1448 /* We run out of single digits for device properties */ 1449 num_com_ports = 10; 1450 cmn_err(CE_WARN, 1451 "%s: more than %d motherboard-serial-ports", 1452 asy_info.mi_idname, num_com_ports); 1453 } 1454 } 1455 mutex_exit(&asy_glob_lock); 1456 1457 instance = ddi_get_instance(devi); /* find out which unit */ 1458 ret = ddi_soft_state_zalloc(asy_soft_state, instance); 1459 if (ret != DDI_SUCCESS) 1460 return (DDI_FAILURE); 1461 asy = ddi_get_soft_state(asy_soft_state, instance); 1462 1463 asy->asy_dip = devi; 1464 #ifdef DEBUG 1465 asy->asy_debug = debug; 1466 #endif 1467 asy->asy_unit = instance; 1468 1469 regnum = asy_get_io_regnum(devi, asy); 1470 1471 if (regnum < 0 || 1472 ddi_regs_map_setup(devi, regnum, (caddr_t *)&asy->asy_ioaddr, 1473 (offset_t)0, (offset_t)0, &ioattr, &asy->asy_iohandle) 1474 != DDI_SUCCESS) { 1475 asyerror(asy, CE_WARN, "could not map UART registers @ %p", 1476 (void *)asy->asy_ioaddr); 1477 goto fail; 1478 } 1479 1480 asy->asy_progress |= ASY_PROGRESS_REGS; 1481 1482 ASY_DPRINTF(asy, ASY_DEBUG_INIT, "UART @ %p", (void *)asy->asy_ioaddr); 1483 1484 /* 1485 * Lookup the i/o address to see if this is a standard COM port 1486 * in which case we assign it the correct tty[a-d] to match the 1487 * COM port number, or some other i/o address in which case it 1488 * will be assigned /dev/term/[0123...] in some rather arbitrary 1489 * fashion. 1490 */ 1491 for (i = 0; i < num_com_ports; i++) { 1492 if (asy->asy_ioaddr == (uint8_t *)(uintptr_t)com_ports[i]) { 1493 asy->asy_com_port = i + 1; 1494 break; 1495 } 1496 } 1497 1498 /* 1499 * It appears that there was async hardware that on reset did not clear 1500 * IER. Hence when we enable interrupts, this hardware would cause the 1501 * system to hang if there was input available. 1502 * 1503 * Don't use asy_disable_interrupts() as the mutexes haven't been 1504 * initialized yet. 1505 */ 1506 ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ASY_IER, 0); 1507 1508 1509 /* 1510 * Establish default settings: 1511 * - use RTS/DTR after open 1512 * - 8N1 data format 1513 * - 9600 baud 1514 */ 1515 asy->asy_mcr |= ASY_MCR_RTS | ASY_MCR_DTR; 1516 asy->asy_lcr = ASY_LCR_STOP1 | ASY_LCR_BITS8; 1517 asy->asy_bidx = B9600; 1518 asy->asy_fifo_buf = 1; 1519 asy->asy_use_fifo = ASY_FCR_FIFO_OFF; 1520 1521 #ifdef DEBUG 1522 asy->asy_msint_cnt = 0; /* # of times in async_msint */ 1523 #endif 1524 mcr = 0; /* don't enable until open */ 1525 1526 if (asy->asy_com_port != 0) { 1527 /* 1528 * For motherboard ports, emulate tty eeprom properties. 1529 * Actually, we can't tell if a port is motherboard or not, 1530 * so for "motherboard ports", read standard DOS COM ports. 1531 */ 1532 switch (asy_getproperty(devi, asy, "ignore-cd")) { 1533 case 0: /* *-ignore-cd=False */ 1534 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 1535 "clear ASY_IGNORE_CD"); 1536 asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */ 1537 break; 1538 case 1: /* *-ignore-cd=True */ 1539 /*FALLTHRU*/ 1540 default: /* *-ignore-cd not defined */ 1541 /* 1542 * We set rather silly defaults of soft carrier on 1543 * and DTR/RTS raised here because it might be that 1544 * one of the motherboard ports is the system console. 1545 */ 1546 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 1547 "set ASY_IGNORE_CD, set RTS & DTR"); 1548 mcr = asy->asy_mcr; /* rts/dtr on */ 1549 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 1550 break; 1551 } 1552 1553 /* Property for not raising DTR/RTS */ 1554 switch (asy_getproperty(devi, asy, "rts-dtr-off")) { 1555 case 0: /* *-rts-dtr-off=False */ 1556 asy->asy_flags |= ASY_RTS_DTR_OFF; /* OFF */ 1557 mcr = asy->asy_mcr; /* rts/dtr on */ 1558 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 1559 "ASY_RTS_DTR_OFF set and DTR & RTS set"); 1560 break; 1561 case 1: /* *-rts-dtr-off=True */ 1562 /*FALLTHRU*/ 1563 default: /* *-rts-dtr-off undefined */ 1564 break; 1565 } 1566 1567 /* Parse property for tty modes */ 1568 asy_parse_mode(devi, asy); 1569 } else { 1570 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 1571 "clear ASY_IGNORE_CD, clear RTS & DTR"); 1572 asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */ 1573 } 1574 1575 /* 1576 * Install per instance software interrupt handler. 1577 */ 1578 if (asy_softintr_setup(asy) != DDI_SUCCESS) { 1579 asyerror(asy, CE_WARN, "Cannot set soft interrupt"); 1580 goto fail; 1581 } 1582 1583 asy->asy_progress |= ASY_PROGRESS_SOFTINT; 1584 1585 /* 1586 * Install interrupt handler for this device. 1587 */ 1588 if ((asy_intr_setup(asy, DDI_INTR_TYPE_MSIX) != DDI_SUCCESS) && 1589 (asy_intr_setup(asy, DDI_INTR_TYPE_MSI) != DDI_SUCCESS) && 1590 (asy_intr_setup(asy, DDI_INTR_TYPE_FIXED) != DDI_SUCCESS)) { 1591 asyerror(asy, CE_WARN, "Cannot set device interrupt"); 1592 goto fail; 1593 } 1594 1595 asy->asy_progress |= ASY_PROGRESS_INT; 1596 1597 /* 1598 * Initialize mutexes before accessing the hardware 1599 */ 1600 mutex_init(&asy->asy_soft_lock, NULL, MUTEX_DRIVER, 1601 DDI_INTR_PRI(asy->asy_soft_intr_pri)); 1602 mutex_init(&asy->asy_soft_sr, NULL, MUTEX_DRIVER, 1603 DDI_INTR_PRI(asy->asy_soft_intr_pri)); 1604 1605 mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, NULL); 1606 mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER, 1607 DDI_INTR_PRI(asy->asy_intr_pri)); 1608 1609 asy->asy_progress |= ASY_PROGRESS_MUTEX; 1610 1611 mutex_enter(&asy->asy_excl); 1612 mutex_enter(&asy->asy_excl_hi); 1613 1614 if (asy_identify_chip(devi, asy) != DDI_SUCCESS) { 1615 asyerror(asy, CE_WARN, "Cannot identify UART chip at %p", 1616 (void *)asy->asy_ioaddr); 1617 goto fail; 1618 } 1619 1620 asy_disable_interrupts(asy, ASY_IER_ALL); 1621 asy_put(asy, ASY_LCR, asy->asy_lcr); 1622 asy_set_baudrate(asy, asy->asy_bidx); 1623 asy_put(asy, ASY_MCR, mcr); 1624 1625 mutex_exit(&asy->asy_excl_hi); 1626 mutex_exit(&asy->asy_excl); 1627 1628 asyinit(asy); /* initialize the asyncline structure */ 1629 asy->asy_progress |= ASY_PROGRESS_ASYNC; 1630 1631 /* create minor device nodes for this device */ 1632 if (asy->asy_com_port != 0) { 1633 /* 1634 * For DOS COM ports, add letter suffix so 1635 * devfsadm can create correct link names. 1636 */ 1637 name[0] = asy->asy_com_port + 'a' - 1; 1638 name[1] = '\0'; 1639 } else { 1640 /* 1641 * asy port which isn't a standard DOS COM 1642 * port gets a numeric name based on instance 1643 */ 1644 (void) snprintf(name, ASY_MINOR_LEN, "%d", instance); 1645 } 1646 status = ddi_create_minor_node(devi, name, S_IFCHR, instance, 1647 asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB : DDI_NT_SERIAL, 0); 1648 if (status == DDI_SUCCESS) { 1649 (void) strcat(name, ",cu"); 1650 status = ddi_create_minor_node(devi, name, S_IFCHR, 1651 OUTLINE | instance, 1652 asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB_DO : 1653 DDI_NT_SERIAL_DO, 0); 1654 } 1655 1656 if (status != DDI_SUCCESS) 1657 goto fail; 1658 1659 asy->asy_progress |= ASY_PROGRESS_MINOR; 1660 1661 /* 1662 * Fill in the polled I/O structure. 1663 */ 1664 asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0; 1665 asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy; 1666 asy->polledio.cons_polledio_putchar = asyputchar; 1667 asy->polledio.cons_polledio_getchar = asygetchar; 1668 asy->polledio.cons_polledio_ischar = asyischar; 1669 asy->polledio.cons_polledio_enter = NULL; 1670 asy->polledio.cons_polledio_exit = NULL; 1671 1672 ddi_report_dev(devi); 1673 ASY_DPRINTF(asy, ASY_DEBUG_INIT, "done"); 1674 return (DDI_SUCCESS); 1675 1676 fail: 1677 (void) asydetach(devi, DDI_DETACH); 1678 return (DDI_FAILURE); 1679 } 1680 1681 static int 1682 asyinfo(dev_info_t *dip __unused, ddi_info_cmd_t infocmd, void *arg, 1683 void **result) 1684 { 1685 dev_t dev = (dev_t)arg; 1686 int instance, error; 1687 struct asycom *asy; 1688 1689 instance = UNIT(dev); 1690 1691 switch (infocmd) { 1692 case DDI_INFO_DEVT2DEVINFO: 1693 asy = ddi_get_soft_state(asy_soft_state, instance); 1694 if ((asy == NULL) || (asy->asy_dip == NULL)) 1695 error = DDI_FAILURE; 1696 else { 1697 *result = (void *) asy->asy_dip; 1698 error = DDI_SUCCESS; 1699 } 1700 break; 1701 case DDI_INFO_DEVT2INSTANCE: 1702 *result = (void *)(intptr_t)instance; 1703 error = DDI_SUCCESS; 1704 break; 1705 default: 1706 error = DDI_FAILURE; 1707 } 1708 return (error); 1709 } 1710 1711 /* asy_getproperty -- walk through all name variants until we find a match */ 1712 1713 static int 1714 asy_getproperty(dev_info_t *devi, struct asycom *asy, const char *property) 1715 { 1716 int len; 1717 int ret; 1718 char letter = asy->asy_com_port + 'a' - 1; /* for ttya */ 1719 char number = asy->asy_com_port + '0'; /* for COM1 */ 1720 char val[40]; 1721 char name[40]; 1722 1723 /* Property for ignoring DCD */ 1724 (void) sprintf(name, "tty%c-%s", letter, property); 1725 len = sizeof (val); 1726 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 1727 if (ret != DDI_PROP_SUCCESS) { 1728 (void) sprintf(name, "com%c-%s", number, property); 1729 len = sizeof (val); 1730 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 1731 } 1732 if (ret != DDI_PROP_SUCCESS) { 1733 (void) sprintf(name, "tty0%c-%s", number, property); 1734 len = sizeof (val); 1735 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 1736 } 1737 if (ret != DDI_PROP_SUCCESS) { 1738 (void) sprintf(name, "port-%c-%s", letter, property); 1739 len = sizeof (val); 1740 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 1741 } 1742 if (ret != DDI_PROP_SUCCESS) 1743 return (-1); /* property non-existant */ 1744 if (val[0] == 'f' || val[0] == 'F' || val[0] == '0') 1745 return (0); /* property false/0 */ 1746 return (1); /* property true/!0 */ 1747 } 1748 1749 /* asy_soft_state_free - local wrapper for ddi_soft_state_free(9F) */ 1750 1751 static void 1752 asy_soft_state_free(struct asycom *asy) 1753 { 1754 if (asy->asy_priv != NULL) { 1755 kmem_free(asy->asy_priv, sizeof (struct asyncline)); 1756 asy->asy_priv = NULL; 1757 } 1758 ddi_soft_state_free(asy_soft_state, asy->asy_unit); 1759 } 1760 1761 static char * 1762 asy_hw_name(struct asycom *asy) 1763 { 1764 switch (asy->asy_hwtype) { 1765 case ASY_8250A: 1766 return ("8250A/16450"); 1767 case ASY_16550: 1768 return ("16550"); 1769 case ASY_16550A: 1770 return ("16550A"); 1771 case ASY_16650: 1772 return ("16650"); 1773 case ASY_16750: 1774 return ("16750"); 1775 case ASY_16950: 1776 return ("16950"); 1777 } 1778 1779 ASY_DPRINTF(asy, ASY_DEBUG_INIT, "unknown asy_hwtype: %d", 1780 asy->asy_hwtype); 1781 return ("?"); 1782 } 1783 1784 static boolean_t 1785 asy_is_devid(struct asycom *asy, char *venprop, char *devprop, 1786 int venid, int devid) 1787 { 1788 if (ddi_prop_get_int(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS, 1789 venprop, 0) != venid) { 1790 return (B_FALSE); 1791 } 1792 1793 if (ddi_prop_get_int(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS, 1794 devprop, 0) != devid) { 1795 return (B_FALSE); 1796 } 1797 1798 return (B_FALSE); 1799 } 1800 1801 static void 1802 asy_check_loopback(struct asycom *asy) 1803 { 1804 if (asy_get_bus_type(asy->asy_dip) != ASY_BUS_PCI) 1805 return; 1806 1807 /* Check if this is a Agere/Lucent Venus PCI modem chipset. */ 1808 if (asy_is_devid(asy, "vendor-id", "device-id", 0x11c1, 0x0480) || 1809 asy_is_devid(asy, "subsystem-vendor-id", "subsystem-id", 0x11c1, 1810 0x0480)) 1811 asy->asy_flags2 |= ASY2_NO_LOOPBACK; 1812 } 1813 1814 static int 1815 asy_identify_chip(dev_info_t *devi, struct asycom *asy) 1816 { 1817 int isr, lsr, mcr, spr; 1818 dev_t dev; 1819 uint_t hwtype; 1820 1821 /* 1822 * Initially, we'll assume we have the highest supported chip model 1823 * until we find out what we actually have. 1824 */ 1825 asy->asy_hwtype = ASY_MAXCHIP; 1826 1827 /* 1828 * First, see if we can even do the loopback check, which may not work 1829 * on certain hardware. 1830 */ 1831 asy_check_loopback(asy); 1832 1833 if (asy_scr_test) { 1834 /* Check that the scratch register works. */ 1835 1836 /* write to scratch register */ 1837 asy_put(asy, ASY_SPR, ASY_SPR_TEST); 1838 /* make sure that pattern doesn't just linger on the bus */ 1839 asy_put(asy, ASY_FCR, 0x00); 1840 /* read data back from scratch register */ 1841 spr = asy_get(asy, ASY_SPR); 1842 if (spr != ASY_SPR_TEST) { 1843 /* 1844 * Scratch register not working. 1845 * Probably not an async chip. 1846 * 8250 and 8250B don't have scratch registers, 1847 * but only worked in ancient PC XT's anyway. 1848 */ 1849 asyerror(asy, CE_WARN, "UART @ %p " 1850 "scratch register: expected 0x5a, got 0x%02x", 1851 (void *)asy->asy_ioaddr, spr); 1852 return (DDI_FAILURE); 1853 } 1854 } 1855 /* 1856 * Use 16550 fifo reset sequence specified in NS application 1857 * note. Disable fifos until chip is initialized. 1858 */ 1859 asy_put(asy, ASY_FCR, 0x00); /* disable */ 1860 asy_put(asy, ASY_FCR, ASY_FCR_FIFO_EN); /* enable */ 1861 asy_put(asy, ASY_FCR, ASY_FCR_FIFO_EN | ASY_FCR_RHR_FL); /* reset */ 1862 if (asymaxchip >= ASY_16650 && asy_scr_test) { 1863 /* 1864 * Reset 16650 enhanced regs also, in case we have one of these 1865 */ 1866 asy_put(asy, ASY_EFR, 0); 1867 } 1868 1869 /* 1870 * See what sort of FIFO we have. 1871 * Try enabling it and see what chip makes of this. 1872 */ 1873 1874 asy->asy_fifor = 0; 1875 if (asymaxchip >= ASY_16550A) 1876 asy->asy_fifor |= 1877 ASY_FCR_FIFO_EN | ASY_FCR_DMA | (asy_trig_level & 0xff); 1878 1879 /* 1880 * On the 16750, FCR[5] enables the 64 byte FIFO. FCR[5] can only be set 1881 * while LCR[7] = 1 (DLAB), which is taken care of by asy_reset_fifo(). 1882 */ 1883 if (asymaxchip >= ASY_16750) 1884 asy->asy_fifor |= ASY_FCR_FIFO64; 1885 1886 asy_reset_fifo(asy, ASY_FCR_THR_FL | ASY_FCR_RHR_FL); 1887 1888 mcr = asy_get(asy, ASY_MCR); 1889 isr = asy_get(asy, ASY_ISR); 1890 1891 /* 1892 * Note we get 0xff if chip didn't return us anything, 1893 * e.g. if there's no chip there. 1894 */ 1895 if (isr == 0xff) { 1896 asyerror(asy, CE_WARN, "UART @ %p interrupt register: got 0xff", 1897 (void *)asy->asy_ioaddr); 1898 return (DDI_FAILURE); 1899 } 1900 1901 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, 1902 "probe fifo FIFOR=0x%02x ISR=0x%02x MCR=0x%02x", 1903 asy->asy_fifor | ASY_FCR_THR_FL | ASY_FCR_RHR_FL, isr, mcr); 1904 1905 /* 1906 * Detect the chip type by comparing ISR[7,6] and ISR[5]. 1907 * 1908 * When the FIFOs are enabled by setting FCR[0], ISR[7,6] read as 1. 1909 * Additionally on a 16750, the 64 byte FIFOs are enabled by setting 1910 * FCR[5], and ISR[5] will read as 1, too. 1911 * 1912 * We will check later whether we have a 16650, which requires EFR[4]=1 1913 * to enable its deeper FIFOs and extra features. It does not use FCR[5] 1914 * and ISR[5] to enable deeper FIFOs like the 16750 does. 1915 */ 1916 switch (isr & (ASY_ISR_FIFOEN | ASY_ISR_FIFO64)) { 1917 case 0x40: /* 16550 with broken FIFOs */ 1918 hwtype = ASY_16550; 1919 asy->asy_fifor = 0; 1920 break; 1921 1922 case ASY_ISR_FIFOEN: /* 16550A with working FIFOs */ 1923 hwtype = ASY_16550A; 1924 asy->asy_fifo_buf = 16; 1925 asy->asy_use_fifo = ASY_FCR_FIFO_EN; 1926 asy->asy_fifor &= ~ASY_FCR_FIFO64; 1927 break; 1928 1929 case ASY_ISR_FIFOEN | ASY_ISR_FIFO64: /* 16750 with 64byte FIFOs */ 1930 hwtype = ASY_16750; 1931 asy->asy_fifo_buf = 64; 1932 asy->asy_use_fifo = ASY_FCR_FIFO_EN; 1933 break; 1934 1935 default: /* 8250A/16450 without FIFOs */ 1936 hwtype = ASY_8250A; 1937 asy->asy_fifor = 0; 1938 } 1939 1940 if (hwtype > asymaxchip) { 1941 asyerror(asy, CE_WARN, "UART @ %p " 1942 "unexpected probe result: " 1943 "FCR=0x%02x ISR=0x%02x MCR=0x%02x", 1944 (void *)asy->asy_ioaddr, 1945 asy->asy_fifor | ASY_FCR_THR_FL | ASY_FCR_RHR_FL, isr, mcr); 1946 return (DDI_FAILURE); 1947 } 1948 1949 /* 1950 * Now reset the FIFO operation appropriate for the chip type. 1951 * Note we must call asy_reset_fifo() before any possible 1952 * downgrade of the asy->asy_hwtype, or it may not disable 1953 * the more advanced features we specifically want downgraded. 1954 */ 1955 asy_reset_fifo(asy, 0); 1956 1957 /* 1958 * Check for Exar/Startech ST16C650 or newer, which will still look like 1959 * a 16550A until we enable its enhanced mode. 1960 */ 1961 if (hwtype >= ASY_16550A && asymaxchip >= ASY_16650 && 1962 asy_scr_test) { 1963 /* 1964 * Write the XOFF2 register, which shadows SPR on the 16650. 1965 * On other chips, SPR will be overwritten. 1966 */ 1967 asy_put(asy, ASY_XOFF2, 0); 1968 1969 /* read back scratch register */ 1970 spr = asy_get(asy, ASY_SPR); 1971 1972 if (spr == ASY_SPR_TEST) { 1973 /* looks like we have an ST16650 -- enable it */ 1974 hwtype = ASY_16650; 1975 asy_put(asy, ASY_EFR, ASY_EFR_ENH_EN); 1976 1977 /* 1978 * Some 16650-compatible chips are also compatible with 1979 * the 16750 and have deeper FIFOs, which we may have 1980 * detected above. Don't downgrade the FIFO size. 1981 */ 1982 if (asy->asy_fifo_buf < 32) 1983 asy->asy_fifo_buf = 32; 1984 1985 /* 1986 * Use a 24 byte transmit FIFO trigger only if were 1987 * allowed to use >16 transmit FIFO depth by the 1988 * global tunable. 1989 */ 1990 if (asy_max_tx_fifo >= asy->asy_fifo_buf) 1991 asy->asy_fifor |= ASY_FCR_THR_TRIG_24; 1992 asy_reset_fifo(asy, 0); 1993 } 1994 } 1995 1996 /* 1997 * If we think we got a 16650, we may actually have a 16950, so check 1998 * for that. 1999 */ 2000 if (hwtype >= ASY_16650 && asymaxchip >= ASY_16950) { 2001 uint8_t ier, asr; 2002 2003 /* 2004 * First, clear IER and read it back. That should be a no-op as 2005 * either asyattach() or asy_resume() disabled all interrupts 2006 * before we were called. 2007 */ 2008 asy_put(asy, ASY_IER, 0); 2009 ier = asy_get(asy, ASY_IER); 2010 if (ier != 0) { 2011 dev_err(asy->asy_dip, CE_WARN, "!%s: UART @ %p " 2012 "interrupt enable register: got 0x%02x", __func__, 2013 (void *)asy->asy_ioaddr, ier); 2014 return (DDI_FAILURE); 2015 } 2016 2017 /* 2018 * Next, try to read ASR, which shares the register offset with 2019 * IER. ASR can only be read if the ASR enable bit is set in 2020 * ACR, which itself is an indexed registers. This is taken care 2021 * of by asy_get(). 2022 * 2023 * There are a few bits in ASR which should be 1 at this point, 2024 * definitely the TX idle bit (ASR[7]) and also the FIFO size 2025 * bit (ASR[6]) since we've done everything we can to enable any 2026 * deeper FIFO support. 2027 * 2028 * Thus if we read back ASR as 0, we failed to read it, and this 2029 * isn't the chip we're looking for. 2030 */ 2031 asr = asy_get(asy, ASY_ASR); 2032 2033 if (asr != ier) { 2034 hwtype = ASY_16950; 2035 2036 if ((asr & ASY_ASR_FIFOSZ) != 0) 2037 asy->asy_fifo_buf = 128; 2038 else 2039 asy->asy_fifo_buf = 16; 2040 2041 asy_reset_fifo(asy, 0); 2042 2043 /* 2044 * Enable 16950 specific trigger level registers. Set 2045 * DTR pin to be compatible to 16450, 16550, and 16750. 2046 */ 2047 asy->asy_acr = ASY_ACR_TRIG | ASY_ACR_DTR_NORM; 2048 asy_put(asy, ASY_ACR, asy->asy_acr); 2049 2050 /* Set half the FIFO size as receive trigger level. */ 2051 asy_put(asy, ASY_RTL, asy->asy_fifo_buf/2); 2052 2053 /* 2054 * Set the transmit trigger level to 1. 2055 * 2056 * While one would expect that any transmit trigger 2057 * level would work (the 16550 uses a hardwired level 2058 * of 16), in my tests with a 16950 compatible chip 2059 * (MosChip 9912) I would never see a TX interrupt 2060 * on any transmit trigger level > 1. 2061 */ 2062 asy_put(asy, ASY_TTL, 1); 2063 2064 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "ASR 0x%02x", asr); 2065 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "RFL 0x%02x", 2066 asy_get(asy, ASY_RFL)); 2067 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "TFL 0x%02x", 2068 asy_get(asy, ASY_TFL)); 2069 2070 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "ACR 0x%02x", 2071 asy_get(asy, ASY_ACR)); 2072 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "CPR 0x%02x", 2073 asy_get(asy, ASY_CPR)); 2074 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "TCR 0x%02x", 2075 asy_get(asy, ASY_TCR)); 2076 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "CKS 0x%02x", 2077 asy_get(asy, ASY_CKS)); 2078 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "TTL 0x%02x", 2079 asy_get(asy, ASY_TTL)); 2080 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "RTL 0x%02x", 2081 asy_get(asy, ASY_RTL)); 2082 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "FCL 0x%02x", 2083 asy_get(asy, ASY_FCL)); 2084 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "FCH 0x%02x", 2085 asy_get(asy, ASY_FCH)); 2086 2087 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, 2088 "Chip ID: %02x%02x%02x,%02x", 2089 asy_get(asy, ASY_ID1), asy_get(asy, ASY_ID2), 2090 asy_get(asy, ASY_ID3), asy_get(asy, ASY_REV)); 2091 2092 } 2093 } 2094 2095 asy->asy_hwtype = hwtype; 2096 2097 /* 2098 * If we think we might have a FIFO larger than 16 characters, 2099 * measure FIFO size and check it against expected. 2100 */ 2101 if (asy_fifo_test > 0 && 2102 !(asy->asy_flags2 & ASY2_NO_LOOPBACK) && 2103 (asy->asy_fifo_buf > 16 || 2104 (asy_fifo_test > 1 && asy->asy_use_fifo == ASY_FCR_FIFO_EN) || 2105 ASY_DEBUG(asy, ASY_DEBUG_CHIP))) { 2106 int i; 2107 2108 /* Set baud rate to 57600 (fairly arbitrary choice) */ 2109 asy_set_baudrate(asy, B57600); 2110 /* Set 8 bits, 1 stop bit */ 2111 asy_put(asy, ASY_LCR, ASY_LCR_STOP1 | ASY_LCR_BITS8); 2112 /* Set loopback mode */ 2113 asy_put(asy, ASY_MCR, ASY_MCR_LOOPBACK); 2114 2115 /* Overfill fifo */ 2116 for (i = 0; i < asy->asy_fifo_buf * 2; i++) { 2117 asy_put(asy, ASY_THR, i); 2118 } 2119 /* 2120 * Now there's an interesting question here about which 2121 * FIFO we're testing the size of, RX or TX. We just 2122 * filled the TX FIFO much faster than it can empty, 2123 * although it is possible one or two characters may 2124 * have gone from it to the TX shift register. 2125 * We wait for enough time for all the characters to 2126 * move into the RX FIFO and any excess characters to 2127 * have been lost, and then read all the RX FIFO. So 2128 * the answer we finally get will be the size which is 2129 * the MIN(RX FIFO,(TX FIFO + 1 or 2)). The critical 2130 * one is actually the TX FIFO, because if we overfill 2131 * it in normal operation, the excess characters are 2132 * lost with no warning. 2133 */ 2134 /* 2135 * Wait for characters to move into RX FIFO. 2136 * In theory, 200 * asy->asy_fifo_buf * 2 should be 2137 * enough. However, in practice it isn't always, so we 2138 * increase to 400 so some slow 16550A's finish, and we 2139 * increase to 3 so we spot more characters coming back 2140 * than we sent, in case that should ever happen. 2141 */ 2142 delay(drv_usectohz(400 * asy->asy_fifo_buf * 3)); 2143 2144 /* Now see how many characters we can read back */ 2145 for (i = 0; i < asy->asy_fifo_buf * 3; i++) { 2146 lsr = asy_get(asy, ASY_LSR); 2147 if (!(lsr & ASY_LSR_DR)) 2148 break; /* FIFO emptied */ 2149 (void) asy_get(asy, ASY_RHR); /* lose another */ 2150 } 2151 2152 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, 2153 "FIFO size: expected=%d, measured=%d", 2154 asy->asy_fifo_buf, i); 2155 2156 hwtype = asy->asy_hwtype; 2157 if (i < asy->asy_fifo_buf) { 2158 /* 2159 * FIFO is somewhat smaller than we anticipated. 2160 * If we have 16 characters usable, then this 2161 * UART will probably work well enough in 2162 * 16550A mode. If less than 16 characters, 2163 * then we'd better not use it at all. 2164 * UARTs with busted FIFOs do crop up. 2165 */ 2166 if (i >= 16 && asy->asy_fifo_buf >= 16) { 2167 /* fall back to a 16550A */ 2168 hwtype = ASY_16550A; 2169 asy->asy_fifo_buf = 16; 2170 asy->asy_fifor &= 2171 ~(ASY_FCR_THR_TR0 | ASY_FCR_THR_TR1); 2172 } else { 2173 /* fall back to no FIFO at all */ 2174 hwtype = ASY_16550; 2175 asy->asy_fifo_buf = 1; 2176 asy->asy_use_fifo = ASY_FCR_FIFO_OFF; 2177 asy->asy_fifor = 0; 2178 } 2179 } else if (i > asy->asy_fifo_buf) { 2180 /* 2181 * The FIFO is larger than expected. Use it if it is 2182 * a power of 2. 2183 */ 2184 if (ISP2(i)) 2185 asy->asy_fifo_buf = i; 2186 } 2187 2188 /* 2189 * We will need to reprogram the FIFO if we changed 2190 * our mind about how to drive it above, and in any 2191 * case, it would be a good idea to flush any garbage 2192 * out incase the loopback test left anything behind. 2193 * Again as earlier above, we must call asy_reset_fifo() 2194 * before any possible downgrade of asy->asy_hwtype. 2195 */ 2196 if (asy->asy_hwtype >= ASY_16650 && hwtype < ASY_16650) { 2197 /* Disable 16650 enhanced mode */ 2198 asy_put(asy, ASY_EFR, 0); 2199 } 2200 asy_reset_fifo(asy, ASY_FCR_THR_FL | ASY_FCR_RHR_FL); 2201 asy->asy_hwtype = hwtype; 2202 2203 /* Clear loopback mode and restore DTR/RTS */ 2204 asy_put(asy, ASY_MCR, mcr); 2205 } 2206 2207 ASY_DPRINTF(asy, ASY_DEBUG_CHIP, "%s @ %p", 2208 asy_hw_name(asy), (void *)asy->asy_ioaddr); 2209 2210 /* Make UART type visible in device tree for prtconf, etc */ 2211 dev = makedevice(DDI_MAJOR_T_UNKNOWN, asy->asy_unit); 2212 (void) ddi_prop_update_string(dev, devi, "uart", asy_hw_name(asy)); 2213 2214 if (asy->asy_hwtype == ASY_16550) /* for broken 16550's, */ 2215 asy->asy_hwtype = ASY_8250A; /* drive them as 8250A */ 2216 2217 return (DDI_SUCCESS); 2218 } 2219 2220 /* 2221 * asyinit() initializes the TTY protocol-private data for this channel 2222 * before enabling the interrupts. 2223 */ 2224 static void 2225 asyinit(struct asycom *asy) 2226 { 2227 struct asyncline *async; 2228 2229 asy->asy_priv = kmem_zalloc(sizeof (struct asyncline), KM_SLEEP); 2230 async = asy->asy_priv; 2231 mutex_enter(&asy->asy_excl); 2232 async->async_common = asy; 2233 cv_init(&async->async_flags_cv, NULL, CV_DRIVER, NULL); 2234 mutex_exit(&asy->asy_excl); 2235 } 2236 2237 static int 2238 asyopen(queue_t *rq, dev_t *dev, int flag, int sflag __unused, cred_t *cr) 2239 { 2240 struct asycom *asy; 2241 struct asyncline *async; 2242 int unit; 2243 int len; 2244 struct termios *termiosp; 2245 2246 unit = UNIT(*dev); 2247 asy = ddi_get_soft_state(asy_soft_state, unit); 2248 if (asy == NULL) 2249 return (ENXIO); /* unit not configured */ 2250 ASY_DPRINTF(asy, ASY_DEBUG_CLOSE, "enter"); 2251 async = asy->asy_priv; 2252 mutex_enter(&asy->asy_excl); 2253 2254 again: 2255 mutex_enter(&asy->asy_excl_hi); 2256 2257 /* 2258 * Block waiting for carrier to come up, unless this is a no-delay open. 2259 */ 2260 if (!(async->async_flags & ASYNC_ISOPEN)) { 2261 /* 2262 * Set the default termios settings (cflag). 2263 * Others are set in ldterm. 2264 */ 2265 mutex_exit(&asy->asy_excl_hi); 2266 2267 if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 2268 0, "ttymodes", 2269 (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS && 2270 len == sizeof (struct termios)) { 2271 async->async_ttycommon.t_cflag = termiosp->c_cflag; 2272 kmem_free(termiosp, len); 2273 } else { 2274 asyerror(asy, CE_WARN, 2275 "couldn't get ttymodes property"); 2276 } 2277 mutex_enter(&asy->asy_excl_hi); 2278 2279 /* eeprom mode support - respect properties */ 2280 if (asy->asy_cflag) 2281 async->async_ttycommon.t_cflag = asy->asy_cflag; 2282 2283 async->async_ttycommon.t_iflag = 0; 2284 async->async_ttycommon.t_iocpending = NULL; 2285 async->async_ttycommon.t_size.ws_row = 0; 2286 async->async_ttycommon.t_size.ws_col = 0; 2287 async->async_ttycommon.t_size.ws_xpixel = 0; 2288 async->async_ttycommon.t_size.ws_ypixel = 0; 2289 async->async_dev = *dev; 2290 async->async_wbufcid = 0; 2291 2292 async->async_startc = CSTART; 2293 async->async_stopc = CSTOP; 2294 asy_program(asy, ASY_INIT); 2295 } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) && 2296 secpolicy_excl_open(cr) != 0) { 2297 mutex_exit(&asy->asy_excl_hi); 2298 mutex_exit(&asy->asy_excl); 2299 return (EBUSY); 2300 } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) { 2301 mutex_exit(&asy->asy_excl_hi); 2302 mutex_exit(&asy->asy_excl); 2303 return (EBUSY); 2304 } 2305 2306 if (*dev & OUTLINE) 2307 async->async_flags |= ASYNC_OUT; 2308 2309 /* Raise DTR on every open, but delay if it was just lowered. */ 2310 while (async->async_flags & ASYNC_DTR_DELAY) { 2311 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 2312 "waiting for the ASYNC_DTR_DELAY to be clear"); 2313 mutex_exit(&asy->asy_excl_hi); 2314 if (cv_wait_sig(&async->async_flags_cv, 2315 &asy->asy_excl) == 0) { 2316 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 2317 "interrupted by signal, exiting"); 2318 mutex_exit(&asy->asy_excl); 2319 return (EINTR); 2320 } 2321 mutex_enter(&asy->asy_excl_hi); 2322 } 2323 2324 asy_set(asy, ASY_MCR, asy->asy_mcr & ASY_MCR_DTR); 2325 2326 ASY_DPRINTF(asy, ASY_DEBUG_INIT, "\"Raise DTR on every open\": " 2327 "make mcr = %x, make TS_SOFTCAR = %s", asy_get(asy, ASY_MCR), 2328 (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF"); 2329 2330 if (asy->asy_flags & ASY_IGNORE_CD) { 2331 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 2332 "ASY_IGNORE_CD set, set TS_SOFTCAR"); 2333 async->async_ttycommon.t_flags |= TS_SOFTCAR; 2334 } 2335 else 2336 async->async_ttycommon.t_flags &= ~TS_SOFTCAR; 2337 2338 /* 2339 * Check carrier. 2340 */ 2341 asy->asy_msr = asy_get(asy, ASY_MSR); 2342 ASY_DPRINTF(asy, ASY_DEBUG_INIT, "TS_SOFTCAR is %s, MSR & DCD is %s", 2343 (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear", 2344 (asy->asy_msr & ASY_MSR_DCD) ? "set" : "clear"); 2345 2346 if (asy->asy_msr & ASY_MSR_DCD) 2347 async->async_flags |= ASYNC_CARR_ON; 2348 else 2349 async->async_flags &= ~ASYNC_CARR_ON; 2350 mutex_exit(&asy->asy_excl_hi); 2351 2352 /* 2353 * If FNDELAY and FNONBLOCK are clear, block until carrier up. 2354 * Quit on interrupt. 2355 */ 2356 if (!(flag & (FNDELAY|FNONBLOCK)) && 2357 !(async->async_ttycommon.t_cflag & CLOCAL)) { 2358 if ((!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) && 2359 !(async->async_ttycommon.t_flags & TS_SOFTCAR)) || 2360 ((async->async_flags & ASYNC_OUT) && 2361 !(*dev & OUTLINE))) { 2362 async->async_flags |= ASYNC_WOPEN; 2363 if (cv_wait_sig(&async->async_flags_cv, 2364 &asy->asy_excl) == B_FALSE) { 2365 async->async_flags &= ~ASYNC_WOPEN; 2366 mutex_exit(&asy->asy_excl); 2367 return (EINTR); 2368 } 2369 async->async_flags &= ~ASYNC_WOPEN; 2370 goto again; 2371 } 2372 } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) { 2373 mutex_exit(&asy->asy_excl); 2374 return (EBUSY); 2375 } 2376 2377 async->async_ttycommon.t_readq = rq; 2378 async->async_ttycommon.t_writeq = WR(rq); 2379 rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async; 2380 mutex_exit(&asy->asy_excl); 2381 /* 2382 * Caution here -- qprocson sets the pointers that are used by canput 2383 * called by async_softint. ASYNC_ISOPEN must *not* be set until those 2384 * pointers are valid. 2385 */ 2386 qprocson(rq); 2387 async->async_flags |= ASYNC_ISOPEN; 2388 async->async_polltid = 0; 2389 ASY_DPRINTF(asy, ASY_DEBUG_INIT, "done"); 2390 return (0); 2391 } 2392 2393 static void 2394 async_progress_check(void *arg) 2395 { 2396 struct asyncline *async = arg; 2397 struct asycom *asy = async->async_common; 2398 mblk_t *bp; 2399 2400 /* 2401 * We define "progress" as either waiting on a timed break or delay, or 2402 * having had at least one transmitter interrupt. If none of these are 2403 * true, then just terminate the output and wake up that close thread. 2404 */ 2405 mutex_enter(&asy->asy_excl); 2406 mutex_enter(&asy->asy_excl_hi); 2407 if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) { 2408 async->async_ocnt = 0; 2409 async->async_flags &= ~ASYNC_BUSY; 2410 async->async_timer = 0; 2411 bp = async->async_xmitblk; 2412 async->async_xmitblk = NULL; 2413 mutex_exit(&asy->asy_excl_hi); 2414 if (bp != NULL) 2415 freeb(bp); 2416 /* 2417 * Since this timer is running, we know that we're in exit(2). 2418 * That means that the user can't possibly be waiting on any 2419 * valid ioctl(2) completion anymore, and we should just flush 2420 * everything. 2421 */ 2422 flushq(async->async_ttycommon.t_writeq, FLUSHALL); 2423 cv_broadcast(&async->async_flags_cv); 2424 } else { 2425 async->async_flags &= ~ASYNC_PROGRESS; 2426 async->async_timer = timeout(async_progress_check, async, 2427 drv_usectohz(asy_drain_check)); 2428 mutex_exit(&asy->asy_excl_hi); 2429 } 2430 mutex_exit(&asy->asy_excl); 2431 } 2432 2433 /* 2434 * Release DTR so that asyopen() can raise it. 2435 */ 2436 static void 2437 async_dtr_free(struct asyncline *async) 2438 { 2439 struct asycom *asy = async->async_common; 2440 2441 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 2442 "async_dtr_free, clearing ASYNC_DTR_DELAY"); 2443 mutex_enter(&asy->asy_excl); 2444 async->async_flags &= ~ASYNC_DTR_DELAY; 2445 async->async_dtrtid = 0; 2446 cv_broadcast(&async->async_flags_cv); 2447 mutex_exit(&asy->asy_excl); 2448 } 2449 2450 /* 2451 * Close routine. 2452 */ 2453 static int 2454 asyclose(queue_t *q, int flag, cred_t *credp __unused) 2455 { 2456 struct asyncline *async; 2457 struct asycom *asy; 2458 2459 async = (struct asyncline *)q->q_ptr; 2460 ASSERT(async != NULL); 2461 2462 asy = async->async_common; 2463 2464 ASY_DPRINTF(asy, ASY_DEBUG_CLOSE, "enter"); 2465 2466 mutex_enter(&asy->asy_excl); 2467 async->async_flags |= ASYNC_CLOSING; 2468 2469 /* 2470 * Turn off PPS handling early to avoid events occuring during 2471 * close. Also reset the DCD edge monitoring bit. 2472 */ 2473 mutex_enter(&asy->asy_excl_hi); 2474 asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE); 2475 mutex_exit(&asy->asy_excl_hi); 2476 2477 /* 2478 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and 2479 * untimed (TIOCSBRK). For the timed case, these are enqueued on our 2480 * write queue and there's a timer running, so we don't have to worry 2481 * about them. For the untimed case, though, the user obviously made a 2482 * mistake, because these are handled immediately. We'll terminate the 2483 * break now and honor their implicit request by discarding the rest of 2484 * the data. 2485 */ 2486 if (async->async_flags & ASYNC_OUT_SUSPEND) { 2487 if (async->async_utbrktid != 0) { 2488 (void) untimeout(async->async_utbrktid); 2489 async->async_utbrktid = 0; 2490 } 2491 mutex_enter(&asy->asy_excl_hi); 2492 (void) asy_clr(asy, ASY_LCR, ASY_LCR_SETBRK); 2493 mutex_exit(&asy->asy_excl_hi); 2494 async->async_flags &= ~ASYNC_OUT_SUSPEND; 2495 goto nodrain; 2496 } 2497 2498 /* 2499 * If the user told us not to delay the close ("non-blocking"), then 2500 * don't bother trying to drain. 2501 * 2502 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever 2503 * getting an M_START (since these messages aren't enqueued), and the 2504 * only other way to clear the stop condition is by loss of DCD, which 2505 * would discard the queue data. Thus, we drop the output data if 2506 * ASYNC_STOPPED is set. 2507 */ 2508 if ((flag & (FNDELAY|FNONBLOCK)) || 2509 (async->async_flags & ASYNC_STOPPED)) { 2510 goto nodrain; 2511 } 2512 2513 /* 2514 * If there's any pending output, then we have to try to drain it. 2515 * There are two main cases to be handled: 2516 * - called by close(2): need to drain until done or until 2517 * a signal is received. No timeout. 2518 * - called by exit(2): need to drain while making progress 2519 * or until a timeout occurs. No signals. 2520 * 2521 * If we can't rely on receiving a signal to get us out of a hung 2522 * session, then we have to use a timer. In this case, we set a timer 2523 * to check for progress in sending the output data -- all that we ask 2524 * (at each interval) is that there's been some progress made. Since 2525 * the interrupt routine grabs buffers from the write queue, we can't 2526 * trust changes in async_ocnt. Instead, we use a progress flag. 2527 * 2528 * Note that loss of carrier will cause the output queue to be flushed, 2529 * and we'll wake up again and finish normally. 2530 */ 2531 if (!ddi_can_receive_sig() && asy_drain_check != 0) { 2532 async->async_flags &= ~ASYNC_PROGRESS; 2533 async->async_timer = timeout(async_progress_check, async, 2534 drv_usectohz(asy_drain_check)); 2535 } 2536 while (async->async_ocnt > 0 || 2537 async->async_ttycommon.t_writeq->q_first != NULL || 2538 (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) { 2539 if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) == 0) 2540 break; 2541 } 2542 if (async->async_timer != 0) { 2543 (void) untimeout(async->async_timer); 2544 async->async_timer = 0; 2545 } 2546 2547 nodrain: 2548 async->async_ocnt = 0; 2549 if (async->async_xmitblk != NULL) 2550 freeb(async->async_xmitblk); 2551 async->async_xmitblk = NULL; 2552 2553 /* 2554 * If line has HUPCL set or is incompletely opened fix up the modem 2555 * lines. 2556 */ 2557 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "next check HUPCL flag"); 2558 mutex_enter(&asy->asy_excl_hi); 2559 if ((async->async_ttycommon.t_cflag & HUPCL) || 2560 (async->async_flags & ASYNC_WOPEN)) { 2561 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 2562 "HUPCL flag = %x, ASYNC_WOPEN flag = %x", 2563 async->async_ttycommon.t_cflag & HUPCL, 2564 async->async_ttycommon.t_cflag & ASYNC_WOPEN); 2565 async->async_flags |= ASYNC_DTR_DELAY; 2566 2567 /* turn off DTR, RTS but NOT interrupt to 386 */ 2568 if (asy->asy_flags & (ASY_IGNORE_CD|ASY_RTS_DTR_OFF)) { 2569 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 2570 "ASY_IGNORE_CD flag = %x, " 2571 "ASY_RTS_DTR_OFF flag = %x", 2572 asy->asy_flags & ASY_IGNORE_CD, 2573 asy->asy_flags & ASY_RTS_DTR_OFF); 2574 2575 asy_put(asy, ASY_MCR, asy->asy_mcr | ASY_MCR_OUT2); 2576 } else { 2577 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 2578 "Dropping DTR and RTS"); 2579 asy_put(asy, ASY_MCR, ASY_MCR_OUT2); 2580 } 2581 async->async_dtrtid = 2582 timeout((void (*)())async_dtr_free, 2583 (caddr_t)async, drv_usectohz(asy_min_dtr_low)); 2584 } 2585 /* 2586 * If nobody's using it now, turn off receiver interrupts. 2587 */ 2588 if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) 2589 asy_disable_interrupts(asy, ASY_IER_RIEN); 2590 2591 mutex_exit(&asy->asy_excl_hi); 2592 2593 ttycommon_close(&async->async_ttycommon); 2594 2595 /* 2596 * Cancel outstanding "bufcall" request. 2597 */ 2598 if (async->async_wbufcid != 0) { 2599 unbufcall(async->async_wbufcid); 2600 async->async_wbufcid = 0; 2601 } 2602 2603 /* Note that qprocsoff can't be done until after interrupts are off */ 2604 qprocsoff(q); 2605 q->q_ptr = WR(q)->q_ptr = NULL; 2606 async->async_ttycommon.t_readq = NULL; 2607 async->async_ttycommon.t_writeq = NULL; 2608 2609 /* 2610 * Clear out device state, except persistant device property flags. 2611 */ 2612 async->async_flags &= (ASYNC_DTR_DELAY|ASY_RTS_DTR_OFF); 2613 cv_broadcast(&async->async_flags_cv); 2614 mutex_exit(&asy->asy_excl); 2615 2616 ASY_DPRINTF(asy, ASY_DEBUG_CLOSE, "done"); 2617 return (0); 2618 } 2619 2620 static boolean_t 2621 asy_isbusy(struct asycom *asy) 2622 { 2623 struct asyncline *async; 2624 2625 ASY_DPRINTF(asy, ASY_DEBUG_EOT, "enter"); 2626 async = asy->asy_priv; 2627 ASSERT(mutex_owned(&asy->asy_excl)); 2628 ASSERT(mutex_owned(&asy->asy_excl_hi)); 2629 /* 2630 * XXXX this should be recoded 2631 */ 2632 return ((async->async_ocnt > 0) || 2633 ((asy_get(asy, ASY_LSR) & (ASY_LSR_TEMT | ASY_LSR_THRE)) == 0)); 2634 } 2635 2636 static void 2637 asy_waiteot(struct asycom *asy) 2638 { 2639 /* 2640 * Wait for the current transmission block and the 2641 * current fifo data to transmit. Once this is done 2642 * we may go on. 2643 */ 2644 ASY_DPRINTF(asy, ASY_DEBUG_EOT, "enter"); 2645 ASSERT(mutex_owned(&asy->asy_excl)); 2646 ASSERT(mutex_owned(&asy->asy_excl_hi)); 2647 while (asy_isbusy(asy)) { 2648 mutex_exit(&asy->asy_excl_hi); 2649 mutex_exit(&asy->asy_excl); 2650 drv_usecwait(10000); /* wait .01 */ 2651 mutex_enter(&asy->asy_excl); 2652 mutex_enter(&asy->asy_excl_hi); 2653 } 2654 } 2655 2656 /* asy_reset_fifo -- flush fifos and [re]program fifo control register */ 2657 static void 2658 asy_reset_fifo(struct asycom *asy, uchar_t flush) 2659 { 2660 ASSERT(mutex_owned(&asy->asy_excl_hi)); 2661 2662 /* On a 16750, we have to set DLAB in order to set ASY_FCR_FIFO64. */ 2663 if (asy->asy_hwtype >= ASY_16750) 2664 asy_set(asy, ASY_LCR, ASY_LCR_DLAB); 2665 2666 asy_put(asy, ASY_FCR, asy->asy_fifor | flush); 2667 2668 /* Clear DLAB */ 2669 if (asy->asy_hwtype >= ASY_16750) 2670 asy_clr(asy, ASY_LCR, ASY_LCR_DLAB); 2671 } 2672 2673 /* 2674 * Program the ASY port. Most of the async operation is based on the values 2675 * of 'c_iflag' and 'c_cflag'. 2676 */ 2677 static void 2678 asy_program(struct asycom *asy, int mode) 2679 { 2680 struct asyncline *async; 2681 int baudrate, c_flag; 2682 uint8_t ier; 2683 int flush_reg; 2684 int ocflags; 2685 2686 ASSERT(mutex_owned(&asy->asy_excl)); 2687 ASSERT(mutex_owned(&asy->asy_excl_hi)); 2688 2689 async = asy->asy_priv; 2690 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "mode = 0x%08X, enter", mode); 2691 2692 baudrate = BAUDINDEX(async->async_ttycommon.t_cflag); 2693 2694 async->async_ttycommon.t_cflag &= ~(CIBAUD); 2695 2696 if (baudrate > CBAUD) { 2697 async->async_ttycommon.t_cflag |= CIBAUDEXT; 2698 async->async_ttycommon.t_cflag |= 2699 (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD); 2700 } else { 2701 async->async_ttycommon.t_cflag &= ~CIBAUDEXT; 2702 async->async_ttycommon.t_cflag |= 2703 ((baudrate << IBSHIFT) & CIBAUD); 2704 } 2705 2706 c_flag = async->async_ttycommon.t_cflag & 2707 (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT); 2708 2709 asy_disable_interrupts(asy, ASY_IER_ALL); 2710 2711 ocflags = asy->asy_ocflag; 2712 2713 /* flush/reset the status registers */ 2714 (void) asy_get(asy, ASY_ISR); 2715 (void) asy_get(asy, ASY_LSR); 2716 asy->asy_msr = flush_reg = asy_get(asy, ASY_MSR); 2717 /* 2718 * The device is programmed in the open sequence, if we 2719 * have to hardware handshake, then this is a good time 2720 * to check if the device can receive any data. 2721 */ 2722 2723 if ((CRTSCTS & async->async_ttycommon.t_cflag) && 2724 !(flush_reg & ASY_MSR_CTS)) { 2725 async_flowcontrol_hw_output(asy, FLOW_STOP); 2726 } else { 2727 /* 2728 * We can not use async_flowcontrol_hw_output(asy, FLOW_START) 2729 * here, because if CRTSCTS is clear, we need clear 2730 * ASYNC_HW_OUT_FLW bit. 2731 */ 2732 async->async_flags &= ~ASYNC_HW_OUT_FLW; 2733 } 2734 2735 /* 2736 * If IXON is not set, clear ASYNC_SW_OUT_FLW; 2737 * If IXON is set, no matter what IXON flag is before this 2738 * function call to asy_program, 2739 * we will use the old ASYNC_SW_OUT_FLW status. 2740 * Because of handling IXON in the driver, we also should re-calculate 2741 * the value of ASYNC_OUT_FLW_RESUME bit, but in fact, 2742 * the TCSET* commands which call asy_program 2743 * are put into the write queue, so there is no output needed to 2744 * be resumed at this point. 2745 */ 2746 if (!(IXON & async->async_ttycommon.t_iflag)) 2747 async->async_flags &= ~ASYNC_SW_OUT_FLW; 2748 2749 /* manually flush receive buffer or fifo (workaround for buggy fifos) */ 2750 if (mode == ASY_INIT) { 2751 if (asy->asy_use_fifo == ASY_FCR_FIFO_EN) { 2752 for (flush_reg = asy->asy_fifo_buf; flush_reg-- > 0; ) { 2753 (void) asy_get(asy, ASY_RHR); 2754 } 2755 } else { 2756 flush_reg = asy_get(asy, ASY_RHR); 2757 } 2758 } 2759 2760 if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) { 2761 /* Set line control */ 2762 uint8_t lcr = 0; 2763 2764 if (c_flag & CSTOPB) 2765 lcr |= ASY_LCR_STOP2; /* 2 stop bits */ 2766 2767 if (c_flag & PARENB) 2768 lcr |= ASY_LCR_PEN; 2769 2770 if ((c_flag & PARODD) == 0) 2771 lcr |= ASY_LCR_EPS; 2772 2773 switch (c_flag & CSIZE) { 2774 case CS5: 2775 lcr |= ASY_LCR_BITS5; 2776 break; 2777 case CS6: 2778 lcr |= ASY_LCR_BITS6; 2779 break; 2780 case CS7: 2781 lcr |= ASY_LCR_BITS7; 2782 break; 2783 case CS8: 2784 lcr |= ASY_LCR_BITS8; 2785 break; 2786 } 2787 2788 asy_clr(asy, ASY_LCR, ASY_LCR_WLS0 | ASY_LCR_WLS1 | 2789 ASY_LCR_STB | ASY_LCR_PEN | ASY_LCR_EPS); 2790 asy_set(asy, ASY_LCR, lcr); 2791 asy_set_baudrate(asy, baudrate); 2792 2793 /* 2794 * If we have a FIFO buffer, enable/flush 2795 * at intialize time, flush if transitioning from 2796 * CREAD off to CREAD on. 2797 */ 2798 if (((ocflags & CREAD) == 0 && (c_flag & CREAD)) || 2799 mode == ASY_INIT) { 2800 if (asy->asy_use_fifo == ASY_FCR_FIFO_EN) 2801 asy_reset_fifo(asy, ASY_FCR_RHR_FL); 2802 } 2803 2804 /* remember the new cflags */ 2805 asy->asy_ocflag = c_flag & ~CLOCAL; 2806 } 2807 2808 if (baudrate == 0) 2809 asy_put(asy, ASY_MCR, 2810 (asy->asy_mcr & ASY_MCR_RTS) | ASY_MCR_OUT2); 2811 else 2812 asy_put(asy, ASY_MCR, asy->asy_mcr | ASY_MCR_OUT2); 2813 2814 /* 2815 * Call the modem status interrupt handler to check for the carrier 2816 * in case CLOCAL was turned off after the carrier came on. 2817 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.) 2818 */ 2819 async_msint(asy); 2820 2821 /* Set interrupt control */ 2822 ASY_DPRINTF(asy, ASY_DEBUG_MODM2, 2823 "c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x", 2824 c_flag & CLOCAL, async->async_ttycommon.t_cflag & CRTSCTS); 2825 2826 2827 /* Always enable transmit and line status interrupts. */ 2828 ier = ASY_IER_TIEN | ASY_IER_SIEN; 2829 2830 /* 2831 * Enable Modem status interrupt if hardware flow control is enabled or 2832 * this isn't a direct-wired (local) line, which ignores DCD. 2833 */ 2834 if (((c_flag & CLOCAL) == 0) || 2835 (async->async_ttycommon.t_cflag & CRTSCTS)) 2836 ier |= ASY_IER_MIEN; 2837 2838 if (c_flag & CREAD) 2839 ier |= ASY_IER_RIEN; 2840 2841 asy_enable_interrupts(asy, ier); 2842 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "done"); 2843 } 2844 2845 static boolean_t 2846 asy_baudok(struct asycom *asy) 2847 { 2848 struct asyncline *async = asy->asy_priv; 2849 int baudrate; 2850 2851 2852 baudrate = BAUDINDEX(async->async_ttycommon.t_cflag); 2853 2854 if (baudrate >= ARRAY_SIZE(asy_baud_tab)) 2855 return (0); 2856 2857 return (baudrate == 0 || 2858 asy_baud_tab[baudrate].asy_dll != 0 || 2859 asy_baud_tab[baudrate].asy_dlh != 0); 2860 } 2861 2862 /* 2863 * asyintr() is the High Level Interrupt Handler. 2864 * 2865 * There are four different interrupt types indexed by ISR register values: 2866 * 0: modem 2867 * 1: Tx holding register is empty, ready for next char 2868 * 2: Rx register now holds a char to be picked up 2869 * 3: error or break on line 2870 * This routine checks the Bit 0 (interrupt-not-pending) to determine if 2871 * the interrupt is from this port. 2872 */ 2873 uint_t 2874 asyintr(caddr_t argasy, caddr_t argunused __unused) 2875 { 2876 struct asycom *asy = (struct asycom *)argasy; 2877 struct asyncline *async = asy->asy_priv; 2878 int ret_status = DDI_INTR_UNCLAIMED; 2879 2880 mutex_enter(&asy->asy_excl_hi); 2881 if ((async == NULL) || 2882 !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { 2883 const uint8_t intr_id = asy_get(asy, ASY_ISR); 2884 2885 if ((intr_id & ASY_ISR_NOINTR) == 0) { 2886 /* 2887 * reset the device by: 2888 * reading line status 2889 * reading any data from data status register 2890 * reading modem status 2891 */ 2892 (void) asy_get(asy, ASY_LSR); 2893 (void) asy_get(asy, ASY_RHR); 2894 asy->asy_msr = asy_get(asy, ASY_MSR); 2895 ret_status = DDI_INTR_CLAIMED; 2896 } 2897 mutex_exit(&asy->asy_excl_hi); 2898 return (ret_status); 2899 } 2900 2901 /* By this point we're sure this is for us. */ 2902 ret_status = DDI_INTR_CLAIMED; 2903 2904 /* 2905 * Before this flag was set, interrupts were disabled. We may still get 2906 * here if asyintr() waited on the mutex. 2907 */ 2908 if (asy->asy_flags & ASY_DDI_SUSPENDED) { 2909 mutex_exit(&asy->asy_excl_hi); 2910 return (ret_status); 2911 } 2912 2913 /* 2914 * We will loop until the interrupt line is pulled low. asy 2915 * interrupt is edge triggered. 2916 */ 2917 for (;;) { 2918 const uint8_t intr_id = asy_get(asy, ASY_ISR); 2919 2920 if (intr_id & ASY_ISR_NOINTR) 2921 break; 2922 2923 ASY_DPRINTF(asy, ASY_DEBUG_INTR, "interrupt_id = 0x%x", 2924 intr_id); 2925 2926 const uint8_t lsr = asy_get(asy, ASY_LSR); 2927 2928 switch (intr_id & ASY_ISR_MASK) { 2929 case ASY_ISR_ID_RLST: 2930 case ASY_ISR_ID_RDA: 2931 case ASY_ISR_ID_TMO: 2932 /* receiver interrupt or receiver errors */ 2933 async_rxint(asy, lsr); 2934 break; 2935 2936 case ASY_ISR_ID_THRE: 2937 /* 2938 * The transmit-ready interrupt implies an empty 2939 * transmit-hold register (or FIFO). Check that it is 2940 * present before attempting to transmit more data. 2941 */ 2942 if ((lsr & ASY_LSR_THRE) == 0) { 2943 /* 2944 * Taking a THRE interrupt only to find THRE 2945 * absent would be a surprise, except for a 2946 * racing asyputchar(), which ignores the 2947 * excl_hi mutex when writing to the device. 2948 */ 2949 continue; 2950 } 2951 async_txint(asy); 2952 /* 2953 * Unlike the other interrupts which fall through to 2954 * attempting to fill the output register/FIFO, THRE 2955 * has no need having just done so. 2956 */ 2957 continue; 2958 2959 case ASY_ISR_ID_MST: 2960 /* modem status interrupt */ 2961 async_msint(asy); 2962 break; 2963 } 2964 2965 /* Refill the output FIFO if it has gone empty */ 2966 if ((lsr & ASY_LSR_THRE) && (async->async_flags & ASYNC_BUSY) && 2967 (async->async_ocnt > 0)) 2968 async_txint(asy); 2969 } 2970 2971 mutex_exit(&asy->asy_excl_hi); 2972 return (ret_status); 2973 } 2974 2975 /* 2976 * Transmitter interrupt service routine. 2977 * If there is more data to transmit in the current pseudo-DMA block, 2978 * send the next character if output is not stopped or draining. 2979 * Otherwise, queue up a soft interrupt. 2980 * 2981 * XXX - Needs review for HW FIFOs. 2982 */ 2983 static void 2984 async_txint(struct asycom *asy) 2985 { 2986 struct asyncline *async = asy->asy_priv; 2987 int fifo_len; 2988 2989 ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 2990 2991 /* 2992 * If ASYNC_BREAK or ASYNC_OUT_SUSPEND has been set, return to 2993 * asyintr()'s context to claim the interrupt without performing 2994 * any action. No character will be loaded into FIFO/THR until 2995 * timed or untimed break is removed 2996 */ 2997 if (async->async_flags & (ASYNC_BREAK|ASYNC_OUT_SUSPEND)) 2998 return; 2999 3000 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 3001 if (fifo_len > asy_max_tx_fifo) 3002 fifo_len = asy_max_tx_fifo; 3003 3004 if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 3005 fifo_len--; 3006 3007 if (async->async_ocnt > 0 && fifo_len > 0 && 3008 !(async->async_flags & 3009 (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_STOPPED))) { 3010 while (fifo_len-- > 0 && async->async_ocnt-- > 0) { 3011 asy_put(asy, ASY_THR, *async->async_optr++); 3012 } 3013 async->async_flags |= ASYNC_PROGRESS; 3014 } 3015 3016 if (fifo_len <= 0) 3017 return; 3018 3019 asysetsoft(asy); 3020 } 3021 3022 /* 3023 * Interrupt on port: handle PPS event. This function is only called 3024 * for a port on which PPS event handling has been enabled. 3025 */ 3026 static void 3027 asy_ppsevent(struct asycom *asy, int msr) 3028 { 3029 ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 3030 3031 if (asy->asy_flags & ASY_PPS_EDGE) { 3032 /* Have seen leading edge, now look for and record drop */ 3033 if ((msr & ASY_MSR_DCD) == 0) 3034 asy->asy_flags &= ~ASY_PPS_EDGE; 3035 /* 3036 * Waiting for leading edge, look for rise; stamp event and 3037 * calibrate kernel clock. 3038 */ 3039 } else if (msr & ASY_MSR_DCD) { 3040 /* 3041 * This code captures a timestamp at the designated 3042 * transition of the PPS signal (DCD asserted). The 3043 * code provides a pointer to the timestamp, as well 3044 * as the hardware counter value at the capture. 3045 * 3046 * Note: the kernel has nano based time values while 3047 * NTP requires micro based, an in-line fast algorithm 3048 * to convert nsec to usec is used here -- see hrt2ts() 3049 * in common/os/timers.c for a full description. 3050 */ 3051 struct timeval *tvp = &asy_ppsev.tv; 3052 timestruc_t ts; 3053 long nsec, usec; 3054 3055 asy->asy_flags |= ASY_PPS_EDGE; 3056 LED_OFF; 3057 gethrestime(&ts); 3058 LED_ON; 3059 nsec = ts.tv_nsec; 3060 usec = nsec + (nsec >> 2); 3061 usec = nsec + (usec >> 1); 3062 usec = nsec + (usec >> 2); 3063 usec = nsec + (usec >> 4); 3064 usec = nsec - (usec >> 3); 3065 usec = nsec + (usec >> 2); 3066 usec = nsec + (usec >> 3); 3067 usec = nsec + (usec >> 4); 3068 usec = nsec + (usec >> 1); 3069 usec = nsec + (usec >> 6); 3070 tvp->tv_usec = usec >> 10; 3071 tvp->tv_sec = ts.tv_sec; 3072 3073 ++asy_ppsev.serial; 3074 3075 /* 3076 * Because the kernel keeps a high-resolution time, 3077 * pass the current highres timestamp in tvp and zero 3078 * in usec. 3079 */ 3080 ddi_hardpps(tvp, 0); 3081 } 3082 } 3083 3084 /* 3085 * Receiver interrupt: RDA interrupt, FIFO timeout interrupt or receive 3086 * error interrupt. 3087 * Try to put the character into the circular buffer for this line; if it 3088 * overflows, indicate a circular buffer overrun. If this port is always 3089 * to be serviced immediately, or the character is a STOP character, or 3090 * more than 15 characters have arrived, queue up a soft interrupt to 3091 * drain the circular buffer. 3092 * XXX - needs review for hw FIFOs support. 3093 */ 3094 3095 static void 3096 async_rxint(struct asycom *asy, uchar_t lsr) 3097 { 3098 struct asyncline *async = asy->asy_priv; 3099 uchar_t c; 3100 uint_t s, needsoft = 0; 3101 tty_common_t *tp; 3102 int looplim = asy->asy_fifo_buf * 2; 3103 3104 ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 3105 3106 tp = &async->async_ttycommon; 3107 if (!(tp->t_cflag & CREAD)) { 3108 /* Line is not open for reading. Flush receiver FIFO. */ 3109 while ((lsr & (ASY_LSR_DR | ASY_LSR_ERRORS)) != 0) { 3110 (void) asy_get(asy, ASY_RHR); 3111 lsr = asy_get(asy, ASY_LSR); 3112 if (looplim-- < 0) /* limit loop */ 3113 break; 3114 } 3115 return; 3116 } 3117 3118 while ((lsr & (ASY_LSR_DR | ASY_LSR_ERRORS)) != 0) { 3119 c = 0; 3120 s = 0; /* reset error status */ 3121 if (lsr & ASY_LSR_DR) { 3122 c = asy_get(asy, ASY_RHR); 3123 3124 /* 3125 * We handle XON/XOFF char if IXON is set, 3126 * but if received char is _POSIX_VDISABLE, 3127 * we left it to the up level module. 3128 */ 3129 if (tp->t_iflag & IXON) { 3130 if ((c == async->async_stopc) && 3131 (c != _POSIX_VDISABLE)) { 3132 async_flowcontrol_sw_output(asy, 3133 FLOW_STOP); 3134 goto check_looplim; 3135 } else if ((c == async->async_startc) && 3136 (c != _POSIX_VDISABLE)) { 3137 async_flowcontrol_sw_output(asy, 3138 FLOW_START); 3139 needsoft = 1; 3140 goto check_looplim; 3141 } 3142 if ((tp->t_iflag & IXANY) && 3143 (async->async_flags & ASYNC_SW_OUT_FLW)) { 3144 async_flowcontrol_sw_output(asy, 3145 FLOW_START); 3146 needsoft = 1; 3147 } 3148 } 3149 } 3150 3151 /* 3152 * Check for character break sequence 3153 */ 3154 if ((abort_enable == KIOCABORTALTERNATE) && 3155 (asy->asy_flags & ASY_CONSOLE)) { 3156 if (abort_charseq_recognize(c)) 3157 abort_sequence_enter((char *)NULL); 3158 } 3159 3160 /* Handle framing errors */ 3161 if (lsr & ASY_LSR_ERRORS) { 3162 if (lsr & ASY_LSR_PE) { 3163 if (tp->t_iflag & INPCK) /* parity enabled */ 3164 s |= PERROR; 3165 } 3166 3167 if (lsr & (ASY_LSR_FE | ASY_LSR_BI)) 3168 s |= FRERROR; 3169 if (lsr & ASY_LSR_OE) { 3170 async->async_hw_overrun = 1; 3171 s |= OVERRUN; 3172 } 3173 } 3174 3175 if (s == 0) 3176 if ((tp->t_iflag & PARMRK) && 3177 !(tp->t_iflag & (IGNPAR|ISTRIP)) && 3178 (c == 0377)) 3179 if (RING_POK(async, 2)) { 3180 RING_PUT(async, 0377); 3181 RING_PUT(async, c); 3182 } else 3183 async->async_sw_overrun = 1; 3184 else 3185 if (RING_POK(async, 1)) 3186 RING_PUT(async, c); 3187 else 3188 async->async_sw_overrun = 1; 3189 else 3190 if (s & FRERROR) /* Handle framing errors */ 3191 if (c == 0) 3192 if ((asy->asy_flags & ASY_CONSOLE) && 3193 (abort_enable != 3194 KIOCABORTALTERNATE)) 3195 abort_sequence_enter((char *)0); 3196 else 3197 async->async_break++; 3198 else 3199 if (RING_POK(async, 1)) 3200 RING_MARK(async, c, s); 3201 else 3202 async->async_sw_overrun = 1; 3203 else /* Parity errors are handled by ldterm */ 3204 if (RING_POK(async, 1)) 3205 RING_MARK(async, c, s); 3206 else 3207 async->async_sw_overrun = 1; 3208 check_looplim: 3209 lsr = asy_get(asy, ASY_LSR); 3210 if (looplim-- < 0) /* limit loop */ 3211 break; 3212 } 3213 if ((RING_CNT(async) > (RINGSIZE * 3)/4) && 3214 !(async->async_inflow_source & IN_FLOW_RINGBUFF)) { 3215 async_flowcontrol_hw_input(asy, FLOW_STOP, IN_FLOW_RINGBUFF); 3216 (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 3217 IN_FLOW_RINGBUFF); 3218 } 3219 3220 if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || 3221 (RING_FRAC(async)) || (async->async_polltid == 0)) { 3222 asysetsoft(asy); /* need a soft interrupt */ 3223 } 3224 } 3225 3226 /* 3227 * Modem status interrupt. 3228 * 3229 * (Note: It is assumed that the MSR hasn't been read by asyintr().) 3230 */ 3231 3232 static void 3233 async_msint(struct asycom *asy) 3234 { 3235 struct asyncline *async = asy->asy_priv; 3236 int msr, t_cflag = async->async_ttycommon.t_cflag; 3237 3238 ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 3239 3240 async_msint_retry: 3241 /* this resets the interrupt */ 3242 msr = asy_get(asy, ASY_MSR); 3243 ASY_DPRINTF(asy, ASY_DEBUG_STATE, "call #%d:", 3244 ++(asy->asy_msint_cnt)); 3245 ASY_DPRINTF(asy, ASY_DEBUG_STATE, " transition: %3s %3s %3s %3s", 3246 (msr & ASY_MSR_DCTS) ? "DCTS" : " ", 3247 (msr & ASY_MSR_DDSR) ? "DDSR" : " ", 3248 (msr & ASY_MSR_TERI) ? "TERI" : " ", 3249 (msr & ASY_MSR_DDCD) ? "DDCD" : " "); 3250 ASY_DPRINTF(asy, ASY_DEBUG_STATE, "current state: %3s %3s %3s %3s", 3251 (msr & ASY_MSR_CTS) ? "CTS " : " ", 3252 (msr & ASY_MSR_DSR) ? "DSR " : " ", 3253 (msr & ASY_MSR_RI) ? "RI " : " ", 3254 (msr & ASY_MSR_DCD) ? "DCD " : " "); 3255 3256 /* If CTS status is changed, do H/W output flow control */ 3257 if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & ASY_MSR_CTS) != 0)) 3258 async_flowcontrol_hw_output(asy, 3259 msr & ASY_MSR_CTS ? FLOW_START : FLOW_STOP); 3260 /* 3261 * Reading MSR resets the interrupt, we save the 3262 * value of msr so that other functions could examine MSR by 3263 * looking at asy_msr. 3264 */ 3265 asy->asy_msr = (uchar_t)msr; 3266 3267 /* Handle PPS event */ 3268 if (asy->asy_flags & ASY_PPS) 3269 asy_ppsevent(asy, msr); 3270 3271 async->async_ext++; 3272 asysetsoft(asy); 3273 /* 3274 * We will make sure that the modem status presented to us 3275 * during the previous read has not changed. If the chip samples 3276 * the modem status on the falling edge of the interrupt line, 3277 * and uses this state as the base for detecting change of modem 3278 * status, we would miss a change of modem status event that occured 3279 * after we initiated a read MSR operation. 3280 */ 3281 msr = asy_get(asy, ASY_MSR); 3282 if (ASY_MSR_STATES(msr) != ASY_MSR_STATES(asy->asy_msr)) 3283 goto async_msint_retry; 3284 } 3285 3286 /* 3287 * Pend a soft interrupt if one isn't already pending. 3288 */ 3289 static void 3290 asysetsoft(struct asycom *asy) 3291 { 3292 ASSERT(MUTEX_HELD(&asy->asy_excl_hi)); 3293 3294 if (mutex_tryenter(&asy->asy_soft_lock) == 0) 3295 return; 3296 3297 asy->asy_flags |= ASY_NEEDSOFT; 3298 if (!asy->asysoftpend) { 3299 asy->asysoftpend = 1; 3300 mutex_exit(&asy->asy_soft_lock); 3301 (void) ddi_intr_trigger_softint(asy->asy_soft_inth, NULL); 3302 } else { 3303 mutex_exit(&asy->asy_soft_lock); 3304 } 3305 } 3306 3307 /* 3308 * Check the carrier signal DCD and handle carrier coming up or 3309 * going down, cleaning up as needed and signalling waiters. 3310 */ 3311 static void 3312 asy_carrier_check(struct asycom *asy) 3313 { 3314 struct asyncline *async = asy->asy_priv; 3315 tty_common_t *tp = &async->async_ttycommon; 3316 queue_t *q = tp->t_readq; 3317 mblk_t *bp; 3318 int flushflag; 3319 3320 ASY_DPRINTF(asy, ASY_DEBUG_MODM2, 3321 "asy_msr & DCD = %x, tp->t_flags & TS_SOFTCAR = %x", 3322 asy->asy_msr & ASY_MSR_DCD, tp->t_flags & TS_SOFTCAR); 3323 3324 if (asy->asy_msr & ASY_MSR_DCD) { 3325 /* 3326 * The DCD line is on. If we already had a carrier, 3327 * nothing changed and there's nothing to do. 3328 */ 3329 if ((async->async_flags & ASYNC_CARR_ON) != 0) 3330 return; 3331 3332 ASY_DPRINTF(asy, ASY_DEBUG_MODM2, "set ASYNC_CARR_ON"); 3333 async->async_flags |= ASYNC_CARR_ON; 3334 if (async->async_flags & ASYNC_ISOPEN) { 3335 mutex_exit(&asy->asy_excl_hi); 3336 mutex_exit(&asy->asy_excl); 3337 (void) putctl(q, M_UNHANGUP); 3338 mutex_enter(&asy->asy_excl); 3339 mutex_enter(&asy->asy_excl_hi); 3340 } 3341 cv_broadcast(&async->async_flags_cv); 3342 3343 return; 3344 } 3345 3346 /* 3347 * The DCD line is off. If we had no carrier, nothing changed 3348 * and there's nothing to do. 3349 */ 3350 if ((async->async_flags & ASYNC_CARR_ON) == 0) 3351 return; 3352 3353 /* 3354 * The DCD line is off, but we had a carrier. If we're on a local line, 3355 * where carrier is ignored, or we're using a soft carrier, we're done 3356 * here. 3357 */ 3358 if ((tp->t_cflag & CLOCAL) != 0 || (tp->t_flags & TS_SOFTCAR) != 0) 3359 goto out; 3360 3361 /* 3362 * Else, drop DTR, abort any output in progress, indicate that output 3363 * is not stopped. 3364 */ 3365 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "carrier dropped, so drop DTR"); 3366 asy_clr(asy, ASY_MCR, ASY_MCR_DTR); 3367 3368 if (async->async_flags & ASYNC_BUSY) { 3369 ASY_DPRINTF(asy, ASY_DEBUG_BUSY, 3370 "Carrier dropped. Clearing async_ocnt"); 3371 async->async_ocnt = 0; 3372 } 3373 3374 async->async_flags &= ~ASYNC_STOPPED; 3375 3376 /* If nobody had the device open, we're done here. */ 3377 if ((async->async_flags & ASYNC_ISOPEN) == 0) 3378 goto out; 3379 3380 /* Else, send a hangup notification upstream and clean up. */ 3381 mutex_exit(&asy->asy_excl_hi); 3382 mutex_exit(&asy->asy_excl); 3383 (void) putctl(q, M_HANGUP); 3384 mutex_enter(&asy->asy_excl); 3385 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "putctl(q, M_HANGUP)"); 3386 3387 /* 3388 * Flush the transmit FIFO. Any data left in there is invalid now. 3389 */ 3390 if (asy->asy_use_fifo == ASY_FCR_FIFO_EN) { 3391 mutex_enter(&asy->asy_excl_hi); 3392 asy_reset_fifo(asy, ASY_FCR_THR_FL); 3393 mutex_exit(&asy->asy_excl_hi); 3394 } 3395 3396 /* 3397 * Flush our write queue if we have one. If we're in the midst of close, 3398 * then flush everything. Don't leave stale ioctls lying about. 3399 */ 3400 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 3401 "Flushing to prevent HUPCL hanging"); 3402 flushflag = (async->async_flags & ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA; 3403 flushq(tp->t_writeq, flushflag); 3404 3405 /* Free the last active msg. */ 3406 bp = async->async_xmitblk; 3407 if (bp != NULL) { 3408 freeb(bp); 3409 async->async_xmitblk = NULL; 3410 } 3411 3412 mutex_enter(&asy->asy_excl_hi); 3413 async->async_flags &= ~ASYNC_BUSY; 3414 3415 3416 out: 3417 /* Clear our carrier flag and signal anyone waiting. */ 3418 async->async_flags &= ~ASYNC_CARR_ON; 3419 cv_broadcast(&async->async_flags_cv); 3420 } 3421 3422 /* 3423 * Handle a second-stage interrupt. 3424 */ 3425 uint_t 3426 asysoftintr(caddr_t intarg, caddr_t unusedarg __unused) 3427 { 3428 struct asycom *asy = (struct asycom *)intarg; 3429 struct asyncline *async; 3430 int rv; 3431 uint_t cc; 3432 3433 /* 3434 * Test and clear soft interrupt. 3435 */ 3436 mutex_enter(&asy->asy_soft_lock); 3437 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter"); 3438 rv = asy->asysoftpend; 3439 if (rv != 0) 3440 asy->asysoftpend = 0; 3441 mutex_exit(&asy->asy_soft_lock); 3442 3443 if (rv) { 3444 if (asy->asy_priv == NULL) 3445 return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 3446 async = (struct asyncline *)asy->asy_priv; 3447 mutex_enter(&asy->asy_excl_hi); 3448 if (asy->asy_flags & ASY_NEEDSOFT) { 3449 asy->asy_flags &= ~ASY_NEEDSOFT; 3450 mutex_exit(&asy->asy_excl_hi); 3451 async_softint(asy); 3452 mutex_enter(&asy->asy_excl_hi); 3453 } 3454 3455 /* 3456 * There are some instances where the softintr is not 3457 * scheduled and hence not called. It so happens that 3458 * causes the last few characters to be stuck in the 3459 * ringbuffer. Hence, call the handler once again so 3460 * the last few characters are cleared. 3461 */ 3462 cc = RING_CNT(async); 3463 mutex_exit(&asy->asy_excl_hi); 3464 if (cc > 0) 3465 (void) async_softint(asy); 3466 } 3467 return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 3468 } 3469 3470 /* 3471 * Handle a software interrupt. 3472 */ 3473 static void 3474 async_softint(struct asycom *asy) 3475 { 3476 struct asyncline *async = asy->asy_priv; 3477 uint_t cc; 3478 mblk_t *bp; 3479 queue_t *q; 3480 uchar_t c; 3481 tty_common_t *tp; 3482 int nb; 3483 3484 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter"); 3485 mutex_enter(&asy->asy_excl_hi); 3486 if (asy->asy_flags & ASY_DOINGSOFT) { 3487 asy->asy_flags |= ASY_DOINGSOFT_RETRY; 3488 mutex_exit(&asy->asy_excl_hi); 3489 return; 3490 } 3491 asy->asy_flags |= ASY_DOINGSOFT; 3492 begin: 3493 asy->asy_flags &= ~ASY_DOINGSOFT_RETRY; 3494 mutex_exit(&asy->asy_excl_hi); 3495 mutex_enter(&asy->asy_excl); 3496 tp = &async->async_ttycommon; 3497 q = tp->t_readq; 3498 3499 if (async->async_flags & ASYNC_OUT_FLW_RESUME) { 3500 if (async->async_ocnt > 0) { 3501 mutex_enter(&asy->asy_excl_hi); 3502 async_resume(async); 3503 mutex_exit(&asy->asy_excl_hi); 3504 } else { 3505 if (async->async_xmitblk) 3506 freeb(async->async_xmitblk); 3507 async->async_xmitblk = NULL; 3508 async_start(async); 3509 } 3510 async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 3511 } 3512 3513 mutex_enter(&asy->asy_excl_hi); 3514 if (async->async_ext) { 3515 async->async_ext = 0; 3516 asy_carrier_check(asy); 3517 } 3518 mutex_exit(&asy->asy_excl_hi); 3519 3520 /* 3521 * If data has been added to the circular buffer, remove 3522 * it from the buffer, and send it up the stream if there's 3523 * somebody listening. Try to do it 16 bytes at a time. If we 3524 * have more than 16 bytes to move, move 16 byte chunks and 3525 * leave the rest for next time around (maybe it will grow). 3526 */ 3527 mutex_enter(&asy->asy_excl_hi); 3528 if (!(async->async_flags & ASYNC_ISOPEN)) { 3529 RING_INIT(async); 3530 goto rv; 3531 } 3532 if ((cc = RING_CNT(async)) == 0) 3533 goto rv; 3534 mutex_exit(&asy->asy_excl_hi); 3535 3536 if (!canput(q)) { 3537 mutex_enter(&asy->asy_excl_hi); 3538 if (!(async->async_inflow_source & IN_FLOW_STREAMS)) { 3539 async_flowcontrol_hw_input(asy, FLOW_STOP, 3540 IN_FLOW_STREAMS); 3541 (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 3542 IN_FLOW_STREAMS); 3543 } 3544 goto rv; 3545 } 3546 if (async->async_inflow_source & IN_FLOW_STREAMS) { 3547 mutex_enter(&asy->asy_excl_hi); 3548 async_flowcontrol_hw_input(asy, FLOW_START, 3549 IN_FLOW_STREAMS); 3550 (void) async_flowcontrol_sw_input(asy, FLOW_START, 3551 IN_FLOW_STREAMS); 3552 mutex_exit(&asy->asy_excl_hi); 3553 } 3554 3555 ASY_DPRINTF(asy, ASY_DEBUG_INPUT, "%d char(s) in queue", cc); 3556 3557 if (!(bp = allocb(cc, BPRI_MED))) { 3558 mutex_exit(&asy->asy_excl); 3559 ttycommon_qfull(&async->async_ttycommon, q); 3560 mutex_enter(&asy->asy_excl); 3561 mutex_enter(&asy->asy_excl_hi); 3562 goto rv; 3563 } 3564 mutex_enter(&asy->asy_excl_hi); 3565 do { 3566 if (RING_ERR(async, S_ERRORS)) { 3567 RING_UNMARK(async); 3568 c = RING_GET(async); 3569 break; 3570 } else { 3571 *bp->b_wptr++ = RING_GET(async); 3572 } 3573 } while (--cc); 3574 mutex_exit(&asy->asy_excl_hi); 3575 mutex_exit(&asy->asy_excl); 3576 if (bp->b_wptr > bp->b_rptr) { 3577 if (!canput(q)) { 3578 asyerror(asy, CE_WARN, "local queue full"); 3579 freemsg(bp); 3580 } else { 3581 (void) putq(q, bp); 3582 } 3583 } else { 3584 freemsg(bp); 3585 } 3586 /* 3587 * If we have a parity error, then send 3588 * up an M_BREAK with the "bad" 3589 * character as an argument. Let ldterm 3590 * figure out what to do with the error. 3591 */ 3592 if (cc) 3593 (void) putctl1(q, M_BREAK, c); 3594 mutex_enter(&asy->asy_excl); 3595 mutex_enter(&asy->asy_excl_hi); 3596 if (cc) { 3597 asysetsoft(asy); /* finish cc chars */ 3598 } 3599 rv: 3600 if ((RING_CNT(async) < (RINGSIZE/4)) && 3601 (async->async_inflow_source & IN_FLOW_RINGBUFF)) { 3602 async_flowcontrol_hw_input(asy, FLOW_START, IN_FLOW_RINGBUFF); 3603 (void) async_flowcontrol_sw_input(asy, FLOW_START, 3604 IN_FLOW_RINGBUFF); 3605 } 3606 3607 /* 3608 * If a transmission has finished, indicate that it's finished, 3609 * and start that line up again. 3610 */ 3611 if (async->async_break > 0) { 3612 nb = async->async_break; 3613 async->async_break = 0; 3614 if (async->async_flags & ASYNC_ISOPEN) { 3615 mutex_exit(&asy->asy_excl_hi); 3616 mutex_exit(&asy->asy_excl); 3617 for (; nb > 0; nb--) 3618 (void) putctl(q, M_BREAK); 3619 mutex_enter(&asy->asy_excl); 3620 mutex_enter(&asy->asy_excl_hi); 3621 } 3622 } 3623 if (async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) { 3624 ASY_DPRINTF(asy, ASY_DEBUG_BUSY, 3625 "Clearing ASYNC_BUSY, async_ocnt=%d", async->async_ocnt); 3626 async->async_flags &= ~ASYNC_BUSY; 3627 mutex_exit(&asy->asy_excl_hi); 3628 if (async->async_xmitblk) 3629 freeb(async->async_xmitblk); 3630 async->async_xmitblk = NULL; 3631 async_start(async); 3632 /* 3633 * If the flag isn't set after doing the async_start above, we 3634 * may have finished all the queued output. Signal any thread 3635 * stuck in close. 3636 */ 3637 if (!(async->async_flags & ASYNC_BUSY)) 3638 cv_broadcast(&async->async_flags_cv); 3639 mutex_enter(&asy->asy_excl_hi); 3640 } 3641 /* 3642 * A note about these overrun bits: all they do is *tell* someone 3643 * about an error- They do not track multiple errors. In fact, 3644 * you could consider them latched register bits if you like. 3645 * We are only interested in printing the error message once for 3646 * any cluster of overrun errors. 3647 */ 3648 if (async->async_hw_overrun) { 3649 if (async->async_flags & ASYNC_ISOPEN) { 3650 mutex_exit(&asy->asy_excl_hi); 3651 mutex_exit(&asy->asy_excl); 3652 asyerror(asy, CE_WARN, "silo overflow"); 3653 mutex_enter(&asy->asy_excl); 3654 mutex_enter(&asy->asy_excl_hi); 3655 } 3656 async->async_hw_overrun = 0; 3657 } 3658 if (async->async_sw_overrun) { 3659 if (async->async_flags & ASYNC_ISOPEN) { 3660 mutex_exit(&asy->asy_excl_hi); 3661 mutex_exit(&asy->asy_excl); 3662 asyerror(asy, CE_WARN, "ring buffer overflow"); 3663 mutex_enter(&asy->asy_excl); 3664 mutex_enter(&asy->asy_excl_hi); 3665 } 3666 async->async_sw_overrun = 0; 3667 } 3668 if (asy->asy_flags & ASY_DOINGSOFT_RETRY) { 3669 mutex_exit(&asy->asy_excl); 3670 goto begin; 3671 } 3672 asy->asy_flags &= ~ASY_DOINGSOFT; 3673 mutex_exit(&asy->asy_excl_hi); 3674 mutex_exit(&asy->asy_excl); 3675 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "done"); 3676 } 3677 3678 /* 3679 * Restart output on a line after a delay or break timer expired. 3680 */ 3681 static void 3682 async_restart(void *arg) 3683 { 3684 struct asyncline *async = (struct asyncline *)arg; 3685 struct asycom *asy = async->async_common; 3686 3687 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter"); 3688 3689 /* 3690 * If break timer expired, turn off the break bit. 3691 */ 3692 3693 mutex_enter(&asy->asy_excl); 3694 /* 3695 * If ASYNC_OUT_SUSPEND is also set, we don't really 3696 * clean the HW break, TIOCCBRK is responsible for this. 3697 */ 3698 if ((async->async_flags & ASYNC_BREAK) && 3699 !(async->async_flags & ASYNC_OUT_SUSPEND)) { 3700 mutex_enter(&asy->asy_excl_hi); 3701 asy_clr(asy, ASY_LCR, ASY_LCR_SETBRK); 3702 mutex_exit(&asy->asy_excl_hi); 3703 } 3704 async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK); 3705 cv_broadcast(&async->async_flags_cv); 3706 async_start(async); 3707 3708 mutex_exit(&asy->asy_excl); 3709 } 3710 3711 /* 3712 * Start output on a line, unless it's busy, frozen, or otherwise. 3713 */ 3714 static void 3715 async_start(struct asyncline *async) 3716 { 3717 struct asycom *asy = async->async_common; 3718 int cc; 3719 queue_t *q; 3720 mblk_t *bp; 3721 uchar_t *xmit_addr; 3722 int fifo_len = 1; 3723 boolean_t didsome; 3724 mblk_t *nbp; 3725 3726 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter"); 3727 3728 if (asy->asy_use_fifo == ASY_FCR_FIFO_EN) { 3729 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 3730 if (fifo_len > asy_max_tx_fifo) 3731 fifo_len = asy_max_tx_fifo; 3732 } 3733 3734 ASSERT(mutex_owned(&asy->asy_excl)); 3735 3736 /* 3737 * If the chip is busy (i.e., we're waiting for a break timeout 3738 * to expire, or for the current transmission to finish, or for 3739 * output to finish draining from chip), don't grab anything new. 3740 */ 3741 if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) { 3742 ASY_DPRINTF(asy, ASY_DEBUG_OUT, "%s", 3743 async->async_flags & ASYNC_BREAK ? "break" : "busy"); 3744 return; 3745 } 3746 3747 /* 3748 * Check only pended sw input flow control. 3749 */ 3750 mutex_enter(&asy->asy_excl_hi); 3751 if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 3752 fifo_len--; 3753 mutex_exit(&asy->asy_excl_hi); 3754 3755 /* 3756 * If we're waiting for a delay timeout to expire, don't grab 3757 * anything new. 3758 */ 3759 if (async->async_flags & ASYNC_DELAY) { 3760 ASY_DPRINTF(asy, ASY_DEBUG_OUT, "start ASYNC_DELAY"); 3761 return; 3762 } 3763 3764 if ((q = async->async_ttycommon.t_writeq) == NULL) { 3765 ASY_DPRINTF(asy, ASY_DEBUG_OUT, "start writeq is null"); 3766 return; /* not attached to a stream */ 3767 } 3768 3769 for (;;) { 3770 if ((bp = getq(q)) == NULL) 3771 return; /* no data to transmit */ 3772 3773 /* 3774 * We have a message block to work on. 3775 * Check whether it's a break, a delay, or an ioctl (the latter 3776 * occurs if the ioctl in question was waiting for the output 3777 * to drain). If it's one of those, process it immediately. 3778 */ 3779 switch (bp->b_datap->db_type) { 3780 3781 case M_BREAK: 3782 /* 3783 * Set the break bit, and arrange for "async_restart" 3784 * to be called in 1/4 second; it will turn the 3785 * break bit off, and call "async_start" to grab 3786 * the next message. 3787 */ 3788 mutex_enter(&asy->asy_excl_hi); 3789 asy_set(asy, ASY_LCR, ASY_LCR_SETBRK); 3790 mutex_exit(&asy->asy_excl_hi); 3791 async->async_flags |= ASYNC_BREAK; 3792 (void) timeout(async_restart, (caddr_t)async, 3793 drv_usectohz(1000000)/4); 3794 freemsg(bp); 3795 return; /* wait for this to finish */ 3796 3797 case M_DELAY: 3798 /* 3799 * Arrange for "async_restart" to be called when the 3800 * delay expires; it will turn ASYNC_DELAY off, 3801 * and call "async_start" to grab the next message. 3802 */ 3803 (void) timeout(async_restart, (caddr_t)async, 3804 (int)(*(unsigned char *)bp->b_rptr + 6)); 3805 async->async_flags |= ASYNC_DELAY; 3806 freemsg(bp); 3807 return; /* wait for this to finish */ 3808 3809 case M_IOCTL: 3810 /* 3811 * This ioctl was waiting for the output ahead of 3812 * it to drain; obviously, it has. Do it, and 3813 * then grab the next message after it. 3814 */ 3815 mutex_exit(&asy->asy_excl); 3816 async_ioctl(async, q, bp); 3817 mutex_enter(&asy->asy_excl); 3818 continue; 3819 } 3820 3821 while (bp != NULL && ((cc = MBLKL(bp)) == 0)) { 3822 nbp = bp->b_cont; 3823 freeb(bp); 3824 bp = nbp; 3825 } 3826 if (bp != NULL) 3827 break; 3828 } 3829 3830 /* 3831 * We have data to transmit. If output is stopped, put 3832 * it back and try again later. 3833 */ 3834 if (async->async_flags & (ASYNC_HW_OUT_FLW | ASYNC_SW_OUT_FLW | 3835 ASYNC_STOPPED | ASYNC_OUT_SUSPEND)) { 3836 (void) putbq(q, bp); 3837 return; 3838 } 3839 3840 async->async_xmitblk = bp; 3841 xmit_addr = bp->b_rptr; 3842 bp = bp->b_cont; 3843 if (bp != NULL) 3844 (void) putbq(q, bp); /* not done with this message yet */ 3845 3846 /* 3847 * In 5-bit mode, the high order bits are used 3848 * to indicate character sizes less than five, 3849 * so we need to explicitly mask before transmitting 3850 */ 3851 if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { 3852 unsigned char *p = xmit_addr; 3853 int cnt = cc; 3854 3855 while (cnt--) 3856 *p++ &= (unsigned char) 0x1f; 3857 } 3858 3859 /* 3860 * Set up this block for pseudo-DMA. 3861 */ 3862 mutex_enter(&asy->asy_excl_hi); 3863 /* 3864 * If the transmitter is ready, shove the first 3865 * character out. 3866 */ 3867 didsome = B_FALSE; 3868 while (--fifo_len >= 0 && cc > 0) { 3869 if (!(asy_get(asy, ASY_LSR) & ASY_LSR_THRE)) 3870 break; 3871 asy_put(asy, ASY_THR, *xmit_addr++); 3872 cc--; 3873 didsome = B_TRUE; 3874 } 3875 async->async_optr = xmit_addr; 3876 async->async_ocnt = cc; 3877 if (didsome) 3878 async->async_flags |= ASYNC_PROGRESS; 3879 ASY_DPRINTF(asy, ASY_DEBUG_BUSY, "Set ASYNC_BUSY, async_ocnt=%d", 3880 async->async_ocnt); 3881 async->async_flags |= ASYNC_BUSY; 3882 mutex_exit(&asy->asy_excl_hi); 3883 } 3884 3885 /* 3886 * Resume output by poking the transmitter. 3887 */ 3888 static void 3889 async_resume(struct asyncline *async) 3890 { 3891 struct asycom *asy = async->async_common; 3892 3893 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter"); 3894 ASSERT(mutex_owned(&asy->asy_excl_hi)); 3895 3896 if (asy_get(asy, ASY_LSR) & ASY_LSR_THRE) { 3897 if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 3898 return; 3899 if (async->async_ocnt > 0 && 3900 !(async->async_flags & 3901 (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_OUT_SUSPEND))) { 3902 asy_put(asy, ASY_THR, *async->async_optr++); 3903 async->async_ocnt--; 3904 async->async_flags |= ASYNC_PROGRESS; 3905 } 3906 } 3907 } 3908 3909 /* 3910 * Hold the untimed break to last the minimum time. 3911 */ 3912 static void 3913 async_hold_utbrk(void *arg) 3914 { 3915 struct asyncline *async = arg; 3916 struct asycom *asy = async->async_common; 3917 3918 mutex_enter(&asy->asy_excl); 3919 async->async_flags &= ~ASYNC_HOLD_UTBRK; 3920 cv_broadcast(&async->async_flags_cv); 3921 async->async_utbrktid = 0; 3922 mutex_exit(&asy->asy_excl); 3923 } 3924 3925 /* 3926 * Resume the untimed break. 3927 */ 3928 static void 3929 async_resume_utbrk(struct asyncline *async) 3930 { 3931 struct asycom *asy = async->async_common; 3932 ASSERT(mutex_owned(&asy->asy_excl)); 3933 3934 /* 3935 * Because the wait time is very short, 3936 * so we use uninterruptably wait. 3937 */ 3938 while (async->async_flags & ASYNC_HOLD_UTBRK) { 3939 cv_wait(&async->async_flags_cv, &asy->asy_excl); 3940 } 3941 mutex_enter(&asy->asy_excl_hi); 3942 /* 3943 * Timed break and untimed break can exist simultaneously, 3944 * if ASYNC_BREAK is also set at here, we don't 3945 * really clean the HW break. 3946 */ 3947 if (!(async->async_flags & ASYNC_BREAK)) 3948 asy_clr(asy, ASY_LCR, ASY_LCR_SETBRK); 3949 3950 async->async_flags &= ~ASYNC_OUT_SUSPEND; 3951 cv_broadcast(&async->async_flags_cv); 3952 if (async->async_ocnt > 0) { 3953 async_resume(async); 3954 mutex_exit(&asy->asy_excl_hi); 3955 } else { 3956 async->async_flags &= ~ASYNC_BUSY; 3957 mutex_exit(&asy->asy_excl_hi); 3958 if (async->async_xmitblk != NULL) { 3959 freeb(async->async_xmitblk); 3960 async->async_xmitblk = NULL; 3961 } 3962 async_start(async); 3963 } 3964 } 3965 3966 /* 3967 * Process an "ioctl" message sent down to us. 3968 * Note that we don't need to get any locks until we are ready to access 3969 * the hardware. Nothing we access until then is going to be altered 3970 * outside of the STREAMS framework, so we should be safe. 3971 */ 3972 int asydelay = 10000; 3973 static void 3974 async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) 3975 { 3976 struct asycom *asy = async->async_common; 3977 tty_common_t *tp = &async->async_ttycommon; 3978 struct iocblk *iocp; 3979 unsigned datasize; 3980 int error = 0; 3981 mblk_t *datamp; 3982 3983 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "enter"); 3984 3985 if (tp->t_iocpending != NULL) { 3986 /* 3987 * We were holding an "ioctl" response pending the 3988 * availability of an "mblk" to hold data to be passed up; 3989 * another "ioctl" came through, which means that "ioctl" 3990 * must have timed out or been aborted. 3991 */ 3992 freemsg(async->async_ttycommon.t_iocpending); 3993 async->async_ttycommon.t_iocpending = NULL; 3994 } 3995 3996 iocp = (struct iocblk *)mp->b_rptr; 3997 3998 /* 3999 * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl() 4000 * because this function frees up the message block (mp->b_cont) that 4001 * contains the user location where we pass back the results. 4002 * 4003 * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl 4004 * zaps. We know that ttycommon_ioctl doesn't know any CONS* 4005 * ioctls, so keep the others safe too. 4006 */ 4007 ASY_DPRINTF(asy, ASY_DEBUG_IOCTL, "%s", 4008 iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" : 4009 iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" : 4010 iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" : 4011 iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" : 4012 "other"); 4013 4014 switch (iocp->ioc_cmd) { 4015 case TIOCMGET: 4016 case TIOCGPPS: 4017 case TIOCSPPS: 4018 case TIOCGPPSEV: 4019 case CONSOPENPOLLEDIO: 4020 case CONSCLOSEPOLLEDIO: 4021 case CONSSETABORTENABLE: 4022 case CONSGETABORTENABLE: 4023 error = -1; /* Do Nothing */ 4024 break; 4025 default: 4026 4027 /* 4028 * The only way in which "ttycommon_ioctl" can fail is if the 4029 * "ioctl" requires a response containing data to be returned 4030 * to the user, and no mblk could be allocated for the data. 4031 * No such "ioctl" alters our state. Thus, we always go ahead 4032 * and do any state-changes the "ioctl" calls for. If we 4033 * couldn't allocate the data, "ttycommon_ioctl" has stashed 4034 * the "ioctl" away safely, so we just call "bufcall" to 4035 * request that we be called back when we stand a better 4036 * chance of allocating the data. 4037 */ 4038 if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { 4039 if (async->async_wbufcid) 4040 unbufcall(async->async_wbufcid); 4041 async->async_wbufcid = bufcall(datasize, BPRI_HI, 4042 (void (*)(void *)) async_reioctl, 4043 (void *)(intptr_t)async->async_common->asy_unit); 4044 return; 4045 } 4046 } 4047 4048 mutex_enter(&asy->asy_excl); 4049 4050 if (error == 0) { 4051 /* 4052 * "ttycommon_ioctl" did most of the work; we just use the 4053 * data it set up. 4054 */ 4055 switch (iocp->ioc_cmd) { 4056 4057 case TCSETS: 4058 mutex_enter(&asy->asy_excl_hi); 4059 if (asy_baudok(asy)) 4060 asy_program(asy, ASY_NOINIT); 4061 else 4062 error = EINVAL; 4063 mutex_exit(&asy->asy_excl_hi); 4064 break; 4065 case TCSETSF: 4066 case TCSETSW: 4067 case TCSETA: 4068 case TCSETAW: 4069 case TCSETAF: 4070 mutex_enter(&asy->asy_excl_hi); 4071 if (!asy_baudok(asy)) 4072 error = EINVAL; 4073 else { 4074 if (asy_isbusy(asy)) 4075 asy_waiteot(asy); 4076 asy_program(asy, ASY_NOINIT); 4077 } 4078 mutex_exit(&asy->asy_excl_hi); 4079 break; 4080 } 4081 } else if (error < 0) { 4082 /* 4083 * "ttycommon_ioctl" didn't do anything; we process it here. 4084 */ 4085 error = 0; 4086 switch (iocp->ioc_cmd) { 4087 4088 case TIOCGPPS: 4089 /* 4090 * Get PPS on/off. 4091 */ 4092 if (mp->b_cont != NULL) 4093 freemsg(mp->b_cont); 4094 4095 mp->b_cont = allocb(sizeof (int), BPRI_HI); 4096 if (mp->b_cont == NULL) { 4097 error = ENOMEM; 4098 break; 4099 } 4100 if (asy->asy_flags & ASY_PPS) 4101 *(int *)mp->b_cont->b_wptr = 1; 4102 else 4103 *(int *)mp->b_cont->b_wptr = 0; 4104 mp->b_cont->b_wptr += sizeof (int); 4105 mp->b_datap->db_type = M_IOCACK; 4106 iocp->ioc_count = sizeof (int); 4107 break; 4108 4109 case TIOCSPPS: 4110 /* 4111 * Set PPS on/off. 4112 */ 4113 error = miocpullup(mp, sizeof (int)); 4114 if (error != 0) 4115 break; 4116 4117 mutex_enter(&asy->asy_excl_hi); 4118 if (*(int *)mp->b_cont->b_rptr) 4119 asy->asy_flags |= ASY_PPS; 4120 else 4121 asy->asy_flags &= ~ASY_PPS; 4122 /* Reset edge sense */ 4123 asy->asy_flags &= ~ASY_PPS_EDGE; 4124 mutex_exit(&asy->asy_excl_hi); 4125 mp->b_datap->db_type = M_IOCACK; 4126 break; 4127 4128 case TIOCGPPSEV: 4129 { 4130 /* 4131 * Get PPS event data. 4132 */ 4133 mblk_t *bp; 4134 void *buf; 4135 #ifdef _SYSCALL32_IMPL 4136 struct ppsclockev32 p32; 4137 #endif 4138 struct ppsclockev ppsclockev; 4139 4140 if (mp->b_cont != NULL) { 4141 freemsg(mp->b_cont); 4142 mp->b_cont = NULL; 4143 } 4144 4145 if ((asy->asy_flags & ASY_PPS) == 0) { 4146 error = ENXIO; 4147 break; 4148 } 4149 4150 /* Protect from incomplete asy_ppsev */ 4151 mutex_enter(&asy->asy_excl_hi); 4152 ppsclockev = asy_ppsev; 4153 mutex_exit(&asy->asy_excl_hi); 4154 4155 #ifdef _SYSCALL32_IMPL 4156 if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { 4157 TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); 4158 p32.serial = ppsclockev.serial; 4159 buf = &p32; 4160 iocp->ioc_count = sizeof (struct ppsclockev32); 4161 } else 4162 #endif 4163 { 4164 buf = &ppsclockev; 4165 iocp->ioc_count = sizeof (struct ppsclockev); 4166 } 4167 4168 if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { 4169 error = ENOMEM; 4170 break; 4171 } 4172 mp->b_cont = bp; 4173 4174 bcopy(buf, bp->b_wptr, iocp->ioc_count); 4175 bp->b_wptr += iocp->ioc_count; 4176 mp->b_datap->db_type = M_IOCACK; 4177 break; 4178 } 4179 4180 case TCSBRK: 4181 error = miocpullup(mp, sizeof (int)); 4182 if (error != 0) 4183 break; 4184 4185 if (*(int *)mp->b_cont->b_rptr == 0) { 4186 4187 /* 4188 * XXX Arrangements to ensure that a break 4189 * isn't in progress should be sufficient. 4190 * This ugly delay() is the only thing 4191 * that seems to work on the NCR Worldmark. 4192 * It should be replaced. Note that an 4193 * asy_waiteot() also does not work. 4194 */ 4195 if (asydelay) 4196 delay(drv_usectohz(asydelay)); 4197 4198 while (async->async_flags & ASYNC_BREAK) { 4199 cv_wait(&async->async_flags_cv, 4200 &asy->asy_excl); 4201 } 4202 mutex_enter(&asy->asy_excl_hi); 4203 /* 4204 * Wait until TSR is empty and then set the 4205 * break. ASYNC_BREAK has been set to ensure 4206 * that no characters are transmitted while the 4207 * TSR is being flushed and SOUT is being used 4208 * for the break signal. 4209 */ 4210 async->async_flags |= ASYNC_BREAK; 4211 asy_wait_baudrate(asy); 4212 /* 4213 * Arrange for "async_restart" 4214 * to be called in 1/4 second; 4215 * it will turn the break bit off, and call 4216 * "async_start" to grab the next message. 4217 */ 4218 asy_set(asy, ASY_LCR, ASY_LCR_SETBRK); 4219 mutex_exit(&asy->asy_excl_hi); 4220 (void) timeout(async_restart, (caddr_t)async, 4221 drv_usectohz(1000000)/4); 4222 } else { 4223 ASY_DPRINTF(asy, ASY_DEBUG_OUT, 4224 "wait for flush"); 4225 mutex_enter(&asy->asy_excl_hi); 4226 asy_waiteot(asy); 4227 mutex_exit(&asy->asy_excl_hi); 4228 ASY_DPRINTF(asy, ASY_DEBUG_OUT, 4229 "ldterm satisfied"); 4230 } 4231 break; 4232 4233 case TIOCSBRK: 4234 if (!(async->async_flags & ASYNC_OUT_SUSPEND)) { 4235 mutex_enter(&asy->asy_excl_hi); 4236 async->async_flags |= ASYNC_OUT_SUSPEND; 4237 async->async_flags |= ASYNC_HOLD_UTBRK; 4238 asy_wait_baudrate(asy); 4239 mutex_exit(&asy->asy_excl_hi); 4240 /* wait for 100ms to hold BREAK */ 4241 async->async_utbrktid = 4242 timeout((void (*)())async_hold_utbrk, 4243 (caddr_t)async, 4244 drv_usectohz(asy_min_utbrk)); 4245 } 4246 mioc2ack(mp, NULL, 0, 0); 4247 break; 4248 4249 case TIOCCBRK: 4250 if (async->async_flags & ASYNC_OUT_SUSPEND) 4251 async_resume_utbrk(async); 4252 mioc2ack(mp, NULL, 0, 0); 4253 break; 4254 4255 case TIOCMSET: 4256 case TIOCMBIS: 4257 case TIOCMBIC: 4258 if (iocp->ioc_count != TRANSPARENT) { 4259 ASY_DPRINTF(asy, ASY_DEBUG_IOCTL, 4260 "non-transparent"); 4261 4262 error = miocpullup(mp, sizeof (int)); 4263 if (error != 0) 4264 break; 4265 4266 mutex_enter(&asy->asy_excl_hi); 4267 (void) asymctl(asy, 4268 dmtoasy(asy, *(int *)mp->b_cont->b_rptr), 4269 iocp->ioc_cmd); 4270 mutex_exit(&asy->asy_excl_hi); 4271 iocp->ioc_error = 0; 4272 mp->b_datap->db_type = M_IOCACK; 4273 } else { 4274 ASY_DPRINTF(asy, ASY_DEBUG_IOCTL, 4275 "transparent"); 4276 mcopyin(mp, NULL, sizeof (int), NULL); 4277 } 4278 break; 4279 4280 case TIOCMGET: 4281 datamp = allocb(sizeof (int), BPRI_MED); 4282 if (datamp == NULL) { 4283 error = EAGAIN; 4284 break; 4285 } 4286 4287 mutex_enter(&asy->asy_excl_hi); 4288 *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); 4289 mutex_exit(&asy->asy_excl_hi); 4290 4291 if (iocp->ioc_count == TRANSPARENT) { 4292 ASY_DPRINTF(asy, ASY_DEBUG_IOCTL, 4293 "transparent"); 4294 mcopyout(mp, NULL, sizeof (int), NULL, datamp); 4295 } else { 4296 ASY_DPRINTF(asy, ASY_DEBUG_IOCTL, 4297 "non-transparent"); 4298 mioc2ack(mp, datamp, sizeof (int), 0); 4299 } 4300 break; 4301 4302 case CONSOPENPOLLEDIO: 4303 error = miocpullup(mp, sizeof (struct cons_polledio *)); 4304 if (error != 0) 4305 break; 4306 4307 *(struct cons_polledio **)mp->b_cont->b_rptr = 4308 &asy->polledio; 4309 4310 mp->b_datap->db_type = M_IOCACK; 4311 break; 4312 4313 case CONSCLOSEPOLLEDIO: 4314 mp->b_datap->db_type = M_IOCACK; 4315 iocp->ioc_error = 0; 4316 iocp->ioc_rval = 0; 4317 break; 4318 4319 case CONSSETABORTENABLE: 4320 error = secpolicy_console(iocp->ioc_cr); 4321 if (error != 0) 4322 break; 4323 4324 if (iocp->ioc_count != TRANSPARENT) { 4325 error = EINVAL; 4326 break; 4327 } 4328 4329 mutex_enter(&asy->asy_excl_hi); 4330 if (*(intptr_t *)mp->b_cont->b_rptr) 4331 asy->asy_flags |= ASY_CONSOLE; 4332 else 4333 asy->asy_flags &= ~ASY_CONSOLE; 4334 mutex_exit(&asy->asy_excl_hi); 4335 4336 mp->b_datap->db_type = M_IOCACK; 4337 iocp->ioc_error = 0; 4338 iocp->ioc_rval = 0; 4339 break; 4340 4341 case CONSGETABORTENABLE: 4342 /*CONSTANTCONDITION*/ 4343 ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *)); 4344 /* 4345 * Store the return value right in the payload 4346 * we were passed. Crude. 4347 */ 4348 mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL); 4349 *(boolean_t *)mp->b_cont->b_rptr = 4350 (asy->asy_flags & ASY_CONSOLE) != 0; 4351 break; 4352 4353 default: 4354 /* 4355 * If we don't understand it, it's an error. NAK it. 4356 */ 4357 error = EINVAL; 4358 break; 4359 } 4360 } 4361 if (error != 0) { 4362 iocp->ioc_error = error; 4363 mp->b_datap->db_type = M_IOCNAK; 4364 } 4365 mutex_exit(&asy->asy_excl); 4366 qreply(wq, mp); 4367 ASY_DPRINTF(asy, ASY_DEBUG_PROCS, "done"); 4368 } 4369 4370 static int 4371 asyrsrv(queue_t *q) 4372 { 4373 mblk_t *bp; 4374 struct asyncline *async; 4375 struct asycom *asy; 4376 4377 async = (struct asyncline *)q->q_ptr; 4378 asy = (struct asycom *)async->async_common; 4379 4380 while (canputnext(q) && (bp = getq(q))) 4381 putnext(q, bp); 4382 mutex_enter(&asy->asy_excl_hi); 4383 asysetsoft(asy); 4384 mutex_exit(&asy->asy_excl_hi); 4385 async->async_polltid = 0; 4386 return (0); 4387 } 4388 4389 /* 4390 * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should 4391 * handle messages as though the driver is operating normally or is 4392 * suspended. In the suspended case, some or all of the processing may have 4393 * to be delayed until the driver is resumed. 4394 */ 4395 #define ASYWPUTDO_NOT_SUSP(async, wput) \ 4396 !((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED)) 4397 4398 /* 4399 * Processing for write queue put procedure. 4400 * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; 4401 * set the flow control character for M_STOPI and M_STARTI messages; 4402 * queue up M_BREAK, M_DELAY, and M_DATA messages for processing 4403 * by the start routine, and then call the start routine; discard 4404 * everything else. Note that this driver does not incorporate any 4405 * mechanism to negotiate to handle the canonicalization process. 4406 * It expects that these functions are handled in upper module(s), 4407 * as we do in ldterm. 4408 */ 4409 static int 4410 asywputdo(queue_t *q, mblk_t *mp, boolean_t wput) 4411 { 4412 struct asyncline *async; 4413 struct asycom *asy; 4414 int error; 4415 4416 async = (struct asyncline *)q->q_ptr; 4417 asy = async->async_common; 4418 4419 switch (mp->b_datap->db_type) { 4420 4421 case M_STOP: 4422 /* 4423 * Since we don't do real DMA, we can just let the 4424 * chip coast to a stop after applying the brakes. 4425 */ 4426 mutex_enter(&asy->asy_excl); 4427 async->async_flags |= ASYNC_STOPPED; 4428 mutex_exit(&asy->asy_excl); 4429 freemsg(mp); 4430 break; 4431 4432 case M_START: 4433 mutex_enter(&asy->asy_excl); 4434 if (async->async_flags & ASYNC_STOPPED) { 4435 async->async_flags &= ~ASYNC_STOPPED; 4436 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4437 /* 4438 * If an output operation is in progress, 4439 * resume it. Otherwise, prod the start 4440 * routine. 4441 */ 4442 if (async->async_ocnt > 0) { 4443 mutex_enter(&asy->asy_excl_hi); 4444 async_resume(async); 4445 mutex_exit(&asy->asy_excl_hi); 4446 } else { 4447 async_start(async); 4448 } 4449 } 4450 } 4451 mutex_exit(&asy->asy_excl); 4452 freemsg(mp); 4453 break; 4454 4455 case M_IOCTL: 4456 switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 4457 4458 case TCSBRK: 4459 error = miocpullup(mp, sizeof (int)); 4460 if (error != 0) { 4461 miocnak(q, mp, 0, error); 4462 return (0); 4463 } 4464 4465 if (*(int *)mp->b_cont->b_rptr != 0) { 4466 ASY_DPRINTF(asy, ASY_DEBUG_OUT, 4467 "flush request"); 4468 (void) putq(q, mp); 4469 4470 mutex_enter(&asy->asy_excl); 4471 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4472 /* 4473 * If an TIOCSBRK is in progress, 4474 * clean it as TIOCCBRK does, 4475 * then kick off output. 4476 * If TIOCSBRK is not in progress, 4477 * just kick off output. 4478 */ 4479 async_resume_utbrk(async); 4480 } 4481 mutex_exit(&asy->asy_excl); 4482 break; 4483 } 4484 /*FALLTHROUGH*/ 4485 case TCSETSW: 4486 case TCSETSF: 4487 case TCSETAW: 4488 case TCSETAF: 4489 /* 4490 * The changes do not take effect until all 4491 * output queued before them is drained. 4492 * Put this message on the queue, so that 4493 * "async_start" will see it when it's done 4494 * with the output before it. Poke the 4495 * start routine, just in case. 4496 */ 4497 (void) putq(q, mp); 4498 4499 mutex_enter(&asy->asy_excl); 4500 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4501 /* 4502 * If an TIOCSBRK is in progress, 4503 * clean it as TIOCCBRK does. 4504 * then kick off output. 4505 * If TIOCSBRK is not in progress, 4506 * just kick off output. 4507 */ 4508 async_resume_utbrk(async); 4509 } 4510 mutex_exit(&asy->asy_excl); 4511 break; 4512 4513 default: 4514 /* 4515 * Do it now. 4516 */ 4517 mutex_enter(&asy->asy_excl); 4518 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4519 mutex_exit(&asy->asy_excl); 4520 async_ioctl(async, q, mp); 4521 break; 4522 } 4523 async_put_suspq(asy, mp); 4524 mutex_exit(&asy->asy_excl); 4525 break; 4526 } 4527 break; 4528 4529 case M_FLUSH: 4530 if (*mp->b_rptr & FLUSHW) { 4531 mutex_enter(&asy->asy_excl); 4532 4533 /* 4534 * Abort any output in progress. 4535 */ 4536 mutex_enter(&asy->asy_excl_hi); 4537 if (async->async_flags & ASYNC_BUSY) { 4538 ASY_DPRINTF(asy, ASY_DEBUG_BUSY, 4539 "Clearing async_ocnt, " 4540 "leaving ASYNC_BUSY set"); 4541 async->async_ocnt = 0; 4542 async->async_flags &= ~ASYNC_BUSY; 4543 } /* if */ 4544 4545 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4546 /* Flush FIFO buffers */ 4547 if (asy->asy_use_fifo == ASY_FCR_FIFO_EN) { 4548 asy_reset_fifo(asy, ASY_FCR_THR_FL); 4549 } 4550 } 4551 mutex_exit(&asy->asy_excl_hi); 4552 4553 /* 4554 * Flush our write queue. 4555 */ 4556 flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ 4557 if (async->async_xmitblk != NULL) { 4558 freeb(async->async_xmitblk); 4559 async->async_xmitblk = NULL; 4560 } 4561 mutex_exit(&asy->asy_excl); 4562 *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ 4563 } 4564 if (*mp->b_rptr & FLUSHR) { 4565 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4566 mutex_enter(&asy->asy_excl); 4567 mutex_enter(&asy->asy_excl_hi); 4568 /* Flush FIFO buffers */ 4569 if (asy->asy_use_fifo == ASY_FCR_FIFO_EN) { 4570 asy_reset_fifo(asy, ASY_FCR_RHR_FL); 4571 } 4572 mutex_exit(&asy->asy_excl_hi); 4573 mutex_exit(&asy->asy_excl); 4574 } 4575 flushq(RD(q), FLUSHDATA); 4576 qreply(q, mp); /* give the read queues a crack at it */ 4577 } else { 4578 freemsg(mp); 4579 } 4580 4581 /* 4582 * We must make sure we process messages that survive the 4583 * write-side flush. 4584 */ 4585 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4586 mutex_enter(&asy->asy_excl); 4587 async_start(async); 4588 mutex_exit(&asy->asy_excl); 4589 } 4590 break; 4591 4592 case M_BREAK: 4593 case M_DELAY: 4594 case M_DATA: 4595 /* 4596 * Queue the message up to be transmitted, 4597 * and poke the start routine. 4598 */ 4599 (void) putq(q, mp); 4600 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4601 mutex_enter(&asy->asy_excl); 4602 async_start(async); 4603 mutex_exit(&asy->asy_excl); 4604 } 4605 break; 4606 4607 case M_STOPI: 4608 mutex_enter(&asy->asy_excl); 4609 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4610 mutex_enter(&asy->asy_excl_hi); 4611 if (!(async->async_inflow_source & IN_FLOW_USER)) { 4612 async_flowcontrol_hw_input(asy, FLOW_STOP, 4613 IN_FLOW_USER); 4614 (void) async_flowcontrol_sw_input(asy, 4615 FLOW_STOP, IN_FLOW_USER); 4616 } 4617 mutex_exit(&asy->asy_excl_hi); 4618 mutex_exit(&asy->asy_excl); 4619 freemsg(mp); 4620 break; 4621 } 4622 async_put_suspq(asy, mp); 4623 mutex_exit(&asy->asy_excl); 4624 break; 4625 4626 case M_STARTI: 4627 mutex_enter(&asy->asy_excl); 4628 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4629 mutex_enter(&asy->asy_excl_hi); 4630 if (async->async_inflow_source & IN_FLOW_USER) { 4631 async_flowcontrol_hw_input(asy, FLOW_START, 4632 IN_FLOW_USER); 4633 (void) async_flowcontrol_sw_input(asy, 4634 FLOW_START, IN_FLOW_USER); 4635 } 4636 mutex_exit(&asy->asy_excl_hi); 4637 mutex_exit(&asy->asy_excl); 4638 freemsg(mp); 4639 break; 4640 } 4641 async_put_suspq(asy, mp); 4642 mutex_exit(&asy->asy_excl); 4643 break; 4644 4645 case M_CTL: 4646 if (MBLKL(mp) >= sizeof (struct iocblk) && 4647 ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { 4648 mutex_enter(&asy->asy_excl); 4649 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4650 ((struct iocblk *)mp->b_rptr)->ioc_cmd = 4651 MC_HAS_POSIX; 4652 mutex_exit(&asy->asy_excl); 4653 qreply(q, mp); 4654 break; 4655 } else { 4656 async_put_suspq(asy, mp); 4657 } 4658 } else { 4659 /* 4660 * These MC_SERVICE type messages are used by upper 4661 * modules to tell this driver to send input up 4662 * immediately, or that it can wait for normal 4663 * processing that may or may not be done. Sun 4664 * requires these for the mouse module. 4665 * (XXX - for x86?) 4666 */ 4667 mutex_enter(&asy->asy_excl); 4668 switch (*mp->b_rptr) { 4669 4670 case MC_SERVICEIMM: 4671 async->async_flags |= ASYNC_SERVICEIMM; 4672 break; 4673 4674 case MC_SERVICEDEF: 4675 async->async_flags &= ~ASYNC_SERVICEIMM; 4676 break; 4677 } 4678 mutex_exit(&asy->asy_excl); 4679 freemsg(mp); 4680 } 4681 break; 4682 4683 case M_IOCDATA: 4684 mutex_enter(&asy->asy_excl); 4685 if (ASYWPUTDO_NOT_SUSP(async, wput)) { 4686 mutex_exit(&asy->asy_excl); 4687 async_iocdata(q, mp); 4688 break; 4689 } 4690 async_put_suspq(asy, mp); 4691 mutex_exit(&asy->asy_excl); 4692 break; 4693 4694 default: 4695 freemsg(mp); 4696 break; 4697 } 4698 return (0); 4699 } 4700 4701 static int 4702 asywput(queue_t *q, mblk_t *mp) 4703 { 4704 return (asywputdo(q, mp, B_TRUE)); 4705 } 4706 4707 /* 4708 * Retry an "ioctl", now that "bufcall" claims we may be able to allocate 4709 * the buffer we need. 4710 */ 4711 static void 4712 async_reioctl(void *unit) 4713 { 4714 int instance = (uintptr_t)unit; 4715 struct asyncline *async; 4716 struct asycom *asy; 4717 queue_t *q; 4718 mblk_t *mp; 4719 4720 asy = ddi_get_soft_state(asy_soft_state, instance); 4721 ASSERT(asy != NULL); 4722 async = asy->asy_priv; 4723 4724 /* 4725 * The bufcall is no longer pending. 4726 */ 4727 mutex_enter(&asy->asy_excl); 4728 async->async_wbufcid = 0; 4729 if ((q = async->async_ttycommon.t_writeq) == NULL) { 4730 mutex_exit(&asy->asy_excl); 4731 return; 4732 } 4733 if ((mp = async->async_ttycommon.t_iocpending) != NULL) { 4734 /* not pending any more */ 4735 async->async_ttycommon.t_iocpending = NULL; 4736 mutex_exit(&asy->asy_excl); 4737 async_ioctl(async, q, mp); 4738 } else 4739 mutex_exit(&asy->asy_excl); 4740 } 4741 4742 static void 4743 async_iocdata(queue_t *q, mblk_t *mp) 4744 { 4745 struct asyncline *async = (struct asyncline *)q->q_ptr; 4746 struct asycom *asy; 4747 struct iocblk *ip; 4748 struct copyresp *csp; 4749 4750 asy = async->async_common; 4751 ip = (struct iocblk *)mp->b_rptr; 4752 csp = (struct copyresp *)mp->b_rptr; 4753 4754 if (csp->cp_rval != 0) { 4755 if (csp->cp_private) 4756 freemsg(csp->cp_private); 4757 freemsg(mp); 4758 return; 4759 } 4760 4761 mutex_enter(&asy->asy_excl); 4762 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "case %s", 4763 csp->cp_cmd == TIOCMGET ? "TIOCMGET" : 4764 csp->cp_cmd == TIOCMSET ? "TIOCMSET" : 4765 csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" : 4766 "TIOCMBIC"); 4767 switch (csp->cp_cmd) { 4768 4769 case TIOCMGET: 4770 if (mp->b_cont) { 4771 freemsg(mp->b_cont); 4772 mp->b_cont = NULL; 4773 } 4774 mp->b_datap->db_type = M_IOCACK; 4775 ip->ioc_error = 0; 4776 ip->ioc_count = 0; 4777 ip->ioc_rval = 0; 4778 mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); 4779 break; 4780 4781 case TIOCMSET: 4782 case TIOCMBIS: 4783 case TIOCMBIC: 4784 mutex_enter(&asy->asy_excl_hi); 4785 (void) asymctl(asy, dmtoasy(asy, *(int *)mp->b_cont->b_rptr), 4786 csp->cp_cmd); 4787 mutex_exit(&asy->asy_excl_hi); 4788 mioc2ack(mp, NULL, 0, 0); 4789 break; 4790 4791 default: 4792 mp->b_datap->db_type = M_IOCNAK; 4793 ip->ioc_error = EINVAL; 4794 break; 4795 } 4796 qreply(q, mp); 4797 mutex_exit(&asy->asy_excl); 4798 } 4799 4800 /* 4801 * debugger/console support routines. 4802 */ 4803 4804 /* 4805 * put a character out 4806 * Do not use interrupts. If char is LF, put out CR, LF. 4807 */ 4808 static void 4809 asyputchar(cons_polledio_arg_t arg, uchar_t c) 4810 { 4811 struct asycom *asy = (struct asycom *)arg; 4812 4813 if (c == '\n') 4814 asyputchar(arg, '\r'); 4815 4816 while ((asy_get_reg(asy, ASY_LSR) & ASY_LSR_THRE) == 0) { 4817 /* wait for xmit to finish */ 4818 drv_usecwait(10); 4819 } 4820 4821 /* put the character out */ 4822 asy_put_reg(asy, ASY_THR, c); 4823 } 4824 4825 /* 4826 * See if there's a character available. If no character is 4827 * available, return 0. Run in polled mode, no interrupts. 4828 */ 4829 static boolean_t 4830 asyischar(cons_polledio_arg_t arg) 4831 { 4832 struct asycom *asy = (struct asycom *)arg; 4833 4834 return ((asy_get_reg(asy, ASY_LSR) & ASY_LSR_DR) != 0); 4835 } 4836 4837 /* 4838 * Get a character. Run in polled mode, no interrupts. 4839 */ 4840 static int 4841 asygetchar(cons_polledio_arg_t arg) 4842 { 4843 struct asycom *asy = (struct asycom *)arg; 4844 4845 while (!asyischar(arg)) 4846 drv_usecwait(10); 4847 return (asy_get_reg(asy, ASY_RHR)); 4848 } 4849 4850 /* 4851 * Set or get the modem control status. 4852 */ 4853 static int 4854 asymctl(struct asycom *asy, int bits, int how) 4855 { 4856 int mcr_r, msr_r; 4857 4858 ASSERT(mutex_owned(&asy->asy_excl_hi)); 4859 ASSERT(mutex_owned(&asy->asy_excl)); 4860 4861 /* Read Modem Control Registers */ 4862 mcr_r = asy_get(asy, ASY_MCR); 4863 4864 switch (how) { 4865 4866 case TIOCMSET: 4867 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "TIOCMSET, bits = %x", bits); 4868 mcr_r = bits; /* Set bits */ 4869 break; 4870 4871 case TIOCMBIS: 4872 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "TIOCMBIS, bits = %x", bits); 4873 mcr_r |= bits; /* Mask in bits */ 4874 break; 4875 4876 case TIOCMBIC: 4877 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "TIOCMBIC, bits = %x", bits); 4878 mcr_r &= ~bits; /* Mask out bits */ 4879 break; 4880 4881 case TIOCMGET: 4882 /* Read Modem Status Registers */ 4883 /* 4884 * If modem interrupts are enabled, we return the 4885 * saved value of msr. We read MSR only in async_msint() 4886 */ 4887 if (asy_get(asy, ASY_IER) & ASY_IER_MIEN) { 4888 msr_r = asy->asy_msr; 4889 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 4890 "TIOCMGET, read msr_r = %x", msr_r); 4891 } else { 4892 msr_r = asy_get(asy, ASY_MSR); 4893 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, 4894 "TIOCMGET, read MSR = %x", msr_r); 4895 } 4896 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "modem_lines = %x", 4897 asytodm(mcr_r, msr_r)); 4898 return (asytodm(mcr_r, msr_r)); 4899 } 4900 4901 asy_put(asy, ASY_MCR, mcr_r); 4902 4903 return (mcr_r); 4904 } 4905 4906 static int 4907 asytodm(int mcr_r, int msr_r) 4908 { 4909 int b = 0; 4910 4911 /* MCR registers */ 4912 if (mcr_r & ASY_MCR_RTS) 4913 b |= TIOCM_RTS; 4914 4915 if (mcr_r & ASY_MCR_DTR) 4916 b |= TIOCM_DTR; 4917 4918 /* MSR registers */ 4919 if (msr_r & ASY_MSR_DCD) 4920 b |= TIOCM_CAR; 4921 4922 if (msr_r & ASY_MSR_CTS) 4923 b |= TIOCM_CTS; 4924 4925 if (msr_r & ASY_MSR_DSR) 4926 b |= TIOCM_DSR; 4927 4928 if (msr_r & ASY_MSR_RI) 4929 b |= TIOCM_RNG; 4930 return (b); 4931 } 4932 4933 static int 4934 dmtoasy(struct asycom *asy, int bits) 4935 { 4936 int b = 0; 4937 4938 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "bits = %x", bits); 4939 #ifdef CAN_NOT_SET /* only DTR and RTS can be set */ 4940 if (bits & TIOCM_CAR) 4941 b |= ASY_MSR_DCD; 4942 if (bits & TIOCM_CTS) 4943 b |= ASY_MSR_CTS; 4944 if (bits & TIOCM_DSR) 4945 b |= ASY_MSR_DSR; 4946 if (bits & TIOCM_RNG) 4947 b |= ASY_MSR_RI; 4948 #endif 4949 4950 if (bits & TIOCM_RTS) { 4951 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "set b & RTS"); 4952 b |= ASY_MCR_RTS; 4953 } 4954 if (bits & TIOCM_DTR) { 4955 ASY_DPRINTF(asy, ASY_DEBUG_MODEM, "set b & DTR"); 4956 b |= ASY_MCR_DTR; 4957 } 4958 4959 return (b); 4960 } 4961 4962 static void 4963 asyerror(const struct asycom *asy, int level, const char *fmt, ...) 4964 { 4965 va_list adx; 4966 static time_t last; 4967 static const char *lastfmt; 4968 time_t now; 4969 4970 /* 4971 * Don't print the same error message too often. 4972 * Print the message only if we have not printed the 4973 * message within the last second. 4974 * Note: that fmt cannot be a pointer to a string 4975 * stored on the stack. The fmt pointer 4976 * must be in the data segment otherwise lastfmt would point 4977 * to non-sense. 4978 */ 4979 now = gethrestime_sec(); 4980 if (last == now && lastfmt == fmt) 4981 return; 4982 4983 last = now; 4984 lastfmt = fmt; 4985 4986 va_start(adx, fmt); 4987 vdev_err(asy->asy_dip, level, fmt, adx); 4988 va_end(adx); 4989 } 4990 4991 /* 4992 * asy_parse_mode(dev_info_t *devi, struct asycom *asy) 4993 * The value of this property is in the form of "9600,8,n,1,-" 4994 * 1) speed: 9600, 4800, ... 4995 * 2) data bits 4996 * 3) parity: n(none), e(even), o(odd) 4997 * 4) stop bits 4998 * 5) handshake: -(none), h(hardware: rts/cts), s(software: xon/off) 4999 * 5000 * This parsing came from a SPARCstation eeprom. 5001 */ 5002 static void 5003 asy_parse_mode(dev_info_t *devi, struct asycom *asy) 5004 { 5005 char name[40]; 5006 char val[40]; 5007 int len; 5008 int ret; 5009 char *p; 5010 char *p1; 5011 5012 ASSERT(asy->asy_com_port != 0); 5013 5014 /* 5015 * Parse the ttyx-mode property 5016 */ 5017 (void) sprintf(name, "tty%c-mode", asy->asy_com_port + 'a' - 1); 5018 len = sizeof (val); 5019 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 5020 if (ret != DDI_PROP_SUCCESS) { 5021 (void) sprintf(name, "com%c-mode", asy->asy_com_port + '0'); 5022 len = sizeof (val); 5023 ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 5024 } 5025 5026 /* no property to parse */ 5027 asy->asy_cflag = 0; 5028 if (ret != DDI_PROP_SUCCESS) 5029 return; 5030 5031 p = val; 5032 /* ---- baud rate ---- */ 5033 asy->asy_cflag = CREAD|B9600; /* initial default */ 5034 if (p && (p1 = strchr(p, ',')) != 0) { 5035 *p1++ = '\0'; 5036 } else { 5037 asy->asy_cflag |= ASY_LCR_BITS8; /* add default bits */ 5038 return; 5039 } 5040 5041 if (strcmp(p, "110") == 0) 5042 asy->asy_bidx = B110; 5043 else if (strcmp(p, "150") == 0) 5044 asy->asy_bidx = B150; 5045 else if (strcmp(p, "300") == 0) 5046 asy->asy_bidx = B300; 5047 else if (strcmp(p, "600") == 0) 5048 asy->asy_bidx = B600; 5049 else if (strcmp(p, "1200") == 0) 5050 asy->asy_bidx = B1200; 5051 else if (strcmp(p, "2400") == 0) 5052 asy->asy_bidx = B2400; 5053 else if (strcmp(p, "4800") == 0) 5054 asy->asy_bidx = B4800; 5055 else if (strcmp(p, "9600") == 0) 5056 asy->asy_bidx = B9600; 5057 else if (strcmp(p, "19200") == 0) 5058 asy->asy_bidx = B19200; 5059 else if (strcmp(p, "38400") == 0) 5060 asy->asy_bidx = B38400; 5061 else if (strcmp(p, "57600") == 0) 5062 asy->asy_bidx = B57600; 5063 else if (strcmp(p, "115200") == 0) 5064 asy->asy_bidx = B115200; 5065 else 5066 asy->asy_bidx = B9600; 5067 5068 asy->asy_cflag &= ~CBAUD; 5069 if (asy->asy_bidx > CBAUD) { /* > 38400 uses the CBAUDEXT bit */ 5070 asy->asy_cflag |= CBAUDEXT; 5071 asy->asy_cflag |= asy->asy_bidx - CBAUD - 1; 5072 } else { 5073 asy->asy_cflag |= asy->asy_bidx; 5074 } 5075 5076 ASSERT(asy->asy_bidx == BAUDINDEX(asy->asy_cflag)); 5077 5078 /* ---- Next item is data bits ---- */ 5079 p = p1; 5080 if (p && (p1 = strchr(p, ',')) != 0) { 5081 *p1++ = '\0'; 5082 } else { 5083 asy->asy_cflag |= ASY_LCR_BITS8; /* add default bits */ 5084 return; 5085 } 5086 switch (*p) { 5087 default: 5088 case '8': 5089 asy->asy_cflag |= CS8; 5090 asy->asy_lcr = ASY_LCR_BITS8; 5091 break; 5092 case '7': 5093 asy->asy_cflag |= CS7; 5094 asy->asy_lcr = ASY_LCR_BITS7; 5095 break; 5096 case '6': 5097 asy->asy_cflag |= CS6; 5098 asy->asy_lcr = ASY_LCR_BITS6; 5099 break; 5100 case '5': 5101 /* LINTED: CS5 is currently zero (but might change) */ 5102 asy->asy_cflag |= CS5; 5103 asy->asy_lcr = ASY_LCR_BITS5; 5104 break; 5105 } 5106 5107 /* ---- Parity info ---- */ 5108 p = p1; 5109 if (p && (p1 = strchr(p, ',')) != 0) { 5110 *p1++ = '\0'; 5111 } else { 5112 return; 5113 } 5114 switch (*p) { 5115 default: 5116 case 'n': 5117 break; 5118 case 'e': 5119 asy->asy_cflag |= PARENB; 5120 asy->asy_lcr |= ASY_LCR_PEN; 5121 break; 5122 case 'o': 5123 asy->asy_cflag |= PARENB|PARODD; 5124 asy->asy_lcr |= ASY_LCR_PEN | ASY_LCR_EPS; 5125 break; 5126 } 5127 5128 /* ---- Find stop bits ---- */ 5129 p = p1; 5130 if (p && (p1 = strchr(p, ',')) != 0) { 5131 *p1++ = '\0'; 5132 } else { 5133 return; 5134 } 5135 if (*p == '2') { 5136 asy->asy_cflag |= CSTOPB; 5137 asy->asy_lcr |= ASY_LCR_STB; 5138 } 5139 5140 /* ---- handshake is next ---- */ 5141 p = p1; 5142 if (p) { 5143 if ((p1 = strchr(p, ',')) != 0) 5144 *p1++ = '\0'; 5145 5146 if (*p == 'h') 5147 asy->asy_cflag |= CRTSCTS; 5148 else if (*p == 's') 5149 asy->asy_cflag |= CRTSXOFF; 5150 } 5151 } 5152 5153 /* 5154 * Check for abort character sequence 5155 */ 5156 static boolean_t 5157 abort_charseq_recognize(uchar_t ch) 5158 { 5159 static int state = 0; 5160 #define CNTRL(c) ((c)&037) 5161 static char sequence[] = { '\r', '~', CNTRL('b') }; 5162 5163 if (ch == sequence[state]) { 5164 if (++state >= sizeof (sequence)) { 5165 state = 0; 5166 return (B_TRUE); 5167 } 5168 } else { 5169 state = (ch == sequence[0]) ? 1 : 0; 5170 } 5171 return (B_FALSE); 5172 } 5173 5174 /* 5175 * Flow control functions 5176 */ 5177 /* 5178 * Software input flow control 5179 * This function can execute software input flow control sucessfully 5180 * at most of situations except that the line is in BREAK status 5181 * (timed and untimed break). 5182 * INPUT VALUE of onoff: 5183 * FLOW_START means to send out a XON char 5184 * and clear SW input flow control flag. 5185 * FLOW_STOP means to send out a XOFF char 5186 * and set SW input flow control flag. 5187 * FLOW_CHECK means to check whether there is pending XON/XOFF 5188 * if it is true, send it out. 5189 * INPUT VALUE of type: 5190 * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 5191 * IN_FLOW_STREAMS means flow control is due to STREAMS 5192 * IN_FLOW_USER means flow control is due to user's commands 5193 * RETURN VALUE: B_FALSE means no flow control char is sent 5194 * B_TRUE means one flow control char is sent 5195 */ 5196 static boolean_t 5197 async_flowcontrol_sw_input(struct asycom *asy, async_flowc_action onoff, 5198 int type) 5199 { 5200 struct asyncline *async = asy->asy_priv; 5201 int rval = B_FALSE; 5202 5203 ASSERT(mutex_owned(&asy->asy_excl_hi)); 5204 5205 if (!(async->async_ttycommon.t_iflag & IXOFF)) 5206 return (rval); 5207 5208 /* 5209 * If we get this far, then we know IXOFF is set. 5210 */ 5211 switch (onoff) { 5212 case FLOW_STOP: 5213 async->async_inflow_source |= type; 5214 5215 /* 5216 * We'll send an XOFF character for each of up to 5217 * three different input flow control attempts to stop input. 5218 * If we already send out one XOFF, but FLOW_STOP comes again, 5219 * it seems that input flow control becomes more serious, 5220 * then send XOFF again. 5221 */ 5222 if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 5223 IN_FLOW_STREAMS | IN_FLOW_USER)) 5224 async->async_flags |= ASYNC_SW_IN_FLOW | 5225 ASYNC_SW_IN_NEEDED; 5226 ASY_DPRINTF(asy, ASY_DEBUG_SFLOW, "input sflow stop, type = %x", 5227 async->async_inflow_source); 5228 break; 5229 case FLOW_START: 5230 async->async_inflow_source &= ~type; 5231 if (async->async_inflow_source == 0) { 5232 async->async_flags = (async->async_flags & 5233 ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED; 5234 ASY_DPRINTF(asy, ASY_DEBUG_SFLOW, "input sflow start"); 5235 } 5236 break; 5237 default: 5238 break; 5239 } 5240 5241 if (((async->async_flags & (ASYNC_SW_IN_NEEDED | ASYNC_BREAK | 5242 ASYNC_OUT_SUSPEND)) == ASYNC_SW_IN_NEEDED) && 5243 (asy_get(asy, ASY_LSR) & ASY_LSR_THRE)) { 5244 /* 5245 * If we get this far, then we know we need to send out 5246 * XON or XOFF char. 5247 */ 5248 async->async_flags = (async->async_flags & 5249 ~ASYNC_SW_IN_NEEDED) | ASYNC_BUSY; 5250 asy_put(asy, ASY_THR, 5251 async->async_flags & ASYNC_SW_IN_FLOW ? 5252 async->async_stopc : async->async_startc); 5253 rval = B_TRUE; 5254 } 5255 return (rval); 5256 } 5257 5258 /* 5259 * Software output flow control 5260 * This function can be executed sucessfully at any situation. 5261 * It does not handle HW, and just change the SW output flow control flag. 5262 * INPUT VALUE of onoff: 5263 * FLOW_START means to clear SW output flow control flag, 5264 * also combine with HW output flow control status to 5265 * determine if we need to set ASYNC_OUT_FLW_RESUME. 5266 * FLOW_STOP means to set SW output flow control flag, 5267 * also clear ASYNC_OUT_FLW_RESUME. 5268 */ 5269 static void 5270 async_flowcontrol_sw_output(struct asycom *asy, async_flowc_action onoff) 5271 { 5272 struct asyncline *async = asy->asy_priv; 5273 5274 ASSERT(mutex_owned(&asy->asy_excl_hi)); 5275 5276 if (!(async->async_ttycommon.t_iflag & IXON)) 5277 return; 5278 5279 switch (onoff) { 5280 case FLOW_STOP: 5281 async->async_flags |= ASYNC_SW_OUT_FLW; 5282 async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 5283 ASY_DPRINTF(asy, ASY_DEBUG_SFLOW, "output sflow stop"); 5284 break; 5285 case FLOW_START: 5286 async->async_flags &= ~ASYNC_SW_OUT_FLW; 5287 if (!(async->async_flags & ASYNC_HW_OUT_FLW)) 5288 async->async_flags |= ASYNC_OUT_FLW_RESUME; 5289 ASY_DPRINTF(asy, ASY_DEBUG_SFLOW, "output sflow start"); 5290 break; 5291 default: 5292 break; 5293 } 5294 } 5295 5296 /* 5297 * Hardware input flow control 5298 * This function can be executed sucessfully at any situation. 5299 * It directly changes RTS depending on input parameter onoff. 5300 * INPUT VALUE of onoff: 5301 * FLOW_START means to clear HW input flow control flag, 5302 * and pull up RTS if it is low. 5303 * FLOW_STOP means to set HW input flow control flag, 5304 * and low RTS if it is high. 5305 * INPUT VALUE of type: 5306 * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 5307 * IN_FLOW_STREAMS means flow control is due to STREAMS 5308 * IN_FLOW_USER means flow control is due to user's commands 5309 */ 5310 static void 5311 async_flowcontrol_hw_input(struct asycom *asy, async_flowc_action onoff, 5312 int type) 5313 { 5314 uchar_t mcr; 5315 uchar_t flag; 5316 struct asyncline *async = asy->asy_priv; 5317 5318 ASSERT(mutex_owned(&asy->asy_excl_hi)); 5319 5320 if (!(async->async_ttycommon.t_cflag & CRTSXOFF)) 5321 return; 5322 5323 switch (onoff) { 5324 case FLOW_STOP: 5325 async->async_inflow_source |= type; 5326 if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 5327 IN_FLOW_STREAMS | IN_FLOW_USER)) 5328 async->async_flags |= ASYNC_HW_IN_FLOW; 5329 ASY_DPRINTF(asy, ASY_DEBUG_HFLOW, "input hflow stop, type = %x", 5330 async->async_inflow_source); 5331 break; 5332 case FLOW_START: 5333 async->async_inflow_source &= ~type; 5334 if (async->async_inflow_source == 0) { 5335 async->async_flags &= ~ASYNC_HW_IN_FLOW; 5336 ASY_DPRINTF(asy, ASY_DEBUG_HFLOW, "input hflow start"); 5337 } 5338 break; 5339 default: 5340 break; 5341 } 5342 mcr = asy_get(asy, ASY_MCR); 5343 flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : ASY_MCR_RTS; 5344 5345 if (((mcr ^ flag) & ASY_MCR_RTS) != 0) { 5346 asy_put(asy, ASY_MCR, (mcr ^ ASY_MCR_RTS)); 5347 } 5348 } 5349 5350 /* 5351 * Hardware output flow control 5352 * This function can execute HW output flow control sucessfully 5353 * at any situation. 5354 * It doesn't really change RTS, and just change 5355 * HW output flow control flag depending on CTS status. 5356 * INPUT VALUE of onoff: 5357 * FLOW_START means to clear HW output flow control flag. 5358 * also combine with SW output flow control status to 5359 * determine if we need to set ASYNC_OUT_FLW_RESUME. 5360 * FLOW_STOP means to set HW output flow control flag. 5361 * also clear ASYNC_OUT_FLW_RESUME. 5362 */ 5363 static void 5364 async_flowcontrol_hw_output(struct asycom *asy, async_flowc_action onoff) 5365 { 5366 struct asyncline *async = asy->asy_priv; 5367 5368 ASSERT(mutex_owned(&asy->asy_excl_hi)); 5369 5370 if (!(async->async_ttycommon.t_cflag & CRTSCTS)) 5371 return; 5372 5373 switch (onoff) { 5374 case FLOW_STOP: 5375 async->async_flags |= ASYNC_HW_OUT_FLW; 5376 async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 5377 ASY_DPRINTF(asy, ASY_DEBUG_HFLOW, "output hflow stop"); 5378 break; 5379 case FLOW_START: 5380 async->async_flags &= ~ASYNC_HW_OUT_FLW; 5381 if (!(async->async_flags & ASYNC_SW_OUT_FLW)) 5382 async->async_flags |= ASYNC_OUT_FLW_RESUME; 5383 ASY_DPRINTF(asy, ASY_DEBUG_HFLOW, "output hflow start"); 5384 break; 5385 default: 5386 break; 5387 } 5388 } 5389 5390 /* 5391 * quiesce(9E) entry point. 5392 * 5393 * This function is called when the system is single-threaded at high 5394 * PIL with preemption disabled. Therefore, this function must not be 5395 * blocked. 5396 * 5397 * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 5398 * DDI_FAILURE indicates an error condition and should almost never happen. 5399 */ 5400 static int 5401 asyquiesce(dev_info_t *devi) 5402 { 5403 int instance; 5404 struct asycom *asy; 5405 5406 instance = ddi_get_instance(devi); /* find out which unit */ 5407 5408 asy = ddi_get_soft_state(asy_soft_state, instance); 5409 if (asy == NULL) 5410 return (DDI_FAILURE); 5411 5412 asy_disable_interrupts(asy, ASY_IER_ALL); 5413 5414 /* Flush the FIFOs */ 5415 asy_reset_fifo(asy, ASY_FCR_THR_FL | ASY_FCR_RHR_FL); 5416 5417 return (DDI_SUCCESS); 5418 } 5419