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