Commit | Line | Data |
---|---|---|
fceaf24a | 1 | /* |
fceaf24a HJ |
2 | * Copyright (c) 2009, Microsoft Corporation. |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify it | |
5 | * under the terms and conditions of the GNU General Public License, | |
6 | * version 2, as published by the Free Software Foundation. | |
7 | * | |
8 | * This program is distributed in the hope it will be useful, but WITHOUT | |
9 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
10 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
11 | * more details. | |
12 | * | |
13 | * You should have received a copy of the GNU General Public License along with | |
14 | * this program; if not, write to the Free Software Foundation, Inc., 59 Temple | |
15 | * Place - Suite 330, Boston, MA 02111-1307 USA. | |
16 | * | |
17 | * Authors: | |
18 | * Haiyang Zhang <haiyangz@microsoft.com> | |
19 | * Hank Janssen <hjanssen@microsoft.com> | |
fceaf24a | 20 | */ |
5654e932 | 21 | #include <linux/kernel.h> |
0c3b7b2f S |
22 | #include <linux/sched.h> |
23 | #include <linux/wait.h> | |
45da89e5 | 24 | #include <linux/highmem.h> |
5a0e3ad6 | 25 | #include <linux/slab.h> |
0120ee0d | 26 | #include <linux/io.h> |
9f8bd8ba | 27 | #include <linux/if_ether.h> |
eb335bc4 | 28 | #include <linux/netdevice.h> |
3f335ea2 S |
29 | |
30 | #include "hyperv.h" | |
e3fe0bb6 | 31 | #include "hv_api.h" |
a82c7a2a | 32 | #include "netvsc_api.h" |
043efcc3 | 33 | #include "rndis_filter.h" |
fceaf24a | 34 | |
454f18a9 | 35 | /* Data types */ |
e681b954 | 36 | struct rndis_filter_driver_object { |
454f18a9 | 37 | /* The original driver */ |
c2a4efdd | 38 | struct netvsc_driver inner_drv; |
e681b954 | 39 | }; |
fceaf24a | 40 | |
e681b954 | 41 | enum rndis_device_state { |
fceaf24a HJ |
42 | RNDIS_DEV_UNINITIALIZED = 0, |
43 | RNDIS_DEV_INITIALIZING, | |
44 | RNDIS_DEV_INITIALIZED, | |
45 | RNDIS_DEV_DATAINITIALIZED, | |
e681b954 | 46 | }; |
fceaf24a | 47 | |
e681b954 | 48 | struct rndis_device { |
c2a4efdd | 49 | struct netvsc_device *net_dev; |
fceaf24a | 50 | |
c2a4efdd HZ |
51 | enum rndis_device_state state; |
52 | u32 link_stat; | |
53 | atomic_t new_req_id; | |
fceaf24a | 54 | |
880fb89c | 55 | spinlock_t request_lock; |
c2a4efdd | 56 | struct list_head req_list; |
fceaf24a | 57 | |
c2a4efdd | 58 | unsigned char hw_mac_adr[ETH_ALEN]; |
e681b954 | 59 | }; |
fceaf24a | 60 | |
e681b954 | 61 | struct rndis_request { |
c2a4efdd | 62 | struct list_head list_ent; |
98d79690 | 63 | struct completion wait_event; |
fceaf24a | 64 | |
0120ee0d GKH |
65 | /* |
66 | * FIXME: We assumed a fixed size response here. If we do ever need to | |
67 | * handle a bigger response, we can either define a max response | |
68 | * message or add a response buffer variable above this field | |
69 | */ | |
c2a4efdd | 70 | struct rndis_message response_msg; |
fceaf24a | 71 | |
454f18a9 | 72 | /* Simplify allocation by having a netvsc packet inline */ |
c2a4efdd HZ |
73 | struct hv_netvsc_packet pkt; |
74 | struct hv_page_buffer buf; | |
454f18a9 | 75 | /* FIXME: We assumed a fixed size request here. */ |
c2a4efdd | 76 | struct rndis_message request_msg; |
e681b954 | 77 | }; |
fceaf24a HJ |
78 | |
79 | ||
e681b954 | 80 | struct rndis_filter_packet { |
c2a4efdd HZ |
81 | void *completion_ctx; |
82 | void (*completion)(void *context); | |
83 | struct rndis_message msg; | |
e681b954 | 84 | }; |
fceaf24a | 85 | |
454f18a9 | 86 | |
9c26aa0d | 87 | static int rndis_filter_send(struct hv_device *dev, |
c2a4efdd | 88 | struct hv_netvsc_packet *pkt); |
0120ee0d | 89 | |
9c26aa0d | 90 | static void rndis_filter_send_completion(void *ctx); |
0120ee0d | 91 | |
9c26aa0d | 92 | static void rndis_filter_send_request_completion(void *ctx); |
454f18a9 BP |
93 | |
94 | ||
95 | /* The one and only */ | |
c2a4efdd | 96 | static struct rndis_filter_driver_object rndis_filter; |
fceaf24a | 97 | |
9c26aa0d | 98 | static struct rndis_device *get_rndis_device(void) |
fceaf24a | 99 | { |
e681b954 | 100 | struct rndis_device *device; |
fceaf24a | 101 | |
e681b954 | 102 | device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL); |
fceaf24a | 103 | if (!device) |
fceaf24a | 104 | return NULL; |
fceaf24a | 105 | |
880fb89c | 106 | spin_lock_init(&device->request_lock); |
fceaf24a | 107 | |
c2a4efdd | 108 | INIT_LIST_HEAD(&device->req_list); |
fceaf24a | 109 | |
c2a4efdd | 110 | device->state = RNDIS_DEV_UNINITIALIZED; |
fceaf24a HJ |
111 | |
112 | return device; | |
113 | } | |
114 | ||
9c26aa0d | 115 | static struct rndis_request *get_rndis_request(struct rndis_device *dev, |
c2a4efdd HZ |
116 | u32 msg_type, |
117 | u32 msg_len) | |
fceaf24a | 118 | { |
e681b954 | 119 | struct rndis_request *request; |
c2a4efdd | 120 | struct rndis_message *rndis_msg; |
9f33d054 | 121 | struct rndis_set_request *set; |
880fb89c | 122 | unsigned long flags; |
fceaf24a | 123 | |
e681b954 | 124 | request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL); |
fceaf24a | 125 | if (!request) |
fceaf24a | 126 | return NULL; |
fceaf24a | 127 | |
98d79690 | 128 | init_completion(&request->wait_event); |
fceaf24a | 129 | |
c2a4efdd | 130 | rndis_msg = &request->request_msg; |
a388eb17 HZ |
131 | rndis_msg->ndis_msg_type = msg_type; |
132 | rndis_msg->msg_len = msg_len; | |
fceaf24a | 133 | |
0120ee0d GKH |
134 | /* |
135 | * Set the request id. This field is always after the rndis header for | |
136 | * request/response packet types so we just used the SetRequest as a | |
137 | * template | |
138 | */ | |
a388eb17 HZ |
139 | set = &rndis_msg->msg.set_req; |
140 | set->req_id = atomic_inc_return(&dev->new_req_id); | |
fceaf24a | 141 | |
454f18a9 | 142 | /* Add to the request list */ |
c2a4efdd HZ |
143 | spin_lock_irqsave(&dev->request_lock, flags); |
144 | list_add_tail(&request->list_ent, &dev->req_list); | |
145 | spin_unlock_irqrestore(&dev->request_lock, flags); | |
fceaf24a HJ |
146 | |
147 | return request; | |
148 | } | |
149 | ||
9c26aa0d | 150 | static void put_rndis_request(struct rndis_device *dev, |
c2a4efdd | 151 | struct rndis_request *req) |
fceaf24a | 152 | { |
880fb89c GKH |
153 | unsigned long flags; |
154 | ||
c2a4efdd HZ |
155 | spin_lock_irqsave(&dev->request_lock, flags); |
156 | list_del(&req->list_ent); | |
157 | spin_unlock_irqrestore(&dev->request_lock, flags); | |
fceaf24a | 158 | |
c2a4efdd | 159 | kfree(req); |
fceaf24a HJ |
160 | } |
161 | ||
9c26aa0d | 162 | static void dump_rndis_message(struct rndis_message *rndis_msg) |
fceaf24a | 163 | { |
a388eb17 | 164 | switch (rndis_msg->ndis_msg_type) { |
fceaf24a | 165 | case REMOTE_NDIS_PACKET_MSG: |
0120ee0d GKH |
166 | DPRINT_DBG(NETVSC, "REMOTE_NDIS_PACKET_MSG (len %u, " |
167 | "data offset %u data len %u, # oob %u, " | |
168 | "oob offset %u, oob len %u, pkt offset %u, " | |
169 | "pkt len %u", | |
a388eb17 HZ |
170 | rndis_msg->msg_len, |
171 | rndis_msg->msg.pkt.data_offset, | |
172 | rndis_msg->msg.pkt.data_len, | |
173 | rndis_msg->msg.pkt.num_oob_data_elements, | |
174 | rndis_msg->msg.pkt.oob_data_offset, | |
175 | rndis_msg->msg.pkt.oob_data_len, | |
176 | rndis_msg->msg.pkt.per_pkt_info_offset, | |
177 | rndis_msg->msg.pkt.per_pkt_info_len); | |
fceaf24a HJ |
178 | break; |
179 | ||
180 | case REMOTE_NDIS_INITIALIZE_CMPLT: | |
0120ee0d GKH |
181 | DPRINT_DBG(NETVSC, "REMOTE_NDIS_INITIALIZE_CMPLT " |
182 | "(len %u, id 0x%x, status 0x%x, major %d, minor %d, " | |
183 | "device flags %d, max xfer size 0x%x, max pkts %u, " | |
184 | "pkt aligned %u)", | |
a388eb17 HZ |
185 | rndis_msg->msg_len, |
186 | rndis_msg->msg.init_complete.req_id, | |
187 | rndis_msg->msg.init_complete.status, | |
188 | rndis_msg->msg.init_complete.major_ver, | |
189 | rndis_msg->msg.init_complete.minor_ver, | |
190 | rndis_msg->msg.init_complete.dev_flags, | |
191 | rndis_msg->msg.init_complete.max_xfer_size, | |
192 | rndis_msg->msg.init_complete. | |
193 | max_pkt_per_msg, | |
194 | rndis_msg->msg.init_complete. | |
195 | pkt_alignment_factor); | |
fceaf24a HJ |
196 | break; |
197 | ||
198 | case REMOTE_NDIS_QUERY_CMPLT: | |
0120ee0d GKH |
199 | DPRINT_DBG(NETVSC, "REMOTE_NDIS_QUERY_CMPLT " |
200 | "(len %u, id 0x%x, status 0x%x, buf len %u, " | |
201 | "buf offset %u)", | |
a388eb17 HZ |
202 | rndis_msg->msg_len, |
203 | rndis_msg->msg.query_complete.req_id, | |
204 | rndis_msg->msg.query_complete.status, | |
205 | rndis_msg->msg.query_complete. | |
206 | info_buflen, | |
207 | rndis_msg->msg.query_complete. | |
208 | info_buf_offset); | |
fceaf24a HJ |
209 | break; |
210 | ||
211 | case REMOTE_NDIS_SET_CMPLT: | |
0120ee0d GKH |
212 | DPRINT_DBG(NETVSC, |
213 | "REMOTE_NDIS_SET_CMPLT (len %u, id 0x%x, status 0x%x)", | |
a388eb17 HZ |
214 | rndis_msg->msg_len, |
215 | rndis_msg->msg.set_complete.req_id, | |
216 | rndis_msg->msg.set_complete.status); | |
fceaf24a HJ |
217 | break; |
218 | ||
219 | case REMOTE_NDIS_INDICATE_STATUS_MSG: | |
0120ee0d GKH |
220 | DPRINT_DBG(NETVSC, "REMOTE_NDIS_INDICATE_STATUS_MSG " |
221 | "(len %u, status 0x%x, buf len %u, buf offset %u)", | |
a388eb17 HZ |
222 | rndis_msg->msg_len, |
223 | rndis_msg->msg.indicate_status.status, | |
224 | rndis_msg->msg.indicate_status.status_buflen, | |
225 | rndis_msg->msg.indicate_status.status_buf_offset); | |
fceaf24a HJ |
226 | break; |
227 | ||
228 | default: | |
229 | DPRINT_DBG(NETVSC, "0x%x (len %u)", | |
a388eb17 HZ |
230 | rndis_msg->ndis_msg_type, |
231 | rndis_msg->msg_len); | |
fceaf24a HJ |
232 | break; |
233 | } | |
234 | } | |
235 | ||
9c26aa0d | 236 | static int rndis_filter_send_request(struct rndis_device *dev, |
c2a4efdd | 237 | struct rndis_request *req) |
fceaf24a | 238 | { |
0120ee0d | 239 | int ret; |
4193d4f4 | 240 | struct hv_netvsc_packet *packet; |
fceaf24a | 241 | |
454f18a9 | 242 | /* Setup the packet to send it */ |
c2a4efdd | 243 | packet = &req->pkt; |
fceaf24a | 244 | |
72a2f5bd | 245 | packet->is_data_pkt = false; |
a388eb17 | 246 | packet->total_data_buflen = req->request_msg.msg_len; |
72a2f5bd | 247 | packet->page_buf_cnt = 1; |
fceaf24a | 248 | |
ca623ad3 | 249 | packet->page_buf[0].pfn = virt_to_phys(&req->request_msg) >> |
0120ee0d | 250 | PAGE_SHIFT; |
ca623ad3 HZ |
251 | packet->page_buf[0].len = req->request_msg.msg_len; |
252 | packet->page_buf[0].offset = | |
c2a4efdd | 253 | (unsigned long)&req->request_msg & (PAGE_SIZE - 1); |
fceaf24a | 254 | |
72a2f5bd HZ |
255 | packet->completion.send.send_completion_ctx = req;/* packet; */ |
256 | packet->completion.send.send_completion = | |
9c26aa0d | 257 | rndis_filter_send_request_completion; |
72a2f5bd | 258 | packet->completion.send.send_completion_tid = (unsigned long)dev; |
fceaf24a | 259 | |
53d21fdb | 260 | ret = rndis_filter.inner_drv.send(dev->net_dev->dev, packet); |
fceaf24a HJ |
261 | return ret; |
262 | } | |
263 | ||
9c26aa0d | 264 | static void rndis_filter_receive_response(struct rndis_device *dev, |
c2a4efdd | 265 | struct rndis_message *resp) |
fceaf24a | 266 | { |
e681b954 | 267 | struct rndis_request *request = NULL; |
0e727613 | 268 | bool found = false; |
880fb89c | 269 | unsigned long flags; |
fceaf24a | 270 | |
c2a4efdd HZ |
271 | spin_lock_irqsave(&dev->request_lock, flags); |
272 | list_for_each_entry(request, &dev->req_list, list_ent) { | |
0120ee0d GKH |
273 | /* |
274 | * All request/response message contains RequestId as the 1st | |
275 | * field | |
276 | */ | |
a388eb17 HZ |
277 | if (request->request_msg.msg.init_req.req_id |
278 | == resp->msg.init_complete.req_id) { | |
0e727613 | 279 | found = true; |
fceaf24a HJ |
280 | break; |
281 | } | |
282 | } | |
c2a4efdd | 283 | spin_unlock_irqrestore(&dev->request_lock, flags); |
fceaf24a | 284 | |
0120ee0d | 285 | if (found) { |
a388eb17 | 286 | if (resp->msg_len <= sizeof(struct rndis_message)) { |
c2a4efdd | 287 | memcpy(&request->response_msg, resp, |
a388eb17 | 288 | resp->msg_len); |
0120ee0d | 289 | } else { |
eb335bc4 HJ |
290 | dev_err(&dev->net_dev->dev->device, |
291 | "rndis response buffer overflow " | |
292 | "detected (size %u max %zu)\n", | |
293 | resp->msg_len, | |
294 | sizeof(struct rndis_filter_packet)); | |
0120ee0d | 295 | |
a388eb17 | 296 | if (resp->ndis_msg_type == |
0120ee0d GKH |
297 | REMOTE_NDIS_RESET_CMPLT) { |
298 | /* does not have a request id field */ | |
a388eb17 HZ |
299 | request->response_msg.msg.reset_complete. |
300 | status = STATUS_BUFFER_OVERFLOW; | |
0120ee0d | 301 | } else { |
a388eb17 HZ |
302 | request->response_msg.msg. |
303 | init_complete.status = | |
c2a4efdd | 304 | STATUS_BUFFER_OVERFLOW; |
fceaf24a HJ |
305 | } |
306 | } | |
307 | ||
98d79690 | 308 | complete(&request->wait_event); |
0120ee0d | 309 | } else { |
eb335bc4 HJ |
310 | dev_err(&dev->net_dev->dev->device, |
311 | "no rndis request found for this response " | |
312 | "(id 0x%x res type 0x%x)\n", | |
313 | resp->msg.init_complete.req_id, | |
314 | resp->ndis_msg_type); | |
fceaf24a | 315 | } |
fceaf24a HJ |
316 | } |
317 | ||
9c26aa0d | 318 | static void rndis_filter_receive_indicate_status(struct rndis_device *dev, |
c2a4efdd | 319 | struct rndis_message *resp) |
fceaf24a | 320 | { |
0120ee0d | 321 | struct rndis_indicate_status *indicate = |
a388eb17 | 322 | &resp->msg.indicate_status; |
fceaf24a | 323 | |
a388eb17 | 324 | if (indicate->status == RNDIS_STATUS_MEDIA_CONNECT) { |
72a2f5bd | 325 | rndis_filter.inner_drv.link_status_change( |
53d21fdb | 326 | dev->net_dev->dev, 1); |
a388eb17 | 327 | } else if (indicate->status == RNDIS_STATUS_MEDIA_DISCONNECT) { |
72a2f5bd | 328 | rndis_filter.inner_drv.link_status_change( |
53d21fdb | 329 | dev->net_dev->dev, 0); |
0120ee0d GKH |
330 | } else { |
331 | /* | |
332 | * TODO: | |
333 | */ | |
fceaf24a HJ |
334 | } |
335 | } | |
336 | ||
9c26aa0d | 337 | static void rndis_filter_receive_data(struct rndis_device *dev, |
c2a4efdd HZ |
338 | struct rndis_message *msg, |
339 | struct hv_netvsc_packet *pkt) | |
fceaf24a | 340 | { |
c2a4efdd HZ |
341 | struct rndis_packet *rndis_pkt; |
342 | u32 data_offset; | |
fceaf24a | 343 | |
a388eb17 | 344 | rndis_pkt = &msg->msg.pkt; |
fceaf24a | 345 | |
0120ee0d GKH |
346 | /* |
347 | * FIXME: Handle multiple rndis pkt msgs that maybe enclosed in this | |
348 | * netvsc packet (ie TotalDataBufferLength != MessageLength) | |
349 | */ | |
fceaf24a | 350 | |
454f18a9 | 351 | /* Remove the rndis header and pass it back up the stack */ |
a388eb17 | 352 | data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset; |
fceaf24a | 353 | |
72a2f5bd | 354 | pkt->total_data_buflen -= data_offset; |
ca623ad3 HZ |
355 | pkt->page_buf[0].offset += data_offset; |
356 | pkt->page_buf[0].len -= data_offset; | |
fceaf24a | 357 | |
72a2f5bd | 358 | pkt->is_data_pkt = true; |
fceaf24a | 359 | |
53d21fdb | 360 | rndis_filter.inner_drv.recv_cb(dev->net_dev->dev, |
c2a4efdd | 361 | pkt); |
fceaf24a HJ |
362 | } |
363 | ||
9c26aa0d | 364 | static int rndis_filter_receive(struct hv_device *dev, |
c2a4efdd | 365 | struct hv_netvsc_packet *pkt) |
fceaf24a | 366 | { |
ca623ad3 | 367 | struct netvsc_device *net_dev = dev->ext; |
c2a4efdd HZ |
368 | struct rndis_device *rndis_dev; |
369 | struct rndis_message rndis_msg; | |
370 | struct rndis_message *rndis_hdr; | |
fceaf24a | 371 | |
c2a4efdd | 372 | if (!net_dev) |
8a62d716 BP |
373 | return -EINVAL; |
374 | ||
454f18a9 | 375 | /* Make sure the rndis device state is initialized */ |
53d21fdb | 376 | if (!net_dev->extension) { |
eb335bc4 HJ |
377 | dev_err(&dev->device, "got rndis message but no rndis device - " |
378 | "dropping this message!\n"); | |
fceaf24a HJ |
379 | return -1; |
380 | } | |
381 | ||
53d21fdb | 382 | rndis_dev = (struct rndis_device *)net_dev->extension; |
c2a4efdd | 383 | if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) { |
eb335bc4 HJ |
384 | dev_err(&dev->device, "got rndis message but rndis device " |
385 | "uninitialized...dropping this message!\n"); | |
fceaf24a HJ |
386 | return -1; |
387 | } | |
388 | ||
c2a4efdd | 389 | rndis_hdr = (struct rndis_message *)kmap_atomic( |
ca623ad3 | 390 | pfn_to_page(pkt->page_buf[0].pfn), KM_IRQ0); |
fceaf24a | 391 | |
c2a4efdd | 392 | rndis_hdr = (void *)((unsigned long)rndis_hdr + |
ca623ad3 | 393 | pkt->page_buf[0].offset); |
fceaf24a | 394 | |
454f18a9 | 395 | /* Make sure we got a valid rndis message */ |
0120ee0d GKH |
396 | /* |
397 | * FIXME: There seems to be a bug in set completion msg where its | |
398 | * MessageLength is 16 bytes but the ByteCount field in the xfer page | |
399 | * range shows 52 bytes | |
400 | * */ | |
fceaf24a | 401 | #if 0 |
a388eb17 | 402 | if (pkt->total_data_buflen != rndis_hdr->msg_len) { |
ca623ad3 | 403 | kunmap_atomic(rndis_hdr - pkt->page_buf[0].offset, |
0120ee0d GKH |
404 | KM_IRQ0); |
405 | ||
eb335bc4 HJ |
406 | dev_err(&dev->device, "invalid rndis message? (expected %u " |
407 | "bytes got %u)...dropping this message!\n", | |
a388eb17 | 408 | rndis_hdr->msg_len, |
72a2f5bd | 409 | pkt->total_data_buflen); |
fceaf24a HJ |
410 | return -1; |
411 | } | |
412 | #endif | |
413 | ||
a388eb17 HZ |
414 | if ((rndis_hdr->ndis_msg_type != REMOTE_NDIS_PACKET_MSG) && |
415 | (rndis_hdr->msg_len > sizeof(struct rndis_message))) { | |
eb335bc4 HJ |
416 | dev_err(&dev->device, "incoming rndis message buffer overflow " |
417 | "detected (got %u, max %zu)..marking it an error!\n", | |
a388eb17 | 418 | rndis_hdr->msg_len, |
0120ee0d | 419 | sizeof(struct rndis_message)); |
fceaf24a HJ |
420 | } |
421 | ||
c2a4efdd | 422 | memcpy(&rndis_msg, rndis_hdr, |
a388eb17 | 423 | (rndis_hdr->msg_len > sizeof(struct rndis_message)) ? |
0120ee0d | 424 | sizeof(struct rndis_message) : |
a388eb17 | 425 | rndis_hdr->msg_len); |
fceaf24a | 426 | |
ca623ad3 | 427 | kunmap_atomic(rndis_hdr - pkt->page_buf[0].offset, KM_IRQ0); |
fceaf24a | 428 | |
9c26aa0d | 429 | dump_rndis_message(&rndis_msg); |
fceaf24a | 430 | |
a388eb17 | 431 | switch (rndis_msg.ndis_msg_type) { |
fceaf24a | 432 | case REMOTE_NDIS_PACKET_MSG: |
0120ee0d | 433 | /* data msg */ |
9c26aa0d | 434 | rndis_filter_receive_data(rndis_dev, &rndis_msg, pkt); |
fceaf24a HJ |
435 | break; |
436 | ||
fceaf24a HJ |
437 | case REMOTE_NDIS_INITIALIZE_CMPLT: |
438 | case REMOTE_NDIS_QUERY_CMPLT: | |
439 | case REMOTE_NDIS_SET_CMPLT: | |
0120ee0d | 440 | /* completion msgs */ |
9c26aa0d | 441 | rndis_filter_receive_response(rndis_dev, &rndis_msg); |
fceaf24a HJ |
442 | break; |
443 | ||
fceaf24a | 444 | case REMOTE_NDIS_INDICATE_STATUS_MSG: |
0120ee0d | 445 | /* notification msgs */ |
9c26aa0d | 446 | rndis_filter_receive_indicate_status(rndis_dev, &rndis_msg); |
fceaf24a HJ |
447 | break; |
448 | default: | |
eb335bc4 HJ |
449 | dev_err(&dev->device, |
450 | "unhandled rndis message (type %u len %u)\n", | |
a388eb17 HZ |
451 | rndis_msg.ndis_msg_type, |
452 | rndis_msg.msg_len); | |
fceaf24a HJ |
453 | break; |
454 | } | |
455 | ||
fceaf24a HJ |
456 | return 0; |
457 | } | |
458 | ||
9c26aa0d | 459 | static int rndis_filter_query_device(struct rndis_device *dev, u32 oid, |
c2a4efdd | 460 | void *result, u32 *result_size) |
fceaf24a | 461 | { |
e681b954 | 462 | struct rndis_request *request; |
c2a4efdd | 463 | u32 inresult_size = *result_size; |
9f33d054 | 464 | struct rndis_query_request *query; |
c2a4efdd | 465 | struct rndis_query_complete *query_complete; |
0120ee0d | 466 | int ret = 0; |
98d79690 | 467 | int t; |
fceaf24a | 468 | |
c2a4efdd | 469 | if (!result) |
8a62d716 | 470 | return -EINVAL; |
fceaf24a | 471 | |
c2a4efdd | 472 | *result_size = 0; |
9c26aa0d | 473 | request = get_rndis_request(dev, REMOTE_NDIS_QUERY_MSG, |
0120ee0d GKH |
474 | RNDIS_MESSAGE_SIZE(struct rndis_query_request)); |
475 | if (!request) { | |
fceaf24a HJ |
476 | ret = -1; |
477 | goto Cleanup; | |
478 | } | |
479 | ||
454f18a9 | 480 | /* Setup the rndis query */ |
a388eb17 HZ |
481 | query = &request->request_msg.msg.query_req; |
482 | query->oid = oid; | |
483 | query->info_buf_offset = sizeof(struct rndis_query_request); | |
484 | query->info_buflen = 0; | |
485 | query->dev_vc_handle = 0; | |
fceaf24a | 486 | |
9c26aa0d | 487 | ret = rndis_filter_send_request(dev, request); |
fceaf24a | 488 | if (ret != 0) |
fceaf24a | 489 | goto Cleanup; |
fceaf24a | 490 | |
98d79690 S |
491 | t = wait_for_completion_timeout(&request->wait_event, HZ); |
492 | if (t == 0) { | |
0c3b7b2f S |
493 | ret = -ETIMEDOUT; |
494 | goto Cleanup; | |
495 | } | |
fceaf24a | 496 | |
454f18a9 | 497 | /* Copy the response back */ |
a388eb17 | 498 | query_complete = &request->response_msg.msg.query_complete; |
fceaf24a | 499 | |
a388eb17 | 500 | if (query_complete->info_buflen > inresult_size) { |
fceaf24a HJ |
501 | ret = -1; |
502 | goto Cleanup; | |
503 | } | |
504 | ||
c2a4efdd HZ |
505 | memcpy(result, |
506 | (void *)((unsigned long)query_complete + | |
a388eb17 HZ |
507 | query_complete->info_buf_offset), |
508 | query_complete->info_buflen); | |
fceaf24a | 509 | |
a388eb17 | 510 | *result_size = query_complete->info_buflen; |
fceaf24a HJ |
511 | |
512 | Cleanup: | |
513 | if (request) | |
9c26aa0d | 514 | put_rndis_request(dev, request); |
fceaf24a HJ |
515 | |
516 | return ret; | |
517 | } | |
518 | ||
9c26aa0d | 519 | static int rndis_filter_query_device_mac(struct rndis_device *dev) |
fceaf24a | 520 | { |
9f8bd8ba | 521 | u32 size = ETH_ALEN; |
fceaf24a | 522 | |
9c26aa0d | 523 | return rndis_filter_query_device(dev, |
0120ee0d | 524 | RNDIS_OID_802_3_PERMANENT_ADDRESS, |
c2a4efdd | 525 | dev->hw_mac_adr, &size); |
fceaf24a HJ |
526 | } |
527 | ||
9c26aa0d | 528 | static int rndis_filter_query_device_link_status(struct rndis_device *dev) |
fceaf24a | 529 | { |
0120ee0d | 530 | u32 size = sizeof(u32); |
fceaf24a | 531 | |
9c26aa0d | 532 | return rndis_filter_query_device(dev, |
0120ee0d | 533 | RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, |
c2a4efdd | 534 | &dev->link_stat, &size); |
fceaf24a HJ |
535 | } |
536 | ||
9c26aa0d | 537 | static int rndis_filter_set_packet_filter(struct rndis_device *dev, |
c2a4efdd | 538 | u32 new_filter) |
fceaf24a | 539 | { |
e681b954 | 540 | struct rndis_request *request; |
9f33d054 | 541 | struct rndis_set_request *set; |
c2a4efdd | 542 | struct rndis_set_complete *set_complete; |
4d643114 | 543 | u32 status; |
98d79690 | 544 | int ret, t; |
fceaf24a | 545 | |
9c26aa0d | 546 | request = get_rndis_request(dev, REMOTE_NDIS_SET_MSG, |
0120ee0d GKH |
547 | RNDIS_MESSAGE_SIZE(struct rndis_set_request) + |
548 | sizeof(u32)); | |
549 | if (!request) { | |
fceaf24a HJ |
550 | ret = -1; |
551 | goto Cleanup; | |
552 | } | |
553 | ||
454f18a9 | 554 | /* Setup the rndis set */ |
a388eb17 HZ |
555 | set = &request->request_msg.msg.set_req; |
556 | set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER; | |
557 | set->info_buflen = sizeof(u32); | |
558 | set->info_buf_offset = sizeof(struct rndis_set_request); | |
fceaf24a | 559 | |
0120ee0d | 560 | memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request), |
c2a4efdd | 561 | &new_filter, sizeof(u32)); |
fceaf24a | 562 | |
9c26aa0d | 563 | ret = rndis_filter_send_request(dev, request); |
fceaf24a | 564 | if (ret != 0) |
fceaf24a | 565 | goto Cleanup; |
fceaf24a | 566 | |
98d79690 S |
567 | t = wait_for_completion_timeout(&request->wait_event, HZ); |
568 | ||
569 | if (t == 0) { | |
fceaf24a | 570 | ret = -1; |
eb335bc4 HJ |
571 | dev_err(&dev->net_dev->dev->device, |
572 | "timeout before we got a set response...\n"); | |
0120ee0d | 573 | /* |
25985edc | 574 | * We can't deallocate the request since we may still receive a |
0120ee0d GKH |
575 | * send completion for it. |
576 | */ | |
fceaf24a | 577 | goto Exit; |
0120ee0d | 578 | } else { |
fceaf24a | 579 | if (ret > 0) |
fceaf24a | 580 | ret = 0; |
a388eb17 HZ |
581 | set_complete = &request->response_msg.msg.set_complete; |
582 | status = set_complete->status; | |
fceaf24a HJ |
583 | } |
584 | ||
585 | Cleanup: | |
586 | if (request) | |
9c26aa0d | 587 | put_rndis_request(dev, request); |
fceaf24a | 588 | Exit: |
fceaf24a HJ |
589 | return ret; |
590 | } | |
591 | ||
9c26aa0d | 592 | int rndis_filter_init(struct netvsc_driver *drv) |
fceaf24a | 593 | { |
72a2f5bd | 594 | drv->req_ext_size = sizeof(struct rndis_filter_packet); |
fceaf24a | 595 | |
454f18a9 | 596 | /* Driver->Context = rndisDriver; */ |
fceaf24a | 597 | |
c2a4efdd | 598 | memset(&rndis_filter, 0, sizeof(struct rndis_filter_driver_object)); |
fceaf24a HJ |
599 | |
600 | /*rndisDriver->Driver = Driver; | |
601 | ||
602 | ASSERT(Driver->OnLinkStatusChanged); | |
603 | rndisDriver->OnLinkStatusChanged = Driver->OnLinkStatusChanged;*/ | |
604 | ||
454f18a9 | 605 | /* Save the original dispatch handlers before we override it */ |
72a2f5bd HZ |
606 | rndis_filter.inner_drv.send = drv->send; |
607 | rndis_filter.inner_drv.recv_cb = drv->recv_cb; | |
608 | rndis_filter.inner_drv.link_status_change = | |
609 | drv->link_status_change; | |
fceaf24a | 610 | |
454f18a9 | 611 | /* Override */ |
72a2f5bd | 612 | drv->send = rndis_filter_send; |
72a2f5bd | 613 | drv->recv_cb = rndis_filter_receive; |
fceaf24a | 614 | |
fceaf24a HJ |
615 | return 0; |
616 | } | |
617 | ||
9c26aa0d | 618 | static int rndis_filter_init_device(struct rndis_device *dev) |
fceaf24a | 619 | { |
e681b954 | 620 | struct rndis_request *request; |
9f33d054 | 621 | struct rndis_initialize_request *init; |
c2a4efdd | 622 | struct rndis_initialize_complete *init_complete; |
4d643114 | 623 | u32 status; |
98d79690 | 624 | int ret, t; |
fceaf24a | 625 | |
9c26aa0d | 626 | request = get_rndis_request(dev, REMOTE_NDIS_INITIALIZE_MSG, |
0120ee0d GKH |
627 | RNDIS_MESSAGE_SIZE(struct rndis_initialize_request)); |
628 | if (!request) { | |
fceaf24a HJ |
629 | ret = -1; |
630 | goto Cleanup; | |
631 | } | |
632 | ||
454f18a9 | 633 | /* Setup the rndis set */ |
a388eb17 HZ |
634 | init = &request->request_msg.msg.init_req; |
635 | init->major_ver = RNDIS_MAJOR_VERSION; | |
636 | init->minor_ver = RNDIS_MINOR_VERSION; | |
0120ee0d | 637 | /* FIXME: Use 1536 - rounded ethernet frame size */ |
a388eb17 | 638 | init->max_xfer_size = 2048; |
fceaf24a | 639 | |
c2a4efdd | 640 | dev->state = RNDIS_DEV_INITIALIZING; |
fceaf24a | 641 | |
9c26aa0d | 642 | ret = rndis_filter_send_request(dev, request); |
0120ee0d | 643 | if (ret != 0) { |
c2a4efdd | 644 | dev->state = RNDIS_DEV_UNINITIALIZED; |
fceaf24a HJ |
645 | goto Cleanup; |
646 | } | |
647 | ||
0c3b7b2f | 648 | |
98d79690 S |
649 | t = wait_for_completion_timeout(&request->wait_event, HZ); |
650 | ||
651 | if (t == 0) { | |
0c3b7b2f S |
652 | ret = -ETIMEDOUT; |
653 | goto Cleanup; | |
654 | } | |
fceaf24a | 655 | |
a388eb17 HZ |
656 | init_complete = &request->response_msg.msg.init_complete; |
657 | status = init_complete->status; | |
0120ee0d | 658 | if (status == RNDIS_STATUS_SUCCESS) { |
c2a4efdd | 659 | dev->state = RNDIS_DEV_INITIALIZED; |
fceaf24a | 660 | ret = 0; |
0120ee0d | 661 | } else { |
c2a4efdd | 662 | dev->state = RNDIS_DEV_UNINITIALIZED; |
fceaf24a HJ |
663 | ret = -1; |
664 | } | |
665 | ||
666 | Cleanup: | |
667 | if (request) | |
9c26aa0d | 668 | put_rndis_request(dev, request); |
fceaf24a HJ |
669 | |
670 | return ret; | |
671 | } | |
672 | ||
9c26aa0d | 673 | static void rndis_filter_halt_device(struct rndis_device *dev) |
fceaf24a | 674 | { |
e681b954 | 675 | struct rndis_request *request; |
9f33d054 | 676 | struct rndis_halt_request *halt; |
fceaf24a | 677 | |
454f18a9 | 678 | /* Attempt to do a rndis device halt */ |
9c26aa0d | 679 | request = get_rndis_request(dev, REMOTE_NDIS_HALT_MSG, |
0120ee0d | 680 | RNDIS_MESSAGE_SIZE(struct rndis_halt_request)); |
fceaf24a | 681 | if (!request) |
fceaf24a | 682 | goto Cleanup; |
fceaf24a | 683 | |
454f18a9 | 684 | /* Setup the rndis set */ |
a388eb17 HZ |
685 | halt = &request->request_msg.msg.halt_req; |
686 | halt->req_id = atomic_inc_return(&dev->new_req_id); | |
fceaf24a | 687 | |
454f18a9 | 688 | /* Ignore return since this msg is optional. */ |
9c26aa0d | 689 | rndis_filter_send_request(dev, request); |
fceaf24a | 690 | |
c2a4efdd | 691 | dev->state = RNDIS_DEV_UNINITIALIZED; |
fceaf24a HJ |
692 | |
693 | Cleanup: | |
694 | if (request) | |
9c26aa0d | 695 | put_rndis_request(dev, request); |
fceaf24a HJ |
696 | return; |
697 | } | |
698 | ||
9c26aa0d | 699 | static int rndis_filter_open_device(struct rndis_device *dev) |
fceaf24a | 700 | { |
0120ee0d | 701 | int ret; |
fceaf24a | 702 | |
c2a4efdd | 703 | if (dev->state != RNDIS_DEV_INITIALIZED) |
fceaf24a HJ |
704 | return 0; |
705 | ||
9c26aa0d | 706 | ret = rndis_filter_set_packet_filter(dev, |
0120ee0d | 707 | NDIS_PACKET_TYPE_BROADCAST | |
95beae90 | 708 | NDIS_PACKET_TYPE_ALL_MULTICAST | |
0120ee0d | 709 | NDIS_PACKET_TYPE_DIRECTED); |
fceaf24a | 710 | if (ret == 0) |
c2a4efdd | 711 | dev->state = RNDIS_DEV_DATAINITIALIZED; |
fceaf24a | 712 | |
fceaf24a HJ |
713 | return ret; |
714 | } | |
715 | ||
9c26aa0d | 716 | static int rndis_filter_close_device(struct rndis_device *dev) |
fceaf24a HJ |
717 | { |
718 | int ret; | |
719 | ||
c2a4efdd | 720 | if (dev->state != RNDIS_DEV_DATAINITIALIZED) |
fceaf24a HJ |
721 | return 0; |
722 | ||
9c26aa0d | 723 | ret = rndis_filter_set_packet_filter(dev, 0); |
fceaf24a | 724 | if (ret == 0) |
c2a4efdd | 725 | dev->state = RNDIS_DEV_INITIALIZED; |
fceaf24a | 726 | |
fceaf24a HJ |
727 | return ret; |
728 | } | |
729 | ||
10f5a6db | 730 | int rndis_filte_device_add(struct hv_device *dev, |
c2a4efdd | 731 | void *additional_info) |
fceaf24a HJ |
732 | { |
733 | int ret; | |
ce9ea4cf | 734 | struct netvsc_device *netDevice; |
e681b954 | 735 | struct rndis_device *rndisDevice; |
c2a4efdd | 736 | struct netvsc_device_info *deviceInfo = additional_info; |
fceaf24a | 737 | |
9c26aa0d | 738 | rndisDevice = get_rndis_device(); |
83c720ea | 739 | if (!rndisDevice) |
fceaf24a | 740 | return -1; |
fceaf24a | 741 | |
0120ee0d GKH |
742 | /* |
743 | * Let the inner driver handle this first to create the netvsc channel | |
744 | * NOTE! Once the channel is created, we may get a receive callback | |
745 | * (RndisFilterOnReceive()) before this call is completed | |
746 | */ | |
ce5bf661 | 747 | ret = netvsc_device_add(dev, additional_info); |
0120ee0d GKH |
748 | if (ret != 0) { |
749 | kfree(rndisDevice); | |
fceaf24a HJ |
750 | return ret; |
751 | } | |
752 | ||
454f18a9 BP |
753 | |
754 | /* Initialize the rndis device */ | |
ca623ad3 | 755 | netDevice = dev->ext; |
fceaf24a | 756 | |
53d21fdb | 757 | netDevice->extension = rndisDevice; |
c2a4efdd | 758 | rndisDevice->net_dev = netDevice; |
fceaf24a | 759 | |
454f18a9 | 760 | /* Send the rndis initialization message */ |
9c26aa0d | 761 | ret = rndis_filter_init_device(rndisDevice); |
0120ee0d GKH |
762 | if (ret != 0) { |
763 | /* | |
764 | * TODO: If rndis init failed, we will need to shut down the | |
765 | * channel | |
766 | */ | |
fceaf24a HJ |
767 | } |
768 | ||
454f18a9 | 769 | /* Get the mac address */ |
9c26aa0d | 770 | ret = rndis_filter_query_device_mac(rndisDevice); |
0120ee0d GKH |
771 | if (ret != 0) { |
772 | /* | |
773 | * TODO: shutdown rndis device and the channel | |
774 | */ | |
fceaf24a HJ |
775 | } |
776 | ||
72a2f5bd | 777 | memcpy(deviceInfo->mac_adr, rndisDevice->hw_mac_adr, ETH_ALEN); |
fceaf24a | 778 | |
9c26aa0d | 779 | rndis_filter_query_device_link_status(rndisDevice); |
fceaf24a | 780 | |
72a2f5bd | 781 | deviceInfo->link_state = rndisDevice->link_stat; |
eb335bc4 HJ |
782 | |
783 | dev_info(&dev->device, "Device MAC %pM link state %s", | |
784 | rndisDevice->hw_mac_adr, | |
785 | ((deviceInfo->link_state) ? ("down\n") : ("up\n"))); | |
fceaf24a | 786 | |
fceaf24a HJ |
787 | return ret; |
788 | } | |
789 | ||
1405139c | 790 | int rndis_filter_device_remove(struct hv_device *dev) |
fceaf24a | 791 | { |
ca623ad3 | 792 | struct netvsc_device *net_dev = dev->ext; |
53d21fdb | 793 | struct rndis_device *rndis_dev = net_dev->extension; |
fceaf24a | 794 | |
454f18a9 | 795 | /* Halt and release the rndis device */ |
9c26aa0d | 796 | rndis_filter_halt_device(rndis_dev); |
fceaf24a | 797 | |
c2a4efdd | 798 | kfree(rndis_dev); |
53d21fdb | 799 | net_dev->extension = NULL; |
fceaf24a | 800 | |
3fae5c8f | 801 | netvsc_device_remove(dev); |
fceaf24a | 802 | |
fceaf24a HJ |
803 | return 0; |
804 | } | |
805 | ||
fceaf24a | 806 | |
9c26aa0d | 807 | int rndis_filter_open(struct hv_device *dev) |
fceaf24a | 808 | { |
ca623ad3 | 809 | struct netvsc_device *netDevice = dev->ext; |
fceaf24a | 810 | |
8a62d716 BP |
811 | if (!netDevice) |
812 | return -EINVAL; | |
813 | ||
53d21fdb | 814 | return rndis_filter_open_device(netDevice->extension); |
fceaf24a HJ |
815 | } |
816 | ||
9c26aa0d | 817 | int rndis_filter_close(struct hv_device *dev) |
fceaf24a | 818 | { |
ca623ad3 | 819 | struct netvsc_device *netDevice = dev->ext; |
fceaf24a | 820 | |
8a62d716 BP |
821 | if (!netDevice) |
822 | return -EINVAL; | |
823 | ||
53d21fdb | 824 | return rndis_filter_close_device(netDevice->extension); |
fceaf24a HJ |
825 | } |
826 | ||
9c26aa0d | 827 | static int rndis_filter_send(struct hv_device *dev, |
c2a4efdd | 828 | struct hv_netvsc_packet *pkt) |
fceaf24a | 829 | { |
0120ee0d | 830 | int ret; |
e681b954 | 831 | struct rndis_filter_packet *filterPacket; |
9f33d054 GKH |
832 | struct rndis_message *rndisMessage; |
833 | struct rndis_packet *rndisPacket; | |
4d643114 | 834 | u32 rndisMessageSize; |
fceaf24a | 835 | |
454f18a9 | 836 | /* Add the rndis header */ |
72a2f5bd | 837 | filterPacket = (struct rndis_filter_packet *)pkt->extension; |
fceaf24a | 838 | |
e681b954 | 839 | memset(filterPacket, 0, sizeof(struct rndis_filter_packet)); |
fceaf24a | 840 | |
c2a4efdd | 841 | rndisMessage = &filterPacket->msg; |
9f33d054 | 842 | rndisMessageSize = RNDIS_MESSAGE_SIZE(struct rndis_packet); |
fceaf24a | 843 | |
a388eb17 HZ |
844 | rndisMessage->ndis_msg_type = REMOTE_NDIS_PACKET_MSG; |
845 | rndisMessage->msg_len = pkt->total_data_buflen + | |
0120ee0d | 846 | rndisMessageSize; |
fceaf24a | 847 | |
a388eb17 HZ |
848 | rndisPacket = &rndisMessage->msg.pkt; |
849 | rndisPacket->data_offset = sizeof(struct rndis_packet); | |
850 | rndisPacket->data_len = pkt->total_data_buflen; | |
fceaf24a | 851 | |
72a2f5bd | 852 | pkt->is_data_pkt = true; |
ca623ad3 HZ |
853 | pkt->page_buf[0].pfn = virt_to_phys(rndisMessage) >> PAGE_SHIFT; |
854 | pkt->page_buf[0].offset = | |
0120ee0d | 855 | (unsigned long)rndisMessage & (PAGE_SIZE-1); |
ca623ad3 | 856 | pkt->page_buf[0].len = rndisMessageSize; |
fceaf24a | 857 | |
454f18a9 | 858 | /* Save the packet send completion and context */ |
72a2f5bd | 859 | filterPacket->completion = pkt->completion.send.send_completion; |
c2a4efdd | 860 | filterPacket->completion_ctx = |
72a2f5bd | 861 | pkt->completion.send.send_completion_ctx; |
fceaf24a | 862 | |
454f18a9 | 863 | /* Use ours */ |
72a2f5bd HZ |
864 | pkt->completion.send.send_completion = rndis_filter_send_completion; |
865 | pkt->completion.send.send_completion_ctx = filterPacket; | |
fceaf24a | 866 | |
72a2f5bd | 867 | ret = rndis_filter.inner_drv.send(dev, pkt); |
0120ee0d GKH |
868 | if (ret != 0) { |
869 | /* | |
870 | * Reset the completion to originals to allow retries from | |
871 | * above | |
872 | */ | |
72a2f5bd | 873 | pkt->completion.send.send_completion = |
c2a4efdd | 874 | filterPacket->completion; |
72a2f5bd | 875 | pkt->completion.send.send_completion_ctx = |
c2a4efdd | 876 | filterPacket->completion_ctx; |
fceaf24a HJ |
877 | } |
878 | ||
fceaf24a HJ |
879 | return ret; |
880 | } | |
881 | ||
9c26aa0d | 882 | static void rndis_filter_send_completion(void *ctx) |
fceaf24a | 883 | { |
c2a4efdd | 884 | struct rndis_filter_packet *filterPacket = ctx; |
fceaf24a | 885 | |
454f18a9 | 886 | /* Pass it back to the original handler */ |
c2a4efdd | 887 | filterPacket->completion(filterPacket->completion_ctx); |
fceaf24a HJ |
888 | } |
889 | ||
890 | ||
9c26aa0d | 891 | static void rndis_filter_send_request_completion(void *ctx) |
fceaf24a | 892 | { |
454f18a9 | 893 | /* Noop */ |
fceaf24a | 894 | } |