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 /*
270Sstevel@tonic-gate  * Data-Link Driver
280Sstevel@tonic-gate  */
290Sstevel@tonic-gate #include <sys/sysmacros.h>
30*8275SEric Cheng #include <sys/strsubr.h>
310Sstevel@tonic-gate #include <sys/strsun.h>
320Sstevel@tonic-gate #include <sys/vlan.h>
330Sstevel@tonic-gate #include <sys/dld_impl.h>
34*8275SEric Cheng #include <sys/mac_client.h>
35*8275SEric Cheng #include <sys/mac_client_impl.h>
36*8275SEric Cheng #include <sys/mac_client_priv.h>
370Sstevel@tonic-gate 
38*8275SEric Cheng typedef void proto_reqfunc_t(dld_str_t *, mblk_t *);
390Sstevel@tonic-gate 
400Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
410Sstevel@tonic-gate     proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
420Sstevel@tonic-gate     proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
430Sstevel@tonic-gate     proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
445895Syz147064     proto_notify_req, proto_passive_req;
450Sstevel@tonic-gate 
46*8275SEric Cheng static void proto_capability_advertise(dld_str_t *, mblk_t *);
47*8275SEric Cheng static int dld_capab_poll_disable(dld_str_t *, dld_capab_poll_t *);
481184Skrgopi 
490Sstevel@tonic-gate #define	DL_ACK_PENDING(state) \
500Sstevel@tonic-gate 	((state) == DL_ATTACH_PENDING || \
510Sstevel@tonic-gate 	(state) == DL_DETACH_PENDING || \
520Sstevel@tonic-gate 	(state) == DL_BIND_PENDING || \
530Sstevel@tonic-gate 	(state) == DL_UNBIND_PENDING)
540Sstevel@tonic-gate 
550Sstevel@tonic-gate /*
56269Sericheng  * Process a DLPI protocol message.
57269Sericheng  * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
58269Sericheng  * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
59269Sericheng  * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
60269Sericheng  * as 'passive' and forbids it from being subsequently made 'active'
61269Sericheng  * by the above primitives.
620Sstevel@tonic-gate  */
630Sstevel@tonic-gate void
64*8275SEric Cheng dld_proto(dld_str_t *dsp, mblk_t *mp)
650Sstevel@tonic-gate {
660Sstevel@tonic-gate 	t_uscalar_t		prim;
670Sstevel@tonic-gate 
68*8275SEric Cheng 	if (MBLKL(mp) < sizeof (t_uscalar_t)) {
69*8275SEric Cheng 		freemsg(mp);
70*8275SEric Cheng 		return;
71*8275SEric Cheng 	}
72*8275SEric Cheng 	prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
730Sstevel@tonic-gate 
74269Sericheng 	switch (prim) {
75269Sericheng 	case DL_INFO_REQ:
76*8275SEric Cheng 		proto_info_req(dsp, mp);
77269Sericheng 		break;
78269Sericheng 	case DL_BIND_REQ:
79*8275SEric Cheng 		proto_bind_req(dsp, mp);
80269Sericheng 		break;
81269Sericheng 	case DL_UNBIND_REQ:
82*8275SEric Cheng 		proto_unbind_req(dsp, mp);
83*8275SEric Cheng 		break;
84*8275SEric Cheng 	case DL_UNITDATA_REQ:
85*8275SEric Cheng 		proto_unitdata_req(dsp, mp);
86269Sericheng 		break;
87269Sericheng 	case DL_UDQOS_REQ:
88*8275SEric Cheng 		proto_udqos_req(dsp, mp);
89269Sericheng 		break;
90269Sericheng 	case DL_ATTACH_REQ:
91*8275SEric Cheng 		proto_attach_req(dsp, mp);
92269Sericheng 		break;
93269Sericheng 	case DL_DETACH_REQ:
94*8275SEric Cheng 		proto_detach_req(dsp, mp);
95269Sericheng 		break;
96269Sericheng 	case DL_ENABMULTI_REQ:
97*8275SEric Cheng 		proto_enabmulti_req(dsp, mp);
98269Sericheng 		break;
99269Sericheng 	case DL_DISABMULTI_REQ:
100*8275SEric Cheng 		proto_disabmulti_req(dsp, mp);
101269Sericheng 		break;
102269Sericheng 	case DL_PROMISCON_REQ:
103*8275SEric Cheng 		proto_promiscon_req(dsp, mp);
104269Sericheng 		break;
105269Sericheng 	case DL_PROMISCOFF_REQ:
106*8275SEric Cheng 		proto_promiscoff_req(dsp, mp);
107269Sericheng 		break;
108269Sericheng 	case DL_PHYS_ADDR_REQ:
109*8275SEric Cheng 		proto_physaddr_req(dsp, mp);
110269Sericheng 		break;
111269Sericheng 	case DL_SET_PHYS_ADDR_REQ:
112*8275SEric Cheng 		proto_setphysaddr_req(dsp, mp);
113269Sericheng 		break;
114269Sericheng 	case DL_NOTIFY_REQ:
115*8275SEric Cheng 		proto_notify_req(dsp, mp);
116269Sericheng 		break;
117269Sericheng 	case DL_CAPABILITY_REQ:
118*8275SEric Cheng 		proto_capability_req(dsp, mp);
119269Sericheng 		break;
120269Sericheng 	case DL_PASSIVE_REQ:
121*8275SEric Cheng 		proto_passive_req(dsp, mp);
122269Sericheng 		break;
123269Sericheng 	default:
124*8275SEric Cheng 		proto_req(dsp, mp);
125269Sericheng 		break;
1260Sstevel@tonic-gate 	}
1270Sstevel@tonic-gate }
1280Sstevel@tonic-gate 
129269Sericheng #define	NEG(x)	-(x)
1300Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1310Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
1322311Sseb 	uint8_t			dl_addr[MAXMACADDRLEN + sizeof (uint16_t)];
1332311Sseb 	uint8_t			dl_brdcst_addr[MAXMACADDRLEN];
1340Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1350Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1360Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1370Sstevel@tonic-gate 
1380Sstevel@tonic-gate /*
139269Sericheng  * DL_INFO_REQ
1400Sstevel@tonic-gate  */
141*8275SEric Cheng static void
142*8275SEric Cheng proto_info_req(dld_str_t *dsp, mblk_t *mp)
1430Sstevel@tonic-gate {
1440Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1450Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
1460Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
1470Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
1480Sstevel@tonic-gate 	uint8_t			*addr;
1490Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
1500Sstevel@tonic-gate 	uint_t			addr_length;
1510Sstevel@tonic-gate 	uint_t			sap_length;
152269Sericheng 	mac_info_t		minfo;
153269Sericheng 	mac_info_t		*minfop;
154269Sericheng 	queue_t			*q = dsp->ds_wq;
1550Sstevel@tonic-gate 
1560Sstevel@tonic-gate 	/*
1570Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
1580Sstevel@tonic-gate 	 * wrapper structure defined above.
1590Sstevel@tonic-gate 	 */
160269Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
1610Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
162*8275SEric Cheng 		return;
1630Sstevel@tonic-gate 
1640Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
1650Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
1680Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
1690Sstevel@tonic-gate 
1700Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
1710Sstevel@tonic-gate 
1720Sstevel@tonic-gate 	/*
1730Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
1740Sstevel@tonic-gate 	 */
1750Sstevel@tonic-gate 	addr = dlwp->dl_addr;
1760Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
1770Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
1780Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
1790Sstevel@tonic-gate 
1800Sstevel@tonic-gate 	/*
1810Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
1820Sstevel@tonic-gate 	 * nodes.
1830Sstevel@tonic-gate 	 */
1840Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
1850Sstevel@tonic-gate 	dlp->dl_version = DL_VERSION_2;
1860Sstevel@tonic-gate 
1870Sstevel@tonic-gate 	/*
188269Sericheng 	 * Set the style of the provider
1890Sstevel@tonic-gate 	 */
190269Sericheng 	dlp->dl_provider_style = dsp->ds_style;
1910Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
1920Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
1930Sstevel@tonic-gate 
1940Sstevel@tonic-gate 	/*
1950Sstevel@tonic-gate 	 * Set the current DLPI state.
1960Sstevel@tonic-gate 	 */
1970Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
1980Sstevel@tonic-gate 
1990Sstevel@tonic-gate 	/*
200269Sericheng 	 * Gratuitously set the media type. This is to deal with modules
201269Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
2020Sstevel@tonic-gate 	 * being completed.
2030Sstevel@tonic-gate 	 */
2040Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2050Sstevel@tonic-gate 
2060Sstevel@tonic-gate 	/*
207269Sericheng 	 * If the stream is not at least attached we try to retrieve the
208269Sericheng 	 * mac_info using mac_info_get()
2090Sstevel@tonic-gate 	 */
2100Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2110Sstevel@tonic-gate 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
212269Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
213269Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
214269Sericheng 			/*
215269Sericheng 			 * Cannot find mac_info. giving up.
216269Sericheng 			 */
217269Sericheng 			goto done;
218269Sericheng 		}
219269Sericheng 		minfop = &minfo;
220269Sericheng 	} else {
221269Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
2225903Ssowmini 		/* We can only get the sdu if we're attached. */
2235903Ssowmini 		mac_sdu_get(dsp->ds_mh, &dlp->dl_min_sdu, &dlp->dl_max_sdu);
224269Sericheng 	}
2250Sstevel@tonic-gate 
2260Sstevel@tonic-gate 	/*
2270Sstevel@tonic-gate 	 * Set the media type (properly this time).
2280Sstevel@tonic-gate 	 */
2293147Sxc151355 	if (dsp->ds_native)
2303147Sxc151355 		dlp->dl_mac_type = minfop->mi_nativemedia;
2313147Sxc151355 	else
2323147Sxc151355 		dlp->dl_mac_type = minfop->mi_media;
2330Sstevel@tonic-gate 
2340Sstevel@tonic-gate 	/*
2350Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2360Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2370Sstevel@tonic-gate 	 */
2380Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2390Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2400Sstevel@tonic-gate 
241269Sericheng 	addr_length = minfop->mi_addr_length;
2420Sstevel@tonic-gate 
2430Sstevel@tonic-gate 	/*
2440Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2450Sstevel@tonic-gate 	 */
2462311Sseb 	if (minfop->mi_brdcst_addr != NULL) {
2472311Sseb 		dlp->dl_brdcst_addr_offset =
2482311Sseb 		    (uintptr_t)brdcst_addr - (uintptr_t)dlp;
2492311Sseb 		bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
2502311Sseb 		dlp->dl_brdcst_addr_length = addr_length;
2512311Sseb 	}
2520Sstevel@tonic-gate 
2532760Sdg199075 	dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
2542760Sdg199075 	dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
2550Sstevel@tonic-gate 
2562760Sdg199075 	rangep->dl_qos_type = DL_QOS_CL_RANGE1;
2572760Sdg199075 	rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
2582760Sdg199075 	rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
2592760Sdg199075 	rangep->dl_protection.dl_min = DL_UNKNOWN;
2602760Sdg199075 	rangep->dl_protection.dl_max = DL_UNKNOWN;
2612760Sdg199075 	rangep->dl_residual_error = DL_UNKNOWN;
2620Sstevel@tonic-gate 
2632760Sdg199075 	/*
2642760Sdg199075 	 * Specify the supported range of priorities.
2652760Sdg199075 	 */
2662760Sdg199075 	rangep->dl_priority.dl_min = 0;
2672760Sdg199075 	rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
2680Sstevel@tonic-gate 
2692760Sdg199075 	dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
2702760Sdg199075 	dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
2710Sstevel@tonic-gate 
2722760Sdg199075 	selp->dl_qos_type = DL_QOS_CL_SEL1;
2732760Sdg199075 	selp->dl_trans_delay = DL_UNKNOWN;
2742760Sdg199075 	selp->dl_protection = DL_UNKNOWN;
2752760Sdg199075 	selp->dl_residual_error = DL_UNKNOWN;
2762760Sdg199075 
2772760Sdg199075 	/*
2782760Sdg199075 	 * Specify the current priority (which can be changed by
2792760Sdg199075 	 * the DL_UDQOS_REQ primitive).
2802760Sdg199075 	 */
2812760Sdg199075 	selp->dl_priority = dsp->ds_pri;
2820Sstevel@tonic-gate 
2830Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
2840Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
2850Sstevel@tonic-gate 		/*
2860Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
2870Sstevel@tonic-gate 		 * DLSAP address.
2880Sstevel@tonic-gate 		 */
2890Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
2902311Sseb 		if (addr_length > 0)
291*8275SEric Cheng 			mac_unicast_primary_get(dsp->ds_mh, addr);
292*8275SEric Cheng 
2930Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
2940Sstevel@tonic-gate 	}
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate done:
2970Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
2980Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
2990Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3000Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3010Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3020Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3030Sstevel@tonic-gate 
304269Sericheng 	qreply(q, mp);
305269Sericheng }
306269Sericheng 
307269Sericheng /*
308269Sericheng  * DL_ATTACH_REQ
309269Sericheng  */
310*8275SEric Cheng static void
311*8275SEric Cheng proto_attach_req(dld_str_t *dsp, mblk_t *mp)
312269Sericheng {
313*8275SEric Cheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)mp->b_rptr;
314269Sericheng 	int		err = 0;
315269Sericheng 	t_uscalar_t	dl_err;
316269Sericheng 	queue_t		*q = dsp->ds_wq;
317269Sericheng 
318269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
319269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
320269Sericheng 		dl_err = DL_BADPRIM;
321269Sericheng 		goto failed;
322269Sericheng 	}
323269Sericheng 
324269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
325269Sericheng 		dl_err = DL_OUTSTATE;
326269Sericheng 		goto failed;
327269Sericheng 	}
328269Sericheng 
329269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
330269Sericheng 
331269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
332269Sericheng 	if (err != 0) {
333269Sericheng 		switch (err) {
334269Sericheng 		case ENOENT:
335269Sericheng 			dl_err = DL_BADPPA;
336269Sericheng 			err = 0;
337269Sericheng 			break;
338269Sericheng 		default:
339269Sericheng 			dl_err = DL_SYSERR;
340269Sericheng 			break;
341269Sericheng 		}
342269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
343269Sericheng 		goto failed;
344269Sericheng 	}
345269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
346*8275SEric Cheng 	dlokack(q, mp, DL_ATTACH_REQ);
347*8275SEric Cheng 	return;
348269Sericheng 
349269Sericheng failed:
350269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
3510Sstevel@tonic-gate }
3520Sstevel@tonic-gate 
353*8275SEric Cheng /*
354*8275SEric Cheng  * DL_DETACH_REQ
355*8275SEric Cheng  */
356*8275SEric Cheng static void
357*8275SEric Cheng proto_detach_req(dld_str_t *dsp, mblk_t *mp)
3580Sstevel@tonic-gate {
359269Sericheng 	queue_t		*q = dsp->ds_wq;
360269Sericheng 	t_uscalar_t	dl_err;
3610Sstevel@tonic-gate 
362269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
363269Sericheng 		dl_err = DL_BADPRIM;
364269Sericheng 		goto failed;
365269Sericheng 	}
3660Sstevel@tonic-gate 
367269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
368269Sericheng 		dl_err = DL_OUTSTATE;
369269Sericheng 		goto failed;
3700Sstevel@tonic-gate 	}
3710Sstevel@tonic-gate 
372269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
373269Sericheng 		dl_err = DL_BADPRIM;
374269Sericheng 		goto failed;
375269Sericheng 	}
376269Sericheng 
377*8275SEric Cheng 	ASSERT(dsp->ds_datathr_cnt == 0);
378269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
379*8275SEric Cheng 
3805895Syz147064 	dld_str_detach(dsp);
3815895Syz147064 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
382*8275SEric Cheng 	return;
383*8275SEric Cheng 
384269Sericheng failed:
385269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
3860Sstevel@tonic-gate }
3870Sstevel@tonic-gate 
3880Sstevel@tonic-gate /*
389269Sericheng  * DL_BIND_REQ
3900Sstevel@tonic-gate  */
391*8275SEric Cheng static void
392*8275SEric Cheng proto_bind_req(dld_str_t *dsp, mblk_t *mp)
3930Sstevel@tonic-gate {
394*8275SEric Cheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)mp->b_rptr;
395269Sericheng 	int		err = 0;
3963037Syz147064 	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
3973037Syz147064 	uint_t		dlsap_addr_length;
398269Sericheng 	t_uscalar_t	dl_err;
399269Sericheng 	t_scalar_t	sap;
400269Sericheng 	queue_t		*q = dsp->ds_wq;
401*8275SEric Cheng 	mac_perim_handle_t	mph;
402*8275SEric Cheng 	void		*mdip;
403*8275SEric Cheng 	int32_t		intr_cpu;
404269Sericheng 
405269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
406269Sericheng 		dl_err = DL_BADPRIM;
407269Sericheng 		goto failed;
408269Sericheng 	}
409269Sericheng 
410269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
411269Sericheng 		dl_err = DL_NOAUTO;
412269Sericheng 		goto failed;
413269Sericheng 	}
414269Sericheng 
415269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
416269Sericheng 		dl_err = DL_UNSUPPORTED;
417269Sericheng 		goto failed;
418269Sericheng 	}
419269Sericheng 
420269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
421269Sericheng 		dl_err = DL_OUTSTATE;
422269Sericheng 		goto failed;
423269Sericheng 	}
4240Sstevel@tonic-gate 
425*8275SEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
426*8275SEric Cheng 
427269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
428*8275SEric Cheng 	    ((err = dls_active_set(dsp)) != 0)) {
429269Sericheng 		dl_err = DL_SYSERR;
430*8275SEric Cheng 		goto failed2;
431269Sericheng 	}
432269Sericheng 
433*8275SEric Cheng 	dsp->ds_dlstate = DL_BIND_PENDING;
434269Sericheng 	/*
435269Sericheng 	 * Set the receive callback.
436269Sericheng 	 */
437*8275SEric Cheng 	dls_rx_set(dsp, (dsp->ds_mode == DLD_RAW) ?
438269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
4390Sstevel@tonic-gate 
440269Sericheng 	/*
441269Sericheng 	 * Bind the channel such that it can receive packets.
442269Sericheng 	 */
4435895Syz147064 	sap = dlp->dl_sap;
444*8275SEric Cheng 	err = dls_bind(dsp, sap);
445269Sericheng 	if (err != 0) {
446269Sericheng 		switch (err) {
447269Sericheng 		case EINVAL:
448269Sericheng 			dl_err = DL_BADADDR;
449269Sericheng 			err = 0;
450269Sericheng 			break;
451269Sericheng 		default:
452269Sericheng 			dl_err = DL_SYSERR;
453269Sericheng 			break;
454269Sericheng 		}
4555895Syz147064 
456*8275SEric Cheng 		dsp->ds_dlstate = DL_UNBOUND;
457269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
458*8275SEric Cheng 			dls_active_clear(dsp);
459*8275SEric Cheng 		goto failed2;
460*8275SEric Cheng 	}
461269Sericheng 
462*8275SEric Cheng 	intr_cpu = mac_client_intr_cpu(dsp->ds_mch);
463*8275SEric Cheng 	mdip = mac_get_devinfo(dsp->ds_mh);
464*8275SEric Cheng 	mac_perim_exit(mph);
465*8275SEric Cheng 
466*8275SEric Cheng 	/*
467*8275SEric Cheng 	 * We do this after we get out of the perim to avoid deadlocks
468*8275SEric Cheng 	 * etc. since part of mac_client_retarget_intr is to walk the
469*8275SEric Cheng 	 * device tree in order to find and retarget the interrupts.
470*8275SEric Cheng 	 */
471*8275SEric Cheng 	mac_client_set_intr_cpu(mdip, dsp->ds_mch, intr_cpu);
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 	/*
4740Sstevel@tonic-gate 	 * Copy in MAC address.
4750Sstevel@tonic-gate 	 */
4763037Syz147064 	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
477*8275SEric Cheng 	mac_unicast_primary_get(dsp->ds_mh, dlsap_addr);
4780Sstevel@tonic-gate 
4790Sstevel@tonic-gate 	/*
4803037Syz147064 	 * Copy in the SAP.
4810Sstevel@tonic-gate 	 */
4825895Syz147064 	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = sap;
4833037Syz147064 	dlsap_addr_length += sizeof (uint16_t);
4840Sstevel@tonic-gate 
4850Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
486269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
487269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
488269Sericheng 
4893037Syz147064 	dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0);
490*8275SEric Cheng 	return;
491*8275SEric Cheng 
492*8275SEric Cheng failed2:
493*8275SEric Cheng 	mac_perim_exit(mph);
4940Sstevel@tonic-gate failed:
495269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
4960Sstevel@tonic-gate }
4970Sstevel@tonic-gate 
4980Sstevel@tonic-gate /*
499269Sericheng  * DL_UNBIND_REQ
5000Sstevel@tonic-gate  */
501*8275SEric Cheng static void
502*8275SEric Cheng proto_unbind_req(dld_str_t *dsp, mblk_t *mp)
5030Sstevel@tonic-gate {
5045895Syz147064 	queue_t		*q = dsp->ds_wq;
5055895Syz147064 	t_uscalar_t	dl_err;
506*8275SEric Cheng 	mac_perim_handle_t	mph;
507269Sericheng 
5085895Syz147064 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
5095895Syz147064 		dl_err = DL_BADPRIM;
5105895Syz147064 		goto failed;
5115895Syz147064 	}
5125895Syz147064 
5135895Syz147064 	if (dsp->ds_dlstate != DL_IDLE) {
5145895Syz147064 		dl_err = DL_OUTSTATE;
5155895Syz147064 		goto failed;
5165895Syz147064 	}
517269Sericheng 
518*8275SEric Cheng 	mutex_enter(&dsp->ds_lock);
519*8275SEric Cheng 	while (dsp->ds_datathr_cnt != 0)
520*8275SEric Cheng 		cv_wait(&dsp->ds_datathr_cv, &dsp->ds_lock);
521269Sericheng 
522*8275SEric Cheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
523*8275SEric Cheng 	mutex_exit(&dsp->ds_lock);
524*8275SEric Cheng 
525*8275SEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
526269Sericheng 	/*
527269Sericheng 	 * Unbind the channel to stop packets being received.
528269Sericheng 	 */
529*8275SEric Cheng 	if (dls_unbind(dsp) != 0) {
530*8275SEric Cheng 		dl_err = DL_OUTSTATE;
531*8275SEric Cheng 		mac_perim_exit(mph);
532*8275SEric Cheng 		goto failed;
533*8275SEric Cheng 	}
5345895Syz147064 
5355895Syz147064 	/*
536269Sericheng 	 * Disable polling mode, if it is enabled.
537269Sericheng 	 */
538*8275SEric Cheng 	(void) dld_capab_poll_disable(dsp, NULL);
5395895Syz147064 
5405895Syz147064 	/*
5413115Syl150051 	 * Clear LSO flags.
5423115Syl150051 	 */
5433115Syl150051 	dsp->ds_lso = B_FALSE;
5443115Syl150051 	dsp->ds_lso_max = 0;
5453115Syl150051 
5463115Syl150051 	/*
547*8275SEric Cheng 	 * Clear the receive callback.
548*8275SEric Cheng 	 */
549*8275SEric Cheng 	dls_rx_set(dsp, NULL, NULL);
550*8275SEric Cheng 	dsp->ds_direct = B_FALSE;
551*8275SEric Cheng 
552*8275SEric Cheng 	/*
553269Sericheng 	 * Set the mode back to the default (unitdata).
554269Sericheng 	 */
555269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
5561353Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
5571353Sericheng 
558*8275SEric Cheng 	mac_perim_exit(mph);
559*8275SEric Cheng 	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
560*8275SEric Cheng 	return;
561269Sericheng failed:
562269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
5630Sstevel@tonic-gate }
5640Sstevel@tonic-gate 
5650Sstevel@tonic-gate /*
566269Sericheng  * DL_PROMISCON_REQ
5670Sstevel@tonic-gate  */
568*8275SEric Cheng static void
569*8275SEric Cheng proto_promiscon_req(dld_str_t *dsp, mblk_t *mp)
5700Sstevel@tonic-gate {
571*8275SEric Cheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)mp->b_rptr;
572269Sericheng 	int		err = 0;
573269Sericheng 	t_uscalar_t	dl_err;
574*8275SEric Cheng 	uint32_t	promisc_saved;
575269Sericheng 	queue_t		*q = dsp->ds_wq;
576*8275SEric Cheng 	mac_perim_handle_t	mph;
577269Sericheng 
578269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
579269Sericheng 		dl_err = DL_BADPRIM;
580269Sericheng 		goto failed;
581269Sericheng 	}
582269Sericheng 
583269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
584269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
585269Sericheng 		dl_err = DL_OUTSTATE;
5860Sstevel@tonic-gate 		goto failed;
587269Sericheng 	}
5880Sstevel@tonic-gate 
589*8275SEric Cheng 	promisc_saved = dsp->ds_promisc;
590269Sericheng 	switch (dlp->dl_level) {
591269Sericheng 	case DL_PROMISC_SAP:
592*8275SEric Cheng 		dsp->ds_promisc |= DLS_PROMISC_SAP;
593269Sericheng 		break;
594*8275SEric Cheng 
5955895Syz147064 	case DL_PROMISC_MULTI:
596*8275SEric Cheng 		dsp->ds_promisc |= DLS_PROMISC_MULTI;
5975895Syz147064 		break;
598*8275SEric Cheng 
599269Sericheng 	case DL_PROMISC_PHYS:
600*8275SEric Cheng 		dsp->ds_promisc |= DLS_PROMISC_PHYS;
601269Sericheng 		break;
602*8275SEric Cheng 
603269Sericheng 	default:
604269Sericheng 		dl_err = DL_NOTSUPPORTED;
605269Sericheng 		goto failed;
606269Sericheng 	}
6070Sstevel@tonic-gate 
608*8275SEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
609*8275SEric Cheng 
610269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
611*8275SEric Cheng 	    ((err = dls_active_set(dsp)) != 0)) {
612*8275SEric Cheng 		dsp->ds_promisc = promisc_saved;
613269Sericheng 		dl_err = DL_SYSERR;
614*8275SEric Cheng 		goto failed2;
615269Sericheng 	}
616269Sericheng 
617269Sericheng 	/*
618269Sericheng 	 * Adjust channel promiscuity.
619269Sericheng 	 */
620*8275SEric Cheng 	err = dls_promisc(dsp, promisc_saved);
621*8275SEric Cheng 
622269Sericheng 	if (err != 0) {
623269Sericheng 		dl_err = DL_SYSERR;
624*8275SEric Cheng 		dsp->ds_promisc = promisc_saved;
625269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
626*8275SEric Cheng 			dls_active_clear(dsp);
627*8275SEric Cheng 		goto failed2;
628269Sericheng 	}
629269Sericheng 
630*8275SEric Cheng 	mac_perim_exit(mph);
631*8275SEric Cheng 
632269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
633269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
634*8275SEric Cheng 	dlokack(q, mp, DL_PROMISCON_REQ);
635*8275SEric Cheng 	return;
636269Sericheng 
637*8275SEric Cheng failed2:
638*8275SEric Cheng 	mac_perim_exit(mph);
6390Sstevel@tonic-gate failed:
640269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
6410Sstevel@tonic-gate }
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate /*
644269Sericheng  * DL_PROMISCOFF_REQ
6450Sstevel@tonic-gate  */
646*8275SEric Cheng static void
647*8275SEric Cheng proto_promiscoff_req(dld_str_t *dsp, mblk_t *mp)
6480Sstevel@tonic-gate {
649*8275SEric Cheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)mp->b_rptr;
650269Sericheng 	int		err = 0;
651269Sericheng 	t_uscalar_t	dl_err;
652*8275SEric Cheng 	uint32_t	promisc_saved;
653269Sericheng 	queue_t		*q = dsp->ds_wq;
654*8275SEric Cheng 	mac_perim_handle_t	mph;
655269Sericheng 
656269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
657269Sericheng 		dl_err = DL_BADPRIM;
6580Sstevel@tonic-gate 		goto failed;
659269Sericheng 	}
6600Sstevel@tonic-gate 
661269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
662269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
663269Sericheng 		dl_err = DL_OUTSTATE;
6640Sstevel@tonic-gate 		goto failed;
665269Sericheng 	}
6660Sstevel@tonic-gate 
667*8275SEric Cheng 	promisc_saved = dsp->ds_promisc;
668269Sericheng 	switch (dlp->dl_level) {
669269Sericheng 	case DL_PROMISC_SAP:
670*8275SEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
671*8275SEric Cheng 			dl_err = DL_NOTENAB;
672*8275SEric Cheng 			goto failed;
673*8275SEric Cheng 		}
674*8275SEric Cheng 		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
6750Sstevel@tonic-gate 		break;
676*8275SEric Cheng 
677269Sericheng 	case DL_PROMISC_MULTI:
678*8275SEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
679*8275SEric Cheng 			dl_err = DL_NOTENAB;
680*8275SEric Cheng 			goto failed;
681*8275SEric Cheng 		}
682*8275SEric Cheng 		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
683269Sericheng 		break;
684*8275SEric Cheng 
685269Sericheng 	case DL_PROMISC_PHYS:
686*8275SEric Cheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
687*8275SEric Cheng 			dl_err = DL_NOTENAB;
688*8275SEric Cheng 			goto failed;
689*8275SEric Cheng 		}
690*8275SEric Cheng 		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
6910Sstevel@tonic-gate 		break;
692*8275SEric Cheng 
6930Sstevel@tonic-gate 	default:
694269Sericheng 		dl_err = DL_NOTSUPPORTED;
695269Sericheng 		goto failed;
696269Sericheng 	}
697269Sericheng 
698*8275SEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
699*8275SEric Cheng 	/*
700*8275SEric Cheng 	 * Adjust channel promiscuity.
701*8275SEric Cheng 	 */
702*8275SEric Cheng 	err = dls_promisc(dsp, promisc_saved);
703*8275SEric Cheng 	mac_perim_exit(mph);
7045895Syz147064 
705269Sericheng 	if (err != 0) {
7060Sstevel@tonic-gate 		dl_err = DL_SYSERR;
707269Sericheng 		goto failed;
708269Sericheng 	}
709269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
710*8275SEric Cheng 	return;
711269Sericheng failed:
712269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
713269Sericheng }
714269Sericheng 
715269Sericheng /*
716269Sericheng  * DL_ENABMULTI_REQ
717269Sericheng  */
718*8275SEric Cheng static void
719*8275SEric Cheng proto_enabmulti_req(dld_str_t *dsp, mblk_t *mp)
720269Sericheng {
721*8275SEric Cheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)mp->b_rptr;
722269Sericheng 	int		err = 0;
723269Sericheng 	t_uscalar_t	dl_err;
724269Sericheng 	queue_t		*q = dsp->ds_wq;
725*8275SEric Cheng 	mac_perim_handle_t	mph;
726269Sericheng 
727269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
728269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
729269Sericheng 		dl_err = DL_OUTSTATE;
730269Sericheng 		goto failed;
731269Sericheng 	}
732269Sericheng 
733269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
734269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
735269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
736269Sericheng 		dl_err = DL_BADPRIM;
737269Sericheng 		goto failed;
738269Sericheng 	}
739269Sericheng 
740*8275SEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
741*8275SEric Cheng 
742269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
743*8275SEric Cheng 	    ((err = dls_active_set(dsp)) != 0)) {
744269Sericheng 		dl_err = DL_SYSERR;
745*8275SEric Cheng 		goto failed2;
7460Sstevel@tonic-gate 	}
7470Sstevel@tonic-gate 
748*8275SEric Cheng 	err = dls_multicst_add(dsp, mp->b_rptr + dlp->dl_addr_offset);
749*8275SEric Cheng 
750269Sericheng 	if (err != 0) {
751269Sericheng 		switch (err) {
752269Sericheng 		case EINVAL:
753269Sericheng 			dl_err = DL_BADADDR;
754269Sericheng 			err = 0;
755269Sericheng 			break;
756269Sericheng 		case ENOSPC:
757269Sericheng 			dl_err = DL_TOOMANY;
758269Sericheng 			err = 0;
759269Sericheng 			break;
760269Sericheng 		default:
761269Sericheng 			dl_err = DL_SYSERR;
762269Sericheng 			break;
763269Sericheng 		}
764*8275SEric Cheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
765*8275SEric Cheng 			dls_active_clear(dsp);
7665895Syz147064 
767*8275SEric Cheng 		goto failed2;
768269Sericheng 	}
769269Sericheng 
770*8275SEric Cheng 	mac_perim_exit(mph);
771*8275SEric Cheng 
772269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
773269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
774*8275SEric Cheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
775*8275SEric Cheng 	return;
776269Sericheng 
777*8275SEric Cheng failed2:
778*8275SEric Cheng 	mac_perim_exit(mph);
779269Sericheng failed:
780269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
781269Sericheng }
782269Sericheng 
783269Sericheng /*
784269Sericheng  * DL_DISABMULTI_REQ
785269Sericheng  */
786*8275SEric Cheng static void
787*8275SEric Cheng proto_disabmulti_req(dld_str_t *dsp, mblk_t *mp)
788269Sericheng {
789*8275SEric Cheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)mp->b_rptr;
790269Sericheng 	int		err = 0;
791269Sericheng 	t_uscalar_t	dl_err;
792269Sericheng 	queue_t		*q = dsp->ds_wq;
793*8275SEric Cheng 	mac_perim_handle_t	mph;
794269Sericheng 
795269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
796269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
797269Sericheng 		dl_err = DL_OUTSTATE;
798269Sericheng 		goto failed;
799269Sericheng 	}
800269Sericheng 
801269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
802269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
803269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
804269Sericheng 		dl_err = DL_BADPRIM;
805269Sericheng 		goto failed;
806269Sericheng 	}
807269Sericheng 
808*8275SEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
809*8275SEric Cheng 	err = dls_multicst_remove(dsp, mp->b_rptr + dlp->dl_addr_offset);
810*8275SEric Cheng 	mac_perim_exit(mph);
811*8275SEric Cheng 
812269Sericheng 	if (err != 0) {
813*8275SEric Cheng 	switch (err) {
814269Sericheng 		case EINVAL:
815269Sericheng 			dl_err = DL_BADADDR;
816269Sericheng 			err = 0;
817269Sericheng 			break;
818*8275SEric Cheng 
819269Sericheng 		case ENOENT:
820269Sericheng 			dl_err = DL_NOTENAB;
821269Sericheng 			err = 0;
822269Sericheng 			break;
823*8275SEric Cheng 
824269Sericheng 		default:
825269Sericheng 			dl_err = DL_SYSERR;
826269Sericheng 			break;
827269Sericheng 		}
828269Sericheng 		goto failed;
829269Sericheng 	}
830269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
831*8275SEric Cheng 	return;
832269Sericheng failed:
833269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
8340Sstevel@tonic-gate }
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate /*
837269Sericheng  * DL_PHYS_ADDR_REQ
8380Sstevel@tonic-gate  */
839*8275SEric Cheng static void
840*8275SEric Cheng proto_physaddr_req(dld_str_t *dsp, mblk_t *mp)
8410Sstevel@tonic-gate {
842*8275SEric Cheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)mp->b_rptr;
843269Sericheng 	queue_t		*q = dsp->ds_wq;
844269Sericheng 	t_uscalar_t	dl_err;
845269Sericheng 	char		*addr;
846269Sericheng 	uint_t		addr_length;
847269Sericheng 
848269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
849269Sericheng 		dl_err = DL_BADPRIM;
850269Sericheng 		goto failed;
851269Sericheng 	}
852269Sericheng 
853269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
854269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
855269Sericheng 		dl_err = DL_OUTSTATE;
856269Sericheng 		goto failed;
857269Sericheng 	}
8580Sstevel@tonic-gate 
859269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
860269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
861269Sericheng 		dl_err = DL_UNSUPPORTED;
8620Sstevel@tonic-gate 		goto failed;
863269Sericheng 	}
8640Sstevel@tonic-gate 
865269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
8666353Sdr146992 	if (addr_length > 0) {
867*8275SEric Cheng 		addr = kmem_alloc(addr_length, KM_SLEEP);
868*8275SEric Cheng 		if (dlp->dl_addr_type == DL_CURR_PHYS_ADDR)
869*8275SEric Cheng 			mac_unicast_primary_get(dsp->ds_mh, (uint8_t *)addr);
870*8275SEric Cheng 		else
871*8275SEric Cheng 			bcopy(dsp->ds_mip->mi_unicst_addr, addr, addr_length);
8720Sstevel@tonic-gate 
8736353Sdr146992 		dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
8746353Sdr146992 		kmem_free(addr, addr_length);
8756353Sdr146992 	} else {
8766353Sdr146992 		dlphysaddrack(q, mp, NULL, 0);
8776353Sdr146992 	}
878*8275SEric Cheng 	return;
8790Sstevel@tonic-gate failed:
880269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
881269Sericheng }
8820Sstevel@tonic-gate 
883269Sericheng /*
884269Sericheng  * DL_SET_PHYS_ADDR_REQ
885269Sericheng  */
886*8275SEric Cheng static void
887*8275SEric Cheng proto_setphysaddr_req(dld_str_t *dsp, mblk_t *mp)
888269Sericheng {
889*8275SEric Cheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)mp->b_rptr;
890269Sericheng 	int		err = 0;
891269Sericheng 	t_uscalar_t	dl_err;
892269Sericheng 	queue_t		*q = dsp->ds_wq;
893*8275SEric Cheng 	mac_perim_handle_t	mph;
8940Sstevel@tonic-gate 
895269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
896269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
897269Sericheng 		dl_err = DL_OUTSTATE;
898269Sericheng 		goto failed;
899269Sericheng 	}
900269Sericheng 
901269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
902269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
903269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
904269Sericheng 		dl_err = DL_BADPRIM;
905269Sericheng 		goto failed;
9060Sstevel@tonic-gate 	}
9070Sstevel@tonic-gate 
908*8275SEric Cheng 	mac_perim_enter_by_mh(dsp->ds_mh, &mph);
909*8275SEric Cheng 
910269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
911*8275SEric Cheng 	    ((err = dls_active_set(dsp)) != 0)) {
912269Sericheng 		dl_err = DL_SYSERR;
913*8275SEric Cheng 		goto failed2;
914269Sericheng 	}
915269Sericheng 
916*8275SEric Cheng 	err = mac_unicast_primary_set(dsp->ds_mh,
917*8275SEric Cheng 	    mp->b_rptr + dlp->dl_addr_offset);
918269Sericheng 	if (err != 0) {
919269Sericheng 		switch (err) {
920269Sericheng 		case EINVAL:
921269Sericheng 			dl_err = DL_BADADDR;
922269Sericheng 			err = 0;
923269Sericheng 			break;
924269Sericheng 
925269Sericheng 		default:
926269Sericheng 			dl_err = DL_SYSERR;
927269Sericheng 			break;
928269Sericheng 		}
929*8275SEric Cheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
930*8275SEric Cheng 			dls_active_clear(dsp);
9315895Syz147064 
932*8275SEric Cheng 		goto failed2;
933269Sericheng 
934269Sericheng 	}
9355895Syz147064 
936*8275SEric Cheng 	mac_perim_exit(mph);
937*8275SEric Cheng 
938269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
939269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
940*8275SEric Cheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
941*8275SEric Cheng 	return;
942269Sericheng 
943*8275SEric Cheng failed2:
944*8275SEric Cheng 	mac_perim_exit(mph);
945269Sericheng failed:
946269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
947269Sericheng }
948269Sericheng 
949269Sericheng /*
950269Sericheng  * DL_UDQOS_REQ
951269Sericheng  */
952*8275SEric Cheng static void
953*8275SEric Cheng proto_udqos_req(dld_str_t *dsp, mblk_t *mp)
954269Sericheng {
955*8275SEric Cheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)mp->b_rptr;
956269Sericheng 	dl_qos_cl_sel1_t *selp;
957269Sericheng 	int		off, len;
958269Sericheng 	t_uscalar_t	dl_err;
959269Sericheng 	queue_t		*q = dsp->ds_wq;
960269Sericheng 
961269Sericheng 	off = dlp->dl_qos_offset;
962269Sericheng 	len = dlp->dl_qos_length;
963269Sericheng 
964269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
965269Sericheng 		dl_err = DL_BADPRIM;
966269Sericheng 		goto failed;
967269Sericheng 	}
968269Sericheng 
969269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
970269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
971269Sericheng 		dl_err = DL_BADQOSTYPE;
972269Sericheng 		goto failed;
973269Sericheng 	}
974269Sericheng 
9752760Sdg199075 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
976269Sericheng 	    selp->dl_priority < 0) {
977269Sericheng 		dl_err = DL_BADQOSPARAM;
978269Sericheng 		goto failed;
979269Sericheng 	}
980269Sericheng 
9815895Syz147064 	dsp->ds_pri = selp->dl_priority;
982269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
983*8275SEric Cheng 	return;
984269Sericheng failed:
985269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
9860Sstevel@tonic-gate }
9870Sstevel@tonic-gate 
9881184Skrgopi static boolean_t
9891184Skrgopi check_ip_above(queue_t *q)
9901184Skrgopi {
9911184Skrgopi 	queue_t		*next_q;
9921184Skrgopi 	boolean_t	ret = B_TRUE;
9931184Skrgopi 
9941184Skrgopi 	claimstr(q);
9951184Skrgopi 	next_q = q->q_next;
9961184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
9971184Skrgopi 		ret = B_FALSE;
9981184Skrgopi 	releasestr(q);
9991184Skrgopi 	return (ret);
10001184Skrgopi }
10011184Skrgopi 
10020Sstevel@tonic-gate /*
1003269Sericheng  * DL_CAPABILITY_REQ
10040Sstevel@tonic-gate  */
1005*8275SEric Cheng static void
1006*8275SEric Cheng proto_capability_req(dld_str_t *dsp, mblk_t *mp)
10070Sstevel@tonic-gate {
1008*8275SEric Cheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)mp->b_rptr;
1009269Sericheng 	dl_capability_sub_t *sp;
1010269Sericheng 	size_t		size, len;
1011269Sericheng 	offset_t	off, end;
1012269Sericheng 	t_uscalar_t	dl_err;
1013269Sericheng 	queue_t		*q = dsp->ds_wq;
1014269Sericheng 
1015269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1016269Sericheng 		dl_err = DL_BADPRIM;
1017269Sericheng 		goto failed;
1018269Sericheng 	}
1019269Sericheng 
1020269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1021269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1022269Sericheng 		dl_err = DL_OUTSTATE;
1023269Sericheng 		goto failed;
1024269Sericheng 	}
1025269Sericheng 
1026269Sericheng 	/*
1027269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1028269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1029269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1030269Sericheng 	 */
1031269Sericheng 	if (dlp->dl_sub_length == 0) {
1032*8275SEric Cheng 		proto_capability_advertise(dsp, mp);
1033*8275SEric Cheng 		return;
1034269Sericheng 	}
1035269Sericheng 
1036269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1037269Sericheng 		dl_err = DL_BADPRIM;
1038269Sericheng 		goto failed;
1039269Sericheng 	}
1040269Sericheng 
1041269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1042269Sericheng 
1043269Sericheng 	off = dlp->dl_sub_offset;
1044269Sericheng 	len = dlp->dl_sub_length;
10450Sstevel@tonic-gate 
10460Sstevel@tonic-gate 	/*
1047269Sericheng 	 * Walk the list of capabilities to be enabled.
10480Sstevel@tonic-gate 	 */
1049269Sericheng 	for (end = off + len; off < end; ) {
1050269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1051269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1052269Sericheng 
1053269Sericheng 		if (off + size > end ||
1054269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1055269Sericheng 			dl_err = DL_BADPRIM;
1056269Sericheng 			goto failed;
1057269Sericheng 		}
1058269Sericheng 
1059269Sericheng 		switch (sp->dl_cap) {
1060269Sericheng 		/*
1061269Sericheng 		 * TCP/IP checksum offload to hardware.
1062269Sericheng 		 */
1063269Sericheng 		case DL_CAPAB_HCKSUM: {
1064269Sericheng 			dl_capab_hcksum_t *hcksump;
1065269Sericheng 			dl_capab_hcksum_t hcksum;
1066269Sericheng 
1067269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1068269Sericheng 			/*
1069269Sericheng 			 * Copy for alignment.
1070269Sericheng 			 */
1071269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1072269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1073269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1074269Sericheng 			break;
1075269Sericheng 		}
1076269Sericheng 
1077*8275SEric Cheng 		case DL_CAPAB_DLD: {
1078*8275SEric Cheng 			dl_capab_dld_t	*dldp;
1079*8275SEric Cheng 			dl_capab_dld_t	dld;
10803115Syl150051 
1081*8275SEric Cheng 			dldp = (dl_capab_dld_t *)&sp[1];
1082269Sericheng 			/*
1083269Sericheng 			 * Copy for alignment.
1084269Sericheng 			 */
1085*8275SEric Cheng 			bcopy(dldp, &dld, sizeof (dl_capab_dld_t));
1086*8275SEric Cheng 			dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
1087*8275SEric Cheng 			bcopy(&dld, dldp, sizeof (dl_capab_dld_t));
1088269Sericheng 			break;
1089269Sericheng 		}
1090269Sericheng 		default:
1091269Sericheng 			break;
1092269Sericheng 		}
1093269Sericheng 		off += size;
1094269Sericheng 	}
1095269Sericheng 	qreply(q, mp);
1096*8275SEric Cheng 	return;
1097269Sericheng failed:
1098269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
10990Sstevel@tonic-gate }
11000Sstevel@tonic-gate 
11010Sstevel@tonic-gate /*
1102269Sericheng  * DL_NOTIFY_REQ
11030Sstevel@tonic-gate  */
1104*8275SEric Cheng static void
1105*8275SEric Cheng proto_notify_req(dld_str_t *dsp, mblk_t *mp)
11060Sstevel@tonic-gate {
1107*8275SEric Cheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)mp->b_rptr;
1108269Sericheng 	t_uscalar_t	dl_err;
1109269Sericheng 	queue_t		*q = dsp->ds_wq;
1110269Sericheng 	uint_t		note =
1111269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1112269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1113269Sericheng 	    DL_NOTE_PHYS_ADDR |
1114269Sericheng 	    DL_NOTE_LINK_UP |
1115269Sericheng 	    DL_NOTE_LINK_DOWN |
11162311Sseb 	    DL_NOTE_CAPAB_RENEG |
11172311Sseb 	    DL_NOTE_SPEED;
11180Sstevel@tonic-gate 
1119269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1120269Sericheng 		dl_err = DL_BADPRIM;
1121269Sericheng 		goto failed;
1122269Sericheng 	}
11230Sstevel@tonic-gate 
1124269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1125269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1126269Sericheng 		dl_err = DL_OUTSTATE;
1127269Sericheng 		goto failed;
11280Sstevel@tonic-gate 	}
11290Sstevel@tonic-gate 
11305895Syz147064 	note &= ~(mac_no_notification(dsp->ds_mh));
11315895Syz147064 
1132269Sericheng 	/*
1133269Sericheng 	 * Cache the notifications that are being enabled.
1134269Sericheng 	 */
1135269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1136269Sericheng 	/*
1137269Sericheng 	 * The ACK carries all notifications regardless of which set is
1138269Sericheng 	 * being enabled.
1139269Sericheng 	 */
1140269Sericheng 	dlnotifyack(q, mp, note);
1141269Sericheng 
1142269Sericheng 	/*
1143*8275SEric Cheng 	 * Generate DL_NOTIFY_IND messages for each enabled notification.
1144269Sericheng 	 */
1145269Sericheng 	if (dsp->ds_notifications != 0) {
1146269Sericheng 		dld_str_notify_ind(dsp);
1147269Sericheng 	}
1148*8275SEric Cheng 	return;
1149269Sericheng failed:
1150269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
11510Sstevel@tonic-gate }
11520Sstevel@tonic-gate 
11530Sstevel@tonic-gate /*
1154*8275SEric Cheng  * DL_UINTDATA_REQ
11550Sstevel@tonic-gate  */
11565895Syz147064 void
1157*8275SEric Cheng proto_unitdata_req(dld_str_t *dsp, mblk_t *mp)
11580Sstevel@tonic-gate {
1159269Sericheng 	queue_t			*q = dsp->ds_wq;
11605895Syz147064 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)mp->b_rptr;
1161269Sericheng 	off_t			off;
1162269Sericheng 	size_t			len, size;
1163269Sericheng 	const uint8_t		*addr;
1164269Sericheng 	uint16_t		sap;
1165269Sericheng 	uint_t			addr_length;
11662311Sseb 	mblk_t			*bp, *payload;
1167269Sericheng 	uint32_t		start, stuff, end, value, flags;
1168269Sericheng 	t_uscalar_t		dl_err;
11695903Ssowmini 	uint_t			max_sdu;
1170269Sericheng 
1171269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1172*8275SEric Cheng 		dlerrorack(q, mp, DL_UNITDATA_REQ, DL_BADPRIM, 0);
1173*8275SEric Cheng 		return;
1174269Sericheng 	}
1175269Sericheng 
1176*8275SEric Cheng 	mutex_enter(&dsp->ds_lock);
1177*8275SEric Cheng 	if (dsp->ds_dlstate != DL_IDLE) {
1178*8275SEric Cheng 		mutex_exit(&dsp->ds_lock);
1179*8275SEric Cheng 		dlerrorack(q, mp, DL_UNITDATA_REQ, DL_OUTSTATE, 0);
1180*8275SEric Cheng 		return;
1181*8275SEric Cheng 	}
1182*8275SEric Cheng 	DLD_DATATHR_INC(dsp);
1183*8275SEric Cheng 	mutex_exit(&dsp->ds_lock);
1184*8275SEric Cheng 
1185269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1186269Sericheng 
1187269Sericheng 	off = dlp->dl_dest_addr_offset;
1188269Sericheng 	len = dlp->dl_dest_addr_length;
1189269Sericheng 
1190269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1191269Sericheng 		dl_err = DL_BADPRIM;
1192269Sericheng 		goto failed;
1193269Sericheng 	}
1194269Sericheng 
1195269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1196269Sericheng 		dl_err = DL_BADADDR;
1197269Sericheng 		goto failed;
1198269Sericheng 	}
1199269Sericheng 
1200269Sericheng 	addr = mp->b_rptr + off;
1201269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1202269Sericheng 
1203269Sericheng 	/*
1204269Sericheng 	 * Check the length of the packet and the block types.
1205269Sericheng 	 */
1206269Sericheng 	size = 0;
12072311Sseb 	payload = mp->b_cont;
12082311Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1209269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1210269Sericheng 			goto baddata;
1211269Sericheng 
1212269Sericheng 		size += MBLKL(bp);
1213269Sericheng 	}
1214269Sericheng 
12155903Ssowmini 	mac_sdu_get(dsp->ds_mh, NULL, &max_sdu);
12165903Ssowmini 	if (size > max_sdu)
1217269Sericheng 		goto baddata;
1218269Sericheng 
1219269Sericheng 	/*
1220269Sericheng 	 * Build a packet header.
1221269Sericheng 	 */
1222*8275SEric Cheng 	if ((bp = dls_header(dsp, addr, sap, dlp->dl_priority.dl_max,
12232760Sdg199075 	    &payload)) == NULL) {
1224269Sericheng 		dl_err = DL_BADADDR;
1225269Sericheng 		goto failed;
1226269Sericheng 	}
1227269Sericheng 
1228269Sericheng 	/*
1229269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1230269Sericheng 	 */
1231269Sericheng 	freeb(mp);
1232269Sericheng 
1233269Sericheng 	/*
1234269Sericheng 	 * Transfer the checksum offload information if it is present.
1235269Sericheng 	 */
12362311Sseb 	hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1237269Sericheng 	    &flags);
12382311Sseb 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1239269Sericheng 
1240269Sericheng 	/*
1241269Sericheng 	 * Link the payload onto the new header.
1242269Sericheng 	 */
1243269Sericheng 	ASSERT(bp->b_cont == NULL);
12442311Sseb 	bp->b_cont = payload;
1245*8275SEric Cheng 
1246*8275SEric Cheng 	/*
1247*8275SEric Cheng 	 * No lock can be held across modules and putnext()'s,
1248*8275SEric Cheng 	 * which can happen here with the call from DLD_TX().
1249*8275SEric Cheng 	 */
1250*8275SEric Cheng 	if (DLD_TX(dsp, bp, 0, 0) != NULL) {
1251*8275SEric Cheng 		/* flow-controlled */
1252*8275SEric Cheng 		DLD_SETQFULL(dsp);
1253*8275SEric Cheng 	}
1254*8275SEric Cheng 	DLD_DATATHR_DCR(dsp);
12555895Syz147064 	return;
1256*8275SEric Cheng 
1257269Sericheng failed:
1258269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1259*8275SEric Cheng 	DLD_DATATHR_DCR(dsp);
12605895Syz147064 	return;
1261269Sericheng 
1262269Sericheng baddata:
1263269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1264*8275SEric Cheng 	DLD_DATATHR_DCR(dsp);
1265269Sericheng }
1266269Sericheng 
1267269Sericheng /*
1268269Sericheng  * DL_PASSIVE_REQ
1269269Sericheng  */
1270*8275SEric Cheng static void
1271*8275SEric Cheng proto_passive_req(dld_str_t *dsp, mblk_t *mp)
1272269Sericheng {
1273269Sericheng 	t_uscalar_t dl_err;
1274269Sericheng 
12755895Syz147064 	/*
1276269Sericheng 	 * If we've already become active by issuing an active primitive,
1277269Sericheng 	 * then it's too late to try to become passive.
1278269Sericheng 	 */
1279269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1280269Sericheng 		dl_err = DL_OUTSTATE;
1281269Sericheng 		goto failed;
1282269Sericheng 	}
1283269Sericheng 
1284269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1285269Sericheng 		dl_err = DL_BADPRIM;
1286269Sericheng 		goto failed;
1287269Sericheng 	}
1288269Sericheng 
1289269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1290269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1291*8275SEric Cheng 	return;
1292269Sericheng failed:
1293269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1294269Sericheng }
1295269Sericheng 
1296*8275SEric Cheng 
1297269Sericheng /*
1298269Sericheng  * Catch-all handler.
1299269Sericheng  */
1300*8275SEric Cheng static void
1301*8275SEric Cheng proto_req(dld_str_t *dsp, mblk_t *mp)
1302*8275SEric Cheng {
1303*8275SEric Cheng 	union DL_primitives	*dlp = (union DL_primitives *)mp->b_rptr;
1304*8275SEric Cheng 
1305*8275SEric Cheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1306*8275SEric Cheng }
1307*8275SEric Cheng 
1308*8275SEric Cheng static int
1309*8275SEric Cheng dld_capab_perim(dld_str_t *dsp, void *data, uint_t flags)
1310269Sericheng {
1311*8275SEric Cheng 	switch (flags) {
1312*8275SEric Cheng 	case DLD_ENABLE:
1313*8275SEric Cheng 		mac_perim_enter_by_mh(dsp->ds_mh, (mac_perim_handle_t *)data);
1314*8275SEric Cheng 		return (0);
1315*8275SEric Cheng 
1316*8275SEric Cheng 	case DLD_DISABLE:
1317*8275SEric Cheng 		mac_perim_exit((mac_perim_handle_t)data);
1318*8275SEric Cheng 		return (0);
1319*8275SEric Cheng 
1320*8275SEric Cheng 	case DLD_QUERY:
1321*8275SEric Cheng 		return (mac_perim_held(dsp->ds_mh));
1322*8275SEric Cheng 	}
1323*8275SEric Cheng 	return (0);
13240Sstevel@tonic-gate }
13250Sstevel@tonic-gate 
1326*8275SEric Cheng static int
1327*8275SEric Cheng dld_capab_direct(dld_str_t *dsp, void *data, uint_t flags)
13280Sstevel@tonic-gate {
1329*8275SEric Cheng 	dld_capab_direct_t	*direct = data;
1330*8275SEric Cheng 
1331*8275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
13320Sstevel@tonic-gate 
1333*8275SEric Cheng 	switch (flags) {
1334*8275SEric Cheng 	case DLD_ENABLE:
1335*8275SEric Cheng 		dls_rx_set(dsp, (dls_rx_t)direct->di_rx_cf,
1336*8275SEric Cheng 		    direct->di_rx_ch);
1337*8275SEric Cheng 		/*
1338*8275SEric Cheng 		 * TODO: XXXGopi
1339*8275SEric Cheng 		 *
1340*8275SEric Cheng 		 * Direct pointer to functions in the MAC layer
1341*8275SEric Cheng 		 * should be passed here:
1342*8275SEric Cheng 		 *
1343*8275SEric Cheng 		 * 1) pass mac_tx() and mac_client_handle instead
1344*8275SEric Cheng 		 * of str_mdata_fastpath_put() and dld_str_t. But
1345*8275SEric Cheng 		 * not done presently because of some VLAN
1346*8275SEric Cheng 		 * processing stuff in str_mdata_fastpath_put().
1347*8275SEric Cheng 		 *
1348*8275SEric Cheng 		 * 2) pass a MAC layer callback instead of
1349*8275SEric Cheng 		 * dld_flow_ctl_callb().
1350*8275SEric Cheng 		 */
1351*8275SEric Cheng 		direct->di_tx_df = (uintptr_t)str_mdata_fastpath_put;
1352*8275SEric Cheng 		direct->di_tx_dh = dsp;
1353*8275SEric Cheng 
1354*8275SEric Cheng 		direct->di_tx_cb_df = (uintptr_t)mac_client_tx_notify;
1355*8275SEric Cheng 		direct->di_tx_cb_dh = dsp->ds_mch;
1356*8275SEric Cheng 		dsp->ds_direct = B_TRUE;
1357*8275SEric Cheng 
1358*8275SEric Cheng 		return (0);
1359269Sericheng 
1360*8275SEric Cheng 	case DLD_DISABLE:
1361*8275SEric Cheng 		dls_rx_set(dsp, (dsp->ds_mode == DLD_FASTPATH) ?
1362*8275SEric Cheng 		    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
1363*8275SEric Cheng 		dsp->ds_direct = B_FALSE;
1364*8275SEric Cheng 
1365*8275SEric Cheng 		return (0);
1366*8275SEric Cheng 	}
1367*8275SEric Cheng 	return (ENOTSUP);
1368*8275SEric Cheng }
13690Sstevel@tonic-gate 
1370*8275SEric Cheng /*
1371*8275SEric Cheng  * dld_capab_poll_enable()
1372*8275SEric Cheng  *
1373*8275SEric Cheng  * This function is misnamed. All polling  and fanouts are run out of the
1374*8275SEric Cheng  * lower mac (in case of VNIC and the only mac in case of NICs). The
1375*8275SEric Cheng  * availability of Rx ring and promiscous mode is all taken care between
1376*8275SEric Cheng  * the soft ring set (mac_srs), the Rx ring, and S/W classifier. Any
1377*8275SEric Cheng  * fanout necessary is done by the soft rings that are part of the
1378*8275SEric Cheng  * mac_srs (by default mac_srs sends the packets up via a TCP and
1379*8275SEric Cheng  * non TCP soft ring).
1380*8275SEric Cheng  *
1381*8275SEric Cheng  * The mac_srs (or its associated soft rings) always store the ill_rx_ring
1382*8275SEric Cheng  * (the cookie returned when they registered with IP during plumb) as their
1383*8275SEric Cheng  * 2nd argument which is passed up as mac_resource_handle_t. The upcall
1384*8275SEric Cheng  * function and 1st argument is what the caller registered when they
1385*8275SEric Cheng  * called mac_rx_classify_flow_add() to register the flow. For VNIC,
1386*8275SEric Cheng  * the function is vnic_rx and argument is vnic_t. For regular NIC
1387*8275SEric Cheng  * case, it mac_rx_default and mac_handle_t. As explained above, the
1388*8275SEric Cheng  * mac_srs (or its soft ring) will add the ill_rx_ring (mac_resource_handle_t)
1389*8275SEric Cheng  * from its stored 2nd argument.
1390*8275SEric Cheng  */
1391*8275SEric Cheng static int
1392*8275SEric Cheng dld_capab_poll_enable(dld_str_t *dsp, dld_capab_poll_t *poll)
1393*8275SEric Cheng {
1394*8275SEric Cheng 	if (dsp->ds_polling)
1395*8275SEric Cheng 		return (EINVAL);
1396*8275SEric Cheng 
1397*8275SEric Cheng 	if ((dld_opt & DLD_OPT_NO_POLL) != 0 || dsp->ds_mode == DLD_RAW)
1398*8275SEric Cheng 		return (ENOTSUP);
13990Sstevel@tonic-gate 
14000Sstevel@tonic-gate 	/*
1401*8275SEric Cheng 	 * Enable client polling if and only if DLS bypass is possible.
1402*8275SEric Cheng 	 * Special cases like VLANs need DLS processing in the Rx data path.
1403*8275SEric Cheng 	 * In such a case we can neither allow the client (IP) to directly
1404*8275SEric Cheng 	 * poll the softring (since DLS processing hasn't been done) nor can
1405*8275SEric Cheng 	 * we allow DLS bypass.
14060Sstevel@tonic-gate 	 */
1407*8275SEric Cheng 	if (!mac_rx_bypass_set(dsp->ds_mch, dsp->ds_rx, dsp->ds_rx_arg))
1408*8275SEric Cheng 		return (ENOTSUP);
14090Sstevel@tonic-gate 
14100Sstevel@tonic-gate 	/*
1411*8275SEric Cheng 	 * Register soft ring resources. This will come in handy later if
1412*8275SEric Cheng 	 * the user decides to modify CPU bindings to use more CPUs for the
1413*8275SEric Cheng 	 * device in which case we will switch to fanout using soft rings.
14140Sstevel@tonic-gate 	 */
1415*8275SEric Cheng 	mac_resource_set_common(dsp->ds_mch,
1416*8275SEric Cheng 	    (mac_resource_add_t)poll->poll_ring_add_cf,
1417*8275SEric Cheng 	    (mac_resource_remove_t)poll->poll_ring_remove_cf,
1418*8275SEric Cheng 	    (mac_resource_quiesce_t)poll->poll_ring_quiesce_cf,
1419*8275SEric Cheng 	    (mac_resource_restart_t)poll->poll_ring_restart_cf,
1420*8275SEric Cheng 	    (mac_resource_bind_t)poll->poll_ring_bind_cf,
1421*8275SEric Cheng 	    poll->poll_ring_ch);
1422*8275SEric Cheng 
1423*8275SEric Cheng 	mac_client_poll_enable(dsp->ds_mch);
14240Sstevel@tonic-gate 
14250Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
1426*8275SEric Cheng 	return (0);
1427*8275SEric Cheng }
1428*8275SEric Cheng 
1429*8275SEric Cheng /* ARGSUSED */
1430*8275SEric Cheng static int
1431*8275SEric Cheng dld_capab_poll_disable(dld_str_t *dsp, dld_capab_poll_t *poll)
1432*8275SEric Cheng {
1433*8275SEric Cheng 	if (!dsp->ds_polling)
1434*8275SEric Cheng 		return (EINVAL);
1435*8275SEric Cheng 
1436*8275SEric Cheng 	mac_client_poll_disable(dsp->ds_mch);
1437*8275SEric Cheng 	mac_resource_set(dsp->ds_mch, NULL, NULL);
1438*8275SEric Cheng 
1439*8275SEric Cheng 	dsp->ds_polling = B_FALSE;
1440*8275SEric Cheng 	return (0);
14410Sstevel@tonic-gate }
14420Sstevel@tonic-gate 
1443*8275SEric Cheng static int
1444*8275SEric Cheng dld_capab_poll(dld_str_t *dsp, void *data, uint_t flags)
1445*8275SEric Cheng {
1446*8275SEric Cheng 	dld_capab_poll_t	*poll = data;
1447*8275SEric Cheng 
1448*8275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1449*8275SEric Cheng 
1450*8275SEric Cheng 	switch (flags) {
1451*8275SEric Cheng 	case DLD_ENABLE:
1452*8275SEric Cheng 		return (dld_capab_poll_enable(dsp, poll));
1453*8275SEric Cheng 	case DLD_DISABLE:
1454*8275SEric Cheng 		return (dld_capab_poll_disable(dsp, poll));
1455*8275SEric Cheng 	}
1456*8275SEric Cheng 	return (ENOTSUP);
1457*8275SEric Cheng }
1458*8275SEric Cheng 
1459*8275SEric Cheng static int
1460*8275SEric Cheng dld_capab_lso(dld_str_t *dsp, void *data, uint_t flags)
14611184Skrgopi {
1462*8275SEric Cheng 	dld_capab_lso_t		*lso = data;
1463*8275SEric Cheng 
1464*8275SEric Cheng 	ASSERT(MAC_PERIM_HELD(dsp->ds_mh));
1465*8275SEric Cheng 
1466*8275SEric Cheng 	switch (flags) {
1467*8275SEric Cheng 	case DLD_ENABLE: {
1468*8275SEric Cheng 		mac_capab_lso_t		mac_lso;
14691184Skrgopi 
1470*8275SEric Cheng 		/*
1471*8275SEric Cheng 		 * Check if LSO is supported on this MAC & enable LSO
1472*8275SEric Cheng 		 * accordingly.
1473*8275SEric Cheng 		 */
1474*8275SEric Cheng 		if (mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
1475*8275SEric Cheng 			lso->lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max;
1476*8275SEric Cheng 			lso->lso_flags = 0;
1477*8275SEric Cheng 			/* translate the flag for mac clients */
1478*8275SEric Cheng 			if ((mac_lso.lso_flags & LSO_TX_BASIC_TCP_IPV4) != 0)
1479*8275SEric Cheng 				lso->lso_flags |= DLD_LSO_TX_BASIC_TCP_IPV4;
1480*8275SEric Cheng 			dsp->ds_lso = B_TRUE;
1481*8275SEric Cheng 			dsp->ds_lso_max = lso->lso_max;
1482*8275SEric Cheng 		} else {
1483*8275SEric Cheng 			dsp->ds_lso = B_FALSE;
1484*8275SEric Cheng 			dsp->ds_lso_max = 0;
1485*8275SEric Cheng 			return (ENOTSUP);
1486*8275SEric Cheng 		}
1487*8275SEric Cheng 		return (0);
1488*8275SEric Cheng 	}
1489*8275SEric Cheng 	case DLD_DISABLE: {
1490*8275SEric Cheng 		dsp->ds_lso = B_FALSE;
1491*8275SEric Cheng 		dsp->ds_lso_max = 0;
1492*8275SEric Cheng 		return (0);
1493*8275SEric Cheng 	}
1494*8275SEric Cheng 	}
1495*8275SEric Cheng 	return (ENOTSUP);
1496*8275SEric Cheng }
1497*8275SEric Cheng 
1498*8275SEric Cheng static int
1499*8275SEric Cheng dld_capab(dld_str_t *dsp, uint_t type, void *data, uint_t flags)
1500*8275SEric Cheng {
1501*8275SEric Cheng 	int	err;
15021184Skrgopi 
15031184Skrgopi 	/*
1504*8275SEric Cheng 	 * Don't enable direct callback capabilities unless the caller is
1505*8275SEric Cheng 	 * the IP client. When a module is inserted in a stream (_I_INSERT)
1506*8275SEric Cheng 	 * the stack initiates capability disable, but due to races, the
1507*8275SEric Cheng 	 * module insertion may complete before the capability disable
1508*8275SEric Cheng 	 * completes. So we limit the check to DLD_ENABLE case.
15091184Skrgopi 	 */
1510*8275SEric Cheng 	if ((flags == DLD_ENABLE && type != DLD_CAPAB_PERIM) &&
1511*8275SEric Cheng 	    (dsp->ds_sap != ETHERTYPE_IP || !check_ip_above(dsp->ds_rq))) {
1512*8275SEric Cheng 		return (ENOTSUP);
1513*8275SEric Cheng 	}
15141184Skrgopi 
1515*8275SEric Cheng 	switch (type) {
1516*8275SEric Cheng 	case DLD_CAPAB_DIRECT:
1517*8275SEric Cheng 		err = dld_capab_direct(dsp, data, flags);
1518*8275SEric Cheng 		break;
15191184Skrgopi 
1520*8275SEric Cheng 	case DLD_CAPAB_POLL:
1521*8275SEric Cheng 		err =  dld_capab_poll(dsp, data, flags);
1522*8275SEric Cheng 		break;
15231184Skrgopi 
1524*8275SEric Cheng 	case DLD_CAPAB_PERIM:
1525*8275SEric Cheng 		err = dld_capab_perim(dsp, data, flags);
1526*8275SEric Cheng 		break;
15271184Skrgopi 
1528*8275SEric Cheng 	case DLD_CAPAB_LSO:
1529*8275SEric Cheng 		err = dld_capab_lso(dsp, data, flags);
1530*8275SEric Cheng 		break;
1531*8275SEric Cheng 
1532*8275SEric Cheng 	default:
1533*8275SEric Cheng 		err = ENOTSUP;
1534*8275SEric Cheng 		break;
15351184Skrgopi 	}
1536*8275SEric Cheng 
1537*8275SEric Cheng 	return (err);
15381184Skrgopi }
15391184Skrgopi 
15400Sstevel@tonic-gate /*
15410Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
15420Sstevel@tonic-gate  */
1543*8275SEric Cheng static void
1544269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
15450Sstevel@tonic-gate {
15460Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
15470Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
15480Sstevel@tonic-gate 	size_t			subsize;
1549*8275SEric Cheng 	dl_capab_dld_t		dld;
15500Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
15510Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
15520Sstevel@tonic-gate 	uint8_t			*ptr;
1553269Sericheng 	queue_t			*q = dsp->ds_wq;
1554269Sericheng 	mblk_t			*mp1;
1555*8275SEric Cheng 	boolean_t		is_vlan;
15565895Syz147064 	boolean_t		hcksum_capable = B_FALSE;
15575895Syz147064 	boolean_t		zcopy_capable = B_FALSE;
1558*8275SEric Cheng 	boolean_t		dld_capable = B_FALSE;
15590Sstevel@tonic-gate 
15600Sstevel@tonic-gate 	/*
15610Sstevel@tonic-gate 	 * Initially assume no capabilities.
15620Sstevel@tonic-gate 	 */
15630Sstevel@tonic-gate 	subsize = 0;
1564*8275SEric Cheng 	is_vlan = (mac_client_vid(dsp->ds_mch) != VLAN_ID_NONE);
15650Sstevel@tonic-gate 
15660Sstevel@tonic-gate 	/*
15675895Syz147064 	 * Check if checksum offload is supported on this MAC.  Don't
15685895Syz147064 	 * advertise DL_CAPAB_HCKSUM if the underlying MAC is VLAN incapable,
15695895Syz147064 	 * since it might not be able to do the hardware checksum offload
15705895Syz147064 	 * with the correct offset.
15710Sstevel@tonic-gate 	 */
15725895Syz147064 	bzero(&hcksum, sizeof (dl_capab_hcksum_t));
15735895Syz147064 	if ((!is_vlan || (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_NATIVEVLAN,
15745895Syz147064 	    NULL))) && mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
15752311Sseb 	    &hcksum.hcksum_txflags)) {
15765895Syz147064 		if (hcksum.hcksum_txflags != 0) {
15775895Syz147064 			hcksum_capable = B_TRUE;
15785895Syz147064 			subsize += sizeof (dl_capability_sub_t) +
15795895Syz147064 			    sizeof (dl_capab_hcksum_t);
15805895Syz147064 		}
15810Sstevel@tonic-gate 	}
15820Sstevel@tonic-gate 
15830Sstevel@tonic-gate 	/*
15845895Syz147064 	 * Check if zerocopy is supported on this interface.
15855895Syz147064 	 * If advertising DL_CAPAB_ZEROCOPY has not been explicitly disabled
15865895Syz147064 	 * then reserve space for that capability.
15870Sstevel@tonic-gate 	 */
15885895Syz147064 	if (!mac_capab_get(dsp->ds_mh, MAC_CAPAB_NO_ZCOPY, NULL) &&
15895895Syz147064 	    !(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
15905895Syz147064 		zcopy_capable = B_TRUE;
15910Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
15920Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
15930Sstevel@tonic-gate 	}
15940Sstevel@tonic-gate 
15950Sstevel@tonic-gate 	/*
1596*8275SEric Cheng 	 * Direct capability negotiation interface between IP and DLD
1597*8275SEric Cheng 	 */
1598*8275SEric Cheng 	if (dsp->ds_sap == ETHERTYPE_IP && check_ip_above(dsp->ds_rq)) {
1599*8275SEric Cheng 		dld_capable = B_TRUE;
1600*8275SEric Cheng 		subsize += sizeof (dl_capability_sub_t) +
1601*8275SEric Cheng 		    sizeof (dl_capab_dld_t);
1602*8275SEric Cheng 	}
1603*8275SEric Cheng 
1604*8275SEric Cheng 	/*
1605269Sericheng 	 * If there are no capabilities to advertise or if we
1606269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
16070Sstevel@tonic-gate 	 */
16081184Skrgopi 	if ((mp1 = reallocb(mp,
1609269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1610269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1611*8275SEric Cheng 		return;
16120Sstevel@tonic-gate 	}
16130Sstevel@tonic-gate 
1614269Sericheng 	mp = mp1;
1615269Sericheng 	DB_TYPE(mp) = M_PROTO;
1616269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1617269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
16180Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
16190Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
16200Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
16210Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
16220Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
16230Sstevel@tonic-gate 
16240Sstevel@tonic-gate 	/*
16250Sstevel@tonic-gate 	 * TCP/IP checksum offload.
16260Sstevel@tonic-gate 	 */
16275895Syz147064 	if (hcksum_capable) {
16280Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16290Sstevel@tonic-gate 
16300Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
16310Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
16320Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
16350Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
16360Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
16370Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
16380Sstevel@tonic-gate 	}
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 	/*
16410Sstevel@tonic-gate 	 * Zero copy
16420Sstevel@tonic-gate 	 */
16435895Syz147064 	if (zcopy_capable) {
16440Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16450Sstevel@tonic-gate 
16460Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
16470Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
16480Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16490Sstevel@tonic-gate 
16500Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
16510Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
16520Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
16530Sstevel@tonic-gate 
16540Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
16550Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
16560Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
16570Sstevel@tonic-gate 	}
16580Sstevel@tonic-gate 
1659*8275SEric Cheng 	/*
1660*8275SEric Cheng 	 * Direct capability negotiation interface between IP and DLD.
1661*8275SEric Cheng 	 * Refer to dld.h for details.
1662*8275SEric Cheng 	 */
1663*8275SEric Cheng 	if (dld_capable) {
1664*8275SEric Cheng 		dlsp = (dl_capability_sub_t *)ptr;
1665*8275SEric Cheng 		dlsp->dl_cap = DL_CAPAB_DLD;
1666*8275SEric Cheng 		dlsp->dl_length = sizeof (dl_capab_dld_t);
1667*8275SEric Cheng 		ptr += sizeof (dl_capability_sub_t);
1668269Sericheng 
1669*8275SEric Cheng 		bzero(&dld, sizeof (dl_capab_dld_t));
1670*8275SEric Cheng 		dld.dld_version = DLD_CURRENT_VERSION;
1671*8275SEric Cheng 		dld.dld_capab = (uintptr_t)dld_capab;
1672*8275SEric Cheng 		dld.dld_capab_handle = (uintptr_t)dsp;
1673*8275SEric Cheng 
1674*8275SEric Cheng 		dlcapabsetqid(&(dld.dld_mid), dsp->ds_rq);
1675*8275SEric Cheng 		bcopy(&dld, ptr, sizeof (dl_capab_dld_t));
1676*8275SEric Cheng 		ptr += sizeof (dl_capab_dld_t);
1677*8275SEric Cheng 	}
1678*8275SEric Cheng 
1679*8275SEric Cheng 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1680269Sericheng 	qreply(q, mp);
16810Sstevel@tonic-gate }
16825113Syz147064 
16835113Syz147064 /*
16845113Syz147064  * Disable any enabled capabilities.
16855113Syz147064  */
16865113Syz147064 void
16875113Syz147064 dld_capabilities_disable(dld_str_t *dsp)
16885113Syz147064 {
16895113Syz147064 	if (dsp->ds_polling)
1690*8275SEric Cheng 		(void) dld_capab_poll_disable(dsp, NULL);
16915113Syz147064 }
1692