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