xref: /onnv-gate/usr/src/uts/common/xen/io/xnbu.c (revision 11878:ac93462db6d7)
15084Sjohnlev /*
25084Sjohnlev  * CDDL HEADER START
35084Sjohnlev  *
45084Sjohnlev  * The contents of this file are subject to the terms of the
55084Sjohnlev  * Common Development and Distribution License (the "License").
65084Sjohnlev  * You may not use this file except in compliance with the License.
75084Sjohnlev  *
85084Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95084Sjohnlev  * or http://www.opensolaris.org/os/licensing.
105084Sjohnlev  * See the License for the specific language governing permissions
115084Sjohnlev  * and limitations under the License.
125084Sjohnlev  *
135084Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
145084Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155084Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
165084Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
175084Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
185084Sjohnlev  *
195084Sjohnlev  * CDDL HEADER END
205084Sjohnlev  */
215084Sjohnlev 
225084Sjohnlev /*
23*11878SVenu.Iyer@Sun.COM  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
245084Sjohnlev  * Use is subject to license terms.
255084Sjohnlev  */
265084Sjohnlev 
275084Sjohnlev /*
285084Sjohnlev  * Xen inter-domain backend - GLDv3 driver edition.
295084Sjohnlev  *
305084Sjohnlev  * A traditional GLDv3 driver used to communicate with a guest
315084Sjohnlev  * domain.  This driver is typically plumbed underneath the IP stack
325084Sjohnlev  * or a software ethernet bridge.
335084Sjohnlev  */
345084Sjohnlev 
355084Sjohnlev #include "xnb.h"
365084Sjohnlev 
375084Sjohnlev #include <sys/sunddi.h>
385084Sjohnlev #include <sys/conf.h>
395084Sjohnlev #include <sys/modctl.h>
405084Sjohnlev #include <sys/strsubr.h>
415084Sjohnlev #include <sys/dlpi.h>
425084Sjohnlev #include <sys/pattr.h>
438275SEric Cheng #include <sys/mac_provider.h>
445084Sjohnlev #include <sys/mac_ether.h>
455084Sjohnlev #include <xen/sys/xendev.h>
4610958Sdme@sun.com #include <sys/note.h>
475084Sjohnlev 
485084Sjohnlev /* Required driver entry points for GLDv3 */
495084Sjohnlev static int	xnbu_m_start(void *);
505084Sjohnlev static void	xnbu_m_stop(void *);
515084Sjohnlev static int	xnbu_m_set_mac_addr(void *, const uint8_t *);
525084Sjohnlev static int	xnbu_m_set_multicast(void *, boolean_t, const uint8_t *);
535084Sjohnlev static int	xnbu_m_set_promiscuous(void *, boolean_t);
545084Sjohnlev static int	xnbu_m_stat(void *, uint_t, uint64_t *);
555084Sjohnlev static boolean_t xnbu_m_getcapab(void *, mac_capab_t, void *);
565084Sjohnlev static mblk_t	*xnbu_m_send(void *, mblk_t *);
575084Sjohnlev 
585084Sjohnlev typedef struct xnbu {
595084Sjohnlev 	mac_handle_t		u_mh;
605084Sjohnlev 	boolean_t		u_need_sched;
615084Sjohnlev } xnbu_t;
625084Sjohnlev 
6310958Sdme@sun.com static mac_callbacks_t xnbu_callbacks = {
648275SEric Cheng 	MC_GETCAPAB,
655084Sjohnlev 	xnbu_m_stat,
665084Sjohnlev 	xnbu_m_start,
675084Sjohnlev 	xnbu_m_stop,
685084Sjohnlev 	xnbu_m_set_promiscuous,
695084Sjohnlev 	xnbu_m_set_multicast,
705084Sjohnlev 	xnbu_m_set_mac_addr,
715084Sjohnlev 	xnbu_m_send,
725084Sjohnlev 	NULL,
73*11878SVenu.Iyer@Sun.COM 	NULL,
745084Sjohnlev 	xnbu_m_getcapab
755084Sjohnlev };
765084Sjohnlev 
775084Sjohnlev static void
xnbu_to_host(xnb_t * xnbp,mblk_t * mp)785084Sjohnlev xnbu_to_host(xnb_t *xnbp, mblk_t *mp)
795084Sjohnlev {
805741Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
815084Sjohnlev 	boolean_t sched = B_FALSE;
825084Sjohnlev 
835084Sjohnlev 	ASSERT(mp != NULL);
845084Sjohnlev 
858275SEric Cheng 	mac_rx(xnbup->u_mh, NULL, mp);
865084Sjohnlev 
877615SMax.Zhen@Sun.COM 	mutex_enter(&xnbp->xnb_rx_lock);
885084Sjohnlev 
895084Sjohnlev 	/*
905084Sjohnlev 	 * If a transmit attempt failed because we ran out of ring
915084Sjohnlev 	 * space and there is now some space, re-enable the transmit
925084Sjohnlev 	 * path.
935084Sjohnlev 	 */
945084Sjohnlev 	if (xnbup->u_need_sched &&
955741Smrj 	    RING_HAS_UNCONSUMED_REQUESTS(&xnbp->xnb_rx_ring)) {
965084Sjohnlev 		sched = B_TRUE;
975084Sjohnlev 		xnbup->u_need_sched = B_FALSE;
985084Sjohnlev 	}
995084Sjohnlev 
1007615SMax.Zhen@Sun.COM 	mutex_exit(&xnbp->xnb_rx_lock);
1015084Sjohnlev 
1025084Sjohnlev 	if (sched)
1035084Sjohnlev 		mac_tx_update(xnbup->u_mh);
1045084Sjohnlev }
1055084Sjohnlev 
1065084Sjohnlev static mblk_t *
xnbu_cksum_from_peer(xnb_t * xnbp,mblk_t * mp,uint16_t flags)1075084Sjohnlev xnbu_cksum_from_peer(xnb_t *xnbp, mblk_t *mp, uint16_t flags)
1085084Sjohnlev {
1095084Sjohnlev 	/*
1105084Sjohnlev 	 * Take a conservative approach - if the checksum is blank
1115084Sjohnlev 	 * then we fill it in.
1125084Sjohnlev 	 *
1135084Sjohnlev 	 * If the consumer of the packet is IP then we might actually
1145084Sjohnlev 	 * only need fill it in if the data is not validated, but how
1155084Sjohnlev 	 * do we know who might end up with the packet?
1165084Sjohnlev 	 */
1175084Sjohnlev 
1185084Sjohnlev 	if ((flags & NETTXF_csum_blank) != 0) {
1195084Sjohnlev 		/*
1205084Sjohnlev 		 * The checksum is blank.  We must fill it in here.
1215084Sjohnlev 		 */
1225084Sjohnlev 		mp = xnb_process_cksum_flags(xnbp, mp, 0);
1235084Sjohnlev 
1245084Sjohnlev 		/*
1255084Sjohnlev 		 * Because we calculated the checksum ourselves we
1265084Sjohnlev 		 * know that it must be good, so we assert this.
1275084Sjohnlev 		 */
1285638Sdme 		flags |= NETTXF_data_validated;
1295084Sjohnlev 	}
1305084Sjohnlev 
1315084Sjohnlev 	if ((flags & NETTXF_data_validated) != 0) {
1325084Sjohnlev 		/*
1335084Sjohnlev 		 * The checksum is asserted valid.
1345084Sjohnlev 		 */
135*11878SVenu.Iyer@Sun.COM 		mac_hcksum_set(mp, 0, 0, 0, 0, HCK_FULLCKSUM_OK);
1365084Sjohnlev 	}
1375084Sjohnlev 
1385084Sjohnlev 	return (mp);
1395084Sjohnlev }
1405084Sjohnlev 
1415084Sjohnlev static uint16_t
xnbu_cksum_to_peer(xnb_t * xnbp,mblk_t * mp)1425084Sjohnlev xnbu_cksum_to_peer(xnb_t *xnbp, mblk_t *mp)
1435084Sjohnlev {
14410958Sdme@sun.com 	_NOTE(ARGUNUSED(xnbp));
1455084Sjohnlev 	uint16_t r = 0;
14610958Sdme@sun.com 	uint32_t pflags;
1475084Sjohnlev 
148*11878SVenu.Iyer@Sun.COM 	mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &pflags);
1495084Sjohnlev 
15010958Sdme@sun.com 	/*
15110958Sdme@sun.com 	 * If the protocol stack has requested checksum
15210958Sdme@sun.com 	 * offload, inform the peer that we have not
15310958Sdme@sun.com 	 * calculated the checksum.
15410958Sdme@sun.com 	 */
15510958Sdme@sun.com 	if ((pflags & HCK_FULLCKSUM) != 0)
15610958Sdme@sun.com 		r |= NETRXF_csum_blank;
1575084Sjohnlev 
1585084Sjohnlev 	return (r);
1595084Sjohnlev }
1605084Sjohnlev 
16110958Sdme@sun.com static boolean_t
xnbu_start_connect(xnb_t * xnbp)16210958Sdme@sun.com xnbu_start_connect(xnb_t *xnbp)
1635084Sjohnlev {
1645741Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
1655084Sjohnlev 
1665084Sjohnlev 	mac_link_update(xnbup->u_mh, LINK_STATE_UP);
1675084Sjohnlev 	/*
1685084Sjohnlev 	 * We are able to send packets now - bring them on.
1695084Sjohnlev 	 */
1705084Sjohnlev 	mac_tx_update(xnbup->u_mh);
17110958Sdme@sun.com 
17210958Sdme@sun.com 	return (B_TRUE);
17310958Sdme@sun.com }
17410958Sdme@sun.com 
17510958Sdme@sun.com static boolean_t
xnbu_peer_connected(xnb_t * xnbp)17610958Sdme@sun.com xnbu_peer_connected(xnb_t *xnbp)
17710958Sdme@sun.com {
17810958Sdme@sun.com 	_NOTE(ARGUNUSED(xnbp));
17910958Sdme@sun.com 
18010958Sdme@sun.com 	return (B_TRUE);
1815084Sjohnlev }
1825084Sjohnlev 
1835084Sjohnlev static void
xnbu_peer_disconnected(xnb_t * xnbp)18410958Sdme@sun.com xnbu_peer_disconnected(xnb_t *xnbp)
1855084Sjohnlev {
1865741Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
1875084Sjohnlev 
1885084Sjohnlev 	mac_link_update(xnbup->u_mh, LINK_STATE_DOWN);
1895084Sjohnlev }
1905084Sjohnlev 
1915084Sjohnlev /*ARGSUSED*/
1925084Sjohnlev static boolean_t
xnbu_hotplug_connected(xnb_t * xnbp)19310958Sdme@sun.com xnbu_hotplug_connected(xnb_t *xnbp)
1945084Sjohnlev {
1955084Sjohnlev 	return (B_TRUE);
1965084Sjohnlev }
1975084Sjohnlev 
1985084Sjohnlev static mblk_t *
xnbu_m_send(void * arg,mblk_t * mp)1995084Sjohnlev xnbu_m_send(void *arg, mblk_t *mp)
2005084Sjohnlev {
2015084Sjohnlev 	xnb_t *xnbp = arg;
2025741Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
20310958Sdme@sun.com 	boolean_t sched = B_FALSE;
2045084Sjohnlev 
2055741Smrj 	mp = xnb_copy_to_peer(arg, mp);
2065084Sjohnlev 
20710958Sdme@sun.com 	mutex_enter(&xnbp->xnb_rx_lock);
2085084Sjohnlev 	/*
2095084Sjohnlev 	 * If we consumed all of the mblk_t's offered, perhaps we need
2105084Sjohnlev 	 * to indicate that we can accept more.  Otherwise we are full
2115084Sjohnlev 	 * and need to wait for space.
2125084Sjohnlev 	 */
2135084Sjohnlev 	if (mp == NULL) {
21410958Sdme@sun.com 		sched = xnbup->u_need_sched;
21510958Sdme@sun.com 		xnbup->u_need_sched = B_FALSE;
2165084Sjohnlev 	} else {
2175084Sjohnlev 		xnbup->u_need_sched = B_TRUE;
2185084Sjohnlev 	}
21910958Sdme@sun.com 	mutex_exit(&xnbp->xnb_rx_lock);
22010958Sdme@sun.com 
22110958Sdme@sun.com 	/*
22210958Sdme@sun.com 	 * If a previous transmit attempt failed because the ring
22310958Sdme@sun.com 	 * was full, try again now.
22410958Sdme@sun.com 	 */
22510958Sdme@sun.com 	if (sched)
22610958Sdme@sun.com 		mac_tx_update(xnbup->u_mh);
2275084Sjohnlev 
2285084Sjohnlev 	return (mp);
2295084Sjohnlev }
2305084Sjohnlev 
2315084Sjohnlev /*
2325084Sjohnlev  *  xnbu_m_set_mac_addr() -- set the physical network address on the board
2335084Sjohnlev  */
2345084Sjohnlev /* ARGSUSED */
2355084Sjohnlev static int
xnbu_m_set_mac_addr(void * arg,const uint8_t * macaddr)2365084Sjohnlev xnbu_m_set_mac_addr(void *arg, const uint8_t *macaddr)
2375084Sjohnlev {
2385084Sjohnlev 	xnb_t *xnbp = arg;
2395741Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
2405084Sjohnlev 
2415741Smrj 	bcopy(macaddr, xnbp->xnb_mac_addr, ETHERADDRL);
2425741Smrj 	mac_unicst_update(xnbup->u_mh, xnbp->xnb_mac_addr);
2435084Sjohnlev 
2445084Sjohnlev 	return (0);
2455084Sjohnlev }
2465084Sjohnlev 
2475084Sjohnlev /*
2485084Sjohnlev  *  xnbu_m_set_multicast() -- set (enable) or disable a multicast address
2495084Sjohnlev  */
2505084Sjohnlev /*ARGSUSED*/
2515084Sjohnlev static int
xnbu_m_set_multicast(void * arg,boolean_t add,const uint8_t * mca)2525084Sjohnlev xnbu_m_set_multicast(void *arg, boolean_t add, const uint8_t *mca)
2535084Sjohnlev {
2545084Sjohnlev 	/*
2555084Sjohnlev 	 * We always accept all packets from the peer, so nothing to
2565084Sjohnlev 	 * do for enable or disable.
2575084Sjohnlev 	 */
2585084Sjohnlev 	return (0);
2595084Sjohnlev }
2605084Sjohnlev 
2615084Sjohnlev 
2625084Sjohnlev /*
2635084Sjohnlev  * xnbu_m_set_promiscuous() -- set or reset promiscuous mode on the board
2645084Sjohnlev  */
2655084Sjohnlev /* ARGSUSED */
2665084Sjohnlev static int
xnbu_m_set_promiscuous(void * arg,boolean_t on)2675084Sjohnlev xnbu_m_set_promiscuous(void *arg, boolean_t on)
2685084Sjohnlev {
2695084Sjohnlev 	/*
2705084Sjohnlev 	 * We always accept all packets from the peer, so nothing to
2715084Sjohnlev 	 * do for enable or disable.
2725084Sjohnlev 	 */
2735084Sjohnlev 	return (0);
2745084Sjohnlev }
2755084Sjohnlev 
2765084Sjohnlev /*
2775084Sjohnlev  *  xnbu_m_start() -- start the board receiving and enable interrupts.
2785084Sjohnlev  */
2795084Sjohnlev /*ARGSUSED*/
2805084Sjohnlev static int
xnbu_m_start(void * arg)2815084Sjohnlev xnbu_m_start(void *arg)
2825084Sjohnlev {
2835084Sjohnlev 	return (0);
2845084Sjohnlev }
2855084Sjohnlev 
2865084Sjohnlev /*
2875084Sjohnlev  * xnbu_m_stop() - disable hardware
2885084Sjohnlev  */
2895084Sjohnlev /*ARGSUSED*/
2905084Sjohnlev static void
xnbu_m_stop(void * arg)2915084Sjohnlev xnbu_m_stop(void *arg)
2925084Sjohnlev {
2935084Sjohnlev }
2945084Sjohnlev 
2955084Sjohnlev static int
xnbu_m_stat(void * arg,uint_t stat,uint64_t * val)2965084Sjohnlev xnbu_m_stat(void *arg, uint_t stat, uint64_t *val)
2975084Sjohnlev {
2985084Sjohnlev 	xnb_t *xnbp = arg;
2995084Sjohnlev 
3005741Smrj 	mutex_enter(&xnbp->xnb_tx_lock);
3015741Smrj 	mutex_enter(&xnbp->xnb_rx_lock);
3025084Sjohnlev 
3035084Sjohnlev #define	map_stat(q, r)				\
3045084Sjohnlev 	case (MAC_STAT_##q):			\
3057615SMax.Zhen@Sun.COM 		*val = xnbp->xnb_stat_##r;	\
3065084Sjohnlev 		break
3075084Sjohnlev 
3085084Sjohnlev 	switch (stat) {
3095084Sjohnlev 
3107615SMax.Zhen@Sun.COM 	map_stat(IPACKETS, opackets);
3117615SMax.Zhen@Sun.COM 	map_stat(OPACKETS, ipackets);
3127615SMax.Zhen@Sun.COM 	map_stat(RBYTES, obytes);
3137615SMax.Zhen@Sun.COM 	map_stat(OBYTES, rbytes);
3145084Sjohnlev 
3155084Sjohnlev 	default:
3165741Smrj 		mutex_exit(&xnbp->xnb_rx_lock);
3175741Smrj 		mutex_exit(&xnbp->xnb_tx_lock);
3185084Sjohnlev 
3195084Sjohnlev 		return (ENOTSUP);
3205084Sjohnlev 	}
3215084Sjohnlev 
3225084Sjohnlev #undef map_stat
3235084Sjohnlev 
3245741Smrj 	mutex_exit(&xnbp->xnb_rx_lock);
3255741Smrj 	mutex_exit(&xnbp->xnb_tx_lock);
3265084Sjohnlev 
3275084Sjohnlev 	return (0);
3285084Sjohnlev }
3295084Sjohnlev 
3305084Sjohnlev static boolean_t
xnbu_m_getcapab(void * arg,mac_capab_t cap,void * cap_data)3315084Sjohnlev xnbu_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
3325084Sjohnlev {
33310958Sdme@sun.com 	_NOTE(ARGUNUSED(arg));
3345084Sjohnlev 
3355084Sjohnlev 	switch (cap) {
3365084Sjohnlev 	case MAC_CAPAB_HCKSUM: {
3375084Sjohnlev 		uint32_t *capab = cap_data;
3385084Sjohnlev 
33910958Sdme@sun.com 		*capab = HCKSUM_INET_PARTIAL;
3405084Sjohnlev 		break;
3415084Sjohnlev 	}
3425084Sjohnlev 	default:
3435084Sjohnlev 		return (B_FALSE);
3445084Sjohnlev 	}
3455084Sjohnlev 
3465084Sjohnlev 	return (B_TRUE);
3475084Sjohnlev }
3485084Sjohnlev 
34910958Sdme@sun.com /*
35010958Sdme@sun.com  * All packets are passed to the peer, so adding and removing
35110958Sdme@sun.com  * multicast addresses is meaningless.
35210958Sdme@sun.com  */
35310958Sdme@sun.com static boolean_t
xnbu_mcast_add(xnb_t * xnbp,ether_addr_t * addr)35410958Sdme@sun.com xnbu_mcast_add(xnb_t *xnbp, ether_addr_t *addr)
35510958Sdme@sun.com {
35610958Sdme@sun.com 	_NOTE(ARGUNUSED(xnbp, addr));
35710958Sdme@sun.com 
35810958Sdme@sun.com 	return (B_TRUE);
35910958Sdme@sun.com }
36010958Sdme@sun.com 
36110958Sdme@sun.com static boolean_t
xnbu_mcast_del(xnb_t * xnbp,ether_addr_t * addr)36210958Sdme@sun.com xnbu_mcast_del(xnb_t *xnbp, ether_addr_t *addr)
36310958Sdme@sun.com {
36410958Sdme@sun.com 	_NOTE(ARGUNUSED(xnbp, addr));
36510958Sdme@sun.com 
36610958Sdme@sun.com 	return (B_TRUE);
36710958Sdme@sun.com }
36810958Sdme@sun.com 
3695084Sjohnlev static int
xnbu_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)3705084Sjohnlev xnbu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
3715084Sjohnlev {
3725084Sjohnlev 	static xnb_flavour_t flavour = {
37310958Sdme@sun.com 		xnbu_to_host, xnbu_peer_connected, xnbu_peer_disconnected,
37410958Sdme@sun.com 		xnbu_hotplug_connected, xnbu_start_connect,
3755084Sjohnlev 		xnbu_cksum_from_peer, xnbu_cksum_to_peer,
37610958Sdme@sun.com 		xnbu_mcast_add, xnbu_mcast_del,
3775084Sjohnlev 	};
3785084Sjohnlev 	xnbu_t *xnbup;
3795084Sjohnlev 	xnb_t *xnbp;
3805084Sjohnlev 	mac_register_t *mr;
3815084Sjohnlev 	int err;
3825084Sjohnlev 
3835084Sjohnlev 	switch (cmd) {
3845084Sjohnlev 	case DDI_ATTACH:
3855084Sjohnlev 		break;
3865084Sjohnlev 	case DDI_RESUME:
3875084Sjohnlev 		return (DDI_SUCCESS);
3885084Sjohnlev 	default:
3895084Sjohnlev 		return (DDI_FAILURE);
3905084Sjohnlev 	}
3915084Sjohnlev 
3925084Sjohnlev 	xnbup = kmem_zalloc(sizeof (*xnbup), KM_SLEEP);
3935084Sjohnlev 
3945084Sjohnlev 	if ((mr = mac_alloc(MAC_VERSION)) == NULL) {
3955084Sjohnlev 		kmem_free(xnbup, sizeof (*xnbup));
3965084Sjohnlev 		return (DDI_FAILURE);
3975084Sjohnlev 	}
3985084Sjohnlev 
3995084Sjohnlev 	if (xnb_attach(dip, &flavour, xnbup) != DDI_SUCCESS) {
4005084Sjohnlev 		mac_free(mr);
4015084Sjohnlev 		kmem_free(xnbup, sizeof (*xnbup));
4025084Sjohnlev 		return (DDI_FAILURE);
4035084Sjohnlev 	}
4045084Sjohnlev 
4055084Sjohnlev 	xnbp = ddi_get_driver_private(dip);
4065084Sjohnlev 	ASSERT(xnbp != NULL);
4075084Sjohnlev 
4085084Sjohnlev 	mr->m_dip = dip;
4095084Sjohnlev 	mr->m_driver = xnbp;
4105084Sjohnlev 
4115084Sjohnlev 	/*
4125084Sjohnlev 	 *  Initialize pointers to device specific functions which will be
4135084Sjohnlev 	 *  used by the generic layer.
4145084Sjohnlev 	 */
4155084Sjohnlev 	mr->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
4165741Smrj 	mr->m_src_addr = xnbp->xnb_mac_addr;
41710958Sdme@sun.com 	mr->m_callbacks = &xnbu_callbacks;
4185084Sjohnlev 	mr->m_min_sdu = 0;
4195084Sjohnlev 	mr->m_max_sdu = XNBMAXPKT;
4205895Syz147064 	/*
4215895Syz147064 	 * xnbu is a virtual device, and it is not associated with any
4225895Syz147064 	 * physical device. Its margin size is determined by the maximum
4235895Syz147064 	 * packet size it can handle, which is PAGESIZE.
4245895Syz147064 	 */
4255895Syz147064 	mr->m_margin = PAGESIZE - XNBMAXPKT - sizeof (struct ether_header);
4265084Sjohnlev 
4275741Smrj 	(void) memset(xnbp->xnb_mac_addr, 0xff, ETHERADDRL);
4285741Smrj 	xnbp->xnb_mac_addr[0] &= 0xfe;
4295084Sjohnlev 	xnbup->u_need_sched = B_FALSE;
4305084Sjohnlev 
4315084Sjohnlev 	/*
4325084Sjohnlev 	 * Register ourselves with the GLDv3 interface.
4335084Sjohnlev 	 */
4345084Sjohnlev 	err = mac_register(mr, &xnbup->u_mh);
4355084Sjohnlev 	mac_free(mr);
4365084Sjohnlev 	if (err != 0) {
4375084Sjohnlev 		xnb_detach(dip);
4385084Sjohnlev 		kmem_free(xnbup, sizeof (*xnbup));
4395084Sjohnlev 		return (DDI_FAILURE);
4405084Sjohnlev 	}
4415084Sjohnlev 
4425084Sjohnlev 	mac_link_update(xnbup->u_mh, LINK_STATE_DOWN);
4435084Sjohnlev 
4445084Sjohnlev 	return (DDI_SUCCESS);
4455084Sjohnlev }
4465084Sjohnlev 
4475084Sjohnlev /*ARGSUSED*/
4485084Sjohnlev int
xnbu_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)4495084Sjohnlev xnbu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
4505084Sjohnlev {
4515084Sjohnlev 	xnb_t *xnbp = ddi_get_driver_private(dip);
4525741Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
4535084Sjohnlev 
4545084Sjohnlev 	switch (cmd) {
4555084Sjohnlev 	case DDI_DETACH:
4565084Sjohnlev 		break;
4575084Sjohnlev 	case DDI_SUSPEND:
4585084Sjohnlev 		return (DDI_SUCCESS);
4595084Sjohnlev 	default:
4605084Sjohnlev 		return (DDI_FAILURE);
4615084Sjohnlev 	}
4625084Sjohnlev 
4635084Sjohnlev 	ASSERT(xnbp != NULL);
4645084Sjohnlev 	ASSERT(xnbup != NULL);
4655084Sjohnlev 
4665741Smrj 	mutex_enter(&xnbp->xnb_tx_lock);
4675741Smrj 	mutex_enter(&xnbp->xnb_rx_lock);
4685084Sjohnlev 
4695741Smrj 	if (!xnbp->xnb_detachable || xnbp->xnb_connected ||
4707615SMax.Zhen@Sun.COM 	    (xnbp->xnb_tx_buf_count > 0)) {
4715741Smrj 		mutex_exit(&xnbp->xnb_rx_lock);
4725741Smrj 		mutex_exit(&xnbp->xnb_tx_lock);
4735084Sjohnlev 
4745084Sjohnlev 		return (DDI_FAILURE);
4755084Sjohnlev 	}
4765084Sjohnlev 
4775741Smrj 	mutex_exit(&xnbp->xnb_rx_lock);
4785741Smrj 	mutex_exit(&xnbp->xnb_tx_lock);
4795084Sjohnlev 
4805084Sjohnlev 	/*
4815084Sjohnlev 	 * Attempt to unregister the mac.
4825084Sjohnlev 	 */
4835084Sjohnlev 	if ((xnbup->u_mh != NULL) && (mac_unregister(xnbup->u_mh) != 0))
4845084Sjohnlev 		return (DDI_FAILURE);
4855084Sjohnlev 	kmem_free(xnbup, sizeof (*xnbup));
4865084Sjohnlev 
4875084Sjohnlev 	xnb_detach(dip);
4885084Sjohnlev 
4895084Sjohnlev 	return (DDI_SUCCESS);
4905084Sjohnlev }
4915084Sjohnlev 
4925084Sjohnlev DDI_DEFINE_STREAM_OPS(ops, nulldev, nulldev, xnbu_attach, xnbu_detach,
4937656SSherry.Moore@Sun.COM     nodev, NULL, D_MP, NULL, ddi_quiesce_not_supported);
4945084Sjohnlev 
4955084Sjohnlev static struct modldrv modldrv = {
4967615SMax.Zhen@Sun.COM 	&mod_driverops, "xnbu driver", &ops
4975084Sjohnlev };
4985084Sjohnlev 
4995084Sjohnlev static struct modlinkage modlinkage = {
5005084Sjohnlev 	MODREV_1, &modldrv, NULL
5015084Sjohnlev };
5025084Sjohnlev 
5035084Sjohnlev int
_init(void)5045084Sjohnlev _init(void)
5055084Sjohnlev {
5065084Sjohnlev 	int i;
5075084Sjohnlev 
5085084Sjohnlev 	mac_init_ops(&ops, "xnbu");
5095084Sjohnlev 
5105084Sjohnlev 	i = mod_install(&modlinkage);
5115084Sjohnlev 	if (i != DDI_SUCCESS)
5125084Sjohnlev 		mac_fini_ops(&ops);
5135084Sjohnlev 
5145084Sjohnlev 	return (i);
5155084Sjohnlev }
5165084Sjohnlev 
5175084Sjohnlev int
_fini(void)5185084Sjohnlev _fini(void)
5195084Sjohnlev {
5205084Sjohnlev 	int i;
5215084Sjohnlev 
5225084Sjohnlev 	i = mod_remove(&modlinkage);
5235084Sjohnlev 	if (i == DDI_SUCCESS)
5245084Sjohnlev 		mac_fini_ops(&ops);
5255084Sjohnlev 
5265084Sjohnlev 	return (i);
5275084Sjohnlev }
5285084Sjohnlev 
5295084Sjohnlev int
_info(struct modinfo * modinfop)5305084Sjohnlev _info(struct modinfo *modinfop)
5315084Sjohnlev {
5325084Sjohnlev 	return (mod_info(&modlinkage, modinfop));
5335084Sjohnlev }
534