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