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 
344f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w);
3595fa0405SHaiyang Zhang 
365b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE
3795fa0405SHaiyang Zhang struct rndis_request {
3895fa0405SHaiyang Zhang 	struct list_head list_ent;
3995fa0405SHaiyang Zhang 	struct completion  wait_event;
4095fa0405SHaiyang Zhang 
4195fa0405SHaiyang Zhang 	struct rndis_message response_msg;
42a3a6cab5SHaiyang Zhang 	/*
43a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
44a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
45a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
46a3a6cab5SHaiyang Zhang 	 * future.
47a3a6cab5SHaiyang Zhang 	 */
48a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
4995fa0405SHaiyang Zhang 
5095fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
5195fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
520f48917bSHaiyang Zhang 
5395fa0405SHaiyang Zhang 	struct rndis_message request_msg;
540f48917bSHaiyang Zhang 	/*
55a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
56a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
570f48917bSHaiyang Zhang 	 */
58a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
5995fa0405SHaiyang Zhang };
6095fa0405SHaiyang Zhang 
61962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = {
62962f3feeSstephen hemminger 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
63962f3feeSstephen hemminger 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
64962f3feeSstephen hemminger 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
65962f3feeSstephen hemminger 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
66962f3feeSstephen hemminger 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
67962f3feeSstephen hemminger };
68962f3feeSstephen hemminger 
6995fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
7095fa0405SHaiyang Zhang {
7195fa0405SHaiyang Zhang 	struct rndis_device *device;
7295fa0405SHaiyang Zhang 
7395fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
7495fa0405SHaiyang Zhang 	if (!device)
7595fa0405SHaiyang Zhang 		return NULL;
7695fa0405SHaiyang Zhang 
7795fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
7895fa0405SHaiyang Zhang 
7995fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
804f19c0d8Sstephen hemminger 	INIT_WORK(&device->mcast_work, rndis_set_multicast);
8195fa0405SHaiyang Zhang 
8295fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
8395fa0405SHaiyang Zhang 
8495fa0405SHaiyang Zhang 	return device;
8595fa0405SHaiyang Zhang }
8695fa0405SHaiyang Zhang 
873962981fSstephen hemminger static struct netvsc_device *
883962981fSstephen hemminger net_device_to_netvsc_device(struct net_device *ndev)
893962981fSstephen hemminger {
903962981fSstephen hemminger 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
913962981fSstephen hemminger 
923962981fSstephen hemminger 	return rtnl_dereference(net_device_ctx->nvdev);
933962981fSstephen hemminger }
943962981fSstephen hemminger 
9595fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
9695fa0405SHaiyang Zhang 					     u32 msg_type,
9795fa0405SHaiyang Zhang 					     u32 msg_len)
9895fa0405SHaiyang Zhang {
9995fa0405SHaiyang Zhang 	struct rndis_request *request;
10095fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
10195fa0405SHaiyang Zhang 	struct rndis_set_request *set;
10295fa0405SHaiyang Zhang 	unsigned long flags;
10395fa0405SHaiyang Zhang 
10495fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
10595fa0405SHaiyang Zhang 	if (!request)
10695fa0405SHaiyang Zhang 		return NULL;
10795fa0405SHaiyang Zhang 
10895fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
10995fa0405SHaiyang Zhang 
11095fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
11195fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
11295fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
11395fa0405SHaiyang Zhang 
1145b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
1155b54dac8SHaiyang Zhang 
11695fa0405SHaiyang Zhang 	/*
11795fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
11895fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
11995fa0405SHaiyang Zhang 	 * template
12095fa0405SHaiyang Zhang 	 */
12195fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
12295fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
12395fa0405SHaiyang Zhang 
12495fa0405SHaiyang Zhang 	/* Add to the request list */
12595fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
12695fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
12795fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
12895fa0405SHaiyang Zhang 
12995fa0405SHaiyang Zhang 	return request;
13095fa0405SHaiyang Zhang }
13195fa0405SHaiyang Zhang 
13295fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
13395fa0405SHaiyang Zhang 			    struct rndis_request *req)
13495fa0405SHaiyang Zhang {
13595fa0405SHaiyang Zhang 	unsigned long flags;
13695fa0405SHaiyang Zhang 
13795fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
13895fa0405SHaiyang Zhang 	list_del(&req->list_ent);
13995fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
14095fa0405SHaiyang Zhang 
14195fa0405SHaiyang Zhang 	kfree(req);
14295fa0405SHaiyang Zhang }
14395fa0405SHaiyang Zhang 
14495fa0405SHaiyang Zhang static void dump_rndis_message(struct hv_device *hv_dev,
145dc54a08cSstephen hemminger 			       const struct rndis_message *rndis_msg)
14695fa0405SHaiyang Zhang {
1473d541ac5SVitaly Kuznetsov 	struct net_device *netdev = hv_get_drvdata(hv_dev);
14895fa0405SHaiyang Zhang 
14995fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
15051491167SLinus Walleij 	case RNDIS_MSG_PACKET:
15151491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
15295fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
15395fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
15495fa0405SHaiyang Zhang 			   "pkt len %u\n",
15595fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
15695fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
15795fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
15895fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
15995fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
16095fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
16195fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
16295fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
16395fa0405SHaiyang Zhang 		break;
16495fa0405SHaiyang Zhang 
16551491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
16651491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
16795fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
16895fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
16995fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
17095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
17195fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
17295fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
17395fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
17495fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
17595fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
17695fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
17795fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17895fa0405SHaiyang Zhang 			   max_pkt_per_msg,
17995fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
18095fa0405SHaiyang Zhang 			   pkt_alignment_factor);
18195fa0405SHaiyang Zhang 		break;
18295fa0405SHaiyang Zhang 
18351491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
18451491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
18595fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
18695fa0405SHaiyang Zhang 			"buf offset %u)\n",
18795fa0405SHaiyang Zhang 			rndis_msg->msg_len,
18895fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
18995fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
19095fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
19195fa0405SHaiyang Zhang 			   info_buflen,
19295fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
19395fa0405SHaiyang Zhang 			   info_buf_offset);
19495fa0405SHaiyang Zhang 		break;
19595fa0405SHaiyang Zhang 
19651491167SLinus Walleij 	case RNDIS_MSG_SET_C:
19795fa0405SHaiyang Zhang 		netdev_dbg(netdev,
19851491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
19995fa0405SHaiyang Zhang 			rndis_msg->msg_len,
20095fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
20195fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
20295fa0405SHaiyang Zhang 		break;
20395fa0405SHaiyang Zhang 
20451491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
20551491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
20695fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
20795fa0405SHaiyang Zhang 			rndis_msg->msg_len,
20895fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
20995fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
21095fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
21195fa0405SHaiyang Zhang 		break;
21295fa0405SHaiyang Zhang 
21395fa0405SHaiyang Zhang 	default:
21495fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
21595fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
21695fa0405SHaiyang Zhang 			rndis_msg->msg_len);
21795fa0405SHaiyang Zhang 		break;
21895fa0405SHaiyang Zhang 	}
21995fa0405SHaiyang Zhang }
22095fa0405SHaiyang Zhang 
22195fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
22295fa0405SHaiyang Zhang 				  struct rndis_request *req)
22395fa0405SHaiyang Zhang {
22495fa0405SHaiyang Zhang 	int ret;
22595fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
226b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
227a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
2283d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
22995fa0405SHaiyang Zhang 
23095fa0405SHaiyang Zhang 	/* Setup the packet to send it */
23195fa0405SHaiyang Zhang 	packet = &req->pkt;
23295fa0405SHaiyang Zhang 
23395fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
23495fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
23595fa0405SHaiyang Zhang 
236a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
23795fa0405SHaiyang Zhang 					PAGE_SHIFT;
238a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
239a9f2e2d6SKY Srinivasan 	pb[0].offset =
24095fa0405SHaiyang Zhang 		(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
24195fa0405SHaiyang Zhang 
24299e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
243a9f2e2d6SKY Srinivasan 	if (pb[0].offset + pb[0].len > PAGE_SIZE) {
24499e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
245a9f2e2d6SKY Srinivasan 		pb[0].len = PAGE_SIZE -
246a9f2e2d6SKY Srinivasan 			pb[0].offset;
247a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
248a9f2e2d6SKY Srinivasan 			+ pb[0].len) >> PAGE_SHIFT;
249a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
250a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
251a9f2e2d6SKY Srinivasan 			pb[0].len;
25299e3fcfaSHaiyang Zhang 	}
25399e3fcfaSHaiyang Zhang 
2542a926f79Sstephen hemminger 	ret = netvsc_send(net_device_ctx, packet, NULL, &pb, NULL);
25595fa0405SHaiyang Zhang 	return ret;
25695fa0405SHaiyang Zhang }
25795fa0405SHaiyang Zhang 
2581b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2591b07da51SHaiyang Zhang 				 struct rndis_request *request)
2601b07da51SHaiyang Zhang {
2611b07da51SHaiyang Zhang 	u32 link_status;
2621b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2631b07da51SHaiyang Zhang 
2641b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2651b07da51SHaiyang Zhang 
2661b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2671b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2681b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2691b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2701b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2711b07da51SHaiyang Zhang 	}
2721b07da51SHaiyang Zhang }
2731b07da51SHaiyang Zhang 
27495fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev,
27595fa0405SHaiyang Zhang 				       struct rndis_message *resp)
27695fa0405SHaiyang Zhang {
27795fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
27895fa0405SHaiyang Zhang 	bool found = false;
27995fa0405SHaiyang Zhang 	unsigned long flags;
2803d541ac5SVitaly Kuznetsov 	struct net_device *ndev = dev->ndev;
28195fa0405SHaiyang Zhang 
28295fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
28395fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
28495fa0405SHaiyang Zhang 		/*
28595fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
28695fa0405SHaiyang Zhang 		 * field
28795fa0405SHaiyang Zhang 		 */
28895fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
28995fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
29095fa0405SHaiyang Zhang 			found = true;
29195fa0405SHaiyang Zhang 			break;
29295fa0405SHaiyang Zhang 		}
29395fa0405SHaiyang Zhang 	}
29495fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
29595fa0405SHaiyang Zhang 
29695fa0405SHaiyang Zhang 	if (found) {
297a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
298a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
29995fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
30095fa0405SHaiyang Zhang 			       resp->msg_len);
3011b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
3021b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
3031b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
3041b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
30595fa0405SHaiyang Zhang 		} else {
30695fa0405SHaiyang Zhang 			netdev_err(ndev,
30795fa0405SHaiyang Zhang 				"rndis response buffer overflow "
30895fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
30995fa0405SHaiyang Zhang 				resp->msg_len,
31086eedaccSKY Srinivasan 				sizeof(struct rndis_message));
31195fa0405SHaiyang Zhang 
31295fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
31351491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
31495fa0405SHaiyang Zhang 				/* does not have a request id field */
31595fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
316007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
31795fa0405SHaiyang Zhang 			} else {
31895fa0405SHaiyang Zhang 				request->response_msg.msg.
31995fa0405SHaiyang Zhang 				init_complete.status =
320007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
32195fa0405SHaiyang Zhang 			}
32295fa0405SHaiyang Zhang 		}
32395fa0405SHaiyang Zhang 
32495fa0405SHaiyang Zhang 		complete(&request->wait_event);
32595fa0405SHaiyang Zhang 	} else {
32695fa0405SHaiyang Zhang 		netdev_err(ndev,
32795fa0405SHaiyang Zhang 			"no rndis request found for this response "
32895fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
32995fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
33095fa0405SHaiyang Zhang 			resp->ndis_msg_type);
33195fa0405SHaiyang Zhang 	}
33295fa0405SHaiyang Zhang }
33395fa0405SHaiyang Zhang 
3341f5f3a75SHaiyang Zhang /*
3351f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3361f5f3a75SHaiyang Zhang  * return NULL if not found.
3371f5f3a75SHaiyang Zhang  */
3381f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
3391f5f3a75SHaiyang Zhang {
3401f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3411f5f3a75SHaiyang Zhang 	int len;
3421f5f3a75SHaiyang Zhang 
3431f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3441f5f3a75SHaiyang Zhang 		return NULL;
3451f5f3a75SHaiyang Zhang 
3461f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3471f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3481f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3491f5f3a75SHaiyang Zhang 
3501f5f3a75SHaiyang Zhang 	while (len > 0) {
3511f5f3a75SHaiyang Zhang 		if (ppi->type == type)
3521f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3531f5f3a75SHaiyang Zhang 		len -= ppi->size;
3541f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3551f5f3a75SHaiyang Zhang 	}
3561f5f3a75SHaiyang Zhang 
3571f5f3a75SHaiyang Zhang 	return NULL;
3581f5f3a75SHaiyang Zhang }
3591f5f3a75SHaiyang Zhang 
360dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev,
361dc54a08cSstephen hemminger 				     struct rndis_device *dev,
36295fa0405SHaiyang Zhang 				     struct rndis_message *msg,
363dc54a08cSstephen hemminger 				     struct vmbus_channel *channel,
364dc54a08cSstephen hemminger 				     void *data, u32 data_buflen)
36595fa0405SHaiyang Zhang {
366dc54a08cSstephen hemminger 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
367dc54a08cSstephen hemminger 	const struct ndis_tcp_ip_checksum_info *csum_info;
368dc54a08cSstephen hemminger 	const struct ndis_pkt_8021q_info *vlan;
36995fa0405SHaiyang Zhang 	u32 data_offset;
37095fa0405SHaiyang Zhang 
37195fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
37295fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
37395fa0405SHaiyang Zhang 
374dc54a08cSstephen hemminger 	data_buflen -= data_offset;
3754b8a8bc9SWei Yongjun 
3764b8a8bc9SWei Yongjun 	/*
3774b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
3784b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
3794b8a8bc9SWei Yongjun 	 */
380dc54a08cSstephen hemminger 	if (unlikely(data_buflen < rndis_pkt->data_len)) {
3813d541ac5SVitaly Kuznetsov 		netdev_err(dev->ndev, "rndis message buffer "
3824b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
3834b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
384dc54a08cSstephen hemminger 			   data_buflen, rndis_pkt->data_len);
38510082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
3864b8a8bc9SWei Yongjun 	}
3874b8a8bc9SWei Yongjun 
388dc54a08cSstephen hemminger 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
389dc54a08cSstephen hemminger 
3904b8a8bc9SWei Yongjun 	/*
3914b8a8bc9SWei Yongjun 	 * Remove the rndis trailer padding from rndis packet message
3924b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
3934b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
3944b8a8bc9SWei Yongjun 	 */
395dc54a08cSstephen hemminger 	data = (void *)((unsigned long)data + data_offset);
396e3d605edSKY Srinivasan 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
397dc54a08cSstephen hemminger 	return netvsc_recv_callback(ndev, channel,
398dc54a08cSstephen hemminger 				    data, rndis_pkt->data_len,
399dc54a08cSstephen hemminger 				    csum_info, vlan);
40095fa0405SHaiyang Zhang }
40195fa0405SHaiyang Zhang 
402dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev,
403dc54a08cSstephen hemminger 			 struct netvsc_device *net_dev,
404dc54a08cSstephen hemminger 			 struct hv_device *dev,
405dc54a08cSstephen hemminger 			 struct vmbus_channel *channel,
406dc54a08cSstephen hemminger 			 void *data, u32 buflen)
40795fa0405SHaiyang Zhang {
4083d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
409dc54a08cSstephen hemminger 	struct rndis_device *rndis_dev = net_dev->extension;
410dc54a08cSstephen hemminger 	struct rndis_message *rndis_msg = data;
41195fa0405SHaiyang Zhang 
41295fa0405SHaiyang Zhang 	/* Make sure the rndis device state is initialized */
413dc54a08cSstephen hemminger 	if (unlikely(!rndis_dev)) {
414dc54a08cSstephen hemminger 		netif_err(net_device_ctx, rx_err, ndev,
415dc54a08cSstephen hemminger 			  "got rndis message but no rndis device!\n");
416dc54a08cSstephen hemminger 		return NVSP_STAT_FAIL;
41795fa0405SHaiyang Zhang 	}
41895fa0405SHaiyang Zhang 
419dc54a08cSstephen hemminger 	if (unlikely(rndis_dev->state == RNDIS_DEV_UNINITIALIZED)) {
420dc54a08cSstephen hemminger 		netif_err(net_device_ctx, rx_err, ndev,
421dc54a08cSstephen hemminger 			  "got rndis message uninitialized\n");
422dc54a08cSstephen hemminger 		return NVSP_STAT_FAIL;
42395fa0405SHaiyang Zhang 	}
42495fa0405SHaiyang Zhang 
425dc54a08cSstephen hemminger 	if (netif_msg_rx_status(net_device_ctx))
426ef31bef6SHaiyang Zhang 		dump_rndis_message(dev, rndis_msg);
42795fa0405SHaiyang Zhang 
428ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
42951491167SLinus Walleij 	case RNDIS_MSG_PACKET:
430dc54a08cSstephen hemminger 		return rndis_filter_receive_data(ndev, rndis_dev, rndis_msg,
431dc54a08cSstephen hemminger 						 channel, data, buflen);
43251491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
43351491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
43451491167SLinus Walleij 	case RNDIS_MSG_SET_C:
43595fa0405SHaiyang Zhang 		/* completion msgs */
436ef31bef6SHaiyang Zhang 		rndis_filter_receive_response(rndis_dev, rndis_msg);
43795fa0405SHaiyang Zhang 		break;
43895fa0405SHaiyang Zhang 
43951491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
44095fa0405SHaiyang Zhang 		/* notification msgs */
4413a494e71SHaiyang Zhang 		netvsc_linkstatus_callback(dev, rndis_msg);
44295fa0405SHaiyang Zhang 		break;
44395fa0405SHaiyang Zhang 	default:
44495fa0405SHaiyang Zhang 		netdev_err(ndev,
44595fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
446ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
447ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
44895fa0405SHaiyang Zhang 		break;
44995fa0405SHaiyang Zhang 	}
45095fa0405SHaiyang Zhang 
451dc54a08cSstephen hemminger 	return 0;
45295fa0405SHaiyang Zhang }
45395fa0405SHaiyang Zhang 
45495fa0405SHaiyang Zhang static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
45595fa0405SHaiyang Zhang 				  void *result, u32 *result_size)
45695fa0405SHaiyang Zhang {
45795fa0405SHaiyang Zhang 	struct rndis_request *request;
45895fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
45995fa0405SHaiyang Zhang 	struct rndis_query_request *query;
46095fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
46195fa0405SHaiyang Zhang 	int ret = 0;
46295fa0405SHaiyang Zhang 
46395fa0405SHaiyang Zhang 	if (!result)
46495fa0405SHaiyang Zhang 		return -EINVAL;
46595fa0405SHaiyang Zhang 
46695fa0405SHaiyang Zhang 	*result_size = 0;
46751491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
46895fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
46995fa0405SHaiyang Zhang 	if (!request) {
47095fa0405SHaiyang Zhang 		ret = -ENOMEM;
47195fa0405SHaiyang Zhang 		goto cleanup;
47295fa0405SHaiyang Zhang 	}
47395fa0405SHaiyang Zhang 
47495fa0405SHaiyang Zhang 	/* Setup the rndis query */
47595fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
47695fa0405SHaiyang Zhang 	query->oid = oid;
47795fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
47895fa0405SHaiyang Zhang 	query->info_buflen = 0;
47995fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
48095fa0405SHaiyang Zhang 
48123312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
48223312a3bSstephen hemminger 		struct net_device_context *ndevctx = netdev_priv(dev->ndev);
4833962981fSstephen hemminger 		struct netvsc_device *nvdev = rtnl_dereference(ndevctx->nvdev);
48423312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
48523312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
48623312a3bSstephen hemminger 		u8 ndis_rev;
48723312a3bSstephen hemminger 		size_t size;
48823312a3bSstephen hemminger 
48923312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
49023312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
49123312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
49223312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
49323312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
49423312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
49523312a3bSstephen hemminger 		} else {
49623312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
49723312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
49823312a3bSstephen hemminger 		}
49923312a3bSstephen hemminger 
50023312a3bSstephen hemminger 		request->request_msg.msg_len += size;
50123312a3bSstephen hemminger 		query->info_buflen = size;
50223312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
50323312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
50423312a3bSstephen hemminger 
50523312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
50623312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
50723312a3bSstephen hemminger 		hwcaps->header.size = size;
50823312a3bSstephen hemminger 
50923312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
5105b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
5115b54dac8SHaiyang Zhang 
5125b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
5135b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
5145b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
5155b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5165b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5175b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5185b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5195b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5205b54dac8SHaiyang Zhang 	}
5215b54dac8SHaiyang Zhang 
52295fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
52395fa0405SHaiyang Zhang 	if (ret != 0)
52495fa0405SHaiyang Zhang 		goto cleanup;
52595fa0405SHaiyang Zhang 
5265362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
52795fa0405SHaiyang Zhang 
52895fa0405SHaiyang Zhang 	/* Copy the response back */
52995fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
53095fa0405SHaiyang Zhang 
53195fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
53295fa0405SHaiyang Zhang 		ret = -1;
53395fa0405SHaiyang Zhang 		goto cleanup;
53495fa0405SHaiyang Zhang 	}
53595fa0405SHaiyang Zhang 
53695fa0405SHaiyang Zhang 	memcpy(result,
53795fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
53895fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
53995fa0405SHaiyang Zhang 	       query_complete->info_buflen);
54095fa0405SHaiyang Zhang 
54195fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
54295fa0405SHaiyang Zhang 
54395fa0405SHaiyang Zhang cleanup:
54495fa0405SHaiyang Zhang 	if (request)
54595fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
54695fa0405SHaiyang Zhang 
54795fa0405SHaiyang Zhang 	return ret;
54895fa0405SHaiyang Zhang }
54995fa0405SHaiyang Zhang 
55023312a3bSstephen hemminger /* Get the hardware offload capabilities */
55123312a3bSstephen hemminger static int
55223312a3bSstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct ndis_offload *caps)
55323312a3bSstephen hemminger {
55423312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
55523312a3bSstephen hemminger 	int ret;
55623312a3bSstephen hemminger 
55723312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
55823312a3bSstephen hemminger 
55923312a3bSstephen hemminger 	ret = rndis_filter_query_device(dev,
56023312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
56123312a3bSstephen hemminger 					caps, &caps_len);
56223312a3bSstephen hemminger 	if (ret)
56323312a3bSstephen hemminger 		return ret;
56423312a3bSstephen hemminger 
56523312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
56623312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
56723312a3bSstephen hemminger 			    caps->header.type);
56823312a3bSstephen hemminger 		return -EINVAL;
56923312a3bSstephen hemminger 	}
57023312a3bSstephen hemminger 
57123312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
57223312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
57323312a3bSstephen hemminger 			    caps->header.revision);
57423312a3bSstephen hemminger 		return -EINVAL;
57523312a3bSstephen hemminger 	}
57623312a3bSstephen hemminger 
57723312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
57823312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
57923312a3bSstephen hemminger 		netdev_warn(dev->ndev,
58023312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
58123312a3bSstephen hemminger 			    caps->header.size, caps_len);
58223312a3bSstephen hemminger 		return -EINVAL;
58323312a3bSstephen hemminger 	}
58423312a3bSstephen hemminger 
58523312a3bSstephen hemminger 	return 0;
58623312a3bSstephen hemminger }
58723312a3bSstephen hemminger 
58895fa0405SHaiyang Zhang static int rndis_filter_query_device_mac(struct rndis_device *dev)
58995fa0405SHaiyang Zhang {
59095fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
59195fa0405SHaiyang Zhang 
59295fa0405SHaiyang Zhang 	return rndis_filter_query_device(dev,
59395fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
59495fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
59595fa0405SHaiyang Zhang }
59695fa0405SHaiyang Zhang 
5971ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
5981ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
5991ce09e89SHaiyang Zhang 
600e834da9aSVitaly Kuznetsov int rndis_filter_set_device_mac(struct net_device *ndev, char *mac)
6011ce09e89SHaiyang Zhang {
6022625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev);
6031ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
6041ce09e89SHaiyang Zhang 	struct rndis_request *request;
6051ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
6061ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
6071ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
6081ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
6091ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
6101ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
6111ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
612999028ccSNicholas Mc Guire 	int ret;
6131ce09e89SHaiyang Zhang 
6141ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6151ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6161ce09e89SHaiyang Zhang 	if (!request)
6171ce09e89SHaiyang Zhang 		return -ENOMEM;
6181ce09e89SHaiyang Zhang 
6191ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
6201ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
6211ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
6221ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
6231ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
6241ce09e89SHaiyang Zhang 
6251ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
6261ce09e89SHaiyang Zhang 		set->info_buf_offset);
6271ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
6281ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
6291ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
6301ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
6311ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
6321ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
6331ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
6341ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
6351ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
6361ce09e89SHaiyang Zhang 
6371ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
6381ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
6391ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
6401ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
6411ce09e89SHaiyang Zhang 	if (ret < 0)
6421ce09e89SHaiyang Zhang 		goto cleanup;
6431ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
6441ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
6451ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
6461ce09e89SHaiyang Zhang 	if (ret < 0)
6471ce09e89SHaiyang Zhang 		goto cleanup;
6481ce09e89SHaiyang Zhang 
6491ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
6501ce09e89SHaiyang Zhang 	if (ret != 0)
6511ce09e89SHaiyang Zhang 		goto cleanup;
6521ce09e89SHaiyang Zhang 
6535362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
6545362855aSVitaly Kuznetsov 
6551ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
656b02a8067SHaiyang Zhang 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
657b02a8067SHaiyang Zhang 		netdev_err(ndev, "Fail to set MAC on host side:0x%x\n",
658b02a8067SHaiyang Zhang 			   set_complete->status);
6591ce09e89SHaiyang Zhang 		ret = -EINVAL;
6601ce09e89SHaiyang Zhang 	}
6611ce09e89SHaiyang Zhang 
6621ce09e89SHaiyang Zhang cleanup:
6631ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
6641ce09e89SHaiyang Zhang 	return ret;
6651ce09e89SHaiyang Zhang }
6661ce09e89SHaiyang Zhang 
667da19fcd0SLad, Prabhakar static int
668426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
6699749fed5Sstephen hemminger 				struct netvsc_device *nvdev,
6704a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
6714a0e70aeSKY Srinivasan {
6724a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
6734a0e70aeSKY Srinivasan 	struct rndis_request *request;
6744a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
6754a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
6764a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
6774a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
678999028ccSNicholas Mc Guire 	int ret;
679af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
680af9893a3SKY Srinivasan 
681af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
682af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
683af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
684af9893a3SKY Srinivasan 		 * UDP checksum offload.
685af9893a3SKY Srinivasan 		 */
686af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
687af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
688af9893a3SKY Srinivasan 	}
6894a0e70aeSKY Srinivasan 
6904a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6914a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6924a0e70aeSKY Srinivasan 	if (!request)
6934a0e70aeSKY Srinivasan 		return -ENOMEM;
6944a0e70aeSKY Srinivasan 
6954a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
6964a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
6974a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
6984a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
6994a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
7004a0e70aeSKY Srinivasan 
7014a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
7024a0e70aeSKY Srinivasan 				set->info_buf_offset);
7034a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
7044a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
7054a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
7064a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
7074a0e70aeSKY Srinivasan 
7084a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
7094a0e70aeSKY Srinivasan 	if (ret != 0)
7104a0e70aeSKY Srinivasan 		goto cleanup;
7114a0e70aeSKY Srinivasan 
7125362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7134a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
7144a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
715af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
7164a0e70aeSKY Srinivasan 			   set_complete->status);
7174a0e70aeSKY Srinivasan 		ret = -EINVAL;
7184a0e70aeSKY Srinivasan 	}
7194a0e70aeSKY Srinivasan 
7204a0e70aeSKY Srinivasan cleanup:
7214a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
7224a0e70aeSKY Srinivasan 	return ret;
7234a0e70aeSKY Srinivasan }
7241ce09e89SHaiyang Zhang 
725962f3feeSstephen hemminger int rndis_filter_set_rss_param(struct rndis_device *rdev,
726962f3feeSstephen hemminger 			       const u8 *rss_key, int num_queue)
7275b54dac8SHaiyang Zhang {
7283d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
7295b54dac8SHaiyang Zhang 	struct rndis_request *request;
7305b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7315b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7325b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
733962f3feeSstephen hemminger 		     4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
7345b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7355b54dac8SHaiyang Zhang 	u32 *itab;
7365b54dac8SHaiyang Zhang 	u8 *keyp;
737999028ccSNicholas Mc Guire 	int i, ret;
7385b54dac8SHaiyang Zhang 
7395b54dac8SHaiyang Zhang 	request = get_rndis_request(
7405b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7415b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7425b54dac8SHaiyang Zhang 	if (!request)
7435b54dac8SHaiyang Zhang 		return -ENOMEM;
7445b54dac8SHaiyang Zhang 
7455b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7465b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7475b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
7485b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
7495b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
7505b54dac8SHaiyang Zhang 
7515b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
7525b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
7535b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
7545b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
7555b54dac8SHaiyang Zhang 	rssp->flag = 0;
7565b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
7574c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
7584c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
7595b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
7605b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
761962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
7625b54dac8SHaiyang Zhang 	rssp->kashkey_offset = rssp->indirect_taboffset +
7635b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
7645b54dac8SHaiyang Zhang 
7655b54dac8SHaiyang Zhang 	/* Set indirection table entries */
7665b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
7675b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
768ff4a4419Sstephen hemminger 		itab[i] = rdev->ind_table[i];
7695b54dac8SHaiyang Zhang 
7705b54dac8SHaiyang Zhang 	/* Set hask key values */
7715b54dac8SHaiyang Zhang 	keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
772962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
7735b54dac8SHaiyang Zhang 
7745b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7755b54dac8SHaiyang Zhang 	if (ret != 0)
7765b54dac8SHaiyang Zhang 		goto cleanup;
7775b54dac8SHaiyang Zhang 
7785362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7795b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
780962f3feeSstephen hemminger 	if (set_complete->status == RNDIS_STATUS_SUCCESS)
781962f3feeSstephen hemminger 		memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
782962f3feeSstephen hemminger 	else {
7835b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
7845b54dac8SHaiyang Zhang 			   set_complete->status);
7855b54dac8SHaiyang Zhang 		ret = -EINVAL;
7865b54dac8SHaiyang Zhang 	}
7875b54dac8SHaiyang Zhang 
7885b54dac8SHaiyang Zhang cleanup:
7895b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
7905b54dac8SHaiyang Zhang 	return ret;
7915b54dac8SHaiyang Zhang }
7925b54dac8SHaiyang Zhang 
79395fa0405SHaiyang Zhang static int rndis_filter_query_device_link_status(struct rndis_device *dev)
79495fa0405SHaiyang Zhang {
79595fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
79695fa0405SHaiyang Zhang 	u32 link_status;
79795fa0405SHaiyang Zhang 	int ret;
79895fa0405SHaiyang Zhang 
79995fa0405SHaiyang Zhang 	ret = rndis_filter_query_device(dev,
80095fa0405SHaiyang Zhang 				      RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
80195fa0405SHaiyang Zhang 				      &link_status, &size);
80295fa0405SHaiyang Zhang 
80395fa0405SHaiyang Zhang 	return ret;
80495fa0405SHaiyang Zhang }
80595fa0405SHaiyang Zhang 
806b37879e6SHaiyang Zhang static int rndis_filter_query_link_speed(struct rndis_device *dev)
807b37879e6SHaiyang Zhang {
808b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
809b37879e6SHaiyang Zhang 	u32 link_speed;
810b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
811b37879e6SHaiyang Zhang 	int ret;
812b37879e6SHaiyang Zhang 
813b37879e6SHaiyang Zhang 	ret = rndis_filter_query_device(dev, RNDIS_OID_GEN_LINK_SPEED,
814b37879e6SHaiyang Zhang 					&link_speed, &size);
815b37879e6SHaiyang Zhang 
816b37879e6SHaiyang Zhang 	if (!ret) {
817b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
818b37879e6SHaiyang Zhang 
819b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
820b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
821b37879e6SHaiyang Zhang 		 */
822b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
823b37879e6SHaiyang Zhang 	}
824b37879e6SHaiyang Zhang 
825b37879e6SHaiyang Zhang 	return ret;
826b37879e6SHaiyang Zhang }
827b37879e6SHaiyang Zhang 
8284f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev,
8294f19c0d8Sstephen hemminger 					  u32 new_filter)
83095fa0405SHaiyang Zhang {
83195fa0405SHaiyang Zhang 	struct rndis_request *request;
83295fa0405SHaiyang Zhang 	struct rndis_set_request *set;
833999028ccSNicholas Mc Guire 	int ret;
83495fa0405SHaiyang Zhang 
83551491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
83695fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
83795fa0405SHaiyang Zhang 			sizeof(u32));
838ce12b810Sstephen hemminger 	if (!request)
839ce12b810Sstephen hemminger 		return -ENOMEM;
840ce12b810Sstephen hemminger 
84195fa0405SHaiyang Zhang 
84295fa0405SHaiyang Zhang 	/* Setup the rndis set */
84395fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
84495fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
84595fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
84695fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
84795fa0405SHaiyang Zhang 
84895fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
84995fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
85095fa0405SHaiyang Zhang 
85195fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
852ce12b810Sstephen hemminger 	if (ret == 0)
8535362855aSVitaly Kuznetsov 		wait_for_completion(&request->wait_event);
85495fa0405SHaiyang Zhang 
85595fa0405SHaiyang Zhang 	put_rndis_request(dev, request);
856ce12b810Sstephen hemminger 
85795fa0405SHaiyang Zhang 	return ret;
85895fa0405SHaiyang Zhang }
85995fa0405SHaiyang Zhang 
8604f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w)
8614f19c0d8Sstephen hemminger {
8624f19c0d8Sstephen hemminger 	struct rndis_device *rdev
8634f19c0d8Sstephen hemminger 		= container_of(w, struct rndis_device, mcast_work);
8644f19c0d8Sstephen hemminger 
8654f19c0d8Sstephen hemminger 	if (rdev->ndev->flags & IFF_PROMISC)
8664f19c0d8Sstephen hemminger 		rndis_filter_set_packet_filter(rdev,
8674f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_PROMISCUOUS);
8684f19c0d8Sstephen hemminger 	else
8694f19c0d8Sstephen hemminger 		rndis_filter_set_packet_filter(rdev,
8704f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_BROADCAST |
8714f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_ALL_MULTICAST |
8724f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_DIRECTED);
8734f19c0d8Sstephen hemminger }
8744f19c0d8Sstephen hemminger 
8754f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev)
8764f19c0d8Sstephen hemminger {
8774f19c0d8Sstephen hemminger 	struct rndis_device *rdev = nvdev->extension;
8784f19c0d8Sstephen hemminger 
8794f19c0d8Sstephen hemminger 	schedule_work(&rdev->mcast_work);
8804f19c0d8Sstephen hemminger }
8814f19c0d8Sstephen hemminger 
88295fa0405SHaiyang Zhang static int rndis_filter_init_device(struct rndis_device *dev)
88395fa0405SHaiyang Zhang {
88495fa0405SHaiyang Zhang 	struct rndis_request *request;
88595fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
88695fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
88795fa0405SHaiyang Zhang 	u32 status;
888999028ccSNicholas Mc Guire 	int ret;
8892625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(dev->ndev);
89095fa0405SHaiyang Zhang 
89151491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
89295fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
89395fa0405SHaiyang Zhang 	if (!request) {
89495fa0405SHaiyang Zhang 		ret = -ENOMEM;
89595fa0405SHaiyang Zhang 		goto cleanup;
89695fa0405SHaiyang Zhang 	}
89795fa0405SHaiyang Zhang 
89895fa0405SHaiyang Zhang 	/* Setup the rndis set */
89995fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
90095fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
90195fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
902fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
90395fa0405SHaiyang Zhang 
90495fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
90595fa0405SHaiyang Zhang 
90695fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
90795fa0405SHaiyang Zhang 	if (ret != 0) {
90895fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
90995fa0405SHaiyang Zhang 		goto cleanup;
91095fa0405SHaiyang Zhang 	}
91195fa0405SHaiyang Zhang 
9125362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
91395fa0405SHaiyang Zhang 
91495fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
91595fa0405SHaiyang Zhang 	status = init_complete->status;
91695fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
91795fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
9187c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
9197c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
92095fa0405SHaiyang Zhang 		ret = 0;
92195fa0405SHaiyang Zhang 	} else {
92295fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
92395fa0405SHaiyang Zhang 		ret = -EINVAL;
92495fa0405SHaiyang Zhang 	}
92595fa0405SHaiyang Zhang 
92695fa0405SHaiyang Zhang cleanup:
92795fa0405SHaiyang Zhang 	if (request)
92895fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
92995fa0405SHaiyang Zhang 
93095fa0405SHaiyang Zhang 	return ret;
93195fa0405SHaiyang Zhang }
93295fa0405SHaiyang Zhang 
93346b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev)
93446b4f7f5Sstephen hemminger {
93546b4f7f5Sstephen hemminger 	int i;
93646b4f7f5Sstephen hemminger 
93746b4f7f5Sstephen hemminger 	if (atomic_read(&nvdev->num_outstanding_recvs) > 0)
93846b4f7f5Sstephen hemminger 		return false;
93946b4f7f5Sstephen hemminger 
94046b4f7f5Sstephen hemminger 	for (i = 0; i < nvdev->num_chn; i++) {
94146b4f7f5Sstephen hemminger 		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
94246b4f7f5Sstephen hemminger 
94346b4f7f5Sstephen hemminger 		if (atomic_read(&nvchan->queue_sends) > 0)
94446b4f7f5Sstephen hemminger 			return false;
94546b4f7f5Sstephen hemminger 	}
94646b4f7f5Sstephen hemminger 
94746b4f7f5Sstephen hemminger 	return true;
94846b4f7f5Sstephen hemminger }
94946b4f7f5Sstephen hemminger 
95095fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev)
95195fa0405SHaiyang Zhang {
95295fa0405SHaiyang Zhang 	struct rndis_request *request;
95395fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
9543d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
9553962981fSstephen hemminger 	struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
95695fa0405SHaiyang Zhang 
95795fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
95851491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
95995fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
96095fa0405SHaiyang Zhang 	if (!request)
96195fa0405SHaiyang Zhang 		goto cleanup;
96295fa0405SHaiyang Zhang 
96395fa0405SHaiyang Zhang 	/* Setup the rndis set */
96495fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
96595fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
96695fa0405SHaiyang Zhang 
96795fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
96895fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
96995fa0405SHaiyang Zhang 
97095fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
97195fa0405SHaiyang Zhang 
97295fa0405SHaiyang Zhang cleanup:
973ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
97400ecfb3bSstephen hemminger 
97500ecfb3bSstephen hemminger 	/* Force flag to be ordered before waiting */
97600ecfb3bSstephen hemminger 	wmb();
977ae9e63bbSHaiyang Zhang 
978ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
97946b4f7f5Sstephen hemminger 	wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
980ae9e63bbSHaiyang Zhang 
98195fa0405SHaiyang Zhang 	if (request)
98295fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
98395fa0405SHaiyang Zhang }
98495fa0405SHaiyang Zhang 
98595fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
98695fa0405SHaiyang Zhang {
98795fa0405SHaiyang Zhang 	int ret;
98895fa0405SHaiyang Zhang 
98995fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
99095fa0405SHaiyang Zhang 		return 0;
99195fa0405SHaiyang Zhang 
99295fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
99395fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
99495fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
99595fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
99695fa0405SHaiyang Zhang 	if (ret == 0)
99795fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
99895fa0405SHaiyang Zhang 
99995fa0405SHaiyang Zhang 	return ret;
100095fa0405SHaiyang Zhang }
100195fa0405SHaiyang Zhang 
100295fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
100395fa0405SHaiyang Zhang {
100495fa0405SHaiyang Zhang 	int ret;
100595fa0405SHaiyang Zhang 
100695fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
100795fa0405SHaiyang Zhang 		return 0;
100895fa0405SHaiyang Zhang 
10094f19c0d8Sstephen hemminger 	/* Make sure rndis_set_multicast doesn't re-enable filter! */
10104f19c0d8Sstephen hemminger 	cancel_work_sync(&dev->mcast_work);
10114f19c0d8Sstephen hemminger 
101295fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
1013c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
1014c3582a2cSHaiyang Zhang 		ret = 0;
1015c3582a2cSHaiyang Zhang 
101695fa0405SHaiyang Zhang 	if (ret == 0)
101795fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
101895fa0405SHaiyang Zhang 
101995fa0405SHaiyang Zhang 	return ret;
102095fa0405SHaiyang Zhang }
102195fa0405SHaiyang Zhang 
10225b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
10235b54dac8SHaiyang Zhang {
10243d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
10253d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
10262625466dSVitaly Kuznetsov 	struct netvsc_device *nvscdev = net_device_to_netvsc_device(ndev);
10275b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
10286de38af6Sstephen hemminger 	struct netvsc_channel *nvchan;
10296de38af6Sstephen hemminger 	int ret;
10305b54dac8SHaiyang Zhang 
10315b54dac8SHaiyang Zhang 	if (chn_index >= nvscdev->num_chn)
10325b54dac8SHaiyang Zhang 		return;
10335b54dac8SHaiyang Zhang 
10346de38af6Sstephen hemminger 	nvchan = nvscdev->chan_table + chn_index;
10356de38af6Sstephen hemminger 	nvchan->mrc.buf
1036b8b835a8Sstephen hemminger 		= vzalloc(NETVSC_RECVSLOT_MAX * sizeof(struct recv_comp_data));
1037c0b558e5SHaiyang Zhang 
10386de38af6Sstephen hemminger 	if (!nvchan->mrc.buf)
10396de38af6Sstephen hemminger 		return;
10406de38af6Sstephen hemminger 
1041b1dd90ceSK. Y. Srinivasan 	/* Because the device uses NAPI, all the interrupt batching and
1042b1dd90ceSK. Y. Srinivasan 	 * control is done via Net softirq, not the channel handling
1043b1dd90ceSK. Y. Srinivasan 	 */
1044b1dd90ceSK. Y. Srinivasan 	set_channel_read_mode(new_sc, HV_CALL_ISR);
1045b1dd90ceSK. Y. Srinivasan 
1046bffb1842SK. Y. Srinivasan 	/* Set the channel before opening.*/
1047bffb1842SK. Y. Srinivasan 	nvchan->channel = new_sc;
104876bb5db5Sstephen hemminger 	netif_napi_add(ndev, &nvchan->napi,
104976bb5db5Sstephen hemminger 		       netvsc_poll, NAPI_POLL_WEIGHT);
1050bffb1842SK. Y. Srinivasan 
10515b54dac8SHaiyang Zhang 	ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
10525b54dac8SHaiyang Zhang 			 nvscdev->ring_size * PAGE_SIZE, NULL, 0,
10536de38af6Sstephen hemminger 			 netvsc_channel_cb, nvchan);
105476bb5db5Sstephen hemminger 	if (ret == 0)
10556de38af6Sstephen hemminger 		napi_enable(&nvchan->napi);
105676bb5db5Sstephen hemminger 	else
10572be0f264Sstephen hemminger 		netif_napi_del(&nvchan->napi);
105815a863bfSstephen hemminger 
105943c7bd1fSstephen hemminger 	if (refcount_dec_and_test(&nvscdev->sc_offered))
10603f735131SHaiyang Zhang 		complete(&nvscdev->channel_init_wait);
10615b54dac8SHaiyang Zhang }
10625b54dac8SHaiyang Zhang 
10639749fed5Sstephen hemminger struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
10642c7f83caSstephen hemminger 				      struct netvsc_device_info *device_info)
106595fa0405SHaiyang Zhang {
10663d541ac5SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
10673d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
106895fa0405SHaiyang Zhang 	struct netvsc_device *net_device;
106995fa0405SHaiyang Zhang 	struct rndis_device *rndis_device;
107023312a3bSstephen hemminger 	struct ndis_offload hwcaps;
10714a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
10725b54dac8SHaiyang Zhang 	struct nvsp_message *init_packet;
10735b54dac8SHaiyang Zhang 	struct ndis_recv_scale_cap rsscap;
10745b54dac8SHaiyang Zhang 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
107523312a3bSstephen hemminger 	unsigned int gso_max_size = GSO_MAX_SIZE;
107643c7bd1fSstephen hemminger 	u32 mtu, size, num_rss_qs;
1077e01ec219SKY Srinivasan 	const struct cpumask *node_cpu_mask;
1078e01ec219SKY Srinivasan 	u32 num_possible_rss_qs;
1079ff4a4419Sstephen hemminger 	int i, ret;
108095fa0405SHaiyang Zhang 
108195fa0405SHaiyang Zhang 	rndis_device = get_rndis_device();
108295fa0405SHaiyang Zhang 	if (!rndis_device)
10839749fed5Sstephen hemminger 		return ERR_PTR(-ENODEV);
108495fa0405SHaiyang Zhang 
108595fa0405SHaiyang Zhang 	/*
108695fa0405SHaiyang Zhang 	 * Let the inner driver handle this first to create the netvsc channel
108795fa0405SHaiyang Zhang 	 * NOTE! Once the channel is created, we may get a receive callback
108895fa0405SHaiyang Zhang 	 * (RndisFilterOnReceive()) before this call is completed
108995fa0405SHaiyang Zhang 	 */
10909749fed5Sstephen hemminger 	net_device = netvsc_device_add(dev, device_info);
10919749fed5Sstephen hemminger 	if (IS_ERR(net_device)) {
109295fa0405SHaiyang Zhang 		kfree(rndis_device);
10939749fed5Sstephen hemminger 		return net_device;
109495fa0405SHaiyang Zhang 	}
109595fa0405SHaiyang Zhang 
109695fa0405SHaiyang Zhang 	/* Initialize the rndis device */
109759995370SAndrew Schwartzmeyer 	net_device->max_chn = 1;
10985b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
109995fa0405SHaiyang Zhang 
110043c7bd1fSstephen hemminger 	refcount_set(&net_device->sc_offered, 0);
1101b3e6b82aSKY Srinivasan 
110295fa0405SHaiyang Zhang 	net_device->extension = rndis_device;
11033d541ac5SVitaly Kuznetsov 	rndis_device->ndev = net;
110495fa0405SHaiyang Zhang 
110595fa0405SHaiyang Zhang 	/* Send the rndis initialization message */
110695fa0405SHaiyang Zhang 	ret = rndis_filter_init_device(rndis_device);
11079749fed5Sstephen hemminger 	if (ret != 0)
11089749fed5Sstephen hemminger 		goto err_dev_remv;
110995fa0405SHaiyang Zhang 
11104d3c9d37SHaiyang Zhang 	/* Get the MTU from the host */
11114d3c9d37SHaiyang Zhang 	size = sizeof(u32);
11124d3c9d37SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
11134d3c9d37SHaiyang Zhang 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
11144d3c9d37SHaiyang Zhang 					&mtu, &size);
11150a1275caSVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
11160a1275caSVitaly Kuznetsov 		net->mtu = mtu;
11174d3c9d37SHaiyang Zhang 
111895fa0405SHaiyang Zhang 	/* Get the mac address */
111995fa0405SHaiyang Zhang 	ret = rndis_filter_query_device_mac(rndis_device);
11209749fed5Sstephen hemminger 	if (ret != 0)
11219749fed5Sstephen hemminger 		goto err_dev_remv;
112295fa0405SHaiyang Zhang 
112395fa0405SHaiyang Zhang 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
112495fa0405SHaiyang Zhang 
112523312a3bSstephen hemminger 	/* Find HW offload capabilities */
112623312a3bSstephen hemminger 	ret = rndis_query_hwcaps(rndis_device, &hwcaps);
11279749fed5Sstephen hemminger 	if (ret != 0)
11289749fed5Sstephen hemminger 		goto err_dev_remv;
112923312a3bSstephen hemminger 
113023312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
11314a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
113223312a3bSstephen hemminger 
113323312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
113423312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
113523312a3bSstephen hemminger 
113623312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
113723312a3bSstephen hemminger 	net->hw_features = NETIF_F_RXCSUM;
113823312a3bSstephen hemminger 
113923312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
114023312a3bSstephen hemminger 		/* Can checksum TCP */
114123312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
114223312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
114323312a3bSstephen hemminger 
11444a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
114523312a3bSstephen hemminger 
114623312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
11474a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
114823312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
114923312a3bSstephen hemminger 
115023312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
115123312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
115223312a3bSstephen hemminger 		}
115323312a3bSstephen hemminger 
115423312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
115523312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
115623312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
115723312a3bSstephen hemminger 		}
115823312a3bSstephen hemminger 	}
115923312a3bSstephen hemminger 
116023312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
116123312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
116223312a3bSstephen hemminger 
116323312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
116423312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
116523312a3bSstephen hemminger 
116623312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
116723312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
116823312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
116923312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
117023312a3bSstephen hemminger 
117123312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
117223312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
117323312a3bSstephen hemminger 		}
117423312a3bSstephen hemminger 
117523312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
117623312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
117723312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
117823312a3bSstephen hemminger 		}
117923312a3bSstephen hemminger 	}
118023312a3bSstephen hemminger 
118123312a3bSstephen hemminger 	netif_set_gso_max_size(net, gso_max_size);
11824a0e70aeSKY Srinivasan 
11839749fed5Sstephen hemminger 	ret = rndis_filter_set_offload_params(net, net_device, &offloads);
11844a0e70aeSKY Srinivasan 	if (ret)
11854a0e70aeSKY Srinivasan 		goto err_dev_remv;
11864a0e70aeSKY Srinivasan 
118795fa0405SHaiyang Zhang 	rndis_filter_query_device_link_status(rndis_device);
118895fa0405SHaiyang Zhang 
118993ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
119095fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
1191dedb459eSHaiyang Zhang 		   rndis_device->link_state ? "down" : "up");
119295fa0405SHaiyang Zhang 
11935b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
11949749fed5Sstephen hemminger 		return net_device;
11955b54dac8SHaiyang Zhang 
1196b37879e6SHaiyang Zhang 	rndis_filter_query_link_speed(rndis_device);
1197b37879e6SHaiyang Zhang 
11985b54dac8SHaiyang Zhang 	/* vRSS setup */
11995b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
12005b54dac8SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
12015b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
12025b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
12035b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
12045b54dac8SHaiyang Zhang 		goto out;
12055b54dac8SHaiyang Zhang 
1206e01ec219SKY Srinivasan 	/*
1207e01ec219SKY Srinivasan 	 * We will limit the VRSS channels to the number CPUs in the NUMA node
1208e01ec219SKY Srinivasan 	 * the primary channel is currently bound to.
12093071ada4Sstephen hemminger 	 *
12103071ada4Sstephen hemminger 	 * This also guarantees that num_possible_rss_qs <= num_online_cpus
1211e01ec219SKY Srinivasan 	 */
1212e01ec219SKY Srinivasan 	node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu));
12133071ada4Sstephen hemminger 	num_possible_rss_qs = min_t(u32, cpumask_weight(node_cpu_mask),
12143071ada4Sstephen hemminger 				    rsscap.num_recv_que);
12153071ada4Sstephen hemminger 
12163071ada4Sstephen hemminger 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs);
12178ebdcc52SAndrew Schwartzmeyer 
12188ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
12193071ada4Sstephen hemminger 	net_device->num_chn = min(net_device->max_chn, device_info->num_chn);
1220ff4a4419Sstephen hemminger 
1221ff4a4419Sstephen hemminger 	for (i = 0; i < ITAB_NUM; i++)
1222ff4a4419Sstephen hemminger 		rndis_device->ind_table[i] = ethtool_rxfh_indir_default(i,
1223ff4a4419Sstephen hemminger 							net_device->num_chn);
1224ff4a4419Sstephen hemminger 
12253071ada4Sstephen hemminger 	num_rss_qs = net_device->num_chn - 1;
12263071ada4Sstephen hemminger 	if (num_rss_qs == 0)
12279749fed5Sstephen hemminger 		return net_device;
12285b54dac8SHaiyang Zhang 
122943c7bd1fSstephen hemminger 	refcount_set(&net_device->sc_offered, num_rss_qs);
12305b54dac8SHaiyang Zhang 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
12315b54dac8SHaiyang Zhang 
12325b54dac8SHaiyang Zhang 	init_packet = &net_device->channel_init_pkt;
12335b54dac8SHaiyang Zhang 	memset(init_packet, 0, sizeof(struct nvsp_message));
12345b54dac8SHaiyang Zhang 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
12355b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
12365b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
12375b54dac8SHaiyang Zhang 						net_device->num_chn - 1;
12385b54dac8SHaiyang Zhang 	ret = vmbus_sendpacket(dev->channel, init_packet,
12395b54dac8SHaiyang Zhang 			       sizeof(struct nvsp_message),
12405b54dac8SHaiyang Zhang 			       (unsigned long)init_packet,
12415b54dac8SHaiyang Zhang 			       VM_PKT_DATA_INBAND,
12425b54dac8SHaiyang Zhang 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
12435b54dac8SHaiyang Zhang 	if (ret)
12445b54dac8SHaiyang Zhang 		goto out;
12455362855aSVitaly Kuznetsov 
124643c7bd1fSstephen hemminger 	if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
12475b54dac8SHaiyang Zhang 		ret = -ENODEV;
12485b54dac8SHaiyang Zhang 		goto out;
12495b54dac8SHaiyang Zhang 	}
125043c7bd1fSstephen hemminger 	wait_for_completion(&net_device->channel_init_wait);
125143c7bd1fSstephen hemminger 
12525b54dac8SHaiyang Zhang 	net_device->num_chn = 1 +
12535b54dac8SHaiyang Zhang 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
12545b54dac8SHaiyang Zhang 
125543c7bd1fSstephen hemminger 	/* ignore failues from setting rss parameters, still have channels */
125643c7bd1fSstephen hemminger 	rndis_filter_set_rss_param(rndis_device, netvsc_hash_key,
1257962f3feeSstephen hemminger 				   net_device->num_chn);
12585b54dac8SHaiyang Zhang out:
125959995370SAndrew Schwartzmeyer 	if (ret) {
126059995370SAndrew Schwartzmeyer 		net_device->max_chn = 1;
12615b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
126259995370SAndrew Schwartzmeyer 	}
1263b3e6b82aSKY Srinivasan 
12649749fed5Sstephen hemminger 	return net_device;
12654a0e70aeSKY Srinivasan 
12664a0e70aeSKY Srinivasan err_dev_remv:
12672289f0aaSstephen hemminger 	rndis_filter_device_remove(dev, net_device);
12689749fed5Sstephen hemminger 	return ERR_PTR(ret);
126995fa0405SHaiyang Zhang }
127095fa0405SHaiyang Zhang 
12712289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev,
12722289f0aaSstephen hemminger 				struct netvsc_device *net_dev)
127395fa0405SHaiyang Zhang {
127495fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1275d66ab514SHaiyang Zhang 
127695fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
127795fa0405SHaiyang Zhang 	rndis_filter_halt_device(rndis_dev);
127895fa0405SHaiyang Zhang 
127995fa0405SHaiyang Zhang 	kfree(rndis_dev);
128095fa0405SHaiyang Zhang 	net_dev->extension = NULL;
128195fa0405SHaiyang Zhang 
128295fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
128395fa0405SHaiyang Zhang }
128495fa0405SHaiyang Zhang 
12852f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev)
128695fa0405SHaiyang Zhang {
12872f5fa6c8SVitaly Kuznetsov 	if (!nvdev)
128895fa0405SHaiyang Zhang 		return -EINVAL;
128995fa0405SHaiyang Zhang 
12902f5fa6c8SVitaly Kuznetsov 	if (atomic_inc_return(&nvdev->open_cnt) != 1)
129184bf9cefSKY Srinivasan 		return 0;
129284bf9cefSKY Srinivasan 
12932f5fa6c8SVitaly Kuznetsov 	return rndis_filter_open_device(nvdev->extension);
129495fa0405SHaiyang Zhang }
129595fa0405SHaiyang Zhang 
12962f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev)
129795fa0405SHaiyang Zhang {
12985fccab3bSHaiyang Zhang 	if (!nvdev)
129995fa0405SHaiyang Zhang 		return -EINVAL;
130095fa0405SHaiyang Zhang 
130184bf9cefSKY Srinivasan 	if (atomic_dec_return(&nvdev->open_cnt) != 0)
130284bf9cefSKY Srinivasan 		return 0;
130384bf9cefSKY Srinivasan 
13045fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
130595fa0405SHaiyang Zhang }
1306ea383bf1Sstephen hemminger 
1307ea383bf1Sstephen hemminger bool rndis_filter_opened(const struct netvsc_device *nvdev)
1308ea383bf1Sstephen hemminger {
1309ea383bf1Sstephen hemminger 	return atomic_read(&nvdev->open_cnt) > 0;
1310ea383bf1Sstephen hemminger }
1311