152Sdf157793 /* 252Sdf157793 * CDDL HEADER START 352Sdf157793 * 452Sdf157793 * The contents of this file are subject to the terms of the 51815Srameshc * Common Development and Distribution License (the "License"). 61815Srameshc * You may not use this file except in compliance with the License. 752Sdf157793 * 852Sdf157793 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 952Sdf157793 * or http://www.opensolaris.org/os/licensing. 1052Sdf157793 * See the License for the specific language governing permissions 1152Sdf157793 * and limitations under the License. 1252Sdf157793 * 1352Sdf157793 * When distributing Covered Code, include this CDDL HEADER in each 1452Sdf157793 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 1552Sdf157793 * If applicable, add the following below this CDDL HEADER, with the 1652Sdf157793 * fields enclosed by brackets "[]" replaced with your own identifying 1752Sdf157793 * information: Portions Copyright [yyyy] [name of copyright owner] 1852Sdf157793 * 1952Sdf157793 * CDDL HEADER END 2052Sdf157793 */ 2152Sdf157793 /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */ 2252Sdf157793 /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */ 2352Sdf157793 /* All Rights Reserved */ 2452Sdf157793 2552Sdf157793 /* 261434Skc28005 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 2752Sdf157793 * Use is subject to license terms. 2852Sdf157793 */ 2952Sdf157793 3052Sdf157793 #pragma ident "%Z%%M% %I% %E% SMI" 3152Sdf157793 3252Sdf157793 /* 3352Sdf157793 * Serial I/O driver for 82510/8250/16450/16550AF chips. 3452Sdf157793 * Modified as sparc keyboard/mouse driver. 3552Sdf157793 */ 3652Sdf157793 #define SU_REGISTER_FILE_NO 0 3752Sdf157793 #define SU_REGOFFSET 0 3852Sdf157793 #define SU_REGISTER_LEN 8 3952Sdf157793 4052Sdf157793 #include <sys/param.h> 4152Sdf157793 #include <sys/types.h> 4252Sdf157793 #include <sys/signal.h> 4352Sdf157793 #include <sys/stream.h> 4452Sdf157793 #include <sys/termio.h> 4552Sdf157793 #include <sys/errno.h> 4652Sdf157793 #include <sys/file.h> 4752Sdf157793 #include <sys/cmn_err.h> 4852Sdf157793 #include <sys/stropts.h> 4952Sdf157793 #include <sys/strsubr.h> 5052Sdf157793 #include <sys/strsun.h> 5152Sdf157793 #include <sys/strtty.h> 5252Sdf157793 #include <sys/debug.h> 5352Sdf157793 #include <sys/kbio.h> 5452Sdf157793 #include <sys/cred.h> 5552Sdf157793 #include <sys/modctl.h> 5652Sdf157793 #include <sys/stat.h> 5752Sdf157793 #include <sys/consdev.h> 5852Sdf157793 #include <sys/mkdev.h> 5952Sdf157793 #include <sys/kmem.h> 6052Sdf157793 #include <sys/cred.h> 6152Sdf157793 #ifdef DEBUG 6252Sdf157793 #include <sys/promif.h> 6352Sdf157793 #endif 6452Sdf157793 #include <sys/ddi.h> 6552Sdf157793 #include <sys/sunddi.h> 6652Sdf157793 #include <sys/sudev.h> 6752Sdf157793 #include <sys/note.h> 6852Sdf157793 #include <sys/timex.h> 6952Sdf157793 #include <sys/policy.h> 7052Sdf157793 7152Sdf157793 #define async_stopc async_ttycommon.t_stopc 7252Sdf157793 #define async_startc async_ttycommon.t_startc 7352Sdf157793 7452Sdf157793 #define ASY_INIT 1 7552Sdf157793 #define ASY_NOINIT 0 7652Sdf157793 7752Sdf157793 #ifdef DEBUG 7852Sdf157793 #define ASY_DEBUG_INIT 0x001 7952Sdf157793 #define ASY_DEBUG_INPUT 0x002 8052Sdf157793 #define ASY_DEBUG_EOT 0x004 8152Sdf157793 #define ASY_DEBUG_CLOSE 0x008 8252Sdf157793 #define ASY_DEBUG_HFLOW 0x010 8352Sdf157793 #define ASY_DEBUG_PROCS 0x020 8452Sdf157793 #define ASY_DEBUG_STATE 0x040 8552Sdf157793 #define ASY_DEBUG_INTR 0x080 8652Sdf157793 static int asydebug = 0; 8752Sdf157793 #endif 8852Sdf157793 static int su_log = 0; 8952Sdf157793 9052Sdf157793 int su_drain_check = 15000000; /* tunable: exit drain check time */ 9152Sdf157793 9252Sdf157793 static struct ppsclockev asy_ppsev; 9352Sdf157793 9452Sdf157793 static int max_asy_instance = -1; 9552Sdf157793 static void *su_asycom; /* soft state asycom pointer */ 9652Sdf157793 static void *su_asyncline; /* soft state asyncline pointer */ 9752Sdf157793 static boolean_t abort_charseq_recognize(uchar_t ch); 9852Sdf157793 9952Sdf157793 static uint_t asysoftintr(caddr_t intarg); 10052Sdf157793 static uint_t asyintr(caddr_t argasy); 10152Sdf157793 10252Sdf157793 /* The async interrupt entry points */ 10352Sdf157793 static void async_txint(struct asycom *asy, uchar_t lsr); 10452Sdf157793 static void async_rxint(struct asycom *asy, uchar_t lsr); 10552Sdf157793 static void async_msint(struct asycom *asy); 10652Sdf157793 static int async_softint(struct asycom *asy); 10752Sdf157793 10852Sdf157793 static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp, 10952Sdf157793 boolean_t iswput); 11052Sdf157793 static void async_reioctl(void *); 11152Sdf157793 static void async_iocdata(queue_t *q, mblk_t *mp); 11252Sdf157793 static void async_restart(void *); 11352Sdf157793 static void async_start(struct asyncline *async); 11452Sdf157793 static void async_nstart(struct asyncline *async, int mode); 11552Sdf157793 static void async_resume(struct asyncline *async); 11652Sdf157793 static int asy_program(struct asycom *asy, int mode); 11752Sdf157793 11852Sdf157793 static int asymctl(struct asycom *, int, int); 11952Sdf157793 static int asytodm(int, int); 12052Sdf157793 static int dmtoasy(int); 12152Sdf157793 static void asycheckflowcontrol_hw(struct asycom *asy); 12252Sdf157793 static boolean_t asycheckflowcontrol_sw(struct asycom *asy); 12352Sdf157793 static void asy_ppsevent(struct asycom *asy, int msr); 12452Sdf157793 12552Sdf157793 extern kcondvar_t lbolt_cv; 12652Sdf157793 extern int ddi_create_internal_pathname(dev_info_t *dip, char *name, 12752Sdf157793 int spec_type, minor_t minor_num); 12852Sdf157793 12952Sdf157793 13052Sdf157793 /* 13152Sdf157793 * Baud rate table. Indexed by #defines found in sys/termios.h 13252Sdf157793 */ 13352Sdf157793 ushort_t asyspdtab[] = { 13452Sdf157793 0, /* 0 baud rate */ 13552Sdf157793 0x900, /* 50 baud rate */ 13652Sdf157793 0x600, /* 75 baud rate */ 13752Sdf157793 0x417, /* 110 baud rate (%0.026) */ 13852Sdf157793 0x359, /* 134 baud rate (%0.058) */ 13952Sdf157793 0x300, /* 150 baud rate */ 14052Sdf157793 0x240, /* 200 baud rate */ 14152Sdf157793 0x180, /* 300 baud rate */ 14252Sdf157793 0x0c0, /* 600 baud rate */ 14352Sdf157793 0x060, /* 1200 baud rate */ 14452Sdf157793 0x040, /* 1800 baud rate */ 14552Sdf157793 0x030, /* 2400 baud rate */ 14652Sdf157793 0x018, /* 4800 baud rate */ 14752Sdf157793 0x00c, /* 9600 baud rate */ 14852Sdf157793 0x006, /* 19200 baud rate */ 14952Sdf157793 0x003, /* 38400 baud rate */ 15052Sdf157793 0x002, /* 57600 baud rate */ 15152Sdf157793 0, /* 76800 baud rate - not supported */ 15252Sdf157793 0x001, /* 115200 baud rate */ 15352Sdf157793 0, /* 153600 baud rate - not supported */ 15452Sdf157793 0x8002, /* 230400 baud rate - supported on specific platforms */ 15552Sdf157793 0, /* 307200 baud rate - not supported */ 15652Sdf157793 0x8001 /* 460800 baud rate - supported on specific platforms */ 15752Sdf157793 }; 15852Sdf157793 15952Sdf157793 /* 16052Sdf157793 * Number of speeds supported is the number of entries in 16152Sdf157793 * the above table. 16252Sdf157793 */ 16352Sdf157793 #define N_SU_SPEEDS (sizeof (asyspdtab)/sizeof (ushort_t)) 16452Sdf157793 16552Sdf157793 /* 16652Sdf157793 * Human-readable baud rate table. 16752Sdf157793 * Indexed by #defines found in sys/termios.h 16852Sdf157793 */ 16952Sdf157793 int baudtable[] = { 17052Sdf157793 0, /* 0 baud rate */ 17152Sdf157793 50, /* 50 baud rate */ 17252Sdf157793 75, /* 75 baud rate */ 17352Sdf157793 110, /* 110 baud rate */ 17452Sdf157793 134, /* 134 baud rate */ 17552Sdf157793 150, /* 150 baud rate */ 17652Sdf157793 200, /* 200 baud rate */ 17752Sdf157793 300, /* 300 baud rate */ 17852Sdf157793 600, /* 600 baud rate */ 17952Sdf157793 1200, /* 1200 baud rate */ 18052Sdf157793 1800, /* 1800 baud rate */ 18152Sdf157793 2400, /* 2400 baud rate */ 18252Sdf157793 4800, /* 4800 baud rate */ 18352Sdf157793 9600, /* 9600 baud rate */ 18452Sdf157793 19200, /* 19200 baud rate */ 18552Sdf157793 38400, /* 38400 baud rate */ 18652Sdf157793 57600, /* 57600 baud rate */ 18752Sdf157793 76800, /* 76800 baud rate */ 18852Sdf157793 115200, /* 115200 baud rate */ 18952Sdf157793 153600, /* 153600 baud rate */ 19052Sdf157793 230400, /* 230400 baud rate */ 19152Sdf157793 307200, /* 307200 baud rate */ 19252Sdf157793 460800 /* 460800 baud rate */ 19352Sdf157793 }; 19452Sdf157793 19552Sdf157793 static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); 19652Sdf157793 static int asyclose(queue_t *q, int flag); 19752Sdf157793 static void asywput(queue_t *q, mblk_t *mp); 19852Sdf157793 static void asyrsrv(queue_t *q); 19952Sdf157793 20052Sdf157793 struct module_info asy_info = { 20152Sdf157793 0, 20252Sdf157793 "su", 20352Sdf157793 0, 20452Sdf157793 INFPSZ, 20552Sdf157793 32*4096, 20652Sdf157793 4096 20752Sdf157793 }; 20852Sdf157793 20952Sdf157793 static struct qinit asy_rint = { 21052Sdf157793 putq, 21152Sdf157793 (int (*)())asyrsrv, 21252Sdf157793 asyopen, 21352Sdf157793 asyclose, 21452Sdf157793 NULL, 21552Sdf157793 &asy_info, 21652Sdf157793 NULL 21752Sdf157793 }; 21852Sdf157793 21952Sdf157793 static struct qinit asy_wint = { 22052Sdf157793 (int (*)())asywput, 22152Sdf157793 NULL, 22252Sdf157793 NULL, 22352Sdf157793 NULL, 22452Sdf157793 NULL, 22552Sdf157793 &asy_info, 22652Sdf157793 NULL 22752Sdf157793 }; 22852Sdf157793 22952Sdf157793 struct streamtab asy_str_info = { 23052Sdf157793 &asy_rint, 23152Sdf157793 &asy_wint, 23252Sdf157793 NULL, 23352Sdf157793 NULL 23452Sdf157793 }; 23552Sdf157793 23652Sdf157793 static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 23752Sdf157793 void **result); 23852Sdf157793 static int asyprobe(dev_info_t *); 23952Sdf157793 static int asyattach(dev_info_t *, ddi_attach_cmd_t); 24052Sdf157793 static int asydetach(dev_info_t *, ddi_detach_cmd_t); 24152Sdf157793 24252Sdf157793 static struct cb_ops cb_asy_ops = { 24352Sdf157793 nodev, /* cb_open */ 24452Sdf157793 nodev, /* cb_close */ 24552Sdf157793 nodev, /* cb_strategy */ 24652Sdf157793 nodev, /* cb_print */ 24752Sdf157793 nodev, /* cb_dump */ 24852Sdf157793 nodev, /* cb_read */ 24952Sdf157793 nodev, /* cb_write */ 25052Sdf157793 nodev, /* cb_ioctl */ 25152Sdf157793 nodev, /* cb_devmap */ 25252Sdf157793 nodev, /* cb_mmap */ 25352Sdf157793 nodev, /* cb_segmap */ 25452Sdf157793 nochpoll, /* cb_chpoll */ 25552Sdf157793 ddi_prop_op, /* cb_prop_op */ 25652Sdf157793 &asy_str_info, /* cb_stream */ 25752Sdf157793 D_MP /* cb_flag */ 25852Sdf157793 }; 25952Sdf157793 26052Sdf157793 struct dev_ops asy_ops = { 26152Sdf157793 DEVO_REV, /* devo_rev */ 26252Sdf157793 0, /* devo_refcnt */ 26352Sdf157793 asyinfo, /* devo_getinfo */ 26452Sdf157793 nulldev, /* devo_identify */ 26552Sdf157793 asyprobe, /* devo_probe */ 26652Sdf157793 asyattach, /* devo_attach */ 26752Sdf157793 asydetach, /* devo_detach */ 26852Sdf157793 nodev, /* devo_reset */ 26952Sdf157793 &cb_asy_ops, /* devo_cb_ops */ 27052Sdf157793 }; 27152Sdf157793 27252Sdf157793 /* 27352Sdf157793 * Module linkage information for the kernel. 27452Sdf157793 */ 27552Sdf157793 27652Sdf157793 static struct modldrv modldrv = { 27752Sdf157793 &mod_driverops, /* Type of module. This one is a driver */ 27852Sdf157793 "su driver %I%", 27952Sdf157793 &asy_ops, /* driver ops */ 28052Sdf157793 }; 28152Sdf157793 28252Sdf157793 static struct modlinkage modlinkage = { 28352Sdf157793 MODREV_1, 28452Sdf157793 &modldrv, 28552Sdf157793 NULL 28652Sdf157793 }; 28752Sdf157793 28852Sdf157793 int 28952Sdf157793 _init(void) 29052Sdf157793 { 29152Sdf157793 int status; 29252Sdf157793 29352Sdf157793 status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom), 29452Sdf157793 SU_INITIAL_SOFT_ITEMS); 29552Sdf157793 if (status != 0) 29652Sdf157793 return (status); 29752Sdf157793 status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline), 29852Sdf157793 SU_INITIAL_SOFT_ITEMS); 29952Sdf157793 if (status != 0) { 30052Sdf157793 ddi_soft_state_fini(&su_asycom); 30152Sdf157793 return (status); 30252Sdf157793 } 30352Sdf157793 30452Sdf157793 if ((status = mod_install(&modlinkage)) != 0) { 30552Sdf157793 ddi_soft_state_fini(&su_asycom); 30652Sdf157793 ddi_soft_state_fini(&su_asyncline); 30752Sdf157793 } 30852Sdf157793 30952Sdf157793 return (status); 31052Sdf157793 } 31152Sdf157793 31252Sdf157793 int 31352Sdf157793 _fini(void) 31452Sdf157793 { 31552Sdf157793 int i; 31652Sdf157793 31752Sdf157793 i = mod_remove(&modlinkage); 31852Sdf157793 if (i == 0) { 31952Sdf157793 ddi_soft_state_fini(&su_asycom); 32052Sdf157793 ddi_soft_state_fini(&su_asyncline); 32152Sdf157793 } 32252Sdf157793 32352Sdf157793 return (i); 32452Sdf157793 } 32552Sdf157793 32652Sdf157793 int 32752Sdf157793 _info(struct modinfo *modinfop) 32852Sdf157793 { 32952Sdf157793 return (mod_info(&modlinkage, modinfop)); 33052Sdf157793 } 33152Sdf157793 33252Sdf157793 static int 33352Sdf157793 asyprobe(dev_info_t *devi) 33452Sdf157793 { 33552Sdf157793 int instance; 33652Sdf157793 ddi_acc_handle_t handle; 33752Sdf157793 uchar_t *addr; 33852Sdf157793 ddi_device_acc_attr_t attr; 33952Sdf157793 34052Sdf157793 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 34152Sdf157793 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 34252Sdf157793 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 34352Sdf157793 34452Sdf157793 if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr, 34552Sdf157793 SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) { 34652Sdf157793 cmn_err(CE_WARN, "asyprobe regs map setup failed"); 34752Sdf157793 return (DDI_PROBE_FAILURE); 34852Sdf157793 } 34952Sdf157793 #ifdef DEBUG 35052Sdf157793 if (asydebug) 35152Sdf157793 printf("Probe address mapped %p\n", (void *)addr); 35252Sdf157793 #endif 35352Sdf157793 35452Sdf157793 /* 35552Sdf157793 * Probe for the device: 35652Sdf157793 * Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low. 35752Sdf157793 * If bit 4 or 5 appears on inb() ISR, board is not there. 35852Sdf157793 */ 35952Sdf157793 if (ddi_get8(handle, addr+ISR) & 0x30) 36052Sdf157793 return (DDI_PROBE_FAILURE); 36152Sdf157793 instance = ddi_get_instance(devi); 36252Sdf157793 if (max_asy_instance < instance) 36352Sdf157793 max_asy_instance = instance; 36452Sdf157793 ddi_regs_map_free(&handle); 36552Sdf157793 36652Sdf157793 return (DDI_PROBE_SUCCESS); /* hw is present */ 36752Sdf157793 } 36852Sdf157793 36952Sdf157793 static int 37052Sdf157793 asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) 37152Sdf157793 { 37252Sdf157793 register int instance; 37352Sdf157793 struct asycom *asy; 37452Sdf157793 struct asyncline *async; 37552Sdf157793 char name[16]; 37652Sdf157793 37752Sdf157793 instance = ddi_get_instance(devi); /* find out which unit */ 37852Sdf157793 37952Sdf157793 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); 38052Sdf157793 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); 38152Sdf157793 38252Sdf157793 switch (cmd) { 38352Sdf157793 case DDI_DETACH: 38452Sdf157793 break; 38552Sdf157793 case DDI_SUSPEND: 38652Sdf157793 /* grab both mutex locks */ 38752Sdf157793 mutex_enter(asy->asy_excl); 38852Sdf157793 mutex_enter(asy->asy_excl_hi); 38952Sdf157793 if (asy->suspended) { 39052Sdf157793 mutex_exit(asy->asy_excl_hi); 39152Sdf157793 mutex_exit(asy->asy_excl); 39252Sdf157793 return (DDI_SUCCESS); 39352Sdf157793 } 39452Sdf157793 asy->suspended = B_TRUE; 3952119Sanovick 3962119Sanovick /* 3972119Sanovick * The quad UART ST16C554D, version D2 (made by EXAR) has an 3982119Sanovick * anomaly of generating spurious interrups when the ICR is 3992119Sanovick * loaded with zero. The workaround would be to read any of 4002119Sanovick * status registers/SPR/ICR before such write. This anomaly 4012119Sanovick * will be fixed in future versions of the chip. 4022119Sanovick */ 4032119Sanovick (void) INB(ICR); 4042119Sanovick 40552Sdf157793 /* Disable further interrupts */ 40652Sdf157793 OUTB(ICR, 0); 40752Sdf157793 mutex_exit(asy->asy_excl_hi); 40852Sdf157793 mutex_exit(asy->asy_excl); 40952Sdf157793 return (DDI_SUCCESS); 41052Sdf157793 41152Sdf157793 default: 41252Sdf157793 return (DDI_FAILURE); 41352Sdf157793 } 41452Sdf157793 41552Sdf157793 #ifdef DEBUG 41652Sdf157793 if (asydebug & ASY_DEBUG_INIT) 41752Sdf157793 cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance, 41852Sdf157793 asy->asy_hwtype == ASY82510 ? "82510" : 41952Sdf157793 asy->asy_hwtype == ASY16550AF ? "16550AF" : 42052Sdf157793 "8250"); 42152Sdf157793 #endif 42252Sdf157793 /* 42352Sdf157793 * Before removing interrupts it is always better to disable 42452Sdf157793 * interrupts if the chip gives a provision to disable the 42552Sdf157793 * serial port interrupts. 42652Sdf157793 */ 42752Sdf157793 mutex_enter(asy->asy_excl); 42852Sdf157793 mutex_enter(asy->asy_excl_hi); 4292119Sanovick /* disable interrupts, see EXAR bug */ 4302119Sanovick (void) INB(ICR); 4312119Sanovick OUTB(ICR, 0); 43252Sdf157793 mutex_exit(asy->asy_excl_hi); 43352Sdf157793 mutex_exit(asy->asy_excl); 43452Sdf157793 43552Sdf157793 /* remove minor device node(s) for this device */ 43652Sdf157793 (void) sprintf(name, "%c", (instance+'a')); /* serial-port */ 43752Sdf157793 ddi_remove_minor_node(devi, name); 43852Sdf157793 (void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */ 43952Sdf157793 ddi_remove_minor_node(devi, name); 44052Sdf157793 44152Sdf157793 mutex_destroy(asy->asy_excl); 44252Sdf157793 mutex_destroy(asy->asy_excl_hi); 44352Sdf157793 kmem_free(asy->asy_excl, sizeof (kmutex_t)); 44452Sdf157793 kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); 44552Sdf157793 cv_destroy(&async->async_flags_cv); 44652Sdf157793 kstat_delete(asy->sukstat); 44752Sdf157793 ddi_remove_intr(devi, 0, asy->asy_iblock); 44852Sdf157793 ddi_regs_map_free(&asy->asy_handle); 44952Sdf157793 ddi_remove_softintr(asy->asy_softintr_id); 45052Sdf157793 mutex_destroy(asy->asy_soft_lock); 45152Sdf157793 kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); 45252Sdf157793 ddi_soft_state_free(su_asycom, instance); 45352Sdf157793 ddi_soft_state_free(su_asyncline, instance); 45452Sdf157793 return (DDI_SUCCESS); 45552Sdf157793 } 45652Sdf157793 45752Sdf157793 static int 45852Sdf157793 asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 45952Sdf157793 { 46052Sdf157793 register int instance; 46152Sdf157793 struct asycom *asy; 46252Sdf157793 struct asyncline *async; 46352Sdf157793 char name[40]; 46452Sdf157793 ddi_device_acc_attr_t attr; 46552Sdf157793 enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR, 46652Sdf157793 SOFTINTR, ASYINIT, KSTAT, MINORNODE }; 46752Sdf157793 enum states state = EMPTY; 46852Sdf157793 46952Sdf157793 instance = ddi_get_instance(devi); /* find out which unit */ 47052Sdf157793 47152Sdf157793 /* cannot attach a device that has not been probed first */ 47252Sdf157793 if (instance > max_asy_instance) 47352Sdf157793 return (DDI_FAILURE); 47452Sdf157793 47552Sdf157793 if (cmd != DDI_RESUME) { 47652Sdf157793 /* Allocate soft state space */ 47752Sdf157793 if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) { 47852Sdf157793 cmn_err(CE_WARN, "su%d: cannot allocate soft state", 47952Sdf157793 instance); 48052Sdf157793 goto error; 48152Sdf157793 } 48252Sdf157793 } 48352Sdf157793 state = SOFTSTATE; 48452Sdf157793 48552Sdf157793 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); 48652Sdf157793 48752Sdf157793 if (asy == NULL) { 48852Sdf157793 cmn_err(CE_WARN, "su%d: cannot get soft state", instance); 48952Sdf157793 goto error; 49052Sdf157793 } 49152Sdf157793 49252Sdf157793 switch (cmd) { 49352Sdf157793 case DDI_ATTACH: 49452Sdf157793 break; 49552Sdf157793 case DDI_RESUME: { 49652Sdf157793 struct asyncline *async; 49752Sdf157793 49852Sdf157793 /* grab both mutex locks */ 49952Sdf157793 mutex_enter(asy->asy_excl); 50052Sdf157793 mutex_enter(asy->asy_excl_hi); 50152Sdf157793 if (!asy->suspended) { 50252Sdf157793 mutex_exit(asy->asy_excl_hi); 50352Sdf157793 mutex_exit(asy->asy_excl); 50452Sdf157793 return (DDI_SUCCESS); 50552Sdf157793 } 50652Sdf157793 /* re-setup all the registers and enable interrupts if needed */ 50752Sdf157793 async = (struct asyncline *)asy->asy_priv; 50852Sdf157793 if ((async) && (async->async_flags & ASYNC_ISOPEN)) 50952Sdf157793 (void) asy_program(asy, ASY_INIT); 51052Sdf157793 asy->suspended = B_FALSE; 51152Sdf157793 mutex_exit(asy->asy_excl_hi); 51252Sdf157793 mutex_exit(asy->asy_excl); 51352Sdf157793 return (DDI_SUCCESS); 51452Sdf157793 } 51552Sdf157793 default: 51652Sdf157793 goto error; 51752Sdf157793 } 51852Sdf157793 51952Sdf157793 attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; 52052Sdf157793 attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 52152Sdf157793 attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; 52252Sdf157793 52352Sdf157793 if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, 52452Sdf157793 (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN, 52552Sdf157793 &attr, &asy->asy_handle) != DDI_SUCCESS) { 52652Sdf157793 cmn_err(CE_WARN, "asyprobe regs map setup failed"); 52752Sdf157793 goto error; 52852Sdf157793 } 52952Sdf157793 state = REGSMAP; 53052Sdf157793 53152Sdf157793 #ifdef DEBUG 53252Sdf157793 if (asydebug) 53352Sdf157793 printf("su attach mapped %p\n", (void *)asy->asy_ioaddr); 53452Sdf157793 #endif 53552Sdf157793 53652Sdf157793 /* 53752Sdf157793 * Initialize the port with default settings. 53852Sdf157793 */ 53952Sdf157793 asy->asy_fifo_buf = 1; 54052Sdf157793 asy->asy_use_fifo = FIFO_OFF; 54152Sdf157793 54252Sdf157793 /* 54352Sdf157793 * Check for baudrate generator's "baud-divisor-factor" property setup 54452Sdf157793 * by OBP, since different UART chips might have different baudrate 54552Sdf157793 * generator divisor. e.g., in case of NSPG's Sputnik platform, the 54652Sdf157793 * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip 54752Sdf157793 * instead of SuperIO. Since the baud-divisor-factor must be a positive 54852Sdf157793 * integer, the divisors will always be at least as large as the values 54952Sdf157793 * in asyspdtab[]. Make the default factor 1. 55052Sdf157793 */ 55152Sdf157793 asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 55252Sdf157793 DDI_PROP_DONTPASS, "baud-divisor-factor", 1); 55352Sdf157793 55452Sdf157793 /* set speed cap */ 55552Sdf157793 asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi, 55652Sdf157793 DDI_PROP_DONTPASS, "serial-speed-cap", 115200); 55752Sdf157793 55852Sdf157793 /* check for ASY82510 chip */ 55952Sdf157793 OUTB(ISR, 0x20); 56052Sdf157793 if (INB(ISR) & 0x20) { /* 82510 chip is present */ 56152Sdf157793 /* 56252Sdf157793 * Since most of the general operation of the 82510 chip 56352Sdf157793 * can be done from BANK 0 (8250A/16450 compatable mode) 56452Sdf157793 * we will default to BANK 0. 56552Sdf157793 */ 56652Sdf157793 asy->asy_hwtype = ASY82510; 56752Sdf157793 OUTB(DAT+7, 0x04); /* clear status */ 56852Sdf157793 OUTB(ISR, 0x40); /* set to bank 2 */ 56952Sdf157793 OUTB(MCR, 0x08); /* IMD */ 57052Sdf157793 OUTB(DAT, 0x21); /* FMD */ 57152Sdf157793 OUTB(ISR, 0x00); /* set to bank 0 */ 57252Sdf157793 asy->asy_trig_level = 0; 57352Sdf157793 } else { /* Set the UART in FIFO mode if it has FIFO buffers */ 57452Sdf157793 asy->asy_hwtype = ASY16550AF; 57552Sdf157793 OUTB(FIFOR, 0x00); /* clear fifo register */ 57652Sdf157793 asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */ 57752Sdf157793 57852Sdf157793 /* set/Enable FIFO */ 57952Sdf157793 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH | 58052Sdf157793 (asy->asy_trig_level & 0xff)); 58152Sdf157793 58252Sdf157793 if ((INB(ISR) & 0xc0) == 0xc0) 58352Sdf157793 asy->asy_use_fifo = FIFO_ON; 58452Sdf157793 else { 58552Sdf157793 asy->asy_hwtype = ASY8250; 58652Sdf157793 OUTB(FIFOR, 0x00); /* NO FIFOs */ 58752Sdf157793 asy->asy_trig_level = 0; 58852Sdf157793 } 58952Sdf157793 } 59052Sdf157793 5912119Sanovick /* disable interrupts, see EXAR bug */ 5922119Sanovick (void) INB(ICR); 5932119Sanovick OUTB(ICR, 0); 59452Sdf157793 OUTB(LCR, DLAB); /* select baud rate generator */ 59552Sdf157793 /* Set the baud rate to 9600 */ 59652Sdf157793 OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff); 59752Sdf157793 OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff); 59852Sdf157793 OUTB(LCR, STOP1|BITS8); 59952Sdf157793 OUTB(MCR, (DTR | RTS| OUT2)); 60052Sdf157793 60152Sdf157793 /* 60252Sdf157793 * Set up the other components of the asycom structure for this port. 60352Sdf157793 */ 60452Sdf157793 asy->asy_excl = (kmutex_t *) 60552Sdf157793 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); 60652Sdf157793 asy->asy_excl_hi = (kmutex_t *) 60752Sdf157793 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); 60852Sdf157793 asy->asy_soft_lock = (kmutex_t *) 60952Sdf157793 kmem_zalloc(sizeof (kmutex_t), KM_SLEEP); 61052Sdf157793 asy->asy_unit = instance; 61152Sdf157793 asy->asy_dip = devi; 61252Sdf157793 61352Sdf157793 if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) { 61452Sdf157793 cmn_err(CE_NOTE, 61552Sdf157793 "Get iblock_cookie failed-Device interrupt%x\n", instance); 61652Sdf157793 goto error; 61752Sdf157793 } 61852Sdf157793 61952Sdf157793 if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH, 62052Sdf157793 &asy->asy_soft_iblock) != DDI_SUCCESS) { 62152Sdf157793 cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n", 62252Sdf157793 instance); 62352Sdf157793 goto error; 62452Sdf157793 } 62552Sdf157793 62652Sdf157793 mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER, 62752Sdf157793 (void *)asy->asy_soft_iblock); 62852Sdf157793 mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL); 62952Sdf157793 mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER, 63052Sdf157793 (void *)asy->asy_iblock); 63152Sdf157793 state = MUTEXES; 63252Sdf157793 63352Sdf157793 /* 63452Sdf157793 * Install interrupt handlers for this device. 63552Sdf157793 */ 63652Sdf157793 if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr, 63752Sdf157793 (caddr_t)asy) != DDI_SUCCESS) { 63852Sdf157793 cmn_err(CE_CONT, 63952Sdf157793 "Cannot set device interrupt for su driver\n"); 64052Sdf157793 goto error; 64152Sdf157793 } 64252Sdf157793 state = ADDINTR; 64352Sdf157793 64452Sdf157793 if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id), 64552Sdf157793 &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy) 64652Sdf157793 != DDI_SUCCESS) { 64752Sdf157793 cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n"); 64852Sdf157793 goto error; 64952Sdf157793 } 65052Sdf157793 state = SOFTINTR; 65152Sdf157793 65252Sdf157793 /* initialize the asyncline structure */ 65352Sdf157793 if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) { 65452Sdf157793 cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance); 65552Sdf157793 goto error; 65652Sdf157793 } 65752Sdf157793 state = ASYINIT; 65852Sdf157793 65952Sdf157793 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance); 66052Sdf157793 66152Sdf157793 mutex_enter(asy->asy_excl); 66252Sdf157793 async->async_common = asy; 66352Sdf157793 cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL); 66452Sdf157793 mutex_exit(asy->asy_excl); 66552Sdf157793 66652Sdf157793 if ((asy->sukstat = kstat_create("su", instance, "serialstat", 66752Sdf157793 "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) { 66852Sdf157793 asy->sukstat->ks_data = &asy->kstats; 66952Sdf157793 kstat_named_init(&asy->kstats.ringover, "ring buffer overflow", 67052Sdf157793 KSTAT_DATA_UINT64); 67152Sdf157793 kstat_named_init(&asy->kstats.siloover, "silo overflow", 67252Sdf157793 KSTAT_DATA_UINT64); 67352Sdf157793 kstat_install(asy->sukstat); 67452Sdf157793 } 67552Sdf157793 state = KSTAT; 67652Sdf157793 67752Sdf157793 if (strcmp(ddi_node_name(devi), "rsc-console") == 0) { 67852Sdf157793 /* 67952Sdf157793 * If the device is configured as the 'rsc-console' 68052Sdf157793 * create the minor device for this node. 68152Sdf157793 */ 68252Sdf157793 if (ddi_create_minor_node(devi, "ssp", S_IFCHR, 68352Sdf157793 asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) 68452Sdf157793 == DDI_FAILURE) { 68552Sdf157793 cmn_err(CE_WARN, 68652Sdf157793 "%s%d: Failed to create node rsc-console", 68752Sdf157793 ddi_get_name(devi), ddi_get_instance(devi)); 68852Sdf157793 goto error; 68952Sdf157793 } 69052Sdf157793 69152Sdf157793 asy->asy_lom_console = 0; 69252Sdf157793 asy->asy_rsc_console = 1; 69352Sdf157793 asy->asy_rsc_control = 0; 69452Sdf157793 asy->asy_device_type = ASY_SERIAL; 69552Sdf157793 asy->asy_flags |= ASY_IGNORE_CD; 69652Sdf157793 69752Sdf157793 } else if (strcmp(ddi_node_name(devi), "lom-console") == 0) { 69852Sdf157793 /* 69952Sdf157793 * If the device is configured as the 'lom-console' 70052Sdf157793 * create the minor device for this node. 70152Sdf157793 * Do not create a dialout device. 70252Sdf157793 * Use the same minor numbers as would be used for standard 70352Sdf157793 * serial instances. 70452Sdf157793 */ 70552Sdf157793 if (ddi_create_minor_node(devi, "lom-console", S_IFCHR, 70652Sdf157793 instance, DDI_NT_SERIAL_LOMCON, NULL) == DDI_FAILURE) { 70752Sdf157793 cmn_err(CE_WARN, 70852Sdf157793 "%s%d: Failed to create node lom-console", 70952Sdf157793 ddi_get_name(devi), ddi_get_instance(devi)); 71052Sdf157793 goto error; 71152Sdf157793 } 71252Sdf157793 asy->asy_lom_console = 1; 71352Sdf157793 asy->asy_rsc_console = 0; 71452Sdf157793 asy->asy_rsc_control = 0; 71552Sdf157793 asy->asy_device_type = ASY_SERIAL; 71652Sdf157793 asy->asy_flags |= ASY_IGNORE_CD; 71752Sdf157793 71852Sdf157793 } else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) { 71952Sdf157793 /* 72052Sdf157793 * If the device is configured as the 'rsc-control' 72152Sdf157793 * create the minor device for this node. 72252Sdf157793 */ 72352Sdf157793 if (ddi_create_minor_node(devi, "sspctl", S_IFCHR, 72452Sdf157793 asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, NULL) 72552Sdf157793 == DDI_FAILURE) { 72652Sdf157793 cmn_err(CE_WARN, "%s%d: Failed to create rsc-control", 72752Sdf157793 ddi_get_name(devi), ddi_get_instance(devi)); 72852Sdf157793 goto error; 72952Sdf157793 } 73052Sdf157793 73152Sdf157793 asy->asy_lom_console = 0; 73252Sdf157793 asy->asy_rsc_console = 0; 73352Sdf157793 asy->asy_rsc_control = 1; 73452Sdf157793 asy->asy_device_type = ASY_SERIAL; 73552Sdf157793 asy->asy_flags |= ASY_IGNORE_CD; 73652Sdf157793 73752Sdf157793 } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 73852Sdf157793 "keyboard", 0)) { 73952Sdf157793 /* 74052Sdf157793 * If the device is a keyboard, then create an internal 74152Sdf157793 * pathname so that the dacf code will link the node into 74252Sdf157793 * the keyboard console stream. See dacf.conf. 74352Sdf157793 */ 74452Sdf157793 if (ddi_create_internal_pathname(devi, "keyboard", 74552Sdf157793 S_IFCHR, instance) == DDI_FAILURE) { 74652Sdf157793 goto error; 74752Sdf157793 } 74852Sdf157793 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 74952Sdf157793 asy->asy_device_type = ASY_KEYBOARD; /* Device type */ 75052Sdf157793 } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 75152Sdf157793 "mouse", 0)) { 75252Sdf157793 /* 75352Sdf157793 * If the device is a mouse, then create an internal 75452Sdf157793 * pathname so that the dacf code will link the node into 75552Sdf157793 * the mouse stream. See dacf.conf. 75652Sdf157793 */ 75752Sdf157793 if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR, 75852Sdf157793 instance) == DDI_FAILURE) { 75952Sdf157793 goto error; 76052Sdf157793 } 76152Sdf157793 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 76252Sdf157793 asy->asy_device_type = ASY_MOUSE; 76352Sdf157793 } else { 76452Sdf157793 /* 76552Sdf157793 * If not used for keyboard/mouse, create minor devices nodes 76652Sdf157793 * for this device 76752Sdf157793 */ 76852Sdf157793 /* serial-port */ 76952Sdf157793 (void) sprintf(name, "%c", (instance+'a')); 77052Sdf157793 if (ddi_create_minor_node(devi, name, S_IFCHR, instance, 77152Sdf157793 DDI_NT_SERIAL_MB, NULL) == DDI_FAILURE) { 77252Sdf157793 goto error; 77352Sdf157793 } 77452Sdf157793 state = MINORNODE; 77552Sdf157793 /* serial-port:dailout */ 77652Sdf157793 (void) sprintf(name, "%c,cu", (instance+'a')); 77752Sdf157793 if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE, 77852Sdf157793 DDI_NT_SERIAL_MB_DO, NULL) == DDI_FAILURE) { 77952Sdf157793 goto error; 78052Sdf157793 } 78152Sdf157793 /* Property for ignoring DCD */ 78252Sdf157793 if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 78352Sdf157793 "ignore-cd", 0)) { 78452Sdf157793 asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 78552Sdf157793 } else { 78652Sdf157793 asy->asy_flags &= ~ASY_IGNORE_CD; 78752Sdf157793 /* 78852Sdf157793 * if ignore-cd is not available it could be 78952Sdf157793 * some old legacy platform, try to see 79052Sdf157793 * whether the old legacy property exists 79152Sdf157793 */ 79252Sdf157793 (void) sprintf(name, 79352Sdf157793 "port-%c-ignore-cd", (instance+ 'a')); 79452Sdf157793 if (ddi_getprop(DDI_DEV_T_ANY, devi, 79552Sdf157793 DDI_PROP_DONTPASS, name, 0)) 79652Sdf157793 asy->asy_flags |= ASY_IGNORE_CD; 79752Sdf157793 } 79852Sdf157793 asy->asy_device_type = ASY_SERIAL; 79952Sdf157793 } 80052Sdf157793 ddi_report_dev(devi); 80152Sdf157793 return (DDI_SUCCESS); 80252Sdf157793 80352Sdf157793 error: 80452Sdf157793 if (state == MINORNODE) { 80552Sdf157793 (void) sprintf(name, "%c", (instance+'a')); 80652Sdf157793 ddi_remove_minor_node(devi, name); 80752Sdf157793 } 80852Sdf157793 if (state >= KSTAT) 80952Sdf157793 kstat_delete(asy->sukstat); 81052Sdf157793 if (state >= ASYINIT) { 81152Sdf157793 cv_destroy(&async->async_flags_cv); 81252Sdf157793 ddi_soft_state_free(su_asyncline, instance); 81352Sdf157793 } 81452Sdf157793 if (state >= SOFTINTR) 81552Sdf157793 ddi_remove_softintr(asy->asy_softintr_id); 81652Sdf157793 if (state >= ADDINTR) 81752Sdf157793 ddi_remove_intr(devi, 0, asy->asy_iblock); 81852Sdf157793 if (state >= MUTEXES) { 81952Sdf157793 mutex_destroy(asy->asy_excl_hi); 82052Sdf157793 mutex_destroy(asy->asy_excl); 82152Sdf157793 mutex_destroy(asy->asy_soft_lock); 82252Sdf157793 kmem_free(asy->asy_excl_hi, sizeof (kmutex_t)); 82352Sdf157793 kmem_free(asy->asy_excl, sizeof (kmutex_t)); 82452Sdf157793 kmem_free(asy->asy_soft_lock, sizeof (kmutex_t)); 82552Sdf157793 } 82652Sdf157793 if (state >= REGSMAP) 82752Sdf157793 ddi_regs_map_free(&asy->asy_handle); 82852Sdf157793 if (state >= SOFTSTATE) 82952Sdf157793 ddi_soft_state_free(su_asycom, instance); 83052Sdf157793 /* no action for EMPTY state */ 83152Sdf157793 return (DDI_FAILURE); 83252Sdf157793 } 83352Sdf157793 83452Sdf157793 static int 83552Sdf157793 asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 83652Sdf157793 void **result) 83752Sdf157793 { 83852Sdf157793 _NOTE(ARGUNUSED(dip)) 83952Sdf157793 register dev_t dev = (dev_t)arg; 84052Sdf157793 register int instance, error; 84152Sdf157793 struct asycom *asy; 84252Sdf157793 84352Sdf157793 if ((instance = UNIT(dev)) > max_asy_instance) 84452Sdf157793 return (DDI_FAILURE); 84552Sdf157793 84652Sdf157793 switch (infocmd) { 84752Sdf157793 case DDI_INFO_DEVT2DEVINFO: 84852Sdf157793 asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance); 84952Sdf157793 if (asy->asy_dip == NULL) 85052Sdf157793 error = DDI_FAILURE; 85152Sdf157793 else { 85252Sdf157793 *result = (void *) asy->asy_dip; 85352Sdf157793 error = DDI_SUCCESS; 85452Sdf157793 } 85552Sdf157793 break; 85652Sdf157793 case DDI_INFO_DEVT2INSTANCE: 857796Smathue *result = (void *)(uintptr_t)instance; 85852Sdf157793 error = DDI_SUCCESS; 85952Sdf157793 break; 86052Sdf157793 default: 86152Sdf157793 error = DDI_FAILURE; 86252Sdf157793 } 86352Sdf157793 return (error); 86452Sdf157793 } 86552Sdf157793 86652Sdf157793 static int 86752Sdf157793 asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr) 86852Sdf157793 { 86952Sdf157793 _NOTE(ARGUNUSED(sflag)) 87052Sdf157793 struct asycom *asy; 87152Sdf157793 struct asyncline *async; 87252Sdf157793 int mcr; 87352Sdf157793 int unit; 87452Sdf157793 int len; 87552Sdf157793 struct termios *termiosp; 87652Sdf157793 87752Sdf157793 #ifdef DEBUG 87852Sdf157793 if (asydebug & ASY_DEBUG_CLOSE) 87952Sdf157793 printf("open\n"); 88052Sdf157793 #endif 88152Sdf157793 unit = UNIT(*dev); 88252Sdf157793 if (unit > max_asy_instance) 88352Sdf157793 return (ENXIO); /* unit not configured */ 88452Sdf157793 88552Sdf157793 async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit); 88652Sdf157793 if (async == NULL) 88752Sdf157793 return (ENXIO); 88852Sdf157793 88952Sdf157793 asy = async->async_common; 89052Sdf157793 if (asy == NULL) 89152Sdf157793 return (ENXIO); /* device not found by autoconfig */ 89252Sdf157793 89352Sdf157793 mutex_enter(asy->asy_excl); 89452Sdf157793 asy->asy_priv = (caddr_t)async; 89552Sdf157793 89652Sdf157793 again: 89752Sdf157793 mutex_enter(asy->asy_excl_hi); 89852Sdf157793 /* 89952Sdf157793 * Block waiting for carrier to come up, unless this is a no-delay open. 90052Sdf157793 */ 90152Sdf157793 if (!(async->async_flags & ASYNC_ISOPEN)) { 90252Sdf157793 /* 90352Sdf157793 * If this port is for a RSC console or control 90452Sdf157793 * use the following termio info 90552Sdf157793 */ 90652Sdf157793 if (asy->asy_rsc_console || asy->asy_rsc_control) { 90752Sdf157793 async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT | 90852Sdf157793 (B115200 & CBAUD); 90952Sdf157793 async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT) 91052Sdf157793 & CIBAUD); 91152Sdf157793 async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; 91252Sdf157793 } else if (asy->asy_lom_console) { 91352Sdf157793 async->async_ttycommon.t_cflag = B9600 & CBAUD; 91452Sdf157793 async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT) 91552Sdf157793 & CIBAUD); 91652Sdf157793 async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL; 91752Sdf157793 } else { 91852Sdf157793 91952Sdf157793 /* 92052Sdf157793 * Set the default termios settings (cflag). 92152Sdf157793 * Others are set in ldterm. Release the spin 92252Sdf157793 * mutex as we can block here, reaquire before 92352Sdf157793 * calling asy_program. 92452Sdf157793 */ 92552Sdf157793 mutex_exit(asy->asy_excl_hi); 92652Sdf157793 if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(), 92752Sdf157793 0, "ttymodes", (caddr_t)&termiosp, &len) 92852Sdf157793 == DDI_PROP_SUCCESS && 92952Sdf157793 len == sizeof (struct termios)) { 93052Sdf157793 async->async_ttycommon.t_cflag = 93152Sdf157793 termiosp->c_cflag; 93252Sdf157793 kmem_free(termiosp, len); 93352Sdf157793 } else { 93452Sdf157793 cmn_err(CE_WARN, 93552Sdf157793 "su: couldn't get ttymodes property!"); 93652Sdf157793 } 93752Sdf157793 mutex_enter(asy->asy_excl_hi); 93852Sdf157793 } 93952Sdf157793 async->async_ttycommon.t_iflag = 0; 94052Sdf157793 async->async_ttycommon.t_iocpending = NULL; 94152Sdf157793 async->async_ttycommon.t_size.ws_row = 0; 94252Sdf157793 async->async_ttycommon.t_size.ws_col = 0; 94352Sdf157793 async->async_ttycommon.t_size.ws_xpixel = 0; 94452Sdf157793 async->async_ttycommon.t_size.ws_ypixel = 0; 94552Sdf157793 async->async_dev = *dev; 94652Sdf157793 async->async_wbufcid = 0; 94752Sdf157793 94852Sdf157793 async->async_startc = CSTART; 94952Sdf157793 async->async_stopc = CSTOP; 95052Sdf157793 (void) asy_program(asy, ASY_INIT); 95152Sdf157793 } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) && 95252Sdf157793 secpolicy_excl_open(cr) != 0) { 95352Sdf157793 mutex_exit(asy->asy_excl_hi); 95452Sdf157793 mutex_exit(asy->asy_excl); 95552Sdf157793 return (EBUSY); 95652Sdf157793 } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) { 95752Sdf157793 mutex_exit(asy->asy_excl_hi); 95852Sdf157793 mutex_exit(asy->asy_excl); 95952Sdf157793 return (EBUSY); 96052Sdf157793 } 96152Sdf157793 96252Sdf157793 if (*dev & OUTLINE) 96352Sdf157793 async->async_flags |= ASYNC_OUT; 96452Sdf157793 96552Sdf157793 /* Raise DTR on every open */ 96652Sdf157793 mcr = INB(MCR); 96752Sdf157793 OUTB(MCR, mcr|DTR); 96852Sdf157793 96952Sdf157793 /* 97052Sdf157793 * Check carrier. 97152Sdf157793 */ 97252Sdf157793 if (asy->asy_flags & ASY_IGNORE_CD) 97352Sdf157793 async->async_ttycommon.t_flags |= TS_SOFTCAR; 97452Sdf157793 if ((async->async_ttycommon.t_flags & TS_SOFTCAR) || 97552Sdf157793 (INB(MSR) & DCD)) 97652Sdf157793 async->async_flags |= ASYNC_CARR_ON; 97752Sdf157793 else 97852Sdf157793 async->async_flags &= ~ASYNC_CARR_ON; 97952Sdf157793 mutex_exit(asy->asy_excl_hi); 98052Sdf157793 98152Sdf157793 /* 98252Sdf157793 * If FNDELAY and FNONBLOCK are clear, block until carrier up. 98352Sdf157793 * Quit on interrupt. 98452Sdf157793 */ 98552Sdf157793 if (!(flag & (FNDELAY|FNONBLOCK)) && 98652Sdf157793 !(async->async_ttycommon.t_cflag & CLOCAL)) { 98752Sdf157793 if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) || 98852Sdf157793 ((async->async_flags & ASYNC_OUT) && 98952Sdf157793 !(*dev & OUTLINE))) { 99052Sdf157793 async->async_flags |= ASYNC_WOPEN; 99152Sdf157793 if (cv_wait_sig(&async->async_flags_cv, 99252Sdf157793 asy->asy_excl) == 0) { 99352Sdf157793 async->async_flags &= ~ASYNC_WOPEN; 99452Sdf157793 mutex_exit(asy->asy_excl); 99552Sdf157793 return (EINTR); 99652Sdf157793 } 99752Sdf157793 async->async_flags &= ~ASYNC_WOPEN; 99852Sdf157793 goto again; 99952Sdf157793 } 100052Sdf157793 } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) { 100152Sdf157793 mutex_exit(asy->asy_excl); 100252Sdf157793 return (EBUSY); 100352Sdf157793 } 100452Sdf157793 100552Sdf157793 if (asy->suspended) { 100652Sdf157793 mutex_exit(asy->asy_excl); 100752Sdf157793 (void) ddi_dev_is_needed(asy->asy_dip, 0, 1); 100852Sdf157793 mutex_enter(asy->asy_excl); 100952Sdf157793 } 101052Sdf157793 101152Sdf157793 async->async_ttycommon.t_readq = rq; 101252Sdf157793 async->async_ttycommon.t_writeq = WR(rq); 101352Sdf157793 rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async; 101452Sdf157793 mutex_exit(asy->asy_excl); 101552Sdf157793 qprocson(rq); 101652Sdf157793 async->async_flags |= ASYNC_ISOPEN; 101752Sdf157793 async->async_polltid = 0; 101852Sdf157793 return (0); 101952Sdf157793 } 102052Sdf157793 102152Sdf157793 static void 102252Sdf157793 async_progress_check(void *arg) 102352Sdf157793 { 102452Sdf157793 struct asyncline *async = arg; 102552Sdf157793 struct asycom *asy = async->async_common; 102652Sdf157793 mblk_t *bp; 102752Sdf157793 102852Sdf157793 /* 102952Sdf157793 * We define "progress" as either waiting on a timed break or delay, or 103052Sdf157793 * having had at least one transmitter interrupt. If none of these are 103152Sdf157793 * true, then just terminate the output and wake up that close thread. 103252Sdf157793 */ 103352Sdf157793 mutex_enter(asy->asy_excl); 103452Sdf157793 mutex_enter(asy->asy_excl_hi); 103552Sdf157793 if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) { 103652Sdf157793 async->async_ocnt = 0; 103752Sdf157793 async->async_flags &= ~ASYNC_BUSY; 103852Sdf157793 async->async_timer = 0; 103952Sdf157793 bp = async->async_xmitblk; 104052Sdf157793 async->async_xmitblk = NULL; 104152Sdf157793 mutex_exit(asy->asy_excl_hi); 104252Sdf157793 if (bp != NULL) 104352Sdf157793 freeb(bp); 104452Sdf157793 /* 104552Sdf157793 * Since this timer is running, we know that we're in exit(2). 104652Sdf157793 * That means that the user can't possibly be waiting on any 104752Sdf157793 * valid ioctl(2) completion anymore, and we should just flush 104852Sdf157793 * everything. 104952Sdf157793 */ 105052Sdf157793 flushq(async->async_ttycommon.t_writeq, FLUSHALL); 105152Sdf157793 cv_broadcast(&async->async_flags_cv); 105252Sdf157793 } else { 105352Sdf157793 async->async_flags &= ~ASYNC_PROGRESS; 105452Sdf157793 async->async_timer = timeout(async_progress_check, async, 105552Sdf157793 drv_usectohz(su_drain_check)); 105652Sdf157793 mutex_exit(asy->asy_excl_hi); 105752Sdf157793 } 105852Sdf157793 mutex_exit(asy->asy_excl); 105952Sdf157793 } 106052Sdf157793 106152Sdf157793 /* 106252Sdf157793 * Close routine. 106352Sdf157793 */ 106452Sdf157793 static int 106552Sdf157793 asyclose(queue_t *q, int flag) 106652Sdf157793 { 106752Sdf157793 struct asyncline *async; 106852Sdf157793 struct asycom *asy; 106952Sdf157793 int icr, lcr; 107052Sdf157793 int nohupcl; 107152Sdf157793 107252Sdf157793 107352Sdf157793 #ifdef DEBUG 107452Sdf157793 if (asydebug & ASY_DEBUG_CLOSE) 107552Sdf157793 printf("close\n"); 107652Sdf157793 #endif 107752Sdf157793 async = q->q_ptr; 107852Sdf157793 ASSERT(async != NULL); 107952Sdf157793 asy = async->async_common; 108052Sdf157793 108152Sdf157793 /* get the nohupcl OBP property of this device */ 108252Sdf157793 nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS, 108352Sdf157793 "nohupcl", 0); 108452Sdf157793 108552Sdf157793 mutex_enter(asy->asy_excl); 108652Sdf157793 async->async_flags |= ASYNC_CLOSING; 108752Sdf157793 108852Sdf157793 /* 108952Sdf157793 * Turn off PPS handling early to avoid events occuring during 109052Sdf157793 * close. Also reset the DCD edge monitoring bit. 109152Sdf157793 */ 109252Sdf157793 mutex_enter(asy->asy_excl_hi); 109352Sdf157793 asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE); 109452Sdf157793 mutex_exit(asy->asy_excl_hi); 109552Sdf157793 109652Sdf157793 /* 109752Sdf157793 * There are two flavors of break -- timed (M_BREAK or TCSBRK) and 109852Sdf157793 * untimed (TIOCSBRK). For the timed case, these are enqueued on our 109952Sdf157793 * write queue and there's a timer running, so we don't have to worry 110052Sdf157793 * about them. For the untimed case, though, the user obviously made a 110152Sdf157793 * mistake, because these are handled immediately. We'll terminate the 110252Sdf157793 * break now and honor his implicit request by discarding the rest of 110352Sdf157793 * the data. 110452Sdf157793 */ 110552Sdf157793 if (!(async->async_flags & ASYNC_BREAK)) { 110652Sdf157793 mutex_enter(asy->asy_excl_hi); 110752Sdf157793 lcr = INB(LCR); 110852Sdf157793 if (lcr & SETBREAK) { 110952Sdf157793 OUTB(LCR, (lcr & ~SETBREAK)); 111052Sdf157793 } 111152Sdf157793 mutex_exit(asy->asy_excl_hi); 111252Sdf157793 if (lcr & SETBREAK) 111352Sdf157793 goto nodrain; 111452Sdf157793 } 111552Sdf157793 111652Sdf157793 /* 111752Sdf157793 * If the user told us not to delay the close ("non-blocking"), then 111852Sdf157793 * don't bother trying to drain. 111952Sdf157793 * 112052Sdf157793 * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever 112152Sdf157793 * getting an M_START (since these messages aren't enqueued), and the 112252Sdf157793 * only other way to clear the stop condition is by loss of DCD, which 112352Sdf157793 * would discard the queue data. Thus, we drop the output data if 112452Sdf157793 * ASYNC_STOPPED is set. 112552Sdf157793 */ 112652Sdf157793 if ((flag & (FNDELAY|FNONBLOCK)) || 112752Sdf157793 (async->async_flags & ASYNC_STOPPED)) { 112852Sdf157793 goto nodrain; 112952Sdf157793 } 113052Sdf157793 113152Sdf157793 /* 113252Sdf157793 * If there's any pending output, then we have to try to drain it. 113352Sdf157793 * There are two main cases to be handled: 113452Sdf157793 * - called by close(2): need to drain until done or until 113552Sdf157793 * a signal is received. No timeout. 113652Sdf157793 * - called by exit(2): need to drain while making progress 113752Sdf157793 * or until a timeout occurs. No signals. 113852Sdf157793 * 113952Sdf157793 * If we can't rely on receiving a signal to get us out of a hung 114052Sdf157793 * session, then we have to use a timer. In this case, we set a timer 114152Sdf157793 * to check for progress in sending the output data -- all that we ask 114252Sdf157793 * (at each interval) is that there's been some progress made. Since 114352Sdf157793 * the interrupt routine grabs buffers from the write queue, we can't 114452Sdf157793 * trust async_ocnt. Instead, we use a flag. 114552Sdf157793 * 114652Sdf157793 * Note that loss of carrier will cause the output queue to be flushed, 114752Sdf157793 * and we'll wake up again and finish normally. 114852Sdf157793 */ 114952Sdf157793 if (!ddi_can_receive_sig() && su_drain_check != 0) { 115052Sdf157793 async->async_flags &= ~ASYNC_PROGRESS; 115152Sdf157793 async->async_timer = timeout(async_progress_check, async, 115252Sdf157793 drv_usectohz(su_drain_check)); 115352Sdf157793 } 115452Sdf157793 115552Sdf157793 while (async->async_ocnt > 0 || 115652Sdf157793 async->async_ttycommon.t_writeq->q_first != NULL || 115752Sdf157793 (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) { 115852Sdf157793 if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0) 115952Sdf157793 break; 116052Sdf157793 } 116152Sdf157793 if (async->async_timer != 0) { 116252Sdf157793 (void) untimeout(async->async_timer); 116352Sdf157793 async->async_timer = 0; 116452Sdf157793 } 116552Sdf157793 116652Sdf157793 nodrain: 116752Sdf157793 mutex_enter(asy->asy_excl_hi); 116852Sdf157793 116952Sdf157793 /* turn off the loopback mode */ 117052Sdf157793 if ((async->async_dev != rconsdev) && 117152Sdf157793 (async->async_dev != kbddev) && 117252Sdf157793 (async->async_dev != stdindev)) { 117352Sdf157793 OUTB(MCR, INB(MCR) & ~ ASY_LOOP); 117452Sdf157793 } 117552Sdf157793 117652Sdf157793 async->async_ocnt = 0; 117752Sdf157793 if (async->async_xmitblk != NULL) 117852Sdf157793 freeb(async->async_xmitblk); 117952Sdf157793 async->async_xmitblk = NULL; 118052Sdf157793 118152Sdf157793 /* 118252Sdf157793 * If the "nohupcl" OBP property is set for this device, do 118352Sdf157793 * not turn off DTR and RTS no matter what. Otherwise, if the 118452Sdf157793 * line has HUPCL set or is incompletely opened, turn off DTR 118552Sdf157793 * and RTS to fix the modem line. 118652Sdf157793 */ 118752Sdf157793 if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) || 118852Sdf157793 (async->async_flags & ASYNC_WOPEN))) { 118952Sdf157793 /* turn off DTR, RTS but NOT interrupt to 386 */ 119052Sdf157793 OUTB(MCR, OUT2); 119152Sdf157793 mutex_exit(asy->asy_excl_hi); 119252Sdf157793 /* 119352Sdf157793 * Don't let an interrupt in the middle of close 119452Sdf157793 * bounce us back to the top; just continue closing 119552Sdf157793 * as if nothing had happened. 119652Sdf157793 */ 119752Sdf157793 if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0) 119852Sdf157793 goto out; 119952Sdf157793 mutex_enter(asy->asy_excl_hi); 120052Sdf157793 } 120152Sdf157793 120252Sdf157793 /* 120352Sdf157793 * If nobody's using it now, turn off receiver interrupts. 120452Sdf157793 */ 120552Sdf157793 if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) { 120652Sdf157793 icr = INB(ICR); 120752Sdf157793 OUTB(ICR, (icr & ~RIEN)); 120852Sdf157793 } 120952Sdf157793 mutex_exit(asy->asy_excl_hi); 121052Sdf157793 out: 121152Sdf157793 /* 121252Sdf157793 * Clear out device state. 121352Sdf157793 */ 121452Sdf157793 async->async_flags = 0; 121552Sdf157793 ttycommon_close(&async->async_ttycommon); 121652Sdf157793 cv_broadcast(&async->async_flags_cv); 121752Sdf157793 121852Sdf157793 /* 121952Sdf157793 * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in 122052Sdf157793 * async_softint or an interrupt was pending when the process 122152Sdf157793 * using the port exited. 122252Sdf157793 */ 122352Sdf157793 asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT; 122452Sdf157793 122552Sdf157793 /* 122652Sdf157793 * Cancel outstanding "bufcall" request. 122752Sdf157793 */ 122852Sdf157793 if (async->async_wbufcid) { 122952Sdf157793 unbufcall(async->async_wbufcid); 123052Sdf157793 async->async_wbufcid = 0; 123152Sdf157793 } 123252Sdf157793 123352Sdf157793 /* 123452Sdf157793 * If inperim is true, it means the port is closing while there's 123552Sdf157793 * a pending software interrupt. async_flags has been zeroed out, 123652Sdf157793 * so this instance of leaveq() needs to be called before we call 123752Sdf157793 * qprocsoff() to disable services on the q. If inperim is false, 123852Sdf157793 * leaveq() has already been called or we're not in a perimeter. 123952Sdf157793 */ 124052Sdf157793 if (asy->inperim == B_TRUE) { 124152Sdf157793 asy->inperim = B_FALSE; 124252Sdf157793 mutex_exit(asy->asy_excl); 124352Sdf157793 leaveq(q); 124452Sdf157793 } else { 124552Sdf157793 mutex_exit(asy->asy_excl); 124652Sdf157793 } 124752Sdf157793 124852Sdf157793 /* Note that qprocsoff can't be done until after interrupts are off */ 124952Sdf157793 qprocsoff(q); 125052Sdf157793 q->q_ptr = WR(q)->q_ptr = NULL; 125152Sdf157793 async->async_ttycommon.t_readq = NULL; 125252Sdf157793 async->async_ttycommon.t_writeq = NULL; 125352Sdf157793 125452Sdf157793 return (0); 125552Sdf157793 } 125652Sdf157793 125752Sdf157793 /* 125852Sdf157793 * Checks to see if the serial port is still transmitting 125952Sdf157793 * characters. It returns true when there are characters 126052Sdf157793 * queued to transmit, when the holding register contains 126152Sdf157793 * a byte, or when the shifting register still contains 126252Sdf157793 * data to send. 126352Sdf157793 * 126452Sdf157793 */ 126552Sdf157793 static boolean_t 126652Sdf157793 asy_isbusy(struct asycom *asy) 126752Sdf157793 { 126852Sdf157793 struct asyncline *async; 126952Sdf157793 127052Sdf157793 #ifdef DEBUG 127152Sdf157793 if (asydebug & ASY_DEBUG_EOT) 127252Sdf157793 printf("isbusy\n"); 127352Sdf157793 #endif 127452Sdf157793 async = (struct asyncline *)asy->asy_priv; 127552Sdf157793 ASSERT(mutex_owned(asy->asy_excl)); 127652Sdf157793 ASSERT(mutex_owned(asy->asy_excl_hi)); 127752Sdf157793 return ((async->async_ocnt > 0) || 127852Sdf157793 ((INB(LSR) & XSRE) == 0)); 127952Sdf157793 } 128052Sdf157793 128152Sdf157793 /* 128252Sdf157793 * Program the ASY port. Most of the async operation is based on the values 128352Sdf157793 * of 'c_iflag' and 'c_cflag'. 128452Sdf157793 */ 128552Sdf157793 static int 128652Sdf157793 asy_program(struct asycom *asy, int mode) 128752Sdf157793 { 128852Sdf157793 struct asyncline *async; 128952Sdf157793 int baudrate, c_flag; 129052Sdf157793 int icr, lcr; 129152Sdf157793 int ocflags; 129252Sdf157793 int error = 0; 129352Sdf157793 129452Sdf157793 ASSERT(mutex_owned(asy->asy_excl)); 129552Sdf157793 ASSERT(mutex_owned(asy->asy_excl_hi)); 129652Sdf157793 129752Sdf157793 #ifdef DEBUG 129852Sdf157793 if (asydebug & ASY_DEBUG_PROCS) 129952Sdf157793 printf("program\n"); 130052Sdf157793 #endif 130152Sdf157793 async = (struct asyncline *)asy->asy_priv; 130252Sdf157793 130352Sdf157793 baudrate = async->async_ttycommon.t_cflag & CBAUD; 130452Sdf157793 if (async->async_ttycommon.t_cflag & CBAUDEXT) 130552Sdf157793 baudrate += 16; 130652Sdf157793 130752Sdf157793 /* Limit baudrate so it can't index out of baudtable */ 130852Sdf157793 if (baudrate >= N_SU_SPEEDS) baudrate = B9600; 130952Sdf157793 131052Sdf157793 /* 131152Sdf157793 * If baud rate requested is greater than the speed cap 131252Sdf157793 * or is an unsupported baud rate then reset t_cflag baud 131352Sdf157793 * to the last valid baud rate. If this is the initial 131452Sdf157793 * pass through asy_program then set it to 9600. 131552Sdf157793 */ 131652Sdf157793 if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) || 131752Sdf157793 (baudtable[baudrate] > asy->asy_speed_cap)) { 131852Sdf157793 async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT & 131952Sdf157793 ~CIBAUD & ~CIBAUDEXT; 132052Sdf157793 if (mode == ASY_INIT) { 132152Sdf157793 async->async_ttycommon.t_cflag |= B9600; 1322*2422Skc28005 async->async_ttycommon.t_cflag |= B9600 << IBSHIFT; 132352Sdf157793 baudrate = B9600; 132452Sdf157793 } else { 132552Sdf157793 async->async_ttycommon.t_cflag |= 132652Sdf157793 (asy->asy_ocflags & (CBAUD | CBAUDEXT | 132752Sdf157793 CIBAUD | CIBAUDEXT)); 1328*2422Skc28005 error = EINVAL; 1329*2422Skc28005 goto end; 133052Sdf157793 } 133152Sdf157793 } 133252Sdf157793 1333*2422Skc28005 /* 1334*2422Skc28005 * If CIBAUD and CIBAUDEXT are zero then we should set them to 1335*2422Skc28005 * the equivelant output baud bits. Else, if CIBAUD and CIBAUDEXT 1336*2422Skc28005 * don't match CBAUD and CBAUDEXT respectively then we should 1337*2422Skc28005 * notify the requestor that we do not support split speeds. 1338*2422Skc28005 */ 1339*2422Skc28005 if ((async->async_ttycommon.t_cflag & (CIBAUD|CIBAUDEXT)) == 0) { 1340*2422Skc28005 async->async_ttycommon.t_cflag |= 1341*2422Skc28005 (async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT; 1342*2422Skc28005 if (async->async_ttycommon.t_cflag & CBAUDEXT) 1343*2422Skc28005 async->async_ttycommon.t_cflag |= CIBAUDEXT; 1344*2422Skc28005 } else { 1345*2422Skc28005 if ((((async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT) != 1346*2422Skc28005 (async->async_ttycommon.t_cflag & CIBAUD)) || 1347*2422Skc28005 !(((async->async_ttycommon.t_cflag & (CBAUDEXT | 1348*2422Skc28005 CIBAUDEXT)) == (CBAUDEXT | CIBAUDEXT)) || 1349*2422Skc28005 ((async->async_ttycommon.t_cflag & (CBAUDEXT | 1350*2422Skc28005 CIBAUDEXT)) == 0))) { 1351*2422Skc28005 async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT & 1352*2422Skc28005 ~CIBAUD & ~CIBAUDEXT; 135352Sdf157793 async->async_ttycommon.t_cflag |= 1354*2422Skc28005 (asy->asy_ocflags & (CBAUD | CBAUDEXT | 1355*2422Skc28005 CIBAUD | CIBAUDEXT)); 1356*2422Skc28005 error = EINVAL; 1357*2422Skc28005 goto end; 135852Sdf157793 } 135952Sdf157793 } 136052Sdf157793 136152Sdf157793 c_flag = async->async_ttycommon.t_cflag & 136252Sdf157793 (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD | 136352Sdf157793 CBAUDEXT | CIBAUD | CIBAUDEXT); 13642119Sanovick 13652119Sanovick /* disable interrupts, see EXAR bug */ 13662119Sanovick (void) INB(ICR); 13672119Sanovick OUTB(ICR, 0); 136852Sdf157793 136952Sdf157793 ocflags = asy->asy_ocflags; 137052Sdf157793 137152Sdf157793 /* flush/reset the status registers */ 137252Sdf157793 if (mode == ASY_INIT) { 137352Sdf157793 (void) INB(DAT); 137452Sdf157793 (void) INB(ISR); 137552Sdf157793 (void) INB(LSR); 137652Sdf157793 (void) INB(MSR); 137752Sdf157793 } 137852Sdf157793 137952Sdf157793 if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) { 138052Sdf157793 /* Set line control */ 138152Sdf157793 lcr = INB(LCR); 138252Sdf157793 lcr &= ~(WLS0|WLS1|STB|PEN|EPS); 138352Sdf157793 138452Sdf157793 if (c_flag & CSTOPB) 138552Sdf157793 lcr |= STB; /* 2 stop bits */ 138652Sdf157793 138752Sdf157793 if (c_flag & PARENB) 138852Sdf157793 lcr |= PEN; 138952Sdf157793 139052Sdf157793 if ((c_flag & PARODD) == 0) 139152Sdf157793 lcr |= EPS; 139252Sdf157793 139352Sdf157793 switch (c_flag & CSIZE) { 139452Sdf157793 case CS5: 139552Sdf157793 lcr |= BITS5; 139652Sdf157793 break; 139752Sdf157793 case CS6: 139852Sdf157793 lcr |= BITS6; 139952Sdf157793 break; 140052Sdf157793 case CS7: 140152Sdf157793 lcr |= BITS7; 140252Sdf157793 break; 140352Sdf157793 case CS8: 140452Sdf157793 lcr |= BITS8; 140552Sdf157793 break; 140652Sdf157793 } 140752Sdf157793 140852Sdf157793 /* set the baud rate when the rate is NOT B0 */ 140952Sdf157793 if (baudrate != 0) { 141052Sdf157793 OUTB(LCR, DLAB); 141152Sdf157793 OUTB(DAT, (asyspdtab[baudrate] * 141252Sdf157793 asy->asy_baud_divisor_factor) & 0xff); 141352Sdf157793 OUTB(ICR, ((asyspdtab[baudrate] * 141452Sdf157793 asy->asy_baud_divisor_factor) >> 8) & 0xff); 141552Sdf157793 } 141652Sdf157793 /* set the line control modes */ 141752Sdf157793 OUTB(LCR, lcr); 141852Sdf157793 141952Sdf157793 /* 142052Sdf157793 * if transitioning from CREAD off to CREAD on, 142152Sdf157793 * flush the FIFO buffer if we have one. 142252Sdf157793 */ 142352Sdf157793 if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) { 142452Sdf157793 if (asy->asy_use_fifo == FIFO_ON) { 142552Sdf157793 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | 142652Sdf157793 (asy->asy_trig_level & 0xff)); 142752Sdf157793 } 142852Sdf157793 } 142952Sdf157793 143052Sdf157793 /* remember the new cflags */ 143152Sdf157793 asy->asy_ocflags = c_flag & ~CLOCAL; 143252Sdf157793 } 143352Sdf157793 143452Sdf157793 /* whether or not CLOCAL is set, modify the modem control lines */ 143552Sdf157793 if (baudrate == 0) 143652Sdf157793 /* B0 has been issued, lower DTR */ 143752Sdf157793 OUTB(MCR, RTS|OUT2); 143852Sdf157793 else 143952Sdf157793 /* raise DTR */ 144052Sdf157793 OUTB(MCR, DTR|RTS|OUT2); 144152Sdf157793 144252Sdf157793 /* 144352Sdf157793 * Call the modem status interrupt handler to check for the carrier 144452Sdf157793 * in case CLOCAL was turned off after the carrier came on. 144552Sdf157793 * (Note: Modem status interrupt is not enabled if CLOCAL is ON.) 144652Sdf157793 */ 144752Sdf157793 async_msint(asy); 144852Sdf157793 144952Sdf157793 /* Set interrupt control */ 145052Sdf157793 if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS)) 145152Sdf157793 /* 145252Sdf157793 * direct-wired line ignores DCD, so we don't enable modem 145352Sdf157793 * status interrupts. 145452Sdf157793 */ 145552Sdf157793 icr = (TIEN | SIEN); 145652Sdf157793 else 145752Sdf157793 icr = (TIEN | SIEN | MIEN); 145852Sdf157793 145952Sdf157793 if (c_flag & CREAD) 146052Sdf157793 icr |= RIEN; 146152Sdf157793 146252Sdf157793 OUTB(ICR, icr); 146352Sdf157793 end: 146452Sdf157793 return (error); 146552Sdf157793 } 146652Sdf157793 146752Sdf157793 /* 146852Sdf157793 * asyintr() is the High Level Interrupt Handler. 146952Sdf157793 * 147052Sdf157793 * There are four different interrupt types indexed by ISR register values: 147152Sdf157793 * 0: modem 147252Sdf157793 * 1: Tx holding register is empty, ready for next char 147352Sdf157793 * 2: Rx register now holds a char to be picked up 147452Sdf157793 * 3: error or break on line 147552Sdf157793 * This routine checks the Bit 0 (interrupt-not-pending) to determine if 147652Sdf157793 * the interrupt is from this port. 147752Sdf157793 */ 147852Sdf157793 uint_t 147952Sdf157793 asyintr(caddr_t argasy) 148052Sdf157793 { 148152Sdf157793 struct asycom *asy = (struct asycom *)argasy; 148252Sdf157793 struct asyncline *async; 148352Sdf157793 int ret_status = DDI_INTR_UNCLAIMED; 148452Sdf157793 uchar_t interrupt_id, lsr; 148552Sdf157793 148652Sdf157793 interrupt_id = INB(ISR) & 0x0F; 148752Sdf157793 async = (struct asyncline *)asy->asy_priv; 148852Sdf157793 if ((async == NULL) || 148952Sdf157793 !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) { 149052Sdf157793 if (interrupt_id & NOINTERRUPT) { 149152Sdf157793 return (DDI_INTR_UNCLAIMED); 149252Sdf157793 } else { 149352Sdf157793 lsr = INB(LSR); 149452Sdf157793 if ((lsr & BRKDET) && 149552Sdf157793 ((abort_enable == KIOCABORTENABLE) && 149652Sdf157793 (async->async_dev == rconsdev))) 149752Sdf157793 abort_sequence_enter((char *)NULL); 149852Sdf157793 else { 149952Sdf157793 /* reset line status */ 150052Sdf157793 (void) INB(LSR); 150152Sdf157793 /* discard any data */ 150252Sdf157793 (void) INB(DAT); 150352Sdf157793 /* reset modem status */ 150452Sdf157793 (void) INB(MSR); 150552Sdf157793 return (DDI_INTR_CLAIMED); 150652Sdf157793 } 150752Sdf157793 } 150852Sdf157793 } 150952Sdf157793 /* 151052Sdf157793 * Spurious interrupts happen in this driver 151152Sdf157793 * because of the transmission on serial port not handled 151252Sdf157793 * properly. 151352Sdf157793 * 151452Sdf157793 * The reasons for Spurious interrupts are: 151552Sdf157793 * 1. There is a path in async_nstart which transmits 151652Sdf157793 * characters without going through interrupt services routine 151752Sdf157793 * which causes spurious interrupts to happen. 151852Sdf157793 * 2. In the async_txint more than one character is sent 151952Sdf157793 * in one interrupt service. 152052Sdf157793 * 3. In async_rxint more than one characters are received in 152152Sdf157793 * in one interrupt service. 152252Sdf157793 * 152352Sdf157793 * Hence we have flags to indicate that such scenerio has happened. 152452Sdf157793 * and claim only such interrupts and others we donot claim it 152552Sdf157793 * as it could be a indicator of some hardware problem. 152652Sdf157793 * 152752Sdf157793 */ 152852Sdf157793 if (interrupt_id & NOINTERRUPT) { 152952Sdf157793 mutex_enter(asy->asy_excl_hi); 153052Sdf157793 if ((asy->asy_xmit_count > 1) || 153152Sdf157793 (asy->asy_out_of_band_xmit > 0) || 153252Sdf157793 (asy->asy_rx_count > 1)) { 153352Sdf157793 asy->asy_xmit_count = 0; 153452Sdf157793 asy->asy_out_of_band_xmit = 0; 153552Sdf157793 asy->asy_rx_count = 0; 153652Sdf157793 mutex_exit(asy->asy_excl_hi); 153752Sdf157793 return (DDI_INTR_CLAIMED); 153852Sdf157793 } else { 153952Sdf157793 mutex_exit(asy->asy_excl_hi); 154052Sdf157793 return (DDI_INTR_UNCLAIMED); 154152Sdf157793 } 154252Sdf157793 } 154352Sdf157793 ret_status = DDI_INTR_CLAIMED; 154452Sdf157793 mutex_enter(asy->asy_excl_hi); 154552Sdf157793 if (asy->asy_hwtype == ASY82510) 154652Sdf157793 OUTB(ISR, 0x00); /* set bank 0 */ 154752Sdf157793 154852Sdf157793 #ifdef DEBUG 154952Sdf157793 if (asydebug & ASY_DEBUG_INTR) 155052Sdf157793 prom_printf("l"); 155152Sdf157793 #endif 155252Sdf157793 lsr = INB(LSR); 155352Sdf157793 switch (interrupt_id) { 155452Sdf157793 case RxRDY: 155552Sdf157793 case RSTATUS: 155652Sdf157793 case FFTMOUT: 155752Sdf157793 /* receiver interrupt or receiver errors */ 155852Sdf157793 async_rxint(asy, lsr); 155952Sdf157793 break; 156052Sdf157793 case TxRDY: 156152Sdf157793 /* transmit interrupt */ 156252Sdf157793 async_txint(asy, lsr); 156352Sdf157793 break; 156452Sdf157793 case MSTATUS: 156552Sdf157793 /* modem status interrupt */ 156652Sdf157793 async_msint(asy); 156752Sdf157793 break; 156852Sdf157793 } 156952Sdf157793 mutex_exit(asy->asy_excl_hi); 157052Sdf157793 return (ret_status); 157152Sdf157793 } 157252Sdf157793 157352Sdf157793 /* 157452Sdf157793 * Transmitter interrupt service routine. 157552Sdf157793 * If there is more data to transmit in the current pseudo-DMA block, 157652Sdf157793 * send the next character if output is not stopped or draining. 157752Sdf157793 * Otherwise, queue up a soft interrupt. 157852Sdf157793 * 157952Sdf157793 * XXX - Needs review for HW FIFOs. 158052Sdf157793 */ 158152Sdf157793 static void 158252Sdf157793 async_txint(struct asycom *asy, uchar_t lsr) 158352Sdf157793 { 158452Sdf157793 struct asyncline *async = (struct asyncline *)asy->asy_priv; 158552Sdf157793 int fifo_len; 158652Sdf157793 int xmit_progress; 158752Sdf157793 158852Sdf157793 asycheckflowcontrol_hw(asy); 158952Sdf157793 159052Sdf157793 /* 159152Sdf157793 * If ASYNC_BREAK has been set, return to asyintr()'s context to 159252Sdf157793 * claim the interrupt without performing any action. 159352Sdf157793 */ 159452Sdf157793 if (async->async_flags & ASYNC_BREAK) 159552Sdf157793 return; 159652Sdf157793 159752Sdf157793 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 159852Sdf157793 159952Sdf157793 /* 160052Sdf157793 * Check for flow control and do the needed action. 160152Sdf157793 */ 160252Sdf157793 if (asycheckflowcontrol_sw(asy)) { 160352Sdf157793 return; 160452Sdf157793 } 160552Sdf157793 160652Sdf157793 if (async->async_ocnt > 0 && 160752Sdf157793 !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) { 160852Sdf157793 xmit_progress = 0; 160952Sdf157793 while (fifo_len > 0 && async->async_ocnt > 0) { 161052Sdf157793 if (lsr & XHRE) { 161152Sdf157793 OUTB(DAT, *async->async_optr++); 161252Sdf157793 fifo_len--; 161352Sdf157793 async->async_ocnt--; 161452Sdf157793 xmit_progress++; 161552Sdf157793 } 161652Sdf157793 /* 161752Sdf157793 * Reading the lsr, (moved reading at the end of 161852Sdf157793 * while loop) as already we have read once at 161952Sdf157793 * the beginning of interrupt service 162052Sdf157793 */ 162152Sdf157793 lsr = INB(LSR); 162252Sdf157793 } 162352Sdf157793 asy->asy_xmit_count = xmit_progress; 162452Sdf157793 if (xmit_progress > 0) 162552Sdf157793 async->async_flags |= ASYNC_PROGRESS; 162652Sdf157793 } 162752Sdf157793 162852Sdf157793 if (fifo_len == 0) { 162952Sdf157793 return; 163052Sdf157793 } 163152Sdf157793 163252Sdf157793 163352Sdf157793 ASYSETSOFT(asy); 163452Sdf157793 } 163552Sdf157793 163652Sdf157793 /* 163752Sdf157793 * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive 163852Sdf157793 * error interrupt. 163952Sdf157793 * Try to put the character into the circular buffer for this line; if it 164052Sdf157793 * overflows, indicate a circular buffer overrun. If this port is always 164152Sdf157793 * to be serviced immediately, or the character is a STOP character, or 164252Sdf157793 * more than 15 characters have arrived, queue up a soft interrupt to 164352Sdf157793 * drain the circular buffer. 164452Sdf157793 * XXX - needs review for hw FIFOs support. 164552Sdf157793 */ 164652Sdf157793 164752Sdf157793 static void 164852Sdf157793 async_rxint(struct asycom *asy, uchar_t lsr) 164952Sdf157793 { 165052Sdf157793 struct asyncline *async = (struct asyncline *)asy->asy_priv; 165152Sdf157793 uchar_t c = 0; 165252Sdf157793 uint_t s = 0, needsoft = 0; 165352Sdf157793 register tty_common_t *tp; 165452Sdf157793 165552Sdf157793 tp = &async->async_ttycommon; 165652Sdf157793 if (!(tp->t_cflag & CREAD)) { 165752Sdf157793 if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 165852Sdf157793 (void) (INB(DAT) & 0xff); 165952Sdf157793 } 166052Sdf157793 return; /* line is not open for read? */ 166152Sdf157793 } 166252Sdf157793 asy->asy_rx_count = 0; 166352Sdf157793 while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 166452Sdf157793 c = 0; 166552Sdf157793 s = 0; 166652Sdf157793 asy->asy_rx_count++; 166752Sdf157793 if (lsr & RCA) { 166852Sdf157793 c = INB(DAT) & 0xff; 166952Sdf157793 /* 167052Sdf157793 * Even a single character is received 167152Sdf157793 * we need Soft interrupt to pass it to 167252Sdf157793 * higher layers. 167352Sdf157793 */ 167452Sdf157793 needsoft = 1; 167552Sdf157793 } 167652Sdf157793 167752Sdf157793 /* Check for character break sequence */ 167852Sdf157793 if ((abort_enable == KIOCABORTALTERNATE) && 167952Sdf157793 (async->async_dev == rconsdev)) { 168052Sdf157793 if (abort_charseq_recognize(c)) 168152Sdf157793 abort_sequence_enter((char *)NULL); 168252Sdf157793 } 168352Sdf157793 168452Sdf157793 /* Handle framing errors */ 168552Sdf157793 if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) { 168652Sdf157793 if (lsr & PARERR) { 168752Sdf157793 if (tp->t_iflag & INPCK) /* parity enabled */ 168852Sdf157793 s |= PERROR; 168952Sdf157793 } 169052Sdf157793 if (lsr & (FRMERR|BRKDET)) 169152Sdf157793 s |= FRERROR; 169252Sdf157793 if (lsr & OVRRUN) { 169352Sdf157793 async->async_hw_overrun = 1; 169452Sdf157793 s |= OVERRUN; 169552Sdf157793 } 169652Sdf157793 } 169752Sdf157793 169852Sdf157793 if (s == 0) 169952Sdf157793 if ((tp->t_iflag & PARMRK) && 170052Sdf157793 !(tp->t_iflag & (IGNPAR|ISTRIP)) && 170152Sdf157793 (c == 0377)) 170252Sdf157793 if (RING_POK(async, 2)) { 170352Sdf157793 RING_PUT(async, 0377); 170452Sdf157793 RING_PUT(async, c); 170552Sdf157793 } else 170652Sdf157793 async->async_sw_overrun = 1; 170752Sdf157793 else 170852Sdf157793 if (RING_POK(async, 1)) 170952Sdf157793 RING_PUT(async, c); 171052Sdf157793 else 171152Sdf157793 async->async_sw_overrun = 1; 171252Sdf157793 else 171352Sdf157793 if (s & FRERROR) { /* Handle framing errors */ 171452Sdf157793 if (c == 0) { 171552Sdf157793 /* Look for break on kbd, stdin, or rconsdev */ 171652Sdf157793 if ((async->async_dev == kbddev) || 171752Sdf157793 ((async->async_dev == rconsdev) || 171852Sdf157793 (async->async_dev == stdindev)) && 171952Sdf157793 (abort_enable != 172052Sdf157793 KIOCABORTALTERNATE)) 172152Sdf157793 abort_sequence_enter((char *)0); 172252Sdf157793 else 172352Sdf157793 async->async_break++; 172452Sdf157793 } else { 172552Sdf157793 if (RING_POK(async, 1)) 172652Sdf157793 RING_MARK(async, c, s); 172752Sdf157793 else 172852Sdf157793 async->async_sw_overrun = 1; 172952Sdf157793 } 173052Sdf157793 } else { /* Parity errors handled by ldterm */ 173152Sdf157793 if (RING_POK(async, 1)) 173252Sdf157793 RING_MARK(async, c, s); 173352Sdf157793 else 173452Sdf157793 async->async_sw_overrun = 1; 173552Sdf157793 } 173652Sdf157793 lsr = INB(LSR); 173752Sdf157793 if (asy->asy_rx_count > 16) break; 173852Sdf157793 } 173952Sdf157793 /* Check whether there is a request for hw/sw inbound/input flow ctrl */ 174052Sdf157793 if ((async->async_ttycommon.t_cflag & CRTSXOFF) || 174152Sdf157793 (async->async_ttycommon.t_iflag & IXOFF)) 174252Sdf157793 if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) { 174352Sdf157793 #ifdef DEBUG 174452Sdf157793 if (asydebug & ASY_DEBUG_HFLOW) 174552Sdf157793 printf("asy%d: hardware flow stop input.\n", 174652Sdf157793 UNIT(async->async_dev)); 174752Sdf157793 #endif 174852Sdf157793 async->async_flags |= ASYNC_HW_IN_FLOW; 174952Sdf157793 async->async_flowc = async->async_stopc; 175052Sdf157793 async->async_ringbuf_overflow = 1; 175152Sdf157793 } 175252Sdf157793 175352Sdf157793 if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || 175452Sdf157793 (RING_FRAC(async)) || (async->async_polltid == 0)) 175552Sdf157793 ASYSETSOFT(asy); /* need a soft interrupt */ 175652Sdf157793 } 175752Sdf157793 175852Sdf157793 /* 175952Sdf157793 * Interrupt on port: handle PPS event. This function is only called 176052Sdf157793 * for a port on which PPS event handling has been enabled. 176152Sdf157793 */ 176252Sdf157793 static void 176352Sdf157793 asy_ppsevent(struct asycom *asy, int msr) 176452Sdf157793 { 176552Sdf157793 if (asy->asy_flags & ASY_PPS_EDGE) { 176652Sdf157793 /* Have seen leading edge, now look for and record drop */ 176752Sdf157793 if ((msr & DCD) == 0) 176852Sdf157793 asy->asy_flags &= ~ASY_PPS_EDGE; 176952Sdf157793 /* 177052Sdf157793 * Waiting for leading edge, look for rise; stamp event and 177152Sdf157793 * calibrate kernel clock. 177252Sdf157793 */ 177352Sdf157793 } else if (msr & DCD) { 177452Sdf157793 /* 177552Sdf157793 * This code captures a timestamp at the designated 177652Sdf157793 * transition of the PPS signal (DCD asserted). The 177752Sdf157793 * code provides a pointer to the timestamp, as well 177852Sdf157793 * as the hardware counter value at the capture. 177952Sdf157793 * 178052Sdf157793 * Note: the kernel has nano based time values while 178152Sdf157793 * NTP requires micro based, an in-line fast algorithm 178252Sdf157793 * to convert nsec to usec is used here -- see hrt2ts() 178352Sdf157793 * in common/os/timers.c for a full description. 178452Sdf157793 */ 178552Sdf157793 struct timeval *tvp = &asy_ppsev.tv; 178652Sdf157793 timestruc_t ts; 178752Sdf157793 long nsec, usec; 178852Sdf157793 178952Sdf157793 asy->asy_flags |= ASY_PPS_EDGE; 179052Sdf157793 gethrestime(&ts); 179152Sdf157793 nsec = ts.tv_nsec; 179252Sdf157793 usec = nsec + (nsec >> 2); 179352Sdf157793 usec = nsec + (usec >> 1); 179452Sdf157793 usec = nsec + (usec >> 2); 179552Sdf157793 usec = nsec + (usec >> 4); 179652Sdf157793 usec = nsec - (usec >> 3); 179752Sdf157793 usec = nsec + (usec >> 2); 179852Sdf157793 usec = nsec + (usec >> 3); 179952Sdf157793 usec = nsec + (usec >> 4); 180052Sdf157793 usec = nsec + (usec >> 1); 180152Sdf157793 usec = nsec + (usec >> 6); 180252Sdf157793 tvp->tv_usec = usec >> 10; 180352Sdf157793 tvp->tv_sec = ts.tv_sec; 180452Sdf157793 180552Sdf157793 ++asy_ppsev.serial; 180652Sdf157793 180752Sdf157793 /* 180852Sdf157793 * Because the kernel keeps a high-resolution time, 180952Sdf157793 * pass the current highres timestamp in tvp and zero 181052Sdf157793 * in usec. 181152Sdf157793 */ 181252Sdf157793 ddi_hardpps(tvp, 0); 181352Sdf157793 } 181452Sdf157793 } 181552Sdf157793 181652Sdf157793 /* 181752Sdf157793 * Modem status interrupt. 181852Sdf157793 * 181952Sdf157793 * (Note: It is assumed that the MSR hasn't been read by asyintr().) 182052Sdf157793 */ 182152Sdf157793 182252Sdf157793 static void 182352Sdf157793 async_msint(struct asycom *asy) 182452Sdf157793 { 182552Sdf157793 struct asyncline *async = (struct asyncline *)asy->asy_priv; 182652Sdf157793 int msr; 182752Sdf157793 182852Sdf157793 msr = INB(MSR); /* this resets the interrupt */ 182952Sdf157793 asy->asy_cached_msr = msr; 183052Sdf157793 #ifdef DEBUG 183152Sdf157793 if (asydebug & ASY_DEBUG_STATE) { 183252Sdf157793 printf(" transition: %3s %3s %3s %3s\n" 183352Sdf157793 "current state: %3s %3s %3s %3s\n", 183452Sdf157793 (msr & DCTS) ? "CTS" : " ", 183552Sdf157793 (msr & DDSR) ? "DSR" : " ", 183652Sdf157793 (msr & DRI) ? "RI " : " ", 183752Sdf157793 (msr & DDCD) ? "DCD" : " ", 183852Sdf157793 (msr & CTS) ? "CTS" : " ", 183952Sdf157793 (msr & DSR) ? "DSR" : " ", 184052Sdf157793 (msr & RI) ? "RI " : " ", 184152Sdf157793 (msr & DCD) ? "DCD" : " "); 184252Sdf157793 } 184352Sdf157793 #endif 184452Sdf157793 if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) { 184552Sdf157793 #ifdef DEBUG 184652Sdf157793 if (asydebug & ASY_DEBUG_HFLOW) 184752Sdf157793 printf("asy%d: hflow start\n", 184852Sdf157793 UNIT(async->async_dev)); 184952Sdf157793 #endif 185052Sdf157793 async->async_flags |= ASYNC_HW_OUT_FLW; 185152Sdf157793 } 185252Sdf157793 if (asy->asy_hwtype == ASY82510) 185352Sdf157793 OUTB(MSR, (msr & 0xF0)); 185452Sdf157793 185552Sdf157793 /* Handle PPS event */ 185652Sdf157793 if (asy->asy_flags & ASY_PPS) 185752Sdf157793 asy_ppsevent(asy, msr); 185852Sdf157793 185952Sdf157793 async->async_ext++; 186052Sdf157793 ASYSETSOFT(asy); 186152Sdf157793 } 186252Sdf157793 186352Sdf157793 /* 186452Sdf157793 * Handle a second-stage interrupt. 186552Sdf157793 */ 186652Sdf157793 uint_t 186752Sdf157793 asysoftintr(caddr_t intarg) 186852Sdf157793 { 186952Sdf157793 struct asycom *asy = (struct asycom *)intarg; 187052Sdf157793 struct asyncline *async; 187152Sdf157793 int rv; 187252Sdf157793 int cc; 187352Sdf157793 /* 187452Sdf157793 * Test and clear soft interrupt. 187552Sdf157793 */ 187652Sdf157793 mutex_enter(asy->asy_soft_lock); 187752Sdf157793 #ifdef DEBUG 187852Sdf157793 if (asydebug & ASY_DEBUG_PROCS) 187952Sdf157793 printf("softintr\n"); 188052Sdf157793 #endif 188152Sdf157793 rv = asy->asysoftpend; 188252Sdf157793 if (rv != 0) 188352Sdf157793 asy->asysoftpend = 0; 188452Sdf157793 mutex_exit(asy->asy_soft_lock); 188552Sdf157793 188652Sdf157793 if (rv) { 188752Sdf157793 if (asy->asy_priv == NULL) 188852Sdf157793 return (rv); 188952Sdf157793 async = (struct asyncline *)asy->asy_priv; 189052Sdf157793 mutex_enter(asy->asy_excl_hi); 189152Sdf157793 if (asy->asy_flags & ASY_NEEDSOFT) { 189252Sdf157793 asy->asy_flags &= ~ASY_NEEDSOFT; 189352Sdf157793 mutex_exit(asy->asy_excl_hi); 189452Sdf157793 (void) async_softint(asy); 189552Sdf157793 mutex_enter(asy->asy_excl_hi); 189652Sdf157793 } 189752Sdf157793 /* 189852Sdf157793 * There are some instances where the softintr is not 189952Sdf157793 * scheduled and hence not called. It so happened that makes 190052Sdf157793 * the last few characters to be stuck in ringbuffer. 190152Sdf157793 * Hence, call once again the handler so that the last few 190252Sdf157793 * characters are cleared. 190352Sdf157793 */ 190452Sdf157793 cc = RING_CNT(async); 190552Sdf157793 mutex_exit(asy->asy_excl_hi); 190652Sdf157793 if (cc > 0) { 190752Sdf157793 (void) async_softint(asy); 190852Sdf157793 } 190952Sdf157793 } 191052Sdf157793 return (rv); 191152Sdf157793 } 191252Sdf157793 191352Sdf157793 /* 191452Sdf157793 * Handle a software interrupt. 191552Sdf157793 */ 191652Sdf157793 static int 191752Sdf157793 async_softint(struct asycom *asy) 191852Sdf157793 { 191952Sdf157793 struct asyncline *async = (struct asyncline *)asy->asy_priv; 192052Sdf157793 uint_t cc; 192152Sdf157793 mblk_t *bp; 192252Sdf157793 queue_t *q; 192352Sdf157793 uchar_t val; 192452Sdf157793 uchar_t c; 192552Sdf157793 tty_common_t *tp; 192652Sdf157793 192752Sdf157793 #ifdef DEBUG 192852Sdf157793 if (asydebug & ASY_DEBUG_PROCS) 192952Sdf157793 printf("process\n"); 193052Sdf157793 #endif 193152Sdf157793 mutex_enter(asy->asy_excl); 193252Sdf157793 if (asy->asy_flags & ASY_DOINGSOFT) { 193352Sdf157793 mutex_exit(asy->asy_excl); 193452Sdf157793 return (0); 193552Sdf157793 } 193652Sdf157793 tp = &async->async_ttycommon; 193752Sdf157793 q = tp->t_readq; 193852Sdf157793 if (q != NULL) { 193952Sdf157793 mutex_exit(asy->asy_excl); 194052Sdf157793 enterq(q); 194152Sdf157793 mutex_enter(asy->asy_excl); 194252Sdf157793 } 194352Sdf157793 mutex_enter(asy->asy_excl_hi); 194452Sdf157793 asy->asy_flags |= ASY_DOINGSOFT; 194552Sdf157793 194652Sdf157793 if (INB(ICR) & MIEN) 194752Sdf157793 val = asy->asy_cached_msr & 0xFF; 194852Sdf157793 else 194952Sdf157793 val = INB(MSR) & 0xFF; 195052Sdf157793 195152Sdf157793 if (async->async_ttycommon.t_cflag & CRTSCTS) { 195252Sdf157793 if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) { 195352Sdf157793 #ifdef DEBUG 195452Sdf157793 if (asydebug & ASY_DEBUG_HFLOW) 195552Sdf157793 printf("asy%d: hflow start\n", 195652Sdf157793 UNIT(async->async_dev)); 195752Sdf157793 #endif 195852Sdf157793 async->async_flags &= ~ASYNC_HW_OUT_FLW; 195952Sdf157793 mutex_exit(asy->asy_excl_hi); 196052Sdf157793 if (async->async_ocnt > 0) { 196152Sdf157793 mutex_enter(asy->asy_excl_hi); 196252Sdf157793 async_resume(async); 196352Sdf157793 mutex_exit(asy->asy_excl_hi); 196452Sdf157793 } else { 196552Sdf157793 async_start(async); 196652Sdf157793 } 196752Sdf157793 mutex_enter(asy->asy_excl_hi); 196852Sdf157793 } 196952Sdf157793 } 197052Sdf157793 if (async->async_ext) { 197152Sdf157793 async->async_ext = 0; 197252Sdf157793 /* check for carrier up */ 197352Sdf157793 if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) { 197452Sdf157793 /* carrier present */ 197552Sdf157793 if ((async->async_flags & ASYNC_CARR_ON) == 0) { 197652Sdf157793 async->async_flags |= ASYNC_CARR_ON; 197752Sdf157793 mutex_exit(asy->asy_excl_hi); 197852Sdf157793 mutex_exit(asy->asy_excl); 197952Sdf157793 if (async->async_flags & ASYNC_ISOPEN) 198052Sdf157793 (void) putctl(q, M_UNHANGUP); 198152Sdf157793 cv_broadcast(&async->async_flags_cv); 198252Sdf157793 mutex_enter(asy->asy_excl); 198352Sdf157793 mutex_enter(asy->asy_excl_hi); 198452Sdf157793 } 198552Sdf157793 } else { 198652Sdf157793 if ((async->async_flags & ASYNC_CARR_ON) && 198752Sdf157793 !(tp->t_cflag & CLOCAL)) { 198852Sdf157793 int flushflag; 198952Sdf157793 199052Sdf157793 /* 199152Sdf157793 * Carrier went away. 199252Sdf157793 * Drop DTR, abort any output in 199352Sdf157793 * progress, indicate that output is 199452Sdf157793 * not stopped, and send a hangup 199552Sdf157793 * notification upstream. 199652Sdf157793 * 199752Sdf157793 * If we're in the midst of close, then flush 199852Sdf157793 * everything. Don't leave stale ioctls lying 199952Sdf157793 * about. 200052Sdf157793 */ 200152Sdf157793 val = INB(MCR); 200252Sdf157793 OUTB(MCR, (val & ~DTR)); 200352Sdf157793 flushflag = (async->async_flags & 200452Sdf157793 ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA; 200552Sdf157793 flushq(tp->t_writeq, flushflag); 200652Sdf157793 if (async->async_xmitblk != NULL) { 200752Sdf157793 freeb(async->async_xmitblk); 200852Sdf157793 async->async_xmitblk = NULL; 200952Sdf157793 } 201052Sdf157793 if (async->async_flags & ASYNC_BUSY) { 201152Sdf157793 async->async_ocnt = 0; 201252Sdf157793 async->async_flags &= ~ASYNC_BUSY; 201352Sdf157793 } 201452Sdf157793 async->async_flags &= ~ASYNC_STOPPED; 201552Sdf157793 if (async->async_flags & ASYNC_ISOPEN) { 201652Sdf157793 mutex_exit(asy->asy_excl_hi); 201752Sdf157793 mutex_exit(asy->asy_excl); 201852Sdf157793 (void) putctl(q, M_HANGUP); 201952Sdf157793 mutex_enter(asy->asy_excl); 202052Sdf157793 mutex_enter(asy->asy_excl_hi); 202152Sdf157793 } 20221815Srameshc async->async_flags &= ~ASYNC_CARR_ON; 20231815Srameshc mutex_exit(asy->asy_excl_hi); 20241815Srameshc cv_broadcast(&async->async_flags_cv); 20251815Srameshc mutex_enter(asy->asy_excl_hi); 202652Sdf157793 } 202752Sdf157793 } 202852Sdf157793 } 202952Sdf157793 203052Sdf157793 /* 203152Sdf157793 * If data has been added to the circular buffer, remove 203252Sdf157793 * it from the buffer, and send it up the stream if there's 203352Sdf157793 * somebody listening. Try to do it 16 bytes at a time. If we 203452Sdf157793 * have more than 16 bytes to move, move 16 byte chunks and 203552Sdf157793 * leave the rest for next time around (maybe it will grow). 203652Sdf157793 */ 203752Sdf157793 if (!(async->async_flags & ASYNC_ISOPEN)) { 203852Sdf157793 RING_INIT(async); 203952Sdf157793 goto rv; 204052Sdf157793 } 204152Sdf157793 if ((cc = RING_CNT(async)) == 0) { 204252Sdf157793 goto rv; 204352Sdf157793 } 204452Sdf157793 mutex_exit(asy->asy_excl_hi); 204552Sdf157793 204652Sdf157793 if (!canput(q)) { 204752Sdf157793 if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { 204852Sdf157793 #ifdef DEBUG 204952Sdf157793 if (!(asydebug & ASY_DEBUG_HFLOW)) { 205052Sdf157793 printf("asy%d: hflow stop input.\n", 205152Sdf157793 UNIT(async->async_dev)); 205252Sdf157793 if (canputnext(q)) 205352Sdf157793 printf("asy%d: next queue is " 205452Sdf157793 "ready\n", 205552Sdf157793 UNIT(async->async_dev)); 205652Sdf157793 } 205752Sdf157793 #endif 205852Sdf157793 mutex_enter(asy->asy_excl_hi); 205952Sdf157793 async->async_flags |= ASYNC_HW_IN_FLOW; 206052Sdf157793 async->async_flowc = async->async_stopc; 206152Sdf157793 } else mutex_enter(asy->asy_excl_hi); 206252Sdf157793 goto rv; 206352Sdf157793 } 206452Sdf157793 206552Sdf157793 if (async->async_ringbuf_overflow) { 206652Sdf157793 if ((async->async_flags & ASYNC_HW_IN_FLOW) && 206752Sdf157793 ((int)(RING_CNT(async)) < (RINGSIZE/4))) { 206852Sdf157793 #ifdef DEBUG 206952Sdf157793 if (asydebug & ASY_DEBUG_HFLOW) 207052Sdf157793 printf("asy%d: hflow start input.\n", 207152Sdf157793 UNIT(async->async_dev)); 207252Sdf157793 #endif 207352Sdf157793 mutex_enter(asy->asy_excl_hi); 207452Sdf157793 async->async_flags &= ~ASYNC_HW_IN_FLOW; 207552Sdf157793 async->async_flowc = async->async_startc; 207652Sdf157793 async->async_ringbuf_overflow = 0; 207752Sdf157793 goto rv; 207852Sdf157793 } 207952Sdf157793 } 208052Sdf157793 #ifdef DEBUG 208152Sdf157793 if (asydebug & ASY_DEBUG_INPUT) 208252Sdf157793 printf("asy%d: %d char(s) in queue.\n", 208352Sdf157793 UNIT(async->async_dev), cc); 208452Sdf157793 #endif 208552Sdf157793 /* 208652Sdf157793 * Before you pull the characters from the RING BUF 208752Sdf157793 * Check whether you can put into the queue again 208852Sdf157793 */ 208952Sdf157793 if ((!canputnext(q)) || (!canput(q))) { 209052Sdf157793 mutex_enter(asy->asy_excl_hi); 209152Sdf157793 if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) { 209252Sdf157793 async->async_flags |= ASYNC_HW_IN_FLOW; 209352Sdf157793 async->async_flowc = async->async_stopc; 209452Sdf157793 async->async_queue_full = 1; 209552Sdf157793 } 209652Sdf157793 goto rv; 209752Sdf157793 } 209852Sdf157793 mutex_enter(asy->asy_excl_hi); 209952Sdf157793 if (async->async_queue_full) { 210052Sdf157793 /* 210152Sdf157793 * Last time the Stream queue didnot allow 210252Sdf157793 * now it allows so, relax, the flow control 210352Sdf157793 */ 210452Sdf157793 if (async->async_flags & ASYNC_HW_IN_FLOW) { 210552Sdf157793 async->async_flags &= ~ASYNC_HW_IN_FLOW; 210652Sdf157793 async->async_queue_full = 0; 210752Sdf157793 async->async_flowc = async->async_startc; 210852Sdf157793 goto rv; 210952Sdf157793 } else 211052Sdf157793 async->async_queue_full = 0; 211152Sdf157793 } 211252Sdf157793 mutex_exit(asy->asy_excl_hi); 211352Sdf157793 if (!(bp = allocb(cc, BPRI_MED))) { 211452Sdf157793 ttycommon_qfull(&async->async_ttycommon, q); 211552Sdf157793 mutex_enter(asy->asy_excl_hi); 211652Sdf157793 goto rv; 211752Sdf157793 } 211852Sdf157793 mutex_enter(asy->asy_excl_hi); 211952Sdf157793 do { 212052Sdf157793 if (RING_ERR(async, S_ERRORS)) { 212152Sdf157793 RING_UNMARK(async); 212252Sdf157793 c = RING_GET(async); 212352Sdf157793 break; 212452Sdf157793 } else { 212552Sdf157793 *bp->b_wptr++ = RING_GET(async); 212652Sdf157793 } 212752Sdf157793 } while (--cc); 212852Sdf157793 212952Sdf157793 mutex_exit(asy->asy_excl_hi); 213052Sdf157793 mutex_exit(asy->asy_excl); 213152Sdf157793 if (bp->b_wptr > bp->b_rptr) { 213252Sdf157793 if (!canputnext(q)) { 213352Sdf157793 if (!canput(q)) { 213452Sdf157793 /* 213552Sdf157793 * Even after taking all precautions that 213652Sdf157793 * Still we are unable to queue, then we 213752Sdf157793 * cannot do anything, just drop the block 213852Sdf157793 */ 213952Sdf157793 cmn_err(CE_NOTE, 214052Sdf157793 "su%d: local queue full\n", 214152Sdf157793 UNIT(async->async_dev)); 214252Sdf157793 freemsg(bp); 214352Sdf157793 mutex_enter(asy->asy_excl_hi); 214452Sdf157793 if ((async->async_flags & 214552Sdf157793 ASYNC_HW_IN_FLOW) == 0) { 214652Sdf157793 async->async_flags |= 214752Sdf157793 ASYNC_HW_IN_FLOW; 214852Sdf157793 async->async_flowc = 214952Sdf157793 async->async_stopc; 215052Sdf157793 async->async_queue_full = 1; 215152Sdf157793 } 215252Sdf157793 mutex_exit(asy->asy_excl_hi); 215352Sdf157793 } else { 215452Sdf157793 (void) putq(q, bp); 215552Sdf157793 } 215652Sdf157793 } else { 215752Sdf157793 putnext(q, bp); 215852Sdf157793 } 215952Sdf157793 } else { 216052Sdf157793 freemsg(bp); 216152Sdf157793 } 216252Sdf157793 /* 216352Sdf157793 * If we have a parity error, then send 216452Sdf157793 * up an M_BREAK with the "bad" 216552Sdf157793 * character as an argument. Let ldterm 216652Sdf157793 * figure out what to do with the error. 216752Sdf157793 */ 216852Sdf157793 if (cc) 216952Sdf157793 (void) putctl1(q, M_BREAK, c); 217052Sdf157793 mutex_enter(asy->asy_excl); 217152Sdf157793 mutex_enter(asy->asy_excl_hi); 217252Sdf157793 rv: 217352Sdf157793 /* 217452Sdf157793 * If a transmission has finished, indicate that it's finished, 217552Sdf157793 * and start that line up again. 217652Sdf157793 */ 217752Sdf157793 if (async->async_break) { 217852Sdf157793 async->async_break = 0; 217952Sdf157793 if (async->async_flags & ASYNC_ISOPEN) { 218052Sdf157793 mutex_exit(asy->asy_excl_hi); 218152Sdf157793 mutex_exit(asy->asy_excl); 218252Sdf157793 (void) putctl(q, M_BREAK); 218352Sdf157793 mutex_enter(asy->asy_excl); 218452Sdf157793 mutex_enter(asy->asy_excl_hi); 218552Sdf157793 } 218652Sdf157793 } 218752Sdf157793 if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) || 2188859Skc28005 (async->async_flowc != '\0')) { 218952Sdf157793 async->async_flags &= ~ASYNC_BUSY; 219052Sdf157793 mutex_exit(asy->asy_excl_hi); 219152Sdf157793 if (async->async_xmitblk) 219252Sdf157793 freeb(async->async_xmitblk); 219352Sdf157793 async->async_xmitblk = NULL; 219452Sdf157793 if (async->async_flags & ASYNC_ISOPEN) { 219552Sdf157793 asy->inperim = B_TRUE; 219652Sdf157793 mutex_exit(asy->asy_excl); 219752Sdf157793 enterq(async->async_ttycommon.t_writeq); 219852Sdf157793 mutex_enter(asy->asy_excl); 219952Sdf157793 } 220052Sdf157793 async_start(async); 220152Sdf157793 /* 220252Sdf157793 * We need to check for inperim and ISOPEN due to 220352Sdf157793 * multi-threading implications; it's possible to close the 220452Sdf157793 * port and nullify async_flags while completing the software 220552Sdf157793 * interrupt. If the port is closed, leaveq() will have already 220652Sdf157793 * been called. We don't want to call it twice. 220752Sdf157793 */ 220852Sdf157793 if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) { 220952Sdf157793 mutex_exit(asy->asy_excl); 221052Sdf157793 leaveq(async->async_ttycommon.t_writeq); 221152Sdf157793 mutex_enter(asy->asy_excl); 221252Sdf157793 asy->inperim = B_FALSE; 221352Sdf157793 } 221452Sdf157793 if (!(async->async_flags & ASYNC_BUSY)) 221552Sdf157793 cv_broadcast(&async->async_flags_cv); 221652Sdf157793 mutex_enter(asy->asy_excl_hi); 221752Sdf157793 } 221852Sdf157793 /* 221952Sdf157793 * A note about these overrun bits: all they do is *tell* someone 222052Sdf157793 * about an error- They do not track multiple errors. In fact, 222152Sdf157793 * you could consider them latched register bits if you like. 222252Sdf157793 * We are only interested in printing the error message once for 222352Sdf157793 * any cluster of overrun errrors. 222452Sdf157793 */ 222552Sdf157793 if (async->async_hw_overrun) { 222652Sdf157793 if (async->async_flags & ASYNC_ISOPEN) { 222752Sdf157793 if (su_log > 0) { 222852Sdf157793 mutex_exit(asy->asy_excl_hi); 222952Sdf157793 mutex_exit(asy->asy_excl); 223052Sdf157793 cmn_err(CE_NOTE, "su%d: silo overflow\n", 223152Sdf157793 UNIT(async->async_dev)); 223252Sdf157793 mutex_enter(asy->asy_excl); 223352Sdf157793 mutex_enter(asy->asy_excl_hi); 223452Sdf157793 } 223552Sdf157793 INC64_KSTAT(asy, siloover); 223652Sdf157793 } 223752Sdf157793 async->async_hw_overrun = 0; 223852Sdf157793 } 223952Sdf157793 if (async->async_sw_overrun) { 224052Sdf157793 if (async->async_flags & ASYNC_ISOPEN) { 224152Sdf157793 if (su_log > 0) { 224252Sdf157793 mutex_exit(asy->asy_excl_hi); 224352Sdf157793 mutex_exit(asy->asy_excl); 224452Sdf157793 cmn_err(CE_NOTE, "su%d: ring buffer overflow\n", 224552Sdf157793 UNIT(async->async_dev)); 224652Sdf157793 mutex_enter(asy->asy_excl); 224752Sdf157793 mutex_enter(asy->asy_excl_hi); 224852Sdf157793 } 224952Sdf157793 INC64_KSTAT(asy, ringover); 225052Sdf157793 } 225152Sdf157793 async->async_sw_overrun = 0; 225252Sdf157793 } 225352Sdf157793 asy->asy_flags &= ~ASY_DOINGSOFT; 225452Sdf157793 mutex_exit(asy->asy_excl_hi); 225552Sdf157793 mutex_exit(asy->asy_excl); 225652Sdf157793 if (q != NULL) 225752Sdf157793 leaveq(q); 225852Sdf157793 return (0); 225952Sdf157793 } 226052Sdf157793 226152Sdf157793 /* 226252Sdf157793 * Restart output on a line after a delay or break timer expired. 226352Sdf157793 */ 226452Sdf157793 static void 226552Sdf157793 async_restart(void *arg) 226652Sdf157793 { 226752Sdf157793 struct asyncline *async = arg; 226852Sdf157793 struct asycom *asy = async->async_common; 226952Sdf157793 queue_t *q; 227052Sdf157793 uchar_t lcr; 227152Sdf157793 227252Sdf157793 /* 227352Sdf157793 * If break timer expired, turn off the break bit. 227452Sdf157793 */ 227552Sdf157793 #ifdef DEBUG 227652Sdf157793 if (asydebug & ASY_DEBUG_PROCS) 227752Sdf157793 printf("restart\n"); 227852Sdf157793 #endif 227952Sdf157793 mutex_enter(asy->asy_excl); 228052Sdf157793 if (async->async_flags & ASYNC_BREAK) { 22811434Skc28005 unsigned int rate; 22821434Skc28005 228352Sdf157793 mutex_enter(asy->asy_excl_hi); 228452Sdf157793 lcr = INB(LCR); 228552Sdf157793 OUTB(LCR, (lcr & ~SETBREAK)); 22861434Skc28005 22871434Skc28005 /* 22881434Skc28005 * Go to sleep for the time it takes for at least one 22891434Skc28005 * stop bit to be received by the device at the other 22901434Skc28005 * end of the line as stated in the RS-232 specification. 22911434Skc28005 * The wait period is equal to: 22921434Skc28005 * 2 clock cycles * (1 MICROSEC / baud rate) 22931434Skc28005 */ 22941434Skc28005 rate = async->async_ttycommon.t_cflag & CBAUD; 22951434Skc28005 if (async->async_ttycommon.t_cflag & CBAUDEXT) 22961434Skc28005 rate += 16; 22971434Skc28005 if (rate >= N_SU_SPEEDS || rate == B0) { 22981434Skc28005 rate = B9600; 22991434Skc28005 } 23001434Skc28005 230152Sdf157793 mutex_exit(asy->asy_excl_hi); 23021434Skc28005 mutex_exit(asy->asy_excl); 23031434Skc28005 drv_usecwait(2 * MICROSEC / baudtable[rate]); 23041434Skc28005 mutex_enter(asy->asy_excl); 230552Sdf157793 } 230652Sdf157793 async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING); 230752Sdf157793 if ((q = async->async_ttycommon.t_writeq) != NULL) { 230852Sdf157793 mutex_exit(asy->asy_excl); 230952Sdf157793 enterq(q); 231052Sdf157793 mutex_enter(asy->asy_excl); 231152Sdf157793 } 231252Sdf157793 async_start(async); 231352Sdf157793 mutex_exit(asy->asy_excl); 231452Sdf157793 if (q != NULL) 231552Sdf157793 leaveq(q); 231652Sdf157793 231752Sdf157793 /* cleared break or delay flag; may have made some output progress */ 231852Sdf157793 cv_broadcast(&async->async_flags_cv); 231952Sdf157793 } 232052Sdf157793 232152Sdf157793 static void 232252Sdf157793 async_start(struct asyncline *async) 232352Sdf157793 { 232452Sdf157793 async_nstart(async, 0); 232552Sdf157793 } 232652Sdf157793 232752Sdf157793 /* 232852Sdf157793 * Start output on a line, unless it's busy, frozen, or otherwise. 232952Sdf157793 */ 233052Sdf157793 static void 233152Sdf157793 async_nstart(struct asyncline *async, int mode) 233252Sdf157793 { 233352Sdf157793 register struct asycom *asy = async->async_common; 233452Sdf157793 register int cc; 233552Sdf157793 register queue_t *q; 233652Sdf157793 mblk_t *bp, *nbp; 233752Sdf157793 uchar_t *xmit_addr; 233852Sdf157793 uchar_t val; 233952Sdf157793 int fifo_len = 1; 234052Sdf157793 int xmit_progress; 234152Sdf157793 234252Sdf157793 #ifdef DEBUG 234352Sdf157793 if (asydebug & ASY_DEBUG_PROCS) 234452Sdf157793 printf("start\n"); 234552Sdf157793 #endif 234652Sdf157793 if (asy->asy_use_fifo == FIFO_ON) 234752Sdf157793 fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 234852Sdf157793 234952Sdf157793 ASSERT(mutex_owned(asy->asy_excl)); 235052Sdf157793 mutex_enter(asy->asy_excl_hi); 235152Sdf157793 asycheckflowcontrol_hw(asy); 235252Sdf157793 235352Sdf157793 /* 235452Sdf157793 * If the chip is busy (i.e., we're waiting for a break timeout 235552Sdf157793 * to expire, or for the current transmission to finish, or for 235652Sdf157793 * output to finish draining from chip), don't grab anything new. 235752Sdf157793 */ 235852Sdf157793 if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) { 235952Sdf157793 mutex_exit(asy->asy_excl_hi); 236052Sdf157793 #ifdef DEBUG 236152Sdf157793 if (mode && asydebug & ASY_DEBUG_CLOSE) 236252Sdf157793 printf("asy%d: start %s.\n", 236352Sdf157793 UNIT(async->async_dev), 236452Sdf157793 async->async_flags & ASYNC_BREAK 236552Sdf157793 ? "break" : "busy"); 236652Sdf157793 #endif 236752Sdf157793 return; 236852Sdf157793 } 236952Sdf157793 237052Sdf157793 /* 237152Sdf157793 * If we have a flow-control character to transmit, do it now. 237252Sdf157793 */ 237352Sdf157793 if (asycheckflowcontrol_sw(asy)) { 237452Sdf157793 mutex_exit(asy->asy_excl_hi); 237552Sdf157793 return; 237652Sdf157793 } 237752Sdf157793 mutex_exit(asy->asy_excl_hi); 237852Sdf157793 /* 237952Sdf157793 * If we're waiting for a delay timeout to expire, don't grab 238052Sdf157793 * anything new. 238152Sdf157793 */ 238252Sdf157793 if (async->async_flags & ASYNC_DELAY) { 238352Sdf157793 #ifdef DEBUG 238452Sdf157793 if (mode && asydebug & ASY_DEBUG_CLOSE) 238552Sdf157793 printf("asy%d: start ASYNC_DELAY.\n", 238652Sdf157793 UNIT(async->async_dev)); 238752Sdf157793 #endif 238852Sdf157793 return; 238952Sdf157793 } 239052Sdf157793 239152Sdf157793 if ((q = async->async_ttycommon.t_writeq) == NULL) { 239252Sdf157793 #ifdef DEBUG 239352Sdf157793 if (mode && asydebug & ASY_DEBUG_CLOSE) 239452Sdf157793 printf("asy%d: start writeq is null.\n", 239552Sdf157793 UNIT(async->async_dev)); 239652Sdf157793 #endif 239752Sdf157793 return; /* not attached to a stream */ 239852Sdf157793 } 239952Sdf157793 240052Sdf157793 for (;;) { 240152Sdf157793 if ((bp = getq(q)) == NULL) 240252Sdf157793 return; /* no data to transmit */ 240352Sdf157793 240452Sdf157793 /* 240552Sdf157793 * We have a message block to work on. 240652Sdf157793 * Check whether it's a break, a delay, or an ioctl (the latter 240752Sdf157793 * occurs if the ioctl in question was waiting for the output 240852Sdf157793 * to drain). If it's one of those, process it immediately. 240952Sdf157793 */ 241052Sdf157793 switch (bp->b_datap->db_type) { 241152Sdf157793 241252Sdf157793 case M_BREAK: 241352Sdf157793 /* 241452Sdf157793 * Set the break bit, and arrange for "async_restart" 241552Sdf157793 * to be called in 1/4 second; it will turn the 241652Sdf157793 * break bit off, and call "async_start" to grab 241752Sdf157793 * the next message. 241852Sdf157793 */ 241952Sdf157793 mutex_enter(asy->asy_excl_hi); 242052Sdf157793 val = INB(LCR); 242152Sdf157793 OUTB(LCR, (val | SETBREAK)); 242252Sdf157793 mutex_exit(asy->asy_excl_hi); 242352Sdf157793 async->async_flags |= ASYNC_BREAK; 242452Sdf157793 (void) timeout(async_restart, async, hz / 4); 242552Sdf157793 freemsg(bp); 242652Sdf157793 return; /* wait for this to finish */ 242752Sdf157793 242852Sdf157793 case M_DELAY: 242952Sdf157793 /* 243052Sdf157793 * Arrange for "async_restart" to be called when the 243152Sdf157793 * delay expires; it will turn ASYNC_DELAY off, 243252Sdf157793 * and call "async_start" to grab the next message. 243352Sdf157793 */ 243452Sdf157793 (void) timeout(async_restart, async, 243552Sdf157793 (clock_t)(*(unsigned char *)bp->b_rptr + 6)); 243652Sdf157793 async->async_flags |= ASYNC_DELAY; 243752Sdf157793 freemsg(bp); 243852Sdf157793 return; /* wait for this to finish */ 243952Sdf157793 244052Sdf157793 case M_IOCTL: 244152Sdf157793 /* 244252Sdf157793 * This ioctl needs to wait for the output ahead of 244352Sdf157793 * it to drain. Try to do it, and then either 244452Sdf157793 * redo the ioctl at a later time or grab the next 244552Sdf157793 * message after it. 244652Sdf157793 */ 244752Sdf157793 244852Sdf157793 mutex_enter(asy->asy_excl_hi); 244952Sdf157793 if (asy_isbusy(asy)) { 245052Sdf157793 /* 245152Sdf157793 * Get the divisor by calculating the rate 245252Sdf157793 */ 245352Sdf157793 unsigned int rate; 245452Sdf157793 245552Sdf157793 mutex_exit(asy->asy_excl_hi); 245652Sdf157793 rate = async->async_ttycommon.t_cflag & CBAUD; 245752Sdf157793 if (async->async_ttycommon.t_cflag & CBAUDEXT) 245852Sdf157793 rate += 16; 245952Sdf157793 if (rate >= N_SU_SPEEDS || rate == B0) { 246052Sdf157793 rate = B9600; 246152Sdf157793 } 246252Sdf157793 246352Sdf157793 /* 246452Sdf157793 * We need to do a callback as the port will 246552Sdf157793 * be set to drain 246652Sdf157793 */ 246752Sdf157793 async->async_flags |= ASYNC_DRAINING; 246852Sdf157793 246952Sdf157793 /* 247052Sdf157793 * Put the message we just processed back onto 247152Sdf157793 * the end of the queue 247252Sdf157793 */ 247352Sdf157793 if (putq(q, bp) == 0) 247452Sdf157793 freemsg(bp); 247552Sdf157793 247652Sdf157793 /* 247752Sdf157793 * We need to delay until the TSR and THR 247852Sdf157793 * have been exhausted. We base the delay on 247952Sdf157793 * the amount of time it takes to transmit 248052Sdf157793 * 2 chars at the current baud rate in 248152Sdf157793 * microseconds. 248252Sdf157793 * 248352Sdf157793 * Therefore, the wait period is: 248452Sdf157793 * 248552Sdf157793 * (#TSR bits + #THR bits) * 248652Sdf157793 * 1 MICROSEC / baud rate 248752Sdf157793 */ 248852Sdf157793 (void) timeout(async_restart, async, 248952Sdf157793 drv_usectohz(16 * MICROSEC / 249052Sdf157793 baudtable[rate])); 249152Sdf157793 return; 249252Sdf157793 } 249352Sdf157793 mutex_exit(asy->asy_excl_hi); 249452Sdf157793 mutex_exit(asy->asy_excl); 249552Sdf157793 async_ioctl(async, q, bp, B_FALSE); 249652Sdf157793 mutex_enter(asy->asy_excl); 249752Sdf157793 continue; 249852Sdf157793 } 249952Sdf157793 250052Sdf157793 while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) { 250152Sdf157793 nbp = bp->b_cont; 250252Sdf157793 freeb(bp); 250352Sdf157793 bp = nbp; 250452Sdf157793 } 250552Sdf157793 if (bp != NULL) 250652Sdf157793 break; 250752Sdf157793 } 250852Sdf157793 250952Sdf157793 /* 251052Sdf157793 * We have data to transmit. If output is stopped, put 251152Sdf157793 * it back and try again later. 251252Sdf157793 */ 251352Sdf157793 if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) { 251452Sdf157793 #ifdef DEBUG 251552Sdf157793 if (asydebug & ASY_DEBUG_HFLOW && 251652Sdf157793 async->async_flags & ASYNC_HW_OUT_FLW) 251752Sdf157793 printf("asy%d: output hflow in effect.\n", 251852Sdf157793 UNIT(async->async_dev)); 251952Sdf157793 #endif 252052Sdf157793 mutex_exit(asy->asy_excl); 252152Sdf157793 (void) putbq(q, bp); 252252Sdf157793 /* 252352Sdf157793 * We entered the routine owning the lock, we need to 252452Sdf157793 * exit the routine owning the lock. 252552Sdf157793 */ 252652Sdf157793 mutex_enter(asy->asy_excl); 252752Sdf157793 return; 252852Sdf157793 } 252952Sdf157793 253052Sdf157793 async->async_xmitblk = bp; 253152Sdf157793 xmit_addr = bp->b_rptr; 253252Sdf157793 bp = bp->b_cont; 253352Sdf157793 if (bp != NULL) { 253452Sdf157793 mutex_exit(asy->asy_excl); 253552Sdf157793 (void) putbq(q, bp); /* not done with this message yet */ 253652Sdf157793 mutex_enter(asy->asy_excl); 253752Sdf157793 } 253852Sdf157793 253952Sdf157793 /* 254052Sdf157793 * In 5-bit mode, the high order bits are used 254152Sdf157793 * to indicate character sizes less than five, 254252Sdf157793 * so we need to explicitly mask before transmitting 254352Sdf157793 */ 254452Sdf157793 if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { 254552Sdf157793 register unsigned char *p = xmit_addr; 254652Sdf157793 register int cnt = cc; 254752Sdf157793 254852Sdf157793 while (cnt--) 254952Sdf157793 *p++ &= (unsigned char) 0x1f; 255052Sdf157793 } 255152Sdf157793 255252Sdf157793 /* 255352Sdf157793 * Set up this block for pseudo-DMA. 255452Sdf157793 */ 255552Sdf157793 mutex_enter(asy->asy_excl_hi); 255652Sdf157793 async->async_optr = xmit_addr; 255752Sdf157793 async->async_ocnt = cc; 255852Sdf157793 /* 255952Sdf157793 * If the transmitter is ready, shove some 256052Sdf157793 * characters out. 256152Sdf157793 */ 256252Sdf157793 xmit_progress = 0; 256352Sdf157793 while (fifo_len-- && async->async_ocnt) { 256452Sdf157793 if (INB(LSR) & XHRE) { 256552Sdf157793 OUTB(DAT, *async->async_optr++); 256652Sdf157793 async->async_ocnt--; 256752Sdf157793 xmit_progress++; 256852Sdf157793 } 256952Sdf157793 } 257052Sdf157793 asy->asy_out_of_band_xmit = xmit_progress; 257152Sdf157793 if (xmit_progress > 0) 257252Sdf157793 async->async_flags |= ASYNC_PROGRESS; 257352Sdf157793 async->async_flags |= ASYNC_BUSY; 257452Sdf157793 mutex_exit(asy->asy_excl_hi); 257552Sdf157793 } 257652Sdf157793 257752Sdf157793 /* 257852Sdf157793 * Resume output by poking the transmitter. 257952Sdf157793 */ 258052Sdf157793 static void 258152Sdf157793 async_resume(struct asyncline *async) 258252Sdf157793 { 258352Sdf157793 register struct asycom *asy = async->async_common; 258452Sdf157793 258552Sdf157793 ASSERT(mutex_owned(asy->asy_excl_hi)); 258652Sdf157793 #ifdef DEBUG 258752Sdf157793 if (asydebug & ASY_DEBUG_PROCS) 258852Sdf157793 printf("resume\n"); 258952Sdf157793 #endif 259052Sdf157793 259152Sdf157793 asycheckflowcontrol_hw(asy); 259252Sdf157793 259352Sdf157793 if (INB(LSR) & XHRE) { 259452Sdf157793 if (asycheckflowcontrol_sw(asy)) { 259552Sdf157793 return; 259652Sdf157793 } else if (async->async_ocnt > 0) { 259752Sdf157793 OUTB(DAT, *async->async_optr++); 259852Sdf157793 async->async_ocnt--; 259952Sdf157793 async->async_flags |= ASYNC_PROGRESS; 260052Sdf157793 } 260152Sdf157793 } 260252Sdf157793 } 260352Sdf157793 260452Sdf157793 /* 260552Sdf157793 * Process an "ioctl" message sent down to us. 260652Sdf157793 * Note that we don't need to get any locks until we are ready to access 260752Sdf157793 * the hardware. Nothing we access until then is going to be altered 260852Sdf157793 * outside of the STREAMS framework, so we should be safe. 260952Sdf157793 */ 261052Sdf157793 static void 261152Sdf157793 async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput) 261252Sdf157793 { 261352Sdf157793 register struct asycom *asy = async->async_common; 261452Sdf157793 register tty_common_t *tp = &async->async_ttycommon; 261552Sdf157793 register struct iocblk *iocp; 261652Sdf157793 register unsigned datasize; 26172211Szk194757 size_t ioc_count; 261852Sdf157793 mblk_t *datamp; 261952Sdf157793 int error = 0; 262052Sdf157793 uchar_t val, icr; 262152Sdf157793 #ifdef DEBUG 262252Sdf157793 if (asydebug & ASY_DEBUG_PROCS) 262352Sdf157793 printf("ioctl\n"); 262452Sdf157793 #endif 262552Sdf157793 262652Sdf157793 if (tp->t_iocpending != NULL) { 262752Sdf157793 /* 262852Sdf157793 * We were holding an "ioctl" response pending the 262952Sdf157793 * availability of an "mblk" to hold data to be passed up; 263052Sdf157793 * another "ioctl" came through, which means that "ioctl" 263152Sdf157793 * must have timed out or been aborted. 263252Sdf157793 */ 263352Sdf157793 freemsg(async->async_ttycommon.t_iocpending); 263452Sdf157793 async->async_ttycommon.t_iocpending = NULL; 263552Sdf157793 } 263652Sdf157793 263752Sdf157793 iocp = (struct iocblk *)mp->b_rptr; 263852Sdf157793 263952Sdf157793 /* 26402211Szk194757 * Save off the ioc count in case we need to restore it 26412211Szk194757 * because we are queuing a message block. 26422211Szk194757 */ 26432211Szk194757 ioc_count = iocp->ioc_count; 26442211Szk194757 26452211Szk194757 /* 264652Sdf157793 * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call 264752Sdf157793 * ttycommon_ioctl() because this function frees up the message block 264852Sdf157793 * (mp->b_cont) that contains the address of the user variable where 264952Sdf157793 * we need to pass back the bit array. 265052Sdf157793 */ 265152Sdf157793 if (iocp->ioc_cmd == TIOCMGET || 265252Sdf157793 iocp->ioc_cmd == TIOCMBIC || 265352Sdf157793 iocp->ioc_cmd == TIOCMBIS || 265452Sdf157793 iocp->ioc_cmd == TIOCMSET || 265552Sdf157793 iocp->ioc_cmd == TIOCGPPS || 265652Sdf157793 iocp->ioc_cmd == TIOCSPPS || 265752Sdf157793 iocp->ioc_cmd == TIOCGPPSEV) 265852Sdf157793 error = -1; /* Do Nothing */ 265952Sdf157793 else 266052Sdf157793 266152Sdf157793 /* 266252Sdf157793 * The only way in which "ttycommon_ioctl" can fail is if the "ioctl" 266352Sdf157793 * requires a response containing data to be returned to the user, 266452Sdf157793 * and no mblk could be allocated for the data. 266552Sdf157793 * No such "ioctl" alters our state. Thus, we always go ahead and 266652Sdf157793 * do any state-changes the "ioctl" calls for. If we couldn't allocate 266752Sdf157793 * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so 266852Sdf157793 * we just call "bufcall" to request that we be called back when we 266952Sdf157793 * stand a better chance of allocating the data. 267052Sdf157793 */ 267152Sdf157793 if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { 267252Sdf157793 if (async->async_wbufcid) 267352Sdf157793 unbufcall(async->async_wbufcid); 267452Sdf157793 async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl, 267552Sdf157793 async); 267652Sdf157793 return; 267752Sdf157793 } 267852Sdf157793 267952Sdf157793 mutex_enter(asy->asy_excl); 268052Sdf157793 268152Sdf157793 if (error == 0) { 268252Sdf157793 /* 268352Sdf157793 * "ttycommon_ioctl" did most of the work; we just use the 268452Sdf157793 * data it set up. 268552Sdf157793 */ 268652Sdf157793 switch (iocp->ioc_cmd) { 268752Sdf157793 268852Sdf157793 case TCSETS: 268952Sdf157793 if (!(asy->asy_rsc_console || asy->asy_rsc_control || 269052Sdf157793 asy->asy_lom_console)) { 269152Sdf157793 mutex_enter(asy->asy_excl_hi); 269252Sdf157793 error = asy_program(asy, ASY_NOINIT); 269352Sdf157793 mutex_exit(asy->asy_excl_hi); 269452Sdf157793 } 269552Sdf157793 break; 269652Sdf157793 case TCSETSF: 269752Sdf157793 case TCSETSW: 269852Sdf157793 case TCSETA: 269952Sdf157793 case TCSETAW: 270052Sdf157793 case TCSETAF: 270152Sdf157793 if (!(asy->asy_rsc_console || asy->asy_rsc_control || 270252Sdf157793 asy->asy_lom_console)) { 270352Sdf157793 mutex_enter(asy->asy_excl_hi); 270452Sdf157793 if (iswput && asy_isbusy(asy)) { 27052211Szk194757 /* 27062211Szk194757 * ttycommon_ioctl sets the db_type to 27072211Szk194757 * M_IOCACK and ioc_count to zero 27082211Szk194757 * we need to undo this when we 27092211Szk194757 * queue a control message. This will 27102211Szk194757 * allow the control messages to be 27112211Szk194757 * processed again when the chip 27122211Szk194757 * becomes available. 27132211Szk194757 */ 27142211Szk194757 mp->b_datap->db_type = M_IOCTL; 27152211Szk194757 iocp->ioc_count = ioc_count; 27162211Szk194757 271752Sdf157793 if (putq(wq, mp) == 0) 271852Sdf157793 freemsg(mp); 271952Sdf157793 mutex_exit(asy->asy_excl_hi); 272052Sdf157793 mutex_exit(asy->asy_excl); 272152Sdf157793 return; 272252Sdf157793 } 272352Sdf157793 error = asy_program(asy, ASY_NOINIT); 272452Sdf157793 mutex_exit(asy->asy_excl_hi); 272552Sdf157793 } 272652Sdf157793 break; 272752Sdf157793 case TIOCSSOFTCAR: 272852Sdf157793 /* Set the driver state appropriately */ 272952Sdf157793 mutex_enter(asy->asy_excl_hi); 273052Sdf157793 if (tp->t_flags & TS_SOFTCAR) 273152Sdf157793 asy->asy_flags |= ASY_IGNORE_CD; 273252Sdf157793 else 273352Sdf157793 asy->asy_flags &= ~ASY_IGNORE_CD; 273452Sdf157793 mutex_exit(asy->asy_excl_hi); 273552Sdf157793 break; 273652Sdf157793 } 273752Sdf157793 } else if (error < 0) { 273852Sdf157793 /* 273952Sdf157793 * "ttycommon_ioctl" didn't do anything; we process it here. 274052Sdf157793 */ 274152Sdf157793 error = 0; 274252Sdf157793 switch (iocp->ioc_cmd) { 274352Sdf157793 274452Sdf157793 case TIOCGPPS: 274552Sdf157793 /* 274652Sdf157793 * Get PPS on/off. 274752Sdf157793 */ 274852Sdf157793 if (mp->b_cont != NULL) 274952Sdf157793 freemsg(mp->b_cont); 275052Sdf157793 275152Sdf157793 mp->b_cont = allocb(sizeof (int), BPRI_HI); 275252Sdf157793 if (mp->b_cont == NULL) { 275352Sdf157793 error = ENOMEM; 275452Sdf157793 break; 275552Sdf157793 } 275652Sdf157793 if (asy->asy_flags & ASY_PPS) 275752Sdf157793 *(int *)mp->b_cont->b_wptr = 1; 275852Sdf157793 else 275952Sdf157793 *(int *)mp->b_cont->b_wptr = 0; 276052Sdf157793 mp->b_cont->b_wptr += sizeof (int); 276152Sdf157793 mp->b_datap->db_type = M_IOCACK; 276252Sdf157793 iocp->ioc_count = sizeof (int); 276352Sdf157793 break; 276452Sdf157793 276552Sdf157793 case TIOCSPPS: 276652Sdf157793 /* 276752Sdf157793 * Set PPS on/off. 276852Sdf157793 */ 276952Sdf157793 error = miocpullup(mp, sizeof (int)); 277052Sdf157793 if (error != 0) 277152Sdf157793 break; 277252Sdf157793 277352Sdf157793 mutex_enter(asy->asy_excl_hi); 277452Sdf157793 if (*(int *)mp->b_cont->b_rptr) 277552Sdf157793 asy->asy_flags |= ASY_PPS; 277652Sdf157793 else 277752Sdf157793 asy->asy_flags &= ~ASY_PPS; 277852Sdf157793 /* Reset edge sense */ 277952Sdf157793 asy->asy_flags &= ~ASY_PPS_EDGE; 278052Sdf157793 mutex_exit(asy->asy_excl_hi); 278152Sdf157793 mp->b_datap->db_type = M_IOCACK; 278252Sdf157793 break; 278352Sdf157793 278452Sdf157793 case TIOCGPPSEV: { 278552Sdf157793 /* 278652Sdf157793 * Get PPS event data. 278752Sdf157793 */ 278852Sdf157793 mblk_t *bp; 278952Sdf157793 void *buf; 279052Sdf157793 #ifdef _SYSCALL32_IMPL 279152Sdf157793 struct ppsclockev32 p32; 279252Sdf157793 #endif 279352Sdf157793 struct ppsclockev ppsclockev; 279452Sdf157793 279552Sdf157793 if (mp->b_cont != NULL) { 279652Sdf157793 freemsg(mp->b_cont); 279752Sdf157793 mp->b_cont = NULL; 279852Sdf157793 } 279952Sdf157793 280052Sdf157793 if ((asy->asy_flags & ASY_PPS) == 0) { 280152Sdf157793 error = ENXIO; 280252Sdf157793 break; 280352Sdf157793 } 280452Sdf157793 280552Sdf157793 /* Protect from incomplete asy_ppsev */ 280652Sdf157793 mutex_enter(asy->asy_excl_hi); 280752Sdf157793 ppsclockev = asy_ppsev; 280852Sdf157793 mutex_exit(asy->asy_excl_hi); 280952Sdf157793 281052Sdf157793 #ifdef _SYSCALL32_IMPL 281152Sdf157793 if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { 281252Sdf157793 TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); 281352Sdf157793 p32.serial = ppsclockev.serial; 281452Sdf157793 buf = &p32; 281552Sdf157793 iocp->ioc_count = sizeof (struct ppsclockev32); 281652Sdf157793 } else 281752Sdf157793 #endif 281852Sdf157793 { 281952Sdf157793 buf = &ppsclockev; 282052Sdf157793 iocp->ioc_count = sizeof (struct ppsclockev); 282152Sdf157793 } 282252Sdf157793 282352Sdf157793 if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { 282452Sdf157793 error = ENOMEM; 282552Sdf157793 break; 282652Sdf157793 } 282752Sdf157793 mp->b_cont = bp; 282852Sdf157793 282952Sdf157793 bcopy(buf, bp->b_wptr, iocp->ioc_count); 283052Sdf157793 bp->b_wptr += iocp->ioc_count; 283152Sdf157793 mp->b_datap->db_type = M_IOCACK; 283252Sdf157793 break; 283352Sdf157793 } 283452Sdf157793 283552Sdf157793 case TCSBRK: 283652Sdf157793 error = miocpullup(mp, sizeof (int)); 283752Sdf157793 if (error != 0) 283852Sdf157793 break; 283952Sdf157793 284052Sdf157793 mutex_enter(asy->asy_excl_hi); 284152Sdf157793 if (*(int *)mp->b_cont->b_rptr == 0) { 284252Sdf157793 /* 284352Sdf157793 * Get the divisor by calculating the rate 284452Sdf157793 */ 284552Sdf157793 unsigned int rate, divisor; 284652Sdf157793 rate = async->async_ttycommon.t_cflag & CBAUD; 284752Sdf157793 if (async->async_ttycommon.t_cflag & CBAUDEXT) 284852Sdf157793 rate += 16; 284952Sdf157793 if (rate >= N_SU_SPEEDS) rate = B9600; 285052Sdf157793 divisor = asyspdtab[rate] & 0xfff; 285152Sdf157793 285252Sdf157793 /* 285352Sdf157793 * To ensure that erroneous characters are 285452Sdf157793 * not sent out when the break is set, SB 285552Sdf157793 * recommends three steps: 285652Sdf157793 * 285752Sdf157793 * 1) pad the TSR with 0 bits 285852Sdf157793 * 2) When the TSR is full, set break 285952Sdf157793 * 3) When the TSR has been flushed, unset 286052Sdf157793 * the break when transmission must be 286152Sdf157793 * restored. 286252Sdf157793 * 286352Sdf157793 * We loop until the TSR is empty and then 286452Sdf157793 * set the break. ASYNC_BREAK has been set 286552Sdf157793 * to ensure that no characters are 286652Sdf157793 * transmitted while the TSR is being 286752Sdf157793 * flushed and SOUT is being used for the 286852Sdf157793 * break signal. 286952Sdf157793 * 287052Sdf157793 * The wait period is equal to 287152Sdf157793 * clock / (baud * 16) * 16 * 2. 287252Sdf157793 */ 287352Sdf157793 async->async_flags |= ASYNC_BREAK; 287452Sdf157793 while ((INB(LSR) & XSRE) == 0) { 287552Sdf157793 mutex_exit(asy->asy_excl_hi); 287652Sdf157793 mutex_exit(asy->asy_excl); 287752Sdf157793 drv_usecwait(32*divisor); 287852Sdf157793 mutex_enter(asy->asy_excl); 287952Sdf157793 mutex_enter(asy->asy_excl_hi); 288052Sdf157793 } 288152Sdf157793 288252Sdf157793 /* 288352Sdf157793 * Set the break bit, and arrange for 288452Sdf157793 * "async_restart" to be called in 1/4 second; 288552Sdf157793 * it will turn the break bit off, and call 288652Sdf157793 * "async_start" to grab the next message. 288752Sdf157793 */ 288852Sdf157793 val = INB(LCR); 288952Sdf157793 OUTB(LCR, (val | SETBREAK)); 289052Sdf157793 mutex_exit(asy->asy_excl_hi); 289152Sdf157793 (void) timeout(async_restart, async, hz / 4); 289252Sdf157793 } else { 289352Sdf157793 #ifdef DEBUG 289452Sdf157793 if (asydebug & ASY_DEBUG_CLOSE) 289552Sdf157793 printf("asy%d: wait for flush.\n", 289652Sdf157793 UNIT(async->async_dev)); 289752Sdf157793 #endif 289852Sdf157793 if (iswput && asy_isbusy(asy)) { 289952Sdf157793 if (putq(wq, mp) == 0) 290052Sdf157793 freemsg(mp); 290152Sdf157793 mutex_exit(asy->asy_excl_hi); 290252Sdf157793 mutex_exit(asy->asy_excl); 290352Sdf157793 return; 290452Sdf157793 } 290552Sdf157793 mutex_exit(asy->asy_excl_hi); 290652Sdf157793 #ifdef DEBUG 290752Sdf157793 if (asydebug & ASY_DEBUG_CLOSE) 290852Sdf157793 printf("asy%d: ldterm satisfied.\n", 290952Sdf157793 UNIT(async->async_dev)); 291052Sdf157793 #endif 291152Sdf157793 } 291252Sdf157793 break; 291352Sdf157793 291452Sdf157793 case TIOCSBRK: 291552Sdf157793 mutex_enter(asy->asy_excl_hi); 291652Sdf157793 val = INB(LCR); 291752Sdf157793 OUTB(LCR, (val | SETBREAK)); 291852Sdf157793 mutex_exit(asy->asy_excl_hi); 291952Sdf157793 mutex_exit(asy->asy_excl); 292052Sdf157793 miocack(wq, mp, 0, 0); 292152Sdf157793 return; 292252Sdf157793 292352Sdf157793 case TIOCCBRK: 292452Sdf157793 mutex_enter(asy->asy_excl_hi); 292552Sdf157793 val = INB(LCR); 292652Sdf157793 OUTB(LCR, (val & ~SETBREAK)); 292752Sdf157793 mutex_exit(asy->asy_excl_hi); 292852Sdf157793 mutex_exit(asy->asy_excl); 292952Sdf157793 miocack(wq, mp, 0, 0); 293052Sdf157793 return; 293152Sdf157793 293252Sdf157793 case TIOCMSET: 293352Sdf157793 case TIOCMBIS: 293452Sdf157793 case TIOCMBIC: 293552Sdf157793 if (iocp->ioc_count == TRANSPARENT) 293652Sdf157793 mcopyin(mp, NULL, sizeof (int), NULL); 293752Sdf157793 else { 293852Sdf157793 error = miocpullup(mp, sizeof (int)); 293952Sdf157793 if (error != 0) 294052Sdf157793 break; 294152Sdf157793 294252Sdf157793 mutex_enter(asy->asy_excl_hi); 294352Sdf157793 294452Sdf157793 (void) asymctl(asy, 294552Sdf157793 dmtoasy(*(int *)mp->b_cont->b_rptr), 294652Sdf157793 iocp->ioc_cmd); 294752Sdf157793 294852Sdf157793 mutex_exit(asy->asy_excl_hi); 294952Sdf157793 iocp->ioc_error = 0; 295052Sdf157793 mp->b_datap->db_type = M_IOCACK; 295152Sdf157793 } 295252Sdf157793 break; 295352Sdf157793 295452Sdf157793 case TIOCSILOOP: 295552Sdf157793 mutex_enter(asy->asy_excl_hi); 295652Sdf157793 /* 295752Sdf157793 * If somebody misues this Ioctl when used for 295852Sdf157793 * driving keyboard and mouse indicate not supported 295952Sdf157793 */ 296052Sdf157793 if ((asy->asy_device_type == ASY_KEYBOARD) || 296152Sdf157793 (asy->asy_device_type == ASY_MOUSE)) { 296252Sdf157793 mutex_exit(asy->asy_excl_hi); 296352Sdf157793 error = ENOTTY; 296452Sdf157793 break; 296552Sdf157793 } 296652Sdf157793 296752Sdf157793 /* should not use when we're the console */ 296852Sdf157793 if ((async->async_dev == kbddev) || 296952Sdf157793 (async->async_dev == rconsdev) || 297052Sdf157793 (async->async_dev == stdindev)) { 297152Sdf157793 mutex_exit(asy->asy_excl_hi); 297252Sdf157793 error = EINVAL; 297352Sdf157793 break; 297452Sdf157793 } 297552Sdf157793 297652Sdf157793 val = INB(MCR); 297752Sdf157793 icr = INB(ICR); 297852Sdf157793 /* 297952Sdf157793 * Disable the Modem Status Interrupt 298052Sdf157793 * The reason for disabling is the status of 298152Sdf157793 * modem signal are in the higher 4 bits instead of 298252Sdf157793 * lower four bits when in loopback mode, 298352Sdf157793 * so, donot worry about Modem interrupt when 298452Sdf157793 * you are planning to set 298552Sdf157793 * this in loopback mode until it is cleared by 298652Sdf157793 * another ioctl to get out of the loopback mode 298752Sdf157793 */ 298852Sdf157793 OUTB(ICR, icr & ~ MIEN); 298952Sdf157793 OUTB(MCR, val | ASY_LOOP); 299052Sdf157793 mutex_exit(asy->asy_excl_hi); 299152Sdf157793 iocp->ioc_error = 0; 299252Sdf157793 mp->b_datap->db_type = M_IOCACK; 299352Sdf157793 break; 299452Sdf157793 299552Sdf157793 case TIOCMGET: 299652Sdf157793 datamp = allocb(sizeof (int), BPRI_MED); 299752Sdf157793 if (datamp == NULL) { 299852Sdf157793 error = EAGAIN; 299952Sdf157793 break; 300052Sdf157793 } 300152Sdf157793 300252Sdf157793 mutex_enter(asy->asy_excl_hi); 300352Sdf157793 *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); 300452Sdf157793 mutex_exit(asy->asy_excl_hi); 300552Sdf157793 300652Sdf157793 if (iocp->ioc_count == TRANSPARENT) { 300752Sdf157793 mcopyout(mp, NULL, sizeof (int), NULL, datamp); 300852Sdf157793 } else { 300952Sdf157793 if (mp->b_cont != NULL) 301052Sdf157793 freemsg(mp->b_cont); 301152Sdf157793 mp->b_cont = datamp; 301252Sdf157793 mp->b_cont->b_wptr += sizeof (int); 301352Sdf157793 mp->b_datap->db_type = M_IOCACK; 301452Sdf157793 iocp->ioc_count = sizeof (int); 301552Sdf157793 } 301652Sdf157793 break; 301752Sdf157793 301852Sdf157793 default: /* unexpected ioctl type */ 301952Sdf157793 /* 302052Sdf157793 * If we don't understand it, it's an error. NAK it. 302152Sdf157793 */ 302252Sdf157793 error = EINVAL; 302352Sdf157793 break; 302452Sdf157793 } 302552Sdf157793 } 302652Sdf157793 if (error != 0) { 302752Sdf157793 iocp->ioc_error = error; 302852Sdf157793 mp->b_datap->db_type = M_IOCNAK; 302952Sdf157793 } 303052Sdf157793 mutex_exit(asy->asy_excl); 303152Sdf157793 qreply(wq, mp); 303252Sdf157793 } 303352Sdf157793 303452Sdf157793 static void 303552Sdf157793 asyrsrv(queue_t *q) 303652Sdf157793 { 303752Sdf157793 mblk_t *bp; 303852Sdf157793 struct asyncline *async; 303952Sdf157793 304052Sdf157793 async = (struct asyncline *)q->q_ptr; 304152Sdf157793 304252Sdf157793 while (canputnext(q) && (bp = getq(q))) 304352Sdf157793 putnext(q, bp); 304452Sdf157793 ASYSETSOFT(async->async_common); 304552Sdf157793 async->async_polltid = 0; 304652Sdf157793 } 304752Sdf157793 304852Sdf157793 /* 304952Sdf157793 * Put procedure for write queue. 305052Sdf157793 * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; 305152Sdf157793 * set the flow control character for M_STOPI and M_STARTI messages; 305252Sdf157793 * queue up M_BREAK, M_DELAY, and M_DATA messages for processing 305352Sdf157793 * by the start routine, and then call the start routine; discard 305452Sdf157793 * everything else. Note that this driver does not incorporate any 305552Sdf157793 * mechanism to negotiate to handle the canonicalization process. 305652Sdf157793 * It expects that these functions are handled in upper module(s), 305752Sdf157793 * as we do in ldterm. 305852Sdf157793 */ 305952Sdf157793 static void 306052Sdf157793 asywput(queue_t *q, mblk_t *mp) 306152Sdf157793 { 306252Sdf157793 register struct asyncline *async; 306352Sdf157793 register struct asycom *asy; 306452Sdf157793 int error; 306552Sdf157793 306652Sdf157793 async = (struct asyncline *)q->q_ptr; 306752Sdf157793 asy = async->async_common; 306852Sdf157793 306952Sdf157793 switch (mp->b_datap->db_type) { 307052Sdf157793 307152Sdf157793 case M_STOP: 307252Sdf157793 /* 307352Sdf157793 * Since we don't do real DMA, we can just let the 307452Sdf157793 * chip coast to a stop after applying the brakes. 307552Sdf157793 */ 307652Sdf157793 mutex_enter(asy->asy_excl); 307752Sdf157793 async->async_flags |= ASYNC_STOPPED; 307852Sdf157793 mutex_exit(asy->asy_excl); 307952Sdf157793 freemsg(mp); 308052Sdf157793 break; 308152Sdf157793 308252Sdf157793 case M_START: 308352Sdf157793 mutex_enter(asy->asy_excl); 308452Sdf157793 if (async->async_flags & ASYNC_STOPPED) { 308552Sdf157793 async->async_flags &= ~ASYNC_STOPPED; 308652Sdf157793 /* 308752Sdf157793 * If an output operation is in progress, 308852Sdf157793 * resume it. Otherwise, prod the start 308952Sdf157793 * routine. 309052Sdf157793 */ 309152Sdf157793 if (async->async_ocnt > 0) { 309252Sdf157793 mutex_enter(asy->asy_excl_hi); 309352Sdf157793 async_resume(async); 309452Sdf157793 mutex_exit(asy->asy_excl_hi); 309552Sdf157793 } else { 309652Sdf157793 async_start(async); 309752Sdf157793 } 309852Sdf157793 } 309952Sdf157793 mutex_exit(asy->asy_excl); 310052Sdf157793 freemsg(mp); 310152Sdf157793 break; 310252Sdf157793 310352Sdf157793 case M_IOCTL: 310452Sdf157793 switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 310552Sdf157793 310652Sdf157793 case TCSBRK: 310752Sdf157793 error = miocpullup(mp, sizeof (int)); 310852Sdf157793 if (error != 0) { 310952Sdf157793 miocnak(q, mp, 0, error); 311052Sdf157793 return; 311152Sdf157793 } 311252Sdf157793 311352Sdf157793 if (*(int *)mp->b_cont->b_rptr != 0) { 311452Sdf157793 #ifdef DEBUG 311552Sdf157793 if (asydebug & ASY_DEBUG_CLOSE) 311652Sdf157793 printf("asy%d: flush request.\n", 311752Sdf157793 UNIT(async->async_dev)); 311852Sdf157793 #endif 311952Sdf157793 (void) putq(q, mp); 312052Sdf157793 mutex_enter(asy->asy_excl); 312152Sdf157793 async_nstart(async, 1); 312252Sdf157793 mutex_exit(asy->asy_excl); 312352Sdf157793 break; 312452Sdf157793 } 312552Sdf157793 /*FALLTHROUGH*/ 312652Sdf157793 case TCSETSW: 312752Sdf157793 case TCSETSF: 312852Sdf157793 case TCSETAW: 312952Sdf157793 case TCSETAF: 313052Sdf157793 /* 313152Sdf157793 * The changes do not take effect until all 313252Sdf157793 * output queued before them is drained. 313352Sdf157793 * Put this message on the queue, so that 313452Sdf157793 * "async_start" will see it when it's done 313552Sdf157793 * with the output before it. Poke the 313652Sdf157793 * start routine, just in case. 313752Sdf157793 */ 313852Sdf157793 (void) putq(q, mp); 313952Sdf157793 mutex_enter(asy->asy_excl); 314052Sdf157793 async_start(async); 314152Sdf157793 mutex_exit(asy->asy_excl); 314252Sdf157793 break; 314352Sdf157793 314452Sdf157793 default: 314552Sdf157793 /* 314652Sdf157793 * Do it now. 314752Sdf157793 */ 314852Sdf157793 async_ioctl(async, q, mp, B_TRUE); 314952Sdf157793 break; 315052Sdf157793 } 315152Sdf157793 break; 315252Sdf157793 315352Sdf157793 case M_FLUSH: 315452Sdf157793 if (*mp->b_rptr & FLUSHW) { 315552Sdf157793 mutex_enter(asy->asy_excl); 315652Sdf157793 315752Sdf157793 /* 315852Sdf157793 * Abort any output in progress. 315952Sdf157793 */ 316052Sdf157793 mutex_enter(asy->asy_excl_hi); 316152Sdf157793 if (async->async_flags & ASYNC_BUSY) { 316252Sdf157793 async->async_ocnt = 0; 316352Sdf157793 async->async_flags &= ~ASYNC_BUSY; 316452Sdf157793 } 316552Sdf157793 mutex_exit(asy->asy_excl_hi); 316652Sdf157793 316752Sdf157793 /* Flush FIFO buffers */ 316852Sdf157793 if (asy->asy_use_fifo == FIFO_ON) { 316952Sdf157793 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | 317052Sdf157793 (asy->asy_trig_level & 0xff)); 317152Sdf157793 } 317252Sdf157793 317352Sdf157793 /* 317452Sdf157793 * Flush our write queue. 317552Sdf157793 */ 317652Sdf157793 flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ 317752Sdf157793 if (async->async_xmitblk != NULL) { 317852Sdf157793 freeb(async->async_xmitblk); 317952Sdf157793 async->async_xmitblk = NULL; 318052Sdf157793 } 318152Sdf157793 318252Sdf157793 mutex_exit(asy->asy_excl); 318352Sdf157793 *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ 318452Sdf157793 } 318552Sdf157793 if (*mp->b_rptr & FLUSHR) { 318652Sdf157793 /* Flush FIFO buffers */ 318752Sdf157793 if (asy->asy_use_fifo == FIFO_ON) { 318852Sdf157793 OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH | 318952Sdf157793 (asy->asy_trig_level & 0xff)); 319052Sdf157793 } 319152Sdf157793 flushq(RD(q), FLUSHDATA); 319252Sdf157793 qreply(q, mp); /* give the read queues a crack at it */ 319352Sdf157793 } else { 319452Sdf157793 freemsg(mp); 319552Sdf157793 } 319652Sdf157793 319752Sdf157793 /* 319852Sdf157793 * We must make sure we process messages that survive the 319952Sdf157793 * write-side flush. Without this call, the close protocol 320052Sdf157793 * with ldterm can hang forever. (ldterm will have sent us a 320152Sdf157793 * TCSBRK ioctl that it expects a response to.) 320252Sdf157793 */ 320352Sdf157793 mutex_enter(asy->asy_excl); 320452Sdf157793 async_start(async); 320552Sdf157793 mutex_exit(asy->asy_excl); 320652Sdf157793 break; 320752Sdf157793 case M_BREAK: 320852Sdf157793 case M_DELAY: 320952Sdf157793 case M_DATA: 321052Sdf157793 /* 321152Sdf157793 * Queue the message up to be transmitted, 321252Sdf157793 * and poke the start routine. 321352Sdf157793 */ 321452Sdf157793 (void) putq(q, mp); 321552Sdf157793 mutex_enter(asy->asy_excl); 321652Sdf157793 async_start(async); 321752Sdf157793 mutex_exit(asy->asy_excl); 321852Sdf157793 break; 321952Sdf157793 322052Sdf157793 case M_STOPI: 322152Sdf157793 mutex_enter(asy->asy_excl); 322252Sdf157793 async->async_flowc = async->async_stopc; 322352Sdf157793 async_start(async); /* poke the start routine */ 322452Sdf157793 mutex_exit(asy->asy_excl); 322552Sdf157793 freemsg(mp); 322652Sdf157793 break; 322752Sdf157793 322852Sdf157793 case M_STARTI: 322952Sdf157793 mutex_enter(asy->asy_excl); 323052Sdf157793 async->async_flowc = async->async_startc; 323152Sdf157793 async_start(async); /* poke the start routine */ 323252Sdf157793 mutex_exit(asy->asy_excl); 323352Sdf157793 freemsg(mp); 323452Sdf157793 break; 323552Sdf157793 323652Sdf157793 case M_CTL: 323752Sdf157793 if (MBLKL(mp) >= sizeof (struct iocblk) && 323852Sdf157793 ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { 323952Sdf157793 ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX; 324052Sdf157793 qreply(q, mp); 324152Sdf157793 } else { 324252Sdf157793 /* 324352Sdf157793 * These MC_SERVICE type messages are used by upper 324452Sdf157793 * modules to tell this driver to send input up 324552Sdf157793 * immediately, or that it can wait for normal 324652Sdf157793 * processing that may or may not be done. Sun 324752Sdf157793 * requires these for the mouse module. 324852Sdf157793 * (XXX - for x86?) 324952Sdf157793 */ 325052Sdf157793 mutex_enter(asy->asy_excl); 325152Sdf157793 switch (*mp->b_rptr) { 325252Sdf157793 325352Sdf157793 case MC_SERVICEIMM: 325452Sdf157793 async->async_flags |= ASYNC_SERVICEIMM; 325552Sdf157793 break; 325652Sdf157793 325752Sdf157793 case MC_SERVICEDEF: 325852Sdf157793 async->async_flags &= ~ASYNC_SERVICEIMM; 325952Sdf157793 break; 326052Sdf157793 } 326152Sdf157793 mutex_exit(asy->asy_excl); 326252Sdf157793 freemsg(mp); 326352Sdf157793 } 326452Sdf157793 break; 326552Sdf157793 326652Sdf157793 case M_IOCDATA: 326752Sdf157793 async_iocdata(q, mp); 326852Sdf157793 break; 326952Sdf157793 327052Sdf157793 default: 327152Sdf157793 freemsg(mp); 327252Sdf157793 break; 327352Sdf157793 } 327452Sdf157793 } 327552Sdf157793 327652Sdf157793 /* 327752Sdf157793 * Retry an "ioctl", now that "bufcall" claims we may be able to allocate 327852Sdf157793 * the buffer we need. 327952Sdf157793 */ 328052Sdf157793 static void 328152Sdf157793 async_reioctl(void *arg) 328252Sdf157793 { 328352Sdf157793 struct asyncline *async = arg; 328452Sdf157793 struct asycom *asy = async->async_common; 328552Sdf157793 queue_t *q; 328652Sdf157793 mblk_t *mp; 328752Sdf157793 328852Sdf157793 /* 328952Sdf157793 * The bufcall is no longer pending. 329052Sdf157793 */ 329152Sdf157793 mutex_enter(asy->asy_excl); 329252Sdf157793 async->async_wbufcid = 0; 329352Sdf157793 if ((q = async->async_ttycommon.t_writeq) == NULL) { 329452Sdf157793 mutex_exit(asy->asy_excl); 329552Sdf157793 return; 329652Sdf157793 } 329752Sdf157793 if ((mp = async->async_ttycommon.t_iocpending) != NULL) { 329852Sdf157793 /* not pending any more */ 329952Sdf157793 async->async_ttycommon.t_iocpending = NULL; 330052Sdf157793 mutex_exit(asy->asy_excl); 330152Sdf157793 /* not in STREAMS queue; we no longer know if we're in wput */ 330252Sdf157793 async_ioctl(async, q, mp, B_TRUE); 330352Sdf157793 } else 330452Sdf157793 mutex_exit(asy->asy_excl); 330552Sdf157793 } 330652Sdf157793 330752Sdf157793 static void 330852Sdf157793 async_iocdata(queue_t *q, mblk_t *mp) 330952Sdf157793 { 331052Sdf157793 struct asyncline *async = (struct asyncline *)q->q_ptr; 331152Sdf157793 struct asycom *asy; 331252Sdf157793 struct copyresp *csp; 331352Sdf157793 331452Sdf157793 asy = async->async_common; 331552Sdf157793 csp = (struct copyresp *)mp->b_rptr; 331652Sdf157793 331752Sdf157793 if (csp->cp_rval != 0) { 331852Sdf157793 freemsg(mp); 331952Sdf157793 return; 332052Sdf157793 } 332152Sdf157793 332252Sdf157793 mutex_enter(asy->asy_excl); 332352Sdf157793 332452Sdf157793 switch (csp->cp_cmd) { 332552Sdf157793 case TIOCMSET: 332652Sdf157793 case TIOCMBIS: 332752Sdf157793 case TIOCMBIC: 332852Sdf157793 if (mp->b_cont == NULL) { 332952Sdf157793 mutex_exit(asy->asy_excl); 333052Sdf157793 miocnak(q, mp, 0, EINVAL); 333152Sdf157793 break; 333252Sdf157793 } 333352Sdf157793 333452Sdf157793 mutex_enter(asy->asy_excl_hi); 333552Sdf157793 (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr), 333652Sdf157793 csp->cp_cmd); 333752Sdf157793 mutex_exit(asy->asy_excl_hi); 333852Sdf157793 333952Sdf157793 freemsg(mp->b_cont); 334052Sdf157793 mp->b_cont = NULL; 334152Sdf157793 mutex_exit(asy->asy_excl); 334252Sdf157793 miocack(q, mp, 0, 0); 334352Sdf157793 break; 334452Sdf157793 334552Sdf157793 case TIOCMGET: 334652Sdf157793 if (mp->b_cont != NULL) { 334752Sdf157793 freemsg(mp->b_cont); 334852Sdf157793 mp->b_cont = NULL; 334952Sdf157793 } 335052Sdf157793 mutex_exit(asy->asy_excl); 335152Sdf157793 miocack(q, mp, 0, 0); 335252Sdf157793 break; 335352Sdf157793 335452Sdf157793 default: 335552Sdf157793 mutex_exit(asy->asy_excl); 335652Sdf157793 miocnak(q, mp, 0, EINVAL); 335752Sdf157793 break; 335852Sdf157793 } 335952Sdf157793 } 336052Sdf157793 336152Sdf157793 336252Sdf157793 /* 336352Sdf157793 * Set or get the modem control status. 336452Sdf157793 */ 336552Sdf157793 static int 336652Sdf157793 asymctl(struct asycom *asy, int bits, int how) 336752Sdf157793 { 336852Sdf157793 register int mcr_r, msr_r; 336952Sdf157793 337052Sdf157793 ASSERT(mutex_owned(asy->asy_excl_hi)); 337152Sdf157793 ASSERT(mutex_owned(asy->asy_excl)); 337252Sdf157793 337352Sdf157793 /* Read Modem Control Registers */ 337452Sdf157793 mcr_r = INB(MCR); 337552Sdf157793 337652Sdf157793 switch (how) { 337752Sdf157793 337852Sdf157793 case TIOCMSET: 337952Sdf157793 mcr_r = bits; 338052Sdf157793 break; 338152Sdf157793 338252Sdf157793 case TIOCMBIS: 338352Sdf157793 mcr_r |= bits; /* Set bits from input */ 338452Sdf157793 break; 338552Sdf157793 338652Sdf157793 case TIOCMBIC: 338752Sdf157793 mcr_r &= ~bits; /* Set ~bits from input */ 338852Sdf157793 break; 338952Sdf157793 339052Sdf157793 case TIOCMGET: 339152Sdf157793 /* Read Modem Status Registers */ 339252Sdf157793 if (INB(ICR) & MIEN) 339352Sdf157793 msr_r = asy->asy_cached_msr; 339452Sdf157793 else 339552Sdf157793 msr_r = INB(MSR); 339652Sdf157793 return (asytodm(mcr_r, msr_r)); 339752Sdf157793 } 339852Sdf157793 339952Sdf157793 OUTB(MCR, mcr_r); 340052Sdf157793 340152Sdf157793 return (mcr_r); 340252Sdf157793 } 340352Sdf157793 340452Sdf157793 static int 340552Sdf157793 asytodm(int mcr_r, int msr_r) 340652Sdf157793 { 340752Sdf157793 register int b = 0; 340852Sdf157793 340952Sdf157793 341052Sdf157793 /* MCR registers */ 341152Sdf157793 if (mcr_r & RTS) 341252Sdf157793 b |= TIOCM_RTS; 341352Sdf157793 341452Sdf157793 if (mcr_r & DTR) 341552Sdf157793 b |= TIOCM_DTR; 341652Sdf157793 341752Sdf157793 /* MSR registers */ 341852Sdf157793 if (msr_r & DCD) 341952Sdf157793 b |= TIOCM_CAR; 342052Sdf157793 342152Sdf157793 if (msr_r & CTS) 342252Sdf157793 b |= TIOCM_CTS; 342352Sdf157793 342452Sdf157793 if (msr_r & DSR) 342552Sdf157793 b |= TIOCM_DSR; 342652Sdf157793 342752Sdf157793 if (msr_r & RI) 342852Sdf157793 b |= TIOCM_RNG; 342952Sdf157793 343052Sdf157793 return (b); 343152Sdf157793 } 343252Sdf157793 343352Sdf157793 static int 343452Sdf157793 dmtoasy(int bits) 343552Sdf157793 { 343652Sdf157793 register int b = 0; 343752Sdf157793 343852Sdf157793 #ifdef CAN_NOT_SET /* only DTR and RTS can be set */ 343952Sdf157793 if (bits & TIOCM_CAR) 344052Sdf157793 b |= DCD; 344152Sdf157793 if (bits & TIOCM_CTS) 344252Sdf157793 b |= CTS; 344352Sdf157793 if (bits & TIOCM_DSR) 344452Sdf157793 b |= DSR; 344552Sdf157793 if (bits & TIOCM_RNG) 344652Sdf157793 b |= RI; 344752Sdf157793 #endif 344852Sdf157793 344952Sdf157793 if (bits & TIOCM_RTS) 345052Sdf157793 b |= RTS; 345152Sdf157793 if (bits & TIOCM_DTR) 345252Sdf157793 b |= DTR; 345352Sdf157793 345452Sdf157793 return (b); 345552Sdf157793 } 345652Sdf157793 345752Sdf157793 static void 345852Sdf157793 asycheckflowcontrol_hw(struct asycom *asy) 345952Sdf157793 { 346052Sdf157793 struct asyncline *async; 346152Sdf157793 uchar_t mcr, flag; 346252Sdf157793 346352Sdf157793 ASSERT(mutex_owned(asy->asy_excl_hi)); 346452Sdf157793 346552Sdf157793 async = (struct asyncline *)asy->asy_priv; 346652Sdf157793 ASSERT(async != NULL); 346752Sdf157793 346852Sdf157793 if (async->async_ttycommon.t_cflag & CRTSXOFF) { 346952Sdf157793 mcr = INB(MCR); 347052Sdf157793 flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS; 347152Sdf157793 if (((mcr ^ flag) & RTS) != 0) { 347252Sdf157793 OUTB(MCR, (mcr ^ RTS)); 347352Sdf157793 } 347452Sdf157793 } 347552Sdf157793 } 347652Sdf157793 347752Sdf157793 static boolean_t 347852Sdf157793 asycheckflowcontrol_sw(struct asycom *asy) 347952Sdf157793 { 348052Sdf157793 uchar_t ss; 348152Sdf157793 struct asyncline *async; 348252Sdf157793 int rval = B_FALSE; 348352Sdf157793 348452Sdf157793 ASSERT(mutex_owned(asy->asy_excl_hi)); 348552Sdf157793 348652Sdf157793 async = (struct asyncline *)asy->asy_priv; 348752Sdf157793 ASSERT(async != NULL); 348852Sdf157793 348952Sdf157793 if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) { 349052Sdf157793 /* 349152Sdf157793 * If we get this far, then we know that flowc is non-zero and 349252Sdf157793 * that there's transmit room available. We've "handled" the 349352Sdf157793 * request now, so clear it. If the user didn't ask for IXOFF, 349452Sdf157793 * then don't actually send anything, but wait for the next 349552Sdf157793 * opportunity. 349652Sdf157793 */ 349752Sdf157793 async->async_flowc = '\0'; 349852Sdf157793 if (async->async_ttycommon.t_iflag & IXOFF) { 349952Sdf157793 async->async_flags |= ASYNC_BUSY; 350052Sdf157793 OUTB(DAT, ss); 350152Sdf157793 rval = B_TRUE; 350252Sdf157793 } 350352Sdf157793 } 350452Sdf157793 350552Sdf157793 return (rval); 350652Sdf157793 } 350752Sdf157793 350852Sdf157793 /* 350952Sdf157793 * Check for abort character sequence 351052Sdf157793 */ 351152Sdf157793 static boolean_t 351252Sdf157793 abort_charseq_recognize(uchar_t ch) 351352Sdf157793 { 351452Sdf157793 static int state = 0; 351552Sdf157793 #define CNTRL(c) ((c)&037) 351652Sdf157793 static char sequence[] = { '\r', '~', CNTRL('b') }; 351752Sdf157793 351852Sdf157793 if (ch == sequence[state]) { 351952Sdf157793 if (++state >= sizeof (sequence)) { 352052Sdf157793 state = 0; 352152Sdf157793 return (B_TRUE); 352252Sdf157793 } 352352Sdf157793 } else { 352452Sdf157793 state = (ch == sequence[0]) ? 1 : 0; 352552Sdf157793 } 352652Sdf157793 return (B_FALSE); 352752Sdf157793 } 3528