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>
3127f5aa92Sstephen hemminger #include <linux/rtnetlink.h>
3295fa0405SHaiyang Zhang 
3395fa0405SHaiyang Zhang #include "hyperv_net.h"
3495fa0405SHaiyang Zhang 
354f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w);
3695fa0405SHaiyang Zhang 
375b54dac8SHaiyang Zhang #define RNDIS_EXT_LEN PAGE_SIZE
3895fa0405SHaiyang Zhang struct rndis_request {
3995fa0405SHaiyang Zhang 	struct list_head list_ent;
4095fa0405SHaiyang Zhang 	struct completion  wait_event;
4195fa0405SHaiyang Zhang 
4295fa0405SHaiyang Zhang 	struct rndis_message response_msg;
43a3a6cab5SHaiyang Zhang 	/*
44a3a6cab5SHaiyang Zhang 	 * The buffer for extended info after the RNDIS response message. It's
45a3a6cab5SHaiyang Zhang 	 * referenced based on the data offset in the RNDIS message. Its size
46a3a6cab5SHaiyang Zhang 	 * is enough for current needs, and should be sufficient for the near
47a3a6cab5SHaiyang Zhang 	 * future.
48a3a6cab5SHaiyang Zhang 	 */
49a3a6cab5SHaiyang Zhang 	u8 response_ext[RNDIS_EXT_LEN];
5095fa0405SHaiyang Zhang 
5195fa0405SHaiyang Zhang 	/* Simplify allocation by having a netvsc packet inline */
5295fa0405SHaiyang Zhang 	struct hv_netvsc_packet	pkt;
530f48917bSHaiyang Zhang 
5495fa0405SHaiyang Zhang 	struct rndis_message request_msg;
550f48917bSHaiyang Zhang 	/*
56a3a6cab5SHaiyang Zhang 	 * The buffer for the extended info after the RNDIS request message.
57a3a6cab5SHaiyang Zhang 	 * It is referenced and sized in a similar way as response_ext.
580f48917bSHaiyang Zhang 	 */
59a3a6cab5SHaiyang Zhang 	u8 request_ext[RNDIS_EXT_LEN];
6095fa0405SHaiyang Zhang };
6195fa0405SHaiyang Zhang 
62962f3feeSstephen hemminger static const u8 netvsc_hash_key[NETVSC_HASH_KEYLEN] = {
63962f3feeSstephen hemminger 	0x6d, 0x5a, 0x56, 0xda, 0x25, 0x5b, 0x0e, 0xc2,
64962f3feeSstephen hemminger 	0x41, 0x67, 0x25, 0x3d, 0x43, 0xa3, 0x8f, 0xb0,
65962f3feeSstephen hemminger 	0xd0, 0xca, 0x2b, 0xcb, 0xae, 0x7b, 0x30, 0xb4,
66962f3feeSstephen hemminger 	0x77, 0xcb, 0x2d, 0xa3, 0x80, 0x30, 0xf2, 0x0c,
67962f3feeSstephen hemminger 	0x6a, 0x42, 0xb7, 0x3b, 0xbe, 0xac, 0x01, 0xfa
68962f3feeSstephen hemminger };
69962f3feeSstephen hemminger 
7095fa0405SHaiyang Zhang static struct rndis_device *get_rndis_device(void)
7195fa0405SHaiyang Zhang {
7295fa0405SHaiyang Zhang 	struct rndis_device *device;
7395fa0405SHaiyang Zhang 
7495fa0405SHaiyang Zhang 	device = kzalloc(sizeof(struct rndis_device), GFP_KERNEL);
7595fa0405SHaiyang Zhang 	if (!device)
7695fa0405SHaiyang Zhang 		return NULL;
7795fa0405SHaiyang Zhang 
7895fa0405SHaiyang Zhang 	spin_lock_init(&device->request_lock);
7995fa0405SHaiyang Zhang 
8095fa0405SHaiyang Zhang 	INIT_LIST_HEAD(&device->req_list);
814f19c0d8Sstephen hemminger 	INIT_WORK(&device->mcast_work, rndis_set_multicast);
8295fa0405SHaiyang Zhang 
8395fa0405SHaiyang Zhang 	device->state = RNDIS_DEV_UNINITIALIZED;
8495fa0405SHaiyang Zhang 
8595fa0405SHaiyang Zhang 	return device;
8695fa0405SHaiyang Zhang }
8795fa0405SHaiyang Zhang 
8895fa0405SHaiyang Zhang static struct rndis_request *get_rndis_request(struct rndis_device *dev,
8995fa0405SHaiyang Zhang 					     u32 msg_type,
9095fa0405SHaiyang Zhang 					     u32 msg_len)
9195fa0405SHaiyang Zhang {
9295fa0405SHaiyang Zhang 	struct rndis_request *request;
9395fa0405SHaiyang Zhang 	struct rndis_message *rndis_msg;
9495fa0405SHaiyang Zhang 	struct rndis_set_request *set;
9595fa0405SHaiyang Zhang 	unsigned long flags;
9695fa0405SHaiyang Zhang 
9795fa0405SHaiyang Zhang 	request = kzalloc(sizeof(struct rndis_request), GFP_KERNEL);
9895fa0405SHaiyang Zhang 	if (!request)
9995fa0405SHaiyang Zhang 		return NULL;
10095fa0405SHaiyang Zhang 
10195fa0405SHaiyang Zhang 	init_completion(&request->wait_event);
10295fa0405SHaiyang Zhang 
10395fa0405SHaiyang Zhang 	rndis_msg = &request->request_msg;
10495fa0405SHaiyang Zhang 	rndis_msg->ndis_msg_type = msg_type;
10595fa0405SHaiyang Zhang 	rndis_msg->msg_len = msg_len;
10695fa0405SHaiyang Zhang 
1075b54dac8SHaiyang Zhang 	request->pkt.q_idx = 0;
1085b54dac8SHaiyang Zhang 
10995fa0405SHaiyang Zhang 	/*
11095fa0405SHaiyang Zhang 	 * Set the request id. This field is always after the rndis header for
11195fa0405SHaiyang Zhang 	 * request/response packet types so we just used the SetRequest as a
11295fa0405SHaiyang Zhang 	 * template
11395fa0405SHaiyang Zhang 	 */
11495fa0405SHaiyang Zhang 	set = &rndis_msg->msg.set_req;
11595fa0405SHaiyang Zhang 	set->req_id = atomic_inc_return(&dev->new_req_id);
11695fa0405SHaiyang Zhang 
11795fa0405SHaiyang Zhang 	/* Add to the request list */
11895fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
11995fa0405SHaiyang Zhang 	list_add_tail(&request->list_ent, &dev->req_list);
12095fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
12195fa0405SHaiyang Zhang 
12295fa0405SHaiyang Zhang 	return request;
12395fa0405SHaiyang Zhang }
12495fa0405SHaiyang Zhang 
12595fa0405SHaiyang Zhang static void put_rndis_request(struct rndis_device *dev,
12695fa0405SHaiyang Zhang 			    struct rndis_request *req)
12795fa0405SHaiyang Zhang {
12895fa0405SHaiyang Zhang 	unsigned long flags;
12995fa0405SHaiyang Zhang 
13095fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
13195fa0405SHaiyang Zhang 	list_del(&req->list_ent);
13295fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
13395fa0405SHaiyang Zhang 
13495fa0405SHaiyang Zhang 	kfree(req);
13595fa0405SHaiyang Zhang }
13695fa0405SHaiyang Zhang 
13779cf1baeSStephen Hemminger static void dump_rndis_message(struct net_device *netdev,
138dc54a08cSstephen hemminger 			       const struct rndis_message *rndis_msg)
13995fa0405SHaiyang Zhang {
14095fa0405SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
14151491167SLinus Walleij 	case RNDIS_MSG_PACKET:
14251491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_PACKET (len %u, "
14395fa0405SHaiyang Zhang 			   "data offset %u data len %u, # oob %u, "
14495fa0405SHaiyang Zhang 			   "oob offset %u, oob len %u, pkt offset %u, "
14595fa0405SHaiyang Zhang 			   "pkt len %u\n",
14695fa0405SHaiyang Zhang 			   rndis_msg->msg_len,
14795fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_offset,
14895fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.data_len,
14995fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.num_oob_data_elements,
15095fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_offset,
15195fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.oob_data_len,
15295fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_offset,
15395fa0405SHaiyang Zhang 			   rndis_msg->msg.pkt.per_pkt_info_len);
15495fa0405SHaiyang Zhang 		break;
15595fa0405SHaiyang Zhang 
15651491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
15751491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INIT_C "
15895fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, major %d, minor %d, "
15995fa0405SHaiyang Zhang 			"device flags %d, max xfer size 0x%x, max pkts %u, "
16095fa0405SHaiyang Zhang 			"pkt aligned %u)\n",
16195fa0405SHaiyang Zhang 			rndis_msg->msg_len,
16295fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.req_id,
16395fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.status,
16495fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.major_ver,
16595fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.minor_ver,
16695fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.dev_flags,
16795fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.max_xfer_size,
16895fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
16995fa0405SHaiyang Zhang 			   max_pkt_per_msg,
17095fa0405SHaiyang Zhang 			rndis_msg->msg.init_complete.
17195fa0405SHaiyang Zhang 			   pkt_alignment_factor);
17295fa0405SHaiyang Zhang 		break;
17395fa0405SHaiyang Zhang 
17451491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
17551491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_QUERY_C "
17695fa0405SHaiyang Zhang 			"(len %u, id 0x%x, status 0x%x, buf len %u, "
17795fa0405SHaiyang Zhang 			"buf offset %u)\n",
17895fa0405SHaiyang Zhang 			rndis_msg->msg_len,
17995fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.req_id,
18095fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.status,
18195fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18295fa0405SHaiyang Zhang 			   info_buflen,
18395fa0405SHaiyang Zhang 			rndis_msg->msg.query_complete.
18495fa0405SHaiyang Zhang 			   info_buf_offset);
18595fa0405SHaiyang Zhang 		break;
18695fa0405SHaiyang Zhang 
18751491167SLinus Walleij 	case RNDIS_MSG_SET_C:
18895fa0405SHaiyang Zhang 		netdev_dbg(netdev,
18951491167SLinus Walleij 			"RNDIS_MSG_SET_C (len %u, id 0x%x, status 0x%x)\n",
19095fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19195fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.req_id,
19295fa0405SHaiyang Zhang 			rndis_msg->msg.set_complete.status);
19395fa0405SHaiyang Zhang 		break;
19495fa0405SHaiyang Zhang 
19551491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
19651491167SLinus Walleij 		netdev_dbg(netdev, "RNDIS_MSG_INDICATE "
19795fa0405SHaiyang Zhang 			"(len %u, status 0x%x, buf len %u, buf offset %u)\n",
19895fa0405SHaiyang Zhang 			rndis_msg->msg_len,
19995fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status,
20095fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buflen,
20195fa0405SHaiyang Zhang 			rndis_msg->msg.indicate_status.status_buf_offset);
20295fa0405SHaiyang Zhang 		break;
20395fa0405SHaiyang Zhang 
20495fa0405SHaiyang Zhang 	default:
20595fa0405SHaiyang Zhang 		netdev_dbg(netdev, "0x%x (len %u)\n",
20695fa0405SHaiyang Zhang 			rndis_msg->ndis_msg_type,
20795fa0405SHaiyang Zhang 			rndis_msg->msg_len);
20895fa0405SHaiyang Zhang 		break;
20995fa0405SHaiyang Zhang 	}
21095fa0405SHaiyang Zhang }
21195fa0405SHaiyang Zhang 
21295fa0405SHaiyang Zhang static int rndis_filter_send_request(struct rndis_device *dev,
21395fa0405SHaiyang Zhang 				  struct rndis_request *req)
21495fa0405SHaiyang Zhang {
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;
21802b6de01Sstephen hemminger 	int ret;
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 
244867047c4Sstephen hemminger 	rcu_read_lock_bh();
245cfd8afd9SStephen Hemminger 	ret = netvsc_send(dev->ndev, packet, NULL, pb, NULL);
246867047c4Sstephen hemminger 	rcu_read_unlock_bh();
247867047c4Sstephen hemminger 
24895fa0405SHaiyang Zhang 	return ret;
24995fa0405SHaiyang Zhang }
25095fa0405SHaiyang Zhang 
2511b07da51SHaiyang Zhang static void rndis_set_link_state(struct rndis_device *rdev,
2521b07da51SHaiyang Zhang 				 struct rndis_request *request)
2531b07da51SHaiyang Zhang {
2541b07da51SHaiyang Zhang 	u32 link_status;
2551b07da51SHaiyang Zhang 	struct rndis_query_complete *query_complete;
2561b07da51SHaiyang Zhang 
2571b07da51SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
2581b07da51SHaiyang Zhang 
2591b07da51SHaiyang Zhang 	if (query_complete->status == RNDIS_STATUS_SUCCESS &&
2601b07da51SHaiyang Zhang 	    query_complete->info_buflen == sizeof(u32)) {
2611b07da51SHaiyang Zhang 		memcpy(&link_status, (void *)((unsigned long)query_complete +
2621b07da51SHaiyang Zhang 		       query_complete->info_buf_offset), sizeof(u32));
2631b07da51SHaiyang Zhang 		rdev->link_state = link_status != 0;
2641b07da51SHaiyang Zhang 	}
2651b07da51SHaiyang Zhang }
2661b07da51SHaiyang Zhang 
26702400fceSStephen Hemminger static void rndis_filter_receive_response(struct net_device *ndev,
26802400fceSStephen Hemminger 					  struct netvsc_device *nvdev,
26902400fceSStephen Hemminger 					  const struct rndis_message *resp)
27095fa0405SHaiyang Zhang {
27102400fceSStephen Hemminger 	struct rndis_device *dev = nvdev->extension;
27295fa0405SHaiyang Zhang 	struct rndis_request *request = NULL;
27395fa0405SHaiyang Zhang 	bool found = false;
27495fa0405SHaiyang Zhang 	unsigned long flags;
27502400fceSStephen Hemminger 
27602400fceSStephen Hemminger 	/* This should never happen, it means control message
27702400fceSStephen Hemminger 	 * response received after device removed.
27802400fceSStephen Hemminger 	 */
27902400fceSStephen Hemminger 	if (dev->state == RNDIS_DEV_UNINITIALIZED) {
28002400fceSStephen Hemminger 		netdev_err(ndev,
28102400fceSStephen Hemminger 			   "got rndis message uninitialized\n");
28202400fceSStephen Hemminger 		return;
28302400fceSStephen Hemminger 	}
28495fa0405SHaiyang Zhang 
28595fa0405SHaiyang Zhang 	spin_lock_irqsave(&dev->request_lock, flags);
28695fa0405SHaiyang Zhang 	list_for_each_entry(request, &dev->req_list, list_ent) {
28795fa0405SHaiyang Zhang 		/*
28895fa0405SHaiyang Zhang 		 * All request/response message contains RequestId as the 1st
28995fa0405SHaiyang Zhang 		 * field
29095fa0405SHaiyang Zhang 		 */
29195fa0405SHaiyang Zhang 		if (request->request_msg.msg.init_req.req_id
29295fa0405SHaiyang Zhang 		    == resp->msg.init_complete.req_id) {
29395fa0405SHaiyang Zhang 			found = true;
29495fa0405SHaiyang Zhang 			break;
29595fa0405SHaiyang Zhang 		}
29695fa0405SHaiyang Zhang 	}
29795fa0405SHaiyang Zhang 	spin_unlock_irqrestore(&dev->request_lock, flags);
29895fa0405SHaiyang Zhang 
29995fa0405SHaiyang Zhang 	if (found) {
300a3a6cab5SHaiyang Zhang 		if (resp->msg_len <=
301a3a6cab5SHaiyang Zhang 		    sizeof(struct rndis_message) + RNDIS_EXT_LEN) {
30295fa0405SHaiyang Zhang 			memcpy(&request->response_msg, resp,
30395fa0405SHaiyang Zhang 			       resp->msg_len);
3041b07da51SHaiyang Zhang 			if (request->request_msg.ndis_msg_type ==
3051b07da51SHaiyang Zhang 			    RNDIS_MSG_QUERY && request->request_msg.msg.
3061b07da51SHaiyang Zhang 			    query_req.oid == RNDIS_OID_GEN_MEDIA_CONNECT_STATUS)
3071b07da51SHaiyang Zhang 				rndis_set_link_state(dev, request);
30895fa0405SHaiyang Zhang 		} else {
30995fa0405SHaiyang Zhang 			netdev_err(ndev,
31095fa0405SHaiyang Zhang 				"rndis response buffer overflow "
31195fa0405SHaiyang Zhang 				"detected (size %u max %zu)\n",
31295fa0405SHaiyang Zhang 				resp->msg_len,
31386eedaccSKY Srinivasan 				sizeof(struct rndis_message));
31495fa0405SHaiyang Zhang 
31595fa0405SHaiyang Zhang 			if (resp->ndis_msg_type ==
31651491167SLinus Walleij 			    RNDIS_MSG_RESET_C) {
31795fa0405SHaiyang Zhang 				/* does not have a request id field */
31895fa0405SHaiyang Zhang 				request->response_msg.msg.reset_complete.
319007e5c8eSLinus Walleij 					status = RNDIS_STATUS_BUFFER_OVERFLOW;
32095fa0405SHaiyang Zhang 			} else {
32195fa0405SHaiyang Zhang 				request->response_msg.msg.
32295fa0405SHaiyang Zhang 				init_complete.status =
323007e5c8eSLinus Walleij 					RNDIS_STATUS_BUFFER_OVERFLOW;
32495fa0405SHaiyang Zhang 			}
32595fa0405SHaiyang Zhang 		}
32695fa0405SHaiyang Zhang 
32795fa0405SHaiyang Zhang 		complete(&request->wait_event);
32895fa0405SHaiyang Zhang 	} else {
32995fa0405SHaiyang Zhang 		netdev_err(ndev,
33095fa0405SHaiyang Zhang 			"no rndis request found for this response "
33195fa0405SHaiyang Zhang 			"(id 0x%x res type 0x%x)\n",
33295fa0405SHaiyang Zhang 			resp->msg.init_complete.req_id,
33395fa0405SHaiyang Zhang 			resp->ndis_msg_type);
33495fa0405SHaiyang Zhang 	}
33595fa0405SHaiyang Zhang }
33695fa0405SHaiyang Zhang 
3371f5f3a75SHaiyang Zhang /*
3381f5f3a75SHaiyang Zhang  * Get the Per-Packet-Info with the specified type
3391f5f3a75SHaiyang Zhang  * return NULL if not found.
3401f5f3a75SHaiyang Zhang  */
3411f5f3a75SHaiyang Zhang static inline void *rndis_get_ppi(struct rndis_packet *rpkt, u32 type)
3421f5f3a75SHaiyang Zhang {
3431f5f3a75SHaiyang Zhang 	struct rndis_per_packet_info *ppi;
3441f5f3a75SHaiyang Zhang 	int len;
3451f5f3a75SHaiyang Zhang 
3461f5f3a75SHaiyang Zhang 	if (rpkt->per_pkt_info_offset == 0)
3471f5f3a75SHaiyang Zhang 		return NULL;
3481f5f3a75SHaiyang Zhang 
3491f5f3a75SHaiyang Zhang 	ppi = (struct rndis_per_packet_info *)((ulong)rpkt +
3501f5f3a75SHaiyang Zhang 		rpkt->per_pkt_info_offset);
3511f5f3a75SHaiyang Zhang 	len = rpkt->per_pkt_info_len;
3521f5f3a75SHaiyang Zhang 
3531f5f3a75SHaiyang Zhang 	while (len > 0) {
3541f5f3a75SHaiyang Zhang 		if (ppi->type == type)
3551f5f3a75SHaiyang Zhang 			return (void *)((ulong)ppi + ppi->ppi_offset);
3561f5f3a75SHaiyang Zhang 		len -= ppi->size;
3571f5f3a75SHaiyang Zhang 		ppi = (struct rndis_per_packet_info *)((ulong)ppi + ppi->size);
3581f5f3a75SHaiyang Zhang 	}
3591f5f3a75SHaiyang Zhang 
3601f5f3a75SHaiyang Zhang 	return NULL;
3611f5f3a75SHaiyang Zhang }
3621f5f3a75SHaiyang Zhang 
363dc54a08cSstephen hemminger static int rndis_filter_receive_data(struct net_device *ndev,
364345ac089SStephen Hemminger 				     struct netvsc_device *nvdev,
36595fa0405SHaiyang Zhang 				     struct rndis_message *msg,
366dc54a08cSstephen hemminger 				     struct vmbus_channel *channel,
367dc54a08cSstephen hemminger 				     void *data, u32 data_buflen)
36895fa0405SHaiyang Zhang {
369dc54a08cSstephen hemminger 	struct rndis_packet *rndis_pkt = &msg->msg.pkt;
370dc54a08cSstephen hemminger 	const struct ndis_tcp_ip_checksum_info *csum_info;
371dc54a08cSstephen hemminger 	const struct ndis_pkt_8021q_info *vlan;
37295fa0405SHaiyang Zhang 	u32 data_offset;
37395fa0405SHaiyang Zhang 
37495fa0405SHaiyang Zhang 	/* Remove the rndis header and pass it back up the stack */
37595fa0405SHaiyang Zhang 	data_offset = RNDIS_HEADER_SIZE + rndis_pkt->data_offset;
37695fa0405SHaiyang Zhang 
377dc54a08cSstephen hemminger 	data_buflen -= data_offset;
3784b8a8bc9SWei Yongjun 
3794b8a8bc9SWei Yongjun 	/*
3804b8a8bc9SWei Yongjun 	 * Make sure we got a valid RNDIS message, now total_data_buflen
3814b8a8bc9SWei Yongjun 	 * should be the data packet size plus the trailer padding size
3824b8a8bc9SWei Yongjun 	 */
383dc54a08cSstephen hemminger 	if (unlikely(data_buflen < rndis_pkt->data_len)) {
38402400fceSStephen Hemminger 		netdev_err(ndev, "rndis message buffer "
3854b8a8bc9SWei Yongjun 			   "overflow detected (got %u, min %u)"
3864b8a8bc9SWei Yongjun 			   "...dropping this message!\n",
387dc54a08cSstephen hemminger 			   data_buflen, rndis_pkt->data_len);
38810082f98SKY Srinivasan 		return NVSP_STAT_FAIL;
3894b8a8bc9SWei Yongjun 	}
3904b8a8bc9SWei Yongjun 
391dc54a08cSstephen hemminger 	vlan = rndis_get_ppi(rndis_pkt, IEEE_8021Q_INFO);
392dc54a08cSstephen hemminger 
3934b8a8bc9SWei Yongjun 	/*
3944b8a8bc9SWei Yongjun 	 * Remove the rndis trailer padding from rndis packet message
3954b8a8bc9SWei Yongjun 	 * rndis_pkt->data_len tell us the real data length, we only copy
3964b8a8bc9SWei Yongjun 	 * the data packet to the stack, without the rndis trailer padding
3974b8a8bc9SWei Yongjun 	 */
398dc54a08cSstephen hemminger 	data = (void *)((unsigned long)data + data_offset);
399e3d605edSKY Srinivasan 	csum_info = rndis_get_ppi(rndis_pkt, TCPIP_CHKSUM_PKTINFO);
400345ac089SStephen Hemminger 
401345ac089SStephen Hemminger 	return netvsc_recv_callback(ndev, nvdev, channel,
402dc54a08cSstephen hemminger 				    data, rndis_pkt->data_len,
403dc54a08cSstephen hemminger 				    csum_info, vlan);
40495fa0405SHaiyang Zhang }
40595fa0405SHaiyang Zhang 
406dc54a08cSstephen hemminger int rndis_filter_receive(struct net_device *ndev,
407dc54a08cSstephen hemminger 			 struct netvsc_device *net_dev,
408dc54a08cSstephen hemminger 			 struct vmbus_channel *channel,
409dc54a08cSstephen hemminger 			 void *data, u32 buflen)
41095fa0405SHaiyang Zhang {
4113d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(ndev);
412dc54a08cSstephen hemminger 	struct rndis_message *rndis_msg = data;
41395fa0405SHaiyang Zhang 
414dc54a08cSstephen hemminger 	if (netif_msg_rx_status(net_device_ctx))
41579cf1baeSStephen Hemminger 		dump_rndis_message(ndev, rndis_msg);
41695fa0405SHaiyang Zhang 
417ef31bef6SHaiyang Zhang 	switch (rndis_msg->ndis_msg_type) {
41851491167SLinus Walleij 	case RNDIS_MSG_PACKET:
41902400fceSStephen Hemminger 		return rndis_filter_receive_data(ndev, net_dev, rndis_msg,
420dc54a08cSstephen hemminger 						 channel, data, buflen);
42151491167SLinus Walleij 	case RNDIS_MSG_INIT_C:
42251491167SLinus Walleij 	case RNDIS_MSG_QUERY_C:
42351491167SLinus Walleij 	case RNDIS_MSG_SET_C:
42495fa0405SHaiyang Zhang 		/* completion msgs */
42502400fceSStephen Hemminger 		rndis_filter_receive_response(ndev, net_dev, rndis_msg);
42695fa0405SHaiyang Zhang 		break;
42795fa0405SHaiyang Zhang 
42851491167SLinus Walleij 	case RNDIS_MSG_INDICATE:
42995fa0405SHaiyang Zhang 		/* notification msgs */
43079cf1baeSStephen Hemminger 		netvsc_linkstatus_callback(ndev, rndis_msg);
43195fa0405SHaiyang Zhang 		break;
43295fa0405SHaiyang Zhang 	default:
43395fa0405SHaiyang Zhang 		netdev_err(ndev,
43495fa0405SHaiyang Zhang 			"unhandled rndis message (type %u len %u)\n",
435ef31bef6SHaiyang Zhang 			   rndis_msg->ndis_msg_type,
436ef31bef6SHaiyang Zhang 			   rndis_msg->msg_len);
43795fa0405SHaiyang Zhang 		break;
43895fa0405SHaiyang Zhang 	}
43995fa0405SHaiyang Zhang 
440dc54a08cSstephen hemminger 	return 0;
44195fa0405SHaiyang Zhang }
44295fa0405SHaiyang Zhang 
443867047c4Sstephen hemminger static int rndis_filter_query_device(struct rndis_device *dev,
444867047c4Sstephen hemminger 				     struct netvsc_device *nvdev,
445867047c4Sstephen hemminger 				     u32 oid, void *result, u32 *result_size)
44695fa0405SHaiyang Zhang {
44795fa0405SHaiyang Zhang 	struct rndis_request *request;
44895fa0405SHaiyang Zhang 	u32 inresult_size = *result_size;
44995fa0405SHaiyang Zhang 	struct rndis_query_request *query;
45095fa0405SHaiyang Zhang 	struct rndis_query_complete *query_complete;
45195fa0405SHaiyang Zhang 	int ret = 0;
45295fa0405SHaiyang Zhang 
45395fa0405SHaiyang Zhang 	if (!result)
45495fa0405SHaiyang Zhang 		return -EINVAL;
45595fa0405SHaiyang Zhang 
45695fa0405SHaiyang Zhang 	*result_size = 0;
45751491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_QUERY,
45895fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_query_request));
45995fa0405SHaiyang Zhang 	if (!request) {
46095fa0405SHaiyang Zhang 		ret = -ENOMEM;
46195fa0405SHaiyang Zhang 		goto cleanup;
46295fa0405SHaiyang Zhang 	}
46395fa0405SHaiyang Zhang 
46495fa0405SHaiyang Zhang 	/* Setup the rndis query */
46595fa0405SHaiyang Zhang 	query = &request->request_msg.msg.query_req;
46695fa0405SHaiyang Zhang 	query->oid = oid;
46795fa0405SHaiyang Zhang 	query->info_buf_offset = sizeof(struct rndis_query_request);
46895fa0405SHaiyang Zhang 	query->info_buflen = 0;
46995fa0405SHaiyang Zhang 	query->dev_vc_handle = 0;
47095fa0405SHaiyang Zhang 
47123312a3bSstephen hemminger 	if (oid == OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES) {
47223312a3bSstephen hemminger 		struct ndis_offload *hwcaps;
47323312a3bSstephen hemminger 		u32 nvsp_version = nvdev->nvsp_version;
47423312a3bSstephen hemminger 		u8 ndis_rev;
47523312a3bSstephen hemminger 		size_t size;
47623312a3bSstephen hemminger 
47723312a3bSstephen hemminger 		if (nvsp_version >= NVSP_PROTOCOL_VERSION_5) {
47823312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
47923312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE;
48023312a3bSstephen hemminger 		} else if (nvsp_version >= NVSP_PROTOCOL_VERSION_4) {
48123312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_2;
48223312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_1;
48323312a3bSstephen hemminger 		} else {
48423312a3bSstephen hemminger 			ndis_rev = NDIS_OFFLOAD_PARAMETERS_REVISION_1;
48523312a3bSstephen hemminger 			size = NDIS_OFFLOAD_SIZE_6_0;
48623312a3bSstephen hemminger 		}
48723312a3bSstephen hemminger 
48823312a3bSstephen hemminger 		request->request_msg.msg_len += size;
48923312a3bSstephen hemminger 		query->info_buflen = size;
49023312a3bSstephen hemminger 		hwcaps = (struct ndis_offload *)
49123312a3bSstephen hemminger 			((unsigned long)query + query->info_buf_offset);
49223312a3bSstephen hemminger 
49323312a3bSstephen hemminger 		hwcaps->header.type = NDIS_OBJECT_TYPE_OFFLOAD;
49423312a3bSstephen hemminger 		hwcaps->header.revision = ndis_rev;
49523312a3bSstephen hemminger 		hwcaps->header.size = size;
49623312a3bSstephen hemminger 
49723312a3bSstephen hemminger 	} else if (oid == OID_GEN_RECEIVE_SCALE_CAPABILITIES) {
4985b54dac8SHaiyang Zhang 		struct ndis_recv_scale_cap *cap;
4995b54dac8SHaiyang Zhang 
5005b54dac8SHaiyang Zhang 		request->request_msg.msg_len +=
5015b54dac8SHaiyang Zhang 			sizeof(struct ndis_recv_scale_cap);
5025b54dac8SHaiyang Zhang 		query->info_buflen = sizeof(struct ndis_recv_scale_cap);
5035b54dac8SHaiyang Zhang 		cap = (struct ndis_recv_scale_cap *)((unsigned long)query +
5045b54dac8SHaiyang Zhang 						     query->info_buf_offset);
5055b54dac8SHaiyang Zhang 		cap->hdr.type = NDIS_OBJECT_TYPE_RSS_CAPABILITIES;
5065b54dac8SHaiyang Zhang 		cap->hdr.rev = NDIS_RECEIVE_SCALE_CAPABILITIES_REVISION_2;
5075b54dac8SHaiyang Zhang 		cap->hdr.size = sizeof(struct ndis_recv_scale_cap);
5085b54dac8SHaiyang Zhang 	}
5095b54dac8SHaiyang Zhang 
51095fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
51195fa0405SHaiyang Zhang 	if (ret != 0)
51295fa0405SHaiyang Zhang 		goto cleanup;
51395fa0405SHaiyang Zhang 
5145362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
51595fa0405SHaiyang Zhang 
51695fa0405SHaiyang Zhang 	/* Copy the response back */
51795fa0405SHaiyang Zhang 	query_complete = &request->response_msg.msg.query_complete;
51895fa0405SHaiyang Zhang 
51995fa0405SHaiyang Zhang 	if (query_complete->info_buflen > inresult_size) {
52095fa0405SHaiyang Zhang 		ret = -1;
52195fa0405SHaiyang Zhang 		goto cleanup;
52295fa0405SHaiyang Zhang 	}
52395fa0405SHaiyang Zhang 
52495fa0405SHaiyang Zhang 	memcpy(result,
52595fa0405SHaiyang Zhang 	       (void *)((unsigned long)query_complete +
52695fa0405SHaiyang Zhang 			 query_complete->info_buf_offset),
52795fa0405SHaiyang Zhang 	       query_complete->info_buflen);
52895fa0405SHaiyang Zhang 
52995fa0405SHaiyang Zhang 	*result_size = query_complete->info_buflen;
53095fa0405SHaiyang Zhang 
53195fa0405SHaiyang Zhang cleanup:
53295fa0405SHaiyang Zhang 	if (request)
53395fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
53495fa0405SHaiyang Zhang 
53595fa0405SHaiyang Zhang 	return ret;
53695fa0405SHaiyang Zhang }
53795fa0405SHaiyang Zhang 
53823312a3bSstephen hemminger /* Get the hardware offload capabilities */
53923312a3bSstephen hemminger static int
540867047c4Sstephen hemminger rndis_query_hwcaps(struct rndis_device *dev, struct netvsc_device *net_device,
541867047c4Sstephen hemminger 		   struct ndis_offload *caps)
54223312a3bSstephen hemminger {
54323312a3bSstephen hemminger 	u32 caps_len = sizeof(*caps);
54423312a3bSstephen hemminger 	int ret;
54523312a3bSstephen hemminger 
54623312a3bSstephen hemminger 	memset(caps, 0, sizeof(*caps));
54723312a3bSstephen hemminger 
548867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
54923312a3bSstephen hemminger 					OID_TCP_OFFLOAD_HARDWARE_CAPABILITIES,
55023312a3bSstephen hemminger 					caps, &caps_len);
55123312a3bSstephen hemminger 	if (ret)
55223312a3bSstephen hemminger 		return ret;
55323312a3bSstephen hemminger 
55423312a3bSstephen hemminger 	if (caps->header.type != NDIS_OBJECT_TYPE_OFFLOAD) {
55523312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objtype %#x\n",
55623312a3bSstephen hemminger 			    caps->header.type);
55723312a3bSstephen hemminger 		return -EINVAL;
55823312a3bSstephen hemminger 	}
55923312a3bSstephen hemminger 
56023312a3bSstephen hemminger 	if (caps->header.revision < NDIS_OFFLOAD_PARAMETERS_REVISION_1) {
56123312a3bSstephen hemminger 		netdev_warn(dev->ndev, "invalid NDIS objrev %x\n",
56223312a3bSstephen hemminger 			    caps->header.revision);
56323312a3bSstephen hemminger 		return -EINVAL;
56423312a3bSstephen hemminger 	}
56523312a3bSstephen hemminger 
56623312a3bSstephen hemminger 	if (caps->header.size > caps_len ||
56723312a3bSstephen hemminger 	    caps->header.size < NDIS_OFFLOAD_SIZE_6_0) {
56823312a3bSstephen hemminger 		netdev_warn(dev->ndev,
56923312a3bSstephen hemminger 			    "invalid NDIS objsize %u, data size %u\n",
57023312a3bSstephen hemminger 			    caps->header.size, caps_len);
57123312a3bSstephen hemminger 		return -EINVAL;
57223312a3bSstephen hemminger 	}
57323312a3bSstephen hemminger 
57423312a3bSstephen hemminger 	return 0;
57523312a3bSstephen hemminger }
57623312a3bSstephen hemminger 
577867047c4Sstephen hemminger static int rndis_filter_query_device_mac(struct rndis_device *dev,
578867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
57995fa0405SHaiyang Zhang {
58095fa0405SHaiyang Zhang 	u32 size = ETH_ALEN;
58195fa0405SHaiyang Zhang 
582867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
58395fa0405SHaiyang Zhang 				      RNDIS_OID_802_3_PERMANENT_ADDRESS,
58495fa0405SHaiyang Zhang 				      dev->hw_mac_adr, &size);
58595fa0405SHaiyang Zhang }
58695fa0405SHaiyang Zhang 
5871ce09e89SHaiyang Zhang #define NWADR_STR "NetworkAddress"
5881ce09e89SHaiyang Zhang #define NWADR_STRLEN 14
5891ce09e89SHaiyang Zhang 
590867047c4Sstephen hemminger int rndis_filter_set_device_mac(struct netvsc_device *nvdev,
591867047c4Sstephen hemminger 				const char *mac)
5921ce09e89SHaiyang Zhang {
5931ce09e89SHaiyang Zhang 	struct rndis_device *rdev = nvdev->extension;
5941ce09e89SHaiyang Zhang 	struct rndis_request *request;
5951ce09e89SHaiyang Zhang 	struct rndis_set_request *set;
5961ce09e89SHaiyang Zhang 	struct rndis_config_parameter_info *cpi;
5971ce09e89SHaiyang Zhang 	wchar_t *cfg_nwadr, *cfg_mac;
5981ce09e89SHaiyang Zhang 	struct rndis_set_complete *set_complete;
5991ce09e89SHaiyang Zhang 	char macstr[2*ETH_ALEN+1];
6001ce09e89SHaiyang Zhang 	u32 extlen = sizeof(struct rndis_config_parameter_info) +
6011ce09e89SHaiyang Zhang 		2*NWADR_STRLEN + 4*ETH_ALEN;
602999028ccSNicholas Mc Guire 	int ret;
6031ce09e89SHaiyang Zhang 
6041ce09e89SHaiyang Zhang 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6051ce09e89SHaiyang Zhang 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6061ce09e89SHaiyang Zhang 	if (!request)
6071ce09e89SHaiyang Zhang 		return -ENOMEM;
6081ce09e89SHaiyang Zhang 
6091ce09e89SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
6101ce09e89SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_RNDIS_CONFIG_PARAMETER;
6111ce09e89SHaiyang Zhang 	set->info_buflen = extlen;
6121ce09e89SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
6131ce09e89SHaiyang Zhang 	set->dev_vc_handle = 0;
6141ce09e89SHaiyang Zhang 
6151ce09e89SHaiyang Zhang 	cpi = (struct rndis_config_parameter_info *)((ulong)set +
6161ce09e89SHaiyang Zhang 		set->info_buf_offset);
6171ce09e89SHaiyang Zhang 	cpi->parameter_name_offset =
6181ce09e89SHaiyang Zhang 		sizeof(struct rndis_config_parameter_info);
6191ce09e89SHaiyang Zhang 	/* Multiply by 2 because host needs 2 bytes (utf16) for each char */
6201ce09e89SHaiyang Zhang 	cpi->parameter_name_length = 2*NWADR_STRLEN;
6211ce09e89SHaiyang Zhang 	cpi->parameter_type = RNDIS_CONFIG_PARAM_TYPE_STRING;
6221ce09e89SHaiyang Zhang 	cpi->parameter_value_offset =
6231ce09e89SHaiyang Zhang 		cpi->parameter_name_offset + cpi->parameter_name_length;
6241ce09e89SHaiyang Zhang 	/* Multiply by 4 because each MAC byte displayed as 2 utf16 chars */
6251ce09e89SHaiyang Zhang 	cpi->parameter_value_length = 4*ETH_ALEN;
6261ce09e89SHaiyang Zhang 
6271ce09e89SHaiyang Zhang 	cfg_nwadr = (wchar_t *)((ulong)cpi + cpi->parameter_name_offset);
6281ce09e89SHaiyang Zhang 	cfg_mac = (wchar_t *)((ulong)cpi + cpi->parameter_value_offset);
6291ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(NWADR_STR, NWADR_STRLEN, UTF16_HOST_ENDIAN,
6301ce09e89SHaiyang Zhang 			      cfg_nwadr, NWADR_STRLEN);
6311ce09e89SHaiyang Zhang 	if (ret < 0)
6321ce09e89SHaiyang Zhang 		goto cleanup;
6331ce09e89SHaiyang Zhang 	snprintf(macstr, 2*ETH_ALEN+1, "%pm", mac);
6341ce09e89SHaiyang Zhang 	ret = utf8s_to_utf16s(macstr, 2*ETH_ALEN, UTF16_HOST_ENDIAN,
6351ce09e89SHaiyang Zhang 			      cfg_mac, 2*ETH_ALEN);
6361ce09e89SHaiyang Zhang 	if (ret < 0)
6371ce09e89SHaiyang Zhang 		goto cleanup;
6381ce09e89SHaiyang Zhang 
6391ce09e89SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
6401ce09e89SHaiyang Zhang 	if (ret != 0)
6411ce09e89SHaiyang Zhang 		goto cleanup;
6421ce09e89SHaiyang Zhang 
6435362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
6445362855aSVitaly Kuznetsov 
6451ce09e89SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
646867047c4Sstephen hemminger 	if (set_complete->status != RNDIS_STATUS_SUCCESS)
647867047c4Sstephen hemminger 		ret = -EIO;
6481ce09e89SHaiyang Zhang 
6491ce09e89SHaiyang Zhang cleanup:
6501ce09e89SHaiyang Zhang 	put_rndis_request(rdev, request);
6511ce09e89SHaiyang Zhang 	return ret;
6521ce09e89SHaiyang Zhang }
6531ce09e89SHaiyang Zhang 
654da19fcd0SLad, Prabhakar static int
655426d9541SVitaly Kuznetsov rndis_filter_set_offload_params(struct net_device *ndev,
6569749fed5Sstephen hemminger 				struct netvsc_device *nvdev,
6574a0e70aeSKY Srinivasan 				struct ndis_offload_params *req_offloads)
6584a0e70aeSKY Srinivasan {
6594a0e70aeSKY Srinivasan 	struct rndis_device *rdev = nvdev->extension;
6604a0e70aeSKY Srinivasan 	struct rndis_request *request;
6614a0e70aeSKY Srinivasan 	struct rndis_set_request *set;
6624a0e70aeSKY Srinivasan 	struct ndis_offload_params *offload_params;
6634a0e70aeSKY Srinivasan 	struct rndis_set_complete *set_complete;
6644a0e70aeSKY Srinivasan 	u32 extlen = sizeof(struct ndis_offload_params);
665999028ccSNicholas Mc Guire 	int ret;
666af9893a3SKY Srinivasan 	u32 vsp_version = nvdev->nvsp_version;
667af9893a3SKY Srinivasan 
668af9893a3SKY Srinivasan 	if (vsp_version <= NVSP_PROTOCOL_VERSION_4) {
669af9893a3SKY Srinivasan 		extlen = VERSION_4_OFFLOAD_SIZE;
670af9893a3SKY Srinivasan 		/* On NVSP_PROTOCOL_VERSION_4 and below, we do not support
671af9893a3SKY Srinivasan 		 * UDP checksum offload.
672af9893a3SKY Srinivasan 		 */
673af9893a3SKY Srinivasan 		req_offloads->udp_ip_v4_csum = 0;
674af9893a3SKY Srinivasan 		req_offloads->udp_ip_v6_csum = 0;
675af9893a3SKY Srinivasan 	}
6764a0e70aeSKY Srinivasan 
6774a0e70aeSKY Srinivasan 	request = get_rndis_request(rdev, RNDIS_MSG_SET,
6784a0e70aeSKY Srinivasan 		RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
6794a0e70aeSKY Srinivasan 	if (!request)
6804a0e70aeSKY Srinivasan 		return -ENOMEM;
6814a0e70aeSKY Srinivasan 
6824a0e70aeSKY Srinivasan 	set = &request->request_msg.msg.set_req;
6834a0e70aeSKY Srinivasan 	set->oid = OID_TCP_OFFLOAD_PARAMETERS;
6844a0e70aeSKY Srinivasan 	set->info_buflen = extlen;
6854a0e70aeSKY Srinivasan 	set->info_buf_offset = sizeof(struct rndis_set_request);
6864a0e70aeSKY Srinivasan 	set->dev_vc_handle = 0;
6874a0e70aeSKY Srinivasan 
6884a0e70aeSKY Srinivasan 	offload_params = (struct ndis_offload_params *)((ulong)set +
6894a0e70aeSKY Srinivasan 				set->info_buf_offset);
6904a0e70aeSKY Srinivasan 	*offload_params = *req_offloads;
6914a0e70aeSKY Srinivasan 	offload_params->header.type = NDIS_OBJECT_TYPE_DEFAULT;
6924a0e70aeSKY Srinivasan 	offload_params->header.revision = NDIS_OFFLOAD_PARAMETERS_REVISION_3;
6934a0e70aeSKY Srinivasan 	offload_params->header.size = extlen;
6944a0e70aeSKY Srinivasan 
6954a0e70aeSKY Srinivasan 	ret = rndis_filter_send_request(rdev, request);
6964a0e70aeSKY Srinivasan 	if (ret != 0)
6974a0e70aeSKY Srinivasan 		goto cleanup;
6984a0e70aeSKY Srinivasan 
6995362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7004a0e70aeSKY Srinivasan 	set_complete = &request->response_msg.msg.set_complete;
7014a0e70aeSKY Srinivasan 	if (set_complete->status != RNDIS_STATUS_SUCCESS) {
702af9893a3SKY Srinivasan 		netdev_err(ndev, "Fail to set offload on host side:0x%x\n",
7034a0e70aeSKY Srinivasan 			   set_complete->status);
7044a0e70aeSKY Srinivasan 		ret = -EINVAL;
7054a0e70aeSKY Srinivasan 	}
7064a0e70aeSKY Srinivasan 
7074a0e70aeSKY Srinivasan cleanup:
7084a0e70aeSKY Srinivasan 	put_rndis_request(rdev, request);
7094a0e70aeSKY Srinivasan 	return ret;
7104a0e70aeSKY Srinivasan }
7111ce09e89SHaiyang Zhang 
712962f3feeSstephen hemminger int rndis_filter_set_rss_param(struct rndis_device *rdev,
713715e2ec5SHaiyang Zhang 			       const u8 *rss_key)
7145b54dac8SHaiyang Zhang {
7153d541ac5SVitaly Kuznetsov 	struct net_device *ndev = rdev->ndev;
7165b54dac8SHaiyang Zhang 	struct rndis_request *request;
7175b54dac8SHaiyang Zhang 	struct rndis_set_request *set;
7185b54dac8SHaiyang Zhang 	struct rndis_set_complete *set_complete;
7195b54dac8SHaiyang Zhang 	u32 extlen = sizeof(struct ndis_recv_scale_param) +
720962f3feeSstephen hemminger 		     4 * ITAB_NUM + NETVSC_HASH_KEYLEN;
7215b54dac8SHaiyang Zhang 	struct ndis_recv_scale_param *rssp;
7225b54dac8SHaiyang Zhang 	u32 *itab;
7235b54dac8SHaiyang Zhang 	u8 *keyp;
724999028ccSNicholas Mc Guire 	int i, ret;
7255b54dac8SHaiyang Zhang 
7265b54dac8SHaiyang Zhang 	request = get_rndis_request(
7275b54dac8SHaiyang Zhang 			rdev, RNDIS_MSG_SET,
7285b54dac8SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) + extlen);
7295b54dac8SHaiyang Zhang 	if (!request)
7305b54dac8SHaiyang Zhang 		return -ENOMEM;
7315b54dac8SHaiyang Zhang 
7325b54dac8SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
7335b54dac8SHaiyang Zhang 	set->oid = OID_GEN_RECEIVE_SCALE_PARAMETERS;
7345b54dac8SHaiyang Zhang 	set->info_buflen = extlen;
7355b54dac8SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
7365b54dac8SHaiyang Zhang 	set->dev_vc_handle = 0;
7375b54dac8SHaiyang Zhang 
7385b54dac8SHaiyang Zhang 	rssp = (struct ndis_recv_scale_param *)(set + 1);
7395b54dac8SHaiyang Zhang 	rssp->hdr.type = NDIS_OBJECT_TYPE_RSS_PARAMETERS;
7405b54dac8SHaiyang Zhang 	rssp->hdr.rev = NDIS_RECEIVE_SCALE_PARAMETERS_REVISION_2;
7415b54dac8SHaiyang Zhang 	rssp->hdr.size = sizeof(struct ndis_recv_scale_param);
7425b54dac8SHaiyang Zhang 	rssp->flag = 0;
7435b54dac8SHaiyang Zhang 	rssp->hashinfo = NDIS_HASH_FUNC_TOEPLITZ | NDIS_HASH_IPV4 |
7444c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV4 | NDIS_HASH_IPV6 |
7454c87454aSHaiyang Zhang 			 NDIS_HASH_TCP_IPV6;
7465b54dac8SHaiyang Zhang 	rssp->indirect_tabsize = 4*ITAB_NUM;
7475b54dac8SHaiyang Zhang 	rssp->indirect_taboffset = sizeof(struct ndis_recv_scale_param);
748962f3feeSstephen hemminger 	rssp->hashkey_size = NETVSC_HASH_KEYLEN;
7495b54dac8SHaiyang Zhang 	rssp->kashkey_offset = rssp->indirect_taboffset +
7505b54dac8SHaiyang Zhang 			       rssp->indirect_tabsize;
7515b54dac8SHaiyang Zhang 
7525b54dac8SHaiyang Zhang 	/* Set indirection table entries */
7535b54dac8SHaiyang Zhang 	itab = (u32 *)(rssp + 1);
7545b54dac8SHaiyang Zhang 	for (i = 0; i < ITAB_NUM; i++)
75547371300SHaiyang Zhang 		itab[i] = rdev->rx_table[i];
7565b54dac8SHaiyang Zhang 
7575b54dac8SHaiyang Zhang 	/* Set hask key values */
7585b54dac8SHaiyang Zhang 	keyp = (u8 *)((unsigned long)rssp + rssp->kashkey_offset);
759962f3feeSstephen hemminger 	memcpy(keyp, rss_key, NETVSC_HASH_KEYLEN);
7605b54dac8SHaiyang Zhang 
7615b54dac8SHaiyang Zhang 	ret = rndis_filter_send_request(rdev, request);
7625b54dac8SHaiyang Zhang 	if (ret != 0)
7635b54dac8SHaiyang Zhang 		goto cleanup;
7645b54dac8SHaiyang Zhang 
7655362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
7665b54dac8SHaiyang Zhang 	set_complete = &request->response_msg.msg.set_complete;
767962f3feeSstephen hemminger 	if (set_complete->status == RNDIS_STATUS_SUCCESS)
768962f3feeSstephen hemminger 		memcpy(rdev->rss_key, rss_key, NETVSC_HASH_KEYLEN);
769962f3feeSstephen hemminger 	else {
7705b54dac8SHaiyang Zhang 		netdev_err(ndev, "Fail to set RSS parameters:0x%x\n",
7715b54dac8SHaiyang Zhang 			   set_complete->status);
7725b54dac8SHaiyang Zhang 		ret = -EINVAL;
7735b54dac8SHaiyang Zhang 	}
7745b54dac8SHaiyang Zhang 
7755b54dac8SHaiyang Zhang cleanup:
7765b54dac8SHaiyang Zhang 	put_rndis_request(rdev, request);
7775b54dac8SHaiyang Zhang 	return ret;
7785b54dac8SHaiyang Zhang }
7795b54dac8SHaiyang Zhang 
780867047c4Sstephen hemminger static int rndis_filter_query_device_link_status(struct rndis_device *dev,
781867047c4Sstephen hemminger 						 struct netvsc_device *net_device)
78295fa0405SHaiyang Zhang {
78395fa0405SHaiyang Zhang 	u32 size = sizeof(u32);
78495fa0405SHaiyang Zhang 	u32 link_status;
78595fa0405SHaiyang Zhang 
786867047c4Sstephen hemminger 	return rndis_filter_query_device(dev, net_device,
78795fa0405SHaiyang Zhang 					 RNDIS_OID_GEN_MEDIA_CONNECT_STATUS,
78895fa0405SHaiyang Zhang 					 &link_status, &size);
78995fa0405SHaiyang Zhang }
79095fa0405SHaiyang Zhang 
791867047c4Sstephen hemminger static int rndis_filter_query_link_speed(struct rndis_device *dev,
792867047c4Sstephen hemminger 					 struct netvsc_device *net_device)
793b37879e6SHaiyang Zhang {
794b37879e6SHaiyang Zhang 	u32 size = sizeof(u32);
795b37879e6SHaiyang Zhang 	u32 link_speed;
796b37879e6SHaiyang Zhang 	struct net_device_context *ndc;
797b37879e6SHaiyang Zhang 	int ret;
798b37879e6SHaiyang Zhang 
799867047c4Sstephen hemminger 	ret = rndis_filter_query_device(dev, net_device,
800867047c4Sstephen hemminger 					RNDIS_OID_GEN_LINK_SPEED,
801b37879e6SHaiyang Zhang 					&link_speed, &size);
802b37879e6SHaiyang Zhang 
803b37879e6SHaiyang Zhang 	if (!ret) {
804b37879e6SHaiyang Zhang 		ndc = netdev_priv(dev->ndev);
805b37879e6SHaiyang Zhang 
806b37879e6SHaiyang Zhang 		/* The link speed reported from host is in 100bps unit, so
807b37879e6SHaiyang Zhang 		 * we convert it to Mbps here.
808b37879e6SHaiyang Zhang 		 */
809b37879e6SHaiyang Zhang 		ndc->speed = link_speed / 10000;
810b37879e6SHaiyang Zhang 	}
811b37879e6SHaiyang Zhang 
812b37879e6SHaiyang Zhang 	return ret;
813b37879e6SHaiyang Zhang }
814b37879e6SHaiyang Zhang 
8154f19c0d8Sstephen hemminger static int rndis_filter_set_packet_filter(struct rndis_device *dev,
8164f19c0d8Sstephen hemminger 					  u32 new_filter)
81795fa0405SHaiyang Zhang {
81895fa0405SHaiyang Zhang 	struct rndis_request *request;
81995fa0405SHaiyang Zhang 	struct rndis_set_request *set;
820999028ccSNicholas Mc Guire 	int ret;
82195fa0405SHaiyang Zhang 
8227eeb4a6eSStephen Hemminger 	if (dev->filter == new_filter)
8237eeb4a6eSStephen Hemminger 		return 0;
8247eeb4a6eSStephen Hemminger 
82551491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_SET,
82695fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_set_request) +
82795fa0405SHaiyang Zhang 			sizeof(u32));
828ce12b810Sstephen hemminger 	if (!request)
829ce12b810Sstephen hemminger 		return -ENOMEM;
830ce12b810Sstephen hemminger 
83195fa0405SHaiyang Zhang 	/* Setup the rndis set */
83295fa0405SHaiyang Zhang 	set = &request->request_msg.msg.set_req;
83395fa0405SHaiyang Zhang 	set->oid = RNDIS_OID_GEN_CURRENT_PACKET_FILTER;
83495fa0405SHaiyang Zhang 	set->info_buflen = sizeof(u32);
83595fa0405SHaiyang Zhang 	set->info_buf_offset = sizeof(struct rndis_set_request);
83695fa0405SHaiyang Zhang 
83795fa0405SHaiyang Zhang 	memcpy((void *)(unsigned long)set + sizeof(struct rndis_set_request),
83895fa0405SHaiyang Zhang 	       &new_filter, sizeof(u32));
83995fa0405SHaiyang Zhang 
84095fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
8417eeb4a6eSStephen Hemminger 	if (ret == 0) {
8425362855aSVitaly Kuznetsov 		wait_for_completion(&request->wait_event);
8437eeb4a6eSStephen Hemminger 		dev->filter = new_filter;
8447eeb4a6eSStephen Hemminger 	}
84595fa0405SHaiyang Zhang 
84695fa0405SHaiyang Zhang 	put_rndis_request(dev, request);
847ce12b810Sstephen hemminger 
84895fa0405SHaiyang Zhang 	return ret;
84995fa0405SHaiyang Zhang }
85095fa0405SHaiyang Zhang 
8514f19c0d8Sstephen hemminger static void rndis_set_multicast(struct work_struct *w)
8524f19c0d8Sstephen hemminger {
8534f19c0d8Sstephen hemminger 	struct rndis_device *rdev
8544f19c0d8Sstephen hemminger 		= container_of(w, struct rndis_device, mcast_work);
855009f766cSStephen Hemminger 	u32 filter = NDIS_PACKET_TYPE_DIRECTED;
856009f766cSStephen Hemminger 	unsigned int flags = rdev->ndev->flags;
8574f19c0d8Sstephen hemminger 
858009f766cSStephen Hemminger 	if (flags & IFF_PROMISC) {
859009f766cSStephen Hemminger 		filter = NDIS_PACKET_TYPE_PROMISCUOUS;
860009f766cSStephen Hemminger 	} else {
861009f766cSStephen Hemminger 		if (flags & IFF_ALLMULTI)
862de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_ALL_MULTICAST;
863009f766cSStephen Hemminger 		if (flags & IFF_BROADCAST)
864de3d50aaSStephen Hemminger 			filter |= NDIS_PACKET_TYPE_BROADCAST;
865009f766cSStephen Hemminger 	}
866009f766cSStephen Hemminger 
867009f766cSStephen Hemminger 	rndis_filter_set_packet_filter(rdev, filter);
8684f19c0d8Sstephen hemminger }
8694f19c0d8Sstephen hemminger 
8704f19c0d8Sstephen hemminger void rndis_filter_update(struct netvsc_device *nvdev)
8714f19c0d8Sstephen hemminger {
8724f19c0d8Sstephen hemminger 	struct rndis_device *rdev = nvdev->extension;
8734f19c0d8Sstephen hemminger 
8744f19c0d8Sstephen hemminger 	schedule_work(&rdev->mcast_work);
8754f19c0d8Sstephen hemminger }
8764f19c0d8Sstephen hemminger 
877867047c4Sstephen hemminger static int rndis_filter_init_device(struct rndis_device *dev,
878867047c4Sstephen hemminger 				    struct netvsc_device *nvdev)
87995fa0405SHaiyang Zhang {
88095fa0405SHaiyang Zhang 	struct rndis_request *request;
88195fa0405SHaiyang Zhang 	struct rndis_initialize_request *init;
88295fa0405SHaiyang Zhang 	struct rndis_initialize_complete *init_complete;
88395fa0405SHaiyang Zhang 	u32 status;
884999028ccSNicholas Mc Guire 	int ret;
88595fa0405SHaiyang Zhang 
88651491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_INIT,
88795fa0405SHaiyang Zhang 			RNDIS_MESSAGE_SIZE(struct rndis_initialize_request));
88895fa0405SHaiyang Zhang 	if (!request) {
88995fa0405SHaiyang Zhang 		ret = -ENOMEM;
89095fa0405SHaiyang Zhang 		goto cleanup;
89195fa0405SHaiyang Zhang 	}
89295fa0405SHaiyang Zhang 
89395fa0405SHaiyang Zhang 	/* Setup the rndis set */
89495fa0405SHaiyang Zhang 	init = &request->request_msg.msg.init_req;
89595fa0405SHaiyang Zhang 	init->major_ver = RNDIS_MAJOR_VERSION;
89695fa0405SHaiyang Zhang 	init->minor_ver = RNDIS_MINOR_VERSION;
897fb1d074eSHaiyang Zhang 	init->max_xfer_size = 0x4000;
89895fa0405SHaiyang Zhang 
89995fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_INITIALIZING;
90095fa0405SHaiyang Zhang 
90195fa0405SHaiyang Zhang 	ret = rndis_filter_send_request(dev, request);
90295fa0405SHaiyang Zhang 	if (ret != 0) {
90395fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
90495fa0405SHaiyang Zhang 		goto cleanup;
90595fa0405SHaiyang Zhang 	}
90695fa0405SHaiyang Zhang 
9075362855aSVitaly Kuznetsov 	wait_for_completion(&request->wait_event);
90895fa0405SHaiyang Zhang 
90995fa0405SHaiyang Zhang 	init_complete = &request->response_msg.msg.init_complete;
91095fa0405SHaiyang Zhang 	status = init_complete->status;
91195fa0405SHaiyang Zhang 	if (status == RNDIS_STATUS_SUCCESS) {
91295fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
9137c3877f2SHaiyang Zhang 		nvdev->max_pkt = init_complete->max_pkt_per_msg;
9147c3877f2SHaiyang Zhang 		nvdev->pkt_align = 1 << init_complete->pkt_alignment_factor;
91595fa0405SHaiyang Zhang 		ret = 0;
91695fa0405SHaiyang Zhang 	} else {
91795fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_UNINITIALIZED;
91895fa0405SHaiyang Zhang 		ret = -EINVAL;
91995fa0405SHaiyang Zhang 	}
92095fa0405SHaiyang Zhang 
92195fa0405SHaiyang Zhang cleanup:
92295fa0405SHaiyang Zhang 	if (request)
92395fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
92495fa0405SHaiyang Zhang 
92595fa0405SHaiyang Zhang 	return ret;
92695fa0405SHaiyang Zhang }
92795fa0405SHaiyang Zhang 
92846b4f7f5Sstephen hemminger static bool netvsc_device_idle(const struct netvsc_device *nvdev)
92946b4f7f5Sstephen hemminger {
93046b4f7f5Sstephen hemminger 	int i;
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 
9357426b1a5Sstephen hemminger 		if (nvchan->mrc.first != nvchan->mrc.next)
9367426b1a5Sstephen hemminger 			return false;
9377426b1a5Sstephen hemminger 
93846b4f7f5Sstephen hemminger 		if (atomic_read(&nvchan->queue_sends) > 0)
93946b4f7f5Sstephen hemminger 			return false;
94046b4f7f5Sstephen hemminger 	}
94146b4f7f5Sstephen hemminger 
94246b4f7f5Sstephen hemminger 	return true;
94346b4f7f5Sstephen hemminger }
94446b4f7f5Sstephen hemminger 
94595fa0405SHaiyang Zhang static void rndis_filter_halt_device(struct rndis_device *dev)
94695fa0405SHaiyang Zhang {
94795fa0405SHaiyang Zhang 	struct rndis_request *request;
94895fa0405SHaiyang Zhang 	struct rndis_halt_request *halt;
9493d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(dev->ndev);
9503962981fSstephen hemminger 	struct netvsc_device *nvdev = rtnl_dereference(net_device_ctx->nvdev);
95195fa0405SHaiyang Zhang 
95295fa0405SHaiyang Zhang 	/* Attempt to do a rndis device halt */
95351491167SLinus Walleij 	request = get_rndis_request(dev, RNDIS_MSG_HALT,
95495fa0405SHaiyang Zhang 				RNDIS_MESSAGE_SIZE(struct rndis_halt_request));
95595fa0405SHaiyang Zhang 	if (!request)
95695fa0405SHaiyang Zhang 		goto cleanup;
95795fa0405SHaiyang Zhang 
95895fa0405SHaiyang Zhang 	/* Setup the rndis set */
95995fa0405SHaiyang Zhang 	halt = &request->request_msg.msg.halt_req;
96095fa0405SHaiyang Zhang 	halt->req_id = atomic_inc_return(&dev->new_req_id);
96195fa0405SHaiyang Zhang 
96295fa0405SHaiyang Zhang 	/* Ignore return since this msg is optional. */
96395fa0405SHaiyang Zhang 	rndis_filter_send_request(dev, request);
96495fa0405SHaiyang Zhang 
96595fa0405SHaiyang Zhang 	dev->state = RNDIS_DEV_UNINITIALIZED;
96695fa0405SHaiyang Zhang 
96795fa0405SHaiyang Zhang cleanup:
968ae9e63bbSHaiyang Zhang 	nvdev->destroy = true;
96900ecfb3bSstephen hemminger 
97000ecfb3bSstephen hemminger 	/* Force flag to be ordered before waiting */
97100ecfb3bSstephen hemminger 	wmb();
972ae9e63bbSHaiyang Zhang 
973ae9e63bbSHaiyang Zhang 	/* Wait for all send completions */
97446b4f7f5Sstephen hemminger 	wait_event(nvdev->wait_drain, netvsc_device_idle(nvdev));
975ae9e63bbSHaiyang Zhang 
97695fa0405SHaiyang Zhang 	if (request)
97795fa0405SHaiyang Zhang 		put_rndis_request(dev, request);
97895fa0405SHaiyang Zhang }
97995fa0405SHaiyang Zhang 
98095fa0405SHaiyang Zhang static int rndis_filter_open_device(struct rndis_device *dev)
98195fa0405SHaiyang Zhang {
98295fa0405SHaiyang Zhang 	int ret;
98395fa0405SHaiyang Zhang 
98495fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_INITIALIZED)
98595fa0405SHaiyang Zhang 		return 0;
98695fa0405SHaiyang Zhang 
98795fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev,
98895fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_BROADCAST |
98995fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_ALL_MULTICAST |
99095fa0405SHaiyang Zhang 					 NDIS_PACKET_TYPE_DIRECTED);
99195fa0405SHaiyang Zhang 	if (ret == 0)
99295fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_DATAINITIALIZED;
99395fa0405SHaiyang Zhang 
99495fa0405SHaiyang Zhang 	return ret;
99595fa0405SHaiyang Zhang }
99695fa0405SHaiyang Zhang 
99795fa0405SHaiyang Zhang static int rndis_filter_close_device(struct rndis_device *dev)
99895fa0405SHaiyang Zhang {
99995fa0405SHaiyang Zhang 	int ret;
100095fa0405SHaiyang Zhang 
100195fa0405SHaiyang Zhang 	if (dev->state != RNDIS_DEV_DATAINITIALIZED)
100295fa0405SHaiyang Zhang 		return 0;
100395fa0405SHaiyang Zhang 
10044f19c0d8Sstephen hemminger 	/* Make sure rndis_set_multicast doesn't re-enable filter! */
10054f19c0d8Sstephen hemminger 	cancel_work_sync(&dev->mcast_work);
10064f19c0d8Sstephen hemminger 
100795fa0405SHaiyang Zhang 	ret = rndis_filter_set_packet_filter(dev, 0);
1008c3582a2cSHaiyang Zhang 	if (ret == -ENODEV)
1009c3582a2cSHaiyang Zhang 		ret = 0;
1010c3582a2cSHaiyang Zhang 
101195fa0405SHaiyang Zhang 	if (ret == 0)
101295fa0405SHaiyang Zhang 		dev->state = RNDIS_DEV_INITIALIZED;
101395fa0405SHaiyang Zhang 
101495fa0405SHaiyang Zhang 	return ret;
101595fa0405SHaiyang Zhang }
101695fa0405SHaiyang Zhang 
10175b54dac8SHaiyang Zhang static void netvsc_sc_open(struct vmbus_channel *new_sc)
10185b54dac8SHaiyang Zhang {
10193d541ac5SVitaly Kuznetsov 	struct net_device *ndev =
10203d541ac5SVitaly Kuznetsov 		hv_get_drvdata(new_sc->primary_channel->device_obj);
1021867047c4Sstephen hemminger 	struct net_device_context *ndev_ctx = netdev_priv(ndev);
1022867047c4Sstephen hemminger 	struct netvsc_device *nvscdev;
10235b54dac8SHaiyang Zhang 	u16 chn_index = new_sc->offermsg.offer.sub_channel_index;
10246de38af6Sstephen hemminger 	struct netvsc_channel *nvchan;
10256de38af6Sstephen hemminger 	int ret;
10265b54dac8SHaiyang Zhang 
1027867047c4Sstephen hemminger 	/* This is safe because this callback only happens when
1028867047c4Sstephen hemminger 	 * new device is being setup and waiting on the channel_init_wait.
1029867047c4Sstephen hemminger 	 */
1030867047c4Sstephen hemminger 	nvscdev = rcu_dereference_raw(ndev_ctx->nvdev);
1031867047c4Sstephen hemminger 	if (!nvscdev || chn_index >= nvscdev->num_chn)
10325b54dac8SHaiyang Zhang 		return;
10335b54dac8SHaiyang Zhang 
10346de38af6Sstephen hemminger 	nvchan = nvscdev->chan_table + chn_index;
10356de38af6Sstephen hemminger 
1036b1dd90ceSK. Y. Srinivasan 	/* Because the device uses NAPI, all the interrupt batching and
1037b1dd90ceSK. Y. Srinivasan 	 * control is done via Net softirq, not the channel handling
1038b1dd90ceSK. Y. Srinivasan 	 */
1039b1dd90ceSK. Y. Srinivasan 	set_channel_read_mode(new_sc, HV_CALL_ISR);
1040b1dd90ceSK. Y. Srinivasan 
1041bffb1842SK. Y. Srinivasan 	/* Set the channel before opening.*/
1042bffb1842SK. Y. Srinivasan 	nvchan->channel = new_sc;
1043bffb1842SK. Y. Srinivasan 
1044a7f99d0fSStephen Hemminger 	ret = vmbus_open(new_sc, netvsc_ring_bytes,
1045a7f99d0fSStephen Hemminger 			 netvsc_ring_bytes, NULL, 0,
10466de38af6Sstephen hemminger 			 netvsc_channel_cb, nvchan);
104776bb5db5Sstephen hemminger 	if (ret == 0)
10486de38af6Sstephen hemminger 		napi_enable(&nvchan->napi);
104976bb5db5Sstephen hemminger 	else
10508195b139SStephen Hemminger 		netdev_notice(ndev, "sub channel open failed: %d\n", ret);
105115a863bfSstephen hemminger 
10528f2bb1deSStephen Hemminger 	if (atomic_inc_return(&nvscdev->open_chn) == nvscdev->num_chn)
1053732e4985Sstephen hemminger 		wake_up(&nvscdev->subchan_open);
10545b54dac8SHaiyang Zhang }
10555b54dac8SHaiyang Zhang 
10568195b139SStephen Hemminger /* Open sub-channels after completing the handling of the device probe.
10578195b139SStephen Hemminger  * This breaks overlap of processing the host message for the
10588195b139SStephen Hemminger  * new primary channel with the initialization of sub-channels.
10598195b139SStephen Hemminger  */
10608195b139SStephen Hemminger void rndis_set_subchannel(struct work_struct *w)
10618195b139SStephen Hemminger {
10628195b139SStephen Hemminger 	struct netvsc_device *nvdev
10638195b139SStephen Hemminger 		= container_of(w, struct netvsc_device, subchan_work);
10648195b139SStephen Hemminger 	struct nvsp_message *init_packet = &nvdev->channel_init_pkt;
10658195b139SStephen Hemminger 	struct net_device_context *ndev_ctx;
10668195b139SStephen Hemminger 	struct rndis_device *rdev;
10678195b139SStephen Hemminger 	struct net_device *ndev;
10688195b139SStephen Hemminger 	struct hv_device *hv_dev;
10698195b139SStephen Hemminger 	int i, ret;
10708195b139SStephen Hemminger 
10718195b139SStephen Hemminger 	if (!rtnl_trylock()) {
10728195b139SStephen Hemminger 		schedule_work(w);
10738195b139SStephen Hemminger 		return;
10748195b139SStephen Hemminger 	}
10758195b139SStephen Hemminger 
10768195b139SStephen Hemminger 	rdev = nvdev->extension;
10778195b139SStephen Hemminger 	if (!rdev)
10788195b139SStephen Hemminger 		goto unlock;	/* device was removed */
10798195b139SStephen Hemminger 
10808195b139SStephen Hemminger 	ndev = rdev->ndev;
10818195b139SStephen Hemminger 	ndev_ctx = netdev_priv(ndev);
10828195b139SStephen Hemminger 	hv_dev = ndev_ctx->device_ctx;
10838195b139SStephen Hemminger 
10848195b139SStephen Hemminger 	memset(init_packet, 0, sizeof(struct nvsp_message));
10858195b139SStephen Hemminger 	init_packet->hdr.msg_type = NVSP_MSG5_TYPE_SUBCHANNEL;
10868195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.op = NVSP_SUBCHANNEL_ALLOCATE;
10878195b139SStephen Hemminger 	init_packet->msg.v5_msg.subchn_req.num_subchannels =
10888195b139SStephen Hemminger 						nvdev->num_chn - 1;
10898195b139SStephen Hemminger 	ret = vmbus_sendpacket(hv_dev->channel, init_packet,
10908195b139SStephen Hemminger 			       sizeof(struct nvsp_message),
10918195b139SStephen Hemminger 			       (unsigned long)init_packet,
10928195b139SStephen Hemminger 			       VM_PKT_DATA_INBAND,
10938195b139SStephen Hemminger 			       VMBUS_DATA_PACKET_FLAG_COMPLETION_REQUESTED);
10948195b139SStephen Hemminger 	if (ret) {
10958195b139SStephen Hemminger 		netdev_err(ndev, "sub channel allocate send failed: %d\n", ret);
10968195b139SStephen Hemminger 		goto failed;
10978195b139SStephen Hemminger 	}
10988195b139SStephen Hemminger 
10998195b139SStephen Hemminger 	wait_for_completion(&nvdev->channel_init_wait);
11008195b139SStephen Hemminger 	if (init_packet->msg.v5_msg.subchn_comp.status != NVSP_STAT_SUCCESS) {
11018195b139SStephen Hemminger 		netdev_err(ndev, "sub channel request failed\n");
11028195b139SStephen Hemminger 		goto failed;
11038195b139SStephen Hemminger 	}
11048195b139SStephen Hemminger 
11058195b139SStephen Hemminger 	nvdev->num_chn = 1 +
11068195b139SStephen Hemminger 		init_packet->msg.v5_msg.subchn_comp.num_subchannels;
11078195b139SStephen Hemminger 
11088195b139SStephen Hemminger 	/* wait for all sub channels to open */
11098195b139SStephen Hemminger 	wait_event(nvdev->subchan_open,
11108195b139SStephen Hemminger 		   atomic_read(&nvdev->open_chn) == nvdev->num_chn);
11118195b139SStephen Hemminger 
11128195b139SStephen Hemminger 	/* ignore failues from setting rss parameters, still have channels */
11138195b139SStephen Hemminger 	rndis_filter_set_rss_param(rdev, netvsc_hash_key);
11148195b139SStephen Hemminger 
11158195b139SStephen Hemminger 	netif_set_real_num_tx_queues(ndev, nvdev->num_chn);
11168195b139SStephen Hemminger 	netif_set_real_num_rx_queues(ndev, nvdev->num_chn);
11178195b139SStephen Hemminger 
1118a6fb6aa3SHaiyang Zhang 	for (i = 0; i < VRSS_SEND_TAB_SIZE; i++)
1119a6fb6aa3SHaiyang Zhang 		ndev_ctx->tx_table[i] = i % nvdev->num_chn;
1120a6fb6aa3SHaiyang Zhang 
11218195b139SStephen Hemminger 	rtnl_unlock();
11228195b139SStephen Hemminger 	return;
11238195b139SStephen Hemminger 
11248195b139SStephen Hemminger failed:
11258195b139SStephen Hemminger 	/* fallback to only primary channel */
11268195b139SStephen Hemminger 	for (i = 1; i < nvdev->num_chn; i++)
11278195b139SStephen Hemminger 		netif_napi_del(&nvdev->chan_table[i].napi);
11288195b139SStephen Hemminger 
11298195b139SStephen Hemminger 	nvdev->max_chn = 1;
11308195b139SStephen Hemminger 	nvdev->num_chn = 1;
11318195b139SStephen Hemminger unlock:
11328195b139SStephen Hemminger 	rtnl_unlock();
11338195b139SStephen Hemminger }
11348195b139SStephen Hemminger 
1135aefd80e8SVitaly Kuznetsov static int rndis_netdev_set_hwcaps(struct rndis_device *rndis_device,
1136aefd80e8SVitaly Kuznetsov 				   struct netvsc_device *nvdev)
113795fa0405SHaiyang Zhang {
1138aefd80e8SVitaly Kuznetsov 	struct net_device *net = rndis_device->ndev;
11393d541ac5SVitaly Kuznetsov 	struct net_device_context *net_device_ctx = netdev_priv(net);
114023312a3bSstephen hemminger 	struct ndis_offload hwcaps;
11414a0e70aeSKY Srinivasan 	struct ndis_offload_params offloads;
114223312a3bSstephen hemminger 	unsigned int gso_max_size = GSO_MAX_SIZE;
1143aefd80e8SVitaly Kuznetsov 	int ret;
114495fa0405SHaiyang Zhang 
114523312a3bSstephen hemminger 	/* Find HW offload capabilities */
1146aefd80e8SVitaly Kuznetsov 	ret = rndis_query_hwcaps(rndis_device, nvdev, &hwcaps);
11479749fed5Sstephen hemminger 	if (ret != 0)
1148aefd80e8SVitaly Kuznetsov 		return ret;
114923312a3bSstephen hemminger 
115023312a3bSstephen hemminger 	/* A value of zero means "no change"; now turn on what we want. */
11514a0e70aeSKY Srinivasan 	memset(&offloads, 0, sizeof(struct ndis_offload_params));
115223312a3bSstephen hemminger 
115323312a3bSstephen hemminger 	/* Linux does not care about IP checksum, always does in kernel */
115423312a3bSstephen hemminger 	offloads.ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_DISABLED;
115523312a3bSstephen hemminger 
1156aefd80e8SVitaly Kuznetsov 	/* Reset previously set hw_features flags */
1157aefd80e8SVitaly Kuznetsov 	net->hw_features &= ~NETVSC_SUPPORTED_HW_FEATURES;
1158aefd80e8SVitaly Kuznetsov 	net_device_ctx->tx_checksum_mask = 0;
1159aefd80e8SVitaly Kuznetsov 
116023312a3bSstephen hemminger 	/* Compute tx offload settings based on hw capabilities */
1161aefd80e8SVitaly Kuznetsov 	net->hw_features |= NETIF_F_RXCSUM;
116223312a3bSstephen hemminger 
116323312a3bSstephen hemminger 	if ((hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_ALL_TCP4) == NDIS_TXCSUM_ALL_TCP4) {
116423312a3bSstephen hemminger 		/* Can checksum TCP */
116523312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IP_CSUM;
116623312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_TCP;
116723312a3bSstephen hemminger 
11684a0e70aeSKY Srinivasan 		offloads.tcp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
116923312a3bSstephen hemminger 
117023312a3bSstephen hemminger 		if (hwcaps.lsov2.ip4_encap & NDIS_OFFLOAD_ENCAP_8023) {
11714a0e70aeSKY Srinivasan 			offloads.lso_v2_ipv4 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
117223312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO;
117323312a3bSstephen hemminger 
117423312a3bSstephen hemminger 			if (hwcaps.lsov2.ip4_maxsz < gso_max_size)
117523312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip4_maxsz;
117623312a3bSstephen hemminger 		}
117723312a3bSstephen hemminger 
117823312a3bSstephen hemminger 		if (hwcaps.csum.ip4_txcsum & NDIS_TXCSUM_CAP_UDP4) {
117923312a3bSstephen hemminger 			offloads.udp_ip_v4_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
118023312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV4_UDP;
118123312a3bSstephen hemminger 		}
118223312a3bSstephen hemminger 	}
118323312a3bSstephen hemminger 
118423312a3bSstephen hemminger 	if ((hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_ALL_TCP6) == NDIS_TXCSUM_ALL_TCP6) {
118523312a3bSstephen hemminger 		net->hw_features |= NETIF_F_IPV6_CSUM;
118623312a3bSstephen hemminger 
118723312a3bSstephen hemminger 		offloads.tcp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
118823312a3bSstephen hemminger 		net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_TCP;
118923312a3bSstephen hemminger 
119023312a3bSstephen hemminger 		if ((hwcaps.lsov2.ip6_encap & NDIS_OFFLOAD_ENCAP_8023) &&
119123312a3bSstephen hemminger 		    (hwcaps.lsov2.ip6_opts & NDIS_LSOV2_CAP_IP6) == NDIS_LSOV2_CAP_IP6) {
119223312a3bSstephen hemminger 			offloads.lso_v2_ipv6 = NDIS_OFFLOAD_PARAMETERS_LSOV2_ENABLED;
119323312a3bSstephen hemminger 			net->hw_features |= NETIF_F_TSO6;
119423312a3bSstephen hemminger 
119523312a3bSstephen hemminger 			if (hwcaps.lsov2.ip6_maxsz < gso_max_size)
119623312a3bSstephen hemminger 				gso_max_size = hwcaps.lsov2.ip6_maxsz;
119723312a3bSstephen hemminger 		}
119823312a3bSstephen hemminger 
119923312a3bSstephen hemminger 		if (hwcaps.csum.ip6_txcsum & NDIS_TXCSUM_CAP_UDP6) {
120023312a3bSstephen hemminger 			offloads.udp_ip_v6_csum = NDIS_OFFLOAD_PARAMETERS_TX_RX_ENABLED;
120123312a3bSstephen hemminger 			net_device_ctx->tx_checksum_mask |= TRANSPORT_INFO_IPV6_UDP;
120223312a3bSstephen hemminger 		}
120323312a3bSstephen hemminger 	}
120423312a3bSstephen hemminger 
1205aefd80e8SVitaly Kuznetsov 	/* In case some hw_features disappeared we need to remove them from
1206aefd80e8SVitaly Kuznetsov 	 * net->features list as they're no longer supported.
1207aefd80e8SVitaly Kuznetsov 	 */
1208aefd80e8SVitaly Kuznetsov 	net->features &= ~NETVSC_SUPPORTED_HW_FEATURES | net->hw_features;
1209aefd80e8SVitaly Kuznetsov 
121023312a3bSstephen hemminger 	netif_set_gso_max_size(net, gso_max_size);
12114a0e70aeSKY Srinivasan 
1212aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_set_offload_params(net, nvdev, &offloads);
1213aefd80e8SVitaly Kuznetsov 
1214aefd80e8SVitaly Kuznetsov 	return ret;
1215aefd80e8SVitaly Kuznetsov }
1216aefd80e8SVitaly Kuznetsov 
1217aefd80e8SVitaly Kuznetsov struct netvsc_device *rndis_filter_device_add(struct hv_device *dev,
1218aefd80e8SVitaly Kuznetsov 				      struct netvsc_device_info *device_info)
1219aefd80e8SVitaly Kuznetsov {
1220aefd80e8SVitaly Kuznetsov 	struct net_device *net = hv_get_drvdata(dev);
1221aefd80e8SVitaly Kuznetsov 	struct netvsc_device *net_device;
1222aefd80e8SVitaly Kuznetsov 	struct rndis_device *rndis_device;
1223aefd80e8SVitaly Kuznetsov 	struct ndis_recv_scale_cap rsscap;
1224aefd80e8SVitaly Kuznetsov 	u32 rsscap_size = sizeof(struct ndis_recv_scale_cap);
1225aefd80e8SVitaly Kuznetsov 	u32 mtu, size;
1226aefd80e8SVitaly Kuznetsov 	u32 num_possible_rss_qs;
1227aefd80e8SVitaly Kuznetsov 	int i, ret;
1228aefd80e8SVitaly Kuznetsov 
1229aefd80e8SVitaly Kuznetsov 	rndis_device = get_rndis_device();
1230aefd80e8SVitaly Kuznetsov 	if (!rndis_device)
1231aefd80e8SVitaly Kuznetsov 		return ERR_PTR(-ENODEV);
1232aefd80e8SVitaly Kuznetsov 
1233aefd80e8SVitaly Kuznetsov 	/* Let the inner driver handle this first to create the netvsc channel
1234aefd80e8SVitaly Kuznetsov 	 * NOTE! Once the channel is created, we may get a receive callback
1235aefd80e8SVitaly Kuznetsov 	 * (RndisFilterOnReceive()) before this call is completed
1236aefd80e8SVitaly Kuznetsov 	 */
1237aefd80e8SVitaly Kuznetsov 	net_device = netvsc_device_add(dev, device_info);
1238aefd80e8SVitaly Kuznetsov 	if (IS_ERR(net_device)) {
1239aefd80e8SVitaly Kuznetsov 		kfree(rndis_device);
1240aefd80e8SVitaly Kuznetsov 		return net_device;
1241aefd80e8SVitaly Kuznetsov 	}
1242aefd80e8SVitaly Kuznetsov 
1243aefd80e8SVitaly Kuznetsov 	/* Initialize the rndis device */
1244aefd80e8SVitaly Kuznetsov 	net_device->max_chn = 1;
1245aefd80e8SVitaly Kuznetsov 	net_device->num_chn = 1;
1246aefd80e8SVitaly Kuznetsov 
1247aefd80e8SVitaly Kuznetsov 	net_device->extension = rndis_device;
1248aefd80e8SVitaly Kuznetsov 	rndis_device->ndev = net;
1249aefd80e8SVitaly Kuznetsov 
1250aefd80e8SVitaly Kuznetsov 	/* Send the rndis initialization message */
1251aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_init_device(rndis_device, net_device);
1252aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1253aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1254aefd80e8SVitaly Kuznetsov 
1255aefd80e8SVitaly Kuznetsov 	/* Get the MTU from the host */
1256aefd80e8SVitaly Kuznetsov 	size = sizeof(u32);
1257aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device(rndis_device, net_device,
1258aefd80e8SVitaly Kuznetsov 					RNDIS_OID_GEN_MAXIMUM_FRAME_SIZE,
1259aefd80e8SVitaly Kuznetsov 					&mtu, &size);
1260aefd80e8SVitaly Kuznetsov 	if (ret == 0 && size == sizeof(u32) && mtu < net->mtu)
1261aefd80e8SVitaly Kuznetsov 		net->mtu = mtu;
1262aefd80e8SVitaly Kuznetsov 
1263aefd80e8SVitaly Kuznetsov 	/* Get the mac address */
1264aefd80e8SVitaly Kuznetsov 	ret = rndis_filter_query_device_mac(rndis_device, net_device);
1265aefd80e8SVitaly Kuznetsov 	if (ret != 0)
1266aefd80e8SVitaly Kuznetsov 		goto err_dev_remv;
1267aefd80e8SVitaly Kuznetsov 
1268aefd80e8SVitaly Kuznetsov 	memcpy(device_info->mac_adr, rndis_device->hw_mac_adr, ETH_ALEN);
1269aefd80e8SVitaly Kuznetsov 
1270aefd80e8SVitaly Kuznetsov 	/* Query and set hardware capabilities */
1271aefd80e8SVitaly Kuznetsov 	ret = rndis_netdev_set_hwcaps(rndis_device, net_device);
1272aefd80e8SVitaly Kuznetsov 	if (ret != 0)
12734a0e70aeSKY Srinivasan 		goto err_dev_remv;
12744a0e70aeSKY Srinivasan 
1275867047c4Sstephen hemminger 	rndis_filter_query_device_link_status(rndis_device, net_device);
127695fa0405SHaiyang Zhang 
127793ba2222SVitaly Kuznetsov 	netdev_dbg(net, "Device MAC %pM link state %s\n",
127895fa0405SHaiyang Zhang 		   rndis_device->hw_mac_adr,
1279dedb459eSHaiyang Zhang 		   rndis_device->link_state ? "down" : "up");
128095fa0405SHaiyang Zhang 
12815b54dac8SHaiyang Zhang 	if (net_device->nvsp_version < NVSP_PROTOCOL_VERSION_5)
12829749fed5Sstephen hemminger 		return net_device;
12835b54dac8SHaiyang Zhang 
1284867047c4Sstephen hemminger 	rndis_filter_query_link_speed(rndis_device, net_device);
1285b37879e6SHaiyang Zhang 
12865b54dac8SHaiyang Zhang 	/* vRSS setup */
12875b54dac8SHaiyang Zhang 	memset(&rsscap, 0, rsscap_size);
1288867047c4Sstephen hemminger 	ret = rndis_filter_query_device(rndis_device, net_device,
12895b54dac8SHaiyang Zhang 					OID_GEN_RECEIVE_SCALE_CAPABILITIES,
12905b54dac8SHaiyang Zhang 					&rsscap, &rsscap_size);
12915b54dac8SHaiyang Zhang 	if (ret || rsscap.num_recv_que < 2)
12925b54dac8SHaiyang Zhang 		goto out;
12935b54dac8SHaiyang Zhang 
129425a39f7fSHaiyang Zhang 	/* This guarantees that num_possible_rss_qs <= num_online_cpus */
129525a39f7fSHaiyang Zhang 	num_possible_rss_qs = min_t(u32, num_online_cpus(),
12963071ada4Sstephen hemminger 				    rsscap.num_recv_que);
12973071ada4Sstephen hemminger 
12983071ada4Sstephen hemminger 	net_device->max_chn = min_t(u32, VRSS_CHANNEL_MAX, num_possible_rss_qs);
12998ebdcc52SAndrew Schwartzmeyer 
13008ebdcc52SAndrew Schwartzmeyer 	/* We will use the given number of channels if available. */
13013071ada4Sstephen hemminger 	net_device->num_chn = min(net_device->max_chn, device_info->num_chn);
1302ff4a4419Sstephen hemminger 
1303ff4a4419Sstephen hemminger 	for (i = 0; i < ITAB_NUM; i++)
130447371300SHaiyang Zhang 		rndis_device->rx_table[i] = ethtool_rxfh_indir_default(
130547371300SHaiyang Zhang 						i, net_device->num_chn);
1306ff4a4419Sstephen hemminger 
1307732e4985Sstephen hemminger 	atomic_set(&net_device->open_chn, 1);
13088195b139SStephen Hemminger 	vmbus_set_sc_create_callback(dev->channel, netvsc_sc_open);
13095b54dac8SHaiyang Zhang 
13107426b1a5Sstephen hemminger 	for (i = 1; i < net_device->num_chn; i++) {
13117426b1a5Sstephen hemminger 		ret = netvsc_alloc_recv_comp_ring(net_device, i);
13127426b1a5Sstephen hemminger 		if (ret) {
13137426b1a5Sstephen hemminger 			while (--i != 0)
13147426b1a5Sstephen hemminger 				vfree(net_device->chan_table[i].mrc.slots);
13157426b1a5Sstephen hemminger 			goto out;
13167426b1a5Sstephen hemminger 		}
13177426b1a5Sstephen hemminger 	}
13187426b1a5Sstephen hemminger 
13198195b139SStephen Hemminger 	for (i = 1; i < net_device->num_chn; i++)
13208195b139SStephen Hemminger 		netif_napi_add(net, &net_device->chan_table[i].napi,
13218195b139SStephen Hemminger 			       netvsc_poll, NAPI_POLL_WEIGHT);
13225b54dac8SHaiyang Zhang 
13238195b139SStephen Hemminger 	if (net_device->num_chn > 1)
13248195b139SStephen Hemminger 		schedule_work(&net_device->subchan_work);
13255362855aSVitaly Kuznetsov 
13265b54dac8SHaiyang Zhang out:
13278195b139SStephen Hemminger 	/* if unavailable, just proceed with one queue */
132859995370SAndrew Schwartzmeyer 	if (ret) {
132959995370SAndrew Schwartzmeyer 		net_device->max_chn = 1;
13305b54dac8SHaiyang Zhang 		net_device->num_chn = 1;
133159995370SAndrew Schwartzmeyer 	}
1332b3e6b82aSKY Srinivasan 
13339749fed5Sstephen hemminger 	return net_device;
13344a0e70aeSKY Srinivasan 
13354a0e70aeSKY Srinivasan err_dev_remv:
13362289f0aaSstephen hemminger 	rndis_filter_device_remove(dev, net_device);
13379749fed5Sstephen hemminger 	return ERR_PTR(ret);
133895fa0405SHaiyang Zhang }
133995fa0405SHaiyang Zhang 
13402289f0aaSstephen hemminger void rndis_filter_device_remove(struct hv_device *dev,
13412289f0aaSstephen hemminger 				struct netvsc_device *net_dev)
134295fa0405SHaiyang Zhang {
134395fa0405SHaiyang Zhang 	struct rndis_device *rndis_dev = net_dev->extension;
1344d66ab514SHaiyang Zhang 
1345a7483ec0SStephen Hemminger 	/* Don't try and setup sub channels if about to halt */
1346a7483ec0SStephen Hemminger 	cancel_work_sync(&net_dev->subchan_work);
1347a7483ec0SStephen Hemminger 
134895fa0405SHaiyang Zhang 	/* Halt and release the rndis device */
134995fa0405SHaiyang Zhang 	rndis_filter_halt_device(rndis_dev);
135095fa0405SHaiyang Zhang 
135195fa0405SHaiyang Zhang 	net_dev->extension = NULL;
135295fa0405SHaiyang Zhang 
135395fa0405SHaiyang Zhang 	netvsc_device_remove(dev);
135495fa0405SHaiyang Zhang }
135595fa0405SHaiyang Zhang 
13562f5fa6c8SVitaly Kuznetsov int rndis_filter_open(struct netvsc_device *nvdev)
135795fa0405SHaiyang Zhang {
13582f5fa6c8SVitaly Kuznetsov 	if (!nvdev)
135995fa0405SHaiyang Zhang 		return -EINVAL;
136095fa0405SHaiyang Zhang 
13612f5fa6c8SVitaly Kuznetsov 	return rndis_filter_open_device(nvdev->extension);
136295fa0405SHaiyang Zhang }
136395fa0405SHaiyang Zhang 
13642f5fa6c8SVitaly Kuznetsov int rndis_filter_close(struct netvsc_device *nvdev)
136595fa0405SHaiyang Zhang {
13665fccab3bSHaiyang Zhang 	if (!nvdev)
136795fa0405SHaiyang Zhang 		return -EINVAL;
136895fa0405SHaiyang Zhang 
13695fccab3bSHaiyang Zhang 	return rndis_filter_close_device(nvdev->extension);
137095fa0405SHaiyang Zhang }
1371ea383bf1Sstephen hemminger 
1372ea383bf1Sstephen hemminger bool rndis_filter_opened(const struct netvsc_device *nvdev)
1373ea383bf1Sstephen hemminger {
13740da6edbdSStephen Hemminger 	const struct rndis_device *dev = nvdev->extension;
13750da6edbdSStephen Hemminger 
13760da6edbdSStephen Hemminger 	return dev->state == RNDIS_DEV_DATAINITIALIZED;
1377ea383bf1Sstephen hemminger }
1378