17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
57cb42c7eSrameshc * Common Development and Distribution License (the "License").
67cb42c7eSrameshc * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
227c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
237c478bd9Sstevel@tonic-gate /* All Rights Reserved */
247c478bd9Sstevel@tonic-gate
257c478bd9Sstevel@tonic-gate /*
264d0b1b0dSAn Bui * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
277c478bd9Sstevel@tonic-gate * Use is subject to license terms.
2848bbca81SDaniel Hoffman * Copyright (c) 2016 by Delphix. All rights reserved.
29730a650aSPeter Tribble * Copyright (c) 2019 Peter Tribble.
307c478bd9Sstevel@tonic-gate */
317c478bd9Sstevel@tonic-gate
327c478bd9Sstevel@tonic-gate
337c478bd9Sstevel@tonic-gate /*
34f63f7506Sanovick * Serial I/O driver for 82510/8250/16450/16550AF/16C554D chips.
357c478bd9Sstevel@tonic-gate * Modified as sparc keyboard/mouse driver.
367c478bd9Sstevel@tonic-gate */
377c478bd9Sstevel@tonic-gate #define SU_REGISTER_FILE_NO 0
387c478bd9Sstevel@tonic-gate #define SU_REGOFFSET 0
397c478bd9Sstevel@tonic-gate #define SU_REGISTER_LEN 8
407c478bd9Sstevel@tonic-gate
417c478bd9Sstevel@tonic-gate #include <sys/param.h>
427c478bd9Sstevel@tonic-gate #include <sys/types.h>
437c478bd9Sstevel@tonic-gate #include <sys/signal.h>
447c478bd9Sstevel@tonic-gate #include <sys/stream.h>
457c478bd9Sstevel@tonic-gate #include <sys/termio.h>
467c478bd9Sstevel@tonic-gate #include <sys/errno.h>
477c478bd9Sstevel@tonic-gate #include <sys/file.h>
487c478bd9Sstevel@tonic-gate #include <sys/cmn_err.h>
497c478bd9Sstevel@tonic-gate #include <sys/stropts.h>
507c478bd9Sstevel@tonic-gate #include <sys/strsubr.h>
517c478bd9Sstevel@tonic-gate #include <sys/strsun.h>
527c478bd9Sstevel@tonic-gate #include <sys/strtty.h>
537c478bd9Sstevel@tonic-gate #include <sys/debug.h>
547c478bd9Sstevel@tonic-gate #include <sys/kbio.h>
557c478bd9Sstevel@tonic-gate #include <sys/cred.h>
567c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
577c478bd9Sstevel@tonic-gate #include <sys/stat.h>
587c478bd9Sstevel@tonic-gate #include <sys/consdev.h>
597c478bd9Sstevel@tonic-gate #include <sys/mkdev.h>
607c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
617c478bd9Sstevel@tonic-gate #include <sys/cred.h>
627c478bd9Sstevel@tonic-gate #ifdef DEBUG
637c478bd9Sstevel@tonic-gate #include <sys/promif.h>
647c478bd9Sstevel@tonic-gate #endif
657c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
667c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
677c478bd9Sstevel@tonic-gate #include <sys/sudev.h>
687c478bd9Sstevel@tonic-gate #include <sys/note.h>
697c478bd9Sstevel@tonic-gate #include <sys/timex.h>
707c478bd9Sstevel@tonic-gate #include <sys/policy.h>
717c478bd9Sstevel@tonic-gate
727c478bd9Sstevel@tonic-gate #define async_stopc async_ttycommon.t_stopc
737c478bd9Sstevel@tonic-gate #define async_startc async_ttycommon.t_startc
747c478bd9Sstevel@tonic-gate
757c478bd9Sstevel@tonic-gate #define ASY_INIT 1
767c478bd9Sstevel@tonic-gate #define ASY_NOINIT 0
777c478bd9Sstevel@tonic-gate
787c478bd9Sstevel@tonic-gate #ifdef DEBUG
797c478bd9Sstevel@tonic-gate #define ASY_DEBUG_INIT 0x001
807c478bd9Sstevel@tonic-gate #define ASY_DEBUG_INPUT 0x002
817c478bd9Sstevel@tonic-gate #define ASY_DEBUG_EOT 0x004
827c478bd9Sstevel@tonic-gate #define ASY_DEBUG_CLOSE 0x008
837c478bd9Sstevel@tonic-gate #define ASY_DEBUG_HFLOW 0x010
847c478bd9Sstevel@tonic-gate #define ASY_DEBUG_PROCS 0x020
857c478bd9Sstevel@tonic-gate #define ASY_DEBUG_STATE 0x040
867c478bd9Sstevel@tonic-gate #define ASY_DEBUG_INTR 0x080
877c478bd9Sstevel@tonic-gate static int asydebug = 0;
887c478bd9Sstevel@tonic-gate #endif
897c478bd9Sstevel@tonic-gate static int su_log = 0;
907c478bd9Sstevel@tonic-gate
917c478bd9Sstevel@tonic-gate int su_drain_check = 15000000; /* tunable: exit drain check time */
927c478bd9Sstevel@tonic-gate
937c478bd9Sstevel@tonic-gate static struct ppsclockev asy_ppsev;
947c478bd9Sstevel@tonic-gate
957c478bd9Sstevel@tonic-gate static int max_asy_instance = -1;
967c478bd9Sstevel@tonic-gate static void *su_asycom; /* soft state asycom pointer */
977c478bd9Sstevel@tonic-gate static void *su_asyncline; /* soft state asyncline pointer */
987c478bd9Sstevel@tonic-gate static boolean_t abort_charseq_recognize(uchar_t ch);
997c478bd9Sstevel@tonic-gate
1007c478bd9Sstevel@tonic-gate static uint_t asysoftintr(caddr_t intarg);
1017c478bd9Sstevel@tonic-gate static uint_t asyintr(caddr_t argasy);
1027c478bd9Sstevel@tonic-gate
1037c478bd9Sstevel@tonic-gate /* The async interrupt entry points */
1047c478bd9Sstevel@tonic-gate static void async_txint(struct asycom *asy, uchar_t lsr);
1057c478bd9Sstevel@tonic-gate static void async_rxint(struct asycom *asy, uchar_t lsr);
1067c478bd9Sstevel@tonic-gate static void async_msint(struct asycom *asy);
1077c478bd9Sstevel@tonic-gate static int async_softint(struct asycom *asy);
1087c478bd9Sstevel@tonic-gate
1097c478bd9Sstevel@tonic-gate static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp,
1107c478bd9Sstevel@tonic-gate boolean_t iswput);
1117c478bd9Sstevel@tonic-gate static void async_reioctl(void *);
1127c478bd9Sstevel@tonic-gate static void async_iocdata(queue_t *q, mblk_t *mp);
1137c478bd9Sstevel@tonic-gate static void async_restart(void *);
1147c478bd9Sstevel@tonic-gate static void async_start(struct asyncline *async);
1157c478bd9Sstevel@tonic-gate static void async_nstart(struct asyncline *async, int mode);
1167c478bd9Sstevel@tonic-gate static void async_resume(struct asyncline *async);
1177c478bd9Sstevel@tonic-gate static int asy_program(struct asycom *asy, int mode);
1187c478bd9Sstevel@tonic-gate
1190280efdcSzk /* Polled mode functions */
1200280efdcSzk static void asyputchar(cons_polledio_arg_t, uchar_t c);
1210280efdcSzk static int asygetchar(cons_polledio_arg_t);
1220280efdcSzk static boolean_t asyischar(cons_polledio_arg_t);
1230280efdcSzk static void asy_polled_enter(cons_polledio_arg_t);
1240280efdcSzk static void asy_polled_exit(cons_polledio_arg_t);
1250280efdcSzk
1267c478bd9Sstevel@tonic-gate static int asymctl(struct asycom *, int, int);
1277c478bd9Sstevel@tonic-gate static int asytodm(int, int);
1287c478bd9Sstevel@tonic-gate static int dmtoasy(int);
1297c478bd9Sstevel@tonic-gate static void asycheckflowcontrol_hw(struct asycom *asy);
1307c478bd9Sstevel@tonic-gate static boolean_t asycheckflowcontrol_sw(struct asycom *asy);
1317c478bd9Sstevel@tonic-gate static void asy_ppsevent(struct asycom *asy, int msr);
1327c478bd9Sstevel@tonic-gate
1337c478bd9Sstevel@tonic-gate extern kcondvar_t lbolt_cv;
1347c478bd9Sstevel@tonic-gate extern int ddi_create_internal_pathname(dev_info_t *dip, char *name,
1357c478bd9Sstevel@tonic-gate int spec_type, minor_t minor_num);
1367c478bd9Sstevel@tonic-gate
1377c478bd9Sstevel@tonic-gate
1387c478bd9Sstevel@tonic-gate /*
1397c478bd9Sstevel@tonic-gate * Baud rate table. Indexed by #defines found in sys/termios.h
1407c478bd9Sstevel@tonic-gate */
1417c478bd9Sstevel@tonic-gate ushort_t asyspdtab[] = {
1427c478bd9Sstevel@tonic-gate 0, /* 0 baud rate */
1437c478bd9Sstevel@tonic-gate 0x900, /* 50 baud rate */
1447c478bd9Sstevel@tonic-gate 0x600, /* 75 baud rate */
1457c478bd9Sstevel@tonic-gate 0x417, /* 110 baud rate (%0.026) */
1467c478bd9Sstevel@tonic-gate 0x359, /* 134 baud rate (%0.058) */
1477c478bd9Sstevel@tonic-gate 0x300, /* 150 baud rate */
1487c478bd9Sstevel@tonic-gate 0x240, /* 200 baud rate */
1497c478bd9Sstevel@tonic-gate 0x180, /* 300 baud rate */
1507c478bd9Sstevel@tonic-gate 0x0c0, /* 600 baud rate */
1517c478bd9Sstevel@tonic-gate 0x060, /* 1200 baud rate */
1527c478bd9Sstevel@tonic-gate 0x040, /* 1800 baud rate */
1537c478bd9Sstevel@tonic-gate 0x030, /* 2400 baud rate */
1547c478bd9Sstevel@tonic-gate 0x018, /* 4800 baud rate */
1557c478bd9Sstevel@tonic-gate 0x00c, /* 9600 baud rate */
1567c478bd9Sstevel@tonic-gate 0x006, /* 19200 baud rate */
1577c478bd9Sstevel@tonic-gate 0x003, /* 38400 baud rate */
1587c478bd9Sstevel@tonic-gate 0x002, /* 57600 baud rate */
1597c478bd9Sstevel@tonic-gate 0, /* 76800 baud rate - not supported */
1607c478bd9Sstevel@tonic-gate 0x001, /* 115200 baud rate */
1617c478bd9Sstevel@tonic-gate 0, /* 153600 baud rate - not supported */
1627c478bd9Sstevel@tonic-gate 0x8002, /* 230400 baud rate - supported on specific platforms */
1637c478bd9Sstevel@tonic-gate 0, /* 307200 baud rate - not supported */
1647c478bd9Sstevel@tonic-gate 0x8001 /* 460800 baud rate - supported on specific platforms */
1657c478bd9Sstevel@tonic-gate };
1667c478bd9Sstevel@tonic-gate
1677c478bd9Sstevel@tonic-gate /*
1687c478bd9Sstevel@tonic-gate * Number of speeds supported is the number of entries in
1697c478bd9Sstevel@tonic-gate * the above table.
1707c478bd9Sstevel@tonic-gate */
1717c478bd9Sstevel@tonic-gate #define N_SU_SPEEDS (sizeof (asyspdtab)/sizeof (ushort_t))
1727c478bd9Sstevel@tonic-gate
1737c478bd9Sstevel@tonic-gate /*
1747c478bd9Sstevel@tonic-gate * Human-readable baud rate table.
1757c478bd9Sstevel@tonic-gate * Indexed by #defines found in sys/termios.h
1767c478bd9Sstevel@tonic-gate */
1777c478bd9Sstevel@tonic-gate int baudtable[] = {
1787c478bd9Sstevel@tonic-gate 0, /* 0 baud rate */
1797c478bd9Sstevel@tonic-gate 50, /* 50 baud rate */
1807c478bd9Sstevel@tonic-gate 75, /* 75 baud rate */
1817c478bd9Sstevel@tonic-gate 110, /* 110 baud rate */
1827c478bd9Sstevel@tonic-gate 134, /* 134 baud rate */
1837c478bd9Sstevel@tonic-gate 150, /* 150 baud rate */
1847c478bd9Sstevel@tonic-gate 200, /* 200 baud rate */
1857c478bd9Sstevel@tonic-gate 300, /* 300 baud rate */
1867c478bd9Sstevel@tonic-gate 600, /* 600 baud rate */
1877c478bd9Sstevel@tonic-gate 1200, /* 1200 baud rate */
1887c478bd9Sstevel@tonic-gate 1800, /* 1800 baud rate */
1897c478bd9Sstevel@tonic-gate 2400, /* 2400 baud rate */
1907c478bd9Sstevel@tonic-gate 4800, /* 4800 baud rate */
1917c478bd9Sstevel@tonic-gate 9600, /* 9600 baud rate */
1927c478bd9Sstevel@tonic-gate 19200, /* 19200 baud rate */
1937c478bd9Sstevel@tonic-gate 38400, /* 38400 baud rate */
1947c478bd9Sstevel@tonic-gate 57600, /* 57600 baud rate */
1957c478bd9Sstevel@tonic-gate 76800, /* 76800 baud rate */
1967c478bd9Sstevel@tonic-gate 115200, /* 115200 baud rate */
1977c478bd9Sstevel@tonic-gate 153600, /* 153600 baud rate */
1987c478bd9Sstevel@tonic-gate 230400, /* 230400 baud rate */
1997c478bd9Sstevel@tonic-gate 307200, /* 307200 baud rate */
2007c478bd9Sstevel@tonic-gate 460800 /* 460800 baud rate */
2017c478bd9Sstevel@tonic-gate };
2027c478bd9Sstevel@tonic-gate
2037c478bd9Sstevel@tonic-gate static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
204730a650aSPeter Tribble static int asyclose(queue_t *q, int flag, cred_t *cr);
205*e476cc14SToomas Soome static int asywput(queue_t *q, mblk_t *mp);
206*e476cc14SToomas Soome static int asyrsrv(queue_t *q);
2077c478bd9Sstevel@tonic-gate
2087c478bd9Sstevel@tonic-gate struct module_info asy_info = {
2097c478bd9Sstevel@tonic-gate 0,
2107c478bd9Sstevel@tonic-gate "su",
2117c478bd9Sstevel@tonic-gate 0,
2127c478bd9Sstevel@tonic-gate INFPSZ,
2137c478bd9Sstevel@tonic-gate 32*4096,
2147c478bd9Sstevel@tonic-gate 4096
2157c478bd9Sstevel@tonic-gate };
2167c478bd9Sstevel@tonic-gate
2177c478bd9Sstevel@tonic-gate static struct qinit asy_rint = {
2187c478bd9Sstevel@tonic-gate putq,
219*e476cc14SToomas Soome asyrsrv,
2207c478bd9Sstevel@tonic-gate asyopen,
2217c478bd9Sstevel@tonic-gate asyclose,
2227c478bd9Sstevel@tonic-gate NULL,
2237c478bd9Sstevel@tonic-gate &asy_info,
2247c478bd9Sstevel@tonic-gate NULL
2257c478bd9Sstevel@tonic-gate };
2267c478bd9Sstevel@tonic-gate
2277c478bd9Sstevel@tonic-gate static struct qinit asy_wint = {
228*e476cc14SToomas Soome asywput,
2297c478bd9Sstevel@tonic-gate NULL,
2307c478bd9Sstevel@tonic-gate NULL,
2317c478bd9Sstevel@tonic-gate NULL,
2327c478bd9Sstevel@tonic-gate NULL,
2337c478bd9Sstevel@tonic-gate &asy_info,
2347c478bd9Sstevel@tonic-gate NULL
2357c478bd9Sstevel@tonic-gate };
2367c478bd9Sstevel@tonic-gate
2377c478bd9Sstevel@tonic-gate struct streamtab asy_str_info = {
2387c478bd9Sstevel@tonic-gate &asy_rint,
2397c478bd9Sstevel@tonic-gate &asy_wint,
2407c478bd9Sstevel@tonic-gate NULL,
2417c478bd9Sstevel@tonic-gate NULL
2427c478bd9Sstevel@tonic-gate };
2437c478bd9Sstevel@tonic-gate
2447c478bd9Sstevel@tonic-gate static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
2457c478bd9Sstevel@tonic-gate void **result);
2467c478bd9Sstevel@tonic-gate static int asyprobe(dev_info_t *);
2477c478bd9Sstevel@tonic-gate static int asyattach(dev_info_t *, ddi_attach_cmd_t);
2487c478bd9Sstevel@tonic-gate static int asydetach(dev_info_t *, ddi_detach_cmd_t);
2497c478bd9Sstevel@tonic-gate
250574493ffSToomas Soome static struct cb_ops cb_asy_ops = {
2517c478bd9Sstevel@tonic-gate nodev, /* cb_open */
2527c478bd9Sstevel@tonic-gate nodev, /* cb_close */
2537c478bd9Sstevel@tonic-gate nodev, /* cb_strategy */
2547c478bd9Sstevel@tonic-gate nodev, /* cb_print */
2557c478bd9Sstevel@tonic-gate nodev, /* cb_dump */
2567c478bd9Sstevel@tonic-gate nodev, /* cb_read */
2577c478bd9Sstevel@tonic-gate nodev, /* cb_write */
2587c478bd9Sstevel@tonic-gate nodev, /* cb_ioctl */
2597c478bd9Sstevel@tonic-gate nodev, /* cb_devmap */
2607c478bd9Sstevel@tonic-gate nodev, /* cb_mmap */
2617c478bd9Sstevel@tonic-gate nodev, /* cb_segmap */
2627c478bd9Sstevel@tonic-gate nochpoll, /* cb_chpoll */
2637c478bd9Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */
2647c478bd9Sstevel@tonic-gate &asy_str_info, /* cb_stream */
2657c478bd9Sstevel@tonic-gate D_MP /* cb_flag */
2667c478bd9Sstevel@tonic-gate };
2677c478bd9Sstevel@tonic-gate
2687c478bd9Sstevel@tonic-gate struct dev_ops asy_ops = {
2697c478bd9Sstevel@tonic-gate DEVO_REV, /* devo_rev */
2707c478bd9Sstevel@tonic-gate 0, /* devo_refcnt */
2717c478bd9Sstevel@tonic-gate asyinfo, /* devo_getinfo */
2727c478bd9Sstevel@tonic-gate nulldev, /* devo_identify */
2737c478bd9Sstevel@tonic-gate asyprobe, /* devo_probe */
2747c478bd9Sstevel@tonic-gate asyattach, /* devo_attach */
2757c478bd9Sstevel@tonic-gate asydetach, /* devo_detach */
2767c478bd9Sstevel@tonic-gate nodev, /* devo_reset */
2777c478bd9Sstevel@tonic-gate &cb_asy_ops, /* devo_cb_ops */
27819397407SSherry Moore NULL, /* devo_bus_ops */
27919397407SSherry Moore NULL, /* devo_power */
28019397407SSherry Moore ddi_quiesce_not_supported, /* devo_quiesce */
2817c478bd9Sstevel@tonic-gate };
2827c478bd9Sstevel@tonic-gate
2837c478bd9Sstevel@tonic-gate /*
2847c478bd9Sstevel@tonic-gate * Module linkage information for the kernel.
2857c478bd9Sstevel@tonic-gate */
2867c478bd9Sstevel@tonic-gate
2877c478bd9Sstevel@tonic-gate static struct modldrv modldrv = {
2887c478bd9Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */
28919397407SSherry Moore "su driver",
2907c478bd9Sstevel@tonic-gate &asy_ops, /* driver ops */
2917c478bd9Sstevel@tonic-gate };
2927c478bd9Sstevel@tonic-gate
2937c478bd9Sstevel@tonic-gate static struct modlinkage modlinkage = {
2947c478bd9Sstevel@tonic-gate MODREV_1,
2957c478bd9Sstevel@tonic-gate &modldrv,
2967c478bd9Sstevel@tonic-gate NULL
2977c478bd9Sstevel@tonic-gate };
2987c478bd9Sstevel@tonic-gate
2997c478bd9Sstevel@tonic-gate int
_init(void)3007c478bd9Sstevel@tonic-gate _init(void)
3017c478bd9Sstevel@tonic-gate {
3027c478bd9Sstevel@tonic-gate int status;
3037c478bd9Sstevel@tonic-gate
3047c478bd9Sstevel@tonic-gate status = ddi_soft_state_init(&su_asycom, sizeof (struct asycom),
3057c478bd9Sstevel@tonic-gate SU_INITIAL_SOFT_ITEMS);
3067c478bd9Sstevel@tonic-gate if (status != 0)
3070280efdcSzk return (status);
3087c478bd9Sstevel@tonic-gate status = ddi_soft_state_init(&su_asyncline, sizeof (struct asyncline),
3097c478bd9Sstevel@tonic-gate SU_INITIAL_SOFT_ITEMS);
3107c478bd9Sstevel@tonic-gate if (status != 0) {
3117c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&su_asycom);
3127c478bd9Sstevel@tonic-gate return (status);
3137c478bd9Sstevel@tonic-gate }
3147c478bd9Sstevel@tonic-gate
3157c478bd9Sstevel@tonic-gate if ((status = mod_install(&modlinkage)) != 0) {
3167c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&su_asycom);
3177c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&su_asyncline);
3187c478bd9Sstevel@tonic-gate }
3197c478bd9Sstevel@tonic-gate
3207c478bd9Sstevel@tonic-gate return (status);
3217c478bd9Sstevel@tonic-gate }
3227c478bd9Sstevel@tonic-gate
3237c478bd9Sstevel@tonic-gate int
_fini(void)3247c478bd9Sstevel@tonic-gate _fini(void)
3257c478bd9Sstevel@tonic-gate {
3267c478bd9Sstevel@tonic-gate int i;
3277c478bd9Sstevel@tonic-gate
3287c478bd9Sstevel@tonic-gate i = mod_remove(&modlinkage);
3297c478bd9Sstevel@tonic-gate if (i == 0) {
3307c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&su_asycom);
3317c478bd9Sstevel@tonic-gate ddi_soft_state_fini(&su_asyncline);
3327c478bd9Sstevel@tonic-gate }
3337c478bd9Sstevel@tonic-gate
3347c478bd9Sstevel@tonic-gate return (i);
3357c478bd9Sstevel@tonic-gate }
3367c478bd9Sstevel@tonic-gate
3377c478bd9Sstevel@tonic-gate int
_info(struct modinfo * modinfop)3387c478bd9Sstevel@tonic-gate _info(struct modinfo *modinfop)
3397c478bd9Sstevel@tonic-gate {
3407c478bd9Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
3417c478bd9Sstevel@tonic-gate }
3427c478bd9Sstevel@tonic-gate
3437c478bd9Sstevel@tonic-gate static int
asyprobe(dev_info_t * devi)3447c478bd9Sstevel@tonic-gate asyprobe(dev_info_t *devi)
3457c478bd9Sstevel@tonic-gate {
3467c478bd9Sstevel@tonic-gate int instance;
3477c478bd9Sstevel@tonic-gate ddi_acc_handle_t handle;
3487c478bd9Sstevel@tonic-gate uchar_t *addr;
3497c478bd9Sstevel@tonic-gate ddi_device_acc_attr_t attr;
3507c478bd9Sstevel@tonic-gate
3517c478bd9Sstevel@tonic-gate attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
3527c478bd9Sstevel@tonic-gate attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
3537c478bd9Sstevel@tonic-gate attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
3547c478bd9Sstevel@tonic-gate if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO, (caddr_t *)&addr,
3557c478bd9Sstevel@tonic-gate SU_REGOFFSET, SU_REGISTER_LEN, &attr, &handle) != DDI_SUCCESS) {
3567c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "asyprobe regs map setup failed");
3577c478bd9Sstevel@tonic-gate return (DDI_PROBE_FAILURE);
3587c478bd9Sstevel@tonic-gate }
3597c478bd9Sstevel@tonic-gate #ifdef DEBUG
3607c478bd9Sstevel@tonic-gate if (asydebug)
3610280efdcSzk printf("Probe address mapped %p\n", (void *)addr);
3627c478bd9Sstevel@tonic-gate #endif
3637c478bd9Sstevel@tonic-gate
3647c478bd9Sstevel@tonic-gate /*
3657c478bd9Sstevel@tonic-gate * Probe for the device:
366574493ffSToomas Soome * Ser. int. uses bits 0,1,2; FIFO uses 3,6,7; 4,5 wired low.
367574493ffSToomas Soome * If bit 4 or 5 appears on inb() ISR, board is not there.
3687c478bd9Sstevel@tonic-gate */
3692d526643Sjesusm if (ddi_get8(handle, addr+ISR) & 0x30) {
3700280efdcSzk ddi_regs_map_free(&handle);
3710280efdcSzk return (DDI_PROBE_FAILURE);
3722d526643Sjesusm }
3732d526643Sjesusm
3747c478bd9Sstevel@tonic-gate instance = ddi_get_instance(devi);
3757c478bd9Sstevel@tonic-gate if (max_asy_instance < instance)
3760280efdcSzk max_asy_instance = instance;
3777c478bd9Sstevel@tonic-gate ddi_regs_map_free(&handle);
3787c478bd9Sstevel@tonic-gate
3797c478bd9Sstevel@tonic-gate return (DDI_PROBE_SUCCESS); /* hw is present */
3807c478bd9Sstevel@tonic-gate }
3817c478bd9Sstevel@tonic-gate
3827c478bd9Sstevel@tonic-gate static int
asydetach(dev_info_t * devi,ddi_detach_cmd_t cmd)3837c478bd9Sstevel@tonic-gate asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
3847c478bd9Sstevel@tonic-gate {
385*e476cc14SToomas Soome int instance;
3867c478bd9Sstevel@tonic-gate struct asycom *asy;
3877c478bd9Sstevel@tonic-gate struct asyncline *async;
3887c478bd9Sstevel@tonic-gate char name[16];
3897c478bd9Sstevel@tonic-gate
3907c478bd9Sstevel@tonic-gate instance = ddi_get_instance(devi); /* find out which unit */
3917c478bd9Sstevel@tonic-gate
3927c478bd9Sstevel@tonic-gate asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
3937c478bd9Sstevel@tonic-gate async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
3947c478bd9Sstevel@tonic-gate
3957c478bd9Sstevel@tonic-gate switch (cmd) {
3960280efdcSzk case DDI_DETACH:
3970280efdcSzk break;
3980280efdcSzk case DDI_SUSPEND:
3990280efdcSzk /* grab both mutex locks */
4000280efdcSzk mutex_enter(asy->asy_excl);
4010280efdcSzk mutex_enter(asy->asy_excl_hi);
4020280efdcSzk if (asy->suspended) {
4030280efdcSzk mutex_exit(asy->asy_excl_hi);
4040280efdcSzk mutex_exit(asy->asy_excl);
4050280efdcSzk return (DDI_SUCCESS);
4060280efdcSzk }
4070280efdcSzk asy->suspended = B_TRUE;
4080280efdcSzk
4090280efdcSzk /*
4100280efdcSzk * The quad UART ST16C554D, version D2 (made by EXAR)
4110280efdcSzk * has an anomaly of generating spurious interrupts
4120280efdcSzk * when the ICR is loaded with zero. The workaround
4130280efdcSzk * would be to read/write any register with DATA1 bit
4140280efdcSzk * set to 0 before such write.
4150280efdcSzk */
4160280efdcSzk if (asy->asy_hwtype == ASY16C554D)
4170280efdcSzk OUTB(SPR, 0);
4180280efdcSzk
4190280efdcSzk /* Disable further interrupts */
4200280efdcSzk OUTB(ICR, 0);
4217c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
4227c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
4237c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
42417ea09c7Sanovick
4250280efdcSzk default:
4260280efdcSzk return (DDI_FAILURE);
4277c478bd9Sstevel@tonic-gate }
4287c478bd9Sstevel@tonic-gate
4297c478bd9Sstevel@tonic-gate #ifdef DEBUG
4307c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_INIT)
4310280efdcSzk cmn_err(CE_NOTE, "su%d: ASY%s shutdown.", instance,
4320280efdcSzk asy->asy_hwtype == ASY82510 ? "82510" :
4330280efdcSzk asy->asy_hwtype == ASY16550AF ? "16550AF" :
4340280efdcSzk asy->asy_hwtype == ASY16C554D ? "16C554D" :
4350280efdcSzk "8250");
4367c478bd9Sstevel@tonic-gate #endif
4377c478bd9Sstevel@tonic-gate /*
4387c478bd9Sstevel@tonic-gate * Before removing interrupts it is always better to disable
4397c478bd9Sstevel@tonic-gate * interrupts if the chip gives a provision to disable the
4407c478bd9Sstevel@tonic-gate * serial port interrupts.
4417c478bd9Sstevel@tonic-gate */
4427c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
4437c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
44417ea09c7Sanovick /* disable interrupts, see EXAR bug */
445f63f7506Sanovick if (asy->asy_hwtype == ASY16C554D)
446f63f7506Sanovick OUTB(SPR, 0);
44717ea09c7Sanovick OUTB(ICR, 0);
4487c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
4497c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
4507c478bd9Sstevel@tonic-gate
4517c478bd9Sstevel@tonic-gate /* remove minor device node(s) for this device */
4527c478bd9Sstevel@tonic-gate (void) sprintf(name, "%c", (instance+'a')); /* serial-port */
4537c478bd9Sstevel@tonic-gate ddi_remove_minor_node(devi, name);
4547c478bd9Sstevel@tonic-gate (void) sprintf(name, "%c,cu", (instance+'a')); /* serial-port:dailout */
4557c478bd9Sstevel@tonic-gate ddi_remove_minor_node(devi, name);
4567c478bd9Sstevel@tonic-gate
4577c478bd9Sstevel@tonic-gate mutex_destroy(asy->asy_excl);
4587c478bd9Sstevel@tonic-gate mutex_destroy(asy->asy_excl_hi);
4597c478bd9Sstevel@tonic-gate kmem_free(asy->asy_excl, sizeof (kmutex_t));
4607c478bd9Sstevel@tonic-gate kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
4617c478bd9Sstevel@tonic-gate cv_destroy(&async->async_flags_cv);
4627c478bd9Sstevel@tonic-gate kstat_delete(asy->sukstat);
4637c478bd9Sstevel@tonic-gate ddi_remove_intr(devi, 0, asy->asy_iblock);
4647c478bd9Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_handle);
4657c478bd9Sstevel@tonic-gate ddi_remove_softintr(asy->asy_softintr_id);
4667c478bd9Sstevel@tonic-gate mutex_destroy(asy->asy_soft_lock);
4677c478bd9Sstevel@tonic-gate kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
4687c478bd9Sstevel@tonic-gate ddi_soft_state_free(su_asycom, instance);
4697c478bd9Sstevel@tonic-gate ddi_soft_state_free(su_asyncline, instance);
4707c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
4717c478bd9Sstevel@tonic-gate }
4727c478bd9Sstevel@tonic-gate
4737c478bd9Sstevel@tonic-gate static int
asyattach(dev_info_t * devi,ddi_attach_cmd_t cmd)4747c478bd9Sstevel@tonic-gate asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
4757c478bd9Sstevel@tonic-gate {
476*e476cc14SToomas Soome int instance;
4777c478bd9Sstevel@tonic-gate struct asycom *asy;
4787c478bd9Sstevel@tonic-gate struct asyncline *async;
4797c478bd9Sstevel@tonic-gate char name[40];
4807c478bd9Sstevel@tonic-gate ddi_device_acc_attr_t attr;
4817c478bd9Sstevel@tonic-gate enum states { EMPTY, SOFTSTATE, REGSMAP, MUTEXES, ADDINTR,
4827c478bd9Sstevel@tonic-gate SOFTINTR, ASYINIT, KSTAT, MINORNODE };
4837c478bd9Sstevel@tonic-gate enum states state = EMPTY;
484f63f7506Sanovick char *hwtype;
4857c478bd9Sstevel@tonic-gate
4867c478bd9Sstevel@tonic-gate instance = ddi_get_instance(devi); /* find out which unit */
4877c478bd9Sstevel@tonic-gate
4887c478bd9Sstevel@tonic-gate /* cannot attach a device that has not been probed first */
4897c478bd9Sstevel@tonic-gate if (instance > max_asy_instance)
4900280efdcSzk return (DDI_FAILURE);
4917c478bd9Sstevel@tonic-gate
4927c478bd9Sstevel@tonic-gate if (cmd != DDI_RESUME) {
4937c478bd9Sstevel@tonic-gate /* Allocate soft state space */
4947c478bd9Sstevel@tonic-gate if (ddi_soft_state_zalloc(su_asycom, instance) != DDI_SUCCESS) {
4957c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "su%d: cannot allocate soft state",
4967c478bd9Sstevel@tonic-gate instance);
4977c478bd9Sstevel@tonic-gate goto error;
4987c478bd9Sstevel@tonic-gate }
4997c478bd9Sstevel@tonic-gate }
5007c478bd9Sstevel@tonic-gate state = SOFTSTATE;
5017c478bd9Sstevel@tonic-gate
5027c478bd9Sstevel@tonic-gate asy = (struct asycom *)ddi_get_soft_state(su_asycom, instance);
5037c478bd9Sstevel@tonic-gate
5047c478bd9Sstevel@tonic-gate if (asy == NULL) {
5057c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "su%d: cannot get soft state", instance);
5067c478bd9Sstevel@tonic-gate goto error;
5077c478bd9Sstevel@tonic-gate }
5087c478bd9Sstevel@tonic-gate
5097c478bd9Sstevel@tonic-gate switch (cmd) {
5100280efdcSzk case DDI_ATTACH:
5110280efdcSzk break;
5120280efdcSzk case DDI_RESUME: {
5130280efdcSzk struct asyncline *async;
5147c478bd9Sstevel@tonic-gate
5150280efdcSzk /* grab both mutex locks */
5160280efdcSzk mutex_enter(asy->asy_excl);
5170280efdcSzk mutex_enter(asy->asy_excl_hi);
5180280efdcSzk if (!asy->suspended) {
5190280efdcSzk mutex_exit(asy->asy_excl_hi);
5200280efdcSzk mutex_exit(asy->asy_excl);
5210280efdcSzk return (DDI_SUCCESS);
5220280efdcSzk }
5230280efdcSzk /*
5240280efdcSzk * re-setup all the registers and enable interrupts if
5250280efdcSzk * needed
5260280efdcSzk */
5270280efdcSzk async = (struct asyncline *)asy->asy_priv;
5280280efdcSzk if ((async) && (async->async_flags & ASYNC_ISOPEN))
5290280efdcSzk (void) asy_program(asy, ASY_INIT);
5300280efdcSzk asy->suspended = B_FALSE;
5317c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
5327c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
5337c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
5347c478bd9Sstevel@tonic-gate }
5350280efdcSzk default:
5360280efdcSzk goto error;
5377c478bd9Sstevel@tonic-gate }
5387c478bd9Sstevel@tonic-gate
5397c478bd9Sstevel@tonic-gate attr.devacc_attr_version = DDI_DEVICE_ATTR_V0;
5407c478bd9Sstevel@tonic-gate attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC;
5417c478bd9Sstevel@tonic-gate attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC;
5427c478bd9Sstevel@tonic-gate
5437c478bd9Sstevel@tonic-gate if (ddi_regs_map_setup(devi, SU_REGISTER_FILE_NO,
5447c478bd9Sstevel@tonic-gate (caddr_t *)&asy->asy_ioaddr, SU_REGOFFSET, SU_REGISTER_LEN,
5457c478bd9Sstevel@tonic-gate &attr, &asy->asy_handle) != DDI_SUCCESS) {
5467c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "asyprobe regs map setup failed");
5477c478bd9Sstevel@tonic-gate goto error;
5487c478bd9Sstevel@tonic-gate }
5497c478bd9Sstevel@tonic-gate state = REGSMAP;
5507c478bd9Sstevel@tonic-gate
5517c478bd9Sstevel@tonic-gate #ifdef DEBUG
5527c478bd9Sstevel@tonic-gate if (asydebug)
5530280efdcSzk printf("su attach mapped %p\n", (void *)asy->asy_ioaddr);
5547c478bd9Sstevel@tonic-gate #endif
5557c478bd9Sstevel@tonic-gate
5567c478bd9Sstevel@tonic-gate /*
5577c478bd9Sstevel@tonic-gate * Initialize the port with default settings.
5587c478bd9Sstevel@tonic-gate */
5597c478bd9Sstevel@tonic-gate asy->asy_fifo_buf = 1;
5607c478bd9Sstevel@tonic-gate asy->asy_use_fifo = FIFO_OFF;
5617c478bd9Sstevel@tonic-gate
5627c478bd9Sstevel@tonic-gate /*
5637c478bd9Sstevel@tonic-gate * Check for baudrate generator's "baud-divisor-factor" property setup
5647c478bd9Sstevel@tonic-gate * by OBP, since different UART chips might have different baudrate
5657c478bd9Sstevel@tonic-gate * generator divisor. e.g., in case of NSPG's Sputnik platform, the
5667c478bd9Sstevel@tonic-gate * baud-divisor-factor is 13, it uses dedicated 16552 "DUART" chip
5677c478bd9Sstevel@tonic-gate * instead of SuperIO. Since the baud-divisor-factor must be a positive
5687c478bd9Sstevel@tonic-gate * integer, the divisors will always be at least as large as the values
5697c478bd9Sstevel@tonic-gate * in asyspdtab[]. Make the default factor 1.
5707c478bd9Sstevel@tonic-gate */
5717c478bd9Sstevel@tonic-gate asy->asy_baud_divisor_factor = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
5727c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "baud-divisor-factor", 1);
5737c478bd9Sstevel@tonic-gate
5747c478bd9Sstevel@tonic-gate /* set speed cap */
5757c478bd9Sstevel@tonic-gate asy->asy_speed_cap = ddi_prop_get_int(DDI_DEV_T_ANY, devi,
5767c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, "serial-speed-cap", 115200);
5777c478bd9Sstevel@tonic-gate
5787c478bd9Sstevel@tonic-gate /* check for ASY82510 chip */
5797c478bd9Sstevel@tonic-gate OUTB(ISR, 0x20);
5807c478bd9Sstevel@tonic-gate if (INB(ISR) & 0x20) { /* 82510 chip is present */
5817c478bd9Sstevel@tonic-gate /*
5827c478bd9Sstevel@tonic-gate * Since most of the general operation of the 82510 chip
5837c478bd9Sstevel@tonic-gate * can be done from BANK 0 (8250A/16450 compatable mode)
5847c478bd9Sstevel@tonic-gate * we will default to BANK 0.
5857c478bd9Sstevel@tonic-gate */
5867c478bd9Sstevel@tonic-gate asy->asy_hwtype = ASY82510;
5877c478bd9Sstevel@tonic-gate OUTB(DAT+7, 0x04); /* clear status */
5887c478bd9Sstevel@tonic-gate OUTB(ISR, 0x40); /* set to bank 2 */
5897c478bd9Sstevel@tonic-gate OUTB(MCR, 0x08); /* IMD */
5907c478bd9Sstevel@tonic-gate OUTB(DAT, 0x21); /* FMD */
5917c478bd9Sstevel@tonic-gate OUTB(ISR, 0x00); /* set to bank 0 */
5927c478bd9Sstevel@tonic-gate asy->asy_trig_level = 0;
5937c478bd9Sstevel@tonic-gate } else { /* Set the UART in FIFO mode if it has FIFO buffers */
5947c478bd9Sstevel@tonic-gate asy->asy_hwtype = ASY16550AF;
5957c478bd9Sstevel@tonic-gate OUTB(FIFOR, 0x00); /* clear fifo register */
5967c478bd9Sstevel@tonic-gate asy->asy_trig_level = 0x00; /* sets the fifo Threshold to 1 */
5977c478bd9Sstevel@tonic-gate
5987c478bd9Sstevel@tonic-gate /* set/Enable FIFO */
5997c478bd9Sstevel@tonic-gate OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH | FIFORXFLSH |
6007c478bd9Sstevel@tonic-gate (asy->asy_trig_level & 0xff));
6017c478bd9Sstevel@tonic-gate
6027c478bd9Sstevel@tonic-gate if ((INB(ISR) & 0xc0) == 0xc0)
6030280efdcSzk asy->asy_use_fifo = FIFO_ON;
6047c478bd9Sstevel@tonic-gate else {
6057c478bd9Sstevel@tonic-gate asy->asy_hwtype = ASY8250;
6067c478bd9Sstevel@tonic-gate OUTB(FIFOR, 0x00); /* NO FIFOs */
6077c478bd9Sstevel@tonic-gate asy->asy_trig_level = 0;
6087c478bd9Sstevel@tonic-gate }
6097c478bd9Sstevel@tonic-gate }
6107c478bd9Sstevel@tonic-gate
611f63f7506Sanovick /* check for ST16C554D chip */
612f63f7506Sanovick if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, devi, DDI_PROP_NOTPROM |
613f63f7506Sanovick DDI_PROP_DONTPASS, "hwtype", &hwtype)) == DDI_PROP_SUCCESS) {
614f63f7506Sanovick if (strcmp(hwtype, "ST16C554D") == 0)
615f63f7506Sanovick asy->asy_hwtype = ASY16C554D;
616f63f7506Sanovick ddi_prop_free(hwtype);
617f63f7506Sanovick }
618f63f7506Sanovick
61917ea09c7Sanovick /* disable interrupts, see EXAR bug */
620f63f7506Sanovick if (asy->asy_hwtype == ASY16C554D)
621f63f7506Sanovick OUTB(SPR, 0);
62217ea09c7Sanovick OUTB(ICR, 0);
6237c478bd9Sstevel@tonic-gate OUTB(LCR, DLAB); /* select baud rate generator */
6247c478bd9Sstevel@tonic-gate /* Set the baud rate to 9600 */
6257c478bd9Sstevel@tonic-gate OUTB(DAT+DLL, (ASY9600*asy->asy_baud_divisor_factor) & 0xff);
6267c478bd9Sstevel@tonic-gate OUTB(DAT+DLH, ((ASY9600*asy->asy_baud_divisor_factor) >> 8) & 0xff);
6277c478bd9Sstevel@tonic-gate OUTB(LCR, STOP1|BITS8);
6287c478bd9Sstevel@tonic-gate OUTB(MCR, (DTR | RTS| OUT2));
6297c478bd9Sstevel@tonic-gate
6307c478bd9Sstevel@tonic-gate /*
6317c478bd9Sstevel@tonic-gate * Set up the other components of the asycom structure for this port.
6327c478bd9Sstevel@tonic-gate */
6337c478bd9Sstevel@tonic-gate asy->asy_excl = (kmutex_t *)
6347c478bd9Sstevel@tonic-gate kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
6357c478bd9Sstevel@tonic-gate asy->asy_excl_hi = (kmutex_t *)
6367c478bd9Sstevel@tonic-gate kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
6377c478bd9Sstevel@tonic-gate asy->asy_soft_lock = (kmutex_t *)
6387c478bd9Sstevel@tonic-gate kmem_zalloc(sizeof (kmutex_t), KM_SLEEP);
6397c478bd9Sstevel@tonic-gate asy->asy_unit = instance;
6407c478bd9Sstevel@tonic-gate asy->asy_dip = devi;
6417c478bd9Sstevel@tonic-gate
6427c478bd9Sstevel@tonic-gate if (ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != DDI_SUCCESS) {
6437c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE,
6447c478bd9Sstevel@tonic-gate "Get iblock_cookie failed-Device interrupt%x\n", instance);
6457c478bd9Sstevel@tonic-gate goto error;
6467c478bd9Sstevel@tonic-gate }
6477c478bd9Sstevel@tonic-gate
6487c478bd9Sstevel@tonic-gate if (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_HIGH,
6497c478bd9Sstevel@tonic-gate &asy->asy_soft_iblock) != DDI_SUCCESS) {
6507c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "Get iblock_cookie failed -soft interrupt%x\n",
6517c478bd9Sstevel@tonic-gate instance);
6527c478bd9Sstevel@tonic-gate goto error;
6537c478bd9Sstevel@tonic-gate }
6547c478bd9Sstevel@tonic-gate
6557c478bd9Sstevel@tonic-gate mutex_init(asy->asy_soft_lock, NULL, MUTEX_DRIVER,
6567c478bd9Sstevel@tonic-gate (void *)asy->asy_soft_iblock);
6577c478bd9Sstevel@tonic-gate mutex_init(asy->asy_excl, NULL, MUTEX_DRIVER, NULL);
6587c478bd9Sstevel@tonic-gate mutex_init(asy->asy_excl_hi, NULL, MUTEX_DRIVER,
6597c478bd9Sstevel@tonic-gate (void *)asy->asy_iblock);
6607c478bd9Sstevel@tonic-gate state = MUTEXES;
6617c478bd9Sstevel@tonic-gate
6627c478bd9Sstevel@tonic-gate /*
6637c478bd9Sstevel@tonic-gate * Install interrupt handlers for this device.
6647c478bd9Sstevel@tonic-gate */
6657c478bd9Sstevel@tonic-gate if (ddi_add_intr(devi, 0, &(asy->asy_iblock), 0, asyintr,
6667c478bd9Sstevel@tonic-gate (caddr_t)asy) != DDI_SUCCESS) {
6677c478bd9Sstevel@tonic-gate cmn_err(CE_CONT,
6687c478bd9Sstevel@tonic-gate "Cannot set device interrupt for su driver\n");
6697c478bd9Sstevel@tonic-gate goto error;
6707c478bd9Sstevel@tonic-gate }
6717c478bd9Sstevel@tonic-gate state = ADDINTR;
6727c478bd9Sstevel@tonic-gate
6737c478bd9Sstevel@tonic-gate if (ddi_add_softintr(devi, DDI_SOFTINT_HIGH, &(asy->asy_softintr_id),
6747c478bd9Sstevel@tonic-gate &asy->asy_soft_iblock, 0, asysoftintr, (caddr_t)asy)
6757c478bd9Sstevel@tonic-gate != DDI_SUCCESS) {
6767c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "Cannot set soft interrupt for su driver\n");
6777c478bd9Sstevel@tonic-gate goto error;
6787c478bd9Sstevel@tonic-gate }
6797c478bd9Sstevel@tonic-gate state = SOFTINTR;
6807c478bd9Sstevel@tonic-gate
6817c478bd9Sstevel@tonic-gate /* initialize the asyncline structure */
6827c478bd9Sstevel@tonic-gate if (ddi_soft_state_zalloc(su_asyncline, instance) != DDI_SUCCESS) {
6837c478bd9Sstevel@tonic-gate cmn_err(CE_CONT, "su%d: cannot allocate soft state", instance);
6847c478bd9Sstevel@tonic-gate goto error;
6857c478bd9Sstevel@tonic-gate }
6867c478bd9Sstevel@tonic-gate state = ASYINIT;
6877c478bd9Sstevel@tonic-gate
6887c478bd9Sstevel@tonic-gate async = (struct asyncline *)ddi_get_soft_state(su_asyncline, instance);
6897c478bd9Sstevel@tonic-gate
6907c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
6917c478bd9Sstevel@tonic-gate async->async_common = asy;
6927c478bd9Sstevel@tonic-gate cv_init(&async->async_flags_cv, NULL, CV_DEFAULT, NULL);
6937c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
6947c478bd9Sstevel@tonic-gate
6957c478bd9Sstevel@tonic-gate if ((asy->sukstat = kstat_create("su", instance, "serialstat",
6967c478bd9Sstevel@tonic-gate "misc", KSTAT_TYPE_NAMED, 2, KSTAT_FLAG_VIRTUAL)) != NULL) {
6977c478bd9Sstevel@tonic-gate asy->sukstat->ks_data = &asy->kstats;
6987c478bd9Sstevel@tonic-gate kstat_named_init(&asy->kstats.ringover, "ring buffer overflow",
6997c478bd9Sstevel@tonic-gate KSTAT_DATA_UINT64);
7007c478bd9Sstevel@tonic-gate kstat_named_init(&asy->kstats.siloover, "silo overflow",
7017c478bd9Sstevel@tonic-gate KSTAT_DATA_UINT64);
7027c478bd9Sstevel@tonic-gate kstat_install(asy->sukstat);
7037c478bd9Sstevel@tonic-gate }
7047c478bd9Sstevel@tonic-gate state = KSTAT;
7057c478bd9Sstevel@tonic-gate
7067c478bd9Sstevel@tonic-gate if (strcmp(ddi_node_name(devi), "rsc-console") == 0) {
7077c478bd9Sstevel@tonic-gate /*
7087c478bd9Sstevel@tonic-gate * If the device is configured as the 'rsc-console'
7097c478bd9Sstevel@tonic-gate * create the minor device for this node.
7107c478bd9Sstevel@tonic-gate */
7117c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(devi, "ssp", S_IFCHR,
712574493ffSToomas Soome asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, 0) == DDI_FAILURE) {
7137c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
7147c478bd9Sstevel@tonic-gate "%s%d: Failed to create node rsc-console",
7157c478bd9Sstevel@tonic-gate ddi_get_name(devi), ddi_get_instance(devi));
7167c478bd9Sstevel@tonic-gate goto error;
7177c478bd9Sstevel@tonic-gate }
7187c478bd9Sstevel@tonic-gate
7197c478bd9Sstevel@tonic-gate asy->asy_lom_console = 0;
7207c478bd9Sstevel@tonic-gate asy->asy_rsc_console = 1;
7217c478bd9Sstevel@tonic-gate asy->asy_rsc_control = 0;
7227c478bd9Sstevel@tonic-gate asy->asy_device_type = ASY_SERIAL;
7237c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD;
7247c478bd9Sstevel@tonic-gate
7257c478bd9Sstevel@tonic-gate } else if (strcmp(ddi_node_name(devi), "lom-console") == 0) {
7267c478bd9Sstevel@tonic-gate /*
7277c478bd9Sstevel@tonic-gate * If the device is configured as the 'lom-console'
7287c478bd9Sstevel@tonic-gate * create the minor device for this node.
7297c478bd9Sstevel@tonic-gate * Do not create a dialout device.
7307c478bd9Sstevel@tonic-gate * Use the same minor numbers as would be used for standard
7317c478bd9Sstevel@tonic-gate * serial instances.
7327c478bd9Sstevel@tonic-gate */
7337c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(devi, "lom-console", S_IFCHR,
734574493ffSToomas Soome instance, DDI_NT_SERIAL_LOMCON, 0) == DDI_FAILURE) {
7357c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
7367c478bd9Sstevel@tonic-gate "%s%d: Failed to create node lom-console",
7377c478bd9Sstevel@tonic-gate ddi_get_name(devi), ddi_get_instance(devi));
7387c478bd9Sstevel@tonic-gate goto error;
7397c478bd9Sstevel@tonic-gate }
7407c478bd9Sstevel@tonic-gate asy->asy_lom_console = 1;
7417c478bd9Sstevel@tonic-gate asy->asy_rsc_console = 0;
7427c478bd9Sstevel@tonic-gate asy->asy_rsc_control = 0;
7437c478bd9Sstevel@tonic-gate asy->asy_device_type = ASY_SERIAL;
7447c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD;
7457c478bd9Sstevel@tonic-gate
7467c478bd9Sstevel@tonic-gate } else if (strcmp(ddi_node_name(devi), "rsc-control") == 0) {
7477c478bd9Sstevel@tonic-gate /*
7487c478bd9Sstevel@tonic-gate * If the device is configured as the 'rsc-control'
7497c478bd9Sstevel@tonic-gate * create the minor device for this node.
7507c478bd9Sstevel@tonic-gate */
7517c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(devi, "sspctl", S_IFCHR,
752574493ffSToomas Soome asy->asy_unit | RSC_DEVICE, DDI_PSEUDO, 0) == DDI_FAILURE) {
7537c478bd9Sstevel@tonic-gate cmn_err(CE_WARN, "%s%d: Failed to create rsc-control",
7547c478bd9Sstevel@tonic-gate ddi_get_name(devi), ddi_get_instance(devi));
7557c478bd9Sstevel@tonic-gate goto error;
7567c478bd9Sstevel@tonic-gate }
7577c478bd9Sstevel@tonic-gate
7587c478bd9Sstevel@tonic-gate asy->asy_lom_console = 0;
7597c478bd9Sstevel@tonic-gate asy->asy_rsc_console = 0;
7607c478bd9Sstevel@tonic-gate asy->asy_rsc_control = 1;
7617c478bd9Sstevel@tonic-gate asy->asy_device_type = ASY_SERIAL;
7627c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD;
7637c478bd9Sstevel@tonic-gate
7647c478bd9Sstevel@tonic-gate } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
7650280efdcSzk "keyboard", 0)) {
7667c478bd9Sstevel@tonic-gate /*
7677c478bd9Sstevel@tonic-gate * If the device is a keyboard, then create an internal
7687c478bd9Sstevel@tonic-gate * pathname so that the dacf code will link the node into
7697c478bd9Sstevel@tonic-gate * the keyboard console stream. See dacf.conf.
7707c478bd9Sstevel@tonic-gate */
7717c478bd9Sstevel@tonic-gate if (ddi_create_internal_pathname(devi, "keyboard",
7727c478bd9Sstevel@tonic-gate S_IFCHR, instance) == DDI_FAILURE) {
7737c478bd9Sstevel@tonic-gate goto error;
7747c478bd9Sstevel@tonic-gate }
7757c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */
776574493ffSToomas Soome asy->asy_device_type = ASY_KEYBOARD; /* Device type */
7777c478bd9Sstevel@tonic-gate } else if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
7780280efdcSzk "mouse", 0)) {
7797c478bd9Sstevel@tonic-gate /*
7807c478bd9Sstevel@tonic-gate * If the device is a mouse, then create an internal
7817c478bd9Sstevel@tonic-gate * pathname so that the dacf code will link the node into
7827c478bd9Sstevel@tonic-gate * the mouse stream. See dacf.conf.
7837c478bd9Sstevel@tonic-gate */
7847c478bd9Sstevel@tonic-gate if (ddi_create_internal_pathname(devi, "mouse", S_IFCHR,
7857c478bd9Sstevel@tonic-gate instance) == DDI_FAILURE) {
7867c478bd9Sstevel@tonic-gate goto error;
7877c478bd9Sstevel@tonic-gate }
7887c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */
7897c478bd9Sstevel@tonic-gate asy->asy_device_type = ASY_MOUSE;
7907c478bd9Sstevel@tonic-gate } else {
7917c478bd9Sstevel@tonic-gate /*
7927c478bd9Sstevel@tonic-gate * If not used for keyboard/mouse, create minor devices nodes
7937c478bd9Sstevel@tonic-gate * for this device
7947c478bd9Sstevel@tonic-gate */
7957c478bd9Sstevel@tonic-gate /* serial-port */
7967c478bd9Sstevel@tonic-gate (void) sprintf(name, "%c", (instance+'a'));
7977c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(devi, name, S_IFCHR, instance,
798574493ffSToomas Soome DDI_NT_SERIAL_MB, 0) == DDI_FAILURE) {
7997c478bd9Sstevel@tonic-gate goto error;
8007c478bd9Sstevel@tonic-gate }
8017c478bd9Sstevel@tonic-gate state = MINORNODE;
8027c478bd9Sstevel@tonic-gate /* serial-port:dailout */
8037c478bd9Sstevel@tonic-gate (void) sprintf(name, "%c,cu", (instance+'a'));
8047c478bd9Sstevel@tonic-gate if (ddi_create_minor_node(devi, name, S_IFCHR, instance|OUTLINE,
805574493ffSToomas Soome DDI_NT_SERIAL_MB_DO, 0) == DDI_FAILURE) {
8067c478bd9Sstevel@tonic-gate goto error;
8077c478bd9Sstevel@tonic-gate }
8087c478bd9Sstevel@tonic-gate /* Property for ignoring DCD */
8097c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
8107c478bd9Sstevel@tonic-gate "ignore-cd", 0)) {
8117c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */
8127c478bd9Sstevel@tonic-gate } else {
8137c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD;
8147c478bd9Sstevel@tonic-gate /*
8157c478bd9Sstevel@tonic-gate * if ignore-cd is not available it could be
8167c478bd9Sstevel@tonic-gate * some old legacy platform, try to see
8177c478bd9Sstevel@tonic-gate * whether the old legacy property exists
8187c478bd9Sstevel@tonic-gate */
8197c478bd9Sstevel@tonic-gate (void) sprintf(name,
8207c478bd9Sstevel@tonic-gate "port-%c-ignore-cd", (instance+ 'a'));
8217c478bd9Sstevel@tonic-gate if (ddi_getprop(DDI_DEV_T_ANY, devi,
8227c478bd9Sstevel@tonic-gate DDI_PROP_DONTPASS, name, 0))
8237c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD;
8247c478bd9Sstevel@tonic-gate }
8257c478bd9Sstevel@tonic-gate asy->asy_device_type = ASY_SERIAL;
8267c478bd9Sstevel@tonic-gate }
8270280efdcSzk
8280280efdcSzk /*
8290280efdcSzk * Fill in the polled I/O structure
8300280efdcSzk */
8310280efdcSzk asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0;
8320280efdcSzk asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy;
8330280efdcSzk asy->polledio.cons_polledio_putchar = asyputchar;
8340280efdcSzk asy->polledio.cons_polledio_getchar = asygetchar;
8350280efdcSzk asy->polledio.cons_polledio_ischar = asyischar;
8360280efdcSzk asy->polledio.cons_polledio_enter = asy_polled_enter;
8370280efdcSzk asy->polledio.cons_polledio_exit = asy_polled_exit;
8380280efdcSzk
8390280efdcSzk /* Initialize saved ICR and polled_enter */
8400280efdcSzk asy->polled_icr = 0;
8410280efdcSzk asy->polled_enter = B_FALSE;
8420280efdcSzk
8437c478bd9Sstevel@tonic-gate ddi_report_dev(devi);
8447c478bd9Sstevel@tonic-gate return (DDI_SUCCESS);
8457c478bd9Sstevel@tonic-gate
8467c478bd9Sstevel@tonic-gate error:
8477c478bd9Sstevel@tonic-gate if (state == MINORNODE) {
8487c478bd9Sstevel@tonic-gate (void) sprintf(name, "%c", (instance+'a'));
8497c478bd9Sstevel@tonic-gate ddi_remove_minor_node(devi, name);
8507c478bd9Sstevel@tonic-gate }
8517c478bd9Sstevel@tonic-gate if (state >= KSTAT)
8527c478bd9Sstevel@tonic-gate kstat_delete(asy->sukstat);
8537c478bd9Sstevel@tonic-gate if (state >= ASYINIT) {
8547c478bd9Sstevel@tonic-gate cv_destroy(&async->async_flags_cv);
8557c478bd9Sstevel@tonic-gate ddi_soft_state_free(su_asyncline, instance);
8567c478bd9Sstevel@tonic-gate }
8577c478bd9Sstevel@tonic-gate if (state >= SOFTINTR)
8587c478bd9Sstevel@tonic-gate ddi_remove_softintr(asy->asy_softintr_id);
8597c478bd9Sstevel@tonic-gate if (state >= ADDINTR)
8607c478bd9Sstevel@tonic-gate ddi_remove_intr(devi, 0, asy->asy_iblock);
8617c478bd9Sstevel@tonic-gate if (state >= MUTEXES) {
8627c478bd9Sstevel@tonic-gate mutex_destroy(asy->asy_excl_hi);
8637c478bd9Sstevel@tonic-gate mutex_destroy(asy->asy_excl);
8647c478bd9Sstevel@tonic-gate mutex_destroy(asy->asy_soft_lock);
8657c478bd9Sstevel@tonic-gate kmem_free(asy->asy_excl_hi, sizeof (kmutex_t));
8667c478bd9Sstevel@tonic-gate kmem_free(asy->asy_excl, sizeof (kmutex_t));
8677c478bd9Sstevel@tonic-gate kmem_free(asy->asy_soft_lock, sizeof (kmutex_t));
8687c478bd9Sstevel@tonic-gate }
8697c478bd9Sstevel@tonic-gate if (state >= REGSMAP)
8707c478bd9Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_handle);
8717c478bd9Sstevel@tonic-gate if (state >= SOFTSTATE)
8727c478bd9Sstevel@tonic-gate ddi_soft_state_free(su_asycom, instance);
8737c478bd9Sstevel@tonic-gate /* no action for EMPTY state */
8747c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
8757c478bd9Sstevel@tonic-gate }
8767c478bd9Sstevel@tonic-gate
8777c478bd9Sstevel@tonic-gate static int
asyinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)8787c478bd9Sstevel@tonic-gate asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
8794cad604cSMarcel Telka void **result)
8807c478bd9Sstevel@tonic-gate {
8817c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(dip))
882*e476cc14SToomas Soome dev_t dev = (dev_t)arg;
883*e476cc14SToomas Soome int instance, error;
8847c478bd9Sstevel@tonic-gate struct asycom *asy;
8857c478bd9Sstevel@tonic-gate
8867c478bd9Sstevel@tonic-gate if ((instance = UNIT(dev)) > max_asy_instance)
8877c478bd9Sstevel@tonic-gate return (DDI_FAILURE);
8887c478bd9Sstevel@tonic-gate
8897c478bd9Sstevel@tonic-gate switch (infocmd) {
8900280efdcSzk case DDI_INFO_DEVT2DEVINFO:
8910280efdcSzk asy = (struct asycom *)ddi_get_soft_state(su_asycom,
8920280efdcSzk instance);
8930280efdcSzk if (asy->asy_dip == NULL)
8940280efdcSzk error = DDI_FAILURE;
8950280efdcSzk else {
8960280efdcSzk *result = (void *) asy->asy_dip;
8970280efdcSzk error = DDI_SUCCESS;
8980280efdcSzk }
8990280efdcSzk break;
9000280efdcSzk case DDI_INFO_DEVT2INSTANCE:
9010280efdcSzk *result = (void *)(uintptr_t)instance;
9027c478bd9Sstevel@tonic-gate error = DDI_SUCCESS;
9030280efdcSzk break;
9040280efdcSzk default:
9050280efdcSzk error = DDI_FAILURE;
9067c478bd9Sstevel@tonic-gate }
9077c478bd9Sstevel@tonic-gate return (error);
9087c478bd9Sstevel@tonic-gate }
9097c478bd9Sstevel@tonic-gate
9107c478bd9Sstevel@tonic-gate static int
asyopen(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)9117c478bd9Sstevel@tonic-gate asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
9127c478bd9Sstevel@tonic-gate {
9137c478bd9Sstevel@tonic-gate _NOTE(ARGUNUSED(sflag))
9147c478bd9Sstevel@tonic-gate struct asycom *asy;
9157c478bd9Sstevel@tonic-gate struct asyncline *async;
9167c478bd9Sstevel@tonic-gate int mcr;
9177c478bd9Sstevel@tonic-gate int unit;
918574493ffSToomas Soome int len;
919574493ffSToomas Soome struct termios *termiosp;
9207c478bd9Sstevel@tonic-gate
9217c478bd9Sstevel@tonic-gate #ifdef DEBUG
9227c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_CLOSE)
9237c478bd9Sstevel@tonic-gate printf("open\n");
9247c478bd9Sstevel@tonic-gate #endif
9257c478bd9Sstevel@tonic-gate unit = UNIT(*dev);
9267c478bd9Sstevel@tonic-gate if (unit > max_asy_instance)
9277c478bd9Sstevel@tonic-gate return (ENXIO); /* unit not configured */
9287c478bd9Sstevel@tonic-gate
9297c478bd9Sstevel@tonic-gate async = (struct asyncline *)ddi_get_soft_state(su_asyncline, unit);
9307c478bd9Sstevel@tonic-gate if (async == NULL)
9317c478bd9Sstevel@tonic-gate return (ENXIO);
9327c478bd9Sstevel@tonic-gate
9337c478bd9Sstevel@tonic-gate asy = async->async_common;
9347c478bd9Sstevel@tonic-gate if (asy == NULL)
9357c478bd9Sstevel@tonic-gate return (ENXIO); /* device not found by autoconfig */
9367c478bd9Sstevel@tonic-gate
9377c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
9387c478bd9Sstevel@tonic-gate asy->asy_priv = (caddr_t)async;
9397c478bd9Sstevel@tonic-gate
9407c478bd9Sstevel@tonic-gate again:
9417c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
9427c478bd9Sstevel@tonic-gate /*
9437c478bd9Sstevel@tonic-gate * Block waiting for carrier to come up, unless this is a no-delay open.
9447c478bd9Sstevel@tonic-gate */
9457c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_ISOPEN)) {
9467c478bd9Sstevel@tonic-gate /*
9477c478bd9Sstevel@tonic-gate * If this port is for a RSC console or control
9487c478bd9Sstevel@tonic-gate * use the following termio info
9497c478bd9Sstevel@tonic-gate */
9507c478bd9Sstevel@tonic-gate if (asy->asy_rsc_console || asy->asy_rsc_control) {
9517c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag = CIBAUDEXT | CBAUDEXT |
9527c478bd9Sstevel@tonic-gate (B115200 & CBAUD);
9537c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |= ((B115200 << IBSHIFT)
9547c478bd9Sstevel@tonic-gate & CIBAUD);
9557c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
9567c478bd9Sstevel@tonic-gate } else if (asy->asy_lom_console) {
9577c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag = B9600 & CBAUD;
9587c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |= ((B9600 << IBSHIFT)
9597c478bd9Sstevel@tonic-gate & CIBAUD);
9607c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |= CS8 | CREAD | CLOCAL;
9617c478bd9Sstevel@tonic-gate } else {
9627c478bd9Sstevel@tonic-gate
9637c478bd9Sstevel@tonic-gate /*
9647c478bd9Sstevel@tonic-gate * Set the default termios settings (cflag).
9657c478bd9Sstevel@tonic-gate * Others are set in ldterm. Release the spin
9667c478bd9Sstevel@tonic-gate * mutex as we can block here, reaquire before
9677c478bd9Sstevel@tonic-gate * calling asy_program.
9687c478bd9Sstevel@tonic-gate */
9697c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
9707c478bd9Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(),
9717c478bd9Sstevel@tonic-gate 0, "ttymodes", (caddr_t)&termiosp, &len)
9727c478bd9Sstevel@tonic-gate == DDI_PROP_SUCCESS &&
9737c478bd9Sstevel@tonic-gate len == sizeof (struct termios)) {
9747c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag =
9757c478bd9Sstevel@tonic-gate termiosp->c_cflag;
9767c478bd9Sstevel@tonic-gate kmem_free(termiosp, len);
9777c478bd9Sstevel@tonic-gate } else {
9787c478bd9Sstevel@tonic-gate cmn_err(CE_WARN,
9797c478bd9Sstevel@tonic-gate "su: couldn't get ttymodes property!");
9807c478bd9Sstevel@tonic-gate }
9817c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
9827c478bd9Sstevel@tonic-gate }
9837c478bd9Sstevel@tonic-gate async->async_ttycommon.t_iflag = 0;
9847c478bd9Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL;
9857c478bd9Sstevel@tonic-gate async->async_ttycommon.t_size.ws_row = 0;
9867c478bd9Sstevel@tonic-gate async->async_ttycommon.t_size.ws_col = 0;
9877c478bd9Sstevel@tonic-gate async->async_ttycommon.t_size.ws_xpixel = 0;
9887c478bd9Sstevel@tonic-gate async->async_ttycommon.t_size.ws_ypixel = 0;
9897c478bd9Sstevel@tonic-gate async->async_dev = *dev;
9907c478bd9Sstevel@tonic-gate async->async_wbufcid = 0;
9917c478bd9Sstevel@tonic-gate
9927c478bd9Sstevel@tonic-gate async->async_startc = CSTART;
9937c478bd9Sstevel@tonic-gate async->async_stopc = CSTOP;
9947c478bd9Sstevel@tonic-gate (void) asy_program(asy, ASY_INIT);
9957c478bd9Sstevel@tonic-gate } else if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
9960280efdcSzk secpolicy_excl_open(cr) != 0) {
9977c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
9987c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
9997c478bd9Sstevel@tonic-gate return (EBUSY);
10007c478bd9Sstevel@tonic-gate } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) {
10017c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
10027c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
10037c478bd9Sstevel@tonic-gate return (EBUSY);
10047c478bd9Sstevel@tonic-gate }
10057c478bd9Sstevel@tonic-gate
10067c478bd9Sstevel@tonic-gate if (*dev & OUTLINE)
10077c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_OUT;
10087c478bd9Sstevel@tonic-gate
10097c478bd9Sstevel@tonic-gate /* Raise DTR on every open */
10107c478bd9Sstevel@tonic-gate mcr = INB(MCR);
10117c478bd9Sstevel@tonic-gate OUTB(MCR, mcr|DTR);
10127c478bd9Sstevel@tonic-gate
10137c478bd9Sstevel@tonic-gate /*
10147c478bd9Sstevel@tonic-gate * Check carrier.
10157c478bd9Sstevel@tonic-gate */
10167c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_IGNORE_CD)
10177c478bd9Sstevel@tonic-gate async->async_ttycommon.t_flags |= TS_SOFTCAR;
10187c478bd9Sstevel@tonic-gate if ((async->async_ttycommon.t_flags & TS_SOFTCAR) ||
10190280efdcSzk (INB(MSR) & DCD))
10207c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_CARR_ON;
10217c478bd9Sstevel@tonic-gate else
10227c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_CARR_ON;
10237c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
10247c478bd9Sstevel@tonic-gate
10257c478bd9Sstevel@tonic-gate /*
10267c478bd9Sstevel@tonic-gate * If FNDELAY and FNONBLOCK are clear, block until carrier up.
10277c478bd9Sstevel@tonic-gate * Quit on interrupt.
10287c478bd9Sstevel@tonic-gate */
10297c478bd9Sstevel@tonic-gate if (!(flag & (FNDELAY|FNONBLOCK)) &&
10300280efdcSzk !(async->async_ttycommon.t_cflag & CLOCAL)) {
10317c478bd9Sstevel@tonic-gate if (!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) ||
10320280efdcSzk ((async->async_flags & ASYNC_OUT) &&
10330280efdcSzk !(*dev & OUTLINE))) {
10340280efdcSzk async->async_flags |= ASYNC_WOPEN;
10350280efdcSzk if (cv_wait_sig(&async->async_flags_cv,
10360280efdcSzk asy->asy_excl) == 0) {
10370280efdcSzk async->async_flags &= ~ASYNC_WOPEN;
10380280efdcSzk mutex_exit(asy->asy_excl);
10390280efdcSzk return (EINTR);
10400280efdcSzk }
10417c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_WOPEN;
10420280efdcSzk goto again;
10437c478bd9Sstevel@tonic-gate }
10447c478bd9Sstevel@tonic-gate } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) {
10450280efdcSzk mutex_exit(asy->asy_excl);
10460280efdcSzk return (EBUSY);
10477c478bd9Sstevel@tonic-gate }
10487c478bd9Sstevel@tonic-gate
10497c478bd9Sstevel@tonic-gate if (asy->suspended) {
10507c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
10517c478bd9Sstevel@tonic-gate (void) ddi_dev_is_needed(asy->asy_dip, 0, 1);
10527c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
10537c478bd9Sstevel@tonic-gate }
10547c478bd9Sstevel@tonic-gate
10557c478bd9Sstevel@tonic-gate async->async_ttycommon.t_readq = rq;
10567c478bd9Sstevel@tonic-gate async->async_ttycommon.t_writeq = WR(rq);
10577c478bd9Sstevel@tonic-gate rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async;
10587c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
10597c478bd9Sstevel@tonic-gate qprocson(rq);
10607c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_ISOPEN;
10617c478bd9Sstevel@tonic-gate async->async_polltid = 0;
10627c478bd9Sstevel@tonic-gate return (0);
10637c478bd9Sstevel@tonic-gate }
10647c478bd9Sstevel@tonic-gate
10657c478bd9Sstevel@tonic-gate static void
async_progress_check(void * arg)10667c478bd9Sstevel@tonic-gate async_progress_check(void *arg)
10677c478bd9Sstevel@tonic-gate {
10687c478bd9Sstevel@tonic-gate struct asyncline *async = arg;
10697c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common;
10707c478bd9Sstevel@tonic-gate mblk_t *bp;
10717c478bd9Sstevel@tonic-gate
10727c478bd9Sstevel@tonic-gate /*
10737c478bd9Sstevel@tonic-gate * We define "progress" as either waiting on a timed break or delay, or
10747c478bd9Sstevel@tonic-gate * having had at least one transmitter interrupt. If none of these are
10757c478bd9Sstevel@tonic-gate * true, then just terminate the output and wake up that close thread.
10767c478bd9Sstevel@tonic-gate */
10777c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
10787c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
10797c478bd9Sstevel@tonic-gate if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) {
10807c478bd9Sstevel@tonic-gate async->async_ocnt = 0;
10817c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY;
10827c478bd9Sstevel@tonic-gate async->async_timer = 0;
10837c478bd9Sstevel@tonic-gate bp = async->async_xmitblk;
10847c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL;
10857c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
10867c478bd9Sstevel@tonic-gate if (bp != NULL)
10877c478bd9Sstevel@tonic-gate freeb(bp);
10887c478bd9Sstevel@tonic-gate /*
10897c478bd9Sstevel@tonic-gate * Since this timer is running, we know that we're in exit(2).
10907c478bd9Sstevel@tonic-gate * That means that the user can't possibly be waiting on any
10917c478bd9Sstevel@tonic-gate * valid ioctl(2) completion anymore, and we should just flush
10927c478bd9Sstevel@tonic-gate * everything.
10937c478bd9Sstevel@tonic-gate */
10947c478bd9Sstevel@tonic-gate flushq(async->async_ttycommon.t_writeq, FLUSHALL);
10957c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
10967c478bd9Sstevel@tonic-gate } else {
10977c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_PROGRESS;
10987c478bd9Sstevel@tonic-gate async->async_timer = timeout(async_progress_check, async,
10997c478bd9Sstevel@tonic-gate drv_usectohz(su_drain_check));
11007c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
11017c478bd9Sstevel@tonic-gate }
11027c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
11037c478bd9Sstevel@tonic-gate }
11047c478bd9Sstevel@tonic-gate
11057c478bd9Sstevel@tonic-gate /*
11067c478bd9Sstevel@tonic-gate * Close routine.
11077c478bd9Sstevel@tonic-gate */
11087c478bd9Sstevel@tonic-gate static int
asyclose(queue_t * q,int flag,cred_t * cr __unused)1109730a650aSPeter Tribble asyclose(queue_t *q, int flag, cred_t *cr __unused)
11107c478bd9Sstevel@tonic-gate {
11117c478bd9Sstevel@tonic-gate struct asyncline *async;
11127c478bd9Sstevel@tonic-gate struct asycom *asy;
11137c478bd9Sstevel@tonic-gate int icr, lcr;
11147c478bd9Sstevel@tonic-gate int nohupcl;
11157c478bd9Sstevel@tonic-gate
11167c478bd9Sstevel@tonic-gate
11177c478bd9Sstevel@tonic-gate #ifdef DEBUG
11187c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_CLOSE)
11197c478bd9Sstevel@tonic-gate printf("close\n");
11207c478bd9Sstevel@tonic-gate #endif
11217c478bd9Sstevel@tonic-gate async = q->q_ptr;
11227c478bd9Sstevel@tonic-gate ASSERT(async != NULL);
11237c478bd9Sstevel@tonic-gate asy = async->async_common;
11247c478bd9Sstevel@tonic-gate
11257c478bd9Sstevel@tonic-gate /* get the nohupcl OBP property of this device */
11267c478bd9Sstevel@tonic-gate nohupcl = ddi_getprop(DDI_DEV_T_ANY, asy->asy_dip, DDI_PROP_DONTPASS,
11277c478bd9Sstevel@tonic-gate "nohupcl", 0);
11287c478bd9Sstevel@tonic-gate
11297c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
11307c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_CLOSING;
11317c478bd9Sstevel@tonic-gate
11327c478bd9Sstevel@tonic-gate /*
11337c478bd9Sstevel@tonic-gate * Turn off PPS handling early to avoid events occuring during
11347c478bd9Sstevel@tonic-gate * close. Also reset the DCD edge monitoring bit.
11357c478bd9Sstevel@tonic-gate */
11367c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
11377c478bd9Sstevel@tonic-gate asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE);
11387c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
11397c478bd9Sstevel@tonic-gate
11407c478bd9Sstevel@tonic-gate /*
11417c478bd9Sstevel@tonic-gate * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
11427c478bd9Sstevel@tonic-gate * untimed (TIOCSBRK). For the timed case, these are enqueued on our
11437c478bd9Sstevel@tonic-gate * write queue and there's a timer running, so we don't have to worry
11447c478bd9Sstevel@tonic-gate * about them. For the untimed case, though, the user obviously made a
11457c478bd9Sstevel@tonic-gate * mistake, because these are handled immediately. We'll terminate the
114648bbca81SDaniel Hoffman * break now and honor their implicit request by discarding the rest of
11477c478bd9Sstevel@tonic-gate * the data.
11487c478bd9Sstevel@tonic-gate */
11497c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BREAK)) {
11507c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
11517c478bd9Sstevel@tonic-gate lcr = INB(LCR);
11527c478bd9Sstevel@tonic-gate if (lcr & SETBREAK) {
11537c478bd9Sstevel@tonic-gate OUTB(LCR, (lcr & ~SETBREAK));
11547c478bd9Sstevel@tonic-gate }
11557c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
11567c478bd9Sstevel@tonic-gate if (lcr & SETBREAK)
11577c478bd9Sstevel@tonic-gate goto nodrain;
11587c478bd9Sstevel@tonic-gate }
11597c478bd9Sstevel@tonic-gate
11607c478bd9Sstevel@tonic-gate /*
11617c478bd9Sstevel@tonic-gate * If the user told us not to delay the close ("non-blocking"), then
11627c478bd9Sstevel@tonic-gate * don't bother trying to drain.
11637c478bd9Sstevel@tonic-gate *
11647c478bd9Sstevel@tonic-gate * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
11657c478bd9Sstevel@tonic-gate * getting an M_START (since these messages aren't enqueued), and the
11667c478bd9Sstevel@tonic-gate * only other way to clear the stop condition is by loss of DCD, which
11677c478bd9Sstevel@tonic-gate * would discard the queue data. Thus, we drop the output data if
11687c478bd9Sstevel@tonic-gate * ASYNC_STOPPED is set.
11697c478bd9Sstevel@tonic-gate */
11707c478bd9Sstevel@tonic-gate if ((flag & (FNDELAY|FNONBLOCK)) ||
11717c478bd9Sstevel@tonic-gate (async->async_flags & ASYNC_STOPPED)) {
11727c478bd9Sstevel@tonic-gate goto nodrain;
11737c478bd9Sstevel@tonic-gate }
11747c478bd9Sstevel@tonic-gate
11757c478bd9Sstevel@tonic-gate /*
11767c478bd9Sstevel@tonic-gate * If there's any pending output, then we have to try to drain it.
11777c478bd9Sstevel@tonic-gate * There are two main cases to be handled:
11787c478bd9Sstevel@tonic-gate * - called by close(2): need to drain until done or until
11797c478bd9Sstevel@tonic-gate * a signal is received. No timeout.
11807c478bd9Sstevel@tonic-gate * - called by exit(2): need to drain while making progress
11817c478bd9Sstevel@tonic-gate * or until a timeout occurs. No signals.
11827c478bd9Sstevel@tonic-gate *
11837c478bd9Sstevel@tonic-gate * If we can't rely on receiving a signal to get us out of a hung
11847c478bd9Sstevel@tonic-gate * session, then we have to use a timer. In this case, we set a timer
11857c478bd9Sstevel@tonic-gate * to check for progress in sending the output data -- all that we ask
11867c478bd9Sstevel@tonic-gate * (at each interval) is that there's been some progress made. Since
11877c478bd9Sstevel@tonic-gate * the interrupt routine grabs buffers from the write queue, we can't
11887c478bd9Sstevel@tonic-gate * trust async_ocnt. Instead, we use a flag.
11897c478bd9Sstevel@tonic-gate *
11907c478bd9Sstevel@tonic-gate * Note that loss of carrier will cause the output queue to be flushed,
11917c478bd9Sstevel@tonic-gate * and we'll wake up again and finish normally.
11927c478bd9Sstevel@tonic-gate */
11937c478bd9Sstevel@tonic-gate if (!ddi_can_receive_sig() && su_drain_check != 0) {
11947c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_PROGRESS;
11957c478bd9Sstevel@tonic-gate async->async_timer = timeout(async_progress_check, async,
11967c478bd9Sstevel@tonic-gate drv_usectohz(su_drain_check));
11977c478bd9Sstevel@tonic-gate }
11987c478bd9Sstevel@tonic-gate
11997c478bd9Sstevel@tonic-gate while (async->async_ocnt > 0 ||
12007c478bd9Sstevel@tonic-gate async->async_ttycommon.t_writeq->q_first != NULL ||
12017c478bd9Sstevel@tonic-gate (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) {
12027c478bd9Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv, asy->asy_excl) == 0)
12037c478bd9Sstevel@tonic-gate break;
12047c478bd9Sstevel@tonic-gate }
12057c478bd9Sstevel@tonic-gate if (async->async_timer != 0) {
12067c478bd9Sstevel@tonic-gate (void) untimeout(async->async_timer);
12077c478bd9Sstevel@tonic-gate async->async_timer = 0;
12087c478bd9Sstevel@tonic-gate }
12097c478bd9Sstevel@tonic-gate
12107c478bd9Sstevel@tonic-gate nodrain:
12117c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
12127c478bd9Sstevel@tonic-gate
12137c478bd9Sstevel@tonic-gate /* turn off the loopback mode */
12147c478bd9Sstevel@tonic-gate if ((async->async_dev != rconsdev) &&
12157c478bd9Sstevel@tonic-gate (async->async_dev != kbddev) &&
12167c478bd9Sstevel@tonic-gate (async->async_dev != stdindev)) {
12177c478bd9Sstevel@tonic-gate OUTB(MCR, INB(MCR) & ~ ASY_LOOP);
12187c478bd9Sstevel@tonic-gate }
12197c478bd9Sstevel@tonic-gate
12207c478bd9Sstevel@tonic-gate async->async_ocnt = 0;
12217c478bd9Sstevel@tonic-gate if (async->async_xmitblk != NULL)
12227c478bd9Sstevel@tonic-gate freeb(async->async_xmitblk);
12237c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL;
12247c478bd9Sstevel@tonic-gate
12257c478bd9Sstevel@tonic-gate /*
12267c478bd9Sstevel@tonic-gate * If the "nohupcl" OBP property is set for this device, do
12277c478bd9Sstevel@tonic-gate * not turn off DTR and RTS no matter what. Otherwise, if the
12287c478bd9Sstevel@tonic-gate * line has HUPCL set or is incompletely opened, turn off DTR
12297c478bd9Sstevel@tonic-gate * and RTS to fix the modem line.
12307c478bd9Sstevel@tonic-gate */
12317c478bd9Sstevel@tonic-gate if (!nohupcl && ((async->async_ttycommon.t_cflag & HUPCL) ||
12327c478bd9Sstevel@tonic-gate (async->async_flags & ASYNC_WOPEN))) {
12337c478bd9Sstevel@tonic-gate /* turn off DTR, RTS but NOT interrupt to 386 */
12347c478bd9Sstevel@tonic-gate OUTB(MCR, OUT2);
12357c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
12367c478bd9Sstevel@tonic-gate /*
12377c478bd9Sstevel@tonic-gate * Don't let an interrupt in the middle of close
12387c478bd9Sstevel@tonic-gate * bounce us back to the top; just continue closing
12397c478bd9Sstevel@tonic-gate * as if nothing had happened.
12407c478bd9Sstevel@tonic-gate */
12417c478bd9Sstevel@tonic-gate if (cv_wait_sig(&lbolt_cv, asy->asy_excl) == 0)
12427c478bd9Sstevel@tonic-gate goto out;
12437c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
12447c478bd9Sstevel@tonic-gate }
12457c478bd9Sstevel@tonic-gate
12467c478bd9Sstevel@tonic-gate /*
12477c478bd9Sstevel@tonic-gate * If nobody's using it now, turn off receiver interrupts.
12487c478bd9Sstevel@tonic-gate */
12497c478bd9Sstevel@tonic-gate if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) {
12507c478bd9Sstevel@tonic-gate icr = INB(ICR);
12517c478bd9Sstevel@tonic-gate OUTB(ICR, (icr & ~RIEN));
12527c478bd9Sstevel@tonic-gate }
12537c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
12547c478bd9Sstevel@tonic-gate out:
12557c478bd9Sstevel@tonic-gate /*
12567c478bd9Sstevel@tonic-gate * Clear out device state.
12577c478bd9Sstevel@tonic-gate */
12587c478bd9Sstevel@tonic-gate async->async_flags = 0;
12597c478bd9Sstevel@tonic-gate ttycommon_close(&async->async_ttycommon);
12607c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
12617c478bd9Sstevel@tonic-gate
12627c478bd9Sstevel@tonic-gate /*
12637c478bd9Sstevel@tonic-gate * Clear ASY_DOINGSOFT and ASY_NEEDSOFT in case we were in
12647c478bd9Sstevel@tonic-gate * async_softint or an interrupt was pending when the process
12657c478bd9Sstevel@tonic-gate * using the port exited.
12667c478bd9Sstevel@tonic-gate */
12677c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
12687c478bd9Sstevel@tonic-gate
12697c478bd9Sstevel@tonic-gate /*
12707c478bd9Sstevel@tonic-gate * Cancel outstanding "bufcall" request.
12717c478bd9Sstevel@tonic-gate */
12727c478bd9Sstevel@tonic-gate if (async->async_wbufcid) {
12737c478bd9Sstevel@tonic-gate unbufcall(async->async_wbufcid);
12747c478bd9Sstevel@tonic-gate async->async_wbufcid = 0;
12757c478bd9Sstevel@tonic-gate }
12767c478bd9Sstevel@tonic-gate
12777c478bd9Sstevel@tonic-gate /*
12787c478bd9Sstevel@tonic-gate * If inperim is true, it means the port is closing while there's
12797c478bd9Sstevel@tonic-gate * a pending software interrupt. async_flags has been zeroed out,
12807c478bd9Sstevel@tonic-gate * so this instance of leaveq() needs to be called before we call
12817c478bd9Sstevel@tonic-gate * qprocsoff() to disable services on the q. If inperim is false,
12827c478bd9Sstevel@tonic-gate * leaveq() has already been called or we're not in a perimeter.
12837c478bd9Sstevel@tonic-gate */
12847c478bd9Sstevel@tonic-gate if (asy->inperim == B_TRUE) {
12857c478bd9Sstevel@tonic-gate asy->inperim = B_FALSE;
12867c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
12877c478bd9Sstevel@tonic-gate leaveq(q);
12887c478bd9Sstevel@tonic-gate } else {
12897c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
12907c478bd9Sstevel@tonic-gate }
12917c478bd9Sstevel@tonic-gate
12927c478bd9Sstevel@tonic-gate /* Note that qprocsoff can't be done until after interrupts are off */
12937c478bd9Sstevel@tonic-gate qprocsoff(q);
12947c478bd9Sstevel@tonic-gate q->q_ptr = WR(q)->q_ptr = NULL;
12957c478bd9Sstevel@tonic-gate async->async_ttycommon.t_readq = NULL;
12967c478bd9Sstevel@tonic-gate async->async_ttycommon.t_writeq = NULL;
12977c478bd9Sstevel@tonic-gate
12987c478bd9Sstevel@tonic-gate return (0);
12997c478bd9Sstevel@tonic-gate }
13007c478bd9Sstevel@tonic-gate
13017c478bd9Sstevel@tonic-gate /*
13027c478bd9Sstevel@tonic-gate * Checks to see if the serial port is still transmitting
13037c478bd9Sstevel@tonic-gate * characters. It returns true when there are characters
13047c478bd9Sstevel@tonic-gate * queued to transmit, when the holding register contains
13057c478bd9Sstevel@tonic-gate * a byte, or when the shifting register still contains
13067c478bd9Sstevel@tonic-gate * data to send.
13077c478bd9Sstevel@tonic-gate *
13087c478bd9Sstevel@tonic-gate */
13097c478bd9Sstevel@tonic-gate static boolean_t
asy_isbusy(struct asycom * asy)13107c478bd9Sstevel@tonic-gate asy_isbusy(struct asycom *asy)
13117c478bd9Sstevel@tonic-gate {
13127c478bd9Sstevel@tonic-gate struct asyncline *async;
13137c478bd9Sstevel@tonic-gate
13147c478bd9Sstevel@tonic-gate #ifdef DEBUG
13157c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_EOT)
13167c478bd9Sstevel@tonic-gate printf("isbusy\n");
13177c478bd9Sstevel@tonic-gate #endif
13187c478bd9Sstevel@tonic-gate async = (struct asyncline *)asy->asy_priv;
13197c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl));
13207c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl_hi));
13217c478bd9Sstevel@tonic-gate return ((async->async_ocnt > 0) ||
13220280efdcSzk ((INB(LSR) & XSRE) == 0));
13237c478bd9Sstevel@tonic-gate }
13247c478bd9Sstevel@tonic-gate
13257c478bd9Sstevel@tonic-gate /*
13267c478bd9Sstevel@tonic-gate * Program the ASY port. Most of the async operation is based on the values
13277c478bd9Sstevel@tonic-gate * of 'c_iflag' and 'c_cflag'.
13287c478bd9Sstevel@tonic-gate */
13297c478bd9Sstevel@tonic-gate static int
asy_program(struct asycom * asy,int mode)13307c478bd9Sstevel@tonic-gate asy_program(struct asycom *asy, int mode)
13317c478bd9Sstevel@tonic-gate {
13327c478bd9Sstevel@tonic-gate struct asyncline *async;
13337c478bd9Sstevel@tonic-gate int baudrate, c_flag;
13347c478bd9Sstevel@tonic-gate int icr, lcr;
13357c478bd9Sstevel@tonic-gate int ocflags;
13367c478bd9Sstevel@tonic-gate int error = 0;
13377c478bd9Sstevel@tonic-gate
13387c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl));
13397c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl_hi));
13407c478bd9Sstevel@tonic-gate
13417c478bd9Sstevel@tonic-gate #ifdef DEBUG
13427c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_PROCS)
13437c478bd9Sstevel@tonic-gate printf("program\n");
13447c478bd9Sstevel@tonic-gate #endif
13457c478bd9Sstevel@tonic-gate async = (struct asyncline *)asy->asy_priv;
13467c478bd9Sstevel@tonic-gate
13477c478bd9Sstevel@tonic-gate baudrate = async->async_ttycommon.t_cflag & CBAUD;
13487c478bd9Sstevel@tonic-gate if (async->async_ttycommon.t_cflag & CBAUDEXT)
13497c478bd9Sstevel@tonic-gate baudrate += 16;
13507c478bd9Sstevel@tonic-gate
13517c478bd9Sstevel@tonic-gate /* Limit baudrate so it can't index out of baudtable */
13527c478bd9Sstevel@tonic-gate if (baudrate >= N_SU_SPEEDS) baudrate = B9600;
13537c478bd9Sstevel@tonic-gate
13547c478bd9Sstevel@tonic-gate /*
13557c478bd9Sstevel@tonic-gate * If baud rate requested is greater than the speed cap
13567c478bd9Sstevel@tonic-gate * or is an unsupported baud rate then reset t_cflag baud
13577c478bd9Sstevel@tonic-gate * to the last valid baud rate. If this is the initial
13587c478bd9Sstevel@tonic-gate * pass through asy_program then set it to 9600.
13597c478bd9Sstevel@tonic-gate */
13607c478bd9Sstevel@tonic-gate if (((baudrate > 0) && (asyspdtab[baudrate] == 0)) ||
13617c478bd9Sstevel@tonic-gate (baudtable[baudrate] > asy->asy_speed_cap)) {
13627c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
13637c478bd9Sstevel@tonic-gate ~CIBAUD & ~CIBAUDEXT;
13647c478bd9Sstevel@tonic-gate if (mode == ASY_INIT) {
13657c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |= B9600;
1366b4a51ac1Skc async->async_ttycommon.t_cflag |= B9600 << IBSHIFT;
13677c478bd9Sstevel@tonic-gate baudrate = B9600;
13687c478bd9Sstevel@tonic-gate } else {
13697c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |=
13707c478bd9Sstevel@tonic-gate (asy->asy_ocflags & (CBAUD | CBAUDEXT |
13717c478bd9Sstevel@tonic-gate CIBAUD | CIBAUDEXT));
1372b4a51ac1Skc error = EINVAL;
1373b4a51ac1Skc goto end;
13747c478bd9Sstevel@tonic-gate }
13757c478bd9Sstevel@tonic-gate }
13767c478bd9Sstevel@tonic-gate
1377b4a51ac1Skc /*
1378b4a51ac1Skc * If CIBAUD and CIBAUDEXT are zero then we should set them to
1379b4a51ac1Skc * the equivelant output baud bits. Else, if CIBAUD and CIBAUDEXT
1380b4a51ac1Skc * don't match CBAUD and CBAUDEXT respectively then we should
1381b4a51ac1Skc * notify the requestor that we do not support split speeds.
1382b4a51ac1Skc */
1383b4a51ac1Skc if ((async->async_ttycommon.t_cflag & (CIBAUD|CIBAUDEXT)) == 0) {
1384b4a51ac1Skc async->async_ttycommon.t_cflag |=
1385b4a51ac1Skc (async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT;
1386b4a51ac1Skc if (async->async_ttycommon.t_cflag & CBAUDEXT)
13870280efdcSzk async->async_ttycommon.t_cflag |= CIBAUDEXT;
1388b4a51ac1Skc } else {
1389b4a51ac1Skc if ((((async->async_ttycommon.t_cflag & CBAUD) << IBSHIFT) !=
1390b4a51ac1Skc (async->async_ttycommon.t_cflag & CIBAUD)) ||
1391b4a51ac1Skc !(((async->async_ttycommon.t_cflag & (CBAUDEXT |
1392b4a51ac1Skc CIBAUDEXT)) == (CBAUDEXT | CIBAUDEXT)) ||
1393b4a51ac1Skc ((async->async_ttycommon.t_cflag & (CBAUDEXT |
1394b4a51ac1Skc CIBAUDEXT)) == 0))) {
1395b4a51ac1Skc async->async_ttycommon.t_cflag &= ~CBAUD & ~CBAUDEXT &
1396b4a51ac1Skc ~CIBAUD & ~CIBAUDEXT;
13977c478bd9Sstevel@tonic-gate async->async_ttycommon.t_cflag |=
1398b4a51ac1Skc (asy->asy_ocflags & (CBAUD | CBAUDEXT |
1399b4a51ac1Skc CIBAUD | CIBAUDEXT));
1400b4a51ac1Skc error = EINVAL;
1401b4a51ac1Skc goto end;
14027c478bd9Sstevel@tonic-gate }
14037c478bd9Sstevel@tonic-gate }
14047c478bd9Sstevel@tonic-gate
14057c478bd9Sstevel@tonic-gate c_flag = async->async_ttycommon.t_cflag &
14067c478bd9Sstevel@tonic-gate (CLOCAL | CREAD | CSTOPB | CSIZE | PARENB | PARODD | CBAUD |
14077c478bd9Sstevel@tonic-gate CBAUDEXT | CIBAUD | CIBAUDEXT);
140817ea09c7Sanovick
140917ea09c7Sanovick /* disable interrupts, see EXAR bug */
1410f63f7506Sanovick if (asy->asy_hwtype == ASY16C554D)
1411f63f7506Sanovick OUTB(SPR, 0);
141217ea09c7Sanovick OUTB(ICR, 0);
14137c478bd9Sstevel@tonic-gate
14147c478bd9Sstevel@tonic-gate ocflags = asy->asy_ocflags;
14157c478bd9Sstevel@tonic-gate
14167c478bd9Sstevel@tonic-gate /* flush/reset the status registers */
14177c478bd9Sstevel@tonic-gate if (mode == ASY_INIT) {
14187c478bd9Sstevel@tonic-gate (void) INB(DAT);
14197c478bd9Sstevel@tonic-gate (void) INB(ISR);
14207c478bd9Sstevel@tonic-gate (void) INB(LSR);
14217c478bd9Sstevel@tonic-gate (void) INB(MSR);
14227c478bd9Sstevel@tonic-gate }
14237c478bd9Sstevel@tonic-gate
14247c478bd9Sstevel@tonic-gate if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) {
14257c478bd9Sstevel@tonic-gate /* Set line control */
14267c478bd9Sstevel@tonic-gate lcr = INB(LCR);
14277c478bd9Sstevel@tonic-gate lcr &= ~(WLS0|WLS1|STB|PEN|EPS);
14287c478bd9Sstevel@tonic-gate
14297c478bd9Sstevel@tonic-gate if (c_flag & CSTOPB)
14307c478bd9Sstevel@tonic-gate lcr |= STB; /* 2 stop bits */
14317c478bd9Sstevel@tonic-gate
14327c478bd9Sstevel@tonic-gate if (c_flag & PARENB)
14337c478bd9Sstevel@tonic-gate lcr |= PEN;
14347c478bd9Sstevel@tonic-gate
14357c478bd9Sstevel@tonic-gate if ((c_flag & PARODD) == 0)
14367c478bd9Sstevel@tonic-gate lcr |= EPS;
14377c478bd9Sstevel@tonic-gate
14387c478bd9Sstevel@tonic-gate switch (c_flag & CSIZE) {
14397c478bd9Sstevel@tonic-gate case CS5:
14407c478bd9Sstevel@tonic-gate lcr |= BITS5;
14417c478bd9Sstevel@tonic-gate break;
14427c478bd9Sstevel@tonic-gate case CS6:
14437c478bd9Sstevel@tonic-gate lcr |= BITS6;
14447c478bd9Sstevel@tonic-gate break;
14457c478bd9Sstevel@tonic-gate case CS7:
14467c478bd9Sstevel@tonic-gate lcr |= BITS7;
14477c478bd9Sstevel@tonic-gate break;
14487c478bd9Sstevel@tonic-gate case CS8:
14497c478bd9Sstevel@tonic-gate lcr |= BITS8;
14507c478bd9Sstevel@tonic-gate break;
14517c478bd9Sstevel@tonic-gate }
14527c478bd9Sstevel@tonic-gate
14537c478bd9Sstevel@tonic-gate /* set the baud rate when the rate is NOT B0 */
14547c478bd9Sstevel@tonic-gate if (baudrate != 0) {
14557c478bd9Sstevel@tonic-gate OUTB(LCR, DLAB);
14567c478bd9Sstevel@tonic-gate OUTB(DAT, (asyspdtab[baudrate] *
14570280efdcSzk asy->asy_baud_divisor_factor) & 0xff);
14587c478bd9Sstevel@tonic-gate OUTB(ICR, ((asyspdtab[baudrate] *
14590280efdcSzk asy->asy_baud_divisor_factor) >> 8) & 0xff);
14607c478bd9Sstevel@tonic-gate }
14617c478bd9Sstevel@tonic-gate /* set the line control modes */
14627c478bd9Sstevel@tonic-gate OUTB(LCR, lcr);
14637c478bd9Sstevel@tonic-gate
14647c478bd9Sstevel@tonic-gate /*
14657c478bd9Sstevel@tonic-gate * if transitioning from CREAD off to CREAD on,
14667c478bd9Sstevel@tonic-gate * flush the FIFO buffer if we have one.
14677c478bd9Sstevel@tonic-gate */
14687c478bd9Sstevel@tonic-gate if ((ocflags & CREAD) == 0 && (c_flag & CREAD)) {
14697c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) {
14707c478bd9Sstevel@tonic-gate OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
14717c478bd9Sstevel@tonic-gate (asy->asy_trig_level & 0xff));
14727c478bd9Sstevel@tonic-gate }
14737c478bd9Sstevel@tonic-gate }
14747c478bd9Sstevel@tonic-gate
14757c478bd9Sstevel@tonic-gate /* remember the new cflags */
14767c478bd9Sstevel@tonic-gate asy->asy_ocflags = c_flag & ~CLOCAL;
14777c478bd9Sstevel@tonic-gate }
14787c478bd9Sstevel@tonic-gate
14797c478bd9Sstevel@tonic-gate /* whether or not CLOCAL is set, modify the modem control lines */
14807c478bd9Sstevel@tonic-gate if (baudrate == 0)
14817c478bd9Sstevel@tonic-gate /* B0 has been issued, lower DTR */
14827c478bd9Sstevel@tonic-gate OUTB(MCR, RTS|OUT2);
14837c478bd9Sstevel@tonic-gate else
14847c478bd9Sstevel@tonic-gate /* raise DTR */
14857c478bd9Sstevel@tonic-gate OUTB(MCR, DTR|RTS|OUT2);
14867c478bd9Sstevel@tonic-gate
14877c478bd9Sstevel@tonic-gate /*
14887c478bd9Sstevel@tonic-gate * Call the modem status interrupt handler to check for the carrier
14897c478bd9Sstevel@tonic-gate * in case CLOCAL was turned off after the carrier came on.
14907c478bd9Sstevel@tonic-gate * (Note: Modem status interrupt is not enabled if CLOCAL is ON.)
14917c478bd9Sstevel@tonic-gate */
14927c478bd9Sstevel@tonic-gate async_msint(asy);
14937c478bd9Sstevel@tonic-gate
14947c478bd9Sstevel@tonic-gate /* Set interrupt control */
14957c478bd9Sstevel@tonic-gate if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS))
14967c478bd9Sstevel@tonic-gate /*
14977c478bd9Sstevel@tonic-gate * direct-wired line ignores DCD, so we don't enable modem
14987c478bd9Sstevel@tonic-gate * status interrupts.
14997c478bd9Sstevel@tonic-gate */
15007c478bd9Sstevel@tonic-gate icr = (TIEN | SIEN);
15017c478bd9Sstevel@tonic-gate else
15027c478bd9Sstevel@tonic-gate icr = (TIEN | SIEN | MIEN);
15037c478bd9Sstevel@tonic-gate
15047c478bd9Sstevel@tonic-gate if (c_flag & CREAD)
15057c478bd9Sstevel@tonic-gate icr |= RIEN;
15067c478bd9Sstevel@tonic-gate
15077c478bd9Sstevel@tonic-gate OUTB(ICR, icr);
15087c478bd9Sstevel@tonic-gate end:
15097c478bd9Sstevel@tonic-gate return (error);
15107c478bd9Sstevel@tonic-gate }
15117c478bd9Sstevel@tonic-gate
15120280efdcSzk /*
15130280efdcSzk * Polled mode support -- all functions called with interrupts
15140280efdcSzk * disabled.
15150280efdcSzk */
15160280efdcSzk
15170280efdcSzk static void
asyputchar(cons_polledio_arg_t arg,uchar_t c)15180280efdcSzk asyputchar(cons_polledio_arg_t arg, uchar_t c)
15190280efdcSzk {
15200280efdcSzk struct asycom *asy = (struct asycom *)arg;
15210280efdcSzk
15220280efdcSzk /*
15230280efdcSzk * If we see a line feed make sure to also
15240280efdcSzk * put out a carriage return.
15250280efdcSzk */
15260280efdcSzk if (c == '\n')
15270280efdcSzk asyputchar(arg, '\r');
15280280efdcSzk
15290280efdcSzk while ((INB(LSR) & XHRE) == 0) {
15300280efdcSzk /* wait for the transmission to complete */
15310280efdcSzk drv_usecwait(10);
15320280efdcSzk }
15330280efdcSzk
15340280efdcSzk /* ouput the character */
15350280efdcSzk OUTB(DAT, c);
15360280efdcSzk }
15370280efdcSzk
15380280efdcSzk /*
15390280efdcSzk * Determines if there is a character avaialable for
15400280efdcSzk * reading.
15410280efdcSzk */
15420280efdcSzk static boolean_t
asyischar(cons_polledio_arg_t arg)15430280efdcSzk asyischar(cons_polledio_arg_t arg)
15440280efdcSzk {
15450280efdcSzk struct asycom *asy = (struct asycom *)arg;
15460280efdcSzk return ((INB(LSR) & RCA) != 0);
15470280efdcSzk }
15480280efdcSzk
15490280efdcSzk static int
asygetchar(cons_polledio_arg_t arg)15500280efdcSzk asygetchar(cons_polledio_arg_t arg)
15510280efdcSzk {
15520280efdcSzk struct asycom *asy = (struct asycom *)arg;
15530280efdcSzk
15540280efdcSzk /*
15550280efdcSzk * Spin waiting for a character to be
15560280efdcSzk * available to read.
15570280efdcSzk */
15580280efdcSzk while (!asyischar(arg))
15590280efdcSzk drv_usecwait(10);
15600280efdcSzk
15610280efdcSzk return (INB(DAT));
15620280efdcSzk }
15630280efdcSzk
15640280efdcSzk /*
15650280efdcSzk * Called when machine is transitioning to polled mode
15660280efdcSzk */
15670280efdcSzk static void
asy_polled_enter(cons_polledio_arg_t arg)15680280efdcSzk asy_polled_enter(cons_polledio_arg_t arg)
15690280efdcSzk {
15700280efdcSzk struct asycom *asy = (struct asycom *)arg;
15710280efdcSzk
15720280efdcSzk mutex_enter(asy->asy_excl);
15730280efdcSzk mutex_enter(asy->asy_excl_hi);
15740280efdcSzk
15750280efdcSzk /*
15760280efdcSzk * If this is the first time that asy_polled_enter()
15770280efdcSzk * has been called, during this transition request,
15780280efdcSzk * save the ICR. Clear the software interrupt
15790280efdcSzk * flags since we won't be able to handle these when
15800280efdcSzk * we are in polled mode.
15810280efdcSzk */
15820280efdcSzk if (!asy->polled_enter) {
15830280efdcSzk asy->polled_enter = B_TRUE;
15840280efdcSzk asy->polled_icr = INB(ICR);
15850280efdcSzk
15860280efdcSzk /* Disable HW interrupts */
15870280efdcSzk if (asy->asy_hwtype == ASY16C554D)
15880280efdcSzk OUTB(SPR, 0);
15890280efdcSzk OUTB(ICR, 0);
15900280efdcSzk
15910280efdcSzk asy->asy_flags &= ~ASY_DOINGSOFT & ~ASY_NEEDSOFT;
15920280efdcSzk }
15930280efdcSzk mutex_exit(asy->asy_excl_hi);
15940280efdcSzk mutex_exit(asy->asy_excl);
15950280efdcSzk }
15960280efdcSzk
15970280efdcSzk /*
15980280efdcSzk * Called when machine is transitioning from polled mode.
15990280efdcSzk */
16000280efdcSzk static void
asy_polled_exit(cons_polledio_arg_t arg)16010280efdcSzk asy_polled_exit(cons_polledio_arg_t arg)
16020280efdcSzk {
16030280efdcSzk struct asycom *asy = (struct asycom *)arg;
16040280efdcSzk
16050280efdcSzk mutex_enter(asy->asy_excl);
16060280efdcSzk mutex_enter(asy->asy_excl_hi);
16070280efdcSzk
16080280efdcSzk /* Restore the ICR */
16090280efdcSzk OUTB(ICR, asy->polled_icr);
16100280efdcSzk
16110280efdcSzk /*
16120280efdcSzk * We have finished this polled IO transition.
16130280efdcSzk * Set polled_enter to B_FALSE to note this.
16140280efdcSzk */
16150280efdcSzk asy->polled_enter = B_FALSE;
16160280efdcSzk mutex_exit(asy->asy_excl_hi);
16170280efdcSzk mutex_exit(asy->asy_excl);
16180280efdcSzk }
16190280efdcSzk
16207c478bd9Sstevel@tonic-gate /*
16217c478bd9Sstevel@tonic-gate * asyintr() is the High Level Interrupt Handler.
16227c478bd9Sstevel@tonic-gate *
16237c478bd9Sstevel@tonic-gate * There are four different interrupt types indexed by ISR register values:
16247c478bd9Sstevel@tonic-gate * 0: modem
16257c478bd9Sstevel@tonic-gate * 1: Tx holding register is empty, ready for next char
16267c478bd9Sstevel@tonic-gate * 2: Rx register now holds a char to be picked up
16277c478bd9Sstevel@tonic-gate * 3: error or break on line
16287c478bd9Sstevel@tonic-gate * This routine checks the Bit 0 (interrupt-not-pending) to determine if
16297c478bd9Sstevel@tonic-gate * the interrupt is from this port.
16307c478bd9Sstevel@tonic-gate */
16317c478bd9Sstevel@tonic-gate uint_t
asyintr(caddr_t argasy)16327c478bd9Sstevel@tonic-gate asyintr(caddr_t argasy)
16337c478bd9Sstevel@tonic-gate {
16347c478bd9Sstevel@tonic-gate struct asycom *asy = (struct asycom *)argasy;
16357c478bd9Sstevel@tonic-gate struct asyncline *async;
16367c478bd9Sstevel@tonic-gate int ret_status = DDI_INTR_UNCLAIMED;
16377c478bd9Sstevel@tonic-gate uchar_t interrupt_id, lsr;
16387c478bd9Sstevel@tonic-gate
16397c478bd9Sstevel@tonic-gate interrupt_id = INB(ISR) & 0x0F;
16407c478bd9Sstevel@tonic-gate async = (struct asyncline *)asy->asy_priv;
16417c478bd9Sstevel@tonic-gate if ((async == NULL) ||
16420280efdcSzk !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
16437c478bd9Sstevel@tonic-gate if (interrupt_id & NOINTERRUPT) {
16447c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED);
16457c478bd9Sstevel@tonic-gate } else {
16467c478bd9Sstevel@tonic-gate lsr = INB(LSR);
16477c478bd9Sstevel@tonic-gate if ((lsr & BRKDET) &&
16487c478bd9Sstevel@tonic-gate ((abort_enable == KIOCABORTENABLE) &&
16497c478bd9Sstevel@tonic-gate (async->async_dev == rconsdev)))
16507c478bd9Sstevel@tonic-gate abort_sequence_enter((char *)NULL);
16517c478bd9Sstevel@tonic-gate else {
16527c478bd9Sstevel@tonic-gate /* reset line status */
16537c478bd9Sstevel@tonic-gate (void) INB(LSR);
16547c478bd9Sstevel@tonic-gate /* discard any data */
16557c478bd9Sstevel@tonic-gate (void) INB(DAT);
16567c478bd9Sstevel@tonic-gate /* reset modem status */
16577c478bd9Sstevel@tonic-gate (void) INB(MSR);
16587c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
16597c478bd9Sstevel@tonic-gate }
16607c478bd9Sstevel@tonic-gate }
16617c478bd9Sstevel@tonic-gate }
16627c478bd9Sstevel@tonic-gate /*
16637c478bd9Sstevel@tonic-gate * Spurious interrupts happen in this driver
16647c478bd9Sstevel@tonic-gate * because of the transmission on serial port not handled
16657c478bd9Sstevel@tonic-gate * properly.
16667c478bd9Sstevel@tonic-gate *
16677c478bd9Sstevel@tonic-gate * The reasons for Spurious interrupts are:
16687c478bd9Sstevel@tonic-gate * 1. There is a path in async_nstart which transmits
16697c478bd9Sstevel@tonic-gate * characters without going through interrupt services routine
16707c478bd9Sstevel@tonic-gate * which causes spurious interrupts to happen.
16717c478bd9Sstevel@tonic-gate * 2. In the async_txint more than one character is sent
16727c478bd9Sstevel@tonic-gate * in one interrupt service.
16737c478bd9Sstevel@tonic-gate * 3. In async_rxint more than one characters are received in
16747c478bd9Sstevel@tonic-gate * in one interrupt service.
16757c478bd9Sstevel@tonic-gate *
16767c478bd9Sstevel@tonic-gate * Hence we have flags to indicate that such scenerio has happened.
16777c478bd9Sstevel@tonic-gate * and claim only such interrupts and others we donot claim it
16787c478bd9Sstevel@tonic-gate * as it could be a indicator of some hardware problem.
16797c478bd9Sstevel@tonic-gate *
16807c478bd9Sstevel@tonic-gate */
16817c478bd9Sstevel@tonic-gate if (interrupt_id & NOINTERRUPT) {
16827c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
16837c478bd9Sstevel@tonic-gate if ((asy->asy_xmit_count > 1) ||
16840280efdcSzk (asy->asy_out_of_band_xmit > 0) ||
16850280efdcSzk (asy->asy_rx_count > 1)) {
16867c478bd9Sstevel@tonic-gate asy->asy_xmit_count = 0;
16877c478bd9Sstevel@tonic-gate asy->asy_out_of_band_xmit = 0;
16887c478bd9Sstevel@tonic-gate asy->asy_rx_count = 0;
16897c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
16907c478bd9Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
16917c478bd9Sstevel@tonic-gate } else {
16927c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
16937c478bd9Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED);
16947c478bd9Sstevel@tonic-gate }
16957c478bd9Sstevel@tonic-gate }
16967c478bd9Sstevel@tonic-gate ret_status = DDI_INTR_CLAIMED;
16977c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
16987c478bd9Sstevel@tonic-gate if (asy->asy_hwtype == ASY82510)
16997c478bd9Sstevel@tonic-gate OUTB(ISR, 0x00); /* set bank 0 */
17007c478bd9Sstevel@tonic-gate
17017c478bd9Sstevel@tonic-gate #ifdef DEBUG
17027c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_INTR)
17037c478bd9Sstevel@tonic-gate prom_printf("l");
17047c478bd9Sstevel@tonic-gate #endif
17057c478bd9Sstevel@tonic-gate lsr = INB(LSR);
17067c478bd9Sstevel@tonic-gate switch (interrupt_id) {
17077c478bd9Sstevel@tonic-gate case RxRDY:
17087c478bd9Sstevel@tonic-gate case RSTATUS:
17097c478bd9Sstevel@tonic-gate case FFTMOUT:
17107c478bd9Sstevel@tonic-gate /* receiver interrupt or receiver errors */
17117c478bd9Sstevel@tonic-gate async_rxint(asy, lsr);
17127c478bd9Sstevel@tonic-gate break;
17137c478bd9Sstevel@tonic-gate case TxRDY:
17147c478bd9Sstevel@tonic-gate /* transmit interrupt */
17157c478bd9Sstevel@tonic-gate async_txint(asy, lsr);
17167c478bd9Sstevel@tonic-gate break;
17177c478bd9Sstevel@tonic-gate case MSTATUS:
17187c478bd9Sstevel@tonic-gate /* modem status interrupt */
17197c478bd9Sstevel@tonic-gate async_msint(asy);
17207c478bd9Sstevel@tonic-gate break;
17217c478bd9Sstevel@tonic-gate }
17227c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
17237c478bd9Sstevel@tonic-gate return (ret_status);
17247c478bd9Sstevel@tonic-gate }
17257c478bd9Sstevel@tonic-gate
17267c478bd9Sstevel@tonic-gate /*
17277c478bd9Sstevel@tonic-gate * Transmitter interrupt service routine.
17287c478bd9Sstevel@tonic-gate * If there is more data to transmit in the current pseudo-DMA block,
17297c478bd9Sstevel@tonic-gate * send the next character if output is not stopped or draining.
17307c478bd9Sstevel@tonic-gate * Otherwise, queue up a soft interrupt.
17317c478bd9Sstevel@tonic-gate *
17327c478bd9Sstevel@tonic-gate * XXX - Needs review for HW FIFOs.
17337c478bd9Sstevel@tonic-gate */
17347c478bd9Sstevel@tonic-gate static void
async_txint(struct asycom * asy,uchar_t lsr)17357c478bd9Sstevel@tonic-gate async_txint(struct asycom *asy, uchar_t lsr)
17367c478bd9Sstevel@tonic-gate {
17377c478bd9Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)asy->asy_priv;
17387c478bd9Sstevel@tonic-gate int fifo_len;
17397c478bd9Sstevel@tonic-gate int xmit_progress;
17407c478bd9Sstevel@tonic-gate
17417c478bd9Sstevel@tonic-gate asycheckflowcontrol_hw(asy);
17427c478bd9Sstevel@tonic-gate
17437c478bd9Sstevel@tonic-gate /*
17447c478bd9Sstevel@tonic-gate * If ASYNC_BREAK has been set, return to asyintr()'s context to
17457c478bd9Sstevel@tonic-gate * claim the interrupt without performing any action.
17467c478bd9Sstevel@tonic-gate */
17477c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_BREAK)
17487c478bd9Sstevel@tonic-gate return;
17497c478bd9Sstevel@tonic-gate
17507c478bd9Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
17517c478bd9Sstevel@tonic-gate
17527c478bd9Sstevel@tonic-gate /*
17537c478bd9Sstevel@tonic-gate * Check for flow control and do the needed action.
17547c478bd9Sstevel@tonic-gate */
17557c478bd9Sstevel@tonic-gate if (asycheckflowcontrol_sw(asy)) {
17567c478bd9Sstevel@tonic-gate return;
17577c478bd9Sstevel@tonic-gate }
17587c478bd9Sstevel@tonic-gate
17597c478bd9Sstevel@tonic-gate if (async->async_ocnt > 0 &&
17607c478bd9Sstevel@tonic-gate !(async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED))) {
17617c478bd9Sstevel@tonic-gate xmit_progress = 0;
17627c478bd9Sstevel@tonic-gate while (fifo_len > 0 && async->async_ocnt > 0) {
17637c478bd9Sstevel@tonic-gate if (lsr & XHRE) {
17647c478bd9Sstevel@tonic-gate OUTB(DAT, *async->async_optr++);
17657c478bd9Sstevel@tonic-gate fifo_len--;
17667c478bd9Sstevel@tonic-gate async->async_ocnt--;
17677c478bd9Sstevel@tonic-gate xmit_progress++;
17687c478bd9Sstevel@tonic-gate }
17697c478bd9Sstevel@tonic-gate /*
17707c478bd9Sstevel@tonic-gate * Reading the lsr, (moved reading at the end of
17717c478bd9Sstevel@tonic-gate * while loop) as already we have read once at
17727c478bd9Sstevel@tonic-gate * the beginning of interrupt service
17737c478bd9Sstevel@tonic-gate */
17747c478bd9Sstevel@tonic-gate lsr = INB(LSR);
17757c478bd9Sstevel@tonic-gate }
17767c478bd9Sstevel@tonic-gate asy->asy_xmit_count = xmit_progress;
17777c478bd9Sstevel@tonic-gate if (xmit_progress > 0)
17787c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS;
17797c478bd9Sstevel@tonic-gate }
17807c478bd9Sstevel@tonic-gate
17817c478bd9Sstevel@tonic-gate if (fifo_len == 0) {
17827c478bd9Sstevel@tonic-gate return;
17837c478bd9Sstevel@tonic-gate }
17847c478bd9Sstevel@tonic-gate
17857c478bd9Sstevel@tonic-gate
17867c478bd9Sstevel@tonic-gate ASYSETSOFT(asy);
17877c478bd9Sstevel@tonic-gate }
17887c478bd9Sstevel@tonic-gate
17897c478bd9Sstevel@tonic-gate /*
17907c478bd9Sstevel@tonic-gate * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive
17917c478bd9Sstevel@tonic-gate * error interrupt.
17927c478bd9Sstevel@tonic-gate * Try to put the character into the circular buffer for this line; if it
17937c478bd9Sstevel@tonic-gate * overflows, indicate a circular buffer overrun. If this port is always
17947c478bd9Sstevel@tonic-gate * to be serviced immediately, or the character is a STOP character, or
17957c478bd9Sstevel@tonic-gate * more than 15 characters have arrived, queue up a soft interrupt to
17967c478bd9Sstevel@tonic-gate * drain the circular buffer.
17977c478bd9Sstevel@tonic-gate * XXX - needs review for hw FIFOs support.
17987c478bd9Sstevel@tonic-gate */
17997c478bd9Sstevel@tonic-gate
18007c478bd9Sstevel@tonic-gate static void
async_rxint(struct asycom * asy,uchar_t lsr)18017c478bd9Sstevel@tonic-gate async_rxint(struct asycom *asy, uchar_t lsr)
18027c478bd9Sstevel@tonic-gate {
18037c478bd9Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)asy->asy_priv;
18047c478bd9Sstevel@tonic-gate uchar_t c = 0;
18057c478bd9Sstevel@tonic-gate uint_t s = 0, needsoft = 0;
18067c478bd9Sstevel@tonic-gate register tty_common_t *tp;
18077c478bd9Sstevel@tonic-gate
18087c478bd9Sstevel@tonic-gate tp = &async->async_ttycommon;
18097c478bd9Sstevel@tonic-gate if (!(tp->t_cflag & CREAD)) {
18107c478bd9Sstevel@tonic-gate if (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
18117c478bd9Sstevel@tonic-gate (void) (INB(DAT) & 0xff);
18127c478bd9Sstevel@tonic-gate }
18137c478bd9Sstevel@tonic-gate return; /* line is not open for read? */
18147c478bd9Sstevel@tonic-gate }
18157c478bd9Sstevel@tonic-gate asy->asy_rx_count = 0;
18167c478bd9Sstevel@tonic-gate while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
18177c478bd9Sstevel@tonic-gate c = 0;
18187c478bd9Sstevel@tonic-gate s = 0;
18197c478bd9Sstevel@tonic-gate asy->asy_rx_count++;
18207c478bd9Sstevel@tonic-gate if (lsr & RCA) {
18217c478bd9Sstevel@tonic-gate c = INB(DAT) & 0xff;
18227c478bd9Sstevel@tonic-gate /*
18237c478bd9Sstevel@tonic-gate * Even a single character is received
18247c478bd9Sstevel@tonic-gate * we need Soft interrupt to pass it to
18257c478bd9Sstevel@tonic-gate * higher layers.
18267c478bd9Sstevel@tonic-gate */
18277c478bd9Sstevel@tonic-gate needsoft = 1;
18287c478bd9Sstevel@tonic-gate }
18297c478bd9Sstevel@tonic-gate
18307c478bd9Sstevel@tonic-gate /* Check for character break sequence */
18317c478bd9Sstevel@tonic-gate if ((abort_enable == KIOCABORTALTERNATE) &&
18327c478bd9Sstevel@tonic-gate (async->async_dev == rconsdev)) {
18337c478bd9Sstevel@tonic-gate if (abort_charseq_recognize(c))
18347c478bd9Sstevel@tonic-gate abort_sequence_enter((char *)NULL);
18357c478bd9Sstevel@tonic-gate }
18367c478bd9Sstevel@tonic-gate
18377c478bd9Sstevel@tonic-gate /* Handle framing errors */
18387c478bd9Sstevel@tonic-gate if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) {
18397c478bd9Sstevel@tonic-gate if (lsr & PARERR) {
18407c478bd9Sstevel@tonic-gate if (tp->t_iflag & INPCK) /* parity enabled */
18417c478bd9Sstevel@tonic-gate s |= PERROR;
18427c478bd9Sstevel@tonic-gate }
18437c478bd9Sstevel@tonic-gate if (lsr & (FRMERR|BRKDET))
18447c478bd9Sstevel@tonic-gate s |= FRERROR;
18457c478bd9Sstevel@tonic-gate if (lsr & OVRRUN) {
18467c478bd9Sstevel@tonic-gate async->async_hw_overrun = 1;
18477c478bd9Sstevel@tonic-gate s |= OVERRUN;
18487c478bd9Sstevel@tonic-gate }
18497c478bd9Sstevel@tonic-gate }
18507c478bd9Sstevel@tonic-gate
18517c478bd9Sstevel@tonic-gate if (s == 0)
18527c478bd9Sstevel@tonic-gate if ((tp->t_iflag & PARMRK) &&
18530280efdcSzk !(tp->t_iflag & (IGNPAR|ISTRIP)) &&
18540280efdcSzk (c == 0377))
18557c478bd9Sstevel@tonic-gate if (RING_POK(async, 2)) {
18567c478bd9Sstevel@tonic-gate RING_PUT(async, 0377);
18577c478bd9Sstevel@tonic-gate RING_PUT(async, c);
18587c478bd9Sstevel@tonic-gate } else
18597c478bd9Sstevel@tonic-gate async->async_sw_overrun = 1;
18607c478bd9Sstevel@tonic-gate else
18617c478bd9Sstevel@tonic-gate if (RING_POK(async, 1))
18627c478bd9Sstevel@tonic-gate RING_PUT(async, c);
18637c478bd9Sstevel@tonic-gate else
18647c478bd9Sstevel@tonic-gate async->async_sw_overrun = 1;
18657c478bd9Sstevel@tonic-gate else
18667c478bd9Sstevel@tonic-gate if (s & FRERROR) { /* Handle framing errors */
18677c478bd9Sstevel@tonic-gate if (c == 0) {
18687c478bd9Sstevel@tonic-gate /* Look for break on kbd, stdin, or rconsdev */
18697c478bd9Sstevel@tonic-gate if ((async->async_dev == kbddev) ||
18707c478bd9Sstevel@tonic-gate ((async->async_dev == rconsdev) ||
18717c478bd9Sstevel@tonic-gate (async->async_dev == stdindev)) &&
18727c478bd9Sstevel@tonic-gate (abort_enable !=
18737c478bd9Sstevel@tonic-gate KIOCABORTALTERNATE))
18747c478bd9Sstevel@tonic-gate abort_sequence_enter((char *)0);
18757c478bd9Sstevel@tonic-gate else
18767c478bd9Sstevel@tonic-gate async->async_break++;
18777c478bd9Sstevel@tonic-gate } else {
18787c478bd9Sstevel@tonic-gate if (RING_POK(async, 1))
18797c478bd9Sstevel@tonic-gate RING_MARK(async, c, s);
18807c478bd9Sstevel@tonic-gate else
18817c478bd9Sstevel@tonic-gate async->async_sw_overrun = 1;
18827c478bd9Sstevel@tonic-gate }
18837c478bd9Sstevel@tonic-gate } else { /* Parity errors handled by ldterm */
18847c478bd9Sstevel@tonic-gate if (RING_POK(async, 1))
18857c478bd9Sstevel@tonic-gate RING_MARK(async, c, s);
18867c478bd9Sstevel@tonic-gate else
18877c478bd9Sstevel@tonic-gate async->async_sw_overrun = 1;
18887c478bd9Sstevel@tonic-gate }
18897c478bd9Sstevel@tonic-gate lsr = INB(LSR);
18907c478bd9Sstevel@tonic-gate if (asy->asy_rx_count > 16) break;
18917c478bd9Sstevel@tonic-gate }
18927c478bd9Sstevel@tonic-gate /* Check whether there is a request for hw/sw inbound/input flow ctrl */
18937c478bd9Sstevel@tonic-gate if ((async->async_ttycommon.t_cflag & CRTSXOFF) ||
18940280efdcSzk (async->async_ttycommon.t_iflag & IXOFF))
18957c478bd9Sstevel@tonic-gate if ((int)(RING_CNT(async)) > (RINGSIZE * 3)/4) {
18967c478bd9Sstevel@tonic-gate #ifdef DEBUG
18977c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_HFLOW)
18987c478bd9Sstevel@tonic-gate printf("asy%d: hardware flow stop input.\n",
18990280efdcSzk UNIT(async->async_dev));
19007c478bd9Sstevel@tonic-gate #endif
19017c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_HW_IN_FLOW;
19027c478bd9Sstevel@tonic-gate async->async_flowc = async->async_stopc;
19037c478bd9Sstevel@tonic-gate async->async_ringbuf_overflow = 1;
19047c478bd9Sstevel@tonic-gate }
19057c478bd9Sstevel@tonic-gate
19067c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft ||
19070280efdcSzk (RING_FRAC(async)) || (async->async_polltid == 0))
19087c478bd9Sstevel@tonic-gate ASYSETSOFT(asy); /* need a soft interrupt */
19097c478bd9Sstevel@tonic-gate }
19107c478bd9Sstevel@tonic-gate
19117c478bd9Sstevel@tonic-gate /*
19127c478bd9Sstevel@tonic-gate * Interrupt on port: handle PPS event. This function is only called
19137c478bd9Sstevel@tonic-gate * for a port on which PPS event handling has been enabled.
19147c478bd9Sstevel@tonic-gate */
19157c478bd9Sstevel@tonic-gate static void
asy_ppsevent(struct asycom * asy,int msr)19167c478bd9Sstevel@tonic-gate asy_ppsevent(struct asycom *asy, int msr)
19177c478bd9Sstevel@tonic-gate {
19187c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS_EDGE) {
19197c478bd9Sstevel@tonic-gate /* Have seen leading edge, now look for and record drop */
19207c478bd9Sstevel@tonic-gate if ((msr & DCD) == 0)
19217c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE;
19227c478bd9Sstevel@tonic-gate /*
19237c478bd9Sstevel@tonic-gate * Waiting for leading edge, look for rise; stamp event and
19247c478bd9Sstevel@tonic-gate * calibrate kernel clock.
19257c478bd9Sstevel@tonic-gate */
19267c478bd9Sstevel@tonic-gate } else if (msr & DCD) {
19277c478bd9Sstevel@tonic-gate /*
19287c478bd9Sstevel@tonic-gate * This code captures a timestamp at the designated
19297c478bd9Sstevel@tonic-gate * transition of the PPS signal (DCD asserted). The
19307c478bd9Sstevel@tonic-gate * code provides a pointer to the timestamp, as well
19317c478bd9Sstevel@tonic-gate * as the hardware counter value at the capture.
19327c478bd9Sstevel@tonic-gate *
19337c478bd9Sstevel@tonic-gate * Note: the kernel has nano based time values while
19347c478bd9Sstevel@tonic-gate * NTP requires micro based, an in-line fast algorithm
19357c478bd9Sstevel@tonic-gate * to convert nsec to usec is used here -- see hrt2ts()
19367c478bd9Sstevel@tonic-gate * in common/os/timers.c for a full description.
19377c478bd9Sstevel@tonic-gate */
19387c478bd9Sstevel@tonic-gate struct timeval *tvp = &asy_ppsev.tv;
19397c478bd9Sstevel@tonic-gate timestruc_t ts;
19407c478bd9Sstevel@tonic-gate long nsec, usec;
19417c478bd9Sstevel@tonic-gate
19427c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_PPS_EDGE;
19437c478bd9Sstevel@tonic-gate gethrestime(&ts);
19447c478bd9Sstevel@tonic-gate nsec = ts.tv_nsec;
19457c478bd9Sstevel@tonic-gate usec = nsec + (nsec >> 2);
19467c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 1);
19477c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 2);
19487c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 4);
19497c478bd9Sstevel@tonic-gate usec = nsec - (usec >> 3);
19507c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 2);
19517c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 3);
19527c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 4);
19537c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 1);
19547c478bd9Sstevel@tonic-gate usec = nsec + (usec >> 6);
19557c478bd9Sstevel@tonic-gate tvp->tv_usec = usec >> 10;
19567c478bd9Sstevel@tonic-gate tvp->tv_sec = ts.tv_sec;
19577c478bd9Sstevel@tonic-gate
19587c478bd9Sstevel@tonic-gate ++asy_ppsev.serial;
19597c478bd9Sstevel@tonic-gate
19607c478bd9Sstevel@tonic-gate /*
19617c478bd9Sstevel@tonic-gate * Because the kernel keeps a high-resolution time,
19627c478bd9Sstevel@tonic-gate * pass the current highres timestamp in tvp and zero
19637c478bd9Sstevel@tonic-gate * in usec.
19647c478bd9Sstevel@tonic-gate */
19657c478bd9Sstevel@tonic-gate ddi_hardpps(tvp, 0);
19667c478bd9Sstevel@tonic-gate }
19677c478bd9Sstevel@tonic-gate }
19687c478bd9Sstevel@tonic-gate
19697c478bd9Sstevel@tonic-gate /*
19707c478bd9Sstevel@tonic-gate * Modem status interrupt.
19717c478bd9Sstevel@tonic-gate *
19727c478bd9Sstevel@tonic-gate * (Note: It is assumed that the MSR hasn't been read by asyintr().)
19737c478bd9Sstevel@tonic-gate */
19747c478bd9Sstevel@tonic-gate
19757c478bd9Sstevel@tonic-gate static void
async_msint(struct asycom * asy)19767c478bd9Sstevel@tonic-gate async_msint(struct asycom *asy)
19777c478bd9Sstevel@tonic-gate {
19787c478bd9Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)asy->asy_priv;
19797c478bd9Sstevel@tonic-gate int msr;
19807c478bd9Sstevel@tonic-gate
19817c478bd9Sstevel@tonic-gate msr = INB(MSR); /* this resets the interrupt */
19827c478bd9Sstevel@tonic-gate asy->asy_cached_msr = msr;
19837c478bd9Sstevel@tonic-gate #ifdef DEBUG
19847c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_STATE) {
19857c478bd9Sstevel@tonic-gate printf(" transition: %3s %3s %3s %3s\n"
19860280efdcSzk "current state: %3s %3s %3s %3s\n",
19870280efdcSzk (msr & DCTS) ? "CTS" : " ",
19880280efdcSzk (msr & DDSR) ? "DSR" : " ",
19890280efdcSzk (msr & DRI) ? "RI " : " ",
19900280efdcSzk (msr & DDCD) ? "DCD" : " ",
19910280efdcSzk (msr & CTS) ? "CTS" : " ",
19920280efdcSzk (msr & DSR) ? "DSR" : " ",
19930280efdcSzk (msr & RI) ? "RI " : " ",
19940280efdcSzk (msr & DCD) ? "DCD" : " ");
19957c478bd9Sstevel@tonic-gate }
19967c478bd9Sstevel@tonic-gate #endif
19977c478bd9Sstevel@tonic-gate if (async->async_ttycommon.t_cflag & CRTSCTS && !(msr & CTS)) {
19987c478bd9Sstevel@tonic-gate #ifdef DEBUG
19997c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_HFLOW)
20007c478bd9Sstevel@tonic-gate printf("asy%d: hflow start\n",
20010280efdcSzk UNIT(async->async_dev));
20027c478bd9Sstevel@tonic-gate #endif
20037c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_HW_OUT_FLW;
20047c478bd9Sstevel@tonic-gate }
20057c478bd9Sstevel@tonic-gate if (asy->asy_hwtype == ASY82510)
20067c478bd9Sstevel@tonic-gate OUTB(MSR, (msr & 0xF0));
20077c478bd9Sstevel@tonic-gate
20087c478bd9Sstevel@tonic-gate /* Handle PPS event */
20097c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS)
20107c478bd9Sstevel@tonic-gate asy_ppsevent(asy, msr);
20117c478bd9Sstevel@tonic-gate
20127c478bd9Sstevel@tonic-gate async->async_ext++;
20137c478bd9Sstevel@tonic-gate ASYSETSOFT(asy);
20147c478bd9Sstevel@tonic-gate }
20157c478bd9Sstevel@tonic-gate
20167c478bd9Sstevel@tonic-gate /*
20177c478bd9Sstevel@tonic-gate * Handle a second-stage interrupt.
20187c478bd9Sstevel@tonic-gate */
20197c478bd9Sstevel@tonic-gate uint_t
asysoftintr(caddr_t intarg)20207c478bd9Sstevel@tonic-gate asysoftintr(caddr_t intarg)
20217c478bd9Sstevel@tonic-gate {
20227c478bd9Sstevel@tonic-gate struct asycom *asy = (struct asycom *)intarg;
20237c478bd9Sstevel@tonic-gate struct asyncline *async;
20247c478bd9Sstevel@tonic-gate int rv;
20257c478bd9Sstevel@tonic-gate int cc;
20267c478bd9Sstevel@tonic-gate /*
20277c478bd9Sstevel@tonic-gate * Test and clear soft interrupt.
20287c478bd9Sstevel@tonic-gate */
20297c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_soft_lock);
20307c478bd9Sstevel@tonic-gate #ifdef DEBUG
20317c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_PROCS)
20327c478bd9Sstevel@tonic-gate printf("softintr\n");
20337c478bd9Sstevel@tonic-gate #endif
20347c478bd9Sstevel@tonic-gate rv = asy->asysoftpend;
20357c478bd9Sstevel@tonic-gate if (rv != 0)
20367c478bd9Sstevel@tonic-gate asy->asysoftpend = 0;
20377c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_soft_lock);
20387c478bd9Sstevel@tonic-gate
20397c478bd9Sstevel@tonic-gate if (rv) {
20407c478bd9Sstevel@tonic-gate if (asy->asy_priv == NULL)
20417c478bd9Sstevel@tonic-gate return (rv);
20427c478bd9Sstevel@tonic-gate async = (struct asyncline *)asy->asy_priv;
20437c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
20447c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_NEEDSOFT) {
20457c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_NEEDSOFT;
20467c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
20477c478bd9Sstevel@tonic-gate (void) async_softint(asy);
20487c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
20497c478bd9Sstevel@tonic-gate }
20507c478bd9Sstevel@tonic-gate /*
20517c478bd9Sstevel@tonic-gate * There are some instances where the softintr is not
20527c478bd9Sstevel@tonic-gate * scheduled and hence not called. It so happened that makes
20537c478bd9Sstevel@tonic-gate * the last few characters to be stuck in ringbuffer.
20547c478bd9Sstevel@tonic-gate * Hence, call once again the handler so that the last few
20557c478bd9Sstevel@tonic-gate * characters are cleared.
20567c478bd9Sstevel@tonic-gate */
20577c478bd9Sstevel@tonic-gate cc = RING_CNT(async);
20587c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
20597c478bd9Sstevel@tonic-gate if (cc > 0) {
20607c478bd9Sstevel@tonic-gate (void) async_softint(asy);
20617c478bd9Sstevel@tonic-gate }
20627c478bd9Sstevel@tonic-gate }
20637c478bd9Sstevel@tonic-gate return (rv);
20647c478bd9Sstevel@tonic-gate }
20657c478bd9Sstevel@tonic-gate
20667c478bd9Sstevel@tonic-gate /*
20677c478bd9Sstevel@tonic-gate * Handle a software interrupt.
20687c478bd9Sstevel@tonic-gate */
20697c478bd9Sstevel@tonic-gate static int
async_softint(struct asycom * asy)20707c478bd9Sstevel@tonic-gate async_softint(struct asycom *asy)
20717c478bd9Sstevel@tonic-gate {
20727c478bd9Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)asy->asy_priv;
20737c478bd9Sstevel@tonic-gate uint_t cc;
20747c478bd9Sstevel@tonic-gate mblk_t *bp;
20757c478bd9Sstevel@tonic-gate queue_t *q;
20767c478bd9Sstevel@tonic-gate uchar_t val;
20777c478bd9Sstevel@tonic-gate uchar_t c;
20787c478bd9Sstevel@tonic-gate tty_common_t *tp;
20797c478bd9Sstevel@tonic-gate
20807c478bd9Sstevel@tonic-gate #ifdef DEBUG
20817c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_PROCS)
20827c478bd9Sstevel@tonic-gate printf("process\n");
20837c478bd9Sstevel@tonic-gate #endif
20847c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
20857c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_DOINGSOFT) {
20867c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
20877c478bd9Sstevel@tonic-gate return (0);
20887c478bd9Sstevel@tonic-gate }
20897c478bd9Sstevel@tonic-gate tp = &async->async_ttycommon;
20907c478bd9Sstevel@tonic-gate q = tp->t_readq;
20917c478bd9Sstevel@tonic-gate if (q != NULL) {
20927c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
20937c478bd9Sstevel@tonic-gate enterq(q);
20947c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
20957c478bd9Sstevel@tonic-gate }
20967c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
20977c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_DOINGSOFT;
20987c478bd9Sstevel@tonic-gate
20997c478bd9Sstevel@tonic-gate if (INB(ICR) & MIEN)
21007c478bd9Sstevel@tonic-gate val = asy->asy_cached_msr & 0xFF;
21017c478bd9Sstevel@tonic-gate else
21027c478bd9Sstevel@tonic-gate val = INB(MSR) & 0xFF;
21037c478bd9Sstevel@tonic-gate
21047c478bd9Sstevel@tonic-gate if (async->async_ttycommon.t_cflag & CRTSCTS) {
21057c478bd9Sstevel@tonic-gate if ((val & CTS) && (async->async_flags & ASYNC_HW_OUT_FLW)) {
21067c478bd9Sstevel@tonic-gate #ifdef DEBUG
21077c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_HFLOW)
21087c478bd9Sstevel@tonic-gate printf("asy%d: hflow start\n",
21090280efdcSzk UNIT(async->async_dev));
21107c478bd9Sstevel@tonic-gate #endif
21117c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_OUT_FLW;
21127c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
21137c478bd9Sstevel@tonic-gate if (async->async_ocnt > 0) {
21147c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
21157c478bd9Sstevel@tonic-gate async_resume(async);
21167c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
21177c478bd9Sstevel@tonic-gate } else {
21187c478bd9Sstevel@tonic-gate async_start(async);
21197c478bd9Sstevel@tonic-gate }
21207c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
21217c478bd9Sstevel@tonic-gate }
21227c478bd9Sstevel@tonic-gate }
21237c478bd9Sstevel@tonic-gate if (async->async_ext) {
21247c478bd9Sstevel@tonic-gate async->async_ext = 0;
21257c478bd9Sstevel@tonic-gate /* check for carrier up */
21267c478bd9Sstevel@tonic-gate if ((val & DCD) || (tp->t_flags & TS_SOFTCAR)) {
21277c478bd9Sstevel@tonic-gate /* carrier present */
21287c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) == 0) {
21297c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_CARR_ON;
21307c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
21317c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
21327c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN)
21337c478bd9Sstevel@tonic-gate (void) putctl(q, M_UNHANGUP);
21347c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
21357c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
21367c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
21377c478bd9Sstevel@tonic-gate }
21387c478bd9Sstevel@tonic-gate } else {
21397c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) &&
21407c478bd9Sstevel@tonic-gate !(tp->t_cflag & CLOCAL)) {
21417c478bd9Sstevel@tonic-gate int flushflag;
21427c478bd9Sstevel@tonic-gate
21437c478bd9Sstevel@tonic-gate /*
21447c478bd9Sstevel@tonic-gate * Carrier went away.
21457c478bd9Sstevel@tonic-gate * Drop DTR, abort any output in
21467c478bd9Sstevel@tonic-gate * progress, indicate that output is
21477c478bd9Sstevel@tonic-gate * not stopped, and send a hangup
21487c478bd9Sstevel@tonic-gate * notification upstream.
21497c478bd9Sstevel@tonic-gate *
21507c478bd9Sstevel@tonic-gate * If we're in the midst of close, then flush
21517c478bd9Sstevel@tonic-gate * everything. Don't leave stale ioctls lying
21527c478bd9Sstevel@tonic-gate * about.
21537c478bd9Sstevel@tonic-gate */
21547c478bd9Sstevel@tonic-gate val = INB(MCR);
21557c478bd9Sstevel@tonic-gate OUTB(MCR, (val & ~DTR));
21567c478bd9Sstevel@tonic-gate flushflag = (async->async_flags &
21577c478bd9Sstevel@tonic-gate ASYNC_CLOSING) ? FLUSHALL : FLUSHDATA;
21584d0b1b0dSAn Bui if (tp->t_writeq != NULL) {
21594d0b1b0dSAn Bui flushq(tp->t_writeq, flushflag);
21604d0b1b0dSAn Bui }
21617c478bd9Sstevel@tonic-gate if (async->async_xmitblk != NULL) {
21627c478bd9Sstevel@tonic-gate freeb(async->async_xmitblk);
21637c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL;
21647c478bd9Sstevel@tonic-gate }
21657c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) {
21667c478bd9Sstevel@tonic-gate async->async_ocnt = 0;
21677c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY;
21687c478bd9Sstevel@tonic-gate }
21697c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED;
21707c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
21717c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
21727c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
21737c478bd9Sstevel@tonic-gate (void) putctl(q, M_HANGUP);
21747c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
21757c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
21767c478bd9Sstevel@tonic-gate }
21777cb42c7eSrameshc async->async_flags &= ~ASYNC_CARR_ON;
21787cb42c7eSrameshc mutex_exit(asy->asy_excl_hi);
21797cb42c7eSrameshc cv_broadcast(&async->async_flags_cv);
21807cb42c7eSrameshc mutex_enter(asy->asy_excl_hi);
21817c478bd9Sstevel@tonic-gate }
21827c478bd9Sstevel@tonic-gate }
21837c478bd9Sstevel@tonic-gate }
21847c478bd9Sstevel@tonic-gate
21857c478bd9Sstevel@tonic-gate /*
21867c478bd9Sstevel@tonic-gate * If data has been added to the circular buffer, remove
21877c478bd9Sstevel@tonic-gate * it from the buffer, and send it up the stream if there's
21887c478bd9Sstevel@tonic-gate * somebody listening. Try to do it 16 bytes at a time. If we
21897c478bd9Sstevel@tonic-gate * have more than 16 bytes to move, move 16 byte chunks and
21907c478bd9Sstevel@tonic-gate * leave the rest for next time around (maybe it will grow).
21917c478bd9Sstevel@tonic-gate */
21927c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_ISOPEN)) {
21937c478bd9Sstevel@tonic-gate RING_INIT(async);
21947c478bd9Sstevel@tonic-gate goto rv;
21957c478bd9Sstevel@tonic-gate }
21967c478bd9Sstevel@tonic-gate if ((cc = RING_CNT(async)) == 0) {
21977c478bd9Sstevel@tonic-gate goto rv;
21987c478bd9Sstevel@tonic-gate }
21997c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
22007c478bd9Sstevel@tonic-gate
22017c478bd9Sstevel@tonic-gate if (!canput(q)) {
22027c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
22037c478bd9Sstevel@tonic-gate #ifdef DEBUG
22047c478bd9Sstevel@tonic-gate if (!(asydebug & ASY_DEBUG_HFLOW)) {
22057c478bd9Sstevel@tonic-gate printf("asy%d: hflow stop input.\n",
22060280efdcSzk UNIT(async->async_dev));
22077c478bd9Sstevel@tonic-gate if (canputnext(q))
22087c478bd9Sstevel@tonic-gate printf("asy%d: next queue is "
22090280efdcSzk "ready\n",
22100280efdcSzk UNIT(async->async_dev));
22117c478bd9Sstevel@tonic-gate }
22127c478bd9Sstevel@tonic-gate #endif
22137c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
22147c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_HW_IN_FLOW;
22157c478bd9Sstevel@tonic-gate async->async_flowc = async->async_stopc;
22167c478bd9Sstevel@tonic-gate } else mutex_enter(asy->asy_excl_hi);
22177c478bd9Sstevel@tonic-gate goto rv;
22187c478bd9Sstevel@tonic-gate }
22197c478bd9Sstevel@tonic-gate
22207c478bd9Sstevel@tonic-gate if (async->async_ringbuf_overflow) {
22217c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_HW_IN_FLOW) &&
22220280efdcSzk ((int)(RING_CNT(async)) < (RINGSIZE/4))) {
22237c478bd9Sstevel@tonic-gate #ifdef DEBUG
22247c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_HFLOW)
22257c478bd9Sstevel@tonic-gate printf("asy%d: hflow start input.\n",
22260280efdcSzk UNIT(async->async_dev));
22277c478bd9Sstevel@tonic-gate #endif
22287c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
22297c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_IN_FLOW;
22307c478bd9Sstevel@tonic-gate async->async_flowc = async->async_startc;
22317c478bd9Sstevel@tonic-gate async->async_ringbuf_overflow = 0;
22327c478bd9Sstevel@tonic-gate goto rv;
22337c478bd9Sstevel@tonic-gate }
22347c478bd9Sstevel@tonic-gate }
22357c478bd9Sstevel@tonic-gate #ifdef DEBUG
22367c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_INPUT)
22377c478bd9Sstevel@tonic-gate printf("asy%d: %d char(s) in queue.\n",
22380280efdcSzk UNIT(async->async_dev), cc);
22397c478bd9Sstevel@tonic-gate #endif
22407c478bd9Sstevel@tonic-gate /*
22417c478bd9Sstevel@tonic-gate * Before you pull the characters from the RING BUF
22427c478bd9Sstevel@tonic-gate * Check whether you can put into the queue again
22437c478bd9Sstevel@tonic-gate */
22447c478bd9Sstevel@tonic-gate if ((!canputnext(q)) || (!canput(q))) {
22457c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
22467c478bd9Sstevel@tonic-gate if ((async->async_flags & ASYNC_HW_IN_FLOW) == 0) {
22477c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_HW_IN_FLOW;
22487c478bd9Sstevel@tonic-gate async->async_flowc = async->async_stopc;
22497c478bd9Sstevel@tonic-gate async->async_queue_full = 1;
22507c478bd9Sstevel@tonic-gate }
22517c478bd9Sstevel@tonic-gate goto rv;
22527c478bd9Sstevel@tonic-gate }
22537c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
22547c478bd9Sstevel@tonic-gate if (async->async_queue_full) {
22557c478bd9Sstevel@tonic-gate /*
22567c478bd9Sstevel@tonic-gate * Last time the Stream queue didnot allow
22577c478bd9Sstevel@tonic-gate * now it allows so, relax, the flow control
22587c478bd9Sstevel@tonic-gate */
22597c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_HW_IN_FLOW) {
22607c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_IN_FLOW;
22617c478bd9Sstevel@tonic-gate async->async_queue_full = 0;
22627c478bd9Sstevel@tonic-gate async->async_flowc = async->async_startc;
22637c478bd9Sstevel@tonic-gate goto rv;
22647c478bd9Sstevel@tonic-gate } else
22657c478bd9Sstevel@tonic-gate async->async_queue_full = 0;
22667c478bd9Sstevel@tonic-gate }
22677c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
22687c478bd9Sstevel@tonic-gate if (!(bp = allocb(cc, BPRI_MED))) {
22697c478bd9Sstevel@tonic-gate ttycommon_qfull(&async->async_ttycommon, q);
22707c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
22717c478bd9Sstevel@tonic-gate goto rv;
22727c478bd9Sstevel@tonic-gate }
22737c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
22747c478bd9Sstevel@tonic-gate do {
22757c478bd9Sstevel@tonic-gate if (RING_ERR(async, S_ERRORS)) {
22767c478bd9Sstevel@tonic-gate RING_UNMARK(async);
22777c478bd9Sstevel@tonic-gate c = RING_GET(async);
22787c478bd9Sstevel@tonic-gate break;
22797c478bd9Sstevel@tonic-gate } else {
22807c478bd9Sstevel@tonic-gate *bp->b_wptr++ = RING_GET(async);
22817c478bd9Sstevel@tonic-gate }
22827c478bd9Sstevel@tonic-gate } while (--cc);
22837c478bd9Sstevel@tonic-gate
22847c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
22857c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
22867c478bd9Sstevel@tonic-gate if (bp->b_wptr > bp->b_rptr) {
22877c478bd9Sstevel@tonic-gate if (!canputnext(q)) {
22887c478bd9Sstevel@tonic-gate if (!canput(q)) {
22897c478bd9Sstevel@tonic-gate /*
22907c478bd9Sstevel@tonic-gate * Even after taking all precautions that
22917c478bd9Sstevel@tonic-gate * Still we are unable to queue, then we
22927c478bd9Sstevel@tonic-gate * cannot do anything, just drop the block
22937c478bd9Sstevel@tonic-gate */
22947c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE,
22950280efdcSzk "su%d: local queue full\n",
22960280efdcSzk UNIT(async->async_dev));
22977c478bd9Sstevel@tonic-gate freemsg(bp);
22987c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
22997c478bd9Sstevel@tonic-gate if ((async->async_flags &
23000280efdcSzk ASYNC_HW_IN_FLOW) == 0) {
23017c478bd9Sstevel@tonic-gate async->async_flags |=
23020280efdcSzk ASYNC_HW_IN_FLOW;
23037c478bd9Sstevel@tonic-gate async->async_flowc =
23040280efdcSzk async->async_stopc;
23057c478bd9Sstevel@tonic-gate async->async_queue_full = 1;
23067c478bd9Sstevel@tonic-gate }
23077c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
23087c478bd9Sstevel@tonic-gate } else {
23097c478bd9Sstevel@tonic-gate (void) putq(q, bp);
23107c478bd9Sstevel@tonic-gate }
23117c478bd9Sstevel@tonic-gate } else {
23127c478bd9Sstevel@tonic-gate putnext(q, bp);
23137c478bd9Sstevel@tonic-gate }
23147c478bd9Sstevel@tonic-gate } else {
23157c478bd9Sstevel@tonic-gate freemsg(bp);
23167c478bd9Sstevel@tonic-gate }
23177c478bd9Sstevel@tonic-gate /*
23187c478bd9Sstevel@tonic-gate * If we have a parity error, then send
23197c478bd9Sstevel@tonic-gate * up an M_BREAK with the "bad"
23207c478bd9Sstevel@tonic-gate * character as an argument. Let ldterm
23217c478bd9Sstevel@tonic-gate * figure out what to do with the error.
23227c478bd9Sstevel@tonic-gate */
23237c478bd9Sstevel@tonic-gate if (cc)
23247c478bd9Sstevel@tonic-gate (void) putctl1(q, M_BREAK, c);
23257c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
23267c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
23277c478bd9Sstevel@tonic-gate rv:
23287c478bd9Sstevel@tonic-gate /*
23297c478bd9Sstevel@tonic-gate * If a transmission has finished, indicate that it's finished,
23307c478bd9Sstevel@tonic-gate * and start that line up again.
23317c478bd9Sstevel@tonic-gate */
23327c478bd9Sstevel@tonic-gate if (async->async_break) {
23337c478bd9Sstevel@tonic-gate async->async_break = 0;
23347c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
23357c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
23367c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
23377c478bd9Sstevel@tonic-gate (void) putctl(q, M_BREAK);
23387c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
23397c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
23407c478bd9Sstevel@tonic-gate }
23417c478bd9Sstevel@tonic-gate }
23427c478bd9Sstevel@tonic-gate if ((async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) ||
234319cdc281Skc (async->async_flowc != '\0')) {
23447c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY;
23457c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
23467c478bd9Sstevel@tonic-gate if (async->async_xmitblk)
23477c478bd9Sstevel@tonic-gate freeb(async->async_xmitblk);
23487c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL;
23497c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
23507c478bd9Sstevel@tonic-gate asy->inperim = B_TRUE;
23517c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
23527c478bd9Sstevel@tonic-gate enterq(async->async_ttycommon.t_writeq);
23537c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
23547c478bd9Sstevel@tonic-gate }
23557c478bd9Sstevel@tonic-gate async_start(async);
23567c478bd9Sstevel@tonic-gate /*
23577c478bd9Sstevel@tonic-gate * We need to check for inperim and ISOPEN due to
23587c478bd9Sstevel@tonic-gate * multi-threading implications; it's possible to close the
23597c478bd9Sstevel@tonic-gate * port and nullify async_flags while completing the software
23607c478bd9Sstevel@tonic-gate * interrupt. If the port is closed, leaveq() will have already
23617c478bd9Sstevel@tonic-gate * been called. We don't want to call it twice.
23627c478bd9Sstevel@tonic-gate */
23637c478bd9Sstevel@tonic-gate if ((asy->inperim) && (async->async_flags & ASYNC_ISOPEN)) {
23647c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
23657c478bd9Sstevel@tonic-gate leaveq(async->async_ttycommon.t_writeq);
23667c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
23677c478bd9Sstevel@tonic-gate asy->inperim = B_FALSE;
23687c478bd9Sstevel@tonic-gate }
23697c478bd9Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BUSY))
23707c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
23717c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
23727c478bd9Sstevel@tonic-gate }
23737c478bd9Sstevel@tonic-gate /*
23747c478bd9Sstevel@tonic-gate * A note about these overrun bits: all they do is *tell* someone
23757c478bd9Sstevel@tonic-gate * about an error- They do not track multiple errors. In fact,
23767c478bd9Sstevel@tonic-gate * you could consider them latched register bits if you like.
23777c478bd9Sstevel@tonic-gate * We are only interested in printing the error message once for
23784cad604cSMarcel Telka * any cluster of overrun errors.
23797c478bd9Sstevel@tonic-gate */
23807c478bd9Sstevel@tonic-gate if (async->async_hw_overrun) {
23817c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
23827c478bd9Sstevel@tonic-gate if (su_log > 0) {
23837c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
23847c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
23857c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "su%d: silo overflow\n",
23867c478bd9Sstevel@tonic-gate UNIT(async->async_dev));
23877c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
23887c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
23897c478bd9Sstevel@tonic-gate }
23907c478bd9Sstevel@tonic-gate INC64_KSTAT(asy, siloover);
23917c478bd9Sstevel@tonic-gate }
23927c478bd9Sstevel@tonic-gate async->async_hw_overrun = 0;
23937c478bd9Sstevel@tonic-gate }
23947c478bd9Sstevel@tonic-gate if (async->async_sw_overrun) {
23957c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
23967c478bd9Sstevel@tonic-gate if (su_log > 0) {
23977c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
23987c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
23997c478bd9Sstevel@tonic-gate cmn_err(CE_NOTE, "su%d: ring buffer overflow\n",
24007c478bd9Sstevel@tonic-gate UNIT(async->async_dev));
24017c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
24027c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
24037c478bd9Sstevel@tonic-gate }
24047c478bd9Sstevel@tonic-gate INC64_KSTAT(asy, ringover);
24057c478bd9Sstevel@tonic-gate }
24067c478bd9Sstevel@tonic-gate async->async_sw_overrun = 0;
24077c478bd9Sstevel@tonic-gate }
24087c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT;
24097c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
24107c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
24117c478bd9Sstevel@tonic-gate if (q != NULL)
24127c478bd9Sstevel@tonic-gate leaveq(q);
24137c478bd9Sstevel@tonic-gate return (0);
24147c478bd9Sstevel@tonic-gate }
24157c478bd9Sstevel@tonic-gate
24167c478bd9Sstevel@tonic-gate /*
24177c478bd9Sstevel@tonic-gate * Restart output on a line after a delay or break timer expired.
24187c478bd9Sstevel@tonic-gate */
24197c478bd9Sstevel@tonic-gate static void
async_restart(void * arg)24207c478bd9Sstevel@tonic-gate async_restart(void *arg)
24217c478bd9Sstevel@tonic-gate {
24227c478bd9Sstevel@tonic-gate struct asyncline *async = arg;
24237c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common;
24247c478bd9Sstevel@tonic-gate queue_t *q;
24257c478bd9Sstevel@tonic-gate uchar_t lcr;
24267c478bd9Sstevel@tonic-gate
24277c478bd9Sstevel@tonic-gate /*
24287c478bd9Sstevel@tonic-gate * If break timer expired, turn off the break bit.
24297c478bd9Sstevel@tonic-gate */
24307c478bd9Sstevel@tonic-gate #ifdef DEBUG
24317c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_PROCS)
24327c478bd9Sstevel@tonic-gate printf("restart\n");
24337c478bd9Sstevel@tonic-gate #endif
24347c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
24357c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_BREAK) {
2436587bcfd8Skc unsigned int rate;
2437587bcfd8Skc
24387c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
24397c478bd9Sstevel@tonic-gate lcr = INB(LCR);
24407c478bd9Sstevel@tonic-gate OUTB(LCR, (lcr & ~SETBREAK));
2441587bcfd8Skc
2442587bcfd8Skc /*
2443587bcfd8Skc * Go to sleep for the time it takes for at least one
2444587bcfd8Skc * stop bit to be received by the device at the other
2445587bcfd8Skc * end of the line as stated in the RS-232 specification.
2446587bcfd8Skc * The wait period is equal to:
2447587bcfd8Skc * 2 clock cycles * (1 MICROSEC / baud rate)
2448587bcfd8Skc */
2449587bcfd8Skc rate = async->async_ttycommon.t_cflag & CBAUD;
2450587bcfd8Skc if (async->async_ttycommon.t_cflag & CBAUDEXT)
2451587bcfd8Skc rate += 16;
2452587bcfd8Skc if (rate >= N_SU_SPEEDS || rate == B0) {
2453587bcfd8Skc rate = B9600;
2454587bcfd8Skc }
2455587bcfd8Skc
24567c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
2457587bcfd8Skc mutex_exit(asy->asy_excl);
2458587bcfd8Skc drv_usecwait(2 * MICROSEC / baudtable[rate]);
2459587bcfd8Skc mutex_enter(asy->asy_excl);
24607c478bd9Sstevel@tonic-gate }
24617c478bd9Sstevel@tonic-gate async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK|ASYNC_DRAINING);
24627c478bd9Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) != NULL) {
24637c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
24647c478bd9Sstevel@tonic-gate enterq(q);
24657c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
24667c478bd9Sstevel@tonic-gate }
24677c478bd9Sstevel@tonic-gate async_start(async);
24687c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
24697c478bd9Sstevel@tonic-gate if (q != NULL)
24707c478bd9Sstevel@tonic-gate leaveq(q);
24717c478bd9Sstevel@tonic-gate
24727c478bd9Sstevel@tonic-gate /* cleared break or delay flag; may have made some output progress */
24737c478bd9Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
24747c478bd9Sstevel@tonic-gate }
24757c478bd9Sstevel@tonic-gate
24767c478bd9Sstevel@tonic-gate static void
async_start(struct asyncline * async)24777c478bd9Sstevel@tonic-gate async_start(struct asyncline *async)
24787c478bd9Sstevel@tonic-gate {
24797c478bd9Sstevel@tonic-gate async_nstart(async, 0);
24807c478bd9Sstevel@tonic-gate }
24817c478bd9Sstevel@tonic-gate
24827c478bd9Sstevel@tonic-gate /*
24837c478bd9Sstevel@tonic-gate * Start output on a line, unless it's busy, frozen, or otherwise.
24847c478bd9Sstevel@tonic-gate */
24857c478bd9Sstevel@tonic-gate static void
async_nstart(struct asyncline * async,int mode)24867c478bd9Sstevel@tonic-gate async_nstart(struct asyncline *async, int mode)
24877c478bd9Sstevel@tonic-gate {
24887c478bd9Sstevel@tonic-gate register struct asycom *asy = async->async_common;
24897c478bd9Sstevel@tonic-gate register int cc;
24907c478bd9Sstevel@tonic-gate register queue_t *q;
24917c478bd9Sstevel@tonic-gate mblk_t *bp, *nbp;
24927c478bd9Sstevel@tonic-gate uchar_t *xmit_addr;
24937c478bd9Sstevel@tonic-gate uchar_t val;
24947c478bd9Sstevel@tonic-gate int fifo_len = 1;
24957c478bd9Sstevel@tonic-gate int xmit_progress;
24967c478bd9Sstevel@tonic-gate
24977c478bd9Sstevel@tonic-gate #ifdef DEBUG
24987c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_PROCS)
24997c478bd9Sstevel@tonic-gate printf("start\n");
25007c478bd9Sstevel@tonic-gate #endif
25017c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON)
25027c478bd9Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
25037c478bd9Sstevel@tonic-gate
25047c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl));
25057c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
25067c478bd9Sstevel@tonic-gate asycheckflowcontrol_hw(asy);
25077c478bd9Sstevel@tonic-gate
25087c478bd9Sstevel@tonic-gate /*
25097c478bd9Sstevel@tonic-gate * If the chip is busy (i.e., we're waiting for a break timeout
25107c478bd9Sstevel@tonic-gate * to expire, or for the current transmission to finish, or for
25117c478bd9Sstevel@tonic-gate * output to finish draining from chip), don't grab anything new.
25127c478bd9Sstevel@tonic-gate */
25137c478bd9Sstevel@tonic-gate if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY|ASYNC_DRAINING)) {
25147c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
25157c478bd9Sstevel@tonic-gate #ifdef DEBUG
25167c478bd9Sstevel@tonic-gate if (mode && asydebug & ASY_DEBUG_CLOSE)
25177c478bd9Sstevel@tonic-gate printf("asy%d: start %s.\n",
25180280efdcSzk UNIT(async->async_dev),
25190280efdcSzk async->async_flags & ASYNC_BREAK
25200280efdcSzk ? "break" : "busy");
25217c478bd9Sstevel@tonic-gate #endif
25227c478bd9Sstevel@tonic-gate return;
25237c478bd9Sstevel@tonic-gate }
25247c478bd9Sstevel@tonic-gate
25257c478bd9Sstevel@tonic-gate /*
25267c478bd9Sstevel@tonic-gate * If we have a flow-control character to transmit, do it now.
25277c478bd9Sstevel@tonic-gate */
25287c478bd9Sstevel@tonic-gate if (asycheckflowcontrol_sw(asy)) {
25297c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
25307c478bd9Sstevel@tonic-gate return;
25317c478bd9Sstevel@tonic-gate }
25327c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
25337c478bd9Sstevel@tonic-gate /*
25347c478bd9Sstevel@tonic-gate * If we're waiting for a delay timeout to expire, don't grab
25357c478bd9Sstevel@tonic-gate * anything new.
25367c478bd9Sstevel@tonic-gate */
25377c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_DELAY) {
25387c478bd9Sstevel@tonic-gate #ifdef DEBUG
25397c478bd9Sstevel@tonic-gate if (mode && asydebug & ASY_DEBUG_CLOSE)
25407c478bd9Sstevel@tonic-gate printf("asy%d: start ASYNC_DELAY.\n",
25410280efdcSzk UNIT(async->async_dev));
25427c478bd9Sstevel@tonic-gate #endif
25437c478bd9Sstevel@tonic-gate return;
25447c478bd9Sstevel@tonic-gate }
25457c478bd9Sstevel@tonic-gate
25467c478bd9Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) {
25477c478bd9Sstevel@tonic-gate #ifdef DEBUG
25487c478bd9Sstevel@tonic-gate if (mode && asydebug & ASY_DEBUG_CLOSE)
25497c478bd9Sstevel@tonic-gate printf("asy%d: start writeq is null.\n",
25500280efdcSzk UNIT(async->async_dev));
25517c478bd9Sstevel@tonic-gate #endif
25527c478bd9Sstevel@tonic-gate return; /* not attached to a stream */
25537c478bd9Sstevel@tonic-gate }
25547c478bd9Sstevel@tonic-gate
25557c478bd9Sstevel@tonic-gate for (;;) {
25567c478bd9Sstevel@tonic-gate if ((bp = getq(q)) == NULL)
25577c478bd9Sstevel@tonic-gate return; /* no data to transmit */
25587c478bd9Sstevel@tonic-gate
25597c478bd9Sstevel@tonic-gate /*
25607c478bd9Sstevel@tonic-gate * We have a message block to work on.
25617c478bd9Sstevel@tonic-gate * Check whether it's a break, a delay, or an ioctl (the latter
25627c478bd9Sstevel@tonic-gate * occurs if the ioctl in question was waiting for the output
25637c478bd9Sstevel@tonic-gate * to drain). If it's one of those, process it immediately.
25647c478bd9Sstevel@tonic-gate */
25657c478bd9Sstevel@tonic-gate switch (bp->b_datap->db_type) {
25667c478bd9Sstevel@tonic-gate
25677c478bd9Sstevel@tonic-gate case M_BREAK:
25687c478bd9Sstevel@tonic-gate /*
25697c478bd9Sstevel@tonic-gate * Set the break bit, and arrange for "async_restart"
25707c478bd9Sstevel@tonic-gate * to be called in 1/4 second; it will turn the
25717c478bd9Sstevel@tonic-gate * break bit off, and call "async_start" to grab
25727c478bd9Sstevel@tonic-gate * the next message.
25737c478bd9Sstevel@tonic-gate */
25747c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
25757c478bd9Sstevel@tonic-gate val = INB(LCR);
25767c478bd9Sstevel@tonic-gate OUTB(LCR, (val | SETBREAK));
25777c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
25787c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK;
25797c478bd9Sstevel@tonic-gate (void) timeout(async_restart, async, hz / 4);
25807c478bd9Sstevel@tonic-gate freemsg(bp);
25817c478bd9Sstevel@tonic-gate return; /* wait for this to finish */
25827c478bd9Sstevel@tonic-gate
25837c478bd9Sstevel@tonic-gate case M_DELAY:
25847c478bd9Sstevel@tonic-gate /*
25857c478bd9Sstevel@tonic-gate * Arrange for "async_restart" to be called when the
25867c478bd9Sstevel@tonic-gate * delay expires; it will turn ASYNC_DELAY off,
25877c478bd9Sstevel@tonic-gate * and call "async_start" to grab the next message.
25887c478bd9Sstevel@tonic-gate */
25897c478bd9Sstevel@tonic-gate (void) timeout(async_restart, async,
25900280efdcSzk (clock_t)(*(unsigned char *)bp->b_rptr + 6));
25917c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_DELAY;
25927c478bd9Sstevel@tonic-gate freemsg(bp);
25937c478bd9Sstevel@tonic-gate return; /* wait for this to finish */
25947c478bd9Sstevel@tonic-gate
25957c478bd9Sstevel@tonic-gate case M_IOCTL:
25967c478bd9Sstevel@tonic-gate /*
25977c478bd9Sstevel@tonic-gate * This ioctl needs to wait for the output ahead of
25987c478bd9Sstevel@tonic-gate * it to drain. Try to do it, and then either
25997c478bd9Sstevel@tonic-gate * redo the ioctl at a later time or grab the next
26007c478bd9Sstevel@tonic-gate * message after it.
26017c478bd9Sstevel@tonic-gate */
26027c478bd9Sstevel@tonic-gate
26037c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
26047c478bd9Sstevel@tonic-gate if (asy_isbusy(asy)) {
26057c478bd9Sstevel@tonic-gate /*
26067c478bd9Sstevel@tonic-gate * Get the divisor by calculating the rate
26077c478bd9Sstevel@tonic-gate */
26087c478bd9Sstevel@tonic-gate unsigned int rate;
26097c478bd9Sstevel@tonic-gate
26107c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
26117c478bd9Sstevel@tonic-gate rate = async->async_ttycommon.t_cflag & CBAUD;
26127c478bd9Sstevel@tonic-gate if (async->async_ttycommon.t_cflag & CBAUDEXT)
26137c478bd9Sstevel@tonic-gate rate += 16;
26147c478bd9Sstevel@tonic-gate if (rate >= N_SU_SPEEDS || rate == B0) {
26157c478bd9Sstevel@tonic-gate rate = B9600;
26167c478bd9Sstevel@tonic-gate }
26177c478bd9Sstevel@tonic-gate
26187c478bd9Sstevel@tonic-gate /*
26197c478bd9Sstevel@tonic-gate * We need to do a callback as the port will
26207c478bd9Sstevel@tonic-gate * be set to drain
26217c478bd9Sstevel@tonic-gate */
26227c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_DRAINING;
26237c478bd9Sstevel@tonic-gate
26247c478bd9Sstevel@tonic-gate /*
26257c478bd9Sstevel@tonic-gate * Put the message we just processed back onto
26267c478bd9Sstevel@tonic-gate * the end of the queue
26277c478bd9Sstevel@tonic-gate */
26287c478bd9Sstevel@tonic-gate if (putq(q, bp) == 0)
26297c478bd9Sstevel@tonic-gate freemsg(bp);
26307c478bd9Sstevel@tonic-gate
26317c478bd9Sstevel@tonic-gate /*
26327c478bd9Sstevel@tonic-gate * We need to delay until the TSR and THR
26337c478bd9Sstevel@tonic-gate * have been exhausted. We base the delay on
26347c478bd9Sstevel@tonic-gate * the amount of time it takes to transmit
26357c478bd9Sstevel@tonic-gate * 2 chars at the current baud rate in
26367c478bd9Sstevel@tonic-gate * microseconds.
26377c478bd9Sstevel@tonic-gate *
26387c478bd9Sstevel@tonic-gate * Therefore, the wait period is:
26397c478bd9Sstevel@tonic-gate *
26407c478bd9Sstevel@tonic-gate * (#TSR bits + #THR bits) *
2641574493ffSToomas Soome * 1 MICROSEC / baud rate
26427c478bd9Sstevel@tonic-gate */
26437c478bd9Sstevel@tonic-gate (void) timeout(async_restart, async,
26440280efdcSzk drv_usectohz(16 * MICROSEC /
26450280efdcSzk baudtable[rate]));
26467c478bd9Sstevel@tonic-gate return;
26477c478bd9Sstevel@tonic-gate }
26487c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
26497c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
26507c478bd9Sstevel@tonic-gate async_ioctl(async, q, bp, B_FALSE);
26517c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
26527c478bd9Sstevel@tonic-gate continue;
26537c478bd9Sstevel@tonic-gate }
26547c478bd9Sstevel@tonic-gate
26557c478bd9Sstevel@tonic-gate while (bp != NULL && (cc = bp->b_wptr - bp->b_rptr) == 0) {
26567c478bd9Sstevel@tonic-gate nbp = bp->b_cont;
26577c478bd9Sstevel@tonic-gate freeb(bp);
26587c478bd9Sstevel@tonic-gate bp = nbp;
26597c478bd9Sstevel@tonic-gate }
26607c478bd9Sstevel@tonic-gate if (bp != NULL)
26617c478bd9Sstevel@tonic-gate break;
26627c478bd9Sstevel@tonic-gate }
26637c478bd9Sstevel@tonic-gate
26647c478bd9Sstevel@tonic-gate /*
26657c478bd9Sstevel@tonic-gate * We have data to transmit. If output is stopped, put
26667c478bd9Sstevel@tonic-gate * it back and try again later.
26677c478bd9Sstevel@tonic-gate */
26687c478bd9Sstevel@tonic-gate if (async->async_flags & (ASYNC_HW_OUT_FLW|ASYNC_STOPPED)) {
26697c478bd9Sstevel@tonic-gate #ifdef DEBUG
26707c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_HFLOW &&
26710280efdcSzk async->async_flags & ASYNC_HW_OUT_FLW)
26727c478bd9Sstevel@tonic-gate printf("asy%d: output hflow in effect.\n",
26730280efdcSzk UNIT(async->async_dev));
26747c478bd9Sstevel@tonic-gate #endif
26757c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
26767c478bd9Sstevel@tonic-gate (void) putbq(q, bp);
26777c478bd9Sstevel@tonic-gate /*
26787c478bd9Sstevel@tonic-gate * We entered the routine owning the lock, we need to
26797c478bd9Sstevel@tonic-gate * exit the routine owning the lock.
26807c478bd9Sstevel@tonic-gate */
26817c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
26827c478bd9Sstevel@tonic-gate return;
26837c478bd9Sstevel@tonic-gate }
26847c478bd9Sstevel@tonic-gate
26857c478bd9Sstevel@tonic-gate async->async_xmitblk = bp;
26867c478bd9Sstevel@tonic-gate xmit_addr = bp->b_rptr;
26877c478bd9Sstevel@tonic-gate bp = bp->b_cont;
26887c478bd9Sstevel@tonic-gate if (bp != NULL) {
26897c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
26907c478bd9Sstevel@tonic-gate (void) putbq(q, bp); /* not done with this message yet */
26917c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
26927c478bd9Sstevel@tonic-gate }
26937c478bd9Sstevel@tonic-gate
26947c478bd9Sstevel@tonic-gate /*
26957c478bd9Sstevel@tonic-gate * In 5-bit mode, the high order bits are used
26967c478bd9Sstevel@tonic-gate * to indicate character sizes less than five,
26977c478bd9Sstevel@tonic-gate * so we need to explicitly mask before transmitting
26987c478bd9Sstevel@tonic-gate */
26997c478bd9Sstevel@tonic-gate if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) {
27007c478bd9Sstevel@tonic-gate register unsigned char *p = xmit_addr;
27017c478bd9Sstevel@tonic-gate register int cnt = cc;
27027c478bd9Sstevel@tonic-gate
27037c478bd9Sstevel@tonic-gate while (cnt--)
27047c478bd9Sstevel@tonic-gate *p++ &= (unsigned char) 0x1f;
27057c478bd9Sstevel@tonic-gate }
27067c478bd9Sstevel@tonic-gate
27077c478bd9Sstevel@tonic-gate /*
27087c478bd9Sstevel@tonic-gate * Set up this block for pseudo-DMA.
27097c478bd9Sstevel@tonic-gate */
27107c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
27117c478bd9Sstevel@tonic-gate async->async_optr = xmit_addr;
27127c478bd9Sstevel@tonic-gate async->async_ocnt = cc;
27137c478bd9Sstevel@tonic-gate /*
27147c478bd9Sstevel@tonic-gate * If the transmitter is ready, shove some
27157c478bd9Sstevel@tonic-gate * characters out.
27167c478bd9Sstevel@tonic-gate */
27177c478bd9Sstevel@tonic-gate xmit_progress = 0;
27187c478bd9Sstevel@tonic-gate while (fifo_len-- && async->async_ocnt) {
27197c478bd9Sstevel@tonic-gate if (INB(LSR) & XHRE) {
27207c478bd9Sstevel@tonic-gate OUTB(DAT, *async->async_optr++);
27217c478bd9Sstevel@tonic-gate async->async_ocnt--;
27227c478bd9Sstevel@tonic-gate xmit_progress++;
27237c478bd9Sstevel@tonic-gate }
27247c478bd9Sstevel@tonic-gate }
27257c478bd9Sstevel@tonic-gate asy->asy_out_of_band_xmit = xmit_progress;
27267c478bd9Sstevel@tonic-gate if (xmit_progress > 0)
27277c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS;
27287c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_BUSY;
27297c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
27307c478bd9Sstevel@tonic-gate }
27317c478bd9Sstevel@tonic-gate
27327c478bd9Sstevel@tonic-gate /*
27337c478bd9Sstevel@tonic-gate * Resume output by poking the transmitter.
27347c478bd9Sstevel@tonic-gate */
27357c478bd9Sstevel@tonic-gate static void
async_resume(struct asyncline * async)27367c478bd9Sstevel@tonic-gate async_resume(struct asyncline *async)
27377c478bd9Sstevel@tonic-gate {
27387c478bd9Sstevel@tonic-gate register struct asycom *asy = async->async_common;
27397c478bd9Sstevel@tonic-gate
27407c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl_hi));
27417c478bd9Sstevel@tonic-gate #ifdef DEBUG
27427c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_PROCS)
27437c478bd9Sstevel@tonic-gate printf("resume\n");
27447c478bd9Sstevel@tonic-gate #endif
27457c478bd9Sstevel@tonic-gate
27467c478bd9Sstevel@tonic-gate asycheckflowcontrol_hw(asy);
27477c478bd9Sstevel@tonic-gate
27487c478bd9Sstevel@tonic-gate if (INB(LSR) & XHRE) {
27497c478bd9Sstevel@tonic-gate if (asycheckflowcontrol_sw(asy)) {
27507c478bd9Sstevel@tonic-gate return;
27517c478bd9Sstevel@tonic-gate } else if (async->async_ocnt > 0) {
27527c478bd9Sstevel@tonic-gate OUTB(DAT, *async->async_optr++);
27537c478bd9Sstevel@tonic-gate async->async_ocnt--;
27547c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS;
27557c478bd9Sstevel@tonic-gate }
27567c478bd9Sstevel@tonic-gate }
27577c478bd9Sstevel@tonic-gate }
27587c478bd9Sstevel@tonic-gate
27597c478bd9Sstevel@tonic-gate /*
27607c478bd9Sstevel@tonic-gate * Process an "ioctl" message sent down to us.
27617c478bd9Sstevel@tonic-gate * Note that we don't need to get any locks until we are ready to access
27627c478bd9Sstevel@tonic-gate * the hardware. Nothing we access until then is going to be altered
27637c478bd9Sstevel@tonic-gate * outside of the STREAMS framework, so we should be safe.
27647c478bd9Sstevel@tonic-gate */
27657c478bd9Sstevel@tonic-gate static void
async_ioctl(struct asyncline * async,queue_t * wq,mblk_t * mp,boolean_t iswput)27667c478bd9Sstevel@tonic-gate async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp, boolean_t iswput)
27677c478bd9Sstevel@tonic-gate {
27687c478bd9Sstevel@tonic-gate register struct asycom *asy = async->async_common;
27697c478bd9Sstevel@tonic-gate register tty_common_t *tp = &async->async_ttycommon;
27707c478bd9Sstevel@tonic-gate register struct iocblk *iocp;
27717c478bd9Sstevel@tonic-gate register unsigned datasize;
277290a71dbdSzk size_t ioc_count;
27737c478bd9Sstevel@tonic-gate mblk_t *datamp;
27747c478bd9Sstevel@tonic-gate int error = 0;
27757c478bd9Sstevel@tonic-gate uchar_t val, icr;
27767c478bd9Sstevel@tonic-gate #ifdef DEBUG
27777c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_PROCS)
27787c478bd9Sstevel@tonic-gate printf("ioctl\n");
27797c478bd9Sstevel@tonic-gate #endif
27807c478bd9Sstevel@tonic-gate
27817c478bd9Sstevel@tonic-gate if (tp->t_iocpending != NULL) {
27827c478bd9Sstevel@tonic-gate /*
27837c478bd9Sstevel@tonic-gate * We were holding an "ioctl" response pending the
27847c478bd9Sstevel@tonic-gate * availability of an "mblk" to hold data to be passed up;
27857c478bd9Sstevel@tonic-gate * another "ioctl" came through, which means that "ioctl"
27867c478bd9Sstevel@tonic-gate * must have timed out or been aborted.
27877c478bd9Sstevel@tonic-gate */
27887c478bd9Sstevel@tonic-gate freemsg(async->async_ttycommon.t_iocpending);
27897c478bd9Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL;
27907c478bd9Sstevel@tonic-gate }
27917c478bd9Sstevel@tonic-gate
27927c478bd9Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr;
27937c478bd9Sstevel@tonic-gate
279490a71dbdSzk /*
279590a71dbdSzk * Save off the ioc count in case we need to restore it
279690a71dbdSzk * because we are queuing a message block.
279790a71dbdSzk */
279890a71dbdSzk ioc_count = iocp->ioc_count;
279990a71dbdSzk
28007c478bd9Sstevel@tonic-gate /*
28017c478bd9Sstevel@tonic-gate * For TIOCMGET, TIOCMBIC, TIOCMBIS, TIOCMSET, and PPS, do NOT call
28027c478bd9Sstevel@tonic-gate * ttycommon_ioctl() because this function frees up the message block
28037c478bd9Sstevel@tonic-gate * (mp->b_cont) that contains the address of the user variable where
28047c478bd9Sstevel@tonic-gate * we need to pass back the bit array.
28050280efdcSzk *
28060280efdcSzk * Similarly, ttycommon_ioctl() does not know about CONSOPENPOLLEDIO
28070280efdcSzk * and CONSCLOSEPOLLEDIO, so don't let ttycommon_ioctl() touch them.
28087c478bd9Sstevel@tonic-gate */
28097c478bd9Sstevel@tonic-gate if (iocp->ioc_cmd == TIOCMGET ||
28100280efdcSzk iocp->ioc_cmd == TIOCMBIC ||
28110280efdcSzk iocp->ioc_cmd == TIOCMBIS ||
28120280efdcSzk iocp->ioc_cmd == TIOCMSET ||
28130280efdcSzk iocp->ioc_cmd == TIOCGPPS ||
28140280efdcSzk iocp->ioc_cmd == TIOCSPPS ||
28150280efdcSzk iocp->ioc_cmd == TIOCGPPSEV ||
28160280efdcSzk iocp->ioc_cmd == CONSOPENPOLLEDIO ||
28170280efdcSzk iocp->ioc_cmd == CONSCLOSEPOLLEDIO)
28187c478bd9Sstevel@tonic-gate error = -1; /* Do Nothing */
28197c478bd9Sstevel@tonic-gate else
28207c478bd9Sstevel@tonic-gate
28217c478bd9Sstevel@tonic-gate /*
28227c478bd9Sstevel@tonic-gate * The only way in which "ttycommon_ioctl" can fail is if the "ioctl"
28237c478bd9Sstevel@tonic-gate * requires a response containing data to be returned to the user,
28247c478bd9Sstevel@tonic-gate * and no mblk could be allocated for the data.
28257c478bd9Sstevel@tonic-gate * No such "ioctl" alters our state. Thus, we always go ahead and
28267c478bd9Sstevel@tonic-gate * do any state-changes the "ioctl" calls for. If we couldn't allocate
28277c478bd9Sstevel@tonic-gate * the data, "ttycommon_ioctl" has stashed the "ioctl" away safely, so
28287c478bd9Sstevel@tonic-gate * we just call "bufcall" to request that we be called back when we
28297c478bd9Sstevel@tonic-gate * stand a better chance of allocating the data.
28307c478bd9Sstevel@tonic-gate */
28317c478bd9Sstevel@tonic-gate if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) {
28327c478bd9Sstevel@tonic-gate if (async->async_wbufcid)
28337c478bd9Sstevel@tonic-gate unbufcall(async->async_wbufcid);
28347c478bd9Sstevel@tonic-gate async->async_wbufcid = bufcall(datasize, BPRI_HI, async_reioctl,
28357c478bd9Sstevel@tonic-gate async);
28367c478bd9Sstevel@tonic-gate return;
28377c478bd9Sstevel@tonic-gate }
28387c478bd9Sstevel@tonic-gate
28397c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
28407c478bd9Sstevel@tonic-gate
28417c478bd9Sstevel@tonic-gate if (error == 0) {
28427c478bd9Sstevel@tonic-gate /*
28437c478bd9Sstevel@tonic-gate * "ttycommon_ioctl" did most of the work; we just use the
28447c478bd9Sstevel@tonic-gate * data it set up.
28457c478bd9Sstevel@tonic-gate */
28467c478bd9Sstevel@tonic-gate switch (iocp->ioc_cmd) {
28477c478bd9Sstevel@tonic-gate
28487c478bd9Sstevel@tonic-gate case TCSETS:
28497c478bd9Sstevel@tonic-gate if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
28507c478bd9Sstevel@tonic-gate asy->asy_lom_console)) {
28517c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
28527c478bd9Sstevel@tonic-gate error = asy_program(asy, ASY_NOINIT);
28537c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
28547c478bd9Sstevel@tonic-gate }
28557c478bd9Sstevel@tonic-gate break;
28567c478bd9Sstevel@tonic-gate case TCSETSF:
28577c478bd9Sstevel@tonic-gate case TCSETSW:
28587c478bd9Sstevel@tonic-gate case TCSETA:
28597c478bd9Sstevel@tonic-gate case TCSETAW:
28607c478bd9Sstevel@tonic-gate case TCSETAF:
28617c478bd9Sstevel@tonic-gate if (!(asy->asy_rsc_console || asy->asy_rsc_control ||
28627c478bd9Sstevel@tonic-gate asy->asy_lom_console)) {
28637c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
28647c478bd9Sstevel@tonic-gate if (iswput && asy_isbusy(asy)) {
286590a71dbdSzk /*
286690a71dbdSzk * ttycommon_ioctl sets the db_type to
286790a71dbdSzk * M_IOCACK and ioc_count to zero
286890a71dbdSzk * we need to undo this when we
286990a71dbdSzk * queue a control message. This will
287090a71dbdSzk * allow the control messages to be
287190a71dbdSzk * processed again when the chip
287290a71dbdSzk * becomes available.
287390a71dbdSzk */
287490a71dbdSzk mp->b_datap->db_type = M_IOCTL;
287590a71dbdSzk iocp->ioc_count = ioc_count;
287690a71dbdSzk
28777c478bd9Sstevel@tonic-gate if (putq(wq, mp) == 0)
28787c478bd9Sstevel@tonic-gate freemsg(mp);
28797c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
28807c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
28817c478bd9Sstevel@tonic-gate return;
28827c478bd9Sstevel@tonic-gate }
28830a450c21Szk
28840a450c21Szk /*
28850a450c21Szk * TCSETA, TCSETAW, and TCSETAF make use of
28860a450c21Szk * the termio structure and therefore have
28870a450c21Szk * no concept of any speed except what can
28880a450c21Szk * be represented by CBAUD. This is because
28890a450c21Szk * of legacy SVR4 code. Therefore, if we see
28900a450c21Szk * one of the aforementioned IOCTL commands
28910a450c21Szk * we should zero out CBAUDEXT, CIBAUD, and
28920a450c21Szk * CIBAUDEXT as to not break legacy
28930a450c21Szk * functionality. This is because CBAUDEXT,
28940a450c21Szk * CIBAUD, and CIBAUDEXT can't be stored in
28950a450c21Szk * an unsigned short. By zeroing out CBAUDEXT,
28960a450c21Szk * CIBAUD, and CIBAUDEXT in the t_cflag of the
28970a450c21Szk * termios structure asy_program() will set the
28980a450c21Szk * input baud rate to the output baud rate.
28990a450c21Szk */
29000a450c21Szk if (iocp->ioc_cmd == TCSETA ||
29010a450c21Szk iocp->ioc_cmd == TCSETAW ||
29020a450c21Szk iocp->ioc_cmd == TCSETAF)
29030a450c21Szk tp->t_cflag &= ~(CIBAUD |
29040a450c21Szk CIBAUDEXT | CBAUDEXT);
29050a450c21Szk
29067c478bd9Sstevel@tonic-gate error = asy_program(asy, ASY_NOINIT);
29077c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
29087c478bd9Sstevel@tonic-gate }
29097c478bd9Sstevel@tonic-gate break;
29107c478bd9Sstevel@tonic-gate case TIOCSSOFTCAR:
29117c478bd9Sstevel@tonic-gate /* Set the driver state appropriately */
29127c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
29137c478bd9Sstevel@tonic-gate if (tp->t_flags & TS_SOFTCAR)
29147c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD;
29157c478bd9Sstevel@tonic-gate else
29167c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD;
29177c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
29187c478bd9Sstevel@tonic-gate break;
29197c478bd9Sstevel@tonic-gate }
29207c478bd9Sstevel@tonic-gate } else if (error < 0) {
29217c478bd9Sstevel@tonic-gate /*
29227c478bd9Sstevel@tonic-gate * "ttycommon_ioctl" didn't do anything; we process it here.
29237c478bd9Sstevel@tonic-gate */
29247c478bd9Sstevel@tonic-gate error = 0;
29257c478bd9Sstevel@tonic-gate switch (iocp->ioc_cmd) {
29267c478bd9Sstevel@tonic-gate
29277c478bd9Sstevel@tonic-gate case TIOCGPPS:
29287c478bd9Sstevel@tonic-gate /*
29297c478bd9Sstevel@tonic-gate * Get PPS on/off.
29307c478bd9Sstevel@tonic-gate */
29317c478bd9Sstevel@tonic-gate if (mp->b_cont != NULL)
29327c478bd9Sstevel@tonic-gate freemsg(mp->b_cont);
29337c478bd9Sstevel@tonic-gate
29347c478bd9Sstevel@tonic-gate mp->b_cont = allocb(sizeof (int), BPRI_HI);
29357c478bd9Sstevel@tonic-gate if (mp->b_cont == NULL) {
29367c478bd9Sstevel@tonic-gate error = ENOMEM;
29377c478bd9Sstevel@tonic-gate break;
29387c478bd9Sstevel@tonic-gate }
29397c478bd9Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS)
29407c478bd9Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 1;
29417c478bd9Sstevel@tonic-gate else
29427c478bd9Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 0;
29437c478bd9Sstevel@tonic-gate mp->b_cont->b_wptr += sizeof (int);
29447c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
29457c478bd9Sstevel@tonic-gate iocp->ioc_count = sizeof (int);
29467c478bd9Sstevel@tonic-gate break;
29477c478bd9Sstevel@tonic-gate
29487c478bd9Sstevel@tonic-gate case TIOCSPPS:
29497c478bd9Sstevel@tonic-gate /*
29507c478bd9Sstevel@tonic-gate * Set PPS on/off.
29517c478bd9Sstevel@tonic-gate */
29527c478bd9Sstevel@tonic-gate error = miocpullup(mp, sizeof (int));
29537c478bd9Sstevel@tonic-gate if (error != 0)
29547c478bd9Sstevel@tonic-gate break;
29557c478bd9Sstevel@tonic-gate
29567c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
29577c478bd9Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr)
29587c478bd9Sstevel@tonic-gate asy->asy_flags |= ASY_PPS;
29597c478bd9Sstevel@tonic-gate else
29607c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS;
29617c478bd9Sstevel@tonic-gate /* Reset edge sense */
29627c478bd9Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE;
29637c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
29647c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
29657c478bd9Sstevel@tonic-gate break;
29667c478bd9Sstevel@tonic-gate
29677c478bd9Sstevel@tonic-gate case TIOCGPPSEV: {
29687c478bd9Sstevel@tonic-gate /*
29697c478bd9Sstevel@tonic-gate * Get PPS event data.
29707c478bd9Sstevel@tonic-gate */
29717c478bd9Sstevel@tonic-gate mblk_t *bp;
29727c478bd9Sstevel@tonic-gate void *buf;
29737c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
29747c478bd9Sstevel@tonic-gate struct ppsclockev32 p32;
29757c478bd9Sstevel@tonic-gate #endif
29767c478bd9Sstevel@tonic-gate struct ppsclockev ppsclockev;
29777c478bd9Sstevel@tonic-gate
29787c478bd9Sstevel@tonic-gate if (mp->b_cont != NULL) {
29797c478bd9Sstevel@tonic-gate freemsg(mp->b_cont);
29807c478bd9Sstevel@tonic-gate mp->b_cont = NULL;
29817c478bd9Sstevel@tonic-gate }
29827c478bd9Sstevel@tonic-gate
29837c478bd9Sstevel@tonic-gate if ((asy->asy_flags & ASY_PPS) == 0) {
29847c478bd9Sstevel@tonic-gate error = ENXIO;
29857c478bd9Sstevel@tonic-gate break;
29867c478bd9Sstevel@tonic-gate }
29877c478bd9Sstevel@tonic-gate
29887c478bd9Sstevel@tonic-gate /* Protect from incomplete asy_ppsev */
29897c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
29907c478bd9Sstevel@tonic-gate ppsclockev = asy_ppsev;
29917c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
29927c478bd9Sstevel@tonic-gate
29937c478bd9Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
29947c478bd9Sstevel@tonic-gate if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
29957c478bd9Sstevel@tonic-gate TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv);
29967c478bd9Sstevel@tonic-gate p32.serial = ppsclockev.serial;
29977c478bd9Sstevel@tonic-gate buf = &p32;
29987c478bd9Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev32);
29997c478bd9Sstevel@tonic-gate } else
30007c478bd9Sstevel@tonic-gate #endif
30017c478bd9Sstevel@tonic-gate {
30027c478bd9Sstevel@tonic-gate buf = &ppsclockev;
30037c478bd9Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev);
30047c478bd9Sstevel@tonic-gate }
30057c478bd9Sstevel@tonic-gate
30067c478bd9Sstevel@tonic-gate if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) {
30077c478bd9Sstevel@tonic-gate error = ENOMEM;
30087c478bd9Sstevel@tonic-gate break;
30097c478bd9Sstevel@tonic-gate }
30107c478bd9Sstevel@tonic-gate mp->b_cont = bp;
30117c478bd9Sstevel@tonic-gate
30127c478bd9Sstevel@tonic-gate bcopy(buf, bp->b_wptr, iocp->ioc_count);
30137c478bd9Sstevel@tonic-gate bp->b_wptr += iocp->ioc_count;
30147c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
30157c478bd9Sstevel@tonic-gate break;
30167c478bd9Sstevel@tonic-gate }
30177c478bd9Sstevel@tonic-gate
30187c478bd9Sstevel@tonic-gate case TCSBRK:
30197c478bd9Sstevel@tonic-gate error = miocpullup(mp, sizeof (int));
30207c478bd9Sstevel@tonic-gate if (error != 0)
30217c478bd9Sstevel@tonic-gate break;
30227c478bd9Sstevel@tonic-gate
30237c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
30247c478bd9Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr == 0) {
30257c478bd9Sstevel@tonic-gate /*
30267c478bd9Sstevel@tonic-gate * Get the divisor by calculating the rate
30277c478bd9Sstevel@tonic-gate */
30287c478bd9Sstevel@tonic-gate unsigned int rate, divisor;
30297c478bd9Sstevel@tonic-gate rate = async->async_ttycommon.t_cflag & CBAUD;
30307c478bd9Sstevel@tonic-gate if (async->async_ttycommon.t_cflag & CBAUDEXT)
30317c478bd9Sstevel@tonic-gate rate += 16;
30327c478bd9Sstevel@tonic-gate if (rate >= N_SU_SPEEDS) rate = B9600;
30337c478bd9Sstevel@tonic-gate divisor = asyspdtab[rate] & 0xfff;
30347c478bd9Sstevel@tonic-gate
30357c478bd9Sstevel@tonic-gate /*
30367c478bd9Sstevel@tonic-gate * To ensure that erroneous characters are
30377c478bd9Sstevel@tonic-gate * not sent out when the break is set, SB
30387c478bd9Sstevel@tonic-gate * recommends three steps:
30397c478bd9Sstevel@tonic-gate *
30407c478bd9Sstevel@tonic-gate * 1) pad the TSR with 0 bits
30417c478bd9Sstevel@tonic-gate * 2) When the TSR is full, set break
30427c478bd9Sstevel@tonic-gate * 3) When the TSR has been flushed, unset
30437c478bd9Sstevel@tonic-gate * the break when transmission must be
30447c478bd9Sstevel@tonic-gate * restored.
30457c478bd9Sstevel@tonic-gate *
30467c478bd9Sstevel@tonic-gate * We loop until the TSR is empty and then
30477c478bd9Sstevel@tonic-gate * set the break. ASYNC_BREAK has been set
30487c478bd9Sstevel@tonic-gate * to ensure that no characters are
30497c478bd9Sstevel@tonic-gate * transmitted while the TSR is being
30507c478bd9Sstevel@tonic-gate * flushed and SOUT is being used for the
30517c478bd9Sstevel@tonic-gate * break signal.
30527c478bd9Sstevel@tonic-gate *
30537c478bd9Sstevel@tonic-gate * The wait period is equal to
30547c478bd9Sstevel@tonic-gate * clock / (baud * 16) * 16 * 2.
30557c478bd9Sstevel@tonic-gate */
30567c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK;
30577c478bd9Sstevel@tonic-gate while ((INB(LSR) & XSRE) == 0) {
30587c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
30597c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
30607c478bd9Sstevel@tonic-gate drv_usecwait(32*divisor);
30617c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
30627c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
30637c478bd9Sstevel@tonic-gate }
30647c478bd9Sstevel@tonic-gate
30657c478bd9Sstevel@tonic-gate /*
30667c478bd9Sstevel@tonic-gate * Set the break bit, and arrange for
30677c478bd9Sstevel@tonic-gate * "async_restart" to be called in 1/4 second;
30687c478bd9Sstevel@tonic-gate * it will turn the break bit off, and call
30697c478bd9Sstevel@tonic-gate * "async_start" to grab the next message.
30707c478bd9Sstevel@tonic-gate */
30717c478bd9Sstevel@tonic-gate val = INB(LCR);
30727c478bd9Sstevel@tonic-gate OUTB(LCR, (val | SETBREAK));
30737c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
30747c478bd9Sstevel@tonic-gate (void) timeout(async_restart, async, hz / 4);
30757c478bd9Sstevel@tonic-gate } else {
30767c478bd9Sstevel@tonic-gate #ifdef DEBUG
30777c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_CLOSE)
30787c478bd9Sstevel@tonic-gate printf("asy%d: wait for flush.\n",
30790280efdcSzk UNIT(async->async_dev));
30807c478bd9Sstevel@tonic-gate #endif
30817c478bd9Sstevel@tonic-gate if (iswput && asy_isbusy(asy)) {
30827c478bd9Sstevel@tonic-gate if (putq(wq, mp) == 0)
30837c478bd9Sstevel@tonic-gate freemsg(mp);
30847c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
30857c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
30867c478bd9Sstevel@tonic-gate return;
30877c478bd9Sstevel@tonic-gate }
30887c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
30897c478bd9Sstevel@tonic-gate #ifdef DEBUG
30907c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_CLOSE)
30917c478bd9Sstevel@tonic-gate printf("asy%d: ldterm satisfied.\n",
30920280efdcSzk UNIT(async->async_dev));
30937c478bd9Sstevel@tonic-gate #endif
30947c478bd9Sstevel@tonic-gate }
30957c478bd9Sstevel@tonic-gate break;
30967c478bd9Sstevel@tonic-gate
30977c478bd9Sstevel@tonic-gate case TIOCSBRK:
30987c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
30997c478bd9Sstevel@tonic-gate val = INB(LCR);
31007c478bd9Sstevel@tonic-gate OUTB(LCR, (val | SETBREAK));
31017c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
31027c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
31037c478bd9Sstevel@tonic-gate miocack(wq, mp, 0, 0);
31047c478bd9Sstevel@tonic-gate return;
31057c478bd9Sstevel@tonic-gate
31067c478bd9Sstevel@tonic-gate case TIOCCBRK:
31077c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
31087c478bd9Sstevel@tonic-gate val = INB(LCR);
31097c478bd9Sstevel@tonic-gate OUTB(LCR, (val & ~SETBREAK));
31107c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
31117c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
31127c478bd9Sstevel@tonic-gate miocack(wq, mp, 0, 0);
31137c478bd9Sstevel@tonic-gate return;
31147c478bd9Sstevel@tonic-gate
31157c478bd9Sstevel@tonic-gate case TIOCMSET:
31167c478bd9Sstevel@tonic-gate case TIOCMBIS:
31177c478bd9Sstevel@tonic-gate case TIOCMBIC:
31187c478bd9Sstevel@tonic-gate if (iocp->ioc_count == TRANSPARENT)
31197c478bd9Sstevel@tonic-gate mcopyin(mp, NULL, sizeof (int), NULL);
31207c478bd9Sstevel@tonic-gate else {
31217c478bd9Sstevel@tonic-gate error = miocpullup(mp, sizeof (int));
31227c478bd9Sstevel@tonic-gate if (error != 0)
31237c478bd9Sstevel@tonic-gate break;
31247c478bd9Sstevel@tonic-gate
31257c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
31267c478bd9Sstevel@tonic-gate
31277c478bd9Sstevel@tonic-gate (void) asymctl(asy,
31280280efdcSzk dmtoasy(*(int *)mp->b_cont->b_rptr),
31290280efdcSzk iocp->ioc_cmd);
31307c478bd9Sstevel@tonic-gate
31317c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
31327c478bd9Sstevel@tonic-gate iocp->ioc_error = 0;
31337c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
31347c478bd9Sstevel@tonic-gate }
31357c478bd9Sstevel@tonic-gate break;
31367c478bd9Sstevel@tonic-gate
31377c478bd9Sstevel@tonic-gate case TIOCSILOOP:
31387c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
31397c478bd9Sstevel@tonic-gate /*
31407c478bd9Sstevel@tonic-gate * If somebody misues this Ioctl when used for
31417c478bd9Sstevel@tonic-gate * driving keyboard and mouse indicate not supported
31427c478bd9Sstevel@tonic-gate */
31437c478bd9Sstevel@tonic-gate if ((asy->asy_device_type == ASY_KEYBOARD) ||
31440280efdcSzk (asy->asy_device_type == ASY_MOUSE)) {
31457c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
31467c478bd9Sstevel@tonic-gate error = ENOTTY;
31477c478bd9Sstevel@tonic-gate break;
31487c478bd9Sstevel@tonic-gate }
31497c478bd9Sstevel@tonic-gate
31507c478bd9Sstevel@tonic-gate /* should not use when we're the console */
31517c478bd9Sstevel@tonic-gate if ((async->async_dev == kbddev) ||
31527c478bd9Sstevel@tonic-gate (async->async_dev == rconsdev) ||
31537c478bd9Sstevel@tonic-gate (async->async_dev == stdindev)) {
31547c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
31557c478bd9Sstevel@tonic-gate error = EINVAL;
31567c478bd9Sstevel@tonic-gate break;
31577c478bd9Sstevel@tonic-gate }
31587c478bd9Sstevel@tonic-gate
31597c478bd9Sstevel@tonic-gate val = INB(MCR);
31607c478bd9Sstevel@tonic-gate icr = INB(ICR);
31617c478bd9Sstevel@tonic-gate /*
31627c478bd9Sstevel@tonic-gate * Disable the Modem Status Interrupt
31637c478bd9Sstevel@tonic-gate * The reason for disabling is the status of
31647c478bd9Sstevel@tonic-gate * modem signal are in the higher 4 bits instead of
31657c478bd9Sstevel@tonic-gate * lower four bits when in loopback mode,
31667c478bd9Sstevel@tonic-gate * so, donot worry about Modem interrupt when
31677c478bd9Sstevel@tonic-gate * you are planning to set
31687c478bd9Sstevel@tonic-gate * this in loopback mode until it is cleared by
31697c478bd9Sstevel@tonic-gate * another ioctl to get out of the loopback mode
31707c478bd9Sstevel@tonic-gate */
31717c478bd9Sstevel@tonic-gate OUTB(ICR, icr & ~ MIEN);
31727c478bd9Sstevel@tonic-gate OUTB(MCR, val | ASY_LOOP);
31737c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
31747c478bd9Sstevel@tonic-gate iocp->ioc_error = 0;
31757c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
31767c478bd9Sstevel@tonic-gate break;
31777c478bd9Sstevel@tonic-gate
31787c478bd9Sstevel@tonic-gate case TIOCMGET:
31797c478bd9Sstevel@tonic-gate datamp = allocb(sizeof (int), BPRI_MED);
31807c478bd9Sstevel@tonic-gate if (datamp == NULL) {
31817c478bd9Sstevel@tonic-gate error = EAGAIN;
31827c478bd9Sstevel@tonic-gate break;
31837c478bd9Sstevel@tonic-gate }
31847c478bd9Sstevel@tonic-gate
31857c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
31867c478bd9Sstevel@tonic-gate *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET);
31877c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
31887c478bd9Sstevel@tonic-gate
31897c478bd9Sstevel@tonic-gate if (iocp->ioc_count == TRANSPARENT) {
31907c478bd9Sstevel@tonic-gate mcopyout(mp, NULL, sizeof (int), NULL, datamp);
31917c478bd9Sstevel@tonic-gate } else {
31927c478bd9Sstevel@tonic-gate if (mp->b_cont != NULL)
31937c478bd9Sstevel@tonic-gate freemsg(mp->b_cont);
31947c478bd9Sstevel@tonic-gate mp->b_cont = datamp;
31957c478bd9Sstevel@tonic-gate mp->b_cont->b_wptr += sizeof (int);
31967c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
31977c478bd9Sstevel@tonic-gate iocp->ioc_count = sizeof (int);
31987c478bd9Sstevel@tonic-gate }
31997c478bd9Sstevel@tonic-gate break;
32007c478bd9Sstevel@tonic-gate
32010280efdcSzk case CONSOPENPOLLEDIO:
32020280efdcSzk /*
32030280efdcSzk * If we are driving a keyboard there is nothing
32040280efdcSzk * upstream to translate the scan codes. Therefore,
32050280efdcSzk * set the error code to ENOTSUP and NAK the request
32060280efdcSzk */
32070280efdcSzk if (asy->asy_device_type == ASY_KEYBOARD) {
32080280efdcSzk error = ENOTSUP;
32090280efdcSzk break;
32100280efdcSzk }
32110280efdcSzk
32120280efdcSzk error = miocpullup(mp, sizeof (struct cons_polledio *));
32130280efdcSzk if (error != 0)
32140280efdcSzk break;
32150280efdcSzk
32160280efdcSzk /*
32170280efdcSzk * send up a message block containing the
32180280efdcSzk * cons_polledio structure. This provides
32190280efdcSzk * handles to the putchar, getchar, ischar,
32200280efdcSzk * polledio_enter and polledio_exit functions.
32210280efdcSzk */
32220280efdcSzk *(struct cons_polledio **)mp->b_cont->b_rptr =
32230280efdcSzk &asy->polledio;
32240280efdcSzk
32250280efdcSzk mp->b_datap->db_type = M_IOCACK;
32260280efdcSzk break;
32270280efdcSzk
32280280efdcSzk case CONSCLOSEPOLLEDIO:
32290280efdcSzk /*
32300280efdcSzk * If we are driving a keyboard we never successfully
32310280efdcSzk * called CONSOPENPOLLEDIO so set the error to
32320280efdcSzk * ENOTSUP and NAK the request.
32330280efdcSzk */
32340280efdcSzk if (asy->asy_device_type == ASY_KEYBOARD) {
32350280efdcSzk error = ENOTSUP;
32360280efdcSzk break;
32370280efdcSzk }
32380280efdcSzk
32390280efdcSzk mp->b_datap->db_type = M_IOCACK;
32400280efdcSzk iocp->ioc_error = 0;
32410280efdcSzk iocp->ioc_rval = 0;
32420280efdcSzk break;
32430280efdcSzk
32447c478bd9Sstevel@tonic-gate default: /* unexpected ioctl type */
32457c478bd9Sstevel@tonic-gate /*
32467c478bd9Sstevel@tonic-gate * If we don't understand it, it's an error. NAK it.
32477c478bd9Sstevel@tonic-gate */
32487c478bd9Sstevel@tonic-gate error = EINVAL;
32497c478bd9Sstevel@tonic-gate break;
32507c478bd9Sstevel@tonic-gate }
32517c478bd9Sstevel@tonic-gate }
32527c478bd9Sstevel@tonic-gate if (error != 0) {
32537c478bd9Sstevel@tonic-gate iocp->ioc_error = error;
32547c478bd9Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK;
32557c478bd9Sstevel@tonic-gate }
32567c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
32577c478bd9Sstevel@tonic-gate qreply(wq, mp);
32587c478bd9Sstevel@tonic-gate }
32597c478bd9Sstevel@tonic-gate
3260*e476cc14SToomas Soome static int
asyrsrv(queue_t * q)32617c478bd9Sstevel@tonic-gate asyrsrv(queue_t *q)
32627c478bd9Sstevel@tonic-gate {
32637c478bd9Sstevel@tonic-gate mblk_t *bp;
32647c478bd9Sstevel@tonic-gate struct asyncline *async;
32657c478bd9Sstevel@tonic-gate
32667c478bd9Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr;
32677c478bd9Sstevel@tonic-gate
32687c478bd9Sstevel@tonic-gate while (canputnext(q) && (bp = getq(q)))
32697c478bd9Sstevel@tonic-gate putnext(q, bp);
32707c478bd9Sstevel@tonic-gate ASYSETSOFT(async->async_common);
32717c478bd9Sstevel@tonic-gate async->async_polltid = 0;
3272*e476cc14SToomas Soome return (0);
32737c478bd9Sstevel@tonic-gate }
32747c478bd9Sstevel@tonic-gate
32757c478bd9Sstevel@tonic-gate /*
32767c478bd9Sstevel@tonic-gate * Put procedure for write queue.
32777c478bd9Sstevel@tonic-gate * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
32787c478bd9Sstevel@tonic-gate * set the flow control character for M_STOPI and M_STARTI messages;
32797c478bd9Sstevel@tonic-gate * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
32807c478bd9Sstevel@tonic-gate * by the start routine, and then call the start routine; discard
32817c478bd9Sstevel@tonic-gate * everything else. Note that this driver does not incorporate any
32827c478bd9Sstevel@tonic-gate * mechanism to negotiate to handle the canonicalization process.
32837c478bd9Sstevel@tonic-gate * It expects that these functions are handled in upper module(s),
32847c478bd9Sstevel@tonic-gate * as we do in ldterm.
32857c478bd9Sstevel@tonic-gate */
3286*e476cc14SToomas Soome static int
asywput(queue_t * q,mblk_t * mp)32877c478bd9Sstevel@tonic-gate asywput(queue_t *q, mblk_t *mp)
32887c478bd9Sstevel@tonic-gate {
32897c478bd9Sstevel@tonic-gate register struct asyncline *async;
32907c478bd9Sstevel@tonic-gate register struct asycom *asy;
32917c478bd9Sstevel@tonic-gate int error;
32927c478bd9Sstevel@tonic-gate
32937c478bd9Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr;
32947c478bd9Sstevel@tonic-gate asy = async->async_common;
32957c478bd9Sstevel@tonic-gate
32967c478bd9Sstevel@tonic-gate switch (mp->b_datap->db_type) {
32977c478bd9Sstevel@tonic-gate
32987c478bd9Sstevel@tonic-gate case M_STOP:
32997c478bd9Sstevel@tonic-gate /*
33007c478bd9Sstevel@tonic-gate * Since we don't do real DMA, we can just let the
33017c478bd9Sstevel@tonic-gate * chip coast to a stop after applying the brakes.
33027c478bd9Sstevel@tonic-gate */
33037c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
33047c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_STOPPED;
33057c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
33067c478bd9Sstevel@tonic-gate freemsg(mp);
33077c478bd9Sstevel@tonic-gate break;
33087c478bd9Sstevel@tonic-gate
33097c478bd9Sstevel@tonic-gate case M_START:
33107c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
33117c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_STOPPED) {
33127c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED;
33137c478bd9Sstevel@tonic-gate /*
33147c478bd9Sstevel@tonic-gate * If an output operation is in progress,
33157c478bd9Sstevel@tonic-gate * resume it. Otherwise, prod the start
33167c478bd9Sstevel@tonic-gate * routine.
33177c478bd9Sstevel@tonic-gate */
33187c478bd9Sstevel@tonic-gate if (async->async_ocnt > 0) {
33197c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
33207c478bd9Sstevel@tonic-gate async_resume(async);
33217c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
33227c478bd9Sstevel@tonic-gate } else {
33237c478bd9Sstevel@tonic-gate async_start(async);
33247c478bd9Sstevel@tonic-gate }
33257c478bd9Sstevel@tonic-gate }
33267c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
33277c478bd9Sstevel@tonic-gate freemsg(mp);
33287c478bd9Sstevel@tonic-gate break;
33297c478bd9Sstevel@tonic-gate
33307c478bd9Sstevel@tonic-gate case M_IOCTL:
33317c478bd9Sstevel@tonic-gate switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
33327c478bd9Sstevel@tonic-gate
33337c478bd9Sstevel@tonic-gate case TCSBRK:
33347c478bd9Sstevel@tonic-gate error = miocpullup(mp, sizeof (int));
33357c478bd9Sstevel@tonic-gate if (error != 0) {
33367c478bd9Sstevel@tonic-gate miocnak(q, mp, 0, error);
3337*e476cc14SToomas Soome return (0);
33387c478bd9Sstevel@tonic-gate }
33397c478bd9Sstevel@tonic-gate
33407c478bd9Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr != 0) {
33417c478bd9Sstevel@tonic-gate #ifdef DEBUG
33427c478bd9Sstevel@tonic-gate if (asydebug & ASY_DEBUG_CLOSE)
33437c478bd9Sstevel@tonic-gate printf("asy%d: flush request.\n",
33447c478bd9Sstevel@tonic-gate UNIT(async->async_dev));
33457c478bd9Sstevel@tonic-gate #endif
33467c478bd9Sstevel@tonic-gate (void) putq(q, mp);
33477c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
33487c478bd9Sstevel@tonic-gate async_nstart(async, 1);
33497c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
33507c478bd9Sstevel@tonic-gate break;
33517c478bd9Sstevel@tonic-gate }
33527c478bd9Sstevel@tonic-gate /*FALLTHROUGH*/
33537c478bd9Sstevel@tonic-gate case TCSETSW:
33547c478bd9Sstevel@tonic-gate case TCSETSF:
33557c478bd9Sstevel@tonic-gate case TCSETAW:
33567c478bd9Sstevel@tonic-gate case TCSETAF:
33577c478bd9Sstevel@tonic-gate /*
33587c478bd9Sstevel@tonic-gate * The changes do not take effect until all
33597c478bd9Sstevel@tonic-gate * output queued before them is drained.
33607c478bd9Sstevel@tonic-gate * Put this message on the queue, so that
33617c478bd9Sstevel@tonic-gate * "async_start" will see it when it's done
33627c478bd9Sstevel@tonic-gate * with the output before it. Poke the
33637c478bd9Sstevel@tonic-gate * start routine, just in case.
33647c478bd9Sstevel@tonic-gate */
33657c478bd9Sstevel@tonic-gate (void) putq(q, mp);
33667c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
33677c478bd9Sstevel@tonic-gate async_start(async);
33687c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
33697c478bd9Sstevel@tonic-gate break;
33707c478bd9Sstevel@tonic-gate
33717c478bd9Sstevel@tonic-gate default:
33727c478bd9Sstevel@tonic-gate /*
33737c478bd9Sstevel@tonic-gate * Do it now.
33747c478bd9Sstevel@tonic-gate */
33757c478bd9Sstevel@tonic-gate async_ioctl(async, q, mp, B_TRUE);
33767c478bd9Sstevel@tonic-gate break;
33777c478bd9Sstevel@tonic-gate }
33787c478bd9Sstevel@tonic-gate break;
33797c478bd9Sstevel@tonic-gate
33807c478bd9Sstevel@tonic-gate case M_FLUSH:
33817c478bd9Sstevel@tonic-gate if (*mp->b_rptr & FLUSHW) {
33827c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
33837c478bd9Sstevel@tonic-gate
33847c478bd9Sstevel@tonic-gate /*
33857c478bd9Sstevel@tonic-gate * Abort any output in progress.
33867c478bd9Sstevel@tonic-gate */
33877c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
33887c478bd9Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) {
33897c478bd9Sstevel@tonic-gate async->async_ocnt = 0;
33907c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY;
33917c478bd9Sstevel@tonic-gate }
33927c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
33937c478bd9Sstevel@tonic-gate
33947c478bd9Sstevel@tonic-gate /* Flush FIFO buffers */
33957c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) {
33967c478bd9Sstevel@tonic-gate OUTB(FIFOR, FIFO_ON | FIFODMA | FIFOTXFLSH |
33977c478bd9Sstevel@tonic-gate (asy->asy_trig_level & 0xff));
33987c478bd9Sstevel@tonic-gate }
33997c478bd9Sstevel@tonic-gate
34007c478bd9Sstevel@tonic-gate /*
34017c478bd9Sstevel@tonic-gate * Flush our write queue.
34027c478bd9Sstevel@tonic-gate */
34037c478bd9Sstevel@tonic-gate flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */
34047c478bd9Sstevel@tonic-gate if (async->async_xmitblk != NULL) {
34057c478bd9Sstevel@tonic-gate freeb(async->async_xmitblk);
34067c478bd9Sstevel@tonic-gate async->async_xmitblk = NULL;
34077c478bd9Sstevel@tonic-gate }
34087c478bd9Sstevel@tonic-gate
34097c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
34107c478bd9Sstevel@tonic-gate *mp->b_rptr &= ~FLUSHW; /* it has been flushed */
34117c478bd9Sstevel@tonic-gate }
34127c478bd9Sstevel@tonic-gate if (*mp->b_rptr & FLUSHR) {
34137c478bd9Sstevel@tonic-gate /* Flush FIFO buffers */
34147c478bd9Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) {
34157c478bd9Sstevel@tonic-gate OUTB(FIFOR, FIFO_ON | FIFODMA | FIFORXFLSH |
34167c478bd9Sstevel@tonic-gate (asy->asy_trig_level & 0xff));
34177c478bd9Sstevel@tonic-gate }
34187c478bd9Sstevel@tonic-gate flushq(RD(q), FLUSHDATA);
34197c478bd9Sstevel@tonic-gate qreply(q, mp); /* give the read queues a crack at it */
34207c478bd9Sstevel@tonic-gate } else {
34217c478bd9Sstevel@tonic-gate freemsg(mp);
34227c478bd9Sstevel@tonic-gate }
34237c478bd9Sstevel@tonic-gate
34247c478bd9Sstevel@tonic-gate /*
34257c478bd9Sstevel@tonic-gate * We must make sure we process messages that survive the
34267c478bd9Sstevel@tonic-gate * write-side flush. Without this call, the close protocol
34277c478bd9Sstevel@tonic-gate * with ldterm can hang forever. (ldterm will have sent us a
34287c478bd9Sstevel@tonic-gate * TCSBRK ioctl that it expects a response to.)
34297c478bd9Sstevel@tonic-gate */
34307c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
34317c478bd9Sstevel@tonic-gate async_start(async);
34327c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
34337c478bd9Sstevel@tonic-gate break;
34347c478bd9Sstevel@tonic-gate case M_BREAK:
34357c478bd9Sstevel@tonic-gate case M_DELAY:
34367c478bd9Sstevel@tonic-gate case M_DATA:
34377c478bd9Sstevel@tonic-gate /*
34387c478bd9Sstevel@tonic-gate * Queue the message up to be transmitted,
34397c478bd9Sstevel@tonic-gate * and poke the start routine.
34407c478bd9Sstevel@tonic-gate */
34417c478bd9Sstevel@tonic-gate (void) putq(q, mp);
34427c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
34437c478bd9Sstevel@tonic-gate async_start(async);
34447c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
34457c478bd9Sstevel@tonic-gate break;
34467c478bd9Sstevel@tonic-gate
34477c478bd9Sstevel@tonic-gate case M_STOPI:
34487c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
34497c478bd9Sstevel@tonic-gate async->async_flowc = async->async_stopc;
34507c478bd9Sstevel@tonic-gate async_start(async); /* poke the start routine */
34517c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
34527c478bd9Sstevel@tonic-gate freemsg(mp);
34537c478bd9Sstevel@tonic-gate break;
34547c478bd9Sstevel@tonic-gate
34557c478bd9Sstevel@tonic-gate case M_STARTI:
34567c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
34577c478bd9Sstevel@tonic-gate async->async_flowc = async->async_startc;
34587c478bd9Sstevel@tonic-gate async_start(async); /* poke the start routine */
34597c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
34607c478bd9Sstevel@tonic-gate freemsg(mp);
34617c478bd9Sstevel@tonic-gate break;
34627c478bd9Sstevel@tonic-gate
34637c478bd9Sstevel@tonic-gate case M_CTL:
34647c478bd9Sstevel@tonic-gate if (MBLKL(mp) >= sizeof (struct iocblk) &&
34657c478bd9Sstevel@tonic-gate ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
34667c478bd9Sstevel@tonic-gate ((struct iocblk *)mp->b_rptr)->ioc_cmd = MC_HAS_POSIX;
34677c478bd9Sstevel@tonic-gate qreply(q, mp);
34687c478bd9Sstevel@tonic-gate } else {
34697c478bd9Sstevel@tonic-gate /*
34707c478bd9Sstevel@tonic-gate * These MC_SERVICE type messages are used by upper
34717c478bd9Sstevel@tonic-gate * modules to tell this driver to send input up
34727c478bd9Sstevel@tonic-gate * immediately, or that it can wait for normal
34737c478bd9Sstevel@tonic-gate * processing that may or may not be done. Sun
34747c478bd9Sstevel@tonic-gate * requires these for the mouse module.
34757c478bd9Sstevel@tonic-gate * (XXX - for x86?)
34767c478bd9Sstevel@tonic-gate */
34777c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
34787c478bd9Sstevel@tonic-gate switch (*mp->b_rptr) {
34797c478bd9Sstevel@tonic-gate
34807c478bd9Sstevel@tonic-gate case MC_SERVICEIMM:
34817c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_SERVICEIMM;
34827c478bd9Sstevel@tonic-gate break;
34837c478bd9Sstevel@tonic-gate
34847c478bd9Sstevel@tonic-gate case MC_SERVICEDEF:
34857c478bd9Sstevel@tonic-gate async->async_flags &= ~ASYNC_SERVICEIMM;
34867c478bd9Sstevel@tonic-gate break;
34877c478bd9Sstevel@tonic-gate }
34887c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
34897c478bd9Sstevel@tonic-gate freemsg(mp);
34907c478bd9Sstevel@tonic-gate }
34917c478bd9Sstevel@tonic-gate break;
34927c478bd9Sstevel@tonic-gate
34937c478bd9Sstevel@tonic-gate case M_IOCDATA:
34947c478bd9Sstevel@tonic-gate async_iocdata(q, mp);
34957c478bd9Sstevel@tonic-gate break;
34967c478bd9Sstevel@tonic-gate
34977c478bd9Sstevel@tonic-gate default:
34987c478bd9Sstevel@tonic-gate freemsg(mp);
34997c478bd9Sstevel@tonic-gate break;
35007c478bd9Sstevel@tonic-gate }
3501*e476cc14SToomas Soome return (0);
35027c478bd9Sstevel@tonic-gate }
35037c478bd9Sstevel@tonic-gate
35047c478bd9Sstevel@tonic-gate /*
35057c478bd9Sstevel@tonic-gate * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
35067c478bd9Sstevel@tonic-gate * the buffer we need.
35077c478bd9Sstevel@tonic-gate */
35087c478bd9Sstevel@tonic-gate static void
async_reioctl(void * arg)35097c478bd9Sstevel@tonic-gate async_reioctl(void *arg)
35107c478bd9Sstevel@tonic-gate {
35117c478bd9Sstevel@tonic-gate struct asyncline *async = arg;
35127c478bd9Sstevel@tonic-gate struct asycom *asy = async->async_common;
35137c478bd9Sstevel@tonic-gate queue_t *q;
35147c478bd9Sstevel@tonic-gate mblk_t *mp;
35157c478bd9Sstevel@tonic-gate
35167c478bd9Sstevel@tonic-gate /*
35177c478bd9Sstevel@tonic-gate * The bufcall is no longer pending.
35187c478bd9Sstevel@tonic-gate */
35197c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
35207c478bd9Sstevel@tonic-gate async->async_wbufcid = 0;
35217c478bd9Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) {
35227c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
35237c478bd9Sstevel@tonic-gate return;
35247c478bd9Sstevel@tonic-gate }
35257c478bd9Sstevel@tonic-gate if ((mp = async->async_ttycommon.t_iocpending) != NULL) {
35267c478bd9Sstevel@tonic-gate /* not pending any more */
35277c478bd9Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL;
35287c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
35297c478bd9Sstevel@tonic-gate /* not in STREAMS queue; we no longer know if we're in wput */
35307c478bd9Sstevel@tonic-gate async_ioctl(async, q, mp, B_TRUE);
35317c478bd9Sstevel@tonic-gate } else
35327c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
35337c478bd9Sstevel@tonic-gate }
35347c478bd9Sstevel@tonic-gate
35357c478bd9Sstevel@tonic-gate static void
async_iocdata(queue_t * q,mblk_t * mp)35367c478bd9Sstevel@tonic-gate async_iocdata(queue_t *q, mblk_t *mp)
35377c478bd9Sstevel@tonic-gate {
35387c478bd9Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)q->q_ptr;
35397c478bd9Sstevel@tonic-gate struct asycom *asy;
35407c478bd9Sstevel@tonic-gate struct copyresp *csp;
35417c478bd9Sstevel@tonic-gate
35427c478bd9Sstevel@tonic-gate asy = async->async_common;
35437c478bd9Sstevel@tonic-gate csp = (struct copyresp *)mp->b_rptr;
35447c478bd9Sstevel@tonic-gate
35457c478bd9Sstevel@tonic-gate if (csp->cp_rval != 0) {
35467c478bd9Sstevel@tonic-gate freemsg(mp);
35477c478bd9Sstevel@tonic-gate return;
35487c478bd9Sstevel@tonic-gate }
35497c478bd9Sstevel@tonic-gate
35507c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl);
35517c478bd9Sstevel@tonic-gate
35527c478bd9Sstevel@tonic-gate switch (csp->cp_cmd) {
35537c478bd9Sstevel@tonic-gate case TIOCMSET:
35547c478bd9Sstevel@tonic-gate case TIOCMBIS:
35557c478bd9Sstevel@tonic-gate case TIOCMBIC:
35567c478bd9Sstevel@tonic-gate if (mp->b_cont == NULL) {
35577c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
35587c478bd9Sstevel@tonic-gate miocnak(q, mp, 0, EINVAL);
35597c478bd9Sstevel@tonic-gate break;
35607c478bd9Sstevel@tonic-gate }
35617c478bd9Sstevel@tonic-gate
35627c478bd9Sstevel@tonic-gate mutex_enter(asy->asy_excl_hi);
35637c478bd9Sstevel@tonic-gate (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr),
35640280efdcSzk csp->cp_cmd);
35657c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl_hi);
35667c478bd9Sstevel@tonic-gate
35677c478bd9Sstevel@tonic-gate freemsg(mp->b_cont);
35687c478bd9Sstevel@tonic-gate mp->b_cont = NULL;
35697c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
35707c478bd9Sstevel@tonic-gate miocack(q, mp, 0, 0);
35717c478bd9Sstevel@tonic-gate break;
35727c478bd9Sstevel@tonic-gate
35737c478bd9Sstevel@tonic-gate case TIOCMGET:
35747c478bd9Sstevel@tonic-gate if (mp->b_cont != NULL) {
35757c478bd9Sstevel@tonic-gate freemsg(mp->b_cont);
35767c478bd9Sstevel@tonic-gate mp->b_cont = NULL;
35777c478bd9Sstevel@tonic-gate }
35787c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
35797c478bd9Sstevel@tonic-gate miocack(q, mp, 0, 0);
35807c478bd9Sstevel@tonic-gate break;
35817c478bd9Sstevel@tonic-gate
35827c478bd9Sstevel@tonic-gate default:
35837c478bd9Sstevel@tonic-gate mutex_exit(asy->asy_excl);
35847c478bd9Sstevel@tonic-gate miocnak(q, mp, 0, EINVAL);
35857c478bd9Sstevel@tonic-gate break;
35867c478bd9Sstevel@tonic-gate }
35877c478bd9Sstevel@tonic-gate }
35887c478bd9Sstevel@tonic-gate
35897c478bd9Sstevel@tonic-gate
35907c478bd9Sstevel@tonic-gate /*
35917c478bd9Sstevel@tonic-gate * Set or get the modem control status.
35927c478bd9Sstevel@tonic-gate */
35937c478bd9Sstevel@tonic-gate static int
asymctl(struct asycom * asy,int bits,int how)35947c478bd9Sstevel@tonic-gate asymctl(struct asycom *asy, int bits, int how)
35957c478bd9Sstevel@tonic-gate {
35967c478bd9Sstevel@tonic-gate register int mcr_r, msr_r;
35977c478bd9Sstevel@tonic-gate
35987c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl_hi));
35997c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl));
36007c478bd9Sstevel@tonic-gate
36017c478bd9Sstevel@tonic-gate /* Read Modem Control Registers */
36027c478bd9Sstevel@tonic-gate mcr_r = INB(MCR);
36037c478bd9Sstevel@tonic-gate
36047c478bd9Sstevel@tonic-gate switch (how) {
36057c478bd9Sstevel@tonic-gate
36067c478bd9Sstevel@tonic-gate case TIOCMSET:
36077c478bd9Sstevel@tonic-gate mcr_r = bits;
36087c478bd9Sstevel@tonic-gate break;
36097c478bd9Sstevel@tonic-gate
36107c478bd9Sstevel@tonic-gate case TIOCMBIS:
36117c478bd9Sstevel@tonic-gate mcr_r |= bits; /* Set bits from input */
36127c478bd9Sstevel@tonic-gate break;
36137c478bd9Sstevel@tonic-gate
36147c478bd9Sstevel@tonic-gate case TIOCMBIC:
36157c478bd9Sstevel@tonic-gate mcr_r &= ~bits; /* Set ~bits from input */
36167c478bd9Sstevel@tonic-gate break;
36177c478bd9Sstevel@tonic-gate
36187c478bd9Sstevel@tonic-gate case TIOCMGET:
36197c478bd9Sstevel@tonic-gate /* Read Modem Status Registers */
36207c478bd9Sstevel@tonic-gate if (INB(ICR) & MIEN)
36217c478bd9Sstevel@tonic-gate msr_r = asy->asy_cached_msr;
36227c478bd9Sstevel@tonic-gate else
36237c478bd9Sstevel@tonic-gate msr_r = INB(MSR);
36247c478bd9Sstevel@tonic-gate return (asytodm(mcr_r, msr_r));
36257c478bd9Sstevel@tonic-gate }
36267c478bd9Sstevel@tonic-gate
36277c478bd9Sstevel@tonic-gate OUTB(MCR, mcr_r);
36287c478bd9Sstevel@tonic-gate
36297c478bd9Sstevel@tonic-gate return (mcr_r);
36307c478bd9Sstevel@tonic-gate }
36317c478bd9Sstevel@tonic-gate
36327c478bd9Sstevel@tonic-gate static int
asytodm(int mcr_r,int msr_r)36337c478bd9Sstevel@tonic-gate asytodm(int mcr_r, int msr_r)
36347c478bd9Sstevel@tonic-gate {
36357c478bd9Sstevel@tonic-gate register int b = 0;
36367c478bd9Sstevel@tonic-gate
36377c478bd9Sstevel@tonic-gate
36387c478bd9Sstevel@tonic-gate /* MCR registers */
36397c478bd9Sstevel@tonic-gate if (mcr_r & RTS)
36407c478bd9Sstevel@tonic-gate b |= TIOCM_RTS;
36417c478bd9Sstevel@tonic-gate
36427c478bd9Sstevel@tonic-gate if (mcr_r & DTR)
36437c478bd9Sstevel@tonic-gate b |= TIOCM_DTR;
36447c478bd9Sstevel@tonic-gate
36457c478bd9Sstevel@tonic-gate /* MSR registers */
36467c478bd9Sstevel@tonic-gate if (msr_r & DCD)
36477c478bd9Sstevel@tonic-gate b |= TIOCM_CAR;
36487c478bd9Sstevel@tonic-gate
36497c478bd9Sstevel@tonic-gate if (msr_r & CTS)
36507c478bd9Sstevel@tonic-gate b |= TIOCM_CTS;
36517c478bd9Sstevel@tonic-gate
36527c478bd9Sstevel@tonic-gate if (msr_r & DSR)
36537c478bd9Sstevel@tonic-gate b |= TIOCM_DSR;
36547c478bd9Sstevel@tonic-gate
36557c478bd9Sstevel@tonic-gate if (msr_r & RI)
36567c478bd9Sstevel@tonic-gate b |= TIOCM_RNG;
36577c478bd9Sstevel@tonic-gate
36587c478bd9Sstevel@tonic-gate return (b);
36597c478bd9Sstevel@tonic-gate }
36607c478bd9Sstevel@tonic-gate
36617c478bd9Sstevel@tonic-gate static int
dmtoasy(int bits)36627c478bd9Sstevel@tonic-gate dmtoasy(int bits)
36637c478bd9Sstevel@tonic-gate {
36647c478bd9Sstevel@tonic-gate register int b = 0;
36657c478bd9Sstevel@tonic-gate
36667c478bd9Sstevel@tonic-gate #ifdef CAN_NOT_SET /* only DTR and RTS can be set */
36677c478bd9Sstevel@tonic-gate if (bits & TIOCM_CAR)
36687c478bd9Sstevel@tonic-gate b |= DCD;
36697c478bd9Sstevel@tonic-gate if (bits & TIOCM_CTS)
36707c478bd9Sstevel@tonic-gate b |= CTS;
36717c478bd9Sstevel@tonic-gate if (bits & TIOCM_DSR)
36727c478bd9Sstevel@tonic-gate b |= DSR;
36737c478bd9Sstevel@tonic-gate if (bits & TIOCM_RNG)
36747c478bd9Sstevel@tonic-gate b |= RI;
36757c478bd9Sstevel@tonic-gate #endif
36767c478bd9Sstevel@tonic-gate
36777c478bd9Sstevel@tonic-gate if (bits & TIOCM_RTS)
36787c478bd9Sstevel@tonic-gate b |= RTS;
36797c478bd9Sstevel@tonic-gate if (bits & TIOCM_DTR)
36807c478bd9Sstevel@tonic-gate b |= DTR;
36817c478bd9Sstevel@tonic-gate
36827c478bd9Sstevel@tonic-gate return (b);
36837c478bd9Sstevel@tonic-gate }
36847c478bd9Sstevel@tonic-gate
36857c478bd9Sstevel@tonic-gate static void
asycheckflowcontrol_hw(struct asycom * asy)36867c478bd9Sstevel@tonic-gate asycheckflowcontrol_hw(struct asycom *asy)
36877c478bd9Sstevel@tonic-gate {
36887c478bd9Sstevel@tonic-gate struct asyncline *async;
36897c478bd9Sstevel@tonic-gate uchar_t mcr, flag;
36907c478bd9Sstevel@tonic-gate
36917c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl_hi));
36927c478bd9Sstevel@tonic-gate
36937c478bd9Sstevel@tonic-gate async = (struct asyncline *)asy->asy_priv;
36947c478bd9Sstevel@tonic-gate ASSERT(async != NULL);
36957c478bd9Sstevel@tonic-gate
36967c478bd9Sstevel@tonic-gate if (async->async_ttycommon.t_cflag & CRTSXOFF) {
36977c478bd9Sstevel@tonic-gate mcr = INB(MCR);
36987c478bd9Sstevel@tonic-gate flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS;
36997c478bd9Sstevel@tonic-gate if (((mcr ^ flag) & RTS) != 0) {
37007c478bd9Sstevel@tonic-gate OUTB(MCR, (mcr ^ RTS));
37017c478bd9Sstevel@tonic-gate }
37027c478bd9Sstevel@tonic-gate }
37037c478bd9Sstevel@tonic-gate }
37047c478bd9Sstevel@tonic-gate
37057c478bd9Sstevel@tonic-gate static boolean_t
asycheckflowcontrol_sw(struct asycom * asy)37067c478bd9Sstevel@tonic-gate asycheckflowcontrol_sw(struct asycom *asy)
37077c478bd9Sstevel@tonic-gate {
37087c478bd9Sstevel@tonic-gate uchar_t ss;
37097c478bd9Sstevel@tonic-gate struct asyncline *async;
37107c478bd9Sstevel@tonic-gate int rval = B_FALSE;
37117c478bd9Sstevel@tonic-gate
37127c478bd9Sstevel@tonic-gate ASSERT(mutex_owned(asy->asy_excl_hi));
37137c478bd9Sstevel@tonic-gate
37147c478bd9Sstevel@tonic-gate async = (struct asyncline *)asy->asy_priv;
37157c478bd9Sstevel@tonic-gate ASSERT(async != NULL);
37167c478bd9Sstevel@tonic-gate
37177c478bd9Sstevel@tonic-gate if ((ss = async->async_flowc) != '\0' && (INB(LSR) & XHRE)) {
37187c478bd9Sstevel@tonic-gate /*
37197c478bd9Sstevel@tonic-gate * If we get this far, then we know that flowc is non-zero and
37207c478bd9Sstevel@tonic-gate * that there's transmit room available. We've "handled" the
37217c478bd9Sstevel@tonic-gate * request now, so clear it. If the user didn't ask for IXOFF,
37227c478bd9Sstevel@tonic-gate * then don't actually send anything, but wait for the next
37237c478bd9Sstevel@tonic-gate * opportunity.
37247c478bd9Sstevel@tonic-gate */
37257c478bd9Sstevel@tonic-gate async->async_flowc = '\0';
37267c478bd9Sstevel@tonic-gate if (async->async_ttycommon.t_iflag & IXOFF) {
37277c478bd9Sstevel@tonic-gate async->async_flags |= ASYNC_BUSY;
37287c478bd9Sstevel@tonic-gate OUTB(DAT, ss);
37297c478bd9Sstevel@tonic-gate rval = B_TRUE;
37307c478bd9Sstevel@tonic-gate }
37317c478bd9Sstevel@tonic-gate }
37327c478bd9Sstevel@tonic-gate
37337c478bd9Sstevel@tonic-gate return (rval);
37347c478bd9Sstevel@tonic-gate }
37357c478bd9Sstevel@tonic-gate
37367c478bd9Sstevel@tonic-gate /*
37377c478bd9Sstevel@tonic-gate * Check for abort character sequence
37387c478bd9Sstevel@tonic-gate */
37397c478bd9Sstevel@tonic-gate static boolean_t
abort_charseq_recognize(uchar_t ch)37407c478bd9Sstevel@tonic-gate abort_charseq_recognize(uchar_t ch)
37417c478bd9Sstevel@tonic-gate {
37427c478bd9Sstevel@tonic-gate static int state = 0;
37437c478bd9Sstevel@tonic-gate #define CNTRL(c) ((c)&037)
37447c478bd9Sstevel@tonic-gate static char sequence[] = { '\r', '~', CNTRL('b') };
37457c478bd9Sstevel@tonic-gate
37467c478bd9Sstevel@tonic-gate if (ch == sequence[state]) {
37477c478bd9Sstevel@tonic-gate if (++state >= sizeof (sequence)) {
37487c478bd9Sstevel@tonic-gate state = 0;
37497c478bd9Sstevel@tonic-gate return (B_TRUE);
37507c478bd9Sstevel@tonic-gate }
37517c478bd9Sstevel@tonic-gate } else {
37527c478bd9Sstevel@tonic-gate state = (ch == sequence[0]) ? 1 : 0;
37537c478bd9Sstevel@tonic-gate }
37547c478bd9Sstevel@tonic-gate return (B_FALSE);
37557c478bd9Sstevel@tonic-gate }
3756