xref: /onnv-gate/usr/src/uts/sun/io/ttymux/ttymux.c (revision 7656:2621e50fdf4a)
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
52319Sja97890  * Common Development and Distribution License (the "License").
62319Sja97890  * 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  */
210Sstevel@tonic-gate /*
22*7656SSherry.Moore@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate 
270Sstevel@tonic-gate /*
280Sstevel@tonic-gate  * DESCRIPTION
290Sstevel@tonic-gate  *
300Sstevel@tonic-gate  * ttymux - Multiplexer driver for multiplexing termio compliant streams onto
310Sstevel@tonic-gate  * a single upper stream.
320Sstevel@tonic-gate  *
330Sstevel@tonic-gate  * ADD2FRONT macro can be used to specify the order in which a console
340Sstevel@tonic-gate  * device is put in the queue of multiplexed physical serial devices,
350Sstevel@tonic-gate  * during the association and disassociation of a console interface.
360Sstevel@tonic-gate  * When this macro is defined, the device is placed in front of the queue,
370Sstevel@tonic-gate  * otherwise by default it is placed at the end.
380Sstevel@tonic-gate  * Console I/O happens to each of the physical devices in the order of
390Sstevel@tonic-gate  * their position in this queue.
400Sstevel@tonic-gate  */
410Sstevel@tonic-gate 
420Sstevel@tonic-gate #include <sys/types.h>
430Sstevel@tonic-gate #include <sys/file.h>
440Sstevel@tonic-gate #include <sys/stream.h>
450Sstevel@tonic-gate #include <sys/strsubr.h>
460Sstevel@tonic-gate #include <sys/strlog.h>
470Sstevel@tonic-gate #include <sys/strsun.h>
480Sstevel@tonic-gate #include <sys/modctl.h>
490Sstevel@tonic-gate #include <sys/debug.h>
500Sstevel@tonic-gate #include <sys/kbio.h>
510Sstevel@tonic-gate #include <sys/devops.h>
520Sstevel@tonic-gate #include <sys/errno.h>
530Sstevel@tonic-gate #include <sys/stat.h>
540Sstevel@tonic-gate #include <sys/kmem.h>
550Sstevel@tonic-gate #include <sys/ddi.h>
560Sstevel@tonic-gate #include <sys/consdev.h>
570Sstevel@tonic-gate #include <sys/tty.h>
580Sstevel@tonic-gate #include <sys/ptyvar.h>
590Sstevel@tonic-gate #include <sys/termio.h>
600Sstevel@tonic-gate #include <sys/fcntl.h>
610Sstevel@tonic-gate #include <sys/mkdev.h>
620Sstevel@tonic-gate #include <sys/ser_sync.h>
630Sstevel@tonic-gate #include <sys/esunddi.h>
640Sstevel@tonic-gate #include <sys/policy.h>
650Sstevel@tonic-gate 
660Sstevel@tonic-gate #include <sys/ttymux.h>
670Sstevel@tonic-gate #include "ttymux_impl.h"
680Sstevel@tonic-gate 
690Sstevel@tonic-gate /*
700Sstevel@tonic-gate  * Extern declarations
710Sstevel@tonic-gate  */
720Sstevel@tonic-gate extern mblk_t *mkiocb(uint_t);
730Sstevel@tonic-gate extern int nulldev();
740Sstevel@tonic-gate extern uintptr_t space_fetch(char *key);
750Sstevel@tonic-gate 
760Sstevel@tonic-gate extern int sm_ioctl_cmd(sm_uqi_t *, mblk_t *);
770Sstevel@tonic-gate extern int ttymux_abort_ioctl(mblk_t *);
780Sstevel@tonic-gate extern int ttymux_device_fini(sm_lqi_t *);
790Sstevel@tonic-gate extern int ttymux_device_init(sm_lqi_t *);
800Sstevel@tonic-gate 
810Sstevel@tonic-gate /*
820Sstevel@tonic-gate  * Exported interfaces
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate int sm_disassociate(int, sm_lqi_t *, ulong_t);
850Sstevel@tonic-gate int sm_associate(int, sm_lqi_t *, ulong_t, uint_t, char *);
860Sstevel@tonic-gate 
870Sstevel@tonic-gate /*
880Sstevel@tonic-gate  * Variables defined here and visible only internally
890Sstevel@tonic-gate  */
900Sstevel@tonic-gate sm_ss_t		*sm_ssp = 0;
910Sstevel@tonic-gate static int	sm_instance = 0;
920Sstevel@tonic-gate static int	smctlunit;
930Sstevel@tonic-gate 
940Sstevel@tonic-gate static uint_t	sm_default_trflag = 0;
950Sstevel@tonic-gate uint_t		sm_max_units = 6;
960Sstevel@tonic-gate uint_t		sm_minor_cnt = 0;
970Sstevel@tonic-gate static uint_t	sm_refuse_opens = 0;
980Sstevel@tonic-gate 
990Sstevel@tonic-gate /*
1000Sstevel@tonic-gate  * Local definitions.
1010Sstevel@tonic-gate  */
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate /* force these flags to be unset on console devices */
1040Sstevel@tonic-gate static ulong_t	sm_cmask = (ulong_t)(CRTSXOFF|CRTSCTS);
1050Sstevel@tonic-gate 
1060Sstevel@tonic-gate /*
1070Sstevel@tonic-gate  * SECTION
1080Sstevel@tonic-gate  * Implementation Section:
1090Sstevel@tonic-gate  */
1100Sstevel@tonic-gate void
sm_debug(char * msg,...)1110Sstevel@tonic-gate sm_debug(char *msg, ...)
1120Sstevel@tonic-gate {
1130Sstevel@tonic-gate 	va_list	args;
1140Sstevel@tonic-gate 	char	buf[256];
1150Sstevel@tonic-gate 	int	sz;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	va_start(args, msg);
1180Sstevel@tonic-gate 	sz = vsnprintf(buf, sizeof (buf), msg, args);
1190Sstevel@tonic-gate 	va_end(args);
1200Sstevel@tonic-gate 
1210Sstevel@tonic-gate 	if (sz < 0)
1220Sstevel@tonic-gate 		(void) strlog(ddi_driver_major(sm_ssp->sm_dip), sm_instance, 1,
1230Sstevel@tonic-gate 		    SL_TRACE, "vsnprintf parse error\n");
1240Sstevel@tonic-gate 	else if (sz > sizeof (buf)) {
1250Sstevel@tonic-gate 		char *b;
1260Sstevel@tonic-gate 		size_t	len = sz + 1;
1270Sstevel@tonic-gate 
1280Sstevel@tonic-gate 		b = kmem_alloc(len, KM_SLEEP);
1290Sstevel@tonic-gate 		va_start(args, msg);
1300Sstevel@tonic-gate 		sz = vsnprintf(b, len, msg, args);
1310Sstevel@tonic-gate 		va_end(args);
1320Sstevel@tonic-gate 		if (sz > 0)
1330Sstevel@tonic-gate 			(void) strlog(ddi_driver_major(sm_ssp->sm_dip),
1340Sstevel@tonic-gate 			    sm_instance, 1, SL_TRACE, b);
1350Sstevel@tonic-gate 		kmem_free(b, len);
1360Sstevel@tonic-gate 	} else {
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate 		(void) strlog(ddi_driver_major(sm_ssp->sm_dip), sm_instance,
1390Sstevel@tonic-gate 		    1, SL_TRACE, buf);
1400Sstevel@tonic-gate 	}
1410Sstevel@tonic-gate }
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate void
sm_log(char * msg,...)1440Sstevel@tonic-gate sm_log(char *msg, ...)
1450Sstevel@tonic-gate {
1460Sstevel@tonic-gate 	va_list	args;
1470Sstevel@tonic-gate 	char	buf[128];
1480Sstevel@tonic-gate 	int	sz;
1490Sstevel@tonic-gate 
1500Sstevel@tonic-gate 	va_start(args, msg);
1510Sstevel@tonic-gate 	sz = vsnprintf(buf, sizeof (buf), msg, args);
1520Sstevel@tonic-gate 	va_end(args);
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate 	if (sz < 0)
1550Sstevel@tonic-gate 		(void) strlog(ddi_driver_major(sm_ssp->sm_dip), sm_instance, 1,
1560Sstevel@tonic-gate 		    SL_TRACE, "vsnprintf parse error\n");
1570Sstevel@tonic-gate 	else if (sz > sizeof (buf)) {
1580Sstevel@tonic-gate 		char *b;
1590Sstevel@tonic-gate 		size_t	len = sz + 1;
1600Sstevel@tonic-gate 
1610Sstevel@tonic-gate 		b = kmem_alloc(len, KM_SLEEP);
1620Sstevel@tonic-gate 		va_start(args, msg);
1630Sstevel@tonic-gate 		sz = vsnprintf(b, len, msg, args);
1640Sstevel@tonic-gate 		va_end(args);
1650Sstevel@tonic-gate 		if (sz > 0)
1660Sstevel@tonic-gate 			(void) strlog(ddi_driver_major(sm_ssp->sm_dip),
1670Sstevel@tonic-gate 			    sm_instance, 1, SL_NOTE, b);
1680Sstevel@tonic-gate 		kmem_free(b, len);
1690Sstevel@tonic-gate 	} else {
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 		(void) strlog(ddi_driver_major(sm_ssp->sm_dip), sm_instance,
1720Sstevel@tonic-gate 		    1, SL_NOTE, buf);
1730Sstevel@tonic-gate 	}
1740Sstevel@tonic-gate }
1750Sstevel@tonic-gate 
1760Sstevel@tonic-gate /*
1770Sstevel@tonic-gate  * Should only be called if the caller can guarantee that the vnode
1780Sstevel@tonic-gate  * and/or the stream won't disappear while finding the dip.
1790Sstevel@tonic-gate  * This routine is only called during an I_PLINK request so it's safe.
1800Sstevel@tonic-gate  * The routine obtains the dev_t for a linked se stream.
1810Sstevel@tonic-gate  */
1820Sstevel@tonic-gate static void
sm_setdip(queue_t * q,sm_lqi_t * lqi)1830Sstevel@tonic-gate sm_setdip(queue_t *q, sm_lqi_t *lqi)
1840Sstevel@tonic-gate {
1850Sstevel@tonic-gate 	lqi->sm_dev = q && STREAM(q) ? STREAM(q)->sd_vnode->v_rdev : NODEV;
1860Sstevel@tonic-gate }
1870Sstevel@tonic-gate 
1880Sstevel@tonic-gate /*
1890Sstevel@tonic-gate  * Called from driver close, state change reports and I_PUNLINK ioctl.
1900Sstevel@tonic-gate  * A lower stream has been unlinked - clean up the state associated with it.
1910Sstevel@tonic-gate  */
1920Sstevel@tonic-gate void
sm_lqifree(sm_lqi_t * lqi)1930Sstevel@tonic-gate sm_lqifree(sm_lqi_t *lqi)
1940Sstevel@tonic-gate {
1950Sstevel@tonic-gate 	int mu_owned;
1960Sstevel@tonic-gate 	sm_lqi_t **pplqi;
1970Sstevel@tonic-gate 
1980Sstevel@tonic-gate 	ASSERT(mutex_owned(lqi->sm_umutex));
1990Sstevel@tonic-gate 	ASSERT(SM_RQ(lqi) != 0);
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 	/*
2020Sstevel@tonic-gate 	 * Clear all state associated with this lower queue except
2030Sstevel@tonic-gate 	 * the identity of the queues themselves and the link id which
2040Sstevel@tonic-gate 	 * can only be cleared by issuing a streams I_PUNLINK ioctl.
2050Sstevel@tonic-gate 	 *
2060Sstevel@tonic-gate 	 * The association of a lower queue is a two step process:
2070Sstevel@tonic-gate 	 * 1. initialise the lower q data structure on I_PLINK
2080Sstevel@tonic-gate 	 * 2. associate an upper q with the lower q on SM_CMD_ASSOCIATE.
2090Sstevel@tonic-gate 	 *
2100Sstevel@tonic-gate 	 * If step 2 has ocurred then
2110Sstevel@tonic-gate 	 * remove this lower queue info from the logical unit.
2120Sstevel@tonic-gate 	 */
2130Sstevel@tonic-gate 	if (lqi->sm_uqi) {
2140Sstevel@tonic-gate 		sm_dbg('Y', ("lqifree unit %d, ", lqi->sm_uqi->sm_lunit));
2150Sstevel@tonic-gate 		if ((mu_owned = mutex_owned(lqi->sm_uqi->sm_umutex)) == 0)
2160Sstevel@tonic-gate 			LOCK_UNIT(lqi->sm_uqi);
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 		pplqi = &lqi->sm_uqi->sm_lqs;
2190Sstevel@tonic-gate 		while (*pplqi != lqi) {
2200Sstevel@tonic-gate 			ASSERT(*pplqi);
2210Sstevel@tonic-gate 			pplqi = &((*pplqi)->sm_nlqi);
2220Sstevel@tonic-gate 		}
2230Sstevel@tonic-gate 		*pplqi = lqi->sm_nlqi;
2240Sstevel@tonic-gate 		lqi->sm_uqi->sm_nlqs--;
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 		if (mu_owned == 0)
2270Sstevel@tonic-gate 			UNLOCK_UNIT(lqi->sm_uqi);
2280Sstevel@tonic-gate 
2290Sstevel@tonic-gate 		lqi->sm_uqi = 0;
2300Sstevel@tonic-gate 	}
2310Sstevel@tonic-gate }
2320Sstevel@tonic-gate 
2330Sstevel@tonic-gate /*
2340Sstevel@tonic-gate  * Given a q return the associated lower queue data structure or NULL.
2350Sstevel@tonic-gate  * Return the data locked.
2360Sstevel@tonic-gate  */
2370Sstevel@tonic-gate static sm_lqi_t *
get_lqi_byq(queue_t * q)2380Sstevel@tonic-gate get_lqi_byq(queue_t *q)
2390Sstevel@tonic-gate {
2400Sstevel@tonic-gate 	int i;
2410Sstevel@tonic-gate 	sm_lqi_t *lqi, *flqi = 0;
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	for (i = 0; i < MAX_LQS; i++) {
2440Sstevel@tonic-gate 		lqi = &sm_ssp->sm_lqs[i];
2450Sstevel@tonic-gate 		LOCK_UNIT(lqi);
2460Sstevel@tonic-gate 		if (flqi == 0 && lqi->sm_linkid == 0) /* assumes muxids != 0 */
2470Sstevel@tonic-gate 			flqi = lqi;
2480Sstevel@tonic-gate 		else if (SM_RQ(lqi) == q || SM_WQ(lqi) == q) {
2490Sstevel@tonic-gate 			if (flqi)
2500Sstevel@tonic-gate 				UNLOCK_UNIT(flqi);
2510Sstevel@tonic-gate 			return (lqi);
2520Sstevel@tonic-gate 		}
2530Sstevel@tonic-gate 		else
2540Sstevel@tonic-gate 			UNLOCK_UNIT(lqi);
2550Sstevel@tonic-gate 	}
2560Sstevel@tonic-gate 	return (flqi);
2570Sstevel@tonic-gate }
2580Sstevel@tonic-gate 
2590Sstevel@tonic-gate /*
2600Sstevel@tonic-gate  * Given a streams link identifier return the associated lower queue data
2610Sstevel@tonic-gate  * structure or NULL.
2620Sstevel@tonic-gate  */
2630Sstevel@tonic-gate sm_lqi_t *
get_lqi_byid(int linkid)2640Sstevel@tonic-gate get_lqi_byid(int linkid)
2650Sstevel@tonic-gate {
2660Sstevel@tonic-gate 	int i;
2670Sstevel@tonic-gate 	sm_lqi_t *lqi;
2680Sstevel@tonic-gate 
2690Sstevel@tonic-gate 	if (linkid == 0)
2700Sstevel@tonic-gate 		return (NULL);
2710Sstevel@tonic-gate 	for (i = 0; i < MAX_LQS; i++) {
2720Sstevel@tonic-gate 		lqi = &sm_ssp->sm_lqs[i];
2730Sstevel@tonic-gate 		if (lqi->sm_linkid == linkid)
2740Sstevel@tonic-gate 			return (lqi);
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 	return (NULL);
2770Sstevel@tonic-gate }
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate /*
2800Sstevel@tonic-gate  * Given a dev_t for a lower stream return the associated lower queue data
2810Sstevel@tonic-gate  * structure or NULL.
2820Sstevel@tonic-gate  */
2830Sstevel@tonic-gate sm_lqi_t *
get_lqi_bydevt(dev_t dev)2840Sstevel@tonic-gate get_lqi_bydevt(dev_t dev)
2850Sstevel@tonic-gate {
2860Sstevel@tonic-gate 	int i;
2870Sstevel@tonic-gate 	sm_lqi_t *lqi;
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	if (dev == NODEV)
2900Sstevel@tonic-gate 		return (NULL);
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	for (i = 0; i < MAX_LQS; i++) {
2930Sstevel@tonic-gate 		lqi = &sm_ssp->sm_lqs[i];
2940Sstevel@tonic-gate 		if (lqi->sm_dev == dev)
2950Sstevel@tonic-gate 			return (lqi);
2960Sstevel@tonic-gate 	}
2970Sstevel@tonic-gate 	return (NULL);
2980Sstevel@tonic-gate }
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate /*
3010Sstevel@tonic-gate  * Determine whether the input flag is set on at least
3020Sstevel@tonic-gate  * howmany queues.
3030Sstevel@tonic-gate  */
3040Sstevel@tonic-gate static int
sm_is_flag_set(sm_uqi_t * uqi,uint_t flag,uint_t howmany)3050Sstevel@tonic-gate sm_is_flag_set(sm_uqi_t *uqi, uint_t flag, uint_t howmany)
3060Sstevel@tonic-gate {
3070Sstevel@tonic-gate 	sm_lqi_t *lqi;
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 	if (howmany == 0)
3100Sstevel@tonic-gate 		return (0);
3110Sstevel@tonic-gate 
3120Sstevel@tonic-gate 	for (lqi = uqi->sm_lqs; lqi; lqi = lqi->sm_nlqi) {
3130Sstevel@tonic-gate 		if (lqi->sm_flags & flag)
3140Sstevel@tonic-gate 			if (--howmany == 0)
3150Sstevel@tonic-gate 				return (1);
3160Sstevel@tonic-gate 	}
3170Sstevel@tonic-gate 	return (0);
3180Sstevel@tonic-gate }
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate /*
3210Sstevel@tonic-gate  * How many usable queues are associated with a given upper stream
3220Sstevel@tonic-gate  */
3230Sstevel@tonic-gate static int
sm_uwq_error(sm_uqi_t * uqi)3240Sstevel@tonic-gate sm_uwq_error(sm_uqi_t *uqi)
3250Sstevel@tonic-gate {
3260Sstevel@tonic-gate 	return (sm_is_flag_set(uqi, (WERROR_MODE|HANGUP_MODE), uqi->sm_nlqs));
3270Sstevel@tonic-gate }
3280Sstevel@tonic-gate 
3290Sstevel@tonic-gate /*
3300Sstevel@tonic-gate  * How many of the queues associated with a given upper stream
3310Sstevel@tonic-gate  * - do not - have the given flags set.
3320Sstevel@tonic-gate  */
3330Sstevel@tonic-gate static int
sm_q_count(sm_uqi_t * uqi,uint_t flag)3340Sstevel@tonic-gate sm_q_count(sm_uqi_t *uqi, uint_t flag)
3350Sstevel@tonic-gate {
3360Sstevel@tonic-gate 	sm_lqi_t *lqi;
3370Sstevel@tonic-gate 	int count = 0;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 	for (lqi = uqi->sm_lqs; lqi; lqi = lqi->sm_nlqi) {
3400Sstevel@tonic-gate 		if ((lqi->sm_flags & flag) == 0)
3410Sstevel@tonic-gate 			count++;
3420Sstevel@tonic-gate 	}
3430Sstevel@tonic-gate 	return (count);
3440Sstevel@tonic-gate }
3450Sstevel@tonic-gate 
3460Sstevel@tonic-gate /*
3470Sstevel@tonic-gate  * How many of the queues associated with a given upper stream
3480Sstevel@tonic-gate  * - do not - have the given flags set.
3490Sstevel@tonic-gate  */
3500Sstevel@tonic-gate static int
sm_qs_without(sm_uqi_t * uqi,uint_t flag,uint_t ioflag)3510Sstevel@tonic-gate sm_qs_without(sm_uqi_t *uqi, uint_t flag, uint_t ioflag)
3520Sstevel@tonic-gate {
3530Sstevel@tonic-gate 	sm_lqi_t *lqi;
3540Sstevel@tonic-gate 	int count = 0;
3550Sstevel@tonic-gate 
3560Sstevel@tonic-gate 	for (lqi = uqi->sm_lqs; lqi; lqi = lqi->sm_nlqi) {
3570Sstevel@tonic-gate 		if ((lqi->sm_flags & flag) == 0 &&
358*7656SSherry.Moore@Sun.COM 		    (lqi->sm_ioflag & ioflag) == 0)
3590Sstevel@tonic-gate 			count++;
3600Sstevel@tonic-gate 	}
3610Sstevel@tonic-gate 	return (count);
3620Sstevel@tonic-gate }
3630Sstevel@tonic-gate 
3640Sstevel@tonic-gate /*
3650Sstevel@tonic-gate  * How many usable queues are associated with a given upper stream
3660Sstevel@tonic-gate  */
3670Sstevel@tonic-gate static int
sm_good_qs(sm_uqi_t * uqi)3680Sstevel@tonic-gate sm_good_qs(sm_uqi_t *uqi)
3690Sstevel@tonic-gate {
3700Sstevel@tonic-gate 	return (sm_q_count(uqi, (WERROR_MODE|HANGUP_MODE)));
3710Sstevel@tonic-gate }
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate static int
sm_cnt_oqs(sm_uqi_t * uqi)3740Sstevel@tonic-gate sm_cnt_oqs(sm_uqi_t *uqi)
3750Sstevel@tonic-gate {
3760Sstevel@tonic-gate 	return (sm_qs_without(uqi, (WERROR_MODE|HANGUP_MODE),
377*7656SSherry.Moore@Sun.COM 	    (uint_t)FOROUTPUT));
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate 
3800Sstevel@tonic-gate /*
3810Sstevel@tonic-gate  * Send an ioctl downstream and remember that it was sent so that
3820Sstevel@tonic-gate  * its response can be caught on the way back up.
3830Sstevel@tonic-gate  */
3840Sstevel@tonic-gate static void
sm_issue_ioctl(void * arg)3850Sstevel@tonic-gate sm_issue_ioctl(void *arg)
3860Sstevel@tonic-gate {
3870Sstevel@tonic-gate 	sm_lqi_t *lqi = arg;
3880Sstevel@tonic-gate 	uint_t cmdflag = 0;
3890Sstevel@tonic-gate 	queue_t *q = SM_WQ(lqi);
3900Sstevel@tonic-gate 	int iocmd, size;
3910Sstevel@tonic-gate 
3920Sstevel@tonic-gate 	LOCK_UNIT(lqi);
3930Sstevel@tonic-gate 
3940Sstevel@tonic-gate 	lqi->sm_bid = 0;
3950Sstevel@tonic-gate 	if ((lqi->sm_flags & (WERROR_MODE|HANGUP_MODE)) == 0 &&
396*7656SSherry.Moore@Sun.COM 	    (lqi->sm_flags & (WANT_CDSTAT|WANT_TCSET))) {
3970Sstevel@tonic-gate 		mblk_t *pioc;
3980Sstevel@tonic-gate 
3990Sstevel@tonic-gate 		if (lqi->sm_flags & WANT_TCSET) {
4000Sstevel@tonic-gate 			lqi->sm_flags &= ~WANT_TCSET;
4010Sstevel@tonic-gate 			iocmd = TCSETS;
4020Sstevel@tonic-gate 			cmdflag = WANT_TCSET;
4030Sstevel@tonic-gate 		} else if (lqi->sm_flags & WANT_SC) {
4040Sstevel@tonic-gate 			lqi->sm_flags &= ~WANT_SC;
4050Sstevel@tonic-gate 			iocmd = TIOCGSOFTCAR;
4060Sstevel@tonic-gate 			cmdflag = WANT_SC;
4070Sstevel@tonic-gate 		} else if (lqi->sm_flags & WANT_CD) {
4080Sstevel@tonic-gate 			lqi->sm_flags &= ~WANT_CD;
4090Sstevel@tonic-gate 			iocmd = TIOCMGET;
4100Sstevel@tonic-gate 		} else if (lqi->sm_flags & WANT_CL) {
4110Sstevel@tonic-gate 			lqi->sm_flags &= ~WANT_CL;
4120Sstevel@tonic-gate 			iocmd = TCGETS;
4130Sstevel@tonic-gate 			cmdflag = WANT_CL;
4140Sstevel@tonic-gate 		} else {
4150Sstevel@tonic-gate 			UNLOCK_UNIT(lqi);
4160Sstevel@tonic-gate 			return;
4170Sstevel@tonic-gate 		}
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate 		if (pioc = mkiocb(iocmd)) {
4200Sstevel@tonic-gate 			if (cmdflag == WANT_TCSET) {
4210Sstevel@tonic-gate 				pioc->b_cont =
4220Sstevel@tonic-gate 				    sm_allocb(sizeof (struct termios),
4230Sstevel@tonic-gate 				    BPRI_MED);
4240Sstevel@tonic-gate 				if (pioc->b_cont == 0) {
4250Sstevel@tonic-gate 					freemsg(pioc);
4260Sstevel@tonic-gate 					pioc = 0;
4270Sstevel@tonic-gate 				} else {
4280Sstevel@tonic-gate 					struct termios *tc = (struct termios *)
4290Sstevel@tonic-gate 					    pioc->b_cont->b_wptr;
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 					bzero((caddr_t)tc,
4320Sstevel@tonic-gate 					    sizeof (struct termios));
4330Sstevel@tonic-gate 					tc->c_cflag = lqi->sm_ttycommon->
434*7656SSherry.Moore@Sun.COM 					    t_cflag;
4350Sstevel@tonic-gate 					pioc->b_cont->b_rptr =
4360Sstevel@tonic-gate 					    pioc->b_cont->b_wptr;
4370Sstevel@tonic-gate 					pioc->b_cont->b_wptr +=
4380Sstevel@tonic-gate 					    sizeof (struct termios);
4390Sstevel@tonic-gate 				}
4400Sstevel@tonic-gate 				size = sizeof (struct iocblk) +
4410Sstevel@tonic-gate 				    sizeof (struct termios);
4420Sstevel@tonic-gate 			}
4430Sstevel@tonic-gate 			else
4440Sstevel@tonic-gate 				size = sizeof (struct iocblk);
4450Sstevel@tonic-gate 		}
4460Sstevel@tonic-gate 		else
4470Sstevel@tonic-gate 			size = sizeof (struct iocblk);
4480Sstevel@tonic-gate 
4490Sstevel@tonic-gate 		if (pioc != 0) {
4500Sstevel@tonic-gate 
4510Sstevel@tonic-gate 			lqi->sm_piocid = ((struct iocblk *)pioc->b_rptr)->
452*7656SSherry.Moore@Sun.COM 			    ioc_id;
4530Sstevel@tonic-gate 			lqi->sm_flags |= SM_IOCPENDING;
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 			/* lqi->sm_flags |= cmdflag; */
4560Sstevel@tonic-gate 			UNLOCK_UNIT(lqi);
4570Sstevel@tonic-gate 			(void) putq(q, pioc);
4580Sstevel@tonic-gate 		} else {
4590Sstevel@tonic-gate 			UNLOCK_UNIT(lqi);
4600Sstevel@tonic-gate 			lqi->sm_bid = qbufcall(WR(q), size, BPRI_MED,
461*7656SSherry.Moore@Sun.COM 			    sm_issue_ioctl, lqi);
4620Sstevel@tonic-gate 		}
4630Sstevel@tonic-gate 	}
4640Sstevel@tonic-gate 	else
4650Sstevel@tonic-gate 		UNLOCK_UNIT(lqi);
4660Sstevel@tonic-gate }
4670Sstevel@tonic-gate 
4680Sstevel@tonic-gate /*
4690Sstevel@tonic-gate  * Associate one of the drivers minor nodes with a serial device.
4700Sstevel@tonic-gate  */
4710Sstevel@tonic-gate int
sm_associate(int unit,sm_lqi_t * plqi,ulong_t tag,uint_t ioflag,char * dp)4720Sstevel@tonic-gate sm_associate(int unit, sm_lqi_t *plqi, ulong_t tag, uint_t ioflag, char *dp)
4730Sstevel@tonic-gate {
4740Sstevel@tonic-gate 	sm_uqi_t *uqi;
4750Sstevel@tonic-gate 	int rval = 0;
4760Sstevel@tonic-gate 
4770Sstevel@tonic-gate 	sm_dbg('Y', ("sm_associate(%d, %d, %d): ",
478*7656SSherry.Moore@Sun.COM 	    (plqi) ? plqi->sm_linkid : 0, unit, ioflag));
4790Sstevel@tonic-gate 	/*
4800Sstevel@tonic-gate 	 * Check the data is valid.
4810Sstevel@tonic-gate 	 * Associate a lower queue with a logical unit.
4820Sstevel@tonic-gate 	 */
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate 	if (unit < 0 || unit >= NLUNITS || plqi == 0 ||
4850Sstevel@tonic-gate 	    (uqi = get_uqi(sm_ssp, unit)) == 0) {
4860Sstevel@tonic-gate 		sm_dbg('@', (" invalid: lqi=0x%p lui=0x%p:", plqi, uqi));
4870Sstevel@tonic-gate 		rval = EINVAL;
4880Sstevel@tonic-gate 	} else {
4890Sstevel@tonic-gate 		if ((ioflag & FORIO) == 0)
4900Sstevel@tonic-gate 			ioflag = FORIO;
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 		LOCK_UNIT(plqi);
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 		if (plqi->sm_uqi) {
4950Sstevel@tonic-gate 			if (plqi->sm_uqi->sm_lunit == unit) {
4960Sstevel@tonic-gate 				if ((ioflag & (uint_t)FORIO) != 0)
4970Sstevel@tonic-gate 					plqi->sm_ioflag =
4980Sstevel@tonic-gate 					    (ioflag & (uint_t)FORIO);
4990Sstevel@tonic-gate 				rval = 0;
5000Sstevel@tonic-gate 			} else {
5010Sstevel@tonic-gate 				sm_dbg('@', ("already associated with unit %d:",
5020Sstevel@tonic-gate 				    plqi->sm_uqi->sm_lunit));
5030Sstevel@tonic-gate 				rval = EINVAL;
5040Sstevel@tonic-gate 			}
5050Sstevel@tonic-gate 		} else {
5060Sstevel@tonic-gate 
5070Sstevel@tonic-gate 			LOCK_UNIT(uqi);
5080Sstevel@tonic-gate 
5090Sstevel@tonic-gate 			if ((ioflag & (uint_t)FORIO) != 0)
5100Sstevel@tonic-gate 				plqi->sm_ioflag = (ioflag & (uint_t)FORIO);
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 			plqi->sm_ttycommon->t_cflag = uqi->sm_ttycommon->
513*7656SSherry.Moore@Sun.COM 			    t_cflag;
5140Sstevel@tonic-gate 			plqi->sm_ttycommon->t_flags = uqi->sm_ttycommon->
515*7656SSherry.Moore@Sun.COM 			    t_flags;
5160Sstevel@tonic-gate 			plqi->sm_uqi = uqi;
5170Sstevel@tonic-gate 			plqi->sm_mbits = 0;
5180Sstevel@tonic-gate 			plqi->sm_tag = tag;
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 			if (*dp == '/')
5210Sstevel@tonic-gate 				(void) strncpy(plqi->sm_path, dp, MAXPATHLEN);
5220Sstevel@tonic-gate 			else
5230Sstevel@tonic-gate 				*(plqi->sm_path) = '\0';
5240Sstevel@tonic-gate 
5250Sstevel@tonic-gate 			plqi->sm_flags |= WANT_TCSET;
5260Sstevel@tonic-gate #ifdef ADD2FRONT
5270Sstevel@tonic-gate 			plqi->sm_nlqi = uqi->sm_lqs;
5280Sstevel@tonic-gate 			uqi->sm_lqs = plqi;
5290Sstevel@tonic-gate #else
5300Sstevel@tonic-gate 			plqi->sm_nlqi = 0;
5310Sstevel@tonic-gate 			if (uqi->sm_lqs) {
5320Sstevel@tonic-gate 				sm_lqi_t *lq;
5330Sstevel@tonic-gate 				for (lq = uqi->sm_lqs; lq->sm_nlqi;
534*7656SSherry.Moore@Sun.COM 				    lq = lq->sm_nlqi) {
5350Sstevel@tonic-gate 				}
5360Sstevel@tonic-gate 				lq->sm_nlqi = plqi;
5370Sstevel@tonic-gate 			} else
5380Sstevel@tonic-gate 				uqi->sm_lqs = plqi;
5390Sstevel@tonic-gate #endif
5400Sstevel@tonic-gate 			uqi->sm_nlqs++;
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 			(void) ttymux_device_init(plqi);
5430Sstevel@tonic-gate 
5440Sstevel@tonic-gate 			UNLOCK_UNIT(uqi);
5450Sstevel@tonic-gate 			rval = 0;
5460Sstevel@tonic-gate 			/*
5470Sstevel@tonic-gate 			 * Everything looks good so it's now ok to enable lower
5480Sstevel@tonic-gate 			 * queue processing.
5490Sstevel@tonic-gate 			 * Note the lower queue should be enabled as soon as
5500Sstevel@tonic-gate 			 * I_PLINK returns (used in sm_get_ttymodes etc).
5510Sstevel@tonic-gate 			 * Schedule ioctls to obtain the terminal settings.
5520Sstevel@tonic-gate 			 */
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 			if ((uqi->sm_flags & FULLY_OPEN) || uqi->sm_waitq)
5550Sstevel@tonic-gate 				plqi->sm_uqflags |= SM_UQVALID;
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 			qenable(SM_RQ(plqi));
5580Sstevel@tonic-gate 			if (plqi->sm_flags & (WANT_CDSTAT|WANT_TCSET)) {
5590Sstevel@tonic-gate 				/*
5600Sstevel@tonic-gate 				 * Bypass the lower half of the driver (hence
5610Sstevel@tonic-gate 				 * no qwriter) and apply the current termio
5620Sstevel@tonic-gate 				 * settings on the lower stream.
5630Sstevel@tonic-gate 				 */
5640Sstevel@tonic-gate 				UNLOCK_UNIT(plqi);
5650Sstevel@tonic-gate 				if (plqi->sm_bid) {
5660Sstevel@tonic-gate 					qunbufcall(SM_WQ(plqi), plqi->sm_bid);
5670Sstevel@tonic-gate 					plqi->sm_bid = 0;
5680Sstevel@tonic-gate 				}
5690Sstevel@tonic-gate 				/*
5700Sstevel@tonic-gate 				 * Only set cflags on the lower q if we know
5710Sstevel@tonic-gate 				 * the settings on any other lower queue.
5720Sstevel@tonic-gate 				 */
5730Sstevel@tonic-gate 				sm_issue_ioctl(plqi);
5740Sstevel@tonic-gate 				LOCK_UNIT(plqi);
5750Sstevel@tonic-gate 
5760Sstevel@tonic-gate 			}
5770Sstevel@tonic-gate 		}
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 		UNLOCK_UNIT(plqi);
5800Sstevel@tonic-gate 	}
5810Sstevel@tonic-gate 	sm_dbg('Y', ("sm_associate: rval=%d.\n", rval));
5820Sstevel@tonic-gate 	return (rval);
5830Sstevel@tonic-gate }
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate /*
5860Sstevel@tonic-gate  * Break an association between one of the driver's minor nodes and
5870Sstevel@tonic-gate  * a serial device.
5880Sstevel@tonic-gate  */
5890Sstevel@tonic-gate int
sm_disassociate(int unit,sm_lqi_t * plqi,ulong_t tag)5900Sstevel@tonic-gate sm_disassociate(int unit, sm_lqi_t *plqi, ulong_t tag)
5910Sstevel@tonic-gate {
5920Sstevel@tonic-gate 	sm_uqi_t *uqi;
5930Sstevel@tonic-gate 	int rval = 0;
5940Sstevel@tonic-gate 
5950Sstevel@tonic-gate 	sm_dbg('Y', ("sm_disassociate: link %d, unit %d: ",
5960Sstevel@tonic-gate 	    (plqi) ? plqi->sm_linkid : 0, unit));
5970Sstevel@tonic-gate 	/*
5980Sstevel@tonic-gate 	 * Check the data is valid.
5990Sstevel@tonic-gate 	 * Disassociate a lower queue with a logical unit.
6000Sstevel@tonic-gate 	 */
6010Sstevel@tonic-gate 	if (unit < 0 || unit >= NLUNITS || plqi == 0 ||
6020Sstevel@tonic-gate 	    (uqi = get_uqi(sm_ssp, unit)) == 0) {
6030Sstevel@tonic-gate 		sm_dbg('@', ("invalid: lqi=0x%p lui=0x%p", plqi, uqi));
6040Sstevel@tonic-gate 		rval = EINVAL;
6050Sstevel@tonic-gate 	} else {
6060Sstevel@tonic-gate 		LOCK_UNIT(plqi);
6070Sstevel@tonic-gate 
6080Sstevel@tonic-gate 		if (plqi->sm_uqi == NULL) {
6090Sstevel@tonic-gate 			sm_dbg('@', ("unit not associated"));
6100Sstevel@tonic-gate 			rval = EINVAL;
6110Sstevel@tonic-gate 		} else if (plqi->sm_uqi->sm_lunit != unit) {
6120Sstevel@tonic-gate 			sm_dbg('@', ("unit and linkid not related",
6130Sstevel@tonic-gate 			    plqi->sm_uqi->sm_lunit));
6140Sstevel@tonic-gate 			rval = EINVAL;
6150Sstevel@tonic-gate 		} else if (plqi->sm_tag != tag) {
6160Sstevel@tonic-gate 			sm_dbg('@',
6170Sstevel@tonic-gate 			    ("Invalid tag for TTYMUX_DISASSOC ioctl\n"));
6180Sstevel@tonic-gate 			rval = EPERM;
6190Sstevel@tonic-gate 		} else {
6200Sstevel@tonic-gate 			sm_dbg('Y', ("disassociating "));
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate 			(void) ttymux_device_fini(plqi);
6230Sstevel@tonic-gate 
6240Sstevel@tonic-gate 			/*
6250Sstevel@tonic-gate 			 * Indicate that carrier status is no
6260Sstevel@tonic-gate 			 * longer required and that the upper
6270Sstevel@tonic-gate 			 * queue should not be used by plqi
6280Sstevel@tonic-gate 			 */
6290Sstevel@tonic-gate 			plqi->sm_flags &= ~(WANT_CDSTAT|WANT_TCSET);
6300Sstevel@tonic-gate 			plqi->sm_uqflags &= ~(SM_UQVALID|SM_OBPCNDEV);
6310Sstevel@tonic-gate 			plqi->sm_ioflag = 0u;
6320Sstevel@tonic-gate 
6330Sstevel@tonic-gate 			sm_lqifree(plqi);
6340Sstevel@tonic-gate 			rval = 0;
6350Sstevel@tonic-gate 		}
6360Sstevel@tonic-gate 		UNLOCK_UNIT(plqi);
6370Sstevel@tonic-gate 	}
6380Sstevel@tonic-gate 	sm_dbg('Y', (" rval=%d.\n", rval));
6390Sstevel@tonic-gate 	return (rval);
6400Sstevel@tonic-gate 
6410Sstevel@tonic-gate }
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate /*
6440Sstevel@tonic-gate  * Streams helper routines;
6450Sstevel@tonic-gate  */
6460Sstevel@tonic-gate 
6470Sstevel@tonic-gate /*
6480Sstevel@tonic-gate  * Schedule a qbufcall for an upper queue.
6490Sstevel@tonic-gate  * Must be called within the perimiter of the parameter q.
6500Sstevel@tonic-gate  * fn must reenable the q.
6510Sstevel@tonic-gate  * Called:
6520Sstevel@tonic-gate  *	 whenever a message must be placed on multiple queues and allocb fails;
6530Sstevel@tonic-gate  */
6540Sstevel@tonic-gate static void
sm_sched_uqcb(queue_t * q,int memreq,int pri,void (* fn)())6550Sstevel@tonic-gate sm_sched_uqcb(queue_t *q, int memreq, int pri, void (*fn)())
6560Sstevel@tonic-gate {
6570Sstevel@tonic-gate 	sm_uqi_t	*uqi = q->q_ptr;
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	if (uqi->sm_ttybid != 0)
6600Sstevel@tonic-gate 		qunbufcall(q, uqi->sm_ttybid);
6610Sstevel@tonic-gate 
6620Sstevel@tonic-gate 	noenable(q);
6630Sstevel@tonic-gate 
6640Sstevel@tonic-gate 	uqi->sm_ttybid = qbufcall(q, memreq, pri, fn, uqi);
6650Sstevel@tonic-gate }
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate /*
6680Sstevel@tonic-gate  * qbufcall routine to restart the queues when memory is available.
6690Sstevel@tonic-gate  */
6700Sstevel@tonic-gate static void
sm_reenable_q(sm_uqi_t * uqi)6710Sstevel@tonic-gate sm_reenable_q(sm_uqi_t *uqi)
6720Sstevel@tonic-gate {
6730Sstevel@tonic-gate 	queue_t *wq = SM_WQ(uqi);
6740Sstevel@tonic-gate 
6750Sstevel@tonic-gate 	if ((uqi->sm_flags & SM_STOPPED) == 0) {
6760Sstevel@tonic-gate 		enableok(wq);
6770Sstevel@tonic-gate 		qenable(wq);
6780Sstevel@tonic-gate 	}
6790Sstevel@tonic-gate }
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate /*
6820Sstevel@tonic-gate  * Place a message on the write queue of each stream associated with
6830Sstevel@tonic-gate  * the given upper stream.
6840Sstevel@tonic-gate  */
6850Sstevel@tonic-gate static void
sm_senddown(sm_uqi_t * uqi)6860Sstevel@tonic-gate sm_senddown(sm_uqi_t *uqi)
6870Sstevel@tonic-gate {
6880Sstevel@tonic-gate 	sm_lqi_t *lqi;
6890Sstevel@tonic-gate 
6900Sstevel@tonic-gate 	for (lqi = uqi->sm_lqs; lqi != 0; lqi = lqi->sm_nlqi) {
6910Sstevel@tonic-gate 		if (lqi->sm_mp != 0) {
6920Sstevel@tonic-gate 			putnext(SM_WQ(lqi), lqi->sm_mp);
6930Sstevel@tonic-gate 			lqi->sm_mp = 0;
6940Sstevel@tonic-gate 		}
6950Sstevel@tonic-gate 	}
6960Sstevel@tonic-gate }
6970Sstevel@tonic-gate 
6980Sstevel@tonic-gate /*
6990Sstevel@tonic-gate  * For each lower device that should receive a write message duplicate
7000Sstevel@tonic-gate  * the message block.
7010Sstevel@tonic-gate  */
7020Sstevel@tonic-gate static int
sm_dupmsg(sm_uqi_t * uqi,mblk_t * mp)7030Sstevel@tonic-gate sm_dupmsg(sm_uqi_t *uqi, mblk_t *mp)
7040Sstevel@tonic-gate {
7050Sstevel@tonic-gate 	sm_lqi_t	*lqi;
7060Sstevel@tonic-gate 	mblk_t	*origmp = mp;
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate 	for (lqi = uqi->sm_lqs; lqi != 0; lqi = lqi->sm_nlqi) {
7090Sstevel@tonic-gate 		lqi->sm_mp = 0;
7100Sstevel@tonic-gate 		if (lqi->sm_flags & WERROR_MODE) {
7110Sstevel@tonic-gate 			continue;
7120Sstevel@tonic-gate 		}
7130Sstevel@tonic-gate 		if ((lqi->sm_ioflag & (uint_t)FOROUTPUT) == 0) {
7140Sstevel@tonic-gate 			if (DB_TYPE(mp) == M_DATA)
7150Sstevel@tonic-gate 				continue;
7160Sstevel@tonic-gate 		}
7170Sstevel@tonic-gate 		if (lqi->sm_nlqi == 0) {
7180Sstevel@tonic-gate 			lqi->sm_mp = mp;
7190Sstevel@tonic-gate 			origmp = NULL;
7200Sstevel@tonic-gate 		} else if ((lqi->sm_mp = sm_copymsg(mp)) == 0) {
7210Sstevel@tonic-gate 			sm_lqi_t *flqi;
7220Sstevel@tonic-gate 
7230Sstevel@tonic-gate 			for (flqi = uqi->sm_lqs; flqi != lqi;
724*7656SSherry.Moore@Sun.COM 			    flqi = flqi->sm_nlqi) {
7250Sstevel@tonic-gate 				if (lqi->sm_mp) {
7260Sstevel@tonic-gate 				/* must have been sm_copymsg */
7270Sstevel@tonic-gate 					sm_freemsg(lqi->sm_mp);
7280Sstevel@tonic-gate 					lqi->sm_mp = 0;
7290Sstevel@tonic-gate 				}
7300Sstevel@tonic-gate 			}
7310Sstevel@tonic-gate 			return (sm_cnt_oqs(uqi) * msgdsize(mp));
7320Sstevel@tonic-gate 		}
7330Sstevel@tonic-gate 	}
7340Sstevel@tonic-gate 	if (origmp != NULL)
7350Sstevel@tonic-gate 		freemsg(origmp);
7360Sstevel@tonic-gate 	return (0);
7370Sstevel@tonic-gate }
7380Sstevel@tonic-gate 
7390Sstevel@tonic-gate /*
7400Sstevel@tonic-gate  * Return 1 if all associated lower devices have room for another message
7410Sstevel@tonic-gate  * otherwise return 0.
7420Sstevel@tonic-gate  */
7430Sstevel@tonic-gate static int
sm_cansenddown(sm_uqi_t * uqi)7440Sstevel@tonic-gate sm_cansenddown(sm_uqi_t *uqi)
7450Sstevel@tonic-gate {
7460Sstevel@tonic-gate 
7470Sstevel@tonic-gate 	register sm_lqi_t	*lqi;
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	if (uqi->sm_lqs == 0)
7500Sstevel@tonic-gate 		return (0);
7510Sstevel@tonic-gate 
7520Sstevel@tonic-gate 	for (lqi = uqi->sm_lqs; lqi != 0; lqi = lqi->sm_nlqi) {
7530Sstevel@tonic-gate 		if ((lqi->sm_flags & WERROR_MODE) == 0 &&
7540Sstevel@tonic-gate 		    canputnext(SM_WQ(lqi)) == 0)
7550Sstevel@tonic-gate 			return (0);
7560Sstevel@tonic-gate 	}
7570Sstevel@tonic-gate 	return (1);
7580Sstevel@tonic-gate }
7590Sstevel@tonic-gate 
7600Sstevel@tonic-gate /*
7610Sstevel@tonic-gate  * Put a message down all associated lower queues.
7620Sstevel@tonic-gate  * Return 1 if the q function was called.
7630Sstevel@tonic-gate  */
7640Sstevel@tonic-gate static int
sm_putqs(queue_t * q,mblk_t * mp,int (* qfn)())7650Sstevel@tonic-gate sm_putqs(queue_t *q, mblk_t *mp, int (*qfn)())
7660Sstevel@tonic-gate {
7670Sstevel@tonic-gate 	register sm_uqi_t *uqi = (sm_uqi_t *)q->q_ptr;
7680Sstevel@tonic-gate 	register int memreq;
7690Sstevel@tonic-gate 	int pri = (DB_TYPE(mp) < QPCTL) ? BPRI_MED : BPRI_HI;
7700Sstevel@tonic-gate 	int rval = 0;
7710Sstevel@tonic-gate 
7720Sstevel@tonic-gate 	if (uqi->sm_lqs == 0 || (uqi->sm_flags & WERROR_MODE)) {
7730Sstevel@tonic-gate 
7740Sstevel@tonic-gate 		sm_dbg('Q', ("sm_putqs: freeing (0x%p 0x%p).\n", uqi->sm_lqs,
775*7656SSherry.Moore@Sun.COM 		    uqi->sm_flags));
7760Sstevel@tonic-gate 		freemsg(mp);
7770Sstevel@tonic-gate 	} else if (pri != BPRI_HI && sm_cansenddown(uqi) == 0) {
7780Sstevel@tonic-gate 		/* a lower q is flow controlled */
7790Sstevel@tonic-gate 		(void) qfn(q, mp);
7800Sstevel@tonic-gate 		rval = 1;
7810Sstevel@tonic-gate 	} else if ((memreq = sm_dupmsg(uqi, mp)) == 0) {
7820Sstevel@tonic-gate 
7830Sstevel@tonic-gate 		sm_senddown(uqi);
7840Sstevel@tonic-gate 
7850Sstevel@tonic-gate 	} else {
7860Sstevel@tonic-gate 		sm_log("sm_putqs: msg 0x%x - can't alloc %d bytes (pri %d).\n",
787*7656SSherry.Moore@Sun.COM 		    DB_TYPE(mp), memreq, pri);
7880Sstevel@tonic-gate 		sm_sched_uqcb(q, memreq, pri, sm_reenable_q);
7890Sstevel@tonic-gate 
7900Sstevel@tonic-gate 		(void) qfn(q, mp);
7910Sstevel@tonic-gate 		rval = 1;
7920Sstevel@tonic-gate 
7930Sstevel@tonic-gate 	}
7940Sstevel@tonic-gate 
7950Sstevel@tonic-gate 	return (rval);
7960Sstevel@tonic-gate }
7970Sstevel@tonic-gate 
7980Sstevel@tonic-gate /*
7990Sstevel@tonic-gate  * Service a streams link and unlink requests.
8000Sstevel@tonic-gate  */
8010Sstevel@tonic-gate static void
sm_link_req(queue_t * wq,mblk_t * mp)8020Sstevel@tonic-gate sm_link_req(queue_t *wq, mblk_t *mp)
8030Sstevel@tonic-gate {
8040Sstevel@tonic-gate 	struct linkblk *linkp;
8050Sstevel@tonic-gate 	int rval;
8060Sstevel@tonic-gate 	int cmd;
8070Sstevel@tonic-gate 	sm_lqi_t *plqi;
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	ASSERT(DB_TYPE(mp) == M_IOCTL);
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
8120Sstevel@tonic-gate 	switch (cmd) {
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	case I_LINK:
8150Sstevel@tonic-gate 	case I_PLINK:
8160Sstevel@tonic-gate 		sm_dbg('G', ("sm_link_req: M_IOCTL %x (I_PLINK).\n", cmd));
8170Sstevel@tonic-gate 
8180Sstevel@tonic-gate 		linkp = (struct linkblk *)mp->b_cont->b_rptr;
8190Sstevel@tonic-gate 
8200Sstevel@tonic-gate 		/*
8210Sstevel@tonic-gate 		 * 1.	Sanity check the link block.
8220Sstevel@tonic-gate 		 * 2.	Validate that the queue is not already linked
8230Sstevel@tonic-gate 		 *		(and resources available).
8240Sstevel@tonic-gate 		 * 3.	Validate that the lower queue is not associated with
8250Sstevel@tonic-gate 		 *		a logical unit.
8260Sstevel@tonic-gate 		 * 4.	Remember that this lower queue is linked to the driver.
8270Sstevel@tonic-gate 		 */
8280Sstevel@tonic-gate 		if ((linkp == NULL) || (MBLKL(mp) < sizeof (*linkp)) ||
829*7656SSherry.Moore@Sun.COM 		    linkp->l_qbot == NULL) {
8300Sstevel@tonic-gate 			sm_dbg('I', ("sm_link_req: invalid link block.\n"));
8310Sstevel@tonic-gate 			rval = EINVAL;
8320Sstevel@tonic-gate 		} else if ((plqi = get_lqi_byq(linkp->l_qbot)) == 0) {
8330Sstevel@tonic-gate 			sm_dbg('I', ("sm_link_req: out of resources.\n"));
8340Sstevel@tonic-gate 			rval = EBUSY; /* out of resources */
8350Sstevel@tonic-gate 		} else if (plqi->sm_uqi) {
8360Sstevel@tonic-gate 			UNLOCK_UNIT(plqi); /* was aquired by get_lqi_byq */
8370Sstevel@tonic-gate 			sm_dbg('I', ("sm_link_req: already associated.\n"));
8380Sstevel@tonic-gate 			rval = EBUSY; /* already linked */
8390Sstevel@tonic-gate 		} else {
8400Sstevel@tonic-gate 			SM_WQ(plqi) = linkp->l_qbot;
8410Sstevel@tonic-gate 			SM_RQ(plqi)	= OTHERQ(linkp->l_qbot);
8420Sstevel@tonic-gate 
8430Sstevel@tonic-gate 			linkp->l_qbot->q_ptr =
844*7656SSherry.Moore@Sun.COM 			    OTHERQ(linkp->l_qbot)->q_ptr = plqi;
8450Sstevel@tonic-gate 			plqi->sm_linkid = linkp->l_index;
8460Sstevel@tonic-gate 			UNLOCK_UNIT(plqi); /* was aquired by get_lqi_byq */
8470Sstevel@tonic-gate 
8480Sstevel@tonic-gate 			sm_dbg('H', ("sm_link_req: linkid = %d.\n",
849*7656SSherry.Moore@Sun.COM 			    linkp->l_index));
8500Sstevel@tonic-gate 
8510Sstevel@tonic-gate 			sm_setdip(linkp->l_qbot, plqi);
8520Sstevel@tonic-gate 			plqi->sm_ttycommon->t_flags = 0;
8530Sstevel@tonic-gate 			plqi->sm_ttycommon->t_cflag = 0;
8540Sstevel@tonic-gate 			plqi->sm_mbits = 0;
8550Sstevel@tonic-gate 			(void) ttymux_device_init(plqi);
8560Sstevel@tonic-gate 			rval = 0;
8570Sstevel@tonic-gate 		}
8580Sstevel@tonic-gate 
8590Sstevel@tonic-gate 		break;
8600Sstevel@tonic-gate 
8610Sstevel@tonic-gate 	case I_UNLINK:
8620Sstevel@tonic-gate 	case I_PUNLINK:
8630Sstevel@tonic-gate 		sm_dbg('G', ("sm_link_req: M_IOCTL (I_PUNLINK).\n"));
8640Sstevel@tonic-gate 
8650Sstevel@tonic-gate 		linkp = (struct linkblk *)mp->b_cont->b_rptr;
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate 		if ((linkp == NULL) ||
868*7656SSherry.Moore@Sun.COM 		    (MBLKL(mp) < sizeof (*linkp)) ||
869*7656SSherry.Moore@Sun.COM 		    linkp->l_qbot == NULL) {
8700Sstevel@tonic-gate 			rval = EINVAL;
8710Sstevel@tonic-gate 		} else if ((plqi = get_lqi_byid(linkp->l_index)) == 0) {
8720Sstevel@tonic-gate 			rval = EINVAL;
8730Sstevel@tonic-gate 		} else {
8740Sstevel@tonic-gate 			sm_uqi_t *uqi;
8750Sstevel@tonic-gate 			int werrmode;
8760Sstevel@tonic-gate 
8770Sstevel@tonic-gate 			/*
8780Sstevel@tonic-gate 			 * Mark the lower q as invalid.
8790Sstevel@tonic-gate 			 */
8800Sstevel@tonic-gate 			sm_dbg('G', ("I_PUNLINK: freeing link %d\n",
881*7656SSherry.Moore@Sun.COM 			    linkp->l_index));
8820Sstevel@tonic-gate 
8830Sstevel@tonic-gate 			if (plqi->sm_bid) {
8840Sstevel@tonic-gate 				qunbufcall(SM_RQ(plqi), plqi->sm_bid);
8850Sstevel@tonic-gate 				plqi->sm_bid = 0;
8860Sstevel@tonic-gate 			}
8870Sstevel@tonic-gate 			if (plqi->sm_ttybid) {
8880Sstevel@tonic-gate 				qunbufcall(SM_RQ(plqi), plqi->sm_ttybid);
8890Sstevel@tonic-gate 				plqi->sm_ttybid = 0;
8900Sstevel@tonic-gate 			}
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 			uqi = plqi->sm_uqi;
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate 
8950Sstevel@tonic-gate 			(void) ttymux_device_fini(plqi);
8960Sstevel@tonic-gate 
8970Sstevel@tonic-gate 			if (uqi)
8980Sstevel@tonic-gate 				(void) sm_disassociate(uqi->sm_lunit,
899*7656SSherry.Moore@Sun.COM 				    plqi, plqi->sm_tag);
9000Sstevel@tonic-gate 
9010Sstevel@tonic-gate 			LOCK_UNIT(plqi);
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 			plqi->sm_piocid = 0;
9040Sstevel@tonic-gate 
9050Sstevel@tonic-gate 			werrmode = (plqi->sm_flags & (WERROR_MODE|HANGUP_MODE))
9060Sstevel@tonic-gate 			    ? 1 : 0;
9070Sstevel@tonic-gate 
9080Sstevel@tonic-gate 			plqi->sm_mbits = 0;
9090Sstevel@tonic-gate 			plqi->sm_flags = 0;
9100Sstevel@tonic-gate 
9110Sstevel@tonic-gate 			ttycommon_close(plqi->sm_ttycommon);
9120Sstevel@tonic-gate 			/* SM_RQ(plqi) = SM_WQ(plqi) = 0; */
9130Sstevel@tonic-gate 			plqi->sm_ttycommon->t_flags = 0;
9140Sstevel@tonic-gate 			plqi->sm_ttycommon->t_cflag = 0;
9150Sstevel@tonic-gate 			plqi->sm_ttycommon->t_iflag = 0;
9160Sstevel@tonic-gate 			plqi->sm_linkid = 0;
9170Sstevel@tonic-gate 			plqi->sm_dev = NODEV;
9180Sstevel@tonic-gate 			plqi->sm_hadkadbchar = 0;
9190Sstevel@tonic-gate 			plqi->sm_nachar = sm_ssp->sm_abs;
9200Sstevel@tonic-gate 
9210Sstevel@tonic-gate 			UNLOCK_UNIT(plqi);
9220Sstevel@tonic-gate 			if (uqi &&
9230Sstevel@tonic-gate 			    werrmode &&
9240Sstevel@tonic-gate 			    (uqi->sm_flags & FULLY_OPEN) &&
9250Sstevel@tonic-gate 			    sm_uwq_error(uqi) &&
9260Sstevel@tonic-gate 			    putnextctl(SM_RQ(uqi), M_HANGUP) == 0) {
9270Sstevel@tonic-gate 				sm_log("sm_link_req: putnextctl(M_HANGUP)"
928*7656SSherry.Moore@Sun.COM 				    " failed.\n");
9290Sstevel@tonic-gate 			}
9300Sstevel@tonic-gate 
9310Sstevel@tonic-gate 			rval = 0;
9320Sstevel@tonic-gate 		}
9330Sstevel@tonic-gate 
9340Sstevel@tonic-gate 		break;
9350Sstevel@tonic-gate 	default:
9360Sstevel@tonic-gate 		rval = EINVAL;
9370Sstevel@tonic-gate 	}
9382319Sja97890 	if (rval != 0)
9392319Sja97890 		miocnak(wq, mp, 0, rval);
9402319Sja97890 	else
9412319Sja97890 		miocack(wq, mp, 0, 0);
9420Sstevel@tonic-gate }
9430Sstevel@tonic-gate 
9440Sstevel@tonic-gate static int
sm_getiocinfo(mblk_t * mp,struct sm_iocinfo * info)9450Sstevel@tonic-gate sm_getiocinfo(mblk_t *mp, struct sm_iocinfo *info)
9460Sstevel@tonic-gate {
9470Sstevel@tonic-gate 	switch (DB_TYPE(mp)) {
9480Sstevel@tonic-gate 	case M_COPYOUT:
9490Sstevel@tonic-gate 		info->sm_id = ((struct copyreq *)mp->b_rptr)->cq_id;
9500Sstevel@tonic-gate 		info->sm_cmd = ((struct copyreq *)mp->b_rptr)->cq_cmd;
9510Sstevel@tonic-gate 		info->sm_data = (((struct copyreq *)mp->b_rptr)->cq_size &&
9520Sstevel@tonic-gate 		    mp->b_cont) ? (void *)mp->b_cont->b_rptr : 0;
9530Sstevel@tonic-gate 		break;
9540Sstevel@tonic-gate 	case M_COPYIN:
9550Sstevel@tonic-gate 		info->sm_id = ((struct copyresp *)mp->b_rptr)->cp_id;
9560Sstevel@tonic-gate 		info->sm_cmd = ((struct copyresp *)mp->b_rptr)->cp_cmd;
9570Sstevel@tonic-gate 		info->sm_data = 0;
9580Sstevel@tonic-gate 		break;
9590Sstevel@tonic-gate 	case M_IOCACK:
9600Sstevel@tonic-gate 		info->sm_id = ((struct iocblk *)mp->b_rptr)->ioc_id;
9610Sstevel@tonic-gate 		info->sm_cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
9620Sstevel@tonic-gate 		/* the se driver has bug so we cannot use ioc_count */
9630Sstevel@tonic-gate 		info->sm_data = (((struct iocblk *)mp->b_rptr)->
964*7656SSherry.Moore@Sun.COM 		    ioc_error == 0 && mp->b_cont) ?
965*7656SSherry.Moore@Sun.COM 		    (void *)mp->b_cont->b_rptr : 0;
9660Sstevel@tonic-gate 		break;
9670Sstevel@tonic-gate 	case M_IOCNAK:
9680Sstevel@tonic-gate 		info->sm_id = ((struct iocblk *)mp->b_rptr)->ioc_id;
9690Sstevel@tonic-gate 		info->sm_cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
9700Sstevel@tonic-gate 		info->sm_data = 0;
9710Sstevel@tonic-gate 		break;
9720Sstevel@tonic-gate 	case M_IOCDATA:
9730Sstevel@tonic-gate 		info->sm_id = ((struct copyresp *)mp->b_rptr)->cp_id;
9740Sstevel@tonic-gate 		info->sm_cmd = ((struct copyresp *)mp->b_rptr)->cp_cmd;
9750Sstevel@tonic-gate 		info->sm_data = (((struct copyresp *)mp->b_rptr)->
976*7656SSherry.Moore@Sun.COM 		    cp_rval == 0 && mp->b_cont) ?
977*7656SSherry.Moore@Sun.COM 		    (void *)mp->b_cont->b_rptr : 0;
9780Sstevel@tonic-gate 		break;
9790Sstevel@tonic-gate 	case M_IOCTL:
9800Sstevel@tonic-gate 		info->sm_id = ((struct iocblk *)mp->b_rptr)->ioc_id;
9810Sstevel@tonic-gate 		info->sm_cmd = ((struct iocblk *)mp->b_rptr)->ioc_cmd;
9820Sstevel@tonic-gate 		info->sm_data = 0;
9830Sstevel@tonic-gate 		break;
9840Sstevel@tonic-gate 	default:
9850Sstevel@tonic-gate 		return (EINVAL);
9860Sstevel@tonic-gate 	}
9870Sstevel@tonic-gate 	return (0);
9880Sstevel@tonic-gate }
9890Sstevel@tonic-gate 
9900Sstevel@tonic-gate /*
9910Sstevel@tonic-gate  * Record the termio settings that have been set on the upper stream
9920Sstevel@tonic-gate  */
9930Sstevel@tonic-gate static int
sm_update_ttyinfo(mblk_t * mp,sm_uqi_t * uqi)9940Sstevel@tonic-gate sm_update_ttyinfo(mblk_t *mp, sm_uqi_t *uqi)
9950Sstevel@tonic-gate {
9960Sstevel@tonic-gate 	int err;
9970Sstevel@tonic-gate 	struct sm_iocinfo info;
9980Sstevel@tonic-gate 
9990Sstevel@tonic-gate 	if ((err = sm_getiocinfo(mp, &info)) != 0)
10000Sstevel@tonic-gate 		return (err);
10010Sstevel@tonic-gate 
10020Sstevel@tonic-gate 	switch (info.sm_cmd) {
10030Sstevel@tonic-gate 	case TIOCSPPS:
10040Sstevel@tonic-gate 	case TIOCGPPS:
10050Sstevel@tonic-gate 	case TIOCGPPSEV:
10060Sstevel@tonic-gate 		return (ENOTSUP);
10070Sstevel@tonic-gate 	case TIOCGWINSZ:
10080Sstevel@tonic-gate 	case TIOCSWINSZ:
10090Sstevel@tonic-gate 		break;
10100Sstevel@tonic-gate 	case TCSBRK:
10110Sstevel@tonic-gate 	case TIOCSBRK:
10120Sstevel@tonic-gate 	case TIOCCBRK:
10130Sstevel@tonic-gate 		break;
10140Sstevel@tonic-gate 	case TCSETSF:
10150Sstevel@tonic-gate 		uqi->sm_flags |= FLUSHR_PEND;
10160Sstevel@tonic-gate 		sm_dbg('I', ("TCSETSF: FLUSH is pending\n"));
10170Sstevel@tonic-gate 		/*FALLTHROUGH*/
10180Sstevel@tonic-gate 	case TCSETSW:
10190Sstevel@tonic-gate 	case TCSETS:
10200Sstevel@tonic-gate 	case TCGETS:
10210Sstevel@tonic-gate 		if (info.sm_data != 0) {
10220Sstevel@tonic-gate 			((struct termios *)info.sm_data)->c_cflag &=
10230Sstevel@tonic-gate 			    (tcflag_t)(~uqi->sm_cmask);
10240Sstevel@tonic-gate 			uqi->sm_ttycommon->t_cflag =
10250Sstevel@tonic-gate 			    ((struct termios *)info.sm_data)->c_cflag;
10260Sstevel@tonic-gate 		}
10270Sstevel@tonic-gate 		break;
10280Sstevel@tonic-gate 	case TCSETAF:
10290Sstevel@tonic-gate 		sm_dbg('I', ("TCSETAF: FLUSH is pending\n"));
10300Sstevel@tonic-gate 		uqi->sm_flags |= FLUSHR_PEND;
10310Sstevel@tonic-gate 		/*FALLTHROUGH*/
10320Sstevel@tonic-gate 	case TCSETAW:
10330Sstevel@tonic-gate 	case TCSETA:
10340Sstevel@tonic-gate 	case TCGETA:
10350Sstevel@tonic-gate 		if (info.sm_data != 0) {
10360Sstevel@tonic-gate 			((struct termio *)info.sm_data)->c_cflag &=
10370Sstevel@tonic-gate 			    (tcflag_t)(~uqi->sm_cmask);
10380Sstevel@tonic-gate 			uqi->sm_ttycommon->t_cflag =
10390Sstevel@tonic-gate 			    (tcflag_t)((struct termio *)info.sm_data)->c_cflag;
10400Sstevel@tonic-gate 		}
10410Sstevel@tonic-gate 		break;
10420Sstevel@tonic-gate 	case TIOCSSOFTCAR:
10430Sstevel@tonic-gate 	case TIOCGSOFTCAR:
10440Sstevel@tonic-gate 		if (info.sm_data != 0) {
10450Sstevel@tonic-gate 			if (*(int *)info.sm_data == 1)
10460Sstevel@tonic-gate 				uqi->sm_ttycommon->t_flags |= TS_SOFTCAR;
10470Sstevel@tonic-gate 			else
10480Sstevel@tonic-gate 				uqi->sm_ttycommon->t_flags &= ~TS_SOFTCAR;
10490Sstevel@tonic-gate 		}
10500Sstevel@tonic-gate 		break;
10510Sstevel@tonic-gate 	case TIOCMSET:
10520Sstevel@tonic-gate 	case TIOCMGET:
10530Sstevel@tonic-gate 		if (info.sm_data != 0)
10540Sstevel@tonic-gate 			uqi->sm_mbits = *(int *)info.sm_data;
10550Sstevel@tonic-gate 		break;
10560Sstevel@tonic-gate 	case TIOCMBIS:
10570Sstevel@tonic-gate 		if (info.sm_data != 0)
10580Sstevel@tonic-gate 			uqi->sm_mbits |= *(int *)info.sm_data;
10590Sstevel@tonic-gate 		break;
10600Sstevel@tonic-gate 	case TIOCMBIC:
10610Sstevel@tonic-gate 		if (info.sm_data != 0)
10620Sstevel@tonic-gate 			uqi->sm_mbits &= ~(*(int *)info.sm_data);
10630Sstevel@tonic-gate 		break;
10640Sstevel@tonic-gate 	default:
10650Sstevel@tonic-gate 		return (EINVAL);
10660Sstevel@tonic-gate 		/* NOTREACHED */
10670Sstevel@tonic-gate 	} /* end switch cmd */
10680Sstevel@tonic-gate 
10690Sstevel@tonic-gate 	if ((uqi->sm_mbits & TIOCM_CD) ||
1070*7656SSherry.Moore@Sun.COM 	    (uqi->sm_ttycommon->t_flags & TS_SOFTCAR) ||
1071*7656SSherry.Moore@Sun.COM 	    (uqi->sm_ttycommon->t_cflag & CLOCAL))
10720Sstevel@tonic-gate 		uqi->sm_flags |= SM_CARON;
10730Sstevel@tonic-gate 	else
10740Sstevel@tonic-gate 		uqi->sm_flags &= ~SM_CARON;
10750Sstevel@tonic-gate 
10760Sstevel@tonic-gate 	return (0);
10770Sstevel@tonic-gate }
10780Sstevel@tonic-gate 
10790Sstevel@tonic-gate /*
10800Sstevel@tonic-gate  * SECTION
10810Sstevel@tonic-gate  * STREAM's interface to the OS.
10820Sstevel@tonic-gate  * Routines directly callable from the OS.
10830Sstevel@tonic-gate  */
10840Sstevel@tonic-gate 
10850Sstevel@tonic-gate /*
10860Sstevel@tonic-gate  * Processes high priority messages comming from modules above the
10870Sstevel@tonic-gate  * multiplexor.
10880Sstevel@tonic-gate  * Return 1 if the queue was disabled.
10890Sstevel@tonic-gate  */
10900Sstevel@tonic-gate static int
sm_hp_uwput(queue_t * wq,mblk_t * mp)10910Sstevel@tonic-gate sm_hp_uwput(queue_t *wq, mblk_t *mp)
10920Sstevel@tonic-gate {
10930Sstevel@tonic-gate 	sm_uqi_t	*uqi = (sm_uqi_t *)(wq->q_ptr);
10940Sstevel@tonic-gate 	int	rval = 0;
10950Sstevel@tonic-gate 	sm_lqi_t	*plqi;
10960Sstevel@tonic-gate 	int	msgtype = DB_TYPE(mp);
10970Sstevel@tonic-gate 
10980Sstevel@tonic-gate 	switch (msgtype) {
10990Sstevel@tonic-gate 
11000Sstevel@tonic-gate 	case M_FLUSH:
11010Sstevel@tonic-gate 		/*
11020Sstevel@tonic-gate 		 * How to flush the bottom half:
11030Sstevel@tonic-gate 		 * putctl1(SM_WQ(plqi), *mp->b_rptr)
11040Sstevel@tonic-gate 		 * will work on the bottom half but if FLUSHR is set
11050Sstevel@tonic-gate 		 * when is the right time to flush the upper read queue.
11060Sstevel@tonic-gate 		 *
11070Sstevel@tonic-gate 		 * Could set uqi->sm_flags & WANT_FLUSH but then what happens
11080Sstevel@tonic-gate 		 * if FLUSHR is set and the driver sends up a FLUSHR
11090Sstevel@tonic-gate 		 * before it handles the current FLUSHR request
11100Sstevel@tonic-gate 		 * (if only there was an id for the message that could
11110Sstevel@tonic-gate 		 * be matched when it returns back from the drivers.
11120Sstevel@tonic-gate 		 *
11130Sstevel@tonic-gate 		 * Thus I'm going by the book - the bottom half acts like
11140Sstevel@tonic-gate 		 * a stream head and turns around FLUSHW back down to
11150Sstevel@tonic-gate 		 * the driver (see lrput). The upper half acts like a
11160Sstevel@tonic-gate 		 * driver and turns around FLUSHR:
11170Sstevel@tonic-gate 		 */
11180Sstevel@tonic-gate 
11190Sstevel@tonic-gate 		sm_dbg('I', ("sm_hp_uwput: FLUSH request 0x%x\n", *mp->b_rptr));
11200Sstevel@tonic-gate 		/* flush the upper write queue */
11210Sstevel@tonic-gate 		if (*mp->b_rptr & FLUSHW)
11220Sstevel@tonic-gate 			flushq(wq, FLUSHDATA);
11230Sstevel@tonic-gate 
11240Sstevel@tonic-gate 		/*
11250Sstevel@tonic-gate 		 * flush each associated lower write queue
11260Sstevel@tonic-gate 		 * and pass down the driver (ignore the FLUSHR and deal with
11270Sstevel@tonic-gate 		 * it when it comes back up the read side.
11280Sstevel@tonic-gate 		 */
11290Sstevel@tonic-gate 		for (plqi = uqi->sm_lqs; plqi != 0; plqi = plqi->sm_nlqi) {
11300Sstevel@tonic-gate 			if ((plqi->sm_flags & WERROR_MODE) == 0 &&
1131*7656SSherry.Moore@Sun.COM 			    SM_WQ(plqi)) {
11320Sstevel@tonic-gate 				sm_dbg('I', ("flush lq 0x%p\n", SM_WQ(plqi)));
11330Sstevel@tonic-gate 				if (*mp->b_rptr & FLUSHW)
11340Sstevel@tonic-gate 					flushq(SM_WQ(plqi), FLUSHDATA);
11350Sstevel@tonic-gate 				(void) putnextctl1(SM_WQ(plqi), M_FLUSH,
11360Sstevel@tonic-gate 				    *mp->b_rptr);
11370Sstevel@tonic-gate 			}
11380Sstevel@tonic-gate 		}
11390Sstevel@tonic-gate 		break;
11400Sstevel@tonic-gate 
11410Sstevel@tonic-gate 	case M_STARTI:
11420Sstevel@tonic-gate 		for (plqi = uqi->sm_lqs; plqi != 0; plqi = plqi->sm_nlqi) {
11430Sstevel@tonic-gate 			plqi->sm_flags &= ~SM_ISTOPPED;
11440Sstevel@tonic-gate 			if ((plqi->sm_flags & WERROR_MODE) == 0)
11450Sstevel@tonic-gate 				(void) putnextctl(SM_WQ(plqi), msgtype);
11460Sstevel@tonic-gate 		}
11470Sstevel@tonic-gate 		break;
11480Sstevel@tonic-gate 
11490Sstevel@tonic-gate 	case M_STOPI:
11500Sstevel@tonic-gate 		for (plqi = uqi->sm_lqs; plqi != 0; plqi = plqi->sm_nlqi) {
11510Sstevel@tonic-gate 			plqi->sm_flags |= SM_ISTOPPED;
11520Sstevel@tonic-gate 			if ((plqi->sm_flags & WERROR_MODE) == 0)
11530Sstevel@tonic-gate 				(void) putnextctl(SM_WQ(plqi), msgtype);
11540Sstevel@tonic-gate 		}
11550Sstevel@tonic-gate 		break;
11560Sstevel@tonic-gate 
11570Sstevel@tonic-gate 	case M_STOP:	/* must never be queued */
11580Sstevel@tonic-gate 		uqi->sm_flags |= SM_STOPPED;
11590Sstevel@tonic-gate 		noenable(wq);
11600Sstevel@tonic-gate 		for (plqi = uqi->sm_lqs; plqi != 0; plqi = plqi->sm_nlqi)
11610Sstevel@tonic-gate 			if ((plqi->sm_flags & WERROR_MODE) == 0)
11620Sstevel@tonic-gate 				(void) putnextctl(SM_WQ(plqi), msgtype);
11630Sstevel@tonic-gate 
11640Sstevel@tonic-gate 		rval = 1;
11650Sstevel@tonic-gate 		break;
11660Sstevel@tonic-gate 
11670Sstevel@tonic-gate 	case M_START:	/* never be queued */
11680Sstevel@tonic-gate 		uqi->sm_flags &= ~SM_STOPPED;
11690Sstevel@tonic-gate 		enableok(wq);
11700Sstevel@tonic-gate 		qenable(wq);
11710Sstevel@tonic-gate 		for (plqi = uqi->sm_lqs; plqi != 0; plqi = plqi->sm_nlqi)
11720Sstevel@tonic-gate 			if ((plqi->sm_flags & WERROR_MODE) == 0)
11730Sstevel@tonic-gate 				(void) putnextctl(SM_WQ(plqi), msgtype);
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate 		break;
11760Sstevel@tonic-gate 
11770Sstevel@tonic-gate 	case M_PCSIG:
11780Sstevel@tonic-gate 	case M_COPYOUT:
11790Sstevel@tonic-gate 	case M_COPYIN:
11800Sstevel@tonic-gate 	case M_IOCACK:
11810Sstevel@tonic-gate 	case M_IOCNAK:
11820Sstevel@tonic-gate 		/* Wrong direction for message */
11830Sstevel@tonic-gate 		break;
11840Sstevel@tonic-gate 	case M_READ:
11850Sstevel@tonic-gate 		break;
11860Sstevel@tonic-gate 	case M_PCPROTO:
11870Sstevel@tonic-gate 	case M_PCRSE:
11880Sstevel@tonic-gate 	default:
11890Sstevel@tonic-gate 		sm_dbg('I', ("sm_hp_uwput: default case %d.\n", msgtype));
11900Sstevel@tonic-gate 		break;
11910Sstevel@tonic-gate 	} /* end switch on high pri message type */
11920Sstevel@tonic-gate 
11930Sstevel@tonic-gate 	freemsg(mp);
11940Sstevel@tonic-gate 	return (rval);
11950Sstevel@tonic-gate }
11960Sstevel@tonic-gate 
11970Sstevel@tonic-gate static int
sm_default_uwioctl(queue_t * wq,mblk_t * mp,int (* qfn)())11980Sstevel@tonic-gate sm_default_uwioctl(queue_t *wq, mblk_t *mp, int (*qfn)())
11990Sstevel@tonic-gate {
12000Sstevel@tonic-gate 	int	err;
12010Sstevel@tonic-gate 	struct iocblk	*iobp;
12020Sstevel@tonic-gate 	sm_uqi_t	*uqi;
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate 	uqi = (sm_uqi_t *)(wq->q_ptr);
12050Sstevel@tonic-gate 	iobp = (struct iocblk *)mp->b_rptr;
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 	switch (iobp->ioc_cmd) {
12080Sstevel@tonic-gate 	case TIOCEXCL:
12090Sstevel@tonic-gate 	case TIOCNXCL:
12100Sstevel@tonic-gate 	case TIOCSTI:
12112319Sja97890 		/*
12122319Sja97890 		 * The three ioctl types we support do not require any
12132319Sja97890 		 * additional allocation and should not return a pending
12142319Sja97890 		 * ioctl state. For this reason it is safe for us to ignore
12152319Sja97890 		 * the return value from ttycommon_ioctl().
12162319Sja97890 		 * Additionally, we translate any error response from
12172319Sja97890 		 * ttycommon_ioctl() into EINVAL.
12182319Sja97890 		 */
12190Sstevel@tonic-gate 		(void) ttycommon_ioctl(uqi->sm_ttycommon, wq, mp, &err);
12202319Sja97890 		if (err < 0)
12212319Sja97890 			miocnak(wq, mp, 0, EINVAL);
12222319Sja97890 		else
12232319Sja97890 			miocack(wq, mp, 0, 0);
12240Sstevel@tonic-gate 		return (0);
12250Sstevel@tonic-gate 	default:
12260Sstevel@tonic-gate 		break;
12270Sstevel@tonic-gate 	}
12282319Sja97890 	if ((err = sm_update_ttyinfo(mp, uqi)) != 0) {
12292319Sja97890 		miocnak(wq, mp, 0, err);
12300Sstevel@tonic-gate 		return (0);
12310Sstevel@tonic-gate 	}
12320Sstevel@tonic-gate 
12330Sstevel@tonic-gate 	/*
12340Sstevel@tonic-gate 	 * If uqi->sm_siocdata.sm_iocid just overwrite it since the stream
12350Sstevel@tonic-gate 	 * head will have timed it out
12360Sstevel@tonic-gate 	 */
12370Sstevel@tonic-gate 	uqi->sm_siocdata.sm_iocid = iobp->ioc_id;
12380Sstevel@tonic-gate 	uqi->sm_siocdata.sm_acked = 0;
12390Sstevel@tonic-gate 	uqi->sm_siocdata.sm_nacks = sm_good_qs(uqi);
12400Sstevel@tonic-gate 	uqi->sm_siocdata.sm_acnt = 0;
12410Sstevel@tonic-gate 	uqi->sm_siocdata.sm_policy = uqi->sm_policy;
12420Sstevel@tonic-gate 	uqi->sm_siocdata.sm_flags = 0;
12430Sstevel@tonic-gate 	sm_dbg('Z', (" want %d acks for id %d.\n",
12440Sstevel@tonic-gate 	    uqi->sm_siocdata.sm_nacks, iobp->ioc_id));
12450Sstevel@tonic-gate 
12460Sstevel@tonic-gate 	return (sm_putqs(wq, mp, qfn));
12470Sstevel@tonic-gate }
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate /*
12500Sstevel@tonic-gate  *
12510Sstevel@tonic-gate  * sm_uwput - put function for an upper STREAM write.
12520Sstevel@tonic-gate  */
12530Sstevel@tonic-gate static int
sm_uwput(queue_t * wq,mblk_t * mp)12540Sstevel@tonic-gate sm_uwput(queue_t *wq, mblk_t *mp)
12550Sstevel@tonic-gate {
12560Sstevel@tonic-gate 	sm_uqi_t		*uqi;
12570Sstevel@tonic-gate 	uchar_t		msgtype;
12580Sstevel@tonic-gate 	int		cmd;
12590Sstevel@tonic-gate 	struct iocblk	*iobp;
12600Sstevel@tonic-gate 
12610Sstevel@tonic-gate 	uqi = (sm_uqi_t *)(wq->q_ptr);
12620Sstevel@tonic-gate 	msgtype = DB_TYPE(mp);
12630Sstevel@tonic-gate 
12640Sstevel@tonic-gate 	ASSERT(uqi != 0 && sm_ssp != 0);
12650Sstevel@tonic-gate 
12660Sstevel@tonic-gate 	if (msgtype >= QPCTL && msgtype != M_IOCDATA) {
12670Sstevel@tonic-gate 		(void) sm_hp_uwput(wq, mp);
12680Sstevel@tonic-gate 		return (0);
12690Sstevel@tonic-gate 	}
12700Sstevel@tonic-gate 
12710Sstevel@tonic-gate 	switch (DB_TYPE(mp)) {
12720Sstevel@tonic-gate 	case M_DATA:
12730Sstevel@tonic-gate 	case M_DELAY:
12740Sstevel@tonic-gate 	case M_BREAK:
12750Sstevel@tonic-gate 	default:
12760Sstevel@tonic-gate 		(void) sm_putqs(wq, mp, putq);
12770Sstevel@tonic-gate 		break;
12780Sstevel@tonic-gate 
12790Sstevel@tonic-gate 	case M_CTL:
12800Sstevel@tonic-gate 		if (((struct iocblk *)mp->b_rptr)->ioc_cmd == MC_CANONQUERY) {
12810Sstevel@tonic-gate 			(void) putnextctl1(OTHERQ(wq), M_CTL, MC_NOCANON);
12820Sstevel@tonic-gate 		}
12830Sstevel@tonic-gate 		freemsg(mp);
12840Sstevel@tonic-gate 		break;
12850Sstevel@tonic-gate 	case M_IOCDATA: /* not handled as high pri because may need to putbq */
12860Sstevel@tonic-gate 		sm_dbg('M', ("sm_uwput(M_IOCDATA)\n"));
12870Sstevel@tonic-gate 		/*FALLTHROUGH*/
12880Sstevel@tonic-gate 	case M_IOCTL:
12890Sstevel@tonic-gate 		cmd = (msgtype == M_IOCDATA) ?
12900Sstevel@tonic-gate 		    ((struct copyresp *)mp->b_rptr)->cp_cmd :
12910Sstevel@tonic-gate 		    ((struct iocblk *)mp->b_rptr)->ioc_cmd;
12920Sstevel@tonic-gate 
12930Sstevel@tonic-gate 		iobp = (struct iocblk *)mp->b_rptr;
12940Sstevel@tonic-gate 		iobp->ioc_rval = 0;
12950Sstevel@tonic-gate 
12960Sstevel@tonic-gate 		sm_dbg('M', ("sm_uwput(M_IOCTL:%d)\n", cmd));
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate 		switch (cmd) {
12990Sstevel@tonic-gate 
13000Sstevel@tonic-gate 		case CONSGETABORTENABLE:
13010Sstevel@tonic-gate 			iobp->ioc_error = ttymux_abort_ioctl(mp);
13020Sstevel@tonic-gate 			DB_TYPE(mp) = iobp->ioc_error ? M_IOCNAK : M_IOCACK;
13030Sstevel@tonic-gate 			qreply(wq, mp);
13040Sstevel@tonic-gate 			break;
13050Sstevel@tonic-gate 		case CONSSETABORTENABLE:
13060Sstevel@tonic-gate 			iobp->ioc_error =
13070Sstevel@tonic-gate 			    secpolicy_sys_config(iobp->ioc_cr, B_FALSE) != 0 ?
1308*7656SSherry.Moore@Sun.COM 			    EPERM : ttymux_abort_ioctl(mp);
13090Sstevel@tonic-gate 			DB_TYPE(mp) = iobp->ioc_error ? M_IOCNAK : M_IOCACK;
13100Sstevel@tonic-gate 			qreply(wq, mp);
13110Sstevel@tonic-gate 			break;
13120Sstevel@tonic-gate 		case TTYMUX_SETABORT:
13130Sstevel@tonic-gate 			if (secpolicy_sys_config(iobp->ioc_cr, B_FALSE) != 0) {
13140Sstevel@tonic-gate 				iobp->ioc_error = EPERM;
13150Sstevel@tonic-gate 				DB_TYPE(mp) = M_IOCNAK;
13160Sstevel@tonic-gate 				qreply(wq, mp);
13170Sstevel@tonic-gate 				break;
13180Sstevel@tonic-gate 			}
13190Sstevel@tonic-gate 			/*FALLTHROUGH*/
13200Sstevel@tonic-gate 		case TTYMUX_GETABORT:
13210Sstevel@tonic-gate 		case TTYMUX_GETABORTSTR:
13220Sstevel@tonic-gate 		case TTYMUX_ASSOC:
13230Sstevel@tonic-gate 		case TTYMUX_DISASSOC:
13240Sstevel@tonic-gate 		case TTYMUX_SETCTL:
13250Sstevel@tonic-gate 		case TTYMUX_GETLINK:
13260Sstevel@tonic-gate 		case TTYMUX_CONSDEV:
13270Sstevel@tonic-gate 		case TTYMUX_GETCTL:
13280Sstevel@tonic-gate 		case TTYMUX_LIST:
13290Sstevel@tonic-gate 			(void) sm_ioctl_cmd(uqi, mp);
13300Sstevel@tonic-gate 			qreply(wq, mp);
13310Sstevel@tonic-gate 			break;
13320Sstevel@tonic-gate 		case I_LINK:
13330Sstevel@tonic-gate 		case I_PLINK:
13340Sstevel@tonic-gate 		case I_UNLINK:
13350Sstevel@tonic-gate 		case I_PUNLINK:
13360Sstevel@tonic-gate 			qwriter(wq, mp, sm_link_req, PERIM_OUTER);
13370Sstevel@tonic-gate 			break;
13380Sstevel@tonic-gate 		case TCSETSW:
13390Sstevel@tonic-gate 		case TCSETSF:
13400Sstevel@tonic-gate 		case TCSETAW:
13410Sstevel@tonic-gate 		case TCSETAF:
13420Sstevel@tonic-gate 		case TCSBRK:
13430Sstevel@tonic-gate 			if (wq->q_first) {
13440Sstevel@tonic-gate 				sm_dbg('A', ("sm_uwput: TCSET-> on srv q.\n"));
13450Sstevel@tonic-gate 				/* keep message order intact */
13460Sstevel@tonic-gate 				(void) putq(wq, mp);
13470Sstevel@tonic-gate 				break;
13480Sstevel@tonic-gate 			}
13490Sstevel@tonic-gate 			/*FALLTHROUGH*/
13500Sstevel@tonic-gate 		default:
13510Sstevel@tonic-gate 			(void) sm_default_uwioctl(wq, mp, putq);
13520Sstevel@tonic-gate 			break;
13530Sstevel@tonic-gate 		}
13540Sstevel@tonic-gate 
13550Sstevel@tonic-gate 		break; /* M_IOCTL */
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate 	} /* end switch on message type */
13580Sstevel@tonic-gate 
13590Sstevel@tonic-gate 	return (0);
13600Sstevel@tonic-gate }
13610Sstevel@tonic-gate 
13620Sstevel@tonic-gate /*
13630Sstevel@tonic-gate  * sm_uwsrv - service function for an upper STREAM write.
13640Sstevel@tonic-gate  * 'sm_uwsrv' takes a q parameter.	The q parameter specifies the queue
13650Sstevel@tonic-gate  * which is to be serviced.	This function reads the messages which are on
13660Sstevel@tonic-gate  * this service queue and passes them to the appropriate lower driver queue.
13670Sstevel@tonic-gate  */
13680Sstevel@tonic-gate static int
sm_uwsrv(queue_t * q)13690Sstevel@tonic-gate sm_uwsrv(queue_t *q)
13700Sstevel@tonic-gate {
13710Sstevel@tonic-gate 	mblk_t	*mp;
13720Sstevel@tonic-gate 	sm_uqi_t	*uqi = (sm_uqi_t *)(q->q_ptr);
13730Sstevel@tonic-gate 	int		msgtype;
13740Sstevel@tonic-gate 
13750Sstevel@tonic-gate 	ASSERT(q == SM_WQ(uqi));
13760Sstevel@tonic-gate 
13770Sstevel@tonic-gate 	/*
13780Sstevel@tonic-gate 	 * Empty the queue unless explicitly stopped.
13790Sstevel@tonic-gate 	 */
13800Sstevel@tonic-gate 	while (mp = getq(q)) {
13810Sstevel@tonic-gate 		msgtype = DB_TYPE(mp);
13820Sstevel@tonic-gate 
13830Sstevel@tonic-gate 		if (msgtype >= QPCTL && msgtype != M_IOCDATA)
13840Sstevel@tonic-gate 			if (sm_hp_uwput(q, mp)) {
13850Sstevel@tonic-gate 				sm_dbg('T', ("sm_uwsrv: flowcontrolled.\n"));
13860Sstevel@tonic-gate 				break; /* indicates that the is disabled */
13870Sstevel@tonic-gate 			}
13880Sstevel@tonic-gate 			else
13890Sstevel@tonic-gate 				continue;
13900Sstevel@tonic-gate 
13910Sstevel@tonic-gate 		if (uqi->sm_flags & SM_STOPPED) {
13920Sstevel@tonic-gate 			(void) putbq(q, mp);
13930Sstevel@tonic-gate 			sm_dbg('T', ("sm_uwsrv: SM_STOPPED.\n"));
13940Sstevel@tonic-gate 			break;
13950Sstevel@tonic-gate 		}
13960Sstevel@tonic-gate 
13970Sstevel@tonic-gate 		/*
13980Sstevel@tonic-gate 		 * Read any ttycommon data that may
13990Sstevel@tonic-gate 		 * change (TS_SOFTCAR, CREAD, etc.).
14000Sstevel@tonic-gate 		 */
14010Sstevel@tonic-gate 		switch (DB_TYPE(mp)) {
14020Sstevel@tonic-gate 		case M_IOCTL:
14030Sstevel@tonic-gate 		case M_IOCDATA:
14040Sstevel@tonic-gate 			if (sm_default_uwioctl(q, mp, putbq))
14050Sstevel@tonic-gate 				return (0);
14060Sstevel@tonic-gate 			break;
14070Sstevel@tonic-gate 
14080Sstevel@tonic-gate 		default:
14090Sstevel@tonic-gate 			if (sm_putqs(q, mp, putbq))
14100Sstevel@tonic-gate 				return (0);
14110Sstevel@tonic-gate 		}
14120Sstevel@tonic-gate 	}
14130Sstevel@tonic-gate 	return (0);
14140Sstevel@tonic-gate }
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate /*
14170Sstevel@tonic-gate  * Lower write side service routine used for backenabling upstream
14180Sstevel@tonic-gate  * flow control.
14190Sstevel@tonic-gate  */
14200Sstevel@tonic-gate static int
sm_lwsrv(queue_t * q)14210Sstevel@tonic-gate sm_lwsrv(queue_t *q)
14220Sstevel@tonic-gate {
14230Sstevel@tonic-gate 	sm_lqi_t *lqi = (sm_lqi_t *)q->q_ptr;
14240Sstevel@tonic-gate 	queue_t *uwq;
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate 	LOCK_UNIT(lqi);
14270Sstevel@tonic-gate 	if (lqi->sm_uqflags & SM_UQVALID) {
14280Sstevel@tonic-gate 		/*
14290Sstevel@tonic-gate 		 * It's safe to lock uqi since lwsrv runs asynchronously
14300Sstevel@tonic-gate 		 * with the upper write routines so this cannot be an
14310Sstevel@tonic-gate 		 * upper half thread. While holding the lqi lock and
14320Sstevel@tonic-gate 		 * if SM_UQVALID is set we are guaranteed that
14330Sstevel@tonic-gate 		 * lqi->sm_uqi will be valid.
14340Sstevel@tonic-gate 		 */
14350Sstevel@tonic-gate 		sm_dbg('I', ("sm_lwsrv: re-enabling upper queue.\n"));
14360Sstevel@tonic-gate 
14370Sstevel@tonic-gate 		uwq = SM_WQ(lqi->sm_uqi);
14380Sstevel@tonic-gate 		UNLOCK_UNIT(lqi);
14390Sstevel@tonic-gate 		qenable(uwq);
14400Sstevel@tonic-gate 	} else  {
14410Sstevel@tonic-gate 		UNLOCK_UNIT(lqi);
14420Sstevel@tonic-gate 	}
14430Sstevel@tonic-gate 	return (0);
14440Sstevel@tonic-gate }
14450Sstevel@tonic-gate 
14460Sstevel@tonic-gate /*
14470Sstevel@tonic-gate  * Upper read queue ioctl response handler for messages
14480Sstevel@tonic-gate  * passed from the lower half of the driver.
14490Sstevel@tonic-gate  */
14500Sstevel@tonic-gate static int
sm_uriocack(queue_t * rq,mblk_t * mp)14510Sstevel@tonic-gate sm_uriocack(queue_t *rq, mblk_t *mp)
14520Sstevel@tonic-gate {
14530Sstevel@tonic-gate 	sm_uqi_t		*uqi = (sm_uqi_t *)rq->q_ptr;
14540Sstevel@tonic-gate 	int		err, flag;
14550Sstevel@tonic-gate 	sm_iocdata_t	*iodp;
14560Sstevel@tonic-gate 	struct sm_iocinfo	info;
14570Sstevel@tonic-gate 
14580Sstevel@tonic-gate 	if ((err = sm_getiocinfo(mp, &info)) != 0) {
14590Sstevel@tonic-gate 		sm_dbg('I', ("Unknown ioctl response\n"));
14600Sstevel@tonic-gate 		return (err);
14610Sstevel@tonic-gate 	}
14620Sstevel@tonic-gate 
14630Sstevel@tonic-gate 	if (info.sm_id == uqi->sm_piocdata.sm_iocid) {
14640Sstevel@tonic-gate 		iodp = &uqi->sm_piocdata;
14650Sstevel@tonic-gate 	} else if (info.sm_id == uqi->sm_siocdata.sm_iocid) {
14660Sstevel@tonic-gate 		iodp = &uqi->sm_siocdata;
14670Sstevel@tonic-gate 	} else {
14680Sstevel@tonic-gate 		sm_log("Unexpected ioctl response\n");
14690Sstevel@tonic-gate 		sm_dbg('I', ("Unexpected ioctl response (id %d)\n",
1470*7656SSherry.Moore@Sun.COM 		    info.sm_id));
14710Sstevel@tonic-gate 
14720Sstevel@tonic-gate 		/*
14730Sstevel@tonic-gate 		 * If the response is sent up it will result in
14740Sstevel@tonic-gate 		 * duplicate ioctl responses. The ioctl has probably been
14750Sstevel@tonic-gate 		 * timed out by the stream head so dispose of the response
14760Sstevel@tonic-gate 		 * (since it has arrived too late.
14770Sstevel@tonic-gate 		 */
14780Sstevel@tonic-gate 		goto out;
14790Sstevel@tonic-gate 	}
14800Sstevel@tonic-gate 
14810Sstevel@tonic-gate 	flag = SM_COPYIN;
14820Sstevel@tonic-gate 
14830Sstevel@tonic-gate 	switch (DB_TYPE(mp)) {
14840Sstevel@tonic-gate 	case M_COPYOUT:
14850Sstevel@tonic-gate 		flag = SM_COPYOUT;
14860Sstevel@tonic-gate 		/*FALLTHRU*/
14870Sstevel@tonic-gate 	case M_COPYIN:
14880Sstevel@tonic-gate 		if (iodp->sm_flags & flag)
14890Sstevel@tonic-gate 			goto out;
14900Sstevel@tonic-gate 		iodp->sm_flags |= flag;
14910Sstevel@tonic-gate 
14920Sstevel@tonic-gate 		break;
14930Sstevel@tonic-gate 	case M_IOCACK:
14940Sstevel@tonic-gate 		iodp->sm_ackcnt += 1;
14950Sstevel@tonic-gate 		iodp->sm_acnt += 1;
14960Sstevel@tonic-gate 		if (iodp->sm_policy == FIRSTACK) {
14970Sstevel@tonic-gate 			if (iodp->sm_acnt == iodp->sm_nacks)
14980Sstevel@tonic-gate 				iodp->sm_iocid = 0;
14990Sstevel@tonic-gate 			if (iodp->sm_acnt == 1)
15000Sstevel@tonic-gate 				iodp->sm_acked = 1;
15010Sstevel@tonic-gate 			else
15020Sstevel@tonic-gate 				goto out;
15030Sstevel@tonic-gate 		} else {
15040Sstevel@tonic-gate 			if (iodp->sm_acnt == iodp->sm_nacks) {
15050Sstevel@tonic-gate 				iodp->sm_iocid = 0;
15060Sstevel@tonic-gate 				iodp->sm_acked = 1;
15070Sstevel@tonic-gate 			} else
15080Sstevel@tonic-gate 				goto out;
15090Sstevel@tonic-gate 		}
15100Sstevel@tonic-gate 		break;
15110Sstevel@tonic-gate 	case M_IOCNAK:
15120Sstevel@tonic-gate 		iodp->sm_nakcnt += 1;
15130Sstevel@tonic-gate 		iodp->sm_acnt += 1;
15140Sstevel@tonic-gate 		if (iodp->sm_acnt == iodp->sm_nacks) {
15150Sstevel@tonic-gate 			iodp->sm_iocid = 0;
15160Sstevel@tonic-gate 			if (iodp->sm_acked == 0) {
15170Sstevel@tonic-gate 				iodp->sm_acked = 1;
15180Sstevel@tonic-gate 				break;
15190Sstevel@tonic-gate 			}
15200Sstevel@tonic-gate 		}
15210Sstevel@tonic-gate 		goto out;
15220Sstevel@tonic-gate 	default:
15230Sstevel@tonic-gate 		goto out;
15240Sstevel@tonic-gate 	}
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate 	/*
15270Sstevel@tonic-gate 	 * Merge the tty settings each of the associated lower streams.
15280Sstevel@tonic-gate 	 */
15290Sstevel@tonic-gate 	if (info.sm_data)
15300Sstevel@tonic-gate 		(void) sm_update_ttyinfo(mp, uqi);
15310Sstevel@tonic-gate 
15320Sstevel@tonic-gate 	if (iodp == &uqi->sm_piocdata) {
15330Sstevel@tonic-gate 		if (iodp->sm_iocid == 0) {
15340Sstevel@tonic-gate 			uqi->sm_flags &= ~SM_IOCPENDING;
15350Sstevel@tonic-gate 		}
15360Sstevel@tonic-gate 	} else {
15370Sstevel@tonic-gate 		sm_dbg('I', ("sm_uriocack: forwarding response for %d.\n",
15380Sstevel@tonic-gate 		    info.sm_id));
15390Sstevel@tonic-gate 		putnext(rq, mp);
15400Sstevel@tonic-gate 		return (0);
15410Sstevel@tonic-gate 	}
15420Sstevel@tonic-gate out:
15430Sstevel@tonic-gate 	sm_dbg('I', ("sm_uriocack: freeing response for %d.\n", info.sm_id));
15440Sstevel@tonic-gate 	freemsg(mp);
15450Sstevel@tonic-gate 	return (0);
15460Sstevel@tonic-gate }
15470Sstevel@tonic-gate 
15480Sstevel@tonic-gate /*
15490Sstevel@tonic-gate  * Transfer a message from the lower read side of the multiplexer onto
15500Sstevel@tonic-gate  * the associated upper stream.
15510Sstevel@tonic-gate  */
15520Sstevel@tonic-gate static int
sm_ursendup(queue_t * q,mblk_t * mp)15530Sstevel@tonic-gate sm_ursendup(queue_t *q, mblk_t *mp)
15540Sstevel@tonic-gate {
15550Sstevel@tonic-gate 	sm_uqi_t	*uqi = (sm_uqi_t *)q->q_ptr;
15560Sstevel@tonic-gate 
15570Sstevel@tonic-gate 	if (!canputnext(q) && DB_TYPE(mp) < QPCTL) {
15580Sstevel@tonic-gate 		sm_dbg('I', ("sm_ursendup: flow controlled.\n"));
15590Sstevel@tonic-gate 		return (1);
15600Sstevel@tonic-gate 	}
15610Sstevel@tonic-gate 
15620Sstevel@tonic-gate 	switch (DB_TYPE(mp)) {
15630Sstevel@tonic-gate 	case M_COPYIN:
15640Sstevel@tonic-gate 	case M_COPYOUT:
15650Sstevel@tonic-gate 	case M_IOCACK:
15660Sstevel@tonic-gate 	case M_IOCNAK:
15670Sstevel@tonic-gate 		(void) sm_uriocack(q, mp);
15680Sstevel@tonic-gate 		break;
15690Sstevel@tonic-gate 	case M_HANGUP:
15700Sstevel@tonic-gate 		if (sm_uwq_error(uqi)) {
15710Sstevel@tonic-gate 			/* there are no usable lower q's */
15720Sstevel@tonic-gate 			uqi->sm_flags &= ~SM_CARON;
15730Sstevel@tonic-gate 			putnext(q, mp);
15740Sstevel@tonic-gate 		} else {
15750Sstevel@tonic-gate 			/* there are still usable q's - don't send up */
15760Sstevel@tonic-gate 			freemsg(mp);
15770Sstevel@tonic-gate 		}
15780Sstevel@tonic-gate 		break;
15790Sstevel@tonic-gate 	case M_ERROR:
15800Sstevel@tonic-gate 		if (sm_uwq_error(uqi)) {
15810Sstevel@tonic-gate 			/* there are no usable lower q's */
15820Sstevel@tonic-gate 			uqi->sm_flags &= ~SM_CARON;
15830Sstevel@tonic-gate 			putnext(q, mp);
15840Sstevel@tonic-gate 		} else if (*mp->b_rptr == NOERROR) {
15850Sstevel@tonic-gate 			/* the error has cleared */
15860Sstevel@tonic-gate 			uqi->sm_flags &= ~ERROR_MODE;
15870Sstevel@tonic-gate 			putnext(q, mp);
15880Sstevel@tonic-gate 		} else {
15890Sstevel@tonic-gate 			/* there are still usable q's - don't send up */
15900Sstevel@tonic-gate 			freemsg(mp);
15910Sstevel@tonic-gate 		}
15920Sstevel@tonic-gate 		break;
15930Sstevel@tonic-gate 	case M_FLUSH:
15940Sstevel@tonic-gate 		flushq(q, FLUSHDATA);
15950Sstevel@tonic-gate 		putnext(q, mp);	/* time to use FLUSHR_PEND flag */
15960Sstevel@tonic-gate 		break;
15970Sstevel@tonic-gate 	case M_CTL:
15980Sstevel@tonic-gate 		/* wrong direction - must have come from sm_close */
15990Sstevel@tonic-gate 		uqi->sm_flags |= SM_CLOSE;
16000Sstevel@tonic-gate 		sm_dbg('I', ("sm_ursrv: had SM_CLOSE.\n"));
16010Sstevel@tonic-gate 		freemsg(mp);
16020Sstevel@tonic-gate 		break;
16030Sstevel@tonic-gate 	case M_UNHANGUP:
16040Sstevel@tonic-gate 		/* just pass them all up - they're harmless */
16050Sstevel@tonic-gate 		uqi->sm_flags |= SM_CARON;
16060Sstevel@tonic-gate 		/* FALLTHROUGH */
16070Sstevel@tonic-gate 	default:
16080Sstevel@tonic-gate 		putnext(q, mp);
16090Sstevel@tonic-gate 		break;
16100Sstevel@tonic-gate 	}
16110Sstevel@tonic-gate 
16120Sstevel@tonic-gate 	return (0);
16130Sstevel@tonic-gate }
16140Sstevel@tonic-gate 
16150Sstevel@tonic-gate /*
16160Sstevel@tonic-gate  * sm_urput - put function for a lower STREAM read.
16170Sstevel@tonic-gate  */
16180Sstevel@tonic-gate static int
sm_urput(queue_t * q,mblk_t * mp)16190Sstevel@tonic-gate sm_urput(queue_t *q, mblk_t *mp)
16200Sstevel@tonic-gate {
16210Sstevel@tonic-gate 	if (sm_ursendup(q, mp) != 0)
16220Sstevel@tonic-gate 		(void) putq(q, mp);
16230Sstevel@tonic-gate 
16240Sstevel@tonic-gate 	return (0);
16250Sstevel@tonic-gate }
16260Sstevel@tonic-gate 
16270Sstevel@tonic-gate /*
16280Sstevel@tonic-gate  * Upper read side service routine.
16290Sstevel@tonic-gate  * Read side needs to be fast so only check for duplicate M_IOCTL acks.
16300Sstevel@tonic-gate  */
16310Sstevel@tonic-gate static int
sm_ursrv(queue_t * q)16320Sstevel@tonic-gate sm_ursrv(queue_t *q)
16330Sstevel@tonic-gate {
16340Sstevel@tonic-gate 	sm_uqi_t	*uqi = (sm_uqi_t *)q->q_ptr;
16350Sstevel@tonic-gate 	mblk_t	*mp;
16360Sstevel@tonic-gate 	int	flags = uqi->sm_flags;
16370Sstevel@tonic-gate 
16380Sstevel@tonic-gate 	while ((mp = getq(q))) {
16390Sstevel@tonic-gate 		if (sm_ursendup(q, mp) != 0) {
16400Sstevel@tonic-gate 			sm_dbg('I', ("sm_ursrv: flow controlled.\n"));
16410Sstevel@tonic-gate 			(void) putbq(q, mp);
16420Sstevel@tonic-gate 			uqi->sm_flags |= WANT_RENB;
16430Sstevel@tonic-gate 			break;
16440Sstevel@tonic-gate 		}
16450Sstevel@tonic-gate 	}
16460Sstevel@tonic-gate 
16470Sstevel@tonic-gate 	/*
16480Sstevel@tonic-gate 	 * If the q service was called because it was no longer
16490Sstevel@tonic-gate 	 * flow controled then enable each of the driver queues.
16500Sstevel@tonic-gate 	 */
16510Sstevel@tonic-gate 	if ((flags & WANT_RENB) && !(uqi->sm_flags & WANT_RENB)) {
16520Sstevel@tonic-gate 		sm_lqi_t *lqi;
16530Sstevel@tonic-gate 		queue_t *drq; /* read q of linked driver */
16540Sstevel@tonic-gate 
16550Sstevel@tonic-gate 		uqi->sm_flags &= ~WANT_RENB;
16560Sstevel@tonic-gate 		for (lqi = uqi->sm_lqs; lqi != 0; lqi = lqi->sm_nlqi) {
16570Sstevel@tonic-gate 			drq = SM_RQ(lqi)->q_next;
16580Sstevel@tonic-gate 			if (drq && drq->q_first != 0)
16590Sstevel@tonic-gate 				qenable(drq);
16600Sstevel@tonic-gate 		}
16610Sstevel@tonic-gate 	}
16620Sstevel@tonic-gate 
16630Sstevel@tonic-gate 	return (0);
16640Sstevel@tonic-gate }
16650Sstevel@tonic-gate 
16660Sstevel@tonic-gate /*
16670Sstevel@tonic-gate  * Check a message sent from a linked device for abort requests and
16680Sstevel@tonic-gate  * for flow control.
16690Sstevel@tonic-gate  */
16700Sstevel@tonic-gate static int
sm_lrmsg_check(queue_t * q,mblk_t * mp)16710Sstevel@tonic-gate sm_lrmsg_check(queue_t *q, mblk_t *mp)
16720Sstevel@tonic-gate {
16730Sstevel@tonic-gate 	sm_lqi_t	*lqi	= (sm_lqi_t *)q->q_ptr;
16740Sstevel@tonic-gate 
16750Sstevel@tonic-gate 	switch (DB_TYPE(mp)) {
16760Sstevel@tonic-gate 	case M_DATA:
16770Sstevel@tonic-gate 		LOCK_UNIT(lqi);
16780Sstevel@tonic-gate 		/*
16790Sstevel@tonic-gate 		 * check for abort - only allow abort on I/O consoles
16800Sstevel@tonic-gate 		 * known to OBP -
16810Sstevel@tonic-gate 		 * fix it when we do polled io
16820Sstevel@tonic-gate 		 */
16830Sstevel@tonic-gate 		if ((lqi->sm_ioflag & (uint_t)FORINPUT) == 0) {
16840Sstevel@tonic-gate 			freemsg(mp);
16850Sstevel@tonic-gate 			UNLOCK_UNIT(lqi);
16860Sstevel@tonic-gate 			return (1);
16870Sstevel@tonic-gate 		}
16880Sstevel@tonic-gate 		if ((lqi->sm_uqflags & SM_OBPCNDEV) &&
16890Sstevel@tonic-gate 		    lqi->sm_ctrla_abort_on &&
16900Sstevel@tonic-gate 		    abort_enable == KIOCABORTALTERNATE) {
16910Sstevel@tonic-gate 
16920Sstevel@tonic-gate 			uchar_t		*rxc;
16930Sstevel@tonic-gate 			boolean_t	aborted = B_FALSE;
16940Sstevel@tonic-gate 
16950Sstevel@tonic-gate 			for (rxc = mp->b_rptr;
16960Sstevel@tonic-gate 			    rxc != mp->b_wptr;
16970Sstevel@tonic-gate 			    rxc++)
16980Sstevel@tonic-gate 
16990Sstevel@tonic-gate 				if (*rxc == *lqi->sm_nachar) {
17000Sstevel@tonic-gate 					lqi->sm_nachar++;
17010Sstevel@tonic-gate 					if (*lqi->sm_nachar == '\0') {
17020Sstevel@tonic-gate 						abort_sequence_enter(
1703*7656SSherry.Moore@Sun.COM 						    (char *)NULL);
17040Sstevel@tonic-gate 						lqi->sm_nachar = sm_ssp->sm_abs;
17050Sstevel@tonic-gate 						aborted = B_TRUE;
17060Sstevel@tonic-gate 					}
17070Sstevel@tonic-gate 				} else
17080Sstevel@tonic-gate 					lqi->sm_nachar = (*rxc == *sm_ssp->
1709*7656SSherry.Moore@Sun.COM 					    sm_abs) ?
1710*7656SSherry.Moore@Sun.COM 					    sm_ssp->
1711*7656SSherry.Moore@Sun.COM 					    sm_abs + 1 :
1712*7656SSherry.Moore@Sun.COM 					    sm_ssp->sm_abs;
17130Sstevel@tonic-gate 
17140Sstevel@tonic-gate 			if (aborted) {
17150Sstevel@tonic-gate 				freemsg(mp);
17160Sstevel@tonic-gate 				UNLOCK_UNIT(lqi);
17170Sstevel@tonic-gate 				return (1);
17180Sstevel@tonic-gate 			}
17190Sstevel@tonic-gate 		}
17200Sstevel@tonic-gate 		UNLOCK_UNIT(lqi);
17210Sstevel@tonic-gate 		break;
17220Sstevel@tonic-gate 	case M_BREAK:	/* we'll eventually see this as a flush */
17230Sstevel@tonic-gate 		LOCK_UNIT(lqi);
17240Sstevel@tonic-gate 		/*
17250Sstevel@tonic-gate 		 * Only allow abort on OBP devices. When polled I/O is
17260Sstevel@tonic-gate 		 * supported allow abort on any console device.
17270Sstevel@tonic-gate 		 * Parity errors are reported upstream as breaks so
17280Sstevel@tonic-gate 		 * ensure that there is no data in the message before
17290Sstevel@tonic-gate 		 * deciding whether to abort.
17300Sstevel@tonic-gate 		 */
17310Sstevel@tonic-gate 		if ((lqi->sm_uqflags & SM_OBPCNDEV) && /* console stream */
17320Sstevel@tonic-gate 		    (mp->b_wptr - mp->b_rptr == 0 &&
17330Sstevel@tonic-gate 		    msgdsize(mp) == 0)) {	/* not due to parity */
17340Sstevel@tonic-gate 
17350Sstevel@tonic-gate 			if (lqi->sm_break_abort_on &&
17360Sstevel@tonic-gate 			    abort_enable != KIOCABORTALTERNATE)
17370Sstevel@tonic-gate 				abort_sequence_enter((char *)NULL);
17380Sstevel@tonic-gate 
17390Sstevel@tonic-gate 			freemsg(mp);
17400Sstevel@tonic-gate 			UNLOCK_UNIT(lqi);
17410Sstevel@tonic-gate 			return (1);
17420Sstevel@tonic-gate 		} else {
17430Sstevel@tonic-gate 			UNLOCK_UNIT(lqi);
17440Sstevel@tonic-gate 		}
17450Sstevel@tonic-gate 		break;
17460Sstevel@tonic-gate 	default:
17470Sstevel@tonic-gate 		break;
17480Sstevel@tonic-gate 	}
17490Sstevel@tonic-gate 
17500Sstevel@tonic-gate 	if (DB_TYPE(mp) >= QPCTL)
17510Sstevel@tonic-gate 		return (0);
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate 	LOCK_UNIT(lqi); /* lock out the upper half */
17540Sstevel@tonic-gate 	if ((lqi->sm_uqflags & SM_UQVALID) && SM_RQ(lqi->sm_uqi)) {
17550Sstevel@tonic-gate 		UNLOCK_UNIT(lqi);
17560Sstevel@tonic-gate 		if (!canput(SM_RQ(lqi->sm_uqi))) {
17570Sstevel@tonic-gate 			sm_dbg('I', ("sm_lrmsg_check: flow controlled.\n"));
17580Sstevel@tonic-gate 			(void) putq(q, mp);
17590Sstevel@tonic-gate 			return (1);
17600Sstevel@tonic-gate 		}
17610Sstevel@tonic-gate 	} else {
17620Sstevel@tonic-gate 		UNLOCK_UNIT(lqi);
17630Sstevel@tonic-gate 	}
17640Sstevel@tonic-gate 
17650Sstevel@tonic-gate 	return (0);
17660Sstevel@tonic-gate }
17670Sstevel@tonic-gate 
17680Sstevel@tonic-gate /*
17690Sstevel@tonic-gate  * sm_sendup - deliver a message to the upper read side of the multiplexer
17700Sstevel@tonic-gate  */
17710Sstevel@tonic-gate static int
sm_sendup(queue_t * q,mblk_t * mp)17720Sstevel@tonic-gate sm_sendup(queue_t *q, mblk_t *mp)
17730Sstevel@tonic-gate {
17740Sstevel@tonic-gate 	sm_lqi_t	*lqi	= (sm_lqi_t *)q->q_ptr;
17750Sstevel@tonic-gate 
17760Sstevel@tonic-gate 	if (sm_ssp == NULL) {
17770Sstevel@tonic-gate 		freemsg(mp);
17780Sstevel@tonic-gate 		return (0);
17790Sstevel@tonic-gate 	}
17800Sstevel@tonic-gate 
17810Sstevel@tonic-gate 	/*
17820Sstevel@tonic-gate 	 * Check for CD status change messages from driver.
17830Sstevel@tonic-gate 	 * (Remark: this is an se driver thread running at soft interupt
17840Sstevel@tonic-gate 	 * priority and the waiters are in user context).
17850Sstevel@tonic-gate 	 */
17860Sstevel@tonic-gate 	switch (DB_TYPE(mp)) {
17870Sstevel@tonic-gate 	case M_DATA:
17880Sstevel@tonic-gate 	case M_BREAK:	/* we'll eventually see this as a flush */
17890Sstevel@tonic-gate 		break;
17900Sstevel@tonic-gate 
17910Sstevel@tonic-gate 	/* high priority messages */
17920Sstevel@tonic-gate 	case M_IOCACK:
17930Sstevel@tonic-gate 	case M_IOCNAK:
17940Sstevel@tonic-gate 		if ((lqi->sm_flags & SM_IOCPENDING) && lqi->sm_piocid ==
17950Sstevel@tonic-gate 		    ((struct iocblk *)mp->b_rptr)->ioc_id) {
17960Sstevel@tonic-gate 			freemsg(mp);
17970Sstevel@tonic-gate 			lqi->sm_flags &= ~SM_IOCPENDING;
17980Sstevel@tonic-gate 			sm_issue_ioctl(lqi);
17990Sstevel@tonic-gate 			return (0);
18000Sstevel@tonic-gate 		}
18010Sstevel@tonic-gate 		break;
18020Sstevel@tonic-gate 	case M_UNHANGUP:
18030Sstevel@tonic-gate 		/*
18040Sstevel@tonic-gate 		 * If the driver can send an M_UNHANGUP it must be able to
18050Sstevel@tonic-gate 		 * accept messages from above (ie clear WERROR_MODE if set).
18060Sstevel@tonic-gate 		 */
18070Sstevel@tonic-gate 		sm_dbg('E', ("lrput: M_UNHANGUP\n"));
18080Sstevel@tonic-gate 		lqi->sm_mbits |= TIOCM_CD;
18090Sstevel@tonic-gate 		lqi->sm_flags &= ~(WERROR_MODE|HANGUP_MODE);
18100Sstevel@tonic-gate 
18110Sstevel@tonic-gate 		break;
18120Sstevel@tonic-gate 
18130Sstevel@tonic-gate 	case M_HANGUP:
18140Sstevel@tonic-gate 		sm_dbg('E', ("lrput: MHANGUP\n"));
18150Sstevel@tonic-gate 		lqi->sm_mbits &= ~TIOCM_CD;
18160Sstevel@tonic-gate 		lqi->sm_flags |= (WERROR_MODE|HANGUP_MODE);
18170Sstevel@tonic-gate 		break;
18180Sstevel@tonic-gate 
18190Sstevel@tonic-gate 	case M_ERROR:
18200Sstevel@tonic-gate 
18210Sstevel@tonic-gate 		sm_dbg('E', ("lrput: MERROR\n"));
18220Sstevel@tonic-gate 		/*
18230Sstevel@tonic-gate 		 * Tell the driver to flush rd/wr queue if its read/write error.
18240Sstevel@tonic-gate 		 * if its a read/write error flush rq/wq (type in first bytes).
18250Sstevel@tonic-gate 		 */
18260Sstevel@tonic-gate 		if ((mp->b_wptr - mp->b_rptr) == 2) {
18270Sstevel@tonic-gate 			uchar_t	rw = 0;
18280Sstevel@tonic-gate 
18290Sstevel@tonic-gate 			if (*mp->b_rptr == NOERROR) {
18300Sstevel@tonic-gate 				/* not in error anymore */
18310Sstevel@tonic-gate 				lqi->sm_flags &= ~ERROR_MODE;
18320Sstevel@tonic-gate 				lqi->sm_flags |= WANT_CD;
18330Sstevel@tonic-gate 			} else {
18340Sstevel@tonic-gate 				if (*mp->b_rptr != 0) {
18350Sstevel@tonic-gate 					/* read error */
18360Sstevel@tonic-gate 					rw |= FLUSHR;
18370Sstevel@tonic-gate 					lqi->sm_flags |= RERROR_MODE;
18380Sstevel@tonic-gate 				}
18390Sstevel@tonic-gate 				mp->b_rptr++;
18400Sstevel@tonic-gate 				if (*mp->b_rptr != 0) {
18410Sstevel@tonic-gate 					/* write error */
18420Sstevel@tonic-gate 					rw |= FLUSHW;
18430Sstevel@tonic-gate 					lqi->sm_flags |= WERROR_MODE;
18440Sstevel@tonic-gate 				}
18450Sstevel@tonic-gate 
18460Sstevel@tonic-gate 				mp->b_rptr--;
18470Sstevel@tonic-gate 				/* has next driver done qprocsoff */
18480Sstevel@tonic-gate 				if (rw && OTHERQ(q)->q_next != NULL) {
18490Sstevel@tonic-gate 					(void) putnextctl1(OTHERQ(q), M_FLUSH,
1850*7656SSherry.Moore@Sun.COM 					    rw);
18510Sstevel@tonic-gate 				}
18520Sstevel@tonic-gate 			}
18530Sstevel@tonic-gate 		} else if (*mp->b_rptr != 0 && OTHERQ(q)->q_next != NULL) {
18540Sstevel@tonic-gate 			sm_dbg('E', ("lrput: old style MERROR (?)\n"));
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 			lqi->sm_flags |= (RERROR_MODE | WERROR_MODE);
18570Sstevel@tonic-gate 			(void) putnextctl1(OTHERQ(q), M_FLUSH, FLUSHRW);
18580Sstevel@tonic-gate 		}
18590Sstevel@tonic-gate 		break;
18600Sstevel@tonic-gate 
18610Sstevel@tonic-gate 	case M_PCSIG:
18620Sstevel@tonic-gate 	case M_SIG:
18630Sstevel@tonic-gate 		break;
18640Sstevel@tonic-gate 	case M_COPYOUT:
18650Sstevel@tonic-gate 	case M_COPYIN:
18660Sstevel@tonic-gate 		break;
18670Sstevel@tonic-gate 	case M_FLUSH:
18680Sstevel@tonic-gate 		/* flush the read queue and pass on up */
18690Sstevel@tonic-gate 		flushq(q, FLUSHDATA);
18700Sstevel@tonic-gate 		break;
18710Sstevel@tonic-gate 	default:
18720Sstevel@tonic-gate 		break;
18730Sstevel@tonic-gate 	}
18740Sstevel@tonic-gate 
18750Sstevel@tonic-gate 	LOCK_UNIT(lqi); /* lock out the upper half */
18760Sstevel@tonic-gate 	if (lqi->sm_uqflags & SM_UQVALID && SM_RQ(lqi->sm_uqi)) {
18770Sstevel@tonic-gate 		UNLOCK_UNIT(lqi);
18780Sstevel@tonic-gate 		(void) putq(SM_RQ(lqi->sm_uqi), mp);
18790Sstevel@tonic-gate 		return (0);
18800Sstevel@tonic-gate 	} else {
18810Sstevel@tonic-gate 		sm_dbg('I', ("sm_sendup: uq not valid\n"));
18820Sstevel@tonic-gate 		freemsg(mp);
18830Sstevel@tonic-gate 	}
18840Sstevel@tonic-gate 	UNLOCK_UNIT(lqi);
18850Sstevel@tonic-gate 
18860Sstevel@tonic-gate 	return (0);
18870Sstevel@tonic-gate }
18880Sstevel@tonic-gate 
18890Sstevel@tonic-gate /*
18900Sstevel@tonic-gate  * sm_lrput - put function for a lower STREAM read.
18910Sstevel@tonic-gate  */
18920Sstevel@tonic-gate static int
sm_lrput(queue_t * q,mblk_t * mp)18930Sstevel@tonic-gate sm_lrput(queue_t *q, mblk_t *mp)
18940Sstevel@tonic-gate {
18950Sstevel@tonic-gate 	if (sm_lrmsg_check(q, mp) == 0)
18960Sstevel@tonic-gate 		(void) sm_sendup(q, mp);
18970Sstevel@tonic-gate 	return (0);
18980Sstevel@tonic-gate }
18990Sstevel@tonic-gate 
19000Sstevel@tonic-gate /*
19010Sstevel@tonic-gate  * sm_lrsrv - service function for the lower read STREAM.
19020Sstevel@tonic-gate  */
19030Sstevel@tonic-gate static int
sm_lrsrv(queue_t * q)19040Sstevel@tonic-gate sm_lrsrv(queue_t *q)
19050Sstevel@tonic-gate {
19060Sstevel@tonic-gate 	mblk_t	*mp;
19070Sstevel@tonic-gate 
19080Sstevel@tonic-gate 	sm_dbg('I', ("sm_lrsrv: not controlled.\n"));
19090Sstevel@tonic-gate 	while (mp = getq(q))
19100Sstevel@tonic-gate 		(void) sm_sendup(q, mp);
19110Sstevel@tonic-gate 
19120Sstevel@tonic-gate 	return (0);
19130Sstevel@tonic-gate }
19140Sstevel@tonic-gate 
19150Sstevel@tonic-gate /*
19160Sstevel@tonic-gate  * Check whether a thread is allowed to open the requested device.
19170Sstevel@tonic-gate  */
19180Sstevel@tonic-gate static int
sm_ok_to_open(sm_uqi_t * uqi,int protocol,cred_t * credp,int * abort_waiters)19190Sstevel@tonic-gate sm_ok_to_open(sm_uqi_t *uqi, int protocol, cred_t *credp, int *abort_waiters)
19200Sstevel@tonic-gate {
19210Sstevel@tonic-gate 	int rval = 0;
19220Sstevel@tonic-gate 	int proto;
19230Sstevel@tonic-gate 
19240Sstevel@tonic-gate 	*abort_waiters = 0;
19250Sstevel@tonic-gate 
19260Sstevel@tonic-gate 	switch (protocol) {
19270Sstevel@tonic-gate 		case ASYNC_DEVICE: /* Standard async protocol */
19280Sstevel@tonic-gate 		if ((uqi->sm_protocol == NULL_PROTOCOL) ||
1929*7656SSherry.Moore@Sun.COM 		    (uqi->sm_protocol == ASYN_PROTOCOL)) {
19300Sstevel@tonic-gate 			/*
19310Sstevel@tonic-gate 			 * Lock out other incompatible protocol requests.
19320Sstevel@tonic-gate 			 */
19330Sstevel@tonic-gate 			proto = ASYN_PROTOCOL;
19340Sstevel@tonic-gate 			rval = 0;
19350Sstevel@tonic-gate 		} else
19360Sstevel@tonic-gate 			rval = EBUSY;
19370Sstevel@tonic-gate 		break;
19380Sstevel@tonic-gate 
19390Sstevel@tonic-gate 		case OUTLINE:	/* Outdial protocol */
19400Sstevel@tonic-gate 		if ((uqi->sm_protocol == NULL_PROTOCOL) ||
1941*7656SSherry.Moore@Sun.COM 		    (uqi->sm_protocol == OUTD_PROTOCOL)) {
19420Sstevel@tonic-gate 			proto = OUTD_PROTOCOL;
19430Sstevel@tonic-gate 			rval = 0;
19440Sstevel@tonic-gate 		} else if (uqi->sm_protocol == ASYN_PROTOCOL) {
19450Sstevel@tonic-gate 			/*
19460Sstevel@tonic-gate 			 * check for dialout request on a line that is already
19470Sstevel@tonic-gate 			 * open for dial in:
19480Sstevel@tonic-gate 			 * kick off any thread that is waiting to fully open
19490Sstevel@tonic-gate 			 */
19500Sstevel@tonic-gate 			if (uqi->sm_flags & FULLY_OPEN)
19510Sstevel@tonic-gate 				rval = EBUSY;
19520Sstevel@tonic-gate 			else {
19530Sstevel@tonic-gate 				proto = OUTD_PROTOCOL;
19540Sstevel@tonic-gate 				*abort_waiters = 1;
19550Sstevel@tonic-gate 			}
19560Sstevel@tonic-gate 		} else
19570Sstevel@tonic-gate 			rval = EBUSY;
19580Sstevel@tonic-gate 		break;
19590Sstevel@tonic-gate 		default:
19600Sstevel@tonic-gate 			rval = ENOTSUP;
19610Sstevel@tonic-gate 	}
19620Sstevel@tonic-gate 
19630Sstevel@tonic-gate 	if (rval == 0 &&
1964*7656SSherry.Moore@Sun.COM 	    (uqi->sm_ttycommon->t_flags & TS_XCLUDE) &&
1965*7656SSherry.Moore@Sun.COM 	    secpolicy_excl_open(credp) != 0) {
19660Sstevel@tonic-gate 
19670Sstevel@tonic-gate 		if (uqi->sm_flags & FULLY_OPEN) {
19680Sstevel@tonic-gate 			rval = EBUSY; /* exclusive device already open */
19690Sstevel@tonic-gate 		} else {
19700Sstevel@tonic-gate 			/* NB TS_XCLUDE cant be set during open so NOTREACHED */
19710Sstevel@tonic-gate 			/* force any waiters to yield TS_XCLUDE */
19720Sstevel@tonic-gate 			*abort_waiters = 1;
19730Sstevel@tonic-gate 		}
19740Sstevel@tonic-gate 	}
19750Sstevel@tonic-gate 
19760Sstevel@tonic-gate 	if (rval == 0)
19770Sstevel@tonic-gate 		uqi->sm_protocol = proto;
19780Sstevel@tonic-gate 
19790Sstevel@tonic-gate 	sm_dbg('A', ("ok_to_open (0x%p, %d) proto=%d rval %d (wabort=%d)",
1980*7656SSherry.Moore@Sun.COM 	    uqi, protocol, uqi->sm_protocol, rval, *abort_waiters));
19810Sstevel@tonic-gate 
19820Sstevel@tonic-gate 	return (rval);
19830Sstevel@tonic-gate }
19840Sstevel@tonic-gate 
19850Sstevel@tonic-gate /* wait for memory to become available whilst performing a qwait */
19860Sstevel@tonic-gate /*ARGSUSED*/
dummy_callback(void * arg)19870Sstevel@tonic-gate static void dummy_callback(void *arg)
19880Sstevel@tonic-gate {}
19890Sstevel@tonic-gate 
19900Sstevel@tonic-gate /* ARGSUSED */
19910Sstevel@tonic-gate static int
sm_dump_msg(queue_t * q,mblk_t * mp)19920Sstevel@tonic-gate sm_dump_msg(queue_t *q, mblk_t *mp)
19930Sstevel@tonic-gate {
19940Sstevel@tonic-gate 	freemsg(mp);
19950Sstevel@tonic-gate 	return (0);
19960Sstevel@tonic-gate }
19970Sstevel@tonic-gate 
19980Sstevel@tonic-gate /*
19990Sstevel@tonic-gate  * Wait for a message to arrive - must be called with exclusive
20000Sstevel@tonic-gate  * access at the outer perimiter.
20010Sstevel@tonic-gate  */
20020Sstevel@tonic-gate static int
sm_qwait_sig(sm_uqi_t * uqi,queue_t * q)20030Sstevel@tonic-gate sm_qwait_sig(sm_uqi_t *uqi, queue_t *q)
20040Sstevel@tonic-gate {
20050Sstevel@tonic-gate 	int err;
20060Sstevel@tonic-gate 
20070Sstevel@tonic-gate 	sm_dbg('C', ("sm_qwait_sig: waiting.\n"));
20080Sstevel@tonic-gate 
20090Sstevel@tonic-gate 	uqi->sm_waitq = q;
20100Sstevel@tonic-gate 	uqi->sm_nwaiters++;	/* required by the close routine */
20110Sstevel@tonic-gate 	err = qwait_sig(q);
20120Sstevel@tonic-gate 	if (--uqi->sm_nwaiters == 0)
20130Sstevel@tonic-gate 		uqi->sm_waitq = 0;
20140Sstevel@tonic-gate 
20150Sstevel@tonic-gate 	if (err == 0)
20160Sstevel@tonic-gate 		err = EINTR;
20170Sstevel@tonic-gate 	else if (q->q_ptr == 0) /* can happen if there are multiple waiters */
20180Sstevel@tonic-gate 		err = -1;
20190Sstevel@tonic-gate 	else if (uqi->sm_flags & SM_CLOSE) {
20200Sstevel@tonic-gate 		uqi->sm_flags &= ~SM_CLOSE;
20210Sstevel@tonic-gate 		err = 1;	/* a different protocol has closed its stream */
20220Sstevel@tonic-gate 	}
20230Sstevel@tonic-gate 	else
20240Sstevel@tonic-gate 		err = 0;	/* was worth waiting for */
20250Sstevel@tonic-gate 
20260Sstevel@tonic-gate 	sm_dbg('C', ("sm_qwait_sig: rval %d\n", err));
20270Sstevel@tonic-gate 	return (err);
20280Sstevel@tonic-gate }
20290Sstevel@tonic-gate 
20300Sstevel@tonic-gate /*
20310Sstevel@tonic-gate  * Defer the opening of one the drivers devices until the state of each
20320Sstevel@tonic-gate  * associated lower stream is known.
20330Sstevel@tonic-gate  */
20340Sstevel@tonic-gate static int
sm_defer_open(sm_uqi_t * uqi,queue_t * q)20350Sstevel@tonic-gate sm_defer_open(sm_uqi_t *uqi, queue_t *q)
20360Sstevel@tonic-gate {
20370Sstevel@tonic-gate 	uint_t cmdflags = WANT_CDSTAT;
20380Sstevel@tonic-gate 	int err, nqs;
20390Sstevel@tonic-gate 
20400Sstevel@tonic-gate 	while ((nqs = sm_good_qs(uqi)) == 0) {
20410Sstevel@tonic-gate 		sm_dbg('C', ("sm_defer_open: no good qs\n"));
20420Sstevel@tonic-gate 		if (err = sm_qwait_sig(uqi, q))
20430Sstevel@tonic-gate 			return (err);
20440Sstevel@tonic-gate 	}
20450Sstevel@tonic-gate 
20460Sstevel@tonic-gate 	while ((uqi->sm_flags & SM_CARON) == 0) {
20470Sstevel@tonic-gate 		int iocmd;
20480Sstevel@tonic-gate 		mblk_t *pioc;
20490Sstevel@tonic-gate 
20500Sstevel@tonic-gate 		sm_dbg('C', ("sm_defer_open: flags 0x%x cmdflags 0x%x\n",
2051*7656SSherry.Moore@Sun.COM 		    uqi->sm_flags, cmdflags));
20520Sstevel@tonic-gate 		if (cmdflags == 0) {
20530Sstevel@tonic-gate 			if (err = sm_qwait_sig(uqi, q))
20540Sstevel@tonic-gate 				return (err);
20550Sstevel@tonic-gate 			continue;	/* waiting for an M_UNHANGUP */
20560Sstevel@tonic-gate 		} else if (cmdflags & WANT_SC) {
20570Sstevel@tonic-gate 			cmdflags &= ~WANT_SC;
20580Sstevel@tonic-gate 			iocmd = TIOCGSOFTCAR;
20590Sstevel@tonic-gate 		} else if (cmdflags & WANT_CD) {
20600Sstevel@tonic-gate 			cmdflags &= ~WANT_CD;
20610Sstevel@tonic-gate 			iocmd = TIOCMGET;
20620Sstevel@tonic-gate 		} else if (cmdflags & WANT_CL) {
20630Sstevel@tonic-gate 			cmdflags &= ~WANT_CL;
20640Sstevel@tonic-gate 			iocmd = TCGETS;
20650Sstevel@tonic-gate 		}
20660Sstevel@tonic-gate 
20670Sstevel@tonic-gate 		if (uqi->sm_piocdata.sm_iocid == 0) {
20680Sstevel@tonic-gate 			while ((pioc = mkiocb(iocmd)) == 0) {
20690Sstevel@tonic-gate 				bufcall_id_t id =
2070*7656SSherry.Moore@Sun.COM 				    qbufcall(q, sizeof (struct iocblk),
2071*7656SSherry.Moore@Sun.COM 				    BPRI_MED, dummy_callback, 0);
20720Sstevel@tonic-gate 				if (err = sm_qwait_sig(uqi, q)) {
20730Sstevel@tonic-gate 					/* wait for the bufcall */
20740Sstevel@tonic-gate 					qunbufcall(q, id);
20750Sstevel@tonic-gate 					return (err);
20760Sstevel@tonic-gate 				}
20770Sstevel@tonic-gate 				qunbufcall(q, id);
20780Sstevel@tonic-gate 			}
20790Sstevel@tonic-gate 
20800Sstevel@tonic-gate 			uqi->sm_flags |= SM_IOCPENDING;
20810Sstevel@tonic-gate 
20820Sstevel@tonic-gate 			uqi->sm_piocdata.sm_iocid =
20830Sstevel@tonic-gate 			    ((struct iocblk *)pioc->b_rptr)->ioc_id;
20840Sstevel@tonic-gate 			uqi->sm_piocdata.sm_acked = 0;
20850Sstevel@tonic-gate 			uqi->sm_piocdata.sm_nacks = nqs;
20860Sstevel@tonic-gate 			uqi->sm_piocdata.sm_acnt = 0;
20870Sstevel@tonic-gate 			uqi->sm_piocdata.sm_ackcnt = uqi->
2088*7656SSherry.Moore@Sun.COM 			    sm_piocdata.sm_nakcnt = 0;
20890Sstevel@tonic-gate 			uqi->sm_piocdata.sm_policy = uqi->sm_policy;
20900Sstevel@tonic-gate 			uqi->sm_piocdata.sm_flags = SM_INTERNALIOC;
20910Sstevel@tonic-gate 			if (sm_putqs(WR(q), pioc, sm_dump_msg) != 0) {
20920Sstevel@tonic-gate 				uqi->sm_piocdata.sm_iocid = 0;
20930Sstevel@tonic-gate 				sm_log("sm_defer_open: bad putqs\n");
20940Sstevel@tonic-gate 				return (-1);
20950Sstevel@tonic-gate 			}
20960Sstevel@tonic-gate 		}
20970Sstevel@tonic-gate 
20980Sstevel@tonic-gate 		sm_dbg('C', ("sm_defer_open: flags 0x%x\n", uqi->sm_flags));
20990Sstevel@tonic-gate 		while ((uqi->sm_flags & SM_CARON) == 0 &&
21000Sstevel@tonic-gate 		    (uqi->sm_flags & SM_IOCPENDING) != 0)
21010Sstevel@tonic-gate 			if (err = sm_qwait_sig(uqi, q))
21020Sstevel@tonic-gate 				return (err);
21030Sstevel@tonic-gate 
21040Sstevel@tonic-gate 		sm_dbg('C', ("defer_open: uq flags 0x%x.\n", uqi->sm_flags));
21050Sstevel@tonic-gate 	}
21060Sstevel@tonic-gate 	sm_dbg('C', ("defer_open: return 0.\n"));
21070Sstevel@tonic-gate 	return (0);
21080Sstevel@tonic-gate }
21090Sstevel@tonic-gate 
21100Sstevel@tonic-gate static int
sm_open(queue_t * rq,dev_t * devp,int flag,int sflag,cred_t * credp)21110Sstevel@tonic-gate sm_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
21120Sstevel@tonic-gate {
21130Sstevel@tonic-gate 	int		ftstat;
21140Sstevel@tonic-gate 	int		unit;
21150Sstevel@tonic-gate 	int		protocol;
21160Sstevel@tonic-gate 	sm_uqi_t		*uqi;
21170Sstevel@tonic-gate 	int		abort_waiters;
21180Sstevel@tonic-gate 
21190Sstevel@tonic-gate 	if (sm_ssp == NULL)
21200Sstevel@tonic-gate 		return (ENXIO);
21210Sstevel@tonic-gate 	/*
21220Sstevel@tonic-gate 	 * sflag = 0 => streams device.
21230Sstevel@tonic-gate 	 */
21240Sstevel@tonic-gate 	if (sflag != 0 || DEV_TO_UNIT(*devp) >= NLUNITS) {
21250Sstevel@tonic-gate 		sm_dbg('C', ("open: sflag=%d or bad dev_t.\n", sflag));
21260Sstevel@tonic-gate 		return (ENXIO);
21270Sstevel@tonic-gate 	}
21280Sstevel@tonic-gate 
21290Sstevel@tonic-gate 	unit = DEV_TO_UNIT(*devp);
21300Sstevel@tonic-gate 	protocol = DEV_TO_PROTOBITS(*devp);
21310Sstevel@tonic-gate 
21320Sstevel@tonic-gate 	uqi = get_uqi(sm_ssp, unit);
21330Sstevel@tonic-gate 
21340Sstevel@tonic-gate 	sm_dbg('C', ("open(0x%p, %d, 0x%x) :- unit=%d, proto=%d, uqi=0x%p\n",
2135*7656SSherry.Moore@Sun.COM 	    rq, *devp, flag, unit, protocol, uqi));
21360Sstevel@tonic-gate 
21370Sstevel@tonic-gate 	if (uqi == 0)
21380Sstevel@tonic-gate 		return (ENXIO);
21390Sstevel@tonic-gate 
21400Sstevel@tonic-gate 	if (sm_refuse_opens && unit > smctlunit && uqi->sm_nlqs == 0)
21410Sstevel@tonic-gate 		return (ENXIO);
21420Sstevel@tonic-gate 
21430Sstevel@tonic-gate 	if (uqi->sm_flags & EXCL_OPEN && (flag & FEXCL)) {
21440Sstevel@tonic-gate 		return (EBUSY); /* device in use */
21450Sstevel@tonic-gate 	}
21460Sstevel@tonic-gate 
21470Sstevel@tonic-gate 	if ((flag & FEXCL)) {
21480Sstevel@tonic-gate 		if (secpolicy_excl_open(credp) != 0)
21490Sstevel@tonic-gate 			return (EPERM);
21500Sstevel@tonic-gate 
21510Sstevel@tonic-gate 		if ((uqi->sm_flags & FULLY_OPEN) || uqi->sm_nwaiters > 0)
21520Sstevel@tonic-gate 			return (EBUSY); /* device in use */
21530Sstevel@tonic-gate 
21540Sstevel@tonic-gate 		uqi->sm_flags |= EXCL_OPEN;
21550Sstevel@tonic-gate 	}
21560Sstevel@tonic-gate 
21570Sstevel@tonic-gate 	if (uqi->sm_protocol == NULL_PROTOCOL) {
21580Sstevel@tonic-gate 		struct termios *termiosp;
21590Sstevel@tonic-gate 		int len;
21600Sstevel@tonic-gate 
21610Sstevel@tonic-gate 		if (ddi_getlongprop(DDI_DEV_T_ANY, ddi_root_node(),
21620Sstevel@tonic-gate 		    DDI_PROP_NOTPROM, "ttymodes", (caddr_t)&termiosp, &len)
21630Sstevel@tonic-gate 		    == DDI_PROP_SUCCESS &&
21640Sstevel@tonic-gate 		    (len == sizeof (struct termios))) {
21650Sstevel@tonic-gate 
21660Sstevel@tonic-gate 			sm_dbg('C', ("open: c_cflag=0x%x\n",
21670Sstevel@tonic-gate 				termiosp->c_cflag));
21680Sstevel@tonic-gate 
21690Sstevel@tonic-gate 			uqi->sm_ttycommon->t_iflag = termiosp->c_iflag;
21700Sstevel@tonic-gate 			uqi->sm_ttycommon->t_cflag = termiosp->c_cflag;
21710Sstevel@tonic-gate 			uqi->sm_ttycommon->t_stopc = termiosp->c_cc[VSTOP];
21720Sstevel@tonic-gate 			uqi->sm_ttycommon->t_startc = termiosp->c_cc[VSTART];
21730Sstevel@tonic-gate 
21740Sstevel@tonic-gate 			/*
21750Sstevel@tonic-gate 			 * IGNBRK,BRKINT,INPCK,IXON,IXANY,IXOFF - drivers
21760Sstevel@tonic-gate 			 * PARMRK,IGNPAR,ISTRIP - how to report parity
21770Sstevel@tonic-gate 			 * INLCR,IGNCR,ICRNL,IUCLC - ldterm (sophisticated I/O)
21780Sstevel@tonic-gate 			 * IXON, IXANY, IXOFF - flow control input
21790Sstevel@tonic-gate 			 * CBAUD,CSIZE,CS5-8,CSTOPB,PARENB,PARODD,HUPCL,
21800Sstevel@tonic-gate 			 * RCV1EN,XMT1EN,LOBLK,XCLUDE,CRTSXOFF,CRTSCTS,
21810Sstevel@tonic-gate 			 * CIBAUD,PAREXT,CBAUDEXT,CIBAUDEXT,CREAD,CLOCAL
21820Sstevel@tonic-gate 			 */
21830Sstevel@tonic-gate 
21840Sstevel@tonic-gate 			kmem_free(termiosp, len);
21850Sstevel@tonic-gate 		}
21860Sstevel@tonic-gate 		else
21870Sstevel@tonic-gate 			bzero((caddr_t)uqi->sm_ttycommon,
2188*7656SSherry.Moore@Sun.COM 			    sizeof (uqi->sm_ttycommon));
21890Sstevel@tonic-gate 
21900Sstevel@tonic-gate 		if (*devp == rconsdev) {
21910Sstevel@tonic-gate 			uqi->sm_cmask = sm_cmask;
21920Sstevel@tonic-gate 			uqi->sm_ttycommon->t_flags |= TS_SOFTCAR;
21930Sstevel@tonic-gate 		} else {
21940Sstevel@tonic-gate 			uqi->sm_ttycommon->t_flags &= ~TS_SOFTCAR;
21950Sstevel@tonic-gate 		}
21960Sstevel@tonic-gate 
21970Sstevel@tonic-gate 		/*
21980Sstevel@tonic-gate 		 * Clear the default CLOCAL and TS_SOFTCAR flags since
21990Sstevel@tonic-gate 		 * they must correspond to the settings on the real devices.
22000Sstevel@tonic-gate 		 */
22010Sstevel@tonic-gate 
22020Sstevel@tonic-gate 		uqi->sm_ttycommon->t_cflag &= ~(uqi->sm_cmask|CLOCAL);
22030Sstevel@tonic-gate 		uqi->sm_mbits = 0;
22040Sstevel@tonic-gate 		uqi->sm_policy = FIRSTACK;
22050Sstevel@tonic-gate 		if (unit == 0 && sm_ssp->sm_ms == 0)
22060Sstevel@tonic-gate 			sm_ssp->sm_ms = (sm_mux_state_t *)
2207*7656SSherry.Moore@Sun.COM 			    space_fetch(TTYMUXPTR);
22080Sstevel@tonic-gate 		if (sm_ssp->sm_ms) {
22090Sstevel@tonic-gate 			if (sm_ssp->sm_ms->sm_cons_stdin.sm_dev == *devp ||
22100Sstevel@tonic-gate 			    sm_ssp->sm_ms->sm_cons_stdout.sm_dev == *devp)
22110Sstevel@tonic-gate 				sm_ssp->sm_lconsole = uqi;
22120Sstevel@tonic-gate 		}
22130Sstevel@tonic-gate 	}
22140Sstevel@tonic-gate 
22150Sstevel@tonic-gate 	/*
22160Sstevel@tonic-gate 	 * Does this thread need to wait?
22170Sstevel@tonic-gate 	 */
22180Sstevel@tonic-gate 
22190Sstevel@tonic-gate 	sm_dbg('C', ("sm_open: %d %d 0x%p 0x%x\n",
22200Sstevel@tonic-gate 	    !(flag & (FNDELAY|FNONBLOCK)), !(protocol == OUTLINE), uqi->sm_lqs,
22210Sstevel@tonic-gate 	    uqi->sm_flags));
22220Sstevel@tonic-gate 
22230Sstevel@tonic-gate tryopen:
22240Sstevel@tonic-gate 
22250Sstevel@tonic-gate 	abort_waiters = 0;
22260Sstevel@tonic-gate 	if (ftstat = sm_ok_to_open(uqi, protocol, credp, &abort_waiters)) {
22270Sstevel@tonic-gate 		sm_dbg('C', ("open failed stat=%d.\n", ftstat));
22280Sstevel@tonic-gate 
22290Sstevel@tonic-gate 		if ((uqi->sm_flags & FULLY_OPEN) == 0 && uqi->sm_nwaiters == 0)
22300Sstevel@tonic-gate 			uqi->sm_protocol = NULL_PROTOCOL;
22310Sstevel@tonic-gate 		if (flag & FEXCL)
22320Sstevel@tonic-gate 			uqi->sm_flags &= ~EXCL_OPEN;
22330Sstevel@tonic-gate 		return (ftstat);
22340Sstevel@tonic-gate 	}
22350Sstevel@tonic-gate 
22360Sstevel@tonic-gate 	if (abort_waiters) {
22370Sstevel@tonic-gate 		uqi->sm_dev = *devp;
22380Sstevel@tonic-gate 		/* different device wants to use the unit */
22390Sstevel@tonic-gate 		SM_RQ(uqi) = rq;
22400Sstevel@tonic-gate 		SM_WQ(uqi) = WR(rq);
22410Sstevel@tonic-gate 	}
22420Sstevel@tonic-gate 	if (rq->q_ptr == 0) {
22430Sstevel@tonic-gate 		sm_lqi_t *lqi;
22440Sstevel@tonic-gate 
22450Sstevel@tonic-gate 		uqi->sm_dev = *devp;
22460Sstevel@tonic-gate 		rq->q_ptr = WR(rq)->q_ptr = uqi;
22470Sstevel@tonic-gate 		SM_RQ(uqi) = rq;
22480Sstevel@tonic-gate 		SM_WQ(uqi) = WR(rq);
22490Sstevel@tonic-gate 		qprocson(rq);
22500Sstevel@tonic-gate 		for (lqi = uqi->sm_lqs; lqi != 0; lqi = lqi->sm_nlqi) {
22510Sstevel@tonic-gate 			LOCK_UNIT(lqi);
22520Sstevel@tonic-gate 			lqi->sm_uqflags |= SM_UQVALID;
22530Sstevel@tonic-gate 			UNLOCK_UNIT(lqi);
22540Sstevel@tonic-gate 		}
22550Sstevel@tonic-gate 
22560Sstevel@tonic-gate 		sm_dbg('C', ("sm_open: SM_UQVALID set on lqs.\n"));
22570Sstevel@tonic-gate 	}
22580Sstevel@tonic-gate 
22590Sstevel@tonic-gate 	if (*devp != rconsdev && BLOCKING(uqi, protocol, flag)) {
22600Sstevel@tonic-gate 
22610Sstevel@tonic-gate 		uqi->sm_flags |= WANT_CDSTAT;
22620Sstevel@tonic-gate 
22630Sstevel@tonic-gate 		do {
22640Sstevel@tonic-gate 			/*
22650Sstevel@tonic-gate 			 * Wait for notifications of changes in the CLOCAL
22660Sstevel@tonic-gate 			 * and TS_SOFTCAR flags and a TIOCM_CD flag of a
22670Sstevel@tonic-gate 			 * TIOCMGET request (come in on the write side queue).
22680Sstevel@tonic-gate 			 */
22690Sstevel@tonic-gate 
22700Sstevel@tonic-gate 			if ((ftstat = sm_defer_open(uqi, rq)) != EINTR) {
22710Sstevel@tonic-gate 				if (ftstat) {
22720Sstevel@tonic-gate 					goto tryopen;
22730Sstevel@tonic-gate 				} else {
22740Sstevel@tonic-gate 					continue;
22750Sstevel@tonic-gate 				}
22760Sstevel@tonic-gate 			}
22770Sstevel@tonic-gate 
22780Sstevel@tonic-gate 			if (uqi->sm_nwaiters == 0) {	/* clean up */
22790Sstevel@tonic-gate 				/*
22800Sstevel@tonic-gate 				 * only opens on an asynchronous
22810Sstevel@tonic-gate 				 * protocols reach here so checking
22820Sstevel@tonic-gate 				 * nwaiters == 0 is sufficient to
22830Sstevel@tonic-gate 				 * ensure that no other thread
22840Sstevel@tonic-gate 				 * is waiting on this logical unit
22850Sstevel@tonic-gate 				 */
22860Sstevel@tonic-gate 				if ((uqi->sm_flags & FULLY_OPEN) == 0) {
22870Sstevel@tonic-gate 
22880Sstevel@tonic-gate 					sm_lqi_t *lqi;
22890Sstevel@tonic-gate 
22900Sstevel@tonic-gate 					uqi->sm_dev = NODEV;
22910Sstevel@tonic-gate 					sm_dbg('C', ("sm_open FULLY_OPEN=0\n"));
22920Sstevel@tonic-gate 					for (lqi = uqi->sm_lqs; lqi != 0;
22930Sstevel@tonic-gate 					    lqi = lqi->sm_nlqi) {
22940Sstevel@tonic-gate 						LOCK_UNIT(lqi);
22950Sstevel@tonic-gate 						lqi->sm_uqflags &= ~SM_UQVALID;
22960Sstevel@tonic-gate 						UNLOCK_UNIT(lqi);
22970Sstevel@tonic-gate 					}
22980Sstevel@tonic-gate 
22990Sstevel@tonic-gate 					qprocsoff(rq);
23000Sstevel@tonic-gate 					rq->q_ptr = WR(rq)->q_ptr = 0;
23010Sstevel@tonic-gate 					SM_RQ(uqi) = 0;
23020Sstevel@tonic-gate 					SM_WQ(uqi) = 0;
23030Sstevel@tonic-gate 				}
23040Sstevel@tonic-gate 			}
23050Sstevel@tonic-gate 			if ((uqi->sm_flags & FULLY_OPEN) == 0 &&
23060Sstevel@tonic-gate 			    uqi->sm_nwaiters == 0)
23070Sstevel@tonic-gate 				uqi->sm_protocol = NULL_PROTOCOL;
23080Sstevel@tonic-gate 			if (flag & FEXCL)
23090Sstevel@tonic-gate 				uqi->sm_flags &= ~EXCL_OPEN;
23100Sstevel@tonic-gate 			sm_dbg('C', ("sm_open: done (ret %d).\n", ftstat));
23110Sstevel@tonic-gate 			return (ftstat);
23120Sstevel@tonic-gate 		} while (BLOCKING(uqi, protocol, flag));
23130Sstevel@tonic-gate 	}
23140Sstevel@tonic-gate 
23150Sstevel@tonic-gate 	uqi->sm_flags |= FULLY_OPEN;
23160Sstevel@tonic-gate 
23170Sstevel@tonic-gate 	sm_dbg('C', ("sm_open done (ret %d).\n", ftstat));
23180Sstevel@tonic-gate 	return (ftstat);
23190Sstevel@tonic-gate }
23200Sstevel@tonic-gate 
23210Sstevel@tonic-gate /*
23220Sstevel@tonic-gate  * Multiplexer device close routine.
23230Sstevel@tonic-gate  */
23240Sstevel@tonic-gate /*ARGSUSED*/
23250Sstevel@tonic-gate static int
sm_close(queue_t * rq,int flag,cred_t * credp)23260Sstevel@tonic-gate sm_close(queue_t *rq, int flag, cred_t *credp)
23270Sstevel@tonic-gate {
23280Sstevel@tonic-gate 	sm_uqi_t *uqi = (sm_uqi_t *)rq->q_ptr;
23290Sstevel@tonic-gate 	sm_lqi_t *lqi;
23300Sstevel@tonic-gate 
23310Sstevel@tonic-gate 	if (sm_ssp == NULL)
23320Sstevel@tonic-gate 		return (ENXIO);
23330Sstevel@tonic-gate 
23340Sstevel@tonic-gate 	if (uqi == NULL) {
23350Sstevel@tonic-gate 		sm_dbg('C', ("close: WARN:- q 0x%p already closed.\n", rq));
23360Sstevel@tonic-gate 		return (ENXIO);
23370Sstevel@tonic-gate 	}
23380Sstevel@tonic-gate 
23390Sstevel@tonic-gate 	sm_dbg('C', ("close: uqi=0x%p unit=%d q=0x%p)\n", uqi, uqi->sm_lunit,
2340*7656SSherry.Moore@Sun.COM 	    rq));
23410Sstevel@tonic-gate 
23420Sstevel@tonic-gate 	if (SM_RQ(uqi) != rq)
23430Sstevel@tonic-gate 		sm_dbg('C', ("sm_close: rq != current uqi queue\n"));
23440Sstevel@tonic-gate 
23450Sstevel@tonic-gate 	if (uqi->sm_ttybid) {
23460Sstevel@tonic-gate 		qunbufcall(SM_RQ(uqi), uqi->sm_ttybid);
23470Sstevel@tonic-gate 		uqi->sm_ttybid = 0;
23480Sstevel@tonic-gate 	}
23490Sstevel@tonic-gate 
23500Sstevel@tonic-gate 	/*
23510Sstevel@tonic-gate 	 * Tell all the linked queues that the upper queue has gone
23520Sstevel@tonic-gate 	 * Note close will never get called on a stream while there is a
23530Sstevel@tonic-gate 	 * thread blocked trying to open the same stream.
23540Sstevel@tonic-gate 	 * If there is a blocked open on a different stream but on
23550Sstevel@tonic-gate 	 * the same logical unit it will reset the lower queue flags.
23560Sstevel@tonic-gate 	 */
23570Sstevel@tonic-gate 	for (lqi = uqi->sm_lqs; lqi != 0; lqi = lqi->sm_nlqi) {
23580Sstevel@tonic-gate 		LOCK_UNIT(lqi);
23590Sstevel@tonic-gate 		lqi->sm_uqflags &= ~SM_UQVALID;
23600Sstevel@tonic-gate 		UNLOCK_UNIT(lqi);
23610Sstevel@tonic-gate 	}
23620Sstevel@tonic-gate 
23630Sstevel@tonic-gate 	/*
23640Sstevel@tonic-gate 	 * Turn off the STREAMs queue processing for this queue.
23650Sstevel@tonic-gate 	 */
23660Sstevel@tonic-gate 	qprocsoff(rq);
23670Sstevel@tonic-gate 
23680Sstevel@tonic-gate 	/*
23690Sstevel@tonic-gate 	 * Similarly we will never get here if there is thread trying to
23700Sstevel@tonic-gate 	 * open ths stream.
23710Sstevel@tonic-gate 	 */
23720Sstevel@tonic-gate 	LOCK_UNIT(uqi);
23730Sstevel@tonic-gate 	if (uqi->sm_waitq == 0)
23740Sstevel@tonic-gate 		uqi->sm_flags = (uqi->sm_flags & SM_OBPCNDEV) ? SM_OBPCNDEV :
2375*7656SSherry.Moore@Sun.COM 		    0U;
23760Sstevel@tonic-gate 
23770Sstevel@tonic-gate 	uqi->sm_dev = NODEV;
23780Sstevel@tonic-gate 	uqi->sm_protocol = NULL_PROTOCOL;
23790Sstevel@tonic-gate 	ttycommon_close(uqi->sm_ttycommon);
23800Sstevel@tonic-gate 	/* it just frees any pending ioctl */
23810Sstevel@tonic-gate 
23820Sstevel@tonic-gate 	uqi->sm_ttycommon->t_cflag = 0;
23830Sstevel@tonic-gate 	uqi->sm_ttycommon->t_flags = 0;
23840Sstevel@tonic-gate 
23850Sstevel@tonic-gate 	/*
23860Sstevel@tonic-gate 	 * Reset the queue pointers to NULL.
23870Sstevel@tonic-gate 	 * If a thread is qwaiting in the open routine it will recheck
23880Sstevel@tonic-gate 	 * the q_ptr.
23890Sstevel@tonic-gate 	 */
23900Sstevel@tonic-gate 	rq->q_ptr = NULL;
23910Sstevel@tonic-gate 	WR(rq)->q_ptr = NULL;
23920Sstevel@tonic-gate 	UNLOCK_UNIT(uqi);
23930Sstevel@tonic-gate 
23940Sstevel@tonic-gate 	if (sm_ssp->sm_lconsole == uqi) {
23950Sstevel@tonic-gate 		/* this will never be the outdial device closing */
23960Sstevel@tonic-gate 		sm_ssp->sm_lconsole = 0;
23970Sstevel@tonic-gate 	}
23980Sstevel@tonic-gate 	/*
23990Sstevel@tonic-gate 	 * If there is another thread waiting for this close then unblock
24000Sstevel@tonic-gate 	 * the thread by putting a message on its read queue.
24010Sstevel@tonic-gate 	 */
24020Sstevel@tonic-gate 	if (uqi->sm_waitq) {
24030Sstevel@tonic-gate 		sm_dbg('C', ("close(0x%p): doing putctl on 0x%p\n",
2404*7656SSherry.Moore@Sun.COM 		    rq, uqi->sm_waitq));
24050Sstevel@tonic-gate 		if (rq == uqi->sm_waitq)
24060Sstevel@tonic-gate 			sm_log("close: waitq and closeq are same q\n");
24070Sstevel@tonic-gate 		(void) putctl(uqi->sm_waitq, M_CTL);
24080Sstevel@tonic-gate 	}
24090Sstevel@tonic-gate 
24100Sstevel@tonic-gate 	uqi->sm_flags &= ~(EXCL_OPEN | FULLY_OPEN);
24110Sstevel@tonic-gate 	sm_dbg('C', ("close: returning ok.\n"));
24120Sstevel@tonic-gate 	return (0);
24130Sstevel@tonic-gate }
24140Sstevel@tonic-gate 
24150Sstevel@tonic-gate /*
24160Sstevel@tonic-gate  * Initialise the software abort sequence for use when one of the
24170Sstevel@tonic-gate  * driver's nodes provides the system console.
24180Sstevel@tonic-gate  */
24190Sstevel@tonic-gate static void
sm_set_abort()24200Sstevel@tonic-gate sm_set_abort()
24210Sstevel@tonic-gate {
24220Sstevel@tonic-gate 	char ds[3] = { '\r', '~', CNTRL('b') };
24230Sstevel@tonic-gate 	char as[SM_MAX_ABSLEN];
24240Sstevel@tonic-gate 	int len = SM_MAX_ABSLEN;
24250Sstevel@tonic-gate 
24260Sstevel@tonic-gate 	if (ddi_prop_op(DDI_DEV_T_ANY, sm_ssp->sm_dip, PROP_LEN_AND_VAL_BUF, 0,
2427*7656SSherry.Moore@Sun.COM 	    "abort-str", as, &len) != DDI_PROP_SUCCESS ||
2428*7656SSherry.Moore@Sun.COM 	    (len = strlen(as)) < SM_MIN_ABSLEN) {
24290Sstevel@tonic-gate 		(void) strcpy(as, ds);
24300Sstevel@tonic-gate 		len = strlen(as);
24310Sstevel@tonic-gate 	} else {
24320Sstevel@tonic-gate 		char *s;
24330Sstevel@tonic-gate 		int i;
24340Sstevel@tonic-gate 
24350Sstevel@tonic-gate 		for (s = as, i = 0; i < len-1; i++, s++) {
24360Sstevel@tonic-gate 			if (as[i] == '^' && as[i+1] >= 'a' && as[i+1] <= 'z') {
24370Sstevel@tonic-gate 				*s = as[i+1] - 'a' + 1;
24380Sstevel@tonic-gate 				i++;
24390Sstevel@tonic-gate 			} else {
24400Sstevel@tonic-gate 				*s = as[i];
24410Sstevel@tonic-gate 			}
24420Sstevel@tonic-gate 		}
24430Sstevel@tonic-gate 		*s++ = as[i];
24440Sstevel@tonic-gate 		*s = '\0';
24450Sstevel@tonic-gate 		len = strlen(as);
24460Sstevel@tonic-gate 	}
24470Sstevel@tonic-gate 
24480Sstevel@tonic-gate 	if (len < SM_MIN_ABSLEN)
24490Sstevel@tonic-gate 		(void) strcpy(sm_ssp->sm_abs, ds);
24500Sstevel@tonic-gate 	else
24510Sstevel@tonic-gate 		(void) strcpy(sm_ssp->sm_abs, as);
24520Sstevel@tonic-gate }
24530Sstevel@tonic-gate 
24540Sstevel@tonic-gate /*
24550Sstevel@tonic-gate  *
24560Sstevel@tonic-gate  * sm_attach - initialisation routine per driver instance.
24570Sstevel@tonic-gate  */
24580Sstevel@tonic-gate static int
sm_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)24590Sstevel@tonic-gate sm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
24600Sstevel@tonic-gate {
24610Sstevel@tonic-gate 	int unit;
24620Sstevel@tonic-gate 	char name[32];
24630Sstevel@tonic-gate 	sm_uqi_t *uqi;
24640Sstevel@tonic-gate 	sm_lqi_t *lqip;
24650Sstevel@tonic-gate 
24660Sstevel@tonic-gate 	/*
24670Sstevel@tonic-gate 	 * Is this an attach?
24680Sstevel@tonic-gate 	 */
24690Sstevel@tonic-gate 	if (cmd != DDI_ATTACH) {
24700Sstevel@tonic-gate 		return (DDI_FAILURE);
24710Sstevel@tonic-gate 	}
24720Sstevel@tonic-gate 
24730Sstevel@tonic-gate 	/*
24740Sstevel@tonic-gate 	 * Validate the instance number (sm is a single instance driver).
24750Sstevel@tonic-gate 	 */
24760Sstevel@tonic-gate 	if (sm_ssp) {	/* only one instance allowed */
24770Sstevel@tonic-gate 		return (DDI_FAILURE);
24780Sstevel@tonic-gate 	}
24790Sstevel@tonic-gate 
24800Sstevel@tonic-gate 	sm_instance = ddi_get_instance(dip);
24810Sstevel@tonic-gate 
24820Sstevel@tonic-gate 	/*
24830Sstevel@tonic-gate 	 * Create the default minor node which will become the console.
24840Sstevel@tonic-gate 	 * (create it with three different names).:
24850Sstevel@tonic-gate 	 *	con which appears in the /dev filesystem;
24860Sstevel@tonic-gate 	 *	input which matches the prom /multiplexer:input node;
24870Sstevel@tonic-gate 	 *	output which matches the prom /multiplexer:input node
24880Sstevel@tonic-gate 	 * Create a minor node for control operations.
24890Sstevel@tonic-gate 	 */
24900Sstevel@tonic-gate 	if (ddi_create_minor_node(dip, "con", S_IFCHR, 0,
24910Sstevel@tonic-gate 	    DDI_PSEUDO, 0) != DDI_SUCCESS ||
24920Sstevel@tonic-gate 	    ddi_create_minor_node(dip, "input", S_IFCHR, 0,
24930Sstevel@tonic-gate 	    DDI_PSEUDO, 0) != DDI_SUCCESS ||
24940Sstevel@tonic-gate 	    ddi_create_minor_node(dip, "output", S_IFCHR, 0,
24950Sstevel@tonic-gate 	    DDI_PSEUDO, 0) != DDI_SUCCESS ||
24960Sstevel@tonic-gate 	    ddi_create_minor_node(dip, "ctl", S_IFCHR, 1,
24970Sstevel@tonic-gate 	    DDI_PSEUDO, 0) != DDI_SUCCESS) {
24980Sstevel@tonic-gate 
24990Sstevel@tonic-gate 		cmn_err(CE_WARN, "sm_attach: create minors failed.\n");
25000Sstevel@tonic-gate 		ddi_remove_minor_node(dip, NULL);
25010Sstevel@tonic-gate 		return (DDI_FAILURE);
25020Sstevel@tonic-gate 	}
25030Sstevel@tonic-gate 
25040Sstevel@tonic-gate 	smctlunit = 1;
25050Sstevel@tonic-gate 
25060Sstevel@tonic-gate 	/*
25070Sstevel@tonic-gate 	 * Allocate private state for this instance.
25080Sstevel@tonic-gate 	 */
25090Sstevel@tonic-gate 	sm_ssp = (sm_ss_t *)kmem_zalloc(sizeof (sm_ss_t), KM_SLEEP);
25100Sstevel@tonic-gate 
25110Sstevel@tonic-gate 	/*
25120Sstevel@tonic-gate 	 * Initialise per instance data.
25130Sstevel@tonic-gate 	 */
25140Sstevel@tonic-gate 	sm_ssp->sm_dip = dip;
25150Sstevel@tonic-gate 
25160Sstevel@tonic-gate 	/*
25170Sstevel@tonic-gate 	 * Get required debug level.
25180Sstevel@tonic-gate 	 */
25190Sstevel@tonic-gate 	sm_ssp->sm_trflag = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2520*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "sm-trlv", sm_default_trflag);
25210Sstevel@tonic-gate 
25220Sstevel@tonic-gate 	sm_max_units = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2523*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "sm-max-units", sm_max_units);
25240Sstevel@tonic-gate 	sm_minor_cnt = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2525*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "sm-minor-cnt", 0);
25260Sstevel@tonic-gate 
25270Sstevel@tonic-gate 	sm_refuse_opens = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2528*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "sm-refuse-opens", sm_refuse_opens);
25290Sstevel@tonic-gate 
25300Sstevel@tonic-gate 	sm_ssp->sm_ctrla_abort_on = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2531*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "sm-ctrla-abort-on", 1);
25320Sstevel@tonic-gate 	sm_ssp->sm_break_abort_on = ddi_prop_get_int(DDI_DEV_T_ANY, dip,
2533*7656SSherry.Moore@Sun.COM 	    DDI_PROP_DONTPASS, "sm-break-abort-on", 0);
25340Sstevel@tonic-gate 
25350Sstevel@tonic-gate 	sm_set_abort();
25360Sstevel@tonic-gate 
25370Sstevel@tonic-gate 	sm_ssp->sm_lqs = (sm_lqi_t *)kmem_zalloc(sizeof (sm_lqi_t) * MAX_LQS,
2538*7656SSherry.Moore@Sun.COM 	    KM_SLEEP);
25390Sstevel@tonic-gate 	sm_ssp->sm_uqs = (sm_uqi_t *)kmem_zalloc(sizeof (sm_uqi_t) * NLUNITS,
2540*7656SSherry.Moore@Sun.COM 	    KM_SLEEP);
25410Sstevel@tonic-gate 
25420Sstevel@tonic-gate 	for (unit = 2; unit < NLUNITS && unit < sm_minor_cnt + 2; unit++) {
25430Sstevel@tonic-gate 
25440Sstevel@tonic-gate 		if (snprintf(name, sizeof (name), "sm%c", 'a' + unit-2) >
25450Sstevel@tonic-gate 		    sizeof (name)) {
25460Sstevel@tonic-gate 			cmn_err(CE_WARN,
25470Sstevel@tonic-gate 			    "sm_attach: create device for unit %d failed.\n",
25480Sstevel@tonic-gate 			    unit);
25490Sstevel@tonic-gate 		} else if (ddi_create_minor_node(dip, name, S_IFCHR,
25500Sstevel@tonic-gate 		    unit, DDI_NT_SERIAL, NULL) != DDI_SUCCESS) {
25510Sstevel@tonic-gate 			ddi_remove_minor_node(dip, NULL);
25520Sstevel@tonic-gate 			return (DDI_FAILURE);
25530Sstevel@tonic-gate 		}
25540Sstevel@tonic-gate 
25550Sstevel@tonic-gate 		if (snprintf(name, sizeof (name), "sm%c,cu", 'a' + unit-2) >
25560Sstevel@tonic-gate 		    sizeof (name)) {
25570Sstevel@tonic-gate 			cmn_err(CE_WARN,
25580Sstevel@tonic-gate 			    "sm_attach: create cu device for unit %d failed.\n",
25590Sstevel@tonic-gate 			    unit);
25600Sstevel@tonic-gate 			continue;
25610Sstevel@tonic-gate 		} else if (ddi_create_minor_node(dip, name, S_IFCHR,
25620Sstevel@tonic-gate 		    unit|OUTLINE, DDI_NT_SERIAL_DO, NULL) != DDI_SUCCESS) {
25630Sstevel@tonic-gate 			ddi_remove_minor_node(dip, NULL);
25640Sstevel@tonic-gate 			return (DDI_FAILURE);
25650Sstevel@tonic-gate 		}
25660Sstevel@tonic-gate 	}
25670Sstevel@tonic-gate 
25680Sstevel@tonic-gate 	for (unit = 0; unit < NLUNITS; unit++) {
25690Sstevel@tonic-gate 
25700Sstevel@tonic-gate 		uqi = get_uqi(sm_ssp, unit);
25710Sstevel@tonic-gate 		uqi->sm_lqs = 0;
25720Sstevel@tonic-gate 		uqi->sm_dev = NODEV;
25730Sstevel@tonic-gate 		uqi->sm_nlqs = 0;
25740Sstevel@tonic-gate 		uqi->sm_lunit = unit;
25750Sstevel@tonic-gate 		uqi->sm_protocol = NULL_PROTOCOL;
25760Sstevel@tonic-gate 		mutex_init(uqi->sm_umutex, NULL, MUTEX_DRIVER, NULL);
25770Sstevel@tonic-gate 		cv_init(uqi->sm_ucv, NULL, CV_DRIVER, NULL);
25780Sstevel@tonic-gate 		mutex_init(&uqi->sm_ttycommon->t_excl, NULL,
2579*7656SSherry.Moore@Sun.COM 		    MUTEX_DRIVER, NULL);
25800Sstevel@tonic-gate 	}
25810Sstevel@tonic-gate 
25820Sstevel@tonic-gate 	for (unit = 0; unit < MAX_LQS; unit++) {
25830Sstevel@tonic-gate 		lqip = get_lqi(sm_ssp, unit);
25840Sstevel@tonic-gate 		lqip->sm_unit = unit;
25850Sstevel@tonic-gate 		lqip->sm_hadkadbchar = 0;
25860Sstevel@tonic-gate 		lqip->sm_nachar = sm_ssp->sm_abs;
25870Sstevel@tonic-gate 		lqip->sm_ioflag = FORIO;
25880Sstevel@tonic-gate 		lqip->sm_ctrla_abort_on = sm_ssp->sm_ctrla_abort_on;
25890Sstevel@tonic-gate 		lqip->sm_break_abort_on = sm_ssp->sm_break_abort_on;
25900Sstevel@tonic-gate 		mutex_init(lqip->sm_umutex, NULL, MUTEX_DRIVER, NULL);
25910Sstevel@tonic-gate 		cv_init(lqip->sm_ucv, NULL, CV_DRIVER, NULL);
25920Sstevel@tonic-gate 		mutex_init(&lqip->sm_ttycommon->t_excl, NULL,
2593*7656SSherry.Moore@Sun.COM 		    MUTEX_DRIVER, NULL);
25940Sstevel@tonic-gate 	}
25950Sstevel@tonic-gate 
25960Sstevel@tonic-gate 	return (DDI_SUCCESS);
25970Sstevel@tonic-gate }
25980Sstevel@tonic-gate 
25990Sstevel@tonic-gate /*
26000Sstevel@tonic-gate  *
26010Sstevel@tonic-gate  * sm_detach - detach routine per driver instance.
26020Sstevel@tonic-gate  */
26030Sstevel@tonic-gate static int
sm_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)26040Sstevel@tonic-gate sm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
26050Sstevel@tonic-gate {
26060Sstevel@tonic-gate 	sm_uqi_t		*lu;
26070Sstevel@tonic-gate 	sm_lqi_t		*pu;
26080Sstevel@tonic-gate 	int		unit;
26090Sstevel@tonic-gate 
26100Sstevel@tonic-gate 	/*
26110Sstevel@tonic-gate 	 * Is this a detach request for instance 0 (single instance driver).
26120Sstevel@tonic-gate 	 */
26130Sstevel@tonic-gate 	if (cmd != DDI_DETACH)
26140Sstevel@tonic-gate 		return (DDI_FAILURE);
26150Sstevel@tonic-gate 
26160Sstevel@tonic-gate 	if (sm_ssp == NULL)
26170Sstevel@tonic-gate 		return (DDI_FAILURE);
26180Sstevel@tonic-gate 
26190Sstevel@tonic-gate 	sm_dbg('V', ("detach ..."));
26200Sstevel@tonic-gate 
26210Sstevel@tonic-gate 
26220Sstevel@tonic-gate 	/*
26230Sstevel@tonic-gate 	 * Check that all the upper and lower queues are closed.
26240Sstevel@tonic-gate 	 */
26250Sstevel@tonic-gate 
26260Sstevel@tonic-gate 	for (unit = 0; unit < NLUNITS; unit++) {
26270Sstevel@tonic-gate 		lu = &sm_ssp->sm_uqs[unit];
26280Sstevel@tonic-gate 		if (lu && lu->sm_protocol != NULL_PROTOCOL) {
26290Sstevel@tonic-gate 			sm_dbg('V', ("detach: upper unit still open.\n"));
26300Sstevel@tonic-gate 			return (DDI_FAILURE);
26310Sstevel@tonic-gate 		}
26320Sstevel@tonic-gate 	}
26330Sstevel@tonic-gate 	for (unit = 0; unit < MAX_LQS; unit++) {
26340Sstevel@tonic-gate 		pu = &sm_ssp->sm_lqs[unit];
26350Sstevel@tonic-gate 		if (pu && pu->sm_linkid != 0) {
26360Sstevel@tonic-gate 			sm_dbg('V', ("detach: lower unit still linked (%d)\n",
26370Sstevel@tonic-gate 			    pu->sm_linkid));
26380Sstevel@tonic-gate 			return (DDI_FAILURE);
26390Sstevel@tonic-gate 		}
26400Sstevel@tonic-gate 	}
26410Sstevel@tonic-gate 
26420Sstevel@tonic-gate 	for (unit = 0; unit < NLUNITS; unit++) {
26430Sstevel@tonic-gate 		lu = &sm_ssp->sm_uqs[unit];
26440Sstevel@tonic-gate 		mutex_destroy(lu->sm_umutex);
26450Sstevel@tonic-gate 		cv_destroy(lu->sm_ucv);
26460Sstevel@tonic-gate 		mutex_destroy(&lu->sm_ttycommon->t_excl);
26470Sstevel@tonic-gate 	}
26480Sstevel@tonic-gate 	for (unit = 0; unit < MAX_LQS; unit++) {
26490Sstevel@tonic-gate 		pu = &sm_ssp->sm_lqs[unit];
26500Sstevel@tonic-gate 		mutex_destroy(pu->sm_umutex);
26510Sstevel@tonic-gate 		cv_destroy(pu->sm_ucv);
26520Sstevel@tonic-gate 		mutex_destroy(&pu->sm_ttycommon->t_excl);
26530Sstevel@tonic-gate 	}
26540Sstevel@tonic-gate 
26550Sstevel@tonic-gate 	/*
26560Sstevel@tonic-gate 	 * Tidy up per instance state.
26570Sstevel@tonic-gate 	 */
26580Sstevel@tonic-gate 	kmem_free(sm_ssp->sm_lqs, sizeof (sm_lqi_t) * MAX_LQS);
26590Sstevel@tonic-gate 	kmem_free(sm_ssp->sm_uqs, sizeof (sm_uqi_t) * NLUNITS);
26600Sstevel@tonic-gate 	kmem_free(sm_ssp, sizeof (sm_ss_t));
26610Sstevel@tonic-gate 
26620Sstevel@tonic-gate 	sm_ssp = 0;
26630Sstevel@tonic-gate 
26640Sstevel@tonic-gate 	/*
26650Sstevel@tonic-gate 	 * Remove all of the devices created in attach.
26660Sstevel@tonic-gate 	 */
26670Sstevel@tonic-gate 	ddi_remove_minor_node(dip, NULL);
26680Sstevel@tonic-gate 
26690Sstevel@tonic-gate 	return (DDI_SUCCESS);
26700Sstevel@tonic-gate }
26710Sstevel@tonic-gate 
26720Sstevel@tonic-gate /*
26730Sstevel@tonic-gate  * SECTION
26740Sstevel@tonic-gate  * Driver interface to the OS.
26750Sstevel@tonic-gate  */
26760Sstevel@tonic-gate 
26770Sstevel@tonic-gate /*
26780Sstevel@tonic-gate  * The driver is responsible for managing the mapping between the file system
26790Sstevel@tonic-gate  * device types (major/minor pairs) and the corresponding instance of the driver
26800Sstevel@tonic-gate  * or device information pointer (dip).
26810Sstevel@tonic-gate  * sm_info - return the instance or dip corresponding to the dev_t.
26820Sstevel@tonic-gate  */
26830Sstevel@tonic-gate /*ARGSUSED*/
26840Sstevel@tonic-gate static int
sm_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)26850Sstevel@tonic-gate sm_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
26860Sstevel@tonic-gate {
26870Sstevel@tonic-gate 	int res = DDI_SUCCESS;
26880Sstevel@tonic-gate 
26890Sstevel@tonic-gate 	switch (infocmd) {
26900Sstevel@tonic-gate 	case DDI_INFO_DEVT2DEVINFO:
26910Sstevel@tonic-gate 		if (sm_ssp == NULL)
26920Sstevel@tonic-gate 			res = DDI_FAILURE;
26930Sstevel@tonic-gate 		else
26940Sstevel@tonic-gate 			*result = (void *)sm_ssp->sm_dip;
26950Sstevel@tonic-gate 		break;
26960Sstevel@tonic-gate 
26970Sstevel@tonic-gate 	case DDI_INFO_DEVT2INSTANCE:
26980Sstevel@tonic-gate 		*result = (void*)0;	/* single instance driver */
26990Sstevel@tonic-gate 		break;
27000Sstevel@tonic-gate 
27010Sstevel@tonic-gate 	default:
27020Sstevel@tonic-gate 		res = DDI_FAILURE;
27030Sstevel@tonic-gate 		break;
27040Sstevel@tonic-gate 	}
27050Sstevel@tonic-gate 
27060Sstevel@tonic-gate 	return (res);
27070Sstevel@tonic-gate }
27080Sstevel@tonic-gate 
27090Sstevel@tonic-gate /*
27100Sstevel@tonic-gate  * End of driver implementation
27110Sstevel@tonic-gate  */
27120Sstevel@tonic-gate 
27130Sstevel@tonic-gate /*
27140Sstevel@tonic-gate  * Loadable module interface to the kernel
27150Sstevel@tonic-gate  */
27160Sstevel@tonic-gate 
27170Sstevel@tonic-gate /*
27180Sstevel@tonic-gate  * Firstly the Streams specific interface
27190Sstevel@tonic-gate  */
27200Sstevel@tonic-gate 
27210Sstevel@tonic-gate /*
27220Sstevel@tonic-gate  * Solaris driver/STREAM initialisation structures.
27230Sstevel@tonic-gate  */
27240Sstevel@tonic-gate static struct module_info uinfo =
27250Sstevel@tonic-gate {
27260Sstevel@tonic-gate 	SM_MOD_ID,
27270Sstevel@tonic-gate 	TTYMUX_DRVNAME,
27280Sstevel@tonic-gate 	0,		/* min packet size */
27290Sstevel@tonic-gate 	INFPSZ,		/* max packet size */
27300Sstevel@tonic-gate 	2048,		/* high water mark */
27310Sstevel@tonic-gate 	256,		/* low water mark */
27320Sstevel@tonic-gate };
27330Sstevel@tonic-gate 
27340Sstevel@tonic-gate /*
27350Sstevel@tonic-gate  * Use zero water marks becuase the lower queues are used only for flow control.
27360Sstevel@tonic-gate  */
27370Sstevel@tonic-gate static struct module_info linfo =
27380Sstevel@tonic-gate {
27390Sstevel@tonic-gate 	SM_MOD_ID,
27400Sstevel@tonic-gate 	TTYMUX_DRVNAME,
27410Sstevel@tonic-gate 	0,		/* min packet size */
27420Sstevel@tonic-gate 	INFPSZ,		/* max packet size */
27430Sstevel@tonic-gate 	0,		/* high water mark */
27440Sstevel@tonic-gate 	0		/* low water mark	*/
27450Sstevel@tonic-gate };
27460Sstevel@tonic-gate 
27470Sstevel@tonic-gate 
27480Sstevel@tonic-gate /*
27490Sstevel@tonic-gate  * Solaris upper read STREAM initialisation structure.
27500Sstevel@tonic-gate  */
27510Sstevel@tonic-gate static struct qinit urinit =
27520Sstevel@tonic-gate {
27530Sstevel@tonic-gate 	sm_urput,	/* put */
27540Sstevel@tonic-gate 	sm_ursrv,	/* service */
27550Sstevel@tonic-gate 	sm_open,	/* open */
27560Sstevel@tonic-gate 	sm_close,	/* close */
27570Sstevel@tonic-gate 	NULL,		/* admin */
27580Sstevel@tonic-gate 	&uinfo,		/* module info */
27590Sstevel@tonic-gate 	NULL		/* stats */
27600Sstevel@tonic-gate };
27610Sstevel@tonic-gate 
27620Sstevel@tonic-gate /*
27630Sstevel@tonic-gate  * Solaris upper write STREAM initialisation structure.
27640Sstevel@tonic-gate  */
27650Sstevel@tonic-gate static struct qinit uwinit =
27660Sstevel@tonic-gate {
27670Sstevel@tonic-gate 	sm_uwput,
27680Sstevel@tonic-gate 	sm_uwsrv,
27690Sstevel@tonic-gate 	NULL,
27700Sstevel@tonic-gate 	NULL,
27710Sstevel@tonic-gate 	NULL,
27720Sstevel@tonic-gate 	&uinfo,
27730Sstevel@tonic-gate 	NULL
27740Sstevel@tonic-gate };
27750Sstevel@tonic-gate 
27760Sstevel@tonic-gate /*
27770Sstevel@tonic-gate  * Solaris lower read STREAM initialisation structure.
27780Sstevel@tonic-gate  */
27790Sstevel@tonic-gate static struct qinit lrinit =
27800Sstevel@tonic-gate {
27810Sstevel@tonic-gate 	sm_lrput,
27820Sstevel@tonic-gate 	sm_lrsrv,
27830Sstevel@tonic-gate 	NULL,
27840Sstevel@tonic-gate 	NULL, NULL,
27850Sstevel@tonic-gate 	&linfo,
27860Sstevel@tonic-gate 	NULL
27870Sstevel@tonic-gate };
27880Sstevel@tonic-gate 
27890Sstevel@tonic-gate /*
27900Sstevel@tonic-gate  * Solaris lower write STREAM initialisation structure.
27910Sstevel@tonic-gate  */
27920Sstevel@tonic-gate static struct qinit lwinit =
27930Sstevel@tonic-gate {
27940Sstevel@tonic-gate 	putq,
27950Sstevel@tonic-gate 	sm_lwsrv,
27960Sstevel@tonic-gate 	NULL,
27970Sstevel@tonic-gate 	NULL,
27980Sstevel@tonic-gate 	NULL,
27990Sstevel@tonic-gate 	&linfo,
28000Sstevel@tonic-gate 	NULL
28010Sstevel@tonic-gate };
28020Sstevel@tonic-gate 
28030Sstevel@tonic-gate /*
28040Sstevel@tonic-gate  * Multiplexing STREAM structure.
28050Sstevel@tonic-gate  */
28060Sstevel@tonic-gate struct streamtab sm_streamtab =
28070Sstevel@tonic-gate {
28080Sstevel@tonic-gate 	&urinit,
28090Sstevel@tonic-gate 	&uwinit,
28100Sstevel@tonic-gate 	&lrinit,
28110Sstevel@tonic-gate 	&lwinit
28120Sstevel@tonic-gate };
28130Sstevel@tonic-gate 
28140Sstevel@tonic-gate /*
28150Sstevel@tonic-gate  * Driver operations structure (struct cb_ops) and
28160Sstevel@tonic-gate  * driver dynamic loading functions (struct dev_ops).
28170Sstevel@tonic-gate  */
28180Sstevel@tonic-gate 
28190Sstevel@tonic-gate /*
28200Sstevel@tonic-gate  * Fold the Stream interface to the kernel into the driver interface
28210Sstevel@tonic-gate  * to the OS.
28220Sstevel@tonic-gate  */
28230Sstevel@tonic-gate 
28240Sstevel@tonic-gate DDI_DEFINE_STREAM_OPS(sm_ops, \
28250Sstevel@tonic-gate 	nulldev, nulldev, \
28260Sstevel@tonic-gate 	sm_attach, sm_detach, nodev, \
28270Sstevel@tonic-gate 	sm_info, (D_NEW | D_MTQPAIR|D_MTOUTPERIM|D_MTOCEXCL | D_MP),
2828*7656SSherry.Moore@Sun.COM 	&sm_streamtab, ddi_quiesce_not_supported);
28290Sstevel@tonic-gate 
28300Sstevel@tonic-gate /*
28310Sstevel@tonic-gate  * Driver module information.
28320Sstevel@tonic-gate  */
28330Sstevel@tonic-gate extern struct mod_ops mod_driverops;
28340Sstevel@tonic-gate static struct modldrv modldrv =
28350Sstevel@tonic-gate {
28360Sstevel@tonic-gate 	&mod_driverops,
2837*7656SSherry.Moore@Sun.COM 	"serial mux driver",
28380Sstevel@tonic-gate 	&sm_ops
28390Sstevel@tonic-gate };
28400Sstevel@tonic-gate 
28410Sstevel@tonic-gate static struct modlinkage modlinkage =
28420Sstevel@tonic-gate {
28430Sstevel@tonic-gate 	MODREV_1,
28440Sstevel@tonic-gate 	&modldrv,
28450Sstevel@tonic-gate 	NULL
28460Sstevel@tonic-gate };
28470Sstevel@tonic-gate 
28480Sstevel@tonic-gate /*
28490Sstevel@tonic-gate  * Define the body of our interface to the OS.
28500Sstevel@tonic-gate  */
28510Sstevel@tonic-gate 
28520Sstevel@tonic-gate /*
28530Sstevel@tonic-gate  * '_init' is called by Solaris to initialise any driver
28540Sstevel@tonic-gate  * specific state and to install the driver.
28550Sstevel@tonic-gate  */
28560Sstevel@tonic-gate int
_init(void)28570Sstevel@tonic-gate _init(void)
28580Sstevel@tonic-gate {
28590Sstevel@tonic-gate 	return (mod_install(&modlinkage));
28600Sstevel@tonic-gate }
28610Sstevel@tonic-gate 
28620Sstevel@tonic-gate /*
28630Sstevel@tonic-gate  * _info - return this drivers interface to the kernel.
28640Sstevel@tonic-gate  */
28650Sstevel@tonic-gate int
_info(struct modinfo * modinfop)28660Sstevel@tonic-gate _info(struct modinfo *modinfop)
28670Sstevel@tonic-gate {
28680Sstevel@tonic-gate 	return (mod_info(&modlinkage, modinfop));
28690Sstevel@tonic-gate }
28700Sstevel@tonic-gate 
28710Sstevel@tonic-gate /*
28720Sstevel@tonic-gate  * _fini - the OS is finished with the services provided by the driver.
28730Sstevel@tonic-gate  * remove ourself and then remove any footprint that remains.
28740Sstevel@tonic-gate  */
28750Sstevel@tonic-gate int
_fini(void)28760Sstevel@tonic-gate _fini(void)
28770Sstevel@tonic-gate {
28780Sstevel@tonic-gate 	return (mod_remove(&modlinkage));
28790Sstevel@tonic-gate }
2880