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
51502Sericheng  * Common Development and Distribution License (the "License").
61502Sericheng  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
22*5895Syz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #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,
57*5895Syz147064     proto_notify_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 *);
611353Sericheng 
621184Skrgopi static void proto_soft_ring_disable(dld_str_t *);
631184Skrgopi static boolean_t proto_soft_ring_enable(dld_str_t *, dl_capab_dls_t *);
641184Skrgopi static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
651184Skrgopi static void proto_change_soft_ring_fanout(dld_str_t *, int);
661184Skrgopi 
670Sstevel@tonic-gate #define	DL_ACK_PENDING(state) \
680Sstevel@tonic-gate 	((state) == DL_ATTACH_PENDING || \
690Sstevel@tonic-gate 	(state) == DL_DETACH_PENDING || \
700Sstevel@tonic-gate 	(state) == DL_BIND_PENDING || \
710Sstevel@tonic-gate 	(state) == DL_UNBIND_PENDING)
720Sstevel@tonic-gate 
730Sstevel@tonic-gate /*
74269Sericheng  * Process a DLPI protocol message.
75269Sericheng  * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
76269Sericheng  * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
77269Sericheng  * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
78269Sericheng  * as 'passive' and forbids it from being subsequently made 'active'
79269Sericheng  * by the above primitives.
800Sstevel@tonic-gate  */
810Sstevel@tonic-gate void
82*5895Syz147064 dld_wput_proto_nondata(dld_str_t *dsp, mblk_t *mp)
830Sstevel@tonic-gate {
840Sstevel@tonic-gate 	union DL_primitives	*udlp;
850Sstevel@tonic-gate 	t_uscalar_t		prim;
860Sstevel@tonic-gate 
87*5895Syz147064 	ASSERT(MBLKL(mp) >= sizeof (t_uscalar_t));
880Sstevel@tonic-gate 
890Sstevel@tonic-gate 	udlp = (union DL_primitives *)mp->b_rptr;
900Sstevel@tonic-gate 	prim = udlp->dl_primitive;
910Sstevel@tonic-gate 
92269Sericheng 	switch (prim) {
93269Sericheng 	case DL_INFO_REQ:
94269Sericheng 		(void) proto_info_req(dsp, udlp, mp);
95269Sericheng 		break;
96269Sericheng 	case DL_BIND_REQ:
97269Sericheng 		(void) proto_bind_req(dsp, udlp, mp);
98269Sericheng 		break;
99269Sericheng 	case DL_UNBIND_REQ:
100269Sericheng 		(void) proto_unbind_req(dsp, udlp, mp);
101269Sericheng 		break;
102269Sericheng 	case DL_UDQOS_REQ:
103269Sericheng 		(void) proto_udqos_req(dsp, udlp, mp);
104269Sericheng 		break;
105269Sericheng 	case DL_ATTACH_REQ:
106269Sericheng 		(void) proto_attach_req(dsp, udlp, mp);
107269Sericheng 		break;
108269Sericheng 	case DL_DETACH_REQ:
109269Sericheng 		(void) proto_detach_req(dsp, udlp, mp);
110269Sericheng 		break;
111269Sericheng 	case DL_ENABMULTI_REQ:
112269Sericheng 		(void) proto_enabmulti_req(dsp, udlp, mp);
113269Sericheng 		break;
114269Sericheng 	case DL_DISABMULTI_REQ:
115269Sericheng 		(void) proto_disabmulti_req(dsp, udlp, mp);
116269Sericheng 		break;
117269Sericheng 	case DL_PROMISCON_REQ:
118269Sericheng 		(void) proto_promiscon_req(dsp, udlp, mp);
119269Sericheng 		break;
120269Sericheng 	case DL_PROMISCOFF_REQ:
121269Sericheng 		(void) proto_promiscoff_req(dsp, udlp, mp);
122269Sericheng 		break;
123269Sericheng 	case DL_PHYS_ADDR_REQ:
124269Sericheng 		(void) proto_physaddr_req(dsp, udlp, mp);
125269Sericheng 		break;
126269Sericheng 	case DL_SET_PHYS_ADDR_REQ:
127269Sericheng 		(void) proto_setphysaddr_req(dsp, udlp, mp);
128269Sericheng 		break;
129269Sericheng 	case DL_NOTIFY_REQ:
130269Sericheng 		(void) proto_notify_req(dsp, udlp, mp);
131269Sericheng 		break;
132269Sericheng 	case DL_CAPABILITY_REQ:
133269Sericheng 		(void) proto_capability_req(dsp, udlp, mp);
134269Sericheng 		break;
135269Sericheng 	case DL_PASSIVE_REQ:
136269Sericheng 		(void) proto_passive_req(dsp, udlp, mp);
137269Sericheng 		break;
138269Sericheng 	default:
139269Sericheng 		(void) proto_req(dsp, udlp, mp);
140269Sericheng 		break;
1410Sstevel@tonic-gate 	}
1420Sstevel@tonic-gate }
1430Sstevel@tonic-gate 
144269Sericheng #define	NEG(x)	-(x)
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1470Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
1482311Sseb 	uint8_t			dl_addr[MAXMACADDRLEN + sizeof (uint16_t)];
1492311Sseb 	uint8_t			dl_brdcst_addr[MAXMACADDRLEN];
1500Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1510Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1520Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate /*
155269Sericheng  * DL_INFO_REQ
1560Sstevel@tonic-gate  */
157269Sericheng /*ARGSUSED*/
158269Sericheng static boolean_t
159269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1600Sstevel@tonic-gate {
1610Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1620Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
1630Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
1640Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
1650Sstevel@tonic-gate 	uint8_t			*addr;
1660Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
1670Sstevel@tonic-gate 	uint_t			addr_length;
1680Sstevel@tonic-gate 	uint_t			sap_length;
169269Sericheng 	mac_info_t		minfo;
170269Sericheng 	mac_info_t		*minfop;
171269Sericheng 	queue_t			*q = dsp->ds_wq;
1720Sstevel@tonic-gate 
1730Sstevel@tonic-gate 	/*
1740Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
1750Sstevel@tonic-gate 	 * wrapper structure defined above.
1760Sstevel@tonic-gate 	 */
177269Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
1780Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
179269Sericheng 		return (B_FALSE);
180269Sericheng 
181269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
1840Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
1870Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
1880Sstevel@tonic-gate 
1890Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
1900Sstevel@tonic-gate 
1910Sstevel@tonic-gate 	/*
1920Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
1930Sstevel@tonic-gate 	 */
1940Sstevel@tonic-gate 	addr = dlwp->dl_addr;
1950Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
1960Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
1970Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	/*
2000Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
2010Sstevel@tonic-gate 	 * nodes.
2020Sstevel@tonic-gate 	 */
2030Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
2040Sstevel@tonic-gate 	dlp->dl_version = DL_VERSION_2;
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	/*
207269Sericheng 	 * Set the style of the provider
2080Sstevel@tonic-gate 	 */
209269Sericheng 	dlp->dl_provider_style = dsp->ds_style;
2100Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
2110Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
2120Sstevel@tonic-gate 
2130Sstevel@tonic-gate 	/*
2140Sstevel@tonic-gate 	 * Set the current DLPI state.
2150Sstevel@tonic-gate 	 */
2160Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	/*
219269Sericheng 	 * Gratuitously set the media type. This is to deal with modules
220269Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
2210Sstevel@tonic-gate 	 * being completed.
2220Sstevel@tonic-gate 	 */
2230Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2240Sstevel@tonic-gate 
2250Sstevel@tonic-gate 	/*
226269Sericheng 	 * If the stream is not at least attached we try to retrieve the
227269Sericheng 	 * mac_info using mac_info_get()
2280Sstevel@tonic-gate 	 */
2290Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2300Sstevel@tonic-gate 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
231269Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
232269Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
233269Sericheng 			/*
234269Sericheng 			 * Cannot find mac_info. giving up.
235269Sericheng 			 */
236269Sericheng 			goto done;
237269Sericheng 		}
238269Sericheng 		minfop = &minfo;
239269Sericheng 	} else {
240269Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
241269Sericheng 	}
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	/*
2440Sstevel@tonic-gate 	 * Set the media type (properly this time).
2450Sstevel@tonic-gate 	 */
2463147Sxc151355 	if (dsp->ds_native)
2473147Sxc151355 		dlp->dl_mac_type = minfop->mi_nativemedia;
2483147Sxc151355 	else
2493147Sxc151355 		dlp->dl_mac_type = minfop->mi_media;
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 	/*
2520Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2530Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2540Sstevel@tonic-gate 	 */
2550Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2560Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2570Sstevel@tonic-gate 
2580Sstevel@tonic-gate 	/*
2590Sstevel@tonic-gate 	 * Set the minimum and maximum payload sizes.
2600Sstevel@tonic-gate 	 */
261269Sericheng 	dlp->dl_min_sdu = minfop->mi_sdu_min;
262269Sericheng 	dlp->dl_max_sdu = minfop->mi_sdu_max;
2630Sstevel@tonic-gate 
264269Sericheng 	addr_length = minfop->mi_addr_length;
2650Sstevel@tonic-gate 
2660Sstevel@tonic-gate 	/*
2670Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2680Sstevel@tonic-gate 	 */
2692311Sseb 	if (minfop->mi_brdcst_addr != NULL) {
2702311Sseb 		dlp->dl_brdcst_addr_offset =
2712311Sseb 		    (uintptr_t)brdcst_addr - (uintptr_t)dlp;
2722311Sseb 		bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
2732311Sseb 		dlp->dl_brdcst_addr_length = addr_length;
2742311Sseb 	}
2750Sstevel@tonic-gate 
2762760Sdg199075 	dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
2772760Sdg199075 	dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
2780Sstevel@tonic-gate 
2792760Sdg199075 	rangep->dl_qos_type = DL_QOS_CL_RANGE1;
2802760Sdg199075 	rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
2812760Sdg199075 	rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
2822760Sdg199075 	rangep->dl_protection.dl_min = DL_UNKNOWN;
2832760Sdg199075 	rangep->dl_protection.dl_max = DL_UNKNOWN;
2842760Sdg199075 	rangep->dl_residual_error = DL_UNKNOWN;
2850Sstevel@tonic-gate 
2862760Sdg199075 	/*
2872760Sdg199075 	 * Specify the supported range of priorities.
2882760Sdg199075 	 */
2892760Sdg199075 	rangep->dl_priority.dl_min = 0;
2902760Sdg199075 	rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
2910Sstevel@tonic-gate 
2922760Sdg199075 	dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
2932760Sdg199075 	dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
2940Sstevel@tonic-gate 
2952760Sdg199075 	selp->dl_qos_type = DL_QOS_CL_SEL1;
2962760Sdg199075 	selp->dl_trans_delay = DL_UNKNOWN;
2972760Sdg199075 	selp->dl_protection = DL_UNKNOWN;
2982760Sdg199075 	selp->dl_residual_error = DL_UNKNOWN;
2992760Sdg199075 
3002760Sdg199075 	/*
3012760Sdg199075 	 * Specify the current priority (which can be changed by
3022760Sdg199075 	 * the DL_UDQOS_REQ primitive).
3032760Sdg199075 	 */
3042760Sdg199075 	selp->dl_priority = dsp->ds_pri;
3050Sstevel@tonic-gate 
3060Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
3070Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
3080Sstevel@tonic-gate 		/*
3090Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
3100Sstevel@tonic-gate 		 * DLSAP address.
3110Sstevel@tonic-gate 		 */
3120Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
3132311Sseb 		if (addr_length > 0)
3142311Sseb 			bcopy(dsp->ds_curr_addr, addr, addr_length);
3150Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3160Sstevel@tonic-gate 	}
3170Sstevel@tonic-gate 
3180Sstevel@tonic-gate done:
3190Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
3200Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
3210Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3220Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3230Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3240Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3250Sstevel@tonic-gate 
326269Sericheng 	rw_exit(&dsp->ds_lock);
327269Sericheng 
328269Sericheng 	qreply(q, mp);
329269Sericheng 	return (B_TRUE);
330269Sericheng }
331269Sericheng 
332269Sericheng /*
333269Sericheng  * DL_ATTACH_REQ
334269Sericheng  */
335269Sericheng static boolean_t
336269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
337269Sericheng {
338269Sericheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)udlp;
339269Sericheng 	int		err = 0;
340269Sericheng 	t_uscalar_t	dl_err;
341269Sericheng 	queue_t		*q = dsp->ds_wq;
342269Sericheng 
343269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
344269Sericheng 
345269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
346269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
347269Sericheng 		dl_err = DL_BADPRIM;
348269Sericheng 		goto failed;
349269Sericheng 	}
350269Sericheng 
351269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
352269Sericheng 		dl_err = DL_OUTSTATE;
353269Sericheng 		goto failed;
354269Sericheng 	}
355269Sericheng 
356269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
357269Sericheng 
358269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
359269Sericheng 	if (err != 0) {
360269Sericheng 		switch (err) {
361269Sericheng 		case ENOENT:
362269Sericheng 			dl_err = DL_BADPPA;
363269Sericheng 			err = 0;
364269Sericheng 			break;
365269Sericheng 		default:
366269Sericheng 			dl_err = DL_SYSERR;
367269Sericheng 			break;
368269Sericheng 		}
369269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
370269Sericheng 		goto failed;
371269Sericheng 	}
372269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
373269Sericheng 	rw_exit(&dsp->ds_lock);
374269Sericheng 
375269Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
376269Sericheng 	return (B_TRUE);
377269Sericheng failed:
378269Sericheng 	rw_exit(&dsp->ds_lock);
379269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
380269Sericheng 	return (B_FALSE);
3810Sstevel@tonic-gate }
3820Sstevel@tonic-gate 
383269Sericheng /*ARGSUSED*/
384269Sericheng static boolean_t
385269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
3860Sstevel@tonic-gate {
387269Sericheng 	queue_t		*q = dsp->ds_wq;
388269Sericheng 	t_uscalar_t	dl_err;
3890Sstevel@tonic-gate 
390269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
3910Sstevel@tonic-gate 
392269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
393269Sericheng 		dl_err = DL_BADPRIM;
394269Sericheng 		goto failed;
395269Sericheng 	}
3960Sstevel@tonic-gate 
397269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
398269Sericheng 		dl_err = DL_OUTSTATE;
399269Sericheng 		goto failed;
4000Sstevel@tonic-gate 	}
4010Sstevel@tonic-gate 
402269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
403269Sericheng 		dl_err = DL_BADPRIM;
404269Sericheng 		goto failed;
405269Sericheng 	}
406269Sericheng 
407269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
408*5895Syz147064 	dld_str_detach(dsp);
409269Sericheng 
410269Sericheng 	rw_exit(&dsp->ds_lock);
411*5895Syz147064 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
412269Sericheng 	return (B_TRUE);
413269Sericheng failed:
414269Sericheng 	rw_exit(&dsp->ds_lock);
415269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
416269Sericheng 	return (B_FALSE);
4170Sstevel@tonic-gate }
4180Sstevel@tonic-gate 
4190Sstevel@tonic-gate /*
420269Sericheng  * DL_BIND_REQ
4210Sstevel@tonic-gate  */
422269Sericheng static boolean_t
423269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4240Sstevel@tonic-gate {
425269Sericheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)udlp;
426269Sericheng 	int		err = 0;
4273037Syz147064 	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
4283037Syz147064 	uint_t		dlsap_addr_length;
429269Sericheng 	t_uscalar_t	dl_err;
430269Sericheng 	t_scalar_t	sap;
431269Sericheng 	queue_t		*q = dsp->ds_wq;
432269Sericheng 
433*5895Syz147064 	/*
434*5895Syz147064 	 * Because control message processing is serialized, we don't need
435*5895Syz147064 	 * to hold any locks to read any fields of dsp; we only need ds_lock
436*5895Syz147064 	 * to update the ds_dlstate, ds_sap and ds_passivestate fields.
437*5895Syz147064 	 */
438269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
439269Sericheng 		dl_err = DL_BADPRIM;
440269Sericheng 		goto failed;
441269Sericheng 	}
442269Sericheng 
443269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
444269Sericheng 		dl_err = DL_NOAUTO;
445269Sericheng 		goto failed;
446269Sericheng 	}
447269Sericheng 
448269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
449269Sericheng 		dl_err = DL_UNSUPPORTED;
450269Sericheng 		goto failed;
451269Sericheng 	}
452269Sericheng 
453269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
454269Sericheng 		dl_err = DL_OUTSTATE;
455269Sericheng 		goto failed;
456269Sericheng 	}
4570Sstevel@tonic-gate 
458269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
459269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
460269Sericheng 		dl_err = DL_SYSERR;
461269Sericheng 		err = EBUSY;
462269Sericheng 		goto failed;
463269Sericheng 	}
464269Sericheng 
465269Sericheng 	/*
466269Sericheng 	 * Set the receive callback.
467269Sericheng 	 */
468269Sericheng 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ?
469269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
4700Sstevel@tonic-gate 
471269Sericheng 	/*
472269Sericheng 	 * Bind the channel such that it can receive packets.
473269Sericheng 	 */
474*5895Syz147064 	sap = dlp->dl_sap;
475*5895Syz147064 	err = dls_bind(dsp->ds_dc, sap);
476269Sericheng 	if (err != 0) {
477269Sericheng 		switch (err) {
478269Sericheng 		case EINVAL:
479269Sericheng 			dl_err = DL_BADADDR;
480269Sericheng 			err = 0;
481269Sericheng 			break;
482269Sericheng 		default:
483269Sericheng 			dl_err = DL_SYSERR;
484269Sericheng 			break;
485269Sericheng 		}
486*5895Syz147064 
487269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
488269Sericheng 			dls_active_clear(dsp->ds_dc);
489269Sericheng 
4900Sstevel@tonic-gate 		goto failed;
491269Sericheng 	}
4920Sstevel@tonic-gate 
4930Sstevel@tonic-gate 	/*
4940Sstevel@tonic-gate 	 * Copy in MAC address.
4950Sstevel@tonic-gate 	 */
4963037Syz147064 	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
4973037Syz147064 	bcopy(dsp->ds_curr_addr, dlsap_addr, dlsap_addr_length);
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 	/*
5003037Syz147064 	 * Copy in the SAP.
5010Sstevel@tonic-gate 	 */
502*5895Syz147064 	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap;
5033037Syz147064 	dlsap_addr_length += sizeof (uint16_t);
5040Sstevel@tonic-gate 
505*5895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
506*5895Syz147064 
5070Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
508269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
509269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
510*5895Syz147064 	dsp->ds_sap = sap;
511*5895Syz147064 
512*5895Syz147064 	if (dsp->ds_mode == DLD_FASTPATH)
513*5895Syz147064 		dsp->ds_tx = str_mdata_fastpath_put;
514*5895Syz147064 	else if (dsp->ds_mode == DLD_RAW)
515*5895Syz147064 		dsp->ds_tx = str_mdata_raw_put;
516*5895Syz147064 	dsp->ds_unitdata_tx = dld_wput_proto_data;
5170Sstevel@tonic-gate 
518269Sericheng 	rw_exit(&dsp->ds_lock);
519269Sericheng 
5203037Syz147064 	dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0);
521269Sericheng 	return (B_TRUE);
5220Sstevel@tonic-gate failed:
523269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
524269Sericheng 	return (B_FALSE);
5250Sstevel@tonic-gate }
5260Sstevel@tonic-gate 
5270Sstevel@tonic-gate /*
528269Sericheng  * DL_UNBIND_REQ
5290Sstevel@tonic-gate  */
530269Sericheng /*ARGSUSED*/
531*5895Syz147064 static boolean_t
532*5895Syz147064 proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
5330Sstevel@tonic-gate {
534*5895Syz147064 	queue_t		*q = dsp->ds_wq;
535*5895Syz147064 	t_uscalar_t	dl_err;
536269Sericheng 
537*5895Syz147064 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
538*5895Syz147064 		dl_err = DL_BADPRIM;
539*5895Syz147064 		goto failed;
540*5895Syz147064 	}
541*5895Syz147064 
542*5895Syz147064 	if (dsp->ds_dlstate != DL_IDLE) {
543*5895Syz147064 		dl_err = DL_OUTSTATE;
544*5895Syz147064 		goto failed;
545*5895Syz147064 	}
546269Sericheng 
547269Sericheng 	/*
548269Sericheng 	 * Flush any remaining packets scheduled for transmission.
549269Sericheng 	 */
550269Sericheng 	dld_tx_flush(dsp);
551269Sericheng 
552269Sericheng 	/*
553269Sericheng 	 * Unbind the channel to stop packets being received.
554269Sericheng 	 */
555269Sericheng 	dls_unbind(dsp->ds_dc);
556269Sericheng 
557269Sericheng 	/*
558*5895Syz147064 	 * Clear the receive callback.
559*5895Syz147064 	 */
560*5895Syz147064 	dls_rx_set(dsp->ds_dc, NULL, NULL);
561*5895Syz147064 
562*5895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
563*5895Syz147064 
564*5895Syz147064 	/*
565269Sericheng 	 * Disable polling mode, if it is enabled.
566269Sericheng 	 */
567269Sericheng 	proto_poll_disable(dsp);
568269Sericheng 
569269Sericheng 	/*
570*5895Syz147064 	 * If soft rings were enabled, the workers should be quiesced.
571*5895Syz147064 	 */
572*5895Syz147064 	dls_soft_ring_disable(dsp->ds_dc);
573*5895Syz147064 
574*5895Syz147064 	/*
5753115Syl150051 	 * Clear LSO flags.
5763115Syl150051 	 */
5773115Syl150051 	dsp->ds_lso = B_FALSE;
5783115Syl150051 	dsp->ds_lso_max = 0;
5793115Syl150051 
5803115Syl150051 	/*
581269Sericheng 	 * Set the mode back to the default (unitdata).
582269Sericheng 	 */
583269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
5841353Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
585*5895Syz147064 	DLD_TX_QUIESCE(dsp);
586*5895Syz147064 	rw_exit(&dsp->ds_lock);
5871353Sericheng 
588*5895Syz147064 	dlokack(q, mp, DL_UNBIND_REQ);
589269Sericheng 
590269Sericheng 	return (B_TRUE);
591269Sericheng failed:
592269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
593269Sericheng 	return (B_FALSE);
5940Sstevel@tonic-gate }
5950Sstevel@tonic-gate 
5960Sstevel@tonic-gate /*
597269Sericheng  * DL_PROMISCON_REQ
5980Sstevel@tonic-gate  */
599269Sericheng static boolean_t
600269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6010Sstevel@tonic-gate {
602269Sericheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp;
603269Sericheng 	int		err = 0;
604269Sericheng 	t_uscalar_t	dl_err;
605*5895Syz147064 	uint32_t	promisc;
606269Sericheng 	queue_t		*q = dsp->ds_wq;
607269Sericheng 
608*5895Syz147064 	/*
609*5895Syz147064 	 * Because control message processing is serialized, we don't need
610*5895Syz147064 	 * to hold any locks to read any fields of dsp; we only need ds_lock
611*5895Syz147064 	 * to update the ds_promisc and ds_passivestate fields.
612*5895Syz147064 	 */
613269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
614269Sericheng 		dl_err = DL_BADPRIM;
615269Sericheng 		goto failed;
616269Sericheng 	}
617269Sericheng 
618269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
619269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
620269Sericheng 		dl_err = DL_OUTSTATE;
6210Sstevel@tonic-gate 		goto failed;
622269Sericheng 	}
6230Sstevel@tonic-gate 
624269Sericheng 	switch (dlp->dl_level) {
625269Sericheng 	case DL_PROMISC_SAP:
626*5895Syz147064 		promisc = DLS_PROMISC_SAP;
627269Sericheng 		break;
628*5895Syz147064 	case DL_PROMISC_MULTI:
629*5895Syz147064 		promisc = DLS_PROMISC_MULTI;
630*5895Syz147064 		break;
631269Sericheng 	case DL_PROMISC_PHYS:
632*5895Syz147064 		promisc = DLS_PROMISC_PHYS;
633269Sericheng 		break;
634269Sericheng 	default:
635269Sericheng 		dl_err = DL_NOTSUPPORTED;
636269Sericheng 		goto failed;
637269Sericheng 	}
6380Sstevel@tonic-gate 
639269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
640269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
641269Sericheng 		dl_err = DL_SYSERR;
642269Sericheng 		err = EBUSY;
643269Sericheng 		goto failed;
644269Sericheng 	}
645269Sericheng 
646269Sericheng 	/*
647269Sericheng 	 * Adjust channel promiscuity.
648269Sericheng 	 */
649*5895Syz147064 	promisc = (dsp->ds_promisc | promisc);
650*5895Syz147064 	err = dls_promisc(dsp->ds_dc, promisc);
651269Sericheng 	if (err != 0) {
652269Sericheng 		dl_err = DL_SYSERR;
653269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
654269Sericheng 			dls_active_clear(dsp->ds_dc);
655269Sericheng 		goto failed;
656269Sericheng 	}
657269Sericheng 
658*5895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
659269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
660269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
661*5895Syz147064 	dsp->ds_promisc = promisc;
662*5895Syz147064 	rw_exit(&dsp->ds_lock);
663269Sericheng 
664269Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
665269Sericheng 	return (B_TRUE);
6660Sstevel@tonic-gate failed:
667269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
668269Sericheng 	return (B_FALSE);
6690Sstevel@tonic-gate }
6700Sstevel@tonic-gate 
6710Sstevel@tonic-gate /*
672269Sericheng  * DL_PROMISCOFF_REQ
6730Sstevel@tonic-gate  */
674269Sericheng static boolean_t
675269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6760Sstevel@tonic-gate {
677269Sericheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp;
678269Sericheng 	int		err = 0;
679269Sericheng 	t_uscalar_t	dl_err;
680*5895Syz147064 	uint32_t	promisc;
681269Sericheng 	queue_t		*q = dsp->ds_wq;
682269Sericheng 
683*5895Syz147064 	/*
684*5895Syz147064 	 * Because control messages processing is serialized, we don't need
685*5895Syz147064 	 * to hold any lock to read any field of dsp; we hold ds_lock to
686*5895Syz147064 	 * update the ds_promisc field.
687*5895Syz147064 	 */
688269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
689269Sericheng 		dl_err = DL_BADPRIM;
6900Sstevel@tonic-gate 		goto failed;
691269Sericheng 	}
6920Sstevel@tonic-gate 
693269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
694269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
695269Sericheng 		dl_err = DL_OUTSTATE;
6960Sstevel@tonic-gate 		goto failed;
697269Sericheng 	}
6980Sstevel@tonic-gate 
699269Sericheng 	switch (dlp->dl_level) {
700269Sericheng 	case DL_PROMISC_SAP:
701*5895Syz147064 		promisc = DLS_PROMISC_SAP;
7020Sstevel@tonic-gate 		break;
703269Sericheng 	case DL_PROMISC_MULTI:
704*5895Syz147064 		promisc = DLS_PROMISC_MULTI;
705269Sericheng 		break;
706269Sericheng 	case DL_PROMISC_PHYS:
707*5895Syz147064 		promisc = DLS_PROMISC_PHYS;
7080Sstevel@tonic-gate 		break;
7090Sstevel@tonic-gate 	default:
710269Sericheng 		dl_err = DL_NOTSUPPORTED;
711269Sericheng 		goto failed;
712269Sericheng 	}
713269Sericheng 
714*5895Syz147064 	if (!(dsp->ds_promisc & promisc)) {
715*5895Syz147064 		dl_err = DL_NOTENAB;
716*5895Syz147064 		goto failed;
717*5895Syz147064 	}
718*5895Syz147064 
719*5895Syz147064 	promisc = (dsp->ds_promisc & ~promisc);
720*5895Syz147064 	err = dls_promisc(dsp->ds_dc, promisc);
721269Sericheng 	if (err != 0) {
7220Sstevel@tonic-gate 		dl_err = DL_SYSERR;
723269Sericheng 		goto failed;
724269Sericheng 	}
725269Sericheng 
726*5895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
727*5895Syz147064 	dsp->ds_promisc = promisc;
728269Sericheng 	rw_exit(&dsp->ds_lock);
729*5895Syz147064 
730269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
731269Sericheng 	return (B_TRUE);
732269Sericheng failed:
733269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
734269Sericheng 	return (B_FALSE);
735269Sericheng }
736269Sericheng 
737269Sericheng /*
738269Sericheng  * DL_ENABMULTI_REQ
739269Sericheng  */
740269Sericheng static boolean_t
741269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
742269Sericheng {
743269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
744269Sericheng 	int		err = 0;
745269Sericheng 	t_uscalar_t	dl_err;
746269Sericheng 	queue_t		*q = dsp->ds_wq;
747269Sericheng 
748*5895Syz147064 	/*
749*5895Syz147064 	 * Because control messages processing is serialized, we don't need
750*5895Syz147064 	 * to hold any lock to read any field of dsp; we hold ds_lock to
751*5895Syz147064 	 * update the ds_passivestate field.
752*5895Syz147064 	 */
753269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
754269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
755269Sericheng 		dl_err = DL_OUTSTATE;
756269Sericheng 		goto failed;
757269Sericheng 	}
758269Sericheng 
759269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
760269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
761269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
762269Sericheng 		dl_err = DL_BADPRIM;
763269Sericheng 		goto failed;
764269Sericheng 	}
765269Sericheng 
766269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
767269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
768269Sericheng 		dl_err = DL_SYSERR;
769269Sericheng 		err = EBUSY;
770269Sericheng 		goto failed;
7710Sstevel@tonic-gate 	}
7720Sstevel@tonic-gate 
773269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
774269Sericheng 	if (err != 0) {
775269Sericheng 		switch (err) {
776269Sericheng 		case EINVAL:
777269Sericheng 			dl_err = DL_BADADDR;
778269Sericheng 			err = 0;
779269Sericheng 			break;
780269Sericheng 		case ENOSPC:
781269Sericheng 			dl_err = DL_TOOMANY;
782269Sericheng 			err = 0;
783269Sericheng 			break;
784269Sericheng 		default:
785269Sericheng 			dl_err = DL_SYSERR;
786269Sericheng 			break;
787269Sericheng 		}
788*5895Syz147064 
789269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
790269Sericheng 			dls_active_clear(dsp->ds_dc);
791269Sericheng 
792269Sericheng 		goto failed;
793269Sericheng 	}
794269Sericheng 
795*5895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
796269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
797269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
798*5895Syz147064 	rw_exit(&dsp->ds_lock);
799269Sericheng 
800269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
801269Sericheng 	return (B_TRUE);
802269Sericheng failed:
803269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
804269Sericheng 	return (B_FALSE);
805269Sericheng }
806269Sericheng 
807269Sericheng /*
808269Sericheng  * DL_DISABMULTI_REQ
809269Sericheng  */
810269Sericheng static boolean_t
811269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
812269Sericheng {
813269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
814269Sericheng 	int		err = 0;
815269Sericheng 	t_uscalar_t	dl_err;
816269Sericheng 	queue_t		*q = dsp->ds_wq;
817269Sericheng 
818*5895Syz147064 	/*
819*5895Syz147064 	 * Because control messages processing is serialized, we don't need
820*5895Syz147064 	 * to hold any lock to read any field of dsp.
821*5895Syz147064 	 */
822269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
823269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
824269Sericheng 		dl_err = DL_OUTSTATE;
825269Sericheng 		goto failed;
826269Sericheng 	}
827269Sericheng 
828269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
829269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
830269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
831269Sericheng 		dl_err = DL_BADPRIM;
832269Sericheng 		goto failed;
833269Sericheng 	}
834269Sericheng 
835269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
836269Sericheng 	if (err != 0) {
837*5895Syz147064 		switch (err) {
838269Sericheng 		case EINVAL:
839269Sericheng 			dl_err = DL_BADADDR;
840269Sericheng 			err = 0;
841269Sericheng 			break;
842269Sericheng 		case ENOENT:
843269Sericheng 			dl_err = DL_NOTENAB;
844269Sericheng 			err = 0;
845269Sericheng 			break;
846269Sericheng 		default:
847269Sericheng 			dl_err = DL_SYSERR;
848269Sericheng 			break;
849269Sericheng 		}
850269Sericheng 		goto failed;
851269Sericheng 	}
852269Sericheng 
853269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
854269Sericheng 	return (B_TRUE);
855269Sericheng failed:
856269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
857269Sericheng 	return (B_FALSE);
8580Sstevel@tonic-gate }
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate /*
861269Sericheng  * DL_PHYS_ADDR_REQ
8620Sstevel@tonic-gate  */
863269Sericheng static boolean_t
864269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
8650Sstevel@tonic-gate {
866269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
867269Sericheng 	queue_t		*q = dsp->ds_wq;
868269Sericheng 	t_uscalar_t	dl_err;
869269Sericheng 	char		*addr;
870269Sericheng 	uint_t		addr_length;
871269Sericheng 
872269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
873269Sericheng 
874269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
875269Sericheng 		dl_err = DL_BADPRIM;
876269Sericheng 		goto failed;
877269Sericheng 	}
878269Sericheng 
879269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
880269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
881269Sericheng 		dl_err = DL_OUTSTATE;
882269Sericheng 		goto failed;
883269Sericheng 	}
8840Sstevel@tonic-gate 
885269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
886269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
887269Sericheng 		dl_err = DL_UNSUPPORTED;
8880Sstevel@tonic-gate 		goto failed;
889269Sericheng 	}
8900Sstevel@tonic-gate 
891269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
892269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
893269Sericheng 	if (addr == NULL) {
894269Sericheng 		rw_exit(&dsp->ds_lock);
895269Sericheng 		merror(q, mp, ENOSR);
896269Sericheng 		return (B_FALSE);
897269Sericheng 	}
8980Sstevel@tonic-gate 
899269Sericheng 	/*
900269Sericheng 	 * Copy out the address before we drop the lock; we don't
901269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
902269Sericheng 	 */
903269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
904269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
905269Sericheng 
906269Sericheng 	rw_exit(&dsp->ds_lock);
907269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
908269Sericheng 	kmem_free(addr, addr_length);
909269Sericheng 	return (B_TRUE);
9100Sstevel@tonic-gate failed:
911269Sericheng 	rw_exit(&dsp->ds_lock);
912269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
913269Sericheng 	return (B_FALSE);
914269Sericheng }
9150Sstevel@tonic-gate 
916269Sericheng /*
917269Sericheng  * DL_SET_PHYS_ADDR_REQ
918269Sericheng  */
919269Sericheng static boolean_t
920269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
921269Sericheng {
922269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
923269Sericheng 	int		err = 0;
924269Sericheng 	t_uscalar_t	dl_err;
925269Sericheng 	queue_t		*q = dsp->ds_wq;
9260Sstevel@tonic-gate 
927*5895Syz147064 	/*
928*5895Syz147064 	 * Because control message processing is serialized, we don't need
929*5895Syz147064 	 * to hold any locks to read any fields of dsp; we only need ds_lock
930*5895Syz147064 	 * to update the ds_passivestate field.
931*5895Syz147064 	 */
932269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
933269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
934269Sericheng 		dl_err = DL_OUTSTATE;
935269Sericheng 		goto failed;
936269Sericheng 	}
937269Sericheng 
938269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
939269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
940269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
941269Sericheng 		dl_err = DL_BADPRIM;
942269Sericheng 		goto failed;
9430Sstevel@tonic-gate 	}
9440Sstevel@tonic-gate 
945269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
946269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
947269Sericheng 		dl_err = DL_SYSERR;
948269Sericheng 		err = EBUSY;
949269Sericheng 		goto failed;
950269Sericheng 	}
951269Sericheng 
952269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
953269Sericheng 	if (err != 0) {
954269Sericheng 		switch (err) {
955269Sericheng 		case EINVAL:
956269Sericheng 			dl_err = DL_BADADDR;
957269Sericheng 			err = 0;
958269Sericheng 			break;
959269Sericheng 
960269Sericheng 		default:
961269Sericheng 			dl_err = DL_SYSERR;
962269Sericheng 			break;
963269Sericheng 		}
964*5895Syz147064 
965269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
966269Sericheng 			dls_active_clear(dsp->ds_dc);
967269Sericheng 
968269Sericheng 		goto failed;
969269Sericheng 	}
970*5895Syz147064 
971*5895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
972269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
973269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
974*5895Syz147064 	rw_exit(&dsp->ds_lock);
975269Sericheng 
976269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
977269Sericheng 	return (B_TRUE);
978269Sericheng failed:
979269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
980269Sericheng 	return (B_FALSE);
981269Sericheng }
982269Sericheng 
983269Sericheng /*
984269Sericheng  * DL_UDQOS_REQ
985269Sericheng  */
986269Sericheng static boolean_t
987269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
988269Sericheng {
989269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
990269Sericheng 	dl_qos_cl_sel1_t *selp;
991269Sericheng 	int		off, len;
992269Sericheng 	t_uscalar_t	dl_err;
993269Sericheng 	queue_t		*q = dsp->ds_wq;
994269Sericheng 
995269Sericheng 	off = dlp->dl_qos_offset;
996269Sericheng 	len = dlp->dl_qos_length;
997269Sericheng 
998269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
999269Sericheng 		dl_err = DL_BADPRIM;
1000269Sericheng 		goto failed;
1001269Sericheng 	}
1002269Sericheng 
1003269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1004269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1005269Sericheng 		dl_err = DL_BADQOSTYPE;
1006269Sericheng 		goto failed;
1007269Sericheng 	}
1008269Sericheng 
10092760Sdg199075 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1010269Sericheng 	    selp->dl_priority < 0) {
1011269Sericheng 		dl_err = DL_BADQOSPARAM;
1012269Sericheng 		goto failed;
1013269Sericheng 	}
1014269Sericheng 
1015*5895Syz147064 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1016*5895Syz147064 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1017*5895Syz147064 		dl_err = DL_OUTSTATE;
1018*5895Syz147064 		goto failed;
1019*5895Syz147064 	}
1020269Sericheng 
1021*5895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
1022*5895Syz147064 	dsp->ds_pri = selp->dl_priority;
1023269Sericheng 	rw_exit(&dsp->ds_lock);
1024*5895Syz147064 
1025269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1026269Sericheng 	return (B_TRUE);
1027269Sericheng failed:
1028269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1029269Sericheng 	return (B_FALSE);
10300Sstevel@tonic-gate }
10310Sstevel@tonic-gate 
10321184Skrgopi static boolean_t
10331184Skrgopi check_ip_above(queue_t *q)
10341184Skrgopi {
10351184Skrgopi 	queue_t		*next_q;
10361184Skrgopi 	boolean_t	ret = B_TRUE;
10371184Skrgopi 
10381184Skrgopi 	claimstr(q);
10391184Skrgopi 	next_q = q->q_next;
10401184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
10411184Skrgopi 		ret = B_FALSE;
10421184Skrgopi 	releasestr(q);
10431184Skrgopi 	return (ret);
10441184Skrgopi }
10451184Skrgopi 
10460Sstevel@tonic-gate /*
1047269Sericheng  * DL_CAPABILITY_REQ
10480Sstevel@tonic-gate  */
1049269Sericheng /*ARGSUSED*/
1050269Sericheng static boolean_t
1051269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
10520Sstevel@tonic-gate {
1053269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1054269Sericheng 	dl_capability_sub_t *sp;
1055269Sericheng 	size_t		size, len;
1056269Sericheng 	offset_t	off, end;
1057269Sericheng 	t_uscalar_t	dl_err;
1058269Sericheng 	queue_t		*q = dsp->ds_wq;
1059269Sericheng 
1060*5895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
1061269Sericheng 
1062269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1063269Sericheng 		dl_err = DL_BADPRIM;
1064269Sericheng 		goto failed;
1065269Sericheng 	}
1066269Sericheng 
1067269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1068269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1069269Sericheng 		dl_err = DL_OUTSTATE;
1070269Sericheng 		goto failed;
1071269Sericheng 	}
1072269Sericheng 
1073269Sericheng 	/*
1074269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1075269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1076269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1077269Sericheng 	 */
1078269Sericheng 	if (dlp->dl_sub_length == 0) {
1079269Sericheng 		/* callee drops lock */
1080269Sericheng 		return (proto_capability_advertise(dsp, mp));
1081269Sericheng 	}
1082269Sericheng 
1083269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1084269Sericheng 		dl_err = DL_BADPRIM;
1085269Sericheng 		goto failed;
1086269Sericheng 	}
1087269Sericheng 
1088269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1089269Sericheng 
1090269Sericheng 	off = dlp->dl_sub_offset;
1091269Sericheng 	len = dlp->dl_sub_length;
10920Sstevel@tonic-gate 
10930Sstevel@tonic-gate 	/*
1094269Sericheng 	 * Walk the list of capabilities to be enabled.
10950Sstevel@tonic-gate 	 */
1096269Sericheng 	for (end = off + len; off < end; ) {
1097269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1098269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1099269Sericheng 
1100269Sericheng 		if (off + size > end ||
1101269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1102269Sericheng 			dl_err = DL_BADPRIM;
1103269Sericheng 			goto failed;
1104269Sericheng 		}
1105269Sericheng 
1106269Sericheng 		switch (sp->dl_cap) {
1107269Sericheng 		/*
1108269Sericheng 		 * TCP/IP checksum offload to hardware.
1109269Sericheng 		 */
1110269Sericheng 		case DL_CAPAB_HCKSUM: {
1111269Sericheng 			dl_capab_hcksum_t *hcksump;
1112269Sericheng 			dl_capab_hcksum_t hcksum;
1113269Sericheng 
1114269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1115269Sericheng 			/*
1116269Sericheng 			 * Copy for alignment.
1117269Sericheng 			 */
1118269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1119269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1120269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1121269Sericheng 			break;
1122269Sericheng 		}
1123269Sericheng 
1124269Sericheng 		/*
11253115Syl150051 		 * Large segment offload. (LSO)
11263115Syl150051 		 */
11273115Syl150051 		case DL_CAPAB_LSO: {
11283115Syl150051 			dl_capab_lso_t *lsop;
11293115Syl150051 			dl_capab_lso_t lso;
11303115Syl150051 
11313115Syl150051 			lsop = (dl_capab_lso_t *)&sp[1];
11323115Syl150051 			/*
11333115Syl150051 			 * Copy for alignment.
11343115Syl150051 			 */
11353115Syl150051 			bcopy(lsop, &lso, sizeof (dl_capab_lso_t));
11363115Syl150051 			dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
11373115Syl150051 			bcopy(&lso, lsop, sizeof (dl_capab_lso_t));
11383115Syl150051 			break;
11393115Syl150051 		}
11403115Syl150051 
11413115Syl150051 		/*
1142269Sericheng 		 * IP polling interface.
1143269Sericheng 		 */
1144269Sericheng 		case DL_CAPAB_POLL: {
11451184Skrgopi 			dl_capab_dls_t *pollp;
11461184Skrgopi 			dl_capab_dls_t	poll;
1147269Sericheng 
11481184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1149269Sericheng 			/*
1150269Sericheng 			 * Copy for alignment.
1151269Sericheng 			 */
11521184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1153269Sericheng 
11541184Skrgopi 			switch (poll.dls_flags) {
1155269Sericheng 			default:
1156269Sericheng 				/*FALLTHRU*/
1157269Sericheng 			case POLL_DISABLE:
1158269Sericheng 				proto_poll_disable(dsp);
1159269Sericheng 				break;
1160269Sericheng 
1161269Sericheng 			case POLL_ENABLE:
1162269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1163269Sericheng 
1164269Sericheng 				/*
1165269Sericheng 				 * Make sure polling is disabled.
1166269Sericheng 				 */
1167269Sericheng 				proto_poll_disable(dsp);
1168269Sericheng 
1169269Sericheng 				/*
1170*5895Syz147064 				 * Note that only IP should enable POLL.
1171269Sericheng 				 */
11721184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
11731184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
11741184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
11751184Skrgopi 					poll.dls_flags = POLL_ENABLE;
1176*5895Syz147064 				} else {
1177*5895Syz147064 					bzero(&poll, sizeof (dl_capab_dls_t));
1178*5895Syz147064 					poll.dls_flags = POLL_DISABLE;
11791184Skrgopi 				}
1180269Sericheng 				break;
1181269Sericheng 			}
1182269Sericheng 
11831184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
11841184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
11851184Skrgopi 			break;
11861184Skrgopi 		}
11871184Skrgopi 		case DL_CAPAB_SOFT_RING: {
11881184Skrgopi 			dl_capab_dls_t *soft_ringp;
11891184Skrgopi 			dl_capab_dls_t soft_ring;
11901184Skrgopi 
11911184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
11921184Skrgopi 			/*
11931184Skrgopi 			 * Copy for alignment.
11941184Skrgopi 			 */
11951184Skrgopi 			bcopy(soft_ringp, &soft_ring,
11961184Skrgopi 			    sizeof (dl_capab_dls_t));
11971184Skrgopi 
11981184Skrgopi 			switch (soft_ring.dls_flags) {
11991184Skrgopi 			default:
12001184Skrgopi 				/*FALLTHRU*/
12011184Skrgopi 			case SOFT_RING_DISABLE:
12021184Skrgopi 				proto_soft_ring_disable(dsp);
12031184Skrgopi 				break;
12041184Skrgopi 
12051184Skrgopi 			case SOFT_RING_ENABLE:
12064114Sja97890 				ASSERT(!(dld_opt & DLD_OPT_NO_SOFTRING));
12071184Skrgopi 				/*
12081184Skrgopi 				 * Make sure soft_ring is disabled.
12091184Skrgopi 				 */
12101184Skrgopi 				proto_soft_ring_disable(dsp);
12111184Skrgopi 
12121184Skrgopi 				/*
1213*5895Syz147064 				 * Note that only IP can enable soft ring.
12141184Skrgopi 				 */
12151184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
12161184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
12171184Skrgopi 					bzero(&soft_ring,
12181184Skrgopi 					    sizeof (dl_capab_dls_t));
1219*5895Syz147064 					soft_ring.dls_flags = SOFT_RING_ENABLE;
12201184Skrgopi 				} else {
12211184Skrgopi 					bzero(&soft_ring,
12221184Skrgopi 					    sizeof (dl_capab_dls_t));
1223*5895Syz147064 					soft_ring.dls_flags = SOFT_RING_DISABLE;
12241184Skrgopi 				}
12251184Skrgopi 				break;
12261184Skrgopi 			}
12271184Skrgopi 
12281184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
12291184Skrgopi 			bcopy(&soft_ring, soft_ringp,
12301184Skrgopi 			    sizeof (dl_capab_dls_t));
1231269Sericheng 			break;
1232269Sericheng 		}
1233269Sericheng 		default:
1234269Sericheng 			break;
1235269Sericheng 		}
1236269Sericheng 
1237269Sericheng 		off += size;
1238269Sericheng 	}
1239269Sericheng 	rw_exit(&dsp->ds_lock);
1240269Sericheng 	qreply(q, mp);
1241269Sericheng 	return (B_TRUE);
1242269Sericheng failed:
1243269Sericheng 	rw_exit(&dsp->ds_lock);
1244269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1245269Sericheng 	return (B_FALSE);
12460Sstevel@tonic-gate }
12470Sstevel@tonic-gate 
12480Sstevel@tonic-gate /*
1249269Sericheng  * DL_NOTIFY_REQ
12500Sstevel@tonic-gate  */
1251269Sericheng static boolean_t
1252269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
12530Sstevel@tonic-gate {
1254269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1255269Sericheng 	t_uscalar_t	dl_err;
1256269Sericheng 	queue_t		*q = dsp->ds_wq;
1257269Sericheng 	uint_t		note =
1258269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1259269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1260269Sericheng 	    DL_NOTE_PHYS_ADDR |
1261269Sericheng 	    DL_NOTE_LINK_UP |
1262269Sericheng 	    DL_NOTE_LINK_DOWN |
12632311Sseb 	    DL_NOTE_CAPAB_RENEG |
12642311Sseb 	    DL_NOTE_SPEED;
12650Sstevel@tonic-gate 
12661521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
12671521Syz147064 
1268269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1269269Sericheng 		dl_err = DL_BADPRIM;
1270269Sericheng 		goto failed;
1271269Sericheng 	}
12720Sstevel@tonic-gate 
1273269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1274269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1275269Sericheng 		dl_err = DL_OUTSTATE;
1276269Sericheng 		goto failed;
12770Sstevel@tonic-gate 	}
12780Sstevel@tonic-gate 
1279*5895Syz147064 	note &= ~(mac_no_notification(dsp->ds_mh));
1280*5895Syz147064 
1281269Sericheng 	/*
1282269Sericheng 	 * Cache the notifications that are being enabled.
1283269Sericheng 	 */
1284269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1285269Sericheng 	rw_exit(&dsp->ds_lock);
1286269Sericheng 	/*
1287269Sericheng 	 * The ACK carries all notifications regardless of which set is
1288269Sericheng 	 * being enabled.
1289269Sericheng 	 */
1290269Sericheng 	dlnotifyack(q, mp, note);
1291269Sericheng 
1292269Sericheng 	/*
1293269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1294269Sericheng 	 */
1295269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1296269Sericheng 	if (dsp->ds_notifications != 0) {
1297269Sericheng 		rw_exit(&dsp->ds_lock);
1298269Sericheng 		dld_str_notify_ind(dsp);
1299269Sericheng 	} else {
1300269Sericheng 		rw_exit(&dsp->ds_lock);
1301269Sericheng 	}
1302269Sericheng 	return (B_TRUE);
1303269Sericheng failed:
1304269Sericheng 	rw_exit(&dsp->ds_lock);
1305269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1306269Sericheng 	return (B_FALSE);
13070Sstevel@tonic-gate }
13080Sstevel@tonic-gate 
13090Sstevel@tonic-gate /*
1310*5895Syz147064  * DL_UNITDATA_REQ
13110Sstevel@tonic-gate  */
1312*5895Syz147064 void
1313*5895Syz147064 dld_wput_proto_data(dld_str_t *dsp, mblk_t *mp)
13140Sstevel@tonic-gate {
1315269Sericheng 	queue_t			*q = dsp->ds_wq;
1316*5895Syz147064 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)mp->b_rptr;
1317269Sericheng 	off_t			off;
1318269Sericheng 	size_t			len, size;
1319269Sericheng 	const uint8_t		*addr;
1320269Sericheng 	uint16_t		sap;
1321269Sericheng 	uint_t			addr_length;
13222311Sseb 	mblk_t			*bp, *payload;
1323269Sericheng 	uint32_t		start, stuff, end, value, flags;
1324269Sericheng 	t_uscalar_t		dl_err;
1325269Sericheng 
1326269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1327269Sericheng 		dl_err = DL_BADPRIM;
1328269Sericheng 		goto failed;
1329269Sericheng 	}
1330269Sericheng 
1331269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1332269Sericheng 
1333269Sericheng 	off = dlp->dl_dest_addr_offset;
1334269Sericheng 	len = dlp->dl_dest_addr_length;
1335269Sericheng 
1336269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1337269Sericheng 		dl_err = DL_BADPRIM;
1338269Sericheng 		goto failed;
1339269Sericheng 	}
1340269Sericheng 
1341269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1342269Sericheng 		dl_err = DL_BADADDR;
1343269Sericheng 		goto failed;
1344269Sericheng 	}
1345269Sericheng 
1346269Sericheng 	addr = mp->b_rptr + off;
1347269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1348269Sericheng 
1349269Sericheng 	/*
1350269Sericheng 	 * Check the length of the packet and the block types.
1351269Sericheng 	 */
1352269Sericheng 	size = 0;
13532311Sseb 	payload = mp->b_cont;
13542311Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1355269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1356269Sericheng 			goto baddata;
1357269Sericheng 
1358269Sericheng 		size += MBLKL(bp);
1359269Sericheng 	}
1360269Sericheng 
1361269Sericheng 	if (size > dsp->ds_mip->mi_sdu_max)
1362269Sericheng 		goto baddata;
1363269Sericheng 
1364269Sericheng 	/*
1365269Sericheng 	 * Build a packet header.
1366269Sericheng 	 */
13672760Sdg199075 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dlp->dl_priority.dl_max,
13682760Sdg199075 	    &payload)) == NULL) {
1369269Sericheng 		dl_err = DL_BADADDR;
1370269Sericheng 		goto failed;
1371269Sericheng 	}
1372269Sericheng 
1373269Sericheng 	/*
1374269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1375269Sericheng 	 */
1376269Sericheng 	freeb(mp);
1377269Sericheng 
1378269Sericheng 	/*
1379269Sericheng 	 * Transfer the checksum offload information if it is present.
1380269Sericheng 	 */
13812311Sseb 	hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1382269Sericheng 	    &flags);
13832311Sseb 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1384269Sericheng 
1385269Sericheng 	/*
1386269Sericheng 	 * Link the payload onto the new header.
1387269Sericheng 	 */
1388269Sericheng 	ASSERT(bp->b_cont == NULL);
13892311Sseb 	bp->b_cont = payload;
13902760Sdg199075 	dld_tx_single(dsp, bp);
1391*5895Syz147064 	return;
1392269Sericheng failed:
1393269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1394*5895Syz147064 	return;
1395269Sericheng 
1396269Sericheng baddata:
1397269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1398269Sericheng }
1399269Sericheng 
1400269Sericheng /*
1401269Sericheng  * DL_PASSIVE_REQ
1402269Sericheng  */
1403269Sericheng /* ARGSUSED */
1404269Sericheng static boolean_t
1405269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1406269Sericheng {
1407269Sericheng 	t_uscalar_t dl_err;
1408269Sericheng 
1409*5895Syz147064 	/*
1410*5895Syz147064 	 * READER lock is enough because ds_passivestate can only be changed
1411*5895Syz147064 	 * as the result of non-data message processing.
1412*5895Syz147064 	 */
1413*5895Syz147064 	rw_enter(&dsp->ds_lock, RW_READER);
1414*5895Syz147064 
1415269Sericheng 	/*
1416269Sericheng 	 * If we've already become active by issuing an active primitive,
1417269Sericheng 	 * then it's too late to try to become passive.
1418269Sericheng 	 */
1419269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1420269Sericheng 		dl_err = DL_OUTSTATE;
1421269Sericheng 		goto failed;
1422269Sericheng 	}
1423269Sericheng 
1424269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1425269Sericheng 		dl_err = DL_BADPRIM;
1426269Sericheng 		goto failed;
1427269Sericheng 	}
1428269Sericheng 
1429269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1430269Sericheng 	rw_exit(&dsp->ds_lock);
1431269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1432269Sericheng 	return (B_TRUE);
1433269Sericheng failed:
1434269Sericheng 	rw_exit(&dsp->ds_lock);
1435269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1436269Sericheng 	return (B_FALSE);
1437269Sericheng }
1438269Sericheng 
1439269Sericheng /*
1440269Sericheng  * Catch-all handler.
1441269Sericheng  */
1442269Sericheng static boolean_t
1443269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1444269Sericheng {
1445269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1446269Sericheng 	return (B_FALSE);
14470Sstevel@tonic-gate }
14480Sstevel@tonic-gate 
14490Sstevel@tonic-gate static void
14500Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
14510Sstevel@tonic-gate {
14520Sstevel@tonic-gate 	mac_handle_t	mh;
14530Sstevel@tonic-gate 
1454*5895Syz147064 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
1455269Sericheng 
14560Sstevel@tonic-gate 	if (!dsp->ds_polling)
14570Sstevel@tonic-gate 		return;
14580Sstevel@tonic-gate 
14590Sstevel@tonic-gate 	/*
14600Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
14610Sstevel@tonic-gate 	 */
14620Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
14630Sstevel@tonic-gate 
14640Sstevel@tonic-gate 	/*
14650Sstevel@tonic-gate 	 * Reset the resource_add callback.
14660Sstevel@tonic-gate 	 */
14670Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
14680Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
14691184Skrgopi 	mac_resources(mh);
14700Sstevel@tonic-gate 
14710Sstevel@tonic-gate 	/*
14720Sstevel@tonic-gate 	 * Set receive function back to default.
14730Sstevel@tonic-gate 	 */
14740Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
1475*5895Syz147064 	    dld_str_rx_fastpath : dld_str_rx_unitdata, dsp);
14760Sstevel@tonic-gate 
14770Sstevel@tonic-gate 	/*
14780Sstevel@tonic-gate 	 * Note that polling is disabled.
14790Sstevel@tonic-gate 	 */
14800Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
14810Sstevel@tonic-gate }
14820Sstevel@tonic-gate 
14830Sstevel@tonic-gate static boolean_t
14841184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
14850Sstevel@tonic-gate {
14860Sstevel@tonic-gate 	mac_handle_t	mh;
14870Sstevel@tonic-gate 
1488269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
14890Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
14900Sstevel@tonic-gate 
14910Sstevel@tonic-gate 	/*
14920Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
14930Sstevel@tonic-gate 	 * has been enabled.
14940Sstevel@tonic-gate 	 */
14950Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
14960Sstevel@tonic-gate 		return (B_FALSE);
14970Sstevel@tonic-gate 
14980Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
14990Sstevel@tonic-gate 
15000Sstevel@tonic-gate 	/*
15010Sstevel@tonic-gate 	 * Register resources.
15020Sstevel@tonic-gate 	 */
15031184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
15041184Skrgopi 	    (void *)pollp->dls_rx_handle);
1505*5895Syz147064 
15060Sstevel@tonic-gate 	mac_resources(mh);
15070Sstevel@tonic-gate 
15080Sstevel@tonic-gate 	/*
1509*5895Syz147064 	 * Set the upstream receive function.
15100Sstevel@tonic-gate 	 */
15111184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
15121184Skrgopi 	    (void *)pollp->dls_rx_handle);
15130Sstevel@tonic-gate 
15140Sstevel@tonic-gate 	/*
15150Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
15160Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
15170Sstevel@tonic-gate 	 */
15180Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
15190Sstevel@tonic-gate 	return (B_TRUE);
15200Sstevel@tonic-gate }
15210Sstevel@tonic-gate 
15221184Skrgopi static void
15231184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
15241184Skrgopi {
15251184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
15261184Skrgopi 
15271184Skrgopi 	if (!dsp->ds_soft_ring)
15281184Skrgopi 		return;
15291184Skrgopi 
15301184Skrgopi 	/*
15311184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
15321184Skrgopi 	 */
15331184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
15341184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
15351184Skrgopi 	/*
15361184Skrgopi 	 * Note that fanout is disabled.
15371184Skrgopi 	 */
15381184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
15391184Skrgopi }
15401184Skrgopi 
15411184Skrgopi static boolean_t
15421184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
15431184Skrgopi {
15441184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
15451184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
15461184Skrgopi 
15471184Skrgopi 	/*
15481184Skrgopi 	 * We cannot enable soft_ring if raw mode
15491184Skrgopi 	 * has been enabled.
15501184Skrgopi 	 */
15511184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
15521184Skrgopi 		return (B_FALSE);
15531184Skrgopi 
15541184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
15551184Skrgopi 		return (B_FALSE);
15561184Skrgopi 
15571184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
15581184Skrgopi 	return (B_TRUE);
15591184Skrgopi }
15601184Skrgopi 
15611184Skrgopi static void
15621184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
15631184Skrgopi {
1564*5895Syz147064 	dls_channel_t	dc = dsp->ds_dc;
15651184Skrgopi 
15661184Skrgopi 	if (type == SOFT_RING_NONE) {
1567*5895Syz147064 		dls_rx_set(dc, (dsp->ds_mode == DLD_FASTPATH) ?
1568*5895Syz147064 		    dld_str_rx_fastpath : dld_str_rx_unitdata, dsp);
1569*5895Syz147064 	} else if (type != SOFT_RING_NONE) {
1570*5895Syz147064 		dls_rx_set(dc, (dls_rx_t)dls_soft_ring_fanout, dc);
15711184Skrgopi 	}
15721184Skrgopi }
15731184Skrgopi 
15740Sstevel@tonic-gate /*
15750Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
15760Sstevel@tonic-gate  */
1577269Sericheng static boolean_t
1578269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
15790Sstevel@tonic-gate {
15800Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
15810Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
15820Sstevel@tonic-gate 	size_t			subsize;
15831184Skrgopi 	dl_capab_dls_t		poll;
15843115Syl150051 	dl_capab_dls_t		soft_ring;
15850Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
15863115Syl150051 	dl_capab_lso_t		lso;
15870Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
15880Sstevel@tonic-gate 	uint8_t			*ptr;
1589269Sericheng 	queue_t			*q = dsp->ds_wq;
1590269Sericheng 	mblk_t			*mp1;
1591*5895Syz147064 	boolean_t		is_vlan = (dsp->ds_vid != VLAN_ID_NONE);
1592*5895Syz147064 	boolean_t		poll_capable = B_FALSE;
1593*5895Syz147064 	boolean_t		soft_ring_capable = B_FALSE;
1594*5895Syz147064 	boolean_t		hcksum_capable = B_FALSE;
1595*5895Syz147064 	boolean_t		zcopy_capable = B_FALSE;
1596*5895Syz147064 	boolean_t		lso_capable = B_FALSE;
1597*5895Syz147064 	mac_capab_lso_t		mac_lso;
1598269Sericheng 
1599*5895Syz147064 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16000Sstevel@tonic-gate 
16010Sstevel@tonic-gate 	/*
16020Sstevel@tonic-gate 	 * Initially assume no capabilities.
16030Sstevel@tonic-gate 	 */
16040Sstevel@tonic-gate 	subsize = 0;
16050Sstevel@tonic-gate 
16061555Skrgopi 	/*
1607*5895Syz147064 	 * Check if soft ring can be enabled on this interface. Note that we
1608*5895Syz147064 	 * do not enable softring on any legacy drivers, because doing that
1609*5895Syz147064 	 * would hurt the performance if the legacy driver has its own taskq
1610*5895Syz147064 	 * implementation. Further, most high-performance legacy drivers do
1611*5895Syz147064 	 * have their own taskq implementation.
1612*5895Syz147064 	 *
1613*5895Syz147064 	 * If advertising DL_CAPAB_SOFT_RING has not been explicitly disabled,
1614*5895Syz147064 	 * reserve space for that capability.
16151555Skrgopi 	 */
1616*5895Syz147064 	if (!mac_is_legacy(dsp->ds_mh) && !(dld_opt & DLD_OPT_NO_SOFTRING)) {
1617*5895Syz147064 		soft_ring_capable = B_TRUE;
16181555Skrgopi 		subsize += sizeof (dl_capability_sub_t) +
16195113Syz147064 		    sizeof (dl_capab_dls_t);
16204114Sja97890 	}
16211184Skrgopi 
16220Sstevel@tonic-gate 	/*
16230Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
16240Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
16250Sstevel@tonic-gate 	 * then reserve space for that capability.
16260Sstevel@tonic-gate 	 */
1627*5895Syz147064 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) &&
1628*5895Syz147064 	    !(dld_opt & DLD_OPT_NO_POLL) && !is_vlan) {
1629*5895Syz147064 		poll_capable = B_TRUE;
16300Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
16311184Skrgopi 		    sizeof (dl_capab_dls_t);
16320Sstevel@tonic-gate 	}
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 	/*
1635*5895Syz147064 	 * Check if checksum offload is supported on this MAC.  Don't
1636*5895Syz147064 	 * advertise DL_CAPAB_HCKSUM if the underlying MAC is VLAN incapable,
1637*5895Syz147064 	 * since it might not be able to do the hardware checksum offload
1638*5895Syz147064 	 * with the correct offset.
16390Sstevel@tonic-gate 	 */
1640*5895Syz147064 	bzero(&hcksum, sizeof (dl_capab_hcksum_t));
1641*5895Syz147064 	if ((!is_vlan || (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_NATIVEVLAN,
1642*5895Syz147064 	    NULL))) && mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
16432311Sseb 	    &hcksum.hcksum_txflags)) {
1644*5895Syz147064 		if (hcksum.hcksum_txflags != 0) {
1645*5895Syz147064 			hcksum_capable = B_TRUE;
1646*5895Syz147064 			subsize += sizeof (dl_capability_sub_t) +
1647*5895Syz147064 			    sizeof (dl_capab_hcksum_t);
1648*5895Syz147064 		}
16490Sstevel@tonic-gate 	}
16500Sstevel@tonic-gate 
16510Sstevel@tonic-gate 	/*
1652*5895Syz147064 	 * Check if LSO is supported on this MAC, then reserve space for
1653*5895Syz147064 	 * the DL_CAPAB_LSO capability.
16543115Syl150051 	 */
1655*5895Syz147064 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
1656*5895Syz147064 		lso_capable = B_TRUE;
16573115Syl150051 		subsize += sizeof (dl_capability_sub_t) +
16583115Syl150051 		    sizeof (dl_capab_lso_t);
16593115Syl150051 	}
16603115Syl150051 
16613115Syl150051 	/*
1662*5895Syz147064 	 * Check if zerocopy is supported on this interface.
1663*5895Syz147064 	 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled
1664*5895Syz147064 	 * then reserve space for that capability.
16650Sstevel@tonic-gate 	 */
1666*5895Syz147064 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) &&
1667*5895Syz147064 	    !(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
1668*5895Syz147064 		zcopy_capable = B_TRUE;
16690Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
16700Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
16710Sstevel@tonic-gate 	}
16720Sstevel@tonic-gate 
16730Sstevel@tonic-gate 	/*
1674269Sericheng 	 * If there are no capabilities to advertise or if we
1675269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
16760Sstevel@tonic-gate 	 */
16771184Skrgopi 	if ((mp1 = reallocb(mp,
1678269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1679269Sericheng 		rw_exit(&dsp->ds_lock);
1680269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1681269Sericheng 		return (B_FALSE);
16820Sstevel@tonic-gate 	}
16830Sstevel@tonic-gate 
1684269Sericheng 	mp = mp1;
1685269Sericheng 	DB_TYPE(mp) = M_PROTO;
1686269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1687269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
16880Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
16890Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
16900Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
16910Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
16920Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
16930Sstevel@tonic-gate 
16940Sstevel@tonic-gate 	/*
16950Sstevel@tonic-gate 	 * IP polling interface.
16960Sstevel@tonic-gate 	 */
1697*5895Syz147064 	if (poll_capable) {
16980Sstevel@tonic-gate 		/*
1699269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
1700*5895Syz147064 		 * READER lock is enough because ds_polling can only be
1701*5895Syz147064 		 * changed as the result of non-data message processing.
1702269Sericheng 		 */
1703*5895Syz147064 		proto_poll_disable(dsp);
17040Sstevel@tonic-gate 
1705*5895Syz147064 		dlsp = (dl_capability_sub_t *)ptr;
17060Sstevel@tonic-gate 
1707*5895Syz147064 		dlsp->dl_cap = DL_CAPAB_POLL;
1708*5895Syz147064 		dlsp->dl_length = sizeof (dl_capab_dls_t);
1709*5895Syz147064 		ptr += sizeof (dl_capability_sub_t);
1710269Sericheng 
1711*5895Syz147064 		bzero(&poll, sizeof (dl_capab_dls_t));
1712*5895Syz147064 		poll.dls_version = POLL_VERSION_1;
1713*5895Syz147064 		poll.dls_flags = POLL_CAPABLE;
1714*5895Syz147064 		poll.dls_tx_handle = (uintptr_t)dsp;
1715*5895Syz147064 		poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
1716*5895Syz147064 		dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
1717*5895Syz147064 		bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
1718*5895Syz147064 		ptr += sizeof (dl_capab_dls_t);
17190Sstevel@tonic-gate 	}
17200Sstevel@tonic-gate 
1721269Sericheng 
1722*5895Syz147064 	if (soft_ring_capable) {
17231555Skrgopi 		dlsp = (dl_capability_sub_t *)ptr;
17241184Skrgopi 
17251555Skrgopi 		dlsp->dl_cap = DL_CAPAB_SOFT_RING;
17261555Skrgopi 		dlsp->dl_length = sizeof (dl_capab_dls_t);
17271555Skrgopi 		ptr += sizeof (dl_capability_sub_t);
17281184Skrgopi 
17291555Skrgopi 		bzero(&soft_ring, sizeof (dl_capab_dls_t));
17301555Skrgopi 		soft_ring.dls_version = SOFT_RING_VERSION_1;
17311555Skrgopi 		soft_ring.dls_flags = SOFT_RING_CAPABLE;
17321555Skrgopi 		soft_ring.dls_tx_handle = (uintptr_t)dsp;
17331555Skrgopi 		soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
17341555Skrgopi 		soft_ring.dls_ring_change_status =
17351555Skrgopi 		    (uintptr_t)proto_change_soft_ring_fanout;
17361555Skrgopi 		soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
17371555Skrgopi 		soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
17381184Skrgopi 
17391555Skrgopi 		dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
17401555Skrgopi 		bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
17411555Skrgopi 		ptr += sizeof (dl_capab_dls_t);
17421555Skrgopi 	}
17431184Skrgopi 
17440Sstevel@tonic-gate 	/*
17450Sstevel@tonic-gate 	 * TCP/IP checksum offload.
17460Sstevel@tonic-gate 	 */
1747*5895Syz147064 	if (hcksum_capable) {
17480Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
17490Sstevel@tonic-gate 
17500Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
17510Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
17520Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
17530Sstevel@tonic-gate 
17540Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
17550Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
17560Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
17570Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
17580Sstevel@tonic-gate 	}
17590Sstevel@tonic-gate 
17600Sstevel@tonic-gate 	/*
17613115Syl150051 	 * Large segment offload. (LSO)
17623115Syl150051 	 */
1763*5895Syz147064 	if (lso_capable) {
17643115Syl150051 		dlsp = (dl_capability_sub_t *)ptr;
17653115Syl150051 
17663115Syl150051 		dlsp->dl_cap = DL_CAPAB_LSO;
17673115Syl150051 		dlsp->dl_length = sizeof (dl_capab_lso_t);
17683115Syl150051 		ptr += sizeof (dl_capability_sub_t);
17693115Syl150051 
17703115Syl150051 		lso.lso_version = LSO_VERSION_1;
17713115Syl150051 		lso.lso_flags = mac_lso.lso_flags;
17723115Syl150051 		lso.lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max;
17733115Syl150051 
17743115Syl150051 		/* Simply enable LSO with DLD */
17753115Syl150051 		dsp->ds_lso = B_TRUE;
17763115Syl150051 		dsp->ds_lso_max = lso.lso_max;
17773115Syl150051 
17783115Syl150051 		dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
17793115Syl150051 		bcopy(&lso, ptr, sizeof (dl_capab_lso_t));
17803115Syl150051 		ptr += sizeof (dl_capab_lso_t);
17813115Syl150051 	} else {
17823115Syl150051 		dsp->ds_lso = B_FALSE;
17833115Syl150051 		dsp->ds_lso_max = 0;
17843115Syl150051 	}
17853115Syl150051 
17863115Syl150051 	/*
17870Sstevel@tonic-gate 	 * Zero copy
17880Sstevel@tonic-gate 	 */
1789*5895Syz147064 	if (zcopy_capable) {
17900Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
17910Sstevel@tonic-gate 
17920Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
17930Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
17940Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
17950Sstevel@tonic-gate 
17960Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
17970Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
17980Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
17990Sstevel@tonic-gate 
18000Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
18010Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
18020Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
18030Sstevel@tonic-gate 	}
18040Sstevel@tonic-gate 
18050Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1806269Sericheng 
1807269Sericheng 	rw_exit(&dsp->ds_lock);
1808269Sericheng 	qreply(q, mp);
1809269Sericheng 	return (B_TRUE);
18100Sstevel@tonic-gate }
18115113Syz147064 
18125113Syz147064 /*
18135113Syz147064  * Disable any enabled capabilities.
18145113Syz147064  */
18155113Syz147064 void
18165113Syz147064 dld_capabilities_disable(dld_str_t *dsp)
18175113Syz147064 {
18185113Syz147064 	if (dsp->ds_polling)
18195113Syz147064 		proto_poll_disable(dsp);
18205113Syz147064 
18215113Syz147064 	if (dsp->ds_soft_ring)
18225113Syz147064 		proto_soft_ring_disable(dsp);
18235113Syz147064 }
1824