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 /*
23*1353Sericheng  * Copyright 2006 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>
401184Skrgopi #include <sys/cpuvar.h>
410Sstevel@tonic-gate #include <sys/dlpi.h>
420Sstevel@tonic-gate #include <netinet/in.h>
430Sstevel@tonic-gate #include <sys/sdt.h>
440Sstevel@tonic-gate #include <sys/strsubr.h>
450Sstevel@tonic-gate #include <sys/vlan.h>
460Sstevel@tonic-gate #include <sys/mac.h>
470Sstevel@tonic-gate #include <sys/dls.h>
480Sstevel@tonic-gate #include <sys/dld.h>
490Sstevel@tonic-gate #include <sys/dld_impl.h>
501184Skrgopi #include <sys/dls_soft_ring.h>
510Sstevel@tonic-gate 
520Sstevel@tonic-gate typedef boolean_t proto_reqfunc_t(dld_str_t *, union DL_primitives *, mblk_t *);
530Sstevel@tonic-gate 
540Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
550Sstevel@tonic-gate     proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
560Sstevel@tonic-gate     proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
570Sstevel@tonic-gate     proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
580Sstevel@tonic-gate     proto_notify_req, proto_unitdata_req, proto_passive_req;
590Sstevel@tonic-gate 
60269Sericheng static void proto_poll_disable(dld_str_t *);
611184Skrgopi static boolean_t proto_poll_enable(dld_str_t *, dl_capab_dls_t *);
62269Sericheng static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
630Sstevel@tonic-gate 
64*1353Sericheng static task_func_t proto_process_unbind_req, proto_process_detach_req;
65*1353Sericheng 
661184Skrgopi static void proto_soft_ring_disable(dld_str_t *);
671184Skrgopi static boolean_t proto_soft_ring_enable(dld_str_t *, dl_capab_dls_t *);
681184Skrgopi static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
691184Skrgopi static void proto_change_soft_ring_fanout(dld_str_t *, int);
701184Skrgopi 
710Sstevel@tonic-gate #define	DL_ACK_PENDING(state) \
720Sstevel@tonic-gate 	((state) == DL_ATTACH_PENDING || \
730Sstevel@tonic-gate 	(state) == DL_DETACH_PENDING || \
740Sstevel@tonic-gate 	(state) == DL_BIND_PENDING || \
750Sstevel@tonic-gate 	(state) == DL_UNBIND_PENDING)
760Sstevel@tonic-gate 
770Sstevel@tonic-gate /*
78269Sericheng  * Process a DLPI protocol message.
79269Sericheng  * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
80269Sericheng  * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
81269Sericheng  * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
82269Sericheng  * as 'passive' and forbids it from being subsequently made 'active'
83269Sericheng  * by the above primitives.
840Sstevel@tonic-gate  */
850Sstevel@tonic-gate void
860Sstevel@tonic-gate dld_proto(dld_str_t *dsp, mblk_t *mp)
870Sstevel@tonic-gate {
880Sstevel@tonic-gate 	union DL_primitives	*udlp;
890Sstevel@tonic-gate 	t_uscalar_t		prim;
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (t_uscalar_t)) {
920Sstevel@tonic-gate 		freemsg(mp);
930Sstevel@tonic-gate 		return;
940Sstevel@tonic-gate 	}
950Sstevel@tonic-gate 
960Sstevel@tonic-gate 	udlp = (union DL_primitives *)mp->b_rptr;
970Sstevel@tonic-gate 	prim = udlp->dl_primitive;
980Sstevel@tonic-gate 
99269Sericheng 	switch (prim) {
100269Sericheng 	case DL_INFO_REQ:
101269Sericheng 		(void) proto_info_req(dsp, udlp, mp);
102269Sericheng 		break;
103269Sericheng 	case DL_BIND_REQ:
104269Sericheng 		(void) proto_bind_req(dsp, udlp, mp);
105269Sericheng 		break;
106269Sericheng 	case DL_UNBIND_REQ:
107269Sericheng 		(void) proto_unbind_req(dsp, udlp, mp);
108269Sericheng 		break;
109269Sericheng 	case DL_UNITDATA_REQ:
110269Sericheng 		(void) proto_unitdata_req(dsp, udlp, mp);
111269Sericheng 		break;
112269Sericheng 	case DL_UDQOS_REQ:
113269Sericheng 		(void) proto_udqos_req(dsp, udlp, mp);
114269Sericheng 		break;
115269Sericheng 	case DL_ATTACH_REQ:
116269Sericheng 		(void) proto_attach_req(dsp, udlp, mp);
117269Sericheng 		break;
118269Sericheng 	case DL_DETACH_REQ:
119269Sericheng 		(void) proto_detach_req(dsp, udlp, mp);
120269Sericheng 		break;
121269Sericheng 	case DL_ENABMULTI_REQ:
122269Sericheng 		(void) proto_enabmulti_req(dsp, udlp, mp);
123269Sericheng 		break;
124269Sericheng 	case DL_DISABMULTI_REQ:
125269Sericheng 		(void) proto_disabmulti_req(dsp, udlp, mp);
126269Sericheng 		break;
127269Sericheng 	case DL_PROMISCON_REQ:
128269Sericheng 		(void) proto_promiscon_req(dsp, udlp, mp);
129269Sericheng 		break;
130269Sericheng 	case DL_PROMISCOFF_REQ:
131269Sericheng 		(void) proto_promiscoff_req(dsp, udlp, mp);
132269Sericheng 		break;
133269Sericheng 	case DL_PHYS_ADDR_REQ:
134269Sericheng 		(void) proto_physaddr_req(dsp, udlp, mp);
135269Sericheng 		break;
136269Sericheng 	case DL_SET_PHYS_ADDR_REQ:
137269Sericheng 		(void) proto_setphysaddr_req(dsp, udlp, mp);
138269Sericheng 		break;
139269Sericheng 	case DL_NOTIFY_REQ:
140269Sericheng 		(void) proto_notify_req(dsp, udlp, mp);
141269Sericheng 		break;
142269Sericheng 	case DL_CAPABILITY_REQ:
143269Sericheng 		(void) proto_capability_req(dsp, udlp, mp);
144269Sericheng 		break;
145269Sericheng 	case DL_PASSIVE_REQ:
146269Sericheng 		(void) proto_passive_req(dsp, udlp, mp);
147269Sericheng 		break;
148269Sericheng 	default:
149269Sericheng 		(void) proto_req(dsp, udlp, mp);
150269Sericheng 		break;
1510Sstevel@tonic-gate 	}
1520Sstevel@tonic-gate }
1530Sstevel@tonic-gate 
1540Sstevel@tonic-gate /*
155*1353Sericheng  * Finish any pending operations.
156*1353Sericheng  * Requests that need to be processed asynchronously will be handled
157*1353Sericheng  * by a separate thread. After this function returns, other threads
158*1353Sericheng  * will be allowed to enter dld; they will not be able to do anything
159*1353Sericheng  * until ds_dlstate transitions to a non-pending state.
1600Sstevel@tonic-gate  */
161269Sericheng void
162269Sericheng dld_finish_pending_ops(dld_str_t *dsp)
1630Sstevel@tonic-gate {
164*1353Sericheng 	task_func_t *op = NULL;
165*1353Sericheng 
166269Sericheng 	ASSERT(MUTEX_HELD(&dsp->ds_thr_lock));
167269Sericheng 	ASSERT(dsp->ds_thr == 0);
1680Sstevel@tonic-gate 
169*1353Sericheng 	op = dsp->ds_pending_op;
170*1353Sericheng 	dsp->ds_pending_op = NULL;
171*1353Sericheng 	mutex_exit(&dsp->ds_thr_lock);
172*1353Sericheng 	if (op != NULL)
173*1353Sericheng 		(void) taskq_dispatch(system_taskq, op, dsp, TQ_SLEEP);
1740Sstevel@tonic-gate }
1750Sstevel@tonic-gate 
176269Sericheng #define	NEG(x)	-(x)
1770Sstevel@tonic-gate 
1780Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1790Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
1800Sstevel@tonic-gate 	uint8_t			dl_addr[MAXADDRLEN + sizeof (uint16_t)];
1810Sstevel@tonic-gate 	uint8_t			dl_brdcst_addr[MAXADDRLEN];
1820Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1830Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1840Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1850Sstevel@tonic-gate 
1860Sstevel@tonic-gate /*
187269Sericheng  * DL_INFO_REQ
1880Sstevel@tonic-gate  */
189269Sericheng /*ARGSUSED*/
190269Sericheng static boolean_t
191269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1920Sstevel@tonic-gate {
1930Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1940Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
1950Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
1960Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
1970Sstevel@tonic-gate 	uint8_t			*addr;
1980Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
1990Sstevel@tonic-gate 	uint_t			addr_length;
2000Sstevel@tonic-gate 	uint_t			sap_length;
201269Sericheng 	mac_info_t		minfo;
202269Sericheng 	mac_info_t		*minfop;
203269Sericheng 	queue_t			*q = dsp->ds_wq;
2040Sstevel@tonic-gate 
2050Sstevel@tonic-gate 	/*
2060Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
2070Sstevel@tonic-gate 	 * wrapper structure defined above.
2080Sstevel@tonic-gate 	 */
209269Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
2100Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
211269Sericheng 		return (B_FALSE);
212269Sericheng 
213269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
2160Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
2190Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
2200Sstevel@tonic-gate 
2210Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
2220Sstevel@tonic-gate 
2230Sstevel@tonic-gate 	/*
2240Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
2250Sstevel@tonic-gate 	 */
2260Sstevel@tonic-gate 	addr = dlwp->dl_addr;
2270Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
2280Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
2290Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
2300Sstevel@tonic-gate 
2310Sstevel@tonic-gate 	/*
2320Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
2330Sstevel@tonic-gate 	 * nodes.
2340Sstevel@tonic-gate 	 */
2350Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
2360Sstevel@tonic-gate 	dlp->dl_version = DL_VERSION_2;
2370Sstevel@tonic-gate 
2380Sstevel@tonic-gate 	/*
239269Sericheng 	 * Set the style of the provider
2400Sstevel@tonic-gate 	 */
241269Sericheng 	dlp->dl_provider_style = dsp->ds_style;
2420Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
2430Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 	/*
2460Sstevel@tonic-gate 	 * Set the current DLPI state.
2470Sstevel@tonic-gate 	 */
2480Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
2490Sstevel@tonic-gate 
2500Sstevel@tonic-gate 	/*
251269Sericheng 	 * Gratuitously set the media type. This is to deal with modules
252269Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
2530Sstevel@tonic-gate 	 * being completed.
2540Sstevel@tonic-gate 	 */
2550Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2560Sstevel@tonic-gate 
2570Sstevel@tonic-gate 	/*
258269Sericheng 	 * If the stream is not at least attached we try to retrieve the
259269Sericheng 	 * mac_info using mac_info_get()
2600Sstevel@tonic-gate 	 */
2610Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2620Sstevel@tonic-gate 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
263269Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
264269Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
265269Sericheng 			/*
266269Sericheng 			 * Cannot find mac_info. giving up.
267269Sericheng 			 */
268269Sericheng 			goto done;
269269Sericheng 		}
270269Sericheng 		minfop = &minfo;
271269Sericheng 	} else {
272269Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
273269Sericheng 	}
2740Sstevel@tonic-gate 
2750Sstevel@tonic-gate 	/*
2760Sstevel@tonic-gate 	 * Set the media type (properly this time).
2770Sstevel@tonic-gate 	 */
278269Sericheng 	dlp->dl_mac_type = minfop->mi_media;
2790Sstevel@tonic-gate 
2800Sstevel@tonic-gate 	/*
2810Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2820Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2830Sstevel@tonic-gate 	 */
2840Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2850Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2860Sstevel@tonic-gate 
2870Sstevel@tonic-gate 	/*
2880Sstevel@tonic-gate 	 * Set the minimum and maximum payload sizes.
2890Sstevel@tonic-gate 	 */
290269Sericheng 	dlp->dl_min_sdu = minfop->mi_sdu_min;
291269Sericheng 	dlp->dl_max_sdu = minfop->mi_sdu_max;
2920Sstevel@tonic-gate 
293269Sericheng 	addr_length = minfop->mi_addr_length;
2940Sstevel@tonic-gate 	ASSERT(addr_length != 0);
2950Sstevel@tonic-gate 
2960Sstevel@tonic-gate 	/*
2970Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2980Sstevel@tonic-gate 	 */
2990Sstevel@tonic-gate 	dlp->dl_brdcst_addr_offset = (uintptr_t)brdcst_addr - (uintptr_t)dlp;
300269Sericheng 	bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
3010Sstevel@tonic-gate 	dlp->dl_brdcst_addr_length = addr_length;
3020Sstevel@tonic-gate 
3030Sstevel@tonic-gate 	/*
3040Sstevel@tonic-gate 	 * We only support QoS information for VLAN interfaces.
3050Sstevel@tonic-gate 	 */
3060Sstevel@tonic-gate 	if (dsp->ds_vid != VLAN_ID_NONE) {
3070Sstevel@tonic-gate 		dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
3080Sstevel@tonic-gate 		dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
3090Sstevel@tonic-gate 
3100Sstevel@tonic-gate 		rangep->dl_qos_type = DL_QOS_CL_RANGE1;
3110Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
3120Sstevel@tonic-gate 		rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
3130Sstevel@tonic-gate 		rangep->dl_protection.dl_min = DL_UNKNOWN;
3140Sstevel@tonic-gate 		rangep->dl_protection.dl_max = DL_UNKNOWN;
3150Sstevel@tonic-gate 		rangep->dl_residual_error = DL_UNKNOWN;
3160Sstevel@tonic-gate 
3170Sstevel@tonic-gate 		/*
3180Sstevel@tonic-gate 		 * Specify the supported range of priorities.
3190Sstevel@tonic-gate 		 */
3200Sstevel@tonic-gate 		rangep->dl_priority.dl_min = 0;
3210Sstevel@tonic-gate 		rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
3220Sstevel@tonic-gate 
3230Sstevel@tonic-gate 		dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
3240Sstevel@tonic-gate 		dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
3250Sstevel@tonic-gate 
3260Sstevel@tonic-gate 		selp->dl_qos_type = DL_QOS_CL_SEL1;
3270Sstevel@tonic-gate 		selp->dl_trans_delay = DL_UNKNOWN;
3280Sstevel@tonic-gate 		selp->dl_protection = DL_UNKNOWN;
3290Sstevel@tonic-gate 		selp->dl_residual_error = DL_UNKNOWN;
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate 		/*
3320Sstevel@tonic-gate 		 * Specify the current priority (which can be changed by
3330Sstevel@tonic-gate 		 * the DL_UDQOS_REQ primitive).
3340Sstevel@tonic-gate 		 */
3350Sstevel@tonic-gate 		selp->dl_priority = dsp->ds_pri;
3360Sstevel@tonic-gate 	} else {
3370Sstevel@tonic-gate 		/*
3380Sstevel@tonic-gate 		 * Shorten the buffer to lose the unused QoS information
339269Sericheng 		 * structures.
3400Sstevel@tonic-gate 		 */
3410Sstevel@tonic-gate 		mp->b_wptr = (uint8_t *)rangep;
3420Sstevel@tonic-gate 	}
3430Sstevel@tonic-gate 
3440Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
3450Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
3460Sstevel@tonic-gate 		/*
3470Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
3480Sstevel@tonic-gate 		 * DLSAP address.
3490Sstevel@tonic-gate 		 */
3500Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
3510Sstevel@tonic-gate 		bcopy(dsp->ds_curr_addr, addr, addr_length);
3520Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3530Sstevel@tonic-gate 	}
3540Sstevel@tonic-gate 
3550Sstevel@tonic-gate done:
3560Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
3570Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
3580Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3590Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3600Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3610Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3620Sstevel@tonic-gate 
363269Sericheng 	rw_exit(&dsp->ds_lock);
364269Sericheng 
365269Sericheng 	qreply(q, mp);
366269Sericheng 	return (B_TRUE);
367269Sericheng }
368269Sericheng 
369269Sericheng /*
370269Sericheng  * DL_ATTACH_REQ
371269Sericheng  */
372269Sericheng static boolean_t
373269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
374269Sericheng {
375269Sericheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)udlp;
376269Sericheng 	int		err = 0;
377269Sericheng 	t_uscalar_t	dl_err;
378269Sericheng 	queue_t		*q = dsp->ds_wq;
379269Sericheng 
380269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
381269Sericheng 
382269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
383269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
384269Sericheng 		dl_err = DL_BADPRIM;
385269Sericheng 		goto failed;
386269Sericheng 	}
387269Sericheng 
388269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
389269Sericheng 		dl_err = DL_OUTSTATE;
390269Sericheng 		goto failed;
391269Sericheng 	}
392269Sericheng 
393269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
394269Sericheng 
395269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
396269Sericheng 	if (err != 0) {
397269Sericheng 		switch (err) {
398269Sericheng 		case ENOENT:
399269Sericheng 			dl_err = DL_BADPPA;
400269Sericheng 			err = 0;
401269Sericheng 			break;
402269Sericheng 		default:
403269Sericheng 			dl_err = DL_SYSERR;
404269Sericheng 			break;
405269Sericheng 		}
406269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
407269Sericheng 		goto failed;
408269Sericheng 	}
409269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
410269Sericheng 	rw_exit(&dsp->ds_lock);
411269Sericheng 
412269Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
413269Sericheng 	return (B_TRUE);
414269Sericheng failed:
415269Sericheng 	rw_exit(&dsp->ds_lock);
416269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
417269Sericheng 	return (B_FALSE);
4180Sstevel@tonic-gate }
4190Sstevel@tonic-gate 
4200Sstevel@tonic-gate /*
421269Sericheng  * DL_DETACH_REQ
4220Sstevel@tonic-gate  */
423*1353Sericheng static void
424*1353Sericheng proto_process_detach_req(void *arg)
425*1353Sericheng {
426*1353Sericheng 	dld_str_t	*dsp = arg;
427*1353Sericheng 	mblk_t		*mp;
428*1353Sericheng 
429*1353Sericheng 	/*
430*1353Sericheng 	 * We don't need to hold locks because no other thread
431*1353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
432*1353Sericheng 	 */
433*1353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
434*1353Sericheng 	ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING);
435*1353Sericheng 
436*1353Sericheng 	mp = dsp->ds_pending_req;
437*1353Sericheng 	dsp->ds_pending_req = NULL;
438*1353Sericheng 	dld_str_detach(dsp);
439*1353Sericheng 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
440*1353Sericheng 
441*1353Sericheng 	DLD_WAKEUP(dsp);
442*1353Sericheng }
443*1353Sericheng 
444269Sericheng /*ARGSUSED*/
445269Sericheng static boolean_t
446269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4470Sstevel@tonic-gate {
448269Sericheng 	queue_t		*q = dsp->ds_wq;
449269Sericheng 	t_uscalar_t	dl_err;
4500Sstevel@tonic-gate 
451269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
4520Sstevel@tonic-gate 
453269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
454269Sericheng 		dl_err = DL_BADPRIM;
455269Sericheng 		goto failed;
456269Sericheng 	}
4570Sstevel@tonic-gate 
458269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
459269Sericheng 		dl_err = DL_OUTSTATE;
460269Sericheng 		goto failed;
4610Sstevel@tonic-gate 	}
4620Sstevel@tonic-gate 
463269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
464269Sericheng 		dl_err = DL_BADPRIM;
465269Sericheng 		goto failed;
466269Sericheng 	}
467269Sericheng 
468269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
469269Sericheng 
470269Sericheng 	/*
471269Sericheng 	 * Complete the detach when the driver is single-threaded.
472269Sericheng 	 */
473269Sericheng 	mutex_enter(&dsp->ds_thr_lock);
474*1353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
475*1353Sericheng 	dsp->ds_pending_req = mp;
476*1353Sericheng 	dsp->ds_pending_op = proto_process_detach_req;
477*1353Sericheng 	dsp->ds_pending_cnt++;
478269Sericheng 	mutex_exit(&dsp->ds_thr_lock);
479269Sericheng 	rw_exit(&dsp->ds_lock);
480269Sericheng 
481269Sericheng 	return (B_TRUE);
482269Sericheng failed:
483269Sericheng 	rw_exit(&dsp->ds_lock);
484269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
485269Sericheng 	return (B_FALSE);
4860Sstevel@tonic-gate }
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate /*
489269Sericheng  * DL_BIND_REQ
4900Sstevel@tonic-gate  */
491269Sericheng static boolean_t
492269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4930Sstevel@tonic-gate {
494269Sericheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)udlp;
495269Sericheng 	int		err = 0;
496269Sericheng 	uint8_t		addr[MAXADDRLEN];
497269Sericheng 	uint_t		addr_length;
498269Sericheng 	t_uscalar_t	dl_err;
499269Sericheng 	t_scalar_t	sap;
500269Sericheng 	queue_t		*q = dsp->ds_wq;
501269Sericheng 
502269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
503269Sericheng 		dl_err = DL_BADPRIM;
504269Sericheng 		goto failed;
505269Sericheng 	}
506269Sericheng 
507269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
508269Sericheng 		dl_err = DL_NOAUTO;
509269Sericheng 		goto failed;
510269Sericheng 	}
511269Sericheng 
512269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
513269Sericheng 		dl_err = DL_UNSUPPORTED;
514269Sericheng 		goto failed;
515269Sericheng 	}
516269Sericheng 
517269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
518269Sericheng 
519269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
520269Sericheng 		dl_err = DL_OUTSTATE;
521269Sericheng 		goto failed;
522269Sericheng 	}
5230Sstevel@tonic-gate 
524269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
525269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
526269Sericheng 		dl_err = DL_SYSERR;
527269Sericheng 		err = EBUSY;
528269Sericheng 		goto failed;
529269Sericheng 	}
530269Sericheng 
531269Sericheng 	dsp->ds_dlstate = DL_BIND_PENDING;
532269Sericheng 	/*
533269Sericheng 	 * Set the receive callback.
534269Sericheng 	 */
535269Sericheng 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ?
536269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
5370Sstevel@tonic-gate 
538269Sericheng 	/*
539269Sericheng 	 * Bind the channel such that it can receive packets.
540269Sericheng 	 */
541269Sericheng 	sap = dsp->ds_sap = dlp->dl_sap;
542269Sericheng 	err = dls_bind(dsp->ds_dc, dlp->dl_sap);
543269Sericheng 	if (err != 0) {
544269Sericheng 		switch (err) {
545269Sericheng 		case EINVAL:
546269Sericheng 			dl_err = DL_BADADDR;
547269Sericheng 			err = 0;
548269Sericheng 			break;
549269Sericheng 		default:
550269Sericheng 			dl_err = DL_SYSERR;
551269Sericheng 			break;
552269Sericheng 		}
553269Sericheng 		dsp->ds_dlstate = DL_UNBOUND;
554269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
555269Sericheng 			dls_active_clear(dsp->ds_dc);
556269Sericheng 
5570Sstevel@tonic-gate 		goto failed;
558269Sericheng 	}
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	/*
5610Sstevel@tonic-gate 	 * Copy in MAC address.
5620Sstevel@tonic-gate 	 */
5630Sstevel@tonic-gate 	addr_length = dsp->ds_mip->mi_addr_length;
5640Sstevel@tonic-gate 	bcopy(dsp->ds_curr_addr, addr, addr_length);
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	/*
5670Sstevel@tonic-gate 	 * Copy in the DLSAP.
5680Sstevel@tonic-gate 	 */
5690Sstevel@tonic-gate 	*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
5700Sstevel@tonic-gate 	addr_length += sizeof (uint16_t);
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
573269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
574269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
5750Sstevel@tonic-gate 
576269Sericheng 	rw_exit(&dsp->ds_lock);
577269Sericheng 
578269Sericheng 	dlbindack(q, mp, sap, (void *)addr, addr_length, 0, 0);
579269Sericheng 	return (B_TRUE);
5800Sstevel@tonic-gate failed:
581269Sericheng 	rw_exit(&dsp->ds_lock);
582269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
583269Sericheng 	return (B_FALSE);
5840Sstevel@tonic-gate }
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate /*
587269Sericheng  * DL_UNBIND_REQ
5880Sstevel@tonic-gate  */
589269Sericheng /*ARGSUSED*/
590*1353Sericheng static void
591*1353Sericheng proto_process_unbind_req(void *arg)
5920Sstevel@tonic-gate {
593*1353Sericheng 	dld_str_t	*dsp = arg;
594*1353Sericheng 	mblk_t		*mp;
595269Sericheng 
596*1353Sericheng 	/*
597*1353Sericheng 	 * We don't need to hold locks because no other thread
598*1353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
599*1353Sericheng 	 */
600*1353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
601*1353Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBIND_PENDING);
602269Sericheng 
603269Sericheng 	/*
604269Sericheng 	 * Flush any remaining packets scheduled for transmission.
605269Sericheng 	 */
606269Sericheng 	dld_tx_flush(dsp);
607269Sericheng 
608269Sericheng 	/*
609269Sericheng 	 * Unbind the channel to stop packets being received.
610269Sericheng 	 */
611269Sericheng 	dls_unbind(dsp->ds_dc);
612269Sericheng 
613269Sericheng 	/*
614269Sericheng 	 * Disable polling mode, if it is enabled.
615269Sericheng 	 */
616269Sericheng 	proto_poll_disable(dsp);
617269Sericheng 
618269Sericheng 	/*
619269Sericheng 	 * Clear the receive callback.
620269Sericheng 	 */
621269Sericheng 	dls_rx_set(dsp->ds_dc, NULL, NULL);
622269Sericheng 
623269Sericheng 	/*
624269Sericheng 	 * Set the mode back to the default (unitdata).
625269Sericheng 	 */
626269Sericheng 	dsp->ds_mode = DLD_UNITDATA;
627269Sericheng 
6281184Skrgopi 	/*
6291184Skrgopi 	 * If soft rings were enabled, the workers
630*1353Sericheng 	 * should be quiesced. We cannot check for
6311184Skrgopi 	 * ds_soft_ring flag because
6321184Skrgopi 	 * proto_soft_ring_disable() called from
6331184Skrgopi 	 * proto_capability_req() would have reset it.
6341184Skrgopi 	 */
635*1353Sericheng 	if (dls_soft_ring_workers(dsp->ds_dc))
636*1353Sericheng 		dls_soft_ring_disable(dsp->ds_dc);
637*1353Sericheng 
638*1353Sericheng 	mp = dsp->ds_pending_req;
639*1353Sericheng 	dsp->ds_pending_req = NULL;
640*1353Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
641*1353Sericheng 	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
642*1353Sericheng 
643*1353Sericheng 	DLD_WAKEUP(dsp);
644*1353Sericheng }
645*1353Sericheng 
646*1353Sericheng /*ARGSUSED*/
647*1353Sericheng static boolean_t
648*1353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
649*1353Sericheng {
650*1353Sericheng 	queue_t		*q = dsp->ds_wq;
651*1353Sericheng 	t_uscalar_t	dl_err;
652*1353Sericheng 
653*1353Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
654*1353Sericheng 
655*1353Sericheng 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
656*1353Sericheng 		dl_err = DL_BADPRIM;
657*1353Sericheng 		goto failed;
6581184Skrgopi 	}
6591184Skrgopi 
660*1353Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
661*1353Sericheng 		dl_err = DL_OUTSTATE;
662*1353Sericheng 		goto failed;
663*1353Sericheng 	}
664*1353Sericheng 
665*1353Sericheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
666*1353Sericheng 
667*1353Sericheng 	mutex_enter(&dsp->ds_thr_lock);
668*1353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
669*1353Sericheng 	dsp->ds_pending_req = mp;
670*1353Sericheng 	dsp->ds_pending_op = proto_process_unbind_req;
671*1353Sericheng 	dsp->ds_pending_cnt++;
672*1353Sericheng 	mutex_exit(&dsp->ds_thr_lock);
673269Sericheng 	rw_exit(&dsp->ds_lock);
674269Sericheng 
675269Sericheng 	return (B_TRUE);
676269Sericheng failed:
677269Sericheng 	rw_exit(&dsp->ds_lock);
678269Sericheng 	dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0);
679269Sericheng 	return (B_FALSE);
6800Sstevel@tonic-gate }
6810Sstevel@tonic-gate 
6820Sstevel@tonic-gate /*
683269Sericheng  * DL_PROMISCON_REQ
6840Sstevel@tonic-gate  */
685269Sericheng static boolean_t
686269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6870Sstevel@tonic-gate {
688269Sericheng 	dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp;
689269Sericheng 	int		err = 0;
690269Sericheng 	t_uscalar_t	dl_err;
691269Sericheng 	uint32_t	promisc_saved;
692269Sericheng 	queue_t		*q = dsp->ds_wq;
693269Sericheng 
694269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
695269Sericheng 		dl_err = DL_BADPRIM;
696269Sericheng 		goto failed;
697269Sericheng 	}
698269Sericheng 
699269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
700269Sericheng 
701269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
702269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
703269Sericheng 		dl_err = DL_OUTSTATE;
7040Sstevel@tonic-gate 		goto failed;
705269Sericheng 	}
7060Sstevel@tonic-gate 
707269Sericheng 	promisc_saved = dsp->ds_promisc;
708269Sericheng 	switch (dlp->dl_level) {
709269Sericheng 	case DL_PROMISC_SAP:
710269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_SAP;
711269Sericheng 		break;
712269Sericheng 
713269Sericheng 	case DL_PROMISC_MULTI:
714269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_MULTI;
715269Sericheng 		break;
716269Sericheng 
717269Sericheng 	case DL_PROMISC_PHYS:
718269Sericheng 		dsp->ds_promisc |= DLS_PROMISC_PHYS;
719269Sericheng 		break;
720269Sericheng 
721269Sericheng 	default:
722269Sericheng 		dl_err = DL_NOTSUPPORTED;
723269Sericheng 		goto failed;
724269Sericheng 	}
7250Sstevel@tonic-gate 
726269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
727269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
728269Sericheng 		dsp->ds_promisc = promisc_saved;
729269Sericheng 		dl_err = DL_SYSERR;
730269Sericheng 		err = EBUSY;
731269Sericheng 		goto failed;
732269Sericheng 	}
733269Sericheng 
734269Sericheng 	/*
735269Sericheng 	 * Adjust channel promiscuity.
736269Sericheng 	 */
737269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
738269Sericheng 	if (err != 0) {
739269Sericheng 		dl_err = DL_SYSERR;
740269Sericheng 		dsp->ds_promisc = promisc_saved;
741269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
742269Sericheng 			dls_active_clear(dsp->ds_dc);
743269Sericheng 
744269Sericheng 		goto failed;
745269Sericheng 	}
746269Sericheng 
747269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
748269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
749269Sericheng 
750269Sericheng 	rw_exit(&dsp->ds_lock);
751269Sericheng 	dlokack(q, mp, DL_PROMISCON_REQ);
752269Sericheng 	return (B_TRUE);
7530Sstevel@tonic-gate failed:
754269Sericheng 	rw_exit(&dsp->ds_lock);
755269Sericheng 	dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err);
756269Sericheng 	return (B_FALSE);
7570Sstevel@tonic-gate }
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate /*
760269Sericheng  * DL_PROMISCOFF_REQ
7610Sstevel@tonic-gate  */
762269Sericheng static boolean_t
763269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
7640Sstevel@tonic-gate {
765269Sericheng 	dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp;
766269Sericheng 	int		err = 0;
767269Sericheng 	t_uscalar_t	dl_err;
768269Sericheng 	uint32_t	promisc_saved;
769269Sericheng 	queue_t		*q = dsp->ds_wq;
770269Sericheng 
771269Sericheng 
772269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
773269Sericheng 		dl_err = DL_BADPRIM;
7740Sstevel@tonic-gate 		goto failed;
775269Sericheng 	}
7760Sstevel@tonic-gate 
777269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
7780Sstevel@tonic-gate 
779269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
780269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
781269Sericheng 		dl_err = DL_OUTSTATE;
7820Sstevel@tonic-gate 		goto failed;
783269Sericheng 	}
7840Sstevel@tonic-gate 
785269Sericheng 	promisc_saved = dsp->ds_promisc;
786269Sericheng 	switch (dlp->dl_level) {
787269Sericheng 	case DL_PROMISC_SAP:
788269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
789269Sericheng 			dl_err = DL_NOTENAB;
790269Sericheng 			goto failed;
791269Sericheng 		}
792269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
7930Sstevel@tonic-gate 		break;
7940Sstevel@tonic-gate 
795269Sericheng 	case DL_PROMISC_MULTI:
796269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
797269Sericheng 			dl_err = DL_NOTENAB;
798269Sericheng 			goto failed;
799269Sericheng 		}
800269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
801269Sericheng 		break;
802269Sericheng 
803269Sericheng 	case DL_PROMISC_PHYS:
804269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
805269Sericheng 			dl_err = DL_NOTENAB;
806269Sericheng 			goto failed;
807269Sericheng 		}
808269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
8090Sstevel@tonic-gate 		break;
8100Sstevel@tonic-gate 
8110Sstevel@tonic-gate 	default:
812269Sericheng 		dl_err = DL_NOTSUPPORTED;
813269Sericheng 		goto failed;
814269Sericheng 	}
815269Sericheng 
816269Sericheng 	/*
817269Sericheng 	 * Adjust channel promiscuity.
818269Sericheng 	 */
819269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
820269Sericheng 	if (err != 0) {
821269Sericheng 		dsp->ds_promisc = promisc_saved;
8220Sstevel@tonic-gate 		dl_err = DL_SYSERR;
823269Sericheng 		goto failed;
824269Sericheng 	}
825269Sericheng 
826269Sericheng 	rw_exit(&dsp->ds_lock);
827269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
828269Sericheng 	return (B_TRUE);
829269Sericheng failed:
830269Sericheng 	rw_exit(&dsp->ds_lock);
831269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
832269Sericheng 	return (B_FALSE);
833269Sericheng }
834269Sericheng 
835269Sericheng /*
836269Sericheng  * DL_ENABMULTI_REQ
837269Sericheng  */
838269Sericheng static boolean_t
839269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
840269Sericheng {
841269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
842269Sericheng 	int		err = 0;
843269Sericheng 	t_uscalar_t	dl_err;
844269Sericheng 	queue_t		*q = dsp->ds_wq;
845269Sericheng 
846269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
847269Sericheng 
848269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
849269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
850269Sericheng 		dl_err = DL_OUTSTATE;
851269Sericheng 		goto failed;
852269Sericheng 	}
853269Sericheng 
854269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
855269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
856269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
857269Sericheng 		dl_err = DL_BADPRIM;
858269Sericheng 		goto failed;
859269Sericheng 	}
860269Sericheng 
861269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
862269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
863269Sericheng 		dl_err = DL_SYSERR;
864269Sericheng 		err = EBUSY;
865269Sericheng 		goto failed;
8660Sstevel@tonic-gate 	}
8670Sstevel@tonic-gate 
868269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
869269Sericheng 	if (err != 0) {
870269Sericheng 		switch (err) {
871269Sericheng 		case EINVAL:
872269Sericheng 			dl_err = DL_BADADDR;
873269Sericheng 			err = 0;
874269Sericheng 			break;
875269Sericheng 		case ENOSPC:
876269Sericheng 			dl_err = DL_TOOMANY;
877269Sericheng 			err = 0;
878269Sericheng 			break;
879269Sericheng 		default:
880269Sericheng 			dl_err = DL_SYSERR;
881269Sericheng 			break;
882269Sericheng 		}
883269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
884269Sericheng 			dls_active_clear(dsp->ds_dc);
885269Sericheng 
886269Sericheng 		goto failed;
887269Sericheng 	}
888269Sericheng 
889269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
890269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
891269Sericheng 
892269Sericheng 	rw_exit(&dsp->ds_lock);
893269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
894269Sericheng 	return (B_TRUE);
895269Sericheng failed:
896269Sericheng 	rw_exit(&dsp->ds_lock);
897269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
898269Sericheng 	return (B_FALSE);
899269Sericheng }
900269Sericheng 
901269Sericheng /*
902269Sericheng  * DL_DISABMULTI_REQ
903269Sericheng  */
904269Sericheng static boolean_t
905269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
906269Sericheng {
907269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
908269Sericheng 	int		err = 0;
909269Sericheng 	t_uscalar_t	dl_err;
910269Sericheng 	queue_t		*q = dsp->ds_wq;
911269Sericheng 
912269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
913269Sericheng 
914269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
915269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
916269Sericheng 		dl_err = DL_OUTSTATE;
917269Sericheng 		goto failed;
918269Sericheng 	}
919269Sericheng 
920269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
921269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
922269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
923269Sericheng 		dl_err = DL_BADPRIM;
924269Sericheng 		goto failed;
925269Sericheng 	}
926269Sericheng 
927269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
928269Sericheng 	if (err != 0) {
929269Sericheng 	switch (err) {
930269Sericheng 		case EINVAL:
931269Sericheng 			dl_err = DL_BADADDR;
932269Sericheng 			err = 0;
933269Sericheng 			break;
934269Sericheng 
935269Sericheng 		case ENOENT:
936269Sericheng 			dl_err = DL_NOTENAB;
937269Sericheng 			err = 0;
938269Sericheng 			break;
939269Sericheng 
940269Sericheng 		default:
941269Sericheng 			dl_err = DL_SYSERR;
942269Sericheng 			break;
943269Sericheng 		}
944269Sericheng 		goto failed;
945269Sericheng 	}
946269Sericheng 
947269Sericheng 	rw_exit(&dsp->ds_lock);
948269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
949269Sericheng 	return (B_TRUE);
950269Sericheng failed:
951269Sericheng 	rw_exit(&dsp->ds_lock);
952269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
953269Sericheng 	return (B_FALSE);
9540Sstevel@tonic-gate }
9550Sstevel@tonic-gate 
9560Sstevel@tonic-gate /*
957269Sericheng  * DL_PHYS_ADDR_REQ
9580Sstevel@tonic-gate  */
959269Sericheng static boolean_t
960269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
9610Sstevel@tonic-gate {
962269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
963269Sericheng 	queue_t		*q = dsp->ds_wq;
964269Sericheng 	t_uscalar_t	dl_err;
965269Sericheng 	char		*addr;
966269Sericheng 	uint_t		addr_length;
967269Sericheng 
968269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
969269Sericheng 
970269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
971269Sericheng 		dl_err = DL_BADPRIM;
972269Sericheng 		goto failed;
973269Sericheng 	}
974269Sericheng 
975269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
976269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
977269Sericheng 		dl_err = DL_OUTSTATE;
978269Sericheng 		goto failed;
979269Sericheng 	}
9800Sstevel@tonic-gate 
981269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
982269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
983269Sericheng 		dl_err = DL_UNSUPPORTED;
9840Sstevel@tonic-gate 		goto failed;
985269Sericheng 	}
9860Sstevel@tonic-gate 
987269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
988269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
989269Sericheng 	if (addr == NULL) {
990269Sericheng 		rw_exit(&dsp->ds_lock);
991269Sericheng 		merror(q, mp, ENOSR);
992269Sericheng 		return (B_FALSE);
993269Sericheng 	}
9940Sstevel@tonic-gate 
995269Sericheng 	/*
996269Sericheng 	 * Copy out the address before we drop the lock; we don't
997269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
998269Sericheng 	 */
999269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
1000269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
1001269Sericheng 
1002269Sericheng 	rw_exit(&dsp->ds_lock);
1003269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
1004269Sericheng 	kmem_free(addr, addr_length);
1005269Sericheng 	return (B_TRUE);
10060Sstevel@tonic-gate failed:
1007269Sericheng 	rw_exit(&dsp->ds_lock);
1008269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
1009269Sericheng 	return (B_FALSE);
1010269Sericheng }
10110Sstevel@tonic-gate 
1012269Sericheng /*
1013269Sericheng  * DL_SET_PHYS_ADDR_REQ
1014269Sericheng  */
1015269Sericheng static boolean_t
1016269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1017269Sericheng {
1018269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
1019269Sericheng 	int		err = 0;
1020269Sericheng 	t_uscalar_t	dl_err;
1021269Sericheng 	queue_t		*q = dsp->ds_wq;
10220Sstevel@tonic-gate 
1023269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1024269Sericheng 
1025269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1026269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1027269Sericheng 		dl_err = DL_OUTSTATE;
1028269Sericheng 		goto failed;
1029269Sericheng 	}
1030269Sericheng 
1031269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
1032269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
1033269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
1034269Sericheng 		dl_err = DL_BADPRIM;
1035269Sericheng 		goto failed;
10360Sstevel@tonic-gate 	}
10370Sstevel@tonic-gate 
1038269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
1039269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
1040269Sericheng 		dl_err = DL_SYSERR;
1041269Sericheng 		err = EBUSY;
1042269Sericheng 		goto failed;
1043269Sericheng 	}
1044269Sericheng 
1045269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
1046269Sericheng 	if (err != 0) {
1047269Sericheng 		switch (err) {
1048269Sericheng 		case EINVAL:
1049269Sericheng 			dl_err = DL_BADADDR;
1050269Sericheng 			err = 0;
1051269Sericheng 			break;
1052269Sericheng 
1053269Sericheng 		default:
1054269Sericheng 			dl_err = DL_SYSERR;
1055269Sericheng 			break;
1056269Sericheng 		}
1057269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1058269Sericheng 			dls_active_clear(dsp->ds_dc);
1059269Sericheng 
1060269Sericheng 		goto failed;
1061269Sericheng 	}
1062269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1063269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
1064269Sericheng 
1065269Sericheng 	rw_exit(&dsp->ds_lock);
1066269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
1067269Sericheng 	return (B_TRUE);
1068269Sericheng failed:
1069269Sericheng 	rw_exit(&dsp->ds_lock);
1070269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
1071269Sericheng 	return (B_FALSE);
1072269Sericheng }
1073269Sericheng 
1074269Sericheng /*
1075269Sericheng  * DL_UDQOS_REQ
1076269Sericheng  */
1077269Sericheng static boolean_t
1078269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1079269Sericheng {
1080269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
1081269Sericheng 	dl_qos_cl_sel1_t *selp;
1082269Sericheng 	int		off, len;
1083269Sericheng 	t_uscalar_t	dl_err;
1084269Sericheng 	queue_t		*q = dsp->ds_wq;
1085269Sericheng 
1086269Sericheng 	off = dlp->dl_qos_offset;
1087269Sericheng 	len = dlp->dl_qos_length;
1088269Sericheng 
1089269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
1090269Sericheng 		dl_err = DL_BADPRIM;
1091269Sericheng 		goto failed;
1092269Sericheng 	}
1093269Sericheng 
1094269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1095269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1096269Sericheng 		dl_err = DL_BADQOSTYPE;
1097269Sericheng 		goto failed;
1098269Sericheng 	}
1099269Sericheng 
1100269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1101269Sericheng 
1102269Sericheng 	if (dsp->ds_vid == VLAN_ID_NONE ||
1103269Sericheng 	    selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1104269Sericheng 	    selp->dl_priority < 0) {
1105269Sericheng 		dl_err = DL_BADQOSPARAM;
1106269Sericheng 		goto failed;
1107269Sericheng 	}
1108269Sericheng 
1109269Sericheng 	dsp->ds_pri = selp->dl_priority;
1110269Sericheng 
1111269Sericheng 	rw_exit(&dsp->ds_lock);
1112269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1113269Sericheng 	return (B_TRUE);
1114269Sericheng failed:
1115269Sericheng 	rw_exit(&dsp->ds_lock);
1116269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1117269Sericheng 	return (B_FALSE);
11180Sstevel@tonic-gate }
11190Sstevel@tonic-gate 
11201184Skrgopi static boolean_t
11211184Skrgopi check_ip_above(queue_t *q)
11221184Skrgopi {
11231184Skrgopi 	queue_t		*next_q;
11241184Skrgopi 	boolean_t	ret = B_TRUE;
11251184Skrgopi 
11261184Skrgopi 	claimstr(q);
11271184Skrgopi 	next_q = q->q_next;
11281184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
11291184Skrgopi 		ret = B_FALSE;
11301184Skrgopi 	releasestr(q);
11311184Skrgopi 	return (ret);
11321184Skrgopi }
11331184Skrgopi 
11340Sstevel@tonic-gate /*
1135269Sericheng  * DL_CAPABILITY_REQ
11360Sstevel@tonic-gate  */
1137269Sericheng /*ARGSUSED*/
1138269Sericheng static boolean_t
1139269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
11400Sstevel@tonic-gate {
1141269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1142269Sericheng 	dl_capability_sub_t *sp;
1143269Sericheng 	size_t		size, len;
1144269Sericheng 	offset_t	off, end;
1145269Sericheng 	t_uscalar_t	dl_err;
1146269Sericheng 	queue_t		*q = dsp->ds_wq;
1147269Sericheng 	boolean_t	upgraded;
1148269Sericheng 
1149269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1150269Sericheng 
1151269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1152269Sericheng 		dl_err = DL_BADPRIM;
1153269Sericheng 		goto failed;
1154269Sericheng 	}
1155269Sericheng 
1156269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1157269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1158269Sericheng 		dl_err = DL_OUTSTATE;
1159269Sericheng 		goto failed;
1160269Sericheng 	}
1161269Sericheng 
1162269Sericheng 	/*
1163269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1164269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1165269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1166269Sericheng 	 */
1167269Sericheng 	if (dlp->dl_sub_length == 0) {
1168269Sericheng 		/* callee drops lock */
1169269Sericheng 		return (proto_capability_advertise(dsp, mp));
1170269Sericheng 	}
1171269Sericheng 
1172269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1173269Sericheng 		dl_err = DL_BADPRIM;
1174269Sericheng 		goto failed;
1175269Sericheng 	}
1176269Sericheng 
1177269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1178269Sericheng 
1179269Sericheng 	off = dlp->dl_sub_offset;
1180269Sericheng 	len = dlp->dl_sub_length;
11810Sstevel@tonic-gate 
11820Sstevel@tonic-gate 	/*
1183269Sericheng 	 * Walk the list of capabilities to be enabled.
11840Sstevel@tonic-gate 	 */
1185269Sericheng 	upgraded = B_FALSE;
1186269Sericheng 	for (end = off + len; off < end; ) {
1187269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1188269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1189269Sericheng 
1190269Sericheng 		if (off + size > end ||
1191269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1192269Sericheng 			dl_err = DL_BADPRIM;
1193269Sericheng 			goto failed;
1194269Sericheng 		}
1195269Sericheng 
1196269Sericheng 		switch (sp->dl_cap) {
1197269Sericheng 		/*
1198269Sericheng 		 * TCP/IP checksum offload to hardware.
1199269Sericheng 		 */
1200269Sericheng 		case DL_CAPAB_HCKSUM: {
1201269Sericheng 			dl_capab_hcksum_t *hcksump;
1202269Sericheng 			dl_capab_hcksum_t hcksum;
1203269Sericheng 
1204269Sericheng 			ASSERT(dsp->ds_mip->mi_cksum != 0);
1205269Sericheng 
1206269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1207269Sericheng 			/*
1208269Sericheng 			 * Copy for alignment.
1209269Sericheng 			 */
1210269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1211269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1212269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1213269Sericheng 			break;
1214269Sericheng 		}
1215269Sericheng 
1216269Sericheng 		/*
1217269Sericheng 		 * IP polling interface.
1218269Sericheng 		 */
1219269Sericheng 		case DL_CAPAB_POLL: {
12201184Skrgopi 			dl_capab_dls_t *pollp;
12211184Skrgopi 			dl_capab_dls_t	poll;
1222269Sericheng 
12231184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1224269Sericheng 			/*
1225269Sericheng 			 * Copy for alignment.
1226269Sericheng 			 */
12271184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1228269Sericheng 
1229269Sericheng 			/*
1230269Sericheng 			 * We need to become writer before enabling and/or
1231269Sericheng 			 * disabling the polling interface.  If we couldn'
1232269Sericheng 			 * upgrade, check state again after re-acquiring the
1233269Sericheng 			 * lock to make sure we can proceed.
1234269Sericheng 			 */
1235269Sericheng 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
1236269Sericheng 				rw_exit(&dsp->ds_lock);
1237269Sericheng 				rw_enter(&dsp->ds_lock, RW_WRITER);
1238269Sericheng 
1239269Sericheng 				if (dsp->ds_dlstate == DL_UNATTACHED ||
1240269Sericheng 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
1241269Sericheng 					dl_err = DL_OUTSTATE;
1242269Sericheng 					goto failed;
1243269Sericheng 				}
1244269Sericheng 			}
1245269Sericheng 			upgraded = B_TRUE;
1246269Sericheng 
12471184Skrgopi 			switch (poll.dls_flags) {
1248269Sericheng 			default:
1249269Sericheng 				/*FALLTHRU*/
1250269Sericheng 			case POLL_DISABLE:
1251269Sericheng 				proto_poll_disable(dsp);
1252269Sericheng 				break;
1253269Sericheng 
1254269Sericheng 			case POLL_ENABLE:
1255269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1256269Sericheng 
1257269Sericheng 				/*
1258269Sericheng 				 * Make sure polling is disabled.
1259269Sericheng 				 */
1260269Sericheng 				proto_poll_disable(dsp);
1261269Sericheng 
1262269Sericheng 				/*
1263269Sericheng 				 * Now attempt enable it.
1264269Sericheng 				 */
12651184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
12661184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
12671184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
12681184Skrgopi 					poll.dls_flags = POLL_ENABLE;
12691184Skrgopi 				}
1270269Sericheng 				break;
1271269Sericheng 			}
1272269Sericheng 
12731184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
12741184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
12751184Skrgopi 			break;
12761184Skrgopi 		}
12771184Skrgopi 		case DL_CAPAB_SOFT_RING: {
12781184Skrgopi 			dl_capab_dls_t *soft_ringp;
12791184Skrgopi 			dl_capab_dls_t soft_ring;
12801184Skrgopi 
12811184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
12821184Skrgopi 			/*
12831184Skrgopi 			 * Copy for alignment.
12841184Skrgopi 			 */
12851184Skrgopi 			bcopy(soft_ringp, &soft_ring,
12861184Skrgopi 			    sizeof (dl_capab_dls_t));
12871184Skrgopi 
12881184Skrgopi 			/*
12891184Skrgopi 			 * We need to become writer before enabling and/or
12901184Skrgopi 			 * disabling the soft_ring interface.  If we couldn'
12911184Skrgopi 			 * upgrade, check state again after re-acquiring the
12921184Skrgopi 			 * lock to make sure we can proceed.
12931184Skrgopi 			 */
12941184Skrgopi 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
12951184Skrgopi 				rw_exit(&dsp->ds_lock);
12961184Skrgopi 				rw_enter(&dsp->ds_lock, RW_WRITER);
12971184Skrgopi 
12981184Skrgopi 				if (dsp->ds_dlstate == DL_UNATTACHED ||
12991184Skrgopi 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
13001184Skrgopi 					dl_err = DL_OUTSTATE;
13011184Skrgopi 					goto failed;
13021184Skrgopi 				}
13031184Skrgopi 			}
13041184Skrgopi 			upgraded = B_TRUE;
13051184Skrgopi 
13061184Skrgopi 			switch (soft_ring.dls_flags) {
13071184Skrgopi 			default:
13081184Skrgopi 				/*FALLTHRU*/
13091184Skrgopi 			case SOFT_RING_DISABLE:
13101184Skrgopi 				proto_soft_ring_disable(dsp);
13111184Skrgopi 				break;
13121184Skrgopi 
13131184Skrgopi 			case SOFT_RING_ENABLE:
13141184Skrgopi 				/*
13151184Skrgopi 				 * Make sure soft_ring is disabled.
13161184Skrgopi 				 */
13171184Skrgopi 				proto_soft_ring_disable(dsp);
13181184Skrgopi 
13191184Skrgopi 				/*
13201184Skrgopi 				 * Now attempt enable it.
13211184Skrgopi 				 */
13221184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
13231184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
13241184Skrgopi 					bzero(&soft_ring,
13251184Skrgopi 					    sizeof (dl_capab_dls_t));
13261184Skrgopi 					soft_ring.dls_flags =
13271184Skrgopi 					    SOFT_RING_ENABLE;
13281184Skrgopi 				} else {
13291184Skrgopi 					bzero(&soft_ring,
13301184Skrgopi 					    sizeof (dl_capab_dls_t));
13311184Skrgopi 					soft_ring.dls_flags =
13321184Skrgopi 					    SOFT_RING_DISABLE;
13331184Skrgopi 				}
13341184Skrgopi 				break;
13351184Skrgopi 			}
13361184Skrgopi 
13371184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
13381184Skrgopi 			bcopy(&soft_ring, soft_ringp,
13391184Skrgopi 			    sizeof (dl_capab_dls_t));
1340269Sericheng 			break;
1341269Sericheng 		}
1342269Sericheng 		default:
1343269Sericheng 			break;
1344269Sericheng 		}
1345269Sericheng 
1346269Sericheng 		off += size;
1347269Sericheng 	}
1348269Sericheng 	rw_exit(&dsp->ds_lock);
1349269Sericheng 	qreply(q, mp);
1350269Sericheng 	return (B_TRUE);
1351269Sericheng failed:
1352269Sericheng 	rw_exit(&dsp->ds_lock);
1353269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1354269Sericheng 	return (B_FALSE);
13550Sstevel@tonic-gate }
13560Sstevel@tonic-gate 
13570Sstevel@tonic-gate /*
1358269Sericheng  * DL_NOTIFY_REQ
13590Sstevel@tonic-gate  */
1360269Sericheng static boolean_t
1361269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
13620Sstevel@tonic-gate {
1363269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1364269Sericheng 	t_uscalar_t	dl_err;
1365269Sericheng 	queue_t		*q = dsp->ds_wq;
1366269Sericheng 	uint_t		note =
1367269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1368269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1369269Sericheng 	    DL_NOTE_PHYS_ADDR |
1370269Sericheng 	    DL_NOTE_LINK_UP |
1371269Sericheng 	    DL_NOTE_LINK_DOWN |
1372269Sericheng 	    DL_NOTE_CAPAB_RENEG;
13730Sstevel@tonic-gate 
1374269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1375269Sericheng 		dl_err = DL_BADPRIM;
1376269Sericheng 		goto failed;
1377269Sericheng 	}
13780Sstevel@tonic-gate 
1379269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1380269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1381269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1382269Sericheng 		dl_err = DL_OUTSTATE;
1383269Sericheng 		goto failed;
13840Sstevel@tonic-gate 	}
13850Sstevel@tonic-gate 
1386269Sericheng 	if (dsp->ds_mip->mi_stat[MAC_STAT_IFSPEED])
1387269Sericheng 		note |= DL_NOTE_SPEED;
1388269Sericheng 
1389269Sericheng 	/*
1390269Sericheng 	 * Cache the notifications that are being enabled.
1391269Sericheng 	 */
1392269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1393269Sericheng 	rw_exit(&dsp->ds_lock);
1394269Sericheng 	/*
1395269Sericheng 	 * The ACK carries all notifications regardless of which set is
1396269Sericheng 	 * being enabled.
1397269Sericheng 	 */
1398269Sericheng 	dlnotifyack(q, mp, note);
1399269Sericheng 
1400269Sericheng 	/*
1401269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1402269Sericheng 	 */
1403269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1404269Sericheng 	if (dsp->ds_notifications != 0) {
1405269Sericheng 		rw_exit(&dsp->ds_lock);
1406269Sericheng 		dld_str_notify_ind(dsp);
1407269Sericheng 	} else {
1408269Sericheng 		rw_exit(&dsp->ds_lock);
1409269Sericheng 	}
1410269Sericheng 	return (B_TRUE);
1411269Sericheng failed:
1412269Sericheng 	rw_exit(&dsp->ds_lock);
1413269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1414269Sericheng 	return (B_FALSE);
14150Sstevel@tonic-gate }
14160Sstevel@tonic-gate 
14170Sstevel@tonic-gate /*
1418269Sericheng  * DL_UINTDATA_REQ
14190Sstevel@tonic-gate  */
1420269Sericheng static boolean_t
1421269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
14220Sstevel@tonic-gate {
1423269Sericheng 	queue_t			*q = dsp->ds_wq;
1424269Sericheng 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)udlp;
1425269Sericheng 	off_t			off;
1426269Sericheng 	size_t			len, size;
1427269Sericheng 	const uint8_t		*addr;
1428269Sericheng 	uint16_t		sap;
1429269Sericheng 	uint_t			addr_length;
1430269Sericheng 	mblk_t			*bp, *cont;
1431269Sericheng 	uint32_t		start, stuff, end, value, flags;
1432269Sericheng 	t_uscalar_t		dl_err;
1433269Sericheng 
1434269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1435269Sericheng 
1436269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1437269Sericheng 		dl_err = DL_BADPRIM;
1438269Sericheng 		goto failed;
1439269Sericheng 	}
1440269Sericheng 
1441269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
1442269Sericheng 		dl_err = DL_OUTSTATE;
1443269Sericheng 		goto failed;
1444269Sericheng 	}
1445269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1446269Sericheng 
1447269Sericheng 	off = dlp->dl_dest_addr_offset;
1448269Sericheng 	len = dlp->dl_dest_addr_length;
1449269Sericheng 
1450269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1451269Sericheng 		dl_err = DL_BADPRIM;
1452269Sericheng 		goto failed;
1453269Sericheng 	}
1454269Sericheng 
1455269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1456269Sericheng 		dl_err = DL_BADADDR;
1457269Sericheng 		goto failed;
1458269Sericheng 	}
1459269Sericheng 
1460269Sericheng 	addr = mp->b_rptr + off;
1461269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1462269Sericheng 
1463269Sericheng 	/*
1464269Sericheng 	 * Check the length of the packet and the block types.
1465269Sericheng 	 */
1466269Sericheng 	size = 0;
1467269Sericheng 	cont = mp->b_cont;
1468269Sericheng 	for (bp = cont; bp != NULL; bp = bp->b_cont) {
1469269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1470269Sericheng 			goto baddata;
1471269Sericheng 
1472269Sericheng 		size += MBLKL(bp);
1473269Sericheng 	}
1474269Sericheng 
1475269Sericheng 	if (size > dsp->ds_mip->mi_sdu_max)
1476269Sericheng 		goto baddata;
1477269Sericheng 
1478269Sericheng 	/*
1479269Sericheng 	 * Build a packet header.
1480269Sericheng 	 */
1481269Sericheng 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dsp->ds_pri)) == NULL) {
1482269Sericheng 		dl_err = DL_BADADDR;
1483269Sericheng 		goto failed;
1484269Sericheng 	}
1485269Sericheng 
1486269Sericheng 	/*
1487269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1488269Sericheng 	 */
1489269Sericheng 	freeb(mp);
1490269Sericheng 
1491269Sericheng 	/*
1492269Sericheng 	 * Transfer the checksum offload information if it is present.
1493269Sericheng 	 */
1494269Sericheng 	hcksum_retrieve(cont, NULL, NULL, &start, &stuff, &end, &value,
1495269Sericheng 	    &flags);
1496269Sericheng 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags,
1497269Sericheng 	    0);
1498269Sericheng 
1499269Sericheng 	/*
1500269Sericheng 	 * Link the payload onto the new header.
1501269Sericheng 	 */
1502269Sericheng 	ASSERT(bp->b_cont == NULL);
1503269Sericheng 	bp->b_cont = cont;
1504269Sericheng 
1505269Sericheng 	str_mdata_fastpath_put(dsp, bp);
1506269Sericheng 	rw_exit(&dsp->ds_lock);
1507269Sericheng 	return (B_TRUE);
1508269Sericheng failed:
1509269Sericheng 	rw_exit(&dsp->ds_lock);
1510269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1511269Sericheng 	return (B_FALSE);
1512269Sericheng 
1513269Sericheng baddata:
1514269Sericheng 	rw_exit(&dsp->ds_lock);
1515269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1516269Sericheng 	return (B_FALSE);
1517269Sericheng }
1518269Sericheng 
1519269Sericheng /*
1520269Sericheng  * DL_PASSIVE_REQ
1521269Sericheng  */
1522269Sericheng /* ARGSUSED */
1523269Sericheng static boolean_t
1524269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1525269Sericheng {
1526269Sericheng 	t_uscalar_t dl_err;
1527269Sericheng 
1528269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1529269Sericheng 	/*
1530269Sericheng 	 * If we've already become active by issuing an active primitive,
1531269Sericheng 	 * then it's too late to try to become passive.
1532269Sericheng 	 */
1533269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1534269Sericheng 		dl_err = DL_OUTSTATE;
1535269Sericheng 		goto failed;
1536269Sericheng 	}
1537269Sericheng 
1538269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1539269Sericheng 		dl_err = DL_BADPRIM;
1540269Sericheng 		goto failed;
1541269Sericheng 	}
1542269Sericheng 
1543269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1544269Sericheng 	rw_exit(&dsp->ds_lock);
1545269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1546269Sericheng 	return (B_TRUE);
1547269Sericheng failed:
1548269Sericheng 	rw_exit(&dsp->ds_lock);
1549269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1550269Sericheng 	return (B_FALSE);
1551269Sericheng }
1552269Sericheng 
1553269Sericheng 
1554269Sericheng /*
1555269Sericheng  * Catch-all handler.
1556269Sericheng  */
1557269Sericheng static boolean_t
1558269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1559269Sericheng {
1560269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1561269Sericheng 	return (B_FALSE);
15620Sstevel@tonic-gate }
15630Sstevel@tonic-gate 
15640Sstevel@tonic-gate static void
15650Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
15660Sstevel@tonic-gate {
15670Sstevel@tonic-gate 	mac_handle_t	mh;
15680Sstevel@tonic-gate 
1569*1353Sericheng 	ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock));
1570269Sericheng 
15710Sstevel@tonic-gate 	if (!dsp->ds_polling)
15720Sstevel@tonic-gate 		return;
15730Sstevel@tonic-gate 
15740Sstevel@tonic-gate 	/*
15750Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
15760Sstevel@tonic-gate 	 */
15770Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
15780Sstevel@tonic-gate 
15790Sstevel@tonic-gate 	/*
15800Sstevel@tonic-gate 	 * Reset the resource_add callback.
15810Sstevel@tonic-gate 	 */
15820Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
15830Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
15841184Skrgopi 	mac_resources(mh);
15850Sstevel@tonic-gate 
15860Sstevel@tonic-gate 	/*
15870Sstevel@tonic-gate 	 * Set receive function back to default.
15880Sstevel@tonic-gate 	 */
15890Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
15900Sstevel@tonic-gate 	    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
15910Sstevel@tonic-gate 
15920Sstevel@tonic-gate 	/*
15930Sstevel@tonic-gate 	 * Note that polling is disabled.
15940Sstevel@tonic-gate 	 */
15950Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
15960Sstevel@tonic-gate }
15970Sstevel@tonic-gate 
15980Sstevel@tonic-gate static boolean_t
15991184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
16000Sstevel@tonic-gate {
16010Sstevel@tonic-gate 	mac_handle_t	mh;
16020Sstevel@tonic-gate 
1603269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16040Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
16050Sstevel@tonic-gate 
16060Sstevel@tonic-gate 	/*
16070Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
16080Sstevel@tonic-gate 	 * has been enabled.
16090Sstevel@tonic-gate 	 */
16100Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
16110Sstevel@tonic-gate 		return (B_FALSE);
16120Sstevel@tonic-gate 
16130Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
16140Sstevel@tonic-gate 
16150Sstevel@tonic-gate 	/*
16160Sstevel@tonic-gate 	 * Register resources.
16170Sstevel@tonic-gate 	 */
16181184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
16191184Skrgopi 	    (void *)pollp->dls_rx_handle);
16200Sstevel@tonic-gate 	mac_resources(mh);
16210Sstevel@tonic-gate 
16220Sstevel@tonic-gate 	/*
16230Sstevel@tonic-gate 	 * Set the receive function.
16240Sstevel@tonic-gate 	 */
16251184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
16261184Skrgopi 	    (void *)pollp->dls_rx_handle);
16270Sstevel@tonic-gate 
16280Sstevel@tonic-gate 	/*
16290Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
16300Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
16310Sstevel@tonic-gate 	 */
16320Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
16330Sstevel@tonic-gate 	return (B_TRUE);
16340Sstevel@tonic-gate }
16350Sstevel@tonic-gate 
16361184Skrgopi static void
16371184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
16381184Skrgopi {
16391184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16401184Skrgopi 
16411184Skrgopi 	if (!dsp->ds_soft_ring)
16421184Skrgopi 		return;
16431184Skrgopi 
16441184Skrgopi 	/*
16451184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
16461184Skrgopi 	 */
16471184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
16481184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
16491184Skrgopi 	/*
16501184Skrgopi 	 * Note that fanout is disabled.
16511184Skrgopi 	 */
16521184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
16531184Skrgopi }
16541184Skrgopi 
16551184Skrgopi static boolean_t
16561184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
16571184Skrgopi {
16581184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16591184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
16601184Skrgopi 
16611184Skrgopi 	/*
16621184Skrgopi 	 * We cannot enable soft_ring if raw mode
16631184Skrgopi 	 * has been enabled.
16641184Skrgopi 	 */
16651184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
16661184Skrgopi 		return (B_FALSE);
16671184Skrgopi 
16681184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
16691184Skrgopi 		return (B_FALSE);
16701184Skrgopi 
16711184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
16721184Skrgopi 	return (B_TRUE);
16731184Skrgopi }
16741184Skrgopi 
16751184Skrgopi static void
16761184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
16771184Skrgopi {
16781184Skrgopi 	dls_rx_t	rx;
16791184Skrgopi 
16801184Skrgopi 	if (type == SOFT_RING_NONE) {
16811184Skrgopi 		rx = (dsp->ds_mode == DLD_FASTPATH) ?
16821184Skrgopi 			    dld_str_rx_fastpath : dld_str_rx_unitdata;
16831184Skrgopi 	} else {
16841184Skrgopi 		rx = (dls_rx_t)dls_ether_soft_ring_fanout;
16851184Skrgopi 	}
16861184Skrgopi 	dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type);
16871184Skrgopi }
16881184Skrgopi 
16890Sstevel@tonic-gate /*
16900Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
16910Sstevel@tonic-gate  */
1692269Sericheng static boolean_t
1693269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
16940Sstevel@tonic-gate {
16950Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
16960Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
16970Sstevel@tonic-gate 	size_t			subsize;
16981184Skrgopi 	dl_capab_dls_t		poll;
16991184Skrgopi 	dl_capab_dls_t	soft_ring;
17000Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
17010Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
17020Sstevel@tonic-gate 	uint8_t			*ptr;
17030Sstevel@tonic-gate 	uint32_t		cksum;
17040Sstevel@tonic-gate 	boolean_t		poll_cap;
1705269Sericheng 	queue_t			*q = dsp->ds_wq;
1706269Sericheng 	mblk_t			*mp1;
1707269Sericheng 
1708269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
17090Sstevel@tonic-gate 
17100Sstevel@tonic-gate 	/*
17110Sstevel@tonic-gate 	 * Initially assume no capabilities.
17120Sstevel@tonic-gate 	 */
17130Sstevel@tonic-gate 	subsize = 0;
17140Sstevel@tonic-gate 
17151184Skrgopi 	/* Always advertize soft ring capability for GLDv3 drivers */
17161184Skrgopi 	subsize += sizeof (dl_capability_sub_t) + sizeof (dl_capab_dls_t);
17171184Skrgopi 
17180Sstevel@tonic-gate 	/*
17190Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
17200Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
17210Sstevel@tonic-gate 	 * then reserve space for that capability.
17220Sstevel@tonic-gate 	 */
17230Sstevel@tonic-gate 	poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) &&
17240Sstevel@tonic-gate 	    !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE));
17250Sstevel@tonic-gate 	if (poll_cap) {
17260Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17271184Skrgopi 		    sizeof (dl_capab_dls_t);
17280Sstevel@tonic-gate 	}
17290Sstevel@tonic-gate 
17300Sstevel@tonic-gate 	/*
17310Sstevel@tonic-gate 	 * If the MAC interface supports checksum offload then reserve
17320Sstevel@tonic-gate 	 * space for the DL_CAPAB_HCKSUM capability.
17330Sstevel@tonic-gate 	 */
17340Sstevel@tonic-gate 	if ((cksum = dsp->ds_mip->mi_cksum) != 0) {
17350Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17360Sstevel@tonic-gate 		    sizeof (dl_capab_hcksum_t);
17370Sstevel@tonic-gate 	}
17380Sstevel@tonic-gate 
17390Sstevel@tonic-gate 	/*
17400Sstevel@tonic-gate 	 * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then
17410Sstevel@tonic-gate 	 * reserve space for it.
17420Sstevel@tonic-gate 	 */
17430Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
17440Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17450Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
17460Sstevel@tonic-gate 	}
17470Sstevel@tonic-gate 
17480Sstevel@tonic-gate 	/*
1749269Sericheng 	 * If there are no capabilities to advertise or if we
1750269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
17510Sstevel@tonic-gate 	 */
17521184Skrgopi 	if ((mp1 = reallocb(mp,
1753269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1754269Sericheng 		rw_exit(&dsp->ds_lock);
1755269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1756269Sericheng 		return (B_FALSE);
17570Sstevel@tonic-gate 	}
17580Sstevel@tonic-gate 
1759269Sericheng 	mp = mp1;
1760269Sericheng 	DB_TYPE(mp) = M_PROTO;
1761269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1762269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
17630Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
17640Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
17650Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
17660Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
17670Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
17680Sstevel@tonic-gate 
17690Sstevel@tonic-gate 	/*
17700Sstevel@tonic-gate 	 * IP polling interface.
17710Sstevel@tonic-gate 	 */
17720Sstevel@tonic-gate 	if (poll_cap) {
17730Sstevel@tonic-gate 		/*
1774269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
1775269Sericheng 		 * we need to become writer before doing so.
17760Sstevel@tonic-gate 		 */
1777269Sericheng 		if (!rw_tryupgrade(&dsp->ds_lock)) {
1778269Sericheng 			rw_exit(&dsp->ds_lock);
1779269Sericheng 			rw_enter(&dsp->ds_lock, RW_WRITER);
1780269Sericheng 		}
17810Sstevel@tonic-gate 
1782269Sericheng 		/*
1783269Sericheng 		 * Check if polling state has changed after we re-acquired
1784269Sericheng 		 * the lock above, so that we don't mis-advertise it.
1785269Sericheng 		 */
1786269Sericheng 		poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) &&
1787269Sericheng 		    !(dld_opt & DLD_OPT_NO_POLL) &&
1788269Sericheng 		    (dsp->ds_vid == VLAN_ID_NONE));
17890Sstevel@tonic-gate 
1790269Sericheng 		if (!poll_cap) {
1791269Sericheng 			int poll_capab_size;
1792269Sericheng 
1793269Sericheng 			rw_downgrade(&dsp->ds_lock);
1794269Sericheng 
1795269Sericheng 			poll_capab_size = sizeof (dl_capability_sub_t) +
17961184Skrgopi 			    sizeof (dl_capab_dls_t);
17970Sstevel@tonic-gate 
1798269Sericheng 			mp->b_wptr -= poll_capab_size;
1799269Sericheng 			subsize -= poll_capab_size;
1800269Sericheng 			dlap->dl_sub_length = subsize;
1801269Sericheng 		} else {
1802269Sericheng 			proto_poll_disable(dsp);
1803269Sericheng 
1804269Sericheng 			rw_downgrade(&dsp->ds_lock);
1805269Sericheng 
1806269Sericheng 			dlsp = (dl_capability_sub_t *)ptr;
1807269Sericheng 
1808269Sericheng 			dlsp->dl_cap = DL_CAPAB_POLL;
18091184Skrgopi 			dlsp->dl_length = sizeof (dl_capab_dls_t);
1810269Sericheng 			ptr += sizeof (dl_capability_sub_t);
18110Sstevel@tonic-gate 
18121184Skrgopi 			bzero(&poll, sizeof (dl_capab_dls_t));
18131184Skrgopi 			poll.dls_version = POLL_VERSION_1;
18141184Skrgopi 			poll.dls_flags = POLL_CAPABLE;
18151184Skrgopi 			poll.dls_tx_handle = (uintptr_t)dsp;
18161184Skrgopi 			poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
1817269Sericheng 
18181184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
18191184Skrgopi 			bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
18201184Skrgopi 			ptr += sizeof (dl_capab_dls_t);
1821269Sericheng 		}
18220Sstevel@tonic-gate 	}
18230Sstevel@tonic-gate 
1824269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
1825269Sericheng 
18261184Skrgopi 	dlsp = (dl_capability_sub_t *)ptr;
18271184Skrgopi 
18281184Skrgopi 	dlsp->dl_cap = DL_CAPAB_SOFT_RING;
18291184Skrgopi 	dlsp->dl_length = sizeof (dl_capab_dls_t);
18301184Skrgopi 	ptr += sizeof (dl_capability_sub_t);
18311184Skrgopi 
18321184Skrgopi 	bzero(&soft_ring, sizeof (dl_capab_dls_t));
18331184Skrgopi 	soft_ring.dls_version = SOFT_RING_VERSION_1;
18341184Skrgopi 	soft_ring.dls_flags = SOFT_RING_CAPABLE;
18351184Skrgopi 	soft_ring.dls_tx_handle = (uintptr_t)dsp;
18361184Skrgopi 	soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
18371184Skrgopi 	soft_ring.dls_ring_change_status =
18381184Skrgopi 	    (uintptr_t)proto_change_soft_ring_fanout;
18391184Skrgopi 	soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
18401184Skrgopi 	soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
18411184Skrgopi 
18421184Skrgopi 	dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
18431184Skrgopi 	bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
18441184Skrgopi 	ptr += sizeof (dl_capab_dls_t);
18451184Skrgopi 
18460Sstevel@tonic-gate 	/*
18470Sstevel@tonic-gate 	 * TCP/IP checksum offload.
18480Sstevel@tonic-gate 	 */
18490Sstevel@tonic-gate 	if (cksum != 0) {
18500Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18510Sstevel@tonic-gate 
18520Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
18530Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
18540Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18550Sstevel@tonic-gate 
18560Sstevel@tonic-gate 		bzero(&hcksum, sizeof (dl_capab_hcksum_t));
18570Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
18580Sstevel@tonic-gate 		hcksum.hcksum_txflags = cksum;
18590Sstevel@tonic-gate 
18600Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
18610Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
18620Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
18630Sstevel@tonic-gate 	}
18640Sstevel@tonic-gate 
18650Sstevel@tonic-gate 	/*
18660Sstevel@tonic-gate 	 * Zero copy
18670Sstevel@tonic-gate 	 */
18680Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
18690Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18700Sstevel@tonic-gate 
18710Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
18720Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
18730Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18740Sstevel@tonic-gate 
18750Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
18760Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
18770Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
18780Sstevel@tonic-gate 
18790Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
18800Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
18810Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
18820Sstevel@tonic-gate 	}
18830Sstevel@tonic-gate 
18840Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1885269Sericheng 
1886269Sericheng 	rw_exit(&dsp->ds_lock);
1887269Sericheng 	qreply(q, mp);
1888269Sericheng 	return (B_TRUE);
18890Sstevel@tonic-gate }
1890