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