195fa0405SHaiyang Zhang /*
295fa0405SHaiyang Zhang  * Copyright (c) 2009, Microsoft Corporation.
395fa0405SHaiyang Zhang  *
495fa0405SHaiyang Zhang  * This program is free software; you can redistribute it and/or modify it
595fa0405SHaiyang Zhang  * under the terms and conditions of the GNU General Public License,
695fa0405SHaiyang Zhang  * version 2, as published by the Free Software Foundation.
795fa0405SHaiyang Zhang  *
895fa0405SHaiyang Zhang  * This program is distributed in the hope it will be useful, but WITHOUT
995fa0405SHaiyang Zhang  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1095fa0405SHaiyang Zhang  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1195fa0405SHaiyang Zhang  * more details.
1295fa0405SHaiyang Zhang  *
1395fa0405SHaiyang Zhang  * You should have received a copy of the GNU General Public License along with
14adf8d3ffSJeff Kirsher  * this program; if not, see <http://www.gnu.org/licenses/>.
1595fa0405SHaiyang Zhang  *
1695fa0405SHaiyang Zhang  * Authors:
1795fa0405SHaiyang Zhang  *   Haiyang Zhang <haiyangz@microsoft.com>
1895fa0405SHaiyang Zhang  *   Hank Janssen  <hjanssen@microsoft.com>
1995fa0405SHaiyang Zhang  */
2095fa0405SHaiyang Zhang #include <linux/kernel.h>
2195fa0405SHaiyang Zhang #include <linux/sched.h>
2295fa0405SHaiyang Zhang #include <linux/wait.h>
2395fa0405SHaiyang Zhang #include <linux/highmem.h>
2495fa0405SHaiyang Zhang #include <linux/slab.h>
2595fa0405SHaiyang Zhang #include <linux/io.h>
2695fa0405SHaiyang Zhang #include <linux/if_ether.h>
2795fa0405SHaiyang Zhang #include <linux/netdevice.h>
281f5f3a75SHaiyang Zhang #include <linux/if_vlan.h>
291ce09e89SHaiyang Zhang #include <linux/nls.h>
30d6472302SStephen Rothwell #include <linux/vmalloc.h>
3195fa0405SHaiyang Zhang 
3295fa0405SHaiyang Zhang #include "hyperv_net.h"
3395fa0405SHaiyang Zhang 
3495fa0405SHaiyang Zhang 
355b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE
3695fa0405SHaiyang Zhang struct rndis_request {
3795fa0405SHaiyang Zhang 	struct list_head list_ent;
3895fa0405SHaiyang Zhang 	struct completion  wait_event;
3995fa0405SHaiyang Zhang 
4095fa0405SHaiyang Zhang 	struct rndis_message response_msg;
41a3a6cab5SHaiyang Zhang 	/*
42a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
43a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
44a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
45a3a6cab5SHaiyang Zhang 	 * future.
46a3a6cab5SHaiyang Zhang 	 */
47a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
4895fa0405SHaiyang Zhang 
4995fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
5095fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
510f48917bSHaiyang Zhang 
5295fa0405SHaiyang Zhang 	struct rndis_message request_msg;
530f48917bSHaiyang Zhang 	/*
54a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
55a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
560f48917bSHaiyang Zhang 	 */
57a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
5895fa0405SHaiyang Zhang };
5995fa0405SHaiyang Zhang 
60962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = {
61962f3feeSstephen hemminger 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
62962f3feeSstephen hemminger 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
63962f3feeSstephen hemminger 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
64962f3feeSstephen hemminger 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
65962f3feeSstephen hemminger 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
66962f3feeSstephen hemminger };
67962f3feeSstephen hemminger 
6895fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
6995fa0405SHaiyang Zhang {
7095fa0405SHaiyang Zhang 	struct rndis_device *device;
7195fa0405SHaiyang Zhang 
7295fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
7395fa0405SHaiyang Zhang 	if (!device)
7495fa0405SHaiyang Zhang 		return NULL;
7595fa0405SHaiyang Zhang 
7695fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
7795fa0405SHaiyang Zhang 
7895fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
7995fa0405SHaiyang Zhang 
8095fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
8195fa0405SHaiyang Zhang 
8295fa0405SHaiyang Zhang 	return device;
8395fa0405SHaiyang Zhang }
8495fa0405SHaiyang Zhang 
8595fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
8695fa0405SHaiyang Zhang 					     u32 msg_type,
8795fa0405SHaiyang Zhang 					     u32 msg_len)
8895fa0405SHaiyang Zhang {
8995fa0405SHaiyang Zhang 	struct rndis_request *request;
9095fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
9195fa0405SHaiyang Zhang 	struct rndis_set_request *set;
9295fa0405SHaiyang Zhang 	unsigned long flags;
9395fa0405SHaiyang Zhang 
9495fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
9595fa0405SHaiyang Zhang 	if (!request)
9695fa0405SHaiyang Zhang 		return NULL;
9795fa0405SHaiyang Zhang 
9895fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
9995fa0405SHaiyang Zhang 
10095fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
10195fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
10295fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
10395fa0405SHaiyang Zhang 
1045b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
1055b54dac8SHaiyang Zhang 
10695fa0405SHaiyang Zhang 	/*
10795fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
10895fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
10995fa0405SHaiyang Zhang 	 * template
11095fa0405SHaiyang Zhang 	 */
11195fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
11295fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
11395fa0405SHaiyang Zhang 
11495fa0405SHaiyang Zhang 	/* Add to the request list */
11595fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
11695fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
11795fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
11895fa0405SHaiyang Zhang 
11995fa0405SHaiyang Zhang 	return request;
12095fa0405SHaiyang Zhang }
12195fa0405SHaiyang Zhang 
12295fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
12395fa0405SHaiyang Zhang 			    struct rndis_request *req)
12495fa0405SHaiyang Zhang {
12595fa0405SHaiyang Zhang 	unsigned long flags;
12695fa0405SHaiyang Zhang 
12795fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
12895fa0405SHaiyang Zhang 	list_del(&req->list_ent);
12995fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
13095fa0405SHaiyang Zhang 
13195fa0405SHaiyang Zhang 	kfree(req);
13295fa0405SHaiyang Zhang }
13395fa0405SHaiyang Zhang 
13495fa0405SHaiyang Zhang static void dump_rndis_message(struct hv_device *hv_dev,
135dc54a08cSstephen hemminger 			       const struct rndis_message *rndis_msg)
13695fa0405SHaiyang Zhang {
1373d541ac5SVitaly Kuznetsov 	struct net_device *netdev = hv_get_drvdata(hv_dev);
13895fa0405SHaiyang Zhang 
13995fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
14051491167SLinus Walleij 	case RNDIS_MSG_PACKET:
14151491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
14295fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
14395fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
14495fa0405SHaiyang Zhang 			   "pkt len %u\n",
14595fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
14695fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
14795fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
14895fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
14995fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
15095fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
15195fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
15295fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
15395fa0405SHaiyang Zhang 		break;
15495fa0405SHaiyang Zhang 
15551491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
15651491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
15795fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
15895fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
15995fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
16095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
16195fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
16295fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
16395fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
16495fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
16595fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
16695fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
16795fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
16895fa0405SHaiyang Zhang 			   max_pkt_per_msg,
16995fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17095fa0405SHaiyang Zhang 			   pkt_alignment_factor);
17195fa0405SHaiyang Zhang 		break;
17295fa0405SHaiyang Zhang 
17351491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
17451491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17595fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
17695fa0405SHaiyang Zhang 			"buf offset %u)\n",
17795fa0405SHaiyang Zhang 			rndis_msg->msg_len,
17895fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
17995fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
18095fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18195fa0405SHaiyang Zhang 			   info_buflen,
18295fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18395fa0405SHaiyang Zhang 			   info_buf_offset);
18495fa0405SHaiyang Zhang 		break;
18595fa0405SHaiyang Zhang 
18651491167SLinus Walleij 	case RNDIS_MSG_SET_C:
18795fa0405SHaiyang Zhang 		netdev_dbg(netdev,
18851491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
18995fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19095fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
19195fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
19295fa0405SHaiyang Zhang 		break;
19395fa0405SHaiyang Zhang 
19451491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
19551491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
19695fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
19795fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19895fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
19995fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
20095fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
20195fa0405SHaiyang Zhang 		break;
20295fa0405SHaiyang Zhang 
20395fa0405SHaiyang Zhang 	default:
20495fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
20595fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
20695fa0405SHaiyang Zhang 			rndis_msg->msg_len);
20795fa0405SHaiyang Zhang 		break;
20895fa0405SHaiyang Zhang 	}
20995fa0405SHaiyang Zhang }
21095fa0405SHaiyang Zhang 
21195fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
21295fa0405SHaiyang Zhang 				  struct rndis_request *req)
21395fa0405SHaiyang Zhang {
21495fa0405SHaiyang Zhang 	int ret;
21595fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
216b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
217a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
2183d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
21995fa0405SHaiyang Zhang 
22095fa0405SHaiyang Zhang 	/* Setup the packet to send it */
22195fa0405SHaiyang Zhang 	packet = &req->pkt;
22295fa0405SHaiyang Zhang 
22395fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
22495fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
22595fa0405SHaiyang Zhang 
226a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
22795fa0405SHaiyang Zhang 					PAGE_SHIFT;
228a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
229a9f2e2d6SKY Srinivasan 	pb[0].offset =
23095fa0405SHaiyang Zhang 		(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
23195fa0405SHaiyang Zhang 
23299e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
233a9f2e2d6SKY Srinivasan 	if (pb[0].offset + pb[0].len > PAGE_SIZE) {
23499e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
235a9f2e2d6SKY Srinivasan 		pb[0].len = PAGE_SIZE -
236a9f2e2d6SKY Srinivasan 			pb[0].offset;
237a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
238a9f2e2d6SKY Srinivasan 			+ pb[0].len) >> PAGE_SHIFT;
239a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
240a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
241a9f2e2d6SKY Srinivasan 			pb[0].len;
24299e3fcfaSHaiyang Zhang 	}
24399e3fcfaSHaiyang Zhang 
2443d541ac5SVitaly Kuznetsov 	ret = netvsc_send(net_device_ctx->device_ctx, packet, NULL, &pb, NULL);
24595fa0405SHaiyang Zhang 	return ret;
24695fa0405SHaiyang Zhang }
24795fa0405SHaiyang Zhang 
2481b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2491b07da51SHaiyang Zhang 				 struct rndis_request *request)
2501b07da51SHaiyang Zhang {
2511b07da51SHaiyang Zhang 	u32 link_status;
2521b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2531b07da51SHaiyang Zhang 
2541b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2551b07da51SHaiyang Zhang 
2561b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2571b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2581b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2591b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2601b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2611b07da51SHaiyang Zhang 	}
2621b07da51SHaiyang Zhang }
2631b07da51SHaiyang Zhang 
26495fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev,
26595fa0405SHaiyang Zhang 				       struct rndis_message *resp)
26695fa0405SHaiyang Zhang {
26795fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
26895fa0405SHaiyang Zhang 	bool found = false;
26995fa0405SHaiyang Zhang 	unsigned long flags;
2703d541ac5SVitaly Kuznetsov 	struct net_device *ndev = dev->ndev;
27195fa0405SHaiyang Zhang 
27295fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
27395fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
27495fa0405SHaiyang Zhang 		/*
27595fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
27695fa0405SHaiyang Zhang 		 * field
27795fa0405SHaiyang Zhang 		 */
27895fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
27995fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
28095fa0405SHaiyang Zhang 			found = true;
28195fa0405SHaiyang Zhang 			break;
28295fa0405SHaiyang Zhang 		}
28395fa0405SHaiyang Zhang 	}
28495fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
28595fa0405SHaiyang Zhang 
28695fa0405SHaiyang Zhang 	if (found) {
287a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
288a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
28995fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
29095fa0405SHaiyang Zhang 			       resp->msg_len);
2911b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
2921b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
2931b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
2941b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
29595fa0405SHaiyang Zhang 		} else {
29695fa0405SHaiyang Zhang 			netdev_err(ndev,
29795fa0405SHaiyang Zhang 				"rndis response buffer overflow "
29895fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
29995fa0405SHaiyang Zhang 				resp->msg_len,
30086eedaccSKY Srinivasan 				sizeof(struct rndis_message));
30195fa0405SHaiyang Zhang 
30295fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
30351491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
30495fa0405SHaiyang Zhang 				/* does not have a request id field */
30595fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
306007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
30795fa0405SHaiyang Zhang 			} else {
30895fa0405SHaiyang Zhang 				request->response_msg.msg.
30995fa0405SHaiyang Zhang 				init_complete.status =
310007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
31195fa0405SHaiyang Zhang 			}
31295fa0405SHaiyang Zhang 		}
31395fa0405SHaiyang Zhang 
31495fa0405SHaiyang Zhang 		complete(&request->wait_event);
31595fa0405SHaiyang Zhang 	} else {
31695fa0405SHaiyang Zhang 		netdev_err(ndev,
31795fa0405SHaiyang Zhang 			"no rndis request found for this response "
31895fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
31995fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
32095fa0405SHaiyang Zhang 			resp->ndis_msg_type);
32195fa0405SHaiyang Zhang 	}
32295fa0405SHaiyang Zhang }
32395fa0405SHaiyang Zhang 
3241f5f3a75SHaiyang Zhang /*
3251f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3261f5f3a75SHaiyang Zhang  * return NULL if not found.
3271f5f3a75SHaiyang Zhang  */
3281f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
3291f5f3a75SHaiyang Zhang {
3301f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3311f5f3a75SHaiyang Zhang 	int len;
3321f5f3a75SHaiyang Zhang 
3331f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3341f5f3a75SHaiyang Zhang 		return NULL;
3351f5f3a75SHaiyang Zhang 
3361f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3371f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3381f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3391f5f3a75SHaiyang Zhang 
3401f5f3a75SHaiyang Zhang 	while (len > 0) {
3411f5f3a75SHaiyang Zhang 		if (ppi->type == type)
3421f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3431f5f3a75SHaiyang Zhang 		len -= ppi->size;
3441f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3451f5f3a75SHaiyang Zhang 	}
3461f5f3a75SHaiyang Zhang 
3471f5f3a75SHaiyang Zhang 	return NULL;
3481f5f3a75SHaiyang Zhang }
3491f5f3a75SHaiyang Zhang 
350dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev,
351dc54a08cSstephen hemminger 				     struct rndis_device *dev,
35295fa0405SHaiyang Zhang 				     struct rndis_message *msg,
353dc54a08cSstephen hemminger 				     struct vmbus_channel *channel,
354dc54a08cSstephen hemminger 				     void *data, u32 data_buflen)
35595fa0405SHaiyang Zhang {
356dc54a08cSstephen hemminger 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
357dc54a08cSstephen hemminger 	const struct ndis_tcp_ip_checksum_info *csum_info;
358dc54a08cSstephen hemminger 	const struct ndis_pkt_8021q_info *vlan;
35995fa0405SHaiyang Zhang 	u32 data_offset;
36095fa0405SHaiyang Zhang 
36195fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
36295fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
36395fa0405SHaiyang Zhang 
364dc54a08cSstephen hemminger 	data_buflen -= data_offset;
3654b8a8bc9SWei Yongjun 
3664b8a8bc9SWei Yongjun 	/*
3674b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
3684b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
3694b8a8bc9SWei Yongjun 	 */
370dc54a08cSstephen hemminger 	if (unlikely(data_buflen < rndis_pkt->data_len)) {
3713d541ac5SVitaly Kuznetsov 		netdev_err(dev->ndev, "rndis message buffer "
3724b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
3734b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
374dc54a08cSstephen hemminger 			   data_buflen, rndis_pkt->data_len);
37510082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
3764b8a8bc9SWei Yongjun 	}
3774b8a8bc9SWei Yongjun 
378dc54a08cSstephen hemminger 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
379dc54a08cSstephen hemminger 
3804b8a8bc9SWei Yongjun 	/*
3814b8a8bc9SWei Yongjun 	 * Remove the rndis trailer padding from rndis packet message
3824b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
3834b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
3844b8a8bc9SWei Yongjun 	 */
385dc54a08cSstephen hemminger 	data = (void *)((unsigned long)data + data_offset);
386e3d605edSKY Srinivasan 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
387dc54a08cSstephen hemminger 	return netvsc_recv_callback(ndev, channel,
388dc54a08cSstephen hemminger 				    data, rndis_pkt->data_len,
389dc54a08cSstephen hemminger 				    csum_info, vlan);
39095fa0405SHaiyang Zhang }
39195fa0405SHaiyang Zhang 
392dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev,
393dc54a08cSstephen hemminger 			 struct netvsc_device *net_dev,
394dc54a08cSstephen hemminger 			 struct hv_device *dev,
395dc54a08cSstephen hemminger 			 struct vmbus_channel *channel,
396dc54a08cSstephen hemminger 			 void *data, u32 buflen)
39795fa0405SHaiyang Zhang {
3983d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
399dc54a08cSstephen hemminger 	struct rndis_device *rndis_dev = net_dev->extension;
400dc54a08cSstephen hemminger 	struct rndis_message *rndis_msg = data;
40195fa0405SHaiyang Zhang 
40295fa0405SHaiyang Zhang 	/* Make sure the rndis device state is initialized */
403dc54a08cSstephen hemminger 	if (unlikely(!rndis_dev)) {
404dc54a08cSstephen hemminger 		netif_err(net_device_ctx, rx_err, ndev,
405dc54a08cSstephen hemminger 			  "got rndis message but no rndis device!\n");
406dc54a08cSstephen hemminger 		return NVSP_STAT_FAIL;
40795fa0405SHaiyang Zhang 	}
40895fa0405SHaiyang Zhang 
409dc54a08cSstephen hemminger 	if (unlikely(rndis_dev->state == RNDIS_DEV_UNINITIALIZED)) {
410dc54a08cSstephen hemminger 		netif_err(net_device_ctx, rx_err, ndev,
411dc54a08cSstephen hemminger 			  "got rndis message uninitialized\n");
412dc54a08cSstephen hemminger 		return NVSP_STAT_FAIL;
41395fa0405SHaiyang Zhang 	}
41495fa0405SHaiyang Zhang 
415dc54a08cSstephen hemminger 	if (netif_msg_rx_status(net_device_ctx))
416ef31bef6SHaiyang Zhang 		dump_rndis_message(dev, rndis_msg);
41795fa0405SHaiyang Zhang 
418ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
41951491167SLinus Walleij 	case RNDIS_MSG_PACKET:
420dc54a08cSstephen hemminger 		return rndis_filter_receive_data(ndev, rndis_dev, rndis_msg,
421dc54a08cSstephen hemminger 						 channel, data, buflen);
42251491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
42351491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
42451491167SLinus Walleij 	case RNDIS_MSG_SET_C:
42595fa0405SHaiyang Zhang 		/* completion msgs */
426ef31bef6SHaiyang Zhang 		rndis_filter_receive_response(rndis_dev, rndis_msg);
42795fa0405SHaiyang Zhang 		break;
42895fa0405SHaiyang Zhang 
42951491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
43095fa0405SHaiyang Zhang 		/* notification msgs */
4313a494e71SHaiyang Zhang 		netvsc_linkstatus_callback(dev, rndis_msg);
43295fa0405SHaiyang Zhang 		break;
43395fa0405SHaiyang Zhang 	default:
43495fa0405SHaiyang Zhang 		netdev_err(ndev,
43595fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
436ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
437ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
43895fa0405SHaiyang Zhang 		break;
43995fa0405SHaiyang Zhang 	}
44095fa0405SHaiyang Zhang 
441dc54a08cSstephen hemminger 	return 0;
44295fa0405SHaiyang Zhang }
44395fa0405SHaiyang Zhang 
44495fa0405SHaiyang Zhang static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
44595fa0405SHaiyang Zhang 				  void *result, u32 *result_size)
44695fa0405SHaiyang Zhang {
44795fa0405SHaiyang Zhang 	struct rndis_request *request;
44895fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
44995fa0405SHaiyang Zhang 	struct rndis_query_request *query;
45095fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
45195fa0405SHaiyang Zhang 	int ret = 0;
45295fa0405SHaiyang Zhang 
45395fa0405SHaiyang Zhang 	if (!result)
45495fa0405SHaiyang Zhang 		return -EINVAL;
45595fa0405SHaiyang Zhang 
45695fa0405SHaiyang Zhang 	*result_size = 0;
45751491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
45895fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
45995fa0405SHaiyang Zhang 	if (!request) {
46095fa0405SHaiyang Zhang 		ret = -ENOMEM;
46195fa0405SHaiyang Zhang 		goto cleanup;
46295fa0405SHaiyang Zhang 	}
46395fa0405SHaiyang Zhang 
46495fa0405SHaiyang Zhang 	/* Setup the rndis query */
46595fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
46695fa0405SHaiyang Zhang 	query->oid = oid;
46795fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
46895fa0405SHaiyang Zhang 	query->info_buflen = 0;
46995fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
47095fa0405SHaiyang Zhang 
47123312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
47223312a3bSstephen hemminger 		struct net_device_context *ndevctx = netdev_priv(dev->ndev);
47323312a3bSstephen hemminger 		struct netvsc_device *nvdev = ndevctx->nvdev;
47423312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
47523312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
47623312a3bSstephen hemminger 		u8 ndis_rev;
47723312a3bSstephen hemminger 		size_t size;
47823312a3bSstephen hemminger 
47923312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
48023312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
48123312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
48223312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
48323312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
48423312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
48523312a3bSstephen hemminger 		} else {
48623312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
48723312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
48823312a3bSstephen hemminger 		}
48923312a3bSstephen hemminger 
49023312a3bSstephen hemminger 		request->request_msg.msg_len += size;
49123312a3bSstephen hemminger 		query->info_buflen = size;
49223312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
49323312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
49423312a3bSstephen hemminger 
49523312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
49623312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
49723312a3bSstephen hemminger 		hwcaps->header.size = size;
49823312a3bSstephen hemminger 
49923312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
5005b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
5015b54dac8SHaiyang Zhang 
5025b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
5035b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
5045b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
5055b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5065b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5075b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5085b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5095b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5105b54dac8SHaiyang Zhang 	}
5115b54dac8SHaiyang Zhang 
51295fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
51395fa0405SHaiyang Zhang 	if (ret != 0)
51495fa0405SHaiyang Zhang 		goto cleanup;
51595fa0405SHaiyang Zhang 
5165362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
51795fa0405SHaiyang Zhang 
51895fa0405SHaiyang Zhang 	/* Copy the response back */
51995fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
52095fa0405SHaiyang Zhang 
52195fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
52295fa0405SHaiyang Zhang 		ret = -1;
52395fa0405SHaiyang Zhang 		goto cleanup;
52495fa0405SHaiyang Zhang 	}
52595fa0405SHaiyang Zhang 
52695fa0405SHaiyang Zhang 	memcpy(result,
52795fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
52895fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
52995fa0405SHaiyang Zhang 	       query_complete->info_buflen);
53095fa0405SHaiyang Zhang 
53195fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
53295fa0405SHaiyang Zhang 
53395fa0405SHaiyang Zhang cleanup:
53495fa0405SHaiyang Zhang 	if (request)
53595fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
53695fa0405SHaiyang Zhang 
53795fa0405SHaiyang Zhang 	return ret;
53895fa0405SHaiyang Zhang }
53995fa0405SHaiyang Zhang 
54023312a3bSstephen hemminger /* Get the hardware offload capabilities */
54123312a3bSstephen hemminger static int
54223312a3bSstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct ndis_offload *caps)
54323312a3bSstephen hemminger {
54423312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
54523312a3bSstephen hemminger 	int ret;
54623312a3bSstephen hemminger 
54723312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
54823312a3bSstephen hemminger 
54923312a3bSstephen hemminger 	ret = rndis_filter_query_device(dev,
55023312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
55123312a3bSstephen hemminger 					caps, &caps_len);
55223312a3bSstephen hemminger 	if (ret)
55323312a3bSstephen hemminger 		return ret;
55423312a3bSstephen hemminger 
55523312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
55623312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
55723312a3bSstephen hemminger 			    caps->header.type);
55823312a3bSstephen hemminger 		return -EINVAL;
55923312a3bSstephen hemminger 	}
56023312a3bSstephen hemminger 
56123312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
56223312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
56323312a3bSstephen hemminger 			    caps->header.revision);
56423312a3bSstephen hemminger 		return -EINVAL;
56523312a3bSstephen hemminger 	}
56623312a3bSstephen hemminger 
56723312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
56823312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
56923312a3bSstephen hemminger 		netdev_warn(dev->ndev,
57023312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
57123312a3bSstephen hemminger 			    caps->header.size, caps_len);
57223312a3bSstephen hemminger 		return -EINVAL;
57323312a3bSstephen hemminger 	}
57423312a3bSstephen hemminger 
57523312a3bSstephen hemminger 	return 0;
57623312a3bSstephen hemminger }
57723312a3bSstephen hemminger 
57895fa0405SHaiyang Zhang static int rndis_filter_query_device_mac(struct rndis_device *dev)
57995fa0405SHaiyang Zhang {
58095fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
58195fa0405SHaiyang Zhang 
58295fa0405SHaiyang Zhang 	return rndis_filter_query_device(dev,
58395fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
58495fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
58595fa0405SHaiyang Zhang }
58695fa0405SHaiyang Zhang 
5871ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
5881ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
5891ce09e89SHaiyang Zhang 
590e834da9aSVitaly Kuznetsov int rndis_filter_set_device_mac(struct net_device *ndev, char *mac)
5911ce09e89SHaiyang Zhang {
5922625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev);
5931ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
5941ce09e89SHaiyang Zhang 	struct rndis_request *request;
5951ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
5961ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
5971ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
5981ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
5991ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
6001ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
6011ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
602999028ccSNicholas Mc Guire 	int ret;
6031ce09e89SHaiyang Zhang 
6041ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6051ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6061ce09e89SHaiyang Zhang 	if (!request)
6071ce09e89SHaiyang Zhang 		return -ENOMEM;
6081ce09e89SHaiyang Zhang 
6091ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
6101ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
6111ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
6121ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
6131ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
6141ce09e89SHaiyang Zhang 
6151ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
6161ce09e89SHaiyang Zhang 		set->info_buf_offset);
6171ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
6181ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
6191ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
6201ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
6211ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
6221ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
6231ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
6241ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
6251ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
6261ce09e89SHaiyang Zhang 
6271ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
6281ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
6291ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
6301ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
6311ce09e89SHaiyang Zhang 	if (ret < 0)
6321ce09e89SHaiyang Zhang 		goto cleanup;
6331ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
6341ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
6351ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
6361ce09e89SHaiyang Zhang 	if (ret < 0)
6371ce09e89SHaiyang Zhang 		goto cleanup;
6381ce09e89SHaiyang Zhang 
6391ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
6401ce09e89SHaiyang Zhang 	if (ret != 0)
6411ce09e89SHaiyang Zhang 		goto cleanup;
6421ce09e89SHaiyang Zhang 
6435362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
6445362855aSVitaly Kuznetsov 
6451ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
646b02a8067SHaiyang Zhang 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
647b02a8067SHaiyang Zhang 		netdev_err(ndev, "Fail to set MAC on host side:0x%x\n",
648b02a8067SHaiyang Zhang 			   set_complete->status);
6491ce09e89SHaiyang Zhang 		ret = -EINVAL;
6501ce09e89SHaiyang Zhang 	}
6511ce09e89SHaiyang Zhang 
6521ce09e89SHaiyang Zhang cleanup:
6531ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
6541ce09e89SHaiyang Zhang 	return ret;
6551ce09e89SHaiyang Zhang }
6561ce09e89SHaiyang Zhang 
657da19fcd0SLad, Prabhakar static int
658426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
6594a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
6604a0e70aeSKY Srinivasan {
6612625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev);
6624a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
6634a0e70aeSKY Srinivasan 	struct rndis_request *request;
6644a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
6654a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
6664a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
6674a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
668999028ccSNicholas Mc Guire 	int ret;
669af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
670af9893a3SKY Srinivasan 
671af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
672af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
673af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
674af9893a3SKY Srinivasan 		 * UDP checksum offload.
675af9893a3SKY Srinivasan 		 */
676af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
677af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
678af9893a3SKY Srinivasan 	}
6794a0e70aeSKY Srinivasan 
6804a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6814a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6824a0e70aeSKY Srinivasan 	if (!request)
6834a0e70aeSKY Srinivasan 		return -ENOMEM;
6844a0e70aeSKY Srinivasan 
6854a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
6864a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
6874a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
6884a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
6894a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
6904a0e70aeSKY Srinivasan 
6914a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
6924a0e70aeSKY Srinivasan 				set->info_buf_offset);
6934a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
6944a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
6954a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
6964a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
6974a0e70aeSKY Srinivasan 
6984a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
6994a0e70aeSKY Srinivasan 	if (ret != 0)
7004a0e70aeSKY Srinivasan 		goto cleanup;
7014a0e70aeSKY Srinivasan 
7025362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7034a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
7044a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
705af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
7064a0e70aeSKY Srinivasan 			   set_complete->status);
7074a0e70aeSKY Srinivasan 		ret = -EINVAL;
7084a0e70aeSKY Srinivasan 	}
7094a0e70aeSKY Srinivasan 
7104a0e70aeSKY Srinivasan cleanup:
7114a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
7124a0e70aeSKY Srinivasan 	return ret;
7134a0e70aeSKY Srinivasan }
7141ce09e89SHaiyang Zhang 
715962f3feeSstephen hemminger int rndis_filter_set_rss_param(struct rndis_device *rdev,
716962f3feeSstephen hemminger 			       const u8 *rss_key, int num_queue)
7175b54dac8SHaiyang Zhang {
7183d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
7195b54dac8SHaiyang Zhang 	struct rndis_request *request;
7205b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7215b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7225b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
723962f3feeSstephen hemminger 		     4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
7245b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7255b54dac8SHaiyang Zhang 	u32 *itab;
7265b54dac8SHaiyang Zhang 	u8 *keyp;
727999028ccSNicholas Mc Guire 	int i, ret;
7285b54dac8SHaiyang Zhang 
7295b54dac8SHaiyang Zhang 	request = get_rndis_request(
7305b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7315b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7325b54dac8SHaiyang Zhang 	if (!request)
7335b54dac8SHaiyang Zhang 		return -ENOMEM;
7345b54dac8SHaiyang Zhang 
7355b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7365b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7375b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
7385b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
7395b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
7405b54dac8SHaiyang Zhang 
7415b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
7425b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
7435b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
7445b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
7455b54dac8SHaiyang Zhang 	rssp->flag = 0;
7465b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
7474c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
7484c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
7495b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
7505b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
751962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
7525b54dac8SHaiyang Zhang 	rssp->kashkey_offset = rssp->indirect_taboffset +
7535b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
7545b54dac8SHaiyang Zhang 
7555b54dac8SHaiyang Zhang 	/* Set indirection table entries */
7565b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
7575b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
758ff4a4419Sstephen hemminger 		itab[i] = rdev->ind_table[i];
7595b54dac8SHaiyang Zhang 
7605b54dac8SHaiyang Zhang 	/* Set hask key values */
7615b54dac8SHaiyang Zhang 	keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
762962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
7635b54dac8SHaiyang Zhang 
7645b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7655b54dac8SHaiyang Zhang 	if (ret != 0)
7665b54dac8SHaiyang Zhang 		goto cleanup;
7675b54dac8SHaiyang Zhang 
7685362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7695b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
770962f3feeSstephen hemminger 	if (set_complete->status == RNDIS_STATUS_SUCCESS)
771962f3feeSstephen hemminger 		memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
772962f3feeSstephen hemminger 	else {
7735b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
7745b54dac8SHaiyang Zhang 			   set_complete->status);
7755b54dac8SHaiyang Zhang 		ret = -EINVAL;
7765b54dac8SHaiyang Zhang 	}
7775b54dac8SHaiyang Zhang 
7785b54dac8SHaiyang Zhang cleanup:
7795b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
7805b54dac8SHaiyang Zhang 	return ret;
7815b54dac8SHaiyang Zhang }
7825b54dac8SHaiyang Zhang 
78395fa0405SHaiyang Zhang static int rndis_filter_query_device_link_status(struct rndis_device *dev)
78495fa0405SHaiyang Zhang {
78595fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
78695fa0405SHaiyang Zhang 	u32 link_status;
78795fa0405SHaiyang Zhang 	int ret;
78895fa0405SHaiyang Zhang 
78995fa0405SHaiyang Zhang 	ret = rndis_filter_query_device(dev,
79095fa0405SHaiyang Zhang 				      RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
79195fa0405SHaiyang Zhang 				      &link_status, &size);
79295fa0405SHaiyang Zhang 
79395fa0405SHaiyang Zhang 	return ret;
79495fa0405SHaiyang Zhang }
79595fa0405SHaiyang Zhang 
796b37879e6SHaiyang Zhang static int rndis_filter_query_link_speed(struct rndis_device *dev)
797b37879e6SHaiyang Zhang {
798b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
799b37879e6SHaiyang Zhang 	u32 link_speed;
800b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
801b37879e6SHaiyang Zhang 	int ret;
802b37879e6SHaiyang Zhang 
803b37879e6SHaiyang Zhang 	ret = rndis_filter_query_device(dev, RNDIS_OID_GEN_LINK_SPEED,
804b37879e6SHaiyang Zhang 					&link_speed, &size);
805b37879e6SHaiyang Zhang 
806b37879e6SHaiyang Zhang 	if (!ret) {
807b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
808b37879e6SHaiyang Zhang 
809b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
810b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
811b37879e6SHaiyang Zhang 		 */
812b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
813b37879e6SHaiyang Zhang 	}
814b37879e6SHaiyang Zhang 
815b37879e6SHaiyang Zhang 	return ret;
816b37879e6SHaiyang Zhang }
817b37879e6SHaiyang Zhang 
818d426b2e3SHaiyang Zhang int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter)
81995fa0405SHaiyang Zhang {
82095fa0405SHaiyang Zhang 	struct rndis_request *request;
82195fa0405SHaiyang Zhang 	struct rndis_set_request *set;
82295fa0405SHaiyang Zhang 	struct rndis_set_complete *set_complete;
823999028ccSNicholas Mc Guire 	int ret;
82495fa0405SHaiyang Zhang 
82551491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
82695fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
82795fa0405SHaiyang Zhang 			sizeof(u32));
82895fa0405SHaiyang Zhang 	if (!request) {
82995fa0405SHaiyang Zhang 		ret = -ENOMEM;
83095fa0405SHaiyang Zhang 		goto cleanup;
83195fa0405SHaiyang Zhang 	}
83295fa0405SHaiyang Zhang 
83395fa0405SHaiyang Zhang 	/* Setup the rndis set */
83495fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
83595fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
83695fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
83795fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
83895fa0405SHaiyang Zhang 
83995fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
84095fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
84195fa0405SHaiyang Zhang 
84295fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
84395fa0405SHaiyang Zhang 	if (ret != 0)
84495fa0405SHaiyang Zhang 		goto cleanup;
84595fa0405SHaiyang Zhang 
8465362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
84795fa0405SHaiyang Zhang 
84895fa0405SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
84995fa0405SHaiyang Zhang cleanup:
85095fa0405SHaiyang Zhang 	if (request)
85195fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
85295fa0405SHaiyang Zhang 	return ret;
85395fa0405SHaiyang Zhang }
85495fa0405SHaiyang Zhang 
85595fa0405SHaiyang Zhang static int rndis_filter_init_device(struct rndis_device *dev)
85695fa0405SHaiyang Zhang {
85795fa0405SHaiyang Zhang 	struct rndis_request *request;
85895fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
85995fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
86095fa0405SHaiyang Zhang 	u32 status;
861999028ccSNicholas Mc Guire 	int ret;
8622625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(dev->ndev);
86395fa0405SHaiyang Zhang 
86451491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
86595fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
86695fa0405SHaiyang Zhang 	if (!request) {
86795fa0405SHaiyang Zhang 		ret = -ENOMEM;
86895fa0405SHaiyang Zhang 		goto cleanup;
86995fa0405SHaiyang Zhang 	}
87095fa0405SHaiyang Zhang 
87195fa0405SHaiyang Zhang 	/* Setup the rndis set */
87295fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
87395fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
87495fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
875fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
87695fa0405SHaiyang Zhang 
87795fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
87895fa0405SHaiyang Zhang 
87995fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
88095fa0405SHaiyang Zhang 	if (ret != 0) {
88195fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
88295fa0405SHaiyang Zhang 		goto cleanup;
88395fa0405SHaiyang Zhang 	}
88495fa0405SHaiyang Zhang 
8855362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
88695fa0405SHaiyang Zhang 
88795fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
88895fa0405SHaiyang Zhang 	status = init_complete->status;
88995fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
89095fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
8917c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
8927c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
89395fa0405SHaiyang Zhang 		ret = 0;
89495fa0405SHaiyang Zhang 	} else {
89595fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
89695fa0405SHaiyang Zhang 		ret = -EINVAL;
89795fa0405SHaiyang Zhang 	}
89895fa0405SHaiyang Zhang 
89995fa0405SHaiyang Zhang cleanup:
90095fa0405SHaiyang Zhang 	if (request)
90195fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
90295fa0405SHaiyang Zhang 
90395fa0405SHaiyang Zhang 	return ret;
90495fa0405SHaiyang Zhang }
90595fa0405SHaiyang Zhang 
90695fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev)
90795fa0405SHaiyang Zhang {
90895fa0405SHaiyang Zhang 	struct rndis_request *request;
90995fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
9103d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
9113d541ac5SVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_ctx->nvdev;
9123d541ac5SVitaly Kuznetsov 	struct hv_device *hdev = net_device_ctx->device_ctx;
913ae9e63bbSHaiyang Zhang 	ulong flags;
91495fa0405SHaiyang Zhang 
91595fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
91651491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
91795fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
91895fa0405SHaiyang Zhang 	if (!request)
91995fa0405SHaiyang Zhang 		goto cleanup;
92095fa0405SHaiyang Zhang 
92195fa0405SHaiyang Zhang 	/* Setup the rndis set */
92295fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
92395fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
92495fa0405SHaiyang Zhang 
92595fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
92695fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
92795fa0405SHaiyang Zhang 
92895fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
92995fa0405SHaiyang Zhang 
93095fa0405SHaiyang Zhang cleanup:
931ae9e63bbSHaiyang Zhang 	spin_lock_irqsave(&hdev->channel->inbound_lock, flags);
932ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
933ae9e63bbSHaiyang Zhang 	spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags);
934ae9e63bbSHaiyang Zhang 
935ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
936ae9e63bbSHaiyang Zhang 	wait_event(nvdev->wait_drain,
937c0b558e5SHaiyang Zhang 		   atomic_read(&nvdev->num_outstanding_sends) == 0 &&
938c0b558e5SHaiyang Zhang 		   atomic_read(&nvdev->num_outstanding_recvs) == 0);
939ae9e63bbSHaiyang Zhang 
94095fa0405SHaiyang Zhang 	if (request)
94195fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
94295fa0405SHaiyang Zhang }
94395fa0405SHaiyang Zhang 
94495fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
94595fa0405SHaiyang Zhang {
94695fa0405SHaiyang Zhang 	int ret;
94795fa0405SHaiyang Zhang 
94895fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
94995fa0405SHaiyang Zhang 		return 0;
95095fa0405SHaiyang Zhang 
95195fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
95295fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
95395fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
95495fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
95595fa0405SHaiyang Zhang 	if (ret == 0)
95695fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
95795fa0405SHaiyang Zhang 
95895fa0405SHaiyang Zhang 	return ret;
95995fa0405SHaiyang Zhang }
96095fa0405SHaiyang Zhang 
96195fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
96295fa0405SHaiyang Zhang {
96395fa0405SHaiyang Zhang 	int ret;
96495fa0405SHaiyang Zhang 
96595fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
96695fa0405SHaiyang Zhang 		return 0;
96795fa0405SHaiyang Zhang 
96895fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
969c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
970c3582a2cSHaiyang Zhang 		ret = 0;
971c3582a2cSHaiyang Zhang 
97295fa0405SHaiyang Zhang 	if (ret == 0)
97395fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
97495fa0405SHaiyang Zhang 
97595fa0405SHaiyang Zhang 	return ret;
97695fa0405SHaiyang Zhang }
97795fa0405SHaiyang Zhang 
9785b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
9795b54dac8SHaiyang Zhang {
9803d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
9813d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
9822625466dSVitaly Kuznetsov 	struct netvsc_device *nvscdev = net_device_to_netvsc_device(ndev);
9835b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
9845b54dac8SHaiyang Zhang 	int ret;
985b3e6b82aSKY Srinivasan 	unsigned long flags;
9865b54dac8SHaiyang Zhang 
9875b54dac8SHaiyang Zhang 	if (chn_index >= nvscdev->num_chn)
9885b54dac8SHaiyang Zhang 		return;
9895b54dac8SHaiyang Zhang 
990b8b835a8Sstephen hemminger 	nvscdev->chan_table[chn_index].mrc.buf
991b8b835a8Sstephen hemminger 		= vzalloc(NETVSC_RECVSLOT_MAX * sizeof(struct recv_comp_data));
992c0b558e5SHaiyang Zhang 
9935b54dac8SHaiyang Zhang 	ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
9945b54dac8SHaiyang Zhang 			 nvscdev->ring_size * PAGE_SIZE, NULL, 0,
9955b54dac8SHaiyang Zhang 			 netvsc_channel_cb, new_sc);
9965b54dac8SHaiyang Zhang 
9975b54dac8SHaiyang Zhang 	if (ret == 0)
998b8b835a8Sstephen hemminger 		nvscdev->chan_table[chn_index].channel = new_sc;
9993f735131SHaiyang Zhang 
10003f735131SHaiyang Zhang 	spin_lock_irqsave(&nvscdev->sc_lock, flags);
10013f735131SHaiyang Zhang 	nvscdev->num_sc_offered--;
10023f735131SHaiyang Zhang 	spin_unlock_irqrestore(&nvscdev->sc_lock, flags);
10033f735131SHaiyang Zhang 	if (nvscdev->num_sc_offered == 0)
10043f735131SHaiyang Zhang 		complete(&nvscdev->channel_init_wait);
10055b54dac8SHaiyang Zhang }
10065b54dac8SHaiyang Zhang 
100795fa0405SHaiyang Zhang int rndis_filter_device_add(struct hv_device *dev,
100895fa0405SHaiyang Zhang 			    void *additional_info)
100995fa0405SHaiyang Zhang {
10103d541ac5SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
10113d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
101295fa0405SHaiyang Zhang 	struct netvsc_device *net_device;
101395fa0405SHaiyang Zhang 	struct rndis_device *rndis_device;
101495fa0405SHaiyang Zhang 	struct netvsc_device_info *device_info = additional_info;
101523312a3bSstephen hemminger 	struct ndis_offload hwcaps;
10164a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
10175b54dac8SHaiyang Zhang 	struct nvsp_message *init_packet;
10185b54dac8SHaiyang Zhang 	struct ndis_recv_scale_cap rsscap;
10195b54dac8SHaiyang Zhang 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
102023312a3bSstephen hemminger 	unsigned int gso_max_size = GSO_MAX_SIZE;
10214d3c9d37SHaiyang Zhang 	u32 mtu, size;
1022e01ec219SKY Srinivasan 	u32 num_rss_qs;
1023b3e6b82aSKY Srinivasan 	u32 sc_delta;
1024e01ec219SKY Srinivasan 	const struct cpumask *node_cpu_mask;
1025e01ec219SKY Srinivasan 	u32 num_possible_rss_qs;
1026b3e6b82aSKY Srinivasan 	unsigned long flags;
1027ff4a4419Sstephen hemminger 	int i, ret;
102895fa0405SHaiyang Zhang 
102995fa0405SHaiyang Zhang 	rndis_device = get_rndis_device();
103095fa0405SHaiyang Zhang 	if (!rndis_device)
103195fa0405SHaiyang Zhang 		return -ENODEV;
103295fa0405SHaiyang Zhang 
103395fa0405SHaiyang Zhang 	/*
103495fa0405SHaiyang Zhang 	 * Let the inner driver handle this first to create the netvsc channel
103595fa0405SHaiyang Zhang 	 * NOTE! Once the channel is created, we may get a receive callback
103695fa0405SHaiyang Zhang 	 * (RndisFilterOnReceive()) before this call is completed
103795fa0405SHaiyang Zhang 	 */
103895fa0405SHaiyang Zhang 	ret = netvsc_device_add(dev, additional_info);
103995fa0405SHaiyang Zhang 	if (ret != 0) {
104095fa0405SHaiyang Zhang 		kfree(rndis_device);
104195fa0405SHaiyang Zhang 		return ret;
104295fa0405SHaiyang Zhang 	}
104395fa0405SHaiyang Zhang 
104495fa0405SHaiyang Zhang 	/* Initialize the rndis device */
10453d541ac5SVitaly Kuznetsov 	net_device = net_device_ctx->nvdev;
104659995370SAndrew Schwartzmeyer 	net_device->max_chn = 1;
10475b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
104895fa0405SHaiyang Zhang 
1049b3e6b82aSKY Srinivasan 	spin_lock_init(&net_device->sc_lock);
1050b3e6b82aSKY Srinivasan 
105195fa0405SHaiyang Zhang 	net_device->extension = rndis_device;
10523d541ac5SVitaly Kuznetsov 	rndis_device->ndev = net;
105395fa0405SHaiyang Zhang 
105495fa0405SHaiyang Zhang 	/* Send the rndis initialization message */
105595fa0405SHaiyang Zhang 	ret = rndis_filter_init_device(rndis_device);
105695fa0405SHaiyang Zhang 	if (ret != 0) {
10575243e7bdSHaiyang Zhang 		rndis_filter_device_remove(dev);
10585243e7bdSHaiyang Zhang 		return ret;
105995fa0405SHaiyang Zhang 	}
106095fa0405SHaiyang Zhang 
10614d3c9d37SHaiyang Zhang 	/* Get the MTU from the host */
10624d3c9d37SHaiyang Zhang 	size = sizeof(u32);
10634d3c9d37SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
10644d3c9d37SHaiyang Zhang 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
10654d3c9d37SHaiyang Zhang 					&mtu, &size);
10660a1275caSVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
10670a1275caSVitaly Kuznetsov 		net->mtu = mtu;
10684d3c9d37SHaiyang Zhang 
106995fa0405SHaiyang Zhang 	/* Get the mac address */
107095fa0405SHaiyang Zhang 	ret = rndis_filter_query_device_mac(rndis_device);
107195fa0405SHaiyang Zhang 	if (ret != 0) {
10725243e7bdSHaiyang Zhang 		rndis_filter_device_remove(dev);
10735243e7bdSHaiyang Zhang 		return ret;
107495fa0405SHaiyang Zhang 	}
107595fa0405SHaiyang Zhang 
107695fa0405SHaiyang Zhang 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
107795fa0405SHaiyang Zhang 
107823312a3bSstephen hemminger 	/* Find HW offload capabilities */
107923312a3bSstephen hemminger 	ret = rndis_query_hwcaps(rndis_device, &hwcaps);
108023312a3bSstephen hemminger 	if (ret != 0) {
108123312a3bSstephen hemminger 		rndis_filter_device_remove(dev);
108223312a3bSstephen hemminger 		return ret;
108323312a3bSstephen hemminger 	}
108423312a3bSstephen hemminger 
108523312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
10864a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
108723312a3bSstephen hemminger 
108823312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
108923312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
109023312a3bSstephen hemminger 
109123312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
109223312a3bSstephen hemminger 	net->hw_features = NETIF_F_RXCSUM;
109323312a3bSstephen hemminger 
109423312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
109523312a3bSstephen hemminger 		/* Can checksum TCP */
109623312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
109723312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
109823312a3bSstephen hemminger 
10994a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
110023312a3bSstephen hemminger 
110123312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
11024a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
110323312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
110423312a3bSstephen hemminger 
110523312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
110623312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
110723312a3bSstephen hemminger 		}
110823312a3bSstephen hemminger 
110923312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
111023312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
111123312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
111223312a3bSstephen hemminger 		}
111323312a3bSstephen hemminger 	}
111423312a3bSstephen hemminger 
111523312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
111623312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
111723312a3bSstephen hemminger 
111823312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
111923312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
112023312a3bSstephen hemminger 
112123312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
112223312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
112323312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
112423312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
112523312a3bSstephen hemminger 
112623312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
112723312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
112823312a3bSstephen hemminger 		}
112923312a3bSstephen hemminger 
113023312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
113123312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
113223312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
113323312a3bSstephen hemminger 		}
113423312a3bSstephen hemminger 	}
113523312a3bSstephen hemminger 
113623312a3bSstephen hemminger 	netif_set_gso_max_size(net, gso_max_size);
11374a0e70aeSKY Srinivasan 
1138426d9541SVitaly Kuznetsov 	ret = rndis_filter_set_offload_params(net, &offloads);
11394a0e70aeSKY Srinivasan 	if (ret)
11404a0e70aeSKY Srinivasan 		goto err_dev_remv;
11414a0e70aeSKY Srinivasan 
114295fa0405SHaiyang Zhang 	rndis_filter_query_device_link_status(rndis_device);
114395fa0405SHaiyang Zhang 
114495fa0405SHaiyang Zhang 	device_info->link_state = rndis_device->link_state;
114595fa0405SHaiyang Zhang 
114693ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
114795fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
114895fa0405SHaiyang Zhang 		   device_info->link_state ? "down" : "up");
114995fa0405SHaiyang Zhang 
11505b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
11515b54dac8SHaiyang Zhang 		return 0;
11525b54dac8SHaiyang Zhang 
1153b37879e6SHaiyang Zhang 	rndis_filter_query_link_speed(rndis_device);
1154b37879e6SHaiyang Zhang 
11555b54dac8SHaiyang Zhang 	/* vRSS setup */
11565b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
11575b54dac8SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
11585b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
11595b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
11605b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
11615b54dac8SHaiyang Zhang 		goto out;
11625b54dac8SHaiyang Zhang 
11639efc2f7dSHaiyang Zhang 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, rsscap.num_recv_que);
1164e01ec219SKY Srinivasan 
11659efc2f7dSHaiyang Zhang 	num_rss_qs = min(device_info->max_num_vrss_chns, net_device->max_chn);
1166e01ec219SKY Srinivasan 
1167e01ec219SKY Srinivasan 	/*
1168e01ec219SKY Srinivasan 	 * We will limit the VRSS channels to the number CPUs in the NUMA node
1169e01ec219SKY Srinivasan 	 * the primary channel is currently bound to.
1170e01ec219SKY Srinivasan 	 */
1171e01ec219SKY Srinivasan 	node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu));
1172e01ec219SKY Srinivasan 	num_possible_rss_qs = cpumask_weight(node_cpu_mask);
11738ebdcc52SAndrew Schwartzmeyer 
11748ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
11758ebdcc52SAndrew Schwartzmeyer 	if (device_info->num_chn && device_info->num_chn < net_device->max_chn)
11768ebdcc52SAndrew Schwartzmeyer 		net_device->num_chn = device_info->num_chn;
11778ebdcc52SAndrew Schwartzmeyer 	else
1178e01ec219SKY Srinivasan 		net_device->num_chn = min(num_possible_rss_qs, num_rss_qs);
1179e01ec219SKY Srinivasan 
1180b3e6b82aSKY Srinivasan 	num_rss_qs = net_device->num_chn - 1;
1181ff4a4419Sstephen hemminger 
1182ff4a4419Sstephen hemminger 	for (i = 0; i < ITAB_NUM; i++)
1183ff4a4419Sstephen hemminger 		rndis_device->ind_table[i] = ethtool_rxfh_indir_default(i,
1184ff4a4419Sstephen hemminger 							net_device->num_chn);
1185ff4a4419Sstephen hemminger 
1186b3e6b82aSKY Srinivasan 	net_device->num_sc_offered = num_rss_qs;
1187b3e6b82aSKY Srinivasan 
11885b54dac8SHaiyang Zhang 	if (net_device->num_chn == 1)
11895b54dac8SHaiyang Zhang 		goto out;
11905b54dac8SHaiyang Zhang 
11915b54dac8SHaiyang Zhang 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
11925b54dac8SHaiyang Zhang 
11935b54dac8SHaiyang Zhang 	init_packet = &net_device->channel_init_pkt;
11945b54dac8SHaiyang Zhang 	memset(init_packet, 0, sizeof(struct nvsp_message));
11955b54dac8SHaiyang Zhang 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
11965b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
11975b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
11985b54dac8SHaiyang Zhang 						net_device->num_chn - 1;
11995b54dac8SHaiyang Zhang 	ret = vmbus_sendpacket(dev->channel, init_packet,
12005b54dac8SHaiyang Zhang 			       sizeof(struct nvsp_message),
12015b54dac8SHaiyang Zhang 			       (unsigned long)init_packet,
12025b54dac8SHaiyang Zhang 			       VM_PKT_DATA_INBAND,
12035b54dac8SHaiyang Zhang 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
12045b54dac8SHaiyang Zhang 	if (ret)
12055b54dac8SHaiyang Zhang 		goto out;
12065362855aSVitaly Kuznetsov 	wait_for_completion(&net_device->channel_init_wait);
12075362855aSVitaly Kuznetsov 
12085b54dac8SHaiyang Zhang 	if (init_packet->msg.v5_msg.subchn_comp.status !=
12095b54dac8SHaiyang Zhang 	    NVSP_STAT_SUCCESS) {
12105b54dac8SHaiyang Zhang 		ret = -ENODEV;
12115b54dac8SHaiyang Zhang 		goto out;
12125b54dac8SHaiyang Zhang 	}
12135b54dac8SHaiyang Zhang 	net_device->num_chn = 1 +
12145b54dac8SHaiyang Zhang 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
12155b54dac8SHaiyang Zhang 
1216962f3feeSstephen hemminger 	ret = rndis_filter_set_rss_param(rndis_device, netvsc_hash_key,
1217962f3feeSstephen hemminger 					 net_device->num_chn);
12185b54dac8SHaiyang Zhang 
1219b3e6b82aSKY Srinivasan 	/*
1220d66ab514SHaiyang Zhang 	 * Set the number of sub-channels to be received.
1221b3e6b82aSKY Srinivasan 	 */
1222b3e6b82aSKY Srinivasan 	spin_lock_irqsave(&net_device->sc_lock, flags);
1223b3e6b82aSKY Srinivasan 	sc_delta = num_rss_qs - (net_device->num_chn - 1);
1224b3e6b82aSKY Srinivasan 	net_device->num_sc_offered -= sc_delta;
1225b3e6b82aSKY Srinivasan 	spin_unlock_irqrestore(&net_device->sc_lock, flags);
1226b3e6b82aSKY Srinivasan 
12275b54dac8SHaiyang Zhang out:
122859995370SAndrew Schwartzmeyer 	if (ret) {
122959995370SAndrew Schwartzmeyer 		net_device->max_chn = 1;
12305b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
1231d66ab514SHaiyang Zhang 		net_device->num_sc_offered = 0;
123259995370SAndrew Schwartzmeyer 	}
1233b3e6b82aSKY Srinivasan 
12345b54dac8SHaiyang Zhang 	return 0; /* return 0 because primary channel can be used alone */
12354a0e70aeSKY Srinivasan 
12364a0e70aeSKY Srinivasan err_dev_remv:
12374a0e70aeSKY Srinivasan 	rndis_filter_device_remove(dev);
12384a0e70aeSKY Srinivasan 	return ret;
123995fa0405SHaiyang Zhang }
124095fa0405SHaiyang Zhang 
124195fa0405SHaiyang Zhang void rndis_filter_device_remove(struct hv_device *dev)
124295fa0405SHaiyang Zhang {
12432625466dSVitaly Kuznetsov 	struct netvsc_device *net_dev = hv_device_to_netvsc_device(dev);
124495fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1245d66ab514SHaiyang Zhang 
1246d66ab514SHaiyang Zhang 	/* If not all subchannel offers are complete, wait for them until
1247d66ab514SHaiyang Zhang 	 * completion to avoid race.
1248d66ab514SHaiyang Zhang 	 */
12495362855aSVitaly Kuznetsov 	if (net_dev->num_sc_offered > 0)
12505362855aSVitaly Kuznetsov 		wait_for_completion(&net_dev->channel_init_wait);
125195fa0405SHaiyang Zhang 
125295fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
125395fa0405SHaiyang Zhang 	rndis_filter_halt_device(rndis_dev);
125495fa0405SHaiyang Zhang 
125595fa0405SHaiyang Zhang 	kfree(rndis_dev);
125695fa0405SHaiyang Zhang 	net_dev->extension = NULL;
125795fa0405SHaiyang Zhang 
125895fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
125995fa0405SHaiyang Zhang }
126095fa0405SHaiyang Zhang 
12612f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev)
126295fa0405SHaiyang Zhang {
12632f5fa6c8SVitaly Kuznetsov 	if (!nvdev)
126495fa0405SHaiyang Zhang 		return -EINVAL;
126595fa0405SHaiyang Zhang 
12662f5fa6c8SVitaly Kuznetsov 	if (atomic_inc_return(&nvdev->open_cnt) != 1)
126784bf9cefSKY Srinivasan 		return 0;
126884bf9cefSKY Srinivasan 
12692f5fa6c8SVitaly Kuznetsov 	return rndis_filter_open_device(nvdev->extension);
127095fa0405SHaiyang Zhang }
127195fa0405SHaiyang Zhang 
12722f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev)
127395fa0405SHaiyang Zhang {
12745fccab3bSHaiyang Zhang 	if (!nvdev)
127595fa0405SHaiyang Zhang 		return -EINVAL;
127695fa0405SHaiyang Zhang 
127784bf9cefSKY Srinivasan 	if (atomic_dec_return(&nvdev->open_cnt) != 0)
127884bf9cefSKY Srinivasan 		return 0;
127984bf9cefSKY Srinivasan 
12805fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
128195fa0405SHaiyang Zhang }
1282