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