10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51106Smrj * Common Development and Distribution License (the "License").
61106Smrj * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
211106Smrj
220Sstevel@tonic-gate /* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
230Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
240Sstevel@tonic-gate /* All Rights Reserved */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /*
2712401SZachary.Kissel@Sun.COM * Copyright (c) 1992, 2010, Oracle and/or its affiliates. All rights reserved.
280Sstevel@tonic-gate */
290Sstevel@tonic-gate
300Sstevel@tonic-gate
310Sstevel@tonic-gate /*
320Sstevel@tonic-gate * Serial I/O driver for 8250/16450/16550A/16650/16750 chips.
330Sstevel@tonic-gate */
340Sstevel@tonic-gate
350Sstevel@tonic-gate #include <sys/param.h>
360Sstevel@tonic-gate #include <sys/types.h>
370Sstevel@tonic-gate #include <sys/signal.h>
380Sstevel@tonic-gate #include <sys/stream.h>
390Sstevel@tonic-gate #include <sys/termio.h>
400Sstevel@tonic-gate #include <sys/errno.h>
410Sstevel@tonic-gate #include <sys/file.h>
420Sstevel@tonic-gate #include <sys/cmn_err.h>
430Sstevel@tonic-gate #include <sys/stropts.h>
440Sstevel@tonic-gate #include <sys/strsubr.h>
450Sstevel@tonic-gate #include <sys/strtty.h>
460Sstevel@tonic-gate #include <sys/debug.h>
470Sstevel@tonic-gate #include <sys/kbio.h>
480Sstevel@tonic-gate #include <sys/cred.h>
490Sstevel@tonic-gate #include <sys/stat.h>
500Sstevel@tonic-gate #include <sys/consdev.h>
510Sstevel@tonic-gate #include <sys/mkdev.h>
520Sstevel@tonic-gate #include <sys/kmem.h>
530Sstevel@tonic-gate #include <sys/cred.h>
540Sstevel@tonic-gate #include <sys/strsun.h>
550Sstevel@tonic-gate #ifdef DEBUG
560Sstevel@tonic-gate #include <sys/promif.h>
570Sstevel@tonic-gate #endif
580Sstevel@tonic-gate #include <sys/modctl.h>
590Sstevel@tonic-gate #include <sys/ddi.h>
600Sstevel@tonic-gate #include <sys/sunddi.h>
613359Smyers #include <sys/pci.h>
620Sstevel@tonic-gate #include <sys/asy.h>
630Sstevel@tonic-gate #include <sys/policy.h>
640Sstevel@tonic-gate
650Sstevel@tonic-gate /*
660Sstevel@tonic-gate * set the RX FIFO trigger_level to half the RX FIFO size for now
670Sstevel@tonic-gate * we may want to make this configurable later.
680Sstevel@tonic-gate */
690Sstevel@tonic-gate static int asy_trig_level = FIFO_TRIG_8;
700Sstevel@tonic-gate
710Sstevel@tonic-gate int asy_drain_check = 15000000; /* tunable: exit drain check time */
720Sstevel@tonic-gate int asy_min_dtr_low = 500000; /* tunable: minimum DTR down time */
730Sstevel@tonic-gate int asy_min_utbrk = 100000; /* tunable: minumum untimed brk time */
740Sstevel@tonic-gate
750Sstevel@tonic-gate int asymaxchip = ASY16750; /* tunable: limit chip support we look for */
760Sstevel@tonic-gate
770Sstevel@tonic-gate /*
780Sstevel@tonic-gate * Just in case someone has a chip with broken loopback mode, we provide a
790Sstevel@tonic-gate * means to disable the loopback test. By default, we only loopback test
800Sstevel@tonic-gate * UARTs which look like they have FIFOs bigger than 16 bytes.
810Sstevel@tonic-gate * Set to 0 to suppress test, or to 2 to enable test on any size FIFO.
820Sstevel@tonic-gate */
830Sstevel@tonic-gate int asy_fifo_test = 1; /* tunable: set to 0, 1, or 2 */
840Sstevel@tonic-gate
850Sstevel@tonic-gate /*
860Sstevel@tonic-gate * Allow ability to switch off testing of the scratch register.
870Sstevel@tonic-gate * Some UART emulators might not have it. This will also disable the test
880Sstevel@tonic-gate * for Exar/Startech ST16C650, as that requires use of the SCR register.
890Sstevel@tonic-gate */
900Sstevel@tonic-gate int asy_scr_test = 1; /* tunable: set to 0 to disable SCR reg test */
910Sstevel@tonic-gate
920Sstevel@tonic-gate /*
930Sstevel@tonic-gate * As we don't yet support on-chip flow control, it's a bad idea to put a
940Sstevel@tonic-gate * large number of characters in the TX FIFO, since if other end tells us
950Sstevel@tonic-gate * to stop transmitting, we can only stop filling the TX FIFO, but it will
960Sstevel@tonic-gate * still carry on draining by itself, so remote end still gets what's left
970Sstevel@tonic-gate * in the FIFO.
980Sstevel@tonic-gate */
990Sstevel@tonic-gate int asy_max_tx_fifo = 16; /* tunable: max fill of TX FIFO */
1000Sstevel@tonic-gate
1010Sstevel@tonic-gate #define async_stopc async_ttycommon.t_stopc
1020Sstevel@tonic-gate #define async_startc async_ttycommon.t_startc
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate #define ASY_INIT 1
1050Sstevel@tonic-gate #define ASY_NOINIT 0
1060Sstevel@tonic-gate
1070Sstevel@tonic-gate /* enum value for sw and hw flow control action */
1080Sstevel@tonic-gate typedef enum {
1090Sstevel@tonic-gate FLOW_CHECK,
1100Sstevel@tonic-gate FLOW_STOP,
1110Sstevel@tonic-gate FLOW_START
1120Sstevel@tonic-gate } async_flowc_action;
1130Sstevel@tonic-gate
1140Sstevel@tonic-gate #ifdef DEBUG
1150Sstevel@tonic-gate #define ASY_DEBUG_INIT 0x0001 /* Output msgs during driver initialization. */
1160Sstevel@tonic-gate #define ASY_DEBUG_INPUT 0x0002 /* Report characters received during int. */
1170Sstevel@tonic-gate #define ASY_DEBUG_EOT 0x0004 /* Output msgs when wait for xmit to finish. */
1180Sstevel@tonic-gate #define ASY_DEBUG_CLOSE 0x0008 /* Output msgs when driver open/close called */
1190Sstevel@tonic-gate #define ASY_DEBUG_HFLOW 0x0010 /* Output msgs when H/W flowcontrol is active */
1200Sstevel@tonic-gate #define ASY_DEBUG_PROCS 0x0020 /* Output each proc name as it is entered. */
1210Sstevel@tonic-gate #define ASY_DEBUG_STATE 0x0040 /* Output value of Interrupt Service Reg. */
1220Sstevel@tonic-gate #define ASY_DEBUG_INTR 0x0080 /* Output value of Interrupt Service Reg. */
1230Sstevel@tonic-gate #define ASY_DEBUG_OUT 0x0100 /* Output msgs about output events. */
1240Sstevel@tonic-gate #define ASY_DEBUG_BUSY 0x0200 /* Output msgs when xmit is enabled/disabled */
1250Sstevel@tonic-gate #define ASY_DEBUG_MODEM 0x0400 /* Output msgs about modem status & control. */
1260Sstevel@tonic-gate #define ASY_DEBUG_MODM2 0x0800 /* Output msgs about modem status & control. */
1270Sstevel@tonic-gate #define ASY_DEBUG_IOCTL 0x1000 /* Output msgs about ioctl messages. */
1280Sstevel@tonic-gate #define ASY_DEBUG_CHIP 0x2000 /* Output msgs about chip identification. */
1290Sstevel@tonic-gate #define ASY_DEBUG_SFLOW 0x4000 /* Output msgs when S/W flowcontrol is active */
1300Sstevel@tonic-gate #define ASY_DEBUG(x) (debug & (x))
1310Sstevel@tonic-gate static int debug = 0;
1320Sstevel@tonic-gate #else
1330Sstevel@tonic-gate #define ASY_DEBUG(x) B_FALSE
1340Sstevel@tonic-gate #endif
1350Sstevel@tonic-gate
1360Sstevel@tonic-gate /* pnpISA compressed device ids */
1370Sstevel@tonic-gate #define pnpMTS0219 0xb6930219 /* Multitech MT5634ZTX modem */
1380Sstevel@tonic-gate
1390Sstevel@tonic-gate /*
1400Sstevel@tonic-gate * PPS (Pulse Per Second) support.
1410Sstevel@tonic-gate */
1420Sstevel@tonic-gate void ddi_hardpps();
1430Sstevel@tonic-gate /*
1440Sstevel@tonic-gate * This is protected by the asy_excl_hi of the port on which PPS event
1450Sstevel@tonic-gate * handling is enabled. Note that only one port should have this enabled at
1460Sstevel@tonic-gate * any one time. Enabling PPS handling on multiple ports will result in
1470Sstevel@tonic-gate * unpredictable (but benign) results.
1480Sstevel@tonic-gate */
1490Sstevel@tonic-gate static struct ppsclockev asy_ppsev;
1500Sstevel@tonic-gate
1510Sstevel@tonic-gate #ifdef PPSCLOCKLED
1520Sstevel@tonic-gate /* XXX Use these to observe PPS latencies and jitter on a scope */
1530Sstevel@tonic-gate #define LED_ON
1540Sstevel@tonic-gate #define LED_OFF
1550Sstevel@tonic-gate #else
1560Sstevel@tonic-gate #define LED_ON
1570Sstevel@tonic-gate #define LED_OFF
1580Sstevel@tonic-gate #endif
1590Sstevel@tonic-gate
1600Sstevel@tonic-gate static int max_asy_instance = -1;
1610Sstevel@tonic-gate
1620Sstevel@tonic-gate static uint_t asysoftintr(caddr_t intarg);
1630Sstevel@tonic-gate static uint_t asyintr(caddr_t argasy);
1640Sstevel@tonic-gate
1650Sstevel@tonic-gate static boolean_t abort_charseq_recognize(uchar_t ch);
1660Sstevel@tonic-gate
1670Sstevel@tonic-gate /* The async interrupt entry points */
1680Sstevel@tonic-gate static void async_txint(struct asycom *asy);
1690Sstevel@tonic-gate static void async_rxint(struct asycom *asy, uchar_t lsr);
1700Sstevel@tonic-gate static void async_msint(struct asycom *asy);
1710Sstevel@tonic-gate static void async_softint(struct asycom *asy);
1720Sstevel@tonic-gate
1730Sstevel@tonic-gate static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp);
1740Sstevel@tonic-gate static void async_reioctl(void *unit);
1750Sstevel@tonic-gate static void async_iocdata(queue_t *q, mblk_t *mp);
1760Sstevel@tonic-gate static void async_restart(void *arg);
1770Sstevel@tonic-gate static void async_start(struct asyncline *async);
1780Sstevel@tonic-gate static void async_nstart(struct asyncline *async, int mode);
1790Sstevel@tonic-gate static void async_resume(struct asyncline *async);
1800Sstevel@tonic-gate static void asy_program(struct asycom *asy, int mode);
1810Sstevel@tonic-gate static void asyinit(struct asycom *asy);
1820Sstevel@tonic-gate static void asy_waiteot(struct asycom *asy);
1831762Slt200341 static void asyputchar(cons_polledio_arg_t, uchar_t c);
1841762Slt200341 static int asygetchar(cons_polledio_arg_t);
1851762Slt200341 static boolean_t asyischar(cons_polledio_arg_t);
1860Sstevel@tonic-gate
1870Sstevel@tonic-gate static int asymctl(struct asycom *, int, int);
1880Sstevel@tonic-gate static int asytodm(int, int);
1890Sstevel@tonic-gate static int dmtoasy(int);
1900Sstevel@tonic-gate /*PRINTFLIKE2*/
1910Sstevel@tonic-gate static void asyerror(int level, const char *fmt, ...) __KPRINTFLIKE(2);
1920Sstevel@tonic-gate static void asy_parse_mode(dev_info_t *devi, struct asycom *asy);
1930Sstevel@tonic-gate static void asy_soft_state_free(struct asycom *);
1940Sstevel@tonic-gate static char *asy_hw_name(struct asycom *asy);
1950Sstevel@tonic-gate static void async_hold_utbrk(void *arg);
1960Sstevel@tonic-gate static void async_resume_utbrk(struct asyncline *async);
1970Sstevel@tonic-gate static void async_dtr_free(struct asyncline *async);
1980Sstevel@tonic-gate static int asy_identify_chip(dev_info_t *devi, struct asycom *asy);
1990Sstevel@tonic-gate static void asy_reset_fifo(struct asycom *asy, uchar_t flags);
2000Sstevel@tonic-gate static int asy_getproperty(dev_info_t *devi, struct asycom *asy,
2010Sstevel@tonic-gate const char *property);
2020Sstevel@tonic-gate static boolean_t async_flowcontrol_sw_input(struct asycom *asy,
2030Sstevel@tonic-gate async_flowc_action onoff, int type);
2040Sstevel@tonic-gate static void async_flowcontrol_sw_output(struct asycom *asy,
2050Sstevel@tonic-gate async_flowc_action onoff);
2060Sstevel@tonic-gate static void async_flowcontrol_hw_input(struct asycom *asy,
2070Sstevel@tonic-gate async_flowc_action onoff, int type);
2080Sstevel@tonic-gate static void async_flowcontrol_hw_output(struct asycom *asy,
2090Sstevel@tonic-gate async_flowc_action onoff);
2100Sstevel@tonic-gate
2110Sstevel@tonic-gate #define GET_PROP(devi, pname, pflag, pval, plen) \
2120Sstevel@tonic-gate (ddi_prop_op(DDI_DEV_T_ANY, (devi), PROP_LEN_AND_VAL_BUF, \
2130Sstevel@tonic-gate (pflag), (pname), (caddr_t)(pval), (plen)))
2140Sstevel@tonic-gate
2150Sstevel@tonic-gate kmutex_t asy_glob_lock; /* lock protecting global data manipulation */
2160Sstevel@tonic-gate void *asy_soft_state;
2170Sstevel@tonic-gate
2180Sstevel@tonic-gate /* Standard COM port I/O addresses */
2190Sstevel@tonic-gate static const int standard_com_ports[] = {
2200Sstevel@tonic-gate COM1_IOADDR, COM2_IOADDR, COM3_IOADDR, COM4_IOADDR
2210Sstevel@tonic-gate };
2220Sstevel@tonic-gate
2230Sstevel@tonic-gate static int *com_ports;
2240Sstevel@tonic-gate static uint_t num_com_ports;
2250Sstevel@tonic-gate
2265295Srandyf #ifdef DEBUG
2275295Srandyf /*
2285295Srandyf * Set this to true to make the driver pretend to do a suspend. Useful
2295295Srandyf * for debugging suspend/resume code with a serial debugger.
2305295Srandyf */
2315295Srandyf boolean_t asy_nosuspend = B_FALSE;
2325295Srandyf #endif
2335295Srandyf
2345295Srandyf
2350Sstevel@tonic-gate /*
2360Sstevel@tonic-gate * Baud rate table. Indexed by #defines found in sys/termios.h
2370Sstevel@tonic-gate */
2380Sstevel@tonic-gate ushort_t asyspdtab[] = {
2390Sstevel@tonic-gate 0, /* 0 baud rate */
2400Sstevel@tonic-gate 0x900, /* 50 baud rate */
2410Sstevel@tonic-gate 0x600, /* 75 baud rate */
2420Sstevel@tonic-gate 0x417, /* 110 baud rate (%0.026) */
2430Sstevel@tonic-gate 0x359, /* 134 baud rate (%0.058) */
2440Sstevel@tonic-gate 0x300, /* 150 baud rate */
2450Sstevel@tonic-gate 0x240, /* 200 baud rate */
2460Sstevel@tonic-gate 0x180, /* 300 baud rate */
2470Sstevel@tonic-gate 0x0c0, /* 600 baud rate */
2480Sstevel@tonic-gate 0x060, /* 1200 baud rate */
2490Sstevel@tonic-gate 0x040, /* 1800 baud rate */
2500Sstevel@tonic-gate 0x030, /* 2400 baud rate */
2510Sstevel@tonic-gate 0x018, /* 4800 baud rate */
2520Sstevel@tonic-gate 0x00c, /* 9600 baud rate */
2530Sstevel@tonic-gate 0x006, /* 19200 baud rate */
2540Sstevel@tonic-gate 0x003, /* 38400 baud rate */
2550Sstevel@tonic-gate
2560Sstevel@tonic-gate 0x002, /* 57600 baud rate */
2570Sstevel@tonic-gate 0x0, /* 76800 baud rate not supported */
2580Sstevel@tonic-gate 0x001, /* 115200 baud rate */
2590Sstevel@tonic-gate 0x0, /* 153600 baud rate not supported */
2600Sstevel@tonic-gate 0x0, /* 0x8002 (SMC chip) 230400 baud rate not supported */
2610Sstevel@tonic-gate 0x0, /* 307200 baud rate not supported */
2620Sstevel@tonic-gate 0x0, /* 0x8001 (SMC chip) 460800 baud rate not supported */
2630Sstevel@tonic-gate 0x0, /* unused */
2640Sstevel@tonic-gate 0x0, /* unused */
2650Sstevel@tonic-gate 0x0, /* unused */
2660Sstevel@tonic-gate 0x0, /* unused */
2670Sstevel@tonic-gate 0x0, /* unused */
2680Sstevel@tonic-gate 0x0, /* unused */
2690Sstevel@tonic-gate 0x0, /* unused */
2700Sstevel@tonic-gate 0x0, /* unused */
2710Sstevel@tonic-gate 0x0, /* unused */
2720Sstevel@tonic-gate };
2730Sstevel@tonic-gate
2740Sstevel@tonic-gate static int asyrsrv(queue_t *q);
2750Sstevel@tonic-gate static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr);
2760Sstevel@tonic-gate static int asyclose(queue_t *q, int flag, cred_t *credp);
2775295Srandyf static int asywputdo(queue_t *q, mblk_t *mp, boolean_t);
2780Sstevel@tonic-gate static int asywput(queue_t *q, mblk_t *mp);
2790Sstevel@tonic-gate
2800Sstevel@tonic-gate struct module_info asy_info = {
2810Sstevel@tonic-gate 0,
2820Sstevel@tonic-gate "asy",
2830Sstevel@tonic-gate 0,
2840Sstevel@tonic-gate INFPSZ,
2850Sstevel@tonic-gate 4096,
2860Sstevel@tonic-gate 128
2870Sstevel@tonic-gate };
2880Sstevel@tonic-gate
2890Sstevel@tonic-gate static struct qinit asy_rint = {
2900Sstevel@tonic-gate putq,
2910Sstevel@tonic-gate asyrsrv,
2920Sstevel@tonic-gate asyopen,
2930Sstevel@tonic-gate asyclose,
2940Sstevel@tonic-gate NULL,
2950Sstevel@tonic-gate &asy_info,
2960Sstevel@tonic-gate NULL
2970Sstevel@tonic-gate };
2980Sstevel@tonic-gate
2990Sstevel@tonic-gate static struct qinit asy_wint = {
3000Sstevel@tonic-gate asywput,
3010Sstevel@tonic-gate NULL,
3020Sstevel@tonic-gate NULL,
3030Sstevel@tonic-gate NULL,
3040Sstevel@tonic-gate NULL,
3050Sstevel@tonic-gate &asy_info,
3060Sstevel@tonic-gate NULL
3070Sstevel@tonic-gate };
3080Sstevel@tonic-gate
3090Sstevel@tonic-gate struct streamtab asy_str_info = {
3100Sstevel@tonic-gate &asy_rint,
3110Sstevel@tonic-gate &asy_wint,
3120Sstevel@tonic-gate NULL,
3130Sstevel@tonic-gate NULL
3140Sstevel@tonic-gate };
3150Sstevel@tonic-gate
3160Sstevel@tonic-gate static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
3170Sstevel@tonic-gate void **result);
3180Sstevel@tonic-gate static int asyprobe(dev_info_t *);
3190Sstevel@tonic-gate static int asyattach(dev_info_t *, ddi_attach_cmd_t);
3200Sstevel@tonic-gate static int asydetach(dev_info_t *, ddi_detach_cmd_t);
3217656SSherry.Moore@Sun.COM static int asyquiesce(dev_info_t *);
3220Sstevel@tonic-gate
3230Sstevel@tonic-gate static struct cb_ops cb_asy_ops = {
3240Sstevel@tonic-gate nodev, /* cb_open */
3250Sstevel@tonic-gate nodev, /* cb_close */
3260Sstevel@tonic-gate nodev, /* cb_strategy */
3270Sstevel@tonic-gate nodev, /* cb_print */
3280Sstevel@tonic-gate nodev, /* cb_dump */
3290Sstevel@tonic-gate nodev, /* cb_read */
3300Sstevel@tonic-gate nodev, /* cb_write */
3310Sstevel@tonic-gate nodev, /* cb_ioctl */
3320Sstevel@tonic-gate nodev, /* cb_devmap */
3330Sstevel@tonic-gate nodev, /* cb_mmap */
3340Sstevel@tonic-gate nodev, /* cb_segmap */
3350Sstevel@tonic-gate nochpoll, /* cb_chpoll */
3360Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */
3370Sstevel@tonic-gate &asy_str_info, /* cb_stream */
3380Sstevel@tonic-gate D_MP /* cb_flag */
3390Sstevel@tonic-gate };
3400Sstevel@tonic-gate
3410Sstevel@tonic-gate struct dev_ops asy_ops = {
3420Sstevel@tonic-gate DEVO_REV, /* devo_rev */
3430Sstevel@tonic-gate 0, /* devo_refcnt */
3440Sstevel@tonic-gate asyinfo, /* devo_getinfo */
3450Sstevel@tonic-gate nulldev, /* devo_identify */
3460Sstevel@tonic-gate asyprobe, /* devo_probe */
3470Sstevel@tonic-gate asyattach, /* devo_attach */
3480Sstevel@tonic-gate asydetach, /* devo_detach */
3490Sstevel@tonic-gate nodev, /* devo_reset */
3500Sstevel@tonic-gate &cb_asy_ops, /* devo_cb_ops */
3517656SSherry.Moore@Sun.COM NULL, /* devo_bus_ops */
3527656SSherry.Moore@Sun.COM NULL, /* power */
3537656SSherry.Moore@Sun.COM asyquiesce, /* quiesce */
3540Sstevel@tonic-gate };
3550Sstevel@tonic-gate
3560Sstevel@tonic-gate static struct modldrv modldrv = {
3570Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */
3587656SSherry.Moore@Sun.COM "ASY driver",
3590Sstevel@tonic-gate &asy_ops, /* driver ops */
3600Sstevel@tonic-gate };
3610Sstevel@tonic-gate
3620Sstevel@tonic-gate static struct modlinkage modlinkage = {
3630Sstevel@tonic-gate MODREV_1,
3640Sstevel@tonic-gate (void *)&modldrv,
3650Sstevel@tonic-gate NULL
3660Sstevel@tonic-gate };
3670Sstevel@tonic-gate
3680Sstevel@tonic-gate int
_init(void)3690Sstevel@tonic-gate _init(void)
3700Sstevel@tonic-gate {
3710Sstevel@tonic-gate int i;
3720Sstevel@tonic-gate
3730Sstevel@tonic-gate i = ddi_soft_state_init(&asy_soft_state, sizeof (struct asycom), 2);
3740Sstevel@tonic-gate if (i == 0) {
3750Sstevel@tonic-gate mutex_init(&asy_glob_lock, NULL, MUTEX_DRIVER, NULL);
3760Sstevel@tonic-gate if ((i = mod_install(&modlinkage)) != 0) {
3770Sstevel@tonic-gate mutex_destroy(&asy_glob_lock);
3780Sstevel@tonic-gate ddi_soft_state_fini(&asy_soft_state);
3790Sstevel@tonic-gate } else {
3800Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INIT, "%s, debug = %x\n",
3810Sstevel@tonic-gate modldrv.drv_linkinfo, debug);
3820Sstevel@tonic-gate }
3830Sstevel@tonic-gate }
3840Sstevel@tonic-gate return (i);
3850Sstevel@tonic-gate }
3860Sstevel@tonic-gate
3870Sstevel@tonic-gate int
_fini(void)3880Sstevel@tonic-gate _fini(void)
3890Sstevel@tonic-gate {
3900Sstevel@tonic-gate int i;
3910Sstevel@tonic-gate
3920Sstevel@tonic-gate if ((i = mod_remove(&modlinkage)) == 0) {
3930Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "%s unloading\n",
3940Sstevel@tonic-gate modldrv.drv_linkinfo);
3950Sstevel@tonic-gate ASSERT(max_asy_instance == -1);
3960Sstevel@tonic-gate mutex_destroy(&asy_glob_lock);
3970Sstevel@tonic-gate /* free "motherboard-serial-ports" property if allocated */
3980Sstevel@tonic-gate if (com_ports != NULL && com_ports != (int *)standard_com_ports)
3995295Srandyf ddi_prop_free(com_ports);
4000Sstevel@tonic-gate com_ports = NULL;
4010Sstevel@tonic-gate ddi_soft_state_fini(&asy_soft_state);
4020Sstevel@tonic-gate }
4030Sstevel@tonic-gate return (i);
4040Sstevel@tonic-gate }
4050Sstevel@tonic-gate
4060Sstevel@tonic-gate int
_info(struct modinfo * modinfop)4070Sstevel@tonic-gate _info(struct modinfo *modinfop)
4080Sstevel@tonic-gate {
4090Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop));
4100Sstevel@tonic-gate }
4110Sstevel@tonic-gate
4125295Srandyf void
async_put_suspq(struct asycom * asy,mblk_t * mp)4135295Srandyf async_put_suspq(struct asycom *asy, mblk_t *mp)
4145295Srandyf {
4155295Srandyf struct asyncline *async = asy->asy_priv;
4165295Srandyf
4175295Srandyf ASSERT(mutex_owned(&asy->asy_excl));
4185295Srandyf
4195295Srandyf if (async->async_suspqf == NULL)
4205295Srandyf async->async_suspqf = mp;
4215295Srandyf else
4225295Srandyf async->async_suspqb->b_next = mp;
4235295Srandyf
4245295Srandyf async->async_suspqb = mp;
4255295Srandyf }
4265295Srandyf
4275295Srandyf static mblk_t *
async_get_suspq(struct asycom * asy)4285295Srandyf async_get_suspq(struct asycom *asy)
4295295Srandyf {
4305295Srandyf struct asyncline *async = asy->asy_priv;
4315295Srandyf mblk_t *mp;
4325295Srandyf
4335295Srandyf ASSERT(mutex_owned(&asy->asy_excl));
4345295Srandyf
4355295Srandyf if ((mp = async->async_suspqf) != NULL) {
4365295Srandyf async->async_suspqf = mp->b_next;
4375295Srandyf mp->b_next = NULL;
4385295Srandyf } else {
4395295Srandyf async->async_suspqb = NULL;
4405295Srandyf }
4415295Srandyf return (mp);
4425295Srandyf }
4435295Srandyf
4445295Srandyf static void
async_process_suspq(struct asycom * asy)4455295Srandyf async_process_suspq(struct asycom *asy)
4465295Srandyf {
4475295Srandyf struct asyncline *async = asy->asy_priv;
4485295Srandyf mblk_t *mp;
4495295Srandyf
4505295Srandyf ASSERT(mutex_owned(&asy->asy_excl));
4515295Srandyf
4525295Srandyf while ((mp = async_get_suspq(asy)) != NULL) {
4535295Srandyf queue_t *q;
4545295Srandyf
4555295Srandyf q = async->async_ttycommon.t_writeq;
4565295Srandyf ASSERT(q != NULL);
4575295Srandyf mutex_exit(&asy->asy_excl);
4585295Srandyf (void) asywputdo(q, mp, B_FALSE);
4595295Srandyf mutex_enter(&asy->asy_excl);
4605295Srandyf }
4615295Srandyf async->async_flags &= ~ASYNC_DDI_SUSPENDED;
4625295Srandyf cv_broadcast(&async->async_flags_cv);
4635295Srandyf }
4645295Srandyf
4650Sstevel@tonic-gate static int
asy_get_bus_type(dev_info_t * devinfo)4663359Smyers asy_get_bus_type(dev_info_t *devinfo)
4673359Smyers {
4683359Smyers char parent_type[16];
4693359Smyers int parentlen;
4703359Smyers
4713359Smyers parentlen = sizeof (parent_type);
4723359Smyers
4733359Smyers if (ddi_prop_op(DDI_DEV_T_ANY, devinfo, PROP_LEN_AND_VAL_BUF, 0,
4743359Smyers "device_type", (caddr_t)parent_type, &parentlen)
4753359Smyers != DDI_PROP_SUCCESS && ddi_prop_op(DDI_DEV_T_ANY, devinfo,
4763359Smyers PROP_LEN_AND_VAL_BUF, 0, "bus-type", (caddr_t)parent_type,
4773359Smyers &parentlen) != DDI_PROP_SUCCESS) {
4783359Smyers cmn_err(CE_WARN,
4793359Smyers "asy: can't figure out device type for"
4803359Smyers " parent \"%s\"",
4813359Smyers ddi_get_name(ddi_get_parent(devinfo)));
4823359Smyers return (ASY_BUS_UNKNOWN);
4833359Smyers }
4843359Smyers if (strcmp(parent_type, "isa") == 0)
4853359Smyers return (ASY_BUS_ISA);
4863359Smyers else if (strcmp(parent_type, "pci") == 0)
4873359Smyers return (ASY_BUS_PCI);
4883359Smyers else
4893359Smyers return (ASY_BUS_UNKNOWN);
4903359Smyers }
4913359Smyers
4923359Smyers static int
asy_get_io_regnum_pci(dev_info_t * devi,struct asycom * asy)4933359Smyers asy_get_io_regnum_pci(dev_info_t *devi, struct asycom *asy)
4943359Smyers {
4953359Smyers int reglen, nregs;
4963359Smyers int regnum, i;
4973359Smyers uint64_t size;
4983359Smyers struct pci_phys_spec *reglist;
4993359Smyers
5003359Smyers if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
5013359Smyers "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) {
5023359Smyers cmn_err(CE_WARN, "asy_get_io_regnum_pci: reg property"
5033359Smyers " not found in devices property list");
5043359Smyers return (-1);
5053359Smyers }
5063359Smyers
5073359Smyers /*
5083359Smyers * PCI devices are assumed to not have broken FIFOs;
5093359Smyers * Agere/Lucent Venus PCI modem chipsets are an example
5103359Smyers */
5113359Smyers if (asy)
5123359Smyers asy->asy_flags2 |= ASY2_NO_LOOPBACK;
5133359Smyers
5143359Smyers regnum = -1;
5153359Smyers nregs = reglen / sizeof (*reglist);
5163359Smyers for (i = 0; i < nregs; i++) {
5173359Smyers switch (reglist[i].pci_phys_hi & PCI_ADDR_MASK) {
5183359Smyers case PCI_ADDR_IO: /* I/O bus reg property */
5193359Smyers if (regnum == -1) /* use only the first one */
5203359Smyers regnum = i;
5213359Smyers break;
5223359Smyers
5233359Smyers default:
5243359Smyers break;
5253359Smyers }
5263359Smyers }
5273359Smyers
5283359Smyers /* check for valid count of registers */
5293359Smyers if (regnum >= 0) {
5303359Smyers size = ((uint64_t)reglist[regnum].pci_size_low) |
5313359Smyers ((uint64_t)reglist[regnum].pci_size_hi) << 32;
5323359Smyers if (size < 8)
5333359Smyers regnum = -1;
5343359Smyers }
5353359Smyers kmem_free(reglist, reglen);
5363359Smyers return (regnum);
5373359Smyers }
5383359Smyers
5393359Smyers static int
asy_get_io_regnum_isa(dev_info_t * devi,struct asycom * asy)5403359Smyers asy_get_io_regnum_isa(dev_info_t *devi, struct asycom *asy)
5413359Smyers {
5423359Smyers int reglen, nregs;
5433359Smyers int regnum, i;
5443359Smyers struct {
5453359Smyers uint_t bustype;
5463359Smyers int base;
5473359Smyers int size;
5483359Smyers } *reglist;
5493359Smyers
5503359Smyers if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS,
5513359Smyers "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) {
5523359Smyers cmn_err(CE_WARN, "asy_get_io_regnum: reg property not found "
5535295Srandyf "in devices property list");
5543359Smyers return (-1);
5553359Smyers }
5563359Smyers
5573359Smyers regnum = -1;
5583359Smyers nregs = reglen / sizeof (*reglist);
5593359Smyers for (i = 0; i < nregs; i++) {
5603359Smyers switch (reglist[i].bustype) {
5613359Smyers case 1: /* I/O bus reg property */
5623359Smyers if (regnum == -1) /* only use the first one */
5633359Smyers regnum = i;
5643359Smyers break;
5653359Smyers
5663359Smyers case pnpMTS0219: /* Multitech MT5634ZTX modem */
5673359Smyers /* Venus chipset can't do loopback test */
5683359Smyers if (asy)
5693359Smyers asy->asy_flags2 |= ASY2_NO_LOOPBACK;
5703359Smyers break;
5713359Smyers
5723359Smyers default:
5733359Smyers break;
5743359Smyers }
5753359Smyers }
5763359Smyers
5773359Smyers /* check for valid count of registers */
5783359Smyers if ((regnum < 0) || (reglist[regnum].size < 8))
5793359Smyers regnum = -1;
5803359Smyers kmem_free(reglist, reglen);
5813359Smyers return (regnum);
5823359Smyers }
5833359Smyers
5843359Smyers static int
asy_get_io_regnum(dev_info_t * devinfo,struct asycom * asy)5853359Smyers asy_get_io_regnum(dev_info_t *devinfo, struct asycom *asy)
5863359Smyers {
5873359Smyers switch (asy_get_bus_type(devinfo)) {
5883359Smyers case ASY_BUS_ISA:
5893359Smyers return (asy_get_io_regnum_isa(devinfo, asy));
5903359Smyers case ASY_BUS_PCI:
5913359Smyers return (asy_get_io_regnum_pci(devinfo, asy));
5923359Smyers default:
5933359Smyers return (-1);
5943359Smyers }
5953359Smyers }
5963359Smyers
5973359Smyers static int
asydetach(dev_info_t * devi,ddi_detach_cmd_t cmd)5980Sstevel@tonic-gate asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd)
5990Sstevel@tonic-gate {
6000Sstevel@tonic-gate int instance;
6010Sstevel@tonic-gate struct asycom *asy;
6020Sstevel@tonic-gate struct asyncline *async;
6030Sstevel@tonic-gate
6040Sstevel@tonic-gate instance = ddi_get_instance(devi); /* find out which unit */
6050Sstevel@tonic-gate
6060Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance);
6070Sstevel@tonic-gate if (asy == NULL)
6080Sstevel@tonic-gate return (DDI_FAILURE);
6090Sstevel@tonic-gate async = asy->asy_priv;
6100Sstevel@tonic-gate
6115295Srandyf switch (cmd) {
6125295Srandyf case DDI_DETACH:
6135295Srandyf DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.",
6145295Srandyf instance, asy_hw_name(asy));
6155295Srandyf
6165295Srandyf /* cancel DTR hold timeout */
6175295Srandyf if (async->async_dtrtid != 0) {
6185295Srandyf (void) untimeout(async->async_dtrtid);
6195295Srandyf async->async_dtrtid = 0;
6205295Srandyf }
6215295Srandyf
6225295Srandyf /* remove all minor device node(s) for this device */
6235295Srandyf ddi_remove_minor_node(devi, NULL);
6245295Srandyf
6255295Srandyf mutex_destroy(&asy->asy_excl);
6265295Srandyf mutex_destroy(&asy->asy_excl_hi);
6275295Srandyf cv_destroy(&async->async_flags_cv);
6285295Srandyf ddi_remove_intr(devi, 0, asy->asy_iblock);
6295295Srandyf ddi_regs_map_free(&asy->asy_iohandle);
63012401SZachary.Kissel@Sun.COM ddi_remove_softintr(asy->asy_softintr_id);
63112401SZachary.Kissel@Sun.COM mutex_destroy(&asy->asy_soft_lock);
6325295Srandyf asy_soft_state_free(asy);
6335295Srandyf DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete",
6345295Srandyf instance);
6355295Srandyf break;
6365295Srandyf case DDI_SUSPEND:
6375295Srandyf {
6385295Srandyf unsigned i;
6395295Srandyf uchar_t lsr;
6405295Srandyf
6415295Srandyf #ifdef DEBUG
6425295Srandyf if (asy_nosuspend)
6435295Srandyf return (DDI_SUCCESS);
6445295Srandyf #endif
6455295Srandyf mutex_enter(&asy->asy_excl);
6465295Srandyf
6475295Srandyf ASSERT(async->async_ops >= 0);
6485295Srandyf while (async->async_ops > 0)
6495295Srandyf cv_wait(&async->async_ops_cv, &asy->asy_excl);
6505295Srandyf
6515295Srandyf async->async_flags |= ASYNC_DDI_SUSPENDED;
6525295Srandyf
6535295Srandyf /* Wait for timed break and delay to complete */
6545295Srandyf while ((async->async_flags & (ASYNC_BREAK|ASYNC_DELAY))) {
6555295Srandyf if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl)
6565295Srandyf == 0) {
6575295Srandyf async_process_suspq(asy);
6585295Srandyf mutex_exit(&asy->asy_excl);
6595295Srandyf return (DDI_FAILURE);
6605295Srandyf }
6615295Srandyf }
6625295Srandyf
6635295Srandyf /* Clear untimed break */
6645295Srandyf if (async->async_flags & ASYNC_OUT_SUSPEND)
6655295Srandyf async_resume_utbrk(async);
6665295Srandyf
6675295Srandyf mutex_exit(&asy->asy_excl);
6685295Srandyf
6695295Srandyf mutex_enter(&asy->asy_soft_sr);
6705295Srandyf mutex_enter(&asy->asy_excl);
6715295Srandyf if (async->async_wbufcid != 0) {
6725295Srandyf bufcall_id_t bcid = async->async_wbufcid;
6735295Srandyf async->async_wbufcid = 0;
6745295Srandyf async->async_flags |= ASYNC_RESUME_BUFCALL;
6755295Srandyf mutex_exit(&asy->asy_excl);
6765295Srandyf unbufcall(bcid);
6775295Srandyf mutex_enter(&asy->asy_excl);
6785295Srandyf }
6795295Srandyf mutex_enter(&asy->asy_excl_hi);
6805295Srandyf
6815295Srandyf /* Disable interrupts from chip */
6825295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
6835295Srandyf asy->asy_flags |= ASY_DDI_SUSPENDED;
6845295Srandyf
685*12876SZachary.Kissel@Sun.COM /*
686*12876SZachary.Kissel@Sun.COM * Hardware interrupts are disabled we can drop our high level
687*12876SZachary.Kissel@Sun.COM * lock and proceed.
688*12876SZachary.Kissel@Sun.COM */
689*12876SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_excl_hi);
690*12876SZachary.Kissel@Sun.COM
6915295Srandyf /* Process remaining RX characters and RX errors, if any */
6925295Srandyf lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
6935295Srandyf async_rxint(asy, lsr);
6945295Srandyf
6955295Srandyf /* Wait for TX to drain */
6965295Srandyf for (i = 1000; i > 0; i--) {
6975295Srandyf lsr = ddi_get8(asy->asy_iohandle,
6985295Srandyf asy->asy_ioaddr + LSR);
6995295Srandyf if ((lsr & (XSRE | XHRE)) == (XSRE | XHRE))
7005295Srandyf break;
7015295Srandyf delay(drv_usectohz(10000));
7025295Srandyf }
7035295Srandyf if (i == 0)
7045295Srandyf cmn_err(CE_WARN,
7055295Srandyf "asy: transmitter wasn't drained before "
7065295Srandyf "driver was suspended");
7075295Srandyf
7085295Srandyf mutex_exit(&asy->asy_excl);
7095295Srandyf mutex_exit(&asy->asy_soft_sr);
7105295Srandyf break;
7110Sstevel@tonic-gate }
7125295Srandyf default:
7135295Srandyf return (DDI_FAILURE);
7145295Srandyf }
7155295Srandyf
7160Sstevel@tonic-gate return (DDI_SUCCESS);
7170Sstevel@tonic-gate }
7180Sstevel@tonic-gate
7190Sstevel@tonic-gate /*
7200Sstevel@tonic-gate * asyprobe
7210Sstevel@tonic-gate * We don't bother probing for the hardware, as since Solaris 2.6, device
7220Sstevel@tonic-gate * nodes are only created for auto-detected hardware or nodes explicitly
7230Sstevel@tonic-gate * created by the user, e.g. via the DCA. However, we should check the
7240Sstevel@tonic-gate * device node is at least vaguely usable, i.e. we have a block of 8 i/o
7250Sstevel@tonic-gate * ports. This prevents attempting to attach to bogus serial ports which
7260Sstevel@tonic-gate * some BIOSs still partially report when they are disabled in the BIOS.
7270Sstevel@tonic-gate */
7280Sstevel@tonic-gate static int
asyprobe(dev_info_t * devi)7290Sstevel@tonic-gate asyprobe(dev_info_t *devi)
7300Sstevel@tonic-gate {
7313359Smyers return ((asy_get_io_regnum(devi, NULL) < 0) ?
7323359Smyers DDI_PROBE_FAILURE : DDI_PROBE_DONTCARE);
7330Sstevel@tonic-gate }
7340Sstevel@tonic-gate
7350Sstevel@tonic-gate static int
asyattach(dev_info_t * devi,ddi_attach_cmd_t cmd)7360Sstevel@tonic-gate asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd)
7370Sstevel@tonic-gate {
7380Sstevel@tonic-gate int instance;
7390Sstevel@tonic-gate int mcr;
7400Sstevel@tonic-gate int ret;
7410Sstevel@tonic-gate int regnum = 0;
7420Sstevel@tonic-gate int i;
7430Sstevel@tonic-gate struct asycom *asy;
7443359Smyers char name[ASY_MINOR_LEN];
7450Sstevel@tonic-gate int status;
7460Sstevel@tonic-gate static ddi_device_acc_attr_t ioattr = {
7470Sstevel@tonic-gate DDI_DEVICE_ATTR_V0,
7480Sstevel@tonic-gate DDI_NEVERSWAP_ACC,
7490Sstevel@tonic-gate DDI_STRICTORDER_ACC,
7500Sstevel@tonic-gate };
7510Sstevel@tonic-gate
7525295Srandyf instance = ddi_get_instance(devi); /* find out which unit */
7535295Srandyf
7545295Srandyf switch (cmd) {
7555295Srandyf case DDI_ATTACH:
7565295Srandyf break;
7575295Srandyf case DDI_RESUME:
7585295Srandyf {
7595295Srandyf struct asyncline *async;
7605295Srandyf
7615295Srandyf #ifdef DEBUG
7625295Srandyf if (asy_nosuspend)
7635295Srandyf return (DDI_SUCCESS);
7645295Srandyf #endif
7655295Srandyf asy = ddi_get_soft_state(asy_soft_state, instance);
7665295Srandyf if (asy == NULL)
7675295Srandyf return (DDI_FAILURE);
7685295Srandyf
7695295Srandyf mutex_enter(&asy->asy_soft_sr);
7705295Srandyf mutex_enter(&asy->asy_excl);
7715295Srandyf mutex_enter(&asy->asy_excl_hi);
7725295Srandyf
7735295Srandyf async = asy->asy_priv;
7745295Srandyf /* Disable interrupts */
7755295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
7765295Srandyf if (asy_identify_chip(devi, asy) != DDI_SUCCESS) {
7775295Srandyf mutex_exit(&asy->asy_excl_hi);
7785295Srandyf mutex_exit(&asy->asy_excl);
7795295Srandyf mutex_exit(&asy->asy_soft_sr);
78010811SKevin.Crowe@Sun.COM cmn_err(CE_WARN, "!Cannot identify UART chip at %p\n",
7815295Srandyf (void *)asy->asy_ioaddr);
7825295Srandyf return (DDI_FAILURE);
7835295Srandyf }
7845295Srandyf asy->asy_flags &= ~ASY_DDI_SUSPENDED;
7855295Srandyf if (async->async_flags & ASYNC_ISOPEN) {
7865295Srandyf asy_program(asy, ASY_INIT);
7875295Srandyf /* Kick off output */
7885295Srandyf if (async->async_ocnt > 0) {
7895295Srandyf async_resume(async);
7905295Srandyf } else {
7915295Srandyf mutex_exit(&asy->asy_excl_hi);
7925295Srandyf if (async->async_xmitblk)
7935295Srandyf freeb(async->async_xmitblk);
7945295Srandyf async->async_xmitblk = NULL;
7955295Srandyf async_start(async);
7965295Srandyf mutex_enter(&asy->asy_excl_hi);
7975295Srandyf }
7985295Srandyf ASYSETSOFT(asy);
7995295Srandyf }
8005295Srandyf mutex_exit(&asy->asy_excl_hi);
8015295Srandyf mutex_exit(&asy->asy_excl);
8025295Srandyf mutex_exit(&asy->asy_soft_sr);
8035295Srandyf
8045295Srandyf mutex_enter(&asy->asy_excl);
8055295Srandyf if (async->async_flags & ASYNC_RESUME_BUFCALL) {
8065295Srandyf async->async_wbufcid = bufcall(async->async_wbufcds,
8075295Srandyf BPRI_HI, (void (*)(void *)) async_reioctl,
8085295Srandyf (void *)(intptr_t)async->async_common->asy_unit);
8095295Srandyf async->async_flags &= ~ASYNC_RESUME_BUFCALL;
8105295Srandyf }
8115295Srandyf async_process_suspq(asy);
8125295Srandyf mutex_exit(&asy->asy_excl);
8135295Srandyf return (DDI_SUCCESS);
8145295Srandyf }
8155295Srandyf default:
8160Sstevel@tonic-gate return (DDI_FAILURE);
8175295Srandyf }
8185295Srandyf
8190Sstevel@tonic-gate ret = ddi_soft_state_zalloc(asy_soft_state, instance);
8200Sstevel@tonic-gate if (ret != DDI_SUCCESS)
8210Sstevel@tonic-gate return (DDI_FAILURE);
8220Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance);
8230Sstevel@tonic-gate ASSERT(asy != NULL); /* can't fail - we only just allocated it */
8240Sstevel@tonic-gate asy->asy_unit = instance;
8250Sstevel@tonic-gate mutex_enter(&asy_glob_lock);
8260Sstevel@tonic-gate if (instance > max_asy_instance)
8270Sstevel@tonic-gate max_asy_instance = instance;
8280Sstevel@tonic-gate mutex_exit(&asy_glob_lock);
8290Sstevel@tonic-gate
8303359Smyers regnum = asy_get_io_regnum(devi, asy);
8310Sstevel@tonic-gate
8320Sstevel@tonic-gate if (regnum < 0 ||
8330Sstevel@tonic-gate ddi_regs_map_setup(devi, regnum, (caddr_t *)&asy->asy_ioaddr,
8340Sstevel@tonic-gate (offset_t)0, (offset_t)0, &ioattr, &asy->asy_iohandle)
8350Sstevel@tonic-gate != DDI_SUCCESS) {
8360Sstevel@tonic-gate cmn_err(CE_WARN, "asy%d: could not map UART registers @ %p",
8370Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr);
8380Sstevel@tonic-gate
8390Sstevel@tonic-gate asy_soft_state_free(asy);
8400Sstevel@tonic-gate return (DDI_FAILURE);
8410Sstevel@tonic-gate }
8420Sstevel@tonic-gate
8430Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INIT, "asy%dattach: UART @ %p\n",
8440Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr);
8450Sstevel@tonic-gate
8460Sstevel@tonic-gate mutex_enter(&asy_glob_lock);
8470Sstevel@tonic-gate if (com_ports == NULL) { /* need to initialize com_ports */
8480Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi, 0,
8490Sstevel@tonic-gate "motherboard-serial-ports", &com_ports, &num_com_ports) !=
8500Sstevel@tonic-gate DDI_PROP_SUCCESS) {
8510Sstevel@tonic-gate /* Use our built-in COM[1234] values */
8520Sstevel@tonic-gate com_ports = (int *)standard_com_ports;
8530Sstevel@tonic-gate num_com_ports = sizeof (standard_com_ports) /
8540Sstevel@tonic-gate sizeof (standard_com_ports[0]);
8550Sstevel@tonic-gate }
8560Sstevel@tonic-gate if (num_com_ports > 10) {
8570Sstevel@tonic-gate /* We run out of single digits for device properties */
8580Sstevel@tonic-gate num_com_ports = 10;
8590Sstevel@tonic-gate cmn_err(CE_WARN,
8600Sstevel@tonic-gate "More than %d motherboard-serial-ports",
8610Sstevel@tonic-gate num_com_ports);
8620Sstevel@tonic-gate }
8630Sstevel@tonic-gate }
8640Sstevel@tonic-gate mutex_exit(&asy_glob_lock);
8650Sstevel@tonic-gate
8660Sstevel@tonic-gate /*
8670Sstevel@tonic-gate * Lookup the i/o address to see if this is a standard COM port
8680Sstevel@tonic-gate * in which case we assign it the correct tty[a-d] to match the
8690Sstevel@tonic-gate * COM port number, or some other i/o address in which case it
8700Sstevel@tonic-gate * will be assigned /dev/term/[0123...] in some rather arbitrary
8710Sstevel@tonic-gate * fashion.
8720Sstevel@tonic-gate */
8730Sstevel@tonic-gate
8740Sstevel@tonic-gate for (i = 0; i < num_com_ports; i++) {
8750Sstevel@tonic-gate if (asy->asy_ioaddr == (uint8_t *)(uintptr_t)com_ports[i]) {
8760Sstevel@tonic-gate asy->asy_com_port = i + 1;
8770Sstevel@tonic-gate break;
8780Sstevel@tonic-gate }
8790Sstevel@tonic-gate }
8800Sstevel@tonic-gate
8810Sstevel@tonic-gate /*
8820Sstevel@tonic-gate * It appears that there was async hardware that on reset
8830Sstevel@tonic-gate * did not clear ICR. Hence when we get to
8840Sstevel@tonic-gate * ddi_get_iblock_cookie below, this hardware would cause
8850Sstevel@tonic-gate * the system to hang if there was input available.
8860Sstevel@tonic-gate */
8870Sstevel@tonic-gate
8881106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0x00);
8890Sstevel@tonic-gate
8900Sstevel@tonic-gate /* establish default usage */
8910Sstevel@tonic-gate asy->asy_mcr |= RTS|DTR; /* do use RTS/DTR after open */
8920Sstevel@tonic-gate asy->asy_lcr = STOP1|BITS8; /* default to 1 stop 8 bits */
8930Sstevel@tonic-gate asy->asy_bidx = B9600; /* default to 9600 */
8940Sstevel@tonic-gate #ifdef DEBUG
8950Sstevel@tonic-gate asy->asy_msint_cnt = 0; /* # of times in async_msint */
8960Sstevel@tonic-gate #endif
8970Sstevel@tonic-gate mcr = 0; /* don't enable until open */
8980Sstevel@tonic-gate
8990Sstevel@tonic-gate if (asy->asy_com_port != 0) {
9000Sstevel@tonic-gate /*
9010Sstevel@tonic-gate * For motherboard ports, emulate tty eeprom properties.
9020Sstevel@tonic-gate * Actually, we can't tell if a port is motherboard or not,
9030Sstevel@tonic-gate * so for "motherboard ports", read standard DOS COM ports.
9040Sstevel@tonic-gate */
9050Sstevel@tonic-gate switch (asy_getproperty(devi, asy, "ignore-cd")) {
9060Sstevel@tonic-gate case 0: /* *-ignore-cd=False */
9070Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM,
9080Sstevel@tonic-gate "asy%dattach: clear ASY_IGNORE_CD\n", instance);
9090Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */
9100Sstevel@tonic-gate break;
9110Sstevel@tonic-gate case 1: /* *-ignore-cd=True */
9120Sstevel@tonic-gate /*FALLTHRU*/
9130Sstevel@tonic-gate default: /* *-ignore-cd not defined */
9140Sstevel@tonic-gate /*
9150Sstevel@tonic-gate * We set rather silly defaults of soft carrier on
9160Sstevel@tonic-gate * and DTR/RTS raised here because it might be that
9170Sstevel@tonic-gate * one of the motherboard ports is the system console.
9180Sstevel@tonic-gate */
9190Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM,
9200Sstevel@tonic-gate "asy%dattach: set ASY_IGNORE_CD, set RTS & DTR\n",
9210Sstevel@tonic-gate instance);
9220Sstevel@tonic-gate mcr = asy->asy_mcr; /* rts/dtr on */
9230Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */
9240Sstevel@tonic-gate break;
9250Sstevel@tonic-gate }
9260Sstevel@tonic-gate
9270Sstevel@tonic-gate /* Property for not raising DTR/RTS */
9280Sstevel@tonic-gate switch (asy_getproperty(devi, asy, "rts-dtr-off")) {
9290Sstevel@tonic-gate case 0: /* *-rts-dtr-off=False */
9300Sstevel@tonic-gate asy->asy_flags |= ASY_RTS_DTR_OFF; /* OFF */
9310Sstevel@tonic-gate mcr = asy->asy_mcr; /* rts/dtr on */
9320Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dattach: "
9330Sstevel@tonic-gate "ASY_RTS_DTR_OFF set and DTR & RTS set\n",
9340Sstevel@tonic-gate instance);
9350Sstevel@tonic-gate break;
9360Sstevel@tonic-gate case 1: /* *-rts-dtr-off=True */
9370Sstevel@tonic-gate /*FALLTHRU*/
9380Sstevel@tonic-gate default: /* *-rts-dtr-off undefined */
9390Sstevel@tonic-gate break;
9400Sstevel@tonic-gate }
9410Sstevel@tonic-gate
9420Sstevel@tonic-gate /* Parse property for tty modes */
9430Sstevel@tonic-gate asy_parse_mode(devi, asy);
9440Sstevel@tonic-gate } else {
9450Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM,
9460Sstevel@tonic-gate "asy%dattach: clear ASY_IGNORE_CD, clear RTS & DTR\n",
9470Sstevel@tonic-gate instance);
9480Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */
9490Sstevel@tonic-gate }
9500Sstevel@tonic-gate
9510Sstevel@tonic-gate /*
9520Sstevel@tonic-gate * Initialize the port with default settings.
9530Sstevel@tonic-gate */
9540Sstevel@tonic-gate
9550Sstevel@tonic-gate asy->asy_fifo_buf = 1;
9560Sstevel@tonic-gate asy->asy_use_fifo = FIFO_OFF;
9570Sstevel@tonic-gate
9580Sstevel@tonic-gate /*
9590Sstevel@tonic-gate * Get icookie for mutexes initialization
9600Sstevel@tonic-gate */
9610Sstevel@tonic-gate if ((ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) !=
9620Sstevel@tonic-gate DDI_SUCCESS) ||
9630Sstevel@tonic-gate (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_MED,
96412401SZachary.Kissel@Sun.COM &asy->asy_soft_iblock) != DDI_SUCCESS)) {
9650Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle);
9660Sstevel@tonic-gate cmn_err(CE_CONT,
9670Sstevel@tonic-gate "asy%d: could not hook interrupt for UART @ %p\n",
9680Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr);
9690Sstevel@tonic-gate asy_soft_state_free(asy);
9700Sstevel@tonic-gate return (DDI_FAILURE);
9710Sstevel@tonic-gate }
9720Sstevel@tonic-gate
9730Sstevel@tonic-gate /*
9740Sstevel@tonic-gate * Initialize mutexes before accessing the hardware
9750Sstevel@tonic-gate */
97612401SZachary.Kissel@Sun.COM mutex_init(&asy->asy_soft_lock, NULL, MUTEX_DRIVER,
97712401SZachary.Kissel@Sun.COM (void *)asy->asy_soft_iblock);
97812401SZachary.Kissel@Sun.COM mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, NULL);
9790Sstevel@tonic-gate mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER,
9805295Srandyf (void *)asy->asy_iblock);
98112401SZachary.Kissel@Sun.COM mutex_init(&asy->asy_soft_sr, NULL, MUTEX_DRIVER,
98212401SZachary.Kissel@Sun.COM (void *)asy->asy_soft_iblock);
9830Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
9840Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
9850Sstevel@tonic-gate
9860Sstevel@tonic-gate if (asy_identify_chip(devi, asy) != DDI_SUCCESS) {
9870Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
9880Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
98912401SZachary.Kissel@Sun.COM mutex_destroy(&asy->asy_soft_lock);
9900Sstevel@tonic-gate mutex_destroy(&asy->asy_excl);
9910Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi);
9925295Srandyf mutex_destroy(&asy->asy_soft_sr);
9930Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle);
99410811SKevin.Crowe@Sun.COM cmn_err(CE_CONT, "!Cannot identify UART chip at %p\n",
9950Sstevel@tonic-gate (void *)asy->asy_ioaddr);
9960Sstevel@tonic-gate asy_soft_state_free(asy);
9970Sstevel@tonic-gate return (DDI_FAILURE);
9980Sstevel@tonic-gate }
9990Sstevel@tonic-gate
10000Sstevel@tonic-gate /* disable all interrupts */
10011106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
10020Sstevel@tonic-gate /* select baud rate generator */
10031106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB);
10040Sstevel@tonic-gate /* Set the baud rate to 9600 */
10051106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLL),
10065295Srandyf asyspdtab[asy->asy_bidx] & 0xff);
10071106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + (DAT+DLH),
10085295Srandyf (asyspdtab[asy->asy_bidx] >> 8) & 0xff);
10095295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, asy->asy_lcr);
10101106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr);
10110Sstevel@tonic-gate
10120Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
10130Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
10140Sstevel@tonic-gate
10150Sstevel@tonic-gate /*
10160Sstevel@tonic-gate * Set up the other components of the asycom structure for this port.
10170Sstevel@tonic-gate */
10180Sstevel@tonic-gate asy->asy_dip = devi;
10190Sstevel@tonic-gate
102012401SZachary.Kissel@Sun.COM /*
102112401SZachary.Kissel@Sun.COM * Install per instance software interrupt handler.
102212401SZachary.Kissel@Sun.COM */
102312401SZachary.Kissel@Sun.COM if (ddi_add_softintr(devi, DDI_SOFTINT_MED,
102412401SZachary.Kissel@Sun.COM &(asy->asy_softintr_id), NULL, 0, asysoftintr,
102512401SZachary.Kissel@Sun.COM (caddr_t)asy) != DDI_SUCCESS) {
102612401SZachary.Kissel@Sun.COM mutex_destroy(&asy->asy_soft_lock);
102712401SZachary.Kissel@Sun.COM mutex_destroy(&asy->asy_excl);
102812401SZachary.Kissel@Sun.COM mutex_destroy(&asy->asy_excl_hi);
102912401SZachary.Kissel@Sun.COM ddi_regs_map_free(&asy->asy_iohandle);
103012401SZachary.Kissel@Sun.COM cmn_err(CE_CONT,
103112401SZachary.Kissel@Sun.COM "Can not set soft interrupt for ASY driver\n");
103212401SZachary.Kissel@Sun.COM asy_soft_state_free(asy);
103312401SZachary.Kissel@Sun.COM return (DDI_FAILURE);
10340Sstevel@tonic-gate }
10350Sstevel@tonic-gate
10360Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
10370Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
10380Sstevel@tonic-gate
10390Sstevel@tonic-gate /*
10400Sstevel@tonic-gate * Install interrupt handler for this device.
10410Sstevel@tonic-gate */
10420Sstevel@tonic-gate if (ddi_add_intr(devi, 0, NULL, 0, asyintr,
10430Sstevel@tonic-gate (caddr_t)asy) != DDI_SUCCESS) {
10440Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
10450Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
104612401SZachary.Kissel@Sun.COM ddi_remove_softintr(asy->asy_softintr_id);
104712401SZachary.Kissel@Sun.COM mutex_destroy(&asy->asy_soft_lock);
10480Sstevel@tonic-gate mutex_destroy(&asy->asy_excl);
10490Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi);
10500Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle);
10510Sstevel@tonic-gate cmn_err(CE_CONT,
10525295Srandyf "Can not set device interrupt for ASY driver\n");
10530Sstevel@tonic-gate asy_soft_state_free(asy);
10540Sstevel@tonic-gate return (DDI_FAILURE);
10550Sstevel@tonic-gate }
10560Sstevel@tonic-gate
10570Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
10580Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
10590Sstevel@tonic-gate
10600Sstevel@tonic-gate asyinit(asy); /* initialize the asyncline structure */
10610Sstevel@tonic-gate
10620Sstevel@tonic-gate /* create minor device nodes for this device */
10630Sstevel@tonic-gate if (asy->asy_com_port != 0) {
10640Sstevel@tonic-gate /*
10650Sstevel@tonic-gate * For DOS COM ports, add letter suffix so
10660Sstevel@tonic-gate * devfsadm can create correct link names.
10670Sstevel@tonic-gate */
10680Sstevel@tonic-gate name[0] = asy->asy_com_port + 'a' - 1;
10690Sstevel@tonic-gate name[1] = '\0';
10700Sstevel@tonic-gate } else {
10710Sstevel@tonic-gate /*
10723359Smyers * asy port which isn't a standard DOS COM
10733359Smyers * port gets a numeric name based on instance
10740Sstevel@tonic-gate */
10753359Smyers (void) snprintf(name, ASY_MINOR_LEN, "%d", instance);
10760Sstevel@tonic-gate }
10770Sstevel@tonic-gate status = ddi_create_minor_node(devi, name, S_IFCHR, instance,
10780Sstevel@tonic-gate asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB : DDI_NT_SERIAL, NULL);
10790Sstevel@tonic-gate if (status == DDI_SUCCESS) {
10800Sstevel@tonic-gate (void) strcat(name, ",cu");
10810Sstevel@tonic-gate status = ddi_create_minor_node(devi, name, S_IFCHR,
10820Sstevel@tonic-gate OUTLINE | instance,
10830Sstevel@tonic-gate asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB_DO :
10840Sstevel@tonic-gate DDI_NT_SERIAL_DO, NULL);
10850Sstevel@tonic-gate }
10860Sstevel@tonic-gate
10870Sstevel@tonic-gate if (status != DDI_SUCCESS) {
10880Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
10890Sstevel@tonic-gate
10900Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL);
10910Sstevel@tonic-gate ddi_remove_intr(devi, 0, asy->asy_iblock);
109212401SZachary.Kissel@Sun.COM ddi_remove_softintr(asy->asy_softintr_id);
109312401SZachary.Kissel@Sun.COM mutex_destroy(&asy->asy_soft_lock);
10940Sstevel@tonic-gate mutex_destroy(&asy->asy_excl);
10950Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi);
10960Sstevel@tonic-gate cv_destroy(&async->async_flags_cv);
10970Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle);
10980Sstevel@tonic-gate asy_soft_state_free(asy);
10990Sstevel@tonic-gate return (DDI_FAILURE);
11000Sstevel@tonic-gate }
11010Sstevel@tonic-gate
11020Sstevel@tonic-gate /*
11030Sstevel@tonic-gate * Fill in the polled I/O structure.
11040Sstevel@tonic-gate */
11050Sstevel@tonic-gate asy->polledio.cons_polledio_version = CONSPOLLEDIO_V0;
11061762Slt200341 asy->polledio.cons_polledio_argument = (cons_polledio_arg_t)asy;
11070Sstevel@tonic-gate asy->polledio.cons_polledio_putchar = asyputchar;
11080Sstevel@tonic-gate asy->polledio.cons_polledio_getchar = asygetchar;
11090Sstevel@tonic-gate asy->polledio.cons_polledio_ischar = asyischar;
11100Sstevel@tonic-gate asy->polledio.cons_polledio_enter = NULL;
11110Sstevel@tonic-gate asy->polledio.cons_polledio_exit = NULL;
11120Sstevel@tonic-gate
11130Sstevel@tonic-gate ddi_report_dev(devi);
11140Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "asy%dattach: done\n", instance);
11150Sstevel@tonic-gate return (DDI_SUCCESS);
11160Sstevel@tonic-gate }
11170Sstevel@tonic-gate
11180Sstevel@tonic-gate /*ARGSUSED*/
11190Sstevel@tonic-gate static int
asyinfo(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)11200Sstevel@tonic-gate asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
11210Sstevel@tonic-gate void **result)
11220Sstevel@tonic-gate {
11230Sstevel@tonic-gate dev_t dev = (dev_t)arg;
11240Sstevel@tonic-gate int instance, error;
11250Sstevel@tonic-gate struct asycom *asy;
11260Sstevel@tonic-gate
11270Sstevel@tonic-gate instance = UNIT(dev);
11280Sstevel@tonic-gate
11290Sstevel@tonic-gate switch (infocmd) {
11300Sstevel@tonic-gate case DDI_INFO_DEVT2DEVINFO:
1131386Scth asy = ddi_get_soft_state(asy_soft_state, instance);
1132386Scth if ((asy == NULL) || (asy->asy_dip == NULL))
11330Sstevel@tonic-gate error = DDI_FAILURE;
11340Sstevel@tonic-gate else {
11350Sstevel@tonic-gate *result = (void *) asy->asy_dip;
11360Sstevel@tonic-gate error = DDI_SUCCESS;
11370Sstevel@tonic-gate }
11380Sstevel@tonic-gate break;
11390Sstevel@tonic-gate case DDI_INFO_DEVT2INSTANCE:
11400Sstevel@tonic-gate *result = (void *)(intptr_t)instance;
11410Sstevel@tonic-gate error = DDI_SUCCESS;
11420Sstevel@tonic-gate break;
11430Sstevel@tonic-gate default:
11440Sstevel@tonic-gate error = DDI_FAILURE;
11450Sstevel@tonic-gate }
11460Sstevel@tonic-gate return (error);
11470Sstevel@tonic-gate }
11480Sstevel@tonic-gate
11490Sstevel@tonic-gate /* asy_getproperty -- walk through all name variants until we find a match */
11500Sstevel@tonic-gate
11510Sstevel@tonic-gate static int
asy_getproperty(dev_info_t * devi,struct asycom * asy,const char * property)11520Sstevel@tonic-gate asy_getproperty(dev_info_t *devi, struct asycom *asy, const char *property)
11530Sstevel@tonic-gate {
11540Sstevel@tonic-gate int len;
11550Sstevel@tonic-gate int ret;
11560Sstevel@tonic-gate char letter = asy->asy_com_port + 'a' - 1; /* for ttya */
11570Sstevel@tonic-gate char number = asy->asy_com_port + '0'; /* for COM1 */
11580Sstevel@tonic-gate char val[40];
11590Sstevel@tonic-gate char name[40];
11600Sstevel@tonic-gate
11610Sstevel@tonic-gate /* Property for ignoring DCD */
11620Sstevel@tonic-gate (void) sprintf(name, "tty%c-%s", letter, property);
11630Sstevel@tonic-gate len = sizeof (val);
11640Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
11650Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) {
11660Sstevel@tonic-gate (void) sprintf(name, "com%c-%s", number, property);
11670Sstevel@tonic-gate len = sizeof (val);
11685295Srandyf ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
11690Sstevel@tonic-gate }
11700Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) {
11710Sstevel@tonic-gate (void) sprintf(name, "tty0%c-%s", number, property);
11720Sstevel@tonic-gate len = sizeof (val);
11735295Srandyf ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
11740Sstevel@tonic-gate }
11750Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) {
11760Sstevel@tonic-gate (void) sprintf(name, "port-%c-%s", letter, property);
11770Sstevel@tonic-gate len = sizeof (val);
11785295Srandyf ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
11790Sstevel@tonic-gate }
11800Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS)
11810Sstevel@tonic-gate return (-1); /* property non-existant */
11820Sstevel@tonic-gate if (val[0] == 'f' || val[0] == 'F' || val[0] == '0')
11830Sstevel@tonic-gate return (0); /* property false/0 */
11840Sstevel@tonic-gate return (1); /* property true/!0 */
11850Sstevel@tonic-gate }
11860Sstevel@tonic-gate
11870Sstevel@tonic-gate /* asy_soft_state_free - local wrapper for ddi_soft_state_free(9F) */
11880Sstevel@tonic-gate
11890Sstevel@tonic-gate static void
asy_soft_state_free(struct asycom * asy)11900Sstevel@tonic-gate asy_soft_state_free(struct asycom *asy)
11910Sstevel@tonic-gate {
11920Sstevel@tonic-gate mutex_enter(&asy_glob_lock);
11930Sstevel@tonic-gate /* If we were the max_asy_instance, work out new value */
11940Sstevel@tonic-gate if (asy->asy_unit == max_asy_instance) {
11950Sstevel@tonic-gate while (--max_asy_instance >= 0) {
11960Sstevel@tonic-gate if (ddi_get_soft_state(asy_soft_state,
11970Sstevel@tonic-gate max_asy_instance) != NULL)
11980Sstevel@tonic-gate break;
11990Sstevel@tonic-gate }
12000Sstevel@tonic-gate }
12010Sstevel@tonic-gate mutex_exit(&asy_glob_lock);
12020Sstevel@tonic-gate
12030Sstevel@tonic-gate if (asy->asy_priv != NULL) {
12040Sstevel@tonic-gate kmem_free(asy->asy_priv, sizeof (struct asyncline));
12050Sstevel@tonic-gate asy->asy_priv = NULL;
12060Sstevel@tonic-gate }
12070Sstevel@tonic-gate ddi_soft_state_free(asy_soft_state, asy->asy_unit);
12080Sstevel@tonic-gate }
12090Sstevel@tonic-gate
12100Sstevel@tonic-gate static char *
asy_hw_name(struct asycom * asy)12110Sstevel@tonic-gate asy_hw_name(struct asycom *asy)
12120Sstevel@tonic-gate {
12130Sstevel@tonic-gate switch (asy->asy_hwtype) {
12140Sstevel@tonic-gate case ASY8250A:
12150Sstevel@tonic-gate return ("8250A/16450");
12160Sstevel@tonic-gate case ASY16550:
12170Sstevel@tonic-gate return ("16550");
12180Sstevel@tonic-gate case ASY16550A:
12190Sstevel@tonic-gate return ("16550A");
12200Sstevel@tonic-gate case ASY16650:
12210Sstevel@tonic-gate return ("16650");
12220Sstevel@tonic-gate case ASY16750:
12230Sstevel@tonic-gate return ("16750");
12240Sstevel@tonic-gate default:
12250Sstevel@tonic-gate DEBUGNOTE2(ASY_DEBUG_INIT,
12260Sstevel@tonic-gate "asy%d: asy_hw_name: unknown asy_hwtype: %d",
12270Sstevel@tonic-gate asy->asy_unit, asy->asy_hwtype);
12280Sstevel@tonic-gate return ("?");
12290Sstevel@tonic-gate }
12300Sstevel@tonic-gate }
12310Sstevel@tonic-gate
12320Sstevel@tonic-gate static int
asy_identify_chip(dev_info_t * devi,struct asycom * asy)12330Sstevel@tonic-gate asy_identify_chip(dev_info_t *devi, struct asycom *asy)
12340Sstevel@tonic-gate {
12350Sstevel@tonic-gate int ret;
12360Sstevel@tonic-gate int mcr;
12370Sstevel@tonic-gate dev_t dev;
12380Sstevel@tonic-gate uint_t hwtype;
12390Sstevel@tonic-gate
12400Sstevel@tonic-gate if (asy_scr_test) {
12410Sstevel@tonic-gate /* Check scratch register works. */
12420Sstevel@tonic-gate
12430Sstevel@tonic-gate /* write to scratch register */
12441106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, SCRTEST);
12450Sstevel@tonic-gate /* make sure that pattern doesn't just linger on the bus */
12461106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR, 0x00);
12470Sstevel@tonic-gate /* read data back from scratch register */
12481106Smrj ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR);
12490Sstevel@tonic-gate if (ret != SCRTEST) {
12500Sstevel@tonic-gate /*
12510Sstevel@tonic-gate * Scratch register not working.
12520Sstevel@tonic-gate * Probably not an async chip.
12530Sstevel@tonic-gate * 8250 and 8250B don't have scratch registers,
12540Sstevel@tonic-gate * but only worked in ancient PC XT's anyway.
12550Sstevel@tonic-gate */
125610811SKevin.Crowe@Sun.COM cmn_err(CE_CONT, "!asy%d: UART @ %p "
12570Sstevel@tonic-gate "scratch register: expected 0x5a, got 0x%02x\n",
12580Sstevel@tonic-gate asy->asy_unit, (void *)asy->asy_ioaddr, ret);
12590Sstevel@tonic-gate return (DDI_FAILURE);
12600Sstevel@tonic-gate }
12610Sstevel@tonic-gate }
12620Sstevel@tonic-gate /*
12630Sstevel@tonic-gate * Use 16550 fifo reset sequence specified in NS application
12640Sstevel@tonic-gate * note. Disable fifos until chip is initialized.
12650Sstevel@tonic-gate */
12661106Smrj ddi_put8(asy->asy_iohandle,
12670Sstevel@tonic-gate asy->asy_ioaddr + FIFOR, 0x00); /* clear */
12681106Smrj ddi_put8(asy->asy_iohandle,
12690Sstevel@tonic-gate asy->asy_ioaddr + FIFOR, FIFO_ON); /* enable */
12701106Smrj ddi_put8(asy->asy_iohandle,
12710Sstevel@tonic-gate asy->asy_ioaddr + FIFOR, FIFO_ON | FIFORXFLSH);
12720Sstevel@tonic-gate /* reset */
12730Sstevel@tonic-gate if (asymaxchip >= ASY16650 && asy_scr_test) {
12740Sstevel@tonic-gate /*
12750Sstevel@tonic-gate * Reset 16650 enhanced regs also, in case we have one of these
12760Sstevel@tonic-gate */
12771106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
12780Sstevel@tonic-gate EFRACCESS);
12791106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR,
12800Sstevel@tonic-gate 0);
12811106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
12820Sstevel@tonic-gate STOP1|BITS8);
12830Sstevel@tonic-gate }
12840Sstevel@tonic-gate
12850Sstevel@tonic-gate /*
12860Sstevel@tonic-gate * See what sort of FIFO we have.
12870Sstevel@tonic-gate * Try enabling it and see what chip makes of this.
12880Sstevel@tonic-gate */
12890Sstevel@tonic-gate
12900Sstevel@tonic-gate asy->asy_fifor = 0;
12910Sstevel@tonic-gate asy->asy_hwtype = asymaxchip; /* just for asy_reset_fifo() */
12920Sstevel@tonic-gate if (asymaxchip >= ASY16550A)
12930Sstevel@tonic-gate asy->asy_fifor |=
12940Sstevel@tonic-gate FIFO_ON | FIFODMA | (asy_trig_level & 0xff);
12950Sstevel@tonic-gate if (asymaxchip >= ASY16650)
12960Sstevel@tonic-gate asy->asy_fifor |= FIFOEXTRA1 | FIFOEXTRA2;
12970Sstevel@tonic-gate
12980Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH);
12990Sstevel@tonic-gate
13001106Smrj mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
13011106Smrj ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR);
13020Sstevel@tonic-gate DEBUGCONT4(ASY_DEBUG_CHIP,
13030Sstevel@tonic-gate "asy%d: probe fifo FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n",
13040Sstevel@tonic-gate asy->asy_unit, asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH,
13050Sstevel@tonic-gate ret, mcr);
13060Sstevel@tonic-gate switch (ret & 0xf0) {
13070Sstevel@tonic-gate case 0x40:
13080Sstevel@tonic-gate hwtype = ASY16550; /* 16550 with broken FIFO */
13090Sstevel@tonic-gate asy->asy_fifor = 0;
13100Sstevel@tonic-gate break;
13110Sstevel@tonic-gate case 0xc0:
13120Sstevel@tonic-gate hwtype = ASY16550A;
13130Sstevel@tonic-gate asy->asy_fifo_buf = 16;
13140Sstevel@tonic-gate asy->asy_use_fifo = FIFO_ON;
13150Sstevel@tonic-gate asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2);
13160Sstevel@tonic-gate break;
13170Sstevel@tonic-gate case 0xe0:
13180Sstevel@tonic-gate hwtype = ASY16650;
13190Sstevel@tonic-gate asy->asy_fifo_buf = 32;
13200Sstevel@tonic-gate asy->asy_use_fifo = FIFO_ON;
13210Sstevel@tonic-gate asy->asy_fifor &= ~(FIFOEXTRA1);
13220Sstevel@tonic-gate break;
13230Sstevel@tonic-gate case 0xf0:
13240Sstevel@tonic-gate /*
13250Sstevel@tonic-gate * Note we get 0xff if chip didn't return us anything,
13260Sstevel@tonic-gate * e.g. if there's no chip there.
13270Sstevel@tonic-gate */
13280Sstevel@tonic-gate if (ret == 0xff) {
13290Sstevel@tonic-gate cmn_err(CE_CONT, "asy%d: UART @ %p "
13300Sstevel@tonic-gate "interrupt register: got 0xff\n",
13310Sstevel@tonic-gate asy->asy_unit, (void *)asy->asy_ioaddr);
13320Sstevel@tonic-gate return (DDI_FAILURE);
13330Sstevel@tonic-gate }
13340Sstevel@tonic-gate /*FALLTHRU*/
13350Sstevel@tonic-gate case 0xd0:
13360Sstevel@tonic-gate hwtype = ASY16750;
13370Sstevel@tonic-gate asy->asy_fifo_buf = 64;
13380Sstevel@tonic-gate asy->asy_use_fifo = FIFO_ON;
13390Sstevel@tonic-gate break;
13400Sstevel@tonic-gate default:
13410Sstevel@tonic-gate hwtype = ASY8250A; /* No FIFO */
13420Sstevel@tonic-gate asy->asy_fifor = 0;
13430Sstevel@tonic-gate }
13440Sstevel@tonic-gate
13450Sstevel@tonic-gate if (hwtype > asymaxchip) {
13460Sstevel@tonic-gate cmn_err(CE_CONT, "asy%d: UART @ %p "
13470Sstevel@tonic-gate "unexpected probe result: "
13480Sstevel@tonic-gate "FIFOR=0x%02x ISR=0x%02x MCR=0x%02x\n",
13490Sstevel@tonic-gate asy->asy_unit, (void *)asy->asy_ioaddr,
13500Sstevel@tonic-gate asy->asy_fifor | FIFOTXFLSH | FIFORXFLSH, ret, mcr);
13510Sstevel@tonic-gate return (DDI_FAILURE);
13520Sstevel@tonic-gate }
13530Sstevel@tonic-gate
13540Sstevel@tonic-gate /*
13550Sstevel@tonic-gate * Now reset the FIFO operation appropriate for the chip type.
13560Sstevel@tonic-gate * Note we must call asy_reset_fifo() before any possible
13570Sstevel@tonic-gate * downgrade of the asy->asy_hwtype, or it may not disable
13580Sstevel@tonic-gate * the more advanced features we specifically want downgraded.
13590Sstevel@tonic-gate */
13600Sstevel@tonic-gate asy_reset_fifo(asy, 0);
13610Sstevel@tonic-gate asy->asy_hwtype = hwtype;
13620Sstevel@tonic-gate
13630Sstevel@tonic-gate /*
13640Sstevel@tonic-gate * Check for Exar/Startech ST16C650, which will still look like a
13650Sstevel@tonic-gate * 16550A until we enable its enhanced mode.
13660Sstevel@tonic-gate */
13670Sstevel@tonic-gate if (asy->asy_hwtype == ASY16550A && asymaxchip >= ASY16650 &&
13680Sstevel@tonic-gate asy_scr_test) {
13690Sstevel@tonic-gate /* Enable enhanced mode register access */
13701106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
13710Sstevel@tonic-gate EFRACCESS);
13720Sstevel@tonic-gate /* zero scratch register (not scratch register if enhanced) */
13731106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + SCR, 0);
13740Sstevel@tonic-gate /* Disable enhanced mode register access */
13751106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
13760Sstevel@tonic-gate STOP1|BITS8);
13770Sstevel@tonic-gate /* read back scratch register */
13781106Smrj ret = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + SCR);
13790Sstevel@tonic-gate if (ret == SCRTEST) {
13800Sstevel@tonic-gate /* looks like we have an ST16650 -- enable it */
13811106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
13820Sstevel@tonic-gate EFRACCESS);
13831106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR,
13840Sstevel@tonic-gate ENHENABLE);
13851106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
13860Sstevel@tonic-gate STOP1|BITS8);
13870Sstevel@tonic-gate asy->asy_hwtype = ASY16650;
13880Sstevel@tonic-gate asy->asy_fifo_buf = 32;
13890Sstevel@tonic-gate asy->asy_fifor |= 0x10; /* 24 byte txfifo trigger */
13900Sstevel@tonic-gate asy_reset_fifo(asy, 0);
13910Sstevel@tonic-gate }
13920Sstevel@tonic-gate }
13930Sstevel@tonic-gate
13940Sstevel@tonic-gate /*
13950Sstevel@tonic-gate * If we think we might have a FIFO larger than 16 characters,
13960Sstevel@tonic-gate * measure FIFO size and check it against expected.
13970Sstevel@tonic-gate */
13980Sstevel@tonic-gate if (asy_fifo_test > 0 &&
13990Sstevel@tonic-gate !(asy->asy_flags2 & ASY2_NO_LOOPBACK) &&
14000Sstevel@tonic-gate (asy->asy_fifo_buf > 16 ||
14010Sstevel@tonic-gate (asy_fifo_test > 1 && asy->asy_use_fifo == FIFO_ON) ||
14020Sstevel@tonic-gate ASY_DEBUG(ASY_DEBUG_CHIP))) {
14030Sstevel@tonic-gate int i;
14040Sstevel@tonic-gate
14050Sstevel@tonic-gate /* Set baud rate to 57600 (fairly arbitrary choice) */
14061106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
14070Sstevel@tonic-gate DLAB);
14081106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
14090Sstevel@tonic-gate asyspdtab[B57600] & 0xff);
14101106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
14110Sstevel@tonic-gate (asyspdtab[B57600] >> 8) & 0xff);
14120Sstevel@tonic-gate /* Set 8 bits, 1 stop bit */
14131106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
14140Sstevel@tonic-gate STOP1|BITS8);
14150Sstevel@tonic-gate /* Set loopback mode */
14161106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
14170Sstevel@tonic-gate DTR | RTS | ASY_LOOP | OUT1 | OUT2);
14180Sstevel@tonic-gate
14190Sstevel@tonic-gate /* Overfill fifo */
14200Sstevel@tonic-gate for (i = 0; i < asy->asy_fifo_buf * 2; i++) {
14211106Smrj ddi_put8(asy->asy_iohandle,
14220Sstevel@tonic-gate asy->asy_ioaddr + DAT, i);
14230Sstevel@tonic-gate }
14240Sstevel@tonic-gate /*
14250Sstevel@tonic-gate * Now there's an interesting question here about which
14260Sstevel@tonic-gate * FIFO we're testing the size of, RX or TX. We just
14270Sstevel@tonic-gate * filled the TX FIFO much faster than it can empty,
14280Sstevel@tonic-gate * although it is possible one or two characters may
14290Sstevel@tonic-gate * have gone from it to the TX shift register.
14300Sstevel@tonic-gate * We wait for enough time for all the characters to
14310Sstevel@tonic-gate * move into the RX FIFO and any excess characters to
14320Sstevel@tonic-gate * have been lost, and then read all the RX FIFO. So
14330Sstevel@tonic-gate * the answer we finally get will be the size which is
14340Sstevel@tonic-gate * the MIN(RX FIFO,(TX FIFO + 1 or 2)). The critical
14350Sstevel@tonic-gate * one is actually the TX FIFO, because if we overfill
14360Sstevel@tonic-gate * it in normal operation, the excess characters are
14370Sstevel@tonic-gate * lost with no warning.
14380Sstevel@tonic-gate */
14390Sstevel@tonic-gate /*
14400Sstevel@tonic-gate * Wait for characters to move into RX FIFO.
14410Sstevel@tonic-gate * In theory, 200 * asy->asy_fifo_buf * 2 should be
14420Sstevel@tonic-gate * enough. However, in practice it isn't always, so we
14430Sstevel@tonic-gate * increase to 400 so some slow 16550A's finish, and we
14440Sstevel@tonic-gate * increase to 3 so we spot more characters coming back
14450Sstevel@tonic-gate * than we sent, in case that should ever happen.
14460Sstevel@tonic-gate */
14470Sstevel@tonic-gate delay(drv_usectohz(400 * asy->asy_fifo_buf * 3));
14480Sstevel@tonic-gate
14490Sstevel@tonic-gate /* Now see how many characters we can read back */
14500Sstevel@tonic-gate for (i = 0; i < asy->asy_fifo_buf * 3; i++) {
14511106Smrj ret = ddi_get8(asy->asy_iohandle,
14520Sstevel@tonic-gate asy->asy_ioaddr + LSR);
14530Sstevel@tonic-gate if (!(ret & RCA))
14540Sstevel@tonic-gate break; /* FIFO emptied */
14551106Smrj (void) ddi_get8(asy->asy_iohandle,
14560Sstevel@tonic-gate asy->asy_ioaddr + DAT); /* lose another */
14570Sstevel@tonic-gate }
14580Sstevel@tonic-gate
14590Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_CHIP,
14600Sstevel@tonic-gate "asy%d FIFO size: expected=%d, measured=%d\n",
14610Sstevel@tonic-gate asy->asy_unit, asy->asy_fifo_buf, i);
14620Sstevel@tonic-gate
14630Sstevel@tonic-gate hwtype = asy->asy_hwtype;
14640Sstevel@tonic-gate if (i < asy->asy_fifo_buf) {
14650Sstevel@tonic-gate /*
14660Sstevel@tonic-gate * FIFO is somewhat smaller than we anticipated.
14670Sstevel@tonic-gate * If we have 16 characters usable, then this
14680Sstevel@tonic-gate * UART will probably work well enough in
14690Sstevel@tonic-gate * 16550A mode. If less than 16 characters,
14700Sstevel@tonic-gate * then we'd better not use it at all.
14710Sstevel@tonic-gate * UARTs with busted FIFOs do crop up.
14720Sstevel@tonic-gate */
14730Sstevel@tonic-gate if (i >= 16 && asy->asy_fifo_buf >= 16) {
14740Sstevel@tonic-gate /* fall back to a 16550A */
14750Sstevel@tonic-gate hwtype = ASY16550A;
14760Sstevel@tonic-gate asy->asy_fifo_buf = 16;
14770Sstevel@tonic-gate asy->asy_fifor &= ~(FIFOEXTRA1 | FIFOEXTRA2);
14780Sstevel@tonic-gate } else {
14790Sstevel@tonic-gate /* fall back to no FIFO at all */
14800Sstevel@tonic-gate hwtype = ASY16550;
14810Sstevel@tonic-gate asy->asy_fifo_buf = 1;
14820Sstevel@tonic-gate asy->asy_use_fifo = FIFO_OFF;
14830Sstevel@tonic-gate asy->asy_fifor &=
14840Sstevel@tonic-gate ~(FIFO_ON | FIFOEXTRA1 | FIFOEXTRA2);
14850Sstevel@tonic-gate }
14860Sstevel@tonic-gate }
14870Sstevel@tonic-gate /*
14880Sstevel@tonic-gate * We will need to reprogram the FIFO if we changed
14890Sstevel@tonic-gate * our mind about how to drive it above, and in any
14900Sstevel@tonic-gate * case, it would be a good idea to flush any garbage
14910Sstevel@tonic-gate * out incase the loopback test left anything behind.
14920Sstevel@tonic-gate * Again as earlier above, we must call asy_reset_fifo()
14930Sstevel@tonic-gate * before any possible downgrade of asy->asy_hwtype.
14940Sstevel@tonic-gate */
14950Sstevel@tonic-gate if (asy->asy_hwtype >= ASY16650 && hwtype < ASY16650) {
14960Sstevel@tonic-gate /* Disable 16650 enhanced mode */
14971106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
14980Sstevel@tonic-gate EFRACCESS);
14991106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + EFR,
15000Sstevel@tonic-gate 0);
15011106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
15020Sstevel@tonic-gate STOP1|BITS8);
15030Sstevel@tonic-gate }
15040Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH);
15050Sstevel@tonic-gate asy->asy_hwtype = hwtype;
15060Sstevel@tonic-gate
15070Sstevel@tonic-gate /* Clear loopback mode and restore DTR/RTS */
15081106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr);
15090Sstevel@tonic-gate }
15100Sstevel@tonic-gate
15110Sstevel@tonic-gate DEBUGNOTE3(ASY_DEBUG_CHIP, "asy%d %s @ %p",
15120Sstevel@tonic-gate asy->asy_unit, asy_hw_name(asy), (void *)asy->asy_ioaddr);
15130Sstevel@tonic-gate
15140Sstevel@tonic-gate /* Make UART type visible in device tree for prtconf, etc */
15150Sstevel@tonic-gate dev = makedevice(DDI_MAJOR_T_UNKNOWN, asy->asy_unit);
15160Sstevel@tonic-gate (void) ddi_prop_update_string(dev, devi, "uart", asy_hw_name(asy));
15170Sstevel@tonic-gate
15180Sstevel@tonic-gate if (asy->asy_hwtype == ASY16550) /* for broken 16550's, */
15190Sstevel@tonic-gate asy->asy_hwtype = ASY8250A; /* drive them as 8250A */
15200Sstevel@tonic-gate
15210Sstevel@tonic-gate return (DDI_SUCCESS);
15220Sstevel@tonic-gate }
15230Sstevel@tonic-gate
15240Sstevel@tonic-gate /*
15250Sstevel@tonic-gate * asyinit() initializes the TTY protocol-private data for this channel
15260Sstevel@tonic-gate * before enabling the interrupts.
15270Sstevel@tonic-gate */
15280Sstevel@tonic-gate static void
asyinit(struct asycom * asy)15290Sstevel@tonic-gate asyinit(struct asycom *asy)
15300Sstevel@tonic-gate {
15310Sstevel@tonic-gate struct asyncline *async;
15320Sstevel@tonic-gate
15330Sstevel@tonic-gate asy->asy_priv = kmem_zalloc(sizeof (struct asyncline), KM_SLEEP);
15340Sstevel@tonic-gate async = asy->asy_priv;
15350Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
15360Sstevel@tonic-gate async->async_common = asy;
15370Sstevel@tonic-gate cv_init(&async->async_flags_cv, NULL, CV_DRIVER, NULL);
15380Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
15390Sstevel@tonic-gate }
15400Sstevel@tonic-gate
15410Sstevel@tonic-gate /*ARGSUSED3*/
15420Sstevel@tonic-gate static int
asyopen(queue_t * rq,dev_t * dev,int flag,int sflag,cred_t * cr)15430Sstevel@tonic-gate asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr)
15440Sstevel@tonic-gate {
15450Sstevel@tonic-gate struct asycom *asy;
15460Sstevel@tonic-gate struct asyncline *async;
15470Sstevel@tonic-gate int mcr;
15480Sstevel@tonic-gate int unit;
15490Sstevel@tonic-gate int len;
15500Sstevel@tonic-gate struct termios *termiosp;
15510Sstevel@tonic-gate
15520Sstevel@tonic-gate unit = UNIT(*dev);
15530Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dopen\n", unit);
15540Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, unit);
15550Sstevel@tonic-gate if (asy == NULL)
15560Sstevel@tonic-gate return (ENXIO); /* unit not configured */
15570Sstevel@tonic-gate async = asy->asy_priv;
15580Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
15590Sstevel@tonic-gate
15600Sstevel@tonic-gate again:
15610Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
15620Sstevel@tonic-gate
15630Sstevel@tonic-gate /*
15640Sstevel@tonic-gate * Block waiting for carrier to come up, unless this is a no-delay open.
15650Sstevel@tonic-gate */
15660Sstevel@tonic-gate if (!(async->async_flags & ASYNC_ISOPEN)) {
15670Sstevel@tonic-gate /*
15680Sstevel@tonic-gate * Set the default termios settings (cflag).
15690Sstevel@tonic-gate * Others are set in ldterm.
15700Sstevel@tonic-gate */
15710Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
15720Sstevel@tonic-gate
15730Sstevel@tonic-gate if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(),
15740Sstevel@tonic-gate 0, "ttymodes",
15750Sstevel@tonic-gate (caddr_t)&termiosp, &len) == DDI_PROP_SUCCESS &&
15760Sstevel@tonic-gate len == sizeof (struct termios)) {
15770Sstevel@tonic-gate async->async_ttycommon.t_cflag = termiosp->c_cflag;
15780Sstevel@tonic-gate kmem_free(termiosp, len);
15790Sstevel@tonic-gate } else
15800Sstevel@tonic-gate cmn_err(CE_WARN,
15815295Srandyf "asy: couldn't get ttymodes property!");
15820Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
15830Sstevel@tonic-gate
15840Sstevel@tonic-gate /* eeprom mode support - respect properties */
15850Sstevel@tonic-gate if (asy->asy_cflag)
15860Sstevel@tonic-gate async->async_ttycommon.t_cflag = asy->asy_cflag;
15870Sstevel@tonic-gate
15880Sstevel@tonic-gate async->async_ttycommon.t_iflag = 0;
15890Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL;
15900Sstevel@tonic-gate async->async_ttycommon.t_size.ws_row = 0;
15910Sstevel@tonic-gate async->async_ttycommon.t_size.ws_col = 0;
15920Sstevel@tonic-gate async->async_ttycommon.t_size.ws_xpixel = 0;
15930Sstevel@tonic-gate async->async_ttycommon.t_size.ws_ypixel = 0;
15940Sstevel@tonic-gate async->async_dev = *dev;
15950Sstevel@tonic-gate async->async_wbufcid = 0;
15960Sstevel@tonic-gate
15970Sstevel@tonic-gate async->async_startc = CSTART;
15980Sstevel@tonic-gate async->async_stopc = CSTOP;
15990Sstevel@tonic-gate asy_program(asy, ASY_INIT);
16005295Srandyf } else
16015295Srandyf if ((async->async_ttycommon.t_flags & TS_XCLUDE) &&
16025295Srandyf secpolicy_excl_open(cr) != 0) {
16030Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
16040Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
16050Sstevel@tonic-gate return (EBUSY);
16060Sstevel@tonic-gate } else if ((*dev & OUTLINE) && !(async->async_flags & ASYNC_OUT)) {
16070Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
16080Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
16090Sstevel@tonic-gate return (EBUSY);
16100Sstevel@tonic-gate }
16110Sstevel@tonic-gate
16120Sstevel@tonic-gate if (*dev & OUTLINE)
16130Sstevel@tonic-gate async->async_flags |= ASYNC_OUT;
16140Sstevel@tonic-gate
16150Sstevel@tonic-gate /* Raise DTR on every open, but delay if it was just lowered. */
16160Sstevel@tonic-gate while (async->async_flags & ASYNC_DTR_DELAY) {
16170Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM,
16180Sstevel@tonic-gate "asy%dopen: waiting for the ASYNC_DTR_DELAY to be clear\n",
16190Sstevel@tonic-gate unit);
16200Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
16210Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv,
16220Sstevel@tonic-gate &asy->asy_excl) == 0) {
16230Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM,
16240Sstevel@tonic-gate "asy%dopen: interrupted by signal, exiting\n",
16250Sstevel@tonic-gate unit);
16260Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
16270Sstevel@tonic-gate return (EINTR);
16280Sstevel@tonic-gate }
16290Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
16300Sstevel@tonic-gate }
16310Sstevel@tonic-gate
16321106Smrj mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
16331106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
16345295Srandyf mcr|(asy->asy_mcr&DTR));
16350Sstevel@tonic-gate
16360Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_INIT,
16375295Srandyf "asy%dopen: \"Raise DTR on every open\": make mcr = %x, "
16385295Srandyf "make TS_SOFTCAR = %s\n",
16395295Srandyf unit, mcr|(asy->asy_mcr&DTR),
16405295Srandyf (asy->asy_flags & ASY_IGNORE_CD) ? "ON" : "OFF");
16415295Srandyf
16420Sstevel@tonic-gate if (asy->asy_flags & ASY_IGNORE_CD) {
16430Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM,
16445295Srandyf "asy%dopen: ASY_IGNORE_CD set, set TS_SOFTCAR\n",
16455295Srandyf unit);
16460Sstevel@tonic-gate async->async_ttycommon.t_flags |= TS_SOFTCAR;
16470Sstevel@tonic-gate }
16480Sstevel@tonic-gate else
16490Sstevel@tonic-gate async->async_ttycommon.t_flags &= ~TS_SOFTCAR;
16500Sstevel@tonic-gate
16510Sstevel@tonic-gate /*
16520Sstevel@tonic-gate * Check carrier.
16530Sstevel@tonic-gate */
16541106Smrj asy->asy_msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
16550Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_INIT, "asy%dopen: TS_SOFTCAR is %s, "
16565295Srandyf "MSR & DCD is %s\n",
16575295Srandyf unit,
16585295Srandyf (async->async_ttycommon.t_flags & TS_SOFTCAR) ? "set" : "clear",
16595295Srandyf (asy->asy_msr & DCD) ? "set" : "clear");
16605295Srandyf
16610Sstevel@tonic-gate if (asy->asy_msr & DCD)
16620Sstevel@tonic-gate async->async_flags |= ASYNC_CARR_ON;
16630Sstevel@tonic-gate else
16640Sstevel@tonic-gate async->async_flags &= ~ASYNC_CARR_ON;
16650Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
16660Sstevel@tonic-gate
16670Sstevel@tonic-gate /*
16680Sstevel@tonic-gate * If FNDELAY and FNONBLOCK are clear, block until carrier up.
16690Sstevel@tonic-gate * Quit on interrupt.
16700Sstevel@tonic-gate */
16710Sstevel@tonic-gate if (!(flag & (FNDELAY|FNONBLOCK)) &&
16720Sstevel@tonic-gate !(async->async_ttycommon.t_cflag & CLOCAL)) {
16730Sstevel@tonic-gate if ((!(async->async_flags & (ASYNC_CARR_ON|ASYNC_OUT)) &&
16740Sstevel@tonic-gate !(async->async_ttycommon.t_flags & TS_SOFTCAR)) ||
16750Sstevel@tonic-gate ((async->async_flags & ASYNC_OUT) &&
16760Sstevel@tonic-gate !(*dev & OUTLINE))) {
16770Sstevel@tonic-gate async->async_flags |= ASYNC_WOPEN;
16780Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv,
16790Sstevel@tonic-gate &asy->asy_excl) == B_FALSE) {
16800Sstevel@tonic-gate async->async_flags &= ~ASYNC_WOPEN;
16810Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
16820Sstevel@tonic-gate return (EINTR);
16830Sstevel@tonic-gate }
16840Sstevel@tonic-gate async->async_flags &= ~ASYNC_WOPEN;
16850Sstevel@tonic-gate goto again;
16860Sstevel@tonic-gate }
16870Sstevel@tonic-gate } else if ((async->async_flags & ASYNC_OUT) && !(*dev & OUTLINE)) {
16880Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
16890Sstevel@tonic-gate return (EBUSY);
16900Sstevel@tonic-gate }
16910Sstevel@tonic-gate
16920Sstevel@tonic-gate async->async_ttycommon.t_readq = rq;
16930Sstevel@tonic-gate async->async_ttycommon.t_writeq = WR(rq);
16940Sstevel@tonic-gate rq->q_ptr = WR(rq)->q_ptr = (caddr_t)async;
16950Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
16960Sstevel@tonic-gate /*
16970Sstevel@tonic-gate * Caution here -- qprocson sets the pointers that are used by canput
16980Sstevel@tonic-gate * called by async_softint. ASYNC_ISOPEN must *not* be set until those
16990Sstevel@tonic-gate * pointers are valid.
17000Sstevel@tonic-gate */
17010Sstevel@tonic-gate qprocson(rq);
17020Sstevel@tonic-gate async->async_flags |= ASYNC_ISOPEN;
17030Sstevel@tonic-gate async->async_polltid = 0;
17040Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "asy%dopen: done\n", unit);
17050Sstevel@tonic-gate return (0);
17060Sstevel@tonic-gate }
17070Sstevel@tonic-gate
17080Sstevel@tonic-gate static void
async_progress_check(void * arg)17090Sstevel@tonic-gate async_progress_check(void *arg)
17100Sstevel@tonic-gate {
17110Sstevel@tonic-gate struct asyncline *async = arg;
17120Sstevel@tonic-gate struct asycom *asy = async->async_common;
17130Sstevel@tonic-gate mblk_t *bp;
17140Sstevel@tonic-gate
17150Sstevel@tonic-gate /*
17160Sstevel@tonic-gate * We define "progress" as either waiting on a timed break or delay, or
17170Sstevel@tonic-gate * having had at least one transmitter interrupt. If none of these are
17180Sstevel@tonic-gate * true, then just terminate the output and wake up that close thread.
17190Sstevel@tonic-gate */
17200Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
17210Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
17220Sstevel@tonic-gate if (!(async->async_flags & (ASYNC_BREAK|ASYNC_DELAY|ASYNC_PROGRESS))) {
17230Sstevel@tonic-gate async->async_ocnt = 0;
17240Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY;
17250Sstevel@tonic-gate async->async_timer = 0;
17260Sstevel@tonic-gate bp = async->async_xmitblk;
17270Sstevel@tonic-gate async->async_xmitblk = NULL;
17280Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
17290Sstevel@tonic-gate if (bp != NULL)
17300Sstevel@tonic-gate freeb(bp);
17310Sstevel@tonic-gate /*
17320Sstevel@tonic-gate * Since this timer is running, we know that we're in exit(2).
17330Sstevel@tonic-gate * That means that the user can't possibly be waiting on any
17340Sstevel@tonic-gate * valid ioctl(2) completion anymore, and we should just flush
17350Sstevel@tonic-gate * everything.
17360Sstevel@tonic-gate */
17370Sstevel@tonic-gate flushq(async->async_ttycommon.t_writeq, FLUSHALL);
17380Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
17390Sstevel@tonic-gate } else {
17400Sstevel@tonic-gate async->async_flags &= ~ASYNC_PROGRESS;
17410Sstevel@tonic-gate async->async_timer = timeout(async_progress_check, async,
17420Sstevel@tonic-gate drv_usectohz(asy_drain_check));
17430Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
17440Sstevel@tonic-gate }
17450Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
17460Sstevel@tonic-gate }
17470Sstevel@tonic-gate
17480Sstevel@tonic-gate /*
17490Sstevel@tonic-gate * Release DTR so that asyopen() can raise it.
17500Sstevel@tonic-gate */
17510Sstevel@tonic-gate static void
async_dtr_free(struct asyncline * async)17520Sstevel@tonic-gate async_dtr_free(struct asyncline *async)
17530Sstevel@tonic-gate {
17540Sstevel@tonic-gate struct asycom *asy = async->async_common;
17550Sstevel@tonic-gate
17560Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM,
17570Sstevel@tonic-gate "async_dtr_free, clearing ASYNC_DTR_DELAY\n");
17580Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
17590Sstevel@tonic-gate async->async_flags &= ~ASYNC_DTR_DELAY;
17600Sstevel@tonic-gate async->async_dtrtid = 0;
17610Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
17620Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
17630Sstevel@tonic-gate }
17640Sstevel@tonic-gate
17650Sstevel@tonic-gate /*
17660Sstevel@tonic-gate * Close routine.
17670Sstevel@tonic-gate */
17680Sstevel@tonic-gate /*ARGSUSED2*/
17690Sstevel@tonic-gate static int
asyclose(queue_t * q,int flag,cred_t * credp)17700Sstevel@tonic-gate asyclose(queue_t *q, int flag, cred_t *credp)
17710Sstevel@tonic-gate {
17720Sstevel@tonic-gate struct asyncline *async;
17730Sstevel@tonic-gate struct asycom *asy;
17740Sstevel@tonic-gate int icr, lcr;
17750Sstevel@tonic-gate #ifdef DEBUG
17760Sstevel@tonic-gate int instance;
17770Sstevel@tonic-gate #endif
17780Sstevel@tonic-gate
17790Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr;
17800Sstevel@tonic-gate ASSERT(async != NULL);
17810Sstevel@tonic-gate #ifdef DEBUG
17820Sstevel@tonic-gate instance = UNIT(async->async_dev);
17830Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dclose\n", instance);
17840Sstevel@tonic-gate #endif
17850Sstevel@tonic-gate asy = async->async_common;
17860Sstevel@tonic-gate
17870Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
17880Sstevel@tonic-gate async->async_flags |= ASYNC_CLOSING;
17890Sstevel@tonic-gate
17900Sstevel@tonic-gate /*
17910Sstevel@tonic-gate * Turn off PPS handling early to avoid events occuring during
17920Sstevel@tonic-gate * close. Also reset the DCD edge monitoring bit.
17930Sstevel@tonic-gate */
17940Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
17950Sstevel@tonic-gate asy->asy_flags &= ~(ASY_PPS | ASY_PPS_EDGE);
17960Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
17970Sstevel@tonic-gate
17980Sstevel@tonic-gate /*
17990Sstevel@tonic-gate * There are two flavors of break -- timed (M_BREAK or TCSBRK) and
18000Sstevel@tonic-gate * untimed (TIOCSBRK). For the timed case, these are enqueued on our
18010Sstevel@tonic-gate * write queue and there's a timer running, so we don't have to worry
18020Sstevel@tonic-gate * about them. For the untimed case, though, the user obviously made a
18030Sstevel@tonic-gate * mistake, because these are handled immediately. We'll terminate the
18040Sstevel@tonic-gate * break now and honor his implicit request by discarding the rest of
18050Sstevel@tonic-gate * the data.
18060Sstevel@tonic-gate */
18070Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_SUSPEND) {
18080Sstevel@tonic-gate if (async->async_utbrktid != 0) {
18090Sstevel@tonic-gate (void) untimeout(async->async_utbrktid);
18100Sstevel@tonic-gate async->async_utbrktid = 0;
18110Sstevel@tonic-gate }
18120Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
18131106Smrj lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
18141106Smrj ddi_put8(asy->asy_iohandle,
18150Sstevel@tonic-gate asy->asy_ioaddr + LCR, (lcr & ~SETBREAK));
18160Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
18170Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_SUSPEND;
18180Sstevel@tonic-gate goto nodrain;
18190Sstevel@tonic-gate }
18200Sstevel@tonic-gate
18210Sstevel@tonic-gate /*
18220Sstevel@tonic-gate * If the user told us not to delay the close ("non-blocking"), then
18230Sstevel@tonic-gate * don't bother trying to drain.
18240Sstevel@tonic-gate *
18250Sstevel@tonic-gate * If the user did M_STOP (ASYNC_STOPPED), there's no hope of ever
18260Sstevel@tonic-gate * getting an M_START (since these messages aren't enqueued), and the
18270Sstevel@tonic-gate * only other way to clear the stop condition is by loss of DCD, which
18280Sstevel@tonic-gate * would discard the queue data. Thus, we drop the output data if
18290Sstevel@tonic-gate * ASYNC_STOPPED is set.
18300Sstevel@tonic-gate */
18310Sstevel@tonic-gate if ((flag & (FNDELAY|FNONBLOCK)) ||
18320Sstevel@tonic-gate (async->async_flags & ASYNC_STOPPED)) {
18330Sstevel@tonic-gate goto nodrain;
18340Sstevel@tonic-gate }
18350Sstevel@tonic-gate
18360Sstevel@tonic-gate /*
18370Sstevel@tonic-gate * If there's any pending output, then we have to try to drain it.
18380Sstevel@tonic-gate * There are two main cases to be handled:
18390Sstevel@tonic-gate * - called by close(2): need to drain until done or until
18400Sstevel@tonic-gate * a signal is received. No timeout.
18410Sstevel@tonic-gate * - called by exit(2): need to drain while making progress
18420Sstevel@tonic-gate * or until a timeout occurs. No signals.
18430Sstevel@tonic-gate *
18440Sstevel@tonic-gate * If we can't rely on receiving a signal to get us out of a hung
18450Sstevel@tonic-gate * session, then we have to use a timer. In this case, we set a timer
18460Sstevel@tonic-gate * to check for progress in sending the output data -- all that we ask
18470Sstevel@tonic-gate * (at each interval) is that there's been some progress made. Since
18480Sstevel@tonic-gate * the interrupt routine grabs buffers from the write queue, we can't
18490Sstevel@tonic-gate * trust changes in async_ocnt. Instead, we use a progress flag.
18500Sstevel@tonic-gate *
18510Sstevel@tonic-gate * Note that loss of carrier will cause the output queue to be flushed,
18520Sstevel@tonic-gate * and we'll wake up again and finish normally.
18530Sstevel@tonic-gate */
18540Sstevel@tonic-gate if (!ddi_can_receive_sig() && asy_drain_check != 0) {
18550Sstevel@tonic-gate async->async_flags &= ~ASYNC_PROGRESS;
18560Sstevel@tonic-gate async->async_timer = timeout(async_progress_check, async,
18570Sstevel@tonic-gate drv_usectohz(asy_drain_check));
18580Sstevel@tonic-gate }
18590Sstevel@tonic-gate while (async->async_ocnt > 0 ||
18600Sstevel@tonic-gate async->async_ttycommon.t_writeq->q_first != NULL ||
18610Sstevel@tonic-gate (async->async_flags & (ASYNC_BUSY|ASYNC_BREAK|ASYNC_DELAY))) {
18620Sstevel@tonic-gate if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) == 0)
18630Sstevel@tonic-gate break;
18640Sstevel@tonic-gate }
18650Sstevel@tonic-gate if (async->async_timer != 0) {
18660Sstevel@tonic-gate (void) untimeout(async->async_timer);
18670Sstevel@tonic-gate async->async_timer = 0;
18680Sstevel@tonic-gate }
18690Sstevel@tonic-gate
18700Sstevel@tonic-gate nodrain:
18710Sstevel@tonic-gate async->async_ocnt = 0;
18720Sstevel@tonic-gate if (async->async_xmitblk != NULL)
18730Sstevel@tonic-gate freeb(async->async_xmitblk);
18740Sstevel@tonic-gate async->async_xmitblk = NULL;
18750Sstevel@tonic-gate
18760Sstevel@tonic-gate /*
18770Sstevel@tonic-gate * If line has HUPCL set or is incompletely opened fix up the modem
18780Sstevel@tonic-gate * lines.
18790Sstevel@tonic-gate */
18805295Srandyf DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dclose: next check HUPCL flag\n",
18815295Srandyf instance);
18820Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
18830Sstevel@tonic-gate if ((async->async_ttycommon.t_cflag & HUPCL) ||
18840Sstevel@tonic-gate (async->async_flags & ASYNC_WOPEN)) {
18850Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODEM,
18865295Srandyf "asy%dclose: HUPCL flag = %x, ASYNC_WOPEN flag = %x\n",
18875295Srandyf instance,
18885295Srandyf async->async_ttycommon.t_cflag & HUPCL,
18895295Srandyf async->async_ttycommon.t_cflag & ASYNC_WOPEN);
18900Sstevel@tonic-gate async->async_flags |= ASYNC_DTR_DELAY;
18910Sstevel@tonic-gate
18920Sstevel@tonic-gate /* turn off DTR, RTS but NOT interrupt to 386 */
18930Sstevel@tonic-gate if (asy->asy_flags & (ASY_IGNORE_CD|ASY_RTS_DTR_OFF)) {
18940Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODEM,
18955295Srandyf "asy%dclose: ASY_IGNORE_CD flag = %x, "
18965295Srandyf "ASY_RTS_DTR_OFF flag = %x\n",
18975295Srandyf instance,
18985295Srandyf asy->asy_flags & ASY_IGNORE_CD,
18995295Srandyf asy->asy_flags & ASY_RTS_DTR_OFF);
19005295Srandyf
19015295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
19025295Srandyf asy->asy_mcr|OUT2);
19030Sstevel@tonic-gate } else {
19040Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM,
19050Sstevel@tonic-gate "asy%dclose: Dropping DTR and RTS\n", instance);
19065295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
19075295Srandyf OUT2);
19080Sstevel@tonic-gate }
19090Sstevel@tonic-gate async->async_dtrtid =
19100Sstevel@tonic-gate timeout((void (*)())async_dtr_free,
19110Sstevel@tonic-gate (caddr_t)async, drv_usectohz(asy_min_dtr_low));
19120Sstevel@tonic-gate }
19130Sstevel@tonic-gate /*
19140Sstevel@tonic-gate * If nobody's using it now, turn off receiver interrupts.
19150Sstevel@tonic-gate */
19160Sstevel@tonic-gate if ((async->async_flags & (ASYNC_WOPEN|ASYNC_ISOPEN)) == 0) {
19175295Srandyf icr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ICR);
19181106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
19195295Srandyf (icr & ~RIEN));
19200Sstevel@tonic-gate }
19210Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
19220Sstevel@tonic-gate out:
19230Sstevel@tonic-gate ttycommon_close(&async->async_ttycommon);
19240Sstevel@tonic-gate
19250Sstevel@tonic-gate /*
19260Sstevel@tonic-gate * Cancel outstanding "bufcall" request.
19270Sstevel@tonic-gate */
19280Sstevel@tonic-gate if (async->async_wbufcid != 0) {
19290Sstevel@tonic-gate unbufcall(async->async_wbufcid);
19300Sstevel@tonic-gate async->async_wbufcid = 0;
19310Sstevel@tonic-gate }
19320Sstevel@tonic-gate
19330Sstevel@tonic-gate /* Note that qprocsoff can't be done until after interrupts are off */
19340Sstevel@tonic-gate qprocsoff(q);
19350Sstevel@tonic-gate q->q_ptr = WR(q)->q_ptr = NULL;
19360Sstevel@tonic-gate async->async_ttycommon.t_readq = NULL;
19370Sstevel@tonic-gate async->async_ttycommon.t_writeq = NULL;
19380Sstevel@tonic-gate
19390Sstevel@tonic-gate /*
19400Sstevel@tonic-gate * Clear out device state, except persistant device property flags.
19410Sstevel@tonic-gate */
19420Sstevel@tonic-gate async->async_flags &= (ASYNC_DTR_DELAY|ASY_RTS_DTR_OFF);
19430Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
19440Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
19450Sstevel@tonic-gate
19460Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_CLOSE, "asy%dclose: done\n", instance);
19470Sstevel@tonic-gate return (0);
19480Sstevel@tonic-gate }
19490Sstevel@tonic-gate
19500Sstevel@tonic-gate static boolean_t
asy_isbusy(struct asycom * asy)19510Sstevel@tonic-gate asy_isbusy(struct asycom *asy)
19520Sstevel@tonic-gate {
19530Sstevel@tonic-gate struct asyncline *async;
19540Sstevel@tonic-gate
19550Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_EOT, "asy_isbusy\n");
19560Sstevel@tonic-gate async = asy->asy_priv;
19570Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl));
19580Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi));
19595295Srandyf /*
19605295Srandyf * XXXX this should be recoded
19615295Srandyf */
19620Sstevel@tonic-gate return ((async->async_ocnt > 0) ||
19635295Srandyf ((ddi_get8(asy->asy_iohandle,
19645295Srandyf asy->asy_ioaddr + LSR) & (XSRE|XHRE)) == 0));
19650Sstevel@tonic-gate }
19660Sstevel@tonic-gate
19670Sstevel@tonic-gate static void
asy_waiteot(struct asycom * asy)19680Sstevel@tonic-gate asy_waiteot(struct asycom *asy)
19690Sstevel@tonic-gate {
19700Sstevel@tonic-gate /*
19710Sstevel@tonic-gate * Wait for the current transmission block and the
19720Sstevel@tonic-gate * current fifo data to transmit. Once this is done
19730Sstevel@tonic-gate * we may go on.
19740Sstevel@tonic-gate */
19750Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_EOT, "asy_waiteot\n");
19760Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl));
19770Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi));
19780Sstevel@tonic-gate while (asy_isbusy(asy)) {
19790Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
19800Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
19810Sstevel@tonic-gate drv_usecwait(10000); /* wait .01 */
19820Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
19830Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
19840Sstevel@tonic-gate }
19850Sstevel@tonic-gate }
19860Sstevel@tonic-gate
19870Sstevel@tonic-gate /* asy_reset_fifo -- flush fifos and [re]program fifo control register */
19880Sstevel@tonic-gate static void
asy_reset_fifo(struct asycom * asy,uchar_t flush)19890Sstevel@tonic-gate asy_reset_fifo(struct asycom *asy, uchar_t flush)
19900Sstevel@tonic-gate {
19910Sstevel@tonic-gate uchar_t lcr;
19920Sstevel@tonic-gate
19930Sstevel@tonic-gate /* On a 16750, we have to set DLAB in order to set FIFOEXTRA. */
19940Sstevel@tonic-gate
19950Sstevel@tonic-gate if (asy->asy_hwtype >= ASY16750) {
19961106Smrj lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
19971106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
19980Sstevel@tonic-gate lcr | DLAB);
19990Sstevel@tonic-gate }
20000Sstevel@tonic-gate
20011106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + FIFOR,
20020Sstevel@tonic-gate asy->asy_fifor | flush);
20030Sstevel@tonic-gate
20040Sstevel@tonic-gate /* Clear DLAB */
20050Sstevel@tonic-gate
20060Sstevel@tonic-gate if (asy->asy_hwtype >= ASY16750) {
20071106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr);
20080Sstevel@tonic-gate }
20090Sstevel@tonic-gate }
20100Sstevel@tonic-gate
20110Sstevel@tonic-gate /*
20120Sstevel@tonic-gate * Program the ASY port. Most of the async operation is based on the values
20130Sstevel@tonic-gate * of 'c_iflag' and 'c_cflag'.
20140Sstevel@tonic-gate */
20150Sstevel@tonic-gate
20160Sstevel@tonic-gate #define BAUDINDEX(cflg) (((cflg) & CBAUDEXT) ? \
20170Sstevel@tonic-gate (((cflg) & CBAUD) + CBAUD + 1) : ((cflg) & CBAUD))
20180Sstevel@tonic-gate
20190Sstevel@tonic-gate static void
asy_program(struct asycom * asy,int mode)20200Sstevel@tonic-gate asy_program(struct asycom *asy, int mode)
20210Sstevel@tonic-gate {
20220Sstevel@tonic-gate struct asyncline *async;
20230Sstevel@tonic-gate int baudrate, c_flag;
20240Sstevel@tonic-gate int icr, lcr;
20250Sstevel@tonic-gate int flush_reg;
20260Sstevel@tonic-gate int ocflags;
20270Sstevel@tonic-gate #ifdef DEBUG
20280Sstevel@tonic-gate int instance;
20290Sstevel@tonic-gate #endif
20300Sstevel@tonic-gate
20310Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl));
20320Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi));
20330Sstevel@tonic-gate
20340Sstevel@tonic-gate async = asy->asy_priv;
20350Sstevel@tonic-gate #ifdef DEBUG
20360Sstevel@tonic-gate instance = UNIT(async->async_dev);
20370Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_PROCS,
20385295Srandyf "asy%d_program: mode = 0x%08X, enter\n", instance, mode);
20390Sstevel@tonic-gate #endif
20400Sstevel@tonic-gate
20410Sstevel@tonic-gate baudrate = BAUDINDEX(async->async_ttycommon.t_cflag);
20420Sstevel@tonic-gate
20430Sstevel@tonic-gate async->async_ttycommon.t_cflag &= ~(CIBAUD);
20440Sstevel@tonic-gate
20450Sstevel@tonic-gate if (baudrate > CBAUD) {
20460Sstevel@tonic-gate async->async_ttycommon.t_cflag |= CIBAUDEXT;
20470Sstevel@tonic-gate async->async_ttycommon.t_cflag |=
20485295Srandyf (((baudrate - CBAUD - 1) << IBSHIFT) & CIBAUD);
20490Sstevel@tonic-gate } else {
20500Sstevel@tonic-gate async->async_ttycommon.t_cflag &= ~CIBAUDEXT;
20510Sstevel@tonic-gate async->async_ttycommon.t_cflag |=
20525295Srandyf ((baudrate << IBSHIFT) & CIBAUD);
20530Sstevel@tonic-gate }
20540Sstevel@tonic-gate
20550Sstevel@tonic-gate c_flag = async->async_ttycommon.t_cflag &
20565295Srandyf (CLOCAL|CREAD|CSTOPB|CSIZE|PARENB|PARODD|CBAUD|CBAUDEXT);
20570Sstevel@tonic-gate
20580Sstevel@tonic-gate /* disable interrupts */
20591106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
20600Sstevel@tonic-gate
20610Sstevel@tonic-gate ocflags = asy->asy_ocflag;
20620Sstevel@tonic-gate
20630Sstevel@tonic-gate /* flush/reset the status registers */
20641106Smrj (void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR);
20651106Smrj (void) ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
20661106Smrj asy->asy_msr = flush_reg = ddi_get8(asy->asy_iohandle,
20675295Srandyf asy->asy_ioaddr + MSR);
20680Sstevel@tonic-gate /*
20690Sstevel@tonic-gate * The device is programmed in the open sequence, if we
20700Sstevel@tonic-gate * have to hardware handshake, then this is a good time
20710Sstevel@tonic-gate * to check if the device can receive any data.
20720Sstevel@tonic-gate */
20730Sstevel@tonic-gate
20740Sstevel@tonic-gate if ((CRTSCTS & async->async_ttycommon.t_cflag) && !(flush_reg & CTS)) {
20750Sstevel@tonic-gate async_flowcontrol_hw_output(asy, FLOW_STOP);
20760Sstevel@tonic-gate } else {
20770Sstevel@tonic-gate /*
20780Sstevel@tonic-gate * We can not use async_flowcontrol_hw_output(asy, FLOW_START)
20790Sstevel@tonic-gate * here, because if CRTSCTS is clear, we need clear
20800Sstevel@tonic-gate * ASYNC_HW_OUT_FLW bit.
20810Sstevel@tonic-gate */
20820Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_OUT_FLW;
20830Sstevel@tonic-gate }
20840Sstevel@tonic-gate
20850Sstevel@tonic-gate /*
20860Sstevel@tonic-gate * If IXON is not set, clear ASYNC_SW_OUT_FLW;
20870Sstevel@tonic-gate * If IXON is set, no matter what IXON flag is before this
20880Sstevel@tonic-gate * function call to asy_program,
20890Sstevel@tonic-gate * we will use the old ASYNC_SW_OUT_FLW status.
20900Sstevel@tonic-gate * Because of handling IXON in the driver, we also should re-calculate
20910Sstevel@tonic-gate * the value of ASYNC_OUT_FLW_RESUME bit, but in fact,
20920Sstevel@tonic-gate * the TCSET* commands which call asy_program
20930Sstevel@tonic-gate * are put into the write queue, so there is no output needed to
20940Sstevel@tonic-gate * be resumed at this point.
20950Sstevel@tonic-gate */
20960Sstevel@tonic-gate if (!(IXON & async->async_ttycommon.t_iflag))
20970Sstevel@tonic-gate async->async_flags &= ~ASYNC_SW_OUT_FLW;
20980Sstevel@tonic-gate
20990Sstevel@tonic-gate /* manually flush receive buffer or fifo (workaround for buggy fifos) */
21000Sstevel@tonic-gate if (mode == ASY_INIT)
21010Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) {
21020Sstevel@tonic-gate for (flush_reg = asy->asy_fifo_buf; flush_reg-- > 0; ) {
21031106Smrj (void) ddi_get8(asy->asy_iohandle,
21045295Srandyf asy->asy_ioaddr + DAT);
21050Sstevel@tonic-gate }
21060Sstevel@tonic-gate } else {
21071106Smrj flush_reg = ddi_get8(asy->asy_iohandle,
21085295Srandyf asy->asy_ioaddr + DAT);
21090Sstevel@tonic-gate }
21100Sstevel@tonic-gate
21110Sstevel@tonic-gate if (ocflags != (c_flag & ~CLOCAL) || mode == ASY_INIT) {
21120Sstevel@tonic-gate /* Set line control */
21135295Srandyf lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
21140Sstevel@tonic-gate lcr &= ~(WLS0|WLS1|STB|PEN|EPS);
21150Sstevel@tonic-gate
21160Sstevel@tonic-gate if (c_flag & CSTOPB)
21170Sstevel@tonic-gate lcr |= STB; /* 2 stop bits */
21180Sstevel@tonic-gate
21190Sstevel@tonic-gate if (c_flag & PARENB)
21200Sstevel@tonic-gate lcr |= PEN;
21210Sstevel@tonic-gate
21220Sstevel@tonic-gate if ((c_flag & PARODD) == 0)
21230Sstevel@tonic-gate lcr |= EPS;
21240Sstevel@tonic-gate
21250Sstevel@tonic-gate switch (c_flag & CSIZE) {
21260Sstevel@tonic-gate case CS5:
21270Sstevel@tonic-gate lcr |= BITS5;
21280Sstevel@tonic-gate break;
21290Sstevel@tonic-gate case CS6:
21300Sstevel@tonic-gate lcr |= BITS6;
21310Sstevel@tonic-gate break;
21320Sstevel@tonic-gate case CS7:
21330Sstevel@tonic-gate lcr |= BITS7;
21340Sstevel@tonic-gate break;
21350Sstevel@tonic-gate case CS8:
21360Sstevel@tonic-gate lcr |= BITS8;
21370Sstevel@tonic-gate break;
21380Sstevel@tonic-gate }
21390Sstevel@tonic-gate
21400Sstevel@tonic-gate /* set the baud rate, unless it is "0" */
21415295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, DLAB);
21425295Srandyf
21430Sstevel@tonic-gate if (baudrate != 0) {
21441106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
21455295Srandyf asyspdtab[baudrate] & 0xff);
21461106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR,
21475295Srandyf (asyspdtab[baudrate] >> 8) & 0xff);
21480Sstevel@tonic-gate }
21490Sstevel@tonic-gate /* set the line control modes */
21501106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, lcr);
21510Sstevel@tonic-gate
21520Sstevel@tonic-gate /*
21530Sstevel@tonic-gate * If we have a FIFO buffer, enable/flush
21540Sstevel@tonic-gate * at intialize time, flush if transitioning from
21550Sstevel@tonic-gate * CREAD off to CREAD on.
21560Sstevel@tonic-gate */
21570Sstevel@tonic-gate if ((ocflags & CREAD) == 0 && (c_flag & CREAD) ||
21580Sstevel@tonic-gate mode == ASY_INIT)
21590Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON)
21600Sstevel@tonic-gate asy_reset_fifo(asy, FIFORXFLSH);
21610Sstevel@tonic-gate
21620Sstevel@tonic-gate /* remember the new cflags */
21630Sstevel@tonic-gate asy->asy_ocflag = c_flag & ~CLOCAL;
21640Sstevel@tonic-gate }
21650Sstevel@tonic-gate
21660Sstevel@tonic-gate if (baudrate == 0)
21671106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
21685295Srandyf (asy->asy_mcr & RTS) | OUT2);
21690Sstevel@tonic-gate else
21701106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR,
21715295Srandyf asy->asy_mcr | OUT2);
21720Sstevel@tonic-gate
21730Sstevel@tonic-gate /*
21740Sstevel@tonic-gate * Call the modem status interrupt handler to check for the carrier
21750Sstevel@tonic-gate * in case CLOCAL was turned off after the carrier came on.
21760Sstevel@tonic-gate * (Note: Modem status interrupt is not enabled if CLOCAL is ON.)
21770Sstevel@tonic-gate */
21780Sstevel@tonic-gate async_msint(asy);
21790Sstevel@tonic-gate
21800Sstevel@tonic-gate /* Set interrupt control */
21810Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODM2,
21825295Srandyf "asy%d_program: c_flag & CLOCAL = %x t_cflag & CRTSCTS = %x\n",
21835295Srandyf instance, c_flag & CLOCAL,
21845295Srandyf async->async_ttycommon.t_cflag & CRTSCTS);
21855295Srandyf
21860Sstevel@tonic-gate if ((c_flag & CLOCAL) && !(async->async_ttycommon.t_cflag & CRTSCTS))
21870Sstevel@tonic-gate /*
21880Sstevel@tonic-gate * direct-wired line ignores DCD, so we don't enable modem
21890Sstevel@tonic-gate * status interrupts.
21900Sstevel@tonic-gate */
21910Sstevel@tonic-gate icr = (TIEN | SIEN);
21920Sstevel@tonic-gate else
21930Sstevel@tonic-gate icr = (TIEN | SIEN | MIEN);
21940Sstevel@tonic-gate
21950Sstevel@tonic-gate if (c_flag & CREAD)
21960Sstevel@tonic-gate icr |= RIEN;
21970Sstevel@tonic-gate
21981106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, icr);
21990Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "asy%d_program: done\n", instance);
22000Sstevel@tonic-gate }
22010Sstevel@tonic-gate
22020Sstevel@tonic-gate static boolean_t
asy_baudok(struct asycom * asy)22030Sstevel@tonic-gate asy_baudok(struct asycom *asy)
22040Sstevel@tonic-gate {
22050Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
22060Sstevel@tonic-gate int baudrate;
22070Sstevel@tonic-gate
22080Sstevel@tonic-gate
22090Sstevel@tonic-gate baudrate = BAUDINDEX(async->async_ttycommon.t_cflag);
22100Sstevel@tonic-gate
22110Sstevel@tonic-gate if (baudrate >= sizeof (asyspdtab)/sizeof (*asyspdtab))
22120Sstevel@tonic-gate return (0);
22130Sstevel@tonic-gate
22140Sstevel@tonic-gate return (baudrate == 0 || asyspdtab[baudrate]);
22150Sstevel@tonic-gate }
22160Sstevel@tonic-gate
22170Sstevel@tonic-gate /*
22180Sstevel@tonic-gate * asyintr() is the High Level Interrupt Handler.
22190Sstevel@tonic-gate *
22200Sstevel@tonic-gate * There are four different interrupt types indexed by ISR register values:
22210Sstevel@tonic-gate * 0: modem
22220Sstevel@tonic-gate * 1: Tx holding register is empty, ready for next char
22230Sstevel@tonic-gate * 2: Rx register now holds a char to be picked up
22240Sstevel@tonic-gate * 3: error or break on line
22250Sstevel@tonic-gate * This routine checks the Bit 0 (interrupt-not-pending) to determine if
22260Sstevel@tonic-gate * the interrupt is from this port.
22270Sstevel@tonic-gate */
22280Sstevel@tonic-gate uint_t
asyintr(caddr_t argasy)22290Sstevel@tonic-gate asyintr(caddr_t argasy)
22300Sstevel@tonic-gate {
22310Sstevel@tonic-gate struct asycom *asy = (struct asycom *)argasy;
22320Sstevel@tonic-gate struct asyncline *async;
22330Sstevel@tonic-gate int ret_status = DDI_INTR_UNCLAIMED;
22340Sstevel@tonic-gate uchar_t interrupt_id, lsr;
22350Sstevel@tonic-gate
22361106Smrj interrupt_id = ddi_get8(asy->asy_iohandle,
22375295Srandyf asy->asy_ioaddr + ISR) & 0x0F;
22380Sstevel@tonic-gate async = asy->asy_priv;
22395295Srandyf
224012401SZachary.Kissel@Sun.COM if ((async == NULL) ||
22415295Srandyf !(async->async_flags & (ASYNC_ISOPEN|ASYNC_WOPEN))) {
22420Sstevel@tonic-gate if (interrupt_id & NOINTERRUPT)
22430Sstevel@tonic-gate return (DDI_INTR_UNCLAIMED);
22440Sstevel@tonic-gate else {
22450Sstevel@tonic-gate /*
22460Sstevel@tonic-gate * reset the device by:
22470Sstevel@tonic-gate * reading line status
22480Sstevel@tonic-gate * reading any data from data status register
22490Sstevel@tonic-gate * reading modem status
22500Sstevel@tonic-gate */
22511106Smrj (void) ddi_get8(asy->asy_iohandle,
22525295Srandyf asy->asy_ioaddr + LSR);
22531106Smrj (void) ddi_get8(asy->asy_iohandle,
22545295Srandyf asy->asy_ioaddr + DAT);
22551106Smrj asy->asy_msr = ddi_get8(asy->asy_iohandle,
22565295Srandyf asy->asy_ioaddr + MSR);
22570Sstevel@tonic-gate return (DDI_INTR_CLAIMED);
22580Sstevel@tonic-gate }
22590Sstevel@tonic-gate }
22605295Srandyf
22610Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
2262*12876SZachary.Kissel@Sun.COM
2263*12876SZachary.Kissel@Sun.COM if (asy->asy_flags & ASY_DDI_SUSPENDED) {
2264*12876SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_excl_hi);
2265*12876SZachary.Kissel@Sun.COM return (DDI_INTR_CLAIMED);
2266*12876SZachary.Kissel@Sun.COM }
2267*12876SZachary.Kissel@Sun.COM
22680Sstevel@tonic-gate /*
22690Sstevel@tonic-gate * We will loop until the interrupt line is pulled low. asy
22700Sstevel@tonic-gate * interrupt is edge triggered.
22710Sstevel@tonic-gate */
22720Sstevel@tonic-gate /* CSTYLED */
22735295Srandyf for (;; interrupt_id =
22745295Srandyf (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR) & 0x0F)) {
22755295Srandyf
22760Sstevel@tonic-gate if (interrupt_id & NOINTERRUPT)
22770Sstevel@tonic-gate break;
22780Sstevel@tonic-gate ret_status = DDI_INTR_CLAIMED;
22790Sstevel@tonic-gate
22805295Srandyf DEBUGCONT1(ASY_DEBUG_INTR, "asyintr: interrupt_id = 0x%d\n",
22815295Srandyf interrupt_id);
22825295Srandyf lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
22830Sstevel@tonic-gate switch (interrupt_id) {
22840Sstevel@tonic-gate case RxRDY:
22850Sstevel@tonic-gate case RSTATUS:
22860Sstevel@tonic-gate case FFTMOUT:
22870Sstevel@tonic-gate /* receiver interrupt or receiver errors */
22880Sstevel@tonic-gate async_rxint(asy, lsr);
22890Sstevel@tonic-gate break;
22900Sstevel@tonic-gate case TxRDY:
22910Sstevel@tonic-gate /* transmit interrupt */
22920Sstevel@tonic-gate async_txint(asy);
22930Sstevel@tonic-gate continue;
22940Sstevel@tonic-gate case MSTATUS:
22950Sstevel@tonic-gate /* modem status interrupt */
22960Sstevel@tonic-gate async_msint(asy);
22970Sstevel@tonic-gate break;
22980Sstevel@tonic-gate }
22990Sstevel@tonic-gate if ((lsr & XHRE) && (async->async_flags & ASYNC_BUSY) &&
23000Sstevel@tonic-gate (async->async_ocnt > 0))
23010Sstevel@tonic-gate async_txint(asy);
23020Sstevel@tonic-gate }
23030Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
23040Sstevel@tonic-gate return (ret_status);
23050Sstevel@tonic-gate }
23060Sstevel@tonic-gate
23070Sstevel@tonic-gate /*
23080Sstevel@tonic-gate * Transmitter interrupt service routine.
23090Sstevel@tonic-gate * If there is more data to transmit in the current pseudo-DMA block,
23100Sstevel@tonic-gate * send the next character if output is not stopped or draining.
23110Sstevel@tonic-gate * Otherwise, queue up a soft interrupt.
23120Sstevel@tonic-gate *
23130Sstevel@tonic-gate * XXX - Needs review for HW FIFOs.
23140Sstevel@tonic-gate */
23150Sstevel@tonic-gate static void
async_txint(struct asycom * asy)23160Sstevel@tonic-gate async_txint(struct asycom *asy)
23170Sstevel@tonic-gate {
23180Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
23190Sstevel@tonic-gate int fifo_len;
23200Sstevel@tonic-gate
23210Sstevel@tonic-gate /*
23220Sstevel@tonic-gate * If ASYNC_BREAK or ASYNC_OUT_SUSPEND has been set, return to
23230Sstevel@tonic-gate * asyintr()'s context to claim the interrupt without performing
23240Sstevel@tonic-gate * any action. No character will be loaded into FIFO/THR until
23250Sstevel@tonic-gate * timed or untimed break is removed
23260Sstevel@tonic-gate */
23270Sstevel@tonic-gate if (async->async_flags & (ASYNC_BREAK|ASYNC_OUT_SUSPEND))
23280Sstevel@tonic-gate return;
23290Sstevel@tonic-gate
23300Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
23310Sstevel@tonic-gate if (fifo_len > asy_max_tx_fifo)
23320Sstevel@tonic-gate fifo_len = asy_max_tx_fifo;
23330Sstevel@tonic-gate
23340Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL))
23350Sstevel@tonic-gate fifo_len--;
23360Sstevel@tonic-gate
23370Sstevel@tonic-gate if (async->async_ocnt > 0 && fifo_len > 0 &&
23380Sstevel@tonic-gate !(async->async_flags &
23390Sstevel@tonic-gate (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_STOPPED))) {
23400Sstevel@tonic-gate while (fifo_len-- > 0 && async->async_ocnt-- > 0) {
23411106Smrj ddi_put8(asy->asy_iohandle,
23420Sstevel@tonic-gate asy->asy_ioaddr + DAT, *async->async_optr++);
23430Sstevel@tonic-gate }
23440Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS;
23450Sstevel@tonic-gate }
23460Sstevel@tonic-gate
23470Sstevel@tonic-gate if (fifo_len <= 0)
23480Sstevel@tonic-gate return;
23490Sstevel@tonic-gate
23500Sstevel@tonic-gate ASYSETSOFT(asy);
23510Sstevel@tonic-gate }
23520Sstevel@tonic-gate
23530Sstevel@tonic-gate /*
23540Sstevel@tonic-gate * Interrupt on port: handle PPS event. This function is only called
23550Sstevel@tonic-gate * for a port on which PPS event handling has been enabled.
23560Sstevel@tonic-gate */
23570Sstevel@tonic-gate static void
asy_ppsevent(struct asycom * asy,int msr)23580Sstevel@tonic-gate asy_ppsevent(struct asycom *asy, int msr)
23590Sstevel@tonic-gate {
23600Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS_EDGE) {
23610Sstevel@tonic-gate /* Have seen leading edge, now look for and record drop */
23620Sstevel@tonic-gate if ((msr & DCD) == 0)
23630Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE;
23640Sstevel@tonic-gate /*
23650Sstevel@tonic-gate * Waiting for leading edge, look for rise; stamp event and
23660Sstevel@tonic-gate * calibrate kernel clock.
23670Sstevel@tonic-gate */
23680Sstevel@tonic-gate } else if (msr & DCD) {
23690Sstevel@tonic-gate /*
23700Sstevel@tonic-gate * This code captures a timestamp at the designated
23710Sstevel@tonic-gate * transition of the PPS signal (DCD asserted). The
23720Sstevel@tonic-gate * code provides a pointer to the timestamp, as well
23730Sstevel@tonic-gate * as the hardware counter value at the capture.
23740Sstevel@tonic-gate *
23750Sstevel@tonic-gate * Note: the kernel has nano based time values while
23760Sstevel@tonic-gate * NTP requires micro based, an in-line fast algorithm
23770Sstevel@tonic-gate * to convert nsec to usec is used here -- see hrt2ts()
23780Sstevel@tonic-gate * in common/os/timers.c for a full description.
23790Sstevel@tonic-gate */
23800Sstevel@tonic-gate struct timeval *tvp = &asy_ppsev.tv;
23810Sstevel@tonic-gate timestruc_t ts;
23820Sstevel@tonic-gate long nsec, usec;
23830Sstevel@tonic-gate
23840Sstevel@tonic-gate asy->asy_flags |= ASY_PPS_EDGE;
23850Sstevel@tonic-gate LED_OFF;
23860Sstevel@tonic-gate gethrestime(&ts);
23870Sstevel@tonic-gate LED_ON;
23880Sstevel@tonic-gate nsec = ts.tv_nsec;
23890Sstevel@tonic-gate usec = nsec + (nsec >> 2);
23900Sstevel@tonic-gate usec = nsec + (usec >> 1);
23910Sstevel@tonic-gate usec = nsec + (usec >> 2);
23920Sstevel@tonic-gate usec = nsec + (usec >> 4);
23930Sstevel@tonic-gate usec = nsec - (usec >> 3);
23940Sstevel@tonic-gate usec = nsec + (usec >> 2);
23950Sstevel@tonic-gate usec = nsec + (usec >> 3);
23960Sstevel@tonic-gate usec = nsec + (usec >> 4);
23970Sstevel@tonic-gate usec = nsec + (usec >> 1);
23980Sstevel@tonic-gate usec = nsec + (usec >> 6);
23990Sstevel@tonic-gate tvp->tv_usec = usec >> 10;
24000Sstevel@tonic-gate tvp->tv_sec = ts.tv_sec;
24010Sstevel@tonic-gate
24020Sstevel@tonic-gate ++asy_ppsev.serial;
24030Sstevel@tonic-gate
24040Sstevel@tonic-gate /*
24050Sstevel@tonic-gate * Because the kernel keeps a high-resolution time,
24060Sstevel@tonic-gate * pass the current highres timestamp in tvp and zero
24070Sstevel@tonic-gate * in usec.
24080Sstevel@tonic-gate */
24090Sstevel@tonic-gate ddi_hardpps(tvp, 0);
24100Sstevel@tonic-gate }
24110Sstevel@tonic-gate }
24120Sstevel@tonic-gate
24130Sstevel@tonic-gate /*
24140Sstevel@tonic-gate * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive
24150Sstevel@tonic-gate * error interrupt.
24160Sstevel@tonic-gate * Try to put the character into the circular buffer for this line; if it
24170Sstevel@tonic-gate * overflows, indicate a circular buffer overrun. If this port is always
24180Sstevel@tonic-gate * to be serviced immediately, or the character is a STOP character, or
24190Sstevel@tonic-gate * more than 15 characters have arrived, queue up a soft interrupt to
24200Sstevel@tonic-gate * drain the circular buffer.
24210Sstevel@tonic-gate * XXX - needs review for hw FIFOs support.
24220Sstevel@tonic-gate */
24230Sstevel@tonic-gate
24240Sstevel@tonic-gate static void
async_rxint(struct asycom * asy,uchar_t lsr)24250Sstevel@tonic-gate async_rxint(struct asycom *asy, uchar_t lsr)
24260Sstevel@tonic-gate {
24270Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
24280Sstevel@tonic-gate uchar_t c;
24290Sstevel@tonic-gate uint_t s, needsoft = 0;
24300Sstevel@tonic-gate tty_common_t *tp;
24310Sstevel@tonic-gate int looplim = asy->asy_fifo_buf * 2;
24320Sstevel@tonic-gate
24330Sstevel@tonic-gate tp = &async->async_ttycommon;
24340Sstevel@tonic-gate if (!(tp->t_cflag & CREAD)) {
24350Sstevel@tonic-gate while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
24361106Smrj (void) (ddi_get8(asy->asy_iohandle,
24375295Srandyf asy->asy_ioaddr + DAT) & 0xff);
24381106Smrj lsr = ddi_get8(asy->asy_iohandle,
24395295Srandyf asy->asy_ioaddr + LSR);
24400Sstevel@tonic-gate if (looplim-- < 0) /* limit loop */
24410Sstevel@tonic-gate break;
24420Sstevel@tonic-gate }
24430Sstevel@tonic-gate return; /* line is not open for read? */
24440Sstevel@tonic-gate }
24450Sstevel@tonic-gate
24460Sstevel@tonic-gate while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) {
24470Sstevel@tonic-gate c = 0;
24480Sstevel@tonic-gate s = 0; /* reset error status */
24490Sstevel@tonic-gate if (lsr & RCA) {
24501106Smrj c = ddi_get8(asy->asy_iohandle,
24515295Srandyf asy->asy_ioaddr + DAT) & 0xff;
24520Sstevel@tonic-gate
24530Sstevel@tonic-gate /*
24540Sstevel@tonic-gate * We handle XON/XOFF char if IXON is set,
24550Sstevel@tonic-gate * but if received char is _POSIX_VDISABLE,
24560Sstevel@tonic-gate * we left it to the up level module.
24570Sstevel@tonic-gate */
24580Sstevel@tonic-gate if (tp->t_iflag & IXON) {
24590Sstevel@tonic-gate if ((c == async->async_stopc) &&
24600Sstevel@tonic-gate (c != _POSIX_VDISABLE)) {
24610Sstevel@tonic-gate async_flowcontrol_sw_output(asy,
24620Sstevel@tonic-gate FLOW_STOP);
24630Sstevel@tonic-gate goto check_looplim;
24640Sstevel@tonic-gate } else if ((c == async->async_startc) &&
24650Sstevel@tonic-gate (c != _POSIX_VDISABLE)) {
24660Sstevel@tonic-gate async_flowcontrol_sw_output(asy,
24670Sstevel@tonic-gate FLOW_START);
24680Sstevel@tonic-gate needsoft = 1;
24690Sstevel@tonic-gate goto check_looplim;
24700Sstevel@tonic-gate }
24710Sstevel@tonic-gate if ((tp->t_iflag & IXANY) &&
24720Sstevel@tonic-gate (async->async_flags & ASYNC_SW_OUT_FLW)) {
24730Sstevel@tonic-gate async_flowcontrol_sw_output(asy,
24740Sstevel@tonic-gate FLOW_START);
24750Sstevel@tonic-gate needsoft = 1;
24760Sstevel@tonic-gate }
24770Sstevel@tonic-gate }
24780Sstevel@tonic-gate }
24790Sstevel@tonic-gate
24800Sstevel@tonic-gate /*
24810Sstevel@tonic-gate * Check for character break sequence
24820Sstevel@tonic-gate */
24830Sstevel@tonic-gate if ((abort_enable == KIOCABORTALTERNATE) &&
24840Sstevel@tonic-gate (asy->asy_flags & ASY_CONSOLE)) {
24850Sstevel@tonic-gate if (abort_charseq_recognize(c))
24860Sstevel@tonic-gate abort_sequence_enter((char *)NULL);
24870Sstevel@tonic-gate }
24880Sstevel@tonic-gate
24890Sstevel@tonic-gate /* Handle framing errors */
24900Sstevel@tonic-gate if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) {
24910Sstevel@tonic-gate if (lsr & PARERR) {
24920Sstevel@tonic-gate if (tp->t_iflag & INPCK) /* parity enabled */
24930Sstevel@tonic-gate s |= PERROR;
24940Sstevel@tonic-gate }
24950Sstevel@tonic-gate
24960Sstevel@tonic-gate if (lsr & (FRMERR|BRKDET))
24970Sstevel@tonic-gate s |= FRERROR;
24980Sstevel@tonic-gate if (lsr & OVRRUN) {
24990Sstevel@tonic-gate async->async_hw_overrun = 1;
25000Sstevel@tonic-gate s |= OVERRUN;
25010Sstevel@tonic-gate }
25020Sstevel@tonic-gate }
25030Sstevel@tonic-gate
25040Sstevel@tonic-gate if (s == 0)
25050Sstevel@tonic-gate if ((tp->t_iflag & PARMRK) &&
25060Sstevel@tonic-gate !(tp->t_iflag & (IGNPAR|ISTRIP)) &&
25070Sstevel@tonic-gate (c == 0377))
25080Sstevel@tonic-gate if (RING_POK(async, 2)) {
25090Sstevel@tonic-gate RING_PUT(async, 0377);
25100Sstevel@tonic-gate RING_PUT(async, c);
25110Sstevel@tonic-gate } else
25120Sstevel@tonic-gate async->async_sw_overrun = 1;
25130Sstevel@tonic-gate else
25140Sstevel@tonic-gate if (RING_POK(async, 1))
25150Sstevel@tonic-gate RING_PUT(async, c);
25160Sstevel@tonic-gate else
25170Sstevel@tonic-gate async->async_sw_overrun = 1;
25180Sstevel@tonic-gate else
25190Sstevel@tonic-gate if (s & FRERROR) /* Handle framing errors */
25200Sstevel@tonic-gate if (c == 0)
25210Sstevel@tonic-gate if ((asy->asy_flags & ASY_CONSOLE) &&
25220Sstevel@tonic-gate (abort_enable !=
25230Sstevel@tonic-gate KIOCABORTALTERNATE))
25240Sstevel@tonic-gate abort_sequence_enter((char *)0);
25250Sstevel@tonic-gate else
25260Sstevel@tonic-gate async->async_break++;
25270Sstevel@tonic-gate else
25280Sstevel@tonic-gate if (RING_POK(async, 1))
25290Sstevel@tonic-gate RING_MARK(async, c, s);
25300Sstevel@tonic-gate else
25310Sstevel@tonic-gate async->async_sw_overrun = 1;
25320Sstevel@tonic-gate else /* Parity errors are handled by ldterm */
25330Sstevel@tonic-gate if (RING_POK(async, 1))
25340Sstevel@tonic-gate RING_MARK(async, c, s);
25350Sstevel@tonic-gate else
25360Sstevel@tonic-gate async->async_sw_overrun = 1;
25370Sstevel@tonic-gate check_looplim:
25385295Srandyf lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR);
25390Sstevel@tonic-gate if (looplim-- < 0) /* limit loop */
25400Sstevel@tonic-gate break;
25410Sstevel@tonic-gate }
25420Sstevel@tonic-gate if ((RING_CNT(async) > (RINGSIZE * 3)/4) &&
25430Sstevel@tonic-gate !(async->async_inflow_source & IN_FLOW_RINGBUFF)) {
25440Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP, IN_FLOW_RINGBUFF);
25450Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_STOP,
25460Sstevel@tonic-gate IN_FLOW_RINGBUFF);
25470Sstevel@tonic-gate }
25480Sstevel@tonic-gate
25490Sstevel@tonic-gate if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft ||
25500Sstevel@tonic-gate (RING_FRAC(async)) || (async->async_polltid == 0))
25510Sstevel@tonic-gate ASYSETSOFT(asy); /* need a soft interrupt */
25520Sstevel@tonic-gate }
25530Sstevel@tonic-gate
25540Sstevel@tonic-gate /*
25550Sstevel@tonic-gate * Modem status interrupt.
25560Sstevel@tonic-gate *
25570Sstevel@tonic-gate * (Note: It is assumed that the MSR hasn't been read by asyintr().)
25580Sstevel@tonic-gate */
25590Sstevel@tonic-gate
25600Sstevel@tonic-gate static void
async_msint(struct asycom * asy)25610Sstevel@tonic-gate async_msint(struct asycom *asy)
25620Sstevel@tonic-gate {
25630Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
25640Sstevel@tonic-gate int msr, t_cflag = async->async_ttycommon.t_cflag;
25650Sstevel@tonic-gate #ifdef DEBUG
25660Sstevel@tonic-gate int instance = UNIT(async->async_dev);
25670Sstevel@tonic-gate #endif
25680Sstevel@tonic-gate
25690Sstevel@tonic-gate async_msint_retry:
25700Sstevel@tonic-gate /* this resets the interrupt */
25711106Smrj msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
25720Sstevel@tonic-gate DEBUGCONT10(ASY_DEBUG_STATE,
25735295Srandyf "async%d_msint call #%d:\n"
25745295Srandyf " transition: %3s %3s %3s %3s\n"
25755295Srandyf "current state: %3s %3s %3s %3s\n",
25765295Srandyf instance,
25775295Srandyf ++(asy->asy_msint_cnt),
25785295Srandyf (msr & DCTS) ? "DCTS" : " ",
25795295Srandyf (msr & DDSR) ? "DDSR" : " ",
25805295Srandyf (msr & DRI) ? "DRI " : " ",
25815295Srandyf (msr & DDCD) ? "DDCD" : " ",
25825295Srandyf (msr & CTS) ? "CTS " : " ",
25835295Srandyf (msr & DSR) ? "DSR " : " ",
25845295Srandyf (msr & RI) ? "RI " : " ",
25855295Srandyf (msr & DCD) ? "DCD " : " ");
25860Sstevel@tonic-gate
25870Sstevel@tonic-gate /* If CTS status is changed, do H/W output flow control */
25880Sstevel@tonic-gate if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & CTS) != 0))
25890Sstevel@tonic-gate async_flowcontrol_hw_output(asy,
25900Sstevel@tonic-gate msr & CTS ? FLOW_START : FLOW_STOP);
25910Sstevel@tonic-gate /*
25920Sstevel@tonic-gate * Reading MSR resets the interrupt, we save the
25930Sstevel@tonic-gate * value of msr so that other functions could examine MSR by
25940Sstevel@tonic-gate * looking at asy_msr.
25950Sstevel@tonic-gate */
25960Sstevel@tonic-gate asy->asy_msr = (uchar_t)msr;
25970Sstevel@tonic-gate
25980Sstevel@tonic-gate /* Handle PPS event */
25990Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS)
26000Sstevel@tonic-gate asy_ppsevent(asy, msr);
26010Sstevel@tonic-gate
26020Sstevel@tonic-gate async->async_ext++;
26030Sstevel@tonic-gate ASYSETSOFT(asy);
26040Sstevel@tonic-gate /*
26050Sstevel@tonic-gate * We will make sure that the modem status presented to us
26060Sstevel@tonic-gate * during the previous read has not changed. If the chip samples
26070Sstevel@tonic-gate * the modem status on the falling edge of the interrupt line,
26080Sstevel@tonic-gate * and uses this state as the base for detecting change of modem
26090Sstevel@tonic-gate * status, we would miss a change of modem status event that occured
26100Sstevel@tonic-gate * after we initiated a read MSR operation.
26110Sstevel@tonic-gate */
26121106Smrj msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR);
26130Sstevel@tonic-gate if (STATES(msr) != STATES(asy->asy_msr))
26140Sstevel@tonic-gate goto async_msint_retry;
26150Sstevel@tonic-gate }
26160Sstevel@tonic-gate
26170Sstevel@tonic-gate /*
26180Sstevel@tonic-gate * Handle a second-stage interrupt.
26190Sstevel@tonic-gate */
26200Sstevel@tonic-gate /*ARGSUSED*/
26210Sstevel@tonic-gate uint_t
asysoftintr(caddr_t intarg)26220Sstevel@tonic-gate asysoftintr(caddr_t intarg)
26230Sstevel@tonic-gate {
262412401SZachary.Kissel@Sun.COM struct asycom *asy = (struct asycom *)intarg;
262512401SZachary.Kissel@Sun.COM struct asyncline *async;
26260Sstevel@tonic-gate int rv;
262712401SZachary.Kissel@Sun.COM uint_t cc;
26280Sstevel@tonic-gate
26290Sstevel@tonic-gate /*
26300Sstevel@tonic-gate * Test and clear soft interrupt.
26310Sstevel@tonic-gate */
263212401SZachary.Kissel@Sun.COM mutex_enter(&asy->asy_soft_lock);
26330Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_PROCS, "asysoftintr: enter\n");
263412401SZachary.Kissel@Sun.COM rv = asy->asysoftpend;
26350Sstevel@tonic-gate if (rv != 0)
263612401SZachary.Kissel@Sun.COM asy->asysoftpend = 0;
263712401SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_soft_lock);
26380Sstevel@tonic-gate
26390Sstevel@tonic-gate if (rv) {
264012401SZachary.Kissel@Sun.COM if (asy->asy_priv == NULL)
264112401SZachary.Kissel@Sun.COM return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
264212401SZachary.Kissel@Sun.COM async = (struct asyncline *)asy->asy_priv;
264312401SZachary.Kissel@Sun.COM mutex_enter(&asy->asy_excl_hi);
264412401SZachary.Kissel@Sun.COM if (asy->asy_flags & ASY_NEEDSOFT) {
264512401SZachary.Kissel@Sun.COM asy->asy_flags &= ~ASY_NEEDSOFT;
264612401SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_excl_hi);
264712401SZachary.Kissel@Sun.COM async_softint(asy);
264812401SZachary.Kissel@Sun.COM mutex_enter(&asy->asy_excl_hi);
264912401SZachary.Kissel@Sun.COM }
265012401SZachary.Kissel@Sun.COM
26510Sstevel@tonic-gate /*
265212401SZachary.Kissel@Sun.COM * There are some instances where the softintr is not
265312401SZachary.Kissel@Sun.COM * scheduled and hence not called. It so happens that
265412401SZachary.Kissel@Sun.COM * causes the last few characters to be stuck in the
265512401SZachary.Kissel@Sun.COM * ringbuffer. Hence, call the handler once again so
265612401SZachary.Kissel@Sun.COM * the last few characters are cleared.
26570Sstevel@tonic-gate */
265812401SZachary.Kissel@Sun.COM cc = RING_CNT(async);
265912401SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_excl_hi);
266012401SZachary.Kissel@Sun.COM if (cc > 0)
266112401SZachary.Kissel@Sun.COM (void) async_softint(asy);
26620Sstevel@tonic-gate }
26630Sstevel@tonic-gate return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED);
26640Sstevel@tonic-gate }
26650Sstevel@tonic-gate
26660Sstevel@tonic-gate /*
26670Sstevel@tonic-gate * Handle a software interrupt.
26680Sstevel@tonic-gate */
26690Sstevel@tonic-gate static void
async_softint(struct asycom * asy)26700Sstevel@tonic-gate async_softint(struct asycom *asy)
26710Sstevel@tonic-gate {
26720Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
267312401SZachary.Kissel@Sun.COM uint_t cc;
26740Sstevel@tonic-gate mblk_t *bp;
26750Sstevel@tonic-gate queue_t *q;
26760Sstevel@tonic-gate uchar_t val;
26770Sstevel@tonic-gate uchar_t c;
26780Sstevel@tonic-gate tty_common_t *tp;
26790Sstevel@tonic-gate int nb;
26800Sstevel@tonic-gate int instance = UNIT(async->async_dev);
26810Sstevel@tonic-gate
26820Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint\n", instance);
268312401SZachary.Kissel@Sun.COM mutex_enter(&asy->asy_excl_hi);
26840Sstevel@tonic-gate if (asy->asy_flags & ASY_DOINGSOFT) {
26850Sstevel@tonic-gate asy->asy_flags |= ASY_DOINGSOFT_RETRY;
268612401SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_excl_hi);
26870Sstevel@tonic-gate return;
26880Sstevel@tonic-gate }
26890Sstevel@tonic-gate asy->asy_flags |= ASY_DOINGSOFT;
26900Sstevel@tonic-gate begin:
26910Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT_RETRY;
269212401SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_excl_hi);
26930Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
26940Sstevel@tonic-gate tp = &async->async_ttycommon;
26950Sstevel@tonic-gate q = tp->t_readq;
26960Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_FLW_RESUME) {
26970Sstevel@tonic-gate if (async->async_ocnt > 0) {
26980Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
26990Sstevel@tonic-gate async_resume(async);
27000Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
27010Sstevel@tonic-gate } else {
27020Sstevel@tonic-gate if (async->async_xmitblk)
27030Sstevel@tonic-gate freeb(async->async_xmitblk);
27040Sstevel@tonic-gate async->async_xmitblk = NULL;
27050Sstevel@tonic-gate async_start(async);
27060Sstevel@tonic-gate }
27070Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
27080Sstevel@tonic-gate }
27090Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
27100Sstevel@tonic-gate if (async->async_ext) {
27110Sstevel@tonic-gate async->async_ext = 0;
27120Sstevel@tonic-gate /* check for carrier up */
27130Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODM2,
27145295Srandyf "async%d_softint: asy_msr & DCD = %x, "
27155295Srandyf "tp->t_flags & TS_SOFTCAR = %x\n",
27165295Srandyf instance, asy->asy_msr & DCD, tp->t_flags & TS_SOFTCAR);
27175295Srandyf
27180Sstevel@tonic-gate if (asy->asy_msr & DCD) {
27190Sstevel@tonic-gate /* carrier present */
27200Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) == 0) {
27210Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODM2,
27225295Srandyf "async%d_softint: set ASYNC_CARR_ON\n",
27235295Srandyf instance);
27240Sstevel@tonic-gate async->async_flags |= ASYNC_CARR_ON;
27250Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
27260Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
27270Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
27280Sstevel@tonic-gate (void) putctl(q, M_UNHANGUP);
27290Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
27300Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
27310Sstevel@tonic-gate }
27320Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
27330Sstevel@tonic-gate }
27340Sstevel@tonic-gate } else {
27350Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) &&
27360Sstevel@tonic-gate !(tp->t_cflag & CLOCAL) &&
27370Sstevel@tonic-gate !(tp->t_flags & TS_SOFTCAR)) {
27380Sstevel@tonic-gate int flushflag;
27390Sstevel@tonic-gate
27400Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM,
27415295Srandyf "async%d_softint: carrier dropped, "
27425295Srandyf "so drop DTR\n",
27435295Srandyf instance);
27440Sstevel@tonic-gate /*
27450Sstevel@tonic-gate * Carrier went away.
27460Sstevel@tonic-gate * Drop DTR, abort any output in
27470Sstevel@tonic-gate * progress, indicate that output is
27480Sstevel@tonic-gate * not stopped, and send a hangup
27490Sstevel@tonic-gate * notification upstream.
27500Sstevel@tonic-gate */
27511106Smrj val = ddi_get8(asy->asy_iohandle,
27525295Srandyf asy->asy_ioaddr + MCR);
27531106Smrj ddi_put8(asy->asy_iohandle,
27540Sstevel@tonic-gate asy->asy_ioaddr + MCR, (val & ~DTR));
27555295Srandyf
27560Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) {
27575295Srandyf DEBUGCONT0(ASY_DEBUG_BUSY,
27580Sstevel@tonic-gate "async_softint: "
27590Sstevel@tonic-gate "Carrier dropped. "
27600Sstevel@tonic-gate "Clearing async_ocnt\n");
27615295Srandyf async->async_ocnt = 0;
27620Sstevel@tonic-gate } /* if */
27630Sstevel@tonic-gate
27640Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED;
27650Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
27665295Srandyf mutex_exit(&asy->asy_excl_hi);
27675295Srandyf mutex_exit(&asy->asy_excl);
27685295Srandyf (void) putctl(q, M_HANGUP);
27695295Srandyf mutex_enter(&asy->asy_excl);
27705295Srandyf DEBUGCONT1(ASY_DEBUG_MODEM,
27715295Srandyf "async%d_softint: "
27725295Srandyf "putctl(q, M_HANGUP)\n",
27735295Srandyf instance);
27745295Srandyf /*
27755295Srandyf * Flush FIFO buffers
27765295Srandyf * Any data left in there is invalid now
27775295Srandyf */
27785295Srandyf if (asy->asy_use_fifo == FIFO_ON)
27795295Srandyf asy_reset_fifo(asy, FIFOTXFLSH);
27805295Srandyf /*
27815295Srandyf * Flush our write queue if we have one.
27825295Srandyf * If we're in the midst of close, then
27835295Srandyf * flush everything. Don't leave stale
27845295Srandyf * ioctls lying about.
27855295Srandyf */
27865295Srandyf flushflag = (async->async_flags &
27875295Srandyf ASYNC_CLOSING) ? FLUSHALL :
27885295Srandyf FLUSHDATA;
27895295Srandyf flushq(tp->t_writeq, flushflag);
27905295Srandyf
27915295Srandyf /* active msg */
27925295Srandyf bp = async->async_xmitblk;
27935295Srandyf if (bp != NULL) {
27945295Srandyf freeb(bp);
27955295Srandyf async->async_xmitblk = NULL;
27965295Srandyf }
27975295Srandyf
27985295Srandyf mutex_enter(&asy->asy_excl_hi);
27995295Srandyf async->async_flags &= ~ASYNC_BUSY;
28005295Srandyf /*
28015295Srandyf * This message warns of Carrier loss
28025295Srandyf * with data left to transmit can hang
28035295Srandyf * the system.
28045295Srandyf */
28055295Srandyf DEBUGCONT0(ASY_DEBUG_MODEM,
28065295Srandyf "async_softint: Flushing to "
28075295Srandyf "prevent HUPCL hanging\n");
28080Sstevel@tonic-gate } /* if (ASYNC_ISOPEN) */
28090Sstevel@tonic-gate } /* if (ASYNC_CARR_ON && CLOCAL) */
28100Sstevel@tonic-gate async->async_flags &= ~ASYNC_CARR_ON;
28110Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
28120Sstevel@tonic-gate } /* else */
28130Sstevel@tonic-gate } /* if (async->async_ext) */
28140Sstevel@tonic-gate
28150Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
28160Sstevel@tonic-gate
28170Sstevel@tonic-gate /*
28180Sstevel@tonic-gate * If data has been added to the circular buffer, remove
28190Sstevel@tonic-gate * it from the buffer, and send it up the stream if there's
28200Sstevel@tonic-gate * somebody listening. Try to do it 16 bytes at a time. If we
28210Sstevel@tonic-gate * have more than 16 bytes to move, move 16 byte chunks and
28220Sstevel@tonic-gate * leave the rest for next time around (maybe it will grow).
28230Sstevel@tonic-gate */
28240Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
28250Sstevel@tonic-gate if (!(async->async_flags & ASYNC_ISOPEN)) {
28260Sstevel@tonic-gate RING_INIT(async);
28270Sstevel@tonic-gate goto rv;
28280Sstevel@tonic-gate }
282912401SZachary.Kissel@Sun.COM if ((cc = RING_CNT(async)) == 0)
28300Sstevel@tonic-gate goto rv;
28310Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
28320Sstevel@tonic-gate
28330Sstevel@tonic-gate if (!canput(q)) {
28340Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
28350Sstevel@tonic-gate if (!(async->async_inflow_source & IN_FLOW_STREAMS)) {
28360Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP,
28370Sstevel@tonic-gate IN_FLOW_STREAMS);
28380Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_STOP,
28390Sstevel@tonic-gate IN_FLOW_STREAMS);
28400Sstevel@tonic-gate }
28410Sstevel@tonic-gate goto rv;
28420Sstevel@tonic-gate }
28430Sstevel@tonic-gate if (async->async_inflow_source & IN_FLOW_STREAMS) {
28440Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
28450Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START,
28460Sstevel@tonic-gate IN_FLOW_STREAMS);
28470Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_START,
28480Sstevel@tonic-gate IN_FLOW_STREAMS);
28490Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
28500Sstevel@tonic-gate }
28515295Srandyf
28525295Srandyf DEBUGCONT2(ASY_DEBUG_INPUT, "async%d_softint: %d char(s) in queue.\n",
28535295Srandyf instance, cc);
28545295Srandyf
28550Sstevel@tonic-gate if (!(bp = allocb(cc, BPRI_MED))) {
28560Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
28570Sstevel@tonic-gate ttycommon_qfull(&async->async_ttycommon, q);
28580Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
28590Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
28600Sstevel@tonic-gate goto rv;
28610Sstevel@tonic-gate }
28620Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
28630Sstevel@tonic-gate do {
28640Sstevel@tonic-gate if (RING_ERR(async, S_ERRORS)) {
28650Sstevel@tonic-gate RING_UNMARK(async);
28660Sstevel@tonic-gate c = RING_GET(async);
28670Sstevel@tonic-gate break;
28680Sstevel@tonic-gate } else
28690Sstevel@tonic-gate *bp->b_wptr++ = RING_GET(async);
28700Sstevel@tonic-gate } while (--cc);
28710Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
28720Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
28730Sstevel@tonic-gate if (bp->b_wptr > bp->b_rptr) {
28740Sstevel@tonic-gate if (!canput(q)) {
28750Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: local queue full",
28765295Srandyf instance);
28770Sstevel@tonic-gate freemsg(bp);
28780Sstevel@tonic-gate } else
28790Sstevel@tonic-gate (void) putq(q, bp);
28800Sstevel@tonic-gate } else
28810Sstevel@tonic-gate freemsg(bp);
28820Sstevel@tonic-gate /*
28830Sstevel@tonic-gate * If we have a parity error, then send
28840Sstevel@tonic-gate * up an M_BREAK with the "bad"
28850Sstevel@tonic-gate * character as an argument. Let ldterm
28860Sstevel@tonic-gate * figure out what to do with the error.
28870Sstevel@tonic-gate */
28880Sstevel@tonic-gate if (cc) {
28890Sstevel@tonic-gate (void) putctl1(q, M_BREAK, c);
28900Sstevel@tonic-gate ASYSETSOFT(async->async_common); /* finish cc chars */
28910Sstevel@tonic-gate }
28920Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
28930Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
28940Sstevel@tonic-gate rv:
28950Sstevel@tonic-gate if ((RING_CNT(async) < (RINGSIZE/4)) &&
28960Sstevel@tonic-gate (async->async_inflow_source & IN_FLOW_RINGBUFF)) {
28970Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START, IN_FLOW_RINGBUFF);
28980Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_START,
28990Sstevel@tonic-gate IN_FLOW_RINGBUFF);
29000Sstevel@tonic-gate }
29010Sstevel@tonic-gate
29020Sstevel@tonic-gate /*
29030Sstevel@tonic-gate * If a transmission has finished, indicate that it's finished,
29040Sstevel@tonic-gate * and start that line up again.
29050Sstevel@tonic-gate */
29060Sstevel@tonic-gate if (async->async_break > 0) {
29070Sstevel@tonic-gate nb = async->async_break;
29080Sstevel@tonic-gate async->async_break = 0;
29090Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
29100Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
29110Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
29120Sstevel@tonic-gate for (; nb > 0; nb--)
29130Sstevel@tonic-gate (void) putctl(q, M_BREAK);
29140Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
29150Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
29160Sstevel@tonic-gate }
29170Sstevel@tonic-gate }
29180Sstevel@tonic-gate if (async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) {
29190Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_BUSY,
29200Sstevel@tonic-gate "async%d_softint: Clearing ASYNC_BUSY. async_ocnt=%d\n",
29210Sstevel@tonic-gate instance,
29220Sstevel@tonic-gate async->async_ocnt);
29230Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY;
29240Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
29250Sstevel@tonic-gate if (async->async_xmitblk)
29260Sstevel@tonic-gate freeb(async->async_xmitblk);
29270Sstevel@tonic-gate async->async_xmitblk = NULL;
29280Sstevel@tonic-gate async_start(async);
29290Sstevel@tonic-gate /*
29300Sstevel@tonic-gate * If the flag isn't set after doing the async_start above, we
29310Sstevel@tonic-gate * may have finished all the queued output. Signal any thread
29320Sstevel@tonic-gate * stuck in close.
29330Sstevel@tonic-gate */
29340Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BUSY))
29350Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
29360Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
29370Sstevel@tonic-gate }
29380Sstevel@tonic-gate /*
29390Sstevel@tonic-gate * A note about these overrun bits: all they do is *tell* someone
29400Sstevel@tonic-gate * about an error- They do not track multiple errors. In fact,
29410Sstevel@tonic-gate * you could consider them latched register bits if you like.
29420Sstevel@tonic-gate * We are only interested in printing the error message once for
29430Sstevel@tonic-gate * any cluster of overrun errrors.
29440Sstevel@tonic-gate */
29450Sstevel@tonic-gate if (async->async_hw_overrun) {
29460Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
29470Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
29480Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
29490Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: silo overflow", instance);
29500Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
29510Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
29520Sstevel@tonic-gate }
29530Sstevel@tonic-gate async->async_hw_overrun = 0;
29540Sstevel@tonic-gate }
29550Sstevel@tonic-gate if (async->async_sw_overrun) {
29560Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) {
29570Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
29580Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
29590Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: ring buffer overflow",
29605295Srandyf instance);
29610Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
29620Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
29630Sstevel@tonic-gate }
29640Sstevel@tonic-gate async->async_sw_overrun = 0;
29650Sstevel@tonic-gate }
29660Sstevel@tonic-gate if (asy->asy_flags & ASY_DOINGSOFT_RETRY) {
296712401SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_excl);
29680Sstevel@tonic-gate goto begin;
29690Sstevel@tonic-gate }
29700Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT;
297112401SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_excl_hi);
297212401SZachary.Kissel@Sun.COM mutex_exit(&asy->asy_excl);
29730Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint: done\n", instance);
29740Sstevel@tonic-gate }
29750Sstevel@tonic-gate
29760Sstevel@tonic-gate /*
29770Sstevel@tonic-gate * Restart output on a line after a delay or break timer expired.
29780Sstevel@tonic-gate */
29790Sstevel@tonic-gate static void
async_restart(void * arg)29800Sstevel@tonic-gate async_restart(void *arg)
29810Sstevel@tonic-gate {
29820Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)arg;
29830Sstevel@tonic-gate struct asycom *asy = async->async_common;
29840Sstevel@tonic-gate uchar_t lcr;
29850Sstevel@tonic-gate
29860Sstevel@tonic-gate /*
29870Sstevel@tonic-gate * If break timer expired, turn off the break bit.
29880Sstevel@tonic-gate */
29890Sstevel@tonic-gate #ifdef DEBUG
29900Sstevel@tonic-gate int instance = UNIT(async->async_dev);
29910Sstevel@tonic-gate
29920Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_restart\n", instance);
29930Sstevel@tonic-gate #endif
29940Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
29950Sstevel@tonic-gate /*
29960Sstevel@tonic-gate * If ASYNC_OUT_SUSPEND is also set, we don't really
29970Sstevel@tonic-gate * clean the HW break, TIOCCBRK is responsible for this.
29980Sstevel@tonic-gate */
29990Sstevel@tonic-gate if ((async->async_flags & ASYNC_BREAK) &&
30000Sstevel@tonic-gate !(async->async_flags & ASYNC_OUT_SUSPEND)) {
30010Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
30025295Srandyf lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
30031106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
30045295Srandyf (lcr & ~SETBREAK));
30050Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
30060Sstevel@tonic-gate }
30070Sstevel@tonic-gate async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK);
30080Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
30090Sstevel@tonic-gate async_start(async);
30100Sstevel@tonic-gate
30110Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
30120Sstevel@tonic-gate }
30130Sstevel@tonic-gate
30140Sstevel@tonic-gate static void
async_start(struct asyncline * async)30150Sstevel@tonic-gate async_start(struct asyncline *async)
30160Sstevel@tonic-gate {
30170Sstevel@tonic-gate async_nstart(async, 0);
30180Sstevel@tonic-gate }
30190Sstevel@tonic-gate
30200Sstevel@tonic-gate /*
30210Sstevel@tonic-gate * Start output on a line, unless it's busy, frozen, or otherwise.
30220Sstevel@tonic-gate */
30230Sstevel@tonic-gate /*ARGSUSED*/
30240Sstevel@tonic-gate static void
async_nstart(struct asyncline * async,int mode)30250Sstevel@tonic-gate async_nstart(struct asyncline *async, int mode)
30260Sstevel@tonic-gate {
30270Sstevel@tonic-gate struct asycom *asy = async->async_common;
30280Sstevel@tonic-gate int cc;
30290Sstevel@tonic-gate queue_t *q;
30300Sstevel@tonic-gate mblk_t *bp;
30310Sstevel@tonic-gate uchar_t *xmit_addr;
30320Sstevel@tonic-gate uchar_t val;
30330Sstevel@tonic-gate int fifo_len = 1;
30340Sstevel@tonic-gate boolean_t didsome;
30350Sstevel@tonic-gate mblk_t *nbp;
30360Sstevel@tonic-gate
30370Sstevel@tonic-gate #ifdef DEBUG
30380Sstevel@tonic-gate int instance = UNIT(async->async_dev);
30390Sstevel@tonic-gate
30400Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_nstart\n", instance);
30410Sstevel@tonic-gate #endif
30420Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) {
30430Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */
30440Sstevel@tonic-gate if (fifo_len > asy_max_tx_fifo)
30450Sstevel@tonic-gate fifo_len = asy_max_tx_fifo;
30460Sstevel@tonic-gate }
30470Sstevel@tonic-gate
30480Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl));
30490Sstevel@tonic-gate
30500Sstevel@tonic-gate /*
30510Sstevel@tonic-gate * If the chip is busy (i.e., we're waiting for a break timeout
30520Sstevel@tonic-gate * to expire, or for the current transmission to finish, or for
30530Sstevel@tonic-gate * output to finish draining from chip), don't grab anything new.
30540Sstevel@tonic-gate */
30550Sstevel@tonic-gate if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) {
30560Sstevel@tonic-gate DEBUGCONT2((mode? ASY_DEBUG_OUT : 0),
30575295Srandyf "async%d_nstart: start %s.\n",
30585295Srandyf instance,
30595295Srandyf async->async_flags & ASYNC_BREAK ? "break" : "busy");
30600Sstevel@tonic-gate return;
30610Sstevel@tonic-gate }
30620Sstevel@tonic-gate
30630Sstevel@tonic-gate /*
30640Sstevel@tonic-gate * Check only pended sw input flow control.
30650Sstevel@tonic-gate */
30660Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
30670Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL))
30680Sstevel@tonic-gate fifo_len--;
30690Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
30700Sstevel@tonic-gate
30710Sstevel@tonic-gate /*
30720Sstevel@tonic-gate * If we're waiting for a delay timeout to expire, don't grab
30730Sstevel@tonic-gate * anything new.
30740Sstevel@tonic-gate */
30750Sstevel@tonic-gate if (async->async_flags & ASYNC_DELAY) {
30760Sstevel@tonic-gate DEBUGCONT1((mode? ASY_DEBUG_OUT : 0),
30775295Srandyf "async%d_nstart: start ASYNC_DELAY.\n", instance);
30780Sstevel@tonic-gate return;
30790Sstevel@tonic-gate }
30800Sstevel@tonic-gate
30810Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) {
30820Sstevel@tonic-gate DEBUGCONT1((mode? ASY_DEBUG_OUT : 0),
30835295Srandyf "async%d_nstart: start writeq is null.\n", instance);
30840Sstevel@tonic-gate return; /* not attached to a stream */
30850Sstevel@tonic-gate }
30860Sstevel@tonic-gate
30870Sstevel@tonic-gate for (;;) {
30880Sstevel@tonic-gate if ((bp = getq(q)) == NULL)
30890Sstevel@tonic-gate return; /* no data to transmit */
30900Sstevel@tonic-gate
30910Sstevel@tonic-gate /*
30920Sstevel@tonic-gate * We have a message block to work on.
30930Sstevel@tonic-gate * Check whether it's a break, a delay, or an ioctl (the latter
30940Sstevel@tonic-gate * occurs if the ioctl in question was waiting for the output
30950Sstevel@tonic-gate * to drain). If it's one of those, process it immediately.
30960Sstevel@tonic-gate */
30970Sstevel@tonic-gate switch (bp->b_datap->db_type) {
30980Sstevel@tonic-gate
30990Sstevel@tonic-gate case M_BREAK:
31000Sstevel@tonic-gate /*
31010Sstevel@tonic-gate * Set the break bit, and arrange for "async_restart"
31020Sstevel@tonic-gate * to be called in 1/4 second; it will turn the
31030Sstevel@tonic-gate * break bit off, and call "async_start" to grab
31040Sstevel@tonic-gate * the next message.
31050Sstevel@tonic-gate */
31060Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
31071106Smrj val = ddi_get8(asy->asy_iohandle,
31085295Srandyf asy->asy_ioaddr + LCR);
31095295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
31105295Srandyf (val | SETBREAK));
31110Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
31120Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK;
31130Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async,
31140Sstevel@tonic-gate drv_usectohz(1000000)/4);
31150Sstevel@tonic-gate freemsg(bp);
31160Sstevel@tonic-gate return; /* wait for this to finish */
31170Sstevel@tonic-gate
31180Sstevel@tonic-gate case M_DELAY:
31190Sstevel@tonic-gate /*
31200Sstevel@tonic-gate * Arrange for "async_restart" to be called when the
31210Sstevel@tonic-gate * delay expires; it will turn ASYNC_DELAY off,
31220Sstevel@tonic-gate * and call "async_start" to grab the next message.
31230Sstevel@tonic-gate */
31240Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async,
31250Sstevel@tonic-gate (int)(*(unsigned char *)bp->b_rptr + 6));
31260Sstevel@tonic-gate async->async_flags |= ASYNC_DELAY;
31270Sstevel@tonic-gate freemsg(bp);
31280Sstevel@tonic-gate return; /* wait for this to finish */
31290Sstevel@tonic-gate
31300Sstevel@tonic-gate case M_IOCTL:
31310Sstevel@tonic-gate /*
31320Sstevel@tonic-gate * This ioctl was waiting for the output ahead of
31330Sstevel@tonic-gate * it to drain; obviously, it has. Do it, and
31340Sstevel@tonic-gate * then grab the next message after it.
31350Sstevel@tonic-gate */
31360Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
31370Sstevel@tonic-gate async_ioctl(async, q, bp);
31380Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
31390Sstevel@tonic-gate continue;
31400Sstevel@tonic-gate }
31410Sstevel@tonic-gate
31426990Sgd78059 while (bp != NULL && ((cc = MBLKL(bp)) == 0)) {
31430Sstevel@tonic-gate nbp = bp->b_cont;
31440Sstevel@tonic-gate freeb(bp);
31450Sstevel@tonic-gate bp = nbp;
31460Sstevel@tonic-gate }
31470Sstevel@tonic-gate if (bp != NULL)
31480Sstevel@tonic-gate break;
31490Sstevel@tonic-gate }
31500Sstevel@tonic-gate
31510Sstevel@tonic-gate /*
31520Sstevel@tonic-gate * We have data to transmit. If output is stopped, put
31530Sstevel@tonic-gate * it back and try again later.
31540Sstevel@tonic-gate */
31550Sstevel@tonic-gate if (async->async_flags & (ASYNC_HW_OUT_FLW | ASYNC_SW_OUT_FLW |
31560Sstevel@tonic-gate ASYNC_STOPPED | ASYNC_OUT_SUSPEND)) {
31570Sstevel@tonic-gate (void) putbq(q, bp);
31580Sstevel@tonic-gate return;
31590Sstevel@tonic-gate }
31600Sstevel@tonic-gate
31610Sstevel@tonic-gate async->async_xmitblk = bp;
31620Sstevel@tonic-gate xmit_addr = bp->b_rptr;
31630Sstevel@tonic-gate bp = bp->b_cont;
31640Sstevel@tonic-gate if (bp != NULL)
31650Sstevel@tonic-gate (void) putbq(q, bp); /* not done with this message yet */
31660Sstevel@tonic-gate
31670Sstevel@tonic-gate /*
31680Sstevel@tonic-gate * In 5-bit mode, the high order bits are used
31690Sstevel@tonic-gate * to indicate character sizes less than five,
31700Sstevel@tonic-gate * so we need to explicitly mask before transmitting
31710Sstevel@tonic-gate */
31720Sstevel@tonic-gate if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) {
31730Sstevel@tonic-gate unsigned char *p = xmit_addr;
31740Sstevel@tonic-gate int cnt = cc;
31750Sstevel@tonic-gate
31760Sstevel@tonic-gate while (cnt--)
31770Sstevel@tonic-gate *p++ &= (unsigned char) 0x1f;
31780Sstevel@tonic-gate }
31790Sstevel@tonic-gate
31800Sstevel@tonic-gate /*
31810Sstevel@tonic-gate * Set up this block for pseudo-DMA.
31820Sstevel@tonic-gate */
31830Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
31840Sstevel@tonic-gate /*
31850Sstevel@tonic-gate * If the transmitter is ready, shove the first
31860Sstevel@tonic-gate * character out.
31870Sstevel@tonic-gate */
31880Sstevel@tonic-gate didsome = B_FALSE;
31890Sstevel@tonic-gate while (--fifo_len >= 0 && cc > 0) {
31901106Smrj if (!(ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) &
31910Sstevel@tonic-gate XHRE))
31920Sstevel@tonic-gate break;
31931106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
31940Sstevel@tonic-gate *xmit_addr++);
31950Sstevel@tonic-gate cc--;
31960Sstevel@tonic-gate didsome = B_TRUE;
31970Sstevel@tonic-gate }
31980Sstevel@tonic-gate async->async_optr = xmit_addr;
31990Sstevel@tonic-gate async->async_ocnt = cc;
32000Sstevel@tonic-gate if (didsome)
32010Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS;
32020Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_BUSY,
32035295Srandyf "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n",
32045295Srandyf instance, async->async_ocnt);
32050Sstevel@tonic-gate async->async_flags |= ASYNC_BUSY;
32060Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
32070Sstevel@tonic-gate }
32080Sstevel@tonic-gate
32090Sstevel@tonic-gate /*
32100Sstevel@tonic-gate * Resume output by poking the transmitter.
32110Sstevel@tonic-gate */
32120Sstevel@tonic-gate static void
async_resume(struct asyncline * async)32130Sstevel@tonic-gate async_resume(struct asyncline *async)
32140Sstevel@tonic-gate {
32150Sstevel@tonic-gate struct asycom *asy = async->async_common;
32160Sstevel@tonic-gate #ifdef DEBUG
32170Sstevel@tonic-gate int instance;
32180Sstevel@tonic-gate #endif
32190Sstevel@tonic-gate
32200Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi));
32210Sstevel@tonic-gate #ifdef DEBUG
32220Sstevel@tonic-gate instance = UNIT(async->async_dev);
32230Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_resume\n", instance);
32240Sstevel@tonic-gate #endif
32250Sstevel@tonic-gate
32261106Smrj if (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE) {
32270Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL))
32280Sstevel@tonic-gate return;
32290Sstevel@tonic-gate if (async->async_ocnt > 0 &&
32300Sstevel@tonic-gate !(async->async_flags &
32310Sstevel@tonic-gate (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_OUT_SUSPEND))) {
32321106Smrj ddi_put8(asy->asy_iohandle,
32330Sstevel@tonic-gate asy->asy_ioaddr + DAT, *async->async_optr++);
32340Sstevel@tonic-gate async->async_ocnt--;
32350Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS;
32360Sstevel@tonic-gate }
32370Sstevel@tonic-gate }
32380Sstevel@tonic-gate }
32390Sstevel@tonic-gate
32400Sstevel@tonic-gate /*
32410Sstevel@tonic-gate * Hold the untimed break to last the minimum time.
32420Sstevel@tonic-gate */
32430Sstevel@tonic-gate static void
async_hold_utbrk(void * arg)32440Sstevel@tonic-gate async_hold_utbrk(void *arg)
32450Sstevel@tonic-gate {
32460Sstevel@tonic-gate struct asyncline *async = arg;
32470Sstevel@tonic-gate struct asycom *asy = async->async_common;
32480Sstevel@tonic-gate
32490Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
32500Sstevel@tonic-gate async->async_flags &= ~ASYNC_HOLD_UTBRK;
32510Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
32520Sstevel@tonic-gate async->async_utbrktid = 0;
32530Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
32540Sstevel@tonic-gate }
32550Sstevel@tonic-gate
32560Sstevel@tonic-gate /*
32570Sstevel@tonic-gate * Resume the untimed break.
32580Sstevel@tonic-gate */
32590Sstevel@tonic-gate static void
async_resume_utbrk(struct asyncline * async)32600Sstevel@tonic-gate async_resume_utbrk(struct asyncline *async)
32610Sstevel@tonic-gate {
32620Sstevel@tonic-gate uchar_t val;
32630Sstevel@tonic-gate struct asycom *asy = async->async_common;
32640Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl));
32650Sstevel@tonic-gate
32660Sstevel@tonic-gate /*
32670Sstevel@tonic-gate * Because the wait time is very short,
32680Sstevel@tonic-gate * so we use uninterruptably wait.
32690Sstevel@tonic-gate */
32700Sstevel@tonic-gate while (async->async_flags & ASYNC_HOLD_UTBRK) {
32710Sstevel@tonic-gate cv_wait(&async->async_flags_cv, &asy->asy_excl);
32720Sstevel@tonic-gate }
32730Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
32740Sstevel@tonic-gate /*
32750Sstevel@tonic-gate * Timed break and untimed break can exist simultaneously,
32760Sstevel@tonic-gate * if ASYNC_BREAK is also set at here, we don't
32770Sstevel@tonic-gate * really clean the HW break.
32780Sstevel@tonic-gate */
32790Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BREAK)) {
32801106Smrj val = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR);
32811106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR,
32820Sstevel@tonic-gate (val & ~SETBREAK));
32830Sstevel@tonic-gate }
32840Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_SUSPEND;
32850Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv);
32860Sstevel@tonic-gate if (async->async_ocnt > 0) {
32870Sstevel@tonic-gate async_resume(async);
32880Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
32890Sstevel@tonic-gate } else {
32900Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY;
32910Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
32920Sstevel@tonic-gate if (async->async_xmitblk != NULL) {
32930Sstevel@tonic-gate freeb(async->async_xmitblk);
32940Sstevel@tonic-gate async->async_xmitblk = NULL;
32950Sstevel@tonic-gate }
32960Sstevel@tonic-gate async_start(async);
32970Sstevel@tonic-gate }
32980Sstevel@tonic-gate }
32990Sstevel@tonic-gate
33000Sstevel@tonic-gate /*
33010Sstevel@tonic-gate * Process an "ioctl" message sent down to us.
33020Sstevel@tonic-gate * Note that we don't need to get any locks until we are ready to access
33030Sstevel@tonic-gate * the hardware. Nothing we access until then is going to be altered
33040Sstevel@tonic-gate * outside of the STREAMS framework, so we should be safe.
33050Sstevel@tonic-gate */
33060Sstevel@tonic-gate int asydelay = 10000;
33070Sstevel@tonic-gate static void
async_ioctl(struct asyncline * async,queue_t * wq,mblk_t * mp)33080Sstevel@tonic-gate async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp)
33090Sstevel@tonic-gate {
33100Sstevel@tonic-gate struct asycom *asy = async->async_common;
33110Sstevel@tonic-gate tty_common_t *tp = &async->async_ttycommon;
33120Sstevel@tonic-gate struct iocblk *iocp;
33130Sstevel@tonic-gate unsigned datasize;
33140Sstevel@tonic-gate int error = 0;
33150Sstevel@tonic-gate uchar_t val;
33160Sstevel@tonic-gate mblk_t *datamp;
33170Sstevel@tonic-gate unsigned int index;
33180Sstevel@tonic-gate
33190Sstevel@tonic-gate #ifdef DEBUG
33200Sstevel@tonic-gate int instance = UNIT(async->async_dev);
33210Sstevel@tonic-gate
33220Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl\n", instance);
33230Sstevel@tonic-gate #endif
33240Sstevel@tonic-gate
33250Sstevel@tonic-gate if (tp->t_iocpending != NULL) {
33260Sstevel@tonic-gate /*
33270Sstevel@tonic-gate * We were holding an "ioctl" response pending the
33280Sstevel@tonic-gate * availability of an "mblk" to hold data to be passed up;
33290Sstevel@tonic-gate * another "ioctl" came through, which means that "ioctl"
33300Sstevel@tonic-gate * must have timed out or been aborted.
33310Sstevel@tonic-gate */
33320Sstevel@tonic-gate freemsg(async->async_ttycommon.t_iocpending);
33330Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL;
33340Sstevel@tonic-gate }
33350Sstevel@tonic-gate
33360Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr;
33370Sstevel@tonic-gate
33380Sstevel@tonic-gate /*
33390Sstevel@tonic-gate * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl()
33400Sstevel@tonic-gate * because this function frees up the message block (mp->b_cont) that
33410Sstevel@tonic-gate * contains the user location where we pass back the results.
33420Sstevel@tonic-gate *
33430Sstevel@tonic-gate * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl
33440Sstevel@tonic-gate * zaps. We know that ttycommon_ioctl doesn't know any CONS*
33450Sstevel@tonic-gate * ioctls, so keep the others safe too.
33460Sstevel@tonic-gate */
33470Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_IOCTL, "async%d_ioctl: %s\n",
33485295Srandyf instance,
33495295Srandyf iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" :
33505295Srandyf iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" :
33515295Srandyf iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" :
33525295Srandyf iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" :
33535295Srandyf "other");
33545295Srandyf
33550Sstevel@tonic-gate switch (iocp->ioc_cmd) {
33560Sstevel@tonic-gate case TIOCMGET:
33570Sstevel@tonic-gate case TIOCGPPS:
33580Sstevel@tonic-gate case TIOCSPPS:
33590Sstevel@tonic-gate case TIOCGPPSEV:
33600Sstevel@tonic-gate case CONSOPENPOLLEDIO:
33610Sstevel@tonic-gate case CONSCLOSEPOLLEDIO:
33620Sstevel@tonic-gate case CONSSETABORTENABLE:
33630Sstevel@tonic-gate case CONSGETABORTENABLE:
33640Sstevel@tonic-gate error = -1; /* Do Nothing */
33650Sstevel@tonic-gate break;
33660Sstevel@tonic-gate default:
33670Sstevel@tonic-gate
33680Sstevel@tonic-gate /*
33690Sstevel@tonic-gate * The only way in which "ttycommon_ioctl" can fail is if the
33700Sstevel@tonic-gate * "ioctl" requires a response containing data to be returned
33710Sstevel@tonic-gate * to the user, and no mblk could be allocated for the data.
33720Sstevel@tonic-gate * No such "ioctl" alters our state. Thus, we always go ahead
33730Sstevel@tonic-gate * and do any state-changes the "ioctl" calls for. If we
33740Sstevel@tonic-gate * couldn't allocate the data, "ttycommon_ioctl" has stashed
33750Sstevel@tonic-gate * the "ioctl" away safely, so we just call "bufcall" to
33760Sstevel@tonic-gate * request that we be called back when we stand a better
33770Sstevel@tonic-gate * chance of allocating the data.
33780Sstevel@tonic-gate */
33790Sstevel@tonic-gate if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) {
33800Sstevel@tonic-gate if (async->async_wbufcid)
33810Sstevel@tonic-gate unbufcall(async->async_wbufcid);
33820Sstevel@tonic-gate async->async_wbufcid = bufcall(datasize, BPRI_HI,
33830Sstevel@tonic-gate (void (*)(void *)) async_reioctl,
33840Sstevel@tonic-gate (void *)(intptr_t)async->async_common->asy_unit);
33850Sstevel@tonic-gate return;
33860Sstevel@tonic-gate }
33870Sstevel@tonic-gate }
33880Sstevel@tonic-gate
33890Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
33900Sstevel@tonic-gate
33910Sstevel@tonic-gate if (error == 0) {
33920Sstevel@tonic-gate /*
33930Sstevel@tonic-gate * "ttycommon_ioctl" did most of the work; we just use the
33940Sstevel@tonic-gate * data it set up.
33950Sstevel@tonic-gate */
33960Sstevel@tonic-gate switch (iocp->ioc_cmd) {
33970Sstevel@tonic-gate
33980Sstevel@tonic-gate case TCSETS:
33990Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
34000Sstevel@tonic-gate if (asy_baudok(asy))
34010Sstevel@tonic-gate asy_program(asy, ASY_NOINIT);
34020Sstevel@tonic-gate else
34030Sstevel@tonic-gate error = EINVAL;
34040Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
34050Sstevel@tonic-gate break;
34060Sstevel@tonic-gate case TCSETSF:
34070Sstevel@tonic-gate case TCSETSW:
34080Sstevel@tonic-gate case TCSETA:
34090Sstevel@tonic-gate case TCSETAW:
34100Sstevel@tonic-gate case TCSETAF:
34110Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
34120Sstevel@tonic-gate if (!asy_baudok(asy))
34130Sstevel@tonic-gate error = EINVAL;
34140Sstevel@tonic-gate else {
34150Sstevel@tonic-gate if (asy_isbusy(asy))
34160Sstevel@tonic-gate asy_waiteot(asy);
34170Sstevel@tonic-gate asy_program(asy, ASY_NOINIT);
34180Sstevel@tonic-gate }
34190Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
34200Sstevel@tonic-gate break;
34210Sstevel@tonic-gate }
34220Sstevel@tonic-gate } else if (error < 0) {
34230Sstevel@tonic-gate /*
34240Sstevel@tonic-gate * "ttycommon_ioctl" didn't do anything; we process it here.
34250Sstevel@tonic-gate */
34260Sstevel@tonic-gate error = 0;
34270Sstevel@tonic-gate switch (iocp->ioc_cmd) {
34280Sstevel@tonic-gate
34290Sstevel@tonic-gate case TIOCGPPS:
34300Sstevel@tonic-gate /*
34310Sstevel@tonic-gate * Get PPS on/off.
34320Sstevel@tonic-gate */
34330Sstevel@tonic-gate if (mp->b_cont != NULL)
34340Sstevel@tonic-gate freemsg(mp->b_cont);
34350Sstevel@tonic-gate
34360Sstevel@tonic-gate mp->b_cont = allocb(sizeof (int), BPRI_HI);
34370Sstevel@tonic-gate if (mp->b_cont == NULL) {
34380Sstevel@tonic-gate error = ENOMEM;
34390Sstevel@tonic-gate break;
34400Sstevel@tonic-gate }
34410Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS)
34420Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 1;
34430Sstevel@tonic-gate else
34440Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 0;
34450Sstevel@tonic-gate mp->b_cont->b_wptr += sizeof (int);
34460Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
34470Sstevel@tonic-gate iocp->ioc_count = sizeof (int);
34480Sstevel@tonic-gate break;
34490Sstevel@tonic-gate
34500Sstevel@tonic-gate case TIOCSPPS:
34510Sstevel@tonic-gate /*
34520Sstevel@tonic-gate * Set PPS on/off.
34530Sstevel@tonic-gate */
34540Sstevel@tonic-gate error = miocpullup(mp, sizeof (int));
34550Sstevel@tonic-gate if (error != 0)
34560Sstevel@tonic-gate break;
34570Sstevel@tonic-gate
34580Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
34590Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr)
34600Sstevel@tonic-gate asy->asy_flags |= ASY_PPS;
34610Sstevel@tonic-gate else
34620Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS;
34630Sstevel@tonic-gate /* Reset edge sense */
34640Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE;
34650Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
34660Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
34670Sstevel@tonic-gate break;
34680Sstevel@tonic-gate
34690Sstevel@tonic-gate case TIOCGPPSEV:
34700Sstevel@tonic-gate {
34710Sstevel@tonic-gate /*
34720Sstevel@tonic-gate * Get PPS event data.
34730Sstevel@tonic-gate */
34740Sstevel@tonic-gate mblk_t *bp;
34750Sstevel@tonic-gate void *buf;
34760Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
34770Sstevel@tonic-gate struct ppsclockev32 p32;
34780Sstevel@tonic-gate #endif
34790Sstevel@tonic-gate struct ppsclockev ppsclockev;
34800Sstevel@tonic-gate
34810Sstevel@tonic-gate if (mp->b_cont != NULL) {
34820Sstevel@tonic-gate freemsg(mp->b_cont);
34830Sstevel@tonic-gate mp->b_cont = NULL;
34840Sstevel@tonic-gate }
34850Sstevel@tonic-gate
34860Sstevel@tonic-gate if ((asy->asy_flags & ASY_PPS) == 0) {
34870Sstevel@tonic-gate error = ENXIO;
34880Sstevel@tonic-gate break;
34890Sstevel@tonic-gate }
34900Sstevel@tonic-gate
34910Sstevel@tonic-gate /* Protect from incomplete asy_ppsev */
34920Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
34930Sstevel@tonic-gate ppsclockev = asy_ppsev;
34940Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
34950Sstevel@tonic-gate
34960Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL
34970Sstevel@tonic-gate if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) {
34980Sstevel@tonic-gate TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv);
34990Sstevel@tonic-gate p32.serial = ppsclockev.serial;
35000Sstevel@tonic-gate buf = &p32;
35010Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev32);
35020Sstevel@tonic-gate } else
35030Sstevel@tonic-gate #endif
35040Sstevel@tonic-gate {
35050Sstevel@tonic-gate buf = &ppsclockev;
35060Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev);
35070Sstevel@tonic-gate }
35080Sstevel@tonic-gate
35090Sstevel@tonic-gate if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) {
35100Sstevel@tonic-gate error = ENOMEM;
35110Sstevel@tonic-gate break;
35120Sstevel@tonic-gate }
35130Sstevel@tonic-gate mp->b_cont = bp;
35140Sstevel@tonic-gate
35150Sstevel@tonic-gate bcopy(buf, bp->b_wptr, iocp->ioc_count);
35160Sstevel@tonic-gate bp->b_wptr += iocp->ioc_count;
35170Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
35180Sstevel@tonic-gate break;
35190Sstevel@tonic-gate }
35200Sstevel@tonic-gate
35210Sstevel@tonic-gate case TCSBRK:
35220Sstevel@tonic-gate error = miocpullup(mp, sizeof (int));
35230Sstevel@tonic-gate if (error != 0)
35240Sstevel@tonic-gate break;
35250Sstevel@tonic-gate
35260Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr == 0) {
35270Sstevel@tonic-gate
35280Sstevel@tonic-gate /*
35290Sstevel@tonic-gate * XXX Arrangements to ensure that a break
35300Sstevel@tonic-gate * isn't in progress should be sufficient.
35310Sstevel@tonic-gate * This ugly delay() is the only thing
35320Sstevel@tonic-gate * that seems to work on the NCR Worldmark.
35330Sstevel@tonic-gate * It should be replaced. Note that an
35340Sstevel@tonic-gate * asy_waiteot() also does not work.
35350Sstevel@tonic-gate */
35360Sstevel@tonic-gate if (asydelay)
35370Sstevel@tonic-gate delay(drv_usectohz(asydelay));
35380Sstevel@tonic-gate
35390Sstevel@tonic-gate while (async->async_flags & ASYNC_BREAK) {
35400Sstevel@tonic-gate cv_wait(&async->async_flags_cv,
35410Sstevel@tonic-gate &asy->asy_excl);
35420Sstevel@tonic-gate }
35430Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
35440Sstevel@tonic-gate /*
35450Sstevel@tonic-gate * We loop until the TSR is empty and then
35460Sstevel@tonic-gate * set the break. ASYNC_BREAK has been set
35470Sstevel@tonic-gate * to ensure that no characters are
35480Sstevel@tonic-gate * transmitted while the TSR is being
35490Sstevel@tonic-gate * flushed and SOUT is being used for the
35500Sstevel@tonic-gate * break signal.
35510Sstevel@tonic-gate *
35520Sstevel@tonic-gate * The wait period is equal to
35530Sstevel@tonic-gate * clock / (baud * 16) * 16 * 2.
35540Sstevel@tonic-gate */
35550Sstevel@tonic-gate index = BAUDINDEX(
35565295Srandyf async->async_ttycommon.t_cflag);
35570Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK;
35585295Srandyf
35591106Smrj while ((ddi_get8(asy->asy_iohandle,
35600Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XSRE) == 0) {
35610Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
35620Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
35630Sstevel@tonic-gate drv_usecwait(
35645295Srandyf 32*asyspdtab[index] & 0xfff);
35650Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
35660Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
35670Sstevel@tonic-gate }
35680Sstevel@tonic-gate /*
35690Sstevel@tonic-gate * Arrange for "async_restart"
35700Sstevel@tonic-gate * to be called in 1/4 second;
35710Sstevel@tonic-gate * it will turn the break bit off, and call
35720Sstevel@tonic-gate * "async_start" to grab the next message.
35730Sstevel@tonic-gate */
35741106Smrj val = ddi_get8(asy->asy_iohandle,
35755295Srandyf asy->asy_ioaddr + LCR);
35761106Smrj ddi_put8(asy->asy_iohandle,
35775295Srandyf asy->asy_ioaddr + LCR,
35785295Srandyf (val | SETBREAK));
35790Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
35800Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async,
35810Sstevel@tonic-gate drv_usectohz(1000000)/4);
35820Sstevel@tonic-gate } else {
35830Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT,
35845295Srandyf "async%d_ioctl: wait for flush.\n",
35855295Srandyf instance);
35860Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
35870Sstevel@tonic-gate asy_waiteot(asy);
35880Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
35890Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT,
35905295Srandyf "async%d_ioctl: ldterm satisfied.\n",
35915295Srandyf instance);
35920Sstevel@tonic-gate }
35930Sstevel@tonic-gate break;
35940Sstevel@tonic-gate
35950Sstevel@tonic-gate case TIOCSBRK:
35960Sstevel@tonic-gate if (!(async->async_flags & ASYNC_OUT_SUSPEND)) {
35970Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
35980Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_SUSPEND;
35990Sstevel@tonic-gate async->async_flags |= ASYNC_HOLD_UTBRK;
36000Sstevel@tonic-gate index = BAUDINDEX(
36010Sstevel@tonic-gate async->async_ttycommon.t_cflag);
36021106Smrj while ((ddi_get8(asy->asy_iohandle,
36030Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XSRE) == 0) {
36040Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
36050Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
36060Sstevel@tonic-gate drv_usecwait(
36070Sstevel@tonic-gate 32*asyspdtab[index] & 0xfff);
36080Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
36090Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
36100Sstevel@tonic-gate }
36111106Smrj val = ddi_get8(asy->asy_iohandle,
36120Sstevel@tonic-gate asy->asy_ioaddr + LCR);
36131106Smrj ddi_put8(asy->asy_iohandle,
36140Sstevel@tonic-gate asy->asy_ioaddr + LCR, (val | SETBREAK));
36150Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
36160Sstevel@tonic-gate /* wait for 100ms to hold BREAK */
36170Sstevel@tonic-gate async->async_utbrktid =
36180Sstevel@tonic-gate timeout((void (*)())async_hold_utbrk,
36190Sstevel@tonic-gate (caddr_t)async,
36200Sstevel@tonic-gate drv_usectohz(asy_min_utbrk));
36210Sstevel@tonic-gate }
36220Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0);
36230Sstevel@tonic-gate break;
36240Sstevel@tonic-gate
36250Sstevel@tonic-gate case TIOCCBRK:
36260Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_SUSPEND)
36270Sstevel@tonic-gate async_resume_utbrk(async);
36280Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0);
36290Sstevel@tonic-gate break;
36300Sstevel@tonic-gate
36310Sstevel@tonic-gate case TIOCMSET:
36320Sstevel@tonic-gate case TIOCMBIS:
36330Sstevel@tonic-gate case TIOCMBIC:
36340Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) {
36350Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
36365295Srandyf "non-transparent\n", instance);
36370Sstevel@tonic-gate
36380Sstevel@tonic-gate error = miocpullup(mp, sizeof (int));
36390Sstevel@tonic-gate if (error != 0)
36400Sstevel@tonic-gate break;
36410Sstevel@tonic-gate
36420Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
36430Sstevel@tonic-gate (void) asymctl(asy,
36445295Srandyf dmtoasy(*(int *)mp->b_cont->b_rptr),
36455295Srandyf iocp->ioc_cmd);
36460Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
36470Sstevel@tonic-gate iocp->ioc_error = 0;
36480Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
36490Sstevel@tonic-gate } else {
36500Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
36515295Srandyf "transparent\n", instance);
36520Sstevel@tonic-gate mcopyin(mp, NULL, sizeof (int), NULL);
36530Sstevel@tonic-gate }
36540Sstevel@tonic-gate break;
36550Sstevel@tonic-gate
36560Sstevel@tonic-gate case TIOCMGET:
36570Sstevel@tonic-gate datamp = allocb(sizeof (int), BPRI_MED);
36580Sstevel@tonic-gate if (datamp == NULL) {
36590Sstevel@tonic-gate error = EAGAIN;
36600Sstevel@tonic-gate break;
36610Sstevel@tonic-gate }
36620Sstevel@tonic-gate
36630Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
36640Sstevel@tonic-gate *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET);
36650Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
36660Sstevel@tonic-gate
36670Sstevel@tonic-gate if (iocp->ioc_count == TRANSPARENT) {
36680Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
36695295Srandyf "transparent\n", instance);
36705295Srandyf mcopyout(mp, NULL, sizeof (int), NULL, datamp);
36710Sstevel@tonic-gate } else {
36720Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: "
36735295Srandyf "non-transparent\n", instance);
36740Sstevel@tonic-gate mioc2ack(mp, datamp, sizeof (int), 0);
36750Sstevel@tonic-gate }
36760Sstevel@tonic-gate break;
36770Sstevel@tonic-gate
36780Sstevel@tonic-gate case CONSOPENPOLLEDIO:
36790Sstevel@tonic-gate error = miocpullup(mp, sizeof (struct cons_polledio *));
36800Sstevel@tonic-gate if (error != 0)
36810Sstevel@tonic-gate break;
36820Sstevel@tonic-gate
36830Sstevel@tonic-gate *(struct cons_polledio **)mp->b_cont->b_rptr =
36845295Srandyf &asy->polledio;
36850Sstevel@tonic-gate
36860Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
36870Sstevel@tonic-gate break;
36880Sstevel@tonic-gate
36890Sstevel@tonic-gate case CONSCLOSEPOLLEDIO:
36900Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
36910Sstevel@tonic-gate iocp->ioc_error = 0;
36920Sstevel@tonic-gate iocp->ioc_rval = 0;
36930Sstevel@tonic-gate break;
36940Sstevel@tonic-gate
36950Sstevel@tonic-gate case CONSSETABORTENABLE:
36960Sstevel@tonic-gate error = secpolicy_console(iocp->ioc_cr);
36970Sstevel@tonic-gate if (error != 0)
36980Sstevel@tonic-gate break;
36990Sstevel@tonic-gate
37000Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) {
37010Sstevel@tonic-gate error = EINVAL;
37020Sstevel@tonic-gate break;
37030Sstevel@tonic-gate }
37040Sstevel@tonic-gate
37050Sstevel@tonic-gate if (*(intptr_t *)mp->b_cont->b_rptr)
37060Sstevel@tonic-gate asy->asy_flags |= ASY_CONSOLE;
37070Sstevel@tonic-gate else
37080Sstevel@tonic-gate asy->asy_flags &= ~ASY_CONSOLE;
37090Sstevel@tonic-gate
37100Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
37110Sstevel@tonic-gate iocp->ioc_error = 0;
37120Sstevel@tonic-gate iocp->ioc_rval = 0;
37130Sstevel@tonic-gate break;
37140Sstevel@tonic-gate
37150Sstevel@tonic-gate case CONSGETABORTENABLE:
37160Sstevel@tonic-gate /*CONSTANTCONDITION*/
37170Sstevel@tonic-gate ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *));
37180Sstevel@tonic-gate /*
37190Sstevel@tonic-gate * Store the return value right in the payload
37200Sstevel@tonic-gate * we were passed. Crude.
37210Sstevel@tonic-gate */
37220Sstevel@tonic-gate mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL);
37230Sstevel@tonic-gate *(boolean_t *)mp->b_cont->b_rptr =
37245295Srandyf (asy->asy_flags & ASY_CONSOLE) != 0;
37250Sstevel@tonic-gate break;
37260Sstevel@tonic-gate
37270Sstevel@tonic-gate default:
37280Sstevel@tonic-gate /*
37290Sstevel@tonic-gate * If we don't understand it, it's an error. NAK it.
37300Sstevel@tonic-gate */
37310Sstevel@tonic-gate error = EINVAL;
37320Sstevel@tonic-gate break;
37330Sstevel@tonic-gate }
37340Sstevel@tonic-gate }
37350Sstevel@tonic-gate if (error != 0) {
37360Sstevel@tonic-gate iocp->ioc_error = error;
37370Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK;
37380Sstevel@tonic-gate }
37390Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
37400Sstevel@tonic-gate qreply(wq, mp);
37410Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl: done\n", instance);
37420Sstevel@tonic-gate }
37430Sstevel@tonic-gate
37440Sstevel@tonic-gate static int
asyrsrv(queue_t * q)37450Sstevel@tonic-gate asyrsrv(queue_t *q)
37460Sstevel@tonic-gate {
37470Sstevel@tonic-gate mblk_t *bp;
37480Sstevel@tonic-gate struct asyncline *async;
37490Sstevel@tonic-gate
37500Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr;
37510Sstevel@tonic-gate
37520Sstevel@tonic-gate while (canputnext(q) && (bp = getq(q)))
37530Sstevel@tonic-gate putnext(q, bp);
37540Sstevel@tonic-gate ASYSETSOFT(async->async_common);
37550Sstevel@tonic-gate async->async_polltid = 0;
37560Sstevel@tonic-gate return (0);
37570Sstevel@tonic-gate }
37580Sstevel@tonic-gate
37590Sstevel@tonic-gate /*
37605295Srandyf * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should
37615295Srandyf * handle messages as though the driver is operating normally or is
37625295Srandyf * suspended. In the suspended case, some or all of the processing may have
37635295Srandyf * to be delayed until the driver is resumed.
37645295Srandyf */
37655295Srandyf #define ASYWPUTDO_NOT_SUSP(async, wput) \
37665295Srandyf !((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED))
37675295Srandyf
37685295Srandyf /*
37695295Srandyf * Processing for write queue put procedure.
37700Sstevel@tonic-gate * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here;
37710Sstevel@tonic-gate * set the flow control character for M_STOPI and M_STARTI messages;
37720Sstevel@tonic-gate * queue up M_BREAK, M_DELAY, and M_DATA messages for processing
37730Sstevel@tonic-gate * by the start routine, and then call the start routine; discard
37740Sstevel@tonic-gate * everything else. Note that this driver does not incorporate any
37750Sstevel@tonic-gate * mechanism to negotiate to handle the canonicalization process.
37760Sstevel@tonic-gate * It expects that these functions are handled in upper module(s),
37770Sstevel@tonic-gate * as we do in ldterm.
37780Sstevel@tonic-gate */
37790Sstevel@tonic-gate static int
asywputdo(queue_t * q,mblk_t * mp,boolean_t wput)37805295Srandyf asywputdo(queue_t *q, mblk_t *mp, boolean_t wput)
37810Sstevel@tonic-gate {
37820Sstevel@tonic-gate struct asyncline *async;
37830Sstevel@tonic-gate struct asycom *asy;
37840Sstevel@tonic-gate #ifdef DEBUG
37850Sstevel@tonic-gate int instance;
37860Sstevel@tonic-gate #endif
37870Sstevel@tonic-gate int error;
37880Sstevel@tonic-gate
37890Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr;
37905295Srandyf
37910Sstevel@tonic-gate #ifdef DEBUG
37920Sstevel@tonic-gate instance = UNIT(async->async_dev);
37930Sstevel@tonic-gate #endif
37940Sstevel@tonic-gate asy = async->async_common;
37950Sstevel@tonic-gate
37960Sstevel@tonic-gate switch (mp->b_datap->db_type) {
37970Sstevel@tonic-gate
37980Sstevel@tonic-gate case M_STOP:
37990Sstevel@tonic-gate /*
38000Sstevel@tonic-gate * Since we don't do real DMA, we can just let the
38010Sstevel@tonic-gate * chip coast to a stop after applying the brakes.
38020Sstevel@tonic-gate */
38030Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
38040Sstevel@tonic-gate async->async_flags |= ASYNC_STOPPED;
38050Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
38060Sstevel@tonic-gate freemsg(mp);
38070Sstevel@tonic-gate break;
38080Sstevel@tonic-gate
38090Sstevel@tonic-gate case M_START:
38100Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
38110Sstevel@tonic-gate if (async->async_flags & ASYNC_STOPPED) {
38120Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED;
38135295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
38145295Srandyf /*
38155295Srandyf * If an output operation is in progress,
38165295Srandyf * resume it. Otherwise, prod the start
38175295Srandyf * routine.
38185295Srandyf */
38195295Srandyf if (async->async_ocnt > 0) {
38205295Srandyf mutex_enter(&asy->asy_excl_hi);
38215295Srandyf async_resume(async);
38225295Srandyf mutex_exit(&asy->asy_excl_hi);
38235295Srandyf } else {
38245295Srandyf async_start(async);
38255295Srandyf }
38260Sstevel@tonic-gate }
38270Sstevel@tonic-gate }
38280Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
38290Sstevel@tonic-gate freemsg(mp);
38300Sstevel@tonic-gate break;
38310Sstevel@tonic-gate
38320Sstevel@tonic-gate case M_IOCTL:
38330Sstevel@tonic-gate switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) {
38340Sstevel@tonic-gate
38350Sstevel@tonic-gate case TCSBRK:
38360Sstevel@tonic-gate error = miocpullup(mp, sizeof (int));
38370Sstevel@tonic-gate if (error != 0) {
38380Sstevel@tonic-gate miocnak(q, mp, 0, error);
38390Sstevel@tonic-gate return (0);
38400Sstevel@tonic-gate }
38410Sstevel@tonic-gate
38420Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr != 0) {
38430Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT,
38445295Srandyf "async%d_ioctl: flush request.\n",
38455295Srandyf instance);
38460Sstevel@tonic-gate (void) putq(q, mp);
38475295Srandyf
38480Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
38495295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
38505295Srandyf /*
38515295Srandyf * If an TIOCSBRK is in progress,
38525295Srandyf * clean it as TIOCCBRK does,
38535295Srandyf * then kick off output.
38545295Srandyf * If TIOCSBRK is not in progress,
38555295Srandyf * just kick off output.
38565295Srandyf */
38575295Srandyf async_resume_utbrk(async);
38585295Srandyf }
38590Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
38600Sstevel@tonic-gate break;
38610Sstevel@tonic-gate }
38620Sstevel@tonic-gate /*FALLTHROUGH*/
38630Sstevel@tonic-gate case TCSETSW:
38640Sstevel@tonic-gate case TCSETSF:
38650Sstevel@tonic-gate case TCSETAW:
38660Sstevel@tonic-gate case TCSETAF:
38670Sstevel@tonic-gate /*
38680Sstevel@tonic-gate * The changes do not take effect until all
38690Sstevel@tonic-gate * output queued before them is drained.
38700Sstevel@tonic-gate * Put this message on the queue, so that
38710Sstevel@tonic-gate * "async_start" will see it when it's done
38720Sstevel@tonic-gate * with the output before it. Poke the
38730Sstevel@tonic-gate * start routine, just in case.
38740Sstevel@tonic-gate */
38750Sstevel@tonic-gate (void) putq(q, mp);
38765295Srandyf
38770Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
38785295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
38795295Srandyf /*
38805295Srandyf * If an TIOCSBRK is in progress,
38815295Srandyf * clean it as TIOCCBRK does.
38825295Srandyf * then kick off output.
38835295Srandyf * If TIOCSBRK is not in progress,
38845295Srandyf * just kick off output.
38855295Srandyf */
38865295Srandyf async_resume_utbrk(async);
38875295Srandyf }
38880Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
38890Sstevel@tonic-gate break;
38900Sstevel@tonic-gate
38910Sstevel@tonic-gate default:
38920Sstevel@tonic-gate /*
38930Sstevel@tonic-gate * Do it now.
38940Sstevel@tonic-gate */
38955295Srandyf mutex_enter(&asy->asy_excl);
38965295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
38975295Srandyf mutex_exit(&asy->asy_excl);
38985295Srandyf async_ioctl(async, q, mp);
38995295Srandyf break;
39005295Srandyf }
39015295Srandyf async_put_suspq(asy, mp);
39025295Srandyf mutex_exit(&asy->asy_excl);
39030Sstevel@tonic-gate break;
39040Sstevel@tonic-gate }
39050Sstevel@tonic-gate break;
39060Sstevel@tonic-gate
39070Sstevel@tonic-gate case M_FLUSH:
39080Sstevel@tonic-gate if (*mp->b_rptr & FLUSHW) {
39090Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
39100Sstevel@tonic-gate
39110Sstevel@tonic-gate /*
39120Sstevel@tonic-gate * Abort any output in progress.
39130Sstevel@tonic-gate */
39140Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
39150Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) {
39165295Srandyf DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: "
39170Sstevel@tonic-gate "Clearing async_ocnt, "
39180Sstevel@tonic-gate "leaving ASYNC_BUSY set\n",
39190Sstevel@tonic-gate instance);
39200Sstevel@tonic-gate async->async_ocnt = 0;
39210Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY;
39220Sstevel@tonic-gate } /* if */
39235295Srandyf
39245295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
39255295Srandyf /* Flush FIFO buffers */
39265295Srandyf if (asy->asy_use_fifo == FIFO_ON) {
39275295Srandyf asy_reset_fifo(asy, FIFOTXFLSH);
39285295Srandyf }
39295295Srandyf }
39300Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
39310Sstevel@tonic-gate
39320Sstevel@tonic-gate /* Flush FIFO buffers */
39330Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) {
39340Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH);
39350Sstevel@tonic-gate }
39360Sstevel@tonic-gate
39370Sstevel@tonic-gate /*
39380Sstevel@tonic-gate * Flush our write queue.
39390Sstevel@tonic-gate */
39400Sstevel@tonic-gate flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */
39410Sstevel@tonic-gate if (async->async_xmitblk != NULL) {
39420Sstevel@tonic-gate freeb(async->async_xmitblk);
39430Sstevel@tonic-gate async->async_xmitblk = NULL;
39440Sstevel@tonic-gate }
39450Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
39460Sstevel@tonic-gate *mp->b_rptr &= ~FLUSHW; /* it has been flushed */
39470Sstevel@tonic-gate }
39480Sstevel@tonic-gate if (*mp->b_rptr & FLUSHR) {
39495295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
39505295Srandyf /* Flush FIFO buffers */
39515295Srandyf if (asy->asy_use_fifo == FIFO_ON) {
39525295Srandyf asy_reset_fifo(asy, FIFORXFLSH);
39535295Srandyf }
39540Sstevel@tonic-gate }
39550Sstevel@tonic-gate flushq(RD(q), FLUSHDATA);
39560Sstevel@tonic-gate qreply(q, mp); /* give the read queues a crack at it */
39570Sstevel@tonic-gate } else {
39580Sstevel@tonic-gate freemsg(mp);
39590Sstevel@tonic-gate }
39600Sstevel@tonic-gate
39610Sstevel@tonic-gate /*
39620Sstevel@tonic-gate * We must make sure we process messages that survive the
39630Sstevel@tonic-gate * write-side flush.
39640Sstevel@tonic-gate */
39655295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
39665295Srandyf mutex_enter(&asy->asy_excl);
39675295Srandyf async_start(async);
39685295Srandyf mutex_exit(&asy->asy_excl);
39695295Srandyf }
39700Sstevel@tonic-gate break;
39710Sstevel@tonic-gate
39720Sstevel@tonic-gate case M_BREAK:
39730Sstevel@tonic-gate case M_DELAY:
39740Sstevel@tonic-gate case M_DATA:
39750Sstevel@tonic-gate /*
39760Sstevel@tonic-gate * Queue the message up to be transmitted,
39770Sstevel@tonic-gate * and poke the start routine.
39780Sstevel@tonic-gate */
39790Sstevel@tonic-gate (void) putq(q, mp);
39805295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
39815295Srandyf mutex_enter(&asy->asy_excl);
39825295Srandyf async_start(async);
39835295Srandyf mutex_exit(&asy->asy_excl);
39845295Srandyf }
39850Sstevel@tonic-gate break;
39860Sstevel@tonic-gate
39870Sstevel@tonic-gate case M_STOPI:
39880Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
39895295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
39905295Srandyf mutex_enter(&asy->asy_excl_hi);
39915295Srandyf if (!(async->async_inflow_source & IN_FLOW_USER)) {
39925295Srandyf async_flowcontrol_hw_input(asy, FLOW_STOP,
39935295Srandyf IN_FLOW_USER);
39945295Srandyf (void) async_flowcontrol_sw_input(asy,
39955295Srandyf FLOW_STOP, IN_FLOW_USER);
39965295Srandyf }
39975295Srandyf mutex_exit(&asy->asy_excl_hi);
39985295Srandyf mutex_exit(&asy->asy_excl);
39995295Srandyf freemsg(mp);
40005295Srandyf break;
40010Sstevel@tonic-gate }
40025295Srandyf async_put_suspq(asy, mp);
40030Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
40040Sstevel@tonic-gate break;
40050Sstevel@tonic-gate
40060Sstevel@tonic-gate case M_STARTI:
40070Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
40085295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
40095295Srandyf mutex_enter(&asy->asy_excl_hi);
40105295Srandyf if (async->async_inflow_source & IN_FLOW_USER) {
40115295Srandyf async_flowcontrol_hw_input(asy, FLOW_START,
40125295Srandyf IN_FLOW_USER);
40135295Srandyf (void) async_flowcontrol_sw_input(asy,
40145295Srandyf FLOW_START, IN_FLOW_USER);
40155295Srandyf }
40165295Srandyf mutex_exit(&asy->asy_excl_hi);
40175295Srandyf mutex_exit(&asy->asy_excl);
40185295Srandyf freemsg(mp);
40195295Srandyf break;
40200Sstevel@tonic-gate }
40215295Srandyf async_put_suspq(asy, mp);
40220Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
40230Sstevel@tonic-gate break;
40240Sstevel@tonic-gate
40250Sstevel@tonic-gate case M_CTL:
40260Sstevel@tonic-gate if (MBLKL(mp) >= sizeof (struct iocblk) &&
40270Sstevel@tonic-gate ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) {
40285295Srandyf mutex_enter(&asy->asy_excl);
40295295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
40305295Srandyf ((struct iocblk *)mp->b_rptr)->ioc_cmd =
40315295Srandyf MC_HAS_POSIX;
40325295Srandyf mutex_exit(&asy->asy_excl);
40335295Srandyf qreply(q, mp);
40345295Srandyf break;
40355295Srandyf } else {
40365295Srandyf async_put_suspq(asy, mp);
40375295Srandyf }
40380Sstevel@tonic-gate } else {
40390Sstevel@tonic-gate /*
40400Sstevel@tonic-gate * These MC_SERVICE type messages are used by upper
40410Sstevel@tonic-gate * modules to tell this driver to send input up
40420Sstevel@tonic-gate * immediately, or that it can wait for normal
40430Sstevel@tonic-gate * processing that may or may not be done. Sun
40440Sstevel@tonic-gate * requires these for the mouse module.
40450Sstevel@tonic-gate * (XXX - for x86?)
40460Sstevel@tonic-gate */
40470Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
40480Sstevel@tonic-gate switch (*mp->b_rptr) {
40490Sstevel@tonic-gate
40500Sstevel@tonic-gate case MC_SERVICEIMM:
40510Sstevel@tonic-gate async->async_flags |= ASYNC_SERVICEIMM;
40520Sstevel@tonic-gate break;
40530Sstevel@tonic-gate
40540Sstevel@tonic-gate case MC_SERVICEDEF:
40550Sstevel@tonic-gate async->async_flags &= ~ASYNC_SERVICEIMM;
40560Sstevel@tonic-gate break;
40570Sstevel@tonic-gate }
40580Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
40590Sstevel@tonic-gate freemsg(mp);
40600Sstevel@tonic-gate }
40610Sstevel@tonic-gate break;
40620Sstevel@tonic-gate
40630Sstevel@tonic-gate case M_IOCDATA:
40645295Srandyf mutex_enter(&asy->asy_excl);
40655295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) {
40665295Srandyf mutex_exit(&asy->asy_excl);
40675295Srandyf async_iocdata(q, mp);
40685295Srandyf break;
40695295Srandyf }
40705295Srandyf async_put_suspq(asy, mp);
40715295Srandyf mutex_exit(&asy->asy_excl);
40720Sstevel@tonic-gate break;
40730Sstevel@tonic-gate
40740Sstevel@tonic-gate default:
40750Sstevel@tonic-gate freemsg(mp);
40760Sstevel@tonic-gate break;
40770Sstevel@tonic-gate }
40780Sstevel@tonic-gate return (0);
40790Sstevel@tonic-gate }
40800Sstevel@tonic-gate
40815295Srandyf static int
asywput(queue_t * q,mblk_t * mp)40825295Srandyf asywput(queue_t *q, mblk_t *mp)
40835295Srandyf {
40845295Srandyf return (asywputdo(q, mp, B_TRUE));
40855295Srandyf }
40865295Srandyf
40870Sstevel@tonic-gate /*
40880Sstevel@tonic-gate * Retry an "ioctl", now that "bufcall" claims we may be able to allocate
40890Sstevel@tonic-gate * the buffer we need.
40900Sstevel@tonic-gate */
40910Sstevel@tonic-gate static void
async_reioctl(void * unit)40920Sstevel@tonic-gate async_reioctl(void *unit)
40930Sstevel@tonic-gate {
40940Sstevel@tonic-gate int instance = (uintptr_t)unit;
40950Sstevel@tonic-gate struct asyncline *async;
40960Sstevel@tonic-gate struct asycom *asy;
40970Sstevel@tonic-gate queue_t *q;
40980Sstevel@tonic-gate mblk_t *mp;
40990Sstevel@tonic-gate
41000Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance);
41010Sstevel@tonic-gate ASSERT(asy != NULL);
41020Sstevel@tonic-gate async = asy->asy_priv;
41030Sstevel@tonic-gate
41040Sstevel@tonic-gate /*
41050Sstevel@tonic-gate * The bufcall is no longer pending.
41060Sstevel@tonic-gate */
41070Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
41080Sstevel@tonic-gate async->async_wbufcid = 0;
41090Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) {
41100Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
41110Sstevel@tonic-gate return;
41120Sstevel@tonic-gate }
41130Sstevel@tonic-gate if ((mp = async->async_ttycommon.t_iocpending) != NULL) {
41140Sstevel@tonic-gate /* not pending any more */
41150Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL;
41160Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
41170Sstevel@tonic-gate async_ioctl(async, q, mp);
41180Sstevel@tonic-gate } else
41190Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
41200Sstevel@tonic-gate }
41210Sstevel@tonic-gate
41220Sstevel@tonic-gate static void
async_iocdata(queue_t * q,mblk_t * mp)41230Sstevel@tonic-gate async_iocdata(queue_t *q, mblk_t *mp)
41240Sstevel@tonic-gate {
41250Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)q->q_ptr;
41260Sstevel@tonic-gate struct asycom *asy;
41270Sstevel@tonic-gate struct iocblk *ip;
41280Sstevel@tonic-gate struct copyresp *csp;
41290Sstevel@tonic-gate #ifdef DEBUG
41300Sstevel@tonic-gate int instance = UNIT(async->async_dev);
41310Sstevel@tonic-gate #endif
41320Sstevel@tonic-gate
41330Sstevel@tonic-gate asy = async->async_common;
41340Sstevel@tonic-gate ip = (struct iocblk *)mp->b_rptr;
41350Sstevel@tonic-gate csp = (struct copyresp *)mp->b_rptr;
41360Sstevel@tonic-gate
41370Sstevel@tonic-gate if (csp->cp_rval != 0) {
41380Sstevel@tonic-gate if (csp->cp_private)
41390Sstevel@tonic-gate freemsg(csp->cp_private);
41400Sstevel@tonic-gate freemsg(mp);
41410Sstevel@tonic-gate return;
41420Sstevel@tonic-gate }
41430Sstevel@tonic-gate
41440Sstevel@tonic-gate mutex_enter(&asy->asy_excl);
41450Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "async%d_iocdata: case %s\n",
41465295Srandyf instance,
41475295Srandyf csp->cp_cmd == TIOCMGET ? "TIOCMGET" :
41485295Srandyf csp->cp_cmd == TIOCMSET ? "TIOCMSET" :
41495295Srandyf csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" :
41505295Srandyf "TIOCMBIC");
41510Sstevel@tonic-gate switch (csp->cp_cmd) {
41520Sstevel@tonic-gate
41530Sstevel@tonic-gate case TIOCMGET:
41540Sstevel@tonic-gate if (mp->b_cont) {
41550Sstevel@tonic-gate freemsg(mp->b_cont);
41560Sstevel@tonic-gate mp->b_cont = NULL;
41570Sstevel@tonic-gate }
41580Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK;
41590Sstevel@tonic-gate ip->ioc_error = 0;
41600Sstevel@tonic-gate ip->ioc_count = 0;
41610Sstevel@tonic-gate ip->ioc_rval = 0;
41620Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + sizeof (struct iocblk);
41630Sstevel@tonic-gate break;
41640Sstevel@tonic-gate
41650Sstevel@tonic-gate case TIOCMSET:
41660Sstevel@tonic-gate case TIOCMBIS:
41670Sstevel@tonic-gate case TIOCMBIC:
41680Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi);
41695295Srandyf (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr),
41705295Srandyf csp->cp_cmd);
41710Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi);
41720Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0);
41730Sstevel@tonic-gate break;
41740Sstevel@tonic-gate
41750Sstevel@tonic-gate default:
41760Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK;
41770Sstevel@tonic-gate ip->ioc_error = EINVAL;
41780Sstevel@tonic-gate break;
41790Sstevel@tonic-gate }
41800Sstevel@tonic-gate qreply(q, mp);
41810Sstevel@tonic-gate mutex_exit(&asy->asy_excl);
41820Sstevel@tonic-gate }
41830Sstevel@tonic-gate
41840Sstevel@tonic-gate /*
41850Sstevel@tonic-gate * debugger/console support routines.
41860Sstevel@tonic-gate */
41870Sstevel@tonic-gate
41880Sstevel@tonic-gate /*
41890Sstevel@tonic-gate * put a character out
41900Sstevel@tonic-gate * Do not use interrupts. If char is LF, put out CR, LF.
41910Sstevel@tonic-gate */
41920Sstevel@tonic-gate static void
asyputchar(cons_polledio_arg_t arg,uchar_t c)41931762Slt200341 asyputchar(cons_polledio_arg_t arg, uchar_t c)
41940Sstevel@tonic-gate {
41950Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg;
41960Sstevel@tonic-gate
41970Sstevel@tonic-gate if (c == '\n')
41980Sstevel@tonic-gate asyputchar(arg, '\r');
41990Sstevel@tonic-gate
42001106Smrj while ((ddi_get8(asy->asy_iohandle,
42010Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XHRE) == 0) {
42020Sstevel@tonic-gate /* wait for xmit to finish */
42030Sstevel@tonic-gate drv_usecwait(10);
42040Sstevel@tonic-gate }
42050Sstevel@tonic-gate
42060Sstevel@tonic-gate /* put the character out */
42071106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, c);
42080Sstevel@tonic-gate }
42090Sstevel@tonic-gate
42100Sstevel@tonic-gate /*
42110Sstevel@tonic-gate * See if there's a character available. If no character is
42120Sstevel@tonic-gate * available, return 0. Run in polled mode, no interrupts.
42130Sstevel@tonic-gate */
42140Sstevel@tonic-gate static boolean_t
asyischar(cons_polledio_arg_t arg)42151762Slt200341 asyischar(cons_polledio_arg_t arg)
42160Sstevel@tonic-gate {
42170Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg;
42180Sstevel@tonic-gate
42195295Srandyf return ((ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & RCA)
42205295Srandyf != 0);
42210Sstevel@tonic-gate }
42220Sstevel@tonic-gate
42230Sstevel@tonic-gate /*
42240Sstevel@tonic-gate * Get a character. Run in polled mode, no interrupts.
42250Sstevel@tonic-gate */
42260Sstevel@tonic-gate static int
asygetchar(cons_polledio_arg_t arg)42271762Slt200341 asygetchar(cons_polledio_arg_t arg)
42280Sstevel@tonic-gate {
42290Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg;
42300Sstevel@tonic-gate
42310Sstevel@tonic-gate while (!asyischar(arg))
42320Sstevel@tonic-gate drv_usecwait(10);
42335295Srandyf return (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + DAT));
42340Sstevel@tonic-gate }
42350Sstevel@tonic-gate
42360Sstevel@tonic-gate /*
42370Sstevel@tonic-gate * Set or get the modem control status.
42380Sstevel@tonic-gate */
42390Sstevel@tonic-gate static int
asymctl(struct asycom * asy,int bits,int how)42400Sstevel@tonic-gate asymctl(struct asycom *asy, int bits, int how)
42410Sstevel@tonic-gate {
42420Sstevel@tonic-gate int mcr_r, msr_r;
42430Sstevel@tonic-gate int instance = asy->asy_unit;
42440Sstevel@tonic-gate
42450Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi));
42460Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl));
42470Sstevel@tonic-gate
42480Sstevel@tonic-gate /* Read Modem Control Registers */
42491106Smrj mcr_r = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
42500Sstevel@tonic-gate
42510Sstevel@tonic-gate switch (how) {
42520Sstevel@tonic-gate
42530Sstevel@tonic-gate case TIOCMSET:
42540Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM,
42555295Srandyf "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits);
42560Sstevel@tonic-gate mcr_r = bits; /* Set bits */
42570Sstevel@tonic-gate break;
42580Sstevel@tonic-gate
42590Sstevel@tonic-gate case TIOCMBIS:
42600Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIS, bits = %x\n",
42615295Srandyf instance, bits);
42620Sstevel@tonic-gate mcr_r |= bits; /* Mask in bits */
42630Sstevel@tonic-gate break;
42640Sstevel@tonic-gate
42650Sstevel@tonic-gate case TIOCMBIC:
42660Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIC, bits = %x\n",
42675295Srandyf instance, bits);
42680Sstevel@tonic-gate mcr_r &= ~bits; /* Mask out bits */
42690Sstevel@tonic-gate break;
42700Sstevel@tonic-gate
42710Sstevel@tonic-gate case TIOCMGET:
42720Sstevel@tonic-gate /* Read Modem Status Registers */
42730Sstevel@tonic-gate /*
42740Sstevel@tonic-gate * If modem interrupts are enabled, we return the
42750Sstevel@tonic-gate * saved value of msr. We read MSR only in async_msint()
42760Sstevel@tonic-gate */
42771106Smrj if (ddi_get8(asy->asy_iohandle,
42780Sstevel@tonic-gate asy->asy_ioaddr + ICR) & MIEN) {
42790Sstevel@tonic-gate msr_r = asy->asy_msr;
42800Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM,
42815295Srandyf "asy%dmctl: TIOCMGET, read msr_r = %x\n",
42825295Srandyf instance, msr_r);
42830Sstevel@tonic-gate } else {
42841106Smrj msr_r = ddi_get8(asy->asy_iohandle,
42855295Srandyf asy->asy_ioaddr + MSR);
42860Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM,
42875295Srandyf "asy%dmctl: TIOCMGET, read MSR = %x\n",
42885295Srandyf instance, msr_r);
42890Sstevel@tonic-gate }
42900Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dtodm: modem_lines = %x\n",
42915295Srandyf instance, asytodm(mcr_r, msr_r));
42920Sstevel@tonic-gate return (asytodm(mcr_r, msr_r));
42930Sstevel@tonic-gate }
42940Sstevel@tonic-gate
42951106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr_r);
42960Sstevel@tonic-gate
42970Sstevel@tonic-gate return (mcr_r);
42980Sstevel@tonic-gate }
42990Sstevel@tonic-gate
43000Sstevel@tonic-gate static int
asytodm(int mcr_r,int msr_r)43010Sstevel@tonic-gate asytodm(int mcr_r, int msr_r)
43020Sstevel@tonic-gate {
43030Sstevel@tonic-gate int b = 0;
43040Sstevel@tonic-gate
43050Sstevel@tonic-gate /* MCR registers */
43060Sstevel@tonic-gate if (mcr_r & RTS)
43070Sstevel@tonic-gate b |= TIOCM_RTS;
43080Sstevel@tonic-gate
43090Sstevel@tonic-gate if (mcr_r & DTR)
43100Sstevel@tonic-gate b |= TIOCM_DTR;
43110Sstevel@tonic-gate
43120Sstevel@tonic-gate /* MSR registers */
43130Sstevel@tonic-gate if (msr_r & DCD)
43140Sstevel@tonic-gate b |= TIOCM_CAR;
43150Sstevel@tonic-gate
43160Sstevel@tonic-gate if (msr_r & CTS)
43170Sstevel@tonic-gate b |= TIOCM_CTS;
43180Sstevel@tonic-gate
43190Sstevel@tonic-gate if (msr_r & DSR)
43200Sstevel@tonic-gate b |= TIOCM_DSR;
43210Sstevel@tonic-gate
43220Sstevel@tonic-gate if (msr_r & RI)
43230Sstevel@tonic-gate b |= TIOCM_RNG;
43240Sstevel@tonic-gate return (b);
43250Sstevel@tonic-gate }
43260Sstevel@tonic-gate
43270Sstevel@tonic-gate static int
dmtoasy(int bits)43280Sstevel@tonic-gate dmtoasy(int bits)
43290Sstevel@tonic-gate {
43300Sstevel@tonic-gate int b = 0;
43310Sstevel@tonic-gate
43320Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, "dmtoasy: bits = %x\n", bits);
43330Sstevel@tonic-gate #ifdef CAN_NOT_SET /* only DTR and RTS can be set */
43340Sstevel@tonic-gate if (bits & TIOCM_CAR)
43350Sstevel@tonic-gate b |= DCD;
43360Sstevel@tonic-gate if (bits & TIOCM_CTS)
43370Sstevel@tonic-gate b |= CTS;
43380Sstevel@tonic-gate if (bits & TIOCM_DSR)
43390Sstevel@tonic-gate b |= DSR;
43400Sstevel@tonic-gate if (bits & TIOCM_RNG)
43410Sstevel@tonic-gate b |= RI;
43420Sstevel@tonic-gate #endif
43430Sstevel@tonic-gate
43440Sstevel@tonic-gate if (bits & TIOCM_RTS) {
43450Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & RTS\n");
43460Sstevel@tonic-gate b |= RTS;
43470Sstevel@tonic-gate }
43480Sstevel@tonic-gate if (bits & TIOCM_DTR) {
43490Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & DTR\n");
43500Sstevel@tonic-gate b |= DTR;
43510Sstevel@tonic-gate }
43520Sstevel@tonic-gate
43530Sstevel@tonic-gate return (b);
43540Sstevel@tonic-gate }
43550Sstevel@tonic-gate
43560Sstevel@tonic-gate static void
asyerror(int level,const char * fmt,...)43570Sstevel@tonic-gate asyerror(int level, const char *fmt, ...)
43580Sstevel@tonic-gate {
43590Sstevel@tonic-gate va_list adx;
43600Sstevel@tonic-gate static time_t last;
43610Sstevel@tonic-gate static const char *lastfmt;
43620Sstevel@tonic-gate time_t now;
43630Sstevel@tonic-gate
43640Sstevel@tonic-gate /*
43650Sstevel@tonic-gate * Don't print the same error message too often.
43660Sstevel@tonic-gate * Print the message only if we have not printed the
43670Sstevel@tonic-gate * message within the last second.
43680Sstevel@tonic-gate * Note: that fmt cannot be a pointer to a string
43690Sstevel@tonic-gate * stored on the stack. The fmt pointer
43700Sstevel@tonic-gate * must be in the data segment otherwise lastfmt would point
43710Sstevel@tonic-gate * to non-sense.
43720Sstevel@tonic-gate */
43730Sstevel@tonic-gate now = gethrestime_sec();
43740Sstevel@tonic-gate if (last == now && lastfmt == fmt)
43750Sstevel@tonic-gate return;
43760Sstevel@tonic-gate
43770Sstevel@tonic-gate last = now;
43780Sstevel@tonic-gate lastfmt = fmt;
43790Sstevel@tonic-gate
43800Sstevel@tonic-gate va_start(adx, fmt);
43810Sstevel@tonic-gate vcmn_err(level, fmt, adx);
43820Sstevel@tonic-gate va_end(adx);
43830Sstevel@tonic-gate }
43840Sstevel@tonic-gate
43850Sstevel@tonic-gate /*
43860Sstevel@tonic-gate * asy_parse_mode(dev_info_t *devi, struct asycom *asy)
43870Sstevel@tonic-gate * The value of this property is in the form of "9600,8,n,1,-"
43880Sstevel@tonic-gate * 1) speed: 9600, 4800, ...
43890Sstevel@tonic-gate * 2) data bits
43900Sstevel@tonic-gate * 3) parity: n(none), e(even), o(odd)
43910Sstevel@tonic-gate * 4) stop bits
43920Sstevel@tonic-gate * 5) handshake: -(none), h(hardware: rts/cts), s(software: xon/off)
43930Sstevel@tonic-gate *
43940Sstevel@tonic-gate * This parsing came from a SPARCstation eeprom.
43950Sstevel@tonic-gate */
43960Sstevel@tonic-gate static void
asy_parse_mode(dev_info_t * devi,struct asycom * asy)43970Sstevel@tonic-gate asy_parse_mode(dev_info_t *devi, struct asycom *asy)
43980Sstevel@tonic-gate {
43990Sstevel@tonic-gate char name[40];
44000Sstevel@tonic-gate char val[40];
44010Sstevel@tonic-gate int len;
44020Sstevel@tonic-gate int ret;
44030Sstevel@tonic-gate char *p;
44040Sstevel@tonic-gate char *p1;
44050Sstevel@tonic-gate
44060Sstevel@tonic-gate ASSERT(asy->asy_com_port != 0);
44070Sstevel@tonic-gate
44080Sstevel@tonic-gate /*
44090Sstevel@tonic-gate * Parse the ttyx-mode property
44100Sstevel@tonic-gate */
44110Sstevel@tonic-gate (void) sprintf(name, "tty%c-mode", asy->asy_com_port + 'a' - 1);
44120Sstevel@tonic-gate len = sizeof (val);
44130Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
44140Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) {
44150Sstevel@tonic-gate (void) sprintf(name, "com%c-mode", asy->asy_com_port + '0');
44160Sstevel@tonic-gate len = sizeof (val);
44170Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len);
44180Sstevel@tonic-gate }
44190Sstevel@tonic-gate
44200Sstevel@tonic-gate /* no property to parse */
44210Sstevel@tonic-gate asy->asy_cflag = 0;
44220Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS)
44230Sstevel@tonic-gate return;
44240Sstevel@tonic-gate
44250Sstevel@tonic-gate p = val;
44260Sstevel@tonic-gate /* ---- baud rate ---- */
44270Sstevel@tonic-gate asy->asy_cflag = CREAD|B9600; /* initial default */
44280Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) {
44290Sstevel@tonic-gate *p1++ = '\0';
44300Sstevel@tonic-gate } else {
44310Sstevel@tonic-gate asy->asy_cflag |= BITS8; /* add default bits */
44320Sstevel@tonic-gate return;
44330Sstevel@tonic-gate }
44340Sstevel@tonic-gate
44350Sstevel@tonic-gate if (strcmp(p, "110") == 0)
44360Sstevel@tonic-gate asy->asy_bidx = B110;
44370Sstevel@tonic-gate else if (strcmp(p, "150") == 0)
44380Sstevel@tonic-gate asy->asy_bidx = B150;
44390Sstevel@tonic-gate else if (strcmp(p, "300") == 0)
44400Sstevel@tonic-gate asy->asy_bidx = B300;
44410Sstevel@tonic-gate else if (strcmp(p, "600") == 0)
44420Sstevel@tonic-gate asy->asy_bidx = B600;
44430Sstevel@tonic-gate else if (strcmp(p, "1200") == 0)
44440Sstevel@tonic-gate asy->asy_bidx = B1200;
44450Sstevel@tonic-gate else if (strcmp(p, "2400") == 0)
44460Sstevel@tonic-gate asy->asy_bidx = B2400;
44470Sstevel@tonic-gate else if (strcmp(p, "4800") == 0)
44480Sstevel@tonic-gate asy->asy_bidx = B4800;
44490Sstevel@tonic-gate else if (strcmp(p, "9600") == 0)
44500Sstevel@tonic-gate asy->asy_bidx = B9600;
44510Sstevel@tonic-gate else if (strcmp(p, "19200") == 0)
44520Sstevel@tonic-gate asy->asy_bidx = B19200;
44530Sstevel@tonic-gate else if (strcmp(p, "38400") == 0)
44540Sstevel@tonic-gate asy->asy_bidx = B38400;
44550Sstevel@tonic-gate else if (strcmp(p, "57600") == 0)
44560Sstevel@tonic-gate asy->asy_bidx = B57600;
44570Sstevel@tonic-gate else if (strcmp(p, "115200") == 0)
44580Sstevel@tonic-gate asy->asy_bidx = B115200;
44590Sstevel@tonic-gate else
44600Sstevel@tonic-gate asy->asy_bidx = B9600;
44610Sstevel@tonic-gate
44620Sstevel@tonic-gate asy->asy_cflag &= ~CBAUD;
44630Sstevel@tonic-gate if (asy->asy_bidx > CBAUD) { /* > 38400 uses the CBAUDEXT bit */
44640Sstevel@tonic-gate asy->asy_cflag |= CBAUDEXT;
44650Sstevel@tonic-gate asy->asy_cflag |= asy->asy_bidx - CBAUD - 1;
44660Sstevel@tonic-gate } else {
44670Sstevel@tonic-gate asy->asy_cflag |= asy->asy_bidx;
44680Sstevel@tonic-gate }
44690Sstevel@tonic-gate
44700Sstevel@tonic-gate ASSERT(asy->asy_bidx == BAUDINDEX(asy->asy_cflag));
44710Sstevel@tonic-gate
44720Sstevel@tonic-gate /* ---- Next item is data bits ---- */
44730Sstevel@tonic-gate p = p1;
44740Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) {
44750Sstevel@tonic-gate *p1++ = '\0';
44760Sstevel@tonic-gate } else {
44770Sstevel@tonic-gate asy->asy_cflag |= BITS8; /* add default bits */
44780Sstevel@tonic-gate return;
44790Sstevel@tonic-gate }
44800Sstevel@tonic-gate switch (*p) {
44810Sstevel@tonic-gate default:
44820Sstevel@tonic-gate case '8':
44830Sstevel@tonic-gate asy->asy_cflag |= CS8;
44840Sstevel@tonic-gate asy->asy_lcr = BITS8;
44850Sstevel@tonic-gate break;
44860Sstevel@tonic-gate case '7':
44870Sstevel@tonic-gate asy->asy_cflag |= CS7;
44880Sstevel@tonic-gate asy->asy_lcr = BITS7;
44890Sstevel@tonic-gate break;
44900Sstevel@tonic-gate case '6':
44910Sstevel@tonic-gate asy->asy_cflag |= CS6;
44920Sstevel@tonic-gate asy->asy_lcr = BITS6;
44930Sstevel@tonic-gate break;
44940Sstevel@tonic-gate case '5':
44950Sstevel@tonic-gate /* LINTED: CS5 is currently zero (but might change) */
44960Sstevel@tonic-gate asy->asy_cflag |= CS5;
44970Sstevel@tonic-gate asy->asy_lcr = BITS5;
44980Sstevel@tonic-gate break;
44990Sstevel@tonic-gate }
45000Sstevel@tonic-gate
45010Sstevel@tonic-gate /* ---- Parity info ---- */
45020Sstevel@tonic-gate p = p1;
45030Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) {
45040Sstevel@tonic-gate *p1++ = '\0';
45050Sstevel@tonic-gate } else {
45060Sstevel@tonic-gate return;
45070Sstevel@tonic-gate }
45080Sstevel@tonic-gate switch (*p) {
45090Sstevel@tonic-gate default:
45100Sstevel@tonic-gate case 'n':
45110Sstevel@tonic-gate break;
45120Sstevel@tonic-gate case 'e':
45130Sstevel@tonic-gate asy->asy_cflag |= PARENB;
45140Sstevel@tonic-gate asy->asy_lcr |= PEN; break;
45150Sstevel@tonic-gate case 'o':
45160Sstevel@tonic-gate asy->asy_cflag |= PARENB|PARODD;
45170Sstevel@tonic-gate asy->asy_lcr |= PEN|EPS;
45180Sstevel@tonic-gate break;
45190Sstevel@tonic-gate }
45200Sstevel@tonic-gate
45210Sstevel@tonic-gate /* ---- Find stop bits ---- */
45220Sstevel@tonic-gate p = p1;
45230Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) {
45240Sstevel@tonic-gate *p1++ = '\0';
45250Sstevel@tonic-gate } else {
45260Sstevel@tonic-gate return;
45270Sstevel@tonic-gate }
45280Sstevel@tonic-gate if (*p == '2') {
45290Sstevel@tonic-gate asy->asy_cflag |= CSTOPB;
45300Sstevel@tonic-gate asy->asy_lcr |= STB;
45310Sstevel@tonic-gate }
45320Sstevel@tonic-gate
45330Sstevel@tonic-gate /* ---- handshake is next ---- */
45340Sstevel@tonic-gate p = p1;
45350Sstevel@tonic-gate if (p) {
45360Sstevel@tonic-gate if ((p1 = strchr(p, ',')) != 0)
45370Sstevel@tonic-gate *p1++ = '\0';
45380Sstevel@tonic-gate
45390Sstevel@tonic-gate if (*p == 'h')
45400Sstevel@tonic-gate asy->asy_cflag |= CRTSCTS;
45410Sstevel@tonic-gate else if (*p == 's')
45420Sstevel@tonic-gate asy->asy_cflag |= CRTSXOFF;
45430Sstevel@tonic-gate }
45440Sstevel@tonic-gate }
45450Sstevel@tonic-gate
45460Sstevel@tonic-gate /*
45470Sstevel@tonic-gate * Check for abort character sequence
45480Sstevel@tonic-gate */
45490Sstevel@tonic-gate static boolean_t
abort_charseq_recognize(uchar_t ch)45500Sstevel@tonic-gate abort_charseq_recognize(uchar_t ch)
45510Sstevel@tonic-gate {
45520Sstevel@tonic-gate static int state = 0;
45530Sstevel@tonic-gate #define CNTRL(c) ((c)&037)
45540Sstevel@tonic-gate static char sequence[] = { '\r', '~', CNTRL('b') };
45550Sstevel@tonic-gate
45560Sstevel@tonic-gate if (ch == sequence[state]) {
45570Sstevel@tonic-gate if (++state >= sizeof (sequence)) {
45580Sstevel@tonic-gate state = 0;
45590Sstevel@tonic-gate return (B_TRUE);
45600Sstevel@tonic-gate }
45610Sstevel@tonic-gate } else {
45620Sstevel@tonic-gate state = (ch == sequence[0]) ? 1 : 0;
45630Sstevel@tonic-gate }
45640Sstevel@tonic-gate return (B_FALSE);
45650Sstevel@tonic-gate }
45660Sstevel@tonic-gate
45670Sstevel@tonic-gate /*
45680Sstevel@tonic-gate * Flow control functions
45690Sstevel@tonic-gate */
45700Sstevel@tonic-gate /*
45710Sstevel@tonic-gate * Software input flow control
45720Sstevel@tonic-gate * This function can execute software input flow control sucessfully
45730Sstevel@tonic-gate * at most of situations except that the line is in BREAK status
45740Sstevel@tonic-gate * (timed and untimed break).
45750Sstevel@tonic-gate * INPUT VALUE of onoff:
45760Sstevel@tonic-gate * FLOW_START means to send out a XON char
45770Sstevel@tonic-gate * and clear SW input flow control flag.
45780Sstevel@tonic-gate * FLOW_STOP means to send out a XOFF char
45790Sstevel@tonic-gate * and set SW input flow control flag.
45800Sstevel@tonic-gate * FLOW_CHECK means to check whether there is pending XON/XOFF
45810Sstevel@tonic-gate * if it is true, send it out.
45820Sstevel@tonic-gate * INPUT VALUE of type:
45830Sstevel@tonic-gate * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER
45840Sstevel@tonic-gate * IN_FLOW_STREAMS means flow control is due to STREAMS
45850Sstevel@tonic-gate * IN_FLOW_USER means flow control is due to user's commands
45860Sstevel@tonic-gate * RETURN VALUE: B_FALSE means no flow control char is sent
45870Sstevel@tonic-gate * B_TRUE means one flow control char is sent
45880Sstevel@tonic-gate */
45890Sstevel@tonic-gate static boolean_t
async_flowcontrol_sw_input(struct asycom * asy,async_flowc_action onoff,int type)45900Sstevel@tonic-gate async_flowcontrol_sw_input(struct asycom *asy, async_flowc_action onoff,
45910Sstevel@tonic-gate int type)
45920Sstevel@tonic-gate {
45930Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
45940Sstevel@tonic-gate int instance = UNIT(async->async_dev);
45950Sstevel@tonic-gate int rval = B_FALSE;
45960Sstevel@tonic-gate
45970Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi));
45980Sstevel@tonic-gate
45990Sstevel@tonic-gate if (!(async->async_ttycommon.t_iflag & IXOFF))
46000Sstevel@tonic-gate return (rval);
46010Sstevel@tonic-gate
46020Sstevel@tonic-gate /*
46030Sstevel@tonic-gate * If we get this far, then we know IXOFF is set.
46040Sstevel@tonic-gate */
46050Sstevel@tonic-gate switch (onoff) {
46060Sstevel@tonic-gate case FLOW_STOP:
46070Sstevel@tonic-gate async->async_inflow_source |= type;
46080Sstevel@tonic-gate
46090Sstevel@tonic-gate /*
46100Sstevel@tonic-gate * We'll send an XOFF character for each of up to
46110Sstevel@tonic-gate * three different input flow control attempts to stop input.
46120Sstevel@tonic-gate * If we already send out one XOFF, but FLOW_STOP comes again,
46130Sstevel@tonic-gate * it seems that input flow control becomes more serious,
46140Sstevel@tonic-gate * then send XOFF again.
46150Sstevel@tonic-gate */
46160Sstevel@tonic-gate if (async->async_inflow_source & (IN_FLOW_RINGBUFF |
46170Sstevel@tonic-gate IN_FLOW_STREAMS | IN_FLOW_USER))
46180Sstevel@tonic-gate async->async_flags |= ASYNC_SW_IN_FLOW |
46190Sstevel@tonic-gate ASYNC_SW_IN_NEEDED;
46200Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_SFLOW, "async%d: input sflow stop, "
46210Sstevel@tonic-gate "type = %x\n", instance, async->async_inflow_source);
46220Sstevel@tonic-gate break;
46230Sstevel@tonic-gate case FLOW_START:
46240Sstevel@tonic-gate async->async_inflow_source &= ~type;
46250Sstevel@tonic-gate if (async->async_inflow_source == 0) {
46260Sstevel@tonic-gate async->async_flags = (async->async_flags &
46270Sstevel@tonic-gate ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED;
46280Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: "
46290Sstevel@tonic-gate "input sflow start\n", instance);
46300Sstevel@tonic-gate }
46310Sstevel@tonic-gate break;
46320Sstevel@tonic-gate default:
46330Sstevel@tonic-gate break;
46340Sstevel@tonic-gate }
46350Sstevel@tonic-gate
46360Sstevel@tonic-gate if (((async->async_flags & (ASYNC_SW_IN_NEEDED | ASYNC_BREAK |
46370Sstevel@tonic-gate ASYNC_OUT_SUSPEND)) == ASYNC_SW_IN_NEEDED) &&
46381106Smrj (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE)) {
46390Sstevel@tonic-gate /*
46400Sstevel@tonic-gate * If we get this far, then we know we need to send out
46410Sstevel@tonic-gate * XON or XOFF char.
46420Sstevel@tonic-gate */
46430Sstevel@tonic-gate async->async_flags = (async->async_flags &
46440Sstevel@tonic-gate ~ASYNC_SW_IN_NEEDED) | ASYNC_BUSY;
46451106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT,
46460Sstevel@tonic-gate async->async_flags & ASYNC_SW_IN_FLOW ?
46470Sstevel@tonic-gate async->async_stopc : async->async_startc);
46480Sstevel@tonic-gate rval = B_TRUE;
46490Sstevel@tonic-gate }
46500Sstevel@tonic-gate return (rval);
46510Sstevel@tonic-gate }
46520Sstevel@tonic-gate
46530Sstevel@tonic-gate /*
46540Sstevel@tonic-gate * Software output flow control
46550Sstevel@tonic-gate * This function can be executed sucessfully at any situation.
46560Sstevel@tonic-gate * It does not handle HW, and just change the SW output flow control flag.
46570Sstevel@tonic-gate * INPUT VALUE of onoff:
46580Sstevel@tonic-gate * FLOW_START means to clear SW output flow control flag,
46590Sstevel@tonic-gate * also combine with HW output flow control status to
46600Sstevel@tonic-gate * determine if we need to set ASYNC_OUT_FLW_RESUME.
46610Sstevel@tonic-gate * FLOW_STOP means to set SW output flow control flag,
46620Sstevel@tonic-gate * also clear ASYNC_OUT_FLW_RESUME.
46630Sstevel@tonic-gate */
46640Sstevel@tonic-gate static void
async_flowcontrol_sw_output(struct asycom * asy,async_flowc_action onoff)46650Sstevel@tonic-gate async_flowcontrol_sw_output(struct asycom *asy, async_flowc_action onoff)
46660Sstevel@tonic-gate {
46670Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
46680Sstevel@tonic-gate int instance = UNIT(async->async_dev);
46690Sstevel@tonic-gate
46700Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi));
46710Sstevel@tonic-gate
46720Sstevel@tonic-gate if (!(async->async_ttycommon.t_iflag & IXON))
46730Sstevel@tonic-gate return;
46740Sstevel@tonic-gate
46750Sstevel@tonic-gate switch (onoff) {
46760Sstevel@tonic-gate case FLOW_STOP:
46770Sstevel@tonic-gate async->async_flags |= ASYNC_SW_OUT_FLW;
46780Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
46790Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow stop\n",
46800Sstevel@tonic-gate instance);
46810Sstevel@tonic-gate break;
46820Sstevel@tonic-gate case FLOW_START:
46830Sstevel@tonic-gate async->async_flags &= ~ASYNC_SW_OUT_FLW;
46840Sstevel@tonic-gate if (!(async->async_flags & ASYNC_HW_OUT_FLW))
46850Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_FLW_RESUME;
46860Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow start\n",
46870Sstevel@tonic-gate instance);
46880Sstevel@tonic-gate break;
46890Sstevel@tonic-gate default:
46900Sstevel@tonic-gate break;
46910Sstevel@tonic-gate }
46920Sstevel@tonic-gate }
46930Sstevel@tonic-gate
46940Sstevel@tonic-gate /*
46950Sstevel@tonic-gate * Hardware input flow control
46960Sstevel@tonic-gate * This function can be executed sucessfully at any situation.
46970Sstevel@tonic-gate * It directly changes RTS depending on input parameter onoff.
46980Sstevel@tonic-gate * INPUT VALUE of onoff:
46990Sstevel@tonic-gate * FLOW_START means to clear HW input flow control flag,
47000Sstevel@tonic-gate * and pull up RTS if it is low.
47010Sstevel@tonic-gate * FLOW_STOP means to set HW input flow control flag,
47020Sstevel@tonic-gate * and low RTS if it is high.
47030Sstevel@tonic-gate * INPUT VALUE of type:
47040Sstevel@tonic-gate * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER
47050Sstevel@tonic-gate * IN_FLOW_STREAMS means flow control is due to STREAMS
47060Sstevel@tonic-gate * IN_FLOW_USER means flow control is due to user's commands
47070Sstevel@tonic-gate */
47080Sstevel@tonic-gate static void
async_flowcontrol_hw_input(struct asycom * asy,async_flowc_action onoff,int type)47090Sstevel@tonic-gate async_flowcontrol_hw_input(struct asycom *asy, async_flowc_action onoff,
47100Sstevel@tonic-gate int type)
47110Sstevel@tonic-gate {
47120Sstevel@tonic-gate uchar_t mcr;
47130Sstevel@tonic-gate uchar_t flag;
47140Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
47150Sstevel@tonic-gate int instance = UNIT(async->async_dev);
47160Sstevel@tonic-gate
47170Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi));
47180Sstevel@tonic-gate
47190Sstevel@tonic-gate if (!(async->async_ttycommon.t_cflag & CRTSXOFF))
47200Sstevel@tonic-gate return;
47210Sstevel@tonic-gate
47220Sstevel@tonic-gate switch (onoff) {
47230Sstevel@tonic-gate case FLOW_STOP:
47240Sstevel@tonic-gate async->async_inflow_source |= type;
47250Sstevel@tonic-gate if (async->async_inflow_source & (IN_FLOW_RINGBUFF |
47260Sstevel@tonic-gate IN_FLOW_STREAMS | IN_FLOW_USER))
47270Sstevel@tonic-gate async->async_flags |= ASYNC_HW_IN_FLOW;
47280Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_HFLOW, "async%d: input hflow stop, "
47290Sstevel@tonic-gate "type = %x\n", instance, async->async_inflow_source);
47300Sstevel@tonic-gate break;
47310Sstevel@tonic-gate case FLOW_START:
47320Sstevel@tonic-gate async->async_inflow_source &= ~type;
47330Sstevel@tonic-gate if (async->async_inflow_source == 0) {
47340Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_IN_FLOW;
47350Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: "
47360Sstevel@tonic-gate "input hflow start\n", instance);
47370Sstevel@tonic-gate }
47380Sstevel@tonic-gate break;
47390Sstevel@tonic-gate default:
47400Sstevel@tonic-gate break;
47410Sstevel@tonic-gate }
47421106Smrj mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR);
47430Sstevel@tonic-gate flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS;
47440Sstevel@tonic-gate
47450Sstevel@tonic-gate if (((mcr ^ flag) & RTS) != 0) {
47461106Smrj ddi_put8(asy->asy_iohandle,
47470Sstevel@tonic-gate asy->asy_ioaddr + MCR, (mcr ^ RTS));
47480Sstevel@tonic-gate }
47490Sstevel@tonic-gate }
47500Sstevel@tonic-gate
47510Sstevel@tonic-gate /*
47520Sstevel@tonic-gate * Hardware output flow control
47530Sstevel@tonic-gate * This function can execute HW output flow control sucessfully
47540Sstevel@tonic-gate * at any situation.
47550Sstevel@tonic-gate * It doesn't really change RTS, and just change
47560Sstevel@tonic-gate * HW output flow control flag depending on CTS status.
47570Sstevel@tonic-gate * INPUT VALUE of onoff:
47580Sstevel@tonic-gate * FLOW_START means to clear HW output flow control flag.
47590Sstevel@tonic-gate * also combine with SW output flow control status to
47600Sstevel@tonic-gate * determine if we need to set ASYNC_OUT_FLW_RESUME.
47610Sstevel@tonic-gate * FLOW_STOP means to set HW output flow control flag.
47620Sstevel@tonic-gate * also clear ASYNC_OUT_FLW_RESUME.
47630Sstevel@tonic-gate */
47640Sstevel@tonic-gate static void
async_flowcontrol_hw_output(struct asycom * asy,async_flowc_action onoff)47650Sstevel@tonic-gate async_flowcontrol_hw_output(struct asycom *asy, async_flowc_action onoff)
47660Sstevel@tonic-gate {
47670Sstevel@tonic-gate struct asyncline *async = asy->asy_priv;
47680Sstevel@tonic-gate int instance = UNIT(async->async_dev);
47690Sstevel@tonic-gate
47700Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi));
47710Sstevel@tonic-gate
47720Sstevel@tonic-gate if (!(async->async_ttycommon.t_cflag & CRTSCTS))
47730Sstevel@tonic-gate return;
47740Sstevel@tonic-gate
47750Sstevel@tonic-gate switch (onoff) {
47760Sstevel@tonic-gate case FLOW_STOP:
47770Sstevel@tonic-gate async->async_flags |= ASYNC_HW_OUT_FLW;
47780Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME;
47790Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow stop\n",
47800Sstevel@tonic-gate instance);
47810Sstevel@tonic-gate break;
47820Sstevel@tonic-gate case FLOW_START:
47830Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_OUT_FLW;
47840Sstevel@tonic-gate if (!(async->async_flags & ASYNC_SW_OUT_FLW))
47850Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_FLW_RESUME;
47860Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow start\n",
47870Sstevel@tonic-gate instance);
47880Sstevel@tonic-gate break;
47890Sstevel@tonic-gate default:
47900Sstevel@tonic-gate break;
47910Sstevel@tonic-gate }
47920Sstevel@tonic-gate }
47937656SSherry.Moore@Sun.COM
47947656SSherry.Moore@Sun.COM
47957656SSherry.Moore@Sun.COM /*
47967656SSherry.Moore@Sun.COM * quiesce(9E) entry point.
47977656SSherry.Moore@Sun.COM *
47987656SSherry.Moore@Sun.COM * This function is called when the system is single-threaded at high
47997656SSherry.Moore@Sun.COM * PIL with preemption disabled. Therefore, this function must not be
48007656SSherry.Moore@Sun.COM * blocked.
48017656SSherry.Moore@Sun.COM *
48027656SSherry.Moore@Sun.COM * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure.
48037656SSherry.Moore@Sun.COM * DDI_FAILURE indicates an error condition and should almost never happen.
48047656SSherry.Moore@Sun.COM */
48057656SSherry.Moore@Sun.COM static int
asyquiesce(dev_info_t * devi)48067656SSherry.Moore@Sun.COM asyquiesce(dev_info_t *devi)
48077656SSherry.Moore@Sun.COM {
48087656SSherry.Moore@Sun.COM int instance;
48097656SSherry.Moore@Sun.COM struct asycom *asy;
48107656SSherry.Moore@Sun.COM
48117656SSherry.Moore@Sun.COM instance = ddi_get_instance(devi); /* find out which unit */
48127656SSherry.Moore@Sun.COM
48137656SSherry.Moore@Sun.COM asy = ddi_get_soft_state(asy_soft_state, instance);
48147656SSherry.Moore@Sun.COM if (asy == NULL)
48157656SSherry.Moore@Sun.COM return (DDI_FAILURE);
48167656SSherry.Moore@Sun.COM
48177656SSherry.Moore@Sun.COM /* disable all interrupts */
48187656SSherry.Moore@Sun.COM ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0);
48197656SSherry.Moore@Sun.COM
48207656SSherry.Moore@Sun.COM /* reset the FIFO */
48217656SSherry.Moore@Sun.COM asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH);
48227656SSherry.Moore@Sun.COM
48237656SSherry.Moore@Sun.COM return (DDI_SUCCESS);
48247656SSherry.Moore@Sun.COM }
4825