195fa0405SHaiyang Zhang /*
295fa0405SHaiyang Zhang  * Copyright (c) 2009, Microsoft Corporation.
395fa0405SHaiyang Zhang  *
495fa0405SHaiyang Zhang  * This program is free software; you can redistribute it and/or modify it
595fa0405SHaiyang Zhang  * under the terms and conditions of the GNU General Public License,
695fa0405SHaiyang Zhang  * version 2, as published by the Free Software Foundation.
795fa0405SHaiyang Zhang  *
895fa0405SHaiyang Zhang  * This program is distributed in the hope it will be useful, but WITHOUT
995fa0405SHaiyang Zhang  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1095fa0405SHaiyang Zhang  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
1195fa0405SHaiyang Zhang  * more details.
1295fa0405SHaiyang Zhang  *
1395fa0405SHaiyang Zhang  * You should have received a copy of the GNU General Public License along with
14adf8d3ffSJeff Kirsher  * this program; if not, see <http://www.gnu.org/licenses/>.
1595fa0405SHaiyang Zhang  *
1695fa0405SHaiyang Zhang  * Authors:
1795fa0405SHaiyang Zhang  *   Haiyang Zhang <haiyangz@microsoft.com>
1895fa0405SHaiyang Zhang  *   Hank Janssen  <hjanssen@microsoft.com>
1995fa0405SHaiyang Zhang  */
2095fa0405SHaiyang Zhang #include <linux/kernel.h>
2195fa0405SHaiyang Zhang #include <linux/sched.h>
2295fa0405SHaiyang Zhang #include <linux/wait.h>
2395fa0405SHaiyang Zhang #include <linux/highmem.h>
2495fa0405SHaiyang Zhang #include <linux/slab.h>
2595fa0405SHaiyang Zhang #include <linux/io.h>
2695fa0405SHaiyang Zhang #include <linux/if_ether.h>
2795fa0405SHaiyang Zhang #include <linux/netdevice.h>
281f5f3a75SHaiyang Zhang #include <linux/if_vlan.h>
291ce09e89SHaiyang Zhang #include <linux/nls.h>
30d6472302SStephen Rothwell #include <linux/vmalloc.h>
3195fa0405SHaiyang Zhang 
3295fa0405SHaiyang Zhang #include "hyperv_net.h"
3395fa0405SHaiyang Zhang 
3495fa0405SHaiyang Zhang 
355b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE
3695fa0405SHaiyang Zhang struct rndis_request {
3795fa0405SHaiyang Zhang 	struct list_head list_ent;
3895fa0405SHaiyang Zhang 	struct completion  wait_event;
3995fa0405SHaiyang Zhang 
4095fa0405SHaiyang Zhang 	struct rndis_message response_msg;
41a3a6cab5SHaiyang Zhang 	/*
42a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
43a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
44a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
45a3a6cab5SHaiyang Zhang 	 * future.
46a3a6cab5SHaiyang Zhang 	 */
47a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
4895fa0405SHaiyang Zhang 
4995fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
5095fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
510f48917bSHaiyang Zhang 
5295fa0405SHaiyang Zhang 	struct rndis_message request_msg;
530f48917bSHaiyang Zhang 	/*
54a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
55a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
560f48917bSHaiyang Zhang 	 */
57a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
5895fa0405SHaiyang Zhang };
5995fa0405SHaiyang Zhang 
60962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = {
61962f3feeSstephen hemminger 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
62962f3feeSstephen hemminger 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
63962f3feeSstephen hemminger 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
64962f3feeSstephen hemminger 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
65962f3feeSstephen hemminger 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
66962f3feeSstephen hemminger };
67962f3feeSstephen hemminger 
6895fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
6995fa0405SHaiyang Zhang {
7095fa0405SHaiyang Zhang 	struct rndis_device *device;
7195fa0405SHaiyang Zhang 
7295fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
7395fa0405SHaiyang Zhang 	if (!device)
7495fa0405SHaiyang Zhang 		return NULL;
7595fa0405SHaiyang Zhang 
7695fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
7795fa0405SHaiyang Zhang 
7895fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
7995fa0405SHaiyang Zhang 
8095fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
8195fa0405SHaiyang Zhang 
8295fa0405SHaiyang Zhang 	return device;
8395fa0405SHaiyang Zhang }
8495fa0405SHaiyang Zhang 
8595fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
8695fa0405SHaiyang Zhang 					     u32 msg_type,
8795fa0405SHaiyang Zhang 					     u32 msg_len)
8895fa0405SHaiyang Zhang {
8995fa0405SHaiyang Zhang 	struct rndis_request *request;
9095fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
9195fa0405SHaiyang Zhang 	struct rndis_set_request *set;
9295fa0405SHaiyang Zhang 	unsigned long flags;
9395fa0405SHaiyang Zhang 
9495fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
9595fa0405SHaiyang Zhang 	if (!request)
9695fa0405SHaiyang Zhang 		return NULL;
9795fa0405SHaiyang Zhang 
9895fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
9995fa0405SHaiyang Zhang 
10095fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
10195fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
10295fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
10395fa0405SHaiyang Zhang 
1045b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
1055b54dac8SHaiyang Zhang 
10695fa0405SHaiyang Zhang 	/*
10795fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
10895fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
10995fa0405SHaiyang Zhang 	 * template
11095fa0405SHaiyang Zhang 	 */
11195fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
11295fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
11395fa0405SHaiyang Zhang 
11495fa0405SHaiyang Zhang 	/* Add to the request list */
11595fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
11695fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
11795fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
11895fa0405SHaiyang Zhang 
11995fa0405SHaiyang Zhang 	return request;
12095fa0405SHaiyang Zhang }
12195fa0405SHaiyang Zhang 
12295fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
12395fa0405SHaiyang Zhang 			    struct rndis_request *req)
12495fa0405SHaiyang Zhang {
12595fa0405SHaiyang Zhang 	unsigned long flags;
12695fa0405SHaiyang Zhang 
12795fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
12895fa0405SHaiyang Zhang 	list_del(&req->list_ent);
12995fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
13095fa0405SHaiyang Zhang 
13195fa0405SHaiyang Zhang 	kfree(req);
13295fa0405SHaiyang Zhang }
13395fa0405SHaiyang Zhang 
13495fa0405SHaiyang Zhang static void dump_rndis_message(struct hv_device *hv_dev,
13595fa0405SHaiyang Zhang 			struct rndis_message *rndis_msg)
13695fa0405SHaiyang Zhang {
1373d541ac5SVitaly Kuznetsov 	struct net_device *netdev = hv_get_drvdata(hv_dev);
13895fa0405SHaiyang Zhang 
13995fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
14051491167SLinus Walleij 	case RNDIS_MSG_PACKET:
14151491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
14295fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
14395fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
14495fa0405SHaiyang Zhang 			   "pkt len %u\n",
14595fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
14695fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
14795fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
14895fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
14995fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
15095fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
15195fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
15295fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
15395fa0405SHaiyang Zhang 		break;
15495fa0405SHaiyang Zhang 
15551491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
15651491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
15795fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
15895fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
15995fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
16095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
16195fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
16295fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
16395fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
16495fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
16595fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
16695fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
16795fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
16895fa0405SHaiyang Zhang 			   max_pkt_per_msg,
16995fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17095fa0405SHaiyang Zhang 			   pkt_alignment_factor);
17195fa0405SHaiyang Zhang 		break;
17295fa0405SHaiyang Zhang 
17351491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
17451491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17595fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
17695fa0405SHaiyang Zhang 			"buf offset %u)\n",
17795fa0405SHaiyang Zhang 			rndis_msg->msg_len,
17895fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
17995fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
18095fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18195fa0405SHaiyang Zhang 			   info_buflen,
18295fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18395fa0405SHaiyang Zhang 			   info_buf_offset);
18495fa0405SHaiyang Zhang 		break;
18595fa0405SHaiyang Zhang 
18651491167SLinus Walleij 	case RNDIS_MSG_SET_C:
18795fa0405SHaiyang Zhang 		netdev_dbg(netdev,
18851491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
18995fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19095fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
19195fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
19295fa0405SHaiyang Zhang 		break;
19395fa0405SHaiyang Zhang 
19451491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
19551491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
19695fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
19795fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19895fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
19995fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
20095fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
20195fa0405SHaiyang Zhang 		break;
20295fa0405SHaiyang Zhang 
20395fa0405SHaiyang Zhang 	default:
20495fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
20595fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
20695fa0405SHaiyang Zhang 			rndis_msg->msg_len);
20795fa0405SHaiyang Zhang 		break;
20895fa0405SHaiyang Zhang 	}
20995fa0405SHaiyang Zhang }
21095fa0405SHaiyang Zhang 
21195fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
21295fa0405SHaiyang Zhang 				  struct rndis_request *req)
21395fa0405SHaiyang Zhang {
21495fa0405SHaiyang Zhang 	int ret;
21595fa0405SHaiyang Zhang 	struct hv_netvsc_packet *packet;
216b08cc791SKY Srinivasan 	struct hv_page_buffer page_buf[2];
217a9f2e2d6SKY Srinivasan 	struct hv_page_buffer *pb = page_buf;
2183d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
21995fa0405SHaiyang Zhang 
22095fa0405SHaiyang Zhang 	/* Setup the packet to send it */
22195fa0405SHaiyang Zhang 	packet = &req->pkt;
22295fa0405SHaiyang Zhang 
22395fa0405SHaiyang Zhang 	packet->total_data_buflen = req->request_msg.msg_len;
22495fa0405SHaiyang Zhang 	packet->page_buf_cnt = 1;
22595fa0405SHaiyang Zhang 
226a9f2e2d6SKY Srinivasan 	pb[0].pfn = virt_to_phys(&req->request_msg) >>
22795fa0405SHaiyang Zhang 					PAGE_SHIFT;
228a9f2e2d6SKY Srinivasan 	pb[0].len = req->request_msg.msg_len;
229a9f2e2d6SKY Srinivasan 	pb[0].offset =
23095fa0405SHaiyang Zhang 		(unsigned long)&req->request_msg & (PAGE_SIZE - 1);
23195fa0405SHaiyang Zhang 
23299e3fcfaSHaiyang Zhang 	/* Add one page_buf when request_msg crossing page boundary */
233a9f2e2d6SKY Srinivasan 	if (pb[0].offset + pb[0].len > PAGE_SIZE) {
23499e3fcfaSHaiyang Zhang 		packet->page_buf_cnt++;
235a9f2e2d6SKY Srinivasan 		pb[0].len = PAGE_SIZE -
236a9f2e2d6SKY Srinivasan 			pb[0].offset;
237a9f2e2d6SKY Srinivasan 		pb[1].pfn = virt_to_phys((void *)&req->request_msg
238a9f2e2d6SKY Srinivasan 			+ pb[0].len) >> PAGE_SHIFT;
239a9f2e2d6SKY Srinivasan 		pb[1].offset = 0;
240a9f2e2d6SKY Srinivasan 		pb[1].len = req->request_msg.msg_len -
241a9f2e2d6SKY Srinivasan 			pb[0].len;
24299e3fcfaSHaiyang Zhang 	}
24399e3fcfaSHaiyang Zhang 
2443d541ac5SVitaly Kuznetsov 	ret = netvsc_send(net_device_ctx->device_ctx, packet, NULL, &pb, NULL);
24595fa0405SHaiyang Zhang 	return ret;
24695fa0405SHaiyang Zhang }
24795fa0405SHaiyang Zhang 
2481b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2491b07da51SHaiyang Zhang 				 struct rndis_request *request)
2501b07da51SHaiyang Zhang {
2511b07da51SHaiyang Zhang 	u32 link_status;
2521b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2531b07da51SHaiyang Zhang 
2541b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2551b07da51SHaiyang Zhang 
2561b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2571b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2581b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2591b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2601b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2611b07da51SHaiyang Zhang 	}
2621b07da51SHaiyang Zhang }
2631b07da51SHaiyang Zhang 
26495fa0405SHaiyang Zhang static void rndis_filter_receive_response(struct rndis_device *dev,
26595fa0405SHaiyang Zhang 				       struct rndis_message *resp)
26695fa0405SHaiyang Zhang {
26795fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
26895fa0405SHaiyang Zhang 	bool found = false;
26995fa0405SHaiyang Zhang 	unsigned long flags;
2703d541ac5SVitaly Kuznetsov 	struct net_device *ndev = dev->ndev;
27195fa0405SHaiyang Zhang 
27295fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
27395fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
27495fa0405SHaiyang Zhang 		/*
27595fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
27695fa0405SHaiyang Zhang 		 * field
27795fa0405SHaiyang Zhang 		 */
27895fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
27995fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
28095fa0405SHaiyang Zhang 			found = true;
28195fa0405SHaiyang Zhang 			break;
28295fa0405SHaiyang Zhang 		}
28395fa0405SHaiyang Zhang 	}
28495fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
28595fa0405SHaiyang Zhang 
28695fa0405SHaiyang Zhang 	if (found) {
287a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
288a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
28995fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
29095fa0405SHaiyang Zhang 			       resp->msg_len);
2911b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
2921b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
2931b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
2941b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
29595fa0405SHaiyang Zhang 		} else {
29695fa0405SHaiyang Zhang 			netdev_err(ndev,
29795fa0405SHaiyang Zhang 				"rndis response buffer overflow "
29895fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
29995fa0405SHaiyang Zhang 				resp->msg_len,
30086eedaccSKY Srinivasan 				sizeof(struct rndis_message));
30195fa0405SHaiyang Zhang 
30295fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
30351491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
30495fa0405SHaiyang Zhang 				/* does not have a request id field */
30595fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
306007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
30795fa0405SHaiyang Zhang 			} else {
30895fa0405SHaiyang Zhang 				request->response_msg.msg.
30995fa0405SHaiyang Zhang 				init_complete.status =
310007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
31195fa0405SHaiyang Zhang 			}
31295fa0405SHaiyang Zhang 		}
31395fa0405SHaiyang Zhang 
31495fa0405SHaiyang Zhang 		complete(&request->wait_event);
31595fa0405SHaiyang Zhang 	} else {
31695fa0405SHaiyang Zhang 		netdev_err(ndev,
31795fa0405SHaiyang Zhang 			"no rndis request found for this response "
31895fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
31995fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
32095fa0405SHaiyang Zhang 			resp->ndis_msg_type);
32195fa0405SHaiyang Zhang 	}
32295fa0405SHaiyang Zhang }
32395fa0405SHaiyang Zhang 
3241f5f3a75SHaiyang Zhang /*
3251f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3261f5f3a75SHaiyang Zhang  * return NULL if not found.
3271f5f3a75SHaiyang Zhang  */
3281f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
3291f5f3a75SHaiyang Zhang {
3301f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3311f5f3a75SHaiyang Zhang 	int len;
3321f5f3a75SHaiyang Zhang 
3331f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3341f5f3a75SHaiyang Zhang 		return NULL;
3351f5f3a75SHaiyang Zhang 
3361f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3371f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3381f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3391f5f3a75SHaiyang Zhang 
3401f5f3a75SHaiyang Zhang 	while (len > 0) {
3411f5f3a75SHaiyang Zhang 		if (ppi->type == type)
3421f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3431f5f3a75SHaiyang Zhang 		len -= ppi->size;
3441f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3451f5f3a75SHaiyang Zhang 	}
3461f5f3a75SHaiyang Zhang 
3471f5f3a75SHaiyang Zhang 	return NULL;
3481f5f3a75SHaiyang Zhang }
3491f5f3a75SHaiyang Zhang 
35010082f98SKY Srinivasan static int rndis_filter_receive_data(struct rndis_device *dev,
35195fa0405SHaiyang Zhang 				   struct rndis_message *msg,
35225b85ee8SKY Srinivasan 				   struct hv_netvsc_packet *pkt,
353c4b20c63SKY Srinivasan 				   void **data,
35425b85ee8SKY Srinivasan 				   struct vmbus_channel *channel)
35595fa0405SHaiyang Zhang {
35695fa0405SHaiyang Zhang 	struct rndis_packet *rndis_pkt;
35795fa0405SHaiyang Zhang 	u32 data_offset;
3581f5f3a75SHaiyang Zhang 	struct ndis_pkt_8021q_info *vlan;
359e3d605edSKY Srinivasan 	struct ndis_tcp_ip_checksum_info *csum_info;
360760d1e36SKY Srinivasan 	u16 vlan_tci = 0;
3613d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
36295fa0405SHaiyang Zhang 
36395fa0405SHaiyang Zhang 	rndis_pkt = &msg->msg.pkt;
36495fa0405SHaiyang Zhang 
36595fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
36695fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
36795fa0405SHaiyang Zhang 
36895fa0405SHaiyang Zhang 	pkt->total_data_buflen -= data_offset;
3694b8a8bc9SWei Yongjun 
3704b8a8bc9SWei Yongjun 	/*
3714b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
3724b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
3734b8a8bc9SWei Yongjun 	 */
3744b8a8bc9SWei Yongjun 	if (pkt->total_data_buflen < rndis_pkt->data_len) {
3753d541ac5SVitaly Kuznetsov 		netdev_err(dev->ndev, "rndis message buffer "
3764b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
3774b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
3784b8a8bc9SWei Yongjun 			   pkt->total_data_buflen, rndis_pkt->data_len);
37910082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
3804b8a8bc9SWei Yongjun 	}
3814b8a8bc9SWei Yongjun 
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 	 */
3874b8a8bc9SWei Yongjun 	pkt->total_data_buflen = rndis_pkt->data_len;
388c4b20c63SKY Srinivasan 	*data = (void *)((unsigned long)(*data) + data_offset);
38995fa0405SHaiyang Zhang 
3901f5f3a75SHaiyang Zhang 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
3911f5f3a75SHaiyang Zhang 	if (vlan) {
392760d1e36SKY Srinivasan 		vlan_tci = VLAN_TAG_PRESENT | vlan->vlanid |
3931f5f3a75SHaiyang Zhang 			(vlan->pri << VLAN_PRIO_SHIFT);
3941f5f3a75SHaiyang Zhang 	}
3951f5f3a75SHaiyang Zhang 
396e3d605edSKY Srinivasan 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
3973d541ac5SVitaly Kuznetsov 	return netvsc_recv_callback(net_device_ctx->device_ctx, pkt, data,
398760d1e36SKY Srinivasan 				    csum_info, channel, vlan_tci);
39995fa0405SHaiyang Zhang }
40095fa0405SHaiyang Zhang 
40195fa0405SHaiyang Zhang int rndis_filter_receive(struct hv_device *dev,
40225b85ee8SKY Srinivasan 				struct hv_netvsc_packet	*pkt,
403c4b20c63SKY Srinivasan 				void **data,
40425b85ee8SKY Srinivasan 				struct vmbus_channel *channel)
40595fa0405SHaiyang Zhang {
4063d541ac5SVitaly Kuznetsov 	struct net_device *ndev = hv_get_drvdata(dev);
4073d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
4083d541ac5SVitaly Kuznetsov 	struct netvsc_device *net_dev = net_device_ctx->nvdev;
40995fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev;
410ef31bef6SHaiyang Zhang 	struct rndis_message *rndis_msg;
41163f6921dSHaiyang Zhang 	int ret = 0;
41295fa0405SHaiyang Zhang 
41363f6921dSHaiyang Zhang 	if (!net_dev) {
41410082f98SKY Srinivasan 		ret = NVSP_STAT_FAIL;
41563f6921dSHaiyang Zhang 		goto exit;
41663f6921dSHaiyang Zhang 	}
41795fa0405SHaiyang Zhang 
41895fa0405SHaiyang Zhang 	/* Make sure the rndis device state is initialized */
41995fa0405SHaiyang Zhang 	if (!net_dev->extension) {
42095fa0405SHaiyang Zhang 		netdev_err(ndev, "got rndis message but no rndis device - "
42195fa0405SHaiyang Zhang 			  "dropping this message!\n");
42210082f98SKY Srinivasan 		ret = NVSP_STAT_FAIL;
42363f6921dSHaiyang Zhang 		goto exit;
42495fa0405SHaiyang Zhang 	}
42595fa0405SHaiyang Zhang 
42695fa0405SHaiyang Zhang 	rndis_dev = (struct rndis_device *)net_dev->extension;
42795fa0405SHaiyang Zhang 	if (rndis_dev->state == RNDIS_DEV_UNINITIALIZED) {
42895fa0405SHaiyang Zhang 		netdev_err(ndev, "got rndis message but rndis device "
42995fa0405SHaiyang Zhang 			   "uninitialized...dropping this message!\n");
43010082f98SKY Srinivasan 		ret = NVSP_STAT_FAIL;
43163f6921dSHaiyang Zhang 		goto exit;
43295fa0405SHaiyang Zhang 	}
43395fa0405SHaiyang Zhang 
434c4b20c63SKY Srinivasan 	rndis_msg = *data;
43595fa0405SHaiyang Zhang 
4363d541ac5SVitaly Kuznetsov 	if (netif_msg_rx_err(net_device_ctx))
437ef31bef6SHaiyang Zhang 		dump_rndis_message(dev, rndis_msg);
43895fa0405SHaiyang Zhang 
439ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
44051491167SLinus Walleij 	case RNDIS_MSG_PACKET:
44195fa0405SHaiyang Zhang 		/* data msg */
44210082f98SKY Srinivasan 		ret = rndis_filter_receive_data(rndis_dev, rndis_msg, pkt,
443c4b20c63SKY Srinivasan 						data, channel);
44495fa0405SHaiyang Zhang 		break;
44595fa0405SHaiyang Zhang 
44651491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
44751491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
44851491167SLinus Walleij 	case RNDIS_MSG_SET_C:
44995fa0405SHaiyang Zhang 		/* completion msgs */
450ef31bef6SHaiyang Zhang 		rndis_filter_receive_response(rndis_dev, rndis_msg);
45195fa0405SHaiyang Zhang 		break;
45295fa0405SHaiyang Zhang 
45351491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
45495fa0405SHaiyang Zhang 		/* notification msgs */
4553a494e71SHaiyang Zhang 		netvsc_linkstatus_callback(dev, rndis_msg);
45695fa0405SHaiyang Zhang 		break;
45795fa0405SHaiyang Zhang 	default:
45895fa0405SHaiyang Zhang 		netdev_err(ndev,
45995fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
460ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
461ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
46295fa0405SHaiyang Zhang 		break;
46395fa0405SHaiyang Zhang 	}
46495fa0405SHaiyang Zhang 
46563f6921dSHaiyang Zhang exit:
46663f6921dSHaiyang Zhang 	return ret;
46795fa0405SHaiyang Zhang }
46895fa0405SHaiyang Zhang 
46995fa0405SHaiyang Zhang static int rndis_filter_query_device(struct rndis_device *dev, u32 oid,
47095fa0405SHaiyang Zhang 				  void *result, u32 *result_size)
47195fa0405SHaiyang Zhang {
47295fa0405SHaiyang Zhang 	struct rndis_request *request;
47395fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
47495fa0405SHaiyang Zhang 	struct rndis_query_request *query;
47595fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
47695fa0405SHaiyang Zhang 	int ret = 0;
47795fa0405SHaiyang Zhang 
47895fa0405SHaiyang Zhang 	if (!result)
47995fa0405SHaiyang Zhang 		return -EINVAL;
48095fa0405SHaiyang Zhang 
48195fa0405SHaiyang Zhang 	*result_size = 0;
48251491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
48395fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
48495fa0405SHaiyang Zhang 	if (!request) {
48595fa0405SHaiyang Zhang 		ret = -ENOMEM;
48695fa0405SHaiyang Zhang 		goto cleanup;
48795fa0405SHaiyang Zhang 	}
48895fa0405SHaiyang Zhang 
48995fa0405SHaiyang Zhang 	/* Setup the rndis query */
49095fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
49195fa0405SHaiyang Zhang 	query->oid = oid;
49295fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
49395fa0405SHaiyang Zhang 	query->info_buflen = 0;
49495fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
49595fa0405SHaiyang Zhang 
49623312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
49723312a3bSstephen hemminger 		struct net_device_context *ndevctx = netdev_priv(dev->ndev);
49823312a3bSstephen hemminger 		struct netvsc_device *nvdev = ndevctx->nvdev;
49923312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
50023312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
50123312a3bSstephen hemminger 		u8 ndis_rev;
50223312a3bSstephen hemminger 		size_t size;
50323312a3bSstephen hemminger 
50423312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
50523312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
50623312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
50723312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
50823312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
50923312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
51023312a3bSstephen hemminger 		} else {
51123312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
51223312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
51323312a3bSstephen hemminger 		}
51423312a3bSstephen hemminger 
51523312a3bSstephen hemminger 		request->request_msg.msg_len += size;
51623312a3bSstephen hemminger 		query->info_buflen = size;
51723312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
51823312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
51923312a3bSstephen hemminger 
52023312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
52123312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
52223312a3bSstephen hemminger 		hwcaps->header.size = size;
52323312a3bSstephen hemminger 
52423312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
5255b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
5265b54dac8SHaiyang Zhang 
5275b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
5285b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
5295b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
5305b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5315b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5325b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5335b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5345b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5355b54dac8SHaiyang Zhang 	}
5365b54dac8SHaiyang Zhang 
53795fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
53895fa0405SHaiyang Zhang 	if (ret != 0)
53995fa0405SHaiyang Zhang 		goto cleanup;
54095fa0405SHaiyang Zhang 
5415362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
54295fa0405SHaiyang Zhang 
54395fa0405SHaiyang Zhang 	/* Copy the response back */
54495fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
54595fa0405SHaiyang Zhang 
54695fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
54795fa0405SHaiyang Zhang 		ret = -1;
54895fa0405SHaiyang Zhang 		goto cleanup;
54995fa0405SHaiyang Zhang 	}
55095fa0405SHaiyang Zhang 
55195fa0405SHaiyang Zhang 	memcpy(result,
55295fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
55395fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
55495fa0405SHaiyang Zhang 	       query_complete->info_buflen);
55595fa0405SHaiyang Zhang 
55695fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
55795fa0405SHaiyang Zhang 
55895fa0405SHaiyang Zhang cleanup:
55995fa0405SHaiyang Zhang 	if (request)
56095fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
56195fa0405SHaiyang Zhang 
56295fa0405SHaiyang Zhang 	return ret;
56395fa0405SHaiyang Zhang }
56495fa0405SHaiyang Zhang 
56523312a3bSstephen hemminger /* Get the hardware offload capabilities */
56623312a3bSstephen hemminger static int
56723312a3bSstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct ndis_offload *caps)
56823312a3bSstephen hemminger {
56923312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
57023312a3bSstephen hemminger 	int ret;
57123312a3bSstephen hemminger 
57223312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
57323312a3bSstephen hemminger 
57423312a3bSstephen hemminger 	ret = rndis_filter_query_device(dev,
57523312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
57623312a3bSstephen hemminger 					caps, &caps_len);
57723312a3bSstephen hemminger 	if (ret)
57823312a3bSstephen hemminger 		return ret;
57923312a3bSstephen hemminger 
58023312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
58123312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
58223312a3bSstephen hemminger 			    caps->header.type);
58323312a3bSstephen hemminger 		return -EINVAL;
58423312a3bSstephen hemminger 	}
58523312a3bSstephen hemminger 
58623312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
58723312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
58823312a3bSstephen hemminger 			    caps->header.revision);
58923312a3bSstephen hemminger 		return -EINVAL;
59023312a3bSstephen hemminger 	}
59123312a3bSstephen hemminger 
59223312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
59323312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
59423312a3bSstephen hemminger 		netdev_warn(dev->ndev,
59523312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
59623312a3bSstephen hemminger 			    caps->header.size, caps_len);
59723312a3bSstephen hemminger 		return -EINVAL;
59823312a3bSstephen hemminger 	}
59923312a3bSstephen hemminger 
60023312a3bSstephen hemminger 	return 0;
60123312a3bSstephen hemminger }
60223312a3bSstephen hemminger 
60395fa0405SHaiyang Zhang static int rndis_filter_query_device_mac(struct rndis_device *dev)
60495fa0405SHaiyang Zhang {
60595fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
60695fa0405SHaiyang Zhang 
60795fa0405SHaiyang Zhang 	return rndis_filter_query_device(dev,
60895fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
60995fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
61095fa0405SHaiyang Zhang }
61195fa0405SHaiyang Zhang 
6121ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
6131ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
6141ce09e89SHaiyang Zhang 
615e834da9aSVitaly Kuznetsov int rndis_filter_set_device_mac(struct net_device *ndev, char *mac)
6161ce09e89SHaiyang Zhang {
6172625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev);
6181ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
6191ce09e89SHaiyang Zhang 	struct rndis_request *request;
6201ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
6211ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
6221ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
6231ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
6241ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
6251ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
6261ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
627999028ccSNicholas Mc Guire 	int ret;
6281ce09e89SHaiyang Zhang 
6291ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6301ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6311ce09e89SHaiyang Zhang 	if (!request)
6321ce09e89SHaiyang Zhang 		return -ENOMEM;
6331ce09e89SHaiyang Zhang 
6341ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
6351ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
6361ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
6371ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
6381ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
6391ce09e89SHaiyang Zhang 
6401ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
6411ce09e89SHaiyang Zhang 		set->info_buf_offset);
6421ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
6431ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
6441ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
6451ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
6461ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
6471ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
6481ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
6491ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
6501ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
6511ce09e89SHaiyang Zhang 
6521ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
6531ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
6541ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
6551ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
6561ce09e89SHaiyang Zhang 	if (ret < 0)
6571ce09e89SHaiyang Zhang 		goto cleanup;
6581ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
6591ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
6601ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
6611ce09e89SHaiyang Zhang 	if (ret < 0)
6621ce09e89SHaiyang Zhang 		goto cleanup;
6631ce09e89SHaiyang Zhang 
6641ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
6651ce09e89SHaiyang Zhang 	if (ret != 0)
6661ce09e89SHaiyang Zhang 		goto cleanup;
6671ce09e89SHaiyang Zhang 
6685362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
6695362855aSVitaly Kuznetsov 
6701ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
671b02a8067SHaiyang Zhang 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
672b02a8067SHaiyang Zhang 		netdev_err(ndev, "Fail to set MAC on host side:0x%x\n",
673b02a8067SHaiyang Zhang 			   set_complete->status);
6741ce09e89SHaiyang Zhang 		ret = -EINVAL;
6751ce09e89SHaiyang Zhang 	}
6761ce09e89SHaiyang Zhang 
6771ce09e89SHaiyang Zhang cleanup:
6781ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
6791ce09e89SHaiyang Zhang 	return ret;
6801ce09e89SHaiyang Zhang }
6811ce09e89SHaiyang Zhang 
682da19fcd0SLad, Prabhakar static int
683426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
6844a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
6854a0e70aeSKY Srinivasan {
6862625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(ndev);
6874a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
6884a0e70aeSKY Srinivasan 	struct rndis_request *request;
6894a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
6904a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
6914a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
6924a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
693999028ccSNicholas Mc Guire 	int ret;
694af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
695af9893a3SKY Srinivasan 
696af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
697af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
698af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
699af9893a3SKY Srinivasan 		 * UDP checksum offload.
700af9893a3SKY Srinivasan 		 */
701af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
702af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
703af9893a3SKY Srinivasan 	}
7044a0e70aeSKY Srinivasan 
7054a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
7064a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7074a0e70aeSKY Srinivasan 	if (!request)
7084a0e70aeSKY Srinivasan 		return -ENOMEM;
7094a0e70aeSKY Srinivasan 
7104a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
7114a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
7124a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
7134a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
7144a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
7154a0e70aeSKY Srinivasan 
7164a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
7174a0e70aeSKY Srinivasan 				set->info_buf_offset);
7184a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
7194a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
7204a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
7214a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
7224a0e70aeSKY Srinivasan 
7234a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
7244a0e70aeSKY Srinivasan 	if (ret != 0)
7254a0e70aeSKY Srinivasan 		goto cleanup;
7264a0e70aeSKY Srinivasan 
7275362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7284a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
7294a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
730af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
7314a0e70aeSKY Srinivasan 			   set_complete->status);
7324a0e70aeSKY Srinivasan 		ret = -EINVAL;
7334a0e70aeSKY Srinivasan 	}
7344a0e70aeSKY Srinivasan 
7354a0e70aeSKY Srinivasan cleanup:
7364a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
7374a0e70aeSKY Srinivasan 	return ret;
7384a0e70aeSKY Srinivasan }
7391ce09e89SHaiyang Zhang 
740962f3feeSstephen hemminger int rndis_filter_set_rss_param(struct rndis_device *rdev,
741962f3feeSstephen hemminger 			       const u8 *rss_key, int num_queue)
7425b54dac8SHaiyang Zhang {
7433d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
7445b54dac8SHaiyang Zhang 	struct rndis_request *request;
7455b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7465b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7475b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
748962f3feeSstephen hemminger 		     4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
7495b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7505b54dac8SHaiyang Zhang 	u32 *itab;
7515b54dac8SHaiyang Zhang 	u8 *keyp;
752999028ccSNicholas Mc Guire 	int i, ret;
7535b54dac8SHaiyang Zhang 
7545b54dac8SHaiyang Zhang 	request = get_rndis_request(
7555b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7565b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7575b54dac8SHaiyang Zhang 	if (!request)
7585b54dac8SHaiyang Zhang 		return -ENOMEM;
7595b54dac8SHaiyang Zhang 
7605b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7615b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7625b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
7635b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
7645b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
7655b54dac8SHaiyang Zhang 
7665b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
7675b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
7685b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
7695b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
7705b54dac8SHaiyang Zhang 	rssp->flag = 0;
7715b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
7724c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
7734c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
7745b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
7755b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
776962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
7775b54dac8SHaiyang Zhang 	rssp->kashkey_offset = rssp->indirect_taboffset +
7785b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
7795b54dac8SHaiyang Zhang 
7805b54dac8SHaiyang Zhang 	/* Set indirection table entries */
7815b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
7825b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
7835b54dac8SHaiyang Zhang 		itab[i] = i % num_queue;
7845b54dac8SHaiyang Zhang 
7855b54dac8SHaiyang Zhang 	/* Set hask key values */
7865b54dac8SHaiyang Zhang 	keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
787962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
7885b54dac8SHaiyang Zhang 
7895b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7905b54dac8SHaiyang Zhang 	if (ret != 0)
7915b54dac8SHaiyang Zhang 		goto cleanup;
7925b54dac8SHaiyang Zhang 
7935362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7945b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
795962f3feeSstephen hemminger 	if (set_complete->status == RNDIS_STATUS_SUCCESS)
796962f3feeSstephen hemminger 		memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
797962f3feeSstephen hemminger 	else {
7985b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
7995b54dac8SHaiyang Zhang 			   set_complete->status);
8005b54dac8SHaiyang Zhang 		ret = -EINVAL;
8015b54dac8SHaiyang Zhang 	}
8025b54dac8SHaiyang Zhang 
8035b54dac8SHaiyang Zhang cleanup:
8045b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
8055b54dac8SHaiyang Zhang 	return ret;
8065b54dac8SHaiyang Zhang }
8075b54dac8SHaiyang Zhang 
80895fa0405SHaiyang Zhang static int rndis_filter_query_device_link_status(struct rndis_device *dev)
80995fa0405SHaiyang Zhang {
81095fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
81195fa0405SHaiyang Zhang 	u32 link_status;
81295fa0405SHaiyang Zhang 	int ret;
81395fa0405SHaiyang Zhang 
81495fa0405SHaiyang Zhang 	ret = rndis_filter_query_device(dev,
81595fa0405SHaiyang Zhang 				      RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
81695fa0405SHaiyang Zhang 				      &link_status, &size);
81795fa0405SHaiyang Zhang 
81895fa0405SHaiyang Zhang 	return ret;
81995fa0405SHaiyang Zhang }
82095fa0405SHaiyang Zhang 
821b37879e6SHaiyang Zhang static int rndis_filter_query_link_speed(struct rndis_device *dev)
822b37879e6SHaiyang Zhang {
823b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
824b37879e6SHaiyang Zhang 	u32 link_speed;
825b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
826b37879e6SHaiyang Zhang 	int ret;
827b37879e6SHaiyang Zhang 
828b37879e6SHaiyang Zhang 	ret = rndis_filter_query_device(dev, RNDIS_OID_GEN_LINK_SPEED,
829b37879e6SHaiyang Zhang 					&link_speed, &size);
830b37879e6SHaiyang Zhang 
831b37879e6SHaiyang Zhang 	if (!ret) {
832b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
833b37879e6SHaiyang Zhang 
834b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
835b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
836b37879e6SHaiyang Zhang 		 */
837b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
838b37879e6SHaiyang Zhang 	}
839b37879e6SHaiyang Zhang 
840b37879e6SHaiyang Zhang 	return ret;
841b37879e6SHaiyang Zhang }
842b37879e6SHaiyang Zhang 
843d426b2e3SHaiyang Zhang int rndis_filter_set_packet_filter(struct rndis_device *dev, u32 new_filter)
84495fa0405SHaiyang Zhang {
84595fa0405SHaiyang Zhang 	struct rndis_request *request;
84695fa0405SHaiyang Zhang 	struct rndis_set_request *set;
84795fa0405SHaiyang Zhang 	struct rndis_set_complete *set_complete;
84895fa0405SHaiyang Zhang 	u32 status;
849999028ccSNicholas Mc Guire 	int ret;
85095fa0405SHaiyang Zhang 
85151491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
85295fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
85395fa0405SHaiyang Zhang 			sizeof(u32));
85495fa0405SHaiyang Zhang 	if (!request) {
85595fa0405SHaiyang Zhang 		ret = -ENOMEM;
85695fa0405SHaiyang Zhang 		goto cleanup;
85795fa0405SHaiyang Zhang 	}
85895fa0405SHaiyang Zhang 
85995fa0405SHaiyang Zhang 	/* Setup the rndis set */
86095fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
86195fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
86295fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
86395fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
86495fa0405SHaiyang Zhang 
86595fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
86695fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
86795fa0405SHaiyang Zhang 
86895fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
86995fa0405SHaiyang Zhang 	if (ret != 0)
87095fa0405SHaiyang Zhang 		goto cleanup;
87195fa0405SHaiyang Zhang 
8725362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
87395fa0405SHaiyang Zhang 
87495fa0405SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
87595fa0405SHaiyang Zhang 	status = set_complete->status;
87695fa0405SHaiyang Zhang 
87795fa0405SHaiyang Zhang cleanup:
87895fa0405SHaiyang Zhang 	if (request)
87995fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
88095fa0405SHaiyang Zhang 	return ret;
88195fa0405SHaiyang Zhang }
88295fa0405SHaiyang Zhang 
88395fa0405SHaiyang Zhang static int rndis_filter_init_device(struct rndis_device *dev)
88495fa0405SHaiyang Zhang {
88595fa0405SHaiyang Zhang 	struct rndis_request *request;
88695fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
88795fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
88895fa0405SHaiyang Zhang 	u32 status;
889999028ccSNicholas Mc Guire 	int ret;
8902625466dSVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_to_netvsc_device(dev->ndev);
89195fa0405SHaiyang Zhang 
89251491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
89395fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
89495fa0405SHaiyang Zhang 	if (!request) {
89595fa0405SHaiyang Zhang 		ret = -ENOMEM;
89695fa0405SHaiyang Zhang 		goto cleanup;
89795fa0405SHaiyang Zhang 	}
89895fa0405SHaiyang Zhang 
89995fa0405SHaiyang Zhang 	/* Setup the rndis set */
90095fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
90195fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
90295fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
903fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
90495fa0405SHaiyang Zhang 
90595fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
90695fa0405SHaiyang Zhang 
90795fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
90895fa0405SHaiyang Zhang 	if (ret != 0) {
90995fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
91095fa0405SHaiyang Zhang 		goto cleanup;
91195fa0405SHaiyang Zhang 	}
91295fa0405SHaiyang Zhang 
9135362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
91495fa0405SHaiyang Zhang 
91595fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
91695fa0405SHaiyang Zhang 	status = init_complete->status;
91795fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
91895fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
9197c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
9207c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
92195fa0405SHaiyang Zhang 		ret = 0;
92295fa0405SHaiyang Zhang 	} else {
92395fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
92495fa0405SHaiyang Zhang 		ret = -EINVAL;
92595fa0405SHaiyang Zhang 	}
92695fa0405SHaiyang Zhang 
92795fa0405SHaiyang Zhang cleanup:
92895fa0405SHaiyang Zhang 	if (request)
92995fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
93095fa0405SHaiyang Zhang 
93195fa0405SHaiyang Zhang 	return ret;
93295fa0405SHaiyang Zhang }
93395fa0405SHaiyang Zhang 
93495fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev)
93595fa0405SHaiyang Zhang {
93695fa0405SHaiyang Zhang 	struct rndis_request *request;
93795fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
9383d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
9393d541ac5SVitaly Kuznetsov 	struct netvsc_device *nvdev = net_device_ctx->nvdev;
9403d541ac5SVitaly Kuznetsov 	struct hv_device *hdev = net_device_ctx->device_ctx;
941ae9e63bbSHaiyang Zhang 	ulong flags;
94295fa0405SHaiyang Zhang 
94395fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
94451491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
94595fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
94695fa0405SHaiyang Zhang 	if (!request)
94795fa0405SHaiyang Zhang 		goto cleanup;
94895fa0405SHaiyang Zhang 
94995fa0405SHaiyang Zhang 	/* Setup the rndis set */
95095fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
95195fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
95295fa0405SHaiyang Zhang 
95395fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
95495fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
95595fa0405SHaiyang Zhang 
95695fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
95795fa0405SHaiyang Zhang 
95895fa0405SHaiyang Zhang cleanup:
959ae9e63bbSHaiyang Zhang 	spin_lock_irqsave(&hdev->channel->inbound_lock, flags);
960ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
961ae9e63bbSHaiyang Zhang 	spin_unlock_irqrestore(&hdev->channel->inbound_lock, flags);
962ae9e63bbSHaiyang Zhang 
963ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
964ae9e63bbSHaiyang Zhang 	wait_event(nvdev->wait_drain,
965c0b558e5SHaiyang Zhang 		   atomic_read(&nvdev->num_outstanding_sends) == 0 &&
966c0b558e5SHaiyang Zhang 		   atomic_read(&nvdev->num_outstanding_recvs) == 0);
967ae9e63bbSHaiyang Zhang 
96895fa0405SHaiyang Zhang 	if (request)
96995fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
97095fa0405SHaiyang Zhang }
97195fa0405SHaiyang Zhang 
97295fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
97395fa0405SHaiyang Zhang {
97495fa0405SHaiyang Zhang 	int ret;
97595fa0405SHaiyang Zhang 
97695fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
97795fa0405SHaiyang Zhang 		return 0;
97895fa0405SHaiyang Zhang 
97995fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
98095fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
98195fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
98295fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
98395fa0405SHaiyang Zhang 	if (ret == 0)
98495fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
98595fa0405SHaiyang Zhang 
98695fa0405SHaiyang Zhang 	return ret;
98795fa0405SHaiyang Zhang }
98895fa0405SHaiyang Zhang 
98995fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
99095fa0405SHaiyang Zhang {
99195fa0405SHaiyang Zhang 	int ret;
99295fa0405SHaiyang Zhang 
99395fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
99495fa0405SHaiyang Zhang 		return 0;
99595fa0405SHaiyang Zhang 
99695fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
997c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
998c3582a2cSHaiyang Zhang 		ret = 0;
999c3582a2cSHaiyang Zhang 
100095fa0405SHaiyang Zhang 	if (ret == 0)
100195fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
100295fa0405SHaiyang Zhang 
100395fa0405SHaiyang Zhang 	return ret;
100495fa0405SHaiyang Zhang }
100595fa0405SHaiyang Zhang 
10065b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
10075b54dac8SHaiyang Zhang {
10083d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
10093d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
10102625466dSVitaly Kuznetsov 	struct netvsc_device *nvscdev = net_device_to_netvsc_device(ndev);
10115b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
10125b54dac8SHaiyang Zhang 	int ret;
1013b3e6b82aSKY Srinivasan 	unsigned long flags;
10145b54dac8SHaiyang Zhang 
10155b54dac8SHaiyang Zhang 	if (chn_index >= nvscdev->num_chn)
10165b54dac8SHaiyang Zhang 		return;
10175b54dac8SHaiyang Zhang 
1018c0b558e5SHaiyang Zhang 	nvscdev->mrc[chn_index].buf = vzalloc(NETVSC_RECVSLOT_MAX *
1019c0b558e5SHaiyang Zhang 					      sizeof(struct recv_comp_data));
1020c0b558e5SHaiyang Zhang 
10215b54dac8SHaiyang Zhang 	ret = vmbus_open(new_sc, nvscdev->ring_size * PAGE_SIZE,
10225b54dac8SHaiyang Zhang 			 nvscdev->ring_size * PAGE_SIZE, NULL, 0,
10235b54dac8SHaiyang Zhang 			 netvsc_channel_cb, new_sc);
10245b54dac8SHaiyang Zhang 
10255b54dac8SHaiyang Zhang 	if (ret == 0)
10265b54dac8SHaiyang Zhang 		nvscdev->chn_table[chn_index] = new_sc;
10273f735131SHaiyang Zhang 
10283f735131SHaiyang Zhang 	spin_lock_irqsave(&nvscdev->sc_lock, flags);
10293f735131SHaiyang Zhang 	nvscdev->num_sc_offered--;
10303f735131SHaiyang Zhang 	spin_unlock_irqrestore(&nvscdev->sc_lock, flags);
10313f735131SHaiyang Zhang 	if (nvscdev->num_sc_offered == 0)
10323f735131SHaiyang Zhang 		complete(&nvscdev->channel_init_wait);
10335b54dac8SHaiyang Zhang }
10345b54dac8SHaiyang Zhang 
103595fa0405SHaiyang Zhang int rndis_filter_device_add(struct hv_device *dev,
103695fa0405SHaiyang Zhang 			    void *additional_info)
103795fa0405SHaiyang Zhang {
103895fa0405SHaiyang Zhang 	int ret;
10393d541ac5SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
10403d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
104195fa0405SHaiyang Zhang 	struct netvsc_device *net_device;
104295fa0405SHaiyang Zhang 	struct rndis_device *rndis_device;
104395fa0405SHaiyang Zhang 	struct netvsc_device_info *device_info = additional_info;
104423312a3bSstephen hemminger 	struct ndis_offload hwcaps;
10454a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
10465b54dac8SHaiyang Zhang 	struct nvsp_message *init_packet;
10475b54dac8SHaiyang Zhang 	struct ndis_recv_scale_cap rsscap;
10485b54dac8SHaiyang Zhang 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
104923312a3bSstephen hemminger 	unsigned int gso_max_size = GSO_MAX_SIZE;
10504d3c9d37SHaiyang Zhang 	u32 mtu, size;
1051e01ec219SKY Srinivasan 	u32 num_rss_qs;
1052b3e6b82aSKY Srinivasan 	u32 sc_delta;
1053e01ec219SKY Srinivasan 	const struct cpumask *node_cpu_mask;
1054e01ec219SKY Srinivasan 	u32 num_possible_rss_qs;
1055b3e6b82aSKY Srinivasan 	unsigned long flags;
105695fa0405SHaiyang Zhang 
105795fa0405SHaiyang Zhang 	rndis_device = get_rndis_device();
105895fa0405SHaiyang Zhang 	if (!rndis_device)
105995fa0405SHaiyang Zhang 		return -ENODEV;
106095fa0405SHaiyang Zhang 
106195fa0405SHaiyang Zhang 	/*
106295fa0405SHaiyang Zhang 	 * Let the inner driver handle this first to create the netvsc channel
106395fa0405SHaiyang Zhang 	 * NOTE! Once the channel is created, we may get a receive callback
106495fa0405SHaiyang Zhang 	 * (RndisFilterOnReceive()) before this call is completed
106595fa0405SHaiyang Zhang 	 */
106695fa0405SHaiyang Zhang 	ret = netvsc_device_add(dev, additional_info);
106795fa0405SHaiyang Zhang 	if (ret != 0) {
106895fa0405SHaiyang Zhang 		kfree(rndis_device);
106995fa0405SHaiyang Zhang 		return ret;
107095fa0405SHaiyang Zhang 	}
107195fa0405SHaiyang Zhang 
107295fa0405SHaiyang Zhang 	/* Initialize the rndis device */
10733d541ac5SVitaly Kuznetsov 	net_device = net_device_ctx->nvdev;
107459995370SAndrew Schwartzmeyer 	net_device->max_chn = 1;
10755b54dac8SHaiyang Zhang 	net_device->num_chn = 1;
107695fa0405SHaiyang Zhang 
1077b3e6b82aSKY Srinivasan 	spin_lock_init(&net_device->sc_lock);
1078b3e6b82aSKY Srinivasan 
107995fa0405SHaiyang Zhang 	net_device->extension = rndis_device;
10803d541ac5SVitaly Kuznetsov 	rndis_device->ndev = net;
108195fa0405SHaiyang Zhang 
108295fa0405SHaiyang Zhang 	/* Send the rndis initialization message */
108395fa0405SHaiyang Zhang 	ret = rndis_filter_init_device(rndis_device);
108495fa0405SHaiyang Zhang 	if (ret != 0) {
10855243e7bdSHaiyang Zhang 		rndis_filter_device_remove(dev);
10865243e7bdSHaiyang Zhang 		return ret;
108795fa0405SHaiyang Zhang 	}
108895fa0405SHaiyang Zhang 
10894d3c9d37SHaiyang Zhang 	/* Get the MTU from the host */
10904d3c9d37SHaiyang Zhang 	size = sizeof(u32);
10914d3c9d37SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
10924d3c9d37SHaiyang Zhang 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
10934d3c9d37SHaiyang Zhang 					&mtu, &size);
10940a1275caSVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
10950a1275caSVitaly Kuznetsov 		net->mtu = mtu;
10964d3c9d37SHaiyang Zhang 
109795fa0405SHaiyang Zhang 	/* Get the mac address */
109895fa0405SHaiyang Zhang 	ret = rndis_filter_query_device_mac(rndis_device);
109995fa0405SHaiyang Zhang 	if (ret != 0) {
11005243e7bdSHaiyang Zhang 		rndis_filter_device_remove(dev);
11015243e7bdSHaiyang Zhang 		return ret;
110295fa0405SHaiyang Zhang 	}
110395fa0405SHaiyang Zhang 
110495fa0405SHaiyang Zhang 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
110595fa0405SHaiyang Zhang 
110623312a3bSstephen hemminger 	/* Find HW offload capabilities */
110723312a3bSstephen hemminger 	ret = rndis_query_hwcaps(rndis_device, &hwcaps);
110823312a3bSstephen hemminger 	if (ret != 0) {
110923312a3bSstephen hemminger 		rndis_filter_device_remove(dev);
111023312a3bSstephen hemminger 		return ret;
111123312a3bSstephen hemminger 	}
111223312a3bSstephen hemminger 
111323312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
11144a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
111523312a3bSstephen hemminger 
111623312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
111723312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
111823312a3bSstephen hemminger 
111923312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
112023312a3bSstephen hemminger 	net->hw_features = NETIF_F_RXCSUM;
112123312a3bSstephen hemminger 
112223312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
112323312a3bSstephen hemminger 		/* Can checksum TCP */
112423312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
112523312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
112623312a3bSstephen hemminger 
11274a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
112823312a3bSstephen hemminger 
112923312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
11304a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
113123312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
113223312a3bSstephen hemminger 
113323312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
113423312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
113523312a3bSstephen hemminger 		}
113623312a3bSstephen hemminger 
113723312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
113823312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
113923312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
114023312a3bSstephen hemminger 		}
114123312a3bSstephen hemminger 	}
114223312a3bSstephen hemminger 
114323312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
114423312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
114523312a3bSstephen hemminger 
114623312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
114723312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
114823312a3bSstephen hemminger 
114923312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
115023312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
115123312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
115223312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
115323312a3bSstephen hemminger 
115423312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
115523312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
115623312a3bSstephen hemminger 		}
115723312a3bSstephen hemminger 
115823312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
115923312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
116023312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
116123312a3bSstephen hemminger 		}
116223312a3bSstephen hemminger 	}
116323312a3bSstephen hemminger 
116423312a3bSstephen hemminger 	netif_set_gso_max_size(net, gso_max_size);
11654a0e70aeSKY Srinivasan 
1166426d9541SVitaly Kuznetsov 	ret = rndis_filter_set_offload_params(net, &offloads);
11674a0e70aeSKY Srinivasan 	if (ret)
11684a0e70aeSKY Srinivasan 		goto err_dev_remv;
11694a0e70aeSKY Srinivasan 
117095fa0405SHaiyang Zhang 	rndis_filter_query_device_link_status(rndis_device);
117195fa0405SHaiyang Zhang 
117295fa0405SHaiyang Zhang 	device_info->link_state = rndis_device->link_state;
117395fa0405SHaiyang Zhang 
117493ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
117595fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
117695fa0405SHaiyang Zhang 		   device_info->link_state ? "down" : "up");
117795fa0405SHaiyang Zhang 
11785b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
11795b54dac8SHaiyang Zhang 		return 0;
11805b54dac8SHaiyang Zhang 
1181b37879e6SHaiyang Zhang 	rndis_filter_query_link_speed(rndis_device);
1182b37879e6SHaiyang Zhang 
11835b54dac8SHaiyang Zhang 	/* vRSS setup */
11845b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
11855b54dac8SHaiyang Zhang 	ret = rndis_filter_query_device(rndis_device,
11865b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
11875b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
11885b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
11895b54dac8SHaiyang Zhang 		goto out;
11905b54dac8SHaiyang Zhang 
11919efc2f7dSHaiyang Zhang 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, rsscap.num_recv_que);
1192e01ec219SKY Srinivasan 
11939efc2f7dSHaiyang Zhang 	num_rss_qs = min(device_info->max_num_vrss_chns, net_device->max_chn);
1194e01ec219SKY Srinivasan 
1195e01ec219SKY Srinivasan 	/*
1196e01ec219SKY Srinivasan 	 * We will limit the VRSS channels to the number CPUs in the NUMA node
1197e01ec219SKY Srinivasan 	 * the primary channel is currently bound to.
1198e01ec219SKY Srinivasan 	 */
1199e01ec219SKY Srinivasan 	node_cpu_mask = cpumask_of_node(cpu_to_node(dev->channel->target_cpu));
1200e01ec219SKY Srinivasan 	num_possible_rss_qs = cpumask_weight(node_cpu_mask);
12018ebdcc52SAndrew Schwartzmeyer 
12028ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
12038ebdcc52SAndrew Schwartzmeyer 	if (device_info->num_chn && device_info->num_chn < net_device->max_chn)
12048ebdcc52SAndrew Schwartzmeyer 		net_device->num_chn = device_info->num_chn;
12058ebdcc52SAndrew Schwartzmeyer 	else
1206e01ec219SKY Srinivasan 		net_device->num_chn = min(num_possible_rss_qs, num_rss_qs);
1207e01ec219SKY Srinivasan 
1208b3e6b82aSKY Srinivasan 	num_rss_qs = net_device->num_chn - 1;
1209b3e6b82aSKY Srinivasan 	net_device->num_sc_offered = num_rss_qs;
1210b3e6b82aSKY Srinivasan 
12115b54dac8SHaiyang Zhang 	if (net_device->num_chn == 1)
12125b54dac8SHaiyang Zhang 		goto out;
12135b54dac8SHaiyang Zhang 
12145b54dac8SHaiyang Zhang 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
12155b54dac8SHaiyang Zhang 
12165b54dac8SHaiyang Zhang 	init_packet = &net_device->channel_init_pkt;
12175b54dac8SHaiyang Zhang 	memset(init_packet, 0, sizeof(struct nvsp_message));
12185b54dac8SHaiyang Zhang 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
12195b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
12205b54dac8SHaiyang Zhang 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
12215b54dac8SHaiyang Zhang 						net_device->num_chn - 1;
12225b54dac8SHaiyang Zhang 	ret = vmbus_sendpacket(dev->channel, init_packet,
12235b54dac8SHaiyang Zhang 			       sizeof(struct nvsp_message),
12245b54dac8SHaiyang Zhang 			       (unsigned long)init_packet,
12255b54dac8SHaiyang Zhang 			       VM_PKT_DATA_INBAND,
12265b54dac8SHaiyang Zhang 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
12275b54dac8SHaiyang Zhang 	if (ret)
12285b54dac8SHaiyang Zhang 		goto out;
12295362855aSVitaly Kuznetsov 	wait_for_completion(&net_device->channel_init_wait);
12305362855aSVitaly Kuznetsov 
12315b54dac8SHaiyang Zhang 	if (init_packet->msg.v5_msg.subchn_comp.status !=
12325b54dac8SHaiyang Zhang 	    NVSP_STAT_SUCCESS) {
12335b54dac8SHaiyang Zhang 		ret = -ENODEV;
12345b54dac8SHaiyang Zhang 		goto out;
12355b54dac8SHaiyang Zhang 	}
12365b54dac8SHaiyang Zhang 	net_device->num_chn = 1 +
12375b54dac8SHaiyang Zhang 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
12385b54dac8SHaiyang Zhang 
1239962f3feeSstephen hemminger 	ret = rndis_filter_set_rss_param(rndis_device, netvsc_hash_key,
1240962f3feeSstephen hemminger 					 net_device->num_chn);
12415b54dac8SHaiyang Zhang 
1242b3e6b82aSKY Srinivasan 	/*
1243d66ab514SHaiyang Zhang 	 * Set the number of sub-channels to be received.
1244b3e6b82aSKY Srinivasan 	 */
1245b3e6b82aSKY Srinivasan 	spin_lock_irqsave(&net_device->sc_lock, flags);
1246b3e6b82aSKY Srinivasan 	sc_delta = num_rss_qs - (net_device->num_chn - 1);
1247b3e6b82aSKY Srinivasan 	net_device->num_sc_offered -= sc_delta;
1248b3e6b82aSKY Srinivasan 	spin_unlock_irqrestore(&net_device->sc_lock, flags);
1249b3e6b82aSKY Srinivasan 
12505b54dac8SHaiyang Zhang out:
125159995370SAndrew Schwartzmeyer 	if (ret) {
125259995370SAndrew Schwartzmeyer 		net_device->max_chn = 1;
12535b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
1254d66ab514SHaiyang Zhang 		net_device->num_sc_offered = 0;
125559995370SAndrew Schwartzmeyer 	}
1256b3e6b82aSKY Srinivasan 
12575b54dac8SHaiyang Zhang 	return 0; /* return 0 because primary channel can be used alone */
12584a0e70aeSKY Srinivasan 
12594a0e70aeSKY Srinivasan err_dev_remv:
12604a0e70aeSKY Srinivasan 	rndis_filter_device_remove(dev);
12614a0e70aeSKY Srinivasan 	return ret;
126295fa0405SHaiyang Zhang }
126395fa0405SHaiyang Zhang 
126495fa0405SHaiyang Zhang void rndis_filter_device_remove(struct hv_device *dev)
126595fa0405SHaiyang Zhang {
12662625466dSVitaly Kuznetsov 	struct netvsc_device *net_dev = hv_device_to_netvsc_device(dev);
126795fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1268d66ab514SHaiyang Zhang 
1269d66ab514SHaiyang Zhang 	/* If not all subchannel offers are complete, wait for them until
1270d66ab514SHaiyang Zhang 	 * completion to avoid race.
1271d66ab514SHaiyang Zhang 	 */
12725362855aSVitaly Kuznetsov 	if (net_dev->num_sc_offered > 0)
12735362855aSVitaly Kuznetsov 		wait_for_completion(&net_dev->channel_init_wait);
127495fa0405SHaiyang 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