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