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
50Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
60Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
70Sstevel@tonic-gate  * with the License.
80Sstevel@tonic-gate  *
90Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
100Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
110Sstevel@tonic-gate  * See the License for the specific language governing permissions
120Sstevel@tonic-gate  * and limitations under the License.
130Sstevel@tonic-gate  *
140Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
150Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
160Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
170Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
180Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
190Sstevel@tonic-gate  *
200Sstevel@tonic-gate  * CDDL HEADER END
210Sstevel@tonic-gate  */
220Sstevel@tonic-gate /*
230Sstevel@tonic-gate  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
240Sstevel@tonic-gate  * Use is subject to license terms.
250Sstevel@tonic-gate  */
260Sstevel@tonic-gate 
270Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
280Sstevel@tonic-gate 
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate  * Data-Link Driver
310Sstevel@tonic-gate  */
320Sstevel@tonic-gate 
330Sstevel@tonic-gate #include <sys/types.h>
340Sstevel@tonic-gate #include <sys/debug.h>
350Sstevel@tonic-gate #include <sys/sysmacros.h>
360Sstevel@tonic-gate #include <sys/stream.h>
370Sstevel@tonic-gate #include <sys/ddi.h>
380Sstevel@tonic-gate #include <sys/sunddi.h>
390Sstevel@tonic-gate #include <sys/strsun.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>
490Sstevel@tonic-gate 
500Sstevel@tonic-gate typedef boolean_t proto_reqfunc_t(dld_str_t *, union DL_primitives *, mblk_t *);
510Sstevel@tonic-gate 
520Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
530Sstevel@tonic-gate     proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
540Sstevel@tonic-gate     proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
550Sstevel@tonic-gate     proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
560Sstevel@tonic-gate     proto_notify_req, proto_unitdata_req, proto_passive_req;
570Sstevel@tonic-gate 
58*269Sericheng static void proto_poll_disable(dld_str_t *);
59*269Sericheng static boolean_t proto_poll_enable(dld_str_t *, dl_capab_poll_t *);
60*269Sericheng static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
610Sstevel@tonic-gate 
620Sstevel@tonic-gate #define	DL_ACK_PENDING(state) \
630Sstevel@tonic-gate 	((state) == DL_ATTACH_PENDING || \
640Sstevel@tonic-gate 	(state) == DL_DETACH_PENDING || \
650Sstevel@tonic-gate 	(state) == DL_BIND_PENDING || \
660Sstevel@tonic-gate 	(state) == DL_UNBIND_PENDING)
670Sstevel@tonic-gate 
680Sstevel@tonic-gate /*
69*269Sericheng  * Process a DLPI protocol message.
70*269Sericheng  * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
71*269Sericheng  * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
72*269Sericheng  * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
73*269Sericheng  * as 'passive' and forbids it from being subsequently made 'active'
74*269Sericheng  * by the above primitives.
750Sstevel@tonic-gate  */
760Sstevel@tonic-gate void
770Sstevel@tonic-gate dld_proto(dld_str_t *dsp, mblk_t *mp)
780Sstevel@tonic-gate {
790Sstevel@tonic-gate 	union DL_primitives	*udlp;
800Sstevel@tonic-gate 	t_uscalar_t		prim;
810Sstevel@tonic-gate 
820Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (t_uscalar_t)) {
830Sstevel@tonic-gate 		freemsg(mp);
840Sstevel@tonic-gate 		return;
850Sstevel@tonic-gate 	}
860Sstevel@tonic-gate 
870Sstevel@tonic-gate 	udlp = (union DL_primitives *)mp->b_rptr;
880Sstevel@tonic-gate 	prim = udlp->dl_primitive;
890Sstevel@tonic-gate 
90*269Sericheng 	switch (prim) {
91*269Sericheng 	case DL_INFO_REQ:
92*269Sericheng 		(void) proto_info_req(dsp, udlp, mp);
93*269Sericheng 		break;
94*269Sericheng 	case DL_BIND_REQ:
95*269Sericheng 		(void) proto_bind_req(dsp, udlp, mp);
96*269Sericheng 		break;
97*269Sericheng 	case DL_UNBIND_REQ:
98*269Sericheng 		(void) proto_unbind_req(dsp, udlp, mp);
99*269Sericheng 		break;
100*269Sericheng 	case DL_UNITDATA_REQ:
101*269Sericheng 		(void) proto_unitdata_req(dsp, udlp, mp);
102*269Sericheng 		break;
103*269Sericheng 	case DL_UDQOS_REQ:
104*269Sericheng 		(void) proto_udqos_req(dsp, udlp, mp);
105*269Sericheng 		break;
106*269Sericheng 	case DL_ATTACH_REQ:
107*269Sericheng 		(void) proto_attach_req(dsp, udlp, mp);
108*269Sericheng 		break;
109*269Sericheng 	case DL_DETACH_REQ:
110*269Sericheng 		(void) proto_detach_req(dsp, udlp, mp);
111*269Sericheng 		break;
112*269Sericheng 	case DL_ENABMULTI_REQ:
113*269Sericheng 		(void) proto_enabmulti_req(dsp, udlp, mp);
114*269Sericheng 		break;
115*269Sericheng 	case DL_DISABMULTI_REQ:
116*269Sericheng 		(void) proto_disabmulti_req(dsp, udlp, mp);
117*269Sericheng 		break;
118*269Sericheng 	case DL_PROMISCON_REQ:
119*269Sericheng 		(void) proto_promiscon_req(dsp, udlp, mp);
120*269Sericheng 		break;
121*269Sericheng 	case DL_PROMISCOFF_REQ:
122*269Sericheng 		(void) proto_promiscoff_req(dsp, udlp, mp);
123*269Sericheng 		break;
124*269Sericheng 	case DL_PHYS_ADDR_REQ:
125*269Sericheng 		(void) proto_physaddr_req(dsp, udlp, mp);
126*269Sericheng 		break;
127*269Sericheng 	case DL_SET_PHYS_ADDR_REQ:
128*269Sericheng 		(void) proto_setphysaddr_req(dsp, udlp, mp);
129*269Sericheng 		break;
130*269Sericheng 	case DL_NOTIFY_REQ:
131*269Sericheng 		(void) proto_notify_req(dsp, udlp, mp);
132*269Sericheng 		break;
133*269Sericheng 	case DL_CAPABILITY_REQ:
134*269Sericheng 		(void) proto_capability_req(dsp, udlp, mp);
135*269Sericheng 		break;
136*269Sericheng 	case DL_PASSIVE_REQ:
137*269Sericheng 		(void) proto_passive_req(dsp, udlp, mp);
138*269Sericheng 		break;
139*269Sericheng 	default:
140*269Sericheng 		(void) proto_req(dsp, udlp, mp);
141*269Sericheng 		break;
1420Sstevel@tonic-gate 	}
1430Sstevel@tonic-gate }
1440Sstevel@tonic-gate 
1450Sstevel@tonic-gate /*
146*269Sericheng  * Finish any pending operations.  At this moment we are single-threaded,
147*269Sericheng  * hence there is no need to hold ds_lock as writer because we're already
148*269Sericheng  * exclusive.
1490Sstevel@tonic-gate  */
150*269Sericheng void
151*269Sericheng dld_finish_pending_ops(dld_str_t *dsp)
1520Sstevel@tonic-gate {
153*269Sericheng 	ASSERT(MUTEX_HELD(&dsp->ds_thr_lock));
154*269Sericheng 	ASSERT(dsp->ds_thr == 0);
1550Sstevel@tonic-gate 
156*269Sericheng 	/* Pending DL_DETACH_REQ? */
157*269Sericheng 	if (dsp->ds_detach_req != NULL) {
158*269Sericheng 		mblk_t *mp;
1590Sstevel@tonic-gate 
160*269Sericheng 		ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING);
161*269Sericheng 		dld_str_detach(dsp);
1620Sstevel@tonic-gate 
163*269Sericheng 		mp = dsp->ds_detach_req;
164*269Sericheng 		dsp->ds_detach_req = NULL;
1650Sstevel@tonic-gate 
166*269Sericheng 		mutex_exit(&dsp->ds_thr_lock);
167*269Sericheng 		dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
168*269Sericheng 	} else {
169*269Sericheng 		mutex_exit(&dsp->ds_thr_lock);
1700Sstevel@tonic-gate 	}
1710Sstevel@tonic-gate }
1720Sstevel@tonic-gate 
173*269Sericheng #define	NEG(x)	-(x)
1740Sstevel@tonic-gate 
1750Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1760Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
1770Sstevel@tonic-gate 	uint8_t			dl_addr[MAXADDRLEN + sizeof (uint16_t)];
1780Sstevel@tonic-gate 	uint8_t			dl_brdcst_addr[MAXADDRLEN];
1790Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1800Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1810Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1820Sstevel@tonic-gate 
1830Sstevel@tonic-gate /*
184*269Sericheng  * DL_INFO_REQ
1850Sstevel@tonic-gate  */
186*269Sericheng /*ARGSUSED*/
187*269Sericheng static boolean_t
188*269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1890Sstevel@tonic-gate {
1900Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1910Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
1920Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
1930Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
1940Sstevel@tonic-gate 	uint8_t			*addr;
1950Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
1960Sstevel@tonic-gate 	uint_t			addr_length;
1970Sstevel@tonic-gate 	uint_t			sap_length;
198*269Sericheng 	mac_info_t		minfo;
199*269Sericheng 	mac_info_t		*minfop;
200*269Sericheng 	queue_t			*q = dsp->ds_wq;
2010Sstevel@tonic-gate 
2020Sstevel@tonic-gate 	/*
2030Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
2040Sstevel@tonic-gate 	 * wrapper structure defined above.
2050Sstevel@tonic-gate 	 */
206*269Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
2070Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
208*269Sericheng 		return (B_FALSE);
209*269Sericheng 
210*269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
2110Sstevel@tonic-gate 
2120Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
2130Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
2160Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	/*
2210Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
2220Sstevel@tonic-gate 	 */
2230Sstevel@tonic-gate 	addr = dlwp->dl_addr;
2240Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
2250Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
2260Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
2270Sstevel@tonic-gate 
2280Sstevel@tonic-gate 	/*
2290Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
2300Sstevel@tonic-gate 	 * nodes.
2310Sstevel@tonic-gate 	 */
2320Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
2330Sstevel@tonic-gate 	dlp->dl_version = DL_VERSION_2;
2340Sstevel@tonic-gate 
2350Sstevel@tonic-gate 	/*
236*269Sericheng 	 * Set the style of the provider
2370Sstevel@tonic-gate 	 */
238*269Sericheng 	dlp->dl_provider_style = dsp->ds_style;
2390Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
2400Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 	/*
2430Sstevel@tonic-gate 	 * Set the current DLPI state.
2440Sstevel@tonic-gate 	 */
2450Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
2460Sstevel@tonic-gate 
2470Sstevel@tonic-gate 	/*
248*269Sericheng 	 * Gratuitously set the media type. This is to deal with modules
249*269Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
2500Sstevel@tonic-gate 	 * being completed.
2510Sstevel@tonic-gate 	 */
2520Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2530Sstevel@tonic-gate 
2540Sstevel@tonic-gate 	/*
255*269Sericheng 	 * If the stream is not at least attached we try to retrieve the
256*269Sericheng 	 * mac_info using mac_info_get()
2570Sstevel@tonic-gate 	 */
2580Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2590Sstevel@tonic-gate 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
260*269Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
261*269Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
262*269Sericheng 			/*
263*269Sericheng 			 * Cannot find mac_info. giving up.
264*269Sericheng 			 */
265*269Sericheng 			goto done;
266*269Sericheng 		}
267*269Sericheng 		minfop = &minfo;
268*269Sericheng 	} else {
269*269Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
270*269Sericheng 	}
2710Sstevel@tonic-gate 
2720Sstevel@tonic-gate 	/*
2730Sstevel@tonic-gate 	 * Set the media type (properly this time).
2740Sstevel@tonic-gate 	 */
275*269Sericheng 	dlp->dl_mac_type = minfop->mi_media;
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	/*
2780Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2790Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2800Sstevel@tonic-gate 	 */
2810Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2820Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	/*
2850Sstevel@tonic-gate 	 * Set the minimum and maximum payload sizes.
2860Sstevel@tonic-gate 	 */
287*269Sericheng 	dlp->dl_min_sdu = minfop->mi_sdu_min;
288*269Sericheng 	dlp->dl_max_sdu = minfop->mi_sdu_max;
2890Sstevel@tonic-gate 
290*269Sericheng 	addr_length = minfop->mi_addr_length;
2910Sstevel@tonic-gate 	ASSERT(addr_length != 0);
2920Sstevel@tonic-gate 
2930Sstevel@tonic-gate 	/*
2940Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2950Sstevel@tonic-gate 	 */
2960Sstevel@tonic-gate 	dlp->dl_brdcst_addr_offset = (uintptr_t)brdcst_addr - (uintptr_t)dlp;
297*269Sericheng 	bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
2980Sstevel@tonic-gate 	dlp->dl_brdcst_addr_length = addr_length;
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate 	/*
3010Sstevel@tonic-gate 	 * We only support QoS information for VLAN interfaces.
3020Sstevel@tonic-gate 	 */
3030Sstevel@tonic-gate 	if (dsp->ds_vid != VLAN_ID_NONE) {
3040Sstevel@tonic-gate 		dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
3050Sstevel@tonic-gate 		dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 		rangep->dl_qos_type = DL_QOS_CL_RANGE1;
3080Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
3090Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
3100Sstevel@tonic-gate 		rangep->dl_protection.dl_min = DL_UNKNOWN;
3110Sstevel@tonic-gate 		rangep->dl_protection.dl_max = DL_UNKNOWN;
3120Sstevel@tonic-gate 		rangep->dl_residual_error = DL_UNKNOWN;
3130Sstevel@tonic-gate 
3140Sstevel@tonic-gate 		/*
3150Sstevel@tonic-gate 		 * Specify the supported range of priorities.
3160Sstevel@tonic-gate 		 */
3170Sstevel@tonic-gate 		rangep->dl_priority.dl_min = 0;
3180Sstevel@tonic-gate 		rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
3190Sstevel@tonic-gate 
3200Sstevel@tonic-gate 		dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
3210Sstevel@tonic-gate 		dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 		selp->dl_qos_type = DL_QOS_CL_SEL1;
3240Sstevel@tonic-gate 		selp->dl_trans_delay = DL_UNKNOWN;
3250Sstevel@tonic-gate 		selp->dl_protection = DL_UNKNOWN;
3260Sstevel@tonic-gate 		selp->dl_residual_error = DL_UNKNOWN;
3270Sstevel@tonic-gate 
3280Sstevel@tonic-gate 		/*
3290Sstevel@tonic-gate 		 * Specify the current priority (which can be changed by
3300Sstevel@tonic-gate 		 * the DL_UDQOS_REQ primitive).
3310Sstevel@tonic-gate 		 */
3320Sstevel@tonic-gate 		selp->dl_priority = dsp->ds_pri;
3330Sstevel@tonic-gate 	} else {
3340Sstevel@tonic-gate 		/*
3350Sstevel@tonic-gate 		 * Shorten the buffer to lose the unused QoS information
336*269Sericheng 		 * structures.
3370Sstevel@tonic-gate 		 */
3380Sstevel@tonic-gate 		mp->b_wptr = (uint8_t *)rangep;
3390Sstevel@tonic-gate 	}
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
3420Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
3430Sstevel@tonic-gate 		/*
3440Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
3450Sstevel@tonic-gate 		 * DLSAP address.
3460Sstevel@tonic-gate 		 */
3470Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
3480Sstevel@tonic-gate 		bcopy(dsp->ds_curr_addr, addr, addr_length);
3490Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3500Sstevel@tonic-gate 	}
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate done:
3530Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
3540Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
3550Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3560Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3570Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3580Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3590Sstevel@tonic-gate 
360*269Sericheng 	rw_exit(&dsp->ds_lock);
361*269Sericheng 
362*269Sericheng 	qreply(q, mp);
363*269Sericheng 	return (B_TRUE);
364*269Sericheng }
365*269Sericheng 
366*269Sericheng /*
367*269Sericheng  * DL_ATTACH_REQ
368*269Sericheng  */
369*269Sericheng static boolean_t
370*269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
371*269Sericheng {
372*269Sericheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)udlp;
373*269Sericheng 	int		err = 0;
374*269Sericheng 	t_uscalar_t	dl_err;
375*269Sericheng 	queue_t		*q = dsp->ds_wq;
376*269Sericheng 
377*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
378*269Sericheng 
379*269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
380*269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
381*269Sericheng 		dl_err = DL_BADPRIM;
382*269Sericheng 		goto failed;
383*269Sericheng 	}
384*269Sericheng 
385*269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
386*269Sericheng 		dl_err = DL_OUTSTATE;
387*269Sericheng 		goto failed;
388*269Sericheng 	}
389*269Sericheng 
390*269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
391*269Sericheng 
392*269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
393*269Sericheng 	if (err != 0) {
394*269Sericheng 		switch (err) {
395*269Sericheng 		case ENOENT:
396*269Sericheng 			dl_err = DL_BADPPA;
397*269Sericheng 			err = 0;
398*269Sericheng 			break;
399*269Sericheng 		default:
400*269Sericheng 			dl_err = DL_SYSERR;
401*269Sericheng 			break;
402*269Sericheng 		}
403*269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
404*269Sericheng 		goto failed;
405*269Sericheng 	}
406*269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
407*269Sericheng 	rw_exit(&dsp->ds_lock);
408*269Sericheng 
409*269Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
410*269Sericheng 	return (B_TRUE);
411*269Sericheng failed:
412*269Sericheng 	rw_exit(&dsp->ds_lock);
413*269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
414*269Sericheng 	return (B_FALSE);
4150Sstevel@tonic-gate }
4160Sstevel@tonic-gate 
4170Sstevel@tonic-gate /*
418*269Sericheng  * DL_DETACH_REQ
4190Sstevel@tonic-gate  */
420*269Sericheng /*ARGSUSED*/
421*269Sericheng static boolean_t
422*269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4230Sstevel@tonic-gate {
424*269Sericheng 	queue_t		*q = dsp->ds_wq;
425*269Sericheng 	t_uscalar_t	dl_err;
4260Sstevel@tonic-gate 
427*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
4280Sstevel@tonic-gate 
429*269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
430*269Sericheng 		dl_err = DL_BADPRIM;
431*269Sericheng 		goto failed;
432*269Sericheng 	}
4330Sstevel@tonic-gate 
434*269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
435*269Sericheng 		dl_err = DL_OUTSTATE;
436*269Sericheng 		goto failed;
4370Sstevel@tonic-gate 	}
4380Sstevel@tonic-gate 
439*269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
440*269Sericheng 		dl_err = DL_BADPRIM;
441*269Sericheng 		goto failed;
442*269Sericheng 	}
443*269Sericheng 
444*269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
445*269Sericheng 
446*269Sericheng 	/*
447*269Sericheng 	 * Complete the detach when the driver is single-threaded.
448*269Sericheng 	 */
449*269Sericheng 	mutex_enter(&dsp->ds_thr_lock);
450*269Sericheng 	ASSERT(dsp->ds_detach_req == NULL);
451*269Sericheng 	dsp->ds_detach_req = mp;
452*269Sericheng 	mutex_exit(&dsp->ds_thr_lock);
453*269Sericheng 	rw_exit(&dsp->ds_lock);
454*269Sericheng 
455*269Sericheng 	return (B_TRUE);
456*269Sericheng failed:
457*269Sericheng 	rw_exit(&dsp->ds_lock);
458*269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
459*269Sericheng 	return (B_FALSE);
4600Sstevel@tonic-gate }
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate /*
463*269Sericheng  * DL_BIND_REQ
4640Sstevel@tonic-gate  */
465*269Sericheng static boolean_t
466*269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4670Sstevel@tonic-gate {
468*269Sericheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)udlp;
469*269Sericheng 	int		err = 0;
470*269Sericheng 	uint8_t		addr[MAXADDRLEN];
471*269Sericheng 	uint_t		addr_length;
472*269Sericheng 	t_uscalar_t	dl_err;
473*269Sericheng 	t_scalar_t	sap;
474*269Sericheng 	queue_t		*q = dsp->ds_wq;
475*269Sericheng 
476*269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
477*269Sericheng 		dl_err = DL_BADPRIM;
478*269Sericheng 		goto failed;
479*269Sericheng 	}
480*269Sericheng 
481*269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
482*269Sericheng 		dl_err = DL_NOAUTO;
483*269Sericheng 		goto failed;
484*269Sericheng 	}
485*269Sericheng 
486*269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
487*269Sericheng 		dl_err = DL_UNSUPPORTED;
488*269Sericheng 		goto failed;
489*269Sericheng 	}
490*269Sericheng 
491*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
492*269Sericheng 
493*269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
494*269Sericheng 		dl_err = DL_OUTSTATE;
495*269Sericheng 		goto failed;
496*269Sericheng 	}
4970Sstevel@tonic-gate 
498*269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
499*269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
500*269Sericheng 		dl_err = DL_SYSERR;
501*269Sericheng 		err = EBUSY;
502*269Sericheng 		goto failed;
503*269Sericheng 	}
504*269Sericheng 
505*269Sericheng 	dsp->ds_dlstate = DL_BIND_PENDING;
506*269Sericheng 	/*
507*269Sericheng 	 * Set the receive callback.
508*269Sericheng 	 */
509*269Sericheng 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ?
510*269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
5110Sstevel@tonic-gate 
512*269Sericheng 	/*
513*269Sericheng 	 * Bind the channel such that it can receive packets.
514*269Sericheng 	 */
515*269Sericheng 	sap = dsp->ds_sap = dlp->dl_sap;
516*269Sericheng 	err = dls_bind(dsp->ds_dc, dlp->dl_sap);
517*269Sericheng 	if (err != 0) {
518*269Sericheng 		switch (err) {
519*269Sericheng 		case EINVAL:
520*269Sericheng 			dl_err = DL_BADADDR;
521*269Sericheng 			err = 0;
522*269Sericheng 			break;
523*269Sericheng 		default:
524*269Sericheng 			dl_err = DL_SYSERR;
525*269Sericheng 			break;
526*269Sericheng 		}
527*269Sericheng 		dsp->ds_dlstate = DL_UNBOUND;
528*269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
529*269Sericheng 			dls_active_clear(dsp->ds_dc);
530*269Sericheng 
5310Sstevel@tonic-gate 		goto failed;
532*269Sericheng 	}
5330Sstevel@tonic-gate 
5340Sstevel@tonic-gate 	/*
5350Sstevel@tonic-gate 	 * Copy in MAC address.
5360Sstevel@tonic-gate 	 */
5370Sstevel@tonic-gate 	addr_length = dsp->ds_mip->mi_addr_length;
5380Sstevel@tonic-gate 	bcopy(dsp->ds_curr_addr, addr, addr_length);
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 	/*
5410Sstevel@tonic-gate 	 * Copy in the DLSAP.
5420Sstevel@tonic-gate 	 */
5430Sstevel@tonic-gate 	*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
5440Sstevel@tonic-gate 	addr_length += sizeof (uint16_t);
5450Sstevel@tonic-gate 
5460Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
547*269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
548*269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
5490Sstevel@tonic-gate 
550*269Sericheng 	rw_exit(&dsp->ds_lock);
551*269Sericheng 
552*269Sericheng 	dlbindack(q, mp, sap, (void *)addr, addr_length, 0, 0);
553*269Sericheng 	return (B_TRUE);
5540Sstevel@tonic-gate failed:
555*269Sericheng 	rw_exit(&dsp->ds_lock);
556*269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
557*269Sericheng 	return (B_FALSE);
5580Sstevel@tonic-gate }
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate /*
561*269Sericheng  * DL_UNBIND_REQ
5620Sstevel@tonic-gate  */
563*269Sericheng /*ARGSUSED*/
564*269Sericheng static boolean_t
565*269Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
5660Sstevel@tonic-gate {
567*269Sericheng 	queue_t		*q = dsp->ds_wq;
568*269Sericheng 	t_uscalar_t	dl_err;
569*269Sericheng 
570*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
571*269Sericheng 
572*269Sericheng 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
573*269Sericheng 		dl_err = DL_BADPRIM;
574*269Sericheng 		goto failed;
575*269Sericheng 	}
576*269Sericheng 
577*269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
578*269Sericheng 		dl_err = DL_OUTSTATE;
579*269Sericheng 		goto failed;
580*269Sericheng 	}
581*269Sericheng 
582*269Sericheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
583*269Sericheng 
584*269Sericheng 	/*
585*269Sericheng 	 * Flush any remaining packets scheduled for transmission.
586*269Sericheng 	 */
587*269Sericheng 	dld_tx_flush(dsp);
588*269Sericheng 
589*269Sericheng 	/*
590*269Sericheng 	 * Unbind the channel to stop packets being received.
591*269Sericheng 	 */
592*269Sericheng 	dls_unbind(dsp->ds_dc);
593*269Sericheng 
594*269Sericheng 	/*
595*269Sericheng 	 * Disable polling mode, if it is enabled.
596*269Sericheng 	 */
597*269Sericheng 	proto_poll_disable(dsp);
598*269Sericheng 
599*269Sericheng 	/*
600*269Sericheng 	 * Clear the receive callback.
601*269Sericheng 	 */
602*269Sericheng 	dls_rx_set(dsp->ds_dc, NULL, NULL);
603*269Sericheng 
604*269Sericheng 	/*
605*269Sericheng 	 * Set the mode back to the default (unitdata).
606*269Sericheng 	 */
607*269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
608*269Sericheng 
6090Sstevel@tonic-gate 	dsp->ds_dlstate = DL_UNBOUND;
610*269Sericheng 	rw_exit(&dsp->ds_lock);
611*269Sericheng 
612*269Sericheng 	dlokack(q, mp, DL_UNBIND_REQ);
613*269Sericheng 	return (B_TRUE);
614*269Sericheng failed:
615*269Sericheng 	rw_exit(&dsp->ds_lock);
616*269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
617*269Sericheng 	return (B_FALSE);
6180Sstevel@tonic-gate }
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate /*
621*269Sericheng  * DL_PROMISCON_REQ
6220Sstevel@tonic-gate  */
623*269Sericheng static boolean_t
624*269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6250Sstevel@tonic-gate {
626*269Sericheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp;
627*269Sericheng 	int		err = 0;
628*269Sericheng 	t_uscalar_t	dl_err;
629*269Sericheng 	uint32_t	promisc_saved;
630*269Sericheng 	queue_t		*q = dsp->ds_wq;
631*269Sericheng 
632*269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
633*269Sericheng 		dl_err = DL_BADPRIM;
634*269Sericheng 		goto failed;
635*269Sericheng 	}
636*269Sericheng 
637*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
638*269Sericheng 
639*269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
640*269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
641*269Sericheng 		dl_err = DL_OUTSTATE;
6420Sstevel@tonic-gate 		goto failed;
643*269Sericheng 	}
6440Sstevel@tonic-gate 
645*269Sericheng 	promisc_saved = dsp->ds_promisc;
646*269Sericheng 	switch (dlp->dl_level) {
647*269Sericheng 	case DL_PROMISC_SAP:
648*269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_SAP;
649*269Sericheng 		break;
650*269Sericheng 
651*269Sericheng 	case DL_PROMISC_MULTI:
652*269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_MULTI;
653*269Sericheng 		break;
654*269Sericheng 
655*269Sericheng 	case DL_PROMISC_PHYS:
656*269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_PHYS;
657*269Sericheng 		break;
658*269Sericheng 
659*269Sericheng 	default:
660*269Sericheng 		dl_err = DL_NOTSUPPORTED;
661*269Sericheng 		goto failed;
662*269Sericheng 	}
6630Sstevel@tonic-gate 
664*269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
665*269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
666*269Sericheng 		dsp->ds_promisc = promisc_saved;
667*269Sericheng 		dl_err = DL_SYSERR;
668*269Sericheng 		err = EBUSY;
669*269Sericheng 		goto failed;
670*269Sericheng 	}
671*269Sericheng 
672*269Sericheng 	/*
673*269Sericheng 	 * Adjust channel promiscuity.
674*269Sericheng 	 */
675*269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
676*269Sericheng 	if (err != 0) {
677*269Sericheng 		dl_err = DL_SYSERR;
678*269Sericheng 		dsp->ds_promisc = promisc_saved;
679*269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
680*269Sericheng 			dls_active_clear(dsp->ds_dc);
681*269Sericheng 
682*269Sericheng 		goto failed;
683*269Sericheng 	}
684*269Sericheng 
685*269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
686*269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
687*269Sericheng 
688*269Sericheng 	rw_exit(&dsp->ds_lock);
689*269Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
690*269Sericheng 	return (B_TRUE);
6910Sstevel@tonic-gate failed:
692*269Sericheng 	rw_exit(&dsp->ds_lock);
693*269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
694*269Sericheng 	return (B_FALSE);
6950Sstevel@tonic-gate }
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate /*
698*269Sericheng  * DL_PROMISCOFF_REQ
6990Sstevel@tonic-gate  */
700*269Sericheng static boolean_t
701*269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
7020Sstevel@tonic-gate {
703*269Sericheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp;
704*269Sericheng 	int		err = 0;
705*269Sericheng 	t_uscalar_t	dl_err;
706*269Sericheng 	uint32_t	promisc_saved;
707*269Sericheng 	queue_t		*q = dsp->ds_wq;
708*269Sericheng 
709*269Sericheng 
710*269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
711*269Sericheng 		dl_err = DL_BADPRIM;
7120Sstevel@tonic-gate 		goto failed;
713*269Sericheng 	}
7140Sstevel@tonic-gate 
715*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
7160Sstevel@tonic-gate 
717*269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
718*269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
719*269Sericheng 		dl_err = DL_OUTSTATE;
7200Sstevel@tonic-gate 		goto failed;
721*269Sericheng 	}
7220Sstevel@tonic-gate 
723*269Sericheng 	promisc_saved = dsp->ds_promisc;
724*269Sericheng 	switch (dlp->dl_level) {
725*269Sericheng 	case DL_PROMISC_SAP:
726*269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
727*269Sericheng 			dl_err = DL_NOTENAB;
728*269Sericheng 			goto failed;
729*269Sericheng 		}
730*269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
7310Sstevel@tonic-gate 		break;
7320Sstevel@tonic-gate 
733*269Sericheng 	case DL_PROMISC_MULTI:
734*269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
735*269Sericheng 			dl_err = DL_NOTENAB;
736*269Sericheng 			goto failed;
737*269Sericheng 		}
738*269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
739*269Sericheng 		break;
740*269Sericheng 
741*269Sericheng 	case DL_PROMISC_PHYS:
742*269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
743*269Sericheng 			dl_err = DL_NOTENAB;
744*269Sericheng 			goto failed;
745*269Sericheng 		}
746*269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
7470Sstevel@tonic-gate 		break;
7480Sstevel@tonic-gate 
7490Sstevel@tonic-gate 	default:
750*269Sericheng 		dl_err = DL_NOTSUPPORTED;
751*269Sericheng 		goto failed;
752*269Sericheng 	}
753*269Sericheng 
754*269Sericheng 	/*
755*269Sericheng 	 * Adjust channel promiscuity.
756*269Sericheng 	 */
757*269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
758*269Sericheng 	if (err != 0) {
759*269Sericheng 		dsp->ds_promisc = promisc_saved;
7600Sstevel@tonic-gate 		dl_err = DL_SYSERR;
761*269Sericheng 		goto failed;
762*269Sericheng 	}
763*269Sericheng 
764*269Sericheng 	rw_exit(&dsp->ds_lock);
765*269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
766*269Sericheng 	return (B_TRUE);
767*269Sericheng failed:
768*269Sericheng 	rw_exit(&dsp->ds_lock);
769*269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
770*269Sericheng 	return (B_FALSE);
771*269Sericheng }
772*269Sericheng 
773*269Sericheng /*
774*269Sericheng  * DL_ENABMULTI_REQ
775*269Sericheng  */
776*269Sericheng static boolean_t
777*269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
778*269Sericheng {
779*269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
780*269Sericheng 	int		err = 0;
781*269Sericheng 	t_uscalar_t	dl_err;
782*269Sericheng 	queue_t		*q = dsp->ds_wq;
783*269Sericheng 
784*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
785*269Sericheng 
786*269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
787*269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
788*269Sericheng 		dl_err = DL_OUTSTATE;
789*269Sericheng 		goto failed;
790*269Sericheng 	}
791*269Sericheng 
792*269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
793*269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
794*269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
795*269Sericheng 		dl_err = DL_BADPRIM;
796*269Sericheng 		goto failed;
797*269Sericheng 	}
798*269Sericheng 
799*269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
800*269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
801*269Sericheng 		dl_err = DL_SYSERR;
802*269Sericheng 		err = EBUSY;
803*269Sericheng 		goto failed;
8040Sstevel@tonic-gate 	}
8050Sstevel@tonic-gate 
806*269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
807*269Sericheng 	if (err != 0) {
808*269Sericheng 		switch (err) {
809*269Sericheng 		case EINVAL:
810*269Sericheng 			dl_err = DL_BADADDR;
811*269Sericheng 			err = 0;
812*269Sericheng 			break;
813*269Sericheng 		case ENOSPC:
814*269Sericheng 			dl_err = DL_TOOMANY;
815*269Sericheng 			err = 0;
816*269Sericheng 			break;
817*269Sericheng 		default:
818*269Sericheng 			dl_err = DL_SYSERR;
819*269Sericheng 			break;
820*269Sericheng 		}
821*269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
822*269Sericheng 			dls_active_clear(dsp->ds_dc);
823*269Sericheng 
824*269Sericheng 		goto failed;
825*269Sericheng 	}
826*269Sericheng 
827*269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
828*269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
829*269Sericheng 
830*269Sericheng 	rw_exit(&dsp->ds_lock);
831*269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
832*269Sericheng 	return (B_TRUE);
833*269Sericheng failed:
834*269Sericheng 	rw_exit(&dsp->ds_lock);
835*269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
836*269Sericheng 	return (B_FALSE);
837*269Sericheng }
838*269Sericheng 
839*269Sericheng /*
840*269Sericheng  * DL_DISABMULTI_REQ
841*269Sericheng  */
842*269Sericheng static boolean_t
843*269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
844*269Sericheng {
845*269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
846*269Sericheng 	int		err = 0;
847*269Sericheng 	t_uscalar_t	dl_err;
848*269Sericheng 	queue_t		*q = dsp->ds_wq;
849*269Sericheng 
850*269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
851*269Sericheng 
852*269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
853*269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
854*269Sericheng 		dl_err = DL_OUTSTATE;
855*269Sericheng 		goto failed;
856*269Sericheng 	}
857*269Sericheng 
858*269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
859*269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
860*269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
861*269Sericheng 		dl_err = DL_BADPRIM;
862*269Sericheng 		goto failed;
863*269Sericheng 	}
864*269Sericheng 
865*269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
866*269Sericheng 	if (err != 0) {
867*269Sericheng 	switch (err) {
868*269Sericheng 		case EINVAL:
869*269Sericheng 			dl_err = DL_BADADDR;
870*269Sericheng 			err = 0;
871*269Sericheng 			break;
872*269Sericheng 
873*269Sericheng 		case ENOENT:
874*269Sericheng 			dl_err = DL_NOTENAB;
875*269Sericheng 			err = 0;
876*269Sericheng 			break;
877*269Sericheng 
878*269Sericheng 		default:
879*269Sericheng 			dl_err = DL_SYSERR;
880*269Sericheng 			break;
881*269Sericheng 		}
882*269Sericheng 		goto failed;
883*269Sericheng 	}
884*269Sericheng 
885*269Sericheng 	rw_exit(&dsp->ds_lock);
886*269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
887*269Sericheng 	return (B_TRUE);
888*269Sericheng failed:
889*269Sericheng 	rw_exit(&dsp->ds_lock);
890*269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
891*269Sericheng 	return (B_FALSE);
8920Sstevel@tonic-gate }
8930Sstevel@tonic-gate 
8940Sstevel@tonic-gate /*
895*269Sericheng  * DL_PHYS_ADDR_REQ
8960Sstevel@tonic-gate  */
897*269Sericheng static boolean_t
898*269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
8990Sstevel@tonic-gate {
900*269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
901*269Sericheng 	queue_t		*q = dsp->ds_wq;
902*269Sericheng 	t_uscalar_t	dl_err;
903*269Sericheng 	char		*addr;
904*269Sericheng 	uint_t		addr_length;
905*269Sericheng 
906*269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
907*269Sericheng 
908*269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
909*269Sericheng 		dl_err = DL_BADPRIM;
910*269Sericheng 		goto failed;
911*269Sericheng 	}
912*269Sericheng 
913*269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
914*269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
915*269Sericheng 		dl_err = DL_OUTSTATE;
916*269Sericheng 		goto failed;
917*269Sericheng 	}
9180Sstevel@tonic-gate 
919*269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
920*269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
921*269Sericheng 		dl_err = DL_UNSUPPORTED;
9220Sstevel@tonic-gate 		goto failed;
923*269Sericheng 	}
9240Sstevel@tonic-gate 
925*269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
926*269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
927*269Sericheng 	if (addr == NULL) {
928*269Sericheng 		rw_exit(&dsp->ds_lock);
929*269Sericheng 		merror(q, mp, ENOSR);
930*269Sericheng 		return (B_FALSE);
931*269Sericheng 	}
9320Sstevel@tonic-gate 
933*269Sericheng 	/*
934*269Sericheng 	 * Copy out the address before we drop the lock; we don't
935*269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
936*269Sericheng 	 */
937*269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
938*269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
939*269Sericheng 
940*269Sericheng 	rw_exit(&dsp->ds_lock);
941*269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
942*269Sericheng 	kmem_free(addr, addr_length);
943*269Sericheng 	return (B_TRUE);
9440Sstevel@tonic-gate failed:
945*269Sericheng 	rw_exit(&dsp->ds_lock);
946*269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
947*269Sericheng 	return (B_FALSE);
948*269Sericheng }
9490Sstevel@tonic-gate 
950*269Sericheng /*
951*269Sericheng  * DL_SET_PHYS_ADDR_REQ
952*269Sericheng  */
953*269Sericheng static boolean_t
954*269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
955*269Sericheng {
956*269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
957*269Sericheng 	int		err = 0;
958*269Sericheng 	t_uscalar_t	dl_err;
959*269Sericheng 	queue_t		*q = dsp->ds_wq;
9600Sstevel@tonic-gate 
961*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
962*269Sericheng 
963*269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
964*269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
965*269Sericheng 		dl_err = DL_OUTSTATE;
966*269Sericheng 		goto failed;
967*269Sericheng 	}
968*269Sericheng 
969*269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
970*269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
971*269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
972*269Sericheng 		dl_err = DL_BADPRIM;
973*269Sericheng 		goto failed;
9740Sstevel@tonic-gate 	}
9750Sstevel@tonic-gate 
976*269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
977*269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
978*269Sericheng 		dl_err = DL_SYSERR;
979*269Sericheng 		err = EBUSY;
980*269Sericheng 		goto failed;
981*269Sericheng 	}
982*269Sericheng 
983*269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
984*269Sericheng 	if (err != 0) {
985*269Sericheng 		switch (err) {
986*269Sericheng 		case EINVAL:
987*269Sericheng 			dl_err = DL_BADADDR;
988*269Sericheng 			err = 0;
989*269Sericheng 			break;
990*269Sericheng 
991*269Sericheng 		default:
992*269Sericheng 			dl_err = DL_SYSERR;
993*269Sericheng 			break;
994*269Sericheng 		}
995*269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
996*269Sericheng 			dls_active_clear(dsp->ds_dc);
997*269Sericheng 
998*269Sericheng 		goto failed;
999*269Sericheng 	}
1000*269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1001*269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
1002*269Sericheng 
1003*269Sericheng 	rw_exit(&dsp->ds_lock);
1004*269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
1005*269Sericheng 	return (B_TRUE);
1006*269Sericheng failed:
1007*269Sericheng 	rw_exit(&dsp->ds_lock);
1008*269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
1009*269Sericheng 	return (B_FALSE);
1010*269Sericheng }
1011*269Sericheng 
1012*269Sericheng /*
1013*269Sericheng  * DL_UDQOS_REQ
1014*269Sericheng  */
1015*269Sericheng static boolean_t
1016*269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1017*269Sericheng {
1018*269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
1019*269Sericheng 	dl_qos_cl_sel1_t *selp;
1020*269Sericheng 	int		off, len;
1021*269Sericheng 	t_uscalar_t	dl_err;
1022*269Sericheng 	queue_t		*q = dsp->ds_wq;
1023*269Sericheng 
1024*269Sericheng 	off = dlp->dl_qos_offset;
1025*269Sericheng 	len = dlp->dl_qos_length;
1026*269Sericheng 
1027*269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
1028*269Sericheng 		dl_err = DL_BADPRIM;
1029*269Sericheng 		goto failed;
1030*269Sericheng 	}
1031*269Sericheng 
1032*269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1033*269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1034*269Sericheng 		dl_err = DL_BADQOSTYPE;
1035*269Sericheng 		goto failed;
1036*269Sericheng 	}
1037*269Sericheng 
1038*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1039*269Sericheng 
1040*269Sericheng 	if (dsp->ds_vid == VLAN_ID_NONE ||
1041*269Sericheng 	    selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1042*269Sericheng 	    selp->dl_priority < 0) {
1043*269Sericheng 		dl_err = DL_BADQOSPARAM;
1044*269Sericheng 		goto failed;
1045*269Sericheng 	}
1046*269Sericheng 
1047*269Sericheng 	dsp->ds_pri = selp->dl_priority;
1048*269Sericheng 
1049*269Sericheng 	rw_exit(&dsp->ds_lock);
1050*269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1051*269Sericheng 	return (B_TRUE);
1052*269Sericheng failed:
1053*269Sericheng 	rw_exit(&dsp->ds_lock);
1054*269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1055*269Sericheng 	return (B_FALSE);
10560Sstevel@tonic-gate }
10570Sstevel@tonic-gate 
10580Sstevel@tonic-gate /*
1059*269Sericheng  * DL_CAPABILITY_REQ
10600Sstevel@tonic-gate  */
1061*269Sericheng /*ARGSUSED*/
1062*269Sericheng static boolean_t
1063*269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
10640Sstevel@tonic-gate {
1065*269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1066*269Sericheng 	dl_capability_sub_t *sp;
1067*269Sericheng 	size_t		size, len;
1068*269Sericheng 	offset_t	off, end;
1069*269Sericheng 	t_uscalar_t	dl_err;
1070*269Sericheng 	queue_t		*q = dsp->ds_wq;
1071*269Sericheng 	boolean_t	upgraded;
1072*269Sericheng 
1073*269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1074*269Sericheng 
1075*269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1076*269Sericheng 		dl_err = DL_BADPRIM;
1077*269Sericheng 		goto failed;
1078*269Sericheng 	}
1079*269Sericheng 
1080*269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1081*269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1082*269Sericheng 		dl_err = DL_OUTSTATE;
1083*269Sericheng 		goto failed;
1084*269Sericheng 	}
1085*269Sericheng 
1086*269Sericheng 	/*
1087*269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1088*269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1089*269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1090*269Sericheng 	 */
1091*269Sericheng 	if (dlp->dl_sub_length == 0) {
1092*269Sericheng 		/* callee drops lock */
1093*269Sericheng 		return (proto_capability_advertise(dsp, mp));
1094*269Sericheng 	}
1095*269Sericheng 
1096*269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1097*269Sericheng 		dl_err = DL_BADPRIM;
1098*269Sericheng 		goto failed;
1099*269Sericheng 	}
1100*269Sericheng 
1101*269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1102*269Sericheng 
1103*269Sericheng 	off = dlp->dl_sub_offset;
1104*269Sericheng 	len = dlp->dl_sub_length;
11050Sstevel@tonic-gate 
11060Sstevel@tonic-gate 	/*
1107*269Sericheng 	 * Walk the list of capabilities to be enabled.
11080Sstevel@tonic-gate 	 */
1109*269Sericheng 	upgraded = B_FALSE;
1110*269Sericheng 	for (end = off + len; off < end; ) {
1111*269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1112*269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1113*269Sericheng 
1114*269Sericheng 		if (off + size > end ||
1115*269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1116*269Sericheng 			dl_err = DL_BADPRIM;
1117*269Sericheng 			goto failed;
1118*269Sericheng 		}
1119*269Sericheng 
1120*269Sericheng 		switch (sp->dl_cap) {
1121*269Sericheng 		/*
1122*269Sericheng 		 * TCP/IP checksum offload to hardware.
1123*269Sericheng 		 */
1124*269Sericheng 		case DL_CAPAB_HCKSUM: {
1125*269Sericheng 			dl_capab_hcksum_t *hcksump;
1126*269Sericheng 			dl_capab_hcksum_t hcksum;
1127*269Sericheng 
1128*269Sericheng 			ASSERT(dsp->ds_mip->mi_cksum != 0);
1129*269Sericheng 
1130*269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1131*269Sericheng 			/*
1132*269Sericheng 			 * Copy for alignment.
1133*269Sericheng 			 */
1134*269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1135*269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1136*269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1137*269Sericheng 			break;
1138*269Sericheng 		}
1139*269Sericheng 
1140*269Sericheng 		/*
1141*269Sericheng 		 * IP polling interface.
1142*269Sericheng 		 */
1143*269Sericheng 		case DL_CAPAB_POLL: {
1144*269Sericheng 			dl_capab_poll_t *pollp;
1145*269Sericheng 			dl_capab_poll_t	poll;
1146*269Sericheng 
1147*269Sericheng 			pollp = (dl_capab_poll_t *)&sp[1];
1148*269Sericheng 			/*
1149*269Sericheng 			 * Copy for alignment.
1150*269Sericheng 			 */
1151*269Sericheng 			bcopy(pollp, &poll, sizeof (dl_capab_poll_t));
1152*269Sericheng 
1153*269Sericheng 			/*
1154*269Sericheng 			 * We need to become writer before enabling and/or
1155*269Sericheng 			 * disabling the polling interface.  If we couldn'
1156*269Sericheng 			 * upgrade, check state again after re-acquiring the
1157*269Sericheng 			 * lock to make sure we can proceed.
1158*269Sericheng 			 */
1159*269Sericheng 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
1160*269Sericheng 				rw_exit(&dsp->ds_lock);
1161*269Sericheng 				rw_enter(&dsp->ds_lock, RW_WRITER);
1162*269Sericheng 
1163*269Sericheng 				if (dsp->ds_dlstate == DL_UNATTACHED ||
1164*269Sericheng 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
1165*269Sericheng 					dl_err = DL_OUTSTATE;
1166*269Sericheng 					goto failed;
1167*269Sericheng 				}
1168*269Sericheng 			}
1169*269Sericheng 			upgraded = B_TRUE;
1170*269Sericheng 
1171*269Sericheng 			switch (poll.poll_flags) {
1172*269Sericheng 			default:
1173*269Sericheng 				/*FALLTHRU*/
1174*269Sericheng 			case POLL_DISABLE:
1175*269Sericheng 				proto_poll_disable(dsp);
1176*269Sericheng 				break;
1177*269Sericheng 
1178*269Sericheng 			case POLL_ENABLE:
1179*269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1180*269Sericheng 
1181*269Sericheng 				/*
1182*269Sericheng 				 * Make sure polling is disabled.
1183*269Sericheng 				 */
1184*269Sericheng 				proto_poll_disable(dsp);
1185*269Sericheng 
1186*269Sericheng 				/*
1187*269Sericheng 				 * Now attempt enable it.
1188*269Sericheng 				 */
1189*269Sericheng 				if (!proto_poll_enable(dsp, &poll))
1190*269Sericheng 					break;
1191*269Sericheng 
1192*269Sericheng 				bzero(&poll, sizeof (dl_capab_poll_t));
1193*269Sericheng 				poll.poll_flags = POLL_ENABLE;
1194*269Sericheng 				break;
1195*269Sericheng 			}
1196*269Sericheng 
1197*269Sericheng 			dlcapabsetqid(&(poll.poll_mid), dsp->ds_rq);
1198*269Sericheng 			bcopy(&poll, pollp, sizeof (dl_capab_poll_t));
1199*269Sericheng 			break;
1200*269Sericheng 		}
1201*269Sericheng 		default:
1202*269Sericheng 			break;
1203*269Sericheng 		}
1204*269Sericheng 
1205*269Sericheng 		off += size;
1206*269Sericheng 	}
1207*269Sericheng 	rw_exit(&dsp->ds_lock);
1208*269Sericheng 	qreply(q, mp);
1209*269Sericheng 	return (B_TRUE);
1210*269Sericheng failed:
1211*269Sericheng 	rw_exit(&dsp->ds_lock);
1212*269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1213*269Sericheng 	return (B_FALSE);
12140Sstevel@tonic-gate }
12150Sstevel@tonic-gate 
12160Sstevel@tonic-gate /*
1217*269Sericheng  * DL_NOTIFY_REQ
12180Sstevel@tonic-gate  */
1219*269Sericheng static boolean_t
1220*269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
12210Sstevel@tonic-gate {
1222*269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1223*269Sericheng 	t_uscalar_t	dl_err;
1224*269Sericheng 	queue_t		*q = dsp->ds_wq;
1225*269Sericheng 	uint_t		note =
1226*269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1227*269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1228*269Sericheng 	    DL_NOTE_PHYS_ADDR |
1229*269Sericheng 	    DL_NOTE_LINK_UP |
1230*269Sericheng 	    DL_NOTE_LINK_DOWN |
1231*269Sericheng 	    DL_NOTE_CAPAB_RENEG;
12320Sstevel@tonic-gate 
1233*269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1234*269Sericheng 		dl_err = DL_BADPRIM;
1235*269Sericheng 		goto failed;
1236*269Sericheng 	}
12370Sstevel@tonic-gate 
1238*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1239*269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1240*269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1241*269Sericheng 		dl_err = DL_OUTSTATE;
1242*269Sericheng 		goto failed;
12430Sstevel@tonic-gate 	}
12440Sstevel@tonic-gate 
1245*269Sericheng 	if (dsp->ds_mip->mi_stat[MAC_STAT_IFSPEED])
1246*269Sericheng 		note |= DL_NOTE_SPEED;
1247*269Sericheng 
1248*269Sericheng 	/*
1249*269Sericheng 	 * Cache the notifications that are being enabled.
1250*269Sericheng 	 */
1251*269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1252*269Sericheng 	rw_exit(&dsp->ds_lock);
1253*269Sericheng 	/*
1254*269Sericheng 	 * The ACK carries all notifications regardless of which set is
1255*269Sericheng 	 * being enabled.
1256*269Sericheng 	 */
1257*269Sericheng 	dlnotifyack(q, mp, note);
1258*269Sericheng 
1259*269Sericheng 	/*
1260*269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1261*269Sericheng 	 */
1262*269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1263*269Sericheng 	if (dsp->ds_notifications != 0) {
1264*269Sericheng 		rw_exit(&dsp->ds_lock);
1265*269Sericheng 		dld_str_notify_ind(dsp);
1266*269Sericheng 	} else {
1267*269Sericheng 		rw_exit(&dsp->ds_lock);
1268*269Sericheng 	}
1269*269Sericheng 	return (B_TRUE);
1270*269Sericheng failed:
1271*269Sericheng 	rw_exit(&dsp->ds_lock);
1272*269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1273*269Sericheng 	return (B_FALSE);
12740Sstevel@tonic-gate }
12750Sstevel@tonic-gate 
12760Sstevel@tonic-gate /*
1277*269Sericheng  * DL_UINTDATA_REQ
12780Sstevel@tonic-gate  */
1279*269Sericheng static boolean_t
1280*269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
12810Sstevel@tonic-gate {
1282*269Sericheng 	queue_t			*q = dsp->ds_wq;
1283*269Sericheng 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)udlp;
1284*269Sericheng 	off_t			off;
1285*269Sericheng 	size_t			len, size;
1286*269Sericheng 	const uint8_t		*addr;
1287*269Sericheng 	uint16_t		sap;
1288*269Sericheng 	uint_t			addr_length;
1289*269Sericheng 	mblk_t			*bp, *cont;
1290*269Sericheng 	uint32_t		start, stuff, end, value, flags;
1291*269Sericheng 	t_uscalar_t		dl_err;
1292*269Sericheng 
1293*269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1294*269Sericheng 
1295*269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1296*269Sericheng 		dl_err = DL_BADPRIM;
1297*269Sericheng 		goto failed;
1298*269Sericheng 	}
1299*269Sericheng 
1300*269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
1301*269Sericheng 		dl_err = DL_OUTSTATE;
1302*269Sericheng 		goto failed;
1303*269Sericheng 	}
1304*269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1305*269Sericheng 
1306*269Sericheng 	off = dlp->dl_dest_addr_offset;
1307*269Sericheng 	len = dlp->dl_dest_addr_length;
1308*269Sericheng 
1309*269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1310*269Sericheng 		dl_err = DL_BADPRIM;
1311*269Sericheng 		goto failed;
1312*269Sericheng 	}
1313*269Sericheng 
1314*269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1315*269Sericheng 		dl_err = DL_BADADDR;
1316*269Sericheng 		goto failed;
1317*269Sericheng 	}
1318*269Sericheng 
1319*269Sericheng 	addr = mp->b_rptr + off;
1320*269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1321*269Sericheng 
1322*269Sericheng 	/*
1323*269Sericheng 	 * Check the length of the packet and the block types.
1324*269Sericheng 	 */
1325*269Sericheng 	size = 0;
1326*269Sericheng 	cont = mp->b_cont;
1327*269Sericheng 	for (bp = cont; bp != NULL; bp = bp->b_cont) {
1328*269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1329*269Sericheng 			goto baddata;
1330*269Sericheng 
1331*269Sericheng 		size += MBLKL(bp);
1332*269Sericheng 	}
1333*269Sericheng 
1334*269Sericheng 	if (size > dsp->ds_mip->mi_sdu_max)
1335*269Sericheng 		goto baddata;
1336*269Sericheng 
1337*269Sericheng 	/*
1338*269Sericheng 	 * Build a packet header.
1339*269Sericheng 	 */
1340*269Sericheng 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dsp->ds_pri)) == NULL) {
1341*269Sericheng 		dl_err = DL_BADADDR;
1342*269Sericheng 		goto failed;
1343*269Sericheng 	}
1344*269Sericheng 
1345*269Sericheng 	/*
1346*269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1347*269Sericheng 	 */
1348*269Sericheng 	freeb(mp);
1349*269Sericheng 
1350*269Sericheng 	/*
1351*269Sericheng 	 * Transfer the checksum offload information if it is present.
1352*269Sericheng 	 */
1353*269Sericheng 	hcksum_retrieve(cont, NULL, NULL, &start, &stuff, &end, &value,
1354*269Sericheng 	    &flags);
1355*269Sericheng 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags,
1356*269Sericheng 	    0);
1357*269Sericheng 
1358*269Sericheng 	/*
1359*269Sericheng 	 * Link the payload onto the new header.
1360*269Sericheng 	 */
1361*269Sericheng 	ASSERT(bp->b_cont == NULL);
1362*269Sericheng 	bp->b_cont = cont;
1363*269Sericheng 
1364*269Sericheng 	str_mdata_fastpath_put(dsp, bp);
1365*269Sericheng 	rw_exit(&dsp->ds_lock);
1366*269Sericheng 	return (B_TRUE);
1367*269Sericheng failed:
1368*269Sericheng 	rw_exit(&dsp->ds_lock);
1369*269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1370*269Sericheng 	return (B_FALSE);
1371*269Sericheng 
1372*269Sericheng baddata:
1373*269Sericheng 	rw_exit(&dsp->ds_lock);
1374*269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1375*269Sericheng 	return (B_FALSE);
1376*269Sericheng }
1377*269Sericheng 
1378*269Sericheng /*
1379*269Sericheng  * DL_PASSIVE_REQ
1380*269Sericheng  */
1381*269Sericheng /* ARGSUSED */
1382*269Sericheng static boolean_t
1383*269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1384*269Sericheng {
1385*269Sericheng 	t_uscalar_t dl_err;
1386*269Sericheng 
1387*269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1388*269Sericheng 	/*
1389*269Sericheng 	 * If we've already become active by issuing an active primitive,
1390*269Sericheng 	 * then it's too late to try to become passive.
1391*269Sericheng 	 */
1392*269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1393*269Sericheng 		dl_err = DL_OUTSTATE;
1394*269Sericheng 		goto failed;
1395*269Sericheng 	}
1396*269Sericheng 
1397*269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1398*269Sericheng 		dl_err = DL_BADPRIM;
1399*269Sericheng 		goto failed;
1400*269Sericheng 	}
1401*269Sericheng 
1402*269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1403*269Sericheng 	rw_exit(&dsp->ds_lock);
1404*269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1405*269Sericheng 	return (B_TRUE);
1406*269Sericheng failed:
1407*269Sericheng 	rw_exit(&dsp->ds_lock);
1408*269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1409*269Sericheng 	return (B_FALSE);
1410*269Sericheng }
1411*269Sericheng 
1412*269Sericheng 
1413*269Sericheng /*
1414*269Sericheng  * Catch-all handler.
1415*269Sericheng  */
1416*269Sericheng static boolean_t
1417*269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1418*269Sericheng {
1419*269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1420*269Sericheng 	return (B_FALSE);
14210Sstevel@tonic-gate }
14220Sstevel@tonic-gate 
14230Sstevel@tonic-gate static void
14240Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
14250Sstevel@tonic-gate {
14260Sstevel@tonic-gate 	mac_handle_t	mh;
14270Sstevel@tonic-gate 
1428*269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
1429*269Sericheng 
14300Sstevel@tonic-gate 	if (!dsp->ds_polling)
14310Sstevel@tonic-gate 		return;
14320Sstevel@tonic-gate 
14330Sstevel@tonic-gate 	/*
14340Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
14350Sstevel@tonic-gate 	 */
14360Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
14370Sstevel@tonic-gate 
14380Sstevel@tonic-gate 	/*
14390Sstevel@tonic-gate 	 * Reset the resource_add callback.
14400Sstevel@tonic-gate 	 */
14410Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
14420Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
14430Sstevel@tonic-gate 
14440Sstevel@tonic-gate 	/*
14450Sstevel@tonic-gate 	 * Set receive function back to default.
14460Sstevel@tonic-gate 	 */
14470Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
14480Sstevel@tonic-gate 	    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
14490Sstevel@tonic-gate 
14500Sstevel@tonic-gate 	/*
14510Sstevel@tonic-gate 	 * Note that polling is disabled.
14520Sstevel@tonic-gate 	 */
14530Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
14540Sstevel@tonic-gate }
14550Sstevel@tonic-gate 
14560Sstevel@tonic-gate static boolean_t
14570Sstevel@tonic-gate proto_poll_enable(dld_str_t *dsp, dl_capab_poll_t *pollp)
14580Sstevel@tonic-gate {
14590Sstevel@tonic-gate 	mac_handle_t	mh;
14600Sstevel@tonic-gate 
1461*269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
14620Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
14630Sstevel@tonic-gate 
14640Sstevel@tonic-gate 	/*
14650Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
14660Sstevel@tonic-gate 	 * has been enabled.
14670Sstevel@tonic-gate 	 */
14680Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
14690Sstevel@tonic-gate 		return (B_FALSE);
14700Sstevel@tonic-gate 
14710Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate 	/*
14740Sstevel@tonic-gate 	 * Register resources.
14750Sstevel@tonic-gate 	 */
14760Sstevel@tonic-gate 	mac_resource_set(mh, (mac_resource_add_t)pollp->poll_ring_add,
14770Sstevel@tonic-gate 	    (void *)pollp->poll_rx_handle);
14780Sstevel@tonic-gate 	mac_resources(mh);
14790Sstevel@tonic-gate 
14800Sstevel@tonic-gate 	/*
14810Sstevel@tonic-gate 	 * Set the receive function.
14820Sstevel@tonic-gate 	 */
14830Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->poll_rx,
14840Sstevel@tonic-gate 	    (void *)pollp->poll_rx_handle);
14850Sstevel@tonic-gate 
14860Sstevel@tonic-gate 	/*
14870Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
14880Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
14890Sstevel@tonic-gate 	 */
14900Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
14910Sstevel@tonic-gate 	return (B_TRUE);
14920Sstevel@tonic-gate }
14930Sstevel@tonic-gate 
14940Sstevel@tonic-gate /*
14950Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
14960Sstevel@tonic-gate  */
1497*269Sericheng static boolean_t
1498*269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
14990Sstevel@tonic-gate {
15000Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
15010Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
15020Sstevel@tonic-gate 	size_t			subsize;
15030Sstevel@tonic-gate 	dl_capab_poll_t		poll;
15040Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
15050Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
15060Sstevel@tonic-gate 	uint8_t			*ptr;
15070Sstevel@tonic-gate 	uint32_t		cksum;
15080Sstevel@tonic-gate 	boolean_t		poll_cap;
1509*269Sericheng 	queue_t			*q = dsp->ds_wq;
1510*269Sericheng 	mblk_t			*mp1;
1511*269Sericheng 
1512*269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
15130Sstevel@tonic-gate 
15140Sstevel@tonic-gate 	/*
15150Sstevel@tonic-gate 	 * Initially assume no capabilities.
15160Sstevel@tonic-gate 	 */
15170Sstevel@tonic-gate 	subsize = 0;
15180Sstevel@tonic-gate 
15190Sstevel@tonic-gate 	/*
15200Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
15210Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
15220Sstevel@tonic-gate 	 * then reserve space for that capability.
15230Sstevel@tonic-gate 	 */
15240Sstevel@tonic-gate 	poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) &&
15250Sstevel@tonic-gate 	    !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE));
15260Sstevel@tonic-gate 	if (poll_cap) {
15270Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
15280Sstevel@tonic-gate 		    sizeof (dl_capab_poll_t);
15290Sstevel@tonic-gate 	}
15300Sstevel@tonic-gate 
15310Sstevel@tonic-gate 	/*
15320Sstevel@tonic-gate 	 * If the MAC interface supports checksum offload then reserve
15330Sstevel@tonic-gate 	 * space for the DL_CAPAB_HCKSUM capability.
15340Sstevel@tonic-gate 	 */
15350Sstevel@tonic-gate 	if ((cksum = dsp->ds_mip->mi_cksum) != 0) {
15360Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
15370Sstevel@tonic-gate 		    sizeof (dl_capab_hcksum_t);
15380Sstevel@tonic-gate 	}
15390Sstevel@tonic-gate 
15400Sstevel@tonic-gate 	/*
15410Sstevel@tonic-gate 	 * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then
15420Sstevel@tonic-gate 	 * reserve space for it.
15430Sstevel@tonic-gate 	 */
15440Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
15450Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
15460Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
15470Sstevel@tonic-gate 	}
15480Sstevel@tonic-gate 
15490Sstevel@tonic-gate 	/*
1550*269Sericheng 	 * If there are no capabilities to advertise or if we
1551*269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
15520Sstevel@tonic-gate 	 */
1553*269Sericheng 	if (subsize == 0 || (mp1 = reallocb(mp,
1554*269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1555*269Sericheng 		rw_exit(&dsp->ds_lock);
1556*269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1557*269Sericheng 		return (B_FALSE);
15580Sstevel@tonic-gate 	}
15590Sstevel@tonic-gate 
1560*269Sericheng 	mp = mp1;
1561*269Sericheng 	DB_TYPE(mp) = M_PROTO;
1562*269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1563*269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
15640Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
15650Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
15660Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
15670Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
15680Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
15690Sstevel@tonic-gate 
15700Sstevel@tonic-gate 	/*
15710Sstevel@tonic-gate 	 * IP polling interface.
15720Sstevel@tonic-gate 	 */
15730Sstevel@tonic-gate 	if (poll_cap) {
15740Sstevel@tonic-gate 		/*
1575*269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
1576*269Sericheng 		 * we need to become writer before doing so.
15770Sstevel@tonic-gate 		 */
1578*269Sericheng 		if (!rw_tryupgrade(&dsp->ds_lock)) {
1579*269Sericheng 			rw_exit(&dsp->ds_lock);
1580*269Sericheng 			rw_enter(&dsp->ds_lock, RW_WRITER);
1581*269Sericheng 		}
15820Sstevel@tonic-gate 
1583*269Sericheng 		/*
1584*269Sericheng 		 * Check if polling state has changed after we re-acquired
1585*269Sericheng 		 * the lock above, so that we don't mis-advertise it.
1586*269Sericheng 		 */
1587*269Sericheng 		poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) &&
1588*269Sericheng 		    !(dld_opt & DLD_OPT_NO_POLL) &&
1589*269Sericheng 		    (dsp->ds_vid == VLAN_ID_NONE));
15900Sstevel@tonic-gate 
1591*269Sericheng 		if (!poll_cap) {
1592*269Sericheng 			int poll_capab_size;
1593*269Sericheng 
1594*269Sericheng 			rw_downgrade(&dsp->ds_lock);
1595*269Sericheng 
1596*269Sericheng 			poll_capab_size = sizeof (dl_capability_sub_t) +
1597*269Sericheng 			    sizeof (dl_capab_poll_t);
15980Sstevel@tonic-gate 
1599*269Sericheng 			mp->b_wptr -= poll_capab_size;
1600*269Sericheng 			subsize -= poll_capab_size;
1601*269Sericheng 			dlap->dl_sub_length = subsize;
1602*269Sericheng 		} else {
1603*269Sericheng 			proto_poll_disable(dsp);
1604*269Sericheng 
1605*269Sericheng 			rw_downgrade(&dsp->ds_lock);
1606*269Sericheng 
1607*269Sericheng 			dlsp = (dl_capability_sub_t *)ptr;
1608*269Sericheng 
1609*269Sericheng 			dlsp->dl_cap = DL_CAPAB_POLL;
1610*269Sericheng 			dlsp->dl_length = sizeof (dl_capab_poll_t);
1611*269Sericheng 			ptr += sizeof (dl_capability_sub_t);
16120Sstevel@tonic-gate 
1613*269Sericheng 			bzero(&poll, sizeof (dl_capab_poll_t));
1614*269Sericheng 			poll.poll_version = POLL_VERSION_1;
1615*269Sericheng 			poll.poll_flags = POLL_CAPABLE;
1616*269Sericheng 			poll.poll_tx_handle = (uintptr_t)dsp;
1617*269Sericheng 			poll.poll_tx = (uintptr_t)str_mdata_fastpath_put;
1618*269Sericheng 
1619*269Sericheng 			dlcapabsetqid(&(poll.poll_mid), dsp->ds_rq);
1620*269Sericheng 			bcopy(&poll, ptr, sizeof (dl_capab_poll_t));
1621*269Sericheng 			ptr += sizeof (dl_capab_poll_t);
1622*269Sericheng 		}
16230Sstevel@tonic-gate 	}
16240Sstevel@tonic-gate 
1625*269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
1626*269Sericheng 
16270Sstevel@tonic-gate 	/*
16280Sstevel@tonic-gate 	 * TCP/IP checksum offload.
16290Sstevel@tonic-gate 	 */
16300Sstevel@tonic-gate 	if (cksum != 0) {
16310Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16320Sstevel@tonic-gate 
16330Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
16340Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
16350Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16360Sstevel@tonic-gate 
16370Sstevel@tonic-gate 		bzero(&hcksum, sizeof (dl_capab_hcksum_t));
16380Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
16390Sstevel@tonic-gate 		hcksum.hcksum_txflags = cksum;
16400Sstevel@tonic-gate 
16410Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
16420Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
16430Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
16440Sstevel@tonic-gate 	}
16450Sstevel@tonic-gate 
16460Sstevel@tonic-gate 	/*
16470Sstevel@tonic-gate 	 * Zero copy
16480Sstevel@tonic-gate 	 */
16490Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
16500Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
16510Sstevel@tonic-gate 
16520Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
16530Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
16540Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
16550Sstevel@tonic-gate 
16560Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
16570Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
16580Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
16590Sstevel@tonic-gate 
16600Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
16610Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
16620Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
16630Sstevel@tonic-gate 	}
16640Sstevel@tonic-gate 
16650Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1666*269Sericheng 
1667*269Sericheng 	rw_exit(&dsp->ds_lock);
1668*269Sericheng 	qreply(q, mp);
1669*269Sericheng 	return (B_TRUE);
16700Sstevel@tonic-gate }
1671