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;
2415903Ssowmini 		/* We can only get the sdu if we're attached. */
2425903Ssowmini 		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;
888*6353Sdr146992 	if (addr_length > 0) {
889*6353Sdr146992 		addr = kmem_alloc(addr_length, KM_NOSLEEP);
890*6353Sdr146992 		if (addr == NULL) {
891*6353Sdr146992 			rw_exit(&dsp->ds_lock);
892*6353Sdr146992 			merror(q, mp, ENOSR);
893*6353Sdr146992 			return (B_FALSE);
894*6353Sdr146992 		}
8950Sstevel@tonic-gate 
896*6353Sdr146992 		/*
897*6353Sdr146992 		 * Copy out the address before we drop the lock; we don't
898*6353Sdr146992 		 * want to call dlphysaddrack() while holding ds_lock.
899*6353Sdr146992 		 */
900*6353Sdr146992 		bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
901*6353Sdr146992 		    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
902269Sericheng 
903*6353Sdr146992 		rw_exit(&dsp->ds_lock);
904*6353Sdr146992 		dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
905*6353Sdr146992 		kmem_free(addr, addr_length);
906*6353Sdr146992 	} else {
907*6353Sdr146992 		rw_exit(&dsp->ds_lock);
908*6353Sdr146992 		dlphysaddrack(q, mp, NULL, 0);
909*6353Sdr146992 	}
910269Sericheng 	return (B_TRUE);
9110Sstevel@tonic-gate failed:
912269Sericheng 	rw_exit(&dsp->ds_lock);
913269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
914269Sericheng 	return (B_FALSE);
915269Sericheng }
9160Sstevel@tonic-gate 
917269Sericheng /*
918269Sericheng  * DL_SET_PHYS_ADDR_REQ
919269Sericheng  */
920269Sericheng static boolean_t
921269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
922269Sericheng {
923269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
924269Sericheng 	int		err = 0;
925269Sericheng 	t_uscalar_t	dl_err;
926269Sericheng 	queue_t		*q = dsp->ds_wq;
9270Sstevel@tonic-gate 
9285895Syz147064 	/*
9295895Syz147064 	 * Because control message processing is serialized, we don't need
9305895Syz147064 	 * to hold any locks to read any fields of dsp; we only need ds_lock
9315895Syz147064 	 * to update the ds_passivestate field.
9325895Syz147064 	 */
933269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
934269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
935269Sericheng 		dl_err = DL_OUTSTATE;
936269Sericheng 		goto failed;
937269Sericheng 	}
938269Sericheng 
939269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
940269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
941269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
942269Sericheng 		dl_err = DL_BADPRIM;
943269Sericheng 		goto failed;
9440Sstevel@tonic-gate 	}
9450Sstevel@tonic-gate 
946269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
947269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
948269Sericheng 		dl_err = DL_SYSERR;
949269Sericheng 		err = EBUSY;
950269Sericheng 		goto failed;
951269Sericheng 	}
952269Sericheng 
953269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
954269Sericheng 	if (err != 0) {
955269Sericheng 		switch (err) {
956269Sericheng 		case EINVAL:
957269Sericheng 			dl_err = DL_BADADDR;
958269Sericheng 			err = 0;
959269Sericheng 			break;
960269Sericheng 
961269Sericheng 		default:
962269Sericheng 			dl_err = DL_SYSERR;
963269Sericheng 			break;
964269Sericheng 		}
9655895Syz147064 
966269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
967269Sericheng 			dls_active_clear(dsp->ds_dc);
968269Sericheng 
969269Sericheng 		goto failed;
970269Sericheng 	}
9715895Syz147064 
9725895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
973269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
974269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
9755895Syz147064 	rw_exit(&dsp->ds_lock);
976269Sericheng 
977269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
978269Sericheng 	return (B_TRUE);
979269Sericheng failed:
980269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
981269Sericheng 	return (B_FALSE);
982269Sericheng }
983269Sericheng 
984269Sericheng /*
985269Sericheng  * DL_UDQOS_REQ
986269Sericheng  */
987269Sericheng static boolean_t
988269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
989269Sericheng {
990269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
991269Sericheng 	dl_qos_cl_sel1_t *selp;
992269Sericheng 	int		off, len;
993269Sericheng 	t_uscalar_t	dl_err;
994269Sericheng 	queue_t		*q = dsp->ds_wq;
995269Sericheng 
996269Sericheng 	off = dlp->dl_qos_offset;
997269Sericheng 	len = dlp->dl_qos_length;
998269Sericheng 
999269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
1000269Sericheng 		dl_err = DL_BADPRIM;
1001269Sericheng 		goto failed;
1002269Sericheng 	}
1003269Sericheng 
1004269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1005269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1006269Sericheng 		dl_err = DL_BADQOSTYPE;
1007269Sericheng 		goto failed;
1008269Sericheng 	}
1009269Sericheng 
10102760Sdg199075 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1011269Sericheng 	    selp->dl_priority < 0) {
1012269Sericheng 		dl_err = DL_BADQOSPARAM;
1013269Sericheng 		goto failed;
1014269Sericheng 	}
1015269Sericheng 
10165895Syz147064 	if (dsp->ds_dlstate == DL_UNATTACHED ||
10175895Syz147064 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
10185895Syz147064 		dl_err = DL_OUTSTATE;
10195895Syz147064 		goto failed;
10205895Syz147064 	}
1021269Sericheng 
10225895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
10235895Syz147064 	dsp->ds_pri = selp->dl_priority;
1024269Sericheng 	rw_exit(&dsp->ds_lock);
10255895Syz147064 
1026269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1027269Sericheng 	return (B_TRUE);
1028269Sericheng failed:
1029269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1030269Sericheng 	return (B_FALSE);
10310Sstevel@tonic-gate }
10320Sstevel@tonic-gate 
10331184Skrgopi static boolean_t
10341184Skrgopi check_ip_above(queue_t *q)
10351184Skrgopi {
10361184Skrgopi 	queue_t		*next_q;
10371184Skrgopi 	boolean_t	ret = B_TRUE;
10381184Skrgopi 
10391184Skrgopi 	claimstr(q);
10401184Skrgopi 	next_q = q->q_next;
10411184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
10421184Skrgopi 		ret = B_FALSE;
10431184Skrgopi 	releasestr(q);
10441184Skrgopi 	return (ret);
10451184Skrgopi }
10461184Skrgopi 
10470Sstevel@tonic-gate /*
1048269Sericheng  * DL_CAPABILITY_REQ
10490Sstevel@tonic-gate  */
1050269Sericheng /*ARGSUSED*/
1051269Sericheng static boolean_t
1052269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
10530Sstevel@tonic-gate {
1054269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1055269Sericheng 	dl_capability_sub_t *sp;
1056269Sericheng 	size_t		size, len;
1057269Sericheng 	offset_t	off, end;
1058269Sericheng 	t_uscalar_t	dl_err;
1059269Sericheng 	queue_t		*q = dsp->ds_wq;
1060269Sericheng 
10615895Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
1062269Sericheng 
1063269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1064269Sericheng 		dl_err = DL_BADPRIM;
1065269Sericheng 		goto failed;
1066269Sericheng 	}
1067269Sericheng 
1068269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1069269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1070269Sericheng 		dl_err = DL_OUTSTATE;
1071269Sericheng 		goto failed;
1072269Sericheng 	}
1073269Sericheng 
1074269Sericheng 	/*
1075269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1076269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1077269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1078269Sericheng 	 */
1079269Sericheng 	if (dlp->dl_sub_length == 0) {
1080269Sericheng 		/* callee drops lock */
1081269Sericheng 		return (proto_capability_advertise(dsp, mp));
1082269Sericheng 	}
1083269Sericheng 
1084269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1085269Sericheng 		dl_err = DL_BADPRIM;
1086269Sericheng 		goto failed;
1087269Sericheng 	}
1088269Sericheng 
1089269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1090269Sericheng 
1091269Sericheng 	off = dlp->dl_sub_offset;
1092269Sericheng 	len = dlp->dl_sub_length;
10930Sstevel@tonic-gate 
10940Sstevel@tonic-gate 	/*
1095269Sericheng 	 * Walk the list of capabilities to be enabled.
10960Sstevel@tonic-gate 	 */
1097269Sericheng 	for (end = off + len; off < end; ) {
1098269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1099269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1100269Sericheng 
1101269Sericheng 		if (off + size > end ||
1102269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1103269Sericheng 			dl_err = DL_BADPRIM;
1104269Sericheng 			goto failed;
1105269Sericheng 		}
1106269Sericheng 
1107269Sericheng 		switch (sp->dl_cap) {
1108269Sericheng 		/*
1109269Sericheng 		 * TCP/IP checksum offload to hardware.
1110269Sericheng 		 */
1111269Sericheng 		case DL_CAPAB_HCKSUM: {
1112269Sericheng 			dl_capab_hcksum_t *hcksump;
1113269Sericheng 			dl_capab_hcksum_t hcksum;
1114269Sericheng 
1115269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1116269Sericheng 			/*
1117269Sericheng 			 * Copy for alignment.
1118269Sericheng 			 */
1119269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1120269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1121269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1122269Sericheng 			break;
1123269Sericheng 		}
1124269Sericheng 
1125269Sericheng 		/*
11263115Syl150051 		 * Large segment offload. (LSO)
11273115Syl150051 		 */
11283115Syl150051 		case DL_CAPAB_LSO: {
11293115Syl150051 			dl_capab_lso_t *lsop;
11303115Syl150051 			dl_capab_lso_t lso;
11313115Syl150051 
11323115Syl150051 			lsop = (dl_capab_lso_t *)&sp[1];
11333115Syl150051 			/*
11343115Syl150051 			 * Copy for alignment.
11353115Syl150051 			 */
11363115Syl150051 			bcopy(lsop, &lso, sizeof (dl_capab_lso_t));
11373115Syl150051 			dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
11383115Syl150051 			bcopy(&lso, lsop, sizeof (dl_capab_lso_t));
11393115Syl150051 			break;
11403115Syl150051 		}
11413115Syl150051 
11423115Syl150051 		/*
1143269Sericheng 		 * IP polling interface.
1144269Sericheng 		 */
1145269Sericheng 		case DL_CAPAB_POLL: {
11461184Skrgopi 			dl_capab_dls_t *pollp;
11471184Skrgopi 			dl_capab_dls_t	poll;
1148269Sericheng 
11491184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1150269Sericheng 			/*
1151269Sericheng 			 * Copy for alignment.
1152269Sericheng 			 */
11531184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1154269Sericheng 
11551184Skrgopi 			switch (poll.dls_flags) {
1156269Sericheng 			default:
1157269Sericheng 				/*FALLTHRU*/
1158269Sericheng 			case POLL_DISABLE:
1159269Sericheng 				proto_poll_disable(dsp);
1160269Sericheng 				break;
1161269Sericheng 
1162269Sericheng 			case POLL_ENABLE:
1163269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1164269Sericheng 
1165269Sericheng 				/*
1166269Sericheng 				 * Make sure polling is disabled.
1167269Sericheng 				 */
1168269Sericheng 				proto_poll_disable(dsp);
1169269Sericheng 
1170269Sericheng 				/*
11715895Syz147064 				 * Note that only IP should enable POLL.
1172269Sericheng 				 */
11731184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
11741184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
11751184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
11761184Skrgopi 					poll.dls_flags = POLL_ENABLE;
11775895Syz147064 				} else {
11785895Syz147064 					bzero(&poll, sizeof (dl_capab_dls_t));
11795895Syz147064 					poll.dls_flags = POLL_DISABLE;
11801184Skrgopi 				}
1181269Sericheng 				break;
1182269Sericheng 			}
1183269Sericheng 
11841184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
11851184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
11861184Skrgopi 			break;
11871184Skrgopi 		}
11881184Skrgopi 		case DL_CAPAB_SOFT_RING: {
11891184Skrgopi 			dl_capab_dls_t *soft_ringp;
11901184Skrgopi 			dl_capab_dls_t soft_ring;
11911184Skrgopi 
11921184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
11931184Skrgopi 			/*
11941184Skrgopi 			 * Copy for alignment.
11951184Skrgopi 			 */
11961184Skrgopi 			bcopy(soft_ringp, &soft_ring,
11971184Skrgopi 			    sizeof (dl_capab_dls_t));
11981184Skrgopi 
11991184Skrgopi 			switch (soft_ring.dls_flags) {
12001184Skrgopi 			default:
12011184Skrgopi 				/*FALLTHRU*/
12021184Skrgopi 			case SOFT_RING_DISABLE:
12031184Skrgopi 				proto_soft_ring_disable(dsp);
12041184Skrgopi 				break;
12051184Skrgopi 
12061184Skrgopi 			case SOFT_RING_ENABLE:
12074114Sja97890 				ASSERT(!(dld_opt & DLD_OPT_NO_SOFTRING));
12081184Skrgopi 				/*
12091184Skrgopi 				 * Make sure soft_ring is disabled.
12101184Skrgopi 				 */
12111184Skrgopi 				proto_soft_ring_disable(dsp);
12121184Skrgopi 
12131184Skrgopi 				/*
12145895Syz147064 				 * Note that only IP can enable soft ring.
12151184Skrgopi 				 */
12161184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
12171184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
12181184Skrgopi 					bzero(&soft_ring,
12191184Skrgopi 					    sizeof (dl_capab_dls_t));
12205895Syz147064 					soft_ring.dls_flags = SOFT_RING_ENABLE;
12211184Skrgopi 				} else {
12221184Skrgopi 					bzero(&soft_ring,
12231184Skrgopi 					    sizeof (dl_capab_dls_t));
12245895Syz147064 					soft_ring.dls_flags = SOFT_RING_DISABLE;
12251184Skrgopi 				}
12261184Skrgopi 				break;
12271184Skrgopi 			}
12281184Skrgopi 
12291184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
12301184Skrgopi 			bcopy(&soft_ring, soft_ringp,
12311184Skrgopi 			    sizeof (dl_capab_dls_t));
1232269Sericheng 			break;
1233269Sericheng 		}
1234269Sericheng 		default:
1235269Sericheng 			break;
1236269Sericheng 		}
1237269Sericheng 
1238269Sericheng 		off += size;
1239269Sericheng 	}
1240269Sericheng 	rw_exit(&dsp->ds_lock);
1241269Sericheng 	qreply(q, mp);
1242269Sericheng 	return (B_TRUE);
1243269Sericheng failed:
1244269Sericheng 	rw_exit(&dsp->ds_lock);
1245269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1246269Sericheng 	return (B_FALSE);
12470Sstevel@tonic-gate }
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate /*
1250269Sericheng  * DL_NOTIFY_REQ
12510Sstevel@tonic-gate  */
1252269Sericheng static boolean_t
1253269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
12540Sstevel@tonic-gate {
1255269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1256269Sericheng 	t_uscalar_t	dl_err;
1257269Sericheng 	queue_t		*q = dsp->ds_wq;
1258269Sericheng 	uint_t		note =
1259269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1260269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1261269Sericheng 	    DL_NOTE_PHYS_ADDR |
1262269Sericheng 	    DL_NOTE_LINK_UP |
1263269Sericheng 	    DL_NOTE_LINK_DOWN |
12642311Sseb 	    DL_NOTE_CAPAB_RENEG |
12652311Sseb 	    DL_NOTE_SPEED;
12660Sstevel@tonic-gate 
12671521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
12681521Syz147064 
1269269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1270269Sericheng 		dl_err = DL_BADPRIM;
1271269Sericheng 		goto failed;
1272269Sericheng 	}
12730Sstevel@tonic-gate 
1274269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1275269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1276269Sericheng 		dl_err = DL_OUTSTATE;
1277269Sericheng 		goto failed;
12780Sstevel@tonic-gate 	}
12790Sstevel@tonic-gate 
12805895Syz147064 	note &= ~(mac_no_notification(dsp->ds_mh));
12815895Syz147064 
1282269Sericheng 	/*
1283269Sericheng 	 * Cache the notifications that are being enabled.
1284269Sericheng 	 */
1285269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1286269Sericheng 	rw_exit(&dsp->ds_lock);
1287269Sericheng 	/*
1288269Sericheng 	 * The ACK carries all notifications regardless of which set is
1289269Sericheng 	 * being enabled.
1290269Sericheng 	 */
1291269Sericheng 	dlnotifyack(q, mp, note);
1292269Sericheng 
1293269Sericheng 	/*
1294269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1295269Sericheng 	 */
1296269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1297269Sericheng 	if (dsp->ds_notifications != 0) {
1298269Sericheng 		rw_exit(&dsp->ds_lock);
1299269Sericheng 		dld_str_notify_ind(dsp);
1300269Sericheng 	} else {
1301269Sericheng 		rw_exit(&dsp->ds_lock);
1302269Sericheng 	}
1303269Sericheng 	return (B_TRUE);
1304269Sericheng failed:
1305269Sericheng 	rw_exit(&dsp->ds_lock);
1306269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1307269Sericheng 	return (B_FALSE);
13080Sstevel@tonic-gate }
13090Sstevel@tonic-gate 
13100Sstevel@tonic-gate /*
13115895Syz147064  * DL_UNITDATA_REQ
13120Sstevel@tonic-gate  */
13135895Syz147064 void
13145895Syz147064 dld_wput_proto_data(dld_str_t *dsp, mblk_t *mp)
13150Sstevel@tonic-gate {
1316269Sericheng 	queue_t			*q = dsp->ds_wq;
13175895Syz147064 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)mp->b_rptr;
1318269Sericheng 	off_t			off;
1319269Sericheng 	size_t			len, size;
1320269Sericheng 	const uint8_t		*addr;
1321269Sericheng 	uint16_t		sap;
1322269Sericheng 	uint_t			addr_length;
13232311Sseb 	mblk_t			*bp, *payload;
1324269Sericheng 	uint32_t		start, stuff, end, value, flags;
1325269Sericheng 	t_uscalar_t		dl_err;
13265903Ssowmini 	uint_t			max_sdu;
1327269Sericheng 
1328269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1329269Sericheng 		dl_err = DL_BADPRIM;
1330269Sericheng 		goto failed;
1331269Sericheng 	}
1332269Sericheng 
1333269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1334269Sericheng 
1335269Sericheng 	off = dlp->dl_dest_addr_offset;
1336269Sericheng 	len = dlp->dl_dest_addr_length;
1337269Sericheng 
1338269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1339269Sericheng 		dl_err = DL_BADPRIM;
1340269Sericheng 		goto failed;
1341269Sericheng 	}
1342269Sericheng 
1343269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1344269Sericheng 		dl_err = DL_BADADDR;
1345269Sericheng 		goto failed;
1346269Sericheng 	}
1347269Sericheng 
1348269Sericheng 	addr = mp->b_rptr + off;
1349269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1350269Sericheng 
1351269Sericheng 	/*
1352269Sericheng 	 * Check the length of the packet and the block types.
1353269Sericheng 	 */
1354269Sericheng 	size = 0;
13552311Sseb 	payload = mp->b_cont;
13562311Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1357269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1358269Sericheng 			goto baddata;
1359269Sericheng 
1360269Sericheng 		size += MBLKL(bp);
1361269Sericheng 	}
1362269Sericheng 
13635903Ssowmini 	mac_sdu_get(dsp->ds_mh, NULL, &max_sdu);
13645903Ssowmini 	if (size > max_sdu)
1365269Sericheng 		goto baddata;
1366269Sericheng 
1367269Sericheng 	/*
1368269Sericheng 	 * Build a packet header.
1369269Sericheng 	 */
13702760Sdg199075 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dlp->dl_priority.dl_max,
13712760Sdg199075 	    &payload)) == NULL) {
1372269Sericheng 		dl_err = DL_BADADDR;
1373269Sericheng 		goto failed;
1374269Sericheng 	}
1375269Sericheng 
1376269Sericheng 	/*
1377269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1378269Sericheng 	 */
1379269Sericheng 	freeb(mp);
1380269Sericheng 
1381269Sericheng 	/*
1382269Sericheng 	 * Transfer the checksum offload information if it is present.
1383269Sericheng 	 */
13842311Sseb 	hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1385269Sericheng 	    &flags);
13862311Sseb 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1387269Sericheng 
1388269Sericheng 	/*
1389269Sericheng 	 * Link the payload onto the new header.
1390269Sericheng 	 */
1391269Sericheng 	ASSERT(bp->b_cont == NULL);
13922311Sseb 	bp->b_cont = payload;
13932760Sdg199075 	dld_tx_single(dsp, bp);
13945895Syz147064 	return;
1395269Sericheng failed:
1396269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
13975895Syz147064 	return;
1398269Sericheng 
1399269Sericheng baddata:
1400269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1401269Sericheng }
1402269Sericheng 
1403269Sericheng /*
1404269Sericheng  * DL_PASSIVE_REQ
1405269Sericheng  */
1406269Sericheng /* ARGSUSED */
1407269Sericheng static boolean_t
1408269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1409269Sericheng {
1410269Sericheng 	t_uscalar_t dl_err;
1411269Sericheng 
14125895Syz147064 	/*
14135895Syz147064 	 * READER lock is enough because ds_passivestate can only be changed
14145895Syz147064 	 * as the result of non-data message processing.
14155895Syz147064 	 */
14165895Syz147064 	rw_enter(&dsp->ds_lock, RW_READER);
14175895Syz147064 
1418269Sericheng 	/*
1419269Sericheng 	 * If we've already become active by issuing an active primitive,
1420269Sericheng 	 * then it's too late to try to become passive.
1421269Sericheng 	 */
1422269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1423269Sericheng 		dl_err = DL_OUTSTATE;
1424269Sericheng 		goto failed;
1425269Sericheng 	}
1426269Sericheng 
1427269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1428269Sericheng 		dl_err = DL_BADPRIM;
1429269Sericheng 		goto failed;
1430269Sericheng 	}
1431269Sericheng 
1432269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1433269Sericheng 	rw_exit(&dsp->ds_lock);
1434269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1435269Sericheng 	return (B_TRUE);
1436269Sericheng failed:
1437269Sericheng 	rw_exit(&dsp->ds_lock);
1438269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1439269Sericheng 	return (B_FALSE);
1440269Sericheng }
1441269Sericheng 
1442269Sericheng /*
1443269Sericheng  * Catch-all handler.
1444269Sericheng  */
1445269Sericheng static boolean_t
1446269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1447269Sericheng {
1448269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1449269Sericheng 	return (B_FALSE);
14500Sstevel@tonic-gate }
14510Sstevel@tonic-gate 
14520Sstevel@tonic-gate static void
14530Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
14540Sstevel@tonic-gate {
14550Sstevel@tonic-gate 	mac_handle_t	mh;
14560Sstevel@tonic-gate 
14575895Syz147064 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
1458269Sericheng 
14590Sstevel@tonic-gate 	if (!dsp->ds_polling)
14600Sstevel@tonic-gate 		return;
14610Sstevel@tonic-gate 
14620Sstevel@tonic-gate 	/*
14630Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
14640Sstevel@tonic-gate 	 */
14650Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
14660Sstevel@tonic-gate 
14670Sstevel@tonic-gate 	/*
14680Sstevel@tonic-gate 	 * Reset the resource_add callback.
14690Sstevel@tonic-gate 	 */
14700Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
14710Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
14721184Skrgopi 	mac_resources(mh);
14730Sstevel@tonic-gate 
14740Sstevel@tonic-gate 	/*
14750Sstevel@tonic-gate 	 * Set receive function back to default.
14760Sstevel@tonic-gate 	 */
14770Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
14785895Syz147064 	    dld_str_rx_fastpath : dld_str_rx_unitdata, dsp);
14790Sstevel@tonic-gate 
14800Sstevel@tonic-gate 	/*
14810Sstevel@tonic-gate 	 * Note that polling is disabled.
14820Sstevel@tonic-gate 	 */
14830Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
14840Sstevel@tonic-gate }
14850Sstevel@tonic-gate 
14860Sstevel@tonic-gate static boolean_t
14871184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
14880Sstevel@tonic-gate {
14890Sstevel@tonic-gate 	mac_handle_t	mh;
14900Sstevel@tonic-gate 
1491269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
14920Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
14930Sstevel@tonic-gate 
14940Sstevel@tonic-gate 	/*
14950Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
14960Sstevel@tonic-gate 	 * has been enabled.
14970Sstevel@tonic-gate 	 */
14980Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
14990Sstevel@tonic-gate 		return (B_FALSE);
15000Sstevel@tonic-gate 
15010Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
15020Sstevel@tonic-gate 
15030Sstevel@tonic-gate 	/*
15040Sstevel@tonic-gate 	 * Register resources.
15050Sstevel@tonic-gate 	 */
15061184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
15071184Skrgopi 	    (void *)pollp->dls_rx_handle);
15085895Syz147064 
15090Sstevel@tonic-gate 	mac_resources(mh);
15100Sstevel@tonic-gate 
15110Sstevel@tonic-gate 	/*
15125895Syz147064 	 * Set the upstream receive function.
15130Sstevel@tonic-gate 	 */
15141184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
15151184Skrgopi 	    (void *)pollp->dls_rx_handle);
15160Sstevel@tonic-gate 
15170Sstevel@tonic-gate 	/*
15180Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
15190Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
15200Sstevel@tonic-gate 	 */
15210Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
15220Sstevel@tonic-gate 	return (B_TRUE);
15230Sstevel@tonic-gate }
15240Sstevel@tonic-gate 
15251184Skrgopi static void
15261184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
15271184Skrgopi {
15281184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
15291184Skrgopi 
15301184Skrgopi 	if (!dsp->ds_soft_ring)
15311184Skrgopi 		return;
15321184Skrgopi 
15331184Skrgopi 	/*
15341184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
15351184Skrgopi 	 */
15361184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
15371184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
15381184Skrgopi 	/*
15391184Skrgopi 	 * Note that fanout is disabled.
15401184Skrgopi 	 */
15411184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
15421184Skrgopi }
15431184Skrgopi 
15441184Skrgopi static boolean_t
15451184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
15461184Skrgopi {
15471184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
15481184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
15491184Skrgopi 
15501184Skrgopi 	/*
15511184Skrgopi 	 * We cannot enable soft_ring if raw mode
15521184Skrgopi 	 * has been enabled.
15531184Skrgopi 	 */
15541184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
15551184Skrgopi 		return (B_FALSE);
15561184Skrgopi 
15571184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
15581184Skrgopi 		return (B_FALSE);
15591184Skrgopi 
15601184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
15611184Skrgopi 	return (B_TRUE);
15621184Skrgopi }
15631184Skrgopi 
15641184Skrgopi static void
15651184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
15661184Skrgopi {
15675895Syz147064 	dls_channel_t	dc = dsp->ds_dc;
15681184Skrgopi 
15691184Skrgopi 	if (type == SOFT_RING_NONE) {
15705895Syz147064 		dls_rx_set(dc, (dsp->ds_mode == DLD_FASTPATH) ?
15715895Syz147064 		    dld_str_rx_fastpath : dld_str_rx_unitdata, dsp);
15725895Syz147064 	} else if (type != SOFT_RING_NONE) {
15735895Syz147064 		dls_rx_set(dc, (dls_rx_t)dls_soft_ring_fanout, dc);
15741184Skrgopi 	}
15751184Skrgopi }
15761184Skrgopi 
15770Sstevel@tonic-gate /*
15780Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
15790Sstevel@tonic-gate  */
1580269Sericheng static boolean_t
1581269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
15820Sstevel@tonic-gate {
15830Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
15840Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
15850Sstevel@tonic-gate 	size_t			subsize;
15861184Skrgopi 	dl_capab_dls_t		poll;
15873115Syl150051 	dl_capab_dls_t		soft_ring;
15880Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
15893115Syl150051 	dl_capab_lso_t		lso;
15900Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
15910Sstevel@tonic-gate 	uint8_t			*ptr;
1592269Sericheng 	queue_t			*q = dsp->ds_wq;
1593269Sericheng 	mblk_t			*mp1;
15945895Syz147064 	boolean_t		is_vlan = (dsp->ds_vid != VLAN_ID_NONE);
15955895Syz147064 	boolean_t		poll_capable = B_FALSE;
15965895Syz147064 	boolean_t		soft_ring_capable = B_FALSE;
15975895Syz147064 	boolean_t		hcksum_capable = B_FALSE;
15985895Syz147064 	boolean_t		zcopy_capable = B_FALSE;
15995895Syz147064 	boolean_t		lso_capable = B_FALSE;
16005895Syz147064 	mac_capab_lso_t		mac_lso;
1601269Sericheng 
16025895Syz147064 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate 	/*
16050Sstevel@tonic-gate 	 * Initially assume no capabilities.
16060Sstevel@tonic-gate 	 */
16070Sstevel@tonic-gate 	subsize = 0;
16080Sstevel@tonic-gate 
16091555Skrgopi 	/*
16105895Syz147064 	 * Check if soft ring can be enabled on this interface. Note that we
16115895Syz147064 	 * do not enable softring on any legacy drivers, because doing that
16125895Syz147064 	 * would hurt the performance if the legacy driver has its own taskq
16135895Syz147064 	 * implementation. Further, most high-performance legacy drivers do
16145895Syz147064 	 * have their own taskq implementation.
16155895Syz147064 	 *
16165895Syz147064 	 * If advertising DL_CAPAB_SOFT_RING has not been explicitly disabled,
16175895Syz147064 	 * reserve space for that capability.
16181555Skrgopi 	 */
16195895Syz147064 	if (!mac_is_legacy(dsp->ds_mh) && !(dld_opt & DLD_OPT_NO_SOFTRING)) {
16205895Syz147064 		soft_ring_capable = B_TRUE;
16211555Skrgopi 		subsize += sizeof (dl_capability_sub_t) +
16225113Syz147064 		    sizeof (dl_capab_dls_t);
16234114Sja97890 	}
16241184Skrgopi 
16250Sstevel@tonic-gate 	/*
16260Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
16270Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
16280Sstevel@tonic-gate 	 * then reserve space for that capability.
16290Sstevel@tonic-gate 	 */
16305895Syz147064 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) &&
16315895Syz147064 	    !(dld_opt & DLD_OPT_NO_POLL) && !is_vlan) {
16325895Syz147064 		poll_capable = B_TRUE;
16330Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
16341184Skrgopi 		    sizeof (dl_capab_dls_t);
16350Sstevel@tonic-gate 	}
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate 	/*
16385895Syz147064 	 * Check if checksum offload is supported on this MAC.  Don't
16395895Syz147064 	 * advertise DL_CAPAB_HCKSUM if the underlying MAC is VLAN incapable,
16405895Syz147064 	 * since it might not be able to do the hardware checksum offload
16415895Syz147064 	 * with the correct offset.
16420Sstevel@tonic-gate 	 */
16435895Syz147064 	bzero(&hcksum, sizeof (dl_capab_hcksum_t));
16445895Syz147064 	if ((!is_vlan || (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_NATIVEVLAN,
16455895Syz147064 	    NULL))) && mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
16462311Sseb 	    &hcksum.hcksum_txflags)) {
16475895Syz147064 		if (hcksum.hcksum_txflags != 0) {
16485895Syz147064 			hcksum_capable = B_TRUE;
16495895Syz147064 			subsize += sizeof (dl_capability_sub_t) +
16505895Syz147064 			    sizeof (dl_capab_hcksum_t);
16515895Syz147064 		}
16520Sstevel@tonic-gate 	}
16530Sstevel@tonic-gate 
16540Sstevel@tonic-gate 	/*
16555895Syz147064 	 * Check if LSO is supported on this MAC, then reserve space for
16565895Syz147064 	 * the DL_CAPAB_LSO capability.
16573115Syl150051 	 */
16585895Syz147064 	if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
16595895Syz147064 		lso_capable = B_TRUE;
16603115Syl150051 		subsize += sizeof (dl_capability_sub_t) +
16613115Syl150051 		    sizeof (dl_capab_lso_t);
16623115Syl150051 	}
16633115Syl150051 
16643115Syl150051 	/*
16655895Syz147064 	 * Check if zerocopy is supported on this interface.
16665895Syz147064 	 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled
16675895Syz147064 	 * then reserve space for that capability.
16680Sstevel@tonic-gate 	 */
16695895Syz147064 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) &&
16705895Syz147064 	    !(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
16715895Syz147064 		zcopy_capable = B_TRUE;
16720Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
16730Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
16740Sstevel@tonic-gate 	}
16750Sstevel@tonic-gate 
16760Sstevel@tonic-gate 	/*
1677269Sericheng 	 * If there are no capabilities to advertise or if we
1678269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
16790Sstevel@tonic-gate 	 */
16801184Skrgopi 	if ((mp1 = reallocb(mp,
1681269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1682269Sericheng 		rw_exit(&dsp->ds_lock);
1683269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1684269Sericheng 		return (B_FALSE);
16850Sstevel@tonic-gate 	}
16860Sstevel@tonic-gate 
1687269Sericheng 	mp = mp1;
1688269Sericheng 	DB_TYPE(mp) = M_PROTO;
1689269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1690269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
16910Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
16920Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
16930Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
16940Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
16950Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
16960Sstevel@tonic-gate 
16970Sstevel@tonic-gate 	/*
16980Sstevel@tonic-gate 	 * IP polling interface.
16990Sstevel@tonic-gate 	 */
17005895Syz147064 	if (poll_capable) {
17010Sstevel@tonic-gate 		/*
1702269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
17035895Syz147064 		 * READER lock is enough because ds_polling can only be
17045895Syz147064 		 * changed as the result of non-data message processing.
1705269Sericheng 		 */
17065895Syz147064 		proto_poll_disable(dsp);
17070Sstevel@tonic-gate 
17085895Syz147064 		dlsp = (dl_capability_sub_t *)ptr;
17090Sstevel@tonic-gate 
17105895Syz147064 		dlsp->dl_cap = DL_CAPAB_POLL;
17115895Syz147064 		dlsp->dl_length = sizeof (dl_capab_dls_t);
17125895Syz147064 		ptr += sizeof (dl_capability_sub_t);
1713269Sericheng 
17145895Syz147064 		bzero(&poll, sizeof (dl_capab_dls_t));
17155895Syz147064 		poll.dls_version = POLL_VERSION_1;
17165895Syz147064 		poll.dls_flags = POLL_CAPABLE;
17175895Syz147064 		poll.dls_tx_handle = (uintptr_t)dsp;
17185895Syz147064 		poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
17195895Syz147064 		dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
17205895Syz147064 		bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
17215895Syz147064 		ptr += sizeof (dl_capab_dls_t);
17220Sstevel@tonic-gate 	}
17230Sstevel@tonic-gate 
1724269Sericheng 
17255895Syz147064 	if (soft_ring_capable) {
17261555Skrgopi 		dlsp = (dl_capability_sub_t *)ptr;
17271184Skrgopi 
17281555Skrgopi 		dlsp->dl_cap = DL_CAPAB_SOFT_RING;
17291555Skrgopi 		dlsp->dl_length = sizeof (dl_capab_dls_t);
17301555Skrgopi 		ptr += sizeof (dl_capability_sub_t);
17311184Skrgopi 
17321555Skrgopi 		bzero(&soft_ring, sizeof (dl_capab_dls_t));
17331555Skrgopi 		soft_ring.dls_version = SOFT_RING_VERSION_1;
17341555Skrgopi 		soft_ring.dls_flags = SOFT_RING_CAPABLE;
17351555Skrgopi 		soft_ring.dls_tx_handle = (uintptr_t)dsp;
17361555Skrgopi 		soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
17371555Skrgopi 		soft_ring.dls_ring_change_status =
17381555Skrgopi 		    (uintptr_t)proto_change_soft_ring_fanout;
17391555Skrgopi 		soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
17401555Skrgopi 		soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
17411184Skrgopi 
17421555Skrgopi 		dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
17431555Skrgopi 		bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
17441555Skrgopi 		ptr += sizeof (dl_capab_dls_t);
17451555Skrgopi 	}
17461184Skrgopi 
17470Sstevel@tonic-gate 	/*
17480Sstevel@tonic-gate 	 * TCP/IP checksum offload.
17490Sstevel@tonic-gate 	 */
17505895Syz147064 	if (hcksum_capable) {
17510Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
17520Sstevel@tonic-gate 
17530Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
17540Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
17550Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
17560Sstevel@tonic-gate 
17570Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
17580Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
17590Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
17600Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
17610Sstevel@tonic-gate 	}
17620Sstevel@tonic-gate 
17630Sstevel@tonic-gate 	/*
17643115Syl150051 	 * Large segment offload. (LSO)
17653115Syl150051 	 */
17665895Syz147064 	if (lso_capable) {
17673115Syl150051 		dlsp = (dl_capability_sub_t *)ptr;
17683115Syl150051 
17693115Syl150051 		dlsp->dl_cap = DL_CAPAB_LSO;
17703115Syl150051 		dlsp->dl_length = sizeof (dl_capab_lso_t);
17713115Syl150051 		ptr += sizeof (dl_capability_sub_t);
17723115Syl150051 
17733115Syl150051 		lso.lso_version = LSO_VERSION_1;
17743115Syl150051 		lso.lso_flags = mac_lso.lso_flags;
17753115Syl150051 		lso.lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max;
17763115Syl150051 
17773115Syl150051 		/* Simply enable LSO with DLD */
17783115Syl150051 		dsp->ds_lso = B_TRUE;
17793115Syl150051 		dsp->ds_lso_max = lso.lso_max;
17803115Syl150051 
17813115Syl150051 		dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
17823115Syl150051 		bcopy(&lso, ptr, sizeof (dl_capab_lso_t));
17833115Syl150051 		ptr += sizeof (dl_capab_lso_t);
17843115Syl150051 	} else {
17853115Syl150051 		dsp->ds_lso = B_FALSE;
17863115Syl150051 		dsp->ds_lso_max = 0;
17873115Syl150051 	}
17883115Syl150051 
17893115Syl150051 	/*
17900Sstevel@tonic-gate 	 * Zero copy
17910Sstevel@tonic-gate 	 */
17925895Syz147064 	if (zcopy_capable) {
17930Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
17940Sstevel@tonic-gate 
17950Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
17960Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
17970Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
17980Sstevel@tonic-gate 
17990Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
18000Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
18010Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
18020Sstevel@tonic-gate 
18030Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
18040Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
18050Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
18060Sstevel@tonic-gate 	}
18070Sstevel@tonic-gate 
18080Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1809269Sericheng 
1810269Sericheng 	rw_exit(&dsp->ds_lock);
1811269Sericheng 	qreply(q, mp);
1812269Sericheng 	return (B_TRUE);
18130Sstevel@tonic-gate }
18145113Syz147064 
18155113Syz147064 /*
18165113Syz147064  * Disable any enabled capabilities.
18175113Syz147064  */
18185113Syz147064 void
18195113Syz147064 dld_capabilities_disable(dld_str_t *dsp)
18205113Syz147064 {
18215113Syz147064 	if (dsp->ds_polling)
18225113Syz147064 		proto_poll_disable(dsp);
18235113Syz147064 
18245113Syz147064 	if (dsp->ds_soft_ring)
18255113Syz147064 		proto_soft_ring_disable(dsp);
18265113Syz147064 }
1827