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