10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
51502Sericheng  * Common Development and Distribution License (the "License").
61502Sericheng  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
221353Sericheng  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * Data-Link Driver
300Sstevel@tonic-gate  */
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include <sys/types.h>
330Sstevel@tonic-gate #include <sys/debug.h>
340Sstevel@tonic-gate #include <sys/sysmacros.h>
350Sstevel@tonic-gate #include <sys/stream.h>
360Sstevel@tonic-gate #include <sys/ddi.h>
370Sstevel@tonic-gate #include <sys/sunddi.h>
380Sstevel@tonic-gate #include <sys/strsun.h>
391184Skrgopi #include <sys/cpuvar.h>
400Sstevel@tonic-gate #include <sys/dlpi.h>
410Sstevel@tonic-gate #include <netinet/in.h>
420Sstevel@tonic-gate #include <sys/sdt.h>
430Sstevel@tonic-gate #include <sys/strsubr.h>
440Sstevel@tonic-gate #include <sys/vlan.h>
450Sstevel@tonic-gate #include <sys/mac.h>
460Sstevel@tonic-gate #include <sys/dls.h>
470Sstevel@tonic-gate #include <sys/dld.h>
480Sstevel@tonic-gate #include <sys/dld_impl.h>
491184Skrgopi #include <sys/dls_soft_ring.h>
500Sstevel@tonic-gate 
510Sstevel@tonic-gate typedef boolean_t proto_reqfunc_t(dld_str_t *, union DL_primitives *, mblk_t *);
520Sstevel@tonic-gate 
530Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req,
540Sstevel@tonic-gate     proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req,
550Sstevel@tonic-gate     proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req,
560Sstevel@tonic-gate     proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req,
570Sstevel@tonic-gate     proto_notify_req, proto_unitdata_req, proto_passive_req;
580Sstevel@tonic-gate 
59269Sericheng static void proto_poll_disable(dld_str_t *);
601184Skrgopi static boolean_t proto_poll_enable(dld_str_t *, dl_capab_dls_t *);
61269Sericheng static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
620Sstevel@tonic-gate 
631353Sericheng static task_func_t proto_process_unbind_req, proto_process_detach_req;
641353Sericheng 
651184Skrgopi static void proto_soft_ring_disable(dld_str_t *);
661184Skrgopi static boolean_t proto_soft_ring_enable(dld_str_t *, dl_capab_dls_t *);
671184Skrgopi static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *);
681184Skrgopi static void proto_change_soft_ring_fanout(dld_str_t *, int);
691184Skrgopi 
700Sstevel@tonic-gate #define	DL_ACK_PENDING(state) \
710Sstevel@tonic-gate 	((state) == DL_ATTACH_PENDING || \
720Sstevel@tonic-gate 	(state) == DL_DETACH_PENDING || \
730Sstevel@tonic-gate 	(state) == DL_BIND_PENDING || \
740Sstevel@tonic-gate 	(state) == DL_UNBIND_PENDING)
750Sstevel@tonic-gate 
760Sstevel@tonic-gate /*
77269Sericheng  * Process a DLPI protocol message.
78269Sericheng  * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ,
79269Sericheng  * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an
80269Sericheng  * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t
81269Sericheng  * as 'passive' and forbids it from being subsequently made 'active'
82269Sericheng  * by the above primitives.
830Sstevel@tonic-gate  */
840Sstevel@tonic-gate void
850Sstevel@tonic-gate dld_proto(dld_str_t *dsp, mblk_t *mp)
860Sstevel@tonic-gate {
870Sstevel@tonic-gate 	union DL_primitives	*udlp;
880Sstevel@tonic-gate 	t_uscalar_t		prim;
890Sstevel@tonic-gate 
900Sstevel@tonic-gate 	if (MBLKL(mp) < sizeof (t_uscalar_t)) {
910Sstevel@tonic-gate 		freemsg(mp);
920Sstevel@tonic-gate 		return;
930Sstevel@tonic-gate 	}
940Sstevel@tonic-gate 
950Sstevel@tonic-gate 	udlp = (union DL_primitives *)mp->b_rptr;
960Sstevel@tonic-gate 	prim = udlp->dl_primitive;
970Sstevel@tonic-gate 
98269Sericheng 	switch (prim) {
99269Sericheng 	case DL_INFO_REQ:
100269Sericheng 		(void) proto_info_req(dsp, udlp, mp);
101269Sericheng 		break;
102269Sericheng 	case DL_BIND_REQ:
103269Sericheng 		(void) proto_bind_req(dsp, udlp, mp);
104269Sericheng 		break;
105269Sericheng 	case DL_UNBIND_REQ:
106269Sericheng 		(void) proto_unbind_req(dsp, udlp, mp);
107269Sericheng 		break;
108269Sericheng 	case DL_UNITDATA_REQ:
109269Sericheng 		(void) proto_unitdata_req(dsp, udlp, mp);
110269Sericheng 		break;
111269Sericheng 	case DL_UDQOS_REQ:
112269Sericheng 		(void) proto_udqos_req(dsp, udlp, mp);
113269Sericheng 		break;
114269Sericheng 	case DL_ATTACH_REQ:
115269Sericheng 		(void) proto_attach_req(dsp, udlp, mp);
116269Sericheng 		break;
117269Sericheng 	case DL_DETACH_REQ:
118269Sericheng 		(void) proto_detach_req(dsp, udlp, mp);
119269Sericheng 		break;
120269Sericheng 	case DL_ENABMULTI_REQ:
121269Sericheng 		(void) proto_enabmulti_req(dsp, udlp, mp);
122269Sericheng 		break;
123269Sericheng 	case DL_DISABMULTI_REQ:
124269Sericheng 		(void) proto_disabmulti_req(dsp, udlp, mp);
125269Sericheng 		break;
126269Sericheng 	case DL_PROMISCON_REQ:
127269Sericheng 		(void) proto_promiscon_req(dsp, udlp, mp);
128269Sericheng 		break;
129269Sericheng 	case DL_PROMISCOFF_REQ:
130269Sericheng 		(void) proto_promiscoff_req(dsp, udlp, mp);
131269Sericheng 		break;
132269Sericheng 	case DL_PHYS_ADDR_REQ:
133269Sericheng 		(void) proto_physaddr_req(dsp, udlp, mp);
134269Sericheng 		break;
135269Sericheng 	case DL_SET_PHYS_ADDR_REQ:
136269Sericheng 		(void) proto_setphysaddr_req(dsp, udlp, mp);
137269Sericheng 		break;
138269Sericheng 	case DL_NOTIFY_REQ:
139269Sericheng 		(void) proto_notify_req(dsp, udlp, mp);
140269Sericheng 		break;
141269Sericheng 	case DL_CAPABILITY_REQ:
142269Sericheng 		(void) proto_capability_req(dsp, udlp, mp);
143269Sericheng 		break;
144269Sericheng 	case DL_PASSIVE_REQ:
145269Sericheng 		(void) proto_passive_req(dsp, udlp, mp);
146269Sericheng 		break;
147269Sericheng 	default:
148269Sericheng 		(void) proto_req(dsp, udlp, mp);
149269Sericheng 		break;
1500Sstevel@tonic-gate 	}
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate 
1530Sstevel@tonic-gate /*
1541353Sericheng  * Finish any pending operations.
1551353Sericheng  * Requests that need to be processed asynchronously will be handled
1561353Sericheng  * by a separate thread. After this function returns, other threads
1571353Sericheng  * will be allowed to enter dld; they will not be able to do anything
1581353Sericheng  * until ds_dlstate transitions to a non-pending state.
1590Sstevel@tonic-gate  */
160269Sericheng void
161269Sericheng dld_finish_pending_ops(dld_str_t *dsp)
1620Sstevel@tonic-gate {
1631353Sericheng 	task_func_t *op = NULL;
1641353Sericheng 
165269Sericheng 	ASSERT(MUTEX_HELD(&dsp->ds_thr_lock));
166269Sericheng 	ASSERT(dsp->ds_thr == 0);
1670Sstevel@tonic-gate 
1681353Sericheng 	op = dsp->ds_pending_op;
1691353Sericheng 	dsp->ds_pending_op = NULL;
1701353Sericheng 	mutex_exit(&dsp->ds_thr_lock);
1711353Sericheng 	if (op != NULL)
1721353Sericheng 		(void) taskq_dispatch(system_taskq, op, dsp, TQ_SLEEP);
1730Sstevel@tonic-gate }
1740Sstevel@tonic-gate 
175269Sericheng #define	NEG(x)	-(x)
1760Sstevel@tonic-gate 
1770Sstevel@tonic-gate typedef struct dl_info_ack_wrapper {
1780Sstevel@tonic-gate 	dl_info_ack_t		dl_info;
1792311Sseb 	uint8_t			dl_addr[MAXMACADDRLEN + sizeof (uint16_t)];
1802311Sseb 	uint8_t			dl_brdcst_addr[MAXMACADDRLEN];
1810Sstevel@tonic-gate 	dl_qos_cl_range1_t	dl_qos_range1;
1820Sstevel@tonic-gate 	dl_qos_cl_sel1_t	dl_qos_sel1;
1830Sstevel@tonic-gate } dl_info_ack_wrapper_t;
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate /*
186269Sericheng  * DL_INFO_REQ
1870Sstevel@tonic-gate  */
188269Sericheng /*ARGSUSED*/
189269Sericheng static boolean_t
190269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1910Sstevel@tonic-gate {
1920Sstevel@tonic-gate 	dl_info_ack_wrapper_t	*dlwp;
1930Sstevel@tonic-gate 	dl_info_ack_t		*dlp;
1940Sstevel@tonic-gate 	dl_qos_cl_sel1_t	*selp;
1950Sstevel@tonic-gate 	dl_qos_cl_range1_t	*rangep;
1960Sstevel@tonic-gate 	uint8_t			*addr;
1970Sstevel@tonic-gate 	uint8_t			*brdcst_addr;
1980Sstevel@tonic-gate 	uint_t			addr_length;
1990Sstevel@tonic-gate 	uint_t			sap_length;
200269Sericheng 	mac_info_t		minfo;
201269Sericheng 	mac_info_t		*minfop;
202269Sericheng 	queue_t			*q = dsp->ds_wq;
2030Sstevel@tonic-gate 
2040Sstevel@tonic-gate 	/*
2050Sstevel@tonic-gate 	 * Swap the request message for one large enough to contain the
2060Sstevel@tonic-gate 	 * wrapper structure defined above.
2070Sstevel@tonic-gate 	 */
208269Sericheng 	if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t),
2090Sstevel@tonic-gate 	    M_PCPROTO, 0)) == NULL)
210269Sericheng 		return (B_FALSE);
211269Sericheng 
212269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
2130Sstevel@tonic-gate 
2140Sstevel@tonic-gate 	bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t));
2150Sstevel@tonic-gate 	dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr;
2160Sstevel@tonic-gate 
2170Sstevel@tonic-gate 	dlp = &(dlwp->dl_info);
2180Sstevel@tonic-gate 	ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr);
2190Sstevel@tonic-gate 
2200Sstevel@tonic-gate 	dlp->dl_primitive = DL_INFO_ACK;
2210Sstevel@tonic-gate 
2220Sstevel@tonic-gate 	/*
2230Sstevel@tonic-gate 	 * Set up the sub-structure pointers.
2240Sstevel@tonic-gate 	 */
2250Sstevel@tonic-gate 	addr = dlwp->dl_addr;
2260Sstevel@tonic-gate 	brdcst_addr = dlwp->dl_brdcst_addr;
2270Sstevel@tonic-gate 	rangep = &(dlwp->dl_qos_range1);
2280Sstevel@tonic-gate 	selp = &(dlwp->dl_qos_sel1);
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 	/*
2310Sstevel@tonic-gate 	 * This driver supports only version 2 connectionless DLPI provider
2320Sstevel@tonic-gate 	 * nodes.
2330Sstevel@tonic-gate 	 */
2340Sstevel@tonic-gate 	dlp->dl_service_mode = DL_CLDLS;
2350Sstevel@tonic-gate 	dlp->dl_version = DL_VERSION_2;
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	/*
238269Sericheng 	 * Set the style of the provider
2390Sstevel@tonic-gate 	 */
240269Sericheng 	dlp->dl_provider_style = dsp->ds_style;
2410Sstevel@tonic-gate 	ASSERT(dlp->dl_provider_style == DL_STYLE1 ||
2420Sstevel@tonic-gate 	    dlp->dl_provider_style == DL_STYLE2);
2430Sstevel@tonic-gate 
2440Sstevel@tonic-gate 	/*
2450Sstevel@tonic-gate 	 * Set the current DLPI state.
2460Sstevel@tonic-gate 	 */
2470Sstevel@tonic-gate 	dlp->dl_current_state = dsp->ds_dlstate;
2480Sstevel@tonic-gate 
2490Sstevel@tonic-gate 	/*
250269Sericheng 	 * Gratuitously set the media type. This is to deal with modules
251269Sericheng 	 * that assume the media type is known prior to DL_ATTACH_REQ
2520Sstevel@tonic-gate 	 * being completed.
2530Sstevel@tonic-gate 	 */
2540Sstevel@tonic-gate 	dlp->dl_mac_type = DL_ETHER;
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 	/*
257269Sericheng 	 * If the stream is not at least attached we try to retrieve the
258269Sericheng 	 * mac_info using mac_info_get()
2590Sstevel@tonic-gate 	 */
2600Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_UNATTACHED ||
2610Sstevel@tonic-gate 	    dsp->ds_dlstate == DL_ATTACH_PENDING ||
262269Sericheng 	    dsp->ds_dlstate == DL_DETACH_PENDING) {
263269Sericheng 		if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) {
264269Sericheng 			/*
265269Sericheng 			 * Cannot find mac_info. giving up.
266269Sericheng 			 */
267269Sericheng 			goto done;
268269Sericheng 		}
269269Sericheng 		minfop = &minfo;
270269Sericheng 	} else {
271269Sericheng 		minfop = (mac_info_t *)dsp->ds_mip;
272269Sericheng 	}
2730Sstevel@tonic-gate 
2740Sstevel@tonic-gate 	/*
2750Sstevel@tonic-gate 	 * Set the media type (properly this time).
2760Sstevel@tonic-gate 	 */
277*3147Sxc151355 	if (dsp->ds_native)
278*3147Sxc151355 		dlp->dl_mac_type = minfop->mi_nativemedia;
279*3147Sxc151355 	else
280*3147Sxc151355 		dlp->dl_mac_type = minfop->mi_media;
2810Sstevel@tonic-gate 
2820Sstevel@tonic-gate 	/*
2830Sstevel@tonic-gate 	 * Set the DLSAP length. We only support 16 bit values and they
2840Sstevel@tonic-gate 	 * appear after the MAC address portion of DLSAP addresses.
2850Sstevel@tonic-gate 	 */
2860Sstevel@tonic-gate 	sap_length = sizeof (uint16_t);
2870Sstevel@tonic-gate 	dlp->dl_sap_length = NEG(sap_length);
2880Sstevel@tonic-gate 
2890Sstevel@tonic-gate 	/*
2900Sstevel@tonic-gate 	 * Set the minimum and maximum payload sizes.
2910Sstevel@tonic-gate 	 */
292269Sericheng 	dlp->dl_min_sdu = minfop->mi_sdu_min;
293269Sericheng 	dlp->dl_max_sdu = minfop->mi_sdu_max;
2940Sstevel@tonic-gate 
295269Sericheng 	addr_length = minfop->mi_addr_length;
2960Sstevel@tonic-gate 
2970Sstevel@tonic-gate 	/*
2980Sstevel@tonic-gate 	 * Copy in the media broadcast address.
2990Sstevel@tonic-gate 	 */
3002311Sseb 	if (minfop->mi_brdcst_addr != NULL) {
3012311Sseb 		dlp->dl_brdcst_addr_offset =
3022311Sseb 		    (uintptr_t)brdcst_addr - (uintptr_t)dlp;
3032311Sseb 		bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length);
3042311Sseb 		dlp->dl_brdcst_addr_length = addr_length;
3052311Sseb 	}
3060Sstevel@tonic-gate 
3072760Sdg199075 	dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp;
3082760Sdg199075 	dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t);
3090Sstevel@tonic-gate 
3102760Sdg199075 	rangep->dl_qos_type = DL_QOS_CL_RANGE1;
3112760Sdg199075 	rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN;
3122760Sdg199075 	rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN;
3132760Sdg199075 	rangep->dl_protection.dl_min = DL_UNKNOWN;
3142760Sdg199075 	rangep->dl_protection.dl_max = DL_UNKNOWN;
3152760Sdg199075 	rangep->dl_residual_error = DL_UNKNOWN;
3160Sstevel@tonic-gate 
3172760Sdg199075 	/*
3182760Sdg199075 	 * Specify the supported range of priorities.
3192760Sdg199075 	 */
3202760Sdg199075 	rangep->dl_priority.dl_min = 0;
3212760Sdg199075 	rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1;
3220Sstevel@tonic-gate 
3232760Sdg199075 	dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp;
3242760Sdg199075 	dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t);
3250Sstevel@tonic-gate 
3262760Sdg199075 	selp->dl_qos_type = DL_QOS_CL_SEL1;
3272760Sdg199075 	selp->dl_trans_delay = DL_UNKNOWN;
3282760Sdg199075 	selp->dl_protection = DL_UNKNOWN;
3292760Sdg199075 	selp->dl_residual_error = DL_UNKNOWN;
3302760Sdg199075 
3312760Sdg199075 	/*
3322760Sdg199075 	 * Specify the current priority (which can be changed by
3332760Sdg199075 	 * the DL_UDQOS_REQ primitive).
3342760Sdg199075 	 */
3352760Sdg199075 	selp->dl_priority = dsp->ds_pri;
3360Sstevel@tonic-gate 
3370Sstevel@tonic-gate 	dlp->dl_addr_length = addr_length + sizeof (uint16_t);
3380Sstevel@tonic-gate 	if (dsp->ds_dlstate == DL_IDLE) {
3390Sstevel@tonic-gate 		/*
3400Sstevel@tonic-gate 		 * The stream is bound. Therefore we can formulate a valid
3410Sstevel@tonic-gate 		 * DLSAP address.
3420Sstevel@tonic-gate 		 */
3430Sstevel@tonic-gate 		dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp;
3442311Sseb 		if (addr_length > 0)
3452311Sseb 			bcopy(dsp->ds_curr_addr, addr, addr_length);
3460Sstevel@tonic-gate 		*(uint16_t *)(addr + addr_length) = dsp->ds_sap;
3470Sstevel@tonic-gate 	}
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate done:
3500Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0));
3510Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_qos_range_offset != 0,
3520Sstevel@tonic-gate 	    dlp->dl_qos_range_length != 0));
3530Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0));
3540Sstevel@tonic-gate 	ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0,
3550Sstevel@tonic-gate 	    dlp->dl_brdcst_addr_length != 0));
3560Sstevel@tonic-gate 
357269Sericheng 	rw_exit(&dsp->ds_lock);
358269Sericheng 
359269Sericheng 	qreply(q, mp);
360269Sericheng 	return (B_TRUE);
361269Sericheng }
362269Sericheng 
363269Sericheng /*
364269Sericheng  * DL_ATTACH_REQ
365269Sericheng  */
366269Sericheng static boolean_t
367269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
368269Sericheng {
369269Sericheng 	dl_attach_req_t	*dlp = (dl_attach_req_t *)udlp;
370269Sericheng 	int		err = 0;
371269Sericheng 	t_uscalar_t	dl_err;
372269Sericheng 	queue_t		*q = dsp->ds_wq;
373269Sericheng 
374269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
375269Sericheng 
376269Sericheng 	if (MBLKL(mp) < sizeof (dl_attach_req_t) ||
377269Sericheng 	    dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) {
378269Sericheng 		dl_err = DL_BADPRIM;
379269Sericheng 		goto failed;
380269Sericheng 	}
381269Sericheng 
382269Sericheng 	if (dsp->ds_dlstate != DL_UNATTACHED) {
383269Sericheng 		dl_err = DL_OUTSTATE;
384269Sericheng 		goto failed;
385269Sericheng 	}
386269Sericheng 
387269Sericheng 	dsp->ds_dlstate = DL_ATTACH_PENDING;
388269Sericheng 
389269Sericheng 	err = dld_str_attach(dsp, dlp->dl_ppa);
390269Sericheng 	if (err != 0) {
391269Sericheng 		switch (err) {
392269Sericheng 		case ENOENT:
393269Sericheng 			dl_err = DL_BADPPA;
394269Sericheng 			err = 0;
395269Sericheng 			break;
396269Sericheng 		default:
397269Sericheng 			dl_err = DL_SYSERR;
398269Sericheng 			break;
399269Sericheng 		}
400269Sericheng 		dsp->ds_dlstate = DL_UNATTACHED;
401269Sericheng 		goto failed;
402269Sericheng 	}
403269Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBOUND);
404269Sericheng 	rw_exit(&dsp->ds_lock);
405269Sericheng 
406269Sericheng 	dlokack(q, mp, DL_ATTACH_REQ);
407269Sericheng 	return (B_TRUE);
408269Sericheng failed:
409269Sericheng 	rw_exit(&dsp->ds_lock);
410269Sericheng 	dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err);
411269Sericheng 	return (B_FALSE);
4120Sstevel@tonic-gate }
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate /*
415269Sericheng  * DL_DETACH_REQ
4160Sstevel@tonic-gate  */
4171353Sericheng static void
4181353Sericheng proto_process_detach_req(void *arg)
4191353Sericheng {
4201353Sericheng 	dld_str_t	*dsp = arg;
4211353Sericheng 	mblk_t		*mp;
4221353Sericheng 
4231353Sericheng 	/*
4241353Sericheng 	 * We don't need to hold locks because no other thread
4251353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
4261353Sericheng 	 */
4271353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
4281353Sericheng 	ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING);
4291353Sericheng 
4301353Sericheng 	mp = dsp->ds_pending_req;
4311353Sericheng 	dsp->ds_pending_req = NULL;
4321353Sericheng 	dld_str_detach(dsp);
4331353Sericheng 	dlokack(dsp->ds_wq, mp, DL_DETACH_REQ);
4341353Sericheng 
4351353Sericheng 	DLD_WAKEUP(dsp);
4361353Sericheng }
4371353Sericheng 
438269Sericheng /*ARGSUSED*/
439269Sericheng static boolean_t
440269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4410Sstevel@tonic-gate {
442269Sericheng 	queue_t		*q = dsp->ds_wq;
443269Sericheng 	t_uscalar_t	dl_err;
4440Sstevel@tonic-gate 
445269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
4460Sstevel@tonic-gate 
447269Sericheng 	if (MBLKL(mp) < sizeof (dl_detach_req_t)) {
448269Sericheng 		dl_err = DL_BADPRIM;
449269Sericheng 		goto failed;
450269Sericheng 	}
4510Sstevel@tonic-gate 
452269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
453269Sericheng 		dl_err = DL_OUTSTATE;
454269Sericheng 		goto failed;
4550Sstevel@tonic-gate 	}
4560Sstevel@tonic-gate 
457269Sericheng 	if (dsp->ds_style == DL_STYLE1) {
458269Sericheng 		dl_err = DL_BADPRIM;
459269Sericheng 		goto failed;
460269Sericheng 	}
461269Sericheng 
462269Sericheng 	dsp->ds_dlstate = DL_DETACH_PENDING;
463269Sericheng 
464269Sericheng 	/*
465269Sericheng 	 * Complete the detach when the driver is single-threaded.
466269Sericheng 	 */
467269Sericheng 	mutex_enter(&dsp->ds_thr_lock);
4681353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
4691353Sericheng 	dsp->ds_pending_req = mp;
4701353Sericheng 	dsp->ds_pending_op = proto_process_detach_req;
4711353Sericheng 	dsp->ds_pending_cnt++;
472269Sericheng 	mutex_exit(&dsp->ds_thr_lock);
473269Sericheng 	rw_exit(&dsp->ds_lock);
474269Sericheng 
475269Sericheng 	return (B_TRUE);
476269Sericheng failed:
477269Sericheng 	rw_exit(&dsp->ds_lock);
478269Sericheng 	dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0);
479269Sericheng 	return (B_FALSE);
4800Sstevel@tonic-gate }
4810Sstevel@tonic-gate 
4820Sstevel@tonic-gate /*
483269Sericheng  * DL_BIND_REQ
4840Sstevel@tonic-gate  */
485269Sericheng static boolean_t
486269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
4870Sstevel@tonic-gate {
488269Sericheng 	dl_bind_req_t	*dlp = (dl_bind_req_t *)udlp;
489269Sericheng 	int		err = 0;
4903037Syz147064 	uint8_t		dlsap_addr[MAXMACADDRLEN + sizeof (uint16_t)];
4913037Syz147064 	uint_t		dlsap_addr_length;
492269Sericheng 	t_uscalar_t	dl_err;
493269Sericheng 	t_scalar_t	sap;
494269Sericheng 	queue_t		*q = dsp->ds_wq;
495269Sericheng 
4961521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
4971521Syz147064 
498269Sericheng 	if (MBLKL(mp) < sizeof (dl_bind_req_t)) {
499269Sericheng 		dl_err = DL_BADPRIM;
500269Sericheng 		goto failed;
501269Sericheng 	}
502269Sericheng 
503269Sericheng 	if (dlp->dl_xidtest_flg != 0) {
504269Sericheng 		dl_err = DL_NOAUTO;
505269Sericheng 		goto failed;
506269Sericheng 	}
507269Sericheng 
508269Sericheng 	if (dlp->dl_service_mode != DL_CLDLS) {
509269Sericheng 		dl_err = DL_UNSUPPORTED;
510269Sericheng 		goto failed;
511269Sericheng 	}
512269Sericheng 
513269Sericheng 	if (dsp->ds_dlstate != DL_UNBOUND) {
514269Sericheng 		dl_err = DL_OUTSTATE;
515269Sericheng 		goto failed;
516269Sericheng 	}
5170Sstevel@tonic-gate 
518269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
519269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
520269Sericheng 		dl_err = DL_SYSERR;
521269Sericheng 		err = EBUSY;
522269Sericheng 		goto failed;
523269Sericheng 	}
524269Sericheng 
525269Sericheng 	dsp->ds_dlstate = DL_BIND_PENDING;
526269Sericheng 	/*
527269Sericheng 	 * Set the receive callback.
528269Sericheng 	 */
529269Sericheng 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ?
530269Sericheng 	    dld_str_rx_raw : dld_str_rx_unitdata, dsp);
5310Sstevel@tonic-gate 
532269Sericheng 	/*
533269Sericheng 	 * Bind the channel such that it can receive packets.
534269Sericheng 	 */
535269Sericheng 	sap = dsp->ds_sap = dlp->dl_sap;
536269Sericheng 	err = dls_bind(dsp->ds_dc, dlp->dl_sap);
537269Sericheng 	if (err != 0) {
538269Sericheng 		switch (err) {
539269Sericheng 		case EINVAL:
540269Sericheng 			dl_err = DL_BADADDR;
541269Sericheng 			err = 0;
542269Sericheng 			break;
543269Sericheng 		default:
544269Sericheng 			dl_err = DL_SYSERR;
545269Sericheng 			break;
546269Sericheng 		}
547269Sericheng 		dsp->ds_dlstate = DL_UNBOUND;
548269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
549269Sericheng 			dls_active_clear(dsp->ds_dc);
550269Sericheng 
5510Sstevel@tonic-gate 		goto failed;
552269Sericheng 	}
5530Sstevel@tonic-gate 
5540Sstevel@tonic-gate 	/*
5550Sstevel@tonic-gate 	 * Copy in MAC address.
5560Sstevel@tonic-gate 	 */
5573037Syz147064 	dlsap_addr_length = dsp->ds_mip->mi_addr_length;
5583037Syz147064 	bcopy(dsp->ds_curr_addr, dlsap_addr, dlsap_addr_length);
5590Sstevel@tonic-gate 
5600Sstevel@tonic-gate 	/*
5613037Syz147064 	 * Copy in the SAP.
5620Sstevel@tonic-gate 	 */
5633037Syz147064 	*(uint16_t *)(dlsap_addr + dlsap_addr_length) = dsp->ds_sap;
5643037Syz147064 	dlsap_addr_length += sizeof (uint16_t);
5650Sstevel@tonic-gate 
5660Sstevel@tonic-gate 	dsp->ds_dlstate = DL_IDLE;
567269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
568269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
5690Sstevel@tonic-gate 
570269Sericheng 	rw_exit(&dsp->ds_lock);
571269Sericheng 
5723037Syz147064 	dlbindack(q, mp, sap, dlsap_addr, dlsap_addr_length, 0, 0);
573269Sericheng 	return (B_TRUE);
5740Sstevel@tonic-gate failed:
575269Sericheng 	rw_exit(&dsp->ds_lock);
576269Sericheng 	dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err);
577269Sericheng 	return (B_FALSE);
5780Sstevel@tonic-gate }
5790Sstevel@tonic-gate 
5800Sstevel@tonic-gate /*
581269Sericheng  * DL_UNBIND_REQ
5820Sstevel@tonic-gate  */
583269Sericheng /*ARGSUSED*/
5841353Sericheng static void
5851353Sericheng proto_process_unbind_req(void *arg)
5860Sstevel@tonic-gate {
5871353Sericheng 	dld_str_t	*dsp = arg;
5881353Sericheng 	mblk_t		*mp;
589269Sericheng 
5901353Sericheng 	/*
5911353Sericheng 	 * We don't need to hold locks because no other thread
5921353Sericheng 	 * would manipulate dsp while it is in a PENDING state.
5931353Sericheng 	 */
5941353Sericheng 	ASSERT(dsp->ds_pending_req != NULL);
5951353Sericheng 	ASSERT(dsp->ds_dlstate == DL_UNBIND_PENDING);
596269Sericheng 
597269Sericheng 	/*
598269Sericheng 	 * Flush any remaining packets scheduled for transmission.
599269Sericheng 	 */
600269Sericheng 	dld_tx_flush(dsp);
601269Sericheng 
602269Sericheng 	/*
603269Sericheng 	 * Unbind the channel to stop packets being received.
604269Sericheng 	 */
605269Sericheng 	dls_unbind(dsp->ds_dc);
606269Sericheng 
607269Sericheng 	/*
608269Sericheng 	 * Disable polling mode, if it is enabled.
609269Sericheng 	 */
610269Sericheng 	proto_poll_disable(dsp);
611269Sericheng 
612269Sericheng 	/*
6133115Syl150051 	 * Clear LSO flags.
6143115Syl150051 	 */
6153115Syl150051 	dsp->ds_lso = B_FALSE;
6163115Syl150051 	dsp->ds_lso_max = 0;
6173115Syl150051 
6183115Syl150051 	/*
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
6301353Sericheng 	 * 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 	 */
6351353Sericheng 	if (dls_soft_ring_workers(dsp->ds_dc))
6361353Sericheng 		dls_soft_ring_disable(dsp->ds_dc);
6371353Sericheng 
6381353Sericheng 	mp = dsp->ds_pending_req;
6391353Sericheng 	dsp->ds_pending_req = NULL;
6401353Sericheng 	dsp->ds_dlstate = DL_UNBOUND;
6411353Sericheng 	dlokack(dsp->ds_wq, mp, DL_UNBIND_REQ);
6421353Sericheng 
6431353Sericheng 	DLD_WAKEUP(dsp);
6441353Sericheng }
6451353Sericheng 
6461353Sericheng /*ARGSUSED*/
6471353Sericheng static boolean_t
6481353Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
6491353Sericheng {
6501353Sericheng 	queue_t		*q = dsp->ds_wq;
6511353Sericheng 	t_uscalar_t	dl_err;
6521353Sericheng 
6531353Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
6541353Sericheng 
6551353Sericheng 	if (MBLKL(mp) < sizeof (dl_unbind_req_t)) {
6561353Sericheng 		dl_err = DL_BADPRIM;
6571353Sericheng 		goto failed;
6581184Skrgopi 	}
6591184Skrgopi 
6601353Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
6611353Sericheng 		dl_err = DL_OUTSTATE;
6621353Sericheng 		goto failed;
6631353Sericheng 	}
6641353Sericheng 
6651353Sericheng 	dsp->ds_dlstate = DL_UNBIND_PENDING;
6661353Sericheng 
6671353Sericheng 	mutex_enter(&dsp->ds_thr_lock);
6681353Sericheng 	ASSERT(dsp->ds_pending_req == NULL);
6691353Sericheng 	dsp->ds_pending_req = mp;
6701353Sericheng 	dsp->ds_pending_op = proto_process_unbind_req;
6711353Sericheng 	dsp->ds_pending_cnt++;
6721353Sericheng 	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 
6941521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
6951521Syz147064 
696269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) {
697269Sericheng 		dl_err = DL_BADPRIM;
698269Sericheng 		goto failed;
699269Sericheng 	}
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 
7711521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
772269Sericheng 
773269Sericheng 	if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) {
774269Sericheng 		dl_err = DL_BADPRIM;
7750Sstevel@tonic-gate 		goto failed;
776269Sericheng 	}
7770Sstevel@tonic-gate 
778269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
779269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
780269Sericheng 		dl_err = DL_OUTSTATE;
7810Sstevel@tonic-gate 		goto failed;
782269Sericheng 	}
7830Sstevel@tonic-gate 
784269Sericheng 	promisc_saved = dsp->ds_promisc;
785269Sericheng 	switch (dlp->dl_level) {
786269Sericheng 	case DL_PROMISC_SAP:
787269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) {
788269Sericheng 			dl_err = DL_NOTENAB;
789269Sericheng 			goto failed;
790269Sericheng 		}
791269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_SAP;
7920Sstevel@tonic-gate 		break;
7930Sstevel@tonic-gate 
794269Sericheng 	case DL_PROMISC_MULTI:
795269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) {
796269Sericheng 			dl_err = DL_NOTENAB;
797269Sericheng 			goto failed;
798269Sericheng 		}
799269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_MULTI;
800269Sericheng 		break;
801269Sericheng 
802269Sericheng 	case DL_PROMISC_PHYS:
803269Sericheng 		if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) {
804269Sericheng 			dl_err = DL_NOTENAB;
805269Sericheng 			goto failed;
806269Sericheng 		}
807269Sericheng 		dsp->ds_promisc &= ~DLS_PROMISC_PHYS;
8080Sstevel@tonic-gate 		break;
8090Sstevel@tonic-gate 
8100Sstevel@tonic-gate 	default:
811269Sericheng 		dl_err = DL_NOTSUPPORTED;
812269Sericheng 		goto failed;
813269Sericheng 	}
814269Sericheng 
815269Sericheng 	/*
816269Sericheng 	 * Adjust channel promiscuity.
817269Sericheng 	 */
818269Sericheng 	err = dls_promisc(dsp->ds_dc, dsp->ds_promisc);
819269Sericheng 	if (err != 0) {
820269Sericheng 		dsp->ds_promisc = promisc_saved;
8210Sstevel@tonic-gate 		dl_err = DL_SYSERR;
822269Sericheng 		goto failed;
823269Sericheng 	}
824269Sericheng 
825269Sericheng 	rw_exit(&dsp->ds_lock);
826269Sericheng 	dlokack(q, mp, DL_PROMISCOFF_REQ);
827269Sericheng 	return (B_TRUE);
828269Sericheng failed:
829269Sericheng 	rw_exit(&dsp->ds_lock);
830269Sericheng 	dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err);
831269Sericheng 	return (B_FALSE);
832269Sericheng }
833269Sericheng 
834269Sericheng /*
835269Sericheng  * DL_ENABMULTI_REQ
836269Sericheng  */
837269Sericheng static boolean_t
838269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
839269Sericheng {
840269Sericheng 	dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp;
841269Sericheng 	int		err = 0;
842269Sericheng 	t_uscalar_t	dl_err;
843269Sericheng 	queue_t		*q = dsp->ds_wq;
844269Sericheng 
845269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
846269Sericheng 
847269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
848269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
849269Sericheng 		dl_err = DL_OUTSTATE;
850269Sericheng 		goto failed;
851269Sericheng 	}
852269Sericheng 
853269Sericheng 	if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) ||
854269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
855269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
856269Sericheng 		dl_err = DL_BADPRIM;
857269Sericheng 		goto failed;
858269Sericheng 	}
859269Sericheng 
860269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
861269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
862269Sericheng 		dl_err = DL_SYSERR;
863269Sericheng 		err = EBUSY;
864269Sericheng 		goto failed;
8650Sstevel@tonic-gate 	}
8660Sstevel@tonic-gate 
867269Sericheng 	err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
868269Sericheng 	if (err != 0) {
869269Sericheng 		switch (err) {
870269Sericheng 		case EINVAL:
871269Sericheng 			dl_err = DL_BADADDR;
872269Sericheng 			err = 0;
873269Sericheng 			break;
874269Sericheng 		case ENOSPC:
875269Sericheng 			dl_err = DL_TOOMANY;
876269Sericheng 			err = 0;
877269Sericheng 			break;
878269Sericheng 		default:
879269Sericheng 			dl_err = DL_SYSERR;
880269Sericheng 			break;
881269Sericheng 		}
882269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
883269Sericheng 			dls_active_clear(dsp->ds_dc);
884269Sericheng 
885269Sericheng 		goto failed;
886269Sericheng 	}
887269Sericheng 
888269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
889269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
890269Sericheng 
891269Sericheng 	rw_exit(&dsp->ds_lock);
892269Sericheng 	dlokack(q, mp, DL_ENABMULTI_REQ);
893269Sericheng 	return (B_TRUE);
894269Sericheng failed:
895269Sericheng 	rw_exit(&dsp->ds_lock);
896269Sericheng 	dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err);
897269Sericheng 	return (B_FALSE);
898269Sericheng }
899269Sericheng 
900269Sericheng /*
901269Sericheng  * DL_DISABMULTI_REQ
902269Sericheng  */
903269Sericheng static boolean_t
904269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
905269Sericheng {
906269Sericheng 	dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp;
907269Sericheng 	int		err = 0;
908269Sericheng 	t_uscalar_t	dl_err;
909269Sericheng 	queue_t		*q = dsp->ds_wq;
910269Sericheng 
911269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
912269Sericheng 
913269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
914269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
915269Sericheng 		dl_err = DL_OUTSTATE;
916269Sericheng 		goto failed;
917269Sericheng 	}
918269Sericheng 
919269Sericheng 	if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) ||
920269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
921269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
922269Sericheng 		dl_err = DL_BADPRIM;
923269Sericheng 		goto failed;
924269Sericheng 	}
925269Sericheng 
926269Sericheng 	err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset);
927269Sericheng 	if (err != 0) {
928269Sericheng 	switch (err) {
929269Sericheng 		case EINVAL:
930269Sericheng 			dl_err = DL_BADADDR;
931269Sericheng 			err = 0;
932269Sericheng 			break;
933269Sericheng 
934269Sericheng 		case ENOENT:
935269Sericheng 			dl_err = DL_NOTENAB;
936269Sericheng 			err = 0;
937269Sericheng 			break;
938269Sericheng 
939269Sericheng 		default:
940269Sericheng 			dl_err = DL_SYSERR;
941269Sericheng 			break;
942269Sericheng 		}
943269Sericheng 		goto failed;
944269Sericheng 	}
945269Sericheng 
946269Sericheng 	rw_exit(&dsp->ds_lock);
947269Sericheng 	dlokack(q, mp, DL_DISABMULTI_REQ);
948269Sericheng 	return (B_TRUE);
949269Sericheng failed:
950269Sericheng 	rw_exit(&dsp->ds_lock);
951269Sericheng 	dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err);
952269Sericheng 	return (B_FALSE);
9530Sstevel@tonic-gate }
9540Sstevel@tonic-gate 
9550Sstevel@tonic-gate /*
956269Sericheng  * DL_PHYS_ADDR_REQ
9570Sstevel@tonic-gate  */
958269Sericheng static boolean_t
959269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
9600Sstevel@tonic-gate {
961269Sericheng 	dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp;
962269Sericheng 	queue_t		*q = dsp->ds_wq;
963269Sericheng 	t_uscalar_t	dl_err;
964269Sericheng 	char		*addr;
965269Sericheng 	uint_t		addr_length;
966269Sericheng 
967269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
968269Sericheng 
969269Sericheng 	if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) {
970269Sericheng 		dl_err = DL_BADPRIM;
971269Sericheng 		goto failed;
972269Sericheng 	}
973269Sericheng 
974269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
975269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
976269Sericheng 		dl_err = DL_OUTSTATE;
977269Sericheng 		goto failed;
978269Sericheng 	}
9790Sstevel@tonic-gate 
980269Sericheng 	if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR &&
981269Sericheng 	    dlp->dl_addr_type != DL_FACT_PHYS_ADDR) {
982269Sericheng 		dl_err = DL_UNSUPPORTED;
9830Sstevel@tonic-gate 		goto failed;
984269Sericheng 	}
9850Sstevel@tonic-gate 
986269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
987269Sericheng 	addr = kmem_alloc(addr_length, KM_NOSLEEP);
988269Sericheng 	if (addr == NULL) {
989269Sericheng 		rw_exit(&dsp->ds_lock);
990269Sericheng 		merror(q, mp, ENOSR);
991269Sericheng 		return (B_FALSE);
992269Sericheng 	}
9930Sstevel@tonic-gate 
994269Sericheng 	/*
995269Sericheng 	 * Copy out the address before we drop the lock; we don't
996269Sericheng 	 * want to call dlphysaddrack() while holding ds_lock.
997269Sericheng 	 */
998269Sericheng 	bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ?
999269Sericheng 	    dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length);
1000269Sericheng 
1001269Sericheng 	rw_exit(&dsp->ds_lock);
1002269Sericheng 	dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length);
1003269Sericheng 	kmem_free(addr, addr_length);
1004269Sericheng 	return (B_TRUE);
10050Sstevel@tonic-gate failed:
1006269Sericheng 	rw_exit(&dsp->ds_lock);
1007269Sericheng 	dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0);
1008269Sericheng 	return (B_FALSE);
1009269Sericheng }
10100Sstevel@tonic-gate 
1011269Sericheng /*
1012269Sericheng  * DL_SET_PHYS_ADDR_REQ
1013269Sericheng  */
1014269Sericheng static boolean_t
1015269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1016269Sericheng {
1017269Sericheng 	dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp;
1018269Sericheng 	int		err = 0;
1019269Sericheng 	t_uscalar_t	dl_err;
1020269Sericheng 	queue_t		*q = dsp->ds_wq;
10210Sstevel@tonic-gate 
1022269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1023269Sericheng 
1024269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1025269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1026269Sericheng 		dl_err = DL_OUTSTATE;
1027269Sericheng 		goto failed;
1028269Sericheng 	}
1029269Sericheng 
1030269Sericheng 	if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) ||
1031269Sericheng 	    !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) ||
1032269Sericheng 	    dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) {
1033269Sericheng 		dl_err = DL_BADPRIM;
1034269Sericheng 		goto failed;
10350Sstevel@tonic-gate 	}
10360Sstevel@tonic-gate 
1037269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED &&
1038269Sericheng 	    !dls_active_set(dsp->ds_dc)) {
1039269Sericheng 		dl_err = DL_SYSERR;
1040269Sericheng 		err = EBUSY;
1041269Sericheng 		goto failed;
1042269Sericheng 	}
1043269Sericheng 
1044269Sericheng 	err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset);
1045269Sericheng 	if (err != 0) {
1046269Sericheng 		switch (err) {
1047269Sericheng 		case EINVAL:
1048269Sericheng 			dl_err = DL_BADADDR;
1049269Sericheng 			err = 0;
1050269Sericheng 			break;
1051269Sericheng 
1052269Sericheng 		default:
1053269Sericheng 			dl_err = DL_SYSERR;
1054269Sericheng 			break;
1055269Sericheng 		}
1056269Sericheng 		if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1057269Sericheng 			dls_active_clear(dsp->ds_dc);
1058269Sericheng 
1059269Sericheng 		goto failed;
1060269Sericheng 	}
1061269Sericheng 	if (dsp->ds_passivestate == DLD_UNINITIALIZED)
1062269Sericheng 		dsp->ds_passivestate = DLD_ACTIVE;
1063269Sericheng 
1064269Sericheng 	rw_exit(&dsp->ds_lock);
1065269Sericheng 	dlokack(q, mp, DL_SET_PHYS_ADDR_REQ);
1066269Sericheng 	return (B_TRUE);
1067269Sericheng failed:
1068269Sericheng 	rw_exit(&dsp->ds_lock);
1069269Sericheng 	dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err);
1070269Sericheng 	return (B_FALSE);
1071269Sericheng }
1072269Sericheng 
1073269Sericheng /*
1074269Sericheng  * DL_UDQOS_REQ
1075269Sericheng  */
1076269Sericheng static boolean_t
1077269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1078269Sericheng {
1079269Sericheng 	dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp;
1080269Sericheng 	dl_qos_cl_sel1_t *selp;
1081269Sericheng 	int		off, len;
1082269Sericheng 	t_uscalar_t	dl_err;
1083269Sericheng 	queue_t		*q = dsp->ds_wq;
1084269Sericheng 
1085269Sericheng 	off = dlp->dl_qos_offset;
1086269Sericheng 	len = dlp->dl_qos_length;
1087269Sericheng 
10881521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
10891521Syz147064 
1090269Sericheng 	if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) {
1091269Sericheng 		dl_err = DL_BADPRIM;
1092269Sericheng 		goto failed;
1093269Sericheng 	}
1094269Sericheng 
1095269Sericheng 	selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off);
1096269Sericheng 	if (selp->dl_qos_type != DL_QOS_CL_SEL1) {
1097269Sericheng 		dl_err = DL_BADQOSTYPE;
1098269Sericheng 		goto failed;
1099269Sericheng 	}
1100269Sericheng 
11012760Sdg199075 	if (selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 ||
1102269Sericheng 	    selp->dl_priority < 0) {
1103269Sericheng 		dl_err = DL_BADQOSPARAM;
1104269Sericheng 		goto failed;
1105269Sericheng 	}
1106269Sericheng 
1107269Sericheng 	dsp->ds_pri = selp->dl_priority;
1108269Sericheng 
1109269Sericheng 	rw_exit(&dsp->ds_lock);
1110269Sericheng 	dlokack(q, mp, DL_UDQOS_REQ);
1111269Sericheng 	return (B_TRUE);
1112269Sericheng failed:
1113269Sericheng 	rw_exit(&dsp->ds_lock);
1114269Sericheng 	dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0);
1115269Sericheng 	return (B_FALSE);
11160Sstevel@tonic-gate }
11170Sstevel@tonic-gate 
11181184Skrgopi static boolean_t
11191184Skrgopi check_ip_above(queue_t *q)
11201184Skrgopi {
11211184Skrgopi 	queue_t		*next_q;
11221184Skrgopi 	boolean_t	ret = B_TRUE;
11231184Skrgopi 
11241184Skrgopi 	claimstr(q);
11251184Skrgopi 	next_q = q->q_next;
11261184Skrgopi 	if (strcmp(next_q->q_qinfo->qi_minfo->mi_idname, "ip") != 0)
11271184Skrgopi 		ret = B_FALSE;
11281184Skrgopi 	releasestr(q);
11291184Skrgopi 	return (ret);
11301184Skrgopi }
11311184Skrgopi 
11320Sstevel@tonic-gate /*
1133269Sericheng  * DL_CAPABILITY_REQ
11340Sstevel@tonic-gate  */
1135269Sericheng /*ARGSUSED*/
1136269Sericheng static boolean_t
1137269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
11380Sstevel@tonic-gate {
1139269Sericheng 	dl_capability_req_t *dlp = (dl_capability_req_t *)udlp;
1140269Sericheng 	dl_capability_sub_t *sp;
1141269Sericheng 	size_t		size, len;
1142269Sericheng 	offset_t	off, end;
1143269Sericheng 	t_uscalar_t	dl_err;
1144269Sericheng 	queue_t		*q = dsp->ds_wq;
1145269Sericheng 	boolean_t	upgraded;
1146269Sericheng 
1147269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1148269Sericheng 
1149269Sericheng 	if (MBLKL(mp) < sizeof (dl_capability_req_t)) {
1150269Sericheng 		dl_err = DL_BADPRIM;
1151269Sericheng 		goto failed;
1152269Sericheng 	}
1153269Sericheng 
1154269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1155269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1156269Sericheng 		dl_err = DL_OUTSTATE;
1157269Sericheng 		goto failed;
1158269Sericheng 	}
1159269Sericheng 
1160269Sericheng 	/*
1161269Sericheng 	 * This request is overloaded. If there are no requested capabilities
1162269Sericheng 	 * then we just want to acknowledge with all the capabilities we
1163269Sericheng 	 * support. Otherwise we enable the set of capabilities requested.
1164269Sericheng 	 */
1165269Sericheng 	if (dlp->dl_sub_length == 0) {
1166269Sericheng 		/* callee drops lock */
1167269Sericheng 		return (proto_capability_advertise(dsp, mp));
1168269Sericheng 	}
1169269Sericheng 
1170269Sericheng 	if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) {
1171269Sericheng 		dl_err = DL_BADPRIM;
1172269Sericheng 		goto failed;
1173269Sericheng 	}
1174269Sericheng 
1175269Sericheng 	dlp->dl_primitive = DL_CAPABILITY_ACK;
1176269Sericheng 
1177269Sericheng 	off = dlp->dl_sub_offset;
1178269Sericheng 	len = dlp->dl_sub_length;
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 	/*
1181269Sericheng 	 * Walk the list of capabilities to be enabled.
11820Sstevel@tonic-gate 	 */
1183269Sericheng 	upgraded = B_FALSE;
1184269Sericheng 	for (end = off + len; off < end; ) {
1185269Sericheng 		sp = (dl_capability_sub_t *)(mp->b_rptr + off);
1186269Sericheng 		size = sizeof (dl_capability_sub_t) + sp->dl_length;
1187269Sericheng 
1188269Sericheng 		if (off + size > end ||
1189269Sericheng 		    !IS_P2ALIGNED(off, sizeof (uint32_t))) {
1190269Sericheng 			dl_err = DL_BADPRIM;
1191269Sericheng 			goto failed;
1192269Sericheng 		}
1193269Sericheng 
1194269Sericheng 		switch (sp->dl_cap) {
1195269Sericheng 		/*
1196269Sericheng 		 * TCP/IP checksum offload to hardware.
1197269Sericheng 		 */
1198269Sericheng 		case DL_CAPAB_HCKSUM: {
1199269Sericheng 			dl_capab_hcksum_t *hcksump;
1200269Sericheng 			dl_capab_hcksum_t hcksum;
1201269Sericheng 
1202269Sericheng 			hcksump = (dl_capab_hcksum_t *)&sp[1];
1203269Sericheng 			/*
1204269Sericheng 			 * Copy for alignment.
1205269Sericheng 			 */
1206269Sericheng 			bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t));
1207269Sericheng 			dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
1208269Sericheng 			bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t));
1209269Sericheng 			break;
1210269Sericheng 		}
1211269Sericheng 
1212269Sericheng 		/*
12133115Syl150051 		 * Large segment offload. (LSO)
12143115Syl150051 		 */
12153115Syl150051 		case DL_CAPAB_LSO: {
12163115Syl150051 			dl_capab_lso_t *lsop;
12173115Syl150051 			dl_capab_lso_t lso;
12183115Syl150051 
12193115Syl150051 			lsop = (dl_capab_lso_t *)&sp[1];
12203115Syl150051 			/*
12213115Syl150051 			 * Copy for alignment.
12223115Syl150051 			 */
12233115Syl150051 			bcopy(lsop, &lso, sizeof (dl_capab_lso_t));
12243115Syl150051 			dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
12253115Syl150051 			bcopy(&lso, lsop, sizeof (dl_capab_lso_t));
12263115Syl150051 			break;
12273115Syl150051 		}
12283115Syl150051 
12293115Syl150051 		/*
1230269Sericheng 		 * IP polling interface.
1231269Sericheng 		 */
1232269Sericheng 		case DL_CAPAB_POLL: {
12331184Skrgopi 			dl_capab_dls_t *pollp;
12341184Skrgopi 			dl_capab_dls_t	poll;
1235269Sericheng 
12361184Skrgopi 			pollp = (dl_capab_dls_t *)&sp[1];
1237269Sericheng 			/*
1238269Sericheng 			 * Copy for alignment.
1239269Sericheng 			 */
12401184Skrgopi 			bcopy(pollp, &poll, sizeof (dl_capab_dls_t));
1241269Sericheng 
1242269Sericheng 			/*
1243269Sericheng 			 * We need to become writer before enabling and/or
1244269Sericheng 			 * disabling the polling interface.  If we couldn'
1245269Sericheng 			 * upgrade, check state again after re-acquiring the
1246269Sericheng 			 * lock to make sure we can proceed.
1247269Sericheng 			 */
1248269Sericheng 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
1249269Sericheng 				rw_exit(&dsp->ds_lock);
1250269Sericheng 				rw_enter(&dsp->ds_lock, RW_WRITER);
1251269Sericheng 
1252269Sericheng 				if (dsp->ds_dlstate == DL_UNATTACHED ||
1253269Sericheng 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
1254269Sericheng 					dl_err = DL_OUTSTATE;
1255269Sericheng 					goto failed;
1256269Sericheng 				}
1257269Sericheng 			}
1258269Sericheng 			upgraded = B_TRUE;
1259269Sericheng 
12601184Skrgopi 			switch (poll.dls_flags) {
1261269Sericheng 			default:
1262269Sericheng 				/*FALLTHRU*/
1263269Sericheng 			case POLL_DISABLE:
1264269Sericheng 				proto_poll_disable(dsp);
1265269Sericheng 				break;
1266269Sericheng 
1267269Sericheng 			case POLL_ENABLE:
1268269Sericheng 				ASSERT(!(dld_opt & DLD_OPT_NO_POLL));
1269269Sericheng 
1270269Sericheng 				/*
1271269Sericheng 				 * Make sure polling is disabled.
1272269Sericheng 				 */
1273269Sericheng 				proto_poll_disable(dsp);
1274269Sericheng 
1275269Sericheng 				/*
1276269Sericheng 				 * Now attempt enable it.
1277269Sericheng 				 */
12781184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
12791184Skrgopi 				    proto_poll_enable(dsp, &poll)) {
12801184Skrgopi 					bzero(&poll, sizeof (dl_capab_dls_t));
12811184Skrgopi 					poll.dls_flags = POLL_ENABLE;
12821184Skrgopi 				}
1283269Sericheng 				break;
1284269Sericheng 			}
1285269Sericheng 
12861184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
12871184Skrgopi 			bcopy(&poll, pollp, sizeof (dl_capab_dls_t));
12881184Skrgopi 			break;
12891184Skrgopi 		}
12901184Skrgopi 		case DL_CAPAB_SOFT_RING: {
12911184Skrgopi 			dl_capab_dls_t *soft_ringp;
12921184Skrgopi 			dl_capab_dls_t soft_ring;
12931184Skrgopi 
12941184Skrgopi 			soft_ringp = (dl_capab_dls_t *)&sp[1];
12951184Skrgopi 			/*
12961184Skrgopi 			 * Copy for alignment.
12971184Skrgopi 			 */
12981184Skrgopi 			bcopy(soft_ringp, &soft_ring,
12991184Skrgopi 			    sizeof (dl_capab_dls_t));
13001184Skrgopi 
13011184Skrgopi 			/*
13021184Skrgopi 			 * We need to become writer before enabling and/or
13031184Skrgopi 			 * disabling the soft_ring interface.  If we couldn'
13041184Skrgopi 			 * upgrade, check state again after re-acquiring the
13051184Skrgopi 			 * lock to make sure we can proceed.
13061184Skrgopi 			 */
13071184Skrgopi 			if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) {
13081184Skrgopi 				rw_exit(&dsp->ds_lock);
13091184Skrgopi 				rw_enter(&dsp->ds_lock, RW_WRITER);
13101184Skrgopi 
13111184Skrgopi 				if (dsp->ds_dlstate == DL_UNATTACHED ||
13121184Skrgopi 				    DL_ACK_PENDING(dsp->ds_dlstate)) {
13131184Skrgopi 					dl_err = DL_OUTSTATE;
13141184Skrgopi 					goto failed;
13151184Skrgopi 				}
13161184Skrgopi 			}
13171184Skrgopi 			upgraded = B_TRUE;
13181184Skrgopi 
13191184Skrgopi 			switch (soft_ring.dls_flags) {
13201184Skrgopi 			default:
13211184Skrgopi 				/*FALLTHRU*/
13221184Skrgopi 			case SOFT_RING_DISABLE:
13231184Skrgopi 				proto_soft_ring_disable(dsp);
13241184Skrgopi 				break;
13251184Skrgopi 
13261184Skrgopi 			case SOFT_RING_ENABLE:
13271184Skrgopi 				/*
13281184Skrgopi 				 * Make sure soft_ring is disabled.
13291184Skrgopi 				 */
13301184Skrgopi 				proto_soft_ring_disable(dsp);
13311184Skrgopi 
13321184Skrgopi 				/*
13331184Skrgopi 				 * Now attempt enable it.
13341184Skrgopi 				 */
13351184Skrgopi 				if (check_ip_above(dsp->ds_rq) &&
13361184Skrgopi 				    proto_soft_ring_enable(dsp, &soft_ring)) {
13371184Skrgopi 					bzero(&soft_ring,
13381184Skrgopi 					    sizeof (dl_capab_dls_t));
13391184Skrgopi 					soft_ring.dls_flags =
13401184Skrgopi 					    SOFT_RING_ENABLE;
13411184Skrgopi 				} else {
13421184Skrgopi 					bzero(&soft_ring,
13431184Skrgopi 					    sizeof (dl_capab_dls_t));
13441184Skrgopi 					soft_ring.dls_flags =
13451184Skrgopi 					    SOFT_RING_DISABLE;
13461184Skrgopi 				}
13471184Skrgopi 				break;
13481184Skrgopi 			}
13491184Skrgopi 
13501184Skrgopi 			dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
13511184Skrgopi 			bcopy(&soft_ring, soft_ringp,
13521184Skrgopi 			    sizeof (dl_capab_dls_t));
1353269Sericheng 			break;
1354269Sericheng 		}
1355269Sericheng 		default:
1356269Sericheng 			break;
1357269Sericheng 		}
1358269Sericheng 
1359269Sericheng 		off += size;
1360269Sericheng 	}
1361269Sericheng 	rw_exit(&dsp->ds_lock);
1362269Sericheng 	qreply(q, mp);
1363269Sericheng 	return (B_TRUE);
1364269Sericheng failed:
1365269Sericheng 	rw_exit(&dsp->ds_lock);
1366269Sericheng 	dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0);
1367269Sericheng 	return (B_FALSE);
13680Sstevel@tonic-gate }
13690Sstevel@tonic-gate 
13700Sstevel@tonic-gate /*
1371269Sericheng  * DL_NOTIFY_REQ
13720Sstevel@tonic-gate  */
1373269Sericheng static boolean_t
1374269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
13750Sstevel@tonic-gate {
1376269Sericheng 	dl_notify_req_t	*dlp = (dl_notify_req_t *)udlp;
1377269Sericheng 	t_uscalar_t	dl_err;
1378269Sericheng 	queue_t		*q = dsp->ds_wq;
1379269Sericheng 	uint_t		note =
1380269Sericheng 	    DL_NOTE_PROMISC_ON_PHYS |
1381269Sericheng 	    DL_NOTE_PROMISC_OFF_PHYS |
1382269Sericheng 	    DL_NOTE_PHYS_ADDR |
1383269Sericheng 	    DL_NOTE_LINK_UP |
1384269Sericheng 	    DL_NOTE_LINK_DOWN |
13852311Sseb 	    DL_NOTE_CAPAB_RENEG |
13862311Sseb 	    DL_NOTE_SPEED;
13870Sstevel@tonic-gate 
13881521Syz147064 	rw_enter(&dsp->ds_lock, RW_WRITER);
13891521Syz147064 
1390269Sericheng 	if (MBLKL(mp) < sizeof (dl_notify_req_t)) {
1391269Sericheng 		dl_err = DL_BADPRIM;
1392269Sericheng 		goto failed;
1393269Sericheng 	}
13940Sstevel@tonic-gate 
1395269Sericheng 	if (dsp->ds_dlstate == DL_UNATTACHED ||
1396269Sericheng 	    DL_ACK_PENDING(dsp->ds_dlstate)) {
1397269Sericheng 		dl_err = DL_OUTSTATE;
1398269Sericheng 		goto failed;
13990Sstevel@tonic-gate 	}
14000Sstevel@tonic-gate 
1401269Sericheng 	/*
1402269Sericheng 	 * Cache the notifications that are being enabled.
1403269Sericheng 	 */
1404269Sericheng 	dsp->ds_notifications = dlp->dl_notifications & note;
1405269Sericheng 	rw_exit(&dsp->ds_lock);
1406269Sericheng 	/*
1407269Sericheng 	 * The ACK carries all notifications regardless of which set is
1408269Sericheng 	 * being enabled.
1409269Sericheng 	 */
1410269Sericheng 	dlnotifyack(q, mp, note);
1411269Sericheng 
1412269Sericheng 	/*
1413269Sericheng 	 * Solicit DL_NOTIFY_IND messages for each enabled notification.
1414269Sericheng 	 */
1415269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1416269Sericheng 	if (dsp->ds_notifications != 0) {
1417269Sericheng 		rw_exit(&dsp->ds_lock);
1418269Sericheng 		dld_str_notify_ind(dsp);
1419269Sericheng 	} else {
1420269Sericheng 		rw_exit(&dsp->ds_lock);
1421269Sericheng 	}
1422269Sericheng 	return (B_TRUE);
1423269Sericheng failed:
1424269Sericheng 	rw_exit(&dsp->ds_lock);
1425269Sericheng 	dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0);
1426269Sericheng 	return (B_FALSE);
14270Sstevel@tonic-gate }
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate /*
1430269Sericheng  * DL_UINTDATA_REQ
14310Sstevel@tonic-gate  */
1432269Sericheng static boolean_t
1433269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
14340Sstevel@tonic-gate {
1435269Sericheng 	queue_t			*q = dsp->ds_wq;
1436269Sericheng 	dl_unitdata_req_t	*dlp = (dl_unitdata_req_t *)udlp;
1437269Sericheng 	off_t			off;
1438269Sericheng 	size_t			len, size;
1439269Sericheng 	const uint8_t		*addr;
1440269Sericheng 	uint16_t		sap;
1441269Sericheng 	uint_t			addr_length;
14422311Sseb 	mblk_t			*bp, *payload;
1443269Sericheng 	uint32_t		start, stuff, end, value, flags;
1444269Sericheng 	t_uscalar_t		dl_err;
1445269Sericheng 
1446269Sericheng 	rw_enter(&dsp->ds_lock, RW_READER);
1447269Sericheng 
1448269Sericheng 	if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) {
1449269Sericheng 		dl_err = DL_BADPRIM;
1450269Sericheng 		goto failed;
1451269Sericheng 	}
1452269Sericheng 
1453269Sericheng 	if (dsp->ds_dlstate != DL_IDLE) {
1454269Sericheng 		dl_err = DL_OUTSTATE;
1455269Sericheng 		goto failed;
1456269Sericheng 	}
1457269Sericheng 	addr_length = dsp->ds_mip->mi_addr_length;
1458269Sericheng 
1459269Sericheng 	off = dlp->dl_dest_addr_offset;
1460269Sericheng 	len = dlp->dl_dest_addr_length;
1461269Sericheng 
1462269Sericheng 	if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) {
1463269Sericheng 		dl_err = DL_BADPRIM;
1464269Sericheng 		goto failed;
1465269Sericheng 	}
1466269Sericheng 
1467269Sericheng 	if (len != addr_length + sizeof (uint16_t)) {
1468269Sericheng 		dl_err = DL_BADADDR;
1469269Sericheng 		goto failed;
1470269Sericheng 	}
1471269Sericheng 
1472269Sericheng 	addr = mp->b_rptr + off;
1473269Sericheng 	sap = *(uint16_t *)(mp->b_rptr + off + addr_length);
1474269Sericheng 
1475269Sericheng 	/*
1476269Sericheng 	 * Check the length of the packet and the block types.
1477269Sericheng 	 */
1478269Sericheng 	size = 0;
14792311Sseb 	payload = mp->b_cont;
14802311Sseb 	for (bp = payload; bp != NULL; bp = bp->b_cont) {
1481269Sericheng 		if (DB_TYPE(bp) != M_DATA)
1482269Sericheng 			goto baddata;
1483269Sericheng 
1484269Sericheng 		size += MBLKL(bp);
1485269Sericheng 	}
1486269Sericheng 
1487269Sericheng 	if (size > dsp->ds_mip->mi_sdu_max)
1488269Sericheng 		goto baddata;
1489269Sericheng 
1490269Sericheng 	/*
1491269Sericheng 	 * Build a packet header.
1492269Sericheng 	 */
14932760Sdg199075 	if ((bp = dls_header(dsp->ds_dc, addr, sap, dlp->dl_priority.dl_max,
14942760Sdg199075 	    &payload)) == NULL) {
1495269Sericheng 		dl_err = DL_BADADDR;
1496269Sericheng 		goto failed;
1497269Sericheng 	}
1498269Sericheng 
1499269Sericheng 	/*
1500269Sericheng 	 * We no longer need the M_PROTO header, so free it.
1501269Sericheng 	 */
1502269Sericheng 	freeb(mp);
1503269Sericheng 
1504269Sericheng 	/*
1505269Sericheng 	 * Transfer the checksum offload information if it is present.
1506269Sericheng 	 */
15072311Sseb 	hcksum_retrieve(payload, NULL, NULL, &start, &stuff, &end, &value,
1508269Sericheng 	    &flags);
15092311Sseb 	(void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 0);
1510269Sericheng 
1511269Sericheng 	/*
1512269Sericheng 	 * Link the payload onto the new header.
1513269Sericheng 	 */
1514269Sericheng 	ASSERT(bp->b_cont == NULL);
15152311Sseb 	bp->b_cont = payload;
1516269Sericheng 
15172760Sdg199075 	dld_tx_single(dsp, bp);
1518269Sericheng 	rw_exit(&dsp->ds_lock);
1519269Sericheng 	return (B_TRUE);
1520269Sericheng failed:
1521269Sericheng 	rw_exit(&dsp->ds_lock);
1522269Sericheng 	dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0);
1523269Sericheng 	return (B_FALSE);
1524269Sericheng 
1525269Sericheng baddata:
1526269Sericheng 	rw_exit(&dsp->ds_lock);
1527269Sericheng 	dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0);
1528269Sericheng 	return (B_FALSE);
1529269Sericheng }
1530269Sericheng 
1531269Sericheng /*
1532269Sericheng  * DL_PASSIVE_REQ
1533269Sericheng  */
1534269Sericheng /* ARGSUSED */
1535269Sericheng static boolean_t
1536269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp)
1537269Sericheng {
1538269Sericheng 	t_uscalar_t dl_err;
1539269Sericheng 
1540269Sericheng 	rw_enter(&dsp->ds_lock, RW_WRITER);
1541269Sericheng 	/*
1542269Sericheng 	 * If we've already become active by issuing an active primitive,
1543269Sericheng 	 * then it's too late to try to become passive.
1544269Sericheng 	 */
1545269Sericheng 	if (dsp->ds_passivestate == DLD_ACTIVE) {
1546269Sericheng 		dl_err = DL_OUTSTATE;
1547269Sericheng 		goto failed;
1548269Sericheng 	}
1549269Sericheng 
1550269Sericheng 	if (MBLKL(mp) < sizeof (dl_passive_req_t)) {
1551269Sericheng 		dl_err = DL_BADPRIM;
1552269Sericheng 		goto failed;
1553269Sericheng 	}
1554269Sericheng 
1555269Sericheng 	dsp->ds_passivestate = DLD_PASSIVE;
1556269Sericheng 	rw_exit(&dsp->ds_lock);
1557269Sericheng 	dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ);
1558269Sericheng 	return (B_TRUE);
1559269Sericheng failed:
1560269Sericheng 	rw_exit(&dsp->ds_lock);
1561269Sericheng 	dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0);
1562269Sericheng 	return (B_FALSE);
1563269Sericheng }
1564269Sericheng 
1565269Sericheng 
1566269Sericheng /*
1567269Sericheng  * Catch-all handler.
1568269Sericheng  */
1569269Sericheng static boolean_t
1570269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp)
1571269Sericheng {
1572269Sericheng 	dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0);
1573269Sericheng 	return (B_FALSE);
15740Sstevel@tonic-gate }
15750Sstevel@tonic-gate 
15760Sstevel@tonic-gate static void
15770Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp)
15780Sstevel@tonic-gate {
15790Sstevel@tonic-gate 	mac_handle_t	mh;
15800Sstevel@tonic-gate 
15811353Sericheng 	ASSERT(dsp->ds_pending_req != NULL || RW_WRITE_HELD(&dsp->ds_lock));
1582269Sericheng 
15830Sstevel@tonic-gate 	if (!dsp->ds_polling)
15840Sstevel@tonic-gate 		return;
15850Sstevel@tonic-gate 
15860Sstevel@tonic-gate 	/*
15870Sstevel@tonic-gate 	 * It should be impossible to enable raw mode if polling is turned on.
15880Sstevel@tonic-gate 	 */
15890Sstevel@tonic-gate 	ASSERT(dsp->ds_mode != DLD_RAW);
15900Sstevel@tonic-gate 
15910Sstevel@tonic-gate 	/*
15920Sstevel@tonic-gate 	 * Reset the resource_add callback.
15930Sstevel@tonic-gate 	 */
15940Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
15950Sstevel@tonic-gate 	mac_resource_set(mh, NULL, NULL);
15961184Skrgopi 	mac_resources(mh);
15970Sstevel@tonic-gate 
15980Sstevel@tonic-gate 	/*
15990Sstevel@tonic-gate 	 * Set receive function back to default.
16000Sstevel@tonic-gate 	 */
16010Sstevel@tonic-gate 	dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ?
16020Sstevel@tonic-gate 	    dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp);
16030Sstevel@tonic-gate 
16040Sstevel@tonic-gate 	/*
16050Sstevel@tonic-gate 	 * Note that polling is disabled.
16060Sstevel@tonic-gate 	 */
16070Sstevel@tonic-gate 	dsp->ds_polling = B_FALSE;
16080Sstevel@tonic-gate }
16090Sstevel@tonic-gate 
16100Sstevel@tonic-gate static boolean_t
16111184Skrgopi proto_poll_enable(dld_str_t *dsp, dl_capab_dls_t *pollp)
16120Sstevel@tonic-gate {
16130Sstevel@tonic-gate 	mac_handle_t	mh;
16140Sstevel@tonic-gate 
1615269Sericheng 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16160Sstevel@tonic-gate 	ASSERT(!dsp->ds_polling);
16170Sstevel@tonic-gate 
16180Sstevel@tonic-gate 	/*
16190Sstevel@tonic-gate 	 * We cannot enable polling if raw mode
16200Sstevel@tonic-gate 	 * has been enabled.
16210Sstevel@tonic-gate 	 */
16220Sstevel@tonic-gate 	if (dsp->ds_mode == DLD_RAW)
16230Sstevel@tonic-gate 		return (B_FALSE);
16240Sstevel@tonic-gate 
16250Sstevel@tonic-gate 	mh = dls_mac(dsp->ds_dc);
16260Sstevel@tonic-gate 
16270Sstevel@tonic-gate 	/*
16280Sstevel@tonic-gate 	 * Register resources.
16290Sstevel@tonic-gate 	 */
16301184Skrgopi 	mac_resource_set(mh, (mac_resource_add_t)pollp->dls_ring_add,
16311184Skrgopi 	    (void *)pollp->dls_rx_handle);
16320Sstevel@tonic-gate 	mac_resources(mh);
16330Sstevel@tonic-gate 
16340Sstevel@tonic-gate 	/*
16350Sstevel@tonic-gate 	 * Set the receive function.
16360Sstevel@tonic-gate 	 */
16371184Skrgopi 	dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->dls_rx,
16381184Skrgopi 	    (void *)pollp->dls_rx_handle);
16390Sstevel@tonic-gate 
16400Sstevel@tonic-gate 	/*
16410Sstevel@tonic-gate 	 * Note that polling is enabled. This prevents further DLIOCHDRINFO
16420Sstevel@tonic-gate 	 * ioctls from overwriting the receive function pointer.
16430Sstevel@tonic-gate 	 */
16440Sstevel@tonic-gate 	dsp->ds_polling = B_TRUE;
16450Sstevel@tonic-gate 	return (B_TRUE);
16460Sstevel@tonic-gate }
16470Sstevel@tonic-gate 
16481184Skrgopi static void
16491184Skrgopi proto_soft_ring_disable(dld_str_t *dsp)
16501184Skrgopi {
16511184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16521184Skrgopi 
16531184Skrgopi 	if (!dsp->ds_soft_ring)
16541184Skrgopi 		return;
16551184Skrgopi 
16561184Skrgopi 	/*
16571184Skrgopi 	 * It should be impossible to enable raw mode if soft_ring is turned on.
16581184Skrgopi 	 */
16591184Skrgopi 	ASSERT(dsp->ds_mode != DLD_RAW);
16601184Skrgopi 	proto_change_soft_ring_fanout(dsp, SOFT_RING_NONE);
16611184Skrgopi 	/*
16621184Skrgopi 	 * Note that fanout is disabled.
16631184Skrgopi 	 */
16641184Skrgopi 	dsp->ds_soft_ring = B_FALSE;
16651184Skrgopi }
16661184Skrgopi 
16671184Skrgopi static boolean_t
16681184Skrgopi proto_soft_ring_enable(dld_str_t *dsp, dl_capab_dls_t *soft_ringp)
16691184Skrgopi {
16701184Skrgopi 	ASSERT(RW_WRITE_HELD(&dsp->ds_lock));
16711184Skrgopi 	ASSERT(!dsp->ds_soft_ring);
16721184Skrgopi 
16731184Skrgopi 	/*
16741184Skrgopi 	 * We cannot enable soft_ring if raw mode
16751184Skrgopi 	 * has been enabled.
16761184Skrgopi 	 */
16771184Skrgopi 	if (dsp->ds_mode == DLD_RAW)
16781184Skrgopi 		return (B_FALSE);
16791184Skrgopi 
16801184Skrgopi 	if (dls_soft_ring_enable(dsp->ds_dc, soft_ringp) == B_FALSE)
16811184Skrgopi 		return (B_FALSE);
16821184Skrgopi 
16831184Skrgopi 	dsp->ds_soft_ring = B_TRUE;
16841184Skrgopi 	return (B_TRUE);
16851184Skrgopi }
16861184Skrgopi 
16871184Skrgopi static void
16881184Skrgopi proto_change_soft_ring_fanout(dld_str_t *dsp, int type)
16891184Skrgopi {
16901184Skrgopi 	dls_rx_t	rx;
16911184Skrgopi 
16921184Skrgopi 	if (type == SOFT_RING_NONE) {
16931184Skrgopi 		rx = (dsp->ds_mode == DLD_FASTPATH) ?
16941184Skrgopi 			    dld_str_rx_fastpath : dld_str_rx_unitdata;
16951184Skrgopi 	} else {
16961184Skrgopi 		rx = (dls_rx_t)dls_ether_soft_ring_fanout;
16971184Skrgopi 	}
16981184Skrgopi 	dls_soft_ring_rx_set(dsp->ds_dc, rx, dsp, type);
16991184Skrgopi }
17001184Skrgopi 
17010Sstevel@tonic-gate /*
17020Sstevel@tonic-gate  * DL_CAPABILITY_ACK/DL_ERROR_ACK
17030Sstevel@tonic-gate  */
1704269Sericheng static boolean_t
1705269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp)
17060Sstevel@tonic-gate {
17070Sstevel@tonic-gate 	dl_capability_ack_t	*dlap;
17080Sstevel@tonic-gate 	dl_capability_sub_t	*dlsp;
17090Sstevel@tonic-gate 	size_t			subsize;
17101184Skrgopi 	dl_capab_dls_t		poll;
17113115Syl150051 	dl_capab_dls_t		soft_ring;
17120Sstevel@tonic-gate 	dl_capab_hcksum_t	hcksum;
17133115Syl150051 	dl_capab_lso_t		lso;
17140Sstevel@tonic-gate 	dl_capab_zerocopy_t	zcopy;
17150Sstevel@tonic-gate 	uint8_t			*ptr;
17162311Sseb 	boolean_t		cksum_cap;
17170Sstevel@tonic-gate 	boolean_t		poll_cap;
17183115Syl150051 	boolean_t		lso_cap;
17193115Syl150051 	mac_capab_lso_t		mac_lso;
1720269Sericheng 	queue_t			*q = dsp->ds_wq;
1721269Sericheng 	mblk_t			*mp1;
1722269Sericheng 
1723269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
17240Sstevel@tonic-gate 
17250Sstevel@tonic-gate 	/*
17260Sstevel@tonic-gate 	 * Initially assume no capabilities.
17270Sstevel@tonic-gate 	 */
17280Sstevel@tonic-gate 	subsize = 0;
17290Sstevel@tonic-gate 
17301555Skrgopi 	/*
17311555Skrgopi 	 * Advertize soft ring capability if
17321555Skrgopi 	 * VLAN_ID_NONE for GLDv3 drivers
17331555Skrgopi 	 */
17341555Skrgopi 	if (dsp->ds_vid == VLAN_ID_NONE)
17351555Skrgopi 		subsize += sizeof (dl_capability_sub_t) +
17361555Skrgopi 				    sizeof (dl_capab_dls_t);
17371184Skrgopi 
17380Sstevel@tonic-gate 	/*
17390Sstevel@tonic-gate 	 * Check if polling can be enabled on this interface.
17400Sstevel@tonic-gate 	 * If advertising DL_CAPAB_POLL has not been explicitly disabled
17410Sstevel@tonic-gate 	 * then reserve space for that capability.
17420Sstevel@tonic-gate 	 */
17432311Sseb 	poll_cap = (mac_capab_get(dsp->ds_mh, MAC_CAPAB_POLL, NULL) &&
17440Sstevel@tonic-gate 	    !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE));
17450Sstevel@tonic-gate 	if (poll_cap) {
17460Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17471184Skrgopi 		    sizeof (dl_capab_dls_t);
17480Sstevel@tonic-gate 	}
17490Sstevel@tonic-gate 
17500Sstevel@tonic-gate 	/*
17510Sstevel@tonic-gate 	 * If the MAC interface supports checksum offload then reserve
17520Sstevel@tonic-gate 	 * space for the DL_CAPAB_HCKSUM capability.
17530Sstevel@tonic-gate 	 */
17542311Sseb 	if (cksum_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_HCKSUM,
17552311Sseb 	    &hcksum.hcksum_txflags)) {
17560Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17570Sstevel@tonic-gate 		    sizeof (dl_capab_hcksum_t);
17580Sstevel@tonic-gate 	}
17590Sstevel@tonic-gate 
17600Sstevel@tonic-gate 	/*
17613115Syl150051 	 * If LSO is usable for MAC, reserve space for the DL_CAPAB_LSO
17623115Syl150051 	 * capability.
17633115Syl150051 	 */
17643115Syl150051 	if (lso_cap = mac_capab_get(dsp->ds_mh, MAC_CAPAB_LSO, &mac_lso)) {
17653115Syl150051 		subsize += sizeof (dl_capability_sub_t) +
17663115Syl150051 		    sizeof (dl_capab_lso_t);
17673115Syl150051 	}
17683115Syl150051 
17693115Syl150051 	/*
17700Sstevel@tonic-gate 	 * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then
17710Sstevel@tonic-gate 	 * reserve space for it.
17720Sstevel@tonic-gate 	 */
17730Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
17740Sstevel@tonic-gate 		subsize += sizeof (dl_capability_sub_t) +
17750Sstevel@tonic-gate 		    sizeof (dl_capab_zerocopy_t);
17760Sstevel@tonic-gate 	}
17770Sstevel@tonic-gate 
17780Sstevel@tonic-gate 	/*
1779269Sericheng 	 * If there are no capabilities to advertise or if we
1780269Sericheng 	 * can't allocate a response, send a DL_ERROR_ACK.
17810Sstevel@tonic-gate 	 */
17821184Skrgopi 	if ((mp1 = reallocb(mp,
1783269Sericheng 	    sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) {
1784269Sericheng 		rw_exit(&dsp->ds_lock);
1785269Sericheng 		dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0);
1786269Sericheng 		return (B_FALSE);
17870Sstevel@tonic-gate 	}
17880Sstevel@tonic-gate 
1789269Sericheng 	mp = mp1;
1790269Sericheng 	DB_TYPE(mp) = M_PROTO;
1791269Sericheng 	mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize;
1792269Sericheng 	bzero(mp->b_rptr, MBLKL(mp));
17930Sstevel@tonic-gate 	dlap = (dl_capability_ack_t *)mp->b_rptr;
17940Sstevel@tonic-gate 	dlap->dl_primitive = DL_CAPABILITY_ACK;
17950Sstevel@tonic-gate 	dlap->dl_sub_offset = sizeof (dl_capability_ack_t);
17960Sstevel@tonic-gate 	dlap->dl_sub_length = subsize;
17970Sstevel@tonic-gate 	ptr = (uint8_t *)&dlap[1];
17980Sstevel@tonic-gate 
17990Sstevel@tonic-gate 	/*
18000Sstevel@tonic-gate 	 * IP polling interface.
18010Sstevel@tonic-gate 	 */
18020Sstevel@tonic-gate 	if (poll_cap) {
18030Sstevel@tonic-gate 		/*
1804269Sericheng 		 * Attempt to disable just in case this is a re-negotiation;
1805269Sericheng 		 * we need to become writer before doing so.
18060Sstevel@tonic-gate 		 */
1807269Sericheng 		if (!rw_tryupgrade(&dsp->ds_lock)) {
1808269Sericheng 			rw_exit(&dsp->ds_lock);
1809269Sericheng 			rw_enter(&dsp->ds_lock, RW_WRITER);
1810269Sericheng 		}
18110Sstevel@tonic-gate 
1812269Sericheng 		/*
1813269Sericheng 		 * Check if polling state has changed after we re-acquired
1814269Sericheng 		 * the lock above, so that we don't mis-advertise it.
1815269Sericheng 		 */
18162311Sseb 		poll_cap = !(dld_opt & DLD_OPT_NO_POLL) &&
18172311Sseb 		    (dsp->ds_vid == VLAN_ID_NONE);
18180Sstevel@tonic-gate 
1819269Sericheng 		if (!poll_cap) {
1820269Sericheng 			int poll_capab_size;
1821269Sericheng 
1822269Sericheng 			rw_downgrade(&dsp->ds_lock);
1823269Sericheng 
1824269Sericheng 			poll_capab_size = sizeof (dl_capability_sub_t) +
18251184Skrgopi 			    sizeof (dl_capab_dls_t);
18260Sstevel@tonic-gate 
1827269Sericheng 			mp->b_wptr -= poll_capab_size;
1828269Sericheng 			subsize -= poll_capab_size;
1829269Sericheng 			dlap->dl_sub_length = subsize;
1830269Sericheng 		} else {
1831269Sericheng 			proto_poll_disable(dsp);
1832269Sericheng 
1833269Sericheng 			rw_downgrade(&dsp->ds_lock);
1834269Sericheng 
1835269Sericheng 			dlsp = (dl_capability_sub_t *)ptr;
1836269Sericheng 
1837269Sericheng 			dlsp->dl_cap = DL_CAPAB_POLL;
18381184Skrgopi 			dlsp->dl_length = sizeof (dl_capab_dls_t);
1839269Sericheng 			ptr += sizeof (dl_capability_sub_t);
18400Sstevel@tonic-gate 
18411184Skrgopi 			bzero(&poll, sizeof (dl_capab_dls_t));
18421184Skrgopi 			poll.dls_version = POLL_VERSION_1;
18431184Skrgopi 			poll.dls_flags = POLL_CAPABLE;
18441184Skrgopi 			poll.dls_tx_handle = (uintptr_t)dsp;
18451184Skrgopi 			poll.dls_tx = (uintptr_t)str_mdata_fastpath_put;
1846269Sericheng 
18471184Skrgopi 			dlcapabsetqid(&(poll.dls_mid), dsp->ds_rq);
18481184Skrgopi 			bcopy(&poll, ptr, sizeof (dl_capab_dls_t));
18491184Skrgopi 			ptr += sizeof (dl_capab_dls_t);
1850269Sericheng 		}
18510Sstevel@tonic-gate 	}
18520Sstevel@tonic-gate 
1853269Sericheng 	ASSERT(RW_READ_HELD(&dsp->ds_lock));
1854269Sericheng 
18551555Skrgopi 	if (dsp->ds_vid == VLAN_ID_NONE) {
18561555Skrgopi 		dlsp = (dl_capability_sub_t *)ptr;
18571184Skrgopi 
18581555Skrgopi 		dlsp->dl_cap = DL_CAPAB_SOFT_RING;
18591555Skrgopi 		dlsp->dl_length = sizeof (dl_capab_dls_t);
18601555Skrgopi 		ptr += sizeof (dl_capability_sub_t);
18611184Skrgopi 
18621555Skrgopi 		bzero(&soft_ring, sizeof (dl_capab_dls_t));
18631555Skrgopi 		soft_ring.dls_version = SOFT_RING_VERSION_1;
18641555Skrgopi 		soft_ring.dls_flags = SOFT_RING_CAPABLE;
18651555Skrgopi 		soft_ring.dls_tx_handle = (uintptr_t)dsp;
18661555Skrgopi 		soft_ring.dls_tx = (uintptr_t)str_mdata_fastpath_put;
18671555Skrgopi 		soft_ring.dls_ring_change_status =
18681555Skrgopi 		    (uintptr_t)proto_change_soft_ring_fanout;
18691555Skrgopi 		soft_ring.dls_ring_bind = (uintptr_t)soft_ring_bind;
18701555Skrgopi 		soft_ring.dls_ring_unbind = (uintptr_t)soft_ring_unbind;
18711184Skrgopi 
18721555Skrgopi 		dlcapabsetqid(&(soft_ring.dls_mid), dsp->ds_rq);
18731555Skrgopi 		bcopy(&soft_ring, ptr, sizeof (dl_capab_dls_t));
18741555Skrgopi 		ptr += sizeof (dl_capab_dls_t);
18751555Skrgopi 	}
18761184Skrgopi 
18770Sstevel@tonic-gate 	/*
18780Sstevel@tonic-gate 	 * TCP/IP checksum offload.
18790Sstevel@tonic-gate 	 */
18802311Sseb 	if (cksum_cap) {
18810Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
18820Sstevel@tonic-gate 
18830Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_HCKSUM;
18840Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_hcksum_t);
18850Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
18860Sstevel@tonic-gate 
18870Sstevel@tonic-gate 		hcksum.hcksum_version = HCKSUM_VERSION_1;
18880Sstevel@tonic-gate 		dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq);
18890Sstevel@tonic-gate 		bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t));
18900Sstevel@tonic-gate 		ptr += sizeof (dl_capab_hcksum_t);
18910Sstevel@tonic-gate 	}
18920Sstevel@tonic-gate 
18930Sstevel@tonic-gate 	/*
18943115Syl150051 	 * Large segment offload. (LSO)
18953115Syl150051 	 */
18963115Syl150051 	if (lso_cap) {
18973115Syl150051 		dlsp = (dl_capability_sub_t *)ptr;
18983115Syl150051 
18993115Syl150051 		dlsp->dl_cap = DL_CAPAB_LSO;
19003115Syl150051 		dlsp->dl_length = sizeof (dl_capab_lso_t);
19013115Syl150051 		ptr += sizeof (dl_capability_sub_t);
19023115Syl150051 
19033115Syl150051 		lso.lso_version = LSO_VERSION_1;
19043115Syl150051 		lso.lso_flags = mac_lso.lso_flags;
19053115Syl150051 		lso.lso_max = mac_lso.lso_basic_tcp_ipv4.lso_max;
19063115Syl150051 
19073115Syl150051 		/* Simply enable LSO with DLD */
19083115Syl150051 		dsp->ds_lso = B_TRUE;
19093115Syl150051 		dsp->ds_lso_max = lso.lso_max;
19103115Syl150051 
19113115Syl150051 		dlcapabsetqid(&(lso.lso_mid), dsp->ds_rq);
19123115Syl150051 		bcopy(&lso, ptr, sizeof (dl_capab_lso_t));
19133115Syl150051 		ptr += sizeof (dl_capab_lso_t);
19143115Syl150051 	} else {
19153115Syl150051 		dsp->ds_lso = B_FALSE;
19163115Syl150051 		dsp->ds_lso_max = 0;
19173115Syl150051 	}
19183115Syl150051 
19193115Syl150051 	/*
19200Sstevel@tonic-gate 	 * Zero copy
19210Sstevel@tonic-gate 	 */
19220Sstevel@tonic-gate 	if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) {
19230Sstevel@tonic-gate 		dlsp = (dl_capability_sub_t *)ptr;
19240Sstevel@tonic-gate 
19250Sstevel@tonic-gate 		dlsp->dl_cap = DL_CAPAB_ZEROCOPY;
19260Sstevel@tonic-gate 		dlsp->dl_length = sizeof (dl_capab_zerocopy_t);
19270Sstevel@tonic-gate 		ptr += sizeof (dl_capability_sub_t);
19280Sstevel@tonic-gate 
19290Sstevel@tonic-gate 		bzero(&zcopy, sizeof (dl_capab_zerocopy_t));
19300Sstevel@tonic-gate 		zcopy.zerocopy_version = ZEROCOPY_VERSION_1;
19310Sstevel@tonic-gate 		zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM;
19320Sstevel@tonic-gate 
19330Sstevel@tonic-gate 		dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq);
19340Sstevel@tonic-gate 		bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t));
19350Sstevel@tonic-gate 		ptr += sizeof (dl_capab_zerocopy_t);
19360Sstevel@tonic-gate 	}
19370Sstevel@tonic-gate 
19380Sstevel@tonic-gate 	ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize);
1939269Sericheng 
1940269Sericheng 	rw_exit(&dsp->ds_lock);
1941269Sericheng 	qreply(q, mp);
1942269Sericheng 	return (B_TRUE);
19430Sstevel@tonic-gate }
1944