]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - sys/dev/hyperv/netvsc/hv_rndis_filter.c
MFC 295307,295308,295309,295606
[FreeBSD/stable/10.git] / sys / dev / hyperv / netvsc / hv_rndis_filter.c
1 /*-
2  * Copyright (c) 2009-2012 Microsoft Corp.
3  * Copyright (c) 2010-2012 Citrix Inc.
4  * Copyright (c) 2012 NetApp Inc.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice unmodified, this list of conditions, and the following
12  *    disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/mbuf.h>
35 #include <sys/socket.h>
36 #include <sys/lock.h>
37 #include <sys/mutex.h>
38 #include <net/if_arp.h>
39 #include <net/ethernet.h>
40 #include <sys/types.h>
41 #include <machine/atomic.h>
42 #include <sys/sema.h>
43 #include <vm/vm.h>
44 #include <vm/vm_param.h>
45 #include <vm/pmap.h>
46
47 #include <dev/hyperv/include/hyperv.h>
48 #include "hv_net_vsc.h"
49 #include "hv_rndis.h"
50 #include "hv_rndis_filter.h"
51
52
53 /*
54  * Forward declarations
55  */
56 static int  hv_rf_send_request(rndis_device *device, rndis_request *request,
57                                uint32_t message_type);
58 static void hv_rf_receive_response(rndis_device *device, rndis_msg *response);
59 static void hv_rf_receive_indicate_status(rndis_device *device,
60                                           rndis_msg *response);
61 static void hv_rf_receive_data(rndis_device *device, rndis_msg *message,
62                                netvsc_packet *pkt);
63 static int  hv_rf_query_device(rndis_device *device, uint32_t oid,
64                                void *result, uint32_t *result_size);
65 static inline int hv_rf_query_device_mac(rndis_device *device);
66 static inline int hv_rf_query_device_link_status(rndis_device *device);
67 static int  hv_rf_set_packet_filter(rndis_device *device, uint32_t new_filter);
68 static int  hv_rf_init_device(rndis_device *device);
69 static int  hv_rf_open_device(rndis_device *device);
70 static int  hv_rf_close_device(rndis_device *device);
71 static void hv_rf_on_send_request_completion(void *context);
72 static void hv_rf_on_send_request_halt_completion(void *context);
73 int
74 hv_rf_send_offload_request(struct hv_device *device,
75     rndis_offload_params *offloads);
76 /*
77  * Set the Per-Packet-Info with the specified type
78  */
79 void *
80 hv_set_rppi_data(rndis_msg *rndis_mesg, uint32_t rppi_size,
81         int pkt_type)
82 {
83         rndis_packet *rndis_pkt;
84         rndis_per_packet_info *rppi;
85
86         rndis_pkt = &rndis_mesg->msg.packet;
87         rndis_pkt->data_offset += rppi_size;
88
89         rppi = (rndis_per_packet_info *)((char *)rndis_pkt +
90             rndis_pkt->per_pkt_info_offset + rndis_pkt->per_pkt_info_length);
91
92         rppi->size = rppi_size;
93         rppi->type = pkt_type;
94         rppi->per_packet_info_offset = sizeof(rndis_per_packet_info);
95
96         rndis_pkt->per_pkt_info_length += rppi_size;
97
98         return (rppi);
99 }
100
101 /*
102  * Get the Per-Packet-Info with the specified type
103  * return NULL if not found.
104  */
105 void *
106 hv_get_ppi_data(rndis_packet *rpkt, uint32_t type)
107 {
108         rndis_per_packet_info *ppi;
109         int len;
110
111         if (rpkt->per_pkt_info_offset == 0)
112                 return (NULL);
113
114         ppi = (rndis_per_packet_info *)((unsigned long)rpkt +
115             rpkt->per_pkt_info_offset);
116         len = rpkt->per_pkt_info_length;
117
118         while (len > 0) {
119                 if (ppi->type == type)
120                         return (void *)((unsigned long)ppi +
121                             ppi->per_packet_info_offset);
122
123                 len -= ppi->size;
124                 ppi = (rndis_per_packet_info *)((unsigned long)ppi + ppi->size);
125         }
126
127         return (NULL);
128 }
129
130
131 /*
132  * Allow module_param to work and override to switch to promiscuous mode.
133  */
134 static inline rndis_device *
135 hv_get_rndis_device(void)
136 {
137         rndis_device *device;
138
139         device = malloc(sizeof(rndis_device), M_NETVSC, M_WAITOK | M_ZERO);
140
141         mtx_init(&device->req_lock, "HV-FRL", NULL, MTX_DEF);
142
143         /* Same effect as STAILQ_HEAD_INITIALIZER() static initializer */
144         STAILQ_INIT(&device->myrequest_list);
145
146         device->state = RNDIS_DEV_UNINITIALIZED;
147
148         return (device);
149 }
150
151 /*
152  *
153  */
154 static inline void
155 hv_put_rndis_device(rndis_device *device)
156 {
157         mtx_destroy(&device->req_lock);
158         free(device, M_NETVSC);
159 }
160
161 /*
162  *
163  */
164 static inline rndis_request *
165 hv_rndis_request(rndis_device *device, uint32_t message_type,
166                  uint32_t message_length)
167 {
168         rndis_request *request;
169         rndis_msg *rndis_mesg;
170         rndis_set_request *set;
171
172         request = malloc(sizeof(rndis_request), M_NETVSC, M_WAITOK | M_ZERO);
173
174         sema_init(&request->wait_sema, 0, "rndis sema");
175         
176         rndis_mesg = &request->request_msg;
177         rndis_mesg->ndis_msg_type = message_type;
178         rndis_mesg->msg_len = message_length;
179
180         /*
181          * Set the request id. This field is always after the rndis header
182          * for request/response packet types so we just use the set_request
183          * as a template.
184          */
185         set = &rndis_mesg->msg.set_request;
186         set->request_id = atomic_fetchadd_int(&device->new_request_id, 1);
187         /* Increment to get the new value (call above returns old value) */
188         set->request_id += 1;
189
190         /* Add to the request list */
191         mtx_lock(&device->req_lock);
192         STAILQ_INSERT_TAIL(&device->myrequest_list, request, mylist_entry);
193         mtx_unlock(&device->req_lock);
194
195         return (request);
196 }
197
198 /*
199  *
200  */
201 static inline void
202 hv_put_rndis_request(rndis_device *device, rndis_request *request)
203 {
204         mtx_lock(&device->req_lock);
205         /* Fixme:  Has O(n) performance */
206         /*
207          * XXXKYS: Use Doubly linked lists.
208          */
209         STAILQ_REMOVE(&device->myrequest_list, request, rndis_request_,
210             mylist_entry);
211         mtx_unlock(&device->req_lock);
212
213         sema_destroy(&request->wait_sema);
214         free(request, M_NETVSC);
215 }
216
217 /*
218  *
219  */
220 static int
221 hv_rf_send_request(rndis_device *device, rndis_request *request,
222     uint32_t message_type)
223 {
224         int ret;
225         netvsc_packet *packet;
226
227         /* Set up the packet to send it */
228         packet = &request->pkt;
229         
230         packet->is_data_pkt = FALSE;
231         packet->tot_data_buf_len = request->request_msg.msg_len;
232         packet->page_buf_count = 1;
233
234         packet->page_buffers[0].pfn =
235             hv_get_phys_addr(&request->request_msg) >> PAGE_SHIFT;
236         packet->page_buffers[0].length = request->request_msg.msg_len;
237         packet->page_buffers[0].offset =
238             (unsigned long)&request->request_msg & (PAGE_SIZE - 1);
239
240         packet->compl.send.send_completion_context = request; /* packet */
241         if (message_type != REMOTE_NDIS_HALT_MSG) {
242                 packet->compl.send.on_send_completion =
243                     hv_rf_on_send_request_completion;
244         } else {
245                 packet->compl.send.on_send_completion =
246                     hv_rf_on_send_request_halt_completion;
247         }
248         packet->compl.send.send_completion_tid = (unsigned long)device;
249         packet->send_buf_section_idx =
250             NVSP_1_CHIMNEY_SEND_INVALID_SECTION_INDEX;
251         packet->send_buf_section_size = 0;
252
253         ret = hv_nv_on_send(device->net_dev->dev, packet);
254
255         return (ret);
256 }
257
258 /*
259  * RNDIS filter receive response
260  */
261 static void 
262 hv_rf_receive_response(rndis_device *device, rndis_msg *response)
263 {
264         rndis_request *request = NULL;
265         rndis_request *next_request;
266         boolean_t found = FALSE;
267
268         mtx_lock(&device->req_lock);
269         request = STAILQ_FIRST(&device->myrequest_list);
270         while (request != NULL) {
271                 /*
272                  * All request/response message contains request_id as the
273                  * first field
274                  */
275                 if (request->request_msg.msg.init_request.request_id ==
276                                       response->msg.init_complete.request_id) {
277                         found = TRUE;
278                         break;
279                 }
280                 next_request = STAILQ_NEXT(request, mylist_entry);
281                 request = next_request;
282         }
283         mtx_unlock(&device->req_lock);
284
285         if (found) {
286                 if (response->msg_len <= sizeof(rndis_msg)) {
287                         memcpy(&request->response_msg, response,
288                             response->msg_len);
289                 } else {
290                         if (response->ndis_msg_type == REMOTE_NDIS_RESET_CMPLT) {
291                                 /* Does not have a request id field */
292                                 request->response_msg.msg.reset_complete.status =
293                                     STATUS_BUFFER_OVERFLOW;
294                         } else {
295                                 request->response_msg.msg.init_complete.status =
296                                     STATUS_BUFFER_OVERFLOW;
297                         }
298                 }
299
300                 sema_post(&request->wait_sema);
301         }
302 }
303
304 int
305 hv_rf_send_offload_request(struct hv_device *device,
306     rndis_offload_params *offloads)
307 {
308         rndis_request *request;
309         rndis_set_request *set;
310         rndis_offload_params *offload_req;
311         rndis_set_complete *set_complete;       
312         rndis_device *rndis_dev;
313         hn_softc_t *sc = device_get_softc(device->device);
314         device_t dev = device->device;
315         netvsc_dev *net_dev = sc->net_dev;
316         uint32_t vsp_version = net_dev->nvsp_version;
317         uint32_t extlen = sizeof(rndis_offload_params);
318         int ret;
319
320         if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
321                 extlen = VERSION_4_OFFLOAD_SIZE;
322                 /* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
323                  * UDP checksum offload.
324                  */
325                 offloads->udp_ipv4_csum = 0;
326                 offloads->udp_ipv6_csum = 0;
327         }
328
329         rndis_dev = net_dev->extension;
330
331         request = hv_rndis_request(rndis_dev, REMOTE_NDIS_SET_MSG,
332             RNDIS_MESSAGE_SIZE(rndis_set_request) + extlen);
333         if (!request)
334                 return (ENOMEM);
335
336         set = &request->request_msg.msg.set_request;
337         set->oid = RNDIS_OID_TCP_OFFLOAD_PARAMETERS;
338         set->info_buffer_length = extlen;
339         set->info_buffer_offset = sizeof(rndis_set_request);
340         set->device_vc_handle = 0;
341
342         offload_req = (rndis_offload_params *)((unsigned long)set +
343             set->info_buffer_offset);
344         *offload_req = *offloads;
345         offload_req->header.type = RNDIS_OBJECT_TYPE_DEFAULT;
346         offload_req->header.revision = RNDIS_OFFLOAD_PARAMETERS_REVISION_3;
347         offload_req->header.size = extlen;
348
349         ret = hv_rf_send_request(rndis_dev, request, REMOTE_NDIS_SET_MSG);
350         if (ret != 0) {
351                 device_printf(dev, "hv send offload request failed, ret=%d!\n",
352                     ret);
353                 goto cleanup;
354         }
355
356         ret = sema_timedwait(&request->wait_sema, 5 * hz);
357         if (ret != 0) {
358                 device_printf(dev, "hv send offload request timeout\n");
359                 goto cleanup;
360         }
361
362         set_complete = &request->response_msg.msg.set_complete;
363         if (set_complete->status == RNDIS_STATUS_SUCCESS) {
364                 device_printf(dev, "hv send offload request succeeded\n");
365                 ret = 0;
366         } else {
367                 if (set_complete->status == STATUS_NOT_SUPPORTED) {
368                         device_printf(dev, "HV Not support offload\n");
369                         ret = 0;
370                 } else {
371                         ret = set_complete->status;
372                 }
373         }
374
375 cleanup:
376         if (request)
377                 hv_put_rndis_request(rndis_dev, request);
378
379         return (ret);
380 }
381
382 /*
383  * RNDIS filter receive indicate status
384  */
385 static void 
386 hv_rf_receive_indicate_status(rndis_device *device, rndis_msg *response)
387 {
388         rndis_indicate_status *indicate = &response->msg.indicate_status;
389                 
390         switch(indicate->status) {
391         case RNDIS_STATUS_MEDIA_CONNECT:
392                 netvsc_linkstatus_callback(device->net_dev->dev, 1);
393                 break;
394         case RNDIS_STATUS_MEDIA_DISCONNECT:
395                 netvsc_linkstatus_callback(device->net_dev->dev, 0);
396                 break;
397         default:
398                 /* TODO: */
399                 device_printf(device->net_dev->dev->device,
400                     "unknown status %d received\n", indicate->status);
401                 break;
402         }
403 }
404
405 /*
406  * RNDIS filter receive data
407  */
408 static void
409 hv_rf_receive_data(rndis_device *device, rndis_msg *message, netvsc_packet *pkt)
410 {
411         rndis_packet *rndis_pkt;
412         ndis_8021q_info *rppi_vlan_info;
413         uint32_t data_offset;
414         rndis_tcp_ip_csum_info *csum_info = NULL;
415         device_t dev = device->net_dev->dev->device;
416
417         rndis_pkt = &message->msg.packet;
418
419         /*
420          * Fixme:  Handle multiple rndis pkt msgs that may be enclosed in this
421          * netvsc packet (ie tot_data_buf_len != message_length)
422          */
423
424         /* Remove rndis header, then pass data packet up the stack */
425         data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
426
427         pkt->tot_data_buf_len -= data_offset;
428         if (pkt->tot_data_buf_len < rndis_pkt->data_length) {
429                 pkt->status = nvsp_status_failure;
430                 device_printf(dev,
431                     "total length %u is less than data length %u\n",
432                     pkt->tot_data_buf_len, rndis_pkt->data_length);
433                 return;
434         }
435
436         pkt->tot_data_buf_len = rndis_pkt->data_length;
437         pkt->data = (void *)((unsigned long)pkt->data + data_offset);
438
439         rppi_vlan_info = hv_get_ppi_data(rndis_pkt, ieee_8021q_info);
440         if (rppi_vlan_info) {
441                 pkt->vlan_tci = rppi_vlan_info->u1.s1.vlan_id;
442         } else {
443                 pkt->vlan_tci = 0;
444         }
445
446         csum_info = hv_get_ppi_data(rndis_pkt, tcpip_chksum_info);
447         netvsc_recv(device->net_dev->dev, pkt, csum_info);
448 }
449
450 /*
451  * RNDIS filter on receive
452  */
453 int
454 hv_rf_on_receive(netvsc_dev *net_dev, struct hv_device *device, netvsc_packet *pkt)
455 {
456         rndis_device *rndis_dev;
457         rndis_msg *rndis_hdr;
458
459         /* Make sure the rndis device state is initialized */
460         if (net_dev->extension == NULL) {
461                 pkt->status = nvsp_status_failure;
462                 return (ENODEV);
463         }
464
465         rndis_dev = (rndis_device *)net_dev->extension;
466         if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) {
467                 pkt->status = nvsp_status_failure;
468                 return (EINVAL);
469         }
470
471         rndis_hdr = pkt->data;
472
473         switch (rndis_hdr->ndis_msg_type) {
474
475         /* data message */
476         case REMOTE_NDIS_PACKET_MSG:
477                 hv_rf_receive_data(rndis_dev, rndis_hdr, pkt);
478                 break;
479         /* completion messages */
480         case REMOTE_NDIS_INITIALIZE_CMPLT:
481         case REMOTE_NDIS_QUERY_CMPLT:
482         case REMOTE_NDIS_SET_CMPLT:
483         case REMOTE_NDIS_RESET_CMPLT:
484         case REMOTE_NDIS_KEEPALIVE_CMPLT:
485                 hv_rf_receive_response(rndis_dev, rndis_hdr);
486                 break;
487         /* notification message */
488         case REMOTE_NDIS_INDICATE_STATUS_MSG:
489                 hv_rf_receive_indicate_status(rndis_dev, rndis_hdr);
490                 break;
491         default:
492                 printf("hv_rf_on_receive():  Unknown msg_type 0x%x\n",
493                         rndis_hdr->ndis_msg_type);
494                 break;
495         }
496
497         return (0);
498 }
499
500 /*
501  * RNDIS filter query device
502  */
503 static int
504 hv_rf_query_device(rndis_device *device, uint32_t oid, void *result,
505                    uint32_t *result_size)
506 {
507         rndis_request *request;
508         uint32_t in_result_size = *result_size;
509         rndis_query_request *query;
510         rndis_query_complete *query_complete;
511         int ret = 0;
512
513         *result_size = 0;
514         request = hv_rndis_request(device, REMOTE_NDIS_QUERY_MSG,
515             RNDIS_MESSAGE_SIZE(rndis_query_request));
516         if (request == NULL) {
517                 ret = -1;
518                 goto cleanup;
519         }
520
521         /* Set up the rndis query */
522         query = &request->request_msg.msg.query_request;
523         query->oid = oid;
524         query->info_buffer_offset = sizeof(rndis_query_request); 
525         query->info_buffer_length = 0;
526         query->device_vc_handle = 0;
527
528         ret = hv_rf_send_request(device, request, REMOTE_NDIS_QUERY_MSG);
529         if (ret != 0) {
530                 /* Fixme:  printf added */
531                 printf("RNDISFILTER request failed to Send!\n");
532                 goto cleanup;
533         }
534
535         sema_wait(&request->wait_sema);
536
537         /* Copy the response back */
538         query_complete = &request->response_msg.msg.query_complete;
539         
540         if (query_complete->info_buffer_length > in_result_size) {
541                 ret = EINVAL;
542                 goto cleanup;
543         }
544
545         memcpy(result, (void *)((unsigned long)query_complete +
546             query_complete->info_buffer_offset),
547             query_complete->info_buffer_length);
548
549         *result_size = query_complete->info_buffer_length;
550
551 cleanup:
552         if (request != NULL)
553                 hv_put_rndis_request(device, request);
554
555         return (ret);
556 }
557
558 /*
559  * RNDIS filter query device MAC address
560  */
561 static inline int
562 hv_rf_query_device_mac(rndis_device *device)
563 {
564         uint32_t size = HW_MACADDR_LEN;
565
566         return (hv_rf_query_device(device,
567             RNDIS_OID_802_3_PERMANENT_ADDRESS, device->hw_mac_addr, &size));
568 }
569
570 /*
571  * RNDIS filter query device link status
572  */
573 static inline int
574 hv_rf_query_device_link_status(rndis_device *device)
575 {
576         uint32_t size = sizeof(uint32_t);
577
578         return (hv_rf_query_device(device,
579             RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, &device->link_status, &size));
580 }
581
582 /*
583  * RNDIS filter set packet filter
584  * Sends an rndis request with the new filter, then waits for a response
585  * from the host.
586  * Returns zero on success, non-zero on failure.
587  */
588 static int
589 hv_rf_set_packet_filter(rndis_device *device, uint32_t new_filter)
590 {
591         rndis_request *request;
592         rndis_set_request *set;
593         rndis_set_complete *set_complete;
594         uint32_t status;
595         int ret;
596
597         request = hv_rndis_request(device, REMOTE_NDIS_SET_MSG,
598             RNDIS_MESSAGE_SIZE(rndis_set_request) + sizeof(uint32_t));
599         if (request == NULL) {
600                 ret = -1;
601                 goto cleanup;
602         }
603
604         /* Set up the rndis set */
605         set = &request->request_msg.msg.set_request;
606         set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
607         set->info_buffer_length = sizeof(uint32_t);
608         set->info_buffer_offset = sizeof(rndis_set_request); 
609
610         memcpy((void *)((unsigned long)set + sizeof(rndis_set_request)),
611             &new_filter, sizeof(uint32_t));
612
613         ret = hv_rf_send_request(device, request, REMOTE_NDIS_SET_MSG);
614         if (ret != 0) {
615                 goto cleanup;
616         }
617
618         /*
619          * Wait for the response from the host.  Another thread will signal
620          * us when the response has arrived.  In the failure case,
621          * sema_timedwait() returns a non-zero status after waiting 5 seconds.
622          */
623         ret = sema_timedwait(&request->wait_sema, 5 * hz);
624         if (ret == 0) {
625                 /* Response received, check status */
626                 set_complete = &request->response_msg.msg.set_complete;
627                 status = set_complete->status;
628                 if (status != RNDIS_STATUS_SUCCESS) {
629                         /* Bad response status, return error */
630                         ret = -2;
631                 }
632         } else {
633                 /*
634                  * We cannot deallocate the request since we may still
635                  * receive a send completion for it.
636                  */
637                 goto exit;
638         }
639
640 cleanup:
641         if (request != NULL) {
642                 hv_put_rndis_request(device, request);
643         }
644 exit:
645         return (ret);
646 }
647
648 /*
649  * RNDIS filter init device
650  */
651 static int
652 hv_rf_init_device(rndis_device *device)
653 {
654         rndis_request *request;
655         rndis_initialize_request *init;
656         rndis_initialize_complete *init_complete;
657         uint32_t status;
658         int ret;
659
660         request = hv_rndis_request(device, REMOTE_NDIS_INITIALIZE_MSG,
661             RNDIS_MESSAGE_SIZE(rndis_initialize_request));
662         if (!request) {
663                 ret = -1;
664                 goto cleanup;
665         }
666
667         /* Set up the rndis set */
668         init = &request->request_msg.msg.init_request;
669         init->major_version = RNDIS_MAJOR_VERSION;
670         init->minor_version = RNDIS_MINOR_VERSION;
671         /*
672          * Per the RNDIS document, this should be set to the max MTU
673          * plus the header size.  However, 2048 works fine, so leaving
674          * it as is.
675          */
676         init->max_xfer_size = 2048;
677         
678         device->state = RNDIS_DEV_INITIALIZING;
679
680         ret = hv_rf_send_request(device, request, REMOTE_NDIS_INITIALIZE_MSG);
681         if (ret != 0) {
682                 device->state = RNDIS_DEV_UNINITIALIZED;
683                 goto cleanup;
684         }
685
686         sema_wait(&request->wait_sema);
687
688         init_complete = &request->response_msg.msg.init_complete;
689         status = init_complete->status;
690         if (status == RNDIS_STATUS_SUCCESS) {
691                 device->state = RNDIS_DEV_INITIALIZED;
692                 ret = 0;
693         } else {
694                 device->state = RNDIS_DEV_UNINITIALIZED; 
695                 ret = -1;
696         }
697
698 cleanup:
699         if (request) {
700                 hv_put_rndis_request(device, request);
701         }
702
703         return (ret);
704 }
705
706 #define HALT_COMPLETION_WAIT_COUNT      25
707
708 /*
709  * RNDIS filter halt device
710  */
711 static int
712 hv_rf_halt_device(rndis_device *device)
713 {
714         rndis_request *request;
715         rndis_halt_request *halt;
716         int i, ret;
717
718         /* Attempt to do a rndis device halt */
719         request = hv_rndis_request(device, REMOTE_NDIS_HALT_MSG,
720             RNDIS_MESSAGE_SIZE(rndis_halt_request));
721         if (request == NULL) {
722                 return (-1);
723         }
724
725         /* initialize "poor man's semaphore" */
726         request->halt_complete_flag = 0;
727
728         /* Set up the rndis set */
729         halt = &request->request_msg.msg.halt_request;
730         halt->request_id = atomic_fetchadd_int(&device->new_request_id, 1);
731         /* Increment to get the new value (call above returns old value) */
732         halt->request_id += 1;
733         
734         ret = hv_rf_send_request(device, request, REMOTE_NDIS_HALT_MSG);
735         if (ret != 0) {
736                 return (-1);
737         }
738
739         /*
740          * Wait for halt response from halt callback.  We must wait for
741          * the transaction response before freeing the request and other
742          * resources.
743          */
744         for (i=HALT_COMPLETION_WAIT_COUNT; i > 0; i--) {
745                 if (request->halt_complete_flag != 0) {
746                         break;
747                 }
748                 DELAY(400);
749         }
750         if (i == 0) {
751                 return (-1);
752         }
753
754         device->state = RNDIS_DEV_UNINITIALIZED;
755         
756         if (request != NULL) {
757                 hv_put_rndis_request(device, request);
758         }
759
760         return (0);
761 }
762
763 /*
764  * RNDIS filter open device
765  */
766 static int
767 hv_rf_open_device(rndis_device *device)
768 {
769         int ret;
770
771         if (device->state != RNDIS_DEV_INITIALIZED) {
772                 return (0);
773         }
774
775         if (hv_promisc_mode != 1) {
776                 ret = hv_rf_set_packet_filter(device, 
777                     NDIS_PACKET_TYPE_BROADCAST     |
778                     NDIS_PACKET_TYPE_ALL_MULTICAST |
779                     NDIS_PACKET_TYPE_DIRECTED);
780         } else {
781                 ret = hv_rf_set_packet_filter(device, 
782                     NDIS_PACKET_TYPE_PROMISCUOUS);
783         }
784
785         if (ret == 0) {
786                 device->state = RNDIS_DEV_DATAINITIALIZED;
787         }
788
789         return (ret);
790 }
791
792 /*
793  * RNDIS filter close device
794  */
795 static int
796 hv_rf_close_device(rndis_device *device)
797 {
798         int ret;
799
800         if (device->state != RNDIS_DEV_DATAINITIALIZED) {
801                 return (0);
802         }
803
804         ret = hv_rf_set_packet_filter(device, 0);
805         if (ret == 0) {
806                 device->state = RNDIS_DEV_INITIALIZED;
807         }
808
809         return (ret);
810 }
811
812 /*
813  * RNDIS filter on device add
814  */
815 int
816 hv_rf_on_device_add(struct hv_device *device, void *additl_info)
817 {
818         int ret;
819         netvsc_dev *net_dev;
820         rndis_device *rndis_dev;
821         rndis_offload_params offloads;
822         netvsc_device_info *dev_info = (netvsc_device_info *)additl_info;
823         device_t dev = device->device;
824
825         rndis_dev = hv_get_rndis_device();
826         if (rndis_dev == NULL) {
827                 return (ENOMEM);
828         }
829
830         /*
831          * Let the inner driver handle this first to create the netvsc channel
832          * NOTE! Once the channel is created, we may get a receive callback 
833          * (hv_rf_on_receive()) before this call is completed.
834          * Note:  Earlier code used a function pointer here.
835          */
836         net_dev = hv_nv_on_device_add(device, additl_info);
837         if (!net_dev) {
838                 hv_put_rndis_device(rndis_dev);
839
840                 return (ENOMEM);
841         }
842
843         /*
844          * Initialize the rndis device
845          */
846
847         net_dev->extension = rndis_dev;
848         rndis_dev->net_dev = net_dev;
849
850         /* Send the rndis initialization message */
851         ret = hv_rf_init_device(rndis_dev);
852         if (ret != 0) {
853                 /*
854                  * TODO: If rndis init failed, we will need to shut down
855                  * the channel
856                  */
857         }
858
859         /* Get the mac address */
860         ret = hv_rf_query_device_mac(rndis_dev);
861         if (ret != 0) {
862                 /* TODO: shut down rndis device and the channel */
863         }
864
865         /* config csum offload and send request to host */
866         memset(&offloads, 0, sizeof(offloads));
867         offloads.ipv4_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
868         offloads.tcp_ipv4_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
869         offloads.udp_ipv4_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
870         offloads.tcp_ipv6_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
871         offloads.udp_ipv6_csum = RNDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
872         offloads.lso_v2_ipv4 = RNDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
873
874         ret = hv_rf_send_offload_request(device, &offloads);
875         if (ret != 0) {
876                 /* TODO: shut down rndis device and the channel */
877                 device_printf(dev,
878                     "hv_rf_send_offload_request failed, ret=%d\n", ret);
879         }
880         
881         memcpy(dev_info->mac_addr, rndis_dev->hw_mac_addr, HW_MACADDR_LEN);
882
883         hv_rf_query_device_link_status(rndis_dev);
884         
885         dev_info->link_state = rndis_dev->link_status;
886
887         return (ret);
888 }
889
890 /*
891  * RNDIS filter on device remove
892  */
893 int
894 hv_rf_on_device_remove(struct hv_device *device, boolean_t destroy_channel)
895 {
896         hn_softc_t *sc = device_get_softc(device->device);
897         netvsc_dev *net_dev = sc->net_dev;
898         rndis_device *rndis_dev = (rndis_device *)net_dev->extension;
899         int ret;
900
901         /* Halt and release the rndis device */
902         ret = hv_rf_halt_device(rndis_dev);
903
904         hv_put_rndis_device(rndis_dev);
905         net_dev->extension = NULL;
906
907         /* Pass control to inner driver to remove the device */
908         ret |= hv_nv_on_device_remove(device, destroy_channel);
909
910         return (ret);
911 }
912
913 /*
914  * RNDIS filter on open
915  */
916 int
917 hv_rf_on_open(struct hv_device *device)
918 {
919         hn_softc_t *sc = device_get_softc(device->device);      
920         netvsc_dev *net_dev = sc->net_dev;
921
922         return (hv_rf_open_device((rndis_device *)net_dev->extension));
923 }
924
925 /*
926  * RNDIS filter on close
927  */
928 int 
929 hv_rf_on_close(struct hv_device *device)
930 {
931         hn_softc_t *sc = device_get_softc(device->device);      
932         netvsc_dev *net_dev = sc->net_dev;
933
934         return (hv_rf_close_device((rndis_device *)net_dev->extension));
935 }
936
937 /*
938  * RNDIS filter on send request completion callback
939  */
940 static void 
941 hv_rf_on_send_request_completion(void *context)
942 {
943 }
944
945 /*
946  * RNDIS filter on send request (halt only) completion callback
947  */
948 static void 
949 hv_rf_on_send_request_halt_completion(void *context)
950 {
951         rndis_request *request = context;
952
953         /*
954          * Notify hv_rf_halt_device() about halt completion.
955          * The halt code must wait for completion before freeing
956          * the transaction resources.
957          */
958         request->halt_complete_flag = 1;
959 }
960
961 /*
962  * RNDIS filter when "all" reception is done
963  */
964 void
965 hv_rf_receive_rollup(netvsc_dev *net_dev)
966 {
967         rndis_device *rndis_dev;
968
969         rndis_dev = (rndis_device *)net_dev->extension;
970         netvsc_recv_rollup(rndis_dev->net_dev->dev);
971 }
972
973 void
974 hv_rf_channel_rollup(netvsc_dev *net_dev)
975 {
976         rndis_device *rndis_dev;
977
978         rndis_dev = (rndis_device *)net_dev->extension;
979
980         /*
981          * This could be called pretty early, so we need
982          * to make sure everything has been setup.
983          */
984         if (rndis_dev == NULL ||
985             rndis_dev->net_dev == NULL ||
986             rndis_dev->net_dev->dev == NULL)
987                 return;
988         netvsc_channel_rollup(rndis_dev->net_dev->dev);
989 }