xref: /freebsd/sys/dev/hyperv/netvsc/hn_rndis.c (revision d5b0e70f7e04d971691517ce1304d86a1e367e2e)
1 /*-
2  * Copyright (c) 2009-2012,2016-2017 Microsoft Corp.
3  * Copyright (c) 2010-2012 Citrix Inc.
4  * Copyright (c) 2012 NetApp Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31 
32 #include "opt_inet6.h"
33 #include "opt_inet.h"
34 
35 #include <sys/param.h>
36 #include <sys/socket.h>
37 #include <sys/systm.h>
38 #include <sys/taskqueue.h>
39 
40 #include <machine/atomic.h>
41 
42 #include <net/ethernet.h>
43 #include <net/if.h>
44 #include <net/if_var.h>
45 #include <net/if_media.h>
46 #include <net/rndis.h>
47 
48 #include <netinet/in.h>
49 #include <netinet/ip.h>
50 #include <netinet/tcp_lro.h>
51 
52 #include <dev/hyperv/include/hyperv.h>
53 #include <dev/hyperv/include/hyperv_busdma.h>
54 #include <dev/hyperv/include/vmbus.h>
55 #include <dev/hyperv/include/vmbus_xact.h>
56 
57 #include <dev/hyperv/netvsc/ndis.h>
58 #include <dev/hyperv/netvsc/if_hnreg.h>
59 #include <dev/hyperv/netvsc/if_hnvar.h>
60 #include <dev/hyperv/netvsc/hn_nvs.h>
61 #include <dev/hyperv/netvsc/hn_rndis.h>
62 
63 #define HN_RNDIS_RID_COMPAT_MASK	0xffff
64 #define HN_RNDIS_RID_COMPAT_MAX		HN_RNDIS_RID_COMPAT_MASK
65 
66 #define HN_RNDIS_XFER_SIZE		2048
67 
68 #define HN_NDIS_TXCSUM_CAP_IP4		\
69 	(NDIS_TXCSUM_CAP_IP4 | NDIS_TXCSUM_CAP_IP4OPT)
70 #define HN_NDIS_TXCSUM_CAP_TCP4		\
71 	(NDIS_TXCSUM_CAP_TCP4 | NDIS_TXCSUM_CAP_TCP4OPT)
72 #define HN_NDIS_TXCSUM_CAP_TCP6		\
73 	(NDIS_TXCSUM_CAP_TCP6 | NDIS_TXCSUM_CAP_TCP6OPT | \
74 	 NDIS_TXCSUM_CAP_IP6EXT)
75 #define HN_NDIS_TXCSUM_CAP_UDP6		\
76 	(NDIS_TXCSUM_CAP_UDP6 | NDIS_TXCSUM_CAP_IP6EXT)
77 #define HN_NDIS_LSOV2_CAP_IP6		\
78 	(NDIS_LSOV2_CAP_IP6EXT | NDIS_LSOV2_CAP_TCP6OPT)
79 
80 static const void	*hn_rndis_xact_exec1(struct hn_softc *,
81 			    struct vmbus_xact *, size_t,
82 			    struct hn_nvs_sendctx *, size_t *);
83 static const void	*hn_rndis_xact_execute(struct hn_softc *,
84 			    struct vmbus_xact *, uint32_t, size_t, size_t *,
85 			    uint32_t);
86 static int		hn_rndis_query(struct hn_softc *, uint32_t,
87 			    const void *, size_t, void *, size_t *);
88 static int		hn_rndis_query2(struct hn_softc *, uint32_t,
89 			    const void *, size_t, void *, size_t *, size_t);
90 static int		hn_rndis_set(struct hn_softc *, uint32_t,
91 			    const void *, size_t);
92 static int		hn_rndis_init(struct hn_softc *);
93 static int		hn_rndis_halt(struct hn_softc *);
94 static int		hn_rndis_conf_offload(struct hn_softc *, int);
95 static int		hn_rndis_query_hwcaps(struct hn_softc *,
96 			    struct ndis_offload *);
97 
98 static __inline uint32_t
99 hn_rndis_rid(struct hn_softc *sc)
100 {
101 	uint32_t rid;
102 
103 again:
104 	rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1);
105 	if (rid == 0)
106 		goto again;
107 
108 	/* Use upper 16 bits for non-compat RNDIS messages. */
109 	return ((rid & 0xffff) << 16);
110 }
111 
112 void
113 hn_rndis_rx_ctrl(struct hn_softc *sc, const void *data, int dlen)
114 {
115 	const struct rndis_comp_hdr *comp;
116 	const struct rndis_msghdr *hdr;
117 
118 	KASSERT(dlen >= sizeof(*hdr), ("invalid RNDIS msg\n"));
119 	hdr = data;
120 
121 	switch (hdr->rm_type) {
122 	case REMOTE_NDIS_INITIALIZE_CMPLT:
123 	case REMOTE_NDIS_QUERY_CMPLT:
124 	case REMOTE_NDIS_SET_CMPLT:
125 	case REMOTE_NDIS_KEEPALIVE_CMPLT:	/* unused */
126 		if (dlen < sizeof(*comp)) {
127 			if_printf(sc->hn_ifp, "invalid RNDIS cmplt\n");
128 			return;
129 		}
130 		comp = data;
131 
132 		KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX,
133 		    ("invalid RNDIS rid 0x%08x\n", comp->rm_rid));
134 		vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen);
135 		break;
136 
137 	case REMOTE_NDIS_RESET_CMPLT:
138 		/*
139 		 * Reset completed, no rid.
140 		 *
141 		 * NOTE:
142 		 * RESET is not issued by hn(4), so this message should
143 		 * _not_ be observed.
144 		 */
145 		if_printf(sc->hn_ifp, "RESET cmplt received\n");
146 		break;
147 
148 	default:
149 		if_printf(sc->hn_ifp, "unknown RNDIS msg 0x%x\n",
150 		    hdr->rm_type);
151 		break;
152 	}
153 }
154 
155 int
156 hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr)
157 {
158 	size_t eaddr_len;
159 	int error;
160 
161 	eaddr_len = ETHER_ADDR_LEN;
162 	error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0,
163 	    eaddr, &eaddr_len);
164 	if (error)
165 		return (error);
166 	if (eaddr_len != ETHER_ADDR_LEN) {
167 		if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len);
168 		return (EINVAL);
169 	}
170 	return (0);
171 }
172 
173 int
174 hn_rndis_get_linkstatus(struct hn_softc *sc, uint32_t *link_status)
175 {
176 	size_t size;
177 	int error;
178 
179 	size = sizeof(*link_status);
180 	error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
181 	    link_status, &size);
182 	if (error)
183 		return (error);
184 	if (size != sizeof(uint32_t)) {
185 		if_printf(sc->hn_ifp, "invalid link status len %zu\n", size);
186 		return (EINVAL);
187 	}
188 	return (0);
189 }
190 
191 int
192 hn_rndis_get_mtu(struct hn_softc *sc, uint32_t *mtu)
193 {
194 	size_t size;
195 	int error;
196 
197 	size = sizeof(*mtu);
198 	error = hn_rndis_query(sc, OID_GEN_MAXIMUM_FRAME_SIZE, NULL, 0,
199 	    mtu, &size);
200 	if (error)
201 		return (error);
202 	if (size != sizeof(uint32_t)) {
203 		if_printf(sc->hn_ifp, "invalid mtu len %zu\n", size);
204 		return (EINVAL);
205 	}
206 	return (0);
207 }
208 
209 static const void *
210 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
211     struct hn_nvs_sendctx *sndc, size_t *comp_len)
212 {
213 	struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT];
214 	int gpa_cnt, error;
215 	bus_addr_t paddr;
216 
217 	KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0,
218 	    ("invalid request length %zu", reqlen));
219 
220 	/*
221 	 * Setup the SG list.
222 	 */
223 	paddr = vmbus_xact_req_paddr(xact);
224 	KASSERT((paddr & PAGE_MASK) == 0,
225 	    ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr));
226 	for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) {
227 		int len = PAGE_SIZE;
228 
229 		if (reqlen == 0)
230 			break;
231 		if (reqlen < len)
232 			len = reqlen;
233 
234 		gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt;
235 		gpa[gpa_cnt].gpa_len = len;
236 		gpa[gpa_cnt].gpa_ofs = 0;
237 
238 		reqlen -= len;
239 	}
240 	KASSERT(reqlen == 0, ("still have %zu request data left", reqlen));
241 
242 	/*
243 	 * Send this RNDIS control message and wait for its completion
244 	 * message.
245 	 */
246 	vmbus_xact_activate(xact);
247 	error = hn_nvs_send_rndis_ctrl(sc->hn_prichan, sndc, gpa, gpa_cnt);
248 	if (error) {
249 		vmbus_xact_deactivate(xact);
250 		if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error);
251 		return (NULL);
252 	}
253 	return (vmbus_chan_xact_wait(sc->hn_prichan, xact, comp_len,
254 	    HN_CAN_SLEEP(sc)));
255 }
256 
257 static const void *
258 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid,
259     size_t reqlen, size_t *comp_len0, uint32_t comp_type)
260 {
261 	const struct rndis_comp_hdr *comp;
262 	size_t comp_len, min_complen = *comp_len0;
263 
264 	KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid));
265 	KASSERT(min_complen >= sizeof(*comp),
266 	    ("invalid minimum complete len %zu", min_complen));
267 
268 	/*
269 	 * Execute the xact setup by the caller.
270 	 */
271 	comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_nvs_sendctx_none,
272 	    &comp_len);
273 	if (comp == NULL)
274 		return (NULL);
275 
276 	/*
277 	 * Check this RNDIS complete message.
278 	 */
279 	if (comp_len < min_complen) {
280 		if (comp_len >= sizeof(*comp)) {
281 			/* rm_status field is valid */
282 			if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, "
283 			    "status 0x%08x\n", comp_len, comp->rm_status);
284 		} else {
285 			if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
286 			    comp_len);
287 		}
288 		return (NULL);
289 	}
290 	if (comp->rm_len < min_complen) {
291 		if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
292 		    comp->rm_len);
293 		return (NULL);
294 	}
295 	if (comp->rm_type != comp_type) {
296 		if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, "
297 		    "expect 0x%08x\n", comp->rm_type, comp_type);
298 		return (NULL);
299 	}
300 	if (comp->rm_rid != rid) {
301 		if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, "
302 		    "expect %u\n", comp->rm_rid, rid);
303 		return (NULL);
304 	}
305 	/* All pass! */
306 	*comp_len0 = comp_len;
307 	return (comp);
308 }
309 
310 static int
311 hn_rndis_query(struct hn_softc *sc, uint32_t oid,
312     const void *idata, size_t idlen, void *odata, size_t *odlen0)
313 {
314 
315 	return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0));
316 }
317 
318 static int
319 hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
320     const void *idata, size_t idlen, void *odata, size_t *odlen0,
321     size_t min_odlen)
322 {
323 	struct rndis_query_req *req;
324 	const struct rndis_query_comp *comp;
325 	struct vmbus_xact *xact;
326 	size_t reqlen, odlen = *odlen0, comp_len;
327 	int error, ofs;
328 	uint32_t rid;
329 
330 	reqlen = sizeof(*req) + idlen;
331 	xact = vmbus_xact_get(sc->hn_xact, reqlen);
332 	if (xact == NULL) {
333 		if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
334 		return (ENXIO);
335 	}
336 	rid = hn_rndis_rid(sc);
337 	req = vmbus_xact_req_data(xact);
338 	req->rm_type = REMOTE_NDIS_QUERY_MSG;
339 	req->rm_len = reqlen;
340 	req->rm_rid = rid;
341 	req->rm_oid = oid;
342 	/*
343 	 * XXX
344 	 * This is _not_ RNDIS Spec conforming:
345 	 * "This MUST be set to 0 when there is no input data
346 	 *  associated with the OID."
347 	 *
348 	 * If this field was set to 0 according to the RNDIS Spec,
349 	 * Hyper-V would set non-SUCCESS status in the query
350 	 * completion.
351 	 */
352 	req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
353 
354 	if (idlen > 0) {
355 		req->rm_infobuflen = idlen;
356 		/* Input data immediately follows RNDIS query. */
357 		memcpy(req + 1, idata, idlen);
358 	}
359 
360 	comp_len = sizeof(*comp) + min_odlen;
361 	comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
362 	    REMOTE_NDIS_QUERY_CMPLT);
363 	if (comp == NULL) {
364 		if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
365 		error = EIO;
366 		goto done;
367 	}
368 
369 	if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
370 		if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: "
371 		    "status 0x%08x\n", oid, comp->rm_status);
372 		error = EIO;
373 		goto done;
374 	}
375 	if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) {
376 		/* No output data! */
377 		if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid);
378 		*odlen0 = 0;
379 		error = 0;
380 		goto done;
381 	}
382 
383 	/*
384 	 * Check output data length and offset.
385 	 */
386 	/* ofs is the offset from the beginning of comp. */
387 	ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
388 	if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
389 		if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
390 		    "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
391 		error = EINVAL;
392 		goto done;
393 	}
394 
395 	/*
396 	 * Save output data.
397 	 */
398 	if (comp->rm_infobuflen < odlen)
399 		odlen = comp->rm_infobuflen;
400 	memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
401 	*odlen0 = odlen;
402 
403 	error = 0;
404 done:
405 	vmbus_xact_put(xact);
406 	return (error);
407 }
408 
409 int
410 hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0)
411 {
412 	struct ndis_rss_caps in, caps;
413 	size_t caps_len;
414 	int error, indsz, rxr_cnt, hash_fnidx;
415 	uint32_t hash_func = 0, hash_types = 0;
416 
417 	*rxr_cnt0 = 0;
418 
419 	if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20)
420 		return (EOPNOTSUPP);
421 
422 	memset(&in, 0, sizeof(in));
423 	in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
424 	in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
425 	in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
426 
427 	caps_len = NDIS_RSS_CAPS_SIZE;
428 	error = hn_rndis_query2(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
429 	    &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len, NDIS_RSS_CAPS_SIZE_6_0);
430 	if (error)
431 		return (error);
432 
433 	/*
434 	 * Preliminary verification.
435 	 */
436 	if (caps.ndis_hdr.ndis_type != NDIS_OBJTYPE_RSS_CAPS) {
437 		if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
438 		    caps.ndis_hdr.ndis_type);
439 		return (EINVAL);
440 	}
441 	if (caps.ndis_hdr.ndis_rev < NDIS_RSS_CAPS_REV_1) {
442 		if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
443 		    caps.ndis_hdr.ndis_rev);
444 		return (EINVAL);
445 	}
446 	if (caps.ndis_hdr.ndis_size > caps_len) {
447 		if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
448 		    "data size %zu\n", caps.ndis_hdr.ndis_size, caps_len);
449 		return (EINVAL);
450 	} else if (caps.ndis_hdr.ndis_size < NDIS_RSS_CAPS_SIZE_6_0) {
451 		if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
452 		    caps.ndis_hdr.ndis_size);
453 		return (EINVAL);
454 	}
455 
456 	/*
457 	 * Save information for later RSS configuration.
458 	 */
459 	if (caps.ndis_nrxr == 0) {
460 		if_printf(sc->hn_ifp, "0 RX rings!?\n");
461 		return (EINVAL);
462 	}
463 	if (bootverbose)
464 		if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr);
465 	rxr_cnt = caps.ndis_nrxr;
466 
467 	if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE &&
468 	    caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) {
469 		if (caps.ndis_nind > NDIS_HASH_INDCNT) {
470 			if_printf(sc->hn_ifp,
471 			    "too many RSS indirect table entries %u\n",
472 			    caps.ndis_nind);
473 			return (EOPNOTSUPP);
474 		}
475 		if (!powerof2(caps.ndis_nind)) {
476 			if_printf(sc->hn_ifp, "RSS indirect table size is not "
477 			    "power-of-2 %u\n", caps.ndis_nind);
478 		}
479 
480 		if (bootverbose) {
481 			if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
482 			    caps.ndis_nind);
483 		}
484 		indsz = caps.ndis_nind;
485 	} else {
486 		indsz = NDIS_HASH_INDCNT;
487 	}
488 	if (indsz < rxr_cnt) {
489 		if_printf(sc->hn_ifp, "# of RX rings (%d) > "
490 		    "RSS indirect table size %d\n", rxr_cnt, indsz);
491 		rxr_cnt = indsz;
492 	}
493 
494 	/*
495 	 * NOTE:
496 	 * Toeplitz is at the lowest bit, and it is preferred; so ffs(),
497 	 * instead of fls(), is used here.
498 	 */
499 	hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK);
500 	if (hash_fnidx == 0) {
501 		if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n",
502 		    caps.ndis_caps);
503 		return (EOPNOTSUPP);
504 	}
505 	hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */
506 
507 	if (caps.ndis_caps & NDIS_RSS_CAP_IPV4)
508 		hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4;
509 	if (caps.ndis_caps & NDIS_RSS_CAP_IPV6)
510 		hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6;
511 	if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX)
512 		hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX;
513 	if (hash_types == 0) {
514 		if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n",
515 		    caps.ndis_caps);
516 		return (EOPNOTSUPP);
517 	}
518 	if (bootverbose)
519 		if_printf(sc->hn_ifp, "RSS caps %#x\n", caps.ndis_caps);
520 
521 	/* Commit! */
522 	sc->hn_rss_ind_size = indsz;
523 	sc->hn_rss_hcap = hash_func | hash_types;
524 	if (sc->hn_caps & HN_CAP_UDPHASH) {
525 		/* UDP 4-tuple hash is unconditionally enabled. */
526 		sc->hn_rss_hcap |= NDIS_HASH_UDP_IPV4_X;
527 	}
528 	*rxr_cnt0 = rxr_cnt;
529 	return (0);
530 }
531 
532 static int
533 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen)
534 {
535 	struct rndis_set_req *req;
536 	const struct rndis_set_comp *comp;
537 	struct vmbus_xact *xact;
538 	size_t reqlen, comp_len;
539 	uint32_t rid;
540 	int error;
541 
542 	KASSERT(dlen > 0, ("invalid dlen %zu", dlen));
543 
544 	reqlen = sizeof(*req) + dlen;
545 	xact = vmbus_xact_get(sc->hn_xact, reqlen);
546 	if (xact == NULL) {
547 		if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid);
548 		return (ENXIO);
549 	}
550 	rid = hn_rndis_rid(sc);
551 	req = vmbus_xact_req_data(xact);
552 	req->rm_type = REMOTE_NDIS_SET_MSG;
553 	req->rm_len = reqlen;
554 	req->rm_rid = rid;
555 	req->rm_oid = oid;
556 	req->rm_infobuflen = dlen;
557 	req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET;
558 	/* Data immediately follows RNDIS set. */
559 	memcpy(req + 1, data, dlen);
560 
561 	comp_len = sizeof(*comp);
562 	comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
563 	    REMOTE_NDIS_SET_CMPLT);
564 	if (comp == NULL) {
565 		if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid);
566 		error = EIO;
567 		goto done;
568 	}
569 
570 	if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
571 		if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: "
572 		    "status 0x%08x\n", oid, comp->rm_status);
573 		error = EIO;
574 		goto done;
575 	}
576 	error = 0;
577 done:
578 	vmbus_xact_put(xact);
579 	return (error);
580 }
581 
582 int
583 hn_rndis_reconf_offload(struct hn_softc *sc, int mtu)
584 {
585 	return(hn_rndis_conf_offload(sc, mtu));
586 }
587 
588 static int
589 hn_rndis_conf_offload(struct hn_softc *sc, int mtu)
590 {
591 	struct ndis_offload hwcaps;
592 	struct ndis_offload_params params;
593 	uint32_t caps = 0;
594 	size_t paramsz;
595 	int error, tso_maxsz, tso_minsg;
596 
597 	error = hn_rndis_query_hwcaps(sc, &hwcaps);
598 	if (error) {
599 		if_printf(sc->hn_ifp, "hwcaps query failed: %d\n", error);
600 		return (error);
601 	}
602 
603 	/* NOTE: 0 means "no change" */
604 	memset(&params, 0, sizeof(params));
605 
606 	params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT;
607 	if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30) {
608 		params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2;
609 		paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1;
610 	} else {
611 		params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3;
612 		paramsz = NDIS_OFFLOAD_PARAMS_SIZE;
613 	}
614 	params.ndis_hdr.ndis_size = paramsz;
615 
616 	/*
617 	 * TSO4/TSO6 setup.
618 	 */
619 	tso_maxsz = IP_MAXPACKET;
620 	tso_minsg = 2;
621 	if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
622 		caps |= HN_CAP_TSO4;
623 		params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
624 
625 		if (hwcaps.ndis_lsov2.ndis_ip4_maxsz < tso_maxsz)
626 			tso_maxsz = hwcaps.ndis_lsov2.ndis_ip4_maxsz;
627 		if (hwcaps.ndis_lsov2.ndis_ip4_minsg > tso_minsg)
628 			tso_minsg = hwcaps.ndis_lsov2.ndis_ip4_minsg;
629 	}
630 	if ((hwcaps.ndis_lsov2.ndis_ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
631 	    (hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6) ==
632 	    HN_NDIS_LSOV2_CAP_IP6) {
633 		caps |= HN_CAP_TSO6;
634 		params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON;
635 
636 		if (hwcaps.ndis_lsov2.ndis_ip6_maxsz < tso_maxsz)
637 			tso_maxsz = hwcaps.ndis_lsov2.ndis_ip6_maxsz;
638 		if (hwcaps.ndis_lsov2.ndis_ip6_minsg > tso_minsg)
639 			tso_minsg = hwcaps.ndis_lsov2.ndis_ip6_minsg;
640 	}
641 	sc->hn_ndis_tso_szmax = 0;
642 	sc->hn_ndis_tso_sgmin = 0;
643 	if (caps & (HN_CAP_TSO4 | HN_CAP_TSO6)) {
644 		KASSERT(tso_maxsz <= IP_MAXPACKET,
645 		    ("invalid NDIS TSO maxsz %d", tso_maxsz));
646 		KASSERT(tso_minsg >= 2,
647 		    ("invalid NDIS TSO minsg %d", tso_minsg));
648 		if (tso_maxsz < tso_minsg * mtu) {
649 			if_printf(sc->hn_ifp, "invalid NDIS TSO config: "
650 			    "maxsz %d, minsg %d, mtu %d; "
651 			    "disable TSO4 and TSO6\n",
652 			    tso_maxsz, tso_minsg, mtu);
653 			caps &= ~(HN_CAP_TSO4 | HN_CAP_TSO6);
654 			params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_OFF;
655 			params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_OFF;
656 		} else {
657 			sc->hn_ndis_tso_szmax = tso_maxsz;
658 			sc->hn_ndis_tso_sgmin = tso_minsg;
659 			if (bootverbose) {
660 				if_printf(sc->hn_ifp, "NDIS TSO "
661 				    "szmax %d sgmin %d\n",
662 				    sc->hn_ndis_tso_szmax,
663 				    sc->hn_ndis_tso_sgmin);
664 			}
665 		}
666 	}
667 
668 	/* IPv4 checksum */
669 	if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4) ==
670 	    HN_NDIS_TXCSUM_CAP_IP4) {
671 		caps |= HN_CAP_IPCS;
672 		params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX;
673 	}
674 	if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4) {
675 		if (params.ndis_ip4csum == NDIS_OFFLOAD_PARAM_TX)
676 			params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX;
677 		else
678 			params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_RX;
679 	}
680 
681 	/* TCP4 checksum */
682 	if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4) ==
683 	    HN_NDIS_TXCSUM_CAP_TCP4) {
684 		caps |= HN_CAP_TCP4CS;
685 		params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TX;
686 	}
687 	if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) {
688 		if (params.ndis_tcp4csum == NDIS_OFFLOAD_PARAM_TX)
689 			params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX;
690 		else
691 			params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_RX;
692 	}
693 
694 	/* UDP4 checksum */
695 	if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
696 		caps |= HN_CAP_UDP4CS;
697 		params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TX;
698 	}
699 	if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) {
700 		if (params.ndis_udp4csum == NDIS_OFFLOAD_PARAM_TX)
701 			params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX;
702 		else
703 			params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_RX;
704 	}
705 
706 	/* TCP6 checksum */
707 	if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6) ==
708 	    HN_NDIS_TXCSUM_CAP_TCP6) {
709 		caps |= HN_CAP_TCP6CS;
710 		params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TX;
711 	}
712 	if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6) {
713 		if (params.ndis_tcp6csum == NDIS_OFFLOAD_PARAM_TX)
714 			params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX;
715 		else
716 			params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_RX;
717 	}
718 
719 	/* UDP6 checksum */
720 	if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_UDP6) ==
721 	    HN_NDIS_TXCSUM_CAP_UDP6) {
722 		caps |= HN_CAP_UDP6CS;
723 		params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TX;
724 	}
725 	if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6) {
726 		if (params.ndis_udp6csum == NDIS_OFFLOAD_PARAM_TX)
727 			params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX;
728 		else
729 			params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_RX;
730 	}
731 
732 	/* RSC offload */
733 	if (hwcaps.ndis_hdr.ndis_rev >= NDIS_OFFLOAD_PARAMS_REV_3) {
734 		if (hwcaps.ndis_rsc.ndis_ip4 && hwcaps.ndis_rsc.ndis_ip6 &&
735 		    sc->hn_rsc_ctrl) {
736 			params.ndis_rsc_ip4 = NDIS_OFFLOAD_RSC_ON;
737 			params.ndis_rsc_ip6 = NDIS_OFFLOAD_RSC_ON;
738 		} else {
739 			params.ndis_rsc_ip4 = NDIS_OFFLOAD_RSC_OFF;
740 			params.ndis_rsc_ip6 = NDIS_OFFLOAD_RSC_OFF;
741 		}
742 	}
743 
744 	if (bootverbose) {
745 		if_printf(sc->hn_ifp, "offload csum: "
746 		    "ip4 %u, tcp4 %u, udp4 %u, tcp6 %u, udp6 %u\n",
747 		    params.ndis_ip4csum,
748 		    params.ndis_tcp4csum,
749 		    params.ndis_udp4csum,
750 		    params.ndis_tcp6csum,
751 		    params.ndis_udp6csum);
752 		if_printf(sc->hn_ifp, "offload lsov2: ip4 %u, ip6 %u\n",
753 		    params.ndis_lsov2_ip4,
754 		    params.ndis_lsov2_ip6);
755 		if (hwcaps.ndis_hdr.ndis_rev >= NDIS_OFFLOAD_PARAMS_REV_3)
756 			if_printf(sc->hn_ifp, "offload rsc: ip4 %u, ip6 %u\n",
757 			    params.ndis_rsc_ip4,
758 			    params.ndis_rsc_ip6);
759 	}
760 
761 	error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, &params, paramsz);
762 	if (error) {
763 		if_printf(sc->hn_ifp, "offload config failed: %d\n", error);
764 		return (error);
765 	}
766 
767 	if (bootverbose)
768 		if_printf(sc->hn_ifp, "offload config done\n");
769 	sc->hn_caps |= caps;
770 	return (0);
771 }
772 
773 int
774 hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags)
775 {
776 	struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
777 	struct ndis_rss_params *prm = &rss->rss_params;
778 	int error, rss_size;
779 
780 	/*
781 	 * Only NDIS 6.20+ is supported:
782 	 * We only support 4bytes element in indirect table, which has been
783 	 * adopted since NDIS 6.20.
784 	 */
785 	KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_20,
786 	    ("NDIS 6.20+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
787 
788 	/* XXX only one can be specified through, popcnt? */
789 	KASSERT((sc->hn_rss_hash & NDIS_HASH_FUNCTION_MASK),
790 	    ("no hash func %08x", sc->hn_rss_hash));
791 	KASSERT((sc->hn_rss_hash & NDIS_HASH_STD),
792 	    ("no standard hash types %08x", sc->hn_rss_hash));
793 	KASSERT(sc->hn_rss_ind_size > 0, ("no indirect table size"));
794 
795 	if (bootverbose) {
796 		if_printf(sc->hn_ifp, "RSS indirect table size %d, "
797 		    "hash 0x%08x\n", sc->hn_rss_ind_size, sc->hn_rss_hash);
798 	}
799 
800 	/*
801 	 * NOTE:
802 	 * DO NOT whack rss_key and rss_ind, which are setup by the caller.
803 	 */
804 	memset(prm, 0, sizeof(*prm));
805 	rss_size = NDIS_RSSPRM_TOEPLITZ_SIZE(sc->hn_rss_ind_size);
806 
807 	prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS;
808 	prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2;
809 	prm->ndis_hdr.ndis_size = rss_size;
810 	prm->ndis_flags = flags;
811 	prm->ndis_hash = sc->hn_rss_hash &
812 	    (NDIS_HASH_FUNCTION_MASK | NDIS_HASH_STD);
813 	prm->ndis_indsize = sizeof(rss->rss_ind[0]) * sc->hn_rss_ind_size;
814 	prm->ndis_indoffset =
815 	    __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]);
816 	prm->ndis_keysize = sizeof(rss->rss_key);
817 	prm->ndis_keyoffset =
818 	    __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]);
819 
820 	error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS,
821 	    rss, rss_size);
822 	if (error) {
823 		if_printf(sc->hn_ifp, "RSS config failed: %d\n", error);
824 	} else {
825 		if (bootverbose)
826 			if_printf(sc->hn_ifp, "RSS config done\n");
827 	}
828 	return (error);
829 }
830 
831 int
832 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter)
833 {
834 	int error;
835 
836 	error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER,
837 	    &filter, sizeof(filter));
838 	if (error) {
839 		if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n",
840 		    filter, error);
841 	} else {
842 		if (bootverbose) {
843 			if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n",
844 			    filter);
845 		}
846 	}
847 	return (error);
848 }
849 
850 static int
851 hn_rndis_init(struct hn_softc *sc)
852 {
853 	struct rndis_init_req *req;
854 	const struct rndis_init_comp *comp;
855 	struct vmbus_xact *xact;
856 	size_t comp_len;
857 	uint32_t rid;
858 	int error;
859 
860 	xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
861 	if (xact == NULL) {
862 		if_printf(sc->hn_ifp, "no xact for RNDIS init\n");
863 		return (ENXIO);
864 	}
865 	rid = hn_rndis_rid(sc);
866 	req = vmbus_xact_req_data(xact);
867 	req->rm_type = REMOTE_NDIS_INITIALIZE_MSG;
868 	req->rm_len = sizeof(*req);
869 	req->rm_rid = rid;
870 	req->rm_ver_major = RNDIS_VERSION_MAJOR;
871 	req->rm_ver_minor = RNDIS_VERSION_MINOR;
872 	req->rm_max_xfersz = HN_RNDIS_XFER_SIZE;
873 
874 	comp_len = RNDIS_INIT_COMP_SIZE_MIN;
875 	comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len,
876 	    REMOTE_NDIS_INITIALIZE_CMPLT);
877 	if (comp == NULL) {
878 		if_printf(sc->hn_ifp, "exec RNDIS init failed\n");
879 		error = EIO;
880 		goto done;
881 	}
882 
883 	if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
884 		if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n",
885 		    comp->rm_status);
886 		error = EIO;
887 		goto done;
888 	}
889 	sc->hn_rndis_agg_size = comp->rm_pktmaxsz;
890 	sc->hn_rndis_agg_pkts = comp->rm_pktmaxcnt;
891 	sc->hn_rndis_agg_align = 1U << comp->rm_align;
892 
893 	if (sc->hn_rndis_agg_align < sizeof(uint32_t)) {
894 		/*
895 		 * The RNDIS packet messsage encap assumes that the RNDIS
896 		 * packet message is at least 4 bytes aligned.  Fix up the
897 		 * alignment here, if the remote side sets the alignment
898 		 * too low.
899 		 */
900 		if_printf(sc->hn_ifp, "fixup RNDIS aggpkt align: %u -> %zu\n",
901 		    sc->hn_rndis_agg_align, sizeof(uint32_t));
902 		sc->hn_rndis_agg_align = sizeof(uint32_t);
903 	}
904 
905 	if (bootverbose) {
906 		if_printf(sc->hn_ifp, "RNDIS ver %u.%u, "
907 		    "aggpkt size %u, aggpkt cnt %u, aggpkt align %u\n",
908 		    comp->rm_ver_major, comp->rm_ver_minor,
909 		    sc->hn_rndis_agg_size, sc->hn_rndis_agg_pkts,
910 		    sc->hn_rndis_agg_align);
911 	}
912 	error = 0;
913 done:
914 	vmbus_xact_put(xact);
915 	return (error);
916 }
917 
918 static int
919 hn_rndis_halt(struct hn_softc *sc)
920 {
921 	struct vmbus_xact *xact;
922 	struct rndis_halt_req *halt;
923 	struct hn_nvs_sendctx sndc;
924 	size_t comp_len;
925 
926 	xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
927 	if (xact == NULL) {
928 		if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
929 		return (ENXIO);
930 	}
931 	halt = vmbus_xact_req_data(xact);
932 	halt->rm_type = REMOTE_NDIS_HALT_MSG;
933 	halt->rm_len = sizeof(*halt);
934 	halt->rm_rid = hn_rndis_rid(sc);
935 
936 	/* No RNDIS completion; rely on NVS message send completion */
937 	hn_nvs_sendctx_init(&sndc, hn_nvs_sent_xact, xact);
938 	hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len);
939 
940 	vmbus_xact_put(xact);
941 	if (bootverbose)
942 		if_printf(sc->hn_ifp, "RNDIS halt done\n");
943 	return (0);
944 }
945 
946 static int
947 hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps)
948 {
949 	struct ndis_offload in;
950 	size_t caps_len, size;
951 	int error;
952 
953 	memset(&in, 0, sizeof(in));
954 	in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD;
955 	if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
956 		in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3;
957 		size = NDIS_OFFLOAD_SIZE;
958 	} else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) {
959 		in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2;
960 		size = NDIS_OFFLOAD_SIZE_6_1;
961 	} else {
962 		in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1;
963 		size = NDIS_OFFLOAD_SIZE_6_0;
964 	}
965 	in.ndis_hdr.ndis_size = size;
966 
967 	caps_len = NDIS_OFFLOAD_SIZE;
968 	error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
969 	    &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_6_0);
970 	if (error)
971 		return (error);
972 
973 	/*
974 	 * Preliminary verification.
975 	 */
976 	if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) {
977 		if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
978 		    caps->ndis_hdr.ndis_type);
979 		return (EINVAL);
980 	}
981 	if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) {
982 		if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
983 		    caps->ndis_hdr.ndis_rev);
984 		return (EINVAL);
985 	}
986 	if (caps->ndis_hdr.ndis_size > caps_len) {
987 		if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
988 		    "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len);
989 		return (EINVAL);
990 	} else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_6_0) {
991 		if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
992 		    caps->ndis_hdr.ndis_size);
993 		return (EINVAL);
994 	} else if (caps->ndis_hdr.ndis_rev >= NDIS_OFFLOAD_REV_3 &&
995 		   caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE) {
996 		if_printf(sc->hn_ifp, "invalid NDIS rev3 objsize %u\n",
997 		    caps->ndis_hdr.ndis_size);
998 		return (EINVAL);
999 	}
1000 
1001 	if (bootverbose) {
1002 		/*
1003 		 * NOTE:
1004 		 * caps->ndis_hdr.ndis_size MUST be checked before accessing
1005 		 * NDIS 6.1+ specific fields.
1006 		 */
1007 		if_printf(sc->hn_ifp, "hwcaps rev %u\n",
1008 		    caps->ndis_hdr.ndis_rev);
1009 
1010 		if_printf(sc->hn_ifp, "hwcaps csum: "
1011 		    "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, "
1012 		    "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n",
1013 		    caps->ndis_csum.ndis_ip4_txcsum,
1014 		    caps->ndis_csum.ndis_ip4_txenc,
1015 		    caps->ndis_csum.ndis_ip4_rxcsum,
1016 		    caps->ndis_csum.ndis_ip4_rxenc,
1017 		    caps->ndis_csum.ndis_ip6_txcsum,
1018 		    caps->ndis_csum.ndis_ip6_txenc,
1019 		    caps->ndis_csum.ndis_ip6_rxcsum,
1020 		    caps->ndis_csum.ndis_ip6_rxenc);
1021 		if_printf(sc->hn_ifp, "hwcaps lsov2: "
1022 		    "ip4 maxsz %u minsg %u encap 0x%x, "
1023 		    "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n",
1024 		    caps->ndis_lsov2.ndis_ip4_maxsz,
1025 		    caps->ndis_lsov2.ndis_ip4_minsg,
1026 		    caps->ndis_lsov2.ndis_ip4_encap,
1027 		    caps->ndis_lsov2.ndis_ip6_maxsz,
1028 		    caps->ndis_lsov2.ndis_ip6_minsg,
1029 		    caps->ndis_lsov2.ndis_ip6_encap,
1030 		    caps->ndis_lsov2.ndis_ip6_opts);
1031 		if (caps->ndis_hdr.ndis_rev >= NDIS_OFFLOAD_REV_3)
1032 			if_printf(sc->hn_ifp, "hwcaps rsc: "
1033 			    "ip4 %u ip6 %u\n",
1034 			    caps->ndis_rsc.ndis_ip4,
1035 			    caps->ndis_rsc.ndis_ip6);
1036 	}
1037 	return (0);
1038 }
1039 
1040 int
1041 hn_rndis_attach(struct hn_softc *sc, int mtu, int *init_done)
1042 {
1043 	int error;
1044 
1045 	*init_done = 0;
1046 
1047 	/*
1048 	 * Initialize RNDIS.
1049 	 */
1050 	error = hn_rndis_init(sc);
1051 	if (error)
1052 		return (error);
1053 	*init_done = 1;
1054 
1055 	/*
1056 	 * Configure NDIS offload settings.
1057 	 */
1058 	hn_rndis_conf_offload(sc, mtu);
1059 	return (0);
1060 }
1061 
1062 void
1063 hn_rndis_detach(struct hn_softc *sc)
1064 {
1065 
1066 	/* Halt the RNDIS. */
1067 	hn_rndis_halt(sc);
1068 }
1069