xref: /illumos-gate/usr/src/uts/common/xen/io/xnbu.c (revision 0dc2366f)
1843e1988Sjohnlev /*
2843e1988Sjohnlev  * CDDL HEADER START
3843e1988Sjohnlev  *
4843e1988Sjohnlev  * The contents of this file are subject to the terms of the
5843e1988Sjohnlev  * Common Development and Distribution License (the "License").
6843e1988Sjohnlev  * You may not use this file except in compliance with the License.
7843e1988Sjohnlev  *
8843e1988Sjohnlev  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9843e1988Sjohnlev  * or http://www.opensolaris.org/os/licensing.
10843e1988Sjohnlev  * See the License for the specific language governing permissions
11843e1988Sjohnlev  * and limitations under the License.
12843e1988Sjohnlev  *
13843e1988Sjohnlev  * When distributing Covered Code, include this CDDL HEADER in each
14843e1988Sjohnlev  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15843e1988Sjohnlev  * If applicable, add the following below this CDDL HEADER, with the
16843e1988Sjohnlev  * fields enclosed by brackets "[]" replaced with your own identifying
17843e1988Sjohnlev  * information: Portions Copyright [yyyy] [name of copyright owner]
18843e1988Sjohnlev  *
19843e1988Sjohnlev  * CDDL HEADER END
20843e1988Sjohnlev  */
21843e1988Sjohnlev 
22843e1988Sjohnlev /*
23*0dc2366fSVenugopal Iyer  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
24843e1988Sjohnlev  * Use is subject to license terms.
25843e1988Sjohnlev  */
26843e1988Sjohnlev 
27843e1988Sjohnlev /*
28843e1988Sjohnlev  * Xen inter-domain backend - GLDv3 driver edition.
29843e1988Sjohnlev  *
30843e1988Sjohnlev  * A traditional GLDv3 driver used to communicate with a guest
31843e1988Sjohnlev  * domain.  This driver is typically plumbed underneath the IP stack
32843e1988Sjohnlev  * or a software ethernet bridge.
33843e1988Sjohnlev  */
34843e1988Sjohnlev 
35843e1988Sjohnlev #include "xnb.h"
36843e1988Sjohnlev 
37843e1988Sjohnlev #include <sys/sunddi.h>
38843e1988Sjohnlev #include <sys/conf.h>
39843e1988Sjohnlev #include <sys/modctl.h>
40843e1988Sjohnlev #include <sys/strsubr.h>
41843e1988Sjohnlev #include <sys/dlpi.h>
42843e1988Sjohnlev #include <sys/pattr.h>
43da14cebeSEric Cheng #include <sys/mac_provider.h>
44843e1988Sjohnlev #include <sys/mac_ether.h>
45843e1988Sjohnlev #include <xen/sys/xendev.h>
4656567907SDavid Edmondson #include <sys/note.h>
47843e1988Sjohnlev 
48843e1988Sjohnlev /* Required driver entry points for GLDv3 */
49843e1988Sjohnlev static int	xnbu_m_start(void *);
50843e1988Sjohnlev static void	xnbu_m_stop(void *);
51843e1988Sjohnlev static int	xnbu_m_set_mac_addr(void *, const uint8_t *);
52843e1988Sjohnlev static int	xnbu_m_set_multicast(void *, boolean_t, const uint8_t *);
53843e1988Sjohnlev static int	xnbu_m_set_promiscuous(void *, boolean_t);
54843e1988Sjohnlev static int	xnbu_m_stat(void *, uint_t, uint64_t *);
55843e1988Sjohnlev static boolean_t xnbu_m_getcapab(void *, mac_capab_t, void *);
56843e1988Sjohnlev static mblk_t	*xnbu_m_send(void *, mblk_t *);
57843e1988Sjohnlev 
58843e1988Sjohnlev typedef struct xnbu {
59843e1988Sjohnlev 	mac_handle_t		u_mh;
60843e1988Sjohnlev 	boolean_t		u_need_sched;
61843e1988Sjohnlev } xnbu_t;
62843e1988Sjohnlev 
6356567907SDavid Edmondson static mac_callbacks_t xnbu_callbacks = {
64da14cebeSEric Cheng 	MC_GETCAPAB,
65843e1988Sjohnlev 	xnbu_m_stat,
66843e1988Sjohnlev 	xnbu_m_start,
67843e1988Sjohnlev 	xnbu_m_stop,
68843e1988Sjohnlev 	xnbu_m_set_promiscuous,
69843e1988Sjohnlev 	xnbu_m_set_multicast,
70843e1988Sjohnlev 	xnbu_m_set_mac_addr,
71843e1988Sjohnlev 	xnbu_m_send,
72843e1988Sjohnlev 	NULL,
73*0dc2366fSVenugopal Iyer 	NULL,
74843e1988Sjohnlev 	xnbu_m_getcapab
75843e1988Sjohnlev };
76843e1988Sjohnlev 
77843e1988Sjohnlev static void
xnbu_to_host(xnb_t * xnbp,mblk_t * mp)78843e1988Sjohnlev xnbu_to_host(xnb_t *xnbp, mblk_t *mp)
79843e1988Sjohnlev {
80551bc2a6Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
81843e1988Sjohnlev 	boolean_t sched = B_FALSE;
82843e1988Sjohnlev 
83843e1988Sjohnlev 	ASSERT(mp != NULL);
84843e1988Sjohnlev 
85da14cebeSEric Cheng 	mac_rx(xnbup->u_mh, NULL, mp);
86843e1988Sjohnlev 
87024c26efSMax zhen 	mutex_enter(&xnbp->xnb_rx_lock);
88843e1988Sjohnlev 
89843e1988Sjohnlev 	/*
90843e1988Sjohnlev 	 * If a transmit attempt failed because we ran out of ring
91843e1988Sjohnlev 	 * space and there is now some space, re-enable the transmit
92843e1988Sjohnlev 	 * path.
93843e1988Sjohnlev 	 */
94843e1988Sjohnlev 	if (xnbup->u_need_sched &&
95551bc2a6Smrj 	    RING_HAS_UNCONSUMED_REQUESTS(&xnbp->xnb_rx_ring)) {
96843e1988Sjohnlev 		sched = B_TRUE;
97843e1988Sjohnlev 		xnbup->u_need_sched = B_FALSE;
98843e1988Sjohnlev 	}
99843e1988Sjohnlev 
100024c26efSMax zhen 	mutex_exit(&xnbp->xnb_rx_lock);
101843e1988Sjohnlev 
102843e1988Sjohnlev 	if (sched)
103843e1988Sjohnlev 		mac_tx_update(xnbup->u_mh);
104843e1988Sjohnlev }
105843e1988Sjohnlev 
106843e1988Sjohnlev static mblk_t *
xnbu_cksum_from_peer(xnb_t * xnbp,mblk_t * mp,uint16_t flags)107843e1988Sjohnlev xnbu_cksum_from_peer(xnb_t *xnbp, mblk_t *mp, uint16_t flags)
108843e1988Sjohnlev {
109843e1988Sjohnlev 	/*
110843e1988Sjohnlev 	 * Take a conservative approach - if the checksum is blank
111843e1988Sjohnlev 	 * then we fill it in.
112843e1988Sjohnlev 	 *
113843e1988Sjohnlev 	 * If the consumer of the packet is IP then we might actually
114843e1988Sjohnlev 	 * only need fill it in if the data is not validated, but how
115843e1988Sjohnlev 	 * do we know who might end up with the packet?
116843e1988Sjohnlev 	 */
117843e1988Sjohnlev 
118843e1988Sjohnlev 	if ((flags & NETTXF_csum_blank) != 0) {
119843e1988Sjohnlev 		/*
120843e1988Sjohnlev 		 * The checksum is blank.  We must fill it in here.
121843e1988Sjohnlev 		 */
122843e1988Sjohnlev 		mp = xnb_process_cksum_flags(xnbp, mp, 0);
123843e1988Sjohnlev 
124843e1988Sjohnlev 		/*
125843e1988Sjohnlev 		 * Because we calculated the checksum ourselves we
126843e1988Sjohnlev 		 * know that it must be good, so we assert this.
127843e1988Sjohnlev 		 */
128d4071fd3Sdme 		flags |= NETTXF_data_validated;
129843e1988Sjohnlev 	}
130843e1988Sjohnlev 
131843e1988Sjohnlev 	if ((flags & NETTXF_data_validated) != 0) {
132843e1988Sjohnlev 		/*
133843e1988Sjohnlev 		 * The checksum is asserted valid.
134843e1988Sjohnlev 		 */
135*0dc2366fSVenugopal Iyer 		mac_hcksum_set(mp, 0, 0, 0, 0, HCK_FULLCKSUM_OK);
136843e1988Sjohnlev 	}
137843e1988Sjohnlev 
138843e1988Sjohnlev 	return (mp);
139843e1988Sjohnlev }
140843e1988Sjohnlev 
141843e1988Sjohnlev static uint16_t
xnbu_cksum_to_peer(xnb_t * xnbp,mblk_t * mp)142843e1988Sjohnlev xnbu_cksum_to_peer(xnb_t *xnbp, mblk_t *mp)
143843e1988Sjohnlev {
14456567907SDavid Edmondson 	_NOTE(ARGUNUSED(xnbp));
145843e1988Sjohnlev 	uint16_t r = 0;
14656567907SDavid Edmondson 	uint32_t pflags;
147843e1988Sjohnlev 
148*0dc2366fSVenugopal Iyer 	mac_hcksum_get(mp, NULL, NULL, NULL, NULL, &pflags);
149843e1988Sjohnlev 
15056567907SDavid Edmondson 	/*
15156567907SDavid Edmondson 	 * If the protocol stack has requested checksum
15256567907SDavid Edmondson 	 * offload, inform the peer that we have not
15356567907SDavid Edmondson 	 * calculated the checksum.
15456567907SDavid Edmondson 	 */
15556567907SDavid Edmondson 	if ((pflags & HCK_FULLCKSUM) != 0)
15656567907SDavid Edmondson 		r |= NETRXF_csum_blank;
157843e1988Sjohnlev 
158843e1988Sjohnlev 	return (r);
159843e1988Sjohnlev }
160843e1988Sjohnlev 
16156567907SDavid Edmondson static boolean_t
xnbu_start_connect(xnb_t * xnbp)16256567907SDavid Edmondson xnbu_start_connect(xnb_t *xnbp)
163843e1988Sjohnlev {
164551bc2a6Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
165843e1988Sjohnlev 
166843e1988Sjohnlev 	mac_link_update(xnbup->u_mh, LINK_STATE_UP);
167843e1988Sjohnlev 	/*
168843e1988Sjohnlev 	 * We are able to send packets now - bring them on.
169843e1988Sjohnlev 	 */
170843e1988Sjohnlev 	mac_tx_update(xnbup->u_mh);
17156567907SDavid Edmondson 
17256567907SDavid Edmondson 	return (B_TRUE);
17356567907SDavid Edmondson }
17456567907SDavid Edmondson 
17556567907SDavid Edmondson static boolean_t
xnbu_peer_connected(xnb_t * xnbp)17656567907SDavid Edmondson xnbu_peer_connected(xnb_t *xnbp)
17756567907SDavid Edmondson {
17856567907SDavid Edmondson 	_NOTE(ARGUNUSED(xnbp));
17956567907SDavid Edmondson 
18056567907SDavid Edmondson 	return (B_TRUE);
181843e1988Sjohnlev }
182843e1988Sjohnlev 
183843e1988Sjohnlev static void
xnbu_peer_disconnected(xnb_t * xnbp)18456567907SDavid Edmondson xnbu_peer_disconnected(xnb_t *xnbp)
185843e1988Sjohnlev {
186551bc2a6Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
187843e1988Sjohnlev 
188843e1988Sjohnlev 	mac_link_update(xnbup->u_mh, LINK_STATE_DOWN);
189843e1988Sjohnlev }
190843e1988Sjohnlev 
191843e1988Sjohnlev /*ARGSUSED*/
192843e1988Sjohnlev static boolean_t
xnbu_hotplug_connected(xnb_t * xnbp)19356567907SDavid Edmondson xnbu_hotplug_connected(xnb_t *xnbp)
194843e1988Sjohnlev {
195843e1988Sjohnlev 	return (B_TRUE);
196843e1988Sjohnlev }
197843e1988Sjohnlev 
198843e1988Sjohnlev static mblk_t *
xnbu_m_send(void * arg,mblk_t * mp)199843e1988Sjohnlev xnbu_m_send(void *arg, mblk_t *mp)
200843e1988Sjohnlev {
201843e1988Sjohnlev 	xnb_t *xnbp = arg;
202551bc2a6Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
20356567907SDavid Edmondson 	boolean_t sched = B_FALSE;
204843e1988Sjohnlev 
205551bc2a6Smrj 	mp = xnb_copy_to_peer(arg, mp);
206843e1988Sjohnlev 
20756567907SDavid Edmondson 	mutex_enter(&xnbp->xnb_rx_lock);
208843e1988Sjohnlev 	/*
209843e1988Sjohnlev 	 * If we consumed all of the mblk_t's offered, perhaps we need
210843e1988Sjohnlev 	 * to indicate that we can accept more.  Otherwise we are full
211843e1988Sjohnlev 	 * and need to wait for space.
212843e1988Sjohnlev 	 */
213843e1988Sjohnlev 	if (mp == NULL) {
21456567907SDavid Edmondson 		sched = xnbup->u_need_sched;
21556567907SDavid Edmondson 		xnbup->u_need_sched = B_FALSE;
216843e1988Sjohnlev 	} else {
217843e1988Sjohnlev 		xnbup->u_need_sched = B_TRUE;
218843e1988Sjohnlev 	}
21956567907SDavid Edmondson 	mutex_exit(&xnbp->xnb_rx_lock);
22056567907SDavid Edmondson 
22156567907SDavid Edmondson 	/*
22256567907SDavid Edmondson 	 * If a previous transmit attempt failed because the ring
22356567907SDavid Edmondson 	 * was full, try again now.
22456567907SDavid Edmondson 	 */
22556567907SDavid Edmondson 	if (sched)
22656567907SDavid Edmondson 		mac_tx_update(xnbup->u_mh);
227843e1988Sjohnlev 
228843e1988Sjohnlev 	return (mp);
229843e1988Sjohnlev }
230843e1988Sjohnlev 
231843e1988Sjohnlev /*
232843e1988Sjohnlev  *  xnbu_m_set_mac_addr() -- set the physical network address on the board
233843e1988Sjohnlev  */
234843e1988Sjohnlev /* ARGSUSED */
235843e1988Sjohnlev static int
xnbu_m_set_mac_addr(void * arg,const uint8_t * macaddr)236843e1988Sjohnlev xnbu_m_set_mac_addr(void *arg, const uint8_t *macaddr)
237843e1988Sjohnlev {
238843e1988Sjohnlev 	xnb_t *xnbp = arg;
239551bc2a6Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
240843e1988Sjohnlev 
241551bc2a6Smrj 	bcopy(macaddr, xnbp->xnb_mac_addr, ETHERADDRL);
242551bc2a6Smrj 	mac_unicst_update(xnbup->u_mh, xnbp->xnb_mac_addr);
243843e1988Sjohnlev 
244843e1988Sjohnlev 	return (0);
245843e1988Sjohnlev }
246843e1988Sjohnlev 
247843e1988Sjohnlev /*
248843e1988Sjohnlev  *  xnbu_m_set_multicast() -- set (enable) or disable a multicast address
249843e1988Sjohnlev  */
250843e1988Sjohnlev /*ARGSUSED*/
251843e1988Sjohnlev static int
xnbu_m_set_multicast(void * arg,boolean_t add,const uint8_t * mca)252843e1988Sjohnlev xnbu_m_set_multicast(void *arg, boolean_t add, const uint8_t *mca)
253843e1988Sjohnlev {
254843e1988Sjohnlev 	/*
255843e1988Sjohnlev 	 * We always accept all packets from the peer, so nothing to
256843e1988Sjohnlev 	 * do for enable or disable.
257843e1988Sjohnlev 	 */
258843e1988Sjohnlev 	return (0);
259843e1988Sjohnlev }
260843e1988Sjohnlev 
261843e1988Sjohnlev 
262843e1988Sjohnlev /*
263843e1988Sjohnlev  * xnbu_m_set_promiscuous() -- set or reset promiscuous mode on the board
264843e1988Sjohnlev  */
265843e1988Sjohnlev /* ARGSUSED */
266843e1988Sjohnlev static int
xnbu_m_set_promiscuous(void * arg,boolean_t on)267843e1988Sjohnlev xnbu_m_set_promiscuous(void *arg, boolean_t on)
268843e1988Sjohnlev {
269843e1988Sjohnlev 	/*
270843e1988Sjohnlev 	 * We always accept all packets from the peer, so nothing to
271843e1988Sjohnlev 	 * do for enable or disable.
272843e1988Sjohnlev 	 */
273843e1988Sjohnlev 	return (0);
274843e1988Sjohnlev }
275843e1988Sjohnlev 
276843e1988Sjohnlev /*
277843e1988Sjohnlev  *  xnbu_m_start() -- start the board receiving and enable interrupts.
278843e1988Sjohnlev  */
279843e1988Sjohnlev /*ARGSUSED*/
280843e1988Sjohnlev static int
xnbu_m_start(void * arg)281843e1988Sjohnlev xnbu_m_start(void *arg)
282843e1988Sjohnlev {
283843e1988Sjohnlev 	return (0);
284843e1988Sjohnlev }
285843e1988Sjohnlev 
286843e1988Sjohnlev /*
287843e1988Sjohnlev  * xnbu_m_stop() - disable hardware
288843e1988Sjohnlev  */
289843e1988Sjohnlev /*ARGSUSED*/
290843e1988Sjohnlev static void
xnbu_m_stop(void * arg)291843e1988Sjohnlev xnbu_m_stop(void *arg)
292843e1988Sjohnlev {
293843e1988Sjohnlev }
294843e1988Sjohnlev 
295843e1988Sjohnlev static int
xnbu_m_stat(void * arg,uint_t stat,uint64_t * val)296843e1988Sjohnlev xnbu_m_stat(void *arg, uint_t stat, uint64_t *val)
297843e1988Sjohnlev {
298843e1988Sjohnlev 	xnb_t *xnbp = arg;
299843e1988Sjohnlev 
300551bc2a6Smrj 	mutex_enter(&xnbp->xnb_tx_lock);
301551bc2a6Smrj 	mutex_enter(&xnbp->xnb_rx_lock);
302843e1988Sjohnlev 
303843e1988Sjohnlev #define	map_stat(q, r)				\
304843e1988Sjohnlev 	case (MAC_STAT_##q):			\
305024c26efSMax zhen 		*val = xnbp->xnb_stat_##r;	\
306843e1988Sjohnlev 		break
307843e1988Sjohnlev 
308843e1988Sjohnlev 	switch (stat) {
309843e1988Sjohnlev 
310024c26efSMax zhen 	map_stat(IPACKETS, opackets);
311024c26efSMax zhen 	map_stat(OPACKETS, ipackets);
312024c26efSMax zhen 	map_stat(RBYTES, obytes);
313024c26efSMax zhen 	map_stat(OBYTES, rbytes);
314843e1988Sjohnlev 
315843e1988Sjohnlev 	default:
316551bc2a6Smrj 		mutex_exit(&xnbp->xnb_rx_lock);
317551bc2a6Smrj 		mutex_exit(&xnbp->xnb_tx_lock);
318843e1988Sjohnlev 
319843e1988Sjohnlev 		return (ENOTSUP);
320843e1988Sjohnlev 	}
321843e1988Sjohnlev 
322843e1988Sjohnlev #undef map_stat
323843e1988Sjohnlev 
324551bc2a6Smrj 	mutex_exit(&xnbp->xnb_rx_lock);
325551bc2a6Smrj 	mutex_exit(&xnbp->xnb_tx_lock);
326843e1988Sjohnlev 
327843e1988Sjohnlev 	return (0);
328843e1988Sjohnlev }
329843e1988Sjohnlev 
330843e1988Sjohnlev static boolean_t
xnbu_m_getcapab(void * arg,mac_capab_t cap,void * cap_data)331843e1988Sjohnlev xnbu_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
332843e1988Sjohnlev {
33356567907SDavid Edmondson 	_NOTE(ARGUNUSED(arg));
334843e1988Sjohnlev 
335843e1988Sjohnlev 	switch (cap) {
336843e1988Sjohnlev 	case MAC_CAPAB_HCKSUM: {
337843e1988Sjohnlev 		uint32_t *capab = cap_data;
338843e1988Sjohnlev 
33956567907SDavid Edmondson 		*capab = HCKSUM_INET_PARTIAL;
340843e1988Sjohnlev 		break;
341843e1988Sjohnlev 	}
342843e1988Sjohnlev 	default:
343843e1988Sjohnlev 		return (B_FALSE);
344843e1988Sjohnlev 	}
345843e1988Sjohnlev 
346843e1988Sjohnlev 	return (B_TRUE);
347843e1988Sjohnlev }
348843e1988Sjohnlev 
34956567907SDavid Edmondson /*
35056567907SDavid Edmondson  * All packets are passed to the peer, so adding and removing
35156567907SDavid Edmondson  * multicast addresses is meaningless.
35256567907SDavid Edmondson  */
35356567907SDavid Edmondson static boolean_t
xnbu_mcast_add(xnb_t * xnbp,ether_addr_t * addr)35456567907SDavid Edmondson xnbu_mcast_add(xnb_t *xnbp, ether_addr_t *addr)
35556567907SDavid Edmondson {
35656567907SDavid Edmondson 	_NOTE(ARGUNUSED(xnbp, addr));
35756567907SDavid Edmondson 
35856567907SDavid Edmondson 	return (B_TRUE);
35956567907SDavid Edmondson }
36056567907SDavid Edmondson 
36156567907SDavid Edmondson static boolean_t
xnbu_mcast_del(xnb_t * xnbp,ether_addr_t * addr)36256567907SDavid Edmondson xnbu_mcast_del(xnb_t *xnbp, ether_addr_t *addr)
36356567907SDavid Edmondson {
36456567907SDavid Edmondson 	_NOTE(ARGUNUSED(xnbp, addr));
36556567907SDavid Edmondson 
36656567907SDavid Edmondson 	return (B_TRUE);
36756567907SDavid Edmondson }
36856567907SDavid Edmondson 
369843e1988Sjohnlev static int
xnbu_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)370843e1988Sjohnlev xnbu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
371843e1988Sjohnlev {
372843e1988Sjohnlev 	static xnb_flavour_t flavour = {
37356567907SDavid Edmondson 		xnbu_to_host, xnbu_peer_connected, xnbu_peer_disconnected,
37456567907SDavid Edmondson 		xnbu_hotplug_connected, xnbu_start_connect,
375843e1988Sjohnlev 		xnbu_cksum_from_peer, xnbu_cksum_to_peer,
37656567907SDavid Edmondson 		xnbu_mcast_add, xnbu_mcast_del,
377843e1988Sjohnlev 	};
378843e1988Sjohnlev 	xnbu_t *xnbup;
379843e1988Sjohnlev 	xnb_t *xnbp;
380843e1988Sjohnlev 	mac_register_t *mr;
381843e1988Sjohnlev 	int err;
382843e1988Sjohnlev 
383843e1988Sjohnlev 	switch (cmd) {
384843e1988Sjohnlev 	case DDI_ATTACH:
385843e1988Sjohnlev 		break;
386843e1988Sjohnlev 	case DDI_RESUME:
387843e1988Sjohnlev 		return (DDI_SUCCESS);
388843e1988Sjohnlev 	default:
389843e1988Sjohnlev 		return (DDI_FAILURE);
390843e1988Sjohnlev 	}
391843e1988Sjohnlev 
392843e1988Sjohnlev 	xnbup = kmem_zalloc(sizeof (*xnbup), KM_SLEEP);
393843e1988Sjohnlev 
394843e1988Sjohnlev 	if ((mr = mac_alloc(MAC_VERSION)) == NULL) {
395843e1988Sjohnlev 		kmem_free(xnbup, sizeof (*xnbup));
396843e1988Sjohnlev 		return (DDI_FAILURE);
397843e1988Sjohnlev 	}
398843e1988Sjohnlev 
399843e1988Sjohnlev 	if (xnb_attach(dip, &flavour, xnbup) != DDI_SUCCESS) {
400843e1988Sjohnlev 		mac_free(mr);
401843e1988Sjohnlev 		kmem_free(xnbup, sizeof (*xnbup));
402843e1988Sjohnlev 		return (DDI_FAILURE);
403843e1988Sjohnlev 	}
404843e1988Sjohnlev 
405843e1988Sjohnlev 	xnbp = ddi_get_driver_private(dip);
406843e1988Sjohnlev 	ASSERT(xnbp != NULL);
407843e1988Sjohnlev 
408843e1988Sjohnlev 	mr->m_dip = dip;
409843e1988Sjohnlev 	mr->m_driver = xnbp;
410843e1988Sjohnlev 
411843e1988Sjohnlev 	/*
412843e1988Sjohnlev 	 *  Initialize pointers to device specific functions which will be
413843e1988Sjohnlev 	 *  used by the generic layer.
414843e1988Sjohnlev 	 */
415843e1988Sjohnlev 	mr->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
416551bc2a6Smrj 	mr->m_src_addr = xnbp->xnb_mac_addr;
41756567907SDavid Edmondson 	mr->m_callbacks = &xnbu_callbacks;
418843e1988Sjohnlev 	mr->m_min_sdu = 0;
419843e1988Sjohnlev 	mr->m_max_sdu = XNBMAXPKT;
420d62bc4baSyz 	/*
421d62bc4baSyz 	 * xnbu is a virtual device, and it is not associated with any
422d62bc4baSyz 	 * physical device. Its margin size is determined by the maximum
423d62bc4baSyz 	 * packet size it can handle, which is PAGESIZE.
424d62bc4baSyz 	 */
425d62bc4baSyz 	mr->m_margin = PAGESIZE - XNBMAXPKT - sizeof (struct ether_header);
426843e1988Sjohnlev 
427551bc2a6Smrj 	(void) memset(xnbp->xnb_mac_addr, 0xff, ETHERADDRL);
428551bc2a6Smrj 	xnbp->xnb_mac_addr[0] &= 0xfe;
429843e1988Sjohnlev 	xnbup->u_need_sched = B_FALSE;
430843e1988Sjohnlev 
431843e1988Sjohnlev 	/*
432843e1988Sjohnlev 	 * Register ourselves with the GLDv3 interface.
433843e1988Sjohnlev 	 */
434843e1988Sjohnlev 	err = mac_register(mr, &xnbup->u_mh);
435843e1988Sjohnlev 	mac_free(mr);
436843e1988Sjohnlev 	if (err != 0) {
437843e1988Sjohnlev 		xnb_detach(dip);
438843e1988Sjohnlev 		kmem_free(xnbup, sizeof (*xnbup));
439843e1988Sjohnlev 		return (DDI_FAILURE);
440843e1988Sjohnlev 	}
441843e1988Sjohnlev 
442843e1988Sjohnlev 	mac_link_update(xnbup->u_mh, LINK_STATE_DOWN);
443843e1988Sjohnlev 
444843e1988Sjohnlev 	return (DDI_SUCCESS);
445843e1988Sjohnlev }
446843e1988Sjohnlev 
447843e1988Sjohnlev /*ARGSUSED*/
448843e1988Sjohnlev int
xnbu_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)449843e1988Sjohnlev xnbu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
450843e1988Sjohnlev {
451843e1988Sjohnlev 	xnb_t *xnbp = ddi_get_driver_private(dip);
452551bc2a6Smrj 	xnbu_t *xnbup = xnbp->xnb_flavour_data;
453843e1988Sjohnlev 
454843e1988Sjohnlev 	switch (cmd) {
455843e1988Sjohnlev 	case DDI_DETACH:
456843e1988Sjohnlev 		break;
457843e1988Sjohnlev 	case DDI_SUSPEND:
458843e1988Sjohnlev 		return (DDI_SUCCESS);
459843e1988Sjohnlev 	default:
460843e1988Sjohnlev 		return (DDI_FAILURE);
461843e1988Sjohnlev 	}
462843e1988Sjohnlev 
463843e1988Sjohnlev 	ASSERT(xnbp != NULL);
464843e1988Sjohnlev 	ASSERT(xnbup != NULL);
465843e1988Sjohnlev 
466551bc2a6Smrj 	mutex_enter(&xnbp->xnb_tx_lock);
467551bc2a6Smrj 	mutex_enter(&xnbp->xnb_rx_lock);
468843e1988Sjohnlev 
469551bc2a6Smrj 	if (!xnbp->xnb_detachable || xnbp->xnb_connected ||
470024c26efSMax zhen 	    (xnbp->xnb_tx_buf_count > 0)) {
471551bc2a6Smrj 		mutex_exit(&xnbp->xnb_rx_lock);
472551bc2a6Smrj 		mutex_exit(&xnbp->xnb_tx_lock);
473843e1988Sjohnlev 
474843e1988Sjohnlev 		return (DDI_FAILURE);
475843e1988Sjohnlev 	}
476843e1988Sjohnlev 
477551bc2a6Smrj 	mutex_exit(&xnbp->xnb_rx_lock);
478551bc2a6Smrj 	mutex_exit(&xnbp->xnb_tx_lock);
479843e1988Sjohnlev 
480843e1988Sjohnlev 	/*
481843e1988Sjohnlev 	 * Attempt to unregister the mac.
482843e1988Sjohnlev 	 */
483843e1988Sjohnlev 	if ((xnbup->u_mh != NULL) && (mac_unregister(xnbup->u_mh) != 0))
484843e1988Sjohnlev 		return (DDI_FAILURE);
485843e1988Sjohnlev 	kmem_free(xnbup, sizeof (*xnbup));
486843e1988Sjohnlev 
487843e1988Sjohnlev 	xnb_detach(dip);
488843e1988Sjohnlev 
489843e1988Sjohnlev 	return (DDI_SUCCESS);
490843e1988Sjohnlev }
491843e1988Sjohnlev 
492843e1988Sjohnlev DDI_DEFINE_STREAM_OPS(ops, nulldev, nulldev, xnbu_attach, xnbu_detach,
49319397407SSherry Moore     nodev, NULL, D_MP, NULL, ddi_quiesce_not_supported);
494843e1988Sjohnlev 
495843e1988Sjohnlev static struct modldrv modldrv = {
496024c26efSMax zhen 	&mod_driverops, "xnbu driver", &ops
497843e1988Sjohnlev };
498843e1988Sjohnlev 
499843e1988Sjohnlev static struct modlinkage modlinkage = {
500843e1988Sjohnlev 	MODREV_1, &modldrv, NULL
501843e1988Sjohnlev };
502843e1988Sjohnlev 
503843e1988Sjohnlev int
_init(void)504843e1988Sjohnlev _init(void)
505843e1988Sjohnlev {
506843e1988Sjohnlev 	int i;
507843e1988Sjohnlev 
508843e1988Sjohnlev 	mac_init_ops(&ops, "xnbu");
509843e1988Sjohnlev 
510843e1988Sjohnlev 	i = mod_install(&modlinkage);
511843e1988Sjohnlev 	if (i != DDI_SUCCESS)
512843e1988Sjohnlev 		mac_fini_ops(&ops);
513843e1988Sjohnlev 
514843e1988Sjohnlev 	return (i);
515843e1988Sjohnlev }
516843e1988Sjohnlev 
517843e1988Sjohnlev int
_fini(void)518843e1988Sjohnlev _fini(void)
519843e1988Sjohnlev {
520843e1988Sjohnlev 	int i;
521843e1988Sjohnlev 
522843e1988Sjohnlev 	i = mod_remove(&modlinkage);
523843e1988Sjohnlev 	if (i == DDI_SUCCESS)
524843e1988Sjohnlev 		mac_fini_ops(&ops);
525843e1988Sjohnlev 
526843e1988Sjohnlev 	return (i);
527843e1988Sjohnlev }
528843e1988Sjohnlev 
529843e1988Sjohnlev int
_info(struct modinfo * modinfop)530843e1988Sjohnlev _info(struct modinfo *modinfop)
531843e1988Sjohnlev {
532843e1988Sjohnlev 	return (mod_info(&modlinkage, modinfop));
533843e1988Sjohnlev }
534