2 * Copyright (c) 2009-2012,2016 Microsoft Corp.
3 * Copyright (c) 2010-2012 Citrix Inc.
4 * Copyright (c) 2012 NetApp Inc.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice unmodified, this list of conditions, and the following
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.
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.
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
32 #include <sys/param.h>
33 #include <sys/kernel.h>
35 #include <sys/socket.h>
37 #include <sys/mutex.h>
39 #include <net/if_arp.h>
40 #include <net/if_var.h>
41 #include <net/ethernet.h>
42 #include <net/rndis.h>
43 #include <netinet/in.h>
44 #include <netinet/ip.h>
45 #include <sys/types.h>
46 #include <machine/atomic.h>
49 #include <vm/vm_param.h>
52 #include <dev/hyperv/include/hyperv.h>
53 #include <dev/hyperv/include/vmbus_xact.h>
54 #include <dev/hyperv/netvsc/hv_net_vsc.h>
55 #include <dev/hyperv/netvsc/hv_rndis_filter.h>
56 #include <dev/hyperv/netvsc/if_hnreg.h>
57 #include <dev/hyperv/netvsc/ndis.h>
59 #define HV_RF_RECVINFO_VLAN 0x1
60 #define HV_RF_RECVINFO_CSUM 0x2
61 #define HV_RF_RECVINFO_HASHINF 0x4
62 #define HV_RF_RECVINFO_HASHVAL 0x8
63 #define HV_RF_RECVINFO_ALL \
64 (HV_RF_RECVINFO_VLAN | \
65 HV_RF_RECVINFO_CSUM | \
66 HV_RF_RECVINFO_HASHINF | \
67 HV_RF_RECVINFO_HASHVAL)
69 #define HN_RNDIS_RID_COMPAT_MASK 0xffff
70 #define HN_RNDIS_RID_COMPAT_MAX HN_RNDIS_RID_COMPAT_MASK
72 #define HN_RNDIS_XFER_SIZE 2048
74 #define HN_NDIS_TXCSUM_CAP_IP4 \
75 (NDIS_TXCSUM_CAP_IP4 | NDIS_TXCSUM_CAP_IP4OPT)
76 #define HN_NDIS_TXCSUM_CAP_TCP4 \
77 (NDIS_TXCSUM_CAP_TCP4 | NDIS_TXCSUM_CAP_TCP4OPT)
78 #define HN_NDIS_TXCSUM_CAP_TCP6 \
79 (NDIS_TXCSUM_CAP_TCP6 | NDIS_TXCSUM_CAP_TCP6OPT | \
80 NDIS_TXCSUM_CAP_IP6EXT)
81 #define HN_NDIS_TXCSUM_CAP_UDP6 \
82 (NDIS_TXCSUM_CAP_UDP6 | NDIS_TXCSUM_CAP_IP6EXT)
83 #define HN_NDIS_LSOV2_CAP_IP6 \
84 (NDIS_LSOV2_CAP_IP6EXT | NDIS_LSOV2_CAP_TCP6OPT)
87 * Forward declarations
89 static void hv_rf_receive_indicate_status(struct hn_softc *sc,
90 const void *data, int dlen);
91 static void hv_rf_receive_data(struct hn_rx_ring *rxr,
92 const void *data, int dlen);
94 static int hn_rndis_query(struct hn_softc *sc, uint32_t oid,
95 const void *idata, size_t idlen, void *odata, size_t *odlen0);
96 static int hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
97 const void *idata, size_t idlen, void *odata, size_t *odlen0,
99 static int hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data,
101 static int hn_rndis_conf_offload(struct hn_softc *sc, int mtu);
102 static int hn_rndis_query_hwcaps(struct hn_softc *sc,
103 struct ndis_offload *caps);
105 static __inline uint32_t
106 hn_rndis_rid(struct hn_softc *sc)
111 rid = atomic_fetchadd_int(&sc->hn_rndis_rid, 1);
115 /* Use upper 16 bits for non-compat RNDIS messages. */
116 return ((rid & 0xffff) << 16);
120 hn_rndis_pktinfo_append(struct rndis_packet_msg *pkt, size_t pktsize,
121 size_t pi_dlen, uint32_t pi_type)
123 const size_t pi_size = HN_RNDIS_PKTINFO_SIZE(pi_dlen);
124 struct rndis_pktinfo *pi;
126 KASSERT((pi_size & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK) == 0,
127 ("unaligned pktinfo size %zu, pktinfo dlen %zu", pi_size, pi_dlen));
130 * Per-packet-info does not move; it only grows.
133 * rm_pktinfooffset in this phase counts from the beginning
134 * of rndis_packet_msg.
136 KASSERT(pkt->rm_pktinfooffset + pkt->rm_pktinfolen + pi_size <= pktsize,
137 ("%u pktinfo overflows RNDIS packet msg", pi_type));
138 pi = (struct rndis_pktinfo *)((uint8_t *)pkt + pkt->rm_pktinfooffset +
140 pkt->rm_pktinfolen += pi_size;
142 pi->rm_size = pi_size;
143 pi->rm_type = pi_type;
144 pi->rm_pktinfooffset = RNDIS_PKTINFO_OFFSET;
146 /* Data immediately follow per-packet-info. */
147 pkt->rm_dataoffset += pi_size;
149 /* Update RNDIS packet msg length */
150 pkt->rm_len += pi_size;
152 return (pi->rm_data);
156 * RNDIS filter receive indicate status
159 hv_rf_receive_indicate_status(struct hn_softc *sc, const void *data, int dlen)
161 const struct rndis_status_msg *msg;
164 if (dlen < sizeof(*msg)) {
165 if_printf(sc->hn_ifp, "invalid RNDIS status\n");
170 switch (msg->rm_status) {
171 case RNDIS_STATUS_MEDIA_CONNECT:
172 case RNDIS_STATUS_MEDIA_DISCONNECT:
173 hn_link_status_update(sc);
176 case RNDIS_STATUS_TASK_OFFLOAD_CURRENT_CONFIG:
177 /* Not really useful; ignore. */
180 case RNDIS_STATUS_NETWORK_CHANGE:
181 ofs = RNDIS_STBUFOFFSET_ABS(msg->rm_stbufoffset);
182 if (dlen < ofs + msg->rm_stbuflen ||
183 msg->rm_stbuflen < sizeof(uint32_t)) {
184 if_printf(sc->hn_ifp, "network changed\n");
188 memcpy(&change, ((const uint8_t *)msg) + ofs,
190 if_printf(sc->hn_ifp, "network changed, change %u\n",
193 hn_network_change(sc);
198 if_printf(sc->hn_ifp, "unknown RNDIS status 0x%08x\n",
205 hn_rndis_rxinfo(const void *info_data, int info_dlen, struct hn_recvinfo *info)
207 const struct rndis_pktinfo *pi = info_data;
210 while (info_dlen != 0) {
214 if (__predict_false(info_dlen < sizeof(*pi)))
216 if (__predict_false(info_dlen < pi->rm_size))
218 info_dlen -= pi->rm_size;
220 if (__predict_false(pi->rm_size & RNDIS_PKTINFO_SIZE_ALIGNMASK))
222 if (__predict_false(pi->rm_size < pi->rm_pktinfooffset))
224 dlen = pi->rm_size - pi->rm_pktinfooffset;
227 switch (pi->rm_type) {
228 case NDIS_PKTINFO_TYPE_VLAN:
229 if (__predict_false(dlen < NDIS_VLAN_INFO_SIZE))
231 info->vlan_info = *((const uint32_t *)data);
232 mask |= HV_RF_RECVINFO_VLAN;
235 case NDIS_PKTINFO_TYPE_CSUM:
236 if (__predict_false(dlen < NDIS_RXCSUM_INFO_SIZE))
238 info->csum_info = *((const uint32_t *)data);
239 mask |= HV_RF_RECVINFO_CSUM;
242 case HN_NDIS_PKTINFO_TYPE_HASHVAL:
243 if (__predict_false(dlen < HN_NDIS_HASH_VALUE_SIZE))
245 info->hash_value = *((const uint32_t *)data);
246 mask |= HV_RF_RECVINFO_HASHVAL;
249 case HN_NDIS_PKTINFO_TYPE_HASHINF:
250 if (__predict_false(dlen < HN_NDIS_HASH_INFO_SIZE))
252 info->hash_info = *((const uint32_t *)data);
253 mask |= HV_RF_RECVINFO_HASHINF;
260 if (mask == HV_RF_RECVINFO_ALL) {
261 /* All found; done */
265 pi = (const struct rndis_pktinfo *)
266 ((const uint8_t *)pi + pi->rm_size);
271 * - If there is no hash value, invalidate the hash info.
273 if ((mask & HV_RF_RECVINFO_HASHVAL) == 0)
274 info->hash_info = HN_NDIS_HASH_INFO_INVALID;
279 hn_rndis_check_overlap(int off, int len, int check_off, int check_len)
282 if (off < check_off) {
283 if (__predict_true(off + len <= check_off))
285 } else if (off > check_off) {
286 if (__predict_true(check_off + check_len <= off))
293 * RNDIS filter receive data
296 hv_rf_receive_data(struct hn_rx_ring *rxr, const void *data, int dlen)
298 const struct rndis_packet_msg *pkt;
299 struct hn_recvinfo info;
300 int data_off, pktinfo_off, data_len, pktinfo_len;
305 if (__predict_false(dlen < sizeof(*pkt))) {
306 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg\n");
311 if (__predict_false(dlen < pkt->rm_len)) {
312 if_printf(rxr->hn_ifp, "truncated RNDIS packet msg, "
313 "dlen %d, msglen %u\n", dlen, pkt->rm_len);
316 if (__predict_false(pkt->rm_len <
317 pkt->rm_datalen + pkt->rm_oobdatalen + pkt->rm_pktinfolen)) {
318 if_printf(rxr->hn_ifp, "invalid RNDIS packet msglen, "
319 "msglen %u, data %u, oob %u, pktinfo %u\n",
320 pkt->rm_len, pkt->rm_datalen, pkt->rm_oobdatalen,
324 if (__predict_false(pkt->rm_datalen == 0)) {
325 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, no data\n");
332 #define IS_OFFSET_INVALID(ofs) \
333 ((ofs) < RNDIS_PACKET_MSG_OFFSET_MIN || \
334 ((ofs) & RNDIS_PACKET_MSG_OFFSET_ALIGNMASK))
336 /* XXX Hyper-V does not meet data offset alignment requirement */
337 if (__predict_false(pkt->rm_dataoffset < RNDIS_PACKET_MSG_OFFSET_MIN)) {
338 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
339 "data offset %u\n", pkt->rm_dataoffset);
342 if (__predict_false(pkt->rm_oobdataoffset > 0 &&
343 IS_OFFSET_INVALID(pkt->rm_oobdataoffset))) {
344 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
345 "oob offset %u\n", pkt->rm_oobdataoffset);
348 if (__predict_true(pkt->rm_pktinfooffset > 0) &&
349 __predict_false(IS_OFFSET_INVALID(pkt->rm_pktinfooffset))) {
350 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
351 "pktinfo offset %u\n", pkt->rm_pktinfooffset);
355 #undef IS_OFFSET_INVALID
357 data_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_dataoffset);
358 data_len = pkt->rm_datalen;
359 pktinfo_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_pktinfooffset);
360 pktinfo_len = pkt->rm_pktinfolen;
363 * Check OOB coverage.
365 if (__predict_false(pkt->rm_oobdatalen != 0)) {
366 int oob_off, oob_len;
368 if_printf(rxr->hn_ifp, "got oobdata\n");
369 oob_off = RNDIS_PACKET_MSG_OFFSET_ABS(pkt->rm_oobdataoffset);
370 oob_len = pkt->rm_oobdatalen;
372 if (__predict_false(oob_off + oob_len > pkt->rm_len)) {
373 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
374 "oob overflow, msglen %u, oob abs %d len %d\n",
375 pkt->rm_len, oob_off, oob_len);
380 * Check against data.
382 if (hn_rndis_check_overlap(oob_off, oob_len,
383 data_off, data_len)) {
384 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
385 "oob overlaps data, oob abs %d len %d, "
386 "data abs %d len %d\n",
387 oob_off, oob_len, data_off, data_len);
392 * Check against pktinfo.
394 if (pktinfo_len != 0 &&
395 hn_rndis_check_overlap(oob_off, oob_len,
396 pktinfo_off, pktinfo_len)) {
397 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
398 "oob overlaps pktinfo, oob abs %d len %d, "
399 "pktinfo abs %d len %d\n",
400 oob_off, oob_len, pktinfo_off, pktinfo_len);
406 * Check per-packet-info coverage and find useful per-packet-info.
408 info.vlan_info = HN_NDIS_VLAN_INFO_INVALID;
409 info.csum_info = HN_NDIS_RXCSUM_INFO_INVALID;
410 info.hash_info = HN_NDIS_HASH_INFO_INVALID;
411 if (__predict_true(pktinfo_len != 0)) {
415 if (__predict_false(pktinfo_off + pktinfo_len > pkt->rm_len)) {
416 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
417 "pktinfo overflow, msglen %u, "
418 "pktinfo abs %d len %d\n",
419 pkt->rm_len, pktinfo_off, pktinfo_len);
424 * Check packet info coverage.
426 overlap = hn_rndis_check_overlap(pktinfo_off, pktinfo_len,
428 if (__predict_false(overlap)) {
429 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
430 "pktinfo overlap data, pktinfo abs %d len %d, "
431 "data abs %d len %d\n",
432 pktinfo_off, pktinfo_len, data_off, data_len);
437 * Find useful per-packet-info.
439 error = hn_rndis_rxinfo(((const uint8_t *)pkt) + pktinfo_off,
441 if (__predict_false(error)) {
442 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg "
448 if (__predict_false(data_off + data_len > pkt->rm_len)) {
449 if_printf(rxr->hn_ifp, "invalid RNDIS packet msg, "
450 "data overflow, msglen %u, data abs %d len %d\n",
451 pkt->rm_len, data_off, data_len);
454 hn_rxpkt(rxr, ((const uint8_t *)pkt) + data_off, data_len, &info);
458 * RNDIS filter on receive
461 hv_rf_on_receive(struct hn_softc *sc, struct hn_rx_ring *rxr,
462 const void *data, int dlen)
464 const struct rndis_comp_hdr *comp;
465 const struct rndis_msghdr *hdr;
467 if (__predict_false(dlen < sizeof(*hdr))) {
468 if_printf(rxr->hn_ifp, "invalid RNDIS msg\n");
473 switch (hdr->rm_type) {
474 case REMOTE_NDIS_PACKET_MSG:
475 hv_rf_receive_data(rxr, data, dlen);
478 case REMOTE_NDIS_INITIALIZE_CMPLT:
479 case REMOTE_NDIS_QUERY_CMPLT:
480 case REMOTE_NDIS_SET_CMPLT:
481 case REMOTE_NDIS_KEEPALIVE_CMPLT: /* unused */
482 if (dlen < sizeof(*comp)) {
483 if_printf(rxr->hn_ifp, "invalid RNDIS cmplt\n");
488 KASSERT(comp->rm_rid > HN_RNDIS_RID_COMPAT_MAX,
489 ("invalid RNDIS rid 0x%08x\n", comp->rm_rid));
490 vmbus_xact_ctx_wakeup(sc->hn_xact, comp, dlen);
493 case REMOTE_NDIS_INDICATE_STATUS_MSG:
494 hv_rf_receive_indicate_status(sc, data, dlen);
497 case REMOTE_NDIS_RESET_CMPLT:
499 * Reset completed, no rid.
502 * RESET is not issued by hn(4), so this message should
505 if_printf(rxr->hn_ifp, "RESET cmplt received\n");
509 if_printf(rxr->hn_ifp, "unknown RNDIS msg 0x%x\n",
516 hn_rndis_get_eaddr(struct hn_softc *sc, uint8_t *eaddr)
521 eaddr_len = ETHER_ADDR_LEN;
522 error = hn_rndis_query(sc, OID_802_3_PERMANENT_ADDRESS, NULL, 0,
526 if (eaddr_len != ETHER_ADDR_LEN) {
527 if_printf(sc->hn_ifp, "invalid eaddr len %zu\n", eaddr_len);
534 hn_rndis_get_linkstatus(struct hn_softc *sc, uint32_t *link_status)
539 size = sizeof(*link_status);
540 error = hn_rndis_query(sc, OID_GEN_MEDIA_CONNECT_STATUS, NULL, 0,
544 if (size != sizeof(uint32_t)) {
545 if_printf(sc->hn_ifp, "invalid link status len %zu\n", size);
552 hn_rndis_xact_exec1(struct hn_softc *sc, struct vmbus_xact *xact, size_t reqlen,
553 struct hn_send_ctx *sndc, size_t *comp_len)
555 struct vmbus_gpa gpa[HN_XACT_REQ_PGCNT];
559 KASSERT(reqlen <= HN_XACT_REQ_SIZE && reqlen > 0,
560 ("invalid request length %zu", reqlen));
565 paddr = vmbus_xact_req_paddr(xact);
566 KASSERT((paddr & PAGE_MASK) == 0,
567 ("vmbus xact request is not page aligned 0x%jx", (uintmax_t)paddr));
568 for (gpa_cnt = 0; gpa_cnt < HN_XACT_REQ_PGCNT; ++gpa_cnt) {
576 gpa[gpa_cnt].gpa_page = atop(paddr) + gpa_cnt;
577 gpa[gpa_cnt].gpa_len = len;
578 gpa[gpa_cnt].gpa_ofs = 0;
582 KASSERT(reqlen == 0, ("still have %zu request data left", reqlen));
585 * Send this RNDIS control message and wait for its completion
588 vmbus_xact_activate(xact);
589 error = hn_nvs_send_rndis_ctrl(sc->hn_prichan, sndc, gpa, gpa_cnt);
591 vmbus_xact_deactivate(xact);
592 if_printf(sc->hn_ifp, "RNDIS ctrl send failed: %d\n", error);
595 return (vmbus_xact_wait(xact, comp_len));
599 hn_rndis_xact_execute(struct hn_softc *sc, struct vmbus_xact *xact, uint32_t rid,
600 size_t reqlen, size_t *comp_len0, uint32_t comp_type)
602 const struct rndis_comp_hdr *comp;
603 size_t comp_len, min_complen = *comp_len0;
605 KASSERT(rid > HN_RNDIS_RID_COMPAT_MAX, ("invalid rid %u\n", rid));
606 KASSERT(min_complen >= sizeof(*comp),
607 ("invalid minimum complete len %zu", min_complen));
610 * Execute the xact setup by the caller.
612 comp = hn_rndis_xact_exec1(sc, xact, reqlen, &hn_send_ctx_none,
618 * Check this RNDIS complete message.
620 if (comp_len < min_complen) {
621 if (comp_len >= sizeof(*comp)) {
622 /* rm_status field is valid */
623 if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu, "
624 "status 0x%08x\n", comp_len, comp->rm_status);
626 if_printf(sc->hn_ifp, "invalid RNDIS comp len %zu\n",
631 if (comp->rm_len < min_complen) {
632 if_printf(sc->hn_ifp, "invalid RNDIS comp msglen %u\n",
636 if (comp->rm_type != comp_type) {
637 if_printf(sc->hn_ifp, "unexpected RNDIS comp 0x%08x, "
638 "expect 0x%08x\n", comp->rm_type, comp_type);
641 if (comp->rm_rid != rid) {
642 if_printf(sc->hn_ifp, "RNDIS comp rid mismatch %u, "
643 "expect %u\n", comp->rm_rid, rid);
647 *comp_len0 = comp_len;
652 hn_rndis_query(struct hn_softc *sc, uint32_t oid,
653 const void *idata, size_t idlen, void *odata, size_t *odlen0)
656 return (hn_rndis_query2(sc, oid, idata, idlen, odata, odlen0, *odlen0));
660 hn_rndis_query2(struct hn_softc *sc, uint32_t oid,
661 const void *idata, size_t idlen, void *odata, size_t *odlen0,
664 struct rndis_query_req *req;
665 const struct rndis_query_comp *comp;
666 struct vmbus_xact *xact;
667 size_t reqlen, odlen = *odlen0, comp_len;
671 reqlen = sizeof(*req) + idlen;
672 xact = vmbus_xact_get(sc->hn_xact, reqlen);
674 if_printf(sc->hn_ifp, "no xact for RNDIS query 0x%08x\n", oid);
677 rid = hn_rndis_rid(sc);
678 req = vmbus_xact_req_data(xact);
679 req->rm_type = REMOTE_NDIS_QUERY_MSG;
680 req->rm_len = reqlen;
685 * This is _not_ RNDIS Spec conforming:
686 * "This MUST be set to 0 when there is no input data
687 * associated with the OID."
689 * If this field was set to 0 according to the RNDIS Spec,
690 * Hyper-V would set non-SUCCESS status in the query
693 req->rm_infobufoffset = RNDIS_QUERY_REQ_INFOBUFOFFSET;
696 req->rm_infobuflen = idlen;
697 /* Input data immediately follows RNDIS query. */
698 memcpy(req + 1, idata, idlen);
701 comp_len = sizeof(*comp) + min_odlen;
702 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
703 REMOTE_NDIS_QUERY_CMPLT);
705 if_printf(sc->hn_ifp, "exec RNDIS query 0x%08x failed\n", oid);
710 if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
711 if_printf(sc->hn_ifp, "RNDIS query 0x%08x failed: "
712 "status 0x%08x\n", oid, comp->rm_status);
716 if (comp->rm_infobuflen == 0 || comp->rm_infobufoffset == 0) {
717 /* No output data! */
718 if_printf(sc->hn_ifp, "RNDIS query 0x%08x, no data\n", oid);
725 * Check output data length and offset.
727 /* ofs is the offset from the beginning of comp. */
728 ofs = RNDIS_QUERY_COMP_INFOBUFOFFSET_ABS(comp->rm_infobufoffset);
729 if (ofs < sizeof(*comp) || ofs + comp->rm_infobuflen > comp_len) {
730 if_printf(sc->hn_ifp, "RNDIS query invalid comp ib off/len, "
731 "%u/%u\n", comp->rm_infobufoffset, comp->rm_infobuflen);
739 if (comp->rm_infobuflen < odlen)
740 odlen = comp->rm_infobuflen;
741 memcpy(odata, ((const uint8_t *)comp) + ofs, odlen);
746 vmbus_xact_put(xact);
751 hn_rndis_query_rsscaps(struct hn_softc *sc, int *rxr_cnt0)
753 struct ndis_rss_caps in, caps;
755 int error, indsz, rxr_cnt, hash_fnidx;
756 uint32_t hash_func = 0, hash_types = 0;
760 if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_20)
763 memset(&in, 0, sizeof(in));
764 in.ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_CAPS;
765 in.ndis_hdr.ndis_rev = NDIS_RSS_CAPS_REV_2;
766 in.ndis_hdr.ndis_size = NDIS_RSS_CAPS_SIZE;
768 caps_len = NDIS_RSS_CAPS_SIZE;
769 error = hn_rndis_query2(sc, OID_GEN_RECEIVE_SCALE_CAPABILITIES,
770 &in, NDIS_RSS_CAPS_SIZE, &caps, &caps_len, NDIS_RSS_CAPS_SIZE_6_0);
775 * Preliminary verification.
777 if (caps.ndis_hdr.ndis_type != NDIS_OBJTYPE_RSS_CAPS) {
778 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
779 caps.ndis_hdr.ndis_type);
782 if (caps.ndis_hdr.ndis_rev < NDIS_RSS_CAPS_REV_1) {
783 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
784 caps.ndis_hdr.ndis_rev);
787 if (caps.ndis_hdr.ndis_size > caps_len) {
788 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
789 "data size %zu\n", caps.ndis_hdr.ndis_size, caps_len);
791 } else if (caps.ndis_hdr.ndis_size < NDIS_RSS_CAPS_SIZE_6_0) {
792 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
793 caps.ndis_hdr.ndis_size);
798 * Save information for later RSS configuration.
800 if (caps.ndis_nrxr == 0) {
801 if_printf(sc->hn_ifp, "0 RX rings!?\n");
805 if_printf(sc->hn_ifp, "%u RX rings\n", caps.ndis_nrxr);
806 rxr_cnt = caps.ndis_nrxr;
808 if (caps.ndis_hdr.ndis_size == NDIS_RSS_CAPS_SIZE &&
809 caps.ndis_hdr.ndis_rev >= NDIS_RSS_CAPS_REV_2) {
810 if (caps.ndis_nind > NDIS_HASH_INDCNT) {
811 if_printf(sc->hn_ifp,
812 "too many RSS indirect table entries %u\n",
816 if (!powerof2(caps.ndis_nind)) {
817 if_printf(sc->hn_ifp, "RSS indirect table size is not "
818 "power-of-2 %u\n", caps.ndis_nind);
822 if_printf(sc->hn_ifp, "RSS indirect table size %u\n",
825 indsz = caps.ndis_nind;
827 indsz = NDIS_HASH_INDCNT;
829 if (indsz < rxr_cnt) {
830 if_printf(sc->hn_ifp, "# of RX rings (%d) > "
831 "RSS indirect table size %d\n", rxr_cnt, indsz);
837 * Toeplitz is at the lowest bit, and it is prefered; so ffs(),
838 * instead of fls(), is used here.
840 hash_fnidx = ffs(caps.ndis_caps & NDIS_RSS_CAP_HASHFUNC_MASK);
841 if (hash_fnidx == 0) {
842 if_printf(sc->hn_ifp, "no hash functions, caps 0x%08x\n",
846 hash_func = 1 << (hash_fnidx - 1); /* ffs is 1-based */
848 if (caps.ndis_caps & NDIS_RSS_CAP_IPV4)
849 hash_types |= NDIS_HASH_IPV4 | NDIS_HASH_TCP_IPV4;
850 if (caps.ndis_caps & NDIS_RSS_CAP_IPV6)
851 hash_types |= NDIS_HASH_IPV6 | NDIS_HASH_TCP_IPV6;
852 if (caps.ndis_caps & NDIS_RSS_CAP_IPV6_EX)
853 hash_types |= NDIS_HASH_IPV6_EX | NDIS_HASH_TCP_IPV6_EX;
854 if (hash_types == 0) {
855 if_printf(sc->hn_ifp, "no hash types, caps 0x%08x\n",
861 sc->hn_rss_ind_size = indsz;
862 sc->hn_rss_hash = hash_func | hash_types;
868 hn_rndis_set(struct hn_softc *sc, uint32_t oid, const void *data, size_t dlen)
870 struct rndis_set_req *req;
871 const struct rndis_set_comp *comp;
872 struct vmbus_xact *xact;
873 size_t reqlen, comp_len;
877 KASSERT(dlen > 0, ("invalid dlen %zu", dlen));
879 reqlen = sizeof(*req) + dlen;
880 xact = vmbus_xact_get(sc->hn_xact, reqlen);
882 if_printf(sc->hn_ifp, "no xact for RNDIS set 0x%08x\n", oid);
885 rid = hn_rndis_rid(sc);
886 req = vmbus_xact_req_data(xact);
887 req->rm_type = REMOTE_NDIS_SET_MSG;
888 req->rm_len = reqlen;
891 req->rm_infobuflen = dlen;
892 req->rm_infobufoffset = RNDIS_SET_REQ_INFOBUFOFFSET;
893 /* Data immediately follows RNDIS set. */
894 memcpy(req + 1, data, dlen);
896 comp_len = sizeof(*comp);
897 comp = hn_rndis_xact_execute(sc, xact, rid, reqlen, &comp_len,
898 REMOTE_NDIS_SET_CMPLT);
900 if_printf(sc->hn_ifp, "exec RNDIS set 0x%08x failed\n", oid);
905 if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
906 if_printf(sc->hn_ifp, "RNDIS set 0x%08x failed: "
907 "status 0x%08x\n", oid, comp->rm_status);
913 vmbus_xact_put(xact);
918 hn_rndis_conf_offload(struct hn_softc *sc, int mtu)
920 struct ndis_offload hwcaps;
921 struct ndis_offload_params params;
924 int error, tso_maxsz, tso_minsg;
926 error = hn_rndis_query_hwcaps(sc, &hwcaps);
928 if_printf(sc->hn_ifp, "hwcaps query failed: %d\n", error);
932 /* NOTE: 0 means "no change" */
933 memset(¶ms, 0, sizeof(params));
935 params.ndis_hdr.ndis_type = NDIS_OBJTYPE_DEFAULT;
936 if (sc->hn_ndis_ver < HN_NDIS_VERSION_6_30) {
937 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_2;
938 paramsz = NDIS_OFFLOAD_PARAMS_SIZE_6_1;
940 params.ndis_hdr.ndis_rev = NDIS_OFFLOAD_PARAMS_REV_3;
941 paramsz = NDIS_OFFLOAD_PARAMS_SIZE;
943 params.ndis_hdr.ndis_size = paramsz;
948 tso_maxsz = IP_MAXPACKET;
950 if (hwcaps.ndis_lsov2.ndis_ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
952 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_ON;
954 if (hwcaps.ndis_lsov2.ndis_ip4_maxsz < tso_maxsz)
955 tso_maxsz = hwcaps.ndis_lsov2.ndis_ip4_maxsz;
956 if (hwcaps.ndis_lsov2.ndis_ip4_minsg > tso_minsg)
957 tso_minsg = hwcaps.ndis_lsov2.ndis_ip4_minsg;
959 if ((hwcaps.ndis_lsov2.ndis_ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
960 (hwcaps.ndis_lsov2.ndis_ip6_opts & HN_NDIS_LSOV2_CAP_IP6) ==
961 HN_NDIS_LSOV2_CAP_IP6) {
964 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_ON;
966 if (hwcaps.ndis_lsov2.ndis_ip6_maxsz < tso_maxsz)
967 tso_maxsz = hwcaps.ndis_lsov2.ndis_ip6_maxsz;
968 if (hwcaps.ndis_lsov2.ndis_ip6_minsg > tso_minsg)
969 tso_minsg = hwcaps.ndis_lsov2.ndis_ip6_minsg;
972 sc->hn_ndis_tso_szmax = 0;
973 sc->hn_ndis_tso_sgmin = 0;
974 if (caps & (HN_CAP_TSO4 | HN_CAP_TSO6)) {
975 KASSERT(tso_maxsz <= IP_MAXPACKET,
976 ("invalid NDIS TSO maxsz %d", tso_maxsz));
977 KASSERT(tso_minsg >= 2,
978 ("invalid NDIS TSO minsg %d", tso_minsg));
979 if (tso_maxsz < tso_minsg * mtu) {
980 if_printf(sc->hn_ifp, "invalid NDIS TSO config: "
981 "maxsz %d, minsg %d, mtu %d; "
982 "disable TSO4 and TSO6\n",
983 tso_maxsz, tso_minsg, mtu);
984 caps &= ~(HN_CAP_TSO4 | HN_CAP_TSO6);
985 params.ndis_lsov2_ip4 = NDIS_OFFLOAD_LSOV2_OFF;
986 params.ndis_lsov2_ip6 = NDIS_OFFLOAD_LSOV2_OFF;
988 sc->hn_ndis_tso_szmax = tso_maxsz;
989 sc->hn_ndis_tso_sgmin = tso_minsg;
991 if_printf(sc->hn_ifp, "NDIS TSO "
992 "szmax %d sgmin %d\n",
993 sc->hn_ndis_tso_szmax,
994 sc->hn_ndis_tso_sgmin);
1000 if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_IP4) ==
1001 HN_NDIS_TXCSUM_CAP_IP4) {
1002 caps |= HN_CAP_IPCS;
1003 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TX;
1005 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_IP4) {
1006 if (params.ndis_ip4csum == NDIS_OFFLOAD_PARAM_TX)
1007 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_TXRX;
1009 params.ndis_ip4csum = NDIS_OFFLOAD_PARAM_RX;
1013 if ((hwcaps.ndis_csum.ndis_ip4_txcsum & HN_NDIS_TXCSUM_CAP_TCP4) ==
1014 HN_NDIS_TXCSUM_CAP_TCP4) {
1015 caps |= HN_CAP_TCP4CS;
1016 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TX;
1018 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_TCP4) {
1019 if (params.ndis_tcp4csum == NDIS_OFFLOAD_PARAM_TX)
1020 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_TXRX;
1022 params.ndis_tcp4csum = NDIS_OFFLOAD_PARAM_RX;
1026 if (hwcaps.ndis_csum.ndis_ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
1027 caps |= HN_CAP_UDP4CS;
1028 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TX;
1030 if (hwcaps.ndis_csum.ndis_ip4_rxcsum & NDIS_RXCSUM_CAP_UDP4) {
1031 if (params.ndis_udp4csum == NDIS_OFFLOAD_PARAM_TX)
1032 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_TXRX;
1034 params.ndis_udp4csum = NDIS_OFFLOAD_PARAM_RX;
1038 if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_TCP6) ==
1039 HN_NDIS_TXCSUM_CAP_TCP6) {
1040 caps |= HN_CAP_TCP6CS;
1041 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TX;
1043 if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_TCP6) {
1044 if (params.ndis_tcp6csum == NDIS_OFFLOAD_PARAM_TX)
1045 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_TXRX;
1047 params.ndis_tcp6csum = NDIS_OFFLOAD_PARAM_RX;
1051 if ((hwcaps.ndis_csum.ndis_ip6_txcsum & HN_NDIS_TXCSUM_CAP_UDP6) ==
1052 HN_NDIS_TXCSUM_CAP_UDP6) {
1053 caps |= HN_CAP_UDP6CS;
1054 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TX;
1056 if (hwcaps.ndis_csum.ndis_ip6_rxcsum & NDIS_RXCSUM_CAP_UDP6) {
1057 if (params.ndis_udp6csum == NDIS_OFFLOAD_PARAM_TX)
1058 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_TXRX;
1060 params.ndis_udp6csum = NDIS_OFFLOAD_PARAM_RX;
1064 if_printf(sc->hn_ifp, "offload csum: "
1065 "ip4 %u, tcp4 %u, udp4 %u, tcp6 %u, udp6 %u\n",
1066 params.ndis_ip4csum,
1067 params.ndis_tcp4csum,
1068 params.ndis_udp4csum,
1069 params.ndis_tcp6csum,
1070 params.ndis_udp6csum);
1071 if_printf(sc->hn_ifp, "offload lsov2: ip4 %u, ip6 %u\n",
1072 params.ndis_lsov2_ip4,
1073 params.ndis_lsov2_ip6);
1076 error = hn_rndis_set(sc, OID_TCP_OFFLOAD_PARAMETERS, ¶ms, paramsz);
1078 if_printf(sc->hn_ifp, "offload config failed: %d\n", error);
1083 if_printf(sc->hn_ifp, "offload config done\n");
1084 sc->hn_caps |= caps;
1089 hn_rndis_conf_rss(struct hn_softc *sc, uint16_t flags)
1091 struct ndis_rssprm_toeplitz *rss = &sc->hn_rss;
1092 struct ndis_rss_params *prm = &rss->rss_params;
1093 int error, rss_size;
1096 * Only NDIS 6.20+ is supported:
1097 * We only support 4bytes element in indirect table, which has been
1098 * adopted since NDIS 6.20.
1100 KASSERT(sc->hn_ndis_ver >= HN_NDIS_VERSION_6_20,
1101 ("NDIS 6.20+ is required, NDIS version 0x%08x", sc->hn_ndis_ver));
1103 /* XXX only one can be specified through, popcnt? */
1104 KASSERT((sc->hn_rss_hash & NDIS_HASH_FUNCTION_MASK), ("no hash func"));
1105 KASSERT((sc->hn_rss_hash & NDIS_HASH_TYPE_MASK), ("no hash types"));
1106 KASSERT(sc->hn_rss_ind_size > 0, ("no indirect table size"));
1109 if_printf(sc->hn_ifp, "RSS indirect table size %d, "
1110 "hash 0x%08x\n", sc->hn_rss_ind_size, sc->hn_rss_hash);
1115 * DO NOT whack rss_key and rss_ind, which are setup by the caller.
1117 memset(prm, 0, sizeof(*prm));
1118 rss_size = NDIS_RSSPRM_TOEPLITZ_SIZE(sc->hn_rss_ind_size);
1120 prm->ndis_hdr.ndis_type = NDIS_OBJTYPE_RSS_PARAMS;
1121 prm->ndis_hdr.ndis_rev = NDIS_RSS_PARAMS_REV_2;
1122 prm->ndis_hdr.ndis_size = rss_size;
1123 prm->ndis_flags = flags;
1124 prm->ndis_hash = sc->hn_rss_hash;
1125 prm->ndis_indsize = sizeof(rss->rss_ind[0]) * sc->hn_rss_ind_size;
1126 prm->ndis_indoffset =
1127 __offsetof(struct ndis_rssprm_toeplitz, rss_ind[0]);
1128 prm->ndis_keysize = sizeof(rss->rss_key);
1129 prm->ndis_keyoffset =
1130 __offsetof(struct ndis_rssprm_toeplitz, rss_key[0]);
1132 error = hn_rndis_set(sc, OID_GEN_RECEIVE_SCALE_PARAMETERS,
1135 if_printf(sc->hn_ifp, "RSS config failed: %d\n", error);
1138 if_printf(sc->hn_ifp, "RSS config done\n");
1144 hn_rndis_set_rxfilter(struct hn_softc *sc, uint32_t filter)
1148 error = hn_rndis_set(sc, OID_GEN_CURRENT_PACKET_FILTER,
1149 &filter, sizeof(filter));
1151 if_printf(sc->hn_ifp, "set RX filter 0x%08x failed: %d\n",
1155 if_printf(sc->hn_ifp, "set RX filter 0x%08x done\n",
1163 hn_rndis_init(struct hn_softc *sc)
1165 struct rndis_init_req *req;
1166 const struct rndis_init_comp *comp;
1167 struct vmbus_xact *xact;
1172 xact = vmbus_xact_get(sc->hn_xact, sizeof(*req));
1174 if_printf(sc->hn_ifp, "no xact for RNDIS init\n");
1177 rid = hn_rndis_rid(sc);
1178 req = vmbus_xact_req_data(xact);
1179 req->rm_type = REMOTE_NDIS_INITIALIZE_MSG;
1180 req->rm_len = sizeof(*req);
1182 req->rm_ver_major = RNDIS_VERSION_MAJOR;
1183 req->rm_ver_minor = RNDIS_VERSION_MINOR;
1184 req->rm_max_xfersz = HN_RNDIS_XFER_SIZE;
1186 comp_len = RNDIS_INIT_COMP_SIZE_MIN;
1187 comp = hn_rndis_xact_execute(sc, xact, rid, sizeof(*req), &comp_len,
1188 REMOTE_NDIS_INITIALIZE_CMPLT);
1190 if_printf(sc->hn_ifp, "exec RNDIS init failed\n");
1195 if (comp->rm_status != RNDIS_STATUS_SUCCESS) {
1196 if_printf(sc->hn_ifp, "RNDIS init failed: status 0x%08x\n",
1202 if_printf(sc->hn_ifp, "RNDIS ver %u.%u, pktsz %u, pktcnt %u, "
1203 "align %u\n", comp->rm_ver_major, comp->rm_ver_minor,
1204 comp->rm_pktmaxsz, comp->rm_pktmaxcnt,
1205 1U << comp->rm_align);
1209 vmbus_xact_put(xact);
1214 hn_rndis_halt(struct hn_softc *sc)
1216 struct vmbus_xact *xact;
1217 struct rndis_halt_req *halt;
1218 struct hn_send_ctx sndc;
1221 xact = vmbus_xact_get(sc->hn_xact, sizeof(*halt));
1223 if_printf(sc->hn_ifp, "no xact for RNDIS halt\n");
1226 halt = vmbus_xact_req_data(xact);
1227 halt->rm_type = REMOTE_NDIS_HALT_MSG;
1228 halt->rm_len = sizeof(*halt);
1229 halt->rm_rid = hn_rndis_rid(sc);
1231 /* No RNDIS completion; rely on NVS message send completion */
1232 hn_send_ctx_init(&sndc, hn_nvs_sent_xact, xact);
1233 hn_rndis_xact_exec1(sc, xact, sizeof(*halt), &sndc, &comp_len);
1235 vmbus_xact_put(xact);
1237 if_printf(sc->hn_ifp, "RNDIS halt done\n");
1242 hn_rndis_query_hwcaps(struct hn_softc *sc, struct ndis_offload *caps)
1244 struct ndis_offload in;
1245 size_t caps_len, size;
1248 memset(&in, 0, sizeof(in));
1249 in.ndis_hdr.ndis_type = NDIS_OBJTYPE_OFFLOAD;
1250 if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_30) {
1251 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_3;
1252 size = NDIS_OFFLOAD_SIZE;
1253 } else if (sc->hn_ndis_ver >= HN_NDIS_VERSION_6_1) {
1254 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_2;
1255 size = NDIS_OFFLOAD_SIZE_6_1;
1257 in.ndis_hdr.ndis_rev = NDIS_OFFLOAD_REV_1;
1258 size = NDIS_OFFLOAD_SIZE_6_0;
1260 in.ndis_hdr.ndis_size = size;
1262 caps_len = NDIS_OFFLOAD_SIZE;
1263 error = hn_rndis_query2(sc, OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
1264 &in, size, caps, &caps_len, NDIS_OFFLOAD_SIZE_6_0);
1269 * Preliminary verification.
1271 if (caps->ndis_hdr.ndis_type != NDIS_OBJTYPE_OFFLOAD) {
1272 if_printf(sc->hn_ifp, "invalid NDIS objtype 0x%02x\n",
1273 caps->ndis_hdr.ndis_type);
1276 if (caps->ndis_hdr.ndis_rev < NDIS_OFFLOAD_REV_1) {
1277 if_printf(sc->hn_ifp, "invalid NDIS objrev 0x%02x\n",
1278 caps->ndis_hdr.ndis_rev);
1281 if (caps->ndis_hdr.ndis_size > caps_len) {
1282 if_printf(sc->hn_ifp, "invalid NDIS objsize %u, "
1283 "data size %zu\n", caps->ndis_hdr.ndis_size, caps_len);
1285 } else if (caps->ndis_hdr.ndis_size < NDIS_OFFLOAD_SIZE_6_0) {
1286 if_printf(sc->hn_ifp, "invalid NDIS objsize %u\n",
1287 caps->ndis_hdr.ndis_size);
1294 * caps->ndis_hdr.ndis_size MUST be checked before accessing
1295 * NDIS 6.1+ specific fields.
1297 if_printf(sc->hn_ifp, "hwcaps rev %u\n",
1298 caps->ndis_hdr.ndis_rev);
1300 if_printf(sc->hn_ifp, "hwcaps csum: "
1301 "ip4 tx 0x%x/0x%x rx 0x%x/0x%x, "
1302 "ip6 tx 0x%x/0x%x rx 0x%x/0x%x\n",
1303 caps->ndis_csum.ndis_ip4_txcsum,
1304 caps->ndis_csum.ndis_ip4_txenc,
1305 caps->ndis_csum.ndis_ip4_rxcsum,
1306 caps->ndis_csum.ndis_ip4_rxenc,
1307 caps->ndis_csum.ndis_ip6_txcsum,
1308 caps->ndis_csum.ndis_ip6_txenc,
1309 caps->ndis_csum.ndis_ip6_rxcsum,
1310 caps->ndis_csum.ndis_ip6_rxenc);
1311 if_printf(sc->hn_ifp, "hwcaps lsov2: "
1312 "ip4 maxsz %u minsg %u encap 0x%x, "
1313 "ip6 maxsz %u minsg %u encap 0x%x opts 0x%x\n",
1314 caps->ndis_lsov2.ndis_ip4_maxsz,
1315 caps->ndis_lsov2.ndis_ip4_minsg,
1316 caps->ndis_lsov2.ndis_ip4_encap,
1317 caps->ndis_lsov2.ndis_ip6_maxsz,
1318 caps->ndis_lsov2.ndis_ip6_minsg,
1319 caps->ndis_lsov2.ndis_ip6_encap,
1320 caps->ndis_lsov2.ndis_ip6_opts);
1326 hn_rndis_attach(struct hn_softc *sc, int mtu)
1333 error = hn_rndis_init(sc);
1338 * Configure NDIS offload settings.
1339 * XXX no offloading, if error happened?
1341 hn_rndis_conf_offload(sc, mtu);
1346 hn_rndis_detach(struct hn_softc *sc)
1349 /* Halt the RNDIS. */
1354 hv_rf_channel_rollup(struct hn_rx_ring *rxr, struct hn_tx_ring *txr)
1357 hn_chan_rollup(rxr, txr);