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