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 /*
225895Syz147064  * 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,
575895Syz147064     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
825895Syz147064 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 
875895Syz147064 	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;
241*5903Ssowmini 		/* We can only get the sdu if we're attached. */
242*5903Ssowmini 		mac_sdu_get(dsp->ds_mh, &dlp->dl_min_sdu, &dlp->dl_max_sdu);
243269Sericheng 	}
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	/*
2460Sstevel@tonic-gate 	 * Set the media type (properly this time).
2470Sstevel@tonic-gate 	 */
2483147Sxc151355 	if (dsp->ds_native)
2493147Sxc151355 		dlp->dl_mac_type = minfop->mi_nativemedia;
2503147Sxc151355 	else
2513147Sxc151355 		dlp->dl_mac_type = minfop->mi_media;
2520Sstevel@tonic-gate 
2530Sstevel@tonic-gate 	/*
2540Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2550Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2560Sstevel@tonic-gate 	 */
2570Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2580Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2590Sstevel@tonic-gate 
260269Sericheng 	addr_length = minfop->mi_addr_length;
2610Sstevel@tonic-gate 
2620Sstevel@tonic-gate 	/*
2630Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2640Sstevel@tonic-gate 	 */
2652311Sseb 	if (minfop->mi_brdcst_addr != NULL) {
2662311Sseb 		dlp->dl_brdcst_addr_offset =
2672311Sseb 		    (uintptr_t)brdcst_addr - (uintptr_t)dlp;
2682311Sseb 		bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
2692311Sseb 		dlp->dl_brdcst_addr_length = addr_length;
2702311Sseb 	}
2710Sstevel@tonic-gate 
2722760Sdg199075 	dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
2732760Sdg199075 	dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
2740Sstevel@tonic-gate 
2752760Sdg199075 	rangep->dl_qos_type = DL_QOS_CL_RANGE1;
2762760Sdg199075 	rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
2772760Sdg199075 	rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
2782760Sdg199075 	rangep->dl_protection.dl_min = DL_UNKNOWN;
2792760Sdg199075 	rangep->dl_protection.dl_max = DL_UNKNOWN;
2802760Sdg199075 	rangep->dl_residual_error = DL_UNKNOWN;
2810Sstevel@tonic-gate 
2822760Sdg199075 	/*
2832760Sdg199075 	 * Specify the supported range of priorities.
2842760Sdg199075 	 */
2852760Sdg199075 	rangep->dl_priority.dl_min = 0;
2862760Sdg199075 	rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
2870Sstevel@tonic-gate 
2882760Sdg199075 	dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
2892760Sdg199075 	dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
2900Sstevel@tonic-gate 
2912760Sdg199075 	selp->dl_qos_type = DL_QOS_CL_SEL1;
2922760Sdg199075 	selp->dl_trans_delay = DL_UNKNOWN;
2932760Sdg199075 	selp->dl_protection = DL_UNKNOWN;
2942760Sdg199075 	selp->dl_residual_error = DL_UNKNOWN;
2952760Sdg199075 
2962760Sdg199075 	/*
2972760Sdg199075 	 * Specify the current priority (which can be changed by
2982760Sdg199075 	 * the DL_UDQOS_REQ primitive).
2992760Sdg199075 	 */
3002760Sdg199075 	selp->dl_priority = dsp->ds_pri;
3010Sstevel@tonic-gate 
3020Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
3030Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
3040Sstevel@tonic-gate 		/*
3050Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
3060Sstevel@tonic-gate 		 * DLSAP address.
3070Sstevel@tonic-gate 		 */
3080Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
3092311Sseb 		if (addr_length > 0)
3102311Sseb 			bcopy(dsp->ds_curr_addr, addr, addr_length);
3110Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3120Sstevel@tonic-gate 	}
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate done:
3150Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
3160Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
3170Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3180Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3190Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3200Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3210Sstevel@tonic-gate 
322269Sericheng 	rw_exit(&dsp->ds_lock);
323269Sericheng 
324269Sericheng 	qreply(q, mp);
325269Sericheng 	return (B_TRUE);
326269Sericheng }
327269Sericheng 
328269Sericheng /*
329269Sericheng  * DL_ATTACH_REQ
330269Sericheng  */
331269Sericheng static boolean_t
332269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
333269Sericheng {
334269Sericheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)udlp;
335269Sericheng 	int		err = 0;
336269Sericheng 	t_uscalar_t	dl_err;
337269Sericheng 	queue_t		*q = dsp->ds_wq;
338269Sericheng 
339269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
340269Sericheng 
341269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
342269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
343269Sericheng 		dl_err = DL_BADPRIM;
344269Sericheng 		goto failed;
345269Sericheng 	}
346269Sericheng 
347269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
348269Sericheng 		dl_err = DL_OUTSTATE;
349269Sericheng 		goto failed;
350269Sericheng 	}
351269Sericheng 
352269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
353269Sericheng 
354269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
355269Sericheng 	if (err != 0) {
356269Sericheng 		switch (err) {
357269Sericheng 		case ENOENT:
358269Sericheng 			dl_err = DL_BADPPA;
359269Sericheng 			err = 0;
360269Sericheng 			break;
361269Sericheng 		default:
362269Sericheng 			dl_err = DL_SYSERR;
363269Sericheng 			break;
364269Sericheng 		}
365269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
366269Sericheng 		goto failed;
367269Sericheng 	}
368269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
369269Sericheng 	rw_exit(&dsp->ds_lock);
370269Sericheng 
371269Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
372269Sericheng 	return (B_TRUE);
373269Sericheng failed:
374269Sericheng 	rw_exit(&dsp->ds_lock);
375269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
376269Sericheng 	return (B_FALSE);
3770Sstevel@tonic-gate }
3780Sstevel@tonic-gate 
379269Sericheng /*ARGSUSED*/
380269Sericheng static boolean_t
381269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
3820Sstevel@tonic-gate {
383269Sericheng 	queue_t		*q = dsp->ds_wq;
384269Sericheng 	t_uscalar_t	dl_err;
3850Sstevel@tonic-gate 
386269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
3870Sstevel@tonic-gate 
388269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
389269Sericheng 		dl_err = DL_BADPRIM;
390269Sericheng 		goto failed;
391269Sericheng 	}
3920Sstevel@tonic-gate 
393269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
394269Sericheng 		dl_err = DL_OUTSTATE;
395269Sericheng 		goto failed;
3960Sstevel@tonic-gate 	}
3970Sstevel@tonic-gate 
398269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
399269Sericheng 		dl_err = DL_BADPRIM;
400269Sericheng 		goto failed;
401269Sericheng 	}
402269Sericheng 
403269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
4045895Syz147064 	dld_str_detach(dsp);
405269Sericheng 
406269Sericheng 	rw_exit(&dsp->ds_lock);
4075895Syz147064 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
408269Sericheng 	return (B_TRUE);
409269Sericheng failed:
410269Sericheng 	rw_exit(&dsp->ds_lock);
411269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
412269Sericheng 	return (B_FALSE);
4130Sstevel@tonic-gate }
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate /*
416269Sericheng  * DL_BIND_REQ
4170Sstevel@tonic-gate  */
418269Sericheng static boolean_t
419269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4200Sstevel@tonic-gate {
421269Sericheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)udlp;
422269Sericheng 	int		err = 0;
4233037Syz147064 	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
4243037Syz147064 	uint_t		dlsap_addr_length;
425269Sericheng 	t_uscalar_t	dl_err;
426269Sericheng 	t_scalar_t	sap;
427269Sericheng 	queue_t		*q = dsp->ds_wq;
428269Sericheng 
4295895Syz147064 	/*
4305895Syz147064 	 * Because control message processing is serialized, we don't need
4315895Syz147064 	 * to hold any locks to read any fields of dsp; we only need ds_lock
4325895Syz147064 	 * to update the ds_dlstate, ds_sap and ds_passivestate fields.
4335895Syz147064 	 */
434269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
435269Sericheng 		dl_err = DL_BADPRIM;
436269Sericheng 		goto failed;
437269Sericheng 	}
438269Sericheng 
439269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
440269Sericheng 		dl_err = DL_NOAUTO;
441269Sericheng 		goto failed;
442269Sericheng 	}
443269Sericheng 
444269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
445269Sericheng 		dl_err = DL_UNSUPPORTED;
446269Sericheng 		goto failed;
447269Sericheng 	}
448269Sericheng 
449269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
450269Sericheng 		dl_err = DL_OUTSTATE;
451269Sericheng 		goto failed;
452269Sericheng 	}
4530Sstevel@tonic-gate 
454269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
455269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
456269Sericheng 		dl_err = DL_SYSERR;
457269Sericheng 		err = EBUSY;
458269Sericheng 		goto failed;
459269Sericheng 	}
460269Sericheng 
461269Sericheng 	/*
462269Sericheng 	 * Set the receive callback.
463269Sericheng 	 */
464269Sericheng 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ?
465269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
4660Sstevel@tonic-gate 
467269Sericheng 	/*
468269Sericheng 	 * Bind the channel such that it can receive packets.
469269Sericheng 	 */
4705895Syz147064 	sap = dlp->dl_sap;
4715895Syz147064 	err = dls_bind(dsp->ds_dc, sap);
472269Sericheng 	if (err != 0) {
473269Sericheng 		switch (err) {
474269Sericheng 		case EINVAL:
475269Sericheng 			dl_err = DL_BADADDR;
476269Sericheng 			err = 0;
477269Sericheng 			break;
478269Sericheng 		default:
479269Sericheng 			dl_err = DL_SYSERR;
480269Sericheng 			break;
481269Sericheng 		}
4825895Syz147064 
483269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
484269Sericheng 			dls_active_clear(dsp->ds_dc);
485269Sericheng 
4860Sstevel@tonic-gate 		goto failed;
487269Sericheng 	}
4880Sstevel@tonic-gate 
4890Sstevel@tonic-gate 	/*
4900Sstevel@tonic-gate 	 * Copy in MAC address.
4910Sstevel@tonic-gate 	 */
4923037Syz147064 	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
4933037Syz147064 	bcopy(dsp->ds_curr_addr, dlsap_addr, dlsap_addr_length);
4940Sstevel@tonic-gate 
4950Sstevel@tonic-gate 	/*
4963037Syz147064 	 * Copy in the SAP.
4970Sstevel@tonic-gate 	 */
4985895Syz147064 	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap;
4993037Syz147064 	dlsap_addr_length += sizeof (uint16_t);
5000Sstevel@tonic-gate 
5015895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
5025895Syz147064 
5030Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
504269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
505269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
5065895Syz147064 	dsp->ds_sap = sap;
5075895Syz147064 
5085895Syz147064 	if (dsp->ds_mode == DLD_FASTPATH)
5095895Syz147064 		dsp->ds_tx = str_mdata_fastpath_put;
5105895Syz147064 	else if (dsp->ds_mode == DLD_RAW)
5115895Syz147064 		dsp->ds_tx = str_mdata_raw_put;
5125895Syz147064 	dsp->ds_unitdata_tx = dld_wput_proto_data;
5130Sstevel@tonic-gate 
514269Sericheng 	rw_exit(&dsp->ds_lock);
515269Sericheng 
5163037Syz147064 	dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0);
517269Sericheng 	return (B_TRUE);
5180Sstevel@tonic-gate failed:
519269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
520269Sericheng 	return (B_FALSE);
5210Sstevel@tonic-gate }
5220Sstevel@tonic-gate 
5230Sstevel@tonic-gate /*
524269Sericheng  * DL_UNBIND_REQ
5250Sstevel@tonic-gate  */
526269Sericheng /*ARGSUSED*/
5275895Syz147064 static boolean_t
5285895Syz147064 proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
5290Sstevel@tonic-gate {
5305895Syz147064 	queue_t		*q = dsp->ds_wq;
5315895Syz147064 	t_uscalar_t	dl_err;
532269Sericheng 
5335895Syz147064 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
5345895Syz147064 		dl_err = DL_BADPRIM;
5355895Syz147064 		goto failed;
5365895Syz147064 	}
5375895Syz147064 
5385895Syz147064 	if (dsp->ds_dlstate != DL_IDLE) {
5395895Syz147064 		dl_err = DL_OUTSTATE;
5405895Syz147064 		goto failed;
5415895Syz147064 	}
542269Sericheng 
543269Sericheng 	/*
544269Sericheng 	 * Flush any remaining packets scheduled for transmission.
545269Sericheng 	 */
546269Sericheng 	dld_tx_flush(dsp);
547269Sericheng 
548269Sericheng 	/*
549269Sericheng 	 * Unbind the channel to stop packets being received.
550269Sericheng 	 */
551269Sericheng 	dls_unbind(dsp->ds_dc);
552269Sericheng 
553269Sericheng 	/*
5545895Syz147064 	 * Clear the receive callback.
5555895Syz147064 	 */
5565895Syz147064 	dls_rx_set(dsp->ds_dc, NULL, NULL);
5575895Syz147064 
5585895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
5595895Syz147064 
5605895Syz147064 	/*
561269Sericheng 	 * Disable polling mode, if it is enabled.
562269Sericheng 	 */
563269Sericheng 	proto_poll_disable(dsp);
564269Sericheng 
565269Sericheng 	/*
5665895Syz147064 	 * If soft rings were enabled, the workers should be quiesced.
5675895Syz147064 	 */
5685895Syz147064 	dls_soft_ring_disable(dsp->ds_dc);
5695895Syz147064 
5705895Syz147064 	/*
5713115Syl150051 	 * Clear LSO flags.
5723115Syl150051 	 */
5733115Syl150051 	dsp->ds_lso = B_FALSE;
5743115Syl150051 	dsp->ds_lso_max = 0;
5753115Syl150051 
5763115Syl150051 	/*
577269Sericheng 	 * Set the mode back to the default (unitdata).
578269Sericheng 	 */
579269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
5801353Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
5815895Syz147064 	DLD_TX_QUIESCE(dsp);
5825895Syz147064 	rw_exit(&dsp->ds_lock);
5831353Sericheng 
5845895Syz147064 	dlokack(q, mp, DL_UNBIND_REQ);
585269Sericheng 
586269Sericheng 	return (B_TRUE);
587269Sericheng failed:
588269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
589269Sericheng 	return (B_FALSE);
5900Sstevel@tonic-gate }
5910Sstevel@tonic-gate 
5920Sstevel@tonic-gate /*
593269Sericheng  * DL_PROMISCON_REQ
5940Sstevel@tonic-gate  */
595269Sericheng static boolean_t
596269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
5970Sstevel@tonic-gate {
598269Sericheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp;
599269Sericheng 	int		err = 0;
600269Sericheng 	t_uscalar_t	dl_err;
6015895Syz147064 	uint32_t	promisc;
602269Sericheng 	queue_t		*q = dsp->ds_wq;
603269Sericheng 
6045895Syz147064 	/*
6055895Syz147064 	 * Because control message processing is serialized, we don't need
6065895Syz147064 	 * to hold any locks to read any fields of dsp; we only need ds_lock
6075895Syz147064 	 * to update the ds_promisc and ds_passivestate fields.
6085895Syz147064 	 */
609269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
610269Sericheng 		dl_err = DL_BADPRIM;
611269Sericheng 		goto failed;
612269Sericheng 	}
613269Sericheng 
614269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
615269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
616269Sericheng 		dl_err = DL_OUTSTATE;
6170Sstevel@tonic-gate 		goto failed;
618269Sericheng 	}
6190Sstevel@tonic-gate 
620269Sericheng 	switch (dlp->dl_level) {
621269Sericheng 	case DL_PROMISC_SAP:
6225895Syz147064 		promisc = DLS_PROMISC_SAP;
623269Sericheng 		break;
6245895Syz147064 	case DL_PROMISC_MULTI:
6255895Syz147064 		promisc = DLS_PROMISC_MULTI;
6265895Syz147064 		break;
627269Sericheng 	case DL_PROMISC_PHYS:
6285895Syz147064 		promisc = DLS_PROMISC_PHYS;
629269Sericheng 		break;
630269Sericheng 	default:
631269Sericheng 		dl_err = DL_NOTSUPPORTED;
632269Sericheng 		goto failed;
633269Sericheng 	}
6340Sstevel@tonic-gate 
635269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
636269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
637269Sericheng 		dl_err = DL_SYSERR;
638269Sericheng 		err = EBUSY;
639269Sericheng 		goto failed;
640269Sericheng 	}
641269Sericheng 
642269Sericheng 	/*
643269Sericheng 	 * Adjust channel promiscuity.
644269Sericheng 	 */
6455895Syz147064 	promisc = (dsp->ds_promisc | promisc);
6465895Syz147064 	err = dls_promisc(dsp->ds_dc, promisc);
647269Sericheng 	if (err != 0) {
648269Sericheng 		dl_err = DL_SYSERR;
649269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
650269Sericheng 			dls_active_clear(dsp->ds_dc);
651269Sericheng 		goto failed;
652269Sericheng 	}
653269Sericheng 
6545895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
655269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
656269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
6575895Syz147064 	dsp->ds_promisc = promisc;
6585895Syz147064 	rw_exit(&dsp->ds_lock);
659269Sericheng 
660269Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
661269Sericheng 	return (B_TRUE);
6620Sstevel@tonic-gate failed:
663269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
664269Sericheng 	return (B_FALSE);
6650Sstevel@tonic-gate }
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate /*
668269Sericheng  * DL_PROMISCOFF_REQ
6690Sstevel@tonic-gate  */
670269Sericheng static boolean_t
671269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6720Sstevel@tonic-gate {
673269Sericheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp;
674269Sericheng 	int		err = 0;
675269Sericheng 	t_uscalar_t	dl_err;
6765895Syz147064 	uint32_t	promisc;
677269Sericheng 	queue_t		*q = dsp->ds_wq;
678269Sericheng 
6795895Syz147064 	/*
6805895Syz147064 	 * Because control messages processing is serialized, we don't need
6815895Syz147064 	 * to hold any lock to read any field of dsp; we hold ds_lock to
6825895Syz147064 	 * update the ds_promisc field.
6835895Syz147064 	 */
684269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
685269Sericheng 		dl_err = DL_BADPRIM;
6860Sstevel@tonic-gate 		goto failed;
687269Sericheng 	}
6880Sstevel@tonic-gate 
689269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
690269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
691269Sericheng 		dl_err = DL_OUTSTATE;
6920Sstevel@tonic-gate 		goto failed;
693269Sericheng 	}
6940Sstevel@tonic-gate 
695269Sericheng 	switch (dlp->dl_level) {
696269Sericheng 	case DL_PROMISC_SAP:
6975895Syz147064 		promisc = DLS_PROMISC_SAP;
6980Sstevel@tonic-gate 		break;
699269Sericheng 	case DL_PROMISC_MULTI:
7005895Syz147064 		promisc = DLS_PROMISC_MULTI;
701269Sericheng 		break;
702269Sericheng 	case DL_PROMISC_PHYS:
7035895Syz147064 		promisc = DLS_PROMISC_PHYS;
7040Sstevel@tonic-gate 		break;
7050Sstevel@tonic-gate 	default:
706269Sericheng 		dl_err = DL_NOTSUPPORTED;
707269Sericheng 		goto failed;
708269Sericheng 	}
709269Sericheng 
7105895Syz147064 	if (!(dsp->ds_promisc & promisc)) {
7115895Syz147064 		dl_err = DL_NOTENAB;
7125895Syz147064 		goto failed;
7135895Syz147064 	}
7145895Syz147064 
7155895Syz147064 	promisc = (dsp->ds_promisc & ~promisc);
7165895Syz147064 	err = dls_promisc(dsp->ds_dc, promisc);
717269Sericheng 	if (err != 0) {
7180Sstevel@tonic-gate 		dl_err = DL_SYSERR;
719269Sericheng 		goto failed;
720269Sericheng 	}
721269Sericheng 
7225895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
7235895Syz147064 	dsp->ds_promisc = promisc;
724269Sericheng 	rw_exit(&dsp->ds_lock);
7255895Syz147064 
726269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
727269Sericheng 	return (B_TRUE);
728269Sericheng failed:
729269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
730269Sericheng 	return (B_FALSE);
731269Sericheng }
732269Sericheng 
733269Sericheng /*
734269Sericheng  * DL_ENABMULTI_REQ
735269Sericheng  */
736269Sericheng static boolean_t
737269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
738269Sericheng {
739269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
740269Sericheng 	int		err = 0;
741269Sericheng 	t_uscalar_t	dl_err;
742269Sericheng 	queue_t		*q = dsp->ds_wq;
743269Sericheng 
7445895Syz147064 	/*
7455895Syz147064 	 * Because control messages processing is serialized, we don't need
7465895Syz147064 	 * to hold any lock to read any field of dsp; we hold ds_lock to
7475895Syz147064 	 * update the ds_passivestate field.
7485895Syz147064 	 */
749269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
750269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
751269Sericheng 		dl_err = DL_OUTSTATE;
752269Sericheng 		goto failed;
753269Sericheng 	}
754269Sericheng 
755269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
756269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
757269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
758269Sericheng 		dl_err = DL_BADPRIM;
759269Sericheng 		goto failed;
760269Sericheng 	}
761269Sericheng 
762269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
763269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
764269Sericheng 		dl_err = DL_SYSERR;
765269Sericheng 		err = EBUSY;
766269Sericheng 		goto failed;
7670Sstevel@tonic-gate 	}
7680Sstevel@tonic-gate 
769269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
770269Sericheng 	if (err != 0) {
771269Sericheng 		switch (err) {
772269Sericheng 		case EINVAL:
773269Sericheng 			dl_err = DL_BADADDR;
774269Sericheng 			err = 0;
775269Sericheng 			break;
776269Sericheng 		case ENOSPC:
777269Sericheng 			dl_err = DL_TOOMANY;
778269Sericheng 			err = 0;
779269Sericheng 			break;
780269Sericheng 		default:
781269Sericheng 			dl_err = DL_SYSERR;
782269Sericheng 			break;
783269Sericheng 		}
7845895Syz147064 
785269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
786269Sericheng 			dls_active_clear(dsp->ds_dc);
787269Sericheng 
788269Sericheng 		goto failed;
789269Sericheng 	}
790269Sericheng 
7915895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
792269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
793269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
7945895Syz147064 	rw_exit(&dsp->ds_lock);
795269Sericheng 
796269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
797269Sericheng 	return (B_TRUE);
798269Sericheng failed:
799269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
800269Sericheng 	return (B_FALSE);
801269Sericheng }
802269Sericheng 
803269Sericheng /*
804269Sericheng  * DL_DISABMULTI_REQ
805269Sericheng  */
806269Sericheng static boolean_t
807269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
808269Sericheng {
809269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
810269Sericheng 	int		err = 0;
811269Sericheng 	t_uscalar_t	dl_err;
812269Sericheng 	queue_t		*q = dsp->ds_wq;
813269Sericheng 
8145895Syz147064 	/*
8155895Syz147064 	 * Because control messages processing is serialized, we don't need
8165895Syz147064 	 * to hold any lock to read any field of dsp.
8175895Syz147064 	 */
818269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
819269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
820269Sericheng 		dl_err = DL_OUTSTATE;
821269Sericheng 		goto failed;
822269Sericheng 	}
823269Sericheng 
824269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
825269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
826269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
827269Sericheng 		dl_err = DL_BADPRIM;
828269Sericheng 		goto failed;
829269Sericheng 	}
830269Sericheng 
831269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
832269Sericheng 	if (err != 0) {
8335895Syz147064 		switch (err) {
834269Sericheng 		case EINVAL:
835269Sericheng 			dl_err = DL_BADADDR;
836269Sericheng 			err = 0;
837269Sericheng 			break;
838269Sericheng 		case ENOENT:
839269Sericheng 			dl_err = DL_NOTENAB;
840269Sericheng 			err = 0;
841269Sericheng 			break;
842269Sericheng 		default:
843269Sericheng 			dl_err = DL_SYSERR;
844269Sericheng 			break;
845269Sericheng 		}
846269Sericheng 		goto failed;
847269Sericheng 	}
848269Sericheng 
849269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
850269Sericheng 	return (B_TRUE);
851269Sericheng failed:
852269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
853269Sericheng 	return (B_FALSE);
8540Sstevel@tonic-gate }
8550Sstevel@tonic-gate 
8560Sstevel@tonic-gate /*
857269Sericheng  * DL_PHYS_ADDR_REQ
8580Sstevel@tonic-gate  */
859269Sericheng static boolean_t
860269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
8610Sstevel@tonic-gate {
862269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
863269Sericheng 	queue_t		*q = dsp->ds_wq;
864269Sericheng 	t_uscalar_t	dl_err;
865269Sericheng 	char		*addr;
866269Sericheng 	uint_t		addr_length;
867269Sericheng 
868269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
869269Sericheng 
870269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
871269Sericheng 		dl_err = DL_BADPRIM;
872269Sericheng 		goto failed;
873269Sericheng 	}
874269Sericheng 
875269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
876269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
877269Sericheng 		dl_err = DL_OUTSTATE;
878269Sericheng 		goto failed;
879269Sericheng 	}
8800Sstevel@tonic-gate 
881269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
882269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
883269Sericheng 		dl_err = DL_UNSUPPORTED;
8840Sstevel@tonic-gate 		goto failed;
885269Sericheng 	}
8860Sstevel@tonic-gate 
887269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
888269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
889269Sericheng 	if (addr == NULL) {
890269Sericheng 		rw_exit(&dsp->ds_lock);
891269Sericheng 		merror(q, mp, ENOSR);
892269Sericheng 		return (B_FALSE);
893269Sericheng 	}
8940Sstevel@tonic-gate 
895269Sericheng 	/*
896269Sericheng 	 * Copy out the address before we drop the lock; we don't
897269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
898269Sericheng 	 */
899269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
900269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
901269Sericheng 
902269Sericheng 	rw_exit(&dsp->ds_lock);
903269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
904269Sericheng 	kmem_free(addr, addr_length);
905269Sericheng 	return (B_TRUE);
9060Sstevel@tonic-gate failed:
907269Sericheng 	rw_exit(&dsp->ds_lock);
908269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
909269Sericheng 	return (B_FALSE);
910269Sericheng }
9110Sstevel@tonic-gate 
912269Sericheng /*
913269Sericheng  * DL_SET_PHYS_ADDR_REQ
914269Sericheng  */
915269Sericheng static boolean_t
916269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
917269Sericheng {
918269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
919269Sericheng 	int		err = 0;
920269Sericheng 	t_uscalar_t	dl_err;
921269Sericheng 	queue_t		*q = dsp->ds_wq;
9220Sstevel@tonic-gate 
9235895Syz147064 	/*
9245895Syz147064 	 * Because control message processing is serialized, we don't need
9255895Syz147064 	 * to hold any locks to read any fields of dsp; we only need ds_lock
9265895Syz147064 	 * to update the ds_passivestate field.
9275895Syz147064 	 */
928269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
929269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
930269Sericheng 		dl_err = DL_OUTSTATE;
931269Sericheng 		goto failed;
932269Sericheng 	}
933269Sericheng 
934269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
935269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
936269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
937269Sericheng 		dl_err = DL_BADPRIM;
938269Sericheng 		goto failed;
9390Sstevel@tonic-gate 	}
9400Sstevel@tonic-gate 
941269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
942269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
943269Sericheng 		dl_err = DL_SYSERR;
944269Sericheng 		err = EBUSY;
945269Sericheng 		goto failed;
946269Sericheng 	}
947269Sericheng 
948269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
949269Sericheng 	if (err != 0) {
950269Sericheng 		switch (err) {
951269Sericheng 		case EINVAL:
952269Sericheng 			dl_err = DL_BADADDR;
953269Sericheng 			err = 0;
954269Sericheng 			break;
955269Sericheng 
956269Sericheng 		default:
957269Sericheng 			dl_err = DL_SYSERR;
958269Sericheng 			break;
959269Sericheng 		}
9605895Syz147064 
961269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
962269Sericheng 			dls_active_clear(dsp->ds_dc);
963269Sericheng 
964269Sericheng 		goto failed;
965269Sericheng 	}
9665895Syz147064 
9675895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
968269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
969269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
9705895Syz147064 	rw_exit(&dsp->ds_lock);
971269Sericheng 
972269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
973269Sericheng 	return (B_TRUE);
974269Sericheng failed:
975269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
976269Sericheng 	return (B_FALSE);
977269Sericheng }
978269Sericheng 
979269Sericheng /*
980269Sericheng  * DL_UDQOS_REQ
981269Sericheng  */
982269Sericheng static boolean_t
983269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
984269Sericheng {
985269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
986269Sericheng 	dl_qos_cl_sel1_t *selp;
987269Sericheng 	int		off, len;
988269Sericheng 	t_uscalar_t	dl_err;
989269Sericheng 	queue_t		*q = dsp->ds_wq;
990269Sericheng 
991269Sericheng 	off = dlp->dl_qos_offset;
992269Sericheng 	len = dlp->dl_qos_length;
993269Sericheng 
994269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
995269Sericheng 		dl_err = DL_BADPRIM;
996269Sericheng 		goto failed;
997269Sericheng 	}
998269Sericheng 
999269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1000269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1001269Sericheng 		dl_err = DL_BADQOSTYPE;
1002269Sericheng 		goto failed;
1003269Sericheng 	}
1004269Sericheng 
10052760Sdg199075 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1006269Sericheng 	    selp->dl_priority < 0) {
1007269Sericheng 		dl_err = DL_BADQOSPARAM;
1008269Sericheng 		goto failed;
1009269Sericheng 	}
1010269Sericheng 
10115895Syz147064 	if (dsp->ds_dlstate == DL_UNATTACHED ||
10125895Syz147064 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
10135895Syz147064 		dl_err = DL_OUTSTATE;
10145895Syz147064 		goto failed;
10155895Syz147064 	}
1016269Sericheng 
10175895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
10185895Syz147064 	dsp->ds_pri = selp->dl_priority;
1019269Sericheng 	rw_exit(&dsp->ds_lock);
10205895Syz147064 
1021269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1022269Sericheng 	return (B_TRUE);
1023269Sericheng failed:
1024269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1025269Sericheng 	return (B_FALSE);
10260Sstevel@tonic-gate }
10270Sstevel@tonic-gate 
10281184Skrgopi static boolean_t
10291184Skrgopi check_ip_above(queue_t *q)
10301184Skrgopi {
10311184Skrgopi 	queue_t		*next_q;
10321184Skrgopi 	boolean_t	ret = B_TRUE;
10331184Skrgopi 
10341184Skrgopi 	claimstr(q);
10351184Skrgopi 	next_q = q->q_next;
10361184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
10371184Skrgopi 		ret = B_FALSE;
10381184Skrgopi 	releasestr(q);
10391184Skrgopi 	return (ret);
10401184Skrgopi }
10411184Skrgopi 
10420Sstevel@tonic-gate /*
1043269Sericheng  * DL_CAPABILITY_REQ
10440Sstevel@tonic-gate  */
1045269Sericheng /*ARGSUSED*/
1046269Sericheng static boolean_t
1047269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
10480Sstevel@tonic-gate {
1049269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1050269Sericheng 	dl_capability_sub_t *sp;
1051269Sericheng 	size_t		size, len;
1052269Sericheng 	offset_t	off, end;
1053269Sericheng 	t_uscalar_t	dl_err;
1054269Sericheng 	queue_t		*q = dsp->ds_wq;
1055269Sericheng 
10565895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
1057269Sericheng 
1058269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1059269Sericheng 		dl_err = DL_BADPRIM;
1060269Sericheng 		goto failed;
1061269Sericheng 	}
1062269Sericheng 
1063269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1064269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1065269Sericheng 		dl_err = DL_OUTSTATE;
1066269Sericheng 		goto failed;
1067269Sericheng 	}
1068269Sericheng 
1069269Sericheng 	/*
1070269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1071269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1072269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1073269Sericheng 	 */
1074269Sericheng 	if (dlp->dl_sub_length == 0) {
1075269Sericheng 		/* callee drops lock */
1076269Sericheng 		return (proto_capability_advertise(dsp, mp));
1077269Sericheng 	}
1078269Sericheng 
1079269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1080269Sericheng 		dl_err = DL_BADPRIM;
1081269Sericheng 		goto failed;
1082269Sericheng 	}
1083269Sericheng 
1084269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1085269Sericheng 
1086269Sericheng 	off = dlp->dl_sub_offset;
1087269Sericheng 	len = dlp->dl_sub_length;
10880Sstevel@tonic-gate 
10890Sstevel@tonic-gate 	/*
1090269Sericheng 	 * Walk the list of capabilities to be enabled.
10910Sstevel@tonic-gate 	 */
1092269Sericheng 	for (end = off + len; off < end; ) {
1093269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1094269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1095269Sericheng 
1096269Sericheng 		if (off + size > end ||
1097269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1098269Sericheng 			dl_err = DL_BADPRIM;
1099269Sericheng 			goto failed;
1100269Sericheng 		}
1101269Sericheng 
1102269Sericheng 		switch (sp->dl_cap) {
1103269Sericheng 		/*
1104269Sericheng 		 * TCP/IP checksum offload to hardware.
1105269Sericheng 		 */
1106269Sericheng 		case DL_CAPAB_HCKSUM: {
1107269Sericheng 			dl_capab_hcksum_t *hcksump;
1108269Sericheng 			dl_capab_hcksum_t hcksum;
1109269Sericheng 
1110269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1111269Sericheng 			/*
1112269Sericheng 			 * Copy for alignment.
1113269Sericheng 			 */
1114269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1115269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1116269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1117269Sericheng 			break;
1118269Sericheng 		}
1119269Sericheng 
1120269Sericheng 		/*
11213115Syl150051 		 * Large segment offload. (LSO)
11223115Syl150051 		 */
11233115Syl150051 		case DL_CAPAB_LSO: {
11243115Syl150051 			dl_capab_lso_t *lsop;
11253115Syl150051 			dl_capab_lso_t lso;
11263115Syl150051 
11273115Syl150051 			lsop = (dl_capab_lso_t *)&sp[1];
11283115Syl150051 			/*
11293115Syl150051 			 * Copy for alignment.
11303115Syl150051 			 */
11313115Syl150051 			bcopy(lsop, &lso, sizeof (dl_capab_lso_t));
11323115Syl150051 			dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
11333115Syl150051 			bcopy(&lso, lsop, sizeof (dl_capab_lso_t));
11343115Syl150051 			break;
11353115Syl150051 		}
11363115Syl150051 
11373115Syl150051 		/*
1138269Sericheng 		 * IP polling interface.
1139269Sericheng 		 */
1140269Sericheng 		case DL_CAPAB_POLL: {
11411184Skrgopi 			dl_capab_dls_t *pollp;
11421184Skrgopi 			dl_capab_dls_t	poll;
1143269Sericheng 
11441184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1145269Sericheng 			/*
1146269Sericheng 			 * Copy for alignment.
1147269Sericheng 			 */
11481184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1149269Sericheng 
11501184Skrgopi 			switch (poll.dls_flags) {
1151269Sericheng 			default:
1152269Sericheng 				/*FALLTHRU*/
1153269Sericheng 			case POLL_DISABLE:
1154269Sericheng 				proto_poll_disable(dsp);
1155269Sericheng 				break;
1156269Sericheng 
1157269Sericheng 			case POLL_ENABLE:
1158269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1159269Sericheng 
1160269Sericheng 				/*
1161269Sericheng 				 * Make sure polling is disabled.
1162269Sericheng 				 */
1163269Sericheng 				proto_poll_disable(dsp);
1164269Sericheng 
1165269Sericheng 				/*
11665895Syz147064 				 * Note that only IP should enable POLL.
1167269Sericheng 				 */
11681184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
11691184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
11701184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
11711184Skrgopi 					poll.dls_flags = POLL_ENABLE;
11725895Syz147064 				} else {
11735895Syz147064 					bzero(&poll, sizeof (dl_capab_dls_t));
11745895Syz147064 					poll.dls_flags = POLL_DISABLE;
11751184Skrgopi 				}
1176269Sericheng 				break;
1177269Sericheng 			}
1178269Sericheng 
11791184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
11801184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
11811184Skrgopi 			break;
11821184Skrgopi 		}
11831184Skrgopi 		case DL_CAPAB_SOFT_RING: {
11841184Skrgopi 			dl_capab_dls_t *soft_ringp;
11851184Skrgopi 			dl_capab_dls_t soft_ring;
11861184Skrgopi 
11871184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
11881184Skrgopi 			/*
11891184Skrgopi 			 * Copy for alignment.
11901184Skrgopi 			 */
11911184Skrgopi 			bcopy(soft_ringp, &soft_ring,
11921184Skrgopi 			    sizeof (dl_capab_dls_t));
11931184Skrgopi 
11941184Skrgopi 			switch (soft_ring.dls_flags) {
11951184Skrgopi 			default:
11961184Skrgopi 				/*FALLTHRU*/
11971184Skrgopi 			case SOFT_RING_DISABLE:
11981184Skrgopi 				proto_soft_ring_disable(dsp);
11991184Skrgopi 				break;
12001184Skrgopi 
12011184Skrgopi 			case SOFT_RING_ENABLE:
12024114Sja97890 				ASSERT(!(dld_opt & DLD_OPT_NO_SOFTRING));
12031184Skrgopi 				/*
12041184Skrgopi 				 * Make sure soft_ring is disabled.
12051184Skrgopi 				 */
12061184Skrgopi 				proto_soft_ring_disable(dsp);
12071184Skrgopi 
12081184Skrgopi 				/*
12095895Syz147064 				 * Note that only IP can enable soft ring.
12101184Skrgopi 				 */
12111184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
12121184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
12131184Skrgopi 					bzero(&soft_ring,
12141184Skrgopi 					    sizeof (dl_capab_dls_t));
12155895Syz147064 					soft_ring.dls_flags = SOFT_RING_ENABLE;
12161184Skrgopi 				} else {
12171184Skrgopi 					bzero(&soft_ring,
12181184Skrgopi 					    sizeof (dl_capab_dls_t));
12195895Syz147064 					soft_ring.dls_flags = SOFT_RING_DISABLE;
12201184Skrgopi 				}
12211184Skrgopi 				break;
12221184Skrgopi 			}
12231184Skrgopi 
12241184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
12251184Skrgopi 			bcopy(&soft_ring, soft_ringp,
12261184Skrgopi 			    sizeof (dl_capab_dls_t));
1227269Sericheng 			break;
1228269Sericheng 		}
1229269Sericheng 		default:
1230269Sericheng 			break;
1231269Sericheng 		}
1232269Sericheng 
1233269Sericheng 		off += size;
1234269Sericheng 	}
1235269Sericheng 	rw_exit(&dsp->ds_lock);
1236269Sericheng 	qreply(q, mp);
1237269Sericheng 	return (B_TRUE);
1238269Sericheng failed:
1239269Sericheng 	rw_exit(&dsp->ds_lock);
1240269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1241269Sericheng 	return (B_FALSE);
12420Sstevel@tonic-gate }
12430Sstevel@tonic-gate 
12440Sstevel@tonic-gate /*
1245269Sericheng  * DL_NOTIFY_REQ
12460Sstevel@tonic-gate  */
1247269Sericheng static boolean_t
1248269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
12490Sstevel@tonic-gate {
1250269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1251269Sericheng 	t_uscalar_t	dl_err;
1252269Sericheng 	queue_t		*q = dsp->ds_wq;
1253269Sericheng 	uint_t		note =
1254269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1255269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1256269Sericheng 	    DL_NOTE_PHYS_ADDR |
1257269Sericheng 	    DL_NOTE_LINK_UP |
1258269Sericheng 	    DL_NOTE_LINK_DOWN |
12592311Sseb 	    DL_NOTE_CAPAB_RENEG |
12602311Sseb 	    DL_NOTE_SPEED;
12610Sstevel@tonic-gate 
12621521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
12631521Syz147064 
1264269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1265269Sericheng 		dl_err = DL_BADPRIM;
1266269Sericheng 		goto failed;
1267269Sericheng 	}
12680Sstevel@tonic-gate 
1269269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1270269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1271269Sericheng 		dl_err = DL_OUTSTATE;
1272269Sericheng 		goto failed;
12730Sstevel@tonic-gate 	}
12740Sstevel@tonic-gate 
12755895Syz147064 	note &= ~(mac_no_notification(dsp->ds_mh));
12765895Syz147064 
1277269Sericheng 	/*
1278269Sericheng 	 * Cache the notifications that are being enabled.
1279269Sericheng 	 */
1280269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1281269Sericheng 	rw_exit(&dsp->ds_lock);
1282269Sericheng 	/*
1283269Sericheng 	 * The ACK carries all notifications regardless of which set is
1284269Sericheng 	 * being enabled.
1285269Sericheng 	 */
1286269Sericheng 	dlnotifyack(q, mp, note);
1287269Sericheng 
1288269Sericheng 	/*
1289269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1290269Sericheng 	 */
1291269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1292269Sericheng 	if (dsp->ds_notifications != 0) {
1293269Sericheng 		rw_exit(&dsp->ds_lock);
1294269Sericheng 		dld_str_notify_ind(dsp);
1295269Sericheng 	} else {
1296269Sericheng 		rw_exit(&dsp->ds_lock);
1297269Sericheng 	}
1298269Sericheng 	return (B_TRUE);
1299269Sericheng failed:
1300269Sericheng 	rw_exit(&dsp->ds_lock);
1301269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1302269Sericheng 	return (B_FALSE);
13030Sstevel@tonic-gate }
13040Sstevel@tonic-gate 
13050Sstevel@tonic-gate /*
13065895Syz147064  * DL_UNITDATA_REQ
13070Sstevel@tonic-gate  */
13085895Syz147064 void
13095895Syz147064 dld_wput_proto_data(dld_str_t *dsp, mblk_t *mp)
13100Sstevel@tonic-gate {
1311269Sericheng 	queue_t			*q = dsp->ds_wq;
13125895Syz147064 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)mp->b_rptr;
1313269Sericheng 	off_t			off;
1314269Sericheng 	size_t			len, size;
1315269Sericheng 	const uint8_t		*addr;
1316269Sericheng 	uint16_t		sap;
1317269Sericheng 	uint_t			addr_length;
13182311Sseb 	mblk_t			*bp, *payload;
1319269Sericheng 	uint32_t		start, stuff, end, value, flags;
1320269Sericheng 	t_uscalar_t		dl_err;
1321*5903Ssowmini 	uint_t			max_sdu;
1322269Sericheng 
1323269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1324269Sericheng 		dl_err = DL_BADPRIM;
1325269Sericheng 		goto failed;
1326269Sericheng 	}
1327269Sericheng 
1328269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1329269Sericheng 
1330269Sericheng 	off = dlp->dl_dest_addr_offset;
1331269Sericheng 	len = dlp->dl_dest_addr_length;
1332269Sericheng 
1333269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1334269Sericheng 		dl_err = DL_BADPRIM;
1335269Sericheng 		goto failed;
1336269Sericheng 	}
1337269Sericheng 
1338269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1339269Sericheng 		dl_err = DL_BADADDR;
1340269Sericheng 		goto failed;
1341269Sericheng 	}
1342269Sericheng 
1343269Sericheng 	addr = mp->b_rptr + off;
1344269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1345269Sericheng 
1346269Sericheng 	/*
1347269Sericheng 	 * Check the length of the packet and the block types.
1348269Sericheng 	 */
1349269Sericheng 	size = 0;
13502311Sseb 	payload = mp->b_cont;
13512311Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1352269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1353269Sericheng 			goto baddata;
1354269Sericheng 
1355269Sericheng 		size += MBLKL(bp);
1356269Sericheng 	}
1357269Sericheng 
1358*5903Ssowmini 	mac_sdu_get(dsp->ds_mh, NULL, &max_sdu);
1359*5903Ssowmini 	if (size > max_sdu)
1360269Sericheng 		goto baddata;
1361269Sericheng 
1362269Sericheng 	/*
1363269Sericheng 	 * Build a packet header.
1364269Sericheng 	 */
13652760Sdg199075 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dlp->dl_priority.dl_max,
13662760Sdg199075 	    &payload)) == NULL) {
1367269Sericheng 		dl_err = DL_BADADDR;
1368269Sericheng 		goto failed;
1369269Sericheng 	}
1370269Sericheng 
1371269Sericheng 	/*
1372269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1373269Sericheng 	 */
1374269Sericheng 	freeb(mp);
1375269Sericheng 
1376269Sericheng 	/*
1377269Sericheng 	 * Transfer the checksum offload information if it is present.
1378269Sericheng 	 */
13792311Sseb 	hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1380269Sericheng 	    &flags);
13812311Sseb 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1382269Sericheng 
1383269Sericheng 	/*
1384269Sericheng 	 * Link the payload onto the new header.
1385269Sericheng 	 */
1386269Sericheng 	ASSERT(bp->b_cont == NULL);
13872311Sseb 	bp->b_cont = payload;
13882760Sdg199075 	dld_tx_single(dsp, bp);
13895895Syz147064 	return;
1390269Sericheng failed:
1391269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
13925895Syz147064 	return;
1393269Sericheng 
1394269Sericheng baddata:
1395269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1396269Sericheng }
1397269Sericheng 
1398269Sericheng /*
1399269Sericheng  * DL_PASSIVE_REQ
1400269Sericheng  */
1401269Sericheng /* ARGSUSED */
1402269Sericheng static boolean_t
1403269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1404269Sericheng {
1405269Sericheng 	t_uscalar_t dl_err;
1406269Sericheng 
14075895Syz147064 	/*
14085895Syz147064 	 * READER lock is enough because ds_passivestate can only be changed
14095895Syz147064 	 * as the result of non-data message processing.
14105895Syz147064 	 */
14115895Syz147064 	rw_enter(&dsp->ds_lock, RW_READER);
14125895Syz147064 
1413269Sericheng 	/*
1414269Sericheng 	 * If we've already become active by issuing an active primitive,
1415269Sericheng 	 * then it's too late to try to become passive.
1416269Sericheng 	 */
1417269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1418269Sericheng 		dl_err = DL_OUTSTATE;
1419269Sericheng 		goto failed;
1420269Sericheng 	}
1421269Sericheng 
1422269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1423269Sericheng 		dl_err = DL_BADPRIM;
1424269Sericheng 		goto failed;
1425269Sericheng 	}
1426269Sericheng 
1427269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1428269Sericheng 	rw_exit(&dsp->ds_lock);
1429269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1430269Sericheng 	return (B_TRUE);
1431269Sericheng failed:
1432269Sericheng 	rw_exit(&dsp->ds_lock);
1433269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1434269Sericheng 	return (B_FALSE);
1435269Sericheng }
1436269Sericheng 
1437269Sericheng /*
1438269Sericheng  * Catch-all handler.
1439269Sericheng  */
1440269Sericheng static boolean_t
1441269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1442269Sericheng {
1443269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1444269Sericheng 	return (B_FALSE);
14450Sstevel@tonic-gate }
14460Sstevel@tonic-gate 
14470Sstevel@tonic-gate static void
14480Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
14490Sstevel@tonic-gate {
14500Sstevel@tonic-gate 	mac_handle_t	mh;
14510Sstevel@tonic-gate 
14525895Syz147064 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
1453269Sericheng 
14540Sstevel@tonic-gate 	if (!dsp->ds_polling)
14550Sstevel@tonic-gate 		return;
14560Sstevel@tonic-gate 
14570Sstevel@tonic-gate 	/*
14580Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
14590Sstevel@tonic-gate 	 */
14600Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 	/*
14630Sstevel@tonic-gate 	 * Reset the resource_add callback.
14640Sstevel@tonic-gate 	 */
14650Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
14660Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
14671184Skrgopi 	mac_resources(mh);
14680Sstevel@tonic-gate 
14690Sstevel@tonic-gate 	/*
14700Sstevel@tonic-gate 	 * Set receive function back to default.
14710Sstevel@tonic-gate 	 */
14720Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
14735895Syz147064 	    dld_str_rx_fastpath : dld_str_rx_unitdata, dsp);
14740Sstevel@tonic-gate 
14750Sstevel@tonic-gate 	/*
14760Sstevel@tonic-gate 	 * Note that polling is disabled.
14770Sstevel@tonic-gate 	 */
14780Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
14790Sstevel@tonic-gate }
14800Sstevel@tonic-gate 
14810Sstevel@tonic-gate static boolean_t
14821184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
14830Sstevel@tonic-gate {
14840Sstevel@tonic-gate 	mac_handle_t	mh;
14850Sstevel@tonic-gate 
1486269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
14870Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
14880Sstevel@tonic-gate 
14890Sstevel@tonic-gate 	/*
14900Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
14910Sstevel@tonic-gate 	 * has been enabled.
14920Sstevel@tonic-gate 	 */
14930Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
14940Sstevel@tonic-gate 		return (B_FALSE);
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
14970Sstevel@tonic-gate 
14980Sstevel@tonic-gate 	/*
14990Sstevel@tonic-gate 	 * Register resources.
15000Sstevel@tonic-gate 	 */
15011184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
15021184Skrgopi 	    (void *)pollp->dls_rx_handle);
15035895Syz147064 
15040Sstevel@tonic-gate 	mac_resources(mh);
15050Sstevel@tonic-gate 
15060Sstevel@tonic-gate 	/*
15075895Syz147064 	 * Set the upstream receive function.
15080Sstevel@tonic-gate 	 */
15091184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
15101184Skrgopi 	    (void *)pollp->dls_rx_handle);
15110Sstevel@tonic-gate 
15120Sstevel@tonic-gate 	/*
15130Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
15140Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
15150Sstevel@tonic-gate 	 */
15160Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
15170Sstevel@tonic-gate 	return (B_TRUE);
15180Sstevel@tonic-gate }
15190Sstevel@tonic-gate 
15201184Skrgopi static void
15211184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
15221184Skrgopi {
15231184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
15241184Skrgopi 
15251184Skrgopi 	if (!dsp->ds_soft_ring)
15261184Skrgopi 		return;
15271184Skrgopi 
15281184Skrgopi 	/*
15291184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
15301184Skrgopi 	 */
15311184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
15321184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
15331184Skrgopi 	/*
15341184Skrgopi 	 * Note that fanout is disabled.
15351184Skrgopi 	 */
15361184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
15371184Skrgopi }
15381184Skrgopi 
15391184Skrgopi static boolean_t
15401184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
15411184Skrgopi {
15421184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
15431184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
15441184Skrgopi 
15451184Skrgopi 	/*
15461184Skrgopi 	 * We cannot enable soft_ring if raw mode
15471184Skrgopi 	 * has been enabled.
15481184Skrgopi 	 */
15491184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
15501184Skrgopi 		return (B_FALSE);
15511184Skrgopi 
15521184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
15531184Skrgopi 		return (B_FALSE);
15541184Skrgopi 
15551184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
15561184Skrgopi 	return (B_TRUE);
15571184Skrgopi }
15581184Skrgopi 
15591184Skrgopi static void
15601184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
15611184Skrgopi {
15625895Syz147064 	dls_channel_t	dc = dsp->ds_dc;
15631184Skrgopi 
15641184Skrgopi 	if (type == SOFT_RING_NONE) {
15655895Syz147064 		dls_rx_set(dc, (dsp->ds_mode == DLD_FASTPATH) ?
15665895Syz147064 		    dld_str_rx_fastpath : dld_str_rx_unitdata, dsp);
15675895Syz147064 	} else if (type != SOFT_RING_NONE) {
15685895Syz147064 		dls_rx_set(dc, (dls_rx_t)dls_soft_ring_fanout, dc);
15691184Skrgopi 	}
15701184Skrgopi }
15711184Skrgopi 
15720Sstevel@tonic-gate /*
15730Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
15740Sstevel@tonic-gate  */
1575269Sericheng static boolean_t
1576269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
15770Sstevel@tonic-gate {
15780Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
15790Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
15800Sstevel@tonic-gate 	size_t			subsize;
15811184Skrgopi 	dl_capab_dls_t		poll;
15823115Syl150051 	dl_capab_dls_t		soft_ring;
15830Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
15843115Syl150051 	dl_capab_lso_t		lso;
15850Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
15860Sstevel@tonic-gate 	uint8_t			*ptr;
1587269Sericheng 	queue_t			*q = dsp->ds_wq;
1588269Sericheng 	mblk_t			*mp1;
15895895Syz147064 	boolean_t		is_vlan = (dsp->ds_vid != VLAN_ID_NONE);
15905895Syz147064 	boolean_t		poll_capable = B_FALSE;
15915895Syz147064 	boolean_t		soft_ring_capable = B_FALSE;
15925895Syz147064 	boolean_t		hcksum_capable = B_FALSE;
15935895Syz147064 	boolean_t		zcopy_capable = B_FALSE;
15945895Syz147064 	boolean_t		lso_capable = B_FALSE;
15955895Syz147064 	mac_capab_lso_t		mac_lso;
1596269Sericheng 
15975895Syz147064 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
15980Sstevel@tonic-gate 
15990Sstevel@tonic-gate 	/*
16000Sstevel@tonic-gate 	 * Initially assume no capabilities.
16010Sstevel@tonic-gate 	 */
16020Sstevel@tonic-gate 	subsize = 0;
16030Sstevel@tonic-gate 
16041555Skrgopi 	/*
16055895Syz147064 	 * Check if soft ring can be enabled on this interface. Note that we
16065895Syz147064 	 * do not enable softring on any legacy drivers, because doing that
16075895Syz147064 	 * would hurt the performance if the legacy driver has its own taskq
16085895Syz147064 	 * implementation. Further, most high-performance legacy drivers do
16095895Syz147064 	 * have their own taskq implementation.
16105895Syz147064 	 *
16115895Syz147064 	 * If advertising DL_CAPAB_SOFT_RING has not been explicitly disabled,
16125895Syz147064 	 * reserve space for that capability.
16131555Skrgopi 	 */
16145895Syz147064 	if (!mac_is_legacy(dsp->ds_mh) && !(dld_opt & DLD_OPT_NO_SOFTRING)) {
16155895Syz147064 		soft_ring_capable = B_TRUE;
16161555Skrgopi 		subsize += sizeof (dl_capability_sub_t) +
16175113Syz147064 		    sizeof (dl_capab_dls_t);
16184114Sja97890 	}
16191184Skrgopi 
16200Sstevel@tonic-gate 	/*
16210Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
16220Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
16230Sstevel@tonic-gate 	 * then reserve space for that capability.
16240Sstevel@tonic-gate 	 */
16255895Syz147064 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) &&
16265895Syz147064 	    !(dld_opt & DLD_OPT_NO_POLL) && !is_vlan) {
16275895Syz147064 		poll_capable = B_TRUE;
16280Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
16291184Skrgopi 		    sizeof (dl_capab_dls_t);
16300Sstevel@tonic-gate 	}
16310Sstevel@tonic-gate 
16320Sstevel@tonic-gate 	/*
16335895Syz147064 	 * Check if checksum offload is supported on this MAC.  Don't
16345895Syz147064 	 * advertise DL_CAPAB_HCKSUM if the underlying MAC is VLAN incapable,
16355895Syz147064 	 * since it might not be able to do the hardware checksum offload
16365895Syz147064 	 * with the correct offset.
16370Sstevel@tonic-gate 	 */
16385895Syz147064 	bzero(&hcksum, sizeof (dl_capab_hcksum_t));
16395895Syz147064 	if ((!is_vlan || (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_NATIVEVLAN,
16405895Syz147064 	    NULL))) && mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
16412311Sseb 	    &hcksum.hcksum_txflags)) {
16425895Syz147064 		if (hcksum.hcksum_txflags != 0) {
16435895Syz147064 			hcksum_capable = B_TRUE;
16445895Syz147064 			subsize += sizeof (dl_capability_sub_t) +
16455895Syz147064 			    sizeof (dl_capab_hcksum_t);
16465895Syz147064 		}
16470Sstevel@tonic-gate 	}
16480Sstevel@tonic-gate 
16490Sstevel@tonic-gate 	/*
16505895Syz147064 	 * Check if LSO is supported on this MAC, then reserve space for
16515895Syz147064 	 * the DL_CAPAB_LSO capability.
16523115Syl150051 	 */
16535895Syz147064 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
16545895Syz147064 		lso_capable = B_TRUE;
16553115Syl150051 		subsize += sizeof (dl_capability_sub_t) +
16563115Syl150051 		    sizeof (dl_capab_lso_t);
16573115Syl150051 	}
16583115Syl150051 
16593115Syl150051 	/*
16605895Syz147064 	 * Check if zerocopy is supported on this interface.
16615895Syz147064 	 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled
16625895Syz147064 	 * then reserve space for that capability.
16630Sstevel@tonic-gate 	 */
16645895Syz147064 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) &&
16655895Syz147064 	    !(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
16665895Syz147064 		zcopy_capable = B_TRUE;
16670Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
16680Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
16690Sstevel@tonic-gate 	}
16700Sstevel@tonic-gate 
16710Sstevel@tonic-gate 	/*
1672269Sericheng 	 * If there are no capabilities to advertise or if we
1673269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
16740Sstevel@tonic-gate 	 */
16751184Skrgopi 	if ((mp1 = reallocb(mp,
1676269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1677269Sericheng 		rw_exit(&dsp->ds_lock);
1678269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1679269Sericheng 		return (B_FALSE);
16800Sstevel@tonic-gate 	}
16810Sstevel@tonic-gate 
1682269Sericheng 	mp = mp1;
1683269Sericheng 	DB_TYPE(mp) = M_PROTO;
1684269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1685269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
16860Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
16870Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
16880Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
16890Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
16900Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
16910Sstevel@tonic-gate 
16920Sstevel@tonic-gate 	/*
16930Sstevel@tonic-gate 	 * IP polling interface.
16940Sstevel@tonic-gate 	 */
16955895Syz147064 	if (poll_capable) {
16960Sstevel@tonic-gate 		/*
1697269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
16985895Syz147064 		 * READER lock is enough because ds_polling can only be
16995895Syz147064 		 * changed as the result of non-data message processing.
1700269Sericheng 		 */
17015895Syz147064 		proto_poll_disable(dsp);
17020Sstevel@tonic-gate 
17035895Syz147064 		dlsp = (dl_capability_sub_t *)ptr;
17040Sstevel@tonic-gate 
17055895Syz147064 		dlsp->dl_cap = DL_CAPAB_POLL;
17065895Syz147064 		dlsp->dl_length = sizeof (dl_capab_dls_t);
17075895Syz147064 		ptr += sizeof (dl_capability_sub_t);
1708269Sericheng 
17095895Syz147064 		bzero(&poll, sizeof (dl_capab_dls_t));
17105895Syz147064 		poll.dls_version = POLL_VERSION_1;
17115895Syz147064 		poll.dls_flags = POLL_CAPABLE;
17125895Syz147064 		poll.dls_tx_handle = (uintptr_t)dsp;
17135895Syz147064 		poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
17145895Syz147064 		dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
17155895Syz147064 		bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
17165895Syz147064 		ptr += sizeof (dl_capab_dls_t);
17170Sstevel@tonic-gate 	}
17180Sstevel@tonic-gate 
1719269Sericheng 
17205895Syz147064 	if (soft_ring_capable) {
17211555Skrgopi 		dlsp = (dl_capability_sub_t *)ptr;
17221184Skrgopi 
17231555Skrgopi 		dlsp->dl_cap = DL_CAPAB_SOFT_RING;
17241555Skrgopi 		dlsp->dl_length = sizeof (dl_capab_dls_t);
17251555Skrgopi 		ptr += sizeof (dl_capability_sub_t);
17261184Skrgopi 
17271555Skrgopi 		bzero(&soft_ring, sizeof (dl_capab_dls_t));
17281555Skrgopi 		soft_ring.dls_version = SOFT_RING_VERSION_1;
17291555Skrgopi 		soft_ring.dls_flags = SOFT_RING_CAPABLE;
17301555Skrgopi 		soft_ring.dls_tx_handle = (uintptr_t)dsp;
17311555Skrgopi 		soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
17321555Skrgopi 		soft_ring.dls_ring_change_status =
17331555Skrgopi 		    (uintptr_t)proto_change_soft_ring_fanout;
17341555Skrgopi 		soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
17351555Skrgopi 		soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
17361184Skrgopi 
17371555Skrgopi 		dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
17381555Skrgopi 		bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
17391555Skrgopi 		ptr += sizeof (dl_capab_dls_t);
17401555Skrgopi 	}
17411184Skrgopi 
17420Sstevel@tonic-gate 	/*
17430Sstevel@tonic-gate 	 * TCP/IP checksum offload.
17440Sstevel@tonic-gate 	 */
17455895Syz147064 	if (hcksum_capable) {
17460Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
17470Sstevel@tonic-gate 
17480Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
17490Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
17500Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
17510Sstevel@tonic-gate 
17520Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
17530Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
17540Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
17550Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
17560Sstevel@tonic-gate 	}
17570Sstevel@tonic-gate 
17580Sstevel@tonic-gate 	/*
17593115Syl150051 	 * Large segment offload. (LSO)
17603115Syl150051 	 */
17615895Syz147064 	if (lso_capable) {
17623115Syl150051 		dlsp = (dl_capability_sub_t *)ptr;
17633115Syl150051 
17643115Syl150051 		dlsp->dl_cap = DL_CAPAB_LSO;
17653115Syl150051 		dlsp->dl_length = sizeof (dl_capab_lso_t);
17663115Syl150051 		ptr += sizeof (dl_capability_sub_t);
17673115Syl150051 
17683115Syl150051 		lso.lso_version = LSO_VERSION_1;
17693115Syl150051 		lso.lso_flags = mac_lso.lso_flags;
17703115Syl150051 		lso.lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max;
17713115Syl150051 
17723115Syl150051 		/* Simply enable LSO with DLD */
17733115Syl150051 		dsp->ds_lso = B_TRUE;
17743115Syl150051 		dsp->ds_lso_max = lso.lso_max;
17753115Syl150051 
17763115Syl150051 		dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
17773115Syl150051 		bcopy(&lso, ptr, sizeof (dl_capab_lso_t));
17783115Syl150051 		ptr += sizeof (dl_capab_lso_t);
17793115Syl150051 	} else {
17803115Syl150051 		dsp->ds_lso = B_FALSE;
17813115Syl150051 		dsp->ds_lso_max = 0;
17823115Syl150051 	}
17833115Syl150051 
17843115Syl150051 	/*
17850Sstevel@tonic-gate 	 * Zero copy
17860Sstevel@tonic-gate 	 */
17875895Syz147064 	if (zcopy_capable) {
17880Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
17890Sstevel@tonic-gate 
17900Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
17910Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
17920Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
17930Sstevel@tonic-gate 
17940Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
17950Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
17960Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
17970Sstevel@tonic-gate 
17980Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
17990Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
18000Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
18010Sstevel@tonic-gate 	}
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1804269Sericheng 
1805269Sericheng 	rw_exit(&dsp->ds_lock);
1806269Sericheng 	qreply(q, mp);
1807269Sericheng 	return (B_TRUE);
18080Sstevel@tonic-gate }
18095113Syz147064 
18105113Syz147064 /*
18115113Syz147064  * Disable any enabled capabilities.
18125113Syz147064  */
18135113Syz147064 void
18145113Syz147064 dld_capabilities_disable(dld_str_t *dsp)
18155113Syz147064 {
18165113Syz147064 	if (dsp->ds_polling)
18175113Syz147064 		proto_poll_disable(dsp);
18185113Syz147064 
18195113Syz147064 	if (dsp->ds_soft_ring)
18205113Syz147064 		proto_soft_ring_disable(dsp);
18215113Syz147064 }
1822