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