hv_netvsc_drv_freebsd.c revision 253869
1250199Sgrehan/*-
2250199Sgrehan * Copyright (c) 2010-2012 Citrix Inc.
3250199Sgrehan * Copyright (c) 2009-2012 Microsoft Corp.
4250199Sgrehan * Copyright (c) 2012 NetApp Inc.
5250199Sgrehan * All rights reserved.
6250199Sgrehan *
7250199Sgrehan * Redistribution and use in source and binary forms, with or without
8250199Sgrehan * modification, are permitted provided that the following conditions
9250199Sgrehan * are met:
10250199Sgrehan * 1. Redistributions of source code must retain the above copyright
11250199Sgrehan *    notice unmodified, this list of conditions, and the following
12250199Sgrehan *    disclaimer.
13250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
14250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
15250199Sgrehan *    documentation and/or other materials provided with the distribution.
16250199Sgrehan *
17250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18250199Sgrehan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19250199Sgrehan * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20250199Sgrehan * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21250199Sgrehan * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22250199Sgrehan * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23250199Sgrehan * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24250199Sgrehan * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25250199Sgrehan * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26250199Sgrehan * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27250199Sgrehan */
28250199Sgrehan
29250199Sgrehan/*-
30250199Sgrehan * Copyright (c) 2004-2006 Kip Macy
31250199Sgrehan * All rights reserved.
32250199Sgrehan *
33250199Sgrehan * Redistribution and use in source and binary forms, with or without
34250199Sgrehan * modification, are permitted provided that the following conditions
35250199Sgrehan * are met:
36250199Sgrehan * 1. Redistributions of source code must retain the above copyright
37250199Sgrehan *    notice, this list of conditions and the following disclaimer.
38250199Sgrehan * 2. Redistributions in binary form must reproduce the above copyright
39250199Sgrehan *    notice, this list of conditions and the following disclaimer in the
40250199Sgrehan *    documentation and/or other materials provided with the distribution.
41250199Sgrehan *
42250199Sgrehan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
43250199Sgrehan * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44250199Sgrehan * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45250199Sgrehan * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
46250199Sgrehan * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47250199Sgrehan * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
48250199Sgrehan * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
49250199Sgrehan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
50250199Sgrehan * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
51250199Sgrehan * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
52250199Sgrehan * SUCH DAMAGE.
53250199Sgrehan */
54250199Sgrehan
55250199Sgrehan#include <sys/param.h>
56250199Sgrehan#include <sys/systm.h>
57250199Sgrehan#include <sys/sockio.h>
58250199Sgrehan#include <sys/mbuf.h>
59250199Sgrehan#include <sys/malloc.h>
60250199Sgrehan#include <sys/module.h>
61250199Sgrehan#include <sys/kernel.h>
62250199Sgrehan#include <sys/socket.h>
63250199Sgrehan#include <sys/queue.h>
64250199Sgrehan#include <sys/lock.h>
65250199Sgrehan#include <sys/sx.h>
66250199Sgrehan
67250199Sgrehan#include <net/if.h>
68250199Sgrehan#include <net/if_arp.h>
69250199Sgrehan#include <net/ethernet.h>
70250199Sgrehan#include <net/if_dl.h>
71250199Sgrehan#include <net/if_media.h>
72250199Sgrehan
73250199Sgrehan#include <net/bpf.h>
74250199Sgrehan
75250199Sgrehan#include <net/if_types.h>
76250199Sgrehan#include <net/if_vlan_var.h>
77250199Sgrehan#include <net/if.h>
78250199Sgrehan
79250199Sgrehan#include <netinet/in_systm.h>
80250199Sgrehan#include <netinet/in.h>
81250199Sgrehan#include <netinet/ip.h>
82250199Sgrehan#include <netinet/if_ether.h>
83250199Sgrehan
84250199Sgrehan#include <vm/vm.h>
85250199Sgrehan#include <vm/vm_param.h>
86250199Sgrehan#include <vm/vm_kern.h>
87250199Sgrehan#include <vm/pmap.h>
88250199Sgrehan
89250199Sgrehan#include <machine/bus.h>
90250199Sgrehan#include <machine/resource.h>
91250199Sgrehan#include <machine/frame.h>
92250199Sgrehan#include <machine/vmparam.h>
93250199Sgrehan
94250199Sgrehan#include <sys/bus.h>
95250199Sgrehan#include <sys/rman.h>
96250199Sgrehan#include <sys/mutex.h>
97250199Sgrehan#include <sys/errno.h>
98250199Sgrehan#include <sys/types.h>
99250199Sgrehan#include <machine/atomic.h>
100250199Sgrehan
101250199Sgrehan#include <machine/intr_machdep.h>
102250199Sgrehan
103250199Sgrehan#include <dev/hyperv/include/hyperv.h>
104250199Sgrehan#include "hv_net_vsc.h"
105250199Sgrehan#include "hv_rndis.h"
106250199Sgrehan#include "hv_rndis_filter.h"
107250199Sgrehan
108250199Sgrehan
109250199Sgrehan/* Short for Hyper-V network interface */
110250199Sgrehan#define NETVSC_DEVNAME    "hn"
111250199Sgrehan
112250199Sgrehan/*
113250199Sgrehan * It looks like offset 0 of buf is reserved to hold the softc pointer.
114250199Sgrehan * The sc pointer evidently not needed, and is not presently populated.
115250199Sgrehan * The packet offset is where the netvsc_packet starts in the buffer.
116250199Sgrehan */
117250199Sgrehan#define HV_NV_SC_PTR_OFFSET_IN_BUF         0
118250199Sgrehan#define HV_NV_PACKET_OFFSET_IN_BUF         16
119250199Sgrehan
120250199Sgrehan
121250199Sgrehan/*
122250199Sgrehan * Data types
123250199Sgrehan */
124250199Sgrehan
125250199Sgrehanstruct hv_netvsc_driver_context {
126250199Sgrehan	uint32_t		drv_inited;
127250199Sgrehan};
128250199Sgrehan
129250199Sgrehan/*
130250199Sgrehan * Be aware that this sleepable mutex will exhibit WITNESS errors when
131250199Sgrehan * certain TCP and ARP code paths are taken.  This appears to be a
132250199Sgrehan * well-known condition, as all other drivers checked use a sleeping
133250199Sgrehan * mutex to protect their transmit paths.
134250199Sgrehan * Also Be aware that mutexes do not play well with semaphores, and there
135250199Sgrehan * is a conflicting semaphore in a certain channel code path.
136250199Sgrehan */
137250199Sgrehan#define NV_LOCK_INIT(_sc, _name) \
138250199Sgrehan	    mtx_init(&(_sc)->hn_lock, _name, MTX_NETWORK_LOCK, MTX_DEF)
139250199Sgrehan#define NV_LOCK(_sc)		mtx_lock(&(_sc)->hn_lock)
140250199Sgrehan#define NV_LOCK_ASSERT(_sc)	mtx_assert(&(_sc)->hn_lock, MA_OWNED)
141250199Sgrehan#define NV_UNLOCK(_sc)		mtx_unlock(&(_sc)->hn_lock)
142250199Sgrehan#define NV_LOCK_DESTROY(_sc)	mtx_destroy(&(_sc)->hn_lock)
143250199Sgrehan
144250199Sgrehan
145250199Sgrehan/*
146250199Sgrehan * Globals
147250199Sgrehan */
148250199Sgrehan
149250199Sgrehanint hv_promisc_mode = 0;    /* normal mode by default */
150250199Sgrehan
151250199Sgrehan/* The one and only one */
152250199Sgrehanstatic struct hv_netvsc_driver_context g_netvsc_drv;
153250199Sgrehan
154250199Sgrehan
155250199Sgrehan/*
156250199Sgrehan * Forward declarations
157250199Sgrehan */
158250199Sgrehanstatic void hn_stop(hn_softc_t *sc);
159250199Sgrehanstatic void hn_ifinit_locked(hn_softc_t *sc);
160250199Sgrehanstatic void hn_ifinit(void *xsc);
161250199Sgrehanstatic int  hn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data);
162250199Sgrehanstatic int  hn_start_locked(struct ifnet *ifp);
163250199Sgrehanstatic void hn_start(struct ifnet *ifp);
164250199Sgrehan
165250199Sgrehan
166250199Sgrehan/*
167250199Sgrehan * NetVsc driver initialization
168250199Sgrehan * Note:  Filter init is no longer required
169250199Sgrehan */
170250199Sgrehanstatic int
171250199Sgrehannetvsc_drv_init(void)
172250199Sgrehan{
173250199Sgrehan	return (0);
174250199Sgrehan}
175250199Sgrehan
176250199Sgrehan/*
177250199Sgrehan * NetVsc global initialization entry point
178250199Sgrehan */
179250199Sgrehanstatic void
180250199Sgrehannetvsc_init(void)
181250199Sgrehan{
182250199Sgrehan	printf("Netvsc initializing... ");
183250199Sgrehan
184250199Sgrehan	/*
185250199Sgrehan	 * XXXKYS: cleanup initialization
186250199Sgrehan	 */
187250199Sgrehan	if (!cold && !g_netvsc_drv.drv_inited) {
188250199Sgrehan		g_netvsc_drv.drv_inited = 1;
189250199Sgrehan		netvsc_drv_init();
190250199Sgrehan	} else {
191250199Sgrehan		printf("Already initialized!\n");
192250199Sgrehan	}
193250199Sgrehan}
194250199Sgrehan
195250199Sgrehan/* {F8615163-DF3E-46c5-913F-F2D2F965ED0E} */
196250199Sgrehanstatic const hv_guid g_net_vsc_device_type = {
197250199Sgrehan	.data = {0x63, 0x51, 0x61, 0xF8, 0x3E, 0xDF, 0xc5, 0x46,
198250199Sgrehan		0x91, 0x3F, 0xF2, 0xD2, 0xF9, 0x65, 0xED, 0x0E}
199250199Sgrehan};
200250199Sgrehan
201250199Sgrehan/*
202250199Sgrehan * Standard probe entry point.
203250199Sgrehan *
204250199Sgrehan */
205250199Sgrehanstatic int
206250199Sgrehannetvsc_probe(device_t dev)
207250199Sgrehan{
208250199Sgrehan	const char *p;
209250199Sgrehan
210250199Sgrehan	p = vmbus_get_type(dev);
211250199Sgrehan	if (!memcmp(p, &g_net_vsc_device_type.data, sizeof(hv_guid))) {
212250199Sgrehan		device_set_desc(dev, "Synthetic Network Interface");
213250199Sgrehan		printf("Netvsc probe... DONE \n");
214250199Sgrehan
215250199Sgrehan		return (0);
216250199Sgrehan	}
217250199Sgrehan
218250199Sgrehan	return (ENXIO);
219250199Sgrehan}
220250199Sgrehan
221250199Sgrehan/*
222250199Sgrehan * Standard attach entry point.
223250199Sgrehan *
224250199Sgrehan * Called when the driver is loaded.  It allocates needed resources,
225250199Sgrehan * and initializes the "hardware" and software.
226250199Sgrehan */
227250199Sgrehanstatic int
228250199Sgrehannetvsc_attach(device_t dev)
229250199Sgrehan{
230250199Sgrehan	struct hv_device *device_ctx = vmbus_get_devctx(dev);
231250199Sgrehan	netvsc_device_info device_info;
232250199Sgrehan	hn_softc_t *sc;
233250199Sgrehan	int unit = device_get_unit(dev);
234250199Sgrehan	struct ifnet *ifp;
235250199Sgrehan	int ret;
236250199Sgrehan
237250199Sgrehan	netvsc_init();
238250199Sgrehan
239250199Sgrehan	sc = device_get_softc(dev);
240250199Sgrehan	if (sc == NULL) {
241250199Sgrehan		return (ENOMEM);
242250199Sgrehan	}
243250199Sgrehan
244250199Sgrehan	bzero(sc, sizeof(hn_softc_t));
245250199Sgrehan	sc->hn_unit = unit;
246250199Sgrehan	sc->hn_dev = dev;
247250199Sgrehan
248250199Sgrehan	NV_LOCK_INIT(sc, "NetVSCLock");
249250199Sgrehan
250250199Sgrehan	sc->hn_dev_obj = device_ctx;
251250199Sgrehan
252250199Sgrehan	ifp = sc->hn_ifp = sc->arpcom.ac_ifp = if_alloc(IFT_ETHER);
253250199Sgrehan	ifp->if_softc = sc;
254250199Sgrehan
255250199Sgrehan	if_initname(ifp, device_get_name(dev), device_get_unit(dev));
256250199Sgrehan	ifp->if_dunit = unit;
257250199Sgrehan	ifp->if_dname = NETVSC_DEVNAME;
258250199Sgrehan
259250199Sgrehan	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
260250199Sgrehan	ifp->if_ioctl = hn_ioctl;
261250199Sgrehan	ifp->if_start = hn_start;
262250199Sgrehan	ifp->if_init = hn_ifinit;
263250199Sgrehan	/* needed by hv_rf_on_device_add() code */
264250199Sgrehan	ifp->if_mtu = ETHERMTU;
265250199Sgrehan	IFQ_SET_MAXLEN(&ifp->if_snd, 512);
266250199Sgrehan	ifp->if_snd.ifq_drv_maxlen = 511;
267250199Sgrehan	IFQ_SET_READY(&ifp->if_snd);
268250199Sgrehan
269250199Sgrehan	/*
270250199Sgrehan	 * Tell upper layers that we support full VLAN capability.
271250199Sgrehan	 */
272250199Sgrehan	ifp->if_data.ifi_hdrlen = sizeof(struct ether_vlan_header);
273250199Sgrehan	ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
274250199Sgrehan	ifp->if_capenable |= IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU;
275250199Sgrehan
276250199Sgrehan	ret = hv_rf_on_device_add(device_ctx, &device_info);
277250199Sgrehan	if (ret != 0) {
278250199Sgrehan		if_free(ifp);
279250199Sgrehan
280250199Sgrehan		return (ret);
281250199Sgrehan	}
282250199Sgrehan	if (device_info.link_state == 0) {
283250199Sgrehan		sc->hn_carrier = 1;
284250199Sgrehan	}
285250199Sgrehan
286250199Sgrehan	ether_ifattach(ifp, device_info.mac_addr);
287250199Sgrehan
288250199Sgrehan	return (0);
289250199Sgrehan}
290250199Sgrehan
291250199Sgrehan/*
292250199Sgrehan * Standard detach entry point
293250199Sgrehan */
294250199Sgrehanstatic int
295250199Sgrehannetvsc_detach(device_t dev)
296250199Sgrehan{
297250199Sgrehan	struct hv_device *hv_device = vmbus_get_devctx(dev);
298250199Sgrehan
299250199Sgrehan	printf("netvsc_detach\n");
300250199Sgrehan
301250199Sgrehan	/*
302250199Sgrehan	 * XXXKYS:  Need to clean up all our
303250199Sgrehan	 * driver state; this is the driver
304250199Sgrehan	 * unloading.
305250199Sgrehan	 */
306250199Sgrehan
307250199Sgrehan	/*
308250199Sgrehan	 * XXXKYS:  Need to stop outgoing traffic and unregister
309250199Sgrehan	 * the netdevice.
310250199Sgrehan	 */
311250199Sgrehan
312250199Sgrehan	hv_rf_on_device_remove(hv_device, HV_RF_NV_DESTROY_CHANNEL);
313250199Sgrehan
314250199Sgrehan	return (0);
315250199Sgrehan}
316250199Sgrehan
317250199Sgrehan/*
318250199Sgrehan * Standard shutdown entry point
319250199Sgrehan */
320250199Sgrehanstatic int
321250199Sgrehannetvsc_shutdown(device_t dev)
322250199Sgrehan{
323250199Sgrehan	return (0);
324250199Sgrehan}
325250199Sgrehan
326250199Sgrehan/*
327250199Sgrehan * Send completion processing
328250199Sgrehan *
329250199Sgrehan * Note:  It looks like offset 0 of buf is reserved to hold the softc
330250199Sgrehan * pointer.  The sc pointer is not currently needed in this function, and
331250199Sgrehan * it is not presently populated by the TX function.
332250199Sgrehan */
333250199Sgrehanvoid
334250199Sgrehannetvsc_xmit_completion(void *context)
335250199Sgrehan{
336250199Sgrehan	netvsc_packet *packet = (netvsc_packet *)context;
337250199Sgrehan	struct mbuf *mb;
338250199Sgrehan	uint8_t *buf;
339250199Sgrehan
340250199Sgrehan	mb = (struct mbuf *)packet->compl.send.send_completion_tid;
341250199Sgrehan	buf = ((uint8_t *)packet) - HV_NV_PACKET_OFFSET_IN_BUF;
342250199Sgrehan
343250199Sgrehan	free(buf, M_DEVBUF);
344250199Sgrehan
345250199Sgrehan	if (mb != NULL) {
346250199Sgrehan		m_freem(mb);
347250199Sgrehan	}
348250199Sgrehan}
349250199Sgrehan
350250199Sgrehan/*
351250199Sgrehan * Start a transmit of one or more packets
352250199Sgrehan */
353250199Sgrehanstatic int
354250199Sgrehanhn_start_locked(struct ifnet *ifp)
355250199Sgrehan{
356250199Sgrehan	hn_softc_t *sc = ifp->if_softc;
357250199Sgrehan	struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev);
358250199Sgrehan	uint8_t *buf;
359250199Sgrehan	netvsc_packet *packet;
360250199Sgrehan	struct mbuf *m_head, *m;
361250199Sgrehan	struct mbuf *mc_head = NULL;
362250199Sgrehan	int i;
363250199Sgrehan	int num_frags;
364250199Sgrehan	int len;
365250199Sgrehan	int xlen;
366250199Sgrehan	int rppi_size;
367250199Sgrehan	int retries = 0;
368250199Sgrehan	int ret = 0;
369250199Sgrehan
370250199Sgrehan	while (!IFQ_DRV_IS_EMPTY(&sc->hn_ifp->if_snd)) {
371250199Sgrehan		IFQ_DRV_DEQUEUE(&sc->hn_ifp->if_snd, m_head);
372250199Sgrehan		if (m_head == NULL) {
373250199Sgrehan			break;
374250199Sgrehan		}
375250199Sgrehan
376250199Sgrehan		len = 0;
377250199Sgrehan		num_frags = 0;
378250199Sgrehan		xlen = 0;
379250199Sgrehan
380250199Sgrehan		/* Walk the mbuf list computing total length and num frags */
381250199Sgrehan		for (m = m_head; m != NULL; m = m->m_next) {
382250199Sgrehan			if (m->m_len != 0) {
383250199Sgrehan				num_frags++;
384250199Sgrehan				len += m->m_len;
385250199Sgrehan			}
386250199Sgrehan		}
387250199Sgrehan
388250199Sgrehan		/*
389250199Sgrehan		 * Reserve the number of pages requested.  Currently,
390250199Sgrehan		 * one page is reserved for the message in the RNDIS
391250199Sgrehan		 * filter packet
392250199Sgrehan		 */
393250199Sgrehan		num_frags += HV_RF_NUM_TX_RESERVED_PAGE_BUFS;
394250199Sgrehan
395250199Sgrehan		/* If exceeds # page_buffers in netvsc_packet */
396250199Sgrehan		if (num_frags > NETVSC_PACKET_MAXPAGE) {
397250199Sgrehan			m_freem(m);
398250199Sgrehan
399250199Sgrehan			return (EINVAL);
400250199Sgrehan		}
401250199Sgrehan
402250199Sgrehan		rppi_size = 0;
403250199Sgrehan		if (m_head->m_flags & M_VLANTAG) {
404250199Sgrehan			rppi_size = sizeof(rndis_per_packet_info) +
405250199Sgrehan			    sizeof(ndis_8021q_info);
406250199Sgrehan		}
407250199Sgrehan
408250199Sgrehan		/*
409250199Sgrehan		 * Allocate a buffer with space for a netvsc packet plus a
410250199Sgrehan		 * number of reserved areas.  First comes a (currently 16
411250199Sgrehan		 * bytes, currently unused) reserved data area.  Second is
412250199Sgrehan		 * the netvsc_packet, which includes (currently 4) page
413250199Sgrehan		 * buffers.  Third (optional) is a rndis_per_packet_info
414250199Sgrehan		 * struct, but only if a VLAN tag should be inserted into the
415250199Sgrehan		 * Ethernet frame by the Hyper-V infrastructure.  Fourth is
416250199Sgrehan		 * an area reserved for an rndis_filter_packet struct.
417250199Sgrehan		 * Changed malloc to M_NOWAIT to avoid sleep under spin lock.
418250199Sgrehan		 * No longer reserving extra space for page buffers, as they
419250199Sgrehan		 * are already part of the netvsc_packet.
420250199Sgrehan		 */
421250199Sgrehan		buf = malloc(HV_NV_PACKET_OFFSET_IN_BUF +
422250199Sgrehan		    sizeof(netvsc_packet) + rppi_size +
423250199Sgrehan		    sizeof(rndis_filter_packet),
424250199Sgrehan		    M_DEVBUF, M_ZERO | M_NOWAIT);
425250199Sgrehan		if (buf == NULL) {
426250199Sgrehan			m_freem(m);
427250199Sgrehan
428250199Sgrehan			return (ENOMEM);
429250199Sgrehan		}
430250199Sgrehan
431250199Sgrehan		packet = (netvsc_packet *)(buf + HV_NV_PACKET_OFFSET_IN_BUF);
432250199Sgrehan		*(vm_offset_t *)buf = HV_NV_SC_PTR_OFFSET_IN_BUF;
433250199Sgrehan
434250199Sgrehan		/*
435250199Sgrehan		 * extension points to the area reserved for the
436250199Sgrehan		 * rndis_filter_packet, which is placed just after
437250199Sgrehan		 * the netvsc_packet (and rppi struct, if present;
438250199Sgrehan		 * length is updated later).
439250199Sgrehan		 */
440250199Sgrehan		packet->extension = packet + 1;
441250199Sgrehan
442250199Sgrehan		/* Set up the rndis header */
443250199Sgrehan		packet->page_buf_count = num_frags;
444250199Sgrehan
445250199Sgrehan		/* Initialize it from the mbuf */
446250199Sgrehan		packet->tot_data_buf_len = len;
447250199Sgrehan
448250199Sgrehan		/*
449250199Sgrehan		 * If the Hyper-V infrastructure needs to embed a VLAN tag,
450250199Sgrehan		 * initialize netvsc_packet and rppi struct values as needed.
451250199Sgrehan		 */
452250199Sgrehan		if (rppi_size) {
453250199Sgrehan			/* Lower layers need the VLAN TCI */
454250199Sgrehan			packet->vlan_tci = m_head->m_pkthdr.ether_vtag;
455250199Sgrehan		}
456250199Sgrehan
457250199Sgrehan		/*
458250199Sgrehan		 * Fill the page buffers with mbuf info starting at index
459250199Sgrehan		 * HV_RF_NUM_TX_RESERVED_PAGE_BUFS.
460250199Sgrehan		 */
461250199Sgrehan		i = HV_RF_NUM_TX_RESERVED_PAGE_BUFS;
462250199Sgrehan		for (m = m_head; m != NULL; m = m->m_next) {
463250199Sgrehan			if (m->m_len) {
464250199Sgrehan				vm_offset_t paddr =
465250199Sgrehan				    vtophys(mtod(m, vm_offset_t));
466250199Sgrehan				packet->page_buffers[i].pfn =
467250199Sgrehan				    paddr >> PAGE_SHIFT;
468250199Sgrehan				packet->page_buffers[i].offset =
469250199Sgrehan				    paddr & (PAGE_SIZE - 1);
470250199Sgrehan				packet->page_buffers[i].length = m->m_len;
471250199Sgrehan				i++;
472250199Sgrehan			}
473250199Sgrehan		}
474250199Sgrehan
475250199Sgrehan		/*
476250199Sgrehan		 * If bpf, copy the mbuf chain.  This is less expensive than
477250199Sgrehan		 * it appears; the mbuf clusters are not copied, only their
478250199Sgrehan		 * reference counts are incremented.
479250199Sgrehan		 * Needed to avoid a race condition where the completion
480250199Sgrehan		 * callback is invoked, freeing the mbuf chain, before the
481250199Sgrehan		 * bpf_mtap code has a chance to run.
482250199Sgrehan		 */
483250199Sgrehan		if (ifp->if_bpf) {
484250199Sgrehan			mc_head = m_copypacket(m_head, M_DONTWAIT);
485250199Sgrehan		}
486250199Sgrehanretry_send:
487250199Sgrehan		/* Set the completion routine */
488250199Sgrehan		packet->compl.send.on_send_completion = netvsc_xmit_completion;
489250199Sgrehan		packet->compl.send.send_completion_context = packet;
490250199Sgrehan		packet->compl.send.send_completion_tid = (uint64_t)m_head;
491250199Sgrehan
492250199Sgrehan		/* Removed critical_enter(), does not appear necessary */
493250199Sgrehan		ret = hv_rf_on_send(device_ctx, packet);
494250199Sgrehan
495250199Sgrehan		if (ret == 0) {
496250199Sgrehan			ifp->if_opackets++;
497250199Sgrehan			/* if bpf && mc_head, call bpf_mtap code */
498250199Sgrehan			if (mc_head) {
499250199Sgrehan				ETHER_BPF_MTAP(ifp, mc_head);
500250199Sgrehan			}
501250199Sgrehan		} else {
502250199Sgrehan			retries++;
503250199Sgrehan			if (retries < 4) {
504250199Sgrehan				goto retry_send;
505250199Sgrehan			}
506250199Sgrehan
507250199Sgrehan			IF_PREPEND(&ifp->if_snd, m_head);
508250199Sgrehan			ifp->if_drv_flags |= IFF_DRV_OACTIVE;
509250199Sgrehan
510250199Sgrehan			/*
511250199Sgrehan			 * Null the mbuf pointer so the completion function
512250199Sgrehan			 * does not free the mbuf chain.  We just pushed the
513250199Sgrehan			 * mbuf chain back on the if_snd queue.
514250199Sgrehan			 */
515250199Sgrehan			packet->compl.send.send_completion_tid = 0;
516250199Sgrehan
517250199Sgrehan			/*
518250199Sgrehan			 * Release the resources since we will not get any
519250199Sgrehan			 * send completion
520250199Sgrehan			 */
521250199Sgrehan			netvsc_xmit_completion(packet);
522250199Sgrehan		}
523250199Sgrehan
524250199Sgrehan		/* if bpf && mc_head, free the mbuf chain copy */
525250199Sgrehan		if (mc_head) {
526250199Sgrehan			m_freem(mc_head);
527250199Sgrehan		}
528250199Sgrehan	}
529250199Sgrehan
530250199Sgrehan	return (ret);
531250199Sgrehan}
532250199Sgrehan
533250199Sgrehan/*
534250199Sgrehan * Link up/down notification
535250199Sgrehan */
536250199Sgrehanvoid
537250199Sgrehannetvsc_linkstatus_callback(struct hv_device *device_obj, uint32_t status)
538250199Sgrehan{
539250199Sgrehan	hn_softc_t *sc = device_get_softc(device_obj->device);
540250199Sgrehan
541250199Sgrehan	if (sc == NULL) {
542250199Sgrehan		return;
543250199Sgrehan	}
544250199Sgrehan
545250199Sgrehan	if (status == 1) {
546250199Sgrehan		sc->hn_carrier = 1;
547250199Sgrehan	} else {
548250199Sgrehan		sc->hn_carrier = 0;
549250199Sgrehan	}
550250199Sgrehan}
551250199Sgrehan
552250199Sgrehan/*
553250199Sgrehan * Append the specified data to the indicated mbuf chain,
554250199Sgrehan * Extend the mbuf chain if the new data does not fit in
555250199Sgrehan * existing space.
556250199Sgrehan *
557250199Sgrehan * This is a minor rewrite of m_append() from sys/kern/uipc_mbuf.c.
558250199Sgrehan * There should be an equivalent in the kernel mbuf code,
559250199Sgrehan * but there does not appear to be one yet.
560250199Sgrehan *
561250199Sgrehan * Differs from m_append() in that additional mbufs are
562250199Sgrehan * allocated with cluster size MJUMPAGESIZE, and filled
563250199Sgrehan * accordingly.
564250199Sgrehan *
565250199Sgrehan * Return 1 if able to complete the job; otherwise 0.
566250199Sgrehan */
567250199Sgrehanstatic int
568250199Sgrehanhv_m_append(struct mbuf *m0, int len, c_caddr_t cp)
569250199Sgrehan{
570250199Sgrehan	struct mbuf *m, *n;
571250199Sgrehan	int remainder, space;
572250199Sgrehan
573250199Sgrehan	for (m = m0; m->m_next != NULL; m = m->m_next)
574250199Sgrehan		;
575250199Sgrehan	remainder = len;
576250199Sgrehan	space = M_TRAILINGSPACE(m);
577250199Sgrehan	if (space > 0) {
578250199Sgrehan		/*
579250199Sgrehan		 * Copy into available space.
580250199Sgrehan		 */
581250199Sgrehan		if (space > remainder)
582250199Sgrehan			space = remainder;
583250199Sgrehan		bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
584250199Sgrehan		m->m_len += space;
585250199Sgrehan		cp += space;
586250199Sgrehan		remainder -= space;
587250199Sgrehan	}
588250199Sgrehan	while (remainder > 0) {
589250199Sgrehan		/*
590250199Sgrehan		 * Allocate a new mbuf; could check space
591250199Sgrehan		 * and allocate a cluster instead.
592250199Sgrehan		 */
593250199Sgrehan		n = m_getjcl(M_DONTWAIT, m->m_type, 0, MJUMPAGESIZE);
594250199Sgrehan		if (n == NULL)
595250199Sgrehan			break;
596250199Sgrehan		n->m_len = min(MJUMPAGESIZE, remainder);
597250199Sgrehan		bcopy(cp, mtod(n, caddr_t), n->m_len);
598250199Sgrehan		cp += n->m_len;
599250199Sgrehan		remainder -= n->m_len;
600250199Sgrehan		m->m_next = n;
601250199Sgrehan		m = n;
602250199Sgrehan	}
603250199Sgrehan	if (m0->m_flags & M_PKTHDR)
604250199Sgrehan		m0->m_pkthdr.len += len - remainder;
605250199Sgrehan
606250199Sgrehan	return (remainder == 0);
607250199Sgrehan}
608250199Sgrehan
609250199Sgrehan
610250199Sgrehan/*
611250199Sgrehan * Called when we receive a data packet from the "wire" on the
612250199Sgrehan * specified device
613250199Sgrehan *
614250199Sgrehan * Note:  This is no longer used as a callback
615250199Sgrehan */
616250199Sgrehanint
617250199Sgrehannetvsc_recv(struct hv_device *device_ctx, netvsc_packet *packet)
618250199Sgrehan{
619250199Sgrehan	hn_softc_t *sc = (hn_softc_t *)device_get_softc(device_ctx->device);
620250199Sgrehan	struct mbuf *m_new;
621250199Sgrehan	struct ifnet *ifp = sc->hn_ifp;
622250199Sgrehan	int size;
623250199Sgrehan	int i;
624250199Sgrehan
625250199Sgrehan	if (sc == NULL) {
626250199Sgrehan		return (0); /* TODO: KYS how can this be! */
627250199Sgrehan	}
628250199Sgrehan
629250199Sgrehan	ifp = sc->arpcom.ac_ifp;
630250199Sgrehan
631250199Sgrehan	if (!(ifp->if_drv_flags & IFF_DRV_RUNNING)) {
632250199Sgrehan		return (0);
633250199Sgrehan	}
634250199Sgrehan
635250199Sgrehan	/*
636250199Sgrehan	 * Bail out if packet contains more data than configured MTU.
637250199Sgrehan	 */
638250199Sgrehan	if (packet->tot_data_buf_len > (ifp->if_mtu + ETHER_HDR_LEN)) {
639250199Sgrehan		return (0);
640250199Sgrehan	}
641250199Sgrehan
642250199Sgrehan	/*
643250199Sgrehan	 * Get an mbuf with a cluster.  For packets 2K or less,
644250199Sgrehan	 * get a standard 2K cluster.  For anything larger, get a
645250199Sgrehan	 * 4K cluster.  Any buffers larger than 4K can cause problems
646250199Sgrehan	 * if looped around to the Hyper-V TX channel, so avoid them.
647250199Sgrehan	 */
648250199Sgrehan	size = MCLBYTES;
649250199Sgrehan
650250199Sgrehan	if (packet->tot_data_buf_len > MCLBYTES) {
651250199Sgrehan		/* 4096 */
652250199Sgrehan		size = MJUMPAGESIZE;
653250199Sgrehan	}
654250199Sgrehan
655250199Sgrehan	m_new = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, size);
656250199Sgrehan
657250199Sgrehan	if (m_new == NULL)
658250199Sgrehan		return (0);
659250199Sgrehan
660250199Sgrehan	/*
661250199Sgrehan	 * Remove trailing junk from RX data buffer.
662250199Sgrehan	 * Fixme:  This will not work for multiple Hyper-V RX buffers.
663250199Sgrehan	 * Fortunately, the channel gathers all RX data into one buffer.
664250199Sgrehan	 *
665250199Sgrehan	 * L2 frame length, with L2 header, not including CRC
666250199Sgrehan	 */
667250199Sgrehan	packet->page_buffers[0].length = packet->tot_data_buf_len;
668250199Sgrehan
669250199Sgrehan	/*
670250199Sgrehan	 * Copy the received packet to one or more mbufs.
671250199Sgrehan	 * The copy is required since the memory pointed to by netvsc_packet
672250199Sgrehan	 * cannot be deallocated
673250199Sgrehan	 */
674250199Sgrehan	for (i=0; i < packet->page_buf_count; i++) {
675250199Sgrehan		/* Shift virtual page number to form virtual page address */
676250199Sgrehan		uint8_t *vaddr = (uint8_t *)
677250199Sgrehan		    (packet->page_buffers[i].pfn << PAGE_SHIFT);
678250199Sgrehan
679250199Sgrehan		hv_m_append(m_new, packet->page_buffers[i].length,
680250199Sgrehan		    vaddr + packet->page_buffers[i].offset);
681250199Sgrehan	}
682250199Sgrehan
683250199Sgrehan	m_new->m_pkthdr.rcvif = ifp;
684250199Sgrehan
685250199Sgrehan	if ((packet->vlan_tci != 0) &&
686250199Sgrehan			    (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0) {
687250199Sgrehan		m_new->m_pkthdr.ether_vtag = packet->vlan_tci;
688250199Sgrehan		m_new->m_flags |= M_VLANTAG;
689250199Sgrehan	}
690250199Sgrehan
691250199Sgrehan	/*
692250199Sgrehan	 * Note:  Moved RX completion back to hv_nv_on_receive() so all
693250199Sgrehan	 * messages (not just data messages) will trigger a response.
694250199Sgrehan	 */
695250199Sgrehan
696250199Sgrehan	ifp->if_ipackets++;
697250199Sgrehan
698250199Sgrehan	/* We're not holding the lock here, so don't release it */
699250199Sgrehan	(*ifp->if_input)(ifp, m_new);
700250199Sgrehan
701250199Sgrehan	return (0);
702250199Sgrehan}
703250199Sgrehan
704250199Sgrehan/*
705250199Sgrehan * Standard ioctl entry point.  Called when the user wants to configure
706250199Sgrehan * the interface.
707250199Sgrehan */
708250199Sgrehanstatic int
709250199Sgrehanhn_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
710250199Sgrehan{
711250199Sgrehan	hn_softc_t *sc = ifp->if_softc;
712250199Sgrehan	struct ifreq *ifr = (struct ifreq *)data;
713250199Sgrehan	netvsc_device_info device_info;
714250199Sgrehan	struct hv_device *hn_dev;
715250199Sgrehan	int mask, error = 0;
716250199Sgrehan
717250199Sgrehan	switch(cmd) {
718250199Sgrehan
719250199Sgrehan	case SIOCSIFADDR:
720250199Sgrehan	case SIOCGIFADDR:
721250199Sgrehan		error = ether_ioctl(ifp, cmd, data);
722250199Sgrehan		break;
723250199Sgrehan	case SIOCSIFMTU:
724250199Sgrehan		hn_dev = vmbus_get_devctx(sc->hn_dev);
725250199Sgrehan
726250199Sgrehan		NV_LOCK(sc);
727250199Sgrehan
728250199Sgrehan		if (ifr->ifr_mtu > NETVSC_MAX_CONFIGURABLE_MTU) {
729250199Sgrehan			error = EINVAL;
730250199Sgrehan			NV_UNLOCK(sc);
731250199Sgrehan			break;
732250199Sgrehan		}
733250199Sgrehan		/* Obtain and record requested MTU */
734250199Sgrehan		ifp->if_mtu = ifr->ifr_mtu;
735250199Sgrehan
736250199Sgrehan		/*
737250199Sgrehan		 * We must remove and add back the device to cause the new
738250199Sgrehan		 * MTU to take effect.  This includes tearing down, but not
739250199Sgrehan		 * deleting the channel, then bringing it back up.
740250199Sgrehan		 */
741250199Sgrehan		error = hv_rf_on_device_remove(hn_dev, HV_RF_NV_RETAIN_CHANNEL);
742250199Sgrehan		if (error) {
743250199Sgrehan			NV_UNLOCK(sc);
744250199Sgrehan			break;
745250199Sgrehan		}
746250199Sgrehan		error = hv_rf_on_device_add(hn_dev, &device_info);
747250199Sgrehan		if (error) {
748250199Sgrehan			NV_UNLOCK(sc);
749250199Sgrehan			break;
750250199Sgrehan		}
751250199Sgrehan
752250199Sgrehan		hn_ifinit_locked(sc);
753250199Sgrehan
754250199Sgrehan		NV_UNLOCK(sc);
755250199Sgrehan		break;
756250199Sgrehan	case SIOCSIFFLAGS:
757250199Sgrehan		NV_LOCK(sc);
758250199Sgrehan		if (ifp->if_flags & IFF_UP) {
759250199Sgrehan			/*
760250199Sgrehan			 * If only the state of the PROMISC flag changed,
761250199Sgrehan			 * then just use the 'set promisc mode' command
762250199Sgrehan			 * instead of reinitializing the entire NIC. Doing
763250199Sgrehan			 * a full re-init means reloading the firmware and
764250199Sgrehan			 * waiting for it to start up, which may take a
765250199Sgrehan			 * second or two.
766250199Sgrehan			 */
767250199Sgrehan#ifdef notyet
768250199Sgrehan			/* Fixme:  Promiscuous mode? */
769250199Sgrehan			/* No promiscuous mode with Xen */
770250199Sgrehan			if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
771250199Sgrehan			    ifp->if_flags & IFF_PROMISC &&
772250199Sgrehan			    !(sc->hn_if_flags & IFF_PROMISC)) {
773250199Sgrehan				/* do something here for Hyper-V */
774250199Sgrehan				;
775250199Sgrehan/*				XN_SETBIT(sc, XN_RX_MODE,		*/
776250199Sgrehan/*					  XN_RXMODE_RX_PROMISC);	*/
777250199Sgrehan			} else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
778250199Sgrehan				   !(ifp->if_flags & IFF_PROMISC) &&
779250199Sgrehan				   sc->hn_if_flags & IFF_PROMISC) {
780250199Sgrehan				/* do something here for Hyper-V */
781250199Sgrehan				;
782250199Sgrehan/*				XN_CLRBIT(sc, XN_RX_MODE,		*/
783250199Sgrehan/*					  XN_RXMODE_RX_PROMISC);	*/
784250199Sgrehan			} else
785250199Sgrehan#endif
786250199Sgrehan				hn_ifinit_locked(sc);
787250199Sgrehan		} else {
788250199Sgrehan			if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
789250199Sgrehan				hn_stop(sc);
790250199Sgrehan			}
791250199Sgrehan		}
792250199Sgrehan		sc->hn_if_flags = ifp->if_flags;
793250199Sgrehan		NV_UNLOCK(sc);
794250199Sgrehan		error = 0;
795250199Sgrehan		break;
796250199Sgrehan	case SIOCSIFCAP:
797250199Sgrehan		mask = ifr->ifr_reqcap ^ ifp->if_capenable;
798250199Sgrehan		if (mask & IFCAP_HWCSUM) {
799250199Sgrehan			if (IFCAP_HWCSUM & ifp->if_capenable) {
800250199Sgrehan				ifp->if_capenable &= ~IFCAP_HWCSUM;
801250199Sgrehan			} else {
802250199Sgrehan				ifp->if_capenable |= IFCAP_HWCSUM;
803250199Sgrehan			}
804250199Sgrehan		}
805250199Sgrehan		error = 0;
806250199Sgrehan		break;
807250199Sgrehan	case SIOCADDMULTI:
808250199Sgrehan	case SIOCDELMULTI:
809250199Sgrehan#ifdef notyet
810250199Sgrehan		/* Fixme:  Multicast mode? */
811250199Sgrehan		if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
812250199Sgrehan			NV_LOCK(sc);
813250199Sgrehan			netvsc_setmulti(sc);
814250199Sgrehan			NV_UNLOCK(sc);
815250199Sgrehan			error = 0;
816250199Sgrehan		}
817250199Sgrehan#endif
818250199Sgrehan		/* FALLTHROUGH */
819250199Sgrehan	case SIOCSIFMEDIA:
820250199Sgrehan	case SIOCGIFMEDIA:
821250199Sgrehan		error = EINVAL;
822250199Sgrehan		break;
823250199Sgrehan	default:
824250199Sgrehan		error = ether_ioctl(ifp, cmd, data);
825250199Sgrehan		break;
826250199Sgrehan	}
827250199Sgrehan
828250199Sgrehan	return (error);
829250199Sgrehan}
830250199Sgrehan
831250199Sgrehan/*
832250199Sgrehan *
833250199Sgrehan */
834250199Sgrehanstatic void
835250199Sgrehanhn_stop(hn_softc_t *sc)
836250199Sgrehan{
837250199Sgrehan	struct ifnet *ifp;
838250199Sgrehan	int ret;
839250199Sgrehan	struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev);
840250199Sgrehan
841250199Sgrehan	NV_LOCK_ASSERT(sc);
842250199Sgrehan	ifp = sc->hn_ifp;
843250199Sgrehan
844250199Sgrehan	printf(" Closing Device ...\n");
845250199Sgrehan
846250199Sgrehan	ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
847250199Sgrehan	sc->hn_initdone = 0;
848250199Sgrehan
849250199Sgrehan	ret = hv_rf_on_close(device_ctx);
850250199Sgrehan}
851250199Sgrehan
852250199Sgrehan/*
853250199Sgrehan * FreeBSD transmit entry point
854250199Sgrehan */
855250199Sgrehanstatic void
856250199Sgrehanhn_start(struct ifnet *ifp)
857250199Sgrehan{
858250199Sgrehan	hn_softc_t *sc;
859250199Sgrehan
860250199Sgrehan	sc = ifp->if_softc;
861250199Sgrehan	NV_LOCK(sc);
862250199Sgrehan	hn_start_locked(ifp);
863250199Sgrehan	NV_UNLOCK(sc);
864250199Sgrehan}
865250199Sgrehan
866250199Sgrehan/*
867250199Sgrehan *
868250199Sgrehan */
869250199Sgrehanstatic void
870250199Sgrehanhn_ifinit_locked(hn_softc_t *sc)
871250199Sgrehan{
872250199Sgrehan	struct ifnet *ifp;
873250199Sgrehan	struct hv_device *device_ctx = vmbus_get_devctx(sc->hn_dev);
874250199Sgrehan	int ret;
875250199Sgrehan
876250199Sgrehan	NV_LOCK_ASSERT(sc);
877250199Sgrehan
878250199Sgrehan	ifp = sc->hn_ifp;
879250199Sgrehan
880250199Sgrehan	if (ifp->if_drv_flags & IFF_DRV_RUNNING) {
881250199Sgrehan		return;
882250199Sgrehan	}
883250199Sgrehan
884250199Sgrehan	hv_promisc_mode = 1;
885250199Sgrehan
886250199Sgrehan	ret = hv_rf_on_open(device_ctx);
887250199Sgrehan	if (ret != 0) {
888250199Sgrehan		return;
889250199Sgrehan	} else {
890250199Sgrehan		sc->hn_initdone = 1;
891250199Sgrehan	}
892250199Sgrehan	ifp->if_drv_flags |= IFF_DRV_RUNNING;
893250199Sgrehan	ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
894250199Sgrehan}
895250199Sgrehan
896250199Sgrehan/*
897250199Sgrehan *
898250199Sgrehan */
899250199Sgrehanstatic void
900250199Sgrehanhn_ifinit(void *xsc)
901250199Sgrehan{
902250199Sgrehan	hn_softc_t *sc = xsc;
903250199Sgrehan
904250199Sgrehan	NV_LOCK(sc);
905250199Sgrehan	hn_ifinit_locked(sc);
906250199Sgrehan	NV_UNLOCK(sc);
907250199Sgrehan}
908250199Sgrehan
909250199Sgrehan#ifdef LATER
910250199Sgrehan/*
911250199Sgrehan *
912250199Sgrehan */
913250199Sgrehanstatic void
914250199Sgrehanhn_watchdog(struct ifnet *ifp)
915250199Sgrehan{
916250199Sgrehan	hn_softc_t *sc;
917250199Sgrehan	sc = ifp->if_softc;
918250199Sgrehan
919250199Sgrehan	printf("hn%d: watchdog timeout -- resetting\n", sc->hn_unit);
920250199Sgrehan	hn_ifinit(sc);    /*???*/
921250199Sgrehan	ifp->if_oerrors++;
922250199Sgrehan}
923250199Sgrehan#endif
924250199Sgrehan
925250199Sgrehanstatic device_method_t netvsc_methods[] = {
926250199Sgrehan        /* Device interface */
927250199Sgrehan        DEVMETHOD(device_probe,         netvsc_probe),
928250199Sgrehan        DEVMETHOD(device_attach,        netvsc_attach),
929250199Sgrehan        DEVMETHOD(device_detach,        netvsc_detach),
930250199Sgrehan        DEVMETHOD(device_shutdown,      netvsc_shutdown),
931250199Sgrehan
932250199Sgrehan        { 0, 0 }
933250199Sgrehan};
934250199Sgrehan
935250199Sgrehanstatic driver_t netvsc_driver = {
936250199Sgrehan        NETVSC_DEVNAME,
937250199Sgrehan        netvsc_methods,
938250199Sgrehan        sizeof(hn_softc_t)
939250199Sgrehan};
940250199Sgrehan
941250199Sgrehanstatic devclass_t netvsc_devclass;
942250199Sgrehan
943250199SgrehanDRIVER_MODULE(hn, vmbus, netvsc_driver, netvsc_devclass, 0, 0);
944250199SgrehanMODULE_VERSION(hn, 1);
945250199SgrehanMODULE_DEPEND(hn, vmbus, 1, 1, 1);
946253869SgrehanSYSINIT(netvsc_initx, SI_SUB_KTHREAD_IDLE, SI_ORDER_MIDDLE + 1, netvsc_init,
947250199Sgrehan     NULL);
948250199Sgrehan
949