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
5*1502Sericheng  * Common Development and Distribution License (the "License").
6*1502Sericheng  * 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 /*
221353Sericheng  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Data-Link Driver
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/debug.h>
340Sstevel@tonic-gate #include <sys/sysmacros.h>
350Sstevel@tonic-gate #include <sys/stream.h>
360Sstevel@tonic-gate #include <sys/ddi.h>
370Sstevel@tonic-gate #include <sys/sunddi.h>
380Sstevel@tonic-gate #include <sys/strsun.h>
391184Skrgopi #include <sys/cpuvar.h>
400Sstevel@tonic-gate #include <sys/dlpi.h>
410Sstevel@tonic-gate #include <netinet/in.h>
420Sstevel@tonic-gate #include <sys/sdt.h>
430Sstevel@tonic-gate #include <sys/strsubr.h>
440Sstevel@tonic-gate #include <sys/vlan.h>
450Sstevel@tonic-gate #include <sys/mac.h>
460Sstevel@tonic-gate #include <sys/dls.h>
470Sstevel@tonic-gate #include <sys/dld.h>
480Sstevel@tonic-gate #include <sys/dld_impl.h>
491184Skrgopi #include <sys/dls_soft_ring.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate typedef boolean_t proto_reqfunc_t(dld_str_t *, union DL_primitives *, mblk_t *);
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
540Sstevel@tonic-gate     proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
550Sstevel@tonic-gate     proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
560Sstevel@tonic-gate     proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
570Sstevel@tonic-gate     proto_notify_req, proto_unitdata_req, proto_passive_req;
580Sstevel@tonic-gate 
59269Sericheng static void proto_poll_disable(dld_str_t *);
601184Skrgopi static boolean_t proto_poll_enable(dld_str_t *, dl_capab_dls_t *);
61269Sericheng static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
620Sstevel@tonic-gate 
631353Sericheng static task_func_t proto_process_unbind_req, proto_process_detach_req;
641353Sericheng 
651184Skrgopi static void proto_soft_ring_disable(dld_str_t *);
661184Skrgopi static boolean_t proto_soft_ring_enable(dld_str_t *, dl_capab_dls_t *);
671184Skrgopi static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
681184Skrgopi static void proto_change_soft_ring_fanout(dld_str_t *, int);
691184Skrgopi 
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 /*
1541353Sericheng  * Finish any pending operations.
1551353Sericheng  * Requests that need to be processed asynchronously will be handled
1561353Sericheng  * by a separate thread. After this function returns, other threads
1571353Sericheng  * will be allowed to enter dld; they will not be able to do anything
1581353Sericheng  * until ds_dlstate transitions to a non-pending state.
1590Sstevel@tonic-gate  */
160269Sericheng void
161269Sericheng dld_finish_pending_ops(dld_str_t *dsp)
1620Sstevel@tonic-gate {
1631353Sericheng 	task_func_t *op = NULL;
1641353Sericheng 
165269Sericheng 	ASSERT(MUTEX_HELD(&dsp->ds_thr_lock));
166269Sericheng 	ASSERT(dsp->ds_thr == 0);
1670Sstevel@tonic-gate 
1681353Sericheng 	op = dsp->ds_pending_op;
1691353Sericheng 	dsp->ds_pending_op = NULL;
1701353Sericheng 	mutex_exit(&dsp->ds_thr_lock);
1711353Sericheng 	if (op != NULL)
1721353Sericheng 		(void) taskq_dispatch(system_taskq, op, dsp, TQ_SLEEP);
1730Sstevel@tonic-gate }
1740Sstevel@tonic-gate 
175269Sericheng #define	NEG(x)	-(x)
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1780Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
1790Sstevel@tonic-gate 	uint8_t			dl_addr[MAXADDRLEN + sizeof (uint16_t)];
1800Sstevel@tonic-gate 	uint8_t			dl_brdcst_addr[MAXADDRLEN];
1810Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1820Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1830Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*
186269Sericheng  * DL_INFO_REQ
1870Sstevel@tonic-gate  */
188269Sericheng /*ARGSUSED*/
189269Sericheng static boolean_t
190269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1910Sstevel@tonic-gate {
1920Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1930Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
1940Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
1950Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
1960Sstevel@tonic-gate 	uint8_t			*addr;
1970Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
1980Sstevel@tonic-gate 	uint_t			addr_length;
1990Sstevel@tonic-gate 	uint_t			sap_length;
200269Sericheng 	mac_info_t		minfo;
201269Sericheng 	mac_info_t		*minfop;
202269Sericheng 	queue_t			*q = dsp->ds_wq;
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	/*
2050Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
2060Sstevel@tonic-gate 	 * wrapper structure defined above.
2070Sstevel@tonic-gate 	 */
208269Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
2090Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
210269Sericheng 		return (B_FALSE);
211269Sericheng 
212269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
2150Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
2180Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	/*
2230Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
2240Sstevel@tonic-gate 	 */
2250Sstevel@tonic-gate 	addr = dlwp->dl_addr;
2260Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
2270Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
2280Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	/*
2310Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
2320Sstevel@tonic-gate 	 * nodes.
2330Sstevel@tonic-gate 	 */
2340Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
2350Sstevel@tonic-gate 	dlp->dl_version = DL_VERSION_2;
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	/*
238269Sericheng 	 * Set the style of the provider
2390Sstevel@tonic-gate 	 */
240269Sericheng 	dlp->dl_provider_style = dsp->ds_style;
2410Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
2420Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 	/*
2450Sstevel@tonic-gate 	 * Set the current DLPI state.
2460Sstevel@tonic-gate 	 */
2470Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	/*
250269Sericheng 	 * Gratuitously set the media type. This is to deal with modules
251269Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
2520Sstevel@tonic-gate 	 * being completed.
2530Sstevel@tonic-gate 	 */
2540Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	/*
257269Sericheng 	 * If the stream is not at least attached we try to retrieve the
258269Sericheng 	 * mac_info using mac_info_get()
2590Sstevel@tonic-gate 	 */
2600Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2610Sstevel@tonic-gate 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
262269Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
263269Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
264269Sericheng 			/*
265269Sericheng 			 * Cannot find mac_info. giving up.
266269Sericheng 			 */
267269Sericheng 			goto done;
268269Sericheng 		}
269269Sericheng 		minfop = &minfo;
270269Sericheng 	} else {
271269Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
272269Sericheng 	}
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	/*
2750Sstevel@tonic-gate 	 * Set the media type (properly this time).
2760Sstevel@tonic-gate 	 */
277269Sericheng 	dlp->dl_mac_type = minfop->mi_media;
2780Sstevel@tonic-gate 
2790Sstevel@tonic-gate 	/*
2800Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2810Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2820Sstevel@tonic-gate 	 */
2830Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2840Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2850Sstevel@tonic-gate 
2860Sstevel@tonic-gate 	/*
2870Sstevel@tonic-gate 	 * Set the minimum and maximum payload sizes.
2880Sstevel@tonic-gate 	 */
289269Sericheng 	dlp->dl_min_sdu = minfop->mi_sdu_min;
290269Sericheng 	dlp->dl_max_sdu = minfop->mi_sdu_max;
2910Sstevel@tonic-gate 
292269Sericheng 	addr_length = minfop->mi_addr_length;
2930Sstevel@tonic-gate 	ASSERT(addr_length != 0);
2940Sstevel@tonic-gate 
2950Sstevel@tonic-gate 	/*
2960Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2970Sstevel@tonic-gate 	 */
2980Sstevel@tonic-gate 	dlp->dl_brdcst_addr_offset = (uintptr_t)brdcst_addr - (uintptr_t)dlp;
299269Sericheng 	bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
3000Sstevel@tonic-gate 	dlp->dl_brdcst_addr_length = addr_length;
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	/*
3030Sstevel@tonic-gate 	 * We only support QoS information for VLAN interfaces.
3040Sstevel@tonic-gate 	 */
3050Sstevel@tonic-gate 	if (dsp->ds_vid != VLAN_ID_NONE) {
3060Sstevel@tonic-gate 		dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
3070Sstevel@tonic-gate 		dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
3080Sstevel@tonic-gate 
3090Sstevel@tonic-gate 		rangep->dl_qos_type = DL_QOS_CL_RANGE1;
3100Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
3110Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
3120Sstevel@tonic-gate 		rangep->dl_protection.dl_min = DL_UNKNOWN;
3130Sstevel@tonic-gate 		rangep->dl_protection.dl_max = DL_UNKNOWN;
3140Sstevel@tonic-gate 		rangep->dl_residual_error = DL_UNKNOWN;
3150Sstevel@tonic-gate 
3160Sstevel@tonic-gate 		/*
3170Sstevel@tonic-gate 		 * Specify the supported range of priorities.
3180Sstevel@tonic-gate 		 */
3190Sstevel@tonic-gate 		rangep->dl_priority.dl_min = 0;
3200Sstevel@tonic-gate 		rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
3210Sstevel@tonic-gate 
3220Sstevel@tonic-gate 		dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
3230Sstevel@tonic-gate 		dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 		selp->dl_qos_type = DL_QOS_CL_SEL1;
3260Sstevel@tonic-gate 		selp->dl_trans_delay = DL_UNKNOWN;
3270Sstevel@tonic-gate 		selp->dl_protection = DL_UNKNOWN;
3280Sstevel@tonic-gate 		selp->dl_residual_error = DL_UNKNOWN;
3290Sstevel@tonic-gate 
3300Sstevel@tonic-gate 		/*
3310Sstevel@tonic-gate 		 * Specify the current priority (which can be changed by
3320Sstevel@tonic-gate 		 * the DL_UDQOS_REQ primitive).
3330Sstevel@tonic-gate 		 */
3340Sstevel@tonic-gate 		selp->dl_priority = dsp->ds_pri;
3350Sstevel@tonic-gate 	} else {
3360Sstevel@tonic-gate 		/*
3370Sstevel@tonic-gate 		 * Shorten the buffer to lose the unused QoS information
338269Sericheng 		 * structures.
3390Sstevel@tonic-gate 		 */
3400Sstevel@tonic-gate 		mp->b_wptr = (uint8_t *)rangep;
3410Sstevel@tonic-gate 	}
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
3440Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
3450Sstevel@tonic-gate 		/*
3460Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
3470Sstevel@tonic-gate 		 * DLSAP address.
3480Sstevel@tonic-gate 		 */
3490Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
3500Sstevel@tonic-gate 		bcopy(dsp->ds_curr_addr, addr, addr_length);
3510Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3520Sstevel@tonic-gate 	}
3530Sstevel@tonic-gate 
3540Sstevel@tonic-gate done:
3550Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
3560Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
3570Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3580Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3590Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3600Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3610Sstevel@tonic-gate 
362269Sericheng 	rw_exit(&dsp->ds_lock);
363269Sericheng 
364269Sericheng 	qreply(q, mp);
365269Sericheng 	return (B_TRUE);
366269Sericheng }
367269Sericheng 
368269Sericheng /*
369269Sericheng  * DL_ATTACH_REQ
370269Sericheng  */
371269Sericheng static boolean_t
372269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
373269Sericheng {
374269Sericheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)udlp;
375269Sericheng 	int		err = 0;
376269Sericheng 	t_uscalar_t	dl_err;
377269Sericheng 	queue_t		*q = dsp->ds_wq;
378269Sericheng 
379269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
380269Sericheng 
381269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
382269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
383269Sericheng 		dl_err = DL_BADPRIM;
384269Sericheng 		goto failed;
385269Sericheng 	}
386269Sericheng 
387269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
388269Sericheng 		dl_err = DL_OUTSTATE;
389269Sericheng 		goto failed;
390269Sericheng 	}
391269Sericheng 
392269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
393269Sericheng 
394269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
395269Sericheng 	if (err != 0) {
396269Sericheng 		switch (err) {
397269Sericheng 		case ENOENT:
398269Sericheng 			dl_err = DL_BADPPA;
399269Sericheng 			err = 0;
400269Sericheng 			break;
401269Sericheng 		default:
402269Sericheng 			dl_err = DL_SYSERR;
403269Sericheng 			break;
404269Sericheng 		}
405269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
406269Sericheng 		goto failed;
407269Sericheng 	}
408269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
409269Sericheng 	rw_exit(&dsp->ds_lock);
410269Sericheng 
411269Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
412269Sericheng 	return (B_TRUE);
413269Sericheng failed:
414269Sericheng 	rw_exit(&dsp->ds_lock);
415269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
416269Sericheng 	return (B_FALSE);
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate /*
420269Sericheng  * DL_DETACH_REQ
4210Sstevel@tonic-gate  */
4221353Sericheng static void
4231353Sericheng proto_process_detach_req(void *arg)
4241353Sericheng {
4251353Sericheng 	dld_str_t	*dsp = arg;
4261353Sericheng 	mblk_t		*mp;
4271353Sericheng 
4281353Sericheng 	/*
4291353Sericheng 	 * We don't need to hold locks because no other thread
4301353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
4311353Sericheng 	 */
4321353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
4331353Sericheng 	ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING);
4341353Sericheng 
4351353Sericheng 	mp = dsp->ds_pending_req;
4361353Sericheng 	dsp->ds_pending_req = NULL;
4371353Sericheng 	dld_str_detach(dsp);
4381353Sericheng 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
4391353Sericheng 
4401353Sericheng 	DLD_WAKEUP(dsp);
4411353Sericheng }
4421353Sericheng 
443269Sericheng /*ARGSUSED*/
444269Sericheng static boolean_t
445269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4460Sstevel@tonic-gate {
447269Sericheng 	queue_t		*q = dsp->ds_wq;
448269Sericheng 	t_uscalar_t	dl_err;
4490Sstevel@tonic-gate 
450269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
4510Sstevel@tonic-gate 
452269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
453269Sericheng 		dl_err = DL_BADPRIM;
454269Sericheng 		goto failed;
455269Sericheng 	}
4560Sstevel@tonic-gate 
457269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
458269Sericheng 		dl_err = DL_OUTSTATE;
459269Sericheng 		goto failed;
4600Sstevel@tonic-gate 	}
4610Sstevel@tonic-gate 
462269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
463269Sericheng 		dl_err = DL_BADPRIM;
464269Sericheng 		goto failed;
465269Sericheng 	}
466269Sericheng 
467269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
468269Sericheng 
469269Sericheng 	/*
470269Sericheng 	 * Complete the detach when the driver is single-threaded.
471269Sericheng 	 */
472269Sericheng 	mutex_enter(&dsp->ds_thr_lock);
4731353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
4741353Sericheng 	dsp->ds_pending_req = mp;
4751353Sericheng 	dsp->ds_pending_op = proto_process_detach_req;
4761353Sericheng 	dsp->ds_pending_cnt++;
477269Sericheng 	mutex_exit(&dsp->ds_thr_lock);
478269Sericheng 	rw_exit(&dsp->ds_lock);
479269Sericheng 
480269Sericheng 	return (B_TRUE);
481269Sericheng failed:
482269Sericheng 	rw_exit(&dsp->ds_lock);
483269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
484269Sericheng 	return (B_FALSE);
4850Sstevel@tonic-gate }
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate /*
488269Sericheng  * DL_BIND_REQ
4890Sstevel@tonic-gate  */
490269Sericheng static boolean_t
491269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4920Sstevel@tonic-gate {
493269Sericheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)udlp;
494269Sericheng 	int		err = 0;
495269Sericheng 	uint8_t		addr[MAXADDRLEN];
496269Sericheng 	uint_t		addr_length;
497269Sericheng 	t_uscalar_t	dl_err;
498269Sericheng 	t_scalar_t	sap;
499269Sericheng 	queue_t		*q = dsp->ds_wq;
500269Sericheng 
501269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
502269Sericheng 		dl_err = DL_BADPRIM;
503269Sericheng 		goto failed;
504269Sericheng 	}
505269Sericheng 
506269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
507269Sericheng 		dl_err = DL_NOAUTO;
508269Sericheng 		goto failed;
509269Sericheng 	}
510269Sericheng 
511269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
512269Sericheng 		dl_err = DL_UNSUPPORTED;
513269Sericheng 		goto failed;
514269Sericheng 	}
515269Sericheng 
516269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
517269Sericheng 
518269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
519269Sericheng 		dl_err = DL_OUTSTATE;
520269Sericheng 		goto failed;
521269Sericheng 	}
5220Sstevel@tonic-gate 
523269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
524269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
525269Sericheng 		dl_err = DL_SYSERR;
526269Sericheng 		err = EBUSY;
527269Sericheng 		goto failed;
528269Sericheng 	}
529269Sericheng 
530269Sericheng 	dsp->ds_dlstate = DL_BIND_PENDING;
531269Sericheng 	/*
532269Sericheng 	 * Set the receive callback.
533269Sericheng 	 */
534269Sericheng 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ?
535269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
5360Sstevel@tonic-gate 
537269Sericheng 	/*
538269Sericheng 	 * Bind the channel such that it can receive packets.
539269Sericheng 	 */
540269Sericheng 	sap = dsp->ds_sap = dlp->dl_sap;
541269Sericheng 	err = dls_bind(dsp->ds_dc, dlp->dl_sap);
542269Sericheng 	if (err != 0) {
543269Sericheng 		switch (err) {
544269Sericheng 		case EINVAL:
545269Sericheng 			dl_err = DL_BADADDR;
546269Sericheng 			err = 0;
547269Sericheng 			break;
548269Sericheng 		default:
549269Sericheng 			dl_err = DL_SYSERR;
550269Sericheng 			break;
551269Sericheng 		}
552269Sericheng 		dsp->ds_dlstate = DL_UNBOUND;
553269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
554269Sericheng 			dls_active_clear(dsp->ds_dc);
555269Sericheng 
5560Sstevel@tonic-gate 		goto failed;
557269Sericheng 	}
5580Sstevel@tonic-gate 
5590Sstevel@tonic-gate 	/*
5600Sstevel@tonic-gate 	 * Copy in MAC address.
5610Sstevel@tonic-gate 	 */
5620Sstevel@tonic-gate 	addr_length = dsp->ds_mip->mi_addr_length;
5630Sstevel@tonic-gate 	bcopy(dsp->ds_curr_addr, addr, addr_length);
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate 	/*
5660Sstevel@tonic-gate 	 * Copy in the DLSAP.
5670Sstevel@tonic-gate 	 */
5680Sstevel@tonic-gate 	*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
5690Sstevel@tonic-gate 	addr_length += sizeof (uint16_t);
5700Sstevel@tonic-gate 
5710Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
572269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
573269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
5740Sstevel@tonic-gate 
575269Sericheng 	rw_exit(&dsp->ds_lock);
576269Sericheng 
577269Sericheng 	dlbindack(q, mp, sap, (void *)addr, addr_length, 0, 0);
578269Sericheng 	return (B_TRUE);
5790Sstevel@tonic-gate failed:
580269Sericheng 	rw_exit(&dsp->ds_lock);
581269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
582269Sericheng 	return (B_FALSE);
5830Sstevel@tonic-gate }
5840Sstevel@tonic-gate 
5850Sstevel@tonic-gate /*
586269Sericheng  * DL_UNBIND_REQ
5870Sstevel@tonic-gate  */
588269Sericheng /*ARGSUSED*/
5891353Sericheng static void
5901353Sericheng proto_process_unbind_req(void *arg)
5910Sstevel@tonic-gate {
5921353Sericheng 	dld_str_t	*dsp = arg;
5931353Sericheng 	mblk_t		*mp;
594269Sericheng 
5951353Sericheng 	/*
5961353Sericheng 	 * We don't need to hold locks because no other thread
5971353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
5981353Sericheng 	 */
5991353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
6001353Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBIND_PENDING);
601269Sericheng 
602269Sericheng 	/*
603269Sericheng 	 * Flush any remaining packets scheduled for transmission.
604269Sericheng 	 */
605269Sericheng 	dld_tx_flush(dsp);
606269Sericheng 
607269Sericheng 	/*
608269Sericheng 	 * Unbind the channel to stop packets being received.
609269Sericheng 	 */
610269Sericheng 	dls_unbind(dsp->ds_dc);
611269Sericheng 
612269Sericheng 	/*
613269Sericheng 	 * Disable polling mode, if it is enabled.
614269Sericheng 	 */
615269Sericheng 	proto_poll_disable(dsp);
616269Sericheng 
617269Sericheng 	/*
618269Sericheng 	 * Clear the receive callback.
619269Sericheng 	 */
620269Sericheng 	dls_rx_set(dsp->ds_dc, NULL, NULL);
621269Sericheng 
622269Sericheng 	/*
623269Sericheng 	 * Set the mode back to the default (unitdata).
624269Sericheng 	 */
625269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
626269Sericheng 
6271184Skrgopi 	/*
6281184Skrgopi 	 * If soft rings were enabled, the workers
6291353Sericheng 	 * should be quiesced. We cannot check for
6301184Skrgopi 	 * ds_soft_ring flag because
6311184Skrgopi 	 * proto_soft_ring_disable() called from
6321184Skrgopi 	 * proto_capability_req() would have reset it.
6331184Skrgopi 	 */
6341353Sericheng 	if (dls_soft_ring_workers(dsp->ds_dc))
6351353Sericheng 		dls_soft_ring_disable(dsp->ds_dc);
6361353Sericheng 
6371353Sericheng 	mp = dsp->ds_pending_req;
6381353Sericheng 	dsp->ds_pending_req = NULL;
6391353Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
6401353Sericheng 	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
6411353Sericheng 
6421353Sericheng 	DLD_WAKEUP(dsp);
6431353Sericheng }
6441353Sericheng 
6451353Sericheng /*ARGSUSED*/
6461353Sericheng static boolean_t
6471353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6481353Sericheng {
6491353Sericheng 	queue_t		*q = dsp->ds_wq;
6501353Sericheng 	t_uscalar_t	dl_err;
6511353Sericheng 
6521353Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
6531353Sericheng 
6541353Sericheng 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
6551353Sericheng 		dl_err = DL_BADPRIM;
6561353Sericheng 		goto failed;
6571184Skrgopi 	}
6581184Skrgopi 
6591353Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
6601353Sericheng 		dl_err = DL_OUTSTATE;
6611353Sericheng 		goto failed;
6621353Sericheng 	}
6631353Sericheng 
6641353Sericheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
6651353Sericheng 
6661353Sericheng 	mutex_enter(&dsp->ds_thr_lock);
6671353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
6681353Sericheng 	dsp->ds_pending_req = mp;
6691353Sericheng 	dsp->ds_pending_op = proto_process_unbind_req;
6701353Sericheng 	dsp->ds_pending_cnt++;
6711353Sericheng 	mutex_exit(&dsp->ds_thr_lock);
672269Sericheng 	rw_exit(&dsp->ds_lock);
673269Sericheng 
674269Sericheng 	return (B_TRUE);
675269Sericheng failed:
676269Sericheng 	rw_exit(&dsp->ds_lock);
677269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
678269Sericheng 	return (B_FALSE);
6790Sstevel@tonic-gate }
6800Sstevel@tonic-gate 
6810Sstevel@tonic-gate /*
682269Sericheng  * DL_PROMISCON_REQ
6830Sstevel@tonic-gate  */
684269Sericheng static boolean_t
685269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6860Sstevel@tonic-gate {
687269Sericheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp;
688269Sericheng 	int		err = 0;
689269Sericheng 	t_uscalar_t	dl_err;
690269Sericheng 	uint32_t	promisc_saved;
691269Sericheng 	queue_t		*q = dsp->ds_wq;
692269Sericheng 
693269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
694269Sericheng 		dl_err = DL_BADPRIM;
695269Sericheng 		goto failed;
696269Sericheng 	}
697269Sericheng 
698269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
699269Sericheng 
700269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
701269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
702269Sericheng 		dl_err = DL_OUTSTATE;
7030Sstevel@tonic-gate 		goto failed;
704269Sericheng 	}
7050Sstevel@tonic-gate 
706269Sericheng 	promisc_saved = dsp->ds_promisc;
707269Sericheng 	switch (dlp->dl_level) {
708269Sericheng 	case DL_PROMISC_SAP:
709269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_SAP;
710269Sericheng 		break;
711269Sericheng 
712269Sericheng 	case DL_PROMISC_MULTI:
713269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_MULTI;
714269Sericheng 		break;
715269Sericheng 
716269Sericheng 	case DL_PROMISC_PHYS:
717269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_PHYS;
718269Sericheng 		break;
719269Sericheng 
720269Sericheng 	default:
721269Sericheng 		dl_err = DL_NOTSUPPORTED;
722269Sericheng 		goto failed;
723269Sericheng 	}
7240Sstevel@tonic-gate 
725269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
726269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
727269Sericheng 		dsp->ds_promisc = promisc_saved;
728269Sericheng 		dl_err = DL_SYSERR;
729269Sericheng 		err = EBUSY;
730269Sericheng 		goto failed;
731269Sericheng 	}
732269Sericheng 
733269Sericheng 	/*
734269Sericheng 	 * Adjust channel promiscuity.
735269Sericheng 	 */
736269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
737269Sericheng 	if (err != 0) {
738269Sericheng 		dl_err = DL_SYSERR;
739269Sericheng 		dsp->ds_promisc = promisc_saved;
740269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
741269Sericheng 			dls_active_clear(dsp->ds_dc);
742269Sericheng 
743269Sericheng 		goto failed;
744269Sericheng 	}
745269Sericheng 
746269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
747269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
748269Sericheng 
749269Sericheng 	rw_exit(&dsp->ds_lock);
750269Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
751269Sericheng 	return (B_TRUE);
7520Sstevel@tonic-gate failed:
753269Sericheng 	rw_exit(&dsp->ds_lock);
754269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
755269Sericheng 	return (B_FALSE);
7560Sstevel@tonic-gate }
7570Sstevel@tonic-gate 
7580Sstevel@tonic-gate /*
759269Sericheng  * DL_PROMISCOFF_REQ
7600Sstevel@tonic-gate  */
761269Sericheng static boolean_t
762269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
7630Sstevel@tonic-gate {
764269Sericheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp;
765269Sericheng 	int		err = 0;
766269Sericheng 	t_uscalar_t	dl_err;
767269Sericheng 	uint32_t	promisc_saved;
768269Sericheng 	queue_t		*q = dsp->ds_wq;
769269Sericheng 
770269Sericheng 
771269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
772269Sericheng 		dl_err = DL_BADPRIM;
7730Sstevel@tonic-gate 		goto failed;
774269Sericheng 	}
7750Sstevel@tonic-gate 
776269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
7770Sstevel@tonic-gate 
778269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
779269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
780269Sericheng 		dl_err = DL_OUTSTATE;
7810Sstevel@tonic-gate 		goto failed;
782269Sericheng 	}
7830Sstevel@tonic-gate 
784269Sericheng 	promisc_saved = dsp->ds_promisc;
785269Sericheng 	switch (dlp->dl_level) {
786269Sericheng 	case DL_PROMISC_SAP:
787269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
788269Sericheng 			dl_err = DL_NOTENAB;
789269Sericheng 			goto failed;
790269Sericheng 		}
791269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
7920Sstevel@tonic-gate 		break;
7930Sstevel@tonic-gate 
794269Sericheng 	case DL_PROMISC_MULTI:
795269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
796269Sericheng 			dl_err = DL_NOTENAB;
797269Sericheng 			goto failed;
798269Sericheng 		}
799269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
800269Sericheng 		break;
801269Sericheng 
802269Sericheng 	case DL_PROMISC_PHYS:
803269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
804269Sericheng 			dl_err = DL_NOTENAB;
805269Sericheng 			goto failed;
806269Sericheng 		}
807269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
8080Sstevel@tonic-gate 		break;
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 	default:
811269Sericheng 		dl_err = DL_NOTSUPPORTED;
812269Sericheng 		goto failed;
813269Sericheng 	}
814269Sericheng 
815269Sericheng 	/*
816269Sericheng 	 * Adjust channel promiscuity.
817269Sericheng 	 */
818269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
819269Sericheng 	if (err != 0) {
820269Sericheng 		dsp->ds_promisc = promisc_saved;
8210Sstevel@tonic-gate 		dl_err = DL_SYSERR;
822269Sericheng 		goto failed;
823269Sericheng 	}
824269Sericheng 
825269Sericheng 	rw_exit(&dsp->ds_lock);
826269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
827269Sericheng 	return (B_TRUE);
828269Sericheng failed:
829269Sericheng 	rw_exit(&dsp->ds_lock);
830269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
831269Sericheng 	return (B_FALSE);
832269Sericheng }
833269Sericheng 
834269Sericheng /*
835269Sericheng  * DL_ENABMULTI_REQ
836269Sericheng  */
837269Sericheng static boolean_t
838269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
839269Sericheng {
840269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
841269Sericheng 	int		err = 0;
842269Sericheng 	t_uscalar_t	dl_err;
843269Sericheng 	queue_t		*q = dsp->ds_wq;
844269Sericheng 
845269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
846269Sericheng 
847269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
848269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
849269Sericheng 		dl_err = DL_OUTSTATE;
850269Sericheng 		goto failed;
851269Sericheng 	}
852269Sericheng 
853269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
854269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
855269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
856269Sericheng 		dl_err = DL_BADPRIM;
857269Sericheng 		goto failed;
858269Sericheng 	}
859269Sericheng 
860269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
861269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
862269Sericheng 		dl_err = DL_SYSERR;
863269Sericheng 		err = EBUSY;
864269Sericheng 		goto failed;
8650Sstevel@tonic-gate 	}
8660Sstevel@tonic-gate 
867269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
868269Sericheng 	if (err != 0) {
869269Sericheng 		switch (err) {
870269Sericheng 		case EINVAL:
871269Sericheng 			dl_err = DL_BADADDR;
872269Sericheng 			err = 0;
873269Sericheng 			break;
874269Sericheng 		case ENOSPC:
875269Sericheng 			dl_err = DL_TOOMANY;
876269Sericheng 			err = 0;
877269Sericheng 			break;
878269Sericheng 		default:
879269Sericheng 			dl_err = DL_SYSERR;
880269Sericheng 			break;
881269Sericheng 		}
882269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
883269Sericheng 			dls_active_clear(dsp->ds_dc);
884269Sericheng 
885269Sericheng 		goto failed;
886269Sericheng 	}
887269Sericheng 
888269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
889269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
890269Sericheng 
891269Sericheng 	rw_exit(&dsp->ds_lock);
892269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
893269Sericheng 	return (B_TRUE);
894269Sericheng failed:
895269Sericheng 	rw_exit(&dsp->ds_lock);
896269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
897269Sericheng 	return (B_FALSE);
898269Sericheng }
899269Sericheng 
900269Sericheng /*
901269Sericheng  * DL_DISABMULTI_REQ
902269Sericheng  */
903269Sericheng static boolean_t
904269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
905269Sericheng {
906269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
907269Sericheng 	int		err = 0;
908269Sericheng 	t_uscalar_t	dl_err;
909269Sericheng 	queue_t		*q = dsp->ds_wq;
910269Sericheng 
911269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
912269Sericheng 
913269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
914269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
915269Sericheng 		dl_err = DL_OUTSTATE;
916269Sericheng 		goto failed;
917269Sericheng 	}
918269Sericheng 
919269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
920269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
921269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
922269Sericheng 		dl_err = DL_BADPRIM;
923269Sericheng 		goto failed;
924269Sericheng 	}
925269Sericheng 
926269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
927269Sericheng 	if (err != 0) {
928269Sericheng 	switch (err) {
929269Sericheng 		case EINVAL:
930269Sericheng 			dl_err = DL_BADADDR;
931269Sericheng 			err = 0;
932269Sericheng 			break;
933269Sericheng 
934269Sericheng 		case ENOENT:
935269Sericheng 			dl_err = DL_NOTENAB;
936269Sericheng 			err = 0;
937269Sericheng 			break;
938269Sericheng 
939269Sericheng 		default:
940269Sericheng 			dl_err = DL_SYSERR;
941269Sericheng 			break;
942269Sericheng 		}
943269Sericheng 		goto failed;
944269Sericheng 	}
945269Sericheng 
946269Sericheng 	rw_exit(&dsp->ds_lock);
947269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
948269Sericheng 	return (B_TRUE);
949269Sericheng failed:
950269Sericheng 	rw_exit(&dsp->ds_lock);
951269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
952269Sericheng 	return (B_FALSE);
9530Sstevel@tonic-gate }
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate /*
956269Sericheng  * DL_PHYS_ADDR_REQ
9570Sstevel@tonic-gate  */
958269Sericheng static boolean_t
959269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
9600Sstevel@tonic-gate {
961269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
962269Sericheng 	queue_t		*q = dsp->ds_wq;
963269Sericheng 	t_uscalar_t	dl_err;
964269Sericheng 	char		*addr;
965269Sericheng 	uint_t		addr_length;
966269Sericheng 
967269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
968269Sericheng 
969269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
970269Sericheng 		dl_err = DL_BADPRIM;
971269Sericheng 		goto failed;
972269Sericheng 	}
973269Sericheng 
974269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
975269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
976269Sericheng 		dl_err = DL_OUTSTATE;
977269Sericheng 		goto failed;
978269Sericheng 	}
9790Sstevel@tonic-gate 
980269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
981269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
982269Sericheng 		dl_err = DL_UNSUPPORTED;
9830Sstevel@tonic-gate 		goto failed;
984269Sericheng 	}
9850Sstevel@tonic-gate 
986269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
987269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
988269Sericheng 	if (addr == NULL) {
989269Sericheng 		rw_exit(&dsp->ds_lock);
990269Sericheng 		merror(q, mp, ENOSR);
991269Sericheng 		return (B_FALSE);
992269Sericheng 	}
9930Sstevel@tonic-gate 
994269Sericheng 	/*
995269Sericheng 	 * Copy out the address before we drop the lock; we don't
996269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
997269Sericheng 	 */
998269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
999269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
1000269Sericheng 
1001269Sericheng 	rw_exit(&dsp->ds_lock);
1002269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
1003269Sericheng 	kmem_free(addr, addr_length);
1004269Sericheng 	return (B_TRUE);
10050Sstevel@tonic-gate failed:
1006269Sericheng 	rw_exit(&dsp->ds_lock);
1007269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
1008269Sericheng 	return (B_FALSE);
1009269Sericheng }
10100Sstevel@tonic-gate 
1011269Sericheng /*
1012269Sericheng  * DL_SET_PHYS_ADDR_REQ
1013269Sericheng  */
1014269Sericheng static boolean_t
1015269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1016269Sericheng {
1017269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
1018269Sericheng 	int		err = 0;
1019269Sericheng 	t_uscalar_t	dl_err;
1020269Sericheng 	queue_t		*q = dsp->ds_wq;
10210Sstevel@tonic-gate 
1022269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1023269Sericheng 
1024269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1025269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1026269Sericheng 		dl_err = DL_OUTSTATE;
1027269Sericheng 		goto failed;
1028269Sericheng 	}
1029269Sericheng 
1030269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
1031269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
1032269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
1033269Sericheng 		dl_err = DL_BADPRIM;
1034269Sericheng 		goto failed;
10350Sstevel@tonic-gate 	}
10360Sstevel@tonic-gate 
1037269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
1038269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
1039269Sericheng 		dl_err = DL_SYSERR;
1040269Sericheng 		err = EBUSY;
1041269Sericheng 		goto failed;
1042269Sericheng 	}
1043269Sericheng 
1044269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
1045269Sericheng 	if (err != 0) {
1046269Sericheng 		switch (err) {
1047269Sericheng 		case EINVAL:
1048269Sericheng 			dl_err = DL_BADADDR;
1049269Sericheng 			err = 0;
1050269Sericheng 			break;
1051269Sericheng 
1052269Sericheng 		default:
1053269Sericheng 			dl_err = DL_SYSERR;
1054269Sericheng 			break;
1055269Sericheng 		}
1056269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1057269Sericheng 			dls_active_clear(dsp->ds_dc);
1058269Sericheng 
1059269Sericheng 		goto failed;
1060269Sericheng 	}
1061269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1062269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
1063269Sericheng 
1064269Sericheng 	rw_exit(&dsp->ds_lock);
1065269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
1066269Sericheng 	return (B_TRUE);
1067269Sericheng failed:
1068269Sericheng 	rw_exit(&dsp->ds_lock);
1069269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
1070269Sericheng 	return (B_FALSE);
1071269Sericheng }
1072269Sericheng 
1073269Sericheng /*
1074269Sericheng  * DL_UDQOS_REQ
1075269Sericheng  */
1076269Sericheng static boolean_t
1077269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1078269Sericheng {
1079269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
1080269Sericheng 	dl_qos_cl_sel1_t *selp;
1081269Sericheng 	int		off, len;
1082269Sericheng 	t_uscalar_t	dl_err;
1083269Sericheng 	queue_t		*q = dsp->ds_wq;
1084269Sericheng 
1085269Sericheng 	off = dlp->dl_qos_offset;
1086269Sericheng 	len = dlp->dl_qos_length;
1087269Sericheng 
1088269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
1089269Sericheng 		dl_err = DL_BADPRIM;
1090269Sericheng 		goto failed;
1091269Sericheng 	}
1092269Sericheng 
1093269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1094269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1095269Sericheng 		dl_err = DL_BADQOSTYPE;
1096269Sericheng 		goto failed;
1097269Sericheng 	}
1098269Sericheng 
1099269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1100269Sericheng 
1101269Sericheng 	if (dsp->ds_vid == VLAN_ID_NONE ||
1102269Sericheng 	    selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1103269Sericheng 	    selp->dl_priority < 0) {
1104269Sericheng 		dl_err = DL_BADQOSPARAM;
1105269Sericheng 		goto failed;
1106269Sericheng 	}
1107269Sericheng 
1108269Sericheng 	dsp->ds_pri = selp->dl_priority;
1109269Sericheng 
1110269Sericheng 	rw_exit(&dsp->ds_lock);
1111269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1112269Sericheng 	return (B_TRUE);
1113269Sericheng failed:
1114269Sericheng 	rw_exit(&dsp->ds_lock);
1115269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1116269Sericheng 	return (B_FALSE);
11170Sstevel@tonic-gate }
11180Sstevel@tonic-gate 
11191184Skrgopi static boolean_t
11201184Skrgopi check_ip_above(queue_t *q)
11211184Skrgopi {
11221184Skrgopi 	queue_t		*next_q;
11231184Skrgopi 	boolean_t	ret = B_TRUE;
11241184Skrgopi 
11251184Skrgopi 	claimstr(q);
11261184Skrgopi 	next_q = q->q_next;
11271184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
11281184Skrgopi 		ret = B_FALSE;
11291184Skrgopi 	releasestr(q);
11301184Skrgopi 	return (ret);
11311184Skrgopi }
11321184Skrgopi 
11330Sstevel@tonic-gate /*
1134269Sericheng  * DL_CAPABILITY_REQ
11350Sstevel@tonic-gate  */
1136269Sericheng /*ARGSUSED*/
1137269Sericheng static boolean_t
1138269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
11390Sstevel@tonic-gate {
1140269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1141269Sericheng 	dl_capability_sub_t *sp;
1142269Sericheng 	size_t		size, len;
1143269Sericheng 	offset_t	off, end;
1144269Sericheng 	t_uscalar_t	dl_err;
1145269Sericheng 	queue_t		*q = dsp->ds_wq;
1146269Sericheng 	boolean_t	upgraded;
1147269Sericheng 
1148269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1149269Sericheng 
1150269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1151269Sericheng 		dl_err = DL_BADPRIM;
1152269Sericheng 		goto failed;
1153269Sericheng 	}
1154269Sericheng 
1155269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1156269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1157269Sericheng 		dl_err = DL_OUTSTATE;
1158269Sericheng 		goto failed;
1159269Sericheng 	}
1160269Sericheng 
1161269Sericheng 	/*
1162269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1163269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1164269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1165269Sericheng 	 */
1166269Sericheng 	if (dlp->dl_sub_length == 0) {
1167269Sericheng 		/* callee drops lock */
1168269Sericheng 		return (proto_capability_advertise(dsp, mp));
1169269Sericheng 	}
1170269Sericheng 
1171269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1172269Sericheng 		dl_err = DL_BADPRIM;
1173269Sericheng 		goto failed;
1174269Sericheng 	}
1175269Sericheng 
1176269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1177269Sericheng 
1178269Sericheng 	off = dlp->dl_sub_offset;
1179269Sericheng 	len = dlp->dl_sub_length;
11800Sstevel@tonic-gate 
11810Sstevel@tonic-gate 	/*
1182269Sericheng 	 * Walk the list of capabilities to be enabled.
11830Sstevel@tonic-gate 	 */
1184269Sericheng 	upgraded = B_FALSE;
1185269Sericheng 	for (end = off + len; off < end; ) {
1186269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1187269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1188269Sericheng 
1189269Sericheng 		if (off + size > end ||
1190269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1191269Sericheng 			dl_err = DL_BADPRIM;
1192269Sericheng 			goto failed;
1193269Sericheng 		}
1194269Sericheng 
1195269Sericheng 		switch (sp->dl_cap) {
1196269Sericheng 		/*
1197269Sericheng 		 * TCP/IP checksum offload to hardware.
1198269Sericheng 		 */
1199269Sericheng 		case DL_CAPAB_HCKSUM: {
1200269Sericheng 			dl_capab_hcksum_t *hcksump;
1201269Sericheng 			dl_capab_hcksum_t hcksum;
1202269Sericheng 
1203269Sericheng 			ASSERT(dsp->ds_mip->mi_cksum != 0);
1204269Sericheng 
1205269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1206269Sericheng 			/*
1207269Sericheng 			 * Copy for alignment.
1208269Sericheng 			 */
1209269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1210269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1211269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1212269Sericheng 			break;
1213269Sericheng 		}
1214269Sericheng 
1215269Sericheng 		/*
1216269Sericheng 		 * IP polling interface.
1217269Sericheng 		 */
1218269Sericheng 		case DL_CAPAB_POLL: {
12191184Skrgopi 			dl_capab_dls_t *pollp;
12201184Skrgopi 			dl_capab_dls_t	poll;
1221269Sericheng 
12221184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1223269Sericheng 			/*
1224269Sericheng 			 * Copy for alignment.
1225269Sericheng 			 */
12261184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1227269Sericheng 
1228269Sericheng 			/*
1229269Sericheng 			 * We need to become writer before enabling and/or
1230269Sericheng 			 * disabling the polling interface.  If we couldn'
1231269Sericheng 			 * upgrade, check state again after re-acquiring the
1232269Sericheng 			 * lock to make sure we can proceed.
1233269Sericheng 			 */
1234269Sericheng 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
1235269Sericheng 				rw_exit(&dsp->ds_lock);
1236269Sericheng 				rw_enter(&dsp->ds_lock, RW_WRITER);
1237269Sericheng 
1238269Sericheng 				if (dsp->ds_dlstate == DL_UNATTACHED ||
1239269Sericheng 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
1240269Sericheng 					dl_err = DL_OUTSTATE;
1241269Sericheng 					goto failed;
1242269Sericheng 				}
1243269Sericheng 			}
1244269Sericheng 			upgraded = B_TRUE;
1245269Sericheng 
12461184Skrgopi 			switch (poll.dls_flags) {
1247269Sericheng 			default:
1248269Sericheng 				/*FALLTHRU*/
1249269Sericheng 			case POLL_DISABLE:
1250269Sericheng 				proto_poll_disable(dsp);
1251269Sericheng 				break;
1252269Sericheng 
1253269Sericheng 			case POLL_ENABLE:
1254269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1255269Sericheng 
1256269Sericheng 				/*
1257269Sericheng 				 * Make sure polling is disabled.
1258269Sericheng 				 */
1259269Sericheng 				proto_poll_disable(dsp);
1260269Sericheng 
1261269Sericheng 				/*
1262269Sericheng 				 * Now attempt enable it.
1263269Sericheng 				 */
12641184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
12651184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
12661184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
12671184Skrgopi 					poll.dls_flags = POLL_ENABLE;
12681184Skrgopi 				}
1269269Sericheng 				break;
1270269Sericheng 			}
1271269Sericheng 
12721184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
12731184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
12741184Skrgopi 			break;
12751184Skrgopi 		}
12761184Skrgopi 		case DL_CAPAB_SOFT_RING: {
12771184Skrgopi 			dl_capab_dls_t *soft_ringp;
12781184Skrgopi 			dl_capab_dls_t soft_ring;
12791184Skrgopi 
12801184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
12811184Skrgopi 			/*
12821184Skrgopi 			 * Copy for alignment.
12831184Skrgopi 			 */
12841184Skrgopi 			bcopy(soft_ringp, &soft_ring,
12851184Skrgopi 			    sizeof (dl_capab_dls_t));
12861184Skrgopi 
12871184Skrgopi 			/*
12881184Skrgopi 			 * We need to become writer before enabling and/or
12891184Skrgopi 			 * disabling the soft_ring interface.  If we couldn'
12901184Skrgopi 			 * upgrade, check state again after re-acquiring the
12911184Skrgopi 			 * lock to make sure we can proceed.
12921184Skrgopi 			 */
12931184Skrgopi 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
12941184Skrgopi 				rw_exit(&dsp->ds_lock);
12951184Skrgopi 				rw_enter(&dsp->ds_lock, RW_WRITER);
12961184Skrgopi 
12971184Skrgopi 				if (dsp->ds_dlstate == DL_UNATTACHED ||
12981184Skrgopi 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
12991184Skrgopi 					dl_err = DL_OUTSTATE;
13001184Skrgopi 					goto failed;
13011184Skrgopi 				}
13021184Skrgopi 			}
13031184Skrgopi 			upgraded = B_TRUE;
13041184Skrgopi 
13051184Skrgopi 			switch (soft_ring.dls_flags) {
13061184Skrgopi 			default:
13071184Skrgopi 				/*FALLTHRU*/
13081184Skrgopi 			case SOFT_RING_DISABLE:
13091184Skrgopi 				proto_soft_ring_disable(dsp);
13101184Skrgopi 				break;
13111184Skrgopi 
13121184Skrgopi 			case SOFT_RING_ENABLE:
13131184Skrgopi 				/*
13141184Skrgopi 				 * Make sure soft_ring is disabled.
13151184Skrgopi 				 */
13161184Skrgopi 				proto_soft_ring_disable(dsp);
13171184Skrgopi 
13181184Skrgopi 				/*
13191184Skrgopi 				 * Now attempt enable it.
13201184Skrgopi 				 */
13211184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
13221184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
13231184Skrgopi 					bzero(&soft_ring,
13241184Skrgopi 					    sizeof (dl_capab_dls_t));
13251184Skrgopi 					soft_ring.dls_flags =
13261184Skrgopi 					    SOFT_RING_ENABLE;
13271184Skrgopi 				} else {
13281184Skrgopi 					bzero(&soft_ring,
13291184Skrgopi 					    sizeof (dl_capab_dls_t));
13301184Skrgopi 					soft_ring.dls_flags =
13311184Skrgopi 					    SOFT_RING_DISABLE;
13321184Skrgopi 				}
13331184Skrgopi 				break;
13341184Skrgopi 			}
13351184Skrgopi 
13361184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
13371184Skrgopi 			bcopy(&soft_ring, soft_ringp,
13381184Skrgopi 			    sizeof (dl_capab_dls_t));
1339269Sericheng 			break;
1340269Sericheng 		}
1341269Sericheng 		default:
1342269Sericheng 			break;
1343269Sericheng 		}
1344269Sericheng 
1345269Sericheng 		off += size;
1346269Sericheng 	}
1347269Sericheng 	rw_exit(&dsp->ds_lock);
1348269Sericheng 	qreply(q, mp);
1349269Sericheng 	return (B_TRUE);
1350269Sericheng failed:
1351269Sericheng 	rw_exit(&dsp->ds_lock);
1352269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1353269Sericheng 	return (B_FALSE);
13540Sstevel@tonic-gate }
13550Sstevel@tonic-gate 
13560Sstevel@tonic-gate /*
1357269Sericheng  * DL_NOTIFY_REQ
13580Sstevel@tonic-gate  */
1359269Sericheng static boolean_t
1360269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
13610Sstevel@tonic-gate {
1362269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1363269Sericheng 	t_uscalar_t	dl_err;
1364269Sericheng 	queue_t		*q = dsp->ds_wq;
1365269Sericheng 	uint_t		note =
1366269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1367269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1368269Sericheng 	    DL_NOTE_PHYS_ADDR |
1369269Sericheng 	    DL_NOTE_LINK_UP |
1370269Sericheng 	    DL_NOTE_LINK_DOWN |
1371269Sericheng 	    DL_NOTE_CAPAB_RENEG;
13720Sstevel@tonic-gate 
1373269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1374269Sericheng 		dl_err = DL_BADPRIM;
1375269Sericheng 		goto failed;
1376269Sericheng 	}
13770Sstevel@tonic-gate 
1378269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1379269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1380269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1381269Sericheng 		dl_err = DL_OUTSTATE;
1382269Sericheng 		goto failed;
13830Sstevel@tonic-gate 	}
13840Sstevel@tonic-gate 
1385269Sericheng 	if (dsp->ds_mip->mi_stat[MAC_STAT_IFSPEED])
1386269Sericheng 		note |= DL_NOTE_SPEED;
1387269Sericheng 
1388269Sericheng 	/*
1389269Sericheng 	 * Cache the notifications that are being enabled.
1390269Sericheng 	 */
1391269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1392269Sericheng 	rw_exit(&dsp->ds_lock);
1393269Sericheng 	/*
1394269Sericheng 	 * The ACK carries all notifications regardless of which set is
1395269Sericheng 	 * being enabled.
1396269Sericheng 	 */
1397269Sericheng 	dlnotifyack(q, mp, note);
1398269Sericheng 
1399269Sericheng 	/*
1400269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1401269Sericheng 	 */
1402269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1403269Sericheng 	if (dsp->ds_notifications != 0) {
1404269Sericheng 		rw_exit(&dsp->ds_lock);
1405269Sericheng 		dld_str_notify_ind(dsp);
1406269Sericheng 	} else {
1407269Sericheng 		rw_exit(&dsp->ds_lock);
1408269Sericheng 	}
1409269Sericheng 	return (B_TRUE);
1410269Sericheng failed:
1411269Sericheng 	rw_exit(&dsp->ds_lock);
1412269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1413269Sericheng 	return (B_FALSE);
14140Sstevel@tonic-gate }
14150Sstevel@tonic-gate 
14160Sstevel@tonic-gate /*
1417269Sericheng  * DL_UINTDATA_REQ
14180Sstevel@tonic-gate  */
1419269Sericheng static boolean_t
1420269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
14210Sstevel@tonic-gate {
1422269Sericheng 	queue_t			*q = dsp->ds_wq;
1423269Sericheng 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)udlp;
1424269Sericheng 	off_t			off;
1425269Sericheng 	size_t			len, size;
1426269Sericheng 	const uint8_t		*addr;
1427269Sericheng 	uint16_t		sap;
1428269Sericheng 	uint_t			addr_length;
1429269Sericheng 	mblk_t			*bp, *cont;
1430269Sericheng 	uint32_t		start, stuff, end, value, flags;
1431269Sericheng 	t_uscalar_t		dl_err;
1432269Sericheng 
1433269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1434269Sericheng 
1435269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1436269Sericheng 		dl_err = DL_BADPRIM;
1437269Sericheng 		goto failed;
1438269Sericheng 	}
1439269Sericheng 
1440269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
1441269Sericheng 		dl_err = DL_OUTSTATE;
1442269Sericheng 		goto failed;
1443269Sericheng 	}
1444269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1445269Sericheng 
1446269Sericheng 	off = dlp->dl_dest_addr_offset;
1447269Sericheng 	len = dlp->dl_dest_addr_length;
1448269Sericheng 
1449269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1450269Sericheng 		dl_err = DL_BADPRIM;
1451269Sericheng 		goto failed;
1452269Sericheng 	}
1453269Sericheng 
1454269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1455269Sericheng 		dl_err = DL_BADADDR;
1456269Sericheng 		goto failed;
1457269Sericheng 	}
1458269Sericheng 
1459269Sericheng 	addr = mp->b_rptr + off;
1460269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1461269Sericheng 
1462269Sericheng 	/*
1463269Sericheng 	 * Check the length of the packet and the block types.
1464269Sericheng 	 */
1465269Sericheng 	size = 0;
1466269Sericheng 	cont = mp->b_cont;
1467269Sericheng 	for (bp = cont; bp != NULL; bp = bp->b_cont) {
1468269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1469269Sericheng 			goto baddata;
1470269Sericheng 
1471269Sericheng 		size += MBLKL(bp);
1472269Sericheng 	}
1473269Sericheng 
1474269Sericheng 	if (size > dsp->ds_mip->mi_sdu_max)
1475269Sericheng 		goto baddata;
1476269Sericheng 
1477269Sericheng 	/*
1478*1502Sericheng 	 * sap <= ETHERMTU indicates that LLC is being used
1479*1502Sericheng 	 * and ethertype needs to be set to the payload length.
1480*1502Sericheng 	 */
1481*1502Sericheng 	if (sap <= ETHERMTU)
1482*1502Sericheng 		sap = (uint16_t)size;
1483*1502Sericheng 
1484*1502Sericheng 	/*
1485269Sericheng 	 * Build a packet header.
1486269Sericheng 	 */
1487269Sericheng 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dsp->ds_pri)) == NULL) {
1488269Sericheng 		dl_err = DL_BADADDR;
1489269Sericheng 		goto failed;
1490269Sericheng 	}
1491269Sericheng 
1492269Sericheng 	/*
1493269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1494269Sericheng 	 */
1495269Sericheng 	freeb(mp);
1496269Sericheng 
1497269Sericheng 	/*
1498269Sericheng 	 * Transfer the checksum offload information if it is present.
1499269Sericheng 	 */
1500269Sericheng 	hcksum_retrieve(cont, NULL, NULL, &start, &stuff, &end, &value,
1501269Sericheng 	    &flags);
1502269Sericheng 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags,
1503269Sericheng 	    0);
1504269Sericheng 
1505269Sericheng 	/*
1506269Sericheng 	 * Link the payload onto the new header.
1507269Sericheng 	 */
1508269Sericheng 	ASSERT(bp->b_cont == NULL);
1509269Sericheng 	bp->b_cont = cont;
1510269Sericheng 
1511269Sericheng 	str_mdata_fastpath_put(dsp, bp);
1512269Sericheng 	rw_exit(&dsp->ds_lock);
1513269Sericheng 	return (B_TRUE);
1514269Sericheng failed:
1515269Sericheng 	rw_exit(&dsp->ds_lock);
1516269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1517269Sericheng 	return (B_FALSE);
1518269Sericheng 
1519269Sericheng baddata:
1520269Sericheng 	rw_exit(&dsp->ds_lock);
1521269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1522269Sericheng 	return (B_FALSE);
1523269Sericheng }
1524269Sericheng 
1525269Sericheng /*
1526269Sericheng  * DL_PASSIVE_REQ
1527269Sericheng  */
1528269Sericheng /* ARGSUSED */
1529269Sericheng static boolean_t
1530269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1531269Sericheng {
1532269Sericheng 	t_uscalar_t dl_err;
1533269Sericheng 
1534269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1535269Sericheng 	/*
1536269Sericheng 	 * If we've already become active by issuing an active primitive,
1537269Sericheng 	 * then it's too late to try to become passive.
1538269Sericheng 	 */
1539269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1540269Sericheng 		dl_err = DL_OUTSTATE;
1541269Sericheng 		goto failed;
1542269Sericheng 	}
1543269Sericheng 
1544269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1545269Sericheng 		dl_err = DL_BADPRIM;
1546269Sericheng 		goto failed;
1547269Sericheng 	}
1548269Sericheng 
1549269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1550269Sericheng 	rw_exit(&dsp->ds_lock);
1551269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1552269Sericheng 	return (B_TRUE);
1553269Sericheng failed:
1554269Sericheng 	rw_exit(&dsp->ds_lock);
1555269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1556269Sericheng 	return (B_FALSE);
1557269Sericheng }
1558269Sericheng 
1559269Sericheng 
1560269Sericheng /*
1561269Sericheng  * Catch-all handler.
1562269Sericheng  */
1563269Sericheng static boolean_t
1564269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1565269Sericheng {
1566269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1567269Sericheng 	return (B_FALSE);
15680Sstevel@tonic-gate }
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate static void
15710Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
15720Sstevel@tonic-gate {
15730Sstevel@tonic-gate 	mac_handle_t	mh;
15740Sstevel@tonic-gate 
15751353Sericheng 	ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock));
1576269Sericheng 
15770Sstevel@tonic-gate 	if (!dsp->ds_polling)
15780Sstevel@tonic-gate 		return;
15790Sstevel@tonic-gate 
15800Sstevel@tonic-gate 	/*
15810Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
15820Sstevel@tonic-gate 	 */
15830Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
15840Sstevel@tonic-gate 
15850Sstevel@tonic-gate 	/*
15860Sstevel@tonic-gate 	 * Reset the resource_add callback.
15870Sstevel@tonic-gate 	 */
15880Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
15890Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
15901184Skrgopi 	mac_resources(mh);
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate 	/*
15930Sstevel@tonic-gate 	 * Set receive function back to default.
15940Sstevel@tonic-gate 	 */
15950Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
15960Sstevel@tonic-gate 	    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
15970Sstevel@tonic-gate 
15980Sstevel@tonic-gate 	/*
15990Sstevel@tonic-gate 	 * Note that polling is disabled.
16000Sstevel@tonic-gate 	 */
16010Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
16020Sstevel@tonic-gate }
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate static boolean_t
16051184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
16060Sstevel@tonic-gate {
16070Sstevel@tonic-gate 	mac_handle_t	mh;
16080Sstevel@tonic-gate 
1609269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16100Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
16110Sstevel@tonic-gate 
16120Sstevel@tonic-gate 	/*
16130Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
16140Sstevel@tonic-gate 	 * has been enabled.
16150Sstevel@tonic-gate 	 */
16160Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
16170Sstevel@tonic-gate 		return (B_FALSE);
16180Sstevel@tonic-gate 
16190Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
16200Sstevel@tonic-gate 
16210Sstevel@tonic-gate 	/*
16220Sstevel@tonic-gate 	 * Register resources.
16230Sstevel@tonic-gate 	 */
16241184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
16251184Skrgopi 	    (void *)pollp->dls_rx_handle);
16260Sstevel@tonic-gate 	mac_resources(mh);
16270Sstevel@tonic-gate 
16280Sstevel@tonic-gate 	/*
16290Sstevel@tonic-gate 	 * Set the receive function.
16300Sstevel@tonic-gate 	 */
16311184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
16321184Skrgopi 	    (void *)pollp->dls_rx_handle);
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 	/*
16350Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
16360Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
16370Sstevel@tonic-gate 	 */
16380Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
16390Sstevel@tonic-gate 	return (B_TRUE);
16400Sstevel@tonic-gate }
16410Sstevel@tonic-gate 
16421184Skrgopi static void
16431184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
16441184Skrgopi {
16451184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16461184Skrgopi 
16471184Skrgopi 	if (!dsp->ds_soft_ring)
16481184Skrgopi 		return;
16491184Skrgopi 
16501184Skrgopi 	/*
16511184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
16521184Skrgopi 	 */
16531184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
16541184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
16551184Skrgopi 	/*
16561184Skrgopi 	 * Note that fanout is disabled.
16571184Skrgopi 	 */
16581184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
16591184Skrgopi }
16601184Skrgopi 
16611184Skrgopi static boolean_t
16621184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
16631184Skrgopi {
16641184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16651184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
16661184Skrgopi 
16671184Skrgopi 	/*
16681184Skrgopi 	 * We cannot enable soft_ring if raw mode
16691184Skrgopi 	 * has been enabled.
16701184Skrgopi 	 */
16711184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
16721184Skrgopi 		return (B_FALSE);
16731184Skrgopi 
16741184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
16751184Skrgopi 		return (B_FALSE);
16761184Skrgopi 
16771184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
16781184Skrgopi 	return (B_TRUE);
16791184Skrgopi }
16801184Skrgopi 
16811184Skrgopi static void
16821184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
16831184Skrgopi {
16841184Skrgopi 	dls_rx_t	rx;
16851184Skrgopi 
16861184Skrgopi 	if (type == SOFT_RING_NONE) {
16871184Skrgopi 		rx = (dsp->ds_mode == DLD_FASTPATH) ?
16881184Skrgopi 			    dld_str_rx_fastpath : dld_str_rx_unitdata;
16891184Skrgopi 	} else {
16901184Skrgopi 		rx = (dls_rx_t)dls_ether_soft_ring_fanout;
16911184Skrgopi 	}
16921184Skrgopi 	dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type);
16931184Skrgopi }
16941184Skrgopi 
16950Sstevel@tonic-gate /*
16960Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
16970Sstevel@tonic-gate  */
1698269Sericheng static boolean_t
1699269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
17000Sstevel@tonic-gate {
17010Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
17020Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
17030Sstevel@tonic-gate 	size_t			subsize;
17041184Skrgopi 	dl_capab_dls_t		poll;
17051184Skrgopi 	dl_capab_dls_t	soft_ring;
17060Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
17070Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
17080Sstevel@tonic-gate 	uint8_t			*ptr;
17090Sstevel@tonic-gate 	uint32_t		cksum;
17100Sstevel@tonic-gate 	boolean_t		poll_cap;
1711269Sericheng 	queue_t			*q = dsp->ds_wq;
1712269Sericheng 	mblk_t			*mp1;
1713269Sericheng 
1714269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
17150Sstevel@tonic-gate 
17160Sstevel@tonic-gate 	/*
17170Sstevel@tonic-gate 	 * Initially assume no capabilities.
17180Sstevel@tonic-gate 	 */
17190Sstevel@tonic-gate 	subsize = 0;
17200Sstevel@tonic-gate 
17211184Skrgopi 	/* Always advertize soft ring capability for GLDv3 drivers */
17221184Skrgopi 	subsize += sizeof (dl_capability_sub_t) + sizeof (dl_capab_dls_t);
17231184Skrgopi 
17240Sstevel@tonic-gate 	/*
17250Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
17260Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
17270Sstevel@tonic-gate 	 * then reserve space for that capability.
17280Sstevel@tonic-gate 	 */
17290Sstevel@tonic-gate 	poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) &&
17300Sstevel@tonic-gate 	    !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE));
17310Sstevel@tonic-gate 	if (poll_cap) {
17320Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17331184Skrgopi 		    sizeof (dl_capab_dls_t);
17340Sstevel@tonic-gate 	}
17350Sstevel@tonic-gate 
17360Sstevel@tonic-gate 	/*
17370Sstevel@tonic-gate 	 * If the MAC interface supports checksum offload then reserve
17380Sstevel@tonic-gate 	 * space for the DL_CAPAB_HCKSUM capability.
17390Sstevel@tonic-gate 	 */
17400Sstevel@tonic-gate 	if ((cksum = dsp->ds_mip->mi_cksum) != 0) {
17410Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17420Sstevel@tonic-gate 		    sizeof (dl_capab_hcksum_t);
17430Sstevel@tonic-gate 	}
17440Sstevel@tonic-gate 
17450Sstevel@tonic-gate 	/*
17460Sstevel@tonic-gate 	 * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then
17470Sstevel@tonic-gate 	 * reserve space for it.
17480Sstevel@tonic-gate 	 */
17490Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
17500Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17510Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
17520Sstevel@tonic-gate 	}
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 	/*
1755269Sericheng 	 * If there are no capabilities to advertise or if we
1756269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
17570Sstevel@tonic-gate 	 */
17581184Skrgopi 	if ((mp1 = reallocb(mp,
1759269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1760269Sericheng 		rw_exit(&dsp->ds_lock);
1761269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1762269Sericheng 		return (B_FALSE);
17630Sstevel@tonic-gate 	}
17640Sstevel@tonic-gate 
1765269Sericheng 	mp = mp1;
1766269Sericheng 	DB_TYPE(mp) = M_PROTO;
1767269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1768269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
17690Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
17700Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
17710Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
17720Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
17730Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
17740Sstevel@tonic-gate 
17750Sstevel@tonic-gate 	/*
17760Sstevel@tonic-gate 	 * IP polling interface.
17770Sstevel@tonic-gate 	 */
17780Sstevel@tonic-gate 	if (poll_cap) {
17790Sstevel@tonic-gate 		/*
1780269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
1781269Sericheng 		 * we need to become writer before doing so.
17820Sstevel@tonic-gate 		 */
1783269Sericheng 		if (!rw_tryupgrade(&dsp->ds_lock)) {
1784269Sericheng 			rw_exit(&dsp->ds_lock);
1785269Sericheng 			rw_enter(&dsp->ds_lock, RW_WRITER);
1786269Sericheng 		}
17870Sstevel@tonic-gate 
1788269Sericheng 		/*
1789269Sericheng 		 * Check if polling state has changed after we re-acquired
1790269Sericheng 		 * the lock above, so that we don't mis-advertise it.
1791269Sericheng 		 */
1792269Sericheng 		poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) &&
1793269Sericheng 		    !(dld_opt & DLD_OPT_NO_POLL) &&
1794269Sericheng 		    (dsp->ds_vid == VLAN_ID_NONE));
17950Sstevel@tonic-gate 
1796269Sericheng 		if (!poll_cap) {
1797269Sericheng 			int poll_capab_size;
1798269Sericheng 
1799269Sericheng 			rw_downgrade(&dsp->ds_lock);
1800269Sericheng 
1801269Sericheng 			poll_capab_size = sizeof (dl_capability_sub_t) +
18021184Skrgopi 			    sizeof (dl_capab_dls_t);
18030Sstevel@tonic-gate 
1804269Sericheng 			mp->b_wptr -= poll_capab_size;
1805269Sericheng 			subsize -= poll_capab_size;
1806269Sericheng 			dlap->dl_sub_length = subsize;
1807269Sericheng 		} else {
1808269Sericheng 			proto_poll_disable(dsp);
1809269Sericheng 
1810269Sericheng 			rw_downgrade(&dsp->ds_lock);
1811269Sericheng 
1812269Sericheng 			dlsp = (dl_capability_sub_t *)ptr;
1813269Sericheng 
1814269Sericheng 			dlsp->dl_cap = DL_CAPAB_POLL;
18151184Skrgopi 			dlsp->dl_length = sizeof (dl_capab_dls_t);
1816269Sericheng 			ptr += sizeof (dl_capability_sub_t);
18170Sstevel@tonic-gate 
18181184Skrgopi 			bzero(&poll, sizeof (dl_capab_dls_t));
18191184Skrgopi 			poll.dls_version = POLL_VERSION_1;
18201184Skrgopi 			poll.dls_flags = POLL_CAPABLE;
18211184Skrgopi 			poll.dls_tx_handle = (uintptr_t)dsp;
18221184Skrgopi 			poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
1823269Sericheng 
18241184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
18251184Skrgopi 			bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
18261184Skrgopi 			ptr += sizeof (dl_capab_dls_t);
1827269Sericheng 		}
18280Sstevel@tonic-gate 	}
18290Sstevel@tonic-gate 
1830269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
1831269Sericheng 
18321184Skrgopi 	dlsp = (dl_capability_sub_t *)ptr;
18331184Skrgopi 
18341184Skrgopi 	dlsp->dl_cap = DL_CAPAB_SOFT_RING;
18351184Skrgopi 	dlsp->dl_length = sizeof (dl_capab_dls_t);
18361184Skrgopi 	ptr += sizeof (dl_capability_sub_t);
18371184Skrgopi 
18381184Skrgopi 	bzero(&soft_ring, sizeof (dl_capab_dls_t));
18391184Skrgopi 	soft_ring.dls_version = SOFT_RING_VERSION_1;
18401184Skrgopi 	soft_ring.dls_flags = SOFT_RING_CAPABLE;
18411184Skrgopi 	soft_ring.dls_tx_handle = (uintptr_t)dsp;
18421184Skrgopi 	soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
18431184Skrgopi 	soft_ring.dls_ring_change_status =
18441184Skrgopi 	    (uintptr_t)proto_change_soft_ring_fanout;
18451184Skrgopi 	soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
18461184Skrgopi 	soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
18471184Skrgopi 
18481184Skrgopi 	dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
18491184Skrgopi 	bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
18501184Skrgopi 	ptr += sizeof (dl_capab_dls_t);
18511184Skrgopi 
18520Sstevel@tonic-gate 	/*
18530Sstevel@tonic-gate 	 * TCP/IP checksum offload.
18540Sstevel@tonic-gate 	 */
18550Sstevel@tonic-gate 	if (cksum != 0) {
18560Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18570Sstevel@tonic-gate 
18580Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
18590Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
18600Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18610Sstevel@tonic-gate 
18620Sstevel@tonic-gate 		bzero(&hcksum, sizeof (dl_capab_hcksum_t));
18630Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
18640Sstevel@tonic-gate 		hcksum.hcksum_txflags = cksum;
18650Sstevel@tonic-gate 
18660Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
18670Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
18680Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
18690Sstevel@tonic-gate 	}
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate 	/*
18720Sstevel@tonic-gate 	 * Zero copy
18730Sstevel@tonic-gate 	 */
18740Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
18750Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18760Sstevel@tonic-gate 
18770Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
18780Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
18790Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18800Sstevel@tonic-gate 
18810Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
18820Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
18830Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
18840Sstevel@tonic-gate 
18850Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
18860Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
18870Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
18880Sstevel@tonic-gate 	}
18890Sstevel@tonic-gate 
18900Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1891269Sericheng 
1892269Sericheng 	rw_exit(&dsp->ds_lock);
1893269Sericheng 	qreply(q, mp);
1894269Sericheng 	return (B_TRUE);
18950Sstevel@tonic-gate }
1896