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