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 /* 276990Sgd78059 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 280Sstevel@tonic-gate * Use is subject to license terms. 290Sstevel@tonic-gate */ 300Sstevel@tonic-gate 310Sstevel@tonic-gate 320Sstevel@tonic-gate /* 330Sstevel@tonic-gate * Serial I/O driver for 8250/16450/16550A/16650/16750 chips. 340Sstevel@tonic-gate */ 350Sstevel@tonic-gate 360Sstevel@tonic-gate #include <sys/param.h> 370Sstevel@tonic-gate #include <sys/types.h> 380Sstevel@tonic-gate #include <sys/signal.h> 390Sstevel@tonic-gate #include <sys/stream.h> 400Sstevel@tonic-gate #include <sys/termio.h> 410Sstevel@tonic-gate #include <sys/errno.h> 420Sstevel@tonic-gate #include <sys/file.h> 430Sstevel@tonic-gate #include <sys/cmn_err.h> 440Sstevel@tonic-gate #include <sys/stropts.h> 450Sstevel@tonic-gate #include <sys/strsubr.h> 460Sstevel@tonic-gate #include <sys/strtty.h> 470Sstevel@tonic-gate #include <sys/debug.h> 480Sstevel@tonic-gate #include <sys/kbio.h> 490Sstevel@tonic-gate #include <sys/cred.h> 500Sstevel@tonic-gate #include <sys/stat.h> 510Sstevel@tonic-gate #include <sys/consdev.h> 520Sstevel@tonic-gate #include <sys/mkdev.h> 530Sstevel@tonic-gate #include <sys/kmem.h> 540Sstevel@tonic-gate #include <sys/cred.h> 550Sstevel@tonic-gate #include <sys/strsun.h> 560Sstevel@tonic-gate #ifdef DEBUG 570Sstevel@tonic-gate #include <sys/promif.h> 580Sstevel@tonic-gate #endif 590Sstevel@tonic-gate #include <sys/modctl.h> 600Sstevel@tonic-gate #include <sys/ddi.h> 610Sstevel@tonic-gate #include <sys/sunddi.h> 623359Smyers #include <sys/pci.h> 630Sstevel@tonic-gate #include <sys/asy.h> 640Sstevel@tonic-gate #include <sys/policy.h> 650Sstevel@tonic-gate 660Sstevel@tonic-gate /* 670Sstevel@tonic-gate * set the RX FIFO trigger_level to half the RX FIFO size for now 680Sstevel@tonic-gate * we may want to make this configurable later. 690Sstevel@tonic-gate */ 700Sstevel@tonic-gate static int asy_trig_level = FIFO_TRIG_8; 710Sstevel@tonic-gate 720Sstevel@tonic-gate int asy_drain_check = 15000000; /* tunable: exit drain check time */ 730Sstevel@tonic-gate int asy_min_dtr_low = 500000; /* tunable: minimum DTR down time */ 740Sstevel@tonic-gate int asy_min_utbrk = 100000; /* tunable: minumum untimed brk time */ 750Sstevel@tonic-gate 760Sstevel@tonic-gate int asymaxchip = ASY16750; /* tunable: limit chip support we look for */ 770Sstevel@tonic-gate 780Sstevel@tonic-gate /* 790Sstevel@tonic-gate * Just in case someone has a chip with broken loopback mode, we provide a 800Sstevel@tonic-gate * means to disable the loopback test. By default, we only loopback test 810Sstevel@tonic-gate * UARTs which look like they have FIFOs bigger than 16 bytes. 820Sstevel@tonic-gate * Set to 0 to suppress test, or to 2 to enable test on any size FIFO. 830Sstevel@tonic-gate */ 840Sstevel@tonic-gate int asy_fifo_test = 1; /* tunable: set to 0, 1, or 2 */ 850Sstevel@tonic-gate 860Sstevel@tonic-gate /* 870Sstevel@tonic-gate * Allow ability to switch off testing of the scratch register. 880Sstevel@tonic-gate * Some UART emulators might not have it. This will also disable the test 890Sstevel@tonic-gate * for Exar/Startech ST16C650, as that requires use of the SCR register. 900Sstevel@tonic-gate */ 910Sstevel@tonic-gate int asy_scr_test = 1; /* tunable: set to 0 to disable SCR reg test */ 920Sstevel@tonic-gate 930Sstevel@tonic-gate /* 940Sstevel@tonic-gate * As we don't yet support on-chip flow control, it's a bad idea to put a 950Sstevel@tonic-gate * large number of characters in the TX FIFO, since if other end tells us 960Sstevel@tonic-gate * to stop transmitting, we can only stop filling the TX FIFO, but it will 970Sstevel@tonic-gate * still carry on draining by itself, so remote end still gets what's left 980Sstevel@tonic-gate * in the FIFO. 990Sstevel@tonic-gate */ 1000Sstevel@tonic-gate int asy_max_tx_fifo = 16; /* tunable: max fill of TX FIFO */ 1010Sstevel@tonic-gate 1020Sstevel@tonic-gate #define async_stopc async_ttycommon.t_stopc 1030Sstevel@tonic-gate #define async_startc async_ttycommon.t_startc 1040Sstevel@tonic-gate 1050Sstevel@tonic-gate #define ASY_INIT 1 1060Sstevel@tonic-gate #define ASY_NOINIT 0 1070Sstevel@tonic-gate 1080Sstevel@tonic-gate /* enum value for sw and hw flow control action */ 1090Sstevel@tonic-gate typedef enum { 1100Sstevel@tonic-gate FLOW_CHECK, 1110Sstevel@tonic-gate FLOW_STOP, 1120Sstevel@tonic-gate FLOW_START 1130Sstevel@tonic-gate } async_flowc_action; 1140Sstevel@tonic-gate 1150Sstevel@tonic-gate #ifdef DEBUG 1160Sstevel@tonic-gate #define ASY_DEBUG_INIT 0x0001 /* Output msgs during driver initialization. */ 1170Sstevel@tonic-gate #define ASY_DEBUG_INPUT 0x0002 /* Report characters received during int. */ 1180Sstevel@tonic-gate #define ASY_DEBUG_EOT 0x0004 /* Output msgs when wait for xmit to finish. */ 1190Sstevel@tonic-gate #define ASY_DEBUG_CLOSE 0x0008 /* Output msgs when driver open/close called */ 1200Sstevel@tonic-gate #define ASY_DEBUG_HFLOW 0x0010 /* Output msgs when H/W flowcontrol is active */ 1210Sstevel@tonic-gate #define ASY_DEBUG_PROCS 0x0020 /* Output each proc name as it is entered. */ 1220Sstevel@tonic-gate #define ASY_DEBUG_STATE 0x0040 /* Output value of Interrupt Service Reg. */ 1230Sstevel@tonic-gate #define ASY_DEBUG_INTR 0x0080 /* Output value of Interrupt Service Reg. */ 1240Sstevel@tonic-gate #define ASY_DEBUG_OUT 0x0100 /* Output msgs about output events. */ 1250Sstevel@tonic-gate #define ASY_DEBUG_BUSY 0x0200 /* Output msgs when xmit is enabled/disabled */ 1260Sstevel@tonic-gate #define ASY_DEBUG_MODEM 0x0400 /* Output msgs about modem status & control. */ 1270Sstevel@tonic-gate #define ASY_DEBUG_MODM2 0x0800 /* Output msgs about modem status & control. */ 1280Sstevel@tonic-gate #define ASY_DEBUG_IOCTL 0x1000 /* Output msgs about ioctl messages. */ 1290Sstevel@tonic-gate #define ASY_DEBUG_CHIP 0x2000 /* Output msgs about chip identification. */ 1300Sstevel@tonic-gate #define ASY_DEBUG_SFLOW 0x4000 /* Output msgs when S/W flowcontrol is active */ 1310Sstevel@tonic-gate #define ASY_DEBUG(x) (debug & (x)) 1320Sstevel@tonic-gate static int debug = 0; 1330Sstevel@tonic-gate #else 1340Sstevel@tonic-gate #define ASY_DEBUG(x) B_FALSE 1350Sstevel@tonic-gate #endif 1360Sstevel@tonic-gate 1370Sstevel@tonic-gate /* pnpISA compressed device ids */ 1380Sstevel@tonic-gate #define pnpMTS0219 0xb6930219 /* Multitech MT5634ZTX modem */ 1390Sstevel@tonic-gate 1400Sstevel@tonic-gate /* 1410Sstevel@tonic-gate * PPS (Pulse Per Second) support. 1420Sstevel@tonic-gate */ 1430Sstevel@tonic-gate void ddi_hardpps(); 1440Sstevel@tonic-gate /* 1450Sstevel@tonic-gate * This is protected by the asy_excl_hi of the port on which PPS event 1460Sstevel@tonic-gate * handling is enabled. Note that only one port should have this enabled at 1470Sstevel@tonic-gate * any one time. Enabling PPS handling on multiple ports will result in 1480Sstevel@tonic-gate * unpredictable (but benign) results. 1490Sstevel@tonic-gate */ 1500Sstevel@tonic-gate static struct ppsclockev asy_ppsev; 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate #ifdef PPSCLOCKLED 1530Sstevel@tonic-gate /* XXX Use these to observe PPS latencies and jitter on a scope */ 1540Sstevel@tonic-gate #define LED_ON 1550Sstevel@tonic-gate #define LED_OFF 1560Sstevel@tonic-gate #else 1570Sstevel@tonic-gate #define LED_ON 1580Sstevel@tonic-gate #define LED_OFF 1590Sstevel@tonic-gate #endif 1600Sstevel@tonic-gate 1610Sstevel@tonic-gate static int max_asy_instance = -1; 1620Sstevel@tonic-gate 1630Sstevel@tonic-gate static uint_t asysoftintr(caddr_t intarg); 1640Sstevel@tonic-gate static uint_t asyintr(caddr_t argasy); 1650Sstevel@tonic-gate 1660Sstevel@tonic-gate static boolean_t abort_charseq_recognize(uchar_t ch); 1670Sstevel@tonic-gate 1680Sstevel@tonic-gate /* The async interrupt entry points */ 1690Sstevel@tonic-gate static void async_txint(struct asycom *asy); 1700Sstevel@tonic-gate static void async_rxint(struct asycom *asy, uchar_t lsr); 1710Sstevel@tonic-gate static void async_msint(struct asycom *asy); 1720Sstevel@tonic-gate static void async_softint(struct asycom *asy); 1730Sstevel@tonic-gate 1740Sstevel@tonic-gate static void async_ioctl(struct asyncline *async, queue_t *q, mblk_t *mp); 1750Sstevel@tonic-gate static void async_reioctl(void *unit); 1760Sstevel@tonic-gate static void async_iocdata(queue_t *q, mblk_t *mp); 1770Sstevel@tonic-gate static void async_restart(void *arg); 1780Sstevel@tonic-gate static void async_start(struct asyncline *async); 1790Sstevel@tonic-gate static void async_nstart(struct asyncline *async, int mode); 1800Sstevel@tonic-gate static void async_resume(struct asyncline *async); 1810Sstevel@tonic-gate static void asy_program(struct asycom *asy, int mode); 1820Sstevel@tonic-gate static void asyinit(struct asycom *asy); 1830Sstevel@tonic-gate static void asy_waiteot(struct asycom *asy); 1841762Slt200341 static void asyputchar(cons_polledio_arg_t, uchar_t c); 1851762Slt200341 static int asygetchar(cons_polledio_arg_t); 1861762Slt200341 static boolean_t asyischar(cons_polledio_arg_t); 1870Sstevel@tonic-gate 1880Sstevel@tonic-gate static int asymctl(struct asycom *, int, int); 1890Sstevel@tonic-gate static int asytodm(int, int); 1900Sstevel@tonic-gate static int dmtoasy(int); 1910Sstevel@tonic-gate /*PRINTFLIKE2*/ 1920Sstevel@tonic-gate static void asyerror(int level, const char *fmt, ...) __KPRINTFLIKE(2); 1930Sstevel@tonic-gate static void asy_parse_mode(dev_info_t *devi, struct asycom *asy); 1940Sstevel@tonic-gate static void asy_soft_state_free(struct asycom *); 1950Sstevel@tonic-gate static char *asy_hw_name(struct asycom *asy); 1960Sstevel@tonic-gate static void async_hold_utbrk(void *arg); 1970Sstevel@tonic-gate static void async_resume_utbrk(struct asyncline *async); 1980Sstevel@tonic-gate static void async_dtr_free(struct asyncline *async); 1990Sstevel@tonic-gate static int asy_identify_chip(dev_info_t *devi, struct asycom *asy); 2000Sstevel@tonic-gate static void asy_reset_fifo(struct asycom *asy, uchar_t flags); 2010Sstevel@tonic-gate static int asy_getproperty(dev_info_t *devi, struct asycom *asy, 2020Sstevel@tonic-gate const char *property); 2030Sstevel@tonic-gate static boolean_t async_flowcontrol_sw_input(struct asycom *asy, 2040Sstevel@tonic-gate async_flowc_action onoff, int type); 2050Sstevel@tonic-gate static void async_flowcontrol_sw_output(struct asycom *asy, 2060Sstevel@tonic-gate async_flowc_action onoff); 2070Sstevel@tonic-gate static void async_flowcontrol_hw_input(struct asycom *asy, 2080Sstevel@tonic-gate async_flowc_action onoff, int type); 2090Sstevel@tonic-gate static void async_flowcontrol_hw_output(struct asycom *asy, 2100Sstevel@tonic-gate async_flowc_action onoff); 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate #define GET_PROP(devi, pname, pflag, pval, plen) \ 2130Sstevel@tonic-gate (ddi_prop_op(DDI_DEV_T_ANY, (devi), PROP_LEN_AND_VAL_BUF, \ 2140Sstevel@tonic-gate (pflag), (pname), (caddr_t)(pval), (plen))) 2150Sstevel@tonic-gate 2160Sstevel@tonic-gate static ddi_iblock_cookie_t asy_soft_iblock; 2170Sstevel@tonic-gate ddi_softintr_t asy_softintr_id; 2180Sstevel@tonic-gate static int asy_addedsoft = 0; 2190Sstevel@tonic-gate int asysoftpend; /* soft interrupt pending */ 2200Sstevel@tonic-gate kmutex_t asy_soft_lock; /* lock protecting asysoftpend */ 2210Sstevel@tonic-gate kmutex_t asy_glob_lock; /* lock protecting global data manipulation */ 2220Sstevel@tonic-gate void *asy_soft_state; 2230Sstevel@tonic-gate 2240Sstevel@tonic-gate /* Standard COM port I/O addresses */ 2250Sstevel@tonic-gate static const int standard_com_ports[] = { 2260Sstevel@tonic-gate COM1_IOADDR, COM2_IOADDR, COM3_IOADDR, COM4_IOADDR 2270Sstevel@tonic-gate }; 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate static int *com_ports; 2300Sstevel@tonic-gate static uint_t num_com_ports; 2310Sstevel@tonic-gate 2325295Srandyf #ifdef DEBUG 2335295Srandyf /* 2345295Srandyf * Set this to true to make the driver pretend to do a suspend. Useful 2355295Srandyf * for debugging suspend/resume code with a serial debugger. 2365295Srandyf */ 2375295Srandyf boolean_t asy_nosuspend = B_FALSE; 2385295Srandyf #endif 2395295Srandyf 2405295Srandyf 2410Sstevel@tonic-gate /* 2420Sstevel@tonic-gate * Baud rate table. Indexed by #defines found in sys/termios.h 2430Sstevel@tonic-gate */ 2440Sstevel@tonic-gate ushort_t asyspdtab[] = { 2450Sstevel@tonic-gate 0, /* 0 baud rate */ 2460Sstevel@tonic-gate 0x900, /* 50 baud rate */ 2470Sstevel@tonic-gate 0x600, /* 75 baud rate */ 2480Sstevel@tonic-gate 0x417, /* 110 baud rate (%0.026) */ 2490Sstevel@tonic-gate 0x359, /* 134 baud rate (%0.058) */ 2500Sstevel@tonic-gate 0x300, /* 150 baud rate */ 2510Sstevel@tonic-gate 0x240, /* 200 baud rate */ 2520Sstevel@tonic-gate 0x180, /* 300 baud rate */ 2530Sstevel@tonic-gate 0x0c0, /* 600 baud rate */ 2540Sstevel@tonic-gate 0x060, /* 1200 baud rate */ 2550Sstevel@tonic-gate 0x040, /* 1800 baud rate */ 2560Sstevel@tonic-gate 0x030, /* 2400 baud rate */ 2570Sstevel@tonic-gate 0x018, /* 4800 baud rate */ 2580Sstevel@tonic-gate 0x00c, /* 9600 baud rate */ 2590Sstevel@tonic-gate 0x006, /* 19200 baud rate */ 2600Sstevel@tonic-gate 0x003, /* 38400 baud rate */ 2610Sstevel@tonic-gate 2620Sstevel@tonic-gate 0x002, /* 57600 baud rate */ 2630Sstevel@tonic-gate 0x0, /* 76800 baud rate not supported */ 2640Sstevel@tonic-gate 0x001, /* 115200 baud rate */ 2650Sstevel@tonic-gate 0x0, /* 153600 baud rate not supported */ 2660Sstevel@tonic-gate 0x0, /* 0x8002 (SMC chip) 230400 baud rate not supported */ 2670Sstevel@tonic-gate 0x0, /* 307200 baud rate not supported */ 2680Sstevel@tonic-gate 0x0, /* 0x8001 (SMC chip) 460800 baud rate not supported */ 2690Sstevel@tonic-gate 0x0, /* unused */ 2700Sstevel@tonic-gate 0x0, /* unused */ 2710Sstevel@tonic-gate 0x0, /* unused */ 2720Sstevel@tonic-gate 0x0, /* unused */ 2730Sstevel@tonic-gate 0x0, /* unused */ 2740Sstevel@tonic-gate 0x0, /* unused */ 2750Sstevel@tonic-gate 0x0, /* unused */ 2760Sstevel@tonic-gate 0x0, /* unused */ 2770Sstevel@tonic-gate 0x0, /* unused */ 2780Sstevel@tonic-gate }; 2790Sstevel@tonic-gate 2800Sstevel@tonic-gate static int asyrsrv(queue_t *q); 2810Sstevel@tonic-gate static int asyopen(queue_t *rq, dev_t *dev, int flag, int sflag, cred_t *cr); 2820Sstevel@tonic-gate static int asyclose(queue_t *q, int flag, cred_t *credp); 2835295Srandyf static int asywputdo(queue_t *q, mblk_t *mp, boolean_t); 2840Sstevel@tonic-gate static int asywput(queue_t *q, mblk_t *mp); 2850Sstevel@tonic-gate 2860Sstevel@tonic-gate struct module_info asy_info = { 2870Sstevel@tonic-gate 0, 2880Sstevel@tonic-gate "asy", 2890Sstevel@tonic-gate 0, 2900Sstevel@tonic-gate INFPSZ, 2910Sstevel@tonic-gate 4096, 2920Sstevel@tonic-gate 128 2930Sstevel@tonic-gate }; 2940Sstevel@tonic-gate 2950Sstevel@tonic-gate static struct qinit asy_rint = { 2960Sstevel@tonic-gate putq, 2970Sstevel@tonic-gate asyrsrv, 2980Sstevel@tonic-gate asyopen, 2990Sstevel@tonic-gate asyclose, 3000Sstevel@tonic-gate NULL, 3010Sstevel@tonic-gate &asy_info, 3020Sstevel@tonic-gate NULL 3030Sstevel@tonic-gate }; 3040Sstevel@tonic-gate 3050Sstevel@tonic-gate static struct qinit asy_wint = { 3060Sstevel@tonic-gate asywput, 3070Sstevel@tonic-gate NULL, 3080Sstevel@tonic-gate NULL, 3090Sstevel@tonic-gate NULL, 3100Sstevel@tonic-gate NULL, 3110Sstevel@tonic-gate &asy_info, 3120Sstevel@tonic-gate NULL 3130Sstevel@tonic-gate }; 3140Sstevel@tonic-gate 3150Sstevel@tonic-gate struct streamtab asy_str_info = { 3160Sstevel@tonic-gate &asy_rint, 3170Sstevel@tonic-gate &asy_wint, 3180Sstevel@tonic-gate NULL, 3190Sstevel@tonic-gate NULL 3200Sstevel@tonic-gate }; 3210Sstevel@tonic-gate 3220Sstevel@tonic-gate static int asyinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, 3230Sstevel@tonic-gate void **result); 3240Sstevel@tonic-gate static int asyprobe(dev_info_t *); 3250Sstevel@tonic-gate static int asyattach(dev_info_t *, ddi_attach_cmd_t); 3260Sstevel@tonic-gate static int asydetach(dev_info_t *, ddi_detach_cmd_t); 327*7656SSherry.Moore@Sun.COM static int asyquiesce(dev_info_t *); 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate static struct cb_ops cb_asy_ops = { 3300Sstevel@tonic-gate nodev, /* cb_open */ 3310Sstevel@tonic-gate nodev, /* cb_close */ 3320Sstevel@tonic-gate nodev, /* cb_strategy */ 3330Sstevel@tonic-gate nodev, /* cb_print */ 3340Sstevel@tonic-gate nodev, /* cb_dump */ 3350Sstevel@tonic-gate nodev, /* cb_read */ 3360Sstevel@tonic-gate nodev, /* cb_write */ 3370Sstevel@tonic-gate nodev, /* cb_ioctl */ 3380Sstevel@tonic-gate nodev, /* cb_devmap */ 3390Sstevel@tonic-gate nodev, /* cb_mmap */ 3400Sstevel@tonic-gate nodev, /* cb_segmap */ 3410Sstevel@tonic-gate nochpoll, /* cb_chpoll */ 3420Sstevel@tonic-gate ddi_prop_op, /* cb_prop_op */ 3430Sstevel@tonic-gate &asy_str_info, /* cb_stream */ 3440Sstevel@tonic-gate D_MP /* cb_flag */ 3450Sstevel@tonic-gate }; 3460Sstevel@tonic-gate 3470Sstevel@tonic-gate struct dev_ops asy_ops = { 3480Sstevel@tonic-gate DEVO_REV, /* devo_rev */ 3490Sstevel@tonic-gate 0, /* devo_refcnt */ 3500Sstevel@tonic-gate asyinfo, /* devo_getinfo */ 3510Sstevel@tonic-gate nulldev, /* devo_identify */ 3520Sstevel@tonic-gate asyprobe, /* devo_probe */ 3530Sstevel@tonic-gate asyattach, /* devo_attach */ 3540Sstevel@tonic-gate asydetach, /* devo_detach */ 3550Sstevel@tonic-gate nodev, /* devo_reset */ 3560Sstevel@tonic-gate &cb_asy_ops, /* devo_cb_ops */ 357*7656SSherry.Moore@Sun.COM NULL, /* devo_bus_ops */ 358*7656SSherry.Moore@Sun.COM NULL, /* power */ 359*7656SSherry.Moore@Sun.COM asyquiesce, /* quiesce */ 3600Sstevel@tonic-gate }; 3610Sstevel@tonic-gate 3620Sstevel@tonic-gate static struct modldrv modldrv = { 3630Sstevel@tonic-gate &mod_driverops, /* Type of module. This one is a driver */ 364*7656SSherry.Moore@Sun.COM "ASY driver", 3650Sstevel@tonic-gate &asy_ops, /* driver ops */ 3660Sstevel@tonic-gate }; 3670Sstevel@tonic-gate 3680Sstevel@tonic-gate static struct modlinkage modlinkage = { 3690Sstevel@tonic-gate MODREV_1, 3700Sstevel@tonic-gate (void *)&modldrv, 3710Sstevel@tonic-gate NULL 3720Sstevel@tonic-gate }; 3730Sstevel@tonic-gate 3740Sstevel@tonic-gate int 3750Sstevel@tonic-gate _init(void) 3760Sstevel@tonic-gate { 3770Sstevel@tonic-gate int i; 3780Sstevel@tonic-gate 3790Sstevel@tonic-gate i = ddi_soft_state_init(&asy_soft_state, sizeof (struct asycom), 2); 3800Sstevel@tonic-gate if (i == 0) { 3810Sstevel@tonic-gate mutex_init(&asy_glob_lock, NULL, MUTEX_DRIVER, NULL); 3820Sstevel@tonic-gate if ((i = mod_install(&modlinkage)) != 0) { 3830Sstevel@tonic-gate mutex_destroy(&asy_glob_lock); 3840Sstevel@tonic-gate ddi_soft_state_fini(&asy_soft_state); 3850Sstevel@tonic-gate } else { 3860Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INIT, "%s, debug = %x\n", 3870Sstevel@tonic-gate modldrv.drv_linkinfo, debug); 3880Sstevel@tonic-gate } 3890Sstevel@tonic-gate } 3900Sstevel@tonic-gate return (i); 3910Sstevel@tonic-gate } 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate int 3940Sstevel@tonic-gate _fini(void) 3950Sstevel@tonic-gate { 3960Sstevel@tonic-gate int i; 3970Sstevel@tonic-gate 3980Sstevel@tonic-gate if ((i = mod_remove(&modlinkage)) == 0) { 3990Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_INIT, "%s unloading\n", 4000Sstevel@tonic-gate modldrv.drv_linkinfo); 4010Sstevel@tonic-gate ASSERT(max_asy_instance == -1); 4020Sstevel@tonic-gate mutex_destroy(&asy_glob_lock); 4030Sstevel@tonic-gate if (asy_addedsoft) 4040Sstevel@tonic-gate ddi_remove_softintr(asy_softintr_id); 4050Sstevel@tonic-gate asy_addedsoft = 0; 4060Sstevel@tonic-gate /* free "motherboard-serial-ports" property if allocated */ 4070Sstevel@tonic-gate if (com_ports != NULL && com_ports != (int *)standard_com_ports) 4085295Srandyf ddi_prop_free(com_ports); 4090Sstevel@tonic-gate com_ports = NULL; 4100Sstevel@tonic-gate mutex_destroy(&asy_soft_lock); 4110Sstevel@tonic-gate ddi_soft_state_fini(&asy_soft_state); 4120Sstevel@tonic-gate } 4130Sstevel@tonic-gate return (i); 4140Sstevel@tonic-gate } 4150Sstevel@tonic-gate 4160Sstevel@tonic-gate int 4170Sstevel@tonic-gate _info(struct modinfo *modinfop) 4180Sstevel@tonic-gate { 4190Sstevel@tonic-gate return (mod_info(&modlinkage, modinfop)); 4200Sstevel@tonic-gate } 4210Sstevel@tonic-gate 4225295Srandyf void 4235295Srandyf async_put_suspq(struct asycom *asy, mblk_t *mp) 4245295Srandyf { 4255295Srandyf struct asyncline *async = asy->asy_priv; 4265295Srandyf 4275295Srandyf ASSERT(mutex_owned(&asy->asy_excl)); 4285295Srandyf 4295295Srandyf if (async->async_suspqf == NULL) 4305295Srandyf async->async_suspqf = mp; 4315295Srandyf else 4325295Srandyf async->async_suspqb->b_next = mp; 4335295Srandyf 4345295Srandyf async->async_suspqb = mp; 4355295Srandyf } 4365295Srandyf 4375295Srandyf static mblk_t * 4385295Srandyf async_get_suspq(struct asycom *asy) 4395295Srandyf { 4405295Srandyf struct asyncline *async = asy->asy_priv; 4415295Srandyf mblk_t *mp; 4425295Srandyf 4435295Srandyf ASSERT(mutex_owned(&asy->asy_excl)); 4445295Srandyf 4455295Srandyf if ((mp = async->async_suspqf) != NULL) { 4465295Srandyf async->async_suspqf = mp->b_next; 4475295Srandyf mp->b_next = NULL; 4485295Srandyf } else { 4495295Srandyf async->async_suspqb = NULL; 4505295Srandyf } 4515295Srandyf return (mp); 4525295Srandyf } 4535295Srandyf 4545295Srandyf static void 4555295Srandyf async_process_suspq(struct asycom *asy) 4565295Srandyf { 4575295Srandyf struct asyncline *async = asy->asy_priv; 4585295Srandyf mblk_t *mp; 4595295Srandyf 4605295Srandyf ASSERT(mutex_owned(&asy->asy_excl)); 4615295Srandyf 4625295Srandyf while ((mp = async_get_suspq(asy)) != NULL) { 4635295Srandyf queue_t *q; 4645295Srandyf 4655295Srandyf q = async->async_ttycommon.t_writeq; 4665295Srandyf ASSERT(q != NULL); 4675295Srandyf mutex_exit(&asy->asy_excl); 4685295Srandyf (void) asywputdo(q, mp, B_FALSE); 4695295Srandyf mutex_enter(&asy->asy_excl); 4705295Srandyf } 4715295Srandyf async->async_flags &= ~ASYNC_DDI_SUSPENDED; 4725295Srandyf cv_broadcast(&async->async_flags_cv); 4735295Srandyf } 4745295Srandyf 4750Sstevel@tonic-gate static int 4763359Smyers asy_get_bus_type(dev_info_t *devinfo) 4773359Smyers { 4783359Smyers char parent_type[16]; 4793359Smyers int parentlen; 4803359Smyers 4813359Smyers parentlen = sizeof (parent_type); 4823359Smyers 4833359Smyers if (ddi_prop_op(DDI_DEV_T_ANY, devinfo, PROP_LEN_AND_VAL_BUF, 0, 4843359Smyers "device_type", (caddr_t)parent_type, &parentlen) 4853359Smyers != DDI_PROP_SUCCESS && ddi_prop_op(DDI_DEV_T_ANY, devinfo, 4863359Smyers PROP_LEN_AND_VAL_BUF, 0, "bus-type", (caddr_t)parent_type, 4873359Smyers &parentlen) != DDI_PROP_SUCCESS) { 4883359Smyers cmn_err(CE_WARN, 4893359Smyers "asy: can't figure out device type for" 4903359Smyers " parent \"%s\"", 4913359Smyers ddi_get_name(ddi_get_parent(devinfo))); 4923359Smyers return (ASY_BUS_UNKNOWN); 4933359Smyers } 4943359Smyers if (strcmp(parent_type, "isa") == 0) 4953359Smyers return (ASY_BUS_ISA); 4963359Smyers else if (strcmp(parent_type, "pci") == 0) 4973359Smyers return (ASY_BUS_PCI); 4983359Smyers else 4993359Smyers return (ASY_BUS_UNKNOWN); 5003359Smyers } 5013359Smyers 5023359Smyers static int 5033359Smyers asy_get_io_regnum_pci(dev_info_t *devi, struct asycom *asy) 5043359Smyers { 5053359Smyers int reglen, nregs; 5063359Smyers int regnum, i; 5073359Smyers uint64_t size; 5083359Smyers struct pci_phys_spec *reglist; 5093359Smyers 5103359Smyers if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 5113359Smyers "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { 5123359Smyers cmn_err(CE_WARN, "asy_get_io_regnum_pci: reg property" 5133359Smyers " not found in devices property list"); 5143359Smyers return (-1); 5153359Smyers } 5163359Smyers 5173359Smyers /* 5183359Smyers * PCI devices are assumed to not have broken FIFOs; 5193359Smyers * Agere/Lucent Venus PCI modem chipsets are an example 5203359Smyers */ 5213359Smyers if (asy) 5223359Smyers asy->asy_flags2 |= ASY2_NO_LOOPBACK; 5233359Smyers 5243359Smyers regnum = -1; 5253359Smyers nregs = reglen / sizeof (*reglist); 5263359Smyers for (i = 0; i < nregs; i++) { 5273359Smyers switch (reglist[i].pci_phys_hi & PCI_ADDR_MASK) { 5283359Smyers case PCI_ADDR_IO: /* I/O bus reg property */ 5293359Smyers if (regnum == -1) /* use only the first one */ 5303359Smyers regnum = i; 5313359Smyers break; 5323359Smyers 5333359Smyers default: 5343359Smyers break; 5353359Smyers } 5363359Smyers } 5373359Smyers 5383359Smyers /* check for valid count of registers */ 5393359Smyers if (regnum >= 0) { 5403359Smyers size = ((uint64_t)reglist[regnum].pci_size_low) | 5413359Smyers ((uint64_t)reglist[regnum].pci_size_hi) << 32; 5423359Smyers if (size < 8) 5433359Smyers regnum = -1; 5443359Smyers } 5453359Smyers kmem_free(reglist, reglen); 5463359Smyers return (regnum); 5473359Smyers } 5483359Smyers 5493359Smyers static int 5503359Smyers asy_get_io_regnum_isa(dev_info_t *devi, struct asycom *asy) 5513359Smyers { 5523359Smyers int reglen, nregs; 5533359Smyers int regnum, i; 5543359Smyers struct { 5553359Smyers uint_t bustype; 5563359Smyers int base; 5573359Smyers int size; 5583359Smyers } *reglist; 5593359Smyers 5603359Smyers if (ddi_getlongprop(DDI_DEV_T_ANY, devi, DDI_PROP_DONTPASS, 5613359Smyers "reg", (caddr_t)®list, ®len) != DDI_PROP_SUCCESS) { 5623359Smyers cmn_err(CE_WARN, "asy_get_io_regnum: reg property not found " 5635295Srandyf "in devices property list"); 5643359Smyers return (-1); 5653359Smyers } 5663359Smyers 5673359Smyers regnum = -1; 5683359Smyers nregs = reglen / sizeof (*reglist); 5693359Smyers for (i = 0; i < nregs; i++) { 5703359Smyers switch (reglist[i].bustype) { 5713359Smyers case 1: /* I/O bus reg property */ 5723359Smyers if (regnum == -1) /* only use the first one */ 5733359Smyers regnum = i; 5743359Smyers break; 5753359Smyers 5763359Smyers case pnpMTS0219: /* Multitech MT5634ZTX modem */ 5773359Smyers /* Venus chipset can't do loopback test */ 5783359Smyers if (asy) 5793359Smyers asy->asy_flags2 |= ASY2_NO_LOOPBACK; 5803359Smyers break; 5813359Smyers 5823359Smyers default: 5833359Smyers break; 5843359Smyers } 5853359Smyers } 5863359Smyers 5873359Smyers /* check for valid count of registers */ 5883359Smyers if ((regnum < 0) || (reglist[regnum].size < 8)) 5893359Smyers regnum = -1; 5903359Smyers kmem_free(reglist, reglen); 5913359Smyers return (regnum); 5923359Smyers } 5933359Smyers 5943359Smyers static int 5953359Smyers asy_get_io_regnum(dev_info_t *devinfo, struct asycom *asy) 5963359Smyers { 5973359Smyers switch (asy_get_bus_type(devinfo)) { 5983359Smyers case ASY_BUS_ISA: 5993359Smyers return (asy_get_io_regnum_isa(devinfo, asy)); 6003359Smyers case ASY_BUS_PCI: 6013359Smyers return (asy_get_io_regnum_pci(devinfo, asy)); 6023359Smyers default: 6033359Smyers return (-1); 6043359Smyers } 6053359Smyers } 6063359Smyers 6073359Smyers static int 6080Sstevel@tonic-gate asydetach(dev_info_t *devi, ddi_detach_cmd_t cmd) 6090Sstevel@tonic-gate { 6100Sstevel@tonic-gate int instance; 6110Sstevel@tonic-gate struct asycom *asy; 6120Sstevel@tonic-gate struct asyncline *async; 6130Sstevel@tonic-gate 6140Sstevel@tonic-gate instance = ddi_get_instance(devi); /* find out which unit */ 6150Sstevel@tonic-gate 6160Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 6170Sstevel@tonic-gate if (asy == NULL) 6180Sstevel@tonic-gate return (DDI_FAILURE); 6190Sstevel@tonic-gate async = asy->asy_priv; 6200Sstevel@tonic-gate 6215295Srandyf switch (cmd) { 6225295Srandyf case DDI_DETACH: 6235295Srandyf DEBUGNOTE2(ASY_DEBUG_INIT, "asy%d: %s shutdown.", 6245295Srandyf instance, asy_hw_name(asy)); 6255295Srandyf 6265295Srandyf /* cancel DTR hold timeout */ 6275295Srandyf if (async->async_dtrtid != 0) { 6285295Srandyf (void) untimeout(async->async_dtrtid); 6295295Srandyf async->async_dtrtid = 0; 6305295Srandyf } 6315295Srandyf 6325295Srandyf /* remove all minor device node(s) for this device */ 6335295Srandyf ddi_remove_minor_node(devi, NULL); 6345295Srandyf 6355295Srandyf mutex_destroy(&asy->asy_excl); 6365295Srandyf mutex_destroy(&asy->asy_excl_hi); 6375295Srandyf cv_destroy(&async->async_flags_cv); 6385295Srandyf ddi_remove_intr(devi, 0, asy->asy_iblock); 6395295Srandyf ddi_regs_map_free(&asy->asy_iohandle); 6405295Srandyf asy_soft_state_free(asy); 6415295Srandyf DEBUGNOTE1(ASY_DEBUG_INIT, "asy%d: shutdown complete", 6425295Srandyf instance); 6435295Srandyf break; 6445295Srandyf case DDI_SUSPEND: 6455295Srandyf { 6465295Srandyf unsigned i; 6475295Srandyf uchar_t lsr; 6485295Srandyf 6495295Srandyf #ifdef DEBUG 6505295Srandyf if (asy_nosuspend) 6515295Srandyf return (DDI_SUCCESS); 6525295Srandyf #endif 6535295Srandyf mutex_enter(&asy->asy_excl); 6545295Srandyf 6555295Srandyf ASSERT(async->async_ops >= 0); 6565295Srandyf while (async->async_ops > 0) 6575295Srandyf cv_wait(&async->async_ops_cv, &asy->asy_excl); 6585295Srandyf 6595295Srandyf async->async_flags |= ASYNC_DDI_SUSPENDED; 6605295Srandyf 6615295Srandyf /* Wait for timed break and delay to complete */ 6625295Srandyf while ((async->async_flags & (ASYNC_BREAK|ASYNC_DELAY))) { 6635295Srandyf if (cv_wait_sig(&async->async_flags_cv, &asy->asy_excl) 6645295Srandyf == 0) { 6655295Srandyf async_process_suspq(asy); 6665295Srandyf mutex_exit(&asy->asy_excl); 6675295Srandyf return (DDI_FAILURE); 6685295Srandyf } 6695295Srandyf } 6705295Srandyf 6715295Srandyf /* Clear untimed break */ 6725295Srandyf if (async->async_flags & ASYNC_OUT_SUSPEND) 6735295Srandyf async_resume_utbrk(async); 6745295Srandyf 6755295Srandyf mutex_exit(&asy->asy_excl); 6765295Srandyf 6775295Srandyf mutex_enter(&asy->asy_soft_sr); 6785295Srandyf mutex_enter(&asy->asy_excl); 6795295Srandyf if (async->async_wbufcid != 0) { 6805295Srandyf bufcall_id_t bcid = async->async_wbufcid; 6815295Srandyf async->async_wbufcid = 0; 6825295Srandyf async->async_flags |= ASYNC_RESUME_BUFCALL; 6835295Srandyf mutex_exit(&asy->asy_excl); 6845295Srandyf unbufcall(bcid); 6855295Srandyf mutex_enter(&asy->asy_excl); 6865295Srandyf } 6875295Srandyf mutex_enter(&asy->asy_excl_hi); 6885295Srandyf 6895295Srandyf /* Disable interrupts from chip */ 6905295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 6915295Srandyf asy->asy_flags |= ASY_DDI_SUSPENDED; 6925295Srandyf 6935295Srandyf /* Process remaining RX characters and RX errors, if any */ 6945295Srandyf lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); 6955295Srandyf async_rxint(asy, lsr); 6965295Srandyf 6975295Srandyf /* Wait for TX to drain */ 6985295Srandyf for (i = 1000; i > 0; i--) { 6995295Srandyf lsr = ddi_get8(asy->asy_iohandle, 7005295Srandyf asy->asy_ioaddr + LSR); 7015295Srandyf if ((lsr & (XSRE | XHRE)) == (XSRE | XHRE)) 7025295Srandyf break; 7035295Srandyf delay(drv_usectohz(10000)); 7045295Srandyf } 7055295Srandyf if (i == 0) 7065295Srandyf cmn_err(CE_WARN, 7075295Srandyf "asy: transmitter wasn't drained before " 7085295Srandyf "driver was suspended"); 7095295Srandyf 7105295Srandyf mutex_exit(&asy->asy_excl_hi); 7115295Srandyf mutex_exit(&asy->asy_excl); 7125295Srandyf mutex_exit(&asy->asy_soft_sr); 7135295Srandyf break; 7140Sstevel@tonic-gate } 7155295Srandyf default: 7165295Srandyf return (DDI_FAILURE); 7175295Srandyf } 7185295Srandyf 7190Sstevel@tonic-gate return (DDI_SUCCESS); 7200Sstevel@tonic-gate } 7210Sstevel@tonic-gate 7220Sstevel@tonic-gate /* 7230Sstevel@tonic-gate * asyprobe 7240Sstevel@tonic-gate * We don't bother probing for the hardware, as since Solaris 2.6, device 7250Sstevel@tonic-gate * nodes are only created for auto-detected hardware or nodes explicitly 7260Sstevel@tonic-gate * created by the user, e.g. via the DCA. However, we should check the 7270Sstevel@tonic-gate * device node is at least vaguely usable, i.e. we have a block of 8 i/o 7280Sstevel@tonic-gate * ports. This prevents attempting to attach to bogus serial ports which 7290Sstevel@tonic-gate * some BIOSs still partially report when they are disabled in the BIOS. 7300Sstevel@tonic-gate */ 7310Sstevel@tonic-gate static int 7320Sstevel@tonic-gate asyprobe(dev_info_t *devi) 7330Sstevel@tonic-gate { 7343359Smyers return ((asy_get_io_regnum(devi, NULL) < 0) ? 7353359Smyers DDI_PROBE_FAILURE : DDI_PROBE_DONTCARE); 7360Sstevel@tonic-gate } 7370Sstevel@tonic-gate 7380Sstevel@tonic-gate static int 7390Sstevel@tonic-gate asyattach(dev_info_t *devi, ddi_attach_cmd_t cmd) 7400Sstevel@tonic-gate { 7410Sstevel@tonic-gate int instance; 7420Sstevel@tonic-gate int mcr; 7430Sstevel@tonic-gate int ret; 7440Sstevel@tonic-gate int regnum = 0; 7450Sstevel@tonic-gate int i; 7460Sstevel@tonic-gate struct asycom *asy; 7473359Smyers char name[ASY_MINOR_LEN]; 7480Sstevel@tonic-gate int status; 7490Sstevel@tonic-gate static ddi_device_acc_attr_t ioattr = { 7500Sstevel@tonic-gate DDI_DEVICE_ATTR_V0, 7510Sstevel@tonic-gate DDI_NEVERSWAP_ACC, 7520Sstevel@tonic-gate DDI_STRICTORDER_ACC, 7530Sstevel@tonic-gate }; 7540Sstevel@tonic-gate 7555295Srandyf instance = ddi_get_instance(devi); /* find out which unit */ 7565295Srandyf 7575295Srandyf switch (cmd) { 7585295Srandyf case DDI_ATTACH: 7595295Srandyf break; 7605295Srandyf case DDI_RESUME: 7615295Srandyf { 7625295Srandyf struct asyncline *async; 7635295Srandyf 7645295Srandyf #ifdef DEBUG 7655295Srandyf if (asy_nosuspend) 7665295Srandyf return (DDI_SUCCESS); 7675295Srandyf #endif 7685295Srandyf asy = ddi_get_soft_state(asy_soft_state, instance); 7695295Srandyf if (asy == NULL) 7705295Srandyf return (DDI_FAILURE); 7715295Srandyf 7725295Srandyf mutex_enter(&asy->asy_soft_sr); 7735295Srandyf mutex_enter(&asy->asy_excl); 7745295Srandyf mutex_enter(&asy->asy_excl_hi); 7755295Srandyf 7765295Srandyf async = asy->asy_priv; 7775295Srandyf /* Disable interrupts */ 7785295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 7795295Srandyf if (asy_identify_chip(devi, asy) != DDI_SUCCESS) { 7805295Srandyf mutex_exit(&asy->asy_excl_hi); 7815295Srandyf mutex_exit(&asy->asy_excl); 7825295Srandyf mutex_exit(&asy->asy_soft_sr); 7835295Srandyf cmn_err(CE_WARN, "Cannot identify UART chip at %p\n", 7845295Srandyf (void *)asy->asy_ioaddr); 7855295Srandyf return (DDI_FAILURE); 7865295Srandyf } 7875295Srandyf asy->asy_flags &= ~ASY_DDI_SUSPENDED; 7885295Srandyf if (async->async_flags & ASYNC_ISOPEN) { 7895295Srandyf asy_program(asy, ASY_INIT); 7905295Srandyf /* Kick off output */ 7915295Srandyf if (async->async_ocnt > 0) { 7925295Srandyf async_resume(async); 7935295Srandyf } else { 7945295Srandyf mutex_exit(&asy->asy_excl_hi); 7955295Srandyf if (async->async_xmitblk) 7965295Srandyf freeb(async->async_xmitblk); 7975295Srandyf async->async_xmitblk = NULL; 7985295Srandyf async_start(async); 7995295Srandyf mutex_enter(&asy->asy_excl_hi); 8005295Srandyf } 8015295Srandyf ASYSETSOFT(asy); 8025295Srandyf } 8035295Srandyf mutex_exit(&asy->asy_excl_hi); 8045295Srandyf mutex_exit(&asy->asy_excl); 8055295Srandyf mutex_exit(&asy->asy_soft_sr); 8065295Srandyf 8075295Srandyf mutex_enter(&asy->asy_excl); 8085295Srandyf if (async->async_flags & ASYNC_RESUME_BUFCALL) { 8095295Srandyf async->async_wbufcid = bufcall(async->async_wbufcds, 8105295Srandyf BPRI_HI, (void (*)(void *)) async_reioctl, 8115295Srandyf (void *)(intptr_t)async->async_common->asy_unit); 8125295Srandyf async->async_flags &= ~ASYNC_RESUME_BUFCALL; 8135295Srandyf } 8145295Srandyf async_process_suspq(asy); 8155295Srandyf mutex_exit(&asy->asy_excl); 8165295Srandyf return (DDI_SUCCESS); 8175295Srandyf } 8185295Srandyf default: 8190Sstevel@tonic-gate return (DDI_FAILURE); 8205295Srandyf } 8215295Srandyf 8220Sstevel@tonic-gate ret = ddi_soft_state_zalloc(asy_soft_state, instance); 8230Sstevel@tonic-gate if (ret != DDI_SUCCESS) 8240Sstevel@tonic-gate return (DDI_FAILURE); 8250Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 8260Sstevel@tonic-gate ASSERT(asy != NULL); /* can't fail - we only just allocated it */ 8270Sstevel@tonic-gate asy->asy_unit = instance; 8280Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 8290Sstevel@tonic-gate if (instance > max_asy_instance) 8300Sstevel@tonic-gate max_asy_instance = instance; 8310Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 8320Sstevel@tonic-gate 8333359Smyers regnum = asy_get_io_regnum(devi, asy); 8340Sstevel@tonic-gate 8350Sstevel@tonic-gate if (regnum < 0 || 8360Sstevel@tonic-gate ddi_regs_map_setup(devi, regnum, (caddr_t *)&asy->asy_ioaddr, 8370Sstevel@tonic-gate (offset_t)0, (offset_t)0, &ioattr, &asy->asy_iohandle) 8380Sstevel@tonic-gate != DDI_SUCCESS) { 8390Sstevel@tonic-gate cmn_err(CE_WARN, "asy%d: could not map UART registers @ %p", 8400Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr); 8410Sstevel@tonic-gate 8420Sstevel@tonic-gate asy_soft_state_free(asy); 8430Sstevel@tonic-gate return (DDI_FAILURE); 8440Sstevel@tonic-gate } 8450Sstevel@tonic-gate 8460Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_INIT, "asy%dattach: UART @ %p\n", 8470Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr); 8480Sstevel@tonic-gate 8490Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 8500Sstevel@tonic-gate if (com_ports == NULL) { /* need to initialize com_ports */ 8510Sstevel@tonic-gate if (ddi_prop_lookup_int_array(DDI_DEV_T_ANY, devi, 0, 8520Sstevel@tonic-gate "motherboard-serial-ports", &com_ports, &num_com_ports) != 8530Sstevel@tonic-gate DDI_PROP_SUCCESS) { 8540Sstevel@tonic-gate /* Use our built-in COM[1234] values */ 8550Sstevel@tonic-gate com_ports = (int *)standard_com_ports; 8560Sstevel@tonic-gate num_com_ports = sizeof (standard_com_ports) / 8570Sstevel@tonic-gate sizeof (standard_com_ports[0]); 8580Sstevel@tonic-gate } 8590Sstevel@tonic-gate if (num_com_ports > 10) { 8600Sstevel@tonic-gate /* We run out of single digits for device properties */ 8610Sstevel@tonic-gate num_com_ports = 10; 8620Sstevel@tonic-gate cmn_err(CE_WARN, 8630Sstevel@tonic-gate "More than %d motherboard-serial-ports", 8640Sstevel@tonic-gate num_com_ports); 8650Sstevel@tonic-gate } 8660Sstevel@tonic-gate } 8670Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 8680Sstevel@tonic-gate 8690Sstevel@tonic-gate /* 8700Sstevel@tonic-gate * Lookup the i/o address to see if this is a standard COM port 8710Sstevel@tonic-gate * in which case we assign it the correct tty[a-d] to match the 8720Sstevel@tonic-gate * COM port number, or some other i/o address in which case it 8730Sstevel@tonic-gate * will be assigned /dev/term/[0123...] in some rather arbitrary 8740Sstevel@tonic-gate * fashion. 8750Sstevel@tonic-gate */ 8760Sstevel@tonic-gate 8770Sstevel@tonic-gate for (i = 0; i < num_com_ports; i++) { 8780Sstevel@tonic-gate if (asy->asy_ioaddr == (uint8_t *)(uintptr_t)com_ports[i]) { 8790Sstevel@tonic-gate asy->asy_com_port = i + 1; 8800Sstevel@tonic-gate break; 8810Sstevel@tonic-gate } 8820Sstevel@tonic-gate } 8830Sstevel@tonic-gate 8840Sstevel@tonic-gate /* 8850Sstevel@tonic-gate * It appears that there was async hardware that on reset 8860Sstevel@tonic-gate * did not clear ICR. Hence when we get to 8870Sstevel@tonic-gate * ddi_get_iblock_cookie below, this hardware would cause 8880Sstevel@tonic-gate * the system to hang if there was input available. 8890Sstevel@tonic-gate */ 8900Sstevel@tonic-gate 8911106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0x00); 8920Sstevel@tonic-gate 8930Sstevel@tonic-gate /* establish default usage */ 8940Sstevel@tonic-gate asy->asy_mcr |= RTS|DTR; /* do use RTS/DTR after open */ 8950Sstevel@tonic-gate asy->asy_lcr = STOP1|BITS8; /* default to 1 stop 8 bits */ 8960Sstevel@tonic-gate asy->asy_bidx = B9600; /* default to 9600 */ 8970Sstevel@tonic-gate #ifdef DEBUG 8980Sstevel@tonic-gate asy->asy_msint_cnt = 0; /* # of times in async_msint */ 8990Sstevel@tonic-gate #endif 9000Sstevel@tonic-gate mcr = 0; /* don't enable until open */ 9010Sstevel@tonic-gate 9020Sstevel@tonic-gate if (asy->asy_com_port != 0) { 9030Sstevel@tonic-gate /* 9040Sstevel@tonic-gate * For motherboard ports, emulate tty eeprom properties. 9050Sstevel@tonic-gate * Actually, we can't tell if a port is motherboard or not, 9060Sstevel@tonic-gate * so for "motherboard ports", read standard DOS COM ports. 9070Sstevel@tonic-gate */ 9080Sstevel@tonic-gate switch (asy_getproperty(devi, asy, "ignore-cd")) { 9090Sstevel@tonic-gate case 0: /* *-ignore-cd=False */ 9100Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 9110Sstevel@tonic-gate "asy%dattach: clear ASY_IGNORE_CD\n", instance); 9120Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */ 9130Sstevel@tonic-gate break; 9140Sstevel@tonic-gate case 1: /* *-ignore-cd=True */ 9150Sstevel@tonic-gate /*FALLTHRU*/ 9160Sstevel@tonic-gate default: /* *-ignore-cd not defined */ 9170Sstevel@tonic-gate /* 9180Sstevel@tonic-gate * We set rather silly defaults of soft carrier on 9190Sstevel@tonic-gate * and DTR/RTS raised here because it might be that 9200Sstevel@tonic-gate * one of the motherboard ports is the system console. 9210Sstevel@tonic-gate */ 9220Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 9230Sstevel@tonic-gate "asy%dattach: set ASY_IGNORE_CD, set RTS & DTR\n", 9240Sstevel@tonic-gate instance); 9250Sstevel@tonic-gate mcr = asy->asy_mcr; /* rts/dtr on */ 9260Sstevel@tonic-gate asy->asy_flags |= ASY_IGNORE_CD; /* ignore cd */ 9270Sstevel@tonic-gate break; 9280Sstevel@tonic-gate } 9290Sstevel@tonic-gate 9300Sstevel@tonic-gate /* Property for not raising DTR/RTS */ 9310Sstevel@tonic-gate switch (asy_getproperty(devi, asy, "rts-dtr-off")) { 9320Sstevel@tonic-gate case 0: /* *-rts-dtr-off=False */ 9330Sstevel@tonic-gate asy->asy_flags |= ASY_RTS_DTR_OFF; /* OFF */ 9340Sstevel@tonic-gate mcr = asy->asy_mcr; /* rts/dtr on */ 9350Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, "asy%dattach: " 9360Sstevel@tonic-gate "ASY_RTS_DTR_OFF set and DTR & RTS set\n", 9370Sstevel@tonic-gate instance); 9380Sstevel@tonic-gate break; 9390Sstevel@tonic-gate case 1: /* *-rts-dtr-off=True */ 9400Sstevel@tonic-gate /*FALLTHRU*/ 9410Sstevel@tonic-gate default: /* *-rts-dtr-off undefined */ 9420Sstevel@tonic-gate break; 9430Sstevel@tonic-gate } 9440Sstevel@tonic-gate 9450Sstevel@tonic-gate /* Parse property for tty modes */ 9460Sstevel@tonic-gate asy_parse_mode(devi, asy); 9470Sstevel@tonic-gate } else { 9480Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 9490Sstevel@tonic-gate "asy%dattach: clear ASY_IGNORE_CD, clear RTS & DTR\n", 9500Sstevel@tonic-gate instance); 9510Sstevel@tonic-gate asy->asy_flags &= ~ASY_IGNORE_CD; /* wait for cd */ 9520Sstevel@tonic-gate } 9530Sstevel@tonic-gate 9540Sstevel@tonic-gate /* 9550Sstevel@tonic-gate * Initialize the port with default settings. 9560Sstevel@tonic-gate */ 9570Sstevel@tonic-gate 9580Sstevel@tonic-gate asy->asy_fifo_buf = 1; 9590Sstevel@tonic-gate asy->asy_use_fifo = FIFO_OFF; 9600Sstevel@tonic-gate 9610Sstevel@tonic-gate /* 9620Sstevel@tonic-gate * Get icookie for mutexes initialization 9630Sstevel@tonic-gate */ 9640Sstevel@tonic-gate if ((ddi_get_iblock_cookie(devi, 0, &asy->asy_iblock) != 9650Sstevel@tonic-gate DDI_SUCCESS) || 9660Sstevel@tonic-gate (ddi_get_soft_iblock_cookie(devi, DDI_SOFTINT_MED, 9670Sstevel@tonic-gate &asy_soft_iblock) != DDI_SUCCESS)) { 9680Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 9690Sstevel@tonic-gate cmn_err(CE_CONT, 9700Sstevel@tonic-gate "asy%d: could not hook interrupt for UART @ %p\n", 9710Sstevel@tonic-gate instance, (void *)asy->asy_ioaddr); 9720Sstevel@tonic-gate asy_soft_state_free(asy); 9730Sstevel@tonic-gate return (DDI_FAILURE); 9740Sstevel@tonic-gate } 9750Sstevel@tonic-gate 9760Sstevel@tonic-gate /* 9770Sstevel@tonic-gate * Initialize mutexes before accessing the hardware 9780Sstevel@tonic-gate */ 9790Sstevel@tonic-gate mutex_init(&asy->asy_excl, NULL, MUTEX_DRIVER, asy_soft_iblock); 9800Sstevel@tonic-gate mutex_init(&asy->asy_excl_hi, NULL, MUTEX_DRIVER, 9815295Srandyf (void *)asy->asy_iblock); 9825295Srandyf mutex_init(&asy->asy_soft_sr, NULL, MUTEX_DRIVER, asy_soft_iblock); 9830Sstevel@tonic-gate 9840Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 9850Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 9860Sstevel@tonic-gate 9870Sstevel@tonic-gate if (asy_identify_chip(devi, asy) != DDI_SUCCESS) { 9880Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 9890Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 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); 9940Sstevel@tonic-gate 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 10200Sstevel@tonic-gate mutex_enter(&asy_glob_lock); 10210Sstevel@tonic-gate if (asy_addedsoft == 0) { /* install the soft interrupt handler */ 10220Sstevel@tonic-gate if (ddi_add_softintr(devi, DDI_SOFTINT_MED, 10230Sstevel@tonic-gate &asy_softintr_id, NULL, 0, asysoftintr, 10240Sstevel@tonic-gate (caddr_t)0) != DDI_SUCCESS) { 10250Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 10260Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 10270Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 10280Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 10290Sstevel@tonic-gate cmn_err(CE_CONT, 10305295Srandyf "Can not set soft interrupt for ASY driver\n"); 10310Sstevel@tonic-gate asy_soft_state_free(asy); 10320Sstevel@tonic-gate return (DDI_FAILURE); 10330Sstevel@tonic-gate } 10340Sstevel@tonic-gate mutex_init(&asy_soft_lock, NULL, MUTEX_DRIVER, 10355295Srandyf (void *)asy->asy_iblock); 10360Sstevel@tonic-gate asy_addedsoft++; 10370Sstevel@tonic-gate } 10380Sstevel@tonic-gate mutex_exit(&asy_glob_lock); 10390Sstevel@tonic-gate 10400Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 10410Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 10420Sstevel@tonic-gate 10430Sstevel@tonic-gate /* 10440Sstevel@tonic-gate * Install interrupt handler for this device. 10450Sstevel@tonic-gate */ 10460Sstevel@tonic-gate if (ddi_add_intr(devi, 0, NULL, 0, asyintr, 10470Sstevel@tonic-gate (caddr_t)asy) != DDI_SUCCESS) { 10480Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 10490Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 10500Sstevel@tonic-gate mutex_destroy(&asy->asy_excl); 10510Sstevel@tonic-gate mutex_destroy(&asy->asy_excl_hi); 10520Sstevel@tonic-gate ddi_regs_map_free(&asy->asy_iohandle); 10530Sstevel@tonic-gate cmn_err(CE_CONT, 10545295Srandyf "Can not set device interrupt for ASY driver\n"); 10550Sstevel@tonic-gate asy_soft_state_free(asy); 10560Sstevel@tonic-gate return (DDI_FAILURE); 10570Sstevel@tonic-gate } 10580Sstevel@tonic-gate 10590Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 10600Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 10610Sstevel@tonic-gate 10620Sstevel@tonic-gate asyinit(asy); /* initialize the asyncline structure */ 10630Sstevel@tonic-gate 10640Sstevel@tonic-gate /* create minor device nodes for this device */ 10650Sstevel@tonic-gate if (asy->asy_com_port != 0) { 10660Sstevel@tonic-gate /* 10670Sstevel@tonic-gate * For DOS COM ports, add letter suffix so 10680Sstevel@tonic-gate * devfsadm can create correct link names. 10690Sstevel@tonic-gate */ 10700Sstevel@tonic-gate name[0] = asy->asy_com_port + 'a' - 1; 10710Sstevel@tonic-gate name[1] = '\0'; 10720Sstevel@tonic-gate } else { 10730Sstevel@tonic-gate /* 10743359Smyers * asy port which isn't a standard DOS COM 10753359Smyers * port gets a numeric name based on instance 10760Sstevel@tonic-gate */ 10773359Smyers (void) snprintf(name, ASY_MINOR_LEN, "%d", instance); 10780Sstevel@tonic-gate } 10790Sstevel@tonic-gate status = ddi_create_minor_node(devi, name, S_IFCHR, instance, 10800Sstevel@tonic-gate asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB : DDI_NT_SERIAL, NULL); 10810Sstevel@tonic-gate if (status == DDI_SUCCESS) { 10820Sstevel@tonic-gate (void) strcat(name, ",cu"); 10830Sstevel@tonic-gate status = ddi_create_minor_node(devi, name, S_IFCHR, 10840Sstevel@tonic-gate OUTLINE | instance, 10850Sstevel@tonic-gate asy->asy_com_port != 0 ? DDI_NT_SERIAL_MB_DO : 10860Sstevel@tonic-gate DDI_NT_SERIAL_DO, NULL); 10870Sstevel@tonic-gate } 10880Sstevel@tonic-gate 10890Sstevel@tonic-gate if (status != DDI_SUCCESS) { 10900Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 10910Sstevel@tonic-gate 10920Sstevel@tonic-gate ddi_remove_minor_node(devi, NULL); 10930Sstevel@tonic-gate ddi_remove_intr(devi, 0, asy->asy_iblock); 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 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 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 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 * 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 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 */ 12560Sstevel@tonic-gate 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 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 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 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 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 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 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 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 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 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 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 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 22400Sstevel@tonic-gate if ((async == NULL) || asy_addedsoft == 0 || 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); 22620Sstevel@tonic-gate /* 22630Sstevel@tonic-gate * We will loop until the interrupt line is pulled low. asy 22640Sstevel@tonic-gate * interrupt is edge triggered. 22650Sstevel@tonic-gate */ 22660Sstevel@tonic-gate /* CSTYLED */ 22675295Srandyf for (;; interrupt_id = 22685295Srandyf (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + ISR) & 0x0F)) { 22695295Srandyf 22700Sstevel@tonic-gate if (interrupt_id & NOINTERRUPT) 22710Sstevel@tonic-gate break; 22720Sstevel@tonic-gate ret_status = DDI_INTR_CLAIMED; 22730Sstevel@tonic-gate 22745295Srandyf DEBUGCONT1(ASY_DEBUG_INTR, "asyintr: interrupt_id = 0x%d\n", 22755295Srandyf interrupt_id); 22765295Srandyf lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); 22770Sstevel@tonic-gate switch (interrupt_id) { 22780Sstevel@tonic-gate case RxRDY: 22790Sstevel@tonic-gate case RSTATUS: 22800Sstevel@tonic-gate case FFTMOUT: 22810Sstevel@tonic-gate /* receiver interrupt or receiver errors */ 22820Sstevel@tonic-gate async_rxint(asy, lsr); 22830Sstevel@tonic-gate break; 22840Sstevel@tonic-gate case TxRDY: 22850Sstevel@tonic-gate /* transmit interrupt */ 22860Sstevel@tonic-gate async_txint(asy); 22870Sstevel@tonic-gate continue; 22880Sstevel@tonic-gate case MSTATUS: 22890Sstevel@tonic-gate /* modem status interrupt */ 22900Sstevel@tonic-gate async_msint(asy); 22910Sstevel@tonic-gate break; 22920Sstevel@tonic-gate } 22930Sstevel@tonic-gate if ((lsr & XHRE) && (async->async_flags & ASYNC_BUSY) && 22940Sstevel@tonic-gate (async->async_ocnt > 0)) 22950Sstevel@tonic-gate async_txint(asy); 22960Sstevel@tonic-gate } 22970Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 22980Sstevel@tonic-gate return (ret_status); 22990Sstevel@tonic-gate } 23000Sstevel@tonic-gate 23010Sstevel@tonic-gate /* 23020Sstevel@tonic-gate * Transmitter interrupt service routine. 23030Sstevel@tonic-gate * If there is more data to transmit in the current pseudo-DMA block, 23040Sstevel@tonic-gate * send the next character if output is not stopped or draining. 23050Sstevel@tonic-gate * Otherwise, queue up a soft interrupt. 23060Sstevel@tonic-gate * 23070Sstevel@tonic-gate * XXX - Needs review for HW FIFOs. 23080Sstevel@tonic-gate */ 23090Sstevel@tonic-gate static void 23100Sstevel@tonic-gate async_txint(struct asycom *asy) 23110Sstevel@tonic-gate { 23120Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 23130Sstevel@tonic-gate int fifo_len; 23140Sstevel@tonic-gate 23150Sstevel@tonic-gate /* 23160Sstevel@tonic-gate * If ASYNC_BREAK or ASYNC_OUT_SUSPEND has been set, return to 23170Sstevel@tonic-gate * asyintr()'s context to claim the interrupt without performing 23180Sstevel@tonic-gate * any action. No character will be loaded into FIFO/THR until 23190Sstevel@tonic-gate * timed or untimed break is removed 23200Sstevel@tonic-gate */ 23210Sstevel@tonic-gate if (async->async_flags & (ASYNC_BREAK|ASYNC_OUT_SUSPEND)) 23220Sstevel@tonic-gate return; 23230Sstevel@tonic-gate 23240Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 23250Sstevel@tonic-gate if (fifo_len > asy_max_tx_fifo) 23260Sstevel@tonic-gate fifo_len = asy_max_tx_fifo; 23270Sstevel@tonic-gate 23280Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 23290Sstevel@tonic-gate fifo_len--; 23300Sstevel@tonic-gate 23310Sstevel@tonic-gate if (async->async_ocnt > 0 && fifo_len > 0 && 23320Sstevel@tonic-gate !(async->async_flags & 23330Sstevel@tonic-gate (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_STOPPED))) { 23340Sstevel@tonic-gate while (fifo_len-- > 0 && async->async_ocnt-- > 0) { 23351106Smrj ddi_put8(asy->asy_iohandle, 23360Sstevel@tonic-gate asy->asy_ioaddr + DAT, *async->async_optr++); 23370Sstevel@tonic-gate } 23380Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS; 23390Sstevel@tonic-gate } 23400Sstevel@tonic-gate 23410Sstevel@tonic-gate if (fifo_len <= 0) 23420Sstevel@tonic-gate return; 23430Sstevel@tonic-gate 23440Sstevel@tonic-gate ASYSETSOFT(asy); 23450Sstevel@tonic-gate } 23460Sstevel@tonic-gate 23470Sstevel@tonic-gate /* 23480Sstevel@tonic-gate * Interrupt on port: handle PPS event. This function is only called 23490Sstevel@tonic-gate * for a port on which PPS event handling has been enabled. 23500Sstevel@tonic-gate */ 23510Sstevel@tonic-gate static void 23520Sstevel@tonic-gate asy_ppsevent(struct asycom *asy, int msr) 23530Sstevel@tonic-gate { 23540Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS_EDGE) { 23550Sstevel@tonic-gate /* Have seen leading edge, now look for and record drop */ 23560Sstevel@tonic-gate if ((msr & DCD) == 0) 23570Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE; 23580Sstevel@tonic-gate /* 23590Sstevel@tonic-gate * Waiting for leading edge, look for rise; stamp event and 23600Sstevel@tonic-gate * calibrate kernel clock. 23610Sstevel@tonic-gate */ 23620Sstevel@tonic-gate } else if (msr & DCD) { 23630Sstevel@tonic-gate /* 23640Sstevel@tonic-gate * This code captures a timestamp at the designated 23650Sstevel@tonic-gate * transition of the PPS signal (DCD asserted). The 23660Sstevel@tonic-gate * code provides a pointer to the timestamp, as well 23670Sstevel@tonic-gate * as the hardware counter value at the capture. 23680Sstevel@tonic-gate * 23690Sstevel@tonic-gate * Note: the kernel has nano based time values while 23700Sstevel@tonic-gate * NTP requires micro based, an in-line fast algorithm 23710Sstevel@tonic-gate * to convert nsec to usec is used here -- see hrt2ts() 23720Sstevel@tonic-gate * in common/os/timers.c for a full description. 23730Sstevel@tonic-gate */ 23740Sstevel@tonic-gate struct timeval *tvp = &asy_ppsev.tv; 23750Sstevel@tonic-gate timestruc_t ts; 23760Sstevel@tonic-gate long nsec, usec; 23770Sstevel@tonic-gate 23780Sstevel@tonic-gate asy->asy_flags |= ASY_PPS_EDGE; 23790Sstevel@tonic-gate LED_OFF; 23800Sstevel@tonic-gate gethrestime(&ts); 23810Sstevel@tonic-gate LED_ON; 23820Sstevel@tonic-gate nsec = ts.tv_nsec; 23830Sstevel@tonic-gate usec = nsec + (nsec >> 2); 23840Sstevel@tonic-gate usec = nsec + (usec >> 1); 23850Sstevel@tonic-gate usec = nsec + (usec >> 2); 23860Sstevel@tonic-gate usec = nsec + (usec >> 4); 23870Sstevel@tonic-gate usec = nsec - (usec >> 3); 23880Sstevel@tonic-gate usec = nsec + (usec >> 2); 23890Sstevel@tonic-gate usec = nsec + (usec >> 3); 23900Sstevel@tonic-gate usec = nsec + (usec >> 4); 23910Sstevel@tonic-gate usec = nsec + (usec >> 1); 23920Sstevel@tonic-gate usec = nsec + (usec >> 6); 23930Sstevel@tonic-gate tvp->tv_usec = usec >> 10; 23940Sstevel@tonic-gate tvp->tv_sec = ts.tv_sec; 23950Sstevel@tonic-gate 23960Sstevel@tonic-gate ++asy_ppsev.serial; 23970Sstevel@tonic-gate 23980Sstevel@tonic-gate /* 23990Sstevel@tonic-gate * Because the kernel keeps a high-resolution time, 24000Sstevel@tonic-gate * pass the current highres timestamp in tvp and zero 24010Sstevel@tonic-gate * in usec. 24020Sstevel@tonic-gate */ 24030Sstevel@tonic-gate ddi_hardpps(tvp, 0); 24040Sstevel@tonic-gate } 24050Sstevel@tonic-gate } 24060Sstevel@tonic-gate 24070Sstevel@tonic-gate /* 24080Sstevel@tonic-gate * Receiver interrupt: RxRDY interrupt, FIFO timeout interrupt or receive 24090Sstevel@tonic-gate * error interrupt. 24100Sstevel@tonic-gate * Try to put the character into the circular buffer for this line; if it 24110Sstevel@tonic-gate * overflows, indicate a circular buffer overrun. If this port is always 24120Sstevel@tonic-gate * to be serviced immediately, or the character is a STOP character, or 24130Sstevel@tonic-gate * more than 15 characters have arrived, queue up a soft interrupt to 24140Sstevel@tonic-gate * drain the circular buffer. 24150Sstevel@tonic-gate * XXX - needs review for hw FIFOs support. 24160Sstevel@tonic-gate */ 24170Sstevel@tonic-gate 24180Sstevel@tonic-gate static void 24190Sstevel@tonic-gate async_rxint(struct asycom *asy, uchar_t lsr) 24200Sstevel@tonic-gate { 24210Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 24220Sstevel@tonic-gate uchar_t c; 24230Sstevel@tonic-gate uint_t s, needsoft = 0; 24240Sstevel@tonic-gate tty_common_t *tp; 24250Sstevel@tonic-gate int looplim = asy->asy_fifo_buf * 2; 24260Sstevel@tonic-gate 24270Sstevel@tonic-gate tp = &async->async_ttycommon; 24280Sstevel@tonic-gate if (!(tp->t_cflag & CREAD)) { 24290Sstevel@tonic-gate while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 24301106Smrj (void) (ddi_get8(asy->asy_iohandle, 24315295Srandyf asy->asy_ioaddr + DAT) & 0xff); 24321106Smrj lsr = ddi_get8(asy->asy_iohandle, 24335295Srandyf asy->asy_ioaddr + LSR); 24340Sstevel@tonic-gate if (looplim-- < 0) /* limit loop */ 24350Sstevel@tonic-gate break; 24360Sstevel@tonic-gate } 24370Sstevel@tonic-gate return; /* line is not open for read? */ 24380Sstevel@tonic-gate } 24390Sstevel@tonic-gate 24400Sstevel@tonic-gate while (lsr & (RCA|PARERR|FRMERR|BRKDET|OVRRUN)) { 24410Sstevel@tonic-gate c = 0; 24420Sstevel@tonic-gate s = 0; /* reset error status */ 24430Sstevel@tonic-gate if (lsr & RCA) { 24441106Smrj c = ddi_get8(asy->asy_iohandle, 24455295Srandyf asy->asy_ioaddr + DAT) & 0xff; 24460Sstevel@tonic-gate 24470Sstevel@tonic-gate /* 24480Sstevel@tonic-gate * We handle XON/XOFF char if IXON is set, 24490Sstevel@tonic-gate * but if received char is _POSIX_VDISABLE, 24500Sstevel@tonic-gate * we left it to the up level module. 24510Sstevel@tonic-gate */ 24520Sstevel@tonic-gate if (tp->t_iflag & IXON) { 24530Sstevel@tonic-gate if ((c == async->async_stopc) && 24540Sstevel@tonic-gate (c != _POSIX_VDISABLE)) { 24550Sstevel@tonic-gate async_flowcontrol_sw_output(asy, 24560Sstevel@tonic-gate FLOW_STOP); 24570Sstevel@tonic-gate goto check_looplim; 24580Sstevel@tonic-gate } else if ((c == async->async_startc) && 24590Sstevel@tonic-gate (c != _POSIX_VDISABLE)) { 24600Sstevel@tonic-gate async_flowcontrol_sw_output(asy, 24610Sstevel@tonic-gate FLOW_START); 24620Sstevel@tonic-gate needsoft = 1; 24630Sstevel@tonic-gate goto check_looplim; 24640Sstevel@tonic-gate } 24650Sstevel@tonic-gate if ((tp->t_iflag & IXANY) && 24660Sstevel@tonic-gate (async->async_flags & ASYNC_SW_OUT_FLW)) { 24670Sstevel@tonic-gate async_flowcontrol_sw_output(asy, 24680Sstevel@tonic-gate FLOW_START); 24690Sstevel@tonic-gate needsoft = 1; 24700Sstevel@tonic-gate } 24710Sstevel@tonic-gate } 24720Sstevel@tonic-gate } 24730Sstevel@tonic-gate 24740Sstevel@tonic-gate /* 24750Sstevel@tonic-gate * Check for character break sequence 24760Sstevel@tonic-gate */ 24770Sstevel@tonic-gate if ((abort_enable == KIOCABORTALTERNATE) && 24780Sstevel@tonic-gate (asy->asy_flags & ASY_CONSOLE)) { 24790Sstevel@tonic-gate if (abort_charseq_recognize(c)) 24800Sstevel@tonic-gate abort_sequence_enter((char *)NULL); 24810Sstevel@tonic-gate } 24820Sstevel@tonic-gate 24830Sstevel@tonic-gate /* Handle framing errors */ 24840Sstevel@tonic-gate if (lsr & (PARERR|FRMERR|BRKDET|OVRRUN)) { 24850Sstevel@tonic-gate if (lsr & PARERR) { 24860Sstevel@tonic-gate if (tp->t_iflag & INPCK) /* parity enabled */ 24870Sstevel@tonic-gate s |= PERROR; 24880Sstevel@tonic-gate } 24890Sstevel@tonic-gate 24900Sstevel@tonic-gate if (lsr & (FRMERR|BRKDET)) 24910Sstevel@tonic-gate s |= FRERROR; 24920Sstevel@tonic-gate if (lsr & OVRRUN) { 24930Sstevel@tonic-gate async->async_hw_overrun = 1; 24940Sstevel@tonic-gate s |= OVERRUN; 24950Sstevel@tonic-gate } 24960Sstevel@tonic-gate } 24970Sstevel@tonic-gate 24980Sstevel@tonic-gate if (s == 0) 24990Sstevel@tonic-gate if ((tp->t_iflag & PARMRK) && 25000Sstevel@tonic-gate !(tp->t_iflag & (IGNPAR|ISTRIP)) && 25010Sstevel@tonic-gate (c == 0377)) 25020Sstevel@tonic-gate if (RING_POK(async, 2)) { 25030Sstevel@tonic-gate RING_PUT(async, 0377); 25040Sstevel@tonic-gate RING_PUT(async, c); 25050Sstevel@tonic-gate } else 25060Sstevel@tonic-gate async->async_sw_overrun = 1; 25070Sstevel@tonic-gate else 25080Sstevel@tonic-gate if (RING_POK(async, 1)) 25090Sstevel@tonic-gate RING_PUT(async, c); 25100Sstevel@tonic-gate else 25110Sstevel@tonic-gate async->async_sw_overrun = 1; 25120Sstevel@tonic-gate else 25130Sstevel@tonic-gate if (s & FRERROR) /* Handle framing errors */ 25140Sstevel@tonic-gate if (c == 0) 25150Sstevel@tonic-gate if ((asy->asy_flags & ASY_CONSOLE) && 25160Sstevel@tonic-gate (abort_enable != 25170Sstevel@tonic-gate KIOCABORTALTERNATE)) 25180Sstevel@tonic-gate abort_sequence_enter((char *)0); 25190Sstevel@tonic-gate else 25200Sstevel@tonic-gate async->async_break++; 25210Sstevel@tonic-gate else 25220Sstevel@tonic-gate if (RING_POK(async, 1)) 25230Sstevel@tonic-gate RING_MARK(async, c, s); 25240Sstevel@tonic-gate else 25250Sstevel@tonic-gate async->async_sw_overrun = 1; 25260Sstevel@tonic-gate else /* Parity errors are handled by ldterm */ 25270Sstevel@tonic-gate if (RING_POK(async, 1)) 25280Sstevel@tonic-gate RING_MARK(async, c, s); 25290Sstevel@tonic-gate else 25300Sstevel@tonic-gate async->async_sw_overrun = 1; 25310Sstevel@tonic-gate check_looplim: 25325295Srandyf lsr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR); 25330Sstevel@tonic-gate if (looplim-- < 0) /* limit loop */ 25340Sstevel@tonic-gate break; 25350Sstevel@tonic-gate } 25360Sstevel@tonic-gate if ((RING_CNT(async) > (RINGSIZE * 3)/4) && 25370Sstevel@tonic-gate !(async->async_inflow_source & IN_FLOW_RINGBUFF)) { 25380Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP, IN_FLOW_RINGBUFF); 25390Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 25400Sstevel@tonic-gate IN_FLOW_RINGBUFF); 25410Sstevel@tonic-gate } 25420Sstevel@tonic-gate 25430Sstevel@tonic-gate if ((async->async_flags & ASYNC_SERVICEIMM) || needsoft || 25440Sstevel@tonic-gate (RING_FRAC(async)) || (async->async_polltid == 0)) 25450Sstevel@tonic-gate ASYSETSOFT(asy); /* need a soft interrupt */ 25460Sstevel@tonic-gate } 25470Sstevel@tonic-gate 25480Sstevel@tonic-gate /* 25490Sstevel@tonic-gate * Modem status interrupt. 25500Sstevel@tonic-gate * 25510Sstevel@tonic-gate * (Note: It is assumed that the MSR hasn't been read by asyintr().) 25520Sstevel@tonic-gate */ 25530Sstevel@tonic-gate 25540Sstevel@tonic-gate static void 25550Sstevel@tonic-gate async_msint(struct asycom *asy) 25560Sstevel@tonic-gate { 25570Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 25580Sstevel@tonic-gate int msr, t_cflag = async->async_ttycommon.t_cflag; 25590Sstevel@tonic-gate #ifdef DEBUG 25600Sstevel@tonic-gate int instance = UNIT(async->async_dev); 25610Sstevel@tonic-gate #endif 25620Sstevel@tonic-gate 25630Sstevel@tonic-gate async_msint_retry: 25640Sstevel@tonic-gate /* this resets the interrupt */ 25651106Smrj msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 25660Sstevel@tonic-gate DEBUGCONT10(ASY_DEBUG_STATE, 25675295Srandyf "async%d_msint call #%d:\n" 25685295Srandyf " transition: %3s %3s %3s %3s\n" 25695295Srandyf "current state: %3s %3s %3s %3s\n", 25705295Srandyf instance, 25715295Srandyf ++(asy->asy_msint_cnt), 25725295Srandyf (msr & DCTS) ? "DCTS" : " ", 25735295Srandyf (msr & DDSR) ? "DDSR" : " ", 25745295Srandyf (msr & DRI) ? "DRI " : " ", 25755295Srandyf (msr & DDCD) ? "DDCD" : " ", 25765295Srandyf (msr & CTS) ? "CTS " : " ", 25775295Srandyf (msr & DSR) ? "DSR " : " ", 25785295Srandyf (msr & RI) ? "RI " : " ", 25795295Srandyf (msr & DCD) ? "DCD " : " "); 25800Sstevel@tonic-gate 25810Sstevel@tonic-gate /* If CTS status is changed, do H/W output flow control */ 25820Sstevel@tonic-gate if ((t_cflag & CRTSCTS) && (((asy->asy_msr ^ msr) & CTS) != 0)) 25830Sstevel@tonic-gate async_flowcontrol_hw_output(asy, 25840Sstevel@tonic-gate msr & CTS ? FLOW_START : FLOW_STOP); 25850Sstevel@tonic-gate /* 25860Sstevel@tonic-gate * Reading MSR resets the interrupt, we save the 25870Sstevel@tonic-gate * value of msr so that other functions could examine MSR by 25880Sstevel@tonic-gate * looking at asy_msr. 25890Sstevel@tonic-gate */ 25900Sstevel@tonic-gate asy->asy_msr = (uchar_t)msr; 25910Sstevel@tonic-gate 25920Sstevel@tonic-gate /* Handle PPS event */ 25930Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS) 25940Sstevel@tonic-gate asy_ppsevent(asy, msr); 25950Sstevel@tonic-gate 25960Sstevel@tonic-gate async->async_ext++; 25970Sstevel@tonic-gate ASYSETSOFT(asy); 25980Sstevel@tonic-gate /* 25990Sstevel@tonic-gate * We will make sure that the modem status presented to us 26000Sstevel@tonic-gate * during the previous read has not changed. If the chip samples 26010Sstevel@tonic-gate * the modem status on the falling edge of the interrupt line, 26020Sstevel@tonic-gate * and uses this state as the base for detecting change of modem 26030Sstevel@tonic-gate * status, we would miss a change of modem status event that occured 26040Sstevel@tonic-gate * after we initiated a read MSR operation. 26050Sstevel@tonic-gate */ 26061106Smrj msr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MSR); 26070Sstevel@tonic-gate if (STATES(msr) != STATES(asy->asy_msr)) 26080Sstevel@tonic-gate goto async_msint_retry; 26090Sstevel@tonic-gate } 26100Sstevel@tonic-gate 26110Sstevel@tonic-gate /* 26120Sstevel@tonic-gate * Handle a second-stage interrupt. 26130Sstevel@tonic-gate */ 26140Sstevel@tonic-gate /*ARGSUSED*/ 26150Sstevel@tonic-gate uint_t 26160Sstevel@tonic-gate asysoftintr(caddr_t intarg) 26170Sstevel@tonic-gate { 26180Sstevel@tonic-gate struct asycom *asy; 26190Sstevel@tonic-gate int rv; 26200Sstevel@tonic-gate int instance; 26210Sstevel@tonic-gate 26220Sstevel@tonic-gate /* 26230Sstevel@tonic-gate * Test and clear soft interrupt. 26240Sstevel@tonic-gate */ 26250Sstevel@tonic-gate mutex_enter(&asy_soft_lock); 26260Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_PROCS, "asysoftintr: enter\n"); 26270Sstevel@tonic-gate rv = asysoftpend; 26280Sstevel@tonic-gate if (rv != 0) 26290Sstevel@tonic-gate asysoftpend = 0; 26300Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 26310Sstevel@tonic-gate 26320Sstevel@tonic-gate if (rv) { 26330Sstevel@tonic-gate /* 26340Sstevel@tonic-gate * Note - we can optimize the loop by remembering the last 26350Sstevel@tonic-gate * device that requested soft interrupt 26360Sstevel@tonic-gate */ 26370Sstevel@tonic-gate for (instance = 0; instance <= max_asy_instance; instance++) { 26380Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 26390Sstevel@tonic-gate if (asy == NULL || asy->asy_priv == NULL) 26400Sstevel@tonic-gate continue; 26410Sstevel@tonic-gate mutex_enter(&asy_soft_lock); 26420Sstevel@tonic-gate if (asy->asy_flags & ASY_NEEDSOFT) { 26430Sstevel@tonic-gate asy->asy_flags &= ~ASY_NEEDSOFT; 26440Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 26450Sstevel@tonic-gate async_softint(asy); 26460Sstevel@tonic-gate } else 26470Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 26480Sstevel@tonic-gate } 26490Sstevel@tonic-gate } 26500Sstevel@tonic-gate return (rv ? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 26510Sstevel@tonic-gate } 26520Sstevel@tonic-gate 26530Sstevel@tonic-gate /* 26540Sstevel@tonic-gate * Handle a software interrupt. 26550Sstevel@tonic-gate */ 26560Sstevel@tonic-gate static void 26570Sstevel@tonic-gate async_softint(struct asycom *asy) 26580Sstevel@tonic-gate { 26590Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 26600Sstevel@tonic-gate short cc; 26610Sstevel@tonic-gate mblk_t *bp; 26620Sstevel@tonic-gate queue_t *q; 26630Sstevel@tonic-gate uchar_t val; 26640Sstevel@tonic-gate uchar_t c; 26650Sstevel@tonic-gate tty_common_t *tp; 26660Sstevel@tonic-gate int nb; 26670Sstevel@tonic-gate int instance = UNIT(async->async_dev); 26680Sstevel@tonic-gate 26690Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint\n", instance); 26700Sstevel@tonic-gate mutex_enter(&asy_soft_lock); 26710Sstevel@tonic-gate if (asy->asy_flags & ASY_DOINGSOFT) { 26720Sstevel@tonic-gate asy->asy_flags |= ASY_DOINGSOFT_RETRY; 26730Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 26740Sstevel@tonic-gate return; 26750Sstevel@tonic-gate } 26760Sstevel@tonic-gate asy->asy_flags |= ASY_DOINGSOFT; 26770Sstevel@tonic-gate begin: 26780Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT_RETRY; 26790Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 26800Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 26810Sstevel@tonic-gate tp = &async->async_ttycommon; 26820Sstevel@tonic-gate q = tp->t_readq; 26830Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_FLW_RESUME) { 26840Sstevel@tonic-gate if (async->async_ocnt > 0) { 26850Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 26860Sstevel@tonic-gate async_resume(async); 26870Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 26880Sstevel@tonic-gate } else { 26890Sstevel@tonic-gate if (async->async_xmitblk) 26900Sstevel@tonic-gate freeb(async->async_xmitblk); 26910Sstevel@tonic-gate async->async_xmitblk = NULL; 26920Sstevel@tonic-gate async_start(async); 26930Sstevel@tonic-gate } 26940Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 26950Sstevel@tonic-gate } 26960Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 26970Sstevel@tonic-gate if (async->async_ext) { 26980Sstevel@tonic-gate async->async_ext = 0; 26990Sstevel@tonic-gate /* check for carrier up */ 27000Sstevel@tonic-gate DEBUGCONT3(ASY_DEBUG_MODM2, 27015295Srandyf "async%d_softint: asy_msr & DCD = %x, " 27025295Srandyf "tp->t_flags & TS_SOFTCAR = %x\n", 27035295Srandyf instance, asy->asy_msr & DCD, tp->t_flags & TS_SOFTCAR); 27045295Srandyf 27050Sstevel@tonic-gate if (asy->asy_msr & DCD) { 27060Sstevel@tonic-gate /* carrier present */ 27070Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) == 0) { 27080Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODM2, 27095295Srandyf "async%d_softint: set ASYNC_CARR_ON\n", 27105295Srandyf instance); 27110Sstevel@tonic-gate async->async_flags |= ASYNC_CARR_ON; 27120Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 27130Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 27140Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 27150Sstevel@tonic-gate (void) putctl(q, M_UNHANGUP); 27160Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 27170Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 27180Sstevel@tonic-gate } 27190Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 27200Sstevel@tonic-gate } 27210Sstevel@tonic-gate } else { 27220Sstevel@tonic-gate if ((async->async_flags & ASYNC_CARR_ON) && 27230Sstevel@tonic-gate !(tp->t_cflag & CLOCAL) && 27240Sstevel@tonic-gate !(tp->t_flags & TS_SOFTCAR)) { 27250Sstevel@tonic-gate int flushflag; 27260Sstevel@tonic-gate 27270Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, 27285295Srandyf "async%d_softint: carrier dropped, " 27295295Srandyf "so drop DTR\n", 27305295Srandyf instance); 27310Sstevel@tonic-gate /* 27320Sstevel@tonic-gate * Carrier went away. 27330Sstevel@tonic-gate * Drop DTR, abort any output in 27340Sstevel@tonic-gate * progress, indicate that output is 27350Sstevel@tonic-gate * not stopped, and send a hangup 27360Sstevel@tonic-gate * notification upstream. 27370Sstevel@tonic-gate */ 27381106Smrj val = ddi_get8(asy->asy_iohandle, 27395295Srandyf asy->asy_ioaddr + MCR); 27401106Smrj ddi_put8(asy->asy_iohandle, 27410Sstevel@tonic-gate asy->asy_ioaddr + MCR, (val & ~DTR)); 27425295Srandyf 27430Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) { 27445295Srandyf DEBUGCONT0(ASY_DEBUG_BUSY, 27450Sstevel@tonic-gate "async_softint: " 27460Sstevel@tonic-gate "Carrier dropped. " 27470Sstevel@tonic-gate "Clearing async_ocnt\n"); 27485295Srandyf async->async_ocnt = 0; 27490Sstevel@tonic-gate } /* if */ 27500Sstevel@tonic-gate 27510Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED; 27520Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 27535295Srandyf mutex_exit(&asy->asy_excl_hi); 27545295Srandyf mutex_exit(&asy->asy_excl); 27555295Srandyf (void) putctl(q, M_HANGUP); 27565295Srandyf mutex_enter(&asy->asy_excl); 27575295Srandyf DEBUGCONT1(ASY_DEBUG_MODEM, 27585295Srandyf "async%d_softint: " 27595295Srandyf "putctl(q, M_HANGUP)\n", 27605295Srandyf instance); 27615295Srandyf /* 27625295Srandyf * Flush FIFO buffers 27635295Srandyf * Any data left in there is invalid now 27645295Srandyf */ 27655295Srandyf if (asy->asy_use_fifo == FIFO_ON) 27665295Srandyf asy_reset_fifo(asy, FIFOTXFLSH); 27675295Srandyf /* 27685295Srandyf * Flush our write queue if we have one. 27695295Srandyf * If we're in the midst of close, then 27705295Srandyf * flush everything. Don't leave stale 27715295Srandyf * ioctls lying about. 27725295Srandyf */ 27735295Srandyf flushflag = (async->async_flags & 27745295Srandyf ASYNC_CLOSING) ? FLUSHALL : 27755295Srandyf FLUSHDATA; 27765295Srandyf flushq(tp->t_writeq, flushflag); 27775295Srandyf 27785295Srandyf /* active msg */ 27795295Srandyf bp = async->async_xmitblk; 27805295Srandyf if (bp != NULL) { 27815295Srandyf freeb(bp); 27825295Srandyf async->async_xmitblk = NULL; 27835295Srandyf } 27845295Srandyf 27855295Srandyf mutex_enter(&asy->asy_excl_hi); 27865295Srandyf async->async_flags &= ~ASYNC_BUSY; 27875295Srandyf /* 27885295Srandyf * This message warns of Carrier loss 27895295Srandyf * with data left to transmit can hang 27905295Srandyf * the system. 27915295Srandyf */ 27925295Srandyf DEBUGCONT0(ASY_DEBUG_MODEM, 27935295Srandyf "async_softint: Flushing to " 27945295Srandyf "prevent HUPCL hanging\n"); 27950Sstevel@tonic-gate } /* if (ASYNC_ISOPEN) */ 27960Sstevel@tonic-gate } /* if (ASYNC_CARR_ON && CLOCAL) */ 27970Sstevel@tonic-gate async->async_flags &= ~ASYNC_CARR_ON; 27980Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 27990Sstevel@tonic-gate } /* else */ 28000Sstevel@tonic-gate } /* if (async->async_ext) */ 28010Sstevel@tonic-gate 28020Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 28030Sstevel@tonic-gate 28040Sstevel@tonic-gate /* 28050Sstevel@tonic-gate * If data has been added to the circular buffer, remove 28060Sstevel@tonic-gate * it from the buffer, and send it up the stream if there's 28070Sstevel@tonic-gate * somebody listening. Try to do it 16 bytes at a time. If we 28080Sstevel@tonic-gate * have more than 16 bytes to move, move 16 byte chunks and 28090Sstevel@tonic-gate * leave the rest for next time around (maybe it will grow). 28100Sstevel@tonic-gate */ 28110Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28120Sstevel@tonic-gate if (!(async->async_flags & ASYNC_ISOPEN)) { 28130Sstevel@tonic-gate RING_INIT(async); 28140Sstevel@tonic-gate goto rv; 28150Sstevel@tonic-gate } 28160Sstevel@tonic-gate if ((cc = RING_CNT(async)) <= 0) 28170Sstevel@tonic-gate goto rv; 28180Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 28190Sstevel@tonic-gate 28200Sstevel@tonic-gate if (!canput(q)) { 28210Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28220Sstevel@tonic-gate if (!(async->async_inflow_source & IN_FLOW_STREAMS)) { 28230Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_STOP, 28240Sstevel@tonic-gate IN_FLOW_STREAMS); 28250Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_STOP, 28260Sstevel@tonic-gate IN_FLOW_STREAMS); 28270Sstevel@tonic-gate } 28280Sstevel@tonic-gate goto rv; 28290Sstevel@tonic-gate } 28300Sstevel@tonic-gate if (async->async_inflow_source & IN_FLOW_STREAMS) { 28310Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28320Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START, 28330Sstevel@tonic-gate IN_FLOW_STREAMS); 28340Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_START, 28350Sstevel@tonic-gate IN_FLOW_STREAMS); 28360Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 28370Sstevel@tonic-gate } 28385295Srandyf 28395295Srandyf DEBUGCONT2(ASY_DEBUG_INPUT, "async%d_softint: %d char(s) in queue.\n", 28405295Srandyf instance, cc); 28415295Srandyf 28420Sstevel@tonic-gate if (!(bp = allocb(cc, BPRI_MED))) { 28430Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 28440Sstevel@tonic-gate ttycommon_qfull(&async->async_ttycommon, q); 28450Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 28460Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28470Sstevel@tonic-gate goto rv; 28480Sstevel@tonic-gate } 28490Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28500Sstevel@tonic-gate do { 28510Sstevel@tonic-gate if (RING_ERR(async, S_ERRORS)) { 28520Sstevel@tonic-gate RING_UNMARK(async); 28530Sstevel@tonic-gate c = RING_GET(async); 28540Sstevel@tonic-gate break; 28550Sstevel@tonic-gate } else 28560Sstevel@tonic-gate *bp->b_wptr++ = RING_GET(async); 28570Sstevel@tonic-gate } while (--cc); 28580Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 28590Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 28600Sstevel@tonic-gate if (bp->b_wptr > bp->b_rptr) { 28610Sstevel@tonic-gate if (!canput(q)) { 28620Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: local queue full", 28635295Srandyf instance); 28640Sstevel@tonic-gate freemsg(bp); 28650Sstevel@tonic-gate } else 28660Sstevel@tonic-gate (void) putq(q, bp); 28670Sstevel@tonic-gate } else 28680Sstevel@tonic-gate freemsg(bp); 28690Sstevel@tonic-gate /* 28700Sstevel@tonic-gate * If we have a parity error, then send 28710Sstevel@tonic-gate * up an M_BREAK with the "bad" 28720Sstevel@tonic-gate * character as an argument. Let ldterm 28730Sstevel@tonic-gate * figure out what to do with the error. 28740Sstevel@tonic-gate */ 28750Sstevel@tonic-gate if (cc) { 28760Sstevel@tonic-gate (void) putctl1(q, M_BREAK, c); 28770Sstevel@tonic-gate ASYSETSOFT(async->async_common); /* finish cc chars */ 28780Sstevel@tonic-gate } 28790Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 28800Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 28810Sstevel@tonic-gate rv: 28820Sstevel@tonic-gate if ((RING_CNT(async) < (RINGSIZE/4)) && 28830Sstevel@tonic-gate (async->async_inflow_source & IN_FLOW_RINGBUFF)) { 28840Sstevel@tonic-gate async_flowcontrol_hw_input(asy, FLOW_START, IN_FLOW_RINGBUFF); 28850Sstevel@tonic-gate (void) async_flowcontrol_sw_input(asy, FLOW_START, 28860Sstevel@tonic-gate IN_FLOW_RINGBUFF); 28870Sstevel@tonic-gate } 28880Sstevel@tonic-gate 28890Sstevel@tonic-gate /* 28900Sstevel@tonic-gate * If a transmission has finished, indicate that it's finished, 28910Sstevel@tonic-gate * and start that line up again. 28920Sstevel@tonic-gate */ 28930Sstevel@tonic-gate if (async->async_break > 0) { 28940Sstevel@tonic-gate nb = async->async_break; 28950Sstevel@tonic-gate async->async_break = 0; 28960Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 28970Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 28980Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 28990Sstevel@tonic-gate for (; nb > 0; nb--) 29000Sstevel@tonic-gate (void) putctl(q, M_BREAK); 29010Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 29020Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 29030Sstevel@tonic-gate } 29040Sstevel@tonic-gate } 29050Sstevel@tonic-gate if (async->async_ocnt <= 0 && (async->async_flags & ASYNC_BUSY)) { 29060Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_BUSY, 29070Sstevel@tonic-gate "async%d_softint: Clearing ASYNC_BUSY. async_ocnt=%d\n", 29080Sstevel@tonic-gate instance, 29090Sstevel@tonic-gate async->async_ocnt); 29100Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 29110Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 29120Sstevel@tonic-gate if (async->async_xmitblk) 29130Sstevel@tonic-gate freeb(async->async_xmitblk); 29140Sstevel@tonic-gate async->async_xmitblk = NULL; 29150Sstevel@tonic-gate async_start(async); 29160Sstevel@tonic-gate /* 29170Sstevel@tonic-gate * If the flag isn't set after doing the async_start above, we 29180Sstevel@tonic-gate * may have finished all the queued output. Signal any thread 29190Sstevel@tonic-gate * stuck in close. 29200Sstevel@tonic-gate */ 29210Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BUSY)) 29220Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 29230Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 29240Sstevel@tonic-gate } 29250Sstevel@tonic-gate /* 29260Sstevel@tonic-gate * A note about these overrun bits: all they do is *tell* someone 29270Sstevel@tonic-gate * about an error- They do not track multiple errors. In fact, 29280Sstevel@tonic-gate * you could consider them latched register bits if you like. 29290Sstevel@tonic-gate * We are only interested in printing the error message once for 29300Sstevel@tonic-gate * any cluster of overrun errrors. 29310Sstevel@tonic-gate */ 29320Sstevel@tonic-gate if (async->async_hw_overrun) { 29330Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 29340Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 29350Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 29360Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: silo overflow", instance); 29370Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 29380Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 29390Sstevel@tonic-gate } 29400Sstevel@tonic-gate async->async_hw_overrun = 0; 29410Sstevel@tonic-gate } 29420Sstevel@tonic-gate if (async->async_sw_overrun) { 29430Sstevel@tonic-gate if (async->async_flags & ASYNC_ISOPEN) { 29440Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 29450Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 29460Sstevel@tonic-gate asyerror(CE_NOTE, "asy%d: ring buffer overflow", 29475295Srandyf instance); 29480Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 29490Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 29500Sstevel@tonic-gate } 29510Sstevel@tonic-gate async->async_sw_overrun = 0; 29520Sstevel@tonic-gate } 29530Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 29540Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 29550Sstevel@tonic-gate mutex_enter(&asy_soft_lock); 29560Sstevel@tonic-gate if (asy->asy_flags & ASY_DOINGSOFT_RETRY) { 29570Sstevel@tonic-gate goto begin; 29580Sstevel@tonic-gate } 29590Sstevel@tonic-gate asy->asy_flags &= ~ASY_DOINGSOFT; 29600Sstevel@tonic-gate mutex_exit(&asy_soft_lock); 29610Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_softint: done\n", instance); 29620Sstevel@tonic-gate } 29630Sstevel@tonic-gate 29640Sstevel@tonic-gate /* 29650Sstevel@tonic-gate * Restart output on a line after a delay or break timer expired. 29660Sstevel@tonic-gate */ 29670Sstevel@tonic-gate static void 29680Sstevel@tonic-gate async_restart(void *arg) 29690Sstevel@tonic-gate { 29700Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)arg; 29710Sstevel@tonic-gate struct asycom *asy = async->async_common; 29720Sstevel@tonic-gate uchar_t lcr; 29730Sstevel@tonic-gate 29740Sstevel@tonic-gate /* 29750Sstevel@tonic-gate * If break timer expired, turn off the break bit. 29760Sstevel@tonic-gate */ 29770Sstevel@tonic-gate #ifdef DEBUG 29780Sstevel@tonic-gate int instance = UNIT(async->async_dev); 29790Sstevel@tonic-gate 29800Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_restart\n", instance); 29810Sstevel@tonic-gate #endif 29820Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 29830Sstevel@tonic-gate /* 29840Sstevel@tonic-gate * If ASYNC_OUT_SUSPEND is also set, we don't really 29850Sstevel@tonic-gate * clean the HW break, TIOCCBRK is responsible for this. 29860Sstevel@tonic-gate */ 29870Sstevel@tonic-gate if ((async->async_flags & ASYNC_BREAK) && 29880Sstevel@tonic-gate !(async->async_flags & ASYNC_OUT_SUSPEND)) { 29890Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 29905295Srandyf lcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 29911106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 29925295Srandyf (lcr & ~SETBREAK)); 29930Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 29940Sstevel@tonic-gate } 29950Sstevel@tonic-gate async->async_flags &= ~(ASYNC_DELAY|ASYNC_BREAK); 29960Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 29970Sstevel@tonic-gate async_start(async); 29980Sstevel@tonic-gate 29990Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 30000Sstevel@tonic-gate } 30010Sstevel@tonic-gate 30020Sstevel@tonic-gate static void 30030Sstevel@tonic-gate async_start(struct asyncline *async) 30040Sstevel@tonic-gate { 30050Sstevel@tonic-gate async_nstart(async, 0); 30060Sstevel@tonic-gate } 30070Sstevel@tonic-gate 30080Sstevel@tonic-gate /* 30090Sstevel@tonic-gate * Start output on a line, unless it's busy, frozen, or otherwise. 30100Sstevel@tonic-gate */ 30110Sstevel@tonic-gate /*ARGSUSED*/ 30120Sstevel@tonic-gate static void 30130Sstevel@tonic-gate async_nstart(struct asyncline *async, int mode) 30140Sstevel@tonic-gate { 30150Sstevel@tonic-gate struct asycom *asy = async->async_common; 30160Sstevel@tonic-gate int cc; 30170Sstevel@tonic-gate queue_t *q; 30180Sstevel@tonic-gate mblk_t *bp; 30190Sstevel@tonic-gate uchar_t *xmit_addr; 30200Sstevel@tonic-gate uchar_t val; 30210Sstevel@tonic-gate int fifo_len = 1; 30220Sstevel@tonic-gate boolean_t didsome; 30230Sstevel@tonic-gate mblk_t *nbp; 30240Sstevel@tonic-gate 30250Sstevel@tonic-gate #ifdef DEBUG 30260Sstevel@tonic-gate int instance = UNIT(async->async_dev); 30270Sstevel@tonic-gate 30280Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_nstart\n", instance); 30290Sstevel@tonic-gate #endif 30300Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 30310Sstevel@tonic-gate fifo_len = asy->asy_fifo_buf; /* with FIFO buffers */ 30320Sstevel@tonic-gate if (fifo_len > asy_max_tx_fifo) 30330Sstevel@tonic-gate fifo_len = asy_max_tx_fifo; 30340Sstevel@tonic-gate } 30350Sstevel@tonic-gate 30360Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 30370Sstevel@tonic-gate 30380Sstevel@tonic-gate /* 30390Sstevel@tonic-gate * If the chip is busy (i.e., we're waiting for a break timeout 30400Sstevel@tonic-gate * to expire, or for the current transmission to finish, or for 30410Sstevel@tonic-gate * output to finish draining from chip), don't grab anything new. 30420Sstevel@tonic-gate */ 30430Sstevel@tonic-gate if (async->async_flags & (ASYNC_BREAK|ASYNC_BUSY)) { 30440Sstevel@tonic-gate DEBUGCONT2((mode? ASY_DEBUG_OUT : 0), 30455295Srandyf "async%d_nstart: start %s.\n", 30465295Srandyf instance, 30475295Srandyf async->async_flags & ASYNC_BREAK ? "break" : "busy"); 30480Sstevel@tonic-gate return; 30490Sstevel@tonic-gate } 30500Sstevel@tonic-gate 30510Sstevel@tonic-gate /* 30520Sstevel@tonic-gate * Check only pended sw input flow control. 30530Sstevel@tonic-gate */ 30540Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 30550Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 30560Sstevel@tonic-gate fifo_len--; 30570Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 30580Sstevel@tonic-gate 30590Sstevel@tonic-gate /* 30600Sstevel@tonic-gate * If we're waiting for a delay timeout to expire, don't grab 30610Sstevel@tonic-gate * anything new. 30620Sstevel@tonic-gate */ 30630Sstevel@tonic-gate if (async->async_flags & ASYNC_DELAY) { 30640Sstevel@tonic-gate DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), 30655295Srandyf "async%d_nstart: start ASYNC_DELAY.\n", instance); 30660Sstevel@tonic-gate return; 30670Sstevel@tonic-gate } 30680Sstevel@tonic-gate 30690Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) { 30700Sstevel@tonic-gate DEBUGCONT1((mode? ASY_DEBUG_OUT : 0), 30715295Srandyf "async%d_nstart: start writeq is null.\n", instance); 30720Sstevel@tonic-gate return; /* not attached to a stream */ 30730Sstevel@tonic-gate } 30740Sstevel@tonic-gate 30750Sstevel@tonic-gate for (;;) { 30760Sstevel@tonic-gate if ((bp = getq(q)) == NULL) 30770Sstevel@tonic-gate return; /* no data to transmit */ 30780Sstevel@tonic-gate 30790Sstevel@tonic-gate /* 30800Sstevel@tonic-gate * We have a message block to work on. 30810Sstevel@tonic-gate * Check whether it's a break, a delay, or an ioctl (the latter 30820Sstevel@tonic-gate * occurs if the ioctl in question was waiting for the output 30830Sstevel@tonic-gate * to drain). If it's one of those, process it immediately. 30840Sstevel@tonic-gate */ 30850Sstevel@tonic-gate switch (bp->b_datap->db_type) { 30860Sstevel@tonic-gate 30870Sstevel@tonic-gate case M_BREAK: 30880Sstevel@tonic-gate /* 30890Sstevel@tonic-gate * Set the break bit, and arrange for "async_restart" 30900Sstevel@tonic-gate * to be called in 1/4 second; it will turn the 30910Sstevel@tonic-gate * break bit off, and call "async_start" to grab 30920Sstevel@tonic-gate * the next message. 30930Sstevel@tonic-gate */ 30940Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 30951106Smrj val = ddi_get8(asy->asy_iohandle, 30965295Srandyf asy->asy_ioaddr + LCR); 30975295Srandyf ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 30985295Srandyf (val | SETBREAK)); 30990Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 31000Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK; 31010Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async, 31020Sstevel@tonic-gate drv_usectohz(1000000)/4); 31030Sstevel@tonic-gate freemsg(bp); 31040Sstevel@tonic-gate return; /* wait for this to finish */ 31050Sstevel@tonic-gate 31060Sstevel@tonic-gate case M_DELAY: 31070Sstevel@tonic-gate /* 31080Sstevel@tonic-gate * Arrange for "async_restart" to be called when the 31090Sstevel@tonic-gate * delay expires; it will turn ASYNC_DELAY off, 31100Sstevel@tonic-gate * and call "async_start" to grab the next message. 31110Sstevel@tonic-gate */ 31120Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async, 31130Sstevel@tonic-gate (int)(*(unsigned char *)bp->b_rptr + 6)); 31140Sstevel@tonic-gate async->async_flags |= ASYNC_DELAY; 31150Sstevel@tonic-gate freemsg(bp); 31160Sstevel@tonic-gate return; /* wait for this to finish */ 31170Sstevel@tonic-gate 31180Sstevel@tonic-gate case M_IOCTL: 31190Sstevel@tonic-gate /* 31200Sstevel@tonic-gate * This ioctl was waiting for the output ahead of 31210Sstevel@tonic-gate * it to drain; obviously, it has. Do it, and 31220Sstevel@tonic-gate * then grab the next message after it. 31230Sstevel@tonic-gate */ 31240Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 31250Sstevel@tonic-gate async_ioctl(async, q, bp); 31260Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 31270Sstevel@tonic-gate continue; 31280Sstevel@tonic-gate } 31290Sstevel@tonic-gate 31306990Sgd78059 while (bp != NULL && ((cc = MBLKL(bp)) == 0)) { 31310Sstevel@tonic-gate nbp = bp->b_cont; 31320Sstevel@tonic-gate freeb(bp); 31330Sstevel@tonic-gate bp = nbp; 31340Sstevel@tonic-gate } 31350Sstevel@tonic-gate if (bp != NULL) 31360Sstevel@tonic-gate break; 31370Sstevel@tonic-gate } 31380Sstevel@tonic-gate 31390Sstevel@tonic-gate /* 31400Sstevel@tonic-gate * We have data to transmit. If output is stopped, put 31410Sstevel@tonic-gate * it back and try again later. 31420Sstevel@tonic-gate */ 31430Sstevel@tonic-gate if (async->async_flags & (ASYNC_HW_OUT_FLW | ASYNC_SW_OUT_FLW | 31440Sstevel@tonic-gate ASYNC_STOPPED | ASYNC_OUT_SUSPEND)) { 31450Sstevel@tonic-gate (void) putbq(q, bp); 31460Sstevel@tonic-gate return; 31470Sstevel@tonic-gate } 31480Sstevel@tonic-gate 31490Sstevel@tonic-gate async->async_xmitblk = bp; 31500Sstevel@tonic-gate xmit_addr = bp->b_rptr; 31510Sstevel@tonic-gate bp = bp->b_cont; 31520Sstevel@tonic-gate if (bp != NULL) 31530Sstevel@tonic-gate (void) putbq(q, bp); /* not done with this message yet */ 31540Sstevel@tonic-gate 31550Sstevel@tonic-gate /* 31560Sstevel@tonic-gate * In 5-bit mode, the high order bits are used 31570Sstevel@tonic-gate * to indicate character sizes less than five, 31580Sstevel@tonic-gate * so we need to explicitly mask before transmitting 31590Sstevel@tonic-gate */ 31600Sstevel@tonic-gate if ((async->async_ttycommon.t_cflag & CSIZE) == CS5) { 31610Sstevel@tonic-gate unsigned char *p = xmit_addr; 31620Sstevel@tonic-gate int cnt = cc; 31630Sstevel@tonic-gate 31640Sstevel@tonic-gate while (cnt--) 31650Sstevel@tonic-gate *p++ &= (unsigned char) 0x1f; 31660Sstevel@tonic-gate } 31670Sstevel@tonic-gate 31680Sstevel@tonic-gate /* 31690Sstevel@tonic-gate * Set up this block for pseudo-DMA. 31700Sstevel@tonic-gate */ 31710Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 31720Sstevel@tonic-gate /* 31730Sstevel@tonic-gate * If the transmitter is ready, shove the first 31740Sstevel@tonic-gate * character out. 31750Sstevel@tonic-gate */ 31760Sstevel@tonic-gate didsome = B_FALSE; 31770Sstevel@tonic-gate while (--fifo_len >= 0 && cc > 0) { 31781106Smrj if (!(ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & 31790Sstevel@tonic-gate XHRE)) 31800Sstevel@tonic-gate break; 31811106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 31820Sstevel@tonic-gate *xmit_addr++); 31830Sstevel@tonic-gate cc--; 31840Sstevel@tonic-gate didsome = B_TRUE; 31850Sstevel@tonic-gate } 31860Sstevel@tonic-gate async->async_optr = xmit_addr; 31870Sstevel@tonic-gate async->async_ocnt = cc; 31880Sstevel@tonic-gate if (didsome) 31890Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS; 31900Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_BUSY, 31915295Srandyf "async%d_nstart: Set ASYNC_BUSY. async_ocnt=%d\n", 31925295Srandyf instance, async->async_ocnt); 31930Sstevel@tonic-gate async->async_flags |= ASYNC_BUSY; 31940Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 31950Sstevel@tonic-gate } 31960Sstevel@tonic-gate 31970Sstevel@tonic-gate /* 31980Sstevel@tonic-gate * Resume output by poking the transmitter. 31990Sstevel@tonic-gate */ 32000Sstevel@tonic-gate static void 32010Sstevel@tonic-gate async_resume(struct asyncline *async) 32020Sstevel@tonic-gate { 32030Sstevel@tonic-gate struct asycom *asy = async->async_common; 32040Sstevel@tonic-gate #ifdef DEBUG 32050Sstevel@tonic-gate int instance; 32060Sstevel@tonic-gate #endif 32070Sstevel@tonic-gate 32080Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 32090Sstevel@tonic-gate #ifdef DEBUG 32100Sstevel@tonic-gate instance = UNIT(async->async_dev); 32110Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_resume\n", instance); 32120Sstevel@tonic-gate #endif 32130Sstevel@tonic-gate 32141106Smrj if (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE) { 32150Sstevel@tonic-gate if (async_flowcontrol_sw_input(asy, FLOW_CHECK, IN_FLOW_NULL)) 32160Sstevel@tonic-gate return; 32170Sstevel@tonic-gate if (async->async_ocnt > 0 && 32180Sstevel@tonic-gate !(async->async_flags & 32190Sstevel@tonic-gate (ASYNC_HW_OUT_FLW|ASYNC_SW_OUT_FLW|ASYNC_OUT_SUSPEND))) { 32201106Smrj ddi_put8(asy->asy_iohandle, 32210Sstevel@tonic-gate asy->asy_ioaddr + DAT, *async->async_optr++); 32220Sstevel@tonic-gate async->async_ocnt--; 32230Sstevel@tonic-gate async->async_flags |= ASYNC_PROGRESS; 32240Sstevel@tonic-gate } 32250Sstevel@tonic-gate } 32260Sstevel@tonic-gate } 32270Sstevel@tonic-gate 32280Sstevel@tonic-gate /* 32290Sstevel@tonic-gate * Hold the untimed break to last the minimum time. 32300Sstevel@tonic-gate */ 32310Sstevel@tonic-gate static void 32320Sstevel@tonic-gate async_hold_utbrk(void *arg) 32330Sstevel@tonic-gate { 32340Sstevel@tonic-gate struct asyncline *async = arg; 32350Sstevel@tonic-gate struct asycom *asy = async->async_common; 32360Sstevel@tonic-gate 32370Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 32380Sstevel@tonic-gate async->async_flags &= ~ASYNC_HOLD_UTBRK; 32390Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 32400Sstevel@tonic-gate async->async_utbrktid = 0; 32410Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 32420Sstevel@tonic-gate } 32430Sstevel@tonic-gate 32440Sstevel@tonic-gate /* 32450Sstevel@tonic-gate * Resume the untimed break. 32460Sstevel@tonic-gate */ 32470Sstevel@tonic-gate static void 32480Sstevel@tonic-gate async_resume_utbrk(struct asyncline *async) 32490Sstevel@tonic-gate { 32500Sstevel@tonic-gate uchar_t val; 32510Sstevel@tonic-gate struct asycom *asy = async->async_common; 32520Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 32530Sstevel@tonic-gate 32540Sstevel@tonic-gate /* 32550Sstevel@tonic-gate * Because the wait time is very short, 32560Sstevel@tonic-gate * so we use uninterruptably wait. 32570Sstevel@tonic-gate */ 32580Sstevel@tonic-gate while (async->async_flags & ASYNC_HOLD_UTBRK) { 32590Sstevel@tonic-gate cv_wait(&async->async_flags_cv, &asy->asy_excl); 32600Sstevel@tonic-gate } 32610Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 32620Sstevel@tonic-gate /* 32630Sstevel@tonic-gate * Timed break and untimed break can exist simultaneously, 32640Sstevel@tonic-gate * if ASYNC_BREAK is also set at here, we don't 32650Sstevel@tonic-gate * really clean the HW break. 32660Sstevel@tonic-gate */ 32670Sstevel@tonic-gate if (!(async->async_flags & ASYNC_BREAK)) { 32681106Smrj val = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LCR); 32691106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + LCR, 32700Sstevel@tonic-gate (val & ~SETBREAK)); 32710Sstevel@tonic-gate } 32720Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_SUSPEND; 32730Sstevel@tonic-gate cv_broadcast(&async->async_flags_cv); 32740Sstevel@tonic-gate if (async->async_ocnt > 0) { 32750Sstevel@tonic-gate async_resume(async); 32760Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 32770Sstevel@tonic-gate } else { 32780Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 32790Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 32800Sstevel@tonic-gate if (async->async_xmitblk != NULL) { 32810Sstevel@tonic-gate freeb(async->async_xmitblk); 32820Sstevel@tonic-gate async->async_xmitblk = NULL; 32830Sstevel@tonic-gate } 32840Sstevel@tonic-gate async_start(async); 32850Sstevel@tonic-gate } 32860Sstevel@tonic-gate } 32870Sstevel@tonic-gate 32880Sstevel@tonic-gate /* 32890Sstevel@tonic-gate * Process an "ioctl" message sent down to us. 32900Sstevel@tonic-gate * Note that we don't need to get any locks until we are ready to access 32910Sstevel@tonic-gate * the hardware. Nothing we access until then is going to be altered 32920Sstevel@tonic-gate * outside of the STREAMS framework, so we should be safe. 32930Sstevel@tonic-gate */ 32940Sstevel@tonic-gate int asydelay = 10000; 32950Sstevel@tonic-gate static void 32960Sstevel@tonic-gate async_ioctl(struct asyncline *async, queue_t *wq, mblk_t *mp) 32970Sstevel@tonic-gate { 32980Sstevel@tonic-gate struct asycom *asy = async->async_common; 32990Sstevel@tonic-gate tty_common_t *tp = &async->async_ttycommon; 33000Sstevel@tonic-gate struct iocblk *iocp; 33010Sstevel@tonic-gate unsigned datasize; 33020Sstevel@tonic-gate int error = 0; 33030Sstevel@tonic-gate uchar_t val; 33040Sstevel@tonic-gate mblk_t *datamp; 33050Sstevel@tonic-gate unsigned int index; 33060Sstevel@tonic-gate 33070Sstevel@tonic-gate #ifdef DEBUG 33080Sstevel@tonic-gate int instance = UNIT(async->async_dev); 33090Sstevel@tonic-gate 33100Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl\n", instance); 33110Sstevel@tonic-gate #endif 33120Sstevel@tonic-gate 33130Sstevel@tonic-gate if (tp->t_iocpending != NULL) { 33140Sstevel@tonic-gate /* 33150Sstevel@tonic-gate * We were holding an "ioctl" response pending the 33160Sstevel@tonic-gate * availability of an "mblk" to hold data to be passed up; 33170Sstevel@tonic-gate * another "ioctl" came through, which means that "ioctl" 33180Sstevel@tonic-gate * must have timed out or been aborted. 33190Sstevel@tonic-gate */ 33200Sstevel@tonic-gate freemsg(async->async_ttycommon.t_iocpending); 33210Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL; 33220Sstevel@tonic-gate } 33230Sstevel@tonic-gate 33240Sstevel@tonic-gate iocp = (struct iocblk *)mp->b_rptr; 33250Sstevel@tonic-gate 33260Sstevel@tonic-gate /* 33270Sstevel@tonic-gate * For TIOCMGET and the PPS ioctls, do NOT call ttycommon_ioctl() 33280Sstevel@tonic-gate * because this function frees up the message block (mp->b_cont) that 33290Sstevel@tonic-gate * contains the user location where we pass back the results. 33300Sstevel@tonic-gate * 33310Sstevel@tonic-gate * Similarly, CONSOPENPOLLEDIO needs ioc_count, which ttycommon_ioctl 33320Sstevel@tonic-gate * zaps. We know that ttycommon_ioctl doesn't know any CONS* 33330Sstevel@tonic-gate * ioctls, so keep the others safe too. 33340Sstevel@tonic-gate */ 33350Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_IOCTL, "async%d_ioctl: %s\n", 33365295Srandyf instance, 33375295Srandyf iocp->ioc_cmd == TIOCMGET ? "TIOCMGET" : 33385295Srandyf iocp->ioc_cmd == TIOCMSET ? "TIOCMSET" : 33395295Srandyf iocp->ioc_cmd == TIOCMBIS ? "TIOCMBIS" : 33405295Srandyf iocp->ioc_cmd == TIOCMBIC ? "TIOCMBIC" : 33415295Srandyf "other"); 33425295Srandyf 33430Sstevel@tonic-gate switch (iocp->ioc_cmd) { 33440Sstevel@tonic-gate case TIOCMGET: 33450Sstevel@tonic-gate case TIOCGPPS: 33460Sstevel@tonic-gate case TIOCSPPS: 33470Sstevel@tonic-gate case TIOCGPPSEV: 33480Sstevel@tonic-gate case CONSOPENPOLLEDIO: 33490Sstevel@tonic-gate case CONSCLOSEPOLLEDIO: 33500Sstevel@tonic-gate case CONSSETABORTENABLE: 33510Sstevel@tonic-gate case CONSGETABORTENABLE: 33520Sstevel@tonic-gate error = -1; /* Do Nothing */ 33530Sstevel@tonic-gate break; 33540Sstevel@tonic-gate default: 33550Sstevel@tonic-gate 33560Sstevel@tonic-gate /* 33570Sstevel@tonic-gate * The only way in which "ttycommon_ioctl" can fail is if the 33580Sstevel@tonic-gate * "ioctl" requires a response containing data to be returned 33590Sstevel@tonic-gate * to the user, and no mblk could be allocated for the data. 33600Sstevel@tonic-gate * No such "ioctl" alters our state. Thus, we always go ahead 33610Sstevel@tonic-gate * and do any state-changes the "ioctl" calls for. If we 33620Sstevel@tonic-gate * couldn't allocate the data, "ttycommon_ioctl" has stashed 33630Sstevel@tonic-gate * the "ioctl" away safely, so we just call "bufcall" to 33640Sstevel@tonic-gate * request that we be called back when we stand a better 33650Sstevel@tonic-gate * chance of allocating the data. 33660Sstevel@tonic-gate */ 33670Sstevel@tonic-gate if ((datasize = ttycommon_ioctl(tp, wq, mp, &error)) != 0) { 33680Sstevel@tonic-gate if (async->async_wbufcid) 33690Sstevel@tonic-gate unbufcall(async->async_wbufcid); 33700Sstevel@tonic-gate async->async_wbufcid = bufcall(datasize, BPRI_HI, 33710Sstevel@tonic-gate (void (*)(void *)) async_reioctl, 33720Sstevel@tonic-gate (void *)(intptr_t)async->async_common->asy_unit); 33730Sstevel@tonic-gate return; 33740Sstevel@tonic-gate } 33750Sstevel@tonic-gate } 33760Sstevel@tonic-gate 33770Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 33780Sstevel@tonic-gate 33790Sstevel@tonic-gate if (error == 0) { 33800Sstevel@tonic-gate /* 33810Sstevel@tonic-gate * "ttycommon_ioctl" did most of the work; we just use the 33820Sstevel@tonic-gate * data it set up. 33830Sstevel@tonic-gate */ 33840Sstevel@tonic-gate switch (iocp->ioc_cmd) { 33850Sstevel@tonic-gate 33860Sstevel@tonic-gate case TCSETS: 33870Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 33880Sstevel@tonic-gate if (asy_baudok(asy)) 33890Sstevel@tonic-gate asy_program(asy, ASY_NOINIT); 33900Sstevel@tonic-gate else 33910Sstevel@tonic-gate error = EINVAL; 33920Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 33930Sstevel@tonic-gate break; 33940Sstevel@tonic-gate case TCSETSF: 33950Sstevel@tonic-gate case TCSETSW: 33960Sstevel@tonic-gate case TCSETA: 33970Sstevel@tonic-gate case TCSETAW: 33980Sstevel@tonic-gate case TCSETAF: 33990Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 34000Sstevel@tonic-gate if (!asy_baudok(asy)) 34010Sstevel@tonic-gate error = EINVAL; 34020Sstevel@tonic-gate else { 34030Sstevel@tonic-gate if (asy_isbusy(asy)) 34040Sstevel@tonic-gate asy_waiteot(asy); 34050Sstevel@tonic-gate asy_program(asy, ASY_NOINIT); 34060Sstevel@tonic-gate } 34070Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 34080Sstevel@tonic-gate break; 34090Sstevel@tonic-gate } 34100Sstevel@tonic-gate } else if (error < 0) { 34110Sstevel@tonic-gate /* 34120Sstevel@tonic-gate * "ttycommon_ioctl" didn't do anything; we process it here. 34130Sstevel@tonic-gate */ 34140Sstevel@tonic-gate error = 0; 34150Sstevel@tonic-gate switch (iocp->ioc_cmd) { 34160Sstevel@tonic-gate 34170Sstevel@tonic-gate case TIOCGPPS: 34180Sstevel@tonic-gate /* 34190Sstevel@tonic-gate * Get PPS on/off. 34200Sstevel@tonic-gate */ 34210Sstevel@tonic-gate if (mp->b_cont != NULL) 34220Sstevel@tonic-gate freemsg(mp->b_cont); 34230Sstevel@tonic-gate 34240Sstevel@tonic-gate mp->b_cont = allocb(sizeof (int), BPRI_HI); 34250Sstevel@tonic-gate if (mp->b_cont == NULL) { 34260Sstevel@tonic-gate error = ENOMEM; 34270Sstevel@tonic-gate break; 34280Sstevel@tonic-gate } 34290Sstevel@tonic-gate if (asy->asy_flags & ASY_PPS) 34300Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 1; 34310Sstevel@tonic-gate else 34320Sstevel@tonic-gate *(int *)mp->b_cont->b_wptr = 0; 34330Sstevel@tonic-gate mp->b_cont->b_wptr += sizeof (int); 34340Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 34350Sstevel@tonic-gate iocp->ioc_count = sizeof (int); 34360Sstevel@tonic-gate break; 34370Sstevel@tonic-gate 34380Sstevel@tonic-gate case TIOCSPPS: 34390Sstevel@tonic-gate /* 34400Sstevel@tonic-gate * Set PPS on/off. 34410Sstevel@tonic-gate */ 34420Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 34430Sstevel@tonic-gate if (error != 0) 34440Sstevel@tonic-gate break; 34450Sstevel@tonic-gate 34460Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 34470Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr) 34480Sstevel@tonic-gate asy->asy_flags |= ASY_PPS; 34490Sstevel@tonic-gate else 34500Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS; 34510Sstevel@tonic-gate /* Reset edge sense */ 34520Sstevel@tonic-gate asy->asy_flags &= ~ASY_PPS_EDGE; 34530Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 34540Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 34550Sstevel@tonic-gate break; 34560Sstevel@tonic-gate 34570Sstevel@tonic-gate case TIOCGPPSEV: 34580Sstevel@tonic-gate { 34590Sstevel@tonic-gate /* 34600Sstevel@tonic-gate * Get PPS event data. 34610Sstevel@tonic-gate */ 34620Sstevel@tonic-gate mblk_t *bp; 34630Sstevel@tonic-gate void *buf; 34640Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 34650Sstevel@tonic-gate struct ppsclockev32 p32; 34660Sstevel@tonic-gate #endif 34670Sstevel@tonic-gate struct ppsclockev ppsclockev; 34680Sstevel@tonic-gate 34690Sstevel@tonic-gate if (mp->b_cont != NULL) { 34700Sstevel@tonic-gate freemsg(mp->b_cont); 34710Sstevel@tonic-gate mp->b_cont = NULL; 34720Sstevel@tonic-gate } 34730Sstevel@tonic-gate 34740Sstevel@tonic-gate if ((asy->asy_flags & ASY_PPS) == 0) { 34750Sstevel@tonic-gate error = ENXIO; 34760Sstevel@tonic-gate break; 34770Sstevel@tonic-gate } 34780Sstevel@tonic-gate 34790Sstevel@tonic-gate /* Protect from incomplete asy_ppsev */ 34800Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 34810Sstevel@tonic-gate ppsclockev = asy_ppsev; 34820Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 34830Sstevel@tonic-gate 34840Sstevel@tonic-gate #ifdef _SYSCALL32_IMPL 34850Sstevel@tonic-gate if ((iocp->ioc_flag & IOC_MODELS) != IOC_NATIVE) { 34860Sstevel@tonic-gate TIMEVAL_TO_TIMEVAL32(&p32.tv, &ppsclockev.tv); 34870Sstevel@tonic-gate p32.serial = ppsclockev.serial; 34880Sstevel@tonic-gate buf = &p32; 34890Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev32); 34900Sstevel@tonic-gate } else 34910Sstevel@tonic-gate #endif 34920Sstevel@tonic-gate { 34930Sstevel@tonic-gate buf = &ppsclockev; 34940Sstevel@tonic-gate iocp->ioc_count = sizeof (struct ppsclockev); 34950Sstevel@tonic-gate } 34960Sstevel@tonic-gate 34970Sstevel@tonic-gate if ((bp = allocb(iocp->ioc_count, BPRI_HI)) == NULL) { 34980Sstevel@tonic-gate error = ENOMEM; 34990Sstevel@tonic-gate break; 35000Sstevel@tonic-gate } 35010Sstevel@tonic-gate mp->b_cont = bp; 35020Sstevel@tonic-gate 35030Sstevel@tonic-gate bcopy(buf, bp->b_wptr, iocp->ioc_count); 35040Sstevel@tonic-gate bp->b_wptr += iocp->ioc_count; 35050Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 35060Sstevel@tonic-gate break; 35070Sstevel@tonic-gate } 35080Sstevel@tonic-gate 35090Sstevel@tonic-gate case TCSBRK: 35100Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 35110Sstevel@tonic-gate if (error != 0) 35120Sstevel@tonic-gate break; 35130Sstevel@tonic-gate 35140Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr == 0) { 35150Sstevel@tonic-gate 35160Sstevel@tonic-gate /* 35170Sstevel@tonic-gate * XXX Arrangements to ensure that a break 35180Sstevel@tonic-gate * isn't in progress should be sufficient. 35190Sstevel@tonic-gate * This ugly delay() is the only thing 35200Sstevel@tonic-gate * that seems to work on the NCR Worldmark. 35210Sstevel@tonic-gate * It should be replaced. Note that an 35220Sstevel@tonic-gate * asy_waiteot() also does not work. 35230Sstevel@tonic-gate */ 35240Sstevel@tonic-gate if (asydelay) 35250Sstevel@tonic-gate delay(drv_usectohz(asydelay)); 35260Sstevel@tonic-gate 35270Sstevel@tonic-gate while (async->async_flags & ASYNC_BREAK) { 35280Sstevel@tonic-gate cv_wait(&async->async_flags_cv, 35290Sstevel@tonic-gate &asy->asy_excl); 35300Sstevel@tonic-gate } 35310Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 35320Sstevel@tonic-gate /* 35330Sstevel@tonic-gate * We loop until the TSR is empty and then 35340Sstevel@tonic-gate * set the break. ASYNC_BREAK has been set 35350Sstevel@tonic-gate * to ensure that no characters are 35360Sstevel@tonic-gate * transmitted while the TSR is being 35370Sstevel@tonic-gate * flushed and SOUT is being used for the 35380Sstevel@tonic-gate * break signal. 35390Sstevel@tonic-gate * 35400Sstevel@tonic-gate * The wait period is equal to 35410Sstevel@tonic-gate * clock / (baud * 16) * 16 * 2. 35420Sstevel@tonic-gate */ 35430Sstevel@tonic-gate index = BAUDINDEX( 35445295Srandyf async->async_ttycommon.t_cflag); 35450Sstevel@tonic-gate async->async_flags |= ASYNC_BREAK; 35465295Srandyf 35471106Smrj while ((ddi_get8(asy->asy_iohandle, 35480Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XSRE) == 0) { 35490Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 35500Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 35510Sstevel@tonic-gate drv_usecwait( 35525295Srandyf 32*asyspdtab[index] & 0xfff); 35530Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 35540Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 35550Sstevel@tonic-gate } 35560Sstevel@tonic-gate /* 35570Sstevel@tonic-gate * Arrange for "async_restart" 35580Sstevel@tonic-gate * to be called in 1/4 second; 35590Sstevel@tonic-gate * it will turn the break bit off, and call 35600Sstevel@tonic-gate * "async_start" to grab the next message. 35610Sstevel@tonic-gate */ 35621106Smrj val = ddi_get8(asy->asy_iohandle, 35635295Srandyf asy->asy_ioaddr + LCR); 35641106Smrj ddi_put8(asy->asy_iohandle, 35655295Srandyf asy->asy_ioaddr + LCR, 35665295Srandyf (val | SETBREAK)); 35670Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 35680Sstevel@tonic-gate (void) timeout(async_restart, (caddr_t)async, 35690Sstevel@tonic-gate drv_usectohz(1000000)/4); 35700Sstevel@tonic-gate } else { 35710Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT, 35725295Srandyf "async%d_ioctl: wait for flush.\n", 35735295Srandyf instance); 35740Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 35750Sstevel@tonic-gate asy_waiteot(asy); 35760Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 35770Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT, 35785295Srandyf "async%d_ioctl: ldterm satisfied.\n", 35795295Srandyf instance); 35800Sstevel@tonic-gate } 35810Sstevel@tonic-gate break; 35820Sstevel@tonic-gate 35830Sstevel@tonic-gate case TIOCSBRK: 35840Sstevel@tonic-gate if (!(async->async_flags & ASYNC_OUT_SUSPEND)) { 35850Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 35860Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_SUSPEND; 35870Sstevel@tonic-gate async->async_flags |= ASYNC_HOLD_UTBRK; 35880Sstevel@tonic-gate index = BAUDINDEX( 35890Sstevel@tonic-gate async->async_ttycommon.t_cflag); 35901106Smrj while ((ddi_get8(asy->asy_iohandle, 35910Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XSRE) == 0) { 35920Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 35930Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 35940Sstevel@tonic-gate drv_usecwait( 35950Sstevel@tonic-gate 32*asyspdtab[index] & 0xfff); 35960Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 35970Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 35980Sstevel@tonic-gate } 35991106Smrj val = ddi_get8(asy->asy_iohandle, 36000Sstevel@tonic-gate asy->asy_ioaddr + LCR); 36011106Smrj ddi_put8(asy->asy_iohandle, 36020Sstevel@tonic-gate asy->asy_ioaddr + LCR, (val | SETBREAK)); 36030Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 36040Sstevel@tonic-gate /* wait for 100ms to hold BREAK */ 36050Sstevel@tonic-gate async->async_utbrktid = 36060Sstevel@tonic-gate timeout((void (*)())async_hold_utbrk, 36070Sstevel@tonic-gate (caddr_t)async, 36080Sstevel@tonic-gate drv_usectohz(asy_min_utbrk)); 36090Sstevel@tonic-gate } 36100Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0); 36110Sstevel@tonic-gate break; 36120Sstevel@tonic-gate 36130Sstevel@tonic-gate case TIOCCBRK: 36140Sstevel@tonic-gate if (async->async_flags & ASYNC_OUT_SUSPEND) 36150Sstevel@tonic-gate async_resume_utbrk(async); 36160Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0); 36170Sstevel@tonic-gate break; 36180Sstevel@tonic-gate 36190Sstevel@tonic-gate case TIOCMSET: 36200Sstevel@tonic-gate case TIOCMBIS: 36210Sstevel@tonic-gate case TIOCMBIC: 36220Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) { 36230Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 36245295Srandyf "non-transparent\n", instance); 36250Sstevel@tonic-gate 36260Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 36270Sstevel@tonic-gate if (error != 0) 36280Sstevel@tonic-gate break; 36290Sstevel@tonic-gate 36300Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 36310Sstevel@tonic-gate (void) asymctl(asy, 36325295Srandyf dmtoasy(*(int *)mp->b_cont->b_rptr), 36335295Srandyf iocp->ioc_cmd); 36340Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 36350Sstevel@tonic-gate iocp->ioc_error = 0; 36360Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 36370Sstevel@tonic-gate } else { 36380Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 36395295Srandyf "transparent\n", instance); 36400Sstevel@tonic-gate mcopyin(mp, NULL, sizeof (int), NULL); 36410Sstevel@tonic-gate } 36420Sstevel@tonic-gate break; 36430Sstevel@tonic-gate 36440Sstevel@tonic-gate case TIOCMGET: 36450Sstevel@tonic-gate datamp = allocb(sizeof (int), BPRI_MED); 36460Sstevel@tonic-gate if (datamp == NULL) { 36470Sstevel@tonic-gate error = EAGAIN; 36480Sstevel@tonic-gate break; 36490Sstevel@tonic-gate } 36500Sstevel@tonic-gate 36510Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 36520Sstevel@tonic-gate *(int *)datamp->b_rptr = asymctl(asy, 0, TIOCMGET); 36530Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 36540Sstevel@tonic-gate 36550Sstevel@tonic-gate if (iocp->ioc_count == TRANSPARENT) { 36560Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 36575295Srandyf "transparent\n", instance); 36585295Srandyf mcopyout(mp, NULL, sizeof (int), NULL, datamp); 36590Sstevel@tonic-gate } else { 36600Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_IOCTL, "async%d_ioctl: " 36615295Srandyf "non-transparent\n", instance); 36620Sstevel@tonic-gate mioc2ack(mp, datamp, sizeof (int), 0); 36630Sstevel@tonic-gate } 36640Sstevel@tonic-gate break; 36650Sstevel@tonic-gate 36660Sstevel@tonic-gate case CONSOPENPOLLEDIO: 36670Sstevel@tonic-gate error = miocpullup(mp, sizeof (struct cons_polledio *)); 36680Sstevel@tonic-gate if (error != 0) 36690Sstevel@tonic-gate break; 36700Sstevel@tonic-gate 36710Sstevel@tonic-gate *(struct cons_polledio **)mp->b_cont->b_rptr = 36725295Srandyf &asy->polledio; 36730Sstevel@tonic-gate 36740Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 36750Sstevel@tonic-gate break; 36760Sstevel@tonic-gate 36770Sstevel@tonic-gate case CONSCLOSEPOLLEDIO: 36780Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 36790Sstevel@tonic-gate iocp->ioc_error = 0; 36800Sstevel@tonic-gate iocp->ioc_rval = 0; 36810Sstevel@tonic-gate break; 36820Sstevel@tonic-gate 36830Sstevel@tonic-gate case CONSSETABORTENABLE: 36840Sstevel@tonic-gate error = secpolicy_console(iocp->ioc_cr); 36850Sstevel@tonic-gate if (error != 0) 36860Sstevel@tonic-gate break; 36870Sstevel@tonic-gate 36880Sstevel@tonic-gate if (iocp->ioc_count != TRANSPARENT) { 36890Sstevel@tonic-gate error = EINVAL; 36900Sstevel@tonic-gate break; 36910Sstevel@tonic-gate } 36920Sstevel@tonic-gate 36930Sstevel@tonic-gate if (*(intptr_t *)mp->b_cont->b_rptr) 36940Sstevel@tonic-gate asy->asy_flags |= ASY_CONSOLE; 36950Sstevel@tonic-gate else 36960Sstevel@tonic-gate asy->asy_flags &= ~ASY_CONSOLE; 36970Sstevel@tonic-gate 36980Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 36990Sstevel@tonic-gate iocp->ioc_error = 0; 37000Sstevel@tonic-gate iocp->ioc_rval = 0; 37010Sstevel@tonic-gate break; 37020Sstevel@tonic-gate 37030Sstevel@tonic-gate case CONSGETABORTENABLE: 37040Sstevel@tonic-gate /*CONSTANTCONDITION*/ 37050Sstevel@tonic-gate ASSERT(sizeof (boolean_t) <= sizeof (boolean_t *)); 37060Sstevel@tonic-gate /* 37070Sstevel@tonic-gate * Store the return value right in the payload 37080Sstevel@tonic-gate * we were passed. Crude. 37090Sstevel@tonic-gate */ 37100Sstevel@tonic-gate mcopyout(mp, NULL, sizeof (boolean_t), NULL, NULL); 37110Sstevel@tonic-gate *(boolean_t *)mp->b_cont->b_rptr = 37125295Srandyf (asy->asy_flags & ASY_CONSOLE) != 0; 37130Sstevel@tonic-gate break; 37140Sstevel@tonic-gate 37150Sstevel@tonic-gate default: 37160Sstevel@tonic-gate /* 37170Sstevel@tonic-gate * If we don't understand it, it's an error. NAK it. 37180Sstevel@tonic-gate */ 37190Sstevel@tonic-gate error = EINVAL; 37200Sstevel@tonic-gate break; 37210Sstevel@tonic-gate } 37220Sstevel@tonic-gate } 37230Sstevel@tonic-gate if (error != 0) { 37240Sstevel@tonic-gate iocp->ioc_error = error; 37250Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK; 37260Sstevel@tonic-gate } 37270Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 37280Sstevel@tonic-gate qreply(wq, mp); 37290Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_PROCS, "async%d_ioctl: done\n", instance); 37300Sstevel@tonic-gate } 37310Sstevel@tonic-gate 37320Sstevel@tonic-gate static int 37330Sstevel@tonic-gate asyrsrv(queue_t *q) 37340Sstevel@tonic-gate { 37350Sstevel@tonic-gate mblk_t *bp; 37360Sstevel@tonic-gate struct asyncline *async; 37370Sstevel@tonic-gate 37380Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr; 37390Sstevel@tonic-gate 37400Sstevel@tonic-gate while (canputnext(q) && (bp = getq(q))) 37410Sstevel@tonic-gate putnext(q, bp); 37420Sstevel@tonic-gate ASYSETSOFT(async->async_common); 37430Sstevel@tonic-gate async->async_polltid = 0; 37440Sstevel@tonic-gate return (0); 37450Sstevel@tonic-gate } 37460Sstevel@tonic-gate 37470Sstevel@tonic-gate /* 37485295Srandyf * The ASYWPUTDO_NOT_SUSP macro indicates to asywputdo() whether it should 37495295Srandyf * handle messages as though the driver is operating normally or is 37505295Srandyf * suspended. In the suspended case, some or all of the processing may have 37515295Srandyf * to be delayed until the driver is resumed. 37525295Srandyf */ 37535295Srandyf #define ASYWPUTDO_NOT_SUSP(async, wput) \ 37545295Srandyf !((wput) && ((async)->async_flags & ASYNC_DDI_SUSPENDED)) 37555295Srandyf 37565295Srandyf /* 37575295Srandyf * Processing for write queue put procedure. 37580Sstevel@tonic-gate * Respond to M_STOP, M_START, M_IOCTL, and M_FLUSH messages here; 37590Sstevel@tonic-gate * set the flow control character for M_STOPI and M_STARTI messages; 37600Sstevel@tonic-gate * queue up M_BREAK, M_DELAY, and M_DATA messages for processing 37610Sstevel@tonic-gate * by the start routine, and then call the start routine; discard 37620Sstevel@tonic-gate * everything else. Note that this driver does not incorporate any 37630Sstevel@tonic-gate * mechanism to negotiate to handle the canonicalization process. 37640Sstevel@tonic-gate * It expects that these functions are handled in upper module(s), 37650Sstevel@tonic-gate * as we do in ldterm. 37660Sstevel@tonic-gate */ 37670Sstevel@tonic-gate static int 37685295Srandyf asywputdo(queue_t *q, mblk_t *mp, boolean_t wput) 37690Sstevel@tonic-gate { 37700Sstevel@tonic-gate struct asyncline *async; 37710Sstevel@tonic-gate struct asycom *asy; 37720Sstevel@tonic-gate #ifdef DEBUG 37730Sstevel@tonic-gate int instance; 37740Sstevel@tonic-gate #endif 37750Sstevel@tonic-gate int error; 37760Sstevel@tonic-gate 37770Sstevel@tonic-gate async = (struct asyncline *)q->q_ptr; 37785295Srandyf 37790Sstevel@tonic-gate #ifdef DEBUG 37800Sstevel@tonic-gate instance = UNIT(async->async_dev); 37810Sstevel@tonic-gate #endif 37820Sstevel@tonic-gate asy = async->async_common; 37830Sstevel@tonic-gate 37840Sstevel@tonic-gate switch (mp->b_datap->db_type) { 37850Sstevel@tonic-gate 37860Sstevel@tonic-gate case M_STOP: 37870Sstevel@tonic-gate /* 37880Sstevel@tonic-gate * Since we don't do real DMA, we can just let the 37890Sstevel@tonic-gate * chip coast to a stop after applying the brakes. 37900Sstevel@tonic-gate */ 37910Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 37920Sstevel@tonic-gate async->async_flags |= ASYNC_STOPPED; 37930Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 37940Sstevel@tonic-gate freemsg(mp); 37950Sstevel@tonic-gate break; 37960Sstevel@tonic-gate 37970Sstevel@tonic-gate case M_START: 37980Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 37990Sstevel@tonic-gate if (async->async_flags & ASYNC_STOPPED) { 38000Sstevel@tonic-gate async->async_flags &= ~ASYNC_STOPPED; 38015295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 38025295Srandyf /* 38035295Srandyf * If an output operation is in progress, 38045295Srandyf * resume it. Otherwise, prod the start 38055295Srandyf * routine. 38065295Srandyf */ 38075295Srandyf if (async->async_ocnt > 0) { 38085295Srandyf mutex_enter(&asy->asy_excl_hi); 38095295Srandyf async_resume(async); 38105295Srandyf mutex_exit(&asy->asy_excl_hi); 38115295Srandyf } else { 38125295Srandyf async_start(async); 38135295Srandyf } 38140Sstevel@tonic-gate } 38150Sstevel@tonic-gate } 38160Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 38170Sstevel@tonic-gate freemsg(mp); 38180Sstevel@tonic-gate break; 38190Sstevel@tonic-gate 38200Sstevel@tonic-gate case M_IOCTL: 38210Sstevel@tonic-gate switch (((struct iocblk *)mp->b_rptr)->ioc_cmd) { 38220Sstevel@tonic-gate 38230Sstevel@tonic-gate case TCSBRK: 38240Sstevel@tonic-gate error = miocpullup(mp, sizeof (int)); 38250Sstevel@tonic-gate if (error != 0) { 38260Sstevel@tonic-gate miocnak(q, mp, 0, error); 38270Sstevel@tonic-gate return (0); 38280Sstevel@tonic-gate } 38290Sstevel@tonic-gate 38300Sstevel@tonic-gate if (*(int *)mp->b_cont->b_rptr != 0) { 38310Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_OUT, 38325295Srandyf "async%d_ioctl: flush request.\n", 38335295Srandyf instance); 38340Sstevel@tonic-gate (void) putq(q, mp); 38355295Srandyf 38360Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 38375295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 38385295Srandyf /* 38395295Srandyf * If an TIOCSBRK is in progress, 38405295Srandyf * clean it as TIOCCBRK does, 38415295Srandyf * then kick off output. 38425295Srandyf * If TIOCSBRK is not in progress, 38435295Srandyf * just kick off output. 38445295Srandyf */ 38455295Srandyf async_resume_utbrk(async); 38465295Srandyf } 38470Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 38480Sstevel@tonic-gate break; 38490Sstevel@tonic-gate } 38500Sstevel@tonic-gate /*FALLTHROUGH*/ 38510Sstevel@tonic-gate case TCSETSW: 38520Sstevel@tonic-gate case TCSETSF: 38530Sstevel@tonic-gate case TCSETAW: 38540Sstevel@tonic-gate case TCSETAF: 38550Sstevel@tonic-gate /* 38560Sstevel@tonic-gate * The changes do not take effect until all 38570Sstevel@tonic-gate * output queued before them is drained. 38580Sstevel@tonic-gate * Put this message on the queue, so that 38590Sstevel@tonic-gate * "async_start" will see it when it's done 38600Sstevel@tonic-gate * with the output before it. Poke the 38610Sstevel@tonic-gate * start routine, just in case. 38620Sstevel@tonic-gate */ 38630Sstevel@tonic-gate (void) putq(q, mp); 38645295Srandyf 38650Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 38665295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 38675295Srandyf /* 38685295Srandyf * If an TIOCSBRK is in progress, 38695295Srandyf * clean it as TIOCCBRK does. 38705295Srandyf * then kick off output. 38715295Srandyf * If TIOCSBRK is not in progress, 38725295Srandyf * just kick off output. 38735295Srandyf */ 38745295Srandyf async_resume_utbrk(async); 38755295Srandyf } 38760Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 38770Sstevel@tonic-gate break; 38780Sstevel@tonic-gate 38790Sstevel@tonic-gate default: 38800Sstevel@tonic-gate /* 38810Sstevel@tonic-gate * Do it now. 38820Sstevel@tonic-gate */ 38835295Srandyf mutex_enter(&asy->asy_excl); 38845295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 38855295Srandyf mutex_exit(&asy->asy_excl); 38865295Srandyf async_ioctl(async, q, mp); 38875295Srandyf break; 38885295Srandyf } 38895295Srandyf async_put_suspq(asy, mp); 38905295Srandyf mutex_exit(&asy->asy_excl); 38910Sstevel@tonic-gate break; 38920Sstevel@tonic-gate } 38930Sstevel@tonic-gate break; 38940Sstevel@tonic-gate 38950Sstevel@tonic-gate case M_FLUSH: 38960Sstevel@tonic-gate if (*mp->b_rptr & FLUSHW) { 38970Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 38980Sstevel@tonic-gate 38990Sstevel@tonic-gate /* 39000Sstevel@tonic-gate * Abort any output in progress. 39010Sstevel@tonic-gate */ 39020Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 39030Sstevel@tonic-gate if (async->async_flags & ASYNC_BUSY) { 39045295Srandyf DEBUGCONT1(ASY_DEBUG_BUSY, "asy%dwput: " 39050Sstevel@tonic-gate "Clearing async_ocnt, " 39060Sstevel@tonic-gate "leaving ASYNC_BUSY set\n", 39070Sstevel@tonic-gate instance); 39080Sstevel@tonic-gate async->async_ocnt = 0; 39090Sstevel@tonic-gate async->async_flags &= ~ASYNC_BUSY; 39100Sstevel@tonic-gate } /* if */ 39115295Srandyf 39125295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39135295Srandyf /* Flush FIFO buffers */ 39145295Srandyf if (asy->asy_use_fifo == FIFO_ON) { 39155295Srandyf asy_reset_fifo(asy, FIFOTXFLSH); 39165295Srandyf } 39175295Srandyf } 39180Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 39190Sstevel@tonic-gate 39200Sstevel@tonic-gate /* Flush FIFO buffers */ 39210Sstevel@tonic-gate if (asy->asy_use_fifo == FIFO_ON) { 39220Sstevel@tonic-gate asy_reset_fifo(asy, FIFOTXFLSH); 39230Sstevel@tonic-gate } 39240Sstevel@tonic-gate 39250Sstevel@tonic-gate /* 39260Sstevel@tonic-gate * Flush our write queue. 39270Sstevel@tonic-gate */ 39280Sstevel@tonic-gate flushq(q, FLUSHDATA); /* XXX doesn't flush M_DELAY */ 39290Sstevel@tonic-gate if (async->async_xmitblk != NULL) { 39300Sstevel@tonic-gate freeb(async->async_xmitblk); 39310Sstevel@tonic-gate async->async_xmitblk = NULL; 39320Sstevel@tonic-gate } 39330Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 39340Sstevel@tonic-gate *mp->b_rptr &= ~FLUSHW; /* it has been flushed */ 39350Sstevel@tonic-gate } 39360Sstevel@tonic-gate if (*mp->b_rptr & FLUSHR) { 39375295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39385295Srandyf /* Flush FIFO buffers */ 39395295Srandyf if (asy->asy_use_fifo == FIFO_ON) { 39405295Srandyf asy_reset_fifo(asy, FIFORXFLSH); 39415295Srandyf } 39420Sstevel@tonic-gate } 39430Sstevel@tonic-gate flushq(RD(q), FLUSHDATA); 39440Sstevel@tonic-gate qreply(q, mp); /* give the read queues a crack at it */ 39450Sstevel@tonic-gate } else { 39460Sstevel@tonic-gate freemsg(mp); 39470Sstevel@tonic-gate } 39480Sstevel@tonic-gate 39490Sstevel@tonic-gate /* 39500Sstevel@tonic-gate * We must make sure we process messages that survive the 39510Sstevel@tonic-gate * write-side flush. 39520Sstevel@tonic-gate */ 39535295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39545295Srandyf mutex_enter(&asy->asy_excl); 39555295Srandyf async_start(async); 39565295Srandyf mutex_exit(&asy->asy_excl); 39575295Srandyf } 39580Sstevel@tonic-gate break; 39590Sstevel@tonic-gate 39600Sstevel@tonic-gate case M_BREAK: 39610Sstevel@tonic-gate case M_DELAY: 39620Sstevel@tonic-gate case M_DATA: 39630Sstevel@tonic-gate /* 39640Sstevel@tonic-gate * Queue the message up to be transmitted, 39650Sstevel@tonic-gate * and poke the start routine. 39660Sstevel@tonic-gate */ 39670Sstevel@tonic-gate (void) putq(q, mp); 39685295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39695295Srandyf mutex_enter(&asy->asy_excl); 39705295Srandyf async_start(async); 39715295Srandyf mutex_exit(&asy->asy_excl); 39725295Srandyf } 39730Sstevel@tonic-gate break; 39740Sstevel@tonic-gate 39750Sstevel@tonic-gate case M_STOPI: 39760Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 39775295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39785295Srandyf mutex_enter(&asy->asy_excl_hi); 39795295Srandyf if (!(async->async_inflow_source & IN_FLOW_USER)) { 39805295Srandyf async_flowcontrol_hw_input(asy, FLOW_STOP, 39815295Srandyf IN_FLOW_USER); 39825295Srandyf (void) async_flowcontrol_sw_input(asy, 39835295Srandyf FLOW_STOP, IN_FLOW_USER); 39845295Srandyf } 39855295Srandyf mutex_exit(&asy->asy_excl_hi); 39865295Srandyf mutex_exit(&asy->asy_excl); 39875295Srandyf freemsg(mp); 39885295Srandyf break; 39890Sstevel@tonic-gate } 39905295Srandyf async_put_suspq(asy, mp); 39910Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 39920Sstevel@tonic-gate break; 39930Sstevel@tonic-gate 39940Sstevel@tonic-gate case M_STARTI: 39950Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 39965295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 39975295Srandyf mutex_enter(&asy->asy_excl_hi); 39985295Srandyf if (async->async_inflow_source & IN_FLOW_USER) { 39995295Srandyf async_flowcontrol_hw_input(asy, FLOW_START, 40005295Srandyf IN_FLOW_USER); 40015295Srandyf (void) async_flowcontrol_sw_input(asy, 40025295Srandyf FLOW_START, IN_FLOW_USER); 40035295Srandyf } 40045295Srandyf mutex_exit(&asy->asy_excl_hi); 40055295Srandyf mutex_exit(&asy->asy_excl); 40065295Srandyf freemsg(mp); 40075295Srandyf break; 40080Sstevel@tonic-gate } 40095295Srandyf async_put_suspq(asy, mp); 40100Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 40110Sstevel@tonic-gate break; 40120Sstevel@tonic-gate 40130Sstevel@tonic-gate case M_CTL: 40140Sstevel@tonic-gate if (MBLKL(mp) >= sizeof (struct iocblk) && 40150Sstevel@tonic-gate ((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_POSIXQUERY) { 40165295Srandyf mutex_enter(&asy->asy_excl); 40175295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 40185295Srandyf ((struct iocblk *)mp->b_rptr)->ioc_cmd = 40195295Srandyf MC_HAS_POSIX; 40205295Srandyf mutex_exit(&asy->asy_excl); 40215295Srandyf qreply(q, mp); 40225295Srandyf break; 40235295Srandyf } else { 40245295Srandyf async_put_suspq(asy, mp); 40255295Srandyf } 40260Sstevel@tonic-gate } else { 40270Sstevel@tonic-gate /* 40280Sstevel@tonic-gate * These MC_SERVICE type messages are used by upper 40290Sstevel@tonic-gate * modules to tell this driver to send input up 40300Sstevel@tonic-gate * immediately, or that it can wait for normal 40310Sstevel@tonic-gate * processing that may or may not be done. Sun 40320Sstevel@tonic-gate * requires these for the mouse module. 40330Sstevel@tonic-gate * (XXX - for x86?) 40340Sstevel@tonic-gate */ 40350Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 40360Sstevel@tonic-gate switch (*mp->b_rptr) { 40370Sstevel@tonic-gate 40380Sstevel@tonic-gate case MC_SERVICEIMM: 40390Sstevel@tonic-gate async->async_flags |= ASYNC_SERVICEIMM; 40400Sstevel@tonic-gate break; 40410Sstevel@tonic-gate 40420Sstevel@tonic-gate case MC_SERVICEDEF: 40430Sstevel@tonic-gate async->async_flags &= ~ASYNC_SERVICEIMM; 40440Sstevel@tonic-gate break; 40450Sstevel@tonic-gate } 40460Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 40470Sstevel@tonic-gate freemsg(mp); 40480Sstevel@tonic-gate } 40490Sstevel@tonic-gate break; 40500Sstevel@tonic-gate 40510Sstevel@tonic-gate case M_IOCDATA: 40525295Srandyf mutex_enter(&asy->asy_excl); 40535295Srandyf if (ASYWPUTDO_NOT_SUSP(async, wput)) { 40545295Srandyf mutex_exit(&asy->asy_excl); 40555295Srandyf async_iocdata(q, mp); 40565295Srandyf break; 40575295Srandyf } 40585295Srandyf async_put_suspq(asy, mp); 40595295Srandyf mutex_exit(&asy->asy_excl); 40600Sstevel@tonic-gate break; 40610Sstevel@tonic-gate 40620Sstevel@tonic-gate default: 40630Sstevel@tonic-gate freemsg(mp); 40640Sstevel@tonic-gate break; 40650Sstevel@tonic-gate } 40660Sstevel@tonic-gate return (0); 40670Sstevel@tonic-gate } 40680Sstevel@tonic-gate 40695295Srandyf static int 40705295Srandyf asywput(queue_t *q, mblk_t *mp) 40715295Srandyf { 40725295Srandyf return (asywputdo(q, mp, B_TRUE)); 40735295Srandyf } 40745295Srandyf 40750Sstevel@tonic-gate /* 40760Sstevel@tonic-gate * Retry an "ioctl", now that "bufcall" claims we may be able to allocate 40770Sstevel@tonic-gate * the buffer we need. 40780Sstevel@tonic-gate */ 40790Sstevel@tonic-gate static void 40800Sstevel@tonic-gate async_reioctl(void *unit) 40810Sstevel@tonic-gate { 40820Sstevel@tonic-gate int instance = (uintptr_t)unit; 40830Sstevel@tonic-gate struct asyncline *async; 40840Sstevel@tonic-gate struct asycom *asy; 40850Sstevel@tonic-gate queue_t *q; 40860Sstevel@tonic-gate mblk_t *mp; 40870Sstevel@tonic-gate 40880Sstevel@tonic-gate asy = ddi_get_soft_state(asy_soft_state, instance); 40890Sstevel@tonic-gate ASSERT(asy != NULL); 40900Sstevel@tonic-gate async = asy->asy_priv; 40910Sstevel@tonic-gate 40920Sstevel@tonic-gate /* 40930Sstevel@tonic-gate * The bufcall is no longer pending. 40940Sstevel@tonic-gate */ 40950Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 40960Sstevel@tonic-gate async->async_wbufcid = 0; 40970Sstevel@tonic-gate if ((q = async->async_ttycommon.t_writeq) == NULL) { 40980Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 40990Sstevel@tonic-gate return; 41000Sstevel@tonic-gate } 41010Sstevel@tonic-gate if ((mp = async->async_ttycommon.t_iocpending) != NULL) { 41020Sstevel@tonic-gate /* not pending any more */ 41030Sstevel@tonic-gate async->async_ttycommon.t_iocpending = NULL; 41040Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 41050Sstevel@tonic-gate async_ioctl(async, q, mp); 41060Sstevel@tonic-gate } else 41070Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 41080Sstevel@tonic-gate } 41090Sstevel@tonic-gate 41100Sstevel@tonic-gate static void 41110Sstevel@tonic-gate async_iocdata(queue_t *q, mblk_t *mp) 41120Sstevel@tonic-gate { 41130Sstevel@tonic-gate struct asyncline *async = (struct asyncline *)q->q_ptr; 41140Sstevel@tonic-gate struct asycom *asy; 41150Sstevel@tonic-gate struct iocblk *ip; 41160Sstevel@tonic-gate struct copyresp *csp; 41170Sstevel@tonic-gate #ifdef DEBUG 41180Sstevel@tonic-gate int instance = UNIT(async->async_dev); 41190Sstevel@tonic-gate #endif 41200Sstevel@tonic-gate 41210Sstevel@tonic-gate asy = async->async_common; 41220Sstevel@tonic-gate ip = (struct iocblk *)mp->b_rptr; 41230Sstevel@tonic-gate csp = (struct copyresp *)mp->b_rptr; 41240Sstevel@tonic-gate 41250Sstevel@tonic-gate if (csp->cp_rval != 0) { 41260Sstevel@tonic-gate if (csp->cp_private) 41270Sstevel@tonic-gate freemsg(csp->cp_private); 41280Sstevel@tonic-gate freemsg(mp); 41290Sstevel@tonic-gate return; 41300Sstevel@tonic-gate } 41310Sstevel@tonic-gate 41320Sstevel@tonic-gate mutex_enter(&asy->asy_excl); 41330Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "async%d_iocdata: case %s\n", 41345295Srandyf instance, 41355295Srandyf csp->cp_cmd == TIOCMGET ? "TIOCMGET" : 41365295Srandyf csp->cp_cmd == TIOCMSET ? "TIOCMSET" : 41375295Srandyf csp->cp_cmd == TIOCMBIS ? "TIOCMBIS" : 41385295Srandyf "TIOCMBIC"); 41390Sstevel@tonic-gate switch (csp->cp_cmd) { 41400Sstevel@tonic-gate 41410Sstevel@tonic-gate case TIOCMGET: 41420Sstevel@tonic-gate if (mp->b_cont) { 41430Sstevel@tonic-gate freemsg(mp->b_cont); 41440Sstevel@tonic-gate mp->b_cont = NULL; 41450Sstevel@tonic-gate } 41460Sstevel@tonic-gate mp->b_datap->db_type = M_IOCACK; 41470Sstevel@tonic-gate ip->ioc_error = 0; 41480Sstevel@tonic-gate ip->ioc_count = 0; 41490Sstevel@tonic-gate ip->ioc_rval = 0; 41500Sstevel@tonic-gate mp->b_wptr = mp->b_rptr + sizeof (struct iocblk); 41510Sstevel@tonic-gate break; 41520Sstevel@tonic-gate 41530Sstevel@tonic-gate case TIOCMSET: 41540Sstevel@tonic-gate case TIOCMBIS: 41550Sstevel@tonic-gate case TIOCMBIC: 41560Sstevel@tonic-gate mutex_enter(&asy->asy_excl_hi); 41575295Srandyf (void) asymctl(asy, dmtoasy(*(int *)mp->b_cont->b_rptr), 41585295Srandyf csp->cp_cmd); 41590Sstevel@tonic-gate mutex_exit(&asy->asy_excl_hi); 41600Sstevel@tonic-gate mioc2ack(mp, NULL, 0, 0); 41610Sstevel@tonic-gate break; 41620Sstevel@tonic-gate 41630Sstevel@tonic-gate default: 41640Sstevel@tonic-gate mp->b_datap->db_type = M_IOCNAK; 41650Sstevel@tonic-gate ip->ioc_error = EINVAL; 41660Sstevel@tonic-gate break; 41670Sstevel@tonic-gate } 41680Sstevel@tonic-gate qreply(q, mp); 41690Sstevel@tonic-gate mutex_exit(&asy->asy_excl); 41700Sstevel@tonic-gate } 41710Sstevel@tonic-gate 41720Sstevel@tonic-gate /* 41730Sstevel@tonic-gate * debugger/console support routines. 41740Sstevel@tonic-gate */ 41750Sstevel@tonic-gate 41760Sstevel@tonic-gate /* 41770Sstevel@tonic-gate * put a character out 41780Sstevel@tonic-gate * Do not use interrupts. If char is LF, put out CR, LF. 41790Sstevel@tonic-gate */ 41800Sstevel@tonic-gate static void 41811762Slt200341 asyputchar(cons_polledio_arg_t arg, uchar_t c) 41820Sstevel@tonic-gate { 41830Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg; 41840Sstevel@tonic-gate 41850Sstevel@tonic-gate if (c == '\n') 41860Sstevel@tonic-gate asyputchar(arg, '\r'); 41870Sstevel@tonic-gate 41881106Smrj while ((ddi_get8(asy->asy_iohandle, 41890Sstevel@tonic-gate asy->asy_ioaddr + LSR) & XHRE) == 0) { 41900Sstevel@tonic-gate /* wait for xmit to finish */ 41910Sstevel@tonic-gate drv_usecwait(10); 41920Sstevel@tonic-gate } 41930Sstevel@tonic-gate 41940Sstevel@tonic-gate /* put the character out */ 41951106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, c); 41960Sstevel@tonic-gate } 41970Sstevel@tonic-gate 41980Sstevel@tonic-gate /* 41990Sstevel@tonic-gate * See if there's a character available. If no character is 42000Sstevel@tonic-gate * available, return 0. Run in polled mode, no interrupts. 42010Sstevel@tonic-gate */ 42020Sstevel@tonic-gate static boolean_t 42031762Slt200341 asyischar(cons_polledio_arg_t arg) 42040Sstevel@tonic-gate { 42050Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg; 42060Sstevel@tonic-gate 42075295Srandyf return ((ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & RCA) 42085295Srandyf != 0); 42090Sstevel@tonic-gate } 42100Sstevel@tonic-gate 42110Sstevel@tonic-gate /* 42120Sstevel@tonic-gate * Get a character. Run in polled mode, no interrupts. 42130Sstevel@tonic-gate */ 42140Sstevel@tonic-gate static int 42151762Slt200341 asygetchar(cons_polledio_arg_t arg) 42160Sstevel@tonic-gate { 42170Sstevel@tonic-gate struct asycom *asy = (struct asycom *)arg; 42180Sstevel@tonic-gate 42190Sstevel@tonic-gate while (!asyischar(arg)) 42200Sstevel@tonic-gate drv_usecwait(10); 42215295Srandyf return (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + DAT)); 42220Sstevel@tonic-gate } 42230Sstevel@tonic-gate 42240Sstevel@tonic-gate /* 42250Sstevel@tonic-gate * Set or get the modem control status. 42260Sstevel@tonic-gate */ 42270Sstevel@tonic-gate static int 42280Sstevel@tonic-gate asymctl(struct asycom *asy, int bits, int how) 42290Sstevel@tonic-gate { 42300Sstevel@tonic-gate int mcr_r, msr_r; 42310Sstevel@tonic-gate int instance = asy->asy_unit; 42320Sstevel@tonic-gate 42330Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 42340Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl)); 42350Sstevel@tonic-gate 42360Sstevel@tonic-gate /* Read Modem Control Registers */ 42371106Smrj mcr_r = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 42380Sstevel@tonic-gate 42390Sstevel@tonic-gate switch (how) { 42400Sstevel@tonic-gate 42410Sstevel@tonic-gate case TIOCMSET: 42420Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, 42435295Srandyf "asy%dmctl: TIOCMSET, bits = %x\n", instance, bits); 42440Sstevel@tonic-gate mcr_r = bits; /* Set bits */ 42450Sstevel@tonic-gate break; 42460Sstevel@tonic-gate 42470Sstevel@tonic-gate case TIOCMBIS: 42480Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIS, bits = %x\n", 42495295Srandyf instance, bits); 42500Sstevel@tonic-gate mcr_r |= bits; /* Mask in bits */ 42510Sstevel@tonic-gate break; 42520Sstevel@tonic-gate 42530Sstevel@tonic-gate case TIOCMBIC: 42540Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dmctl: TIOCMBIC, bits = %x\n", 42555295Srandyf instance, bits); 42560Sstevel@tonic-gate mcr_r &= ~bits; /* Mask out bits */ 42570Sstevel@tonic-gate break; 42580Sstevel@tonic-gate 42590Sstevel@tonic-gate case TIOCMGET: 42600Sstevel@tonic-gate /* Read Modem Status Registers */ 42610Sstevel@tonic-gate /* 42620Sstevel@tonic-gate * If modem interrupts are enabled, we return the 42630Sstevel@tonic-gate * saved value of msr. We read MSR only in async_msint() 42640Sstevel@tonic-gate */ 42651106Smrj if (ddi_get8(asy->asy_iohandle, 42660Sstevel@tonic-gate asy->asy_ioaddr + ICR) & MIEN) { 42670Sstevel@tonic-gate msr_r = asy->asy_msr; 42680Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, 42695295Srandyf "asy%dmctl: TIOCMGET, read msr_r = %x\n", 42705295Srandyf instance, msr_r); 42710Sstevel@tonic-gate } else { 42721106Smrj msr_r = ddi_get8(asy->asy_iohandle, 42735295Srandyf asy->asy_ioaddr + MSR); 42740Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, 42755295Srandyf "asy%dmctl: TIOCMGET, read MSR = %x\n", 42765295Srandyf instance, msr_r); 42770Sstevel@tonic-gate } 42780Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_MODEM, "asy%dtodm: modem_lines = %x\n", 42795295Srandyf instance, asytodm(mcr_r, msr_r)); 42800Sstevel@tonic-gate return (asytodm(mcr_r, msr_r)); 42810Sstevel@tonic-gate } 42820Sstevel@tonic-gate 42831106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + MCR, mcr_r); 42840Sstevel@tonic-gate 42850Sstevel@tonic-gate return (mcr_r); 42860Sstevel@tonic-gate } 42870Sstevel@tonic-gate 42880Sstevel@tonic-gate static int 42890Sstevel@tonic-gate asytodm(int mcr_r, int msr_r) 42900Sstevel@tonic-gate { 42910Sstevel@tonic-gate int b = 0; 42920Sstevel@tonic-gate 42930Sstevel@tonic-gate /* MCR registers */ 42940Sstevel@tonic-gate if (mcr_r & RTS) 42950Sstevel@tonic-gate b |= TIOCM_RTS; 42960Sstevel@tonic-gate 42970Sstevel@tonic-gate if (mcr_r & DTR) 42980Sstevel@tonic-gate b |= TIOCM_DTR; 42990Sstevel@tonic-gate 43000Sstevel@tonic-gate /* MSR registers */ 43010Sstevel@tonic-gate if (msr_r & DCD) 43020Sstevel@tonic-gate b |= TIOCM_CAR; 43030Sstevel@tonic-gate 43040Sstevel@tonic-gate if (msr_r & CTS) 43050Sstevel@tonic-gate b |= TIOCM_CTS; 43060Sstevel@tonic-gate 43070Sstevel@tonic-gate if (msr_r & DSR) 43080Sstevel@tonic-gate b |= TIOCM_DSR; 43090Sstevel@tonic-gate 43100Sstevel@tonic-gate if (msr_r & RI) 43110Sstevel@tonic-gate b |= TIOCM_RNG; 43120Sstevel@tonic-gate return (b); 43130Sstevel@tonic-gate } 43140Sstevel@tonic-gate 43150Sstevel@tonic-gate static int 43160Sstevel@tonic-gate dmtoasy(int bits) 43170Sstevel@tonic-gate { 43180Sstevel@tonic-gate int b = 0; 43190Sstevel@tonic-gate 43200Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_MODEM, "dmtoasy: bits = %x\n", bits); 43210Sstevel@tonic-gate #ifdef CAN_NOT_SET /* only DTR and RTS can be set */ 43220Sstevel@tonic-gate if (bits & TIOCM_CAR) 43230Sstevel@tonic-gate b |= DCD; 43240Sstevel@tonic-gate if (bits & TIOCM_CTS) 43250Sstevel@tonic-gate b |= CTS; 43260Sstevel@tonic-gate if (bits & TIOCM_DSR) 43270Sstevel@tonic-gate b |= DSR; 43280Sstevel@tonic-gate if (bits & TIOCM_RNG) 43290Sstevel@tonic-gate b |= RI; 43300Sstevel@tonic-gate #endif 43310Sstevel@tonic-gate 43320Sstevel@tonic-gate if (bits & TIOCM_RTS) { 43330Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & RTS\n"); 43340Sstevel@tonic-gate b |= RTS; 43350Sstevel@tonic-gate } 43360Sstevel@tonic-gate if (bits & TIOCM_DTR) { 43370Sstevel@tonic-gate DEBUGCONT0(ASY_DEBUG_MODEM, "dmtoasy: set b & DTR\n"); 43380Sstevel@tonic-gate b |= DTR; 43390Sstevel@tonic-gate } 43400Sstevel@tonic-gate 43410Sstevel@tonic-gate return (b); 43420Sstevel@tonic-gate } 43430Sstevel@tonic-gate 43440Sstevel@tonic-gate static void 43450Sstevel@tonic-gate asyerror(int level, const char *fmt, ...) 43460Sstevel@tonic-gate { 43470Sstevel@tonic-gate va_list adx; 43480Sstevel@tonic-gate static time_t last; 43490Sstevel@tonic-gate static const char *lastfmt; 43500Sstevel@tonic-gate time_t now; 43510Sstevel@tonic-gate 43520Sstevel@tonic-gate /* 43530Sstevel@tonic-gate * Don't print the same error message too often. 43540Sstevel@tonic-gate * Print the message only if we have not printed the 43550Sstevel@tonic-gate * message within the last second. 43560Sstevel@tonic-gate * Note: that fmt cannot be a pointer to a string 43570Sstevel@tonic-gate * stored on the stack. The fmt pointer 43580Sstevel@tonic-gate * must be in the data segment otherwise lastfmt would point 43590Sstevel@tonic-gate * to non-sense. 43600Sstevel@tonic-gate */ 43610Sstevel@tonic-gate now = gethrestime_sec(); 43620Sstevel@tonic-gate if (last == now && lastfmt == fmt) 43630Sstevel@tonic-gate return; 43640Sstevel@tonic-gate 43650Sstevel@tonic-gate last = now; 43660Sstevel@tonic-gate lastfmt = fmt; 43670Sstevel@tonic-gate 43680Sstevel@tonic-gate va_start(adx, fmt); 43690Sstevel@tonic-gate vcmn_err(level, fmt, adx); 43700Sstevel@tonic-gate va_end(adx); 43710Sstevel@tonic-gate } 43720Sstevel@tonic-gate 43730Sstevel@tonic-gate /* 43740Sstevel@tonic-gate * asy_parse_mode(dev_info_t *devi, struct asycom *asy) 43750Sstevel@tonic-gate * The value of this property is in the form of "9600,8,n,1,-" 43760Sstevel@tonic-gate * 1) speed: 9600, 4800, ... 43770Sstevel@tonic-gate * 2) data bits 43780Sstevel@tonic-gate * 3) parity: n(none), e(even), o(odd) 43790Sstevel@tonic-gate * 4) stop bits 43800Sstevel@tonic-gate * 5) handshake: -(none), h(hardware: rts/cts), s(software: xon/off) 43810Sstevel@tonic-gate * 43820Sstevel@tonic-gate * This parsing came from a SPARCstation eeprom. 43830Sstevel@tonic-gate */ 43840Sstevel@tonic-gate static void 43850Sstevel@tonic-gate asy_parse_mode(dev_info_t *devi, struct asycom *asy) 43860Sstevel@tonic-gate { 43870Sstevel@tonic-gate char name[40]; 43880Sstevel@tonic-gate char val[40]; 43890Sstevel@tonic-gate int len; 43900Sstevel@tonic-gate int ret; 43910Sstevel@tonic-gate char *p; 43920Sstevel@tonic-gate char *p1; 43930Sstevel@tonic-gate 43940Sstevel@tonic-gate ASSERT(asy->asy_com_port != 0); 43950Sstevel@tonic-gate 43960Sstevel@tonic-gate /* 43970Sstevel@tonic-gate * Parse the ttyx-mode property 43980Sstevel@tonic-gate */ 43990Sstevel@tonic-gate (void) sprintf(name, "tty%c-mode", asy->asy_com_port + 'a' - 1); 44000Sstevel@tonic-gate len = sizeof (val); 44010Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 44020Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) { 44030Sstevel@tonic-gate (void) sprintf(name, "com%c-mode", asy->asy_com_port + '0'); 44040Sstevel@tonic-gate len = sizeof (val); 44050Sstevel@tonic-gate ret = GET_PROP(devi, name, DDI_PROP_CANSLEEP, val, &len); 44060Sstevel@tonic-gate } 44070Sstevel@tonic-gate 44080Sstevel@tonic-gate /* no property to parse */ 44090Sstevel@tonic-gate asy->asy_cflag = 0; 44100Sstevel@tonic-gate if (ret != DDI_PROP_SUCCESS) 44110Sstevel@tonic-gate return; 44120Sstevel@tonic-gate 44130Sstevel@tonic-gate p = val; 44140Sstevel@tonic-gate /* ---- baud rate ---- */ 44150Sstevel@tonic-gate asy->asy_cflag = CREAD|B9600; /* initial default */ 44160Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 44170Sstevel@tonic-gate *p1++ = '\0'; 44180Sstevel@tonic-gate } else { 44190Sstevel@tonic-gate asy->asy_cflag |= BITS8; /* add default bits */ 44200Sstevel@tonic-gate return; 44210Sstevel@tonic-gate } 44220Sstevel@tonic-gate 44230Sstevel@tonic-gate if (strcmp(p, "110") == 0) 44240Sstevel@tonic-gate asy->asy_bidx = B110; 44250Sstevel@tonic-gate else if (strcmp(p, "150") == 0) 44260Sstevel@tonic-gate asy->asy_bidx = B150; 44270Sstevel@tonic-gate else if (strcmp(p, "300") == 0) 44280Sstevel@tonic-gate asy->asy_bidx = B300; 44290Sstevel@tonic-gate else if (strcmp(p, "600") == 0) 44300Sstevel@tonic-gate asy->asy_bidx = B600; 44310Sstevel@tonic-gate else if (strcmp(p, "1200") == 0) 44320Sstevel@tonic-gate asy->asy_bidx = B1200; 44330Sstevel@tonic-gate else if (strcmp(p, "2400") == 0) 44340Sstevel@tonic-gate asy->asy_bidx = B2400; 44350Sstevel@tonic-gate else if (strcmp(p, "4800") == 0) 44360Sstevel@tonic-gate asy->asy_bidx = B4800; 44370Sstevel@tonic-gate else if (strcmp(p, "9600") == 0) 44380Sstevel@tonic-gate asy->asy_bidx = B9600; 44390Sstevel@tonic-gate else if (strcmp(p, "19200") == 0) 44400Sstevel@tonic-gate asy->asy_bidx = B19200; 44410Sstevel@tonic-gate else if (strcmp(p, "38400") == 0) 44420Sstevel@tonic-gate asy->asy_bidx = B38400; 44430Sstevel@tonic-gate else if (strcmp(p, "57600") == 0) 44440Sstevel@tonic-gate asy->asy_bidx = B57600; 44450Sstevel@tonic-gate else if (strcmp(p, "115200") == 0) 44460Sstevel@tonic-gate asy->asy_bidx = B115200; 44470Sstevel@tonic-gate else 44480Sstevel@tonic-gate asy->asy_bidx = B9600; 44490Sstevel@tonic-gate 44500Sstevel@tonic-gate asy->asy_cflag &= ~CBAUD; 44510Sstevel@tonic-gate if (asy->asy_bidx > CBAUD) { /* > 38400 uses the CBAUDEXT bit */ 44520Sstevel@tonic-gate asy->asy_cflag |= CBAUDEXT; 44530Sstevel@tonic-gate asy->asy_cflag |= asy->asy_bidx - CBAUD - 1; 44540Sstevel@tonic-gate } else { 44550Sstevel@tonic-gate asy->asy_cflag |= asy->asy_bidx; 44560Sstevel@tonic-gate } 44570Sstevel@tonic-gate 44580Sstevel@tonic-gate ASSERT(asy->asy_bidx == BAUDINDEX(asy->asy_cflag)); 44590Sstevel@tonic-gate 44600Sstevel@tonic-gate /* ---- Next item is data bits ---- */ 44610Sstevel@tonic-gate p = p1; 44620Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 44630Sstevel@tonic-gate *p1++ = '\0'; 44640Sstevel@tonic-gate } else { 44650Sstevel@tonic-gate asy->asy_cflag |= BITS8; /* add default bits */ 44660Sstevel@tonic-gate return; 44670Sstevel@tonic-gate } 44680Sstevel@tonic-gate switch (*p) { 44690Sstevel@tonic-gate default: 44700Sstevel@tonic-gate case '8': 44710Sstevel@tonic-gate asy->asy_cflag |= CS8; 44720Sstevel@tonic-gate asy->asy_lcr = BITS8; 44730Sstevel@tonic-gate break; 44740Sstevel@tonic-gate case '7': 44750Sstevel@tonic-gate asy->asy_cflag |= CS7; 44760Sstevel@tonic-gate asy->asy_lcr = BITS7; 44770Sstevel@tonic-gate break; 44780Sstevel@tonic-gate case '6': 44790Sstevel@tonic-gate asy->asy_cflag |= CS6; 44800Sstevel@tonic-gate asy->asy_lcr = BITS6; 44810Sstevel@tonic-gate break; 44820Sstevel@tonic-gate case '5': 44830Sstevel@tonic-gate /* LINTED: CS5 is currently zero (but might change) */ 44840Sstevel@tonic-gate asy->asy_cflag |= CS5; 44850Sstevel@tonic-gate asy->asy_lcr = BITS5; 44860Sstevel@tonic-gate break; 44870Sstevel@tonic-gate } 44880Sstevel@tonic-gate 44890Sstevel@tonic-gate /* ---- Parity info ---- */ 44900Sstevel@tonic-gate p = p1; 44910Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 44920Sstevel@tonic-gate *p1++ = '\0'; 44930Sstevel@tonic-gate } else { 44940Sstevel@tonic-gate return; 44950Sstevel@tonic-gate } 44960Sstevel@tonic-gate switch (*p) { 44970Sstevel@tonic-gate default: 44980Sstevel@tonic-gate case 'n': 44990Sstevel@tonic-gate break; 45000Sstevel@tonic-gate case 'e': 45010Sstevel@tonic-gate asy->asy_cflag |= PARENB; 45020Sstevel@tonic-gate asy->asy_lcr |= PEN; break; 45030Sstevel@tonic-gate case 'o': 45040Sstevel@tonic-gate asy->asy_cflag |= PARENB|PARODD; 45050Sstevel@tonic-gate asy->asy_lcr |= PEN|EPS; 45060Sstevel@tonic-gate break; 45070Sstevel@tonic-gate } 45080Sstevel@tonic-gate 45090Sstevel@tonic-gate /* ---- Find stop bits ---- */ 45100Sstevel@tonic-gate p = p1; 45110Sstevel@tonic-gate if (p && (p1 = strchr(p, ',')) != 0) { 45120Sstevel@tonic-gate *p1++ = '\0'; 45130Sstevel@tonic-gate } else { 45140Sstevel@tonic-gate return; 45150Sstevel@tonic-gate } 45160Sstevel@tonic-gate if (*p == '2') { 45170Sstevel@tonic-gate asy->asy_cflag |= CSTOPB; 45180Sstevel@tonic-gate asy->asy_lcr |= STB; 45190Sstevel@tonic-gate } 45200Sstevel@tonic-gate 45210Sstevel@tonic-gate /* ---- handshake is next ---- */ 45220Sstevel@tonic-gate p = p1; 45230Sstevel@tonic-gate if (p) { 45240Sstevel@tonic-gate if ((p1 = strchr(p, ',')) != 0) 45250Sstevel@tonic-gate *p1++ = '\0'; 45260Sstevel@tonic-gate 45270Sstevel@tonic-gate if (*p == 'h') 45280Sstevel@tonic-gate asy->asy_cflag |= CRTSCTS; 45290Sstevel@tonic-gate else if (*p == 's') 45300Sstevel@tonic-gate asy->asy_cflag |= CRTSXOFF; 45310Sstevel@tonic-gate } 45320Sstevel@tonic-gate } 45330Sstevel@tonic-gate 45340Sstevel@tonic-gate /* 45350Sstevel@tonic-gate * Check for abort character sequence 45360Sstevel@tonic-gate */ 45370Sstevel@tonic-gate static boolean_t 45380Sstevel@tonic-gate abort_charseq_recognize(uchar_t ch) 45390Sstevel@tonic-gate { 45400Sstevel@tonic-gate static int state = 0; 45410Sstevel@tonic-gate #define CNTRL(c) ((c)&037) 45420Sstevel@tonic-gate static char sequence[] = { '\r', '~', CNTRL('b') }; 45430Sstevel@tonic-gate 45440Sstevel@tonic-gate if (ch == sequence[state]) { 45450Sstevel@tonic-gate if (++state >= sizeof (sequence)) { 45460Sstevel@tonic-gate state = 0; 45470Sstevel@tonic-gate return (B_TRUE); 45480Sstevel@tonic-gate } 45490Sstevel@tonic-gate } else { 45500Sstevel@tonic-gate state = (ch == sequence[0]) ? 1 : 0; 45510Sstevel@tonic-gate } 45520Sstevel@tonic-gate return (B_FALSE); 45530Sstevel@tonic-gate } 45540Sstevel@tonic-gate 45550Sstevel@tonic-gate /* 45560Sstevel@tonic-gate * Flow control functions 45570Sstevel@tonic-gate */ 45580Sstevel@tonic-gate /* 45590Sstevel@tonic-gate * Software input flow control 45600Sstevel@tonic-gate * This function can execute software input flow control sucessfully 45610Sstevel@tonic-gate * at most of situations except that the line is in BREAK status 45620Sstevel@tonic-gate * (timed and untimed break). 45630Sstevel@tonic-gate * INPUT VALUE of onoff: 45640Sstevel@tonic-gate * FLOW_START means to send out a XON char 45650Sstevel@tonic-gate * and clear SW input flow control flag. 45660Sstevel@tonic-gate * FLOW_STOP means to send out a XOFF char 45670Sstevel@tonic-gate * and set SW input flow control flag. 45680Sstevel@tonic-gate * FLOW_CHECK means to check whether there is pending XON/XOFF 45690Sstevel@tonic-gate * if it is true, send it out. 45700Sstevel@tonic-gate * INPUT VALUE of type: 45710Sstevel@tonic-gate * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 45720Sstevel@tonic-gate * IN_FLOW_STREAMS means flow control is due to STREAMS 45730Sstevel@tonic-gate * IN_FLOW_USER means flow control is due to user's commands 45740Sstevel@tonic-gate * RETURN VALUE: B_FALSE means no flow control char is sent 45750Sstevel@tonic-gate * B_TRUE means one flow control char is sent 45760Sstevel@tonic-gate */ 45770Sstevel@tonic-gate static boolean_t 45780Sstevel@tonic-gate async_flowcontrol_sw_input(struct asycom *asy, async_flowc_action onoff, 45790Sstevel@tonic-gate int type) 45800Sstevel@tonic-gate { 45810Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 45820Sstevel@tonic-gate int instance = UNIT(async->async_dev); 45830Sstevel@tonic-gate int rval = B_FALSE; 45840Sstevel@tonic-gate 45850Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 45860Sstevel@tonic-gate 45870Sstevel@tonic-gate if (!(async->async_ttycommon.t_iflag & IXOFF)) 45880Sstevel@tonic-gate return (rval); 45890Sstevel@tonic-gate 45900Sstevel@tonic-gate /* 45910Sstevel@tonic-gate * If we get this far, then we know IXOFF is set. 45920Sstevel@tonic-gate */ 45930Sstevel@tonic-gate switch (onoff) { 45940Sstevel@tonic-gate case FLOW_STOP: 45950Sstevel@tonic-gate async->async_inflow_source |= type; 45960Sstevel@tonic-gate 45970Sstevel@tonic-gate /* 45980Sstevel@tonic-gate * We'll send an XOFF character for each of up to 45990Sstevel@tonic-gate * three different input flow control attempts to stop input. 46000Sstevel@tonic-gate * If we already send out one XOFF, but FLOW_STOP comes again, 46010Sstevel@tonic-gate * it seems that input flow control becomes more serious, 46020Sstevel@tonic-gate * then send XOFF again. 46030Sstevel@tonic-gate */ 46040Sstevel@tonic-gate if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 46050Sstevel@tonic-gate IN_FLOW_STREAMS | IN_FLOW_USER)) 46060Sstevel@tonic-gate async->async_flags |= ASYNC_SW_IN_FLOW | 46070Sstevel@tonic-gate ASYNC_SW_IN_NEEDED; 46080Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_SFLOW, "async%d: input sflow stop, " 46090Sstevel@tonic-gate "type = %x\n", instance, async->async_inflow_source); 46100Sstevel@tonic-gate break; 46110Sstevel@tonic-gate case FLOW_START: 46120Sstevel@tonic-gate async->async_inflow_source &= ~type; 46130Sstevel@tonic-gate if (async->async_inflow_source == 0) { 46140Sstevel@tonic-gate async->async_flags = (async->async_flags & 46150Sstevel@tonic-gate ~ASYNC_SW_IN_FLOW) | ASYNC_SW_IN_NEEDED; 46160Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: " 46170Sstevel@tonic-gate "input sflow start\n", instance); 46180Sstevel@tonic-gate } 46190Sstevel@tonic-gate break; 46200Sstevel@tonic-gate default: 46210Sstevel@tonic-gate break; 46220Sstevel@tonic-gate } 46230Sstevel@tonic-gate 46240Sstevel@tonic-gate if (((async->async_flags & (ASYNC_SW_IN_NEEDED | ASYNC_BREAK | 46250Sstevel@tonic-gate ASYNC_OUT_SUSPEND)) == ASYNC_SW_IN_NEEDED) && 46261106Smrj (ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + LSR) & XHRE)) { 46270Sstevel@tonic-gate /* 46280Sstevel@tonic-gate * If we get this far, then we know we need to send out 46290Sstevel@tonic-gate * XON or XOFF char. 46300Sstevel@tonic-gate */ 46310Sstevel@tonic-gate async->async_flags = (async->async_flags & 46320Sstevel@tonic-gate ~ASYNC_SW_IN_NEEDED) | ASYNC_BUSY; 46331106Smrj ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + DAT, 46340Sstevel@tonic-gate async->async_flags & ASYNC_SW_IN_FLOW ? 46350Sstevel@tonic-gate async->async_stopc : async->async_startc); 46360Sstevel@tonic-gate rval = B_TRUE; 46370Sstevel@tonic-gate } 46380Sstevel@tonic-gate return (rval); 46390Sstevel@tonic-gate } 46400Sstevel@tonic-gate 46410Sstevel@tonic-gate /* 46420Sstevel@tonic-gate * Software output flow control 46430Sstevel@tonic-gate * This function can be executed sucessfully at any situation. 46440Sstevel@tonic-gate * It does not handle HW, and just change the SW output flow control flag. 46450Sstevel@tonic-gate * INPUT VALUE of onoff: 46460Sstevel@tonic-gate * FLOW_START means to clear SW output flow control flag, 46470Sstevel@tonic-gate * also combine with HW output flow control status to 46480Sstevel@tonic-gate * determine if we need to set ASYNC_OUT_FLW_RESUME. 46490Sstevel@tonic-gate * FLOW_STOP means to set SW output flow control flag, 46500Sstevel@tonic-gate * also clear ASYNC_OUT_FLW_RESUME. 46510Sstevel@tonic-gate */ 46520Sstevel@tonic-gate static void 46530Sstevel@tonic-gate async_flowcontrol_sw_output(struct asycom *asy, async_flowc_action onoff) 46540Sstevel@tonic-gate { 46550Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 46560Sstevel@tonic-gate int instance = UNIT(async->async_dev); 46570Sstevel@tonic-gate 46580Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 46590Sstevel@tonic-gate 46600Sstevel@tonic-gate if (!(async->async_ttycommon.t_iflag & IXON)) 46610Sstevel@tonic-gate return; 46620Sstevel@tonic-gate 46630Sstevel@tonic-gate switch (onoff) { 46640Sstevel@tonic-gate case FLOW_STOP: 46650Sstevel@tonic-gate async->async_flags |= ASYNC_SW_OUT_FLW; 46660Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 46670Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow stop\n", 46680Sstevel@tonic-gate instance); 46690Sstevel@tonic-gate break; 46700Sstevel@tonic-gate case FLOW_START: 46710Sstevel@tonic-gate async->async_flags &= ~ASYNC_SW_OUT_FLW; 46720Sstevel@tonic-gate if (!(async->async_flags & ASYNC_HW_OUT_FLW)) 46730Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_FLW_RESUME; 46740Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_SFLOW, "async%d: output sflow start\n", 46750Sstevel@tonic-gate instance); 46760Sstevel@tonic-gate break; 46770Sstevel@tonic-gate default: 46780Sstevel@tonic-gate break; 46790Sstevel@tonic-gate } 46800Sstevel@tonic-gate } 46810Sstevel@tonic-gate 46820Sstevel@tonic-gate /* 46830Sstevel@tonic-gate * Hardware input flow control 46840Sstevel@tonic-gate * This function can be executed sucessfully at any situation. 46850Sstevel@tonic-gate * It directly changes RTS depending on input parameter onoff. 46860Sstevel@tonic-gate * INPUT VALUE of onoff: 46870Sstevel@tonic-gate * FLOW_START means to clear HW input flow control flag, 46880Sstevel@tonic-gate * and pull up RTS if it is low. 46890Sstevel@tonic-gate * FLOW_STOP means to set HW input flow control flag, 46900Sstevel@tonic-gate * and low RTS if it is high. 46910Sstevel@tonic-gate * INPUT VALUE of type: 46920Sstevel@tonic-gate * IN_FLOW_RINGBUFF means flow control is due to RING BUFFER 46930Sstevel@tonic-gate * IN_FLOW_STREAMS means flow control is due to STREAMS 46940Sstevel@tonic-gate * IN_FLOW_USER means flow control is due to user's commands 46950Sstevel@tonic-gate */ 46960Sstevel@tonic-gate static void 46970Sstevel@tonic-gate async_flowcontrol_hw_input(struct asycom *asy, async_flowc_action onoff, 46980Sstevel@tonic-gate int type) 46990Sstevel@tonic-gate { 47000Sstevel@tonic-gate uchar_t mcr; 47010Sstevel@tonic-gate uchar_t flag; 47020Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 47030Sstevel@tonic-gate int instance = UNIT(async->async_dev); 47040Sstevel@tonic-gate 47050Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 47060Sstevel@tonic-gate 47070Sstevel@tonic-gate if (!(async->async_ttycommon.t_cflag & CRTSXOFF)) 47080Sstevel@tonic-gate return; 47090Sstevel@tonic-gate 47100Sstevel@tonic-gate switch (onoff) { 47110Sstevel@tonic-gate case FLOW_STOP: 47120Sstevel@tonic-gate async->async_inflow_source |= type; 47130Sstevel@tonic-gate if (async->async_inflow_source & (IN_FLOW_RINGBUFF | 47140Sstevel@tonic-gate IN_FLOW_STREAMS | IN_FLOW_USER)) 47150Sstevel@tonic-gate async->async_flags |= ASYNC_HW_IN_FLOW; 47160Sstevel@tonic-gate DEBUGCONT2(ASY_DEBUG_HFLOW, "async%d: input hflow stop, " 47170Sstevel@tonic-gate "type = %x\n", instance, async->async_inflow_source); 47180Sstevel@tonic-gate break; 47190Sstevel@tonic-gate case FLOW_START: 47200Sstevel@tonic-gate async->async_inflow_source &= ~type; 47210Sstevel@tonic-gate if (async->async_inflow_source == 0) { 47220Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_IN_FLOW; 47230Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: " 47240Sstevel@tonic-gate "input hflow start\n", instance); 47250Sstevel@tonic-gate } 47260Sstevel@tonic-gate break; 47270Sstevel@tonic-gate default: 47280Sstevel@tonic-gate break; 47290Sstevel@tonic-gate } 47301106Smrj mcr = ddi_get8(asy->asy_iohandle, asy->asy_ioaddr + MCR); 47310Sstevel@tonic-gate flag = (async->async_flags & ASYNC_HW_IN_FLOW) ? 0 : RTS; 47320Sstevel@tonic-gate 47330Sstevel@tonic-gate if (((mcr ^ flag) & RTS) != 0) { 47341106Smrj ddi_put8(asy->asy_iohandle, 47350Sstevel@tonic-gate asy->asy_ioaddr + MCR, (mcr ^ RTS)); 47360Sstevel@tonic-gate } 47370Sstevel@tonic-gate } 47380Sstevel@tonic-gate 47390Sstevel@tonic-gate /* 47400Sstevel@tonic-gate * Hardware output flow control 47410Sstevel@tonic-gate * This function can execute HW output flow control sucessfully 47420Sstevel@tonic-gate * at any situation. 47430Sstevel@tonic-gate * It doesn't really change RTS, and just change 47440Sstevel@tonic-gate * HW output flow control flag depending on CTS status. 47450Sstevel@tonic-gate * INPUT VALUE of onoff: 47460Sstevel@tonic-gate * FLOW_START means to clear HW output flow control flag. 47470Sstevel@tonic-gate * also combine with SW output flow control status to 47480Sstevel@tonic-gate * determine if we need to set ASYNC_OUT_FLW_RESUME. 47490Sstevel@tonic-gate * FLOW_STOP means to set HW output flow control flag. 47500Sstevel@tonic-gate * also clear ASYNC_OUT_FLW_RESUME. 47510Sstevel@tonic-gate */ 47520Sstevel@tonic-gate static void 47530Sstevel@tonic-gate async_flowcontrol_hw_output(struct asycom *asy, async_flowc_action onoff) 47540Sstevel@tonic-gate { 47550Sstevel@tonic-gate struct asyncline *async = asy->asy_priv; 47560Sstevel@tonic-gate int instance = UNIT(async->async_dev); 47570Sstevel@tonic-gate 47580Sstevel@tonic-gate ASSERT(mutex_owned(&asy->asy_excl_hi)); 47590Sstevel@tonic-gate 47600Sstevel@tonic-gate if (!(async->async_ttycommon.t_cflag & CRTSCTS)) 47610Sstevel@tonic-gate return; 47620Sstevel@tonic-gate 47630Sstevel@tonic-gate switch (onoff) { 47640Sstevel@tonic-gate case FLOW_STOP: 47650Sstevel@tonic-gate async->async_flags |= ASYNC_HW_OUT_FLW; 47660Sstevel@tonic-gate async->async_flags &= ~ASYNC_OUT_FLW_RESUME; 47670Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow stop\n", 47680Sstevel@tonic-gate instance); 47690Sstevel@tonic-gate break; 47700Sstevel@tonic-gate case FLOW_START: 47710Sstevel@tonic-gate async->async_flags &= ~ASYNC_HW_OUT_FLW; 47720Sstevel@tonic-gate if (!(async->async_flags & ASYNC_SW_OUT_FLW)) 47730Sstevel@tonic-gate async->async_flags |= ASYNC_OUT_FLW_RESUME; 47740Sstevel@tonic-gate DEBUGCONT1(ASY_DEBUG_HFLOW, "async%d: output hflow start\n", 47750Sstevel@tonic-gate instance); 47760Sstevel@tonic-gate break; 47770Sstevel@tonic-gate default: 47780Sstevel@tonic-gate break; 47790Sstevel@tonic-gate } 47800Sstevel@tonic-gate } 4781*7656SSherry.Moore@Sun.COM 4782*7656SSherry.Moore@Sun.COM 4783*7656SSherry.Moore@Sun.COM /* 4784*7656SSherry.Moore@Sun.COM * quiesce(9E) entry point. 4785*7656SSherry.Moore@Sun.COM * 4786*7656SSherry.Moore@Sun.COM * This function is called when the system is single-threaded at high 4787*7656SSherry.Moore@Sun.COM * PIL with preemption disabled. Therefore, this function must not be 4788*7656SSherry.Moore@Sun.COM * blocked. 4789*7656SSherry.Moore@Sun.COM * 4790*7656SSherry.Moore@Sun.COM * This function returns DDI_SUCCESS on success, or DDI_FAILURE on failure. 4791*7656SSherry.Moore@Sun.COM * DDI_FAILURE indicates an error condition and should almost never happen. 4792*7656SSherry.Moore@Sun.COM */ 4793*7656SSherry.Moore@Sun.COM static int 4794*7656SSherry.Moore@Sun.COM asyquiesce(dev_info_t *devi) 4795*7656SSherry.Moore@Sun.COM { 4796*7656SSherry.Moore@Sun.COM int instance; 4797*7656SSherry.Moore@Sun.COM struct asycom *asy; 4798*7656SSherry.Moore@Sun.COM 4799*7656SSherry.Moore@Sun.COM instance = ddi_get_instance(devi); /* find out which unit */ 4800*7656SSherry.Moore@Sun.COM 4801*7656SSherry.Moore@Sun.COM asy = ddi_get_soft_state(asy_soft_state, instance); 4802*7656SSherry.Moore@Sun.COM if (asy == NULL) 4803*7656SSherry.Moore@Sun.COM return (DDI_FAILURE); 4804*7656SSherry.Moore@Sun.COM 4805*7656SSherry.Moore@Sun.COM /* disable all interrupts */ 4806*7656SSherry.Moore@Sun.COM ddi_put8(asy->asy_iohandle, asy->asy_ioaddr + ICR, 0); 4807*7656SSherry.Moore@Sun.COM 4808*7656SSherry.Moore@Sun.COM /* reset the FIFO */ 4809*7656SSherry.Moore@Sun.COM asy_reset_fifo(asy, FIFOTXFLSH | FIFORXFLSH); 4810*7656SSherry.Moore@Sun.COM 4811*7656SSherry.Moore@Sun.COM return (DDI_SUCCESS); 4812*7656SSherry.Moore@Sun.COM } 4813