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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * Data-Link Driver
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <sys/debug.h>
350Sstevel@tonic-gate #include <sys/sysmacros.h>
360Sstevel@tonic-gate #include <sys/stream.h>
370Sstevel@tonic-gate #include <sys/ddi.h>
380Sstevel@tonic-gate #include <sys/sunddi.h>
390Sstevel@tonic-gate #include <sys/strsun.h>
40*1184Skrgopi #include <sys/cpuvar.h>
410Sstevel@tonic-gate #include <sys/dlpi.h>
420Sstevel@tonic-gate #include <netinet/in.h>
430Sstevel@tonic-gate #include <sys/sdt.h>
440Sstevel@tonic-gate #include <sys/strsubr.h>
450Sstevel@tonic-gate #include <sys/vlan.h>
460Sstevel@tonic-gate #include <sys/mac.h>
470Sstevel@tonic-gate #include <sys/dls.h>
480Sstevel@tonic-gate #include <sys/dld.h>
490Sstevel@tonic-gate #include <sys/dld_impl.h>
50*1184Skrgopi #include <sys/dls_soft_ring.h>
510Sstevel@tonic-gate 
520Sstevel@tonic-gate typedef boolean_t proto_reqfunc_t(dld_str_t *, union DL_primitives *, mblk_t *);
530Sstevel@tonic-gate 
540Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
550Sstevel@tonic-gate     proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
560Sstevel@tonic-gate     proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
570Sstevel@tonic-gate     proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
580Sstevel@tonic-gate     proto_notify_req, proto_unitdata_req, proto_passive_req;
590Sstevel@tonic-gate 
60269Sericheng static void proto_poll_disable(dld_str_t *);
61*1184Skrgopi static boolean_t proto_poll_enable(dld_str_t *, dl_capab_dls_t *);
62269Sericheng static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
630Sstevel@tonic-gate 
64*1184Skrgopi static void proto_soft_ring_disable(dld_str_t *);
65*1184Skrgopi static boolean_t proto_soft_ring_enable(dld_str_t *, dl_capab_dls_t *);
66*1184Skrgopi static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
67*1184Skrgopi static void proto_change_soft_ring_fanout(dld_str_t *, int);
68*1184Skrgopi static void proto_stop_soft_ring_threads(void *);
69*1184Skrgopi 
700Sstevel@tonic-gate #define	DL_ACK_PENDING(state) \
710Sstevel@tonic-gate 	((state) == DL_ATTACH_PENDING || \
720Sstevel@tonic-gate 	(state) == DL_DETACH_PENDING || \
730Sstevel@tonic-gate 	(state) == DL_BIND_PENDING || \
740Sstevel@tonic-gate 	(state) == DL_UNBIND_PENDING)
750Sstevel@tonic-gate 
760Sstevel@tonic-gate /*
77269Sericheng  * Process a DLPI protocol message.
78269Sericheng  * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
79269Sericheng  * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
80269Sericheng  * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
81269Sericheng  * as 'passive' and forbids it from being subsequently made 'active'
82269Sericheng  * by the above primitives.
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate void
850Sstevel@tonic-gate dld_proto(dld_str_t *dsp, mblk_t *mp)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate 	union DL_primitives	*udlp;
880Sstevel@tonic-gate 	t_uscalar_t		prim;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (t_uscalar_t)) {
910Sstevel@tonic-gate 		freemsg(mp);
920Sstevel@tonic-gate 		return;
930Sstevel@tonic-gate 	}
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	udlp = (union DL_primitives *)mp->b_rptr;
960Sstevel@tonic-gate 	prim = udlp->dl_primitive;
970Sstevel@tonic-gate 
98269Sericheng 	switch (prim) {
99269Sericheng 	case DL_INFO_REQ:
100269Sericheng 		(void) proto_info_req(dsp, udlp, mp);
101269Sericheng 		break;
102269Sericheng 	case DL_BIND_REQ:
103269Sericheng 		(void) proto_bind_req(dsp, udlp, mp);
104269Sericheng 		break;
105269Sericheng 	case DL_UNBIND_REQ:
106269Sericheng 		(void) proto_unbind_req(dsp, udlp, mp);
107269Sericheng 		break;
108269Sericheng 	case DL_UNITDATA_REQ:
109269Sericheng 		(void) proto_unitdata_req(dsp, udlp, mp);
110269Sericheng 		break;
111269Sericheng 	case DL_UDQOS_REQ:
112269Sericheng 		(void) proto_udqos_req(dsp, udlp, mp);
113269Sericheng 		break;
114269Sericheng 	case DL_ATTACH_REQ:
115269Sericheng 		(void) proto_attach_req(dsp, udlp, mp);
116269Sericheng 		break;
117269Sericheng 	case DL_DETACH_REQ:
118269Sericheng 		(void) proto_detach_req(dsp, udlp, mp);
119269Sericheng 		break;
120269Sericheng 	case DL_ENABMULTI_REQ:
121269Sericheng 		(void) proto_enabmulti_req(dsp, udlp, mp);
122269Sericheng 		break;
123269Sericheng 	case DL_DISABMULTI_REQ:
124269Sericheng 		(void) proto_disabmulti_req(dsp, udlp, mp);
125269Sericheng 		break;
126269Sericheng 	case DL_PROMISCON_REQ:
127269Sericheng 		(void) proto_promiscon_req(dsp, udlp, mp);
128269Sericheng 		break;
129269Sericheng 	case DL_PROMISCOFF_REQ:
130269Sericheng 		(void) proto_promiscoff_req(dsp, udlp, mp);
131269Sericheng 		break;
132269Sericheng 	case DL_PHYS_ADDR_REQ:
133269Sericheng 		(void) proto_physaddr_req(dsp, udlp, mp);
134269Sericheng 		break;
135269Sericheng 	case DL_SET_PHYS_ADDR_REQ:
136269Sericheng 		(void) proto_setphysaddr_req(dsp, udlp, mp);
137269Sericheng 		break;
138269Sericheng 	case DL_NOTIFY_REQ:
139269Sericheng 		(void) proto_notify_req(dsp, udlp, mp);
140269Sericheng 		break;
141269Sericheng 	case DL_CAPABILITY_REQ:
142269Sericheng 		(void) proto_capability_req(dsp, udlp, mp);
143269Sericheng 		break;
144269Sericheng 	case DL_PASSIVE_REQ:
145269Sericheng 		(void) proto_passive_req(dsp, udlp, mp);
146269Sericheng 		break;
147269Sericheng 	default:
148269Sericheng 		(void) proto_req(dsp, udlp, mp);
149269Sericheng 		break;
1500Sstevel@tonic-gate 	}
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate /*
154269Sericheng  * Finish any pending operations.  At this moment we are single-threaded,
155269Sericheng  * hence there is no need to hold ds_lock as writer because we're already
156269Sericheng  * exclusive.
1570Sstevel@tonic-gate  */
158269Sericheng void
159269Sericheng dld_finish_pending_ops(dld_str_t *dsp)
1600Sstevel@tonic-gate {
161269Sericheng 	ASSERT(MUTEX_HELD(&dsp->ds_thr_lock));
162269Sericheng 	ASSERT(dsp->ds_thr == 0);
1630Sstevel@tonic-gate 
164269Sericheng 	/* Pending DL_DETACH_REQ? */
165269Sericheng 	if (dsp->ds_detach_req != NULL) {
166269Sericheng 		mblk_t *mp;
1670Sstevel@tonic-gate 
168269Sericheng 		ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING);
169269Sericheng 		dld_str_detach(dsp);
1700Sstevel@tonic-gate 
171269Sericheng 		mp = dsp->ds_detach_req;
172269Sericheng 		dsp->ds_detach_req = NULL;
1730Sstevel@tonic-gate 
174269Sericheng 		mutex_exit(&dsp->ds_thr_lock);
175269Sericheng 		dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
176269Sericheng 	} else {
177269Sericheng 		mutex_exit(&dsp->ds_thr_lock);
1780Sstevel@tonic-gate 	}
1790Sstevel@tonic-gate }
1800Sstevel@tonic-gate 
181269Sericheng #define	NEG(x)	-(x)
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1840Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
1850Sstevel@tonic-gate 	uint8_t			dl_addr[MAXADDRLEN + sizeof (uint16_t)];
1860Sstevel@tonic-gate 	uint8_t			dl_brdcst_addr[MAXADDRLEN];
1870Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1880Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1890Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate /*
192269Sericheng  * DL_INFO_REQ
1930Sstevel@tonic-gate  */
194269Sericheng /*ARGSUSED*/
195269Sericheng static boolean_t
196269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1970Sstevel@tonic-gate {
1980Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1990Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
2000Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
2010Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
2020Sstevel@tonic-gate 	uint8_t			*addr;
2030Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
2040Sstevel@tonic-gate 	uint_t			addr_length;
2050Sstevel@tonic-gate 	uint_t			sap_length;
206269Sericheng 	mac_info_t		minfo;
207269Sericheng 	mac_info_t		*minfop;
208269Sericheng 	queue_t			*q = dsp->ds_wq;
2090Sstevel@tonic-gate 
2100Sstevel@tonic-gate 	/*
2110Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
2120Sstevel@tonic-gate 	 * wrapper structure defined above.
2130Sstevel@tonic-gate 	 */
214269Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
2150Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
216269Sericheng 		return (B_FALSE);
217269Sericheng 
218269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
2210Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
2240Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	/*
2290Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
2300Sstevel@tonic-gate 	 */
2310Sstevel@tonic-gate 	addr = dlwp->dl_addr;
2320Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
2330Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
2340Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
2350Sstevel@tonic-gate 
2360Sstevel@tonic-gate 	/*
2370Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
2380Sstevel@tonic-gate 	 * nodes.
2390Sstevel@tonic-gate 	 */
2400Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
2410Sstevel@tonic-gate 	dlp->dl_version = DL_VERSION_2;
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	/*
244269Sericheng 	 * Set the style of the provider
2450Sstevel@tonic-gate 	 */
246269Sericheng 	dlp->dl_provider_style = dsp->ds_style;
2470Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
2480Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 	/*
2510Sstevel@tonic-gate 	 * Set the current DLPI state.
2520Sstevel@tonic-gate 	 */
2530Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
2540Sstevel@tonic-gate 
2550Sstevel@tonic-gate 	/*
256269Sericheng 	 * Gratuitously set the media type. This is to deal with modules
257269Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
2580Sstevel@tonic-gate 	 * being completed.
2590Sstevel@tonic-gate 	 */
2600Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	/*
263269Sericheng 	 * If the stream is not at least attached we try to retrieve the
264269Sericheng 	 * mac_info using mac_info_get()
2650Sstevel@tonic-gate 	 */
2660Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2670Sstevel@tonic-gate 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
268269Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
269269Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
270269Sericheng 			/*
271269Sericheng 			 * Cannot find mac_info. giving up.
272269Sericheng 			 */
273269Sericheng 			goto done;
274269Sericheng 		}
275269Sericheng 		minfop = &minfo;
276269Sericheng 	} else {
277269Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
278269Sericheng 	}
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	/*
2810Sstevel@tonic-gate 	 * Set the media type (properly this time).
2820Sstevel@tonic-gate 	 */
283269Sericheng 	dlp->dl_mac_type = minfop->mi_media;
2840Sstevel@tonic-gate 
2850Sstevel@tonic-gate 	/*
2860Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2870Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2880Sstevel@tonic-gate 	 */
2890Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2900Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2910Sstevel@tonic-gate 
2920Sstevel@tonic-gate 	/*
2930Sstevel@tonic-gate 	 * Set the minimum and maximum payload sizes.
2940Sstevel@tonic-gate 	 */
295269Sericheng 	dlp->dl_min_sdu = minfop->mi_sdu_min;
296269Sericheng 	dlp->dl_max_sdu = minfop->mi_sdu_max;
2970Sstevel@tonic-gate 
298269Sericheng 	addr_length = minfop->mi_addr_length;
2990Sstevel@tonic-gate 	ASSERT(addr_length != 0);
3000Sstevel@tonic-gate 
3010Sstevel@tonic-gate 	/*
3020Sstevel@tonic-gate 	 * Copy in the media broadcast address.
3030Sstevel@tonic-gate 	 */
3040Sstevel@tonic-gate 	dlp->dl_brdcst_addr_offset = (uintptr_t)brdcst_addr - (uintptr_t)dlp;
305269Sericheng 	bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
3060Sstevel@tonic-gate 	dlp->dl_brdcst_addr_length = addr_length;
3070Sstevel@tonic-gate 
3080Sstevel@tonic-gate 	/*
3090Sstevel@tonic-gate 	 * We only support QoS information for VLAN interfaces.
3100Sstevel@tonic-gate 	 */
3110Sstevel@tonic-gate 	if (dsp->ds_vid != VLAN_ID_NONE) {
3120Sstevel@tonic-gate 		dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
3130Sstevel@tonic-gate 		dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 		rangep->dl_qos_type = DL_QOS_CL_RANGE1;
3160Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
3170Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
3180Sstevel@tonic-gate 		rangep->dl_protection.dl_min = DL_UNKNOWN;
3190Sstevel@tonic-gate 		rangep->dl_protection.dl_max = DL_UNKNOWN;
3200Sstevel@tonic-gate 		rangep->dl_residual_error = DL_UNKNOWN;
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 		/*
3230Sstevel@tonic-gate 		 * Specify the supported range of priorities.
3240Sstevel@tonic-gate 		 */
3250Sstevel@tonic-gate 		rangep->dl_priority.dl_min = 0;
3260Sstevel@tonic-gate 		rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 		dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
3290Sstevel@tonic-gate 		dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 		selp->dl_qos_type = DL_QOS_CL_SEL1;
3320Sstevel@tonic-gate 		selp->dl_trans_delay = DL_UNKNOWN;
3330Sstevel@tonic-gate 		selp->dl_protection = DL_UNKNOWN;
3340Sstevel@tonic-gate 		selp->dl_residual_error = DL_UNKNOWN;
3350Sstevel@tonic-gate 
3360Sstevel@tonic-gate 		/*
3370Sstevel@tonic-gate 		 * Specify the current priority (which can be changed by
3380Sstevel@tonic-gate 		 * the DL_UDQOS_REQ primitive).
3390Sstevel@tonic-gate 		 */
3400Sstevel@tonic-gate 		selp->dl_priority = dsp->ds_pri;
3410Sstevel@tonic-gate 	} else {
3420Sstevel@tonic-gate 		/*
3430Sstevel@tonic-gate 		 * Shorten the buffer to lose the unused QoS information
344269Sericheng 		 * structures.
3450Sstevel@tonic-gate 		 */
3460Sstevel@tonic-gate 		mp->b_wptr = (uint8_t *)rangep;
3470Sstevel@tonic-gate 	}
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
3500Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
3510Sstevel@tonic-gate 		/*
3520Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
3530Sstevel@tonic-gate 		 * DLSAP address.
3540Sstevel@tonic-gate 		 */
3550Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
3560Sstevel@tonic-gate 		bcopy(dsp->ds_curr_addr, addr, addr_length);
3570Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3580Sstevel@tonic-gate 	}
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate done:
3610Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
3620Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
3630Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3640Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3650Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3660Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3670Sstevel@tonic-gate 
368269Sericheng 	rw_exit(&dsp->ds_lock);
369269Sericheng 
370269Sericheng 	qreply(q, mp);
371269Sericheng 	return (B_TRUE);
372269Sericheng }
373269Sericheng 
374269Sericheng /*
375269Sericheng  * DL_ATTACH_REQ
376269Sericheng  */
377269Sericheng static boolean_t
378269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
379269Sericheng {
380269Sericheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)udlp;
381269Sericheng 	int		err = 0;
382269Sericheng 	t_uscalar_t	dl_err;
383269Sericheng 	queue_t		*q = dsp->ds_wq;
384269Sericheng 
385269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
386269Sericheng 
387269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
388269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
389269Sericheng 		dl_err = DL_BADPRIM;
390269Sericheng 		goto failed;
391269Sericheng 	}
392269Sericheng 
393269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
394269Sericheng 		dl_err = DL_OUTSTATE;
395269Sericheng 		goto failed;
396269Sericheng 	}
397269Sericheng 
398269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
399269Sericheng 
400269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
401269Sericheng 	if (err != 0) {
402269Sericheng 		switch (err) {
403269Sericheng 		case ENOENT:
404269Sericheng 			dl_err = DL_BADPPA;
405269Sericheng 			err = 0;
406269Sericheng 			break;
407269Sericheng 		default:
408269Sericheng 			dl_err = DL_SYSERR;
409269Sericheng 			break;
410269Sericheng 		}
411269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
412269Sericheng 		goto failed;
413269Sericheng 	}
414269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
415269Sericheng 	rw_exit(&dsp->ds_lock);
416269Sericheng 
417269Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
418269Sericheng 	return (B_TRUE);
419269Sericheng failed:
420269Sericheng 	rw_exit(&dsp->ds_lock);
421269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
422269Sericheng 	return (B_FALSE);
4230Sstevel@tonic-gate }
4240Sstevel@tonic-gate 
4250Sstevel@tonic-gate /*
426269Sericheng  * DL_DETACH_REQ
4270Sstevel@tonic-gate  */
428269Sericheng /*ARGSUSED*/
429269Sericheng static boolean_t
430269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4310Sstevel@tonic-gate {
432269Sericheng 	queue_t		*q = dsp->ds_wq;
433269Sericheng 	t_uscalar_t	dl_err;
4340Sstevel@tonic-gate 
435269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
4360Sstevel@tonic-gate 
437269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
438269Sericheng 		dl_err = DL_BADPRIM;
439269Sericheng 		goto failed;
440269Sericheng 	}
4410Sstevel@tonic-gate 
442269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
443269Sericheng 		dl_err = DL_OUTSTATE;
444269Sericheng 		goto failed;
4450Sstevel@tonic-gate 	}
4460Sstevel@tonic-gate 
447269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
448269Sericheng 		dl_err = DL_BADPRIM;
449269Sericheng 		goto failed;
450269Sericheng 	}
451269Sericheng 
452269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
453269Sericheng 
454269Sericheng 	/*
455269Sericheng 	 * Complete the detach when the driver is single-threaded.
456269Sericheng 	 */
457269Sericheng 	mutex_enter(&dsp->ds_thr_lock);
458269Sericheng 	ASSERT(dsp->ds_detach_req == NULL);
459269Sericheng 	dsp->ds_detach_req = mp;
460269Sericheng 	mutex_exit(&dsp->ds_thr_lock);
461269Sericheng 	rw_exit(&dsp->ds_lock);
462269Sericheng 
463269Sericheng 	return (B_TRUE);
464269Sericheng failed:
465269Sericheng 	rw_exit(&dsp->ds_lock);
466269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
467269Sericheng 	return (B_FALSE);
4680Sstevel@tonic-gate }
4690Sstevel@tonic-gate 
4700Sstevel@tonic-gate /*
471269Sericheng  * DL_BIND_REQ
4720Sstevel@tonic-gate  */
473269Sericheng static boolean_t
474269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4750Sstevel@tonic-gate {
476269Sericheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)udlp;
477269Sericheng 	int		err = 0;
478269Sericheng 	uint8_t		addr[MAXADDRLEN];
479269Sericheng 	uint_t		addr_length;
480269Sericheng 	t_uscalar_t	dl_err;
481269Sericheng 	t_scalar_t	sap;
482269Sericheng 	queue_t		*q = dsp->ds_wq;
483269Sericheng 
484269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
485269Sericheng 		dl_err = DL_BADPRIM;
486269Sericheng 		goto failed;
487269Sericheng 	}
488269Sericheng 
489269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
490269Sericheng 		dl_err = DL_NOAUTO;
491269Sericheng 		goto failed;
492269Sericheng 	}
493269Sericheng 
494269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
495269Sericheng 		dl_err = DL_UNSUPPORTED;
496269Sericheng 		goto failed;
497269Sericheng 	}
498269Sericheng 
499269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
500269Sericheng 
501269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
502269Sericheng 		dl_err = DL_OUTSTATE;
503269Sericheng 		goto failed;
504269Sericheng 	}
5050Sstevel@tonic-gate 
506269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
507269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
508269Sericheng 		dl_err = DL_SYSERR;
509269Sericheng 		err = EBUSY;
510269Sericheng 		goto failed;
511269Sericheng 	}
512269Sericheng 
513269Sericheng 	dsp->ds_dlstate = DL_BIND_PENDING;
514269Sericheng 	/*
515269Sericheng 	 * Set the receive callback.
516269Sericheng 	 */
517269Sericheng 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ?
518269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
5190Sstevel@tonic-gate 
520269Sericheng 	/*
521269Sericheng 	 * Bind the channel such that it can receive packets.
522269Sericheng 	 */
523269Sericheng 	sap = dsp->ds_sap = dlp->dl_sap;
524269Sericheng 	err = dls_bind(dsp->ds_dc, dlp->dl_sap);
525269Sericheng 	if (err != 0) {
526269Sericheng 		switch (err) {
527269Sericheng 		case EINVAL:
528269Sericheng 			dl_err = DL_BADADDR;
529269Sericheng 			err = 0;
530269Sericheng 			break;
531269Sericheng 		default:
532269Sericheng 			dl_err = DL_SYSERR;
533269Sericheng 			break;
534269Sericheng 		}
535269Sericheng 		dsp->ds_dlstate = DL_UNBOUND;
536269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
537269Sericheng 			dls_active_clear(dsp->ds_dc);
538269Sericheng 
5390Sstevel@tonic-gate 		goto failed;
540269Sericheng 	}
5410Sstevel@tonic-gate 
5420Sstevel@tonic-gate 	/*
5430Sstevel@tonic-gate 	 * Copy in MAC address.
5440Sstevel@tonic-gate 	 */
5450Sstevel@tonic-gate 	addr_length = dsp->ds_mip->mi_addr_length;
5460Sstevel@tonic-gate 	bcopy(dsp->ds_curr_addr, addr, addr_length);
5470Sstevel@tonic-gate 
5480Sstevel@tonic-gate 	/*
5490Sstevel@tonic-gate 	 * Copy in the DLSAP.
5500Sstevel@tonic-gate 	 */
5510Sstevel@tonic-gate 	*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
5520Sstevel@tonic-gate 	addr_length += sizeof (uint16_t);
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
555269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
556269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
5570Sstevel@tonic-gate 
558269Sericheng 	rw_exit(&dsp->ds_lock);
559269Sericheng 
560269Sericheng 	dlbindack(q, mp, sap, (void *)addr, addr_length, 0, 0);
561269Sericheng 	return (B_TRUE);
5620Sstevel@tonic-gate failed:
563269Sericheng 	rw_exit(&dsp->ds_lock);
564269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
565269Sericheng 	return (B_FALSE);
5660Sstevel@tonic-gate }
5670Sstevel@tonic-gate 
5680Sstevel@tonic-gate /*
569269Sericheng  * DL_UNBIND_REQ
5700Sstevel@tonic-gate  */
571269Sericheng /*ARGSUSED*/
572269Sericheng static boolean_t
573269Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
5740Sstevel@tonic-gate {
575269Sericheng 	queue_t		*q = dsp->ds_wq;
576269Sericheng 	t_uscalar_t	dl_err;
577269Sericheng 
578269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
579269Sericheng 
580269Sericheng 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
581269Sericheng 		dl_err = DL_BADPRIM;
582269Sericheng 		goto failed;
583269Sericheng 	}
584269Sericheng 
585269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
586269Sericheng 		dl_err = DL_OUTSTATE;
587269Sericheng 		goto failed;
588269Sericheng 	}
589269Sericheng 
590269Sericheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
591269Sericheng 
592269Sericheng 	/*
593269Sericheng 	 * Flush any remaining packets scheduled for transmission.
594269Sericheng 	 */
595269Sericheng 	dld_tx_flush(dsp);
596269Sericheng 
597269Sericheng 	/*
598269Sericheng 	 * Unbind the channel to stop packets being received.
599269Sericheng 	 */
600269Sericheng 	dls_unbind(dsp->ds_dc);
601269Sericheng 
602269Sericheng 	/*
603269Sericheng 	 * Disable polling mode, if it is enabled.
604269Sericheng 	 */
605269Sericheng 	proto_poll_disable(dsp);
606269Sericheng 
607269Sericheng 	/*
608269Sericheng 	 * Clear the receive callback.
609269Sericheng 	 */
610269Sericheng 	dls_rx_set(dsp->ds_dc, NULL, NULL);
611269Sericheng 
612269Sericheng 	/*
613269Sericheng 	 * Set the mode back to the default (unitdata).
614269Sericheng 	 */
615269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
616269Sericheng 
617*1184Skrgopi 	/*
618*1184Skrgopi 	 * If soft rings were enabled, the workers
619*1184Skrgopi 	 * should be quiesced. Start a task that will
620*1184Skrgopi 	 * get this in motion. We cannot check for
621*1184Skrgopi 	 * ds_soft_ring flag because
622*1184Skrgopi 	 * proto_soft_ring_disable() called from
623*1184Skrgopi 	 * proto_capability_req() would have reset it.
624*1184Skrgopi 	 */
625*1184Skrgopi 	if (dls_soft_ring_workers(dsp->ds_dc)) {
626*1184Skrgopi 		dsp->ds_unbind_req = mp;
627*1184Skrgopi 		dsp->ds_task_id = taskq_dispatch(system_taskq,
628*1184Skrgopi 		    proto_stop_soft_ring_threads, (void *)dsp, TQ_SLEEP);
629*1184Skrgopi 		rw_exit(&dsp->ds_lock);
630*1184Skrgopi 		return (B_TRUE);
631*1184Skrgopi 	}
632*1184Skrgopi 
6330Sstevel@tonic-gate 	dsp->ds_dlstate = DL_UNBOUND;
634269Sericheng 	rw_exit(&dsp->ds_lock);
635269Sericheng 
636269Sericheng 	dlokack(q, mp, DL_UNBIND_REQ);
637269Sericheng 	return (B_TRUE);
638269Sericheng failed:
639269Sericheng 	rw_exit(&dsp->ds_lock);
640269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
641269Sericheng 	return (B_FALSE);
6420Sstevel@tonic-gate }
6430Sstevel@tonic-gate 
6440Sstevel@tonic-gate /*
645269Sericheng  * DL_PROMISCON_REQ
6460Sstevel@tonic-gate  */
647269Sericheng static boolean_t
648269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6490Sstevel@tonic-gate {
650269Sericheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp;
651269Sericheng 	int		err = 0;
652269Sericheng 	t_uscalar_t	dl_err;
653269Sericheng 	uint32_t	promisc_saved;
654269Sericheng 	queue_t		*q = dsp->ds_wq;
655269Sericheng 
656269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
657269Sericheng 		dl_err = DL_BADPRIM;
658269Sericheng 		goto failed;
659269Sericheng 	}
660269Sericheng 
661269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
662269Sericheng 
663269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
664269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
665269Sericheng 		dl_err = DL_OUTSTATE;
6660Sstevel@tonic-gate 		goto failed;
667269Sericheng 	}
6680Sstevel@tonic-gate 
669269Sericheng 	promisc_saved = dsp->ds_promisc;
670269Sericheng 	switch (dlp->dl_level) {
671269Sericheng 	case DL_PROMISC_SAP:
672269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_SAP;
673269Sericheng 		break;
674269Sericheng 
675269Sericheng 	case DL_PROMISC_MULTI:
676269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_MULTI;
677269Sericheng 		break;
678269Sericheng 
679269Sericheng 	case DL_PROMISC_PHYS:
680269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_PHYS;
681269Sericheng 		break;
682269Sericheng 
683269Sericheng 	default:
684269Sericheng 		dl_err = DL_NOTSUPPORTED;
685269Sericheng 		goto failed;
686269Sericheng 	}
6870Sstevel@tonic-gate 
688269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
689269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
690269Sericheng 		dsp->ds_promisc = promisc_saved;
691269Sericheng 		dl_err = DL_SYSERR;
692269Sericheng 		err = EBUSY;
693269Sericheng 		goto failed;
694269Sericheng 	}
695269Sericheng 
696269Sericheng 	/*
697269Sericheng 	 * Adjust channel promiscuity.
698269Sericheng 	 */
699269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
700269Sericheng 	if (err != 0) {
701269Sericheng 		dl_err = DL_SYSERR;
702269Sericheng 		dsp->ds_promisc = promisc_saved;
703269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
704269Sericheng 			dls_active_clear(dsp->ds_dc);
705269Sericheng 
706269Sericheng 		goto failed;
707269Sericheng 	}
708269Sericheng 
709269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
710269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
711269Sericheng 
712269Sericheng 	rw_exit(&dsp->ds_lock);
713269Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
714269Sericheng 	return (B_TRUE);
7150Sstevel@tonic-gate failed:
716269Sericheng 	rw_exit(&dsp->ds_lock);
717269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
718269Sericheng 	return (B_FALSE);
7190Sstevel@tonic-gate }
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate /*
722269Sericheng  * DL_PROMISCOFF_REQ
7230Sstevel@tonic-gate  */
724269Sericheng static boolean_t
725269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
7260Sstevel@tonic-gate {
727269Sericheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp;
728269Sericheng 	int		err = 0;
729269Sericheng 	t_uscalar_t	dl_err;
730269Sericheng 	uint32_t	promisc_saved;
731269Sericheng 	queue_t		*q = dsp->ds_wq;
732269Sericheng 
733269Sericheng 
734269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
735269Sericheng 		dl_err = DL_BADPRIM;
7360Sstevel@tonic-gate 		goto failed;
737269Sericheng 	}
7380Sstevel@tonic-gate 
739269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
7400Sstevel@tonic-gate 
741269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
742269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
743269Sericheng 		dl_err = DL_OUTSTATE;
7440Sstevel@tonic-gate 		goto failed;
745269Sericheng 	}
7460Sstevel@tonic-gate 
747269Sericheng 	promisc_saved = dsp->ds_promisc;
748269Sericheng 	switch (dlp->dl_level) {
749269Sericheng 	case DL_PROMISC_SAP:
750269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
751269Sericheng 			dl_err = DL_NOTENAB;
752269Sericheng 			goto failed;
753269Sericheng 		}
754269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
7550Sstevel@tonic-gate 		break;
7560Sstevel@tonic-gate 
757269Sericheng 	case DL_PROMISC_MULTI:
758269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
759269Sericheng 			dl_err = DL_NOTENAB;
760269Sericheng 			goto failed;
761269Sericheng 		}
762269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
763269Sericheng 		break;
764269Sericheng 
765269Sericheng 	case DL_PROMISC_PHYS:
766269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
767269Sericheng 			dl_err = DL_NOTENAB;
768269Sericheng 			goto failed;
769269Sericheng 		}
770269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
7710Sstevel@tonic-gate 		break;
7720Sstevel@tonic-gate 
7730Sstevel@tonic-gate 	default:
774269Sericheng 		dl_err = DL_NOTSUPPORTED;
775269Sericheng 		goto failed;
776269Sericheng 	}
777269Sericheng 
778269Sericheng 	/*
779269Sericheng 	 * Adjust channel promiscuity.
780269Sericheng 	 */
781269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
782269Sericheng 	if (err != 0) {
783269Sericheng 		dsp->ds_promisc = promisc_saved;
7840Sstevel@tonic-gate 		dl_err = DL_SYSERR;
785269Sericheng 		goto failed;
786269Sericheng 	}
787269Sericheng 
788269Sericheng 	rw_exit(&dsp->ds_lock);
789269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
790269Sericheng 	return (B_TRUE);
791269Sericheng failed:
792269Sericheng 	rw_exit(&dsp->ds_lock);
793269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
794269Sericheng 	return (B_FALSE);
795269Sericheng }
796269Sericheng 
797269Sericheng /*
798269Sericheng  * DL_ENABMULTI_REQ
799269Sericheng  */
800269Sericheng static boolean_t
801269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
802269Sericheng {
803269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
804269Sericheng 	int		err = 0;
805269Sericheng 	t_uscalar_t	dl_err;
806269Sericheng 	queue_t		*q = dsp->ds_wq;
807269Sericheng 
808269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
809269Sericheng 
810269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
811269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
812269Sericheng 		dl_err = DL_OUTSTATE;
813269Sericheng 		goto failed;
814269Sericheng 	}
815269Sericheng 
816269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
817269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
818269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
819269Sericheng 		dl_err = DL_BADPRIM;
820269Sericheng 		goto failed;
821269Sericheng 	}
822269Sericheng 
823269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
824269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
825269Sericheng 		dl_err = DL_SYSERR;
826269Sericheng 		err = EBUSY;
827269Sericheng 		goto failed;
8280Sstevel@tonic-gate 	}
8290Sstevel@tonic-gate 
830269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
831269Sericheng 	if (err != 0) {
832269Sericheng 		switch (err) {
833269Sericheng 		case EINVAL:
834269Sericheng 			dl_err = DL_BADADDR;
835269Sericheng 			err = 0;
836269Sericheng 			break;
837269Sericheng 		case ENOSPC:
838269Sericheng 			dl_err = DL_TOOMANY;
839269Sericheng 			err = 0;
840269Sericheng 			break;
841269Sericheng 		default:
842269Sericheng 			dl_err = DL_SYSERR;
843269Sericheng 			break;
844269Sericheng 		}
845269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
846269Sericheng 			dls_active_clear(dsp->ds_dc);
847269Sericheng 
848269Sericheng 		goto failed;
849269Sericheng 	}
850269Sericheng 
851269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
852269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
853269Sericheng 
854269Sericheng 	rw_exit(&dsp->ds_lock);
855269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
856269Sericheng 	return (B_TRUE);
857269Sericheng failed:
858269Sericheng 	rw_exit(&dsp->ds_lock);
859269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
860269Sericheng 	return (B_FALSE);
861269Sericheng }
862269Sericheng 
863269Sericheng /*
864269Sericheng  * DL_DISABMULTI_REQ
865269Sericheng  */
866269Sericheng static boolean_t
867269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
868269Sericheng {
869269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
870269Sericheng 	int		err = 0;
871269Sericheng 	t_uscalar_t	dl_err;
872269Sericheng 	queue_t		*q = dsp->ds_wq;
873269Sericheng 
874269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
875269Sericheng 
876269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
877269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
878269Sericheng 		dl_err = DL_OUTSTATE;
879269Sericheng 		goto failed;
880269Sericheng 	}
881269Sericheng 
882269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
883269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
884269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
885269Sericheng 		dl_err = DL_BADPRIM;
886269Sericheng 		goto failed;
887269Sericheng 	}
888269Sericheng 
889269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
890269Sericheng 	if (err != 0) {
891269Sericheng 	switch (err) {
892269Sericheng 		case EINVAL:
893269Sericheng 			dl_err = DL_BADADDR;
894269Sericheng 			err = 0;
895269Sericheng 			break;
896269Sericheng 
897269Sericheng 		case ENOENT:
898269Sericheng 			dl_err = DL_NOTENAB;
899269Sericheng 			err = 0;
900269Sericheng 			break;
901269Sericheng 
902269Sericheng 		default:
903269Sericheng 			dl_err = DL_SYSERR;
904269Sericheng 			break;
905269Sericheng 		}
906269Sericheng 		goto failed;
907269Sericheng 	}
908269Sericheng 
909269Sericheng 	rw_exit(&dsp->ds_lock);
910269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
911269Sericheng 	return (B_TRUE);
912269Sericheng failed:
913269Sericheng 	rw_exit(&dsp->ds_lock);
914269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
915269Sericheng 	return (B_FALSE);
9160Sstevel@tonic-gate }
9170Sstevel@tonic-gate 
9180Sstevel@tonic-gate /*
919269Sericheng  * DL_PHYS_ADDR_REQ
9200Sstevel@tonic-gate  */
921269Sericheng static boolean_t
922269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
9230Sstevel@tonic-gate {
924269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
925269Sericheng 	queue_t		*q = dsp->ds_wq;
926269Sericheng 	t_uscalar_t	dl_err;
927269Sericheng 	char		*addr;
928269Sericheng 	uint_t		addr_length;
929269Sericheng 
930269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
931269Sericheng 
932269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
933269Sericheng 		dl_err = DL_BADPRIM;
934269Sericheng 		goto failed;
935269Sericheng 	}
936269Sericheng 
937269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
938269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
939269Sericheng 		dl_err = DL_OUTSTATE;
940269Sericheng 		goto failed;
941269Sericheng 	}
9420Sstevel@tonic-gate 
943269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
944269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
945269Sericheng 		dl_err = DL_UNSUPPORTED;
9460Sstevel@tonic-gate 		goto failed;
947269Sericheng 	}
9480Sstevel@tonic-gate 
949269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
950269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
951269Sericheng 	if (addr == NULL) {
952269Sericheng 		rw_exit(&dsp->ds_lock);
953269Sericheng 		merror(q, mp, ENOSR);
954269Sericheng 		return (B_FALSE);
955269Sericheng 	}
9560Sstevel@tonic-gate 
957269Sericheng 	/*
958269Sericheng 	 * Copy out the address before we drop the lock; we don't
959269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
960269Sericheng 	 */
961269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
962269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
963269Sericheng 
964269Sericheng 	rw_exit(&dsp->ds_lock);
965269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
966269Sericheng 	kmem_free(addr, addr_length);
967269Sericheng 	return (B_TRUE);
9680Sstevel@tonic-gate failed:
969269Sericheng 	rw_exit(&dsp->ds_lock);
970269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
971269Sericheng 	return (B_FALSE);
972269Sericheng }
9730Sstevel@tonic-gate 
974269Sericheng /*
975269Sericheng  * DL_SET_PHYS_ADDR_REQ
976269Sericheng  */
977269Sericheng static boolean_t
978269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
979269Sericheng {
980269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
981269Sericheng 	int		err = 0;
982269Sericheng 	t_uscalar_t	dl_err;
983269Sericheng 	queue_t		*q = dsp->ds_wq;
9840Sstevel@tonic-gate 
985269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
986269Sericheng 
987269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
988269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
989269Sericheng 		dl_err = DL_OUTSTATE;
990269Sericheng 		goto failed;
991269Sericheng 	}
992269Sericheng 
993269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
994269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
995269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
996269Sericheng 		dl_err = DL_BADPRIM;
997269Sericheng 		goto failed;
9980Sstevel@tonic-gate 	}
9990Sstevel@tonic-gate 
1000269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
1001269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
1002269Sericheng 		dl_err = DL_SYSERR;
1003269Sericheng 		err = EBUSY;
1004269Sericheng 		goto failed;
1005269Sericheng 	}
1006269Sericheng 
1007269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
1008269Sericheng 	if (err != 0) {
1009269Sericheng 		switch (err) {
1010269Sericheng 		case EINVAL:
1011269Sericheng 			dl_err = DL_BADADDR;
1012269Sericheng 			err = 0;
1013269Sericheng 			break;
1014269Sericheng 
1015269Sericheng 		default:
1016269Sericheng 			dl_err = DL_SYSERR;
1017269Sericheng 			break;
1018269Sericheng 		}
1019269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1020269Sericheng 			dls_active_clear(dsp->ds_dc);
1021269Sericheng 
1022269Sericheng 		goto failed;
1023269Sericheng 	}
1024269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1025269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
1026269Sericheng 
1027269Sericheng 	rw_exit(&dsp->ds_lock);
1028269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
1029269Sericheng 	return (B_TRUE);
1030269Sericheng failed:
1031269Sericheng 	rw_exit(&dsp->ds_lock);
1032269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
1033269Sericheng 	return (B_FALSE);
1034269Sericheng }
1035269Sericheng 
1036269Sericheng /*
1037269Sericheng  * DL_UDQOS_REQ
1038269Sericheng  */
1039269Sericheng static boolean_t
1040269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1041269Sericheng {
1042269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
1043269Sericheng 	dl_qos_cl_sel1_t *selp;
1044269Sericheng 	int		off, len;
1045269Sericheng 	t_uscalar_t	dl_err;
1046269Sericheng 	queue_t		*q = dsp->ds_wq;
1047269Sericheng 
1048269Sericheng 	off = dlp->dl_qos_offset;
1049269Sericheng 	len = dlp->dl_qos_length;
1050269Sericheng 
1051269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
1052269Sericheng 		dl_err = DL_BADPRIM;
1053269Sericheng 		goto failed;
1054269Sericheng 	}
1055269Sericheng 
1056269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1057269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1058269Sericheng 		dl_err = DL_BADQOSTYPE;
1059269Sericheng 		goto failed;
1060269Sericheng 	}
1061269Sericheng 
1062269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1063269Sericheng 
1064269Sericheng 	if (dsp->ds_vid == VLAN_ID_NONE ||
1065269Sericheng 	    selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1066269Sericheng 	    selp->dl_priority < 0) {
1067269Sericheng 		dl_err = DL_BADQOSPARAM;
1068269Sericheng 		goto failed;
1069269Sericheng 	}
1070269Sericheng 
1071269Sericheng 	dsp->ds_pri = selp->dl_priority;
1072269Sericheng 
1073269Sericheng 	rw_exit(&dsp->ds_lock);
1074269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1075269Sericheng 	return (B_TRUE);
1076269Sericheng failed:
1077269Sericheng 	rw_exit(&dsp->ds_lock);
1078269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1079269Sericheng 	return (B_FALSE);
10800Sstevel@tonic-gate }
10810Sstevel@tonic-gate 
1082*1184Skrgopi static boolean_t
1083*1184Skrgopi check_ip_above(queue_t *q)
1084*1184Skrgopi {
1085*1184Skrgopi 	queue_t		*next_q;
1086*1184Skrgopi 	boolean_t	ret = B_TRUE;
1087*1184Skrgopi 
1088*1184Skrgopi 	claimstr(q);
1089*1184Skrgopi 	next_q = q->q_next;
1090*1184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
1091*1184Skrgopi 		ret = B_FALSE;
1092*1184Skrgopi 	releasestr(q);
1093*1184Skrgopi 	return (ret);
1094*1184Skrgopi }
1095*1184Skrgopi 
10960Sstevel@tonic-gate /*
1097269Sericheng  * DL_CAPABILITY_REQ
10980Sstevel@tonic-gate  */
1099269Sericheng /*ARGSUSED*/
1100269Sericheng static boolean_t
1101269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
11020Sstevel@tonic-gate {
1103269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1104269Sericheng 	dl_capability_sub_t *sp;
1105269Sericheng 	size_t		size, len;
1106269Sericheng 	offset_t	off, end;
1107269Sericheng 	t_uscalar_t	dl_err;
1108269Sericheng 	queue_t		*q = dsp->ds_wq;
1109269Sericheng 	boolean_t	upgraded;
1110269Sericheng 
1111269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1112269Sericheng 
1113269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1114269Sericheng 		dl_err = DL_BADPRIM;
1115269Sericheng 		goto failed;
1116269Sericheng 	}
1117269Sericheng 
1118269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1119269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1120269Sericheng 		dl_err = DL_OUTSTATE;
1121269Sericheng 		goto failed;
1122269Sericheng 	}
1123269Sericheng 
1124269Sericheng 	/*
1125269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1126269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1127269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1128269Sericheng 	 */
1129269Sericheng 	if (dlp->dl_sub_length == 0) {
1130269Sericheng 		/* callee drops lock */
1131269Sericheng 		return (proto_capability_advertise(dsp, mp));
1132269Sericheng 	}
1133269Sericheng 
1134269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1135269Sericheng 		dl_err = DL_BADPRIM;
1136269Sericheng 		goto failed;
1137269Sericheng 	}
1138269Sericheng 
1139269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1140269Sericheng 
1141269Sericheng 	off = dlp->dl_sub_offset;
1142269Sericheng 	len = dlp->dl_sub_length;
11430Sstevel@tonic-gate 
11440Sstevel@tonic-gate 	/*
1145269Sericheng 	 * Walk the list of capabilities to be enabled.
11460Sstevel@tonic-gate 	 */
1147269Sericheng 	upgraded = B_FALSE;
1148269Sericheng 	for (end = off + len; off < end; ) {
1149269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1150269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1151269Sericheng 
1152269Sericheng 		if (off + size > end ||
1153269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1154269Sericheng 			dl_err = DL_BADPRIM;
1155269Sericheng 			goto failed;
1156269Sericheng 		}
1157269Sericheng 
1158269Sericheng 		switch (sp->dl_cap) {
1159269Sericheng 		/*
1160269Sericheng 		 * TCP/IP checksum offload to hardware.
1161269Sericheng 		 */
1162269Sericheng 		case DL_CAPAB_HCKSUM: {
1163269Sericheng 			dl_capab_hcksum_t *hcksump;
1164269Sericheng 			dl_capab_hcksum_t hcksum;
1165269Sericheng 
1166269Sericheng 			ASSERT(dsp->ds_mip->mi_cksum != 0);
1167269Sericheng 
1168269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1169269Sericheng 			/*
1170269Sericheng 			 * Copy for alignment.
1171269Sericheng 			 */
1172269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1173269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1174269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1175269Sericheng 			break;
1176269Sericheng 		}
1177269Sericheng 
1178269Sericheng 		/*
1179269Sericheng 		 * IP polling interface.
1180269Sericheng 		 */
1181269Sericheng 		case DL_CAPAB_POLL: {
1182*1184Skrgopi 			dl_capab_dls_t *pollp;
1183*1184Skrgopi 			dl_capab_dls_t	poll;
1184269Sericheng 
1185*1184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1186269Sericheng 			/*
1187269Sericheng 			 * Copy for alignment.
1188269Sericheng 			 */
1189*1184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1190269Sericheng 
1191269Sericheng 			/*
1192269Sericheng 			 * We need to become writer before enabling and/or
1193269Sericheng 			 * disabling the polling interface.  If we couldn'
1194269Sericheng 			 * upgrade, check state again after re-acquiring the
1195269Sericheng 			 * lock to make sure we can proceed.
1196269Sericheng 			 */
1197269Sericheng 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
1198269Sericheng 				rw_exit(&dsp->ds_lock);
1199269Sericheng 				rw_enter(&dsp->ds_lock, RW_WRITER);
1200269Sericheng 
1201269Sericheng 				if (dsp->ds_dlstate == DL_UNATTACHED ||
1202269Sericheng 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
1203269Sericheng 					dl_err = DL_OUTSTATE;
1204269Sericheng 					goto failed;
1205269Sericheng 				}
1206269Sericheng 			}
1207269Sericheng 			upgraded = B_TRUE;
1208269Sericheng 
1209*1184Skrgopi 			switch (poll.dls_flags) {
1210269Sericheng 			default:
1211269Sericheng 				/*FALLTHRU*/
1212269Sericheng 			case POLL_DISABLE:
1213269Sericheng 				proto_poll_disable(dsp);
1214269Sericheng 				break;
1215269Sericheng 
1216269Sericheng 			case POLL_ENABLE:
1217269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1218269Sericheng 
1219269Sericheng 				/*
1220269Sericheng 				 * Make sure polling is disabled.
1221269Sericheng 				 */
1222269Sericheng 				proto_poll_disable(dsp);
1223269Sericheng 
1224269Sericheng 				/*
1225269Sericheng 				 * Now attempt enable it.
1226269Sericheng 				 */
1227*1184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
1228*1184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
1229*1184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
1230*1184Skrgopi 					poll.dls_flags = POLL_ENABLE;
1231*1184Skrgopi 				}
1232269Sericheng 				break;
1233269Sericheng 			}
1234269Sericheng 
1235*1184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
1236*1184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
1237*1184Skrgopi 			break;
1238*1184Skrgopi 		}
1239*1184Skrgopi 		case DL_CAPAB_SOFT_RING: {
1240*1184Skrgopi 			dl_capab_dls_t *soft_ringp;
1241*1184Skrgopi 			dl_capab_dls_t soft_ring;
1242*1184Skrgopi 
1243*1184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
1244*1184Skrgopi 			/*
1245*1184Skrgopi 			 * Copy for alignment.
1246*1184Skrgopi 			 */
1247*1184Skrgopi 			bcopy(soft_ringp, &soft_ring,
1248*1184Skrgopi 			    sizeof (dl_capab_dls_t));
1249*1184Skrgopi 
1250*1184Skrgopi 			/*
1251*1184Skrgopi 			 * We need to become writer before enabling and/or
1252*1184Skrgopi 			 * disabling the soft_ring interface.  If we couldn'
1253*1184Skrgopi 			 * upgrade, check state again after re-acquiring the
1254*1184Skrgopi 			 * lock to make sure we can proceed.
1255*1184Skrgopi 			 */
1256*1184Skrgopi 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
1257*1184Skrgopi 				rw_exit(&dsp->ds_lock);
1258*1184Skrgopi 				rw_enter(&dsp->ds_lock, RW_WRITER);
1259*1184Skrgopi 
1260*1184Skrgopi 				if (dsp->ds_dlstate == DL_UNATTACHED ||
1261*1184Skrgopi 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
1262*1184Skrgopi 					dl_err = DL_OUTSTATE;
1263*1184Skrgopi 					goto failed;
1264*1184Skrgopi 				}
1265*1184Skrgopi 			}
1266*1184Skrgopi 			upgraded = B_TRUE;
1267*1184Skrgopi 
1268*1184Skrgopi 			switch (soft_ring.dls_flags) {
1269*1184Skrgopi 			default:
1270*1184Skrgopi 				/*FALLTHRU*/
1271*1184Skrgopi 			case SOFT_RING_DISABLE:
1272*1184Skrgopi 				proto_soft_ring_disable(dsp);
1273*1184Skrgopi 				break;
1274*1184Skrgopi 
1275*1184Skrgopi 			case SOFT_RING_ENABLE:
1276*1184Skrgopi 				/*
1277*1184Skrgopi 				 * Make sure soft_ring is disabled.
1278*1184Skrgopi 				 */
1279*1184Skrgopi 				proto_soft_ring_disable(dsp);
1280*1184Skrgopi 
1281*1184Skrgopi 				/*
1282*1184Skrgopi 				 * Now attempt enable it.
1283*1184Skrgopi 				 */
1284*1184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
1285*1184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
1286*1184Skrgopi 					bzero(&soft_ring,
1287*1184Skrgopi 					    sizeof (dl_capab_dls_t));
1288*1184Skrgopi 					soft_ring.dls_flags =
1289*1184Skrgopi 					    SOFT_RING_ENABLE;
1290*1184Skrgopi 				} else {
1291*1184Skrgopi 					bzero(&soft_ring,
1292*1184Skrgopi 					    sizeof (dl_capab_dls_t));
1293*1184Skrgopi 					soft_ring.dls_flags =
1294*1184Skrgopi 					    SOFT_RING_DISABLE;
1295*1184Skrgopi 				}
1296*1184Skrgopi 				break;
1297*1184Skrgopi 			}
1298*1184Skrgopi 
1299*1184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
1300*1184Skrgopi 			bcopy(&soft_ring, soft_ringp,
1301*1184Skrgopi 			    sizeof (dl_capab_dls_t));
1302269Sericheng 			break;
1303269Sericheng 		}
1304269Sericheng 		default:
1305269Sericheng 			break;
1306269Sericheng 		}
1307269Sericheng 
1308269Sericheng 		off += size;
1309269Sericheng 	}
1310269Sericheng 	rw_exit(&dsp->ds_lock);
1311269Sericheng 	qreply(q, mp);
1312269Sericheng 	return (B_TRUE);
1313269Sericheng failed:
1314269Sericheng 	rw_exit(&dsp->ds_lock);
1315269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1316269Sericheng 	return (B_FALSE);
13170Sstevel@tonic-gate }
13180Sstevel@tonic-gate 
13190Sstevel@tonic-gate /*
1320269Sericheng  * DL_NOTIFY_REQ
13210Sstevel@tonic-gate  */
1322269Sericheng static boolean_t
1323269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
13240Sstevel@tonic-gate {
1325269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1326269Sericheng 	t_uscalar_t	dl_err;
1327269Sericheng 	queue_t		*q = dsp->ds_wq;
1328269Sericheng 	uint_t		note =
1329269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1330269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1331269Sericheng 	    DL_NOTE_PHYS_ADDR |
1332269Sericheng 	    DL_NOTE_LINK_UP |
1333269Sericheng 	    DL_NOTE_LINK_DOWN |
1334269Sericheng 	    DL_NOTE_CAPAB_RENEG;
13350Sstevel@tonic-gate 
1336269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1337269Sericheng 		dl_err = DL_BADPRIM;
1338269Sericheng 		goto failed;
1339269Sericheng 	}
13400Sstevel@tonic-gate 
1341269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1342269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1343269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1344269Sericheng 		dl_err = DL_OUTSTATE;
1345269Sericheng 		goto failed;
13460Sstevel@tonic-gate 	}
13470Sstevel@tonic-gate 
1348269Sericheng 	if (dsp->ds_mip->mi_stat[MAC_STAT_IFSPEED])
1349269Sericheng 		note |= DL_NOTE_SPEED;
1350269Sericheng 
1351269Sericheng 	/*
1352269Sericheng 	 * Cache the notifications that are being enabled.
1353269Sericheng 	 */
1354269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1355269Sericheng 	rw_exit(&dsp->ds_lock);
1356269Sericheng 	/*
1357269Sericheng 	 * The ACK carries all notifications regardless of which set is
1358269Sericheng 	 * being enabled.
1359269Sericheng 	 */
1360269Sericheng 	dlnotifyack(q, mp, note);
1361269Sericheng 
1362269Sericheng 	/*
1363269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1364269Sericheng 	 */
1365269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1366269Sericheng 	if (dsp->ds_notifications != 0) {
1367269Sericheng 		rw_exit(&dsp->ds_lock);
1368269Sericheng 		dld_str_notify_ind(dsp);
1369269Sericheng 	} else {
1370269Sericheng 		rw_exit(&dsp->ds_lock);
1371269Sericheng 	}
1372269Sericheng 	return (B_TRUE);
1373269Sericheng failed:
1374269Sericheng 	rw_exit(&dsp->ds_lock);
1375269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1376269Sericheng 	return (B_FALSE);
13770Sstevel@tonic-gate }
13780Sstevel@tonic-gate 
13790Sstevel@tonic-gate /*
1380269Sericheng  * DL_UINTDATA_REQ
13810Sstevel@tonic-gate  */
1382269Sericheng static boolean_t
1383269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
13840Sstevel@tonic-gate {
1385269Sericheng 	queue_t			*q = dsp->ds_wq;
1386269Sericheng 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)udlp;
1387269Sericheng 	off_t			off;
1388269Sericheng 	size_t			len, size;
1389269Sericheng 	const uint8_t		*addr;
1390269Sericheng 	uint16_t		sap;
1391269Sericheng 	uint_t			addr_length;
1392269Sericheng 	mblk_t			*bp, *cont;
1393269Sericheng 	uint32_t		start, stuff, end, value, flags;
1394269Sericheng 	t_uscalar_t		dl_err;
1395269Sericheng 
1396269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1397269Sericheng 
1398269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1399269Sericheng 		dl_err = DL_BADPRIM;
1400269Sericheng 		goto failed;
1401269Sericheng 	}
1402269Sericheng 
1403269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
1404269Sericheng 		dl_err = DL_OUTSTATE;
1405269Sericheng 		goto failed;
1406269Sericheng 	}
1407269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1408269Sericheng 
1409269Sericheng 	off = dlp->dl_dest_addr_offset;
1410269Sericheng 	len = dlp->dl_dest_addr_length;
1411269Sericheng 
1412269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1413269Sericheng 		dl_err = DL_BADPRIM;
1414269Sericheng 		goto failed;
1415269Sericheng 	}
1416269Sericheng 
1417269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1418269Sericheng 		dl_err = DL_BADADDR;
1419269Sericheng 		goto failed;
1420269Sericheng 	}
1421269Sericheng 
1422269Sericheng 	addr = mp->b_rptr + off;
1423269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1424269Sericheng 
1425269Sericheng 	/*
1426269Sericheng 	 * Check the length of the packet and the block types.
1427269Sericheng 	 */
1428269Sericheng 	size = 0;
1429269Sericheng 	cont = mp->b_cont;
1430269Sericheng 	for (bp = cont; bp != NULL; bp = bp->b_cont) {
1431269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1432269Sericheng 			goto baddata;
1433269Sericheng 
1434269Sericheng 		size += MBLKL(bp);
1435269Sericheng 	}
1436269Sericheng 
1437269Sericheng 	if (size > dsp->ds_mip->mi_sdu_max)
1438269Sericheng 		goto baddata;
1439269Sericheng 
1440269Sericheng 	/*
1441269Sericheng 	 * Build a packet header.
1442269Sericheng 	 */
1443269Sericheng 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dsp->ds_pri)) == NULL) {
1444269Sericheng 		dl_err = DL_BADADDR;
1445269Sericheng 		goto failed;
1446269Sericheng 	}
1447269Sericheng 
1448269Sericheng 	/*
1449269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1450269Sericheng 	 */
1451269Sericheng 	freeb(mp);
1452269Sericheng 
1453269Sericheng 	/*
1454269Sericheng 	 * Transfer the checksum offload information if it is present.
1455269Sericheng 	 */
1456269Sericheng 	hcksum_retrieve(cont, NULL, NULL, &start, &stuff, &end, &value,
1457269Sericheng 	    &flags);
1458269Sericheng 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags,
1459269Sericheng 	    0);
1460269Sericheng 
1461269Sericheng 	/*
1462269Sericheng 	 * Link the payload onto the new header.
1463269Sericheng 	 */
1464269Sericheng 	ASSERT(bp->b_cont == NULL);
1465269Sericheng 	bp->b_cont = cont;
1466269Sericheng 
1467269Sericheng 	str_mdata_fastpath_put(dsp, bp);
1468269Sericheng 	rw_exit(&dsp->ds_lock);
1469269Sericheng 	return (B_TRUE);
1470269Sericheng failed:
1471269Sericheng 	rw_exit(&dsp->ds_lock);
1472269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1473269Sericheng 	return (B_FALSE);
1474269Sericheng 
1475269Sericheng baddata:
1476269Sericheng 	rw_exit(&dsp->ds_lock);
1477269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1478269Sericheng 	return (B_FALSE);
1479269Sericheng }
1480269Sericheng 
1481269Sericheng /*
1482269Sericheng  * DL_PASSIVE_REQ
1483269Sericheng  */
1484269Sericheng /* ARGSUSED */
1485269Sericheng static boolean_t
1486269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1487269Sericheng {
1488269Sericheng 	t_uscalar_t dl_err;
1489269Sericheng 
1490269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1491269Sericheng 	/*
1492269Sericheng 	 * If we've already become active by issuing an active primitive,
1493269Sericheng 	 * then it's too late to try to become passive.
1494269Sericheng 	 */
1495269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1496269Sericheng 		dl_err = DL_OUTSTATE;
1497269Sericheng 		goto failed;
1498269Sericheng 	}
1499269Sericheng 
1500269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1501269Sericheng 		dl_err = DL_BADPRIM;
1502269Sericheng 		goto failed;
1503269Sericheng 	}
1504269Sericheng 
1505269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1506269Sericheng 	rw_exit(&dsp->ds_lock);
1507269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1508269Sericheng 	return (B_TRUE);
1509269Sericheng failed:
1510269Sericheng 	rw_exit(&dsp->ds_lock);
1511269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1512269Sericheng 	return (B_FALSE);
1513269Sericheng }
1514269Sericheng 
1515269Sericheng 
1516269Sericheng /*
1517269Sericheng  * Catch-all handler.
1518269Sericheng  */
1519269Sericheng static boolean_t
1520269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1521269Sericheng {
1522269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1523269Sericheng 	return (B_FALSE);
15240Sstevel@tonic-gate }
15250Sstevel@tonic-gate 
15260Sstevel@tonic-gate static void
15270Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
15280Sstevel@tonic-gate {
15290Sstevel@tonic-gate 	mac_handle_t	mh;
15300Sstevel@tonic-gate 
1531269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
1532269Sericheng 
15330Sstevel@tonic-gate 	if (!dsp->ds_polling)
15340Sstevel@tonic-gate 		return;
15350Sstevel@tonic-gate 
15360Sstevel@tonic-gate 	/*
15370Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
15380Sstevel@tonic-gate 	 */
15390Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
15400Sstevel@tonic-gate 
15410Sstevel@tonic-gate 	/*
15420Sstevel@tonic-gate 	 * Reset the resource_add callback.
15430Sstevel@tonic-gate 	 */
15440Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
15450Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
1546*1184Skrgopi 	mac_resources(mh);
15470Sstevel@tonic-gate 
15480Sstevel@tonic-gate 	/*
15490Sstevel@tonic-gate 	 * Set receive function back to default.
15500Sstevel@tonic-gate 	 */
15510Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
15520Sstevel@tonic-gate 	    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
15530Sstevel@tonic-gate 
15540Sstevel@tonic-gate 	/*
15550Sstevel@tonic-gate 	 * Note that polling is disabled.
15560Sstevel@tonic-gate 	 */
15570Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
15580Sstevel@tonic-gate }
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate static boolean_t
1561*1184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
15620Sstevel@tonic-gate {
15630Sstevel@tonic-gate 	mac_handle_t	mh;
15640Sstevel@tonic-gate 
1565269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
15660Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
15670Sstevel@tonic-gate 
15680Sstevel@tonic-gate 	/*
15690Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
15700Sstevel@tonic-gate 	 * has been enabled.
15710Sstevel@tonic-gate 	 */
15720Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
15730Sstevel@tonic-gate 		return (B_FALSE);
15740Sstevel@tonic-gate 
15750Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
15760Sstevel@tonic-gate 
15770Sstevel@tonic-gate 	/*
15780Sstevel@tonic-gate 	 * Register resources.
15790Sstevel@tonic-gate 	 */
1580*1184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
1581*1184Skrgopi 	    (void *)pollp->dls_rx_handle);
15820Sstevel@tonic-gate 	mac_resources(mh);
15830Sstevel@tonic-gate 
15840Sstevel@tonic-gate 	/*
15850Sstevel@tonic-gate 	 * Set the receive function.
15860Sstevel@tonic-gate 	 */
1587*1184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
1588*1184Skrgopi 	    (void *)pollp->dls_rx_handle);
15890Sstevel@tonic-gate 
15900Sstevel@tonic-gate 	/*
15910Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
15920Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
15930Sstevel@tonic-gate 	 */
15940Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
15950Sstevel@tonic-gate 	return (B_TRUE);
15960Sstevel@tonic-gate }
15970Sstevel@tonic-gate 
1598*1184Skrgopi static void
1599*1184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
1600*1184Skrgopi {
1601*1184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
1602*1184Skrgopi 
1603*1184Skrgopi 	if (!dsp->ds_soft_ring)
1604*1184Skrgopi 		return;
1605*1184Skrgopi 
1606*1184Skrgopi 	/*
1607*1184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
1608*1184Skrgopi 	 */
1609*1184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
1610*1184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
1611*1184Skrgopi 	/*
1612*1184Skrgopi 	 * Note that fanout is disabled.
1613*1184Skrgopi 	 */
1614*1184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
1615*1184Skrgopi }
1616*1184Skrgopi 
1617*1184Skrgopi static boolean_t
1618*1184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
1619*1184Skrgopi {
1620*1184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
1621*1184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
1622*1184Skrgopi 
1623*1184Skrgopi 	/*
1624*1184Skrgopi 	 * We cannot enable soft_ring if raw mode
1625*1184Skrgopi 	 * has been enabled.
1626*1184Skrgopi 	 */
1627*1184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
1628*1184Skrgopi 		return (B_FALSE);
1629*1184Skrgopi 
1630*1184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
1631*1184Skrgopi 		return (B_FALSE);
1632*1184Skrgopi 
1633*1184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
1634*1184Skrgopi 	return (B_TRUE);
1635*1184Skrgopi }
1636*1184Skrgopi 
1637*1184Skrgopi static void
1638*1184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
1639*1184Skrgopi {
1640*1184Skrgopi 	dls_rx_t	rx;
1641*1184Skrgopi 
1642*1184Skrgopi 	if (type == SOFT_RING_NONE) {
1643*1184Skrgopi 		rx = (dsp->ds_mode == DLD_FASTPATH) ?
1644*1184Skrgopi 			    dld_str_rx_fastpath : dld_str_rx_unitdata;
1645*1184Skrgopi 	} else {
1646*1184Skrgopi 		rx = (dls_rx_t)dls_ether_soft_ring_fanout;
1647*1184Skrgopi 	}
1648*1184Skrgopi 	dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type);
1649*1184Skrgopi }
1650*1184Skrgopi 
1651*1184Skrgopi static void
1652*1184Skrgopi proto_stop_soft_ring_threads(void *arg)
1653*1184Skrgopi {
1654*1184Skrgopi 	dld_str_t	*dsp = (dld_str_t *)arg;
1655*1184Skrgopi 
1656*1184Skrgopi 	rw_enter(&dsp->ds_lock, RW_WRITER);
1657*1184Skrgopi 	dls_soft_ring_disable(dsp->ds_dc);
1658*1184Skrgopi 	dsp->ds_dlstate = DL_UNBOUND;
1659*1184Skrgopi 	rw_exit(&dsp->ds_lock);
1660*1184Skrgopi 	dlokack(dsp->ds_wq, dsp->ds_unbind_req, DL_UNBIND_REQ);
1661*1184Skrgopi 	rw_enter(&dsp->ds_lock, RW_WRITER);
1662*1184Skrgopi 	dsp->ds_task_id = NULL;
1663*1184Skrgopi 	rw_exit(&dsp->ds_lock);
1664*1184Skrgopi }
1665*1184Skrgopi 
16660Sstevel@tonic-gate /*
16670Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
16680Sstevel@tonic-gate  */
1669269Sericheng static boolean_t
1670269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
16710Sstevel@tonic-gate {
16720Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
16730Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
16740Sstevel@tonic-gate 	size_t			subsize;
1675*1184Skrgopi 	dl_capab_dls_t		poll;
1676*1184Skrgopi 	dl_capab_dls_t	soft_ring;
16770Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
16780Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
16790Sstevel@tonic-gate 	uint8_t			*ptr;
16800Sstevel@tonic-gate 	uint32_t		cksum;
16810Sstevel@tonic-gate 	boolean_t		poll_cap;
1682269Sericheng 	queue_t			*q = dsp->ds_wq;
1683269Sericheng 	mblk_t			*mp1;
1684269Sericheng 
1685269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
16860Sstevel@tonic-gate 
16870Sstevel@tonic-gate 	/*
16880Sstevel@tonic-gate 	 * Initially assume no capabilities.
16890Sstevel@tonic-gate 	 */
16900Sstevel@tonic-gate 	subsize = 0;
16910Sstevel@tonic-gate 
1692*1184Skrgopi 	/* Always advertize soft ring capability for GLDv3 drivers */
1693*1184Skrgopi 	subsize += sizeof (dl_capability_sub_t) + sizeof (dl_capab_dls_t);
1694*1184Skrgopi 
16950Sstevel@tonic-gate 	/*
16960Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
16970Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
16980Sstevel@tonic-gate 	 * then reserve space for that capability.
16990Sstevel@tonic-gate 	 */
17000Sstevel@tonic-gate 	poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) &&
17010Sstevel@tonic-gate 	    !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE));
17020Sstevel@tonic-gate 	if (poll_cap) {
17030Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
1704*1184Skrgopi 		    sizeof (dl_capab_dls_t);
17050Sstevel@tonic-gate 	}
17060Sstevel@tonic-gate 
17070Sstevel@tonic-gate 	/*
17080Sstevel@tonic-gate 	 * If the MAC interface supports checksum offload then reserve
17090Sstevel@tonic-gate 	 * space for the DL_CAPAB_HCKSUM capability.
17100Sstevel@tonic-gate 	 */
17110Sstevel@tonic-gate 	if ((cksum = dsp->ds_mip->mi_cksum) != 0) {
17120Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17130Sstevel@tonic-gate 		    sizeof (dl_capab_hcksum_t);
17140Sstevel@tonic-gate 	}
17150Sstevel@tonic-gate 
17160Sstevel@tonic-gate 	/*
17170Sstevel@tonic-gate 	 * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then
17180Sstevel@tonic-gate 	 * reserve space for it.
17190Sstevel@tonic-gate 	 */
17200Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
17210Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17220Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
17230Sstevel@tonic-gate 	}
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate 	/*
1726269Sericheng 	 * If there are no capabilities to advertise or if we
1727269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
17280Sstevel@tonic-gate 	 */
1729*1184Skrgopi 	if ((mp1 = reallocb(mp,
1730269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1731269Sericheng 		rw_exit(&dsp->ds_lock);
1732269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1733269Sericheng 		return (B_FALSE);
17340Sstevel@tonic-gate 	}
17350Sstevel@tonic-gate 
1736269Sericheng 	mp = mp1;
1737269Sericheng 	DB_TYPE(mp) = M_PROTO;
1738269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1739269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
17400Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
17410Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
17420Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
17430Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
17440Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
17450Sstevel@tonic-gate 
17460Sstevel@tonic-gate 	/*
17470Sstevel@tonic-gate 	 * IP polling interface.
17480Sstevel@tonic-gate 	 */
17490Sstevel@tonic-gate 	if (poll_cap) {
17500Sstevel@tonic-gate 		/*
1751269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
1752269Sericheng 		 * we need to become writer before doing so.
17530Sstevel@tonic-gate 		 */
1754269Sericheng 		if (!rw_tryupgrade(&dsp->ds_lock)) {
1755269Sericheng 			rw_exit(&dsp->ds_lock);
1756269Sericheng 			rw_enter(&dsp->ds_lock, RW_WRITER);
1757269Sericheng 		}
17580Sstevel@tonic-gate 
1759269Sericheng 		/*
1760269Sericheng 		 * Check if polling state has changed after we re-acquired
1761269Sericheng 		 * the lock above, so that we don't mis-advertise it.
1762269Sericheng 		 */
1763269Sericheng 		poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) &&
1764269Sericheng 		    !(dld_opt & DLD_OPT_NO_POLL) &&
1765269Sericheng 		    (dsp->ds_vid == VLAN_ID_NONE));
17660Sstevel@tonic-gate 
1767269Sericheng 		if (!poll_cap) {
1768269Sericheng 			int poll_capab_size;
1769269Sericheng 
1770269Sericheng 			rw_downgrade(&dsp->ds_lock);
1771269Sericheng 
1772269Sericheng 			poll_capab_size = sizeof (dl_capability_sub_t) +
1773*1184Skrgopi 			    sizeof (dl_capab_dls_t);
17740Sstevel@tonic-gate 
1775269Sericheng 			mp->b_wptr -= poll_capab_size;
1776269Sericheng 			subsize -= poll_capab_size;
1777269Sericheng 			dlap->dl_sub_length = subsize;
1778269Sericheng 		} else {
1779269Sericheng 			proto_poll_disable(dsp);
1780269Sericheng 
1781269Sericheng 			rw_downgrade(&dsp->ds_lock);
1782269Sericheng 
1783269Sericheng 			dlsp = (dl_capability_sub_t *)ptr;
1784269Sericheng 
1785269Sericheng 			dlsp->dl_cap = DL_CAPAB_POLL;
1786*1184Skrgopi 			dlsp->dl_length = sizeof (dl_capab_dls_t);
1787269Sericheng 			ptr += sizeof (dl_capability_sub_t);
17880Sstevel@tonic-gate 
1789*1184Skrgopi 			bzero(&poll, sizeof (dl_capab_dls_t));
1790*1184Skrgopi 			poll.dls_version = POLL_VERSION_1;
1791*1184Skrgopi 			poll.dls_flags = POLL_CAPABLE;
1792*1184Skrgopi 			poll.dls_tx_handle = (uintptr_t)dsp;
1793*1184Skrgopi 			poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
1794269Sericheng 
1795*1184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
1796*1184Skrgopi 			bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
1797*1184Skrgopi 			ptr += sizeof (dl_capab_dls_t);
1798269Sericheng 		}
17990Sstevel@tonic-gate 	}
18000Sstevel@tonic-gate 
1801269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
1802269Sericheng 
1803*1184Skrgopi 	dlsp = (dl_capability_sub_t *)ptr;
1804*1184Skrgopi 
1805*1184Skrgopi 	dlsp->dl_cap = DL_CAPAB_SOFT_RING;
1806*1184Skrgopi 	dlsp->dl_length = sizeof (dl_capab_dls_t);
1807*1184Skrgopi 	ptr += sizeof (dl_capability_sub_t);
1808*1184Skrgopi 
1809*1184Skrgopi 	bzero(&soft_ring, sizeof (dl_capab_dls_t));
1810*1184Skrgopi 	soft_ring.dls_version = SOFT_RING_VERSION_1;
1811*1184Skrgopi 	soft_ring.dls_flags = SOFT_RING_CAPABLE;
1812*1184Skrgopi 	soft_ring.dls_tx_handle = (uintptr_t)dsp;
1813*1184Skrgopi 	soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
1814*1184Skrgopi 	soft_ring.dls_ring_change_status =
1815*1184Skrgopi 	    (uintptr_t)proto_change_soft_ring_fanout;
1816*1184Skrgopi 	soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
1817*1184Skrgopi 	soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
1818*1184Skrgopi 
1819*1184Skrgopi 	dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
1820*1184Skrgopi 	bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
1821*1184Skrgopi 	ptr += sizeof (dl_capab_dls_t);
1822*1184Skrgopi 
18230Sstevel@tonic-gate 	/*
18240Sstevel@tonic-gate 	 * TCP/IP checksum offload.
18250Sstevel@tonic-gate 	 */
18260Sstevel@tonic-gate 	if (cksum != 0) {
18270Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18280Sstevel@tonic-gate 
18290Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
18300Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
18310Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18320Sstevel@tonic-gate 
18330Sstevel@tonic-gate 		bzero(&hcksum, sizeof (dl_capab_hcksum_t));
18340Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
18350Sstevel@tonic-gate 		hcksum.hcksum_txflags = cksum;
18360Sstevel@tonic-gate 
18370Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
18380Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
18390Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
18400Sstevel@tonic-gate 	}
18410Sstevel@tonic-gate 
18420Sstevel@tonic-gate 	/*
18430Sstevel@tonic-gate 	 * Zero copy
18440Sstevel@tonic-gate 	 */
18450Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
18460Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18470Sstevel@tonic-gate 
18480Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
18490Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
18500Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18510Sstevel@tonic-gate 
18520Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
18530Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
18540Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
18570Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
18580Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
18590Sstevel@tonic-gate 	}
18600Sstevel@tonic-gate 
18610Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1862269Sericheng 
1863269Sericheng 	rw_exit(&dsp->ds_lock);
1864269Sericheng 	qreply(q, mp);
1865269Sericheng 	return (B_TRUE);
18660Sstevel@tonic-gate }
1867