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 
8795fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
8895fa0405SHaiyang Zhang 					     u32 msg_type,
8995fa0405SHaiyang Zhang 					     u32 msg_len)
9095fa0405SHaiyang Zhang {
9195fa0405SHaiyang Zhang 	struct rndis_request *request;
9295fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
9395fa0405SHaiyang Zhang 	struct rndis_set_request *set;
9495fa0405SHaiyang Zhang 	unsigned long flags;
9595fa0405SHaiyang Zhang 
9695fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
9795fa0405SHaiyang Zhang 	if (!request)
9895fa0405SHaiyang Zhang 		return NULL;
9995fa0405SHaiyang Zhang 
10095fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
10195fa0405SHaiyang Zhang 
10295fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
10395fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
10495fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
10595fa0405SHaiyang Zhang 
1065b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
1075b54dac8SHaiyang Zhang 
10895fa0405SHaiyang Zhang 	/*
10995fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
11095fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
11195fa0405SHaiyang Zhang 	 * template
11295fa0405SHaiyang Zhang 	 */
11395fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
11495fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
11595fa0405SHaiyang Zhang 
11695fa0405SHaiyang Zhang 	/* Add to the request list */
11795fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
11895fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
11995fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
12095fa0405SHaiyang Zhang 
12195fa0405SHaiyang Zhang 	return request;
12295fa0405SHaiyang Zhang }
12395fa0405SHaiyang Zhang 
12495fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
12595fa0405SHaiyang Zhang 			    struct rndis_request *req)
12695fa0405SHaiyang Zhang {
12795fa0405SHaiyang Zhang 	unsigned long flags;
12895fa0405SHaiyang Zhang 
12995fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
13095fa0405SHaiyang Zhang 	list_del(&req->list_ent);
13195fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
13295fa0405SHaiyang Zhang 
13395fa0405SHaiyang Zhang 	kfree(req);
13495fa0405SHaiyang Zhang }
13595fa0405SHaiyang Zhang 
13695fa0405SHaiyang Zhang static void dump_rndis_message(struct hv_device *hv_dev,
137dc54a08cSstephen hemminger 			       const struct rndis_message *rndis_msg)
13895fa0405SHaiyang Zhang {
1393d541ac5SVitaly Kuznetsov 	struct net_device *netdev = hv_get_drvdata(hv_dev);
14095fa0405SHaiyang Zhang 
14195fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
14251491167SLinus Walleij 	case RNDIS_MSG_PACKET:
14351491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
14495fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
14595fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
14695fa0405SHaiyang Zhang 			   "pkt len %u\n",
14795fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
14895fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
14995fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
15095fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
15195fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
15295fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
15395fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
15495fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
15595fa0405SHaiyang Zhang 		break;
15695fa0405SHaiyang Zhang 
15751491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
15851491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
15995fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
16095fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
16195fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
16295fa0405SHaiyang Zhang 			rndis_msg->msg_len,
16395fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
16495fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
16595fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
16695fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
16795fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
16895fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
16995fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17095fa0405SHaiyang Zhang 			   max_pkt_per_msg,
17195fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17295fa0405SHaiyang Zhang 			   pkt_alignment_factor);
17395fa0405SHaiyang Zhang 		break;
17495fa0405SHaiyang Zhang 
17551491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
17651491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17795fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
17895fa0405SHaiyang Zhang 			"buf offset %u)\n",
17995fa0405SHaiyang Zhang 			rndis_msg->msg_len,
18095fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
18195fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
18295fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18395fa0405SHaiyang Zhang 			   info_buflen,
18495fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18595fa0405SHaiyang Zhang 			   info_buf_offset);
18695fa0405SHaiyang Zhang 		break;
18795fa0405SHaiyang Zhang 
18851491167SLinus Walleij 	case RNDIS_MSG_SET_C:
18995fa0405SHaiyang Zhang 		netdev_dbg(netdev,
19051491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
19195fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19295fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
19395fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
19495fa0405SHaiyang Zhang 		break;
19595fa0405SHaiyang Zhang 
19651491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
19751491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
19895fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
19995fa0405SHaiyang Zhang 			rndis_msg->msg_len,
20095fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
20195fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
20295fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
20395fa0405SHaiyang Zhang 		break;
20495fa0405SHaiyang Zhang 
20595fa0405SHaiyang Zhang 	default:
20695fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
20795fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
20895fa0405SHaiyang Zhang 			rndis_msg->msg_len);
20995fa0405SHaiyang Zhang 		break;
21095fa0405SHaiyang Zhang 	}
21195fa0405SHaiyang Zhang }
21295fa0405SHaiyang Zhang 
21395fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
21495fa0405SHaiyang Zhang 				  struct rndis_request *req)
21595fa0405SHaiyang Zhang {
21695fa0405SHaiyang Zhang 	int ret;
21795fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
218b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
219a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
2203d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
22195fa0405SHaiyang Zhang 
22295fa0405SHaiyang Zhang 	/* Setup the packet to send it */
22395fa0405SHaiyang Zhang 	packet = &req->pkt;
22495fa0405SHaiyang Zhang 
22595fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
22695fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
22795fa0405SHaiyang Zhang 
228a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
22995fa0405SHaiyang Zhang 					PAGE_SHIFT;
230a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
231a9f2e2d6SKY Srinivasan 	pb[0].offset =
23295fa0405SHaiyang Zhang 		(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
23395fa0405SHaiyang Zhang 
23499e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
235a9f2e2d6SKY Srinivasan 	if (pb[0].offset + pb[0].len > PAGE_SIZE) {
23699e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
237a9f2e2d6SKY Srinivasan 		pb[0].len = PAGE_SIZE -
238a9f2e2d6SKY Srinivasan 			pb[0].offset;
239a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
240a9f2e2d6SKY Srinivasan 			+ pb[0].len) >> PAGE_SHIFT;
241a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
242a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
243a9f2e2d6SKY Srinivasan 			pb[0].len;
24499e3fcfaSHaiyang Zhang 	}
24599e3fcfaSHaiyang Zhang 
2463d541ac5SVitaly Kuznetsov 	ret = netvsc_send(net_device_ctx->device_ctx, packet, NULL, &pb, NULL);
24795fa0405SHaiyang Zhang 	return ret;
24895fa0405SHaiyang Zhang }
24995fa0405SHaiyang Zhang 
2501b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2511b07da51SHaiyang Zhang 				 struct rndis_request *request)
2521b07da51SHaiyang Zhang {
2531b07da51SHaiyang Zhang 	u32 link_status;
2541b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2551b07da51SHaiyang Zhang 
2561b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2571b07da51SHaiyang Zhang 
2581b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2591b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2601b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2611b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2621b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2631b07da51SHaiyang Zhang 	}
2641b07da51SHaiyang Zhang }
2651b07da51SHaiyang Zhang 
26695fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev,
26795fa0405SHaiyang Zhang 				       struct rndis_message *resp)
26895fa0405SHaiyang Zhang {
26995fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
27095fa0405SHaiyang Zhang 	bool found = false;
27195fa0405SHaiyang Zhang 	unsigned long flags;
2723d541ac5SVitaly Kuznetsov 	struct net_device *ndev = dev->ndev;
27395fa0405SHaiyang Zhang 
27495fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
27595fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
27695fa0405SHaiyang Zhang 		/*
27795fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
27895fa0405SHaiyang Zhang 		 * field
27995fa0405SHaiyang Zhang 		 */
28095fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
28195fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
28295fa0405SHaiyang Zhang 			found = true;
28395fa0405SHaiyang Zhang 			break;
28495fa0405SHaiyang Zhang 		}
28595fa0405SHaiyang Zhang 	}
28695fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
28795fa0405SHaiyang Zhang 
28895fa0405SHaiyang Zhang 	if (found) {
289a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
290a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
29195fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
29295fa0405SHaiyang Zhang 			       resp->msg_len);
2931b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
2941b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
2951b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
2961b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
29795fa0405SHaiyang Zhang 		} else {
29895fa0405SHaiyang Zhang 			netdev_err(ndev,
29995fa0405SHaiyang Zhang 				"rndis response buffer overflow "
30095fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
30195fa0405SHaiyang Zhang 				resp->msg_len,
30286eedaccSKY Srinivasan 				sizeof(struct rndis_message));
30395fa0405SHaiyang Zhang 
30495fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
30551491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
30695fa0405SHaiyang Zhang 				/* does not have a request id field */
30795fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
308007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
30995fa0405SHaiyang Zhang 			} else {
31095fa0405SHaiyang Zhang 				request->response_msg.msg.
31195fa0405SHaiyang Zhang 				init_complete.status =
312007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
31395fa0405SHaiyang Zhang 			}
31495fa0405SHaiyang Zhang 		}
31595fa0405SHaiyang Zhang 
31695fa0405SHaiyang Zhang 		complete(&request->wait_event);
31795fa0405SHaiyang Zhang 	} else {
31895fa0405SHaiyang Zhang 		netdev_err(ndev,
31995fa0405SHaiyang Zhang 			"no rndis request found for this response "
32095fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
32195fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
32295fa0405SHaiyang Zhang 			resp->ndis_msg_type);
32395fa0405SHaiyang Zhang 	}
32495fa0405SHaiyang Zhang }
32595fa0405SHaiyang Zhang 
3261f5f3a75SHaiyang Zhang /*
3271f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3281f5f3a75SHaiyang Zhang  * return NULL if not found.
3291f5f3a75SHaiyang Zhang  */
3301f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
3311f5f3a75SHaiyang Zhang {
3321f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3331f5f3a75SHaiyang Zhang 	int len;
3341f5f3a75SHaiyang Zhang 
3351f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3361f5f3a75SHaiyang Zhang 		return NULL;
3371f5f3a75SHaiyang Zhang 
3381f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3391f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3401f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3411f5f3a75SHaiyang Zhang 
3421f5f3a75SHaiyang Zhang 	while (len > 0) {
3431f5f3a75SHaiyang Zhang 		if (ppi->type == type)
3441f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3451f5f3a75SHaiyang Zhang 		len -= ppi->size;
3461f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3471f5f3a75SHaiyang Zhang 	}
3481f5f3a75SHaiyang Zhang 
3491f5f3a75SHaiyang Zhang 	return NULL;
3501f5f3a75SHaiyang Zhang }
3511f5f3a75SHaiyang Zhang 
352dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev,
353dc54a08cSstephen hemminger 				     struct rndis_device *dev,
35495fa0405SHaiyang Zhang 				     struct rndis_message *msg,
355dc54a08cSstephen hemminger 				     struct vmbus_channel *channel,
356dc54a08cSstephen hemminger 				     void *data, u32 data_buflen)
35795fa0405SHaiyang Zhang {
358dc54a08cSstephen hemminger 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
359dc54a08cSstephen hemminger 	const struct ndis_tcp_ip_checksum_info *csum_info;
360dc54a08cSstephen hemminger 	const struct ndis_pkt_8021q_info *vlan;
36195fa0405SHaiyang Zhang 	u32 data_offset;
36295fa0405SHaiyang Zhang 
36395fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
36495fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
36595fa0405SHaiyang Zhang 
366dc54a08cSstephen hemminger 	data_buflen -= data_offset;
3674b8a8bc9SWei Yongjun 
3684b8a8bc9SWei Yongjun 	/*
3694b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
3704b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
3714b8a8bc9SWei Yongjun 	 */
372dc54a08cSstephen hemminger 	if (unlikely(data_buflen < rndis_pkt->data_len)) {
3733d541ac5SVitaly Kuznetsov 		netdev_err(dev->ndev, "rndis message buffer "
3744b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
3754b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
376dc54a08cSstephen hemminger 			   data_buflen, rndis_pkt->data_len);
37710082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
3784b8a8bc9SWei Yongjun 	}
3794b8a8bc9SWei Yongjun 
380dc54a08cSstephen hemminger 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
381dc54a08cSstephen hemminger 
3824b8a8bc9SWei Yongjun 	/*
3834b8a8bc9SWei Yongjun 	 * Remove the rndis trailer padding from rndis packet message
3844b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
3854b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
3864b8a8bc9SWei Yongjun 	 */
387dc54a08cSstephen hemminger 	data = (void *)((unsigned long)data + data_offset);
388e3d605edSKY Srinivasan 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
389dc54a08cSstephen hemminger 	return netvsc_recv_callback(ndev, channel,
390dc54a08cSstephen hemminger 				    data, rndis_pkt->data_len,
391dc54a08cSstephen hemminger 				    csum_info, vlan);
39295fa0405SHaiyang Zhang }
39395fa0405SHaiyang Zhang 
394dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev,
395dc54a08cSstephen hemminger 			 struct netvsc_device *net_dev,
396dc54a08cSstephen hemminger 			 struct hv_device *dev,
397dc54a08cSstephen hemminger 			 struct vmbus_channel *channel,
398dc54a08cSstephen hemminger 			 void *data, u32 buflen)
39995fa0405SHaiyang Zhang {
4003d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
401dc54a08cSstephen hemminger 	struct rndis_device *rndis_dev = net_dev->extension;
402dc54a08cSstephen hemminger 	struct rndis_message *rndis_msg = data;
40395fa0405SHaiyang Zhang 
40495fa0405SHaiyang Zhang 	/* Make sure the rndis device state is initialized */
405dc54a08cSstephen hemminger 	if (unlikely(!rndis_dev)) {
406dc54a08cSstephen hemminger 		netif_err(net_device_ctx, rx_err, ndev,
407dc54a08cSstephen hemminger 			  "got rndis message but no rndis device!\n");
408dc54a08cSstephen hemminger 		return NVSP_STAT_FAIL;
40995fa0405SHaiyang Zhang 	}
41095fa0405SHaiyang Zhang 
411dc54a08cSstephen hemminger 	if (unlikely(rndis_dev->state == RNDIS_DEV_UNINITIALIZED)) {
412dc54a08cSstephen hemminger 		netif_err(net_device_ctx, rx_err, ndev,
413dc54a08cSstephen hemminger 			  "got rndis message uninitialized\n");
414dc54a08cSstephen hemminger 		return NVSP_STAT_FAIL;
41595fa0405SHaiyang Zhang 	}
41695fa0405SHaiyang Zhang 
417dc54a08cSstephen hemminger 	if (netif_msg_rx_status(net_device_ctx))
418ef31bef6SHaiyang Zhang 		dump_rndis_message(dev, rndis_msg);
41995fa0405SHaiyang Zhang 
420ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
42151491167SLinus Walleij 	case RNDIS_MSG_PACKET:
422dc54a08cSstephen hemminger 		return rndis_filter_receive_data(ndev, rndis_dev, rndis_msg,
423dc54a08cSstephen hemminger 						 channel, data, buflen);
42451491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
42551491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
42651491167SLinus Walleij 	case RNDIS_MSG_SET_C:
42795fa0405SHaiyang Zhang 		/* completion msgs */
428ef31bef6SHaiyang Zhang 		rndis_filter_receive_response(rndis_dev, rndis_msg);
42995fa0405SHaiyang Zhang 		break;
43095fa0405SHaiyang Zhang 
43151491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
43295fa0405SHaiyang Zhang 		/* notification msgs */
4333a494e71SHaiyang Zhang 		netvsc_linkstatus_callback(dev, rndis_msg);
43495fa0405SHaiyang Zhang 		break;
43595fa0405SHaiyang Zhang 	default:
43695fa0405SHaiyang Zhang 		netdev_err(ndev,
43795fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
438ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
439ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
44095fa0405SHaiyang Zhang 		break;
44195fa0405SHaiyang Zhang 	}
44295fa0405SHaiyang Zhang 
443dc54a08cSstephen hemminger 	return 0;
44495fa0405SHaiyang Zhang }
44595fa0405SHaiyang Zhang 
44695fa0405SHaiyang Zhang static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
44795fa0405SHaiyang Zhang 				  void *result, u32 *result_size)
44895fa0405SHaiyang Zhang {
44995fa0405SHaiyang Zhang 	struct rndis_request *request;
45095fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
45195fa0405SHaiyang Zhang 	struct rndis_query_request *query;
45295fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
45395fa0405SHaiyang Zhang 	int ret = 0;
45495fa0405SHaiyang Zhang 
45595fa0405SHaiyang Zhang 	if (!result)
45695fa0405SHaiyang Zhang 		return -EINVAL;
45795fa0405SHaiyang Zhang 
45895fa0405SHaiyang Zhang 	*result_size = 0;
45951491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
46095fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
46195fa0405SHaiyang Zhang 	if (!request) {
46295fa0405SHaiyang Zhang 		ret = -ENOMEM;
46395fa0405SHaiyang Zhang 		goto cleanup;
46495fa0405SHaiyang Zhang 	}
46595fa0405SHaiyang Zhang 
46695fa0405SHaiyang Zhang 	/* Setup the rndis query */
46795fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
46895fa0405SHaiyang Zhang 	query->oid = oid;
46995fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
47095fa0405SHaiyang Zhang 	query->info_buflen = 0;
47195fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
47295fa0405SHaiyang Zhang 
47323312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
47423312a3bSstephen hemminger 		struct net_device_context *ndevctx = netdev_priv(dev->ndev);
47523312a3bSstephen hemminger 		struct netvsc_device *nvdev = ndevctx->nvdev;
47623312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
47723312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
47823312a3bSstephen hemminger 		u8 ndis_rev;
47923312a3bSstephen hemminger 		size_t size;
48023312a3bSstephen hemminger 
48123312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
48223312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
48323312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
48423312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
48523312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
48623312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
48723312a3bSstephen hemminger 		} else {
48823312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
48923312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
49023312a3bSstephen hemminger 		}
49123312a3bSstephen hemminger 
49223312a3bSstephen hemminger 		request->request_msg.msg_len += size;
49323312a3bSstephen hemminger 		query->info_buflen = size;
49423312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
49523312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
49623312a3bSstephen hemminger 
49723312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
49823312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
49923312a3bSstephen hemminger 		hwcaps->header.size = size;
50023312a3bSstephen hemminger 
50123312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
5025b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
5035b54dac8SHaiyang Zhang 
5045b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
5055b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
5065b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
5075b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5085b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5095b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5105b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5115b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5125b54dac8SHaiyang Zhang 	}
5135b54dac8SHaiyang Zhang 
51495fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
51595fa0405SHaiyang Zhang 	if (ret != 0)
51695fa0405SHaiyang Zhang 		goto cleanup;
51795fa0405SHaiyang Zhang 
5185362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
51995fa0405SHaiyang Zhang 
52095fa0405SHaiyang Zhang 	/* Copy the response back */
52195fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
52295fa0405SHaiyang Zhang 
52395fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
52495fa0405SHaiyang Zhang 		ret = -1;
52595fa0405SHaiyang Zhang 		goto cleanup;
52695fa0405SHaiyang Zhang 	}
52795fa0405SHaiyang Zhang 
52895fa0405SHaiyang Zhang 	memcpy(result,
52995fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
53095fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
53195fa0405SHaiyang Zhang 	       query_complete->info_buflen);
53295fa0405SHaiyang Zhang 
53395fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
53495fa0405SHaiyang Zhang 
53595fa0405SHaiyang Zhang cleanup:
53695fa0405SHaiyang Zhang 	if (request)
53795fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
53895fa0405SHaiyang Zhang 
53995fa0405SHaiyang Zhang 	return ret;
54095fa0405SHaiyang Zhang }
54195fa0405SHaiyang Zhang 
54223312a3bSstephen hemminger /* Get the hardware offload capabilities */
54323312a3bSstephen hemminger static int
54423312a3bSstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct ndis_offload *caps)
54523312a3bSstephen hemminger {
54623312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
54723312a3bSstephen hemminger 	int ret;
54823312a3bSstephen hemminger 
54923312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
55023312a3bSstephen hemminger 
55123312a3bSstephen hemminger 	ret = rndis_filter_query_device(dev,
55223312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
55323312a3bSstephen hemminger 					caps, &caps_len);
55423312a3bSstephen hemminger 	if (ret)
55523312a3bSstephen hemminger 		return ret;
55623312a3bSstephen hemminger 
55723312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
55823312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
55923312a3bSstephen hemminger 			    caps->header.type);
56023312a3bSstephen hemminger 		return -EINVAL;
56123312a3bSstephen hemminger 	}
56223312a3bSstephen hemminger 
56323312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
56423312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
56523312a3bSstephen hemminger 			    caps->header.revision);
56623312a3bSstephen hemminger 		return -EINVAL;
56723312a3bSstephen hemminger 	}
56823312a3bSstephen hemminger 
56923312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
57023312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
57123312a3bSstephen hemminger 		netdev_warn(dev->ndev,
57223312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
57323312a3bSstephen hemminger 			    caps->header.size, caps_len);
57423312a3bSstephen hemminger 		return -EINVAL;
57523312a3bSstephen hemminger 	}
57623312a3bSstephen hemminger 
57723312a3bSstephen hemminger 	return 0;
57823312a3bSstephen hemminger }
57923312a3bSstephen hemminger 
58095fa0405SHaiyang Zhang static int rndis_filter_query_device_mac(struct rndis_device *dev)
58195fa0405SHaiyang Zhang {
58295fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
58395fa0405SHaiyang Zhang 
58495fa0405SHaiyang Zhang 	return rndis_filter_query_device(dev,
58595fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
58695fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
58795fa0405SHaiyang Zhang }
58895fa0405SHaiyang Zhang 
5891ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
5901ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
5911ce09e89SHaiyang Zhang 
592e834da9aSVitaly Kuznetsov int rndis_filter_set_device_mac(struct net_device *ndev, char *mac)
5931ce09e89SHaiyang Zhang {
5942625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev);
5951ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
5961ce09e89SHaiyang Zhang 	struct rndis_request *request;
5971ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
5981ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
5991ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
6001ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
6011ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
6021ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
6031ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
604999028ccSNicholas Mc Guire 	int ret;
6051ce09e89SHaiyang Zhang 
6061ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6071ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6081ce09e89SHaiyang Zhang 	if (!request)
6091ce09e89SHaiyang Zhang 		return -ENOMEM;
6101ce09e89SHaiyang Zhang 
6111ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
6121ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
6131ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
6141ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
6151ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
6161ce09e89SHaiyang Zhang 
6171ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
6181ce09e89SHaiyang Zhang 		set->info_buf_offset);
6191ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
6201ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
6211ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
6221ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
6231ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
6241ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
6251ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
6261ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
6271ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
6281ce09e89SHaiyang Zhang 
6291ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
6301ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
6311ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
6321ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
6331ce09e89SHaiyang Zhang 	if (ret < 0)
6341ce09e89SHaiyang Zhang 		goto cleanup;
6351ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
6361ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
6371ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
6381ce09e89SHaiyang Zhang 	if (ret < 0)
6391ce09e89SHaiyang Zhang 		goto cleanup;
6401ce09e89SHaiyang Zhang 
6411ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
6421ce09e89SHaiyang Zhang 	if (ret != 0)
6431ce09e89SHaiyang Zhang 		goto cleanup;
6441ce09e89SHaiyang Zhang 
6455362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
6465362855aSVitaly Kuznetsov 
6471ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
648b02a8067SHaiyang Zhang 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
649b02a8067SHaiyang Zhang 		netdev_err(ndev, "Fail to set MAC on host side:0x%x\n",
650b02a8067SHaiyang Zhang 			   set_complete->status);
6511ce09e89SHaiyang Zhang 		ret = -EINVAL;
6521ce09e89SHaiyang Zhang 	}
6531ce09e89SHaiyang Zhang 
6541ce09e89SHaiyang Zhang cleanup:
6551ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
6561ce09e89SHaiyang Zhang 	return ret;
6571ce09e89SHaiyang Zhang }
6581ce09e89SHaiyang Zhang 
659da19fcd0SLad, Prabhakar static int
660426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
6614a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
6624a0e70aeSKY Srinivasan {
6632625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev);
6644a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
6654a0e70aeSKY Srinivasan 	struct rndis_request *request;
6664a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
6674a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
6684a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
6694a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
670999028ccSNicholas Mc Guire 	int ret;
671af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
672af9893a3SKY Srinivasan 
673af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
674af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
675af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
676af9893a3SKY Srinivasan 		 * UDP checksum offload.
677af9893a3SKY Srinivasan 		 */
678af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
679af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
680af9893a3SKY Srinivasan 	}
6814a0e70aeSKY Srinivasan 
6824a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6834a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6844a0e70aeSKY Srinivasan 	if (!request)
6854a0e70aeSKY Srinivasan 		return -ENOMEM;
6864a0e70aeSKY Srinivasan 
6874a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
6884a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
6894a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
6904a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
6914a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
6924a0e70aeSKY Srinivasan 
6934a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
6944a0e70aeSKY Srinivasan 				set->info_buf_offset);
6954a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
6964a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
6974a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
6984a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
6994a0e70aeSKY Srinivasan 
7004a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
7014a0e70aeSKY Srinivasan 	if (ret != 0)
7024a0e70aeSKY Srinivasan 		goto cleanup;
7034a0e70aeSKY Srinivasan 
7045362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7054a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
7064a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
707af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
7084a0e70aeSKY Srinivasan 			   set_complete->status);
7094a0e70aeSKY Srinivasan 		ret = -EINVAL;
7104a0e70aeSKY Srinivasan 	}
7114a0e70aeSKY Srinivasan 
7124a0e70aeSKY Srinivasan cleanup:
7134a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
7144a0e70aeSKY Srinivasan 	return ret;
7154a0e70aeSKY Srinivasan }
7161ce09e89SHaiyang Zhang 
717962f3feeSstephen hemminger int rndis_filter_set_rss_param(struct rndis_device *rdev,
718962f3feeSstephen hemminger 			       const u8 *rss_key, int num_queue)
7195b54dac8SHaiyang Zhang {
7203d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
7215b54dac8SHaiyang Zhang 	struct rndis_request *request;
7225b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7235b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7245b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
725962f3feeSstephen hemminger 		     4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
7265b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7275b54dac8SHaiyang Zhang 	u32 *itab;
7285b54dac8SHaiyang Zhang 	u8 *keyp;
729999028ccSNicholas Mc Guire 	int i, ret;
7305b54dac8SHaiyang Zhang 
7315b54dac8SHaiyang Zhang 	request = get_rndis_request(
7325b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7335b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7345b54dac8SHaiyang Zhang 	if (!request)
7355b54dac8SHaiyang Zhang 		return -ENOMEM;
7365b54dac8SHaiyang Zhang 
7375b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7385b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7395b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
7405b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
7415b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
7425b54dac8SHaiyang Zhang 
7435b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
7445b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
7455b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
7465b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
7475b54dac8SHaiyang Zhang 	rssp->flag = 0;
7485b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
7494c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
7504c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
7515b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
7525b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
753962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
7545b54dac8SHaiyang Zhang 	rssp->kashkey_offset = rssp->indirect_taboffset +
7555b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
7565b54dac8SHaiyang Zhang 
7575b54dac8SHaiyang Zhang 	/* Set indirection table entries */
7585b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
7595b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
760ff4a4419Sstephen hemminger 		itab[i] = rdev->ind_table[i];
7615b54dac8SHaiyang Zhang 
7625b54dac8SHaiyang Zhang 	/* Set hask key values */
7635b54dac8SHaiyang Zhang 	keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
764962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
7655b54dac8SHaiyang Zhang 
7665b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7675b54dac8SHaiyang Zhang 	if (ret != 0)
7685b54dac8SHaiyang Zhang 		goto cleanup;
7695b54dac8SHaiyang Zhang 
7705362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7715b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
772962f3feeSstephen hemminger 	if (set_complete->status == RNDIS_STATUS_SUCCESS)
773962f3feeSstephen hemminger 		memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
774962f3feeSstephen hemminger 	else {
7755b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
7765b54dac8SHaiyang Zhang 			   set_complete->status);
7775b54dac8SHaiyang Zhang 		ret = -EINVAL;
7785b54dac8SHaiyang Zhang 	}
7795b54dac8SHaiyang Zhang 
7805b54dac8SHaiyang Zhang cleanup:
7815b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
7825b54dac8SHaiyang Zhang 	return ret;
7835b54dac8SHaiyang Zhang }
7845b54dac8SHaiyang Zhang 
78595fa0405SHaiyang Zhang static int rndis_filter_query_device_link_status(struct rndis_device *dev)
78695fa0405SHaiyang Zhang {
78795fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
78895fa0405SHaiyang Zhang 	u32 link_status;
78995fa0405SHaiyang Zhang 	int ret;
79095fa0405SHaiyang Zhang 
79195fa0405SHaiyang Zhang 	ret = rndis_filter_query_device(dev,
79295fa0405SHaiyang Zhang 				      RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
79395fa0405SHaiyang Zhang 				      &link_status, &size);
79495fa0405SHaiyang Zhang 
79595fa0405SHaiyang Zhang 	return ret;
79695fa0405SHaiyang Zhang }
79795fa0405SHaiyang Zhang 
798b37879e6SHaiyang Zhang static int rndis_filter_query_link_speed(struct rndis_device *dev)
799b37879e6SHaiyang Zhang {
800b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
801b37879e6SHaiyang Zhang 	u32 link_speed;
802b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
803b37879e6SHaiyang Zhang 	int ret;
804b37879e6SHaiyang Zhang 
805b37879e6SHaiyang Zhang 	ret = rndis_filter_query_device(dev, RNDIS_OID_GEN_LINK_SPEED,
806b37879e6SHaiyang Zhang 					&link_speed, &size);
807b37879e6SHaiyang Zhang 
808b37879e6SHaiyang Zhang 	if (!ret) {
809b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
810b37879e6SHaiyang Zhang 
811b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
812b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
813b37879e6SHaiyang Zhang 		 */
814b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
815b37879e6SHaiyang Zhang 	}
816b37879e6SHaiyang Zhang 
817b37879e6SHaiyang Zhang 	return ret;
818b37879e6SHaiyang Zhang }
819b37879e6SHaiyang Zhang 
8204f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev,
8214f19c0d8Sstephen hemminger 					  u32 new_filter)
82295fa0405SHaiyang Zhang {
82395fa0405SHaiyang Zhang 	struct rndis_request *request;
82495fa0405SHaiyang Zhang 	struct rndis_set_request *set;
825999028ccSNicholas Mc Guire 	int ret;
82695fa0405SHaiyang Zhang 
82751491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
82895fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
82995fa0405SHaiyang Zhang 			sizeof(u32));
830ce12b810Sstephen hemminger 	if (!request)
831ce12b810Sstephen hemminger 		return -ENOMEM;
832ce12b810Sstephen hemminger 
83395fa0405SHaiyang Zhang 
83495fa0405SHaiyang Zhang 	/* Setup the rndis set */
83595fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
83695fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
83795fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
83895fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
83995fa0405SHaiyang Zhang 
84095fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
84195fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
84295fa0405SHaiyang Zhang 
84395fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
844ce12b810Sstephen hemminger 	if (ret == 0)
8455362855aSVitaly Kuznetsov 		wait_for_completion(&request->wait_event);
84695fa0405SHaiyang Zhang 
84795fa0405SHaiyang Zhang 	put_rndis_request(dev, request);
848ce12b810Sstephen hemminger 
84995fa0405SHaiyang Zhang 	return ret;
85095fa0405SHaiyang Zhang }
85195fa0405SHaiyang Zhang 
8524f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w)
8534f19c0d8Sstephen hemminger {
8544f19c0d8Sstephen hemminger 	struct rndis_device *rdev
8554f19c0d8Sstephen hemminger 		= container_of(w, struct rndis_device, mcast_work);
8564f19c0d8Sstephen hemminger 
8574f19c0d8Sstephen hemminger 	if (rdev->ndev->flags & IFF_PROMISC)
8584f19c0d8Sstephen hemminger 		rndis_filter_set_packet_filter(rdev,
8594f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_PROMISCUOUS);
8604f19c0d8Sstephen hemminger 	else
8614f19c0d8Sstephen hemminger 		rndis_filter_set_packet_filter(rdev,
8624f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_BROADCAST |
8634f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_ALL_MULTICAST |
8644f19c0d8Sstephen hemminger 					       NDIS_PACKET_TYPE_DIRECTED);
8654f19c0d8Sstephen hemminger }
8664f19c0d8Sstephen hemminger 
8674f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev)
8684f19c0d8Sstephen hemminger {
8694f19c0d8Sstephen hemminger 	struct rndis_device *rdev = nvdev->extension;
8704f19c0d8Sstephen hemminger 
8714f19c0d8Sstephen hemminger 	schedule_work(&rdev->mcast_work);
8724f19c0d8Sstephen hemminger }
8734f19c0d8Sstephen hemminger 
87495fa0405SHaiyang Zhang static int rndis_filter_init_device(struct rndis_device *dev)
87595fa0405SHaiyang Zhang {
87695fa0405SHaiyang Zhang 	struct rndis_request *request;
87795fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
87895fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
87995fa0405SHaiyang Zhang 	u32 status;
880999028ccSNicholas Mc Guire 	int ret;
8812625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(dev->ndev);
88295fa0405SHaiyang Zhang 
88351491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
88495fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
88595fa0405SHaiyang Zhang 	if (!request) {
88695fa0405SHaiyang Zhang 		ret = -ENOMEM;
88795fa0405SHaiyang Zhang 		goto cleanup;
88895fa0405SHaiyang Zhang 	}
88995fa0405SHaiyang Zhang 
89095fa0405SHaiyang Zhang 	/* Setup the rndis set */
89195fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
89295fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
89395fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
894fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
89595fa0405SHaiyang Zhang 
89695fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
89795fa0405SHaiyang Zhang 
89895fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
89995fa0405SHaiyang Zhang 	if (ret != 0) {
90095fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
90195fa0405SHaiyang Zhang 		goto cleanup;
90295fa0405SHaiyang Zhang 	}
90395fa0405SHaiyang Zhang 
9045362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
90595fa0405SHaiyang Zhang 
90695fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
90795fa0405SHaiyang Zhang 	status = init_complete->status;
90895fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
90995fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
9107c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
9117c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
91295fa0405SHaiyang Zhang 		ret = 0;
91395fa0405SHaiyang Zhang 	} else {
91495fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
91595fa0405SHaiyang Zhang 		ret = -EINVAL;
91695fa0405SHaiyang Zhang 	}
91795fa0405SHaiyang Zhang 
91895fa0405SHaiyang Zhang cleanup:
91995fa0405SHaiyang Zhang 	if (request)
92095fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
92195fa0405SHaiyang Zhang 
92295fa0405SHaiyang Zhang 	return ret;
92395fa0405SHaiyang Zhang }
92495fa0405SHaiyang Zhang 
92546b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev)
92646b4f7f5Sstephen hemminger {
92746b4f7f5Sstephen hemminger 	int i;
92846b4f7f5Sstephen hemminger 
92946b4f7f5Sstephen hemminger 	if (atomic_read(&nvdev->num_outstanding_recvs) > 0)
93046b4f7f5Sstephen hemminger 		return false;
93146b4f7f5Sstephen hemminger 
93246b4f7f5Sstephen hemminger 	for (i = 0; i < nvdev->num_chn; i++) {
93346b4f7f5Sstephen hemminger 		const struct netvsc_channel *nvchan = &nvdev->chan_table[i];
93446b4f7f5Sstephen hemminger 
93546b4f7f5Sstephen hemminger 		if (atomic_read(&nvchan->queue_sends) > 0)
93646b4f7f5Sstephen hemminger 			return false;
93746b4f7f5Sstephen hemminger 	}
93846b4f7f5Sstephen hemminger 
93946b4f7f5Sstephen hemminger 	return true;
94046b4f7f5Sstephen hemminger }
94146b4f7f5Sstephen hemminger 
94295fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev)
94395fa0405SHaiyang Zhang {
94495fa0405SHaiyang Zhang 	struct rndis_request *request;
94595fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
9463d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
9473d541ac5SVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_ctx->nvdev;
94895fa0405SHaiyang Zhang 
94995fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
95051491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
95195fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
95295fa0405SHaiyang Zhang 	if (!request)
95395fa0405SHaiyang Zhang 		goto cleanup;
95495fa0405SHaiyang Zhang 
95595fa0405SHaiyang Zhang 	/* Setup the rndis set */
95695fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
95795fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
95895fa0405SHaiyang Zhang 
95995fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
96095fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
96195fa0405SHaiyang Zhang 
96295fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
96395fa0405SHaiyang Zhang 
96495fa0405SHaiyang Zhang cleanup:
965ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
96600ecfb3bSstephen hemminger 
96700ecfb3bSstephen hemminger 	/* Force flag to be ordered before waiting */
96800ecfb3bSstephen hemminger 	wmb();
969ae9e63bbSHaiyang Zhang 
970ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
97146b4f7f5Sstephen hemminger 	wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
972ae9e63bbSHaiyang Zhang 
97395fa0405SHaiyang Zhang 	if (request)
97495fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
97595fa0405SHaiyang Zhang }
97695fa0405SHaiyang Zhang 
97795fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
97895fa0405SHaiyang Zhang {
97995fa0405SHaiyang Zhang 	int ret;
98095fa0405SHaiyang Zhang 
98195fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
98295fa0405SHaiyang Zhang 		return 0;
98395fa0405SHaiyang Zhang 
98495fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
98595fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
98695fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
98795fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
98895fa0405SHaiyang Zhang 	if (ret == 0)
98995fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
99095fa0405SHaiyang Zhang 
99195fa0405SHaiyang Zhang 	return ret;
99295fa0405SHaiyang Zhang }
99395fa0405SHaiyang Zhang 
99495fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
99595fa0405SHaiyang Zhang {
99695fa0405SHaiyang Zhang 	int ret;
99795fa0405SHaiyang Zhang 
99895fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
99995fa0405SHaiyang Zhang 		return 0;
100095fa0405SHaiyang Zhang 
10014f19c0d8Sstephen hemminger 	/* Make sure rndis_set_multicast doesn't re-enable filter! */
10024f19c0d8Sstephen hemminger 	cancel_work_sync(&dev->mcast_work);
10034f19c0d8Sstephen hemminger 
100495fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
1005c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
1006c3582a2cSHaiyang Zhang 		ret = 0;
1007c3582a2cSHaiyang Zhang 
100895fa0405SHaiyang Zhang 	if (ret == 0)
100995fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
101095fa0405SHaiyang Zhang 
101195fa0405SHaiyang Zhang 	return ret;
101295fa0405SHaiyang Zhang }
101395fa0405SHaiyang Zhang 
10145b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
10155b54dac8SHaiyang Zhang {
10163d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
10173d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
10182625466dSVitaly Kuznetsov 	struct netvsc_device *nvscdev = net_device_to_netvsc_device(ndev);
10195b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
10206de38af6Sstephen hemminger 	struct netvsc_channel *nvchan;
10216de38af6Sstephen hemminger 	int ret;
10225b54dac8SHaiyang Zhang 
10235b54dac8SHaiyang Zhang 	if (chn_index >= nvscdev->num_chn)
10245b54dac8SHaiyang Zhang 		return;
10255b54dac8SHaiyang Zhang 
10266de38af6Sstephen hemminger 	nvchan = nvscdev->chan_table + chn_index;
10276de38af6Sstephen hemminger 	nvchan->mrc.buf
1028b8b835a8Sstephen hemminger 		= vzalloc(NETVSC_RECVSLOT_MAX * sizeof(struct recv_comp_data));
1029c0b558e5SHaiyang Zhang 
10306de38af6Sstephen hemminger 	if (!nvchan->mrc.buf)
10316de38af6Sstephen hemminger 		return;
10326de38af6Sstephen hemminger 
1033b1dd90ceSK. Y. Srinivasan 	/* Because the device uses NAPI, all the interrupt batching and
1034b1dd90ceSK. Y. Srinivasan 	 * control is done via Net softirq, not the channel handling
1035b1dd90ceSK. Y. Srinivasan 	 */
1036b1dd90ceSK. Y. Srinivasan 	set_channel_read_mode(new_sc, HV_CALL_ISR);
1037b1dd90ceSK. Y. Srinivasan 
1038bffb1842SK. Y. Srinivasan 	/* Set the channel before opening.*/
1039bffb1842SK. Y. Srinivasan 	nvchan->channel = new_sc;
104076bb5db5Sstephen hemminger 	netif_napi_add(ndev, &nvchan->napi,
104176bb5db5Sstephen hemminger 		       netvsc_poll, NAPI_POLL_WEIGHT);
1042bffb1842SK. Y. Srinivasan 
10435b54dac8SHaiyang Zhang 	ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
10445b54dac8SHaiyang Zhang 			 nvscdev->ring_size * PAGE_SIZE, NULL, 0,
10456de38af6Sstephen hemminger 			 netvsc_channel_cb, nvchan);
104676bb5db5Sstephen hemminger 	if (ret == 0)
10476de38af6Sstephen hemminger 		napi_enable(&nvchan->napi);
104876bb5db5Sstephen hemminger 	else
10492be0f264Sstephen hemminger 		netif_napi_del(&nvchan->napi);
105015a863bfSstephen hemminger 
105143c7bd1fSstephen hemminger 	if (refcount_dec_and_test(&nvscdev->sc_offered))
10523f735131SHaiyang Zhang 		complete(&nvscdev->channel_init_wait);
10535b54dac8SHaiyang Zhang }
10545b54dac8SHaiyang Zhang 
105595fa0405SHaiyang Zhang int rndis_filter_device_add(struct hv_device *dev,
10562c7f83caSstephen hemminger 			    struct netvsc_device_info *device_info)
105795fa0405SHaiyang Zhang {
10583d541ac5SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
10593d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
106095fa0405SHaiyang Zhang 	struct netvsc_device *net_device;
106195fa0405SHaiyang Zhang 	struct rndis_device *rndis_device;
106223312a3bSstephen hemminger 	struct ndis_offload hwcaps;
10634a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
10645b54dac8SHaiyang Zhang 	struct nvsp_message *init_packet;
10655b54dac8SHaiyang Zhang 	struct ndis_recv_scale_cap rsscap;
10665b54dac8SHaiyang Zhang 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
106723312a3bSstephen hemminger 	unsigned int gso_max_size = GSO_MAX_SIZE;
106843c7bd1fSstephen hemminger 	u32 mtu, size, num_rss_qs;
1069e01ec219SKY Srinivasan 	const struct cpumask *node_cpu_mask;
1070e01ec219SKY Srinivasan 	u32 num_possible_rss_qs;
1071ff4a4419Sstephen hemminger 	int i, ret;
107295fa0405SHaiyang Zhang 
107395fa0405SHaiyang Zhang 	rndis_device = get_rndis_device();
107495fa0405SHaiyang Zhang 	if (!rndis_device)
107595fa0405SHaiyang Zhang 		return -ENODEV;
107695fa0405SHaiyang Zhang 
107795fa0405SHaiyang Zhang 	/*
107895fa0405SHaiyang Zhang 	 * Let the inner driver handle this first to create the netvsc channel
107995fa0405SHaiyang Zhang 	 * NOTE! Once the channel is created, we may get a receive callback
108095fa0405SHaiyang Zhang 	 * (RndisFilterOnReceive()) before this call is completed
108195fa0405SHaiyang Zhang 	 */
10822c7f83caSstephen hemminger 	ret = netvsc_device_add(dev, device_info);
108395fa0405SHaiyang Zhang 	if (ret != 0) {
108495fa0405SHaiyang Zhang 		kfree(rndis_device);
108595fa0405SHaiyang Zhang 		return ret;
108695fa0405SHaiyang Zhang 	}
108795fa0405SHaiyang Zhang 
108895fa0405SHaiyang Zhang 	/* Initialize the rndis device */
10893d541ac5SVitaly Kuznetsov 	net_device = net_device_ctx->nvdev;
109059995370SAndrew Schwartzmeyer 	net_device->max_chn = 1;
10915b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
109295fa0405SHaiyang Zhang 
109343c7bd1fSstephen hemminger 	refcount_set(&net_device->sc_offered, 0);
1094b3e6b82aSKY Srinivasan 
109595fa0405SHaiyang Zhang 	net_device->extension = rndis_device;
10963d541ac5SVitaly Kuznetsov 	rndis_device->ndev = net;
109795fa0405SHaiyang Zhang 
109895fa0405SHaiyang Zhang 	/* Send the rndis initialization message */
109995fa0405SHaiyang Zhang 	ret = rndis_filter_init_device(rndis_device);
110095fa0405SHaiyang Zhang 	if (ret != 0) {
11012289f0aaSstephen hemminger 		rndis_filter_device_remove(dev, net_device);
11025243e7bdSHaiyang Zhang 		return ret;
110395fa0405SHaiyang Zhang 	}
110495fa0405SHaiyang Zhang 
11054d3c9d37SHaiyang Zhang 	/* Get the MTU from the host */
11064d3c9d37SHaiyang Zhang 	size = sizeof(u32);
11074d3c9d37SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
11084d3c9d37SHaiyang Zhang 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
11094d3c9d37SHaiyang Zhang 					&mtu, &size);
11100a1275caSVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
11110a1275caSVitaly Kuznetsov 		net->mtu = mtu;
11124d3c9d37SHaiyang Zhang 
111395fa0405SHaiyang Zhang 	/* Get the mac address */
111495fa0405SHaiyang Zhang 	ret = rndis_filter_query_device_mac(rndis_device);
111595fa0405SHaiyang Zhang 	if (ret != 0) {
11162289f0aaSstephen hemminger 		rndis_filter_device_remove(dev, net_device);
11175243e7bdSHaiyang Zhang 		return ret;
111895fa0405SHaiyang Zhang 	}
111995fa0405SHaiyang Zhang 
112095fa0405SHaiyang Zhang 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
112195fa0405SHaiyang Zhang 
112223312a3bSstephen hemminger 	/* Find HW offload capabilities */
112323312a3bSstephen hemminger 	ret = rndis_query_hwcaps(rndis_device, &hwcaps);
112423312a3bSstephen hemminger 	if (ret != 0) {
11252289f0aaSstephen hemminger 		rndis_filter_device_remove(dev, net_device);
112623312a3bSstephen hemminger 		return ret;
112723312a3bSstephen hemminger 	}
112823312a3bSstephen hemminger 
112923312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
11304a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
113123312a3bSstephen hemminger 
113223312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
113323312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
113423312a3bSstephen hemminger 
113523312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
113623312a3bSstephen hemminger 	net->hw_features = NETIF_F_RXCSUM;
113723312a3bSstephen hemminger 
113823312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
113923312a3bSstephen hemminger 		/* Can checksum TCP */
114023312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
114123312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
114223312a3bSstephen hemminger 
11434a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
114423312a3bSstephen hemminger 
114523312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
11464a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
114723312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
114823312a3bSstephen hemminger 
114923312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
115023312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
115123312a3bSstephen hemminger 		}
115223312a3bSstephen hemminger 
115323312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
115423312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
115523312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
115623312a3bSstephen hemminger 		}
115723312a3bSstephen hemminger 	}
115823312a3bSstephen hemminger 
115923312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
116023312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
116123312a3bSstephen hemminger 
116223312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
116323312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
116423312a3bSstephen hemminger 
116523312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
116623312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
116723312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
116823312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
116923312a3bSstephen hemminger 
117023312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
117123312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
117223312a3bSstephen hemminger 		}
117323312a3bSstephen hemminger 
117423312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
117523312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
117623312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
117723312a3bSstephen hemminger 		}
117823312a3bSstephen hemminger 	}
117923312a3bSstephen hemminger 
118023312a3bSstephen hemminger 	netif_set_gso_max_size(net, gso_max_size);
11814a0e70aeSKY Srinivasan 
1182426d9541SVitaly Kuznetsov 	ret = rndis_filter_set_offload_params(net, &offloads);
11834a0e70aeSKY Srinivasan 	if (ret)
11844a0e70aeSKY Srinivasan 		goto err_dev_remv;
11854a0e70aeSKY Srinivasan 
118695fa0405SHaiyang Zhang 	rndis_filter_query_device_link_status(rndis_device);
118795fa0405SHaiyang Zhang 
118893ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
118995fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
1190dedb459eSHaiyang Zhang 		   rndis_device->link_state ? "down" : "up");
119195fa0405SHaiyang Zhang 
11925b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
11935b54dac8SHaiyang Zhang 		return 0;
11945b54dac8SHaiyang Zhang 
1195b37879e6SHaiyang Zhang 	rndis_filter_query_link_speed(rndis_device);
1196b37879e6SHaiyang Zhang 
11975b54dac8SHaiyang Zhang 	/* vRSS setup */
11985b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
11995b54dac8SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
12005b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
12015b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
12025b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
12035b54dac8SHaiyang Zhang 		goto out;
12045b54dac8SHaiyang Zhang 
1205e01ec219SKY Srinivasan 	/*
1206e01ec219SKY Srinivasan 	 * We will limit the VRSS channels to the number CPUs in the NUMA node
1207e01ec219SKY Srinivasan 	 * the primary channel is currently bound to.
12083071ada4Sstephen hemminger 	 *
12093071ada4Sstephen hemminger 	 * This also guarantees that num_possible_rss_qs <= num_online_cpus
1210e01ec219SKY Srinivasan 	 */
1211e01ec219SKY Srinivasan 	node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu));
12123071ada4Sstephen hemminger 	num_possible_rss_qs = min_t(u32, cpumask_weight(node_cpu_mask),
12133071ada4Sstephen hemminger 				    rsscap.num_recv_que);
12143071ada4Sstephen hemminger 
12153071ada4Sstephen hemminger 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs);
12168ebdcc52SAndrew Schwartzmeyer 
12178ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
12183071ada4Sstephen hemminger 	net_device->num_chn = min(net_device->max_chn, device_info->num_chn);
1219ff4a4419Sstephen hemminger 
1220ff4a4419Sstephen hemminger 	for (i = 0; i < ITAB_NUM; i++)
1221ff4a4419Sstephen hemminger 		rndis_device->ind_table[i] = ethtool_rxfh_indir_default(i,
1222ff4a4419Sstephen hemminger 							net_device->num_chn);
1223ff4a4419Sstephen hemminger 
12243071ada4Sstephen hemminger 	num_rss_qs = net_device->num_chn - 1;
12253071ada4Sstephen hemminger 	if (num_rss_qs == 0)
12263071ada4Sstephen hemminger 		return 0;
12275b54dac8SHaiyang Zhang 
122843c7bd1fSstephen hemminger 	refcount_set(&net_device->sc_offered, num_rss_qs);
12295b54dac8SHaiyang Zhang 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
12305b54dac8SHaiyang Zhang 
12315b54dac8SHaiyang Zhang 	init_packet = &net_device->channel_init_pkt;
12325b54dac8SHaiyang Zhang 	memset(init_packet, 0, sizeof(struct nvsp_message));
12335b54dac8SHaiyang Zhang 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
12345b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
12355b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
12365b54dac8SHaiyang Zhang 						net_device->num_chn - 1;
12375b54dac8SHaiyang Zhang 	ret = vmbus_sendpacket(dev->channel, init_packet,
12385b54dac8SHaiyang Zhang 			       sizeof(struct nvsp_message),
12395b54dac8SHaiyang Zhang 			       (unsigned long)init_packet,
12405b54dac8SHaiyang Zhang 			       VM_PKT_DATA_INBAND,
12415b54dac8SHaiyang Zhang 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
12425b54dac8SHaiyang Zhang 	if (ret)
12435b54dac8SHaiyang Zhang 		goto out;
12445362855aSVitaly Kuznetsov 
124543c7bd1fSstephen hemminger 	if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
12465b54dac8SHaiyang Zhang 		ret = -ENODEV;
12475b54dac8SHaiyang Zhang 		goto out;
12485b54dac8SHaiyang Zhang 	}
124943c7bd1fSstephen hemminger 	wait_for_completion(&net_device->channel_init_wait);
125043c7bd1fSstephen hemminger 
12515b54dac8SHaiyang Zhang 	net_device->num_chn = 1 +
12525b54dac8SHaiyang Zhang 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
12535b54dac8SHaiyang Zhang 
125443c7bd1fSstephen hemminger 	/* ignore failues from setting rss parameters, still have channels */
125543c7bd1fSstephen hemminger 	rndis_filter_set_rss_param(rndis_device, netvsc_hash_key,
1256962f3feeSstephen hemminger 				   net_device->num_chn);
12575b54dac8SHaiyang Zhang out:
125859995370SAndrew Schwartzmeyer 	if (ret) {
125959995370SAndrew Schwartzmeyer 		net_device->max_chn = 1;
12605b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
126159995370SAndrew Schwartzmeyer 	}
1262b3e6b82aSKY Srinivasan 
12635b54dac8SHaiyang Zhang 	return 0; /* return 0 because primary channel can be used alone */
12644a0e70aeSKY Srinivasan 
12654a0e70aeSKY Srinivasan err_dev_remv:
12662289f0aaSstephen hemminger 	rndis_filter_device_remove(dev, net_device);
12674a0e70aeSKY Srinivasan 	return ret;
126895fa0405SHaiyang Zhang }
126995fa0405SHaiyang Zhang 
12702289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev,
12712289f0aaSstephen hemminger 				struct netvsc_device *net_dev)
127295fa0405SHaiyang Zhang {
127395fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1274d66ab514SHaiyang Zhang 
127595fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
127695fa0405SHaiyang Zhang 	rndis_filter_halt_device(rndis_dev);
127795fa0405SHaiyang Zhang 
127895fa0405SHaiyang Zhang 	kfree(rndis_dev);
127995fa0405SHaiyang Zhang 	net_dev->extension = NULL;
128095fa0405SHaiyang Zhang 
128195fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
128295fa0405SHaiyang Zhang }
128395fa0405SHaiyang Zhang 
12842f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev)
128595fa0405SHaiyang Zhang {
12862f5fa6c8SVitaly Kuznetsov 	if (!nvdev)
128795fa0405SHaiyang Zhang 		return -EINVAL;
128895fa0405SHaiyang Zhang 
12892f5fa6c8SVitaly Kuznetsov 	if (atomic_inc_return(&nvdev->open_cnt) != 1)
129084bf9cefSKY Srinivasan 		return 0;
129184bf9cefSKY Srinivasan 
12922f5fa6c8SVitaly Kuznetsov 	return rndis_filter_open_device(nvdev->extension);
129395fa0405SHaiyang Zhang }
129495fa0405SHaiyang Zhang 
12952f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev)
129695fa0405SHaiyang Zhang {
12975fccab3bSHaiyang Zhang 	if (!nvdev)
129895fa0405SHaiyang Zhang 		return -EINVAL;
129995fa0405SHaiyang Zhang 
130084bf9cefSKY Srinivasan 	if (atomic_dec_return(&nvdev->open_cnt) != 0)
130184bf9cefSKY Srinivasan 		return 0;
130284bf9cefSKY Srinivasan 
13035fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
130495fa0405SHaiyang Zhang }
1305